ast-types
Advanced tools
Comparing version 0.3.7 to 0.3.8
@@ -27,3 +27,3 @@ var assert = require("assert"); | ||
if (Scope.isEstablishedBy(node)) { | ||
scope = new Scope(node, scope); | ||
scope = new Scope(this, scope); | ||
} | ||
@@ -30,0 +30,0 @@ |
@@ -9,5 +9,7 @@ var assert = require("assert"); | ||
function Scope(node, parentScope) { | ||
function Scope(path, parentScope) { | ||
assert.ok(this instanceof Scope); | ||
assert.ok(Scope.isEstablishedBy(node)); | ||
assert.ok(path instanceof require("./node-path")); | ||
ScopeType.assert(path.value); | ||
var depth; | ||
@@ -24,3 +26,4 @@ | ||
Object.defineProperties(this, { | ||
node: { value: node }, | ||
path: { value: path }, | ||
node: { value: path.value }, | ||
isGlobal: { value: !parentScope, enumerable: true }, | ||
@@ -68,3 +71,3 @@ depth: { value: depth }, | ||
} | ||
scanScope(this.node, this.bindings); | ||
scanScope(this.path, this.bindings); | ||
this.didScan = true; | ||
@@ -76,27 +79,45 @@ } | ||
// TODO What about CatchClause?? | ||
function scanScope(node, bindings) { | ||
function scanScope(path, bindings) { | ||
var node = path.value; | ||
ScopeType.assert(node); | ||
if (namedTypes.CatchClause.check(node)) { | ||
// A catch clause establishes a new scope but the only variable | ||
// bound in that scope is the catch parameter. Any other | ||
// declarations create bindings in the outer scope. | ||
addPattern(path.get("param"), bindings); | ||
} else { | ||
recursiveScanScope(path, bindings); | ||
} | ||
} | ||
function recursiveScanScope(path, bindings) { | ||
var node = path.value; | ||
if (isArray.check(node)) { | ||
node.forEach(function(child) { | ||
scanChild(child, bindings); | ||
path.each(function(childPath) { | ||
recursiveScanChild(childPath, bindings); | ||
}); | ||
} else if (namedTypes.Function.check(node)) { | ||
node.params.map(function(param) { | ||
addPattern(param, bindings); | ||
path.get("params").each(function(paramPath) { | ||
addPattern(paramPath, bindings); | ||
}); | ||
scanChild(node.body, bindings); | ||
recursiveScanChild(path.get("body"), bindings); | ||
} else if (namedTypes.VariableDeclarator.check(node)) { | ||
addPattern(node.id, bindings); | ||
scanChild(node.init, bindings); | ||
addPattern(path.get("id"), bindings); | ||
recursiveScanChild(path.get("init"), bindings); | ||
} else if (namedTypes.ImportSpecifier && | ||
namedTypes.ImportSpecifier.check(node)) { | ||
addPattern(node.name || node.id); | ||
addPattern(node.name ? path.get("name") : path.get("id")); | ||
} else if (Node.check(node)) { | ||
types.eachField(node, function(name, child) { | ||
scanChild(child, bindings); | ||
var childPath = path.get(name); | ||
assert.strictEqual(childPath.value, child); | ||
recursiveScanChild(childPath, bindings); | ||
}); | ||
@@ -106,26 +127,50 @@ } | ||
function scanChild(node, bindings) { | ||
function recursiveScanChild(path, bindings) { | ||
var node = path.value; | ||
if (namedTypes.FunctionDeclaration.check(node)) { | ||
addPattern(node.id, bindings); | ||
addPattern(path.get("id"), bindings); | ||
} else if (namedTypes.ClassDeclaration && | ||
namedTypes.ClassDeclaration.check(node)) { | ||
addPattern(node.id, bindings); | ||
addPattern(path.get("id"), bindings); | ||
} else if (Scope.isEstablishedBy(node)) { | ||
// Don't descend into nested scopes. | ||
if (namedTypes.CatchClause.check(node)) { | ||
var catchParamName = node.param.name; | ||
var hadBinding = hasOwn.call(bindings, catchParamName); | ||
// Any declarations that occur inside the catch body that do | ||
// not have the same name as the catch parameter should count | ||
// as bindings in the outer scope. | ||
recursiveScanScope(path.get("body"), bindings); | ||
// If a new binding matching the catch parameter name was | ||
// created while scanning the catch body, ignore it because it | ||
// actually refers to the catch parameter and not the outer | ||
// scope that we're currently scanning. | ||
if (!hadBinding) { | ||
delete bindings[catchParamName]; | ||
} | ||
} | ||
} else { | ||
scanScope(node, bindings); | ||
recursiveScanScope(path, bindings); | ||
} | ||
} | ||
function addPattern(pattern, bindings) { | ||
function addPattern(patternPath, bindings) { | ||
var pattern = patternPath.value; | ||
namedTypes.Pattern.assert(pattern); | ||
if (namedTypes.Identifier.check(pattern)) { | ||
bindings[pattern.name] = pattern; | ||
if (hasOwn.call(bindings, pattern.name)) { | ||
bindings[pattern.name].push(patternPath); | ||
} else { | ||
bindings[pattern.name] = [patternPath]; | ||
} | ||
} else if (namedTypes.SpreadElement && | ||
namedTypes.SpreadElement.check(pattern)) { | ||
addPattern(pattern.argument, bindings); | ||
addPattern(patternPath.get("argument"), bindings); | ||
} | ||
@@ -132,0 +177,0 @@ } |
@@ -21,3 +21,3 @@ { | ||
], | ||
"version": "0.3.7", | ||
"version": "0.3.8", | ||
"homepage": "http://github.com/benjamn/ast-types", | ||
@@ -24,0 +24,0 @@ "repository": { |
@@ -195,7 +195,7 @@ AST Types | ||
The `def` syntax is used to define all the default AST node types found in | ||
https://github.com/benjamn/ast-types/blob/master/lib/core.js, | ||
https://github.com/benjamn/ast-types/blob/master/lib/es6.js, | ||
https://github.com/benjamn/ast-types/blob/master/lib/mozilla.js, | ||
https://github.com/benjamn/ast-types/blob/master/lib/e4x.js, and | ||
https://github.com/benjamn/ast-types/blob/master/lib/xjs.js, so you have | ||
[core.js](def/core.js), | ||
[es6.js](def/es6.js), | ||
[mozilla.js](def/mozilla.js), | ||
[e4x.js](def/e4x.js), and | ||
[fb-harmony.js](def/fb-harmony.js), so you have | ||
no shortage of examples to learn from. |
@@ -458,1 +458,46 @@ var types = require("../main"); | ||
}; | ||
var catchWithVarDecl = [ | ||
"function foo(e) {", | ||
" try {", | ||
" bar();", | ||
" } catch (e) {", | ||
" var f = e + 1;", | ||
" return function(g) {", | ||
" return e + g;", | ||
" };", | ||
" }", | ||
" return f;", | ||
"}" | ||
]; | ||
exports.testCatchScope = function(t, assert) { | ||
var path = new NodePath(parse(catchWithVarDecl.join("\n"))); | ||
var fooPath = path.get("body", 0); | ||
var fooScope = fooPath.scope; | ||
n.FunctionDeclaration.assert(fooScope.node); | ||
assert.strictEqual(fooScope.declares("e"), true); | ||
assert.strictEqual(fooScope.declares("f"), true); | ||
assert.strictEqual(fooScope.lookup("e"), fooScope); | ||
var catchPath = fooPath.get("body", "body", 0, "handler"); | ||
var catchScope = catchPath.scope; | ||
n.CatchClause.assert(catchScope.node); | ||
assert.strictEqual(catchScope.declares("e"), true); | ||
assert.strictEqual(catchScope.declares("f"), false); | ||
assert.strictEqual(catchScope.lookup("e"), catchScope); | ||
assert.strictEqual(catchScope.lookup("f"), fooScope); | ||
var closurePath = catchPath.get("body", "body", 1, "argument"); | ||
var closureScope = closurePath.scope; | ||
n.FunctionExpression.assert(closureScope.node); | ||
assert.strictEqual(closureScope.declares("e"), false); | ||
assert.strictEqual(closureScope.declares("f"), false); | ||
assert.strictEqual(closureScope.declares("g"), true); | ||
assert.strictEqual(closureScope.lookup("g"), closureScope); | ||
assert.strictEqual(closureScope.lookup("e"), catchScope); | ||
assert.strictEqual(closureScope.lookup("f"), fooScope); | ||
t.finish(); | ||
}; |
408497
11434