diff --git a/packages/babel-plugin-minify-mangle-names/__tests__/mangle-names-test.js b/packages/babel-plugin-minify-mangle-names/__tests__/mangle-names-test.js index e38983089..5fa1c84f2 100644 --- a/packages/babel-plugin-minify-mangle-names/__tests__/mangle-names-test.js +++ b/packages/babel-plugin-minify-mangle-names/__tests__/mangle-names-test.js @@ -177,7 +177,7 @@ describe("mangle-names", () => { }); // https://phabricator.babeljs.io/T6957 - xit("labels should not shadow bindings", () => { + it("labels should not shadow bindings", () => { const source = unpad(` function foo() { var meh; @@ -201,6 +201,31 @@ describe("mangle-names", () => { expect(transform(source)).toBe(expected); }); + // https://github.com/babel/babili/issues/185 + it("labels should not shadow bindings 2", () => { + const source = unpad(` + function f(a) { + try { + a: { + console.log(a); + } + } catch ($a) {} + } + `); + + const expected = unpad(` + function f(b) { + try { + a: { + console.log(b); + } + } catch (c) {} + } + `); + + expect(transform(source)).toBe(expected); + }); + it("should be order independent", () => { const source = unpad(` function foo() { diff --git a/packages/babel-plugin-minify-mangle-names/src/index.js b/packages/babel-plugin-minify-mangle-names/src/index.js index 62222864a..4df3932a8 100644 --- a/packages/babel-plugin-minify-mangle-names/src/index.js +++ b/packages/babel-plugin-minify-mangle-names/src/index.js @@ -77,6 +77,68 @@ module.exports = ({ types: t }) => { mangle() { const mangler = this; + this.program.traverse({ + Scopable(path) { + const {scope} = path; + + const bindings = scope.getAllBindings(); + const names = Object.keys(bindings); + + for (let i = 0; i < names.length; i++) { + const oldName = names[i]; + const binding = bindings[oldName]; + + if (binding.path.isLabeledStatement()) { + const faulty = binding.referencePaths.filter((ref) => { + return !(ref.parentPath.isBreakStatement() || ref.parentPath.isContinueStatement()); + }); + faulty.forEach((f) => { + const index = binding.referencePaths.indexOf(f); + if (index > -1) { + binding.referencePaths.splice(index, 1); + binding.references--; + if (binding.references === 0) { + binding.referenced = false; + } + } + }); + + // probably really bad for labels + scope.removeBinding(oldName); + + const newBinding = scope.getBinding(oldName); + if (newBinding) { + // we found a binding in outer scopes + faulty.forEach((f) => newBinding.reference(f)); + } else { + // we might have a binding in the same scope + // slow + + // register binding + path.traverse({ + BindingIdentifier(bindingIdPath) { + if (bindingIdPath.parentPath.isLabeledStatement({ label: bindingIdPath.node })) { + return; + } + if (bindingIdPath.node.name === oldName) { + scope.registerDeclaration(bindingIdPath); + } + } + }); + + // update references + const registeredBinding = scope.getBinding(oldName); + if (!registeredBinding) { + // I'm not sure what this is - a global? + return; + } + faulty.forEach((f) => registeredBinding.reference(f)); + } + } + } + } + }); + this.program.traverse({ Scopable(path) { const {scope} = path;