sveld 0.31.0 → 0.32.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
@@ -127,6 +127,7 @@ export default class Button extends SvelteComponentTyped<
127
127
  - [CLI](#cli)
128
128
  - [Publishing to NPM](#publishing-to-npm)
129
129
  - [Available Options](#available-options)
130
+ - [JSON Output](#json-output)
130
131
  - [API Reference](#api-reference)
131
132
  - [@type](#type)
132
133
  - [@default](#default)
@@ -333,6 +334,121 @@ sveld({
333
334
  })
334
335
  ```
335
336
 
337
+ ## JSON Output
338
+
339
+ When `json: true` is enabled, `sveld` emits a `COMPONENT_API.json` file with schema and generator metadata plus the parsed
340
+ component API.
341
+
342
+ The public JSON Schema for the combined output is hosted 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 document or validate
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.
345
+
346
+ ```ts
347
+ interface ComponentApiJson {
348
+ schemaVersion: 1;
349
+ generator: {
350
+ name: string;
351
+ version: string;
352
+ svelteVersion: string;
353
+ };
354
+ total: number;
355
+ components: ComponentDocApi[];
356
+ }
357
+
358
+ interface SourceRange {
359
+ start: SourcePosition;
360
+ end: SourcePosition;
361
+ }
362
+
363
+ interface SourcePosition {
364
+ line: number;
365
+ column: number;
366
+ }
367
+
368
+ interface ComponentDocApi {
369
+ moduleName: string;
370
+ filePath: string;
371
+ source?: SourceRange;
372
+ syntaxMode: "legacy" | "runes";
373
+ scriptLanguage?: "js" | "ts";
374
+ props: ComponentProp[];
375
+ moduleExports: ComponentProp[];
376
+ slots: ComponentSlot[];
377
+ events: ComponentEvent[];
378
+ typedefs: TypeDef[];
379
+ generics: null | [name: string, type: string];
380
+ rest_props?: RestProps;
381
+ extends?: { interface: string; import: string };
382
+ componentComment?: string;
383
+ componentCommentSource?: SourceRange;
384
+ contexts?: ComponentContext[];
385
+ }
386
+
387
+ interface ComponentProp {
388
+ name: string;
389
+ localName?: string;
390
+ kind: "let" | "const" | "function";
391
+ constant: boolean;
392
+ type?: string;
393
+ typeSource?: "typescript" | "jsdoc" | "default" | "inferred" | "unknown";
394
+ value?: string;
395
+ defaultValue?: {
396
+ raw: string;
397
+ kind: "literal" | "array" | "object" | "expression" | "function" | "unknown";
398
+ value?: unknown;
399
+ };
400
+ description?: string;
401
+ params?: Array<{ name: string; type: string; description?: string; optional?: boolean }>;
402
+ returnType?: string;
403
+ isFunction: boolean;
404
+ isFunctionDeclaration: boolean;
405
+ isRequired: boolean;
406
+ reactive: boolean;
407
+ binding?: "readonly" | "writable";
408
+ bindable?: true;
409
+ source?: SourceRange;
410
+ }
411
+
412
+ interface ComponentSlot {
413
+ name?: string | null;
414
+ default: boolean;
415
+ fallback?: string;
416
+ slot_props?: string;
417
+ description?: string;
418
+ tags?: Array<{ name: string; body: string }>;
419
+ source?: SourceRange;
420
+ }
421
+
422
+ type ComponentEvent =
423
+ | {
424
+ type: "forwarded";
425
+ name: string;
426
+ element: string;
427
+ description?: string;
428
+ detail?: string;
429
+ source?: SourceRange;
430
+ }
431
+ | {
432
+ type: "dispatched";
433
+ name: string;
434
+ detail?: string;
435
+ description?: string;
436
+ source?: SourceRange;
437
+ };
438
+ ```
439
+
440
+ `source` fields are optional and are included only when the Svelte or JavaScript AST provides stable positions. They do not
441
+ include source text or raw character offsets.
442
+
443
+ Note that `SourcePosition.line` is 1-based and `SourcePosition.column` is 0-based.
444
+
445
+ Prop metadata is additive and preserves the older public fields:
446
+
447
+ - `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
+ - `typeSource` identifies the conservative source of the emitted `type`: TypeScript annotation, JSDoc, initializer/default inference, other parser inference, or unknown fallback.
449
+ - `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.
450
+ - `bindable: true` is emitted only for props explicitly declared with Svelte 5 `$bindable(...)`. Missing `bindable` should be treated as false.
451
+
336
452
  ## API Reference
337
453
 
338
454
  ### `reactive`
@@ -350,6 +466,38 @@ Local variables or parameters that shadow a prop name do not count as writes to
350
466
 
351
467
  `reactive: false` means `sveld` found no such evidence. It does not imply that parent-side `bind:` usage is impossible.
352
468
 
469
+ ### `binding`
470
+
471
+ The optional `binding` field in generated JSON is explicit documentation metadata for a prop's intended `bind:` contract. It is separate from `reactive`, and it is never inferred from internal writes or `$bindable()`.
472
+
473
+ Use `@bindable readonly` for component-owned or output-style bindings where the consumer binds to the current value emitted by the component:
474
+
475
+ ```svelte
476
+ <script>
477
+ /**
478
+ * Bind to the current value emitted by the component.
479
+ * @bindable readonly
480
+ */
481
+ export let size = undefined;
482
+ </script>
483
+ ```
484
+
485
+ Use `@bindable writable` for two-way or shared state bindings where either the consumer or component may control the value:
486
+
487
+ ```svelte
488
+ <script>
489
+ /**
490
+ * Bind to state controlled by either the consumer or component.
491
+ * @bindable writable
492
+ */
493
+ export let open = false;
494
+ </script>
495
+ ```
496
+
497
+ Generated JSON includes `"binding": "readonly"` or `"binding": "writable"` for annotated props. Unannotated props omit the field.
498
+
499
+ This is documentation metadata only. Generated `.svelte.d.ts` prop types are unchanged because TypeScript cannot reliably express Svelte component binding direction.
500
+
353
501
  For stable output, generated `events` arrays are emitted in deterministic sorted order.
354
502
 
355
503
  ### `@type`
@@ -1,3 +1,13 @@
1
+ export interface SourcePosition {
2
+ /** 1-based source line number */
3
+ line: number;
4
+ /** 0-based source column number */
5
+ column: number;
6
+ }
7
+ export interface SourceRange {
8
+ start: SourcePosition;
9
+ end: SourcePosition;
10
+ }
1
11
  export interface ParsedComponentTypeScriptMetadata {
2
12
  canonicalPropsType?: string;
3
13
  canonicalPropNames: string[];
@@ -8,6 +18,15 @@ export declare const PARSED_COMPONENT_TYPE_SCRIPT_METADATA: unique symbol;
8
18
  export declare function getParsedComponentTypeScriptMetadata(component: {
9
19
  [PARSED_COMPONENT_TYPE_SCRIPT_METADATA]?: ParsedComponentTypeScriptMetadata;
10
20
  }): ParsedComponentTypeScriptMetadata | undefined;
21
+ type SyntaxMode = "legacy" | "runes";
22
+ type ScriptLanguage = "js" | "ts";
23
+ type ComponentPropTypeSource = "typescript" | "jsdoc" | "default" | "inferred" | "unknown";
24
+ type ComponentPropDefaultValueKind = "literal" | "array" | "object" | "expression" | "function" | "unknown";
25
+ interface ComponentPropDefaultValue {
26
+ raw: string;
27
+ kind: ComponentPropDefaultValueKind;
28
+ value?: unknown;
29
+ }
11
30
  /**
12
31
  * Diagnostic information for component parsing.
13
32
  *
@@ -27,6 +46,7 @@ interface ComponentParserOptions {
27
46
  /** Enable verbose logging for debugging parsing issues */
28
47
  verbose?: boolean;
29
48
  }
49
+ type ComponentPropBinding = "readonly" | "writable";
30
50
  /**
31
51
  * Parameter information for function props.
32
52
  *
@@ -58,8 +78,14 @@ interface ComponentProp {
58
78
  constant: boolean;
59
79
  /** The TypeScript type of the prop (e.g., "string", "number | string") */
60
80
  type?: string;
81
+ /** Conservative provenance for the prop type */
82
+ typeSource?: ComponentPropTypeSource;
83
+ /** Local variable name, emitted when it differs from the public prop name */
84
+ localName?: string;
61
85
  /** The default value as a string representation of the source code */
62
86
  value?: string;
87
+ /** Structured default value metadata for docs UIs */
88
+ defaultValue?: ComponentPropDefaultValue;
63
89
  /** Description extracted from JSDoc comments */
64
90
  description?: string;
65
91
  /** Function parameters (for function props) extracted from `@param` tags */
@@ -74,6 +100,12 @@ interface ComponentProp {
74
100
  isRequired: boolean;
75
101
  /** Whether this prop is reactive (can change and trigger reactivity) */
76
102
  reactive: boolean;
103
+ /** Explicit author-documented binding direction from `@bindable` JSDoc */
104
+ binding?: ComponentPropBinding;
105
+ /** True when the prop is explicitly declared with Svelte 5 `$bindable()` */
106
+ bindable?: true;
107
+ /** Source range for the prop declaration, when available */
108
+ source?: SourceRange;
77
109
  }
78
110
  /**
79
111
  * Component slot definition.
@@ -100,6 +132,8 @@ interface ComponentSlot {
100
132
  name: string;
101
133
  body: string;
102
134
  }>;
135
+ /** Source range for the slot/snippet declaration or documentation tag, when available */
136
+ source?: SourceRange;
103
137
  }
104
138
  /**
105
139
  * Event that is forwarded from a child component or element.
@@ -118,6 +152,8 @@ interface ForwardedEvent {
118
152
  description?: string;
119
153
  /** The detail type if explicitly specified in `@event` tag */
120
154
  detail?: string;
155
+ /** Source range for the forwarded event declaration or documentation tag, when available */
156
+ source?: SourceRange;
121
157
  }
122
158
  /**
123
159
  * Event that is dispatched by the component.
@@ -134,6 +170,8 @@ interface DispatchedEvent {
134
170
  detail?: string;
135
171
  /** Description extracted from JSDoc `@event` tags */
136
172
  description?: string;
173
+ /** Source range for the dispatched event call or documentation tag, when available */
174
+ source?: SourceRange;
137
175
  }
138
176
  type ComponentEvent = ForwardedEvent | DispatchedEvent;
139
177
  /**
@@ -256,6 +294,12 @@ interface ComponentContext {
256
294
  * ```
257
295
  */
258
296
  export interface ParsedComponent {
297
+ /** Source range for the component source that was parsed */
298
+ source?: SourceRange;
299
+ /** Whether the component uses legacy or runes syntax according to compiler metadata */
300
+ syntaxMode: SyntaxMode;
301
+ /** Language used by the instance or module script when it can be determined */
302
+ scriptLanguage?: ScriptLanguage;
259
303
  /** Component props that can be passed to the component */
260
304
  props: ComponentProp[];
261
305
  /** Exports from `<script context="module">` block */
@@ -274,6 +318,8 @@ export interface ParsedComponent {
274
318
  extends?: Extends;
275
319
  /** Component-level description from `@component` HTML comment */
276
320
  componentComment?: string;
321
+ /** Source range for the `@component` HTML comment, when available */
322
+ componentCommentSource?: SourceRange;
277
323
  /** Contexts created with `setContext` in the component */
278
324
  contexts?: ComponentContext[];
279
325
  /** Internal writer-only TypeScript metadata. Not serialized to JSON. */
@@ -284,6 +330,8 @@ export default class ComponentParser {
284
330
  private options?;
285
331
  /** Whether the component uses legacy or runes syntax according to compiler metadata */
286
332
  private syntaxMode;
333
+ /** Language used by the component's instance or module script, when supported */
334
+ private scriptLanguage?;
287
335
  /** Raw source code of the Svelte component being parsed */
288
336
  private source?;
289
337
  /** Compiled Svelte code containing extracted variables and AST */
@@ -296,6 +344,8 @@ export default class ComponentParser {
296
344
  private extends?;
297
345
  /** Component-level description extracted from `@component` HTML comment */
298
346
  private componentComment?;
347
+ /** Source range for the `@component` HTML comment, when available */
348
+ private componentCommentSource?;
299
349
  /** Set of reactive variable names found in the component */
300
350
  private readonly reactive_vars;
301
351
  /** Set of all variable declarations found in the component script */
@@ -348,8 +398,12 @@ export default class ComponentParser {
348
398
  private readonly activeScopes;
349
399
  /** Cached array of source code lines split by newline for efficient line-based operations */
350
400
  private sourceLinesCache?;
401
+ /** Cached 0-based source offsets for the start of each line */
402
+ private sourceLineStartOffsetsCache?;
351
403
  constructor(options?: ComponentParserOptions);
352
404
  private static mapToArray;
405
+ private static getStaticAttributeValue;
406
+ private static resolveScriptLanguage;
353
407
  private static assignValue;
354
408
  private resolvePublicPropName;
355
409
  private trackPropLocalName;
@@ -361,6 +415,11 @@ export default class ComponentParser {
361
415
  private getTypeReferenceName;
362
416
  private getTypeDependencyName;
363
417
  private getTypeAnnotationText;
418
+ private getSourceLineStartOffsets;
419
+ private sourcePositionFromOffset;
420
+ private sourceRangeFromOffsets;
421
+ private sourceRangeFromNode;
422
+ private sourceRangeFromCommentTag;
364
423
  private collectReferencedTypeDependencies;
365
424
  private buildTypeImportStatements;
366
425
  private buildTypeScriptMetadata;
@@ -384,6 +443,7 @@ export default class ComponentParser {
384
443
  private isCallExpressionNamed;
385
444
  private getPropertyName;
386
445
  private logUnsupportedRunesPattern;
446
+ private logParserWarning;
387
447
  private static formatComment;
388
448
  /**
389
449
  * Extracts and categorizes JSDoc tags from a parsed comment.
@@ -498,6 +558,10 @@ export default class ComponentParser {
498
558
  * @returns The source code substring, or undefined if source is not available
499
559
  */
500
560
  private sourceAtPos;
561
+ private sourceForExpression;
562
+ private jsonSafeValueFromExpression;
563
+ private classifyDefaultValue;
564
+ private resolveTypeSource;
501
565
  /**
502
566
  * Processes an initializer expression to extract its value, type, and function status.
503
567
  *