@ankhorage/paradox 0.1.27 → 0.2.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.
Files changed (60) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +53 -61
  3. package/dist/analyze/analyze.d.ts +2 -2
  4. package/dist/analyze/analyze.js +51 -35
  5. package/dist/analyze/badges.d.ts +2 -1
  6. package/dist/analyze/badges.js +14 -4
  7. package/dist/analyze/components.js +12 -11
  8. package/dist/analyze/documentation/collectDocumentationCommentsAsync.d.ts +11 -0
  9. package/dist/analyze/documentation/collectDocumentationCommentsAsync.js +72 -0
  10. package/dist/analyze/documentation/findings.d.ts +5 -0
  11. package/dist/analyze/documentation/findings.js +17 -0
  12. package/dist/analyze/documentation/validateDocumentationPolicyAsync.d.ts +13 -0
  13. package/dist/analyze/documentation/validateDocumentationPolicyAsync.js +150 -0
  14. package/dist/analyze/documentation/validateReferencesAsync.d.ts +11 -0
  15. package/dist/analyze/documentation/validateReferencesAsync.js +148 -0
  16. package/dist/analyze/exports.d.ts +4 -0
  17. package/dist/analyze/exports.js +17 -15
  18. package/dist/analyze/modules.js +3 -8
  19. package/dist/analyze/readmeConfig.d.ts +1 -3
  20. package/dist/analyze/readmeConfig.js +8 -19
  21. package/dist/analyze/readmeUsage.d.ts +7 -10
  22. package/dist/analyze/readmeUsage.js +124 -48
  23. package/dist/analyze/semantic/docBlocks.js +18 -48
  24. package/dist/analyze/semantic/exports.js +3 -7
  25. package/dist/analyze/semantic/model.d.ts +0 -2
  26. package/dist/analyze/semantic/paradoxComment.d.ts +1 -11
  27. package/dist/analyze/semantic/paradoxComment.js +1 -43
  28. package/dist/analyze/semantic/tagRegistry.js +2 -1
  29. package/dist/analyze/sequenceScenarios.js +2 -4
  30. package/dist/analyze/sourceFunctions.js +4 -4
  31. package/dist/analyze/types.d.ts +31 -24
  32. package/dist/analyze/usage.d.ts +2 -2
  33. package/dist/analyze/usage.js +3 -33
  34. package/dist/analyze/utils/getExportMetadata.js +11 -40
  35. package/dist/analyze/utils/parseParadoxComment.d.ts +12 -9
  36. package/dist/analyze/utils/parseParadoxComment.js +66 -78
  37. package/dist/cli/index.d.ts +3 -2
  38. package/dist/cli/index.js +3 -2
  39. package/dist/cli/standalone.js +11 -0
  40. package/dist/config/defineParadoxConfig.d.ts +1 -1
  41. package/dist/doc-tags/registry.d.ts +28 -32
  42. package/dist/doc-tags/registry.js +35 -39
  43. package/dist/index.d.ts +1 -1
  44. package/dist/model/buildModel.d.ts +26 -19
  45. package/dist/model/buildModel.js +33 -99
  46. package/dist/model/types.d.ts +27 -20
  47. package/dist/paths/policy.d.ts +1 -1
  48. package/dist/render/renderers/diagrams.js +3 -11
  49. package/dist/render/renderers/html.js +89 -58
  50. package/dist/render/renderers/markdown.js +139 -86
  51. package/dist/render/toFileStem.d.ts +2 -0
  52. package/dist/render/toFileStem.js +8 -0
  53. package/dist/{config/types.d.ts → types/config.d.ts} +3 -5
  54. package/dist/write/write.d.ts +1 -1
  55. package/package.json +2 -1
  56. package/dist/analyze/readmeCli.d.ts +0 -9
  57. package/dist/analyze/readmeCli.js +0 -33
  58. package/dist/analyze/utils/getLeadingParadoxComment.d.ts +0 -10
  59. package/dist/analyze/utils/getLeadingParadoxComment.js +0 -16
  60. /package/dist/{config/types.js → types/config.js} +0 -0
@@ -8,6 +8,9 @@ export function renderMarkdown({ badges, diagrams, model, outputDir, }) {
8
8
  components: renderComponents(model),
9
9
  };
10
10
  }
11
+ /***
12
+ * Renders the generated package README.
13
+ */
11
14
  function renderReadme(model, outputDir, badges, diagrams) {
12
15
  const lines = [
13
16
  '<!-- markdownlint-disable MD013 MD033 -->',
@@ -23,77 +26,67 @@ function renderReadme(model, outputDir, badges, diagrams) {
23
26
  }
24
27
  if (model.description)
25
28
  lines.push(model.description, '');
26
- renderReadmeUsage(lines, model.readmeUsageDescription, model.readmeUsage);
27
- renderReadmeCli(lines, model, outputDir, diagrams);
29
+ renderUsage(lines, model);
28
30
  renderConfiguration(lines, model);
29
31
  renderGeneratedDocumentation(lines, outputDir, diagrams);
30
32
  renderReadmeApi(lines, model);
31
33
  return `${lines.join('\n').trimEnd()}\n`;
32
34
  }
33
- function renderReadmeUsage(lines, description, entries) {
34
- if (description === null && entries.length === 0)
35
- return;
35
+ /***
36
+ * Renders the canonical CLI-first Usage chapter.
37
+ */
38
+ function renderUsage(lines, model) {
39
+ const readmeExample = model.usageEntries.find((entry) => entry.area === 'examples' && entry.isReadme);
36
40
  lines.push('## Usage', '');
37
- if (description !== null)
38
- lines.push(description, '');
39
- for (const entry of entries) {
40
- if (entry.title !== null)
41
- lines.push(`### ${entry.title}`, '');
42
- if (entry.description !== null) {
43
- const [, ...rest] = entry.description.split('\n');
44
- const entryDescription = rest.join('\n').trim();
45
- if (entryDescription.length > 0)
46
- lines.push(entryDescription, '');
47
- }
48
- lines.push(`Source: \`${entry.sourcePath}\``, '');
49
- lines.push(`\`\`\`${entry.language}`);
50
- lines.push(entry.code);
51
- lines.push('```', '');
52
- }
53
- }
54
- function renderReadmeCli(lines, model, outputDir, diagrams) {
55
- if (model.readmeCli === null)
41
+ lines.push('### CLI', '');
42
+ lines.push('Ankhorage packages expose their command-line interface through `ankh`. Use `ankh --help` to discover available package commands, or run a package command with `--help` for package-specific usage.', '');
43
+ lines.push('```zsh');
44
+ lines.push('# Install the Ankhorage CLI');
45
+ lines.push('bun add --global @ankhorage/ankh', '');
46
+ lines.push(`# Show usage information for ${getPackageDisplayName(model.packageId)}`);
47
+ lines.push(model.usage.command);
48
+ lines.push('```', '');
49
+ if (readmeExample === undefined)
56
50
  return;
57
- lines.push('## CLI', '');
58
- if (model.readmeCli.description !== null)
59
- lines.push(model.readmeCli.description, '');
60
- if (model.usage !== null && model.usage.commands.length > 0) {
61
- lines.push('```bash');
62
- for (const command of model.usage.commands)
63
- lines.push(command.command);
64
- lines.push('```', '');
65
- }
66
- const scenarios = model.sequenceScenarios.filter((scenario) => scenario.kind === 'bin');
67
- for (const scenario of scenarios) {
68
- const diagram = findScenarioDiagram(diagrams, scenario);
69
- if (scenario.description === null && diagram === undefined)
70
- continue;
71
- lines.push('<details>');
72
- lines.push(`<summary>${scenario.name}</summary>`, '');
73
- if (scenario.description !== null)
74
- lines.push(scenario.description, '');
75
- if (diagram !== undefined) {
76
- lines.push(`Diagram: [${diagram.title}](./${outputDir}/${diagram.path})`, '');
77
- lines.push('```mermaid');
78
- lines.push(diagram.content.trimEnd());
79
- lines.push('```', '');
80
- }
81
- lines.push('</details>', '');
51
+ lines.push(`### ${readmeExample.title ?? 'Programmatic Usage'}`, '');
52
+ if (readmeExample.description !== null)
53
+ lines.push(readmeExample.description, '');
54
+ renderReferences(lines, readmeExample);
55
+ lines.push('```' + readmeExample.language);
56
+ lines.push(readmeExample.code);
57
+ lines.push('```', '');
58
+ if (model.exampleCount > 1) {
59
+ const additionalExamples = model.exampleCount - 1;
60
+ const label = additionalExamples === 1 ? 'example' : 'examples';
61
+ lines.push(`This package contains ${additionalExamples} additional ${label}. See the generated documentation for the complete set.`, '');
82
62
  }
83
63
  }
84
- function findScenarioDiagram(diagrams, scenario) {
85
- return diagrams.find((diagram) => diagram.path === `diagrams/sequences/${toFileStem(scenario.name)}.mmd`);
64
+ /***
65
+ * Returns a human-readable package command name.
66
+ */
67
+ function getPackageDisplayName(packageId) {
68
+ return packageId.split('/').pop() ?? packageId;
86
69
  }
70
+ /***
71
+ * Renders the canonical Configuration chapter from the tagged schema plus concrete config instance.
72
+ */
87
73
  function renderConfiguration(lines, model) {
88
74
  const config = model.config?.isReadme ? model.config : null;
89
75
  const example = model.readmeConfig;
90
76
  if (config === null && example === null)
91
77
  return;
92
78
  lines.push('## Configuration', '');
79
+ if (config !== null) {
80
+ if (config.title !== null && config.title !== 'Configuration') {
81
+ lines.push(`### ${config.title}`, '');
82
+ }
83
+ if (config.description !== null)
84
+ lines.push(config.description, '');
85
+ renderReferences(lines, config);
86
+ }
93
87
  if (example !== null) {
94
- if (example.description !== null)
95
- lines.push(example.description, '');
96
- lines.push(`\`\`\`${example.language}`);
88
+ lines.push('### Example', '');
89
+ lines.push('```' + example.language);
97
90
  lines.push(example.code);
98
91
  lines.push('```', '');
99
92
  }
@@ -108,15 +101,33 @@ function renderConfiguration(lines, model) {
108
101
  }
109
102
  lines.push('', '</details>', '');
110
103
  }
104
+ /***
105
+ * Renders links and security evidence owned by one documented item.
106
+ */
107
+ function renderReferences(lines, metadata) {
108
+ if (metadata.see.length > 0) {
109
+ lines.push(`See also: ${metadata.see.map((url) => `[${url}](${url})`).join(', ')}`, '');
110
+ }
111
+ if (metadata.security.length > 0) {
112
+ lines.push(`Security tests: ${metadata.security.map((reference) => `\`${reference}\``).join(', ')}`, '');
113
+ }
114
+ }
115
+ /***
116
+ * Renders links to generated documentation artifacts.
117
+ */
111
118
  function renderGeneratedDocumentation(lines, outputDir, diagrams) {
112
119
  lines.push('## Generated documentation', '');
113
120
  lines.push(`- [Interactive documentation app](./${outputDir}/index.html)`);
114
121
  lines.push(`- [Public API reference](./${outputDir}/exports.md)`);
115
122
  lines.push(`- [Component registry](./${outputDir}/components.md)`);
116
- for (const diagram of diagrams)
123
+ for (const diagram of diagrams) {
117
124
  lines.push(`- [${diagram.title}](./${outputDir}/${diagram.path})`);
125
+ }
118
126
  lines.push('');
119
127
  }
128
+ /***
129
+ * Renders README-promoted public API entries.
130
+ */
120
131
  function renderReadmeApi(lines, model) {
121
132
  const groups = getReadmeGroups(model);
122
133
  if (groups.length === 0)
@@ -125,20 +136,25 @@ function renderReadmeApi(lines, model) {
125
136
  for (const group of groups) {
126
137
  lines.push(`### ${group.title}`, '');
127
138
  for (const item of group.items) {
128
- if (item.kind === 'component')
139
+ if (item.kind === 'component') {
129
140
  renderComponentAccordion(lines, item.component, item.exportEntry);
130
- else
141
+ }
142
+ else {
131
143
  renderExportAccordion(lines, item.exportEntry);
144
+ }
132
145
  }
133
146
  }
134
147
  }
148
+ /***
149
+ * Renders one README-promoted component.
150
+ */
135
151
  function renderComponentAccordion(lines, component, exportEntry) {
136
152
  lines.push('<details>');
137
- lines.push(`<summary>${component.name}</summary>`, '');
153
+ lines.push(`<summary>${exportEntry?.title ?? component.name}</summary>`, '');
138
154
  renderSignature(lines, exportEntry);
139
155
  if (component.description)
140
156
  lines.push(component.description, '');
141
- renderExamples(lines, component.examples);
157
+ renderReferences(lines, component);
142
158
  if (exportEntry && exportEntry.relatedSymbols.length > 0) {
143
159
  lines.push(`Related types: ${exportEntry.relatedSymbols.map((symbol) => `\`${symbol}\``).join(', ')}`, '');
144
160
  }
@@ -154,13 +170,16 @@ function renderComponentAccordion(lines, component, exportEntry) {
154
170
  }
155
171
  lines.push('</details>', '');
156
172
  }
173
+ /***
174
+ * Renders one README-promoted non-component export.
175
+ */
157
176
  function renderExportAccordion(lines, item) {
158
177
  lines.push('<details>');
159
- lines.push(`<summary>${item.name}</summary>`, '');
178
+ lines.push(`<summary>${item.title ?? item.name}</summary>`, '');
160
179
  renderSignature(lines, item);
161
180
  lines.push(item.description ?? `\`${item.kind}\` export.`, '');
181
+ renderReferences(lines, item);
162
182
  renderStructuredRows(lines, item);
163
- renderExamples(lines, item.examples);
164
183
  lines.push(`Module: \`${item.modulePath}\``);
165
184
  lines.push(`Source: \`${item.sourceLocation.filePath}:${item.sourceLocation.line}:${item.sourceLocation.column}\``);
166
185
  if (item.relatedSymbols.length > 0) {
@@ -168,6 +187,9 @@ function renderExportAccordion(lines, item) {
168
187
  }
169
188
  lines.push('', '</details>', '');
170
189
  }
190
+ /***
191
+ * Renders the primary signature for a README public API entry.
192
+ */
171
193
  function renderSignature(lines, item) {
172
194
  const signature = item?.signatures[0]?.label;
173
195
  if (!signature)
@@ -176,15 +198,9 @@ function renderSignature(lines, item) {
176
198
  lines.push(`${item.name}${signature}`);
177
199
  lines.push('```', '');
178
200
  }
179
- function renderExamples(lines, examples) {
180
- for (const example of examples) {
181
- if (example.title)
182
- lines.push(`#### ${example.title}`, '');
183
- lines.push(`\`\`\`${example.language ?? ''}`);
184
- lines.push(example.code);
185
- lines.push('```', '');
186
- }
187
- }
201
+ /***
202
+ * Renders structured const-array rows as a Markdown table.
203
+ */
188
204
  function renderStructuredRows(lines, item) {
189
205
  if (item.structuredRows.length === 0)
190
206
  return;
@@ -202,6 +218,9 @@ function renderStructuredRows(lines, item) {
202
218
  }
203
219
  lines.push('');
204
220
  }
221
+ /***
222
+ * Returns stable structured-row columns.
223
+ */
205
224
  function getStructuredColumns(item) {
206
225
  const columns = new Set();
207
226
  for (const row of item.structuredRows) {
@@ -210,9 +229,15 @@ function getStructuredColumns(item) {
210
229
  }
211
230
  return [...columns];
212
231
  }
232
+ /***
233
+ * Formats one structured-row column header.
234
+ */
213
235
  function formatStructuredColumnHeader(column) {
214
236
  return escapeTableCell(column.replace(/([a-z])([A-Z])/g, '$1 $2').toLowerCase());
215
237
  }
238
+ /***
239
+ * Formats one structured-row table cell.
240
+ */
216
241
  function formatStructuredCell(column, value) {
217
242
  const escaped = escapeTableCell(value);
218
243
  if (column === 'syntax' || column === 'name' || column === 'handler')
@@ -223,6 +248,9 @@ function formatStructuredCell(column, value) {
223
248
  return 'no';
224
249
  return escaped;
225
250
  }
251
+ /***
252
+ * Groups README-promoted API entries by presentation category.
253
+ */
226
254
  function getReadmeGroups(model) {
227
255
  const exportsByName = new Map(model.exports.map((entry) => [entry.name, entry]));
228
256
  const componentNames = new Set(model.components.map((component) => component.name));
@@ -244,22 +272,32 @@ function getReadmeGroups(model) {
244
272
  }
245
273
  return CATEGORY_ORDER.flatMap((title) => {
246
274
  const items = groups.get(title);
247
- if (!items || items.length === 0)
248
- return [];
249
- return [{ title, items: sortReadmeItems(items) }];
275
+ return items === undefined || items.length === 0
276
+ ? []
277
+ : [{ title, items: sortReadmeItems(items) }];
250
278
  });
251
279
  }
280
+ /***
281
+ * Adds one public API item to a README category.
282
+ */
252
283
  function addReadmeItem(groups, title, item) {
253
- const existing = groups.get(title) ?? [];
254
- existing.push(item);
255
- groups.set(title, existing);
284
+ groups.set(title, [...(groups.get(title) ?? []), item]);
256
285
  }
286
+ /***
287
+ * Sorts README category items by symbol name.
288
+ */
257
289
  function sortReadmeItems(items) {
258
290
  return [...items].sort((left, right) => getReadmeItemName(left).localeCompare(getReadmeItemName(right)));
259
291
  }
292
+ /***
293
+ * Returns one README item's source symbol name.
294
+ */
260
295
  function getReadmeItemName(item) {
261
296
  return item.kind === 'component' ? item.component.name : item.exportEntry.name;
262
297
  }
298
+ /***
299
+ * Derives a stable README public API category from a module path.
300
+ */
263
301
  function getReadmeCategory(modulePath, name) {
264
302
  if (modulePath.includes('/config/'))
265
303
  return 'Config';
@@ -281,24 +319,30 @@ function getReadmeCategory(modulePath, name) {
281
319
  return 'Types';
282
320
  return 'Utilities';
283
321
  }
322
+ /***
323
+ * Renders the complete public API reference.
324
+ */
284
325
  function renderExports(model) {
285
326
  const lines = ['# Public API', ''];
286
327
  for (const item of model.exports) {
287
- lines.push(`## ${item.name}`, '');
328
+ lines.push(`## ${item.title ?? item.name}`, '');
329
+ if (item.title !== null && item.title !== item.name)
330
+ lines.push(`Symbol: \`${item.name}\``, '');
288
331
  lines.push(`Kind: \`${item.kind}\``);
289
332
  lines.push(`Module: \`${item.modulePath}\``);
290
333
  lines.push(`Source: \`${item.sourceLocation.filePath}:${item.sourceLocation.line}:${item.sourceLocation.column}\``, '');
291
334
  if (item.description)
292
335
  lines.push(item.description, '');
336
+ renderReferences(lines, item);
293
337
  renderStructuredRows(lines, item);
294
338
  if (item.signatures.length > 0) {
295
339
  lines.push('### Signatures', '');
296
340
  for (const signature of item.signatures) {
297
341
  lines.push(`- \`${signature.label}\``);
298
342
  for (const parameter of signature.parameters) {
299
- lines.push(` - ${parameter.name}: \`${parameter.type}\`${parameter.required ? '' : ' (optional)'}${parameter.description ? ` — ${parameter.description}` : ''}`);
343
+ lines.push(` - ${parameter.name}: \`${parameter.type}\`${parameter.required ? '' : ' (optional)'}`);
300
344
  }
301
- lines.push(` - returns: \`${signature.returnType ?? 'void'}\`${signature.returnDescription ? ` — ${signature.returnDescription}` : ''}`);
345
+ lines.push(` - returns: \`${signature.returnType ?? 'void'}\``);
302
346
  }
303
347
  lines.push('');
304
348
  }
@@ -314,6 +358,9 @@ function renderExports(model) {
314
358
  }
315
359
  return `${lines.join('\n').trimEnd()}\n`;
316
360
  }
361
+ /***
362
+ * Renders the complete component registry.
363
+ */
317
364
  function renderComponents(model) {
318
365
  const lines = ['# Components', ''];
319
366
  for (const component of model.components) {
@@ -321,6 +368,7 @@ function renderComponents(model) {
321
368
  lines.push(`Source: \`${component.sourceLocation.filePath}:${component.sourceLocation.line}:${component.sourceLocation.column}\``, '');
322
369
  if (component.description)
323
370
  lines.push(component.description, '');
371
+ renderReferences(lines, component);
324
372
  if (component.exportPaths.length > 0) {
325
373
  lines.push(`Export paths: ${component.exportPaths.map((path) => `\`${path}\``).join(', ')}`, '');
326
374
  }
@@ -335,12 +383,21 @@ function renderComponents(model) {
335
383
  }
336
384
  return `${lines.join('\n').trimEnd()}\n`;
337
385
  }
386
+ /***
387
+ * Escapes one Markdown table cell.
388
+ */
338
389
  function escapeTableCell(value) {
339
390
  return value.replaceAll('|', '\\|');
340
391
  }
392
+ /***
393
+ * Renders an optional default value.
394
+ */
341
395
  function renderDefault(value) {
342
396
  return value === undefined ? '—' : `\`${escapeTableCell(value)}\``;
343
397
  }
398
+ /***
399
+ * Flattens nested configuration members into dot paths.
400
+ */
344
401
  function flattenConfigMembers(members, prefix = '') {
345
402
  return members.flatMap((member) => {
346
403
  const path = prefix ? `${prefix}.${member.name}` : member.name;
@@ -355,6 +412,9 @@ function flattenConfigMembers(members, prefix = '') {
355
412
  return [current, ...children];
356
413
  });
357
414
  }
415
+ /***
416
+ * Returns the accessible badge label stored in a rendered badge artifact.
417
+ */
358
418
  function badgeLabel(model, badgePath) {
359
419
  const fileName = badgePath.split('/').pop();
360
420
  if (!fileName)
@@ -363,13 +423,6 @@ function badgeLabel(model, badgePath) {
363
423
  const badge = model.badges.find((entry) => entry.id === id);
364
424
  return badge ? `${badge.label}: ${badge.value}` : badgePath;
365
425
  }
366
- function toFileStem(value) {
367
- return value
368
- .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
369
- .replace(/[^A-Za-z0-9]+/g, '-')
370
- .replace(/^-+|-+$/g, '')
371
- .toLowerCase();
372
- }
373
426
  const CATEGORY_ORDER = [
374
427
  'Config',
375
428
  'Documentation',
@@ -0,0 +1,2 @@
1
+ /*** Convert a documentation label to the stable lowercase kebab-case file stem used by Paradox render artifacts. */
2
+ export declare function toFileStem(value: string): string;
@@ -0,0 +1,8 @@
1
+ /*** Convert a documentation label to the stable lowercase kebab-case file stem used by Paradox render artifacts. */
2
+ export function toFileStem(value) {
3
+ return value
4
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
5
+ .replace(/[^A-Za-z0-9]+/g, '-')
6
+ .replace(/^-+|-+$/g, '')
7
+ .toLowerCase();
8
+ }
@@ -1,5 +1,7 @@
1
1
  /***
2
- * Configuration for running Paradox.
2
+ * @title Configuration
3
+ *
4
+ * Configures Paradox documentation generation for a package.
3
5
  *
4
6
  * @config
5
7
  * @readme
@@ -16,10 +18,6 @@ export interface ParadoxConfig {
16
18
  docs?: {
17
19
  title?: string;
18
20
  description?: string;
19
- usage?: {
20
- description?: string;
21
- entrypoints?: string[];
22
- };
23
21
  };
24
22
  package?: {
25
23
  root?: string;
@@ -1,5 +1,5 @@
1
- import type { ParadoxConfig } from '../config/types.js';
2
1
  import type { RenderResult } from '../render/types.js';
2
+ import type { ParadoxConfig } from '../types/config.js';
3
3
  /***
4
4
  * Writes generated documentation artifacts to the configured output paths.
5
5
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ankhorage/paradox",
3
- "version": "0.1.27",
3
+ "version": "0.2.1",
4
4
  "description": "Deterministic documentation generator for TypeScript packages.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -71,6 +71,7 @@
71
71
  "test:standalone": "bun test tests/cli.e2e.test.ts"
72
72
  },
73
73
  "dependencies": {
74
+ "@ankhorage/policy": "^0.2.0",
74
75
  "@ankhorage/utility": "^1.8.0",
75
76
  "ts-morph": "^28.0.0"
76
77
  },
@@ -1,9 +0,0 @@
1
- export interface AnalysisReadmeCli {
2
- description: string | null;
3
- sourcePath: string;
4
- }
5
- /***
6
- * Collects README CLI metadata from the canonical Ankhorage CLI entrypoint when its leading
7
- * Paradox comment opts into README output with @readme.
8
- */
9
- export declare function analyzeReadmeCli(root: string): Promise<AnalysisReadmeCli | null>;
@@ -1,33 +0,0 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { join } from 'node:path';
3
- import { getLeadingParadoxComment } from './utils/getLeadingParadoxComment.js';
4
- const CLI_INDEX_PATH = 'src/cli/index.ts';
5
- /***
6
- * Collects README CLI metadata from the canonical Ankhorage CLI entrypoint when its leading
7
- * Paradox comment opts into README output with @readme.
8
- */
9
- export async function analyzeReadmeCli(root) {
10
- const filePath = join(root, CLI_INDEX_PATH);
11
- let source;
12
- try {
13
- source = await readFile(filePath, 'utf-8');
14
- }
15
- catch (error) {
16
- if (isMissingPathError(error))
17
- return null;
18
- throw error;
19
- }
20
- const comment = getLeadingParadoxComment(source);
21
- if (!comment?.parsed.isReadme)
22
- return null;
23
- return {
24
- description: comment.parsed.description,
25
- sourcePath: CLI_INDEX_PATH,
26
- };
27
- }
28
- function isMissingPathError(error) {
29
- return (error instanceof Error &&
30
- 'code' in error &&
31
- typeof error.code === 'string' &&
32
- error.code === 'ENOENT');
33
- }
@@ -1,10 +0,0 @@
1
- import { type ParsedParadoxComment } from './parseParadoxComment.js';
2
- export interface LeadingParadoxComment {
3
- parsed: ParsedParadoxComment;
4
- start: number;
5
- end: number;
6
- }
7
- /***
8
- * Parses a leading Paradox comment from a source file.
9
- */
10
- export declare function getLeadingParadoxComment(source: string): LeadingParadoxComment | null;
@@ -1,16 +0,0 @@
1
- import { parseParadoxComment } from './parseParadoxComment.js';
2
- /***
3
- * Parses a leading Paradox comment from a source file.
4
- */
5
- export function getLeadingParadoxComment(source) {
6
- const match = /^\s*(\/\*\*\*[\s\S]*?\*\/)/.exec(source);
7
- const comment = match?.[1];
8
- if (match === null || comment === undefined)
9
- return null;
10
- const start = match[0].indexOf(comment);
11
- return {
12
- parsed: parseParadoxComment(comment),
13
- start,
14
- end: start + comment.length,
15
- };
16
- }
File without changes