From 8151231e0fccd81794e1be621b3d9911ed722c8b Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 2 May 2026 19:32:52 -0700 Subject: [PATCH] test(plugins): guard generated inventory coverage --- scripts/generate-plugin-inventory-doc.mjs | 52 ++++++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/scripts/generate-plugin-inventory-doc.mjs b/scripts/generate-plugin-inventory-doc.mjs index 8f572200b72..6df64c25ab5 100644 --- a/scripts/generate-plugin-inventory-doc.mjs +++ b/scripts/generate-plugin-inventory-doc.mjs @@ -33,6 +33,10 @@ function readJson(relativePath) { return JSON.parse(fs.readFileSync(path.join(ROOT, relativePath), "utf8")); } +function readJsonPath(filePath) { + return JSON.parse(fs.readFileSync(filePath, "utf8")); +} + function fileExists(relativePath) { return fs.existsSync(path.join(ROOT, relativePath)); } @@ -416,11 +420,8 @@ ${renderTable(records)} `; } -function collectPluginRecords() { - const rootPackageJson = readJson("package.json"); - const excludedDirs = collectExcludedPackagedExtensionDirs(rootPackageJson); - const records = []; - +function collectPluginSourceEntries() { + const entries = []; for (const dirName of fs .readdirSync(EXTENSIONS_DIR) .toSorted((left, right) => left.localeCompare(right))) { @@ -429,9 +430,45 @@ function collectPluginRecords() { if (!fs.existsSync(packagePath) || !fs.existsSync(manifestPath)) { continue; } - const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf8")); - const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8")); + const packageJson = readJsonPath(packagePath); + const manifest = readJsonPath(manifestPath); const id = typeof manifest.id === "string" && manifest.id ? manifest.id : dirName; + entries.push({ dirName, id, manifest, packageJson }); + } + return entries; +} + +function validatePluginCoverage(records, sourceEntries) { + const expectedIds = sourceEntries + .map((entry) => entry.id) + .toSorted((left, right) => left.localeCompare(right)); + const actualIds = records + .map((record) => record.id) + .toSorted((left, right) => left.localeCompare(right)); + const missing = expectedIds.filter((id) => !actualIds.includes(id)); + const extra = actualIds.filter((id) => !expectedIds.includes(id)); + const duplicateIds = actualIds.filter((id, index) => actualIds.indexOf(id) !== index); + if (missing.length > 0 || extra.length > 0 || duplicateIds.length > 0) { + throw new Error( + [ + "plugin inventory coverage mismatch", + missing.length > 0 ? `missing: ${missing.join(", ")}` : null, + extra.length > 0 ? `extra: ${extra.join(", ")}` : null, + duplicateIds.length > 0 ? `duplicates: ${duplicateIds.join(", ")}` : null, + ] + .filter(Boolean) + .join("; "), + ); + } +} + +function collectPluginRecords() { + const rootPackageJson = readJson("package.json"); + const excludedDirs = collectExcludedPackagedExtensionDirs(rootPackageJson); + const sourceEntries = collectPluginSourceEntries(); + const records = []; + + for (const { dirName, id, manifest, packageJson } of sourceEntries) { const status = resolveStatus({ dirName, packageJson, excludedDirs }); records.push({ description: resolveDescription({ manifest, packageJson }), @@ -445,6 +482,7 @@ function collectPluginRecords() { }); } + validatePluginCoverage(records, sourceEntries); return records.toSorted((left, right) => left.id.localeCompare(right.id)); }