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 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' && node.argument?.type === 'Identifier') {
1085
- if (node.argument.name)
1086
- out.add(node.argument.name);
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' && part.expression?.type === 'Literal') {
1471
- return { known: true, value: part.expression.value };
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
- if (node?.type === 'Identifier' && node.name && setEnv.has(node.name))
197
- return setEnv.get(node.name);
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-shaker",
3
- "version": "0.15.1",
3
+ "version": "0.15.2",
4
4
  "description": "Tree shaking for Svelte components",
5
5
  "keywords": [
6
6
  "dead-code-elimination",