postprocess.ts

Post-processing for analyzed library metadata.

These functions transform analysis results after module-level analysis is complete:

  1. ValidationfindDuplicates checks flat namespace constraints
  2. TransformationmergeReExports resolves re-export relationships, computeDependents builds bidirectional dependency graphs
  3. OutputsortModules prepares deterministic output

@see analyze.ts for the main analysis entry point

view source

Declarations
#

10 declarations

compareStrings
#

postprocess.ts view source

(a: string, b: string): number import {compareStrings} from 'svelte-docinfo/postprocess.js';

Case-insensitive string comparator for deterministic output ordering.

Case-folded comparison first (so Analyze and analyze sort together instead of all uppercase before all lowercase), then a code-unit tiebreak — so equal-ignoring-case strings still compare unequal and the result is an exact total order. Unlike localeCompare (host-locale/ICU-dependent, so byte-identical input can serialize in different orders on different machines), both passes use Unicode default mappings only and are environment-independent. All output ordering goes through this comparator — never bare localeCompare or default Array.prototype.sort.

a

type string

b

type string

returns

number

computeDependents
#

postprocess.ts view source

(files: readonly SourceFileInfo[]): (SourceFileInfo & { dependents?: readonly string[] | undefined; })[] import {computeDependents} from 'svelte-docinfo/postprocess.js';

Compute bidirectional dependencies from source files.

This function ensures that if file A has file B in its dependencies, then file B will have file A in its dependents. This provides consistent output regardless of whether callers provide one-directional or bidirectional dependency information.

Returns new SourceFileInfo objects when computed dependents exist or when paths needed posixification; otherwise the original input objects flow through ===-equal (fast path for session callers, who already pass POSIX paths and may have no inferable dependents for a given file).

files

source files with optional dependency information

type readonly SourceFileInfo[]

returns

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

new array with bidirectional dependencies computed

examples

// Input: Calculator.svelte has dependencies: [math.ts] // Output: Calculator.svelte has dependencies: [math.ts] // math.ts has dependents: [Calculator.svelte] const filesWithBidirectional = computeDependents(files);

DuplicateDeclaration
#

postprocess.ts view source

also exported from index.ts

DuplicateDeclaration import type {DuplicateDeclaration} from 'svelte-docinfo/postprocess.js';

A duplicate declaration with its full metadata and module path.

declaration

The full declaration metadata.

type DeclarationJson

module

Module path where this declaration is defined.

type string

ExportSurface
#

postprocess.ts view source

also exported from index.ts

ExportSurface import type {ExportSurface} from 'svelte-docinfo/postprocess.js';

A module's resolved export surface — see resolveExportSurface.

entries

Surface entries, sorted by name (case-insensitive order, compareStrings).

type Array<ExportSurfaceEntry>

unresolvedStarExports

Star targets (own or transitive) absent from the analyzed set — their projected names are unknown, so the surface is incomplete.

type Array<string>

externalStarExports

External star specifiers reachable from this module (own or transitive) — their projected names are unknowable without analyzing the package.

type Array<string>

ExportSurfaceEntry
#

postprocess.ts view source

also exported from index.ts

ExportSurfaceEntry import type {ExportSurfaceEntry} from 'svelte-docinfo/postprocess.js';

One name on a module's resolved export surface.

name

Exported name, in the docinfo model's terms — Svelte components appear under their filename-derived name (the model's convention for default exports of .svelte files), not 'default'.

type string

via

How the name reaches this module's surface: an own declaration (including synthesized aliases), a same-name re-export edge, a direct external re-export, or projection through export * from './x'.

type 'declaration' | 'reExport' | 'external' | 'star'

module?

Canonical module path, when known (undefined for external entries).

type string

declaration?

The canonical declaration, when present in the analyzed set.

type DeclarationJson

specifier?

Package specifier for external entries (as written in the statement).

type string

originalName?

Name inside the external package when renamed.

type string

typeOnly?

Type-only re-export — the name is erased at runtime.

type boolean

starFrom?

For star-projected entries: the starExports target the name arrived through.

type string

findDuplicates
#

postprocess.ts view source

also exported from index.ts

(modules: { path: string; declarations: ({ kind: "function"; parameters: { name: string; type: string; optional: boolean; rest: boolean; description?: string | undefined; defaultValue?: string | undefined; propertyDescriptions?: Record<...> | undefined; }[]; ... 17 more ...; sourceLine?: number | undefined; } | ... 7 more ... | { ...; })[]; ... 7 more ...; moduleComment?: string | undefined; }[]): Map<...> import {findDuplicates} from 'svelte-docinfo/postprocess.js';

Find duplicate declaration names across modules.

A duplicate is two *different things* sharing a name in the flat namespace. Occurrences are compared by canonical identity — aliasOf chains are resolved first, so an alias and its canonical (or two aliases of the same canonical) are one thing, not a collision. Documenting a same-name re-export (which synthesizes an alias) or re-exporting a component under its own name (export {default as Foo} from './Foo.svelte') therefore doesn't flag. When a name does flag, all occurrences are reported, aliases included.

Callers can decide how to handle duplicates (throw, warn, ignore).

modules

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

returns

Map<string, DuplicateDeclaration[]>

Map of declaration names to their DuplicateDeclaration occurrences (only includes duplicates)

examples

const duplicates = findDuplicates(modules); if (duplicates.size > 0) { for (const [name, occurrences] of duplicates) { console.error(`"${name}" found in:`); for (const {declaration, module} of occurrences) { console.error(` - ${module}:${declaration.sourceLine} (${declaration.kind})`); } } throw new Error(`Found ${duplicates.size} duplicate declaration names`); }

mergeReExports
#

postprocess.ts view source

also exported from index.ts

(modules: { path: string; declarations: ({ kind: "function"; parameters: { name: string; type: string; optional: boolean; rest: boolean; description?: string | undefined; defaultValue?: string | undefined; propertyDescriptions?: Record<...> | undefined; }[]; ... 17 more ...; sourceLine?: number | undefined; } | ... 7 more ... | { ...; })[]; ... 7 more ...; moduleComment?: string | undefined; }[]): void import {mergeReExports} from 'svelte-docinfo/postprocess.js';

Build alsoExportedFrom arrays from the modules' forward re-export edges.

Each module carries its same-name re-export edges as ModuleJson.reExports (collected in phase 1); this phase-2 pass inverts them onto the canonical declarations so both directions of the same fact are queryable. Edges whose canonical module or declaration is absent from modules are skipped — the forward entry remains without a back-link (see ReExportJson for the presence caveats).

Component-only fields on renamed component aliases (props, acceptsChildren, etc.) are populated separately by resolveComponentAliases — call it after this function. They split because they touch disjoint fields and have different inputs.

modules

the modules array with all modules (will be mutated). Must be parsed ModuleJsons — wire JSON strips empty arrays, so run raw JSON through AnalyzeResultJson.parse first or reExports may be undefined

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

returns

void

mutates

  • modules — unions re-exporters into `declaration.alsoExportedFrom`

examples

// helpers.ts exports: foo, bar // index.ts does: export {foo, bar} from './helpers.js' // (so index.ts's ModuleJson carries reExports: // [{name: 'foo', module: 'helpers.ts'}, {name: 'bar', module: 'helpers.ts'}]) // // After processing: // - helpers.ts foo declaration gets: alsoExportedFrom: ['index.ts'] // - helpers.ts bar declaration gets: alsoExportedFrom: ['index.ts']

resolveComponentAliases
#

postprocess.ts view source

also exported from index.ts

(modules: { path: string; declarations: ({ kind: "function"; parameters: { name: string; type: string; optional: boolean; rest: boolean; description?: string | undefined; defaultValue?: string | undefined; propertyDescriptions?: Record<...> | undefined; }[]; ... 17 more ...; sourceLine?: number | undefined; } | ... 7 more ... | { ...; })[]; ... 7 more ...; moduleComment?: string | undefined; }[]): void import {resolveComponentAliases} from 'svelte-docinfo/postprocess.js';

Copy props/acceptsChildren/lang/etc. from canonical component declarations onto synthesized component-aliased declarations.

Renamed Svelte component re-exports (export {default as Foo} from './X.svelte') are emitted as kind: 'component' placeholders by analyzeExports, with aliasOf pointing at the canonical. The canonical's component-specific fields are only available after analyzeSvelteModule synthesizes the canonical declaration, so the copy happens here in phase 2 once all modules are analyzed.

Call this *after* mergeReExports — both walk the same modules array but read/write disjoint fields, so order between them only matters for clarity.

modules

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

returns

void

mutates

  • modules — fills component-only fields on aliased component declarations

resolveExportSurface
#

postprocess.ts view source

also exported from index.ts

(modules: { path: string; declarations: ({ kind: "function"; parameters: { name: string; type: string; optional: boolean; rest: boolean; description?: string | undefined; defaultValue?: string | undefined; propertyDescriptions?: Record<...> | undefined; }[]; ... 17 more ...; sourceLine?: number | undefined; } | ... 7 more ... | { ...; })[]; ... 7 more ...; moduleComment?: string | undefined; }[], path: string): ExportSurface | null import {resolveExportSurface} from 'svelte-docinfo/postprocess.js';

Resolve a module's full export surface from the analyzed model, applying ES module semantics to star exports.

Combines the module's own declarations (including synthesized aliases), reExports edges, externalReExports, and transitively-resolved starExports into one deduped, name-sorted list. The ES rules applied to star projection:

  • explicit exports (declarations, edges, externals) shadow star-projected names
  • a name projected by two stars that resolve to *different* canonicals is ambiguous and excluded (same canonical through a diamond is included once)
  • default is never star-projected — including canonical Svelte component declarations, which represent their file's default export. (Caveat: a star-projected re-export edge whose canonical is a component is treated as a default-slot re-export and skipped; a <script module> const sharing the component's exact name would be skipped with it.)

A Position-3 alias and its reExports edge are the same fact — the declaration entry wins, inheriting the edge's typeOnly.

Cyclic star graphs terminate by contributing nothing along the back-edge (an approximation of ES fixpoint resolution — fine in practice). Surfaces resolved inside a cycle are path-relative and not memoized, so sibling star paths resolve independently rather than inheriting them. Star targets missing from modules are reported in unresolvedStarExports rather than guessed at; externalStarExports aggregates external star specifiers reachable from the module, whose names are unknowable.

modules

the analyzed modules (parsed ModuleJsons — run wire JSON through AnalyzeResultJson.parse first)

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

path

the module to resolve, as a ModuleJson.path value

type string

returns

ExportSurface | null

the resolved surface, or null when path isn't in modules

sortModules
#

postprocess.ts view source

(modules: { path: string; declarations: ({ kind: "function"; parameters: { name: string; type: string; optional: boolean; rest: boolean; description?: string | undefined; defaultValue?: string | undefined; propertyDescriptions?: Record<...> | undefined; }[]; ... 17 more ...; sourceLine?: number | undefined; } | ... 7 more ... | { ...; })[]; ... 7 more ...; moduleComment?: string | undefined; }[]): { ...; }[] import {sortModules} from 'svelte-docinfo/postprocess.js';

Sort modules alphabetically by path for deterministic output and cleaner diffs.

Case-insensitive order (compareStrings) so the output is environment-independent.

modules

the modules to sort

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

returns

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

a new sorted array (does not mutate input)

Depends on
#

Imported by
#