@inditextech/docouture-cli 0.1.0-SNAPSHOT.40.1
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/build/bin.js +321 -0
- package/build/commands/build.js +22 -0
- package/build/commands/completion.js +69 -0
- package/build/commands/dev.js +67 -0
- package/build/commands/doctor.js +156 -0
- package/build/commands/eject.js +71 -0
- package/build/commands/new.js +512 -0
- package/build/commands/publish.js +111 -0
- package/build/commands/teardown.js +66 -0
- package/build/commands/upgrade.js +156 -0
- package/build/commands/version.js +60 -0
- package/build/lib/agents-md.js +67 -0
- package/build/lib/antora-log.js +36 -0
- package/build/lib/antora-yml.js +47 -0
- package/build/lib/args.js +26 -0
- package/build/lib/cli-context.js +17 -0
- package/build/lib/cli-info.js +20 -0
- package/build/lib/config-resolver.js +23 -0
- package/build/lib/copy-template.js +162 -0
- package/build/lib/debug-log.js +21 -0
- package/build/lib/detect-package-manager.js +79 -0
- package/build/lib/dev-server.js +337 -0
- package/build/lib/doctor-checks.js +240 -0
- package/build/lib/global-flags.js +31 -0
- package/build/lib/kroki-compose.js +54 -0
- package/build/lib/playbook-yml.js +90 -0
- package/build/lib/repo-root.js +21 -0
- package/build/lib/run-script.js +50 -0
- package/build/lib/theme.js +51 -0
- package/build/templates/agent-support/.claude/skills/docs-internals/SKILL.md +53 -0
- package/build/templates/agent-support/.claude/skills/docs-internals/reference/antora-extensions.md +54 -0
- package/build/templates/agent-support/.claude/skills/docs-internals/reference/naming.md +41 -0
- package/build/templates/agent-support/.claude/skills/docs-internals/reference/page-patterns.md +238 -0
- package/build/templates/agent-support/.claude/skills/docs-versioning.versioned/SKILL.md +37 -0
- package/build/templates/agent-support/.claude/skills/docs-versioning.versioned/reference/releasing.md +106 -0
- package/build/templates/agent-support/.claude/skills/documenting-your-repo/SKILL.md +55 -0
- package/build/templates/agent-support/.claude/skills/documenting-your-repo/reference/content-sourcing.md +55 -0
- package/build/templates/agent-support/.claude/skills/documenting-your-repo/reference/maintenance-loop.md +68 -0
- package/build/templates/agent-support/.claude/skills/documenting-your-repo/reference/structure-planning.md +102 -0
- package/build/templates/agent-support/.claude/skills/writing-docs-pages/SKILL.md +112 -0
- package/build/templates/agent-support/.claude/skills/writing-docs-pages/reference/docouture-blocks.md +170 -0
- package/build/templates/agent-support/.claude/skills/writing-docs-pages/reference/language-basics.md +402 -0
- package/build/templates/agent-support/.opencode/skills/docs-internals/SKILL.md +53 -0
- package/build/templates/agent-support/.opencode/skills/docs-internals/reference/antora-extensions.md +54 -0
- package/build/templates/agent-support/.opencode/skills/docs-internals/reference/naming.md +41 -0
- package/build/templates/agent-support/.opencode/skills/docs-internals/reference/page-patterns.md +238 -0
- package/build/templates/agent-support/.opencode/skills/docs-versioning.versioned/SKILL.md +37 -0
- package/build/templates/agent-support/.opencode/skills/docs-versioning.versioned/reference/releasing.md +106 -0
- package/build/templates/agent-support/.opencode/skills/documenting-your-repo/SKILL.md +55 -0
- package/build/templates/agent-support/.opencode/skills/documenting-your-repo/reference/content-sourcing.md +55 -0
- package/build/templates/agent-support/.opencode/skills/documenting-your-repo/reference/maintenance-loop.md +68 -0
- package/build/templates/agent-support/.opencode/skills/documenting-your-repo/reference/structure-planning.md +102 -0
- package/build/templates/agent-support/.opencode/skills/writing-docs-pages/SKILL.md +112 -0
- package/build/templates/agent-support/.opencode/skills/writing-docs-pages/reference/docouture-blocks.md +170 -0
- package/build/templates/agent-support/.opencode/skills/writing-docs-pages/reference/language-basics.md +402 -0
- package/build/templates/agent-support/AGENTS.md +72 -0
- package/build/templates/starter/antora-playbook.local.yml +99 -0
- package/build/templates/starter/antora-playbook.versioned.yml +167 -0
- package/build/templates/starter/antora-playbook.yml +189 -0
- package/build/templates/starter/gitignore +3 -0
- package/build/templates/starter/package.json +37 -0
- package/build/templates/starter/scripts/check-links.mjs +178 -0
- package/build/templates/starter/src/antora.yml +85 -0
- package/build/templates/starter/src/modules/ROOT/images/card-placeholder.png +0 -0
- package/build/templates/starter/src/modules/ROOT/images/feature-placeholder-dark.png +0 -0
- package/build/templates/starter/src/modules/ROOT/images/feature-placeholder.png +0 -0
- package/build/templates/starter/src/modules/ROOT/images/hero-placeholder.png +0 -0
- package/build/templates/starter/src/modules/ROOT/pages/index.adoc +124 -0
- package/build/templates/starter/src/modules/main/nav.adoc +19 -0
- package/build/templates/starter/src/modules/main/pages/app.adoc +5 -0
- package/build/templates/starter/src/modules/main/pages/architecture.adoc +6 -0
- package/build/templates/starter/src/modules/main/pages/changelog/index.adoc +9 -0
- package/build/templates/starter/src/modules/main/pages/components.adoc +701 -0
- package/build/templates/starter/src/modules/main/pages/contributing.adoc +6 -0
- package/build/templates/starter/src/modules/main/pages/getting-started.adoc +169 -0
- package/build/templates/starter/src/modules/main/pages/glossary.adoc +8 -0
- package/build/templates/starter/src/modules/main/pages/index.adoc +9 -0
- package/build/templates/starter/src/modules/main/pages/prerequisites.adoc +55 -0
- package/build/templates/starter/src/modules/main/pages/quickstart.adoc +21 -0
- package/build/templates/starter/src/modules/main/pages/release-notes/index.adoc +9 -0
- package/build/templates/starter/src/modules/main/pages/requirements.adoc +8 -0
- package/build/templates/starter/src/modules/main/pages/roadmap.adoc +4 -0
- package/build/templates/starter/src/modules/main/pages/structure.adoc +5 -0
- package/build/templates/starter/src/release-version.versioned +1 -0
- package/build/templates/starter/supplemental-ui/favicon.ico +0 -0
- package/build/templates/starter/supplemental-ui/product-logo-dark.png +0 -0
- package/build/templates/starter/supplemental-ui/product-logo.png +0 -0
- package/build/templates/workflows/docouture-kroki-cache-warm.yml +101 -0
- package/build/templates/workflows/docouture-pr-verify.yml +120 -0
- package/build/templates/workflows/docouture-publish-prerelease.yml +122 -0
- package/build/templates/workflows/docouture-publish.yml +138 -0
- package/build/templates/workflows/docouture-release-preview.yml +198 -0
- package/build/templates/workflows/docouture-release.yml +414 -0
- package/package.json +38 -0
package/build/bin.js
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
import { runNew } from './commands/new.js';
|
|
4
|
+
import { runVersion } from './commands/version.js';
|
|
5
|
+
import { runDev } from './commands/dev.js';
|
|
6
|
+
import { runBuild } from './commands/build.js';
|
|
7
|
+
import { runDoctor } from './commands/doctor.js';
|
|
8
|
+
import { runPublish } from './commands/publish.js';
|
|
9
|
+
import { runUpgrade } from './commands/upgrade.js';
|
|
10
|
+
import { runEject } from './commands/eject.js';
|
|
11
|
+
import { runTeardown } from './commands/teardown.js';
|
|
12
|
+
import { runCompletion } from './commands/completion.js';
|
|
13
|
+
import { readCliInfo } from './lib/cli-info.js';
|
|
14
|
+
import { setContext } from './lib/cli-context.js';
|
|
15
|
+
import { theme } from './lib/theme.js';
|
|
16
|
+
import { extractGlobalFlags } from './lib/global-flags.js';
|
|
17
|
+
const COMMAND_ORDER = [
|
|
18
|
+
'new',
|
|
19
|
+
'version',
|
|
20
|
+
'dev',
|
|
21
|
+
'build',
|
|
22
|
+
'publish',
|
|
23
|
+
'doctor',
|
|
24
|
+
'upgrade',
|
|
25
|
+
'eject',
|
|
26
|
+
'teardown',
|
|
27
|
+
'completion',
|
|
28
|
+
];
|
|
29
|
+
const COMMAND_INFO = {
|
|
30
|
+
new: {
|
|
31
|
+
usageLine: 'new <name>',
|
|
32
|
+
summary: 'Scaffold a new documentation site',
|
|
33
|
+
help: `Usage:
|
|
34
|
+
docouture new <name> [--dir <path>] [--title <title>] [--mode standalone|versioned] [--pm npm|pnpm] [--yes]
|
|
35
|
+
|
|
36
|
+
Scaffold an Antora documentation site into docs/ (and its workflows into
|
|
37
|
+
.github/workflows/), plus AGENTS.md and agent skills (.opencode/skills/,
|
|
38
|
+
.claude/skills/) into the root of an existing git repository. --dir/cwd
|
|
39
|
+
can be anywhere inside that repository — the actual top-level is found
|
|
40
|
+
automatically. If any workflows, skills or a docouture-managed AGENTS.md
|
|
41
|
+
section already exist, prompts to overwrite them (refuses outright when
|
|
42
|
+
not running interactively). Prompts interactively for anything not given
|
|
43
|
+
as a flag when run in a terminal (pass --yes to always use defaults, and
|
|
44
|
+
to never prompt to overwrite, instead).
|
|
45
|
+
|
|
46
|
+
Options:
|
|
47
|
+
--dir <path> Repository root to scaffold into (default: cwd, or its enclosing repo)
|
|
48
|
+
--title <title> Site title (default: title-cased from <name>)
|
|
49
|
+
--mode <mode> 'standalone' (default) or 'versioned'
|
|
50
|
+
--pm <pm> 'npm' or 'pnpm' (default: auto-detected from a lockfile/packageManager
|
|
51
|
+
field, or how docouture itself was invoked)
|
|
52
|
+
--yes Skip the interactive wizard, use defaults for anything unset
|
|
53
|
+
`,
|
|
54
|
+
},
|
|
55
|
+
version: {
|
|
56
|
+
usageLine: 'version <value>',
|
|
57
|
+
summary: 'Set the version recorded in docs/antora.yml',
|
|
58
|
+
help: `Usage:
|
|
59
|
+
docouture version <value> [--dir <path>] [--file <path>] [--prerelease | --stable]
|
|
60
|
+
|
|
61
|
+
Set the version recorded in docs/antora.yml.
|
|
62
|
+
|
|
63
|
+
Options:
|
|
64
|
+
--dir <path> Repository root (default: cwd, or its enclosing repo)
|
|
65
|
+
--file <path> antora.yml path directly, overrides --dir
|
|
66
|
+
--prerelease Mark this version as a prerelease
|
|
67
|
+
--stable Mark this version as stable (mutually exclusive with --prerelease)
|
|
68
|
+
`,
|
|
69
|
+
},
|
|
70
|
+
dev: {
|
|
71
|
+
usageLine: 'dev',
|
|
72
|
+
summary: 'Build and serve the site with live reload',
|
|
73
|
+
help: `Usage:
|
|
74
|
+
docouture dev [--port <port>] [--dir <path>]
|
|
75
|
+
|
|
76
|
+
Build the site and serve it with live reload, rebuilding on every
|
|
77
|
+
change to docs/ or antora-playbook.yml. --dir is the repository root
|
|
78
|
+
(the parent of docs/), same as new/version — not the site directory.
|
|
79
|
+
|
|
80
|
+
Options:
|
|
81
|
+
--port <port> Port to listen on (default: 5000)
|
|
82
|
+
--dir <path> Repository root (default: cwd, or its enclosing repo)
|
|
83
|
+
`,
|
|
84
|
+
},
|
|
85
|
+
build: {
|
|
86
|
+
usageLine: 'build',
|
|
87
|
+
summary: 'Build the site once',
|
|
88
|
+
help: `Usage:
|
|
89
|
+
docouture build [--dir <path>]
|
|
90
|
+
|
|
91
|
+
Build the site once: a thin wrapper around the site's own 'npm run
|
|
92
|
+
build' (antora --fetch antora-playbook.yml).
|
|
93
|
+
|
|
94
|
+
Options:
|
|
95
|
+
--dir <path> Repository root (default: cwd, or its enclosing repo)
|
|
96
|
+
`,
|
|
97
|
+
},
|
|
98
|
+
publish: {
|
|
99
|
+
usageLine: 'publish <target>',
|
|
100
|
+
summary: 'Publish the already-built site',
|
|
101
|
+
help: `Usage:
|
|
102
|
+
docouture publish <target> [--dir <path>] [--<option> <value> ...]
|
|
103
|
+
|
|
104
|
+
Publish the already-built site (output.dir in antora-playbook.yml,
|
|
105
|
+
default build/site — run 'docouture build' first) using the
|
|
106
|
+
'@inditextech/docouture-publish-<target>' driver, e.g. 'gh-pages'. That
|
|
107
|
+
package must be a devDependency of the site itself. Options come
|
|
108
|
+
from docs/package.json's own "docouture".publish.<target> object,
|
|
109
|
+
overridden by any --flags given here.
|
|
110
|
+
|
|
111
|
+
Options:
|
|
112
|
+
--dir <path> Repository root (default: cwd, or its enclosing repo)
|
|
113
|
+
--<option> <value> Forwarded to the publish driver as one of its own options
|
|
114
|
+
--user-name/--user-email Combined into a nested {user:{name,email}} option
|
|
115
|
+
|
|
116
|
+
Example:
|
|
117
|
+
docouture publish gh-pages --branch gh-pages
|
|
118
|
+
`,
|
|
119
|
+
},
|
|
120
|
+
doctor: {
|
|
121
|
+
usageLine: 'doctor',
|
|
122
|
+
summary: 'Check environment and site health',
|
|
123
|
+
help: `Usage:
|
|
124
|
+
docouture doctor [--dir <path>] [--json]
|
|
125
|
+
|
|
126
|
+
Check that the environment and site configuration are healthy: Node
|
|
127
|
+
version, the four names that must agree (component name, start page,
|
|
128
|
+
content path, package name), git history, that antora is installed,
|
|
129
|
+
and (advisory only) whether AGENTS.md and the scaffolded skills are
|
|
130
|
+
still present.
|
|
131
|
+
|
|
132
|
+
Options:
|
|
133
|
+
--dir <path> Repository root (default: cwd, or its enclosing repo)
|
|
134
|
+
--json Print a machine-readable report to stdout instead
|
|
135
|
+
`,
|
|
136
|
+
},
|
|
137
|
+
upgrade: {
|
|
138
|
+
usageLine: 'upgrade',
|
|
139
|
+
summary: 'Re-sync workflows and agent support files',
|
|
140
|
+
help: `Usage:
|
|
141
|
+
docouture upgrade [--dir <path>] [--title <title>] [--dry-run]
|
|
142
|
+
|
|
143
|
+
Re-sync an already-scaffolded repository's .github/workflows/ and
|
|
144
|
+
agent support files (AGENTS.md, .opencode/skills/, .claude/skills/)
|
|
145
|
+
from the CLI's current templates — --dir/cwd can be anywhere inside
|
|
146
|
+
that repository, the actual top-level is found automatically.
|
|
147
|
+
Workflows and skills are fully overwritten; AGENTS.md is merged
|
|
148
|
+
instead — only docouture' own managed section is replaced, anything a
|
|
149
|
+
human added around it survives. Does not touch docs/. --dry-run lists
|
|
150
|
+
what would be written without changing anything.
|
|
151
|
+
|
|
152
|
+
Options:
|
|
153
|
+
--dir <path> Repository root (default: cwd, or its enclosing repo)
|
|
154
|
+
--title <title> Override the title read back from docs/antora.yml
|
|
155
|
+
--dry-run List what would be written without changing anything
|
|
156
|
+
`,
|
|
157
|
+
},
|
|
158
|
+
eject: {
|
|
159
|
+
usageLine: 'eject <target>',
|
|
160
|
+
summary: 'Copy a bundled default file out for customization',
|
|
161
|
+
help: `Usage:
|
|
162
|
+
docouture eject <target> [--dir <path>]
|
|
163
|
+
|
|
164
|
+
Copy a bundled default file out into docs/ for local customization.
|
|
165
|
+
'kroki': docs/kroki-compose.yml, the docker compose definition
|
|
166
|
+
@inditextech/docouture-antora-extensions' kroki-prewarm.js starts
|
|
167
|
+
automatically when a build needs Kroki (kroki-enabled: true) and finds
|
|
168
|
+
nothing already running — auto-detected and preferred over the
|
|
169
|
+
bundled default once it exists. Refuses to overwrite an existing file.
|
|
170
|
+
|
|
171
|
+
Options:
|
|
172
|
+
--dir <path> Repository root (default: cwd, or its enclosing repo)
|
|
173
|
+
`,
|
|
174
|
+
},
|
|
175
|
+
teardown: {
|
|
176
|
+
usageLine: 'teardown <target>',
|
|
177
|
+
summary: 'Stop a service docouture started for you',
|
|
178
|
+
help: `Usage:
|
|
179
|
+
docouture teardown <target> [--dir <path>]
|
|
180
|
+
|
|
181
|
+
Stop a service docouture started for you. 'kroki': runs 'docker compose
|
|
182
|
+
down' against whichever kroki-compose.yml is actually in effect (an
|
|
183
|
+
ejected docs/kroki-compose.yml if you have one, else the bundled
|
|
184
|
+
default) — the manual counterpart to kroki-prewarm.js's own auto-start,
|
|
185
|
+
which never stops it itself.
|
|
186
|
+
|
|
187
|
+
Options:
|
|
188
|
+
--dir <path> Repository root (default: cwd, or its enclosing repo)
|
|
189
|
+
`,
|
|
190
|
+
},
|
|
191
|
+
completion: {
|
|
192
|
+
usageLine: 'completion <shell>',
|
|
193
|
+
summary: 'Print a shell completion script (bash|zsh)',
|
|
194
|
+
help: `Usage:
|
|
195
|
+
docouture completion <bash|zsh>
|
|
196
|
+
|
|
197
|
+
Print a shell completion script for docouture to stdout.
|
|
198
|
+
|
|
199
|
+
Examples:
|
|
200
|
+
eval "$(docouture completion bash)"
|
|
201
|
+
docouture completion zsh > "\${fpath[1]}/_docouture"
|
|
202
|
+
`,
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
// Top-level equivalent of each COMMAND_INFO entry's `summary` — printed as
|
|
206
|
+
// the title of the bare/--help screen, same 'docouture — <summary>' shape as
|
|
207
|
+
// 'docouture <command> --help' uses for a single command.
|
|
208
|
+
const CLI_SUMMARY = 'Scaffold, build, and publish Antora documentation sites';
|
|
209
|
+
// Left column width of the command table below, including the 2-space
|
|
210
|
+
// indent — wide enough for the longest usageLine ('completion <shell>').
|
|
211
|
+
const COMMAND_COLUMN_WIDTH = 22;
|
|
212
|
+
const USAGE = `Usage: docouture <command> [options]
|
|
213
|
+
|
|
214
|
+
Commands:
|
|
215
|
+
${COMMAND_ORDER.map((cmd) => {
|
|
216
|
+
const info = COMMAND_INFO[cmd];
|
|
217
|
+
return ` ${info.usageLine}`.padEnd(COMMAND_COLUMN_WIDTH) + info.summary;
|
|
218
|
+
}).join('\n')}
|
|
219
|
+
|
|
220
|
+
Global options:
|
|
221
|
+
-h, --help Show help (pass after a command for command-specific help)
|
|
222
|
+
-v, --version Print the installed @inditextech/docouture-cli version and exit
|
|
223
|
+
--json Machine-readable output where supported (currently: doctor)
|
|
224
|
+
--verbose Print extra diagnostic detail to stderr (same as DEBUG=docouture)
|
|
225
|
+
--no-color Disable coloured output regardless of TTY detection
|
|
226
|
+
|
|
227
|
+
Run 'docouture <command> --help' for command-specific usage and options.
|
|
228
|
+
`;
|
|
229
|
+
const RUNNERS = {
|
|
230
|
+
new: runNew,
|
|
231
|
+
version: runVersion,
|
|
232
|
+
dev: runDev,
|
|
233
|
+
build: runBuild,
|
|
234
|
+
publish: runPublish,
|
|
235
|
+
doctor: runDoctor,
|
|
236
|
+
upgrade: runUpgrade,
|
|
237
|
+
eject: runEject,
|
|
238
|
+
teardown: runTeardown,
|
|
239
|
+
completion: async (argv) => runCompletion(argv),
|
|
240
|
+
};
|
|
241
|
+
// completion's whole output must be shell-consumable on stdout (piped to a
|
|
242
|
+
// file, or `eval`'d directly on every shell startup if a user wires it into
|
|
243
|
+
// their rc file) — the banner is harmless there too, since it only ever
|
|
244
|
+
// goes to stderr, but printing it on every single new shell would still be
|
|
245
|
+
// needless noise for something meant to be sourced silently.
|
|
246
|
+
const QUIET_COMMANDS = new Set(['completion']);
|
|
247
|
+
// build/bin.js -> package root, 1 level up — see readCliInfo's own comment.
|
|
248
|
+
async function banner() {
|
|
249
|
+
const { name, version } = await readCliInfo(import.meta.url, 1);
|
|
250
|
+
return `${theme.bold(theme.info(name))} ${theme.dim(`v${version}`)}`;
|
|
251
|
+
}
|
|
252
|
+
// Recognises the three global flags anywhere in argv (before or after the
|
|
253
|
+
// command name — `docouture --verbose dev` and `docouture dev --verbose` both
|
|
254
|
+
// work), strips them out, and returns what's left for command dispatch —
|
|
255
|
+
// see lib/global-flags.ts.
|
|
256
|
+
async function main() {
|
|
257
|
+
const { json, verbose, noColor, rest: argv } = extractGlobalFlags(process.argv.slice(2));
|
|
258
|
+
setContext({ json, verbose, noColor });
|
|
259
|
+
const [command, ...rest] = argv;
|
|
260
|
+
if (command === '--version' || command === '-v') {
|
|
261
|
+
console.log(await banner());
|
|
262
|
+
return 0;
|
|
263
|
+
}
|
|
264
|
+
// Bare invocation prints help and succeeds — mirrors git/npm's own
|
|
265
|
+
// convention of treating "show me what you can do" as a legitimate,
|
|
266
|
+
// successful outcome rather than an error, unlike a genuinely unknown
|
|
267
|
+
// command below.
|
|
268
|
+
if (command === undefined || command === '--help' || command === '-h' || command === 'help') {
|
|
269
|
+
console.log(await banner());
|
|
270
|
+
console.log('');
|
|
271
|
+
console.log(theme.bold(`docouture — ${CLI_SUMMARY}`));
|
|
272
|
+
console.log('');
|
|
273
|
+
console.log(USAGE);
|
|
274
|
+
return 0;
|
|
275
|
+
}
|
|
276
|
+
if (command in COMMAND_INFO && (rest[0] === '--help' || rest[0] === '-h')) {
|
|
277
|
+
const info = COMMAND_INFO[command];
|
|
278
|
+
console.log(await banner());
|
|
279
|
+
console.log('');
|
|
280
|
+
console.log(theme.bold(`docouture ${command} — ${info.summary}`));
|
|
281
|
+
console.log('');
|
|
282
|
+
console.log(info.help);
|
|
283
|
+
return 0;
|
|
284
|
+
}
|
|
285
|
+
if (!(command in RUNNERS)) {
|
|
286
|
+
console.error(`unknown command: '${command}'\n`);
|
|
287
|
+
console.error(await banner());
|
|
288
|
+
console.error('');
|
|
289
|
+
console.error(theme.bold(`docouture — ${CLI_SUMMARY}`));
|
|
290
|
+
console.error('');
|
|
291
|
+
console.error(USAGE);
|
|
292
|
+
return 1;
|
|
293
|
+
}
|
|
294
|
+
// Printed once per invocation, to stderr — so it never lands in piped or
|
|
295
|
+
// parsed stdout — right before the command actually runs, rather than
|
|
296
|
+
// only on --version/--help/error paths as before. Suppressed under
|
|
297
|
+
// --json (stdout is promised clean there; a stray stderr banner is still
|
|
298
|
+
// needless noise for a script capturing combined output) and for
|
|
299
|
+
// completion (see QUIET_COMMANDS' own comment). The title line repeats
|
|
300
|
+
// the same '<command> — <summary>' shown on --help, so a plain run gives
|
|
301
|
+
// just as much "what is this" context as asking for help would — followed
|
|
302
|
+
// by a blank line so the command's own output never runs straight on
|
|
303
|
+
// from the banner/title.
|
|
304
|
+
if (!json && !QUIET_COMMANDS.has(command)) {
|
|
305
|
+
const info = COMMAND_INFO[command];
|
|
306
|
+
console.error(await banner());
|
|
307
|
+
console.error('');
|
|
308
|
+
console.error(theme.bold(`docouture ${command} — ${info.summary}`));
|
|
309
|
+
console.error('');
|
|
310
|
+
}
|
|
311
|
+
return RUNNERS[command](rest);
|
|
312
|
+
}
|
|
313
|
+
main()
|
|
314
|
+
.then((code) => {
|
|
315
|
+
process.exitCode = code;
|
|
316
|
+
})
|
|
317
|
+
.catch((err) => {
|
|
318
|
+
console.error(err instanceof Error ? err.stack : String(err));
|
|
319
|
+
process.exitCode = 1;
|
|
320
|
+
});
|
|
321
|
+
//# sourceMappingURL=bin.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { parseArgs } from '../lib/args.js';
|
|
4
|
+
import { ANTORA_LOG_LEVEL_ARGS } from '../lib/antora-log.js';
|
|
5
|
+
import { exists } from '../lib/copy-template.js';
|
|
6
|
+
import { findRepoRoot } from '../lib/repo-root.js';
|
|
7
|
+
import { runNpmScript } from '../lib/run-script.js';
|
|
8
|
+
export async function runBuild(argv) {
|
|
9
|
+
const { flags } = parseArgs(argv);
|
|
10
|
+
// --dir (or cwd) can be anywhere inside the repository — findRepoRoot
|
|
11
|
+
// walks up to the actual repository root, same as `new`/`version`/`doctor`.
|
|
12
|
+
const startDir = typeof flags.dir === 'string' ? resolve(flags.dir) : resolve(process.cwd());
|
|
13
|
+
const target = await findRepoRoot(startDir);
|
|
14
|
+
const siteRoot = join(target, 'docs');
|
|
15
|
+
if (!(await exists(join(siteRoot, 'package.json')))) {
|
|
16
|
+
console.error(`no package.json found at '${siteRoot}'`);
|
|
17
|
+
console.error('pass --dir <path> to a component root (the parent of docs/), or run docouture new first');
|
|
18
|
+
return 1;
|
|
19
|
+
}
|
|
20
|
+
return runNpmScript('build', { cwd: siteRoot, args: [...ANTORA_LOG_LEVEL_ARGS] });
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// `docouture completion <bash|zsh>` — prints a completion script to stdout (so
|
|
3
|
+
// `docouture completion bash > file` and `eval "$(docouture completion bash)"` both
|
|
4
|
+
// work); everything else this command has to say (usage errors) goes to
|
|
5
|
+
// stderr, same discipline as every other command. The candidate list is
|
|
6
|
+
// kept here as a literal array rather than derived from bin.ts's dispatch
|
|
7
|
+
// table, since introducing a shared "registry" module for nine commands
|
|
8
|
+
// that essentially never change is more machinery than the problem needs —
|
|
9
|
+
// see COMMANDS' own comment for what keeps it honest.
|
|
10
|
+
// Kept in sync with bin.ts's RUNNERS keys by test (completion.spec.ts) —
|
|
11
|
+
// not by import, since bin.ts is the entrypoint and importing it back here
|
|
12
|
+
// would run its top-level `main()` invocation.
|
|
13
|
+
export const COMMANDS = [
|
|
14
|
+
'new',
|
|
15
|
+
'version',
|
|
16
|
+
'dev',
|
|
17
|
+
'build',
|
|
18
|
+
'publish',
|
|
19
|
+
'doctor',
|
|
20
|
+
'upgrade',
|
|
21
|
+
'eject',
|
|
22
|
+
'teardown',
|
|
23
|
+
'completion',
|
|
24
|
+
];
|
|
25
|
+
function bashScript() {
|
|
26
|
+
const words = COMMANDS.join(' ');
|
|
27
|
+
return `# docouture bash completion
|
|
28
|
+
#
|
|
29
|
+
# Install for the current session:
|
|
30
|
+
# eval "$(docouture completion bash)"
|
|
31
|
+
#
|
|
32
|
+
# Install permanently, e.g. on macOS with Homebrew's bash-completion@2:
|
|
33
|
+
# docouture completion bash > "$(brew --prefix)/etc/bash_completion.d/docouture"
|
|
34
|
+
_docouture_completions() {
|
|
35
|
+
local cur=\${COMP_WORDS[COMP_CWORD]}
|
|
36
|
+
COMPREPLY=($(compgen -W "${words}" -- "$cur"))
|
|
37
|
+
}
|
|
38
|
+
complete -F _docouture_completions docouture
|
|
39
|
+
`;
|
|
40
|
+
}
|
|
41
|
+
function zshScript() {
|
|
42
|
+
const words = COMMANDS.map((c) => `'${c}'`).join(' ');
|
|
43
|
+
return `#compdef docouture
|
|
44
|
+
# docouture zsh completion
|
|
45
|
+
#
|
|
46
|
+
# Install by placing this on your $fpath, e.g.:
|
|
47
|
+
# docouture completion zsh > "\${fpath[1]}/_docouture"
|
|
48
|
+
_docouture() {
|
|
49
|
+
local -a commands
|
|
50
|
+
commands=(${words})
|
|
51
|
+
_describe 'docouture command' commands
|
|
52
|
+
}
|
|
53
|
+
_docouture
|
|
54
|
+
`;
|
|
55
|
+
}
|
|
56
|
+
export function runCompletion(argv) {
|
|
57
|
+
const shell = argv[0];
|
|
58
|
+
if (shell === 'bash') {
|
|
59
|
+
process.stdout.write(bashScript());
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
if (shell === 'zsh') {
|
|
63
|
+
process.stdout.write(zshScript());
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
console.error('usage: docouture completion <bash|zsh>');
|
|
67
|
+
return 1;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=completion.js.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { parseArgs } from '../lib/args.js';
|
|
5
|
+
import { exists } from '../lib/copy-template.js';
|
|
6
|
+
import { findRepoRoot } from '../lib/repo-root.js';
|
|
7
|
+
import { startDevServer } from '../lib/dev-server.js';
|
|
8
|
+
export async function runDev(argv) {
|
|
9
|
+
const { flags } = parseArgs(argv);
|
|
10
|
+
// --dir (or cwd) can be anywhere inside the repository — findRepoRoot
|
|
11
|
+
// walks up to the actual repository root, same as `new`/`version`/`doctor`.
|
|
12
|
+
const startDir = typeof flags.dir === 'string' ? resolve(flags.dir) : resolve(process.cwd());
|
|
13
|
+
const target = await findRepoRoot(startDir);
|
|
14
|
+
const siteRoot = join(target, 'docs');
|
|
15
|
+
if (!(await exists(join(siteRoot, 'antora-playbook.local.yml')))) {
|
|
16
|
+
console.error(`no antora-playbook.local.yml found at '${siteRoot}'`);
|
|
17
|
+
console.error('pass --dir <path> to a component root (the parent of docs/), or run docouture new first');
|
|
18
|
+
return 1;
|
|
19
|
+
}
|
|
20
|
+
let port;
|
|
21
|
+
if (typeof flags.port === 'string') {
|
|
22
|
+
port = Number(flags.port);
|
|
23
|
+
if (!Number.isInteger(port) || port <= 0) {
|
|
24
|
+
console.error(`invalid --port: '${flags.port}'`);
|
|
25
|
+
return 1;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Spinner covers only the initial build+listen wait, where the previous
|
|
29
|
+
// behaviour was otherwise silent for however long the first Antora build
|
|
30
|
+
// takes — ora auto-detects a non-TTY stderr (CI, piped output) and
|
|
31
|
+
// degrades to plain text on its own, so this stays quiet under
|
|
32
|
+
// automation without a separate flag. Once the server is up, log/logError
|
|
33
|
+
// below revert to plain per-rebuild lines exactly as before.
|
|
34
|
+
const spinner = ora({ text: 'Starting dev server…', stream: process.stderr }).start();
|
|
35
|
+
let ready = false;
|
|
36
|
+
const log = (msg) => {
|
|
37
|
+
if (ready)
|
|
38
|
+
console.error(`dev ${msg}`);
|
|
39
|
+
};
|
|
40
|
+
const logError = (msg) => {
|
|
41
|
+
if (ready)
|
|
42
|
+
console.error(`dev ${msg}`);
|
|
43
|
+
};
|
|
44
|
+
try {
|
|
45
|
+
const server = await startDevServer({ siteRoot, port, log, logError });
|
|
46
|
+
ready = true;
|
|
47
|
+
spinner.succeed(`Serving ${server.url}`);
|
|
48
|
+
await new Promise((resolvePromise) => {
|
|
49
|
+
const shutdown = () => {
|
|
50
|
+
void server.close().then(resolvePromise);
|
|
51
|
+
};
|
|
52
|
+
process.once('SIGINT', shutdown);
|
|
53
|
+
process.once('SIGTERM', shutdown);
|
|
54
|
+
});
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
if (err?.code === 'EADDRINUSE') {
|
|
59
|
+
spinner.fail(`port ${port ?? 5000} is already in use`);
|
|
60
|
+
console.error(' stop whatever is listening on it, or pass --port <port>');
|
|
61
|
+
return 1;
|
|
62
|
+
}
|
|
63
|
+
spinner.fail(err instanceof Error ? err.message : String(err));
|
|
64
|
+
return 1;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=dev.js.map
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { dirname, join, relative, resolve, sep } from 'node:path';
|
|
4
|
+
import { parseArgs } from '../lib/args.js';
|
|
5
|
+
import { exists } from '../lib/copy-template.js';
|
|
6
|
+
import { findRepoRoot } from '../lib/repo-root.js';
|
|
7
|
+
import { getContext } from '../lib/cli-context.js';
|
|
8
|
+
import { theme } from '../lib/theme.js';
|
|
9
|
+
import { readSourceUrl, readStartPageComponent, readStartPath } from '../lib/playbook-yml.js';
|
|
10
|
+
import { checkAgentFilesPresent, checkAntoraAvailable, checkGitHasCommit, checkNamesAgree, checkNodeVersion, checkReleaseLabelExists, } from '../lib/doctor-checks.js';
|
|
11
|
+
async function readJson(file) {
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(await readFile(file, 'utf8'));
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function readAntoraYmlName(content) {
|
|
20
|
+
const match = /^name:\s*(.+?)\s*$/m.exec(content);
|
|
21
|
+
return match?.[1] ?? null;
|
|
22
|
+
}
|
|
23
|
+
// Every check is recorded here as it runs, in section order, regardless of
|
|
24
|
+
// whether the human-readable report or --json is what actually gets
|
|
25
|
+
// printed — this is the one source of truth both output modes read from,
|
|
26
|
+
// so they can never drift from each other on which checks ran or what they
|
|
27
|
+
// found.
|
|
28
|
+
function record(report, section, result, severity) {
|
|
29
|
+
report.push({ ...result, section, severity: result.ok ? 'ok' : severity });
|
|
30
|
+
if (severity === 'fail')
|
|
31
|
+
return result.ok ? 0 : 1;
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
function printResult(result) {
|
|
35
|
+
const ok = theme.success(' ok ');
|
|
36
|
+
const fail = theme.error('FAIL');
|
|
37
|
+
console.log(` ${result.ok ? ok : fail} ${result.label} — ${result.message}`);
|
|
38
|
+
if (!result.ok && result.detail) {
|
|
39
|
+
console.log(` ${result.detail}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Same rendering as printResult, but never contributes to the overall exit
|
|
43
|
+
// code — used only for advisory checks (agent files, release label): 'warn'
|
|
44
|
+
// instead of 'FAIL' so a missing file reads as advisory, not as the same
|
|
45
|
+
// severity as a broken build.
|
|
46
|
+
function printAdvisory(result) {
|
|
47
|
+
const ok = theme.success(' ok ');
|
|
48
|
+
const warn = theme.warn('warn');
|
|
49
|
+
console.log(` ${result.ok ? ok : warn} ${result.label} — ${result.message}`);
|
|
50
|
+
if (!result.ok && result.detail) {
|
|
51
|
+
console.log(` ${result.detail}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export async function runDoctor(argv) {
|
|
55
|
+
const { flags } = parseArgs(argv);
|
|
56
|
+
const json = getContext().json;
|
|
57
|
+
const startDir = typeof flags.dir === 'string' ? resolve(flags.dir) : resolve(process.cwd());
|
|
58
|
+
// --dir (or cwd) can be anywhere inside the repository — its root, inside
|
|
59
|
+
// docs/, wherever — findRepoRoot walks up to find the actual repository
|
|
60
|
+
// root the same way `git` itself would, so doctor always looks at
|
|
61
|
+
// <repoRoot>/docs regardless of where it was invoked from.
|
|
62
|
+
const target = await findRepoRoot(startDir);
|
|
63
|
+
const siteRoot = join(target, 'docs');
|
|
64
|
+
if (!(await exists(siteRoot))) {
|
|
65
|
+
console.error(`no site found at '${siteRoot}'`);
|
|
66
|
+
console.error('pass --dir <path> to a component root (the parent of docs/), or run docouture new first');
|
|
67
|
+
return 1;
|
|
68
|
+
}
|
|
69
|
+
let status = 0;
|
|
70
|
+
const report = [];
|
|
71
|
+
const log = (line) => {
|
|
72
|
+
if (!json)
|
|
73
|
+
console.log(line);
|
|
74
|
+
};
|
|
75
|
+
log('toolchain');
|
|
76
|
+
const pkg = await readJson(join(siteRoot, 'package.json'));
|
|
77
|
+
const nodeResult = checkNodeVersion(pkg?.engines?.node, process.version);
|
|
78
|
+
status |= record(report, 'toolchain', nodeResult, 'fail');
|
|
79
|
+
if (!json)
|
|
80
|
+
printResult(nodeResult);
|
|
81
|
+
log('names');
|
|
82
|
+
const playbookFile = join(siteRoot, 'antora-playbook.yml');
|
|
83
|
+
const antoraYmlFile = join(siteRoot, 'src', 'antora.yml');
|
|
84
|
+
const playbookContent = (await exists(playbookFile)) ? await readFile(playbookFile, 'utf8') : null;
|
|
85
|
+
const antoraYmlContent = (await exists(antoraYmlFile)) ? await readFile(antoraYmlFile, 'utf8') : null;
|
|
86
|
+
if (!playbookContent) {
|
|
87
|
+
const result = { ok: false, label: 'antora-playbook.yml', message: `not found at '${playbookFile}'` };
|
|
88
|
+
status |= record(report, 'names', result, 'fail');
|
|
89
|
+
if (!json)
|
|
90
|
+
printResult(result);
|
|
91
|
+
}
|
|
92
|
+
if (!antoraYmlContent) {
|
|
93
|
+
const result = { ok: false, label: 'src/antora.yml', message: `not found at '${antoraYmlFile}'` };
|
|
94
|
+
status |= record(report, 'names', result, 'fail');
|
|
95
|
+
if (!json)
|
|
96
|
+
printResult(result);
|
|
97
|
+
}
|
|
98
|
+
if (playbookContent && antoraYmlContent) {
|
|
99
|
+
const sourceUrl = readSourceUrl(playbookContent);
|
|
100
|
+
if (sourceUrl && sourceUrl !== '.') {
|
|
101
|
+
// The repository-root-relative form of `src/antora.yml`'s real
|
|
102
|
+
// location — see lib/playbook-yml.ts's own comment on why `url: .`
|
|
103
|
+
// only works when the playbook itself sits at the repository root.
|
|
104
|
+
const descriptorPath = relative(target, dirname(antoraYmlFile)).split(sep).join('/');
|
|
105
|
+
const results = checkNamesAgree({
|
|
106
|
+
antoraYmlName: readAntoraYmlName(antoraYmlContent),
|
|
107
|
+
startPageComponent: readStartPageComponent(playbookContent),
|
|
108
|
+
startPath: readStartPath(playbookContent),
|
|
109
|
+
descriptorPath,
|
|
110
|
+
packageName: pkg?.name ?? null,
|
|
111
|
+
});
|
|
112
|
+
for (const result of results) {
|
|
113
|
+
status |= record(report, 'names', result, 'fail');
|
|
114
|
+
if (!json)
|
|
115
|
+
printResult(result);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
const result = {
|
|
120
|
+
ok: false,
|
|
121
|
+
label: 'content source url',
|
|
122
|
+
message: `content.sources[0].url is '.' but the playbook does not sit at the repository root`,
|
|
123
|
+
detail: "url: '.' only works when antora-playbook.yml sits at the repository root — use 'url: ..' (or however many levels reach it) otherwise",
|
|
124
|
+
};
|
|
125
|
+
status |= record(report, 'names', result, 'fail');
|
|
126
|
+
if (!json)
|
|
127
|
+
printResult(result);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
log('content');
|
|
131
|
+
const gitResult = await checkGitHasCommit(target);
|
|
132
|
+
status |= record(report, 'content', gitResult, 'fail');
|
|
133
|
+
if (!json)
|
|
134
|
+
printResult(gitResult);
|
|
135
|
+
log('dependencies');
|
|
136
|
+
const antoraResult = checkAntoraAvailable(siteRoot);
|
|
137
|
+
status |= record(report, 'dependencies', antoraResult, 'fail');
|
|
138
|
+
if (!json)
|
|
139
|
+
printResult(antoraResult);
|
|
140
|
+
log('agent files');
|
|
141
|
+
for (const result of checkAgentFilesPresent(target)) {
|
|
142
|
+
record(report, 'agent files', result, 'warn');
|
|
143
|
+
if (!json)
|
|
144
|
+
printAdvisory(result);
|
|
145
|
+
}
|
|
146
|
+
log('release');
|
|
147
|
+
const releaseResult = await checkReleaseLabelExists(target);
|
|
148
|
+
record(report, 'release', releaseResult, 'warn');
|
|
149
|
+
if (!json)
|
|
150
|
+
printAdvisory(releaseResult);
|
|
151
|
+
if (json) {
|
|
152
|
+
process.stdout.write(`${JSON.stringify({ status: status === 0 ? 'ok' : 'fail', checks: report }, null, 2)}\n`);
|
|
153
|
+
}
|
|
154
|
+
return status === 0 ? 0 : 1;
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=doctor.js.map
|