diff --git a/scripts/patch-micropub-category-from-posts.mjs b/scripts/patch-micropub-category-from-posts.mjs new file mode 100644 index 00000000..63921a5e --- /dev/null +++ b/scripts/patch-micropub-category-from-posts.mjs @@ -0,0 +1,81 @@ +/** + * Patch: make q=category return distinct tags from MongoDB posts collection. + * + * In queryController, line 28 rewrites q === "category" → "categories" before + * the switch, so the new case label must be "categories" (not "category"). + * The default branch returns config.categories (always []) because + * publication.categories is not configured. This patch inserts a + * case "categories": before default: that queries the posts collection instead. + * + * postsCollection may be undefined (no MongoDB). The ternary provides a [] + * fallback consistent with the guard pattern used throughout query.js. + */ + +import { access, readFile, writeFile } from "node:fs/promises"; + +const MARKER = "// [patch] micropub-category-from-posts"; + +const candidates = [ + "node_modules/@indiekit/endpoint-micropub/lib/controllers/query.js", + "node_modules/@indiekit/indiekit/node_modules/@indiekit/endpoint-micropub/lib/controllers/query.js", +]; + +const OLD_SNIPPET = ` break; + } + + default: { + // Query configuration value (can be filtered, limited and offset) + if (config[q]) {`; + +const NEW_SNIPPET = ` break; + } + + case "categories": { ${MARKER} + const cats = postsCollection + ? (await postsCollection.distinct("properties.category")).filter(Boolean).sort() + : []; + response.json({ + categories: queryConfig(cats, { filter: filter?.toLowerCase(), limit, offset }), + }); + break; + } + + default: { + // Query configuration value (can be filtered, limited and offset) + if (config[q]) {`; + +async function exists(p) { + try { await access(p); return true; } catch { return false; } +} + +let totalPatched = 0; +let totalChecked = 0; + +for (const filePath of candidates) { + if (!(await exists(filePath))) continue; + totalChecked++; + + const source = await readFile(filePath, "utf8"); + if (source.includes(MARKER)) { + console.log(`[postinstall] patch-micropub-category-from-posts: already applied to ${filePath}`); + continue; + } + + if (!source.includes(OLD_SNIPPET)) { + console.warn(`[postinstall] patch-micropub-category-from-posts: snippet not found in ${filePath} (upstream changed?)`); + continue; + } + + const updated = source.replace(OLD_SNIPPET, NEW_SNIPPET); + await writeFile(filePath, updated, "utf8"); + console.log(`[postinstall] Applied patch-micropub-category-from-posts to ${filePath}`); + totalPatched++; +} + +if (totalChecked === 0) { + console.log("[postinstall] patch-micropub-category-from-posts: no target files found"); +} else if (totalPatched === 0) { + console.log("[postinstall] patch-micropub-category-from-posts: already up to date"); +} else { + console.log(`[postinstall] patch-micropub-category-from-posts: patched ${totalPatched} file(s)`); +}