Merge pull request #3389 from babel/block-binding-actual
Update scope binding info after transforming block-scoped bindings
This commit is contained in:
@@ -13,7 +13,7 @@ export default function () {
|
||||
VariableDeclaration(path, file) {
|
||||
let { node, parent, scope } = path;
|
||||
if (!isBlockScoped(node)) return;
|
||||
convertBlockScopedToVar(node, parent, scope);
|
||||
convertBlockScopedToVar(path, parent, scope, true);
|
||||
|
||||
if (node._tdzThis) {
|
||||
let nodes = [node];
|
||||
@@ -69,7 +69,8 @@ function isBlockScoped(node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function convertBlockScopedToVar(node, parent, scope) {
|
||||
function convertBlockScopedToVar(path, parent, scope, moveBindingsToParent = false) {
|
||||
const { node } = path;
|
||||
// https://github.com/babel/babel/issues/255
|
||||
if (!t.isFor(parent)) {
|
||||
for (let i = 0; i < node.declarations.length; i++) {
|
||||
@@ -80,6 +81,20 @@ function convertBlockScopedToVar(node, parent, scope) {
|
||||
|
||||
node[t.BLOCK_SCOPED_SYMBOL] = true;
|
||||
node.kind = "var";
|
||||
|
||||
// Move bindings from current block scope to function scope.
|
||||
if (moveBindingsToParent) {
|
||||
const parentScope = scope.getFunctionParent();
|
||||
if (parentScope === scope) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ids = path.getBindingIdentifiers();
|
||||
for (let name in ids) {
|
||||
scope.removeOwnBinding(name);
|
||||
}
|
||||
parentScope.registerBinding("var", path);
|
||||
}
|
||||
}
|
||||
|
||||
function isVar(node) {
|
||||
@@ -284,6 +299,7 @@ class BlockScoping {
|
||||
this.outsideLetReferences = Object.create(null);
|
||||
this.hasLetReferences = false;
|
||||
this.letReferences = Object.create(null);
|
||||
this.letReferencesDeclars = [];
|
||||
this.body = [];
|
||||
|
||||
if (loopPath) {
|
||||
@@ -327,6 +343,8 @@ class BlockScoping {
|
||||
let letRefs = this.letReferences;
|
||||
let scope = this.scope;
|
||||
|
||||
const parentScope = scope.getFunctionParent();
|
||||
|
||||
// alright, so since we aren't wrapping this block in a closure
|
||||
// we have to check if any of our let variables collide with
|
||||
// those in upper scopes and then if they do, generate a uid
|
||||
@@ -349,6 +367,18 @@ class BlockScoping {
|
||||
uid: uid
|
||||
};
|
||||
}
|
||||
|
||||
// Remove binding from block scope so it's moved to function scope.
|
||||
if (parentScope !== scope) {
|
||||
scope.removeOwnBinding(ref.name);
|
||||
}
|
||||
}
|
||||
|
||||
// Move bindings to parent scope.
|
||||
if (parentScope !== scope) {
|
||||
for (let declar of (this.letReferencesDeclars: Array<Object>)) {
|
||||
parentScope.registerBinding("var", declar);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasRemaps) return;
|
||||
@@ -499,7 +529,11 @@ class BlockScoping {
|
||||
for (let i = 0; i < block.body.length; i++) {
|
||||
let declar = block.body[i];
|
||||
if (t.isClassDeclaration(declar) || t.isFunctionDeclaration(declar) || isBlockScoped(declar)) {
|
||||
if (isBlockScoped(declar)) convertBlockScopedToVar(declar, block, this.scope);
|
||||
let declarPath = this.blockPath.get("body")[i];
|
||||
if (isBlockScoped(declar)) {
|
||||
convertBlockScopedToVar(declarPath, block, this.scope);
|
||||
this.letReferencesDeclars.push(declarPath);
|
||||
}
|
||||
declarators = declarators.concat(declar.declarations || declar);
|
||||
}
|
||||
}
|
||||
|
||||
39
packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/exec/scope-bindings.js
vendored
Normal file
39
packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/exec/scope-bindings.js
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
var code = [
|
||||
'var foo = 1;',
|
||||
'if (x) {',
|
||||
' const bar = 1;',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
var innerScope = true;
|
||||
var res = transform(code, {
|
||||
plugins: opts.plugins.concat([
|
||||
function (b) {
|
||||
var t = b.types;
|
||||
return {
|
||||
visitor: {
|
||||
Scope: {
|
||||
exit: function(path) {
|
||||
if (innerScope) {
|
||||
assert(Object.keys(path.scope.bindings).length === 0, 'Inner scope should not have any bindings');
|
||||
innerScope = false;
|
||||
return;
|
||||
}
|
||||
|
||||
assert(Object.keys(path.scope.bindings).length === 2, 'Outer scope subsume the inner-scope binding');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]),
|
||||
});
|
||||
|
||||
var expected = [
|
||||
'var foo = 1;',
|
||||
'if (x) {',
|
||||
' var bar = 1;',
|
||||
'}',
|
||||
].join('\n');
|
||||
|
||||
assert.equal(res.code, expected);
|
||||
@@ -3,9 +3,9 @@ let b = false;
|
||||
|
||||
switch (a) {
|
||||
case true:
|
||||
let b = 2;
|
||||
let c = 2;
|
||||
break;
|
||||
case false:
|
||||
let c = 3;
|
||||
let d = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ var b = false;
|
||||
|
||||
switch (a) {
|
||||
case true:
|
||||
var b = 2;
|
||||
var c = 2;
|
||||
break;
|
||||
case false:
|
||||
var c = 3;
|
||||
var d = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user