#!/usr/bin/env bun import { Glob } from "bun"; import * as path from "node:path"; const docsDir = new URL("../../../docs/", import.meta.url).pathname; const outputPath = new URL("../src/internal-urls/docs-index.generated.ts", import.meta.url).pathname; const importBase = "../../../../docs"; function toIdentifier(relativePath: string): string { const withoutExt = relativePath.replace(/\.md$/i, ""); const parts = withoutExt .split(/[^a-zA-Z0-9]+/) .filter(Boolean) .map((part, index) => { if (index === 0) { return part.toLowerCase(); } return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase(); }); const base = parts.length > 0 ? parts.join("") : "doc"; const safeBase = /^[0-9]/.test(base) ? `doc${base}` : base; return `${safeBase}Md`; } const glob = new Glob("**/*.md"); const entries: string[] = []; for await (const relativePath of glob.scan(docsDir)) { entries.push(relativePath.split(path.sep).join("/")); } entries.sort(); const usedIdentifiers = new Set(); const docs = entries.map((relativePath) => { let identifier = toIdentifier(relativePath); let suffix = 2; while (usedIdentifiers.has(identifier)) { identifier = `${toIdentifier(relativePath)}${suffix}`; suffix++; } usedIdentifiers.add(identifier); return { relativePath, identifier }; }); docs.sort((a, b) => a.identifier.localeCompare(b.identifier) || a.relativePath.localeCompare(b.relativePath)); const imports = docs .map(({ relativePath, identifier }) => `import ${identifier} from "${importBase}/${relativePath}" with { type: "text" };`) .join("\n"); const mapEntries = docs.map(({ relativePath, identifier }) => `\t"${relativePath}": ${identifier},`).join("\n"); const output = `// Auto-generated by scripts/generate-docs-index.ts - DO NOT EDIT\n\n${imports}\n\nexport const EMBEDDED_DOCS: Readonly> = {\n${mapEntries}\n};\n\nexport const EMBEDDED_DOC_FILENAMES = Object.keys(EMBEDDED_DOCS).sort();\n`; await Bun.write(outputPath, output); console.log(`Generated ${path.relative(process.cwd(), outputPath)} (${docs.length} docs)`);