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 9e9658370058..8813bcafa3b5 100644 --- a/packages/babel-plugin-proposal-class-static-block/src/index.js +++ b/packages/babel-plugin-proposal-class-static-block/src/index.js @@ -19,22 +19,6 @@ function generateUid(scope, denyList: Set) { return uid; } -/** - * Get private names defined in current class body - * - * @param {NodePath} classBody - * @returns {Set} A set of defined private names - */ -function getPrivateNames(classBody: NodePath): Set { - const privateNames = new Set(); - for (const path of classBody.get("body")) { - if (path.isPrivate()) { - privateNames.add(path.get("key.id").node.name); - } - } - return privateNames; -} - export default declare(({ types: t, template, assertVersion }) => { // todo remove this check after Babel 7.12.0 is published if (process.env.NODE_ENV !== "test") { @@ -45,21 +29,35 @@ export default declare(({ types: t, template, assertVersion }) => { name: "proposal-class-static-block", inherits: syntaxClassStaticBlock, visitor: { - StaticBlock(path: NodePath) { - const { parentPath: classBody, scope } = path; + Class(path: NodePath) { + const { scope } = path; + const classBody = path.get("body"); + const privateNames = new Set(); + let staticBlockPath; + for (const path of classBody.get("body")) { + if (path.isPrivate()) { + privateNames.add(path.get("key.id").node.name); + } else if (path.isStaticBlock()) { + staticBlockPath = path; + } + } + if (!staticBlockPath) { + return; + } const staticBlockRef = t.privateName( - t.identifier(generateUid(scope, getPrivateNames(classBody))), + t.identifier(generateUid(scope, privateNames)), ); classBody.pushContainer( "body", t.classPrivateProperty( staticBlockRef, - template.expression.ast`(() => { ${path.node.body} })()`, + template.expression.ast`(() => { ${staticBlockPath.node.body} })()`, [], /* static */ true, ), ); - path.remove(); + staticBlockPath.remove(); + path.requeue(); }, }, }; diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/class-static-block/non-static/exec.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/class-static-block/non-static/exec.js new file mode 100644 index 000000000000..1d8845c283de --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/class-static-block/non-static/exec.js @@ -0,0 +1,7 @@ +class Foo { + bar = 21; + static { + this.foo = this.bar; + } +} +expect(Foo.foo).toBe(undefined); diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-binding/exec.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-binding/exec.js new file mode 100644 index 000000000000..aea35fe5c6ec --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-binding/exec.js @@ -0,0 +1,7 @@ +class Foo { + static { + this.foo = Foo.bar; + } + static bar = 42; +} +expect(Foo.foo).toBe(42); diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-binding/input.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-binding/input.js new file mode 100644 index 000000000000..aea35fe5c6ec --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-binding/input.js @@ -0,0 +1,7 @@ +class Foo { + static { + this.foo = Foo.bar; + } + static bar = 42; +} +expect(Foo.foo).toBe(42); diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-binding/output.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-binding/output.js new file mode 100644 index 000000000000..810eeba965a4 --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-binding/output.js @@ -0,0 +1,10 @@ +class Foo {} + +babelHelpers.defineProperty(Foo, "bar", 42); +var _ = { + writable: true, + value: (() => { + Foo.foo = Foo.bar; + })() +}; +expect(Foo.foo).toBe(42); diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-declaration/exec.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-declaration/exec.js new file mode 100644 index 000000000000..a748d4ad1f2e --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-declaration/exec.js @@ -0,0 +1,7 @@ +class Foo { + static { + this.foo = this.bar; + } + static bar = 42; +} +expect(Foo.foo).toBe(42); diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-declaration/input.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-declaration/input.js new file mode 100644 index 000000000000..a748d4ad1f2e --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-declaration/input.js @@ -0,0 +1,7 @@ +class Foo { + static { + this.foo = this.bar; + } + static bar = 42; +} +expect(Foo.foo).toBe(42); diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-declaration/output.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-declaration/output.js new file mode 100644 index 000000000000..810eeba965a4 --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-declaration/output.js @@ -0,0 +1,10 @@ +class Foo {} + +babelHelpers.defineProperty(Foo, "bar", 42); +var _ = { + writable: true, + value: (() => { + Foo.foo = Foo.bar; + })() +}; +expect(Foo.foo).toBe(42); diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-expression/exec.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-expression/exec.js new file mode 100644 index 000000000000..719d9f22eb77 --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/class-expression/exec.js @@ -0,0 +1,6 @@ +class Foo { + static { + this.foo = 42; + } +} +expect(Foo.foo).toBe(42); diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/in-class-heritage/exec.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/in-class-heritage/exec.js new file mode 100644 index 000000000000..be708dae2b0d --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/in-class-heritage/exec.js @@ -0,0 +1,14 @@ +class Foo extends class extends class Base { + static { + this.qux = 21; + } +} { + static { + this.bar = 21; + } +} { + static { + this.foo = this.bar + this.qux; + } +} +expect(Foo.foo).toBe(42); diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/in-class-heritage/input.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/in-class-heritage/input.js new file mode 100644 index 000000000000..be708dae2b0d --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/in-class-heritage/input.js @@ -0,0 +1,14 @@ +class Foo extends class extends class Base { + static { + this.qux = 21; + } +} { + static { + this.bar = 21; + } +} { + static { + this.foo = this.bar + this.qux; + } +} +expect(Foo.foo).toBe(42); diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/in-class-heritage/output.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/in-class-heritage/output.js new file mode 100644 index 000000000000..ed5682822e11 --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/in-class-heritage/output.js @@ -0,0 +1,21 @@ +var _class, _temp, _2, _class2, _temp2, _3; + +class Foo extends (_temp = _class = class extends (_temp2 = _class2 = class Base {}, _3 = { + writable: true, + value: (() => { + _class2.qux = 21; + })() +}, _temp2) {}, _2 = { + writable: true, + value: (() => { + _class.bar = 21; + })() +}, _temp) {} + +var _ = { + writable: true, + value: (() => { + Foo.foo = Foo.bar + Foo.qux; + })() +}; +expect(Foo.foo).toBe(42); diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/multiple-static-initializers/exec.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/multiple-static-initializers/exec.js new file mode 100644 index 000000000000..657be074147c --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/multiple-static-initializers/exec.js @@ -0,0 +1,8 @@ +class Foo { + static #bar = 21; + static { + this.foo = this.#bar + this.qux; + } + static qux = 21; +} +expect(Foo.foo).toBe(42); diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/name-conflict/exec.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/name-conflict/exec.js new file mode 100644 index 000000000000..0de932f8d73a --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/name-conflict/exec.js @@ -0,0 +1,8 @@ +class Foo { + static #_ = 42; + // static block can not be tranformed as `#_` here + static { + this.foo = this.#_; + } +} +expect(Foo.foo).toBe(42); diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/name-conflict/input.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/name-conflict/input.js new file mode 100644 index 000000000000..0de932f8d73a --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/name-conflict/input.js @@ -0,0 +1,8 @@ +class Foo { + static #_ = 42; + // static block can not be tranformed as `#_` here + static { + this.foo = this.#_; + } +} +expect(Foo.foo).toBe(42); diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/name-conflict/output.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/name-conflict/output.js new file mode 100644 index 000000000000..afd334165910 --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/name-conflict/output.js @@ -0,0 +1,15 @@ +function _classStaticPrivateFieldSpecGet(receiver, classConstructor, descriptor) { if (receiver !== classConstructor) { throw new TypeError("Private static access of wrong provenance"); } if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; } + +class Foo {} + +var _ = { + writable: true, + value: 42 +}; +var _2 = { + writable: true, + value: (() => { + Foo.foo = _classStaticPrivateFieldSpecGet(Foo, Foo, _); + })() +}; +expect(Foo.foo).toBe(42); diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/non-static/exec.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/non-static/exec.js new file mode 100644 index 000000000000..1d8845c283de --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/non-static/exec.js @@ -0,0 +1,7 @@ +class Foo { + bar = 21; + static { + this.foo = this.bar; + } +} +expect(Foo.foo).toBe(undefined); diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/options.json b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/options.json new file mode 100644 index 000000000000..20511baf4fe1 --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/options.json @@ -0,0 +1,7 @@ +{ + "plugins": [ + "proposal-class-static-block", + "proposal-class-properties", + "external-helpers" + ] +} diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/private-elements-access/exec.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/private-elements-access/exec.js new file mode 100644 index 000000000000..07e589d487dc --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/private-elements-access/exec.js @@ -0,0 +1,8 @@ +let getFoo; +class Foo { + static #foo = 42; + static { + getFoo = () => this.#foo; + } +} +expect(getFoo()).toBe(42); diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/super-property/exec.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/super-property/exec.js new file mode 100644 index 000000000000..866e400fc377 --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration/super-property/exec.js @@ -0,0 +1,11 @@ +class Foo extends class { + static { + this.bar = 42; + } +} { + static bar = 21; + static { + this.foo = super.bar; + } +} +expect(Foo.foo).toBe(42); 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 new file mode 100644 index 000000000000..3839d8d72837 --- /dev/null +++ b/packages/babel-plugin-proposal-class-static-block/test/plugin-ordering.test.js @@ -0,0 +1,42 @@ +import * as babel from "@babel/core"; +import proposalClassStaticBlock from "../lib/index.js"; + +describe("plugin ordering", () => { + it("should throw 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(() => { + babel.transform(source, { + filename: "example.js", + highlightCode: false, + configFile: false, + babelrc: false, + plugins: [ + "@babel/plugin-proposal-class-properties", + proposalClassStaticBlock, + ], + }); + }).toThrowErrorMatchingInlineSnapshot(` +"/Users/jh/code/babel/example.js: Incorrect plugin orders, \`@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;" +`); + }); +});