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 1d8114a94d85..7724406d2b46 100644 --- a/packages/babel-helper-member-expression-to-functions/src/index.js +++ b/packages/babel-helper-member-expression-to-functions/src/index.js @@ -167,9 +167,8 @@ const handle = { const parentIsOptionalCall = parentPath.isOptionalCallExpression({ callee: node, }); - const isParenthesizedMemberCall = - parentPath.isCallExpression({ callee: node }) && - node.extra?.parenthesized; + // if parentIsCall is true, it implies that node.extra.parenthesized is always true + const parentIsCall = parentPath.isCallExpression({ callee: node }); startingOptional.replaceWith(toNonOptional(startingOptional, baseRef)); if (parentIsOptionalCall) { if (parent.optional) { @@ -177,7 +176,7 @@ const handle = { } else { parentPath.replaceWith(this.call(member, parent.arguments)); } - } else if (isParenthesizedMemberCall) { + } else if (parentIsCall) { // `(a?.#b)()` to `(a == null ? void 0 : a.#b.bind(a))()` member.replaceWith(this.boundGet(member)); } else { diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/parenthesized-optional-member-call-with-transform/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/parenthesized-optional-member-call-with-transform/output.js index b67e17103b8f..7b39559261e3 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/parenthesized-optional-member-call-with-transform/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/parenthesized-optional-member-call-with-transform/output.js @@ -8,7 +8,7 @@ class Foo { } test() { - var _o$Foo$self$getSelf, _o$Foo$self$getSelf2, _fn$Foo$self$getSelf, _fn$Foo$self$getSelf2, _o$Foo, _o$Foo$self, _fn, _fn$Foo, _fn$Foo$self, _fn$Foo$self2; + var _o$Foo$self$getSelf, _o$Foo$self$getSelf2, _fn$Foo$self$getSelf, _fn$Foo$self$getSelf2, _o$Foo, _o$Foo$self, _fn, _fn$Foo, _fn$Foo$self; const o = { Foo: Foo @@ -26,8 +26,8 @@ class Foo { (o === null || o === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(o.Foo, _m)[_m].bind(o.Foo))().toString(); ((_o$Foo$self$getSelf = ((_o$Foo = o.Foo) == null ? void 0 : _o$Foo.self.getSelf.bind(_o$Foo.self))()) === 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 = o.Foo.self) == null ? void 0 : _o$Foo$self.getSelf.bind(_o$Foo$self))()) === null || _o$Foo$self$getSelf2 === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(_o$Foo$self$getSelf2, _m)[_m].bind(_o$Foo$self$getSelf2))(); - ((_fn$Foo$self$getSelf = ((_fn = fn()) == null ? void 0 : (_fn$Foo = _fn.Foo) == null ? void 0 : _fn$Foo.self.getSelf.bind(_fn.Foo.self))()) === null || _fn$Foo$self$getSelf === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(_fn$Foo$self$getSelf, _m)[_m].bind(_fn$Foo$self$getSelf))(); - ((_fn$Foo$self$getSelf2 = (fn == null ? void 0 : (_fn$Foo$self2 = _fn$Foo$self = fn().Foo.self) == null ? void 0 : _fn$Foo$self2.getSelf.bind(_fn$Foo$self))()) === null || _fn$Foo$self$getSelf2 === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(_fn$Foo$self$getSelf2, _m)[_m].bind(_fn$Foo$self$getSelf2))(); + ((_fn$Foo$self$getSelf = ((_fn = fn()) == null ? void 0 : (_fn$Foo = _fn.Foo) == null ? void 0 : _fn$Foo.self.getSelf.bind(_fn$Foo.self))()) === null || _fn$Foo$self$getSelf === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(_fn$Foo$self$getSelf, _m)[_m].bind(_fn$Foo$self$getSelf))(); + ((_fn$Foo$self$getSelf2 = (fn == null ? void 0 : (_fn$Foo$self = fn().Foo.self) == null ? void 0 : _fn$Foo$self.getSelf.bind(_fn$Foo$self))()) === null || _fn$Foo$self$getSelf2 === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(_fn$Foo$self$getSelf2, _m)[_m].bind(_fn$Foo$self$getSelf2))(); } } diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/parenthesized-optional-member-call-with-transform/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/parenthesized-optional-member-call-with-transform/output.js index 7f51bfebb3cb..962b865ed654 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/parenthesized-optional-member-call-with-transform/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/parenthesized-optional-member-call-with-transform/output.js @@ -4,7 +4,7 @@ class Foo { } test() { - var _o$Foo, _o$Foo2, _o$Foo3, _o$Foo$self$getSelf, _o$Foo$self$getSelf2, _fn$Foo$self$getSelf, _fn$Foo$self$getSelf2, _o$Foo4, _o$Foo4$self, _o$Foo$self, _fn, _fn$Foo$self, _fn$Foo, _fn$Foo$self2, _fn$Foo$self3; + var _o$Foo, _o$Foo2, _o$Foo3, _o$Foo$self$getSelf, _o$Foo$self$getSelf2, _fn$Foo$self$getSelf, _fn$Foo$self$getSelf2, _o$Foo4, _o$Foo4$self, _o$Foo$self, _fn, _fn$Foo, _fn$Foo$self, _fn$Foo$self2; const o = { Foo: Foo @@ -23,7 +23,7 @@ class Foo { ((_o$Foo$self$getSelf = ((_o$Foo4 = o.Foo) === null || _o$Foo4 === void 0 ? void 0 : (_o$Foo4$self = _o$Foo4.self).getSelf.bind(_o$Foo4$self))()) === 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 = o.Foo.self) === null || _o$Foo$self === void 0 ? void 0 : _o$Foo$self.getSelf.bind(_o$Foo$self))()) === null || _o$Foo$self$getSelf2 === void 0 ? void 0 : babelHelpers.classStaticPrivateFieldSpecGet(_o$Foo$self$getSelf2, Foo, _m).bind(_o$Foo$self$getSelf2))(); ((_fn$Foo$self$getSelf = ((_fn = fn()) === null || _fn === void 0 ? void 0 : (_fn$Foo = _fn.Foo) === null || _fn$Foo === void 0 ? void 0 : (_fn$Foo$self = _fn$Foo.self).getSelf.bind(_fn$Foo$self))()) === null || _fn$Foo$self$getSelf === void 0 ? void 0 : babelHelpers.classStaticPrivateFieldSpecGet(_fn$Foo$self$getSelf, Foo, _m).bind(_fn$Foo$self$getSelf))(); - ((_fn$Foo$self$getSelf2 = (fn === null || fn === void 0 ? void 0 : (_fn$Foo$self3 = _fn$Foo$self2 = fn().Foo.self) === null || _fn$Foo$self3 === void 0 ? void 0 : _fn$Foo$self3.getSelf.bind(_fn$Foo$self2))()) === null || _fn$Foo$self$getSelf2 === void 0 ? void 0 : babelHelpers.classStaticPrivateFieldSpecGet(_fn$Foo$self$getSelf2, Foo, _m).bind(_fn$Foo$self$getSelf2))(); + ((_fn$Foo$self$getSelf2 = (fn === null || fn === void 0 ? void 0 : (_fn$Foo$self2 = fn().Foo.self) === null || _fn$Foo$self2 === void 0 ? void 0 : _fn$Foo$self2.getSelf.bind(_fn$Foo$self2))()) === null || _fn$Foo$self$getSelf2 === void 0 ? void 0 : babelHelpers.classStaticPrivateFieldSpecGet(_fn$Foo$self$getSelf2, Foo, _m).bind(_fn$Foo$self$getSelf2))(); } } diff --git a/packages/babel-plugin-proposal-optional-chaining/src/index.js b/packages/babel-plugin-proposal-optional-chaining/src/index.js index c7346891244a..a1f03bf94c95 100644 --- a/packages/babel-plugin-proposal-optional-chaining/src/index.js +++ b/packages/babel-plugin-proposal-optional-chaining/src/index.js @@ -24,8 +24,20 @@ export default declare((api, options) => { visitor: { "OptionalCallExpression|OptionalMemberExpression"(path) { const { scope } = path; - const parentPath = path.findParent(p => !p.isParenthesizedExpression()); + // maybeParenthesized points to the outermost parenthesizedExpression + // or the path itself + let maybeParenthesized = path; + const parentPath = path.findParent(p => { + if (!p.isParenthesizedExpression()) return true; + maybeParenthesized = p; + }); let isDeleteOperation = false; + const parentIsCall = + parentPath.isCallExpression({ callee: maybeParenthesized.node }) && + // note that the first condition must implies that `path.optional` is `true`, + // otherwise the parentPath should be an OptionalCallExpressioin + path.isOptionalMemberExpression(); + const optionals = []; let optionalPath = path; @@ -116,14 +128,10 @@ export default declare((api, options) => { } let replacement = replacementPath.node; // Ensure (a?.b)() has proper `this` - if ( - t.isMemberExpression(replacement) && - (replacement.extra?.parenthesized || - // if replacementPath.parentPath does not equal parentPath, - // it must be unwrapped from parenthesized expression. - replacementPath.parentPath !== parentPath) && - parentPath.isCallExpression() - ) { + // The `parentIsCall` is constant within loop, we should check i === 0 + // to ensure that it is only applied to the first optional chain element + // i.e. `?.b` in `(a?.b.c)()` + if (i === 0 && parentIsCall) { // `(a?.b)()` to `(a == null ? undefined : a.b.bind(a))()` const { object } = replacement; let baseRef; diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/containers/input.js b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/containers/input.js index d699b16fa16f..bd8e7b16d459 100644 --- a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/containers/input.js +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/containers/input.js @@ -2,5 +2,6 @@ var street = user.address?.street street = user.address?.street test(a?.b, 1); +test((a?.b), 1); (1, a?.b, 2); diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/containers/output.js b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/containers/output.js index d2a39179ca94..7d4419088c62 100644 --- a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/containers/output.js +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/containers/output.js @@ -1,6 +1,7 @@ -var _user$address, _user$address2, _a, _a2; +var _user$address, _user$address2, _a, _a2, _a3; var street = (_user$address = user.address) === null || _user$address === void 0 ? void 0 : _user$address.street; street = (_user$address2 = user.address) === null || _user$address2 === void 0 ? void 0 : _user$address2.street; test((_a = a) === null || _a === void 0 ? void 0 : _a.b, 1); -1, (_a2 = a) === null || _a2 === void 0 ? void 0 : _a2.b, 2; +test((_a2 = a) === null || _a2 === void 0 ? void 0 : _a2.b, 1); +1, (_a3 = a) === null || _a3 === void 0 ? void 0 : _a3.b, 2; diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/parenthesized-expression-containers/input.js b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/parenthesized-expression-containers/input.js new file mode 100644 index 000000000000..bd8e7b16d459 --- /dev/null +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/parenthesized-expression-containers/input.js @@ -0,0 +1,7 @@ +var street = user.address?.street +street = user.address?.street + +test(a?.b, 1); +test((a?.b), 1); + +(1, a?.b, 2); diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/parenthesized-expression-containers/options.json b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/parenthesized-expression-containers/options.json new file mode 100644 index 000000000000..eae39736acf4 --- /dev/null +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/parenthesized-expression-containers/options.json @@ -0,0 +1,6 @@ +{ + "plugins": ["proposal-optional-chaining"], + "parserOpts": { + "createParenthesizedExpressions": true + } +} diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/parenthesized-expression-containers/output.js b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/parenthesized-expression-containers/output.js new file mode 100644 index 000000000000..72b42c86c35f --- /dev/null +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/parenthesized-expression-containers/output.js @@ -0,0 +1,7 @@ +var _user$address, _user$address2, _a, _a2, _a3; + +var street = (_user$address = user.address) === null || _user$address === void 0 ? void 0 : _user$address.street; +street = (_user$address2 = user.address) === null || _user$address2 === void 0 ? void 0 : _user$address2.street; +test((_a = a) === null || _a === void 0 ? void 0 : _a.b, 1); +test(((_a2 = a) === null || _a2 === void 0 ? void 0 : _a2.b), 1); +((1, (_a3 = a) === null || _a3 === void 0 ? void 0 : _a3.b, 2)); diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/parenthesized-member-call-loose/output.js b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/parenthesized-member-call-loose/output.js index 80c198711db7..20db389f8f92 100644 --- a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/parenthesized-member-call-loose/output.js +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/parenthesized-member-call-loose/output.js @@ -13,7 +13,7 @@ class Foo { } test() { - var _o$Foo$self$getSelf, _o$Foo, _o$Foo$self$getSelf2, _o$Foo$self, _fn$Foo$self$getSelf, _fn, _fn$Foo, _fn$Foo$self$getSelf2, _fn$Foo$self, _fn$Foo$self2; + var _o$Foo$self$getSelf, _o$Foo, _o$Foo$self$getSelf2, _o$Foo$self, _fn$Foo$self$getSelf, _fn, _fn$Foo, _fn$Foo$self$getSelf2, _fn$Foo$self; const Foo = this; const o = { @@ -32,8 +32,8 @@ class Foo { (o == null ? void 0 : o.Foo.m.bind(o.Foo))().toString(); ((_o$Foo$self$getSelf = ((_o$Foo = o.Foo) == null ? void 0 : _o$Foo.self.getSelf.bind(_o$Foo.self))()) == null ? void 0 : _o$Foo$self$getSelf.m.bind(_o$Foo$self$getSelf))(); ((_o$Foo$self$getSelf2 = ((_o$Foo$self = o.Foo.self) == null ? void 0 : _o$Foo$self.getSelf.bind(_o$Foo$self))()) == null ? void 0 : _o$Foo$self$getSelf2.m.bind(_o$Foo$self$getSelf2))(); - ((_fn$Foo$self$getSelf = ((_fn = fn()) == null ? void 0 : (_fn$Foo = _fn.Foo) == null ? void 0 : _fn$Foo.self.getSelf.bind(_fn.Foo.self))()) == null ? void 0 : _fn$Foo$self$getSelf.m.bind(_fn$Foo$self$getSelf))(); - ((_fn$Foo$self$getSelf2 = (fn == null ? void 0 : (_fn$Foo$self2 = _fn$Foo$self = fn().Foo.self) == null ? void 0 : _fn$Foo$self2.getSelf.bind(_fn$Foo$self))()) == null ? void 0 : _fn$Foo$self$getSelf2.m.bind(_fn$Foo$self$getSelf2))(); + ((_fn$Foo$self$getSelf = ((_fn = fn()) == null ? void 0 : (_fn$Foo = _fn.Foo) == null ? void 0 : _fn$Foo.self.getSelf.bind(_fn$Foo.self))()) == null ? void 0 : _fn$Foo$self$getSelf.m.bind(_fn$Foo$self$getSelf))(); + ((_fn$Foo$self$getSelf2 = (fn == null ? void 0 : (_fn$Foo$self = fn().Foo.self) == null ? void 0 : _fn$Foo$self.getSelf.bind(_fn$Foo$self))()) == null ? void 0 : _fn$Foo$self$getSelf2.m.bind(_fn$Foo$self$getSelf2))(); } } diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/parenthesized-member-call/output.js b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/parenthesized-member-call/output.js index a41ae311d626..fb1fc9fbbbb4 100644 --- a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/parenthesized-member-call/output.js +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/parenthesized-member-call/output.js @@ -13,7 +13,7 @@ class Foo { } test() { - var _o$Foo, _o$Foo2, _o$Foo3, _o$Foo$self$getSelf, _o$Foo4, _o$Foo4$self, _o$Foo$self$getSelf2, _o$Foo$self, _fn$Foo$self$getSelf, _fn, _fn$Foo$self, _fn$Foo, _fn$Foo$self$getSelf2, _fn$Foo$self2, _fn$Foo$self3; + var _o$Foo, _o$Foo2, _o$Foo3, _o$Foo$self$getSelf, _o$Foo4, _o$Foo4$self, _o$Foo$self$getSelf2, _o$Foo$self, _fn$Foo$self$getSelf, _fn, _fn$Foo, _fn$Foo$self, _fn$Foo$self$getSelf2, _fn$Foo$self2; const Foo = this; const o = { @@ -33,7 +33,7 @@ class Foo { ((_o$Foo$self$getSelf = ((_o$Foo4 = o.Foo) === null || _o$Foo4 === void 0 ? void 0 : (_o$Foo4$self = _o$Foo4.self).getSelf.bind(_o$Foo4$self))()) === null || _o$Foo$self$getSelf === void 0 ? void 0 : _o$Foo$self$getSelf.m.bind(_o$Foo$self$getSelf))(); ((_o$Foo$self$getSelf2 = ((_o$Foo$self = o.Foo.self) === null || _o$Foo$self === void 0 ? void 0 : _o$Foo$self.getSelf.bind(_o$Foo$self))()) === null || _o$Foo$self$getSelf2 === void 0 ? void 0 : _o$Foo$self$getSelf2.m.bind(_o$Foo$self$getSelf2))(); ((_fn$Foo$self$getSelf = ((_fn = fn()) === null || _fn === void 0 ? void 0 : (_fn$Foo = _fn.Foo) === null || _fn$Foo === void 0 ? void 0 : (_fn$Foo$self = _fn$Foo.self).getSelf.bind(_fn$Foo$self))()) === null || _fn$Foo$self$getSelf === void 0 ? void 0 : _fn$Foo$self$getSelf.m.bind(_fn$Foo$self$getSelf))(); - ((_fn$Foo$self$getSelf2 = (fn === null || fn === void 0 ? void 0 : (_fn$Foo$self3 = _fn$Foo$self2 = fn().Foo.self) === null || _fn$Foo$self3 === void 0 ? void 0 : _fn$Foo$self3.getSelf.bind(_fn$Foo$self2))()) === null || _fn$Foo$self$getSelf2 === void 0 ? void 0 : _fn$Foo$self$getSelf2.m.bind(_fn$Foo$self$getSelf2))(); + ((_fn$Foo$self$getSelf2 = (fn === null || fn === void 0 ? void 0 : (_fn$Foo$self2 = fn().Foo.self) === null || _fn$Foo$self2 === void 0 ? void 0 : _fn$Foo$self2.getSelf.bind(_fn$Foo$self2))()) === null || _fn$Foo$self$getSelf2 === void 0 ? void 0 : _fn$Foo$self$getSelf2.m.bind(_fn$Foo$self$getSelf2))(); } }