Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions scripts/validate-plugins.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env node

import { readFileSync, existsSync } from "fs";
import { readFileSync, existsSync, readdirSync, statSync } from "fs";
import { resolve, dirname } from "path";
import { fileURLToPath } from "url";
import Ajv from "ajv";
Expand Down Expand Up @@ -92,7 +92,35 @@ for (const entry of marketplace.plugins ?? []) {
}
}

// 3. Report results
// 3. Every plugin directory in the repo must be listed in marketplace.json.
// A directory with a .cursor-plugin/plugin.json that marketplace.json does not
// reference is invisible to the marketplace and to the checks above, so a
// forgotten listing (or a typo in `source`) would otherwise pass silently.
const listedDirs = new Set(
(marketplace.plugins ?? []).map((entry) => resolve(root, entry.source))
);

function pluginDirsUnder(dir) {
if (!existsSync(dir)) return [];
return readdirSync(dir)
.filter((name) => !name.startsWith("."))
.map((name) => resolve(dir, name))
.filter(
(path) =>
statSync(path).isDirectory() &&
existsSync(resolve(path, ".cursor-plugin/plugin.json"))
);
}

for (const dir of [...pluginDirsUnder(root), ...pluginDirsUnder(resolve(root, "third_party"))]) {
if (!listedDirs.has(dir)) {
fail(
`Unlisted plugin: "${dir.slice(root.length + 1)}" has a .cursor-plugin/plugin.json but no entry in marketplace.json`
);
}
}

// 4. Report results
if (errors > 0) {
console.error(`\nValidation failed with ${errors} error(s).`);
process.exit(1);
Expand Down