sveld 0.37.2 → 0.37.3

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
@@ -302,7 +302,7 @@ Without `resolveTypes`, JSON lists no props. With it, each field shows up with `
302
302
 
303
303
  ### Persistent parse cache (`cache`)
304
304
 
305
- Parsed output is written to disk and reused when the source file has not changed, on by default. That applies across runs, including CI on a fresh checkout.
305
+ Parsed output is written to disk and reused when the source file has not changed, on by default. That applies across runs, including CI on a fresh checkout. Generated `.d.ts` text is cached the same way, but is only reused when the component source _and_ every `typesOptions` value that affects output (for example [`format`](#dts-output-format-typesoptionsformat)) are unchanged; changing any of them regenerates that component's `.d.ts` without invalidating its cached parse.
306
306
 
307
307
  ```ts
308
308
  await sveld({ json: true });
@@ -445,10 +445,11 @@ Every diagnostic carries a stable, namespaced `code` (`"sveld/<kind>"`) alongsid
445
445
  | `sveld/generics-conflict` | `warning` | Rename one of the `@generics`/`@template` declarations to a distinct generic name. |
446
446
  | `sveld/jsdoc-tag-dropped` | `warning` | Move the tag next to a `@slot`/`@snippet`/`@event`/`@typedef`/`@callback` tag in the same comment block so it has something to attach to. |
447
447
  | `sveld/internal-typedef-referenced` | `error` | Remove `@internal`/`@ignore` from the referenced typedef, or stop referencing it from public type text (inline the shape, or make the referencing item `@internal` too). |
448
+ | `sveld/types-inline-unresolved` | `warning` | The import is kept as-is. Point it at a relative `.ts` file that exports a `type`/`interface`, or rename the colliding type. |
448
449
 
449
450
  #### Severity and `--strict=errors`
450
451
 
451
- 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`, `extend-props-duplicate`, `extend-props-override`, `jsdoc-unknown-tag`, `typedef-duplicate`, `property-duplicate`, `generics-conflict`, `jsdoc-tag-dropped` — 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:
452
+ 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`, `extend-props-duplicate`, `extend-props-override`, `jsdoc-unknown-tag`, `typedef-duplicate`, `property-duplicate`, `generics-conflict`, `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:
452
453
 
453
454
  ```sh
454
455
  npx sveld --json --strict=errors
@@ -505,8 +506,9 @@ A bare `@sveld-ignore` (no code) suppresses every diagnostic for that symbol.
505
506
 
506
507
  - Node 22+ if your config is `sveld.config.js` or `sveld.config.mjs` — `sveld` declares no `engines.node` floor. A `sveld.config.ts` file loads via a raw `import()`, with no transpile step, so it only works if the runtime strips TypeScript itself: on Node this needs unflagged type stripping (Node 22.18 / 23.6+; on older Node 22 patches it's behind `--experimental-strip-types`, so use a `.js`/`.mjs` config instead), which is why Node 24 — the first current major where type stripping is always on — is the only reason to require it. Bun (see `.bun-version`) loads `.ts` configs without Node at all. CI runs on Bun and does not pin a Node version; the release workflow pins Node 24 for `actions/setup-node` on the npm publish job only, not a requirement for consumers.
507
508
  - `sveld` is ESM-only. `require("sveld")` does not work — use `import` or dynamic `import()`.
509
+ - The [persistent parse cache](#persistent-parse-cache-cache) hashes source with `node:crypto`'s one-shot `hash()`, which needs Node 22 (or Bun).
508
510
  - `sveld` bundles its own template parser to parse `.svelte` files, kept in parity with `svelte/compiler` (see [Approach](#approach)). Parsing does not depend on the Svelte version installed in your project, so Svelte 3 and Svelte 4 codebases parse the same way Svelte 5 codebases do — there is no compiler version to match up.
509
- - [`resolveTypes`](#opt-in-semantic-resolution-resolvetypes) and [`checkExamples`](#compile-checked-example-blocks-checkexamples) are optional and need `typescript` 7 or later (which provides `typescript/unstable/async`) plus a `tsconfig.json`. Everything else, including `.d.ts` generation, is AST-only and never loads TypeScript. If either is enabled and TypeScript can't be started (missing, too old, or no `tsconfig.json`), the run fails loudly: `sveld()` throws and the CLI exits `2` naming the requirement, rather than silently skipping the check.
511
+ - [`resolveTypes`](#opt-in-semantic-resolution-resolvetypes), [`checkExamples`](#compile-checked-example-blocks-checkexamples), and [`typesOptions.inline: "all"`](#typesoptionsinline) are optional and need `typescript` 7 or later (which provides `typescript/unstable/async`) plus a `tsconfig.json`. Everything else, including `.d.ts` generation and `typesOptions.inline: "local"`, is AST-only and never loads TypeScript. If any of the three is enabled and TypeScript can't be started (missing, too old, or no `tsconfig.json`), the run fails loudly: `sveld()` throws and the CLI exits `2` naming the requirement, rather than silently skipping the check. For `typesOptions.inline: "all"` this only actually triggers when there's a non-allow-listed bare import somewhere in the bundle to resolve; see [`typesOptions.inline`](#typesoptionsinline).
510
512
 
511
513
  ## Usage
512
514
 
@@ -592,7 +594,7 @@ npx sveld --json --markdown
592
594
 
593
595
  If no entry point can be resolved (no `package.json#svelte` field and no `--entry`), the CLI exits `1` and prints the reason to `stderr`. If `src/index.js` happens to exist relative to your working directory, sveld falls back to it and prints a one-line note asking you to set `package.json#svelte` (or `--entry`) instead of relying on the fallback.
594
596
 
595
- Flags are kebab-case: `--entry`, `--glob`, `--types`, `--json`, `--markdown`, `--custom-elements`, `--llms`, `--fail-fast`, `--dry-run`, `--cache`, `--resolve-types`, `--check-examples`, `--report-diagnostics`, `--strict`, `--check`, `--check-level`, `--types-format`, `--quiet`, `--stdout`, `--format`. The camelCase spellings `--resolveTypes` and `--checkExamples` still work as deprecated aliases for compatibility with existing scripts. `--entry`, `--cache`, `--check`, and `--types-format` take their value either as `--flag=value` or as a separate `--flag value` argument (`sveld --entry src/index.js` and `sveld --entry=src/index.js` are equivalent); if the next argument starts with `--` it's not consumed as a value, so `--cache` and `--check` fall back to their default location and `--entry` and `--types-format` report a usage error naming the flag. Boolean flags (`--json`, `--glob`, `--strict`, and the like) never consume a following argument. An unrecognized flag (e.g. `--markdwon`) prints `Unknown flag: --markdwon` to `stderr`, exits `1`, and skips generation; when a close match exists it appends a suggestion, e.g. `Unknown flag: --markdwon Did you mean --markdown?`. sveld takes no positional arguments, so any non-flag argument errors the same way.
597
+ Flags are kebab-case: `--entry`, `--glob`, `--types`, `--json`, `--markdown`, `--custom-elements`, `--llms`, `--fail-fast`, `--dry-run`, `--cache`, `--resolve-types`, `--check-examples`, `--report-diagnostics`, `--strict`, `--check`, `--check-level`, `--types-format`, `--types-export`, `--types-comments`, `--types-inline`, `--types-index-types`, `--types-props-declaration`, `--quiet`, `--stdout`, `--format`. The camelCase spellings `--resolveTypes` and `--checkExamples` still work as deprecated aliases for compatibility with existing scripts. `--entry`, `--cache`, `--check`, `--types-format`, `--types-export`, `--types-comments`, `--types-inline`, and `--types-props-declaration` take their value either as `--flag=value` or as a separate `--flag value` argument (`sveld --entry src/index.js` and `sveld --entry=src/index.js` are equivalent); if the next argument starts with `--` it's not consumed as a value, so `--cache` and `--check` fall back to their default location and the rest of that list report a usage error naming the flag. Boolean flags (`--json`, `--glob`, `--strict`, `--types-index-types`, and the like) never consume a following argument. An unrecognized flag (e.g. `--markdwon`) prints `Unknown flag: --markdwon` to `stderr`, exits `1`, and skips generation; when a close match exists it appends a suggestion, e.g. `Unknown flag: --markdwon Did you mean --markdown?`. sveld takes no positional arguments, so any non-flag argument errors the same way.
596
598
 
597
599
  Writer progress lines (`created "..."` / `unchanged "..."`) print to `stderr`, keeping `stdout` reserved for machine-readable data. Pass `--quiet` (or `quiet: true` in `sveld.config.*`) to suppress them; it does not suppress error messages, the diagnostics summary (`--report-diagnostics` / `--strict`), or the `--check` report.
598
600
 
@@ -923,6 +925,13 @@ The `svelte` condition lets bundlers that understand it (Vite, Rollup, webpack v
923
925
  - **`outDir`** (string, optional, default: `"types"`): Output directory for generated `.d.ts` files, relative to the project root.
924
926
  - **`preamble`** (string, optional, default: `""`): Raw text prepended to the top of the generated `index.d.ts` barrel file, before the `export * from "./..."` lines. Useful for license headers or lint-disable comments. See [`typesOptions.preamble`](#typesoptionspreamble) below.
925
927
  - **`format`** (`"class"` | `"component"`, optional, default: `"class"`): `.d.ts` output shape. `"class"` extends `SvelteComponentTyped`; `"component"` emits the Svelte 5 `Component` type. Also available as `--types-format`. See [`.d.ts` output format](#dts-output-format-typesoptionsformat).
928
+ - **`exportTypes`** (`boolean | { props?, exports?, typedefs?, contexts? }`, optional, default: `true`): Which generated type declarations get an `export` keyword. `false` keeps them all local to the `.d.ts` file; an object picks per kind. Also available as `--types-export=<all|none>` (the CLI can only set the boolean form, not the per-kind object). See [`typesOptions.exportTypes`](#typesoptionsexporttypes).
929
+ - **`typeNames`** (`{ props?: string; exports?: string }`, optional, default: `{ props: "{name}Props", exports: "{name}Exports" }`): Templates for generated type names. No CLI flag; config file or `sveld()` only. See [`typesOptions.typeNames`](#typesoptionstypenames).
930
+ - **`comments`** (`"all" | "descriptions" | "none"`, optional, default: `"all"`): How much JSDoc lands in the generated `.d.ts`. `"all"` keeps descriptions, `@deprecated`, `@default`, and passthrough tags (`@since`, `@see`, `@example`, `@link`); `"descriptions"` keeps descriptions and `@deprecated` only; `"none"` emits no comments at all. Also available as `--types-comments=<all|descriptions|none>`. See [`typesOptions.comments`](#typesoptionscomments).
931
+ - **`propsDeclaration`** (`"type" | "interface"`, optional, default: `"type"`): `"interface"` emits the props type as an `interface` instead of a `type` alias when the props are a plain object. Also available as `--types-props-declaration=<type|interface>`. See [`typesOptions.propsDeclaration`](#typesoptionspropsdeclaration).
932
+ - **`transform`** (function, optional): Post-processes each generated file's text before it is written. Runs after the generated-text cache, so it applies on every run. No CLI flag; config file or `sveld()` only. See [`typesOptions.transform`](#typesoptionstransform).
933
+ - **`indexTypes`** (`boolean | { props?, exports?, typedefs?, contexts? }`, optional, default: `false`): Also re-export generated types from `index.d.ts`. `true` re-exports each component's `Props` type (and `Exports` under `format: "component"`); an object can additionally include typedefs and contexts. Also available as `--types-index-types` (the CLI can only set the boolean form, not the per-kind object). See [`typesOptions.indexTypes`](#typesoptionsindextypes).
934
+ - **`inline`** (`false | "local" | "all"`, optional, default: `false`): Copies `type`/`interface` declarations imported from a relative source (or a tsconfig/jsconfig path alias) directly into the `.d.ts`, dropping the import. Also available as `--types-inline=<local|all>` (`--types-inline=false` resets to the default). See [`typesOptions.inline`](#typesoptionsinline).
926
935
  - **`json`** (boolean, optional): Generate component documentation in JSON format.
927
936
  - **`jsonOptions`** (object, optional): Options for JSON output.
928
937
  - **`outFile`** (string, optional, default: `"COMPONENT_API.json"`): Path (relative to the project root) for the single combined JSON document. Ignored when `outDir` is set.
@@ -990,6 +999,390 @@ export { default as Button } from "./Button.svelte";
990
999
 
991
1000
  `preamble` only affects the barrel file (`index.d.ts`); per-component `.d.ts` files are untouched.
992
1001
 
1002
+ #### `typesOptions.exportTypes`
1003
+
1004
+ Every type sveld generates is `export`ed by default. `typesOptions.exportTypes` lets a library keep some or all of them local to each component's `.d.ts` — useful when the props type is an implementation detail, or when it collides with a hand-written type of the same name in the package's own `index.d.ts`. Only the component itself needs to be public either way.
1005
+
1006
+ ```js
1007
+ sveld({
1008
+ types: true,
1009
+ typesOptions: {
1010
+ exportTypes: false,
1011
+ },
1012
+ });
1013
+ ```
1014
+
1015
+ **Button.svelte.d.ts** before:
1016
+
1017
+ ```ts
1018
+ export type ButtonProps = { label?: string };
1019
+
1020
+ export default class Button extends SvelteComponentTyped<
1021
+ ButtonProps,
1022
+ { click: WindowEventMap["click"] },
1023
+ { default: Record<string, never> }
1024
+ > {}
1025
+ ```
1026
+
1027
+ **Button.svelte.d.ts** after (`exportTypes: false`):
1028
+
1029
+ ```ts
1030
+ type ButtonProps = { label?: string };
1031
+
1032
+ export default class Button extends SvelteComponentTyped<
1033
+ ButtonProps,
1034
+ { click: WindowEventMap["click"] },
1035
+ { default: Record<string, never> }
1036
+ > {}
1037
+ ```
1038
+
1039
+ `Button` is still the only thing a consumer imports; `ButtonProps` is just no longer part of the public surface (it can still be referenced structurally — through `ComponentProps<typeof Button>`, for instance).
1040
+
1041
+ Pass an object instead of a boolean to pick per kind:
1042
+
1043
+ ```js
1044
+ typesOptions: {
1045
+ exportTypes: {
1046
+ props: false, // export type <Name>Props
1047
+ exports: true, // export type <Name>Exports (format: "component" only)
1048
+ typedefs: true, // export interface|type <Typedef> from @typedef
1049
+ contexts: true, // export type <Context> from setContext
1050
+ },
1051
+ }
1052
+ ```
1053
+
1054
+ Any key left out defaults to `true`.
1055
+
1056
+ `<script context="module">` exports (`export declare const` / `export declare function`) are real runtime exports and are always emitted as exports, regardless of `exportTypes`.
1057
+
1058
+ One exception: when a bundled component uses [`@extendProps`](#extendprops) to extend another bundled component, sveld emits `import type { ButtonProps } from "./Button.svelte"` in the extending component's `.d.ts`. If `Button`'s props type stopped being exported, that import would break — so sveld always keeps a component's props type exported when another component in the same run extends it, even under `exportTypes: false`.
1059
+
1060
+ Also available as `--types-export=<all|none>` on the CLI, mapped to the boolean form (`all` => `true`, `none` => `false`) — the CLI can't express the per-kind object form.
1061
+
1062
+ #### `typesOptions.typeNames`
1063
+
1064
+ By default, sveld names the generated props type `<Name>Props` and (for [`format: "component"`](#dts-output-format-typesoptionsformat)) the exports type `<Name>Exports`, where `<Name>` is the component's module name. `typesOptions.typeNames` lets a library follow its own naming convention instead, via a `{name}` placeholder template.
1065
+
1066
+ ```js
1067
+ sveld({
1068
+ types: true,
1069
+ typesOptions: {
1070
+ typeNames: { props: "I{name}Props" },
1071
+ },
1072
+ });
1073
+ ```
1074
+
1075
+ **Button.svelte.d.ts** before / after:
1076
+
1077
+ ```diff
1078
+ - export type ButtonProps = { label?: string };
1079
+ + export type IButtonProps = { label?: string };
1080
+
1081
+ - export default class Button extends SvelteComponentTyped<ButtonProps, ...> {}
1082
+ + export default class Button extends SvelteComponentTyped<IButtonProps, ...> {}
1083
+ ```
1084
+
1085
+ Each template must contain `{name}` and produce a valid identifier once substituted; sveld throws otherwise. Any key left out of the object keeps its default (`"{name}Props"` / `"{name}Exports"`).
1086
+
1087
+ If a bundled component then [`@extendProps`](#extendprops)/`@extends`-es another one, the tag must name the *templated* interface — `@extendProps {"./Button.svelte"} IButtonProps`, not `ButtonProps` — or sveld reports [`extend-props-target-missing`](#type-inference-diagnostics).
1088
+
1089
+ There's no CLI flag for `typeNames`; set it via a config file or the programmatic `sveld()` API.
1090
+
1091
+ #### `typesOptions.comments`
1092
+
1093
+ By default sveld carries every prop's JSDoc into the generated `.d.ts`: the description, `@deprecated`, `@default`, and passthrough tags like `@since`. `typesOptions.comments` lets a library ship a leaner `.d.ts` instead — useful when the docs live elsewhere (a docs site, Storybook) and duplicating them in the type declaration just adds noise.
1094
+
1095
+ ```svelte
1096
+ <script>
1097
+ /**
1098
+ * The button's visible label.
1099
+ * @deprecated Use `text` instead.
1100
+ * @default ""
1101
+ * @since 1.2.0
1102
+ */
1103
+ export let label = "";
1104
+ </script>
1105
+ ```
1106
+
1107
+ **`comments: "all"`** (default) — description, `@deprecated`, `@default`, and passthrough tags:
1108
+
1109
+ ```ts
1110
+ /**
1111
+ * The button's visible label.
1112
+ * @deprecated Use `text` instead.
1113
+ * @default ""
1114
+ * @since 1.2.0
1115
+ */
1116
+ label?: string;
1117
+ ```
1118
+
1119
+ **`comments: "descriptions"`** — description and `@deprecated` only; `@default` and passthrough tags are dropped:
1120
+
1121
+ ```ts
1122
+ /**
1123
+ * The button's visible label.
1124
+ * @deprecated Use `text` instead.
1125
+ */
1126
+ label?: string;
1127
+ ```
1128
+
1129
+ `@deprecated` survives at this level on purpose: editors render a strikethrough from it, so it's API information a consumer needs, not documentation.
1130
+
1131
+ **`comments: "none"`** — no comments at all; only the declaration:
1132
+
1133
+ ```ts
1134
+ label?: string;
1135
+ ```
1136
+
1137
+ ```js
1138
+ sveld({
1139
+ types: true,
1140
+ typesOptions: {
1141
+ comments: "descriptions",
1142
+ },
1143
+ });
1144
+ ```
1145
+
1146
+ `@internal`/`@ignore` members are removed from every output regardless of this option — see [`@ignore` / `@internal`](#ignore--internal). `comments` only controls how much JSDoc survives for members that *do* get emitted.
1147
+
1148
+ Also available as `--types-comments=<all|descriptions|none>` on the CLI.
1149
+
1150
+ #### `typesOptions.propsDeclaration`
1151
+
1152
+ By default sveld emits the props type as a `type` alias. `typesOptions.propsDeclaration: "interface"` emits an `interface` instead, for consumers who want to extend it with `interface MyProps extends ButtonProps`, rely on declaration merging, or just prefer the shorter editor hover an `interface` gets.
1153
+
1154
+ ```js
1155
+ sveld({
1156
+ types: true,
1157
+ typesOptions: {
1158
+ propsDeclaration: "interface",
1159
+ },
1160
+ });
1161
+ ```
1162
+
1163
+ **Button.svelte.d.ts** before / after:
1164
+
1165
+ ```diff
1166
+ - export type ButtonProps = { label?: string };
1167
+ + export interface ButtonProps { label?: string }
1168
+ ```
1169
+
1170
+ Only a plain object props type can become an `interface` safely. These shapes are intersections instead, and always stay a `type` alias regardless of this option:
1171
+
1172
+ - a component with [`@restProps`](#restprops) (`Omit<$RestProps, keyof $Props> & $Props`)
1173
+ - a component with [`@extendProps`](#extendprops) (`Omit<Extended, keyof $Props> & $Props`)
1174
+ - a component with a whole-object `$props()` type resolved via [`resolveTypes`](#opt-in-semantic-resolution-resolvetypes) (`CanonicalPropsType & { ... }`)
1175
+ - a component with no props at all (`Record<string, never>`; an empty `interface` trips `noEmptyInterface` in consumer lint setups)
1176
+
1177
+ Also available as `--types-props-declaration=<type|interface>` on the CLI.
1178
+
1179
+ #### `typesOptions.transform`
1180
+
1181
+ `typesOptions.transform` is an escape hatch for one-off edits no other option covers - appending a `/// <reference>` directive, rewriting an import specifier for a monorepo, stripping a banner. It runs on every generated `.d.ts` file's text, immediately before it's written, and receives the block's `kind` (`"component"` or `"index"`), and for `"component"` the parsed `ComponentDocApi` too, plus the output `filePath` relative to `outDir` (e.g. `"Button.svelte.d.ts"` or `"index.d.ts"`). Return the new text, synchronously or via a `Promise`.
1182
+
1183
+ ```js
1184
+ sveld({
1185
+ types: true,
1186
+ typesOptions: {
1187
+ transform: (text, context) => {
1188
+ if (context.kind !== "component") return text;
1189
+ return `/// <reference types="svelte" />\n${text}`;
1190
+ },
1191
+ },
1192
+ });
1193
+ ```
1194
+
1195
+ ```ts
1196
+ // Button.svelte.d.ts
1197
+ /// <reference types="svelte" />
1198
+ export type ButtonProps = { label?: string };
1199
+ // ...
1200
+ ```
1201
+
1202
+ `transform` runs after the [persistent parse cache](#persistent-parse-cache-cache)'s generated-text cache, so a changed transform is never served stale cached output - it always re-applies, even when the underlying `.d.ts` text was reused from a previous run. A transform that throws, or resolves to something other than a string, fails the whole `sveld` run with an error naming the file that failed.
1203
+
1204
+ There's no CLI flag for `transform`; set it via a config file or the programmatic `sveld()` API.
1205
+
1206
+ #### `typesOptions.indexTypes`
1207
+
1208
+ By default, `types/index.d.ts` only re-exports components:
1209
+
1210
+ ```ts
1211
+ export { default as Button } from "./Button.svelte";
1212
+ ```
1213
+
1214
+ A consumer who wants `ButtonProps` has to deep-import `my-lib/types/Button.svelte`. `typesOptions.indexTypes` re-exports the generated types from the barrel instead, so `import type { ButtonProps } from "my-lib"` works directly.
1215
+
1216
+ ```js
1217
+ sveld({
1218
+ types: true,
1219
+ typesOptions: {
1220
+ indexTypes: true,
1221
+ },
1222
+ });
1223
+ ```
1224
+
1225
+ ```ts
1226
+ // types/index.d.ts
1227
+ export { default as Button } from "./Button.svelte";
1228
+
1229
+ export type { ButtonProps } from "./Button.svelte";
1230
+ ```
1231
+
1232
+ ```ts
1233
+ import type { ButtonProps } from "my-lib";
1234
+ ```
1235
+
1236
+ `true` is shorthand for `{ props: true, exports: true }`: it re-exports each component's `Props` type, and (under [`format: "component"`](#dts-output-format-typesoptionsformat)) its `Exports` type. Pass an object to also re-export typedefs and/or contexts:
1237
+
1238
+ ```js
1239
+ typesOptions: {
1240
+ indexTypes: { props: true, typedefs: true, contexts: true },
1241
+ }
1242
+ ```
1243
+
1244
+ `Props`/`Exports` names are unique per component by construction (they're derived from the module name), so they're always safe to re-export together. Typedef and context names are user-authored and can collide across components - when two components generate the same name, the first one in barrel order wins and every later collision is dropped with a one-line warning to `stderr`:
1245
+
1246
+ ```
1247
+ sveld: index.d.ts skips duplicate type export "TabsContext" from "./Tabs2.svelte" (already exported from "./Tabs.svelte").
1248
+ ```
1249
+
1250
+ `indexTypes` composes with [`exportTypes`](#typesoptionsexporttypes): a type that `exportTypes` keeps local to its component's `.d.ts` is never re-exported from the barrel either, since it wouldn't resolve.
1251
+
1252
+ Also available as `--types-index-types` on the CLI, mapped to the boolean form (`true` re-exports `Props`/`Exports` only) — the CLI can't express the per-kind object form. Pass `--types-index-types=false` to disable it explicitly.
1253
+
1254
+ #### `typesOptions.inline`
1255
+
1256
+ A TypeScript component that imports a type keeps that import in its `.d.ts`:
1257
+
1258
+ ```svelte
1259
+ <script lang="ts">
1260
+ import type { Size } from "./types";
1261
+ let { size }: { size: Size } = $props();
1262
+ </script>
1263
+ ```
1264
+
1265
+ ```ts
1266
+ import type { Component } from "svelte";
1267
+ import type { Size } from "./types";
1268
+
1269
+ type $Props = { size: Size };
1270
+ export type MyComponentProps = $Props;
1271
+ ```
1272
+
1273
+ Publishers who don't ship their `.svelte` sources need `.d.ts` files without relative imports:
1274
+ `./types.ts` might sit outside the published `types/` output directory, or the package might be
1275
+ bundled to a single file. `typesOptions.inline: "local"` copies the imported declaration into
1276
+ the `.d.ts` instead of importing it:
1277
+
1278
+ ```js
1279
+ sveld({
1280
+ types: true,
1281
+ typesOptions: {
1282
+ inline: "local",
1283
+ },
1284
+ });
1285
+ ```
1286
+
1287
+ ```ts
1288
+ import type { Component } from "svelte";
1289
+
1290
+ type Size = "sm" | "md" | "lg";
1291
+
1292
+ type $Props = { size: Size };
1293
+ export type MyComponentProps = $Props;
1294
+ ```
1295
+
1296
+ `"local"` follows:
1297
+
1298
+ - relative sources (`./types`) and tsconfig/jsconfig `paths` aliases;
1299
+ - re-exports (`export { X as Y } from "./z"` and `export * from "./z"`);
1300
+ - same-file dependencies (a copied type that itself references another type or interface
1301
+ declared in the same file copies that one too, before the type that references it).
1302
+
1303
+ It leaves alone:
1304
+
1305
+ - bare/package imports (`import type { CSSProperties } from "some-package"`) and `.svelte`
1306
+ sources — `"all"` covers bare imports, see below;
1307
+ - `@extendProps`/`@extends` imports and `import("./x")` inline import types inside typedefs,
1308
+ which are unrelated mechanisms;
1309
+ - `enum`, `class`, and `function` exports, which can't be safely copied as a `type`/`interface`.
1310
+
1311
+ An import that can't be safely inlined (a missing file, a missing export, one of the unsupported
1312
+ export kinds above, or a name collision with something the component already declares — a
1313
+ typedef, a context type, a local `type`/`interface`, or its `Props`/`Exports` type name) is left
1314
+ as an import, with a [`types-inline-unresolved`](#diagnostic-codes) warning explaining why. If an
1315
+ `import type { A, B } from "./x"` statement imports several names and even one of them can't be
1316
+ inlined, the whole statement is kept and nothing from it is inlined — simpler, and always correct.
1317
+
1318
+ **`"all"`** additionally inlines bare/package imports:
1319
+
1320
+ ```svelte
1321
+ <script lang="ts">
1322
+ import type { HTMLButtonAttributes } from "svelte/elements";
1323
+ import type { Alignment } from "some-design-system";
1324
+ let { rest, align }: { rest: HTMLButtonAttributes; align: Alignment } = $props();
1325
+ </script>
1326
+ ```
1327
+
1328
+ ```js
1329
+ sveld({
1330
+ types: true,
1331
+ typesOptions: {
1332
+ inline: "all",
1333
+ },
1334
+ });
1335
+ ```
1336
+
1337
+ ```ts
1338
+ import type { Component } from "svelte";
1339
+ import type { HTMLButtonAttributes } from "svelte/elements";
1340
+
1341
+ type Alignment = "start" | "center" | "end";
1342
+
1343
+ type $Props = { rest: HTMLButtonAttributes; align: Alignment };
1344
+ export type MyComponentProps = $Props;
1345
+ ```
1346
+
1347
+ `some-design-system`'s `Alignment` got copied in, exactly like a `"local"` relative import would.
1348
+ `svelte`/`svelte/elements` did not, even though `HTMLButtonAttributes` is itself a bare import a
1349
+ real TypeScript checker could resolve and copy just as easily: those two specifiers are a
1350
+ hard-coded allow-list and always stay imports under `"all"`, regardless of what they resolve to.
1351
+ Copying framework types would freeze whatever Svelte version happened to be installed at
1352
+ generation time into every consumer's `.d.ts` output, which defeats the point of importing them
1353
+ from `svelte` in the first place.
1354
+
1355
+ Resolving a bare specifier to a file needs a real module resolver, so `"all"` uses the actual
1356
+ TypeScript checker (the same one behind [`resolveTypes`](#opt-in-semantic-resolution-resolvetypes)
1357
+ and [`checkExamples`](#compile-checked-example-blocks-checkexamples), shared across all three when
1358
+ more than one is enabled) rather than sveld's own AST-only pass. This makes `"all"` a **hard
1359
+ requirement** on `typescript` 7+ and a resolvable `tsconfig.json` — same contract `resolveTypes`
1360
+ already has (see [Requirements](#requirements)): if TypeScript can't be started, the run fails
1361
+ loudly rather than silently falling back to `"local"` behavior. The requirement only actually
1362
+ kicks in when there's at least one non-allow-listed bare import somewhere in the bundle to
1363
+ resolve; an `"all"` run with nothing bare to inline never loads TypeScript at all.
1364
+
1365
+ A reference inside a copied bare declaration is chased exactly like a local one: a same-file
1366
+ helper type is copied and recursed into, a relative/aliased import is followed the same way
1367
+ `"local"` already does, and a further bare import goes back through the checker (again skipping
1368
+ the svelte/svelte-elements allow-list). A reference that resolves to a TypeScript default-lib file
1369
+ (e.g. `Element`, `EventTarget` from `lib.dom.d.ts`) is left alone — it's already global and needs
1370
+ no import — the same way a local pass leaves an unimported name alone.
1371
+
1372
+ `@extendProps`/`@extends` inlining (replacing `import type { ButtonProps } from "./Button.svelte"`
1373
+ with a copy of `Button`'s own generated declaration) is out of scope for both `"local"` and
1374
+ `"all"`; that's a distinct, deferred mechanism, not covered here.
1375
+
1376
+ Copying from a large package can produce a large `.d.ts`: `svelte/elements`'s attribute-map
1377
+ interfaces (`HTMLButtonAttributes` and friends) are individually sizable, and every type they
1378
+ transitively reference gets copied too. Prefer `"local"` if you don't specifically need bare
1379
+ imports inlined.
1380
+
1381
+ A component with at least one inlined declaration skips the generated-text cache (its output now
1382
+ depends on another file's contents, not just its own source hash), though its *parse* is still
1383
+ cached as usual. Also available as `--types-inline=<local|all>` on the CLI; `--types-inline=false`
1384
+ resets to the default.
1385
+
993
1386
  #### `markdownOptions.onAppend`
994
1387
 
995
1388
  `markdownOptions.onAppend` fires on every heading, quote, paragraph, divider, and raw block written to the Markdown document, and receives the block's `type`, the in-progress `WriterMarkdown` document, and the full component map. Use it to inject extra content, e.g. a summary line under the `h1` title.
@@ -3509,6 +3902,8 @@ export const secondary = true;
3509
3902
  import Button from "./Button.svelte";
3510
3903
  ```
3511
3904
 
3905
+ The named interface must match the target's generated props type name exactly — the default `<Name>Props`, or whatever [`typesOptions.typeNames`](#typesoptionstypenames) templates it to.
3906
+
3512
3907
  ### `@template`
3513
3908
 
3514
3909
  Svelte supports defining generics via the [`generics` attribute](https://svelte.dev/docs/svelte/typescript) on the script tag, but this requires `lang="ts"`:
package/lib/browser.d.ts CHANGED
@@ -478,7 +478,7 @@ export class ComponentParser {
478
478
  parseSvelteComponent(source: string, diagnostics: ComponentParserDiagnostics): ParsedComponent;
479
479
  }
480
480
 
481
- 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" | "extend-props-target-missing" | "extend-props-duplicate" | "extend-props-override" | "jsdoc-unknown-tag" | "typedef-duplicate" | "property-duplicate" | "generics-conflict" | "jsdoc-tag-dropped" | "internal-typedef-referenced";
481
+ 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" | "extend-props-target-missing" | "extend-props-duplicate" | "extend-props-override" | "jsdoc-unknown-tag" | "typedef-duplicate" | "property-duplicate" | "generics-conflict" | "jsdoc-tag-dropped" | "internal-typedef-referenced" | "types-inline-unresolved";
482
482
 
483
483
  type SveldDiagnosticSeverity = "error" | "warning";
484
484
 
@@ -512,6 +512,95 @@ export interface ComponentDocApi extends ParsedComponent {
512
512
 
513
513
  export type ComponentDocs = Map<string, ComponentDocApi>;
514
514
 
515
+ interface InlinedTypes {
516
+ /** Exact `typeImportStatements` entries the writer must drop. */
517
+ droppedImportStatements: string[];
518
+ /** Declarations to emit (already stripped of `export`), in dependency order. */
519
+ declarations: string[];
520
+ /** Absolute paths of every file read, for watch-mode invalidation. */
521
+ dependencies: string[];
522
+ }
523
+
524
+ type CommentLevel = "all" | "descriptions" | "none";
525
+
526
+ export declare function formatTsProps(props?: string): string;
527
+
528
+ export declare function getTypeDefs(def: Pick<ComponentDocApi, "typedefs">, emit?: {
529
+ export: boolean;
530
+ }, commentLevel?: CommentLevel): string;
531
+
532
+ export declare function getContextDefs(def: Pick<ComponentDocApi, "contexts" | "generics">, emit?: {
533
+ export: boolean;
534
+ }, commentLevel?: CommentLevel): string;
535
+
536
+ export interface WriteTsDefinitionOptions {
537
+ /**
538
+ * `"class"` (default) extends the deprecated `SvelteComponentTyped`.
539
+ * `"component"` emits `declare const X: Component<Props, Exports, Bindings>`
540
+ * instead, for Svelte 5+ consumers. Generic components get a per-component
541
+ * interface with a generic call signature instead of `Component<...>`
542
+ * directly, since a `declare const` can't itself carry a generic type
543
+ * parameter (see `genGenericComponentDeclaration`).
544
+ */
545
+ format?: "class" | "component";
546
+ /**
547
+ * Which generated type declarations get an `export` keyword. `true`
548
+ * (default) exports all; `false` exports none; an object picks per kind.
549
+ * Module-script exports (`export declare const/function`) are runtime
550
+ * exports and are always emitted as exports.
551
+ */
552
+ exportTypes?: boolean | {
553
+ props?: boolean;
554
+ exports?: boolean;
555
+ typedefs?: boolean;
556
+ contexts?: boolean;
557
+ };
558
+ /** @internal Set by `writeTsDefinitions` for `@extends` targets; overrides `exportTypes.props`. */
559
+ forceExportProps?: boolean;
560
+ /**
561
+ * Templates for generated type names. `{name}` is replaced with the
562
+ * component's module name. Defaults: `"{name}Props"`, `"{name}Exports"`.
563
+ */
564
+ typeNames?: {
565
+ props?: string;
566
+ exports?: string;
567
+ };
568
+ /**
569
+ * How much JSDoc to emit. `"all"` (default) keeps descriptions,
570
+ * `@deprecated`, `@default`, and passthrough tags (`@since`, `@see`,
571
+ * `@example`, `@link`). `"descriptions"` keeps descriptions and
572
+ * `@deprecated` only. `"none"` emits no comments at all.
573
+ */
574
+ comments?: "all" | "descriptions" | "none";
575
+ /**
576
+ * `"type"` (default) emits the props type as a type alias.
577
+ * `"interface"` emits `interface <Name>Props { ... }` when the props are a
578
+ * plain object (no `@restProps`, no `@extendProps`, no whole-object
579
+ * `$props()` type); other shapes are intersections and stay aliases.
580
+ */
581
+ propsDeclaration?: "type" | "interface";
582
+ /**
583
+ * Copies `type`/`interface` declarations imported from a relative source (or a
584
+ * tsconfig/jsconfig path alias) directly into the `.d.ts`, dropping the import. `"local"`
585
+ * follows relative sources, path aliases, re-exports, and same-file dependencies; bare package
586
+ * imports, `.svelte` sources, and unsupported exports (enums, classes, functions, consts,
587
+ * namespaces) stay imports and get a `types-inline-unresolved` warning. `"all"` additionally
588
+ * inlines bare/package imports (e.g. `import type { Foo } from "some-lib"`) using the real
589
+ * TypeScript checker - same unsupported-export/collision rules, same warning on failure - with
590
+ * two exceptions kept as plain imports regardless: `svelte`/`svelte/elements` (a hard-coded
591
+ * allow-list; copying framework types would freeze a Svelte version into consumer output) and
592
+ * `@extendProps`/`@extends` targets (deferred; unrelated mechanism). `"all"` needs `typescript`
593
+ * 7+ and a `tsconfig.json`, same hard requirement as `resolveTypes`. `false` (default)
594
+ * preserves every import as-is.
595
+ * @default false
596
+ */
597
+ inline?: false | "local" | "all";
598
+ /** @internal Set by `writeTsDefinitions` from `GenerateBundleResult.inlinedTypesByFilePath` when `inline` resolved something for this component. */
599
+ inlined?: InlinedTypes;
600
+ }
601
+
602
+ export declare function writeTsDefinition(component: ComponentDocApi, options?: WriteTsDefinitionOptions): string;
603
+
515
604
  interface EntryExport {
516
605
  name: string;
517
606
  kind: "const" | "let" | "var" | "function" | "class" | "type" | "interface" | "enum";
@@ -735,26 +824,6 @@ declare class Writer {
735
824
  write(filePath: string, raw: string): Promise<boolean>;
736
825
  }
737
826
 
738
- export declare function formatTsProps(props?: string): string;
739
-
740
- export declare function getTypeDefs(def: Pick<ComponentDocApi, "typedefs">): string;
741
-
742
- export declare function getContextDefs(def: Pick<ComponentDocApi, "contexts" | "generics">): string;
743
-
744
- export interface WriteTsDefinitionOptions {
745
- /**
746
- * `"class"` (default) extends the deprecated `SvelteComponentTyped`.
747
- * `"component"` emits `declare const X: Component<Props, Exports, Bindings>`
748
- * instead, for Svelte 5+ consumers. Generic components get a per-component
749
- * interface with a generic call signature instead of `Component<...>`
750
- * directly, since a `declare const` can't itself carry a generic type
751
- * parameter (see `genGenericComponentDeclaration`).
752
- */
753
- format?: "class" | "component";
754
- }
755
-
756
- export declare function writeTsDefinition(component: ComponentDocApi, options?: WriteTsDefinitionOptions): string;
757
-
758
827
  export declare const COMPONENT_API_SCHEMA_VERSION = 1;
759
828
 
760
829
  export interface ComponentApiDocument {