Skip to content

Commit

Permalink
fix: avoid URL illegal constructor error (#458)
Browse files Browse the repository at this point in the history
`nodeFileTrace` errors on `const URLParser = typeof window ===
'undefined' ? URL : 'b'` with `TypeError: Class constructor URL cannot
be invoked without 'new'`.

This is because this code:

```ts
async function ConditionalExpression() {
  return {
    test: 'foo',
    then: URL,
    else: 'bar',
  };
}
await ConditionalExpression();
```

calls `URL()` since `ConditionalExpression` is an async function
returning a
[thenable](https://masteringjs.io/tutorials/fundamentals/thenable).

Solution: rename all `.then` fields to `.ifTrue` since `then` is
reserved for thenables.

Closes #447
  • Loading branch information
onsclom authored Dec 19, 2024
1 parent 7fbb559 commit 5777c8b
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 59 deletions.
16 changes: 8 additions & 8 deletions src/analyze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -552,8 +552,8 @@ export default async function analyze(
else if (computed.wildcards.length >= 1)
emitWildcardRequire(computed.value);
} else {
if ('then' in computed && typeof computed.then === 'string')
add(computed.then);
if ('ifTrue' in computed && typeof computed.ifTrue === 'string')
add(computed.ifTrue);
if ('else' in computed && typeof computed.else === 'string')
add(computed.else);
}
Expand Down Expand Up @@ -585,8 +585,8 @@ export default async function analyze(
if (
('value' in curStaticValue &&
typeof curStaticValue.value !== 'symbol') ||
('then' in curStaticValue &&
typeof curStaticValue.then !== 'symbol' &&
('ifTrue' in curStaticValue &&
typeof curStaticValue.ifTrue !== 'symbol' &&
typeof curStaticValue.else !== 'symbol')
) {
staticChildValue = curStaticValue;
Expand Down Expand Up @@ -974,7 +974,7 @@ export default async function analyze(
}
if (
!('value' in computed) &&
isAbsolutePathOrUrl(computed.then) &&
isAbsolutePathOrUrl(computed.ifTrue) &&
isAbsolutePathOrUrl(computed.else)
) {
staticChildValue = computed;
Expand Down Expand Up @@ -1196,14 +1196,14 @@ export default async function analyze(
await emitAssetPath(resolved);
} catch (e) {}
} else if (
'then' in staticChildValue &&
'ifTrue' in staticChildValue &&
'else' in staticChildValue &&
isAbsolutePathOrUrl(staticChildValue.then) &&
isAbsolutePathOrUrl(staticChildValue.ifTrue) &&
isAbsolutePathOrUrl(staticChildValue.else)
) {
let resolvedThen;
try {
resolvedThen = resolveAbsolutePathOrUrl(staticChildValue.then);
resolvedThen = resolveAbsolutePathOrUrl(staticChildValue.ifTrue);
} catch (e) {}
let resolvedElse;
try {
Expand Down
100 changes: 50 additions & 50 deletions src/utils/static-eval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export async function evaluate(

// walk returns:
// 1. Single known value: { value: value }
// 2. Conditional value: { test, then, else }
// 2. Conditional value: { test, ifTrue, else }
// 3. Unknown value: undefined
function walk(node: Node) {
const visitor = visitors[node.type];
Expand Down Expand Up @@ -130,79 +130,79 @@ const visitors: Record<
if ('test' in l && 'value' in r) {
const v: any = r.value;
if (op === '==')
return { test: l.test, then: l.then == v, else: l.else == v };
return { test: l.test, ifTrue: l.ifTrue == v, else: l.else == v };
if (op === '===')
return { test: l.test, then: l.then === v, else: l.else === v };
return { test: l.test, ifTrue: l.ifTrue === v, else: l.else === v };
if (op === '!=')
return { test: l.test, then: l.then != v, else: l.else != v };
return { test: l.test, ifTrue: l.ifTrue != v, else: l.else != v };
if (op === '!==')
return { test: l.test, then: l.then !== v, else: l.else !== v };
return { test: l.test, ifTrue: l.ifTrue !== v, else: l.else !== v };
if (op === '+')
return { test: l.test, then: l.then + v, else: l.else + v };
return { test: l.test, ifTrue: l.ifTrue + v, else: l.else + v };
if (op === '-')
return { test: l.test, then: l.then - v, else: l.else - v };
return { test: l.test, ifTrue: l.ifTrue - v, else: l.else - v };
if (op === '*')
return { test: l.test, then: l.then * v, else: l.else * v };
return { test: l.test, ifTrue: l.ifTrue * v, else: l.else * v };
if (op === '/')
return { test: l.test, then: l.then / v, else: l.else / v };
return { test: l.test, ifTrue: l.ifTrue / v, else: l.else / v };
if (op === '%')
return { test: l.test, then: l.then % v, else: l.else % v };
return { test: l.test, ifTrue: l.ifTrue % v, else: l.else % v };
if (op === '<')
return { test: l.test, then: l.then < v, else: l.else < v };
return { test: l.test, ifTrue: l.ifTrue < v, else: l.else < v };
if (op === '<=')
return { test: l.test, then: l.then <= v, else: l.else <= v };
return { test: l.test, ifTrue: l.ifTrue <= v, else: l.else <= v };
if (op === '>')
return { test: l.test, then: l.then > v, else: l.else > v };
return { test: l.test, ifTrue: l.ifTrue > v, else: l.else > v };
if (op === '>=')
return { test: l.test, then: l.then >= v, else: l.else >= v };
return { test: l.test, ifTrue: l.ifTrue >= v, else: l.else >= v };
if (op === '|')
return { test: l.test, then: l.then | v, else: l.else | v };
return { test: l.test, ifTrue: l.ifTrue | v, else: l.else | v };
if (op === '&')
return { test: l.test, then: l.then & v, else: l.else & v };
return { test: l.test, ifTrue: l.ifTrue & v, else: l.else & v };
if (op === '^')
return { test: l.test, then: l.then ^ v, else: l.else ^ v };
return { test: l.test, ifTrue: l.ifTrue ^ v, else: l.else ^ v };
if (op === '&&')
return { test: l.test, then: l.then && v, else: l.else && v };
return { test: l.test, ifTrue: l.ifTrue && v, else: l.else && v };
if (op === '||')
return { test: l.test, then: l.then || v, else: l.else || v };
return { test: l.test, ifTrue: l.ifTrue || v, else: l.else || v };
} else if ('test' in r && 'value' in l) {
const v: any = l.value;
if (op === '==')
return { test: r.test, then: v == r.then, else: v == r.else };
return { test: r.test, ifTrue: v == r.ifTrue, else: v == r.else };
if (op === '===')
return { test: r.test, then: v === r.then, else: v === r.else };
return { test: r.test, ifTrue: v === r.ifTrue, else: v === r.else };
if (op === '!=')
return { test: r.test, then: v != r.then, else: v != r.else };
return { test: r.test, ifTrue: v != r.ifTrue, else: v != r.else };
if (op === '!==')
return { test: r.test, then: v !== r.then, else: v !== r.else };
return { test: r.test, ifTrue: v !== r.ifTrue, else: v !== r.else };
if (op === '+')
return { test: r.test, then: v + r.then, else: v + r.else };
return { test: r.test, ifTrue: v + r.ifTrue, else: v + r.else };
if (op === '-')
return { test: r.test, then: v - r.then, else: v - r.else };
return { test: r.test, ifTrue: v - r.ifTrue, else: v - r.else };
if (op === '*')
return { test: r.test, then: v * r.then, else: v * r.else };
return { test: r.test, ifTrue: v * r.ifTrue, else: v * r.else };
if (op === '/')
return { test: r.test, then: v / r.then, else: v / r.else };
return { test: r.test, ifTrue: v / r.ifTrue, else: v / r.else };
if (op === '%')
return { test: r.test, then: v % r.then, else: v % r.else };
return { test: r.test, ifTrue: v % r.ifTrue, else: v % r.else };
if (op === '<')
return { test: r.test, then: v < r.then, else: v < r.else };
return { test: r.test, ifTrue: v < r.ifTrue, else: v < r.else };
if (op === '<=')
return { test: r.test, then: v <= r.then, else: v <= r.else };
return { test: r.test, ifTrue: v <= r.ifTrue, else: v <= r.else };
if (op === '>')
return { test: r.test, then: v > r.then, else: v > r.else };
return { test: r.test, ifTrue: v > r.ifTrue, else: v > r.else };
if (op === '>=')
return { test: r.test, then: v >= r.then, else: v >= r.else };
return { test: r.test, ifTrue: v >= r.ifTrue, else: v >= r.else };
if (op === '|')
return { test: r.test, then: v | r.then, else: v | r.else };
return { test: r.test, ifTrue: v | r.ifTrue, else: v | r.else };
if (op === '&')
return { test: r.test, then: v & r.then, else: v & r.else };
return { test: r.test, ifTrue: v & r.ifTrue, else: v & r.else };
if (op === '^')
return { test: r.test, then: v ^ r.then, else: v ^ r.else };
return { test: r.test, ifTrue: v ^ r.ifTrue, else: v ^ r.else };
if (op === '&&')
return { test: r.test, then: v && r.then, else: l && r.else };
return { test: r.test, ifTrue: v && r.ifTrue, else: l && r.else };
if (op === '||')
return { test: r.test, then: v || r.then, else: l || r.else };
return { test: r.test, ifTrue: v || r.ifTrue, else: l || r.else };
} else if ('value' in l && 'value' in r) {
if (op === '==') return { value: l.value == r.value };
if (op === '===') return { value: l.value === r.value };
Expand Down Expand Up @@ -280,7 +280,7 @@ const visitors: Record<
if (predicate) return;
predicate = x.test;
argsElse = args.concat([]);
args.push(x.then);
args.push(x.ifTrue);
argsElse.push(x.else);
} else {
args.push(x.value);
Expand All @@ -304,7 +304,7 @@ const visitors: Record<
}
const resultElse = await fn.apply(ctx, argsElse);
if (result === UNKNOWN) return;
return { test: predicate, then: result, else: resultElse };
return { test: predicate, ifTrue: result, else: resultElse };
} catch (e) {
return;
}
Expand All @@ -327,7 +327,7 @@ const visitors: Record<

return {
test: node.test,
then: thenValue.value,
ifTrue: thenValue.value,
else: elseValue.value,
};
},
Expand Down Expand Up @@ -444,7 +444,7 @@ const visitors: Record<
try {
return {
test,
then: new URL(arg.then, parent.value),
ifTrue: new URL(arg.ifTrue, parent.value),
else: new URL(arg.else, parent.value),
};
} catch {
Expand All @@ -454,7 +454,7 @@ const visitors: Record<
try {
return {
test,
then: new URL(arg.then),
ifTrue: new URL(arg.ifTrue),
else: new URL(arg.else),
};
} catch {
Expand Down Expand Up @@ -512,7 +512,7 @@ const visitors: Record<
if ('value' in val) {
val.value += node.quasis[i].value.cooked;
} else {
val.then += node.quasis[i].value.cooked;
val.ifTrue += node.quasis[i].value.cooked;
val.else += node.quasis[i].value.cooked;
}
let exprValue = await walk(node.expressions[i]);
Expand All @@ -527,7 +527,7 @@ const visitors: Record<
val.wildcards = [...(val.wildcards || []), ...exprValue.wildcards];
} else {
if (exprValue.wildcards) return;
val.then += exprValue.value;
val.ifTrue += exprValue.value;
val.else += exprValue.value;
}
} else if ('value' in val) {
Expand All @@ -537,7 +537,7 @@ const visitors: Record<
}
val = {
test: exprValue.test,
then: val.value + exprValue.then,
ifTrue: val.value + exprValue.ifTrue,
else: val.value + exprValue.else,
};
} else {
Expand All @@ -548,7 +548,7 @@ const visitors: Record<
if ('value' in val) {
val.value += node.quasis[i].value.cooked;
} else {
val.then += node.quasis[i].value.cooked;
val.ifTrue += node.quasis[i].value.cooked;
val.else += node.quasis[i].value.cooked;
}
return val;
Expand All @@ -575,13 +575,13 @@ const visitors: Record<
if (node.operator === '!') return { value: !val.value };
} else if ('test' in val && 'wildcards' in val === false) {
if (node.operator === '+')
return { test: val.test, then: +val.then, else: +val.else };
return { test: val.test, ifTrue: +val.ifTrue, else: +val.else };
if (node.operator === '-')
return { test: val.test, then: -val.then, else: -val.else };
return { test: val.test, ifTrue: -val.ifTrue, else: -val.else };
if (node.operator === '~')
return { test: val.test, then: ~val.then, else: ~val.else };
return { test: val.test, ifTrue: ~val.ifTrue, else: ~val.else };
if (node.operator === '!')
return { test: val.test, then: !val.then, else: !val.else };
return { test: val.test, ifTrue: !val.ifTrue, else: !val.else };
}
return undefined;
},
Expand Down
2 changes: 1 addition & 1 deletion src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export interface StaticValue {

export interface ConditionalValue {
test: string;
then: any;
ifTrue: any;
else: any;
}

Expand Down
1 change: 1 addition & 0 deletions test/unit/url-error/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
const URLParser = typeof window === 'undefined' ? URL : 'b';
4 changes: 4 additions & 0 deletions test/unit/url-error/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[
"package.json",
"test/unit/url-error/input.js"
]

0 comments on commit 5777c8b

Please sign in to comment.