From a820a307dfeb950fa633c0ae6fb2386a35a91991 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 29 Apr 2026 01:20:36 +0100 Subject: [PATCH] fix(ci): keep postinstall script self-contained --- scripts/postinstall-bundled-plugins.mjs | 136 +++++++++++++++++++++++- 1 file changed, 133 insertions(+), 3 deletions(-) diff --git a/scripts/postinstall-bundled-plugins.mjs b/scripts/postinstall-bundled-plugins.mjs index b93e0730f2f..b0ef9c53059 100644 --- a/scripts/postinstall-bundled-plugins.mjs +++ b/scripts/postinstall-bundled-plugins.mjs @@ -21,9 +21,8 @@ import { unlinkSync, writeFileSync, } from "node:fs"; -import { basename, dirname, isAbsolute, join, relative } from "node:path"; +import { basename, dirname, isAbsolute, join, posix, relative } from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; -import { expandPackageDistImportClosure } from "./lib/package-dist-imports.mjs"; import { resolveNpmRunner } from "./npm-runner.mjs"; export const BUNDLED_PLUGIN_INSTALL_TARGETS = []; @@ -272,6 +271,137 @@ function pruneEmptyDistDirectories(params = {}) { prune(distRoot.distDir); } +const JS_DIST_FILE_RE = /^dist\/.*\.(?:cjs|js|mjs)$/u; + +function stripSpecifierSuffix(value) { + return value.replace(/[?#].*$/u, ""); +} + +function resolveDistImportPath(importerPath, specifier) { + if (!specifier.startsWith(".")) { + return null; + } + const stripped = stripSpecifierSuffix(specifier); + if (!stripped) { + return null; + } + return posix.normalize(posix.join(posix.dirname(importerPath), stripped)); +} + +function findStatementStart(source, index) { + return ( + Math.max( + source.lastIndexOf(";", index), + source.lastIndexOf("{", index), + source.lastIndexOf("}", index), + source.lastIndexOf("\n", index), + source.lastIndexOf("\r", index), + ) + 1 + ); +} + +function isImportSpecifierContext(source, index) { + const dynamicPrefix = source.slice(Math.max(0, index - 32), index); + if (/\bimport\s*\(\s*$/u.test(dynamicPrefix)) { + return true; + } + const statementPrefix = source.slice(findStatementStart(source, index), index).trimStart(); + return ( + /^(?:import|export)\b[\s\S]*\bfrom\s*$/u.test(statementPrefix) || + /^import\s*$/u.test(statementPrefix) + ); +} + +function collectImportSpecifiers(source) { + const specifiers = []; + let inBlockComment = false; + let inLineComment = false; + for (let index = 0; index < source.length; index += 1) { + if (inBlockComment) { + if (source[index] === "*" && source[index + 1] === "/") { + inBlockComment = false; + index += 1; + } + continue; + } + if (inLineComment) { + if (source[index] === "\n" || source[index] === "\r") { + inLineComment = false; + } + continue; + } + if (source[index] === "/" && source[index + 1] === "*") { + inBlockComment = true; + index += 1; + continue; + } + if (source[index] === "/" && source[index + 1] === "/") { + inLineComment = true; + index += 1; + continue; + } + + const quote = source[index]; + if (quote !== '"' && quote !== "'") { + continue; + } + + let cursor = index + 1; + let value = ""; + while (cursor < source.length) { + const char = source[cursor]; + if (char === "\\") { + value += source.slice(cursor, cursor + 2); + cursor += 2; + continue; + } + if (char === quote) { + break; + } + value += char; + cursor += 1; + } + if (cursor >= source.length) { + break; + } + + if (value.startsWith(".") && isImportSpecifierContext(source, index)) { + specifiers.push(value); + } + index = cursor; + } + return specifiers; +} + +function expandInstalledDistImportClosure(params) { + const files = [...new Set(params.files)]; + const fileSet = new Set(files); + const expectedSet = new Set(params.seedFiles); + let changed = true; + + while (changed) { + changed = false; + for (const importerPath of [...expectedSet] + .filter((file) => fileSet.has(file)) + .toSorted((left, right) => left.localeCompare(right))) { + if (!JS_DIST_FILE_RE.test(importerPath) || importerPath.includes("/node_modules/")) { + continue; + } + const source = params.readText(importerPath); + for (const specifier of collectImportSpecifiers(source)) { + const importedPath = resolveDistImportPath(importerPath, specifier); + if (!importedPath || !fileSet.has(importedPath) || expectedSet.has(importedPath)) { + continue; + } + expectedSet.add(importedPath); + changed = true; + } + } + } + + return [...expectedSet].toSorted((left, right) => left.localeCompare(right)); +} + export function pruneInstalledPackageDist(params = {}) { const packageRoot = params.packageRoot ?? DEFAULT_PACKAGE_ROOT; const removeFile = params.unlinkSync ?? unlinkSync; @@ -295,7 +425,7 @@ export function pruneInstalledPackageDist(params = {}) { const installedFiles = listInstalledDistFiles(params); const readFile = params.readFileSync ?? readFileSync; expectedFiles = new Set( - expandPackageDistImportClosure({ + expandInstalledDistImportClosure({ files: installedFiles, seedFiles: [...expectedFiles], readText(relativePath) {