@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.
- package/CHANGELOG.md +12 -0
- package/README.md +117 -13
- package/dist/analyze/analyze.d.ts +0 -3
- package/dist/analyze/analyze.js +47 -13
- package/dist/analyze/components.d.ts +4 -1
- package/dist/analyze/components.js +16 -2
- package/dist/analyze/exports.d.ts +1 -0
- package/dist/analyze/exports.js +16 -3
- package/dist/analyze/semantic/analyzeProject.d.ts +10 -0
- package/dist/analyze/semantic/analyzeProject.js +62 -0
- package/dist/analyze/semantic/associateDocBlocksWithSymbols.d.ts +5 -0
- package/dist/analyze/semantic/associateDocBlocksWithSymbols.js +51 -0
- package/dist/analyze/semantic/collectSourceFiles.d.ts +5 -0
- package/dist/analyze/semantic/collectSourceFiles.js +22 -0
- package/dist/analyze/semantic/createTypeScriptProgram.d.ts +13 -0
- package/dist/analyze/semantic/createTypeScriptProgram.js +23 -0
- package/dist/analyze/semantic/docBlocks.d.ts +10 -0
- package/dist/analyze/semantic/docBlocks.js +102 -0
- package/dist/analyze/semantic/exports.d.ts +29 -0
- package/dist/analyze/semantic/exports.js +399 -0
- package/dist/analyze/semantic/graphs.d.ts +13 -0
- package/dist/analyze/semantic/graphs.js +151 -0
- package/dist/analyze/semantic/isReactComponent.d.ts +5 -0
- package/dist/analyze/semantic/isReactComponent.js +30 -0
- package/dist/analyze/semantic/model.d.ts +109 -0
- package/dist/analyze/semantic/model.js +1 -0
- package/dist/analyze/semantic/paradoxComment.d.ts +16 -0
- package/dist/analyze/semantic/paradoxComment.js +64 -0
- package/dist/analyze/semantic/tagRegistry.d.ts +2 -0
- package/dist/analyze/semantic/tagRegistry.js +1 -0
- package/dist/analyze/semantic/utils.d.ts +4 -0
- package/dist/analyze/semantic/utils.js +13 -0
- package/dist/analyze/types.d.ts +55 -1
- package/dist/analyze/utils/parseParadoxComment.d.ts +7 -0
- package/dist/analyze/utils/parseParadoxComment.js +61 -14
- package/dist/config/defineParadoxConfig.d.ts +2 -0
- package/dist/config/defineParadoxConfig.js +2 -0
- package/dist/config/types.d.ts +1 -0
- package/dist/model/buildModel.d.ts +56 -7
- package/dist/model/buildModel.js +16 -0
- package/dist/model/types.d.ts +62 -8
- package/dist/paths/policy.d.ts +0 -15
- package/dist/paths/policy.js +0 -9
- package/dist/render/renderers/diagrams.js +11 -0
- package/dist/render/renderers/markdown.js +235 -39
- package/package.json +15 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- dd41897: Generate compact README docs from `@readme` symbols.
|
|
8
|
+
|
|
9
|
+
## 0.0.10
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 46bc15e: Add a reusable semantic TypeScript analysis foundation for exports, props, type members, and graph metadata.
|
|
14
|
+
|
|
3
15
|
## 0.0.9
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,15 +1,41 @@
|
|
|
1
|
+
<!-- markdownlint-disable MD013 MD033 -->
|
|
2
|
+
<!-- This file is generated by Paradox. Do not edit manually. -->
|
|
3
|
+
|
|
1
4
|
# @ankhorage/paradox
|
|
2
5
|
|
|
3
|
-
         
|
|
4
7
|
|
|
5
8
|
Deterministic documentation generator for TypeScript packages.
|
|
6
9
|
|
|
7
|
-
##
|
|
10
|
+
## Installation
|
|
8
11
|
|
|
9
12
|
```bash
|
|
10
13
|
bunx @ankhorage/paradox
|
|
11
14
|
```
|
|
12
15
|
|
|
16
|
+
## Documentation Tags
|
|
17
|
+
|
|
18
|
+
<details>
|
|
19
|
+
<summary>@readme</summary>
|
|
20
|
+
|
|
21
|
+
Includes a documentation block or exported symbol in README output.
|
|
22
|
+
|
|
23
|
+
</details>
|
|
24
|
+
|
|
25
|
+
<details>
|
|
26
|
+
<summary>@config</summary>
|
|
27
|
+
|
|
28
|
+
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.
|
|
29
|
+
|
|
30
|
+
</details>
|
|
31
|
+
|
|
32
|
+
<details>
|
|
33
|
+
<summary>@example</summary>
|
|
34
|
+
|
|
35
|
+
Adds a titled fenced code example to the generated documentation for a symbol.
|
|
36
|
+
|
|
37
|
+
</details>
|
|
38
|
+
|
|
13
39
|
## Configuration
|
|
14
40
|
|
|
15
41
|
Create a `paradox.config.ts` file:
|
|
@@ -22,6 +48,18 @@ export default defineParadoxConfig({
|
|
|
22
48
|
});
|
|
23
49
|
```
|
|
24
50
|
|
|
51
|
+
<details>
|
|
52
|
+
<summary>Configuration options</summary>
|
|
53
|
+
|
|
54
|
+
| Field | Type | Required | Default | Description |
|
|
55
|
+
| ------- | --------------------------------------------------------- | -------- | ------- | ----------- |
|
|
56
|
+
| mode | `'safe' \| 'write' \| undefined` | no | — | |
|
|
57
|
+
| docs | `{ title?: string; description?: string; } \| undefined` | no | — | |
|
|
58
|
+
| package | `{ root?: string; entrypoints?: string[]; } \| undefined` | no | — | |
|
|
59
|
+
| output | `{ dir?: string; } \| undefined` | no | — | |
|
|
60
|
+
|
|
61
|
+
</details>
|
|
62
|
+
|
|
25
63
|
## Generated documentation
|
|
26
64
|
|
|
27
65
|
- [Interactive documentation app](./paradox/index.html)
|
|
@@ -34,6 +72,9 @@ export default defineParadoxConfig({
|
|
|
34
72
|
|
|
35
73
|
## Architecture preview
|
|
36
74
|
|
|
75
|
+
<details>
|
|
76
|
+
<summary>Architecture overview</summary>
|
|
77
|
+
|
|
37
78
|
```mermaid
|
|
38
79
|
graph TD
|
|
39
80
|
package__ankhorage_paradox["@ankhorage/paradox"]
|
|
@@ -46,6 +87,9 @@ graph TD
|
|
|
46
87
|
module_src_analyze_analyze_ts --> module_src_analyze_exports_ts
|
|
47
88
|
module_src_analyze_analyze_ts --> module_src_analyze_modules_ts
|
|
48
89
|
module_src_analyze_analyze_ts --> module_src_analyze_project_ts
|
|
90
|
+
module_src_analyze_analyze_ts --> module_src_analyze_semantic_createTypeScriptProgram_ts
|
|
91
|
+
module_src_analyze_analyze_ts --> module_src_analyze_semantic_exports_ts
|
|
92
|
+
module_src_analyze_analyze_ts --> module_src_analyze_semantic_graphs_ts
|
|
49
93
|
module_src_analyze_analyze_ts --> module_src_analyze_types_ts
|
|
50
94
|
module_src_analyze_analyze_ts --> module_src_analyze_usage_ts
|
|
51
95
|
module_src_analyze_analyze_ts --> module_src_config_types_ts
|
|
@@ -55,6 +99,8 @@ graph TD
|
|
|
55
99
|
module_src_analyze_badges_ts --> module_src_analyze_usage_ts
|
|
56
100
|
module_src_analyze_components_ts["src/analyze/components.ts"]
|
|
57
101
|
package__ankhorage_paradox -.-> module_src_analyze_components_ts
|
|
102
|
+
module_src_analyze_components_ts --> module_src_analyze_semantic_exports_ts
|
|
103
|
+
module_src_analyze_components_ts --> module_src_analyze_semantic_model_ts
|
|
58
104
|
module_src_analyze_components_ts --> module_src_analyze_types_ts
|
|
59
105
|
module_src_analyze_components_ts --> module_src_analyze_utils_getComponentPropsType_ts
|
|
60
106
|
module_src_analyze_components_ts --> module_src_analyze_utils_getPropsFromType_ts
|
|
@@ -71,6 +117,54 @@ graph TD
|
|
|
71
117
|
module_src_analyze_modules_ts --> module_src_analyze_types_ts
|
|
72
118
|
module_src_analyze_project_ts["src/analyze/project.ts"]
|
|
73
119
|
package__ankhorage_paradox -.-> module_src_analyze_project_ts
|
|
120
|
+
module_src_analyze_semantic_analyzeProject_ts["src/analyze/semantic/analyzeProject.ts"]
|
|
121
|
+
package__ankhorage_paradox -.-> module_src_analyze_semantic_analyzeProject_ts
|
|
122
|
+
module_src_analyze_semantic_analyzeProject_ts --> module_src_analyze_semantic_associateDocBlocksWithSymbols_ts
|
|
123
|
+
module_src_analyze_semantic_analyzeProject_ts --> module_src_analyze_semantic_collectSourceFiles_ts
|
|
124
|
+
module_src_analyze_semantic_analyzeProject_ts --> module_src_analyze_semantic_createTypeScriptProgram_ts
|
|
125
|
+
module_src_analyze_semantic_analyzeProject_ts --> module_src_analyze_semantic_docBlocks_ts
|
|
126
|
+
module_src_analyze_semantic_analyzeProject_ts --> module_src_analyze_semantic_exports_ts
|
|
127
|
+
module_src_analyze_semantic_analyzeProject_ts --> module_src_analyze_semantic_graphs_ts
|
|
128
|
+
module_src_analyze_semantic_analyzeProject_ts --> module_src_analyze_semantic_model_ts
|
|
129
|
+
module_src_analyze_semantic_analyzeProject_ts --> module_src_analyze_semantic_tagRegistry_ts
|
|
130
|
+
module_src_analyze_semantic_associateDocBlocksWithSymbols_ts["src/analyze/semantic/associateDocBlocksWithSymbols.ts"]
|
|
131
|
+
package__ankhorage_paradox -.-> module_src_analyze_semantic_associateDocBlocksWithSymbols_ts
|
|
132
|
+
module_src_analyze_semantic_associateDocBlocksWithSymbols_ts --> module_src_analyze_semantic_model_ts
|
|
133
|
+
module_src_analyze_semantic_associateDocBlocksWithSymbols_ts --> module_src_analyze_semantic_utils_ts
|
|
134
|
+
module_src_analyze_semantic_collectSourceFiles_ts["src/analyze/semantic/collectSourceFiles.ts"]
|
|
135
|
+
package__ankhorage_paradox -.-> module_src_analyze_semantic_collectSourceFiles_ts
|
|
136
|
+
module_src_analyze_semantic_collectSourceFiles_ts --> module_src_analyze_semantic_model_ts
|
|
137
|
+
module_src_analyze_semantic_collectSourceFiles_ts --> module_src_analyze_semantic_utils_ts
|
|
138
|
+
module_src_analyze_semantic_createTypeScriptProgram_ts["src/analyze/semantic/createTypeScriptProgram.ts"]
|
|
139
|
+
package__ankhorage_paradox -.-> module_src_analyze_semantic_createTypeScriptProgram_ts
|
|
140
|
+
module_src_analyze_semantic_createTypeScriptProgram_ts --> module_src_analyze_semantic_model_ts
|
|
141
|
+
module_src_analyze_semantic_createTypeScriptProgram_ts --> module_src_analyze_semantic_utils_ts
|
|
142
|
+
module_src_analyze_semantic_docBlocks_ts["src/analyze/semantic/docBlocks.ts"]
|
|
143
|
+
package__ankhorage_paradox -.-> module_src_analyze_semantic_docBlocks_ts
|
|
144
|
+
module_src_analyze_semantic_docBlocks_ts --> module_src_analyze_semantic_model_ts
|
|
145
|
+
module_src_analyze_semantic_docBlocks_ts --> module_src_analyze_semantic_tagRegistry_ts
|
|
146
|
+
module_src_analyze_semantic_docBlocks_ts --> module_src_analyze_semantic_utils_ts
|
|
147
|
+
module_src_analyze_semantic_exports_ts["src/analyze/semantic/exports.ts"]
|
|
148
|
+
package__ankhorage_paradox -.-> module_src_analyze_semantic_exports_ts
|
|
149
|
+
module_src_analyze_semantic_exports_ts --> module_src_analyze_semantic_isReactComponent_ts
|
|
150
|
+
module_src_analyze_semantic_exports_ts --> module_src_analyze_semantic_model_ts
|
|
151
|
+
module_src_analyze_semantic_exports_ts --> module_src_analyze_semantic_paradoxComment_ts
|
|
152
|
+
module_src_analyze_semantic_exports_ts --> module_src_analyze_semantic_utils_ts
|
|
153
|
+
module_src_analyze_semantic_graphs_ts["src/analyze/semantic/graphs.ts"]
|
|
154
|
+
package__ankhorage_paradox -.-> module_src_analyze_semantic_graphs_ts
|
|
155
|
+
module_src_analyze_semantic_graphs_ts --> module_src_analyze_semantic_exports_ts
|
|
156
|
+
module_src_analyze_semantic_graphs_ts --> module_src_analyze_semantic_model_ts
|
|
157
|
+
module_src_analyze_semantic_graphs_ts --> module_src_analyze_semantic_utils_ts
|
|
158
|
+
module_src_analyze_semantic_isReactComponent_ts["src/analyze/semantic/isReactComponent.ts"]
|
|
159
|
+
package__ankhorage_paradox -.-> module_src_analyze_semantic_isReactComponent_ts
|
|
160
|
+
module_src_analyze_semantic_model_ts["src/analyze/semantic/model.ts"]
|
|
161
|
+
package__ankhorage_paradox -.-> module_src_analyze_semantic_model_ts
|
|
162
|
+
module_src_analyze_semantic_paradoxComment_ts["src/analyze/semantic/paradoxComment.ts"]
|
|
163
|
+
package__ankhorage_paradox -.-> module_src_analyze_semantic_paradoxComment_ts
|
|
164
|
+
module_src_analyze_semantic_tagRegistry_ts["src/analyze/semantic/tagRegistry.ts"]
|
|
165
|
+
package__ankhorage_paradox -.-> module_src_analyze_semantic_tagRegistry_ts
|
|
166
|
+
module_src_analyze_semantic_utils_ts["src/analyze/semantic/utils.ts"]
|
|
167
|
+
package__ankhorage_paradox -.-> module_src_analyze_semantic_utils_ts
|
|
74
168
|
module_src_analyze_types_ts["src/analyze/types.ts"]
|
|
75
169
|
package__ankhorage_paradox -.-> module_src_analyze_types_ts
|
|
76
170
|
module_src_analyze_usage_ts["src/analyze/usage.ts"]
|
|
@@ -150,6 +244,8 @@ graph TD
|
|
|
150
244
|
module_src_write_write_ts --> module_src_render_types_ts
|
|
151
245
|
```
|
|
152
246
|
|
|
247
|
+
</details>
|
|
248
|
+
|
|
153
249
|
## Path resolution
|
|
154
250
|
|
|
155
251
|
- Config discovery: searches upward from `process.cwd()` for `paradox.config.ts/js/mjs/cjs` (required; no fallback).
|
|
@@ -161,21 +257,29 @@ graph TD
|
|
|
161
257
|
|
|
162
258
|
## Public API
|
|
163
259
|
|
|
164
|
-
###
|
|
260
|
+
### Configuration
|
|
261
|
+
|
|
262
|
+
<details>
|
|
263
|
+
<summary>defineParadoxConfig</summary>
|
|
264
|
+
|
|
265
|
+
```ts
|
|
266
|
+
defineParadoxConfig(config: ParadoxConfig) => ParadoxConfig
|
|
267
|
+
```
|
|
165
268
|
|
|
166
269
|
Defines a Paradox configuration object without changing its shape.
|
|
167
270
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
- Export paths: `src/index.ts`
|
|
172
|
-
- Related symbols: `ParadoxConfig`
|
|
271
|
+
Module: `src/config/defineParadoxConfig.ts`
|
|
272
|
+
Source: `src/config/defineParadoxConfig.ts:8:1`
|
|
273
|
+
Related symbols: `ParadoxConfig`
|
|
173
274
|
|
|
174
|
-
|
|
275
|
+
</details>
|
|
276
|
+
|
|
277
|
+
<details>
|
|
278
|
+
<summary>ParadoxConfig</summary>
|
|
175
279
|
|
|
176
280
|
Configuration for running Paradox.
|
|
177
281
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
282
|
+
Module: `src/config/types.ts`
|
|
283
|
+
Source: `src/config/types.ts:7:1`
|
|
284
|
+
|
|
285
|
+
</details>
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import type { ParadoxConfig } from '../config/types.js';
|
|
2
2
|
import type { AnalysisResult } from './types.js';
|
|
3
|
-
/***
|
|
4
|
-
* Runs the source analysis pipeline for a configured package.
|
|
5
|
-
*/
|
|
6
3
|
export declare function analyze(config: ParadoxConfig, runtime: {
|
|
7
4
|
packageRoot: string;
|
|
8
5
|
}): Promise<AnalysisResult>;
|
package/dist/analyze/analyze.js
CHANGED
|
@@ -5,10 +5,10 @@ import { analyzeComponents } from './components.js';
|
|
|
5
5
|
import { analyzeExports } from './exports.js';
|
|
6
6
|
import { analyzeModules } from './modules.js';
|
|
7
7
|
import { createProject } from './project.js';
|
|
8
|
+
import { createTypeScriptProgram } from './semantic/createTypeScriptProgram.js';
|
|
9
|
+
import { collectTypeMembers, resolveTypeReference } from './semantic/exports.js';
|
|
10
|
+
import { collectCallGraph, collectComponentCompositionGraph, collectImportGraph, } from './semantic/graphs.js';
|
|
8
11
|
import { createUsageFromPackageJson } from './usage.js';
|
|
9
|
-
/***
|
|
10
|
-
* Runs the source analysis pipeline for a configured package.
|
|
11
|
-
*/
|
|
12
12
|
export async function analyze(config, runtime) {
|
|
13
13
|
const root = runtime.packageRoot;
|
|
14
14
|
const pkg = await readPackageJson(root);
|
|
@@ -16,15 +16,31 @@ export async function analyze(config, runtime) {
|
|
|
16
16
|
const badges = await analyzeBadges(root, pkg);
|
|
17
17
|
const project = createProject(root);
|
|
18
18
|
const entrypoints = config.package?.entrypoints ?? ['src/index.ts'];
|
|
19
|
-
const {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
});
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
const program = createTypeScriptProgram({ root, entrypoints, project });
|
|
20
|
+
const { config: configMetadata, exports } = analyzeExports(project, { root, entrypoints });
|
|
21
|
+
const components = analyzeComponents(exports, { program });
|
|
22
|
+
const modules = analyzeModules(project, { root, entrypoints });
|
|
23
|
+
const configExport = configMetadata
|
|
24
|
+
? (exports.find((entry) => entry.name === configMetadata.exportName) ?? null)
|
|
25
|
+
: null;
|
|
26
|
+
const configMembers = configExport && (configExport.kind === 'type' || configExport.kind === 'unknown')
|
|
27
|
+
? collectTypeMembers(program, resolveTypeReference(program, configExport.node) ?? {
|
|
28
|
+
type: configExport.node.getType(),
|
|
29
|
+
name: configExport.name,
|
|
30
|
+
sourcePath: configExport.modulePath,
|
|
31
|
+
symbol: configExport.node.getSymbol() ?? null,
|
|
32
|
+
})
|
|
33
|
+
: [];
|
|
34
|
+
const graphs = {
|
|
35
|
+
imports: collectImportGraph(program),
|
|
36
|
+
calls: collectCallGraph(program),
|
|
37
|
+
typeReferences: exports.flatMap((entry) => entry.relatedSymbols.map((symbol) => ({
|
|
38
|
+
fromSymbol: entry.name,
|
|
39
|
+
toType: symbol,
|
|
40
|
+
sourcePath: entry.modulePath,
|
|
41
|
+
}))),
|
|
42
|
+
componentComposition: collectComponentCompositionGraph(program),
|
|
43
|
+
};
|
|
28
44
|
return {
|
|
29
45
|
packageName: config.docs?.title ?? pkg.name,
|
|
30
46
|
packageId: pkg.name,
|
|
@@ -35,9 +51,27 @@ export async function analyze(config, runtime) {
|
|
|
35
51
|
modules,
|
|
36
52
|
badges,
|
|
37
53
|
usage,
|
|
38
|
-
config: configMetadata
|
|
54
|
+
config: configMetadata
|
|
55
|
+
? {
|
|
56
|
+
exportName: configMetadata.exportName,
|
|
57
|
+
isReadme: configMetadata.isReadme,
|
|
58
|
+
members: mapTypeMembers(configMembers),
|
|
59
|
+
}
|
|
60
|
+
: null,
|
|
61
|
+
graphs,
|
|
39
62
|
};
|
|
40
63
|
}
|
|
64
|
+
function mapTypeMembers(members) {
|
|
65
|
+
return members.map((member) => ({
|
|
66
|
+
name: member.name,
|
|
67
|
+
type: member.type,
|
|
68
|
+
required: member.required,
|
|
69
|
+
description: member.description ?? null,
|
|
70
|
+
...(member.defaultValue !== undefined ? { defaultValue: member.defaultValue } : {}),
|
|
71
|
+
...(member.inheritedFrom !== undefined ? { inheritedFrom: member.inheritedFrom } : {}),
|
|
72
|
+
...(member.children ? { children: mapTypeMembers(member.children) } : {}),
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
41
75
|
async function readPackageJson(root) {
|
|
42
76
|
const raw = await readFile(join(root, 'package.json'), 'utf-8');
|
|
43
77
|
return JSON.parse(raw);
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import type { AnalyzedProgram } from './semantic/model.js';
|
|
1
2
|
import type { AnalysisComponent, AnalysisExport } from './types.js';
|
|
2
3
|
/***
|
|
3
4
|
* Extracts React components and their props from analyzed exports.
|
|
4
5
|
*/
|
|
5
|
-
export declare function analyzeComponents(exports: readonly AnalysisExport[]
|
|
6
|
+
export declare function analyzeComponents(exports: readonly AnalysisExport[], options?: {
|
|
7
|
+
program?: AnalyzedProgram;
|
|
8
|
+
}): AnalysisComponent[];
|
|
@@ -1,19 +1,33 @@
|
|
|
1
|
+
import { collectPropsForExport } from './semantic/exports.js';
|
|
1
2
|
import { getComponentPropsType } from './utils/getComponentPropsType.js';
|
|
2
3
|
import { getPropsFromType } from './utils/getPropsFromType.js';
|
|
3
4
|
import { isReactComponent } from './utils/isReactComponent.js';
|
|
4
5
|
/***
|
|
5
6
|
* Extracts React components and their props from analyzed exports.
|
|
6
7
|
*/
|
|
7
|
-
export function analyzeComponents(exports) {
|
|
8
|
+
export function analyzeComponents(exports, options = {}) {
|
|
8
9
|
const components = [];
|
|
9
10
|
for (const e of exports) {
|
|
10
11
|
if (!isReactComponent(e.node))
|
|
11
12
|
continue;
|
|
13
|
+
const propsFromAnalyzer = options.program
|
|
14
|
+
? collectPropsForExport(options.program, { name: e.name, node: e.node })
|
|
15
|
+
: undefined;
|
|
16
|
+
const analyzerProps = propsFromAnalyzer?.members.map((member) => ({
|
|
17
|
+
name: member.name,
|
|
18
|
+
type: member.type,
|
|
19
|
+
required: member.required,
|
|
20
|
+
...(member.defaultValue !== undefined ? { defaultValue: member.defaultValue } : {}),
|
|
21
|
+
description: member.description ?? null,
|
|
22
|
+
})) ?? [];
|
|
12
23
|
const propsType = getComponentPropsType(e.node);
|
|
13
|
-
const
|
|
24
|
+
const legacyProps = propsType != null ? getPropsFromType(propsType) : [];
|
|
25
|
+
const props = analyzerProps.length > 0 ? analyzerProps : legacyProps;
|
|
14
26
|
components.push({
|
|
15
27
|
name: e.name,
|
|
16
28
|
description: e.description,
|
|
29
|
+
isReadme: e.isReadme,
|
|
30
|
+
examples: e.examples,
|
|
17
31
|
modulePath: e.modulePath,
|
|
18
32
|
sourceLocation: e.sourceLocation,
|
|
19
33
|
exportPaths: e.exportPaths,
|
package/dist/analyze/exports.js
CHANGED
|
@@ -19,13 +19,12 @@ export function analyzeExports(project, options) {
|
|
|
19
19
|
continue;
|
|
20
20
|
}
|
|
21
21
|
const rawComment = getParadoxComment(decl);
|
|
22
|
-
const parsed = rawComment
|
|
23
|
-
? parseParadoxComment(rawComment)
|
|
24
|
-
: { description: null, isConfig: false, params: {}, returns: null };
|
|
22
|
+
const parsed = rawComment ? parseParadoxComment(rawComment) : createEmptyMetadata();
|
|
25
23
|
const name = resolved.getName();
|
|
26
24
|
if (parsed.isConfig) {
|
|
27
25
|
config = {
|
|
28
26
|
exportName: name,
|
|
27
|
+
isReadme: parsed.isReadme,
|
|
29
28
|
};
|
|
30
29
|
}
|
|
31
30
|
const metadata = getExportMetadata({
|
|
@@ -40,6 +39,8 @@ export function analyzeExports(project, options) {
|
|
|
40
39
|
? {
|
|
41
40
|
...existing,
|
|
42
41
|
description: existing.description ?? parsed.description,
|
|
42
|
+
isReadme: existing.isReadme || parsed.isReadme,
|
|
43
|
+
examples: existing.examples.length > 0 ? existing.examples : parsed.examples,
|
|
43
44
|
exportPaths: uniqueSorted([...existing.exportPaths, ...metadata.exportPaths]),
|
|
44
45
|
relatedSymbols: uniqueSorted([
|
|
45
46
|
...existing.relatedSymbols,
|
|
@@ -52,6 +53,8 @@ export function analyzeExports(project, options) {
|
|
|
52
53
|
name,
|
|
53
54
|
node: decl,
|
|
54
55
|
description: parsed.description,
|
|
56
|
+
isReadme: parsed.isReadme,
|
|
57
|
+
examples: parsed.examples,
|
|
55
58
|
kind: inferKind(decl),
|
|
56
59
|
...metadata,
|
|
57
60
|
});
|
|
@@ -87,3 +90,13 @@ function uniqueSorted(values) {
|
|
|
87
90
|
function toPosixPath(path) {
|
|
88
91
|
return path.replaceAll('\\', '/');
|
|
89
92
|
}
|
|
93
|
+
function createEmptyMetadata() {
|
|
94
|
+
return {
|
|
95
|
+
description: null,
|
|
96
|
+
isConfig: false,
|
|
97
|
+
isReadme: false,
|
|
98
|
+
examples: [],
|
|
99
|
+
params: {},
|
|
100
|
+
returns: null,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AnalyzedProject } from './model.js';
|
|
2
|
+
interface AnalyzeProjectConfig {
|
|
3
|
+
root: string;
|
|
4
|
+
entrypoints?: string[];
|
|
5
|
+
}
|
|
6
|
+
/***
|
|
7
|
+
* Runs the semantic analyzer pipeline for a project.
|
|
8
|
+
*/
|
|
9
|
+
export declare function analyzeProject(config: AnalyzeProjectConfig): AnalyzedProject;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { associateDocBlocksWithSymbols } from './associateDocBlocksWithSymbols.js';
|
|
2
|
+
import { collectSourceFiles } from './collectSourceFiles.js';
|
|
3
|
+
import { createTypeScriptProgram } from './createTypeScriptProgram.js';
|
|
4
|
+
import { collectDocBlocks } from './docBlocks.js';
|
|
5
|
+
import { collectExports, collectPropsForExport, collectRelatedTypes, collectTypeMembers, getExportSignature, resolveTypeReference, } from './exports.js';
|
|
6
|
+
import { collectCallGraph, collectComponentCompositionGraph, collectImportGraph, } from './graphs.js';
|
|
7
|
+
import { defaultTagRegistry } from './tagRegistry.js';
|
|
8
|
+
/***
|
|
9
|
+
* Runs the semantic analyzer pipeline for a project.
|
|
10
|
+
*/
|
|
11
|
+
export function analyzeProject(config) {
|
|
12
|
+
const program = createTypeScriptProgram({
|
|
13
|
+
root: config.root,
|
|
14
|
+
entrypoints: config.entrypoints,
|
|
15
|
+
});
|
|
16
|
+
const files = collectSourceFiles(program);
|
|
17
|
+
const docBlocks = files.flatMap((file) => collectDocBlocks(file.sourceFile, { program, tagRegistry: defaultTagRegistry }));
|
|
18
|
+
const exports = associateDocBlocksWithSymbols(program, docBlocks, collectExports(program)).map((entry) => {
|
|
19
|
+
const signature = getExportSignature(program, entry);
|
|
20
|
+
const props = collectPropsForExport(program, entry);
|
|
21
|
+
const typeReference = resolveTypeReference(program, entry.node);
|
|
22
|
+
const typeMembers = typeReference && (entry.kind === 'interface' || entry.kind === 'type')
|
|
23
|
+
? collectTypeMembers(program, typeReference)
|
|
24
|
+
: undefined;
|
|
25
|
+
const relatedTypes = collectRelatedTypes({
|
|
26
|
+
name: entry.name,
|
|
27
|
+
signature,
|
|
28
|
+
props,
|
|
29
|
+
typeMembers,
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
...entry,
|
|
33
|
+
signature,
|
|
34
|
+
props,
|
|
35
|
+
typeMembers,
|
|
36
|
+
relatedTypes,
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
const tags = new Map();
|
|
40
|
+
for (const docBlock of docBlocks) {
|
|
41
|
+
for (const tag of docBlock.tags) {
|
|
42
|
+
tags.set(tag.name, tag.value ?? null);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const graphs = {
|
|
46
|
+
imports: collectImportGraph(program),
|
|
47
|
+
calls: collectCallGraph(program),
|
|
48
|
+
typeReferences: exports.flatMap((entry) => entry.relatedTypes.map((typeName) => ({
|
|
49
|
+
fromSymbol: entry.name,
|
|
50
|
+
toType: typeName,
|
|
51
|
+
sourcePath: entry.sourcePath,
|
|
52
|
+
}))),
|
|
53
|
+
componentComposition: collectComponentCompositionGraph(program, exports),
|
|
54
|
+
};
|
|
55
|
+
return {
|
|
56
|
+
files,
|
|
57
|
+
exports,
|
|
58
|
+
docBlocks,
|
|
59
|
+
tags: [...tags.entries()].map(([name, value]) => ({ name, value })),
|
|
60
|
+
graphs,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AnalyzedDocBlock, AnalyzedExport, AnalyzedProgram } from './model.js';
|
|
2
|
+
/***
|
|
3
|
+
* Attaches doc blocks and tags to their nearest symbol declarations.
|
|
4
|
+
*/
|
|
5
|
+
export declare function associateDocBlocksWithSymbols(program: AnalyzedProgram, docBlocks: readonly AnalyzedDocBlock[], exports: readonly AnalyzedExport[]): AnalyzedExport[];
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { relativeToRoot } from './utils.js';
|
|
2
|
+
/***
|
|
3
|
+
* Attaches doc blocks and tags to their nearest symbol declarations.
|
|
4
|
+
*/
|
|
5
|
+
export function associateDocBlocksWithSymbols(program, docBlocks, exports) {
|
|
6
|
+
const docBlocksByFile = new Map();
|
|
7
|
+
for (const docBlock of docBlocks) {
|
|
8
|
+
const entries = docBlocksByFile.get(docBlock.sourcePath);
|
|
9
|
+
if (entries) {
|
|
10
|
+
entries.push(docBlock);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
docBlocksByFile.set(docBlock.sourcePath, [docBlock]);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
for (const [, entries] of docBlocksByFile) {
|
|
17
|
+
entries.sort((left, right) => left.start - right.start);
|
|
18
|
+
}
|
|
19
|
+
return exports.map((entry) => {
|
|
20
|
+
const sourceFile = entry.node.getSourceFile();
|
|
21
|
+
const sourcePath = relativeToRoot(program.root, sourceFile.getFilePath());
|
|
22
|
+
const docBlocksForFile = docBlocksByFile.get(sourcePath) ?? [];
|
|
23
|
+
const nodeStart = entry.node.getStart(false);
|
|
24
|
+
let match = undefined;
|
|
25
|
+
for (const block of docBlocksForFile) {
|
|
26
|
+
if (block.end > nodeStart)
|
|
27
|
+
break;
|
|
28
|
+
if (block.end > 0) {
|
|
29
|
+
match = block;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (match) {
|
|
33
|
+
const text = sourceFile.getFullText();
|
|
34
|
+
const between = text.slice(match.end, nodeStart);
|
|
35
|
+
if (!/^[\s;]*(export\s+)?(default\s+)?$/.test(between)) {
|
|
36
|
+
match = undefined;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!match) {
|
|
40
|
+
return {
|
|
41
|
+
...entry,
|
|
42
|
+
tags: entry.tags,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
...entry,
|
|
47
|
+
docBlock: match,
|
|
48
|
+
tags: match.tags,
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { normalize } from 'node:path';
|
|
2
|
+
import { isNodeModulePath, isPathInsideRoot, relativeToRoot } from './utils.js';
|
|
3
|
+
/***
|
|
4
|
+
* Collects stable source file metadata for the analyzer.
|
|
5
|
+
*/
|
|
6
|
+
export function collectSourceFiles(program) {
|
|
7
|
+
const entrypoints = new Set(program.entrypointFilePaths.map((entrypoint) => normalize(entrypoint)));
|
|
8
|
+
return program.project
|
|
9
|
+
.getSourceFiles()
|
|
10
|
+
.filter((sourceFile) => {
|
|
11
|
+
const filePath = sourceFile.getFilePath();
|
|
12
|
+
return (!sourceFile.isDeclarationFile() &&
|
|
13
|
+
isPathInsideRoot(program.root, filePath) &&
|
|
14
|
+
!isNodeModulePath(filePath));
|
|
15
|
+
})
|
|
16
|
+
.map((sourceFile) => ({
|
|
17
|
+
path: relativeToRoot(program.root, sourceFile.getFilePath()),
|
|
18
|
+
sourceFile,
|
|
19
|
+
isEntrypoint: entrypoints.has(normalize(sourceFile.getFilePath())),
|
|
20
|
+
}))
|
|
21
|
+
.sort((left, right) => left.path.localeCompare(right.path));
|
|
22
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Project } from 'ts-morph';
|
|
2
|
+
import type { AnalyzedProgram } from './model.js';
|
|
3
|
+
interface CreateProgramOptions {
|
|
4
|
+
root: string;
|
|
5
|
+
entrypoints?: string[];
|
|
6
|
+
project?: Project;
|
|
7
|
+
tsconfigPath?: string;
|
|
8
|
+
}
|
|
9
|
+
/***
|
|
10
|
+
* Creates or reuses a ts-morph project for analysis.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createTypeScriptProgram(options: CreateProgramOptions): AnalyzedProgram;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { isAbsolute, join, normalize } from 'node:path';
|
|
2
|
+
import { Project } from 'ts-morph';
|
|
3
|
+
import { toPosixPath } from './utils.js';
|
|
4
|
+
/***
|
|
5
|
+
* Creates or reuses a ts-morph project for analysis.
|
|
6
|
+
*/
|
|
7
|
+
export function createTypeScriptProgram(options) {
|
|
8
|
+
const root = normalize(options.root);
|
|
9
|
+
const entrypoints = options.entrypoints ?? ['src/index.ts'];
|
|
10
|
+
const entrypointFilePaths = entrypoints.map((entrypoint) => normalize(isAbsolute(entrypoint) ? entrypoint : join(root, entrypoint)));
|
|
11
|
+
const project = options.project ??
|
|
12
|
+
new Project({
|
|
13
|
+
tsConfigFilePath: options.tsconfigPath ?? join(root, 'tsconfig.json'),
|
|
14
|
+
skipAddingFilesFromTsConfig: false,
|
|
15
|
+
});
|
|
16
|
+
return {
|
|
17
|
+
project,
|
|
18
|
+
typeChecker: project.getTypeChecker(),
|
|
19
|
+
root,
|
|
20
|
+
entrypoints: entrypoints.map((entrypoint) => toPosixPath(entrypoint)),
|
|
21
|
+
entrypointFilePaths,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SourceFile } from 'ts-morph';
|
|
2
|
+
import type { AnalyzedDocBlock, AnalyzedProgram } from './model.js';
|
|
3
|
+
import type { TagRegistry } from './tagRegistry.js';
|
|
4
|
+
/***
|
|
5
|
+
* Collects all Paradox doc blocks from a source file.
|
|
6
|
+
*/
|
|
7
|
+
export declare function collectDocBlocks(sourceFile: SourceFile, options: {
|
|
8
|
+
program: AnalyzedProgram;
|
|
9
|
+
tagRegistry?: TagRegistry;
|
|
10
|
+
}): AnalyzedDocBlock[];
|