Comparing version 0.2.1 to 0.3.0
@@ -5,4 +5,7 @@ | ||
exports.Scope = require('./lib/scope'); | ||
exports.VariableScope = require('./lib/variable-scope'); | ||
exports.Assignment = require('./lib/assignment'); | ||
exports.Variable = require('./lib/variable'); | ||
exports.Reference = require('./lib/reference'); | ||
exports.utils = require('./lib/utils'); | ||
exports.scopes = require('./lib/scopes'); |
@@ -33,5 +33,6 @@ | ||
this.variable = scope.resolveVariable(this.left); | ||
if (this.variable) { | ||
this.variable.assignments.push(this); | ||
if (!this.variable) { | ||
this.variable = scope.undefined(this.left); | ||
} | ||
this.variable.assignments.push(this); | ||
} | ||
@@ -38,0 +39,0 @@ |
var estraverse = require('estraverse'); | ||
var Scope = require('./scope'); | ||
var scopes = require('./scopes'); | ||
@@ -11,3 +11,4 @@ | ||
// Create a top level scope. | ||
topScope = scope = new Scope(); | ||
topScope = scope = scopes.create(); | ||
scope.hoist(ast); | ||
} | ||
@@ -17,7 +18,10 @@ | ||
enter: function(node, parent) { | ||
if (Scope.isRequired(node)) { | ||
scope = scope ? scope.createChild(node) : new Scope(node); | ||
if (scopes.isRequired(node)) { | ||
scope = scope ? scope.createChild(node) : scopes.create(node); | ||
if (!topScope) { | ||
topScope = scope; | ||
} | ||
if (scope.isVariableScope()) { | ||
scope.hoist(); | ||
} | ||
} | ||
@@ -52,3 +56,3 @@ | ||
case 'CatchClause': | ||
scope.declare(node.param); | ||
scope.define(node.param); | ||
break; | ||
@@ -94,6 +98,6 @@ | ||
case 'FunctionDeclaration': | ||
scope.parent.resolveVariableScope().declare(node.id, node); | ||
scope.parent.assign(node); | ||
scope.parent.reference(node.id); | ||
node.params.forEach(function(param) { | ||
scope.declare(param) | ||
scope.define(param); | ||
}); | ||
@@ -104,3 +108,3 @@ break; | ||
node.params.forEach(function(param) { | ||
scope.declare(param) | ||
scope.define(param); | ||
}); | ||
@@ -182,4 +186,4 @@ break; | ||
case 'VariableDeclarator': | ||
scope.resolveVariableScope().declare(node.id, node.init ? node : null); | ||
if (node.init) { | ||
scope.assign(node); | ||
scope.reference(node.id); | ||
@@ -200,3 +204,3 @@ scope.reference(node.init); | ||
leave: function(node, parent) { | ||
if (Scope.isRequired(node)) { | ||
if (scopes.isRequired(node)) { | ||
scope = scope.parent; | ||
@@ -203,0 +207,0 @@ } |
var Variable = require('./variable'); | ||
var utils = require('./utils'); | ||
@@ -13,25 +14,13 @@ | ||
this.scope = scope; | ||
this.id = Reference.extractId(node); | ||
this.variable = scope.resolveVariable(Reference.extractId(node)); | ||
if (this.variable) { | ||
this.variable.references.push(this); | ||
this.id = utils.extractId(node); | ||
this.variable = scope.resolveVariable(this.id); | ||
if (!this.variable) { | ||
this.variable = scope.undefined(this.id); | ||
} | ||
this.variable.references.push(this); | ||
} | ||
Reference.extractId = function(node) { | ||
switch (node.type) { | ||
case 'Identifier': | ||
case 'ThisExpression': | ||
return node; | ||
case 'MemberExpression': | ||
return Reference.extractId(node.object); | ||
case 'CallExpression': | ||
case 'NewExpression': | ||
return Reference.extractId(node.callee); | ||
} | ||
}; | ||
Reference.isRequired = function(node) { | ||
return !!Reference.extractId(node); | ||
return !!utils.extractId(node); | ||
}; | ||
@@ -1,2 +0,1 @@ | ||
var estraverse = require('estraverse'); | ||
var Variable = require('./variable'); | ||
@@ -10,6 +9,2 @@ var Assignment = require('./assignment'); | ||
function Scope(node, parent) { | ||
if (node && !Scope.isRequired(node)) { | ||
throw new Error('Invalid node type: ' + node.type); | ||
} | ||
this.node = node; | ||
@@ -24,41 +19,26 @@ this.parent = parent; | ||
parent.children.push(this); | ||
} else { | ||
this.unscopedVariables = []; | ||
} | ||
} | ||
Scope.isRequired = function(node) { | ||
switch (node.type) { | ||
case 'FunctionExpression': | ||
case 'FunctionDeclaration': | ||
case 'Program': | ||
case 'CatchClause': | ||
case 'WithStatement': | ||
return true; | ||
} | ||
return false; | ||
}; | ||
Scope.prototype.createChild = function(node) { | ||
return new Scope(node, this); | ||
var scopes = require('./scopes'); | ||
return scopes.create(node, this); | ||
}; | ||
Scope.prototype.isVariableScope = function() { | ||
if (!this.node) return true; | ||
switch (this.node.type) { | ||
case 'FunctionExpression': | ||
case 'FunctionDeclaration': | ||
case 'Program': | ||
return true; | ||
} | ||
return false; | ||
}; | ||
Scope.prototype.declare = function(node, init) { | ||
var name = node.name; | ||
Scope.prototype.define = function(node) { | ||
var name; | ||
if (typeof node === 'string') { | ||
name = node; | ||
node = null; | ||
} else { | ||
name = node.name; | ||
} | ||
this.variables[name] = this.variables[name] || new Variable(node, this); | ||
if (init) { | ||
this.assign(init); | ||
} | ||
return this.variables[name] = this.variables[name] || new Variable(node || name, this); | ||
}; | ||
@@ -79,2 +59,30 @@ | ||
Scope.prototype.undefined = function(node) { | ||
var name; | ||
if (typeof node === 'string') { | ||
name = node; | ||
} else { | ||
switch (node.type) { | ||
case 'Identifier': | ||
name = node.name; | ||
break; | ||
case 'ThisExpression': | ||
name = 'this'; | ||
break; | ||
default: | ||
throw new Error('Invalid node type: ' + node.type); | ||
} | ||
} | ||
switch (name) { | ||
case 'this': | ||
case 'arguments': | ||
return this.resolveVariableScope().define(name); | ||
} | ||
var scope = this.ancestors().pop() || this; | ||
return scope.unscopedVariables[name] = scope.unscopedVariables[name] || new Variable(name); | ||
}; | ||
Scope.prototype.resolveVariable = function(name) { | ||
@@ -86,6 +94,6 @@ if (typeof name !== 'string') { | ||
var scope = this; | ||
while (scope && !(name in scope.variables)) { | ||
while (!(name in scope.variables) && scope.parent) { | ||
scope = scope.parent; | ||
} | ||
return scope ? scope.variables[name] : null; | ||
return scope.variables[name] || scope.unscopedVariables[name]; | ||
}; | ||
@@ -92,0 +100,0 @@ |
@@ -5,14 +5,24 @@ | ||
function Variable(node, scope) { | ||
switch (node.type) { | ||
case 'Identifier': | ||
case 'ThisExpression': | ||
break; | ||
default: | ||
throw new Error('Invalid node type: ' + node.type); | ||
var name; | ||
if ('string' === typeof node) { | ||
name = node; | ||
node = null; | ||
} else { | ||
switch (node.type) { | ||
case 'Identifier': | ||
case 'ThisExpression': | ||
name = node.name; | ||
break; | ||
default: | ||
throw new Error('Invalid node type: ' + node.type); | ||
} | ||
} | ||
this.name = name; | ||
this.node = node; | ||
this.scope = scope; | ||
this.declarations = []; | ||
this.assignments = []; | ||
this.references = []; | ||
} |
{ | ||
"name": "ast-scope", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"description": "A JavaScript AST scope analyzer", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
13240
14
414