From fad47596a64a8d33aadacb7695dbf1aa60a528ae Mon Sep 17 00:00:00 2001 From: Mickey Rose Date: Thu, 5 Aug 2021 12:01:45 +0200 Subject: [PATCH 1/3] overload `skipTransparentExprWrappers` * fixes isSimpleMemberExpression in @babel/plugin-proposal-optional-chaining * use skipTransparentExprWrappers instead of the NodePath overload where possible in @babel/plugin-proposal-optional-chaining --- .../src/index.ts | 26 ++++++++++++++----- .../src/transform.js | 15 +++-------- .../ts-as-function-call-loose/output.js | 4 +-- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/packages/babel-helper-skip-transparent-expression-wrappers/src/index.ts b/packages/babel-helper-skip-transparent-expression-wrappers/src/index.ts index edc336543dda..80c6e358cf1e 100644 --- a/packages/babel-helper-skip-transparent-expression-wrappers/src/index.ts +++ b/packages/babel-helper-skip-transparent-expression-wrappers/src/index.ts @@ -1,3 +1,4 @@ +import { NodePath } from "@babel/traverse"; import { isParenthesizedExpression, isTSAsExpression, @@ -7,7 +8,6 @@ import { } from "@babel/types"; import type * as t from "@babel/types"; -import type { NodePath } from "@babel/traverse"; export type TransparentExprWrapper = | t.TSAsExpression @@ -33,11 +33,25 @@ export function isTransparentExprWrapper( ); } +type ExprNodeOrPath = t.Expression | NodePath; + +export function skipTransparentExprWrappers(expr: t.Expression): t.Expression; + +export function skipTransparentExprWrappers( + expr: NodePath, +): NodePath; + export function skipTransparentExprWrappers( - path: NodePath, -): NodePath { - while (isTransparentExprWrapper(path.node)) { - path = path.get("expression"); + expr: ExprNodeOrPath, +): ExprNodeOrPath { + if (expr instanceof NodePath) { + while (isTransparentExprWrapper(expr.node)) { + expr = expr.get("expression"); + } + } else { + while (isTransparentExprWrapper(expr)) { + expr = expr.expression; + } } - return path; + return expr; } diff --git a/packages/babel-plugin-proposal-optional-chaining/src/transform.js b/packages/babel-plugin-proposal-optional-chaining/src/transform.js index 656b4ea09e6c..9bdbbf87f09d 100644 --- a/packages/babel-plugin-proposal-optional-chaining/src/transform.js +++ b/packages/babel-plugin-proposal-optional-chaining/src/transform.js @@ -1,8 +1,5 @@ import { types as t, template } from "@babel/core"; -import { - isTransparentExprWrapper, - skipTransparentExprWrappers, -} from "@babel/helper-skip-transparent-expression-wrappers"; +import { skipTransparentExprWrappers } from "@babel/helper-skip-transparent-expression-wrappers"; import { willPathCastToBoolean, findOutermostTransparentParent } from "./util"; const { ast } = template.expression; @@ -103,11 +100,7 @@ export function transform( const replaceKey = isCall ? "callee" : "object"; const chainWithTypes = node[replaceKey]; - let chain = chainWithTypes; - - while (isTransparentExprWrapper(chain)) { - chain = chain.expression; - } + const chain = skipTransparentExprWrappers(chainWithTypes); let ref; let check; @@ -169,9 +162,7 @@ export function transform( // i.e. `?.b` in `(a?.b.c)()` if (i === 0 && parentIsCall) { // `(a?.b)()` to `(a == null ? undefined : a.b.bind(a))()` - const object = skipTransparentExprWrappers( - replacementPath.get("object"), - ).node; + const object = skipTransparentExprWrappers(replacement.object); let baseRef; if (!pureGetters || !isSimpleMemberExpression(object)) { // memoize the context object when getters are not always pure diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/transparent-expr-wrappers/ts-as-function-call-loose/output.js b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/transparent-expr-wrappers/ts-as-function-call-loose/output.js index 93a8b5c45d88..6638d5a0908e 100644 --- a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/transparent-expr-wrappers/ts-as-function-call-loose/output.js +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/transparent-expr-wrappers/ts-as-function-call-loose/output.js @@ -1,3 +1 @@ -var _bar, _ref; - -(_bar = ((_ref = (foo as A)).bar as B)) == null ? void 0 : _bar.call(_ref, foo.bar, false); +((foo as A).bar as B) == null ? void 0 : ((foo as A).bar as B)(foo.bar, false); From 3ec1256a14bd58a0f3b420f1418167368252e684 Mon Sep 17 00:00:00 2001 From: Mickey Rose Date: Sat, 21 Aug 2021 00:36:22 +0200 Subject: [PATCH 2/3] Revert "overload `skipTransparentExprWrappers`" This reverts commit fad47596a64a8d33aadacb7695dbf1aa60a528ae. --- .../src/index.ts | 26 +++++-------------- .../src/transform.js | 15 ++++++++--- .../ts-as-function-call-loose/output.js | 4 ++- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/packages/babel-helper-skip-transparent-expression-wrappers/src/index.ts b/packages/babel-helper-skip-transparent-expression-wrappers/src/index.ts index 80c6e358cf1e..edc336543dda 100644 --- a/packages/babel-helper-skip-transparent-expression-wrappers/src/index.ts +++ b/packages/babel-helper-skip-transparent-expression-wrappers/src/index.ts @@ -1,4 +1,3 @@ -import { NodePath } from "@babel/traverse"; import { isParenthesizedExpression, isTSAsExpression, @@ -8,6 +7,7 @@ import { } from "@babel/types"; import type * as t from "@babel/types"; +import type { NodePath } from "@babel/traverse"; export type TransparentExprWrapper = | t.TSAsExpression @@ -33,25 +33,11 @@ export function isTransparentExprWrapper( ); } -type ExprNodeOrPath = t.Expression | NodePath; - -export function skipTransparentExprWrappers(expr: t.Expression): t.Expression; - -export function skipTransparentExprWrappers( - expr: NodePath, -): NodePath; - export function skipTransparentExprWrappers( - expr: ExprNodeOrPath, -): ExprNodeOrPath { - if (expr instanceof NodePath) { - while (isTransparentExprWrapper(expr.node)) { - expr = expr.get("expression"); - } - } else { - while (isTransparentExprWrapper(expr)) { - expr = expr.expression; - } + path: NodePath, +): NodePath { + while (isTransparentExprWrapper(path.node)) { + path = path.get("expression"); } - return expr; + return path; } diff --git a/packages/babel-plugin-proposal-optional-chaining/src/transform.js b/packages/babel-plugin-proposal-optional-chaining/src/transform.js index 9bdbbf87f09d..656b4ea09e6c 100644 --- a/packages/babel-plugin-proposal-optional-chaining/src/transform.js +++ b/packages/babel-plugin-proposal-optional-chaining/src/transform.js @@ -1,5 +1,8 @@ import { types as t, template } from "@babel/core"; -import { skipTransparentExprWrappers } from "@babel/helper-skip-transparent-expression-wrappers"; +import { + isTransparentExprWrapper, + skipTransparentExprWrappers, +} from "@babel/helper-skip-transparent-expression-wrappers"; import { willPathCastToBoolean, findOutermostTransparentParent } from "./util"; const { ast } = template.expression; @@ -100,7 +103,11 @@ export function transform( const replaceKey = isCall ? "callee" : "object"; const chainWithTypes = node[replaceKey]; - const chain = skipTransparentExprWrappers(chainWithTypes); + let chain = chainWithTypes; + + while (isTransparentExprWrapper(chain)) { + chain = chain.expression; + } let ref; let check; @@ -162,7 +169,9 @@ export function transform( // i.e. `?.b` in `(a?.b.c)()` if (i === 0 && parentIsCall) { // `(a?.b)()` to `(a == null ? undefined : a.b.bind(a))()` - const object = skipTransparentExprWrappers(replacement.object); + const object = skipTransparentExprWrappers( + replacementPath.get("object"), + ).node; let baseRef; if (!pureGetters || !isSimpleMemberExpression(object)) { // memoize the context object when getters are not always pure diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/transparent-expr-wrappers/ts-as-function-call-loose/output.js b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/transparent-expr-wrappers/ts-as-function-call-loose/output.js index 6638d5a0908e..93a8b5c45d88 100644 --- a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/transparent-expr-wrappers/ts-as-function-call-loose/output.js +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/transparent-expr-wrappers/ts-as-function-call-loose/output.js @@ -1 +1,3 @@ -((foo as A).bar as B) == null ? void 0 : ((foo as A).bar as B)(foo.bar, false); +var _bar, _ref; + +(_bar = ((_ref = (foo as A)).bar as B)) == null ? void 0 : _bar.call(_ref, foo.bar, false); From 8f26798bd1db86a7118bff47232130d00b396eab Mon Sep 17 00:00:00 2001 From: Mickey Rose Date: Thu, 5 Aug 2021 12:01:45 +0200 Subject: [PATCH 3/3] add helper `skipTransparentExprWrapperNodes` * avoids constructing NodePaths when only the Node is needed * fixes isSimpleMemberExpression in @babel/plugin-proposal-optional-chaining * use this new helper instead of skipTransparentExprWrappers where possible in @babel/plugin-proposal-optional-chaining --- .../src/index.ts | 9 +++++++++ .../src/transform.js | 14 ++++---------- .../ts-as-function-call-loose/output.js | 4 +--- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/babel-helper-skip-transparent-expression-wrappers/src/index.ts b/packages/babel-helper-skip-transparent-expression-wrappers/src/index.ts index edc336543dda..e3da1587a8fe 100644 --- a/packages/babel-helper-skip-transparent-expression-wrappers/src/index.ts +++ b/packages/babel-helper-skip-transparent-expression-wrappers/src/index.ts @@ -41,3 +41,12 @@ export function skipTransparentExprWrappers( } return path; } + +export function skipTransparentExprWrapperNodes( + node: t.Expression, +): t.Expression { + while (isTransparentExprWrapper(node)) { + node = node.expression; + } + return node; +} diff --git a/packages/babel-plugin-proposal-optional-chaining/src/transform.js b/packages/babel-plugin-proposal-optional-chaining/src/transform.js index 656b4ea09e6c..05ae81bdb433 100644 --- a/packages/babel-plugin-proposal-optional-chaining/src/transform.js +++ b/packages/babel-plugin-proposal-optional-chaining/src/transform.js @@ -1,6 +1,6 @@ import { types as t, template } from "@babel/core"; import { - isTransparentExprWrapper, + skipTransparentExprWrapperNodes, skipTransparentExprWrappers, } from "@babel/helper-skip-transparent-expression-wrappers"; import { willPathCastToBoolean, findOutermostTransparentParent } from "./util"; @@ -8,7 +8,7 @@ import { willPathCastToBoolean, findOutermostTransparentParent } from "./util"; const { ast } = template.expression; function isSimpleMemberExpression(expression) { - expression = skipTransparentExprWrappers(expression); + expression = skipTransparentExprWrapperNodes(expression); return ( t.isIdentifier(expression) || t.isSuper(expression) || @@ -103,11 +103,7 @@ export function transform( const replaceKey = isCall ? "callee" : "object"; const chainWithTypes = node[replaceKey]; - let chain = chainWithTypes; - - while (isTransparentExprWrapper(chain)) { - chain = chain.expression; - } + const chain = skipTransparentExprWrapperNodes(chainWithTypes); let ref; let check; @@ -169,9 +165,7 @@ export function transform( // i.e. `?.b` in `(a?.b.c)()` if (i === 0 && parentIsCall) { // `(a?.b)()` to `(a == null ? undefined : a.b.bind(a))()` - const object = skipTransparentExprWrappers( - replacementPath.get("object"), - ).node; + const object = skipTransparentExprWrapperNodes(replacement.object); let baseRef; if (!pureGetters || !isSimpleMemberExpression(object)) { // memoize the context object when getters are not always pure diff --git a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/transparent-expr-wrappers/ts-as-function-call-loose/output.js b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/transparent-expr-wrappers/ts-as-function-call-loose/output.js index 93a8b5c45d88..6638d5a0908e 100644 --- a/packages/babel-plugin-proposal-optional-chaining/test/fixtures/transparent-expr-wrappers/ts-as-function-call-loose/output.js +++ b/packages/babel-plugin-proposal-optional-chaining/test/fixtures/transparent-expr-wrappers/ts-as-function-call-loose/output.js @@ -1,3 +1 @@ -var _bar, _ref; - -(_bar = ((_ref = (foo as A)).bar as B)) == null ? void 0 : _bar.call(_ref, foo.bar, false); +((foo as A).bar as B) == null ? void 0 : ((foo as A).bar as B)(foo.bar, false);