Skip to content

Commit c1d157f

Browse files
committed
improve resolveSource
1 parent 91e1930 commit c1d157f

File tree

5 files changed

+463
-9
lines changed

5 files changed

+463
-9
lines changed

packages/babel-helper-define-polyfill-provider/src/utils.ts

Lines changed: 112 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,21 +103,129 @@ export function resolveSource(obj: NodePath): {
103103
}
104104

105105
const path = resolve(obj);
106-
switch (path?.type) {
106+
if (!path) return { id: null, placement: null };
107+
108+
switch (path.type) {
109+
case "NullLiteral":
110+
return { id: null, placement: null };
107111
case "RegExpLiteral":
108112
return { id: "RegExp", placement: "prototype" };
109-
case "FunctionExpression":
110-
return { id: "Function", placement: "prototype" };
111113
case "StringLiteral":
114+
case "TemplateLiteral":
112115
return { id: "String", placement: "prototype" };
113-
case "NumberLiteral":
116+
case "NumericLiteral":
114117
return { id: "Number", placement: "prototype" };
115118
case "BooleanLiteral":
116119
return { id: "Boolean", placement: "prototype" };
120+
case "BigIntLiteral":
121+
return { id: "BigInt", placement: "prototype" };
117122
case "ObjectExpression":
118123
return { id: "Object", placement: "prototype" };
119124
case "ArrayExpression":
120125
return { id: "Array", placement: "prototype" };
126+
case "FunctionExpression":
127+
case "ArrowFunctionExpression":
128+
case "ClassExpression":
129+
return { id: "Function", placement: "prototype" };
130+
// new Constructor() -> resolve the constructor name
131+
case "NewExpression": {
132+
const calleeId = resolveId(
133+
(path as NodePath<t.NewExpression>).get("callee"),
134+
);
135+
if (calleeId) return { id: calleeId, placement: "prototype" };
136+
return { id: null, placement: null };
137+
}
138+
// Unary expressions -> result type depends on operator
139+
case "UnaryExpression": {
140+
const { operator } = path.node as t.UnaryExpression;
141+
if (operator === "typeof")
142+
return { id: "String", placement: "prototype" };
143+
if (operator === "!" || operator === "delete")
144+
return { id: "Boolean", placement: "prototype" };
145+
if (operator === "+" || operator === "-" || operator === "~")
146+
return { id: "Number", placement: "prototype" };
147+
return { id: null, placement: null };
148+
}
149+
// ++i, i++ always produce a number
150+
case "UpdateExpression":
151+
return { id: "Number", placement: "prototype" };
152+
// Binary expressions -> result type depends on operator
153+
case "BinaryExpression": {
154+
const { operator } = path.node as t.BinaryExpression;
155+
if (
156+
operator === "==" ||
157+
operator === "!=" ||
158+
operator === "===" ||
159+
operator === "!==" ||
160+
operator === "<" ||
161+
operator === ">" ||
162+
operator === "<=" ||
163+
operator === ">=" ||
164+
operator === "instanceof" ||
165+
operator === "in"
166+
) {
167+
return { id: "Boolean", placement: "prototype" };
168+
}
169+
if (
170+
operator === "-" ||
171+
operator === "*" ||
172+
operator === "/" ||
173+
operator === "%" ||
174+
operator === "**" ||
175+
operator === "&" ||
176+
operator === "|" ||
177+
operator === "^" ||
178+
operator === "<<" ||
179+
operator === ">>" ||
180+
operator === ">>>"
181+
) {
182+
return { id: "Number", placement: "prototype" };
183+
}
184+
// + is ambiguous (string or number), so we can't determine the type
185+
return { id: null, placement: null };
186+
}
187+
// (a, b, c) -> the result is the last expression
188+
case "SequenceExpression": {
189+
const expressions = (path as NodePath<t.SequenceExpression>).get(
190+
"expressions",
191+
);
192+
return resolveSource(expressions[expressions.length - 1]);
193+
}
194+
// a = b -> the result is the right side
195+
case "AssignmentExpression": {
196+
if ((path.node as t.AssignmentExpression).operator === "=") {
197+
return resolveSource(
198+
(path as NodePath<t.AssignmentExpression>).get("right"),
199+
);
200+
}
201+
return { id: null, placement: null };
202+
}
203+
// a ? b : c -> if both branches resolve to the same type, use it
204+
case "ConditionalExpression": {
205+
const consequent = resolveSource(
206+
(path as NodePath<t.ConditionalExpression>).get("consequent"),
207+
);
208+
const alternate = resolveSource(
209+
(path as NodePath<t.ConditionalExpression>).get("alternate"),
210+
);
211+
if (consequent.id && consequent.id === alternate.id) {
212+
return consequent;
213+
}
214+
return { id: null, placement: null };
215+
}
216+
// (expr) -> unwrap parenthesized expressions
217+
case "ParenthesizedExpression":
218+
return resolveSource(
219+
(path as NodePath<t.ParenthesizedExpression>).get("expression"),
220+
);
221+
// TypeScript / Flow type wrappers -> unwrap to the inner expression
222+
case "TSAsExpression":
223+
case "TSSatisfiesExpression":
224+
case "TSNonNullExpression":
225+
case "TSInstantiationExpression":
226+
case "TSTypeAssertion":
227+
case "TypeCastExpression":
228+
return resolveSource(path.get("expression") as NodePath);
121229
}
122230

123231
return { id: null, placement: null };

0 commit comments

Comments
 (0)