sveld 0.29.1 → 0.30.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 +102 -2
- package/lib/ComponentParser.d.ts +38 -1
- 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 | ✓ |
|
|
@@ -127,6 +129,7 @@ export default class Button extends SvelteComponentTyped<
|
|
|
127
129
|
- [Available Options](#available-options)
|
|
128
130
|
- [API Reference](#api-reference)
|
|
129
131
|
- [@type](#type)
|
|
132
|
+
- [@default](#default)
|
|
130
133
|
- [@typedef](#typedef)
|
|
131
134
|
- [@callback](#callback)
|
|
132
135
|
- [@slot / @snippet](#slot--snippet)
|
|
@@ -156,6 +159,15 @@ Extracted metadata include:
|
|
|
156
159
|
|
|
157
160
|
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
161
|
|
|
162
|
+
When both TypeScript syntax and JSDoc are present, `sveld` resolves prop types in this order:
|
|
163
|
+
|
|
164
|
+
1. explicit TypeScript annotation
|
|
165
|
+
2. explicit JSDoc annotation
|
|
166
|
+
3. initializer inference
|
|
167
|
+
4. `any`
|
|
168
|
+
|
|
169
|
+
`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.
|
|
170
|
+
|
|
159
171
|
## Usage
|
|
160
172
|
|
|
161
173
|
### Installation
|
|
@@ -204,13 +216,15 @@ sveld({
|
|
|
204
216
|
When building the library, TypeScript definitions are emitted to the `types` folder by default.
|
|
205
217
|
|
|
206
218
|
Customize the output folder using the `typesOptions.outDir` option.
|
|
219
|
+
Use `typesOptions.printWidth` to control Prettier wrapping for generated `.d.ts` files. The default is `80`.
|
|
207
220
|
|
|
208
221
|
The following example emits the output to the `dist` folder:
|
|
209
222
|
|
|
210
223
|
```diff
|
|
211
224
|
sveld({
|
|
212
225
|
+ typesOptions: {
|
|
213
|
-
+ outDir: 'dist'
|
|
226
|
+
+ outDir: 'dist',
|
|
227
|
+
+ printWidth: 80
|
|
214
228
|
+ }
|
|
215
229
|
})
|
|
216
230
|
```
|
|
@@ -300,7 +314,7 @@ TypeScript definitions are outputted to the `types` folder by default. Don't for
|
|
|
300
314
|
- **`entry`** (string, optional): Specify the entry point to uncompiled Svelte source. If not provided, sveld will use the `"svelte"` field from `package.json`.
|
|
301
315
|
- **`glob`** (boolean, optional): Enable glob mode to analyze all `*.svelte` files.
|
|
302
316
|
- **`types`** (boolean, optional, default: `true`): Generate TypeScript definitions.
|
|
303
|
-
- **`typesOptions`** (object, optional): Options for TypeScript definition generation
|
|
317
|
+
- **`typesOptions`** (object, optional): Options for TypeScript definition generation, including `outDir`, `preamble`, and `printWidth`.
|
|
304
318
|
- **`json`** (boolean, optional): Generate component documentation in JSON format.
|
|
305
319
|
- **`jsonOptions`** (object, optional): Options for JSON output.
|
|
306
320
|
- **`markdown`** (boolean, optional): Generate component documentation in Markdown format.
|
|
@@ -354,6 +368,8 @@ export let id = `ccs-${Math.random().toString(36)}`;
|
|
|
354
368
|
|
|
355
369
|
Use the `@type` tag to explicitly document the type. In the following example, the `kind` property has an enumerated (enum) type.
|
|
356
370
|
|
|
371
|
+
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.
|
|
372
|
+
|
|
357
373
|
**Signature:**
|
|
358
374
|
|
|
359
375
|
```js
|
|
@@ -404,6 +420,88 @@ For runes components with multiple destructured props, place JSDoc on the indivi
|
|
|
404
420
|
</script>
|
|
405
421
|
```
|
|
406
422
|
|
|
423
|
+
### `@default`
|
|
424
|
+
|
|
425
|
+
By default, `sveld` infers the `@default` value from the prop's initializer and includes it in the generated TypeScript definitions:
|
|
426
|
+
|
|
427
|
+
```svelte
|
|
428
|
+
<script>
|
|
429
|
+
export let open = false;
|
|
430
|
+
</script>
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
```ts
|
|
434
|
+
/**
|
|
435
|
+
* @default false
|
|
436
|
+
*/
|
|
437
|
+
open?: boolean;
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
Use the `@default` tag to explicitly document the default value. When an explicit `@default` annotation is provided, `sveld` uses it instead of the inferred value, avoiding duplicate `@default` tags in the output.
|
|
441
|
+
|
|
442
|
+
This is useful when the initializer references a variable or expression that is not meaningful to consumers:
|
|
443
|
+
|
|
444
|
+
```svelte
|
|
445
|
+
<script>
|
|
446
|
+
const defaultFilter = () => true;
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* @default () => true
|
|
450
|
+
* @type {(item: string, value: string) => boolean}
|
|
451
|
+
*/
|
|
452
|
+
export let shouldFilter = defaultFilter;
|
|
453
|
+
</script>
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
```ts
|
|
457
|
+
/**
|
|
458
|
+
* @default () => true
|
|
459
|
+
*/
|
|
460
|
+
shouldFilter?: (item: string, value: string) => boolean;
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
#### Identifier resolution
|
|
464
|
+
|
|
465
|
+
When a prop's initializer is a variable reference, `sveld` resolves it to the actual value automatically:
|
|
466
|
+
|
|
467
|
+
```svelte
|
|
468
|
+
<script>
|
|
469
|
+
const DEFAULT_SIZE = "md";
|
|
470
|
+
|
|
471
|
+
/** @type {"sm" | "md" | "lg"} */
|
|
472
|
+
export let size = DEFAULT_SIZE;
|
|
473
|
+
</script>
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
```ts
|
|
477
|
+
/**
|
|
478
|
+
* @default "md"
|
|
479
|
+
*/
|
|
480
|
+
size?: "sm" | "md" | "lg";
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
Chained references are also resolved:
|
|
484
|
+
|
|
485
|
+
```svelte
|
|
486
|
+
<script>
|
|
487
|
+
const ACTUAL_VALUE = 42;
|
|
488
|
+
const ALIAS = ACTUAL_VALUE;
|
|
489
|
+
|
|
490
|
+
export let count = ALIAS;
|
|
491
|
+
</script>
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
```ts
|
|
495
|
+
/**
|
|
496
|
+
* @default 42
|
|
497
|
+
*/
|
|
498
|
+
count?: number;
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
Resolution follows up to 5 levels of indirection. Beyond that, the last resolved identifier name is used as the default value. If the identifier cannot be resolved (e.g., it is imported from another module), the variable name is used as-is.
|
|
502
|
+
|
|
503
|
+
When an explicit `@default` annotation is provided, it always takes precedence over the resolved value.
|
|
504
|
+
|
|
407
505
|
### `@typedef`
|
|
408
506
|
|
|
409
507
|
The `@typedef` tag can be used to define a common type that is used multiple times within a component. All typedefs defined in a component will be exported from the generated TypeScript definition file.
|
|
@@ -761,6 +859,8 @@ For Svelte 5 compatibility, `sveld` automatically generates optional snippet pro
|
|
|
761
859
|
|
|
762
860
|
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.
|
|
763
861
|
|
|
862
|
+
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.
|
|
863
|
+
|
|
764
864
|
For slots with props (e.g., `let:prop`), the generated type uses a Snippet-compatible signature:
|
|
765
865
|
|
|
766
866
|
```ts
|
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
|
*
|
|
@@ -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,10 +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;
|
|
311
325
|
/** Per-declarator type metadata extracted from modern AST `$props()` annotations */
|
|
312
|
-
private readonly
|
|
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;
|
|
313
335
|
/** Component-level lexical scope shared by instance script and template */
|
|
314
336
|
private readonly componentScope;
|
|
315
337
|
/** Precomputed lexical scopes for nested AST nodes */
|
|
@@ -325,7 +347,16 @@ export default class ComponentParser {
|
|
|
325
347
|
private trackPropLocalName;
|
|
326
348
|
private getPropByLocalOrPublic;
|
|
327
349
|
private getPropTypeByLocalOrPublic;
|
|
350
|
+
private getExplicitPropType;
|
|
351
|
+
private getRunesPropsDeclarationMetadata;
|
|
328
352
|
private getRunesPropTypeMetadata;
|
|
353
|
+
private getTypeReferenceName;
|
|
354
|
+
private getTypeDependencyName;
|
|
355
|
+
private getTypeAnnotationText;
|
|
356
|
+
private collectReferencedTypeDependencies;
|
|
357
|
+
private buildTypeImportStatements;
|
|
358
|
+
private buildTypeScriptMetadata;
|
|
359
|
+
private buildRunesPropTypeMetadataMap;
|
|
329
360
|
private declareScopeBinding;
|
|
330
361
|
private resolveIdentifierToReactiveProp;
|
|
331
362
|
private collectPatternIdentifiers;
|
|
@@ -489,6 +520,11 @@ export default class ComponentParser {
|
|
|
489
520
|
* ```
|
|
490
521
|
*/
|
|
491
522
|
private processInitializer;
|
|
523
|
+
/**
|
|
524
|
+
* Look up a local variable's initializer AST node by name.
|
|
525
|
+
* Returns the init node if found, or undefined.
|
|
526
|
+
*/
|
|
527
|
+
private resolveLocalVarInitializer;
|
|
492
528
|
/**
|
|
493
529
|
* Unwraps `$bindable(...)` calls so defaults are documented as their underlying values.
|
|
494
530
|
*/
|
|
@@ -499,6 +535,7 @@ export default class ComponentParser {
|
|
|
499
535
|
private parseRunesPropsDeclaration;
|
|
500
536
|
private inferSlotPropValueFromExpression;
|
|
501
537
|
private buildSlotPropsFromObjectExpression;
|
|
538
|
+
private resolveRenderTagPropReference;
|
|
502
539
|
private extractRenderTagInfo;
|
|
503
540
|
/**
|
|
504
541
|
* Adds or merges a component prop to the props map.
|