sveld 0.34.1 → 0.35.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
@@ -3,24 +3,21 @@
3
3
  [![NPM][npm]][npm-url]
4
4
  ![npm downloads to date](https://img.shields.io/npm/dt/sveld?color=262626&style=for-the-badge)
5
5
 
6
- `sveld` generates TypeScript definitions and component documentation (Markdown/JSON) for Svelte components. It statically analyzes props, events, slots, and the rest. Add types with [JSDoc](https://jsdoc.app/) when inference is not enough.
6
+ `sveld` generates TypeScript definitions and component documentation (Markdown/JSON) for Svelte components. It statically analyzes props, events, slots, module exports, context, and `$$restProps`. Add types with [JSDoc](https://jsdoc.app/) when inference is not enough.
7
7
 
8
8
  The goal is to get third-party Svelte libraries working with the Svelte Language Server and TypeScript with minimal effort from the author. Generated `.d.ts` files give you autocomplete in VS Code and other IDEs.
9
9
 
10
10
  [Carbon Components Svelte](https://github.com/carbon-design-system/carbon-components-svelte) uses this library to auto-generate component types and API metadata.
11
11
 
12
- `sveld` uses the Svelte 5 compiler to parse `.svelte` files. That single parse path powers docgen and TypeScript output for Svelte 3, Svelte 4, Svelte 5 without runes (`export let`, `<slot>`, `$$restProps`, …), and Svelte 5 Runes (`$props()`, `$bindable()`, `{@render ...}`, callback props such as `onclick`, …).
12
+ `sveld` uses the Svelte 5 compiler to parse `.svelte` files. That single parse path powers docgen and TypeScript output for Svelte 3, Svelte 4, and Svelte 5 without runes (`export let`, `<slot>`, `$$restProps`, …). It also covers Svelte 5 Runes (`$props()`, `$bindable()`, `{@render ...}`, callback props such as `onclick`, …).
13
13
 
14
14
  For `lang="ts"` components, `sveld` keeps source-level prop type annotations when it can, instead of forcing JSDoc. That covers legacy `export let` props, typed `$props()` destructuring, typed whole-object `$props()` captures, local `interface`/`type` declarations, and imported type references in emitted `.d.ts` files.
15
15
 
16
- | Syntax mode | Supported |
17
- | :------------------- | :-------: |
18
- | Svelte 3 | ✓ |
19
- | Svelte 4 | ✓ |
20
- | Svelte 5 (non-Runes) | ✓ |
21
- | Svelte 5 Runes | ✓ |
16
+ By default, generated `.d.ts` files extend `SvelteComponentTyped` from `svelte`, so TypeScript and the Svelte Language Server work whether consumers use Svelte 3, Svelte 4, or Svelte 5. Set `typesOptions.format: "component"` to instead emit the Svelte 5 `Component` type; see [`typesOptions.format`](#typesoptionsformat).
22
17
 
23
- Generated `.d.ts` files extend `SvelteComponentTyped` from `svelte`, so TypeScript and the Svelte Language Server work whether consumers use Svelte 3, Svelte 4, or Svelte 5.
18
+ ## When to use sveld
19
+
20
+ SvelteKit's library tooling (`svelte-package`) and `svelte2tsx` already emit `.d.ts` files for SvelteKit-based projects, so start there if that's your setup. `sveld` targets JS-first Svelte libraries: components authored in plain JavaScript with JSDoc rather than `lang="ts"`, where you still want full editor types without adopting TypeScript. It also covers cases `svelte-package` doesn't: generating JSON and Markdown component docs from the same source as the types, checking for API drift between releases (`--check`), and deriving richer types from JSDoc tags (`@typedef`, `@callback`, `@slot`, context types) than plain type inference produces.
24
21
 
25
22
  ---
26
23
 
@@ -72,6 +69,8 @@ export default class Button extends SvelteComponentTyped<
72
69
  > {}
73
70
  ```
74
71
 
72
+ `sveld` adds the `[key: \`data-${string}\`]: unknown;` index signature whenever `$$restProps` is spread onto an element, so callers can pass arbitrary `data-*` attributes.
73
+
75
74
  Inference only gets you so far. Use [JSDoc](https://jsdoc.app/) to document prop, event, and slot types when you need more precision.
76
75
 
77
76
  ```js
@@ -87,6 +86,7 @@ export let primary = false;
87
86
  With JSDoc, the output looks like this:
88
87
 
89
88
  ```ts
89
+ import { SvelteComponentTyped } from "svelte";
90
90
  import type { SvelteHTMLElements } from "svelte/elements";
91
91
 
92
92
  type $RestProps = SvelteHTMLElements["button"];
@@ -117,19 +117,31 @@ export default class Button extends SvelteComponentTyped<
117
117
 
118
118
  ## Table of Contents
119
119
 
120
+ - [When to use sveld](#when-to-use-sveld)
120
121
  - [Approach](#approach)
122
+ - [Features](#features)
123
+ - [`.d.ts` output format (`typesOptions.format`)](#dts-output-format-typesoptionsformat)
124
+ - [Opt-in semantic resolution (`resolveTypes`)](#opt-in-semantic-resolution-resolvetypes)
125
+ - [Persistent parse cache (`cache`)](#persistent-parse-cache-cache)
126
+ - [Compile-checked `@example` blocks (`checkExamples`)](#compile-checked-example-blocks-checkexamples)
127
+ - [Type inference diagnostics](#type-inference-diagnostics)
128
+ - [Requirements](#requirements)
121
129
  - [Usage](#usage)
122
130
  - [Installation](#installation)
123
131
  - [Vite](#vite)
124
- - [Node.js](#nodejs)
125
132
  - [CLI](#cli)
126
133
  - [CI: API-drift checks (`--check`)](#ci-api-drift-checks---check)
134
+ - [Node.js](#nodejs)
127
135
  - [Config File](#config-file)
128
136
  - [Publishing to NPM](#publishing-to-npm)
129
137
  - [Available Options](#available-options)
130
138
  - [Documenting Entry Exports](#documenting-entry-exports)
131
139
  - [JSON Output](#json-output)
140
+ - [Custom Elements Manifest](#custom-elements-manifest)
141
+ - [Consuming the manifest](#consuming-the-manifest)
132
142
  - [API Reference](#api-reference)
143
+ - [reactive](#reactive)
144
+ - [binding](#binding)
133
145
  - [@type](#type)
134
146
  - [@default](#default)
135
147
  - [@typedef](#typedef)
@@ -146,6 +158,7 @@ export default class Button extends SvelteComponentTyped<
146
158
  - [@generics](#generics)
147
159
  - [@component comments](#component-comments)
148
160
  - [Accessor Props](#accessor-props)
161
+ - [Troubleshooting](#troubleshooting)
149
162
  - [Contributing](#contributing)
150
163
  - [License](#license)
151
164
 
@@ -173,7 +186,73 @@ When both TypeScript syntax and JSDoc are present, `sveld` resolves prop types i
173
186
 
174
187
  `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.
175
188
 
176
- #### Opt-in semantic resolution (`resolveTypes`)
189
+ ## Features
190
+
191
+ ### `.d.ts` output format (`typesOptions.format`)
192
+
193
+ `typesOptions.format` controls the shape of generated `.d.ts` files: `"class"` (the default) or `"component"`.
194
+
195
+ `"class"` extends `SvelteComponentTyped`, deprecated in Svelte 5 and plausibly removed in Svelte 6:
196
+
197
+ ```ts
198
+ import { SvelteComponentTyped } from "svelte";
199
+
200
+ export type ButtonProps = { label?: string };
201
+
202
+ export default class Button extends SvelteComponentTyped<
203
+ ButtonProps,
204
+ { click: WindowEventMap["click"] },
205
+ { default: Record<string, never> }
206
+ > {}
207
+ ```
208
+
209
+ `"component"` emits the Svelte 5 `Component` type instead:
210
+
211
+ ```ts
212
+ import type { Component } from "svelte";
213
+
214
+ export type ButtonProps = { label?: string; onclick?: (event: WindowEventMap["click"]) => void };
215
+ export type ButtonExports = Record<string, never>;
216
+
217
+ declare const Button: Component<ButtonProps, ButtonExports, "">;
218
+ export default Button;
219
+ ```
220
+
221
+ Publish `"component"` if you target Svelte 5+ consumers. `"class"` remains compatible with Svelte 3, 4, and 5.
222
+
223
+ ```ts
224
+ await sveld({ types: true, typesOptions: { format: "component" } });
225
+ ```
226
+
227
+ Also available as `--types-format=component` on the CLI.
228
+
229
+ `"component"`'s three type parameters:
230
+
231
+ - **Props**: the same `$Props`/`<Name>Props` type as `"class"`. Runes components already declare callback props (e.g. `onclick`) as regular props. Legacy (non-runes) components additionally get `on<name>?: (event: Type) => void` callback props for every dispatched and forwarded event, so Svelte 5 consumers can attach handlers as props instead of `on:event`.
232
+ - **Exports**: the component's accessor props (exported `function`/`const` members), the same members that render as class members under `"class"`.
233
+ - **Bindings**: a union of string literals for props declared with `$bindable(...)` (runes) or marked `@bindable writable` ([see `binding`](#binding)) (legacy) — e.g. `"value"`, or `"value" | "open"` for more than one. `""` when the component declares none, matching Svelte's own convention for "no bindings."
234
+
235
+ **Generic components.** A `declare const X: Component<...>` value can't itself carry a generic type parameter the way a class can, so generic components (`@template`/`generics`) get a per-component interface instead of `Component<...>` directly:
236
+
237
+ ```ts
238
+ interface GenericListComponent {
239
+ new <Item extends { id: string | number } = { id: string }>(
240
+ options: ComponentConstructorOptions<GenericListProps<Item>>,
241
+ ): SvelteComponent<GenericListProps<Item>> & GenericListExports;
242
+ <Item extends { id: string | number } = { id: string }>(
243
+ this: void,
244
+ internals: ComponentInternals,
245
+ props: GenericListProps<Item>,
246
+ ): { $on?(...): () => void; $set?(...): void } & GenericListExports;
247
+ z_$$bindings?: "";
248
+ }
249
+ declare const GenericList: GenericListComponent;
250
+ export default GenericList;
251
+ ```
252
+
253
+ Both signatures carry their own generic parameter (not the `const` itself), so `Item` is inferred per usage site, e.g. `<GenericList items={numbers} />` infers `Item` from `numbers`. The `new` signature is the one place `"component"` format still touches a legacy type (`SvelteComponent`/`ComponentConstructorOptions`, not the deprecated `SvelteComponentTyped`): the Svelte language server resolves generic inference for `<Comp prop={...} />` template usage through `new`, not the plain call signature, confirmed by comparing against `@sveltejs/package`'s own generated output for the same component. Omitting it silently breaks inference instead of erroring, so it stays in even though it means one legacy import for generic components.
254
+
255
+ ### Opt-in semantic resolution (`resolveTypes`)
177
256
 
178
257
  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
258
 
@@ -203,19 +282,19 @@ Without `resolveTypes`, JSON lists no props. With it, each field shows up with `
203
282
 
204
283
  **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
284
 
206
- #### Persistent parse cache (`cache`)
285
+ ### Persistent parse cache (`cache`)
207
286
 
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.
287
+ Parsed output is written to disk and reused when the source file has not changed, on by default. That applies across runs, including CI on a fresh checkout.
209
288
 
210
289
  ```ts
211
- await sveld({ json: true, cache: true });
290
+ await sveld({ json: true });
212
291
  ```
213
292
 
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.
293
+ By default this writes to `node_modules/.cache/sveld/parse-cache.json`. Pass a string to use a different location, e.g. `cache: ".cache/sveld.json"`, or `cache: false` to disable it. Also available as `--cache` / `--cache=<path>` / `--cache=false` on the CLI.
215
294
 
216
295
  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
296
 
218
- #### Compile-checked `@example` blocks (`checkExamples`)
297
+ ### Compile-checked `@example` blocks (`checkExamples`)
219
298
 
220
299
  `@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
300
 
@@ -254,30 +333,34 @@ The check is narrow on purpose. It catches renamed or removed symbols and wrong
254
333
 
255
334
  Needs `typescript` and a `tsconfig.json`, same as `resolveTypes`. Use `--strict` (or the `strict` option) to fail CI when an example breaks.
256
335
 
257
- #### Type inference diagnostics
336
+ ### Type inference diagnostics
258
337
 
259
- `sveld` collects unresolved-type diagnostics on every run: props that fall back to `any`, context values typed as `any`, `@event` tags with no dispatch or callback, and (when `checkExamples` is enabled) `example-compile-error`. They are always returned from the programmatic `sveld()` API in `SveldResult.diagnostics`.
338
+ `sveld` collects unresolved-type diagnostics on every run: props that fall back to `any`, context values typed as `any`, `@event` tags with no dispatch or callback, `$props()`/`{@render}` syntax sveld can't model, and (when `checkExamples` is enabled) `example-compile-error`. They are always returned from the programmatic `sveld()` API in `SveldResult.diagnostics`. Each diagnostic carries an optional `source` range (the same `{ start: { line, column }, end: { line, column } }` shape as JSON output source ranges) whenever the parser holds a stable position for it.
260
339
 
261
340
  With `reportDiagnostics` or `strict`, the grouped summary looks like this:
262
341
 
263
342
  ```
264
- sveld: 4 unresolved types found.
343
+ sveld: 5 unresolved types found.
265
344
 
266
345
  Props without inferred types (1):
267
346
  ./icons/Add.svelte
268
- - Prop "title" type could not be inferred; falling back to "any".
347
+ - Prop "title" type could not be inferred; falling back to "any". (./icons/Add.svelte:4:2)
269
348
 
270
349
  Context values typed as `any` (1):
271
350
  ./ThemeProvider.svelte
272
- - Context "theme" variable "themeStore" has no type annotation; defaulted to "any".
351
+ - Context "theme" variable "themeStore" has no type annotation; defaulted to "any". (./ThemeProvider.svelte:8:6)
273
352
 
274
353
  @event tags with no dispatch or callback (2):
275
354
  ./Modal.svelte
276
- - @event "open" has no matching dispatch or callback prop.
277
- - @event "close" has no matching dispatch or callback prop.
355
+ - @event "open" has no matching dispatch or callback prop. (./Modal.svelte:3:5)
356
+ - @event "close" has no matching dispatch or callback prop. (./Modal.svelte:4:5)
357
+
358
+ Component syntax sveld skipped (1):
359
+ ./Tabs.svelte
360
+ - {@render tabs(getTabProps())} argument is not a plain object literal; the render call was not mapped to slot metadata. (./Tabs.svelte:6:4)
278
361
  ```
279
362
 
280
- When `checkExamples` is also enabled, `@example` compile failures appear as a fourth group:
363
+ When `checkExamples` is also enabled, `@example` compile failures appear as a fifth group:
281
364
 
282
365
  ```
283
366
  @example blocks that failed to compile (1):
@@ -306,6 +389,12 @@ npx sveld --json --strict
306
389
 
307
390
  `--check` is separate: it diffs `COMPONENT_API.json` for API drift and semver classification, not inference warnings.
308
391
 
392
+ ## Requirements
393
+
394
+ - Node 22+. CI tests against Node 22 on Linux, Windows, and macOS; earlier LTS versions are not verified.
395
+ - `sveld` is ESM-only. `require("sveld")` does not work — use `import` or dynamic `import()`.
396
+ - `sveld` bundles its own Svelte 5 compiler to parse `.svelte` files. Parsing does not depend on the Svelte version installed in your project, so Svelte 3 and Svelte 4 codebases parse the same way Svelte 5 codebases do — there is no compiler version to match up.
397
+
309
398
  ## Usage
310
399
 
311
400
  ### Installation
@@ -328,7 +417,7 @@ yarn add -D sveld
328
417
 
329
418
  ### Vite
330
419
 
331
- Import and add `sveld` as a plugin to your `vite.config.ts`. The plugin only runs during `vite build` (not the dev server).
420
+ Import and add `sveld` as a plugin to your `vite.config.ts`. The plugin only runs during `vite build`, unless `watch: true` is set, in which case it also regenerates output during `vite dev`.
332
421
 
333
422
  ```ts
334
423
  // vite.config.ts
@@ -343,7 +432,7 @@ export default defineConfig({
343
432
 
344
433
  Since Vite uses Rollup for production builds, the same plugin works in Rollup configs.
345
434
 
346
- By default, `sveld` will use the `"svelte"` field from your `package.json` to determine the entry point. You can override this by specifying an explicit `entry` option:
435
+ By default, `sveld` uses the `"svelte"` field from your `package.json` to determine the entry point. You can override this by specifying an explicit `entry` option:
347
436
 
348
437
  ```js
349
438
  sveld({
@@ -354,7 +443,6 @@ sveld({
354
443
  When building the library, TypeScript definitions are emitted to the `types` folder by default.
355
444
 
356
445
  Customize the output folder using the `typesOptions.outDir` option.
357
- Use `typesOptions.printWidth` to control Prettier wrapping for generated `.d.ts` files. The default is `80`.
358
446
 
359
447
  The following example emits the output to the `dist` folder:
360
448
 
@@ -362,7 +450,6 @@ The following example emits the output to the `dist` folder:
362
450
  sveld({
363
451
  + typesOptions: {
364
452
  + outDir: 'dist',
365
- + printWidth: 80
366
453
  + }
367
454
  })
368
455
  ```
@@ -381,6 +468,12 @@ Generate documentation in JSON and/or Markdown formats using the following flags
381
468
  npx sveld --json --markdown
382
469
  ```
383
470
 
471
+ If no entry point can be resolved (no `package.json#svelte` field and no `--entry`), the CLI exits `1` and prints the reason to `stderr`. If `src/index.js` happens to exist relative to your working directory, sveld falls back to it and prints a one-line note asking you to set `package.json#svelte` (or `--entry`) instead of relying on the fallback.
472
+
473
+ Flags are kebab-case: `--entry`, `--glob`, `--types`, `--json`, `--markdown`, `--fail-fast`, `--cache`, `--resolve-types`, `--check-examples`, `--report-diagnostics`, `--strict`, `--check`, `--types-format`. The camelCase spellings `--resolveTypes` and `--checkExamples` still work as deprecated aliases for compatibility with existing scripts. An unrecognized flag (e.g. `--markdwon`) prints `Unknown flag: --markdwon` to `stderr`, exits `1`, and skips generation; sveld takes no positional arguments, so any non-flag argument errors the same way.
474
+
475
+ Run `npx sveld --help` for the full flag list with descriptions, or `npx sveld --version` to print the installed version.
476
+
384
477
  ### CI: API-drift checks (`--check`)
385
478
 
386
479
  `--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.
@@ -407,18 +500,22 @@ Removed props, events, or slots, and props that become required, are breaking (`
407
500
 
408
501
  Use `--check=<path>` to diff against a snapshot at a custom location (defaults to `jsonOptions.outFile`, or `COMPONENT_API.json`).
409
502
 
503
+ The CLI exits non-zero on any fatal error (an unreadable entry, a config file that throws, etc.), not just on `--strict`/`--check` findings, so it's safe to use either flag as a CI gate.
504
+
410
505
  ### Node.js
411
506
 
412
- You can also call `sveld` from Node.js. The package is **ESM-only**; `require("sveld")` does not work. Use `import` or dynamic `import()`.
507
+ You can also call `sveld` from Node.js. See [Requirements](#requirements) for supported Node versions and the ESM-only constraint.
413
508
 
414
- If no `input` is specified, `sveld` will infer the entry point based on the `package.json#svelte` field.
509
+ If no `entry` is specified, `sveld` infers the entry point based on the `package.json#svelte` field. If no entry point can be resolved, `sveld()` throws with an actionable message (previously it resolved to `{ diagnostics: [] }` and generated nothing).
510
+
511
+ > `input` was renamed to `entry` to match the CLI, plugin, and config. Calls passing `input` throw with a message pointing at `entry`.
415
512
 
416
513
  ```js
417
514
  import { sveld } from "sveld";
418
515
  import pkg from "./package.json" with { type: "json" };
419
516
 
420
517
  const { diagnostics } = await sveld({
421
- input: "./src/index.js",
518
+ entry: "./src/index.js",
422
519
  glob: true,
423
520
  markdown: true,
424
521
  markdownOptions: {
@@ -439,6 +536,21 @@ const { diagnostics } = await sveld({
439
536
 
440
537
  `diagnostics` is always populated; printing is opt-in via `reportDiagnostics` or `strict` (see [Type inference diagnostics](#type-inference-diagnostics)).
441
538
 
539
+ Pass `check: true` (or `check: "<path>"` for a custom snapshot location) to diff against a committed `COMPONENT_API.json`, the same way `--check` does on the CLI. The result lands on `SveldResult.check`; sveld does not print it or touch `process.exitCode` for you, so inspect and act on it yourself:
540
+
541
+ ```js
542
+ import { formatCheckReport } from "sveld";
543
+
544
+ const { check } = await sveld({ json: true, check: true });
545
+
546
+ if (check) {
547
+ console.log(formatCheckReport(check));
548
+ if (check.bump === "major") process.exitCode = 1;
549
+ }
550
+ ```
551
+
552
+ See [CI: API-drift checks (`--check`)](#ci-api-drift-checks---check) for how changes are classified.
553
+
442
554
  #### `jsonOptions.outDir`
443
555
 
444
556
  With `json: true`, `sveld` writes `COMPONENT_API.json` at the project root. The file documents all components.
@@ -479,17 +591,34 @@ CLI flags > config file > `package.json#svelte` inference / defaults
479
591
 
480
592
  With the config above, `npx sveld --json` keeps `glob` and `markdown` from the file. A CLI flag overrides the same key in the config.
481
593
 
594
+ `strict`, `reportDiagnostics`, and `check` are valid config keys too — not just CLI flags or `sveld()` options — and follow the same precedence: a CLI flag (or an option passed to `sveld()`) overrides the same key set in the config file.
595
+
596
+ ```js
597
+ // sveld.config.js
598
+ export default {
599
+ json: true,
600
+ strict: true,
601
+ check: "snapshots/COMPONENT_API.json",
602
+ };
603
+ ```
604
+
482
605
  A bad config (syntax error, throws at load time, or no default-export object) fails with an error that names the file.
483
606
 
484
607
  ### Publishing to NPM
485
608
 
486
- TypeScript definitions land in the `types` folder by default. Include that folder in `package.json` when you publish to npm.
609
+ TypeScript definitions land in the `types` folder by default. Point consumers at them with an `exports` map, and include that folder in `package.json` when you publish to npm.
487
610
 
488
611
  ```diff
489
612
  {
490
613
  "svelte": "./src/index.js",
614
+ + "exports": {
615
+ + ".": {
616
+ + "types": "./types/index.d.ts",
617
+ + "svelte": "./src/index.js",
618
+ + "default": "./lib/index.mjs"
619
+ + }
620
+ + },
491
621
  "main": "./lib/index.mjs",
492
- + "types": "./types/index.d.ts",
493
622
  "files": [
494
623
  "src",
495
624
  "lib",
@@ -498,26 +627,36 @@ TypeScript definitions land in the `types` folder by default. Include that folde
498
627
  }
499
628
  ```
500
629
 
630
+ The `svelte` condition lets bundlers that understand it (Vite, Rollup, webpack via `svelte-loader`) resolve straight to source; `types` and `default` cover TypeScript and everything else. Keep the top-level `"svelte"` field too — older tooling that predates conditional exports still reads it directly.
631
+
501
632
  ## Available Options
502
633
 
503
634
  ### Plugin Options
504
635
 
505
- - **`entry`** (string, optional): Specify the entry point to uncompiled Svelte source. If not provided, sveld will use the `"svelte"` field from `package.json`.
636
+ - **`entry`** (string, optional): Specify the entry point to uncompiled Svelte source. If not provided, sveld uses the `"svelte"` field from `package.json`.
506
637
  - **`glob`** (boolean, optional): Enable glob mode to analyze all `*.svelte` files.
507
638
  - **`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).
508
639
  - **`types`** (boolean, optional, default: `true`): Generate TypeScript definitions.
509
- - **`typesOptions`** (object, optional): Options for TypeScript definition generation, including `outDir`, `preamble`, and `printWidth`.
640
+ <<<<<<< HEAD
641
+ - **`typesOptions`** (object, optional): Options for TypeScript definition generation, including `outDir`, `preamble`, and `format`.
642
+ - **`format`** (`"class"` | `"component"`, optional, default: `"class"`): `.d.ts` output shape. `"class"` extends `SvelteComponentTyped`; `"component"` emits the Svelte 5 `Component` type. Also available as `--types-format`. See [`.d.ts` output format](#dts-output-format-typesoptionsformat).
643
+ =======
644
+ - **`typesOptions`** (object, optional): Options for TypeScript definition generation, including `outDir` and `preamble`.
645
+ >>>>>>> 0724225 (feat(writer)!: remove prettier entirely)
510
646
  - **`json`** (boolean, optional): Generate component documentation in JSON format.
511
647
  - **`jsonOptions`** (object, optional): Options for JSON output.
512
648
  - **`markdown`** (boolean, optional): Generate component documentation in Markdown format.
513
649
  - **`markdownOptions`** (object, optional): Options for Markdown output.
650
+ - **`customElements`** (boolean, optional): Generate a [Custom Elements Manifest](#custom-elements-manifest) (`custom-elements.json`). Also available as the `--custom-elements` CLI flag.
651
+ - **`customElementsOptions`** (object, optional): Options for Custom Elements Manifest output, including `outFile`.
514
652
  - **`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`.
515
653
  - **`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.
516
- - **`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).
517
- - **`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).
518
- - **`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).
654
+ - **`resolveTypes`** (boolean, optional, default: `false`): Load the TypeScript program to expand opaque imported whole-object `$props()` types into JSON. Also available as `--resolve-types` (`--resolveTypes` remains as a deprecated alias). See [Opt-in semantic resolution](#opt-in-semantic-resolution-resolvetypes).
655
+ - **`cache`** (boolean | string, optional, default: `true`): Write parsed component output to disk and skip re-parsing unchanged files on later runs. On by default, writing to `node_modules/.cache/sveld/parse-cache.json`; a string sets a custom path; pass `false` to disable. Also available as `--cache` / `--cache=<path>` / `--cache=false`. See [Persistent parse cache](#persistent-parse-cache-cache).
656
+ - **`checkExamples`** (boolean, optional, default: `false`): Run plain TS/JS `@example` blocks through the TypeScript program. Broken ones get an `example-compile-error` diagnostic. Also available as `--check-examples` (`--checkExamples` remains as a deprecated alias). See [Compile-checked `@example` blocks](#compile-checked-example-blocks-checkexamples).
519
657
  - **`reportDiagnostics`** (boolean, optional, default: `false`): Print unresolved-type diagnostics to stderr (CLI) or `console.warn` (programmatic API). Also available as `--report-diagnostics`. See [Type inference diagnostics](#type-inference-diagnostics).
520
658
  - **`strict`** (boolean, optional, default: `false`): Exit with code `1` when diagnostics exist. Implies `reportDiagnostics`. Also available as `--strict`. See [Type inference diagnostics](#type-inference-diagnostics).
659
+ - **`check`** (boolean | string, optional, default: `false`): Diff the parsed component API against a committed snapshot and assign a semver bump to each change. `true` uses the `json` writer's `outFile` (or `COMPONENT_API.json`); a string sets a custom snapshot path. Also available as `--check` / `--check=<path>`. On the CLI this exits `1` on a breaking change; from `sveld()` it's returned on `SveldResult.check` for you to act on. See [CI: API-drift checks (`--check`)](#ci-api-drift-checks---check).
521
660
 
522
661
  By default, only TypeScript definitions are generated.
523
662
 
@@ -559,7 +698,7 @@ JSON adds `exports` and `totalExports`. Markdown adds an "Exports" section. Each
559
698
  ## JSON Output
560
699
 
561
700
  When `json: true` is enabled, `sveld` emits a `COMPONENT_API.json` file with schema and generator metadata plus the parsed
562
- component API.
701
+ component API. For stable output, generated `events` arrays are emitted in deterministic sorted order.
563
702
 
564
703
  The JSON Schema lives on GitHub ([path to file](https://github.com/carbon-design-system/sveld/blob/main/schema/component-api.schema.json), [raw URL](https://raw.githubusercontent.com/carbon-design-system/sveld/main/schema/component-api.schema.json)). Use it to validate generated `COMPONENT_API.json` files. Optional fields may be missing when the parser has no stable source for that metadata.
565
704
 
@@ -615,6 +754,7 @@ interface ComponentDocApi {
615
754
  componentComment?: string;
616
755
  componentCommentSource?: SourceRange;
617
756
  contexts?: ComponentContext[];
757
+ customElementTag?: string;
618
758
  }
619
759
 
620
760
  interface ComponentProp {
@@ -681,6 +821,47 @@ Prop metadata is additive and keeps the older public fields:
681
821
  - `value` remains the raw default expression string. `defaultValue` adds structured metadata with the same raw expression, a coarse `kind`, and a parsed `value` only for JSON-safe literals, arrays, and plain objects. `sveld` does not evaluate arbitrary code.
682
822
  - `bindable: true` is emitted only for props explicitly declared with Svelte 5 `$bindable(...)`. Missing `bindable` should be treated as false.
683
823
 
824
+ ## Custom Elements Manifest
825
+
826
+ Set `customElements: true` to emit a [Custom Elements Manifest](https://github.com/webcomponents/custom-elements-manifest) (`custom-elements.json`, `schemaVersion: "1.0.0"`). This is the interchange format the web-components ecosystem standardized on: Storybook autodocs, VS Code/JetBrains HTML data, and other CEM-aware tooling can all read it directly.
827
+
828
+ ```diff
829
+ sveld({
830
+ + customElements: true,
831
+ })
832
+ ```
833
+
834
+ - **`customElements`** (boolean, optional): Generate `custom-elements.json`.
835
+ - **`customElementsOptions.outFile`** (string, optional, default: `"custom-elements.json"`): Override the output path.
836
+ - Also available as the `--custom-elements` CLI flag.
837
+
838
+ Each exported component becomes one `javascript-module` with a class declaration:
839
+
840
+ - **Members** — every prop becomes a `ClassField` (`name`, `type.text`, `default`, `description`, `deprecated`).
841
+ - **Attributes** — derived conservatively from props: only props with a bare primitive type (`string`, `number`, or `boolean`) become attributes, using the same default name Svelte's custom-element runtime uses (`prop.toLowerCase()`, unless the tool's own `attribute` override applies). Props whose lowercased name collides with another prop are dropped from `attributes` on both sides, since which one wins at runtime is ambiguous. Complex types (arrays, objects, unions, custom types) never become attributes.
842
+ - **Events** — dispatched events (`createEventDispatcher()`, and `$host().dispatchEvent(...)` from inside a custom element) become `{ name, type: { text: "CustomEvent<...>" } }`. Forwarded (`on:click`) events are left out, since they aren't dispatched by the component's own class.
843
+ - **Slots** — named and default slots, with descriptions. The default slot's `name` is `""`, matching the CEM convention.
844
+
845
+ When a component sets `<svelte:options customElement="x-foo" />` (or the object form, `<svelte:options customElement={{ tag: "x-foo" }} />`), its declaration gets `tagName: "x-foo"` and `customElement: true`, and the module's `exports` include a `custom-element-definition` export alongside the plain `js` export.
846
+
847
+ Components without `customElement` still emit a plain class declaration (no `tagName`/`customElement`) — useful for documenting the class shape even before it's compiled as a custom element, but the manifest is most useful for `customElement`-compiled builds, where downstream tooling can resolve `tagName`, `attributes`, and `events` for actual custom-element usage.
848
+
849
+ ### Consuming the manifest
850
+
851
+ Most tools discover `custom-elements.json` through a `customElements` field in `package.json`, pointing at the generated file:
852
+
853
+ ```json
854
+ {
855
+ "customElements": "custom-elements.json"
856
+ }
857
+ ```
858
+
859
+ With that in place:
860
+
861
+ - The [VS Code custom elements extension](https://marketplace.visualstudio.com/items?itemName=BendingSpoons.vscode-custom-elements) and JetBrains IDEs pick it up automatically, giving tag name, attribute, and slot completion/hover in HTML and Svelte templates.
862
+ - [Storybook](https://storybook.js.org/docs/api/doc-blocks/doc-block-argtypes#extracting-argtypes) for web components reads the manifest to auto-generate `argTypes` (controls, docs tables) for `customElement`-compiled components, once you point it at the file (e.g. `setCustomElementsManifest` from `@storybook/web-components`, or `customElements: "custom-elements.json"` in `.storybook/main.js`).
863
+ - Any other tool built against the [Custom Elements Manifest spec](https://github.com/webcomponents/custom-elements-manifest) (API viewers, doc generators, linters) can read the file directly without sveld-specific integration.
864
+
684
865
  ## API Reference
685
866
 
686
867
  ### `reactive`
@@ -730,11 +911,9 @@ Generated JSON includes `"binding": "readonly"` or `"binding": "writable"` for a
730
911
 
731
912
  This is documentation only. Generated `.svelte.d.ts` prop types do not change. TypeScript cannot express Svelte binding direction reliably.
732
913
 
733
- For stable output, generated `events` arrays are emitted in deterministic sorted order.
734
-
735
914
  ### `@type`
736
915
 
737
- Without a `@type` annotation, `sveld` will infer the primitive type for a prop:
916
+ Without a `@type` annotation, `sveld` infers the primitive type for a prop:
738
917
 
739
918
  ```js
740
919
  export let kind = "primary";
@@ -763,8 +942,6 @@ For `lang="ts"` components, prefer native TypeScript annotations when you alread
763
942
 
764
943
  **Example:**
765
944
 
766
- **Svelte 5 Runes:**
767
-
768
945
  ```svelte
769
946
  <script>
770
947
  let {
@@ -784,7 +961,8 @@ For `lang="ts"` components, prefer native TypeScript annotations when you alread
784
961
 
785
962
  For runes components with multiple destructured props, put JSDoc on the property you want to document. A declaration-level block is a fallback when the destructure exposes a single public prop.
786
963
 
787
- **Svelte 3, 4, 5 (non-Runes):**
964
+ <details>
965
+ <summary>Svelte 3/4 (legacy) syntax</summary>
788
966
 
789
967
  ```svelte
790
968
  <script>
@@ -802,6 +980,8 @@ For runes components with multiple destructured props, put JSDoc on the property
802
980
  </script>
803
981
  ```
804
982
 
983
+ </details>
984
+
805
985
  #### Importing types
806
986
 
807
987
  `sveld` supports TypeScript's [`import(...)` type syntax](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#import-types), so a `@type` or `@typedef` can reference a type from another module without a top-level `import`. The expression is copied verbatim into the generated `.d.ts` and resolves the same way as hand-written TypeScript:
@@ -1012,8 +1192,6 @@ The `@typedef` tag defines a shared type used multiple times in a component. All
1012
1192
 
1013
1193
  **Example:**
1014
1194
 
1015
- **Svelte 5 Runes:**
1016
-
1017
1195
  ```svelte
1018
1196
  <script>
1019
1197
  /**
@@ -1030,7 +1208,8 @@ The `@typedef` tag defines a shared type used multiple times in a component. All
1030
1208
  </script>
1031
1209
  ```
1032
1210
 
1033
- **Svelte 3, 4, 5 (non-Runes):**
1211
+ <details>
1212
+ <summary>Svelte 3/4 (legacy) syntax</summary>
1034
1213
 
1035
1214
  ```svelte
1036
1215
  <script>
@@ -1047,6 +1226,8 @@ The `@typedef` tag defines a shared type used multiple times in a component. All
1047
1226
  </script>
1048
1227
  ```
1049
1228
 
1229
+ </details>
1230
+
1050
1231
  #### Using `@property` for complex typedefs
1051
1232
 
1052
1233
  For complex object types, use `@property` to document individual fields. That gives per-property tooltips in the IDE.
@@ -1063,8 +1244,6 @@ For complex object types, use `@property` to document individual fields. That gi
1063
1244
 
1064
1245
  **Example:**
1065
1246
 
1066
- **Svelte 5 Runes:**
1067
-
1068
1247
  ```svelte
1069
1248
  <script>
1070
1249
  /**
@@ -1080,7 +1259,8 @@ For complex object types, use `@property` to document individual fields. That gi
1080
1259
  </script>
1081
1260
  ```
1082
1261
 
1083
- **Svelte 3, 4, 5 (non-Runes):**
1262
+ <details>
1263
+ <summary>Svelte 3/4 (legacy) syntax</summary>
1084
1264
 
1085
1265
  ```svelte
1086
1266
  <script>
@@ -1097,6 +1277,10 @@ For complex object types, use `@property` to document individual fields. That gi
1097
1277
  </script>
1098
1278
  ```
1099
1279
 
1280
+ </details>
1281
+
1282
+ Output is identical for both syntax modes.
1283
+
1100
1284
  Output:
1101
1285
 
1102
1286
  ```ts
@@ -1134,8 +1318,6 @@ Use square brackets for optional properties, per JSDoc. Default values use `[pro
1134
1318
 
1135
1319
  **Example:**
1136
1320
 
1137
- **Svelte 5 Runes:**
1138
-
1139
1321
  ```svelte
1140
1322
  <script>
1141
1323
  /**
@@ -1152,7 +1334,8 @@ Use square brackets for optional properties, per JSDoc. Default values use `[pro
1152
1334
  </script>
1153
1335
  ```
1154
1336
 
1155
- **Svelte 3, 4, 5 (non-Runes):**
1337
+ <details>
1338
+ <summary>Svelte 3/4 (legacy) syntax</summary>
1156
1339
 
1157
1340
  ```svelte
1158
1341
  <script>
@@ -1170,6 +1353,10 @@ Use square brackets for optional properties, per JSDoc. Default values use `[pro
1170
1353
  </script>
1171
1354
  ```
1172
1355
 
1356
+ </details>
1357
+
1358
+ Output is identical for both syntax modes.
1359
+
1173
1360
  Output:
1174
1361
 
1175
1362
  ```ts
@@ -1459,8 +1646,6 @@ Use it for callback props when you do not want inline function type syntax.
1459
1646
 
1460
1647
  **Example:**
1461
1648
 
1462
- **Svelte 5 Runes:**
1463
-
1464
1649
  ```svelte
1465
1650
  <script>
1466
1651
  /**
@@ -1476,7 +1661,8 @@ Use it for callback props when you do not want inline function type syntax.
1476
1661
  </script>
1477
1662
  ```
1478
1663
 
1479
- **Svelte 3, 4, 5 (non-Runes):**
1664
+ <details>
1665
+ <summary>Svelte 3/4 (legacy) syntax</summary>
1480
1666
 
1481
1667
  ```svelte
1482
1668
  <script>
@@ -1493,6 +1679,10 @@ Use it for callback props when you do not want inline function type syntax.
1493
1679
  </script>
1494
1680
  ```
1495
1681
 
1682
+ </details>
1683
+
1684
+ Output is identical for both syntax modes.
1685
+
1496
1686
  Output:
1497
1687
 
1498
1688
  ```ts
@@ -1550,8 +1740,6 @@ Omit the `slot-name` to type the default slot.
1550
1740
 
1551
1741
  **Example:**
1552
1742
 
1553
- **Svelte 5 Runes:**
1554
-
1555
1743
  ```svelte
1556
1744
  <script>
1557
1745
  /**
@@ -1573,7 +1761,8 @@ Omit the `slot-name` to type the default slot.
1573
1761
  </p>
1574
1762
  ```
1575
1763
 
1576
- **Svelte 3, 4, 5 (non-Runes):**
1764
+ <details>
1765
+ <summary>Svelte 3/4 (legacy) syntax</summary>
1577
1766
 
1578
1767
  ```svelte
1579
1768
  <script>
@@ -1596,6 +1785,8 @@ Omit the `slot-name` to type the default slot.
1596
1785
  </p>
1597
1786
  ```
1598
1787
 
1788
+ </details>
1789
+
1599
1790
  #### Extra JSDoc tags before `@slot`
1600
1791
 
1601
1792
  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" }, ...]`.
@@ -1749,8 +1940,6 @@ Use `null` as the value if no event detail is provided.
1749
1940
 
1750
1941
  **Example:**
1751
1942
 
1752
- **Svelte 5 Runes:**
1753
-
1754
1943
  ```svelte
1755
1944
  <script>
1756
1945
  /**
@@ -1785,7 +1974,8 @@ Use `null` as the value if no event detail is provided.
1785
1974
  </script>
1786
1975
  ```
1787
1976
 
1788
- **Svelte 3, 4, 5 (non-Runes):**
1977
+ <details>
1978
+ <summary>Svelte 3/4 (legacy) syntax</summary>
1789
1979
 
1790
1980
  ```svelte
1791
1981
  <script>
@@ -1805,6 +1995,10 @@ Use `null` as the value if no event detail is provided.
1805
1995
  </script>
1806
1996
  ```
1807
1997
 
1998
+ </details>
1999
+
2000
+ Output is identical for both syntax modes.
2001
+
1808
2002
  Output:
1809
2003
 
1810
2004
  ```ts
@@ -1837,8 +2031,6 @@ This is the idiomatic way to describe each field of an event detail. An inline o
1837
2031
 
1838
2032
  **Example:**
1839
2033
 
1840
- **Svelte 5 Runes:**
1841
-
1842
2034
  ```svelte
1843
2035
  <script>
1844
2036
  /**
@@ -1865,7 +2057,8 @@ This is the idiomatic way to describe each field of an event detail. An inline o
1865
2057
  <button type="button" onclick={handleSubmit}>Submit</button>
1866
2058
  ```
1867
2059
 
1868
- **Svelte 3, 4, 5 (non-Runes):**
2060
+ <details>
2061
+ <summary>Svelte 3/4 (legacy) syntax</summary>
1869
2062
 
1870
2063
  ```svelte
1871
2064
  <script>
@@ -1895,6 +2088,10 @@ This is the idiomatic way to describe each field of an event detail. An inline o
1895
2088
  <button type="button" on:click={handleSubmit}>Submit</button>
1896
2089
  ```
1897
2090
 
2091
+ </details>
2092
+
2093
+ Output is identical for both syntax modes.
2094
+
1898
2095
  Output:
1899
2096
 
1900
2097
  ```ts
@@ -1921,8 +2118,6 @@ Like typedefs, you can mark event detail properties as optional with square brac
1921
2118
 
1922
2119
  **Example:**
1923
2120
 
1924
- **Svelte 5 Runes:**
1925
-
1926
2121
  ```svelte
1927
2122
  <script>
1928
2123
  /**
@@ -1953,7 +2148,8 @@ Like typedefs, you can mark event detail properties as optional with square brac
1953
2148
  <button type="button" onclick={throwSnowball}>Throw</button>
1954
2149
  ```
1955
2150
 
1956
- **Svelte 3, 4, 5 (non-Runes):**
2151
+ <details>
2152
+ <summary>Svelte 3/4 (legacy) syntax</summary>
1957
2153
 
1958
2154
  ```svelte
1959
2155
  <script>
@@ -1985,6 +2181,10 @@ Like typedefs, you can mark event detail properties as optional with square brac
1985
2181
  <button type="button" on:click={throwSnowball}>Throw</button>
1986
2182
  ```
1987
2183
 
2184
+ </details>
2185
+
2186
+ Output is identical for both syntax modes.
2187
+
1988
2188
  Output:
1989
2189
 
1990
2190
  ```ts
@@ -2123,8 +2323,6 @@ Anything else (dynamic identifiers, template interpolation, other function calls
2123
2323
 
2124
2324
  **Modal.svelte**
2125
2325
 
2126
- **Svelte 5 Runes:**
2127
-
2128
2326
  ```svelte
2129
2327
  <script>
2130
2328
  import { setContext } from "svelte";
@@ -2155,7 +2353,8 @@ Anything else (dynamic identifiers, template interpolation, other function calls
2155
2353
  </div>
2156
2354
  ```
2157
2355
 
2158
- **Svelte 3, 4, 5 (non-Runes):**
2356
+ <details>
2357
+ <summary>Svelte 3/4 (legacy) syntax</summary>
2159
2358
 
2160
2359
  ```svelte
2161
2360
  <script>
@@ -2185,6 +2384,10 @@ Anything else (dynamic identifiers, template interpolation, other function calls
2185
2384
  </div>
2186
2385
  ```
2187
2386
 
2387
+ </details>
2388
+
2389
+ Output is identical for both syntax modes.
2390
+
2188
2391
  **Generated TypeScript definition:**
2189
2392
 
2190
2393
  ```ts
@@ -2294,7 +2497,7 @@ There are several ways to type contexts:
2294
2497
 
2295
2498
  #### Notes
2296
2499
 
2297
- - Context keys must be string literals (dynamic keys are not supported)
2500
+ - Context keys must be statically resolvable: a string literal, a static template literal, a `const`-bound string, or a `Symbol()` / `Symbol.for()` call with a static description. Dynamic expressions (runtime identifiers, template interpolation, other function calls) are skipped with a warning.
2298
2501
  - Variables passed to `setContext` should have JSDoc `@type` annotations for accurate types
2299
2502
  - The generated type name follows the pattern: `{PascalCase}Context`. Separators (hyphens, underscores, dots, colons, slashes, spaces) are stripped and each segment is capitalized:
2300
2503
  | Context Key | Generated Type Name |
@@ -2328,8 +2531,6 @@ Use `@restProps` to name the element tags `$$restProps` is forwarded to.
2328
2531
 
2329
2532
  **Example:**
2330
2533
 
2331
- **Svelte 5 Runes:**
2332
-
2333
2534
  ```svelte
2334
2535
  <script>
2335
2536
  import Button from "./Button.svelte";
@@ -2347,7 +2548,8 @@ Use `@restProps` to name the element tags `$$restProps` is forwarded to.
2347
2548
  {/if}
2348
2549
  ```
2349
2550
 
2350
- **Svelte 3, 4, 5 (non-Runes):**
2551
+ <details>
2552
+ <summary>Svelte 3/4 (legacy) syntax</summary>
2351
2553
 
2352
2554
  ```svelte
2353
2555
  <script>
@@ -2364,6 +2566,8 @@ Use `@restProps` to name the element tags `$$restProps` is forwarded to.
2364
2566
  {/if}
2365
2567
  ```
2366
2568
 
2569
+ </details>
2570
+
2367
2571
  ### `@extendProps`
2368
2572
 
2369
2573
  When a component wraps another, use `@extendProps` to extend generated props.
@@ -2390,14 +2594,15 @@ import Button from "./Button.svelte";
2390
2594
 
2391
2595
  ### `@template`
2392
2596
 
2393
- Svelte supports defining generics via the [`generics` attribute](https://svelte.dev/docs/svelte/typescript) on the script tag, but this requires `lang="ts"`.
2597
+ Svelte supports defining generics via the [`generics` attribute](https://svelte.dev/docs/svelte/typescript) on the script tag, but this requires `lang="ts"`:
2394
2598
 
2395
2599
  ```svelte
2396
- <!-- Requires lang="ts" -->
2397
2600
  <script lang="ts" generics="Row extends DataTableRow = any"></script>
2398
2601
  ```
2399
2602
 
2400
- Because `sveld` targets JavaScript-only usage as a baseline, generics use the standard JSDoc `@template` tag. `@generics` is also supported as an alias.
2603
+ `sveld` reads the `generics` attribute directly, and it's the recommended way to declare generics for `lang="ts"` components. Because `sveld` also targets JavaScript-only usage as a baseline, plain JS (or JS-with-JSDoc) components instead use the standard JSDoc `@template` tag; `@generics` is also supported as an alias.
2604
+
2605
+ **Precedence:** if a component declares generics both ways, the `generics` attribute wins — it's the compiler-checked source of truth — and the `@generics`/`@template` tag is reported as a `syntax-skipped` diagnostic rather than silently dropped. The attribute is invalid without `lang="ts"`; if present on a plain-JS script, sveld reports `syntax-skipped` and ignores it rather than guessing.
2401
2606
 
2402
2607
  **Signature:** Uses standard [JSDoc `@template` syntax](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#template):
2403
2608
 
@@ -2417,8 +2622,6 @@ Because `sveld` targets JavaScript-only usage as a baseline, generics use the st
2417
2622
 
2418
2623
  **Component example:**
2419
2624
 
2420
- **Svelte 5 Runes:**
2421
-
2422
2625
  ```svelte
2423
2626
  <script>
2424
2627
  /**
@@ -2440,7 +2643,8 @@ Because `sveld` targets JavaScript-only usage as a baseline, generics use the st
2440
2643
  {@render children?.({ headers, rows })}
2441
2644
  ```
2442
2645
 
2443
- **Svelte 3, 4, 5 (non-Runes):**
2646
+ <details>
2647
+ <summary>Svelte 3/4 (legacy) syntax</summary>
2444
2648
 
2445
2649
  ```svelte
2446
2650
  <script>
@@ -2461,6 +2665,10 @@ Because `sveld` targets JavaScript-only usage as a baseline, generics use the st
2461
2665
  <slot {headers} {rows} />
2462
2666
  ```
2463
2667
 
2668
+ </details>
2669
+
2670
+ Output is identical for both syntax modes.
2671
+
2464
2672
  Generated output looks like this:
2465
2673
 
2466
2674
  ```ts
@@ -2533,12 +2741,10 @@ For multiple generics, use a single `@generics` tag with comma-separated names:
2533
2741
 
2534
2742
  The Svelte Language Server supports component-level comments through the following syntax: `<!-- @component [comment] -->`.
2535
2743
 
2536
- `sveld` will copy these over to the exported default component in the TypeScript definition.
2744
+ `sveld` copies these over to the exported default component in the TypeScript definition.
2537
2745
 
2538
2746
  **Example:**
2539
2747
 
2540
- **Svelte 5 Runes:**
2541
-
2542
2748
  ```svelte
2543
2749
  <!-- @component
2544
2750
  @example
@@ -2555,7 +2761,8 @@ The Svelte Language Server supports component-level comments through the followi
2555
2761
  </button>
2556
2762
  ```
2557
2763
 
2558
- **Svelte 3, 4, 5 (non-Runes):**
2764
+ <details>
2765
+ <summary>Svelte 3/4 (legacy) syntax</summary>
2559
2766
 
2560
2767
  ```svelte
2561
2768
  <!-- @component
@@ -2569,6 +2776,10 @@ The Svelte Language Server supports component-level comments through the followi
2569
2776
  </button>
2570
2777
  ```
2571
2778
 
2779
+ </details>
2780
+
2781
+ Output is identical for both syntax modes.
2782
+
2572
2783
  Output:
2573
2784
 
2574
2785
  ```ts
@@ -2604,8 +2815,6 @@ Exported functions and consts become accessor props in generated TypeScript defi
2604
2815
 
2605
2816
  **Example:**
2606
2817
 
2607
- **Svelte 5 Runes:**
2608
-
2609
2818
  ```svelte
2610
2819
  <script>
2611
2820
  /**
@@ -2649,7 +2858,8 @@ Exported functions and consts become accessor props in generated TypeScript defi
2649
2858
  </div>
2650
2859
  ```
2651
2860
 
2652
- **Svelte 3, 4, 5 (non-Runes):**
2861
+ <details>
2862
+ <summary>Svelte 3/4 (legacy) syntax</summary>
2653
2863
 
2654
2864
  ```svelte
2655
2865
  <script>
@@ -2688,6 +2898,10 @@ Exported functions and consts become accessor props in generated TypeScript defi
2688
2898
  </script>
2689
2899
  ```
2690
2900
 
2901
+ </details>
2902
+
2903
+ Output is identical for both syntax modes.
2904
+
2691
2905
  Output:
2692
2906
 
2693
2907
  ```ts
@@ -2723,6 +2937,14 @@ export default class Component extends SvelteComponentTyped<
2723
2937
 
2724
2938
  When only `@param` tags are present without `@returns`, the return type defaults to `any`. When only `@returns` is present without `@param`, the function signature is `() => returnType`.
2725
2939
 
2940
+ ## Troubleshooting
2941
+
2942
+ **A prop came out `any`.** Enable [`reportDiagnostics`](#type-inference-diagnostics) (or `strict` to fail CI) to see which props sveld couldn't infer, then tighten them with `@type` or a native TypeScript annotation.
2943
+
2944
+ **Generated types don't appear for consumers.** Check that the `types` folder is listed in `exports` and `files` in `package.json`. See [Publishing to NPM](#publishing-to-npm).
2945
+
2946
+ **Output differs in CI.** Commit `COMPONENT_API.json` and run [`--check`](#ci-api-drift-checks---check) in CI so API drift fails the build instead of silently diverging.
2947
+
2726
2948
  ## Contributing
2727
2949
 
2728
2950
  See [contributing guidelines](CONTRIBUTING.md).