@ankhorage/paradox 0.1.17 → 0.1.19
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 +20 -11
- package/dist/analyze/analyze.d.ts +1 -0
- package/dist/analyze/analyze.js +9 -0
- package/dist/analyze/readmeCli.d.ts +9 -0
- package/dist/analyze/readmeCli.js +33 -0
- package/dist/analyze/readmeConfig.d.ts +14 -0
- package/dist/analyze/readmeConfig.js +44 -0
- package/dist/analyze/types.d.ts +12 -0
- package/dist/analyze/usage.d.ts +1 -1
- package/dist/analyze/usage.js +1 -1
- package/dist/analyze/utils/getLeadingParadoxComment.d.ts +10 -0
- package/dist/analyze/utils/getLeadingParadoxComment.js +16 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.js +5 -0
- package/dist/cli/standalone.js +1 -3
- package/dist/doc-tags/registry.d.ts +2 -2
- package/dist/doc-tags/registry.js +2 -2
- package/dist/model/buildModel.d.ts +10 -0
- package/dist/model/buildModel.js +14 -21
- package/dist/model/types.d.ts +21 -11
- package/dist/render/renderers/html.js +21 -6
- package/dist/render/renderers/markdown.js +26 -40
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.19
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 5f6a40f: Generate the README CLI chapter from the canonical `src/cli/index.ts` `@readme` opt-in, keep executable commands under CLI instead of Installation, and declare the required Node.js types dependency.
|
|
8
|
+
|
|
9
|
+
## 0.1.18
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- eaa56ad: Render README Configuration examples from the actual tagged Paradox config file instead of synthesized boilerplate.
|
|
14
|
+
|
|
3
15
|
## 0.1.17
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
|
|
4
4
|
# @ankhorage/paradox
|
|
5
5
|
|
|
6
|
-
         
|
|
7
7
|
|
|
8
8
|
Deterministic documentation generator for TypeScript packages.
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## CLI
|
|
11
|
+
|
|
12
|
+
Generates deterministic documentation for a package through the Paradox CLI.
|
|
11
13
|
|
|
12
14
|
```bash
|
|
13
15
|
bunx @ankhorage/paradox
|
|
14
16
|
```
|
|
15
17
|
|
|
16
|
-
## CLI
|
|
17
|
-
|
|
18
18
|
<details>
|
|
19
19
|
<summary>paradox</summary>
|
|
20
20
|
|
|
@@ -24,10 +24,6 @@ The command discovers the nearest Paradox config, resolves the package and outpu
|
|
|
24
24
|
analyzes the package, builds the documentation model, renders all documentation artifacts,
|
|
25
25
|
and writes them to the configured output directory.
|
|
26
26
|
|
|
27
|
-
```bash
|
|
28
|
-
bunx @ankhorage/paradox
|
|
29
|
-
```
|
|
30
|
-
|
|
31
27
|
Diagram: [paradox sequence](./paradox/diagrams/sequences/paradox.mmd)
|
|
32
28
|
|
|
33
29
|
```mermaid
|
|
@@ -66,13 +62,26 @@ sequenceDiagram
|
|
|
66
62
|
|
|
67
63
|
## Configuration
|
|
68
64
|
|
|
69
|
-
|
|
65
|
+
Canonical Paradox configuration for this package.
|
|
70
66
|
|
|
71
67
|
```ts
|
|
72
|
-
import { defineParadoxConfig } from '
|
|
68
|
+
import { defineParadoxConfig } from './src/config/defineParadoxConfig.js';
|
|
73
69
|
|
|
74
70
|
export default defineParadoxConfig({
|
|
75
|
-
|
|
71
|
+
mode: 'write',
|
|
72
|
+
|
|
73
|
+
docs: {
|
|
74
|
+
title: '@ankhorage/paradox',
|
|
75
|
+
description: 'Deterministic documentation generator for TypeScript packages.',
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
package: {
|
|
79
|
+
entrypoints: ['src/index.ts'],
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
output: {
|
|
83
|
+
dir: 'paradox',
|
|
84
|
+
},
|
|
76
85
|
});
|
|
77
86
|
```
|
|
78
87
|
|
package/dist/analyze/analyze.js
CHANGED
|
@@ -5,6 +5,8 @@ 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 { analyzeReadmeCli } from './readmeCli.js';
|
|
9
|
+
import { analyzeReadmeConfig } from './readmeConfig.js';
|
|
8
10
|
import { analyzeReadmeUsage } from './readmeUsage.js';
|
|
9
11
|
import { createTypeScriptProgram } from './semantic/createTypeScriptProgram.js';
|
|
10
12
|
import { collectTypeMembers, resolveTypeReference } from './semantic/exports.js';
|
|
@@ -25,6 +27,11 @@ export async function analyze(config, runtime) {
|
|
|
25
27
|
const readmeUsageDescription = config.docs?.usage?.description ?? null;
|
|
26
28
|
const usageEntryPoints = config.docs?.usage?.entrypoints ?? [];
|
|
27
29
|
const readmeUsage = await analyzeReadmeUsage({ root, entrypoints: usageEntryPoints });
|
|
30
|
+
const readmeCli = await analyzeReadmeCli(root);
|
|
31
|
+
const readmeConfig = await analyzeReadmeConfig({
|
|
32
|
+
root,
|
|
33
|
+
configFilePath: runtime.configFilePath ?? null,
|
|
34
|
+
});
|
|
28
35
|
const program = createTypeScriptProgram({ root, entrypoints, project });
|
|
29
36
|
const { config: configMetadata, exports } = analyzeExports(project, { root, entrypoints });
|
|
30
37
|
const components = analyzeComponents(exports, { program });
|
|
@@ -70,6 +77,8 @@ export async function analyze(config, runtime) {
|
|
|
70
77
|
usage,
|
|
71
78
|
readmeUsageDescription,
|
|
72
79
|
readmeUsage,
|
|
80
|
+
readmeCli,
|
|
81
|
+
readmeConfig,
|
|
73
82
|
config: configMetadata
|
|
74
83
|
? {
|
|
75
84
|
exportName: configMetadata.exportName,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface AnalysisReadmeCli {
|
|
2
|
+
description: string | null;
|
|
3
|
+
sourcePath: string;
|
|
4
|
+
}
|
|
5
|
+
/***
|
|
6
|
+
* Collects README CLI metadata from the canonical Ankhorage CLI entrypoint when its leading
|
|
7
|
+
* Paradox comment opts into README output with @readme.
|
|
8
|
+
*/
|
|
9
|
+
export declare function analyzeReadmeCli(root: string): Promise<AnalysisReadmeCli | null>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { getLeadingParadoxComment } from './utils/getLeadingParadoxComment.js';
|
|
4
|
+
const CLI_INDEX_PATH = 'src/cli/index.ts';
|
|
5
|
+
/***
|
|
6
|
+
* Collects README CLI metadata from the canonical Ankhorage CLI entrypoint when its leading
|
|
7
|
+
* Paradox comment opts into README output with @readme.
|
|
8
|
+
*/
|
|
9
|
+
export async function analyzeReadmeCli(root) {
|
|
10
|
+
const filePath = join(root, CLI_INDEX_PATH);
|
|
11
|
+
let source;
|
|
12
|
+
try {
|
|
13
|
+
source = await readFile(filePath, 'utf-8');
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (isMissingPathError(error))
|
|
17
|
+
return null;
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
const comment = getLeadingParadoxComment(source);
|
|
21
|
+
if (!comment?.parsed.isReadme)
|
|
22
|
+
return null;
|
|
23
|
+
return {
|
|
24
|
+
description: comment.parsed.description,
|
|
25
|
+
sourcePath: CLI_INDEX_PATH,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function isMissingPathError(error) {
|
|
29
|
+
return (error instanceof Error &&
|
|
30
|
+
'code' in error &&
|
|
31
|
+
typeof error.code === 'string' &&
|
|
32
|
+
error.code === 'ENOENT');
|
|
33
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface AnalysisReadmeConfig {
|
|
2
|
+
description: string | null;
|
|
3
|
+
language: string;
|
|
4
|
+
code: string;
|
|
5
|
+
sourcePath: string;
|
|
6
|
+
}
|
|
7
|
+
/***
|
|
8
|
+
* Collects a README configuration example from the actual Paradox config file when its
|
|
9
|
+
* leading Paradox comment is marked with both @config and @readme.
|
|
10
|
+
*/
|
|
11
|
+
export declare function analyzeReadmeConfig(options: {
|
|
12
|
+
root: string;
|
|
13
|
+
configFilePath: string | null;
|
|
14
|
+
}): Promise<AnalysisReadmeConfig | null>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { extname, relative } from 'node:path';
|
|
3
|
+
import { getLeadingParadoxComment } from './utils/getLeadingParadoxComment.js';
|
|
4
|
+
/***
|
|
5
|
+
* Collects a README configuration example from the actual Paradox config file when its
|
|
6
|
+
* leading Paradox comment is marked with both @config and @readme.
|
|
7
|
+
*/
|
|
8
|
+
export async function analyzeReadmeConfig(options) {
|
|
9
|
+
if (options.configFilePath === null)
|
|
10
|
+
return null;
|
|
11
|
+
const source = await readFile(options.configFilePath, 'utf-8');
|
|
12
|
+
const comment = getLeadingParadoxComment(source);
|
|
13
|
+
if (comment === null)
|
|
14
|
+
return null;
|
|
15
|
+
if (!comment.parsed.isConfig || !comment.parsed.isReadme)
|
|
16
|
+
return null;
|
|
17
|
+
const sourcePath = toPosixPath(relative(options.root, options.configFilePath));
|
|
18
|
+
return {
|
|
19
|
+
description: comment.parsed.description,
|
|
20
|
+
language: getLanguage(sourcePath),
|
|
21
|
+
code: removeRange(source, comment.start, comment.end).trim(),
|
|
22
|
+
sourcePath,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function removeRange(source, start, end) {
|
|
26
|
+
const before = source.slice(0, start).trimEnd();
|
|
27
|
+
const after = source.slice(end).trimStart();
|
|
28
|
+
if (before.length === 0)
|
|
29
|
+
return after;
|
|
30
|
+
if (after.length === 0)
|
|
31
|
+
return before;
|
|
32
|
+
return `${before}\n\n${after}`;
|
|
33
|
+
}
|
|
34
|
+
function getLanguage(sourcePath) {
|
|
35
|
+
const extension = extname(sourcePath).toLowerCase();
|
|
36
|
+
if (extension === '.ts')
|
|
37
|
+
return 'ts';
|
|
38
|
+
if (extension === '.js' || extension === '.mjs' || extension === '.cjs')
|
|
39
|
+
return 'js';
|
|
40
|
+
return '';
|
|
41
|
+
}
|
|
42
|
+
function toPosixPath(path) {
|
|
43
|
+
return path.replaceAll('\\', '/');
|
|
44
|
+
}
|
package/dist/analyze/types.d.ts
CHANGED
|
@@ -86,6 +86,16 @@ interface AnalysisReadmeUsage {
|
|
|
86
86
|
code: string;
|
|
87
87
|
sourcePath: string;
|
|
88
88
|
}
|
|
89
|
+
interface AnalysisReadmeCli {
|
|
90
|
+
description: string | null;
|
|
91
|
+
sourcePath: string;
|
|
92
|
+
}
|
|
93
|
+
interface AnalysisReadmeConfig {
|
|
94
|
+
description: string | null;
|
|
95
|
+
language: string;
|
|
96
|
+
code: string;
|
|
97
|
+
sourcePath: string;
|
|
98
|
+
}
|
|
89
99
|
export interface AnalysisBadge {
|
|
90
100
|
id: string;
|
|
91
101
|
label: string;
|
|
@@ -165,6 +175,8 @@ export interface AnalysisResult {
|
|
|
165
175
|
usage: AnalysisUsage | null;
|
|
166
176
|
readmeUsageDescription: string | null;
|
|
167
177
|
readmeUsage: AnalysisReadmeUsage[];
|
|
178
|
+
readmeCli: AnalysisReadmeCli | null;
|
|
179
|
+
readmeConfig: AnalysisReadmeConfig | null;
|
|
168
180
|
config: {
|
|
169
181
|
exportName: string;
|
|
170
182
|
isReadme: boolean;
|
package/dist/analyze/usage.d.ts
CHANGED
|
@@ -11,6 +11,6 @@ export interface PackageJsonModel {
|
|
|
11
11
|
prettier?: unknown;
|
|
12
12
|
}
|
|
13
13
|
/***
|
|
14
|
-
* Builds
|
|
14
|
+
* Builds executable CLI commands from package metadata.
|
|
15
15
|
*/
|
|
16
16
|
export declare function createUsageFromPackageJson(pkg: PackageJsonModel): AnalysisUsage | null;
|
package/dist/analyze/usage.js
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ParsedParadoxComment } from './parseParadoxComment.js';
|
|
2
|
+
export interface LeadingParadoxComment {
|
|
3
|
+
parsed: ParsedParadoxComment;
|
|
4
|
+
start: number;
|
|
5
|
+
end: number;
|
|
6
|
+
}
|
|
7
|
+
/***
|
|
8
|
+
* Parses a leading Paradox comment from a source file.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getLeadingParadoxComment(source: string): LeadingParadoxComment | null;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { parseParadoxComment } from './parseParadoxComment.js';
|
|
2
|
+
/***
|
|
3
|
+
* Parses a leading Paradox comment from a source file.
|
|
4
|
+
*/
|
|
5
|
+
export function getLeadingParadoxComment(source) {
|
|
6
|
+
const match = /^\s*(\/\*\*\*[\s\S]*?\*\/)/.exec(source);
|
|
7
|
+
const comment = match?.[1];
|
|
8
|
+
if (match === null || comment === undefined)
|
|
9
|
+
return null;
|
|
10
|
+
const start = match[0].indexOf(comment);
|
|
11
|
+
return {
|
|
12
|
+
parsed: parseParadoxComment(comment),
|
|
13
|
+
start,
|
|
14
|
+
end: start + comment.length,
|
|
15
|
+
};
|
|
16
|
+
}
|
package/dist/cli/index.d.ts
CHANGED
package/dist/cli/index.js
CHANGED
package/dist/cli/standalone.js
CHANGED
|
@@ -11,8 +11,6 @@ import { write } from '../write/write.js';
|
|
|
11
11
|
* The command discovers the nearest Paradox config, resolves the package and output roots,
|
|
12
12
|
* analyzes the package, builds the documentation model, renders all documentation artifacts,
|
|
13
13
|
* and writes them to the configured output directory.
|
|
14
|
-
*
|
|
15
|
-
* @readme
|
|
16
14
|
*/
|
|
17
15
|
async function main() {
|
|
18
16
|
const cwd = process.cwd();
|
|
@@ -24,7 +22,7 @@ async function main() {
|
|
|
24
22
|
const config = await loadParadoxConfig(configFilePath);
|
|
25
23
|
const packageRoot = await resolvePackageRoot(config, configDir);
|
|
26
24
|
const { outputDir, outputRoot } = resolveOutputRoot(config, packageRoot);
|
|
27
|
-
const analysis = await analyze(config, { packageRoot });
|
|
25
|
+
const analysis = await analyze(config, { packageRoot, configFilePath });
|
|
28
26
|
const model = buildModel(analysis);
|
|
29
27
|
const result = render(model, { outputDir });
|
|
30
28
|
await write(result, config, { packageRoot, outputRoot });
|
|
@@ -8,8 +8,8 @@ export declare const PARADOX_DOC_TAGS: readonly [{
|
|
|
8
8
|
}, {
|
|
9
9
|
readonly name: "config";
|
|
10
10
|
readonly syntax: "@config";
|
|
11
|
-
readonly description: "Marks a type
|
|
12
|
-
readonly appliesTo: readonly ["interface", "type"];
|
|
11
|
+
readonly description: "Marks a configuration type, interface, or source block. Pair with @readme to include the schema or actual config source in README Configuration output.";
|
|
12
|
+
readonly appliesTo: readonly ["block", "interface", "type"];
|
|
13
13
|
readonly repeatable: false;
|
|
14
14
|
readonly handler: "markConfig";
|
|
15
15
|
}, {
|
|
@@ -19,8 +19,8 @@ export const PARADOX_DOC_TAGS = [
|
|
|
19
19
|
{
|
|
20
20
|
name: 'config',
|
|
21
21
|
syntax: '@config',
|
|
22
|
-
description: 'Marks a type
|
|
23
|
-
appliesTo: ['interface', 'type'],
|
|
22
|
+
description: 'Marks a configuration type, interface, or source block. Pair with @readme to include the schema or actual config source in README Configuration output.',
|
|
23
|
+
appliesTo: ['block', 'interface', 'type'],
|
|
24
24
|
repeatable: false,
|
|
25
25
|
handler: 'markConfig',
|
|
26
26
|
},
|
|
@@ -115,6 +115,16 @@ interface BuildModelInput {
|
|
|
115
115
|
code: string;
|
|
116
116
|
sourcePath: string;
|
|
117
117
|
}[];
|
|
118
|
+
readmeCli: {
|
|
119
|
+
description: string | null;
|
|
120
|
+
sourcePath: string;
|
|
121
|
+
} | null;
|
|
122
|
+
readmeConfig: {
|
|
123
|
+
description: string | null;
|
|
124
|
+
language: string;
|
|
125
|
+
code: string;
|
|
126
|
+
sourcePath: string;
|
|
127
|
+
} | null;
|
|
118
128
|
config: {
|
|
119
129
|
exportName: string;
|
|
120
130
|
isReadme: boolean;
|
package/dist/model/buildModel.js
CHANGED
|
@@ -34,14 +34,24 @@ export function buildModel(analysis) {
|
|
|
34
34
|
sourcePath: usageEntry.sourcePath,
|
|
35
35
|
}))
|
|
36
36
|
.sort((left, right) => left.sourcePath.localeCompare(right.sourcePath)),
|
|
37
|
+
readmeCli: analysis.readmeCli !== null
|
|
38
|
+
? {
|
|
39
|
+
description: analysis.readmeCli.description,
|
|
40
|
+
sourcePath: analysis.readmeCli.sourcePath,
|
|
41
|
+
}
|
|
42
|
+
: null,
|
|
43
|
+
readmeConfig: analysis.readmeConfig !== null
|
|
44
|
+
? {
|
|
45
|
+
description: analysis.readmeConfig.description,
|
|
46
|
+
language: analysis.readmeConfig.language,
|
|
47
|
+
code: analysis.readmeConfig.code,
|
|
48
|
+
sourcePath: analysis.readmeConfig.sourcePath,
|
|
49
|
+
}
|
|
50
|
+
: null,
|
|
37
51
|
config: analysis.config !== null
|
|
38
52
|
? {
|
|
39
53
|
exportName: analysis.config.exportName,
|
|
40
54
|
isReadme: analysis.config.isReadme,
|
|
41
|
-
configFile: getDefaultConfigFileName(analysis.packageId),
|
|
42
|
-
factoryName: findConfigFactoryName(analysis.config.exportName, [
|
|
43
|
-
...exportsByName.keys(),
|
|
44
|
-
]),
|
|
45
55
|
members: analysis.config.members,
|
|
46
56
|
}
|
|
47
57
|
: null,
|
|
@@ -150,23 +160,6 @@ function mapComponent(component, exportModel) {
|
|
|
150
160
|
}))),
|
|
151
161
|
};
|
|
152
162
|
}
|
|
153
|
-
/***
|
|
154
|
-
* Finds the conventional config factory export for a config type when present.
|
|
155
|
-
*/
|
|
156
|
-
function findConfigFactoryName(configExportName, exportNames) {
|
|
157
|
-
const prefix = configExportName.endsWith('Config')
|
|
158
|
-
? configExportName.slice(0, -'Config'.length)
|
|
159
|
-
: configExportName;
|
|
160
|
-
const expectedFactoryName = `define${prefix}Config`;
|
|
161
|
-
return exportNames.includes(expectedFactoryName) ? expectedFactoryName : null;
|
|
162
|
-
}
|
|
163
|
-
/***
|
|
164
|
-
* Derives the default config file name from a package id.
|
|
165
|
-
*/
|
|
166
|
-
function getDefaultConfigFileName(packageId) {
|
|
167
|
-
const packageBaseName = packageId.split('/').pop() ?? packageId;
|
|
168
|
-
return `${packageBaseName}.config.ts`;
|
|
169
|
-
}
|
|
170
163
|
/***
|
|
171
164
|
* Returns a copy of items sorted by their `name` property.
|
|
172
165
|
*/
|
package/dist/model/types.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export interface DocumentationModel {
|
|
|
9
9
|
usage: UsageModel | null;
|
|
10
10
|
readmeUsageDescription: string | null;
|
|
11
11
|
readmeUsage: ReadmeUsageModel[];
|
|
12
|
+
readmeCli: ReadmeCliModel | null;
|
|
13
|
+
readmeConfig: ReadmeConfigModel | null;
|
|
12
14
|
config: ConfigModel | null;
|
|
13
15
|
entrypoints: string[];
|
|
14
16
|
modules: ModuleModel[];
|
|
@@ -39,11 +41,19 @@ interface ReadmeUsageModel {
|
|
|
39
41
|
code: string;
|
|
40
42
|
sourcePath: string;
|
|
41
43
|
}
|
|
44
|
+
interface ReadmeCliModel {
|
|
45
|
+
description: string | null;
|
|
46
|
+
sourcePath: string;
|
|
47
|
+
}
|
|
48
|
+
interface ReadmeConfigModel {
|
|
49
|
+
description: string | null;
|
|
50
|
+
language: string;
|
|
51
|
+
code: string;
|
|
52
|
+
sourcePath: string;
|
|
53
|
+
}
|
|
42
54
|
interface ConfigModel {
|
|
43
55
|
exportName: string;
|
|
44
56
|
isReadme: boolean;
|
|
45
|
-
configFile: string;
|
|
46
|
-
factoryName: string | null;
|
|
47
57
|
members: ConfigMemberModel[];
|
|
48
58
|
}
|
|
49
59
|
export interface ExportModel {
|
|
@@ -84,6 +94,12 @@ export interface SequenceScenarioModel {
|
|
|
84
94
|
description: string | null;
|
|
85
95
|
isReadme: boolean;
|
|
86
96
|
}
|
|
97
|
+
export interface ModuleModel {
|
|
98
|
+
path: string;
|
|
99
|
+
isEntrypoint: boolean;
|
|
100
|
+
dependencies: string[];
|
|
101
|
+
exports: string[];
|
|
102
|
+
}
|
|
87
103
|
interface ExampleModel {
|
|
88
104
|
title: string | null;
|
|
89
105
|
language: string | null;
|
|
@@ -116,15 +132,6 @@ interface MemberModel {
|
|
|
116
132
|
inheritedFrom?: string;
|
|
117
133
|
children?: MemberModel[];
|
|
118
134
|
}
|
|
119
|
-
interface StructuredRowModel {
|
|
120
|
-
values: Record<string, string>;
|
|
121
|
-
}
|
|
122
|
-
export interface ModuleModel {
|
|
123
|
-
path: string;
|
|
124
|
-
isEntrypoint: boolean;
|
|
125
|
-
dependencies: string[];
|
|
126
|
-
exports: string[];
|
|
127
|
-
}
|
|
128
135
|
interface PropModel {
|
|
129
136
|
name: string;
|
|
130
137
|
type: string;
|
|
@@ -132,6 +139,9 @@ interface PropModel {
|
|
|
132
139
|
defaultValue?: string;
|
|
133
140
|
description: string | null;
|
|
134
141
|
}
|
|
142
|
+
interface StructuredRowModel {
|
|
143
|
+
values: Record<string, string>;
|
|
144
|
+
}
|
|
135
145
|
interface ConfigMemberModel {
|
|
136
146
|
name: string;
|
|
137
147
|
type: string;
|
|
@@ -211,7 +211,7 @@ function renderHomeView(model, diagrams, cliScenarios, exportsByModule) {
|
|
|
211
211
|
${model.entrypoints.map((entrypoint) => `<li><code>${escapeHtml(entrypoint)}</code></li>`).join('')}
|
|
212
212
|
</ul>
|
|
213
213
|
</section>
|
|
214
|
-
${
|
|
214
|
+
${model.readmeCli !== null ? renderCliPanel(model, diagrams, cliScenarios) : ''}
|
|
215
215
|
<section class="panel">
|
|
216
216
|
<h2>Modules</h2>
|
|
217
217
|
${model.modules.map(renderModuleCard).join('')}
|
|
@@ -239,22 +239,33 @@ function renderHomeView(model, diagrams, cliScenarios, exportsByModule) {
|
|
|
239
239
|
* Renders the Home CLI chapter for detected bin scenarios.
|
|
240
240
|
*/
|
|
241
241
|
function renderCliPanel(model, diagrams, scenarios) {
|
|
242
|
-
|
|
242
|
+
const commands = model.usage?.commands ?? [];
|
|
243
|
+
const searchText = [
|
|
244
|
+
'cli',
|
|
245
|
+
model.readmeCli?.description ?? '',
|
|
246
|
+
...commands.map((command) => command.command),
|
|
247
|
+
...scenarios.map((scenario) => scenario.name),
|
|
248
|
+
].join(' ');
|
|
249
|
+
return `<section class="panel" data-search="${escapeAttribute(searchText)}">
|
|
243
250
|
<h2>CLI</h2>
|
|
251
|
+
${model.readmeCli?.description === null || model.readmeCli?.description === undefined ? '' : `<p>${escapeHtml(model.readmeCli.description)}</p>`}
|
|
252
|
+
${commands.length === 0 ? '' : `<pre>${escapeHtml(commands.map((command) => command.command).join('\n'))}</pre>`}
|
|
244
253
|
${scenarios
|
|
245
254
|
.map((scenario) => {
|
|
246
|
-
const command = model.usage?.commands.find((item) => item.name === scenario.name);
|
|
247
255
|
const diagram = findScenarioDiagram(diagrams, scenario);
|
|
248
|
-
|
|
256
|
+
if (scenario.description === null && diagram === undefined)
|
|
257
|
+
return '';
|
|
258
|
+
return `<article class="item" data-search="${escapeAttribute([scenario.name, scenario.description ?? ''].join(' '))}">
|
|
249
259
|
<h3>${escapeHtml(scenario.name)}</h3>
|
|
250
260
|
${scenario.description === null ? '' : `<p>${escapeHtml(scenario.description)}</p>`}
|
|
251
|
-
${command === undefined ? '' : `<pre>${escapeHtml(command.command)}</pre>`}
|
|
252
261
|
${diagram === undefined ? '' : renderDiagramCard(diagram)}
|
|
253
262
|
</article>`;
|
|
254
263
|
})
|
|
255
264
|
.join('')}
|
|
256
265
|
</section>`;
|
|
257
266
|
}
|
|
267
|
+
/***
|
|
268
|
+
* Renders one source file entry in the left navigation.
|
|
258
269
|
/***
|
|
259
270
|
* Renders one source file entry in the left navigation.
|
|
260
271
|
*/
|
|
@@ -299,8 +310,12 @@ function getSourceAreas(model) {
|
|
|
299
310
|
* Selects bin scenarios that should be shown on the Home page.
|
|
300
311
|
*/
|
|
301
312
|
function getReadmeCliScenarios(model) {
|
|
302
|
-
|
|
313
|
+
if (model.readmeCli === null)
|
|
314
|
+
return [];
|
|
315
|
+
return model.sequenceScenarios.filter((scenario) => scenario.kind === 'bin');
|
|
303
316
|
}
|
|
317
|
+
/***
|
|
318
|
+
* Finds the generated Mermaid artifact for a sequence scenario.
|
|
304
319
|
/***
|
|
305
320
|
* Finds the generated Mermaid artifact for a sequence scenario.
|
|
306
321
|
*/
|
|
@@ -24,15 +24,8 @@ function renderReadme(model, outputDir, badges, diagrams) {
|
|
|
24
24
|
if (model.description)
|
|
25
25
|
lines.push(model.description, '');
|
|
26
26
|
renderReadmeUsage(lines, model.readmeUsageDescription, model.readmeUsage);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
for (const command of model.usage.commands)
|
|
30
|
-
lines.push(command.command);
|
|
31
|
-
lines.push('```', '');
|
|
32
|
-
}
|
|
33
|
-
renderCliScenarios(lines, model, outputDir, diagrams);
|
|
34
|
-
if (model.config?.isReadme)
|
|
35
|
-
renderConfiguration(lines, model);
|
|
27
|
+
renderReadmeCli(lines, model, outputDir, diagrams);
|
|
28
|
+
renderConfiguration(lines, model);
|
|
36
29
|
renderGeneratedDocumentation(lines, outputDir, diagrams);
|
|
37
30
|
renderReadmeApi(lines, model);
|
|
38
31
|
return `${lines.join('\n').trimEnd()}\n`;
|
|
@@ -58,23 +51,27 @@ function renderReadmeUsage(lines, description, entries) {
|
|
|
58
51
|
lines.push('```', '');
|
|
59
52
|
}
|
|
60
53
|
}
|
|
61
|
-
function
|
|
62
|
-
|
|
63
|
-
if (scenarios.length === 0)
|
|
54
|
+
function renderReadmeCli(lines, model, outputDir, diagrams) {
|
|
55
|
+
if (model.readmeCli === null)
|
|
64
56
|
return;
|
|
65
57
|
lines.push('## CLI', '');
|
|
58
|
+
if (model.readmeCli.description !== null)
|
|
59
|
+
lines.push(model.readmeCli.description, '');
|
|
60
|
+
if (model.usage !== null && model.usage.commands.length > 0) {
|
|
61
|
+
lines.push('```bash');
|
|
62
|
+
for (const command of model.usage.commands)
|
|
63
|
+
lines.push(command.command);
|
|
64
|
+
lines.push('```', '');
|
|
65
|
+
}
|
|
66
|
+
const scenarios = model.sequenceScenarios.filter((scenario) => scenario.kind === 'bin');
|
|
66
67
|
for (const scenario of scenarios) {
|
|
68
|
+
const diagram = findScenarioDiagram(diagrams, scenario);
|
|
69
|
+
if (scenario.description === null && diagram === undefined)
|
|
70
|
+
continue;
|
|
67
71
|
lines.push('<details>');
|
|
68
72
|
lines.push(`<summary>${scenario.name}</summary>`, '');
|
|
69
73
|
if (scenario.description !== null)
|
|
70
74
|
lines.push(scenario.description, '');
|
|
71
|
-
const command = model.usage?.commands.find((item) => item.name === scenario.name);
|
|
72
|
-
if (command !== undefined) {
|
|
73
|
-
lines.push('```bash');
|
|
74
|
-
lines.push(command.command);
|
|
75
|
-
lines.push('```', '');
|
|
76
|
-
}
|
|
77
|
-
const diagram = findScenarioDiagram(diagrams, scenario);
|
|
78
75
|
if (diagram !== undefined) {
|
|
79
76
|
lines.push(`Diagram: [${diagram.title}](./${outputDir}/${diagram.path})`, '');
|
|
80
77
|
lines.push('```mermaid');
|
|
@@ -88,30 +85,19 @@ function findScenarioDiagram(diagrams, scenario) {
|
|
|
88
85
|
return diagrams.find((diagram) => diagram.path === `diagrams/sequences/${toFileStem(scenario.name)}.mmd`);
|
|
89
86
|
}
|
|
90
87
|
function renderConfiguration(lines, model) {
|
|
91
|
-
const
|
|
92
|
-
|
|
88
|
+
const config = model.config?.isReadme ? model.config : null;
|
|
89
|
+
const example = model.readmeConfig;
|
|
90
|
+
if (config === null && example === null)
|
|
93
91
|
return;
|
|
94
92
|
lines.push('## Configuration', '');
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
lines.push(
|
|
99
|
-
lines.push(
|
|
100
|
-
lines.push(
|
|
101
|
-
lines.push(' // ...');
|
|
102
|
-
lines.push('});');
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
lines.push(`import type { ${config.exportName} } from '${model.packageId}';`);
|
|
106
|
-
lines.push('');
|
|
107
|
-
lines.push('const config = {');
|
|
108
|
-
lines.push(' // ...');
|
|
109
|
-
lines.push(`} satisfies ${config.exportName};`);
|
|
110
|
-
lines.push('');
|
|
111
|
-
lines.push('export default config;');
|
|
93
|
+
if (example !== null) {
|
|
94
|
+
if (example.description !== null)
|
|
95
|
+
lines.push(example.description, '');
|
|
96
|
+
lines.push(`\`\`\`${example.language}`);
|
|
97
|
+
lines.push(example.code);
|
|
98
|
+
lines.push('```', '');
|
|
112
99
|
}
|
|
113
|
-
|
|
114
|
-
if (config.members.length === 0)
|
|
100
|
+
if (config === null || config.members.length === 0)
|
|
115
101
|
return;
|
|
116
102
|
lines.push('<details>');
|
|
117
103
|
lines.push('<summary>Configuration options</summary>', '');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/paradox",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.19",
|
|
4
4
|
"description": "Deterministic documentation generator for TypeScript packages.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -75,7 +75,8 @@
|
|
|
75
75
|
"@ankhorage/devtools": "^1.0.6",
|
|
76
76
|
"@changesets/cli": "^2.31.0",
|
|
77
77
|
"@types/bun": "^1.3.13",
|
|
78
|
-
"typescript": "^5.6.3"
|
|
78
|
+
"typescript": "^5.6.3",
|
|
79
|
+
"@types/node": "^25.6.0"
|
|
79
80
|
},
|
|
80
81
|
"packageManager": "bun@1.3.13"
|
|
81
82
|
}
|