1 #!/usr/bin/env node
2 /**
3 * Generate the /developer docs from the sibling `docs/` tree.
4 *
5 * The CLI / tooling documentation lives in `docs/` at the smbcloud-cli repo root
6 * (this site is `website/` in the same repo), so it is read in place — no clone,
7 * no network. This runs automatically on `predev`/`prebuild`; the generated
8 * pages are gitignored, leaving `docs/` as the single source of truth.
9 *
10 * pnpm docs:generate
11 *
12 * Hand-written index pages (content/developer/**\/index.mdx) are NOT touched.
13 * Only the files listed in MANIFEST and the generated _meta.js are written.
14 */
15 import { readFileSync, writeFileSync, mkdirSync } from 'node:fs'
16 import { join, dirname } from 'node:path'
17 import { fileURLToPath } from 'node:url'
18
19 const BRANCH = 'main'
20 const SRC_DIR = 'docs'
21
22 const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')
23 // `docs/` sits next to `website/` at the smbcloud-cli repo root.
24 const SRC = join(ROOT, '..', SRC_DIR)
25 const DEST = join(ROOT, 'content', 'developer')
26
27 // Source file -> where it lands and how it's labelled in the sidebar.
28 // `mail-service.md` is intentionally excluded: it's a scratch notes file in the
29 // source repo, not publishable documentation.
30 const MANIFEST = {
31 cli: {
32 title: 'CLI',
33 pages: [
34 { src: 'cli-install.md', slug: 'native', title: 'Native (Cargo)' },
35 { src: 'cli-install-homebrew.md', slug: 'homebrew', title: 'Homebrew' },
36 { src: 'cli-install-npm.md', slug: 'npm', title: 'npm' },
37 { src: 'cli-install-pypi.md', slug: 'pypi', title: 'PyPI' }
38 ]
39 },
40 contributing: {
41 title: 'Contributing',
42 pages: [
43 { src: 'debugging.md', slug: 'debugging', title: 'VSCode Debugging' },
44 { src: 'development.md', slug: 'development', title: 'Releasing & ADRs' },
45 { src: 'gems.md', slug: 'gems', title: 'Publishing the gem' }
46 ]
47 }
48 }
49
50 const blobUrl = src =>
51 `https://github.com/smbcloudXYZ/smbcloud-cli/blob/${BRANCH}/${SRC_DIR}/${src}`
52
53 /** Wrap generated markdown: header note + a source callout after the first H1. */
54 function decorate(markdown, src) {
55 const note = `{/* AUTO-GENERATED — do not edit. Generated by scripts/generate-developer-docs.mjs from ../${SRC_DIR}/${src}. */}`
56 const callout = [
57 "import { Callout } from 'nextra/components'",
58 '',
59 '<Callout type="info">',
60 ` Generated from [\`${SRC_DIR}/${src}\`](${blobUrl(src)}) in`,
61 ' [smbcloud-cli](https://github.com/smbcloudXYZ/smbcloud-cli). Edit it there.',
62 '</Callout>'
63 ].join('\n')
64
65 const lines = markdown.replace(/\r\n/g, '\n').trimEnd().split('\n')
66 const h1 = lines.findIndex(l => /^#\s/.test(l))
67 if (h1 === -1) {
68 return `${note}\n\n${callout}\n\n${lines.join('\n')}\n`
69 }
70 const before = lines.slice(0, h1 + 1).join('\n')
71 const after = lines.slice(h1 + 1).join('\n')
72 return `${note}\n\n${before}\n\n${callout}\n${after}\n`
73 }
74
75 // Note: the folder's own index page uses `asIndexPage` frontmatter, so it must
76 // NOT be listed as an `index` key here — Nextra surfaces it via the parent.
77 function metaFile(pages) {
78 const entries = pages.map(p => ` ${p.slug}: ${JSON.stringify(p.title)},`)
79 return `export default {\n${entries.join('\n')}\n}\n`
80 }
81
82 function main() {
83 let count = 0
84 for (const [section, { title, pages }] of Object.entries(MANIFEST)) {
85 const dir = join(DEST, section)
86 mkdirSync(dir, { recursive: true })
87 for (const page of pages) {
88 const raw = readFileSync(join(SRC, page.src), 'utf8')
89 writeFileSync(join(dir, `${page.slug}.mdx`), decorate(raw, page.src))
90 count++
91 }
92 writeFileSync(join(dir, '_meta.js'), metaFile(pages))
93 console.log(` ${section}: ${pages.length} pages (${title})`)
94 }
95
96 // Top-level developer nav: each section (the overview is asIndexPage).
97 const top = []
98 for (const [section, { title }] of Object.entries(MANIFEST)) {
99 top.push(` ${section}: ${JSON.stringify(title)},`)
100 }
101 writeFileSync(join(DEST, '_meta.js'), `export default {\n${top.join('\n')}\n}\n`)
102
103 console.log(`\nGenerated ${count} pages into content/developer/ from ${SRC}.`)
104 }
105
106 main()