diff --git a/packages/babel-plugin-transform-es2015-block-scoping/README.md b/packages/babel-plugin-transform-es2015-block-scoping/README.md index ee1d426600b9..2890f38d0942 100644 --- a/packages/babel-plugin-transform-es2015-block-scoping/README.md +++ b/packages/babel-plugin-transform-es2015-block-scoping/README.md @@ -14,12 +14,26 @@ npm install --save-dev babel-plugin-transform-es2015-block-scoping **.babelrc** +Without options: + ```json { "plugins": ["transform-es2015-block-scoping"] } ``` +With options: + +```json +{ + "plugins": [ + ["transform-es2015-block-scoping", { + "throwIfClosureRequired": true + }] + ] +} +``` + ### Via CLI ```sh @@ -33,3 +47,15 @@ require("babel-core").transform("code", { plugins: ["transform-es2015-block-scoping"] }); ``` + +## Options `throwIfClosureRequired` + +In cases such as the following it's impossible to rewrite let/const without adding an additional function and closure while transforming: + +```javascript +for (let i = 0; i < 5; i++) { + setTimeout(() => console.log(i), 1); +} +``` + +In extremely performance-sensitive code, this can be undesirable. If `"throwIfClosureRequired": true` is set, Babel throws when transforming these patterns instead of automatically adding an additional function. diff --git a/packages/babel-plugin-transform-es2015-block-scoping/src/index.js b/packages/babel-plugin-transform-es2015-block-scoping/src/index.js index 19a36f558c16..37943664fbf0 100644 --- a/packages/babel-plugin-transform-es2015-block-scoping/src/index.js +++ b/packages/babel-plugin-transform-es2015-block-scoping/src/index.js @@ -362,6 +362,12 @@ class BlockScoping { } wrapClosure() { + if (this.file.opts.throwIfClosureRequired) { + throw this.blockPath.buildCodeFrameError( + "Compiling let/const in this block would add a closure " + + "(throwIfClosureRequired)." + ); + } const block = this.block; const outsideRefs = this.outsideLetReferences; diff --git a/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/for-const-closure/actual.js b/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/for-const-closure/actual.js new file mode 100644 index 000000000000..55dbb21d3764 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/for-const-closure/actual.js @@ -0,0 +1,6 @@ +for (let i = 0; i < 5; i++) { + const l = i; + setTimeout(function() { + console.log(l); + }, 1); +} diff --git a/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/for-const-closure/options.json b/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/for-const-closure/options.json new file mode 100644 index 000000000000..d210fdebfc97 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/for-const-closure/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Compiling let/const in this block would add a closure (throwIfClosureRequired)." +} diff --git a/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/function/actual.js b/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/function/actual.js new file mode 100644 index 000000000000..2b9635ec33a0 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/function/actual.js @@ -0,0 +1,3 @@ +function test() { + let foo = "bar"; +} diff --git a/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/function/expected.js b/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/function/expected.js new file mode 100644 index 000000000000..bb4b09c9c5b3 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/function/expected.js @@ -0,0 +1,3 @@ +function test() { + var foo = "bar"; +} diff --git a/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/options.json b/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/options.json new file mode 100644 index 000000000000..3228559d0b34 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/options.json @@ -0,0 +1,3 @@ +{ + "plugins": [["transform-es2015-block-scoping", { "throwIfClosureRequired": true }], "syntax-jsx", "transform-react-jsx", "transform-es2015-block-scoped-functions", "transform-es2015-arrow-functions"] +} diff --git a/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/superswitch/actual.js b/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/superswitch/actual.js new file mode 100644 index 000000000000..857b8641f415 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/superswitch/actual.js @@ -0,0 +1,16 @@ +function foo() { + switch (2) { + case 0: { + if (true) { + return; + } + + const stuff = new Map(); + const data = 0; + stuff.forEach(() => { + const d = data; + }); + break; + } + } +} diff --git a/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/superswitch/options.json b/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/superswitch/options.json new file mode 100644 index 000000000000..d210fdebfc97 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/superswitch/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Compiling let/const in this block would add a closure (throwIfClosureRequired)." +}