Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

replaced uglify-js with esprima #13

Closed
wants to merge 1 commit into from

4 participants

@matjaz

No description provided.

@substack
Owner

Do you have any figures about what this does to performance?

@matjaz

I've benchmarked /w browserify.

detective /w uglify-js

total detective time 86519
total compile time 1582

detective /w esprima

total detective time 80951
total compile time 1580

So we can say it's faster around 5-10%.

@substack
Owner

I just revisited this using a much bigger file and a quick esprima parser I wrote and the performance improvement is much more noteworthy:

> var src = require('fs').readFileSync('./node_modules/jquery-browserify/lib/jquery.js'); var find = require('./esp').find; var t0 = Date.now(); var res = find(src); Date.now() - t0
333

> var src = require('fs').readFileSync('./node_modules/jquery-browserify/lib/jquery.js'); var find = require('./').find; var t0 = Date.now(); var res = find(src); Date.now() - t0
1283

Nearly a 4x speedup.

@matjaz

Good to hear that. Esprima kicks ass.

@substack
Owner

The latest detective is now using esprima but I'm not seeing too much of a speedup in browserify since there's a lot of other caching going on that hides the performance gains. The browserify test suite does run a few seconds faster now at least.

@substack substack closed this
@ariya

+1

@tonysung

+1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 13, 2012
  1. @matjaz

    replaced uglify-js with esprima

    matjaz authored
This page is out of date. Refresh to see the latest.
Showing with 66 additions and 45 deletions.
  1. +58 −41 index.js
  2. +7 −3 package.json
  3. +1 −1  test/both.js
View
99 index.js
@@ -1,29 +1,6 @@
-var uglify = require('uglify-js');
-
-var traverse = function (node, cb, parent, grandparent) {
- // Call cb on all good AST nodes.
- if (Array.isArray(node) && node[0]
- && typeof node[0] === 'object' && node[0].name) {
- cb({ name : node[0].name, value : node.slice(1) , grandparent: grandparent});
- }
-
- // Traverse down the tree on arrays and objects.
- if (Array.isArray(node)
- || Object.prototype.toString.call(node) === "[object Object]") {
- for (var key in node) traverse(node[key], cb, node, parent);
- }
-};
+var parse = require('esprima').parse;
-var walk = function (src, cb) {
- var ast = uglify.parser.parse(src.toString(), false, true);
- traverse(ast, cb);
-};
-
-var deparse = function (ast) {
- return uglify.uglify.gen_code(ast);
-};
-
-var exports = module.exports = function (src, opts) {
+exports = module.exports = function (src, opts) {
return exports.find(src, opts).strings;
};
@@ -33,24 +10,64 @@ exports.find = function (src, opts) {
var modules = { strings : [], expressions : [] };
- if (src.toString().indexOf(word) == -1) return modules;
+ src = '' + src;
+ if (src.indexOf(word) == -1) return modules;
- walk(src, function (node) {
- var gp = node.grandparent;
- var isRequire = Array.isArray(gp)
- && gp[0]
- && (gp[0] === 'call' || gp[0].name === 'call')
- && gp[1][0] === 'name'
- && gp[1][1] === word
- ;
- if(isRequire) {
- if(node.name === 'string') {
- modules.strings.push(node.value[0]);
- } else {
- modules.expressions.push(deparse(gp[2][0]));
+ var ast = parse(src, {
+ range: true
+ });
+
+ var chunks = src.split('');
+
+ var node2String = function(node) {
+ if (node.range) {
+ return chunks.slice(node.range[0], node.range[1] + 1).join('');
+ } else {
+ return '';
+ }
+ };
+
+ var processAttr = function(node) {
+ switch (node.type) {
+ case 'Literal':
+ return node.value;
+ case 'BinaryExpression':
+ return processAttr(node.left) + processAttr(node.right);
+ default:
+ throw new Error('Invalid expression ' + node.type + ': ' + node2String(node));
+ }
+ };
+
+ var fn = function(node) {
+ if (node.type === 'CallExpression' && node.callee.name === word && node.callee.type === 'Identifier') {
+ var args = node['arguments'];
+ for (var i = 0, len = args.length; i < len; i++) {
+ var a = args[i];
+ try {
+ modules.strings.push(processAttr(a));
+ } catch (e) {
+ modules.expressions.push(node2String(a));
+ }
}
}
- });
-
+ };
+
+ var walk = function(node) {
+ Object.keys(node).forEach(function(key) {
+ var child = node[key];
+ if (Array.isArray(child)) {
+ child.forEach(function(c) {
+ if (c && typeof c === 'object' && c.type) {
+ walk(c, node);
+ }
+ });
+ } else if (child && typeof child === 'object' && child.type) {
+ walk(child, node);
+ }
+ });
+ fn(node);
+ };
+ walk(ast);
+
return modules;
};
View
10 package.json
@@ -1,7 +1,7 @@
{
"name" : "detective",
"description" : "Find all calls to require() no matter how crazily nested using a proper walk of the AST",
- "version" : "0.1.1",
+ "version" : "0.2.0",
"repository" : {
"type" : "git",
"url" : "git://github.com/substack/node-detective.git"
@@ -22,7 +22,7 @@
"test" : "tap test/*.js"
},
"dependencies" : {
- "uglify-js" : "~1.2.5"
+ "esprima" : "~0.9.9"
},
"devDependencies" : {
"tap" : "~0.2.3"
@@ -35,5 +35,9 @@
"name" : "James Halliday",
"email" : "mail@substack.net",
"url" : "http://substack.net"
- }
+ },
+ "contributors": [ {
+ "name": "Matjaž Lipuš",
+ "email": "matjazl@gmail.com"
+ } ]
}
View
2  test/both.js
@@ -6,6 +6,6 @@ var src = fs.readFileSync(__dirname + '/files/both.js');
test('both', function (t) {
var modules = detective.find(src);
t.deepEqual(modules.strings, [ 'a', 'b' ]);
- t.deepEqual(modules.expressions, [ '"c"+x', '"d"+y' ]);
+ t.deepEqual(modules.expressions, [ "'c'+x", "'d'+y" ]);
t.end();
});
Something went wrong with that request. Please try again.