mirror of
https://github.com/civitai/civitai.git
synced 2026-09-20 22:08:18 +08:00
70 lines
3.0 KiB
JavaScript
70 lines
3.0 KiB
JavaScript
// Generates the moderator endpoint catalog that `/moderator/api` renders.
|
|
//
|
|
// The spoke gets this free from Vite's `import.meta.glob`. Next has no equivalent, and a registry the
|
|
// builder populates at import time does not help: the side effect only fires once a module is imported,
|
|
// so the catalog would have to import every endpoint to discover them — dragging every moderator service
|
|
// into the docs page's module graph, which is what the generated lazy `import()`s exist to avoid.
|
|
//
|
|
// So: scan for the files, emit the loaders.
|
|
//
|
|
// Run BY HAND — `pnpm run generate:moderator-endpoints` — deliberately not wired into install or build.
|
|
// The output is committed, so a fresh checkout typechecks without it. The cost of that choice: adding an
|
|
// endpoint and forgetting this leaves it missing from /moderator/api, and nothing fails.
|
|
|
|
import { readdir, readFile, writeFile } from 'node:fs/promises';
|
|
import { join, relative, sep } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const ROOT = join(fileURLToPath(new URL('.', import.meta.url)), '..');
|
|
const API_ROOT = join(ROOT, 'src', 'pages', 'api');
|
|
const OUT = join(ROOT, 'src', 'server', 'utils', 'moderator-endpoint-catalog.generated.ts');
|
|
const MARKER = 'defineModeratorEndpoint(';
|
|
|
|
async function walk(dir) {
|
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
const files = await Promise.all(
|
|
entries.map((entry) => {
|
|
const full = join(dir, entry.name);
|
|
if (entry.isDirectory()) return walk(full);
|
|
return entry.isFile() && entry.name.endsWith('.ts') ? [full] : [];
|
|
})
|
|
);
|
|
return files.flat();
|
|
}
|
|
|
|
/** `src/pages/api/mod/homeblock/create.ts` -> `/api/mod/homeblock/create`. */
|
|
function routePath(file) {
|
|
const rel = relative(join(ROOT, 'src', 'pages'), file).split(sep).join('/');
|
|
return '/' + rel.replace(/\.ts$/, '').replace(/\/index$/, '');
|
|
}
|
|
|
|
const files = await walk(API_ROOT);
|
|
const matched = [];
|
|
for (const file of files) {
|
|
const source = await readFile(file, 'utf-8');
|
|
if (source.includes(MARKER)) matched.push(file);
|
|
}
|
|
|
|
// Sorted so the output is stable — an unstable order would show up as a spurious diff on every run.
|
|
const entries = matched
|
|
.map((file) => ({ path: routePath(file), module: routePath(file).replace(/^\/api/, '~/pages/api') }))
|
|
.sort((a, b) => a.path.localeCompare(b.path));
|
|
|
|
const header = [
|
|
'// GENERATED by scripts/generate-moderator-endpoint-catalog.mjs — do not edit.',
|
|
"// Re-run 'pnpm run generate:moderator-endpoints' after adding or removing a moderator endpoint.",
|
|
'',
|
|
'export const MODERATOR_ENDPOINT_MODULES: Record<string, () => Promise<unknown>> = {',
|
|
].join('\n');
|
|
|
|
const lines = entries.map((e) => " '" + e.path + "': () => import('" + e.module + "'),");
|
|
const body = [header, ...lines, '};', ''].join('\n');
|
|
|
|
const previous = await readFile(OUT, 'utf-8').catch(() => null);
|
|
if (previous !== body) {
|
|
await writeFile(OUT, body, 'utf-8');
|
|
console.log(`moderator endpoint catalog: wrote ${entries.length} endpoint(s)`);
|
|
} else {
|
|
console.log(`moderator endpoint catalog: up to date (${entries.length} endpoint(s))`);
|
|
}
|