diff --git a/packages/babel-helper-create-class-features-plugin/src/fields.js b/packages/babel-helper-create-class-features-plugin/src/fields.js index 4c441108c9dd..908d0b133062 100644 --- a/packages/babel-helper-create-class-features-plugin/src/fields.js +++ b/packages/babel-helper-create-class-features-plugin/src/fields.js @@ -672,7 +672,7 @@ const thisContextVisitor = traverse.visitors.merge([ function replaceThisContext( path, ref, - superRef, + getSuperRef, file, isStaticBlock, constantSuper, @@ -682,9 +682,9 @@ function replaceThisContext( const replacer = new ReplaceSupers({ methodPath: path, constantSuper, - superRef, file, refToPreserve: ref, + getSuperRef, getObjectRef() { state.needsClassRef = true; return isStaticBlock || path.node.static @@ -710,11 +710,20 @@ export function buildFieldsInitNodes( constantSuper, ) { let needsClassRef = false; + let injectSuperRef; const staticNodes = []; const instanceNodes = []; // These nodes are pure and can be moved to the closest statement position const pureStaticNodes = []; + const getSuperRef = t.isIdentifier(superRef) + ? () => superRef + : () => { + injectSuperRef ??= + props[0].scope.generateUidIdentifierBasedOnNode(superRef); + return injectSuperRef; + }; + for (const prop of props) { ts.assertFieldTransformed(prop); @@ -730,7 +739,7 @@ export function buildFieldsInitNodes( const replaced = replaceThisContext( prop, ref, - superRef, + getSuperRef, state, isStaticBlock, constantSuper, @@ -865,6 +874,14 @@ export function buildFieldsInitNodes( prop.remove(); } + if (injectSuperRef) { + path.scope.push({ id: t.cloneNode(injectSuperRef) }); + path.set( + "superClass", + t.assignmentExpression("=", injectSuperRef, path.node.superClass), + ); + } + if (!needsClassRef) return path; if (path.isClassExpression()) { diff --git a/packages/babel-helper-replace-supers/src/index.ts b/packages/babel-helper-replace-supers/src/index.ts index 12591ad49a2a..5fd6d4bc1022 100644 --- a/packages/babel-helper-replace-supers/src/index.ts +++ b/packages/babel-helper-replace-supers/src/index.ts @@ -206,22 +206,20 @@ const looseHandlers = { }, get(superMember) { - const { isStatic, superRef } = this; + const { isStatic, getSuperRef } = this; const { computed } = superMember.node; const prop = this.prop(superMember); let object; if (isStatic) { - object = superRef - ? t.cloneNode(superRef) - : t.memberExpression( - t.identifier("Function"), - t.identifier("prototype"), - ); + object = + getSuperRef() ?? + t.memberExpression(t.identifier("Function"), t.identifier("prototype")); } else { - object = superRef - ? t.memberExpression(t.cloneNode(superRef), t.identifier("prototype")) - : t.memberExpression(t.identifier("Object"), t.identifier("prototype")); + object = t.memberExpression( + getSuperRef() ?? t.identifier("Object"), + t.identifier("prototype"), + ); } return t.memberExpression(object, prop, computed); @@ -264,15 +262,15 @@ type ReplaceSupersOptionsBase = { refToPreserve?: t.Identifier; }; -type ReplaceSupersOptions = - | ({ - objectRef?: undefined; - getObjectRef: () => t.Node; - } & ReplaceSupersOptionsBase) - | ({ - objectRef: t.Node; - getObjectRef?: () => t.Node; - } & ReplaceSupersOptionsBase); +type ReplaceSupersOptions = ReplaceSupersOptionsBase & + ( + | { objectRef?: undefined; getObjectRef: () => t.Node } + | { objectRef: t.Node; getObjectRef?: undefined } + ) & + ( + | { superRef?: undefined; getSuperRef: () => t.Node } + | { superRef: t.Node; getSuperRef?: undefined } + ); export default class ReplaceSupers { constructor(opts: ReplaceSupersOptions) { @@ -286,7 +284,6 @@ export default class ReplaceSupers { this.isPrivateMethod = path.isPrivate() && path.isMethod(); this.file = opts.file; - this.superRef = opts.superRef; this.constantSuper = process.env.BABEL_8_BREAKING ? opts.constantSuper : // Fallback to isLoose for backward compatibility @@ -301,12 +298,16 @@ export default class ReplaceSupers { declare isStatic: boolean; declare methodPath: NodePath; declare opts: ReplaceSupersOptions; - declare superRef: any; getObjectRef() { return t.cloneNode(this.opts.objectRef || this.opts.getObjectRef()); } + getSuperRef() { + if (this.opts.superRef) return t.cloneNode(this.opts.superRef); + if (this.opts.getSuperRef) return t.cloneNode(this.opts.getSuperRef()); + } + replace() { // https://github.com/babel/babel/issues/11994 if (this.opts.refToPreserve) { @@ -324,7 +325,7 @@ export default class ReplaceSupers { isStatic: this.isStatic, isPrivateMethod: this.isPrivateMethod, getObjectRef: this.getObjectRef.bind(this), - superRef: this.superRef, + getSuperRef: this.getSuperRef.bind(this), ...handler, }); } diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/complex-super-class/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/complex-super-class/input.js new file mode 100644 index 000000000000..323295f7acb6 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/complex-super-class/input.js @@ -0,0 +1,3 @@ +class A extends class B {} { + static x = super.x; +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/complex-super-class/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/complex-super-class/output.js new file mode 100644 index 000000000000..ae773405c622 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/complex-super-class/output.js @@ -0,0 +1,5 @@ +var _B; + +class A extends (_B = class B {}) {} + +babelHelpers.defineProperty(A, "x", _B.x); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/instance-field/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/instance-field/input.js new file mode 100644 index 000000000000..754ad2b596c3 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/instance-field/input.js @@ -0,0 +1,3 @@ +class A extends B { + foo = super.bar; +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/instance-field/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/instance-field/output.js new file mode 100644 index 000000000000..442213516e1d --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/instance-field/output.js @@ -0,0 +1,7 @@ +class A extends B { + constructor(...args) { + super(...args); + babelHelpers.defineProperty(this, "foo", super.bar); + } + +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/options.json new file mode 100644 index 000000000000..237085211996 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/options.json @@ -0,0 +1,6 @@ +{ + "assumptions": { + "constantSuper": true + }, + "plugins": ["proposal-class-properties"] +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/static-field/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/static-field/input.js new file mode 100644 index 000000000000..682db35d854d --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/static-field/input.js @@ -0,0 +1,3 @@ +class A extends B { + static foo = super.bar; +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/static-field/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/static-field/output.js new file mode 100644 index 000000000000..83c70777daa8 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/assumption-constantSuper/static-field/output.js @@ -0,0 +1,3 @@ +class A extends B {} + +babelHelpers.defineProperty(A, "foo", B.bar); diff --git a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration-loose/super-static-block/output.js b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration-loose/super-static-block/output.js index ccbf240a5efa..ec5bf773773c 100644 --- a/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration-loose/super-static-block/output.js +++ b/packages/babel-plugin-proposal-class-static-block/test/fixtures/integration-loose/super-static-block/output.js @@ -1,17 +1,13 @@ -var _class, _temp; +var _ref, _class, _temp; -class Foo extends (_temp = _class = class {}, (() => { +class Foo extends (_ref = (_temp = _class = class _ref {}, (() => { _class.bar = 42; -})(), _temp) {} +})(), _temp)) {} Foo.bar = 21; (() => { - var _class2, _temp2; - - Foo.foo = (_temp2 = _class2 = class {}, (() => { - _class2.bar = 42; - })(), _temp2).bar; + Foo.foo = _ref.bar; })(); expect(Foo.foo).toBe(42);