From 85e52aa333716a3e0b4220201c7ad204678ff86d Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Sat, 28 Jan 2017 20:56:23 +0000 Subject: [PATCH] Add option to block-scoping to slow on throw code The let/const plugin can add closures where you don't expect them. This is undesirable in some perf-sensitive projects (ex: React). I added an option that throws whenever the plugin adds a function (as opposed to simply renaming variables when converting to var). --- .../README.md | 22 +++++++++++++++++++ .../src/index.js | 6 +++++ .../for-const-closure/actual.js | 6 +++++ .../for-const-closure/options.json | 3 +++ .../throwIfClosureRequired/function/actual.js | 3 +++ .../function/expected.js | 3 +++ .../throwIfClosureRequired/options.json | 3 +++ .../superswitch/actual.js | 16 ++++++++++++++ .../superswitch/options.json | 3 +++ 9 files changed, 65 insertions(+) create mode 100644 packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/for-const-closure/actual.js create mode 100644 packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/for-const-closure/options.json create mode 100644 packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/function/actual.js create mode 100644 packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/function/expected.js create mode 100644 packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/options.json create mode 100644 packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/superswitch/actual.js create mode 100644 packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/superswitch/options.json diff --git a/packages/babel-plugin-transform-es2015-block-scoping/README.md b/packages/babel-plugin-transform-es2015-block-scoping/README.md index ee1d426600b9..03ee439fc73a 100644 --- a/packages/babel-plugin-transform-es2015-block-scoping/README.md +++ b/packages/babel-plugin-transform-es2015-block-scoping/README.md @@ -15,9 +15,19 @@ npm install --save-dev babel-plugin-transform-es2015-block-scoping **.babelrc** ```json +// without options { "plugins": ["transform-es2015-block-scoping"] } + +// with options +{ + "plugins": [ + ["transform-es2015-block-scoping", { + "throwIfClosureRequired": true + }] + ] +} ``` ### Via CLI @@ -33,3 +43,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..190f9cd266c6 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( + "Variable declarations in this block can't be automatically rewritten " + + "without adding a closure -- refactor or avoid using let/const." + ); + } 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..98d6a42473dd --- /dev/null +++ b/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/for-const-closure/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Variable declarations in this block can't be automatically rewritten without adding a closure -- refactor or avoid using let/const." +} 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..98d6a42473dd --- /dev/null +++ b/packages/babel-plugin-transform-es2015-block-scoping/test/fixtures/throwIfClosureRequired/superswitch/options.json @@ -0,0 +1,3 @@ +{ + "throws": "Variable declarations in this block can't be automatically rewritten without adding a closure -- refactor or avoid using let/const." +}