politty 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +297 -28
- package/dist/arg-registry-ClI2WGgH.d.cts +89 -0
- package/dist/arg-registry-ClI2WGgH.d.cts.map +1 -0
- package/dist/arg-registry-D4NsqcNZ.d.ts +89 -0
- package/dist/arg-registry-D4NsqcNZ.d.ts.map +1 -0
- package/dist/augment.cjs +0 -0
- package/dist/augment.d.cts +17 -0
- package/dist/augment.d.cts.map +1 -0
- package/dist/augment.d.ts +17 -0
- package/dist/augment.d.ts.map +1 -0
- package/dist/augment.js +1 -0
- package/dist/command-Bgd-yIwv.cjs +25 -0
- package/dist/command-Bgd-yIwv.cjs.map +1 -0
- package/dist/command-CvKyk4ag.js +20 -0
- package/dist/command-CvKyk4ag.js.map +1 -0
- package/dist/completion/index.cjs +595 -0
- package/dist/completion/index.cjs.map +1 -0
- package/dist/completion/index.d.cts +153 -0
- package/dist/completion/index.d.cts.map +1 -0
- package/dist/completion/index.d.ts +153 -0
- package/dist/completion/index.d.ts.map +1 -0
- package/dist/completion/index.js +588 -0
- package/dist/completion/index.js.map +1 -0
- package/dist/docs/index.cjs +1239 -0
- package/dist/docs/index.cjs.map +1 -0
- package/dist/docs/index.d.cts +500 -0
- package/dist/docs/index.d.cts.map +1 -0
- package/dist/docs/index.d.ts +500 -0
- package/dist/docs/index.d.ts.map +1 -0
- package/dist/docs/index.js +1182 -0
- package/dist/docs/index.js.map +1 -0
- package/dist/index.cjs +28 -0
- package/dist/index.d.cts +439 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +439 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/runner-C8rhhx6L.js +1372 -0
- package/dist/runner-C8rhhx6L.js.map +1 -0
- package/dist/runner-C_m6ve-j.js +4 -0
- package/dist/runner-CmjYWlam.cjs +1486 -0
- package/dist/runner-CmjYWlam.cjs.map +1 -0
- package/dist/runner-pmoTWUXY.cjs +4 -0
- package/dist/schema-extractor-B9D3Rf22.cjs +354 -0
- package/dist/schema-extractor-B9D3Rf22.cjs.map +1 -0
- package/dist/schema-extractor-D-Eo7I77.d.cts +303 -0
- package/dist/schema-extractor-D-Eo7I77.d.cts.map +1 -0
- package/dist/schema-extractor-Dk5Z0Iei.js +324 -0
- package/dist/schema-extractor-Dk5Z0Iei.js.map +1 -0
- package/dist/schema-extractor-kkajLb9E.d.ts +303 -0
- package/dist/schema-extractor-kkajLb9E.d.ts.map +1 -0
- package/dist/subcommand-router-BiSvDXHg.js +153 -0
- package/dist/subcommand-router-BiSvDXHg.js.map +1 -0
- package/dist/subcommand-router-Vf-0w9P4.cjs +189 -0
- package/dist/subcommand-router-Vf-0w9P4.cjs.map +1 -0
- package/package.json +108 -6
|
@@ -0,0 +1,1182 @@
|
|
|
1
|
+
import { n as getExtractedFields } from "../schema-extractor-Dk5Z0Iei.js";
|
|
2
|
+
import { i as createLogCollector, n as resolveLazyCommand } from "../subcommand-router-BiSvDXHg.js";
|
|
3
|
+
import * as fs from "node:fs";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
|
|
6
|
+
//#region src/docs/default-renderers.ts
|
|
7
|
+
/**
|
|
8
|
+
* Escape markdown special characters in table cells
|
|
9
|
+
*/
|
|
10
|
+
function escapeTableCell(str) {
|
|
11
|
+
return str.replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Format default value for display
|
|
15
|
+
*/
|
|
16
|
+
function formatDefaultValue(value) {
|
|
17
|
+
if (value === void 0) return "-";
|
|
18
|
+
return `\`${JSON.stringify(value)}\``;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Render usage line
|
|
22
|
+
*/
|
|
23
|
+
function renderUsage(info) {
|
|
24
|
+
const parts = [info.fullCommandPath];
|
|
25
|
+
if (info.options.length > 0) parts.push("[options]");
|
|
26
|
+
if (info.subCommands.length > 0) parts.push("[command]");
|
|
27
|
+
for (const arg of info.positionalArgs) if (arg.required) parts.push(`<${arg.name}>`);
|
|
28
|
+
else parts.push(`[${arg.name}]`);
|
|
29
|
+
return parts.join(" ");
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Render arguments as table
|
|
33
|
+
*/
|
|
34
|
+
function renderArgumentsTable(info) {
|
|
35
|
+
if (info.positionalArgs.length === 0) return "";
|
|
36
|
+
const lines = [];
|
|
37
|
+
lines.push("| Argument | Description | Required |");
|
|
38
|
+
lines.push("|----------|-------------|----------|");
|
|
39
|
+
for (const arg of info.positionalArgs) {
|
|
40
|
+
const desc = escapeTableCell(arg.description ?? "");
|
|
41
|
+
const required = arg.required ? "Yes" : "No";
|
|
42
|
+
lines.push(`| \`${arg.name}\` | ${desc} | ${required} |`);
|
|
43
|
+
}
|
|
44
|
+
return lines.join("\n");
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Render arguments as list
|
|
48
|
+
*/
|
|
49
|
+
function renderArgumentsList(info) {
|
|
50
|
+
if (info.positionalArgs.length === 0) return "";
|
|
51
|
+
const lines = [];
|
|
52
|
+
for (const arg of info.positionalArgs) {
|
|
53
|
+
const required = arg.required ? "(required)" : "(optional)";
|
|
54
|
+
const desc = arg.description ? ` - ${arg.description}` : "";
|
|
55
|
+
lines.push(`- \`${arg.name}\`${desc} ${required}`);
|
|
56
|
+
}
|
|
57
|
+
return lines.join("\n");
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Format environment variable info for display
|
|
61
|
+
*/
|
|
62
|
+
function formatEnvInfo(env) {
|
|
63
|
+
if (!env) return "";
|
|
64
|
+
return ` [env: ${(Array.isArray(env) ? env : [env]).join(", ")}]`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Format option flags (uses kebab-case cliName)
|
|
68
|
+
*/
|
|
69
|
+
function formatOptionFlags(opt) {
|
|
70
|
+
const parts = [];
|
|
71
|
+
const placeholder = opt.placeholder ?? opt.cliName.toUpperCase().replace(/-/g, "_");
|
|
72
|
+
const longFlag = opt.type === "boolean" ? `--${opt.cliName}` : `--${opt.cliName} <${placeholder}>`;
|
|
73
|
+
if (opt.alias) parts.push(`\`-${opt.alias}\`, \`${longFlag}\``);
|
|
74
|
+
else parts.push(`\`${longFlag}\``);
|
|
75
|
+
return parts.join("");
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Render options as markdown table
|
|
79
|
+
*
|
|
80
|
+
* Features:
|
|
81
|
+
* - Uses kebab-case (cliName) for option names (e.g., `--dry-run` instead of `--dryRun`)
|
|
82
|
+
* - Automatically adds Env column when any option has env configured
|
|
83
|
+
* - Displays multiple env vars as comma-separated list
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* | Option | Alias | Description | Default | Env |
|
|
87
|
+
* |--------|-------|-------------|---------|-----|
|
|
88
|
+
* | `--dry-run` | `-d` | Dry run mode | `false` | - |
|
|
89
|
+
* | `--port <PORT>` | - | Server port | - | `PORT`, `SERVER_PORT` |
|
|
90
|
+
*/
|
|
91
|
+
function renderOptionsTable(info) {
|
|
92
|
+
if (info.options.length === 0) return "";
|
|
93
|
+
const hasEnv = info.options.some((opt) => opt.env);
|
|
94
|
+
const lines = [];
|
|
95
|
+
if (hasEnv) {
|
|
96
|
+
lines.push("| Option | Alias | Description | Default | Env |");
|
|
97
|
+
lines.push("|--------|-------|-------------|---------|-----|");
|
|
98
|
+
} else {
|
|
99
|
+
lines.push("| Option | Alias | Description | Default |");
|
|
100
|
+
lines.push("|--------|-------|-------------|---------|");
|
|
101
|
+
}
|
|
102
|
+
for (const opt of info.options) {
|
|
103
|
+
const placeholder = opt.placeholder ?? opt.cliName.toUpperCase().replace(/-/g, "_");
|
|
104
|
+
const optionName = opt.type === "boolean" ? `\`--${opt.cliName}\`` : `\`--${opt.cliName} <${placeholder}>\``;
|
|
105
|
+
const alias = opt.alias ? `\`-${opt.alias}\`` : "-";
|
|
106
|
+
const desc = escapeTableCell(opt.description ?? "");
|
|
107
|
+
const defaultVal = formatDefaultValue(opt.defaultValue);
|
|
108
|
+
if (hasEnv) {
|
|
109
|
+
const envNames = opt.env ? Array.isArray(opt.env) ? opt.env.map((e) => `\`${e}\``).join(", ") : `\`${opt.env}\`` : "-";
|
|
110
|
+
lines.push(`| ${optionName} | ${alias} | ${desc} | ${defaultVal} | ${envNames} |`);
|
|
111
|
+
} else lines.push(`| ${optionName} | ${alias} | ${desc} | ${defaultVal} |`);
|
|
112
|
+
}
|
|
113
|
+
return lines.join("\n");
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Render options as markdown list
|
|
117
|
+
*
|
|
118
|
+
* Features:
|
|
119
|
+
* - Uses kebab-case (cliName) for option names (e.g., `--dry-run` instead of `--dryRun`)
|
|
120
|
+
* - Appends env info at the end of each option (e.g., `[env: PORT, SERVER_PORT]`)
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* - `-d`, `--dry-run` - Dry run mode (default: false)
|
|
124
|
+
* - `--port <PORT>` - Server port [env: PORT, SERVER_PORT]
|
|
125
|
+
*/
|
|
126
|
+
function renderOptionsList(info) {
|
|
127
|
+
if (info.options.length === 0) return "";
|
|
128
|
+
const lines = [];
|
|
129
|
+
for (const opt of info.options) {
|
|
130
|
+
const flags = formatOptionFlags(opt);
|
|
131
|
+
const desc = opt.description ? ` - ${opt.description}` : "";
|
|
132
|
+
const defaultVal = opt.defaultValue !== void 0 ? ` (default: ${JSON.stringify(opt.defaultValue)})` : "";
|
|
133
|
+
const envInfo = formatEnvInfo(opt.env);
|
|
134
|
+
lines.push(`- ${flags}${desc}${defaultVal}${envInfo}`);
|
|
135
|
+
}
|
|
136
|
+
return lines.join("\n");
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Generate anchor from command path
|
|
140
|
+
*/
|
|
141
|
+
function generateAnchor(commandPath) {
|
|
142
|
+
return commandPath.join("-").toLowerCase();
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Generate relative path from one file to another
|
|
146
|
+
*/
|
|
147
|
+
function getRelativePath(from, to) {
|
|
148
|
+
const fromParts = from.split("/").slice(0, -1);
|
|
149
|
+
const toParts = to.split("/");
|
|
150
|
+
let commonLength = 0;
|
|
151
|
+
while (commonLength < fromParts.length && commonLength < toParts.length - 1 && fromParts[commonLength] === toParts[commonLength]) commonLength++;
|
|
152
|
+
const upCount = fromParts.length - commonLength;
|
|
153
|
+
return [...Array(upCount).fill(".."), ...toParts.slice(commonLength)].join("/") || (toParts[toParts.length - 1] ?? "");
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Render subcommands as table
|
|
157
|
+
*/
|
|
158
|
+
function renderSubcommandsTable(info, generateAnchors = true) {
|
|
159
|
+
if (info.subCommands.length === 0) return "";
|
|
160
|
+
const lines = [];
|
|
161
|
+
lines.push("| Command | Description |");
|
|
162
|
+
lines.push("|---------|-------------|");
|
|
163
|
+
const currentFile = info.filePath;
|
|
164
|
+
const fileMap = info.fileMap;
|
|
165
|
+
for (const sub of info.subCommands) {
|
|
166
|
+
const fullName = sub.fullPath.join(" ");
|
|
167
|
+
const desc = escapeTableCell(sub.description ?? "");
|
|
168
|
+
const subCommandPath = sub.fullPath.join(" ");
|
|
169
|
+
if (generateAnchors) {
|
|
170
|
+
const anchor = generateAnchor(sub.fullPath);
|
|
171
|
+
const subFile = fileMap?.[subCommandPath];
|
|
172
|
+
if (currentFile && subFile && currentFile !== subFile) {
|
|
173
|
+
const relativePath = getRelativePath(currentFile, subFile);
|
|
174
|
+
lines.push(`| [\`${fullName}\`](${relativePath}#${anchor}) | ${desc} |`);
|
|
175
|
+
} else lines.push(`| [\`${fullName}\`](#${anchor}) | ${desc} |`);
|
|
176
|
+
} else lines.push(`| \`${fullName}\` | ${desc} |`);
|
|
177
|
+
}
|
|
178
|
+
return lines.join("\n");
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Render options from array as table
|
|
182
|
+
*/
|
|
183
|
+
function renderOptionsTableFromArray(options) {
|
|
184
|
+
if (options.length === 0) return "";
|
|
185
|
+
const hasEnv = options.some((opt) => opt.env);
|
|
186
|
+
const lines = [];
|
|
187
|
+
if (hasEnv) {
|
|
188
|
+
lines.push("| Option | Alias | Description | Default | Env |");
|
|
189
|
+
lines.push("|--------|-------|-------------|---------|-----|");
|
|
190
|
+
} else {
|
|
191
|
+
lines.push("| Option | Alias | Description | Default |");
|
|
192
|
+
lines.push("|--------|-------|-------------|---------|");
|
|
193
|
+
}
|
|
194
|
+
for (const opt of options) {
|
|
195
|
+
const placeholder = opt.placeholder ?? opt.cliName.toUpperCase().replace(/-/g, "_");
|
|
196
|
+
const optionName = opt.type === "boolean" ? `\`--${opt.cliName}\`` : `\`--${opt.cliName} <${placeholder}>\``;
|
|
197
|
+
const alias = opt.alias ? `\`-${opt.alias}\`` : "-";
|
|
198
|
+
const desc = escapeTableCell(opt.description ?? "");
|
|
199
|
+
const defaultVal = formatDefaultValue(opt.defaultValue);
|
|
200
|
+
if (hasEnv) {
|
|
201
|
+
const envNames = opt.env ? Array.isArray(opt.env) ? opt.env.map((e) => `\`${e}\``).join(", ") : `\`${opt.env}\`` : "-";
|
|
202
|
+
lines.push(`| ${optionName} | ${alias} | ${desc} | ${defaultVal} | ${envNames} |`);
|
|
203
|
+
} else lines.push(`| ${optionName} | ${alias} | ${desc} | ${defaultVal} |`);
|
|
204
|
+
}
|
|
205
|
+
return lines.join("\n");
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Render options from array as list
|
|
209
|
+
*/
|
|
210
|
+
function renderOptionsListFromArray(options) {
|
|
211
|
+
if (options.length === 0) return "";
|
|
212
|
+
const lines = [];
|
|
213
|
+
for (const opt of options) {
|
|
214
|
+
const flags = formatOptionFlags(opt);
|
|
215
|
+
const desc = opt.description ? ` - ${opt.description}` : "";
|
|
216
|
+
const defaultVal = opt.defaultValue !== void 0 ? ` (default: ${JSON.stringify(opt.defaultValue)})` : "";
|
|
217
|
+
const envInfo = formatEnvInfo(opt.env);
|
|
218
|
+
lines.push(`- ${flags}${desc}${defaultVal}${envInfo}`);
|
|
219
|
+
}
|
|
220
|
+
return lines.join("\n");
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Render arguments from array as table
|
|
224
|
+
*/
|
|
225
|
+
function renderArgumentsTableFromArray(args) {
|
|
226
|
+
if (args.length === 0) return "";
|
|
227
|
+
const lines = [];
|
|
228
|
+
lines.push("| Argument | Description | Required |");
|
|
229
|
+
lines.push("|----------|-------------|----------|");
|
|
230
|
+
for (const arg of args) {
|
|
231
|
+
const desc = escapeTableCell(arg.description ?? "");
|
|
232
|
+
const required = arg.required ? "Yes" : "No";
|
|
233
|
+
lines.push(`| \`${arg.name}\` | ${desc} | ${required} |`);
|
|
234
|
+
}
|
|
235
|
+
return lines.join("\n");
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Render arguments from array as list
|
|
239
|
+
*/
|
|
240
|
+
function renderArgumentsListFromArray(args) {
|
|
241
|
+
if (args.length === 0) return "";
|
|
242
|
+
const lines = [];
|
|
243
|
+
for (const arg of args) {
|
|
244
|
+
const required = arg.required ? "(required)" : "(optional)";
|
|
245
|
+
const desc = arg.description ? ` - ${arg.description}` : "";
|
|
246
|
+
lines.push(`- \`${arg.name}\`${desc} ${required}`);
|
|
247
|
+
}
|
|
248
|
+
return lines.join("\n");
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Render subcommands from array as table
|
|
252
|
+
*/
|
|
253
|
+
function renderSubcommandsTableFromArray(subcommands, info, generateAnchors = true) {
|
|
254
|
+
if (subcommands.length === 0) return "";
|
|
255
|
+
const lines = [];
|
|
256
|
+
lines.push("| Command | Description |");
|
|
257
|
+
lines.push("|---------|-------------|");
|
|
258
|
+
const currentFile = info.filePath;
|
|
259
|
+
const fileMap = info.fileMap;
|
|
260
|
+
for (const sub of subcommands) {
|
|
261
|
+
const fullName = sub.fullPath.join(" ");
|
|
262
|
+
const desc = escapeTableCell(sub.description ?? "");
|
|
263
|
+
const subCommandPath = sub.fullPath.join(" ");
|
|
264
|
+
if (generateAnchors) {
|
|
265
|
+
const anchor = generateAnchor(sub.fullPath);
|
|
266
|
+
const subFile = fileMap?.[subCommandPath];
|
|
267
|
+
if (currentFile && subFile && currentFile !== subFile) {
|
|
268
|
+
const relativePath = getRelativePath(currentFile, subFile);
|
|
269
|
+
lines.push(`| [\`${fullName}\`](${relativePath}#${anchor}) | ${desc} |`);
|
|
270
|
+
} else lines.push(`| [\`${fullName}\`](#${anchor}) | ${desc} |`);
|
|
271
|
+
} else lines.push(`| \`${fullName}\` | ${desc} |`);
|
|
272
|
+
}
|
|
273
|
+
return lines.join("\n");
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Render examples as markdown
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* **Basic usage**
|
|
280
|
+
*
|
|
281
|
+
* ```bash
|
|
282
|
+
* $ greet World
|
|
283
|
+
* ```
|
|
284
|
+
*
|
|
285
|
+
* Output:
|
|
286
|
+
* ```
|
|
287
|
+
* Hello, World!
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
function renderExamplesDefault(examples, results, opts) {
|
|
291
|
+
if (examples.length === 0) return "";
|
|
292
|
+
const showOutput = opts?.showOutput ?? true;
|
|
293
|
+
const lines = [];
|
|
294
|
+
for (let i = 0; i < examples.length; i++) {
|
|
295
|
+
const example = examples[i];
|
|
296
|
+
if (!example) continue;
|
|
297
|
+
const result = results?.[i];
|
|
298
|
+
lines.push(`**${example.desc}**`);
|
|
299
|
+
lines.push("");
|
|
300
|
+
lines.push("```bash");
|
|
301
|
+
lines.push(`$ ${example.cmd}`);
|
|
302
|
+
if (showOutput) {
|
|
303
|
+
if (result) {
|
|
304
|
+
if (result.stdout) lines.push(result.stdout);
|
|
305
|
+
if (result.stderr) lines.push(`[stderr] ${result.stderr}`);
|
|
306
|
+
} else if (example.output) lines.push(example.output);
|
|
307
|
+
}
|
|
308
|
+
lines.push("```");
|
|
309
|
+
lines.push("");
|
|
310
|
+
}
|
|
311
|
+
while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
312
|
+
return lines.join("\n");
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Create command renderer with options
|
|
316
|
+
*/
|
|
317
|
+
function createCommandRenderer(options = {}) {
|
|
318
|
+
const { headingLevel = 1, optionStyle = "table", generateAnchors = true, includeSubcommandDetails = true, renderDescription: customRenderDescription, renderUsage: customRenderUsage, renderArguments: customRenderArguments, renderOptions: customRenderOptions, renderSubcommands: customRenderSubcommands, renderNotes: customRenderNotes, renderFooter: customRenderFooter, renderExamples: customRenderExamples } = options;
|
|
319
|
+
return (info) => {
|
|
320
|
+
const lines = [];
|
|
321
|
+
const effectiveLevel = Math.min(headingLevel + (info.depth - 1), 6);
|
|
322
|
+
const h = "#".repeat(effectiveLevel);
|
|
323
|
+
const title = info.commandPath || info.name;
|
|
324
|
+
lines.push(`${h} ${title}`);
|
|
325
|
+
lines.push("");
|
|
326
|
+
if (info.description) {
|
|
327
|
+
const context = {
|
|
328
|
+
content: info.description,
|
|
329
|
+
heading: "",
|
|
330
|
+
info
|
|
331
|
+
};
|
|
332
|
+
const content = customRenderDescription ? customRenderDescription(context) : context.content;
|
|
333
|
+
if (content) {
|
|
334
|
+
lines.push(content);
|
|
335
|
+
lines.push("");
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
{
|
|
339
|
+
const context = {
|
|
340
|
+
content: `**Usage**\n\n\`\`\`\n${renderUsage(info)}\n\`\`\``,
|
|
341
|
+
heading: "**Usage**",
|
|
342
|
+
info
|
|
343
|
+
};
|
|
344
|
+
const content = customRenderUsage ? customRenderUsage(context) : context.content;
|
|
345
|
+
if (content) {
|
|
346
|
+
lines.push(content);
|
|
347
|
+
lines.push("");
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (info.positionalArgs.length > 0) {
|
|
351
|
+
const renderArgs = (args, opts) => {
|
|
352
|
+
const style = opts?.style ?? optionStyle;
|
|
353
|
+
const withHeading = opts?.withHeading ?? true;
|
|
354
|
+
const content$1 = style === "table" ? renderArgumentsTableFromArray(args) : renderArgumentsListFromArray(args);
|
|
355
|
+
return withHeading ? `**Arguments**\n\n${content$1}` : content$1;
|
|
356
|
+
};
|
|
357
|
+
const context = {
|
|
358
|
+
args: info.positionalArgs,
|
|
359
|
+
render: renderArgs,
|
|
360
|
+
heading: "**Arguments**",
|
|
361
|
+
info
|
|
362
|
+
};
|
|
363
|
+
const content = customRenderArguments ? customRenderArguments(context) : renderArgs(context.args);
|
|
364
|
+
if (content) {
|
|
365
|
+
lines.push(content);
|
|
366
|
+
lines.push("");
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (info.options.length > 0) {
|
|
370
|
+
const renderOpts = (opts, renderOpts$1) => {
|
|
371
|
+
const style = renderOpts$1?.style ?? optionStyle;
|
|
372
|
+
const withHeading = renderOpts$1?.withHeading ?? true;
|
|
373
|
+
const content$1 = style === "table" ? renderOptionsTableFromArray(opts) : renderOptionsListFromArray(opts);
|
|
374
|
+
return withHeading ? `**Options**\n\n${content$1}` : content$1;
|
|
375
|
+
};
|
|
376
|
+
const context = {
|
|
377
|
+
options: info.options,
|
|
378
|
+
render: renderOpts,
|
|
379
|
+
heading: "**Options**",
|
|
380
|
+
info
|
|
381
|
+
};
|
|
382
|
+
const content = customRenderOptions ? customRenderOptions(context) : renderOpts(context.options);
|
|
383
|
+
if (content) {
|
|
384
|
+
lines.push(content);
|
|
385
|
+
lines.push("");
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
if (info.subCommands.length > 0) {
|
|
389
|
+
const effectiveAnchors = generateAnchors && includeSubcommandDetails;
|
|
390
|
+
const renderSubs = (subs, opts) => {
|
|
391
|
+
const anchors = opts?.generateAnchors ?? effectiveAnchors;
|
|
392
|
+
const withHeading = opts?.withHeading ?? true;
|
|
393
|
+
const content$1 = renderSubcommandsTableFromArray(subs, info, anchors);
|
|
394
|
+
return withHeading ? `**Commands**\n\n${content$1}` : content$1;
|
|
395
|
+
};
|
|
396
|
+
const context = {
|
|
397
|
+
subcommands: info.subCommands,
|
|
398
|
+
render: renderSubs,
|
|
399
|
+
heading: "**Commands**",
|
|
400
|
+
info
|
|
401
|
+
};
|
|
402
|
+
const content = customRenderSubcommands ? customRenderSubcommands(context) : renderSubs(context.subcommands);
|
|
403
|
+
if (content) {
|
|
404
|
+
lines.push(content);
|
|
405
|
+
lines.push("");
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
if (info.examples && info.examples.length > 0) {
|
|
409
|
+
const renderEx = (examples, results, opts) => {
|
|
410
|
+
const withHeading = opts?.withHeading ?? true;
|
|
411
|
+
const content$1 = renderExamplesDefault(examples, results, opts);
|
|
412
|
+
return withHeading ? `**Examples**\n\n${content$1}` : content$1;
|
|
413
|
+
};
|
|
414
|
+
const context = {
|
|
415
|
+
examples: info.examples,
|
|
416
|
+
results: info.exampleResults,
|
|
417
|
+
render: renderEx,
|
|
418
|
+
heading: "**Examples**",
|
|
419
|
+
info
|
|
420
|
+
};
|
|
421
|
+
const content = customRenderExamples ? customRenderExamples(context) : renderEx(context.examples, context.results);
|
|
422
|
+
if (content) {
|
|
423
|
+
lines.push(content);
|
|
424
|
+
lines.push("");
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (info.notes) {
|
|
428
|
+
const context = {
|
|
429
|
+
content: `**Notes**\n\n${info.notes}`,
|
|
430
|
+
heading: "**Notes**",
|
|
431
|
+
info
|
|
432
|
+
};
|
|
433
|
+
const content = customRenderNotes ? customRenderNotes(context) : context.content;
|
|
434
|
+
if (content) {
|
|
435
|
+
lines.push(content);
|
|
436
|
+
lines.push("");
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
{
|
|
440
|
+
const context = {
|
|
441
|
+
content: "",
|
|
442
|
+
heading: "",
|
|
443
|
+
info
|
|
444
|
+
};
|
|
445
|
+
const content = customRenderFooter ? customRenderFooter(context) : context.content;
|
|
446
|
+
if (content) {
|
|
447
|
+
lines.push(content);
|
|
448
|
+
lines.push("");
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
452
|
+
lines.push("");
|
|
453
|
+
return lines.join("\n");
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Default renderers presets
|
|
458
|
+
*/
|
|
459
|
+
const defaultRenderers = {
|
|
460
|
+
command: (options) => createCommandRenderer(options),
|
|
461
|
+
tableStyle: createCommandRenderer({ optionStyle: "table" }),
|
|
462
|
+
listStyle: createCommandRenderer({ optionStyle: "list" })
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
//#endregion
|
|
466
|
+
//#region src/docs/doc-comparator.ts
|
|
467
|
+
/**
|
|
468
|
+
* Compare generated content with existing file
|
|
469
|
+
*/
|
|
470
|
+
function compareWithExisting(generatedContent, filePath) {
|
|
471
|
+
const absolutePath = path.resolve(filePath);
|
|
472
|
+
if (!fs.existsSync(absolutePath)) return {
|
|
473
|
+
match: false,
|
|
474
|
+
fileExists: false
|
|
475
|
+
};
|
|
476
|
+
const existingContent = fs.readFileSync(absolutePath, "utf-8");
|
|
477
|
+
if (generatedContent === existingContent) return {
|
|
478
|
+
match: true,
|
|
479
|
+
fileExists: true
|
|
480
|
+
};
|
|
481
|
+
return {
|
|
482
|
+
match: false,
|
|
483
|
+
diff: formatDiff(existingContent, generatedContent),
|
|
484
|
+
fileExists: true
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Format diff between two strings in unified diff format
|
|
489
|
+
*/
|
|
490
|
+
function formatDiff(expected, actual) {
|
|
491
|
+
const expectedLines = expected.split("\n");
|
|
492
|
+
const actualLines = actual.split("\n");
|
|
493
|
+
const result = [];
|
|
494
|
+
result.push("--- existing");
|
|
495
|
+
result.push("+++ generated");
|
|
496
|
+
result.push("");
|
|
497
|
+
const maxLines = Math.max(expectedLines.length, actualLines.length);
|
|
498
|
+
let inChunk = false;
|
|
499
|
+
let chunkStart = 0;
|
|
500
|
+
const chunk = [];
|
|
501
|
+
const flushChunk = () => {
|
|
502
|
+
if (chunk.length > 0) {
|
|
503
|
+
result.push(`@@ -${chunkStart + 1},${chunk.length} @@`);
|
|
504
|
+
result.push(...chunk);
|
|
505
|
+
chunk.length = 0;
|
|
506
|
+
}
|
|
507
|
+
inChunk = false;
|
|
508
|
+
};
|
|
509
|
+
for (let i = 0; i < maxLines; i++) {
|
|
510
|
+
const expectedLine = expectedLines[i];
|
|
511
|
+
const actualLine = actualLines[i];
|
|
512
|
+
if (expectedLine === actualLine) {
|
|
513
|
+
if (inChunk) {
|
|
514
|
+
chunk.push(` ${expectedLine ?? ""}`);
|
|
515
|
+
const lastChangeIndex = chunk.findIndex((line, idx) => (line.startsWith("-") || line.startsWith("+")) && chunk.slice(idx + 1).every((l) => l.startsWith(" ")));
|
|
516
|
+
if (lastChangeIndex !== -1 && chunk.length - lastChangeIndex > 3) flushChunk();
|
|
517
|
+
}
|
|
518
|
+
} else {
|
|
519
|
+
if (!inChunk) {
|
|
520
|
+
inChunk = true;
|
|
521
|
+
chunkStart = i;
|
|
522
|
+
const contextStart = Math.max(0, i - 3);
|
|
523
|
+
for (let j = contextStart; j < i; j++) chunk.push(` ${expectedLines[j] ?? ""}`);
|
|
524
|
+
}
|
|
525
|
+
if (expectedLine !== void 0 && (actualLine === void 0 || expectedLine !== actualLine)) chunk.push(`-${expectedLine}`);
|
|
526
|
+
if (actualLine !== void 0 && (expectedLine === void 0 || expectedLine !== actualLine)) chunk.push(`+${actualLine}`);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
flushChunk();
|
|
530
|
+
return result.join("\n");
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Write content to file, creating directories if needed
|
|
534
|
+
*/
|
|
535
|
+
function writeFile(filePath, content) {
|
|
536
|
+
const absolutePath = path.resolve(filePath);
|
|
537
|
+
const dir = path.dirname(absolutePath);
|
|
538
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
539
|
+
fs.writeFileSync(absolutePath, content, "utf-8");
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Read file content if it exists
|
|
543
|
+
* Returns null if file does not exist
|
|
544
|
+
*/
|
|
545
|
+
function readFile(filePath) {
|
|
546
|
+
const absolutePath = path.resolve(filePath);
|
|
547
|
+
if (!fs.existsSync(absolutePath)) return null;
|
|
548
|
+
return fs.readFileSync(absolutePath, "utf-8");
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Delete file if it exists
|
|
552
|
+
* @param filePath - Path to the file to delete
|
|
553
|
+
* @param fileSystem - Optional fs implementation (useful when fs is mocked)
|
|
554
|
+
*/
|
|
555
|
+
function deleteFile(filePath, fileSystem = fs) {
|
|
556
|
+
const absolutePath = path.resolve(filePath);
|
|
557
|
+
if (fileSystem.existsSync(absolutePath)) fileSystem.unlinkSync(absolutePath);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
//#endregion
|
|
561
|
+
//#region src/docs/doc-generator.ts
|
|
562
|
+
/**
|
|
563
|
+
* Build CommandInfo from a command
|
|
564
|
+
*/
|
|
565
|
+
async function buildCommandInfo(command, rootName, commandPath = []) {
|
|
566
|
+
const extracted = getExtractedFields(command);
|
|
567
|
+
const positionalArgs = extracted?.fields.filter((f) => f.positional) ?? [];
|
|
568
|
+
const options = extracted?.fields.filter((f) => !f.positional) ?? [];
|
|
569
|
+
const subCommands = [];
|
|
570
|
+
if (command.subCommands) for (const [name, subCmd] of Object.entries(command.subCommands)) {
|
|
571
|
+
const resolved = await resolveLazyCommand(subCmd);
|
|
572
|
+
const fullPath = [...commandPath, name];
|
|
573
|
+
subCommands.push({
|
|
574
|
+
name,
|
|
575
|
+
description: resolved.description,
|
|
576
|
+
fullPath
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
return {
|
|
580
|
+
name: command.name ?? "",
|
|
581
|
+
description: command.description,
|
|
582
|
+
fullCommandPath: commandPath.length > 0 ? `${rootName} ${commandPath.join(" ")}` : rootName,
|
|
583
|
+
commandPath: commandPath.join(" "),
|
|
584
|
+
depth: commandPath.length + 1,
|
|
585
|
+
positionalArgs,
|
|
586
|
+
options,
|
|
587
|
+
subCommands,
|
|
588
|
+
extracted,
|
|
589
|
+
command,
|
|
590
|
+
notes: command.notes,
|
|
591
|
+
examples: command.examples
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Collect all commands with their paths
|
|
596
|
+
* Returns a map of command path -> CommandInfo
|
|
597
|
+
*/
|
|
598
|
+
async function collectAllCommands(command, rootName) {
|
|
599
|
+
const root = rootName ?? command.name ?? "command";
|
|
600
|
+
const result = /* @__PURE__ */ new Map();
|
|
601
|
+
async function traverse(cmd, path$1) {
|
|
602
|
+
const info = await buildCommandInfo(cmd, root, path$1);
|
|
603
|
+
const pathKey = path$1.join(" ");
|
|
604
|
+
result.set(pathKey, info);
|
|
605
|
+
if (cmd.subCommands) for (const [name, subCmd] of Object.entries(cmd.subCommands)) await traverse(await resolveLazyCommand(subCmd), [...path$1, name]);
|
|
606
|
+
}
|
|
607
|
+
await traverse(command, []);
|
|
608
|
+
return result;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
//#endregion
|
|
612
|
+
//#region src/docs/example-executor.ts
|
|
613
|
+
/**
|
|
614
|
+
* Execute examples for a command and capture output
|
|
615
|
+
*
|
|
616
|
+
* @param examples - Examples to execute
|
|
617
|
+
* @param config - Execution configuration (mock setup/cleanup)
|
|
618
|
+
* @param rootCommand - Root command to execute against
|
|
619
|
+
* @param commandPath - Command path for subcommands (e.g., ["config", "get"])
|
|
620
|
+
* @returns Array of execution results with captured stdout/stderr
|
|
621
|
+
*/
|
|
622
|
+
async function executeExamples(examples, config, rootCommand, commandPath = []) {
|
|
623
|
+
const results = [];
|
|
624
|
+
if (config.mock) await config.mock();
|
|
625
|
+
try {
|
|
626
|
+
for (const example of examples) {
|
|
627
|
+
const result = await executeSingleExample(example, rootCommand, commandPath);
|
|
628
|
+
results.push(result);
|
|
629
|
+
}
|
|
630
|
+
} finally {
|
|
631
|
+
if (config.cleanup) await config.cleanup();
|
|
632
|
+
}
|
|
633
|
+
return results;
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Execute a single example and capture output
|
|
637
|
+
*/
|
|
638
|
+
async function executeSingleExample(example, rootCommand, commandPath) {
|
|
639
|
+
const exampleArgs = parseExampleCmd(example.cmd);
|
|
640
|
+
const argv = [...commandPath, ...exampleArgs];
|
|
641
|
+
const collector = createLogCollector({ passthrough: false });
|
|
642
|
+
collector.start();
|
|
643
|
+
let success = true;
|
|
644
|
+
try {
|
|
645
|
+
const { runCommand } = await import("../runner-C_m6ve-j.js");
|
|
646
|
+
const result = await runCommand(rootCommand, argv);
|
|
647
|
+
success = result.success;
|
|
648
|
+
if (!result.success && result.error) console.error(result.error.message);
|
|
649
|
+
} catch (error) {
|
|
650
|
+
success = false;
|
|
651
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
652
|
+
} finally {
|
|
653
|
+
collector.stop();
|
|
654
|
+
}
|
|
655
|
+
const logs = collector.getLogs();
|
|
656
|
+
const stdout = logs.entries.filter((e) => e.stream === "stdout").map((e) => e.message).join("\n");
|
|
657
|
+
const stderr = logs.entries.filter((e) => e.stream === "stderr").map((e) => e.message).join("\n");
|
|
658
|
+
return {
|
|
659
|
+
cmd: example.cmd,
|
|
660
|
+
desc: example.desc,
|
|
661
|
+
expectedOutput: example.output,
|
|
662
|
+
stdout,
|
|
663
|
+
stderr,
|
|
664
|
+
success
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Parse example command string into argv array
|
|
669
|
+
* Handles quoted strings (single and double quotes)
|
|
670
|
+
*
|
|
671
|
+
* @example
|
|
672
|
+
* parseExampleCmd('World') // ['World']
|
|
673
|
+
* parseExampleCmd('--name "John Doe"') // ['--name', 'John Doe']
|
|
674
|
+
* parseExampleCmd("--greeting 'Hello World'") // ['--greeting', 'Hello World']
|
|
675
|
+
*/
|
|
676
|
+
function parseExampleCmd(cmd) {
|
|
677
|
+
const args = [];
|
|
678
|
+
let current = "";
|
|
679
|
+
let inQuote = false;
|
|
680
|
+
let quoteChar = "";
|
|
681
|
+
for (let i = 0; i < cmd.length; i++) {
|
|
682
|
+
const char = cmd[i];
|
|
683
|
+
if ((char === "\"" || char === "'") && !inQuote) {
|
|
684
|
+
inQuote = true;
|
|
685
|
+
quoteChar = char;
|
|
686
|
+
} else if (char === quoteChar && inQuote) {
|
|
687
|
+
inQuote = false;
|
|
688
|
+
quoteChar = "";
|
|
689
|
+
} else if (char === " " && !inQuote) {
|
|
690
|
+
if (current) {
|
|
691
|
+
args.push(current);
|
|
692
|
+
current = "";
|
|
693
|
+
}
|
|
694
|
+
} else current += char;
|
|
695
|
+
}
|
|
696
|
+
if (current) args.push(current);
|
|
697
|
+
return args;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
//#endregion
|
|
701
|
+
//#region src/docs/types.ts
|
|
702
|
+
/**
|
|
703
|
+
* Environment variable name for update mode
|
|
704
|
+
*/
|
|
705
|
+
const UPDATE_GOLDEN_ENV = "POLITTY_DOCS_UPDATE";
|
|
706
|
+
/**
|
|
707
|
+
* Marker prefix for command sections in generated documentation
|
|
708
|
+
* Format: <!-- politty:command:<path>:start --> ... <!-- politty:command:<path>:end -->
|
|
709
|
+
*/
|
|
710
|
+
const COMMAND_MARKER_PREFIX = "politty:command";
|
|
711
|
+
/**
|
|
712
|
+
* Generate start marker for a command section
|
|
713
|
+
*/
|
|
714
|
+
function commandStartMarker(commandPath) {
|
|
715
|
+
return `<!-- ${COMMAND_MARKER_PREFIX}:${commandPath}:start -->`;
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Generate end marker for a command section
|
|
719
|
+
*/
|
|
720
|
+
function commandEndMarker(commandPath) {
|
|
721
|
+
return `<!-- ${COMMAND_MARKER_PREFIX}:${commandPath}:end -->`;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
//#endregion
|
|
725
|
+
//#region src/docs/golden-test.ts
|
|
726
|
+
/**
|
|
727
|
+
* Apply formatter to content if provided
|
|
728
|
+
* Supports both sync and async formatters
|
|
729
|
+
*/
|
|
730
|
+
async function applyFormatter(content, formatter) {
|
|
731
|
+
if (!formatter) return content;
|
|
732
|
+
return await formatter(content);
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Check if update mode is enabled via environment variable
|
|
736
|
+
*/
|
|
737
|
+
function isUpdateMode() {
|
|
738
|
+
const value = process.env[UPDATE_GOLDEN_ENV];
|
|
739
|
+
return value === "true" || value === "1";
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Normalize file mapping entry to FileConfig
|
|
743
|
+
*/
|
|
744
|
+
function normalizeFileConfig(config) {
|
|
745
|
+
if (Array.isArray(config)) return { commands: config };
|
|
746
|
+
return config;
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Check if a command path is a subcommand of another
|
|
750
|
+
*/
|
|
751
|
+
function isSubcommandOf(childPath, parentPath) {
|
|
752
|
+
if (parentPath === "") return true;
|
|
753
|
+
if (childPath === parentPath) return true;
|
|
754
|
+
return childPath.startsWith(parentPath + " ");
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Check if a pattern contains wildcards
|
|
758
|
+
*/
|
|
759
|
+
function containsWildcard(pattern) {
|
|
760
|
+
return pattern.includes("*");
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Check if a command path matches a wildcard pattern
|
|
764
|
+
* - `*` matches any single command segment
|
|
765
|
+
* - Pattern segments are space-separated
|
|
766
|
+
*
|
|
767
|
+
* @example
|
|
768
|
+
* matchesWildcard("config get", "* *") // true
|
|
769
|
+
* matchesWildcard("config", "* *") // false
|
|
770
|
+
* matchesWildcard("config get", "config *") // true
|
|
771
|
+
* matchesWildcard("greet", "*") // true
|
|
772
|
+
*/
|
|
773
|
+
function matchesWildcard(path$1, pattern) {
|
|
774
|
+
const pathSegments = path$1 === "" ? [] : path$1.split(" ");
|
|
775
|
+
const patternSegments = pattern === "" ? [] : pattern.split(" ");
|
|
776
|
+
if (pathSegments.length !== patternSegments.length) return false;
|
|
777
|
+
for (let i = 0; i < patternSegments.length; i++) {
|
|
778
|
+
const patternSeg = patternSegments[i];
|
|
779
|
+
const pathSeg = pathSegments[i];
|
|
780
|
+
if (patternSeg !== "*" && patternSeg !== pathSeg) return false;
|
|
781
|
+
}
|
|
782
|
+
return true;
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Expand a wildcard pattern to matching command paths
|
|
786
|
+
*/
|
|
787
|
+
function expandWildcardPattern(pattern, allCommands) {
|
|
788
|
+
const matches = [];
|
|
789
|
+
for (const cmdPath of allCommands.keys()) if (matchesWildcard(cmdPath, pattern)) matches.push(cmdPath);
|
|
790
|
+
return matches;
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Check if a path matches any ignore pattern (with wildcard support)
|
|
794
|
+
* For wildcard patterns, also ignores subcommands of matched commands
|
|
795
|
+
*/
|
|
796
|
+
function matchesIgnorePattern(path$1, ignorePattern) {
|
|
797
|
+
if (containsWildcard(ignorePattern)) {
|
|
798
|
+
if (matchesWildcard(path$1, ignorePattern)) return true;
|
|
799
|
+
const pathSegments = path$1 === "" ? [] : path$1.split(" ");
|
|
800
|
+
const patternSegments = ignorePattern === "" ? [] : ignorePattern.split(" ");
|
|
801
|
+
if (pathSegments.length > patternSegments.length) return matchesWildcard(pathSegments.slice(0, patternSegments.length).join(" "), ignorePattern);
|
|
802
|
+
return false;
|
|
803
|
+
}
|
|
804
|
+
return isSubcommandOf(path$1, ignorePattern);
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Expand command paths to include all subcommands (with wildcard support)
|
|
808
|
+
*/
|
|
809
|
+
function expandCommandPaths(commandPaths, allCommands) {
|
|
810
|
+
const expanded = /* @__PURE__ */ new Set();
|
|
811
|
+
for (const cmdPath of commandPaths) if (containsWildcard(cmdPath)) {
|
|
812
|
+
const matches = expandWildcardPattern(cmdPath, allCommands);
|
|
813
|
+
for (const match of matches) {
|
|
814
|
+
expanded.add(match);
|
|
815
|
+
for (const path$1 of allCommands.keys()) if (isSubcommandOf(path$1, match)) expanded.add(path$1);
|
|
816
|
+
}
|
|
817
|
+
} else {
|
|
818
|
+
if (allCommands.has(cmdPath)) expanded.add(cmdPath);
|
|
819
|
+
for (const path$1 of allCommands.keys()) if (isSubcommandOf(path$1, cmdPath)) expanded.add(path$1);
|
|
820
|
+
}
|
|
821
|
+
return Array.from(expanded);
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Filter out ignored commands (with wildcard support)
|
|
825
|
+
*/
|
|
826
|
+
function filterIgnoredCommands(commandPaths, ignores) {
|
|
827
|
+
return commandPaths.filter((path$1) => {
|
|
828
|
+
return !ignores.some((ignorePattern) => matchesIgnorePattern(path$1, ignorePattern));
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* Validate that there are no conflicts between files and ignores (with wildcard support)
|
|
833
|
+
*/
|
|
834
|
+
function validateNoConflicts(filesCommands, ignores, allCommands) {
|
|
835
|
+
const conflicts = [];
|
|
836
|
+
for (const filePattern of filesCommands) {
|
|
837
|
+
const filePaths = containsWildcard(filePattern) ? expandWildcardPattern(filePattern, allCommands) : [filePattern];
|
|
838
|
+
for (const filePath of filePaths) for (const ignorePattern of ignores) if (containsWildcard(ignorePattern)) {
|
|
839
|
+
if (matchesWildcard(filePath, ignorePattern)) conflicts.push(`"${filePath}" is both in files and ignored by "${ignorePattern}"`);
|
|
840
|
+
} else if (filePath === ignorePattern || isSubcommandOf(filePath, ignorePattern)) conflicts.push(`"${filePath}" is both in files and ignored by "${ignorePattern}"`);
|
|
841
|
+
}
|
|
842
|
+
if (conflicts.length > 0) throw new Error(`Conflict between files and ignores:\n - ${conflicts.join("\n - ")}`);
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* Validate that all ignored paths exist in the command tree (with wildcard support)
|
|
846
|
+
*/
|
|
847
|
+
function validateIgnoresExist(ignores, allCommands) {
|
|
848
|
+
const nonExistent = [];
|
|
849
|
+
for (const ignorePattern of ignores) if (containsWildcard(ignorePattern)) {
|
|
850
|
+
if (expandWildcardPattern(ignorePattern, allCommands).length === 0) nonExistent.push(`"${ignorePattern}"`);
|
|
851
|
+
} else if (!allCommands.has(ignorePattern)) nonExistent.push(`"${ignorePattern}"`);
|
|
852
|
+
if (nonExistent.length > 0) throw new Error(`Ignored command paths do not exist: ${nonExistent.join(", ")}`);
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Sort command paths in depth-first order while preserving the specified command order
|
|
856
|
+
* Parent commands are immediately followed by their subcommands
|
|
857
|
+
*/
|
|
858
|
+
function sortDepthFirst(commandPaths, specifiedOrder) {
|
|
859
|
+
const pathSet = new Set(commandPaths);
|
|
860
|
+
const topLevelPaths = specifiedOrder.filter((cmd) => pathSet.has(cmd));
|
|
861
|
+
for (const path$1 of commandPaths) if ((path$1 === "" ? 0 : path$1.split(" ").length) === 1 && !topLevelPaths.includes(path$1)) topLevelPaths.push(path$1);
|
|
862
|
+
const result = [];
|
|
863
|
+
const visited = /* @__PURE__ */ new Set();
|
|
864
|
+
function addWithChildren(cmdPath) {
|
|
865
|
+
if (visited.has(cmdPath) || !pathSet.has(cmdPath)) return;
|
|
866
|
+
visited.add(cmdPath);
|
|
867
|
+
result.push(cmdPath);
|
|
868
|
+
const children = commandPaths.filter((p) => {
|
|
869
|
+
if (p === cmdPath || visited.has(p)) return false;
|
|
870
|
+
if (cmdPath === "") return p.split(" ").length === 1;
|
|
871
|
+
return p.startsWith(cmdPath + " ") && p.split(" ").length === cmdPath.split(" ").length + 1;
|
|
872
|
+
}).sort((a, b) => a.localeCompare(b));
|
|
873
|
+
for (const child of children) addWithChildren(child);
|
|
874
|
+
}
|
|
875
|
+
for (const topLevel of topLevelPaths) addWithChildren(topLevel);
|
|
876
|
+
for (const path$1 of commandPaths) if (!visited.has(path$1)) result.push(path$1);
|
|
877
|
+
return result;
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Generate file header from FileConfig
|
|
881
|
+
*/
|
|
882
|
+
function generateFileHeader(fileConfig) {
|
|
883
|
+
if (!fileConfig.title && !fileConfig.description) return null;
|
|
884
|
+
const parts = [];
|
|
885
|
+
if (fileConfig.title) parts.push(`# ${fileConfig.title}`);
|
|
886
|
+
if (fileConfig.description) {
|
|
887
|
+
parts.push("");
|
|
888
|
+
parts.push(fileConfig.description);
|
|
889
|
+
}
|
|
890
|
+
parts.push("");
|
|
891
|
+
return parts.join("\n");
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Extract a command section from content using markers
|
|
895
|
+
* Returns the content between start and end markers (including markers)
|
|
896
|
+
*/
|
|
897
|
+
function extractCommandSection(content, commandPath) {
|
|
898
|
+
const startMarker = commandStartMarker(commandPath);
|
|
899
|
+
const endMarker = commandEndMarker(commandPath);
|
|
900
|
+
const startIndex = content.indexOf(startMarker);
|
|
901
|
+
if (startIndex === -1) return null;
|
|
902
|
+
const endIndex = content.indexOf(endMarker, startIndex);
|
|
903
|
+
if (endIndex === -1) return null;
|
|
904
|
+
return content.slice(startIndex, endIndex + endMarker.length);
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* Replace a command section in content using markers
|
|
908
|
+
* Returns the updated content with the new section
|
|
909
|
+
*/
|
|
910
|
+
function replaceCommandSection(content, commandPath, newSection) {
|
|
911
|
+
const startMarker = commandStartMarker(commandPath);
|
|
912
|
+
const endMarker = commandEndMarker(commandPath);
|
|
913
|
+
const startIndex = content.indexOf(startMarker);
|
|
914
|
+
if (startIndex === -1) return null;
|
|
915
|
+
const endIndex = content.indexOf(endMarker, startIndex);
|
|
916
|
+
if (endIndex === -1) return null;
|
|
917
|
+
return content.slice(0, startIndex) + newSection + content.slice(endIndex + endMarker.length);
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Insert a command section at the correct position based on specified order
|
|
921
|
+
* Returns the updated content with the section inserted at the right position
|
|
922
|
+
*/
|
|
923
|
+
function insertCommandSection(content, commandPath, newSection, specifiedOrder) {
|
|
924
|
+
const targetIndex = specifiedOrder.indexOf(commandPath);
|
|
925
|
+
if (targetIndex === -1) return content.trimEnd() + "\n\n" + newSection + "\n";
|
|
926
|
+
for (let i = targetIndex + 1; i < specifiedOrder.length; i++) {
|
|
927
|
+
const nextCmd = specifiedOrder[i];
|
|
928
|
+
if (nextCmd === void 0) continue;
|
|
929
|
+
const nextMarker = commandStartMarker(nextCmd);
|
|
930
|
+
const nextIndex = content.indexOf(nextMarker);
|
|
931
|
+
if (nextIndex !== -1) {
|
|
932
|
+
let insertPos = nextIndex;
|
|
933
|
+
while (insertPos > 0 && content[insertPos - 1] === "\n") insertPos--;
|
|
934
|
+
if (insertPos < nextIndex) insertPos++;
|
|
935
|
+
return content.slice(0, insertPos) + newSection + "\n" + content.slice(nextIndex);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
for (let i = targetIndex - 1; i >= 0; i--) {
|
|
939
|
+
const prevCmd = specifiedOrder[i];
|
|
940
|
+
if (prevCmd === void 0) continue;
|
|
941
|
+
const prevEndMarker = commandEndMarker(prevCmd);
|
|
942
|
+
const prevEndIndex = content.indexOf(prevEndMarker);
|
|
943
|
+
if (prevEndIndex !== -1) {
|
|
944
|
+
const insertPos = prevEndIndex + prevEndMarker.length;
|
|
945
|
+
return content.slice(0, insertPos) + "\n" + newSection + content.slice(insertPos);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
return content.trimEnd() + "\n" + newSection + "\n";
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* Find which file contains a specific command
|
|
952
|
+
*/
|
|
953
|
+
function findFileForCommand(commandPath, files, allCommands, ignores) {
|
|
954
|
+
for (const [filePath, fileConfigRaw] of Object.entries(files)) {
|
|
955
|
+
const specifiedCommands = normalizeFileConfig(fileConfigRaw).commands;
|
|
956
|
+
if (filterIgnoredCommands(expandCommandPaths(specifiedCommands, allCommands), ignores).includes(commandPath)) return filePath;
|
|
957
|
+
}
|
|
958
|
+
return null;
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* Find which target commands are contained in a file
|
|
962
|
+
* Also expands each target command to include subcommands that are NOT explicitly in specifiedCommands
|
|
963
|
+
*/
|
|
964
|
+
function findTargetCommandsInFile(targetCommands, filePath, files, allCommands, ignores) {
|
|
965
|
+
const fileConfigRaw = files[filePath];
|
|
966
|
+
if (!fileConfigRaw) return [];
|
|
967
|
+
const specifiedCommands = normalizeFileConfig(fileConfigRaw).commands;
|
|
968
|
+
const commandPaths = filterIgnoredCommands(expandCommandPaths(specifiedCommands, allCommands), ignores);
|
|
969
|
+
const expandedTargets = /* @__PURE__ */ new Set();
|
|
970
|
+
for (const targetCmd of targetCommands) {
|
|
971
|
+
if (!commandPaths.includes(targetCmd)) continue;
|
|
972
|
+
expandedTargets.add(targetCmd);
|
|
973
|
+
for (const cmdPath of commandPaths) if (isSubcommandOf(cmdPath, targetCmd) && !specifiedCommands.includes(cmdPath)) expandedTargets.add(cmdPath);
|
|
974
|
+
}
|
|
975
|
+
return Array.from(expandedTargets);
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* Generate a single command section with markers
|
|
979
|
+
*/
|
|
980
|
+
function generateCommandSection(cmdPath, allCommands, render, filePath, fileMap) {
|
|
981
|
+
const info = allCommands.get(cmdPath);
|
|
982
|
+
if (!info) return null;
|
|
983
|
+
const renderedSection = render({
|
|
984
|
+
...info,
|
|
985
|
+
filePath,
|
|
986
|
+
fileMap
|
|
987
|
+
});
|
|
988
|
+
return [
|
|
989
|
+
commandStartMarker(cmdPath),
|
|
990
|
+
renderedSection,
|
|
991
|
+
commandEndMarker(cmdPath)
|
|
992
|
+
].join("\n");
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Generate markdown for a file containing multiple commands
|
|
996
|
+
* Each command section is wrapped with markers for partial validation
|
|
997
|
+
*/
|
|
998
|
+
function generateFileMarkdown(commandPaths, allCommands, render, filePath, fileMap, specifiedOrder, fileConfig) {
|
|
999
|
+
const sections = [];
|
|
1000
|
+
const header = fileConfig ? generateFileHeader(fileConfig) : null;
|
|
1001
|
+
if (header) sections.push(header);
|
|
1002
|
+
const sortedPaths = sortDepthFirst(commandPaths, specifiedOrder ?? []);
|
|
1003
|
+
for (const cmdPath of sortedPaths) {
|
|
1004
|
+
const section = generateCommandSection(cmdPath, allCommands, render, filePath, fileMap);
|
|
1005
|
+
if (section) sections.push(section);
|
|
1006
|
+
}
|
|
1007
|
+
return sections.join("\n");
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Build a map of command path to file path
|
|
1011
|
+
*/
|
|
1012
|
+
function buildFileMap(files, allCommands, ignores) {
|
|
1013
|
+
const fileMap = {};
|
|
1014
|
+
for (const [filePath, fileConfigRaw] of Object.entries(files)) {
|
|
1015
|
+
const specifiedCommands = normalizeFileConfig(fileConfigRaw).commands;
|
|
1016
|
+
const commandPaths = filterIgnoredCommands(expandCommandPaths(specifiedCommands, allCommands), ignores);
|
|
1017
|
+
for (const cmdPath of commandPaths) fileMap[cmdPath] = filePath;
|
|
1018
|
+
}
|
|
1019
|
+
return fileMap;
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Execute examples for commands based on configuration
|
|
1023
|
+
*/
|
|
1024
|
+
async function executeConfiguredExamples(allCommands, examplesConfig, rootCommand) {
|
|
1025
|
+
for (const [cmdPath, cmdConfig] of Object.entries(examplesConfig)) {
|
|
1026
|
+
const commandInfo = allCommands.get(cmdPath);
|
|
1027
|
+
if (!commandInfo?.examples?.length) continue;
|
|
1028
|
+
const config = cmdConfig === true ? {} : cmdConfig;
|
|
1029
|
+
const commandPath = cmdPath ? cmdPath.split(" ") : [];
|
|
1030
|
+
commandInfo.exampleResults = await executeExamples(commandInfo.examples, config, rootCommand, commandPath);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* Generate documentation from command definition
|
|
1035
|
+
*/
|
|
1036
|
+
async function generateDoc(config) {
|
|
1037
|
+
const { command, files, ignores = [], format = {}, formatter, examples: examplesConfig, targetCommands } = config;
|
|
1038
|
+
const updateMode = isUpdateMode();
|
|
1039
|
+
const allCommands = await collectAllCommands(command);
|
|
1040
|
+
if (examplesConfig) await executeConfiguredExamples(allCommands, examplesConfig, command);
|
|
1041
|
+
const allFilesCommands = [];
|
|
1042
|
+
for (const fileConfigRaw of Object.values(files)) {
|
|
1043
|
+
const fileConfig = normalizeFileConfig(fileConfigRaw);
|
|
1044
|
+
allFilesCommands.push(...fileConfig.commands);
|
|
1045
|
+
}
|
|
1046
|
+
validateIgnoresExist(ignores, allCommands);
|
|
1047
|
+
validateNoConflicts(allFilesCommands, ignores, allCommands);
|
|
1048
|
+
const fileMap = buildFileMap(files, allCommands, ignores);
|
|
1049
|
+
const results = [];
|
|
1050
|
+
let hasError = false;
|
|
1051
|
+
if (targetCommands && targetCommands.length > 0) {
|
|
1052
|
+
for (const targetCommand of targetCommands) if (!findFileForCommand(targetCommand, files, allCommands, ignores)) throw new Error(`Target command "${targetCommand}" not found in any file configuration`);
|
|
1053
|
+
}
|
|
1054
|
+
for (const [filePath, fileConfigRaw] of Object.entries(files)) {
|
|
1055
|
+
const fileConfig = normalizeFileConfig(fileConfigRaw);
|
|
1056
|
+
const specifiedCommands = fileConfig.commands;
|
|
1057
|
+
if (specifiedCommands.length === 0) continue;
|
|
1058
|
+
const commandPaths = filterIgnoredCommands(expandCommandPaths(specifiedCommands, allCommands), ignores);
|
|
1059
|
+
if (commandPaths.length === 0) continue;
|
|
1060
|
+
const minDepth = Math.min(...commandPaths.map((p) => allCommands.get(p)?.depth ?? 1));
|
|
1061
|
+
const adjustedHeadingLevel = Math.max(1, (format?.headingLevel ?? 1) - (minDepth - 1));
|
|
1062
|
+
const fileRenderer = createCommandRenderer({
|
|
1063
|
+
...format,
|
|
1064
|
+
headingLevel: adjustedHeadingLevel
|
|
1065
|
+
});
|
|
1066
|
+
const render = fileConfig.render ?? fileRenderer;
|
|
1067
|
+
if (targetCommands !== void 0 && targetCommands.length > 0) {
|
|
1068
|
+
const fileTargetCommands = findTargetCommandsInFile(targetCommands, filePath, files, allCommands, ignores);
|
|
1069
|
+
if (fileTargetCommands.length === 0) continue;
|
|
1070
|
+
let existingContent = readFile(filePath);
|
|
1071
|
+
let fileStatus = "match";
|
|
1072
|
+
const diffs = [];
|
|
1073
|
+
for (const targetCommand of fileTargetCommands) {
|
|
1074
|
+
const rawSection = generateCommandSection(targetCommand, allCommands, render, filePath, fileMap);
|
|
1075
|
+
if (!rawSection) throw new Error(`Target command "${targetCommand}" not found in commands`);
|
|
1076
|
+
const header = targetCommand === "" && fileConfig ? generateFileHeader(fileConfig) : null;
|
|
1077
|
+
const generatedSection = await applyFormatter(header ? `${header}\n${rawSection}` : rawSection, formatter);
|
|
1078
|
+
if (!existingContent) {
|
|
1079
|
+
if (updateMode) {
|
|
1080
|
+
writeFile(filePath, generatedSection);
|
|
1081
|
+
existingContent = generatedSection;
|
|
1082
|
+
fileStatus = "created";
|
|
1083
|
+
} else {
|
|
1084
|
+
hasError = true;
|
|
1085
|
+
fileStatus = "diff";
|
|
1086
|
+
diffs.push(`File does not exist. Target command "${targetCommand}" section cannot be validated.`);
|
|
1087
|
+
}
|
|
1088
|
+
continue;
|
|
1089
|
+
}
|
|
1090
|
+
const existingSection = extractCommandSection(existingContent, targetCommand);
|
|
1091
|
+
const generatedSectionOnly = extractCommandSection(generatedSection, targetCommand);
|
|
1092
|
+
if (!generatedSectionOnly) throw new Error(`Generated content does not contain section for command "${targetCommand}"`);
|
|
1093
|
+
if (!existingSection) {
|
|
1094
|
+
if (updateMode) {
|
|
1095
|
+
existingContent = insertCommandSection(existingContent, targetCommand, generatedSectionOnly, specifiedCommands);
|
|
1096
|
+
writeFile(filePath, existingContent);
|
|
1097
|
+
if (fileStatus !== "created") fileStatus = "updated";
|
|
1098
|
+
} else {
|
|
1099
|
+
hasError = true;
|
|
1100
|
+
fileStatus = "diff";
|
|
1101
|
+
diffs.push(`Existing file does not contain section for command "${targetCommand}"`);
|
|
1102
|
+
}
|
|
1103
|
+
continue;
|
|
1104
|
+
}
|
|
1105
|
+
if (existingSection !== generatedSectionOnly) if (updateMode) {
|
|
1106
|
+
const updatedContent = replaceCommandSection(existingContent, targetCommand, generatedSectionOnly);
|
|
1107
|
+
if (updatedContent) {
|
|
1108
|
+
existingContent = updatedContent;
|
|
1109
|
+
writeFile(filePath, existingContent);
|
|
1110
|
+
if (fileStatus !== "created") fileStatus = "updated";
|
|
1111
|
+
} else throw new Error(`Failed to replace section for command "${targetCommand}"`);
|
|
1112
|
+
} else {
|
|
1113
|
+
hasError = true;
|
|
1114
|
+
fileStatus = "diff";
|
|
1115
|
+
diffs.push(formatDiff(existingSection, generatedSectionOnly));
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
results.push({
|
|
1119
|
+
path: filePath,
|
|
1120
|
+
status: fileStatus,
|
|
1121
|
+
diff: diffs.length > 0 ? diffs.join("\n\n") : void 0
|
|
1122
|
+
});
|
|
1123
|
+
} else {
|
|
1124
|
+
const generatedMarkdown = await applyFormatter(generateFileMarkdown(commandPaths, allCommands, render, filePath, fileMap, specifiedCommands, fileConfig), formatter);
|
|
1125
|
+
const comparison = compareWithExisting(generatedMarkdown, filePath);
|
|
1126
|
+
if (comparison.match) results.push({
|
|
1127
|
+
path: filePath,
|
|
1128
|
+
status: "match"
|
|
1129
|
+
});
|
|
1130
|
+
else if (updateMode) {
|
|
1131
|
+
writeFile(filePath, generatedMarkdown);
|
|
1132
|
+
results.push({
|
|
1133
|
+
path: filePath,
|
|
1134
|
+
status: comparison.fileExists ? "updated" : "created"
|
|
1135
|
+
});
|
|
1136
|
+
} else {
|
|
1137
|
+
hasError = true;
|
|
1138
|
+
results.push({
|
|
1139
|
+
path: filePath,
|
|
1140
|
+
status: "diff",
|
|
1141
|
+
diff: comparison.diff
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
return {
|
|
1147
|
+
success: !hasError,
|
|
1148
|
+
files: results,
|
|
1149
|
+
error: hasError ? `Documentation is out of date. Run with ${UPDATE_GOLDEN_ENV}=true to update.` : void 0
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
/**
|
|
1153
|
+
* Assert that documentation matches golden files
|
|
1154
|
+
* Throws an error if there are differences and update mode is not enabled
|
|
1155
|
+
*/
|
|
1156
|
+
async function assertDocMatch(config) {
|
|
1157
|
+
const result = await generateDoc(config);
|
|
1158
|
+
if (!result.success) {
|
|
1159
|
+
const diffMessages = result.files.filter((f) => f.status === "diff").map((f) => {
|
|
1160
|
+
let msg = `File: ${f.path}\n`;
|
|
1161
|
+
if (f.diff) msg += f.diff;
|
|
1162
|
+
return msg;
|
|
1163
|
+
}).join("\n\n");
|
|
1164
|
+
throw new Error(`Documentation does not match golden files.\n\n${diffMessages}\n\nRun with ${UPDATE_GOLDEN_ENV}=true to update the documentation.`);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Initialize documentation files by deleting them
|
|
1169
|
+
* Only deletes when update mode is enabled (POLITTY_DOCS_UPDATE=true)
|
|
1170
|
+
* Use this in beforeAll to ensure skipped tests don't leave stale sections
|
|
1171
|
+
* @param config - Config containing files to initialize, or a single file path
|
|
1172
|
+
* @param fileSystem - Optional fs implementation (useful when fs is mocked)
|
|
1173
|
+
*/
|
|
1174
|
+
function initDocFile(config, fileSystem) {
|
|
1175
|
+
if (!isUpdateMode()) return;
|
|
1176
|
+
if (typeof config === "string") deleteFile(config, fileSystem);
|
|
1177
|
+
else for (const filePath of Object.keys(config.files)) deleteFile(filePath, fileSystem);
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
//#endregion
|
|
1181
|
+
export { COMMAND_MARKER_PREFIX, UPDATE_GOLDEN_ENV, assertDocMatch, buildCommandInfo, collectAllCommands, commandEndMarker, commandStartMarker, compareWithExisting, createCommandRenderer, defaultRenderers, executeExamples, formatDiff, generateDoc, initDocFile, renderArgumentsList, renderArgumentsListFromArray, renderArgumentsTable, renderArgumentsTableFromArray, renderExamplesDefault, renderOptionsList, renderOptionsListFromArray, renderOptionsTable, renderOptionsTableFromArray, renderSubcommandsTable, renderSubcommandsTableFromArray, renderUsage, resolveLazyCommand, writeFile };
|
|
1182
|
+
//# sourceMappingURL=index.js.map
|