@ankhorage/paradox 0.1.26 → 0.2.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 +14 -0
- package/README.md +53 -61
- package/dist/analyze/analyze.d.ts +2 -2
- package/dist/analyze/analyze.js +51 -35
- package/dist/analyze/badges.d.ts +2 -1
- package/dist/analyze/badges.js +14 -4
- package/dist/analyze/components.js +12 -11
- package/dist/analyze/documentation/collectDocumentationCommentsAsync.d.ts +11 -0
- package/dist/analyze/documentation/collectDocumentationCommentsAsync.js +72 -0
- package/dist/analyze/documentation/findings.d.ts +5 -0
- package/dist/analyze/documentation/findings.js +17 -0
- package/dist/analyze/documentation/validateDocumentationPolicyAsync.d.ts +13 -0
- package/dist/analyze/documentation/validateDocumentationPolicyAsync.js +150 -0
- package/dist/analyze/documentation/validateReferencesAsync.d.ts +11 -0
- package/dist/analyze/documentation/validateReferencesAsync.js +148 -0
- package/dist/analyze/exports.d.ts +4 -0
- package/dist/analyze/exports.js +14 -7
- package/dist/analyze/readmeConfig.d.ts +1 -3
- package/dist/analyze/readmeConfig.js +8 -19
- package/dist/analyze/readmeUsage.d.ts +7 -10
- package/dist/analyze/readmeUsage.js +124 -48
- package/dist/analyze/semantic/docBlocks.js +18 -48
- package/dist/analyze/semantic/exports.js +1 -3
- package/dist/analyze/semantic/model.d.ts +0 -2
- package/dist/analyze/semantic/paradoxComment.d.ts +1 -11
- package/dist/analyze/semantic/paradoxComment.js +1 -43
- package/dist/analyze/semantic/tagRegistry.js +2 -1
- package/dist/analyze/sourceFunctions.js +4 -4
- package/dist/analyze/types.d.ts +31 -24
- package/dist/analyze/usage.d.ts +2 -2
- package/dist/analyze/usage.js +3 -33
- package/dist/analyze/utils/getExportMetadata.js +11 -40
- package/dist/analyze/utils/parseParadoxComment.d.ts +12 -9
- package/dist/analyze/utils/parseParadoxComment.js +66 -78
- package/dist/cli/index.d.ts +3 -2
- package/dist/cli/index.js +3 -2
- package/dist/cli/standalone.js +11 -0
- package/dist/config/defineParadoxConfig.d.ts +1 -1
- package/dist/doc-tags/registry.d.ts +28 -32
- package/dist/doc-tags/registry.js +35 -39
- package/dist/index.d.ts +1 -1
- package/dist/model/buildModel.d.ts +26 -19
- package/dist/model/buildModel.js +33 -99
- package/dist/model/types.d.ts +27 -20
- package/dist/paths/policy.d.ts +1 -1
- package/dist/render/renderers/diagrams.js +1 -7
- package/dist/render/renderers/html.js +93 -70
- package/dist/render/renderers/markdown.js +139 -86
- package/dist/render/toFileStem.d.ts +2 -0
- package/dist/render/toFileStem.js +8 -0
- package/dist/{config/types.d.ts → types/config.d.ts} +3 -5
- package/dist/write/write.d.ts +1 -1
- package/package.json +9 -5
- package/dist/analyze/readmeCli.d.ts +0 -9
- package/dist/analyze/readmeCli.js +0 -33
- package/dist/analyze/utils/getLeadingParadoxComment.d.ts +0 -10
- package/dist/analyze/utils/getLeadingParadoxComment.js +0 -16
- /package/dist/{config/types.js → types/config.js} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 9fe93b9: Adopt the canonical Ankhorage documentation policy: fixed usage and config locations, source-backed
|
|
8
|
+
examples, explicit titles, validated external and security references, policy-derived status
|
|
9
|
+
badges, and hard failures for invalid documentation contracts.
|
|
10
|
+
|
|
11
|
+
## 0.1.27
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- ef42af1: Reuse the canonical Utility ASCII slugifier for generated HTML anchor ids.
|
|
16
|
+
|
|
3
17
|
## 0.1.26
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -3,66 +3,44 @@
|
|
|
3
3
|
|
|
4
4
|
# @ankhorage/paradox
|
|
5
5
|
|
|
6
|
-
         
|
|
7
7
|
|
|
8
8
|
Deterministic documentation generator for TypeScript packages.
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## Usage
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
### CLI
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
bunx @ankhorage/paradox
|
|
16
|
-
```
|
|
14
|
+
Ankhorage packages expose their command-line interface through `ankh`. Use `ankh --help` to discover available package commands, or run a package command with `--help` for package-specific usage.
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
analyzes the package, builds the documentation model, renders all documentation artifacts,
|
|
25
|
-
and writes them to the configured output directory.
|
|
26
|
-
|
|
27
|
-
Diagram: [paradox sequence](./paradox/diagrams/sequences/paradox.mmd)
|
|
28
|
-
|
|
29
|
-
```mermaid
|
|
30
|
-
sequenceDiagram
|
|
31
|
-
participant participant_analyze as analyze
|
|
32
|
-
participant participant_buildModel as buildModel
|
|
33
|
-
participant participant_dirname as dirname
|
|
34
|
-
participant participant_findParadoxConfigFile as findParadoxConfigFile
|
|
35
|
-
participant participant_loadParadoxConfig as loadParadoxConfig
|
|
36
|
-
participant participant_main as main
|
|
37
|
-
participant participant_render as render
|
|
38
|
-
participant participant_resolveOutputRoot as resolveOutputRoot
|
|
39
|
-
participant participant_resolvePackageRoot as resolvePackageRoot
|
|
40
|
-
participant participant_write as write
|
|
41
|
-
participant_main->>participant_findParadoxConfigFile: findParadoxConfigFile()
|
|
42
|
-
participant_findParadoxConfigFile-->>participant_main: return
|
|
43
|
-
participant_main->>participant_dirname: dirname()
|
|
44
|
-
participant_dirname-->>participant_main: return
|
|
45
|
-
participant_main->>participant_loadParadoxConfig: loadParadoxConfig()
|
|
46
|
-
participant_loadParadoxConfig-->>participant_main: return
|
|
47
|
-
participant_main->>participant_resolvePackageRoot: resolvePackageRoot()
|
|
48
|
-
participant_resolvePackageRoot-->>participant_main: return
|
|
49
|
-
participant_main->>participant_resolveOutputRoot: resolveOutputRoot()
|
|
50
|
-
participant_resolveOutputRoot-->>participant_main: return
|
|
51
|
-
participant_main->>participant_analyze: analyze()
|
|
52
|
-
participant_analyze-->>participant_main: return
|
|
53
|
-
participant_main->>participant_buildModel: buildModel()
|
|
54
|
-
participant_buildModel-->>participant_main: return
|
|
55
|
-
participant_main->>participant_render: render()
|
|
56
|
-
participant_render-->>participant_main: return
|
|
57
|
-
participant_main->>participant_write: write()
|
|
58
|
-
participant_write-->>participant_main: return
|
|
16
|
+
```zsh
|
|
17
|
+
# Install the Ankhorage CLI
|
|
18
|
+
bun add --global @ankhorage/ankh
|
|
19
|
+
|
|
20
|
+
# Show usage information for paradox
|
|
21
|
+
ankh paradox --help
|
|
59
22
|
```
|
|
60
23
|
|
|
61
|
-
|
|
24
|
+
### Basic Usage
|
|
25
|
+
|
|
26
|
+
Paradox generates documentation from the canonical package structure. Usage documentation lives
|
|
27
|
+
only below `examples/**` or `src/cli/**`. A repository may contain multiple `@usage`
|
|
28
|
+
examples, but exactly one example below `examples/**` is promoted into README with `@readme`.
|
|
29
|
+
|
|
30
|
+
README-promoted usage provides an explicit `@title` and non-empty prose. Code always comes from
|
|
31
|
+
real source declarations rather than duplicated code blocks inside Paradox comments.
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
export const basicConfig = defineParadoxConfig({
|
|
35
|
+
mode: 'safe',
|
|
36
|
+
});
|
|
37
|
+
```
|
|
62
38
|
|
|
63
39
|
## Configuration
|
|
64
40
|
|
|
65
|
-
|
|
41
|
+
Configures Paradox documentation generation for a package.
|
|
42
|
+
|
|
43
|
+
### Example
|
|
66
44
|
|
|
67
45
|
```ts
|
|
68
46
|
import { defineParadoxConfig } from './src/config/defineParadoxConfig.js';
|
|
@@ -89,14 +67,14 @@ export default defineParadoxConfig({
|
|
|
89
67
|
<details>
|
|
90
68
|
<summary>Configuration options</summary>
|
|
91
69
|
|
|
92
|
-
| Field | Type
|
|
93
|
-
| ------------- |
|
|
94
|
-
| mode | `'safe' \| 'write' \| undefined`
|
|
95
|
-
| collaborators | `true \| undefined`
|
|
96
|
-
| donation | `{ account: string; } \| undefined`
|
|
97
|
-
| docs | `{ title?: string; description?: string;
|
|
98
|
-
| package | `{ root?: string; entrypoints?: string[]; } \| undefined`
|
|
99
|
-
| output | `{ dir?: string; } \| undefined`
|
|
70
|
+
| Field | Type | Required | Default | Description |
|
|
71
|
+
| ------------- | --------------------------------------------------------- | -------- | ------- | ----------- |
|
|
72
|
+
| mode | `'safe' \| 'write' \| undefined` | no | — | |
|
|
73
|
+
| collaborators | `true \| undefined` | no | — | |
|
|
74
|
+
| donation | `{ account: string; } \| undefined` | no | — | |
|
|
75
|
+
| docs | `{ title?: string; description?: string; } \| undefined` | no | — | |
|
|
76
|
+
| package | `{ root?: string; entrypoints?: string[]; } \| undefined` | no | — | |
|
|
77
|
+
| output | `{ dir?: string; } \| undefined` | no | — | |
|
|
100
78
|
|
|
101
79
|
</details>
|
|
102
80
|
|
|
@@ -130,13 +108,27 @@ Related symbols: `ParadoxConfig`
|
|
|
130
108
|
|
|
131
109
|
</details>
|
|
132
110
|
|
|
111
|
+
### Documentation
|
|
112
|
+
|
|
113
|
+
<details>
|
|
114
|
+
<summary>PARADOX_DOC_TAGS</summary>
|
|
115
|
+
|
|
116
|
+
Supported Paradox documentation tags projected from the canonical Ankhorage documentation policy.
|
|
117
|
+
|
|
118
|
+
Module: `src/doc-tags/registry.ts`
|
|
119
|
+
Source: `src/doc-tags/registry.ts:32:14`
|
|
120
|
+
|
|
121
|
+
</details>
|
|
122
|
+
|
|
123
|
+
### Types
|
|
124
|
+
|
|
133
125
|
<details>
|
|
134
|
-
<summary>
|
|
126
|
+
<summary>Configuration</summary>
|
|
135
127
|
|
|
136
|
-
|
|
128
|
+
Configures Paradox documentation generation for a package.
|
|
137
129
|
|
|
138
|
-
Module: `src/config
|
|
139
|
-
Source: `src/config
|
|
130
|
+
Module: `src/types/config.ts`
|
|
131
|
+
Source: `src/types/config.ts:9:1`
|
|
140
132
|
|
|
141
133
|
</details>
|
|
142
134
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { ParadoxConfig } from '../config
|
|
1
|
+
import type { ParadoxConfig } from '../types/config.js';
|
|
2
2
|
import type { AnalysisResult } from './types.js';
|
|
3
3
|
/***
|
|
4
|
-
* Analyzes a package and returns
|
|
4
|
+
* Analyzes a package and returns documentation plus canonical policy findings.
|
|
5
5
|
*/
|
|
6
6
|
export declare function analyze(config: ParadoxConfig, runtime: {
|
|
7
7
|
packageRoot: string;
|
package/dist/analyze/analyze.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { resolvePolicyStatus } from '@ankhorage/policy/status';
|
|
3
4
|
import { validateCollaborators } from '../config/utils/validateCollaborators.js';
|
|
4
5
|
import { validateDonationAccount } from '../config/utils/validateDonationAccount.js';
|
|
5
6
|
import { analyzeBadges } from './badges.js';
|
|
6
7
|
import { analyzeComponents } from './components.js';
|
|
8
|
+
import { collectDocumentationCommentsAsync } from './documentation/collectDocumentationCommentsAsync.js';
|
|
9
|
+
import { validateDocumentationPolicyAsync } from './documentation/validateDocumentationPolicyAsync.js';
|
|
7
10
|
import { analyzeExports } from './exports.js';
|
|
8
11
|
import { analyzeModules } from './modules.js';
|
|
9
12
|
import { createProject } from './project.js';
|
|
10
|
-
import { analyzeReadmeCli } from './readmeCli.js';
|
|
11
13
|
import { analyzeReadmeConfig } from './readmeConfig.js';
|
|
12
|
-
import { analyzeReadmeUsage } from './readmeUsage.js';
|
|
14
|
+
import { analyzeReadmeUsage, countExampleDirectoriesAsync } from './readmeUsage.js';
|
|
13
15
|
import { createTypeScriptProgram } from './semantic/createTypeScriptProgram.js';
|
|
14
16
|
import { collectTypeMembers, resolveTypeReference } from './semantic/exports.js';
|
|
15
17
|
import { collectCallGraph, collectComponentCompositionGraph, collectImportGraph, } from './semantic/graphs.js';
|
|
@@ -17,7 +19,7 @@ import { analyzeSequenceScenarios } from './sequenceScenarios.js';
|
|
|
17
19
|
import { analyzeSourceFunctions } from './sourceFunctions.js';
|
|
18
20
|
import { createUsageFromPackageJson } from './usage.js';
|
|
19
21
|
/***
|
|
20
|
-
* Analyzes a package and returns
|
|
22
|
+
* Analyzes a package and returns documentation plus canonical policy findings.
|
|
21
23
|
*/
|
|
22
24
|
export async function analyze(config, runtime) {
|
|
23
25
|
const root = runtime.packageRoot;
|
|
@@ -27,38 +29,22 @@ export async function analyze(config, runtime) {
|
|
|
27
29
|
? null
|
|
28
30
|
: { account: validateDonationAccount(config.donation.account) };
|
|
29
31
|
const usage = createUsageFromPackageJson(pkg);
|
|
30
|
-
const badges = await analyzeBadges(root, pkg);
|
|
31
32
|
const project = createProject(root);
|
|
32
33
|
const entrypoints = config.package?.entrypoints ?? ['src/index.ts'];
|
|
33
|
-
const readmeUsageDescription = config.docs?.usage?.description ?? null;
|
|
34
|
-
const usageEntryPoints = config.docs?.usage?.entrypoints ?? [];
|
|
35
|
-
const readmeUsage = await analyzeReadmeUsage({ root, entrypoints: usageEntryPoints });
|
|
36
|
-
const readmeCli = await analyzeReadmeCli(root);
|
|
37
|
-
const readmeConfig = await analyzeReadmeConfig({
|
|
38
|
-
root,
|
|
39
|
-
configFilePath: runtime.configFilePath ?? null,
|
|
40
|
-
});
|
|
41
34
|
const program = createTypeScriptProgram({ root, entrypoints, project });
|
|
42
35
|
const { config: configMetadata, exports } = analyzeExports(project, { root, entrypoints });
|
|
43
36
|
const components = analyzeComponents(exports, { program });
|
|
44
|
-
const modules = analyzeModules(project, {
|
|
45
|
-
root,
|
|
46
|
-
entrypoints,
|
|
47
|
-
excludePaths: usageEntryPoints,
|
|
48
|
-
});
|
|
37
|
+
const modules = analyzeModules(project, { root, entrypoints });
|
|
49
38
|
const sourceFunctions = analyzeSourceFunctions(project, root);
|
|
50
39
|
const sequenceScenarios = analyzeSequenceScenarios({ project, root, pkg, exports });
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
symbol: configExport.node.getSymbol() ?? null,
|
|
60
|
-
})
|
|
61
|
-
: [];
|
|
40
|
+
const usageEntries = await analyzeReadmeUsage({ root });
|
|
41
|
+
const exampleCount = await countExampleDirectoriesAsync(root);
|
|
42
|
+
const comments = await collectDocumentationCommentsAsync(root);
|
|
43
|
+
const readmeConfig = await analyzeReadmeConfig({
|
|
44
|
+
root,
|
|
45
|
+
configFilePath: runtime.configFilePath ?? null,
|
|
46
|
+
});
|
|
47
|
+
const configMembers = collectConfigMembers(program, exports, configMetadata);
|
|
62
48
|
const graphs = {
|
|
63
49
|
imports: collectImportGraph(program),
|
|
64
50
|
calls: collectCallGraph(program),
|
|
@@ -69,6 +55,14 @@ export async function analyze(config, runtime) {
|
|
|
69
55
|
}))),
|
|
70
56
|
componentComposition: collectComponentCompositionGraph(program),
|
|
71
57
|
};
|
|
58
|
+
const findings = await validateDocumentationPolicyAsync({
|
|
59
|
+
root,
|
|
60
|
+
project,
|
|
61
|
+
comments,
|
|
62
|
+
exports,
|
|
63
|
+
});
|
|
64
|
+
const documentationStatus = resolvePolicyStatus(findings);
|
|
65
|
+
const badges = await analyzeBadges(root, pkg, documentationStatus.status);
|
|
72
66
|
return {
|
|
73
67
|
packageName: config.docs?.title ?? pkg.name,
|
|
74
68
|
packageId: pkg.name,
|
|
@@ -83,20 +77,42 @@ export async function analyze(config, runtime) {
|
|
|
83
77
|
badges,
|
|
84
78
|
sequenceScenarios,
|
|
85
79
|
usage,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
80
|
+
usageEntries,
|
|
81
|
+
exampleCount,
|
|
82
|
+
findings,
|
|
89
83
|
readmeConfig,
|
|
90
|
-
config: configMetadata
|
|
91
|
-
?
|
|
84
|
+
config: configMetadata === null
|
|
85
|
+
? null
|
|
86
|
+
: {
|
|
92
87
|
exportName: configMetadata.exportName,
|
|
88
|
+
title: configMetadata.title,
|
|
89
|
+
description: configMetadata.description,
|
|
93
90
|
isReadme: configMetadata.isReadme,
|
|
91
|
+
see: configMetadata.see,
|
|
92
|
+
security: configMetadata.security,
|
|
94
93
|
members: mapTypeMembers(configMembers),
|
|
95
|
-
}
|
|
96
|
-
: null,
|
|
94
|
+
},
|
|
97
95
|
graphs,
|
|
98
96
|
};
|
|
99
97
|
}
|
|
98
|
+
/***
|
|
99
|
+
* Resolves member metadata for the public configuration root when one exists.
|
|
100
|
+
*/
|
|
101
|
+
function collectConfigMembers(program, exports, configMetadata) {
|
|
102
|
+
if (configMetadata === null)
|
|
103
|
+
return [];
|
|
104
|
+
const configExport = exports.find((entry) => entry.name === configMetadata.exportName);
|
|
105
|
+
if (configExport === undefined ||
|
|
106
|
+
(configExport.kind !== 'type' && configExport.kind !== 'unknown')) {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
return collectTypeMembers(program, resolveTypeReference(program, configExport.node) ?? {
|
|
110
|
+
type: configExport.node.getType(),
|
|
111
|
+
name: configExport.name,
|
|
112
|
+
sourcePath: configExport.modulePath,
|
|
113
|
+
symbol: configExport.node.getSymbol() ?? null,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
100
116
|
/***
|
|
101
117
|
* Converts semantic type members into serializable analysis output.
|
|
102
118
|
*/
|
package/dist/analyze/badges.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import type { PolicyStatus } from '@ankhorage/policy/status';
|
|
1
2
|
import type { AnalysisBadge } from './types.js';
|
|
2
3
|
import type { PackageJsonModel } from './usage.js';
|
|
3
4
|
/***
|
|
4
5
|
* Derives deterministic repository metadata badges from local repository files.
|
|
5
6
|
*/
|
|
6
|
-
export declare function analyzeBadges(root: string, pkg: PackageJsonModel): Promise<AnalysisBadge[]>;
|
|
7
|
+
export declare function analyzeBadges(root: string, pkg: PackageJsonModel, documentationStatus: PolicyStatus): Promise<AnalysisBadge[]>;
|
package/dist/analyze/badges.js
CHANGED
|
@@ -39,7 +39,7 @@ const BADGE_ORDER = [
|
|
|
39
39
|
/***
|
|
40
40
|
* Derives deterministic repository metadata badges from local repository files.
|
|
41
41
|
*/
|
|
42
|
-
export async function analyzeBadges(root, pkg) {
|
|
42
|
+
export async function analyzeBadges(root, pkg, documentationStatus) {
|
|
43
43
|
const workflowFiles = await readWorkflowFiles(root);
|
|
44
44
|
const badges = [];
|
|
45
45
|
if ((await hasAnyFile(root, ESLINT_CONFIG_FILES)) ||
|
|
@@ -126,9 +126,9 @@ export async function analyzeBadges(root, pkg) {
|
|
|
126
126
|
}
|
|
127
127
|
badges.push({
|
|
128
128
|
id: 'docs',
|
|
129
|
-
label: '
|
|
130
|
-
value:
|
|
131
|
-
color:
|
|
129
|
+
label: 'paradox',
|
|
130
|
+
value: documentationStatus,
|
|
131
|
+
color: getDocumentationStatusColor(documentationStatus),
|
|
132
132
|
});
|
|
133
133
|
return sortBadges(badges);
|
|
134
134
|
}
|
|
@@ -305,3 +305,13 @@ function getBadgeOrder(id) {
|
|
|
305
305
|
const index = BADGE_ORDER.indexOf(id);
|
|
306
306
|
return index === -1 ? BADGE_ORDER.length : index;
|
|
307
307
|
}
|
|
308
|
+
/***
|
|
309
|
+
* Maps the shared traffic-light status to a deterministic badge color.
|
|
310
|
+
*/
|
|
311
|
+
function getDocumentationStatusColor(status) {
|
|
312
|
+
if (status === 'invalid')
|
|
313
|
+
return 'dc2626';
|
|
314
|
+
if (status === 'warnings')
|
|
315
|
+
return 'ca8a04';
|
|
316
|
+
return '0a7f3f';
|
|
317
|
+
}
|
|
@@ -7,11 +7,11 @@ import { isReactComponent } from './utils/isReactComponent.js';
|
|
|
7
7
|
*/
|
|
8
8
|
export function analyzeComponents(exports, options = {}) {
|
|
9
9
|
const components = [];
|
|
10
|
-
for (const
|
|
11
|
-
if (!isReactComponent(
|
|
10
|
+
for (const entry of exports) {
|
|
11
|
+
if (!isReactComponent(entry.node))
|
|
12
12
|
continue;
|
|
13
13
|
const propsFromAnalyzer = options.program
|
|
14
|
-
? collectPropsForExport(options.program, { name:
|
|
14
|
+
? collectPropsForExport(options.program, { name: entry.name, node: entry.node })
|
|
15
15
|
: undefined;
|
|
16
16
|
const analyzerProps = propsFromAnalyzer?.members.map((member) => ({
|
|
17
17
|
name: member.name,
|
|
@@ -20,17 +20,18 @@ export function analyzeComponents(exports, options = {}) {
|
|
|
20
20
|
...(member.defaultValue !== undefined ? { defaultValue: member.defaultValue } : {}),
|
|
21
21
|
description: member.description ?? null,
|
|
22
22
|
})) ?? [];
|
|
23
|
-
const propsType = getComponentPropsType(
|
|
23
|
+
const propsType = getComponentPropsType(entry.node);
|
|
24
24
|
const legacyProps = propsType != null ? getPropsFromType(propsType, options.program?.root) : [];
|
|
25
25
|
const props = analyzerProps.length > 0 ? analyzerProps : legacyProps;
|
|
26
26
|
components.push({
|
|
27
|
-
name:
|
|
28
|
-
description:
|
|
29
|
-
isReadme:
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
name: entry.name,
|
|
28
|
+
description: entry.description,
|
|
29
|
+
isReadme: entry.isReadme,
|
|
30
|
+
see: entry.see,
|
|
31
|
+
security: entry.security,
|
|
32
|
+
modulePath: entry.modulePath,
|
|
33
|
+
sourceLocation: entry.sourceLocation,
|
|
34
|
+
exportPaths: entry.exportPaths,
|
|
34
35
|
props,
|
|
35
36
|
});
|
|
36
37
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ParsedParadoxComment } from '../utils/parseParadoxComment.js';
|
|
2
|
+
export interface CollectedDocumentationComment {
|
|
3
|
+
readonly sourcePath: string;
|
|
4
|
+
readonly line: number;
|
|
5
|
+
readonly raw: string;
|
|
6
|
+
readonly parsed: ParsedParadoxComment;
|
|
7
|
+
}
|
|
8
|
+
/***
|
|
9
|
+
* Collects real Paradox comments from canonical source roots and source-like repository root files.
|
|
10
|
+
*/
|
|
11
|
+
export declare function collectDocumentationCommentsAsync(root: string): Promise<CollectedDocumentationComment[]>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
2
|
+
import { extname, join, relative } from 'node:path';
|
|
3
|
+
import { parseParadoxComment } from '../utils/parseParadoxComment.js';
|
|
4
|
+
const SOURCE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json5']);
|
|
5
|
+
const SCANNED_ROOTS = ['src', 'examples'];
|
|
6
|
+
const COMMENT_PATTERN = /\/\*\*\*[\s\S]*?\*\//g;
|
|
7
|
+
/***
|
|
8
|
+
* Collects real Paradox comments from canonical source roots and source-like repository root files.
|
|
9
|
+
*/
|
|
10
|
+
export async function collectDocumentationCommentsAsync(root) {
|
|
11
|
+
const nestedFiles = (await Promise.all(SCANNED_ROOTS.map((name) => collectSourceFilesAsync(join(root, name))))).flat();
|
|
12
|
+
const rootFiles = await collectRootSourceFilesAsync(root);
|
|
13
|
+
const files = [...new Set([...nestedFiles, ...rootFiles])].sort((a, b) => a.localeCompare(b));
|
|
14
|
+
const comments = await Promise.all(files.map((filePath) => collectFileCommentsAsync(root, filePath)));
|
|
15
|
+
return comments.flat();
|
|
16
|
+
}
|
|
17
|
+
/***
|
|
18
|
+
* Collects supported source files recursively while treating missing canonical roots as empty.
|
|
19
|
+
*/
|
|
20
|
+
async function collectSourceFilesAsync(root) {
|
|
21
|
+
let entries;
|
|
22
|
+
try {
|
|
23
|
+
entries = await readdir(root, { withFileTypes: true });
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (isMissingPathError(error))
|
|
27
|
+
return [];
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
const nested = await Promise.all(entries.map(async (entry) => {
|
|
31
|
+
const path = join(root, entry.name);
|
|
32
|
+
if (entry.isDirectory())
|
|
33
|
+
return collectSourceFilesAsync(path);
|
|
34
|
+
return entry.isFile() && SOURCE_EXTENSIONS.has(extname(entry.name)) ? [path] : [];
|
|
35
|
+
}));
|
|
36
|
+
return nested.flat();
|
|
37
|
+
}
|
|
38
|
+
/***
|
|
39
|
+
* Collects source-like files directly at repository root.
|
|
40
|
+
*/
|
|
41
|
+
async function collectRootSourceFilesAsync(root) {
|
|
42
|
+
const entries = await readdir(root, { withFileTypes: true });
|
|
43
|
+
return entries.flatMap((entry) => entry.isFile() && SOURCE_EXTENSIONS.has(extname(entry.name)) ? [join(root, entry.name)] : []);
|
|
44
|
+
}
|
|
45
|
+
/***
|
|
46
|
+
* Collects Paradox comments from one source file with stable line locations.
|
|
47
|
+
*/
|
|
48
|
+
async function collectFileCommentsAsync(root, filePath) {
|
|
49
|
+
const source = await readFile(filePath, 'utf-8');
|
|
50
|
+
const sourcePath = toPosixPath(relative(root, filePath));
|
|
51
|
+
return [...source.matchAll(COMMENT_PATTERN)].map((match) => {
|
|
52
|
+
const [raw] = match;
|
|
53
|
+
return {
|
|
54
|
+
sourcePath,
|
|
55
|
+
line: source.slice(0, match.index).split('\n').length,
|
|
56
|
+
raw,
|
|
57
|
+
parsed: parseParadoxComment(raw),
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/***
|
|
62
|
+
* Checks whether a filesystem error reports a missing path.
|
|
63
|
+
*/
|
|
64
|
+
function isMissingPathError(error) {
|
|
65
|
+
return error instanceof Error && 'code' in error && error.code === 'ENOENT';
|
|
66
|
+
}
|
|
67
|
+
/***
|
|
68
|
+
* Normalizes filesystem separators for stable documentation paths.
|
|
69
|
+
*/
|
|
70
|
+
function toPosixPath(path) {
|
|
71
|
+
return path.replaceAll('\\', '/');
|
|
72
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AnalysisDocumentationFinding } from '../types.js';
|
|
2
|
+
/***
|
|
3
|
+
* Creates a finding from one canonical documentation policy rule.
|
|
4
|
+
*/
|
|
5
|
+
export declare function createDocumentationFinding(ruleId: string, message: string, sourcePath?: string | null, line?: number | null): AnalysisDocumentationFinding;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { DOCUMENTATION_POLICY } from '@ankhorage/policy/documentation';
|
|
2
|
+
/***
|
|
3
|
+
* Creates a finding from one canonical documentation policy rule.
|
|
4
|
+
*/
|
|
5
|
+
export function createDocumentationFinding(ruleId, message, sourcePath = null, line = null) {
|
|
6
|
+
const rule = DOCUMENTATION_POLICY.rules.find((candidate) => candidate.id === ruleId);
|
|
7
|
+
if (rule === undefined) {
|
|
8
|
+
throw new Error(`Unknown documentation policy rule: ${ruleId}`);
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
ruleId,
|
|
12
|
+
severity: rule.severity,
|
|
13
|
+
message,
|
|
14
|
+
sourcePath,
|
|
15
|
+
line,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Project } from 'ts-morph';
|
|
2
|
+
import type { AnalysisDocumentationFinding, AnalysisExport } from '../types.js';
|
|
3
|
+
import type { CollectedDocumentationComment } from './collectDocumentationCommentsAsync.js';
|
|
4
|
+
/***
|
|
5
|
+
* Evaluates package documentation evidence against the canonical Ankhorage documentation policy.
|
|
6
|
+
*/
|
|
7
|
+
export declare function validateDocumentationPolicyAsync(options: {
|
|
8
|
+
root: string;
|
|
9
|
+
project: Project;
|
|
10
|
+
comments: readonly CollectedDocumentationComment[];
|
|
11
|
+
exports: readonly AnalysisExport[];
|
|
12
|
+
validateSeeUrlAsync?: (url: string) => Promise<unknown>;
|
|
13
|
+
}): Promise<AnalysisDocumentationFinding[]>;
|