From a6cda9ae744c3dcba220d04b6215c2d8ffd8615f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Wed, 12 May 2021 01:16:13 +0200 Subject: [PATCH] Remove ordering constraints for `static-blocks` plugin --- .../src/index.js | 13 ++-- .../basic/input.js | 4 ++ .../basic/options.json | 4 ++ .../src/index.js | 59 +++++++++++-------- .../test/plugin-ordering.test.js | 48 ++++++++------- 5 files changed, 68 insertions(+), 60 deletions(-) create mode 100644 packages/babel-helper-create-class-features-plugin/test/fixtures/missing-class-static-blocks-plugin/basic/input.js create mode 100644 packages/babel-helper-create-class-features-plugin/test/fixtures/missing-class-static-blocks-plugin/basic/options.json diff --git a/packages/babel-helper-create-class-features-plugin/src/index.js b/packages/babel-helper-create-class-features-plugin/src/index.js index 2534a88132e8..1da251febd96 100644 --- a/packages/babel-helper-create-class-features-plugin/src/index.js +++ b/packages/babel-helper-create-class-features-plugin/src/index.js @@ -154,15 +154,10 @@ export function createClassFeaturePlugin({ if (!isDecorated) isDecorated = hasOwnDecorators(path.node); if (path.isStaticBlock?.()) { - throw path.buildCodeFrameError(`Incorrect plugin order, \`@babel/plugin-proposal-class-static-block\` should be placed before class features plugins -{ - "plugins": [ - "@babel/plugin-proposal-class-static-block", - "@babel/plugin-proposal-private-property-in-object", - "@babel/plugin-proposal-private-methods", - "@babel/plugin-proposal-class-properties", - ] -}`); + throw path.buildCodeFrameError( + "Compiling class fields and private methods requires compiling class static blocks." + + " Please add `@babel/plugin-proposal-class-static-block` to your Babel configuration.", + ); } } diff --git a/packages/babel-helper-create-class-features-plugin/test/fixtures/missing-class-static-blocks-plugin/basic/input.js b/packages/babel-helper-create-class-features-plugin/test/fixtures/missing-class-static-blocks-plugin/basic/input.js new file mode 100644 index 000000000000..9ecd90c8ed8f --- /dev/null +++ b/packages/babel-helper-create-class-features-plugin/test/fixtures/missing-class-static-blocks-plugin/basic/input.js @@ -0,0 +1,4 @@ +class A { + #x; + static {} +} diff --git a/packages/babel-helper-create-class-features-plugin/test/fixtures/missing-class-static-blocks-plugin/basic/options.json b/packages/babel-helper-create-class-features-plugin/test/fixtures/missing-class-static-blocks-plugin/basic/options.json new file mode 100644 index 000000000000..f8121360b7eb --- /dev/null +++ b/packages/babel-helper-create-class-features-plugin/test/fixtures/missing-class-static-blocks-plugin/basic/options.json @@ -0,0 +1,4 @@ +{ + "plugins": ["proposal-class-properties", "syntax-class-static-block"], + "throws": "Compiling class fields and private methods requires compiling class static blocks. Please add `@babel/plugin-proposal-class-static-block` to your Babel configuration." +} diff --git a/packages/babel-plugin-proposal-class-static-block/src/index.js b/packages/babel-plugin-proposal-class-static-block/src/index.js index e9eed3441d92..78460d1a04e4 100644 --- a/packages/babel-plugin-proposal-class-static-block/src/index.js +++ b/packages/babel-plugin-proposal-class-static-block/src/index.js @@ -26,32 +26,39 @@ export default declare(({ types: t, template, assertVersion }) => { name: "proposal-class-static-block", inherits: syntaxClassStaticBlock, visitor: { - Class(path: NodePath) { - const { scope } = path; - const classBody = path.get("body"); - const privateNames = new Set(); - const body = classBody.get("body"); - for (const path of body) { - if (path.isPrivate()) { - privateNames.add(path.get("key.id").node.name); - } - } - for (const path of body) { - if (!path.isStaticBlock()) continue; - const staticBlockPrivateId = generateUid(scope, privateNames); - privateNames.add(staticBlockPrivateId); - const staticBlockRef = t.privateName( - t.identifier(staticBlockPrivateId), - ); - path.replaceWith( - t.classPrivateProperty( - staticBlockRef, - template.expression.ast`(() => { ${path.node.body} })()`, - [], - /* static */ true, - ), - ); - } + Program(path) { + // We run this from the program visitor, so that when transforming private elements + // we are sure that static blocks have already been transformed regarless of the + // plugins order in the user config. + path.traverse({ + Class(path: NodePath) { + const { scope } = path; + const classBody = path.get("body"); + const privateNames = new Set(); + const body = classBody.get("body"); + for (const path of body) { + if (path.isPrivate()) { + privateNames.add(path.get("key.id").node.name); + } + } + for (const path of body) { + if (!path.isStaticBlock()) continue; + const staticBlockPrivateId = generateUid(scope, privateNames); + privateNames.add(staticBlockPrivateId); + const staticBlockRef = t.privateName( + t.identifier(staticBlockPrivateId), + ); + path.replaceWith( + t.classPrivateProperty( + staticBlockRef, + template.expression.ast`(() => { ${path.node.body} })()`, + [], + /* static */ true, + ), + ); + } + }, + }); }, }, }; diff --git a/packages/babel-plugin-proposal-class-static-block/test/plugin-ordering.test.js b/packages/babel-plugin-proposal-class-static-block/test/plugin-ordering.test.js index d32d3ffe93aa..fed75455a473 100644 --- a/packages/babel-plugin-proposal-class-static-block/test/plugin-ordering.test.js +++ b/packages/babel-plugin-proposal-class-static-block/test/plugin-ordering.test.js @@ -2,15 +2,15 @@ import * as babel from "@babel/core"; import proposalClassStaticBlock from ".."; describe("plugin ordering", () => { - it("should throw when @babel/plugin-proposal-class-static-block is after class features plugin", () => { + it("should work when @babel/plugin-proposal-class-static-block is after class features plugin", () => { const source = `class Foo { - static { - this.foo = Foo.bar; - } - static bar = 42; -} -`; - expect(() => { + static { + this.foo = Foo.bar; + } + static bar = 42; + } + `; + expect( babel.transform(source, { filename: "example.js", highlightCode: false, @@ -20,22 +20,20 @@ describe("plugin ordering", () => { "@babel/plugin-proposal-class-properties", proposalClassStaticBlock, ], - }); - }) - .toThrow(`Incorrect plugin order, \`@babel/plugin-proposal-class-static-block\` should be placed before class features plugins -{ - "plugins": [ - "@babel/plugin-proposal-class-static-block", - "@babel/plugin-proposal-private-property-in-object", - "@babel/plugin-proposal-private-methods", - "@babel/plugin-proposal-class-properties", - ] -} - 1 | class Foo { -> 2 | static { - | ^ - 3 | this.foo = Foo.bar; - 4 | } - 5 | static bar = 42;`); + }).code, + ).toMatchInlineSnapshot(` + "function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + + class Foo {} + + var _ = { + writable: true, + value: (() => { + Foo.foo = Foo.bar; + })() + }; + + _defineProperty(Foo, \\"bar\\", 42);" + `); }); });