-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
/
util.ts
57 lines (54 loc) 路 1.86 KB
/
util.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
import { skipTransparentExprWrappers } from "@babel/helper-skip-transparent-expression-wrappers";
import type { NodePath } from "@babel/traverse";
import { types as t } from "@babel/core";
// https://crbug.com/v8/11558
// check if there is a spread element followed by another argument.
// (...[], 0) or (...[], ...[])
function matchAffectedArguments(argumentNodes: t.CallExpression["arguments"]) {
const spreadIndex = argumentNodes.findIndex(node => t.isSpreadElement(node));
return spreadIndex >= 0 && spreadIndex !== argumentNodes.length - 1;
}
/**
* Check whether the optional chain is affected by https://crbug.com/v8/11558.
* This routine MUST not manipulate NodePath
*
* @export
* @param {(NodePath<t.OptionalMemberExpression | t.OptionalCallExpression>)} path
* @returns {boolean}
*/
export function shouldTransform(
path: NodePath<t.OptionalMemberExpression | t.OptionalCallExpression>,
): boolean {
let optionalPath: NodePath<t.Expression> = path;
const chains = [];
for (;;) {
if (optionalPath.isOptionalMemberExpression()) {
optionalPath = skipTransparentExprWrappers(optionalPath.get("object"));
chains.push(optionalPath.node);
} else if (optionalPath.isOptionalCallExpression()) {
optionalPath = skipTransparentExprWrappers(optionalPath.get("callee"));
chains.push(optionalPath.node);
} else {
break;
}
}
for (let i = 0; i < chains.length; i++) {
const node = chains[i];
if (
t.isOptionalCallExpression(node) &&
matchAffectedArguments(node.arguments)
) {
// f?.(...[], 0)
if (node.optional) {
return true;
}
// o?.m(...[], 0)
// when node.optional is false, chains[i + 1] is always well defined
const callee = chains[i + 1];
if (t.isOptionalMemberExpression(callee, { optional: true })) {
return true;
}
}
}
return false;
}