build: avoid ambiguous runtime aliases

This commit is contained in:
Peter Steinberger
2026-05-02 21:26:27 +01:00
parent beefac0564
commit c96e62d5ab
2 changed files with 88 additions and 6 deletions

View File

@@ -107,7 +107,8 @@ export function writeStableRootRuntimeAliases(params = {}) {
return;
}
for (const entry of entries) {
const candidatesByAlias = new Map();
for (const entry of entries.toSorted((left, right) => left.name.localeCompare(right.name))) {
if (!entry.isFile()) {
continue;
}
@@ -115,8 +116,19 @@ export function writeStableRootRuntimeAliases(params = {}) {
if (!match?.groups?.base) {
continue;
}
const aliasPath = path.join(distDir, `${match.groups.base}.js`);
writeTextFileIfChanged(aliasPath, `export * from "./${entry.name}";\n`);
const aliasFileName = `${match.groups.base}.js`;
const candidates = candidatesByAlias.get(aliasFileName) ?? [];
candidates.push(entry.name);
candidatesByAlias.set(aliasFileName, candidates);
}
for (const [aliasFileName, candidates] of candidatesByAlias) {
const aliasPath = path.join(distDir, aliasFileName);
if (candidates.length !== 1) {
fsImpl.rmSync?.(aliasPath, { force: true });
continue;
}
writeTextFileIfChanged(aliasPath, `export * from "./${candidates[0]}";\n`);
}
}
@@ -131,16 +143,26 @@ export function rewriteRootRuntimeImportsToStableAliases(params = {}) {
return;
}
const runtimeAliasFiles = new Map();
for (const entry of entries) {
const candidatesByAlias = new Map();
for (const entry of entries.toSorted((left, right) => left.name.localeCompare(right.name))) {
if (!entry.isFile()) {
continue;
}
const match = entry.name.match(ROOT_RUNTIME_ALIAS_PATTERN);
if (match?.groups?.base) {
runtimeAliasFiles.set(entry.name, `${match.groups.base}.js`);
const aliasFileName = `${match.groups.base}.js`;
const candidates = candidatesByAlias.get(aliasFileName) ?? [];
candidates.push(entry.name);
candidatesByAlias.set(aliasFileName, candidates);
}
}
const runtimeAliasFiles = new Map();
for (const [aliasFileName, candidates] of candidatesByAlias) {
if (candidates.length !== 1) {
continue;
}
runtimeAliasFiles.set(candidates[0], aliasFileName);
}
if (runtimeAliasFiles.size === 0) {
return;
}

View File

@@ -88,6 +88,31 @@ describe("runtime postbuild static assets", () => {
await expect(fs.stat(path.join(distDir, "library.js"))).rejects.toThrow();
});
it("does not write ambiguous stable aliases for colliding root runtime chunks", async () => {
const rootDir = createTempDir("openclaw-runtime-postbuild-");
const distDir = path.join(rootDir, "dist");
await fs.mkdir(distDir, { recursive: true });
await fs.writeFile(
path.join(distDir, "install.runtime-Aaa111.js"),
"export const pluginInstall = true;\n",
"utf8",
);
await fs.writeFile(
path.join(distDir, "install.runtime-Bbb222.js"),
"export const daemonInstall = true;\n",
"utf8",
);
await fs.writeFile(
path.join(distDir, "install.runtime.js"),
'export * from "./install.runtime-Stale.js";\n',
"utf8",
);
writeStableRootRuntimeAliases({ rootDir });
await expect(fs.stat(path.join(distDir, "install.runtime.js"))).rejects.toThrow();
});
it("rewrites root runtime imports to stable aliases", async () => {
const rootDir = createTempDir("openclaw-runtime-postbuild-");
const distDir = path.join(rootDir, "dist");
@@ -118,6 +143,41 @@ describe("runtime postbuild static assets", () => {
);
});
it("keeps hashed imports when a stable runtime alias would collide", async () => {
const rootDir = createTempDir("openclaw-runtime-postbuild-");
const distDir = path.join(rootDir, "dist");
await fs.mkdir(distDir, { recursive: true });
await fs.writeFile(
path.join(distDir, "install.runtime-Aaa111.js"),
"export const pluginInstall = true;\n",
"utf8",
);
await fs.writeFile(
path.join(distDir, "install.runtime-Bbb222.js"),
"export const daemonInstall = true;\n",
"utf8",
);
await fs.writeFile(
path.join(distDir, "install-OldHash.js"),
[
'const pluginRuntime = () => import("./install.runtime-Aaa111.js");',
'const daemonRuntime = () => import("./install.runtime-Bbb222.js");',
"",
].join("\n"),
"utf8",
);
rewriteRootRuntimeImportsToStableAliases({ rootDir });
expect(await fs.readFile(path.join(distDir, "install-OldHash.js"), "utf8")).toBe(
[
'const pluginRuntime = () => import("./install.runtime-Aaa111.js");',
'const daemonRuntime = () => import("./install.runtime-Bbb222.js");',
"",
].join("\n"),
);
});
it("leaves stable alias files pointing at their hashed runtime chunks", async () => {
const rootDir = createTempDir("openclaw-runtime-postbuild-");
const distDir = path.join(rootDir, "dist");