From 6ddd5a95db57286394758b6b4431db25d16be81c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e=20Kooi?= Date: Tue, 16 Jan 2018 12:27:20 +0100 Subject: [PATCH] add 'dep' event to transforms When a transform stream emits 'dep', the path is added to the transformed file's dependencies. This is useful for something like [split-require][0] which uses a custom `require`-like function. Instead of transforming the source code and adding in dummy `require()` calls, split-require could emit the 'dep' event. [0]: https://github.com/goto-bus-stop/split-require --- index.js | 16 +++++++++- readme.markdown | 4 +++ test/files/transformdeps.js | 1 + test/tr_deps.js | 60 +++++++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 test/files/transformdeps.js create mode 100644 test/tr_deps.js diff --git a/index.js b/index.js index bf60790..dd5ed58 100644 --- a/index.js +++ b/index.js @@ -38,6 +38,7 @@ function Deps (opts) { this.pkgFileCache = {}; this.pkgFileCachePending = {}; this._emittedPkg = {}; + this._transformDeps = {}; this.visited = {}; this.walking = {}; this.entries = []; @@ -261,6 +262,11 @@ Deps.prototype.getTransforms = function (file, pkg, opts) { trOpts._flags = trOpts.hasOwnProperty('_flags') ? trOpts._flags : self.options; if (typeof tr === 'function') { var t = tr(file, trOpts); + // allow transforms to `stream.emit('dep', path)` to add dependencies for this file + self._transformDeps[file] = []; + t.on('dep', function (dep) { + self._transformDeps[file].push(dep); + }); self.emit('transform', t, file); nextTick(cb, null, wrapTransform(t)); } @@ -302,6 +308,11 @@ Deps.prototype.getTransforms = function (file, pkg, opts) { } var trs = r(file, trOpts); + // allow transforms to `stream.emit('dep', path)` to add dependencies for this file + self._transformDeps[file] = []; + trs.on('dep', function (dep) { + self._transformDeps[file].push(dep); + }); self.emit('transform', trs, file); cb(null, trs); }); @@ -422,7 +433,10 @@ Deps.prototype.walk = function (id, parent, cb) { }); function getDeps (file, src) { - return rec.noparse ? [] : self.parseDeps(file, src); + var deps = rec.noparse ? [] : self.parseDeps(file, src); + // dependencies emitted by transforms + if (self._transformDeps[file]) deps = deps.concat(self._transformDeps[file]); + return deps; } function fromSource (file, src, pkg, fakePath) { diff --git a/readme.markdown b/readme.markdown index 33352ef..767c162 100644 --- a/readme.markdown +++ b/readme.markdown @@ -203,6 +203,10 @@ You don't necessarily need to use the readable/writable filter stream for transforming file contents, but this is an easy way to do it. +module-deps looks for `require()` calls and adds their arguments as dependencies +of a file. Transform streams can emit `'dep'` events to include additional +dependencies that are not consumed with `require()`. + When you call `mdeps()` with an `opts.transform`, the transformations you specify will not be run for any files in node_modules/. This is because modules you include should be self-contained and not need to worry about guarding diff --git a/test/files/transformdeps.js b/test/files/transformdeps.js new file mode 100644 index 0000000..0f12530 --- /dev/null +++ b/test/files/transformdeps.js @@ -0,0 +1 @@ +// dependencies added by transform diff --git a/test/tr_deps.js b/test/tr_deps.js new file mode 100644 index 0000000..f00a854 --- /dev/null +++ b/test/tr_deps.js @@ -0,0 +1,60 @@ +var parser = require('../'); +var through = require('through2'); +var test = require('tap').test; +var fs = require('fs'); +var path = require('path'); + +var files = { + transformdeps: path.join(__dirname, '/files/transformdeps.js'), + foo: path.join(__dirname, '/files/foo.js'), + bar: path.join(__dirname, '/files/bar.js') +}; + +var sources = Object.keys(files).reduce(function (acc, file) { + acc[file] = fs.readFileSync(files[file], 'utf8'); + return acc; +}, {}); + +test('deps added by transforms', function (t) { + t.plan(1); + var p = parser(); + p.write({ transform: transform, options: {} }); + p.end({ file: files.transformdeps, entry: true }); + function transform (file) { + if (file === files.transformdeps) return through(function(chunk, enc, cb) { + cb(null, chunk); + }, function (cb) { + this.emit('dep', './foo'); + cb(); + }); + return through(); + } + + var rows = []; + p.on('data', function (row) { rows.push(row) }); + p.on('end', function () { + t.same(rows.sort(cmp), [ + { + id: files.transformdeps, + file: files.transformdeps, + source: sources.transformdeps, + entry: true, + deps: { './foo': files.foo } + }, + { + id: files.foo, + file: files.foo, + source: sources.foo, + deps: { './bar': files.bar } + }, + { + id: files.bar, + file: files.bar, + source: sources.bar, + deps: {} + } + ].sort(cmp)); + }); +}); + +function cmp (a, b) { return a.id < b.id ? -1 : 1 }