@ankhorage/paradox 0.0.5 → 0.0.7

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.
@@ -0,0 +1,155 @@
1
+ /***
2
+ * Renders markdown artifacts from the documentation model.
3
+ */
4
+ export function renderMarkdown({ badges, diagrams, model, outputDir, }) {
5
+ return {
6
+ readme: renderReadme(model, outputDir, badges, diagrams),
7
+ exportsMarkdown: renderExports(model),
8
+ components: renderComponents(model),
9
+ };
10
+ }
11
+ function renderReadme(model, outputDir, badges, diagrams) {
12
+ const lines = [`# ${model.packageName}`, ''];
13
+ if (badges.length > 0) {
14
+ lines.push(badges
15
+ .map((badge) => `![${badgeLabel(model, badge.path)}](./${outputDir}/${badge.path})`)
16
+ .join(' '), '');
17
+ }
18
+ if (model.description) {
19
+ lines.push(model.description, '');
20
+ }
21
+ if (model.usage !== null) {
22
+ lines.push('## Usage', '');
23
+ lines.push('```bash');
24
+ for (const command of model.usage.commands) {
25
+ lines.push(command.command);
26
+ }
27
+ lines.push('```', '');
28
+ }
29
+ if (model.config !== null) {
30
+ lines.push('## Configuration', '');
31
+ lines.push(`Create a \`${model.config.configFile}\` file:`, '');
32
+ lines.push('```ts');
33
+ if (model.config.factoryName !== null) {
34
+ lines.push(`import { ${model.config.factoryName} } from '${model.packageId}';`);
35
+ lines.push('');
36
+ lines.push(`export default ${model.config.factoryName}({`);
37
+ lines.push(' // ...');
38
+ lines.push('});');
39
+ }
40
+ else {
41
+ lines.push(`import type { ${model.config.exportName} } from '${model.packageId}';`);
42
+ lines.push('');
43
+ lines.push('const config = {');
44
+ lines.push(' // ...');
45
+ lines.push(`} satisfies ${model.config.exportName};`);
46
+ lines.push('');
47
+ lines.push('export default config;');
48
+ }
49
+ lines.push('```', '');
50
+ }
51
+ lines.push('## Generated documentation', '');
52
+ lines.push(`- [Interactive documentation app](./${outputDir}/index.html)`);
53
+ lines.push(`- [Public API reference](./${outputDir}/exports.md)`);
54
+ lines.push(`- [Component registry](./${outputDir}/components.md)`);
55
+ for (const diagram of diagrams) {
56
+ lines.push(`- [${diagram.title}](./${outputDir}/${diagram.path})`);
57
+ }
58
+ lines.push('');
59
+ lines.push('## Architecture preview', '');
60
+ if (diagrams.length > 0) {
61
+ lines.push('```mermaid');
62
+ lines.push(diagrams[0].content.trimEnd());
63
+ lines.push('```', '');
64
+ }
65
+ lines.push('## Path resolution', '');
66
+ lines.push('- Config discovery: searches upward from `process.cwd()` for `paradox.config.ts/js/mjs/cjs` (required; no fallback).');
67
+ lines.push('- Package root: defaults to the directory containing `paradox.config.*`; `package.root` (when relative) resolves relative to that directory.');
68
+ lines.push('- Output directory: defaults to `paradox/`; `output.dir` (when relative) resolves relative to the resolved package root and must stay inside it.');
69
+ lines.push('- Modes:');
70
+ lines.push(' - `safe`: writes generated artifacts only under the output directory');
71
+ lines.push(' - `write`: additionally updates `<packageRoot>/README.md`', '');
72
+ if (model.exports.length > 0) {
73
+ lines.push('## Public API', '');
74
+ for (const item of model.exports) {
75
+ lines.push(`### ${item.name}`, '');
76
+ lines.push(item.description ?? `\`${item.kind}\` export.`, '');
77
+ lines.push(`- Kind: \`${item.kind}\``);
78
+ lines.push(`- Module: \`${item.modulePath}\``);
79
+ lines.push(`- Source: \`${item.sourceLocation.filePath}:${item.sourceLocation.line}:${item.sourceLocation.column}\``);
80
+ lines.push(`- Export paths: ${item.exportPaths.map((path) => `\`${path}\``).join(', ')}`);
81
+ if (item.relatedSymbols.length > 0) {
82
+ lines.push(`- Related symbols: ${item.relatedSymbols.map((symbol) => `\`${symbol}\``).join(', ')}`);
83
+ }
84
+ lines.push('');
85
+ }
86
+ }
87
+ return `${lines.join('\n').trimEnd()}\n`;
88
+ }
89
+ function renderExports(model) {
90
+ const lines = ['# Public API', ''];
91
+ for (const item of model.exports) {
92
+ lines.push(`## ${item.name}`, '');
93
+ lines.push(`Kind: \`${item.kind}\``);
94
+ lines.push(`Module: \`${item.modulePath}\``);
95
+ lines.push(`Source: \`${item.sourceLocation.filePath}:${item.sourceLocation.line}:${item.sourceLocation.column}\``, '');
96
+ if (item.description) {
97
+ lines.push(item.description, '');
98
+ }
99
+ if (item.signatures.length > 0) {
100
+ lines.push('### Signatures', '');
101
+ for (const signature of item.signatures) {
102
+ lines.push(`- \`${signature.label}\``);
103
+ for (const parameter of signature.parameters) {
104
+ lines.push(` - ${parameter.name}: \`${parameter.type}\`${parameter.required ? '' : ' (optional)'}${parameter.description ? ` — ${parameter.description}` : ''}`);
105
+ }
106
+ lines.push(` - returns: \`${signature.returnType ?? 'void'}\`${signature.returnDescription ? ` — ${signature.returnDescription}` : ''}`);
107
+ }
108
+ lines.push('');
109
+ }
110
+ if (item.members.length > 0) {
111
+ lines.push('### Members', '');
112
+ lines.push('| Name | Kind | Type | Required | Description |');
113
+ lines.push('| --- | --- | --- | --- | --- |');
114
+ for (const member of item.members) {
115
+ lines.push(`| ${escapeTableCell(member.name)} | ${member.kind} | \`${escapeTableCell(member.type)}\` | ${member.required ? 'yes' : 'no'} | ${escapeTableCell(member.description ?? '')} |`);
116
+ }
117
+ lines.push('');
118
+ }
119
+ }
120
+ return `${lines.join('\n').trimEnd()}\n`;
121
+ }
122
+ function renderComponents(model) {
123
+ const lines = ['# Components', ''];
124
+ for (const component of model.components) {
125
+ lines.push(`## ${component.name}`, '');
126
+ lines.push(`Source: \`${component.sourceLocation.filePath}:${component.sourceLocation.line}:${component.sourceLocation.column}\``, '');
127
+ if (component.description) {
128
+ lines.push(component.description, '');
129
+ }
130
+ if (component.exportPaths.length > 0) {
131
+ lines.push(`Export paths: ${component.exportPaths.map((path) => `\`${path}\``).join(', ')}`, '');
132
+ }
133
+ if (component.props.length > 0) {
134
+ lines.push('| Prop | Type | Required | Description |');
135
+ lines.push('| --- | --- | --- | --- |');
136
+ for (const prop of component.props) {
137
+ lines.push(`| ${escapeTableCell(prop.name)} | \`${escapeTableCell(prop.type)}\` | ${prop.required ? 'yes' : 'no'} | ${escapeTableCell(prop.description ?? '')} |`);
138
+ }
139
+ lines.push('');
140
+ }
141
+ }
142
+ return `${lines.join('\n').trimEnd()}\n`;
143
+ }
144
+ function escapeTableCell(value) {
145
+ return value.replaceAll('|', '\\|');
146
+ }
147
+ function badgeLabel(model, badgePath) {
148
+ const fileName = badgePath.split('/').pop();
149
+ if (!fileName) {
150
+ return badgePath;
151
+ }
152
+ const id = fileName.replace(/\.svg$/, '');
153
+ const badge = model.badges.find((entry) => entry.id === id);
154
+ return badge ? `${badge.label}: ${badge.value}` : badgePath;
155
+ }
@@ -1,10 +1,30 @@
1
+ import type { DocumentationModel } from '../model/types.js';
1
2
  /***
2
3
  * Rendered documentation files ready to be written to disk.
3
4
  */
5
+ export interface DiagramArtifact {
6
+ path: string;
7
+ title: string;
8
+ content: string;
9
+ }
10
+ export interface BadgeArtifact {
11
+ path: string;
12
+ content: string;
13
+ }
4
14
  export interface RenderResult {
5
15
  readme: string;
6
16
  exportsMarkdown: string;
7
17
  components: string;
8
18
  exportsJson: string;
9
19
  paradoxJson: string;
20
+ indexHtml: string;
21
+ diagrams: DiagramArtifact[];
22
+ badges: BadgeArtifact[];
23
+ }
24
+ export interface RenderContext {
25
+ model: DocumentationModel;
26
+ outputDir: string;
27
+ diagrams: DiagramArtifact[];
28
+ badges: BadgeArtifact[];
29
+ result: RenderResult;
10
30
  }
@@ -1,5 +1,5 @@
1
1
  import { mkdir, writeFile } from 'node:fs/promises';
2
- import { join } from 'node:path';
2
+ import { dirname, join } from 'node:path';
3
3
  /***
4
4
  * Writes generated documentation artifacts to the configured output paths.
5
5
  */
@@ -12,6 +12,17 @@ export async function write(result, config, runtime) {
12
12
  await writeFile(join(outputRoot, 'components.md'), result.components);
13
13
  await writeFile(join(outputRoot, 'exports.json'), result.exportsJson);
14
14
  await writeFile(join(outputRoot, 'paradox.json'), result.paradoxJson);
15
+ await writeFile(join(outputRoot, 'index.html'), result.indexHtml);
16
+ for (const badge of result.badges) {
17
+ const badgePath = join(outputRoot, badge.path);
18
+ await mkdir(dirname(badgePath), { recursive: true });
19
+ await writeFile(badgePath, badge.content);
20
+ }
21
+ for (const diagram of result.diagrams) {
22
+ const diagramPath = join(outputRoot, diagram.path);
23
+ await mkdir(dirname(diagramPath), { recursive: true });
24
+ await writeFile(diagramPath, diagram.content);
25
+ }
15
26
  if (mode === 'write') {
16
27
  await writeFile(join(root, 'README.md'), result.readme);
17
28
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ankhorage/paradox",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "Deterministic documentation generator for TypeScript packages.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {