@ankhorage/paradox 0.1.9 → 0.1.10

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.10
4
+
5
+ ### Patch Changes
6
+
7
+ - 47b7ced: Add generated README usage support from real source examples marked through configured usage entrypoints, while keeping existing package command usage intact.
8
+
3
9
  ## 0.1.9
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # @ankhorage/paradox
5
5
 
6
- ![license: MIT](./paradox/badges/license.svg) ![npm: v0.1.9](./paradox/badges/npm.svg) ![runtime: bun](./paradox/badges/runtime.svg) ![typescript: strict](./paradox/badges/typescript.svg) ![eslint: checked](./paradox/badges/eslint.svg) ![prettier: checked](./paradox/badges/prettier.svg) ![build: checked](./paradox/badges/build.svg) ![tests: checked](./paradox/badges/tests.svg) ![docs: paradox](./paradox/badges/docs.svg)
6
+ ![license: MIT](./paradox/badges/license.svg) ![npm: v0.1.10](./paradox/badges/npm.svg) ![runtime: bun](./paradox/badges/runtime.svg) ![typescript: strict](./paradox/badges/typescript.svg) ![eslint: checked](./paradox/badges/eslint.svg) ![prettier: checked](./paradox/badges/prettier.svg) ![build: checked](./paradox/badges/build.svg) ![tests: checked](./paradox/badges/tests.svg) ![docs: paradox](./paradox/badges/docs.svg)
7
7
 
8
8
  Deterministic documentation generator for TypeScript packages.
9
9
 
@@ -79,12 +79,12 @@ export default defineParadoxConfig({
79
79
  <details>
80
80
  <summary>Configuration options</summary>
81
81
 
82
- | Field | Type | Required | Default | Description |
83
- | ------- | --------------------------------------------------------- | -------- | ------- | ----------- |
84
- | mode | `'safe' \| 'write' \| undefined` | no | — | |
85
- | docs | `{ title?: string; description?: string; } \| undefined` | no | — | |
86
- | package | `{ root?: string; entrypoints?: string[]; } \| undefined` | no | — | |
87
- | output | `{ dir?: string; } \| undefined` | no | — | |
82
+ | Field | Type | Required | Default | Description |
83
+ | ------- | --------------------------------------------------------------------------------------------- | -------- | ------- | ----------- |
84
+ | mode | `'safe' \| 'write' \| undefined` | no | — | |
85
+ | docs | `{ title?: string; description?: string; usage?: { entrypoints?: string[]; }; } \| undefined` | no | — | |
86
+ | package | `{ root?: string; entrypoints?: string[]; } \| undefined` | no | — | |
87
+ | output | `{ dir?: string; } \| undefined` | no | — | |
88
88
 
89
89
  </details>
90
90
 
@@ -127,23 +127,3 @@ Module: `src/config/types.ts`
127
127
  Source: `src/config/types.ts:7:1`
128
128
 
129
129
  </details>
130
-
131
- ### Documentation
132
-
133
- <details>
134
- <summary>PARADOX_DOC_TAGS</summary>
135
-
136
- Supported Paradox documentation tags.
137
-
138
- Paradox supports doc tags inside triple-star documentation comments.
139
-
140
- | name | syntax | description | applies to | repeatable | handler |
141
- | --------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ---------- | -------------- |
142
- | `readme` | `@readme` | Includes a documentation block or exported symbol in README output. | block, symbol | no | `markReadme` |
143
- | `config` | `@config` | Marks a type or interface as part of the Paradox configuration model. @config alone does not imply README inclusion; use @config plus @readme for README output. | interface, type | no | `markConfig` |
144
- | `example` | `@example` | Adds a titled fenced code example to the generated documentation for a symbol. | symbol | yes | `parseExample` |
145
-
146
- Module: `src/doc-tags/registry.ts`
147
- Source: `src/doc-tags/registry.ts:8:14`
148
-
149
- </details>
@@ -5,6 +5,7 @@ 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 { analyzeReadmeUsage } from './readmeUsage.js';
8
9
  import { createTypeScriptProgram } from './semantic/createTypeScriptProgram.js';
9
10
  import { collectTypeMembers, resolveTypeReference } from './semantic/exports.js';
10
11
  import { collectCallGraph, collectComponentCompositionGraph, collectImportGraph, } from './semantic/graphs.js';
@@ -21,10 +22,16 @@ export async function analyze(config, runtime) {
21
22
  const badges = await analyzeBadges(root, pkg);
22
23
  const project = createProject(root);
23
24
  const entrypoints = config.package?.entrypoints ?? ['src/index.ts'];
25
+ const usageEntryPoints = config.docs?.usage?.entrypoints ?? [];
26
+ const readmeUsage = await analyzeReadmeUsage({ root, entrypoints: usageEntryPoints });
24
27
  const program = createTypeScriptProgram({ root, entrypoints, project });
25
28
  const { config: configMetadata, exports } = analyzeExports(project, { root, entrypoints });
26
29
  const components = analyzeComponents(exports, { program });
27
- const modules = analyzeModules(project, { root, entrypoints });
30
+ const modules = analyzeModules(project, {
31
+ root,
32
+ entrypoints,
33
+ excludePaths: usageEntryPoints,
34
+ });
28
35
  const sourceFunctions = analyzeSourceFunctions(project, root);
29
36
  const sequenceScenarios = analyzeSequenceScenarios({ project, root, pkg, exports });
30
37
  const configExport = configMetadata
@@ -60,6 +67,7 @@ export async function analyze(config, runtime) {
60
67
  badges,
61
68
  sequenceScenarios,
62
69
  usage,
70
+ readmeUsage,
63
71
  config: configMetadata
64
72
  ? {
65
73
  exportName: configMetadata.exportName,
@@ -6,4 +6,5 @@ import type { AnalysisModule } from './types.js';
6
6
  export declare function analyzeModules(project: Project, options: {
7
7
  root: string;
8
8
  entrypoints: readonly string[];
9
+ excludePaths?: readonly string[];
9
10
  }): AnalysisModule[];
@@ -5,6 +5,7 @@ import { isAbsolute, join, normalize, relative } from 'node:path';
5
5
  export function analyzeModules(project, options) {
6
6
  const rootPath = normalize(options.root);
7
7
  const entrypointPaths = new Set(options.entrypoints.map((entrypoint) => normalize(isAbsolute(entrypoint) ? entrypoint : join(options.root, entrypoint))));
8
+ const excludedPaths = new Set((options.excludePaths ?? []).map((entrypoint) => normalize(isAbsolute(entrypoint) ? entrypoint : join(options.root, entrypoint))));
8
9
  return project
9
10
  .getSourceFiles()
10
11
  .filter((sourceFile) => {
@@ -12,6 +13,7 @@ export function analyzeModules(project, options) {
12
13
  const normalizedPath = toPosixPath(filePath);
13
14
  return (!sourceFile.isDeclarationFile() &&
14
15
  filePath.startsWith(rootPath) &&
16
+ !excludedPaths.has(filePath) &&
15
17
  !normalizedPath.includes('/node_modules/'));
16
18
  })
17
19
  .map((sourceFile) => {
@@ -21,7 +23,9 @@ export function analyzeModules(project, options) {
21
23
  .map((declaration) => declaration.getModuleSpecifierSourceFile())
22
24
  .filter((dependency) => dependency != null)
23
25
  .map((dependency) => normalize(dependency.getFilePath()))
24
- .filter((dependency) => dependency.startsWith(rootPath) && !toPosixPath(dependency).includes('/node_modules/'))
26
+ .filter((dependency) => dependency.startsWith(rootPath) &&
27
+ !excludedPaths.has(dependency) &&
28
+ !toPosixPath(dependency).includes('/node_modules/'))
25
29
  .map((dependency) => toPosixPath(relative(options.root, dependency)));
26
30
  const exports = sourceFile
27
31
  .getExportSymbols()
@@ -0,0 +1,14 @@
1
+ export interface AnalysisReadmeUsage {
2
+ title: string | null;
3
+ description: string | null;
4
+ language: string;
5
+ code: string;
6
+ sourcePath: string;
7
+ }
8
+ /***
9
+ * Collects README usage examples from configured real source files.
10
+ */
11
+ export declare function analyzeReadmeUsage(options: {
12
+ root: string;
13
+ entrypoints: readonly string[];
14
+ }): Promise<AnalysisReadmeUsage[]>;
@@ -0,0 +1,72 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { extname, isAbsolute, join, relative } from 'node:path';
3
+ import { parseParadoxComment } from './utils/parseParadoxComment.js';
4
+ const USAGE_TAG = `${String.fromCharCode(64)}usage`;
5
+ /***
6
+ * Collects README usage examples from configured real source files.
7
+ */
8
+ export async function analyzeReadmeUsage(options) {
9
+ const entries = await Promise.all(options.entrypoints.map(async (entrypoint) => analyzeUsageEntrypoint(options.root, entrypoint)));
10
+ return entries.flat().sort((left, right) => left.sourcePath.localeCompare(right.sourcePath));
11
+ }
12
+ async function analyzeUsageEntrypoint(root, entrypoint) {
13
+ const absolutePath = isAbsolute(entrypoint) ? entrypoint : join(root, entrypoint);
14
+ const source = await readFile(absolutePath, 'utf-8');
15
+ const sourcePath = toPosixPath(relative(root, absolutePath));
16
+ const matches = findUsageComments(source);
17
+ return matches.map((match) => {
18
+ const parsed = parseParadoxComment(match.comment);
19
+ return {
20
+ title: getUsageTitle(parsed.description, sourcePath),
21
+ description: parsed.description,
22
+ language: getLanguage(sourcePath),
23
+ code: removeRange(source, match.start, match.end).trim(),
24
+ sourcePath,
25
+ };
26
+ });
27
+ }
28
+ function findUsageComments(source) {
29
+ const matches = [];
30
+ const pattern = /\/\*\*\*[\s\S]*?\*\//g;
31
+ for (const match of source.matchAll(pattern)) {
32
+ const [comment] = match;
33
+ if (!comment.includes(USAGE_TAG))
34
+ continue;
35
+ matches.push({
36
+ comment,
37
+ start: match.index,
38
+ end: match.index + comment.length,
39
+ });
40
+ }
41
+ return matches;
42
+ }
43
+ function removeRange(source, start, end) {
44
+ const before = source.slice(0, start).trimEnd();
45
+ const after = source.slice(end).trimStart();
46
+ if (before.length === 0)
47
+ return after;
48
+ if (after.length === 0)
49
+ return before;
50
+ return `${before}\n\n${after}`;
51
+ }
52
+ function getUsageTitle(description, sourcePath) {
53
+ if (description === null)
54
+ return sourcePath;
55
+ const [firstLine = sourcePath] = description.split('\n');
56
+ return firstLine.trim() || sourcePath;
57
+ }
58
+ function getLanguage(sourcePath) {
59
+ const extension = extname(sourcePath).toLowerCase();
60
+ if (extension === '.tsx')
61
+ return 'tsx';
62
+ if (extension === '.ts')
63
+ return 'ts';
64
+ if (extension === '.jsx')
65
+ return 'jsx';
66
+ if (extension === '.js')
67
+ return 'js';
68
+ return '';
69
+ }
70
+ function toPosixPath(path) {
71
+ return path.replaceAll('\\', '/');
72
+ }
@@ -79,6 +79,13 @@ interface AnalysisUsageCommand {
79
79
  name: string;
80
80
  command: string;
81
81
  }
82
+ interface AnalysisReadmeUsage {
83
+ title: string | null;
84
+ description: string | null;
85
+ language: string;
86
+ code: string;
87
+ sourcePath: string;
88
+ }
82
89
  export interface AnalysisBadge {
83
90
  id: string;
84
91
  label: string;
@@ -156,6 +163,7 @@ export interface AnalysisResult {
156
163
  badges: AnalysisBadge[];
157
164
  sequenceScenarios: AnalysisSequenceScenario[];
158
165
  usage: AnalysisUsage | null;
166
+ readmeUsage: AnalysisReadmeUsage[];
159
167
  config: {
160
168
  exportName: string;
161
169
  isReadme: boolean;
@@ -1,10 +1,11 @@
1
1
  /***
2
2
  * Parsed representation of a Paradox doc comment.
3
3
  */
4
- interface ParsedParadoxComment {
4
+ export interface ParsedParadoxComment {
5
5
  description: string | null;
6
6
  isConfig: boolean;
7
7
  isReadme: boolean;
8
+ isUsage: boolean;
8
9
  examples: ParsedExample[];
9
10
  params: Record<string, string>;
10
11
  returns: string | null;
@@ -1,3 +1,4 @@
1
+ const USAGE_TAG = `${String.fromCharCode(64)}usage`;
1
2
  /***
2
3
  * Parses a Paradox doc comment into structured metadata.
3
4
  */
@@ -7,6 +8,7 @@ export function parseParadoxComment(rawComment) {
7
8
  const examples = [];
8
9
  let isConfig = false;
9
10
  let isReadme = false;
11
+ let isUsage = false;
10
12
  const params = {};
11
13
  let returns = null;
12
14
  for (let index = 0; index < lines.length; index += 1) {
@@ -20,6 +22,10 @@ export function parseParadoxComment(rawComment) {
20
22
  isReadme = true;
21
23
  continue;
22
24
  }
25
+ if (trimmed.startsWith(USAGE_TAG)) {
26
+ isUsage = true;
27
+ continue;
28
+ }
23
29
  if (trimmed.startsWith('@example')) {
24
30
  const parsed = parseExample(lines, index);
25
31
  examples.push(parsed.example);
@@ -46,6 +52,7 @@ export function parseParadoxComment(rawComment) {
46
52
  description: description.length > 0 ? description : null,
47
53
  isConfig,
48
54
  isReadme,
55
+ isUsage,
49
56
  examples,
50
57
  params,
51
58
  returns,
@@ -9,6 +9,9 @@ export interface ParadoxConfig {
9
9
  docs?: {
10
10
  title?: string;
11
11
  description?: string;
12
+ usage?: {
13
+ entrypoints?: string[];
14
+ };
12
15
  };
13
16
  package?: {
14
17
  root?: string;
@@ -1,10 +1,3 @@
1
- /***
2
- * Supported Paradox documentation tags.
3
- *
4
- * Paradox supports doc tags inside triple-star documentation comments.
5
- *
6
- * @readme
7
- */
8
1
  export declare const PARADOX_DOC_TAGS: readonly [{
9
2
  readonly name: "readme";
10
3
  readonly syntax: "@readme";
@@ -26,6 +19,13 @@ export declare const PARADOX_DOC_TAGS: readonly [{
26
19
  readonly appliesTo: readonly ["symbol"];
27
20
  readonly repeatable: true;
28
21
  readonly handler: "parseExample";
22
+ }, {
23
+ readonly name: "usage";
24
+ readonly syntax: "@usage";
25
+ readonly description: "Promotes a real source example into the generated README Usage section.";
26
+ readonly appliesTo: readonly ["block", "symbol"];
27
+ readonly repeatable: false;
28
+ readonly handler: "markUsage";
29
29
  }];
30
30
  export type ParadoxDocTagName = (typeof PARADOX_DOC_TAGS)[number]['name'];
31
31
  export type ParadoxDocTagHandlerId = (typeof PARADOX_DOC_TAGS)[number]['handler'];
@@ -5,6 +5,8 @@
5
5
  *
6
6
  * @readme
7
7
  */
8
+ const DOC_TAG_PREFIX = '\u0040';
9
+ const USAGE_DOC_TAG = `${DOC_TAG_PREFIX}usage`;
8
10
  export const PARADOX_DOC_TAGS = [
9
11
  {
10
12
  name: 'readme',
@@ -30,6 +32,14 @@ export const PARADOX_DOC_TAGS = [
30
32
  repeatable: true,
31
33
  handler: 'parseExample',
32
34
  },
35
+ {
36
+ name: 'usage',
37
+ syntax: USAGE_DOC_TAG,
38
+ description: 'Promotes a real source example into the generated README Usage section.',
39
+ appliesTo: ['block', 'symbol'],
40
+ repeatable: false,
41
+ handler: 'markUsage',
42
+ },
33
43
  ];
34
44
  /***
35
45
  * Looks up documentation tag metadata by tag name.
@@ -107,6 +107,13 @@ interface BuildModelInput {
107
107
  command: string;
108
108
  }[];
109
109
  } | null;
110
+ readmeUsage: {
111
+ title: string | null;
112
+ description: string | null;
113
+ language: string;
114
+ code: string;
115
+ sourcePath: string;
116
+ }[];
110
117
  config: {
111
118
  exportName: string;
112
119
  isReadme: boolean;
@@ -24,6 +24,15 @@ export function buildModel(analysis) {
24
24
  }))),
25
25
  }
26
26
  : null,
27
+ readmeUsage: analysis.readmeUsage
28
+ .map((usageEntry) => ({
29
+ title: usageEntry.title,
30
+ description: usageEntry.description,
31
+ language: usageEntry.language,
32
+ code: usageEntry.code,
33
+ sourcePath: usageEntry.sourcePath,
34
+ }))
35
+ .sort((left, right) => left.sourcePath.localeCompare(right.sourcePath)),
27
36
  config: analysis.config !== null
28
37
  ? {
29
38
  exportName: analysis.config.exportName,
@@ -7,6 +7,7 @@ export interface DocumentationModel {
7
7
  description: string | null;
8
8
  badges: GeneratedBadge[];
9
9
  usage: UsageModel | null;
10
+ readmeUsage: ReadmeUsageModel[];
10
11
  config: ConfigModel | null;
11
12
  entrypoints: string[];
12
13
  modules: ModuleModel[];
@@ -30,6 +31,13 @@ interface UsageCommandModel {
30
31
  name: string;
31
32
  command: string;
32
33
  }
34
+ interface ReadmeUsageModel {
35
+ title: string | null;
36
+ description: string | null;
37
+ language: string;
38
+ code: string;
39
+ sourcePath: string;
40
+ }
33
41
  interface ConfigModel {
34
42
  exportName: string;
35
43
  isReadme: boolean;
@@ -21,25 +21,41 @@ function renderReadme(model, outputDir, badges, diagrams) {
21
21
  .map((badge) => `![${badgeLabel(model, badge.path)}](./${outputDir}/${badge.path})`)
22
22
  .join(' '), '');
23
23
  }
24
- if (model.description) {
24
+ if (model.description)
25
25
  lines.push(model.description, '');
26
- }
26
+ renderReadmeUsage(lines, model.readmeUsage);
27
27
  if (model.usage !== null) {
28
- lines.push('## Installation', '');
29
- lines.push('```bash');
30
- for (const command of model.usage.commands) {
28
+ lines.push('## Installation', '', '```bash');
29
+ for (const command of model.usage.commands)
31
30
  lines.push(command.command);
32
- }
33
31
  lines.push('```', '');
34
32
  }
35
33
  renderCliScenarios(lines, model, outputDir, diagrams);
36
- if (model.config?.isReadme) {
34
+ if (model.config?.isReadme)
37
35
  renderConfiguration(lines, model);
38
- }
39
36
  renderGeneratedDocumentation(lines, outputDir, diagrams);
40
37
  renderReadmeApi(lines, model);
41
38
  return `${lines.join('\n').trimEnd()}\n`;
42
39
  }
40
+ function renderReadmeUsage(lines, entries) {
41
+ if (entries.length === 0)
42
+ return;
43
+ lines.push('## Usage', '');
44
+ for (const entry of entries) {
45
+ if (entry.title !== null)
46
+ lines.push(`### ${entry.title}`, '');
47
+ if (entry.description !== null) {
48
+ const [, ...rest] = entry.description.split('\n');
49
+ const description = rest.join('\n').trim();
50
+ if (description.length > 0)
51
+ lines.push(description, '');
52
+ }
53
+ lines.push(`Source: \`${entry.sourcePath}\``, '');
54
+ lines.push(`\`\`\`${entry.language}`);
55
+ lines.push(entry.code);
56
+ lines.push('```', '');
57
+ }
58
+ }
43
59
  function renderCliScenarios(lines, model, outputDir, diagrams) {
44
60
  const scenarios = model.sequenceScenarios.filter((scenario) => scenario.kind === 'bin' && scenario.isReadme);
45
61
  if (scenarios.length === 0)
@@ -48,9 +64,8 @@ function renderCliScenarios(lines, model, outputDir, diagrams) {
48
64
  for (const scenario of scenarios) {
49
65
  lines.push('<details>');
50
66
  lines.push(`<summary>${scenario.name}</summary>`, '');
51
- if (scenario.description !== null) {
67
+ if (scenario.description !== null)
52
68
  lines.push(scenario.description, '');
53
- }
54
69
  const command = model.usage?.commands.find((item) => item.name === scenario.name);
55
70
  if (command !== undefined) {
56
71
  lines.push('```bash');
@@ -94,25 +109,24 @@ function renderConfiguration(lines, model) {
94
109
  lines.push('export default config;');
95
110
  }
96
111
  lines.push('```', '');
97
- if (config.members.length > 0) {
98
- lines.push('<details>');
99
- lines.push('<summary>Configuration options</summary>', '');
100
- lines.push('| Field | Type | Required | Default | Description |');
101
- lines.push('| --- | --- | --- | --- | --- |');
102
- for (const configMember of flattenConfigMembers(config.members)) {
103
- lines.push(`| ${escapeTableCell(configMember.path)} | \`${escapeTableCell(configMember.type)}\` | ${configMember.required ? 'yes' : 'no'} | ${renderDefault(configMember.defaultValue)} | ${escapeTableCell(configMember.description ?? '')} |`);
104
- }
105
- lines.push('', '</details>', '');
112
+ if (config.members.length === 0)
113
+ return;
114
+ lines.push('<details>');
115
+ lines.push('<summary>Configuration options</summary>', '');
116
+ lines.push('| Field | Type | Required | Default | Description |');
117
+ lines.push('| --- | --- | --- | --- | --- |');
118
+ for (const configMember of flattenConfigMembers(config.members)) {
119
+ lines.push(`| ${escapeTableCell(configMember.path)} | \`${escapeTableCell(configMember.type)}\` | ${configMember.required ? 'yes' : 'no'} | ${renderDefault(configMember.defaultValue)} | ${escapeTableCell(configMember.description ?? '')} |`);
106
120
  }
121
+ lines.push('', '</details>', '');
107
122
  }
108
123
  function renderGeneratedDocumentation(lines, outputDir, diagrams) {
109
124
  lines.push('## Generated documentation', '');
110
125
  lines.push(`- [Interactive documentation app](./${outputDir}/index.html)`);
111
126
  lines.push(`- [Public API reference](./${outputDir}/exports.md)`);
112
127
  lines.push(`- [Component registry](./${outputDir}/components.md)`);
113
- for (const diagram of diagrams) {
128
+ for (const diagram of diagrams)
114
129
  lines.push(`- [${diagram.title}](./${outputDir}/${diagram.path})`);
115
- }
116
130
  lines.push('');
117
131
  }
118
132
  function renderReadmeApi(lines, model) {
@@ -123,12 +137,10 @@ function renderReadmeApi(lines, model) {
123
137
  for (const group of groups) {
124
138
  lines.push(`### ${group.title}`, '');
125
139
  for (const item of group.items) {
126
- if (item.kind === 'component') {
140
+ if (item.kind === 'component')
127
141
  renderComponentAccordion(lines, item.component, item.exportEntry);
128
- }
129
- else {
142
+ else
130
143
  renderExportAccordion(lines, item.exportEntry);
131
- }
132
144
  }
133
145
  }
134
146
  }
@@ -205,9 +217,8 @@ function renderStructuredRows(lines, item) {
205
217
  function getStructuredColumns(item) {
206
218
  const columns = new Set();
207
219
  for (const row of item.structuredRows) {
208
- for (const column of Object.keys(row.values)) {
220
+ for (const column of Object.keys(row.values))
209
221
  columns.add(column);
210
- }
211
222
  }
212
223
  return [...columns];
213
224
  }
@@ -216,9 +227,8 @@ function formatStructuredColumnHeader(column) {
216
227
  }
217
228
  function formatStructuredCell(column, value) {
218
229
  const escaped = escapeTableCell(value);
219
- if (column === 'syntax' || column === 'name' || column === 'handler') {
230
+ if (column === 'syntax' || column === 'name' || column === 'handler')
220
231
  return `\`${escaped}\``;
221
- }
222
232
  if (value === 'true')
223
233
  return 'yes';
224
234
  if (value === 'false')
@@ -290,9 +300,8 @@ function renderExports(model) {
290
300
  lines.push(`Kind: \`${item.kind}\``);
291
301
  lines.push(`Module: \`${item.modulePath}\``);
292
302
  lines.push(`Source: \`${item.sourceLocation.filePath}:${item.sourceLocation.line}:${item.sourceLocation.column}\``, '');
293
- if (item.description) {
303
+ if (item.description)
294
304
  lines.push(item.description, '');
295
- }
296
305
  renderStructuredRows(lines, item);
297
306
  if (item.signatures.length > 0) {
298
307
  lines.push('### Signatures', '');
@@ -322,9 +331,8 @@ function renderComponents(model) {
322
331
  for (const component of model.components) {
323
332
  lines.push(`## ${component.name}`, '');
324
333
  lines.push(`Source: \`${component.sourceLocation.filePath}:${component.sourceLocation.line}:${component.sourceLocation.column}\``, '');
325
- if (component.description) {
334
+ if (component.description)
326
335
  lines.push(component.description, '');
327
- }
328
336
  if (component.exportPaths.length > 0) {
329
337
  lines.push(`Export paths: ${component.exportPaths.map((path) => `\`${path}\``).join(', ')}`, '');
330
338
  }
@@ -361,9 +369,8 @@ function flattenConfigMembers(members, prefix = '') {
361
369
  }
362
370
  function badgeLabel(model, badgePath) {
363
371
  const fileName = badgePath.split('/').pop();
364
- if (!fileName) {
372
+ if (!fileName)
365
373
  return badgePath;
366
- }
367
374
  const id = fileName.replace(/\.svg$/, '');
368
375
  const badge = model.badges.find((entry) => entry.id === id);
369
376
  return badge ? `${badge.label}: ${badge.value}` : badgePath;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ankhorage/paradox",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Deterministic documentation generator for TypeScript packages.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {