Files
orca/config/scripts/script-module-dependencies.mjs
T
Neil 2a73e6b03a fix(windows): say why the packaged ConPTY fell back, not just that it did
The previous commit resolved the addon by architecture but still had one message
for every way the resolution could land on the published prebuild. Those ways
want opposite remedies, and the one it printed was the remedy the commit before
it had just called wrong:

- no source build in the package at all — the slice has to be built somewhere
  that can build node-pty for the target arch.
- a source build that is there but is the packaging host's architecture, because
  the cross-arch rebuild did not honour `--arch` — re-running that rebuild is the
  fix, and "package on a Windows arm64 host" is neither necessary nor possible.

The second is the common one, since node-pty publishes a prebuild for both
Windows arches and prune keeps the target's on every cross-arch package. So the
old text fired mostly on the case it described least. It now reports which source
builds were skipped and the machine field each carried, and names the rebuild
command.

"Nothing the target can load" had the same problem in reverse: a zero-length or
truncated `conpty.node` got a cross-architecture diagnosis. Every candidate is
now named with what was actually read, including "not a PE image".

The rebuild path asserts the architecture too. A rebuild that ignored `--arch`
was otherwise only visible at packaging, two steps from the command that fixes
it. Arches with no known machine value are left unjudged rather than guessed at.

Two things the extraction broke or nearly broke, both found by mutation:

- the shared PE reader answers `null` where the relay builder's private copy
  returned a number, which would have turned its "node-gyp ignored --arch" error
  into a `TypeError`. Both callers now go through `describePeMachine`.
- the rebuild fixtures stage a script's co-located modules by walking its
  imports, and the walker only understood `from '...'` — so the gate's new
  `require('./windows-pe-machine.cjs')` was left behind and every subprocess test
  failed with a resolution error, which is the exact failure its own comment
  warns about. It now follows `require` and bare side-effect `import` as well,
  and has tests; the fixture stages the gate by walking it rather than by naming
  one file.

Fixtures write real PE headers through one shared builder instead of three
hand-rolled ones.
2026-09-15 22:19:46 -07:00

35 lines
1.4 KiB
JavaScript

import { copyFileSync, mkdirSync, readFileSync } from 'node:fs'
import { basename, dirname, join } from 'node:path'
/**
* Copy a script and every co-located module it imports into a fixture's `config/scripts`.
*
* Walked rather than listed: a module the script needs but the fixture never copied fails every
* test in the suite with a module-resolution error that looks nothing like the defect it hides.
*/
export function copyScriptWithLocalModules(sourceScriptPath, destinationScriptsDir) {
mkdirSync(destinationScriptsDir, { recursive: true })
for (const modulePath of collectScriptModules(sourceScriptPath)) {
copyFileSync(modulePath, join(destinationScriptsDir, basename(modulePath)))
}
}
function collectScriptModules(scriptPath, seen = new Set()) {
if (seen.has(scriptPath)) {
return seen
}
seen.add(scriptPath)
// Every shape that reaches a co-located module: `from`, static and dynamic
// `import`, and `require` -- the Windows gates are .cjs, and a module reached
// only by require or by a side-effect import is the one nobody notices is
// missing until a subprocess fails with a resolution error instead.
const source = readFileSync(scriptPath, 'utf8')
const specifiers = source.matchAll(
/(?:\bfrom|\brequire\s*\(|\bimport\s*\(|\bimport)\s*'(\.\/[^']+)'/g
)
for (const [, specifier] of specifiers) {
collectScriptModules(join(dirname(scriptPath), specifier), seen)
}
return seen
}