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 76bc96b6d662..6e5062ea698a 100644 --- a/packages/babel-helper-create-class-features-plugin/src/fields.js +++ b/packages/babel-helper-create-class-features-plugin/src/fields.js @@ -241,6 +241,13 @@ const privateNameHandlerSpec = { ]); }, + boundGet(member) { + return t.callExpression( + t.memberExpression(this.get(member), t.identifier("bind")), + [this.receiver(member)], + ); + }, + set(member, value) { const { classRef, privateNamesMap, file } = this; const { name } = member.node.property.id; @@ -323,6 +330,13 @@ const privateNameHandlerLoose = { }); }, + boundGet(member) { + return t.callExpression( + t.memberExpression(this.get(member), t.identifier("bind")), + [member.node.object], + ); + }, + simpleSet(member) { return this.get(member); }, diff --git a/packages/babel-helper-member-expression-to-functions/src/index.js b/packages/babel-helper-member-expression-to-functions/src/index.js index 67d3621debc6..1d8114a94d85 100644 --- a/packages/babel-helper-member-expression-to-functions/src/index.js +++ b/packages/babel-helper-member-expression-to-functions/src/index.js @@ -167,6 +167,9 @@ const handle = { const parentIsOptionalCall = parentPath.isOptionalCallExpression({ callee: node, }); + const isParenthesizedMemberCall = + parentPath.isCallExpression({ callee: node }) && + node.extra?.parenthesized; startingOptional.replaceWith(toNonOptional(startingOptional, baseRef)); if (parentIsOptionalCall) { if (parent.optional) { @@ -174,6 +177,9 @@ const handle = { } else { parentPath.replaceWith(this.call(member, parent.arguments)); } + } else if (isParenthesizedMemberCall) { + // `(a?.#b)()` to `(a == null ? void 0 : a.#b.bind(a))()` + member.replaceWith(this.boundGet(member)); } else { member.replaceWith(this.get(member)); } diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/parenthesized-optional-member-call/exec.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/parenthesized-optional-member-call/exec.js new file mode 100644 index 000000000000..a6fb131dbd3f --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/parenthesized-optional-member-call/exec.js @@ -0,0 +1,36 @@ +class Foo { + static #x = 1; + + static self = Foo; + static #m = function() { return this.#x; }; + static getSelf() { return Foo } + + test() { + const o = { Foo: Foo }; + + expect((Foo?.#m)()).toEqual(1); + expect((Foo?.#m)().toString).toEqual(1..toString); + expect((Foo?.#m)().toString()).toEqual('1'); + + expect((o?.Foo.#m)()).toEqual(1); + expect((o?.Foo.#m)().toString).toEqual(1..toString); + expect((o?.Foo.#m)().toString()).toEqual('1'); + + expect((((o.Foo?.self.getSelf)())?.#m)()).toEqual(1); + expect((((o.Foo.self?.getSelf)())?.#m)()).toEqual(1); + } + + testNull() { + const o = null; + + expect(() => { (o?.Foo.#m)() }).toThrow(); + expect(() => { (o?.Foo.#m)().toString }).toThrow(); + expect(() => { (o?.Foo.#m)().toString() }).toThrow(); + + expect(() => { (((o.Foo?.self.getSelf)())?.#m)() }).toThrow(); + expect(() => { (((o.Foo.self?.getSelf)())?.#m)() }).toThrow(); + } +} + +(new Foo).test(); +(new Foo).testNull(); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/parenthesized-optional-member-call/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/parenthesized-optional-member-call/input.js new file mode 100644 index 000000000000..a2231680adfa --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/parenthesized-optional-member-call/input.js @@ -0,0 +1,24 @@ +class Foo { + static #x = 1; + + static self = Foo; + static #m = function() { return this.#x; }; + static getSelf() { return Foo } + + test() { + const o = { Foo: Foo }; + + (Foo?.#m)(); + (Foo?.#m)().toString; + (Foo?.#m)().toString(); + + (o?.Foo.#m)(); + (o?.Foo.#m)().toString; + (o?.Foo.#m)().toString(); + + (((o.Foo?.self.getSelf)())?.#m)(); + (((o.Foo.self?.getSelf)())?.#m)(); + } +} + +(new Foo).test(); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/parenthesized-optional-member-call/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/parenthesized-optional-member-call/options.json new file mode 100644 index 000000000000..5ef7bf85a640 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/parenthesized-optional-member-call/options.json @@ -0,0 +1,8 @@ +{ + "plugins": [ + ["external-helpers", { "helperVersion": "7.100.0" }], + ["proposal-class-properties", { "loose": true }], + "transform-classes", + "transform-block-scoping" + ] +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/parenthesized-optional-member-call/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/parenthesized-optional-member-call/output.js new file mode 100644 index 000000000000..2732e841ac91 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/parenthesized-optional-member-call/output.js @@ -0,0 +1,49 @@ +var Foo = /*#__PURE__*/function () { + "use strict"; + + function Foo() { + babelHelpers.classCallCheck(this, Foo); + } + + babelHelpers.createClass(Foo, [{ + key: "test", + value: function test() { + var _o$Foo$self$getSelf, _o$Foo$self$getSelf2; + + var o = { + Foo: Foo + }; + (Foo === null || Foo === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(Foo, _m)[_m].bind(Foo))(); + (Foo === null || Foo === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(Foo, _m)[_m].bind(Foo))().toString; + (Foo === null || Foo === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(Foo, _m)[_m].bind(Foo))().toString(); + (o === null || o === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(o.Foo, _m)[_m].bind(o.Foo))(); + (o === null || o === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(o.Foo, _m)[_m].bind(o.Foo))().toString; + (o === null || o === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(o.Foo, _m)[_m].bind(o.Foo))().toString(); + ((_o$Foo$self$getSelf = (o.Foo?.self.getSelf)()) === null || _o$Foo$self$getSelf === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(_o$Foo$self$getSelf, _m)[_m].bind(_o$Foo$self$getSelf))(); + ((_o$Foo$self$getSelf2 = (o.Foo.self?.getSelf)()) === null || _o$Foo$self$getSelf2 === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(_o$Foo$self$getSelf2, _m)[_m].bind(_o$Foo$self$getSelf2))(); + } + }], [{ + key: "getSelf", + value: function getSelf() { + return Foo; + } + }]); + return Foo; +}(); + +var _x = babelHelpers.classPrivateFieldLooseKey("x"); + +var _m = babelHelpers.classPrivateFieldLooseKey("m"); + +Object.defineProperty(Foo, _x, { + writable: true, + value: 1 +}); +Foo.self = Foo; +Object.defineProperty(Foo, _m, { + writable: true, + value: function () { + return babelHelpers.classPrivateFieldLooseBase(this, _x)[_x]; + } +}); +new Foo().test(); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/parenthesized-optional-member-call/exec.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/parenthesized-optional-member-call/exec.js new file mode 100644 index 000000000000..a6fb131dbd3f --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/parenthesized-optional-member-call/exec.js @@ -0,0 +1,36 @@ +class Foo { + static #x = 1; + + static self = Foo; + static #m = function() { return this.#x; }; + static getSelf() { return Foo } + + test() { + const o = { Foo: Foo }; + + expect((Foo?.#m)()).toEqual(1); + expect((Foo?.#m)().toString).toEqual(1..toString); + expect((Foo?.#m)().toString()).toEqual('1'); + + expect((o?.Foo.#m)()).toEqual(1); + expect((o?.Foo.#m)().toString).toEqual(1..toString); + expect((o?.Foo.#m)().toString()).toEqual('1'); + + expect((((o.Foo?.self.getSelf)())?.#m)()).toEqual(1); + expect((((o.Foo.self?.getSelf)())?.#m)()).toEqual(1); + } + + testNull() { + const o = null; + + expect(() => { (o?.Foo.#m)() }).toThrow(); + expect(() => { (o?.Foo.#m)().toString }).toThrow(); + expect(() => { (o?.Foo.#m)().toString() }).toThrow(); + + expect(() => { (((o.Foo?.self.getSelf)())?.#m)() }).toThrow(); + expect(() => { (((o.Foo.self?.getSelf)())?.#m)() }).toThrow(); + } +} + +(new Foo).test(); +(new Foo).testNull(); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/parenthesized-optional-member-call/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/parenthesized-optional-member-call/input.js new file mode 100644 index 000000000000..a2231680adfa --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/parenthesized-optional-member-call/input.js @@ -0,0 +1,24 @@ +class Foo { + static #x = 1; + + static self = Foo; + static #m = function() { return this.#x; }; + static getSelf() { return Foo } + + test() { + const o = { Foo: Foo }; + + (Foo?.#m)(); + (Foo?.#m)().toString; + (Foo?.#m)().toString(); + + (o?.Foo.#m)(); + (o?.Foo.#m)().toString; + (o?.Foo.#m)().toString(); + + (((o.Foo?.self.getSelf)())?.#m)(); + (((o.Foo.self?.getSelf)())?.#m)(); + } +} + +(new Foo).test(); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/parenthesized-optional-member-call/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/parenthesized-optional-member-call/options.json new file mode 100644 index 000000000000..2b19921774d7 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/parenthesized-optional-member-call/options.json @@ -0,0 +1,8 @@ +{ + "plugins": [ + ["external-helpers", { "helperVersion": "7.100.0" }], + "proposal-class-properties", + "transform-classes", + "transform-block-scoping" + ] +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/parenthesized-optional-member-call/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/parenthesized-optional-member-call/output.js new file mode 100644 index 000000000000..4965554f479e --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/parenthesized-optional-member-call/output.js @@ -0,0 +1,45 @@ +var Foo = /*#__PURE__*/function () { + "use strict"; + + function Foo() { + babelHelpers.classCallCheck(this, Foo); + } + + babelHelpers.createClass(Foo, [{ + key: "test", + value: function test() { + var _o$Foo$self$getSelf, _o$Foo$self$getSelf2; + + var o = { + Foo: Foo + }; + (Foo === null || Foo === void 0 ? void 0 : babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _m).bind(Foo))(); + (Foo === null || Foo === void 0 ? void 0 : babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _m).bind(Foo))().toString; + (Foo === null || Foo === void 0 ? void 0 : babelHelpers.classStaticPrivateFieldSpecGet(Foo, Foo, _m).bind(Foo))().toString(); + (o === null || o === void 0 ? void 0 : babelHelpers.classStaticPrivateFieldSpecGet(o.Foo, Foo, _m).bind(o.Foo))(); + (o === null || o === void 0 ? void 0 : babelHelpers.classStaticPrivateFieldSpecGet(o.Foo, Foo, _m).bind(o.Foo))().toString; + (o === null || o === void 0 ? void 0 : babelHelpers.classStaticPrivateFieldSpecGet(o.Foo, Foo, _m).bind(o.Foo))().toString(); + ((_o$Foo$self$getSelf = (o.Foo?.self.getSelf)()) === null || _o$Foo$self$getSelf === void 0 ? void 0 : babelHelpers.classStaticPrivateFieldSpecGet(_o$Foo$self$getSelf, Foo, _m).bind(_o$Foo$self$getSelf))(); + ((_o$Foo$self$getSelf2 = (o.Foo.self?.getSelf)()) === null || _o$Foo$self$getSelf2 === void 0 ? void 0 : babelHelpers.classStaticPrivateFieldSpecGet(_o$Foo$self$getSelf2, Foo, _m).bind(_o$Foo$self$getSelf2))(); + } + }], [{ + key: "getSelf", + value: function getSelf() { + return Foo; + } + }]); + return Foo; +}(); + +var _x = { + writable: true, + value: 1 +}; +babelHelpers.defineProperty(Foo, "self", Foo); +var _m = { + writable: true, + value: function () { + return babelHelpers.classStaticPrivateFieldSpecGet(this, Foo, _x); + } +}; +new Foo().test();