sveld 0.28.0 → 0.29.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
@@ -3,13 +3,22 @@
3
3
  [![NPM][npm]][npm-url]
4
4
  ![npm downloads to date](https://img.shields.io/npm/dt/sveld?color=262626&style=for-the-badge)
5
5
 
6
- `sveld` is a TypeScript definition generator for Svelte components. It analyzes props, events, slots, and other component features through static analysis. Types and signatures can be defined using [JSDoc notation](https://jsdoc.app/). The tool can also generate component documentation in Markdown and JSON formats.
6
+ `sveld` generates TypeScript definitions and component documentation (Markdown/JSON) for Svelte components. It analyzes props, events, slots, and other component features through static analysis. Types and signatures can be defined using [JSDoc notation](https://jsdoc.app/).
7
7
 
8
- The purpose of this project is to make third party Svelte component libraries compatible with the Svelte Language Server and TypeScript with minimal effort required by the author. For example, TypeScript definitions may be used during development via intelligent code completion in Integrated Development Environments (IDE) like VSCode.
8
+ The purpose of this project is to make third party Svelte component libraries compatible with the Svelte Language Server and TypeScript with minimal effort required by the author. For example, TypeScript definitions may be used during development via intelligent code completion in Integrated Development Environments (IDEs) like VSCode.
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
- **Note:** `sveld` supports Svelte 3, 4, and 5, but does not support Svelte 5-specific syntax or runes-only usage. Components must use traditional Svelte syntax (e.g., `export let` for props, not `$props()`).
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
+
14
+ | Syntax mode | Supported |
15
+ | :------------------- | :-------: |
16
+ | Svelte 3 | ✓ |
17
+ | Svelte 4 | ✓ |
18
+ | Svelte 5 (non-Runes) | ✓ |
19
+ | Svelte 5 Runes | ✓ |
20
+
21
+ **Note** that 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.
13
22
 
14
23
  ---
15
24
 
@@ -28,7 +37,7 @@ Given a Svelte component, `sveld` can infer basic prop types to generate TypeScr
28
37
  </button>
29
38
  ```
30
39
 
31
- The generated definition extends the official `SvelteComponentTyped` interface exported from Svelte.
40
+ The following generated `.d.ts` extends `SvelteComponentTyped`:
32
41
 
33
42
  **Button.svelte.d.ts**
34
43
 
@@ -120,7 +129,7 @@ export default class Button extends SvelteComponentTyped<
120
129
  - [@type](#type)
121
130
  - [@typedef](#typedef)
122
131
  - [@callback](#callback)
123
- - [@slot](#slot)
132
+ - [@slot / @snippet](#slot--snippet)
124
133
  - [Svelte 5 Snippet Compatibility](#svelte-5-snippet-compatibility)
125
134
  - [@event](#event)
126
135
  - [Context API](#context-api)
@@ -310,6 +319,23 @@ sveld({
310
319
 
311
320
  ## API Reference
312
321
 
322
+ ### `reactive`
323
+
324
+ The `reactive` field in generated JSON is heuristic metadata. It is not a complete statement of whether a parent may use `bind:prop` in Svelte.
325
+
326
+ `sveld` marks `reactive: true` when it finds internal evidence that a prop is writable, including:
327
+
328
+ - the prop is assigned or mutated inside the component
329
+ - the prop is marked bindable in runes mode with `$bindable(...)`
330
+ - the prop is used as the target of `bind:*` on an element or child component
331
+ - wrapper-forwarded bindings such as `bind:value`, `bind:selected`, and `bind:ref`
332
+
333
+ Local variables or parameters that shadow a prop name do not count as writes to the exported prop.
334
+
335
+ `reactive: false` means `sveld` found no such evidence. It does not imply that parent-side `bind:` usage is impossible.
336
+
337
+ For stable output, generated `events` arrays are emitted in deterministic sorted order.
338
+
313
339
  ### `@type`
314
340
 
315
341
  Without a `@type` annotation, `sveld` will infer the primitive type for a prop:
@@ -328,7 +354,7 @@ export let id = `ccs-${Math.random().toString(36)}`;
328
354
 
329
355
  Use the `@type` tag to explicitly document the type. In the following example, the `kind` property has an enumerated (enum) type.
330
356
 
331
- Signature:
357
+ **Signature:**
332
358
 
333
359
  ```js
334
360
  /**
@@ -337,27 +363,52 @@ Signature:
337
363
  */
338
364
  ```
339
365
 
340
- Example:
366
+ **Example:**
341
367
 
342
- ```js
343
- /**
344
- * Specify the kind of button
345
- * @type {"primary" | "secondary" | "tertiary"}
346
- */
347
- export let kind = "primary";
368
+ **Svelte 5 Runes:**
348
369
 
349
- /**
350
- * Specify the Carbon icon to render
351
- * @type {typeof import("carbon-icons-svelte").CarbonIcon}
352
- */
353
- export let renderIcon = Close20;
370
+ ```svelte
371
+ <script>
372
+ let {
373
+ /**
374
+ * Specify the kind of button
375
+ * @type {"primary" | "secondary" | "tertiary"}
376
+ */
377
+ kind = "primary",
378
+ /**
379
+ * Specify the Carbon icon to render
380
+ * @type {typeof import("carbon-icons-svelte").CarbonIcon}
381
+ */
382
+ renderIcon = Close20,
383
+ } = $props();
384
+ </script>
385
+ ```
386
+
387
+ 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.
388
+
389
+ **Svelte 3, 4, 5 (non-Runes):**
390
+
391
+ ```svelte
392
+ <script>
393
+ /**
394
+ * Specify the kind of button
395
+ * @type {"primary" | "secondary" | "tertiary"}
396
+ */
397
+ export let kind = "primary";
398
+
399
+ /**
400
+ * Specify the Carbon icon to render
401
+ * @type {typeof import("carbon-icons-svelte").CarbonIcon}
402
+ */
403
+ export let renderIcon = Close20;
404
+ </script>
354
405
  ```
355
406
 
356
407
  ### `@typedef`
357
408
 
358
409
  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.
359
410
 
360
- Signature:
411
+ **Signature:**
361
412
 
362
413
  ```js
363
414
  /**
@@ -365,26 +416,48 @@ Signature:
365
416
  */
366
417
  ```
367
418
 
368
- Example:
419
+ **Example:**
369
420
 
370
- ```js
371
- /**
372
- * @typedef {string} AuthorName
373
- * @typedef {{ name?: AuthorName; dob?: string; }} Author
374
- */
421
+ **Svelte 5 Runes:**
375
422
 
376
- /** @type {Author} */
377
- export let author = {};
423
+ ```svelte
424
+ <script>
425
+ /**
426
+ * @typedef {string} AuthorName
427
+ * @typedef {{ name?: AuthorName; dob?: string; }} Author
428
+ */
378
429
 
379
- /** @type {Author[]} */
380
- export let authors = [];
430
+ let {
431
+ /** @type {Author} */
432
+ author = {},
433
+ /** @type {Author[]} */
434
+ authors = [],
435
+ } = $props();
436
+ </script>
437
+ ```
438
+
439
+ **Svelte 3, 4, 5 (non-Runes):**
440
+
441
+ ```svelte
442
+ <script>
443
+ /**
444
+ * @typedef {string} AuthorName
445
+ * @typedef {{ name?: AuthorName; dob?: string; }} Author
446
+ */
447
+
448
+ /** @type {Author} */
449
+ export let author = {};
450
+
451
+ /** @type {Author[]} */
452
+ export let authors = [];
453
+ </script>
381
454
  ```
382
455
 
383
456
  #### Using `@property` for complex typedefs
384
457
 
385
458
  For complex object types, use the `@property` tag to document individual properties. This provides better documentation and IDE support with per-property tooltips.
386
459
 
387
- Signature:
460
+ **Signature:**
388
461
 
389
462
  ```js
390
463
  /**
@@ -394,19 +467,40 @@ Signature:
394
467
  */
395
468
  ```
396
469
 
397
- Example:
470
+ **Example:**
398
471
 
399
- ```js
400
- /**
401
- * Represents a user in the system
402
- * @typedef {object} User
403
- * @property {string} name - The user's full name
404
- * @property {string} email - The user's email address
405
- * @property {number} age - The user's age in years
406
- */
472
+ **Svelte 5 Runes:**
473
+
474
+ ```svelte
475
+ <script>
476
+ /**
477
+ * Represents a user in the system
478
+ * @typedef {object} User
479
+ * @property {string} name - The user's full name
480
+ * @property {string} email - The user's email address
481
+ * @property {number} age - The user's age in years
482
+ */
483
+
484
+ /** @type {User} */
485
+ let { user = { name: "John", email: "john@example.com", age: 30 } } = $props();
486
+ </script>
487
+ ```
488
+
489
+ **Svelte 3, 4, 5 (non-Runes):**
490
+
491
+ ```svelte
492
+ <script>
493
+ /**
494
+ * Represents a user in the system
495
+ * @typedef {object} User
496
+ * @property {string} name - The user's full name
497
+ * @property {string} email - The user's email address
498
+ * @property {number} age - The user's age in years
499
+ */
407
500
 
408
- /** @type {User} */
409
- export let user = { name: "John", email: "john@example.com", age: 30 };
501
+ /** @type {User} */
502
+ export let user = { name: "John", email: "john@example.com", age: 30 };
503
+ </script>
410
504
  ```
411
505
 
412
506
  Output:
@@ -434,7 +528,7 @@ export type ComponentProps = {
434
528
 
435
529
  Following JSDoc standards, use square brackets to mark properties as optional. You can also specify default values using the `[propertyName=defaultValue]` syntax.
436
530
 
437
- Signature:
531
+ **Signature:**
438
532
 
439
533
  ```js
440
534
  /**
@@ -444,20 +538,42 @@ Signature:
444
538
  */
445
539
  ```
446
540
 
447
- Example:
541
+ **Example:**
448
542
 
449
- ```js
450
- /**
451
- * Configuration options for the component
452
- * @typedef {object} ComponentConfig
453
- * @property {boolean} enabled - Whether the component is enabled
454
- * @property {string} theme - The component theme
455
- * @property {number} [timeout=5000] - Optional timeout in milliseconds
456
- * @property {boolean} [debug] - Optional debug mode flag
457
- */
543
+ **Svelte 5 Runes:**
544
+
545
+ ```svelte
546
+ <script>
547
+ /**
548
+ * Configuration options for the component
549
+ * @typedef {object} ComponentConfig
550
+ * @property {boolean} enabled - Whether the component is enabled
551
+ * @property {string} theme - The component theme
552
+ * @property {number} [timeout=5000] - Optional timeout in milliseconds
553
+ * @property {boolean} [debug] - Optional debug mode flag
554
+ */
555
+
556
+ /** @type {ComponentConfig} */
557
+ let { config = { enabled: true, theme: "dark" } } = $props();
558
+ </script>
559
+ ```
560
+
561
+ **Svelte 3, 4, 5 (non-Runes):**
562
+
563
+ ```svelte
564
+ <script>
565
+ /**
566
+ * Configuration options for the component
567
+ * @typedef {object} ComponentConfig
568
+ * @property {boolean} enabled - Whether the component is enabled
569
+ * @property {string} theme - The component theme
570
+ * @property {number} [timeout=5000] - Optional timeout in milliseconds
571
+ * @property {boolean} [debug] - Optional debug mode flag
572
+ */
458
573
 
459
- /** @type {ComponentConfig} */
460
- export let config = { enabled: true, theme: "dark" };
574
+ /** @type {ComponentConfig} */
575
+ export let config = { enabled: true, theme: "dark" };
576
+ </script>
461
577
  ```
462
578
 
463
579
  Output:
@@ -491,7 +607,7 @@ The `@callback` tag defines a function type using `@param` and `@returns` tags,
491
607
 
492
608
  This is useful for typing callback props without using inline function type syntax.
493
609
 
494
- Signature:
610
+ **Signature:**
495
611
 
496
612
  ```js
497
613
  /**
@@ -502,19 +618,40 @@ Signature:
502
618
  */
503
619
  ```
504
620
 
505
- Example:
621
+ **Example:**
506
622
 
507
- ```js
508
- /**
509
- * Callback fired when the value changes
510
- * @callback OnChange
511
- * @param {string} value - The new value
512
- * @param {number} index - The index of the changed item
513
- * @returns {void}
514
- */
623
+ **Svelte 5 Runes:**
624
+
625
+ ```svelte
626
+ <script>
627
+ /**
628
+ * Callback fired when the value changes
629
+ * @callback OnChange
630
+ * @param {string} value - The new value
631
+ * @param {number} index - The index of the changed item
632
+ * @returns {void}
633
+ */
515
634
 
516
- /** @type {OnChange} */
517
- export let onChange = (value, index) => {};
635
+ /** @type {OnChange} */
636
+ let { onChange = (value, index) => {} } = $props();
637
+ </script>
638
+ ```
639
+
640
+ **Svelte 3, 4, 5 (non-Runes):**
641
+
642
+ ```svelte
643
+ <script>
644
+ /**
645
+ * Callback fired when the value changes
646
+ * @callback OnChange
647
+ * @param {string} value - The new value
648
+ * @param {number} index - The index of the changed item
649
+ * @returns {void}
650
+ */
651
+
652
+ /** @type {OnChange} */
653
+ export let onChange = (value, index) => {};
654
+ </script>
518
655
  ```
519
656
 
520
657
  Output:
@@ -548,27 +685,54 @@ Callbacks can be combined with `@typedef` in the same comment block:
548
685
 
549
686
  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.
550
687
 
551
- ### `@slot`
688
+ ### `@slot` / `@snippet`
552
689
 
553
- Use the `@slot` tag for typing component slots. Note that `@slot` is a non-standard JSDoc tag.
690
+ 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.
554
691
 
555
692
  Descriptions are optional for named slots. Currently, the default slot cannot have a description.
556
693
 
557
- Signature:
694
+ **Signature:**
558
695
 
559
696
  ```js
560
697
  /**
561
698
  * @slot {Type} slot-name [slot description]
699
+ * @snippet {Type} snippet-name [snippet description]
562
700
  */
563
701
 
564
702
  Omit the `slot-name` to type the default slot.
565
703
 
566
704
  /**
567
705
  * @slot {Type}
706
+ * @snippet {Type}
568
707
  */
569
708
  ```
570
709
 
571
- Example:
710
+ **Example:**
711
+
712
+ **Svelte 5 Runes:**
713
+
714
+ ```svelte
715
+ <script>
716
+ /**
717
+ * @snippet {{ prop: number; doubled: number; }}
718
+ * @snippet {{}} title
719
+ * @snippet {{ prop: number }} body - Customize the paragraph text.
720
+ */
721
+
722
+ let { prop = 0, children, title, body } = $props();
723
+ </script>
724
+
725
+ <h1>
726
+ {@render children?.({ prop, doubled: prop * 2 })}
727
+ {@render title?.()}
728
+ </h1>
729
+
730
+ <p>
731
+ {@render body?.({ prop })}
732
+ </p>
733
+ ```
734
+
735
+ **Svelte 3, 4, 5 (non-Runes):**
572
736
 
573
737
  ```svelte
574
738
  <script>
@@ -595,6 +759,8 @@ Example:
595
759
 
596
760
  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.
597
761
 
762
+ 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
+
598
764
  For slots with props (e.g., `let:prop`), the generated type uses a Snippet-compatible signature:
599
765
 
600
766
  ```ts
@@ -697,9 +863,11 @@ export default class DataTable<Row> extends SvelteComponentTyped<
697
863
 
698
864
  Use the `@event` tag to type dispatched events. An event name is required and a description optional.
699
865
 
866
+ 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.
867
+
700
868
  Use `null` as the value if no event detail is provided.
701
869
 
702
- Signature:
870
+ **Signature:**
703
871
 
704
872
  ```js
705
873
  /**
@@ -708,22 +876,62 @@ Signature:
708
876
  */
709
877
  ```
710
878
 
711
- Example:
879
+ **Example:**
712
880
 
713
- ```js
714
- /**
715
- * @event {{ key: string }} button:key
716
- * @event {null} key – Fired when `key` changes.
717
- */
881
+ **Svelte 5 Runes:**
882
+
883
+ ```svelte
884
+ <script>
885
+ /**
886
+ * Fired when a value is saved.
887
+ * @event {{ id: string }} save
888
+ */
889
+ let { onsave } = $props();
890
+ </script>
891
+
892
+ <button onclick={() => onsave?.({ id: "1" })}>Save</button>
893
+ ```
894
+
895
+ **Svelte 5 Runes with dispatched events:**
896
+
897
+ ```svelte
898
+ <script>
899
+ /**
900
+ * @event {{ key: string }} button:key
901
+ * @event {null} key - Fired when `key` changes.
902
+ */
903
+
904
+ let { key = "" } = $props();
905
+
906
+ import { createEventDispatcher } from "svelte";
907
+
908
+ const dispatch = createEventDispatcher();
909
+
910
+ $effect(() => {
911
+ dispatch("button:key", { key });
912
+ if (key) dispatch("key");
913
+ });
914
+ </script>
915
+ ```
718
916
 
719
- export let key = "";
917
+ **Svelte 3, 4, 5 (non-Runes):**
720
918
 
721
- import { createEventDispatcher } from "svelte";
919
+ ```svelte
920
+ <script>
921
+ /**
922
+ * @event {{ key: string }} button:key
923
+ * @event {null} key - Fired when `key` changes.
924
+ */
722
925
 
723
- const dispatch = createEventDispatcher();
926
+ export let key = "";
724
927
 
725
- $: dispatch("button:key", { key });
726
- $: if (key) dispatch("key");
928
+ import { createEventDispatcher } from "svelte";
929
+
930
+ const dispatch = createEventDispatcher();
931
+
932
+ $: dispatch("button:key", { key });
933
+ $: if (key) dispatch("key");
934
+ </script>
727
935
  ```
728
936
 
729
937
  Output:
@@ -743,7 +951,7 @@ export default class Component extends SvelteComponentTyped<
743
951
 
744
952
  For events with complex object payloads, use the `@property` tag to document individual properties. The main comment description will be used as the event description.
745
953
 
746
- Signature:
954
+ **Signature:**
747
955
 
748
956
  ```js
749
957
  /**
@@ -754,30 +962,64 @@ Signature:
754
962
  */
755
963
  ```
756
964
 
757
- Example:
965
+ **Example:**
758
966
 
759
- ```js
760
- /**
761
- * Fired when the user submits the form
762
- *
763
- * @event submit
764
- * @type {object}
765
- * @property {string} name - The user's name
766
- * @property {string} email - The user's email address
767
- * @property {boolean} newsletter - Whether the user opted into the newsletter
768
- */
967
+ **Svelte 5 Runes:**
968
+
969
+ ```svelte
970
+ <script>
971
+ /**
972
+ * Fired when the user submits the form
973
+ *
974
+ * @event submit
975
+ * @type {object}
976
+ * @property {string} name - The user's name
977
+ * @property {string} email - The user's email address
978
+ * @property {boolean} newsletter - Whether the user opted into the newsletter
979
+ */
769
980
 
770
- import { createEventDispatcher } from "svelte";
981
+ let { name = "Jane Doe", email = "jane@example.com", newsletter = true } = $props();
771
982
 
772
- const dispatch = createEventDispatcher();
983
+ import { createEventDispatcher } from "svelte";
773
984
 
774
- function handleSubmit() {
775
- dispatch("submit", {
776
- name: "Jane Doe",
777
- email: "jane@example.com",
778
- newsletter: true,
779
- });
780
- }
985
+ const dispatch = createEventDispatcher();
986
+
987
+ function handleSubmit() {
988
+ dispatch("submit", { name, email, newsletter });
989
+ }
990
+ </script>
991
+
992
+ <button type="button" onclick={handleSubmit}>Submit</button>
993
+ ```
994
+
995
+ **Svelte 3, 4, 5 (non-Runes):**
996
+
997
+ ```svelte
998
+ <script>
999
+ /**
1000
+ * Fired when the user submits the form
1001
+ *
1002
+ * @event submit
1003
+ * @type {object}
1004
+ * @property {string} name - The user's name
1005
+ * @property {string} email - The user's email address
1006
+ * @property {boolean} newsletter - Whether the user opted into the newsletter
1007
+ */
1008
+
1009
+ export let name = "Jane Doe";
1010
+ export let email = "jane@example.com";
1011
+ export let newsletter = true;
1012
+
1013
+ import { createEventDispatcher } from "svelte";
1014
+
1015
+ const dispatch = createEventDispatcher();
1016
+
1017
+ function handleSubmit() {
1018
+ dispatch("submit", { name, email, newsletter });
1019
+ }
1020
+ </script>
1021
+
1022
+ <button type="button" on:click={handleSubmit}>Submit</button>
781
1023
  ```
782
1024
 
783
1025
  Output:
@@ -804,30 +1046,70 @@ export default class Component extends SvelteComponentTyped<
804
1046
 
805
1047
  Just like with typedefs, you can mark event detail properties as optional using square brackets. This is useful when some properties may not always be included in the event payload.
806
1048
 
807
- Example:
1049
+ **Example:**
808
1050
 
809
- ```js
810
- /**
811
- * Snowball event fired when throwing a snowball
812
- *
813
- * @event snowball
814
- * @type {object}
815
- * @property {boolean} isPacked - Indicates whether the snowball is tightly packed
816
- * @property {number} speed - The speed of the snowball in mph
817
- * @property {string} [color] - Optional color of the snowball
818
- * @property {number} [density=0.9] - Optional density with default value
819
- */
1051
+ **Svelte 5 Runes:**
820
1052
 
821
- import { createEventDispatcher } from "svelte";
1053
+ ```svelte
1054
+ <script>
1055
+ /**
1056
+ * Snowball event fired when throwing a snowball
1057
+ *
1058
+ * @event snowball
1059
+ * @type {object}
1060
+ * @property {boolean} isPacked - Indicates whether the snowball is tightly packed
1061
+ * @property {number} speed - The speed of the snowball in mph
1062
+ * @property {string} [color] - Optional color of the snowball
1063
+ * @property {number} [density=0.9] - Optional density with default value
1064
+ */
822
1065
 
823
- const dispatch = createEventDispatcher();
1066
+ let { speed = 50 } = $props();
824
1067
 
825
- function throwSnowball() {
826
- dispatch("snowball", {
827
- isPacked: true,
828
- speed: 50,
829
- });
830
- }
1068
+ import { createEventDispatcher } from "svelte";
1069
+
1070
+ const dispatch = createEventDispatcher();
1071
+
1072
+ function throwSnowball() {
1073
+ dispatch("snowball", {
1074
+ isPacked: true,
1075
+ speed,
1076
+ });
1077
+ }
1078
+ </script>
1079
+
1080
+ <button type="button" onclick={throwSnowball}>Throw</button>
1081
+ ```
1082
+
1083
+ **Svelte 3, 4, 5 (non-Runes):**
1084
+
1085
+ ```svelte
1086
+ <script>
1087
+ /**
1088
+ * Snowball event fired when throwing a snowball
1089
+ *
1090
+ * @event snowball
1091
+ * @type {object}
1092
+ * @property {boolean} isPacked - Indicates whether the snowball is tightly packed
1093
+ * @property {number} speed - The speed of the snowball in mph
1094
+ * @property {string} [color] - Optional color of the snowball
1095
+ * @property {number} [density=0.9] - Optional density with default value
1096
+ */
1097
+
1098
+ export let speed = 50;
1099
+
1100
+ import { createEventDispatcher } from "svelte";
1101
+
1102
+ const dispatch = createEventDispatcher();
1103
+
1104
+ function throwSnowball() {
1105
+ dispatch("snowball", {
1106
+ isPacked: true,
1107
+ speed,
1108
+ });
1109
+ }
1110
+ </script>
1111
+
1112
+ <button type="button" on:click={throwSnowball}>Throw</button>
831
1113
  ```
832
1114
 
833
1115
  Output:
@@ -869,9 +1151,43 @@ When you use `setContext` in a component, `sveld` will:
869
1151
 
870
1152
  **Modal.svelte**
871
1153
 
1154
+ **Svelte 5 Runes:**
1155
+
872
1156
  ```svelte
873
1157
  <script>
874
- import { setContext } from 'svelte';
1158
+ import { setContext } from "svelte";
1159
+
1160
+ /**
1161
+ * Close the modal
1162
+ * @type {() => void}
1163
+ */
1164
+ const close = () => {
1165
+ // Close logic
1166
+ };
1167
+
1168
+ /**
1169
+ * Open the modal with content
1170
+ * @type {(component: any, props?: any) => void}
1171
+ */
1172
+ const open = (component, props) => {
1173
+ // Open logic
1174
+ };
1175
+
1176
+ setContext("simple-modal", { open, close });
1177
+
1178
+ let { children } = $props();
1179
+ </script>
1180
+
1181
+ <div class="modal">
1182
+ {@render children?.()}
1183
+ </div>
1184
+ ```
1185
+
1186
+ **Svelte 3, 4, 5 (non-Runes):**
1187
+
1188
+ ```svelte
1189
+ <script>
1190
+ import { setContext } from "svelte";
875
1191
 
876
1192
  /**
877
1193
  * Close the modal
@@ -889,7 +1205,7 @@ When you use `setContext` in a component, `sveld` will:
889
1205
  // Open logic
890
1206
  };
891
1207
 
892
- setContext('simple-modal', { open, close });
1208
+ setContext("simple-modal", { open, close });
893
1209
  </script>
894
1210
 
895
1211
  <div class="modal">
@@ -1027,7 +1343,7 @@ There are several ways to provide type information for contexts:
1027
1343
 
1028
1344
  You can use the `@restProps` tag to specify the element tags that `$$restProps` is forwarded to.
1029
1345
 
1030
- Signature:
1346
+ **Signature:**
1031
1347
 
1032
1348
  ```js
1033
1349
  /**
@@ -1039,14 +1355,35 @@ Signature:
1039
1355
  */
1040
1356
  ```
1041
1357
 
1042
- Example:
1358
+ **Example:**
1359
+
1360
+ **Svelte 5 Runes:**
1361
+
1362
+ ```svelte
1363
+ <script>
1364
+ import Button from "./Button.svelte";
1365
+
1366
+ /** @restProps {h1 | button} */
1367
+ let { edit = false, children, ...restProps } = $props();
1368
+ </script>
1369
+
1370
+ {#if edit}
1371
+ <Button {...restProps} />
1372
+ {:else}
1373
+ <h1 {...restProps}>
1374
+ {@render children?.()}
1375
+ </h1>
1376
+ {/if}
1377
+ ```
1378
+
1379
+ **Svelte 3, 4, 5 (non-Runes):**
1043
1380
 
1044
1381
  ```svelte
1045
1382
  <script>
1046
1383
  /** @restProps {h1 | button} */
1047
1384
  export let edit = false;
1048
1385
 
1049
- import Button from "../";
1386
+ import Button from "./Button.svelte";
1050
1387
  </script>
1051
1388
 
1052
1389
  {#if edit}
@@ -1062,7 +1399,7 @@ In some cases, a component may be based on another component. The `@extendProps`
1062
1399
 
1063
1400
  > **Note:** `@extends` is supported as an alias but `@extendProps` is preferred to avoid conflicts with standard JSDoc `@extends` (used for class inheritance).
1064
1401
 
1065
- Signature:
1402
+ **Signature:**
1066
1403
 
1067
1404
  ```js
1068
1405
  /**
@@ -1070,7 +1407,7 @@ Signature:
1070
1407
  */
1071
1408
  ```
1072
1409
 
1073
- Example:
1410
+ **Example:**
1074
1411
 
1075
1412
  ```js
1076
1413
  /** @extendProps {"./Button.svelte"} ButtonProps */
@@ -1093,7 +1430,7 @@ However, the `generics` attribute only works if using `lang="ts"`; the language
1093
1430
 
1094
1431
  Because `sveld` is designed to support JavaScript-only usage as a baseline, the API design to specify generics uses a custom JSDoc tag `@generics`.
1095
1432
 
1096
- Signature:
1433
+ **Signature:**
1097
1434
 
1098
1435
  ```js
1099
1436
  /**
@@ -1101,7 +1438,7 @@ Signature:
1101
1438
  */
1102
1439
  ```
1103
1440
 
1104
- Example
1441
+ **Example:**
1105
1442
 
1106
1443
  ```js
1107
1444
  /**
@@ -1109,6 +1446,54 @@ Example
1109
1446
  */
1110
1447
  ```
1111
1448
 
1449
+ **Component example:**
1450
+
1451
+ **Svelte 5 Runes:**
1452
+
1453
+ ```svelte
1454
+ <script>
1455
+ /**
1456
+ * @typedef {{ id: string | number; [key: string]: any; }} DataTableRow
1457
+ * @typedef {Exclude<keyof Row, "id">} DataTableKey<Row>
1458
+ * @typedef {{ key: DataTableKey<Row>; value: string; }} DataTableHeader<Row=DataTableRow>
1459
+ * @template {DataTableRow} <Row extends DataTableRow = DataTableRow>
1460
+ * @generics {Row extends DataTableRow = DataTableRow} Row
1461
+ */
1462
+
1463
+ let {
1464
+ /** @type {ReadonlyArray<DataTableHeader<Row>>} */
1465
+ headers = [],
1466
+ /** @type {ReadonlyArray<Row>} */
1467
+ rows = [],
1468
+ children,
1469
+ } = $props();
1470
+ </script>
1471
+
1472
+ {@render children?.({ headers, rows })}
1473
+ ```
1474
+
1475
+ **Svelte 3, 4, 5 (non-Runes):**
1476
+
1477
+ ```svelte
1478
+ <script>
1479
+ /**
1480
+ * @typedef {{ id: string | number; [key: string]: any; }} DataTableRow
1481
+ * @typedef {Exclude<keyof Row, "id">} DataTableKey<Row>
1482
+ * @typedef {{ key: DataTableKey<Row>; value: string; }} DataTableHeader<Row=DataTableRow>
1483
+ * @template {DataTableRow} <Row extends DataTableRow = DataTableRow>
1484
+ * @generics {Row extends DataTableRow = DataTableRow} Row
1485
+ */
1486
+
1487
+ /** @type {ReadonlyArray<DataTableHeader<Row>>} */
1488
+ export let headers = [];
1489
+
1490
+ /** @type {ReadonlyArray<Row>} */
1491
+ export let rows = [];
1492
+ </script>
1493
+
1494
+ <slot {headers} {rows} />
1495
+ ```
1496
+
1112
1497
  The generated TypeScript definition will resemble the following:
1113
1498
 
1114
1499
  ```ts
@@ -1150,7 +1535,27 @@ The Svelte Language Server supports component-level comments through the followi
1150
1535
 
1151
1536
  `sveld` will copy these over to the exported default component in the TypeScript definition.
1152
1537
 
1153
- Example:
1538
+ **Example:**
1539
+
1540
+ **Svelte 5 Runes:**
1541
+
1542
+ ```svelte
1543
+ <!-- @component
1544
+ @example
1545
+ <Button>
1546
+ Text
1547
+ </Button>
1548
+ -->
1549
+ <script>
1550
+ let { children } = $props();
1551
+ </script>
1552
+
1553
+ <button>
1554
+ {@render children?.()}
1555
+ </button>
1556
+ ```
1557
+
1558
+ **Svelte 3, 4, 5 (non-Runes):**
1154
1559
 
1155
1560
  ```svelte
1156
1561
  <!-- @component
@@ -1186,7 +1591,7 @@ Exported functions and consts become accessor props in generated TypeScript defi
1186
1591
 
1187
1592
  Note that `@type` tag annotations take precedence over `@param`/`@returns` tags.
1188
1593
 
1189
- Signature:
1594
+ **Signature:**
1190
1595
 
1191
1596
  ```js
1192
1597
  /**
@@ -1197,7 +1602,54 @@ Signature:
1197
1602
  */
1198
1603
  ```
1199
1604
 
1200
- Example:
1605
+ **Example:**
1606
+
1607
+ **Svelte 5 Runes:**
1608
+
1609
+ ```svelte
1610
+ <script>
1611
+ /**
1612
+ * @typedef {object} NotificationData
1613
+ * @property {string} [id] - Optional id for deduplication
1614
+ * @property {"error" | "info" | "success"} [kind]
1615
+ */
1616
+
1617
+ let { children } = $props();
1618
+
1619
+ /**
1620
+ * Add a notification to the queue.
1621
+ * @param {NotificationData} notification
1622
+ * @returns {string} The notification id
1623
+ */
1624
+ export function add(notification) {
1625
+ const id = notification.id ?? "id";
1626
+ return id;
1627
+ }
1628
+
1629
+ /**
1630
+ * Remove a notification by id.
1631
+ * @param {string} id
1632
+ * @returns {boolean} True if the notification was found and removed
1633
+ */
1634
+ export function remove(id) {
1635
+ return true;
1636
+ }
1637
+
1638
+ /**
1639
+ * Get notification count.
1640
+ * @returns {number} The number of notifications
1641
+ */
1642
+ export function getCount() {
1643
+ return 0;
1644
+ }
1645
+ </script>
1646
+
1647
+ <div>
1648
+ {@render children?.()}
1649
+ </div>
1650
+ ```
1651
+
1652
+ **Svelte 3, 4, 5 (non-Runes):**
1201
1653
 
1202
1654
  ```svelte
1203
1655
  <script>