@hyperfrontend/builder/bundle/declarationsdeclarations
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
.d.ts (e.g., ../models, ../../models, ./bundle/declarations). Always omits the trailing
index.d.ts so both moduleResolution: 'bundler' and node accept it.Parameters
Returns
stringid 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'/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
| Name | Type | Description |
|---|---|---|
§input | SiblingResolverInput | Self + sibling entry descriptors. |
Returns
PluginExample
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' }],
})srcPath to produce the absolute path of the entry's bundled index.d.ts.Parameters
Returns
string<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'srcPath.Parameters
Returns
T[]srcPath differs from the supplied one.Example
Building the sibling list for `bundle/`
const siblings = filterSiblings('bundle', allEntries)absolutePath, or undefined when no sibling owns the path.Parameters
Returns
SiblingEntryundefined.Example
Identifying a sibling for a resolved type import
const owner = findOwningSibling('/abs/dist/libs/foo/models/index.d.ts', siblings)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
| Name | Type | Description |
|---|---|---|
§context | BuildContext | Resolved build context. Uses projectRoot, outputPath, and workspaceRoot. |
Example
Flattening declarations after a manual tsc invocation
flattenDeclarationPaths(context).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
| Name | Type | Description |
|---|---|---|
§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>Example
Generating declarations as part of a custom build
const result = await generateDeclarations(context)
console.log(result.stdout).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.mapis kept iff its sibling.d.tsis reachable. - A dynamic (non-literal)
import(/require(in any reached declaration
Parameters
| Name | Type | Description |
|---|---|---|
§context | BuildContext | Resolved build context. |
Returns
number.d.ts / .d.ts.map files removed.Example
Pruning orphans after generateDeclarations
const removed = pruneOrphanDeclarations(context)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
Example
Inlining bundled-dep types into every entry's .d.ts after tsc emission
await runDtsPerEntry(context)_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
Example
Producing _dependencies/<dep>/index.d.ts for a self-contained build
await runDtsPrePass(context)◈ Interfaces
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).