sveld 0.32.3 → 0.32.5
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 +352 -73
- package/lib/ComponentParser.d.ts +84 -45
- package/lib/ast-guards.d.ts +12 -0
- package/lib/brands.d.ts +11 -0
- package/lib/cli.d.ts +1 -6
- package/lib/create-exports.d.ts +3 -16
- package/lib/element-tag-map.d.ts +4 -13
- package/lib/get-svelte-entry.d.ts +4 -6
- package/lib/index.d.ts +2 -1
- package/lib/index.js +583 -583
- package/lib/parse-exports.d.ts +3 -10
- package/lib/path.d.ts +3 -6
- package/lib/plugin.d.ts +3 -3
- package/lib/resolve-alias.d.ts +3 -24
- package/lib/sveld.d.ts +2 -9
- package/lib/validate.d.ts +13 -0
- package/lib/writer/MarkdownWriterBase.d.ts +0 -4
- package/lib/writer/WriterMarkdown.d.ts +1 -10
- package/lib/writer/markdown-format-utils.d.ts +7 -49
- package/lib/writer/markdown-render-utils.d.ts +1 -8
- package/lib/writer/writer-markdown.d.ts +1 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
[![NPM][npm]][npm-url]
|
|
4
4
|

|
|
5
5
|
|
|
6
|
-
`sveld` generates TypeScript definitions and component documentation (Markdown/JSON) for Svelte components. It analyzes props, events, slots, and
|
|
6
|
+
`sveld` generates TypeScript definitions and component documentation (Markdown/JSON) for Svelte components. It statically analyzes props, events, slots, and the rest. Add types with [JSDoc](https://jsdoc.app/) when inference is not enough.
|
|
7
7
|
|
|
8
|
-
The
|
|
8
|
+
The goal is to get third-party Svelte libraries working with the Svelte Language Server and TypeScript with minimal effort from the author. Generated `.d.ts` files give you autocomplete in VS Code and other IDEs.
|
|
9
9
|
|
|
10
10
|
[Carbon Components Svelte](https://github.com/carbon-design-system/carbon-components-svelte) uses this library to auto-generate component types and API metadata.
|
|
11
11
|
|
|
12
12
|
`sveld` uses the Svelte 5 compiler to parse `.svelte` files. That single parse path powers docgen and TypeScript output for Svelte 3, Svelte 4, Svelte 5 without runes (`export let`, `<slot>`, `$$restProps`, …), and Svelte 5 Runes (`$props()`, `$bindable()`, `{@render ...}`, callback props such as `onclick`, …).
|
|
13
13
|
|
|
14
|
-
For `lang="ts"` components, `sveld`
|
|
14
|
+
For `lang="ts"` components, `sveld` keeps source-level prop type annotations when it can, instead of forcing JSDoc. That covers legacy `export let` props, typed `$props()` destructuring, typed whole-object `$props()` captures, local `interface`/`type` declarations, and imported type references in emitted `.d.ts` files.
|
|
15
15
|
|
|
16
16
|
| Syntax mode | Supported |
|
|
17
17
|
| :------------------- | :-------: |
|
|
@@ -20,11 +20,11 @@ For `lang="ts"` components, `sveld` preserves source-level prop type annotations
|
|
|
20
20
|
| Svelte 5 (non-Runes) | ✓ |
|
|
21
21
|
| Svelte 5 Runes | ✓ |
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Generated `.d.ts` files extend `SvelteComponentTyped` from `svelte`, so TypeScript and the Svelte Language Server work whether consumers use Svelte 3, Svelte 4, or Svelte 5.
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
From a Svelte component, `sveld` can infer basic prop types and emit definitions the [Svelte Language Server](https://github.com/sveltejs/language-tools) understands:
|
|
28
28
|
|
|
29
29
|
**Button.svelte**
|
|
30
30
|
|
|
@@ -72,9 +72,7 @@ export default class Button extends SvelteComponentTyped<
|
|
|
72
72
|
> {}
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
Prop/event/slot types and signatures can be augmented using [JSDoc](https://jsdoc.app/) notations.
|
|
75
|
+
Inference only gets you so far. Use [JSDoc](https://jsdoc.app/) to document prop, event, and slot types when you need more precision.
|
|
78
76
|
|
|
79
77
|
```js
|
|
80
78
|
/** @type {"button" | "submit" | "reset"} */
|
|
@@ -86,7 +84,7 @@ export let type = "button";
|
|
|
86
84
|
export let primary = false;
|
|
87
85
|
```
|
|
88
86
|
|
|
89
|
-
|
|
87
|
+
With JSDoc, the output looks like this:
|
|
90
88
|
|
|
91
89
|
```ts
|
|
92
90
|
import type { SvelteHTMLElements } from "svelte/elements";
|
|
@@ -149,9 +147,9 @@ export default class Button extends SvelteComponentTyped<
|
|
|
149
147
|
|
|
150
148
|
## Approach
|
|
151
149
|
|
|
152
|
-
`sveld` uses the Svelte compiler to statically analyze
|
|
150
|
+
`sveld` uses the Svelte compiler to statically analyze exported components and emit docs for consumers.
|
|
153
151
|
|
|
154
|
-
|
|
152
|
+
It extracts:
|
|
155
153
|
|
|
156
154
|
- props
|
|
157
155
|
- slots
|
|
@@ -160,7 +158,7 @@ Extracted metadata include:
|
|
|
160
158
|
- context (setContext/getContext)
|
|
161
159
|
- `$$restProps`
|
|
162
160
|
|
|
163
|
-
|
|
161
|
+
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.
|
|
164
162
|
|
|
165
163
|
When both TypeScript syntax and JSDoc are present, `sveld` resolves prop types in this order:
|
|
166
164
|
|
|
@@ -169,7 +167,7 @@ When both TypeScript syntax and JSDoc are present, `sveld` resolves prop types i
|
|
|
169
167
|
3. initializer inference
|
|
170
168
|
4. `any`
|
|
171
169
|
|
|
172
|
-
`sveld`
|
|
170
|
+
`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.
|
|
173
171
|
|
|
174
172
|
## Usage
|
|
175
173
|
|
|
@@ -206,7 +204,7 @@ export default defineConfig({
|
|
|
206
204
|
});
|
|
207
205
|
```
|
|
208
206
|
|
|
209
|
-
Since Vite uses Rollup for production builds,
|
|
207
|
+
Since Vite uses Rollup for production builds, the same plugin works in Rollup configs.
|
|
210
208
|
|
|
211
209
|
By default, `sveld` will use the `"svelte"` field from your `package.json` to determine the entry point. You can override this by specifying an explicit `entry` option:
|
|
212
210
|
|
|
@@ -248,7 +246,7 @@ npx sveld --json --markdown
|
|
|
248
246
|
|
|
249
247
|
### Node.js
|
|
250
248
|
|
|
251
|
-
You can also
|
|
249
|
+
You can also call `sveld` from Node.js. The package is **ESM-only**; `require("sveld")` does not work. Use `import` or dynamic `import()`.
|
|
252
250
|
|
|
253
251
|
If no `input` is specified, `sveld` will infer the entry point based on the `package.json#svelte` field.
|
|
254
252
|
|
|
@@ -278,7 +276,7 @@ sveld({
|
|
|
278
276
|
|
|
279
277
|
#### `jsonOptions.outDir`
|
|
280
278
|
|
|
281
|
-
|
|
279
|
+
With `json: true`, `sveld` writes `COMPONENT_API.json` at the project root. The file documents all components.
|
|
282
280
|
|
|
283
281
|
Use the `jsonOptions.outDir` option to specify the folder for individual JSON files to be emitted.
|
|
284
282
|
|
|
@@ -295,7 +293,7 @@ sveld({
|
|
|
295
293
|
|
|
296
294
|
### Publishing to NPM
|
|
297
295
|
|
|
298
|
-
TypeScript definitions
|
|
296
|
+
TypeScript definitions land in the `types` folder by default. Include that folder in `package.json` when you publish to npm.
|
|
299
297
|
|
|
300
298
|
```diff
|
|
301
299
|
{
|
|
@@ -339,9 +337,7 @@ sveld({
|
|
|
339
337
|
When `json: true` is enabled, `sveld` emits a `COMPONENT_API.json` file with schema and generator metadata plus the parsed
|
|
340
338
|
component API.
|
|
341
339
|
|
|
342
|
-
The
|
|
343
|
-
generated `COMPONENT_API.json` files. The schema describes the emitted metadata contract; optional fields may be absent when
|
|
344
|
-
the parser does not have a stable source for that metadata.
|
|
340
|
+
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.
|
|
345
341
|
|
|
346
342
|
```ts
|
|
347
343
|
interface ComponentApiJson {
|
|
@@ -437,12 +433,11 @@ type ComponentEvent =
|
|
|
437
433
|
};
|
|
438
434
|
```
|
|
439
435
|
|
|
440
|
-
`source` fields
|
|
441
|
-
include source text or raw character offsets.
|
|
436
|
+
`source` fields appear only when the Svelte or JavaScript AST has stable positions. They omit source text and raw character offsets.
|
|
442
437
|
|
|
443
|
-
|
|
438
|
+
`SourcePosition.line` is 1-based. `SourcePosition.column` is 0-based.
|
|
444
439
|
|
|
445
|
-
Prop metadata is additive and
|
|
440
|
+
Prop metadata is additive and keeps the older public fields:
|
|
446
441
|
|
|
447
442
|
- `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.
|
|
448
443
|
- `typeSource` identifies the conservative source of the emitted `type`: TypeScript annotation, JSDoc, initializer/default inference, other parser inference, or unknown fallback.
|
|
@@ -453,7 +448,7 @@ Prop metadata is additive and preserves the older public fields:
|
|
|
453
448
|
|
|
454
449
|
### `reactive`
|
|
455
450
|
|
|
456
|
-
The `reactive` field in generated JSON is heuristic
|
|
451
|
+
The `reactive` field in generated JSON is a heuristic. It does not fully answer whether a parent can use `bind:prop` in Svelte.
|
|
457
452
|
|
|
458
453
|
`sveld` marks `reactive: true` when it finds internal evidence that a prop is writable, including:
|
|
459
454
|
|
|
@@ -468,7 +463,7 @@ Local variables or parameters that shadow a prop name do not count as writes to
|
|
|
468
463
|
|
|
469
464
|
### `binding`
|
|
470
465
|
|
|
471
|
-
The optional `binding` field
|
|
466
|
+
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()`.
|
|
472
467
|
|
|
473
468
|
Use `@bindable readonly` for component-owned or output-style bindings where the consumer binds to the current value emitted by the component:
|
|
474
469
|
|
|
@@ -496,7 +491,7 @@ Use `@bindable writable` for two-way or shared state bindings where either the c
|
|
|
496
491
|
|
|
497
492
|
Generated JSON includes `"binding": "readonly"` or `"binding": "writable"` for annotated props. Unannotated props omit the field.
|
|
498
493
|
|
|
499
|
-
This is documentation
|
|
494
|
+
This is documentation only. Generated `.svelte.d.ts` prop types do not change. TypeScript cannot express Svelte binding direction reliably.
|
|
500
495
|
|
|
501
496
|
For stable output, generated `events` arrays are emitted in deterministic sorted order.
|
|
502
497
|
|
|
@@ -516,9 +511,9 @@ export let id = `ccs-${Math.random().toString(36)}`;
|
|
|
516
511
|
// inferred type: "string"
|
|
517
512
|
```
|
|
518
513
|
|
|
519
|
-
Use the `@type` tag to
|
|
514
|
+
Use the `@type` tag to document the type explicitly. In the example below, `kind` is a string union.
|
|
520
515
|
|
|
521
|
-
For `lang="ts"` components, prefer native TypeScript annotations when
|
|
516
|
+
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.
|
|
522
517
|
|
|
523
518
|
**Signature:**
|
|
524
519
|
|
|
@@ -550,7 +545,7 @@ For `lang="ts"` components, prefer native TypeScript annotations when they are a
|
|
|
550
545
|
</script>
|
|
551
546
|
```
|
|
552
547
|
|
|
553
|
-
For runes components with multiple destructured props,
|
|
548
|
+
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.
|
|
554
549
|
|
|
555
550
|
**Svelte 3, 4, 5 (non-Runes):**
|
|
556
551
|
|
|
@@ -570,6 +565,120 @@ For runes components with multiple destructured props, place JSDoc on the indivi
|
|
|
570
565
|
</script>
|
|
571
566
|
```
|
|
572
567
|
|
|
568
|
+
#### Importing types
|
|
569
|
+
|
|
570
|
+
`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:
|
|
571
|
+
|
|
572
|
+
- `import("module").Type` references an exported **type**.
|
|
573
|
+
- `typeof import("module").value` references the type of an exported **value**.
|
|
574
|
+
- `import("svelte").ComponentProps<...>` and other utility types compose with imports.
|
|
575
|
+
|
|
576
|
+
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.
|
|
577
|
+
|
|
578
|
+
**Example:**
|
|
579
|
+
|
|
580
|
+
```svelte
|
|
581
|
+
<script>
|
|
582
|
+
/**
|
|
583
|
+
* A store from `svelte/store`. No top-level `import` required.
|
|
584
|
+
* @type {import("svelte/store").Writable<string>}
|
|
585
|
+
*/
|
|
586
|
+
export let value;
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* `typeof import(...)` references the type of a value export, here the
|
|
590
|
+
* `writable` factory itself rather than a type it exports.
|
|
591
|
+
* @type {typeof import("svelte/store").writable}
|
|
592
|
+
*/
|
|
593
|
+
export let createStore;
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Reuse another component's props with Svelte's `ComponentProps` utility.
|
|
597
|
+
* @type {import("svelte").ComponentProps<import("svelte").SvelteComponent>}
|
|
598
|
+
*/
|
|
599
|
+
export let buttonProps;
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* `import(...)` works inside a `@typedef` too, which helps when typing values shared via `setContext` / `getContext`.
|
|
603
|
+
*
|
|
604
|
+
* @typedef {{ rows: import("svelte/store").Writable<string[]>; selected: import("svelte/store").Readable<number> }} TableContext
|
|
605
|
+
*/
|
|
606
|
+
|
|
607
|
+
/** @type {TableContext} */
|
|
608
|
+
export let context;
|
|
609
|
+
</script>
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
Output:
|
|
613
|
+
|
|
614
|
+
```ts
|
|
615
|
+
export interface TableContext {
|
|
616
|
+
rows: import("svelte/store").Writable<string[]>;
|
|
617
|
+
selected: import("svelte/store").Readable<number>;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
export type ComponentProps = {
|
|
621
|
+
/** @default undefined */
|
|
622
|
+
value: import("svelte/store").Writable<string>;
|
|
623
|
+
/** @default undefined */
|
|
624
|
+
createStore: typeof import("svelte/store").writable;
|
|
625
|
+
/** @default undefined */
|
|
626
|
+
buttonProps: import("svelte").ComponentProps<import("svelte").SvelteComponent>;
|
|
627
|
+
/** @default undefined */
|
|
628
|
+
context: TableContext;
|
|
629
|
+
};
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
#### Prefer `unknown` over `any`
|
|
633
|
+
|
|
634
|
+
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.
|
|
635
|
+
|
|
636
|
+
**Example:**
|
|
637
|
+
|
|
638
|
+
```svelte
|
|
639
|
+
<script>
|
|
640
|
+
/**
|
|
641
|
+
* A value of unknown shape. Prefer `unknown` over `any`: consumers must
|
|
642
|
+
* narrow it before use instead of silently opting out of type checking.
|
|
643
|
+
*
|
|
644
|
+
* @type {unknown}
|
|
645
|
+
*/
|
|
646
|
+
export let payload;
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* An escape hatch typed as `any`, shown for contrast. `any` disables type
|
|
650
|
+
* checking everywhere it flows, so reach for `unknown` at boundaries instead.
|
|
651
|
+
*
|
|
652
|
+
* @type {any}
|
|
653
|
+
*/
|
|
654
|
+
export let raw;
|
|
655
|
+
</script>
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
Output:
|
|
659
|
+
|
|
660
|
+
```ts
|
|
661
|
+
export type ComponentProps = {
|
|
662
|
+
/** @default undefined */
|
|
663
|
+
payload: unknown;
|
|
664
|
+
/** @default undefined */
|
|
665
|
+
raw: any;
|
|
666
|
+
};
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
Consumers must narrow an `unknown` prop before using it, while an `any` prop silently accepts anything:
|
|
670
|
+
|
|
671
|
+
```ts
|
|
672
|
+
function handle(props: ComponentProps) {
|
|
673
|
+
// Error: 'payload' is of type 'unknown'. Narrow it first.
|
|
674
|
+
props.payload.toUpperCase();
|
|
675
|
+
|
|
676
|
+
if (typeof props.payload === "string") {
|
|
677
|
+
props.payload.toUpperCase(); // OK after narrowing
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
```
|
|
681
|
+
|
|
573
682
|
### `@default`
|
|
574
683
|
|
|
575
684
|
By default, `sveld` infers the `@default` value from the prop's initializer and includes it in the generated TypeScript definitions:
|
|
@@ -587,9 +696,9 @@ By default, `sveld` infers the `@default` value from the prop's initializer and
|
|
|
587
696
|
open?: boolean;
|
|
588
697
|
```
|
|
589
698
|
|
|
590
|
-
Use
|
|
699
|
+
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.
|
|
591
700
|
|
|
592
|
-
|
|
701
|
+
Use `@default` when the initializer references a variable or expression that means nothing to consumers:
|
|
593
702
|
|
|
594
703
|
```svelte
|
|
595
704
|
<script>
|
|
@@ -612,7 +721,7 @@ shouldFilter?: (item: string, value: string) => boolean;
|
|
|
612
721
|
|
|
613
722
|
#### Identifier resolution
|
|
614
723
|
|
|
615
|
-
When a prop's initializer is a variable reference, `sveld` resolves it to the actual value
|
|
724
|
+
When a prop's initializer is a variable reference, `sveld` resolves it to the actual value:
|
|
616
725
|
|
|
617
726
|
```svelte
|
|
618
727
|
<script>
|
|
@@ -654,7 +763,7 @@ When an explicit `@default` annotation is provided, it always takes precedence o
|
|
|
654
763
|
|
|
655
764
|
### `@typedef`
|
|
656
765
|
|
|
657
|
-
The `@typedef` tag
|
|
766
|
+
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`.
|
|
658
767
|
|
|
659
768
|
**Signature:**
|
|
660
769
|
|
|
@@ -703,7 +812,7 @@ The `@typedef` tag can be used to define a common type that is used multiple tim
|
|
|
703
812
|
|
|
704
813
|
#### Using `@property` for complex typedefs
|
|
705
814
|
|
|
706
|
-
For complex object types, use
|
|
815
|
+
For complex object types, use `@property` to document individual fields. That gives per-property tooltips in the IDE.
|
|
707
816
|
|
|
708
817
|
**Signature:**
|
|
709
818
|
|
|
@@ -774,7 +883,7 @@ export type ComponentProps = {
|
|
|
774
883
|
|
|
775
884
|
#### Optional properties and default values
|
|
776
885
|
|
|
777
|
-
|
|
886
|
+
Use square brackets for optional properties, per JSDoc. Default values use `[propertyName=defaultValue]`.
|
|
778
887
|
|
|
779
888
|
**Signature:**
|
|
780
889
|
|
|
@@ -847,7 +956,7 @@ export type ComponentProps = {
|
|
|
847
956
|
};
|
|
848
957
|
```
|
|
849
958
|
|
|
850
|
-
>
|
|
959
|
+
> The inline syntax `@typedef {{ name: string }} User` still works for backwards compatibility.
|
|
851
960
|
|
|
852
961
|
#### Discriminated unions
|
|
853
962
|
|
|
@@ -913,7 +1022,7 @@ The same pattern works inline via `@type`, which is useful when the union is onl
|
|
|
913
1022
|
export let result = { kind: "success", value: "ok" };
|
|
914
1023
|
```
|
|
915
1024
|
|
|
916
|
-
In `<script lang="ts">` components, write the type alias directly
|
|
1025
|
+
In `<script lang="ts">` components, write the type alias directly. `sveld` preserves it in the emitted `.d.ts`:
|
|
917
1026
|
|
|
918
1027
|
```svelte
|
|
919
1028
|
<script lang="ts">
|
|
@@ -923,11 +1032,182 @@ In `<script lang="ts">` components, write the type alias directly — `sveld` pr
|
|
|
923
1032
|
</script>
|
|
924
1033
|
```
|
|
925
1034
|
|
|
1035
|
+
#### Branded types
|
|
1036
|
+
|
|
1037
|
+
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.
|
|
1038
|
+
|
|
1039
|
+
**Example:**
|
|
1040
|
+
|
|
1041
|
+
```svelte
|
|
1042
|
+
<script>
|
|
1043
|
+
/**
|
|
1044
|
+
* A branded string. At runtime it is a plain `string`, but the brand makes it
|
|
1045
|
+
* a distinct domain type that other strings cannot be assigned to.
|
|
1046
|
+
*
|
|
1047
|
+
* @type {string & { readonly __brand: "UserId" }}
|
|
1048
|
+
*/
|
|
1049
|
+
export let userId;
|
|
1050
|
+
|
|
1051
|
+
/**
|
|
1052
|
+
* A branded number representing a monetary amount in cents.
|
|
1053
|
+
*
|
|
1054
|
+
* @type {number & { readonly __brand: "Cents" }}
|
|
1055
|
+
*/
|
|
1056
|
+
export let amount;
|
|
1057
|
+
</script>
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1060
|
+
Output:
|
|
1061
|
+
|
|
1062
|
+
```ts
|
|
1063
|
+
export type ComponentProps = {
|
|
1064
|
+
/**
|
|
1065
|
+
* A branded string. At runtime it is a plain `string`, but the brand makes it
|
|
1066
|
+
* a distinct domain type that other strings cannot be assigned to.
|
|
1067
|
+
* @default undefined
|
|
1068
|
+
*/
|
|
1069
|
+
userId: string & { readonly __brand: "UserId" };
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* A branded number representing a monetary amount in cents.
|
|
1073
|
+
* @default undefined
|
|
1074
|
+
*/
|
|
1075
|
+
amount: number & { readonly __brand: "Cents" };
|
|
1076
|
+
};
|
|
1077
|
+
```
|
|
1078
|
+
|
|
1079
|
+
Consumers construct branded values with a narrowing cast, then get compile-time protection against mixing them up:
|
|
1080
|
+
|
|
1081
|
+
```ts
|
|
1082
|
+
const userId = "user_123" as ComponentProps["userId"];
|
|
1083
|
+
|
|
1084
|
+
// Error: a plain string is not assignable to the branded userId
|
|
1085
|
+
component.$set({ userId: "user_123" });
|
|
1086
|
+
```
|
|
1087
|
+
|
|
1088
|
+
#### Utility types
|
|
1089
|
+
|
|
1090
|
+
`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.
|
|
1091
|
+
|
|
1092
|
+
**Example:**
|
|
1093
|
+
|
|
1094
|
+
```svelte
|
|
1095
|
+
<script>
|
|
1096
|
+
/**
|
|
1097
|
+
* @typedef {{ id: string; size: "sm" | "md" | "lg"; disabled: boolean }} Options
|
|
1098
|
+
*/
|
|
1099
|
+
|
|
1100
|
+
/**
|
|
1101
|
+
* @typedef {() => Options} Factory
|
|
1102
|
+
*/
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* A subset of `Options`.
|
|
1106
|
+
* @type {Pick<Options, "id" | "size">}
|
|
1107
|
+
*/
|
|
1108
|
+
export let summary;
|
|
1109
|
+
|
|
1110
|
+
/**
|
|
1111
|
+
* Everything in `Options` except `disabled`.
|
|
1112
|
+
* @type {Omit<Options, "disabled">}
|
|
1113
|
+
*/
|
|
1114
|
+
export let editable;
|
|
1115
|
+
|
|
1116
|
+
/**
|
|
1117
|
+
* Derived from the factory's return type rather than restated.
|
|
1118
|
+
* @type {ReturnType<Factory>}
|
|
1119
|
+
*/
|
|
1120
|
+
export let defaults;
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
* The resolved value of an async source.
|
|
1124
|
+
* @type {Awaited<Promise<Options>>}
|
|
1125
|
+
*/
|
|
1126
|
+
export let resolved;
|
|
1127
|
+
</script>
|
|
1128
|
+
```
|
|
1129
|
+
|
|
1130
|
+
Output:
|
|
1131
|
+
|
|
1132
|
+
```ts
|
|
1133
|
+
export interface Options {
|
|
1134
|
+
id: string;
|
|
1135
|
+
size: "sm" | "md" | "lg";
|
|
1136
|
+
disabled: boolean;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
export type Factory = () => Options;
|
|
1140
|
+
|
|
1141
|
+
export type ComponentProps = {
|
|
1142
|
+
summary: Pick<Options, "id" | "size">;
|
|
1143
|
+
editable: Omit<Options, "disabled">;
|
|
1144
|
+
defaults: ReturnType<Factory>;
|
|
1145
|
+
resolved: Awaited<Promise<Options>>;
|
|
1146
|
+
};
|
|
1147
|
+
```
|
|
1148
|
+
|
|
1149
|
+
#### Type guards
|
|
1150
|
+
|
|
1151
|
+
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.
|
|
1152
|
+
|
|
1153
|
+
**Example:**
|
|
1154
|
+
|
|
1155
|
+
```svelte
|
|
1156
|
+
<script>
|
|
1157
|
+
/**
|
|
1158
|
+
* @typedef {{ id: string; name: string }} User
|
|
1159
|
+
*/
|
|
1160
|
+
|
|
1161
|
+
/**
|
|
1162
|
+
* A type guard. It accepts an `unknown` value and returns a type predicate,
|
|
1163
|
+
* so callers can narrow `unknown` to `User` before accessing its fields.
|
|
1164
|
+
*
|
|
1165
|
+
* @type {(value: unknown) => value is User}
|
|
1166
|
+
*/
|
|
1167
|
+
export let isUser;
|
|
1168
|
+
|
|
1169
|
+
/**
|
|
1170
|
+
* A type guard expressed as a reusable `@typedef`.
|
|
1171
|
+
*
|
|
1172
|
+
* @typedef {(value: unknown) => value is User} UserGuard
|
|
1173
|
+
*/
|
|
1174
|
+
|
|
1175
|
+
/** @type {UserGuard} */
|
|
1176
|
+
export let validate;
|
|
1177
|
+
</script>
|
|
1178
|
+
```
|
|
1179
|
+
|
|
1180
|
+
Output:
|
|
1181
|
+
|
|
1182
|
+
```ts
|
|
1183
|
+
export interface User {
|
|
1184
|
+
id: string;
|
|
1185
|
+
name: string;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
export type UserGuard = (value: unknown) => value is User;
|
|
1189
|
+
|
|
1190
|
+
export type ComponentProps = {
|
|
1191
|
+
isUser: (value: unknown) => value is User;
|
|
1192
|
+
validate: UserGuard;
|
|
1193
|
+
};
|
|
1194
|
+
```
|
|
1195
|
+
|
|
1196
|
+
Consumers use the guard to narrow an `unknown` value:
|
|
1197
|
+
|
|
1198
|
+
```ts
|
|
1199
|
+
function render(value: unknown, props: ComponentProps) {
|
|
1200
|
+
if (props.isUser(value)) {
|
|
1201
|
+
value.name; // narrowed to User
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
```
|
|
1205
|
+
|
|
926
1206
|
### `@callback`
|
|
927
1207
|
|
|
928
|
-
The `@callback` tag defines a function type
|
|
1208
|
+
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`.
|
|
929
1209
|
|
|
930
|
-
|
|
1210
|
+
Use it for callback props when you do not want inline function type syntax.
|
|
931
1211
|
|
|
932
1212
|
**Signature:**
|
|
933
1213
|
|
|
@@ -1009,9 +1289,9 @@ When `@returns` is omitted, the return type defaults to `void`. When no `@param`
|
|
|
1009
1289
|
|
|
1010
1290
|
### `@slot` / `@snippet`
|
|
1011
1291
|
|
|
1012
|
-
Use
|
|
1292
|
+
Use `@slot` to type component slots. In Svelte 5 runes components, `@snippet` is an alias. Both are non-standard JSDoc tags.
|
|
1013
1293
|
|
|
1014
|
-
Descriptions are optional for every slot, including the default slot
|
|
1294
|
+
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.
|
|
1015
1295
|
|
|
1016
1296
|
**Signature:**
|
|
1017
1297
|
|
|
@@ -1079,9 +1359,9 @@ Omit the `slot-name` to type the default slot.
|
|
|
1079
1359
|
|
|
1080
1360
|
#### Extra JSDoc tags before `@slot`
|
|
1081
1361
|
|
|
1082
|
-
Tags such as `@example`, `@deprecated`, `@see`, or `@since` that appear
|
|
1362
|
+
Tags such as `@example`, `@deprecated`, `@see`, or `@since` that appear after the prose description and before the `@slot` / `@snippet` line are copied into generated `.d.ts` files. The emitted JSDoc above each slot's snippet prop (and the traditional `SlotDefs` shape) lists the description, then those tags in source order. The same entries appear in JSON as `tags: [{ "name", "body" }, ...]`.
|
|
1083
1363
|
|
|
1084
|
-
Put `@slot` / `@snippet` last in the block (
|
|
1364
|
+
Put `@slot` / `@snippet` last in the block (description, optional extra tags, slot tag). Tags after `@slot` / `@snippet` in the same comment are not tied to that slot. Unknown tag names pass through as-is. Markdown docs do not render slot descriptions or these tags yet; use TypeScript hover or JSON.
|
|
1085
1365
|
|
|
1086
1366
|
**Example (default slot with `@example` and `@deprecated`):**
|
|
1087
1367
|
|
|
@@ -1105,11 +1385,11 @@ Put `@slot` / `@snippet` last in the block (`description` → optional extra tag
|
|
|
1105
1385
|
|
|
1106
1386
|
#### Svelte 5 Snippet Compatibility
|
|
1107
1387
|
|
|
1108
|
-
For Svelte 5
|
|
1388
|
+
For Svelte 5, `sveld` generates optional snippet props for all slots so consumers can use traditional slot syntax or `{#snippet}`.
|
|
1109
1389
|
|
|
1110
|
-
When parsing runes components, `sveld` maps `{@render ...}` calls back into the same slot metadata used for
|
|
1390
|
+
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`.
|
|
1111
1391
|
|
|
1112
|
-
Positional snippet calls
|
|
1392
|
+
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.
|
|
1113
1393
|
|
|
1114
1394
|
For slots with props (e.g., `let:prop`), the generated type uses a Snippet-compatible signature:
|
|
1115
1395
|
|
|
@@ -1125,8 +1405,8 @@ slotName?: (this: void) => void;
|
|
|
1125
1405
|
|
|
1126
1406
|
**Why this signature?**
|
|
1127
1407
|
|
|
1128
|
-
- **`this: void`**
|
|
1129
|
-
- **`...args: [Props]`**
|
|
1408
|
+
- **`this: void`** blocks calling the snippet with a `this` context, matching Svelte's rule that snippets are pure render functions
|
|
1409
|
+
- **`...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
|
|
1130
1410
|
|
|
1131
1411
|
**Default slot (`children` prop):**
|
|
1132
1412
|
|
|
@@ -1168,7 +1448,7 @@ type DropdownProps = {
|
|
|
1168
1448
|
</DataTable>
|
|
1169
1449
|
```
|
|
1170
1450
|
|
|
1171
|
-
|
|
1451
|
+
Generated output includes both the snippet prop and the traditional slot definition:
|
|
1172
1452
|
|
|
1173
1453
|
```ts
|
|
1174
1454
|
type DataTableProps<Row> = {
|
|
@@ -1213,7 +1493,7 @@ export default class DataTable<Row> extends SvelteComponentTyped<
|
|
|
1213
1493
|
|
|
1214
1494
|
Use the `@event` tag to type dispatched events. An event name is required and a description optional.
|
|
1215
1495
|
|
|
1216
|
-
In Svelte 5 runes components, callback props
|
|
1496
|
+
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.
|
|
1217
1497
|
|
|
1218
1498
|
Use `null` as the value if no event detail is provided.
|
|
1219
1499
|
|
|
@@ -1299,7 +1579,7 @@ export default class Component extends SvelteComponentTyped<
|
|
|
1299
1579
|
|
|
1300
1580
|
#### Using `@property` for complex event details
|
|
1301
1581
|
|
|
1302
|
-
For events with complex object payloads, use
|
|
1582
|
+
For events with complex object payloads, use `@property` to document individual fields. The main comment becomes the event description.
|
|
1303
1583
|
|
|
1304
1584
|
**Signature:**
|
|
1305
1585
|
|
|
@@ -1394,7 +1674,7 @@ export default class Component extends SvelteComponentTyped<
|
|
|
1394
1674
|
|
|
1395
1675
|
#### Optional properties in event details
|
|
1396
1676
|
|
|
1397
|
-
|
|
1677
|
+
Like typedefs, you can mark event detail properties as optional with square brackets when they are not always in the payload.
|
|
1398
1678
|
|
|
1399
1679
|
**Example:**
|
|
1400
1680
|
|
|
@@ -1486,7 +1766,7 @@ export default class Component extends SvelteComponentTyped<
|
|
|
1486
1766
|
|
|
1487
1767
|
#### Discriminated unions in event details
|
|
1488
1768
|
|
|
1489
|
-
When the event detail is a union (or any non-object shape), use `@type` to declare it directly. An explicit `@type`
|
|
1769
|
+
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).
|
|
1490
1770
|
|
|
1491
1771
|
**Example:**
|
|
1492
1772
|
|
|
@@ -1521,16 +1801,16 @@ Any free-text prose after the tags is attached to the event description, not to
|
|
|
1521
1801
|
|
|
1522
1802
|
### Context API
|
|
1523
1803
|
|
|
1524
|
-
`sveld`
|
|
1804
|
+
`sveld` generates TypeScript definitions for Svelte's `setContext`/`getContext` by extracting types from JSDoc on context values.
|
|
1525
1805
|
|
|
1526
1806
|
#### How it works
|
|
1527
1807
|
|
|
1528
|
-
When you
|
|
1808
|
+
When you call `setContext` in a component, `sveld`:
|
|
1529
1809
|
|
|
1530
|
-
1.
|
|
1531
|
-
2.
|
|
1532
|
-
3.
|
|
1533
|
-
4.
|
|
1810
|
+
1. Detects the `setContext` call
|
|
1811
|
+
2. Extracts the context key (must be a string literal)
|
|
1812
|
+
3. Finds JSDoc `@type` annotations on the variables being passed
|
|
1813
|
+
4. Generates a TypeScript type export for the context
|
|
1534
1814
|
|
|
1535
1815
|
#### Example
|
|
1536
1816
|
|
|
@@ -1624,7 +1904,6 @@ export default class Modal extends SvelteComponentTyped<
|
|
|
1624
1904
|
import { getContext } from 'svelte';
|
|
1625
1905
|
import type { SimpleModalContext } from 'modal-library/Modal.svelte';
|
|
1626
1906
|
|
|
1627
|
-
// Fully typed with autocomplete!
|
|
1628
1907
|
const { close, open } = getContext<SimpleModalContext>('simple-modal');
|
|
1629
1908
|
</script>
|
|
1630
1909
|
|
|
@@ -1633,7 +1912,7 @@ export default class Modal extends SvelteComponentTyped<
|
|
|
1633
1912
|
|
|
1634
1913
|
#### Explicitly typing contexts
|
|
1635
1914
|
|
|
1636
|
-
There are several ways to
|
|
1915
|
+
There are several ways to type contexts:
|
|
1637
1916
|
|
|
1638
1917
|
**Option 1: Inline JSDoc on variables (recommended)**
|
|
1639
1918
|
|
|
@@ -1704,7 +1983,7 @@ There are several ways to provide type information for contexts:
|
|
|
1704
1983
|
</script>
|
|
1705
1984
|
```
|
|
1706
1985
|
|
|
1707
|
-
>
|
|
1986
|
+
> Inline functions without `@type` annotations get generic inferred signatures. Add explicit JSDoc when you care about the shape.
|
|
1708
1987
|
|
|
1709
1988
|
#### Notes
|
|
1710
1989
|
|
|
@@ -1724,9 +2003,9 @@ There are several ways to provide type information for contexts:
|
|
|
1724
2003
|
|
|
1725
2004
|
### `@restProps`
|
|
1726
2005
|
|
|
1727
|
-
`sveld` can
|
|
2006
|
+
`sveld` can detect inline HTML elements that `$$restProps` is forwarded to. It cannot infer the underlying element for instantiated components.
|
|
1728
2007
|
|
|
1729
|
-
|
|
2008
|
+
Use `@restProps` to name the element tags `$$restProps` is forwarded to.
|
|
1730
2009
|
|
|
1731
2010
|
**Signature:**
|
|
1732
2011
|
|
|
@@ -1780,9 +2059,9 @@ You can use the `@restProps` tag to specify the element tags that `$$restProps`
|
|
|
1780
2059
|
|
|
1781
2060
|
### `@extendProps`
|
|
1782
2061
|
|
|
1783
|
-
|
|
2062
|
+
When a component wraps another, use `@extendProps` to extend generated props.
|
|
1784
2063
|
|
|
1785
|
-
>
|
|
2064
|
+
> `@extends` works as an alias, but prefer `@extendProps` to avoid clashing with standard JSDoc `@extends` for class inheritance.
|
|
1786
2065
|
|
|
1787
2066
|
**Signature:**
|
|
1788
2067
|
|
|
@@ -1811,7 +2090,7 @@ Svelte supports defining generics via the [`generics` attribute](https://svelte.
|
|
|
1811
2090
|
<script lang="ts" generics="Row extends DataTableRow = any"></script>
|
|
1812
2091
|
```
|
|
1813
2092
|
|
|
1814
|
-
Because `sveld`
|
|
2093
|
+
Because `sveld` targets JavaScript-only usage as a baseline, generics use the standard JSDoc `@template` tag. `@generics` is also supported as an alias.
|
|
1815
2094
|
|
|
1816
2095
|
**Signature:** Uses standard [JSDoc `@template` syntax](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#template):
|
|
1817
2096
|
|
|
@@ -1877,7 +2156,7 @@ Because `sveld` is designed to support JavaScript-only usage as a baseline, the
|
|
|
1877
2156
|
<slot {headers} {rows} />
|
|
1878
2157
|
```
|
|
1879
2158
|
|
|
1880
|
-
|
|
2159
|
+
Generated output looks like this:
|
|
1881
2160
|
|
|
1882
2161
|
```ts
|
|
1883
2162
|
export type ComponentProps<Row extends DataTableRow = DataTableRow> = {
|
|
@@ -1920,7 +2199,7 @@ export default class Component<
|
|
|
1920
2199
|
|
|
1921
2200
|
### `@generics`
|
|
1922
2201
|
|
|
1923
|
-
As an alternative to `@template`, sveld
|
|
2202
|
+
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:
|
|
1924
2203
|
|
|
1925
2204
|
```js
|
|
1926
2205
|
/**
|
|
@@ -2002,9 +2281,9 @@ export default class Button extends SvelteComponentTyped<
|
|
|
2002
2281
|
|
|
2003
2282
|
### Accessor Props
|
|
2004
2283
|
|
|
2005
|
-
Exported functions and consts become accessor props in generated TypeScript definitions. Use `@type`
|
|
2284
|
+
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.
|
|
2006
2285
|
|
|
2007
|
-
|
|
2286
|
+
`@type` wins over `@param`/`@returns` when both are present.
|
|
2008
2287
|
|
|
2009
2288
|
**Signature:**
|
|
2010
2289
|
|
|
@@ -2140,7 +2419,7 @@ When only `@param` tags are present without `@returns`, the return type defaults
|
|
|
2140
2419
|
|
|
2141
2420
|
## Contributing
|
|
2142
2421
|
|
|
2143
|
-
|
|
2422
|
+
See [contributing guidelines](CONTRIBUTING.md).
|
|
2144
2423
|
|
|
2145
2424
|
## License
|
|
2146
2425
|
|