Permalink
Browse files

Proof-of-concept implementation of Package#findRequires

  • Loading branch information...
1 parent 078c491 commit 82bd11b29859181fe55a08d6aa5b1e191e95c9b1 @sstephenson committed Mar 4, 2011
Showing with 113 additions and 2 deletions.
  1. +65 −1 lib/stitch.js
  2. +48 −1 src/stitch.coffee
View
66 lib/stitch.js
@@ -43,7 +43,7 @@
this.compileCache = {};
}
Package.prototype.compile = function(callback) {
- return async.reduce(this.paths, {}, _.bind(this.gatherSourcesFromPath, this), __bind(function(err, sources) {
+ return this.gatherSources(__bind(function(err, sources) {
var filename, index, name, result, source, _ref;
if (err) {
return callback(err);
@@ -60,6 +60,67 @@
return callback(err, result);
}, this));
};
+ Package.prototype.findRequires = function(callback) {
+ return this.gatherSources(__bind(function(err, sources) {
+ var path, requires, source;
+ requires = [];
+ if (!err) {
+ for (path in sources) {
+ source = sources[path].source;
+ requires.push.apply(requires, this.findRequiresInSource(source));
+ }
+ }
+ return callback(err, _.uniq(requires));
+ }, this));
+ };
+ Package.prototype.findRequiresInSource = function(source) {
+ var ast, isStringConcatenation, parser, requires, stringFrom, uglify, walker, _ref;
+ _ref = require('uglify-js'), parser = _ref.parser, uglify = _ref.uglify;
+ requires = [];
+ ast = parser.parse(source);
+ walker = uglify.ast_walker();
+ isStringConcatenation = function(expr) {
+ var left, right;
+ if (expr[0] === 'binary' && expr[1] === '+') {
+ left = expr[2];
+ right = expr[3];
+ if (isStringConcatenation(left)) {
+ return true;
+ } else if (left[0] === 'string') {
+ return true;
+ } else if (right[0] === 'string') {
+ if (left[0] === 'dot') {
+ return true;
+ } else if (left[0] === 'call') {
+ return true;
+ }
+ }
+ }
+ };
+ stringFrom = function(expr) {
+ if (expr[0] === 'binary' && expr[1] === '+') {
+ return stringFrom(expr[2]) + stringFrom(expr[3]);
+ } else if (expr[0] === 'string') {
+ return expr[1];
+ } else {
+ return '*';
+ }
+ };
+ walker.with_walkers({
+ 'call': function(expr, args) {
+ if (expr[0] === 'name' && expr[1] === 'require' && args.length === 1) {
+ if (args[0][0] === 'string') {
+ return requires.push(args[0][1]);
+ } else if (isStringConcatenation(args[0])) {
+ return requires.push(stringFrom(args[0]));
+ }
+ }
+ }
+ }, function() {
+ return walker.walk(ast);
+ });
+ return requires;
+ };
Package.prototype.createServer = function() {
return __bind(function(req, res, next) {
return this.compile(function(err, source) {
@@ -80,6 +141,9 @@
});
}, this);
};
+ Package.prototype.gatherSources = function(callback) {
+ return async.reduce(this.paths, {}, _.bind(this.gatherSourcesFromPath, this), callback);
+ };
Package.prototype.gatherSourcesFromPath = function(sources, sourcePath, callback) {
return fs.stat(sourcePath, __bind(function(err, stat) {
if (err) {
View
49 src/stitch.coffee
@@ -35,7 +35,7 @@ exports.Package = class Package
@compileCache = {}
compile: (callback) ->
- async.reduce @paths, {}, _.bind(@gatherSourcesFromPath, @), (err, sources) =>
+ @gatherSources (err, sources) =>
return callback err if err
result = """
@@ -103,6 +103,50 @@ exports.Package = class Package
callback err, result
+ findRequires: (callback) ->
+ @gatherSources (err, sources) =>
+ requires = []
+ unless err
+ for path, {source} of sources
+ requires.push @findRequiresInSource(source)...
+ callback err, _.uniq requires
+
+ findRequiresInSource: (source) ->
+ {parser, uglify} = require 'uglify-js'
+ requires = []
+ ast = parser.parse source
+ walker = uglify.ast_walker()
+
+ isStringConcatenation = (expr) ->
+ if expr[0] is 'binary' and expr[1] is '+'
+ left = expr[2]
+ right = expr[3]
+ if isStringConcatenation left
+ true
+ else if left[0] is 'string'
+ true
+ else if right[0] is 'string'
+ left[0] in ['dot', 'call']
+
+ stringFrom = (expr) ->
+ if expr[0] is 'binary' and expr[1] is '+'
+ stringFrom(expr[2]) + stringFrom(expr[3])
+ else if expr[0] is 'string'
+ expr[1]
+ else
+ '*'
+
+ walker.with_walkers
+ 'call': (expr, args) ->
+ if expr[0] is 'name' and expr[1] is 'require' and args.length is 1
+ if args[0][0] is 'string'
+ requires.push args[0][1]
+ else if isStringConcatenation args[0]
+ requires.push stringFrom args[0]
+ , -> walker.walk ast
+
+ requires
+
createServer: ->
(req, res, next) =>
@compile (err, source) ->
@@ -116,6 +160,9 @@ exports.Package = class Package
res.end source
+ gatherSources: (callback) ->
+ async.reduce @paths, {}, _.bind(@gatherSourcesFromPath, @), callback
+
gatherSourcesFromPath: (sources, sourcePath, callback) ->
fs.stat sourcePath, (err, stat) =>
return callback err if err

0 comments on commit 82bd11b

Please sign in to comment.