From 3bf1bbbaace99b2ea6b46d5340360b15bf490b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 2 Jun 2022 09:58:17 -0400 Subject: [PATCH] Improve pipeline-operator typings (#14629) * refactor: simplify buildOptimizedSequenceExpression * proposal-pipeline-operator * address review comments --- .../src/buildOptimizedSequenceExpression.ts | 67 ++++++++++++------- .../src/fsharpVisitor.ts | 7 +- .../src/hackVisitor.ts | 8 ++- .../src/minimalVisitor.ts | 7 +- 4 files changed, 54 insertions(+), 35 deletions(-) diff --git a/packages/babel-plugin-proposal-pipeline-operator/src/buildOptimizedSequenceExpression.ts b/packages/babel-plugin-proposal-pipeline-operator/src/buildOptimizedSequenceExpression.ts index 44909eac757a..e15e7eb3801b 100644 --- a/packages/babel-plugin-proposal-pipeline-operator/src/buildOptimizedSequenceExpression.ts +++ b/packages/babel-plugin-proposal-pipeline-operator/src/buildOptimizedSequenceExpression.ts @@ -1,58 +1,73 @@ import { types as t } from "@babel/core"; +import type { NodePath } from "@babel/traverse"; // tries to optimize sequence expressions in the format // (a = b, (c => c + e)(a)) // to // (a = b, a + e) -const buildOptimizedSequenceExpression = ({ call, path, placeholder }) => { + +type Options = { + call: t.CallExpression | t.AwaitExpression; + path: NodePath" }>; + placeholder: t.Identifier; +}; + +function isConciseArrowExpression( + node: t.Node, +): node is t.ArrowFunctionExpression & { body: t.Expression } { + return ( + t.isArrowFunctionExpression(node) && + t.isExpression(node.body) && + !node.async + ); +} + +const buildOptimizedSequenceExpression = ({ + call, + path, + placeholder, +}: Options) => { + // @ts-expect-error AwaitExpression does not have callee property const { callee: calledExpression } = call; - const pipelineLeft = path.node.left; + // pipelineLeft must not be a PrivateName + const pipelineLeft = path.node.left as t.Expression; const assign = t.assignmentExpression( "=", t.cloneNode(placeholder), pipelineLeft, ); - let optimizeArrow = - t.isArrowFunctionExpression(calledExpression) && - t.isExpression(calledExpression.body) && - !calledExpression.async && - !calledExpression.generator; - let param; + const expressionIsArrow = isConciseArrowExpression(calledExpression); - if (optimizeArrow) { + if (expressionIsArrow) { + let param; + let optimizeArrow = true; const { params } = calledExpression; if (params.length === 1 && t.isIdentifier(params[0])) { param = params[0]; } else if (params.length > 0) { optimizeArrow = false; } + if (optimizeArrow && !param) { + // fixme: arrow function with 1 pattern argument will also go into this branch + // Arrow function with 0 arguments + return t.sequenceExpression([pipelineLeft, calledExpression.body]); + } else if (param) { + path.scope.push({ id: t.cloneNode(placeholder) }); + path.get("right").scope.rename(param.name, placeholder.name); + + return t.sequenceExpression([assign, calledExpression.body]); + } } else if (t.isIdentifier(calledExpression, { name: "eval" })) { const evalSequence = t.sequenceExpression([ t.numericLiteral(0), calledExpression, ]); - call.callee = evalSequence; - - path.scope.push({ id: t.cloneNode(placeholder) }); - - return t.sequenceExpression([assign, call]); - } - - if (optimizeArrow && !param) { - // Arrow function with 0 arguments - return t.sequenceExpression([pipelineLeft, calledExpression.body]); + (call as t.CallExpression).callee = evalSequence; } - path.scope.push({ id: t.cloneNode(placeholder) }); - if (param) { - path.get("right").scope.rename(param.name, placeholder.name); - - return t.sequenceExpression([assign, calledExpression.body]); - } - return t.sequenceExpression([assign, call]); }; diff --git a/packages/babel-plugin-proposal-pipeline-operator/src/fsharpVisitor.ts b/packages/babel-plugin-proposal-pipeline-operator/src/fsharpVisitor.ts index d285588f37df..5b1b5cac24cd 100644 --- a/packages/babel-plugin-proposal-pipeline-operator/src/fsharpVisitor.ts +++ b/packages/babel-plugin-proposal-pipeline-operator/src/fsharpVisitor.ts @@ -1,7 +1,8 @@ -import { types as t } from "@babel/core"; +import { types as t, type PluginObject } from "@babel/core"; +import type { NodePath } from "@babel/traverse"; import buildOptimizedSequenceExpression from "./buildOptimizedSequenceExpression"; -const fsharpVisitor = { +const fsharpVisitor: PluginObject["visitor"] = { BinaryExpression(path) { const { scope, node } = path; const { operator, left, right } = node; @@ -16,7 +17,7 @@ const fsharpVisitor = { const sequence = buildOptimizedSequenceExpression({ placeholder, call, - path, + path: path as NodePath" }>, }); path.replaceWith(sequence); }, diff --git a/packages/babel-plugin-proposal-pipeline-operator/src/hackVisitor.ts b/packages/babel-plugin-proposal-pipeline-operator/src/hackVisitor.ts index 36af34d537d5..c7e9d8d865c4 100644 --- a/packages/babel-plugin-proposal-pipeline-operator/src/hackVisitor.ts +++ b/packages/babel-plugin-proposal-pipeline-operator/src/hackVisitor.ts @@ -33,7 +33,7 @@ const topicReferenceVisitor: Visitor = { // with sequence expressions containing assignment expressions // with automatically generated variables, // from inside to outside, from left to right. -export default { +const visitor: Visitor = { BinaryExpression: { exit(path) { const { scope, node } = path; @@ -52,7 +52,7 @@ export default { return; } - const visitorState = { + const visitorState: State = { topicReferences: [], // pipeBodyPath might be a function, and it won't be visited by // topicReferenceVisitor because traverse() skips the top-level @@ -93,4 +93,6 @@ export default { ); }, }, -} as Visitor; +}; + +export default visitor; diff --git a/packages/babel-plugin-proposal-pipeline-operator/src/minimalVisitor.ts b/packages/babel-plugin-proposal-pipeline-operator/src/minimalVisitor.ts index 66d3bff98af0..a7a0e3e72464 100644 --- a/packages/babel-plugin-proposal-pipeline-operator/src/minimalVisitor.ts +++ b/packages/babel-plugin-proposal-pipeline-operator/src/minimalVisitor.ts @@ -1,7 +1,8 @@ -import { types as t } from "@babel/core"; +import { types as t, type PluginPass } from "@babel/core"; +import type { NodePath, Visitor } from "@babel/traverse"; import buildOptimizedSequenceExpression from "./buildOptimizedSequenceExpression"; -const minimalVisitor = { +const minimalVisitor: Visitor = { BinaryExpression(path) { const { scope, node } = path; const { operator, left, right } = node; @@ -14,7 +15,7 @@ const minimalVisitor = { buildOptimizedSequenceExpression({ placeholder, call, - path, + path: path as NodePath" }>, }), ); },