From 46dffd54228e5eb53928a4ef82dc7efaf3df5964 Mon Sep 17 00:00:00 2001 From: James Talmage Date: Mon, 9 Nov 2015 20:09:45 -0500 Subject: [PATCH 1/4] Allow intercept of transitive require calls files. This paves the way for https://github.com/sindresorhus/ava/issues/156 --- index.js | 5 +++ test/fixture/depth0.js | 2 ++ test/fixture/depth1/depth2/index.js | 1 + test/fixture/depth1/index.js | 1 + test/fixture/greet-james.js | 3 ++ test/fixture/james.js | 1 + test/index.js | 55 +++++++++++++++++++++++++---- 7 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 test/fixture/depth0.js create mode 100644 test/fixture/depth1/depth2/index.js create mode 100644 test/fixture/depth1/index.js create mode 100644 test/fixture/greet-james.js create mode 100644 test/fixture/james.js diff --git a/index.js b/index.js index 69da662..bebb9e4 100644 --- a/index.js +++ b/index.js @@ -21,6 +21,11 @@ module.exports = function requireFromString(code, filename, opts) { var paths = Module._nodeModulePaths(path.dirname(filename)); var m = new Module(filename, module.parent); + if (opts.require) { + m.require = function (path) { + return opts.require.call(this, path); + } + } m.filename = filename; m.paths = [].concat(opts.prependPaths).concat(paths).concat(opts.appendPaths); m._compile(code, filename); diff --git a/test/fixture/depth0.js b/test/fixture/depth0.js new file mode 100644 index 0000000..d812491 --- /dev/null +++ b/test/fixture/depth0.js @@ -0,0 +1,2 @@ + +module.exports = require('./depth1'); diff --git a/test/fixture/depth1/depth2/index.js b/test/fixture/depth1/depth2/index.js new file mode 100644 index 0000000..f13355e --- /dev/null +++ b/test/fixture/depth1/depth2/index.js @@ -0,0 +1 @@ +module.exports = 'FOO - depth2'; diff --git a/test/fixture/depth1/index.js b/test/fixture/depth1/index.js new file mode 100644 index 0000000..f5b6081 --- /dev/null +++ b/test/fixture/depth1/index.js @@ -0,0 +1 @@ +module.exports = require('./depth2'); diff --git a/test/fixture/greet-james.js b/test/fixture/greet-james.js new file mode 100644 index 0000000..36f51c7 --- /dev/null +++ b/test/fixture/greet-james.js @@ -0,0 +1,3 @@ +module.exports = function() { + return 'Hello ' + require('./james'); +}; diff --git a/test/fixture/james.js b/test/fixture/james.js new file mode 100644 index 0000000..852a223 --- /dev/null +++ b/test/fixture/james.js @@ -0,0 +1 @@ +module.exports = 'James'; diff --git a/test/index.js b/test/index.js index 05b197a..46c6b70 100644 --- a/test/index.js +++ b/test/index.js @@ -7,6 +7,13 @@ var fs = require('fs'); var path = require('path'); var requireFromString = require('../'); +function getFixture(file) { + file = path.join(__dirname, 'fixture', file); + var code = fs.readFileSync(file, 'utf8'); + + return {file: file, code: code}; +} + it('should accept only string as code', function () { assert.throws(function () { requireFromString(); @@ -24,18 +31,16 @@ it('should accept filename', function () { }); it('should work with relative require in file', function () { - var file = path.join(__dirname, '/fixture/module.js'); - var code = fs.readFileSync(file, 'utf8'); - var result = requireFromString(code, file); + var fixture = getFixture('module.js'); + var result = requireFromString(fixture.code, fixture.file); assert.ok(result); assert.ok(module === result.parent.parent); }); it('should have appended and preppended paths', function () { - var file = path.join(__dirname, '/fixture/submodule.js'); - var code = fs.readFileSync(file, 'utf8'); - var result = requireFromString(code, file, { + var fixture = getFixture('submodule.js'); + var result = requireFromString(fixture.code, fixture.file, { appendPaths: ['append'], prependPaths: ['prepend'] }); @@ -44,3 +49,41 @@ it('should have appended and preppended paths', function () { assert.equal(result.paths.indexOf('append'), result.paths.length - 1); assert.equal(result.paths.indexOf('prepend'), 0); }); + +it('should allow modification of other required modules via callback', function () { + var fixture = getFixture('greet-james.js'); + var Module = module.constructor; + + function transform(code) { + return code.replace('James', 'Jim'); + } + + function requireHook(path) { + var file = Module._resolveFilename(path, this); + var code = fs.readFileSync(file, 'utf8'); + return requireFromString(transform(code), file, {require: requireHook}); + } + + var result = requireFromString(fixture.code, fixture.file, {require: requireHook}); + + assert.equal(result(), 'Hello Jim'); +}); + +it('transforms should be doable even as relative directory changes', function () { + var fixture = getFixture('depth0.js'); + var Module = module.constructor; + + function transform(code) { + return code.replace('FOO', 'BAR'); + } + + function requireHook(path) { + var file = Module._resolveFilename(path, this); + var code = fs.readFileSync(file, 'utf8'); + return requireFromString(transform(code), file, {require: requireHook}); + } + + var result = requireFromString(fixture.code, fixture.file, {require: requireHook}); + + assert.equal(result, 'BAR - depth2'); +}); From 111801391249a8850d7e1ce8316778f5fd1a1a29 Mon Sep 17 00:00:00 2001 From: James Talmage Date: Mon, 9 Nov 2015 20:45:43 -0500 Subject: [PATCH 2/4] add `.load()` method that returns the module before it is compiled --- index.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index bebb9e4..a3ac78d 100644 --- a/index.js +++ b/index.js @@ -3,7 +3,18 @@ var Module = module.constructor; var path = require('path'); -module.exports = function requireFromString(code, filename, opts) { +module.exports = requireFromString; +module.exports.load = load; + +function requireFromString(code, filename, opts) { + var m = load(code, filename, opts); + + m._compile(code, filename); + + return m.exports; +} + +function load(code, filename, opts) { if (typeof filename === 'object') { opts = filename; filename = undefined; @@ -28,7 +39,5 @@ module.exports = function requireFromString(code, filename, opts) { } m.filename = filename; m.paths = [].concat(opts.prependPaths).concat(paths).concat(opts.appendPaths); - m._compile(code, filename); - - return m.exports; -}; + return m; +} From 96063025416ba03ddddbcc71b6d76218a9eeced4 Mon Sep 17 00:00:00 2001 From: James Talmage Date: Mon, 9 Nov 2015 22:28:43 -0500 Subject: [PATCH 3/4] wip - refactor load signature --- index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index a3ac78d..c5f1a3e 100644 --- a/index.js +++ b/index.js @@ -7,14 +7,18 @@ module.exports = requireFromString; module.exports.load = load; function requireFromString(code, filename, opts) { - var m = load(code, filename, opts); + if (typeof code !== 'string') { + throw new Error('code must be a string, not ' + typeof code); + } + + var m = load(filename, opts); m._compile(code, filename); return m.exports; } -function load(code, filename, opts) { +function load(filename, opts) { if (typeof filename === 'object') { opts = filename; filename = undefined; @@ -25,10 +29,6 @@ function load(code, filename, opts) { opts.appendPaths = opts.appendPaths || []; opts.prependPaths = opts.prependPaths || []; - if (typeof code !== 'string') { - throw new Error('code must be a string, not ' + typeof code); - } - var paths = Module._nodeModulePaths(path.dirname(filename)); var m = new Module(filename, module.parent); From 5908dab20a114dc50bb1d2cd8a03946d14cd4460 Mon Sep 17 00:00:00 2001 From: James Talmage Date: Mon, 9 Nov 2015 23:33:05 -0500 Subject: [PATCH 4/4] better input validation --- index.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index c5f1a3e..3dfbb06 100644 --- a/index.js +++ b/index.js @@ -2,11 +2,17 @@ var Module = module.constructor; var path = require('path'); +var assert = require('assert'); module.exports = requireFromString; module.exports.load = load; function requireFromString(code, filename, opts) { + if (typeof filename === 'object') { + opts = filename; + filename = undefined; + } + if (typeof code !== 'string') { throw new Error('code must be a string, not ' + typeof code); } @@ -32,9 +38,14 @@ function load(filename, opts) { var paths = Module._nodeModulePaths(path.dirname(filename)); var m = new Module(filename, module.parent); - if (opts.require) { + + var requireHook = opts.require; + + if (requireHook) { m.require = function (path) { - return opts.require.call(this, path); + assert(typeof path === 'string', 'path must be a string'); + assert(path, 'missing path'); + return requireHook.call(this, path); } } m.filename = filename;