UNPKG

@babel/plugin-proposal-pipeline-operator

Version:

Transform pipeline operator into call expressions

140 lines (134 loc) 4.27 kB
import { declare } from '@babel/helper-plugin-utils'; import syntaxPipelineOperator from '@babel/plugin-syntax-pipeline-operator'; import { types } from '@babel/core'; const topicReferenceVisitor = { exit(path, state) { if (path.isTopicReference()) { state.topicReferences.push(path); } else { if (state.topicReferences.length === 0 && !state.sideEffectsBeforeFirstTopicReference && !path.isPure()) { state.sideEffectsBeforeFirstTopicReference = true; } } }, "ClassBody|Function"(_, state) { if (state.topicReferences.length === 0) { state.sideEffectsBeforeFirstTopicReference = true; } } }; const visitor = { BinaryExpression: { exit(path) { const { scope, node } = path; if (node.operator !== "|>") { return; } const pipeBodyPath = path.get("right"); if (pipeBodyPath.node.type === "TopicReference") { path.replaceWith(node.left); return; } const visitorState = { topicReferences: [], sideEffectsBeforeFirstTopicReference: pipeBodyPath.isFunction() }; pipeBodyPath.traverse(topicReferenceVisitor, visitorState); if (visitorState.topicReferences.length === 1 && (!visitorState.sideEffectsBeforeFirstTopicReference || path.scope.isPure(node.left, true))) { visitorState.topicReferences[0].replaceWith(node.left); path.replaceWith(node.right); return; } const topicVariable = scope.generateUidIdentifierBasedOnNode(node); scope.push({ id: topicVariable }); visitorState.topicReferences.forEach(path => path.replaceWith(types.cloneNode(topicVariable))); path.replaceWith(types.sequenceExpression([types.assignmentExpression("=", types.cloneNode(topicVariable), node.left), node.right])); } } }; function isConciseArrowExpression(node) { return types.isArrowFunctionExpression(node) && types.isExpression(node.body) && !node.async; } const buildOptimizedSequenceExpression = ({ call, path, placeholder }) => { const { callee: calledExpression } = call; const pipelineLeft = path.node.left; const assign = types.assignmentExpression("=", types.cloneNode(placeholder), pipelineLeft); const expressionIsArrow = isConciseArrowExpression(calledExpression); if (expressionIsArrow) { let param; let optimizeArrow = true; const { params } = calledExpression; if (params.length === 1 && types.isIdentifier(params[0])) { param = params[0]; } else if (params.length > 0) { optimizeArrow = false; } if (optimizeArrow && !param) { return types.sequenceExpression([pipelineLeft, calledExpression.body]); } else if (param) { path.scope.push({ id: types.cloneNode(placeholder) }); path.get("right").scope.rename(param.name, placeholder.name); return types.sequenceExpression([assign, calledExpression.body]); } } else if (types.isIdentifier(calledExpression, { name: "eval" })) { const evalSequence = types.sequenceExpression([types.numericLiteral(0), calledExpression]); call.callee = evalSequence; } path.scope.push({ id: types.cloneNode(placeholder) }); return types.sequenceExpression([assign, call]); }; const fsharpVisitor = { BinaryExpression(path) { const { scope, node } = path; const { operator, left, right } = node; if (operator !== "|>") return; const placeholder = scope.generateUidIdentifierBasedOnNode(left); const call = right.type === "AwaitExpression" ? types.awaitExpression(types.cloneNode(placeholder)) : types.callExpression(right, [types.cloneNode(placeholder)]); const sequence = buildOptimizedSequenceExpression({ placeholder, call, path: path }); path.replaceWith(sequence); } }; const visitorsPerProposal = { hack: visitor, fsharp: fsharpVisitor }; const index = declare((api, options) => { api.assertVersion("^7.0.0-0 || ^8.0.0"); return { name: "proposal-pipeline-operator", inherits: syntaxPipelineOperator, visitor: visitorsPerProposal[options.proposal] }; }); export { index as default }; //# sourceMappingURL=index.js.map