refactor: share package cleanup helpers

This commit is contained in:
Peter Steinberger
2026-05-03 13:51:14 +01:00
parent 6f9a9241a6
commit 0459bff556
3 changed files with 17 additions and 235 deletions

View File

@@ -1,9 +1,12 @@
import fs from "node:fs";
import path from "node:path";
import { pathToFileURL } from "node:url";
import JSON5 from "json5";
import { NON_PACKAGED_BUNDLED_PLUGIN_DIRS } from "./lib/bundled-plugin-build-entries.mjs";
import { shouldBuildBundledCluster } from "./lib/optional-bundled-clusters.mjs";
import {
mergeGeneratedChannelConfigs,
readGeneratedBundledChannelConfigs,
} from "./lib/plugin-npm-package-manifest.mjs";
import {
removeFileIfExists,
removePathIfExists,
@@ -11,8 +14,6 @@ import {
} from "./runtime-postbuild-shared.mjs";
const GENERATED_BUNDLED_SKILLS_DIR = "bundled-skills";
const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA_PATH =
"src/config/bundled-channel-config-metadata.generated.ts";
const TRANSIENT_COPY_ERROR_CODES = new Set(["EEXIST", "ENOENT", "ENOTEMPTY", "EBUSY"]);
const COPY_RETRY_DELAYS_MS = [10, 25, 50];
@@ -220,86 +221,6 @@ function copyDeclaredPluginSkillPaths(params) {
return copiedSkills;
}
function readGeneratedBundledChannelConfigs(repoRoot) {
const metadataPath = path.join(repoRoot, GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA_PATH);
if (!fs.existsSync(metadataPath)) {
return new Map();
}
const source = fs.readFileSync(metadataPath, "utf8");
const match = source.match(
/export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = ([\s\S]*?) as const;/u,
);
if (!match?.[1]) {
return new Map();
}
let entries;
try {
entries = JSON5.parse(match[1]);
} catch {
return new Map();
}
if (!Array.isArray(entries)) {
return new Map();
}
const byPlugin = new Map();
for (const entry of entries) {
if (
!entry ||
typeof entry !== "object" ||
typeof entry.pluginId !== "string" ||
typeof entry.channelId !== "string" ||
!entry.schema ||
typeof entry.schema !== "object"
) {
continue;
}
const pluginConfigs = byPlugin.get(entry.pluginId) ?? {};
pluginConfigs[entry.channelId] = {
schema: entry.schema,
...(typeof entry.label === "string" && entry.label ? { label: entry.label } : {}),
...(typeof entry.description === "string" && entry.description
? { description: entry.description }
: {}),
...(entry.uiHints && typeof entry.uiHints === "object" ? { uiHints: entry.uiHints } : {}),
};
byPlugin.set(entry.pluginId, pluginConfigs);
}
return byPlugin;
}
function mergeGeneratedChannelConfigs(manifest, generatedChannelConfigs) {
if (!generatedChannelConfigs || Object.keys(generatedChannelConfigs).length === 0) {
return manifest;
}
const existingChannelConfigs =
manifest.channelConfigs && typeof manifest.channelConfigs === "object"
? manifest.channelConfigs
: {};
const channelConfigs = { ...existingChannelConfigs };
for (const [channelId, generated] of Object.entries(generatedChannelConfigs)) {
const existing =
existingChannelConfigs[channelId] && typeof existingChannelConfigs[channelId] === "object"
? existingChannelConfigs[channelId]
: {};
channelConfigs[channelId] = {
...generated,
...existing,
schema: generated.schema,
...(generated.uiHints || existing.uiHints
? { uiHints: { ...generated.uiHints, ...existing.uiHints } }
: {}),
...(existing.label || generated.label ? { label: existing.label ?? generated.label } : {}),
...(existing.description || generated.description
? { description: existing.description ?? generated.description }
: {}),
};
}
return {
...manifest,
channelConfigs,
};
}
/**
* @param {{
* cwd?: string;

View File

@@ -96,7 +96,7 @@ export function resolveAugmentedPluginNpmPackageJson(params) {
};
}
function readGeneratedBundledChannelConfigs(repoRoot) {
export function readGeneratedBundledChannelConfigs(repoRoot) {
const metadataPath = path.join(repoRoot, GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA_PATH);
if (!fs.existsSync(metadataPath)) {
return new Map();
@@ -145,7 +145,7 @@ function readGeneratedBundledChannelConfigs(repoRoot) {
return byPlugin;
}
function mergeGeneratedChannelConfigs(manifest, generatedChannelConfigs) {
export function mergeGeneratedChannelConfigs(manifest, generatedChannelConfigs) {
if (!generatedChannelConfigs || Object.keys(generatedChannelConfigs).length === 0) {
return manifest;
}

View File

@@ -19,16 +19,9 @@ import {
writeFileSync,
} from "node:fs";
import { homedir, tmpdir } from "node:os";
import {
basename,
dirname,
isAbsolute,
join,
posix,
relative,
resolve as pathResolve,
} from "node:path";
import { basename, dirname, isAbsolute, join, relative, resolve as pathResolve } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { expandPackageDistImportClosure } from "./lib/package-dist-imports.mjs";
const __dirname = dirname(fileURLToPath(import.meta.url));
const DEFAULT_PACKAGE_ROOT = join(__dirname, "..");
@@ -400,145 +393,6 @@ export function pruneLegacyPluginRuntimeDepsState(params = {}) {
return removed;
}
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;
}
let source;
try {
source = params.readText(importerPath);
} catch (error) {
if (error?.code === "ENOENT") {
continue;
}
throw error;
}
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;
@@ -569,11 +423,18 @@ export function pruneInstalledPackageDist(params = {}) {
const installedFiles = listInstalledDistFiles(params);
const readFile = params.readFileSync ?? readFileSync;
expectedFiles = new Set(
expandInstalledDistImportClosure({
expandPackageDistImportClosure({
files: installedFiles,
seedFiles: [...expectedFiles],
readText(relativePath) {
return readFile(join(packageRoot, relativePath), "utf8");
try {
return readFile(join(packageRoot, relativePath), "utf8");
} catch (error) {
if (error?.code === "ENOENT") {
return "";
}
throw error;
}
},
}),
);