Skip to content

Instantly share code, notes, and snippets.

@anaisbetts
Created June 23, 2016 08:49
Show Gist options
  • Save anaisbetts/6fb2871fcb6e1b0d8bac761b156ee7df to your computer and use it in GitHub Desktop.
Save anaisbetts/6fb2871fcb6e1b0d8bac761b156ee7df to your computer and use it in GitHub Desktop.
A failed attempt to make a lazy require
const emptyFunc = () => {};
emptyFunc.prototype = {};
// On initial call, we just return the proxy
// On next tick or >1 call, we make getters call require
// Until then getters have to create a proxy too
//
// cases:
// require('foo')();
// const {foo} = require('bar');
// const baz = require('baz');
class DelayedProxyHandler {
constructor(factory) {
this.factory = factory;
this.shouldReturnProxy = true;
process.nextTick(() => this.shouldReturnProxy = false);
}
getOrCreateBackingObject() {
if (this.factory) {
this.value = this.factory();
this.factory = null;
}
return this.value;
}
get(target, property) {
if (!this.shouldReturnProxy) {
let backing = this.getOrCreateBackingObject();
if (property !== 'default') return backing[property];
if (!(property in backing)) return backing;
return backing[property];
}
if (property === Symbol.toPrimitive) return undefined;
this.shouldReturnProxy = false;
let handler = new DelayedProxyHandler(() => this.getOrCreateBackingObject()[property]);
handler.shouldReturnProxy = false;
return new Proxy(emptyFunc, handler);
}
/*
* Boring methods that just return the real object
*/
getPrototypeOf() {
return Object.getPrototypeOf(this.getOrCreateBackingObject());
}
setPrototypeOf(target, proto) {
return Object.setPrototypeOf(this.getOrCreateBackingObject(), proto);
}
isExtensible() {
return Object.isExtensible(this.getOrCreateBackingObject());
}
preventExtensions() {
return Object.preventExtensions(this.getOrCreateBackingObject());
}
/*
getOwnPropertyDescriptor(target, prop) {
return Object.getOwnPropertyDescriptor(this.getOrCreateBackingObject(), prop);
}
defineProperty(target, prop, desc) {
return Object.defineProperty(this.getOrCreateBackingObject(), prop, desc);
}
*/
has(target, prop) {
return prop in this.getOrCreateBackingObject();
}
set(target, prop, val) {
this.getOrCreateBackingObject()[prop] = val;
}
deleteProperty(target, prop) {
delete this.getOrCreateBackingObject()[prop];
}
ownKeys() {
return Object.getOwnPropertyNames(this.getOrCreateBackingObject());
}
apply(target, thisArg, args) {
let ret = this.getOrCreateBackingObject();
return ret.apply(thisArg, args);
}
construct(target, args) {
console.log("Constructing!");
let Ctor = this.getOrCreateBackingObject();
return new (Function.prototype.bind.apply(Ctor, args));
}
}
module.exports = function(origRequire) {
let ret = function() {
let origArgs = arguments;
let that = this;
let handler = new DelayedProxyHandler(function() { return origRequire.apply(that, origArgs); });
return new Proxy(emptyFunc, handler);
}
Object.keys(origRequire).forEach((k) => ret[k] = origRequire[k]);
return ret;
};
const origRequire = global.require;
const Module = origRequire('module');
const origPrototypeRequire = Module.prototype.require;
let lazify = require('./deferred-require');
let lazyRequire = lazify(origRequire);
global.require = function() {
if (arguments[0].match(/_global$/)) {
return global;
}
return lazyRequire.apply(this, arguments);
};
global.eagerRequire = origRequire;
lazyModuleRequire = lazify(origPrototypeRequire);
Module.prototype.require = function() {
if (arguments[0].match(/_global$/)) {
return global;
}
return lazyModuleRequire.apply(this, arguments);
};
module.exports = global.require;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment