From c8d86442d280ccc3d669ea5ac1b8e0a4cdc87f3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 3 Sep 2020 15:55:45 -0400 Subject: [PATCH 1/8] test: add test for nullish coalescing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Nicolò Ribaudo --- .../transform-in-default-destructuring/input.js | 1 + .../options.json | 0 .../transform-in-default-destructuring/output.js | 5 +++++ .../input.js | 0 .../transform-in-default-param/options.json | 3 +++ .../nullish-coalescing/transform-in-default-param/output.js | 5 +++++ .../nullish-coalescing/transform-in-default/output.js | 3 --- 7 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-destructuring/input.js rename packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/{transform-in-default => transform-in-default-destructuring}/options.json (100%) create mode 100644 packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-destructuring/output.js rename packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/{transform-in-default => transform-in-default-param}/input.js (100%) create mode 100644 packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-param/options.json create mode 100644 packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-param/output.js delete mode 100644 packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default/output.js diff --git a/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-destructuring/input.js b/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-destructuring/input.js new file mode 100644 index 000000000000..876e05c1a625 --- /dev/null +++ b/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-destructuring/input.js @@ -0,0 +1 @@ +var { qux = foo.bar ?? "qux" } = {}; diff --git a/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default/options.json b/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-destructuring/options.json similarity index 100% rename from packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default/options.json rename to packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-destructuring/options.json diff --git a/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-destructuring/output.js b/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-destructuring/output.js new file mode 100644 index 000000000000..e6dcd5e32e2c --- /dev/null +++ b/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-destructuring/output.js @@ -0,0 +1,5 @@ +var _foo$bar; + +var { + qux = (_foo$bar = foo.bar) !== null && _foo$bar !== void 0 ? _foo$bar : "qux" +} = {}; diff --git a/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default/input.js b/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-param/input.js similarity index 100% rename from packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default/input.js rename to packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-param/input.js diff --git a/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-param/options.json b/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-param/options.json new file mode 100644 index 000000000000..47e490d2afcf --- /dev/null +++ b/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-param/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["proposal-nullish-coalescing-operator"] +} diff --git a/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-param/output.js b/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-param/output.js new file mode 100644 index 000000000000..d218e3dd8d65 --- /dev/null +++ b/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-param/output.js @@ -0,0 +1,5 @@ +function foo(foo, qux = (() => { + var _foo$bar; + + return (_foo$bar = foo.bar) !== null && _foo$bar !== void 0 ? _foo$bar : "qux"; +})()) {} diff --git a/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default/output.js b/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default/output.js deleted file mode 100644 index 2b2f2314b366..000000000000 --- a/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default/output.js +++ /dev/null @@ -1,3 +0,0 @@ -function foo(foo, qux = (_foo$bar = foo.bar) !== null && _foo$bar !== void 0 ? _foo$bar : "qux") { - var _foo$bar; -} From 000c9240524df7023227810495a63c0d2a679c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 3 Sep 2020 16:53:35 -0400 Subject: [PATCH 2/8] test: add control group --- .../nullish-coalescing/transform-in-default-param/input.js | 2 ++ .../nullish-coalescing/transform-in-default-param/output.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-param/input.js b/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-param/input.js index 0c13164800af..65b7719722bc 100644 --- a/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-param/input.js +++ b/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-param/input.js @@ -1 +1,3 @@ function foo(foo, qux = foo.bar ?? "qux") {} + +function bar(bar, qux = bar ?? "qux") {} diff --git a/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-param/output.js b/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-param/output.js index d218e3dd8d65..b746a752e57a 100644 --- a/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-param/output.js +++ b/packages/babel-plugin-proposal-nullish-coalescing-operator/test/fixtures/nullish-coalescing/transform-in-default-param/output.js @@ -3,3 +3,5 @@ function foo(foo, qux = (() => { return (_foo$bar = foo.bar) !== null && _foo$bar !== void 0 ? _foo$bar : "qux"; })()) {} + +function bar(bar, qux = bar !== null && bar !== void 0 ? bar : "qux") {} From babc8f789a6b53b6890309d1fa41ad45ca0790a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 3 Sep 2020 17:23:15 -0400 Subject: [PATCH 3/8] test: add tests for optional chaining MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Nicolò Ribaudo --- .../general/in-function-params/input.js | 7 +++++ .../general/in-function-params/output.js | 26 +++++++++++++++++++ .../general/in-var-destructuring/input.js | 1 + .../general/in-var-destructuring/output.js | 5 ++++ 4 files changed, 39 insertions(+) create mode 100644 packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params/input.js create mode 100644 packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params/output.js create mode 100644 packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-var-destructuring/input.js create mode 100644 packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-var-destructuring/output.js diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params/input.js b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params/input.js new file mode 100644 index 000000000000..802d92c1342c --- /dev/null +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params/input.js @@ -0,0 +1,7 @@ +function f(a = x?.y) {} + +function g({ a, b = a?.c }) {} + +function h(a, { b = a.b?.c?.d.e }) {} + +function i(a, { b = (a.b?.c?.d).e }) {} diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params/output.js b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params/output.js new file mode 100644 index 000000000000..e2c8101d8d32 --- /dev/null +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params/output.js @@ -0,0 +1,26 @@ +function f(a = (() => { + var _x; + + return (_x = x) === null || _x === void 0 ? void 0 : _x.y; +})()) {} + +function g({ + a, + b = a === null || a === void 0 ? void 0 : a.c +}) {} + +function h(a, { + b = (() => { + var _a$b, _a$b$c; + + return (_a$b = a.b) === null || _a$b === void 0 ? void 0 : (_a$b$c = _a$b.c) === null || _a$b$c === void 0 ? void 0 : _a$b$c.d.e; + })() +}) {} + +function i(a, { + b = (() => { + var _a$b2, _a$b2$c; + + return (_a$b2 = a.b) === null || _a$b2 === void 0 ? void 0 : (_a$b2$c = _a$b2.c) === null || _a$b2$c === void 0 ? void 0 : _a$b2$c.d; + })().e +}) {} diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-var-destructuring/input.js b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-var-destructuring/input.js new file mode 100644 index 000000000000..d88b60a0e957 --- /dev/null +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-var-destructuring/input.js @@ -0,0 +1 @@ +var { a = x?.y } = {}; diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-var-destructuring/output.js b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-var-destructuring/output.js new file mode 100644 index 000000000000..0e70d31a12bf --- /dev/null +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-var-destructuring/output.js @@ -0,0 +1,5 @@ +var _x; + +var { + a = (_x = x) === null || _x === void 0 ? void 0 : _x.y +} = {}; From 9481ae01373246bd9bea06ae1b8a95b089ffd964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 3 Sep 2020 18:37:47 -0400 Subject: [PATCH 4/8] test: add tests on optional chaining mixed with private class elements --- .../optional-chain-in-function-param/exec.js | 46 ++++++++++ .../optional-chain-in-function-param/input.js | 46 ++++++++++ .../options.json | 4 + .../output.js | 90 +++++++++++++++++++ 4 files changed, 186 insertions(+) create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param/exec.js create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param/input.js create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param/options.json create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param/output.js diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param/exec.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param/exec.js new file mode 100644 index 000000000000..6eb26ab6e01a --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param/exec.js @@ -0,0 +1,46 @@ +class Foo { + static #x = 1; + static #m = function() { return this.#x; }; + static #self = Foo; + static self = Foo; + static getSelf() { return this } + + static test() { + const o = { Foo: Foo }; + const deep = { very: { o } }; + function fn() { + return o; + } + function fnDeep() { + return deep; + } + + function f(o, r = o?.Foo.#m()) { + return r; + } + + function g(o, r = o?.Foo.#self.getSelf().#m()) { + return r; + } + + function h(fnDeep, r = fnDeep?.().very.o?.Foo?.#m()) { + return r; + } + + function i(fn, r = fn?.().Foo.#self?.getSelf()?.self.#m()) { + return r; + } + + function j(fn, r = (fn().Foo.#self.getSelf().self.#m)?.()) { + return r; + } + + expect(f(o)).toBe(1); + expect(g(o)).toBe(1); + expect(h(fnDeep)).toBe(1); + expect(i(fn)).toBe(1); + expect(j(fn)).toBe(1); + } +} + +Foo.test(); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param/input.js new file mode 100644 index 000000000000..c5fe33636eca --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param/input.js @@ -0,0 +1,46 @@ +class Foo { + static #x = 1; + static #m = function() { return this.#x; }; + static #self = Foo; + static self = Foo; + static getSelf() { return this } + + static test() { + const o = { Foo: Foo }; + const deep = { very: { o } }; + function fn() { + return o; + } + function fnDeep() { + return deep; + } + + function f(o, r = o?.Foo.#m()) { + return r; + } + + function g(o, r = o?.Foo.#self.getSelf().#m?.()) { + return r; + } + + function h(fnDeep, r = fnDeep?.().very.o?.Foo?.#m()) { + return r; + } + + function i(fn, r = fn?.().Foo.#self?.getSelf()?.self.#m()) { + return r; + } + + function j(fn, r = (fn().Foo.#self.getSelf().self.#m)?.()) { + return r; + } + + f(o); + g(o); + h(fnDeep); + i(fn); + j(fn); + } +} + +Foo.test(); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param/options.json new file mode 100644 index 000000000000..3b59e1bbfcc8 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param/options.json @@ -0,0 +1,4 @@ +{ + "plugins": ["proposal-class-properties"], + "minNodeVersion": "14.0.0" +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param/output.js new file mode 100644 index 000000000000..782d93a5793a --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param/output.js @@ -0,0 +1,90 @@ +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; } + +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 { + static getSelf() { + return this; + } + + static test() { + const o = { + Foo: Foo + }; + const deep = { + very: { + o + } + }; + + function fn() { + return o; + } + + function fnDeep() { + return deep; + } + + function f(o, r = (() => { + var _o$Foo; + + return o === null || o === void 0 ? void 0 : _classStaticPrivateFieldSpecGet(_o$Foo = o.Foo, Foo, _m).call(_o$Foo); + })()) { + return r; + } + + function g(o, r = (() => o === null || o === void 0 ? void 0 : _classStaticPrivateFieldSpecGet(_classStaticPrivateFieldSpecGet(o.Foo, Foo, _self).getSelf(), Foo, _m))()?.()) { + return r; + } + + function h(fnDeep, r = (() => { + var _fnDeep$very$o$Foo; + + return (_fnDeep$very$o$Foo = fnDeep?.().very.o?.Foo) === null || _fnDeep$very$o$Foo === void 0 ? void 0 : _classStaticPrivateFieldSpecGet(_fnDeep$very$o$Foo, Foo, _m).call(_fnDeep$very$o$Foo); + })()) { + return r; + } + + function i(fn, r = (() => { + var _getSelf, _getSelf$self; + + return (_getSelf = (() => fn === null || fn === void 0 ? void 0 : _classStaticPrivateFieldSpecGet(fn().Foo, Foo, _self))()?.getSelf()) === null || _getSelf === void 0 ? void 0 : _classStaticPrivateFieldSpecGet(_getSelf$self = _getSelf.self, Foo, _m).call(_getSelf$self); + })()) { + return r; + } + + function j(fn, r = (() => { + var _classStaticPrivateFi; + + return _classStaticPrivateFieldSpecGet(_classStaticPrivateFi = _classStaticPrivateFieldSpecGet(fn().Foo, Foo, _self).getSelf().self, Foo, _m)?.call(_classStaticPrivateFi); + })()) { + return r; + } + + f(o); + g(o); + h(fnDeep); + i(fn); + j(fn); + } + +} + +var _x = { + writable: true, + value: 1 +}; +var _m = { + writable: true, + value: function () { + return _classStaticPrivateFieldSpecGet(this, Foo, _x); + } +}; +var _self = { + writable: true, + value: Foo +}; + +_defineProperty(Foo, "self", Foo); + +Foo.test(); From 833f6aa4afb6e9c7d6d32a9ea89edc3e789acff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 3 Sep 2020 18:38:53 -0400 Subject: [PATCH 5/8] fix: wrap member chains to IIFE when it is in parameter default --- .../src/index.js | 24 ++++++++++++-- .../src/index.js | 14 ++++++-- .../src/index.js | 33 ++++++++++++++++++- 3 files changed, 65 insertions(+), 6 deletions(-) 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 62d844b9f177..0e5ef5ecb8e1 100644 --- a/packages/babel-helper-member-expression-to-functions/src/index.js +++ b/packages/babel-helper-member-expression-to-functions/src/index.js @@ -84,7 +84,7 @@ const handle = { }, handle(member) { - const { node, parent, parentPath } = member; + const { node, parent, parentPath, scope } = member; if (member.isOptionalMemberExpression()) { // Transforming optional chaining requires we replace ancestors. @@ -118,6 +118,17 @@ const handle = { return true; }); + // Replace `function (a, x = a.b?.#c) {}` to `function (a, x = (() => a.b?.#c)() ){}` + // so the temporary variable can be injected in correct scope + // This can be further optimized to avoid unecessary IIFE + if (scope.path.isPattern()) { + endPath.replaceWith( + // The injected member will be queued and eventually transformed when visited + t.callExpression(t.arrowFunctionExpression([], endPath.node), []), + ); + return; + } + const rootParentPath = endPath.parentPath; if ( rootParentPath.isUpdateExpression({ argument: node }) || @@ -165,7 +176,6 @@ const handle = { ); } - const { scope } = member; const startingProp = startingOptional.isOptionalMemberExpression() ? "object" : "callee"; @@ -362,6 +372,16 @@ const handle = { // MEMBER?.(ARGS) -> _optionalCall(MEMBER, ARGS) if (parentPath.isOptionalCallExpression({ callee: node })) { + // Replace `function (a, x = a.b.#c?.()) {}` to `function (a, x = (() => a.b.#c?.())() ){}` + // so the temporary variable can be injected in correct scope + // This can be further optimized to avoid unecessary IIFE + if (scope.path.isPattern()) { + parentPath.replaceWith( + // The injected member will be queued and eventually transformed when visited + t.callExpression(t.arrowFunctionExpression([], parentPath.node), []), + ); + return; + } parentPath.replaceWith(this.optionalCall(member, parent.arguments)); return; } diff --git a/packages/babel-plugin-proposal-nullish-coalescing-operator/src/index.js b/packages/babel-plugin-proposal-nullish-coalescing-operator/src/index.js index 848a6c3ee3cd..12ddbc7cba97 100644 --- a/packages/babel-plugin-proposal-nullish-coalescing-operator/src/index.js +++ b/packages/babel-plugin-proposal-nullish-coalescing-operator/src/index.js @@ -1,6 +1,6 @@ import { declare } from "@babel/helper-plugin-utils"; import syntaxNullishCoalescingOperator from "@babel/plugin-syntax-nullish-coalescing-operator"; -import { types as t } from "@babel/core"; +import { types as t, template } from "@babel/core"; export default declare((api, { loose = false }) => { api.assertVersion(7); @@ -16,13 +16,21 @@ export default declare((api, { loose = false }) => { return; } - let ref = scope.maybeGenerateMemoised(node.left); + let ref; let assignment; // skip creating extra reference when `left` is static - if (ref === null) { + if (scope.isStatic(node.left)) { ref = node.left; assignment = t.cloneNode(node.left); + } else if (scope.path.isPattern()) { + // Replace `function (a, x = a.b ?? c) {}` to `function (a, x = (() => a.b ?? c)() ){}` + // so the temporary variable can be injected in correct scope + path.replaceWith(template.ast`(() => ${path.node})()`); + // The injected nullish expression will be queued and eventually transformed when visited + return; } else { + ref = scope.generateUidIdentifierBasedOnNode(node.left); + scope.push({ id: t.cloneNode(ref) }); assignment = t.assignmentExpression("=", ref, node.left); } diff --git a/packages/babel-plugin-proposal-optional-chaining/src/index.js b/packages/babel-plugin-proposal-optional-chaining/src/index.js index f3d32be4338a..3fb69e104bf5 100644 --- a/packages/babel-plugin-proposal-optional-chaining/src/index.js +++ b/packages/babel-plugin-proposal-optional-chaining/src/index.js @@ -4,7 +4,7 @@ import { skipTransparentExprWrappers, } from "@babel/helper-skip-transparent-expression-wrappers"; import syntaxOptionalChaining from "@babel/plugin-syntax-optional-chaining"; -import { types as t } from "@babel/core"; +import { types as t, template } from "@babel/core"; export default declare((api, options) => { api.assertVersion(7); @@ -22,6 +22,30 @@ export default declare((api, options) => { ); } + /** + * Test if a given optional chain `path` needs to be memoized + * @param {NodePath} path + * @returns {boolean} + */ + function needsMemoize(path) { + let optionalPath = path; + const { scope } = path; + while ( + optionalPath.isOptionalMemberExpression() || + optionalPath.isOptionalCallExpression() + ) { + const { node } = optionalPath; + const childPath = skipTransparentExprWrappers( + optionalPath.get("object") ?? optionalPath.get("callee"), + ); + if (node.optional) { + return !scope.isStatic(childPath.node); + } + + optionalPath = childPath; + } + } + return { name: "proposal-optional-chaining", inherits: syntaxOptionalChaining, @@ -46,6 +70,13 @@ export default declare((api, options) => { const optionals = []; let optionalPath = path; + // Replace `function (a, x = a.b?.c) {}` to `function (a, x = (() => a.b?.c)() ){}` + // so the temporary variable can be injected in correct scope + if (scope.path.isPattern() && needsMemoize(optionalPath)) { + path.replaceWith(template.ast`(() => ${path.node})()`); + // The injected optional chain will be queued and eventually transformed when visited + return; + } while ( optionalPath.isOptionalMemberExpression() || optionalPath.isOptionalCallExpression() From 99ad23644818b736a458ae76614051a73d2059fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Fri, 4 Sep 2020 15:37:44 -0400 Subject: [PATCH 6/8] chore: add more testcases --- .../exec.js | 46 +++++++++ .../input.js | 46 +++++++++ .../output.js | 92 ++++++++++++++++++ .../optional-chain-in-function-param/exec.js | 46 +++++++++ .../optional-chain-in-function-param/input.js | 46 +++++++++ .../options.json | 4 + .../output.js | 88 +++++++++++++++++ .../exec.js | 46 +++++++++ .../input.js | 46 +++++++++ .../options.json | 6 ++ .../output.js | 96 +++++++++++++++++++ 11 files changed, 562 insertions(+) create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/exec.js create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/input.js create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/output.js create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param/exec.js create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param/input.js create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param/options.json create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param/output.js create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/exec.js create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/input.js create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/options.json create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/output.js diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/exec.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/exec.js new file mode 100644 index 000000000000..6eb26ab6e01a --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/exec.js @@ -0,0 +1,46 @@ +class Foo { + static #x = 1; + static #m = function() { return this.#x; }; + static #self = Foo; + static self = Foo; + static getSelf() { return this } + + static test() { + const o = { Foo: Foo }; + const deep = { very: { o } }; + function fn() { + return o; + } + function fnDeep() { + return deep; + } + + function f(o, r = o?.Foo.#m()) { + return r; + } + + function g(o, r = o?.Foo.#self.getSelf().#m()) { + return r; + } + + function h(fnDeep, r = fnDeep?.().very.o?.Foo?.#m()) { + return r; + } + + function i(fn, r = fn?.().Foo.#self?.getSelf()?.self.#m()) { + return r; + } + + function j(fn, r = (fn().Foo.#self.getSelf().self.#m)?.()) { + return r; + } + + expect(f(o)).toBe(1); + expect(g(o)).toBe(1); + expect(h(fnDeep)).toBe(1); + expect(i(fn)).toBe(1); + expect(j(fn)).toBe(1); + } +} + +Foo.test(); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/input.js new file mode 100644 index 000000000000..c5fe33636eca --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/input.js @@ -0,0 +1,46 @@ +class Foo { + static #x = 1; + static #m = function() { return this.#x; }; + static #self = Foo; + static self = Foo; + static getSelf() { return this } + + static test() { + const o = { Foo: Foo }; + const deep = { very: { o } }; + function fn() { + return o; + } + function fnDeep() { + return deep; + } + + function f(o, r = o?.Foo.#m()) { + return r; + } + + function g(o, r = o?.Foo.#self.getSelf().#m?.()) { + return r; + } + + function h(fnDeep, r = fnDeep?.().very.o?.Foo?.#m()) { + return r; + } + + function i(fn, r = fn?.().Foo.#self?.getSelf()?.self.#m()) { + return r; + } + + function j(fn, r = (fn().Foo.#self.getSelf().self.#m)?.()) { + return r; + } + + f(o); + g(o); + h(fnDeep); + i(fn); + j(fn); + } +} + +Foo.test(); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/output.js new file mode 100644 index 000000000000..fe7ddec253e6 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/output.js @@ -0,0 +1,92 @@ +var _x = babelHelpers.classPrivateFieldLooseKey("x"); + +var _m = babelHelpers.classPrivateFieldLooseKey("m"); + +var _self = babelHelpers.classPrivateFieldLooseKey("self"); + +var Foo = /*#__PURE__*/function () { + "use strict"; + + function Foo() { + babelHelpers.classCallCheck(this, Foo); + } + + babelHelpers.createClass(Foo, null, [{ + key: "getSelf", + value: function getSelf() { + return this; + } + }, { + key: "test", + value: function test() { + var o = { + Foo: Foo + }; + var deep = { + very: { + o + } + }; + + function fn() { + return o; + } + + function fnDeep() { + return deep; + } + + function f(o, r = (() => o === null || o === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(o.Foo, _m)[_m]())()) { + return r; + } + + function g(o, r = (() => o === null || o === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(babelHelpers.classPrivateFieldLooseBase(o.Foo, _self)[_self].getSelf(), _m)[_m])()?.()) { + return r; + } + + function h(fnDeep, r = (() => { + var _fnDeep$very$o$Foo; + + return (_fnDeep$very$o$Foo = fnDeep?.().very.o?.Foo) === null || _fnDeep$very$o$Foo === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(_fnDeep$very$o$Foo, _m)[_m](); + })()) { + return r; + } + + function i(fn, r = (() => { + var _getSelf; + + return (_getSelf = (() => fn === null || fn === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(fn().Foo, _self)[_self])()?.getSelf()) === null || _getSelf === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(_getSelf.self, _m)[_m](); + })()) { + return r; + } + + function j(fn, r = (() => babelHelpers.classPrivateFieldLooseBase(babelHelpers.classPrivateFieldLooseBase(fn().Foo, _self)[_self].getSelf().self, _m)[_m]?.())()) { + return r; + } + + f(o); + g(o); + h(fnDeep); + i(fn); + j(fn); + } + }]); + return Foo; +}(); + +Object.defineProperty(Foo, _x, { + writable: true, + value: 1 +}); +Object.defineProperty(Foo, _m, { + writable: true, + value: function () { + return babelHelpers.classPrivateFieldLooseBase(this, _x)[_x]; + } +}); +Object.defineProperty(Foo, _self, { + writable: true, + value: Foo +}); +Foo.self = Foo; +Foo.test(); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param/exec.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param/exec.js new file mode 100644 index 000000000000..6eb26ab6e01a --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param/exec.js @@ -0,0 +1,46 @@ +class Foo { + static #x = 1; + static #m = function() { return this.#x; }; + static #self = Foo; + static self = Foo; + static getSelf() { return this } + + static test() { + const o = { Foo: Foo }; + const deep = { very: { o } }; + function fn() { + return o; + } + function fnDeep() { + return deep; + } + + function f(o, r = o?.Foo.#m()) { + return r; + } + + function g(o, r = o?.Foo.#self.getSelf().#m()) { + return r; + } + + function h(fnDeep, r = fnDeep?.().very.o?.Foo?.#m()) { + return r; + } + + function i(fn, r = fn?.().Foo.#self?.getSelf()?.self.#m()) { + return r; + } + + function j(fn, r = (fn().Foo.#self.getSelf().self.#m)?.()) { + return r; + } + + expect(f(o)).toBe(1); + expect(g(o)).toBe(1); + expect(h(fnDeep)).toBe(1); + expect(i(fn)).toBe(1); + expect(j(fn)).toBe(1); + } +} + +Foo.test(); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param/input.js new file mode 100644 index 000000000000..c5fe33636eca --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param/input.js @@ -0,0 +1,46 @@ +class Foo { + static #x = 1; + static #m = function() { return this.#x; }; + static #self = Foo; + static self = Foo; + static getSelf() { return this } + + static test() { + const o = { Foo: Foo }; + const deep = { very: { o } }; + function fn() { + return o; + } + function fnDeep() { + return deep; + } + + function f(o, r = o?.Foo.#m()) { + return r; + } + + function g(o, r = o?.Foo.#self.getSelf().#m?.()) { + return r; + } + + function h(fnDeep, r = fnDeep?.().very.o?.Foo?.#m()) { + return r; + } + + function i(fn, r = fn?.().Foo.#self?.getSelf()?.self.#m()) { + return r; + } + + function j(fn, r = (fn().Foo.#self.getSelf().self.#m)?.()) { + return r; + } + + f(o); + g(o); + h(fnDeep); + i(fn); + j(fn); + } +} + +Foo.test(); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param/options.json new file mode 100644 index 000000000000..2d5cfe8e8095 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param/options.json @@ -0,0 +1,4 @@ +{ + "plugins": [["proposal-class-properties", { "loose": true }]], + "minNodeVersion": "14.0.0" +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param/output.js new file mode 100644 index 000000000000..2b7d864e3b04 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param/output.js @@ -0,0 +1,88 @@ +function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } + +var id = 0; + +function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } + +var _x = _classPrivateFieldLooseKey("x"); + +var _m = _classPrivateFieldLooseKey("m"); + +var _self = _classPrivateFieldLooseKey("self"); + +class Foo { + static getSelf() { + return this; + } + + static test() { + const o = { + Foo: Foo + }; + const deep = { + very: { + o + } + }; + + function fn() { + return o; + } + + function fnDeep() { + return deep; + } + + function f(o, r = (() => o === null || o === void 0 ? void 0 : _classPrivateFieldLooseBase(o.Foo, _m)[_m]())()) { + return r; + } + + function g(o, r = (() => o === null || o === void 0 ? void 0 : _classPrivateFieldLooseBase(_classPrivateFieldLooseBase(o.Foo, _self)[_self].getSelf(), _m)[_m])()?.()) { + return r; + } + + function h(fnDeep, r = (() => { + var _fnDeep$very$o$Foo; + + return (_fnDeep$very$o$Foo = fnDeep?.().very.o?.Foo) === null || _fnDeep$very$o$Foo === void 0 ? void 0 : _classPrivateFieldLooseBase(_fnDeep$very$o$Foo, _m)[_m](); + })()) { + return r; + } + + function i(fn, r = (() => { + var _getSelf; + + return (_getSelf = (() => fn === null || fn === void 0 ? void 0 : _classPrivateFieldLooseBase(fn().Foo, _self)[_self])()?.getSelf()) === null || _getSelf === void 0 ? void 0 : _classPrivateFieldLooseBase(_getSelf.self, _m)[_m](); + })()) { + return r; + } + + function j(fn, r = (() => _classPrivateFieldLooseBase(_classPrivateFieldLooseBase(fn().Foo, _self)[_self].getSelf().self, _m)[_m]?.())()) { + return r; + } + + f(o); + g(o); + h(fnDeep); + i(fn); + j(fn); + } + +} + +Object.defineProperty(Foo, _x, { + writable: true, + value: 1 +}); +Object.defineProperty(Foo, _m, { + writable: true, + value: function () { + return _classPrivateFieldLooseBase(this, _x)[_x]; + } +}); +Object.defineProperty(Foo, _self, { + writable: true, + value: Foo +}); +Foo.self = Foo; +Foo.test(); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/exec.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/exec.js new file mode 100644 index 000000000000..6eb26ab6e01a --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/exec.js @@ -0,0 +1,46 @@ +class Foo { + static #x = 1; + static #m = function() { return this.#x; }; + static #self = Foo; + static self = Foo; + static getSelf() { return this } + + static test() { + const o = { Foo: Foo }; + const deep = { very: { o } }; + function fn() { + return o; + } + function fnDeep() { + return deep; + } + + function f(o, r = o?.Foo.#m()) { + return r; + } + + function g(o, r = o?.Foo.#self.getSelf().#m()) { + return r; + } + + function h(fnDeep, r = fnDeep?.().very.o?.Foo?.#m()) { + return r; + } + + function i(fn, r = fn?.().Foo.#self?.getSelf()?.self.#m()) { + return r; + } + + function j(fn, r = (fn().Foo.#self.getSelf().self.#m)?.()) { + return r; + } + + expect(f(o)).toBe(1); + expect(g(o)).toBe(1); + expect(h(fnDeep)).toBe(1); + expect(i(fn)).toBe(1); + expect(j(fn)).toBe(1); + } +} + +Foo.test(); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/input.js new file mode 100644 index 000000000000..c5fe33636eca --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/input.js @@ -0,0 +1,46 @@ +class Foo { + static #x = 1; + static #m = function() { return this.#x; }; + static #self = Foo; + static self = Foo; + static getSelf() { return this } + + static test() { + const o = { Foo: Foo }; + const deep = { very: { o } }; + function fn() { + return o; + } + function fnDeep() { + return deep; + } + + function f(o, r = o?.Foo.#m()) { + return r; + } + + function g(o, r = o?.Foo.#self.getSelf().#m?.()) { + return r; + } + + function h(fnDeep, r = fnDeep?.().very.o?.Foo?.#m()) { + return r; + } + + function i(fn, r = fn?.().Foo.#self?.getSelf()?.self.#m()) { + return r; + } + + function j(fn, r = (fn().Foo.#self.getSelf().self.#m)?.()) { + return r; + } + + f(o); + g(o); + h(fnDeep); + i(fn); + j(fn); + } +} + +Foo.test(); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/options.json new file mode 100644 index 000000000000..124133b5af2b --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + ["proposal-optional-chaining", { "loose": true }], + ["proposal-class-properties", { "loose": true }] + ] +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/output.js new file mode 100644 index 000000000000..fb3516c4b811 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/output.js @@ -0,0 +1,96 @@ +function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } + +var id = 0; + +function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } + +var _x = _classPrivateFieldLooseKey("x"); + +var _m = _classPrivateFieldLooseKey("m"); + +var _self = _classPrivateFieldLooseKey("self"); + +class Foo { + static getSelf() { + return this; + } + + static test() { + const o = { + Foo: Foo + }; + const deep = { + very: { + o + } + }; + + function fn() { + return o; + } + + function fnDeep() { + return deep; + } + + function f(o, r = (() => o === null || o === void 0 ? void 0 : _classPrivateFieldLooseBase(o.Foo, _m)[_m]())()) { + return r; + } + + function g(o, r = (() => { + var _ref; + + return (_ref = (() => o === null || o === void 0 ? void 0 : _classPrivateFieldLooseBase(_classPrivateFieldLooseBase(o.Foo, _self)[_self].getSelf(), _m)[_m])()) == null ? void 0 : _ref(); + })()) { + return r; + } + + function h(fnDeep, r = (() => { + var _fnDeep$very$o$Foo, _fnDeep$very$o; + + return (_fnDeep$very$o$Foo = fnDeep == null ? void 0 : (_fnDeep$very$o = fnDeep().very.o) == null ? void 0 : _fnDeep$very$o.Foo) === null || _fnDeep$very$o$Foo === void 0 ? void 0 : _classPrivateFieldLooseBase(_fnDeep$very$o$Foo, _m)[_m](); + })()) { + return r; + } + + function i(fn, r = (() => { + var _getSelf, _ref2; + + return (_getSelf = (_ref2 = (() => fn === null || fn === void 0 ? void 0 : _classPrivateFieldLooseBase(fn().Foo, _self)[_self])()) == null ? void 0 : _ref2.getSelf()) === null || _getSelf === void 0 ? void 0 : _classPrivateFieldLooseBase(_getSelf.self, _m)[_m](); + })()) { + return r; + } + + function j(fn, r = (() => { + var _classPrivateFieldLoo, _classPrivateFieldLoo2; + + return (_classPrivateFieldLoo = (_classPrivateFieldLoo2 = _classPrivateFieldLooseBase(_classPrivateFieldLooseBase(fn().Foo, _self)[_self].getSelf().self, _m))[_m]) == null ? void 0 : _classPrivateFieldLoo.call(_classPrivateFieldLoo2); + })()) { + return r; + } + + f(o); + g(o); + h(fnDeep); + i(fn); + j(fn); + } + +} + +Object.defineProperty(Foo, _x, { + writable: true, + value: 1 +}); +Object.defineProperty(Foo, _m, { + writable: true, + value: function () { + return _classPrivateFieldLooseBase(this, _x)[_x]; + } +}); +Object.defineProperty(Foo, _self, { + writable: true, + value: Foo +}); +Foo.self = Foo; +Foo.test(); From 47b51c4f91ab1b92c7d56a3ac42420c8fdb7e314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Wed, 9 Sep 2020 17:36:34 -0400 Subject: [PATCH 7/8] chore: update test fixtures --- .../options.json | 6 + .../output.js | 122 +++++++++--------- .../options.json | 5 +- .../output.js | 48 ++++--- 4 files changed, 93 insertions(+), 88 deletions(-) create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/options.json diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/options.json new file mode 100644 index 000000000000..124133b5af2b --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + ["proposal-optional-chaining", { "loose": true }], + ["proposal-class-properties", { "loose": true }] + ] +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/output.js index fe7ddec253e6..fb3516c4b811 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/optional-chain-in-function-param-with-transform/output.js @@ -1,78 +1,82 @@ -var _x = babelHelpers.classPrivateFieldLooseKey("x"); +function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } -var _m = babelHelpers.classPrivateFieldLooseKey("m"); +var id = 0; -var _self = babelHelpers.classPrivateFieldLooseKey("self"); +function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } -var Foo = /*#__PURE__*/function () { - "use strict"; +var _x = _classPrivateFieldLooseKey("x"); - function Foo() { - babelHelpers.classCallCheck(this, Foo); +var _m = _classPrivateFieldLooseKey("m"); + +var _self = _classPrivateFieldLooseKey("self"); + +class Foo { + static getSelf() { + return this; } - babelHelpers.createClass(Foo, null, [{ - key: "getSelf", - value: function getSelf() { - return this; - } - }, { - key: "test", - value: function test() { - var o = { - Foo: Foo - }; - var deep = { - very: { - o - } - }; - - function fn() { - return o; + static test() { + const o = { + Foo: Foo + }; + const deep = { + very: { + o } + }; - function fnDeep() { - return deep; - } + function fn() { + return o; + } - function f(o, r = (() => o === null || o === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(o.Foo, _m)[_m]())()) { - return r; - } + function fnDeep() { + return deep; + } - function g(o, r = (() => o === null || o === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(babelHelpers.classPrivateFieldLooseBase(o.Foo, _self)[_self].getSelf(), _m)[_m])()?.()) { - return r; - } + function f(o, r = (() => o === null || o === void 0 ? void 0 : _classPrivateFieldLooseBase(o.Foo, _m)[_m]())()) { + return r; + } - function h(fnDeep, r = (() => { - var _fnDeep$very$o$Foo; + function g(o, r = (() => { + var _ref; - return (_fnDeep$very$o$Foo = fnDeep?.().very.o?.Foo) === null || _fnDeep$very$o$Foo === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(_fnDeep$very$o$Foo, _m)[_m](); - })()) { - return r; - } + return (_ref = (() => o === null || o === void 0 ? void 0 : _classPrivateFieldLooseBase(_classPrivateFieldLooseBase(o.Foo, _self)[_self].getSelf(), _m)[_m])()) == null ? void 0 : _ref(); + })()) { + return r; + } - function i(fn, r = (() => { - var _getSelf; + function h(fnDeep, r = (() => { + var _fnDeep$very$o$Foo, _fnDeep$very$o; - return (_getSelf = (() => fn === null || fn === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(fn().Foo, _self)[_self])()?.getSelf()) === null || _getSelf === void 0 ? void 0 : babelHelpers.classPrivateFieldLooseBase(_getSelf.self, _m)[_m](); - })()) { - return r; - } + return (_fnDeep$very$o$Foo = fnDeep == null ? void 0 : (_fnDeep$very$o = fnDeep().very.o) == null ? void 0 : _fnDeep$very$o.Foo) === null || _fnDeep$very$o$Foo === void 0 ? void 0 : _classPrivateFieldLooseBase(_fnDeep$very$o$Foo, _m)[_m](); + })()) { + return r; + } - function j(fn, r = (() => babelHelpers.classPrivateFieldLooseBase(babelHelpers.classPrivateFieldLooseBase(fn().Foo, _self)[_self].getSelf().self, _m)[_m]?.())()) { - return r; - } + function i(fn, r = (() => { + var _getSelf, _ref2; + + return (_getSelf = (_ref2 = (() => fn === null || fn === void 0 ? void 0 : _classPrivateFieldLooseBase(fn().Foo, _self)[_self])()) == null ? void 0 : _ref2.getSelf()) === null || _getSelf === void 0 ? void 0 : _classPrivateFieldLooseBase(_getSelf.self, _m)[_m](); + })()) { + return r; + } + + function j(fn, r = (() => { + var _classPrivateFieldLoo, _classPrivateFieldLoo2; - f(o); - g(o); - h(fnDeep); - i(fn); - j(fn); + return (_classPrivateFieldLoo = (_classPrivateFieldLoo2 = _classPrivateFieldLooseBase(_classPrivateFieldLooseBase(fn().Foo, _self)[_self].getSelf().self, _m))[_m]) == null ? void 0 : _classPrivateFieldLoo.call(_classPrivateFieldLoo2); + })()) { + return r; } - }]); - return Foo; -}(); + + f(o); + g(o); + h(fnDeep); + i(fn); + j(fn); + } + +} Object.defineProperty(Foo, _x, { writable: true, @@ -81,7 +85,7 @@ Object.defineProperty(Foo, _x, { Object.defineProperty(Foo, _m, { writable: true, value: function () { - return babelHelpers.classPrivateFieldLooseBase(this, _x)[_x]; + return _classPrivateFieldLooseBase(this, _x)[_x]; } }); Object.defineProperty(Foo, _self, { diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/options.json index 124133b5af2b..63b4c77cc8e8 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/options.json +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/options.json @@ -1,6 +1,3 @@ { - "plugins": [ - ["proposal-optional-chaining", { "loose": true }], - ["proposal-class-properties", { "loose": true }] - ] + "plugins": ["proposal-optional-chaining", "proposal-class-properties"] } diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/output.js index fb3516c4b811..1c52675c3085 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/optional-chain-in-function-param-with-transform/output.js @@ -1,14 +1,6 @@ -function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } +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; } -var id = 0; - -function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } - -var _x = _classPrivateFieldLooseKey("x"); - -var _m = _classPrivateFieldLooseKey("m"); - -var _self = _classPrivateFieldLooseKey("self"); +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 { static getSelf() { @@ -33,14 +25,18 @@ class Foo { return deep; } - function f(o, r = (() => o === null || o === void 0 ? void 0 : _classPrivateFieldLooseBase(o.Foo, _m)[_m]())()) { + function f(o, r = (() => { + var _o$Foo; + + return o === null || o === void 0 ? void 0 : _classStaticPrivateFieldSpecGet(_o$Foo = o.Foo, Foo, _m).call(_o$Foo); + })()) { return r; } function g(o, r = (() => { var _ref; - return (_ref = (() => o === null || o === void 0 ? void 0 : _classPrivateFieldLooseBase(_classPrivateFieldLooseBase(o.Foo, _self)[_self].getSelf(), _m)[_m])()) == null ? void 0 : _ref(); + return (_ref = (() => o === null || o === void 0 ? void 0 : _classStaticPrivateFieldSpecGet(_classStaticPrivateFieldSpecGet(o.Foo, Foo, _self).getSelf(), Foo, _m))()) === null || _ref === void 0 ? void 0 : _ref(); })()) { return r; } @@ -48,23 +44,23 @@ class Foo { function h(fnDeep, r = (() => { var _fnDeep$very$o$Foo, _fnDeep$very$o; - return (_fnDeep$very$o$Foo = fnDeep == null ? void 0 : (_fnDeep$very$o = fnDeep().very.o) == null ? void 0 : _fnDeep$very$o.Foo) === null || _fnDeep$very$o$Foo === void 0 ? void 0 : _classPrivateFieldLooseBase(_fnDeep$very$o$Foo, _m)[_m](); + return (_fnDeep$very$o$Foo = fnDeep === null || fnDeep === void 0 ? void 0 : (_fnDeep$very$o = fnDeep().very.o) === null || _fnDeep$very$o === void 0 ? void 0 : _fnDeep$very$o.Foo) === null || _fnDeep$very$o$Foo === void 0 ? void 0 : _classStaticPrivateFieldSpecGet(_fnDeep$very$o$Foo, Foo, _m).call(_fnDeep$very$o$Foo); })()) { return r; } function i(fn, r = (() => { - var _getSelf, _ref2; + var _getSelf, _getSelf$self, _ref2; - return (_getSelf = (_ref2 = (() => fn === null || fn === void 0 ? void 0 : _classPrivateFieldLooseBase(fn().Foo, _self)[_self])()) == null ? void 0 : _ref2.getSelf()) === null || _getSelf === void 0 ? void 0 : _classPrivateFieldLooseBase(_getSelf.self, _m)[_m](); + return (_getSelf = (_ref2 = (() => fn === null || fn === void 0 ? void 0 : _classStaticPrivateFieldSpecGet(fn().Foo, Foo, _self))()) === null || _ref2 === void 0 ? void 0 : _ref2.getSelf()) === null || _getSelf === void 0 ? void 0 : _classStaticPrivateFieldSpecGet(_getSelf$self = _getSelf.self, Foo, _m).call(_getSelf$self); })()) { return r; } function j(fn, r = (() => { - var _classPrivateFieldLoo, _classPrivateFieldLoo2; + var _classStaticPrivateFi, _classStaticPrivateFi2; - return (_classPrivateFieldLoo = (_classPrivateFieldLoo2 = _classPrivateFieldLooseBase(_classPrivateFieldLooseBase(fn().Foo, _self)[_self].getSelf().self, _m))[_m]) == null ? void 0 : _classPrivateFieldLoo.call(_classPrivateFieldLoo2); + return (_classStaticPrivateFi2 = _classStaticPrivateFieldSpecGet(_classStaticPrivateFi = _classStaticPrivateFieldSpecGet(fn().Foo, Foo, _self).getSelf().self, Foo, _m)) === null || _classStaticPrivateFi2 === void 0 ? void 0 : _classStaticPrivateFi2.call(_classStaticPrivateFi); })()) { return r; } @@ -78,19 +74,21 @@ class Foo { } -Object.defineProperty(Foo, _x, { +var _x = { writable: true, value: 1 -}); -Object.defineProperty(Foo, _m, { +}; +var _m = { writable: true, value: function () { - return _classPrivateFieldLooseBase(this, _x)[_x]; + return _classStaticPrivateFieldSpecGet(this, Foo, _x); } -}); -Object.defineProperty(Foo, _self, { +}; +var _self = { writable: true, value: Foo -}); -Foo.self = Foo; +}; + +_defineProperty(Foo, "self", Foo); + Foo.test(); From dc257bff519565f3deacc26f80e7011faac685dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Wed, 23 Sep 2020 17:39:45 -0400 Subject: [PATCH 8/8] fix: NodePath.get is always non nullish --- .../src/index.js | 7 ++-- .../general/in-function-params-loose/input.js | 9 +++++ .../in-function-params-loose/options.json | 3 ++ .../in-function-params-loose/output.js | 34 +++++++++++++++++++ .../general/in-function-params/input.js | 2 ++ .../general/in-function-params/output.js | 8 +++++ 6 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params-loose/input.js create mode 100644 packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params-loose/options.json create mode 100644 packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params-loose/output.js diff --git a/packages/babel-plugin-proposal-optional-chaining/src/index.js b/packages/babel-plugin-proposal-optional-chaining/src/index.js index 3fb69e104bf5..f9f8f99ab84d 100644 --- a/packages/babel-plugin-proposal-optional-chaining/src/index.js +++ b/packages/babel-plugin-proposal-optional-chaining/src/index.js @@ -35,9 +35,10 @@ export default declare((api, options) => { optionalPath.isOptionalCallExpression() ) { const { node } = optionalPath; - const childPath = skipTransparentExprWrappers( - optionalPath.get("object") ?? optionalPath.get("callee"), - ); + const childKey = optionalPath.isOptionalMemberExpression() + ? "object" + : "callee"; + const childPath = skipTransparentExprWrappers(optionalPath.get(childKey)); if (node.optional) { return !scope.isStatic(childPath.node); } diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params-loose/input.js b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params-loose/input.js new file mode 100644 index 000000000000..882511b447f8 --- /dev/null +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params-loose/input.js @@ -0,0 +1,9 @@ +function f(a = x?.y) {} + +function g({ a, b = a?.c }) {} + +function h(a, { b = a.b?.c?.d.e }) {} + +function i(a, { b = (a.b?.c?.d).e }) {} + +function j(a, { b = a?.b?.c().d.e }) {} diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params-loose/options.json b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params-loose/options.json new file mode 100644 index 000000000000..39ea3f99c7ff --- /dev/null +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params-loose/options.json @@ -0,0 +1,3 @@ +{ + "plugins": [["proposal-optional-chaining", { "loose": true }]] +} diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params-loose/output.js b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params-loose/output.js new file mode 100644 index 000000000000..f5cda260cbfe --- /dev/null +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params-loose/output.js @@ -0,0 +1,34 @@ +function f(a = (() => { + var _x; + + return (_x = x) == null ? void 0 : _x.y; +})()) {} + +function g({ + a, + b = a == null ? void 0 : a.c +}) {} + +function h(a, { + b = (() => { + var _a$b, _a$b$c; + + return (_a$b = a.b) == null ? void 0 : (_a$b$c = _a$b.c) == null ? void 0 : _a$b$c.d.e; + })() +}) {} + +function i(a, { + b = (() => { + var _a$b2, _a$b2$c; + + return (_a$b2 = a.b) == null ? void 0 : (_a$b2$c = _a$b2.c) == null ? void 0 : _a$b2$c.d; + })().e +}) {} + +function j(a, { + b = (() => { + var _a$b3; + + return a == null ? void 0 : (_a$b3 = a.b) == null ? void 0 : _a$b3.c().d.e; + })() +}) {} diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params/input.js b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params/input.js index 802d92c1342c..882511b447f8 100644 --- a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params/input.js +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params/input.js @@ -5,3 +5,5 @@ function g({ a, b = a?.c }) {} function h(a, { b = a.b?.c?.d.e }) {} function i(a, { b = (a.b?.c?.d).e }) {} + +function j(a, { b = a?.b?.c().d.e }) {} diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params/output.js b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params/output.js index e2c8101d8d32..8d7909dc76fa 100644 --- a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params/output.js +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/general/in-function-params/output.js @@ -24,3 +24,11 @@ function i(a, { return (_a$b2 = a.b) === null || _a$b2 === void 0 ? void 0 : (_a$b2$c = _a$b2.c) === null || _a$b2$c === void 0 ? void 0 : _a$b2$c.d; })().e }) {} + +function j(a, { + b = (() => { + var _a$b3; + + return a === null || a === void 0 ? void 0 : (_a$b3 = a.b) === null || _a$b3 === void 0 ? void 0 : _a$b3.c().d.e; + })() +}) {}