output format #
svelte-docinfo outputs JSON describing your project's exported API. The data format is a hierarchy: modules contain declarations, and some declarations contain members or props.
Top-level structure #
Programmatic entry points (analyze, analyzeFromFiles) return both modules and accumulated diagnostics:
{
modules: ModuleJson[],
diagnostics: Diagnostic[]
} All surfaces emit this shape. The CLI's stdout JSON and the Vite plugin's virtual module
both expose modules and diagnostics (matching AnalyzeResultJson). The CLI runs output through compactReplacer so empty arrays strip on the wire (an empty-project
run emits {}); parse JSON consumers through AnalyzeResultJson to restore Zod defaults.
ModuleJson #
A ModuleJson describes a single source file and its exports:
path: file path relative to the source root (e.g.,"math.ts")declarations: exported items from this modulemoduleComment: file-level JSDoc comment, if presentdependencies: paths of modules this file importsdependents: paths of modules that import this filestarExports:export * from './module'patterns
Array fields (declarations, dependencies, etc.) are omitted from
JSON when empty and default to [] at runtime after parsing.
DeclarationJson #
Each declaration is a DeclarationJson, a discriminated union on
the kind field with nine variants:
"function": addsparameters,returnType,returnDescription,overloads"variable": adds optionaldefaultValue(from@default), plusreactivitywhen the initializer is a Svelte rune ($state,$state.raw,$derived,$derived.by)"class": addsmembers,extends,implements"interface": addsmembers,extends"type": addsmembers,intersects"enum": addsmembers(enum values)"component": addsprops,intersects,acceptsChildren,lang(Svelte components)"snippet": addsparameters(exported Svelte template snippets)"namespace": addsmodule(the source module path projected under this binding); synthesized forexport * as ns from './x'
Shared fields on all variants:
name,kind: identity. Default exports carryname === "default"(see Re-exports below)docComment: JSDoc comment texttypeSignature: full type as a stringsourceLine: line number in the source filemodifiers: e.g.,"readonly","static","getter"genericParams: type parameters with constraints and defaultsexamples,deprecatedMessage,seeAlso,throws,since: from standard JSDoc tagsmutates: from the non-standard@mutatestag, stored asRecord<string, string>mapping target keys to descriptions. Keys are typically parameter names but compound paths (this.foo) and external state references are accepted as-isalsoExportedFrom: modules that re-export this declarationaliasOf: original name if this is a renamed re-exportpartial:truewhen extraction failed partway through the declaration, indicating incomplete data
Declarations tagged with @nodocs are excluded from the output entirely and are also
excluded from duplicate name checking.
Re-exports #
Re-exports are encoded with two shapes, chosen by content:
- Same-name: the canonical declaration carries an
alsoExportedFromarray listing the modules that re-export it. One declaration, multiple import paths. - Renamed: a synthesized declaration appears in the re-exporting module
with
aliasOf: {module, name}pointing at the canonical. InheritstypeSignature,docComment,parameters,reactivity, anddefaultValuefrom the canonical;sourceLineis undefined. - Star exports:
export * from './x'patterns are tracked separately onModuleJson.starExportsand don't synthesize per-declaration entries.
When a re-export statement carries its own JSDoc or @nodocs, an alias is also synthesized in the re-exporting module so the local content has somewhere to
live, even when the name is unchanged. The trigger is "presence of local content," not
"presence of rename." Local doc-comment fields apply first and stick; canonical fields only
fill gaps. @nodocs on a re-export suppresses both the link and the synthesis.
Default-slot entries carry name === "default" (see the
shared-fields note above for why). Renames out of the default slot (export {default as Foo} from './x') carry name: "Foo" and aliasOf: {module, name: "default"}. Duplicate-name checks skip "default" since the default slot is module-scoped per the JS spec.
Namespace re-exports (export * as ns from './x') synthesize a NamespaceDeclarationJson with module pointing at the source the
namespace projects. Consumers render ns.a / ns.b by reading the
source module's declarations; namespaces don't inline members.
MemberJson #
Classes, interfaces, types, and enums can contain MemberJson entries
in their members arrays. MemberJson is a discriminated union on kind with three variants:
"function": methods and call signatures. Addsparameters,returnType,returnDescription,overloads"constructor": class constructors and construct signatures. Addsparameters,overloads"variable": properties, accessors, and index signatures. Adds optionaldefaultValue(from@default), plusreactivityfor class fields initialized with a Svelte rune
Member kind is restricted to these three variants. Nesting is exactly one level deep:
members never contain their own members.
Member name is the user-chosen identifier in most cases, but three synthesized
sentinels appear when no source identifier exists: "constructor" (class
constructor), "(construct)" (construct signature on an interface or type
alias), and "(call)" (call signature on an interface or type alias).
ComponentPropJson #
Component declarations have a props array of ComponentPropJson entries:
name,type: prop name and TypeScript typeoptional: whether the prop is optionaldescription: from JSDoc on the propdefaultValue: default value as a string, if presentbindable: set when the prop is declared with the$bindable()rune, so<Foo bind:value />is supported. Modeled here (not via the variable-levelreactivityfield) because$props/$bindableare component-prop concernsparameters: structured parameters for snippet-typed props (e.g.,Snippet<[text: string]>), absent for non-snippet propsexamples,deprecatedMessage,seeAlso,throws,since: symbol-scope JSDoc tags parsed from the prop's own doc comment (same shape as the declaration shared fields)
Asymmetry with ParameterJson. Props carry the
symbol-scope tag fields above; function parameters deliberately don't. A prop is a named
slot with its own documentation surface. A parameter is positional, and its @example/@deprecated/@since/@see/@throws belong on the enclosing function symbol per the TSDoc spec. Per-parameter content lives on ParameterJson.description from @param only.
ParameterJson #
Functions, snippets, constructors, and snippet-typed component props use ParameterJson entries in their parameters arrays:
name: parameter name (e.g.,"options","...args")type: resolved TypeScript type as a stringoptional: whether the parameter has a?tokenrest: whether the parameter uses rest syntax (...args)description: from@paramJSDocdefaultValue: default value expression from the source, if present
OverloadJson #
Functions and constructors with multiple signatures use OverloadJson entries in their overloads arrays. Each overload
captures only signature-scope content, the fields that can vary meaningfully per signature:
typeSignature: the full overload signature as a stringparameters: parameter list for this overload, with per-overload@paramdescriptionsreturnType: return type for this overload (functions only)genericParams: type parameters for this overloaddocComment: per-overload JSDoc text, if presentreturnDescription: from@returnson this overload
Symbol-scope JSDoc tags (@example, @deprecated, @since, @see, @throws, @mutates)
describe the function as a whole and live on the parent declaration only, not duplicated per
overload. The primary overload's JSDoc feeds the parent's symbol-level extraction; placing
one of those tags on a non-primary overload signature emits a misplaced_tag warning and the tag is dropped (no synthetic content, no silent loss). Typo'd or stale @param keys produce unknown_param warnings the same way.
Reactivity #
The reactivity field appears on VariableDeclarationJson and VariableMemberJson when the initializer is a value-producing Svelte
rune call: $state, $state.raw, $derived, or $derived.by. Detection is purely syntactic and runs on every analyzed file
regardless of extension, capturing the same patterns in a plain .ts file as in .svelte.ts or a component's <script>.
It covers variables (top-level and class fields). Function parameters and destructured
bindings are not annotated even when the value flows from a rune. $props and $bindable are component-prop concerns and surface on ComponentPropJson's bindable field instead.
GenericParamJson #
Declarations and members with type parameters use GenericParamJson entries in their genericParams arrays:
name: type parameter name (e.g.,"T")constraint:extendsconstraint, if presentdefaultType: default type, if present
Working with type strings #
Type signatures are opaque strings produced by the TypeScript compiler. To discover which in-project declaration names appear in a type string (e.g., for rendering clickable links), use findTypeReferences:
import {findTypeReferences} from 'svelte-docinfo';
const names = new Set(modules.flatMap(m => m.declarations.map(d => d.name)));
findTypeReferences('Map<string, ModuleJson[]>', names);
// => ['ModuleJson'] When scanning many type strings against the same set of names, pre-compile the patterns with buildTypeReferencePatterns to avoid recompiling regexes on every call.
Compact JSON and absent-as-false #
By default, output uses compact JSON via compactReplacer: empty
arrays, false booleans, and undefined fields are stripped, so optional, acceptsChildren, partial, rest, bindable, and similar fields vanish from the wire form when their value is the
default. After parsing with the Zod schemas from types.ts (or AnalyzeResultJson for the full {modules, diagnostics} envelope), all defaults are restored, and the round-trip
is lossless.
Raw-JSON consumers (e.g., jq, hand-rolled pipelines that skip .parse()) must treat absent as false; a literal decl.optional === false check silently fails because the key is gone. Use the
schemas, or truthy/falsy checks (if (decl.optional) …) on raw JSON.
Examples #
A TypeScript function:
{
"modules": [
{
"path": "math.ts",
"declarations": [
{
"name": "clamp",
"kind": "function",
"docComment": "Clamp a number to a range.",
"typeSignature": "(value: number, min: number, max: number): number",
"parameters": [
{"name": "value", "type": "number"},
{"name": "min", "type": "number"},
{"name": "max", "type": "number"}
],
"returnType": "number",
"sourceLine": 2
}
]
}
]
} A Svelte component with a snippet prop, children, and an exported snippet:
{
"modules": [
{
"path": "Card.svelte",
"declarations": [
{
"name": "Card",
"kind": "component",
"docComment": "A card with a customizable header.",
"acceptsChildren": true,
"props": [
{"name": "title", "type": "string"},
{
"name": "header",
"type": "Snippet<[title: string]>",
"optional": true,
"description": "Custom header rendering.",
"parameters": [
{"name": "title", "type": "string"}
]
}
],
"sourceLine": 1
},
{
"name": "card_footer",
"kind": "snippet",
"docComment": "Default footer snippet.",
"typeSignature": "Snippet<[text: string]>",
"parameters": [
{"name": "text", "type": "string"}
],
"sourceLine": 12
}
]
}
]
} A rune module exporting reactive state (e.g., a .svelte.ts file):
{
"modules": [
{
"path": "counter.svelte.ts",
"declarations": [
{
"name": "count",
"kind": "variable",
"typeSignature": "number",
"reactivity": "$state",
"sourceLine": 1
},
{
"name": "doubled",
"kind": "variable",
"typeSignature": "number",
"reactivity": "$derived",
"sourceLine": 2
}
]
}
]
} A function defined in math.ts and re-exported under a new name from the barrel index.ts. The canonical entry carries alsoExportedFrom if any module re-exports it under the same name. Renames
synthesize a separate declaration with aliasOf:
{
"modules": [
{
"path": "math.ts",
"declarations": [
{
"name": "clamp",
"kind": "function",
"typeSignature": "(value: number, min: number, max: number): number",
"sourceLine": 2
}
]
},
{
"path": "index.ts",
"declarations": [
{
"name": "clampNumber",
"kind": "function",
"typeSignature": "(value: number, min: number, max: number): number",
"aliasOf": {"module": "math.ts", "name": "clamp"}
}
],
"starExports": ["other.ts"]
}
]
} See the types module for the full Zod schemas, and the API reference for all exported types.