diff --git a/src/node.js b/src/node.js index e1bac5a2412b33..5034e31f3c2007 100644 --- a/src/node.js +++ b/src/node.js @@ -65,11 +65,10 @@ var module = (function () { // Set the environ variable NODE_MODULE_CONTEXTS=1 to make node load all // modules in thier own context. var contextLoad = false; - if (parseInt(process.env["NODE_MODULE_CONTEXTS"]) > 0) contextLoad = true; + if (+process.env["NODE_MODULE_CONTEXTS"] > 0) contextLoad = true; var Script; var internalModuleCache = {}; - var extensionCache = {}; function Module (id, parent) { this.id = id; @@ -142,11 +141,13 @@ var module = (function () { modulePaths = process.env["NODE_PATH"].split(":").concat(modulePaths); } - var moduleNativeExtensions = ['js', 'node']; + var extensions = {}; + var registerExtension = removed('require.registerExtension() removed. Use require.extensions instead'); // Which files to traverse while finding id? Returns generator function. function traverser (id, dirs) { - var head = [], inDir = [], _dirs = dirs.slice(); + var head = [], inDir = [], dirs = dirs.slice(), + exts = Object.keys(extensions); return function next () { var result = head.shift(); if (result) { return result; } @@ -154,16 +155,13 @@ var module = (function () { var gen = inDir.shift(); if (gen) { head = gen(); return next(); } - var dir = _dirs.shift(); + var dir = dirs.shift(); if (dir !== undefined) { - function direct (ext) { return path.join(dir, id + '.' + ext); } - function index (ext) { return path.join(dir, id, 'index.' + ext); } - var userExts = Object.keys(extensionCache); + function direct (ext) { return path.join(dir, id + ext); } + function index (ext) { return path.join(dir, id, 'index' + ext); } inDir = [ - function () { return moduleNativeExtensions.map(direct); }, - function () { return userExts.map(direct); }, - function () { return moduleNativeExtensions.map(index); }, - function () { return userExts.map(index); } + function () { return exts.map(direct); }, + function () { return exts.map(index); }, ]; head = [path.join(dir, id)]; return next(); @@ -190,15 +188,17 @@ var module = (function () { // sync - no i/o performed function resolveModulePath(request, parent) { var start = request.substring(0, 2); - if (start !== "./" && start !== "..") { return [request, modulePaths]; } - - // Relative request - var exts = moduleNativeExtensions.concat(Object.keys(extensionCache)), - indexRE = new RegExp('^index\\.(' + exts.join('|') + ')$'), - // XXX dangerous code: ^^^ what if exts contained some RE control chars? - isIndex = path.basename(parent.filename).match(indexRE), - parentIdPath = isIndex ? parent.id : path.dirname(parent.id), - id = path.join(parentIdPath, request); + if (start !== "./" && start !== "..") { + return [request, modulePaths]; + } + + // Is the parent an index module? + // We can assume the parent has a valid extension, + // as it already has been accepted as a module. + var isIndex = /^index\.\w+?$/.test(path.basename(parent.filename)), + parentIdPath = isIndex ? parent.id : path.dirname(parent.id), + id = path.join(parentIdPath, request); + // make sure require('./path') and require('path') get distinct ids, even // when called from the toplevel js file if (parentIdPath === '.' && id.indexOf('/') === -1) { @@ -210,22 +210,23 @@ var module = (function () { function loadModule (request, parent) { - var resolvedModule = resolveModulePath(request, parent), - id = resolvedModule[0], - paths = resolvedModule[1]; - debug("loadModule REQUEST " + (request) + " parent: " + parent.id); - // native modules always take precedence. - var cachedNative = internalModuleCache[id]; + // With natives id === request + // We deal with these first + var cachedNative = internalModuleCache[request]; if (cachedNative) { return cachedNative.exports; } - if (natives[id]) { - debug('load native module ' + id); - return loadNative(id).exports; + if (natives[request]) { + debug('load native module ' + request); + return loadNative(request).exports; } + var resolvedModule = resolveModulePath(request, parent), + id = resolvedModule[0], + paths = resolvedModule[1]; + // look up the filename first, since that's the cache key. debug("looking for " + JSON.stringify(id) + " in " + JSON.stringify(paths)); var filename = findModulePath(request, paths); @@ -243,48 +244,15 @@ var module = (function () { }; - // This function allows the user to register file extensions to custom - // Javascript 'compilers'. It accepts 2 arguments, where ext is a file - // extension as a string. E.g. '.coffee' for coffee-script files. compiler - // is the second argument, which is a function that gets called when the - // specified file extension is found. The compiler is passed a single - // argument, which is, the file contents, which need to be compiled. - // - // The function needs to return the compiled source, or an non-string - // variable that will get attached directly to the module exports. Example: - // - // require.registerExtension('.coffee', function(content) { - // return doCompileMagic(content); - // }); - function registerExtension(ext, compiler) { - if ('string' !== typeof ext && false === /\.\w+$/.test(ext)) { - throw new Error('require.registerExtension: First argument not a valid extension string.'); - } - - if ('function' !== typeof compiler) { - throw new Error('require.registerExtension: Second argument not a valid compiler function.'); - } - - extensionCache[ext.slice(1)] = compiler; - } - - Module.prototype.load = function (filename) { debug("load " + JSON.stringify(filename) + " for module " + JSON.stringify(this.id)); process.assert(!this.loaded); this.filename = filename; - if (filename.match(/\.node$/)) { - this._loadObject(filename); - } else { - this._loadScript(filename); - } - }; - - - Module.prototype._loadObject = function (filename) { - process.dlopen(filename, this.exports); + var extension = path.extname(filename) || '.js'; + extensions[extension](this, filename); + this.loaded = true; }; @@ -299,23 +267,15 @@ var module = (function () { // remove shebang content = content.replace(/^\#\!.*/, ''); - // Compile content if needed - var ext = path.extname(filename).slice(1); - if (extensionCache[ext]) { - content = extensionCache[ext](content); - } - - if ("string" !== typeof content) { - self.exports = content; - return; - } - function require (path) { return loadModule(path, self); } require.paths = modulePaths; require.main = process.mainModule; + // Enable support to add extra extension types + require.extensions = extensions; + // TODO: Insert depreciation warning require.registerExtension = registerExtension; var dirname = path.dirname(filename); @@ -366,10 +326,16 @@ var module = (function () { }; - Module.prototype._loadScript = function (filename) { + // Native extension for .js + extensions['.js'] = function (module, filename) { var content = requireNative('fs').readFileSync(filename, 'utf8'); - this._compile(content, filename); - this.loaded = true; + module._compile(content, filename); + }; + + + // Native extension for .node + extensions['.node'] = function (module, filename) { + process.dlopen(filename, module.exports); }; diff --git a/test/simple/test-module-loading.js b/test/simple/test-module-loading.js index 8cadcb9a11d0d0..9c9cfeb7e219fa 100644 --- a/test/simple/test-module-loading.js +++ b/test/simple/test-module-loading.js @@ -1,6 +1,7 @@ common = require("../common"); assert = common.assert -var path = require('path'); +var path = require('path'), + fs = require('fs'); common.debug("load test-module-loading.js"); @@ -66,21 +67,22 @@ try { assert.equal(require('path').dirname(__filename), __dirname); -common.debug('load custom file types with registerExtension'); -require.registerExtension('.test', function(content) { +common.debug('load custom file types with extensions'); +require.extensions['.test'] = function (module, filename) { + var content = fs.readFileSync(filename).toString(); assert.equal("this is custom source\n", content); - - return content.replace("this is custom source", "exports.test = 'passed'"); -}); + content = content.replace("this is custom source", "exports.test = 'passed'"); + module._compile(content, filename); +}; assert.equal(require('../fixtures/registerExt').test, "passed"); common.debug('load custom file types that return non-strings'); -require.registerExtension('.test', function(content) { - return { +require.extensions['.test'] = function (module, filename) { + module.exports = { custom: 'passed' }; -}); +}; assert.equal(require('../fixtures/registerExt2').custom, 'passed'); common.debug("load modules by absolute id, then change require.paths, and load another module with the same absolute id."); @@ -104,8 +106,8 @@ common.debug('load order'); var loadOrder = '../fixtures/module-load-order/', msg = "Load order incorrect."; -require.registerExtension('.reg', function(content) { return content; }); -require.registerExtension('.reg2', function(content) { return content; }); +require.extensions['.reg'] = require.extensions['.js']; +require.extensions['.reg2'] = require.extensions['.js']; assert.equal(require(loadOrder + 'file1').file1, 'file1', msg); assert.equal(require(loadOrder + 'file2').file2, 'file2.js', msg);