@babel/plugin-proposal-pipeline-operator
Version:
Transform pipeline operator into call expressions
140 lines (134 loc) • 4.27 kB
JavaScript
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