create-dox 0.1.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/README.md +79 -0
- package/dist/index.js +350 -0
- package/package.json +30 -0
package/README.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# create-dox
|
|
2
|
+
|
|
3
|
+
Scaffold a new [Dox](https://github.com/kenny-io/Dox) documentation project in seconds.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
### With npx (recommended)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx create-dox my-docs
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Or install globally
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g create-dox
|
|
17
|
+
create-dox my-docs
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Interactive prompts
|
|
21
|
+
|
|
22
|
+
If you run without arguments, the CLI will ask you:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
$ npx create-dox
|
|
26
|
+
|
|
27
|
+
╔══════════════════════════════════════╗
|
|
28
|
+
║ ██████╗ ██████╗ ██╗ ██╗ ║
|
|
29
|
+
║ ██╔══██╗██╔═══██╗╚██╗██╔╝ ║
|
|
30
|
+
║ ██║ ██║██║ ██║ ╚███╔╝ ║
|
|
31
|
+
║ ██║ ██║██║ ██║ ██╔██╗ ║
|
|
32
|
+
║ ██████╔╝╚██████╔╝██╔╝ ██╗ ║
|
|
33
|
+
║ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ║
|
|
34
|
+
║ ║
|
|
35
|
+
║ Beautiful docs, zero lock-in. ║
|
|
36
|
+
╚══════════════════════════════════════╝
|
|
37
|
+
|
|
38
|
+
Project directory (my-docs): acme-docs
|
|
39
|
+
Project name (Acme Docs):
|
|
40
|
+
Description (Documentation for Acme Docs.):
|
|
41
|
+
Brand preset:
|
|
42
|
+
1) primary
|
|
43
|
+
2) secondary
|
|
44
|
+
> Choose [1]: 1
|
|
45
|
+
GitHub repo URL (optional): https://github.com/acme/docs
|
|
46
|
+
Install dependencies? (Y/n): Y
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## What it does
|
|
50
|
+
|
|
51
|
+
1. Clones the Dox template from GitHub
|
|
52
|
+
2. Replaces example content with starter pages customized to your project name
|
|
53
|
+
3. Updates `src/data/site.ts` with your name, description, branding, and repo URL
|
|
54
|
+
4. Writes a minimal `docs.json` navigation config
|
|
55
|
+
5. Installs dependencies
|
|
56
|
+
6. Initializes a fresh git repo
|
|
57
|
+
|
|
58
|
+
## After scaffolding
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
cd my-docs
|
|
62
|
+
npm run dev
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Open [http://localhost:3040](http://localhost:3040) to see your docs.
|
|
66
|
+
|
|
67
|
+
### Key files
|
|
68
|
+
|
|
69
|
+
| File | Purpose |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| `src/data/site.ts` | Site name, links, brand colors |
|
|
72
|
+
| `docs.json` | Navigation tabs, groups, pages |
|
|
73
|
+
| `src/content/*.mdx` | Your documentation pages |
|
|
74
|
+
| `openapi.yaml` | API spec (optional) |
|
|
75
|
+
|
|
76
|
+
## Requirements
|
|
77
|
+
|
|
78
|
+
- Node.js >= 18
|
|
79
|
+
- Git
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// create-dox — Scaffold a new Dox documentation project.
|
|
4
|
+
// Zero dependencies. Requires Node >= 18.
|
|
5
|
+
|
|
6
|
+
import { createInterface } from 'node:readline'
|
|
7
|
+
import { execSync } from 'node:child_process'
|
|
8
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, cpSync } from 'node:fs'
|
|
9
|
+
import { resolve, join, basename } from 'node:path'
|
|
10
|
+
|
|
11
|
+
// ── Constants ────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
const REPO_URL = 'https://github.com/kenny-io/Dox.git'
|
|
14
|
+
const BRAND_PRESETS = ['primary', 'secondary']
|
|
15
|
+
const args = process.argv.slice(2)
|
|
16
|
+
const flags = args.filter((a) => a.startsWith('-'))
|
|
17
|
+
const positional = args.filter((a) => !a.startsWith('-'))
|
|
18
|
+
const useDefaults = flags.includes('--yes') || flags.includes('-y')
|
|
19
|
+
|
|
20
|
+
const STARTER_PAGES = {
|
|
21
|
+
'introduction.mdx': `---
|
|
22
|
+
title: Introduction
|
|
23
|
+
description: Welcome to {NAME} documentation.
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Welcome
|
|
27
|
+
|
|
28
|
+
This is the home page of your **{NAME}** documentation site, powered by [Dox](https://github.com/kenny-io/Dox).
|
|
29
|
+
|
|
30
|
+
Get started by editing this file at \`src/content/introduction.mdx\`.
|
|
31
|
+
`,
|
|
32
|
+
'quickstart.mdx': `---
|
|
33
|
+
title: Quickstart
|
|
34
|
+
description: Get up and running with {NAME} in under 5 minutes.
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
\`\`\`bash
|
|
40
|
+
npm install {SLUG}
|
|
41
|
+
\`\`\`
|
|
42
|
+
|
|
43
|
+
## Basic usage
|
|
44
|
+
|
|
45
|
+
\`\`\`ts
|
|
46
|
+
import { create } from '{SLUG}'
|
|
47
|
+
|
|
48
|
+
const client = create({ apiKey: 'your-api-key' })
|
|
49
|
+
\`\`\`
|
|
50
|
+
|
|
51
|
+
That's it — you're ready to go!
|
|
52
|
+
`,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const STARTER_DOCS_JSON = `{
|
|
56
|
+
"tabs": [
|
|
57
|
+
{
|
|
58
|
+
"tab": "Overview",
|
|
59
|
+
"groups": [
|
|
60
|
+
{
|
|
61
|
+
"group": "Getting Started",
|
|
62
|
+
"pages": ["introduction", "quickstart"]
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"tab": "Changelog",
|
|
68
|
+
"href": "/changelog"
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
`
|
|
73
|
+
|
|
74
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
77
|
+
|
|
78
|
+
function ask(question, fallback) {
|
|
79
|
+
return new Promise((resolve) => {
|
|
80
|
+
const suffix = fallback ? ` (${fallback})` : ''
|
|
81
|
+
rl.question(`${question}${suffix}: `, (answer) => {
|
|
82
|
+
resolve(answer.trim() || fallback || '')
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function choose(question, options, fallback) {
|
|
88
|
+
return new Promise((resolve) => {
|
|
89
|
+
const optionList = options.map((o, i) => ` ${i + 1}) ${o}`).join('\n')
|
|
90
|
+
const defaultIndex = options.indexOf(fallback) + 1
|
|
91
|
+
const suffix = defaultIndex ? ` [${defaultIndex}]` : ''
|
|
92
|
+
rl.question(`${question}\n${optionList}\n> Choose${suffix}: `, (answer) => {
|
|
93
|
+
const num = parseInt(answer.trim(), 10)
|
|
94
|
+
if (num >= 1 && num <= options.length) {
|
|
95
|
+
resolve(options[num - 1])
|
|
96
|
+
} else {
|
|
97
|
+
resolve(fallback || options[0])
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function slugify(name) {
|
|
104
|
+
return name
|
|
105
|
+
.toLowerCase()
|
|
106
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
107
|
+
.replace(/(^-|-$)/g, '')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function run(cmd, cwd) {
|
|
111
|
+
execSync(cmd, { cwd, stdio: 'inherit' })
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function runSilent(cmd, cwd) {
|
|
115
|
+
return execSync(cmd, { cwd, encoding: 'utf8' }).trim()
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function logo() {
|
|
119
|
+
console.log('')
|
|
120
|
+
console.log(' ╔══════════════════════════════════════╗')
|
|
121
|
+
console.log(' ║ ║')
|
|
122
|
+
console.log(' ║ ██████╗ ██████╗ ██╗ ██╗ ║')
|
|
123
|
+
console.log(' ║ ██╔══██╗██╔═══██╗╚██╗██╔╝ ║')
|
|
124
|
+
console.log(' ║ ██║ ██║██║ ██║ ╚███╔╝ ║')
|
|
125
|
+
console.log(' ║ ██║ ██║██║ ██║ ██╔██╗ ║')
|
|
126
|
+
console.log(' ║ ██████╔╝╚██████╔╝██╔╝ ██╗ ║')
|
|
127
|
+
console.log(' ║ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ║')
|
|
128
|
+
console.log(' ║ ║')
|
|
129
|
+
console.log(' ║ Beautiful docs, zero lock-in. ║')
|
|
130
|
+
console.log(' ║ ║')
|
|
131
|
+
console.log(' ╚══════════════════════════════════════╝')
|
|
132
|
+
console.log('')
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function success(projectDir, projectName) {
|
|
136
|
+
console.log('')
|
|
137
|
+
console.log(' ✅ Your Dox project is ready!')
|
|
138
|
+
console.log('')
|
|
139
|
+
console.log(` 📂 ${projectDir}`)
|
|
140
|
+
console.log('')
|
|
141
|
+
console.log(' Next steps:')
|
|
142
|
+
console.log('')
|
|
143
|
+
console.log(` cd ${basename(projectDir)}`)
|
|
144
|
+
console.log(' npm run dev')
|
|
145
|
+
console.log('')
|
|
146
|
+
console.log(` Then open http://localhost:3040 to see your ${projectName} docs.`)
|
|
147
|
+
console.log('')
|
|
148
|
+
console.log(' 📝 Key files to edit:')
|
|
149
|
+
console.log(' • src/data/site.ts — name, links, branding')
|
|
150
|
+
console.log(' • docs.json — navigation structure')
|
|
151
|
+
console.log(' • src/content/*.mdx — your documentation')
|
|
152
|
+
console.log(' • openapi.yaml — API spec (optional)')
|
|
153
|
+
console.log('')
|
|
154
|
+
console.log(' Happy documenting! 🚀')
|
|
155
|
+
console.log('')
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ── Scaffold logic ───────────────────────────────────────────────────────────
|
|
159
|
+
|
|
160
|
+
function cloneTemplate(targetDir) {
|
|
161
|
+
console.log('')
|
|
162
|
+
console.log(' ⏳ Cloning Dox template...')
|
|
163
|
+
run(`git clone --depth 1 ${REPO_URL} "${targetDir}"`)
|
|
164
|
+
|
|
165
|
+
// Remove the template's .git so the user starts fresh
|
|
166
|
+
const gitDir = join(targetDir, '.git')
|
|
167
|
+
if (existsSync(gitDir)) {
|
|
168
|
+
execSync(`rm -rf "${gitDir}"`)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Remove the CLI folder from the cloned project (they don't need it)
|
|
172
|
+
const cliDir = join(targetDir, 'cli')
|
|
173
|
+
if (existsSync(cliDir)) {
|
|
174
|
+
execSync(`rm -rf "${cliDir}"`)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function writeStarterContent(targetDir, projectName, slug) {
|
|
179
|
+
const contentDir = join(targetDir, 'src', 'content')
|
|
180
|
+
|
|
181
|
+
// Clear existing example content
|
|
182
|
+
if (existsSync(contentDir)) {
|
|
183
|
+
const entries = readdirSync(contentDir)
|
|
184
|
+
for (const entry of entries) {
|
|
185
|
+
const fullPath = join(contentDir, entry)
|
|
186
|
+
execSync(`rm -rf "${fullPath}"`)
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
mkdirSync(contentDir, { recursive: true })
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Write starter pages
|
|
193
|
+
for (const [filename, template] of Object.entries(STARTER_PAGES)) {
|
|
194
|
+
const content = template
|
|
195
|
+
.replace(/\{NAME\}/g, projectName)
|
|
196
|
+
.replace(/\{SLUG\}/g, slug)
|
|
197
|
+
writeFileSync(join(contentDir, filename), content, 'utf8')
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Write docs.json
|
|
201
|
+
writeFileSync(join(targetDir, 'docs.json'), STARTER_DOCS_JSON, 'utf8')
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function updateSiteConfig(targetDir, projectName, description, brandPreset, repoUrl) {
|
|
205
|
+
const siteFile = join(targetDir, 'src', 'data', 'site.ts')
|
|
206
|
+
if (!existsSync(siteFile)) {
|
|
207
|
+
console.log(' ⚠️ Could not find src/data/site.ts — skipping config update.')
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
let source = readFileSync(siteFile, 'utf8')
|
|
212
|
+
|
|
213
|
+
// Replace name
|
|
214
|
+
source = source.replace(
|
|
215
|
+
/name:\s*'[^']*'/,
|
|
216
|
+
`name: '${projectName.replace(/'/g, "\\'")}'`,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
// Replace description (handles multiline template string)
|
|
220
|
+
source = source.replace(
|
|
221
|
+
/description:[\s\S]*?'([^']*)'/,
|
|
222
|
+
`description:\n '${description.replace(/'/g, "\\'")}'`,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
// Replace brand preset
|
|
226
|
+
source = source.replace(
|
|
227
|
+
/const brandPreset:\s*BrandPresetKey\s*=\s*'[^']*'/,
|
|
228
|
+
`const brandPreset: BrandPresetKey = '${brandPreset}'`,
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
// Replace repo URL
|
|
232
|
+
if (repoUrl) {
|
|
233
|
+
source = source.replace(
|
|
234
|
+
/repoUrl:\s*'[^']*'/,
|
|
235
|
+
`repoUrl: '${repoUrl}'`,
|
|
236
|
+
)
|
|
237
|
+
// Also update GitHub link
|
|
238
|
+
source = source.replace(
|
|
239
|
+
/\{\s*label:\s*'GitHub',\s*href:\s*'[^']*'\s*\}/,
|
|
240
|
+
`{ label: 'GitHub', href: '${repoUrl}' }`,
|
|
241
|
+
)
|
|
242
|
+
// Update support link
|
|
243
|
+
source = source.replace(
|
|
244
|
+
/\{\s*label:\s*'Support',\s*href:\s*'[^']*'\s*\}/,
|
|
245
|
+
`{ label: 'Support', href: '${repoUrl}/issues/new' }`,
|
|
246
|
+
)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
writeFileSync(siteFile, source, 'utf8')
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function updateEnvExample(targetDir) {
|
|
253
|
+
const envFile = join(targetDir, '.env.example')
|
|
254
|
+
if (existsSync(envFile)) {
|
|
255
|
+
// Copy .env.example to .env.local for immediate use
|
|
256
|
+
const envLocal = join(targetDir, '.env.local')
|
|
257
|
+
if (!existsSync(envLocal)) {
|
|
258
|
+
cpSync(envFile, envLocal)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function initGit(targetDir) {
|
|
264
|
+
try {
|
|
265
|
+
run('git init', targetDir)
|
|
266
|
+
run('git add -A', targetDir)
|
|
267
|
+
run('git commit -m "Initial commit from create-dox"', targetDir)
|
|
268
|
+
} catch {
|
|
269
|
+
// Git might not be configured — that's fine
|
|
270
|
+
console.log(' ⚠️ Could not initialize git (you can do this manually).')
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function installDeps(targetDir) {
|
|
275
|
+
console.log('')
|
|
276
|
+
console.log(' 📦 Installing dependencies...')
|
|
277
|
+
console.log('')
|
|
278
|
+
run('npm install', targetDir)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ── Main ─────────────────────────────────────────────────────────────────────
|
|
282
|
+
|
|
283
|
+
async function main() {
|
|
284
|
+
logo()
|
|
285
|
+
|
|
286
|
+
// 1. Project directory
|
|
287
|
+
const dirArg = positional[0]
|
|
288
|
+
let projectDir
|
|
289
|
+
if (dirArg) {
|
|
290
|
+
projectDir = resolve(dirArg)
|
|
291
|
+
} else if (useDefaults) {
|
|
292
|
+
projectDir = resolve('my-docs')
|
|
293
|
+
} else {
|
|
294
|
+
const dirName = await ask(' Project directory', 'my-docs')
|
|
295
|
+
projectDir = resolve(dirName)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (existsSync(projectDir) && readdirSync(projectDir).length > 0) {
|
|
299
|
+
console.log(`\n ❌ Directory "${projectDir}" already exists and is not empty.`)
|
|
300
|
+
rl.close()
|
|
301
|
+
process.exit(1)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// 2. Project name
|
|
305
|
+
const defaultName = basename(projectDir)
|
|
306
|
+
.replace(/[-_]/g, ' ')
|
|
307
|
+
.replace(/\b\w/g, (c) => c.toUpperCase())
|
|
308
|
+
const projectName = useDefaults ? defaultName : await ask(' Project name', defaultName)
|
|
309
|
+
|
|
310
|
+
// 3. Description
|
|
311
|
+
const defaultDesc = `Documentation for ${projectName}.`
|
|
312
|
+
const description = useDefaults ? defaultDesc : await ask(' Description', defaultDesc)
|
|
313
|
+
|
|
314
|
+
// 4. Brand preset
|
|
315
|
+
const brandPreset = useDefaults ? 'primary' : await choose('\n Brand preset:', BRAND_PRESETS, 'primary')
|
|
316
|
+
|
|
317
|
+
// 5. GitHub repo (optional)
|
|
318
|
+
const repoUrl = useDefaults ? '' : await ask(' GitHub repo URL (optional)', '')
|
|
319
|
+
|
|
320
|
+
// 6. Install deps?
|
|
321
|
+
let doInstall = true
|
|
322
|
+
if (!useDefaults) {
|
|
323
|
+
const shouldInstall = await ask(' Install dependencies? (Y/n)', 'Y')
|
|
324
|
+
doInstall = shouldInstall.toLowerCase() !== 'n'
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const slug = slugify(projectName)
|
|
328
|
+
|
|
329
|
+
// ── Execute ──────────────────────────────────────────────────────────────
|
|
330
|
+
|
|
331
|
+
cloneTemplate(projectDir)
|
|
332
|
+
writeStarterContent(projectDir, projectName, slug)
|
|
333
|
+
updateSiteConfig(projectDir, projectName, description, brandPreset, repoUrl)
|
|
334
|
+
updateEnvExample(projectDir)
|
|
335
|
+
|
|
336
|
+
if (doInstall) {
|
|
337
|
+
installDeps(projectDir)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
initGit(projectDir)
|
|
341
|
+
success(projectDir, projectName)
|
|
342
|
+
|
|
343
|
+
rl.close()
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
main().catch((err) => {
|
|
347
|
+
console.error('\n ❌ Error:', err.message)
|
|
348
|
+
rl.close()
|
|
349
|
+
process.exit(1)
|
|
350
|
+
})
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-dox",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Scaffold a new Dox documentation project.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"create-dox": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "node build.mjs",
|
|
15
|
+
"dev": "node src/index.js"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {},
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"dox",
|
|
23
|
+
"docs",
|
|
24
|
+
"documentation",
|
|
25
|
+
"cli",
|
|
26
|
+
"scaffold",
|
|
27
|
+
"template",
|
|
28
|
+
"mintlify"
|
|
29
|
+
]
|
|
30
|
+
}
|