diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index 4c07ab0991..de544eac11 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -627,6 +627,12 @@ helpers.temporalRef = defineHelper(` } `); +helpers.readOnlyError = defineHelper(` + export default function _readOnlyError(name) { + throw new Error("\\"" + name + "\\" is read-only"); + } +`); + helpers.temporalUndefined = defineHelper(` export default {}; `); diff --git a/packages/babel-plugin-check-constants/src/index.js b/packages/babel-plugin-check-constants/src/index.js index 2f6670a6d5..4c2d50430a 100644 --- a/packages/babel-plugin-check-constants/src/index.js +++ b/packages/babel-plugin-check-constants/src/index.js @@ -1,50 +1,37 @@ import { types as t } from "@babel/core"; export default function() { - /** - * Helper function to run a statement before an expression by replacing it with a comma expression - * and wrapping the statement in an IIFE as the first operand. - */ - function statementBeforeExpression(statement, expression) { - return t.sequenceExpression([ - t.callExpression( - t.functionExpression(null, [], t.blockStatement([statement])), - [], - ), - expression, - ]); - } - return { visitor: { - Scope({ scope }) { + Scope({ scope }, state) { for (const name in scope.bindings) { const binding = scope.bindings[name]; if (binding.kind !== "const") continue; for (const violation of (binding.constantViolations: Array)) { - const throwNode = t.throwStatement( - t.newExpression(t.identifier("Error"), [ - t.stringLiteral(`"${name}" is read-only`), - ]), - ); + const readOnlyError = state.addHelper("readOnlyError"); + const throwNode = t.callExpression(readOnlyError, [ + t.stringLiteral(name), + ]); if (violation.isAssignmentExpression()) { violation .get("right") .replaceWith( - statementBeforeExpression( + t.sequenceExpression([ throwNode, violation.get("right").node, - ), + ]), ); } else if (violation.isUpdateExpression()) { violation.replaceWith( - statementBeforeExpression(throwNode, violation.node), + t.sequenceExpression([throwNode, violation.node]), ); } else if (violation.isForXStatement()) { violation.ensureBlock(); - violation.node.body.body.unshift(throwNode); + violation.node.body.body.unshift( + t.expressionStatement(throwNode), + ); } } } diff --git a/packages/babel-plugin-check-constants/test/fixtures/general/deadcode-violation/expected.js b/packages/babel-plugin-check-constants/test/fixtures/general/deadcode-violation/expected.js index c81769f60c..cd1955d279 100644 --- a/packages/babel-plugin-check-constants/test/fixtures/general/deadcode-violation/expected.js +++ b/packages/babel-plugin-check-constants/test/fixtures/general/deadcode-violation/expected.js @@ -1,7 +1,7 @@ +function _readOnlyError(name) { throw new Error("\"" + name + "\" is read-only"); } + (function () { var a = "foo"; - if (false) a = (function () { - throw new Error("\"a\" is read-only"); - }(), "false"); + if (false) a = (_readOnlyError("a"), "false"); return a; })(); diff --git a/packages/babel-plugin-check-constants/test/fixtures/general/destructuring-assignment/expected.js b/packages/babel-plugin-check-constants/test/fixtures/general/destructuring-assignment/expected.js index 53fd9435f6..1c642d30f3 100644 --- a/packages/babel-plugin-check-constants/test/fixtures/general/destructuring-assignment/expected.js +++ b/packages/babel-plugin-check-constants/test/fixtures/general/destructuring-assignment/expected.js @@ -1,5 +1,5 @@ +function _readOnlyError(name) { throw new Error("\"" + name + "\" is read-only"); } + var a = 1, b = 2; -a = (function () { - throw new Error("\"a\" is read-only"); -}(), 3); +a = (_readOnlyError("a"), 3); diff --git a/packages/babel-plugin-check-constants/test/fixtures/general/loop/expected.js b/packages/babel-plugin-check-constants/test/fixtures/general/loop/expected.js index ca260e39f3..8c07b3afab 100644 --- a/packages/babel-plugin-check-constants/test/fixtures/general/loop/expected.js +++ b/packages/babel-plugin-check-constants/test/fixtures/general/loop/expected.js @@ -1,5 +1,5 @@ -for (var i = 0; i < 3; i = (function () { - throw new Error("\"i\" is read-only"); -}(), i + 1)) { +function _readOnlyError(name) { throw new Error("\"" + name + "\" is read-only"); } + +for (var i = 0; i < 3; i = (_readOnlyError("i"), i + 1)) { console.log(i); } diff --git a/packages/babel-plugin-check-constants/test/fixtures/general/nested-update-expressions/expected.js b/packages/babel-plugin-check-constants/test/fixtures/general/nested-update-expressions/expected.js index 5ff85cb84e..80e5f5ac11 100644 --- a/packages/babel-plugin-check-constants/test/fixtures/general/nested-update-expressions/expected.js +++ b/packages/babel-plugin-check-constants/test/fixtures/general/nested-update-expressions/expected.js @@ -1,8 +1,8 @@ +function _readOnlyError(name) { throw new Error("\"" + name + "\" is read-only"); } + var c = 17; var a = 0; function f() { - return (function () { - throw new Error("\"c\" is read-only"); - }(), ++c) + --a; + return (_readOnlyError("c"), ++c) + --a; } diff --git a/packages/babel-plugin-check-constants/test/fixtures/general/no-assignment/expected.js b/packages/babel-plugin-check-constants/test/fixtures/general/no-assignment/expected.js index 06e4d1b672..f6d05db787 100644 --- a/packages/babel-plugin-check-constants/test/fixtures/general/no-assignment/expected.js +++ b/packages/babel-plugin-check-constants/test/fixtures/general/no-assignment/expected.js @@ -1,4 +1,4 @@ +function _readOnlyError(name) { throw new Error("\"" + name + "\" is read-only"); } + var MULTIPLIER = 5; -MULTIPLIER = (function () { - throw new Error("\"MULTIPLIER\" is read-only"); -}(), "overwrite"); +MULTIPLIER = (_readOnlyError("MULTIPLIER"), "overwrite"); diff --git a/packages/babel-plugin-check-constants/test/fixtures/general/no-for-in/expected.js b/packages/babel-plugin-check-constants/test/fixtures/general/no-for-in/expected.js index eb57557844..30340cc04a 100644 --- a/packages/babel-plugin-check-constants/test/fixtures/general/no-for-in/expected.js +++ b/packages/babel-plugin-check-constants/test/fixtures/general/no-for-in/expected.js @@ -1,6 +1,9 @@ +function _readOnlyError(name) { throw new Error("\"" + name + "\" is read-only"); } + var MULTIPLIER = 5; for (MULTIPLIER in arr) { - throw new Error("\"MULTIPLIER\" is read-only"); + _readOnlyError("MULTIPLIER"); + ; } diff --git a/packages/babel-plugin-check-constants/test/fixtures/general/update-expression-prefix/expected.js b/packages/babel-plugin-check-constants/test/fixtures/general/update-expression-prefix/expected.js index f274ab5edf..b9991508ec 100644 --- a/packages/babel-plugin-check-constants/test/fixtures/general/update-expression-prefix/expected.js +++ b/packages/babel-plugin-check-constants/test/fixtures/general/update-expression-prefix/expected.js @@ -1,4 +1,4 @@ +function _readOnlyError(name) { throw new Error("\"" + name + "\" is read-only"); } + var a = "str"; -(function () { - throw new Error("\"a\" is read-only"); -})(), --a; +_readOnlyError("a"), --a; diff --git a/packages/babel-plugin-check-constants/test/fixtures/general/update-expression/expected.js b/packages/babel-plugin-check-constants/test/fixtures/general/update-expression/expected.js index da1ba3f075..392c434876 100644 --- a/packages/babel-plugin-check-constants/test/fixtures/general/update-expression/expected.js +++ b/packages/babel-plugin-check-constants/test/fixtures/general/update-expression/expected.js @@ -1,4 +1,4 @@ +function _readOnlyError(name) { throw new Error("\"" + name + "\" is read-only"); } + var foo = 1; -(function () { - throw new Error("\"foo\" is read-only"); -})(), foo++; +_readOnlyError("foo"), foo++;