Skip to content

Commit 0459bff

Browse files
committed
refactor: share package cleanup helpers
1 parent 6f9a924 commit 0459bff

3 files changed

Lines changed: 17 additions & 235 deletions

File tree

scripts/copy-bundled-plugin-metadata.mjs

Lines changed: 4 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import fs from "node:fs";
22
import path from "node:path";
33
import { pathToFileURL } from "node:url";
4-
import JSON5 from "json5";
54
import { NON_PACKAGED_BUNDLED_PLUGIN_DIRS } from "./lib/bundled-plugin-build-entries.mjs";
65
import { shouldBuildBundledCluster } from "./lib/optional-bundled-clusters.mjs";
6+
import {
7+
mergeGeneratedChannelConfigs,
8+
readGeneratedBundledChannelConfigs,
9+
} from "./lib/plugin-npm-package-manifest.mjs";
710
import {
811
removeFileIfExists,
912
removePathIfExists,
1013
writeTextFileIfChanged,
1114
} from "./runtime-postbuild-shared.mjs";
1215

1316
const GENERATED_BUNDLED_SKILLS_DIR = "bundled-skills";
14-
const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA_PATH =
15-
"src/config/bundled-channel-config-metadata.generated.ts";
1617
const TRANSIENT_COPY_ERROR_CODES = new Set(["EEXIST", "ENOENT", "ENOTEMPTY", "EBUSY"]);
1718
const COPY_RETRY_DELAYS_MS = [10, 25, 50];
1819

@@ -220,86 +221,6 @@ function copyDeclaredPluginSkillPaths(params) {
220221
return copiedSkills;
221222
}
222223

223-
function readGeneratedBundledChannelConfigs(repoRoot) {
224-
const metadataPath = path.join(repoRoot, GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA_PATH);
225-
if (!fs.existsSync(metadataPath)) {
226-
return new Map();
227-
}
228-
const source = fs.readFileSync(metadataPath, "utf8");
229-
const match = source.match(
230-
/export const GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA = ([\s\S]*?) as const;/u,
231-
);
232-
if (!match?.[1]) {
233-
return new Map();
234-
}
235-
let entries;
236-
try {
237-
entries = JSON5.parse(match[1]);
238-
} catch {
239-
return new Map();
240-
}
241-
if (!Array.isArray(entries)) {
242-
return new Map();
243-
}
244-
const byPlugin = new Map();
245-
for (const entry of entries) {
246-
if (
247-
!entry ||
248-
typeof entry !== "object" ||
249-
typeof entry.pluginId !== "string" ||
250-
typeof entry.channelId !== "string" ||
251-
!entry.schema ||
252-
typeof entry.schema !== "object"
253-
) {
254-
continue;
255-
}
256-
const pluginConfigs = byPlugin.get(entry.pluginId) ?? {};
257-
pluginConfigs[entry.channelId] = {
258-
schema: entry.schema,
259-
...(typeof entry.label === "string" && entry.label ? { label: entry.label } : {}),
260-
...(typeof entry.description === "string" && entry.description
261-
? { description: entry.description }
262-
: {}),
263-
...(entry.uiHints && typeof entry.uiHints === "object" ? { uiHints: entry.uiHints } : {}),
264-
};
265-
byPlugin.set(entry.pluginId, pluginConfigs);
266-
}
267-
return byPlugin;
268-
}
269-
270-
function mergeGeneratedChannelConfigs(manifest, generatedChannelConfigs) {
271-
if (!generatedChannelConfigs || Object.keys(generatedChannelConfigs).length === 0) {
272-
return manifest;
273-
}
274-
const existingChannelConfigs =
275-
manifest.channelConfigs && typeof manifest.channelConfigs === "object"
276-
? manifest.channelConfigs
277-
: {};
278-
const channelConfigs = { ...existingChannelConfigs };
279-
for (const [channelId, generated] of Object.entries(generatedChannelConfigs)) {
280-
const existing =
281-
existingChannelConfigs[channelId] && typeof existingChannelConfigs[channelId] === "object"
282-
? existingChannelConfigs[channelId]
283-
: {};
284-
channelConfigs[channelId] = {
285-
...generated,
286-
...existing,
287-
schema: generated.schema,
288-
...(generated.uiHints || existing.uiHints
289-
? { uiHints: { ...generated.uiHints, ...existing.uiHints } }
290-
: {}),
291-
...(existing.label || generated.label ? { label: existing.label ?? generated.label } : {}),
292-
...(existing.description || generated.description
293-
? { description: existing.description ?? generated.description }
294-
: {}),
295-
};
296-
}
297-
return {
298-
...manifest,
299-
channelConfigs,
300-
};
301-
}
302-
303224
/**
304225
* @param {{
305226
* cwd?: string;

scripts/lib/plugin-npm-package-manifest.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ export function resolveAugmentedPluginNpmPackageJson(params) {
9696
};
9797
}
9898

99-
function readGeneratedBundledChannelConfigs(repoRoot) {
99+
export function readGeneratedBundledChannelConfigs(repoRoot) {
100100
const metadataPath = path.join(repoRoot, GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA_PATH);
101101
if (!fs.existsSync(metadataPath)) {
102102
return new Map();
@@ -145,7 +145,7 @@ function readGeneratedBundledChannelConfigs(repoRoot) {
145145
return byPlugin;
146146
}
147147

148-
function mergeGeneratedChannelConfigs(manifest, generatedChannelConfigs) {
148+
export function mergeGeneratedChannelConfigs(manifest, generatedChannelConfigs) {
149149
if (!generatedChannelConfigs || Object.keys(generatedChannelConfigs).length === 0) {
150150
return manifest;
151151
}

scripts/postinstall-bundled-plugins.mjs

Lines changed: 11 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,9 @@ import {
1919
writeFileSync,
2020
} from "node:fs";
2121
import { homedir, tmpdir } from "node:os";
22-
import {
23-
basename,
24-
dirname,
25-
isAbsolute,
26-
join,
27-
posix,
28-
relative,
29-
resolve as pathResolve,
30-
} from "node:path";
22+
import { basename, dirname, isAbsolute, join, relative, resolve as pathResolve } from "node:path";
3123
import { fileURLToPath, pathToFileURL } from "node:url";
24+
import { expandPackageDistImportClosure } from "./lib/package-dist-imports.mjs";
3225

3326
const __dirname = dirname(fileURLToPath(import.meta.url));
3427
const DEFAULT_PACKAGE_ROOT = join(__dirname, "..");
@@ -400,145 +393,6 @@ export function pruneLegacyPluginRuntimeDepsState(params = {}) {
400393
return removed;
401394
}
402395

403-
const JS_DIST_FILE_RE = /^dist\/.*\.(?:cjs|js|mjs)$/u;
404-
405-
function stripSpecifierSuffix(value) {
406-
return value.replace(/[?#].*$/u, "");
407-
}
408-
409-
function resolveDistImportPath(importerPath, specifier) {
410-
if (!specifier.startsWith(".")) {
411-
return null;
412-
}
413-
const stripped = stripSpecifierSuffix(specifier);
414-
if (!stripped) {
415-
return null;
416-
}
417-
return posix.normalize(posix.join(posix.dirname(importerPath), stripped));
418-
}
419-
420-
function findStatementStart(source, index) {
421-
return (
422-
Math.max(
423-
source.lastIndexOf(";", index),
424-
source.lastIndexOf("{", index),
425-
source.lastIndexOf("}", index),
426-
source.lastIndexOf("\n", index),
427-
source.lastIndexOf("\r", index),
428-
) + 1
429-
);
430-
}
431-
432-
function isImportSpecifierContext(source, index) {
433-
const dynamicPrefix = source.slice(Math.max(0, index - 32), index);
434-
if (/\bimport\s*\(\s*$/u.test(dynamicPrefix)) {
435-
return true;
436-
}
437-
const statementPrefix = source.slice(findStatementStart(source, index), index).trimStart();
438-
return (
439-
/^(?:import|export)\b[\s\S]*\bfrom\s*$/u.test(statementPrefix) ||
440-
/^import\s*$/u.test(statementPrefix)
441-
);
442-
}
443-
444-
function collectImportSpecifiers(source) {
445-
const specifiers = [];
446-
let inBlockComment = false;
447-
let inLineComment = false;
448-
for (let index = 0; index < source.length; index += 1) {
449-
if (inBlockComment) {
450-
if (source[index] === "*" && source[index + 1] === "/") {
451-
inBlockComment = false;
452-
index += 1;
453-
}
454-
continue;
455-
}
456-
if (inLineComment) {
457-
if (source[index] === "\n" || source[index] === "\r") {
458-
inLineComment = false;
459-
}
460-
continue;
461-
}
462-
if (source[index] === "/" && source[index + 1] === "*") {
463-
inBlockComment = true;
464-
index += 1;
465-
continue;
466-
}
467-
if (source[index] === "/" && source[index + 1] === "/") {
468-
inLineComment = true;
469-
index += 1;
470-
continue;
471-
}
472-
473-
const quote = source[index];
474-
if (quote !== '"' && quote !== "'") {
475-
continue;
476-
}
477-
478-
let cursor = index + 1;
479-
let value = "";
480-
while (cursor < source.length) {
481-
const char = source[cursor];
482-
if (char === "\\") {
483-
value += source.slice(cursor, cursor + 2);
484-
cursor += 2;
485-
continue;
486-
}
487-
if (char === quote) {
488-
break;
489-
}
490-
value += char;
491-
cursor += 1;
492-
}
493-
if (cursor >= source.length) {
494-
break;
495-
}
496-
497-
if (value.startsWith(".") && isImportSpecifierContext(source, index)) {
498-
specifiers.push(value);
499-
}
500-
index = cursor;
501-
}
502-
return specifiers;
503-
}
504-
505-
function expandInstalledDistImportClosure(params) {
506-
const files = [...new Set(params.files)];
507-
const fileSet = new Set(files);
508-
const expectedSet = new Set(params.seedFiles);
509-
let changed = true;
510-
511-
while (changed) {
512-
changed = false;
513-
for (const importerPath of [...expectedSet]
514-
.filter((file) => fileSet.has(file))
515-
.toSorted((left, right) => left.localeCompare(right))) {
516-
if (!JS_DIST_FILE_RE.test(importerPath) || importerPath.includes("/node_modules/")) {
517-
continue;
518-
}
519-
let source;
520-
try {
521-
source = params.readText(importerPath);
522-
} catch (error) {
523-
if (error?.code === "ENOENT") {
524-
continue;
525-
}
526-
throw error;
527-
}
528-
for (const specifier of collectImportSpecifiers(source)) {
529-
const importedPath = resolveDistImportPath(importerPath, specifier);
530-
if (!importedPath || !fileSet.has(importedPath) || expectedSet.has(importedPath)) {
531-
continue;
532-
}
533-
expectedSet.add(importedPath);
534-
changed = true;
535-
}
536-
}
537-
}
538-
539-
return [...expectedSet].toSorted((left, right) => left.localeCompare(right));
540-
}
541-
542396
export function pruneInstalledPackageDist(params = {}) {
543397
const packageRoot = params.packageRoot ?? DEFAULT_PACKAGE_ROOT;
544398
const removeFile = params.unlinkSync ?? unlinkSync;
@@ -569,11 +423,18 @@ export function pruneInstalledPackageDist(params = {}) {
569423
const installedFiles = listInstalledDistFiles(params);
570424
const readFile = params.readFileSync ?? readFileSync;
571425
expectedFiles = new Set(
572-
expandInstalledDistImportClosure({
426+
expandPackageDistImportClosure({
573427
files: installedFiles,
574428
seedFiles: [...expectedFiles],
575429
readText(relativePath) {
576-
return readFile(join(packageRoot, relativePath), "utf8");
430+
try {
431+
return readFile(join(packageRoot, relativePath), "utf8");
432+
} catch (error) {
433+
if (error?.code === "ENOENT") {
434+
return "";
435+
}
436+
throw error;
437+
}
577438
},
578439
}),
579440
);

0 commit comments

Comments
 (0)