[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