@hyperfrontend/builder 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/_dependencies/@hyperfrontend/project-scope/core/fs/index.cjs.js +10 -3
  3. package/_dependencies/@hyperfrontend/project-scope/core/fs/index.esm.js +10 -3
  4. package/_dependencies/@hyperfrontend/project-scope/core/index.cjs.js +16 -6
  5. package/_dependencies/@hyperfrontend/project-scope/core/index.esm.js +16 -6
  6. package/_dependencies/@hyperfrontend/project-scope/core/path/index.cjs.js +4 -5
  7. package/_dependencies/@hyperfrontend/project-scope/core/path/index.esm.js +5 -4
  8. package/_dependencies/@hyperfrontend/project-scope/project/root/index.cjs.js +5 -1
  9. package/_dependencies/@hyperfrontend/project-scope/project/root/index.esm.js +5 -1
  10. package/bin/hf-build.js +110 -82
  11. package/bin/hf-build.linux-x64 +0 -0
  12. package/bin/index.cjs.js +82 -36
  13. package/bin/index.esm.js +82 -36
  14. package/bin/script/index.cjs.js +91 -45
  15. package/bin/script/index.esm.js +81 -35
  16. package/bundle/declarations/dts-per-entry.d.ts.map +1 -1
  17. package/bundle/declarations/dts-pre-pass.d.ts.map +1 -1
  18. package/bundle/declarations/index.cjs.js +118 -72
  19. package/bundle/declarations/index.esm.js +83 -37
  20. package/bundle/dependencies/index.cjs.js +95 -49
  21. package/bundle/dependencies/index.d.ts +13 -24
  22. package/bundle/dependencies/index.esm.js +79 -33
  23. package/bundle/dependencies/pre-pass.d.ts +13 -24
  24. package/bundle/dependencies/pre-pass.d.ts.map +1 -1
  25. package/bundle/index.cjs.js +203 -175
  26. package/bundle/index.esm.js +107 -79
  27. package/bundle/rollup/dispatch.d.ts +12 -14
  28. package/bundle/rollup/dispatch.d.ts.map +1 -1
  29. package/bundle/rollup/index.cjs.js +75 -29
  30. package/bundle/rollup/index.d.ts +14 -16
  31. package/bundle/rollup/index.esm.js +76 -30
  32. package/bundle/worker-locator.d.ts +47 -0
  33. package/bundle/worker-locator.d.ts.map +1 -0
  34. package/index.cjs.js +112 -84
  35. package/index.esm.js +113 -85
  36. package/package.json +31 -3
@@ -4,7 +4,7 @@ import { join as join$2, exists, relativePath, removeDirectory, readFileContent,
4
4
  import { from } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/array/index.esm.js';
5
5
  import { createSet } from '../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/set/index.esm.js';
6
6
  import { createRequire } from 'node:module';
7
- import { join as join$1, isAbsolute, resolve, dirname } from 'node:path';
7
+ import { join as join$1, dirname, isAbsolute, resolve } from 'node:path';
8
8
  import { spawn } from 'node:child_process';
9
9
  import { existsSync, rmSync, mkdtempSync, readFileSync, cpSync, statSync, unlinkSync } from 'node:fs';
10
10
  import { tmpdir } from 'node:os';
@@ -23,6 +23,70 @@ import { collectChunkSpecifiers, hasDynamicSpecifier } from '../../_shared/bundl
23
23
  import { entryDirOf } from '../../_shared/bundle/fs/entry-dir/index.esm.js';
24
24
  import { isUnderDir } from '../../_shared/bundle/fs/under-dir/index.esm.js';
25
25
 
26
+ const SWC_NODE_REGISTER = '@swc-node/register';
27
+ const probeDir = (dir, offset) => {
28
+ const compiled = join$1(dir, ...offset, 'index.cjs.js');
29
+ if (existsSync(compiled))
30
+ return { path: compiled, execArgv: [] };
31
+ const source = join$1(dir, ...offset, 'index.ts');
32
+ if (existsSync(source))
33
+ return { path: source, execArgv: ['--require', SWC_NODE_REGISTER] };
34
+ return undefined;
35
+ };
36
+ /**
37
+ * Directory of the currently-executing builder module, read from `__dirname`.
38
+ * It is present in the shipped CommonJS build, in the `@swc-node/register` source
39
+ * bootstrap, and under the test runner, so callers get their own on-disk location
40
+ * and can find the worker beside them however the builder was packaged.
41
+ *
42
+ * @returns Absolute directory path of the running module.
43
+ *
44
+ * @example Anchoring a lookup beside the running builder
45
+ * ```typescript
46
+ * const invocation = ascendForWorker(['bundle', 'rollup', 'worker'], currentModuleDir())
47
+ * ```
48
+ */
49
+ const currentModuleDir = () => {
50
+ /* istanbul ignore if -- @preserve the ESM build has no __dirname; the shipped CommonJS build and the test runner always define it, so this guard is unreachable under test */
51
+ if (typeof __dirname === 'undefined') {
52
+ throw createError('@hyperfrontend/builder self-location requires the CommonJS build; drive build() from the CommonJS entry point');
53
+ }
54
+ return __dirname;
55
+ };
56
+ /**
57
+ * Locate a builder worker entry by ascending from a start directory toward the
58
+ * filesystem root, returning the first ancestor at which the worker exists at the
59
+ * given in-package offset. The compiled `index.cjs.js` is preferred; an `index.ts`
60
+ * sibling (source-mode bootstrap) resolves with the swc loader attached.
61
+ *
62
+ * The worker ships inside the builder's own package, so resolving relative to the
63
+ * running module finds it wherever the package lives — built dist, an installed
64
+ * `node_modules` copy, or melded into a host bundle under `_dependencies/`.
65
+ *
66
+ * @param offset - In-package path segments to the worker directory (e.g. `['bundle', 'rollup', 'worker']`).
67
+ * @param startDir - Directory to begin the ascent from. Defaults to the running module's directory; pass an explicit value to resolve from another anchor or under test.
68
+ * @returns The resolved worker invocation, or `undefined` if no worker exists at the offset under any ancestor.
69
+ *
70
+ * @example Resolving the rollup worker from the running module
71
+ * ```typescript
72
+ * const invocation = ascendForWorker(['bundle', 'rollup', 'worker'])
73
+ * if (!invocation) throw new Error('builder rollup worker not found beside the builder module')
74
+ * ```
75
+ */
76
+ const ascendForWorker = (offset, startDir = currentModuleDir()) => {
77
+ let dir = startDir;
78
+ let parent = dirname(dir);
79
+ // how: probe each level then step up; the final probe covers the filesystem root, where parent === dir
80
+ while (parent !== dir) {
81
+ const found = probeDir(dir, offset);
82
+ if (found)
83
+ return found;
84
+ dir = parent;
85
+ parent = dirname(dir);
86
+ }
87
+ return probeDir(dir, offset);
88
+ };
89
+
26
90
  const log$4 = logger.channel('builder:bundle:dependencies:pre-pass');
27
91
  const REPORT_DIR_PREFIX = 'hf-builder-prepass-';
28
92
  const createReportDir = () => mkdtempSync(join$2(tmpdir(), REPORT_DIR_PREFIX));
@@ -59,43 +123,25 @@ const readReport = (reportPath, job) => {
59
123
  const data = parse(readFileSync(reportPath, 'utf8'));
60
124
  return { job, outputSize: data.outputSize, endHeapMB: data.endHeapMB, endRssMB: data.endRssMB, durationMs: data.durationMs };
61
125
  };
62
- const SWC_NODE_REGISTER = '@swc-node/register';
63
- const swcNodeAvailable = (workspaceRoot) => existsSync(join$2(workspaceRoot, 'node_modules', '@swc-node', 'register', 'index.js'));
64
126
  /**
65
- * Default worker-path resolution: prefers the built-and-published artifact, falls
66
- * back to the workspace dist path, and finally to the in-source TypeScript file
67
- * via `@swc-node/register` (bootstrap case where builder is building itself for
68
- * the first time and the dist worker doesn't exist yet).
69
- *
70
- * Looks at, in order:
71
- * 1. `<workspaceRoot>/dist/libs/builder/bundle/dependencies/worker/index.cjs.js`
72
- * 2. `<workspaceRoot>/node_modules/@hyperfrontend/builder/bundle/dependencies/worker/index.cjs.js`
73
- * 3. `<workspaceRoot>/libs/builder/src/bundle/dependencies/worker/index.ts` (with `--require \@swc-node/register`)
74
- *
75
- * @param workspaceRoot - Absolute workspace root.
76
- * @returns Worker invocation descriptor, or `undefined` if no candidate exists.
77
- *
78
- * @example Locating the worker for an in-workspace consumer
127
+ * Resolves the dependency pre-pass worker by self-locating it beside the running
128
+ * builder module: ascends from the module's own directory to the builder package
129
+ * root and returns the worker at `bundle/dependencies/worker`. This works whether
130
+ * the builder runs from its built dist, an installed `node_modules` copy, or
131
+ * melded into a host bundle under `_dependencies/`. The compiled `index.cjs.js`
132
+ * is preferred; an `index.ts` sibling resolves with the `@swc-node/register`
133
+ * loader for source-mode bootstrap.
134
+ *
135
+ * @param startDir - Directory to begin the ascent from. Defaults to the running module's directory; pass an explicit value to resolve from another anchor or under test.
136
+ * @returns Worker invocation descriptor, or `undefined` if no worker is found under any ancestor.
137
+ *
138
+ * @example Locating the worker beside the builder
79
139
  * ```typescript
80
- * const invocation = resolveDefaultWorkerPath('/abs/repo')
140
+ * const invocation = resolveDefaultWorkerPath()
81
141
  * if (!invocation) throw new Error('builder worker artifact not found')
82
142
  * ```
83
143
  */
84
- const resolveDefaultWorkerPath = (workspaceRoot) => {
85
- const distCandidates = [
86
- join$2(workspaceRoot, 'dist', 'libs', 'builder', 'bundle', 'dependencies', 'worker', 'index.cjs.js'),
87
- join$2(workspaceRoot, 'node_modules', '@hyperfrontend', 'builder', 'bundle', 'dependencies', 'worker', 'index.cjs.js'),
88
- ];
89
- for (const path of distCandidates) {
90
- if (existsSync(path))
91
- return { path, execArgv: [] };
92
- }
93
- const sourcePath = join$2(workspaceRoot, 'libs', 'builder', 'src', 'bundle', 'dependencies', 'worker', 'index.ts');
94
- if (existsSync(sourcePath) && swcNodeAvailable(workspaceRoot)) {
95
- return { path: sourcePath, execArgv: ['--require', SWC_NODE_REGISTER] };
96
- }
97
- return undefined;
98
- };
144
+ const resolveDefaultWorkerPath = (startDir) => ascendForWorker(['bundle', 'dependencies', 'worker'], startDir);
99
145
  /**
100
146
  * Sequentially runs the supplied pre-pass jobs by forking a fresh Node child
101
147
  * per invocation. Strict sequential execution is mandatory — concurrent
@@ -401,9 +447,9 @@ const buildJobs$1 = (entries, context) => {
401
447
  const runDtsPerEntry = async (context, monitor) => {
402
448
  if (context.bundledDeps.length === 0 && context.workspaceBundledDeps.length === 0)
403
449
  return;
404
- const invocation = resolveDefaultWorkerPath(context.workspaceRoot);
450
+ const invocation = resolveDefaultWorkerPath();
405
451
  if (!invocation) {
406
- throw createError('bundleAllDeps is enabled but the pre-pass worker artifact was not found for the per-entry d.ts pass.');
452
+ throw createError('bundleAllDeps is enabled but the pre-pass worker could not be located beside the builder module for the per-entry d.ts pass.');
407
453
  }
408
454
  const jobs = buildJobs$1(context.entryPointDiscovery.entryPoints, context);
409
455
  if (jobs.length === 0) {
@@ -585,9 +631,9 @@ const buildWorkspaceJobs = (context, npmDeps) => {
585
631
  const runDtsPrePass = async (context, monitor) => {
586
632
  if (context.bundledDeps.length === 0 && context.workspaceBundledDeps.length === 0)
587
633
  return;
588
- const invocation = resolveDefaultWorkerPath(context.workspaceRoot);
634
+ const invocation = resolveDefaultWorkerPath();
589
635
  if (!invocation) {
590
- throw createError('bundleAllDeps is enabled but the pre-pass worker artifact was not found for the d.ts pre-pass.');
636
+ throw createError('bundleAllDeps is enabled but the pre-pass worker could not be located beside the builder module for the d.ts pre-pass.');
591
637
  }
592
638
  const npmJobs = buildJobs(context.bundledDeps, context);
593
639
  const workspaceJobs = buildWorkspaceJobs(context, context.bundledDeps);
@@ -5,11 +5,11 @@ const node_path = require('node:path');
5
5
  const node_child_process = require('node:child_process');
6
6
  const node_fs = require('node:fs');
7
7
  const node_os = require('node:os');
8
- const index_cjs_js$4 = require('../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.cjs.js');
9
- const index_cjs_js$3 = require('../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/json/index.cjs.js');
10
- const index_cjs_js$2 = require('../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/promise/index.cjs.js');
11
- const index_cjs_js = require('../../_dependencies/@hyperfrontend/logging/index.cjs.js');
12
- const index_cjs_js$1 = require('../../_dependencies/@hyperfrontend/project-scope/core/index.cjs.js');
8
+ const index_cjs_js = require('../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.cjs.js');
9
+ const index_cjs_js$4 = require('../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/json/index.cjs.js');
10
+ const index_cjs_js$3 = require('../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/promise/index.cjs.js');
11
+ const index_cjs_js$1 = require('../../_dependencies/@hyperfrontend/logging/index.cjs.js');
12
+ const index_cjs_js$2 = require('../../_dependencies/@hyperfrontend/project-scope/core/index.cjs.js');
13
13
  const index_cjs_js$7 = require('../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/array/index.cjs.js');
14
14
  const index_cjs_js$6 = require('../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.cjs.js');
15
15
  const index_cjs_js$5 = require('../../_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/set/index.cjs.js');
@@ -31,13 +31,77 @@ const json__default = /*#__PURE__*/_interopDefaultCompat(json);
31
31
  const nodeResolve__default = /*#__PURE__*/_interopDefaultCompat(nodeResolve);
32
32
  const typescript__default = /*#__PURE__*/_interopDefaultCompat(typescript);
33
33
 
34
- const log = index_cjs_js.logger.channel('builder:bundle:dependencies:pre-pass');
34
+ const SWC_NODE_REGISTER = '@swc-node/register';
35
+ const probeDir = (dir, offset) => {
36
+ const compiled = node_path.join(dir, ...offset, 'index.cjs.js');
37
+ if (node_fs.existsSync(compiled))
38
+ return { path: compiled, execArgv: [] };
39
+ const source = node_path.join(dir, ...offset, 'index.ts');
40
+ if (node_fs.existsSync(source))
41
+ return { path: source, execArgv: ['--require', SWC_NODE_REGISTER] };
42
+ return undefined;
43
+ };
44
+ /**
45
+ * Directory of the currently-executing builder module, read from `__dirname`.
46
+ * It is present in the shipped CommonJS build, in the `@swc-node/register` source
47
+ * bootstrap, and under the test runner, so callers get their own on-disk location
48
+ * and can find the worker beside them however the builder was packaged.
49
+ *
50
+ * @returns Absolute directory path of the running module.
51
+ *
52
+ * @example Anchoring a lookup beside the running builder
53
+ * ```typescript
54
+ * const invocation = ascendForWorker(['bundle', 'rollup', 'worker'], currentModuleDir())
55
+ * ```
56
+ */
57
+ const currentModuleDir = () => {
58
+ /* istanbul ignore if -- @preserve the ESM build has no __dirname; the shipped CommonJS build and the test runner always define it, so this guard is unreachable under test */
59
+ if (typeof __dirname === 'undefined') {
60
+ throw index_cjs_js.createError('@hyperfrontend/builder self-location requires the CommonJS build; drive build() from the CommonJS entry point');
61
+ }
62
+ return __dirname;
63
+ };
64
+ /**
65
+ * Locate a builder worker entry by ascending from a start directory toward the
66
+ * filesystem root, returning the first ancestor at which the worker exists at the
67
+ * given in-package offset. The compiled `index.cjs.js` is preferred; an `index.ts`
68
+ * sibling (source-mode bootstrap) resolves with the swc loader attached.
69
+ *
70
+ * The worker ships inside the builder's own package, so resolving relative to the
71
+ * running module finds it wherever the package lives — built dist, an installed
72
+ * `node_modules` copy, or melded into a host bundle under `_dependencies/`.
73
+ *
74
+ * @param offset - In-package path segments to the worker directory (e.g. `['bundle', 'rollup', 'worker']`).
75
+ * @param startDir - Directory to begin the ascent from. Defaults to the running module's directory; pass an explicit value to resolve from another anchor or under test.
76
+ * @returns The resolved worker invocation, or `undefined` if no worker exists at the offset under any ancestor.
77
+ *
78
+ * @example Resolving the rollup worker from the running module
79
+ * ```typescript
80
+ * const invocation = ascendForWorker(['bundle', 'rollup', 'worker'])
81
+ * if (!invocation) throw new Error('builder rollup worker not found beside the builder module')
82
+ * ```
83
+ */
84
+ const ascendForWorker = (offset, startDir = currentModuleDir()) => {
85
+ let dir = startDir;
86
+ let parent = node_path.dirname(dir);
87
+ // how: probe each level then step up; the final probe covers the filesystem root, where parent === dir
88
+ while (parent !== dir) {
89
+ const found = probeDir(dir, offset);
90
+ if (found)
91
+ return found;
92
+ dir = parent;
93
+ parent = node_path.dirname(dir);
94
+ }
95
+ return probeDir(dir, offset);
96
+ };
97
+
98
+ const log = index_cjs_js$1.logger.channel('builder:bundle:dependencies:pre-pass');
35
99
  const REPORT_DIR_PREFIX = 'hf-builder-prepass-';
36
- const createReportDir = () => node_fs.mkdtempSync(index_cjs_js$1.join(node_os.tmpdir(), REPORT_DIR_PREFIX));
37
- const reportPathFor = (reportDir, job, index) => index_cjs_js$1.join(reportDir, `${index}-${job.dep.replace(/\//g, '__')}-${job.kind}-${job.format}.json`);
38
- const runOne = (job, reportPath, options) => index_cjs_js$2.createPromise((resolve, reject) => {
100
+ const createReportDir = () => node_fs.mkdtempSync(index_cjs_js$2.join(node_os.tmpdir(), REPORT_DIR_PREFIX));
101
+ const reportPathFor = (reportDir, job, index) => index_cjs_js$2.join(reportDir, `${index}-${job.dep.replace(/\//g, '__')}-${job.kind}-${job.format}.json`);
102
+ const runOne = (job, reportPath, options) => index_cjs_js$3.createPromise((resolve, reject) => {
39
103
  const execPath = options.execPath ?? process.execPath;
40
- const argv = [...(options.execArgv ?? []), options.workerPath, index_cjs_js$3.stringify({ ...job, reportPath })];
104
+ const argv = [...(options.execArgv ?? []), options.workerPath, index_cjs_js$4.stringify({ ...job, reportPath })];
41
105
  const child = node_child_process.spawn(execPath, argv, { stdio: ['ignore', 'pipe', 'pipe'] });
42
106
  let capturedStderr = '';
43
107
  child.stdout?.on('data', (chunk) => {
@@ -49,12 +113,12 @@ const runOne = (job, reportPath, options) => index_cjs_js$2.createPromise((resol
49
113
  process.stderr.write(text);
50
114
  });
51
115
  child.on('error', (error) => {
52
- reject(index_cjs_js$4.createError(`pre-pass worker for ${job.dep} (${job.kind}/${job.format}) failed to spawn: ${error.message}`));
116
+ reject(index_cjs_js.createError(`pre-pass worker for ${job.dep} (${job.kind}/${job.format}) failed to spawn: ${error.message}`));
53
117
  });
54
118
  child.on('exit', (code) => {
55
119
  if (code !== 0) {
56
120
  const tail = capturedStderr.trim().split('\n').slice(-10).join('\n');
57
- reject(index_cjs_js$4.createError(`pre-pass worker for ${job.dep} (${job.kind}/${job.format}) exited with code ${code}\n${tail}`));
121
+ reject(index_cjs_js.createError(`pre-pass worker for ${job.dep} (${job.kind}/${job.format}) exited with code ${code}\n${tail}`));
58
122
  return;
59
123
  }
60
124
  resolve();
@@ -62,48 +126,30 @@ const runOne = (job, reportPath, options) => index_cjs_js$2.createPromise((resol
62
126
  });
63
127
  const readReport = (reportPath, job) => {
64
128
  if (!node_fs.existsSync(reportPath)) {
65
- throw index_cjs_js$4.createError(`pre-pass worker for ${job.dep} (${job.kind}/${job.format}) did not write a report at ${reportPath}`);
129
+ throw index_cjs_js.createError(`pre-pass worker for ${job.dep} (${job.kind}/${job.format}) did not write a report at ${reportPath}`);
66
130
  }
67
- const data = index_cjs_js$3.parse(node_fs.readFileSync(reportPath, 'utf8'));
131
+ const data = index_cjs_js$4.parse(node_fs.readFileSync(reportPath, 'utf8'));
68
132
  return { job, outputSize: data.outputSize, endHeapMB: data.endHeapMB, endRssMB: data.endRssMB, durationMs: data.durationMs };
69
133
  };
70
- const SWC_NODE_REGISTER = '@swc-node/register';
71
- const swcNodeAvailable = (workspaceRoot) => node_fs.existsSync(index_cjs_js$1.join(workspaceRoot, 'node_modules', '@swc-node', 'register', 'index.js'));
72
134
  /**
73
- * Default worker-path resolution: prefers the built-and-published artifact, falls
74
- * back to the workspace dist path, and finally to the in-source TypeScript file
75
- * via `@swc-node/register` (bootstrap case where builder is building itself for
76
- * the first time and the dist worker doesn't exist yet).
77
- *
78
- * Looks at, in order:
79
- * 1. `<workspaceRoot>/dist/libs/builder/bundle/dependencies/worker/index.cjs.js`
80
- * 2. `<workspaceRoot>/node_modules/@hyperfrontend/builder/bundle/dependencies/worker/index.cjs.js`
81
- * 3. `<workspaceRoot>/libs/builder/src/bundle/dependencies/worker/index.ts` (with `--require \@swc-node/register`)
82
- *
83
- * @param workspaceRoot - Absolute workspace root.
84
- * @returns Worker invocation descriptor, or `undefined` if no candidate exists.
85
- *
86
- * @example Locating the worker for an in-workspace consumer
135
+ * Resolves the dependency pre-pass worker by self-locating it beside the running
136
+ * builder module: ascends from the module's own directory to the builder package
137
+ * root and returns the worker at `bundle/dependencies/worker`. This works whether
138
+ * the builder runs from its built dist, an installed `node_modules` copy, or
139
+ * melded into a host bundle under `_dependencies/`. The compiled `index.cjs.js`
140
+ * is preferred; an `index.ts` sibling resolves with the `@swc-node/register`
141
+ * loader for source-mode bootstrap.
142
+ *
143
+ * @param startDir - Directory to begin the ascent from. Defaults to the running module's directory; pass an explicit value to resolve from another anchor or under test.
144
+ * @returns Worker invocation descriptor, or `undefined` if no worker is found under any ancestor.
145
+ *
146
+ * @example Locating the worker beside the builder
87
147
  * ```typescript
88
- * const invocation = resolveDefaultWorkerPath('/abs/repo')
148
+ * const invocation = resolveDefaultWorkerPath()
89
149
  * if (!invocation) throw new Error('builder worker artifact not found')
90
150
  * ```
91
151
  */
92
- const resolveDefaultWorkerPath = (workspaceRoot) => {
93
- const distCandidates = [
94
- index_cjs_js$1.join(workspaceRoot, 'dist', 'libs', 'builder', 'bundle', 'dependencies', 'worker', 'index.cjs.js'),
95
- index_cjs_js$1.join(workspaceRoot, 'node_modules', '@hyperfrontend', 'builder', 'bundle', 'dependencies', 'worker', 'index.cjs.js'),
96
- ];
97
- for (const path of distCandidates) {
98
- if (node_fs.existsSync(path))
99
- return { path, execArgv: [] };
100
- }
101
- const sourcePath = index_cjs_js$1.join(workspaceRoot, 'libs', 'builder', 'src', 'bundle', 'dependencies', 'worker', 'index.ts');
102
- if (node_fs.existsSync(sourcePath) && swcNodeAvailable(workspaceRoot)) {
103
- return { path: sourcePath, execArgv: ['--require', SWC_NODE_REGISTER] };
104
- }
105
- return undefined;
106
- };
152
+ const resolveDefaultWorkerPath = (startDir) => ascendForWorker(['bundle', 'dependencies', 'worker'], startDir);
107
153
  /**
108
154
  * Sequentially runs the supplied pre-pass jobs by forking a fresh Node child
109
155
  * per invocation. Strict sequential execution is mandatory — concurrent
@@ -425,13 +471,13 @@ const resolveWorkspaceBundledDeps = (packageJsonPath, workspaceRoot, options) =>
425
471
  for (const packageName of filtered) {
426
472
  const matches = collectMatchingSpecifiers(packageName, mappings);
427
473
  if (matches.length === 0) {
428
- throw index_cjs_js$4.createError(`bundleAllDeps: workspace dependency "${packageName}" has no resolvable tsconfig path mapping under ${workspaceRoot} (no matching paths entry, or its mapped source files are missing), so it cannot be bundled. Add a "${packageName}" (or "${packageName}/<sub-path>") entry to tsconfig.base.json paths pointing at an existing source file, or exclude it via bundleAllDeps.exclude.`);
474
+ throw index_cjs_js.createError(`bundleAllDeps: workspace dependency "${packageName}" has no resolvable tsconfig path mapping under ${workspaceRoot} (no matching paths entry, or its mapped source files are missing), so it cannot be bundled. Add a "${packageName}" (or "${packageName}/<sub-path>") entry to tsconfig.base.json paths pointing at an existing source file, or exclude it via bundleAllDeps.exclude.`);
429
475
  }
430
476
  const policy = policyFor(packageName);
431
477
  const pushEntry = (specifier, inputPath) => {
432
478
  const tsConfigPath = resolveTsConfigForPackage(inputPath);
433
479
  if (!tsConfigPath) {
434
- throw index_cjs_js$4.createError(`bundleAllDeps: workspace dependency "${specifier}" resolves to ${inputPath}, but no tsconfig (tsconfig.lib.json / tsconfig.json) was found in its project root, so it cannot be transformed during bundling.`);
480
+ throw index_cjs_js.createError(`bundleAllDeps: workspace dependency "${specifier}" resolves to ${inputPath}, but no tsconfig (tsconfig.lib.json / tsconfig.json) was found in its project root, so it cannot be transformed during bundling.`);
435
481
  }
436
482
  const split = splitSpecifier(specifier);
437
483
  results.push({ packageName, subPath: split.subPath, specifier, inputPath, tsConfigPath, policy });
@@ -439,7 +485,7 @@ const resolveWorkspaceBundledDeps = (packageJsonPath, workspaceRoot, options) =>
439
485
  if (policy === 'whole-surface') {
440
486
  const root = matches.find((m) => m.specifier === packageName);
441
487
  if (!root) {
442
- throw index_cjs_js$4.createError(`bundleAllDeps: workspace dependency "${packageName}" is explicitly set to the 'whole-surface' hoist policy but is subpath-only (no root export; tsconfig exposes only ${matches.map((m) => `"${m.specifier}"`).join(', ')}). Remove the 'whole-surface' override for it (sub-path is the default), or add a root export.`);
488
+ throw index_cjs_js.createError(`bundleAllDeps: workspace dependency "${packageName}" is explicitly set to the 'whole-surface' hoist policy but is subpath-only (no root export; tsconfig exposes only ${matches.map((m) => `"${m.specifier}"`).join(', ')}). Remove the 'whole-surface' override for it (sub-path is the default), or add a root export.`);
443
489
  }
444
490
  pushEntry(root.specifier, root.inputPath);
445
491
  continue;
@@ -1,6 +1,7 @@
1
1
  import { Plugin } from '../../_dependencies/rollup/index.js';
2
2
  import { WorkspaceBundledDep, IsWorkspacePackagePredicate, WorkspaceDepHoistPolicy } from '../../models';
3
3
  import { MemoryMonitor } from '../../memory';
4
+ import { WorkerInvocation } from '..';
4
5
  export { PrePassWorkerJob, PrePassWorkerReport, runPrePassWorkerJob } from './worker';
5
6
 
6
7
  /**
@@ -199,36 +200,24 @@ interface RunPrePassOptions {
199
200
  execArgv?: string[];
200
201
  }
201
202
  /**
202
- * Resolved worker invocation: absolute path + any extra Node args (e.g. `--require \@swc-node/register`
203
- * when the worker is loaded from TypeScript source during a bootstrap build).
204
- */
205
- interface WorkerInvocation {
206
- /** Absolute path to the worker entry script. */
207
- path: string;
208
- /** Extra args prepended to the spawned child's argv. */
209
- execArgv: string[];
210
- }
211
- /**
212
- * Default worker-path resolution: prefers the built-and-published artifact, falls
213
- * back to the workspace dist path, and finally to the in-source TypeScript file
214
- * via `@swc-node/register` (bootstrap case where builder is building itself for
215
- * the first time and the dist worker doesn't exist yet).
203
+ * Resolves the dependency pre-pass worker by self-locating it beside the running
204
+ * builder module: ascends from the module's own directory to the builder package
205
+ * root and returns the worker at `bundle/dependencies/worker`. This works whether
206
+ * the builder runs from its built dist, an installed `node_modules` copy, or
207
+ * melded into a host bundle under `_dependencies/`. The compiled `index.cjs.js`
208
+ * is preferred; an `index.ts` sibling resolves with the `@swc-node/register`
209
+ * loader for source-mode bootstrap.
216
210
  *
217
- * Looks at, in order:
218
- * 1. `<workspaceRoot>/dist/libs/builder/bundle/dependencies/worker/index.cjs.js`
219
- * 2. `<workspaceRoot>/node_modules/@hyperfrontend/builder/bundle/dependencies/worker/index.cjs.js`
220
- * 3. `<workspaceRoot>/libs/builder/src/bundle/dependencies/worker/index.ts` (with `--require \@swc-node/register`)
221
- *
222
- * @param workspaceRoot - Absolute workspace root.
223
- * @returns Worker invocation descriptor, or `undefined` if no candidate exists.
211
+ * @param startDir - Directory to begin the ascent from. Defaults to the running module's directory; pass an explicit value to resolve from another anchor or under test.
212
+ * @returns Worker invocation descriptor, or `undefined` if no worker is found under any ancestor.
224
213
  *
225
- * @example Locating the worker for an in-workspace consumer
214
+ * @example Locating the worker beside the builder
226
215
  * ```typescript
227
- * const invocation = resolveDefaultWorkerPath('/abs/repo')
216
+ * const invocation = resolveDefaultWorkerPath()
228
217
  * if (!invocation) throw new Error('builder worker artifact not found')
229
218
  * ```
230
219
  */
231
- declare const resolveDefaultWorkerPath: (workspaceRoot: string) => WorkerInvocation | undefined;
220
+ declare const resolveDefaultWorkerPath: (startDir?: string) => WorkerInvocation | undefined;
232
221
  /**
233
222
  * Sequentially runs the supplied pre-pass jobs by forking a fresh Node child
234
223
  * per invocation. Strict sequential execution is mandatory — concurrent
@@ -1,5 +1,5 @@
1
1
  import { isBuiltin } from 'node:module';
2
- import { join as join$1, relative, isAbsolute, resolve, dirname } from 'node:path';
2
+ import { join as join$1, relative, dirname, isAbsolute, resolve } from 'node:path';
3
3
  import { spawn } from 'node:child_process';
4
4
  import { existsSync, rmSync, mkdtempSync, readFileSync, statSync, writeFileSync, mkdirSync } from 'node:fs';
5
5
  import { tmpdir } from 'node:os';
@@ -22,6 +22,70 @@ import { rollup } from '../../_dependencies/rollup/index.esm.js';
22
22
  import { normalizeToForwardSlashes, join } from '../../_shared/bundle/fs/posix-path/index.esm.js';
23
23
  import { relativeImport, buildWorkspaceRoutes, createExternalizeBundledDepsPlugin } from '../../_shared/bundle/dependencies/externalize-plugin/index.esm.js';
24
24
 
25
+ const SWC_NODE_REGISTER = '@swc-node/register';
26
+ const probeDir = (dir, offset) => {
27
+ const compiled = join$1(dir, ...offset, 'index.cjs.js');
28
+ if (existsSync(compiled))
29
+ return { path: compiled, execArgv: [] };
30
+ const source = join$1(dir, ...offset, 'index.ts');
31
+ if (existsSync(source))
32
+ return { path: source, execArgv: ['--require', SWC_NODE_REGISTER] };
33
+ return undefined;
34
+ };
35
+ /**
36
+ * Directory of the currently-executing builder module, read from `__dirname`.
37
+ * It is present in the shipped CommonJS build, in the `@swc-node/register` source
38
+ * bootstrap, and under the test runner, so callers get their own on-disk location
39
+ * and can find the worker beside them however the builder was packaged.
40
+ *
41
+ * @returns Absolute directory path of the running module.
42
+ *
43
+ * @example Anchoring a lookup beside the running builder
44
+ * ```typescript
45
+ * const invocation = ascendForWorker(['bundle', 'rollup', 'worker'], currentModuleDir())
46
+ * ```
47
+ */
48
+ const currentModuleDir = () => {
49
+ /* istanbul ignore if -- @preserve the ESM build has no __dirname; the shipped CommonJS build and the test runner always define it, so this guard is unreachable under test */
50
+ if (typeof __dirname === 'undefined') {
51
+ throw createError('@hyperfrontend/builder self-location requires the CommonJS build; drive build() from the CommonJS entry point');
52
+ }
53
+ return __dirname;
54
+ };
55
+ /**
56
+ * Locate a builder worker entry by ascending from a start directory toward the
57
+ * filesystem root, returning the first ancestor at which the worker exists at the
58
+ * given in-package offset. The compiled `index.cjs.js` is preferred; an `index.ts`
59
+ * sibling (source-mode bootstrap) resolves with the swc loader attached.
60
+ *
61
+ * The worker ships inside the builder's own package, so resolving relative to the
62
+ * running module finds it wherever the package lives — built dist, an installed
63
+ * `node_modules` copy, or melded into a host bundle under `_dependencies/`.
64
+ *
65
+ * @param offset - In-package path segments to the worker directory (e.g. `['bundle', 'rollup', 'worker']`).
66
+ * @param startDir - Directory to begin the ascent from. Defaults to the running module's directory; pass an explicit value to resolve from another anchor or under test.
67
+ * @returns The resolved worker invocation, or `undefined` if no worker exists at the offset under any ancestor.
68
+ *
69
+ * @example Resolving the rollup worker from the running module
70
+ * ```typescript
71
+ * const invocation = ascendForWorker(['bundle', 'rollup', 'worker'])
72
+ * if (!invocation) throw new Error('builder rollup worker not found beside the builder module')
73
+ * ```
74
+ */
75
+ const ascendForWorker = (offset, startDir = currentModuleDir()) => {
76
+ let dir = startDir;
77
+ let parent = dirname(dir);
78
+ // how: probe each level then step up; the final probe covers the filesystem root, where parent === dir
79
+ while (parent !== dir) {
80
+ const found = probeDir(dir, offset);
81
+ if (found)
82
+ return found;
83
+ dir = parent;
84
+ parent = dirname(dir);
85
+ }
86
+ return probeDir(dir, offset);
87
+ };
88
+
25
89
  const log = logger.channel('builder:bundle:dependencies:pre-pass');
26
90
  const REPORT_DIR_PREFIX = 'hf-builder-prepass-';
27
91
  const createReportDir = () => mkdtempSync(join$2(tmpdir(), REPORT_DIR_PREFIX));
@@ -58,43 +122,25 @@ const readReport = (reportPath, job) => {
58
122
  const data = parse(readFileSync(reportPath, 'utf8'));
59
123
  return { job, outputSize: data.outputSize, endHeapMB: data.endHeapMB, endRssMB: data.endRssMB, durationMs: data.durationMs };
60
124
  };
61
- const SWC_NODE_REGISTER = '@swc-node/register';
62
- const swcNodeAvailable = (workspaceRoot) => existsSync(join$2(workspaceRoot, 'node_modules', '@swc-node', 'register', 'index.js'));
63
125
  /**
64
- * Default worker-path resolution: prefers the built-and-published artifact, falls
65
- * back to the workspace dist path, and finally to the in-source TypeScript file
66
- * via `@swc-node/register` (bootstrap case where builder is building itself for
67
- * the first time and the dist worker doesn't exist yet).
68
- *
69
- * Looks at, in order:
70
- * 1. `<workspaceRoot>/dist/libs/builder/bundle/dependencies/worker/index.cjs.js`
71
- * 2. `<workspaceRoot>/node_modules/@hyperfrontend/builder/bundle/dependencies/worker/index.cjs.js`
72
- * 3. `<workspaceRoot>/libs/builder/src/bundle/dependencies/worker/index.ts` (with `--require \@swc-node/register`)
73
- *
74
- * @param workspaceRoot - Absolute workspace root.
75
- * @returns Worker invocation descriptor, or `undefined` if no candidate exists.
76
- *
77
- * @example Locating the worker for an in-workspace consumer
126
+ * Resolves the dependency pre-pass worker by self-locating it beside the running
127
+ * builder module: ascends from the module's own directory to the builder package
128
+ * root and returns the worker at `bundle/dependencies/worker`. This works whether
129
+ * the builder runs from its built dist, an installed `node_modules` copy, or
130
+ * melded into a host bundle under `_dependencies/`. The compiled `index.cjs.js`
131
+ * is preferred; an `index.ts` sibling resolves with the `@swc-node/register`
132
+ * loader for source-mode bootstrap.
133
+ *
134
+ * @param startDir - Directory to begin the ascent from. Defaults to the running module's directory; pass an explicit value to resolve from another anchor or under test.
135
+ * @returns Worker invocation descriptor, or `undefined` if no worker is found under any ancestor.
136
+ *
137
+ * @example Locating the worker beside the builder
78
138
  * ```typescript
79
- * const invocation = resolveDefaultWorkerPath('/abs/repo')
139
+ * const invocation = resolveDefaultWorkerPath()
80
140
  * if (!invocation) throw new Error('builder worker artifact not found')
81
141
  * ```
82
142
  */
83
- const resolveDefaultWorkerPath = (workspaceRoot) => {
84
- const distCandidates = [
85
- join$2(workspaceRoot, 'dist', 'libs', 'builder', 'bundle', 'dependencies', 'worker', 'index.cjs.js'),
86
- join$2(workspaceRoot, 'node_modules', '@hyperfrontend', 'builder', 'bundle', 'dependencies', 'worker', 'index.cjs.js'),
87
- ];
88
- for (const path of distCandidates) {
89
- if (existsSync(path))
90
- return { path, execArgv: [] };
91
- }
92
- const sourcePath = join$2(workspaceRoot, 'libs', 'builder', 'src', 'bundle', 'dependencies', 'worker', 'index.ts');
93
- if (existsSync(sourcePath) && swcNodeAvailable(workspaceRoot)) {
94
- return { path: sourcePath, execArgv: ['--require', SWC_NODE_REGISTER] };
95
- }
96
- return undefined;
97
- };
143
+ const resolveDefaultWorkerPath = (startDir) => ascendForWorker(['bundle', 'dependencies', 'worker'], startDir);
98
144
  /**
99
145
  * Sequentially runs the supplied pre-pass jobs by forking a fresh Node child
100
146
  * per invocation. Strict sequential execution is mandatory — concurrent
@@ -1,4 +1,5 @@
1
1
  import type { MemoryMonitor } from '../../memory/monitor';
2
+ import type { WorkerInvocation } from '../worker-locator';
2
3
  import type { WorkspaceBundledDepRoute } from './externalize-plugin';
3
4
  /**
4
5
  * Pre-pass kind. Mirrors `PrePassWorkerJobKind` on the worker side.
@@ -98,36 +99,24 @@ export interface RunPrePassOptions {
98
99
  execArgv?: string[];
99
100
  }
100
101
  /**
101
- * Resolved worker invocation: absolute path + any extra Node args (e.g. `--require \@swc-node/register`
102
- * when the worker is loaded from TypeScript source during a bootstrap build).
103
- */
104
- export interface WorkerInvocation {
105
- /** Absolute path to the worker entry script. */
106
- path: string;
107
- /** Extra args prepended to the spawned child's argv. */
108
- execArgv: string[];
109
- }
110
- /**
111
- * Default worker-path resolution: prefers the built-and-published artifact, falls
112
- * back to the workspace dist path, and finally to the in-source TypeScript file
113
- * via `@swc-node/register` (bootstrap case where builder is building itself for
114
- * the first time and the dist worker doesn't exist yet).
115
- *
116
- * Looks at, in order:
117
- * 1. `<workspaceRoot>/dist/libs/builder/bundle/dependencies/worker/index.cjs.js`
118
- * 2. `<workspaceRoot>/node_modules/@hyperfrontend/builder/bundle/dependencies/worker/index.cjs.js`
119
- * 3. `<workspaceRoot>/libs/builder/src/bundle/dependencies/worker/index.ts` (with `--require \@swc-node/register`)
102
+ * Resolves the dependency pre-pass worker by self-locating it beside the running
103
+ * builder module: ascends from the module's own directory to the builder package
104
+ * root and returns the worker at `bundle/dependencies/worker`. This works whether
105
+ * the builder runs from its built dist, an installed `node_modules` copy, or
106
+ * melded into a host bundle under `_dependencies/`. The compiled `index.cjs.js`
107
+ * is preferred; an `index.ts` sibling resolves with the `@swc-node/register`
108
+ * loader for source-mode bootstrap.
120
109
  *
121
- * @param workspaceRoot - Absolute workspace root.
122
- * @returns Worker invocation descriptor, or `undefined` if no candidate exists.
110
+ * @param startDir - Directory to begin the ascent from. Defaults to the running module's directory; pass an explicit value to resolve from another anchor or under test.
111
+ * @returns Worker invocation descriptor, or `undefined` if no worker is found under any ancestor.
123
112
  *
124
- * @example Locating the worker for an in-workspace consumer
113
+ * @example Locating the worker beside the builder
125
114
  * ```typescript
126
- * const invocation = resolveDefaultWorkerPath('/abs/repo')
115
+ * const invocation = resolveDefaultWorkerPath()
127
116
  * if (!invocation) throw new Error('builder worker artifact not found')
128
117
  * ```
129
118
  */
130
- export declare const resolveDefaultWorkerPath: (workspaceRoot: string) => WorkerInvocation | undefined;
119
+ export declare const resolveDefaultWorkerPath: (startDir?: string) => WorkerInvocation | undefined;
131
120
  /**
132
121
  * Sequentially runs the supplied pre-pass jobs by forking a fresh Node child
133
122
  * per invocation. Strict sequential execution is mandatory — concurrent