sveld 0.37.9 → 0.38.1
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 +175 -4337
- package/{lib/browser.d.ts → browser.d.ts} +109 -479
- package/browser.js +265 -0
- package/chunk-wpqsw5ca.js +13 -0
- package/chunk-x0hkebcs.js +29 -0
- package/chunk-y5gqpp8d.js +5 -0
- package/chunk-ymq8jc0f.js +254 -0
- package/chunk-ynrmtc58.js +1 -0
- package/cli-entry.js +38 -0
- package/cli.js +1 -1
- package/{lib/index.d.ts → index.d.ts} +207 -601
- package/index.js +1 -0
- package/package.json +9 -14
- package/lib/browser.js +0 -272
- package/lib/chunk-81w3m3mq.js +0 -8
- package/lib/chunk-f8nq2w4s.js +0 -325
- package/lib/chunk-fk1w3bnw.js +0 -29
- package/lib/chunk-fz6nj0w3.js +0 -4
- package/lib/chunk-r92p6t72.js +0 -1
- package/lib/chunk-wd6xaaes.js +0 -13
- package/lib/chunk-yp6p39jn.js +0 -6
- package/lib/cli-entry.js +0 -1
- package/lib/index.js +0 -1
package/README.md
CHANGED
|
@@ -3,29 +3,95 @@
|
|
|
3
3
|
[![NPM][npm]][npm-url]
|
|
4
4
|

|
|
5
5
|
|
|
6
|
-
`sveld` generates TypeScript definitions and component documentation (Markdown
|
|
6
|
+
`sveld` generates TypeScript definitions and component documentation (JSON and Markdown) for Svelte component libraries. It statically analyzes props, events, slots, snippets, context, module exports, and rest props, and reads [JSDoc](https://jsdoc.app/) where inference isn't enough.
|
|
7
7
|
|
|
8
|
-
The
|
|
8
|
+
The generated `.d.ts` files give consumers autocomplete and type checking through the Svelte Language Server and TypeScript, with little effort from the library author. [Carbon Components Svelte](https://github.com/carbon-design-system/carbon-components-svelte) uses sveld to generate its component types and API metadata.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
sveld supports Svelte 3, Svelte 4, Svelte 5 without runes (`export let`, `<slot>`, `$$restProps`), and Svelte 5 runes (`$props()`, `$bindable()`, `{@render}`, callback props).
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
## When to use sveld
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
If your library is built with SvelteKit's `svelte-package`, it already emits `.d.ts` files through `svelte2tsx`; start there.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
sveld is for:
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
- **JavaScript-first libraries.** Components written in plain JavaScript with JSDoc, where you want full editor types without converting to `lang="ts"`.
|
|
19
|
+
- **Docs from the same source as the types.** JSON and Markdown API docs, generated in the same run.
|
|
20
|
+
- **API drift checks.** `--check` diffs the component API against a committed snapshot and fails CI on breaking changes.
|
|
21
|
+
- **Richer types from JSDoc.** `@typedef`, `@callback`, `@slot`, `@event`, and context types that plain inference can't produce.
|
|
22
|
+
|
|
23
|
+
`lang="ts"` components work too: sveld keeps their type annotations as written.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
npm i -D sveld
|
|
29
|
+
# or: pnpm i -D sveld / bun i -D sveld / yarn add -D sveld
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick start
|
|
33
|
+
|
|
34
|
+
sveld reads the `"svelte"` field of your `package.json` as the entry point: the file that re-exports your components.
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"svelte": "./src/index.js"
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### CLI
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
npx sveld # writes types/*.d.ts
|
|
46
|
+
npx sveld --json --markdown # also writes COMPONENT_API.json and COMPONENT_INDEX.md
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Run `npx sveld --help` for every flag. Options can also live in a `sveld.config.js`:
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
// sveld.config.js
|
|
53
|
+
import { defineConfig } from "sveld";
|
|
54
|
+
|
|
55
|
+
export default defineConfig({
|
|
56
|
+
json: true,
|
|
57
|
+
markdown: true,
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Vite plugin
|
|
19
62
|
|
|
20
|
-
|
|
63
|
+
```ts
|
|
64
|
+
// vite.config.ts
|
|
65
|
+
import { svelte } from "@sveltejs/vite-plugin-svelte";
|
|
66
|
+
import sveld from "sveld";
|
|
67
|
+
import { defineConfig } from "vite";
|
|
68
|
+
|
|
69
|
+
export default defineConfig({
|
|
70
|
+
plugins: [svelte(), sveld({ json: true })],
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The plugin runs during `vite build` (and during `vite dev` with `watch: true`). It also works in Rollup configs.
|
|
75
|
+
|
|
76
|
+
### Node
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
import { sveld } from "sveld";
|
|
80
|
+
|
|
81
|
+
const { document, diagnostics, exitCode } = await sveld({
|
|
82
|
+
entry: "./src/index.js",
|
|
83
|
+
json: true,
|
|
84
|
+
});
|
|
85
|
+
```
|
|
21
86
|
|
|
22
|
-
|
|
87
|
+
`document` is the same component API document `json: true` writes, so you can render your own formats from it. See [Options](docs/options.md) for everything `sveld()` accepts and returns.
|
|
23
88
|
|
|
24
|
-
|
|
89
|
+
## Example
|
|
25
90
|
|
|
26
|
-
|
|
91
|
+
Given this component:
|
|
27
92
|
|
|
28
93
|
```svelte
|
|
94
|
+
<!-- Button.svelte -->
|
|
29
95
|
<script>
|
|
30
96
|
export let type = "button";
|
|
31
97
|
export let primary = false;
|
|
@@ -36,11 +102,10 @@ From a Svelte component, `sveld` can infer basic prop types and emit definitions
|
|
|
36
102
|
</button>
|
|
37
103
|
```
|
|
38
104
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
**Button.svelte.d.ts**
|
|
105
|
+
sveld infers prop types and defaults, the forwarded `click` event, the default slot, and the rest props spread onto `<button>`:
|
|
42
106
|
|
|
43
107
|
```ts
|
|
108
|
+
// types/Button.svelte.d.ts
|
|
44
109
|
import { SvelteComponentTyped } from "svelte";
|
|
45
110
|
import type { SvelteHTMLElements } from "svelte/elements";
|
|
46
111
|
|
|
@@ -57,6 +122,8 @@ type $Props = {
|
|
|
57
122
|
*/
|
|
58
123
|
primary?: boolean;
|
|
59
124
|
|
|
125
|
+
children?: (this: void) => void;
|
|
126
|
+
|
|
60
127
|
[key: `data-${string}`]: unknown;
|
|
61
128
|
};
|
|
62
129
|
|
|
@@ -69,28 +136,23 @@ export default class Button extends SvelteComponentTyped<
|
|
|
69
136
|
> {}
|
|
70
137
|
```
|
|
71
138
|
|
|
72
|
-
|
|
139
|
+
The default slot is also typed as a `children` snippet prop for Svelte 5 consumers, and the `data-*` index signature lets callers pass data attributes through `$$restProps`.
|
|
73
140
|
|
|
74
|
-
|
|
141
|
+
Add JSDoc to tighten types and add descriptions:
|
|
75
142
|
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
|
|
143
|
+
```svelte
|
|
144
|
+
<script>
|
|
145
|
+
/** @type {"button" | "submit" | "reset"} */
|
|
146
|
+
export let type = "button";
|
|
79
147
|
|
|
80
|
-
/**
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
export let primary = false;
|
|
148
|
+
/**
|
|
149
|
+
* Set to `true` to use the primary variant
|
|
150
|
+
*/
|
|
151
|
+
export let primary = false;
|
|
152
|
+
</script>
|
|
84
153
|
```
|
|
85
154
|
|
|
86
|
-
With JSDoc, the output looks like this:
|
|
87
|
-
|
|
88
155
|
```ts
|
|
89
|
-
import { SvelteComponentTyped } from "svelte";
|
|
90
|
-
import type { SvelteHTMLElements } from "svelte/elements";
|
|
91
|
-
|
|
92
|
-
type $RestProps = SvelteHTMLElements["button"];
|
|
93
|
-
|
|
94
156
|
type $Props = {
|
|
95
157
|
/**
|
|
96
158
|
* @default "button"
|
|
@@ -102,4350 +164,126 @@ type $Props = {
|
|
|
102
164
|
* @default false
|
|
103
165
|
*/
|
|
104
166
|
primary?: boolean;
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
export type ButtonProps = Omit<$RestProps, keyof $Props> & $Props;
|
|
108
|
-
|
|
109
|
-
export default class Button extends SvelteComponentTyped<
|
|
110
|
-
ButtonProps,
|
|
111
|
-
{ click: WindowEventMap["click"] },
|
|
112
|
-
{ default: Record<string, never> }
|
|
113
|
-
> {}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
---
|
|
117
|
-
|
|
118
|
-
## Table of Contents
|
|
119
|
-
|
|
120
|
-
- [When to use sveld](#when-to-use-sveld)
|
|
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
|
-
- [Diagnostic codes](#diagnostic-codes)
|
|
129
|
-
- [Severity and `--strict=errors`](#severity-and---stricterrors)
|
|
130
|
-
- [Ignoring diagnostics](#ignoring-diagnostics)
|
|
131
|
-
- [Requirements](#requirements)
|
|
132
|
-
- [Usage](#usage)
|
|
133
|
-
- [Installation](#installation)
|
|
134
|
-
- [Vite](#vite)
|
|
135
|
-
- [CLI](#cli)
|
|
136
|
-
- [Exit codes](#exit-codes)
|
|
137
|
-
- [CI: API-drift checks (`--check`)](#ci-api-drift-checks---check)
|
|
138
|
-
- [CI: strictness profiles (`--strict=ci`/`--strict=local`)](#ci-strictness-profiles---strictci---strictlocal)
|
|
139
|
-
- [Node.js](#nodejs)
|
|
140
|
-
- [Browser](#browser)
|
|
141
|
-
- [Config File](#config-file)
|
|
142
|
-
- [Publishing to NPM](#publishing-to-npm)
|
|
143
|
-
- [Available Options](#available-options)
|
|
144
|
-
- [Documenting Entry Exports](#documenting-entry-exports)
|
|
145
|
-
- [JSON Output](#json-output)
|
|
146
|
-
- [Custom Elements Manifest](#custom-elements-manifest)
|
|
147
|
-
- [Consuming the manifest](#consuming-the-manifest)
|
|
148
|
-
- [llms.txt Output](#llmstxt-output)
|
|
149
|
-
- [Custom Writers](#custom-writers)
|
|
150
|
-
- [The `OutputWriter` contract](#the-outputwriter-contract)
|
|
151
|
-
- [Registering a writer](#registering-a-writer)
|
|
152
|
-
- [Running it via the plugin](#running-it-via-the-plugin)
|
|
153
|
-
- [Worked example: a `components.txt` name-list writer](#worked-example-a-componentstxt-name-list-writer)
|
|
154
|
-
- [API Reference](#api-reference)
|
|
155
|
-
- [reactive](#reactive)
|
|
156
|
-
- [binding](#binding)
|
|
157
|
-
- [@type](#type)
|
|
158
|
-
- [@default](#default)
|
|
159
|
-
- [@typedef](#typedef)
|
|
160
|
-
- [@property](#property)
|
|
161
|
-
- [@callback](#callback)
|
|
162
|
-
- [@slot / @snippet](#slot--snippet)
|
|
163
|
-
- [Extra JSDoc tags before `@slot`](#extra-jsdoc-tags-before-slot)
|
|
164
|
-
- [Svelte 5 Snippet Compatibility](#svelte-5-snippet-compatibility)
|
|
165
|
-
- [@event](#event)
|
|
166
|
-
- [@ignore / @internal](#ignore--internal)
|
|
167
|
-
- [@deprecated](#deprecated)
|
|
168
|
-
- [@since](#since)
|
|
169
|
-
- [@see](#see)
|
|
170
|
-
- [@link](#link)
|
|
171
|
-
- [@example](#example)
|
|
172
|
-
- [Context API](#context-api)
|
|
173
|
-
- [@restProps](#restprops)
|
|
174
|
-
- [@extendProps](#extendprops)
|
|
175
|
-
- [@template](#template)
|
|
176
|
-
- [@generics](#generics)
|
|
177
|
-
- [@component comments](#component-comments)
|
|
178
|
-
- [Accessor Props](#accessor-props)
|
|
179
|
-
- [Troubleshooting](#troubleshooting)
|
|
180
|
-
- [Contributing](#contributing)
|
|
181
|
-
- [License](#license)
|
|
182
|
-
|
|
183
|
-
## Approach
|
|
184
|
-
|
|
185
|
-
`sveld` statically analyzes exported components and emits docs for consumers. Template parsing runs through sveld's own parser rather than `svelte/compiler`; `svelte/compiler` is imported only for its types, and `svelte/package.json` is read for the installed Svelte version. A differential test (`tests/svelte-template-parse-shim.test.ts`) parses every fixture `.svelte` file with both parsers and asserts the resulting ASTs match, and a weekly `svelte-canary` workflow re-runs that comparison against `svelte@latest` ahead of the lockfile pin.
|
|
186
|
-
|
|
187
|
-
It extracts:
|
|
188
|
-
|
|
189
|
-
- props
|
|
190
|
-
- slots
|
|
191
|
-
- forwarded events
|
|
192
|
-
- dispatched events
|
|
193
|
-
- context (setContext/getContext)
|
|
194
|
-
- `$$restProps`
|
|
195
|
-
|
|
196
|
-
When inference fails, props fall back to `any` rather than guessing wrong. Authors can tighten types with JSDoc. Comments are optional from the compiler's point of view, so plain JavaScript components still parse.
|
|
197
|
-
|
|
198
|
-
When both TypeScript syntax and JSDoc are present, `sveld` resolves prop types in this order:
|
|
199
|
-
|
|
200
|
-
1. explicit TypeScript annotation
|
|
201
|
-
2. explicit JSDoc annotation
|
|
202
|
-
3. initializer inference
|
|
203
|
-
4. `any`
|
|
204
|
-
|
|
205
|
-
`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.
|
|
206
|
-
|
|
207
|
-
## Features
|
|
208
|
-
|
|
209
|
-
### `.d.ts` output format (`typesOptions.format`)
|
|
210
|
-
|
|
211
|
-
`typesOptions.format` controls the shape of generated `.d.ts` files: `"class"` (the default) or `"component"`.
|
|
212
|
-
|
|
213
|
-
`"class"` extends `SvelteComponentTyped`, deprecated in Svelte 5 and plausibly removed in Svelte 6:
|
|
214
|
-
|
|
215
|
-
```ts
|
|
216
|
-
import { SvelteComponentTyped } from "svelte";
|
|
217
|
-
|
|
218
|
-
export type ButtonProps = { label?: string };
|
|
219
|
-
|
|
220
|
-
export default class Button extends SvelteComponentTyped<
|
|
221
|
-
ButtonProps,
|
|
222
|
-
{ click: WindowEventMap["click"] },
|
|
223
|
-
{ default: Record<string, never> }
|
|
224
|
-
> {}
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
`"component"` emits the Svelte 5 `Component` type instead:
|
|
228
|
-
|
|
229
|
-
```ts
|
|
230
|
-
import type { Component } from "svelte";
|
|
231
167
|
|
|
232
|
-
|
|
233
|
-
export type ButtonExports = Record<string, never>;
|
|
234
|
-
|
|
235
|
-
declare const Button: Component<ButtonProps, ButtonExports, "">;
|
|
236
|
-
export default Button;
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
Publish `"component"` if you target Svelte 5+ consumers. `"class"` remains compatible with Svelte 3, 4, and 5.
|
|
168
|
+
children?: (this: void) => void;
|
|
240
169
|
|
|
241
|
-
|
|
242
|
-
|
|
170
|
+
[key: `data-${string}`]: unknown;
|
|
171
|
+
};
|
|
243
172
|
```
|
|
244
173
|
|
|
245
|
-
|
|
174
|
+
The runes version (`let { type = "button", primary = false, children, ...rest } = $props()`, spreading `rest` and rendering `children`) produces the same `$Props`. It has no `click` event, since runes components take `onclick` as a prop through `rest`.
|
|
246
175
|
|
|
247
|
-
`"component"`
|
|
176
|
+
The `.d.ts` extends `SvelteComponentTyped` by default, which works for consumers on Svelte 3, 4, and 5. Set `typesOptions.format: "component"` (`--types-format=component`) to emit the Svelte 5 `Component` type instead. See [Output](docs/output.md#formats).
|
|
248
177
|
|
|
249
|
-
|
|
250
|
-
- **Exports**: the component's accessor props (exported `function`/`const` members), the same members that render as class members under `"class"`.
|
|
251
|
-
- **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."
|
|
178
|
+
### JSON and Markdown
|
|
252
179
|
|
|
253
|
-
|
|
180
|
+
`npx sveld --markdown` documents the same component in `COMPONENT_INDEX.md`:
|
|
254
181
|
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
new <Item extends { id: string | number } = { id: string }>(
|
|
258
|
-
options: ComponentConstructorOptions<GenericListProps<Item>>,
|
|
259
|
-
): SvelteComponent<GenericListProps<Item>> & GenericListExports;
|
|
260
|
-
<Item extends { id: string | number } = { id: string }>(
|
|
261
|
-
this: void,
|
|
262
|
-
internals: ComponentInternals,
|
|
263
|
-
props: GenericListProps<Item>,
|
|
264
|
-
): { $on?(...): () => void; $set?(...): void } & GenericListExports;
|
|
265
|
-
z_$$bindings?: "";
|
|
266
|
-
}
|
|
267
|
-
declare const GenericList: GenericListComponent;
|
|
268
|
-
export default GenericList;
|
|
269
|
-
```
|
|
182
|
+
```md
|
|
183
|
+
## `Button`
|
|
270
184
|
|
|
271
|
-
|
|
185
|
+
### Props
|
|
272
186
|
|
|
273
|
-
|
|
187
|
+
| Prop name | Required | Kind | Reactive | Binding | Type | Default value | Description |
|
|
188
|
+
| :- | :- | :- | :- | :- | :- | :- | :- |
|
|
189
|
+
| type | No | <code>let</code> | No | -- | <code>"button" | "submit" | "reset"</code> | <code>"button"</code> | -- |
|
|
190
|
+
| primary | No | <code>let</code> | No | -- | <code>boolean</code> | <code>false</code> | Set to `true` to use the primary variant |
|
|
274
191
|
|
|
275
|
-
|
|
192
|
+
### Slots
|
|
276
193
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
194
|
+
| Slot name | Default | Props | Fallback | Description |
|
|
195
|
+
| :- | :- | :- | :- | :- |
|
|
196
|
+
| -- | Yes | <code>Record<string, never> </code> | <code>Click me</code> | -- |
|
|
280
197
|
|
|
281
|
-
|
|
282
|
-
<script lang="ts">
|
|
283
|
-
import type { Props } from "./types";
|
|
198
|
+
### Events
|
|
284
199
|
|
|
285
|
-
|
|
286
|
-
|
|
200
|
+
| Event name | Type | Detail | Description |
|
|
201
|
+
| :- | :- | :- | :- |
|
|
202
|
+
| click | forwarded | -- | -- |
|
|
287
203
|
```
|
|
288
204
|
|
|
289
|
-
|
|
205
|
+
`npx sveld --json` writes the same data to `COMPONENT_API.json`, validated by a [JSON Schema](schema/component-api.schema.json) that ships with the package. An excerpt of the prop entry:
|
|
290
206
|
|
|
291
|
-
```
|
|
207
|
+
```json
|
|
292
208
|
{
|
|
293
|
-
"
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
209
|
+
"name": "type",
|
|
210
|
+
"kind": "let",
|
|
211
|
+
"type": "\"button\" | \"submit\" | \"reset\"",
|
|
212
|
+
"typeSource": "jsdoc",
|
|
213
|
+
"value": "\"button\"",
|
|
214
|
+
"isRequired": false,
|
|
215
|
+
"reactive": false
|
|
298
216
|
}
|
|
299
217
|
```
|
|
300
218
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
### Persistent parse cache (`cache`)
|
|
304
|
-
|
|
305
|
-
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. Generated `.d.ts` text is cached the same way, but is only reused when the component source _and_ every `typesOptions` value that affects output (for example [`format`](#dts-output-format-typesoptionsformat)) are unchanged; changing any of them regenerates that component's `.d.ts` without invalidating its cached parse.
|
|
306
|
-
|
|
307
|
-
```ts
|
|
308
|
-
await sveld({ json: true });
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
By default this writes to `node_modules/.cache/sveld/parse-cache.json` in the project root, the nearest directory above the entry that has a `package.json`. Pass a string to use a different location, e.g. `cache: ".cache/sveld.json"` (relative paths resolve against the same project root), or `cache: false` to disable it. Also available as `--cache` / `--cache=<path>` / `--cache=false` on the CLI.
|
|
312
|
-
|
|
313
|
-
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.
|
|
314
|
-
|
|
315
|
-
### Compile-checked `@example` blocks (`checkExamples`)
|
|
316
|
-
|
|
317
|
-
`@example` blocks are just text. Rename a prop and the sample code can sit there broken for months. Set `checkExamples: true` to check them: plain TS/JS bodies run through the TypeScript program, and `svelte`/`html` bodies run through sveld's own template parser. Broken examples show up as `example-compile-error` (TS/JS) or `example-syntax-error` (markup) diagnostics.
|
|
318
|
-
|
|
319
|
-
```ts
|
|
320
|
-
await sveld({ json: true, checkExamples: true });
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
```svelte
|
|
324
|
-
<script>
|
|
325
|
-
/**
|
|
326
|
-
* Formats a value.
|
|
327
|
-
* @param {string} value
|
|
328
|
-
* @returns {string}
|
|
329
|
-
* @example
|
|
330
|
-
* ```js
|
|
331
|
-
* formatValue("ok");
|
|
332
|
-
* ```
|
|
333
|
-
*/
|
|
334
|
-
export function formatValue(value) {
|
|
335
|
-
return value;
|
|
336
|
-
}
|
|
337
|
-
</script>
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
If `formatValue` is later renamed and the example is never updated, `checkExamples` reports it:
|
|
341
|
-
|
|
342
|
-
```
|
|
343
|
-
@example blocks that failed to compile (1):
|
|
344
|
-
./Component.svelte
|
|
345
|
-
- Line 1: Cannot find name 'formatValue'.
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
A `svelte`/`html`-fenced example is syntax-checked, not type-checked: sveld parses the markup and discards the AST, so it catches malformed markup (a mismatched closing tag, an unterminated attribute) but not a prop that doesn't exist or a type error inside an expression. Those still need `svelte-check` in the consumer's own tests. Bare unfenced markup (`<Button />` with no code fence) is skipped either way.
|
|
349
|
-
|
|
350
|
-
```
|
|
351
|
-
@example blocks that failed to parse (1):
|
|
352
|
-
./Component.svelte
|
|
353
|
-
- Line 1: sveld: invalid closing tag </span>.
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
The TS/JS check is narrow on purpose too. It catches renamed or removed symbols and wrong argument counts. Neither path is full type checking, and neither pulls in types sveld cannot see.
|
|
357
|
-
|
|
358
|
-
The TS/JS path needs `typescript` 7+ and a `tsconfig.json`, same as `resolveTypes` (see [Requirements](#requirements)); missing either fails the run rather than silently skipping every example. The markup path needs neither: pass `checkExamples: "syntax"` to run only it, so a project with only `svelte`/`html` examples (or no `tsconfig.json`) never loads TypeScript. Use `--strict` (or the `strict` option) to fail CI when an example breaks.
|
|
359
|
-
|
|
360
|
-
### Type inference diagnostics
|
|
361
|
-
|
|
362
|
-
`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`/`example-syntax-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.
|
|
363
|
-
|
|
364
|
-
A prop with no type annotation, no `@type` JSDoc, and no initializer has nothing to infer a type or a default from: its JSON `type` stays absent (`"typeSource": "unknown"`), it triggers a `prop-unknown-type` diagnostic, and the emitted `.d.ts` types it as `any` with no `@default` line (rather than the literal type `undefined`).
|
|
365
|
-
|
|
366
|
-
With `reportDiagnostics` or `strict`, the grouped summary looks like this:
|
|
367
|
-
|
|
368
|
-
```
|
|
369
|
-
sveld: 5 diagnostics (1 error, 4 warnings).
|
|
370
|
-
|
|
371
|
-
Props without inferred types (1):
|
|
372
|
-
./icons/Add.svelte
|
|
373
|
-
- Prop "title" type could not be inferred; falling back to "any". (./icons/Add.svelte:4:2) [sveld/prop-unknown-type]
|
|
219
|
+
See [Output](docs/output.md#json-output) for the full shape.
|
|
374
220
|
|
|
375
|
-
|
|
376
|
-
./ThemeProvider.svelte
|
|
377
|
-
- Context "theme" variable "themeStore" has no type annotation; defaulted to "any". (./ThemeProvider.svelte:8:6) [sveld/context-any-type]
|
|
221
|
+
## In CI
|
|
378
222
|
|
|
379
|
-
|
|
380
|
-
./Modal.svelte
|
|
381
|
-
- @event "open" has no matching dispatch or callback prop. (./Modal.svelte:3:5) [sveld/event-no-source]
|
|
382
|
-
- @event "close" has no matching dispatch or callback prop. (./Modal.svelte:4:5) [sveld/event-no-source]
|
|
383
|
-
|
|
384
|
-
Component syntax sveld skipped (1):
|
|
385
|
-
./List.svelte
|
|
386
|
-
- Both the "generics" script attribute and @generics/@template JSDoc tags declare component generics; the script attribute takes precedence and the JSDoc declaration was ignored. (./List.svelte:1:1) [sveld/syntax-skipped]
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
When `checkExamples` is also enabled, `@example` failures appear as additional groups: TS/JS failures under `example-compile-error`, `svelte`/`html` failures under `example-syntax-error`.
|
|
390
|
-
|
|
391
|
-
```
|
|
392
|
-
@example blocks that failed to compile (1):
|
|
393
|
-
./Component.svelte
|
|
394
|
-
- Line 1: Cannot find name 'formatValue'. [sveld/example-compile-error]
|
|
395
|
-
|
|
396
|
-
@example blocks that failed to parse (1):
|
|
397
|
-
./Component.svelte
|
|
398
|
-
- Line 1: sveld: invalid closing tag </span>. [sveld/example-syntax-error]
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
By default, nothing is printed. Opt in when you are working on types or want CI output:
|
|
402
|
-
|
|
403
|
-
```ts
|
|
404
|
-
await sveld({ json: true, reportDiagnostics: true });
|
|
405
|
-
```
|
|
406
|
-
|
|
407
|
-
Use `strict: true` (or `--strict`) to exit with code `4` when diagnostics exist. `strict` implies `reportDiagnostics`, so CI always shows why the run failed.
|
|
408
|
-
|
|
409
|
-
```ts
|
|
410
|
-
await sveld({ json: true, strict: true });
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
CLI equivalent:
|
|
223
|
+
Commit `COMPONENT_API.json`, then gate pull requests on API changes and inference gaps:
|
|
414
224
|
|
|
415
225
|
```sh
|
|
416
|
-
npx sveld --
|
|
417
|
-
npx sveld --
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
`--check` is separate: it diffs `COMPONENT_API.json` for API drift and semver classification, not inference warnings.
|
|
421
|
-
|
|
422
|
-
#### Diagnostic codes
|
|
423
|
-
|
|
424
|
-
Every diagnostic carries a stable, namespaced `code` (`"sveld/<kind>"`) alongside the older `kind`, so CI config and `diagnostics.ignore` matchers (below) have something that won't shift if the human-readable `message` text changes:
|
|
425
|
-
|
|
426
|
-
| Code | Severity | Fix |
|
|
427
|
-
| --- | --- | --- |
|
|
428
|
-
| `sveld/prop-unknown-type` | `warning` | Add a native TypeScript annotation, a `@type` JSDoc tag, or an initializer sveld can infer a type from. |
|
|
429
|
-
| `sveld/context-any-type` | `warning` | Annotate the `setContext` value's declaration with `@type` or a native TypeScript type. |
|
|
430
|
-
| `sveld/slot-missing-type` | `warning` | Add the required `{Type}` annotation to the `@slot`/`@snippet` tag (e.g. `@slot {{}} name`); until then it falls back to `Record<string, never>`. |
|
|
431
|
-
| `sveld/event-no-source` | `warning` | Dispatch the event (`createEventDispatcher`/`dispatch`), forward it (`on:name`), or add a matching `on<Name>` callback prop; otherwise remove the stale `@event` tag. |
|
|
432
|
-
| `sveld/dispatch-escapes` | `warning` | The `createEventDispatcher()` result is passed to a function sveld can't follow: one that isn't imported from a local module, names an event with something other than a string literal, or passes the dispatcher on. Document the events it dispatches with `@event` tags. Add `@sveld-ignore sveld/dispatch-escapes` to the dispatcher's JSDoc once they're covered. |
|
|
433
|
-
| `sveld/example-compile-error` | `error` | Fix the `@example` TS/JS code block so it type-checks, or remove the broken example. |
|
|
434
|
-
| `sveld/example-syntax-error` | `error` | Fix the `@example` `svelte`/`html` markup so it parses, or remove the broken example. |
|
|
435
|
-
| `sveld/syntax-skipped` | `error` | Rewrite the flagged syntax in a form sveld can model (see the diagnostic's `message` for what was skipped). |
|
|
436
|
-
| `sveld/rest-props-unresolved` | `warning` | Spread `$$restProps` onto a plain element (or `svelte:element`) instead of a component, or add an `@restProps` tag to type it manually. |
|
|
437
|
-
| `sveld/context-duplicate-key` | `warning` | Remove the duplicate `setContext` call, or give it a distinct key; only the first call's shape is used. |
|
|
438
|
-
| `sveld/context-key-unresolved` | `warning` | Use a string literal, a `const`-bound string, `Symbol()`, or a string or `Symbol()` `export const` imported from a relative module as the `setContext` key; otherwise the context is left out of every output. |
|
|
439
|
-
| `sveld/context-value-unresolved` | `warning` | Pass an object literal or a typed variable as the `setContext` value (e.g. `const store = writable(0);` with a `@type` annotation, or `{ store }`); a call or other expression has no shape sveld can describe, so the context is left out of every output. |
|
|
440
|
-
| `sveld/spread-unresolved` | `warning` | Spread a local object literal or a variable with a resolvable type instead; otherwise the spread widens the generated type to `Record<string, any>`. |
|
|
441
|
-
| `sveld/export-unresolved` | `warning` | Export a local declaration directly. Instance-script exports are props, so move a re-export (`export { x } from "..."`, or `export { x }` of an import) into `<script context="module">`, where sveld writes it to the `.d.ts` as-is. A class can't be a prop either, so export it from `<script context="module">`, where sveld documents it. |
|
|
442
|
-
| `sveld/module-export-conflict` | `warning` | Rename the module-script export. `default` is always skipped (it collides with the component itself); a name matching the generated `<Name>Props`/`<Name>Exports` type breaks the `.d.ts` if the export carries a type. |
|
|
443
|
-
| `sveld/extend-props-target-missing` | `error` | Point `@extends`/`@extendProps` at a file that exists, and (for a bundled `.svelte` target) name its generated `<Name>Props` interface exactly. |
|
|
444
|
-
| `sveld/extend-props-duplicate` | `warning` | Remove the extra `@extends`/`@extendProps` tag; only the last one is used. |
|
|
445
|
-
| `sveld/extend-props-override` | `warning` | Rename the own prop, or accept that it intentionally overrides the `@extends` target's prop of the same name. |
|
|
446
|
-
| `sveld/jsdoc-unknown-tag` | `warning` | Fix the tag name if it's a typo (e.g. `@depreacted` → `@deprecated`); otherwise no action needed, the tag still passes through unchanged. Only surfaced under `--strict`/`--report-diagnostics`. |
|
|
447
|
-
| `sveld/typedef-duplicate` | `warning` | Rename one of the `@typedef`/`@callback` declarations; only the later one is kept. |
|
|
448
|
-
| `sveld/property-duplicate` | `warning` | Remove the duplicate `@property`; only the later one is kept. |
|
|
449
|
-
| `sveld/generics-conflict` | `warning` | Rename one of the `@generics`/`@template` declarations to a distinct generic name. |
|
|
450
|
-
| `sveld/event-description-ambiguous` | `warning` | Put an event's description above its `@event` tag (or indent it as a continuation of the tag's line), or give each event its own comment block. Unindented text after an `@event` is read as the description of the tag below it. |
|
|
451
|
-
| `sveld/jsdoc-tag-dropped` | `warning` | Move the tag next to a `@slot`/`@snippet`/`@event`/`@typedef`/`@callback` tag in the same comment block so it has something to attach to. |
|
|
452
|
-
| `sveld/internal-typedef-referenced` | `error` | Remove `@internal`/`@ignore` from the referenced typedef, or stop referencing it from public type text (inline the shape, or make the referencing item `@internal` too). |
|
|
453
|
-
| `sveld/types-inline-unresolved` | `warning` | The import is kept as-is. Point it at a relative `.ts` file that exports a `type`/`interface`, or rename the colliding type. |
|
|
454
|
-
| `sveld/cross-file-unresolved` | `warning` | Only recorded by [`finalizeWithoutCrossFileResolution`](#browser) on a standalone parse: an imported `setContext` key, prop default, or dispatch helper needs the imported file read. Run `sveld` through the CLI or `generateBundle` to resolve it, or inline the value in the component. |
|
|
455
|
-
| `sveld/export-ambiguous` | `warning` | Two `export *` statements in the entry barrel bring in the same name from different modules, so (as in ES modules) the barrel doesn't export it and the [entry exports](#documenting-entry-exports) leave it out. Export it explicitly from the barrel (`export { format } from "./a.js"`) to pick one. Reported with the barrel file (e.g. `./index.js`) as its `component`, and only when `documentExports` is on. |
|
|
456
|
-
|
|
457
|
-
#### Severity and `--strict=errors`
|
|
458
|
-
|
|
459
|
-
Each diagnostic's `severity` is `"error"` (`example-compile-error`, `example-syntax-error`, `syntax-skipped`, `extend-props-target-missing`, `internal-typedef-referenced` — sveld emitted broken or unmodeled output) or `"warning"` (`prop-unknown-type`, `context-any-type`, `slot-missing-type`, `event-no-source`, `dispatch-escapes`, `rest-props-unresolved`, `context-duplicate-key`, `context-key-unresolved`, `context-value-unresolved`, `spread-unresolved`, `export-unresolved`, `module-export-conflict`, `extend-props-duplicate`, `extend-props-override`, `jsdoc-unknown-tag`, `typedef-duplicate`, `property-duplicate`, `generics-conflict`, `event-description-ambiguous`, `jsdoc-tag-dropped`, `types-inline-unresolved`, `cross-file-unresolved`, `export-ambiguous` — a type fell back to `any`, or something was left out of the docs). Plain `strict: true` / `--strict` fails on both, unchanged from before. Pass `strict: "errors"` (or `--strict=errors`) to fail CI only on `error`-severity diagnostics, letting `any`-fallback warnings through:
|
|
460
|
-
|
|
461
|
-
```sh
|
|
462
|
-
npx sveld --json --strict=errors
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
```ts
|
|
466
|
-
await sveld({ json: true, strict: "errors" });
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
#### Ignoring diagnostics
|
|
470
|
-
|
|
471
|
-
Two ways to suppress a diagnostic without disabling `strict` for the whole run. Either way, the diagnostic still appears in `SveldResult.diagnostics` (with `ignored: true`) and is still counted in the text summary (`sveld: 2 diagnostics (0 errors, 2 warnings) (1 ignored).`), but never fails `--strict` / `--strict=errors`.
|
|
472
|
-
|
|
473
|
-
**Config matchers** (`diagnostics.ignore`, an array of `{ code?, component?, name? }`): every field you set on a matcher must match for it to apply; an omitted field matches anything. `component` is a glob (`*` within a path segment, `**` across segments):
|
|
474
|
-
|
|
475
|
-
```ts
|
|
476
|
-
// sveld.config.js
|
|
477
|
-
export default defineConfig({
|
|
478
|
-
diagnostics: {
|
|
479
|
-
ignore: [
|
|
480
|
-
// Every prop-unknown-type diagnostic under legacy/.
|
|
481
|
-
{ code: "sveld/prop-unknown-type", component: "./legacy/**" },
|
|
482
|
-
// One named symbol, anywhere.
|
|
483
|
-
{ name: "internalOnly" },
|
|
484
|
-
],
|
|
485
|
-
},
|
|
486
|
-
});
|
|
226
|
+
npx sveld --check # exit 3 on a breaking API change against the snapshot
|
|
227
|
+
npx sveld --strict # exit 4 when a prop fell back to `any`, a tag was dropped, ...
|
|
487
228
|
```
|
|
488
229
|
|
|
489
|
-
**Inline `@sveld-ignore <code>`**, on the same JSDoc comment as the prop, `@event` tag, context variable, or event dispatcher it applies to:
|
|
490
|
-
|
|
491
|
-
```svelte
|
|
492
|
-
<script>
|
|
493
|
-
/**
|
|
494
|
-
* @sveld-ignore sveld/prop-unknown-type
|
|
495
|
-
*/
|
|
496
|
-
export let value;
|
|
497
|
-
</script>
|
|
498
230
|
```
|
|
231
|
+
sveld --check: 2 API changes detected against "COMPONENT_API.json".
|
|
232
|
+
Suggested semver bump: major.
|
|
499
233
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
* @event {CustomEvent<null>} legacyEvent
|
|
504
|
-
* @sveld-ignore sveld/event-no-source
|
|
505
|
-
*/
|
|
506
|
-
export let label;
|
|
507
|
-
</script>
|
|
234
|
+
Button
|
|
235
|
+
[BREAKING] prop "target" added (required)
|
|
236
|
+
[BREAKING] prop "href" removed
|
|
508
237
|
```
|
|
509
238
|
|
|
510
|
-
|
|
239
|
+
See [CI](docs/ci.md) for exit codes, the change classification table, diagnostic codes, and how to ignore a diagnostic.
|
|
240
|
+
|
|
241
|
+
## JSDoc tags
|
|
242
|
+
|
|
243
|
+
| Tag | Use it to | Example |
|
|
244
|
+
| :- | :- | :- |
|
|
245
|
+
| [`@type`](docs/jsdoc-tags.md#type) | Type a prop | `@type {"sm" \| "md" \| "lg"}` |
|
|
246
|
+
| [`@default`](docs/jsdoc-tags.md#default) | Override the inferred default shown in docs | `@default () => true` |
|
|
247
|
+
| [`@typedef`](docs/jsdoc-tags.md#typedef) | Declare a shared, exported type | `@typedef {{ id: string }} Item` |
|
|
248
|
+
| [`@property`](docs/jsdoc-tags.md#property) | Document a field of a `@typedef {object}` or event detail | `@property {string} [label] - Label text` |
|
|
249
|
+
| [`@callback`](docs/jsdoc-tags.md#callback) | Declare a function type | `@callback OnChange` + `@param`/`@returns` |
|
|
250
|
+
| [`@slot` / `@snippet`](docs/jsdoc-tags.md#slot--snippet) | Type slot or snippet props | `@slot {{ item: Item }} row` |
|
|
251
|
+
| [`@event`](docs/jsdoc-tags.md#event) | Type a dispatched event's detail | `@event {{ id: string }} save` |
|
|
252
|
+
| [`@param` / `@returns`](docs/jsdoc-tags.md#accessor-props) | Type an exported function | `@param {string} id` |
|
|
253
|
+
| [`@restProps`](docs/jsdoc-tags.md#restprops) | Name the element rest props are spread onto | `@restProps {button \| a}` |
|
|
254
|
+
| [`@extendProps`](docs/jsdoc-tags.md#extendprops) | Extend another component's props | `@extendProps {"./Button.svelte"} ButtonProps` |
|
|
255
|
+
| [`@template` / `@generics`](docs/jsdoc-tags.md#template--generics) | Declare generics in a JS component | `@template {Item} [T=Item]` |
|
|
256
|
+
| [`@bindable`](docs/jsdoc-tags.md#bindable) | Document a prop's `bind:` contract | `@bindable writable` |
|
|
257
|
+
| [`@deprecated`](docs/jsdoc-tags.md#deprecated) | Mark a prop, event, slot, or export deprecated | `@deprecated Use "size" instead.` |
|
|
258
|
+
| [`@ignore` / `@internal`](docs/jsdoc-tags.md#ignore--internal) | Hide from every output | `@internal` |
|
|
259
|
+
| [`@since` / `@see` / `@example`](docs/jsdoc-tags.md#since-see-example) | Pass structured tags through to all outputs | `@since 1.2.0` |
|
|
260
|
+
| [`{@link}`](docs/jsdoc-tags.md#link) | Link in a description | `{@link https://example.com\|docs}` |
|
|
261
|
+
| [`@csspart` / `@cssprop`](docs/jsdoc-tags.md#csspart--cssprop) | Document shadow-DOM styling hooks | `@cssprop {Color} [--bg=white]` |
|
|
262
|
+
| [`@sveld-ignore`](docs/ci.md#ignoring-diagnostics) | Suppress a diagnostic | `@sveld-ignore sveld/prop-unknown-type` |
|
|
263
|
+
| [`<!-- @component -->`](docs/jsdoc-tags.md#component-comments) | Document the component itself | `<!-- @component Renders a button. -->` |
|
|
264
|
+
|
|
265
|
+
Context types come from `setContext` calls with no tag needed. See [Context](docs/jsdoc-tags.md#context).
|
|
266
|
+
|
|
267
|
+
## Docs
|
|
268
|
+
|
|
269
|
+
- [Options](docs/options.md): every option and CLI flag, the config file, the Vite plugin, `sveld()` and its result, and `sveld/browser`.
|
|
270
|
+
- [JSDoc tags](docs/jsdoc-tags.md): how types are inferred and every tag sveld reads.
|
|
271
|
+
- [Output](docs/output.md): `.d.ts` formats, JSON output and schema, Markdown, entry exports, custom element metadata, and custom output formats.
|
|
272
|
+
- [CI](docs/ci.md): exit codes, `--check` for API drift, `--strict` and diagnostic codes, and `@example` checking.
|
|
273
|
+
- [Troubleshooting](docs/troubleshooting.md)
|
|
274
|
+
|
|
275
|
+
Try sveld in the browser at [sveld.onrender.com](https://sveld.onrender.com) (source in [`playground/`](playground)).
|
|
511
276
|
|
|
512
277
|
## Requirements
|
|
513
278
|
|
|
514
|
-
- Node 22
|
|
515
|
-
- `sveld`
|
|
516
|
-
- The [
|
|
517
|
-
-
|
|
518
|
-
- [`resolveTypes`](#opt-in-semantic-resolution-resolvetypes), [`checkExamples`](#compile-checked-example-blocks-checkexamples), and [`typesOptions.inline: "all"`](#typesoptionsinline) are optional and need `typescript` 7 or later (which provides `typescript/unstable/async`) plus a `tsconfig.json`. Everything else, including `.d.ts` generation and `typesOptions.inline: "local"`, is AST-only and never loads TypeScript. If any of the three is enabled and TypeScript can't be started (missing, too old, or no `tsconfig.json`), the run fails loudly: `sveld()` throws and the CLI exits `2` naming the requirement, rather than silently skipping the check. For `typesOptions.inline: "all"` this only actually triggers when there's a non-allow-listed bare import somewhere in the bundle to resolve; see [`typesOptions.inline`](#typesoptionsinline).
|
|
519
|
-
|
|
520
|
-
## Usage
|
|
521
|
-
|
|
522
|
-
### Installation
|
|
523
|
-
|
|
524
|
-
Install `sveld` as a development dependency.
|
|
525
|
-
|
|
526
|
-
```sh
|
|
527
|
-
# npm
|
|
528
|
-
npm i -D sveld
|
|
529
|
-
|
|
530
|
-
# pnpm
|
|
531
|
-
pnpm i -D sveld
|
|
532
|
-
|
|
533
|
-
# Bun
|
|
534
|
-
bun i -D sveld
|
|
535
|
-
|
|
536
|
-
# Yarn
|
|
537
|
-
yarn add -D sveld
|
|
538
|
-
```
|
|
539
|
-
|
|
540
|
-
### Vite
|
|
541
|
-
|
|
542
|
-
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`.
|
|
543
|
-
|
|
544
|
-
```ts
|
|
545
|
-
// vite.config.ts
|
|
546
|
-
import { svelte } from "@sveltejs/vite-plugin-svelte";
|
|
547
|
-
import sveld from "sveld";
|
|
548
|
-
import { defineConfig } from "vite";
|
|
549
|
-
|
|
550
|
-
export default defineConfig({
|
|
551
|
-
plugins: [svelte(), sveld()],
|
|
552
|
-
});
|
|
553
|
-
```
|
|
554
|
-
|
|
555
|
-
Since Vite uses Rollup for production builds, the same plugin works in Rollup configs.
|
|
556
|
-
|
|
557
|
-
Unlike the CLI and the programmatic `sveld()` API, the plugin ignores `sveld.config.*` by default: pass `config: true` to load it and merge it with the options given here (these take precedence over the same key in the file), or a string to point at a specific config file.
|
|
558
|
-
|
|
559
|
-
```ts
|
|
560
|
-
sveld({
|
|
561
|
-
config: true,
|
|
562
|
-
json: true, // wins over `json` set in sveld.config.*
|
|
563
|
-
});
|
|
564
|
-
```
|
|
565
|
-
|
|
566
|
-
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:
|
|
567
|
-
|
|
568
|
-
```js
|
|
569
|
-
sveld({
|
|
570
|
-
entry: "src/index.js",
|
|
571
|
-
});
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
When building the library, TypeScript definitions are emitted to the `types` folder by default.
|
|
575
|
-
|
|
576
|
-
Customize the output folder using the `typesOptions.outDir` option.
|
|
577
|
-
|
|
578
|
-
The following example emits the output to the `dist` folder:
|
|
579
|
-
|
|
580
|
-
```diff
|
|
581
|
-
sveld({
|
|
582
|
-
+ typesOptions: {
|
|
583
|
-
+ outDir: 'dist',
|
|
584
|
-
+ }
|
|
585
|
-
})
|
|
586
|
-
```
|
|
587
|
-
|
|
588
|
-
### CLI
|
|
589
|
-
|
|
590
|
-
The CLI uses the `"svelte"` field from your `package.json` as the entry point:
|
|
591
|
-
|
|
592
|
-
```sh
|
|
593
|
-
npx sveld
|
|
594
|
-
```
|
|
595
|
-
|
|
596
|
-
Generate documentation in JSON and/or Markdown formats using the following flags:
|
|
597
|
-
|
|
598
|
-
```sh
|
|
599
|
-
npx sveld --json --markdown
|
|
600
|
-
```
|
|
601
|
-
|
|
602
|
-
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.
|
|
603
|
-
|
|
604
|
-
Flags are kebab-case: `--entry`, `--glob`, `--types`, `--json`, `--markdown`, `--custom-elements`, `--llms`, `--fail-fast`, `--dry-run`, `--cache`, `--resolve-types`, `--check-examples`, `--report-diagnostics`, `--strict`, `--check`, `--check-level`, `--types-format`, `--types-export`, `--types-comments`, `--types-inline`, `--types-index-types`, `--types-props-declaration`, `--quiet`, `--stdout`, `--format`. The camelCase spellings `--resolveTypes` and `--checkExamples` still work as deprecated aliases for compatibility with existing scripts. `--entry`, `--cache`, `--check`, `--types-format`, `--types-export`, `--types-comments`, `--types-inline`, and `--types-props-declaration` take their value either as `--flag=value` or as a separate `--flag value` argument (`sveld --entry src/index.js` and `sveld --entry=src/index.js` are equivalent); if the next argument starts with `--` it's not consumed as a value, so `--cache` and `--check` fall back to their default location and the rest of that list report a usage error naming the flag. Boolean flags (`--json`, `--glob`, `--strict`, `--types-index-types`, and the like) never consume a following argument. An unrecognized flag (e.g. `--markdwon`) prints `sveld: unknown flag "--markdwon".` to `stderr`, exits `1`, and skips generation; when a close match exists it appends a suggestion, e.g. `Did you mean "--markdown"?`. sveld takes no positional arguments, so any non-flag argument errors the same way.
|
|
605
|
-
|
|
606
|
-
Writer progress lines (`created "..."` / `unchanged "..."`) print to `stderr`, keeping `stdout` reserved for machine-readable data. Pass `--quiet` (or `quiet: true` in `sveld.config.*`) to suppress them; it does not suppress error messages, the diagnostics summary (`--report-diagnostics` / `--strict`), or the `--check` report.
|
|
607
|
-
|
|
608
|
-
Pass `--dry-run` to resolve the entry, load config, and parse components through the real pipeline (so parse errors, diagnostics, `--strict`, and `--check` all behave as in a real run), then print `would write "<path>"` to `stdout` for each output file instead of writing it, e.g. `sveld --json --markdown --dry-run`. Nothing is written, including the parse cache. `--check` never writes regardless of `--dry-run`, since it only diffs against the committed snapshot.
|
|
609
|
-
|
|
610
|
-
Pass `--stdout` alongside exactly one of `--json`, `--markdown`, or `--custom-elements` to print that single document to `stdout` instead of writing it to disk, e.g. `sveld --json --stdout | jq '.components[].moduleName'`. Zero or more than one of those three flags, `--types`, or `--check` combined with `--stdout` is a usage error that prints to `stderr` and exits `1` without generating anything; the default `.d.ts` generation is skipped in `--stdout` mode since type definitions span multiple files.
|
|
611
|
-
|
|
612
|
-
`--stdout=ndjson` (only valid with `--json`) prints one minified JSON object per exported component per line instead of the single combined document, in the same order as the combined document's `components` array, e.g. `sveld --json --stdout=ndjson | jq -c 'select(.props | length > 0)'`. Lines are only written after the run completes (component records are only final after cross-component resolution), so this is a line-oriented serialization format, not incremental streaming. Bare `--stdout` (or `--stdout=json`) keeps the single-document behavior; any other value is a usage error.
|
|
613
|
-
|
|
614
|
-
`--format=json` switches the `--check` report and the `--report-diagnostics` / `--strict` diagnostics summary from prose to JSON, so scripts and agents don't have to regex the text output, e.g. `sveld --json --check --format=json | jq '.bump'`. Channels are unchanged: the check report prints to `stdout` and the diagnostics summary to `stderr`, same as the text format. Each envelope (`{ kind: "check-report", schemaVersion: 1, ... }` / `{ kind: "diagnostics", schemaVersion: 1, diagnostics: [...] }`) carries its own `schemaVersion`, bumped independently of `COMPONENT_API.json`'s if either shape ever changes incompatibly. The default remains `--format=text`; an unrecognized value (e.g. `--format=yaml`) is a usage error that prints to `stderr` and exits `1` without generating anything.
|
|
615
|
-
|
|
616
|
-
`--format=github` prints [GitHub Actions workflow commands](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions) instead: `::warning`/`::error` for each diagnostic (by `severity`) and `::error` for each `--check` change that meets `--check-level` (`major` by default), annotating the offending line directly in the PR's "Files changed" tab. Ignored diagnostics and changes below `--check-level` produce no annotation. When the `GITHUB_STEP_SUMMARY` env var is set (GitHub Actions sets it automatically), sveld also appends a Markdown table mirroring the same rows to that file, so the job summary shows them even for someone not reviewing the diff. A minimal workflow step:
|
|
617
|
-
|
|
618
|
-
```yaml
|
|
619
|
-
- run: npx sveld --json --check --report-diagnostics --strict=errors --format=github
|
|
620
|
-
```
|
|
621
|
-
|
|
622
|
-
Run `npx sveld --help` for the full flag list with descriptions, or `npx sveld --version` to print the installed version.
|
|
623
|
-
|
|
624
|
-
Pass `--glob` with a directory as `--entry` (no barrel file) to document every `.svelte` file under it directly, with no re-export needed: each component's sanitized filename becomes its module name in JSON, Markdown, and the generated `index.d.ts`, in addition to the per-component `.d.ts` every `--glob` run already produces. This is the same set `mergeGlobbedComponents` discovers when `--glob` is combined with a file entry; a directory entry just has no barrel to layer it onto.
|
|
625
|
-
|
|
626
|
-
### Exit codes
|
|
627
|
-
|
|
628
|
-
| Code | Meaning |
|
|
629
|
-
|------|---------|
|
|
630
|
-
| `0` | Success |
|
|
631
|
-
| `1` | Usage or configuration error (unknown flag, bad flag value, unresolvable entry, unresolved re-export or path alias) |
|
|
632
|
-
| `2` | Generation failure (a component failed to parse under `--fail-fast`, or an unrecoverable pipeline error) |
|
|
633
|
-
| `3` | Breaking API change detected by `--check` |
|
|
634
|
-
| `4` | Diagnostics present under `--strict` |
|
|
635
|
-
|
|
636
|
-
Every failure is still reported to its usual channel even when more than one applies in a single run (e.g. a breaking change under `--check` alongside diagnostics under `--strict`); the process exits with the lowest applicable code. All failure codes are nonzero, so existing `if sveld; then ...` and `set -e` scripts that only check for success keep working unmodified.
|
|
637
|
-
|
|
638
|
-
### CI: API-drift checks (`--check`)
|
|
639
|
-
|
|
640
|
-
`--check` diffs the parsed component API against a committed `COMPONENT_API.json` snapshot, assigns a semver bump to each change, and exits `3` when it finds a breaking change.
|
|
641
|
-
|
|
642
|
-
1. Generate and commit the snapshot once: `npx sveld --json`, then commit `COMPONENT_API.json`.
|
|
643
|
-
2. Add `npx sveld --check` to CI:
|
|
644
|
-
|
|
645
|
-
```sh
|
|
646
|
-
npx sveld --check
|
|
647
|
-
```
|
|
648
|
-
|
|
649
|
-
```
|
|
650
|
-
sveld --check: 2 API changes detected against "COMPONENT_API.json".
|
|
651
|
-
Suggested semver bump: major.
|
|
652
|
-
|
|
653
|
-
Button
|
|
654
|
-
[BREAKING] prop "target" added (required)
|
|
655
|
-
[BREAKING] prop "href" removed
|
|
656
|
-
```
|
|
657
|
-
|
|
658
|
-
| Change | Bump |
|
|
659
|
-
| --- | --- |
|
|
660
|
-
| Component added | `minor` |
|
|
661
|
-
| Component removed | `major` |
|
|
662
|
-
| Prop/export added (optional) | `minor` |
|
|
663
|
-
| Prop/export added (required) | `major` |
|
|
664
|
-
| Prop/export removed | `major` |
|
|
665
|
-
| Prop/export became required | `major` |
|
|
666
|
-
| Prop/export became optional | `minor` |
|
|
667
|
-
| Prop/export type widened (union gained a member) | `minor` |
|
|
668
|
-
| Prop/export type narrowed (union lost a member) | `major` |
|
|
669
|
-
| Prop/export type changed (anything else) | `major` |
|
|
670
|
-
| Function-typed prop/export gained a trailing optional param | `minor` |
|
|
671
|
-
| Function-typed prop/export lost a param, or return type changed | `major` |
|
|
672
|
-
| Prop gained a writable binding (`bind:`-able) | `minor` |
|
|
673
|
-
| Prop lost a writable binding | `major` |
|
|
674
|
-
| Prop/export default value changed (type unchanged) | `patch` |
|
|
675
|
-
| `@deprecated` added | `minor` |
|
|
676
|
-
| `@deprecated` removed | `patch` |
|
|
677
|
-
| `constant`/`reactive` flag flipped | `minor` |
|
|
678
|
-
| Event/slot added | `minor` |
|
|
679
|
-
| Event/slot removed | `major` |
|
|
680
|
-
| Event detail / slot props type widened | `minor` |
|
|
681
|
-
| Event detail / slot props type narrowed or otherwise changed | `major` |
|
|
682
|
-
| `generics`, `@restProps`, `@extends`, or context shape changed | `major` (not classified further) |
|
|
683
|
-
| Description-only change | not reported |
|
|
684
|
-
|
|
685
|
-
`--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`.
|
|
686
|
-
|
|
687
|
-
Use `--check=<path>` to diff against a snapshot at a custom location (defaults to `jsonOptions.outFile`, or `COMPONENT_API.json`).
|
|
688
|
-
|
|
689
|
-
By default `--check` only fails the run (exit `3`) on a `major` bump; `minor` and `patch` changes are still reported but don't fail CI. Pass `--check-level=minor` or `--check-level=patch` to fail the run at a lower threshold, e.g. to gate a package that promises no additive changes without a minor release.
|
|
690
|
-
|
|
691
|
-
If the committed snapshot's `schemaVersion` doesn't match the version `sveld` currently emits, `--check` reports a `kind: "schema"` entry instead of diffing (a version mismatch isn't a semver bump) and exits `1` (usage error): regenerate the snapshot with `sveld --json`.
|
|
692
|
-
|
|
693
|
-
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. See [Exit codes](#exit-codes) for how these are differentiated.
|
|
694
|
-
|
|
695
|
-
Pass `--format=json` for a machine-readable report on `stdout` instead of the prose above, e.g. `sveld --check --format=json | jq '.bump'` or `sveld --check --format=json | jq '.changes[] | select(.bump == "major")'`.
|
|
696
|
-
|
|
697
|
-
### CI: strictness profiles (`--strict=ci`/`--strict=local`)
|
|
698
|
-
|
|
699
|
-
CI and local runs usually want a different bundle of `strict`, `reportDiagnostics`, `check`, and `checkExamples` flags, previously assembled by hand. `--strict=ci`/`strict: "ci"` and `--strict=local`/`strict: "local"` are shorthands for two common bundles. Bare `--strict`/`strict: true` (and `--strict=errors`) are unchanged.
|
|
700
|
-
|
|
701
|
-
`--strict=ci` expands to `{ strict: true, reportDiagnostics: true, check: true, checkExamples: true }` — the full gate for a CI job:
|
|
702
|
-
|
|
703
|
-
```sh
|
|
704
|
-
npx sveld --json --strict=ci
|
|
705
|
-
```
|
|
706
|
-
|
|
707
|
-
`--strict=local` expands to `{ reportDiagnostics: true }` — diagnostics printed for a developer to see, without failing the command or requiring a committed `COMPONENT_API.json` snapshot:
|
|
708
|
-
|
|
709
|
-
```sh
|
|
710
|
-
npx sveld --json --strict=local
|
|
711
|
-
```
|
|
712
|
-
|
|
713
|
-
The profile's keys are applied first, then any other key set alongside `strict` overrides it, so you can opt back out of one piece:
|
|
714
|
-
|
|
715
|
-
```ts
|
|
716
|
-
// Everything --strict=ci implies, except the TypeScript-checked half of checkExamples.
|
|
717
|
-
await sveld({ json: true, strict: "ci", checkExamples: "syntax" });
|
|
718
|
-
```
|
|
719
|
-
|
|
720
|
-
### Node.js
|
|
721
|
-
|
|
722
|
-
You can also call `sveld` from Node.js. See [Requirements](#requirements) for supported Node versions and the ESM-only constraint.
|
|
723
|
-
|
|
724
|
-
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).
|
|
725
|
-
|
|
726
|
-
> `input` was renamed to `entry` to match the CLI, plugin, and config. Calls passing `input` throw with a message pointing at `entry`.
|
|
727
|
-
|
|
728
|
-
```js
|
|
729
|
-
import { sveld } from "sveld";
|
|
730
|
-
import pkg from "./package.json" with { type: "json" };
|
|
731
|
-
|
|
732
|
-
const { diagnostics } = await sveld({
|
|
733
|
-
entry: "./src/index.js",
|
|
734
|
-
glob: true,
|
|
735
|
-
markdown: true,
|
|
736
|
-
markdownOptions: {
|
|
737
|
-
onAppend: (type, document, components) => {
|
|
738
|
-
if (type === "h1")
|
|
739
|
-
document.append(
|
|
740
|
-
"quote",
|
|
741
|
-
`${components.size} components exported from ${pkg.name}@${pkg.version}.`,
|
|
742
|
-
);
|
|
743
|
-
},
|
|
744
|
-
},
|
|
745
|
-
json: true,
|
|
746
|
-
jsonOptions: {
|
|
747
|
-
outFile: "docs/src/COMPONENT_API.json",
|
|
748
|
-
},
|
|
749
|
-
});
|
|
750
|
-
```
|
|
751
|
-
|
|
752
|
-
`diagnostics` is always populated; printing is opt-in via `reportDiagnostics` or `strict` (see [Type inference diagnostics](#type-inference-diagnostics)). `errors` is always populated too: components that failed to parse, whether or not `failFast` is set.
|
|
753
|
-
|
|
754
|
-
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()` never touches `process.exitCode` itself; it returns a suggested `exitCode` (`0`, `3` for a breaking `check` result, or `4` for `strict` diagnostics — the same mapping the CLI uses, `3` winning over `4`) so you can assign it yourself:
|
|
755
|
-
|
|
756
|
-
```js
|
|
757
|
-
import { formatCheckReport } from "sveld";
|
|
758
|
-
|
|
759
|
-
const { check, exitCode } = await sveld({ json: true, check: true });
|
|
760
|
-
|
|
761
|
-
if (check) {
|
|
762
|
-
console.log(formatCheckReport(check));
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
process.exitCode = exitCode;
|
|
766
|
-
```
|
|
767
|
-
|
|
768
|
-
See [CI: API-drift checks (`--check`)](#ci-api-drift-checks---check) for how changes are classified.
|
|
769
|
-
|
|
770
|
-
#### `jsonOptions.outDir`
|
|
771
|
-
|
|
772
|
-
With `json: true`, `sveld` writes `COMPONENT_API.json` at the project root. The file documents all components.
|
|
773
|
-
|
|
774
|
-
Use the `jsonOptions.outDir` option to specify the folder for individual JSON files to be emitted.
|
|
775
|
-
|
|
776
|
-
```js
|
|
777
|
-
sveld({
|
|
778
|
-
json: true,
|
|
779
|
-
jsonOptions: {
|
|
780
|
-
// an individual JSON file will be generated for each component API
|
|
781
|
-
// e.g. "docs/Button.api.json"
|
|
782
|
-
outDir: "docs",
|
|
783
|
-
},
|
|
784
|
-
});
|
|
785
|
-
```
|
|
786
|
-
|
|
787
|
-
#### `jsonOptions.source`
|
|
788
|
-
|
|
789
|
-
Every prop, slot, event, typedef, and context in `COMPONENT_API.json` carries a `source` position range (plus `componentCommentSource` on the component itself) when the parser has a stable AST position for it. For a large component library this is a substantial share of the file — roughly a quarter of `COMPONENT_API.json` for a 150+ component library — and many consumers (docs sites, LLM context) never read it.
|
|
790
|
-
|
|
791
|
-
Set `jsonOptions.source` to `false` to omit every `source`/`componentCommentSource` range and shrink the file:
|
|
792
|
-
|
|
793
|
-
```js
|
|
794
|
-
sveld({
|
|
795
|
-
json: true,
|
|
796
|
-
jsonOptions: {
|
|
797
|
-
source: false,
|
|
798
|
-
},
|
|
799
|
-
});
|
|
800
|
-
```
|
|
801
|
-
|
|
802
|
-
`source: true` is the default; every other field is unaffected. `EntryExport.source` (the declaring module's relative path, from `documentExports`) is a different field with a string value and is never stripped.
|
|
803
|
-
|
|
804
|
-
### Browser
|
|
805
|
-
|
|
806
|
-
`sveld/browser` is a Node-free subpath export for running `sveld` client-side — e.g. an in-browser Svelte playground or REPL that parses whatever `.svelte` source the user typed and renders docs for it live. It bundles with Vite, esbuild, webpack, or Rollup without a `node:fs`/`node:path` polyfill.
|
|
807
|
-
|
|
808
|
-
It covers parsing one component's source and rendering that result to any output format `sveld` supports (JSON, Markdown, `.d.ts`, Custom Elements Manifest). It does not cover project-wide glob scanning, the config file, or the CLI/Vite plugin (`sveld()`/`pluginSveld()`) — those walk the filesystem and only make sense in Node. Use the main `sveld` entry point for those.
|
|
809
|
-
|
|
810
|
-
```ts
|
|
811
|
-
import {
|
|
812
|
-
asNormalizedPath,
|
|
813
|
-
ComponentParser,
|
|
814
|
-
finalizeWithoutCrossFileResolution,
|
|
815
|
-
buildComponentApiDocument,
|
|
816
|
-
writeMarkdownCore,
|
|
817
|
-
writeTsDefinition,
|
|
818
|
-
buildCustomElementsManifest,
|
|
819
|
-
} from "sveld/browser";
|
|
820
|
-
|
|
821
|
-
const parser = new ComponentParser();
|
|
822
|
-
const moduleName = "Button";
|
|
823
|
-
const filePath = "Button.svelte";
|
|
824
|
-
const parsed = finalizeWithoutCrossFileResolution(
|
|
825
|
-
parser.parseSvelteComponent(source, { moduleName, filePath }),
|
|
826
|
-
{ filePath },
|
|
827
|
-
);
|
|
828
|
-
|
|
829
|
-
// `parseSvelteComponent` returns component metadata only; add `moduleName`
|
|
830
|
-
// and `filePath` yourself to match the `ComponentDocApi` shape the writers expect.
|
|
831
|
-
const component = { ...parsed, moduleName, filePath: asNormalizedPath(filePath) };
|
|
832
|
-
const components = new Map([[moduleName, component]]);
|
|
833
|
-
|
|
834
|
-
// JSON
|
|
835
|
-
const jsonDoc = buildComponentApiDocument(components);
|
|
836
|
-
|
|
837
|
-
// Markdown
|
|
838
|
-
const markdown = writeMarkdownCore(components);
|
|
839
|
-
|
|
840
|
-
// TypeScript definitions (per component)
|
|
841
|
-
const dts = writeTsDefinition(jsonDoc.components[0]);
|
|
842
|
-
|
|
843
|
-
// Custom Elements Manifest
|
|
844
|
-
const cem = buildCustomElementsManifest(components, {
|
|
845
|
-
resolveModulePath: (component) => component.filePath,
|
|
846
|
-
});
|
|
847
|
-
```
|
|
848
|
-
|
|
849
|
-
A standalone parse can't read the files a component imports from, so some output the CLI and `generateBundle` produce is missing: a context whose `setContext` key is imported, the value of a prop default that names an imported `const`, the return type of a default that calls an imported function, and events dispatched by an imported helper (`wire(dispatch)`). `parseSvelteComponent` leaves these pending for the cross-file pass, which never runs here. Pass the result through `finalizeWithoutCrossFileResolution(parsed, { filePath })` to record a `sveld/cross-file-unresolved` warning for each one, naming the import (e.g. `` setContext key `keys.THEME` is imported from "./keys.js" ``), and to release the `sveld/event-no-source` warnings held back while a helper's events were unknown. It returns a new component and leaves the input unchanged.
|
|
850
|
-
|
|
851
|
-
`ComponentParser` is stateful but reusable across parses — call `parseSvelteComponent` again on the same instance for the next component instead of constructing a new one each time.
|
|
852
|
-
|
|
853
|
-
See [`playground/`](playground) in this repo for a working example: it parses Svelte source typed into an editor and renders JSON, Markdown, TypeScript, and Custom Elements Manifest tabs, all client-side. Deployed at [sveld.onrender.com](https://sveld.onrender.com).
|
|
854
|
-
|
|
855
|
-
### Config File
|
|
856
|
-
|
|
857
|
-
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. The Vite/Rollup plugin ignores it unless you opt in with the [`config`](#vite) option.
|
|
858
|
-
|
|
859
|
-
Import `defineConfig` from `sveld` for typed options. Config files must use ESM syntax (`export default`).
|
|
860
|
-
|
|
861
|
-
```js
|
|
862
|
-
// sveld.config.js
|
|
863
|
-
import { defineConfig } from "sveld";
|
|
864
|
-
|
|
865
|
-
export default defineConfig({
|
|
866
|
-
glob: true,
|
|
867
|
-
json: true,
|
|
868
|
-
markdown: true,
|
|
869
|
-
});
|
|
870
|
-
```
|
|
871
|
-
|
|
872
|
-
Later sources win when options overlap:
|
|
873
|
-
|
|
874
|
-
CLI flags > config file > `package.json#svelte` inference / defaults
|
|
875
|
-
|
|
876
|
-
With the config above, `npx sveld --json` keeps `glob` and `markdown` from the file. A CLI flag overrides the same key in the config.
|
|
877
|
-
|
|
878
|
-
`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.
|
|
879
|
-
|
|
880
|
-
```js
|
|
881
|
-
// sveld.config.js
|
|
882
|
-
export default {
|
|
883
|
-
json: true,
|
|
884
|
-
strict: true,
|
|
885
|
-
check: "snapshots/COMPONENT_API.json",
|
|
886
|
-
};
|
|
887
|
-
```
|
|
888
|
-
|
|
889
|
-
Merging is one level deep for object-valued options (`typesOptions`, `jsonOptions`, `markdownOptions`, `customElementsOptions`, `additionalWriters`): setting one nested key at the CLI or in `sveld()` doesn't drop sibling keys set in the config file. Arrays and functions (e.g. `markdownOptions.onAppend`) are replaced outright, never merged.
|
|
890
|
-
|
|
891
|
-
```js
|
|
892
|
-
// sveld.config.js
|
|
893
|
-
export default {
|
|
894
|
-
typesOptions: { outDir: "dist", preamble: "// license" },
|
|
895
|
-
};
|
|
896
|
-
```
|
|
897
|
-
|
|
898
|
-
`npx sveld --types-format=component` keeps `outDir` and `preamble` from the file and adds `format: "component"`, rather than replacing `typesOptions` entirely.
|
|
899
|
-
|
|
900
|
-
An unrecognized option key (top-level, or inside a `*Options` object) is not an error: it prints a `console.warn` naming the key, with a "did you mean" suggestion when a known key is close enough.
|
|
901
|
-
|
|
902
|
-
A bad config (syntax error, throws at load time, or no default-export object) fails with an error that names the file.
|
|
903
|
-
|
|
904
|
-
### Publishing to NPM
|
|
905
|
-
|
|
906
|
-
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.
|
|
907
|
-
|
|
908
|
-
```diff
|
|
909
|
-
{
|
|
910
|
-
"svelte": "./src/index.js",
|
|
911
|
-
+ "exports": {
|
|
912
|
-
+ ".": {
|
|
913
|
-
+ "types": "./types/index.d.ts",
|
|
914
|
-
+ "svelte": "./src/index.js",
|
|
915
|
-
+ "default": "./lib/index.mjs"
|
|
916
|
-
+ }
|
|
917
|
-
+ },
|
|
918
|
-
"main": "./lib/index.mjs",
|
|
919
|
-
"files": [
|
|
920
|
-
"src",
|
|
921
|
-
"lib",
|
|
922
|
-
+ "types",
|
|
923
|
-
]
|
|
924
|
-
}
|
|
925
|
-
```
|
|
926
|
-
|
|
927
|
-
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.
|
|
928
|
-
|
|
929
|
-
## Available Options
|
|
930
|
-
|
|
931
|
-
### Plugin Options
|
|
932
|
-
|
|
933
|
-
- **`entry`** (string, optional): Specify the entry point to uncompiled Svelte source. If not provided, sveld uses the `"svelte"` field from `package.json`.
|
|
934
|
-
- **`glob`** (boolean, optional): Enable glob mode to analyze all `*.svelte` files.
|
|
935
|
-
- **`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).
|
|
936
|
-
- **`types`** (boolean, optional, default: `true`): Generate TypeScript definitions.
|
|
937
|
-
- **`typesOptions`** (object, optional): Options for TypeScript definition generation.
|
|
938
|
-
- **`outDir`** (string, optional, default: `"types"`): Output directory for generated `.d.ts` files, relative to the project root.
|
|
939
|
-
- **`preamble`** (string, optional, default: `""`): Raw text prepended to the top of the generated `index.d.ts` barrel file, before the `export * from "./..."` lines. Useful for license headers or lint-disable comments. See [`typesOptions.preamble`](#typesoptionspreamble) below.
|
|
940
|
-
- **`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).
|
|
941
|
-
- **`exportTypes`** (`boolean | { props?, exports?, typedefs?, contexts? }`, optional, default: `true`): Which generated type declarations get an `export` keyword. `false` keeps them all local to the `.d.ts` file; an object picks per kind. Also available as `--types-export=<all|none>` (the CLI can only set the boolean form, not the per-kind object). See [`typesOptions.exportTypes`](#typesoptionsexporttypes).
|
|
942
|
-
- **`typeNames`** (`{ props?: string; exports?: string }`, optional, default: `{ props: "{name}Props", exports: "{name}Exports" }`): Templates for generated type names. No CLI flag; config file or `sveld()` only. See [`typesOptions.typeNames`](#typesoptionstypenames).
|
|
943
|
-
- **`comments`** (`"all" | "descriptions" | "none"`, optional, default: `"all"`): How much JSDoc lands in the generated `.d.ts`. `"all"` keeps descriptions, `@deprecated`, `@default`, and passthrough tags (`@since`, `@see`, `@example`, `@link`); `"descriptions"` keeps descriptions and `@deprecated` only; `"none"` emits no comments at all. Also available as `--types-comments=<all|descriptions|none>`. See [`typesOptions.comments`](#typesoptionscomments).
|
|
944
|
-
- **`propsDeclaration`** (`"type" | "interface"`, optional, default: `"type"`): `"interface"` emits the props type as an `interface` instead of a `type` alias when the props are a plain object. Also available as `--types-props-declaration=<type|interface>`. See [`typesOptions.propsDeclaration`](#typesoptionspropsdeclaration).
|
|
945
|
-
- **`transform`** (function, optional): Post-processes each generated file's text before it is written. Runs after the generated-text cache, so it applies on every run. No CLI flag; config file or `sveld()` only. See [`typesOptions.transform`](#typesoptionstransform).
|
|
946
|
-
- **`indexTypes`** (`boolean | { props?, exports?, typedefs?, contexts? }`, optional, default: `false`): Also re-export generated types from `index.d.ts`. `true` re-exports each component's `Props` type (and `Exports` under `format: "component"`); an object can additionally include typedefs and contexts. Also available as `--types-index-types` (the CLI can only set the boolean form, not the per-kind object). See [`typesOptions.indexTypes`](#typesoptionsindextypes).
|
|
947
|
-
- **`inline`** (`false | "local" | "all"`, optional, default: `false`): Copies `type`/`interface` declarations imported from a relative source (or a tsconfig/jsconfig path alias) directly into the `.d.ts`, dropping the import. Also available as `--types-inline=<local|all>` (`--types-inline=false` resets to the default). See [`typesOptions.inline`](#typesoptionsinline).
|
|
948
|
-
- **`json`** (boolean, optional): Generate component documentation in JSON format.
|
|
949
|
-
- **`jsonOptions`** (object, optional): Options for JSON output.
|
|
950
|
-
- **`outFile`** (string, optional, default: `"COMPONENT_API.json"`): Path (relative to the project root) for the single combined JSON document. Ignored when `outDir` is set.
|
|
951
|
-
- **`outDir`** (string, optional): Emit one JSON file per component (`<ComponentName>.api.json`) into this directory instead of a single combined file. See [`jsonOptions.outDir`](#jsonoptionsoutdir).
|
|
952
|
-
- **`source`** (boolean, optional, default: `true`): Set to `false` to omit every `source`/`componentCommentSource` position range from the output. See [`jsonOptions.source`](#jsonoptionssource).
|
|
953
|
-
- **`markdown`** (boolean, optional): Generate component documentation in Markdown format.
|
|
954
|
-
- **`markdownOptions`** (object, optional): Options for Markdown output.
|
|
955
|
-
- **`outFile`** (string, optional, default: `"COMPONENT_INDEX.md"`): Path (relative to the project root) for the single combined Markdown document. Ignored when `outDir` is set.
|
|
956
|
-
- **`outDir`** (string, optional): Emit one `<ModuleName>.md` file per component into this directory, plus an index `README.md` linking to each, instead of a single combined file. See [`markdownOptions.outDir`](#markdownoptionsoutdir).
|
|
957
|
-
- **`write`** (boolean, optional, default: `true`): Set to `false` to skip writing to disk — the rendered combined document is still returned, or (with `outDir` set) no files are written at all.
|
|
958
|
-
- **`onAppend`** (function, optional): Callback invoked every time a heading, quote, paragraph, divider, or raw block is appended to the document. Lets you inject extra content, e.g. a summary line under the title. See [`markdownOptions.onAppend`](#markdownoptionsonappend) below.
|
|
959
|
-
- **`customElements`** (boolean, optional): Generate a [Custom Elements Manifest](#custom-elements-manifest) (`custom-elements.json`). Also available as the `--custom-elements` CLI flag.
|
|
960
|
-
- **`customElementsOptions`** (object, optional): Options for Custom Elements Manifest output.
|
|
961
|
-
- **`outFile`** (string, optional, default: `"custom-elements.json"`): Path (relative to the project root) for the generated manifest file.
|
|
962
|
-
- **`llms`** (boolean, optional): Generate an [`llms.txt` / `llms-full.txt`](#llmstxt-output) pair. Also available as the `--llms` CLI flag.
|
|
963
|
-
- **`llmsOptions`** (object, optional): Options for `llms.txt` / `llms-full.txt` output.
|
|
964
|
-
- **`outDir`** (string, optional): Directory (relative to the project root) both files are written into. Defaults to the project root.
|
|
965
|
-
- **`linkBase`** (string, optional, default: `""`): Prefixed to each component's link in `llms.txt`.
|
|
966
|
-
- **`title`** (string, optional, default: the `"name"` field from `package.json`): The `# <title>` heading both files start with.
|
|
967
|
-
- **`summary`** (string, optional, default: the `"description"` field from `package.json`): The `> <summary>` blockquote under the title.
|
|
968
|
-
- **`config`** (boolean | string, optional, default: `false`): Load `sveld.config.{js,mjs,ts}` and merge it with these options; these options win when a key is set in both. `true` resolves the config from the Vite project root (or `process.cwd()` outside Vite); a string is an explicit path to the config file. The plugin warns about keys only the CLI and `sveld()` act on (`reportDiagnostics`, `strict`, `check`, `checkLevel`, `stdout`, `format`, `dryRun`). See [Config File](#config-file).
|
|
969
|
-
- **`watch`** (boolean, optional, default: `false`): Regenerate output incrementally when relevant source changes during `vite dev` / `vite build --watch`. A reparse is triggered by: editing a component; editing the entry barrel itself, which adds/removes the corresponding component; or editing a non-`.svelte` file a component depends on via [`@extendProps`](#extendprops) / `@extends` or a typedef `import("./x")` reference. Only the affected components are re-parsed, rather than rebuilding every component. Overlapping regenerations are queued, never run concurrently. Without this option, the plugin only runs during `vite build`.
|
|
970
|
-
- **`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.
|
|
971
|
-
- **`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).
|
|
972
|
-
- **`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).
|
|
973
|
-
- **`checkExamples`** (`boolean | "syntax"`, optional, default: `false`): `true` runs plain TS/JS `@example` blocks through the TypeScript program (`example-compile-error` diagnostics) and `svelte`/`html` blocks through sveld's own template parser (`example-syntax-error` diagnostics). `"syntax"` runs only the markup path, so `typescript` is never loaded. Also available as `--check-examples` / `--check-examples=syntax` (`--checkExamples` remains as a deprecated alias). See [Compile-checked `@example` blocks](#compile-checked-example-blocks-checkexamples).
|
|
974
|
-
- **`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).
|
|
975
|
-
- **`strict`** (`boolean | "errors" | "ci" | "local"`, optional, default: `false`): Exit with code `4` when diagnostics exist. Implies `reportDiagnostics`. `"errors"` fails only on `severity: "error"` diagnostics, letting `warning` ones through. `"ci"` and `"local"` are strictness profiles that expand into other options before this object's own keys are applied. Also available as `--strict` / `--strict=errors` / `--strict=ci` / `--strict=local`. See [Type inference diagnostics](#type-inference-diagnostics) and [CI: strictness profiles](#ci-strictness-profiles---strictci---strictlocal).
|
|
976
|
-
- **`diagnostics.ignore`** (`Array<{ code?: string; component?: string; name?: string }>`, optional): Marks matching diagnostics `ignored` — they're still reported and counted, but never fail `strict`. `component` is a glob; an omitted field on a matcher matches anything. No CLI flag; config-file or `sveld()` only. See [Ignoring diagnostics](#ignoring-diagnostics).
|
|
977
|
-
- **`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 `3` 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).
|
|
978
|
-
- **`checkLevel`** (`"major" | "minor" | "patch"`, optional, default: `"major"`): Minimum bump `--check` fails the CLI run on. Also available as `--check-level`. See [CI: API-drift checks (`--check`)](#ci-api-drift-checks---check).
|
|
979
|
-
- **`quiet`** (boolean, optional, default: `false`): Suppress writer progress logs (`created "..."` / `unchanged "..."`), which print to `stderr` by default. Does not suppress error messages, the diagnostics summary, or the `--check` report. Also available as `--quiet`.
|
|
980
|
-
- **`dryRun`** (boolean, optional, default: `false`): Resolve the entry, load config, and parse components through the real pipeline, then print `would write "<path>"` to `stdout` for each output file instead of writing it, including the parse cache. Diagnostics, `strict`, and `check` behave as in a real run. CLI-only via `--dry-run`; the Vite plugin does not expose this option.
|
|
981
|
-
|
|
982
|
-
By default, only TypeScript definitions are generated.
|
|
983
|
-
|
|
984
|
-
To generate documentation in Markdown and JSON formats, set `markdown` and `json` to `true`.
|
|
985
|
-
|
|
986
|
-
```diff
|
|
987
|
-
sveld({
|
|
988
|
-
+ markdown: true,
|
|
989
|
-
+ json: true,
|
|
990
|
-
})
|
|
991
|
-
```
|
|
992
|
-
|
|
993
|
-
#### `typesOptions.preamble`
|
|
994
|
-
|
|
995
|
-
Use `typesOptions.preamble` to prepend raw text to the generated `types/index.d.ts` barrel file — for example, a license header that should ship with every published `.d.ts` file.
|
|
996
|
-
|
|
997
|
-
```js
|
|
998
|
-
sveld({
|
|
999
|
-
types: true,
|
|
1000
|
-
typesOptions: {
|
|
1001
|
-
preamble: "// Copyright (c) 2026 Acme Inc. All rights reserved.\n\n",
|
|
1002
|
-
},
|
|
1003
|
-
});
|
|
1004
|
-
```
|
|
1005
|
-
|
|
1006
|
-
```ts
|
|
1007
|
-
// types/index.d.ts
|
|
1008
|
-
// Copyright (c) 2026 Acme Inc. All rights reserved.
|
|
1009
|
-
|
|
1010
|
-
export { default as Button } from "./Button.svelte";
|
|
1011
|
-
```
|
|
1012
|
-
|
|
1013
|
-
`preamble` only affects the barrel file (`index.d.ts`); per-component `.d.ts` files are untouched.
|
|
1014
|
-
|
|
1015
|
-
#### `typesOptions.exportTypes`
|
|
1016
|
-
|
|
1017
|
-
Every type sveld generates is `export`ed by default. `typesOptions.exportTypes` lets a library keep some or all of them local to each component's `.d.ts` — useful when the props type is an implementation detail, or when it collides with a hand-written type of the same name in the package's own `index.d.ts`. Only the component itself needs to be public either way.
|
|
1018
|
-
|
|
1019
|
-
```js
|
|
1020
|
-
sveld({
|
|
1021
|
-
types: true,
|
|
1022
|
-
typesOptions: {
|
|
1023
|
-
exportTypes: false,
|
|
1024
|
-
},
|
|
1025
|
-
});
|
|
1026
|
-
```
|
|
1027
|
-
|
|
1028
|
-
**Button.svelte.d.ts** before:
|
|
1029
|
-
|
|
1030
|
-
```ts
|
|
1031
|
-
export type ButtonProps = { label?: string };
|
|
1032
|
-
|
|
1033
|
-
export default class Button extends SvelteComponentTyped<
|
|
1034
|
-
ButtonProps,
|
|
1035
|
-
{ click: WindowEventMap["click"] },
|
|
1036
|
-
{ default: Record<string, never> }
|
|
1037
|
-
> {}
|
|
1038
|
-
```
|
|
1039
|
-
|
|
1040
|
-
**Button.svelte.d.ts** after (`exportTypes: false`):
|
|
1041
|
-
|
|
1042
|
-
```ts
|
|
1043
|
-
type ButtonProps = { label?: string };
|
|
1044
|
-
|
|
1045
|
-
export default class Button extends SvelteComponentTyped<
|
|
1046
|
-
ButtonProps,
|
|
1047
|
-
{ click: WindowEventMap["click"] },
|
|
1048
|
-
{ default: Record<string, never> }
|
|
1049
|
-
> {}
|
|
1050
|
-
```
|
|
1051
|
-
|
|
1052
|
-
`Button` is still the only thing a consumer imports; `ButtonProps` is just no longer part of the public surface (it can still be referenced structurally — through `ComponentProps<typeof Button>`, for instance).
|
|
1053
|
-
|
|
1054
|
-
Pass an object instead of a boolean to pick per kind:
|
|
1055
|
-
|
|
1056
|
-
```js
|
|
1057
|
-
typesOptions: {
|
|
1058
|
-
exportTypes: {
|
|
1059
|
-
props: false, // export type <Name>Props
|
|
1060
|
-
exports: true, // export type <Name>Exports (format: "component" only)
|
|
1061
|
-
typedefs: true, // export interface|type <Typedef> from @typedef
|
|
1062
|
-
contexts: true, // export type <Context> from setContext
|
|
1063
|
-
},
|
|
1064
|
-
}
|
|
1065
|
-
```
|
|
1066
|
-
|
|
1067
|
-
Any key left out defaults to `true`.
|
|
1068
|
-
|
|
1069
|
-
`<script context="module">` exports (`export declare const` / `export declare function` / `export declare class`) are real runtime exports and are always emitted as exports, regardless of `exportTypes`. So are the types a module script exports (`export interface Item`, `export type Mode`, `export type { Local }`), since they're part of the component module's API.
|
|
1070
|
-
|
|
1071
|
-
A module-script class (`export class Store {}`, or `class Store {}` then `export { Store }`) is emitted with its public surface: the constructor, methods (with their overload signatures, if any), fields, constructor parameter properties, getter/setter pairs, and `static`/`readonly`/`abstract`/optional modifiers. Types come from TypeScript annotations, then JSDoc (`@param`, `@returns`, `@type`, and `@template` on the class or a method), and are `any` otherwise. In a JS script, a `this.x = ...` assignment in the constructor declares property `x`. Private (`#x`, `private`), `protected`, computed-key, and `@internal` members are left out. `extends` and `implements` clauses are kept, and an imported base class or interface gets an `import type`. A class that extends something the `.d.ts` can't reference (a non-exported local class, or an expression like `mixin(Base)`) is declared without `extends`, with an `export-unresolved` warning, since its inherited members would be missing.
|
|
1072
|
-
|
|
1073
|
-
```ts
|
|
1074
|
-
export declare class Store<T> {
|
|
1075
|
-
constructor(initial: T);
|
|
1076
|
-
|
|
1077
|
-
value: T;
|
|
1078
|
-
|
|
1079
|
-
static create<U>(value: U): Store<U>;
|
|
1080
|
-
|
|
1081
|
-
subscribe(run: (value: T) => void): () => void;
|
|
1082
|
-
}
|
|
1083
|
-
```
|
|
1084
|
-
|
|
1085
|
-
In JSON the class is a `moduleExports` entry with `kind: "class"`, `type: "typeof Store"`, and its members under `members`; the Markdown and `llms-full.txt` output list them in a members table after the Module exports table.
|
|
1086
|
-
|
|
1087
|
-
One exception: when a bundled component uses [`@extendProps`](#extendprops) to extend another bundled component, sveld emits `import type { ButtonProps } from "./Button.svelte"` in the extending component's `.d.ts`. If `Button`'s props type stopped being exported, that import would break — so sveld always keeps a component's props type exported when another component in the same run extends it, even under `exportTypes: false`.
|
|
1088
|
-
|
|
1089
|
-
Also available as `--types-export=<all|none>` on the CLI, mapped to the boolean form (`all` => `true`, `none` => `false`) — the CLI can't express the per-kind object form.
|
|
1090
|
-
|
|
1091
|
-
#### `typesOptions.typeNames`
|
|
1092
|
-
|
|
1093
|
-
By default, sveld names the generated props type `<Name>Props` and (for [`format: "component"`](#dts-output-format-typesoptionsformat)) the exports type `<Name>Exports`, where `<Name>` is the component's module name. `typesOptions.typeNames` lets a library follow its own naming convention instead, via a `{name}` placeholder template.
|
|
1094
|
-
|
|
1095
|
-
```js
|
|
1096
|
-
sveld({
|
|
1097
|
-
types: true,
|
|
1098
|
-
typesOptions: {
|
|
1099
|
-
typeNames: { props: "I{name}Props" },
|
|
1100
|
-
},
|
|
1101
|
-
});
|
|
1102
|
-
```
|
|
1103
|
-
|
|
1104
|
-
**Button.svelte.d.ts** before / after:
|
|
1105
|
-
|
|
1106
|
-
```diff
|
|
1107
|
-
- export type ButtonProps = { label?: string };
|
|
1108
|
-
+ export type IButtonProps = { label?: string };
|
|
1109
|
-
|
|
1110
|
-
- export default class Button extends SvelteComponentTyped<ButtonProps, ...> {}
|
|
1111
|
-
+ export default class Button extends SvelteComponentTyped<IButtonProps, ...> {}
|
|
1112
|
-
```
|
|
1113
|
-
|
|
1114
|
-
Each template must contain `{name}` and produce a valid identifier once substituted, and the two must produce different names that aren't the component's own (`{name}`) or its generic-component interface's (`{name}Component`); sveld throws otherwise. Any key left out of the object keeps its default (`"{name}Props"` / `"{name}Exports"`).
|
|
1115
|
-
|
|
1116
|
-
If a bundled component then [`@extendProps`](#extendprops)/`@extends`-es another one, the tag must name the *templated* interface — `@extendProps {"./Button.svelte"} IButtonProps`, not `ButtonProps` — or sveld reports [`extend-props-target-missing`](#type-inference-diagnostics).
|
|
1117
|
-
|
|
1118
|
-
There's no CLI flag for `typeNames`; set it via a config file or the programmatic `sveld()` API.
|
|
1119
|
-
|
|
1120
|
-
#### `typesOptions.comments`
|
|
1121
|
-
|
|
1122
|
-
By default sveld carries every prop's JSDoc into the generated `.d.ts`: the description, `@deprecated`, `@default`, and passthrough tags like `@since`. `typesOptions.comments` lets a library ship a leaner `.d.ts` instead — useful when the docs live elsewhere (a docs site, Storybook) and duplicating them in the type declaration just adds noise.
|
|
1123
|
-
|
|
1124
|
-
```svelte
|
|
1125
|
-
<script>
|
|
1126
|
-
/**
|
|
1127
|
-
* The button's visible label.
|
|
1128
|
-
* @deprecated Use `text` instead.
|
|
1129
|
-
* @default ""
|
|
1130
|
-
* @since 1.2.0
|
|
1131
|
-
*/
|
|
1132
|
-
export let label = "";
|
|
1133
|
-
</script>
|
|
1134
|
-
```
|
|
1135
|
-
|
|
1136
|
-
**`comments: "all"`** (default) — description, `@deprecated`, `@default`, and passthrough tags:
|
|
1137
|
-
|
|
1138
|
-
```ts
|
|
1139
|
-
/**
|
|
1140
|
-
* The button's visible label.
|
|
1141
|
-
* @deprecated Use `text` instead.
|
|
1142
|
-
* @default ""
|
|
1143
|
-
* @since 1.2.0
|
|
1144
|
-
*/
|
|
1145
|
-
label?: string;
|
|
1146
|
-
```
|
|
1147
|
-
|
|
1148
|
-
**`comments: "descriptions"`** — description and `@deprecated` only; `@default` and passthrough tags are dropped:
|
|
1149
|
-
|
|
1150
|
-
```ts
|
|
1151
|
-
/**
|
|
1152
|
-
* The button's visible label.
|
|
1153
|
-
* @deprecated Use `text` instead.
|
|
1154
|
-
*/
|
|
1155
|
-
label?: string;
|
|
1156
|
-
```
|
|
1157
|
-
|
|
1158
|
-
`@deprecated` survives at this level on purpose: editors render a strikethrough from it, so it's API information a consumer needs, not documentation.
|
|
1159
|
-
|
|
1160
|
-
**`comments: "none"`** — no comments at all; only the declaration:
|
|
1161
|
-
|
|
1162
|
-
```ts
|
|
1163
|
-
label?: string;
|
|
1164
|
-
```
|
|
1165
|
-
|
|
1166
|
-
```js
|
|
1167
|
-
sveld({
|
|
1168
|
-
types: true,
|
|
1169
|
-
typesOptions: {
|
|
1170
|
-
comments: "descriptions",
|
|
1171
|
-
},
|
|
1172
|
-
});
|
|
1173
|
-
```
|
|
1174
|
-
|
|
1175
|
-
The same levels apply to the comments on slots, events, their snippet and `on<event>` callback props, `@restProps`, typedefs, contexts, module exports, and the component itself. A `@property` description inside a `@typedef` is part of the type's own text, so it stays at every level.
|
|
1176
|
-
|
|
1177
|
-
`@internal`/`@ignore` members are removed from every output regardless of this option — see [`@ignore` / `@internal`](#ignore--internal). `comments` only controls how much JSDoc survives for members that *do* get emitted.
|
|
1178
|
-
|
|
1179
|
-
Also available as `--types-comments=<all|descriptions|none>` on the CLI.
|
|
1180
|
-
|
|
1181
|
-
#### `typesOptions.propsDeclaration`
|
|
1182
|
-
|
|
1183
|
-
By default sveld emits the props type as a `type` alias. `typesOptions.propsDeclaration: "interface"` emits an `interface` instead, for consumers who want to extend it with `interface MyProps extends ButtonProps`, rely on declaration merging, or just prefer the shorter editor hover an `interface` gets.
|
|
1184
|
-
|
|
1185
|
-
```js
|
|
1186
|
-
sveld({
|
|
1187
|
-
types: true,
|
|
1188
|
-
typesOptions: {
|
|
1189
|
-
propsDeclaration: "interface",
|
|
1190
|
-
},
|
|
1191
|
-
});
|
|
1192
|
-
```
|
|
1193
|
-
|
|
1194
|
-
**Button.svelte.d.ts** before / after:
|
|
1195
|
-
|
|
1196
|
-
```diff
|
|
1197
|
-
- export type ButtonProps = { label?: string };
|
|
1198
|
-
+ export interface ButtonProps { label?: string }
|
|
1199
|
-
```
|
|
1200
|
-
|
|
1201
|
-
Only a plain object props type can become an `interface` safely. These shapes are intersections instead, and always stay a `type` alias regardless of this option:
|
|
1202
|
-
|
|
1203
|
-
- a component with [`@restProps`](#restprops) (`Omit<$RestProps, keyof $Props> & $Props`)
|
|
1204
|
-
- a component with [`@extendProps`](#extendprops) (`Omit<Extended, keyof $Props> & $Props`)
|
|
1205
|
-
- a component with a whole-object `$props()` type resolved via [`resolveTypes`](#opt-in-semantic-resolution-resolvetypes) (`CanonicalPropsType & { ... }`)
|
|
1206
|
-
- a component with no props at all (`Record<string, never>`; an empty `interface` trips `noEmptyInterface` in consumer lint setups)
|
|
1207
|
-
|
|
1208
|
-
Also available as `--types-props-declaration=<type|interface>` on the CLI.
|
|
1209
|
-
|
|
1210
|
-
#### `typesOptions.transform`
|
|
1211
|
-
|
|
1212
|
-
`typesOptions.transform` is an escape hatch for one-off edits no other option covers - appending a `/// <reference>` directive, rewriting an import specifier for a monorepo, stripping a banner. It runs on every generated `.d.ts` file's text, immediately before it's written, and receives the block's `kind` (`"component"` or `"index"`), and for `"component"` the parsed `ComponentDocApi` too, plus the output `filePath` relative to `outDir` (e.g. `"Button.svelte.d.ts"` or `"index.d.ts"`). Return the new text, synchronously or via a `Promise`.
|
|
1213
|
-
|
|
1214
|
-
```js
|
|
1215
|
-
sveld({
|
|
1216
|
-
types: true,
|
|
1217
|
-
typesOptions: {
|
|
1218
|
-
transform: (text, context) => {
|
|
1219
|
-
if (context.kind !== "component") return text;
|
|
1220
|
-
return `/// <reference types="svelte" />\n${text}`;
|
|
1221
|
-
},
|
|
1222
|
-
},
|
|
1223
|
-
});
|
|
1224
|
-
```
|
|
1225
|
-
|
|
1226
|
-
```ts
|
|
1227
|
-
// Button.svelte.d.ts
|
|
1228
|
-
/// <reference types="svelte" />
|
|
1229
|
-
export type ButtonProps = { label?: string };
|
|
1230
|
-
// ...
|
|
1231
|
-
```
|
|
1232
|
-
|
|
1233
|
-
`transform` runs after the [persistent parse cache](#persistent-parse-cache-cache)'s generated-text cache, so a changed transform is never served stale cached output - it always re-applies, even when the underlying `.d.ts` text was reused from a previous run. A transform that throws, or resolves to something other than a string, fails the whole `sveld` run with an error naming the file that failed.
|
|
1234
|
-
|
|
1235
|
-
There's no CLI flag for `transform`; set it via a config file or the programmatic `sveld()` API.
|
|
1236
|
-
|
|
1237
|
-
#### `typesOptions.indexTypes`
|
|
1238
|
-
|
|
1239
|
-
By default, `types/index.d.ts` only re-exports components:
|
|
1240
|
-
|
|
1241
|
-
```ts
|
|
1242
|
-
export { default as Button } from "./Button.svelte";
|
|
1243
|
-
```
|
|
1244
|
-
|
|
1245
|
-
A consumer who wants `ButtonProps` has to deep-import `my-lib/types/Button.svelte`. `typesOptions.indexTypes` re-exports the generated types from the barrel instead, so `import type { ButtonProps } from "my-lib"` works directly.
|
|
1246
|
-
|
|
1247
|
-
```js
|
|
1248
|
-
sveld({
|
|
1249
|
-
types: true,
|
|
1250
|
-
typesOptions: {
|
|
1251
|
-
indexTypes: true,
|
|
1252
|
-
},
|
|
1253
|
-
});
|
|
1254
|
-
```
|
|
1255
|
-
|
|
1256
|
-
```ts
|
|
1257
|
-
// types/index.d.ts
|
|
1258
|
-
export { default as Button } from "./Button.svelte";
|
|
1259
|
-
|
|
1260
|
-
export type { ButtonProps } from "./Button.svelte";
|
|
1261
|
-
```
|
|
1262
|
-
|
|
1263
|
-
```ts
|
|
1264
|
-
import type { ButtonProps } from "my-lib";
|
|
1265
|
-
```
|
|
1266
|
-
|
|
1267
|
-
`true` is shorthand for `{ props: true, exports: true }`: it re-exports each component's `Props` type, and (under [`format: "component"`](#dts-output-format-typesoptionsformat)) its `Exports` type. Pass an object to also re-export typedefs and/or contexts:
|
|
1268
|
-
|
|
1269
|
-
```js
|
|
1270
|
-
typesOptions: {
|
|
1271
|
-
indexTypes: { props: true, typedefs: true, contexts: true },
|
|
1272
|
-
}
|
|
1273
|
-
```
|
|
1274
|
-
|
|
1275
|
-
`Props`/`Exports` names are unique per component by construction (they're derived from the module name), so they're always safe to re-export together. Typedef and context names are user-authored and can collide across components - when two components generate the same name, the first one in barrel order wins and every later collision is dropped with a one-line warning to `stderr`:
|
|
1276
|
-
|
|
1277
|
-
```
|
|
1278
|
-
sveld: index.d.ts skips duplicate type export "TabsContext" from "./Tabs2.svelte" (already exported from "./Tabs.svelte").
|
|
1279
|
-
```
|
|
1280
|
-
|
|
1281
|
-
`indexTypes` composes with [`exportTypes`](#typesoptionsexporttypes): a type that `exportTypes` keeps local to its component's `.d.ts` is never re-exported from the barrel either, since it wouldn't resolve.
|
|
1282
|
-
|
|
1283
|
-
Also available as `--types-index-types` on the CLI, mapped to the boolean form (`true` re-exports `Props`/`Exports` only) — the CLI can't express the per-kind object form. Pass `--types-index-types=false` to disable it explicitly.
|
|
1284
|
-
|
|
1285
|
-
#### `typesOptions.inline`
|
|
1286
|
-
|
|
1287
|
-
A TypeScript component that imports a type keeps that import in its `.d.ts`:
|
|
1288
|
-
|
|
1289
|
-
```svelte
|
|
1290
|
-
<script lang="ts">
|
|
1291
|
-
import type { Size } from "./types";
|
|
1292
|
-
let { size }: { size: Size } = $props();
|
|
1293
|
-
</script>
|
|
1294
|
-
```
|
|
1295
|
-
|
|
1296
|
-
```ts
|
|
1297
|
-
import type { Component } from "svelte";
|
|
1298
|
-
import type { Size } from "./types";
|
|
1299
|
-
|
|
1300
|
-
type $Props = { size: Size };
|
|
1301
|
-
export type MyComponentProps = $Props;
|
|
1302
|
-
```
|
|
1303
|
-
|
|
1304
|
-
Publishers who don't ship their `.svelte` sources need `.d.ts` files without relative imports:
|
|
1305
|
-
`./types.ts` might sit outside the published `types/` output directory, or the package might be
|
|
1306
|
-
bundled to a single file. `typesOptions.inline: "local"` copies the imported declaration into
|
|
1307
|
-
the `.d.ts` instead of importing it:
|
|
1308
|
-
|
|
1309
|
-
```js
|
|
1310
|
-
sveld({
|
|
1311
|
-
types: true,
|
|
1312
|
-
typesOptions: {
|
|
1313
|
-
inline: "local",
|
|
1314
|
-
},
|
|
1315
|
-
});
|
|
1316
|
-
```
|
|
1317
|
-
|
|
1318
|
-
```ts
|
|
1319
|
-
import type { Component } from "svelte";
|
|
1320
|
-
|
|
1321
|
-
type Size = "sm" | "md" | "lg";
|
|
1322
|
-
|
|
1323
|
-
type $Props = { size: Size };
|
|
1324
|
-
export type MyComponentProps = $Props;
|
|
1325
|
-
```
|
|
1326
|
-
|
|
1327
|
-
`"local"` follows:
|
|
1328
|
-
|
|
1329
|
-
- relative sources (`./types`) and tsconfig/jsconfig `paths` aliases;
|
|
1330
|
-
- re-exports (`export { X as Y } from "./z"` and `export * from "./z"`);
|
|
1331
|
-
- same-file dependencies (a copied type that itself references another type or interface
|
|
1332
|
-
declared in the same file copies that one too, before the type that references it).
|
|
1333
|
-
|
|
1334
|
-
It leaves alone:
|
|
1335
|
-
|
|
1336
|
-
- bare/package imports (`import type { CSSProperties } from "some-package"`) and `.svelte`
|
|
1337
|
-
sources — `"all"` covers bare imports, see below;
|
|
1338
|
-
- `@extendProps`/`@extends` imports and `import("./x")` inline import types inside typedefs,
|
|
1339
|
-
which are unrelated mechanisms;
|
|
1340
|
-
- `enum`, `class`, and `function` exports, which can't be safely copied as a `type`/`interface`.
|
|
1341
|
-
|
|
1342
|
-
An import that can't be safely inlined (a missing file, a missing export, one of the unsupported
|
|
1343
|
-
export kinds above, or a name collision with something the component's `.d.ts` already declares
|
|
1344
|
-
or imports — a typedef, a context type, a local `type`/`interface`, its `Props`/`Exports` type
|
|
1345
|
-
name, the component's own name, `$Props`/`$RestProps`, the svelte types the `.d.ts` imports, or a
|
|
1346
|
-
name bound by an import that stays) is left
|
|
1347
|
-
as an import, with a [`types-inline-unresolved`](#diagnostic-codes) warning explaining why. If an
|
|
1348
|
-
`import type { A, B } from "./x"` statement imports several names and even one of them can't be
|
|
1349
|
-
inlined, the whole statement is kept and nothing from it is inlined — simpler, and always correct.
|
|
1350
|
-
|
|
1351
|
-
**`"all"`** additionally inlines bare/package imports:
|
|
1352
|
-
|
|
1353
|
-
```svelte
|
|
1354
|
-
<script lang="ts">
|
|
1355
|
-
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
1356
|
-
import type { Alignment } from "some-design-system";
|
|
1357
|
-
let { rest, align }: { rest: HTMLButtonAttributes; align: Alignment } = $props();
|
|
1358
|
-
</script>
|
|
1359
|
-
```
|
|
1360
|
-
|
|
1361
|
-
```js
|
|
1362
|
-
sveld({
|
|
1363
|
-
types: true,
|
|
1364
|
-
typesOptions: {
|
|
1365
|
-
inline: "all",
|
|
1366
|
-
},
|
|
1367
|
-
});
|
|
1368
|
-
```
|
|
1369
|
-
|
|
1370
|
-
```ts
|
|
1371
|
-
import type { Component } from "svelte";
|
|
1372
|
-
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
1373
|
-
|
|
1374
|
-
type Alignment = "start" | "center" | "end";
|
|
1375
|
-
|
|
1376
|
-
type $Props = { rest: HTMLButtonAttributes; align: Alignment };
|
|
1377
|
-
export type MyComponentProps = $Props;
|
|
1378
|
-
```
|
|
1379
|
-
|
|
1380
|
-
`some-design-system`'s `Alignment` got copied in, exactly like a `"local"` relative import would.
|
|
1381
|
-
`svelte`/`svelte/elements` did not, even though `HTMLButtonAttributes` is itself a bare import a
|
|
1382
|
-
real TypeScript checker could resolve and copy just as easily: those two specifiers are a
|
|
1383
|
-
hard-coded allow-list and always stay imports under `"all"`, regardless of what they resolve to.
|
|
1384
|
-
Copying framework types would freeze whatever Svelte version happened to be installed at
|
|
1385
|
-
generation time into every consumer's `.d.ts` output, which defeats the point of importing them
|
|
1386
|
-
from `svelte` in the first place.
|
|
1387
|
-
|
|
1388
|
-
Resolving a bare specifier to a file needs a real module resolver, so `"all"` uses the actual
|
|
1389
|
-
TypeScript checker (the same one behind [`resolveTypes`](#opt-in-semantic-resolution-resolvetypes)
|
|
1390
|
-
and [`checkExamples`](#compile-checked-example-blocks-checkexamples), shared across all three when
|
|
1391
|
-
more than one is enabled) rather than sveld's own AST-only pass. This makes `"all"` a **hard
|
|
1392
|
-
requirement** on `typescript` 7+ and a resolvable `tsconfig.json` — same contract `resolveTypes`
|
|
1393
|
-
already has (see [Requirements](#requirements)): if TypeScript can't be started, the run fails
|
|
1394
|
-
loudly rather than silently falling back to `"local"` behavior. The requirement only actually
|
|
1395
|
-
kicks in when there's at least one non-allow-listed bare import somewhere in the bundle to
|
|
1396
|
-
resolve; an `"all"` run with nothing bare to inline never loads TypeScript at all.
|
|
1397
|
-
|
|
1398
|
-
A reference inside a copied bare declaration is chased exactly like a local one: a same-file
|
|
1399
|
-
helper type is copied and recursed into, a relative/aliased import is followed the same way
|
|
1400
|
-
`"local"` already does, and a further bare import goes back through the checker (again skipping
|
|
1401
|
-
the svelte/svelte-elements allow-list). A reference that resolves to a TypeScript default-lib file
|
|
1402
|
-
(e.g. `Element`, `EventTarget` from `lib.dom.d.ts`) is left alone — it's already global and needs
|
|
1403
|
-
no import — the same way a local pass leaves an unimported name alone.
|
|
1404
|
-
|
|
1405
|
-
`@extendProps`/`@extends` inlining (replacing `import type { ButtonProps } from "./Button.svelte"`
|
|
1406
|
-
with a copy of `Button`'s own generated declaration) is out of scope for both `"local"` and
|
|
1407
|
-
`"all"`; that's a distinct, deferred mechanism, not covered here.
|
|
1408
|
-
|
|
1409
|
-
Copying from a large package can produce a large `.d.ts`: `svelte/elements`'s attribute-map
|
|
1410
|
-
interfaces (`HTMLButtonAttributes` and friends) are individually sizable, and every type they
|
|
1411
|
-
transitively reference gets copied too. Prefer `"local"` if you don't specifically need bare
|
|
1412
|
-
imports inlined.
|
|
1413
|
-
|
|
1414
|
-
A component with at least one inlined declaration skips the generated-text cache (its output now
|
|
1415
|
-
depends on another file's contents, not just its own source hash), though its *parse* is still
|
|
1416
|
-
cached as usual. Also available as `--types-inline=<local|all>` on the CLI; `--types-inline=false`
|
|
1417
|
-
resets to the default.
|
|
1418
|
-
|
|
1419
|
-
#### `markdownOptions.onAppend`
|
|
1420
|
-
|
|
1421
|
-
`markdownOptions.onAppend` fires on every heading, quote, paragraph, divider, and raw block written to the Markdown document, and receives the block's `type`, the in-progress `WriterMarkdown` document, and the full component map. Use it to inject extra content, e.g. a summary line under the `h1` title.
|
|
1422
|
-
|
|
1423
|
-
```js
|
|
1424
|
-
import pkg from "./package.json" with { type: "json" };
|
|
1425
|
-
|
|
1426
|
-
sveld({
|
|
1427
|
-
markdown: true,
|
|
1428
|
-
markdownOptions: {
|
|
1429
|
-
onAppend: (type, document, components) => {
|
|
1430
|
-
if (type === "h1") {
|
|
1431
|
-
document.append(
|
|
1432
|
-
"quote",
|
|
1433
|
-
`${components.size} components exported from ${pkg.name}@${pkg.version}.`,
|
|
1434
|
-
);
|
|
1435
|
-
}
|
|
1436
|
-
},
|
|
1437
|
-
},
|
|
1438
|
-
});
|
|
1439
|
-
```
|
|
1440
|
-
|
|
1441
|
-
```md
|
|
1442
|
-
# Component Index
|
|
1443
|
-
|
|
1444
|
-
> 1 components exported from sveld-scratch@1.0.0.
|
|
1445
|
-
```
|
|
1446
|
-
|
|
1447
|
-
#### `markdownOptions.outDir`
|
|
1448
|
-
|
|
1449
|
-
With `markdown: true`, `sveld` writes a single `COMPONENT_INDEX.md` at the project root, documenting every component.
|
|
1450
|
-
|
|
1451
|
-
Use `markdownOptions.outDir` to split that into one `<ModuleName>.md` file per component, plus an index `README.md` that links to each one (and holds the Exports section when `documentExports` is on — per-component sections live entirely in their own file):
|
|
1452
|
-
|
|
1453
|
-
```js
|
|
1454
|
-
sveld({
|
|
1455
|
-
markdown: true,
|
|
1456
|
-
markdownOptions: {
|
|
1457
|
-
// e.g. "docs/Button.md", "docs/README.md"
|
|
1458
|
-
outDir: "docs",
|
|
1459
|
-
},
|
|
1460
|
-
});
|
|
1461
|
-
```
|
|
1462
|
-
|
|
1463
|
-
`onAppend` still fires for both the index and every per-component file. `outFile` is ignored once `outDir` is set.
|
|
1464
|
-
|
|
1465
|
-
## Documenting Entry Exports
|
|
1466
|
-
|
|
1467
|
-
Most entry barrels re-export more than `.svelte` components. Set `documentExports: true` to add consts, functions, and types to the JSON and Markdown output.
|
|
1468
|
-
|
|
1469
|
-
```diff
|
|
1470
|
-
sveld({
|
|
1471
|
-
json: true,
|
|
1472
|
-
markdown: true,
|
|
1473
|
-
+ documentExports: true,
|
|
1474
|
-
})
|
|
1475
|
-
```
|
|
1476
|
-
|
|
1477
|
-
Example entry file:
|
|
1478
|
-
|
|
1479
|
-
```ts
|
|
1480
|
-
// src/index.ts
|
|
1481
|
-
export { default as Button } from "./Button.svelte";
|
|
1482
|
-
export { VERSION } from "./constants";
|
|
1483
|
-
export { clamp } from "./utils";
|
|
1484
|
-
export type { Theme } from "./types";
|
|
1485
|
-
```
|
|
1486
|
-
|
|
1487
|
-
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.
|
|
1488
|
-
|
|
1489
|
-
Nested barrels are followed too: `export { X } from "./dir"`, where `./dir/index.js` itself re-exports `.svelte` files, resolves `X` to the underlying component without needing `--glob`.
|
|
1490
|
-
|
|
1491
|
-
JSON adds `exports` and `totalExports`. Markdown adds an "Exports" section. Each item has `name`, `kind`, type text, optional JSDoc `description`, `source`, and — same as props — optional `@deprecated` and pass-through `tags`. A deprecated export's name is struck through in the Markdown table, same as a deprecated prop. See [`@deprecated`](#deprecated).
|
|
1492
|
-
|
|
1493
|
-
An overloaded function (repeated `export function f(...)` signatures, or an import re-exported through a barrel) always documents the implementation signature, i.e. the last declaration. An `enum` export's `type` is the literal union of its members' values (`"A" | "B"` for a string enum, `0 | 1` for a numeric one), falling back to the bare enum name when a member's value can't be determined (e.g. a computed initializer). A name the barrel exports itself (`export const X` or `export { X } from "./x"`) wins over the same name from an `export *`, as it does at runtime. When two `export *` statements bring in the same name from different modules (`export * from "./a"; export * from "./b"`), the name is ambiguous: the barrel doesn't export it at all, so `sveld` leaves it out and reports a [`sveld/export-ambiguous`](#diagnostic-codes) diagnostic naming both modules. Export it explicitly from the barrel (`export { format } from "./a"`) to pick one; that export wins silently. A namespace re-export (`export * as utils from "./utils"`, or an `import * as utils` the barrel re-exports) documents the one `const` export `utils`, with the wrapped module as its `source` and `typeof import("./utils.ts")` as its `type` (the module's path relative to the entry file, so the Markdown Type column names it); the names inside `./utils` aren't exports of the barrel, so they aren't listed. A re-exported default (`export { default as track } from "./track"`) is documented when that default export is a function, a class, or a named binding, with the JSDoc above `export default` as a named export would have; any other default (an object literal, say) is left out.
|
|
1494
|
-
|
|
1495
|
-
## JSON Output
|
|
1496
|
-
|
|
1497
|
-
When `json: true` is enabled, `sveld` emits a `COMPONENT_API.json` file with schema and generator metadata plus the parsed
|
|
1498
|
-
component API. For stable output, generated `events` arrays are emitted in deterministic sorted order.
|
|
1499
|
-
|
|
1500
|
-
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.
|
|
1501
|
-
|
|
1502
|
-
`sveld` also ships the schema as a package subpath, so you don't need network access to validate at build time:
|
|
1503
|
-
|
|
1504
|
-
```ts
|
|
1505
|
-
import schema from "sveld/schema/component-api.schema.json" with { type: "json" };
|
|
1506
|
-
```
|
|
1507
|
-
|
|
1508
|
-
`require.resolve("sveld/schema/component-api.schema.json")` works too. The `$id` in the schema still points at the `main` branch on GitHub for tooling that dereferences it by URL, but that URL always reflects the latest release; the copy packaged with your installed `sveld` version is the authoritative one for the output it produced.
|
|
1509
|
-
|
|
1510
|
-
```ts
|
|
1511
|
-
interface ComponentApiJson {
|
|
1512
|
-
schemaVersion: 1;
|
|
1513
|
-
generator: {
|
|
1514
|
-
name: string;
|
|
1515
|
-
version: string;
|
|
1516
|
-
svelteVersion: string;
|
|
1517
|
-
};
|
|
1518
|
-
total: number;
|
|
1519
|
-
components: ComponentDocApi[];
|
|
1520
|
-
// Present only when `documentExports` is enabled.
|
|
1521
|
-
totalExports?: number;
|
|
1522
|
-
exports?: EntryExport[];
|
|
1523
|
-
}
|
|
1524
|
-
|
|
1525
|
-
interface EntryExport {
|
|
1526
|
-
name: string;
|
|
1527
|
-
kind: "const" | "let" | "var" | "function" | "class" | "type" | "interface" | "enum";
|
|
1528
|
-
type?: string;
|
|
1529
|
-
value?: string;
|
|
1530
|
-
description?: string;
|
|
1531
|
-
deprecated?: string | true;
|
|
1532
|
-
tags?: Array<{ name: string; body: string }>;
|
|
1533
|
-
source?: string;
|
|
1534
|
-
isTypeOnly: boolean;
|
|
1535
|
-
}
|
|
1536
|
-
|
|
1537
|
-
interface SourceRange {
|
|
1538
|
-
start: SourcePosition;
|
|
1539
|
-
end: SourcePosition;
|
|
1540
|
-
}
|
|
1541
|
-
|
|
1542
|
-
interface SourcePosition {
|
|
1543
|
-
line: number;
|
|
1544
|
-
column: number;
|
|
1545
|
-
}
|
|
1546
|
-
|
|
1547
|
-
interface ComponentDocApi {
|
|
1548
|
-
moduleName: string;
|
|
1549
|
-
filePath: string;
|
|
1550
|
-
source?: SourceRange;
|
|
1551
|
-
syntaxMode: "legacy" | "runes";
|
|
1552
|
-
scriptLanguage?: "js" | "ts";
|
|
1553
|
-
props: ComponentProp[];
|
|
1554
|
-
moduleExports: ComponentProp[];
|
|
1555
|
-
slots: ComponentSlot[];
|
|
1556
|
-
events: ComponentEvent[];
|
|
1557
|
-
typedefs: TypeDef[];
|
|
1558
|
-
generics: null | [name: string, type: string];
|
|
1559
|
-
rest_props?: RestProps;
|
|
1560
|
-
extends?: { interface: string; import: string };
|
|
1561
|
-
componentComment?: string;
|
|
1562
|
-
componentCommentSource?: SourceRange;
|
|
1563
|
-
contexts?: ComponentContext[];
|
|
1564
|
-
customElementTag?: string;
|
|
1565
|
-
}
|
|
1566
|
-
|
|
1567
|
-
interface ComponentProp {
|
|
1568
|
-
name: string;
|
|
1569
|
-
localName?: string;
|
|
1570
|
-
// "re-export" and "class" are module exports only.
|
|
1571
|
-
kind: "let" | "const" | "function" | "re-export" | "class";
|
|
1572
|
-
constant: boolean;
|
|
1573
|
-
type?: string;
|
|
1574
|
-
typeSource?: "typescript" | "jsdoc" | "default" | "inferred" | "unknown";
|
|
1575
|
-
value?: string;
|
|
1576
|
-
defaultValue?: {
|
|
1577
|
-
raw: string;
|
|
1578
|
-
kind: "literal" | "array" | "object" | "expression" | "function" | "unknown";
|
|
1579
|
-
value?: unknown;
|
|
1580
|
-
};
|
|
1581
|
-
description?: string;
|
|
1582
|
-
params?: Array<{ name: string; type: string; description?: string; optional?: boolean }>;
|
|
1583
|
-
returnType?: string;
|
|
1584
|
-
isFunction: boolean;
|
|
1585
|
-
isFunctionDeclaration: boolean;
|
|
1586
|
-
isRequired: boolean;
|
|
1587
|
-
reactive: boolean;
|
|
1588
|
-
binding?: "readonly" | "writable";
|
|
1589
|
-
bindable?: true;
|
|
1590
|
-
// Set when `kind` is "class".
|
|
1591
|
-
members?: Array<{
|
|
1592
|
-
kind: "constructor" | "method" | "property";
|
|
1593
|
-
name: string;
|
|
1594
|
-
type?: string;
|
|
1595
|
-
params?: Array<{ name: string; type: string; description?: string; optional?: boolean }>;
|
|
1596
|
-
returnType?: string;
|
|
1597
|
-
typeParameters?: string;
|
|
1598
|
-
static?: true;
|
|
1599
|
-
readonly?: true;
|
|
1600
|
-
optional?: true;
|
|
1601
|
-
abstract?: true;
|
|
1602
|
-
description?: string;
|
|
1603
|
-
deprecated?: string | true;
|
|
1604
|
-
tags?: Array<{ name: string; body: string }>;
|
|
1605
|
-
}>;
|
|
1606
|
-
abstract?: true;
|
|
1607
|
-
source?: SourceRange;
|
|
1608
|
-
}
|
|
1609
|
-
|
|
1610
|
-
interface ComponentSlot {
|
|
1611
|
-
name?: string | null;
|
|
1612
|
-
default: boolean;
|
|
1613
|
-
fallback?: string;
|
|
1614
|
-
slot_props?: string;
|
|
1615
|
-
description?: string;
|
|
1616
|
-
tags?: Array<{ name: string; body: string }>;
|
|
1617
|
-
source?: SourceRange;
|
|
1618
|
-
}
|
|
1619
|
-
|
|
1620
|
-
type ComponentEvent =
|
|
1621
|
-
| {
|
|
1622
|
-
type: "forwarded";
|
|
1623
|
-
name: string;
|
|
1624
|
-
element: string;
|
|
1625
|
-
description?: string;
|
|
1626
|
-
detail?: string;
|
|
1627
|
-
source?: SourceRange;
|
|
1628
|
-
}
|
|
1629
|
-
| {
|
|
1630
|
-
type: "dispatched";
|
|
1631
|
-
name: string;
|
|
1632
|
-
detail?: string;
|
|
1633
|
-
description?: string;
|
|
1634
|
-
source?: SourceRange;
|
|
1635
|
-
};
|
|
1636
|
-
```
|
|
1637
|
-
|
|
1638
|
-
`source` fields appear only when the Svelte or JavaScript AST has stable positions. They omit source text and raw character offsets.
|
|
1639
|
-
|
|
1640
|
-
`SourcePosition.line` is 1-based. `SourcePosition.column` is 0-based.
|
|
1641
|
-
|
|
1642
|
-
Prop metadata is additive and keeps the older public fields:
|
|
1643
|
-
|
|
1644
|
-
- `name` is always the public prop name. For runes `$props()` aliases such as `let { class: className } = $props()`, `localName` is emitted only when the local binding differs.
|
|
1645
|
-
- `typeSource` identifies the conservative source of the emitted `type`: TypeScript annotation, JSDoc, initializer/default inference, other parser inference, or unknown fallback.
|
|
1646
|
-
- `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.
|
|
1647
|
-
- `bindable: true` is emitted only for props explicitly declared with Svelte 5 `$bindable(...)`. Missing `bindable` should be treated as false.
|
|
1648
|
-
|
|
1649
|
-
## Custom Elements Manifest
|
|
1650
|
-
|
|
1651
|
-
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.
|
|
1652
|
-
|
|
1653
|
-
```diff
|
|
1654
|
-
sveld({
|
|
1655
|
-
+ customElements: true,
|
|
1656
|
-
})
|
|
1657
|
-
```
|
|
1658
|
-
|
|
1659
|
-
- **`customElements`** (boolean, optional): Generate `custom-elements.json`.
|
|
1660
|
-
- **`customElementsOptions.outFile`** (string, optional, default: `"custom-elements.json"`): Override the output path.
|
|
1661
|
-
- Also available as the `--custom-elements` CLI flag.
|
|
1662
|
-
|
|
1663
|
-
Each exported component becomes one `javascript-module` with a class declaration:
|
|
1664
|
-
|
|
1665
|
-
- **Members** — every `export let`/`const` prop becomes a `ClassField` (`name`, `type.text`, `default`, `description`, `deprecated`; `readonly: true` for an `export const`). An accessor prop (`export function`) becomes a `ClassMethod` (`name`, `static: false`, `parameters`, `return.type.text`) instead — see [Accessor methods](#accessor-methods) below.
|
|
1666
|
-
- **Attributes** — every prop becomes an attribute (Svelte's custom-element runtime observes one for every prop by default), excluding `export function` accessors. The attribute name is `prop.toLowerCase()` by default, or the `customElement.props.<name>.attribute` config when set — see [Object-form `customElement` config](#object-form-customelement-config) below. Props that collide on the same attribute name keep the first (in declaration order); the rest are skipped with a console warning, since which one wins at runtime is ambiguous.
|
|
1667
|
-
- **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.
|
|
1668
|
-
- **Slots** — named and default slots, with descriptions. The default slot's `name` is `""`, matching the CEM convention.
|
|
1669
|
-
- **`cssParts`/`cssProperties`** — from `@csspart`/`@cssprop` JSDoc tags — see [CSS parts and custom properties](#css-parts-and-custom-properties) below.
|
|
1670
|
-
|
|
1671
|
-
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.
|
|
1672
|
-
|
|
1673
|
-
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.
|
|
1674
|
-
|
|
1675
|
-
### Object-form `customElement` config
|
|
1676
|
-
|
|
1677
|
-
The object form of `<svelte:options customElement={{ ... }} />` is read in full, not just `tag`:
|
|
1678
|
-
|
|
1679
|
-
```svelte
|
|
1680
|
-
<svelte:options
|
|
1681
|
-
customElement={{
|
|
1682
|
-
tag: "x-widget",
|
|
1683
|
-
shadow: "none",
|
|
1684
|
-
props: {
|
|
1685
|
-
variant: { attribute: "data-variant" },
|
|
1686
|
-
active: { reflect: true },
|
|
1687
|
-
tags: { type: "Array" }
|
|
1688
|
-
},
|
|
1689
|
-
extend: (customElementConstructor) => customElementConstructor
|
|
1690
|
-
}}
|
|
1691
|
-
/>
|
|
1692
|
-
```
|
|
1693
|
-
|
|
1694
|
-
| Config | Effect on the manifest |
|
|
1695
|
-
| --- | --- |
|
|
1696
|
-
| `props.<name>.attribute` | Overrides that prop's attribute name (default: `name.toLowerCase()`). `attribute: false` omits the prop's attribute entirely. |
|
|
1697
|
-
| `props.<name>.reflect` | Adds `reflects: true` to that prop's attribute. |
|
|
1698
|
-
| `props.<name>.type` | `"Array"`/`"Object"` appends a note to the attribute's description that the value is JSON-serialized (matching Svelte's runtime `JSON.stringify`/`JSON.parse` for those types); the attribute's `type.text` is still the prop's own TS type, not this config value. |
|
|
1699
|
-
| `shadow`, `extend` | Parsed and available on the raw `ParsedComponent.customElement` (Node API), but don't affect the manifest. |
|
|
1700
|
-
|
|
1701
|
-
This full config (`tag`, `shadow`, `props`, `extend`) is also on `ParsedComponent.customElement` in the JSON output (`COMPONENT_API.json`), alongside the existing `customElementTag` shorthand.
|
|
1702
|
-
|
|
1703
|
-
### Accessor methods
|
|
1704
|
-
|
|
1705
|
-
An `export function` prop (a Svelte accessor, e.g. `export function focus() { ... }`) becomes a `ClassMethod`, not a `ClassField`, and is excluded from `attributes` (accessors aren't part of Svelte's props definition). `parameters`/`return` come from `@param`/`@returns` JSDoc when present, otherwise from splitting the function's TypeScript signature text (e.g. `(id: string) => boolean`).
|
|
1706
|
-
|
|
1707
|
-
### CSS parts and custom properties
|
|
1708
|
-
|
|
1709
|
-
Document shadow-DOM styling hooks with `@csspart`/`@cssprop` (alias `@cssproperty`) tags in the component's own JSDoc comment (the same comment block `@slot` tags go in):
|
|
1710
|
-
|
|
1711
|
-
```js
|
|
1712
|
-
/**
|
|
1713
|
-
* @csspart header - Styles the header region.
|
|
1714
|
-
* @cssprop {Color} [--card-background=white] - Background color of the card.
|
|
1715
|
-
* @cssprop --card-border-color - Border color of the card.
|
|
1716
|
-
*/
|
|
1717
|
-
```
|
|
1718
|
-
|
|
1719
|
-
`@cssprop`'s `{type}` and `[--name=default]` are both optional, following the [Custom Elements Manifest analyzer](https://custom-elements-manifest.open-wc.org/analyzer/getting-started/#css-custom-properties) grammar. These populate `cssParts`/`cssProperties` on the class declaration, and `ParsedComponent.cssParts`/`cssProperties` in the JSON output; the Markdown writer renders them as two extra tables when present.
|
|
1720
|
-
|
|
1721
|
-
### Consuming the manifest
|
|
1722
|
-
|
|
1723
|
-
Most tools discover `custom-elements.json` through a `customElements` field in `package.json`, pointing at the generated file:
|
|
1724
|
-
|
|
1725
|
-
```json
|
|
1726
|
-
{
|
|
1727
|
-
"customElements": "custom-elements.json"
|
|
1728
|
-
}
|
|
1729
|
-
```
|
|
1730
|
-
|
|
1731
|
-
With that in place:
|
|
1732
|
-
|
|
1733
|
-
- 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.
|
|
1734
|
-
- [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`).
|
|
1735
|
-
- 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.
|
|
1736
|
-
|
|
1737
|
-
## llms.txt Output
|
|
1738
|
-
|
|
1739
|
-
Set `llms: true` to emit an [`llms.txt`](https://llmstxt.org) / `llms-full.txt` pair: `llms.txt` is an index of every exported component (one link plus a one-line summary each), and `llms-full.txt` is the flattened full reference (every component's Props, Bindings, Events, Slots/Snippets, Typedefs, and Module exports, as terse Markdown tables). Props and Events Description columns include `@since`/`@example` tags, same as `COMPONENT_INDEX.md`. Fenced code in a Description cell is printed after its table as a regular fenced block, with `(code below)` left in the cell.
|
|
1740
|
-
|
|
1741
|
-
```diff
|
|
1742
|
-
sveld({
|
|
1743
|
-
+ llms: true,
|
|
1744
|
-
})
|
|
1745
|
-
```
|
|
1746
|
-
|
|
1747
|
-
- **`llms`** (boolean, optional): Generate `llms.txt` and `llms-full.txt`. Also available as the `--llms` CLI flag.
|
|
1748
|
-
- **`llmsOptions`** (object, optional):
|
|
1749
|
-
- **`outDir`** (string, optional): Directory (relative to the project root) both files are written into. Defaults to the project root.
|
|
1750
|
-
- **`linkBase`** (string, optional, default: `""`): Prefixed to each component's link in `llms.txt`, e.g. `[Button](<linkBase>/Button)`. Set this to your published docs site's base path.
|
|
1751
|
-
- **`title`** (string, optional, default: the `"name"` field from `package.json`): The `# <title>` heading both files start with.
|
|
1752
|
-
- **`summary`** (string, optional, default: the `"description"` field from `package.json`): The `> <summary>` blockquote under the title. Omitted when neither is set.
|
|
1753
|
-
|
|
1754
|
-
Each component's one-line summary in `llms.txt` is the first sentence of its [`@component` comment](#component-comments), falling back to `"Component"`. Set `documentExports: true` to also list entry-barrel exports (consts, functions, types) in `llms.txt` under an `## Exports` heading.
|
|
1755
|
-
|
|
1756
|
-
## Custom Writers
|
|
1757
|
-
|
|
1758
|
-
`json`, `markdown`, `types`, and `custom-elements` are all built on the same
|
|
1759
|
-
extensibility point: a small writer registry that third parties can use to
|
|
1760
|
-
add new output formats without a core PR. This is a stable, public part of
|
|
1761
|
-
sveld's API — the four built-in writers register through it in
|
|
1762
|
-
`src/writer/built-in-writers.ts`, there is no separate "internal" path.
|
|
1763
|
-
|
|
1764
|
-
### The `OutputWriter` contract
|
|
1765
|
-
|
|
1766
|
-
```ts
|
|
1767
|
-
interface OutputWriter<TOptions = unknown> {
|
|
1768
|
-
name: string;
|
|
1769
|
-
/** Which component set this writer expects. @default "exported" */
|
|
1770
|
-
componentSet?: "exported" | "all";
|
|
1771
|
-
write(components: ComponentDocs, options: TOptions): Promise<unknown> | unknown;
|
|
1772
|
-
}
|
|
1773
|
-
```
|
|
1774
|
-
|
|
1775
|
-
- **`name`** is how the plugin's `additionalWriters` option (and anything else
|
|
1776
|
-
that calls `getWriter`) looks the writer up. Pick something that won't
|
|
1777
|
-
collide with `types` / `json` / `markdown` / `custom-elements` or another
|
|
1778
|
-
third-party writer.
|
|
1779
|
-
- **`componentSet`** picks which map `write` receives:
|
|
1780
|
-
- `"exported"` (the default) — components reachable from the entry barrel,
|
|
1781
|
-
keyed by `moduleName`. This is what `json` / `markdown` /
|
|
1782
|
-
`custom-elements` use.
|
|
1783
|
-
- `"all"` — every `--glob`-discovered `.svelte` file, keyed by resolved
|
|
1784
|
-
`filePath` instead of `moduleName` (two files in different directories
|
|
1785
|
-
can share a basename). This is what `types` uses.
|
|
1786
|
-
- **`write`** receives a `ComponentDocs` (`Map<string, ComponentDocApi>`) —
|
|
1787
|
-
the same `ComponentDocApi` shape documented in [JSON Output](#json-output).
|
|
1788
|
-
Don't iterate the raw map yourself; call `buildComponentApiDocument(components, { entryExports })`
|
|
1789
|
-
(also exported from `sveld`) to get the sorted, `diagnostics`-stripped,
|
|
1790
|
-
schema-versioned document the built-in writers build from. It's memoized
|
|
1791
|
-
per `components` map, so calling it more than once in one run is free.
|
|
1792
|
-
- **`options`** always carries `dryRun` alongside whatever fields the writer
|
|
1793
|
-
itself defines. See [The `dryRun` contract](#the-dryrun-contract) below.
|
|
1794
|
-
|
|
1795
|
-
### Registering a writer
|
|
1796
|
-
|
|
1797
|
-
```ts
|
|
1798
|
-
import { registerWriter } from "sveld";
|
|
1799
|
-
|
|
1800
|
-
registerWriter({
|
|
1801
|
-
name: "my-format",
|
|
1802
|
-
write(components, options) {
|
|
1803
|
-
// ...
|
|
1804
|
-
},
|
|
1805
|
-
});
|
|
1806
|
-
```
|
|
1807
|
-
|
|
1808
|
-
`registerWriter` is a side effect — call it once, before the plugin or CLI
|
|
1809
|
-
runs, e.g. at the top of `vite.config.ts` or `sveld.config.ts`, or in a file
|
|
1810
|
-
either of those imports.
|
|
1811
|
-
|
|
1812
|
-
Registering a `name` that's already taken — a built-in writer's name, or
|
|
1813
|
-
another `registerWriter` call — throws instead of silently overwriting it.
|
|
1814
|
-
Pass `{ replace: true }` as a second argument when overwriting is intentional
|
|
1815
|
-
(e.g. re-registering a writer module during development):
|
|
1816
|
-
|
|
1817
|
-
```ts
|
|
1818
|
-
registerWriter(
|
|
1819
|
-
{
|
|
1820
|
-
name: "my-format",
|
|
1821
|
-
write(components, options) {
|
|
1822
|
-
// ...
|
|
1823
|
-
},
|
|
1824
|
-
},
|
|
1825
|
-
{ replace: true },
|
|
1826
|
-
);
|
|
1827
|
-
```
|
|
1828
|
-
|
|
1829
|
-
### The `dryRun` contract
|
|
1830
|
-
|
|
1831
|
-
Every writer — built-in or `additionalWriters` — receives `dryRun: true` in
|
|
1832
|
-
its `options` when the run is `sveld --dry-run` (or `{ dryRun: true }` from
|
|
1833
|
-
the programmatic API). A writer must check it and skip touching disk; sveld
|
|
1834
|
-
does not do this for you:
|
|
1835
|
-
|
|
1836
|
-
```ts
|
|
1837
|
-
import { writeFileSync } from "node:fs";
|
|
1838
|
-
|
|
1839
|
-
registerWriter({
|
|
1840
|
-
name: "my-format",
|
|
1841
|
-
write(components, options: { outFile: string; dryRun?: boolean }) {
|
|
1842
|
-
if (options.dryRun) {
|
|
1843
|
-
console.log(`would write "${options.outFile}"`);
|
|
1844
|
-
return;
|
|
1845
|
-
}
|
|
1846
|
-
writeFileSync(options.outFile, "...");
|
|
1847
|
-
},
|
|
1848
|
-
});
|
|
1849
|
-
```
|
|
1850
|
-
|
|
1851
|
-
A writer that throws — synchronously or from a rejected promise — has its
|
|
1852
|
-
error re-thrown as `sveld: writer "<name>" failed: <message>`, with the
|
|
1853
|
-
original error attached as `cause`, so a broken third-party writer never
|
|
1854
|
-
fails silently or without saying which one.
|
|
1855
|
-
|
|
1856
|
-
### Running it via the plugin
|
|
1857
|
-
|
|
1858
|
-
```ts
|
|
1859
|
-
// vite.config.ts
|
|
1860
|
-
import sveld from "sveld";
|
|
1861
|
-
import "./writers/my-format"; // calls registerWriter as a side effect
|
|
1862
|
-
|
|
1863
|
-
export default {
|
|
1864
|
-
plugins: [
|
|
1865
|
-
sveld({
|
|
1866
|
-
additionalWriters: {
|
|
1867
|
-
"my-format": { outFile: "MY_FORMAT.txt" },
|
|
1868
|
-
},
|
|
1869
|
-
}),
|
|
1870
|
-
],
|
|
1871
|
-
};
|
|
1872
|
-
```
|
|
1873
|
-
|
|
1874
|
-
`additionalWriters` is keyed by the writer's registered `name`; the value is
|
|
1875
|
-
whatever options object that writer's `write` expects. An unknown name logs a
|
|
1876
|
-
warning and is skipped rather than failing the build. Registered writers run
|
|
1877
|
-
alongside whichever built-in outputs (`types` / `json` / `markdown` /
|
|
1878
|
-
`customElements`) are also enabled, and the same option is available from the
|
|
1879
|
-
programmatic Node API and from `sveld.config.ts` (`additionalWriters` lives on
|
|
1880
|
-
the options shared by all three entry points).
|
|
1881
|
-
|
|
1882
|
-
### Worked example: a `components.txt` name-list writer
|
|
1883
|
-
|
|
1884
|
-
Looking for an `llms.txt` writer specifically? Sveld ships one — see [llms.txt Output](#llmstxt-output). This example is a much smaller custom writer, just to show the pattern: a plain-text list of exported component names, needing nothing beyond `ComponentApiDocument`.
|
|
1885
|
-
|
|
1886
|
-
```ts
|
|
1887
|
-
// writers/components-txt-writer.ts
|
|
1888
|
-
import { writeFileSync } from "node:fs";
|
|
1889
|
-
import { join } from "node:path";
|
|
1890
|
-
import { buildComponentApiDocument, registerWriter } from "sveld";
|
|
1891
|
-
|
|
1892
|
-
interface ComponentsTxtWriterOptions {
|
|
1893
|
-
outFile?: string;
|
|
1894
|
-
}
|
|
1895
|
-
|
|
1896
|
-
registerWriter<ComponentsTxtWriterOptions>({
|
|
1897
|
-
name: "components-txt",
|
|
1898
|
-
componentSet: "exported",
|
|
1899
|
-
write(components, options = {}) {
|
|
1900
|
-
const document = buildComponentApiDocument(components);
|
|
1901
|
-
const rendered = document.components.map((component) => component.moduleName).join("\n");
|
|
1902
|
-
|
|
1903
|
-
writeFileSync(join(process.cwd(), options.outFile ?? "components.txt"), rendered);
|
|
1904
|
-
},
|
|
1905
|
-
});
|
|
1906
|
-
```
|
|
1907
|
-
|
|
1908
|
-
```ts
|
|
1909
|
-
// vite.config.ts
|
|
1910
|
-
import sveld from "sveld";
|
|
1911
|
-
import "./writers/components-txt-writer";
|
|
1912
|
-
|
|
1913
|
-
export default {
|
|
1914
|
-
plugins: [
|
|
1915
|
-
sveld({
|
|
1916
|
-
additionalWriters: {
|
|
1917
|
-
"components-txt": { outFile: "components.txt" },
|
|
1918
|
-
},
|
|
1919
|
-
}),
|
|
1920
|
-
],
|
|
1921
|
-
};
|
|
1922
|
-
```
|
|
1923
|
-
|
|
1924
|
-
Running a build now produces a `components.txt` alongside the usual output,
|
|
1925
|
-
listing one exported component name per line.
|
|
1926
|
-
|
|
1927
|
-
## API Reference
|
|
1928
|
-
|
|
1929
|
-
A JSDoc block documents the declaration below it. Blank lines and ordinary comments may sit between the two, such as a `// biome-ignore ...` or `/* istanbul ignore next */` line.
|
|
1930
|
-
|
|
1931
|
-
### `reactive`
|
|
1932
|
-
|
|
1933
|
-
The `reactive` field in generated JSON is a heuristic. It does not fully answer whether a parent can use `bind:prop` in Svelte.
|
|
1934
|
-
|
|
1935
|
-
`sveld` marks `reactive: true` when it finds internal evidence that a prop is writable, including:
|
|
1936
|
-
|
|
1937
|
-
- the prop is assigned or mutated inside the component
|
|
1938
|
-
- the prop is marked bindable in runes mode with `$bindable(...)`
|
|
1939
|
-
- the prop is used as the target of `bind:*` on an element or child component
|
|
1940
|
-
- wrapper-forwarded bindings such as `bind:value`, `bind:selected`, and `bind:ref`
|
|
1941
|
-
|
|
1942
|
-
Local variables or parameters that shadow a prop name do not count as writes to the exported prop.
|
|
1943
|
-
|
|
1944
|
-
`reactive: false` means `sveld` found no such evidence. It does not imply that parent-side `bind:` usage is impossible.
|
|
1945
|
-
|
|
1946
|
-
### `binding`
|
|
1947
|
-
|
|
1948
|
-
The optional `binding` field documents a prop's intended `bind:` contract. It is separate from `reactive` and is never inferred from internal writes or `$bindable()`.
|
|
1949
|
-
|
|
1950
|
-
Use `@bindable readonly` for component-owned or output-style bindings where the consumer binds to the current value emitted by the component:
|
|
1951
|
-
|
|
1952
|
-
```svelte
|
|
1953
|
-
<script>
|
|
1954
|
-
/**
|
|
1955
|
-
* Bind to the current value emitted by the component.
|
|
1956
|
-
* @bindable readonly
|
|
1957
|
-
*/
|
|
1958
|
-
export let size = undefined;
|
|
1959
|
-
</script>
|
|
1960
|
-
```
|
|
1961
|
-
|
|
1962
|
-
Use `@bindable writable` for two-way or shared state bindings where either the consumer or component may control the value:
|
|
1963
|
-
|
|
1964
|
-
```svelte
|
|
1965
|
-
<script>
|
|
1966
|
-
/**
|
|
1967
|
-
* Bind to state controlled by either the consumer or component.
|
|
1968
|
-
* @bindable writable
|
|
1969
|
-
*/
|
|
1970
|
-
export let open = false;
|
|
1971
|
-
</script>
|
|
1972
|
-
```
|
|
1973
|
-
|
|
1974
|
-
Generated JSON includes `"binding": "readonly"` or `"binding": "writable"` for annotated props. Unannotated props omit the field.
|
|
1975
|
-
|
|
1976
|
-
This is documentation only. Generated `.svelte.d.ts` prop types do not change. TypeScript cannot express Svelte binding direction reliably.
|
|
1977
|
-
|
|
1978
|
-
### `@type`
|
|
1979
|
-
|
|
1980
|
-
Without a `@type` annotation, `sveld` infers the primitive type for a prop:
|
|
1981
|
-
|
|
1982
|
-
```js
|
|
1983
|
-
export let kind = "primary";
|
|
1984
|
-
// inferred type: "string"
|
|
1985
|
-
```
|
|
1986
|
-
|
|
1987
|
-
For template literal default values, `sveld` infers the type as `string`:
|
|
1988
|
-
|
|
1989
|
-
```js
|
|
1990
|
-
export let id = `ccs-${Math.random().toString(36)}`;
|
|
1991
|
-
// inferred type: "string"
|
|
1992
|
-
```
|
|
1993
|
-
|
|
1994
|
-
Use the `@type` tag to document the type explicitly. In the example below, `kind` is a string union.
|
|
1995
|
-
|
|
1996
|
-
For `lang="ts"` components, prefer native TypeScript annotations when you already have them. `@type` still helps in JavaScript components, for overriding inferred types, and when the AST cannot recover a sharper type.
|
|
1997
|
-
|
|
1998
|
-
**Signature:**
|
|
1999
|
-
|
|
2000
|
-
```js
|
|
2001
|
-
/**
|
|
2002
|
-
* Optional description
|
|
2003
|
-
* @type {Type}
|
|
2004
|
-
*/
|
|
2005
|
-
```
|
|
2006
|
-
|
|
2007
|
-
**Example:**
|
|
2008
|
-
|
|
2009
|
-
```svelte
|
|
2010
|
-
<script>
|
|
2011
|
-
let {
|
|
2012
|
-
/**
|
|
2013
|
-
* Specify the kind of button
|
|
2014
|
-
* @type {"primary" | "secondary" | "tertiary"}
|
|
2015
|
-
*/
|
|
2016
|
-
kind = "primary",
|
|
2017
|
-
/**
|
|
2018
|
-
* Specify the Carbon icon to render
|
|
2019
|
-
* @type {typeof import("carbon-icons-svelte").CarbonIcon}
|
|
2020
|
-
*/
|
|
2021
|
-
renderIcon = Close20,
|
|
2022
|
-
} = $props();
|
|
2023
|
-
</script>
|
|
2024
|
-
```
|
|
2025
|
-
|
|
2026
|
-
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.
|
|
2027
|
-
|
|
2028
|
-
A declaration-level `@type` naming an object `@typedef` (or an inline `{ ... }` type) types the whole `$props()` object instead, the form Svelte's docs recommend for JavaScript components. Each prop takes its type, optionality, and description from the matching member:
|
|
2029
|
-
|
|
2030
|
-
```svelte
|
|
2031
|
-
<script>
|
|
2032
|
-
/**
|
|
2033
|
-
* @typedef {object} Props
|
|
2034
|
-
* @property {string} title Card title
|
|
2035
|
-
* @property {boolean} [elevated=false] Raise the card
|
|
2036
|
-
*/
|
|
2037
|
-
|
|
2038
|
-
/** @type {Props} */
|
|
2039
|
-
let { title, elevated = false } = $props();
|
|
2040
|
-
</script>
|
|
2041
|
-
```
|
|
2042
|
-
|
|
2043
|
-
<details>
|
|
2044
|
-
<summary>Svelte 3/4 (legacy) syntax</summary>
|
|
2045
|
-
|
|
2046
|
-
```svelte
|
|
2047
|
-
<script>
|
|
2048
|
-
/**
|
|
2049
|
-
* Specify the kind of button
|
|
2050
|
-
* @type {"primary" | "secondary" | "tertiary"}
|
|
2051
|
-
*/
|
|
2052
|
-
export let kind = "primary";
|
|
2053
|
-
|
|
2054
|
-
/**
|
|
2055
|
-
* Specify the Carbon icon to render
|
|
2056
|
-
* @type {typeof import("carbon-icons-svelte").CarbonIcon}
|
|
2057
|
-
*/
|
|
2058
|
-
export let renderIcon = Close20;
|
|
2059
|
-
</script>
|
|
2060
|
-
```
|
|
2061
|
-
|
|
2062
|
-
</details>
|
|
2063
|
-
|
|
2064
|
-
#### Importing types
|
|
2065
|
-
|
|
2066
|
-
`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:
|
|
2067
|
-
|
|
2068
|
-
- `import("module").Type` references an exported **type**.
|
|
2069
|
-
- `typeof import("module").value` references the type of an exported **value**.
|
|
2070
|
-
- `import("svelte").ComponentProps<...>` and other utility types compose with imports.
|
|
2071
|
-
|
|
2072
|
-
This keeps third-party types (Svelte stores, another component's props, library types) out of runtime imports while still showing up in IntelliSense for consumers.
|
|
2073
|
-
|
|
2074
|
-
**Example:**
|
|
2075
|
-
|
|
2076
|
-
```svelte
|
|
2077
|
-
<script>
|
|
2078
|
-
/**
|
|
2079
|
-
* A store from `svelte/store`. No top-level `import` required.
|
|
2080
|
-
* @type {import("svelte/store").Writable<string>}
|
|
2081
|
-
*/
|
|
2082
|
-
export let value;
|
|
2083
|
-
|
|
2084
|
-
/**
|
|
2085
|
-
* `typeof import(...)` references the type of a value export, here the
|
|
2086
|
-
* `writable` factory itself rather than a type it exports.
|
|
2087
|
-
* @type {typeof import("svelte/store").writable}
|
|
2088
|
-
*/
|
|
2089
|
-
export let createStore;
|
|
2090
|
-
|
|
2091
|
-
/**
|
|
2092
|
-
* Reuse another component's props with Svelte's `ComponentProps` utility.
|
|
2093
|
-
* @type {import("svelte").ComponentProps<import("svelte").SvelteComponent>}
|
|
2094
|
-
*/
|
|
2095
|
-
export let buttonProps;
|
|
2096
|
-
|
|
2097
|
-
/**
|
|
2098
|
-
* `import(...)` works inside a `@typedef` too, which helps when typing values shared via `setContext` / `getContext`.
|
|
2099
|
-
*
|
|
2100
|
-
* @typedef {{ rows: import("svelte/store").Writable<string[]>; selected: import("svelte/store").Readable<number> }} TableContext
|
|
2101
|
-
*/
|
|
2102
|
-
|
|
2103
|
-
/** @type {TableContext} */
|
|
2104
|
-
export let context;
|
|
2105
|
-
</script>
|
|
2106
|
-
```
|
|
2107
|
-
|
|
2108
|
-
Output:
|
|
2109
|
-
|
|
2110
|
-
```ts
|
|
2111
|
-
export interface TableContext {
|
|
2112
|
-
rows: import("svelte/store").Writable<string[]>;
|
|
2113
|
-
selected: import("svelte/store").Readable<number>;
|
|
2114
|
-
}
|
|
2115
|
-
|
|
2116
|
-
export type ComponentProps = {
|
|
2117
|
-
/** @default undefined */
|
|
2118
|
-
value: import("svelte/store").Writable<string>;
|
|
2119
|
-
/** @default undefined */
|
|
2120
|
-
createStore: typeof import("svelte/store").writable;
|
|
2121
|
-
/** @default undefined */
|
|
2122
|
-
buttonProps: import("svelte").ComponentProps<import("svelte").SvelteComponent>;
|
|
2123
|
-
/** @default undefined */
|
|
2124
|
-
context: TableContext;
|
|
2125
|
-
};
|
|
2126
|
-
```
|
|
2127
|
-
|
|
2128
|
-
#### Prefer `unknown` over `any`
|
|
2129
|
-
|
|
2130
|
-
When a prop accepts data whose shape you do not know ahead of time, annotate it as `unknown` rather than `any`. `sveld` preserves either keyword in the emitted prop type, but they behave differently for consumers: `unknown` forces a narrowing check before use; `any` disables type checking everywhere the value flows. Reserve `any` for real escape hatches.
|
|
2131
|
-
|
|
2132
|
-
**Example:**
|
|
2133
|
-
|
|
2134
|
-
```svelte
|
|
2135
|
-
<script>
|
|
2136
|
-
/**
|
|
2137
|
-
* A value of unknown shape. Prefer `unknown` over `any`: consumers must
|
|
2138
|
-
* narrow it before use instead of silently opting out of type checking.
|
|
2139
|
-
*
|
|
2140
|
-
* @type {unknown}
|
|
2141
|
-
*/
|
|
2142
|
-
export let payload;
|
|
2143
|
-
|
|
2144
|
-
/**
|
|
2145
|
-
* An escape hatch typed as `any`, shown for contrast. `any` disables type
|
|
2146
|
-
* checking everywhere it flows, so reach for `unknown` at boundaries instead.
|
|
2147
|
-
*
|
|
2148
|
-
* @type {any}
|
|
2149
|
-
*/
|
|
2150
|
-
export let raw;
|
|
2151
|
-
</script>
|
|
2152
|
-
```
|
|
2153
|
-
|
|
2154
|
-
Output:
|
|
2155
|
-
|
|
2156
|
-
```ts
|
|
2157
|
-
export type ComponentProps = {
|
|
2158
|
-
/** @default undefined */
|
|
2159
|
-
payload: unknown;
|
|
2160
|
-
/** @default undefined */
|
|
2161
|
-
raw: any;
|
|
2162
|
-
};
|
|
2163
|
-
```
|
|
2164
|
-
|
|
2165
|
-
Consumers must narrow an `unknown` prop before using it, while an `any` prop silently accepts anything:
|
|
2166
|
-
|
|
2167
|
-
```ts
|
|
2168
|
-
function handle(props: ComponentProps) {
|
|
2169
|
-
// Error: 'payload' is of type 'unknown'. Narrow it first.
|
|
2170
|
-
props.payload.toUpperCase();
|
|
2171
|
-
|
|
2172
|
-
if (typeof props.payload === "string") {
|
|
2173
|
-
props.payload.toUpperCase(); // OK after narrowing
|
|
2174
|
-
}
|
|
2175
|
-
}
|
|
2176
|
-
```
|
|
2177
|
-
|
|
2178
|
-
### `@default`
|
|
2179
|
-
|
|
2180
|
-
By default, `sveld` infers the `@default` value from the prop's initializer and includes it in the generated TypeScript definitions:
|
|
2181
|
-
|
|
2182
|
-
```svelte
|
|
2183
|
-
<script>
|
|
2184
|
-
export let open = false;
|
|
2185
|
-
</script>
|
|
2186
|
-
```
|
|
2187
|
-
|
|
2188
|
-
```ts
|
|
2189
|
-
/**
|
|
2190
|
-
* @default false
|
|
2191
|
-
*/
|
|
2192
|
-
open?: boolean;
|
|
2193
|
-
```
|
|
2194
|
-
|
|
2195
|
-
A fallback or conditional initializer (`??`, `||`, `&&`, `?:`) shows its source text, folded onto one line, in the `.d.ts`, the JSON `value`, and the Markdown "Default value" column:
|
|
2196
|
-
|
|
2197
|
-
```svelte
|
|
2198
|
-
<script>
|
|
2199
|
-
export let theme = undefined;
|
|
2200
|
-
const defaultSize = theme === "dense" ? "sm" : "md";
|
|
2201
|
-
|
|
2202
|
-
export let size = defaultSize ?? "md";
|
|
2203
|
-
</script>
|
|
2204
|
-
```
|
|
2205
|
-
|
|
2206
|
-
```ts
|
|
2207
|
-
/**
|
|
2208
|
-
* @default defaultSize ?? "md"
|
|
2209
|
-
*/
|
|
2210
|
-
size?: string;
|
|
2211
|
-
```
|
|
2212
|
-
|
|
2213
|
-
Use `@default` to document the default value. When you supply `@default`, `sveld` uses it instead of the inferred value and avoids duplicate `@default` tags in the output.
|
|
2214
|
-
|
|
2215
|
-
Use `@default` when the initializer references a variable or expression that means nothing to consumers:
|
|
2216
|
-
|
|
2217
|
-
```svelte
|
|
2218
|
-
<script>
|
|
2219
|
-
const defaultFilter = () => true;
|
|
2220
|
-
|
|
2221
|
-
/**
|
|
2222
|
-
* @default () => true
|
|
2223
|
-
* @type {(item: string, value: string) => boolean}
|
|
2224
|
-
*/
|
|
2225
|
-
export let shouldFilter = defaultFilter;
|
|
2226
|
-
</script>
|
|
2227
|
-
```
|
|
2228
|
-
|
|
2229
|
-
```ts
|
|
2230
|
-
/**
|
|
2231
|
-
* @default () => true
|
|
2232
|
-
*/
|
|
2233
|
-
shouldFilter?: (item: string, value: string) => boolean;
|
|
2234
|
-
```
|
|
2235
|
-
|
|
2236
|
-
#### Identifier resolution
|
|
2237
|
-
|
|
2238
|
-
When a prop's initializer is a variable reference, `sveld` resolves it to the actual value:
|
|
2239
|
-
|
|
2240
|
-
```svelte
|
|
2241
|
-
<script>
|
|
2242
|
-
const DEFAULT_SIZE = "md";
|
|
2243
|
-
|
|
2244
|
-
/** @type {"sm" | "md" | "lg"} */
|
|
2245
|
-
export let size = DEFAULT_SIZE;
|
|
2246
|
-
</script>
|
|
2247
|
-
```
|
|
2248
|
-
|
|
2249
|
-
```ts
|
|
2250
|
-
/**
|
|
2251
|
-
* @default "md"
|
|
2252
|
-
*/
|
|
2253
|
-
size?: "sm" | "md" | "lg";
|
|
2254
|
-
```
|
|
2255
|
-
|
|
2256
|
-
Chained references are also resolved:
|
|
2257
|
-
|
|
2258
|
-
```svelte
|
|
2259
|
-
<script>
|
|
2260
|
-
const ACTUAL_VALUE = 42;
|
|
2261
|
-
const ALIAS = ACTUAL_VALUE;
|
|
2262
|
-
|
|
2263
|
-
export let count = ALIAS;
|
|
2264
|
-
</script>
|
|
2265
|
-
```
|
|
2266
|
-
|
|
2267
|
-
```ts
|
|
2268
|
-
/**
|
|
2269
|
-
* @default 42
|
|
2270
|
-
*/
|
|
2271
|
-
count?: number;
|
|
2272
|
-
```
|
|
2273
|
-
|
|
2274
|
-
Resolution follows up to 5 levels of indirection. Beyond that, the last resolved identifier name is used as the default value.
|
|
2275
|
-
|
|
2276
|
-
A named import, or a member of a namespace import (`import * as timing from "./timing.js"` with `timing.TOOLTIP_LEAVE_DELAY_MS`, also through a namespace the module re-exports with `export * as`), resolves when the imported module (followed through re-exports) declares it as an `export const` with a string, number, boolean, or static template literal:
|
|
2277
|
-
|
|
2278
|
-
```svelte
|
|
2279
|
-
<script>
|
|
2280
|
-
// timing.js: export const TOOLTIP_LEAVE_DELAY_MS = 300;
|
|
2281
|
-
import { TOOLTIP_LEAVE_DELAY_MS } from "./timing.js";
|
|
2282
|
-
|
|
2283
|
-
export let leaveDelayMs = TOOLTIP_LEAVE_DELAY_MS;
|
|
2284
|
-
</script>
|
|
2285
|
-
```
|
|
2286
|
-
|
|
2287
|
-
```ts
|
|
2288
|
-
/**
|
|
2289
|
-
* @default 300
|
|
2290
|
-
*/
|
|
2291
|
-
leaveDelayMs?: number;
|
|
2292
|
-
```
|
|
2293
|
-
|
|
2294
|
-
Any other identifier that cannot be resolved (an `export let`, a computed value, a package import) is used as-is, and its type falls back to `any`.
|
|
2295
|
-
|
|
2296
|
-
When an explicit `@default` annotation is provided, it always takes precedence over the resolved value.
|
|
2297
|
-
|
|
2298
|
-
### `@typedef`
|
|
2299
|
-
|
|
2300
|
-
The `@typedef` tag defines a shared type used multiple times in a component. All typedefs in a component are exported from the generated `.d.ts`.
|
|
2301
|
-
|
|
2302
|
-
**Signature:**
|
|
2303
|
-
|
|
2304
|
-
```js
|
|
2305
|
-
/**
|
|
2306
|
-
* @typedef {Type} TypeName
|
|
2307
|
-
*/
|
|
2308
|
-
```
|
|
2309
|
-
|
|
2310
|
-
**Example:**
|
|
2311
|
-
|
|
2312
|
-
```svelte
|
|
2313
|
-
<script>
|
|
2314
|
-
/**
|
|
2315
|
-
* @typedef {string} AuthorName
|
|
2316
|
-
* @typedef {{ name?: AuthorName; dob?: string; }} Author
|
|
2317
|
-
*/
|
|
2318
|
-
|
|
2319
|
-
let {
|
|
2320
|
-
/** @type {Author} */
|
|
2321
|
-
author = {},
|
|
2322
|
-
/** @type {Author[]} */
|
|
2323
|
-
authors = [],
|
|
2324
|
-
} = $props();
|
|
2325
|
-
</script>
|
|
2326
|
-
```
|
|
2327
|
-
|
|
2328
|
-
<details>
|
|
2329
|
-
<summary>Svelte 3/4 (legacy) syntax</summary>
|
|
2330
|
-
|
|
2331
|
-
```svelte
|
|
2332
|
-
<script>
|
|
2333
|
-
/**
|
|
2334
|
-
* @typedef {string} AuthorName
|
|
2335
|
-
* @typedef {{ name?: AuthorName; dob?: string; }} Author
|
|
2336
|
-
*/
|
|
2337
|
-
|
|
2338
|
-
/** @type {Author} */
|
|
2339
|
-
export let author = {};
|
|
2340
|
-
|
|
2341
|
-
/** @type {Author[]} */
|
|
2342
|
-
export let authors = [];
|
|
2343
|
-
</script>
|
|
2344
|
-
```
|
|
2345
|
-
|
|
2346
|
-
</details>
|
|
2347
|
-
|
|
2348
|
-
#### Using `@property` for complex typedefs
|
|
2349
|
-
|
|
2350
|
-
For complex object types, use `@property` to document individual fields. That gives per-property tooltips in the IDE. See the [`@property`](#property) reference for the tag's valid contexts.
|
|
2351
|
-
|
|
2352
|
-
**Signature:**
|
|
2353
|
-
|
|
2354
|
-
```js
|
|
2355
|
-
/**
|
|
2356
|
-
* Type description
|
|
2357
|
-
* @typedef {object} TypeName
|
|
2358
|
-
* @property {Type} propertyName - Property description
|
|
2359
|
-
*/
|
|
2360
|
-
```
|
|
2361
|
-
|
|
2362
|
-
**Example:**
|
|
2363
|
-
|
|
2364
|
-
```svelte
|
|
2365
|
-
<script>
|
|
2366
|
-
/**
|
|
2367
|
-
* Represents a user in the system
|
|
2368
|
-
* @typedef {object} User
|
|
2369
|
-
* @property {string} name - The user's full name
|
|
2370
|
-
* @property {string} email - The user's email address
|
|
2371
|
-
* @property {number} age - The user's age in years
|
|
2372
|
-
*/
|
|
2373
|
-
|
|
2374
|
-
/** @type {User} */
|
|
2375
|
-
let { user = { name: "John", email: "john@example.com", age: 30 } } = $props();
|
|
2376
|
-
</script>
|
|
2377
|
-
```
|
|
2378
|
-
|
|
2379
|
-
<details>
|
|
2380
|
-
<summary>Svelte 3/4 (legacy) syntax</summary>
|
|
2381
|
-
|
|
2382
|
-
```svelte
|
|
2383
|
-
<script>
|
|
2384
|
-
/**
|
|
2385
|
-
* Represents a user in the system
|
|
2386
|
-
* @typedef {object} User
|
|
2387
|
-
* @property {string} name - The user's full name
|
|
2388
|
-
* @property {string} email - The user's email address
|
|
2389
|
-
* @property {number} age - The user's age in years
|
|
2390
|
-
*/
|
|
2391
|
-
|
|
2392
|
-
/** @type {User} */
|
|
2393
|
-
export let user = { name: "John", email: "john@example.com", age: 30 };
|
|
2394
|
-
</script>
|
|
2395
|
-
```
|
|
2396
|
-
|
|
2397
|
-
</details>
|
|
2398
|
-
|
|
2399
|
-
Output is identical for both syntax modes.
|
|
2400
|
-
|
|
2401
|
-
Output:
|
|
2402
|
-
|
|
2403
|
-
```ts
|
|
2404
|
-
export type User = {
|
|
2405
|
-
/** The user's full name */
|
|
2406
|
-
name: string;
|
|
2407
|
-
/** The user's email address */
|
|
2408
|
-
email: string;
|
|
2409
|
-
/** The user's age in years */
|
|
2410
|
-
age: number;
|
|
2411
|
-
};
|
|
2412
|
-
|
|
2413
|
-
export type ComponentProps = {
|
|
2414
|
-
/**
|
|
2415
|
-
* Represents a user in the system
|
|
2416
|
-
* @default { name: "John", email: "john@example.com", age: 30 }
|
|
2417
|
-
*/
|
|
2418
|
-
user?: User;
|
|
2419
|
-
};
|
|
2420
|
-
```
|
|
2421
|
-
|
|
2422
|
-
#### Optional properties and default values
|
|
2423
|
-
|
|
2424
|
-
Use square brackets for optional properties, per JSDoc. Default values use `[propertyName=defaultValue]`.
|
|
2425
|
-
|
|
2426
|
-
**Signature:**
|
|
2427
|
-
|
|
2428
|
-
```js
|
|
2429
|
-
/**
|
|
2430
|
-
* @typedef {object} TypeName
|
|
2431
|
-
* @property {Type} [optionalProperty] - Optional property description
|
|
2432
|
-
* @property {Type} [propertyWithDefault=defaultValue] - Property with default value
|
|
2433
|
-
*/
|
|
2434
|
-
```
|
|
2435
|
-
|
|
2436
|
-
**Example:**
|
|
2437
|
-
|
|
2438
|
-
```svelte
|
|
2439
|
-
<script>
|
|
2440
|
-
/**
|
|
2441
|
-
* Configuration options for the component
|
|
2442
|
-
* @typedef {object} ComponentConfig
|
|
2443
|
-
* @property {boolean} enabled - Whether the component is enabled
|
|
2444
|
-
* @property {string} theme - The component theme
|
|
2445
|
-
* @property {number} [timeout=5000] - Optional timeout in milliseconds
|
|
2446
|
-
* @property {boolean} [debug] - Optional debug mode flag
|
|
2447
|
-
*/
|
|
2448
|
-
|
|
2449
|
-
/** @type {ComponentConfig} */
|
|
2450
|
-
let { config = { enabled: true, theme: "dark" } } = $props();
|
|
2451
|
-
</script>
|
|
2452
|
-
```
|
|
2453
|
-
|
|
2454
|
-
<details>
|
|
2455
|
-
<summary>Svelte 3/4 (legacy) syntax</summary>
|
|
2456
|
-
|
|
2457
|
-
```svelte
|
|
2458
|
-
<script>
|
|
2459
|
-
/**
|
|
2460
|
-
* Configuration options for the component
|
|
2461
|
-
* @typedef {object} ComponentConfig
|
|
2462
|
-
* @property {boolean} enabled - Whether the component is enabled
|
|
2463
|
-
* @property {string} theme - The component theme
|
|
2464
|
-
* @property {number} [timeout=5000] - Optional timeout in milliseconds
|
|
2465
|
-
* @property {boolean} [debug] - Optional debug mode flag
|
|
2466
|
-
*/
|
|
2467
|
-
|
|
2468
|
-
/** @type {ComponentConfig} */
|
|
2469
|
-
export let config = { enabled: true, theme: "dark" };
|
|
2470
|
-
</script>
|
|
2471
|
-
```
|
|
2472
|
-
|
|
2473
|
-
</details>
|
|
2474
|
-
|
|
2475
|
-
Output is identical for both syntax modes.
|
|
2476
|
-
|
|
2477
|
-
Output:
|
|
2478
|
-
|
|
2479
|
-
```ts
|
|
2480
|
-
export type ComponentConfig = {
|
|
2481
|
-
/** Whether the component is enabled */
|
|
2482
|
-
enabled: boolean;
|
|
2483
|
-
/** The component theme */
|
|
2484
|
-
theme: string;
|
|
2485
|
-
/** Optional timeout in milliseconds @default 5000 */
|
|
2486
|
-
timeout?: number;
|
|
2487
|
-
/** Optional debug mode flag */
|
|
2488
|
-
debug?: boolean;
|
|
2489
|
-
};
|
|
2490
|
-
|
|
2491
|
-
export type ComponentProps = {
|
|
2492
|
-
/**
|
|
2493
|
-
* Configuration options for the component
|
|
2494
|
-
* @default { enabled: true, theme: "dark" }
|
|
2495
|
-
*/
|
|
2496
|
-
config?: ComponentConfig;
|
|
2497
|
-
};
|
|
2498
|
-
```
|
|
2499
|
-
|
|
2500
|
-
> The inline syntax `@typedef {{ name: string }} User` still works for backwards compatibility.
|
|
2501
|
-
|
|
2502
|
-
#### Discriminated unions
|
|
2503
|
-
|
|
2504
|
-
A `@typedef` can be a union of object literals, optionally mixed with primitive members. `sveld` emits these as `export type X = ...` aliases (not `interface`), so the discriminant narrows correctly on the consumer side.
|
|
2505
|
-
|
|
2506
|
-
**Signature:**
|
|
2507
|
-
|
|
2508
|
-
```js
|
|
2509
|
-
/**
|
|
2510
|
-
* @typedef {A | B | C} TypeName
|
|
2511
|
-
*/
|
|
2512
|
-
```
|
|
2513
|
-
|
|
2514
|
-
**Example:**
|
|
2515
|
-
|
|
2516
|
-
```svelte
|
|
2517
|
-
<script>
|
|
2518
|
-
/**
|
|
2519
|
-
* @typedef {{ kind: "success"; value: string } | { kind: "error"; error: Error }} Result
|
|
2520
|
-
* @typedef {{ ok: true; data: number } | { ok: false; reason: string } | "pending"} Status
|
|
2521
|
-
*/
|
|
2522
|
-
|
|
2523
|
-
/** @type {Result} */
|
|
2524
|
-
export let result = { kind: "success", value: "ok" };
|
|
2525
|
-
|
|
2526
|
-
/** @type {Status} */
|
|
2527
|
-
export let status = "pending";
|
|
2528
|
-
</script>
|
|
2529
|
-
```
|
|
2530
|
-
|
|
2531
|
-
Output:
|
|
2532
|
-
|
|
2533
|
-
```ts
|
|
2534
|
-
export type Result = { kind: "success"; value: string } | { kind: "error"; error: Error };
|
|
2535
|
-
|
|
2536
|
-
export type Status = { ok: true; data: number } | { ok: false; reason: string } | "pending";
|
|
2537
|
-
|
|
2538
|
-
export type ComponentProps = {
|
|
2539
|
-
/** @default { kind: "success", value: "ok" } */
|
|
2540
|
-
result?: Result;
|
|
2541
|
-
/** @default "pending" */
|
|
2542
|
-
status?: Status;
|
|
2543
|
-
};
|
|
2544
|
-
```
|
|
2545
|
-
|
|
2546
|
-
Consumers can then narrow on the discriminant:
|
|
2547
|
-
|
|
2548
|
-
```ts
|
|
2549
|
-
function describe(r: Result) {
|
|
2550
|
-
switch (r.kind) {
|
|
2551
|
-
case "success":
|
|
2552
|
-
return r.value;
|
|
2553
|
-
case "error":
|
|
2554
|
-
return r.error.message;
|
|
2555
|
-
}
|
|
2556
|
-
}
|
|
2557
|
-
```
|
|
2558
|
-
|
|
2559
|
-
The same pattern works inline via `@type`, which is useful when the union is only used for a single prop:
|
|
2560
|
-
|
|
2561
|
-
```js
|
|
2562
|
-
/** @type {{ kind: "success"; value: string } | { kind: "error"; error: Error }} */
|
|
2563
|
-
export let result = { kind: "success", value: "ok" };
|
|
2564
|
-
```
|
|
2565
|
-
|
|
2566
|
-
In `<script lang="ts">` components, write the type alias directly. `sveld` preserves it in the emitted `.d.ts`:
|
|
2567
|
-
|
|
2568
|
-
```svelte
|
|
2569
|
-
<script lang="ts">
|
|
2570
|
-
type Result = { kind: "success"; value: string } | { kind: "error"; error: Error };
|
|
2571
|
-
|
|
2572
|
-
let { result = { kind: "success", value: "ok" } }: { result?: Result } = $props();
|
|
2573
|
-
</script>
|
|
2574
|
-
```
|
|
2575
|
-
|
|
2576
|
-
#### Branded types
|
|
2577
|
-
|
|
2578
|
-
A branded type is a primitive plus a unique marker so values like `UserId` are not interchangeable with any other `string`. At runtime it is still the underlying primitive; TypeScript treats the brand as a separate type. Declare the brand inline with `@type`. `sveld` copies the intersection verbatim into the emitted prop type, so the brand shows up in IntelliSense, autocomplete, and hover tooltips on the consumer side.
|
|
2579
|
-
|
|
2580
|
-
**Example:**
|
|
2581
|
-
|
|
2582
|
-
```svelte
|
|
2583
|
-
<script>
|
|
2584
|
-
/**
|
|
2585
|
-
* A branded string. At runtime it is a plain `string`, but the brand makes it
|
|
2586
|
-
* a distinct domain type that other strings cannot be assigned to.
|
|
2587
|
-
*
|
|
2588
|
-
* @type {string & { readonly __brand: "UserId" }}
|
|
2589
|
-
*/
|
|
2590
|
-
export let userId;
|
|
2591
|
-
|
|
2592
|
-
/**
|
|
2593
|
-
* A branded number representing a monetary amount in cents.
|
|
2594
|
-
*
|
|
2595
|
-
* @type {number & { readonly __brand: "Cents" }}
|
|
2596
|
-
*/
|
|
2597
|
-
export let amount;
|
|
2598
|
-
</script>
|
|
2599
|
-
```
|
|
2600
|
-
|
|
2601
|
-
Output:
|
|
2602
|
-
|
|
2603
|
-
```ts
|
|
2604
|
-
export type ComponentProps = {
|
|
2605
|
-
/**
|
|
2606
|
-
* A branded string. At runtime it is a plain `string`, but the brand makes it
|
|
2607
|
-
* a distinct domain type that other strings cannot be assigned to.
|
|
2608
|
-
* @default undefined
|
|
2609
|
-
*/
|
|
2610
|
-
userId: string & { readonly __brand: "UserId" };
|
|
2611
|
-
|
|
2612
|
-
/**
|
|
2613
|
-
* A branded number representing a monetary amount in cents.
|
|
2614
|
-
* @default undefined
|
|
2615
|
-
*/
|
|
2616
|
-
amount: number & { readonly __brand: "Cents" };
|
|
2617
|
-
};
|
|
2618
|
-
```
|
|
2619
|
-
|
|
2620
|
-
Consumers construct branded values with a narrowing cast, then get compile-time protection against mixing them up:
|
|
2621
|
-
|
|
2622
|
-
```ts
|
|
2623
|
-
const userId = "user_123" as ComponentProps["userId"];
|
|
2624
|
-
|
|
2625
|
-
// Error: a plain string is not assignable to the branded userId
|
|
2626
|
-
component.$set({ userId: "user_123" });
|
|
2627
|
-
```
|
|
2628
|
-
|
|
2629
|
-
#### Utility types
|
|
2630
|
-
|
|
2631
|
-
`sveld` preserves TypeScript utility types verbatim, so a prop type can be derived from an existing `@typedef` instead of restating its fields. `Pick`, `Omit`, `Partial`, `Required`, `Readonly`, `ReturnType`, `Parameters`, and `Awaited` pass through unchanged. When the base type changes, derived props follow.
|
|
2632
|
-
|
|
2633
|
-
**Example:**
|
|
2634
|
-
|
|
2635
|
-
```svelte
|
|
2636
|
-
<script>
|
|
2637
|
-
/**
|
|
2638
|
-
* @typedef {{ id: string; size: "sm" | "md" | "lg"; disabled: boolean }} Options
|
|
2639
|
-
*/
|
|
2640
|
-
|
|
2641
|
-
/**
|
|
2642
|
-
* @typedef {() => Options} Factory
|
|
2643
|
-
*/
|
|
2644
|
-
|
|
2645
|
-
/**
|
|
2646
|
-
* A subset of `Options`.
|
|
2647
|
-
* @type {Pick<Options, "id" | "size">}
|
|
2648
|
-
*/
|
|
2649
|
-
export let summary;
|
|
2650
|
-
|
|
2651
|
-
/**
|
|
2652
|
-
* Everything in `Options` except `disabled`.
|
|
2653
|
-
* @type {Omit<Options, "disabled">}
|
|
2654
|
-
*/
|
|
2655
|
-
export let editable;
|
|
2656
|
-
|
|
2657
|
-
/**
|
|
2658
|
-
* Derived from the factory's return type rather than restated.
|
|
2659
|
-
* @type {ReturnType<Factory>}
|
|
2660
|
-
*/
|
|
2661
|
-
export let defaults;
|
|
2662
|
-
|
|
2663
|
-
/**
|
|
2664
|
-
* The resolved value of an async source.
|
|
2665
|
-
* @type {Awaited<Promise<Options>>}
|
|
2666
|
-
*/
|
|
2667
|
-
export let resolved;
|
|
2668
|
-
</script>
|
|
2669
|
-
```
|
|
2670
|
-
|
|
2671
|
-
Output:
|
|
2672
|
-
|
|
2673
|
-
```ts
|
|
2674
|
-
export interface Options {
|
|
2675
|
-
id: string;
|
|
2676
|
-
size: "sm" | "md" | "lg";
|
|
2677
|
-
disabled: boolean;
|
|
2678
|
-
}
|
|
2679
|
-
|
|
2680
|
-
export type Factory = () => Options;
|
|
2681
|
-
|
|
2682
|
-
export type ComponentProps = {
|
|
2683
|
-
summary: Pick<Options, "id" | "size">;
|
|
2684
|
-
editable: Omit<Options, "disabled">;
|
|
2685
|
-
defaults: ReturnType<Factory>;
|
|
2686
|
-
resolved: Awaited<Promise<Options>>;
|
|
2687
|
-
};
|
|
2688
|
-
```
|
|
2689
|
-
|
|
2690
|
-
#### Type guards
|
|
2691
|
-
|
|
2692
|
-
A prop typed as a [type predicate](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates) (`value is T`) lets a component accept a user-defined type guard. `sveld` copies the predicate verbatim, whether you write it inline with `@type` or name it with `@typedef`, so narrowing survives in the generated `.d.ts`. Name guards `isX` or `hasX`, and make sure the implementation actually checks what the predicate claims.
|
|
2693
|
-
|
|
2694
|
-
**Example:**
|
|
2695
|
-
|
|
2696
|
-
```svelte
|
|
2697
|
-
<script>
|
|
2698
|
-
/**
|
|
2699
|
-
* @typedef {{ id: string; name: string }} User
|
|
2700
|
-
*/
|
|
2701
|
-
|
|
2702
|
-
/**
|
|
2703
|
-
* A type guard. It accepts an `unknown` value and returns a type predicate,
|
|
2704
|
-
* so callers can narrow `unknown` to `User` before accessing its fields.
|
|
2705
|
-
*
|
|
2706
|
-
* @type {(value: unknown) => value is User}
|
|
2707
|
-
*/
|
|
2708
|
-
export let isUser;
|
|
2709
|
-
|
|
2710
|
-
/**
|
|
2711
|
-
* A type guard expressed as a reusable `@typedef`.
|
|
2712
|
-
*
|
|
2713
|
-
* @typedef {(value: unknown) => value is User} UserGuard
|
|
2714
|
-
*/
|
|
2715
|
-
|
|
2716
|
-
/** @type {UserGuard} */
|
|
2717
|
-
export let validate;
|
|
2718
|
-
</script>
|
|
2719
|
-
```
|
|
2720
|
-
|
|
2721
|
-
Output:
|
|
2722
|
-
|
|
2723
|
-
```ts
|
|
2724
|
-
export interface User {
|
|
2725
|
-
id: string;
|
|
2726
|
-
name: string;
|
|
2727
|
-
}
|
|
2728
|
-
|
|
2729
|
-
export type UserGuard = (value: unknown) => value is User;
|
|
2730
|
-
|
|
2731
|
-
export type ComponentProps = {
|
|
2732
|
-
isUser: (value: unknown) => value is User;
|
|
2733
|
-
validate: UserGuard;
|
|
2734
|
-
};
|
|
2735
|
-
```
|
|
2736
|
-
|
|
2737
|
-
Consumers use the guard to narrow an `unknown` value:
|
|
2738
|
-
|
|
2739
|
-
```ts
|
|
2740
|
-
function render(value: unknown, props: ComponentProps) {
|
|
2741
|
-
if (props.isUser(value)) {
|
|
2742
|
-
value.name; // narrowed to User
|
|
2743
|
-
}
|
|
2744
|
-
}
|
|
2745
|
-
```
|
|
2746
|
-
|
|
2747
|
-
### `@property`
|
|
2748
|
-
|
|
2749
|
-
`@property` documents one field of an object. `sveld` only reads it in two places: directly inside a `@typedef {object}` block, and directly inside an `@event` block (after an explicit `@type {object}`, or with no `@type` at all — `sveld` builds the detail type from the collected `@property` entries). It has no effect anywhere else.
|
|
2750
|
-
|
|
2751
|
-
**Valid contexts:**
|
|
2752
|
-
|
|
2753
|
-
| Context | Behavior |
|
|
2754
|
-
| :- | :- |
|
|
2755
|
-
| After `@typedef {object} Name`, in the same comment block | Builds `Name`'s fields. See [Using `@property` for complex typedefs](#using-property-for-complex-typedefs). |
|
|
2756
|
-
| After `@event eventname`, in the same comment block | Builds the event's detail type. See [Using `@property` for complex event details](#using-property-for-complex-event-details). |
|
|
2757
|
-
| A plain prop with no `@typedef`/`@type {object}` | Not structured. Folded into the prop's description as literal text (`@property name - description`). |
|
|
2758
|
-
| Before a `@slot` / `@snippet` line | Dropped entirely — no structured output, and unlike other unrecognized tags in that position, it is not folded into the slot's description either. |
|
|
2759
|
-
| Inside `@callback` | Not read. Use `@param` for callback parameters instead. |
|
|
2760
|
-
|
|
2761
|
-
**Signature:**
|
|
2762
|
-
|
|
2763
|
-
```js
|
|
2764
|
-
/**
|
|
2765
|
-
* @typedef {object} TypeName
|
|
2766
|
-
* @property {Type} propertyName - Property description
|
|
2767
|
-
*
|
|
2768
|
-
* @event eventname
|
|
2769
|
-
* @type {object}
|
|
2770
|
-
* @property {Type} propertyName - Property description
|
|
2771
|
-
*/
|
|
2772
|
-
```
|
|
2773
|
-
|
|
2774
|
-
Both forms support the same modifiers as typedef properties elsewhere in this doc: optional properties (`[name]`), default values (`[name=value]`), and nested/discriminated-union shapes. See the linked sections above for full signatures and worked examples with generated output.
|
|
2775
|
-
|
|
2776
|
-
A description can wrap onto continuation lines, which run until the next tag. Indent them past the `*` gutter to keep them attached to the tag unambiguously; an unindented line right above an `@event`/`@typedef`/`@slot` with no description of its own is read as that tag's description instead, and an unindented line after an event's last `@property` describes the event. A blank line between indented paragraphs doesn't end the description; it's kept as a paragraph break. Lines inside a ```` ``` ```` code fence keep their indentation relative to the fence.
|
|
2777
|
-
|
|
2778
|
-
```js
|
|
2779
|
-
/**
|
|
2780
|
-
* @typedef {object} Config
|
|
2781
|
-
* @property {number} itemHeight Height of each item in pixels, used for
|
|
2782
|
-
* virtualization math.
|
|
2783
|
-
*/
|
|
2784
|
-
```
|
|
2785
|
-
|
|
2786
|
-
A `{type}` can wrap too. The text after its closing `}` and the name is the tag's own description, exactly as if the type fit on one line:
|
|
2787
|
-
|
|
2788
|
-
```js
|
|
2789
|
-
/**
|
|
2790
|
-
* @property {"ascending"
|
|
2791
|
-
* | "descending"} [dir] - The next sort direction,
|
|
2792
|
-
* reported regardless.
|
|
2793
|
-
*/
|
|
2794
|
-
```
|
|
2795
|
-
|
|
2796
|
-
### `@callback`
|
|
2797
|
-
|
|
2798
|
-
The `@callback` tag defines a function type with `@param` and `@returns`, following the [TypeScript JSDoc `@callback` spec](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#callback). Like `@typedef`, callbacks are exported from the generated `.d.ts`.
|
|
2799
|
-
|
|
2800
|
-
Use it for callback props when you do not want inline function type syntax.
|
|
2801
|
-
|
|
2802
|
-
**Signature:**
|
|
2803
|
-
|
|
2804
|
-
```js
|
|
2805
|
-
/**
|
|
2806
|
-
* Optional description
|
|
2807
|
-
* @callback CallbackName
|
|
2808
|
-
* @param {Type} paramName - Parameter description
|
|
2809
|
-
* @returns {ReturnType}
|
|
2810
|
-
*/
|
|
2811
|
-
```
|
|
2812
|
-
|
|
2813
|
-
**Example:**
|
|
2814
|
-
|
|
2815
|
-
```svelte
|
|
2816
|
-
<script>
|
|
2817
|
-
/**
|
|
2818
|
-
* Callback fired when the value changes
|
|
2819
|
-
* @callback OnChange
|
|
2820
|
-
* @param {string} value - The new value
|
|
2821
|
-
* @param {number} index - The index of the changed item
|
|
2822
|
-
* @returns {void}
|
|
2823
|
-
*/
|
|
2824
|
-
|
|
2825
|
-
/** @type {OnChange} */
|
|
2826
|
-
let { onChange = (value, index) => {} } = $props();
|
|
2827
|
-
</script>
|
|
2828
|
-
```
|
|
2829
|
-
|
|
2830
|
-
<details>
|
|
2831
|
-
<summary>Svelte 3/4 (legacy) syntax</summary>
|
|
2832
|
-
|
|
2833
|
-
```svelte
|
|
2834
|
-
<script>
|
|
2835
|
-
/**
|
|
2836
|
-
* Callback fired when the value changes
|
|
2837
|
-
* @callback OnChange
|
|
2838
|
-
* @param {string} value - The new value
|
|
2839
|
-
* @param {number} index - The index of the changed item
|
|
2840
|
-
* @returns {void}
|
|
2841
|
-
*/
|
|
2842
|
-
|
|
2843
|
-
/** @type {OnChange} */
|
|
2844
|
-
export let onChange = (value, index) => {};
|
|
2845
|
-
</script>
|
|
2846
|
-
```
|
|
2847
|
-
|
|
2848
|
-
</details>
|
|
2849
|
-
|
|
2850
|
-
Output is identical for both syntax modes.
|
|
2851
|
-
|
|
2852
|
-
Output:
|
|
2853
|
-
|
|
2854
|
-
```ts
|
|
2855
|
-
/**
|
|
2856
|
-
* Callback fired when the value changes
|
|
2857
|
-
*/
|
|
2858
|
-
export type OnChange = (value: string, index: number) => void;
|
|
2859
|
-
|
|
2860
|
-
export type ComponentProps = {
|
|
2861
|
-
/**
|
|
2862
|
-
* Callback fired when the value changes
|
|
2863
|
-
*/
|
|
2864
|
-
onChange?: OnChange;
|
|
2865
|
-
};
|
|
2866
|
-
```
|
|
2867
|
-
|
|
2868
|
-
Callbacks can be combined with `@typedef` in the same comment block:
|
|
2869
|
-
|
|
2870
|
-
```js
|
|
2871
|
-
/**
|
|
2872
|
-
* @typedef {"asc" | "desc"} SortDirection
|
|
2873
|
-
* @callback SortFn
|
|
2874
|
-
* @param {any} a
|
|
2875
|
-
* @param {any} b
|
|
2876
|
-
* @param {SortDirection} direction
|
|
2877
|
-
* @returns {number}
|
|
2878
|
-
*/
|
|
2879
|
-
```
|
|
2880
|
-
|
|
2881
|
-
When `@returns` is omitted, the return type defaults to `void`. When no `@param` tags are present, the callback is typed as a no-argument function.
|
|
2882
|
-
|
|
2883
|
-
### `@slot` / `@snippet`
|
|
2884
|
-
|
|
2885
|
-
Use `@slot` to type component slots. In Svelte 5 runes components, `@snippet` is an alias. Both are non-standard JSDoc tags.
|
|
2886
|
-
|
|
2887
|
-
Descriptions are optional for every slot, including the default slot. Put prose in the same `/** */` block above `@slot` / `@snippet`, or an inline description on the `@slot` line for named slots.
|
|
2888
|
-
|
|
2889
|
-
**Signature:**
|
|
2890
|
-
|
|
2891
|
-
```js
|
|
2892
|
-
/**
|
|
2893
|
-
* @slot {Type} slot-name [slot description]
|
|
2894
|
-
* @snippet {Type} snippet-name [snippet description]
|
|
2895
|
-
*/
|
|
2896
|
-
```
|
|
2897
|
-
|
|
2898
|
-
Omit the `slot-name` to type the default slot. `{Type}` itself is required; omitting it falls back to `Record<string, never>` and raises a [`sveld/slot-missing-type`](#diagnostic-codes) warning.
|
|
2899
|
-
|
|
2900
|
-
```js
|
|
2901
|
-
/**
|
|
2902
|
-
* @slot {Type}
|
|
2903
|
-
* @snippet {Type}
|
|
2904
|
-
*/
|
|
2905
|
-
```
|
|
2906
|
-
|
|
2907
|
-
**Example:**
|
|
2908
|
-
|
|
2909
|
-
```svelte
|
|
2910
|
-
<script>
|
|
2911
|
-
/**
|
|
2912
|
-
* @snippet {{ prop: number; doubled: number; }}
|
|
2913
|
-
* @snippet {{}} title
|
|
2914
|
-
* @snippet {{ prop: number }} body - Customize the paragraph text.
|
|
2915
|
-
*/
|
|
2916
|
-
|
|
2917
|
-
let { prop = 0, children, title, body } = $props();
|
|
2918
|
-
</script>
|
|
2919
|
-
|
|
2920
|
-
<h1>
|
|
2921
|
-
{@render children?.({ prop, doubled: prop * 2 })}
|
|
2922
|
-
{@render title?.()}
|
|
2923
|
-
</h1>
|
|
2924
|
-
|
|
2925
|
-
<p>
|
|
2926
|
-
{@render body?.({ prop })}
|
|
2927
|
-
</p>
|
|
2928
|
-
```
|
|
2929
|
-
|
|
2930
|
-
<details>
|
|
2931
|
-
<summary>Svelte 3/4 (legacy) syntax</summary>
|
|
2932
|
-
|
|
2933
|
-
```svelte
|
|
2934
|
-
<script>
|
|
2935
|
-
/**
|
|
2936
|
-
* @slot {{ prop: number; doubled: number; }}
|
|
2937
|
-
* @slot {{}} title
|
|
2938
|
-
* @slot {{ prop: number }} body - Customize the paragraph text.
|
|
2939
|
-
*/
|
|
2940
|
-
|
|
2941
|
-
export let prop = 0;
|
|
2942
|
-
</script>
|
|
2943
|
-
|
|
2944
|
-
<h1>
|
|
2945
|
-
<slot {prop} doubled={prop * 2} />
|
|
2946
|
-
<slot name="title" />
|
|
2947
|
-
</h1>
|
|
2948
|
-
|
|
2949
|
-
<p>
|
|
2950
|
-
<slot name="body" {prop} />
|
|
2951
|
-
</p>
|
|
2952
|
-
```
|
|
2953
|
-
|
|
2954
|
-
</details>
|
|
2955
|
-
|
|
2956
|
-
#### Extra JSDoc tags before `@slot`
|
|
2957
|
-
|
|
2958
|
-
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" }, ...]`.
|
|
2959
|
-
|
|
2960
|
-
`@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).
|
|
2961
|
-
|
|
2962
|
-
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.
|
|
2963
|
-
|
|
2964
|
-
**Example (default slot with `@example` and `@deprecated`):**
|
|
2965
|
-
|
|
2966
|
-
````svelte
|
|
2967
|
-
<script>
|
|
2968
|
-
/**
|
|
2969
|
-
* Spread `props` onto a custom element.
|
|
2970
|
-
* @example
|
|
2971
|
-
* ```svelte
|
|
2972
|
-
* <Item let:props>
|
|
2973
|
-
* <a {...props} href="/">Home</a>
|
|
2974
|
-
* </Item>
|
|
2975
|
-
* ```
|
|
2976
|
-
* @deprecated Prefer the `link` snippet.
|
|
2977
|
-
* @slot {{ props?: { class: string } }}
|
|
2978
|
-
*/
|
|
2979
|
-
</script>
|
|
2980
|
-
|
|
2981
|
-
<slot props={{ class: "bx--link" }} />
|
|
2982
|
-
````
|
|
2983
|
-
|
|
2984
|
-
#### Svelte 5 Snippet Compatibility
|
|
2985
|
-
|
|
2986
|
-
For Svelte 5, `sveld` generates optional snippet props for all slots so consumers can use traditional slot syntax or `{#snippet}`.
|
|
2987
|
-
|
|
2988
|
-
When parsing runes components, `sveld` maps `{@render ...}` calls back into the same slot metadata used for `<slot>`. Reserved snippet props like `children`, plus named snippet props from `{@render ...}`, live in `slots` metadata and generated snippet prop types, not duplicated in `props`.
|
|
2989
|
-
|
|
2990
|
-
Positional snippet calls like `{@render row?.(item, index)}` stay typed props when the prop has an explicit type like `Snippet<[Item, number]>`. They are not turned into synthetic slot metadata.
|
|
2991
|
-
|
|
2992
|
-
For slots with props (e.g., `let:prop`), the generated type uses a Snippet-compatible signature:
|
|
2993
|
-
|
|
2994
|
-
```ts
|
|
2995
|
-
slotName?: (this: void, ...args: [{ prop: PropType }]) => void;
|
|
2996
|
-
```
|
|
2997
|
-
|
|
2998
|
-
For slots without props:
|
|
2999
|
-
|
|
3000
|
-
```ts
|
|
3001
|
-
slotName?: (this: void) => void;
|
|
3002
|
-
```
|
|
3003
|
-
|
|
3004
|
-
**Why this signature?**
|
|
3005
|
-
|
|
3006
|
-
- **`this: void`** blocks calling the snippet with a `this` context, matching Svelte's rule that snippets are pure render functions
|
|
3007
|
-
- **`...args: [Props]`** uses tuple spread for type-safe parameters. It accepts fixed-length tuples (like `[{ row: Row }]`) and rejects array types (like `Props[]`), matching Svelte's `Snippet<T>` type
|
|
3008
|
-
|
|
3009
|
-
**Default slot (`children` prop):**
|
|
3010
|
-
|
|
3011
|
-
The default slot generates an optional `children` snippet prop:
|
|
3012
|
-
|
|
3013
|
-
```svelte
|
|
3014
|
-
<!-- Component with default slot that passes props -->
|
|
3015
|
-
<Dropdown {items} selectedId="1">
|
|
3016
|
-
{#snippet children({ item, index })}
|
|
3017
|
-
<span>{item.text} (#{index})</span>
|
|
3018
|
-
{/snippet}
|
|
3019
|
-
</Dropdown>
|
|
3020
|
-
```
|
|
3021
|
-
|
|
3022
|
-
Generated types:
|
|
3023
|
-
|
|
3024
|
-
```ts
|
|
3025
|
-
type DropdownProps = {
|
|
3026
|
-
items: Item[];
|
|
3027
|
-
selectedId?: string;
|
|
3028
|
-
|
|
3029
|
-
// Default slot as children snippet prop
|
|
3030
|
-
children?: (this: void, ...args: [{ item: Item; index: number }]) => void;
|
|
3031
|
-
};
|
|
3032
|
-
```
|
|
3033
|
-
|
|
3034
|
-
**Named slots:**
|
|
3035
|
-
|
|
3036
|
-
```svelte
|
|
3037
|
-
<!-- Using the generated types with Svelte 5 syntax -->
|
|
3038
|
-
<DataTable headers={headers} rows={rows}>
|
|
3039
|
-
{#snippet cell({ cell, row })}
|
|
3040
|
-
{#if cell.key === 'actions'}
|
|
3041
|
-
<Button on:click={() => handleAction(row)}>Edit</Button>
|
|
3042
|
-
{:else}
|
|
3043
|
-
{cell.value}
|
|
3044
|
-
{/if}
|
|
3045
|
-
{/snippet}
|
|
3046
|
-
</DataTable>
|
|
3047
|
-
```
|
|
3048
|
-
|
|
3049
|
-
Generated output includes both the snippet prop and the traditional slot definition:
|
|
3050
|
-
|
|
3051
|
-
```ts
|
|
3052
|
-
type DataTableProps<Row> = {
|
|
3053
|
-
// ... other props
|
|
3054
|
-
|
|
3055
|
-
// Snippet prop for Svelte 5 compatibility
|
|
3056
|
-
cell?: (
|
|
3057
|
-
this: void,
|
|
3058
|
-
...args: [
|
|
3059
|
-
{
|
|
3060
|
-
row: Row;
|
|
3061
|
-
cell: DataTableCell<Row>;
|
|
3062
|
-
rowIndex: number;
|
|
3063
|
-
cellIndex: number;
|
|
3064
|
-
},
|
|
3065
|
-
]
|
|
3066
|
-
) => void;
|
|
3067
|
-
|
|
3068
|
-
// Default slot as children prop
|
|
3069
|
-
children?: (this: void) => void;
|
|
3070
|
-
};
|
|
3071
|
-
|
|
3072
|
-
export default class DataTable<Row> extends SvelteComponentTyped<
|
|
3073
|
-
DataTableProps<Row>,
|
|
3074
|
-
{
|
|
3075
|
-
/* events */
|
|
3076
|
-
},
|
|
3077
|
-
{
|
|
3078
|
-
// Traditional slot definition (Svelte 3/4)
|
|
3079
|
-
default: Record<string, never>;
|
|
3080
|
-
cell: {
|
|
3081
|
-
row: Row;
|
|
3082
|
-
cell: DataTableCell<Row>;
|
|
3083
|
-
rowIndex: number;
|
|
3084
|
-
cellIndex: number;
|
|
3085
|
-
};
|
|
3086
|
-
}
|
|
3087
|
-
> {}
|
|
3088
|
-
```
|
|
3089
|
-
|
|
3090
|
-
### `@event`
|
|
3091
|
-
|
|
3092
|
-
Use the `@event` tag to type dispatched events. An event name is required and a description optional.
|
|
3093
|
-
|
|
3094
|
-
In Svelte 5 runes components, callback props like `onclick` are props, not events. The `events` output stays reserved for dispatched events and legacy forwarded events. If a runes component documents `@event foo` and exposes a matching callback prop like `onfoo` without actually dispatching or forwarding `foo`, `sveld` aliases that documentation onto the callback prop instead of synthesizing an emitted event.
|
|
3095
|
-
|
|
3096
|
-
Use `null` as the value if no event detail is provided.
|
|
3097
|
-
|
|
3098
|
-
`sveld` infers events from `dispatch("name")` calls. When the dispatcher is passed to a function imported from a local module, as `helper(dispatch)` or `helper({ dispatch })` (a named or default import) or `helpers.open(dispatch)` (a namespace import, or a namespace another module re-exports with `export * as helpers from "./helpers.js"`), it reads that function too and picks up the events it dispatches:
|
|
3099
|
-
|
|
3100
|
-
```js
|
|
3101
|
-
// dispatch-open-close.js
|
|
3102
|
-
export function createOpenCloseDispatcher(dispatch) {
|
|
3103
|
-
return (open) => dispatch(open ? "open" : "close");
|
|
3104
|
-
}
|
|
3105
|
-
```
|
|
3106
|
-
|
|
3107
|
-
```svelte
|
|
3108
|
-
<script>
|
|
3109
|
-
import { createEventDispatcher } from "svelte";
|
|
3110
|
-
import { createOpenCloseDispatcher } from "./dispatch-open-close.js";
|
|
3111
|
-
|
|
3112
|
-
const dispatch = createEventDispatcher();
|
|
3113
|
-
// Types `open` and `close` as `CustomEvent<null>`
|
|
3114
|
-
const notifyOpenChange = createOpenCloseDispatcher(dispatch);
|
|
3115
|
-
</script>
|
|
3116
|
-
```
|
|
3117
|
-
|
|
3118
|
-
This happens when sveld builds the whole library (CLI, `sveld()`, or the Vite plugin), not when parsing a single component on its own. Each event name there must be a string literal, or a conditional between them. The detail is typed from a literal argument, and an object or array literal is typed member by member as for a dispatch in the component (`{ id: "a" }` is `{ id: string; }`); a member or argument the helper computes, including one of its own variables, is `any`, so add an `@event` tag to type it more precisely. When sveld can't follow the dispatcher (a package import, a local function, a computed event name, or a helper that passes the dispatcher on), it reports `sveld/dispatch-escapes`, and you document those events with `@event` tags.
|
|
3119
|
-
|
|
3120
|
-
**Signature:**
|
|
3121
|
-
|
|
3122
|
-
```js
|
|
3123
|
-
/**
|
|
3124
|
-
* Optional event description
|
|
3125
|
-
* @event {EventDetail} eventname [inline description]
|
|
3126
|
-
*/
|
|
3127
|
-
```
|
|
3128
|
-
|
|
3129
|
-
**Example:**
|
|
3130
|
-
|
|
3131
|
-
```svelte
|
|
3132
|
-
<script>
|
|
3133
|
-
/**
|
|
3134
|
-
* Fired when a value is saved.
|
|
3135
|
-
* @event {{ id: string }} save
|
|
3136
|
-
*/
|
|
3137
|
-
let { onsave } = $props();
|
|
3138
|
-
</script>
|
|
3139
|
-
|
|
3140
|
-
<button onclick={() => onsave?.({ id: "1" })}>Save</button>
|
|
3141
|
-
```
|
|
3142
|
-
|
|
3143
|
-
**Svelte 5 Runes with dispatched events:**
|
|
3144
|
-
|
|
3145
|
-
```svelte
|
|
3146
|
-
<script>
|
|
3147
|
-
/**
|
|
3148
|
-
* @event {{ key: string }} button:key
|
|
3149
|
-
* @event {null} key - Fired when `key` changes.
|
|
3150
|
-
*/
|
|
3151
|
-
|
|
3152
|
-
let { key = "" } = $props();
|
|
3153
|
-
|
|
3154
|
-
import { createEventDispatcher } from "svelte";
|
|
3155
|
-
|
|
3156
|
-
const dispatch = createEventDispatcher();
|
|
3157
|
-
|
|
3158
|
-
$effect(() => {
|
|
3159
|
-
dispatch("button:key", { key });
|
|
3160
|
-
if (key) dispatch("key");
|
|
3161
|
-
});
|
|
3162
|
-
</script>
|
|
3163
|
-
```
|
|
3164
|
-
|
|
3165
|
-
<details>
|
|
3166
|
-
<summary>Svelte 3/4 (legacy) syntax</summary>
|
|
3167
|
-
|
|
3168
|
-
```svelte
|
|
3169
|
-
<script>
|
|
3170
|
-
/**
|
|
3171
|
-
* @event {{ key: string }} button:key
|
|
3172
|
-
* @event {null} key - Fired when `key` changes.
|
|
3173
|
-
*/
|
|
3174
|
-
|
|
3175
|
-
export let key = "";
|
|
3176
|
-
|
|
3177
|
-
import { createEventDispatcher } from "svelte";
|
|
3178
|
-
|
|
3179
|
-
const dispatch = createEventDispatcher();
|
|
3180
|
-
|
|
3181
|
-
$: dispatch("button:key", { key });
|
|
3182
|
-
$: if (key) dispatch("key");
|
|
3183
|
-
</script>
|
|
3184
|
-
```
|
|
3185
|
-
|
|
3186
|
-
</details>
|
|
3187
|
-
|
|
3188
|
-
Output is identical for both syntax modes.
|
|
3189
|
-
|
|
3190
|
-
Output:
|
|
3191
|
-
|
|
3192
|
-
```ts
|
|
3193
|
-
export default class Component extends SvelteComponentTyped<
|
|
3194
|
-
ComponentProps,
|
|
3195
|
-
{
|
|
3196
|
-
"button:key": CustomEvent<{ key: string }>;
|
|
3197
|
-
/** Fired when `key` changes. */ key: CustomEvent<null>;
|
|
3198
|
-
},
|
|
3199
|
-
Record<string, never>
|
|
3200
|
-
> {}
|
|
3201
|
-
```
|
|
3202
|
-
|
|
3203
|
-
#### Detail inference without `@event`
|
|
3204
|
-
|
|
3205
|
-
Without an `@event` tag or a typed dispatcher, `sveld` infers a dispatched event's detail type from the `dispatch()` call site itself:
|
|
3206
|
-
|
|
3207
|
-
- A scalar literal argument narrows to its literal type: `dispatch("count", 5)` types the detail as `5`, not `number`. Use `@event` or a typed dispatcher (below) to widen it.
|
|
3208
|
-
- An object or array literal argument infers a structural type per field/element: `dispatch("save", { id })` types the detail as `{ id: string }` (resolving the `id` variable's own type), and `dispatch("items", [1, 2])` types it as `number[]`. Fields or elements sveld can't resolve fall back to `any` individually, not for the whole detail.
|
|
3209
|
-
- A variable, as the whole detail (`dispatch("count", count)`) or as a field or element, takes its JSDoc `@type` or TS annotation. Without one, it's typed from its initializer the way a prop default is: `let count = 0` and `let count = $state(0)` are `number`, `$state<Item[]>([])` is `Item[]`, and `$derived(total * 2)` is `number`. A literal widens to its primitive, since a `let` can be reassigned. A variable initialized from a call (`let roll = Math.random()`), a destructured binding without an annotation, or a function parameter or nested variable is `any`.
|
|
3210
|
-
- `$host().dispatchEvent(new CustomEvent("name", { detail }))` in a custom element types `detail` the same way.
|
|
3211
|
-
- An empty object literal (`dispatch("reset", {})`) types the detail as `Record<string, never>`. A spread or computed key (`{ ...state }`, `{ [key]: 1 }`) adds fields sveld can't name, so the detail becomes `Record<string, any>`, or keeps the named fields next to a `[key: string]: any` index signature (`{ id: number; [key: string]: any }`).
|
|
3212
|
-
|
|
3213
|
-
#### Typed dispatchers
|
|
3214
|
-
|
|
3215
|
-
`createEventDispatcher<T>()`'s generic argument (`lang="ts"`, or the JSDoc `/** @type {import('svelte').EventDispatcher<T>} */` cast form) works like an `@event` block for every member of `T`, including ones never actually dispatched in the file:
|
|
3216
|
-
|
|
3217
|
-
```svelte
|
|
3218
|
-
<script lang="ts">
|
|
3219
|
-
import { createEventDispatcher } from "svelte";
|
|
3220
|
-
|
|
3221
|
-
const dispatch = createEventDispatcher<{ save: { id: string }; cancel: null }>();
|
|
3222
|
-
</script>
|
|
3223
|
-
```
|
|
3224
|
-
|
|
3225
|
-
`T` may also be a reference to a local `type`/`interface`. An `@event` tag for the same name still overrides the generic's detail type.
|
|
3226
|
-
|
|
3227
|
-
#### Using `@property` for complex event details
|
|
3228
|
-
|
|
3229
|
-
For events with complex object payloads, use `@property` to document individual fields. The main comment becomes the event description. See the [`@property`](#property) reference for the tag's valid contexts.
|
|
3230
|
-
|
|
3231
|
-
This is the idiomatic way to describe each field of an event detail. An inline object literal such as `@event {{ items: string[]; added: string[] }} change` types the payload but cannot carry per-field descriptions, since a nested block comment would terminate the host JSDoc. Declare the detail with `@type {object}` and `@property` instead to document every field.
|
|
3232
|
-
|
|
3233
|
-
**Signature:**
|
|
3234
|
-
|
|
3235
|
-
```js
|
|
3236
|
-
/**
|
|
3237
|
-
* Event description
|
|
3238
|
-
* @event eventname
|
|
3239
|
-
* @type {object}
|
|
3240
|
-
* @property {Type} propertyName - Property description
|
|
3241
|
-
*/
|
|
3242
|
-
```
|
|
3243
|
-
|
|
3244
|
-
**Example:**
|
|
3245
|
-
|
|
3246
|
-
```svelte
|
|
3247
|
-
<script>
|
|
3248
|
-
/**
|
|
3249
|
-
* Fired when the user submits the form
|
|
3250
|
-
*
|
|
3251
|
-
* @event submit
|
|
3252
|
-
* @type {object}
|
|
3253
|
-
* @property {string} name - The user's name
|
|
3254
|
-
* @property {string} email - The user's email address
|
|
3255
|
-
* @property {boolean} newsletter - Whether the user opted into the newsletter
|
|
3256
|
-
*/
|
|
3257
|
-
|
|
3258
|
-
let { name = "Jane Doe", email = "jane@example.com", newsletter = true } = $props();
|
|
3259
|
-
|
|
3260
|
-
import { createEventDispatcher } from "svelte";
|
|
3261
|
-
|
|
3262
|
-
const dispatch = createEventDispatcher();
|
|
3263
|
-
|
|
3264
|
-
function handleSubmit() {
|
|
3265
|
-
dispatch("submit", { name, email, newsletter });
|
|
3266
|
-
}
|
|
3267
|
-
</script>
|
|
3268
|
-
|
|
3269
|
-
<button type="button" onclick={handleSubmit}>Submit</button>
|
|
3270
|
-
```
|
|
3271
|
-
|
|
3272
|
-
<details>
|
|
3273
|
-
<summary>Svelte 3/4 (legacy) syntax</summary>
|
|
3274
|
-
|
|
3275
|
-
```svelte
|
|
3276
|
-
<script>
|
|
3277
|
-
/**
|
|
3278
|
-
* Fired when the user submits the form
|
|
3279
|
-
*
|
|
3280
|
-
* @event submit
|
|
3281
|
-
* @type {object}
|
|
3282
|
-
* @property {string} name - The user's name
|
|
3283
|
-
* @property {string} email - The user's email address
|
|
3284
|
-
* @property {boolean} newsletter - Whether the user opted into the newsletter
|
|
3285
|
-
*/
|
|
3286
|
-
|
|
3287
|
-
export let name = "Jane Doe";
|
|
3288
|
-
export let email = "jane@example.com";
|
|
3289
|
-
export let newsletter = true;
|
|
3290
|
-
|
|
3291
|
-
import { createEventDispatcher } from "svelte";
|
|
3292
|
-
|
|
3293
|
-
const dispatch = createEventDispatcher();
|
|
3294
|
-
|
|
3295
|
-
function handleSubmit() {
|
|
3296
|
-
dispatch("submit", { name, email, newsletter });
|
|
3297
|
-
}
|
|
3298
|
-
</script>
|
|
3299
|
-
|
|
3300
|
-
<button type="button" on:click={handleSubmit}>Submit</button>
|
|
3301
|
-
```
|
|
3302
|
-
|
|
3303
|
-
</details>
|
|
3304
|
-
|
|
3305
|
-
Output is identical for both syntax modes.
|
|
3306
|
-
|
|
3307
|
-
Output:
|
|
3308
|
-
|
|
3309
|
-
```ts
|
|
3310
|
-
export default class Component extends SvelteComponentTyped<
|
|
3311
|
-
ComponentProps,
|
|
3312
|
-
{
|
|
3313
|
-
/** Fired when the user submits the form */
|
|
3314
|
-
submit: CustomEvent<{
|
|
3315
|
-
/** The user's name */
|
|
3316
|
-
name: string;
|
|
3317
|
-
/** The user's email address */
|
|
3318
|
-
email: string;
|
|
3319
|
-
/** Whether the user opted into the newsletter */
|
|
3320
|
-
newsletter: boolean;
|
|
3321
|
-
}>;
|
|
3322
|
-
},
|
|
3323
|
-
Record<string, never>
|
|
3324
|
-
> {}
|
|
3325
|
-
```
|
|
3326
|
-
|
|
3327
|
-
#### Optional properties in event details
|
|
3328
|
-
|
|
3329
|
-
Like typedefs, you can mark event detail properties as optional with square brackets when they are not always in the payload.
|
|
3330
|
-
|
|
3331
|
-
**Example:**
|
|
3332
|
-
|
|
3333
|
-
```svelte
|
|
3334
|
-
<script>
|
|
3335
|
-
/**
|
|
3336
|
-
* Snowball event fired when throwing a snowball
|
|
3337
|
-
*
|
|
3338
|
-
* @event snowball
|
|
3339
|
-
* @type {object}
|
|
3340
|
-
* @property {boolean} isPacked - Indicates whether the snowball is tightly packed
|
|
3341
|
-
* @property {number} speed - The speed of the snowball in mph
|
|
3342
|
-
* @property {string} [color] - Optional color of the snowball
|
|
3343
|
-
* @property {number} [density=0.9] - Optional density with default value
|
|
3344
|
-
*/
|
|
3345
|
-
|
|
3346
|
-
let { speed = 50 } = $props();
|
|
3347
|
-
|
|
3348
|
-
import { createEventDispatcher } from "svelte";
|
|
3349
|
-
|
|
3350
|
-
const dispatch = createEventDispatcher();
|
|
3351
|
-
|
|
3352
|
-
function throwSnowball() {
|
|
3353
|
-
dispatch("snowball", {
|
|
3354
|
-
isPacked: true,
|
|
3355
|
-
speed,
|
|
3356
|
-
});
|
|
3357
|
-
}
|
|
3358
|
-
</script>
|
|
3359
|
-
|
|
3360
|
-
<button type="button" onclick={throwSnowball}>Throw</button>
|
|
3361
|
-
```
|
|
3362
|
-
|
|
3363
|
-
<details>
|
|
3364
|
-
<summary>Svelte 3/4 (legacy) syntax</summary>
|
|
3365
|
-
|
|
3366
|
-
```svelte
|
|
3367
|
-
<script>
|
|
3368
|
-
/**
|
|
3369
|
-
* Snowball event fired when throwing a snowball
|
|
3370
|
-
*
|
|
3371
|
-
* @event snowball
|
|
3372
|
-
* @type {object}
|
|
3373
|
-
* @property {boolean} isPacked - Indicates whether the snowball is tightly packed
|
|
3374
|
-
* @property {number} speed - The speed of the snowball in mph
|
|
3375
|
-
* @property {string} [color] - Optional color of the snowball
|
|
3376
|
-
* @property {number} [density=0.9] - Optional density with default value
|
|
3377
|
-
*/
|
|
3378
|
-
|
|
3379
|
-
export let speed = 50;
|
|
3380
|
-
|
|
3381
|
-
import { createEventDispatcher } from "svelte";
|
|
3382
|
-
|
|
3383
|
-
const dispatch = createEventDispatcher();
|
|
3384
|
-
|
|
3385
|
-
function throwSnowball() {
|
|
3386
|
-
dispatch("snowball", {
|
|
3387
|
-
isPacked: true,
|
|
3388
|
-
speed,
|
|
3389
|
-
});
|
|
3390
|
-
}
|
|
3391
|
-
</script>
|
|
3392
|
-
|
|
3393
|
-
<button type="button" on:click={throwSnowball}>Throw</button>
|
|
3394
|
-
```
|
|
3395
|
-
|
|
3396
|
-
</details>
|
|
3397
|
-
|
|
3398
|
-
Output is identical for both syntax modes.
|
|
3399
|
-
|
|
3400
|
-
Output:
|
|
3401
|
-
|
|
3402
|
-
```ts
|
|
3403
|
-
export default class Component extends SvelteComponentTyped<
|
|
3404
|
-
ComponentProps,
|
|
3405
|
-
{
|
|
3406
|
-
/** Snowball event fired when throwing a snowball */
|
|
3407
|
-
snowball: CustomEvent<{
|
|
3408
|
-
/** Indicates whether the snowball is tightly packed */
|
|
3409
|
-
isPacked: boolean;
|
|
3410
|
-
/** The speed of the snowball in mph */
|
|
3411
|
-
speed: number;
|
|
3412
|
-
/** Optional color of the snowball */
|
|
3413
|
-
color?: string;
|
|
3414
|
-
/** Optional density with default value @default 0.9 */
|
|
3415
|
-
density?: number;
|
|
3416
|
-
}>;
|
|
3417
|
-
},
|
|
3418
|
-
Record<string, never>
|
|
3419
|
-
> {}
|
|
3420
|
-
```
|
|
3421
|
-
|
|
3422
|
-
#### Discriminated unions in event details
|
|
3423
|
-
|
|
3424
|
-
When the event detail is a union (or any non-object shape), use `@type` to declare it directly. An explicit `@type` wins over `@property` tags, so the union is copied verbatim into the emitted `.d.ts` instead of being flattened into independent property unions. The only exception is `@type {object}`, which tells `sveld` to build the shape from `@property` tags (as shown above).
|
|
3425
|
-
|
|
3426
|
-
**Example:**
|
|
3427
|
-
|
|
3428
|
-
```svelte
|
|
3429
|
-
<script>
|
|
3430
|
-
/**
|
|
3431
|
-
* @event sort
|
|
3432
|
-
* @type {{ key: null; direction: "none" } | { key: string; direction: "ascending" | "descending" }}
|
|
3433
|
-
* Dispatched when a sortable column header would change the active sort.
|
|
3434
|
-
*/
|
|
3435
|
-
|
|
3436
|
-
import { createEventDispatcher } from "svelte";
|
|
3437
|
-
|
|
3438
|
-
const dispatch = createEventDispatcher();
|
|
3439
|
-
</script>
|
|
3440
|
-
```
|
|
3441
|
-
|
|
3442
|
-
Output:
|
|
3443
|
-
|
|
3444
|
-
```ts
|
|
3445
|
-
export default class Component extends SvelteComponentTyped<
|
|
3446
|
-
ComponentProps,
|
|
3447
|
-
{
|
|
3448
|
-
/** Dispatched when a sortable column header would change the active sort. */
|
|
3449
|
-
sort: CustomEvent<{ key: null; direction: "none" } | { key: string; direction: "ascending" | "descending" }>;
|
|
3450
|
-
},
|
|
3451
|
-
Record<string, never>
|
|
3452
|
-
> {}
|
|
3453
|
-
```
|
|
3454
|
-
|
|
3455
|
-
Any free-text prose after the tags is attached to the event description, not to a property doc.
|
|
3456
|
-
|
|
3457
|
-
### `@ignore` / `@internal`
|
|
3458
|
-
|
|
3459
|
-
`@ignore` and `@internal` are equivalent aliases: either one excludes a prop, event, slot, typedef, module export, entry export, or context from every output — JSON, Markdown, `.d.ts`, and the Custom Elements Manifest. Use them for implementation details that would otherwise leak into the public API docs.
|
|
3460
|
-
|
|
3461
|
-
The tag's position mirrors [`@deprecated`](#deprecated): before `@slot`/`@snippet`/`@typedef`/`@callback`, alongside the description, and after the `@event` line.
|
|
3462
|
-
|
|
3463
|
-
```svelte
|
|
3464
|
-
<script>
|
|
3465
|
-
/** The visible label. */
|
|
3466
|
-
export let label = "";
|
|
3467
|
-
|
|
3468
|
-
/**
|
|
3469
|
-
* Implementation detail; not part of the public API.
|
|
3470
|
-
* @internal
|
|
3471
|
-
*/
|
|
3472
|
-
export let debugId = "";
|
|
3473
|
-
|
|
3474
|
-
/**
|
|
3475
|
-
* Fired when the value changes.
|
|
3476
|
-
* @event {{ value: string }} change
|
|
3477
|
-
*/
|
|
3478
|
-
|
|
3479
|
-
/**
|
|
3480
|
-
* Fired for internal diagnostics only.
|
|
3481
|
-
* @event {{ reason: string }} debug
|
|
3482
|
-
* @internal
|
|
3483
|
-
*/
|
|
3484
|
-
|
|
3485
|
-
/**
|
|
3486
|
-
* @internal
|
|
3487
|
-
* @slot {{}} debug-panel
|
|
3488
|
-
*/
|
|
3489
|
-
</script>
|
|
3490
|
-
```
|
|
3491
|
-
|
|
3492
|
-
`debugId`, the `debug` event, and the `debug-panel` slot never appear in `COMPONENT_INDEX.md`, `COMPONENT_API.json`, the generated `.d.ts`, or the Custom Elements Manifest — as if they were never declared. Internally, the parser still records them (with an `internal: true` flag) on the raw parsed component; only `buildComponentApiDocument` — the shared step every writer runs through — filters them out, so a custom writer built on the raw parse result can still see them if it chooses to.
|
|
3493
|
-
|
|
3494
|
-
For a context (`setContext(key, value)`), tag the JSDoc on the *value* variable, the same place its type annotation and description already live:
|
|
3495
|
-
|
|
3496
|
-
```svelte
|
|
3497
|
-
<script>
|
|
3498
|
-
/**
|
|
3499
|
-
* @type {{ token: string }}
|
|
3500
|
-
* @internal
|
|
3501
|
-
*/
|
|
3502
|
-
let authContext = { token: "" };
|
|
3503
|
-
|
|
3504
|
-
setContext("auth", authContext);
|
|
3505
|
-
</script>
|
|
3506
|
-
```
|
|
3507
|
-
|
|
3508
|
-
A whole inline object literal passed directly to `setContext` (no intermediate variable) has no JSDoc position of its own, so `@internal` isn't supported there. An individual property *inside* one can be marked `@internal`, though, as long as its value is an identifier carrying its own JSDoc:
|
|
3509
|
-
|
|
3510
|
-
```svelte
|
|
3511
|
-
<script>
|
|
3512
|
-
/**
|
|
3513
|
-
* @type {string}
|
|
3514
|
-
* @internal
|
|
3515
|
-
*/
|
|
3516
|
-
let debugToken = "";
|
|
3517
|
-
|
|
3518
|
-
let publicUser = { name: "" };
|
|
3519
|
-
|
|
3520
|
-
setContext("session", { user: publicUser, debug: debugToken });
|
|
3521
|
-
</script>
|
|
3522
|
-
```
|
|
3523
|
-
|
|
3524
|
-
Only the `debug` property is excluded from `session`'s generated shape; `user` and the context itself are unaffected.
|
|
3525
|
-
|
|
3526
|
-
Because an internal member never appears in output, adding `@internal` to a previously-public prop, event, slot, or typedef is a breaking change for `--check` purposes (it disappears from the generated `.d.ts` just like an outright removal). Removing an already-`@internal` member, by contrast, is not breaking — it was never part of the committed public snapshot to begin with.
|
|
3527
|
-
|
|
3528
|
-
### `@deprecated`
|
|
3529
|
-
|
|
3530
|
-
Add `@deprecated` to a prop, event, slot, entry export, or exported accessor. An optional message after the tag can explain why or name a replacement.
|
|
3531
|
-
|
|
3532
|
-
```svelte
|
|
3533
|
-
<script>
|
|
3534
|
-
/**
|
|
3535
|
-
* The visible label.
|
|
3536
|
-
* @deprecated Use the `text` prop instead.
|
|
3537
|
-
*/
|
|
3538
|
-
export let label = "";
|
|
3539
|
-
|
|
3540
|
-
/**
|
|
3541
|
-
* Programmatically focus the field.
|
|
3542
|
-
* @deprecated Focus the underlying element directly.
|
|
3543
|
-
*/
|
|
3544
|
-
export function focus() {}
|
|
3545
|
-
|
|
3546
|
-
/**
|
|
3547
|
-
* @event {{ value: string }} change
|
|
3548
|
-
* @deprecated Listen for the native `input` event instead.
|
|
3549
|
-
*/
|
|
3550
|
-
|
|
3551
|
-
/**
|
|
3552
|
-
* Badge content rendered next to the label.
|
|
3553
|
-
* @deprecated Render the badge inline instead.
|
|
3554
|
-
* @slot {{ count: number }} badge
|
|
3555
|
-
*/
|
|
3556
|
-
</script>
|
|
3557
|
-
```
|
|
3558
|
-
|
|
3559
|
-
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. For entry exports (see [Documenting Entry Exports](#documenting-entry-exports)), put it in the JSDoc directly above the `const`/`function`/`class`/`type`/`interface` declaration, same as a prop.
|
|
3560
|
-
|
|
3561
|
-
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.
|
|
3562
|
-
|
|
3563
|
-
```ts
|
|
3564
|
-
/**
|
|
3565
|
-
* The visible label.
|
|
3566
|
-
* @deprecated Use the `text` prop instead.
|
|
3567
|
-
*/
|
|
3568
|
-
label?: string;
|
|
3569
|
-
```
|
|
3570
|
-
|
|
3571
|
-
```json
|
|
3572
|
-
{ "name": "label", "deprecated": "Use the `text` prop instead." }
|
|
3573
|
-
```
|
|
3574
|
-
|
|
3575
|
-
### `@since`
|
|
3576
|
-
|
|
3577
|
-
`@since` records the version a prop, event, or slot was introduced. `sveld` does not validate or parse the version text — whatever follows the tag is copied through as-is.
|
|
3578
|
-
|
|
3579
|
-
**Valid contexts:**
|
|
3580
|
-
|
|
3581
|
-
- **Prop / module export** — captured as a structured tag: JSON adds a `tags: [{ "name": "since", "body": "..." }]` array on the prop (kept separate from `description`), the generated `.d.ts` emits `@since ...` as its own JSDoc line above `@default`, and the Markdown table's Description column appends `@since ...` after the description, same as slots.
|
|
3582
|
-
- **`@event`** — same structured behavior as props, but only when `@since` is placed **after** the `@event` line in the same comment block (matching the [`@deprecated`](#deprecated) rule for events). Placed before `@event`, it is silently dropped.
|
|
3583
|
-
- **`@slot` / `@snippet`** — placed before the `@slot`/`@snippet` line, alongside the description. Fully surfaced in JSON, `.d.ts`, *and* the Markdown table. See [extra tags before `@slot`](#extra-jsdoc-tags-before-slot).
|
|
3584
|
-
- **`@typedef`** — **not supported.** A `@since` before `@typedef` produces no output anywhere; it is silently dropped.
|
|
3585
|
-
- **`@component` comments** — passed through verbatim as part of the raw HTML comment text. See [@component comments](#component-comments).
|
|
3586
|
-
|
|
3587
|
-
**Example:**
|
|
3588
|
-
|
|
3589
|
-
```svelte
|
|
3590
|
-
<script>
|
|
3591
|
-
/**
|
|
3592
|
-
* A width prop.
|
|
3593
|
-
* @since 1.2.0
|
|
3594
|
-
* @type {number}
|
|
3595
|
-
*/
|
|
3596
|
-
let { width = 0 } = $props();
|
|
3597
|
-
</script>
|
|
3598
|
-
```
|
|
3599
|
-
|
|
3600
|
-
Output (`.d.ts`):
|
|
3601
|
-
|
|
3602
|
-
```ts
|
|
3603
|
-
export type ScratchProps = {
|
|
3604
|
-
/**
|
|
3605
|
-
* A width prop.
|
|
3606
|
-
* @since 1.2.0
|
|
3607
|
-
* @default 0
|
|
3608
|
-
*/
|
|
3609
|
-
width?: number;
|
|
3610
|
-
};
|
|
3611
|
-
```
|
|
3612
|
-
|
|
3613
|
-
Output (JSON, relevant slice):
|
|
3614
|
-
|
|
3615
|
-
```json
|
|
3616
|
-
{ "name": "width", "description": "A width prop.", "tags": [{ "name": "since", "body": "1.2.0" }] }
|
|
3617
|
-
```
|
|
3618
|
-
|
|
3619
|
-
Output (Markdown props table Description column):
|
|
3620
|
-
|
|
3621
|
-
```
|
|
3622
|
-
A width prop.<br />@since 1.2.0
|
|
3623
|
-
```
|
|
3624
|
-
|
|
3625
|
-
### `@see`
|
|
3626
|
-
|
|
3627
|
-
`@see` adds a reference link or citation. Like `@since`, `sveld` does not resolve or validate the target — it is free text.
|
|
3628
|
-
|
|
3629
|
-
**Valid contexts:**
|
|
3630
|
-
|
|
3631
|
-
- **Prop / module export** — `@see` is *not* one of the small set of tags `sveld` structures specially, so it is folded verbatim into the prop's `description` as an extra line (`@see ...`) rather than getting its own `tags` entry. Because it lives in `description`, it *does* show up everywhere `description` is used: JSON, `.d.ts`, and the Markdown table.
|
|
3632
|
-
- **`@event`** — **not supported in either position** (before or after `@event`). A `@see` tag near an `@event` block is silently dropped in both JSON and `.d.ts`.
|
|
3633
|
-
- **`@slot` / `@snippet`** — placed before `@slot`/`@snippet`, alongside the description: fully supported in JSON, `.d.ts`, and Markdown, same as `@since`. See [extra tags before `@slot`](#extra-jsdoc-tags-before-slot).
|
|
3634
|
-
- **`@typedef`** — **not supported.** Dropped silently, same as `@since`.
|
|
3635
|
-
- **`@component` comments** — passed through verbatim, same as `@since`.
|
|
3636
|
-
|
|
3637
|
-
**Example:**
|
|
3638
|
-
|
|
3639
|
-
```svelte
|
|
3640
|
-
<script>
|
|
3641
|
-
/**
|
|
3642
|
-
* A width prop.
|
|
3643
|
-
* @see https://example.com/width-docs
|
|
3644
|
-
* @type {number}
|
|
3645
|
-
*/
|
|
3646
|
-
let { width = 0 } = $props();
|
|
3647
|
-
</script>
|
|
3648
|
-
```
|
|
3649
|
-
|
|
3650
|
-
Output (`.d.ts`):
|
|
3651
|
-
|
|
3652
|
-
```ts
|
|
3653
|
-
export type ScratchProps = {
|
|
3654
|
-
/**
|
|
3655
|
-
* A width prop.
|
|
3656
|
-
* @see https://example.com/width-docs
|
|
3657
|
-
* @default 0
|
|
3658
|
-
*/
|
|
3659
|
-
width?: number;
|
|
3660
|
-
};
|
|
3661
|
-
```
|
|
3662
|
-
|
|
3663
|
-
Output (Markdown props table Description column):
|
|
3664
|
-
|
|
3665
|
-
```
|
|
3666
|
-
A width prop.<br />@see https://example.com/width-docs
|
|
3667
|
-
```
|
|
3668
|
-
|
|
3669
|
-
### `@link`
|
|
3670
|
-
|
|
3671
|
-
`{@link target}` (optionally `{@link target|display text}`) is an inline JSDoc tag used inside prose, not a block-level tag like the others on this page. `sveld`'s comment parser only treats a *line* as starting a new tag when it begins with `@` — `{@link ...}` always starts with `{`, so it's never intercepted. It is always literal text.
|
|
3672
|
-
|
|
3673
|
-
**Valid contexts:** anywhere free-form description text is read — prop and module export descriptions, event descriptions, slot descriptions, entry export descriptions, typedef descriptions, `@component` HTML comments, and `@example` bodies. `sveld` never resolves or validates the target. JSON `description` and `.d.ts` JSDoc always keep the tag verbatim. **Markdown is the one exception:** in a prop, event, slot, or entry export's Description table cell, `{@link target|text}` / `{@link target}` is rewritten to a Markdown link, `[text](target)` / `[target](target)`, except inside a fenced code block. Typedef descriptions (rendered as a `.d.ts`-style code block) and `@component` comments keep the tag literal in Markdown too, since neither goes through the table-cell renderer.
|
|
3674
|
-
|
|
3675
|
-
**Example:**
|
|
3676
|
-
|
|
3677
|
-
```svelte
|
|
3678
|
-
<script>
|
|
3679
|
-
/**
|
|
3680
|
-
* The element's width in pixels. See {@link https://example.com/width|width docs}.
|
|
3681
|
-
* @type {number}
|
|
3682
|
-
*/
|
|
3683
|
-
let { width = 0 } = $props();
|
|
3684
|
-
</script>
|
|
3685
|
-
```
|
|
3686
|
-
|
|
3687
|
-
Output (`.d.ts`):
|
|
3688
|
-
|
|
3689
|
-
```ts
|
|
3690
|
-
export type ScratchProps = {
|
|
3691
|
-
/**
|
|
3692
|
-
* The element's width in pixels. See {@link https://example.com/width|width docs}.
|
|
3693
|
-
* @default 0
|
|
3694
|
-
*/
|
|
3695
|
-
width?: number;
|
|
3696
|
-
};
|
|
3697
|
-
```
|
|
3698
|
-
|
|
3699
|
-
The same literal string appears unchanged in JSON `description`. The Markdown table's Description column instead shows: `The element's width in pixels. See [width docs](https://example.com/width).`
|
|
3700
|
-
|
|
3701
|
-
### `@example`
|
|
3702
|
-
|
|
3703
|
-
`@example` has two related uses: as a plain JSDoc tag whose body is copied into generated output, and — with `checkExamples: true` — as input to sveld's compile-checker for plain TS/JS example code. This section covers the tag itself; see [Compile-checked `@example` blocks (`checkExamples`)](#compile-checked-example-blocks-checkexamples) for the type-checking feature.
|
|
3704
|
-
|
|
3705
|
-
`@example` shares its underlying mechanism with `@since` (both are in the small set of tags `sveld` treats as structured "IDE passthrough" tags), so the valid contexts are the same:
|
|
3706
|
-
|
|
3707
|
-
- **Prop / module export (including functions)** — structured `tags` entry in JSON, its own `@example` block in `.d.ts`, and appended to the Markdown table's Description column, same as `@since`.
|
|
3708
|
-
- **`@event`** — only when placed **after** the `@event` line in the same block, same rule as `@since`.
|
|
3709
|
-
- **`@slot` / `@snippet`** — placed before `@slot`/`@snippet`: fully supported in JSON, `.d.ts`, and Markdown. See [extra tags before `@slot`](#extra-jsdoc-tags-before-slot).
|
|
3710
|
-
- **`@typedef`** — **not supported**, dropped silently, same as `@since`.
|
|
3711
|
-
- **`@component` comments** — passed through verbatim; this is already how the [`@component` comments](#component-comments) example on this page uses `@example`.
|
|
3712
|
-
|
|
3713
|
-
**Example:**
|
|
3714
|
-
|
|
3715
|
-
```svelte
|
|
3716
|
-
<script>
|
|
3717
|
-
/**
|
|
3718
|
-
* Formats a value.
|
|
3719
|
-
* @param {string} value
|
|
3720
|
-
* @returns {string}
|
|
3721
|
-
* @example
|
|
3722
|
-
* ```js
|
|
3723
|
-
* formatValue("ok");
|
|
3724
|
-
* ```
|
|
3725
|
-
*/
|
|
3726
|
-
export function formatValue(value) {
|
|
3727
|
-
return value;
|
|
3728
|
-
}
|
|
3729
|
-
</script>
|
|
3730
|
-
```
|
|
3731
|
-
|
|
3732
|
-
Output (`.d.ts`):
|
|
3733
|
-
|
|
3734
|
-
```ts
|
|
3735
|
-
/**
|
|
3736
|
-
* Formats a value.
|
|
3737
|
-
* @example
|
|
3738
|
-
* ```js
|
|
3739
|
-
* formatValue("ok");
|
|
3740
|
-
* ```
|
|
3741
|
-
*/
|
|
3742
|
-
formatValue: (value: string) => string;
|
|
3743
|
-
```
|
|
3744
|
-
|
|
3745
|
-
`@param`/`@returns` are consumed into the function's type signature rather than kept as separate JSDoc lines.
|
|
3746
|
-
|
|
3747
|
-
In Markdown table cells, a fenced block (in an `@example` body or anywhere in a description) renders as `<pre><code>` with one `<br />` per line, since a table row can't span lines. `<pre>` keeps the indentation, and GitHub renders it as a monospaced block inside the cell. The example above shows up in the Description column as:
|
|
3748
|
-
|
|
3749
|
-
```
|
|
3750
|
-
Formats a value.<br />@example <pre><code>formatValue("ok");</code></pre>
|
|
3751
|
-
```
|
|
3752
|
-
|
|
3753
|
-
`llms-full.txt` instead puts `(code below)` in the cell and prints the code after the table as a regular multi-line fenced block, under a ``Code for `formatValue`:`` line.
|
|
3754
|
-
|
|
3755
|
-
Output (Markdown props table Description column, newlines rendered as `<br />`):
|
|
3756
|
-
|
|
3757
|
-
````
|
|
3758
|
-
Formats a value.<br />@example ```js<br /> formatValue("ok");<br /> ```
|
|
3759
|
-
````
|
|
3760
|
-
|
|
3761
|
-
### Context API
|
|
3762
|
-
|
|
3763
|
-
`sveld` generates TypeScript definitions for Svelte's `setContext`/`getContext` by extracting types from JSDoc on context values.
|
|
3764
|
-
|
|
3765
|
-
#### How it works
|
|
3766
|
-
|
|
3767
|
-
When you call `setContext` in a component, `sveld`:
|
|
3768
|
-
|
|
3769
|
-
1. Detects the `setContext` call
|
|
3770
|
-
2. Resolves the context key (see [Supported context keys](#supported-context-keys))
|
|
3771
|
-
3. Finds JSDoc `@type` annotations on the variables being passed
|
|
3772
|
-
4. Generates a TypeScript type export for the context
|
|
3773
|
-
|
|
3774
|
-
#### Supported context keys
|
|
3775
|
-
|
|
3776
|
-
The key becomes the `{PascalCase}Context` type name. `sveld` can resolve:
|
|
3777
|
-
|
|
3778
|
-
| Key form | Example | Generated type |
|
|
3779
|
-
| --- | --- | --- |
|
|
3780
|
-
| String literal | `setContext("simple-modal", …)` | `SimpleModalContext` |
|
|
3781
|
-
| Static template literal | `` setContext(`simple-modal`, …) `` | `SimpleModalContext` |
|
|
3782
|
-
| `const`-bound string | `const KEY = "simple-modal";`<br>`setContext(KEY, …)` | `SimpleModalContext` |
|
|
3783
|
-
| `Symbol()` / `Symbol.for()` | `setContext(Symbol("tabs"), …)` | `TabsContext` |
|
|
3784
|
-
| Imported `export const` string | `import { KEY } from "./keys.js";`<br>`setContext(KEY, …)` | `SimpleModalContext` |
|
|
3785
|
-
| Namespace-imported `export const` string | `import * as keys from "./keys.js";`<br>`setContext(keys.KEY, …)` | `SimpleModalContext` |
|
|
3786
|
-
|
|
3787
|
-
Characters that can't appear in a TypeScript identifier are dropped and start a new PascalCase word, and a name starting with a digit gets a leading `_`: `"@scope/ctx"` becomes `ScopeCtxContext` and `"123"` becomes `_123Context`.
|
|
3788
|
-
|
|
3789
|
-
`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.
|
|
3790
|
-
|
|
3791
|
-
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`.
|
|
3792
|
-
|
|
3793
|
-
An imported key (named, or read off a namespace import, including one another module re-exports with `export * as keys from "./keys.js"`) is read from its module, following re-exports, when that module is a relative `.js`/`.ts` file declaring it as `export const KEY = "simple-modal"` (or a static template literal). This works when sveld builds a whole library (CLI, `sveld()`, or the Vite plugin), not when parsing a single component on its own.
|
|
3794
|
-
|
|
3795
|
-
Anything else (dynamic identifiers, `let` exports, template interpolation, other function calls) records a `sveld/context-key-unresolved` diagnostic. No context type is generated.
|
|
3796
|
-
|
|
3797
|
-
#### Example
|
|
3798
|
-
|
|
3799
|
-
**Modal.svelte**
|
|
3800
|
-
|
|
3801
|
-
```svelte
|
|
3802
|
-
<script>
|
|
3803
|
-
import { setContext } from "svelte";
|
|
3804
|
-
|
|
3805
|
-
/**
|
|
3806
|
-
* Close the modal
|
|
3807
|
-
* @type {() => void}
|
|
3808
|
-
*/
|
|
3809
|
-
const close = () => {
|
|
3810
|
-
// Close logic
|
|
3811
|
-
};
|
|
3812
|
-
|
|
3813
|
-
/**
|
|
3814
|
-
* Open the modal with content
|
|
3815
|
-
* @type {(component: any, props?: any) => void}
|
|
3816
|
-
*/
|
|
3817
|
-
const open = (component, props) => {
|
|
3818
|
-
// Open logic
|
|
3819
|
-
};
|
|
3820
|
-
|
|
3821
|
-
setContext("simple-modal", { open, close });
|
|
3822
|
-
|
|
3823
|
-
let { children } = $props();
|
|
3824
|
-
</script>
|
|
3825
|
-
|
|
3826
|
-
<div class="modal">
|
|
3827
|
-
{@render children?.()}
|
|
3828
|
-
</div>
|
|
3829
|
-
```
|
|
3830
|
-
|
|
3831
|
-
<details>
|
|
3832
|
-
<summary>Svelte 3/4 (legacy) syntax</summary>
|
|
3833
|
-
|
|
3834
|
-
```svelte
|
|
3835
|
-
<script>
|
|
3836
|
-
import { setContext } from "svelte";
|
|
3837
|
-
|
|
3838
|
-
/**
|
|
3839
|
-
* Close the modal
|
|
3840
|
-
* @type {() => void}
|
|
3841
|
-
*/
|
|
3842
|
-
const close = () => {
|
|
3843
|
-
// Close logic
|
|
3844
|
-
};
|
|
3845
|
-
|
|
3846
|
-
/**
|
|
3847
|
-
* Open the modal with content
|
|
3848
|
-
* @type {(component: any, props?: any) => void}
|
|
3849
|
-
*/
|
|
3850
|
-
const open = (component, props) => {
|
|
3851
|
-
// Open logic
|
|
3852
|
-
};
|
|
3853
|
-
|
|
3854
|
-
setContext("simple-modal", { open, close });
|
|
3855
|
-
</script>
|
|
3856
|
-
|
|
3857
|
-
<div class="modal">
|
|
3858
|
-
<slot />
|
|
3859
|
-
</div>
|
|
3860
|
-
```
|
|
3861
|
-
|
|
3862
|
-
</details>
|
|
3863
|
-
|
|
3864
|
-
Output is identical for both syntax modes.
|
|
3865
|
-
|
|
3866
|
-
**Generated TypeScript definition:**
|
|
3867
|
-
|
|
3868
|
-
```ts
|
|
3869
|
-
export type SimpleModalContext = {
|
|
3870
|
-
/** Open the modal with content */
|
|
3871
|
-
open: (component: any, props?: any) => void;
|
|
3872
|
-
/** Close the modal */
|
|
3873
|
-
close: () => void;
|
|
3874
|
-
};
|
|
3875
|
-
|
|
3876
|
-
export type ModalProps = {};
|
|
3877
|
-
|
|
3878
|
-
export default class Modal extends SvelteComponentTyped<
|
|
3879
|
-
ModalProps,
|
|
3880
|
-
Record<string, any>,
|
|
3881
|
-
{ default: Record<string, never> }
|
|
3882
|
-
> {}
|
|
3883
|
-
```
|
|
3884
|
-
|
|
3885
|
-
**Consumer usage:**
|
|
3886
|
-
|
|
3887
|
-
```svelte
|
|
3888
|
-
<script>
|
|
3889
|
-
import { getContext } from 'svelte';
|
|
3890
|
-
import type { SimpleModalContext } from 'modal-library/Modal.svelte';
|
|
3891
|
-
|
|
3892
|
-
const { close, open } = getContext<SimpleModalContext>('simple-modal');
|
|
3893
|
-
</script>
|
|
3894
|
-
|
|
3895
|
-
<button on:click={close}>Close</button>
|
|
3896
|
-
```
|
|
3897
|
-
|
|
3898
|
-
#### Explicitly typing contexts
|
|
3899
|
-
|
|
3900
|
-
There are several ways to type contexts:
|
|
3901
|
-
|
|
3902
|
-
**Option 1: Inline JSDoc on variables (recommended)**
|
|
3903
|
-
|
|
3904
|
-
```svelte
|
|
3905
|
-
<script>
|
|
3906
|
-
import { setContext } from 'svelte';
|
|
3907
|
-
|
|
3908
|
-
/**
|
|
3909
|
-
* @type {() => void}
|
|
3910
|
-
*/
|
|
3911
|
-
const close = () => {};
|
|
3912
|
-
|
|
3913
|
-
setContext('modal', { close });
|
|
3914
|
-
</script>
|
|
3915
|
-
```
|
|
3916
|
-
|
|
3917
|
-
**Option 2: Using @typedef for complex types**
|
|
3918
|
-
|
|
3919
|
-
```svelte
|
|
3920
|
-
<script>
|
|
3921
|
-
import { setContext } from 'svelte';
|
|
3922
|
-
|
|
3923
|
-
/**
|
|
3924
|
-
* @typedef {object} TabData
|
|
3925
|
-
* @property {string} id
|
|
3926
|
-
* @property {string} label
|
|
3927
|
-
* @property {boolean} [disabled]
|
|
3928
|
-
*/
|
|
3929
|
-
|
|
3930
|
-
/**
|
|
3931
|
-
* @type {(tab: TabData) => void}
|
|
3932
|
-
*/
|
|
3933
|
-
const addTab = (tab) => {};
|
|
3934
|
-
|
|
3935
|
-
setContext('tabs', { addTab });
|
|
3936
|
-
</script>
|
|
3937
|
-
```
|
|
3938
|
-
|
|
3939
|
-
**Option 3: Passing a typed variable as the whole value**
|
|
3940
|
-
|
|
3941
|
-
```svelte
|
|
3942
|
-
<script>
|
|
3943
|
-
import { setContext } from 'svelte';
|
|
3944
|
-
|
|
3945
|
-
/**
|
|
3946
|
-
* Modal controls
|
|
3947
|
-
* @type {import("./types").ModalAPI}
|
|
3948
|
-
*/
|
|
3949
|
-
const modalAPI = {
|
|
3950
|
-
open: () => {},
|
|
3951
|
-
close: () => {}
|
|
3952
|
-
};
|
|
3953
|
-
|
|
3954
|
-
setContext('modal', modalAPI);
|
|
3955
|
-
</script>
|
|
3956
|
-
```
|
|
3957
|
-
|
|
3958
|
-
`getContext('modal')` returns `modalAPI` itself, so the context type is the variable's type, and the variable's description documents it:
|
|
3959
|
-
|
|
3960
|
-
```ts
|
|
3961
|
-
/**
|
|
3962
|
-
* Modal controls
|
|
3963
|
-
*/
|
|
3964
|
-
export type ModalContext = import("./types").ModalAPI;
|
|
3965
|
-
```
|
|
3966
|
-
|
|
3967
|
-
When the variable's type is an object type literal (`@type {{ open: () => void }}` or `const api: { open: () => void } = ...`), its members become the context type's members instead. An untyped `const` holding an object literal is described from that literal, as with `{ ...api }`. Any other untyped variable is typed from its initializer the way a prop default is (`let count = $state(0)` or `let count = 0` gives `number`, `$state<Item[]>([])` gives `Item[]`), and one sveld can't type gives `any` with a `sveld/context-any-type` diagnostic. In `COMPONENT_API.json`, a context typed this way has a `type` field holding the whole type and an empty `properties` array.
|
|
3968
|
-
|
|
3969
|
-
**Option 4: Direct object literal with inline functions**
|
|
3970
|
-
|
|
3971
|
-
```svelte
|
|
3972
|
-
<script>
|
|
3973
|
-
import { setContext } from 'svelte';
|
|
3974
|
-
|
|
3975
|
-
// sveld infers basic function signatures
|
|
3976
|
-
setContext('modal', {
|
|
3977
|
-
open: (component, props) => {}, // Inferred as (arg, arg) => any
|
|
3978
|
-
close: () => {} // Inferred as () => any
|
|
3979
|
-
});
|
|
3980
|
-
</script>
|
|
3981
|
-
```
|
|
3982
|
-
|
|
3983
|
-
> Inline functions without `@type` annotations get generic inferred signatures. Add explicit JSDoc when you care about the shape.
|
|
3984
|
-
|
|
3985
|
-
#### Notes
|
|
3986
|
-
|
|
3987
|
-
- 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, either local or an imported `export const`. Dynamic expressions (runtime identifiers, template interpolation, other function calls) are skipped with a `sveld/context-key-unresolved` diagnostic.
|
|
3988
|
-
- Variables passed to `setContext` should have JSDoc `@type` annotations (or native TypeScript types) for accurate types. Without one, a variable is typed from its initializer the way a prop default is, unwrapping `$state`, `$state.raw`, and `$derived` (a rune's type argument, as in `$state<number>(0)`, wins). A variable passed as the whole value (`setContext("modal", modalAPI)`) types the context as that variable's type; one passed inside an object literal (`setContext("modal", { modalAPI })`) becomes a property
|
|
3989
|
-
- The value must be an object literal or a variable. Any other expression (such as `setContext("store", writable(0))`) is skipped with a `sveld/context-value-unresolved` diagnostic.
|
|
3990
|
-
- The generated type name follows the pattern: `{PascalCase}Context`. Separators (underscores and any character that can't appear in an identifier, such as hyphens, dots, colons, slashes, `@`, and spaces) are stripped and each segment is capitalized. A name starting with a digit gets a leading `_`:
|
|
3991
|
-
| Context Key | Generated Type Name |
|
|
3992
|
-
| --- | --- |
|
|
3993
|
-
| `"simple-modal"` | `SimpleModalContext` |
|
|
3994
|
-
| `"user_settings"` | `UserSettingsContext` |
|
|
3995
|
-
| `"Carbon.Modal"` | `CarbonModalContext` |
|
|
3996
|
-
| `"Carbon:Modal"` | `CarbonModalContext` |
|
|
3997
|
-
| `"app/modal"` | `AppModalContext` |
|
|
3998
|
-
| `"My Context"` | `MyContextContext` |
|
|
3999
|
-
| `"@scope/ctx"` | `ScopeCtxContext` |
|
|
4000
|
-
| `"123"` | `_123Context` |
|
|
4001
|
-
| `"Tabs"` | `TabsContext` |
|
|
4002
|
-
- If no type annotation is found, the type defaults to `any` with a warning
|
|
4003
|
-
|
|
4004
|
-
### `@restProps`
|
|
4005
|
-
|
|
4006
|
-
`sveld` can detect inline HTML elements that `$$restProps` is forwarded to. It cannot infer the underlying element for instantiated components.
|
|
4007
|
-
|
|
4008
|
-
Use `@restProps` to name the element tags `$$restProps` is forwarded to.
|
|
4009
|
-
|
|
4010
|
-
**Signature:**
|
|
4011
|
-
|
|
4012
|
-
```js
|
|
4013
|
-
/**
|
|
4014
|
-
* Single element
|
|
4015
|
-
* @restProps {tagname}
|
|
4016
|
-
*
|
|
4017
|
-
* Multiple elements
|
|
4018
|
-
* @restProps {tagname-1 | tagname-2 | tagname-3}
|
|
4019
|
-
*/
|
|
4020
|
-
```
|
|
4021
|
-
|
|
4022
|
-
**Example:**
|
|
4023
|
-
|
|
4024
|
-
```svelte
|
|
4025
|
-
<script>
|
|
4026
|
-
import Button from "./Button.svelte";
|
|
4027
|
-
|
|
4028
|
-
/** @restProps {h1 | button} */
|
|
4029
|
-
let { edit = false, children, ...restProps } = $props();
|
|
4030
|
-
</script>
|
|
4031
|
-
|
|
4032
|
-
{#if edit}
|
|
4033
|
-
<Button {...restProps} />
|
|
4034
|
-
{:else}
|
|
4035
|
-
<h1 {...restProps}>
|
|
4036
|
-
{@render children?.()}
|
|
4037
|
-
</h1>
|
|
4038
|
-
{/if}
|
|
4039
|
-
```
|
|
4040
|
-
|
|
4041
|
-
<details>
|
|
4042
|
-
<summary>Svelte 3/4 (legacy) syntax</summary>
|
|
4043
|
-
|
|
4044
|
-
```svelte
|
|
4045
|
-
<script>
|
|
4046
|
-
/** @restProps {h1 | button} */
|
|
4047
|
-
export let edit = false;
|
|
4048
|
-
|
|
4049
|
-
import Button from "./Button.svelte";
|
|
4050
|
-
</script>
|
|
4051
|
-
|
|
4052
|
-
{#if edit}
|
|
4053
|
-
<Button {...$$restProps} />
|
|
4054
|
-
{:else}
|
|
4055
|
-
<h1 {...$$restProps}><slot /></h1>
|
|
4056
|
-
{/if}
|
|
4057
|
-
```
|
|
4058
|
-
|
|
4059
|
-
</details>
|
|
4060
|
-
|
|
4061
|
-
### `@extendProps`
|
|
4062
|
-
|
|
4063
|
-
When a component wraps another, use `@extendProps` to extend generated props.
|
|
4064
|
-
|
|
4065
|
-
> `@extends` works as an alias, but prefer `@extendProps` to avoid clashing with standard JSDoc `@extends` for class inheritance.
|
|
4066
|
-
|
|
4067
|
-
**Signature:**
|
|
4068
|
-
|
|
4069
|
-
```js
|
|
4070
|
-
/**
|
|
4071
|
-
* @extendProps {<relative path to component>} ComponentProps
|
|
4072
|
-
*/
|
|
4073
|
-
```
|
|
4074
|
-
|
|
4075
|
-
**Example:**
|
|
4076
|
-
|
|
4077
|
-
```js
|
|
4078
|
-
/** @extendProps {"./Button.svelte"} ButtonProps */
|
|
4079
|
-
|
|
4080
|
-
export const secondary = true;
|
|
4081
|
-
|
|
4082
|
-
import Button from "./Button.svelte";
|
|
4083
|
-
```
|
|
4084
|
-
|
|
4085
|
-
The named interface must match the target's generated props type name exactly — the default `<Name>Props`, or whatever [`typesOptions.typeNames`](#typesoptionstypenames) templates it to.
|
|
4086
|
-
|
|
4087
|
-
### `@template`
|
|
4088
|
-
|
|
4089
|
-
Svelte supports defining generics via the [`generics` attribute](https://svelte.dev/docs/svelte/typescript) on the script tag, but this requires `lang="ts"`:
|
|
4090
|
-
|
|
4091
|
-
```svelte
|
|
4092
|
-
<script lang="ts" generics="Row extends DataTableRow = any"></script>
|
|
4093
|
-
```
|
|
4094
|
-
|
|
4095
|
-
`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.
|
|
4096
|
-
|
|
4097
|
-
**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.
|
|
4098
|
-
|
|
4099
|
-
**Signature:** Uses standard [JSDoc `@template` syntax](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#template):
|
|
4100
|
-
|
|
4101
|
-
```js
|
|
4102
|
-
/**
|
|
4103
|
-
* @template {Constraint} [Name=Default]
|
|
4104
|
-
*/
|
|
4105
|
-
```
|
|
4106
|
-
|
|
4107
|
-
**Example:**
|
|
4108
|
-
|
|
4109
|
-
```js
|
|
4110
|
-
/**
|
|
4111
|
-
* @template {DataTableRow} [Row=DataTableRow]
|
|
4112
|
-
*/
|
|
4113
|
-
```
|
|
4114
|
-
|
|
4115
|
-
**Component example:**
|
|
4116
|
-
|
|
4117
|
-
```svelte
|
|
4118
|
-
<script>
|
|
4119
|
-
/**
|
|
4120
|
-
* @typedef {{ id: string | number; [key: string]: any; }} DataTableRow
|
|
4121
|
-
* @typedef {Exclude<keyof Row, "id">} DataTableKey<Row>
|
|
4122
|
-
* @typedef {{ key: DataTableKey<Row>; value: string; }} DataTableHeader<Row=DataTableRow>
|
|
4123
|
-
* @template {DataTableRow} [Row=DataTableRow]
|
|
4124
|
-
*/
|
|
4125
|
-
|
|
4126
|
-
let {
|
|
4127
|
-
/** @type {ReadonlyArray<DataTableHeader<Row>>} */
|
|
4128
|
-
headers = [],
|
|
4129
|
-
/** @type {ReadonlyArray<Row>} */
|
|
4130
|
-
rows = [],
|
|
4131
|
-
children,
|
|
4132
|
-
} = $props();
|
|
4133
|
-
</script>
|
|
4134
|
-
|
|
4135
|
-
{@render children?.({ headers, rows })}
|
|
4136
|
-
```
|
|
4137
|
-
|
|
4138
|
-
<details>
|
|
4139
|
-
<summary>Svelte 3/4 (legacy) syntax</summary>
|
|
4140
|
-
|
|
4141
|
-
```svelte
|
|
4142
|
-
<script>
|
|
4143
|
-
/**
|
|
4144
|
-
* @typedef {{ id: string | number; [key: string]: any; }} DataTableRow
|
|
4145
|
-
* @typedef {Exclude<keyof Row, "id">} DataTableKey<Row>
|
|
4146
|
-
* @typedef {{ key: DataTableKey<Row>; value: string; }} DataTableHeader<Row=DataTableRow>
|
|
4147
|
-
* @template {DataTableRow} [Row=DataTableRow]
|
|
4148
|
-
*/
|
|
4149
|
-
|
|
4150
|
-
/** @type {ReadonlyArray<DataTableHeader<Row>>} */
|
|
4151
|
-
export let headers = [];
|
|
4152
|
-
|
|
4153
|
-
/** @type {ReadonlyArray<Row>} */
|
|
4154
|
-
export let rows = [];
|
|
4155
|
-
</script>
|
|
4156
|
-
|
|
4157
|
-
<slot {headers} {rows} />
|
|
4158
|
-
```
|
|
4159
|
-
|
|
4160
|
-
</details>
|
|
4161
|
-
|
|
4162
|
-
Output is identical for both syntax modes.
|
|
4163
|
-
|
|
4164
|
-
Generated output looks like this:
|
|
4165
|
-
|
|
4166
|
-
```ts
|
|
4167
|
-
export type ComponentProps<Row extends DataTableRow = DataTableRow> = {
|
|
4168
|
-
headers?: ReadonlyArray<DataTableHeader<Row>>;
|
|
4169
|
-
rows?: ReadonlyArray<Row>;
|
|
4170
|
-
};
|
|
4171
|
-
|
|
4172
|
-
export default class Component<
|
|
4173
|
-
Row extends DataTableRow = DataTableRow,
|
|
4174
|
-
> extends SvelteComponentTyped<
|
|
4175
|
-
ComponentProps<Row>,
|
|
4176
|
-
Record<string, any>,
|
|
4177
|
-
Record<string, any>
|
|
4178
|
-
> {}
|
|
4179
|
-
```
|
|
4180
|
-
|
|
4181
|
-
For multiple generics, use separate `@template` tags:
|
|
4182
|
-
|
|
4183
|
-
```js
|
|
4184
|
-
/**
|
|
4185
|
-
* @template {DataTableRow} [Row=DataTableRow]
|
|
4186
|
-
* @template {DataTableRow} [Header=DataTableRow]
|
|
4187
|
-
*/
|
|
4188
|
-
```
|
|
4189
|
-
|
|
4190
|
-
```ts
|
|
4191
|
-
export type ComponentProps<
|
|
4192
|
-
Row extends DataTableRow = DataTableRow,
|
|
4193
|
-
Header extends DataTableRow = DataTableRow,
|
|
4194
|
-
> = { ... };
|
|
4195
|
-
|
|
4196
|
-
export default class Component<
|
|
4197
|
-
Row extends DataTableRow = DataTableRow,
|
|
4198
|
-
Header extends DataTableRow = DataTableRow,
|
|
4199
|
-
> extends SvelteComponentTyped<
|
|
4200
|
-
ComponentProps<Row, Header>,
|
|
4201
|
-
Record<string, any>,
|
|
4202
|
-
Record<string, any>
|
|
4203
|
-
> {}
|
|
4204
|
-
```
|
|
4205
|
-
|
|
4206
|
-
### `@generics`
|
|
4207
|
-
|
|
4208
|
-
As an alternative to `@template`, sveld supports `@generics`. Unlike `@template`, which [JSDoc/TypeScript support officially](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#template), `@generics` is sveld-specific. The syntax can be easier to read because the full constraint is inline:
|
|
4209
|
-
|
|
4210
|
-
```js
|
|
4211
|
-
/**
|
|
4212
|
-
* @generics {Row extends DataTableRow = DataTableRow} Row
|
|
4213
|
-
*/
|
|
4214
|
-
```
|
|
4215
|
-
|
|
4216
|
-
This is equivalent to:
|
|
4217
|
-
|
|
4218
|
-
```js
|
|
4219
|
-
/**
|
|
4220
|
-
* @template {DataTableRow} [Row=DataTableRow]
|
|
4221
|
-
*/
|
|
4222
|
-
```
|
|
4223
|
-
|
|
4224
|
-
For multiple generics, use a single `@generics` tag with comma-separated names:
|
|
4225
|
-
|
|
4226
|
-
```js
|
|
4227
|
-
/**
|
|
4228
|
-
* @generics {Row extends DataTableRow = DataTableRow, Header extends DataTableRow = DataTableRow} Row,Header
|
|
4229
|
-
*/
|
|
4230
|
-
```
|
|
4231
|
-
|
|
4232
|
-
### `@component` comments
|
|
4233
|
-
|
|
4234
|
-
The Svelte Language Server supports component-level comments through the following syntax: `<!-- @component [comment] -->`.
|
|
4235
|
-
|
|
4236
|
-
`sveld` copies these over to the exported default component in the TypeScript definition.
|
|
4237
|
-
|
|
4238
|
-
**Example:**
|
|
4239
|
-
|
|
4240
|
-
```svelte
|
|
4241
|
-
<!-- @component
|
|
4242
|
-
@example
|
|
4243
|
-
<Button>
|
|
4244
|
-
Text
|
|
4245
|
-
</Button>
|
|
4246
|
-
-->
|
|
4247
|
-
<script>
|
|
4248
|
-
let { children } = $props();
|
|
4249
|
-
</script>
|
|
4250
|
-
|
|
4251
|
-
<button>
|
|
4252
|
-
{@render children?.()}
|
|
4253
|
-
</button>
|
|
4254
|
-
```
|
|
4255
|
-
|
|
4256
|
-
<details>
|
|
4257
|
-
<summary>Svelte 3/4 (legacy) syntax</summary>
|
|
4258
|
-
|
|
4259
|
-
```svelte
|
|
4260
|
-
<!-- @component
|
|
4261
|
-
@example
|
|
4262
|
-
<Button>
|
|
4263
|
-
Text
|
|
4264
|
-
</Button>
|
|
4265
|
-
-->
|
|
4266
|
-
<button>
|
|
4267
|
-
<slot />
|
|
4268
|
-
</button>
|
|
4269
|
-
```
|
|
4270
|
-
|
|
4271
|
-
</details>
|
|
4272
|
-
|
|
4273
|
-
Output is identical for both syntax modes.
|
|
4274
|
-
|
|
4275
|
-
Output:
|
|
4276
|
-
|
|
4277
|
-
```ts
|
|
4278
|
-
/**
|
|
4279
|
-
* @example
|
|
4280
|
-
* <Button>
|
|
4281
|
-
* Text
|
|
4282
|
-
* </Button>
|
|
4283
|
-
*/
|
|
4284
|
-
export default class Button extends SvelteComponentTyped<
|
|
4285
|
-
ButtonProps,
|
|
4286
|
-
Record<string, any>,
|
|
4287
|
-
{ default: Record<string, never> }
|
|
4288
|
-
> {}
|
|
4289
|
-
```
|
|
4290
|
-
|
|
4291
|
-
### Accessor Props
|
|
4292
|
-
|
|
4293
|
-
Exported functions and consts become accessor props in generated TypeScript definitions. Use `@type` for function signatures, or `@param` and `@returns` (or `@return`) for richer docs.
|
|
4294
|
-
|
|
4295
|
-
`@type` wins over `@param`/`@returns` when both are present.
|
|
4296
|
-
|
|
4297
|
-
**Signature:**
|
|
4298
|
-
|
|
4299
|
-
```js
|
|
4300
|
-
/**
|
|
4301
|
-
* Function description
|
|
4302
|
-
* @param {Type} paramName - Parameter description
|
|
4303
|
-
* @param {Type} [optionalParam] - Optional parameter
|
|
4304
|
-
* @returns {ReturnType} Return value description
|
|
4305
|
-
*/
|
|
4306
|
-
```
|
|
4307
|
-
|
|
4308
|
-
**Example:**
|
|
4309
|
-
|
|
4310
|
-
```svelte
|
|
4311
|
-
<script>
|
|
4312
|
-
/**
|
|
4313
|
-
* @typedef {object} NotificationData
|
|
4314
|
-
* @property {string} [id] - Optional id for deduplication
|
|
4315
|
-
* @property {"error" | "info" | "success"} [kind]
|
|
4316
|
-
*/
|
|
4317
|
-
|
|
4318
|
-
let { children } = $props();
|
|
4319
|
-
|
|
4320
|
-
/**
|
|
4321
|
-
* Add a notification to the queue.
|
|
4322
|
-
* @param {NotificationData} notification
|
|
4323
|
-
* @returns {string} The notification id
|
|
4324
|
-
*/
|
|
4325
|
-
export function add(notification) {
|
|
4326
|
-
const id = notification.id ?? "id";
|
|
4327
|
-
return id;
|
|
4328
|
-
}
|
|
4329
|
-
|
|
4330
|
-
/**
|
|
4331
|
-
* Remove a notification by id.
|
|
4332
|
-
* @param {string} id
|
|
4333
|
-
* @returns {boolean} True if the notification was found and removed
|
|
4334
|
-
*/
|
|
4335
|
-
export function remove(id) {
|
|
4336
|
-
return true;
|
|
4337
|
-
}
|
|
4338
|
-
|
|
4339
|
-
/**
|
|
4340
|
-
* Get notification count.
|
|
4341
|
-
* @returns {number} The number of notifications
|
|
4342
|
-
*/
|
|
4343
|
-
export function getCount() {
|
|
4344
|
-
return 0;
|
|
4345
|
-
}
|
|
4346
|
-
</script>
|
|
4347
|
-
|
|
4348
|
-
<div>
|
|
4349
|
-
{@render children?.()}
|
|
4350
|
-
</div>
|
|
4351
|
-
```
|
|
4352
|
-
|
|
4353
|
-
<details>
|
|
4354
|
-
<summary>Svelte 3/4 (legacy) syntax</summary>
|
|
4355
|
-
|
|
4356
|
-
```svelte
|
|
4357
|
-
<script>
|
|
4358
|
-
/**
|
|
4359
|
-
* @typedef {object} NotificationData
|
|
4360
|
-
* @property {string} [id] - Optional id for deduplication
|
|
4361
|
-
* @property {"error" | "info" | "success"} [kind]
|
|
4362
|
-
*/
|
|
4363
|
-
|
|
4364
|
-
/**
|
|
4365
|
-
* Add a notification to the queue.
|
|
4366
|
-
* @param {NotificationData} notification
|
|
4367
|
-
* @returns {string} The notification id
|
|
4368
|
-
*/
|
|
4369
|
-
export function add(notification) {
|
|
4370
|
-
const id = notification.id ?? "id";
|
|
4371
|
-
return id;
|
|
4372
|
-
}
|
|
4373
|
-
|
|
4374
|
-
/**
|
|
4375
|
-
* Remove a notification by id.
|
|
4376
|
-
* @param {string} id
|
|
4377
|
-
* @returns {boolean} True if the notification was found and removed
|
|
4378
|
-
*/
|
|
4379
|
-
export function remove(id) {
|
|
4380
|
-
return true;
|
|
4381
|
-
}
|
|
4382
|
-
|
|
4383
|
-
/**
|
|
4384
|
-
* Get notification count.
|
|
4385
|
-
* @returns {number} The number of notifications
|
|
4386
|
-
*/
|
|
4387
|
-
export function getCount() {
|
|
4388
|
-
return 0;
|
|
4389
|
-
}
|
|
4390
|
-
</script>
|
|
4391
|
-
```
|
|
4392
|
-
|
|
4393
|
-
</details>
|
|
4394
|
-
|
|
4395
|
-
Output is identical for both syntax modes.
|
|
4396
|
-
|
|
4397
|
-
Output:
|
|
4398
|
-
|
|
4399
|
-
```ts
|
|
4400
|
-
export type NotificationData = {
|
|
4401
|
-
/** Optional id for deduplication */
|
|
4402
|
-
id?: string;
|
|
4403
|
-
kind?: "error" | "info" | "success";
|
|
4404
|
-
};
|
|
4405
|
-
|
|
4406
|
-
export type ComponentProps = Record<string, never>;
|
|
4407
|
-
|
|
4408
|
-
export default class Component extends SvelteComponentTyped<
|
|
4409
|
-
ComponentProps,
|
|
4410
|
-
Record<string, any>,
|
|
4411
|
-
Record<string, never>
|
|
4412
|
-
> {
|
|
4413
|
-
/**
|
|
4414
|
-
* Add a notification to the queue.
|
|
4415
|
-
*/
|
|
4416
|
-
add: (notification: NotificationData) => string;
|
|
4417
|
-
|
|
4418
|
-
/**
|
|
4419
|
-
* Remove a notification by id.
|
|
4420
|
-
*/
|
|
4421
|
-
remove: (id: string) => boolean;
|
|
4422
|
-
|
|
4423
|
-
/**
|
|
4424
|
-
* Get notification count.
|
|
4425
|
-
*/
|
|
4426
|
-
getCount: () => number;
|
|
4427
|
-
}
|
|
4428
|
-
```
|
|
4429
|
-
|
|
4430
|
-
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`.
|
|
4431
|
-
|
|
4432
|
-
The Markdown writer marks the same exports (a `const`, or a real function declaration) with Kind `accessor` in the Props table, instead of their raw `const`/`function` kind, matching the `.d.ts` output above.
|
|
4433
|
-
|
|
4434
|
-
## Troubleshooting
|
|
4435
|
-
|
|
4436
|
-
**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.
|
|
4437
|
-
|
|
4438
|
-
**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).
|
|
4439
|
-
|
|
4440
|
-
**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.
|
|
4441
|
-
|
|
4442
|
-
**`sveld: cannot resolve "..." from ...`.** An `export *` or a named re-export points at a module or path alias that doesn't resolve to a file on disk. Fix the specifier, or check the tsconfig/jsconfig `paths` entry it's supposed to match. This exits `1`; see [Exit codes](#exit-codes).
|
|
4443
|
-
|
|
4444
|
-
**A path alias (`$lib`, `@components`, ...) isn't picked up.** sveld only reads aliases from `compilerOptions.paths` in the nearest `tsconfig.json` or `jsconfig.json`, found by walking up from the file doing the importing. Vite/SvelteKit alias config (`vite.config.*`, `svelte.config.*`) is not read directly — add the same aliases to `paths` so both tools agree. When a pattern has more than one mapping (`"$lib/*": ["./src/lib/*", "./lib/*"]`), sveld tries them in order and uses the first one that exists on disk, falling back to the first mapping if none do; among multiple matching patterns, the one with the longest non-wildcard prefix wins, regardless of declaration order (matching `tsc`).
|
|
279
|
+
- Node 22 or later, or Bun. sveld is ESM-only.
|
|
280
|
+
- A `sveld.config.ts` needs a runtime that strips TypeScript: Bun, or Node 22.18+ / 23.6+.
|
|
281
|
+
- The optional [`checkExamples`](docs/ci.md#checking-example-blocks) check for TS/JS examples needs `typescript` 7+ and a `tsconfig.json`. Nothing else loads TypeScript.
|
|
282
|
+
- Your installed Svelte version doesn't affect parsing. sveld parses with [sveast](https://github.com/metonym/sveast), kept in parity with `svelte/compiler`.
|
|
4445
283
|
|
|
4446
284
|
## Contributing
|
|
4447
285
|
|
|
4448
|
-
See [contributing guidelines](CONTRIBUTING.md).
|
|
286
|
+
See the [contributing guidelines](CONTRIBUTING.md).
|
|
4449
287
|
|
|
4450
288
|
## License
|
|
4451
289
|
|