[Pkg-javascript-commits] [node-module-deps] 197/444: complete rewrite using prototypes for clarity
Bastien Roucariès
rouca at moszumanska.debian.org
Fri Dec 15 09:47:55 UTC 2017
This is an automated email from the git hooks/post-receive script.
rouca pushed a commit to branch master
in repository node-module-deps.
commit a0f5e9838bc6c93e14f52a7259d33162c38b676f
Author: James Halliday <mail at substack.net>
Date: Thu May 8 22:28:20 2014 -0700
complete rewrite using prototypes for clarity
---
index.js | 569 ++++++++++++++++++++++++++++++++---------------------------
package.json | 3 +-
2 files changed, 309 insertions(+), 263 deletions(-)
diff --git a/index.js b/index.js
index 3517c44..805e4a3 100644
--- a/index.js
+++ b/index.js
@@ -5,260 +5,274 @@ var spawn = require('child_process').spawn;
var browserResolve = require('browser-resolve');
var nodeResolve = require('resolve');
var detective = require('detective');
-var through = require('through');
+var through = require('through2');
var concat = require('concat-stream');
var parents = require('parents');
+var combine = require('stream-combiner');
+var duplexer = require('duplexer2');
-module.exports = function (mains, opts) {
+var inherits = require('inherits');
+var Readable = require('readable-stream').Readable;
+
+module.exports = Deps;
+inherits(Deps, Readable);
+
+function Deps (mains, opts) {
+ var self = this;
+ if (!(this instanceof Deps)) return new Deps(mains, opts);
+ Readable.call(this, { objectMode: true });
+
if (!opts) opts = {};
- var cache = opts.cache;
- var pkgCache = opts.packageCache || {};
+ if (!Array.isArray(mains)) mains = [ mains ].filter(Boolean);
+
+ this.basedir = opts.basedir || process.cwd();
+ this.cache = opts.cache;
+ this.pkgCache = opts.packageCache || {};
+ this.pkgFileCache = {};
+ this.pkgFileCachePending = {};
+ this.visited = {};
- var paths = opts.paths || process.env.NODE_PATH;
- if (typeof paths === 'string') {
- paths = process.env.NODE_PATH.split(':');
+ this.paths = opts.paths || process.env.NODE_PATH;
+ if (typeof this.paths === 'string') {
+ this.paths = process.env.NODE_PATH.split(':');
}
- if (!paths) paths = [];
+ if (!this.paths) this.paths = [];
+ this.entries = [];
+ this.mains = [];
- if (!Array.isArray(mains)) mains = [ mains ].filter(Boolean);
- var basedir = opts.basedir || process.cwd();
+ this.transforms = [].concat(opts.transform).filter(Boolean);
+ this.resolver = opts.resolve || browserResolve;
+ this.options = opts;
+ this.pending = 0;
+ this.top = { id: '/', filename: '/', paths: this.paths };
- var entries = mains.map(function (file) {
- if (file.path) return file.path;
- if (typeof file.pipe === 'function') {
- var n = Math.floor(Math.pow(16,8) * Math.random()).toString(16);
- return path.join(basedir, 'fake_' + n + '.js');
- }
- return path.resolve(file);
- });
+ mains.forEach(function (file) { self.add(file) });
+}
+
+Deps.prototype._read = function () {
+ if (this._started) return;
+ this._started = true;
+ this._start();
+};
+
+Deps.prototype._start = function () {
+ var self = this;
- var visited = {};
- var pending = 0;
+ for (var i = 0; i < this.entries.length; i++) {
+ var main = this.mains[i];
+ var file = this.entries[i];
+
+ var id = path.resolve(this.basedir, main);
+ this.lookupPackage(file, function (err, pkg) {
+ if (err) return self.emit('error', err)
+ else start(main, file, pkg)
+ });
+ }
- var output = through();
+ function start (main, file, pkg) {
+ if (!pkg) pkg = {};
+ if (!pkg.__dirname) pkg.__dirname = path.dirname(file);
+
+ if (typeof main === 'object') {
+ self.walk({ stream: main, file: file }, main);
+ }
+ else self.walk(main, self.top);
+ }
+};
+
+Deps.prototype.add = function (main) {
+ var self = this;
- var transforms = [].concat(opts.transform).filter(Boolean);
- var resolve = opts.resolve || browserResolve;
+ var file;
+ if (typeof main.pipe === 'function') {
+ var n = Math.floor(Math.pow(16,8) * Math.random()).toString(16);
+ file = path.join(basedir, 'fake_' + n + '.js');
+ }
+ else file = main;
+ file = path.resolve(file);
+ this.mains.push(main);
+ this.entries.push(file);
+};
+
+Deps.prototype.resolve = function (id, parent, cb) {
+ var self = this;
+ var opts = self.options;
- function pushResult (row) {
- output.queue(row);
- if (--pending === 0) output.queue(null);
+ /*
+ if (typeof id === 'object') {
+ id.stream.pipe(concat({ encoding: 'string' }, function (src) {
+ var pkgfile = path.join(basedir, 'package.json');
+ fs.readFile(pkgfile, function (err, pkgsrc) {
+ var pkg = {};
+ if (!err) {
+ try { pkg = JSON.parse(pkgsrc) }
+ catch (e) {};
+ }
+ var trx = getTransform(pkg);
+ applyTransforms(id.file, trx, src, pkg);
+ });
+ }));
+ if (cb) cb(false);
+ return;
}
+ */
+
+ var c = this.cache && this.cache[parent.id];
+ var resolver = c && typeof c === 'object'
+ && !Buffer.isBuffer(c) && c.deps[id]
+ ? function (xid, xparent, fn) {
+ var file = self.cache[parent.id].deps[id];
+ fn(null, file, self.pkgCache && self.pkgCache[file]);
+ }
+ : self.resolver
+ ;
+
+ var pkgdir;
+ parent.packageFilter = function (p, x) {
+ pkgdir = x;
+ if (opts.packageFilter) return opts.packageFilter(p, x);
+ else return p;
+ };
- var top = { id: '/', filename: '/', paths: paths };
+ if (opts.extensions) parent.extensions = opts.extensions;
+ if (opts.modules) parent.modules = opts.modules;
- (function () {
- var pkgCount = mains.length;
- if (pkgCount === 0) next();
+ self.resolver(id, parent, function onresolve (err, file, pkg) {
+ if (err) return cb(err);
+ if (!file) return cb(new Error(
+ 'module not found: "' + id + '" from file '
+ + parent.filename
+ ));
- mains.forEach(function (main) {
- if (typeof main === 'object') return done();
- var id = path.resolve(basedir, main);
- if (pkgCache[id]) return done();
-
- lookupPkg(main, function (err, pkg) {
- if (err) return output.emit('error', err);
- if (!pkg) pkg = {};
- if (!pkg.__dirname) pkg.__dirname = path.dirname(id);
- pkgCache[id] = pkg;
- done();
+ if (pkg && pkgdir) pkg.__dirname = pkgdir;
+ if (!pkg || !pkg.__dirname) {
+ self.lookupPackage(file, function (err, p) {
+ if (err) return cb(err);
+ if (!p) p = {};
+ if (!p.__dirname) p.__dirname = path.dirname(file);
+ self.pkgCache[file] = p;
+ onresolve(err, file, opts.packageFilter
+ ? opts.packageFilter(p, p.__dirname) : p
+ );
});
- });
- function done () { if (--pkgCount === 0) next() }
- })();
+ }
+ else cb(err, file, pkg);
+ });
+};
+
+Deps.prototype.readFile = function (file, pkg) {
+ if (this.cache && this.cache[file]) {
+ var tr = through();
+ tr.push(this.cache[file]);
+ return tr;
+ }
+ var rs = fs.createReadStream(file);
+ rs.on('error', function (err) { tr.emit('error', err) });
+ return rs.pipe(this.getTransforms(file, pkg));
+};
+
+Deps.prototype.getTransforms = function (file, pkg) {
+ var self = this;
+ var isTopLevel = this.entries.some(function (main) {
+ var m = path.relative(path.dirname(main), file);
+ return m.split('/').indexOf('node_modules') < 0;
+ });
- function next () {
- mains.forEach(function (main, ix) {
- if (typeof main === 'object') {
- walk({ stream: main, file: entries[ix] }, top);
- }
- else walk(main, top)
+ var transforms = [].concat(isTopLevel ? this.transforms : [])
+ .concat(getTransforms(pkg, this.options))
+ ;
+ if (transforms.length === 0) return through();
+
+ var pending = transforms.length;
+ var streams = [];
+ var input = through();
+ var output = through();
+ var dup = duplexer(input, output);
+
+ for (var i = 0; i < transforms.length; i++) {
+ makeTransform(transforms[i], function (err, trs) {
+ if (err) return dup.emit('error', err)
+ streams.push(trs);
+ if (-- pending === 0) done();
});
-
- if (mains.length === 0) {
- output.pause();
- output.queue(null);
- process.nextTick(function () { output.resume() });
- }
}
+ return dup;
- return output;
+ function done () {
+ var middle = combine.apply(null, streams);
+ input.pipe(middle).pipe(output);
+ }
- function walk (id, parent, cb) {
- pending ++;
-
- if (typeof id === 'object') {
- id.stream.pipe(concat({ encoding: 'string' }, function (src) {
- var pkgfile = path.join(basedir, 'package.json');
- fs.readFile(pkgfile, function (err, pkgsrc) {
- var pkg = {};
- if (!err) {
- try { pkg = JSON.parse(pkgsrc) }
- catch (e) {};
- }
- var trx = getTransform(pkg);
- applyTransforms(id.file, trx, src, pkg);
- });
- }));
- if (cb) cb(false);
- return;
+ function makeTransform (tr, cb) {
+ var trOpts = {};
+ if (Array.isArray(tr)) {
+ trOpts = tr[1];
+ tr = tr[0];
}
-
- var c = cache && cache[parent.id];
- var resolver = c && typeof c === 'object'
- && !Buffer.isBuffer(c) && c.deps[id]
- ? function (xid, xparent, cb) {
- var file = cache[parent.id].deps[id];
- cb(null, file, pkgCache && pkgCache[file]);
- }
- : resolve;
- ;
-
- var pkgdir;
- parent.packageFilter = function (p, x) {
- pkgdir = x;
- if (opts.packageFilter) return opts.packageFilter(p, x);
- else return p;
- };
- if (opts.extensions) parent.extensions = opts.extensions;
- if (opts.modules) parent.modules = opts.modules;
-
- resolver(id, parent, function f (err, file, pkg) {
- if (err) return output.emit('error', err);
- if (!file) return output.emit('error', new Error([
- 'module not found: "' + id + '" from file ',
- parent.filename
- ].join('')));
+ if (typeof tr === 'function') {
+ var t = tr(file, trOpts);
+ self.emit('transform', t, file);
+ nextTick(cb, null, t);
+ }
+ else {
+ loadTransform(tr, trOpts, function (err, trs) {
+ self.emit('transform', t, file);
+ cb(null, trs);
+ });
+ }
+ }
+
+ function loadTransform (file, trOpts, cb) {
+ var params = { basedir: path.dirname(file) };
+ nodeResolve(file, params, function nr (err, res, again) {
+ if (err && again) return cb(err);
- if (pkg && pkgdir) pkg.__dirname = pkgdir;
- if (!pkg || !pkg.__dirname) {
- lookupPkg(file, function (err, p) {
- if (err) return output.emit('error', err);
- if (!p) p = {};
- if (!p.__dirname) p.__dirname = path.dirname(file);
- pkgCache[file] = p;
- f(err, file, opts.packageFilter ? opts.packageFilter(p, p.__dirname) : p);
+ if (err) {
+ params.basedir = process.cwd();
+ return nodeResolve(file, params, function (e, r) {
+ nr(e, r, true)
});
- return;
- }
-
- if (cb) cb(file);
- if (visited[file]) {
- if (--pending === 0) output.queue(null);
- return;
}
- visited[file] = true;
- if (!pkg && pkgCache[file]) pkg = pkgCache[file];
+ if (!res) return cb(new Error(
+ 'cannot find transform module ' + tr
+ + ' while transforming ' + file
+ ));
- var trx = getTransform(pkg);
-
- if (cache && cache[file]) {
- parseDeps(file, cache[file], pkg);
+ var r = require(res);
+ if (typeof r !== 'function') {
+ return cb(new Error('transform not a function'));
}
- else fs.readFile(file, 'utf8', function (err, src) {
- if (err) {
- var e = new Error(
- err.message
- + ' while resolving '
- + JSON.stringify(id)
- + ' from file '
- + parent.filename
- );
- Object.keys(err).forEach(function (key) {
- e[key] = err[key];
- });
- if (err.code === 'ENOENT') {
- e.type = 'not found';
- }
- e.parent = parent.filename;
- e.filename = file;
- return output.emit('error', e);
- }
- applyTransforms(file, trx, src, pkg);
- });
+
+ var trs = r(file, trOpts);
+ self.emit('transform', trs, file);
+ cb(null, trs);
});
}
+};
+
+Deps.prototype.walk = function (id, parent, cb) {
+ var self = this;
+ var opts = self.options;
+ this.pending ++;
- function getTransform (pkg) {
- var trx = [];
- if (opts.transformKey) {
- var n = pkg;
- var keys = opts.transformKey;
- for (var i = 0; i < keys.length; i++) {
- if (n && typeof n === 'object') n = n[keys[i]];
- else break;
- }
- if (i === keys.length) {
- trx = [].concat(n).filter(Boolean);
- }
- }
- return trx.concat(opts.globalTransform || []);
- }
-
- function applyTransforms (file, trx, src, pkg) {
- var isTopLevel = mains.some(function (main) {
- var m = path.relative(path.dirname(main), file);
- return m.split('/').indexOf('node_modules') < 0;
- });
- var transf = (isTopLevel ? transforms : []).concat(trx);
- if (transf.length === 0) return done();
-
- (function ap (trs) {
- if (trs.length === 0) return done();
- var tr = trs[0], trOpts = {};
- if (Array.isArray(tr)) {
- trOpts = tr[1];
- tr = tr[0];
- }
-
- makeTransform(file, [ tr, trOpts ], function f (err, s) {
- if (err && pkg && pkg.__dirname) {
- var t = path.resolve(pkg.__dirname, tr);
- return makeTransform(file, [ t, trOpts ], function (e, s_) {
- if (e) output.emit('error', e);
- else f(null, s_);
- });
- }
- if (err) return output.emit('error', err);
-
- s.on('error', output.emit.bind(output, 'error'));
- s.pipe(concat({ encoding: 'string' }, function (data) {
- src = data;
- ap(trs.slice(1));
- }));
- s.end(src);
- });
- })(transf);
-
- function done () {
- parseDeps(file, src, pkg);
- }
- }
+ self.resolve(id, parent, function (err, file, pkg) {
+ if (err) return self.emit('error', err);
+ self.readFile(file).pipe(concat(function (body) {
+ var src = body.toString('utf8');
+ var deps = self.parseDeps(file, src);
+ fromDeps(file, src, pkg, deps);
+ }));
+ });
- function parseDeps (file, src, pkg) {
- var deps;
- if (!Buffer.isBuffer(src) && typeof src === 'object') {
- deps = Object.keys(src.deps).sort();
- src = src.source;
- }
- else if (opts.noParse && opts.noParse.indexOf(file) >= 0) {
- deps = [];
- }
- else if (/\.json$/.test(file)) {
- deps = [];
- }
- else {
- try { var deps = detective(src) }
- catch (ex) {
- var message = ex && ex.message ? ex.message : ex;
- return output.emit('error', new Error(
- 'Parsing file ' + file + ': ' + message
- ));
- }
- }
+ function fromDeps (file, src, pkg, deps) {
var p = deps.length;
- var current = { id: file, filename: file, paths: paths, package: pkg };
+ var current = {
+ id: file,
+ filename: file,
+ paths: self.paths,
+ package: pkg
+ };
var resolved = {};
deps.forEach(function (id) {
@@ -267,8 +281,7 @@ module.exports = function (mains, opts) {
if (--p === 0) done();
return;
}
-
- walk(id, current, function (r) {
+ self.walk(id, current, function (r) {
resolved[id] = r;
if (--p === 0) done();
});
@@ -281,66 +294,98 @@ module.exports = function (mains, opts) {
source: src,
deps: resolved
};
- if (entries.indexOf(file) >= 0) {
+ if (self.entries.indexOf(file) >= 0) {
rec.entry = true;
}
- pushResult(rec);
+ self.push(rec);
+
+ if (cb) cb(null, file);
+ if (-- self.pending === 0) self.push(null);
}
}
+};
+
+Deps.prototype.parseDeps = function (file, src, cb) {
+ if (this.options.noParse === true) return [];
+ if (/\.json$/.test(file)) return [];
- function makeTransform (file, tpair, cb) {
- var tr = tpair[0], trOpts = tpair[1];
- if (typeof tr === 'function') {
- var t = tr(file, trOpts);
- output.emit('transform', t, file);
- return cb(null, t);
- }
-
- var params = { basedir: path.dirname(file) };
- nodeResolve(tr, params, function nr (err, res, again) {
- if (err && again) return cb(err);
-
- if (err) {
- params.basedir = process.cwd();
- nodeResolve(tr, params, function (e, r) {
- nr(e, r, true)
- });
- return;
- }
-
- if (!res) return cb(new Error([
- 'cannot find transform module ', tr,
- ' while transforming ', file
- ].join('')));
-
- var r = require(res);
- if (typeof r !== 'function') {
- return cb(new Error('transform not a function'));
- }
-
- var trs = r(file, trOpts);
- output.emit('transform', trs, file);
- cb(null, trs);
- });
+ if (Array.isArray(this.options.noParse)
+ && this.options.noParse.indexOf(file) >= 0) {
+ return [];
}
+
+ try { var deps = detective(src) }
+ catch (ex) {
+ return
+ var message = ex && ex.message ? ex.message : ex;
+ return output.emit('error', new Error(
+ 'Parsing file ' + file + ': ' + message
+ ));
+ }
+ return deps;
};
-function lookupPkg (file, cb) {
+Deps.prototype.lookupPackage = function (file, cb) {
+ var self = this;
+
+ var id = path.resolve(this.basedir, file);
+ var cached = this.pkgCache[id];
+ if (cached) return process.nextTick(function () { cb(null, cached) });
+
var dirs = parents(path.dirname(file));
(function next () {
if (dirs.length === 0) return cb(null, undefined);
var dir = dirs.shift();
var pkgfile = path.join(dir, 'package.json');
+
+ var cached = self.pkgFileCachePending[pkgfile];
+ if (cached) return cached.push(onpkg);
+ cached = self.pkgFileCachePending[pkgfile] = [];
+
fs.readFile(pkgfile, function (err, src) {
- if (err) return next();
+ if (err) return onpkg();
try { var pkg = JSON.parse(src) }
catch (err) {
- return cb(new Error([
+ return onpkg(new Error([
err + ' while parsing json file ' + pkgfile
].join('')))
}
pkg.__dirname = dir;
- cb(null, pkg);
+
+ self.pkgCache[id] = pkg;
+ onpkg(null, pkg);
});
+
+ function onpkg (err, pkg) {
+ if (self.pkgFileCachePending[pkgfile]) {
+ var fns = self.pkgFileCachePending[pkgfile];
+ delete self.pkgFileCachePending[pkgfile];
+ fns.forEach(function (f) { f(err, pkg) });
+ }
+ if (err) cb(err)
+ else if (pkg) cb(null, pkg)
+ else next()
+ }
})();
+};
+
+function getTransforms (pkg, opts) {
+ var trx = [];
+ if (opts.transformKey) {
+ var n = pkg;
+ var keys = opts.transformKey;
+ for (var i = 0; i < keys.length; i++) {
+ if (n && typeof n === 'object') n = n[keys[i]];
+ else break;
+ }
+ if (i === keys.length) {
+ trx = [].concat(n).filter(Boolean);
+ }
+ }
+ return trx.concat(opts.globalTransform || []);
+}
+
+function nextTick (cb) {
+ var args = [].slice.call(arguments, 1);
+ process.nextTick(function () { cb.apply(null, args) });
}
diff --git a/package.json b/package.json
index 1c57f21..ed868e2 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,8 @@
"minimist": "~0.0.5",
"parents": "0.0.2",
"resolve": "~0.6.0",
- "through": "~2.3.4"
+ "through": "~2.3.4",
+ "duplexer2": "0.0.2"
},
"devDependencies": {
"tape": "~2.3.2",
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-module-deps.git
More information about the Pkg-javascript-commits
mailing list