@ankhorage/paradox 0.0.4 → 0.0.6
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/CHANGELOG.md +14 -0
- package/README.md +129 -0
- package/dist/analyze/analyze.js +7 -0
- package/dist/analyze/components.js +3 -0
- package/dist/analyze/exports.js +39 -9
- package/dist/analyze/modules.d.ts +9 -0
- package/dist/analyze/modules.js +44 -0
- package/dist/analyze/types.d.ts +41 -0
- package/dist/analyze/utils/getExportMetadata.d.ts +12 -0
- package/dist/analyze/utils/getExportMetadata.js +162 -0
- package/dist/analyze/utils/getPropsFromType.js +3 -1
- package/dist/analyze/utils/parseParadoxComment.d.ts +2 -0
- package/dist/analyze/utils/parseParadoxComment.js +19 -1
- package/dist/cli.js +2 -2
- package/dist/model/buildModel.d.ts +40 -0
- package/dist/model/buildModel.js +49 -4
- package/dist/model/types.d.ts +41 -0
- package/dist/render/render.d.ts +3 -1
- package/dist/render/render.js +21 -87
- package/dist/render/renderers/diagrams.d.ts +6 -0
- package/dist/render/renderers/diagrams.js +106 -0
- package/dist/render/renderers/html.d.ts +5 -0
- package/dist/render/renderers/html.js +317 -0
- package/dist/render/renderers/markdown.d.ts +5 -0
- package/dist/render/renderers/markdown.js +141 -0
- package/dist/render/types.d.ts +14 -0
- package/dist/write/write.js +7 -1
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.0.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 1a836cf: Generate a deterministic static documentation app with computed API metadata and Mermaid diagrams.
|
|
8
|
+
|
|
9
|
+
Paradox now emits an offline `paradox/index.html` documentation app, diagram artifacts, richer export metadata, function signature details, parameter and return descriptions, related symbols, and README links to the generated documentation outputs.
|
|
10
|
+
|
|
11
|
+
## 0.0.5
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- 859c537: Standardize CI/release workflow files and update the Bun tooling baseline.
|
|
16
|
+
|
|
3
17
|
## 0.0.4
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -20,6 +20,124 @@ export default defineParadoxConfig({
|
|
|
20
20
|
});
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
## Generated documentation
|
|
24
|
+
|
|
25
|
+
- [Interactive documentation app](./paradox/index.html)
|
|
26
|
+
- [Public API reference](./paradox/exports.md)
|
|
27
|
+
- [Component registry](./paradox/components.md)
|
|
28
|
+
- [Architecture overview](./paradox/diagrams/architecture-overview.mmd)
|
|
29
|
+
- [Module relationships](./paradox/diagrams/module-relationships.mmd)
|
|
30
|
+
- [Export graph](./paradox/diagrams/export-graph.mmd)
|
|
31
|
+
- [Entrypoint sequence](./paradox/diagrams/entrypoint-sequence.mmd)
|
|
32
|
+
|
|
33
|
+
## Architecture preview
|
|
34
|
+
|
|
35
|
+
```mermaid
|
|
36
|
+
graph TD
|
|
37
|
+
package__ankhorage_paradox["@ankhorage/paradox"]
|
|
38
|
+
entrypoint_src_index_ts["src/index.ts"]
|
|
39
|
+
package__ankhorage_paradox --> entrypoint_src_index_ts
|
|
40
|
+
module_src_analyze_analyze_ts["src/analyze/analyze.ts"]
|
|
41
|
+
package__ankhorage_paradox -.-> module_src_analyze_analyze_ts
|
|
42
|
+
module_src_analyze_analyze_ts --> module_src_analyze_components_ts
|
|
43
|
+
module_src_analyze_analyze_ts --> module_src_analyze_exports_ts
|
|
44
|
+
module_src_analyze_analyze_ts --> module_src_analyze_modules_ts
|
|
45
|
+
module_src_analyze_analyze_ts --> module_src_analyze_project_ts
|
|
46
|
+
module_src_analyze_analyze_ts --> module_src_analyze_types_ts
|
|
47
|
+
module_src_analyze_analyze_ts --> module_src_analyze_usage_ts
|
|
48
|
+
module_src_analyze_analyze_ts --> module_src_config_types_ts
|
|
49
|
+
module_src_analyze_components_ts["src/analyze/components.ts"]
|
|
50
|
+
package__ankhorage_paradox -.-> module_src_analyze_components_ts
|
|
51
|
+
module_src_analyze_components_ts --> module_src_analyze_types_ts
|
|
52
|
+
module_src_analyze_components_ts --> module_src_analyze_utils_getComponentPropsType_ts
|
|
53
|
+
module_src_analyze_components_ts --> module_src_analyze_utils_getPropsFromType_ts
|
|
54
|
+
module_src_analyze_components_ts --> module_src_analyze_utils_isReactComponent_ts
|
|
55
|
+
module_src_analyze_exports_ts["src/analyze/exports.ts"]
|
|
56
|
+
package__ankhorage_paradox -.-> module_src_analyze_exports_ts
|
|
57
|
+
module_src_analyze_exports_ts --> module_src_analyze_types_ts
|
|
58
|
+
module_src_analyze_exports_ts --> module_src_analyze_utils_getExportMetadata_ts
|
|
59
|
+
module_src_analyze_exports_ts --> module_src_analyze_utils_getParadoxComment_ts
|
|
60
|
+
module_src_analyze_exports_ts --> module_src_analyze_utils_parseParadoxComment_ts
|
|
61
|
+
module_src_analyze_exports_ts --> module_src_analyze_utils_resolveExportSymbol_ts
|
|
62
|
+
module_src_analyze_modules_ts["src/analyze/modules.ts"]
|
|
63
|
+
package__ankhorage_paradox -.-> module_src_analyze_modules_ts
|
|
64
|
+
module_src_analyze_modules_ts --> module_src_analyze_types_ts
|
|
65
|
+
module_src_analyze_project_ts["src/analyze/project.ts"]
|
|
66
|
+
package__ankhorage_paradox -.-> module_src_analyze_project_ts
|
|
67
|
+
module_src_analyze_types_ts["src/analyze/types.ts"]
|
|
68
|
+
package__ankhorage_paradox -.-> module_src_analyze_types_ts
|
|
69
|
+
module_src_analyze_usage_ts["src/analyze/usage.ts"]
|
|
70
|
+
package__ankhorage_paradox -.-> module_src_analyze_usage_ts
|
|
71
|
+
module_src_analyze_usage_ts --> module_src_analyze_types_ts
|
|
72
|
+
module_src_analyze_utils_getComponentPropsType_ts["src/analyze/utils/getComponentPropsType.ts"]
|
|
73
|
+
package__ankhorage_paradox -.-> module_src_analyze_utils_getComponentPropsType_ts
|
|
74
|
+
module_src_analyze_utils_getExportMetadata_ts["src/analyze/utils/getExportMetadata.ts"]
|
|
75
|
+
package__ankhorage_paradox -.-> module_src_analyze_utils_getExportMetadata_ts
|
|
76
|
+
module_src_analyze_utils_getExportMetadata_ts --> module_src_analyze_types_ts
|
|
77
|
+
module_src_analyze_utils_getExportMetadata_ts --> module_src_analyze_utils_getParadoxComment_ts
|
|
78
|
+
module_src_analyze_utils_getExportMetadata_ts --> module_src_analyze_utils_parseParadoxComment_ts
|
|
79
|
+
module_src_analyze_utils_getParadoxComment_ts["src/analyze/utils/getParadoxComment.ts"]
|
|
80
|
+
package__ankhorage_paradox -.-> module_src_analyze_utils_getParadoxComment_ts
|
|
81
|
+
module_src_analyze_utils_getPropsFromType_ts["src/analyze/utils/getPropsFromType.ts"]
|
|
82
|
+
package__ankhorage_paradox -.-> module_src_analyze_utils_getPropsFromType_ts
|
|
83
|
+
module_src_analyze_utils_getPropsFromType_ts --> module_src_analyze_types_ts
|
|
84
|
+
module_src_analyze_utils_getPropsFromType_ts --> module_src_analyze_utils_getParadoxComment_ts
|
|
85
|
+
module_src_analyze_utils_getPropsFromType_ts --> module_src_analyze_utils_parseParadoxComment_ts
|
|
86
|
+
module_src_analyze_utils_isReactComponent_ts["src/analyze/utils/isReactComponent.ts"]
|
|
87
|
+
package__ankhorage_paradox -.-> module_src_analyze_utils_isReactComponent_ts
|
|
88
|
+
module_src_analyze_utils_parseParadoxComment_ts["src/analyze/utils/parseParadoxComment.ts"]
|
|
89
|
+
package__ankhorage_paradox -.-> module_src_analyze_utils_parseParadoxComment_ts
|
|
90
|
+
module_src_analyze_utils_resolveExportSymbol_ts["src/analyze/utils/resolveExportSymbol.ts"]
|
|
91
|
+
package__ankhorage_paradox -.-> module_src_analyze_utils_resolveExportSymbol_ts
|
|
92
|
+
module_src_cli_ts["src/cli.ts"]
|
|
93
|
+
package__ankhorage_paradox -.-> module_src_cli_ts
|
|
94
|
+
module_src_cli_ts --> module_src_analyze_analyze_ts
|
|
95
|
+
module_src_cli_ts --> module_src_model_buildModel_ts
|
|
96
|
+
module_src_cli_ts --> module_src_paths_policy_ts
|
|
97
|
+
module_src_cli_ts --> module_src_render_render_ts
|
|
98
|
+
module_src_cli_ts --> module_src_write_write_ts
|
|
99
|
+
module_src_config_defineParadoxConfig_ts["src/config/defineParadoxConfig.ts"]
|
|
100
|
+
package__ankhorage_paradox -.-> module_src_config_defineParadoxConfig_ts
|
|
101
|
+
module_src_config_defineParadoxConfig_ts --> module_src_config_types_ts
|
|
102
|
+
module_src_config_types_ts["src/config/types.ts"]
|
|
103
|
+
package__ankhorage_paradox -.-> module_src_config_types_ts
|
|
104
|
+
module_src_index_ts["src/index.ts"]
|
|
105
|
+
module_src_model_buildModel_ts["src/model/buildModel.ts"]
|
|
106
|
+
package__ankhorage_paradox -.-> module_src_model_buildModel_ts
|
|
107
|
+
module_src_model_buildModel_ts --> module_src_model_types_ts
|
|
108
|
+
module_src_model_types_ts["src/model/types.ts"]
|
|
109
|
+
package__ankhorage_paradox -.-> module_src_model_types_ts
|
|
110
|
+
module_src_paths_policy_ts["src/paths/policy.ts"]
|
|
111
|
+
package__ankhorage_paradox -.-> module_src_paths_policy_ts
|
|
112
|
+
module_src_paths_policy_ts --> module_src_config_types_ts
|
|
113
|
+
module_src_render_render_ts["src/render/render.ts"]
|
|
114
|
+
package__ankhorage_paradox -.-> module_src_render_render_ts
|
|
115
|
+
module_src_render_render_ts --> module_src_model_types_ts
|
|
116
|
+
module_src_render_render_ts --> module_src_render_renderers_diagrams_ts
|
|
117
|
+
module_src_render_render_ts --> module_src_render_renderers_html_ts
|
|
118
|
+
module_src_render_render_ts --> module_src_render_renderers_markdown_ts
|
|
119
|
+
module_src_render_render_ts --> module_src_render_types_ts
|
|
120
|
+
module_src_render_renderers_diagrams_ts["src/render/renderers/diagrams.ts"]
|
|
121
|
+
package__ankhorage_paradox -.-> module_src_render_renderers_diagrams_ts
|
|
122
|
+
module_src_render_renderers_diagrams_ts --> module_src_model_types_ts
|
|
123
|
+
module_src_render_renderers_diagrams_ts --> module_src_render_types_ts
|
|
124
|
+
module_src_render_renderers_html_ts["src/render/renderers/html.ts"]
|
|
125
|
+
package__ankhorage_paradox -.-> module_src_render_renderers_html_ts
|
|
126
|
+
module_src_render_renderers_html_ts --> module_src_model_types_ts
|
|
127
|
+
module_src_render_renderers_html_ts --> module_src_render_types_ts
|
|
128
|
+
module_src_render_renderers_markdown_ts["src/render/renderers/markdown.ts"]
|
|
129
|
+
package__ankhorage_paradox -.-> module_src_render_renderers_markdown_ts
|
|
130
|
+
module_src_render_renderers_markdown_ts --> module_src_model_types_ts
|
|
131
|
+
module_src_render_renderers_markdown_ts --> module_src_render_types_ts
|
|
132
|
+
module_src_render_types_ts["src/render/types.ts"]
|
|
133
|
+
package__ankhorage_paradox -.-> module_src_render_types_ts
|
|
134
|
+
module_src_render_types_ts --> module_src_model_types_ts
|
|
135
|
+
module_src_write_write_ts["src/write/write.ts"]
|
|
136
|
+
package__ankhorage_paradox -.-> module_src_write_write_ts
|
|
137
|
+
module_src_write_write_ts --> module_src_config_types_ts
|
|
138
|
+
module_src_write_write_ts --> module_src_render_types_ts
|
|
139
|
+
```
|
|
140
|
+
|
|
23
141
|
## Path resolution
|
|
24
142
|
|
|
25
143
|
- Config discovery: searches upward from `process.cwd()` for `paradox.config.ts/js/mjs/cjs` (required; no fallback).
|
|
@@ -35,6 +153,17 @@ export default defineParadoxConfig({
|
|
|
35
153
|
|
|
36
154
|
Defines a Paradox configuration object without changing its shape.
|
|
37
155
|
|
|
156
|
+
- Kind: `function`
|
|
157
|
+
- Module: `src/config/defineParadoxConfig.ts`
|
|
158
|
+
- Source: `src/config/defineParadoxConfig.ts:6:1`
|
|
159
|
+
- Export paths: `src/index.ts`
|
|
160
|
+
- Related symbols: `ParadoxConfig`
|
|
161
|
+
|
|
38
162
|
### ParadoxConfig
|
|
39
163
|
|
|
40
164
|
Configuration for running Paradox.
|
|
165
|
+
|
|
166
|
+
- Kind: `type`
|
|
167
|
+
- Module: `src/config/types.ts`
|
|
168
|
+
- Source: `src/config/types.ts:6:1`
|
|
169
|
+
- Export paths: `src/index.ts`
|
package/dist/analyze/analyze.js
CHANGED
|
@@ -2,6 +2,7 @@ import { readFile } from 'node:fs/promises';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { analyzeComponents } from './components.js';
|
|
4
4
|
import { analyzeExports } from './exports.js';
|
|
5
|
+
import { analyzeModules } from './modules.js';
|
|
5
6
|
import { createProject } from './project.js';
|
|
6
7
|
import { createUsageFromPackageJson } from './usage.js';
|
|
7
8
|
/***
|
|
@@ -18,12 +19,18 @@ export async function analyze(config, runtime) {
|
|
|
18
19
|
entrypoints,
|
|
19
20
|
});
|
|
20
21
|
const components = analyzeComponents(exports);
|
|
22
|
+
const modules = analyzeModules(project, {
|
|
23
|
+
root,
|
|
24
|
+
entrypoints,
|
|
25
|
+
});
|
|
21
26
|
return {
|
|
22
27
|
packageName: config.docs?.title ?? pkg.name,
|
|
23
28
|
packageId: pkg.name,
|
|
24
29
|
description: config.docs?.description ?? pkg.description ?? null,
|
|
25
30
|
exports,
|
|
26
31
|
components,
|
|
32
|
+
entrypoints: entrypoints.map((entrypoint) => entrypoint.replaceAll('\\', '/')).sort(),
|
|
33
|
+
modules,
|
|
27
34
|
usage,
|
|
28
35
|
config: configMetadata,
|
|
29
36
|
};
|
package/dist/analyze/exports.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { isAbsolute, join, normalize } from 'node:path';
|
|
1
|
+
import { isAbsolute, join, normalize, relative } from 'node:path';
|
|
2
|
+
import { getExportMetadata } from './utils/getExportMetadata.js';
|
|
2
3
|
import { getParadoxComment } from './utils/getParadoxComment.js';
|
|
3
4
|
import { parseParadoxComment } from './utils/parseParadoxComment.js';
|
|
4
5
|
import { resolveExportSymbol } from './utils/resolveExportSymbol.js';
|
|
@@ -6,9 +7,10 @@ import { resolveExportSymbol } from './utils/resolveExportSymbol.js';
|
|
|
6
7
|
* Collects exported declarations from configured package entrypoints.
|
|
7
8
|
*/
|
|
8
9
|
export function analyzeExports(project, options) {
|
|
9
|
-
const
|
|
10
|
+
const exportsByName = new Map();
|
|
10
11
|
let config = null;
|
|
11
12
|
for (const sourceFile of getEntryPointSourceFiles(project, options)) {
|
|
13
|
+
const entrypointPath = toPosixPath(relative(options.root, sourceFile.getFilePath()));
|
|
12
14
|
const exported = sourceFile.getExportSymbols();
|
|
13
15
|
for (const symbol of exported) {
|
|
14
16
|
const resolved = resolveExportSymbol(symbol);
|
|
@@ -16,22 +18,44 @@ export function analyzeExports(project, options) {
|
|
|
16
18
|
const rawComment = getParadoxComment(decl);
|
|
17
19
|
const parsed = rawComment
|
|
18
20
|
? parseParadoxComment(rawComment)
|
|
19
|
-
: { description: null, isConfig: false };
|
|
21
|
+
: { description: null, isConfig: false, params: {}, returns: null };
|
|
22
|
+
const name = resolved.getName();
|
|
20
23
|
if (parsed.isConfig) {
|
|
21
24
|
config = {
|
|
22
|
-
exportName:
|
|
25
|
+
exportName: name,
|
|
23
26
|
};
|
|
24
27
|
}
|
|
25
|
-
|
|
26
|
-
name
|
|
28
|
+
const metadata = getExportMetadata({
|
|
29
|
+
name,
|
|
27
30
|
node: decl,
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
root: options.root,
|
|
32
|
+
entrypointPath,
|
|
33
|
+
symbol: resolved,
|
|
30
34
|
});
|
|
35
|
+
const existing = exportsByName.get(name);
|
|
36
|
+
exportsByName.set(name, existing
|
|
37
|
+
? {
|
|
38
|
+
...existing,
|
|
39
|
+
description: existing.description ?? parsed.description,
|
|
40
|
+
exportPaths: uniqueSorted([...existing.exportPaths, ...metadata.exportPaths]),
|
|
41
|
+
relatedSymbols: uniqueSorted([
|
|
42
|
+
...existing.relatedSymbols,
|
|
43
|
+
...metadata.relatedSymbols,
|
|
44
|
+
]),
|
|
45
|
+
signatures: existing.signatures.length > 0 ? existing.signatures : metadata.signatures,
|
|
46
|
+
members: existing.members.length > 0 ? existing.members : metadata.members,
|
|
47
|
+
}
|
|
48
|
+
: {
|
|
49
|
+
name,
|
|
50
|
+
node: decl,
|
|
51
|
+
description: parsed.description,
|
|
52
|
+
kind: inferKind(decl),
|
|
53
|
+
...metadata,
|
|
54
|
+
});
|
|
31
55
|
}
|
|
32
56
|
}
|
|
33
57
|
return {
|
|
34
|
-
exports,
|
|
58
|
+
exports: [...exportsByName.values()],
|
|
35
59
|
config,
|
|
36
60
|
};
|
|
37
61
|
}
|
|
@@ -50,3 +74,9 @@ function inferKind(node) {
|
|
|
50
74
|
return 'type';
|
|
51
75
|
return 'unknown';
|
|
52
76
|
}
|
|
77
|
+
function uniqueSorted(values) {
|
|
78
|
+
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
79
|
+
}
|
|
80
|
+
function toPosixPath(path) {
|
|
81
|
+
return path.replaceAll('\\', '/');
|
|
82
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Project } from 'ts-morph';
|
|
2
|
+
import type { AnalysisModule } from './types.js';
|
|
3
|
+
/***
|
|
4
|
+
* Builds a deterministic module relationship graph for documentation renderers.
|
|
5
|
+
*/
|
|
6
|
+
export declare function analyzeModules(project: Project, options: {
|
|
7
|
+
root: string;
|
|
8
|
+
entrypoints: readonly string[];
|
|
9
|
+
}): AnalysisModule[];
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { isAbsolute, join, normalize, relative } from 'node:path';
|
|
2
|
+
/***
|
|
3
|
+
* Builds a deterministic module relationship graph for documentation renderers.
|
|
4
|
+
*/
|
|
5
|
+
export function analyzeModules(project, options) {
|
|
6
|
+
const rootPath = normalize(options.root);
|
|
7
|
+
const entrypointPaths = new Set(options.entrypoints.map((entrypoint) => normalize(isAbsolute(entrypoint) ? entrypoint : join(options.root, entrypoint))));
|
|
8
|
+
return project
|
|
9
|
+
.getSourceFiles()
|
|
10
|
+
.filter((sourceFile) => {
|
|
11
|
+
const filePath = normalize(sourceFile.getFilePath());
|
|
12
|
+
const normalizedPath = toPosixPath(filePath);
|
|
13
|
+
return (!sourceFile.isDeclarationFile() &&
|
|
14
|
+
filePath.startsWith(rootPath) &&
|
|
15
|
+
!normalizedPath.includes('/node_modules/'));
|
|
16
|
+
})
|
|
17
|
+
.map((sourceFile) => {
|
|
18
|
+
const path = toPosixPath(relative(options.root, sourceFile.getFilePath()));
|
|
19
|
+
const dependencies = sourceFile
|
|
20
|
+
.getImportDeclarations()
|
|
21
|
+
.map((declaration) => declaration.getModuleSpecifierSourceFile())
|
|
22
|
+
.filter((dependency) => dependency != null)
|
|
23
|
+
.map((dependency) => normalize(dependency.getFilePath()))
|
|
24
|
+
.filter((dependency) => dependency.startsWith(rootPath) && !toPosixPath(dependency).includes('/node_modules/'))
|
|
25
|
+
.map((dependency) => toPosixPath(relative(options.root, dependency)));
|
|
26
|
+
const exports = sourceFile
|
|
27
|
+
.getExportSymbols()
|
|
28
|
+
.map((symbol) => symbol.getName())
|
|
29
|
+
.sort((left, right) => left.localeCompare(right));
|
|
30
|
+
return {
|
|
31
|
+
path,
|
|
32
|
+
isEntrypoint: entrypointPaths.has(normalize(sourceFile.getFilePath())),
|
|
33
|
+
dependencies: uniqueSorted(dependencies),
|
|
34
|
+
exports: uniqueSorted(exports),
|
|
35
|
+
};
|
|
36
|
+
})
|
|
37
|
+
.sort((left, right) => left.path.localeCompare(right.path));
|
|
38
|
+
}
|
|
39
|
+
function uniqueSorted(values) {
|
|
40
|
+
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
41
|
+
}
|
|
42
|
+
function toPosixPath(path) {
|
|
43
|
+
return path.replaceAll('\\', '/');
|
|
44
|
+
}
|
package/dist/analyze/types.d.ts
CHANGED
|
@@ -2,11 +2,41 @@ import type { Node } from 'ts-morph';
|
|
|
2
2
|
/***
|
|
3
3
|
* Describes one exported declaration discovered in a package.
|
|
4
4
|
*/
|
|
5
|
+
export interface AnalysisSourceLocation {
|
|
6
|
+
filePath: string;
|
|
7
|
+
line: number;
|
|
8
|
+
column: number;
|
|
9
|
+
}
|
|
10
|
+
export interface AnalysisParameter {
|
|
11
|
+
name: string;
|
|
12
|
+
type: string;
|
|
13
|
+
required: boolean;
|
|
14
|
+
description: string | null;
|
|
15
|
+
}
|
|
16
|
+
export interface AnalysisSignature {
|
|
17
|
+
label: string;
|
|
18
|
+
parameters: AnalysisParameter[];
|
|
19
|
+
returnType: string | null;
|
|
20
|
+
returnDescription: string | null;
|
|
21
|
+
}
|
|
22
|
+
export interface AnalysisMember {
|
|
23
|
+
name: string;
|
|
24
|
+
kind: 'property' | 'method';
|
|
25
|
+
type: string;
|
|
26
|
+
required: boolean;
|
|
27
|
+
description: string | null;
|
|
28
|
+
}
|
|
5
29
|
export interface AnalysisExport {
|
|
6
30
|
name: string;
|
|
7
31
|
node: Node;
|
|
8
32
|
description: string | null;
|
|
9
33
|
kind: 'function' | 'type' | 'unknown';
|
|
34
|
+
modulePath: string;
|
|
35
|
+
sourceLocation: AnalysisSourceLocation;
|
|
36
|
+
exportPaths: string[];
|
|
37
|
+
relatedSymbols: string[];
|
|
38
|
+
signatures: AnalysisSignature[];
|
|
39
|
+
members: AnalysisMember[];
|
|
10
40
|
}
|
|
11
41
|
/***
|
|
12
42
|
* Describes one React component and its extracted props.
|
|
@@ -14,6 +44,9 @@ export interface AnalysisExport {
|
|
|
14
44
|
export interface AnalysisComponent {
|
|
15
45
|
name: string;
|
|
16
46
|
description: string | null;
|
|
47
|
+
modulePath: string;
|
|
48
|
+
sourceLocation: AnalysisSourceLocation;
|
|
49
|
+
exportPaths: string[];
|
|
17
50
|
props: {
|
|
18
51
|
name: string;
|
|
19
52
|
type: string;
|
|
@@ -29,6 +62,12 @@ export interface AnalysisUsageCommand {
|
|
|
29
62
|
name: string;
|
|
30
63
|
command: string;
|
|
31
64
|
}
|
|
65
|
+
export interface AnalysisModule {
|
|
66
|
+
path: string;
|
|
67
|
+
isEntrypoint: boolean;
|
|
68
|
+
dependencies: string[];
|
|
69
|
+
exports: string[];
|
|
70
|
+
}
|
|
32
71
|
/***
|
|
33
72
|
* Complete analysis output used to build the documentation model.
|
|
34
73
|
*/
|
|
@@ -38,6 +77,8 @@ export interface AnalysisResult {
|
|
|
38
77
|
description: string | null;
|
|
39
78
|
exports: AnalysisExport[];
|
|
40
79
|
components: AnalysisComponent[];
|
|
80
|
+
entrypoints: string[];
|
|
81
|
+
modules: AnalysisModule[];
|
|
41
82
|
usage: AnalysisUsage | null;
|
|
42
83
|
config: {
|
|
43
84
|
exportName: string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Node, type Symbol as MorphSymbol } from 'ts-morph';
|
|
2
|
+
import type { AnalysisExport } from '../types.js';
|
|
3
|
+
/***
|
|
4
|
+
* Extracts computed metadata for an exported declaration.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getExportMetadata(options: {
|
|
7
|
+
name: string;
|
|
8
|
+
node: Node;
|
|
9
|
+
root: string;
|
|
10
|
+
entrypointPath: string;
|
|
11
|
+
symbol: MorphSymbol;
|
|
12
|
+
}): Pick<AnalysisExport, 'exportPaths' | 'members' | 'modulePath' | 'relatedSymbols' | 'signatures' | 'sourceLocation'>;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { relative } from 'node:path';
|
|
2
|
+
import { Node, } from 'ts-morph';
|
|
3
|
+
import { getParadoxComment } from './getParadoxComment.js';
|
|
4
|
+
import { parseParadoxComment } from './parseParadoxComment.js';
|
|
5
|
+
/***
|
|
6
|
+
* Extracts computed metadata for an exported declaration.
|
|
7
|
+
*/
|
|
8
|
+
export function getExportMetadata(options) {
|
|
9
|
+
const modulePath = toPosixPath(relative(options.root, options.node.getSourceFile().getFilePath()));
|
|
10
|
+
const sourceLocation = getSourceLocation(options.node, options.root);
|
|
11
|
+
const signatures = getSignatures(options.symbol, options.node);
|
|
12
|
+
const members = getMembers(options.node);
|
|
13
|
+
const relatedSymbols = collectRelatedSymbols(options.name, signatures.flatMap((signature) => [
|
|
14
|
+
...signature.parameters.map((parameter) => parameter.type),
|
|
15
|
+
signature.returnType,
|
|
16
|
+
]), members.map((member) => member.type));
|
|
17
|
+
return {
|
|
18
|
+
modulePath,
|
|
19
|
+
sourceLocation,
|
|
20
|
+
exportPaths: [options.entrypointPath],
|
|
21
|
+
relatedSymbols,
|
|
22
|
+
signatures,
|
|
23
|
+
members,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function getSourceLocation(node, root) {
|
|
27
|
+
const sourceFile = node.getSourceFile();
|
|
28
|
+
const { column, line } = sourceFile.getLineAndColumnAtPos(node.getStart(false));
|
|
29
|
+
return {
|
|
30
|
+
filePath: toPosixPath(relative(root, sourceFile.getFilePath())),
|
|
31
|
+
line,
|
|
32
|
+
column,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function getSignatures(symbol, node) {
|
|
36
|
+
const parsed = readParadoxMetadata(node);
|
|
37
|
+
const signatures = getCallableDeclarations(symbol, node).map((declaration) => getSignature(declaration, parsed.params, parsed.returns));
|
|
38
|
+
return uniqueBy(signatures.filter((signature) => signature.parameters.length > 0 ||
|
|
39
|
+
signature.returnType !== null ||
|
|
40
|
+
signature.returnDescription !== null), (signature) => signature.label);
|
|
41
|
+
}
|
|
42
|
+
function getSignature(declaration, params, returns) {
|
|
43
|
+
const normalizedParameters = declaration.getParameters().map((parameter) => {
|
|
44
|
+
const parameterDescription = params[parameter.getName()];
|
|
45
|
+
return {
|
|
46
|
+
name: parameter.getName(),
|
|
47
|
+
type: parameter.getType().getText(parameter),
|
|
48
|
+
required: !parameter.isOptional(),
|
|
49
|
+
description: parameterDescription ? parameterDescription.trim() : null,
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
const returnType = declaration.getReturnType().getText(declaration);
|
|
53
|
+
const parameterLabel = normalizedParameters
|
|
54
|
+
.map((parameter) => `${parameter.name}${parameter.required ? '' : '?'}: ${parameter.type}`)
|
|
55
|
+
.join(', ');
|
|
56
|
+
return {
|
|
57
|
+
label: `(${parameterLabel})${returnType === 'void' ? '' : ` => ${returnType}`}`,
|
|
58
|
+
parameters: normalizedParameters,
|
|
59
|
+
returnType,
|
|
60
|
+
returnDescription: returns,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function getMembers(node) {
|
|
64
|
+
if (getCallableNode(node) !== null)
|
|
65
|
+
return [];
|
|
66
|
+
return node
|
|
67
|
+
.getType()
|
|
68
|
+
.getProperties()
|
|
69
|
+
.map((property) => {
|
|
70
|
+
const [declaration] = property.getDeclarations();
|
|
71
|
+
const rawComment = getParadoxComment(declaration);
|
|
72
|
+
const parsed = rawComment
|
|
73
|
+
? parseParadoxComment(rawComment)
|
|
74
|
+
: { description: null, isConfig: false, params: {}, returns: null };
|
|
75
|
+
return {
|
|
76
|
+
name: property.getName(),
|
|
77
|
+
kind: isMemberMethodDeclaration(declaration) ? 'method' : 'property',
|
|
78
|
+
type: property.getTypeAtLocation(declaration).getText(declaration),
|
|
79
|
+
required: !property.isOptional(),
|
|
80
|
+
description: parsed.description,
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function getCallableDeclarations(symbol, node) {
|
|
85
|
+
const declarations = symbol
|
|
86
|
+
.getDeclarations()
|
|
87
|
+
.map((declaration) => getCallableNode(declaration))
|
|
88
|
+
.filter((declaration) => declaration !== null);
|
|
89
|
+
if (declarations.length > 0) {
|
|
90
|
+
return declarations;
|
|
91
|
+
}
|
|
92
|
+
const callableNode = getCallableNode(node);
|
|
93
|
+
return callableNode !== null ? [callableNode] : [];
|
|
94
|
+
}
|
|
95
|
+
function getCallableNode(node) {
|
|
96
|
+
if (Node.isFunctionDeclaration(node))
|
|
97
|
+
return node;
|
|
98
|
+
if (Node.isMethodDeclaration(node))
|
|
99
|
+
return node;
|
|
100
|
+
if (Node.isMethodSignature(node))
|
|
101
|
+
return node;
|
|
102
|
+
if (Node.isArrowFunction(node))
|
|
103
|
+
return node;
|
|
104
|
+
if (Node.isFunctionExpression(node))
|
|
105
|
+
return node;
|
|
106
|
+
if (Node.isVariableDeclaration(node)) {
|
|
107
|
+
const initializer = node.getInitializer();
|
|
108
|
+
if (initializer &&
|
|
109
|
+
(Node.isArrowFunction(initializer) || Node.isFunctionExpression(initializer))) {
|
|
110
|
+
return initializer;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
function isMemberMethodDeclaration(node) {
|
|
116
|
+
return Node.isMethodDeclaration(node) || Node.isMethodSignature(node);
|
|
117
|
+
}
|
|
118
|
+
function readParadoxMetadata(node) {
|
|
119
|
+
const rawComment = getParadoxComment(node);
|
|
120
|
+
return rawComment
|
|
121
|
+
? parseParadoxComment(rawComment)
|
|
122
|
+
: { description: null, isConfig: false, params: {}, returns: null };
|
|
123
|
+
}
|
|
124
|
+
function collectRelatedSymbols(exportName, ...values) {
|
|
125
|
+
const candidates = values.flatMap((entries) => entries).filter((entry) => entry !== null);
|
|
126
|
+
const related = new Set();
|
|
127
|
+
for (const value of candidates) {
|
|
128
|
+
for (const symbol of value.matchAll(/\b[A-Z][A-Za-z0-9_]*\b/g)) {
|
|
129
|
+
const [candidate] = symbol;
|
|
130
|
+
if (!IGNORED_RELATED_SYMBOLS.has(candidate) && candidate !== exportName) {
|
|
131
|
+
related.add(candidate);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return [...related].sort((left, right) => left.localeCompare(right));
|
|
136
|
+
}
|
|
137
|
+
function toPosixPath(path) {
|
|
138
|
+
return path.replaceAll('\\', '/');
|
|
139
|
+
}
|
|
140
|
+
function uniqueBy(items, key) {
|
|
141
|
+
const seen = new Set();
|
|
142
|
+
return items.filter((item) => {
|
|
143
|
+
const itemKey = key(item);
|
|
144
|
+
if (seen.has(itemKey))
|
|
145
|
+
return false;
|
|
146
|
+
seen.add(itemKey);
|
|
147
|
+
return true;
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
const IGNORED_RELATED_SYMBOLS = new Set([
|
|
151
|
+
'Array',
|
|
152
|
+
'Boolean',
|
|
153
|
+
'Date',
|
|
154
|
+
'Element',
|
|
155
|
+
'JSX',
|
|
156
|
+
'Map',
|
|
157
|
+
'Promise',
|
|
158
|
+
'ReadonlyArray',
|
|
159
|
+
'Record',
|
|
160
|
+
'Set',
|
|
161
|
+
'String',
|
|
162
|
+
]);
|
|
@@ -8,7 +8,9 @@ export function getPropsFromType(type) {
|
|
|
8
8
|
const [declaration] = property.getDeclarations();
|
|
9
9
|
const propertyType = property.getTypeAtLocation(declaration);
|
|
10
10
|
const rawComment = getParadoxComment(declaration);
|
|
11
|
-
const parsed = rawComment
|
|
11
|
+
const parsed = rawComment
|
|
12
|
+
? parseParadoxComment(rawComment)
|
|
13
|
+
: { description: null, isConfig: false, params: {}, returns: null };
|
|
12
14
|
return {
|
|
13
15
|
name: property.getName(),
|
|
14
16
|
type: propertyType.getText(declaration),
|
|
@@ -8,12 +8,28 @@ export function parseParadoxComment(rawComment) {
|
|
|
8
8
|
.split('\n')
|
|
9
9
|
.map((line) => line.replace(/^\s*\*\s?/, '').trimEnd());
|
|
10
10
|
let isConfig = false;
|
|
11
|
+
const params = {};
|
|
12
|
+
let returns = null;
|
|
11
13
|
const description = lines
|
|
12
14
|
.filter((line) => {
|
|
13
|
-
|
|
15
|
+
const trimmed = line.trimStart();
|
|
16
|
+
if (trimmed.startsWith('@config')) {
|
|
14
17
|
isConfig = true;
|
|
15
18
|
return false;
|
|
16
19
|
}
|
|
20
|
+
if (trimmed.startsWith('@param ')) {
|
|
21
|
+
const paramBody = trimmed.slice('@param '.length).trim();
|
|
22
|
+
const [name, ...descriptionParts] = paramBody.split(/\s+/);
|
|
23
|
+
if (name) {
|
|
24
|
+
params[name] = descriptionParts.join(' ').trim();
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (trimmed.startsWith('@returns') || trimmed.startsWith('@return')) {
|
|
29
|
+
const returnBody = trimmed.replace(/^@returns?/, '').trim();
|
|
30
|
+
returns = returnBody.length > 0 ? returnBody : null;
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
17
33
|
return true;
|
|
18
34
|
})
|
|
19
35
|
.join('\n')
|
|
@@ -21,5 +37,7 @@ export function parseParadoxComment(rawComment) {
|
|
|
21
37
|
return {
|
|
22
38
|
description: description.length > 0 ? description : null,
|
|
23
39
|
isConfig,
|
|
40
|
+
params,
|
|
41
|
+
returns,
|
|
24
42
|
};
|
|
25
43
|
}
|
package/dist/cli.js
CHANGED
|
@@ -14,10 +14,10 @@ async function main() {
|
|
|
14
14
|
const configDir = dirname(configFilePath);
|
|
15
15
|
const config = await loadParadoxConfig(configFilePath);
|
|
16
16
|
const packageRoot = await resolvePackageRoot(config, configDir);
|
|
17
|
-
const { outputRoot } = resolveOutputRoot(config, packageRoot);
|
|
17
|
+
const { outputDir, outputRoot } = resolveOutputRoot(config, packageRoot);
|
|
18
18
|
const analysis = await analyze(config, { packageRoot });
|
|
19
19
|
const model = buildModel(analysis);
|
|
20
|
-
const result = render(model);
|
|
20
|
+
const result = render(model, { outputDir });
|
|
21
21
|
await write(result, config, { packageRoot, outputRoot });
|
|
22
22
|
}
|
|
23
23
|
main().catch((error) => {
|