Keep installed libnode packs out of the executable cache key

An installed @scriptc/libnode-<platform> pack versions in lockstep with
the compiler, whose version every executable cache key already holds;
keying on it as well made the full compiler's key differ from the
bootstrap's routed key wherever a pack directory was present, so routed
cache hits stopped. Only directories with a manifest.json count as
installed packs, so an unbuilt workspace package is not one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Malte Ubl
2026-10-21 17:52:22 -07:00
co-authored by Claude Opus 5.5
parent 27b27fd5a8
commit 79931be198
4 changed files with 18 additions and 24 deletions
+1 -1
View File
@@ -1376,7 +1376,7 @@ async function compileTracked(
const helperObjectRoute =
opts.nativeProgramObject === true || usesPrecompiledRuntimePack(opts, "llvm");
// Calls into Node record the libnode pack, keyed by its manifest (node-islands/libnode.ts).
const libnodeKey = libnodeCacheKey(opts.libnode, process.env, dirname(entryPath));
const libnodeKey = libnodeCacheKey(opts.libnode);
const routeOptions: Omit<
EarlyExecutableCacheOptions,
"implementation" | "implementationDependencies" | "nativeEnvironment"
@@ -186,12 +186,13 @@ export function readLibnodePack(
return { root, manifest };
}
/** The nearest `node_modules/<name>` directory walking up from `from`. */
/** The nearest `node_modules/<name>` directory with a built pack (a
* manifest.json) walking up from `from`. */
function findInstalled(name: string, from: string): string | null {
let dir = resolve(from);
for (;;) {
const candidate = join(dir, "node_modules", name);
if (existsSync(join(candidate, "package.json"))) return candidate;
if (existsSync(join(candidate, "manifest.json"))) return candidate;
const parent = dirname(dir);
if (parent === dir) return null;
dir = parent;
@@ -200,7 +201,9 @@ function findInstalled(name: string, from: string): string | null {
function resolveNextToCompiler(name: string): string | null {
try {
return dirname(createRequire(import.meta.url).resolve(`${name}/package.json`));
const dir = dirname(createRequire(import.meta.url).resolve(`${name}/package.json`));
// An unbuilt workspace package is not a pack.
return existsSync(join(dir, "manifest.json")) ? dir : null;
} catch {
return null;
}
@@ -93,13 +93,8 @@ describe("the recorded libnode pack", () => {
});
describe("the libnode pack in an executable's cache key", () => {
test("is absent without --libnode, SCRIPTC_LIBNODE or an installed pack", () => {
const installed = findInstalledLibnodePack({
targetName: hostLibnodeTarget(),
projectDir: "/nonexistent",
});
if (installed === null) expect(libnodeCacheKey(undefined, {}, "/nonexistent")).toBeUndefined();
else expect(libnodeCacheKey(undefined, {}, "/nonexistent")).toContain(installed);
test("is absent without --libnode or SCRIPTC_LIBNODE", () => {
expect(libnodeCacheKey(undefined, {})).toBeUndefined();
});
test("changes when the pack in the same directory is upgraded", () => {
+9 -13
View File
@@ -119,24 +119,20 @@ export function libnodeRecord(
}
/** The part of an executable's cache key that names its libnode pack, or
* undefined when neither --libnode nor SCRIPTC_LIBNODE is set and no pack
* is installed. It holds the option, the pack directory and a digest of the
* pack's manifest.json, so that a pack upgraded in place (another Node
* version, bridge ABI or file list) gives cached executables a new key
* instead of their old record. */
* undefined when neither --libnode nor SCRIPTC_LIBNODE is set. It holds the
* option, the pack directory and a digest of the pack's manifest.json, so
* that a pack upgraded in place (another Node version, bridge ABI or file
* list) gives cached executables a new key instead of their old record.
* An installed @scriptc/libnode-<platform> pack needs no part: it versions
* in lockstep with the compiler, whose version every key already holds, so
* the bootstrap's routed cache keeps serving builds where one is installed. */
export function libnodeCacheKey(
option: string | undefined,
env: NodeJS.ProcessEnv = process.env,
projectDir: string = process.cwd(),
): string | undefined {
const fromEnv = env["SCRIPTC_LIBNODE"];
const installed =
option === undefined && fromEnv === undefined
? findInstalledLibnodePack({ targetName: hostLibnodeTarget(), projectDir })
: null;
if (option === undefined && fromEnv === undefined && installed === null) return undefined;
const source =
installed ?? (option !== undefined && option !== "beside" ? option : (fromEnv ?? ""));
if (option === undefined && fromEnv === undefined) return undefined;
const source = option !== undefined && option !== "beside" ? option : (fromEnv ?? "");
let manifest = "none";
if (isAbsolute(source)) {
try {