Refuse a shadowing barrel copy, and record the packaging contract
This commit is contained in:
@@ -57,6 +57,7 @@ const badCases: Array<{ name: string; files: Record<string, string>; match: RegE
|
||||
{ name: "a route gating on a bare word", files: { "bare/plugin.ts": `export default { apiVersion: "1.0.0", routes: [{ method: "GET", path: "/", permission: "admin", handler: () => ({ html: "x" }) }] };` }, match: /bare.*admin.*<resource>:<action>/s },
|
||||
{ name: "a nav node gating on a bare word", files: { "barenav/plugin.ts": `export default { apiVersion: "1.0.0", nav: [{ id: "n", label: "N", permission: "admin" }] };` }, match: /barenav.*admin.*<resource>:<action>/s },
|
||||
{ name: "a declared permission that is a bare word", files: { "baredecl/plugin.ts": `export default { apiVersion: "1.0.0", permissions: [{ name: "admin" }] };` }, match: /baredecl.*admin.*<resource>:<action>/s },
|
||||
{ name: "a plugin shipping its own copy of the barrel", files: { "shadow/node_modules/@plainpages/plugin-api/index.js": `export class GuardError extends Error {}`, "shadow/plugin.ts": full("shadow") }, match: /shadow.*@plainpages\/plugin-api/s },
|
||||
{ name: "a plugin package.json that forgets type: module", files: { "cjs/package.json": `{ "name": "cjs" }`, "cjs/plugin.ts": full("cjs") }, match: /cjs.*"type": "module"/s },
|
||||
{ name: "a plugin package.json that is not valid JSON", files: { "bent/package.json": `{`, "bent/plugin.ts": full("bent") }, match: /bent.*package\.json.*JSON/s },
|
||||
{ name: "two plugins claim the public home", files: { "a/plugin.ts": `export default { apiVersion: "1.0.0", home: () => ({ html: "a" }) };`, "b/plugin.ts": `export default { apiVersion: "1.0.0", home: () => ({ html: "b" }) };` }, match: /home/ },
|
||||
|
||||
@@ -89,9 +89,14 @@ function pluginFolders(dir: string): string[] {
|
||||
.sort();
|
||||
}
|
||||
|
||||
// Without a `type`, which npm never writes, the plugin's own package.json leaves its folder
|
||||
// CommonJS: a .js helper breaks outright and every .ts costs a re-parse.
|
||||
// The two ways a plugin's own packaging breaks it. A barrel copy resolves before the host's, and its
|
||||
// GuardError matches no `instanceof` here — the sign-in redirect silently becomes a 500. Without a
|
||||
// `type`, which npm never writes, the folder is left CommonJS: a .js helper breaks, every .ts re-parses.
|
||||
function packagingError(folder: string): string | null {
|
||||
if (existsSync(join(folder, "node_modules", "@plainpages", "plugin-api"))) {
|
||||
return "ships its own copy of @plainpages/plugin-api — remove it; the host provides the one instance";
|
||||
}
|
||||
|
||||
const file = join(folder, "package.json");
|
||||
if (!existsSync(file)) return null;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user