@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
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
import { join, resolve } from 'node:path';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import { parseArgs } from '../lib/args.js';
|
|
7
|
+
import { exists } from '../lib/copy-template.js';
|
|
8
|
+
import { findRepoRoot } from '../lib/repo-root.js';
|
|
9
|
+
import { readOutputDir } from '../lib/playbook-yml.js';
|
|
10
|
+
import { resolveConfig } from '../lib/config-resolver.js';
|
|
11
|
+
import { debugLog } from '../lib/debug-log.js';
|
|
12
|
+
/**
|
|
13
|
+
* Resolved from the SITE's own node_modules (via its package.json), not
|
|
14
|
+
* this CLI's — a driver is a devDependency of the site being published,
|
|
15
|
+
* not of docouture itself. Exposed as its own function — rather than inlined
|
|
16
|
+
* in runPublish — so tests can inject a fake in its place: under the test
|
|
17
|
+
* runner, a bare `require()` of a package name that also happens to be a
|
|
18
|
+
* real published dependency of this monorepo can resolve to THAT real
|
|
19
|
+
* package instead of failing, regardless of the `from` path passed to
|
|
20
|
+
* `createRequire` — the dynamic-require equivalent of `vi.mock` not
|
|
21
|
+
* intercepting a plain CommonJS `require()` (see
|
|
22
|
+
* @inditextech/docouture-publish-gh-pages's own index.js for the same
|
|
23
|
+
* seam-over-mock reasoning applied to its `gh-pages` dependency).
|
|
24
|
+
*/
|
|
25
|
+
function loadDriver(packageJsonFile, driverPackageName) {
|
|
26
|
+
return createRequire(packageJsonFile)(driverPackageName);
|
|
27
|
+
}
|
|
28
|
+
async function readJson(file) {
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(await readFile(file, 'utf8'));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Every flag except `--dir` (which this command consumes itself) is
|
|
38
|
+
* forwarded to the driver as one of its own options. `--user-name`/
|
|
39
|
+
* `--user-email`, if given, are combined into the nested `user: {name,
|
|
40
|
+
* email}` shape @inditextech/docouture-publish-gh-pages (and presumably other
|
|
41
|
+
* drivers needing a commit identity) expects — the only nested option
|
|
42
|
+
* shape a flat `--flag value` CLI syntax can't express directly.
|
|
43
|
+
*/
|
|
44
|
+
function flagsToOptions(flags) {
|
|
45
|
+
const { 'user-name': userName, 'user-email': userEmail, ...rest } = flags;
|
|
46
|
+
delete rest.dir;
|
|
47
|
+
const options = { ...rest };
|
|
48
|
+
if (userName !== undefined || userEmail !== undefined) {
|
|
49
|
+
options.user = { name: userName, email: userEmail };
|
|
50
|
+
}
|
|
51
|
+
return options;
|
|
52
|
+
}
|
|
53
|
+
export async function runPublish(argv, deps = {}) {
|
|
54
|
+
const resolveDriver = deps.loadDriver ?? loadDriver;
|
|
55
|
+
const { positional, flags } = parseArgs(argv);
|
|
56
|
+
const target = positional[0];
|
|
57
|
+
if (!target) {
|
|
58
|
+
console.error('usage: docouture publish <target> [--dir <path>] [--<option> <value> ...]');
|
|
59
|
+
console.error('example: docouture publish gh-pages --branch gh-pages');
|
|
60
|
+
return 1;
|
|
61
|
+
}
|
|
62
|
+
const startDir = typeof flags.dir === 'string' ? resolve(flags.dir) : resolve(process.cwd());
|
|
63
|
+
const repoRoot = await findRepoRoot(startDir);
|
|
64
|
+
const siteRoot = join(repoRoot, 'docs');
|
|
65
|
+
const packageJsonFile = join(siteRoot, 'package.json');
|
|
66
|
+
if (!(await exists(packageJsonFile))) {
|
|
67
|
+
console.error(`no package.json found at '${siteRoot}'`);
|
|
68
|
+
console.error('pass --dir <path> to a component root (the parent of docs/), or run docouture new first');
|
|
69
|
+
return 1;
|
|
70
|
+
}
|
|
71
|
+
const driverPackageName = `@inditextech/docouture-publish-${target}`;
|
|
72
|
+
let driver;
|
|
73
|
+
try {
|
|
74
|
+
driver = resolveDriver(packageJsonFile, driverPackageName);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
console.error(`could not load publish driver '${driverPackageName}'`);
|
|
78
|
+
console.error(`add it to ${packageJsonFile}'s devDependencies, e.g.: npm install --save-dev ${driverPackageName}`);
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
const playbookFile = join(siteRoot, 'antora-playbook.yml');
|
|
82
|
+
const playbookContent = (await exists(playbookFile)) ? await readFile(playbookFile, 'utf8') : '';
|
|
83
|
+
const siteDir = resolve(siteRoot, readOutputDir(playbookContent) || 'build/site');
|
|
84
|
+
if (!(await exists(siteDir))) {
|
|
85
|
+
console.error(`no built site found at '${siteDir}' — run 'docouture build' first`);
|
|
86
|
+
return 1;
|
|
87
|
+
}
|
|
88
|
+
const packageJson = await readJson(packageJsonFile);
|
|
89
|
+
const configuredOptions = packageJson?.docouture?.publish?.[target] ?? {};
|
|
90
|
+
// CLI flags win over docs/package.json's own "docouture".publish.<target>
|
|
91
|
+
// config, which wins over whatever defaults the driver applies itself —
|
|
92
|
+
// see lib/config-resolver.ts for this precedence rule shared with upgrade.
|
|
93
|
+
const options = resolveConfig({}, configuredOptions, flagsToOptions(flags));
|
|
94
|
+
debugLog(`publishing '${target}' from ${siteDir} with options: ${JSON.stringify(options)}`);
|
|
95
|
+
const spinner = ora({ text: `Publishing to ${target}…`, stream: process.stderr }).start();
|
|
96
|
+
try {
|
|
97
|
+
const published = await driver(siteDir, options);
|
|
98
|
+
if (published === false) {
|
|
99
|
+
spinner.fail(`'${target}' driver reported it did not publish`);
|
|
100
|
+
return 1;
|
|
101
|
+
}
|
|
102
|
+
spinner.succeed(`Published to ${target}`);
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
spinner.fail(`publishing to '${target}' failed`);
|
|
107
|
+
console.error(err instanceof Error ? err.stack : String(err));
|
|
108
|
+
return 1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=publish.js.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
import { execFile } from 'node:child_process';
|
|
3
|
+
import { promisify } from 'node:util';
|
|
4
|
+
import { join, resolve } from 'node:path';
|
|
5
|
+
import { parseArgs } from '../lib/args.js';
|
|
6
|
+
import { exists } from '../lib/copy-template.js';
|
|
7
|
+
import { findRepoRoot } from '../lib/repo-root.js';
|
|
8
|
+
import { resolveEffectiveComposeFile } from '../lib/kroki-compose.js';
|
|
9
|
+
// `docouture teardown kroki` — the explicit, manual stop side of GH-44's Kroki
|
|
10
|
+
// auto-start. `kroki-prewarm.js` (in @inditextech/docouture-antora-extensions)
|
|
11
|
+
// starts the service on demand but never stops it — see kroki-docker.js's
|
|
12
|
+
// own header for why: a build has no natural "I'm completely done with this
|
|
13
|
+
// machine for good" moment to hook, and stopping it mid-session would only
|
|
14
|
+
// force the next build to pay the startup latency again. This command is
|
|
15
|
+
// the escape hatch for the moment a human actually does know that: done for
|
|
16
|
+
// the day, freeing the port for something else, before switching to a
|
|
17
|
+
// project with different diagram types configured, etc.
|
|
18
|
+
//
|
|
19
|
+
// Targets whichever compose file is ACTUALLY in effect — an ejected
|
|
20
|
+
// override at the site root if one exists, else the bundled default — via
|
|
21
|
+
// the exact same resolution order kroki-docker.js's own auto-start uses
|
|
22
|
+
// (lib/kroki-compose.js's `resolveEffectiveComposeFile`), so this always
|
|
23
|
+
// stops the containers a build would actually have started, not
|
|
24
|
+
// necessarily the package's un-customized default.
|
|
25
|
+
const execFileAsync = promisify(execFile);
|
|
26
|
+
const SUPPORTED_TARGETS = {
|
|
27
|
+
kroki: { description: 'Kroki diagram rendering service (docker compose)' },
|
|
28
|
+
};
|
|
29
|
+
export async function runTeardown(argv, deps = {}) {
|
|
30
|
+
const { positional, flags } = parseArgs(argv);
|
|
31
|
+
const target = positional[0];
|
|
32
|
+
if (!target || !(target in SUPPORTED_TARGETS)) {
|
|
33
|
+
console.error('usage: docouture teardown <target> [--dir <path>]');
|
|
34
|
+
console.error('supported targets:');
|
|
35
|
+
for (const [name, { description }] of Object.entries(SUPPORTED_TARGETS)) {
|
|
36
|
+
console.error(` ${name} — ${description}`);
|
|
37
|
+
}
|
|
38
|
+
return 1;
|
|
39
|
+
}
|
|
40
|
+
const startDir = typeof flags.dir === 'string' ? resolve(flags.dir) : resolve(process.cwd());
|
|
41
|
+
const repoRoot = await findRepoRoot(startDir);
|
|
42
|
+
const siteRoot = join(repoRoot, 'docs');
|
|
43
|
+
if (!(await exists(join(siteRoot, 'package.json')))) {
|
|
44
|
+
console.error(`no package.json found at '${siteRoot}'`);
|
|
45
|
+
console.error('pass --dir <path> to a component root (the parent of docs/), or run docouture new first');
|
|
46
|
+
return 1;
|
|
47
|
+
}
|
|
48
|
+
const resolveFile = deps.resolveEffectiveComposeFile || resolveEffectiveComposeFile;
|
|
49
|
+
const composeFile = await resolveFile(siteRoot);
|
|
50
|
+
if (!composeFile) {
|
|
51
|
+
console.error(`could not find @inditextech/docouture-antora-extensions' bundled kroki-compose.yml, and no override exists at '${join(siteRoot, 'kroki-compose.yml')}'`);
|
|
52
|
+
console.error('add @inditextech/docouture-antora-extensions to devDependencies, or run docouture eject kroki first');
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const runDockerCompose = deps.execFileAsync || execFileAsync;
|
|
57
|
+
await runDockerCompose('docker', ['compose', '-f', composeFile, 'down']);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
61
|
+
return 1;
|
|
62
|
+
}
|
|
63
|
+
console.log(`stopped the Kroki service (${composeFile})`);
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=teardown.js.map
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { parseArgs } from '../lib/args.js';
|
|
7
|
+
import { readCliInfo } from '../lib/cli-info.js';
|
|
8
|
+
import { copyTemplate, exists, renderTemplateFile } from '../lib/copy-template.js';
|
|
9
|
+
import { detectPackageManager, packageManagerPlan } from '../lib/detect-package-manager.js';
|
|
10
|
+
import { resolveConfig } from '../lib/config-resolver.js';
|
|
11
|
+
import { findRepoRoot } from '../lib/repo-root.js';
|
|
12
|
+
import { AGENTS_MD_FILENAME, mergeAgentsMd } from '../lib/agents-md.js';
|
|
13
|
+
// Same check `new.ts` uses, duplicated rather than imported — it's three
|
|
14
|
+
// lines, and `doctor-checks.ts` already sets the precedent of duplicating a
|
|
15
|
+
// small piece of `new.ts`'s own logic instead of reaching across into a
|
|
16
|
+
// module built around the interactive wizard (see its own comment on
|
|
17
|
+
// AGENT_SUPPORT_CHECK_PATHS).
|
|
18
|
+
function isInsideGitWorkTree(dir) {
|
|
19
|
+
try {
|
|
20
|
+
execFileSync('git', ['rev-parse', '--is-inside-work-tree'], { cwd: dir, stdio: 'ignore' });
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function titleCase(name) {
|
|
28
|
+
return name
|
|
29
|
+
.split('-')
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.map((word) => word[0].toUpperCase() + word.slice(1))
|
|
32
|
+
.join(' ');
|
|
33
|
+
}
|
|
34
|
+
// docs/antora.yml's `name:`/`title:` are read the same way antora-yml.ts
|
|
35
|
+
// reads `version:` — a plain top-of-line regex, not a full YAML parse, so
|
|
36
|
+
// comments and formatting a human added survive untouched (this file is
|
|
37
|
+
// only ever read here, never rewritten). Unlike antora-yml.ts, upgrade.ts
|
|
38
|
+
// never writes this file, so that module's write-side helpers don't apply
|
|
39
|
+
// here and this stays a small, local, read-only counterpart instead of
|
|
40
|
+
// growing antora-yml.ts's own scope to cover a field it doesn't otherwise
|
|
41
|
+
// need.
|
|
42
|
+
function readAntoraField(content, key) {
|
|
43
|
+
const match = new RegExp(`^${key}:.*$`, 'm').exec(content);
|
|
44
|
+
if (!match)
|
|
45
|
+
return null;
|
|
46
|
+
const value = match[0].slice(key.length + 1).trim();
|
|
47
|
+
return value.length > 0 ? value : null;
|
|
48
|
+
}
|
|
49
|
+
export async function runUpgrade(argv) {
|
|
50
|
+
const { flags } = parseArgs(argv);
|
|
51
|
+
const dryRun = flags['dry-run'] === true;
|
|
52
|
+
const startDir = typeof flags.dir === 'string' ? resolve(flags.dir) : resolve(process.cwd());
|
|
53
|
+
if (!isInsideGitWorkTree(startDir)) {
|
|
54
|
+
console.error(`'${startDir}' is not inside a git repository`);
|
|
55
|
+
console.error('docouture upgrade re-syncs an already-scaffolded repository — run it from your repo root, or pass --dir <path> to one');
|
|
56
|
+
return 1;
|
|
57
|
+
}
|
|
58
|
+
// --dir/cwd can be anywhere inside the repository — findRepoRoot walks up
|
|
59
|
+
// to the actual top-level, same as new.ts and the rest of the CLI.
|
|
60
|
+
const target = await findRepoRoot(startDir);
|
|
61
|
+
// build/commands/upgrade.js -> build/templates/{workflows,agent-support} —
|
|
62
|
+
// same resolution new.ts uses, see its own comment.
|
|
63
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
64
|
+
const templatesRoot = join(here, '..', 'templates');
|
|
65
|
+
const workflowsTemplateDir = join(templatesRoot, 'workflows');
|
|
66
|
+
const agentSupportDir = join(templatesRoot, 'agent-support');
|
|
67
|
+
// build/commands/upgrade.js -> package root, 2 levels up — see
|
|
68
|
+
// readCliInfo's own comment. Always re-read fresh: an upgrade run re-pins
|
|
69
|
+
// whatever templates reference the CLI version to the one actually
|
|
70
|
+
// installed now, which may well differ from whatever `docouture new`
|
|
71
|
+
// originally stamped in.
|
|
72
|
+
const { version: cliVersion } = await readCliInfo(import.meta.url, 2);
|
|
73
|
+
// Re-detected fresh too, same reasoning — a site may have picked up a
|
|
74
|
+
// lockfile (or a packageManager field) since it was first scaffolded.
|
|
75
|
+
const pm = packageManagerPlan(detectPackageManager(target));
|
|
76
|
+
// AGENTS.md's own __DOCOUTURE_TITLE__ placeholder (the only name/title token
|
|
77
|
+
// used outside `new.ts`'s starter/docs templates — see copy-template.ts's
|
|
78
|
+
// PLACEHOLDERS) needs a value from somewhere, but `upgrade` takes no
|
|
79
|
+
// <name> argument: it re-syncs an existing site, so the value it already
|
|
80
|
+
// recorded in docs/antora.yml at scaffold time is read back rather than
|
|
81
|
+
// asked for again. --title still overrides it, and a site with no
|
|
82
|
+
// docs/antora.yml (workflows/skills used standalone of the starter
|
|
83
|
+
// subtree) falls back to a plain, title-cased directory name.
|
|
84
|
+
const descriptorPath = join(target, 'docs', 'src', 'antora.yml');
|
|
85
|
+
let name = 'docs';
|
|
86
|
+
let fromYmlTitle;
|
|
87
|
+
if (await exists(descriptorPath)) {
|
|
88
|
+
const content = await readFile(descriptorPath, 'utf8');
|
|
89
|
+
name = readAntoraField(content, 'name') ?? name;
|
|
90
|
+
fromYmlTitle = readAntoraField(content, 'title') ?? undefined;
|
|
91
|
+
}
|
|
92
|
+
// Same three-tier precedence publish.ts uses (see lib/config-resolver.ts):
|
|
93
|
+
// --title flag > the value already recorded in docs/antora.yml > a plain
|
|
94
|
+
// title-cased fallback computed from the name above.
|
|
95
|
+
const { title } = resolveConfig({ title: titleCase(name) }, { title: fromYmlTitle }, { title: typeof flags.title === 'string' ? flags.title : undefined });
|
|
96
|
+
const values = {
|
|
97
|
+
name,
|
|
98
|
+
title,
|
|
99
|
+
// Same reasoning as repoIgnoreGlob below: __DOCOUTURE_COMPONENT_NAME__ only
|
|
100
|
+
// appears in docs/antora.yml and antora-playbook.yml, neither of which
|
|
101
|
+
// upgrade ever re-copies (see the comment above `name` itself) — this
|
|
102
|
+
// value is structurally required by TemplateValues but never actually
|
|
103
|
+
// substituted anywhere upgrade touches.
|
|
104
|
+
componentName: 'unused-by-upgrade',
|
|
105
|
+
cliVersion,
|
|
106
|
+
pmName: pm.pm,
|
|
107
|
+
pmCacheName: pm.cacheName,
|
|
108
|
+
pmLockfile: pm.lockfile,
|
|
109
|
+
pmCiCmd: pm.ciCmd,
|
|
110
|
+
pmSetupStepYaml: pm.setupStepYaml,
|
|
111
|
+
// upgrade never re-copies docs/ (see below) — package.json, the only
|
|
112
|
+
// template file this placeholder appears in, is never touched here — so
|
|
113
|
+
// there's nothing meaningful to compute it from. Not an empty string:
|
|
114
|
+
// an empty glob (see check-links.mjs's globToRegExp) compiles to a
|
|
115
|
+
// pattern matching every URL, which would be a silent, dangerous no-op
|
|
116
|
+
// if this value were ever actually substituted somewhere.
|
|
117
|
+
repoIgnoreGlob: 'unused-by-upgrade',
|
|
118
|
+
};
|
|
119
|
+
const workflowsDir = join(target, '.github', 'workflows');
|
|
120
|
+
const agentsMdFile = join(target, AGENTS_MD_FILENAME);
|
|
121
|
+
// Unlike `new.ts`, this command's whole purpose is to overwrite what's
|
|
122
|
+
// already there — workflows and agent skills are meant to be regenerable
|
|
123
|
+
// from the template on every upgrade, not merged with local edits (there
|
|
124
|
+
// is no content-hash/diff tracking anywhere in this CLI to tell a stock
|
|
125
|
+
// file from a user-edited one). `docs/` itself — the starter content a
|
|
126
|
+
// site has since written its own pages into — is never touched here.
|
|
127
|
+
// AGENTS.md is the one exception: copyTemplate's own SKIP_FILENAMES skip
|
|
128
|
+
// (see copy-template.ts) leaves it untouched by the walk above, and it's
|
|
129
|
+
// merged instead — see lib/agents-md.ts for why a blind overwrite here
|
|
130
|
+
// would silently destroy the 'Documentation state' table
|
|
131
|
+
// `documenting-your-repo` maintains outside docouture' own managed section.
|
|
132
|
+
if (dryRun) {
|
|
133
|
+
const plannedWorkflows = await copyTemplate(workflowsTemplateDir, workflowsDir, values, { dryRun: true });
|
|
134
|
+
const plannedAgentSupport = await copyTemplate(agentSupportDir, target, values, { dryRun: true });
|
|
135
|
+
console.log('would write:');
|
|
136
|
+
for (const path of [...plannedWorkflows, ...plannedAgentSupport, agentsMdFile]) {
|
|
137
|
+
console.log(` ${relative(target, path)}`);
|
|
138
|
+
}
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
await copyTemplate(workflowsTemplateDir, workflowsDir, values);
|
|
142
|
+
await copyTemplate(agentSupportDir, target, values);
|
|
143
|
+
const existingAgentsMd = (await exists(agentsMdFile)) ? await readFile(agentsMdFile, 'utf8') : undefined;
|
|
144
|
+
const renderedAgentsMd = await renderTemplateFile(join(agentSupportDir, AGENTS_MD_FILENAME), values);
|
|
145
|
+
await writeFile(agentsMdFile, mergeAgentsMd(existingAgentsMd, renderedAgentsMd), 'utf8');
|
|
146
|
+
// Relative to `target` (the repo root), not process.cwd() — cwd may be a
|
|
147
|
+
// nested subdirectory findRepoRoot walked up from, in which case
|
|
148
|
+
// relative(cwd, ...) produces a useless '../../..' chain for paths that
|
|
149
|
+
// are actually just '.github/workflows', 'AGENTS.md' etc. at the root.
|
|
150
|
+
console.log(`updated ${relative(target, workflowsDir)}`);
|
|
151
|
+
console.log(`updated ${relative(target, agentsMdFile)}`);
|
|
152
|
+
console.log(`updated ${relative(target, join(target, '.opencode', 'skills'))}`);
|
|
153
|
+
console.log(`updated ${relative(target, join(target, '.claude', 'skills'))}`);
|
|
154
|
+
return 0;
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=upgrade.js.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { join, resolve } 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 { patchVersion, readVersion } from '../lib/antora-yml.js';
|
|
8
|
+
// Unlike `just bump` — which moves this workspace's own package.json files
|
|
9
|
+
// through npm semver arithmetic — this takes the target version as a literal
|
|
10
|
+
// value rather than a bump level (major/minor/patch/...). Antora's component
|
|
11
|
+
// `version` is a free-form string, not required to be semver: sites in the
|
|
12
|
+
// wild use "2.1", "v3", "latest". Guessing what "minor" means for "2.1" would
|
|
13
|
+
// be presumptuous, and wrong for the sites where it does not apply. If your
|
|
14
|
+
// documented version does happen to be semver, compute the next value
|
|
15
|
+
// yourself and pass it here.
|
|
16
|
+
export async function runVersion(argv) {
|
|
17
|
+
const { positional, flags } = parseArgs(argv);
|
|
18
|
+
const value = positional[0];
|
|
19
|
+
if (!value) {
|
|
20
|
+
console.error('usage: docouture version <value> [--dir <path>] [--file <path>] [--prerelease | --stable]');
|
|
21
|
+
return 1;
|
|
22
|
+
}
|
|
23
|
+
if (flags.prerelease && flags.stable) {
|
|
24
|
+
console.error('--prerelease and --stable are mutually exclusive');
|
|
25
|
+
return 1;
|
|
26
|
+
}
|
|
27
|
+
// --dir (or cwd) can be anywhere inside the repository — findRepoRoot
|
|
28
|
+
// walks up to the actual repository root, same as dev/build/doctor, so
|
|
29
|
+
// this works whether run from the repo root, from inside docs/, or from a
|
|
30
|
+
// nested page directory. --file bypasses all of this with a literal path.
|
|
31
|
+
let file;
|
|
32
|
+
if (typeof flags.file === 'string') {
|
|
33
|
+
file = resolve(flags.file);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
const startDir = typeof flags.dir === 'string' ? resolve(flags.dir) : resolve(process.cwd());
|
|
37
|
+
const repoRoot = await findRepoRoot(startDir);
|
|
38
|
+
file = join(repoRoot, 'docs', 'src', 'antora.yml');
|
|
39
|
+
}
|
|
40
|
+
if (!(await exists(file))) {
|
|
41
|
+
console.error(`no antora.yml found at '${file}'`);
|
|
42
|
+
console.error('pass --dir <path> (anywhere inside the repository) or --file <path> directly');
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
const content = await readFile(file, 'utf8');
|
|
46
|
+
const before = readVersion(content);
|
|
47
|
+
let patched;
|
|
48
|
+
try {
|
|
49
|
+
patched = patchVersion(content, { version: value, prerelease: flags.prerelease === true });
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
53
|
+
console.error(`'${file}' does not look like an Antora component descriptor`);
|
|
54
|
+
return 1;
|
|
55
|
+
}
|
|
56
|
+
await writeFile(file, patched, 'utf8');
|
|
57
|
+
console.log(` ${before ?? '(none)'} → ${value}${flags.prerelease ? ' (prerelease)' : ''}`);
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// AGENTS.md is the one scaffolded file `docouture new`/`docouture upgrade` never
|
|
3
|
+
// treat as all-or-nothing: unlike a workflow file or a skill directory, a
|
|
4
|
+
// repository may well already have its own AGENTS.md before `docouture new`
|
|
5
|
+
// ever runs, and the `documenting-your-repo` skill edits this file's own
|
|
6
|
+
// '## Documentation state' table over time as a repo's docs grow — content
|
|
7
|
+
// no template regenerates. Rather than clobber the whole file (today's
|
|
8
|
+
// behavior for every other scaffolded file) or refuse outright, docouture' own
|
|
9
|
+
// content lives inside a clearly marked, mechanically findable block —
|
|
10
|
+
// everything outside it (a human's own notes, or that evolving table) is
|
|
11
|
+
// never touched. The template itself
|
|
12
|
+
// (templates/agent-support/AGENTS.md) places MANAGED_END right before
|
|
13
|
+
// '## Documentation state', so that section is deliberately outside the
|
|
14
|
+
// block even on a brand-new scaffold.
|
|
15
|
+
export const MANAGED_START = '<!-- docouture:start - managed by docouture; edits inside this block are overwritten by `docouture new`/`docouture upgrade` -->';
|
|
16
|
+
export const MANAGED_END = '<!-- docouture:end -->';
|
|
17
|
+
export const AGENTS_MD_FILENAME = 'AGENTS.md';
|
|
18
|
+
// True when `content` already has a complete docouture-managed block — the
|
|
19
|
+
// signal `new.ts` uses to decide whether an existing AGENTS.md counts as a
|
|
20
|
+
// genuine overwrite conflict (subject to the same confirm-before-overwrite
|
|
21
|
+
// prompt as workflows/skills) versus a foreign file that's simply safe to
|
|
22
|
+
// append to without asking.
|
|
23
|
+
export function hasManagedSection(content) {
|
|
24
|
+
const start = content.indexOf(MANAGED_START);
|
|
25
|
+
const end = content.indexOf(MANAGED_END);
|
|
26
|
+
return start !== -1 && end !== -1 && end > start;
|
|
27
|
+
}
|
|
28
|
+
function extractManagedBlock(templateContent) {
|
|
29
|
+
const start = templateContent.indexOf(MANAGED_START);
|
|
30
|
+
const end = templateContent.indexOf(MANAGED_END);
|
|
31
|
+
if (start === -1 || end === -1 || end <= start) {
|
|
32
|
+
// Only reachable if templates/agent-support/AGENTS.md itself loses its
|
|
33
|
+
// own markers — a packaging bug, not a runtime/user condition.
|
|
34
|
+
throw new Error('templates/agent-support/AGENTS.md is missing its docouture:start/docouture:end markers');
|
|
35
|
+
}
|
|
36
|
+
return templateContent.slice(start, end + MANAGED_END.length);
|
|
37
|
+
}
|
|
38
|
+
// Merges `templateContent` — this run's freshly rendered AGENTS.md, exactly
|
|
39
|
+
// as `docouture new` would write it from scratch (placeholders already
|
|
40
|
+
// substituted, both markers and the default '## Documentation state' tail
|
|
41
|
+
// all present) — into `existing`, the AGENTS.md content already on disk (or
|
|
42
|
+
// `undefined` if there wasn't one):
|
|
43
|
+
// - no existing file -> templateContent as-is, unchanged from
|
|
44
|
+
// today's whole-file scaffold output.
|
|
45
|
+
// - existing has a managed -> only that block is replaced, sliced out
|
|
46
|
+
// block already of templateContent; everything in
|
|
47
|
+
// `existing` before/after it (a human's
|
|
48
|
+
// own notes, the Documentation state
|
|
49
|
+
// table) survives byte for byte.
|
|
50
|
+
// - existing has no managed -> templateContent (block + its own
|
|
51
|
+
// block (a foreign/human default tail) is appended to the end
|
|
52
|
+
// AGENTS.md, or one from of `existing`, blank-line separated;
|
|
53
|
+
// before this existed) nothing already there is touched.
|
|
54
|
+
export function mergeAgentsMd(existing, templateContent) {
|
|
55
|
+
if (existing === undefined)
|
|
56
|
+
return templateContent;
|
|
57
|
+
const start = existing.indexOf(MANAGED_START);
|
|
58
|
+
const end = existing.indexOf(MANAGED_END);
|
|
59
|
+
if (start !== -1 && end !== -1 && end > start) {
|
|
60
|
+
const before = existing.slice(0, start);
|
|
61
|
+
const after = existing.slice(end + MANAGED_END.length);
|
|
62
|
+
return `${before}${extractManagedBlock(templateContent)}${after}`;
|
|
63
|
+
}
|
|
64
|
+
const separator = existing.length === 0 ? '' : existing.endsWith('\n\n') ? '' : existing.endsWith('\n') ? '\n' : '\n\n';
|
|
65
|
+
return `${existing}${separator}${templateContent}`;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=agents-md.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Antora's own default log level is `warn` — set explicitly by @antora/
|
|
3
|
+
// playbook-builder's convict schema, not the `info` @antora/logger falls
|
|
4
|
+
// back to on its own when nothing configures it. Left alone, every one of
|
|
5
|
+
// docouture's own Antora pipeline extensions (kroki-prewarm.js, search-index.js,
|
|
6
|
+
// llms-txt.js, footer.js, nav-modules.js, not-found-page.js,
|
|
7
|
+
// version-report.js — anything calling `context.getLogger('docouture-...')`)
|
|
8
|
+
// logs its own healthy-run observability at `info` and is silently dropped
|
|
9
|
+
// before it ever reaches stdout. `docouture build`/`docouture dev` pass this to
|
|
10
|
+
// every Antora invocation they make so those lines are actually emitted —
|
|
11
|
+
// the monorepo's own `just dev`/`just build-site` recipes do the same for
|
|
12
|
+
// the identical reason (see justfile's own comments on this).
|
|
13
|
+
export const ANTORA_LOG_LEVEL_ARGS = ['--log-level=info'];
|
|
14
|
+
// Matches a JSON log line carrying `"level":"warn"` or `"level":"error"`
|
|
15
|
+
// (Antora's own genuine signal, regardless of source), or one from any
|
|
16
|
+
// `docouture-*` named logger regardless of level (every current and future
|
|
17
|
+
// docouture extension follows that naming convention — see the file list
|
|
18
|
+
// above) — docouture's own healthy-run observability, which is otherwise
|
|
19
|
+
// indistinguishable from Antora's own internal `info` chatter that
|
|
20
|
+
// `--log-level=info` also just turned on.
|
|
21
|
+
const OBSERVABLE_LINE = /"level":"(?:warn|error)"|"name":"docouture-/;
|
|
22
|
+
/**
|
|
23
|
+
* Filters raw Antora output (stdout, or stdout+stderr concatenated) down to
|
|
24
|
+
* the lines worth showing on an otherwise-quiet, successful run: see
|
|
25
|
+
* `OBSERVABLE_LINE`'s own comment for exactly which ones. Returns '' when
|
|
26
|
+
* nothing qualifies — callers should skip printing entirely rather than
|
|
27
|
+
* print a blank line. A failed run should show its raw, unfiltered output
|
|
28
|
+
* instead of this — the whole story a filter might otherwise cut short.
|
|
29
|
+
*/
|
|
30
|
+
export function filterObservableAntoraLog(output) {
|
|
31
|
+
return output
|
|
32
|
+
.split('\n')
|
|
33
|
+
.filter((line) => OBSERVABLE_LINE.test(line))
|
|
34
|
+
.join('\n');
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=antora-log.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Patches the two fields `docouture version` cares about — `version:` and
|
|
3
|
+
// `prerelease:` — by line, rather than parsing the file into a YAML document
|
|
4
|
+
// and re-serialising it. A full parse/dump round-trip would lose the comments
|
|
5
|
+
// antora.yml carries (see docs-site-package's own antora.yml for an example),
|
|
6
|
+
// and this file's whole reason to be readable is that a human edits it too.
|
|
7
|
+
//
|
|
8
|
+
// This only ever touches top-level keys, matched at the start of a line
|
|
9
|
+
// (column 0), which is where Antora requires `name`/`title`/`version` to live
|
|
10
|
+
// in a component descriptor.
|
|
11
|
+
const VERSION_LINE = /^version:.*$/m;
|
|
12
|
+
const PRERELEASE_LINE = /^prerelease:.*$/m;
|
|
13
|
+
export function readVersion(content) {
|
|
14
|
+
const match = VERSION_LINE.exec(content);
|
|
15
|
+
if (!match)
|
|
16
|
+
return null;
|
|
17
|
+
const value = match[0].slice('version:'.length).trim();
|
|
18
|
+
return value.length > 0 ? value : null;
|
|
19
|
+
}
|
|
20
|
+
export function hasVersionLine(content) {
|
|
21
|
+
return VERSION_LINE.test(content);
|
|
22
|
+
}
|
|
23
|
+
export function patchVersion(content, patch) {
|
|
24
|
+
if (!VERSION_LINE.test(content)) {
|
|
25
|
+
throw new Error("no top-level 'version:' line found");
|
|
26
|
+
}
|
|
27
|
+
let next = content.replace(VERSION_LINE, `version: ${patch.version}`);
|
|
28
|
+
if (patch.prerelease) {
|
|
29
|
+
if (PRERELEASE_LINE.test(next)) {
|
|
30
|
+
next = next.replace(PRERELEASE_LINE, 'prerelease: true');
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
// Insert directly after the version line, so a reader sees the two
|
|
34
|
+
// together rather than prerelease landing wherever the regex first
|
|
35
|
+
// matched something else.
|
|
36
|
+
next = next.replace(VERSION_LINE, (line) => `${line}\nprerelease: true`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else if (PRERELEASE_LINE.test(next)) {
|
|
40
|
+
next = next.replace(`${PRERELEASE_LINE.exec(next)[0]}\n`, '');
|
|
41
|
+
// The line may be the last one in the file, with no trailing newline to
|
|
42
|
+
// consume in the replacement above — handle that case too.
|
|
43
|
+
next = next.replace(PRERELEASE_LINE, '').replace(/\n{3,}/g, '\n\n');
|
|
44
|
+
}
|
|
45
|
+
return next;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=antora-yml.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
export function parseArgs(argv) {
|
|
3
|
+
const positional = [];
|
|
4
|
+
const flags = {};
|
|
5
|
+
for (let i = 0; i < argv.length; i++) {
|
|
6
|
+
const arg = argv[i];
|
|
7
|
+
if (arg === undefined)
|
|
8
|
+
continue;
|
|
9
|
+
if (arg.startsWith('--')) {
|
|
10
|
+
const name = arg.slice(2);
|
|
11
|
+
const next = argv[i + 1];
|
|
12
|
+
if (next !== undefined && !next.startsWith('--')) {
|
|
13
|
+
flags[name] = next;
|
|
14
|
+
i++;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
flags[name] = true;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
positional.push(arg);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return { positional, flags };
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=args.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const DEFAULT_CONTEXT = { json: false, verbose: false, noColor: false };
|
|
3
|
+
let context = { ...DEFAULT_CONTEXT };
|
|
4
|
+
export function setContext(next) {
|
|
5
|
+
context = { ...context, ...next };
|
|
6
|
+
}
|
|
7
|
+
export function getContext() {
|
|
8
|
+
return context;
|
|
9
|
+
}
|
|
10
|
+
// Test-only: every other consumer only ever wants to add to context, never
|
|
11
|
+
// to guarantee a clean slate — vitest, however, runs every *.spec.ts in the
|
|
12
|
+
// same process, so a --json/--verbose/--no-color set by one test would
|
|
13
|
+
// otherwise leak into the next.
|
|
14
|
+
export function resetContext() {
|
|
15
|
+
context = { ...DEFAULT_CONTEXT };
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=cli-context.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
// Reads THIS package's own package.json — the one npm actually published (or
|
|
6
|
+
// `just release-local` snapshot-published) — never a value baked in at
|
|
7
|
+
// compile time, so it can never drift from what's really running. Callers
|
|
8
|
+
// pass their own import.meta.url plus how many directories separate them
|
|
9
|
+
// from the package root (bin.js lives at build/bin.js, one level down;
|
|
10
|
+
// new.js lives at build/commands/new.js, two levels down), since that
|
|
11
|
+
// differs by caller and there is no other reliable anchor at runtime.
|
|
12
|
+
export async function readCliInfo(callerUrl, levelsToPackageRoot) {
|
|
13
|
+
const here = dirname(fileURLToPath(callerUrl));
|
|
14
|
+
const upSegments = Array.from({ length: levelsToPackageRoot }, () => '..');
|
|
15
|
+
const pkgPath = join(here, ...upSegments, 'package.json');
|
|
16
|
+
const raw = await readFile(pkgPath, 'utf8');
|
|
17
|
+
const pkg = JSON.parse(raw);
|
|
18
|
+
return { name: pkg.name ?? '@inditextech/docouture-cli', version: pkg.version ?? '0.0.0' };
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=cli-info.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// The one config-precedence rule this CLI applies in more than one place —
|
|
3
|
+
// CLI flags win over a persisted/configured value, which wins over a
|
|
4
|
+
// hardcoded default — pulled out into a single, tested function instead of
|
|
5
|
+
// each command re-deriving the same `{ ...a, ...b, ...c }` merge with its
|
|
6
|
+
// own slightly different shape (publish.ts's options, upgrade.ts's title).
|
|
7
|
+
// Per-command commands still own *what* the three layers are (reading
|
|
8
|
+
// docs/package.json, docs/antora.yml, flags) — this only owns the merge
|
|
9
|
+
// order itself, and only overrides a key when the flag/configured layer
|
|
10
|
+
// actually provided a value: `undefined` never wins over a real default.
|
|
11
|
+
export function resolveConfig(defaults, configured = {}, flags = {}) {
|
|
12
|
+
const result = { ...defaults };
|
|
13
|
+
for (const [key, value] of Object.entries(configured)) {
|
|
14
|
+
if (value !== undefined)
|
|
15
|
+
result[key] = value;
|
|
16
|
+
}
|
|
17
|
+
for (const [key, value] of Object.entries(flags)) {
|
|
18
|
+
if (value !== undefined)
|
|
19
|
+
result[key] = value;
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=config-resolver.js.map
|