@hyperfrontend/builder/bundle/declarations

declarations

tsc-driven .d.ts emission and post-emit path flattening.

generateDeclarations(context) shells out to the workspace-local tsc binary with --emitDeclarationOnly --declaration --declarationMap, captures stdout / stderr, and throws when the compiler exits with a non-zero status. After tsc finishes it calls flattenDeclarationPaths to relocate the nested dist/<lib>/libs/<lib>/src/... structure that tsc emits when baseUrl points at the workspace root back into the flat per-library shape consumers expect. The flatten is a recursive, unconditional copy of the whole nested src declaration tree, so internal non-entry subdirectories any entry's index.d.ts re-exports from (e.g. shared/, lib/) are preserved rather than dropped; it then removes the leftover libs/, plugins/, and apps/ folders left at the output root.

API Reference

ƒ Functions

§function

collectEntryRefs(source: string): EntryRef[]

Collects every named import/re-export a declaration file makes from another module, keyed by the specifier as written.
Namespace and star forms carry no names to falsify and are skipped; a default import is recorded as the default name.

Parameters

NameTypeDescription
§source
string
Raw text of the declaration file.

Returns

EntryRef[]
One record per named import/re-export statement.

Example

Reading the cross-entry references of a subpath entry

collectEntryRefs("import { A } from '..';")
// => [{ specifier: '..', names: ['A'] }]
§function

collectExportedNames(source: string): Set<string>

Collects the names an entry's bundled index.d.ts exports.
Returns null for an open export set — a bare export * from '…' re-exports names this pass cannot enumerate, so no reference against that entry can be called dangling.

Parameters

NameTypeDescription
§source
string
Raw text of the entry's index.d.ts.

Returns

Set<string>
The exported names, or null when the set is open.

Example

Reading an entry's public type surface

collectExportedNames('export type { A } from "./a";\nexport declare const b: number;')
// => Set { 'A', 'b' }
§function

computeSiblingSpecifier(selfDtsPath: string, sibling: SiblingEntry): string

Computes the bare-specifier form of a sibling import as emitted in the rewritten .d.ts (e.g., ../models, ../../models, ./bundle/declarations).
Always omits the trailing index.d.ts so both moduleResolution: 'bundler' and node accept it.

Parameters

NameTypeDescription
§selfDtsPath
string
Absolute path to the current entry's index.d.ts.
§sibling
SiblingEntry
The sibling entry being targeted.

Returns

string
POSIX-style relative specifier ready for id in a rollup external resolution.

Example

Computing a sibling import from `bundle/index.d.ts` to `models/`

computeSiblingSpecifier(
  '/abs/dist/libs/foo/bundle/index.d.ts',
  { srcPath: 'models', indexDtsPath: '/abs/dist/libs/foo/models/index.d.ts' }
) // => '../models'
§function

createSiblingExternalizePlugin(input: SiblingResolverInput): Plugin

Returns a rollup plugin that externalizes any import resolving inside a sibling entry's directory tree, rewriting the emitted specifier to a POSIX relative path stripped of /index and .d.ts suffixes.
The plugin's resolveId hook returns null for everything that does not resolve into a sibling — that lets the rest of the rollup-plugin-dts chain inline third-party type imports as before.
Self-ownership is enforced by adding the current entry into the ownership pool: when the deepest-matching entry directory is the entry being rewritten (and not a sibling), the resolver returns null so rollup keeps the path internal. This matters for the package root entry whose directory prefix- matches every other entry's path — without including self, root would spuriously claim its own subtree's files.

Parameters

NameTypeDescription
§input
SiblingResolverInput
Self + sibling entry descriptors.

Returns

Plugin
Rollup plugin.

Example

Plugin instance for the bundle entry pass

const plugin = createSiblingExternalizePlugin({
  selfSrcPath: 'bundle',
  selfDtsPath: '/abs/dist/libs/foo/bundle/index.d.ts',
  siblings: [{ srcPath: 'models', indexDtsPath: '/abs/dist/libs/foo/models/index.d.ts' }],
})
§function

dtsPathFor(outputPath: string, srcPath: string): string

Joins an output base directory with an entry's srcPath to produce the absolute path of the entry's bundled index.d.ts.

Parameters

NameTypeDescription
§outputPath
string
Absolute output directory.
§srcPath
string
Entry's srcPath. '' for root.

Returns

string
Absolute path to <outputPath>/<srcPath>/index.d.ts.

Example

Computing the d.ts path for a sub-entry

dtsPathFor('/abs/dist/libs/foo', 'models') // => '/abs/dist/libs/foo/models/index.d.ts'
§function

filterSiblings<T>(srcPath: string, entries: T[]): T[]

Convenience: filters an entry list to siblings of the supplied srcPath.

Parameters

NameTypeDescription
§srcPath
string
The entry's srcPath to exclude.
§entries
T[]
All discovered entries.

Returns

T[]
Entries whose srcPath differs from the supplied one.

Example

Building the sibling list for `bundle/`

const siblings = filterSiblings('bundle', allEntries)
§function

findDanglingEntryRefs(context: BuildContext): DanglingEntryRef[]

Finds cross-entry declaration references that name symbols the target entry does not export.
The per-entry d.ts pass rewrites any type import that resolves inside another entry's directory into a specifier targeting that entry ('..', './feature'), on the assumption that the entry re-exports what its subtree declares. When it does not, the emitted reference resolves to nothing: skipLibCheck (on in every consumer scaffold) hides the broken declaration file, and the symbol silently degrades to an error type in consumer code instead — an event handler's parameter turning implicitly any, a re-exported payload type becoming unusable. This pass reads the emitted entry declarations back and reports each such reference.

Parameters

NameTypeDescription
§context
BuildContext
Resolved build context.

Returns

DanglingEntryRef[]
One record per dangling reference; empty when the type surface resolves.

Example

Auditing the emitted declarations after the per-entry pass

const dangling = findDanglingEntryRefs(context)
§function

findOwningSibling(absolutePath: string, siblings: SiblingEntry[]): SiblingEntry

Returns the sibling entry whose directory contains absolutePath, or undefined when no sibling owns the path.

Parameters

NameTypeDescription
§absolutePath
string
Resolved absolute path of an import.
§siblings
SiblingEntry[]
Sibling-entry records.

Returns

SiblingEntry
Owning sibling entry, or undefined.

Example

Identifying a sibling for a resolved type import

const owner = findOwningSibling('/abs/dist/libs/foo/models/index.d.ts', siblings)
§function

flattenDeclarationPaths(context: BuildContext): void

Reshapes the nested declaration tree produced by tsc (with baseUrl=workspaceRoot) into the flat per-library structure consumers expect.
Without this step tsc emits declarations at dist/libs/<lib>/libs/<lib>/src/index.d.ts because it preserves the workspace-relative path. This primitive flattens that to dist/libs/<lib>/index.d.ts while preserving any nested platform / feature subdirectories.
The copy is recursive and unconditional, so internal non-entry subdirectories that any entry's index.d.ts re-exports from (e.g. shared/, lib/) are preserved rather than dropped. Per-source declarations left unreachable once the per-entry pass inlines each index.d.ts are removed afterwards by pruneOrphanDeclarations. Finally removes the leftover libs/, plugins/, and apps/ folders tsc created at the output root.

Parameters

NameTypeDescription
§context
BuildContext
Resolved build context. Uses projectRoot, outputPath, and workspaceRoot.

Example

Flattening declarations after a manual tsc invocation

flattenDeclarationPaths(context)
§function

generateDeclarations(context: BuildContext): Promise<GenerateDeclarationsResult>

Generates .d.ts and .d.ts.map files for every entry point in the project by spawning the workspace-local TypeScript compiler.
After tsc finishes, calls flattenDeclarationPaths to relocate the nested dist/<lib>/libs/<lib>/src/... structure that tsc emits with baseUrl=workspaceRoot back into the flat per-library shape consumers expect.

Parameters

NameTypeDescription
§context
BuildContext
Resolved build context. Provides project root, output path, tsconfig path, workspace root, and entry point discovery for the flatten step.

Returns

Promise<GenerateDeclarationsResult>
Promise resolving with tsc's exit status, captured stdout, and captured stderr.

Example

Generating declarations as part of a custom build

const result = await generateDeclarations(context)
console.log(result.stdout)
§function

pruneOrphanDeclarations(context: BuildContext): number

Removes redundant per-source .d.ts / .d.ts.map files left behind once the per-entry flatten has inlined each entry's declarations into a self-contained index.d.ts.
Walks the whole package tree (excluding _dependencies/) and deletes every declaration file not reachable from an entry-point index.d.ts via its transitive relative specifiers. Reachability — rather than a per-directory sweep — guarantees the invariant that after this runs no surviving .d.ts references a removed one: when the flatten made index.d.ts self-contained the reachable set collapses to the roots and every per-source sibling is pruned; when the flatten was skipped the siblings the public entry still re-exports (and anything they transitively re-export) stay live so the shipped types resolve. This removes the redundancy at the source so dist == the shipped tarball, demoting package.json#files to a backstop.
Safety rails:
  • Never touches anything inside _dependencies/.
  • A .d.ts.map is kept iff its sibling .d.ts is reachable.
  • A dynamic (non-literal) import(/require( in any reached declaration
aborts the prune and keeps every declaration file.

Parameters

NameTypeDescription
§context
BuildContext
Resolved build context.

Returns

number
The total number of .d.ts / .d.ts.map files removed.

Example

Pruning orphans after generateDeclarations

const removed = pruneOrphanDeclarations(context)
§function

runDtsPerEntry(context: BuildContext, monitor?: MemoryMonitor): Promise<void>

Runs the per-entry d.ts inlining pass: re-runs rollup-plugin-dts over every tsc-emitted entry .d.ts so (a) local per-source sibling re-exports (export … from './create-logger') are flattened into the entry's index.d.ts, and (b) bundled-dep type imports are routed through _dependencies/<dep>/index.d.ts rather than left as bare specifiers. This self-containment is what lets pruneOrphanDeclarations delete the orphaned per-source .d.ts afterwards without orphaning a still-referenced sibling.
Runs whenever the build bundles any dep — npm (bundledDeps) or workspace (workspaceBundledDeps); a package whose deps are all @hyperfrontend/* still needs the flatten. Entries whose tsc output is missing are skipped silently — the bundle phase may have skipped them deliberately (e.g., empty bundles).

Parameters

NameTypeDescription
§context
BuildContext
Resolved build context.
§monitor?
MemoryMonitor
Optional memory monitor invoked between jobs.

Example

Inlining bundled-dep types into every entry's .d.ts after tsc emission

await runDtsPerEntry(context)
§function

runDtsPrePass(context: BuildContext, monitor?: MemoryMonitor): Promise<void>

Runs the d.ts pre-pass: for each bundled dep, produces _dependencies/<dep>/index.d.ts by running rollup-plugin-dts over the dep's types entry. Cross-dep type imports are marked external so the per-entry d.ts pass can route them through _dependencies/.

Parameters

NameTypeDescription
§context
BuildContext
Resolved build context.
§monitor?
MemoryMonitor
Optional memory monitor invoked between jobs.

Example

Producing _dependencies/<dep>/index.d.ts for a self-contained build

await runDtsPrePass(context)
§function

verifyEntryTypeRefs(context: BuildContext): void

Fails the build when the emitted entry declarations reference symbols a sibling entry does not export, so a package can never publish a type surface that silently resolves to any in consumer code.

Parameters

NameTypeDescription
§context
BuildContext
Resolved build context.

Example

Verifying the type surface after the declaration passes

verifyEntryTypeRefs(context)

Interfaces

§interface

DanglingEntryRef

A reference from one entry's index.d.ts to a sibling entry that names symbols the sibling does not export.

Properties

§missing:string[]
Referenced names absent from the target entry's export list.
§source:string
Absolute path of the index.d.ts holding the reference.
§specifier:string
Specifier exactly as written in the referencing declaration.
§target:string
Absolute path of the entry index.d.ts the specifier resolves to.
§interface

EntryRef

A named reference into another module found in a declaration file.

Properties

§names:string[]
Names the statement pulls from the target module, as the target must export them.
§specifier:string
Specifier exactly as written in the declaration.
§interface

GenerateDeclarationsResult

Result of running tsc to emit declaration files.

Properties

§stderr:string
Captured stderr content from tsc.
§stdout:string
Captured stdout content from tsc.
§success:boolean
Whether the tsc invocation succeeded.
§interface

SiblingEntry

Sibling entry record consumed by createSiblingExternalizePlugin.
Each sibling represents another entry-point in the same library that has its own bundled index.d.ts. The per-entry d.ts pass externalizes imports that resolve into a sibling's directory so consumers see one canonical type surface (e.g., import type { EntryPoint } from '../models' instead of an inlined declaration in every entry).

Properties

§indexDtsPath:string
Absolute path to the entry's bundled index.d.ts.
§srcPath:string
Entry's srcPath (subpath under <outputPath>). '' for the package root.
§interface

SiblingResolverInput

Inputs to createSiblingExternalizePlugin.

Properties

§selfDtsPath:string
Absolute path to the current entry's bundled index.d.ts.
§selfSrcPath:string
srcPath of the entry the plugin is rewriting.
§siblings:SiblingEntry[]
Other entries' bundled index.d.ts records.