sveld 0.29.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -21
- package/lib/ComponentParser.d.ts +40 -2
- package/lib/index.js +690 -164
- package/lib/writer/Writer.d.ts +4 -2
- package/lib/writer/writer-ts-definitions.d.ts +1 -0
- package/package.json +2 -5
package/README.md
CHANGED
|
@@ -11,6 +11,8 @@ The purpose of this project is to make third party Svelte component libraries co
|
|
|
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` preserves source-level prop type annotations when possible instead of requiring JSDoc as the primary source of truth. This includes 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
|
+
|
|
14
16
|
| Syntax mode | Supported |
|
|
15
17
|
| :------------------- | :-------: |
|
|
16
18
|
| Svelte 3 | ✓ |
|
|
@@ -129,7 +131,7 @@ export default class Button extends SvelteComponentTyped<
|
|
|
129
131
|
- [@type](#type)
|
|
130
132
|
- [@typedef](#typedef)
|
|
131
133
|
- [@callback](#callback)
|
|
132
|
-
- [@slot](#slot)
|
|
134
|
+
- [@slot / @snippet](#slot--snippet)
|
|
133
135
|
- [Svelte 5 Snippet Compatibility](#svelte-5-snippet-compatibility)
|
|
134
136
|
- [@event](#event)
|
|
135
137
|
- [Context API](#context-api)
|
|
@@ -156,6 +158,15 @@ Extracted metadata include:
|
|
|
156
158
|
|
|
157
159
|
This library adopts a progressively enhanced approach. Any property type that cannot be inferred (e.g., "hello" is a string) falls back to "any" to minimize incorrectly typed properties or signatures. To mitigate this, the library author can add JSDoc annotations to specify types that cannot be reliably inferred. This represents a progressively enhanced approach because JSDocs are comments that can be ignored by the compiler.
|
|
158
160
|
|
|
161
|
+
When both TypeScript syntax and JSDoc are present, `sveld` resolves prop types in this order:
|
|
162
|
+
|
|
163
|
+
1. explicit TypeScript annotation
|
|
164
|
+
2. explicit JSDoc annotation
|
|
165
|
+
3. initializer inference
|
|
166
|
+
4. `any`
|
|
167
|
+
|
|
168
|
+
`sveld` intentionally stays AST-only. It preserves imported and local type text in generated `.d.ts` output, but it does not perform project-wide semantic resolution with the TypeScript compiler. That means opaque imported whole-object `$props()` types can be preserved in declarations without being fully expanded into JSON metadata.
|
|
169
|
+
|
|
159
170
|
## Usage
|
|
160
171
|
|
|
161
172
|
### Installation
|
|
@@ -204,13 +215,15 @@ sveld({
|
|
|
204
215
|
When building the library, TypeScript definitions are emitted to the `types` folder by default.
|
|
205
216
|
|
|
206
217
|
Customize the output folder using the `typesOptions.outDir` option.
|
|
218
|
+
Use `typesOptions.printWidth` to control Prettier wrapping for generated `.d.ts` files. The default is `80`.
|
|
207
219
|
|
|
208
220
|
The following example emits the output to the `dist` folder:
|
|
209
221
|
|
|
210
222
|
```diff
|
|
211
223
|
sveld({
|
|
212
224
|
+ typesOptions: {
|
|
213
|
-
+ outDir: 'dist'
|
|
225
|
+
+ outDir: 'dist',
|
|
226
|
+
+ printWidth: 80
|
|
214
227
|
+ }
|
|
215
228
|
})
|
|
216
229
|
```
|
|
@@ -300,7 +313,7 @@ TypeScript definitions are outputted to the `types` folder by default. Don't for
|
|
|
300
313
|
- **`entry`** (string, optional): Specify the entry point to uncompiled Svelte source. If not provided, sveld will use the `"svelte"` field from `package.json`.
|
|
301
314
|
- **`glob`** (boolean, optional): Enable glob mode to analyze all `*.svelte` files.
|
|
302
315
|
- **`types`** (boolean, optional, default: `true`): Generate TypeScript definitions.
|
|
303
|
-
- **`typesOptions`** (object, optional): Options for TypeScript definition generation
|
|
316
|
+
- **`typesOptions`** (object, optional): Options for TypeScript definition generation, including `outDir`, `preamble`, and `printWidth`.
|
|
304
317
|
- **`json`** (boolean, optional): Generate component documentation in JSON format.
|
|
305
318
|
- **`jsonOptions`** (object, optional): Options for JSON output.
|
|
306
319
|
- **`markdown`** (boolean, optional): Generate component documentation in Markdown format.
|
|
@@ -354,6 +367,8 @@ export let id = `ccs-${Math.random().toString(36)}`;
|
|
|
354
367
|
|
|
355
368
|
Use the `@type` tag to explicitly document the type. In the following example, the `kind` property has an enumerated (enum) type.
|
|
356
369
|
|
|
370
|
+
For `lang="ts"` components, prefer native TypeScript annotations when they are already present. `@type` remains useful for JavaScript components, for overriding inferred types, and for cases where the AST cannot recover a more precise type.
|
|
371
|
+
|
|
357
372
|
**Signature:**
|
|
358
373
|
|
|
359
374
|
```js
|
|
@@ -369,18 +384,23 @@ Use the `@type` tag to explicitly document the type. In the following example, t
|
|
|
369
384
|
|
|
370
385
|
```svelte
|
|
371
386
|
<script>
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
387
|
+
let {
|
|
388
|
+
/**
|
|
389
|
+
* Specify the kind of button
|
|
390
|
+
* @type {"primary" | "secondary" | "tertiary"}
|
|
391
|
+
*/
|
|
392
|
+
kind = "primary",
|
|
393
|
+
/**
|
|
394
|
+
* Specify the Carbon icon to render
|
|
395
|
+
* @type {typeof import("carbon-icons-svelte").CarbonIcon}
|
|
396
|
+
*/
|
|
397
|
+
renderIcon = Close20,
|
|
398
|
+
} = $props();
|
|
381
399
|
</script>
|
|
382
400
|
```
|
|
383
401
|
|
|
402
|
+
For runes components with multiple destructured props, place JSDoc on the individual property you want to document. A declaration-level JSDoc block is only used as a fallback when the destructure exposes a single public prop.
|
|
403
|
+
|
|
384
404
|
**Svelte 3, 4, 5 (non-Runes):**
|
|
385
405
|
|
|
386
406
|
```svelte
|
|
@@ -680,9 +700,9 @@ Callbacks can be combined with `@typedef` in the same comment block:
|
|
|
680
700
|
|
|
681
701
|
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.
|
|
682
702
|
|
|
683
|
-
### `@slot`
|
|
703
|
+
### `@slot` / `@snippet`
|
|
684
704
|
|
|
685
|
-
Use the `@slot` tag for typing component slots.
|
|
705
|
+
Use the `@slot` tag for typing component slots. For Svelte 5 runes components, `@snippet` is also supported as an alias. Both are non-standard JSDoc tags.
|
|
686
706
|
|
|
687
707
|
Descriptions are optional for named slots. Currently, the default slot cannot have a description.
|
|
688
708
|
|
|
@@ -691,12 +711,14 @@ Descriptions are optional for named slots. Currently, the default slot cannot ha
|
|
|
691
711
|
```js
|
|
692
712
|
/**
|
|
693
713
|
* @slot {Type} slot-name [slot description]
|
|
714
|
+
* @snippet {Type} snippet-name [snippet description]
|
|
694
715
|
*/
|
|
695
716
|
|
|
696
717
|
Omit the `slot-name` to type the default slot.
|
|
697
718
|
|
|
698
719
|
/**
|
|
699
720
|
* @slot {Type}
|
|
721
|
+
* @snippet {Type}
|
|
700
722
|
*/
|
|
701
723
|
```
|
|
702
724
|
|
|
@@ -707,9 +729,9 @@ Omit the `slot-name` to type the default slot.
|
|
|
707
729
|
```svelte
|
|
708
730
|
<script>
|
|
709
731
|
/**
|
|
710
|
-
* @
|
|
711
|
-
* @
|
|
712
|
-
* @
|
|
732
|
+
* @snippet {{ prop: number; doubled: number; }}
|
|
733
|
+
* @snippet {{}} title
|
|
734
|
+
* @snippet {{ prop: number }} body - Customize the paragraph text.
|
|
713
735
|
*/
|
|
714
736
|
|
|
715
737
|
let { prop = 0, children, title, body } = $props();
|
|
@@ -752,7 +774,9 @@ Omit the `slot-name` to type the default slot.
|
|
|
752
774
|
|
|
753
775
|
For Svelte 5 compatibility, `sveld` automatically generates optional snippet props for all slots. This allows consumers to use either the traditional slot syntax or Svelte 5's `{#snippet}` syntax.
|
|
754
776
|
|
|
755
|
-
When parsing runes components, `sveld`
|
|
777
|
+
When parsing runes components, `sveld` maps `{@render ...}` calls back into the same slot metadata used for traditional `<slot>` declarations. Reserved snippet props such as `children`, along with named snippet props discovered from `{@render ...}`, are represented through `slots` metadata and generated snippet prop types rather than duplicated in the `props` output.
|
|
778
|
+
|
|
779
|
+
Positional snippet calls such as `{@render row?.(item, index)}` are preserved as typed props when the prop itself has an explicit type like `Snippet<[Item, number]>`. They are not converted into synthetic slot metadata.
|
|
756
780
|
|
|
757
781
|
For slots with props (e.g., `let:prop`), the generated type uses a Snippet-compatible signature:
|
|
758
782
|
|
|
@@ -856,7 +880,7 @@ export default class DataTable<Row> extends SvelteComponentTyped<
|
|
|
856
880
|
|
|
857
881
|
Use the `@event` tag to type dispatched events. An event name is required and a description optional.
|
|
858
882
|
|
|
859
|
-
In Svelte 5 runes components, callback props such as `onclick` are treated as component props, not events. The `events` output remains reserved for dispatched events and legacy forwarded events.
|
|
883
|
+
In Svelte 5 runes components, callback props such as `onclick` are treated as component props, not events. The `events` output remains reserved for real 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.
|
|
860
884
|
|
|
861
885
|
Use `null` as the value if no event detail is provided.
|
|
862
886
|
|
|
@@ -873,6 +897,20 @@ Use `null` as the value if no event detail is provided.
|
|
|
873
897
|
|
|
874
898
|
**Svelte 5 Runes:**
|
|
875
899
|
|
|
900
|
+
```svelte
|
|
901
|
+
<script>
|
|
902
|
+
/**
|
|
903
|
+
* Fired when a value is saved.
|
|
904
|
+
* @event {{ id: string }} save
|
|
905
|
+
*/
|
|
906
|
+
let { onsave } = $props();
|
|
907
|
+
</script>
|
|
908
|
+
|
|
909
|
+
<button onclick={() => onsave?.({ id: "1" })}>Save</button>
|
|
910
|
+
```
|
|
911
|
+
|
|
912
|
+
**Svelte 5 Runes with dispatched events:**
|
|
913
|
+
|
|
876
914
|
```svelte
|
|
877
915
|
<script>
|
|
878
916
|
/**
|
|
@@ -1439,8 +1477,13 @@ Because `sveld` is designed to support JavaScript-only usage as a baseline, the
|
|
|
1439
1477
|
* @generics {Row extends DataTableRow = DataTableRow} Row
|
|
1440
1478
|
*/
|
|
1441
1479
|
|
|
1442
|
-
|
|
1443
|
-
|
|
1480
|
+
let {
|
|
1481
|
+
/** @type {ReadonlyArray<DataTableHeader<Row>>} */
|
|
1482
|
+
headers = [],
|
|
1483
|
+
/** @type {ReadonlyArray<Row>} */
|
|
1484
|
+
rows = [],
|
|
1485
|
+
children,
|
|
1486
|
+
} = $props();
|
|
1444
1487
|
</script>
|
|
1445
1488
|
|
|
1446
1489
|
{@render children?.({ headers, rows })}
|
package/lib/ComponentParser.d.ts
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
export interface ParsedComponentTypeScriptMetadata {
|
|
2
|
+
canonicalPropsType?: string;
|
|
3
|
+
canonicalPropNames: string[];
|
|
4
|
+
localTypeDeclarations: string[];
|
|
5
|
+
typeImportStatements: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare const PARSED_COMPONENT_TYPE_SCRIPT_METADATA: unique symbol;
|
|
8
|
+
export declare function getParsedComponentTypeScriptMetadata(component: {
|
|
9
|
+
[PARSED_COMPONENT_TYPE_SCRIPT_METADATA]?: ParsedComponentTypeScriptMetadata;
|
|
10
|
+
}): ParsedComponentTypeScriptMetadata | undefined;
|
|
1
11
|
/**
|
|
2
12
|
* Diagnostic information for component parsing.
|
|
3
13
|
*
|
|
@@ -80,7 +90,7 @@ interface ComponentSlot {
|
|
|
80
90
|
fallback?: string;
|
|
81
91
|
/** TypeScript type definition for slot props (e.g., "{ title: string }") */
|
|
82
92
|
slot_props?: string;
|
|
83
|
-
/** Description extracted from JSDoc `@slot` tags */
|
|
93
|
+
/** Description extracted from JSDoc `@slot` or `@snippet` tags */
|
|
84
94
|
description?: string;
|
|
85
95
|
}
|
|
86
96
|
/**
|
|
@@ -258,6 +268,8 @@ export interface ParsedComponent {
|
|
|
258
268
|
componentComment?: string;
|
|
259
269
|
/** Contexts created with `setContext` in the component */
|
|
260
270
|
contexts?: ComponentContext[];
|
|
271
|
+
/** Internal writer-only TypeScript metadata. Not serialized to JSON. */
|
|
272
|
+
[PARSED_COMPONENT_TYPE_SCRIPT_METADATA]?: ParsedComponentTypeScriptMetadata;
|
|
261
273
|
}
|
|
262
274
|
export default class ComponentParser {
|
|
263
275
|
/** Parser configuration options (e.g., verbose logging) */
|
|
@@ -306,8 +318,20 @@ export default class ComponentParser {
|
|
|
306
318
|
private readonly propLocalToPublicName;
|
|
307
319
|
/** Tracks `$props()` bindings that are used as spread/rest props */
|
|
308
320
|
private readonly restPropLocals;
|
|
321
|
+
/** Tracks identifier bindings that capture the entire `$props()` object */
|
|
322
|
+
private readonly wholePropsLocals;
|
|
309
323
|
/** Tracks prop locals that are used as snippet/render props */
|
|
310
324
|
private readonly snippetPropLocals;
|
|
325
|
+
/** Per-declarator type metadata extracted from modern AST `$props()` annotations */
|
|
326
|
+
private readonly runesPropsDeclarationMetadataByDeclaratorStart;
|
|
327
|
+
/** Explicit TypeScript prop annotations for legacy `export let` declarations keyed by local name */
|
|
328
|
+
private readonly explicitPropTypesByName;
|
|
329
|
+
/** Type-only imports keyed by their local binding names */
|
|
330
|
+
private readonly typeImportBindingsByLocalName;
|
|
331
|
+
/** Local interface/type declarations keyed by type name */
|
|
332
|
+
private readonly localTypeDeclarationsByName;
|
|
333
|
+
/** Typed `$props()` declarations discovered in source order */
|
|
334
|
+
private readonly typedRunesPropsDeclarations;
|
|
311
335
|
/** Component-level lexical scope shared by instance script and template */
|
|
312
336
|
private readonly componentScope;
|
|
313
337
|
/** Precomputed lexical scopes for nested AST nodes */
|
|
@@ -323,6 +347,16 @@ export default class ComponentParser {
|
|
|
323
347
|
private trackPropLocalName;
|
|
324
348
|
private getPropByLocalOrPublic;
|
|
325
349
|
private getPropTypeByLocalOrPublic;
|
|
350
|
+
private getExplicitPropType;
|
|
351
|
+
private getRunesPropsDeclarationMetadata;
|
|
352
|
+
private getRunesPropTypeMetadata;
|
|
353
|
+
private getTypeReferenceName;
|
|
354
|
+
private getTypeDependencyName;
|
|
355
|
+
private getTypeAnnotationText;
|
|
356
|
+
private collectReferencedTypeDependencies;
|
|
357
|
+
private buildTypeImportStatements;
|
|
358
|
+
private buildTypeScriptMetadata;
|
|
359
|
+
private buildRunesPropTypeMetadataMap;
|
|
326
360
|
private declareScopeBinding;
|
|
327
361
|
private resolveIdentifierToReactiveProp;
|
|
328
362
|
private collectPatternIdentifiers;
|
|
@@ -347,7 +381,7 @@ export default class ComponentParser {
|
|
|
347
381
|
* Extracts and categorizes JSDoc tags from a parsed comment.
|
|
348
382
|
*
|
|
349
383
|
* Separates tags into type, param, returns, and additional categories while
|
|
350
|
-
* excluding tags that are handled separately (extends, restProps, slot, event, typedef).
|
|
384
|
+
* excluding tags that are handled separately (extends, restProps, slot/snippet, event, typedef).
|
|
351
385
|
*
|
|
352
386
|
* @param parsed - The parsed comment result from comment-parser
|
|
353
387
|
* @returns An object containing categorized tags and the comment description
|
|
@@ -390,6 +424,7 @@ export default class ComponentParser {
|
|
|
390
424
|
private static findJSDocComment;
|
|
391
425
|
private findAdjacentJSDocComment;
|
|
392
426
|
private processNodeJSDoc;
|
|
427
|
+
private processLeadingCommentsJSDoc;
|
|
393
428
|
/**
|
|
394
429
|
* Processes JSDoc comments from leadingComments and extracts structured information.
|
|
395
430
|
*
|
|
@@ -423,6 +458,7 @@ export default class ComponentParser {
|
|
|
423
458
|
* ```
|
|
424
459
|
*/
|
|
425
460
|
private processJSDocComment;
|
|
461
|
+
private buildRunesPropTypeMetadata;
|
|
426
462
|
/**
|
|
427
463
|
* Checks if a MemberExpression represents a well-known numeric constant.
|
|
428
464
|
*
|
|
@@ -494,6 +530,7 @@ export default class ComponentParser {
|
|
|
494
530
|
private parseRunesPropsDeclaration;
|
|
495
531
|
private inferSlotPropValueFromExpression;
|
|
496
532
|
private buildSlotPropsFromObjectExpression;
|
|
533
|
+
private resolveRenderTagPropReference;
|
|
497
534
|
private extractRenderTagInfo;
|
|
498
535
|
/**
|
|
499
536
|
* Adds or merges a component prop to the props map.
|
|
@@ -640,6 +677,7 @@ export default class ComponentParser {
|
|
|
640
677
|
* ```
|
|
641
678
|
*/
|
|
642
679
|
private addDispatchedEvent;
|
|
680
|
+
private normalizeRunesCallbackProps;
|
|
643
681
|
/**
|
|
644
682
|
* Parses custom types, events, slots, and other JSDoc annotations from component comments.
|
|
645
683
|
*
|