@ankhorage/paradox 0.0.9 → 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.
Files changed (46) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +117 -13
  3. package/dist/analyze/analyze.d.ts +0 -3
  4. package/dist/analyze/analyze.js +47 -13
  5. package/dist/analyze/components.d.ts +4 -1
  6. package/dist/analyze/components.js +16 -2
  7. package/dist/analyze/exports.d.ts +1 -0
  8. package/dist/analyze/exports.js +16 -3
  9. package/dist/analyze/semantic/analyzeProject.d.ts +10 -0
  10. package/dist/analyze/semantic/analyzeProject.js +62 -0
  11. package/dist/analyze/semantic/associateDocBlocksWithSymbols.d.ts +5 -0
  12. package/dist/analyze/semantic/associateDocBlocksWithSymbols.js +51 -0
  13. package/dist/analyze/semantic/collectSourceFiles.d.ts +5 -0
  14. package/dist/analyze/semantic/collectSourceFiles.js +22 -0
  15. package/dist/analyze/semantic/createTypeScriptProgram.d.ts +13 -0
  16. package/dist/analyze/semantic/createTypeScriptProgram.js +23 -0
  17. package/dist/analyze/semantic/docBlocks.d.ts +10 -0
  18. package/dist/analyze/semantic/docBlocks.js +102 -0
  19. package/dist/analyze/semantic/exports.d.ts +29 -0
  20. package/dist/analyze/semantic/exports.js +399 -0
  21. package/dist/analyze/semantic/graphs.d.ts +13 -0
  22. package/dist/analyze/semantic/graphs.js +151 -0
  23. package/dist/analyze/semantic/isReactComponent.d.ts +5 -0
  24. package/dist/analyze/semantic/isReactComponent.js +30 -0
  25. package/dist/analyze/semantic/model.d.ts +109 -0
  26. package/dist/analyze/semantic/model.js +1 -0
  27. package/dist/analyze/semantic/paradoxComment.d.ts +16 -0
  28. package/dist/analyze/semantic/paradoxComment.js +64 -0
  29. package/dist/analyze/semantic/tagRegistry.d.ts +2 -0
  30. package/dist/analyze/semantic/tagRegistry.js +1 -0
  31. package/dist/analyze/semantic/utils.d.ts +4 -0
  32. package/dist/analyze/semantic/utils.js +13 -0
  33. package/dist/analyze/types.d.ts +55 -1
  34. package/dist/analyze/utils/parseParadoxComment.d.ts +7 -0
  35. package/dist/analyze/utils/parseParadoxComment.js +61 -14
  36. package/dist/config/defineParadoxConfig.d.ts +2 -0
  37. package/dist/config/defineParadoxConfig.js +2 -0
  38. package/dist/config/types.d.ts +1 -0
  39. package/dist/model/buildModel.d.ts +56 -7
  40. package/dist/model/buildModel.js +16 -0
  41. package/dist/model/types.d.ts +62 -8
  42. package/dist/paths/policy.d.ts +0 -15
  43. package/dist/paths/policy.js +0 -9
  44. package/dist/render/renderers/diagrams.js +11 -0
  45. package/dist/render/renderers/markdown.js +235 -39
  46. package/package.json +15 -6
@@ -12,6 +12,7 @@ export interface DocumentationModel {
12
12
  modules: ModuleModel[];
13
13
  exports: ExportModel[];
14
14
  components: ComponentModel[];
15
+ graphs: GraphModel;
15
16
  }
16
17
  export interface GeneratedBadge {
17
18
  id: string;
@@ -19,22 +20,26 @@ export interface GeneratedBadge {
19
20
  value: string;
20
21
  color: string;
21
22
  }
22
- export interface UsageModel {
23
+ interface UsageModel {
23
24
  packageName: string;
24
25
  commands: UsageCommandModel[];
25
26
  }
26
- export interface UsageCommandModel {
27
+ interface UsageCommandModel {
27
28
  name: string;
28
29
  command: string;
29
30
  }
30
- export interface ConfigModel {
31
+ interface ConfigModel {
31
32
  exportName: string;
33
+ isReadme: boolean;
32
34
  configFile: string;
33
35
  factoryName: string | null;
36
+ members: ConfigMemberModel[];
34
37
  }
35
38
  export interface ExportModel {
36
39
  name: string;
37
40
  description: string | null;
41
+ isReadme: boolean;
42
+ examples: ExampleModel[];
38
43
  kind: ExportKind;
39
44
  modulePath: string;
40
45
  sourceLocation: SourceLocationModel;
@@ -47,34 +52,44 @@ export type ExportKind = 'function' | 'type' | 'unknown';
47
52
  export interface ComponentModel {
48
53
  name: string;
49
54
  description: string | null;
55
+ isReadme: boolean;
56
+ examples: ExampleModel[];
50
57
  modulePath: string;
51
58
  sourceLocation: SourceLocationModel;
52
59
  exportPaths: string[];
53
60
  props: PropModel[];
54
61
  }
55
- export interface SourceLocationModel {
62
+ interface ExampleModel {
63
+ title: string | null;
64
+ language: string | null;
65
+ code: string;
66
+ }
67
+ interface SourceLocationModel {
56
68
  filePath: string;
57
69
  line: number;
58
70
  column: number;
59
71
  }
60
- export interface SignatureModel {
72
+ interface SignatureModel {
61
73
  label: string;
62
74
  parameters: ParameterModel[];
63
75
  returnType: string | null;
64
76
  returnDescription: string | null;
65
77
  }
66
- export interface ParameterModel {
78
+ interface ParameterModel {
67
79
  name: string;
68
80
  type: string;
69
81
  required: boolean;
70
82
  description: string | null;
71
83
  }
72
- export interface MemberModel {
84
+ interface MemberModel {
73
85
  name: string;
74
86
  kind: 'property' | 'method';
75
87
  type: string;
76
88
  required: boolean;
77
89
  description: string | null;
90
+ defaultValue?: string;
91
+ inheritedFrom?: string;
92
+ children?: MemberModel[];
78
93
  }
79
94
  export interface ModuleModel {
80
95
  path: string;
@@ -82,9 +97,48 @@ export interface ModuleModel {
82
97
  dependencies: string[];
83
98
  exports: string[];
84
99
  }
85
- export interface PropModel {
100
+ interface PropModel {
101
+ name: string;
102
+ type: string;
103
+ required: boolean;
104
+ defaultValue?: string;
105
+ description: string | null;
106
+ }
107
+ interface ConfigMemberModel {
86
108
  name: string;
87
109
  type: string;
88
110
  required: boolean;
89
111
  description: string | null;
112
+ defaultValue?: string;
113
+ inheritedFrom?: string;
114
+ children?: ConfigMemberModel[];
115
+ }
116
+ interface GraphModel {
117
+ imports: ImportEdgeModel[];
118
+ calls: CallEdgeModel[];
119
+ typeReferences: TypeReferenceEdgeModel[];
120
+ componentComposition: ComponentCompositionEdgeModel[];
121
+ }
122
+ interface ImportEdgeModel {
123
+ fromPath: string;
124
+ toPath: string;
125
+ sourcePath: string;
126
+ }
127
+ interface CallEdgeModel {
128
+ fromSymbol: string;
129
+ toSymbol: string;
130
+ callExpression: string;
131
+ sourcePath: string;
132
+ }
133
+ interface TypeReferenceEdgeModel {
134
+ fromSymbol: string;
135
+ toType: string;
136
+ sourcePath: string;
137
+ }
138
+ interface ComponentCompositionEdgeModel {
139
+ fromComponent: string;
140
+ toComponent: string;
141
+ jsxElement: string;
142
+ sourcePath: string;
90
143
  }
144
+ export {};
@@ -1,12 +1,4 @@
1
1
  import type { ParadoxConfig } from '../config/types.js';
2
- export interface ResolvedRuntimePaths {
3
- cwd: string;
4
- configFilePath: string;
5
- configDir: string;
6
- packageRoot: string;
7
- outputDir: string;
8
- outputRoot: string;
9
- }
10
2
  export declare function findParadoxConfigFile(startDir: string): Promise<string | null>;
11
3
  export declare function loadParadoxConfig(configFilePath: string): Promise<ParadoxConfig>;
12
4
  export declare function resolvePackageRoot(config: ParadoxConfig, configDir: string): Promise<string>;
@@ -14,10 +6,3 @@ export declare function resolveOutputRoot(config: ParadoxConfig, packageRoot: st
14
6
  outputDir: string;
15
7
  outputRoot: string;
16
8
  };
17
- export declare function resolveWriteTargets(packageRoot: string, outputRoot: string): {
18
- exportsMarkdown: string;
19
- componentsMarkdown: string;
20
- exportsJson: string;
21
- paradoxJson: string;
22
- readme: string;
23
- };
@@ -48,15 +48,6 @@ export function resolveOutputRoot(config, packageRoot) {
48
48
  assertWithinRoot(outputRoot, packageRoot, `Resolved output directory escapes package root: ${outputRoot}`);
49
49
  return { outputDir, outputRoot };
50
50
  }
51
- export function resolveWriteTargets(packageRoot, outputRoot) {
52
- return {
53
- exportsMarkdown: join(outputRoot, 'exports.md'),
54
- componentsMarkdown: join(outputRoot, 'components.md'),
55
- exportsJson: join(outputRoot, 'exports.json'),
56
- paradoxJson: join(outputRoot, 'paradox.json'),
57
- readme: join(packageRoot, 'README.md'),
58
- };
59
- }
60
51
  async function pathExists(path) {
61
52
  try {
62
53
  await access(path);
@@ -73,6 +73,17 @@ function renderExportGraph(model) {
73
73
  }
74
74
  function renderEntrypointSequence(model) {
75
75
  const lines = ['sequenceDiagram'];
76
+ const callEdges = model.graphs.calls;
77
+ if (callEdges.length > 0) {
78
+ const participants = new Map();
79
+ for (const edge of callEdges) {
80
+ participants.set(edge.fromSymbol, `participant ${toMermaidId(`participant-${edge.fromSymbol}`)} as ${edge.fromSymbol}`);
81
+ participants.set(edge.toSymbol, `participant ${toMermaidId(`participant-${edge.toSymbol}`)} as ${edge.toSymbol}`);
82
+ }
83
+ lines.push(...participants.values());
84
+ lines.push(...callEdges.map((edge) => ` ${toMermaidId(`participant-${edge.fromSymbol}`)}->>${toMermaidId(`participant-${edge.toSymbol}`)}: ${edge.callExpression}`));
85
+ return `${lines.join('\n')}\n`;
86
+ }
76
87
  const participants = new Map();
77
88
  for (const module of model.modules) {
78
89
  participants.set(module.path, `participant ${toMermaidId(`participant-${module.path}`)} as ${module.path}`);
@@ -9,7 +9,13 @@ export function renderMarkdown({ badges, diagrams, model, outputDir, }) {
9
9
  };
10
10
  }
11
11
  function renderReadme(model, outputDir, badges, diagrams) {
12
- const lines = [`# ${model.packageName}`, ''];
12
+ const lines = [
13
+ '<!-- markdownlint-disable MD013 MD033 -->',
14
+ '<!-- This file is generated by Paradox. Do not edit manually. -->',
15
+ '',
16
+ `# ${model.packageName}`,
17
+ '',
18
+ ];
13
19
  if (badges.length > 0) {
14
20
  lines.push(badges
15
21
  .map((badge) => `![${badgeLabel(model, badge.path)}](./${outputDir}/${badge.path})`)
@@ -19,35 +25,68 @@ function renderReadme(model, outputDir, badges, diagrams) {
19
25
  lines.push(model.description, '');
20
26
  }
21
27
  if (model.usage !== null) {
22
- lines.push('## Usage', '');
28
+ lines.push('## Installation', '');
23
29
  lines.push('```bash');
24
30
  for (const command of model.usage.commands) {
25
31
  lines.push(command.command);
26
32
  }
27
33
  lines.push('```', '');
28
34
  }
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;');
35
+ renderDocumentationTags(lines);
36
+ if (model.config?.isReadme) {
37
+ renderConfiguration(lines, model);
38
+ }
39
+ renderGeneratedDocumentation(lines, outputDir, diagrams);
40
+ renderArchitecturePreview(lines, diagrams);
41
+ renderPathResolution(lines);
42
+ renderReadmeApi(lines, model);
43
+ return `${lines.join('\n').trimEnd()}\n`;
44
+ }
45
+ function renderDocumentationTags(lines) {
46
+ lines.push('## Documentation Tags', '');
47
+ for (const tag of DOCUMENTATION_TAGS) {
48
+ lines.push('<details>');
49
+ lines.push(`<summary>@${tag.name}</summary>`, '');
50
+ lines.push(tag.description, '');
51
+ lines.push('</details>', '');
52
+ }
53
+ }
54
+ function renderConfiguration(lines, model) {
55
+ const { config } = model;
56
+ if (config === null)
57
+ return;
58
+ lines.push('## Configuration', '');
59
+ lines.push(`Create a \`${config.configFile}\` file:`, '');
60
+ lines.push('```ts');
61
+ if (config.factoryName !== null) {
62
+ lines.push(`import { ${config.factoryName} } from '${model.packageId}';`);
63
+ lines.push('');
64
+ lines.push(`export default ${config.factoryName}({`);
65
+ lines.push(' // ...');
66
+ lines.push('});');
67
+ }
68
+ else {
69
+ lines.push(`import type { ${config.exportName} } from '${model.packageId}';`);
70
+ lines.push('');
71
+ lines.push('const config = {');
72
+ lines.push(' // ...');
73
+ lines.push(`} satisfies ${config.exportName};`);
74
+ lines.push('');
75
+ lines.push('export default config;');
76
+ }
77
+ lines.push('```', '');
78
+ if (config.members.length > 0) {
79
+ lines.push('<details>');
80
+ lines.push('<summary>Configuration options</summary>', '');
81
+ lines.push('| Field | Type | Required | Default | Description |');
82
+ lines.push('| --- | --- | --- | --- | --- |');
83
+ for (const configMember of flattenConfigMembers(config.members)) {
84
+ lines.push(`| ${escapeTableCell(configMember.path)} | \`${escapeTableCell(configMember.type)}\` | ${configMember.required ? 'yes' : 'no'} | ${renderDefault(configMember.defaultValue)} | ${escapeTableCell(configMember.description ?? '')} |`);
48
85
  }
49
- lines.push('```', '');
86
+ lines.push('', '</details>', '');
50
87
  }
88
+ }
89
+ function renderGeneratedDocumentation(lines, outputDir, diagrams) {
51
90
  lines.push('## Generated documentation', '');
52
91
  lines.push(`- [Interactive documentation app](./${outputDir}/index.html)`);
53
92
  lines.push(`- [Public API reference](./${outputDir}/exports.md)`);
@@ -56,12 +95,19 @@ function renderReadme(model, outputDir, badges, diagrams) {
56
95
  lines.push(`- [${diagram.title}](./${outputDir}/${diagram.path})`);
57
96
  }
58
97
  lines.push('');
98
+ }
99
+ function renderArchitecturePreview(lines, diagrams) {
59
100
  lines.push('## Architecture preview', '');
60
101
  if (diagrams.length > 0) {
102
+ lines.push('<details>');
103
+ lines.push('<summary>Architecture overview</summary>', '');
61
104
  lines.push('```mermaid');
62
- lines.push(diagrams[0].content.trimEnd());
105
+ lines.push(diagrams[0]?.content.trimEnd() ?? '');
63
106
  lines.push('```', '');
107
+ lines.push('</details>', '');
64
108
  }
109
+ }
110
+ function renderPathResolution(lines) {
65
111
  lines.push('## Path resolution', '');
66
112
  lines.push('- Config discovery: searches upward from `process.cwd()` for `paradox.config.ts/js/mjs/cjs` (required; no fallback).');
67
113
  lines.push('- Package root: defaults to the directory containing `paradox.config.*`; `package.root` (when relative) resolves relative to that directory.');
@@ -69,22 +115,131 @@ function renderReadme(model, outputDir, badges, diagrams) {
69
115
  lines.push('- Modes:');
70
116
  lines.push(' - `safe`: writes generated artifacts only under the output directory');
71
117
  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(', ')}`);
118
+ }
119
+ function renderReadmeApi(lines, model) {
120
+ const groups = getReadmeGroups(model);
121
+ if (groups.length === 0)
122
+ return;
123
+ lines.push('## Public API', '');
124
+ for (const group of groups) {
125
+ lines.push(`### ${group.title}`, '');
126
+ for (const item of group.items) {
127
+ if (item.kind === 'component') {
128
+ renderComponentAccordion(lines, item.component, item.exportEntry);
129
+ }
130
+ else {
131
+ renderExportAccordion(lines, item.exportEntry);
83
132
  }
84
- lines.push('');
85
133
  }
86
134
  }
87
- return `${lines.join('\n').trimEnd()}\n`;
135
+ }
136
+ function renderComponentAccordion(lines, component, exportEntry) {
137
+ lines.push('<details>');
138
+ lines.push(`<summary>${component.name}</summary>`, '');
139
+ renderSignature(lines, exportEntry);
140
+ if (component.description)
141
+ lines.push(component.description, '');
142
+ renderExamples(lines, component.examples);
143
+ if (exportEntry && exportEntry.relatedSymbols.length > 0) {
144
+ lines.push(`Related types: ${exportEntry.relatedSymbols.map((symbol) => `\`${symbol}\``).join(', ')}`, '');
145
+ }
146
+ if (component.props.length > 0) {
147
+ lines.push('<details>');
148
+ lines.push('<summary>Props</summary>', '');
149
+ lines.push('| Prop | Type | Required | Default | Description |');
150
+ lines.push('| --- | --- | --- | --- | --- |');
151
+ for (const prop of component.props) {
152
+ lines.push(`| ${escapeTableCell(prop.name)} | \`${escapeTableCell(prop.type)}\` | ${prop.required ? 'yes' : 'no'} | ${renderDefault(prop.defaultValue)} | ${escapeTableCell(prop.description ?? '')} |`);
153
+ }
154
+ lines.push('', '</details>', '');
155
+ }
156
+ lines.push('</details>', '');
157
+ }
158
+ function renderExportAccordion(lines, item) {
159
+ lines.push('<details>');
160
+ lines.push(`<summary>${item.name}</summary>`, '');
161
+ renderSignature(lines, item);
162
+ lines.push(item.description ?? `\`${item.kind}\` export.`, '');
163
+ renderExamples(lines, item.examples);
164
+ lines.push(`Module: \`${item.modulePath}\``);
165
+ lines.push(`Source: \`${item.sourceLocation.filePath}:${item.sourceLocation.line}:${item.sourceLocation.column}\``);
166
+ if (item.relatedSymbols.length > 0) {
167
+ lines.push(`Related symbols: ${item.relatedSymbols.map((symbol) => `\`${symbol}\``).join(', ')}`);
168
+ }
169
+ lines.push('', '</details>', '');
170
+ }
171
+ function renderSignature(lines, item) {
172
+ const signature = item?.signatures[0]?.label;
173
+ if (!signature)
174
+ return;
175
+ lines.push('```ts');
176
+ lines.push(`${item.name}${signature}`);
177
+ lines.push('```', '');
178
+ }
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
+ }
188
+ function getReadmeGroups(model) {
189
+ const exportsByName = new Map(model.exports.map((entry) => [entry.name, entry]));
190
+ const componentNames = new Set(model.components.map((component) => component.name));
191
+ const groups = new Map();
192
+ for (const component of model.components.filter((entry) => entry.isReadme)) {
193
+ addReadmeItem(groups, getReadmeCategory(component.modulePath, component.name), {
194
+ kind: 'component',
195
+ component,
196
+ exportEntry: exportsByName.get(component.name),
197
+ });
198
+ }
199
+ for (const item of model.exports.filter((entry) => entry.isReadme)) {
200
+ if (componentNames.has(item.name))
201
+ continue;
202
+ addReadmeItem(groups, getReadmeCategory(item.modulePath, item.name), {
203
+ kind: 'export',
204
+ exportEntry: item,
205
+ });
206
+ }
207
+ return CATEGORY_ORDER.flatMap((title) => {
208
+ const items = groups.get(title);
209
+ if (!items || items.length === 0)
210
+ return [];
211
+ return [{ title, items: sortReadmeItems(items) }];
212
+ });
213
+ }
214
+ function addReadmeItem(groups, title, item) {
215
+ const existing = groups.get(title) ?? [];
216
+ existing.push(item);
217
+ groups.set(title, existing);
218
+ }
219
+ function sortReadmeItems(items) {
220
+ return [...items].sort((left, right) => getReadmeItemName(left).localeCompare(getReadmeItemName(right)));
221
+ }
222
+ function getReadmeItemName(item) {
223
+ return item.kind === 'component' ? item.component.name : item.exportEntry.name;
224
+ }
225
+ function getReadmeCategory(modulePath, name) {
226
+ if (modulePath.includes('/config/'))
227
+ return 'Configuration';
228
+ if (modulePath.includes('/primitives/'))
229
+ return 'Primitives';
230
+ if (modulePath.includes('/components/'))
231
+ return 'Components';
232
+ if (modulePath.includes('/patterns/'))
233
+ return 'Patterns';
234
+ if (modulePath.includes('/layout/'))
235
+ return 'Layout';
236
+ if (modulePath.includes('/hooks/') || /^use[A-Z]/.test(name))
237
+ return 'Hooks';
238
+ if (modulePath.includes('/utils/'))
239
+ return 'Utilities';
240
+ if (modulePath.endsWith('types.ts') || modulePath.includes('/types/'))
241
+ return 'Types';
242
+ return 'Utilities';
88
243
  }
89
244
  function renderExports(model) {
90
245
  const lines = ['# Public API', ''];
@@ -131,10 +286,10 @@ function renderComponents(model) {
131
286
  lines.push(`Export paths: ${component.exportPaths.map((path) => `\`${path}\``).join(', ')}`, '');
132
287
  }
133
288
  if (component.props.length > 0) {
134
- lines.push('| Prop | Type | Required | Description |');
135
- lines.push('| --- | --- | --- | --- |');
289
+ lines.push('| Prop | Type | Required | Default | Description |');
290
+ lines.push('| --- | --- | --- | --- | --- |');
136
291
  for (const prop of component.props) {
137
- lines.push(`| ${escapeTableCell(prop.name)} | \`${escapeTableCell(prop.type)}\` | ${prop.required ? 'yes' : 'no'} | ${escapeTableCell(prop.description ?? '')} |`);
292
+ lines.push(`| ${escapeTableCell(prop.name)} | \`${escapeTableCell(prop.type)}\` | ${prop.required ? 'yes' : 'no'} | ${renderDefault(prop.defaultValue)} | ${escapeTableCell(prop.description ?? '')} |`);
138
293
  }
139
294
  lines.push('');
140
295
  }
@@ -144,6 +299,23 @@ function renderComponents(model) {
144
299
  function escapeTableCell(value) {
145
300
  return value.replaceAll('|', '\\|');
146
301
  }
302
+ function renderDefault(value) {
303
+ return value === undefined ? '—' : `\`${escapeTableCell(value)}\``;
304
+ }
305
+ function flattenConfigMembers(members, prefix = '') {
306
+ return members.flatMap((member) => {
307
+ const path = prefix ? `${prefix}.${member.name}` : member.name;
308
+ const current = {
309
+ path,
310
+ type: member.type,
311
+ required: member.required,
312
+ description: member.description,
313
+ defaultValue: member.defaultValue,
314
+ };
315
+ const children = member.children ? flattenConfigMembers(member.children, path) : [];
316
+ return [current, ...children];
317
+ });
318
+ }
147
319
  function badgeLabel(model, badgePath) {
148
320
  const fileName = badgePath.split('/').pop();
149
321
  if (!fileName) {
@@ -153,3 +325,27 @@ function badgeLabel(model, badgePath) {
153
325
  const badge = model.badges.find((entry) => entry.id === id);
154
326
  return badge ? `${badge.label}: ${badge.value}` : badgePath;
155
327
  }
328
+ const CATEGORY_ORDER = [
329
+ 'Configuration',
330
+ 'Primitives',
331
+ 'Components',
332
+ 'Patterns',
333
+ 'Layout',
334
+ 'Hooks',
335
+ 'Utilities',
336
+ 'Types',
337
+ ];
338
+ const DOCUMENTATION_TAGS = [
339
+ {
340
+ name: 'readme',
341
+ description: 'Includes a documentation block or exported symbol in README output.',
342
+ },
343
+ {
344
+ name: 'config',
345
+ description: 'Marks a type or interface as part of the Paradox configuration model. `@config` alone does not imply README inclusion; use `@config` plus `@readme` for README output.',
346
+ },
347
+ {
348
+ name: 'example',
349
+ description: 'Adds a titled fenced code example to the generated documentation for a symbol.',
350
+ },
351
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ankhorage/paradox",
3
- "version": "0.0.9",
3
+ "version": "0.1.0",
4
4
  "description": "Deterministic documentation generator for TypeScript packages.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -14,6 +14,14 @@
14
14
  "type": "git",
15
15
  "url": "git+https://github.com/ankhorage/paradox.git"
16
16
  },
17
+ "keywords": [
18
+ "typescript",
19
+ "documentation",
20
+ "readme-generator",
21
+ "static-analysis",
22
+ "ts-morph",
23
+ "developer-tools"
24
+ ],
17
25
  "type": "module",
18
26
  "main": "./dist/index.js",
19
27
  "types": "./dist/index.d.ts",
@@ -39,10 +47,11 @@
39
47
  "changeset:status": "changeset status --since=origin/main",
40
48
  "docs": "bun src/cli.ts",
41
49
  "docs:bunx": "bunx @ankhorage/paradox",
42
- "format": "prettier --write .",
43
- "format:check": "prettier --check .",
44
- "lint": "eslint . --max-warnings=0",
45
- "lint:fix": "eslint . --fix --max-warnings=0",
50
+ "format": "ankhorage-prettier --write .",
51
+ "format:check": "ankhorage-prettier --check .",
52
+ "knip": "ankhorage-knip",
53
+ "lint": "ankhorage-eslint . --max-warnings=0",
54
+ "lint:fix": "ankhorage-eslint . --fix --max-warnings=0",
46
55
  "prepack": "bun run build",
47
56
  "test": "bun test",
48
57
  "typecheck": "bun x tsc --noEmit -p tsconfig.json",
@@ -52,7 +61,7 @@
52
61
  "ts-morph": "^24.0.0"
53
62
  },
54
63
  "devDependencies": {
55
- "@ankhorage/devtools": "^1.0.0",
64
+ "@ankhorage/devtools": "^1.0.5",
56
65
  "@changesets/cli": "^2.31.0",
57
66
  "@types/bun": "^1.3.13",
58
67
  "typescript": "^5.6.3"