sveld 0.37.5 → 0.37.6

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
@@ -429,11 +429,13 @@ Every diagnostic carries a stable, namespaced `code` (`"sveld/<kind>"`) alongsid
429
429
  | `sveld/context-any-type` | `warning` | Annotate the `setContext` value's declaration with `@type` or a native TypeScript type. |
430
430
  | `sveld/slot-missing-type` | `warning` | Add the required `{Type}` annotation to the `@slot`/`@snippet` tag (e.g. `@slot {{}} name`); until then it falls back to `Record<string, never>`. |
431
431
  | `sveld/event-no-source` | `warning` | Dispatch the event (`createEventDispatcher`/`dispatch`), forward it (`on:name`), or add a matching `on<Name>` callback prop; otherwise remove the stale `@event` tag. |
432
+ | `sveld/dispatch-escapes` | `warning` | The `createEventDispatcher()` result is passed to a function sveld can't follow: one that isn't imported from a local module, names an event with something other than a string literal, or passes the dispatcher on. Document the events it dispatches with `@event` tags. Add `@sveld-ignore sveld/dispatch-escapes` to the dispatcher's JSDoc once they're covered. |
432
433
  | `sveld/example-compile-error` | `error` | Fix the `@example` TS/JS code block so it type-checks, or remove the broken example. |
433
434
  | `sveld/example-syntax-error` | `error` | Fix the `@example` `svelte`/`html` markup so it parses, or remove the broken example. |
434
435
  | `sveld/syntax-skipped` | `error` | Rewrite the flagged syntax in a form sveld can model (see the diagnostic's `message` for what was skipped). |
435
436
  | `sveld/rest-props-unresolved` | `warning` | Spread `$$restProps` onto a plain element (or `svelte:element`) instead of a component, or add an `@restProps` tag to type it manually. |
436
437
  | `sveld/context-duplicate-key` | `warning` | Remove the duplicate `setContext` call, or give it a distinct key; only the first call's shape is used. |
438
+ | `sveld/context-key-unresolved` | `warning` | Use a string literal, a `const`-bound string, `Symbol()`, or a string `export const` imported from a relative module as the `setContext` key; otherwise the context is left out of every output. |
437
439
  | `sveld/spread-unresolved` | `warning` | Spread a local object literal or a variable with a resolvable type instead; otherwise the spread widens the generated type to `Record<string, any>`. |
438
440
  | `sveld/export-unresolved` | `warning` | Export a local declaration directly. Instance-script exports are props, so move a re-export (`export { x } from "..."`, or `export { x }` of an import) into `<script context="module">`, where sveld writes it to the `.d.ts` as-is. |
439
441
  | `sveld/module-export-conflict` | `warning` | Rename the module-script export. `default` is always skipped (it collides with the component itself); a name matching the generated `<Name>Props`/`<Name>Exports` type breaks the `.d.ts` if the export carries a type. |
@@ -451,7 +453,7 @@ Every diagnostic carries a stable, namespaced `code` (`"sveld/<kind>"`) alongsid
451
453
 
452
454
  #### Severity and `--strict=errors`
453
455
 
454
- Each diagnostic's `severity` is `"error"` (`example-compile-error`, `example-syntax-error`, `syntax-skipped`, `extend-props-target-missing`, `internal-typedef-referenced` — sveld emitted broken or unmodeled output) or `"warning"` (`prop-unknown-type`, `context-any-type`, `slot-missing-type`, `event-no-source`, `rest-props-unresolved`, `context-duplicate-key`, `spread-unresolved`, `export-unresolved`, `module-export-conflict`, `extend-props-duplicate`, `extend-props-override`, `jsdoc-unknown-tag`, `typedef-duplicate`, `property-duplicate`, `generics-conflict`, `event-description-ambiguous`, `jsdoc-tag-dropped`, `types-inline-unresolved` — a type fell back to `any`). Plain `strict: true` / `--strict` fails on both, unchanged from before. Pass `strict: "errors"` (or `--strict=errors`) to fail CI only on `error`-severity diagnostics, letting `any`-fallback warnings through:
456
+ Each diagnostic's `severity` is `"error"` (`example-compile-error`, `example-syntax-error`, `syntax-skipped`, `extend-props-target-missing`, `internal-typedef-referenced` — sveld emitted broken or unmodeled output) or `"warning"` (`prop-unknown-type`, `context-any-type`, `slot-missing-type`, `event-no-source`, `dispatch-escapes`, `rest-props-unresolved`, `context-duplicate-key`, `context-key-unresolved`, `spread-unresolved`, `export-unresolved`, `module-export-conflict`, `extend-props-duplicate`, `extend-props-override`, `jsdoc-unknown-tag`, `typedef-duplicate`, `property-duplicate`, `generics-conflict`, `event-description-ambiguous`, `jsdoc-tag-dropped`, `types-inline-unresolved` — a type fell back to `any`). Plain `strict: true` / `--strict` fails on both, unchanged from before. Pass `strict: "errors"` (or `--strict=errors`) to fail CI only on `error`-severity diagnostics, letting `any`-fallback warnings through:
455
457
 
456
458
  ```sh
457
459
  npx sveld --json --strict=errors
@@ -481,7 +483,7 @@ export default defineConfig({
481
483
  });
482
484
  ```
483
485
 
484
- **Inline `@sveld-ignore <code>`**, on the same JSDoc comment as the prop, `@event` tag, or context variable it applies to:
486
+ **Inline `@sveld-ignore <code>`**, on the same JSDoc comment as the prop, `@event` tag, context variable, or event dispatcher it applies to:
485
487
 
486
488
  ```svelte
487
489
  <script>
@@ -2699,6 +2701,16 @@ A description can wrap onto continuation lines, which run until the next tag. In
2699
2701
  */
2700
2702
  ```
2701
2703
 
2704
+ A `{type}` can wrap too. The text after its closing `}` and the name is the tag's own description, exactly as if the type fit on one line:
2705
+
2706
+ ```js
2707
+ /**
2708
+ * @property {"ascending"
2709
+ * | "descending"} [dir] - The next sort direction,
2710
+ * reported regardless.
2711
+ */
2712
+ ```
2713
+
2702
2714
  ### `@callback`
2703
2715
 
2704
2716
  The `@callback` tag defines a function type with `@param` and `@returns`, following the [TypeScript JSDoc `@callback` spec](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#callback). Like `@typedef`, callbacks are exported from the generated `.d.ts`.
@@ -3001,6 +3013,28 @@ In Svelte 5 runes components, callback props like `onclick` are props, not event
3001
3013
 
3002
3014
  Use `null` as the value if no event detail is provided.
3003
3015
 
3016
+ `sveld` infers events from `dispatch("name")` calls. When the dispatcher is passed to a function imported from a local module, as `helper(dispatch)` or `helper({ dispatch })`, it reads that function too and picks up the events it dispatches:
3017
+
3018
+ ```js
3019
+ // dispatch-open-close.js
3020
+ export function createOpenCloseDispatcher(dispatch) {
3021
+ return (open) => dispatch(open ? "open" : "close");
3022
+ }
3023
+ ```
3024
+
3025
+ ```svelte
3026
+ <script>
3027
+ import { createEventDispatcher } from "svelte";
3028
+ import { createOpenCloseDispatcher } from "./dispatch-open-close.js";
3029
+
3030
+ const dispatch = createEventDispatcher();
3031
+ // Types `open` and `close` as `CustomEvent<null>`
3032
+ const notifyOpenChange = createOpenCloseDispatcher(dispatch);
3033
+ </script>
3034
+ ```
3035
+
3036
+ This happens when sveld builds the whole library (CLI, `sveld()`, or the Vite plugin), not when parsing a single component on its own. Each event name there must be a string literal, or a conditional between them. The detail is typed from a literal argument and is `any` otherwise, so add an `@event` tag to type it more precisely. When sveld can't follow the dispatcher (a package import, a local function, a computed event name, or a helper that passes the dispatcher on), it reports `sveld/dispatch-escapes`, and you document those events with `@event` tags.
3037
+
3004
3038
  **Signature:**
3005
3039
 
3006
3040
  ```js
@@ -3654,12 +3688,15 @@ The key becomes the `{PascalCase}Context` type name. `sveld` can resolve:
3654
3688
  | Static template literal | `` setContext(`simple-modal`, …) `` | `SimpleModalContext` |
3655
3689
  | `const`-bound string | `const KEY = "simple-modal";`<br>`setContext(KEY, …)` | `SimpleModalContext` |
3656
3690
  | `Symbol()` / `Symbol.for()` | `setContext(Symbol("tabs"), …)` | `TabsContext` |
3691
+ | Imported `export const` string | `import { KEY } from "./keys.js";`<br>`setContext(KEY, …)` | `SimpleModalContext` |
3657
3692
 
3658
3693
  `const` identifiers are followed up to 5 levels deep (`const A = "x"; const B = A;`). Only `const` bindings count. `let`, `var`, and props are skipped because they can change at runtime.
3659
3694
 
3660
3695
  Symbol keys take their name from the description: `Symbol("tabs")` and `Symbol.for("tabs")` both become `TabsContext`. For `const ModalKey = Symbol()` with no description, the binding name wins: `ModalKeyContext`.
3661
3696
 
3662
- Anything else (dynamic identifiers, template interpolation, other function calls) logs a warning. No context type is generated.
3697
+ An imported key is read from its module, following re-exports, when that module is a relative `.js`/`.ts` file declaring it as `export const KEY = "simple-modal"` (or a static template literal). This works when sveld builds a whole library (CLI, `sveld()`, or the Vite plugin), not when parsing a single component on its own.
3698
+
3699
+ Anything else (dynamic identifiers, `let` exports, template interpolation, other function calls) records a `sveld/context-key-unresolved` diagnostic. No context type is generated.
3663
3700
 
3664
3701
  #### Example
3665
3702
 
@@ -3839,7 +3876,7 @@ There are several ways to type contexts:
3839
3876
 
3840
3877
  #### Notes
3841
3878
 
3842
- - Context keys must be statically resolvable: a string literal, a static template literal, a `const`-bound string, or a `Symbol()` / `Symbol.for()` call with a static description. Dynamic expressions (runtime identifiers, template interpolation, other function calls) are skipped with a warning.
3879
+ - Context keys must be statically resolvable: a string literal, a static template literal, a `const`-bound string (local or an imported `export const`), or a `Symbol()` / `Symbol.for()` call with a static description. Dynamic expressions (runtime identifiers, template interpolation, other function calls) are skipped with a `sveld/context-key-unresolved` diagnostic.
3843
3880
  - Variables passed to `setContext` should have JSDoc `@type` annotations for accurate types
3844
3881
  - The generated type name follows the pattern: `{PascalCase}Context`. Separators (hyphens, underscores, dots, colons, slashes, spaces) are stripped and each segment is capitalized:
3845
3882
  | Context Key | Generated Type Name |
package/lib/browser.d.ts CHANGED
@@ -53,6 +53,23 @@ interface PendingContextKeyCandidate {
53
53
  source?: SourceRange;
54
54
  }
55
55
 
56
+ interface PendingDispatchEscapeCandidate {
57
+ importSource: string;
58
+ importedName: string;
59
+ /** Callee as written, for messages. */
60
+ calleeText: string;
61
+ /** Local name of the `createEventDispatcher()` result. */
62
+ dispatcherName: string;
63
+ /** Argument position the dispatcher is passed at. */
64
+ argumentIndex: number;
65
+ /** Set when passed as an object literal property (`{ dispatch }`): that property's key. */
66
+ property?: string;
67
+ /** Source range of the call, when available. */
68
+ source?: SourceRange;
69
+ /** `@sveld-ignore sveld/dispatch-escapes` on the dispatcher's declaration. */
70
+ ignored?: boolean;
71
+ }
72
+
56
73
  interface ParsedComponentTypeScriptMetadata {
57
74
  canonicalPropsType?: string;
58
75
  canonicalPropNames: string[];
@@ -71,6 +88,13 @@ interface ParsedComponentTypeScriptMetadata {
71
88
  pendingConstDefaultCandidates?: PendingConstDefaultCandidate[];
72
89
  /** Unresolved `setContext` import keys for the cross-file pass in `generateBundle`. */
73
90
  pendingContextKeyCandidates?: PendingContextKeyCandidate[];
91
+ /** Dispatchers passed to imported functions, for the cross-file pass in `generateBundle`. */
92
+ pendingDispatchEscapeCandidates?: PendingDispatchEscapeCandidate[];
93
+ /**
94
+ * `event-no-source` diagnostics held back while the dispatcher escapes to
95
+ * imported functions: `generateBundle` keeps those the functions don't dispatch.
96
+ */
97
+ deferredEventNoSourceDiagnostics?: SveldDiagnostic[];
74
98
  }
75
99
 
76
100
  type SyntaxMode = "legacy" | "runes";
@@ -515,7 +539,7 @@ export class ComponentParser {
515
539
  parseSvelteComponent(source: string, diagnostics: ComponentParserDiagnostics): ParsedComponent;
516
540
  }
517
541
 
518
- export type SveldDiagnosticKind = "prop-unknown-type" | "context-any-type" | "slot-missing-type" | "event-no-source" | "example-compile-error" | "example-syntax-error" | "syntax-skipped" | "rest-props-unresolved" | "context-duplicate-key" | "spread-unresolved" | "export-unresolved" | "module-export-conflict" | "extend-props-target-missing" | "extend-props-duplicate" | "extend-props-override" | "jsdoc-unknown-tag" | "typedef-duplicate" | "property-duplicate" | "generics-conflict" | "event-description-ambiguous" | "jsdoc-tag-dropped" | "internal-typedef-referenced" | "types-inline-unresolved";
542
+ export type SveldDiagnosticKind = "prop-unknown-type" | "context-any-type" | "slot-missing-type" | "event-no-source" | "dispatch-escapes" | "example-compile-error" | "example-syntax-error" | "syntax-skipped" | "rest-props-unresolved" | "context-duplicate-key" | "context-key-unresolved" | "spread-unresolved" | "export-unresolved" | "module-export-conflict" | "extend-props-target-missing" | "extend-props-duplicate" | "extend-props-override" | "jsdoc-unknown-tag" | "typedef-duplicate" | "property-duplicate" | "generics-conflict" | "event-description-ambiguous" | "jsdoc-tag-dropped" | "internal-typedef-referenced" | "types-inline-unresolved";
519
543
 
520
544
  type SveldDiagnosticSeverity = "error" | "warning";
521
545