@hyperfrontend/builder/bundle/dependencies

dependencies

Per-format pre-pass + externalize plugin that build each third-party dep once into _dependencies/<dep>/, then route every entry's import of that dep through a relative path. Locked per Decision #38 (overview).

resolveBundledDeps(packageJsonPath, { isWorkspacePackage, include, exclude }) derives the third-party dep set from the project's package.json#dependencies, subtracts peerDependencies (those stay external) and any package matching the workspace predicate (those are inlined per the existing bundleWorkspaceDeps flow), and finally applies the caller's include / exclude overrides: exclude always wins over include, and neither can resurrect a peer or workspace dep.

createExternalizeBundledDepsPlugin({ deps, entryOutDir, format, depsRoot }) returns a Rollup plugin whose resolveId hook maps any import of a bundled dep (or its subpath) to a relative import that points at _dependencies/<dep>/index.<ext>. Node builtins and node:* specifiers are marked external untouched. Unrelated imports return null so the rest of the plugin chain handles them. Used by the per-entry passes for 'esm' / 'cjs' and by the d.ts pass for 'dts'.

runPrePass(jobs, { workerPath, monitor }) orchestrates the per-dep × per-format rollup invocations as forked Node child processes (Decision #39). Each child runs exactly one rollup invocation, writes its output, and emits a JSON report. Strictly sequential: concurrent workers would simultaneously pressure RAM. The orchestrator throws with the failed job's context if any worker exits non-zero or fails to write its report.

API Reference§

ƒ Functions

§function

buildWorkspaceRoutes(entries: WorkspaceBundledDep[]): WorkspaceBundledDepRoute[]

Groups WorkspaceBundledDep entries into per-package WorkspaceBundledDepRoute shapes consumable by createExternalizeBundledDepsPlugin.
Whole-surface deps emit a single route with no specifiers; sub-path deps emit a route enumerating each pre-passed specifier so the plugin can match exactly.
Parameters
NameTypeDescription
§entries
WorkspaceBundledDep[]Workspace bundled-dep entries from BuildContext.workspaceBundledDeps.
Returns
WorkspaceBundledDepRoute[]
Routes ready to feed into the externalize plugin.
Example

Building plugin routes from the build context

const routes = buildWorkspaceRoutes(context.workspaceBundledDeps)
createExternalizeBundledDepsPlugin({ deps, entryOutDir, format, depsRoot, workspaceRoutes: routes })
§function

createExternalizeBundledDepsPlugin(options: ExternalizeBundledDepsPluginOptions): Plugin

Returns a rollup plugin whose resolveId hook maps any import of a bundled dep (or its subpath) to a relative import that points at the pre-passed artifact under _dependencies/<dep>/. With workspaceRoutes populated, the plugin also routes workspace @hyperfrontend/ imports through the matching workspace chunk (whole-surface or sub-path mode per route).
The plugin marks node builtins (and node:
imports) as external so they survive untouched, and returns null for everything else so the rest of the plugin chain can resolve normally.
Parameters
NameTypeDescription
§options
ExternalizeBundledDepsPluginOptionsPlugin configuration.
Returns
Plugin
Rollup plugin.
Example

Routing imports of `rollup` to the pre-passed copy

const plugin = createExternalizeBundledDepsPlugin({
  deps: ['rollup'],
  entryOutDir: '/abs/dist/libs/foo/bundle/rollup',
  format: 'esm',
  depsRoot: '/abs/dist/libs/foo/_dependencies',
})
§function

loadWorkspacePathMappings(workspaceRoot: string): Map<string, string[]>

Loads tsconfig path-mappings from a workspace, preferring tsconfig.base.json at the workspace root and falling back to a tsconfig.json extends chain when the base form is absent.
Parameters
NameTypeDescription
§workspaceRoot
stringAbsolute workspace root.
Returns
Map<string, string[]>
Map keyed by tsconfig paths key (e.g. @hyperfrontend/logging), with values pointing at absolute source files.
Example

Loading the workspace's path-mapping table

const paths = loadWorkspacePathMappings('/abs/repo')
paths.get('@hyperfrontend/logging') // => ['/abs/repo/libs/logging/src/index.ts']
§function

relativeImport(fromDir: string, toFile: string): string

Computes the relative import specifier that a rollup output at fromDir needs to use to reach toFile. The result always uses POSIX separators and starts with ./ or ../ so node treats it as a relative path.
Parameters
NameTypeDescription
§fromDir
stringAbsolute directory containing the output file.
§toFile
stringAbsolute path to the target file.
Returns
string
Relative import specifier ready for inclusion in emitted code.
Example

Computing the relative path to a bundled dep

relativeImport('/abs/dist/libs/foo/bundle/rollup', '/abs/dist/libs/foo/_dependencies/rollup/index.esm.js')
// => '../../_dependencies/rollup/index.esm.js'
§function

resolveBundledDeps(packageJsonPath: string, options: ResolveBundledDepsOptions): string[]

Resolves the list of third-party dependencies that should be pre-passed (bundled once into _dependencies/<dep>/) for the build.
Algorithm:
  1. Read dependencies from the project's package.json.
  2. Subtract peerDependencies: those stay external.
  3. Subtract anything matching isWorkspacePackage: workspace deps are inlined per the existing flow.
  4. Apply include / exclude overrides.
The returned list is sorted and de-duplicated.
Parameters
NameTypeDescription
§packageJsonPath
stringAbsolute path to the project's package.json.
§options
ResolveBundledDepsOptionsCaller overrides.(default: {})
Returns
string[]
Sorted list of third-party package names to pre-pass.
Example

Resolving bundled deps for a workspace library

const deps = resolveBundledDeps('/abs/libs/foo/package.json', {
  isWorkspacePackage: (n) => n.startsWith('@hyperfrontend/'),
})
§function

resolveDefaultWorkerPath(startDir?: string): WorkerInvocation

Resolves the dependency pre-pass worker by self-locating it beside the running builder module: ascends from the module's own directory to the builder package root and returns the worker at bundle/dependencies/worker. This works whether the builder runs from its built dist, an installed node_modules copy, or melded into a host bundle under _dependencies/. The compiled index.cjs.js is preferred; an index.ts sibling resolves with the @swc-node/register loader for source-mode bootstrap.
Parameters
NameTypeDescription
§startDir?
stringDirectory to begin the ascent from. Defaults to the running module's directory; pass an explicit value to resolve from another anchor or under test.
Returns
WorkerInvocation
Worker invocation descriptor, or undefined if no worker is found under any ancestor.
Example

Locating the worker beside the builder

const invocation = resolveDefaultWorkerPath()
if (!invocation) throw new Error('builder worker artifact not found')
§function

resolveWorkspaceBundledDeps(packageJsonPath: string, workspaceRoot: string, options: ResolveWorkspaceBundledDepsOptions): ResolvedWorkspaceDepEntry[]

Resolves the list of workspace @hyperfrontend/* deps that should be hoisted into _dependencies/<name>(/<sub>)?/index.<ext>.js.
Algorithm:
  1. Read the project's package.json#dependencies, retain entries matching
isWorkspacePackage, and apply caller include / exclude overrides. Peer deps and excluded packages are skipped; include cannot resurrect a peer dep.
  1. Load workspace path-mappings (tsconfig paths).
  2. For each eligible workspace dep, apply the per-dep hoist policy
(options.policy, defaulting to 'sub-path'):
  • 'sub-path' (the zero-config default) emits one entry per resolvable
tsconfig specifier (root + every sub-path with a real source file), preserving sub-module granularity and supporting subpath-only packages that expose no root export.
  • 'whole-surface' is an explicit opt-in collapse: it emits a single entry
for the package root, so it requires the dep to expose a root export.
  1. The returned list is sorted by specifier for stable downstream ordering.
Because the caller requested bundleAllDeps, an eligible dep that cannot be fully resolved is a contract violation, not a soft skip. The function throws (rather than silently externalising) when an eligible dep has no resolvable tsconfig mapping, when a mapped source has no owning tsconfig, or when a dep is explicitly opted into 'whole-surface' yet exposes no root export, each with a message naming the dep and the remedy.
Parameters
NameTypeDescription
§packageJsonPath
stringAbsolute path to the project's package.json.
§workspaceRoot
stringAbsolute workspace root used to load tsconfig.base.json paths.
§options
ResolveWorkspaceBundledDepsOptionsCaller overrides + workspace-package predicate + per-package policy map.
Returns
ResolvedWorkspaceDepEntry[]
Resolved workspace-dep pre-pass entries sorted by specifier.
Example

Resolving workspace pre-pass entries for builder

const entries = resolveWorkspaceBundledDeps(
  '/abs/libs/builder/package.json',
  '/abs/repo',
  { isWorkspacePackage: (n) => n.startsWith('@hyperfrontend/') }
)
§function

runPrePass(jobs: PrePassJob[], options: RunPrePassOptions): Promise<PrePassResult[]>

Sequentially runs the supplied pre-pass jobs by forking a fresh Node child per invocation. Strict sequential execution is mandatory: concurrent children would simultaneously pressure RAM and OOM the container.
Each child writes a JSON report to a parent-supplied path; this function reads the report after the child exits and accumulates per-job statistics. If any worker exits non-zero or fails to produce a report, the function throws with the failed job's context.
The report directory is created in the OS temp dir and removed before returning, regardless of success or failure.
Parameters
NameTypeDescription
§jobs
PrePassJob[]Pre-pass jobs to run.
§options
RunPrePassOptionsWorker path + optional memory monitor.
Returns
Promise<PrePassResult[]>
One result per supplied job, in the order the jobs were given.
Example

Pre-passing rollup and one of its plugins

const results = await runPrePass(jobs, { workerPath: '/abs/dist/libs/builder/bundle/dependencies/worker.cjs.js' })
§function

runPrePassWorkerJob(job: PrePassWorkerJob): Promise<PrePassWorkerReport>

Runs a single pre-pass rollup invocation as described by job and writes the resulting report to job.reportPath.
Public so callers (and tests) can drive the worker logic without spawning a new Node process.
Parameters
NameTypeDescription
§job
PrePassWorkerJobJob spec describing the rollup invocation.
Returns
Promise<PrePassWorkerReport>
The on-disk report data the worker would have persisted.
Example

Driving the worker logic in-process for a fixture

const report = await runPrePassWorkerJob({ kind: 'js', dep: 'rollup', ... })

◈ Interfaces

§interface

ExternalizeBundledDepsPluginOptions

Inputs to createExternalizeBundledDepsPlugin.
Properties
§deps:string[]Bundled-dep package names. Any import of these (or their subpaths) is rerouted to _dependencies/<dep>/.
§depsRoot:stringAbsolute path to the _dependencies/ root.
§entryOutDir:stringAbsolute directory the rollup invocation writes its output to.
§format:ExternalizeFormatFormat being produced by this rollup invocation.
§workspaceRoutes?:WorkspaceBundledDepRoute[]Workspace bundled-dep routes. Imports of these packages reroute to _dependencies/<packageName>(/<sub>)?/.
§interface

PrePassJob

Single-rollup-invocation job description fed to a forked worker.
Properties
§dep:stringDep package name (or workspace specifier), e.g. "rollup" or "@hyperfrontend/logging".
§depsRoot?:stringAbsolute path to the project's _dependencies/ root. Required when npmDeps or workspaceRoutes is non-empty.
§format:"cjs" | "esm"Format produced by this rollup invocation.
§inputPath:stringAbsolute path to the dep's input entry.
§kind:PrePassJobKindPre-pass kind. js and dts cover npm bundled deps; workspace-js and workspace-dts cover workspace @hyperfrontend/* deps whose entries are TypeScript source.
§npmDeps?:string[]NPM bundled-dep names consumed by the worker's externalize plugin to rewrite cross-dep imports to relative paths under depsRoot. Disjoint from workspaceRoutes.
§otherDeps:string[]Other deps in the pre-pass set; prefix-matched and marked external so cross-dep imports stay link-time.
§otherWorkspaceSpecifiers?:string[]Sub-path-mode workspace specifiers in the pre-pass set; matched as exact specifier only. Used by workspace-* jobs so sibling sub-paths externalize cleanly (e.g., one built-in-copy/<x> chunk does not pull in another).
§outputPath:stringAbsolute path to the worker's output file.
§selfDtsPath?:stringAbsolute path to the input file's owning entry directory (used to compute sibling specifiers).
§selfSrcPath?:stringOwning entry's srcPath. Empty string for the package root.
§siblingEntries?:SiblingEntryDescriptor[]Sibling-entry descriptors used by the per-entry dts pass to externalize imports that resolve into another entry's directory. See SiblingEntryDescriptor. Empty / omitted for dep pre-pass jobs.
§tsConfigPath?:stringAbsolute path to the project's tsconfig (workspace-* jobs only).
§workspaceRoot?:stringAbsolute workspace root used as baseUrl for path-mapping resolution (workspace-* jobs only).
§workspaceRoutes?:WorkspaceBundledDepRoute[]Workspace bundled-dep routes consumed by the worker's externalize plugin. For self-pre-pass jobs (workspace-js / workspace-dts) this excludes the specifier or package being built so the chunk inlines its own internals.
§interface

PrePassResult

Result emitted by the worker for a single job.
Properties
§durationMs:numberWall-clock duration the worker reported, in ms.
§endHeapMB:numberHeap reported by the child at exit, in MB.
§endRssMB:numberRSS reported by the child at exit, in MB.
§job:PrePassJobThe job that produced this result.
§outputSize:numberOutput file size in bytes.
§interface

PrePassWorkerJob

Job spec consumed by the pre-pass worker via process.argv[2].
Each invocation produces exactly one rollup output and one JSON report at reportPath so the parent orchestrator can collect per-job statistics.
Properties
§dep:stringDep package name (or workspace specifier) being pre-passed.
§depsRoot?:stringAbsolute path to the project's _dependencies/ root. Required when npmDeps or workspaceRoutes is non-empty.
§format:"cjs" | "esm"Output format. JS jobs must use 'esm' or 'cjs'. dts jobs always use 'es' internally.
§inputPath:stringAbsolute path to the dep's entry (main / module for JS, types for dts, source .ts for workspace-*).
§kind:PrePassWorkerJobKindPre-pass kind. js and dts run the npm-dep pipeline; workspace-js and workspace-dts add @rollup/plugin-typescript (or rollup-plugin-dts's tsconfig integration) so TypeScript source workspace deps can be hoisted.
§npmDeps?:string[]NPM bundled-dep names consumed by the canonical externalize plugin to rewrite cross-dep imports to relative paths under depsRoot.
§otherDeps:string[]Other deps in the pre-pass set; marked external so cross-dep imports stay link-time. Prefix-matched.
§otherWorkspaceSpecifiers?:string[]Sub-path-mode workspace specifiers in the pre-pass set; matched as exact specifier only (e.g. @hyperfrontend/immutable-api-utils/built-in-copy/array). Used by workspace-* jobs so sibling sub-paths externalize cleanly without also externalizing every other sub-path on the same package.
§outputPath:stringAbsolute path to the output file.
§reportPath:stringAbsolute path where the worker writes its JSON report.
§selfDtsPath?:stringAbsolute path to the input file's owning entry directory (used to compute sibling specifiers).
§selfSrcPath?:stringOwning entry's srcPath (used for diagnostics). Empty string for the package root.
§siblingEntries?:SiblingEntry[]Sibling-entry descriptors used by the per-entry dts pass to externalize imports that resolve into another entry's directory. Empty / omitted for dep pre-pass jobs.
§tsConfigPath?:stringAbsolute path to the project's tsconfig (workspace-* jobs only).
§workspaceRoot?:stringAbsolute workspace root used as baseUrl for path-mapping resolution (workspace-* jobs only).
§workspaceRoutes?:WorkspaceBundledDepRoute[]Workspace bundled-dep routes consumed by the canonical externalize plugin. For self-pre-pass jobs (workspace-js / workspace-dts) this excludes the specifier or package being built so the chunk inlines its own internals.
§interface

PrePassWorkerReport

Memory + size summary written to reportPath when a worker exits cleanly.
Properties
§durationMs:numberWorker wall-clock duration in ms.
§endHeapMB:numberprocess.memoryUsage().heapUsed in MB at end of bundle.
§endRssMB:numberprocess.memoryUsage().rss in MB at end of bundle.
§outputSize:numberFinal on-disk size of the rollup output, in bytes.
§interface

ResolveBundledDepsOptions

Inputs to resolveBundledDeps.
Properties
§exclude?:string[]Skip these packages even if otherwise selected.
§include?:string[]Force-include packages even if absent from package.json#dependencies.
§isWorkspacePackage?:IsWorkspacePackagePredicatePredicate identifying workspace-internal packages; when matched, packages are NOT pre-passed.
§interface

ResolvedWorkspaceDepEntry

Resolved workspace-dep pre-pass entry.
Properties
§inputPath:stringAbsolute path to the source file (from tsconfig path-mapping).
§packageName:stringWorkspace package name, e.g. @hyperfrontend/logging.
§policy:WorkspaceDepHoistPolicyHoist policy applied to this entry's package; carried through so callers need not re-derive it.
§specifier:stringPublic import specifier this entry resolves: <packageName> or <packageName>/<subPath>.
§subPath:stringSub-path under the package; '' when the entry is the package root.
§tsConfigPath:stringAbsolute path to the dep's own tsconfig used by @rollup/plugin-typescript during pre-pass.
§interface

ResolveWorkspaceBundledDepsOptions

Inputs to resolveWorkspaceBundledDeps.
Properties
§exclude?:string[]Skip these packages even if otherwise selected.
§include?:string[]Force-include packages even if absent from package.json#dependencies.
§isWorkspacePackage:IsWorkspacePackagePredicatePredicate identifying workspace-internal packages.
§policy?:Record<string, WorkspaceDepHoistPolicy>Per-package hoist-policy override, keyed by package name. Packages absent from the map default to 'sub-path' (granular, zero-config). Set a package to 'whole-surface' to opt into collapsing its sub-paths onto the root chunk. No built-in entries; callers supply their own opinions.
§interface

RunPrePassOptions

Caller-supplied options threaded through runPrePass.
Properties
§execArgv?:string[]Extra arguments prepended to the worker invocation (e.g. ['--require', '@swc-node/register']).
§execPath?:stringOverride process.execPath for the spawned worker (used by tests).
§monitor?:MemoryMonitorOptional memory monitor invoked between every spawned worker.
§workerPath:stringAbsolute path to the worker entry script. Required; see resolveDefaultWorkerPath.
§interface

WorkspaceBundledDepRoute

Per-package workspace-dep routing entry.
  • policy: 'whole-surface' collapses every import of <packageName> (root or
sub-path) onto the single root chunk at _dependencies/<packageName>/index.<ext>.
  • policy: 'sub-path' matches each pre-passed specifier exactly; non-matched
sub-path imports of the same package fall through unchanged so the rest of the plugin chain can resolve / inline them.
Properties
§packageName:stringWorkspace package name (e.g. @hyperfrontend/logging).
§policy:"sub-path" | "whole-surface"Routing policy applied to imports of this package.
§specifiers?:string[]When policy === 'sub-path': the exact specifiers that were pre-passed.

◆ Types

§type

ChunkFormat

Module output format of the chunks being analyzed.
type ChunkFormat = "esm" | "cjs"
§type

ExternalizeFormat

Format produced by the rollup invocation that uses the plugin.
D.ts passes use 'dts' so the plugin maps imports to .d.ts siblings under _dependencies/<dep>/; JS passes use 'esm' / 'cjs'.
type ExternalizeFormat = "esm" | "cjs" | "dts"
§type

PrePassJobKind

Pre-pass kind. Mirrors PrePassWorkerJobKind on the worker side.
type PrePassJobKind = "js" | "dts" | "workspace-js" | "workspace-dts"