svelte-shaker 0.15.1 → 0.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analyze.js +29 -6
- package/dist/eval.d.ts +15 -0
- package/dist/eval.js +30 -2
- package/dist/svelte_shaker_engine_bg.wasm +0 -0
- package/package.json +1 -1
package/dist/analyze.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parseCached, parseModuleProgram, walk, } from './parse.js';
|
|
2
2
|
import { emptyPlan, } from './ir.js';
|
|
3
3
|
import { computeDeadSpans, inSpans } from './dead.js';
|
|
4
|
-
import { evaluate, setVar } from './eval.js';
|
|
4
|
+
import { evaluate, setVar, unwrapTsAssertions } from './eval.js';
|
|
5
5
|
const isSvelte = (source) => source.endsWith('.svelte');
|
|
6
6
|
/** Floor for the fixpoint iteration bound (see {@link fixpointIterationBound}). */
|
|
7
7
|
const MIN_FIXPOINT_ITERATIONS = 10;
|
|
@@ -856,6 +856,11 @@ function computeScriptConstEnv(ast, instance, moduleScript, propsDeclaration, wr
|
|
|
856
856
|
* rune call — is evaluated verbatim, so a non-value rune simply falls to unknown.
|
|
857
857
|
*/
|
|
858
858
|
function evalDeclaratorValue(init, env) {
|
|
859
|
+
// `const x = $state(0) as T` wraps the rune in a `TSAsExpression`; erase the
|
|
860
|
+
// (runtime-transparent) assertion first so the `$state`/`$state.raw` unwrap
|
|
861
|
+
// below and `evaluate` both see the real initializer, same as a parser that
|
|
862
|
+
// strips the assertion would.
|
|
863
|
+
init = unwrapTsAssertions(init) ?? undefined;
|
|
859
864
|
if (isStateRuneCall(init)) {
|
|
860
865
|
const arg = init?.arguments?.[0];
|
|
861
866
|
if (arg == null)
|
|
@@ -1081,9 +1086,13 @@ function collectTemplateBindings(ast, instance, propsDeclaration) {
|
|
|
1081
1086
|
function collectWrittenNames(node, out) {
|
|
1082
1087
|
if (node.type === 'AssignmentExpression')
|
|
1083
1088
|
addPatternNames(node.left, out);
|
|
1084
|
-
else if (node.type === 'UpdateExpression'
|
|
1085
|
-
|
|
1086
|
-
|
|
1089
|
+
else if (node.type === 'UpdateExpression') {
|
|
1090
|
+
// `x!++` keeps the `!` as a `TSNonNullExpression` around the target (only a
|
|
1091
|
+
// bare `x = …` LHS is normalized to the identifier), so read through it or the
|
|
1092
|
+
// write goes uncounted and the name is wrongly admitted as a constant.
|
|
1093
|
+
const arg = unwrapTsAssertions(node.argument);
|
|
1094
|
+
if (arg?.type === 'Identifier' && arg.name)
|
|
1095
|
+
out.add(arg.name);
|
|
1087
1096
|
}
|
|
1088
1097
|
}
|
|
1089
1098
|
/**
|
|
@@ -1091,6 +1100,11 @@ function collectWrittenNames(node, out) {
|
|
|
1091
1100
|
* Handles bare identifiers, object/array destructuring, defaults and rest.
|
|
1092
1101
|
*/
|
|
1093
1102
|
function addPatternNames(pattern, out) {
|
|
1103
|
+
// A non-null-asserted assignment target keeps its `TSNonNullExpression` wrapper
|
|
1104
|
+
// in every position but a bare `x = …` LHS — `x! += 1`, `[x!] = a`, `({k: x!} =
|
|
1105
|
+
// o)` — so peel it here (the one choke point every pattern position recurses
|
|
1106
|
+
// through) to count the write against the bare name.
|
|
1107
|
+
pattern = unwrapTsAssertions(pattern) ?? undefined;
|
|
1094
1108
|
if (!pattern)
|
|
1095
1109
|
return;
|
|
1096
1110
|
switch (pattern.type) {
|
|
@@ -1467,8 +1481,14 @@ function literalAttrValue(value) {
|
|
|
1467
1481
|
const part = parts[0];
|
|
1468
1482
|
if (part.type === 'Text')
|
|
1469
1483
|
return { known: true, value: (part.data ?? part.raw ?? '') };
|
|
1470
|
-
if (part.type === 'ExpressionTag'
|
|
1471
|
-
|
|
1484
|
+
if (part.type === 'ExpressionTag') {
|
|
1485
|
+
// Recognize `prop={'x' as const}` as the literal it wraps, so the write is
|
|
1486
|
+
// classified NON-dynamic identically to a parser that strips the assertion.
|
|
1487
|
+
// Both paths already fold the value (via the expr fallback), but the
|
|
1488
|
+
// `dynamic` flag itself must match — mono's `specializableShape` reads it.
|
|
1489
|
+
const expr = unwrapTsAssertions(part.expression);
|
|
1490
|
+
if (expr?.type === 'Literal')
|
|
1491
|
+
return { known: true, value: expr.value };
|
|
1472
1492
|
}
|
|
1473
1493
|
return { known: false };
|
|
1474
1494
|
}
|
|
@@ -1608,6 +1628,9 @@ function valueSetFor(decl, sites, ownerEnv) {
|
|
|
1608
1628
|
return { values, dynamic, top };
|
|
1609
1629
|
}
|
|
1610
1630
|
function literalDefault(expr) {
|
|
1631
|
+
// A default like `= 500 as const` reaches here as a `TSAsExpression`; the
|
|
1632
|
+
// assertion erases at runtime, so read through it to the bare default value.
|
|
1633
|
+
expr = unwrapTsAssertions(expr) ?? undefined;
|
|
1611
1634
|
if (!expr)
|
|
1612
1635
|
return { known: true, value: undefined }; // omitted default -> undefined
|
|
1613
1636
|
if (expr.type === 'Literal')
|
package/dist/eval.d.ts
CHANGED
|
@@ -6,6 +6,21 @@ export type EvalResult = {
|
|
|
6
6
|
} | {
|
|
7
7
|
known: false;
|
|
8
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* Strip TypeScript assertion wrappers — `x as T` (`TSAsExpression`), `x!`
|
|
11
|
+
* (`TSNonNullExpression`), `x satisfies T` (`TSSatisfiesExpression`) — down to
|
|
12
|
+
* the runtime expression they wrap, recursing so `('a' as const)!` becomes `'a'`.
|
|
13
|
+
*
|
|
14
|
+
* These are compile-time-only type operators: they are erased before any code
|
|
15
|
+
* runs and do not touch the operand's value, so interpreting the operand IS
|
|
16
|
+
* interpreting the whole expression (docs/ARCHITECTURE.md §2.2 — the value the
|
|
17
|
+
* expression abstracts to is exactly the operand's). The engine runs on the
|
|
18
|
+
* `lang="ts"` source with types preserved (§6.3), so svelte/compiler hands it
|
|
19
|
+
* these nodes verbatim; a parser that erases them (rsvelte today) yields the bare
|
|
20
|
+
* operand instead. Unwrapping here folds both ASTs identically — the
|
|
21
|
+
* parser-neutrality contract every value-interpreting entry point relies on.
|
|
22
|
+
*/
|
|
23
|
+
export declare function unwrapTsAssertions(node: AnyNode | null | undefined): AnyNode | null | undefined;
|
|
9
24
|
/**
|
|
10
25
|
* A deliberately tiny, total constant evaluator over an ESTree expression,
|
|
11
26
|
* given an environment of statically-known identifiers. It never throws and
|
package/dist/eval.js
CHANGED
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
const UNKNOWN = { known: false };
|
|
2
|
+
/**
|
|
3
|
+
* Strip TypeScript assertion wrappers — `x as T` (`TSAsExpression`), `x!`
|
|
4
|
+
* (`TSNonNullExpression`), `x satisfies T` (`TSSatisfiesExpression`) — down to
|
|
5
|
+
* the runtime expression they wrap, recursing so `('a' as const)!` becomes `'a'`.
|
|
6
|
+
*
|
|
7
|
+
* These are compile-time-only type operators: they are erased before any code
|
|
8
|
+
* runs and do not touch the operand's value, so interpreting the operand IS
|
|
9
|
+
* interpreting the whole expression (docs/ARCHITECTURE.md §2.2 — the value the
|
|
10
|
+
* expression abstracts to is exactly the operand's). The engine runs on the
|
|
11
|
+
* `lang="ts"` source with types preserved (§6.3), so svelte/compiler hands it
|
|
12
|
+
* these nodes verbatim; a parser that erases them (rsvelte today) yields the bare
|
|
13
|
+
* operand instead. Unwrapping here folds both ASTs identically — the
|
|
14
|
+
* parser-neutrality contract every value-interpreting entry point relies on.
|
|
15
|
+
*/
|
|
16
|
+
export function unwrapTsAssertions(node) {
|
|
17
|
+
let current = node;
|
|
18
|
+
while (current?.type === 'TSAsExpression' ||
|
|
19
|
+
current?.type === 'TSNonNullExpression' ||
|
|
20
|
+
current?.type === 'TSSatisfiesExpression') {
|
|
21
|
+
current = current.expression;
|
|
22
|
+
}
|
|
23
|
+
return current;
|
|
24
|
+
}
|
|
2
25
|
/**
|
|
3
26
|
* A deliberately tiny, total constant evaluator over an ESTree expression,
|
|
4
27
|
* given an environment of statically-known identifiers. It never throws and
|
|
@@ -9,6 +32,7 @@ const UNKNOWN = { known: false };
|
|
|
9
32
|
* unknown on non-distributive ops), just without the interprocedural lattice.
|
|
10
33
|
*/
|
|
11
34
|
export function evaluate(node, env) {
|
|
35
|
+
node = unwrapTsAssertions(node);
|
|
12
36
|
if (!node)
|
|
13
37
|
return UNKNOWN;
|
|
14
38
|
switch (node.type) {
|
|
@@ -126,6 +150,7 @@ export function evaluateWithSets(node, constEnv, setEnv) {
|
|
|
126
150
|
}
|
|
127
151
|
/** Evaluate a boolean condition to a Kleene truth over the value sets. */
|
|
128
152
|
function evalTri(node, constEnv, setEnv) {
|
|
153
|
+
node = unwrapTsAssertions(node);
|
|
129
154
|
if (!node)
|
|
130
155
|
return 'unknown';
|
|
131
156
|
switch (node.type) {
|
|
@@ -193,8 +218,11 @@ function equalityTri(left, right, constEnv, setEnv, loose) {
|
|
|
193
218
|
* set pass-through (analyze.ts) all decide it through this, so they stay in lockstep.
|
|
194
219
|
*/
|
|
195
220
|
export function setVar(node, setEnv) {
|
|
196
|
-
|
|
197
|
-
|
|
221
|
+
// A `variant as const` / `variant!` reference is still a bare read of `variant`
|
|
222
|
+
// (the assertion erases at runtime), so it narrows like the identifier it wraps.
|
|
223
|
+
const bare = unwrapTsAssertions(node);
|
|
224
|
+
if (bare?.type === 'Identifier' && bare.name && setEnv.has(bare.name))
|
|
225
|
+
return setEnv.get(bare.name);
|
|
198
226
|
return null;
|
|
199
227
|
}
|
|
200
228
|
/**
|
|
Binary file
|