@ankhorage/paradox 0.0.9 → 0.0.10

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 (38) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +63 -1
  3. package/dist/analyze/analyze.js +44 -2
  4. package/dist/analyze/components.d.ts +4 -1
  5. package/dist/analyze/components.js +13 -2
  6. package/dist/analyze/semantic/analyzeProject.d.ts +10 -0
  7. package/dist/analyze/semantic/analyzeProject.js +62 -0
  8. package/dist/analyze/semantic/associateDocBlocksWithSymbols.d.ts +5 -0
  9. package/dist/analyze/semantic/associateDocBlocksWithSymbols.js +51 -0
  10. package/dist/analyze/semantic/collectSourceFiles.d.ts +5 -0
  11. package/dist/analyze/semantic/collectSourceFiles.js +22 -0
  12. package/dist/analyze/semantic/createTypeScriptProgram.d.ts +13 -0
  13. package/dist/analyze/semantic/createTypeScriptProgram.js +23 -0
  14. package/dist/analyze/semantic/docBlocks.d.ts +10 -0
  15. package/dist/analyze/semantic/docBlocks.js +102 -0
  16. package/dist/analyze/semantic/exports.d.ts +29 -0
  17. package/dist/analyze/semantic/exports.js +399 -0
  18. package/dist/analyze/semantic/graphs.d.ts +13 -0
  19. package/dist/analyze/semantic/graphs.js +151 -0
  20. package/dist/analyze/semantic/isReactComponent.d.ts +5 -0
  21. package/dist/analyze/semantic/isReactComponent.js +30 -0
  22. package/dist/analyze/semantic/model.d.ts +109 -0
  23. package/dist/analyze/semantic/model.js +1 -0
  24. package/dist/analyze/semantic/paradoxComment.d.ts +16 -0
  25. package/dist/analyze/semantic/paradoxComment.js +64 -0
  26. package/dist/analyze/semantic/tagRegistry.d.ts +2 -0
  27. package/dist/analyze/semantic/tagRegistry.js +1 -0
  28. package/dist/analyze/semantic/utils.d.ts +4 -0
  29. package/dist/analyze/semantic/utils.js +13 -0
  30. package/dist/analyze/types.d.ts +44 -1
  31. package/dist/model/buildModel.d.ts +45 -7
  32. package/dist/model/buildModel.js +10 -0
  33. package/dist/model/types.d.ts +51 -8
  34. package/dist/paths/policy.d.ts +0 -15
  35. package/dist/paths/policy.js +0 -9
  36. package/dist/render/renderers/diagrams.js +11 -0
  37. package/dist/render/renderers/markdown.js +31 -7
  38. package/package.json +15 -6
@@ -0,0 +1,109 @@
1
+ import type { Node, Project, SourceFile, Symbol, Type, TypeChecker } from 'ts-morph';
2
+ export interface AnalyzedProgram {
3
+ project: Project;
4
+ typeChecker: TypeChecker;
5
+ root: string;
6
+ entrypoints: string[];
7
+ entrypointFilePaths: string[];
8
+ }
9
+ export interface AnalyzedFile {
10
+ path: string;
11
+ sourceFile: SourceFile;
12
+ isEntrypoint: boolean;
13
+ }
14
+ export interface AnalyzedDocBlock {
15
+ id: string;
16
+ sourcePath: string;
17
+ start: number;
18
+ end: number;
19
+ line: number;
20
+ column: number;
21
+ raw: string;
22
+ description: string | null;
23
+ params: Record<string, string>;
24
+ returns: string | null;
25
+ tags: AnalyzedTag[];
26
+ }
27
+ export interface AnalyzedTag {
28
+ name: string;
29
+ value: string | null;
30
+ }
31
+ export type AnalyzedExportKind = 'function' | 'component' | 'class' | 'interface' | 'type' | 'const' | 'hook' | 'unknown';
32
+ export interface AnalyzedExport {
33
+ name: string;
34
+ sourcePath: string;
35
+ exportPaths: string[];
36
+ kind: AnalyzedExportKind;
37
+ signature?: string;
38
+ docBlock?: AnalyzedDocBlock;
39
+ tags: AnalyzedTag[];
40
+ props?: AnalyzedProps;
41
+ typeMembers?: AnalyzedTypeMember[];
42
+ relatedTypes: string[];
43
+ node: Node;
44
+ symbol: Symbol;
45
+ }
46
+ export interface ResolvedTypeReference {
47
+ type: Type;
48
+ name: string | null;
49
+ sourcePath: string | null;
50
+ symbol: Symbol | null;
51
+ }
52
+ export interface AnalyzedProps {
53
+ typeName: string;
54
+ sourcePath: string | null;
55
+ members: AnalyzedProp[];
56
+ }
57
+ export interface AnalyzedProp {
58
+ name: string;
59
+ type: string;
60
+ required: boolean;
61
+ defaultValue?: string;
62
+ description?: string;
63
+ inheritedFrom?: string;
64
+ }
65
+ export interface AnalyzedTypeMember {
66
+ name: string;
67
+ type: string;
68
+ required: boolean;
69
+ description?: string;
70
+ defaultValue?: string;
71
+ inheritedFrom?: string;
72
+ children?: AnalyzedTypeMember[];
73
+ }
74
+ export interface ImportEdge {
75
+ fromPath: string;
76
+ toPath: string;
77
+ sourcePath: string;
78
+ }
79
+ export interface CallEdge {
80
+ fromSymbol: string;
81
+ toSymbol: string;
82
+ callExpression: string;
83
+ sourcePath: string;
84
+ }
85
+ interface TypeReferenceEdge {
86
+ fromSymbol: string;
87
+ toType: string;
88
+ sourcePath: string;
89
+ }
90
+ export interface ComponentCompositionEdge {
91
+ fromComponent: string;
92
+ toComponent: string;
93
+ jsxElement: string;
94
+ sourcePath: string;
95
+ }
96
+ interface AnalyzedGraphs {
97
+ imports: ImportEdge[];
98
+ calls: CallEdge[];
99
+ typeReferences: TypeReferenceEdge[];
100
+ componentComposition: ComponentCompositionEdge[];
101
+ }
102
+ export interface AnalyzedProject {
103
+ files: AnalyzedFile[];
104
+ exports: AnalyzedExport[];
105
+ docBlocks: AnalyzedDocBlock[];
106
+ tags: AnalyzedTag[];
107
+ graphs: AnalyzedGraphs;
108
+ }
109
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,16 @@
1
+ import type { Node } from 'ts-morph';
2
+ /***
3
+ * Reads the nearest Paradox doc comment attached to a declaration.
4
+ */
5
+ export declare function getParadoxComment(node: Node | undefined): string | null;
6
+ interface ParsedParadoxComment {
7
+ description: string | null;
8
+ isConfig: boolean;
9
+ params: Record<string, string>;
10
+ returns: string | null;
11
+ }
12
+ /***
13
+ * Parses a Paradox doc comment into structured metadata.
14
+ */
15
+ export declare function parseParadoxComment(rawComment: string): ParsedParadoxComment;
16
+ export {};
@@ -0,0 +1,64 @@
1
+ /***
2
+ * Reads the nearest Paradox doc comment attached to a declaration.
3
+ */
4
+ export function getParadoxComment(node) {
5
+ if (!node)
6
+ return null;
7
+ const sourceFile = node.getSourceFile();
8
+ const text = sourceFile.getFullText();
9
+ const nodeStart = node.getStart(false);
10
+ const beforeNode = text.slice(0, nodeStart);
11
+ const commentStart = beforeNode.lastIndexOf('/***');
12
+ if (commentStart === -1)
13
+ return null;
14
+ const commentEnd = text.indexOf('*/', commentStart);
15
+ if (commentEnd === -1 || commentEnd > nodeStart)
16
+ return null;
17
+ const between = text.slice(commentEnd + 2, nodeStart);
18
+ if (!/^[\s;]*(export\s+)?(default\s+)?$/.test(between))
19
+ return null;
20
+ return text.slice(commentStart, commentEnd + 2);
21
+ }
22
+ /***
23
+ * Parses a Paradox doc comment into structured metadata.
24
+ */
25
+ export function parseParadoxComment(rawComment) {
26
+ const lines = rawComment
27
+ .replace(/^\/\*\*\*/, '')
28
+ .replace(/\*\/$/, '')
29
+ .split('\n')
30
+ .map((line) => line.replace(/^\s*\*\s?/, '').trimEnd());
31
+ let isConfig = false;
32
+ const params = {};
33
+ let returns = null;
34
+ const description = lines
35
+ .filter((line) => {
36
+ const trimmed = line.trimStart();
37
+ if (trimmed.startsWith('@config')) {
38
+ isConfig = true;
39
+ return false;
40
+ }
41
+ if (trimmed.startsWith('@param ')) {
42
+ const paramBody = trimmed.slice('@param '.length).trim();
43
+ const [name, ...descriptionParts] = paramBody.split(/\s+/);
44
+ if (name) {
45
+ params[name] = descriptionParts.join(' ').trim();
46
+ }
47
+ return false;
48
+ }
49
+ if (trimmed.startsWith('@returns') || trimmed.startsWith('@return')) {
50
+ const returnBody = trimmed.replace(/^@returns?/, '').trim();
51
+ returns = returnBody.length > 0 ? returnBody : null;
52
+ return false;
53
+ }
54
+ return true;
55
+ })
56
+ .join('\n')
57
+ .trim();
58
+ return {
59
+ description: description.length > 0 ? description : null,
60
+ isConfig,
61
+ params,
62
+ returns,
63
+ };
64
+ }
@@ -0,0 +1,2 @@
1
+ export type TagRegistry = ReadonlySet<string>;
2
+ export declare const defaultTagRegistry: TagRegistry;
@@ -0,0 +1 @@
1
+ export const defaultTagRegistry = new Set(['readme', 'config']);
@@ -0,0 +1,4 @@
1
+ export declare function toPosixPath(path: string): string;
2
+ export declare function relativeToRoot(root: string, filePath: string): string;
3
+ export declare function isPathInsideRoot(root: string, filePath: string): boolean;
4
+ export declare function isNodeModulePath(filePath: string): boolean;
@@ -0,0 +1,13 @@
1
+ import { normalize, relative } from 'node:path';
2
+ export function toPosixPath(path) {
3
+ return path.replaceAll('\\', '/');
4
+ }
5
+ export function relativeToRoot(root, filePath) {
6
+ return toPosixPath(relative(root, filePath));
7
+ }
8
+ export function isPathInsideRoot(root, filePath) {
9
+ return normalize(filePath).startsWith(normalize(root));
10
+ }
11
+ export function isNodeModulePath(filePath) {
12
+ return toPosixPath(filePath).includes('/node_modules/');
13
+ }
@@ -25,6 +25,9 @@ export interface AnalysisMember {
25
25
  type: string;
26
26
  required: boolean;
27
27
  description: string | null;
28
+ defaultValue?: string;
29
+ inheritedFrom?: string;
30
+ children?: AnalysisMember[];
28
31
  }
29
32
  export interface AnalysisExport {
30
33
  name: string;
@@ -58,7 +61,7 @@ export interface AnalysisUsage {
58
61
  packageName: string;
59
62
  commands: AnalysisUsageCommand[];
60
63
  }
61
- export interface AnalysisUsageCommand {
64
+ interface AnalysisUsageCommand {
62
65
  name: string;
63
66
  command: string;
64
67
  }
@@ -74,6 +77,43 @@ export interface AnalysisModule {
74
77
  dependencies: string[];
75
78
  exports: string[];
76
79
  }
80
+ interface AnalysisTypeMember {
81
+ name: string;
82
+ type: string;
83
+ required: boolean;
84
+ description: string | null;
85
+ defaultValue?: string;
86
+ inheritedFrom?: string;
87
+ children?: AnalysisTypeMember[];
88
+ }
89
+ interface ImportEdge {
90
+ fromPath: string;
91
+ toPath: string;
92
+ sourcePath: string;
93
+ }
94
+ interface CallEdge {
95
+ fromSymbol: string;
96
+ toSymbol: string;
97
+ callExpression: string;
98
+ sourcePath: string;
99
+ }
100
+ interface TypeReferenceEdge {
101
+ fromSymbol: string;
102
+ toType: string;
103
+ sourcePath: string;
104
+ }
105
+ interface ComponentCompositionEdge {
106
+ fromComponent: string;
107
+ toComponent: string;
108
+ jsxElement: string;
109
+ sourcePath: string;
110
+ }
111
+ interface AnalysisGraphs {
112
+ imports: ImportEdge[];
113
+ calls: CallEdge[];
114
+ typeReferences: TypeReferenceEdge[];
115
+ componentComposition: ComponentCompositionEdge[];
116
+ }
77
117
  /***
78
118
  * Complete analysis output used to build the documentation model.
79
119
  */
@@ -89,5 +129,8 @@ export interface AnalysisResult {
89
129
  usage: AnalysisUsage | null;
90
130
  config: {
91
131
  exportName: string;
132
+ members: AnalysisTypeMember[];
92
133
  } | null;
134
+ graphs: AnalysisGraphs;
93
135
  }
136
+ export {};
@@ -1,4 +1,23 @@
1
1
  import type { DocumentationModel, ExportKind } from './types.js';
2
+ interface ExportMemberInput {
3
+ name: string;
4
+ kind: 'property' | 'method';
5
+ type: string;
6
+ required: boolean;
7
+ description: string | null;
8
+ defaultValue?: string;
9
+ inheritedFrom?: string;
10
+ children?: ExportMemberInput[];
11
+ }
12
+ interface ConfigMemberInput {
13
+ name: string;
14
+ type: string;
15
+ required: boolean;
16
+ description: string | null;
17
+ defaultValue?: string;
18
+ inheritedFrom?: string;
19
+ children?: ConfigMemberInput[];
20
+ }
2
21
  interface BuildModelInput {
3
22
  packageName: string;
4
23
  packageId: string;
@@ -32,13 +51,7 @@ interface BuildModelInput {
32
51
  returnType: string | null;
33
52
  returnDescription: string | null;
34
53
  }[];
35
- members: {
36
- name: string;
37
- kind: 'property' | 'method';
38
- type: string;
39
- required: boolean;
40
- description: string | null;
41
- }[];
54
+ members: ExportMemberInput[];
42
55
  }[];
43
56
  components: {
44
57
  name: string;
@@ -66,6 +79,7 @@ interface BuildModelInput {
66
79
  } | null;
67
80
  config: {
68
81
  exportName: string;
82
+ members: ConfigMemberInput[];
69
83
  } | null;
70
84
  entrypoints: string[];
71
85
  modules: {
@@ -74,6 +88,30 @@ interface BuildModelInput {
74
88
  dependencies: string[];
75
89
  exports: string[];
76
90
  }[];
91
+ graphs: {
92
+ imports: {
93
+ fromPath: string;
94
+ toPath: string;
95
+ sourcePath: string;
96
+ }[];
97
+ calls: {
98
+ fromSymbol: string;
99
+ toSymbol: string;
100
+ callExpression: string;
101
+ sourcePath: string;
102
+ }[];
103
+ typeReferences: {
104
+ fromSymbol: string;
105
+ toType: string;
106
+ sourcePath: string;
107
+ }[];
108
+ componentComposition: {
109
+ fromComponent: string;
110
+ toComponent: string;
111
+ jsxElement: string;
112
+ sourcePath: string;
113
+ }[];
114
+ };
77
115
  }
78
116
  /***
79
117
  * Converts analysis output into a serializable documentation model.
@@ -31,6 +31,7 @@ export function buildModel(analysis) {
31
31
  factoryName: findConfigFactoryName(analysis.config.exportName, [
32
32
  ...exportsByName.keys(),
33
33
  ]),
34
+ members: analysis.config.members,
34
35
  }
35
36
  : null,
36
37
  entrypoints: [...analysis.entrypoints].sort((a, b) => a.localeCompare(b)),
@@ -44,6 +45,12 @@ export function buildModel(analysis) {
44
45
  .sort((left, right) => left.path.localeCompare(right.path)),
45
46
  exports,
46
47
  components: sortByName(analysis.components.map((component) => mapComponent(component, exportsByName.get(component.name)))),
48
+ graphs: {
49
+ imports: [...analysis.graphs.imports],
50
+ calls: [...analysis.graphs.calls],
51
+ typeReferences: [...analysis.graphs.typeReferences],
52
+ componentComposition: [...analysis.graphs.componentComposition],
53
+ },
47
54
  };
48
55
  }
49
56
  function mapExport(item, exportNames) {
@@ -78,6 +85,9 @@ function mapExport(item, exportNames) {
78
85
  type: member.type,
79
86
  required: member.required,
80
87
  description: member.description,
88
+ defaultValue: member.defaultValue,
89
+ inheritedFrom: member.inheritedFrom,
90
+ children: member.children,
81
91
  }))),
82
92
  };
83
93
  }
@@ -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,18 +20,19 @@ 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;
32
33
  configFile: string;
33
34
  factoryName: string | null;
35
+ members: ConfigMemberModel[];
34
36
  }
35
37
  export interface ExportModel {
36
38
  name: string;
@@ -52,29 +54,32 @@ export interface ComponentModel {
52
54
  exportPaths: string[];
53
55
  props: PropModel[];
54
56
  }
55
- export interface SourceLocationModel {
57
+ interface SourceLocationModel {
56
58
  filePath: string;
57
59
  line: number;
58
60
  column: number;
59
61
  }
60
- export interface SignatureModel {
62
+ interface SignatureModel {
61
63
  label: string;
62
64
  parameters: ParameterModel[];
63
65
  returnType: string | null;
64
66
  returnDescription: string | null;
65
67
  }
66
- export interface ParameterModel {
68
+ interface ParameterModel {
67
69
  name: string;
68
70
  type: string;
69
71
  required: boolean;
70
72
  description: string | null;
71
73
  }
72
- export interface MemberModel {
74
+ interface MemberModel {
73
75
  name: string;
74
76
  kind: 'property' | 'method';
75
77
  type: string;
76
78
  required: boolean;
77
79
  description: string | null;
80
+ defaultValue?: string;
81
+ inheritedFrom?: string;
82
+ children?: MemberModel[];
78
83
  }
79
84
  export interface ModuleModel {
80
85
  path: string;
@@ -82,9 +87,47 @@ export interface ModuleModel {
82
87
  dependencies: string[];
83
88
  exports: string[];
84
89
  }
85
- export interface PropModel {
90
+ interface PropModel {
86
91
  name: string;
87
92
  type: string;
88
93
  required: boolean;
89
94
  description: string | null;
90
95
  }
96
+ interface ConfigMemberModel {
97
+ name: string;
98
+ type: string;
99
+ required: boolean;
100
+ description: string | null;
101
+ defaultValue?: string;
102
+ inheritedFrom?: string;
103
+ children?: ConfigMemberModel[];
104
+ }
105
+ interface GraphModel {
106
+ imports: ImportEdgeModel[];
107
+ calls: CallEdgeModel[];
108
+ typeReferences: TypeReferenceEdgeModel[];
109
+ componentComposition: ComponentCompositionEdgeModel[];
110
+ }
111
+ interface ImportEdgeModel {
112
+ fromPath: string;
113
+ toPath: string;
114
+ sourcePath: string;
115
+ }
116
+ interface CallEdgeModel {
117
+ fromSymbol: string;
118
+ toSymbol: string;
119
+ callExpression: string;
120
+ sourcePath: string;
121
+ }
122
+ interface TypeReferenceEdgeModel {
123
+ fromSymbol: string;
124
+ toType: string;
125
+ sourcePath: string;
126
+ }
127
+ interface ComponentCompositionEdgeModel {
128
+ fromComponent: string;
129
+ toComponent: string;
130
+ jsxElement: string;
131
+ sourcePath: string;
132
+ }
133
+ 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}`);
@@ -26,27 +26,37 @@ function renderReadme(model, outputDir, badges, diagrams) {
26
26
  }
27
27
  lines.push('```', '');
28
28
  }
29
- if (model.config !== null) {
29
+ const { config } = model;
30
+ if (config !== null) {
30
31
  lines.push('## Configuration', '');
31
- lines.push(`Create a \`${model.config.configFile}\` file:`, '');
32
+ lines.push(`Create a \`${config.configFile}\` file:`, '');
32
33
  lines.push('```ts');
33
- if (model.config.factoryName !== null) {
34
- lines.push(`import { ${model.config.factoryName} } from '${model.packageId}';`);
34
+ if (config.factoryName !== null) {
35
+ lines.push(`import { ${config.factoryName} } from '${model.packageId}';`);
35
36
  lines.push('');
36
- lines.push(`export default ${model.config.factoryName}({`);
37
+ lines.push(`export default ${config.factoryName}({`);
37
38
  lines.push(' // ...');
38
39
  lines.push('});');
39
40
  }
40
41
  else {
41
- lines.push(`import type { ${model.config.exportName} } from '${model.packageId}';`);
42
+ lines.push(`import type { ${config.exportName} } from '${model.packageId}';`);
42
43
  lines.push('');
43
44
  lines.push('const config = {');
44
45
  lines.push(' // ...');
45
- lines.push(`} satisfies ${model.config.exportName};`);
46
+ lines.push(`} satisfies ${config.exportName};`);
46
47
  lines.push('');
47
48
  lines.push('export default config;');
48
49
  }
49
50
  lines.push('```', '');
51
+ if (config.members.length > 0) {
52
+ lines.push('### Configuration options', '');
53
+ lines.push('| Field | Type | Required | Default | Description |');
54
+ lines.push('| --- | --- | --- | --- | --- |');
55
+ for (const configMember of flattenConfigMembers(config.members)) {
56
+ lines.push(`| ${escapeTableCell(configMember.path)} | \`${escapeTableCell(configMember.type)}\` | ${configMember.required ? 'yes' : 'no'} | ${escapeTableCell(configMember.defaultValue ?? '')} | ${escapeTableCell(configMember.description ?? '')} |`);
57
+ }
58
+ lines.push('');
59
+ }
50
60
  }
51
61
  lines.push('## Generated documentation', '');
52
62
  lines.push(`- [Interactive documentation app](./${outputDir}/index.html)`);
@@ -144,6 +154,20 @@ function renderComponents(model) {
144
154
  function escapeTableCell(value) {
145
155
  return value.replaceAll('|', '\\|');
146
156
  }
157
+ function flattenConfigMembers(members, prefix = '') {
158
+ return members.flatMap((member) => {
159
+ const path = prefix ? `${prefix}.${member.name}` : member.name;
160
+ const current = {
161
+ path,
162
+ type: member.type,
163
+ required: member.required,
164
+ description: member.description,
165
+ defaultValue: member.defaultValue,
166
+ };
167
+ const children = member.children ? flattenConfigMembers(member.children, path) : [];
168
+ return [current, ...children];
169
+ });
170
+ }
147
171
  function badgeLabel(model, badgePath) {
148
172
  const fileName = badgePath.split('/').pop();
149
173
  if (!fileName) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ankhorage/paradox",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
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"