diff --git a/package.json b/package.json index ecfc42a1..2e794d08 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "acorn-dynamic-import": "^4.0.0", "acorn-jsx": "^5.0.1", "chalk": "^2.4.2", - "magic-string": "^0.25.3", + "magic-string": "0.25.3", "minimist": "^1.2.0", "regexpu-core": "^4.5.4" } diff --git a/src/program/BlockStatement.js b/src/program/BlockStatement.js index b93cf267..fce1af69 100644 --- a/src/program/BlockStatement.js +++ b/src/program/BlockStatement.js @@ -16,6 +16,7 @@ export default class BlockStatement extends Node { this.scope = new Scope({ block: !this.isFunctionBlock, parent: this.parent.findScope(false), + parentType: this.parent.type, declare: id => this.createdDeclarations.push(id) }); diff --git a/src/program/Scope.js b/src/program/Scope.js index bb93d3e2..6fa430a3 100644 --- a/src/program/Scope.js +++ b/src/program/Scope.js @@ -5,6 +5,7 @@ export default function Scope(options) { options = options || {}; this.parent = options.parent; + this.parentType = options.parentType; this.isBlockScope = !!options.block; this.createDeclarationCallback = options.declare; @@ -108,6 +109,23 @@ Scope.prototype = { ); }, + findDeclarationScope(name, initialScope = this) { + if ( + this.declarations[name] || + (this.blockScopedDeclarations && this.blockScopedDeclarations[name]) || + // Creation of new lexical environment for `catch` block: https://github.com/bublejs/buble/pull/240 + this.parentType === 'CatchClause' + ) { + return this; + } + + if (this.parent) { + return this.parent.findDeclarationScope(name, initialScope); + } + + return initialScope; + }, + // Sometimes, block scope declarations change name during transpilation resolveName(name) { const declaration = this.findDeclaration(name); diff --git a/src/program/types/ForInStatement.js b/src/program/types/ForInStatement.js index c368c019..36674788 100644 --- a/src/program/types/ForInStatement.js +++ b/src/program/types/ForInStatement.js @@ -10,6 +10,7 @@ export default class ForInStatement extends LoopStatement { this.scope = new Scope({ block: true, parent: this.parent.findScope(false), + parentType: this.parent.type, declare: id => this.createdDeclarations.push(id) }); diff --git a/src/program/types/ForOfStatement.js b/src/program/types/ForOfStatement.js index 7dcad961..9ec25e4d 100644 --- a/src/program/types/ForOfStatement.js +++ b/src/program/types/ForOfStatement.js @@ -15,6 +15,7 @@ export default class ForOfStatement extends LoopStatement { this.scope = new Scope({ block: true, parent: this.parent.findScope(false), + parentType: this.parent.type, declare: id => this.createdDeclarations.push(id) }); diff --git a/src/program/types/ForStatement.js b/src/program/types/ForStatement.js index 617a3884..fffeb8ce 100644 --- a/src/program/types/ForStatement.js +++ b/src/program/types/ForStatement.js @@ -9,6 +9,7 @@ export default class ForStatement extends LoopStatement { this.scope = new Scope({ block: true, parent: this.parent.findScope(false), + parentType: this.parent.type, declare: id => this.createdDeclarations.push(id) }); diff --git a/src/program/types/Identifier.js b/src/program/types/Identifier.js index 5aa78784..1f46b2c0 100644 --- a/src/program/types/Identifier.js +++ b/src/program/types/Identifier.js @@ -12,7 +12,7 @@ export default class Identifier extends Node { return this.parent.body.scope; } - return this.parent.findScope(functionScope); + return this.parent.findScope(functionScope).findDeclarationScope(this.name); } initialise(transforms) { diff --git a/test/samples/block-scoping.js b/test/samples/block-scoping.js index a1b0bf99..eff45a36 100644 --- a/test/samples/block-scoping.js +++ b/test/samples/block-scoping.js @@ -642,5 +642,62 @@ module.exports = [ var x = 1; for (var x$1 in { x: x$1 }) {} ` + }, + + { + description: 'deconflicts blocks in switch-statement and parent function scope', + options: { transforms: { letConst: true } }, + input: ` + function foo() { + const n = 1; + for (let i = 0; i < n; i++) { + const o = n; + switch (n) { + case 1: + const n = 12; + console.log(n); + break; + } + } + } + `, + output:` + function foo() { + var n = 1; + for (var i = 0; i < n; i++) { + var o = n; + switch (n) { + case 1: + var n$1 = 12; + console.log(n$1); + break; + } + } + } + ` + }, + { + description: 'deconflicts blocks in class declaration and for statement', + options: { transforms: { letConst: true, classes: false } }, + input: ` + class e { + run() { + if(this instanceof e) { + for(let e = 0; e < 1; e++) console.log('here'); + } + } + } + new e().run(); + `, + output:` + class e { + run() { + if(this instanceof e) { + for(var e$1 = 0; e$1 < 1; e$1++) { console.log('here'); } + } + } + } + new e().run(); + ` } ];