sveld 0.32.8 → 0.34.0

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
@@ -123,8 +123,11 @@ export default class Button extends SvelteComponentTyped<
123
123
  - [Vite](#vite)
124
124
  - [Node.js](#nodejs)
125
125
  - [CLI](#cli)
126
+ - [CI: API-drift checks (`--check`)](#ci-api-drift-checks---check)
127
+ - [Config File](#config-file)
126
128
  - [Publishing to NPM](#publishing-to-npm)
127
129
  - [Available Options](#available-options)
130
+ - [Documenting Entry Exports](#documenting-entry-exports)
128
131
  - [JSON Output](#json-output)
129
132
  - [API Reference](#api-reference)
130
133
  - [@type](#type)
@@ -135,6 +138,7 @@ export default class Button extends SvelteComponentTyped<
135
138
  - [Extra JSDoc tags before `@slot`](#extra-jsdoc-tags-before-slot)
136
139
  - [Svelte 5 Snippet Compatibility](#svelte-5-snippet-compatibility)
137
140
  - [@event](#event)
141
+ - [@deprecated](#deprecated)
138
142
  - [Context API](#context-api)
139
143
  - [@restProps](#restprops)
140
144
  - [@extendProps](#extendprops)
@@ -169,6 +173,87 @@ When both TypeScript syntax and JSDoc are present, `sveld` resolves prop types i
169
173
 
170
174
  `sveld` stays AST-only. It copies imported and local type text into generated `.d.ts` output but does not run project-wide semantic resolution with the TypeScript compiler. Opaque imported whole-object `$props()` types can therefore stay in declarations without being fully expanded into JSON metadata.
171
175
 
176
+ #### Opt-in semantic resolution (`resolveTypes`)
177
+
178
+ Imported whole-object `$props()` types stay opaque in JSON by default (`"props": []`). Turn on `resolveTypes` when a docs site or prop table needs the individual fields.
179
+
180
+ ```ts
181
+ await sveld({ json: true, resolveTypes: true });
182
+ ```
183
+
184
+ ```svelte
185
+ <script lang="ts">
186
+ import type { Props } from "./types";
187
+
188
+ let props: Props = $props();
189
+ </script>
190
+ ```
191
+
192
+ Without `resolveTypes`, JSON lists no props. With it, each field shows up with `"typeSource": "typescript"`:
193
+
194
+ ```jsonc
195
+ {
196
+ "props": [
197
+ { "name": "disabled", "type": "boolean", "isRequired": false, "typeSource": "typescript" },
198
+ { "name": "href", "type": "string", "isRequired": true, "typeSource": "typescript" },
199
+ { "name": "variant", "type": "\"primary\" | \"secondary\"", "isRequired": true, "typeSource": "typescript" }
200
+ ]
201
+ }
202
+ ```
203
+
204
+ **Performance.** Off by default. This is the only path that loads TypeScript. It needs `typescript` and a `tsconfig.json`, runs slower than the AST-only pipeline, and gets slower as your types grow. Use it only when you need expanded JSON. `.d.ts` output is unchanged.
205
+
206
+ #### Persistent parse cache (`cache`)
207
+
208
+ By default, every component is re-parsed on every run. With `cache`, parsed output is written to disk and reused when the source file has not changed. That applies across runs, including CI on a fresh checkout.
209
+
210
+ ```ts
211
+ await sveld({ json: true, cache: true });
212
+ ```
213
+
214
+ `cache: true` writes to `node_modules/.cache/sveld/parse-cache.json`. Pass a string to use a different location, e.g. `cache: ".cache/sveld.json"`. Also available as `--cache` / `--cache=<path>` on the CLI.
215
+
216
+ If a component [`@extendProps`](#extendprops) / [`@extends`](#extendprops) another file, it is re-parsed when that dependency changes, same as in [`watch`](#available-options) mode. Bumping the `sveld` or Svelte version clears the cache.
217
+
218
+ #### Compile-checked `@example` blocks (`checkExamples`)
219
+
220
+ `@example` blocks are just text. Rename a prop and the sample code can sit there broken for months. Set `checkExamples: true` to run plain TS/JS `@example` blocks through the TypeScript program. Broken examples show up as `example-compile-error` diagnostics.
221
+
222
+ ```ts
223
+ await sveld({ json: true, checkExamples: true });
224
+ ```
225
+
226
+ ```svelte
227
+ <script>
228
+ /**
229
+ * Formats a value.
230
+ * @param {string} value
231
+ * @returns {string}
232
+ * @example
233
+ * ```js
234
+ * formatValue("ok");
235
+ * ```
236
+ */
237
+ export function formatValue(value) {
238
+ return value;
239
+ }
240
+ </script>
241
+ ```
242
+
243
+ If `formatValue` is later renamed and the example is never updated, `checkExamples` reports it:
244
+
245
+ ```
246
+ @example blocks that failed to compile (1):
247
+ ./Component.svelte
248
+ - Line 1: Cannot find name 'formatValue'.
249
+ ```
250
+
251
+ Plain TS/JS only. Examples fenced as `svelte` or `html`, or bare markup like `<Button />`, are skipped. Checking those needs `svelte2tsx` or similar, and sveld stays AST-only.
252
+
253
+ The check is narrow on purpose. It catches renamed or removed symbols and wrong argument counts. It is not full type checking and never pulls in types sveld cannot see.
254
+
255
+ Needs `typescript` and a `tsconfig.json`, same as `resolveTypes`. Use `--strict` (or the `strict` option) to fail CI when an example breaks.
256
+
172
257
  ## Usage
173
258
 
174
259
  ### Installation
@@ -244,6 +329,32 @@ Generate documentation in JSON and/or Markdown formats using the following flags
244
329
  npx sveld --json --markdown
245
330
  ```
246
331
 
332
+ ### CI: API-drift checks (`--check`)
333
+
334
+ `--check` diffs the parsed component API against a committed `COMPONENT_API.json` snapshot, assigns a semver bump to each change, and exits `1` when it finds a breaking change.
335
+
336
+ 1. Generate and commit the snapshot once: `npx sveld --json`, then commit `COMPONENT_API.json`.
337
+ 2. Add `npx sveld --check` to CI:
338
+
339
+ ```sh
340
+ npx sveld --check
341
+ ```
342
+
343
+ ```
344
+ sveld --check: 2 API changes detected against "COMPONENT_API.json".
345
+ Suggested semver bump: major.
346
+
347
+ Button
348
+ [BREAKING] prop "target" added (required)
349
+ [BREAKING] prop "href" removed
350
+ ```
351
+
352
+ Removed props, events, or slots, and props that become required, are breaking (`major`). New optional props, new events, and widened union types are additive (`minor`). Changes to generics, `@restProps`, `@extends`, or context shapes are not classified further. If any of those changed, `--check` calls it breaking.
353
+
354
+ `--check` does not write the snapshot. Run `sveld --json` (or `sveld --json --check`) and commit the file when you want to update it. If there is no snapshot yet, `--check` prints a notice and exits `0`.
355
+
356
+ Use `--check=<path>` to diff against a snapshot at a custom location (defaults to `jsonOptions.outFile`, or `COMPONENT_API.json`).
357
+
247
358
  ### Node.js
248
359
 
249
360
  You can also call `sveld` from Node.js. The package is **ESM-only**; `require("sveld")` does not work. Use `import` or dynamic `import()`.
@@ -291,6 +402,31 @@ sveld({
291
402
  });
292
403
  ```
293
404
 
405
+ ### Config File
406
+
407
+ Put a `sveld.config.js`, `sveld.config.mjs`, or `sveld.config.ts` at your project root to set defaults for the CLI and the programmatic `sveld()` API.
408
+
409
+ Import `defineConfig` from `sveld` for typed options. Config files must use ESM syntax (`export default`).
410
+
411
+ ```js
412
+ // sveld.config.js
413
+ import { defineConfig } from "sveld";
414
+
415
+ export default defineConfig({
416
+ glob: true,
417
+ json: true,
418
+ markdown: true,
419
+ });
420
+ ```
421
+
422
+ Later sources win when options overlap:
423
+
424
+ CLI flags > config file > `package.json#svelte` inference / defaults
425
+
426
+ With the config above, `npx sveld --json` keeps `glob` and `markdown` from the file. A CLI flag overrides the same key in the config.
427
+
428
+ A bad config (syntax error, throws at load time, or no default-export object) fails with an error that names the file.
429
+
294
430
  ### Publishing to NPM
295
431
 
296
432
  TypeScript definitions land in the `types` folder by default. Include that folder in `package.json` when you publish to npm.
@@ -314,12 +450,18 @@ TypeScript definitions land in the `types` folder by default. Include that folde
314
450
 
315
451
  - **`entry`** (string, optional): Specify the entry point to uncompiled Svelte source. If not provided, sveld will use the `"svelte"` field from `package.json`.
316
452
  - **`glob`** (boolean, optional): Enable glob mode to analyze all `*.svelte` files.
453
+ - **`documentExports`** (boolean, optional): Include consts, functions, and types from the entry barrel in JSON (`exports`) and Markdown ("Exports"). Off by default. See [Documenting Entry Exports](#documenting-entry-exports).
317
454
  - **`types`** (boolean, optional, default: `true`): Generate TypeScript definitions.
318
455
  - **`typesOptions`** (object, optional): Options for TypeScript definition generation, including `outDir`, `preamble`, and `printWidth`.
319
456
  - **`json`** (boolean, optional): Generate component documentation in JSON format.
320
457
  - **`jsonOptions`** (object, optional): Options for JSON output.
321
458
  - **`markdown`** (boolean, optional): Generate component documentation in Markdown format.
322
459
  - **`markdownOptions`** (object, optional): Options for Markdown output.
460
+ - **`watch`** (boolean, optional, default: `false`): Regenerate output incrementally when `.svelte` source changes during `vite dev` / `vite build --watch`. Only the changed component and the components that depend on it via [`@extendProps`](#extendprops) / `@extends` are re-parsed, rather than rebuilding every component. Without this option, the plugin only runs during `vite build`.
461
+ - **`failFast`** (boolean, optional, default: `false`): Abort the entire run when a single component fails to parse. By default, parse failures are collected as diagnostics (and reported to `stderr`) so the remaining components still emit their output. Also available as the `--fail-fast` CLI flag.
462
+ - **`resolveTypes`** (boolean, optional, default: `false`): Load the TypeScript program to expand opaque imported whole-object `$props()` types into JSON. See [Opt-in semantic resolution](#opt-in-semantic-resolution-resolvetypes).
463
+ - **`cache`** (boolean | string, optional, default: `false`): Write parsed component output to disk and skip re-parsing unchanged files on later runs. `true` uses `node_modules/.cache/sveld/parse-cache.json`; a string sets a custom path. Also available as `--cache` / `--cache=<path>`. See [Persistent parse cache](#persistent-parse-cache-cache).
464
+ - **`checkExamples`** (boolean, optional, default: `false`): Run plain TS/JS `@example` blocks through the TypeScript program. Broken ones get an `example-compile-error` diagnostic. See [Compile-checked `@example` blocks](#compile-checked-example-blocks-checkexamples).
323
465
 
324
466
  By default, only TypeScript definitions are generated.
325
467
 
@@ -332,6 +474,32 @@ sveld({
332
474
  })
333
475
  ```
334
476
 
477
+ ## Documenting Entry Exports
478
+
479
+ Most entry barrels re-export more than `.svelte` components. Set `documentExports: true` to add consts, functions, and types to the JSON and Markdown output.
480
+
481
+ ```diff
482
+ sveld({
483
+ json: true,
484
+ markdown: true,
485
+ + documentExports: true,
486
+ })
487
+ ```
488
+
489
+ Example entry file:
490
+
491
+ ```ts
492
+ // src/index.ts
493
+ export { default as Button } from "./Button.svelte";
494
+ export { VERSION } from "./constants";
495
+ export { clamp } from "./utils";
496
+ export type { Theme } from "./types";
497
+ ```
498
+
499
+ From that barrel, `sveld` documents `VERSION`, `clamp`, and `Theme`. `Button` still goes through the component path. Type text is copied from source, not resolved with `tsc`, same as the rest of the tool.
500
+
501
+ JSON adds `exports` and `totalExports`. Markdown adds an "Exports" section. Each item has `name`, `kind`, type text, optional JSDoc `description`, and `source`.
502
+
335
503
  ## JSON Output
336
504
 
337
505
  When `json: true` is enabled, `sveld` emits a `COMPONENT_API.json` file with schema and generator metadata plus the parsed
@@ -349,6 +517,19 @@ interface ComponentApiJson {
349
517
  };
350
518
  total: number;
351
519
  components: ComponentDocApi[];
520
+ // Present only when `documentExports` is enabled.
521
+ totalExports?: number;
522
+ exports?: EntryExport[];
523
+ }
524
+
525
+ interface EntryExport {
526
+ name: string;
527
+ kind: "const" | "let" | "var" | "function" | "class" | "type" | "interface" | "enum";
528
+ type?: string;
529
+ value?: string;
530
+ description?: string;
531
+ source?: string;
532
+ isTypeOnly: boolean;
352
533
  }
353
534
 
354
535
  interface SourceRange {
@@ -1361,9 +1542,11 @@ Omit the `slot-name` to type the default slot.
1361
1542
 
1362
1543
  #### Extra JSDoc tags before `@slot`
1363
1544
 
1364
- Tags such as `@example`, `@deprecated`, `@see`, or `@since` that appear after the prose description and before the `@slot` / `@snippet` line are copied into generated `.d.ts` files. The emitted JSDoc above each slot's snippet prop (and the traditional `SlotDefs` shape) lists the description, then those tags in source order. The same entries appear in JSON as `tags: [{ "name", "body" }, ...]`.
1545
+ Tags such as `@example`, `@see`, or `@since` that appear after the prose description and before the `@slot` / `@snippet` line are copied into generated `.d.ts` files. The emitted JSDoc above each slot's snippet prop (and the traditional `SlotDefs` shape) lists the description, then those tags in source order. The same entries appear in JSON as `tags: [{ "name", "body" }, ...]`.
1365
1546
 
1366
- Put `@slot` / `@snippet` last in the block (description, optional extra tags, slot tag). Tags after `@slot` / `@snippet` in the same comment are not tied to that slot. Unknown tag names pass through as-is. Markdown docs do not render slot descriptions or these tags yet; use TypeScript hover or JSON.
1547
+ `@deprecated` is handled separately from passthrough slot tags. It fills the slot's `deprecated` JSON field, adds a Markdown badge, and emits `@deprecated` in the `.d.ts`. See [@deprecated](#deprecated).
1548
+
1549
+ Put `@slot` / `@snippet` last in the block (description, optional extra tags, slot tag). Tags after `@slot` / `@snippet` in the same comment are not tied to that slot. Unknown tag names pass through as-is. Markdown docs render the description and these tags in the slot's **Description** column (newlines and tag boundaries become `<br />`), alongside TypeScript hover and JSON.
1367
1550
 
1368
1551
  **Example (default slot with `@example` and `@deprecated`):**
1369
1552
 
@@ -1803,6 +1986,53 @@ export default class Component extends SvelteComponentTyped<
1803
1986
 
1804
1987
  Any free-text prose after the tags is attached to the event description, not to a property doc.
1805
1988
 
1989
+ ### `@deprecated`
1990
+
1991
+ Add `@deprecated` to a prop, event, slot, or exported accessor. An optional message after the tag can explain why or name a replacement.
1992
+
1993
+ ```svelte
1994
+ <script>
1995
+ /**
1996
+ * The visible label.
1997
+ * @deprecated Use the `text` prop instead.
1998
+ */
1999
+ export let label = "";
2000
+
2001
+ /**
2002
+ * Programmatically focus the field.
2003
+ * @deprecated Focus the underlying element directly.
2004
+ */
2005
+ export function focus() {}
2006
+
2007
+ /**
2008
+ * @event {{ value: string }} change
2009
+ * @deprecated Listen for the native `input` event instead.
2010
+ */
2011
+
2012
+ /**
2013
+ * Badge content rendered next to the label.
2014
+ * @deprecated Render the badge inline instead.
2015
+ * @slot {{ count: number }} badge
2016
+ */
2017
+ </script>
2018
+ ```
2019
+
2020
+ For slots, put `@deprecated` before the `@slot` / `@snippet` line, alongside the description and any other [extra tags](#extra-jsdoc-tags-before-slot). For events, put it after the `@event` line.
2021
+
2022
+ Generated `.d.ts` files include an `@deprecated` JSDoc line so editors strike the symbol through. JSON adds a `deprecated` field (the message string, or `true` when the tag has no message). Markdown strikes through the name and adds a **Deprecated** badge with the message when present.
2023
+
2024
+ ```ts
2025
+ /**
2026
+ * The visible label.
2027
+ * @deprecated Use the `text` prop instead.
2028
+ */
2029
+ label?: string;
2030
+ ```
2031
+
2032
+ ```json
2033
+ { "name": "label", "deprecated": "Use the `text` prop instead." }
2034
+ ```
2035
+
1806
2036
  ### Context API
1807
2037
 
1808
2038
  `sveld` generates TypeScript definitions for Svelte's `setContext`/`getContext` by extracting types from JSDoc on context values.
@@ -1812,10 +2042,27 @@ Any free-text prose after the tags is attached to the event description, not to
1812
2042
  When you call `setContext` in a component, `sveld`:
1813
2043
 
1814
2044
  1. Detects the `setContext` call
1815
- 2. Extracts the context key (must be a string literal)
2045
+ 2. Resolves the context key (see [Supported context keys](#supported-context-keys))
1816
2046
  3. Finds JSDoc `@type` annotations on the variables being passed
1817
2047
  4. Generates a TypeScript type export for the context
1818
2048
 
2049
+ #### Supported context keys
2050
+
2051
+ The key becomes the `{PascalCase}Context` type name. `sveld` can resolve:
2052
+
2053
+ | Key form | Example | Generated type |
2054
+ | --- | --- | --- |
2055
+ | String literal | `setContext("simple-modal", …)` | `SimpleModalContext` |
2056
+ | Static template literal | `` setContext(`simple-modal`, …) `` | `SimpleModalContext` |
2057
+ | `const`-bound string | `const KEY = "simple-modal";`<br>`setContext(KEY, …)` | `SimpleModalContext` |
2058
+ | `Symbol()` / `Symbol.for()` | `setContext(Symbol("tabs"), …)` | `TabsContext` |
2059
+
2060
+ `const` identifiers are followed up to 5 levels deep (`const A = "x"; const B = A;`). Only `const` bindings count. `let`, `var`, and props are skipped because they can change at runtime.
2061
+
2062
+ Symbol keys take their name from the description: `Symbol("tabs")` and `Symbol.for("tabs")` both become `TabsContext`. For `const ModalKey = Symbol()` with no description, the binding name wins: `ModalKeyContext`.
2063
+
2064
+ Anything else (dynamic identifiers, template interpolation, other function calls) logs a warning. No context type is generated.
2065
+
1819
2066
  #### Example
1820
2067
 
1821
2068
  **Modal.svelte**
@@ -1,3 +1,13 @@
1
+ import type { SveldDiagnostic } from "./diagnostics";
2
+ /** Structured JSDoc tag (e.g. `{ name: "since", body: "1.2.0" }`). */
3
+ interface JsDocPassthroughTag {
4
+ name: string;
5
+ body: string;
6
+ }
7
+ /**
8
+ * From `@deprecated` JSDoc: a message string, or `true` when the tag has no message.
9
+ */
10
+ export type DeprecatedValue = string | true;
1
11
  export interface SourcePosition {
2
12
  /** 1-based source line number */
3
13
  line: number;
@@ -18,6 +28,15 @@ export declare const PARSED_COMPONENT_TYPE_SCRIPT_METADATA: unique symbol;
18
28
  export declare function getParsedComponentTypeScriptMetadata(component: {
19
29
  [PARSED_COMPONENT_TYPE_SCRIPT_METADATA]?: ParsedComponentTypeScriptMetadata;
20
30
  }): ParsedComponentTypeScriptMetadata | undefined;
31
+ /** One prop returned by the TypeScript checker during `resolveTypes`. */
32
+ export interface ResolvedComponentProp {
33
+ name: string;
34
+ type: string;
35
+ isRequired: boolean;
36
+ description?: string;
37
+ }
38
+ /** Append checker-resolved props. Names the AST walker already found are left alone. */
39
+ export declare function applyResolvedProps(component: ParsedComponent, resolved: ResolvedComponentProp[]): void;
21
40
  type SyntaxMode = "legacy" | "runes";
22
41
  type ScriptLanguage = "js" | "ts";
23
42
  type ComponentPropTypeSource = "typescript" | "jsdoc" | "default" | "inferred" | "unknown";
@@ -69,7 +88,7 @@ interface ComponentPropParam {
69
88
  * its type, default value, description, and metadata about whether it's
70
89
  * required, reactive, or a function.
71
90
  */
72
- interface ComponentProp {
91
+ export interface ComponentProp {
73
92
  /** The prop name as declared in the component */
74
93
  name: string;
75
94
  /** The declaration kind: "let" (required), "const" (optional with default), or "function" */
@@ -104,6 +123,10 @@ interface ComponentProp {
104
123
  binding?: ComponentPropBinding;
105
124
  /** True when the prop is explicitly declared with Svelte 5 `$bindable()` */
106
125
  bindable?: true;
126
+ /** From `@deprecated` JSDoc. */
127
+ deprecated?: DeprecatedValue;
128
+ /** Structured `@since` / `@example` tags, in source order. */
129
+ tags?: JsDocPassthroughTag[];
107
130
  /** Source range for the prop declaration, when available */
108
131
  source?: SourceRange;
109
132
  }
@@ -113,7 +136,7 @@ interface ComponentProp {
113
136
  * Represents a slot that can be used to pass content into the component.
114
137
  * Includes information about slot props, fallback content, and descriptions.
115
138
  */
116
- interface ComponentSlot {
139
+ export interface ComponentSlot {
117
140
  /** The slot name (null or undefined for default slot) */
118
141
  name?: string | null;
119
142
  /** Whether this is the default slot */
@@ -124,14 +147,13 @@ interface ComponentSlot {
124
147
  slot_props?: string;
125
148
  /** Description extracted from JSDoc `@slot` or `@snippet` tags */
126
149
  description?: string;
150
+ /** From `@deprecated` JSDoc. */
151
+ deprecated?: DeprecatedValue;
127
152
  /**
128
153
  * JSDoc tags that appeared after the prose description and before `@slot` / `@snippet`
129
- * (e.g. `@example`, `@deprecated`), in source order.
154
+ * (e.g. `@example`), in source order.
130
155
  */
131
- tags?: Array<{
132
- name: string;
133
- body: string;
134
- }>;
156
+ tags?: JsDocPassthroughTag[];
135
157
  /** Source range for the slot/snippet declaration or documentation tag, when available */
136
158
  source?: SourceRange;
137
159
  }
@@ -150,6 +172,10 @@ interface DispatchedEvent {
150
172
  detail?: string;
151
173
  /** Description extracted from JSDoc `@event` tags */
152
174
  description?: string;
175
+ /** From `@deprecated` JSDoc. */
176
+ deprecated?: DeprecatedValue;
177
+ /** Structured `@since` / `@example` tags, in source order. */
178
+ tags?: JsDocPassthroughTag[];
153
179
  /** Source range for the dispatched event call or documentation tag, when available */
154
180
  source?: SourceRange;
155
181
  }
@@ -181,8 +207,12 @@ export interface SerializedForwardedEvent {
181
207
  element: string;
182
208
  /** Description extracted from JSDoc `@event` tags */
183
209
  description?: string;
210
+ /** From `@deprecated` JSDoc. */
211
+ deprecated?: DeprecatedValue;
184
212
  /** The detail type if explicitly specified in `@event` tag */
185
213
  detail?: string;
214
+ /** Structured `@since` / `@example` tags, in source order. */
215
+ tags?: JsDocPassthroughTag[];
186
216
  /** Source range for the forwarded event declaration or documentation tag, when available */
187
217
  source?: SourceRange;
188
218
  }
@@ -335,6 +365,11 @@ export interface ParsedComponent {
335
365
  componentCommentSource?: SourceRange;
336
366
  /** Contexts created with `setContext` in the component */
337
367
  contexts?: ComponentContext[];
368
+ /**
369
+ * Type guesses from this parse (unknown props, `any` contexts, orphan `@event` tags).
370
+ * Set on every {@link ComponentParser.parseSvelteComponent} call.
371
+ */
372
+ diagnostics?: SveldDiagnostic[];
338
373
  /** Internal writer-only TypeScript metadata. Not serialized to JSON. */
339
374
  [PARSED_COMPONENT_TYPE_SCRIPT_METADATA]?: ParsedComponentTypeScriptMetadata;
340
375
  }
@@ -381,6 +416,8 @@ export default class ComponentParser {
381
416
  private readonly typedefs;
382
417
  /** Component generic type parameters (null if no generics) */
383
418
  private generics;
419
+ /** @template tags in a @slot/@snippet block (no @extends), held until finalization. */
420
+ private deferredSlotBlockGenerics;
384
421
  /** Map of prop bindings (e.g., `bind:value`) keyed by prop name */
385
422
  private readonly bindings;
386
423
  /** Map of component contexts (created with `setContext`) keyed by context name */
@@ -411,6 +448,12 @@ export default class ComponentParser {
411
448
  private scopeDeclarations;
412
449
  /** Active lexical scopes while walking the component AST */
413
450
  private readonly activeScopes;
451
+ /** Diagnostics for the parse in progress. */
452
+ private readonly diagnosticRecords;
453
+ /** File path tagged onto each diagnostic. */
454
+ private componentFilePath;
455
+ /** `@event` names from JSDoc, checked against dispatches at end of parse. */
456
+ private readonly jsDocEventNames;
414
457
  /** Cached array of source code lines split by newline for efficient line-based operations */
415
458
  private sourceLinesCache?;
416
459
  /** Cached 0-based source offsets for the start of each line */
@@ -420,6 +463,7 @@ export default class ComponentParser {
420
463
  private static getStaticAttributeValue;
421
464
  private static resolveScriptLanguage;
422
465
  private static assignValue;
466
+ private recordDiagnostic;
423
467
  private resolvePublicPropName;
424
468
  private trackPropLocalName;
425
469
  private getPropByLocalOrPublic;
@@ -478,7 +522,8 @@ export default class ComponentParser {
478
522
  * { tag: "param", name: "value", type: "string" }
479
523
  * ],
480
524
  * returns: { tag: "returns", type: "void" },
481
- * additional: [{ tag: "since", name: "1.0.0" }],
525
+ * additional: [{ tag: "see", name: "OtherType" }],
526
+ * passthrough: [{ tag: "since", name: "1.0.0" }],
482
527
  * description: "Main description text"
483
528
  * }
484
529
  * ```
@@ -602,6 +647,16 @@ export default class ComponentParser {
602
647
  * Returns the init node if found, or undefined.
603
648
  */
604
649
  private resolveLocalVarInitializer;
650
+ /**
651
+ * Look up the initializer for a local `const` by name.
652
+ *
653
+ * {@link resolveLocalVarInitializer} also walks `let`/`var`. This method does not.
654
+ * Props and mutable bindings can change at runtime, so they cannot be context keys.
655
+ *
656
+ * @param name - The variable name to look up
657
+ * @returns The initializer node for a matching `const` binding, or undefined
658
+ */
659
+ private resolveConstInitializer;
605
660
  /**
606
661
  * Look up JSDoc on a local variable declaration by name.
607
662
  */
@@ -960,12 +1015,32 @@ export default class ComponentParser {
960
1015
  * ```
961
1016
  */
962
1017
  private parseContextValue;
1018
+ /**
1019
+ * Resolve a `setContext` key to the string used in `{PascalCase}Context`.
1020
+ *
1021
+ * Accepts string literals, static template literals, `const`-bound identifiers
1022
+ * (up to 5 indirection levels, same as `@default`), and `Symbol()` /
1023
+ * `Symbol.for()`. Description-less symbols fall back to the binding name.
1024
+ *
1025
+ * @param keyArg - The first argument of the `setContext` call
1026
+ * @param depth - Current identifier-resolution depth (internal recursion guard)
1027
+ * @returns The resolved key string, or `null` when the key cannot be resolved at parse time
1028
+ */
1029
+ private resolveContextKey;
1030
+ /**
1031
+ * Extracts the description string from a `Symbol(...)` or `Symbol.for(...)` call
1032
+ * used as a context key.
1033
+ *
1034
+ * @param node - A CallExpression or NewExpression AST node
1035
+ * @returns The symbol description (`""` when the symbol has no static description),
1036
+ * or `null` when the node is not a `Symbol()` / `Symbol.for()` call
1037
+ */
1038
+ private resolveSymbolKeyDescription;
963
1039
  /**
964
1040
  * Parses a `setContext` call expression to extract context information.
965
1041
  *
966
- * Extracts the context key from the first argument (must be a string literal
967
- * or simple template literal) and the context value from the second argument.
968
- * Only processes static keys - dynamic keys are skipped with a warning.
1042
+ * Reads the context key from the first argument and the value from the second.
1043
+ * Unresolved keys log a warning and are skipped.
969
1044
  *
970
1045
  * @param node - The AST node (should be a CallExpression)
971
1046
  * @param _parent - The parent node (unused)
@@ -975,7 +1050,10 @@ export default class ComponentParser {
975
1050
  * // Parses: setContext('modal', { open, close })
976
1051
  * // Extracts: key = "modal", value = { open, close }
977
1052
  *
978
- * // Skips: setContext(dynamicKey, value) // key is not a literal
1053
+ * // Parses: const KEY = "modal"; setContext(KEY, value) // resolves to "modal"
1054
+ * // Parses: setContext(Symbol("modal"), value) // resolves to "modal"
1055
+ *
1056
+ * // Diagnostic: setContext(dynamicKey, value) // key cannot be statically resolved
979
1057
  * ```
980
1058
  */
981
1059
  private parseSetContextCall;
@@ -993,6 +1071,7 @@ export default class ComponentParser {
993
1071
  * parser.parseSvelteComponent(source2, diagnostics2); // Fresh parse
994
1072
  * ```
995
1073
  */
1074
+ private accumulateGeneric;
996
1075
  cleanup(): void;
997
1076
  /**
998
1077
  * Pre-compiled regex for matching script blocks in Svelte components.