@ankhorage/paradox 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +141 -0
- package/dist/analyze/analyze.js +10 -0
- package/dist/analyze/badges.d.ts +6 -0
- package/dist/analyze/badges.js +256 -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 +48 -0
- package/dist/analyze/usage.d.ts +6 -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 +46 -0
- package/dist/model/buildModel.js +55 -4
- package/dist/model/types.d.ts +48 -0
- package/dist/render/render.d.ts +3 -1
- package/dist/render/render.js +25 -87
- package/dist/render/renderers/badges.d.ts +6 -0
- package/dist/render/renderers/badges.js +35 -0
- 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 +155 -0
- package/dist/render/types.d.ts +20 -0
- package/dist/write/write.js +12 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.0.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 5246138: Generate deterministic README quality badges from local repository metadata.
|
|
8
|
+
|
|
9
|
+
Paradox now computes README badge metadata from local package, TypeScript, linting, formatting, workflow, test, coverage, and documentation signals, then renders stable local SVG badge assets under `paradox/badges/` and includes them in generated README output without relying on external badge services.
|
|
10
|
+
|
|
11
|
+
## 0.0.6
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- 1a836cf: Generate a deterministic static documentation app with computed API metadata and Mermaid diagrams.
|
|
16
|
+
|
|
17
|
+
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.
|
|
18
|
+
|
|
3
19
|
## 0.0.5
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# @ankhorage/paradox
|
|
2
2
|
|
|
3
|
+
        
|
|
4
|
+
|
|
3
5
|
Deterministic documentation generator for TypeScript packages.
|
|
4
6
|
|
|
5
7
|
## Usage
|
|
@@ -20,6 +22,134 @@ export default defineParadoxConfig({
|
|
|
20
22
|
});
|
|
21
23
|
```
|
|
22
24
|
|
|
25
|
+
## Generated documentation
|
|
26
|
+
|
|
27
|
+
- [Interactive documentation app](./paradox/index.html)
|
|
28
|
+
- [Public API reference](./paradox/exports.md)
|
|
29
|
+
- [Component registry](./paradox/components.md)
|
|
30
|
+
- [Architecture overview](./paradox/diagrams/architecture-overview.mmd)
|
|
31
|
+
- [Module relationships](./paradox/diagrams/module-relationships.mmd)
|
|
32
|
+
- [Export graph](./paradox/diagrams/export-graph.mmd)
|
|
33
|
+
- [Entrypoint sequence](./paradox/diagrams/entrypoint-sequence.mmd)
|
|
34
|
+
|
|
35
|
+
## Architecture preview
|
|
36
|
+
|
|
37
|
+
```mermaid
|
|
38
|
+
graph TD
|
|
39
|
+
package__ankhorage_paradox["@ankhorage/paradox"]
|
|
40
|
+
entrypoint_src_index_ts["src/index.ts"]
|
|
41
|
+
package__ankhorage_paradox --> entrypoint_src_index_ts
|
|
42
|
+
module_src_analyze_analyze_ts["src/analyze/analyze.ts"]
|
|
43
|
+
package__ankhorage_paradox -.-> module_src_analyze_analyze_ts
|
|
44
|
+
module_src_analyze_analyze_ts --> module_src_analyze_badges_ts
|
|
45
|
+
module_src_analyze_analyze_ts --> module_src_analyze_components_ts
|
|
46
|
+
module_src_analyze_analyze_ts --> module_src_analyze_exports_ts
|
|
47
|
+
module_src_analyze_analyze_ts --> module_src_analyze_modules_ts
|
|
48
|
+
module_src_analyze_analyze_ts --> module_src_analyze_project_ts
|
|
49
|
+
module_src_analyze_analyze_ts --> module_src_analyze_types_ts
|
|
50
|
+
module_src_analyze_analyze_ts --> module_src_analyze_usage_ts
|
|
51
|
+
module_src_analyze_analyze_ts --> module_src_config_types_ts
|
|
52
|
+
module_src_analyze_badges_ts["src/analyze/badges.ts"]
|
|
53
|
+
package__ankhorage_paradox -.-> module_src_analyze_badges_ts
|
|
54
|
+
module_src_analyze_badges_ts --> module_src_analyze_types_ts
|
|
55
|
+
module_src_analyze_badges_ts --> module_src_analyze_usage_ts
|
|
56
|
+
module_src_analyze_components_ts["src/analyze/components.ts"]
|
|
57
|
+
package__ankhorage_paradox -.-> module_src_analyze_components_ts
|
|
58
|
+
module_src_analyze_components_ts --> module_src_analyze_types_ts
|
|
59
|
+
module_src_analyze_components_ts --> module_src_analyze_utils_getComponentPropsType_ts
|
|
60
|
+
module_src_analyze_components_ts --> module_src_analyze_utils_getPropsFromType_ts
|
|
61
|
+
module_src_analyze_components_ts --> module_src_analyze_utils_isReactComponent_ts
|
|
62
|
+
module_src_analyze_exports_ts["src/analyze/exports.ts"]
|
|
63
|
+
package__ankhorage_paradox -.-> module_src_analyze_exports_ts
|
|
64
|
+
module_src_analyze_exports_ts --> module_src_analyze_types_ts
|
|
65
|
+
module_src_analyze_exports_ts --> module_src_analyze_utils_getExportMetadata_ts
|
|
66
|
+
module_src_analyze_exports_ts --> module_src_analyze_utils_getParadoxComment_ts
|
|
67
|
+
module_src_analyze_exports_ts --> module_src_analyze_utils_parseParadoxComment_ts
|
|
68
|
+
module_src_analyze_exports_ts --> module_src_analyze_utils_resolveExportSymbol_ts
|
|
69
|
+
module_src_analyze_modules_ts["src/analyze/modules.ts"]
|
|
70
|
+
package__ankhorage_paradox -.-> module_src_analyze_modules_ts
|
|
71
|
+
module_src_analyze_modules_ts --> module_src_analyze_types_ts
|
|
72
|
+
module_src_analyze_project_ts["src/analyze/project.ts"]
|
|
73
|
+
package__ankhorage_paradox -.-> module_src_analyze_project_ts
|
|
74
|
+
module_src_analyze_types_ts["src/analyze/types.ts"]
|
|
75
|
+
package__ankhorage_paradox -.-> module_src_analyze_types_ts
|
|
76
|
+
module_src_analyze_usage_ts["src/analyze/usage.ts"]
|
|
77
|
+
package__ankhorage_paradox -.-> module_src_analyze_usage_ts
|
|
78
|
+
module_src_analyze_usage_ts --> module_src_analyze_types_ts
|
|
79
|
+
module_src_analyze_utils_getComponentPropsType_ts["src/analyze/utils/getComponentPropsType.ts"]
|
|
80
|
+
package__ankhorage_paradox -.-> module_src_analyze_utils_getComponentPropsType_ts
|
|
81
|
+
module_src_analyze_utils_getExportMetadata_ts["src/analyze/utils/getExportMetadata.ts"]
|
|
82
|
+
package__ankhorage_paradox -.-> module_src_analyze_utils_getExportMetadata_ts
|
|
83
|
+
module_src_analyze_utils_getExportMetadata_ts --> module_src_analyze_types_ts
|
|
84
|
+
module_src_analyze_utils_getExportMetadata_ts --> module_src_analyze_utils_getParadoxComment_ts
|
|
85
|
+
module_src_analyze_utils_getExportMetadata_ts --> module_src_analyze_utils_parseParadoxComment_ts
|
|
86
|
+
module_src_analyze_utils_getParadoxComment_ts["src/analyze/utils/getParadoxComment.ts"]
|
|
87
|
+
package__ankhorage_paradox -.-> module_src_analyze_utils_getParadoxComment_ts
|
|
88
|
+
module_src_analyze_utils_getPropsFromType_ts["src/analyze/utils/getPropsFromType.ts"]
|
|
89
|
+
package__ankhorage_paradox -.-> module_src_analyze_utils_getPropsFromType_ts
|
|
90
|
+
module_src_analyze_utils_getPropsFromType_ts --> module_src_analyze_types_ts
|
|
91
|
+
module_src_analyze_utils_getPropsFromType_ts --> module_src_analyze_utils_getParadoxComment_ts
|
|
92
|
+
module_src_analyze_utils_getPropsFromType_ts --> module_src_analyze_utils_parseParadoxComment_ts
|
|
93
|
+
module_src_analyze_utils_isReactComponent_ts["src/analyze/utils/isReactComponent.ts"]
|
|
94
|
+
package__ankhorage_paradox -.-> module_src_analyze_utils_isReactComponent_ts
|
|
95
|
+
module_src_analyze_utils_parseParadoxComment_ts["src/analyze/utils/parseParadoxComment.ts"]
|
|
96
|
+
package__ankhorage_paradox -.-> module_src_analyze_utils_parseParadoxComment_ts
|
|
97
|
+
module_src_analyze_utils_resolveExportSymbol_ts["src/analyze/utils/resolveExportSymbol.ts"]
|
|
98
|
+
package__ankhorage_paradox -.-> module_src_analyze_utils_resolveExportSymbol_ts
|
|
99
|
+
module_src_cli_ts["src/cli.ts"]
|
|
100
|
+
package__ankhorage_paradox -.-> module_src_cli_ts
|
|
101
|
+
module_src_cli_ts --> module_src_analyze_analyze_ts
|
|
102
|
+
module_src_cli_ts --> module_src_model_buildModel_ts
|
|
103
|
+
module_src_cli_ts --> module_src_paths_policy_ts
|
|
104
|
+
module_src_cli_ts --> module_src_render_render_ts
|
|
105
|
+
module_src_cli_ts --> module_src_write_write_ts
|
|
106
|
+
module_src_config_defineParadoxConfig_ts["src/config/defineParadoxConfig.ts"]
|
|
107
|
+
package__ankhorage_paradox -.-> module_src_config_defineParadoxConfig_ts
|
|
108
|
+
module_src_config_defineParadoxConfig_ts --> module_src_config_types_ts
|
|
109
|
+
module_src_config_types_ts["src/config/types.ts"]
|
|
110
|
+
package__ankhorage_paradox -.-> module_src_config_types_ts
|
|
111
|
+
module_src_index_ts["src/index.ts"]
|
|
112
|
+
module_src_model_buildModel_ts["src/model/buildModel.ts"]
|
|
113
|
+
package__ankhorage_paradox -.-> module_src_model_buildModel_ts
|
|
114
|
+
module_src_model_buildModel_ts --> module_src_model_types_ts
|
|
115
|
+
module_src_model_types_ts["src/model/types.ts"]
|
|
116
|
+
package__ankhorage_paradox -.-> module_src_model_types_ts
|
|
117
|
+
module_src_paths_policy_ts["src/paths/policy.ts"]
|
|
118
|
+
package__ankhorage_paradox -.-> module_src_paths_policy_ts
|
|
119
|
+
module_src_paths_policy_ts --> module_src_config_types_ts
|
|
120
|
+
module_src_render_render_ts["src/render/render.ts"]
|
|
121
|
+
package__ankhorage_paradox -.-> module_src_render_render_ts
|
|
122
|
+
module_src_render_render_ts --> module_src_model_types_ts
|
|
123
|
+
module_src_render_render_ts --> module_src_render_renderers_badges_ts
|
|
124
|
+
module_src_render_render_ts --> module_src_render_renderers_diagrams_ts
|
|
125
|
+
module_src_render_render_ts --> module_src_render_renderers_html_ts
|
|
126
|
+
module_src_render_render_ts --> module_src_render_renderers_markdown_ts
|
|
127
|
+
module_src_render_render_ts --> module_src_render_types_ts
|
|
128
|
+
module_src_render_renderers_badges_ts["src/render/renderers/badges.ts"]
|
|
129
|
+
package__ankhorage_paradox -.-> module_src_render_renderers_badges_ts
|
|
130
|
+
module_src_render_renderers_badges_ts --> module_src_model_types_ts
|
|
131
|
+
module_src_render_renderers_badges_ts --> module_src_render_types_ts
|
|
132
|
+
module_src_render_renderers_diagrams_ts["src/render/renderers/diagrams.ts"]
|
|
133
|
+
package__ankhorage_paradox -.-> module_src_render_renderers_diagrams_ts
|
|
134
|
+
module_src_render_renderers_diagrams_ts --> module_src_model_types_ts
|
|
135
|
+
module_src_render_renderers_diagrams_ts --> module_src_render_types_ts
|
|
136
|
+
module_src_render_renderers_html_ts["src/render/renderers/html.ts"]
|
|
137
|
+
package__ankhorage_paradox -.-> module_src_render_renderers_html_ts
|
|
138
|
+
module_src_render_renderers_html_ts --> module_src_model_types_ts
|
|
139
|
+
module_src_render_renderers_html_ts --> module_src_render_types_ts
|
|
140
|
+
module_src_render_renderers_markdown_ts["src/render/renderers/markdown.ts"]
|
|
141
|
+
package__ankhorage_paradox -.-> module_src_render_renderers_markdown_ts
|
|
142
|
+
module_src_render_renderers_markdown_ts --> module_src_model_types_ts
|
|
143
|
+
module_src_render_renderers_markdown_ts --> module_src_render_types_ts
|
|
144
|
+
module_src_render_types_ts["src/render/types.ts"]
|
|
145
|
+
package__ankhorage_paradox -.-> module_src_render_types_ts
|
|
146
|
+
module_src_render_types_ts --> module_src_model_types_ts
|
|
147
|
+
module_src_write_write_ts["src/write/write.ts"]
|
|
148
|
+
package__ankhorage_paradox -.-> module_src_write_write_ts
|
|
149
|
+
module_src_write_write_ts --> module_src_config_types_ts
|
|
150
|
+
module_src_write_write_ts --> module_src_render_types_ts
|
|
151
|
+
```
|
|
152
|
+
|
|
23
153
|
## Path resolution
|
|
24
154
|
|
|
25
155
|
- Config discovery: searches upward from `process.cwd()` for `paradox.config.ts/js/mjs/cjs` (required; no fallback).
|
|
@@ -35,6 +165,17 @@ export default defineParadoxConfig({
|
|
|
35
165
|
|
|
36
166
|
Defines a Paradox configuration object without changing its shape.
|
|
37
167
|
|
|
168
|
+
- Kind: `function`
|
|
169
|
+
- Module: `src/config/defineParadoxConfig.ts`
|
|
170
|
+
- Source: `src/config/defineParadoxConfig.ts:6:1`
|
|
171
|
+
- Export paths: `src/index.ts`
|
|
172
|
+
- Related symbols: `ParadoxConfig`
|
|
173
|
+
|
|
38
174
|
### ParadoxConfig
|
|
39
175
|
|
|
40
176
|
Configuration for running Paradox.
|
|
177
|
+
|
|
178
|
+
- Kind: `type`
|
|
179
|
+
- Module: `src/config/types.ts`
|
|
180
|
+
- Source: `src/config/types.ts:6:1`
|
|
181
|
+
- Export paths: `src/index.ts`
|
package/dist/analyze/analyze.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { analyzeBadges } from './badges.js';
|
|
3
4
|
import { analyzeComponents } from './components.js';
|
|
4
5
|
import { analyzeExports } from './exports.js';
|
|
6
|
+
import { analyzeModules } from './modules.js';
|
|
5
7
|
import { createProject } from './project.js';
|
|
6
8
|
import { createUsageFromPackageJson } from './usage.js';
|
|
7
9
|
/***
|
|
@@ -11,6 +13,7 @@ export async function analyze(config, runtime) {
|
|
|
11
13
|
const root = runtime.packageRoot;
|
|
12
14
|
const pkg = await readPackageJson(root);
|
|
13
15
|
const usage = createUsageFromPackageJson(pkg);
|
|
16
|
+
const badges = await analyzeBadges(root, pkg);
|
|
14
17
|
const project = createProject(root);
|
|
15
18
|
const entrypoints = config.package?.entrypoints ?? ['src/index.ts'];
|
|
16
19
|
const { config: configMetadata, exports } = analyzeExports(project, {
|
|
@@ -18,12 +21,19 @@ export async function analyze(config, runtime) {
|
|
|
18
21
|
entrypoints,
|
|
19
22
|
});
|
|
20
23
|
const components = analyzeComponents(exports);
|
|
24
|
+
const modules = analyzeModules(project, {
|
|
25
|
+
root,
|
|
26
|
+
entrypoints,
|
|
27
|
+
});
|
|
21
28
|
return {
|
|
22
29
|
packageName: config.docs?.title ?? pkg.name,
|
|
23
30
|
packageId: pkg.name,
|
|
24
31
|
description: config.docs?.description ?? pkg.description ?? null,
|
|
25
32
|
exports,
|
|
26
33
|
components,
|
|
34
|
+
entrypoints: entrypoints.map((entrypoint) => entrypoint.replaceAll('\\', '/')).sort(),
|
|
35
|
+
modules,
|
|
36
|
+
badges,
|
|
27
37
|
usage,
|
|
28
38
|
config: configMetadata,
|
|
29
39
|
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AnalysisBadge } from './types.js';
|
|
2
|
+
import type { PackageJsonModel } from './usage.js';
|
|
3
|
+
/***
|
|
4
|
+
* Derives deterministic repository metadata badges from local repository files.
|
|
5
|
+
*/
|
|
6
|
+
export declare function analyzeBadges(root: string, pkg: PackageJsonModel): Promise<AnalysisBadge[]>;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { access, readdir, readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const ESLINT_CONFIG_FILES = [
|
|
4
|
+
'eslint.config.js',
|
|
5
|
+
'eslint.config.mjs',
|
|
6
|
+
'eslint.config.cjs',
|
|
7
|
+
'eslint.config.ts',
|
|
8
|
+
'.eslintrc',
|
|
9
|
+
'.eslintrc.js',
|
|
10
|
+
'.eslintrc.cjs',
|
|
11
|
+
'.eslintrc.json',
|
|
12
|
+
'.eslintrc.yaml',
|
|
13
|
+
'.eslintrc.yml',
|
|
14
|
+
];
|
|
15
|
+
const PRETTIER_CONFIG_FILES = [
|
|
16
|
+
'.prettierrc',
|
|
17
|
+
'.prettierrc.js',
|
|
18
|
+
'.prettierrc.cjs',
|
|
19
|
+
'.prettierrc.json',
|
|
20
|
+
'.prettierrc.yaml',
|
|
21
|
+
'.prettierrc.yml',
|
|
22
|
+
'prettier.config.js',
|
|
23
|
+
'prettier.config.cjs',
|
|
24
|
+
'prettier.config.mjs',
|
|
25
|
+
'prettier.config.ts',
|
|
26
|
+
];
|
|
27
|
+
const BADGE_ORDER = [
|
|
28
|
+
'license',
|
|
29
|
+
'npm',
|
|
30
|
+
'runtime',
|
|
31
|
+
'typescript',
|
|
32
|
+
'eslint',
|
|
33
|
+
'prettier',
|
|
34
|
+
'build',
|
|
35
|
+
'tests',
|
|
36
|
+
'coverage',
|
|
37
|
+
'docs',
|
|
38
|
+
];
|
|
39
|
+
/***
|
|
40
|
+
* Derives deterministic repository metadata badges from local repository files.
|
|
41
|
+
*/
|
|
42
|
+
export async function analyzeBadges(root, pkg) {
|
|
43
|
+
const workflowFiles = await readWorkflowFiles(root);
|
|
44
|
+
const badges = [];
|
|
45
|
+
if ((await hasAnyFile(root, ESLINT_CONFIG_FILES)) ||
|
|
46
|
+
pkg.eslintConfig !== undefined ||
|
|
47
|
+
scriptContains(pkg, 'lint', 'eslint')) {
|
|
48
|
+
badges.push({
|
|
49
|
+
id: 'eslint',
|
|
50
|
+
label: 'eslint',
|
|
51
|
+
value: workflowRunsScript(workflowFiles, 'lint') ? 'checked' : 'configured',
|
|
52
|
+
color: workflowRunsScript(workflowFiles, 'lint') ? '0a7f3f' : '2563eb',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if ((await hasAnyFile(root, PRETTIER_CONFIG_FILES)) ||
|
|
56
|
+
pkg.prettier !== undefined ||
|
|
57
|
+
scriptContains(pkg, 'format', 'prettier') ||
|
|
58
|
+
scriptContains(pkg, 'format:check', 'prettier')) {
|
|
59
|
+
const prettierChecked = workflowRunsScript(workflowFiles, 'format') ||
|
|
60
|
+
workflowRunsScript(workflowFiles, 'format:check');
|
|
61
|
+
badges.push({
|
|
62
|
+
id: 'prettier',
|
|
63
|
+
label: 'prettier',
|
|
64
|
+
value: prettierChecked ? 'checked' : 'configured',
|
|
65
|
+
color: prettierChecked ? '0a7f3f' : '2563eb',
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (hasScript(pkg, 'build')) {
|
|
69
|
+
const buildChecked = workflowRunsScript(workflowFiles, 'build');
|
|
70
|
+
badges.push({
|
|
71
|
+
id: 'build',
|
|
72
|
+
label: 'build',
|
|
73
|
+
value: buildChecked ? 'checked' : 'configured',
|
|
74
|
+
color: buildChecked ? '0a7f3f' : '6f42c1',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (hasScript(pkg, 'test')) {
|
|
78
|
+
const testsChecked = workflowRunsScript(workflowFiles, 'test');
|
|
79
|
+
badges.push({
|
|
80
|
+
id: 'tests',
|
|
81
|
+
label: 'tests',
|
|
82
|
+
value: testsChecked ? 'checked' : 'configured',
|
|
83
|
+
color: testsChecked ? '0a7f3f' : '6f42c1',
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (await hasTypeScriptStrictMode(root)) {
|
|
87
|
+
badges.push({
|
|
88
|
+
id: 'typescript',
|
|
89
|
+
label: 'typescript',
|
|
90
|
+
value: 'strict',
|
|
91
|
+
color: '2563eb',
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (supportsBun(pkg, workflowFiles)) {
|
|
95
|
+
badges.push({
|
|
96
|
+
id: 'runtime',
|
|
97
|
+
label: 'runtime',
|
|
98
|
+
value: 'bun',
|
|
99
|
+
color: 'f59e0b',
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
const coverage = await readCoveragePercent(root);
|
|
103
|
+
if (coverage !== null) {
|
|
104
|
+
badges.push({
|
|
105
|
+
id: 'coverage',
|
|
106
|
+
label: 'coverage',
|
|
107
|
+
value: `${formatPercent(coverage)}%`,
|
|
108
|
+
color: getCoverageColor(coverage),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (typeof pkg.license === 'string' && pkg.license.length > 0) {
|
|
112
|
+
badges.push({
|
|
113
|
+
id: 'license',
|
|
114
|
+
label: 'license',
|
|
115
|
+
value: pkg.license,
|
|
116
|
+
color: '2563eb',
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
if (typeof pkg.version === 'string' && pkg.version.length > 0) {
|
|
120
|
+
badges.push({
|
|
121
|
+
id: 'npm',
|
|
122
|
+
label: 'npm',
|
|
123
|
+
value: `v${pkg.version}`,
|
|
124
|
+
color: 'cb3837',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
badges.push({
|
|
128
|
+
id: 'docs',
|
|
129
|
+
label: 'docs',
|
|
130
|
+
value: 'paradox',
|
|
131
|
+
color: '0f766e',
|
|
132
|
+
});
|
|
133
|
+
return sortBadges(badges);
|
|
134
|
+
}
|
|
135
|
+
async function hasAnyFile(root, paths) {
|
|
136
|
+
for (const relativePath of paths) {
|
|
137
|
+
if (await fileExists(join(root, relativePath))) {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
async function hasTypeScriptStrictMode(root) {
|
|
144
|
+
const tsconfig = await readJsonFile(join(root, 'tsconfig.json'));
|
|
145
|
+
return tsconfig?.compilerOptions?.strict === true;
|
|
146
|
+
}
|
|
147
|
+
async function readCoveragePercent(root) {
|
|
148
|
+
const coverage = await readJsonFile(join(root, 'coverage', 'coverage-summary.json'));
|
|
149
|
+
const value = coverage?.total?.lines?.pct;
|
|
150
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
151
|
+
}
|
|
152
|
+
function formatPercent(value) {
|
|
153
|
+
return Number.isInteger(value) ? `${value}` : value.toFixed(1);
|
|
154
|
+
}
|
|
155
|
+
function getCoverageColor(value) {
|
|
156
|
+
if (value >= 90)
|
|
157
|
+
return '0a7f3f';
|
|
158
|
+
if (value >= 75)
|
|
159
|
+
return '65a30d';
|
|
160
|
+
if (value >= 50)
|
|
161
|
+
return 'ca8a04';
|
|
162
|
+
return 'dc2626';
|
|
163
|
+
}
|
|
164
|
+
function hasScript(pkg, name) {
|
|
165
|
+
return typeof pkg.scripts?.[name] === 'string' && pkg.scripts[name].length > 0;
|
|
166
|
+
}
|
|
167
|
+
function scriptContains(pkg, name, command) {
|
|
168
|
+
return pkg.scripts?.[name]?.includes(command) ?? false;
|
|
169
|
+
}
|
|
170
|
+
function supportsBun(pkg, workflowFiles) {
|
|
171
|
+
if (pkg.packageManager?.startsWith('bun@') ?? false) {
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
if (Object.values(pkg.scripts ?? {}).some((script) => script.includes('bun') || script.includes('bunx'))) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
return workflowFiles.some((workflow) => workflow.includes('oven-sh/setup-bun') ||
|
|
178
|
+
workflow.includes('bun install') ||
|
|
179
|
+
/\bbun(?:x)?\b/.test(workflow));
|
|
180
|
+
}
|
|
181
|
+
function workflowRunsScript(workflowFiles, scriptName) {
|
|
182
|
+
const escaped = escapeRegExp(scriptName);
|
|
183
|
+
const patterns = [
|
|
184
|
+
new RegExp(`\\bbun\\s+run\\s+${escaped}(?=\\s|$)`, 'i'),
|
|
185
|
+
new RegExp(`\\bnpm\\s+run\\s+${escaped}(?=\\s|$)`, 'i'),
|
|
186
|
+
new RegExp(`\\bpnpm\\s+${escaped}(?=\\s|$)`, 'i'),
|
|
187
|
+
new RegExp(`\\byarn\\s+${escaped}(?=\\s|$)`, 'i'),
|
|
188
|
+
];
|
|
189
|
+
return workflowFiles.some((workflow) => patterns.some((pattern) => pattern.test(workflow)));
|
|
190
|
+
}
|
|
191
|
+
function escapeRegExp(value) {
|
|
192
|
+
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
193
|
+
}
|
|
194
|
+
async function readWorkflowFiles(root) {
|
|
195
|
+
const workflowsPath = join(root, '.github', 'workflows');
|
|
196
|
+
const entries = await readDirectory(workflowsPath);
|
|
197
|
+
const contents = await Promise.all(entries
|
|
198
|
+
.filter((entry) => entry.endsWith('.yml') || entry.endsWith('.yaml'))
|
|
199
|
+
.sort((a, b) => a.localeCompare(b))
|
|
200
|
+
.map((entry) => readFile(join(workflowsPath, entry), 'utf-8')));
|
|
201
|
+
return contents;
|
|
202
|
+
}
|
|
203
|
+
async function readDirectory(path) {
|
|
204
|
+
try {
|
|
205
|
+
return await readdir(path);
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
if (isNotFoundError(error)) {
|
|
209
|
+
return [];
|
|
210
|
+
}
|
|
211
|
+
throw new Error(`Unable to read workflow directory: ${path}`, { cause: error });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function isNotFoundError(error) {
|
|
215
|
+
return typeof error === 'object' && error !== null && 'code' in error && error.code === 'ENOENT';
|
|
216
|
+
}
|
|
217
|
+
async function readJsonFile(path) {
|
|
218
|
+
let content;
|
|
219
|
+
try {
|
|
220
|
+
content = await readFile(path, 'utf-8');
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
if (isNotFoundError(error)) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
throw new Error(`Unable to read JSON metadata file: ${path}`, { cause: error });
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
return JSON.parse(content);
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
throw new Error(`Unable to parse JSON metadata file: ${path}`, { cause: error });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
async function fileExists(path) {
|
|
236
|
+
try {
|
|
237
|
+
await access(path);
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
if (isNotFoundError(error)) {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
throw error;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function sortBadges(badges) {
|
|
248
|
+
return [...badges].sort((left, right) => {
|
|
249
|
+
const order = getBadgeOrder(left.id) - getBadgeOrder(right.id);
|
|
250
|
+
return order !== 0 ? order : left.id.localeCompare(right.id);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
function getBadgeOrder(id) {
|
|
254
|
+
const index = BADGE_ORDER.indexOf(id);
|
|
255
|
+
return index === -1 ? BADGE_ORDER.length : index;
|
|
256
|
+
}
|
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
|
+
}
|