@inovus-medical/docs 0.9.0 → 0.10.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/bin/cli.js +29 -7
- package/package.json +1 -1
- package/src/build.js +4 -3
- package/src/config.js +2 -0
- package/src/init.js +77 -14
- package/src/watch.js +8 -3
package/bin/cli.js
CHANGED
|
@@ -29,7 +29,7 @@ const flags = parseFlags(cmd === argv[0] ? argv.slice(1) : argv)
|
|
|
29
29
|
|
|
30
30
|
const root = path.resolve(flags.root || process.cwd())
|
|
31
31
|
const inDir = flags.in // undefined -> build() auto-detects (docs/ or repo root)
|
|
32
|
-
const
|
|
32
|
+
const outDirFlag = typeof flags.out === 'string' ? flags.out : undefined
|
|
33
33
|
const port = Number(flags.port || 8788)
|
|
34
34
|
const theme = typeof flags.theme === 'string' ? flags.theme : undefined
|
|
35
35
|
|
|
@@ -48,6 +48,9 @@ async function run() {
|
|
|
48
48
|
content,
|
|
49
49
|
buildCommand,
|
|
50
50
|
deployCommand,
|
|
51
|
+
localInstall,
|
|
52
|
+
localDev,
|
|
53
|
+
packageManager,
|
|
51
54
|
coexisting
|
|
52
55
|
} = result
|
|
53
56
|
console.log(
|
|
@@ -55,6 +58,7 @@ async function run() {
|
|
|
55
58
|
)
|
|
56
59
|
if (content && content !== '.') console.log(` content: ${content}/`)
|
|
57
60
|
if (coexisting) console.log(' mode: coexisting with existing package.json (docs scripts are docs:*)')
|
|
61
|
+
if (packageManager) console.log(` package manager: ${packageManager}`)
|
|
58
62
|
if (created.length) console.log(' created: ' + created.join(', '))
|
|
59
63
|
if (skipped.length) console.log(' kept existing (use --force to overwrite): ' + skipped.join(', '))
|
|
60
64
|
for (const n of notes || []) console.log(' ⚠ ' + n)
|
|
@@ -64,21 +68,36 @@ async function run() {
|
|
|
64
68
|
)
|
|
65
69
|
}
|
|
66
70
|
console.log('\nNext:')
|
|
67
|
-
console.log(
|
|
68
|
-
console.log(
|
|
71
|
+
console.log(` ${localInstall}`)
|
|
72
|
+
console.log(` ${localDev} # preview at http://localhost:8788`)
|
|
69
73
|
console.log(' git add -A && git commit -m "Add docs site config" && git push')
|
|
70
|
-
console.log(
|
|
74
|
+
console.log(' (Commit the lockfile too — Cloudflare installs with --frozen-lockfile.)')
|
|
75
|
+
console.log('\nCloudflare Worker (same for every repo — copy exactly):')
|
|
76
|
+
console.log(` Build command: ${buildCommand}`)
|
|
77
|
+
console.log(` Deploy command: ${deployCommand}`)
|
|
71
78
|
return
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
if (cmd === 'build') {
|
|
75
|
-
const { pageCount, outPath, contentDir } = await build({
|
|
76
|
-
|
|
82
|
+
const { pageCount, outPath, contentDir, outDir } = await build({
|
|
83
|
+
root,
|
|
84
|
+
inDir,
|
|
85
|
+
outDir: outDirFlag,
|
|
86
|
+
theme
|
|
87
|
+
})
|
|
88
|
+
console.log(
|
|
89
|
+
`✓ Built ${pageCount} page(s) from ${contentDir === '.' ? 'repo root' : contentDir + '/'} -> ${path.relative(process.cwd(), outPath) || outDir}`
|
|
90
|
+
)
|
|
77
91
|
return
|
|
78
92
|
}
|
|
79
93
|
|
|
80
94
|
if (cmd === 'dev') {
|
|
81
|
-
const { pageCount, outPath, contentDir } = await build({
|
|
95
|
+
const { pageCount, outPath, contentDir, outDir } = await build({
|
|
96
|
+
root,
|
|
97
|
+
inDir,
|
|
98
|
+
outDir: outDirFlag,
|
|
99
|
+
theme
|
|
100
|
+
})
|
|
82
101
|
console.log(`✓ Built ${pageCount} page(s) from ${contentDir === '.' ? 'repo root' : contentDir + '/'}`)
|
|
83
102
|
const { port: actual } = await serve({ dir: outPath, port })
|
|
84
103
|
const busy = actual !== port ? ` (port ${port} was busy)` : ''
|
|
@@ -88,6 +107,9 @@ async function run() {
|
|
|
88
107
|
}
|
|
89
108
|
|
|
90
109
|
if (cmd === 'serve') {
|
|
110
|
+
const { loadConfig } = await import('../src/config.js')
|
|
111
|
+
const config = await loadConfig(root)
|
|
112
|
+
const outDir = outDirFlag || config.outDir || 'dist'
|
|
91
113
|
const outPath = path.join(root, outDir)
|
|
92
114
|
const { port: actual } = await serve({ dir: outPath, port })
|
|
93
115
|
const busy = actual !== port ? ` (port ${port} was busy)` : ''
|
package/package.json
CHANGED
package/src/build.js
CHANGED
|
@@ -119,11 +119,12 @@ function routeToFile(outPath, route) {
|
|
|
119
119
|
return path.join(outPath, rel, 'index.html')
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
export async function build({ root = process.cwd(), inDir, outDir
|
|
122
|
+
export async function build({ root = process.cwd(), inDir, outDir, theme: themeFlag } = {}) {
|
|
123
123
|
const config = await loadConfig(root)
|
|
124
124
|
const theme = await resolveTheme({ themeFlag, config })
|
|
125
125
|
config.theme = theme
|
|
126
|
-
const
|
|
126
|
+
const resolvedOut = outDir || config.outDir || 'dist'
|
|
127
|
+
const outPath = path.join(root, resolvedOut)
|
|
127
128
|
const ignore = makeIgnore(config, path.basename(outPath))
|
|
128
129
|
|
|
129
130
|
const contentDir = await resolveContentDir({ root, inDir, config, ignore })
|
|
@@ -235,5 +236,5 @@ export async function build({ root = process.cwd(), inDir, outDir = 'dist', them
|
|
|
235
236
|
await fs.copyFile(file, dest)
|
|
236
237
|
}
|
|
237
238
|
|
|
238
|
-
return { pageCount: pages.length, outPath, contentDir }
|
|
239
|
+
return { pageCount: pages.length, outPath, contentDir, outDir: resolvedOut }
|
|
239
240
|
}
|
package/src/config.js
CHANGED
|
@@ -9,6 +9,8 @@ const DEFAULTS = {
|
|
|
9
9
|
accent: null,
|
|
10
10
|
// Named theme (each has light + dark). Overridden by DOCS_THEME / --theme.
|
|
11
11
|
theme: 'totum',
|
|
12
|
+
// Build output directory. null → "dist". App repos usually use "dist-docs".
|
|
13
|
+
outDir: null,
|
|
12
14
|
repo: null,
|
|
13
15
|
editBase: null,
|
|
14
16
|
visibility: 'private',
|
package/src/init.js
CHANGED
|
@@ -71,6 +71,34 @@ function titleCase(s) {
|
|
|
71
71
|
.replace(/\b\w/g, (c) => c.toUpperCase())
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Detect the repo's package manager from lockfiles / packageManager field.
|
|
76
|
+
* Cloudflare Workers Builds does the same (pnpm/yarn/npm from lockfile).
|
|
77
|
+
*/
|
|
78
|
+
async function detectPackageManager(root, pkg) {
|
|
79
|
+
if (await fileExists(path.join(root, 'pnpm-lock.yaml'))) return 'pnpm'
|
|
80
|
+
if (await fileExists(path.join(root, 'yarn.lock'))) return 'yarn'
|
|
81
|
+
if (
|
|
82
|
+
(await fileExists(path.join(root, 'bun.lockb'))) ||
|
|
83
|
+
(await fileExists(path.join(root, 'bun.lock')))
|
|
84
|
+
) {
|
|
85
|
+
return 'bun'
|
|
86
|
+
}
|
|
87
|
+
const field = pkg && typeof pkg.packageManager === 'string' ? pkg.packageManager : ''
|
|
88
|
+
if (field.startsWith('pnpm@')) return 'pnpm'
|
|
89
|
+
if (field.startsWith('yarn@')) return 'yarn'
|
|
90
|
+
if (field.startsWith('bun@')) return 'bun'
|
|
91
|
+
if (await fileExists(path.join(root, 'package-lock.json'))) return 'npm'
|
|
92
|
+
return 'npm'
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function runScript(pm, script) {
|
|
96
|
+
if (pm === 'yarn') return `yarn ${script}`
|
|
97
|
+
if (pm === 'bun') return `bun run ${script}`
|
|
98
|
+
if (pm === 'pnpm') return `pnpm run ${script}`
|
|
99
|
+
return `npm run ${script}`
|
|
100
|
+
}
|
|
101
|
+
|
|
74
102
|
const WRANGLER = (name, outDir) => `{
|
|
75
103
|
"$schema": "node_modules/wrangler/config-schema.json",
|
|
76
104
|
"name": "${name}",
|
|
@@ -145,10 +173,13 @@ export async function init({ root = process.cwd(), flags = {} } = {}) {
|
|
|
145
173
|
let content = typeof flags.content === 'string' ? flags.content.trim() : null
|
|
146
174
|
if (!content && (await fileExists(path.join(root, 'docs')))) content = 'docs'
|
|
147
175
|
|
|
148
|
-
// App repos often already use dist/
|
|
176
|
+
// App repos often already use dist/ — keep docs output out of the way via config.outDir.
|
|
149
177
|
const outDir = coexisting ? 'dist-docs' : 'dist'
|
|
150
|
-
|
|
151
|
-
const
|
|
178
|
+
// One Cloudflare build command for every repo (npm/pnpm/yarn). Reads docs.config.json.
|
|
179
|
+
const buildCommand = 'npx inovus-docs build'
|
|
180
|
+
const pm = await detectPackageManager(root, existingPkg)
|
|
181
|
+
const localInstall = pm === 'yarn' ? 'yarn' : pm === 'bun' ? 'bun install' : `${pm} install`
|
|
182
|
+
const localDev = runScript(pm, 'docs:dev')
|
|
152
183
|
|
|
153
184
|
const hasAppWrangler =
|
|
154
185
|
(await fileExists(path.join(root, 'wrangler.jsonc'))) ||
|
|
@@ -160,19 +191,39 @@ export async function init({ root = process.cwd(), flags = {} } = {}) {
|
|
|
160
191
|
const skipped = []
|
|
161
192
|
const notes = []
|
|
162
193
|
|
|
163
|
-
// docs.config.json (
|
|
194
|
+
// docs.config.json — create or merge additive fields (never wipe an existing file)
|
|
164
195
|
const cfgPath = path.join(root, 'docs.config.json')
|
|
165
196
|
if ((await fileExists(cfgPath)) && !force) {
|
|
166
|
-
|
|
197
|
+
const cfg = (await readJsonIfExists(cfgPath)) || {}
|
|
198
|
+
let changed = false
|
|
199
|
+
if (content && !cfg.content) {
|
|
200
|
+
cfg.content = content
|
|
201
|
+
changed = true
|
|
202
|
+
}
|
|
203
|
+
if (coexisting && !cfg.outDir) {
|
|
204
|
+
cfg.outDir = outDir
|
|
205
|
+
changed = true
|
|
206
|
+
}
|
|
207
|
+
if (!cfg.theme) {
|
|
208
|
+
cfg.theme = 'totum'
|
|
209
|
+
changed = true
|
|
210
|
+
}
|
|
211
|
+
if (changed) {
|
|
212
|
+
await fs.writeFile(cfgPath, JSON.stringify(cfg, null, 2) + '\n')
|
|
213
|
+
created.push('docs.config.json (updated)')
|
|
214
|
+
} else {
|
|
215
|
+
skipped.push('docs.config.json')
|
|
216
|
+
}
|
|
167
217
|
} else {
|
|
168
218
|
const cfg = { title, description: '', visibility, theme: 'totum' }
|
|
169
219
|
if (repoUrl) cfg.repo = repoUrl
|
|
170
220
|
if (content) cfg.content = content
|
|
221
|
+
if (outDir !== 'dist') cfg.outDir = outDir
|
|
171
222
|
await fs.writeFile(cfgPath, JSON.stringify(cfg, null, 2) + '\n')
|
|
172
223
|
created.push('docs.config.json')
|
|
173
224
|
}
|
|
174
225
|
|
|
175
|
-
// Wrangler config — never overwrite an app's wrangler unless --force on
|
|
226
|
+
// Wrangler config — never overwrite an app's wrangler unless --force on our docs file
|
|
176
227
|
const wranglerPath = path.join(root, wranglerFile)
|
|
177
228
|
if ((await fileExists(wranglerPath)) && !force) {
|
|
178
229
|
skipped.push(wranglerFile)
|
|
@@ -190,18 +241,18 @@ export async function init({ root = process.cwd(), flags = {} } = {}) {
|
|
|
190
241
|
const pkgPath = path.join(root, 'package.json')
|
|
191
242
|
const pkg = existingPkg ? { ...existingPkg } : { name, private: true }
|
|
192
243
|
pkg.scripts = { ...(pkg.scripts || {}) }
|
|
193
|
-
|
|
194
|
-
pkg.scripts['docs:
|
|
195
|
-
|
|
244
|
+
// Local convenience scripts (CF uses `npx inovus-docs build` — not these names).
|
|
245
|
+
pkg.scripts['docs:build'] = 'inovus-docs build'
|
|
246
|
+
pkg.scripts['docs:dev'] = 'inovus-docs dev'
|
|
196
247
|
if (!pkg.scripts.build) {
|
|
197
|
-
pkg.scripts.build = '
|
|
248
|
+
pkg.scripts.build = 'inovus-docs build'
|
|
198
249
|
} else {
|
|
199
250
|
notes.push(
|
|
200
|
-
`Left existing "build" script alone
|
|
251
|
+
`Left existing "build" script alone (app build). Cloudflare must use: ${buildCommand}`
|
|
201
252
|
)
|
|
202
253
|
}
|
|
203
254
|
if (!pkg.scripts.dev) {
|
|
204
|
-
pkg.scripts.dev = '
|
|
255
|
+
pkg.scripts.dev = 'inovus-docs dev'
|
|
205
256
|
}
|
|
206
257
|
if (wranglerFile !== 'wrangler.jsonc' && !pkg.scripts['docs:deploy']) {
|
|
207
258
|
pkg.scripts['docs:deploy'] = `wrangler deploy -c ${wranglerFile}`
|
|
@@ -214,6 +265,17 @@ export async function init({ root = process.cwd(), flags = {} } = {}) {
|
|
|
214
265
|
await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
|
|
215
266
|
created.push(existingPkg ? 'package.json (updated)' : 'package.json')
|
|
216
267
|
|
|
268
|
+
// Update the lockfile so Cloudflare --frozen-lockfile can install the engine.
|
|
269
|
+
try {
|
|
270
|
+
console.log(`Updating lockfile (${localInstall})…`)
|
|
271
|
+
execSync(localInstall, { cwd: root, stdio: 'inherit', shell: true })
|
|
272
|
+
created.push('lockfile (updated)')
|
|
273
|
+
} catch {
|
|
274
|
+
notes.push(
|
|
275
|
+
`Could not run "${localInstall}" automatically. Run it yourself and commit the lockfile before deploying.`
|
|
276
|
+
)
|
|
277
|
+
}
|
|
278
|
+
|
|
217
279
|
// .node-version — only for greenfield docs repos (apps often pin their own Node).
|
|
218
280
|
const nodeVerPath = path.join(root, '.node-version')
|
|
219
281
|
if (!coexisting && !(await fileExists(nodeVerPath))) {
|
|
@@ -235,7 +297,6 @@ export async function init({ root = process.cwd(), flags = {} } = {}) {
|
|
|
235
297
|
const rel = path.join(contentRel === '.' ? '' : contentRel, 'README.md').replace(/^\//, '')
|
|
236
298
|
const readmePath = path.join(root, rel || 'README.md')
|
|
237
299
|
await fs.mkdir(path.dirname(readmePath), { recursive: true })
|
|
238
|
-
// Don't overwrite an existing root README when content is "."
|
|
239
300
|
if (!(await fileExists(readmePath))) {
|
|
240
301
|
await fs.writeFile(readmePath, starterReadme(title))
|
|
241
302
|
created.push((rel || 'README.md').split(path.sep).join('/'))
|
|
@@ -245,7 +306,6 @@ export async function init({ root = process.cwd(), flags = {} } = {}) {
|
|
|
245
306
|
}
|
|
246
307
|
}
|
|
247
308
|
|
|
248
|
-
const buildCommand = 'npm install && npm run docs:build'
|
|
249
309
|
const deployCommand =
|
|
250
310
|
wranglerFile === 'wrangler.jsonc'
|
|
251
311
|
? 'npx wrangler deploy'
|
|
@@ -265,6 +325,9 @@ export async function init({ root = process.cwd(), flags = {} } = {}) {
|
|
|
265
325
|
wranglerFile,
|
|
266
326
|
buildCommand,
|
|
267
327
|
deployCommand,
|
|
328
|
+
localInstall,
|
|
329
|
+
localDev,
|
|
330
|
+
packageManager: pm,
|
|
268
331
|
coexisting
|
|
269
332
|
}
|
|
270
333
|
}
|
package/src/watch.js
CHANGED
|
@@ -2,7 +2,7 @@ import { watch } from 'node:fs'
|
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import { build } from './build.js'
|
|
4
4
|
|
|
5
|
-
const IGNORED = /(^|[\\/])(node_modules|\.git|dist)([\\/]|$)/
|
|
5
|
+
const IGNORED = /(^|[\\/])(node_modules|\.git|dist-docs|dist)([\\/]|$)/
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Rebuild on any change under the content directory. Debounced so a burst of
|
|
@@ -12,7 +12,7 @@ const IGNORED = /(^|[\\/])(node_modules|\.git|dist)([\\/]|$)/
|
|
|
12
12
|
*/
|
|
13
13
|
export function watchAndRebuild({ root, inDir, outDir, theme }) {
|
|
14
14
|
const watchPath = inDir === '.' ? root : path.join(root, inDir)
|
|
15
|
-
const outBase = path.basename(outDir)
|
|
15
|
+
const outBase = path.basename(outDir || 'dist')
|
|
16
16
|
let timer = null
|
|
17
17
|
let building = false
|
|
18
18
|
|
|
@@ -23,7 +23,12 @@ export function watchAndRebuild({ root, inDir, outDir, theme }) {
|
|
|
23
23
|
if (building) return
|
|
24
24
|
building = true
|
|
25
25
|
try {
|
|
26
|
-
const { pageCount } = await build({
|
|
26
|
+
const { pageCount } = await build({
|
|
27
|
+
root,
|
|
28
|
+
inDir: inDir === '.' ? '.' : inDir,
|
|
29
|
+
outDir,
|
|
30
|
+
theme
|
|
31
|
+
})
|
|
27
32
|
console.log(`↻ Rebuilt ${pageCount} page(s)`)
|
|
28
33
|
} catch (err) {
|
|
29
34
|
console.error('Build failed:', err.message)
|