From 0c17067d873f807b1280d5e922f44da3abc4c328 Mon Sep 17 00:00:00 2001 From: Tim-Smart Date: Mon, 8 Mar 2010 17:35:39 +1300 Subject: [PATCH] Add require.registerExtension --- src/node.js | 78 +++++++++++++++++++++++++++++++------- test/fixtures/registerExt.test | 1 + test/fixtures/registerExt2.test | 1 + test/simple/test-module-loading.js | 18 +++++++++ 4 files changed, 85 insertions(+), 13 deletions(-) create mode 100644 test/fixtures/registerExt.test create mode 100644 test/fixtures/registerExt2.test diff --git a/src/node.js b/src/node.js index 0398ba2..bbaa684 100644 --- a/src/node.js +++ b/src/node.js @@ -45,6 +45,7 @@ node.dns.createConnection = removed("node.dns.createConnection() has moved. Use // Module var internalModuleCache = {}; +var extensionCache = {}; function Module (id, parent) { this.id = id; @@ -481,6 +482,12 @@ function findModulePath (id, dirs, callback) { path.join(dir, id, "index.addon") ]; + var ext; + for (ext in extensionCache) { + locations.push(path.join(dir, id + ext)); + locations.push(path.join(dir, id, 'index' + ext)); + } + function searchLocations () { var location = locations.shift(); if (!location) { @@ -515,8 +522,13 @@ function resolveModulePath(request, parent) { var id, paths; if (request.charAt(0) == "." && (request.charAt(1) == "/" || request.charAt(1) == ".")) { // Relative request + var exts = ['js', 'node'], ext; + for (ext in extensionCache) { + exts.push(ext.slice(1)); + } + var parentIdPath = path.dirname(parent.id + - (path.basename(parent.filename).match(/^index\.(js|addon)$/) ? "/" : "")); + (path.basename(parent.filename).match(new RegExp('^index\\.(' + exts.join('|') + ')$')) ? "/" : "")); id = path.join(parentIdPath, request); // debug("RELATIVE: requested:"+request+" set ID to: "+id+" from "+parent.id+"("+parentIdPath+")"); paths = [path.dirname(parent.filename)]; @@ -584,6 +596,32 @@ function loadModule (request, parent, callback) { }; +// 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 then 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] = compiler; +} + + Module.prototype.loadSync = function (filename) { debug("loadSync " + JSON.stringify(filename) + " for module " + JSON.stringify(this.id)); @@ -649,6 +687,12 @@ Module.prototype._loadContent = function (content, filename) { // remove shebang content = content.replace(/^\#\!.*/, ''); + // Compile content if needed + var ext = path.extname(filename); + if (extensionCache[ext]) { + content = extensionCache[ext](content); + } + function requireAsync (url, cb) { loadModule(url, self, cb); } @@ -660,19 +704,27 @@ Module.prototype._loadContent = function (content, filename) { require.paths = process.paths; require.async = requireAsync; require.main = process.mainModule; - // create wrapper function - var wrapper = "(function (exports, require, module, __filename, __dirname) { " - + content - + "\n});"; + require.registerExtension = registerExtension; - try { - var compiledWrapper = process.compile(wrapper, filename); - var dirName = path.dirname(filename); - if (filename === process.argv[1]) - process.checkBreak(); - compiledWrapper.apply(self.exports, [self.exports, require, self, filename, dirName]); - } catch (e) { - return e; + + if ('string' === typeof content) { + // create wrapper function + var wrapper = "(function (exports, require, module, __filename, __dirname) { " + + content + + "\n});"; + + try { + var compiledWrapper = process.compile(wrapper, filename); + var dirName = path.dirname(filename); + if (filename === process.argv[1]) { + process.checkBreak(); + } + compiledWrapper.apply(self.exports, [self.exports, require, self, filename, dirName]); + } catch (e) { + return e; + } + } else { + self.exports = content; } }; diff --git a/test/fixtures/registerExt.test b/test/fixtures/registerExt.test new file mode 100644 index 0000000..d08e5b1 --- /dev/null +++ b/test/fixtures/registerExt.test @@ -0,0 +1 @@ +this is custom source diff --git a/test/fixtures/registerExt2.test b/test/fixtures/registerExt2.test new file mode 100644 index 0000000..94544f1 --- /dev/null +++ b/test/fixtures/registerExt2.test @@ -0,0 +1 @@ +This is for the object return test diff --git a/test/simple/test-module-loading.js b/test/simple/test-module-loading.js index 0d99c25..0011e13 100644 --- a/test/simple/test-module-loading.js +++ b/test/simple/test-module-loading.js @@ -63,6 +63,24 @@ require.async('../fixtures/a', function (err, a) { assert.equal("A", a.A()); }); +debug('load custom file types with registerExtension'); +require.registerExtension('.test', function(content) { + assert.equal("this is custom source\n", content); + + return content.replace("this is custom source", "exports.test = 'passed'"); +}); + +assert.equal(require('../fixtures/registerExt').test, "passed"); + +debug('load custom file types that return non-strings'); +require.registerExtension('.test', function(content) { + return { + custom: 'passed' + }; +}); + +assert.equal(require('../fixtures/registerExt2').custom, 'passed'); + process.addListener("exit", function () { assert.equal(true, a.A instanceof Function); assert.equal("A done", a.A()); -- 2.7.4