diagnostics #
Analysis accumulates errors and warnings without halting. A failing declaration is marked partial: true and the rest of the module still analyzes. Detail lands in an array
of Diagnostic entries, alongside modules in the result.
Two-tier error model #
Accumulated (non-fatal): appended to the Diagnostic array, analysis continues. Covers type resolution failures,
member or prop extraction failures, and JSDoc tag misuse. The return value is still valid but
may carry partial: true on affected declarations.
Thrown (fatal): a small set of setup-level conditions throws from public
entry points: missing tsconfig.json, Svelte <5 detected, or discovery: 'exports' mode with no resolvable exports. Wrap the top-level analyze / analyzeFromFiles call if you want to handle these.
svelte2tsx transformation failures are not thrown; they flow as transform_failed diagnostics.
Shape #
diagnostics is a plain Array<Diagnostic>, no wrapper, no methods.
Round-trips through JSON.stringify / z.array(Diagnostic).parse, so
it serializes alongside modules in CLI output and rehydrates cleanly.
{
modules: ModuleJson[],
diagnostics: Diagnostic[]
} Each Diagnostic carries:
kind: discriminant, one per failure mode (see table below)severity:"error"or"warning"file: POSIX-form, project-relative (no leading./). Rejoin withprojectRootfor absolute paths.line,column: 1-based, optional. Absent when there's no precise AST node (e.g., a module-level skip)message: human-readable description- additional fields specific to the variant:
symbolName,className,tagName, etc.
Diagnostic kinds #
Severity is stable per kind: every kind below is warning severity except transform_failed and module_unreadable, which are always error.
| kind | When it fires |
|---|---|
type_extraction_failed | Trigger: type resolution threw on a symbol. Consequence: declaration included with partial: true and
empty typeSignature. |
signature_analysis_failed | Trigger: function or method signature analysis threw, usually
circular generics or unresolved call signatures. Consequence: declaration included with partial: true;
parameters and overloads may be empty. |
class_member_failed | Trigger: one member of a class couldn't be analyzed. Consequence: member included with partial: true;
siblings still extract normally. |
svelte_prop_failed | Trigger: a Svelte component prop type couldn't be resolved through
the checker. Consequence: per-prop type resolution failures fall back
to "any" for that prop with siblings unaffected; when the whole $props<T>() annotation type is itself unresolvable, the component's props array drops to empty. |
module_skipped | Trigger: whole module skipped during the analysis pass. reason narrows to "not_in_program", "no_analyzer", or "requires_program". Consequence: module absent from modules[]. |
module_unreadable | Trigger: file named in package.json exports exists but readFile failed (permission denied, FS error). Consequence: file dropped from the discovered set. Discovery-time. |
import_parse_failed | Trigger: import parsing failed during dependency resolution. Consequence: the dependency edge is dropped; the module itself still analyzes. Ingest-time. |
duplicate_comment | Trigger: two sources supplied a comment for the same target (HTML @component + script JSDoc, or multiple @module comments). commentType narrows to "module_comment" or "doc_comment". Consequence: the higher-priority source
wins: JSDoc for doc comments; instance <script> > <script module> > HTML comment for module comments. |
misplaced_tag | Trigger: symbol-scope tag (@example, @deprecated, @since, @see, @throws, @mutates, @default, @nodocs) found on a non-primary overload signature. Consequence: the tag is dropped; move it to the primary signature to keep
it. |
unknown_param | Trigger: @param key didn't match any actual parameter
(typo or stale doc after a rename). Consequence: the description is dropped. |
duplicate_declaration | Trigger: a declaration name appears in more than one module, so the
flat-namespace assumption collides. declarationName and modules name the conflict. Consequence: always emitted; onDuplicates only controls whether to additionally throw, log, or invoke a
callback. |
transform_failed | Trigger: svelte2tsx threw on a .svelte file. Consequence: the file's ModuleJson is synthesized as a placeholder (partial: true, empty declarations). Ingest-time. |
source_map_failed | Trigger: source map parsing failed for a Svelte virtual file. Consequence: analysis continues using virtual positions, so
downstream line/column may point into the svelte2tsx output
rather than the original .svelte source. Ingest-time. |
resolver_failed | Trigger: import resolver threw on a specifier (vs. legitimately
returning null for externals). specifier names the failing
import. Consequence: the dependency edge is dropped. Ingest-time. |
Each variant is a strict Zod object with its own extra fields. Use byKind to narrow to a specific variant for typed access:
import {byKind} from 'svelte-docinfo';
for (const d of byKind(diagnostics, 'misplaced_tag')) {
// d.tagName, d.functionName, d.file, d.line are typed
console.warn(`${d.functionName}: move @${d.tagName} to the primary overload`);
}severity vs partial #
severity says how loud to be about a problem; partial: true says a specific declaration or member has incomplete data,
typically from type_extraction_failed, signature_analysis_failed, class_member_failed, or svelte_prop_failed. Branch on partial directly; no need to cross-reference diagnostics by file and line.
Helpers #
The diagnostics array is a plain Array<Diagnostic>: construct with [] and mutate with Array.push. Read helpers:
- hasErrors, hasWarnings: boolean checks by severity
- errorsOf, warningsOf: filter by severity
- byKind: filter by kind, narrowed to the matching variant
- formatDiagnostic: format as
'./file.ts:10:5: error: message'(the'./'prefix is fixed)
Consuming diagnostics #
The CLI always emits the structured diagnostics field alongside modules in JSON output, and also prints warnings and errors to stderr:
npx svelte-docinfo | jq '.diagnostics | group_by(.kind) | map({kind: .[0].kind, count: length})' Programmatically:
import {analyzeFromFiles, errorsOf, formatDiagnostic, byKind} from 'svelte-docinfo';
const {modules, diagnostics} = await analyzeFromFiles({projectRoot: process.cwd()});
// File paths in diagnostics are already project-relative.
for (const d of errorsOf(diagnostics)) {
console.error(formatDiagnostic(d));
}
// Specific check: any @param typos?
const stale = byKind(diagnostics, 'unknown_param');
if (stale.length) {
console.warn(`${stale.length} stale @param tag(s); fix or remove`);
} The Vite plugin's virtual module exports both modules and diagnostics, so SvelteKit apps can render a
doc-warnings page without re-running analysis:
import {modules, diagnostics} from 'virtual:svelte-docinfo';
import {hasErrors} from 'svelte-docinfo';
if (hasErrors(diagnostics)) {
// surface in the UI or fail the build
}Absence rule #
Optional scalar fields (line, column) drop on serialize per the
same compact-output rules as the rest of the schema. See output format for
the full rule. The Vite plugin's virtual module exposes modules and diagnostics as separate ES module exports, so they're always present even when
empty. See diagnostics.ts for the Zod schemas
and helper signatures.