analyze-core.ts

Pure two-phase analysis orchestrator.

Extracted from analyze.ts to break what would otherwise be a circular import: session.tsanalyze.ts (for the orchestrator) → session.ts (for the wrappers). With the orchestrator here, both session.ts and analyze.ts import downward into this module without depending on each other.

Public surface:

@internal — module split is implementation detail; consumers go through analyze.ts / session.ts for the stable surface.

view source

Declarations
#

11 declarations

analyzeCore
#

analyze-core.ts view source

(inputs: AnalyzeCoreInputs): { modules: { path: string; declarations: ({ kind: "function"; parameters: { name: string; type: string; optional: boolean; rest: boolean; typeInfo?: TypeJson | undefined; description?: string | undefined; defaultValue?: string | undefined; propertyDescriptions?: Record<...> | undefined; }[]; ... 19 more ...; sourceLine?: number | undefined; } | ... 7 more ... | { ...; })[]; ... 7 more ...; moduleComment?: string | undefined; }[]; diagnostics: ({ ...; } | ... 14 more ... | { ...; })[]; } import {analyzeCore} from 'svelte-docinfo/analyze-core.js';

inputs

returns

{ modules: { path: string; declarations: ({ kind: "function"; parameters: { name: string; type: string; optional: boolean; rest: boolean; typeInfo?: TypeJson | undefined; description?: string | undefined; defaultValue?: string | undefined; propertyDescriptions?: Record<...> | undefined; }[]; ... 19 more ...; sourceL...

AnalyzeCoreInputs
#

analyze-core.ts view source

AnalyzeCoreInputs import type {AnalyzeCoreInputs} from 'svelte-docinfo/analyze-core.js';

Inputs to analyzeCore. The caller (one-shot wrapper or session.query) is responsible for normalizing sourceOptions, obtaining the program, and pre-transforming Svelte files into svelteVirtualFiles.

transformFailedIds carries the IDs of .svelte files whose svelte2tsx transform threw at ingest. The dispatch synthesizes a placeholder ModuleJson (partial: true, empty declarations) for each so consumers see the file's existence in modules even though analysis couldn't run. Identifying these via a sibling Set keeps svelteVirtualFiles a clean "files we can analyze" map; the failure side-channel doesn't pollute it.

sourceFiles

type readonly SourceFileInfo[]

sourceOptions

type ModuleSourceOptions

program

type Program

svelteVirtualFiles

type ReadonlyMap<string, SvelteVirtualFile>

transformFailedIds?

Svelte file IDs whose svelte2tsx transform failed at ingest.

type ReadonlySet<string>

contextSvelteFiles?

Gated Svelte files (owned but failing isSource — the internal/ convention) whose virtuals are in svelteVirtualFiles. Analyzed only when an emitted component alias references them, as canonical-fill context for resolveComponentAliases — their modules never emit and their analysis diagnostics are dropped (partial on the canonical propagates to the filled alias instead).

type readonly SourceFileInfo[]

onDuplicates?

type OnDuplicates

log?

type AnalysisLog

analyzeModule
#

analyze-core.ts view source

(sourceFile: SourceFileInfo & { dependents?: readonly string[] | undefined; }, program: Program, options: ModuleSourceOptions, diagnostics: ({ symbolName: string; ... 5 more ...; column?: number | undefined; } | ... 14 more ... | { ...; })[], log?: AnalysisLog | undefined, aliasRegistry?: AliasRegistry | undefined): { ...; } | undefined import {analyzeModule} from 'svelte-docinfo/analyze-core.js';

Analyze a single non-Svelte source file and extract module metadata.

sourceFile

type SourceFileInfo & { dependents?: readonly string[] | undefined; }

program

type Program

options

diagnostics

type ({ symbolName: string; file: string; message: string; severity: "error" | "warning"; kind: "type_extraction_failed"; line?: number | undefined; column?: number | undefined; } | { functionName: string; ... 5 more ...; column?: number | undefined; } | ... 13 more ... | { ...; })[]

log?

optional

aliasRegistry?

optional

returns

{ path: string; declarations: ({ kind: "function"; parameters: { name: string; type: string; optional: boolean; rest: boolean; typeInfo?: TypeJson | undefined; description?: string | undefined; defaultValue?: string | undefined; propertyDescriptions?: Record<...> | undefined; }[]; ... 19 more ...; sourceLine?: numbe...

AnalyzeResultJson
#

analyze-core.ts view source

also exported from index.ts

{ modules: { path: string; declarations: ({ kind: "function"; parameters: { name: string; type: string; optional: boolean; rest: boolean; typeInfo?: TypeJson | undefined; description?: string | undefined; defaultValue?: string | undefined; propertyDescriptions?: Record<...> | undefined; }[]; ... 19 more ...; sourceL... import {AnalyzeResultJson} from 'svelte-docinfo/analyze-core.js';

Result of analyze, analyzeFromFiles, and AnalysisSession.query.

Modules sorted alphabetically by path. Diagnostics are query-time (analysis-pass) diagnostics only when produced by session.query; one-shot wrappers concatenate ingest + query diagnostics into this same array.

Schema-validated round-trip

The envelope is a Zod schema (AnalyzeResultJson) — both fields default to [], so JSON.stringify(result, compactReplacer) strips empty arrays on the wire and AnalyzeResultJson.parse(JSON.parse(json)) restores them. Consumers programmatically ingesting analysis JSON should parse through the schema to get defaults restored; raw-JSON consumers (e.g., jq) treat missing keys as null-equivalent (jq '.diagnostics | length' returns 0 on {}) and don't need the parse step.

Construction sites (one-shot wrappers, session.query) hand back hand-built objects without re-running .parse() — the inner modules and diagnostics arrays are already Zod-validated upstream, and the envelope schema is the type contract, not a validation gate.

See AnalyzeResultJsonWire for the serialized input-side shape published on virtual:svelte-docinfo.

modules

type { path: string; declarations: ({ kind: "function"; parameters: { name: string; type: string; optional: boolean; rest: boolean; typeInfo?: TypeJson | undefined; description?: string | undefined; defaultValue?: string | undefined; propertyDescriptions?: Record<...> | undefined; }[]; ... 19 more ...; sourceLine?: numbe...

diagnostics

type ({ symbolName: string; file: string; message: string; severity: "error" | "warning"; kind: "type_extraction_failed"; line?: number | undefined; column?: number | undefined; } | { functionName: string; ... 5 more ...; column?: number | undefined; } | ... 13 more ... | { ...; })[]

AnalyzeResultJsonWire
#

analyze-core.ts view source

also exported from index.ts

AnalyzeResultJsonWire import type {AnalyzeResultJsonWire} from 'svelte-docinfo/analyze-core.js';

Serialized wire shape of an analysis result, as published by the Vite plugin on virtual:svelte-docinfo — the input-side counterpart to AnalyzeResultJson (the validated output of .parse()).

The two fields are deliberately asymmetric:

  • modules is ModuleJsonInput (the z.input of ModuleJson) because the plugin runs it through compactReplacer, which strips .default([]) arrays and .default(false) booleans. Default-bearing fields therefore arrive undefined.
  • diagnostics is the output Diagnostic — the plugin serializes it without the replacer, and Diagnostic has no defaults to strip, so the array is always present and the shape matches runtime exactly.

Consumers restore defaults by parsing through AnalyzeResultJson.

Note this describes the Vite virtual module specifically. The CLI runs the whole envelope through compactReplacer, so its JSON may additionally omit an empty diagnostics array — CLI consumers should parse through AnalyzeResultJson rather than assume this shape.

modules

type { path: string; declarations?: ({ kind: "function"; name: string; returnType?: string | undefined; returnTypeInfo?: TypeJson | undefined; returnDescription?: string | undefined; ... 16 more ...; genericParams?: { ...; }[] | undefined; } | ... 7 more ... | { ...; })[] | undefined; ... 7 more ...; partial?: boolean | ...

diagnostics

type ({ symbolName: string; file: string; message: string; severity: "error" | "warning"; kind: "type_extraction_failed"; line?: number | undefined; column?: number | undefined; } | { functionName: string; ... 5 more ...; column?: number | undefined; } | ... 13 more ... | { ...; })[]

finalizeDiagnostics
#

analyze-core.ts view source

(diagnostics: ({ symbolName: string; file: string; message: string; severity: "error" | "warning"; kind: "type_extraction_failed"; line?: number | undefined; column?: number | undefined; } | { functionName: string; ... 5 more ...; column?: number | undefined; } | ... 13 more ... | { ...; })[], options: { ...; }): void import {finalizeDiagnostics} from 'svelte-docinfo/analyze-core.js';

Run the diagnostic boundary passes in their required order: virtual position remap first (remapVirtualDiagnosticPositions), then path normalization (normalizeDiagnosticPaths — it strips the virtual suffix the remap matches file against, so the reverse order silently keeps virtual positions). The one call for callers assembling modules themselves through analyzeModule / analyzeSvelteModule; using it makes the ordering unrepresentable instead of a contract to remember. Omit virtualFiles when no Svelte virtuals are in play.

diagnostics

type ({ symbolName: string; file: string; message: string; severity: "error" | "warning"; kind: "type_extraction_failed"; line?: number | undefined; column?: number | undefined; } | { functionName: string; ... 5 more ...; column?: number | undefined; } | ... 13 more ... | { ...; })[]

options

type { projectRoot: string; virtualFiles?: Iterable<SvelteVirtualFile> | undefined; }

returns

void

mutates

  • diagnostics — — rewrites positions, `file`, and `message`

normalizeDiagnosticPaths
#

analyze-core.ts view source

(diagnostics: ({ symbolName: string; file: string; message: string; severity: "error" | "warning"; kind: "type_extraction_failed"; line?: number | undefined; column?: number | undefined; } | { functionName: string; ... 5 more ...; column?: number | undefined; } | ... 13 more ... | { ...; })[], projectRoot: string): void import {normalizeDiagnosticPaths} from 'svelte-docinfo/analyze-core.js';

Normalize the paths a Diagnostic carries to project-root-relative form, in place.

Producers inside the analysis pipeline can write absolute paths or virtual paths (svelte2tsx output like Foo.svelte.__svelte2tsx__.ts). This pass collapses both to the public contract: a path relative to projectRoot with no leading slash and no ./ prefix. A file outside the root gets the ../ form, matching module paths in printed type text — and making the documented "rejoin with projectRoot to get an absolute path" actually hold for it, which dropping the leading slash did not.

message gets the same treatment as file, by textual substitution. A message is free-form, and not every path in one comes from a field this pass can see — import_parse_failed wraps an es-module-lexer error that embeds the file name itself. Scrubbing here means the contract holds for the whole record rather than one field, so a producer can't reintroduce an absolute path through prose.

Exposed for build-tool integrations that bypass the session and collect their own discovery/dep diagnostics — they need the same normalization to match the public contract. Hand it absolute paths. An already-relative file is left alone (relativizing it would resolve against cwd), so this pass can only correct the absolute form — a relative path on a base other than the project root passes through and ships as-is.

diagnostics

type ({ symbolName: string; file: string; message: string; severity: "error" | "warning"; kind: "type_extraction_failed"; line?: number | undefined; column?: number | undefined; } | { functionName: string; ... 5 more ...; column?: number | undefined; } | ... 13 more ... | { ...; })[]

projectRoot

type string

returns

void

mutates

  • diagnostics — — rewrites each diagnostic's `file` and `message`

see also

  • ``finalizeDiagnostics`` — when Svelte virtuals are in play, the position remap must run before this pass (it strips the suffix the remap matches on)

normalizeModulePathsInTypes
#

analyze-core.ts view source

(modules: { path: string; declarations: ({ kind: "function"; parameters: { name: string; type: string; optional: boolean; rest: boolean; typeInfo?: TypeJson | undefined; description?: string | undefined; defaultValue?: string | undefined; propertyDescriptions?: Record<...> | undefined; }[]; ... 19 more ...; sourceLine?: number | undefined; } | ... 7 more ... | { ...; })[]; ... 7 more ...; moduleComment?: string | undefined; }[], options: ModuleSourceOptions, program: Program): void import {normalizeModulePathsInTypes} from 'svelte-docinfo/analyze-core.js';

Normalize the absolute module paths TypeScript embeds in printed type text, in place.

The checker prints a module object as typeof import("<absolute path>"), which reaches output through every checker-printed field — typeSignature on declarations and members, returnType, and the text/name of a TypeJson node. Left alone it makes output machine-dependent (two checkouts of the same source produce different bytes), publishes local filesystem paths on any site that renders typeSignature, and exposes the svelte2tsx virtual suffix that stripVirtualSuffix exists to hide.

A path that resolves to a module in this output is rewritten to that module's ModuleJson.path, so the string doubles as a lookup key: a consumer linkifies with modules.find((m) => m.path === s) and reads a miss as "not a module here." See createModulePathNormalizer for the remaining tiers.

Runs as a whole-output pass rather than at the ~20 typeToString / signatureToString call sites, so a new printing site can't miss it.

modules

type { path: string; declarations: ({ kind: "function"; parameters: { name: string; type: string; optional: boolean; rest: boolean; typeInfo?: TypeJson | undefined; description?: string | undefined; defaultValue?: string | undefined; propertyDescriptions?: Record<...> | undefined; }[]; ... 19 more ...; sourceLine?: numbe...

options

program

type Program

returns

void

mutates

  • modules — — rewrites printed type text on declarations and members

OnDuplicates
#

analyze-core.ts view source

also exported from index.ts

OnDuplicates import type {OnDuplicates} from 'svelte-docinfo/analyze-core.js';

Behavior selector for duplicate declaration names across modules.

  • 'throw' — throw an Error listing every duplicate (strict flat-namespace enforcement)
  • 'warn' — log to log.error and continue
  • OnDuplicatesCallback — custom handler

Omitted entirely: no dispatch runs, but a duplicate_declaration diagnostic is still emitted into the diagnostics array for every collision (the diagnostic is the data; this option is the action).

'throw' trade-off: the throw fires after diagnostics are emitted but before the result is returned, so 'throw' callers never reach the diagnostics array. Callers that want fail-fast *and* diagnostic access should omit onDuplicates and inspect themselves:

const result = await analyze({...}); if (hasErrors(result.diagnostics)) throw new Error('analysis errors');

Or use an OnDuplicatesCallback and stash the data before throwing.

OnDuplicatesCallback
#

analyze-core.ts view source

also exported from index.ts

OnDuplicatesCallback import type {OnDuplicatesCallback} from 'svelte-docinfo/analyze-core.js';

Custom callback for handling duplicate declaration names.

Use the 'throw' or 'warn' shortcuts on onDuplicates for the common cases. Pass a function to fully control reporting.

(call)

type (duplicates: Map<string, DuplicateDeclaration[]>, log: Pick<AnalysisLog, "error">): void

duplicates

type Map<string, DuplicateDeclaration[]>

log

type Pick<AnalysisLog, "error">
returns void

throwOnDuplicates
#

analyze-core.ts view source

also exported from index.ts

(duplicates: Map<string, DuplicateDeclaration[]>, log: Pick<AnalysisLog, "error">): void import {throwOnDuplicates} from 'svelte-docinfo/analyze-core.js';

Convenience OnDuplicatesCallback that throws on any duplicate.

duplicates

type Map<string, DuplicateDeclaration[]>

log

type Pick<AnalysisLog, "error">

returns

void

throws

  • Error - listing every duplicate name and module location

Depends on
#

Imported by
#