forked from babel/babel
-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
hackVisitor.ts
88 lines (79 loc) · 2.71 KB
/
hackVisitor.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import { types as t } from "@babel/core";
import type { NodePath, Visitor } from "@babel/traverse";
const topicReferenceVisitor: Visitor<{
topicReferences: NodePath<t.TopicReference>[];
sideEffectsBeforeFirstTopicReference: boolean;
}> = {
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;
}
},
};
// This visitor traverses `BinaryExpression`
// and replaces any that use `|>`
// with sequence expressions containing assignment expressions
// with automatically generated variables,
// from inside to outside, from left to right.
export default {
BinaryExpression: {
exit(path) {
const { scope, node } = path;
if (node.operator !== "|>") {
// The path node is a binary expression,
// but it is not a pipe expression.
return;
}
const pipeBodyPath = path.get("right");
if (pipeBodyPath.node.type === "TopicReference") {
// If the pipe body is itself a lone topic reference,
// then replace the whole expression with its left operand.
path.replaceWith(node.left);
return;
}
const visitorState = {
topicReferences: [],
// pipeBodyPath might be a function, and it won't be visited by
// topicReferenceVisitor because traverse() skips the top-level
// node. We must handle that case here manually.
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 });
// Replace topic references with the topic variable.
visitorState.topicReferences.forEach(path =>
path.replaceWith(t.cloneNode(topicVariable)),
);
// Replace the pipe expression itself with an assignment expression.
path.replaceWith(
t.sequenceExpression([
t.assignmentExpression("=", t.cloneNode(topicVariable), node.left),
node.right,
]),
);
},
},
};