@blaaiz/docs-core 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-JHN5XQPU.js → chunk-ZWDLAW5M.js} +6 -5
- package/dist/chunk-ZWDLAW5M.js.map +1 -0
- package/dist/generator.cjs +44 -13
- package/dist/generator.cjs.map +1 -1
- package/dist/generator.js +41 -11
- package/dist/generator.js.map +1 -1
- package/dist/index.cjs +4 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -5
- package/dist/index.d.ts +19 -5
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/skills/SKILL.md +5 -0
- package/dist/chunk-JHN5XQPU.js.map +0 -1
package/dist/generator.cjs
CHANGED
|
@@ -48,7 +48,7 @@ function planDocsTree(navigation, options = {}) {
|
|
|
48
48
|
const apiGroups = [];
|
|
49
49
|
const metas = [];
|
|
50
50
|
const rootPages = [];
|
|
51
|
-
const
|
|
51
|
+
const apiDocLinks = [];
|
|
52
52
|
const apiTabs = navigation.filter((tab) => hasOpenApi(tab.items));
|
|
53
53
|
const soleApiTab = apiTabs.length === 1;
|
|
54
54
|
for (const tab of navigation) {
|
|
@@ -59,7 +59,7 @@ function planDocsTree(navigation, options = {}) {
|
|
|
59
59
|
planDocTab(tab);
|
|
60
60
|
}
|
|
61
61
|
metas.unshift({ path: "meta.json", content: { pages: rootPages } });
|
|
62
|
-
return { specs, apiPages, apiGroups, metas,
|
|
62
|
+
return { specs, apiPages, apiGroups, metas, apiDocLinks };
|
|
63
63
|
function planApiTab(tab) {
|
|
64
64
|
const tabDir = soleApiTab ? apiDir : slugify(tab.title);
|
|
65
65
|
rootPages.push(tabDir);
|
|
@@ -102,7 +102,8 @@ function planDocsTree(navigation, options = {}) {
|
|
|
102
102
|
order += 1;
|
|
103
103
|
continue;
|
|
104
104
|
}
|
|
105
|
-
|
|
105
|
+
apiDocLinks.push({ file: item.file, dir, order });
|
|
106
|
+
order += 1;
|
|
106
107
|
}
|
|
107
108
|
return children;
|
|
108
109
|
}
|
|
@@ -389,6 +390,17 @@ function mergeOpenApiDocuments(inputs, options) {
|
|
|
389
390
|
function slugify2(value) {
|
|
390
391
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
391
392
|
}
|
|
393
|
+
function frontmatterTitle(source) {
|
|
394
|
+
const block = /^---\r?\n([\s\S]*?)\r?\n---/.exec(source);
|
|
395
|
+
if (!block?.[1]) {
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
const line = /^title\s*:\s*(.+)\s*$/m.exec(block[1]);
|
|
399
|
+
if (!line?.[1]) {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
return line[1].replace(/^(['"])(.*)\1$/, "$2");
|
|
403
|
+
}
|
|
392
404
|
function operationKeyOf(content) {
|
|
393
405
|
const match = /operations=\{(\[[\s\S]*?\])\}/.exec(content);
|
|
394
406
|
if (!match?.[1]) {
|
|
@@ -494,6 +506,35 @@ async function generateDocsTree(options) {
|
|
|
494
506
|
}
|
|
495
507
|
});
|
|
496
508
|
}
|
|
509
|
+
const baseUrl = (options.baseUrl ?? "/docs").replace(/\/$/, "");
|
|
510
|
+
const docUrl = (file) => {
|
|
511
|
+
const clean = file.replace(/\/?index$/, "");
|
|
512
|
+
return clean ? `${baseUrl}/${clean}` : baseUrl;
|
|
513
|
+
};
|
|
514
|
+
for (const link of plan.apiDocLinks) {
|
|
515
|
+
const source = path__default.default.join(outDir, `${link.file}.mdx`);
|
|
516
|
+
let title = null;
|
|
517
|
+
try {
|
|
518
|
+
title = frontmatterTitle(await promises.readFile(source, "utf8"));
|
|
519
|
+
} catch {
|
|
520
|
+
console.warn(
|
|
521
|
+
`[docs-core] docs.json lists ${link.file} inside an API tab, but ${source} does not exist. The sidebar entry is skipped.`
|
|
522
|
+
);
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
if (title === null) {
|
|
526
|
+
title = link.file.split("/").pop() ?? link.file;
|
|
527
|
+
console.warn(
|
|
528
|
+
`[docs-core] ${link.file}.mdx has no frontmatter title; its sidebar entry falls back to '${title}'.`
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
const group = groupPages.get(link.dir) ?? [];
|
|
532
|
+
group.push({
|
|
533
|
+
order: link.order,
|
|
534
|
+
slug: `[${title.replace(/[[\]()]/g, "")}](${docUrl(link.file)})`
|
|
535
|
+
});
|
|
536
|
+
groupPages.set(link.dir, group);
|
|
537
|
+
}
|
|
497
538
|
const metas = [
|
|
498
539
|
...plan.metas,
|
|
499
540
|
...plan.apiGroups.map((group) => ({
|
|
@@ -518,11 +559,6 @@ async function generateDocsTree(options) {
|
|
|
518
559
|
await promises.writeFile(target, `${JSON.stringify(meta.content, null, 2)}
|
|
519
560
|
`);
|
|
520
561
|
}
|
|
521
|
-
const baseUrl = (options.baseUrl ?? "/docs").replace(/\/$/, "");
|
|
522
|
-
const docUrl = (file) => {
|
|
523
|
-
const clean = file.replace(/\/?index$/, "");
|
|
524
|
-
return clean ? `${baseUrl}/${clean}` : baseUrl;
|
|
525
|
-
};
|
|
526
562
|
const firstUrl = (items) => {
|
|
527
563
|
for (const item of items) {
|
|
528
564
|
if (item.kind === "group") {
|
|
@@ -565,11 +601,6 @@ async function generateDocsTree(options) {
|
|
|
565
601
|
`${JSON.stringify(methodsByUrl, null, 2)}
|
|
566
602
|
`
|
|
567
603
|
);
|
|
568
|
-
if (plan.apiTabDocPages.length > 0) {
|
|
569
|
-
console.warn(
|
|
570
|
-
`[docs-core] ${String(plan.apiTabDocPages.length)} doc page(s) are declared inside API tabs and cannot be placed in the generated sidebar folders: ${plan.apiTabDocPages.join(", ")}. Each page stays reachable at its own URL; to list one in the sidebar, move it into a doc tab.`
|
|
571
|
-
);
|
|
572
|
-
}
|
|
573
604
|
return { pages, metas: metas.length, navLinks, methodsByUrl, document };
|
|
574
605
|
}
|
|
575
606
|
|
package/dist/generator.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/docs-tree.ts","../src/adapters/docs-json/parse-navigation.ts","../src/adapters/openapi/merge-documents.ts","../src/generator/index.ts"],"names":["path","isRecord","HTTP_METHODS","slugify","readFile","rm","mkdir","writeFile"],"mappings":"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","file":"generator.cjs","sourcesContent":["/**\n * Turns the normalized navigation into a content-tree plan: which folders and\n * `meta.json` files encode the navigation, and where each API page belongs.\n * The site's `docs.json` stays the single source of truth — authors reorganize\n * routes and groups by editing one file, never by moving generated files.\n *\n * The planner is pure; `@blaaiz/docs-core/generator` executes the plan.\n *\n * @packageDocumentation\n */\n\nimport type { NavGroup, NavItem, NavTab, Navigation } from './navigation.js';\n\n/**\n * A planned API reference page: one operation and the folder it belongs to.\n *\n * @public\n */\nexport interface PlannedApiPage {\n /** Spec file reference from `docs.json`, relative to the content directory. */\n readonly file: string;\n /** The operation's HTTP method. */\n readonly method: string;\n /** The operation's API route. */\n readonly apiPath: string;\n /** Output folder, relative to the docs output directory (e.g. `api/authentication`). */\n readonly dir: string;\n /** Position within the group. */\n readonly order: number;\n}\n\n/**\n * A child folder of a planned API group, at its position among the group's\n * pages. The generator merges these with the resolved page slugs to write the\n * group's `meta.json` in declared order.\n *\n * @public\n */\nexport interface PlannedGroupChild {\n readonly order: number;\n /** The child folder's name within the group directory. */\n readonly folder: string;\n}\n\n/**\n * A planned API group folder. The generator fills its page order once the\n * operation slugs are known, interleaving {@link PlannedGroupChild} folders at\n * their declared positions. A group with `root: true` is an API tab's folder.\n *\n * @public\n */\nexport interface PlannedApiGroup {\n readonly dir: string;\n readonly title: string;\n readonly children: readonly PlannedGroupChild[];\n /** Set on an API tab's own folder, so the sidebar scopes to the tab. */\n readonly root?: boolean;\n /** The tab's icon, on a `root` group. */\n readonly icon?: string;\n}\n\n/**\n * A `meta.json` file the plan wants written.\n *\n * @public\n */\nexport interface PlannedMeta {\n /** Path relative to the docs output directory, e.g. `guides/meta.json`. */\n readonly path: string;\n readonly content: Record<string, unknown>;\n}\n\n/**\n * The complete content-tree plan for one navigation.\n *\n * @public\n */\nexport interface DocsTreePlan {\n /** Unique spec files referenced by the navigation, in order of appearance. */\n readonly specs: readonly string[];\n readonly apiPages: readonly PlannedApiPage[];\n readonly apiGroups: readonly PlannedApiGroup[];\n /** Fully-known meta files: the root, doc tab folders, and doc groups. */\n readonly metas: readonly PlannedMeta[];\n /**\n * Doc pages declared inside API tabs. The plan cannot place them in the\n * generated folders, so the generator reports them instead of dropping them\n * silently; they stay reachable at their own URLs.\n */\n readonly apiTabDocPages: readonly string[];\n}\n\n/**\n * Options for {@link planDocsTree}.\n *\n * @public\n */\nexport interface PlanDocsTreeOptions {\n /** Folder for generated API pages when one tab publishes them. Default `api`. */\n readonly apiDir?: string;\n}\n\n/** Kebab-case a title into a folder name. */\nfunction slugify(title: string): string {\n return title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\nfunction isOpenApi(item: NavItem): boolean {\n return item.kind === 'openapi';\n}\n\nfunction hasOpenApi(items: readonly NavItem[]): boolean {\n return items.some((item) => (item.kind === 'group' ? hasOpenApi(item.items) : isOpenApi(item)));\n}\n\n/** The basename of a doc page path (its file name in the folder). */\nfunction basename(file: string): string {\n const i = file.lastIndexOf('/');\n return i === -1 ? file : file.slice(i + 1);\n}\n\n/** The directory of a doc page path, or '' for a root page. */\nfunction dirname(file: string): string {\n const i = file.lastIndexOf('/');\n return i === -1 ? '' : file.slice(0, i);\n}\n\n/** The first doc page in a group, descending nested groups in order. */\nfunction firstDocFile(items: readonly NavItem[]): string | undefined {\n for (const item of items) {\n if (item.kind === 'doc') {\n return item.file;\n }\n if (item.kind === 'group') {\n const found = firstDocFile(item.items);\n if (found !== undefined) {\n return found;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Plan the content tree for a navigation.\n *\n * Conventions (Mintlify-shaped):\n * - A tab whose items reference OpenAPI files becomes an API tab. With one API\n * tab its folder is `apiDir`; with several, each tab's folder is its\n * slugified title, so tabs never collide. Groups become nested folders named\n * by slugified group title, to any depth.\n * - A tab of doc pages becomes the folder its pages share (their first path\n * segment); a doc group's pages must share one directory. Doc groups nest.\n * - Root-level doc pages (no `/` in the path) are listed in the root meta.\n * - Tab folders get `root: true`, so the sidebar scopes to the active tab.\n * - Sidebar order is declaration order, pages and groups interleaved.\n *\n * @param navigation - the parsed navigation\n * @param options - the API folder name\n * @returns the plan the generator executes\n * @throws when two sibling groups slugify to the same folder\n * @public\n */\nexport function planDocsTree(\n navigation: Navigation,\n options: PlanDocsTreeOptions = {},\n): DocsTreePlan {\n const apiDir = options.apiDir ?? 'api';\n\n const specs: string[] = [];\n const apiPages: PlannedApiPage[] = [];\n const apiGroups: PlannedApiGroup[] = [];\n const metas: PlannedMeta[] = [];\n const rootPages: string[] = [];\n const apiTabDocPages: string[] = [];\n\n const apiTabs = navigation.filter((tab) => hasOpenApi(tab.items));\n const soleApiTab = apiTabs.length === 1;\n\n for (const tab of navigation) {\n if (hasOpenApi(tab.items)) {\n planApiTab(tab);\n continue;\n }\n planDocTab(tab);\n }\n\n metas.unshift({ path: 'meta.json', content: { pages: rootPages } });\n\n return { specs, apiPages, apiGroups, metas, apiTabDocPages };\n\n function planApiTab(tab: NavTab): void {\n const tabDir = soleApiTab ? apiDir : slugify(tab.title);\n rootPages.push(tabDir);\n const at = apiGroups.length;\n const children = planApiItems(tab.items, tabDir);\n apiGroups.splice(at, 0, {\n dir: tabDir,\n title: tab.title,\n root: true,\n ...(tab.icon ? { icon: tab.icon } : {}),\n children,\n });\n }\n\n /**\n * Plan a run of API-tab items into `dir`: operations become planned pages,\n * nested groups become child folders, and doc pages are reported. Returns\n * the child folders at their declared positions.\n */\n function planApiItems(items: readonly NavItem[], dir: string): PlannedGroupChild[] {\n const children: PlannedGroupChild[] = [];\n const takenFolders = new Set<string>();\n let order = 0;\n for (const item of items) {\n if (item.kind === 'group') {\n const folder = slugify(item.title);\n if (takenFolders.has(folder)) {\n throw new Error(\n `[docs-core] Two sibling groups under '${dir}' share the folder name '${folder}'. ` +\n 'Rename one of the groups in docs.json.',\n );\n }\n takenFolders.add(folder);\n const childDir = `${dir}/${folder}`;\n const at = apiGroups.length;\n const sub = planApiItems(item.items, childDir);\n apiGroups.splice(at, 0, { dir: childDir, title: item.title, children: sub });\n children.push({ order, folder });\n order += 1;\n continue;\n }\n if (item.kind === 'openapi') {\n if (!specs.includes(item.file)) {\n specs.push(item.file);\n }\n apiPages.push({ file: item.file, method: item.method, apiPath: item.apiPath, dir, order });\n order += 1;\n continue;\n }\n apiTabDocPages.push(item.file);\n }\n return children;\n }\n\n function planDocTab(tab: NavTab): void {\n const directPages = tab.items.filter((item) => item.kind === 'doc');\n const groups = tab.items.filter((item) => item.kind === 'group');\n\n // A tab of root-level pages (e.g. Home -> index) lists them at the root.\n if (groups.length === 0 && directPages.every((page) => !page.file.includes('/'))) {\n for (const page of directPages) {\n rootPages.push(page.file);\n }\n return;\n }\n\n // The tab folder is the first path segment its pages share.\n const firstFile = firstDocFile(tab.items);\n if (firstFile === undefined) {\n return;\n }\n const tabDir = firstFile.split('/')[0] ?? '';\n rootPages.push(tabDir);\n\n metas.push({\n path: `${tabDir}/meta.json`,\n content: {\n title: tab.title,\n root: true,\n ...(tab.icon ? { icon: tab.icon } : {}),\n pages: planDocItems(tab.items, tabDir),\n },\n });\n }\n\n /**\n * Plan a run of doc-tab items belonging to the folder `dir`: pages are\n * listed by basename, nested groups get their own `meta.json` and are\n * listed by their folder path relative to `dir`. A nested group whose pages\n * live directly in `dir` is flattened into it. Returns the meta entries in\n * declared order.\n */\n function planDocItems(items: readonly NavItem[], dir: string): string[] {\n const entries: string[] = [];\n for (const item of items) {\n if (item.kind === 'doc') {\n entries.push(basename(item.file));\n continue;\n }\n if (item.kind !== 'group') {\n continue;\n }\n const groupDir = planDocGroup(item, dir);\n if (groupDir === null) {\n continue;\n }\n if (groupDir === dir) {\n // The group's pages live directly in this folder.\n entries.push(...planDocItems(item.items, dir));\n continue;\n }\n entries.push(groupDir.slice(dir.length + 1));\n }\n return entries;\n }\n\n /**\n * Plan one doc group. Returns the group's directory, `dir` itself when the\n * group flattens into its parent folder, or `null` for an empty group.\n */\n function planDocGroup(group: NavGroup, dir: string): string | null {\n const firstFile = firstDocFile(group.items);\n if (firstFile === undefined) {\n return null;\n }\n const groupDir = dirname(firstFile);\n if (groupDir === dir || groupDir === '') {\n return dir;\n }\n metas.push({\n path: `${groupDir}/meta.json`,\n content: { title: group.title, pages: planDocItems(group.items, groupDir) },\n });\n return groupDir;\n }\n}\n","/**\n * `docs.json` navigation adapter.\n *\n * Translates a Mintlify `docs.json` navigation block into the framework's\n * normalized {@link Navigation} tree. It understands the two page-string forms\n * Mintlify uses — a plain content path, and an OpenAPI entry of the form\n * `\"<file> <METHOD> <route>\"` — and it recurses through arbitrarily nested\n * groups.\n *\n * @packageDocumentation\n */\n\nimport {\n type NavGroup,\n type NavItem,\n type NavMethod,\n type NavPage,\n type NavTab,\n type Navigation,\n} from '../../core/index.js';\n\nconst HTTP_METHODS: ReadonlySet<string> = new Set([\n 'GET',\n 'POST',\n 'PUT',\n 'PATCH',\n 'DELETE',\n 'HEAD',\n 'OPTIONS',\n 'TRACE',\n]);\n\n/**\n * Thrown when a `docs.json` navigation block does not match the expected shape.\n *\n * @public\n */\nexport class InvalidNavigationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'InvalidNavigationError';\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Parse a single page string into a normalized page node.\n *\n * A string shaped `\"<file> <METHOD> <route>\"` (where `<METHOD>` is a known HTTP\n * method) becomes an {@link OpenApiPage}; anything else becomes a {@link DocPage}.\n */\nfunction parsePageString(entry: string): NavPage {\n const parts = entry.trim().split(/\\s+/);\n const [file, maybeMethod, ...rest] = parts;\n\n if (file === undefined || file === '') {\n throw new InvalidNavigationError('Encountered an empty navigation page entry.');\n }\n\n if (maybeMethod !== undefined && rest.length > 0 && HTTP_METHODS.has(maybeMethod.toUpperCase())) {\n return {\n kind: 'openapi',\n file,\n method: maybeMethod.toUpperCase() as NavMethod,\n apiPath: rest.join(' '),\n };\n }\n\n return { kind: 'doc', file: entry.trim() };\n}\n\nfunction parseItem(entry: unknown, path: string): NavItem {\n if (typeof entry === 'string') {\n return parsePageString(entry);\n }\n\n if (isRecord(entry) && typeof entry['group'] === 'string') {\n return parseGroup(entry, path);\n }\n\n throw new InvalidNavigationError(\n `Navigation entry at ${path} must be a page string or a group object.`,\n );\n}\n\nfunction parseItems(pages: unknown, path: string): NavItem[] {\n if (!Array.isArray(pages)) {\n throw new InvalidNavigationError(`Expected an array of pages at ${path}.`);\n }\n return pages.map((entry, index) => parseItem(entry, `${path}[${String(index)}]`));\n}\n\nfunction parseGroup(group: Record<string, unknown>, path: string): NavGroup {\n const title = group['group'];\n if (typeof title !== 'string') {\n throw new InvalidNavigationError(`Group at ${path} is missing a string \"group\" title.`);\n }\n return {\n kind: 'group',\n title,\n items: parseItems(group['pages'], `${path}.pages`),\n };\n}\n\nfunction parseTab(tab: unknown, path: string): NavTab {\n if (!isRecord(tab) || typeof tab['tab'] !== 'string') {\n throw new InvalidNavigationError(`Tab at ${path} is missing a string \"tab\" title.`);\n }\n\n // A tab holds either `groups` or `pages`; normalize both into `items`.\n const items: NavItem[] =\n tab['groups'] !== undefined\n ? parseGroups(tab['groups'], `${path}.groups`)\n : parseItems(tab['pages'], `${path}.pages`);\n\n const icon = tab['icon'];\n return {\n kind: 'tab',\n title: tab['tab'],\n items,\n ...(typeof icon === 'string' ? { icon } : {}),\n };\n}\n\nfunction parseGroups(groups: unknown, path: string): NavGroup[] {\n if (!Array.isArray(groups)) {\n throw new InvalidNavigationError(`Expected an array of groups at ${path}.`);\n }\n return groups.map((group, index) => {\n const groupPath = `${path}[${String(index)}]`;\n if (!isRecord(group)) {\n throw new InvalidNavigationError(`Group at ${groupPath} must be an object.`);\n }\n return parseGroup(group, groupPath);\n });\n}\n\n/**\n * Parse a Mintlify `docs.json` object into the normalized {@link Navigation} tree.\n *\n * @param docsJson - the parsed contents of a `docs.json` file\n * @returns the normalized, ordered list of tabs\n * @throws {@link InvalidNavigationError} when the navigation shape is invalid\n * @public\n */\nexport function parseNavigation(docsJson: unknown): Navigation {\n if (!isRecord(docsJson)) {\n throw new InvalidNavigationError('docs.json must be an object.');\n }\n\n const navigation = docsJson['navigation'];\n if (!isRecord(navigation)) {\n throw new InvalidNavigationError('docs.json must have a \"navigation\" object.');\n }\n\n const tabs = navigation['tabs'];\n if (!Array.isArray(tabs)) {\n throw new InvalidNavigationError('docs.json navigation must have a \"tabs\" array.');\n }\n\n return tabs.map((tab, index) => parseTab(tab, `navigation.tabs[${String(index)}]`));\n}\n","/**\n * OpenAPI merge adapter.\n *\n * A Mintlify-style API reference stores one OpenAPI file per endpoint. Renderers\n * (Scalar, fumadocs-openapi) want a single document. `mergeOpenApiDocuments`\n * combines many single-operation OpenAPI files into one document: it unions\n * servers, tags, and components, and assembles the paths. A genuine conflict —\n * the same method on the same route defined twice — is an error, not a\n * silent overwrite.\n *\n * @packageDocumentation\n */\n\nimport {\n type OpenApiComponents,\n type OpenApiDocument,\n type OpenApiInfo,\n type OpenApiPathItem,\n type OpenApiServer,\n type OpenApiTag,\n} from '../../core/index.js';\n\n/**\n * Options controlling the merge.\n *\n * @public\n */\nexport interface MergeOpenApiOptions {\n /** The `info` block of the merged document. */\n readonly info: OpenApiInfo;\n /**\n * Servers for the merged document. When omitted, servers are unioned from\n * the input documents (deduplicated by `url`).\n */\n readonly servers?: readonly OpenApiServer[];\n /** The OpenAPI version string of the merged document. Defaults to `3.1.0`. */\n readonly openapi?: string;\n}\n\n/**\n * Thrown when an input is not a usable OpenAPI document, or when two inputs\n * define the same operation (method + route).\n *\n * @public\n */\nexport class OpenApiMergeError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'OpenApiMergeError';\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction collectServers(\n inputs: readonly Record<string, unknown>[],\n override: readonly OpenApiServer[] | undefined,\n): OpenApiServer[] {\n if (override !== undefined) {\n return [...override];\n }\n const byUrl = new Map<string, OpenApiServer>();\n for (const doc of inputs) {\n const servers = doc['servers'];\n if (!Array.isArray(servers)) {\n continue;\n }\n for (const server of servers) {\n if (isRecord(server) && typeof server['url'] === 'string' && !byUrl.has(server['url'])) {\n byUrl.set(server['url'], server as unknown as OpenApiServer);\n }\n }\n }\n return [...byUrl.values()];\n}\n\nfunction collectTags(inputs: readonly Record<string, unknown>[]): OpenApiTag[] {\n const byName = new Map<string, OpenApiTag>();\n for (const doc of inputs) {\n const tags = doc['tags'];\n if (!Array.isArray(tags)) {\n continue;\n }\n for (const tag of tags) {\n if (isRecord(tag) && typeof tag['name'] === 'string' && !byName.has(tag['name'])) {\n byName.set(tag['name'], tag as unknown as OpenApiTag);\n }\n }\n }\n return [...byName.values()];\n}\n\nfunction mergeComponents(inputs: readonly Record<string, unknown>[]): OpenApiComponents {\n const merged: Record<string, Record<string, unknown>> = {};\n for (const doc of inputs) {\n const components = doc['components'];\n if (!isRecord(components)) {\n continue;\n }\n for (const [section, value] of Object.entries(components)) {\n if (!isRecord(value)) {\n continue;\n }\n const target = merged[section] ?? (merged[section] = {});\n for (const [key, definition] of Object.entries(value)) {\n // First definition wins; later identical re-declarations are ignored.\n if (!(key in target)) {\n target[key] = definition;\n }\n }\n }\n }\n return merged;\n}\n\nconst HTTP_METHODS = new Set(['get', 'put', 'post', 'delete', 'patch', 'options', 'head', 'trace']);\n\nfunction assemblePaths(\n inputs: readonly Record<string, unknown>[],\n): Record<string, OpenApiPathItem> {\n const paths: Record<string, OpenApiPathItem> = {};\n for (const doc of inputs) {\n const docPaths = doc['paths'];\n if (!isRecord(docPaths)) {\n throw new OpenApiMergeError('Each OpenAPI input must have a \"paths\" object.');\n }\n // Each file's top-level security applies to all its operations. The merged\n // document can't carry one shared security, so push it onto the operations.\n const docSecurity = doc['security'];\n for (const [route, pathItem] of Object.entries(docPaths)) {\n if (!isRecord(pathItem)) {\n throw new OpenApiMergeError(`Path item for \"${route}\" must be an object.`);\n }\n const target = paths[route] ?? (paths[route] = {});\n for (const [method, operation] of Object.entries(pathItem)) {\n if (method in target) {\n throw new OpenApiMergeError(\n `Duplicate operation ${method.toUpperCase()} ${route} across inputs.`,\n );\n }\n let resolved = operation;\n if (\n HTTP_METHODS.has(method.toLowerCase()) &&\n isRecord(operation) &&\n !('security' in operation) &&\n Array.isArray(docSecurity)\n ) {\n resolved = { ...operation, security: docSecurity };\n }\n target[method] = resolved as OpenApiPathItem[string];\n }\n }\n }\n return paths;\n}\n\n/**\n * Merge many single-operation OpenAPI documents into one document.\n *\n * @param inputs - parsed OpenAPI documents, each typically holding one operation\n * @param options - the merged document's `info`, and optional server/version overrides\n * @returns the merged {@link OpenApiDocument}\n * @throws {@link OpenApiMergeError} on an invalid input or a duplicate operation\n * @public\n */\nexport function mergeOpenApiDocuments(\n inputs: readonly unknown[],\n options: MergeOpenApiOptions,\n): OpenApiDocument {\n const records: Record<string, unknown>[] = inputs.map((input, index) => {\n if (!isRecord(input)) {\n throw new OpenApiMergeError(`OpenAPI input at index ${String(index)} must be an object.`);\n }\n return input;\n });\n\n return {\n openapi: options.openapi ?? '3.1.0',\n info: options.info,\n servers: collectServers(records, options.servers),\n tags: collectTags(records),\n paths: assemblePaths(records),\n components: mergeComponents(records),\n };\n}\n","/**\n * `@blaaiz/docs-core/generator` — builds the docs content tree from `docs.json`.\n *\n * One call turns the site's navigation file into everything fumadocs needs:\n * generated API pages placed in their configured groups, and `meta.json` files\n * encoding tab and group order. Authors reorganize the docs by editing\n * `docs.json` — never by moving files.\n *\n * Node-only. A site that publishes an API reference injects `createOpenAPI` and\n * `generateFiles` from its own `fumadocs-openapi` install, so this package takes\n * no dependency on it. A site of prose alone injects nothing: with no API pages\n * in `docs.json` the generator writes the `meta.json` files and `nav.json` and\n * never touches `fumadocs-openapi`.\n *\n * @packageDocumentation\n */\n\nimport { mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { mergeOpenApiDocuments } from '../adapters/index.js';\nimport { parseNavigation } from '../adapters/index.js';\nimport type { NavItem, OpenApiDocument, OpenApiInfo, OpenApiServer } from '../core/index.js';\nimport { planDocsTree } from '../core/index.js';\n\n/**\n * The two fumadocs-openapi functions the generator needs, injected by the site\n * (`createOpenAPI` from `fumadocs-openapi/server`, `generateFiles` from\n * `fumadocs-openapi`). Typed loosely so this package needs no fumadocs types.\n *\n * @public\n */\nexport interface FumadocsOpenApi {\n readonly createOpenAPI: (...args: never[]) => unknown;\n readonly generateFiles: (...args: never[]) => unknown;\n}\n\n/**\n * Options for {@link generateDocsTree}.\n *\n * @public\n */\nexport interface GenerateDocsTreeOptions {\n /** The site's content directory (holds `docs.json`, spec files, and `docs/`). */\n readonly contentDir: string;\n /** Path to the navigation file. Default `<contentDir>/docs.json`. */\n readonly docsJson?: string;\n /** Output directory for the page tree. Default `<contentDir>/docs`. */\n readonly outDir?: string;\n /** Folder for generated API pages. Default `api`. */\n readonly apiDir?: string;\n /**\n * `info` block of the merged OpenAPI document. Required when `docs.json`\n * publishes API pages; ignored on a site of prose alone, which merges no\n * documents.\n */\n readonly info?: OpenApiInfo;\n /** Servers of the merged document. */\n readonly servers?: readonly OpenApiServer[];\n /** Same-origin proxy route for the try-it playground. */\n readonly proxyUrl?: string;\n /** URL base the fumadocs loader serves pages under. Default `/docs`. */\n readonly baseUrl?: string;\n /**\n * The site's fumadocs-openapi functions. Required when `docs.json` publishes\n * API pages; omit it on a site of prose alone.\n */\n readonly fumadocs?: FumadocsOpenApi;\n}\n\n/**\n * A top-navigation link: one tab, pointing at its first page.\n *\n * @public\n */\nexport interface NavLink {\n readonly text: string;\n readonly url: string;\n /** The tab's icon name from `docs.json`, for the site to resolve. */\n readonly icon?: string;\n}\n\n/**\n * What the generator produced.\n *\n * @public\n */\nexport interface GenerateDocsTreeResult {\n readonly pages: number;\n readonly metas: number;\n /**\n * Top-nav links, one per tab, each pointing at the tab's first page — written\n * to `<contentDir>/nav.json` for the site's layout. Keeps the top nav in sync\n * with `docs.json` instead of a hand-maintained list.\n */\n readonly navLinks: readonly NavLink[];\n /**\n * Page URL to HTTP method, written to `<contentDir>/api-methods.json` for the\n * sidebar method badges (`apiMethodBadges` in `@blaaiz/docs-core/ui`).\n */\n readonly methodsByUrl: Readonly<Record<string, string>>;\n /** The merged OpenAPI document (reusable for the runtime page renderer). */\n readonly document: OpenApiDocument;\n}\n\ninterface GeneratedFile {\n path: string;\n content: string;\n}\n\nfunction slugify(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\n/** The `METHOD path` key of the first operation a generated page renders. */\nfunction operationKeyOf(content: string): string | null {\n const match = /operations=\\{(\\[[\\s\\S]*?\\])\\}/.exec(content);\n if (!match?.[1]) {\n return null;\n }\n try {\n const ops = JSON.parse(match[1]) as { path: string; method: string }[];\n const op = ops[0];\n return op ? `${op.method.toUpperCase()} ${op.path}` : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The `info` a docs-only run reports on its (empty) merged document. Nothing\n * renders it: with no API pages there is no reference view to title.\n */\nconst PROSE_ONLY_INFO: OpenApiInfo = { title: 'No API reference', version: '0.0.0' };\n\n/**\n * The fumadocs functions, checked. Called only when the navigation publishes\n * API pages, so the message can name the exact reason they are needed.\n */\nfunction requireFumadocs(value: unknown, apiPageCount: number): FumadocsOpenApi {\n const because = `docs.json publishes ${String(apiPageCount)} API page(s)`;\n if (typeof value !== 'object' || value === null) {\n throw new Error(\n `[docs-core] generateDocsTree: the 'fumadocs' option is required because ${because}. ` +\n \"Pass { createOpenAPI, generateFiles } from the site's fumadocs-openapi install.\",\n );\n }\n const candidate = value as Record<string, unknown>;\n for (const name of ['createOpenAPI', 'generateFiles'] as const) {\n if (typeof candidate[name] !== 'function') {\n throw new Error(\n `[docs-core] generateDocsTree: fumadocs.${name} must be a function because ${because}. ` +\n `Import ${name} from ${name === 'createOpenAPI' ? \"'fumadocs-openapi/server'\" : \"'fumadocs-openapi'\"}.`,\n );\n }\n }\n return value as FumadocsOpenApi;\n}\n\n/**\n * Build the docs content tree from `docs.json`.\n *\n * Reads the navigation, merges every referenced spec, generates one page per\n * declared endpoint into its configured group folder, and writes the\n * `meta.json` files that give the sidebar the navigation's exact structure and\n * order. Operations present in a spec but not declared in `docs.json` get no\n * page — the navigation decides what is published, as on Mintlify.\n *\n * A site of prose alone needs neither option: with no API pages in `docs.json`\n * the run writes the `meta.json` files, `nav.json`, and an empty\n * `api-methods.json`, and never loads `fumadocs-openapi`.\n *\n * @example\n * ```ts\n * // A site of prose alone.\n * await generateDocsTree({ contentDir: 'content' });\n * ```\n *\n * @param options - directories, document info, and the injected fumadocs functions\n * @returns counts and the merged document\n * @throws when `docs.json` publishes API pages and `fumadocs` or `info` is missing\n * @public\n */\nexport async function generateDocsTree(\n options: GenerateDocsTreeOptions,\n): Promise<GenerateDocsTreeResult> {\n const docsJson = options.docsJson ?? path.join(options.contentDir, 'docs.json');\n const outDir = options.outDir ?? path.join(options.contentDir, 'docs');\n const apiDir = options.apiDir ?? 'api';\n\n const navigation = parseNavigation(JSON.parse(await readFile(docsJson, 'utf8')));\n const plan = planDocsTree(navigation, { apiDir });\n\n // Where each planned API page lands. Both stay empty on a prose-only site.\n const placements = new Map<string, { dir: string; order: number; slug: string }>();\n const groupPages = new Map<string, { order: number; slug: string }[]>();\n let pages = 0;\n let document: OpenApiDocument;\n\n if (plan.apiPages.length === 0) {\n // A site of prose alone. Nothing to merge, nothing to generate, and — most\n // importantly — nothing removed: a hand-written folder named `api` is the\n // site's own content, not our output.\n document = mergeOpenApiDocuments([], { info: options.info ?? PROSE_ONLY_INFO });\n } else {\n const fumadocs = requireFumadocs(options.fumadocs, plan.apiPages.length);\n if (options.info === undefined) {\n throw new Error(\n `[docs-core] generateDocsTree: the 'info' option is required because docs.json ` +\n `publishes ${String(plan.apiPages.length)} API page(s). ` +\n 'Pass { title, version } for the merged OpenAPI document.',\n );\n }\n\n // Merge exactly the specs the navigation references.\n const docs = await Promise.all(\n plan.specs.map(\n async (file) =>\n JSON.parse(await readFile(path.join(options.contentDir, file), 'utf8')) as unknown,\n ),\n );\n document = mergeOpenApiDocuments(docs, {\n info: options.info,\n ...(options.servers ? { servers: options.servers } : {}),\n });\n\n // Resolve each planned page's slug from the merged document.\n for (const page of plan.apiPages) {\n const operation = document.paths[page.apiPath]?.[page.method.toLowerCase()] as\n { operationId?: string } | undefined;\n if (!operation) {\n throw new Error(\n `[docs-core] docs.json references ${page.method} ${page.apiPath}, ` +\n `but ${page.file} defines no such operation.`,\n );\n }\n const slug = operation.operationId ?? slugify(`${page.method} ${page.apiPath}`);\n placements.set(`${page.method.toUpperCase()} ${page.apiPath}`, { ...page, slug });\n const group = groupPages.get(page.dir) ?? [];\n group.push({ order: page.order, slug });\n groupPages.set(page.dir, group);\n }\n\n // Fresh API tree, then generate pages and route each into its planned folder.\n for (const group of plan.apiGroups) {\n if (group.root) {\n await rm(path.join(outDir, group.dir), { recursive: true, force: true });\n }\n }\n const createOpenAPI = fumadocs.createOpenAPI as (input: unknown) => unknown;\n const generateFiles = fumadocs.generateFiles as (input: unknown) => Promise<void>;\n await generateFiles({\n input: createOpenAPI({\n input: { blaaiz: document },\n ...(options.proxyUrl ? { proxyUrl: options.proxyUrl } : {}),\n }),\n output: outDir,\n per: 'operation',\n groupBy: 'none',\n beforeWrite(files: GeneratedFile[]) {\n for (let i = files.length - 1; i >= 0; i -= 1) {\n const file = files[i];\n if (!file) {\n continue;\n }\n const key = operationKeyOf(file.content);\n const placement = key ? placements.get(key) : undefined;\n if (!placement) {\n files.splice(i, 1); // not declared in docs.json -> not published\n continue;\n }\n file.path = path.join(placement.dir, `${placement.slug}.mdx`);\n pages += 1;\n }\n },\n });\n }\n\n // Meta files: the plan's static ones, plus each API group's entries — its\n // resolved page slugs and child folders, merged back into declared order.\n const metas = [\n ...plan.metas,\n ...plan.apiGroups.map((group) => ({\n path: `${group.dir}/meta.json`,\n content: {\n title: group.title,\n ...(group.root ? { root: true } : {}),\n ...(group.icon ? { icon: group.icon } : {}),\n pages: [\n ...(groupPages.get(group.dir) ?? []).map((page) => ({\n order: page.order,\n name: page.slug,\n })),\n ...group.children.map((child) => ({ order: child.order, name: child.folder })),\n ]\n .sort((a, b) => a.order - b.order)\n .map((entry) => entry.name),\n },\n })),\n ];\n for (const meta of metas) {\n const target = path.join(outDir, meta.path);\n await mkdir(path.dirname(target), { recursive: true });\n await writeFile(target, `${JSON.stringify(meta.content, null, 2)}\\n`);\n }\n\n // Top-nav links: each tab points at its first page. The URL of an API page\n // needs its resolved slug, which is why this runs here, not in the planner.\n const baseUrl = (options.baseUrl ?? '/docs').replace(/\\/$/, '');\n const docUrl = (file: string): string => {\n const clean = file.replace(/\\/?index$/, '');\n return clean ? `${baseUrl}/${clean}` : baseUrl;\n };\n const firstUrl = (items: readonly NavItem[]): string | undefined => {\n for (const item of items) {\n if (item.kind === 'group') {\n const url = firstUrl(item.items);\n if (url) {\n return url;\n }\n } else if (item.kind === 'doc') {\n return docUrl(item.file);\n } else {\n const placement = placements.get(`${item.method.toUpperCase()} ${item.apiPath}`);\n if (placement) {\n return `${baseUrl}/${placement.dir}/${placement.slug}`;\n }\n }\n }\n return undefined;\n };\n const navLinks: NavLink[] = [];\n for (const tab of navigation) {\n const url = firstUrl(tab.items);\n if (url) {\n navLinks.push({ text: tab.title, url, ...(tab.icon ? { icon: tab.icon } : {}) });\n }\n }\n await writeFile(\n path.join(options.contentDir, 'nav.json'),\n `${JSON.stringify(navLinks, null, 2)}\\n`,\n );\n\n // Page URL -> HTTP method, for the sidebar method badges.\n const methodsByUrl: Record<string, string> = {};\n for (const page of plan.apiPages) {\n const placement = placements.get(`${page.method.toUpperCase()} ${page.apiPath}`);\n if (placement) {\n methodsByUrl[`${baseUrl}/${placement.dir}/${placement.slug}`] = page.method.toUpperCase();\n }\n }\n await writeFile(\n path.join(options.contentDir, 'api-methods.json'),\n `${JSON.stringify(methodsByUrl, null, 2)}\\n`,\n );\n\n if (plan.apiTabDocPages.length > 0) {\n console.warn(\n `[docs-core] ${String(plan.apiTabDocPages.length)} doc page(s) are declared inside API ` +\n `tabs and cannot be placed in the generated sidebar folders: ` +\n `${plan.apiTabDocPages.join(', ')}. Each page stays reachable at its own URL; ` +\n 'to list one in the sidebar, move it into a doc tab.',\n );\n }\n\n return { pages, metas: metas.length, navLinks, methodsByUrl, document };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/docs-tree.ts","../src/adapters/docs-json/parse-navigation.ts","../src/adapters/openapi/merge-documents.ts","../src/generator/index.ts"],"names":["path","isRecord","HTTP_METHODS","slugify","readFile","rm","mkdir","writeFile"],"mappings":"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","file":"generator.cjs","sourcesContent":["/**\n * Turns the normalized navigation into a content-tree plan: which folders and\n * `meta.json` files encode the navigation, and where each API page belongs.\n * The site's `docs.json` stays the single source of truth — authors reorganize\n * routes and groups by editing one file, never by moving generated files.\n *\n * The planner is pure; `@blaaiz/docs-core/generator` executes the plan.\n *\n * @packageDocumentation\n */\n\nimport type { NavGroup, NavItem, NavTab, Navigation } from './navigation.js';\n\n/**\n * A planned API reference page: one operation and the folder it belongs to.\n *\n * @public\n */\nexport interface PlannedApiPage {\n /** Spec file reference from `docs.json`, relative to the content directory. */\n readonly file: string;\n /** The operation's HTTP method. */\n readonly method: string;\n /** The operation's API route. */\n readonly apiPath: string;\n /** Output folder, relative to the docs output directory (e.g. `api/authentication`). */\n readonly dir: string;\n /** Position within the group. */\n readonly order: number;\n}\n\n/**\n * A child folder of a planned API group, at its position among the group's\n * pages. The generator merges these with the resolved page slugs to write the\n * group's `meta.json` in declared order.\n *\n * @public\n */\nexport interface PlannedGroupChild {\n readonly order: number;\n /** The child folder's name within the group directory. */\n readonly folder: string;\n}\n\n/**\n * A planned API group folder. The generator fills its page order once the\n * operation slugs are known, interleaving {@link PlannedGroupChild} folders at\n * their declared positions. A group with `root: true` is an API tab's folder.\n *\n * @public\n */\nexport interface PlannedApiGroup {\n readonly dir: string;\n readonly title: string;\n readonly children: readonly PlannedGroupChild[];\n /** Set on an API tab's own folder, so the sidebar scopes to the tab. */\n readonly root?: boolean;\n /** The tab's icon, on a `root` group. */\n readonly icon?: string;\n}\n\n/**\n * A doc page declared inside an API tab, at its position among the group's\n * operations. The generator links it into the group's `meta.json`: the page\n * keeps living in its own folder, outside the generated tree.\n *\n * @public\n */\nexport interface PlannedApiDocLink {\n /** Page path relative to the docs output directory, without extension. */\n readonly file: string;\n /** The generated group folder whose meta lists the link. */\n readonly dir: string;\n /** Position within the group. */\n readonly order: number;\n}\n\n/**\n * A `meta.json` file the plan wants written.\n *\n * @public\n */\nexport interface PlannedMeta {\n /** Path relative to the docs output directory, e.g. `guides/meta.json`. */\n readonly path: string;\n readonly content: Record<string, unknown>;\n}\n\n/**\n * The complete content-tree plan for one navigation.\n *\n * @public\n */\nexport interface DocsTreePlan {\n /** Unique spec files referenced by the navigation, in order of appearance. */\n readonly specs: readonly string[];\n readonly apiPages: readonly PlannedApiPage[];\n readonly apiGroups: readonly PlannedApiGroup[];\n /** Fully-known meta files: the root, doc tab folders, and doc groups. */\n readonly metas: readonly PlannedMeta[];\n /**\n * Doc pages declared inside API tabs, each linked into its group's meta at\n * its declared position.\n */\n readonly apiDocLinks: readonly PlannedApiDocLink[];\n}\n\n/**\n * Options for {@link planDocsTree}.\n *\n * @public\n */\nexport interface PlanDocsTreeOptions {\n /** Folder for generated API pages when one tab publishes them. Default `api`. */\n readonly apiDir?: string;\n}\n\n/** Kebab-case a title into a folder name. */\nfunction slugify(title: string): string {\n return title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\nfunction isOpenApi(item: NavItem): boolean {\n return item.kind === 'openapi';\n}\n\nfunction hasOpenApi(items: readonly NavItem[]): boolean {\n return items.some((item) => (item.kind === 'group' ? hasOpenApi(item.items) : isOpenApi(item)));\n}\n\n/** The basename of a doc page path (its file name in the folder). */\nfunction basename(file: string): string {\n const i = file.lastIndexOf('/');\n return i === -1 ? file : file.slice(i + 1);\n}\n\n/** The directory of a doc page path, or '' for a root page. */\nfunction dirname(file: string): string {\n const i = file.lastIndexOf('/');\n return i === -1 ? '' : file.slice(0, i);\n}\n\n/** The first doc page in a group, descending nested groups in order. */\nfunction firstDocFile(items: readonly NavItem[]): string | undefined {\n for (const item of items) {\n if (item.kind === 'doc') {\n return item.file;\n }\n if (item.kind === 'group') {\n const found = firstDocFile(item.items);\n if (found !== undefined) {\n return found;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Plan the content tree for a navigation.\n *\n * Conventions (Mintlify-shaped):\n * - A tab whose items reference OpenAPI files becomes an API tab. With one API\n * tab its folder is `apiDir`; with several, each tab's folder is its\n * slugified title, so tabs never collide. Groups become nested folders named\n * by slugified group title, to any depth.\n * - A tab of doc pages becomes the folder its pages share (their first path\n * segment); a doc group's pages must share one directory. Doc groups nest.\n * - Root-level doc pages (no `/` in the path) are listed in the root meta.\n * - Tab folders get `root: true`, so the sidebar scopes to the active tab.\n * - Sidebar order is declaration order, pages and groups interleaved.\n *\n * @param navigation - the parsed navigation\n * @param options - the API folder name\n * @returns the plan the generator executes\n * @throws when two sibling groups slugify to the same folder\n * @public\n */\nexport function planDocsTree(\n navigation: Navigation,\n options: PlanDocsTreeOptions = {},\n): DocsTreePlan {\n const apiDir = options.apiDir ?? 'api';\n\n const specs: string[] = [];\n const apiPages: PlannedApiPage[] = [];\n const apiGroups: PlannedApiGroup[] = [];\n const metas: PlannedMeta[] = [];\n const rootPages: string[] = [];\n const apiDocLinks: PlannedApiDocLink[] = [];\n\n const apiTabs = navigation.filter((tab) => hasOpenApi(tab.items));\n const soleApiTab = apiTabs.length === 1;\n\n for (const tab of navigation) {\n if (hasOpenApi(tab.items)) {\n planApiTab(tab);\n continue;\n }\n planDocTab(tab);\n }\n\n metas.unshift({ path: 'meta.json', content: { pages: rootPages } });\n\n return { specs, apiPages, apiGroups, metas, apiDocLinks };\n\n function planApiTab(tab: NavTab): void {\n const tabDir = soleApiTab ? apiDir : slugify(tab.title);\n rootPages.push(tabDir);\n const at = apiGroups.length;\n const children = planApiItems(tab.items, tabDir);\n apiGroups.splice(at, 0, {\n dir: tabDir,\n title: tab.title,\n root: true,\n ...(tab.icon ? { icon: tab.icon } : {}),\n children,\n });\n }\n\n /**\n * Plan a run of API-tab items into `dir`: operations become planned pages,\n * nested groups become child folders, and doc pages become links into the\n * group's meta. Returns the child folders at their declared positions.\n */\n function planApiItems(items: readonly NavItem[], dir: string): PlannedGroupChild[] {\n const children: PlannedGroupChild[] = [];\n const takenFolders = new Set<string>();\n let order = 0;\n for (const item of items) {\n if (item.kind === 'group') {\n const folder = slugify(item.title);\n if (takenFolders.has(folder)) {\n throw new Error(\n `[docs-core] Two sibling groups under '${dir}' share the folder name '${folder}'. ` +\n 'Rename one of the groups in docs.json.',\n );\n }\n takenFolders.add(folder);\n const childDir = `${dir}/${folder}`;\n const at = apiGroups.length;\n const sub = planApiItems(item.items, childDir);\n apiGroups.splice(at, 0, { dir: childDir, title: item.title, children: sub });\n children.push({ order, folder });\n order += 1;\n continue;\n }\n if (item.kind === 'openapi') {\n if (!specs.includes(item.file)) {\n specs.push(item.file);\n }\n apiPages.push({ file: item.file, method: item.method, apiPath: item.apiPath, dir, order });\n order += 1;\n continue;\n }\n apiDocLinks.push({ file: item.file, dir, order });\n order += 1;\n }\n return children;\n }\n\n function planDocTab(tab: NavTab): void {\n const directPages = tab.items.filter((item) => item.kind === 'doc');\n const groups = tab.items.filter((item) => item.kind === 'group');\n\n // A tab of root-level pages (e.g. Home -> index) lists them at the root.\n if (groups.length === 0 && directPages.every((page) => !page.file.includes('/'))) {\n for (const page of directPages) {\n rootPages.push(page.file);\n }\n return;\n }\n\n // The tab folder is the first path segment its pages share.\n const firstFile = firstDocFile(tab.items);\n if (firstFile === undefined) {\n return;\n }\n const tabDir = firstFile.split('/')[0] ?? '';\n rootPages.push(tabDir);\n\n metas.push({\n path: `${tabDir}/meta.json`,\n content: {\n title: tab.title,\n root: true,\n ...(tab.icon ? { icon: tab.icon } : {}),\n pages: planDocItems(tab.items, tabDir),\n },\n });\n }\n\n /**\n * Plan a run of doc-tab items belonging to the folder `dir`: pages are\n * listed by basename, nested groups get their own `meta.json` and are\n * listed by their folder path relative to `dir`. A nested group whose pages\n * live directly in `dir` is flattened into it. Returns the meta entries in\n * declared order.\n */\n function planDocItems(items: readonly NavItem[], dir: string): string[] {\n const entries: string[] = [];\n for (const item of items) {\n if (item.kind === 'doc') {\n entries.push(basename(item.file));\n continue;\n }\n if (item.kind !== 'group') {\n continue;\n }\n const groupDir = planDocGroup(item, dir);\n if (groupDir === null) {\n continue;\n }\n if (groupDir === dir) {\n // The group's pages live directly in this folder.\n entries.push(...planDocItems(item.items, dir));\n continue;\n }\n entries.push(groupDir.slice(dir.length + 1));\n }\n return entries;\n }\n\n /**\n * Plan one doc group. Returns the group's directory, `dir` itself when the\n * group flattens into its parent folder, or `null` for an empty group.\n */\n function planDocGroup(group: NavGroup, dir: string): string | null {\n const firstFile = firstDocFile(group.items);\n if (firstFile === undefined) {\n return null;\n }\n const groupDir = dirname(firstFile);\n if (groupDir === dir || groupDir === '') {\n return dir;\n }\n metas.push({\n path: `${groupDir}/meta.json`,\n content: { title: group.title, pages: planDocItems(group.items, groupDir) },\n });\n return groupDir;\n }\n}\n","/**\n * `docs.json` navigation adapter.\n *\n * Translates a Mintlify `docs.json` navigation block into the framework's\n * normalized {@link Navigation} tree. It understands the two page-string forms\n * Mintlify uses — a plain content path, and an OpenAPI entry of the form\n * `\"<file> <METHOD> <route>\"` — and it recurses through arbitrarily nested\n * groups.\n *\n * @packageDocumentation\n */\n\nimport {\n type NavGroup,\n type NavItem,\n type NavMethod,\n type NavPage,\n type NavTab,\n type Navigation,\n} from '../../core/index.js';\n\nconst HTTP_METHODS: ReadonlySet<string> = new Set([\n 'GET',\n 'POST',\n 'PUT',\n 'PATCH',\n 'DELETE',\n 'HEAD',\n 'OPTIONS',\n 'TRACE',\n]);\n\n/**\n * Thrown when a `docs.json` navigation block does not match the expected shape.\n *\n * @public\n */\nexport class InvalidNavigationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'InvalidNavigationError';\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Parse a single page string into a normalized page node.\n *\n * A string shaped `\"<file> <METHOD> <route>\"` (where `<METHOD>` is a known HTTP\n * method) becomes an {@link OpenApiPage}; anything else becomes a {@link DocPage}.\n */\nfunction parsePageString(entry: string): NavPage {\n const parts = entry.trim().split(/\\s+/);\n const [file, maybeMethod, ...rest] = parts;\n\n if (file === undefined || file === '') {\n throw new InvalidNavigationError('Encountered an empty navigation page entry.');\n }\n\n if (maybeMethod !== undefined && rest.length > 0 && HTTP_METHODS.has(maybeMethod.toUpperCase())) {\n return {\n kind: 'openapi',\n file,\n method: maybeMethod.toUpperCase() as NavMethod,\n apiPath: rest.join(' '),\n };\n }\n\n return { kind: 'doc', file: entry.trim() };\n}\n\nfunction parseItem(entry: unknown, path: string): NavItem {\n if (typeof entry === 'string') {\n return parsePageString(entry);\n }\n\n if (isRecord(entry) && typeof entry['group'] === 'string') {\n return parseGroup(entry, path);\n }\n\n throw new InvalidNavigationError(\n `Navigation entry at ${path} must be a page string or a group object.`,\n );\n}\n\nfunction parseItems(pages: unknown, path: string): NavItem[] {\n if (!Array.isArray(pages)) {\n throw new InvalidNavigationError(`Expected an array of pages at ${path}.`);\n }\n return pages.map((entry, index) => parseItem(entry, `${path}[${String(index)}]`));\n}\n\nfunction parseGroup(group: Record<string, unknown>, path: string): NavGroup {\n const title = group['group'];\n if (typeof title !== 'string') {\n throw new InvalidNavigationError(`Group at ${path} is missing a string \"group\" title.`);\n }\n return {\n kind: 'group',\n title,\n items: parseItems(group['pages'], `${path}.pages`),\n };\n}\n\nfunction parseTab(tab: unknown, path: string): NavTab {\n if (!isRecord(tab) || typeof tab['tab'] !== 'string') {\n throw new InvalidNavigationError(`Tab at ${path} is missing a string \"tab\" title.`);\n }\n\n // A tab holds either `groups` or `pages`; normalize both into `items`.\n const items: NavItem[] =\n tab['groups'] !== undefined\n ? parseGroups(tab['groups'], `${path}.groups`)\n : parseItems(tab['pages'], `${path}.pages`);\n\n const icon = tab['icon'];\n return {\n kind: 'tab',\n title: tab['tab'],\n items,\n ...(typeof icon === 'string' ? { icon } : {}),\n };\n}\n\nfunction parseGroups(groups: unknown, path: string): NavGroup[] {\n if (!Array.isArray(groups)) {\n throw new InvalidNavigationError(`Expected an array of groups at ${path}.`);\n }\n return groups.map((group, index) => {\n const groupPath = `${path}[${String(index)}]`;\n if (!isRecord(group)) {\n throw new InvalidNavigationError(`Group at ${groupPath} must be an object.`);\n }\n return parseGroup(group, groupPath);\n });\n}\n\n/**\n * Parse a Mintlify `docs.json` object into the normalized {@link Navigation} tree.\n *\n * @param docsJson - the parsed contents of a `docs.json` file\n * @returns the normalized, ordered list of tabs\n * @throws {@link InvalidNavigationError} when the navigation shape is invalid\n * @public\n */\nexport function parseNavigation(docsJson: unknown): Navigation {\n if (!isRecord(docsJson)) {\n throw new InvalidNavigationError('docs.json must be an object.');\n }\n\n const navigation = docsJson['navigation'];\n if (!isRecord(navigation)) {\n throw new InvalidNavigationError('docs.json must have a \"navigation\" object.');\n }\n\n const tabs = navigation['tabs'];\n if (!Array.isArray(tabs)) {\n throw new InvalidNavigationError('docs.json navigation must have a \"tabs\" array.');\n }\n\n return tabs.map((tab, index) => parseTab(tab, `navigation.tabs[${String(index)}]`));\n}\n","/**\n * OpenAPI merge adapter.\n *\n * A Mintlify-style API reference stores one OpenAPI file per endpoint. Renderers\n * (Scalar, fumadocs-openapi) want a single document. `mergeOpenApiDocuments`\n * combines many single-operation OpenAPI files into one document: it unions\n * servers, tags, and components, and assembles the paths. A genuine conflict —\n * the same method on the same route defined twice — is an error, not a\n * silent overwrite.\n *\n * @packageDocumentation\n */\n\nimport {\n type OpenApiComponents,\n type OpenApiDocument,\n type OpenApiInfo,\n type OpenApiPathItem,\n type OpenApiServer,\n type OpenApiTag,\n} from '../../core/index.js';\n\n/**\n * Options controlling the merge.\n *\n * @public\n */\nexport interface MergeOpenApiOptions {\n /** The `info` block of the merged document. */\n readonly info: OpenApiInfo;\n /**\n * Servers for the merged document. When omitted, servers are unioned from\n * the input documents (deduplicated by `url`).\n */\n readonly servers?: readonly OpenApiServer[];\n /** The OpenAPI version string of the merged document. Defaults to `3.1.0`. */\n readonly openapi?: string;\n}\n\n/**\n * Thrown when an input is not a usable OpenAPI document, or when two inputs\n * define the same operation (method + route).\n *\n * @public\n */\nexport class OpenApiMergeError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'OpenApiMergeError';\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction collectServers(\n inputs: readonly Record<string, unknown>[],\n override: readonly OpenApiServer[] | undefined,\n): OpenApiServer[] {\n if (override !== undefined) {\n return [...override];\n }\n const byUrl = new Map<string, OpenApiServer>();\n for (const doc of inputs) {\n const servers = doc['servers'];\n if (!Array.isArray(servers)) {\n continue;\n }\n for (const server of servers) {\n if (isRecord(server) && typeof server['url'] === 'string' && !byUrl.has(server['url'])) {\n byUrl.set(server['url'], server as unknown as OpenApiServer);\n }\n }\n }\n return [...byUrl.values()];\n}\n\nfunction collectTags(inputs: readonly Record<string, unknown>[]): OpenApiTag[] {\n const byName = new Map<string, OpenApiTag>();\n for (const doc of inputs) {\n const tags = doc['tags'];\n if (!Array.isArray(tags)) {\n continue;\n }\n for (const tag of tags) {\n if (isRecord(tag) && typeof tag['name'] === 'string' && !byName.has(tag['name'])) {\n byName.set(tag['name'], tag as unknown as OpenApiTag);\n }\n }\n }\n return [...byName.values()];\n}\n\nfunction mergeComponents(inputs: readonly Record<string, unknown>[]): OpenApiComponents {\n const merged: Record<string, Record<string, unknown>> = {};\n for (const doc of inputs) {\n const components = doc['components'];\n if (!isRecord(components)) {\n continue;\n }\n for (const [section, value] of Object.entries(components)) {\n if (!isRecord(value)) {\n continue;\n }\n const target = merged[section] ?? (merged[section] = {});\n for (const [key, definition] of Object.entries(value)) {\n // First definition wins; later identical re-declarations are ignored.\n if (!(key in target)) {\n target[key] = definition;\n }\n }\n }\n }\n return merged;\n}\n\nconst HTTP_METHODS = new Set(['get', 'put', 'post', 'delete', 'patch', 'options', 'head', 'trace']);\n\nfunction assemblePaths(\n inputs: readonly Record<string, unknown>[],\n): Record<string, OpenApiPathItem> {\n const paths: Record<string, OpenApiPathItem> = {};\n for (const doc of inputs) {\n const docPaths = doc['paths'];\n if (!isRecord(docPaths)) {\n throw new OpenApiMergeError('Each OpenAPI input must have a \"paths\" object.');\n }\n // Each file's top-level security applies to all its operations. The merged\n // document can't carry one shared security, so push it onto the operations.\n const docSecurity = doc['security'];\n for (const [route, pathItem] of Object.entries(docPaths)) {\n if (!isRecord(pathItem)) {\n throw new OpenApiMergeError(`Path item for \"${route}\" must be an object.`);\n }\n const target = paths[route] ?? (paths[route] = {});\n for (const [method, operation] of Object.entries(pathItem)) {\n if (method in target) {\n throw new OpenApiMergeError(\n `Duplicate operation ${method.toUpperCase()} ${route} across inputs.`,\n );\n }\n let resolved = operation;\n if (\n HTTP_METHODS.has(method.toLowerCase()) &&\n isRecord(operation) &&\n !('security' in operation) &&\n Array.isArray(docSecurity)\n ) {\n resolved = { ...operation, security: docSecurity };\n }\n target[method] = resolved as OpenApiPathItem[string];\n }\n }\n }\n return paths;\n}\n\n/**\n * Merge many single-operation OpenAPI documents into one document.\n *\n * @param inputs - parsed OpenAPI documents, each typically holding one operation\n * @param options - the merged document's `info`, and optional server/version overrides\n * @returns the merged {@link OpenApiDocument}\n * @throws {@link OpenApiMergeError} on an invalid input or a duplicate operation\n * @public\n */\nexport function mergeOpenApiDocuments(\n inputs: readonly unknown[],\n options: MergeOpenApiOptions,\n): OpenApiDocument {\n const records: Record<string, unknown>[] = inputs.map((input, index) => {\n if (!isRecord(input)) {\n throw new OpenApiMergeError(`OpenAPI input at index ${String(index)} must be an object.`);\n }\n return input;\n });\n\n return {\n openapi: options.openapi ?? '3.1.0',\n info: options.info,\n servers: collectServers(records, options.servers),\n tags: collectTags(records),\n paths: assemblePaths(records),\n components: mergeComponents(records),\n };\n}\n","/**\n * `@blaaiz/docs-core/generator` — builds the docs content tree from `docs.json`.\n *\n * One call turns the site's navigation file into everything fumadocs needs:\n * generated API pages placed in their configured groups, and `meta.json` files\n * encoding tab and group order. Authors reorganize the docs by editing\n * `docs.json` — never by moving files.\n *\n * Node-only. A site that publishes an API reference injects `createOpenAPI` and\n * `generateFiles` from its own `fumadocs-openapi` install, so this package takes\n * no dependency on it. A site of prose alone injects nothing: with no API pages\n * in `docs.json` the generator writes the `meta.json` files and `nav.json` and\n * never touches `fumadocs-openapi`.\n *\n * @packageDocumentation\n */\n\nimport { mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { mergeOpenApiDocuments } from '../adapters/index.js';\nimport { parseNavigation } from '../adapters/index.js';\nimport type { NavItem, OpenApiDocument, OpenApiInfo, OpenApiServer } from '../core/index.js';\nimport { planDocsTree } from '../core/index.js';\n\n/**\n * The two fumadocs-openapi functions the generator needs, injected by the site\n * (`createOpenAPI` from `fumadocs-openapi/server`, `generateFiles` from\n * `fumadocs-openapi`). Typed loosely so this package needs no fumadocs types.\n *\n * @public\n */\nexport interface FumadocsOpenApi {\n readonly createOpenAPI: (...args: never[]) => unknown;\n readonly generateFiles: (...args: never[]) => unknown;\n}\n\n/**\n * Options for {@link generateDocsTree}.\n *\n * @public\n */\nexport interface GenerateDocsTreeOptions {\n /** The site's content directory (holds `docs.json`, spec files, and `docs/`). */\n readonly contentDir: string;\n /** Path to the navigation file. Default `<contentDir>/docs.json`. */\n readonly docsJson?: string;\n /** Output directory for the page tree. Default `<contentDir>/docs`. */\n readonly outDir?: string;\n /** Folder for generated API pages. Default `api`. */\n readonly apiDir?: string;\n /**\n * `info` block of the merged OpenAPI document. Required when `docs.json`\n * publishes API pages; ignored on a site of prose alone, which merges no\n * documents.\n */\n readonly info?: OpenApiInfo;\n /** Servers of the merged document. */\n readonly servers?: readonly OpenApiServer[];\n /** Same-origin proxy route for the try-it playground. */\n readonly proxyUrl?: string;\n /** URL base the fumadocs loader serves pages under. Default `/docs`. */\n readonly baseUrl?: string;\n /**\n * The site's fumadocs-openapi functions. Required when `docs.json` publishes\n * API pages; omit it on a site of prose alone.\n */\n readonly fumadocs?: FumadocsOpenApi;\n}\n\n/**\n * A top-navigation link: one tab, pointing at its first page.\n *\n * @public\n */\nexport interface NavLink {\n readonly text: string;\n readonly url: string;\n /** The tab's icon name from `docs.json`, for the site to resolve. */\n readonly icon?: string;\n}\n\n/**\n * What the generator produced.\n *\n * @public\n */\nexport interface GenerateDocsTreeResult {\n readonly pages: number;\n readonly metas: number;\n /**\n * Top-nav links, one per tab, each pointing at the tab's first page — written\n * to `<contentDir>/nav.json` for the site's layout. Keeps the top nav in sync\n * with `docs.json` instead of a hand-maintained list.\n */\n readonly navLinks: readonly NavLink[];\n /**\n * Page URL to HTTP method, written to `<contentDir>/api-methods.json` for the\n * sidebar method badges (`apiMethodBadges` in `@blaaiz/docs-core/ui`).\n */\n readonly methodsByUrl: Readonly<Record<string, string>>;\n /** The merged OpenAPI document (reusable for the runtime page renderer). */\n readonly document: OpenApiDocument;\n}\n\ninterface GeneratedFile {\n path: string;\n content: string;\n}\n\nfunction slugify(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\n/**\n * The frontmatter `title` of an MDX page, or null when the file has none.\n * Reads only the leading frontmatter block; quotes around the value are\n * stripped.\n */\nfunction frontmatterTitle(source: string): string | null {\n const block = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/.exec(source);\n if (!block?.[1]) {\n return null;\n }\n const line = /^title\\s*:\\s*(.+)\\s*$/m.exec(block[1]);\n if (!line?.[1]) {\n return null;\n }\n return line[1].replace(/^(['\"])(.*)\\1$/, '$2');\n}\n\n/** The `METHOD path` key of the first operation a generated page renders. */\nfunction operationKeyOf(content: string): string | null {\n const match = /operations=\\{(\\[[\\s\\S]*?\\])\\}/.exec(content);\n if (!match?.[1]) {\n return null;\n }\n try {\n const ops = JSON.parse(match[1]) as { path: string; method: string }[];\n const op = ops[0];\n return op ? `${op.method.toUpperCase()} ${op.path}` : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The `info` a docs-only run reports on its (empty) merged document. Nothing\n * renders it: with no API pages there is no reference view to title.\n */\nconst PROSE_ONLY_INFO: OpenApiInfo = { title: 'No API reference', version: '0.0.0' };\n\n/**\n * The fumadocs functions, checked. Called only when the navigation publishes\n * API pages, so the message can name the exact reason they are needed.\n */\nfunction requireFumadocs(value: unknown, apiPageCount: number): FumadocsOpenApi {\n const because = `docs.json publishes ${String(apiPageCount)} API page(s)`;\n if (typeof value !== 'object' || value === null) {\n throw new Error(\n `[docs-core] generateDocsTree: the 'fumadocs' option is required because ${because}. ` +\n \"Pass { createOpenAPI, generateFiles } from the site's fumadocs-openapi install.\",\n );\n }\n const candidate = value as Record<string, unknown>;\n for (const name of ['createOpenAPI', 'generateFiles'] as const) {\n if (typeof candidate[name] !== 'function') {\n throw new Error(\n `[docs-core] generateDocsTree: fumadocs.${name} must be a function because ${because}. ` +\n `Import ${name} from ${name === 'createOpenAPI' ? \"'fumadocs-openapi/server'\" : \"'fumadocs-openapi'\"}.`,\n );\n }\n }\n return value as FumadocsOpenApi;\n}\n\n/**\n * Build the docs content tree from `docs.json`.\n *\n * Reads the navigation, merges every referenced spec, generates one page per\n * declared endpoint into its configured group folder, and writes the\n * `meta.json` files that give the sidebar the navigation's exact structure and\n * order. Operations present in a spec but not declared in `docs.json` get no\n * page — the navigation decides what is published, as on Mintlify.\n *\n * A site of prose alone needs neither option: with no API pages in `docs.json`\n * the run writes the `meta.json` files, `nav.json`, and an empty\n * `api-methods.json`, and never loads `fumadocs-openapi`.\n *\n * @example\n * ```ts\n * // A site of prose alone.\n * await generateDocsTree({ contentDir: 'content' });\n * ```\n *\n * @param options - directories, document info, and the injected fumadocs functions\n * @returns counts and the merged document\n * @throws when `docs.json` publishes API pages and `fumadocs` or `info` is missing\n * @public\n */\nexport async function generateDocsTree(\n options: GenerateDocsTreeOptions,\n): Promise<GenerateDocsTreeResult> {\n const docsJson = options.docsJson ?? path.join(options.contentDir, 'docs.json');\n const outDir = options.outDir ?? path.join(options.contentDir, 'docs');\n const apiDir = options.apiDir ?? 'api';\n\n const navigation = parseNavigation(JSON.parse(await readFile(docsJson, 'utf8')));\n const plan = planDocsTree(navigation, { apiDir });\n\n // Where each planned API page lands. Both stay empty on a prose-only site.\n const placements = new Map<string, { dir: string; order: number; slug: string }>();\n const groupPages = new Map<string, { order: number; slug: string }[]>();\n let pages = 0;\n let document: OpenApiDocument;\n\n if (plan.apiPages.length === 0) {\n // A site of prose alone. Nothing to merge, nothing to generate, and — most\n // importantly — nothing removed: a hand-written folder named `api` is the\n // site's own content, not our output.\n document = mergeOpenApiDocuments([], { info: options.info ?? PROSE_ONLY_INFO });\n } else {\n const fumadocs = requireFumadocs(options.fumadocs, plan.apiPages.length);\n if (options.info === undefined) {\n throw new Error(\n `[docs-core] generateDocsTree: the 'info' option is required because docs.json ` +\n `publishes ${String(plan.apiPages.length)} API page(s). ` +\n 'Pass { title, version } for the merged OpenAPI document.',\n );\n }\n\n // Merge exactly the specs the navigation references.\n const docs = await Promise.all(\n plan.specs.map(\n async (file) =>\n JSON.parse(await readFile(path.join(options.contentDir, file), 'utf8')) as unknown,\n ),\n );\n document = mergeOpenApiDocuments(docs, {\n info: options.info,\n ...(options.servers ? { servers: options.servers } : {}),\n });\n\n // Resolve each planned page's slug from the merged document.\n for (const page of plan.apiPages) {\n const operation = document.paths[page.apiPath]?.[page.method.toLowerCase()] as\n { operationId?: string } | undefined;\n if (!operation) {\n throw new Error(\n `[docs-core] docs.json references ${page.method} ${page.apiPath}, ` +\n `but ${page.file} defines no such operation.`,\n );\n }\n const slug = operation.operationId ?? slugify(`${page.method} ${page.apiPath}`);\n placements.set(`${page.method.toUpperCase()} ${page.apiPath}`, { ...page, slug });\n const group = groupPages.get(page.dir) ?? [];\n group.push({ order: page.order, slug });\n groupPages.set(page.dir, group);\n }\n\n // Fresh API tree, then generate pages and route each into its planned folder.\n for (const group of plan.apiGroups) {\n if (group.root) {\n await rm(path.join(outDir, group.dir), { recursive: true, force: true });\n }\n }\n const createOpenAPI = fumadocs.createOpenAPI as (input: unknown) => unknown;\n const generateFiles = fumadocs.generateFiles as (input: unknown) => Promise<void>;\n await generateFiles({\n input: createOpenAPI({\n input: { blaaiz: document },\n ...(options.proxyUrl ? { proxyUrl: options.proxyUrl } : {}),\n }),\n output: outDir,\n per: 'operation',\n groupBy: 'none',\n beforeWrite(files: GeneratedFile[]) {\n for (let i = files.length - 1; i >= 0; i -= 1) {\n const file = files[i];\n if (!file) {\n continue;\n }\n const key = operationKeyOf(file.content);\n const placement = key ? placements.get(key) : undefined;\n if (!placement) {\n files.splice(i, 1); // not declared in docs.json -> not published\n continue;\n }\n file.path = path.join(placement.dir, `${placement.slug}.mdx`);\n pages += 1;\n }\n },\n });\n }\n\n // Doc pages declared inside API tabs become link entries in their group's\n // meta, at their declared position; the page keeps living in its own folder.\n const baseUrl = (options.baseUrl ?? '/docs').replace(/\\/$/, '');\n const docUrl = (file: string): string => {\n const clean = file.replace(/\\/?index$/, '');\n return clean ? `${baseUrl}/${clean}` : baseUrl;\n };\n for (const link of plan.apiDocLinks) {\n const source = path.join(outDir, `${link.file}.mdx`);\n let title: string | null = null;\n try {\n title = frontmatterTitle(await readFile(source, 'utf8'));\n } catch {\n console.warn(\n `[docs-core] docs.json lists ${link.file} inside an API tab, but ` +\n `${source} does not exist. The sidebar entry is skipped.`,\n );\n continue;\n }\n if (title === null) {\n title = link.file.split('/').pop() ?? link.file;\n console.warn(\n `[docs-core] ${link.file}.mdx has no frontmatter title; its sidebar ` +\n `entry falls back to '${title}'.`,\n );\n }\n const group = groupPages.get(link.dir) ?? [];\n // The meta link syntax cannot carry these characters in a name or URL.\n group.push({\n order: link.order,\n slug: `[${title.replace(/[[\\]()]/g, '')}](${docUrl(link.file)})`,\n });\n groupPages.set(link.dir, group);\n }\n\n // Meta files: the plan's static ones, plus each API group's entries — its\n // resolved page slugs and child folders, merged back into declared order.\n const metas = [\n ...plan.metas,\n ...plan.apiGroups.map((group) => ({\n path: `${group.dir}/meta.json`,\n content: {\n title: group.title,\n ...(group.root ? { root: true } : {}),\n ...(group.icon ? { icon: group.icon } : {}),\n pages: [\n ...(groupPages.get(group.dir) ?? []).map((page) => ({\n order: page.order,\n name: page.slug,\n })),\n ...group.children.map((child) => ({ order: child.order, name: child.folder })),\n ]\n .sort((a, b) => a.order - b.order)\n .map((entry) => entry.name),\n },\n })),\n ];\n for (const meta of metas) {\n const target = path.join(outDir, meta.path);\n await mkdir(path.dirname(target), { recursive: true });\n await writeFile(target, `${JSON.stringify(meta.content, null, 2)}\\n`);\n }\n\n // Top-nav links: each tab points at its first page. The URL of an API page\n // needs its resolved slug, which is why this runs here, not in the planner.\n const firstUrl = (items: readonly NavItem[]): string | undefined => {\n for (const item of items) {\n if (item.kind === 'group') {\n const url = firstUrl(item.items);\n if (url) {\n return url;\n }\n } else if (item.kind === 'doc') {\n return docUrl(item.file);\n } else {\n const placement = placements.get(`${item.method.toUpperCase()} ${item.apiPath}`);\n if (placement) {\n return `${baseUrl}/${placement.dir}/${placement.slug}`;\n }\n }\n }\n return undefined;\n };\n const navLinks: NavLink[] = [];\n for (const tab of navigation) {\n const url = firstUrl(tab.items);\n if (url) {\n navLinks.push({ text: tab.title, url, ...(tab.icon ? { icon: tab.icon } : {}) });\n }\n }\n await writeFile(\n path.join(options.contentDir, 'nav.json'),\n `${JSON.stringify(navLinks, null, 2)}\\n`,\n );\n\n // Page URL -> HTTP method, for the sidebar method badges.\n const methodsByUrl: Record<string, string> = {};\n for (const page of plan.apiPages) {\n const placement = placements.get(`${page.method.toUpperCase()} ${page.apiPath}`);\n if (placement) {\n methodsByUrl[`${baseUrl}/${placement.dir}/${placement.slug}`] = page.method.toUpperCase();\n }\n }\n await writeFile(\n path.join(options.contentDir, 'api-methods.json'),\n `${JSON.stringify(methodsByUrl, null, 2)}\\n`,\n );\n\n return { pages, metas: metas.length, navLinks, methodsByUrl, document };\n}\n"]}
|
package/dist/generator.js
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
|
-
import { parseNavigation, planDocsTree, mergeOpenApiDocuments } from './chunk-
|
|
1
|
+
import { parseNavigation, planDocsTree, mergeOpenApiDocuments } from './chunk-ZWDLAW5M.js';
|
|
2
2
|
import { readFile, rm, mkdir, writeFile } from 'fs/promises';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
|
|
5
5
|
function slugify(value) {
|
|
6
6
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
7
7
|
}
|
|
8
|
+
function frontmatterTitle(source) {
|
|
9
|
+
const block = /^---\r?\n([\s\S]*?)\r?\n---/.exec(source);
|
|
10
|
+
if (!block?.[1]) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
const line = /^title\s*:\s*(.+)\s*$/m.exec(block[1]);
|
|
14
|
+
if (!line?.[1]) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
return line[1].replace(/^(['"])(.*)\1$/, "$2");
|
|
18
|
+
}
|
|
8
19
|
function operationKeyOf(content) {
|
|
9
20
|
const match = /operations=\{(\[[\s\S]*?\])\}/.exec(content);
|
|
10
21
|
if (!match?.[1]) {
|
|
@@ -110,6 +121,35 @@ async function generateDocsTree(options) {
|
|
|
110
121
|
}
|
|
111
122
|
});
|
|
112
123
|
}
|
|
124
|
+
const baseUrl = (options.baseUrl ?? "/docs").replace(/\/$/, "");
|
|
125
|
+
const docUrl = (file) => {
|
|
126
|
+
const clean = file.replace(/\/?index$/, "");
|
|
127
|
+
return clean ? `${baseUrl}/${clean}` : baseUrl;
|
|
128
|
+
};
|
|
129
|
+
for (const link of plan.apiDocLinks) {
|
|
130
|
+
const source = path.join(outDir, `${link.file}.mdx`);
|
|
131
|
+
let title = null;
|
|
132
|
+
try {
|
|
133
|
+
title = frontmatterTitle(await readFile(source, "utf8"));
|
|
134
|
+
} catch {
|
|
135
|
+
console.warn(
|
|
136
|
+
`[docs-core] docs.json lists ${link.file} inside an API tab, but ${source} does not exist. The sidebar entry is skipped.`
|
|
137
|
+
);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (title === null) {
|
|
141
|
+
title = link.file.split("/").pop() ?? link.file;
|
|
142
|
+
console.warn(
|
|
143
|
+
`[docs-core] ${link.file}.mdx has no frontmatter title; its sidebar entry falls back to '${title}'.`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
const group = groupPages.get(link.dir) ?? [];
|
|
147
|
+
group.push({
|
|
148
|
+
order: link.order,
|
|
149
|
+
slug: `[${title.replace(/[[\]()]/g, "")}](${docUrl(link.file)})`
|
|
150
|
+
});
|
|
151
|
+
groupPages.set(link.dir, group);
|
|
152
|
+
}
|
|
113
153
|
const metas = [
|
|
114
154
|
...plan.metas,
|
|
115
155
|
...plan.apiGroups.map((group) => ({
|
|
@@ -134,11 +174,6 @@ async function generateDocsTree(options) {
|
|
|
134
174
|
await writeFile(target, `${JSON.stringify(meta.content, null, 2)}
|
|
135
175
|
`);
|
|
136
176
|
}
|
|
137
|
-
const baseUrl = (options.baseUrl ?? "/docs").replace(/\/$/, "");
|
|
138
|
-
const docUrl = (file) => {
|
|
139
|
-
const clean = file.replace(/\/?index$/, "");
|
|
140
|
-
return clean ? `${baseUrl}/${clean}` : baseUrl;
|
|
141
|
-
};
|
|
142
177
|
const firstUrl = (items) => {
|
|
143
178
|
for (const item of items) {
|
|
144
179
|
if (item.kind === "group") {
|
|
@@ -181,11 +216,6 @@ async function generateDocsTree(options) {
|
|
|
181
216
|
`${JSON.stringify(methodsByUrl, null, 2)}
|
|
182
217
|
`
|
|
183
218
|
);
|
|
184
|
-
if (plan.apiTabDocPages.length > 0) {
|
|
185
|
-
console.warn(
|
|
186
|
-
`[docs-core] ${String(plan.apiTabDocPages.length)} doc page(s) are declared inside API tabs and cannot be placed in the generated sidebar folders: ${plan.apiTabDocPages.join(", ")}. Each page stays reachable at its own URL; to list one in the sidebar, move it into a doc tab.`
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
219
|
return { pages, metas: metas.length, navLinks, methodsByUrl, document };
|
|
190
220
|
}
|
|
191
221
|
|
package/dist/generator.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/generator/index.ts"],"names":[],"mappings":"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","file":"generator.js","sourcesContent":["/**\n * `@blaaiz/docs-core/generator` — builds the docs content tree from `docs.json`.\n *\n * One call turns the site's navigation file into everything fumadocs needs:\n * generated API pages placed in their configured groups, and `meta.json` files\n * encoding tab and group order. Authors reorganize the docs by editing\n * `docs.json` — never by moving files.\n *\n * Node-only. A site that publishes an API reference injects `createOpenAPI` and\n * `generateFiles` from its own `fumadocs-openapi` install, so this package takes\n * no dependency on it. A site of prose alone injects nothing: with no API pages\n * in `docs.json` the generator writes the `meta.json` files and `nav.json` and\n * never touches `fumadocs-openapi`.\n *\n * @packageDocumentation\n */\n\nimport { mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { mergeOpenApiDocuments } from '../adapters/index.js';\nimport { parseNavigation } from '../adapters/index.js';\nimport type { NavItem, OpenApiDocument, OpenApiInfo, OpenApiServer } from '../core/index.js';\nimport { planDocsTree } from '../core/index.js';\n\n/**\n * The two fumadocs-openapi functions the generator needs, injected by the site\n * (`createOpenAPI` from `fumadocs-openapi/server`, `generateFiles` from\n * `fumadocs-openapi`). Typed loosely so this package needs no fumadocs types.\n *\n * @public\n */\nexport interface FumadocsOpenApi {\n readonly createOpenAPI: (...args: never[]) => unknown;\n readonly generateFiles: (...args: never[]) => unknown;\n}\n\n/**\n * Options for {@link generateDocsTree}.\n *\n * @public\n */\nexport interface GenerateDocsTreeOptions {\n /** The site's content directory (holds `docs.json`, spec files, and `docs/`). */\n readonly contentDir: string;\n /** Path to the navigation file. Default `<contentDir>/docs.json`. */\n readonly docsJson?: string;\n /** Output directory for the page tree. Default `<contentDir>/docs`. */\n readonly outDir?: string;\n /** Folder for generated API pages. Default `api`. */\n readonly apiDir?: string;\n /**\n * `info` block of the merged OpenAPI document. Required when `docs.json`\n * publishes API pages; ignored on a site of prose alone, which merges no\n * documents.\n */\n readonly info?: OpenApiInfo;\n /** Servers of the merged document. */\n readonly servers?: readonly OpenApiServer[];\n /** Same-origin proxy route for the try-it playground. */\n readonly proxyUrl?: string;\n /** URL base the fumadocs loader serves pages under. Default `/docs`. */\n readonly baseUrl?: string;\n /**\n * The site's fumadocs-openapi functions. Required when `docs.json` publishes\n * API pages; omit it on a site of prose alone.\n */\n readonly fumadocs?: FumadocsOpenApi;\n}\n\n/**\n * A top-navigation link: one tab, pointing at its first page.\n *\n * @public\n */\nexport interface NavLink {\n readonly text: string;\n readonly url: string;\n /** The tab's icon name from `docs.json`, for the site to resolve. */\n readonly icon?: string;\n}\n\n/**\n * What the generator produced.\n *\n * @public\n */\nexport interface GenerateDocsTreeResult {\n readonly pages: number;\n readonly metas: number;\n /**\n * Top-nav links, one per tab, each pointing at the tab's first page — written\n * to `<contentDir>/nav.json` for the site's layout. Keeps the top nav in sync\n * with `docs.json` instead of a hand-maintained list.\n */\n readonly navLinks: readonly NavLink[];\n /**\n * Page URL to HTTP method, written to `<contentDir>/api-methods.json` for the\n * sidebar method badges (`apiMethodBadges` in `@blaaiz/docs-core/ui`).\n */\n readonly methodsByUrl: Readonly<Record<string, string>>;\n /** The merged OpenAPI document (reusable for the runtime page renderer). */\n readonly document: OpenApiDocument;\n}\n\ninterface GeneratedFile {\n path: string;\n content: string;\n}\n\nfunction slugify(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\n/** The `METHOD path` key of the first operation a generated page renders. */\nfunction operationKeyOf(content: string): string | null {\n const match = /operations=\\{(\\[[\\s\\S]*?\\])\\}/.exec(content);\n if (!match?.[1]) {\n return null;\n }\n try {\n const ops = JSON.parse(match[1]) as { path: string; method: string }[];\n const op = ops[0];\n return op ? `${op.method.toUpperCase()} ${op.path}` : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The `info` a docs-only run reports on its (empty) merged document. Nothing\n * renders it: with no API pages there is no reference view to title.\n */\nconst PROSE_ONLY_INFO: OpenApiInfo = { title: 'No API reference', version: '0.0.0' };\n\n/**\n * The fumadocs functions, checked. Called only when the navigation publishes\n * API pages, so the message can name the exact reason they are needed.\n */\nfunction requireFumadocs(value: unknown, apiPageCount: number): FumadocsOpenApi {\n const because = `docs.json publishes ${String(apiPageCount)} API page(s)`;\n if (typeof value !== 'object' || value === null) {\n throw new Error(\n `[docs-core] generateDocsTree: the 'fumadocs' option is required because ${because}. ` +\n \"Pass { createOpenAPI, generateFiles } from the site's fumadocs-openapi install.\",\n );\n }\n const candidate = value as Record<string, unknown>;\n for (const name of ['createOpenAPI', 'generateFiles'] as const) {\n if (typeof candidate[name] !== 'function') {\n throw new Error(\n `[docs-core] generateDocsTree: fumadocs.${name} must be a function because ${because}. ` +\n `Import ${name} from ${name === 'createOpenAPI' ? \"'fumadocs-openapi/server'\" : \"'fumadocs-openapi'\"}.`,\n );\n }\n }\n return value as FumadocsOpenApi;\n}\n\n/**\n * Build the docs content tree from `docs.json`.\n *\n * Reads the navigation, merges every referenced spec, generates one page per\n * declared endpoint into its configured group folder, and writes the\n * `meta.json` files that give the sidebar the navigation's exact structure and\n * order. Operations present in a spec but not declared in `docs.json` get no\n * page — the navigation decides what is published, as on Mintlify.\n *\n * A site of prose alone needs neither option: with no API pages in `docs.json`\n * the run writes the `meta.json` files, `nav.json`, and an empty\n * `api-methods.json`, and never loads `fumadocs-openapi`.\n *\n * @example\n * ```ts\n * // A site of prose alone.\n * await generateDocsTree({ contentDir: 'content' });\n * ```\n *\n * @param options - directories, document info, and the injected fumadocs functions\n * @returns counts and the merged document\n * @throws when `docs.json` publishes API pages and `fumadocs` or `info` is missing\n * @public\n */\nexport async function generateDocsTree(\n options: GenerateDocsTreeOptions,\n): Promise<GenerateDocsTreeResult> {\n const docsJson = options.docsJson ?? path.join(options.contentDir, 'docs.json');\n const outDir = options.outDir ?? path.join(options.contentDir, 'docs');\n const apiDir = options.apiDir ?? 'api';\n\n const navigation = parseNavigation(JSON.parse(await readFile(docsJson, 'utf8')));\n const plan = planDocsTree(navigation, { apiDir });\n\n // Where each planned API page lands. Both stay empty on a prose-only site.\n const placements = new Map<string, { dir: string; order: number; slug: string }>();\n const groupPages = new Map<string, { order: number; slug: string }[]>();\n let pages = 0;\n let document: OpenApiDocument;\n\n if (plan.apiPages.length === 0) {\n // A site of prose alone. Nothing to merge, nothing to generate, and — most\n // importantly — nothing removed: a hand-written folder named `api` is the\n // site's own content, not our output.\n document = mergeOpenApiDocuments([], { info: options.info ?? PROSE_ONLY_INFO });\n } else {\n const fumadocs = requireFumadocs(options.fumadocs, plan.apiPages.length);\n if (options.info === undefined) {\n throw new Error(\n `[docs-core] generateDocsTree: the 'info' option is required because docs.json ` +\n `publishes ${String(plan.apiPages.length)} API page(s). ` +\n 'Pass { title, version } for the merged OpenAPI document.',\n );\n }\n\n // Merge exactly the specs the navigation references.\n const docs = await Promise.all(\n plan.specs.map(\n async (file) =>\n JSON.parse(await readFile(path.join(options.contentDir, file), 'utf8')) as unknown,\n ),\n );\n document = mergeOpenApiDocuments(docs, {\n info: options.info,\n ...(options.servers ? { servers: options.servers } : {}),\n });\n\n // Resolve each planned page's slug from the merged document.\n for (const page of plan.apiPages) {\n const operation = document.paths[page.apiPath]?.[page.method.toLowerCase()] as\n { operationId?: string } | undefined;\n if (!operation) {\n throw new Error(\n `[docs-core] docs.json references ${page.method} ${page.apiPath}, ` +\n `but ${page.file} defines no such operation.`,\n );\n }\n const slug = operation.operationId ?? slugify(`${page.method} ${page.apiPath}`);\n placements.set(`${page.method.toUpperCase()} ${page.apiPath}`, { ...page, slug });\n const group = groupPages.get(page.dir) ?? [];\n group.push({ order: page.order, slug });\n groupPages.set(page.dir, group);\n }\n\n // Fresh API tree, then generate pages and route each into its planned folder.\n for (const group of plan.apiGroups) {\n if (group.root) {\n await rm(path.join(outDir, group.dir), { recursive: true, force: true });\n }\n }\n const createOpenAPI = fumadocs.createOpenAPI as (input: unknown) => unknown;\n const generateFiles = fumadocs.generateFiles as (input: unknown) => Promise<void>;\n await generateFiles({\n input: createOpenAPI({\n input: { blaaiz: document },\n ...(options.proxyUrl ? { proxyUrl: options.proxyUrl } : {}),\n }),\n output: outDir,\n per: 'operation',\n groupBy: 'none',\n beforeWrite(files: GeneratedFile[]) {\n for (let i = files.length - 1; i >= 0; i -= 1) {\n const file = files[i];\n if (!file) {\n continue;\n }\n const key = operationKeyOf(file.content);\n const placement = key ? placements.get(key) : undefined;\n if (!placement) {\n files.splice(i, 1); // not declared in docs.json -> not published\n continue;\n }\n file.path = path.join(placement.dir, `${placement.slug}.mdx`);\n pages += 1;\n }\n },\n });\n }\n\n // Meta files: the plan's static ones, plus each API group's entries — its\n // resolved page slugs and child folders, merged back into declared order.\n const metas = [\n ...plan.metas,\n ...plan.apiGroups.map((group) => ({\n path: `${group.dir}/meta.json`,\n content: {\n title: group.title,\n ...(group.root ? { root: true } : {}),\n ...(group.icon ? { icon: group.icon } : {}),\n pages: [\n ...(groupPages.get(group.dir) ?? []).map((page) => ({\n order: page.order,\n name: page.slug,\n })),\n ...group.children.map((child) => ({ order: child.order, name: child.folder })),\n ]\n .sort((a, b) => a.order - b.order)\n .map((entry) => entry.name),\n },\n })),\n ];\n for (const meta of metas) {\n const target = path.join(outDir, meta.path);\n await mkdir(path.dirname(target), { recursive: true });\n await writeFile(target, `${JSON.stringify(meta.content, null, 2)}\\n`);\n }\n\n // Top-nav links: each tab points at its first page. The URL of an API page\n // needs its resolved slug, which is why this runs here, not in the planner.\n const baseUrl = (options.baseUrl ?? '/docs').replace(/\\/$/, '');\n const docUrl = (file: string): string => {\n const clean = file.replace(/\\/?index$/, '');\n return clean ? `${baseUrl}/${clean}` : baseUrl;\n };\n const firstUrl = (items: readonly NavItem[]): string | undefined => {\n for (const item of items) {\n if (item.kind === 'group') {\n const url = firstUrl(item.items);\n if (url) {\n return url;\n }\n } else if (item.kind === 'doc') {\n return docUrl(item.file);\n } else {\n const placement = placements.get(`${item.method.toUpperCase()} ${item.apiPath}`);\n if (placement) {\n return `${baseUrl}/${placement.dir}/${placement.slug}`;\n }\n }\n }\n return undefined;\n };\n const navLinks: NavLink[] = [];\n for (const tab of navigation) {\n const url = firstUrl(tab.items);\n if (url) {\n navLinks.push({ text: tab.title, url, ...(tab.icon ? { icon: tab.icon } : {}) });\n }\n }\n await writeFile(\n path.join(options.contentDir, 'nav.json'),\n `${JSON.stringify(navLinks, null, 2)}\\n`,\n );\n\n // Page URL -> HTTP method, for the sidebar method badges.\n const methodsByUrl: Record<string, string> = {};\n for (const page of plan.apiPages) {\n const placement = placements.get(`${page.method.toUpperCase()} ${page.apiPath}`);\n if (placement) {\n methodsByUrl[`${baseUrl}/${placement.dir}/${placement.slug}`] = page.method.toUpperCase();\n }\n }\n await writeFile(\n path.join(options.contentDir, 'api-methods.json'),\n `${JSON.stringify(methodsByUrl, null, 2)}\\n`,\n );\n\n if (plan.apiTabDocPages.length > 0) {\n console.warn(\n `[docs-core] ${String(plan.apiTabDocPages.length)} doc page(s) are declared inside API ` +\n `tabs and cannot be placed in the generated sidebar folders: ` +\n `${plan.apiTabDocPages.join(', ')}. Each page stays reachable at its own URL; ` +\n 'to list one in the sidebar, move it into a doc tab.',\n );\n }\n\n return { pages, metas: metas.length, navLinks, methodsByUrl, document };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/generator/index.ts"],"names":[],"mappings":"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","file":"generator.js","sourcesContent":["/**\n * `@blaaiz/docs-core/generator` — builds the docs content tree from `docs.json`.\n *\n * One call turns the site's navigation file into everything fumadocs needs:\n * generated API pages placed in their configured groups, and `meta.json` files\n * encoding tab and group order. Authors reorganize the docs by editing\n * `docs.json` — never by moving files.\n *\n * Node-only. A site that publishes an API reference injects `createOpenAPI` and\n * `generateFiles` from its own `fumadocs-openapi` install, so this package takes\n * no dependency on it. A site of prose alone injects nothing: with no API pages\n * in `docs.json` the generator writes the `meta.json` files and `nav.json` and\n * never touches `fumadocs-openapi`.\n *\n * @packageDocumentation\n */\n\nimport { mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { mergeOpenApiDocuments } from '../adapters/index.js';\nimport { parseNavigation } from '../adapters/index.js';\nimport type { NavItem, OpenApiDocument, OpenApiInfo, OpenApiServer } from '../core/index.js';\nimport { planDocsTree } from '../core/index.js';\n\n/**\n * The two fumadocs-openapi functions the generator needs, injected by the site\n * (`createOpenAPI` from `fumadocs-openapi/server`, `generateFiles` from\n * `fumadocs-openapi`). Typed loosely so this package needs no fumadocs types.\n *\n * @public\n */\nexport interface FumadocsOpenApi {\n readonly createOpenAPI: (...args: never[]) => unknown;\n readonly generateFiles: (...args: never[]) => unknown;\n}\n\n/**\n * Options for {@link generateDocsTree}.\n *\n * @public\n */\nexport interface GenerateDocsTreeOptions {\n /** The site's content directory (holds `docs.json`, spec files, and `docs/`). */\n readonly contentDir: string;\n /** Path to the navigation file. Default `<contentDir>/docs.json`. */\n readonly docsJson?: string;\n /** Output directory for the page tree. Default `<contentDir>/docs`. */\n readonly outDir?: string;\n /** Folder for generated API pages. Default `api`. */\n readonly apiDir?: string;\n /**\n * `info` block of the merged OpenAPI document. Required when `docs.json`\n * publishes API pages; ignored on a site of prose alone, which merges no\n * documents.\n */\n readonly info?: OpenApiInfo;\n /** Servers of the merged document. */\n readonly servers?: readonly OpenApiServer[];\n /** Same-origin proxy route for the try-it playground. */\n readonly proxyUrl?: string;\n /** URL base the fumadocs loader serves pages under. Default `/docs`. */\n readonly baseUrl?: string;\n /**\n * The site's fumadocs-openapi functions. Required when `docs.json` publishes\n * API pages; omit it on a site of prose alone.\n */\n readonly fumadocs?: FumadocsOpenApi;\n}\n\n/**\n * A top-navigation link: one tab, pointing at its first page.\n *\n * @public\n */\nexport interface NavLink {\n readonly text: string;\n readonly url: string;\n /** The tab's icon name from `docs.json`, for the site to resolve. */\n readonly icon?: string;\n}\n\n/**\n * What the generator produced.\n *\n * @public\n */\nexport interface GenerateDocsTreeResult {\n readonly pages: number;\n readonly metas: number;\n /**\n * Top-nav links, one per tab, each pointing at the tab's first page — written\n * to `<contentDir>/nav.json` for the site's layout. Keeps the top nav in sync\n * with `docs.json` instead of a hand-maintained list.\n */\n readonly navLinks: readonly NavLink[];\n /**\n * Page URL to HTTP method, written to `<contentDir>/api-methods.json` for the\n * sidebar method badges (`apiMethodBadges` in `@blaaiz/docs-core/ui`).\n */\n readonly methodsByUrl: Readonly<Record<string, string>>;\n /** The merged OpenAPI document (reusable for the runtime page renderer). */\n readonly document: OpenApiDocument;\n}\n\ninterface GeneratedFile {\n path: string;\n content: string;\n}\n\nfunction slugify(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\n/**\n * The frontmatter `title` of an MDX page, or null when the file has none.\n * Reads only the leading frontmatter block; quotes around the value are\n * stripped.\n */\nfunction frontmatterTitle(source: string): string | null {\n const block = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/.exec(source);\n if (!block?.[1]) {\n return null;\n }\n const line = /^title\\s*:\\s*(.+)\\s*$/m.exec(block[1]);\n if (!line?.[1]) {\n return null;\n }\n return line[1].replace(/^(['\"])(.*)\\1$/, '$2');\n}\n\n/** The `METHOD path` key of the first operation a generated page renders. */\nfunction operationKeyOf(content: string): string | null {\n const match = /operations=\\{(\\[[\\s\\S]*?\\])\\}/.exec(content);\n if (!match?.[1]) {\n return null;\n }\n try {\n const ops = JSON.parse(match[1]) as { path: string; method: string }[];\n const op = ops[0];\n return op ? `${op.method.toUpperCase()} ${op.path}` : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The `info` a docs-only run reports on its (empty) merged document. Nothing\n * renders it: with no API pages there is no reference view to title.\n */\nconst PROSE_ONLY_INFO: OpenApiInfo = { title: 'No API reference', version: '0.0.0' };\n\n/**\n * The fumadocs functions, checked. Called only when the navigation publishes\n * API pages, so the message can name the exact reason they are needed.\n */\nfunction requireFumadocs(value: unknown, apiPageCount: number): FumadocsOpenApi {\n const because = `docs.json publishes ${String(apiPageCount)} API page(s)`;\n if (typeof value !== 'object' || value === null) {\n throw new Error(\n `[docs-core] generateDocsTree: the 'fumadocs' option is required because ${because}. ` +\n \"Pass { createOpenAPI, generateFiles } from the site's fumadocs-openapi install.\",\n );\n }\n const candidate = value as Record<string, unknown>;\n for (const name of ['createOpenAPI', 'generateFiles'] as const) {\n if (typeof candidate[name] !== 'function') {\n throw new Error(\n `[docs-core] generateDocsTree: fumadocs.${name} must be a function because ${because}. ` +\n `Import ${name} from ${name === 'createOpenAPI' ? \"'fumadocs-openapi/server'\" : \"'fumadocs-openapi'\"}.`,\n );\n }\n }\n return value as FumadocsOpenApi;\n}\n\n/**\n * Build the docs content tree from `docs.json`.\n *\n * Reads the navigation, merges every referenced spec, generates one page per\n * declared endpoint into its configured group folder, and writes the\n * `meta.json` files that give the sidebar the navigation's exact structure and\n * order. Operations present in a spec but not declared in `docs.json` get no\n * page — the navigation decides what is published, as on Mintlify.\n *\n * A site of prose alone needs neither option: with no API pages in `docs.json`\n * the run writes the `meta.json` files, `nav.json`, and an empty\n * `api-methods.json`, and never loads `fumadocs-openapi`.\n *\n * @example\n * ```ts\n * // A site of prose alone.\n * await generateDocsTree({ contentDir: 'content' });\n * ```\n *\n * @param options - directories, document info, and the injected fumadocs functions\n * @returns counts and the merged document\n * @throws when `docs.json` publishes API pages and `fumadocs` or `info` is missing\n * @public\n */\nexport async function generateDocsTree(\n options: GenerateDocsTreeOptions,\n): Promise<GenerateDocsTreeResult> {\n const docsJson = options.docsJson ?? path.join(options.contentDir, 'docs.json');\n const outDir = options.outDir ?? path.join(options.contentDir, 'docs');\n const apiDir = options.apiDir ?? 'api';\n\n const navigation = parseNavigation(JSON.parse(await readFile(docsJson, 'utf8')));\n const plan = planDocsTree(navigation, { apiDir });\n\n // Where each planned API page lands. Both stay empty on a prose-only site.\n const placements = new Map<string, { dir: string; order: number; slug: string }>();\n const groupPages = new Map<string, { order: number; slug: string }[]>();\n let pages = 0;\n let document: OpenApiDocument;\n\n if (plan.apiPages.length === 0) {\n // A site of prose alone. Nothing to merge, nothing to generate, and — most\n // importantly — nothing removed: a hand-written folder named `api` is the\n // site's own content, not our output.\n document = mergeOpenApiDocuments([], { info: options.info ?? PROSE_ONLY_INFO });\n } else {\n const fumadocs = requireFumadocs(options.fumadocs, plan.apiPages.length);\n if (options.info === undefined) {\n throw new Error(\n `[docs-core] generateDocsTree: the 'info' option is required because docs.json ` +\n `publishes ${String(plan.apiPages.length)} API page(s). ` +\n 'Pass { title, version } for the merged OpenAPI document.',\n );\n }\n\n // Merge exactly the specs the navigation references.\n const docs = await Promise.all(\n plan.specs.map(\n async (file) =>\n JSON.parse(await readFile(path.join(options.contentDir, file), 'utf8')) as unknown,\n ),\n );\n document = mergeOpenApiDocuments(docs, {\n info: options.info,\n ...(options.servers ? { servers: options.servers } : {}),\n });\n\n // Resolve each planned page's slug from the merged document.\n for (const page of plan.apiPages) {\n const operation = document.paths[page.apiPath]?.[page.method.toLowerCase()] as\n { operationId?: string } | undefined;\n if (!operation) {\n throw new Error(\n `[docs-core] docs.json references ${page.method} ${page.apiPath}, ` +\n `but ${page.file} defines no such operation.`,\n );\n }\n const slug = operation.operationId ?? slugify(`${page.method} ${page.apiPath}`);\n placements.set(`${page.method.toUpperCase()} ${page.apiPath}`, { ...page, slug });\n const group = groupPages.get(page.dir) ?? [];\n group.push({ order: page.order, slug });\n groupPages.set(page.dir, group);\n }\n\n // Fresh API tree, then generate pages and route each into its planned folder.\n for (const group of plan.apiGroups) {\n if (group.root) {\n await rm(path.join(outDir, group.dir), { recursive: true, force: true });\n }\n }\n const createOpenAPI = fumadocs.createOpenAPI as (input: unknown) => unknown;\n const generateFiles = fumadocs.generateFiles as (input: unknown) => Promise<void>;\n await generateFiles({\n input: createOpenAPI({\n input: { blaaiz: document },\n ...(options.proxyUrl ? { proxyUrl: options.proxyUrl } : {}),\n }),\n output: outDir,\n per: 'operation',\n groupBy: 'none',\n beforeWrite(files: GeneratedFile[]) {\n for (let i = files.length - 1; i >= 0; i -= 1) {\n const file = files[i];\n if (!file) {\n continue;\n }\n const key = operationKeyOf(file.content);\n const placement = key ? placements.get(key) : undefined;\n if (!placement) {\n files.splice(i, 1); // not declared in docs.json -> not published\n continue;\n }\n file.path = path.join(placement.dir, `${placement.slug}.mdx`);\n pages += 1;\n }\n },\n });\n }\n\n // Doc pages declared inside API tabs become link entries in their group's\n // meta, at their declared position; the page keeps living in its own folder.\n const baseUrl = (options.baseUrl ?? '/docs').replace(/\\/$/, '');\n const docUrl = (file: string): string => {\n const clean = file.replace(/\\/?index$/, '');\n return clean ? `${baseUrl}/${clean}` : baseUrl;\n };\n for (const link of plan.apiDocLinks) {\n const source = path.join(outDir, `${link.file}.mdx`);\n let title: string | null = null;\n try {\n title = frontmatterTitle(await readFile(source, 'utf8'));\n } catch {\n console.warn(\n `[docs-core] docs.json lists ${link.file} inside an API tab, but ` +\n `${source} does not exist. The sidebar entry is skipped.`,\n );\n continue;\n }\n if (title === null) {\n title = link.file.split('/').pop() ?? link.file;\n console.warn(\n `[docs-core] ${link.file}.mdx has no frontmatter title; its sidebar ` +\n `entry falls back to '${title}'.`,\n );\n }\n const group = groupPages.get(link.dir) ?? [];\n // The meta link syntax cannot carry these characters in a name or URL.\n group.push({\n order: link.order,\n slug: `[${title.replace(/[[\\]()]/g, '')}](${docUrl(link.file)})`,\n });\n groupPages.set(link.dir, group);\n }\n\n // Meta files: the plan's static ones, plus each API group's entries — its\n // resolved page slugs and child folders, merged back into declared order.\n const metas = [\n ...plan.metas,\n ...plan.apiGroups.map((group) => ({\n path: `${group.dir}/meta.json`,\n content: {\n title: group.title,\n ...(group.root ? { root: true } : {}),\n ...(group.icon ? { icon: group.icon } : {}),\n pages: [\n ...(groupPages.get(group.dir) ?? []).map((page) => ({\n order: page.order,\n name: page.slug,\n })),\n ...group.children.map((child) => ({ order: child.order, name: child.folder })),\n ]\n .sort((a, b) => a.order - b.order)\n .map((entry) => entry.name),\n },\n })),\n ];\n for (const meta of metas) {\n const target = path.join(outDir, meta.path);\n await mkdir(path.dirname(target), { recursive: true });\n await writeFile(target, `${JSON.stringify(meta.content, null, 2)}\\n`);\n }\n\n // Top-nav links: each tab points at its first page. The URL of an API page\n // needs its resolved slug, which is why this runs here, not in the planner.\n const firstUrl = (items: readonly NavItem[]): string | undefined => {\n for (const item of items) {\n if (item.kind === 'group') {\n const url = firstUrl(item.items);\n if (url) {\n return url;\n }\n } else if (item.kind === 'doc') {\n return docUrl(item.file);\n } else {\n const placement = placements.get(`${item.method.toUpperCase()} ${item.apiPath}`);\n if (placement) {\n return `${baseUrl}/${placement.dir}/${placement.slug}`;\n }\n }\n }\n return undefined;\n };\n const navLinks: NavLink[] = [];\n for (const tab of navigation) {\n const url = firstUrl(tab.items);\n if (url) {\n navLinks.push({ text: tab.title, url, ...(tab.icon ? { icon: tab.icon } : {}) });\n }\n }\n await writeFile(\n path.join(options.contentDir, 'nav.json'),\n `${JSON.stringify(navLinks, null, 2)}\\n`,\n );\n\n // Page URL -> HTTP method, for the sidebar method badges.\n const methodsByUrl: Record<string, string> = {};\n for (const page of plan.apiPages) {\n const placement = placements.get(`${page.method.toUpperCase()} ${page.apiPath}`);\n if (placement) {\n methodsByUrl[`${baseUrl}/${placement.dir}/${placement.slug}`] = page.method.toUpperCase();\n }\n }\n await writeFile(\n path.join(options.contentDir, 'api-methods.json'),\n `${JSON.stringify(methodsByUrl, null, 2)}\\n`,\n );\n\n return { pages, metas: metas.length, navLinks, methodsByUrl, document };\n}\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -1567,7 +1567,7 @@ function planDocsTree(navigation, options = {}) {
|
|
|
1567
1567
|
const apiGroups = [];
|
|
1568
1568
|
const metas = [];
|
|
1569
1569
|
const rootPages = [];
|
|
1570
|
-
const
|
|
1570
|
+
const apiDocLinks = [];
|
|
1571
1571
|
const apiTabs = navigation.filter((tab) => hasOpenApi(tab.items));
|
|
1572
1572
|
const soleApiTab = apiTabs.length === 1;
|
|
1573
1573
|
for (const tab of navigation) {
|
|
@@ -1578,7 +1578,7 @@ function planDocsTree(navigation, options = {}) {
|
|
|
1578
1578
|
planDocTab(tab);
|
|
1579
1579
|
}
|
|
1580
1580
|
metas.unshift({ path: "meta.json", content: { pages: rootPages } });
|
|
1581
|
-
return { specs, apiPages, apiGroups, metas,
|
|
1581
|
+
return { specs, apiPages, apiGroups, metas, apiDocLinks };
|
|
1582
1582
|
function planApiTab(tab) {
|
|
1583
1583
|
const tabDir = soleApiTab ? apiDir : slugify(tab.title);
|
|
1584
1584
|
rootPages.push(tabDir);
|
|
@@ -1621,7 +1621,8 @@ function planDocsTree(navigation, options = {}) {
|
|
|
1621
1621
|
order += 1;
|
|
1622
1622
|
continue;
|
|
1623
1623
|
}
|
|
1624
|
-
|
|
1624
|
+
apiDocLinks.push({ file: item.file, dir, order });
|
|
1625
|
+
order += 1;
|
|
1625
1626
|
}
|
|
1626
1627
|
return children;
|
|
1627
1628
|
}
|