svelte-shaker 0.15.0 → 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/README.md CHANGED
@@ -54,10 +54,10 @@ binary). `parser: 'svelte'` falls back to svelte/compiler if you ever need it
54
54
 
55
55
  ## Usage (Vite)
56
56
 
57
- Add the plugin **before** `svelte()`. It runs only in `vite build` — dev/HMR is
58
- a pass-through by design. Out of the box it runs the native **Rust (WASM)
59
- engine** and parses with **rsvelte**; both fall back cleanly (see
60
- [Options](#options)).
57
+ Add the plugin **before** `svelte()`. By default it runs only in `vite build` —
58
+ dev/HMR is a pass-through (opt into dev shaking with the `dev` option, see
59
+ [Options](#options)). Out of the box it runs the native **Rust (WASM) engine**
60
+ and parses with **rsvelte**; both fall back cleanly (see [Options](#options)).
61
61
 
62
62
  ```ts
63
63
  // vite.config.ts
@@ -103,6 +103,9 @@ shaker({
103
103
  // out (e.g. if you ever hit a bug in it).
104
104
  engine: 'auto', // 'auto' (default: Rust/WASM, else JS) | 'js' | 'rust'
105
105
  parser: 'rsvelte', // 'rsvelte' (default) | 'svelte' (fallback)
106
+
107
+ dev: false, // default off: dev is a pass-through. 'incremental' (re-parse only
108
+ // changed files) | 'coarse' (re-analyze everything) opts in; never monomorphizes
106
109
  });
107
110
  ```
108
111
 
@@ -150,6 +153,14 @@ options that do exist. A typo would otherwise be ignored — and a misspelled
150
153
  can't (a broken install), the plugin **throws** rather than silently falling
151
154
  back — so the same source always shakes the same on every machine. Reinstall
152
155
  dependencies, or set `parser: 'svelte'`.
156
+ - **`dev`** — whether to shake in `vite dev` too. **Off** by default: dev is a
157
+ pass-through, which is always correct and keeps HMR simple. Opt in with
158
+ `dev: 'incremental'` — re-parses only the changed files and re-runs the
159
+ whole-program fixpoint over a long-lived incremental engine (the intended mode)
160
+ — or `dev: 'coarse'`, which re-analyzes the whole program on every change (the
161
+ slow but trivially-correct safety valve). Monomorphization is **never** applied
162
+ in dev; only the always-on passes (unused-prop fold / constant fold / value-set
163
+ narrowing) run.
153
164
  - **`preserve`** — keep a component's **prop interface** exactly as written, because
154
165
  something the shake can't see passes props to it. What is preserved is the props,
155
166
  **not** the file's presence in the bundle: this is unrelated to Rollup/Vite's
@@ -236,8 +247,9 @@ The whole point is to **never change observable behavior**.
236
247
  scope.
237
248
  - **Needs `.svelte` source** — libraries shipping compiled JS pass through
238
249
  unshaken; distribute via `svelte-package`.
239
- - **Build only** — whole-program analysis is incompatible with dev/HMR locality,
240
- so dev is always a pass-through.
250
+ - **Build-first** — whole-program analysis is incompatible with dev/HMR locality,
251
+ so dev is a pass-through by default; opt into incremental dev shaking with the
252
+ `dev` option.
241
253
  - **`entries` must cover the whole app** — the crawl starts there, and every
242
254
  `.svelte` file it finds is a call-site source. A call site outside those roots
243
255
  is invisible, so narrowing `entries` does not shake less, it shakes _wrongly_.
package/dist/analyze.js CHANGED
@@ -1,7 +1,7 @@
1
- import { parseCached, parseSvelte, walk, } from './parse.js';
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')
@@ -1809,21 +1832,15 @@ function followLocalImport(body, localName) {
1809
1832
  return null;
1810
1833
  }
1811
1834
  /**
1812
- * Parse a `.js`/`.ts` module's top-level body by reusing the Svelte parser via a
1813
- * `<script module>` wrapper (the engine has no standalone JS parser). `lang="ts"`
1814
- * is required so TypeScript barrels parse — `export type { … }`, type-only
1815
- * specifiers and annotations are the norm for a design-system's `index.ts`, and a
1816
- * plain JS parse throws on them, leaving the whole library unfollowed. Returns
1817
- * `null` if it cannot be parsed — callers then leave the barrel unfollowed.
1835
+ * Parse a `.js`/`.ts` barrel's top-level body via {@link parseModuleProgram}
1836
+ * (the engine has no standalone JS parser; the shared helper wraps the source in
1837
+ * a `<script module lang="ts">` so TypeScript barrels — `export type { … }`,
1838
+ * type-only specifiers parse, and neutralizes any `</script>` in the text so a
1839
+ * valid module that merely mentions it still parses, issue #146). Returns `null`
1840
+ * if it cannot be parsed — callers then leave the barrel unfollowed.
1818
1841
  */
1819
1842
  function parseModuleBody(code, id) {
1820
- try {
1821
- const ast = parseSvelte(`<script module lang="ts">\n${code}\n</script>`, id);
1822
- return ast.module?.content?.body ?? null;
1823
- }
1824
- catch {
1825
- return null;
1826
- }
1843
+ return parseModuleProgram(code, id)?.body ?? null;
1827
1844
  }
1828
1845
  /**
1829
1846
  * Derive the child's {@link ReachableInputs} from its `$props()` shape (docs
@@ -7,7 +7,7 @@
7
7
  // ----------------------------------------------------------------------
8
8
  import * as fs from 'node:fs';
9
9
  import * as path from 'node:path';
10
- import { parseSvelte, walk } from './parse.js';
10
+ import { parseModuleProgram, walk } from './parse.js';
11
11
  import { compileDevOnly } from './dev-only.js';
12
12
  /**
13
13
  * The non-`.svelte` module extensions we scan for `.svelte` call sites (docs
@@ -58,25 +58,17 @@ function collectNonSvelteModules(dir, devOnly, out) {
58
58
  * Every module specifier a JS/TS module statically references: `import … from`,
59
59
  * `export … from` / `export *`, and a dynamic `import('…')` whose argument is a
60
60
  * STRING LITERAL. A computed dynamic import (`import(expr)`) is unknowable here —
61
- * the `preserve` option (docs §4.2) is the sound fallback for it. Parsed via the
62
- * Svelte parser's `<script module lang="ts">` wrapper (the same TS-capable parse
63
- * the engine's barrel-following uses). Returns `null` when the module does NOT
64
- * parse the wrapper handles TS but not JSX, and rejects some exotic syntax
65
- * (`.jsx`/`.tsx` bodies, bleeding-edge TS), and a parse failure hides any call site
66
- * inside, so the caller must surface it (a mounted component would go un-escaped);
67
- * `preserve` is the fix for that file.
61
+ * the `preserve` option (docs §4.2) is the sound fallback for it. Parsed via
62
+ * {@link parseModuleProgram} (the same TS-capable parse the engine's
63
+ * barrel-following uses). Returns `null` when the module does NOT parse — a JSX
64
+ * body or exotic/bleeding-edge TS the wrapper rejects because a parse failure
65
+ * hides any call site inside, so the caller must surface it (a mounted component
66
+ * would go un-escaped); `preserve` is the fix for that file.
68
67
  */
69
68
  function moduleImportSpecifiers(code, id) {
70
- let module;
71
- try {
72
- module = parseSvelte(`<script module lang="ts">\n${code}\n</script>`, id).module;
73
- }
74
- catch {
75
- return null; // unparseable — the caller reports it (soundness hole, `preserve` fixes it)
76
- }
77
- const program = module?.content;
78
- if (!program)
79
- return []; // parsed, no module body — nothing to follow (not a failure)
69
+ const program = parseModuleProgram(code, id);
70
+ if (program === null)
71
+ return null; // unparseable — the caller reports it (`preserve` fixes it)
80
72
  const specs = [];
81
73
  const literalSource = (node) => node?.type === 'Literal' && typeof node.value === 'string' ? node.value : undefined;
82
74
  walk(program, null, {
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
  /**
package/dist/parse.d.ts CHANGED
@@ -81,6 +81,39 @@ export interface Root extends AnyNode {
81
81
  fragment: AnyNode;
82
82
  }
83
83
  export declare function parseSvelte(code: string, filename: string): Root;
84
+ /**
85
+ * Parse a plain `.js`/`.ts` module (NOT a `.svelte` component) and return its
86
+ * top-level ESTree Program, or `null` if it does not parse. The engine has no
87
+ * standalone JS parser, so we reuse the Svelte parser via a `<script module
88
+ * lang="ts">` wrapper: `lang="ts"` is what lets a TypeScript barrel — `export
89
+ * type { … }`, type-only specifiers, annotations, the norm for a design-system
90
+ * `index.ts` — parse where a plain-JS parse would throw.
91
+ *
92
+ * The Svelte parser ends a `<script>` at the first `</script…>` it finds in the
93
+ * RAW text (`read_until(/<\/script\s*>/)`), so a valid module whose text merely
94
+ * MENTIONS `</script>` — in a comment, string, regex or template literal, as an
95
+ * HTML sanitizer or a markdown pipeline routinely does — would close the wrapper
96
+ * early and fail to parse (issue #146). We break every `</script` in the body
97
+ * before wrapping by inserting a space after the `<`. In valid JS/TS `</script`
98
+ * only ever occurs inside a comment or a string/regex/template literal (it is
99
+ * never part of executable syntax), so this alters only inert text — with ONE
100
+ * exception a caller does read: a module SPECIFIER that itself contains
101
+ * `</script` (`import x from './a</script>b.js'`). Rewriting it would silently
102
+ * change which file it resolves to, turning a parse failure into a partial one
103
+ * (a missed escape / an unfollowed export). Rather than claim such a specifier
104
+ * cannot exist, we detect a rewritten one after parsing and fail the whole
105
+ * module (return `null`), the same loud degrade a parse failure gives — the
106
+ * specifier is absurd (`<`/`>` are invalid in real paths and package names), so
107
+ * this only trades one conservative outcome for another. The inserted spaces
108
+ * shift byte offsets, but no caller reads spans off this AST — only specifier
109
+ * names and string values.
110
+ *
111
+ * Returns `null` on a genuine parse failure (a JSX body, exotic/bleeding-edge
112
+ * TS) or a specifier the neutralization would corrupt: a call site hidden inside
113
+ * then goes unfollowed, which each caller handles conservatively (escape scan
114
+ * reports the file as unscannable; barrel-following leaves the barrel unfollowed).
115
+ */
116
+ export declare function parseModuleProgram(code: string, filename: string): AnyNode | null;
84
117
  /**
85
118
  * Content-keyed parse cache: a hit returns the IDENTICAL AST for unchanged
86
119
  * source, so the dev engine re-parses only the files that actually changed
package/dist/parse.js CHANGED
@@ -3,6 +3,93 @@ import { walk as zfWalk } from 'zimmerframe';
3
3
  export function parseSvelte(code, filename) {
4
4
  return parse(code, { modern: true, filename });
5
5
  }
6
+ /** Matches every `</script` in module text (any case), the sequence that would
7
+ * otherwise close the {@link parseModuleProgram} wrapper early. */
8
+ const CLOSING_SCRIPT = /<\/script/gi;
9
+ /** What `CLOSING_SCRIPT` is rewritten to: a space after the `<` breaks the
10
+ * `</script\s*>` the Svelte parser scans for, while staying a recognizable
11
+ * marker so a corrupted specifier can be detected afterwards. */
12
+ const NEUTRALIZED_SCRIPT = '< /script';
13
+ /**
14
+ * Parse a plain `.js`/`.ts` module (NOT a `.svelte` component) and return its
15
+ * top-level ESTree Program, or `null` if it does not parse. The engine has no
16
+ * standalone JS parser, so we reuse the Svelte parser via a `<script module
17
+ * lang="ts">` wrapper: `lang="ts"` is what lets a TypeScript barrel — `export
18
+ * type { … }`, type-only specifiers, annotations, the norm for a design-system
19
+ * `index.ts` — parse where a plain-JS parse would throw.
20
+ *
21
+ * The Svelte parser ends a `<script>` at the first `</script…>` it finds in the
22
+ * RAW text (`read_until(/<\/script\s*>/)`), so a valid module whose text merely
23
+ * MENTIONS `</script>` — in a comment, string, regex or template literal, as an
24
+ * HTML sanitizer or a markdown pipeline routinely does — would close the wrapper
25
+ * early and fail to parse (issue #146). We break every `</script` in the body
26
+ * before wrapping by inserting a space after the `<`. In valid JS/TS `</script`
27
+ * only ever occurs inside a comment or a string/regex/template literal (it is
28
+ * never part of executable syntax), so this alters only inert text — with ONE
29
+ * exception a caller does read: a module SPECIFIER that itself contains
30
+ * `</script` (`import x from './a</script>b.js'`). Rewriting it would silently
31
+ * change which file it resolves to, turning a parse failure into a partial one
32
+ * (a missed escape / an unfollowed export). Rather than claim such a specifier
33
+ * cannot exist, we detect a rewritten one after parsing and fail the whole
34
+ * module (return `null`), the same loud degrade a parse failure gives — the
35
+ * specifier is absurd (`<`/`>` are invalid in real paths and package names), so
36
+ * this only trades one conservative outcome for another. The inserted spaces
37
+ * shift byte offsets, but no caller reads spans off this AST — only specifier
38
+ * names and string values.
39
+ *
40
+ * Returns `null` on a genuine parse failure (a JSX body, exotic/bleeding-edge
41
+ * TS) or a specifier the neutralization would corrupt: a call site hidden inside
42
+ * then goes unfollowed, which each caller handles conservatively (escape scan
43
+ * reports the file as unscannable; barrel-following leaves the barrel unfollowed).
44
+ */
45
+ export function parseModuleProgram(code, filename) {
46
+ const wrapped = `<script module lang="ts">\n${code.replace(CLOSING_SCRIPT, NEUTRALIZED_SCRIPT)}\n</script>`;
47
+ let program;
48
+ try {
49
+ program = parseSvelte(wrapped, filename).module?.content ?? null;
50
+ }
51
+ catch {
52
+ return null; // unparseable — the caller decides how to degrade (see above)
53
+ }
54
+ // A specifier that carried `</script` was rewritten by the neutralization and
55
+ // no longer names the real module; degrade loudly rather than resolve a lie.
56
+ if (program && hasNeutralizedSpecifier(program))
57
+ return null;
58
+ return program;
59
+ }
60
+ /** True when any static import/export/dynamic-`import()` specifier string in
61
+ * `program` contains the {@link NEUTRALIZED_SCRIPT} marker — i.e. the source
62
+ * originally held `</script` and was rewritten by {@link parseModuleProgram}, so
63
+ * it can no longer be trusted to resolve. */
64
+ function hasNeutralizedSpecifier(program) {
65
+ let corrupted = false;
66
+ const check = (source) => {
67
+ if (source?.type === 'Literal' &&
68
+ typeof source.value === 'string' &&
69
+ source.value.includes(NEUTRALIZED_SCRIPT)) {
70
+ corrupted = true;
71
+ }
72
+ };
73
+ walk(program, null, {
74
+ ImportDeclaration(node, { next }) {
75
+ check(node.source);
76
+ next();
77
+ },
78
+ ExportNamedDeclaration(node, { next }) {
79
+ check(node.source);
80
+ next();
81
+ },
82
+ ExportAllDeclaration(node, { next }) {
83
+ check(node.source);
84
+ next();
85
+ },
86
+ ImportExpression(node, { next }) {
87
+ check(node.source);
88
+ next();
89
+ },
90
+ });
91
+ return corrupted;
92
+ }
6
93
  export function parseCached(filename, code, cache, parse = parseSvelte) {
7
94
  if (!cache)
8
95
  return parse(code, filename);
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-shaker",
3
- "version": "0.15.0",
3
+ "version": "0.15.2",
4
4
  "description": "Tree shaking for Svelte components",
5
5
  "keywords": [
6
6
  "dead-code-elimination",