enhanced-require
Advanced tools
Comparing version 0.4.0-beta3 to 0.4.0-beta4
@@ -13,3 +13,4 @@ /* | ||
* @param request {string} the compile request string | ||
* @param loaders {string[]} the absolute filenames of the loaders | ||
* @param loaders {{path: String, query: String, module: Boolean}[]} | ||
* the absolute filenames of the loaders | ||
* @param filenames {string[]} the filenames of "contents" | ||
@@ -23,3 +24,3 @@ * @param contents {Buffer[]} read contents | ||
function execLoaders(context, request, loaders, filenames, contents, loaderContextExtras, dependencyInfo, options, sync, callback) { | ||
var loaderFunctions, cacheable = true; | ||
var loaderCallObjects, cacheable = true; | ||
if(loaders.length === 0) { | ||
@@ -30,8 +31,10 @@ // if no loaders are used, the file content is the resulting code | ||
// try to load all loaders | ||
loaderFunctions = []; | ||
loaderCallObjects = []; | ||
try { | ||
loaders.forEach(function(loaderFilename) { | ||
var loader = require(loaderFilename); | ||
loaderFunctions.push(loader); | ||
loaders.forEach(function(l) { | ||
loaderCallObjects.push({ | ||
fn: require(l.path), | ||
path: l.path, | ||
query: l.query | ||
}); | ||
}); | ||
@@ -46,2 +49,3 @@ } catch(e) { | ||
} | ||
function nextLoader(/* err, paramBuffer1, paramBuffer2, ...*/) { | ||
@@ -56,3 +60,3 @@ var args = Array.prototype.slice.apply(arguments); | ||
// if loaders are remaining | ||
if(loaderFunctions.length > 0) { | ||
if(loaderCallObjects.length > 0) { | ||
var loaderCacheable = false; | ||
@@ -70,2 +74,3 @@ var async = false; | ||
} | ||
var loaderCallObject = loaderCallObjects.pop(); | ||
var loaderContext = { | ||
@@ -124,4 +129,5 @@ context: context, | ||
}, | ||
loaderIndex: loaderFunctions.length - 1, | ||
currentLoaders: loaders, | ||
loaderIndex: loaderCallObjects.length, | ||
currentLoaders: loaders.map(resolve.stringify.part), | ||
query: loaderCallObject.query, | ||
web: true, | ||
@@ -153,3 +159,3 @@ debug: options.debug, | ||
// exec to loader | ||
var retVal = loaderFunctions.pop().apply(loaderContext, params); | ||
var retVal = loaderCallObject.fn.apply(loaderContext, params); | ||
@@ -156,0 +162,0 @@ // if it isn't asynchron, use the return value |
@@ -5,6 +5,3 @@ /* | ||
*/ | ||
var Module = require("module"); | ||
var vm = require("vm"); | ||
var path = require("path"); | ||
var runInThisContext = require("vm").runInThisContext; | ||
@@ -21,38 +18,19 @@ function stripBOM(content) { | ||
var wrapper = ["(function (exports, require, define, module, __filename, __dirname) {", "})"]; | ||
module.exports = function(code, parent, request, filename, enhancedRequire, options, reqObj) { | ||
module.exports = function(code, parent, request, filename, enhancedRequire, options, requireRoot) { | ||
var m; | ||
if(enhancedRequire) { | ||
var m = { | ||
id: request, | ||
parent: parent, | ||
filename: filename, | ||
loaded: false, | ||
children: [], | ||
exports: {} | ||
}; | ||
var req = require("./require").factory(m, reqObj); | ||
var wrappedCode = wrapper[0] + stripBOM(code) + wrapper[1]; | ||
var wrappedFunction = runInThisContext(wrappedCode, request, request == filename); | ||
var exec = function() { | ||
wrappedFunction.call(m.exports, m.exports, req, req.define, m, filename, path.dirname(filename)); | ||
m.loaded = true; | ||
return m.exports; | ||
} | ||
exec.module = m; | ||
return exec; | ||
var EnhancedModule = require("./Module"); | ||
m = new EnhancedModule(request, parent, requireRoot); | ||
} else { | ||
var m = new Module(request, parent); | ||
m.filename = filename; | ||
if(options && options.paths) | ||
m.paths = options.paths; | ||
else | ||
m.paths = Module._nodeModulePaths(path.dirname(filename)); | ||
var exec = function() { | ||
m._compile(code, filename); | ||
return m.exports; | ||
} | ||
exec.module = m; | ||
return exec; | ||
var NodeModule = require("module"); | ||
m = new NodeModule(request, parent); | ||
m.paths = NodeModule._nodeModulePaths(path.dirname(filename)); | ||
} | ||
m.filename = filename; | ||
var exec = function() { | ||
m._compile(code, filename); | ||
return m.exports; | ||
} | ||
exec.module = m; | ||
return exec; | ||
} |
@@ -9,265 +9,13 @@ /* | ||
var Hot = require("./Hot"); | ||
var execModule = require("./execModule"); | ||
var execLoaders = require("./execLoaders"); | ||
var resolve = require("enhanced-resolve"); | ||
var SELF_REQUIRE = "require = require(" + JSON.stringify(__filename) + ")(module);"; | ||
var natives = process.binding("natives"); | ||
function requireNativeModule(name) { | ||
return require(name); | ||
} | ||
function existsNativeModule(name) { | ||
return natives.hasOwnProperty(name); | ||
} | ||
function addToSet(set, item) { | ||
if(!set) return [item]; | ||
if(set.indexOf(item) >= 0) return set; | ||
set.push(item); | ||
return set; | ||
} | ||
/** | ||
* any require(string module) - sync require | ||
* void require(array modules, function callback(modules...), [function errorCallback(err)]) - async require | ||
* void require(array modules) - async require | ||
*/ | ||
function theRequire(parent, context, modules, callback, errorCallback) { | ||
if(Array.isArray(modules)) { | ||
theEnsure.call(this, parent, context, modules, function(req, err) { | ||
if(err && errorCallback) return errorCallback(err); | ||
var reqModules = modules.map(function(n) { return req(n) }); | ||
if(callback) callback.apply(null, reqModules); | ||
}); | ||
} else { | ||
if(callback) throw new Error("require(string, callback) is not a valid signature. You may want to call require(array, function)."); | ||
// check native module | ||
if(existsNativeModule(modules)) return requireNativeModule(modules); | ||
// resolve filename | ||
var filename = resolve.sync(context, modules, this.options.resolve); | ||
// check in cache | ||
var cache = this.cache; | ||
if(cache[filename]) { | ||
var m = cache[filename]; | ||
if(parent) { | ||
m.parents = addToSet(m.parents, parent.id); | ||
parent.children = addToSet(parent.children, m); | ||
} | ||
return m.exports; | ||
} | ||
// split loaders from resource | ||
var filenameWithLoaders = filename; | ||
var loaders = filename.split(/!/g); | ||
filename = loaders.pop(); | ||
// check for resource cache | ||
var content = this.contentCache[filename]; | ||
if(!content) { | ||
content = this.contentCache[filename] = fs.readFileSync(filename); | ||
} | ||
// execute the loaders | ||
var source = this.sourceCache[filenameWithLoaders]; | ||
if(!source) { | ||
source = | ||
this.sourceCache[filenameWithLoaders] = | ||
execLoaders.sync( | ||
context, | ||
filenameWithLoaders, | ||
loaders, [filename], | ||
[content], | ||
{ | ||
loaderType: "loader" | ||
}, | ||
null, | ||
this.options | ||
)[0].toString("utf-8"); | ||
} | ||
// load the source code | ||
var exec = execModule( | ||
source, | ||
parent, | ||
filenameWithLoaders, | ||
filename, | ||
loaders.length > 0 || this.options.recursive, | ||
this.options, | ||
this | ||
); | ||
// add to cache | ||
cache[filenameWithLoaders] = exec.module; | ||
// make dependency graph | ||
if(parent) { | ||
exec.module.parents = [parent.id]; | ||
parent.children.push(exec.module); | ||
} | ||
// execute | ||
return exec(); | ||
} | ||
} | ||
function theResolve(parent, context, name, callback) { | ||
if(callback) { | ||
if(existsNativeModule(name)) return callback(null, name); | ||
return resolve(context, name, this.options.resolve, callback); | ||
} else { | ||
if(existsNativeModule(name)) return name; | ||
return resolve.sync(context, name, this.options.resolve); | ||
} | ||
} | ||
function theEnsure(parent, context, modules, callback) { | ||
var options = this.options; | ||
var cache = this.cache; | ||
var reqFn = this.require; | ||
var contentCache = this.contentCache; | ||
var sourceCache = this.sourceCache; | ||
var loadingContent = this.loadingContent; | ||
var loadingSource = this.loadingSource; | ||
mapAsync(modules, function(name, callback) { | ||
if(existsNativeModule(name)) return callback(null, name); | ||
resolve(context, name, callback); | ||
}, function(err, resolvedModules) { | ||
if(err) return callback(err); | ||
mapAsync(resolvedModules, function(resolvedModule, callback) { | ||
if(existsNativeModule(resolvedModule)) return callback(); | ||
if(cache[resolvedModule]) return callback(); | ||
if(sourceCache[resolvedModule]) return callback(); | ||
// split loaders from resource | ||
var filenameWithLoaders = resolvedModule; | ||
var loaders = resolvedModule.split(/!/g); | ||
var filename = loaders.pop(); | ||
if(contentCache[filename]) return makeSource(null, contentCache[filename]); | ||
return loadContent(); | ||
function loadContent() { | ||
if(!loadingContent[filename]) { | ||
loadingContent[filename] = [makeSource]; | ||
fs.readFile(filename, applyToAll(loadingContent[filename], function(content) { | ||
if(!contentCache[filename]) | ||
contentCache[filename] = content; | ||
delete loadingContent[filename]; | ||
return contentCache[filename]; | ||
})); | ||
} else | ||
loadingContent[filename].push(makeSource); | ||
} | ||
function makeSource(err, content) { | ||
if(err) return callback(err); | ||
if(!loadingSource[filenameWithLoaders]) { | ||
loadingSource[filenameWithLoaders] = [callback]; | ||
var finished = applyToAll(loadingSource[filenameWithLoaders], function(content) { | ||
if(!sourceCache[filenameWithLoaders]) | ||
sourceCache[filenameWithLoaders] = content; | ||
delete loadingSource[filenameWithLoaders]; | ||
return sourceCache[filenameWithLoaders]; | ||
}); | ||
execLoaders( | ||
context, | ||
filenameWithLoaders, | ||
loaders, [filename], | ||
[content], | ||
null, | ||
null, | ||
options, | ||
function(err, sources) { | ||
if(err) return finished(err); | ||
if(sources[0] instanceof Buffer || typeof sources[0] == "string") | ||
finished(null, sources[0].toString("utf-8")); | ||
else | ||
callback(new Error("Loader result is not a Buffer or string")); | ||
} | ||
) | ||
} else | ||
loadingSource[filenameWithLoaders].push(callback); | ||
} | ||
}, function(err) { | ||
return callback(reqFn, err); | ||
}) | ||
}); | ||
} | ||
function theDefine(parent, context, dependencies, fn, arg3) { | ||
var withName = false; | ||
if(typeof dependencies == "string") { | ||
// pop name | ||
dependencies = fn; | ||
fn = arg3; | ||
withName = true; | ||
} | ||
if(Array.isArray(dependencies)) { | ||
theEnsure.call(this, parent, context, dependencies, function(req) { | ||
parent.exports = fn.apply(null, dependencies.map(function(n) { return req(n) })); | ||
}); | ||
} else if(withName) { | ||
fn = dependencies; | ||
if(typeof fn == "function") | ||
parent.exports = fn(); | ||
else | ||
parent.exports = fn; | ||
} else { | ||
fn = dependencies; | ||
if(typeof fn == "function") | ||
fn(require, parent.exports, parent); | ||
else | ||
parent.exports = fn; | ||
} | ||
} | ||
function theContext(parent, context, contextName) { | ||
return function(name) { | ||
if(typeof name != "string" || name.substr(0, 2) != "./") | ||
throw new Error("A function created by require.context must be called with a string beginning with './'"); | ||
return theRequire.call(this, parent, context, contextName + "/" + name); | ||
}.bind(this); | ||
} | ||
/** | ||
* create a require function from a filename | ||
*/ | ||
function requireFactory(parent, reqObj) { | ||
function requireFactory(parent, requireRoot) { | ||
reqObj = Object.create(reqObj.base); | ||
// get the directory | ||
var context = parent ? path.dirname(parent.filename) : ""; | ||
// make require function | ||
var require = reqObj.require = theRequire.bind(reqObj, parent, context); | ||
require.options = reqObj.options; | ||
require.cache = reqObj.cache; | ||
require.sourceCache = reqObj.sourceCache; | ||
require.contentCache = reqObj.contentCache; | ||
require.resolve = theResolve.bind(reqObj, parent, context); | ||
require.ensure = theEnsure.bind(reqObj, parent, context); | ||
require.context = theContext.bind(reqObj, parent, context); | ||
require.define = theDefine.bind(reqObj, parent, context); | ||
require.enhanced = require.options.enhanced; | ||
require.amd = require.options.amd; | ||
// init hot | ||
if(reqObj.options && reqObj.options.hot && parent) { | ||
var hot = new Hot(parent, reqObj, require); | ||
require.hot = hot; | ||
if(reqObj.main !== parent) { | ||
hot.data = reqObj.data[parent.id]; | ||
parent.hot = hot; | ||
} | ||
var requireContext = requireRoot.createContext(parent); | ||
if(requireContext.require.hot && requireRoot.main !== parent) { | ||
parent.hot = requireContext.require.hot; | ||
} | ||
return require; | ||
return requireContext.require; | ||
}; | ||
@@ -296,53 +44,11 @@ | ||
if(!options.loader) options.loader = {}; | ||
if(!options.watchDelay) options.watchDelay = 400; | ||
if(options.watchDelay === undefined) options.watchDelay = 400; | ||
if(options.recursive === undefined) options.recursive = options.hot; | ||
var reqObj = { | ||
options: options, | ||
cache: {}, | ||
sourceCache: {}, | ||
contentCache: {}, | ||
loadingContent: {}, | ||
loadingSource: {}, | ||
data: {}, | ||
main: parent, | ||
status: options.watch ? "watch" : "idle", | ||
// watch: | ||
updatedContents: [], | ||
contentWatchers: {} | ||
}; | ||
reqObj.base = reqObj; | ||
if(options.hot && !options.recursive) throw new Error("hot option depends on recursive option"); | ||
return requireFactory(parent, reqObj); | ||
var requireRoot = new (options.hot ? require("./HotRequireRoot") : require("./RequireRoot"))(parent, options); | ||
return requireFactory(parent, requireRoot); | ||
}; | ||
exports.factory = requireFactory; | ||
// Helpers | ||
function mapAsync(array, fn, callback) { | ||
var count = array.length; | ||
if(count == 0) return callback(null, array); | ||
var results = array.slice(0); | ||
array.forEach(function(item, idx) { | ||
fn(item, function(err, result) { | ||
if(count < 0) return; | ||
if(err) { | ||
count = -1; | ||
return callback(err); | ||
} | ||
results[idx] = result; | ||
count--; | ||
if(count == 0) { | ||
return callback(null, results); | ||
} | ||
}); | ||
}); | ||
} | ||
function applyToAll(array, callback) { | ||
return function(err, result) { | ||
if(!err && callback) result = callback(result); | ||
array.forEach(function(fn) { | ||
fn(err, result); | ||
}); | ||
} | ||
} |
{ | ||
"name": "enhanced-require", | ||
"version": "0.4.0-beta3", | ||
"version": "0.4.0-beta4", | ||
"author": "Tobias Koppers @sokra", | ||
"description": "Enhance the require function in node.js with support for loaders which preprocess files and really async require (AMD). Enables Hot Code Replacement.", | ||
"dependencies": { | ||
"enhanced-resolve": "0.3.x", | ||
"enhanced-resolve": "0.4.x", | ||
"clone": "0.0.x", | ||
@@ -20,5 +20,7 @@ "buffer-equal": "0.0.x" | ||
"should": "*", | ||
"raw-loader": "0.2.x" | ||
"raw-loader": "0.2.x", | ||
"jade-loader": "0.2.x" | ||
}, | ||
"main": "lib/require", | ||
"bin": "bin/enhanced-require-hot-watch.js", | ||
"engines": { | ||
@@ -25,0 +27,0 @@ "node": ">=0.6" |
@@ -34,4 +34,3 @@ var should = require("should"); | ||
if(err) throw err; | ||
should.exist(updatedModules); | ||
updatedModules.should.be.eql([]); | ||
should.not.exist(updatedModules); | ||
list.should.be.eql([1]); | ||
@@ -47,4 +46,3 @@ writeCounter(2); | ||
if(err) throw err; | ||
should.exist(updatedModules); | ||
updatedModules.should.be.eql([]); | ||
should.not.exist(updatedModules); | ||
list.should.be.eql([1, -1, 2]); | ||
@@ -74,4 +72,3 @@ writeCounter(3); | ||
if(err) throw err; | ||
should.exist(updatedModules); | ||
updatedModules.should.be.eql([]); | ||
should.not.exist(updatedModules); | ||
list.should.be.eql([1]); | ||
@@ -87,4 +84,3 @@ writeCounter(2); | ||
if(err) throw err; | ||
should.exist(updatedModules); | ||
updatedModules.should.be.eql([]); | ||
should.not.exist(updatedModules); | ||
list.should.be.eql([1, -1, 2]); | ||
@@ -104,3 +100,3 @@ writeCounter(3); | ||
it("should not accept a bubbling update by manual sync check", function(done) { | ||
it("should not accept a bubbling update by manual check", function(done) { | ||
var req = reqFactory(module, { | ||
@@ -117,3 +113,3 @@ hot: true, | ||
err.should.be.instanceOf(Error); | ||
/bubble/.test(err.toString()).should.be.ok; | ||
/bubbling/.test(err.toString()).should.be.ok; | ||
done(); | ||
@@ -133,4 +129,3 @@ }); | ||
if(err) throw err; | ||
should.exist(updatedModules); | ||
updatedModules.should.be.eql([]); | ||
should.not.exist(updatedModules); | ||
list.should.be.eql(["module.exports = 1"]); | ||
@@ -146,4 +141,3 @@ writeCounter(2); | ||
if(err) throw err; | ||
should.exist(updatedModules); | ||
updatedModules.should.be.eql([]); | ||
should.not.exist(updatedModules); | ||
list.should.be.eql(["module.exports = 1", "MODULE.EXPORTS = 1", "module.exports = 2"]); | ||
@@ -190,3 +184,45 @@ writeCounter(3); | ||
it("should wait for apply if applyOnUpdate = false", function(done) { | ||
var req = reqFactory(module, { | ||
hot: true, | ||
recursive: true, | ||
watch: true, | ||
watchDelay: 10 | ||
}); | ||
var list = req("./fixtures/hot/counter"); | ||
req.hot.setApplyOnUpdate(false); | ||
list.should.be.eql([1]); | ||
setTimeout(function() { | ||
list.should.be.eql([1]); | ||
writeCounter(2); | ||
setTimeout(function() { | ||
list.should.be.eql([1]); | ||
req.hot.status().should.be.eql("ready"); | ||
req.hot.apply(function(err, outdatedModules) { | ||
should.not.exist(err); | ||
should.exist(outdatedModules); | ||
outdatedModules.should.have.property("length").be.eql(1); | ||
outdatedModules[0].id.should.be.eql(counterValuePath); | ||
req.hot.status().should.be.eql("watch"); | ||
list.should.be.eql([1, -1, 2]); | ||
writeCounter(3); | ||
setTimeout(function() { | ||
req.hot.status().should.be.eql("ready"); | ||
req.hot.apply(function(err, outdatedModules) { | ||
should.not.exist(err); | ||
should.exist(outdatedModules); | ||
outdatedModules.should.have.property("length").be.eql(1); | ||
outdatedModules[0].id.should.be.eql(counterValuePath); | ||
req.hot.status().should.be.eql("watch"); | ||
list.should.be.eql([1, -1, 2, -2, 3]); | ||
done(); | ||
}); | ||
}, 100); | ||
}); | ||
}, 100); | ||
}, 100); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
60457
53
1679
15
4
1
+ Addedenhanced-resolve@0.4.12(transitive)
- Removedenhanced-resolve@0.3.1(transitive)
Updatedenhanced-resolve@0.4.x