build: avoid ambiguous runtime aliases

(cherry picked from commit c96e62d5ab)
This commit is contained in:
Peter Steinberger
2026-05-02 21:26:27 +01:00
parent 422f1fe9bb
commit 82dd68d299
2 changed files with 250 additions and 3 deletions

View File

@@ -16,6 +16,27 @@ export { copyStaticExtensionAssets, listStaticExtensionAssetOutputs };
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const ROOT_RUNTIME_ALIAS_PATTERN = /^(?<base>.+\.(?:runtime|contract))-[A-Za-z0-9_-]+\.js$/u;
const ROOT_STABLE_RUNTIME_ALIAS_PATTERN = /^.+\.(?:runtime|contract)\.js$/u;
const ROOT_RUNTIME_IMPORT_SPECIFIER_PATTERN =
/(["'])\.\/([^"']+\.(?:runtime|contract)-[A-Za-z0-9_-]+\.js)\1/gu;
const LEGACY_ROOT_RUNTIME_COMPAT_ALIASES = [
// v2026.4.29 dispatch lazy chunks. Package updates used to replace the
// dist tree before the live gateway had restarted, so an already-loaded old
// dispatch chunk could still resolve these names after the swap.
["abort.runtime-DX6vo4yJ.js", "abort.runtime.js"],
["get-reply-from-config.runtime-uABrvCZ-.js", "get-reply-from-config.runtime.js"],
["reply-media-paths.runtime-C5UnVaLF.js", "reply-media-paths.runtime.js"],
["route-reply.runtime-D4PGzijU.js", "route-reply.runtime.js"],
["runtime-plugins.runtime-fLHuT7Vs.js", "runtime-plugins.runtime.js"],
["tts.runtime-66taD50M.js", "tts.runtime.js"],
// v2026.5.2-beta.1 dispatch lazy chunks.
["abort.runtime-CKviLU0L.js", "abort.runtime.js"],
["get-reply-from-config.runtime-BzFAggVK.js", "get-reply-from-config.runtime.js"],
["reply-media-paths.runtime-ZpULeITb.js", "reply-media-paths.runtime.js"],
["route-reply.runtime-uzaOjbd1.js", "route-reply.runtime.js"],
["runtime-plugins.runtime-CNAfmQRG.js", "runtime-plugins.runtime.js"],
["tts.runtime-D-THXDsp.js", "tts.runtime.js"],
];
const LEGACY_CLI_EXIT_COMPAT_CHUNKS = [
{
dest: "dist/memory-state-CcqRgDZU.js",
@@ -38,7 +59,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;
}
@@ -46,8 +68,97 @@ 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`);
}
}
export function rewriteRootRuntimeImportsToStableAliases(params = {}) {
const rootDir = params.rootDir ?? ROOT;
const distDir = path.join(rootDir, "dist");
const fsImpl = params.fs ?? fs;
let entries = [];
try {
entries = fsImpl.readdirSync(distDir, { withFileTypes: true });
} catch {
return;
}
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) {
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;
}
for (const entry of entries) {
if (!entry.isFile() || !entry.name.endsWith(".js")) {
continue;
}
if (ROOT_STABLE_RUNTIME_ALIAS_PATTERN.test(entry.name)) {
continue;
}
const filePath = path.join(distDir, entry.name);
let source;
try {
source = fsImpl.readFileSync(filePath, "utf8");
} catch {
continue;
}
const rewritten = source.replace(
ROOT_RUNTIME_IMPORT_SPECIFIER_PATTERN,
(specifier, quote, fileName) => {
const aliasFileName = runtimeAliasFiles.get(fileName);
return aliasFileName ? `${quote}./${aliasFileName}${quote}` : specifier;
},
);
if (rewritten !== source) {
writeTextFileIfChanged(filePath, rewritten);
}
}
}
export function writeLegacyRootRuntimeCompatAliases(params = {}) {
const rootDir = params.rootDir ?? ROOT;
const distDir = path.join(rootDir, "dist");
const fsImpl = params.fs ?? fs;
for (const [legacyFileName, aliasFileName] of LEGACY_ROOT_RUNTIME_COMPAT_ALIASES) {
const legacyPath = path.join(distDir, legacyFileName);
if (fsImpl.existsSync(legacyPath)) {
continue;
}
if (!fsImpl.existsSync(path.join(distDir, aliasFileName))) {
continue;
}
writeTextFileIfChanged(legacyPath, `export * from "./${aliasFileName}";\n`);
}
}
@@ -76,7 +187,9 @@ export function runRuntimePostBuild(params = {}) {
runPhase("bundled plugin metadata", () => copyBundledPluginMetadata(params));
runPhase("official channel catalog", () => writeOfficialChannelCatalog(params));
runPhase("bundled plugin runtime overlay", () => stageBundledPluginRuntime(params));
runPhase("stable root runtime imports", () => rewriteRootRuntimeImportsToStableAliases(params));
runPhase("stable root runtime aliases", () => writeStableRootRuntimeAliases(params));
runPhase("legacy root runtime compat aliases", () => writeLegacyRootRuntimeCompatAliases(params));
runPhase("legacy CLI exit compat chunks", () => writeLegacyCliExitCompatChunks(params));
runPhase("static extension assets", () =>
copyStaticExtensionAssets({