From 573e2786ff4ac6a2823a4abf4c745f0831960a86 Mon Sep 17 00:00:00 2001 From: mdevils Date: Tue, 2 Dec 2014 18:59:16 +0300 Subject: [PATCH] Use estraverse in tree-iterator. --- lib/js-file.js | 45 ++++++------ lib/rules/require-multiple-var-decl.js | 6 +- lib/tree-iterator.js | 99 +++++++------------------- package.json | 1 + test/tree-iterator.js | 33 +++++++++ 5 files changed, 83 insertions(+), 101 deletions(-) create mode 100644 test/tree-iterator.js diff --git a/lib/js-file.js b/lib/js-file.js index cf2d6a2ae..f5bac07f1 100644 --- a/lib/js-file.js +++ b/lib/js-file.js @@ -26,25 +26,22 @@ var JsFile = function(filename, source, tree) { this._buildTokenIndex(); this.iterate(function(node, parentNode, parentCollection) { - if (node) { - var type = node.type; - if (type) { - node.parentNode = parentNode; - node.parentCollection = parentCollection; - (index[type] || (index[type] = [])).push(node); + var type = node.type; - // Temporary fix (i hope) for esprima tokenizer - // (https://code.google.com/p/esprima/issues/detail?id=481) - // Fixes #83, #180 - switch (type) { - case 'Property': - convertKeywordToIdentifierIfRequired(node.key); - break; - case 'MemberExpression': - convertKeywordToIdentifierIfRequired(node.property); - break; - } - } + node.parentNode = parentNode; + node.parentCollection = parentCollection; + (index[type] || (index[type] = [])).push(node); + + // Temporary fix (i hope) for esprima tokenizer + // (https://code.google.com/p/esprima/issues/detail?id=481) + // Fixes #83, #180 + switch (type) { + case 'Property': + convertKeywordToIdentifierIfRequired(node.key); + break; + case 'MemberExpression': + convertKeywordToIdentifierIfRequired(node.property); + break; } }); @@ -285,13 +282,11 @@ JsFile.prototype = { getNodeByRange: function(number) { var result = {}; this.iterate(function(node) { - if (node && node.range) { - if (number > node.range[0] && number < node.range[1]) { - result = node; - } - if (number < node.range[0]) { - return false; - } + if (number > node.range[0] && number < node.range[1]) { + result = node; + } + if (number < node.range[0]) { + return false; } }); return result; diff --git a/lib/rules/require-multiple-var-decl.js b/lib/rules/require-multiple-var-decl.js index b8c37e684..71d9d5161 100644 --- a/lib/rules/require-multiple-var-decl.js +++ b/lib/rules/require-multiple-var-decl.js @@ -22,11 +22,11 @@ function onevar(file, errors) { var firstParent = true; file.iterate(function(node) { - var type = node && node.type; - var kind = node && node.kind; + var type = node.type; + var kind = node.kind; // Don't go in nested scopes - if (!firstParent && type && ['FunctionDeclaration', 'FunctionExpression'].indexOf(type) > -1) { + if (!firstParent && ['FunctionDeclaration', 'FunctionExpression'].indexOf(type) > -1) { return false; } diff --git a/lib/tree-iterator.js b/lib/tree-iterator.js index 60172b17b..ebc9f8a79 100644 --- a/lib/tree-iterator.js +++ b/lib/tree-iterator.js @@ -1,83 +1,36 @@ +var estraverse = require('estraverse'); + module.exports = { iterate: iterate }; -var iterableProperties = { - 'body': true, - 'expression': true, - - // if - 'test': true, - 'consequent': true, - 'alternate': true, - - 'object': true, - - //switch - 'discriminant': true, - 'cases': true, - - // return - 'argument': true, - 'arguments': true, - - // try - 'block': true, - 'guardedHandlers': true, - 'handlers': true, - 'finalizer': true, - - // catch - 'handler': true, - - // for - 'init': true, - 'update': true, - - // for in - 'left': true, - 'right': true, - - // var - 'declarations': true, - - // array - 'elements': true, - - // object - 'properties': true, - 'key': true, - 'value': true, - - // new - 'callee': true, - - // export (ES6) - 'declaration': true, - - // xxx.yyy - 'property': true -}; - -function iterate(node, cb, parentNode, parentCollection) { - if (cb(node, parentNode, parentCollection) === false) { - return; - } - - for (var propName in node) { - if (node.hasOwnProperty(propName)) { - if (iterableProperties[propName]) { - var contents = node[propName]; - if (typeof contents === 'object') { - if (Array.isArray(contents)) { - for (var i = 0, l = contents.length; i < l; i++) { - iterate(contents[i], cb, node, contents); +function iterate(node, cb) { + if ('type' in node) { + estraverse.traverse(node, { + enter: function(node, parent) { + var parentCollection = []; + + // parentCollection support + var path = this.path(); + if (path) { + var collectionKey; + while (path.length > 0) { + var pathElement = path.pop(); + if (typeof pathElement === 'string') { + collectionKey = pathElement; } - } else { - iterate(contents, cb, node, [contents]); } + + parentCollection = parent[collectionKey]; + if (!Array.isArray(parentCollection)) { + parentCollection = [parentCollection]; + } + } + + if (cb(node, parent, parentCollection) === false) { + return estraverse.VisitorOption.Skip; } } - } + }); } } diff --git a/package.json b/package.json index 77525cc8b..c0860fba8 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "commander": "~2.5.0", "esprima": "~1.2.2", "esprima-harmony-jscs": "1.1.0-dev-harmony", + "estraverse": "~1.8.0", "exit": "~0.1.2", "glob": "~4.0.0", "minimatch": "~1.0.0", diff --git a/test/tree-iterator.js b/test/tree-iterator.js new file mode 100644 index 000000000..d714382a0 --- /dev/null +++ b/test/tree-iterator.js @@ -0,0 +1,33 @@ +var assert = require('assert'); +var esprima = require('esprima'); +var sinon = require('sinon'); +var iterate = require('../lib/tree-iterator').iterate; + +describe('modules/tree-iterator', function() { + + it('should pass parent and parentCollection', function() { + var spy = sinon.spy(); + iterate(esprima.parse('1;', {loc: true, range: true, comment: true, tokens: true}), spy); + assert.equal(spy.callCount, 3); + assert.equal(spy.getCall(0).args[0].type, 'Program'); + assert.equal(spy.getCall(0).args[1], null); + assert.equal(spy.getCall(1).args[0].type, 'ExpressionStatement'); + assert.equal(spy.getCall(2).args[0].type, 'Literal'); + }); + + it('should not fail for empty object', function() { + var spy = sinon.spy(); + iterate({}, spy); + assert.equal(spy.callCount, 0); + }); + + it('should exit thread on false result', function() { + var spy = sinon.spy(function() { + return false; + }); + iterate(esprima.parse('1;', {loc: true, range: true, comment: true, tokens: true}), spy); + assert.equal(spy.callCount, 1); + assert.equal(spy.getCall(0).args[0].type, 'Program'); + }); + +});