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 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
@@ -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 runesPropTypeMetadataByDeclaratorStart;
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.