@ankhorage/paradox 0.2.0 → 0.2.2
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 +13 -0
- package/README.md +1 -1
- package/dist/analyze/documentation/validateDocumentationPolicyAsync.js +22 -5
- package/dist/analyze/exports.js +5 -10
- package/dist/analyze/modules.js +3 -8
- package/dist/analyze/semantic/exports.js +2 -4
- package/dist/analyze/sequenceScenarios.js +2 -4
- package/dist/render/renderers/diagrams.js +2 -4
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 336ea72: Respect capability-aware documentation policy so packages without programmatic usage or
|
|
8
|
+
configuration surfaces do not require fake README examples or config schemas.
|
|
9
|
+
|
|
10
|
+
## 0.2.1
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- f556df5: Update Ankhorage dependencies: `@ankhorage/policy`.
|
|
15
|
+
|
|
3
16
|
## 0.2.0
|
|
4
17
|
|
|
5
18
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
# @ankhorage/paradox
|
|
5
5
|
|
|
6
|
-
         
|
|
7
7
|
|
|
8
8
|
Deterministic documentation generator for TypeScript packages.
|
|
9
9
|
|
|
@@ -13,7 +13,7 @@ export async function validateDocumentationPolicyAsync(options) {
|
|
|
13
13
|
return [
|
|
14
14
|
...validateCommentRules(options.comments),
|
|
15
15
|
...validateUsageRules(options.comments),
|
|
16
|
-
...(await validateConfigRulesAsync(options.root, options.project)),
|
|
16
|
+
...(await validateConfigRulesAsync(options.root, options.project, options.comments)),
|
|
17
17
|
...validatePublicApiRules(options.exports),
|
|
18
18
|
...(await validateReferencesAsync(options.root, options.project, options.comments, {
|
|
19
19
|
validateSeeUrlAsync: options.validateSeeUrlAsync,
|
|
@@ -44,6 +44,8 @@ function validateCommentRules(comments) {
|
|
|
44
44
|
*/
|
|
45
45
|
function validateUsageRules(comments) {
|
|
46
46
|
const usageComments = comments.filter((comment) => comment.parsed.isUsage);
|
|
47
|
+
if (usageComments.length === 0)
|
|
48
|
+
return [];
|
|
47
49
|
const findings = usageComments.flatMap((comment) => validateUsageComment(comment));
|
|
48
50
|
const readmeExamples = usageComments.filter((comment) => isBelow(comment.sourcePath, DOCUMENTATION_POLICY.paths.examplesRoot) &&
|
|
49
51
|
comment.parsed.isReadme);
|
|
@@ -76,17 +78,27 @@ function validateUsageComment(comment) {
|
|
|
76
78
|
return [...location, ...cliReadme];
|
|
77
79
|
}
|
|
78
80
|
/***
|
|
79
|
-
* Validates
|
|
81
|
+
* Validates configuration only after the package opts into that documentation surface.
|
|
80
82
|
*/
|
|
81
|
-
async function validateConfigRulesAsync(root, project) {
|
|
83
|
+
async function validateConfigRulesAsync(root, project, comments) {
|
|
82
84
|
const configPath = join(root, DOCUMENTATION_POLICY.config.path);
|
|
83
|
-
|
|
85
|
+
const configExists = await fileExistsAsync(configPath);
|
|
86
|
+
const configTagged = comments.some((comment) => comment.parsed.isConfig);
|
|
87
|
+
if (!configExists && !configTagged)
|
|
88
|
+
return [];
|
|
89
|
+
if (!configExists) {
|
|
84
90
|
return [
|
|
85
91
|
createDocumentationFinding('documentation.config.file', `Missing canonical config schema: ${DOCUMENTATION_POLICY.config.path}`),
|
|
86
92
|
];
|
|
87
93
|
}
|
|
88
94
|
const sourceFile = project.getSourceFile(configPath) ?? project.addSourceFileAtPath(configPath);
|
|
89
|
-
|
|
95
|
+
return validateConfigRoots(collectConfigRoots(sourceFile));
|
|
96
|
+
}
|
|
97
|
+
/***
|
|
98
|
+
* Collects canonical @config + @readme type declarations from the config schema.
|
|
99
|
+
*/
|
|
100
|
+
function collectConfigRoots(sourceFile) {
|
|
101
|
+
return sourceFile.getStatements().flatMap((statement) => {
|
|
90
102
|
if (!Node.isInterfaceDeclaration(statement) && !Node.isTypeAliasDeclaration(statement)) {
|
|
91
103
|
return [];
|
|
92
104
|
}
|
|
@@ -96,6 +108,11 @@ async function validateConfigRulesAsync(root, project) {
|
|
|
96
108
|
const parsed = parseParadoxComment(raw);
|
|
97
109
|
return parsed.isConfig && parsed.isReadme ? [{ statement, parsed }] : [];
|
|
98
110
|
});
|
|
111
|
+
}
|
|
112
|
+
/***
|
|
113
|
+
* Validates cardinality and README metadata for canonical config roots.
|
|
114
|
+
*/
|
|
115
|
+
function validateConfigRoots(roots) {
|
|
99
116
|
const findings = [];
|
|
100
117
|
if (roots.length !== DOCUMENTATION_POLICY.config.exactCount) {
|
|
101
118
|
findings.push(createDocumentationFinding('documentation.config.readme.unique', `Expected exactly one @config + @readme root in ${DOCUMENTATION_POLICY.config.path}; found ${roots.length}.`, DOCUMENTATION_POLICY.config.path));
|
package/dist/analyze/exports.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isAbsolute, join, normalize, relative } from 'node:path';
|
|
2
|
+
import { uniqueSortedStrings } from '@ankhorage/utility/array';
|
|
2
3
|
import { Node } from 'ts-morph';
|
|
3
4
|
import { getExportMetadata } from './utils/getExportMetadata.js';
|
|
4
5
|
import { getParadoxComment } from './utils/getParadoxComment.js';
|
|
@@ -45,10 +46,10 @@ export function analyzeExports(project, options) {
|
|
|
45
46
|
title: existing.title ?? parsed.title,
|
|
46
47
|
description: existing.description ?? parsed.description,
|
|
47
48
|
isReadme: existing.isReadme || parsed.isReadme,
|
|
48
|
-
see:
|
|
49
|
-
security:
|
|
50
|
-
exportPaths:
|
|
51
|
-
relatedSymbols:
|
|
49
|
+
see: uniqueSortedStrings([...existing.see, ...parsed.see]),
|
|
50
|
+
security: uniqueSortedStrings([...existing.security, ...parsed.security]),
|
|
51
|
+
exportPaths: uniqueSortedStrings([...existing.exportPaths, ...metadata.exportPaths]),
|
|
52
|
+
relatedSymbols: uniqueSortedStrings([
|
|
52
53
|
...existing.relatedSymbols,
|
|
53
54
|
...metadata.relatedSymbols,
|
|
54
55
|
]),
|
|
@@ -106,12 +107,6 @@ function inferKind(node) {
|
|
|
106
107
|
return 'value';
|
|
107
108
|
return 'unknown';
|
|
108
109
|
}
|
|
109
|
-
/***
|
|
110
|
-
* Returns unique string values sorted for deterministic generated output.
|
|
111
|
-
*/
|
|
112
|
-
function uniqueSorted(values) {
|
|
113
|
-
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
114
|
-
}
|
|
115
110
|
/***
|
|
116
111
|
* Normalizes platform-specific path separators for generated documentation output.
|
|
117
112
|
*/
|
package/dist/analyze/modules.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isAbsolute, join, normalize, relative } from 'node:path';
|
|
2
|
+
import { uniqueSortedStrings } from '@ankhorage/utility/array';
|
|
2
3
|
/***
|
|
3
4
|
* Builds a deterministic module relationship graph for documentation renderers.
|
|
4
5
|
*/
|
|
@@ -34,18 +35,12 @@ export function analyzeModules(project, options) {
|
|
|
34
35
|
return {
|
|
35
36
|
path,
|
|
36
37
|
isEntrypoint: entrypointPaths.has(normalize(sourceFile.getFilePath())),
|
|
37
|
-
dependencies:
|
|
38
|
-
exports:
|
|
38
|
+
dependencies: uniqueSortedStrings(dependencies),
|
|
39
|
+
exports: uniqueSortedStrings(exports),
|
|
39
40
|
};
|
|
40
41
|
})
|
|
41
42
|
.sort((left, right) => left.path.localeCompare(right.path));
|
|
42
43
|
}
|
|
43
|
-
/***
|
|
44
|
-
* Returns unique string values sorted for deterministic generated output.
|
|
45
|
-
*/
|
|
46
|
-
function uniqueSorted(values) {
|
|
47
|
-
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
48
|
-
}
|
|
49
44
|
/***
|
|
50
45
|
* Normalizes platform-specific path separators for generated documentation output.
|
|
51
46
|
*/
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { uniqueSortedStrings } from '@ankhorage/utility/array';
|
|
1
2
|
import { Node as MorphNode, TypeFormatFlags } from 'ts-morph';
|
|
2
3
|
import { isReactComponent } from './isReactComponent.js';
|
|
3
4
|
import { getParadoxComment, parseParadoxComment } from './paradoxComment.js';
|
|
@@ -21,7 +22,7 @@ export function collectExports(program) {
|
|
|
21
22
|
const sourcePath = relativeToRoot(program.root, declaration.getSourceFile().getFilePath());
|
|
22
23
|
const existing = exportsByName.get(name);
|
|
23
24
|
const exportPaths = existing
|
|
24
|
-
?
|
|
25
|
+
? uniqueSortedStrings([...existing.exportPaths, entrypointPath])
|
|
25
26
|
: [entrypointPath];
|
|
26
27
|
const kind = existing?.kind ?? detectExportKind(declaration);
|
|
27
28
|
exportsByName.set(name, {
|
|
@@ -378,9 +379,6 @@ function getCallableNode(node) {
|
|
|
378
379
|
}
|
|
379
380
|
return null;
|
|
380
381
|
}
|
|
381
|
-
function uniqueSorted(values) {
|
|
382
|
-
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
383
|
-
}
|
|
384
382
|
const IGNORED_RELATED_SYMBOLS = new Set([
|
|
385
383
|
'Array',
|
|
386
384
|
'Boolean',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isAbsolute, join, normalize } from 'node:path';
|
|
2
|
+
import { uniqueSortedStrings } from '@ankhorage/utility/array';
|
|
2
3
|
import { Node as MorphNode, } from 'ts-morph';
|
|
3
4
|
import { relativeToRoot, toPosixPath } from './semantic/utils.js';
|
|
4
5
|
import { getParadoxComment } from './utils/getParadoxComment.js';
|
|
@@ -85,7 +86,7 @@ function getBinSourceCandidates(targetPath) {
|
|
|
85
86
|
candidates.push(normalized.replace(/\.jsx?$/, '.ts'));
|
|
86
87
|
candidates.push(normalized.replace(/\.jsx?$/, '.tsx'));
|
|
87
88
|
}
|
|
88
|
-
return
|
|
89
|
+
return uniqueSortedStrings(candidates);
|
|
89
90
|
}
|
|
90
91
|
function getSourceFileByRelativePath(project, root, relativePath) {
|
|
91
92
|
const absolutePath = normalize(isAbsolute(relativePath) ? relativePath : join(root, relativePath));
|
|
@@ -169,6 +170,3 @@ function uniqueByFunctionName(declarations) {
|
|
|
169
170
|
return true;
|
|
170
171
|
});
|
|
171
172
|
}
|
|
172
|
-
function uniqueSorted(values) {
|
|
173
|
-
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
174
|
-
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { uniqueSortedStrings } from '@ankhorage/utility/array';
|
|
1
2
|
import { toFileStem } from '../toFileStem.js';
|
|
2
3
|
const MAX_SEQUENCE_CALL_EDGES = 12;
|
|
3
4
|
const MAX_SEQUENCE_PARTICIPANTS = 8;
|
|
@@ -174,7 +175,7 @@ function groupCallsBySource(callEdges) {
|
|
|
174
175
|
return grouped;
|
|
175
176
|
}
|
|
176
177
|
function collectSequenceParticipants(callEdges) {
|
|
177
|
-
return
|
|
178
|
+
return uniqueSortedStrings(callEdges.flatMap((edge) => [edge.fromSymbol, edge.toSymbol]));
|
|
178
179
|
}
|
|
179
180
|
function getCallEdgeKey(edge) {
|
|
180
181
|
return `${edge.fromSymbol}->${edge.toSymbol}@${edge.sourcePath}:${edge.callExpression}`;
|
|
@@ -191,9 +192,6 @@ function renderFallbackEdge(modules, prefix) {
|
|
|
191
192
|
return ` ${toMermaidId(`${prefix}-${previous.path}`)} -.-> ${toMermaidId(`${prefix}-${module.path}`)}`;
|
|
192
193
|
});
|
|
193
194
|
}
|
|
194
|
-
function uniqueSorted(values) {
|
|
195
|
-
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
196
|
-
}
|
|
197
195
|
function toMermaidId(value) {
|
|
198
196
|
return value.replace(/[^A-Za-z0-9_]/g, '_');
|
|
199
197
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/paradox",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Deterministic documentation generator for TypeScript packages.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"test:standalone": "bun test tests/cli.e2e.test.ts"
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
|
-
"@ankhorage/policy": "^0.
|
|
74
|
+
"@ankhorage/policy": "^0.2.0",
|
|
75
75
|
"@ankhorage/utility": "^1.8.0",
|
|
76
76
|
"ts-morph": "^28.0.0"
|
|
77
77
|
},
|