@reicek/neataptic-ts 0.1.24 → 0.1.26

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.
Files changed (215) hide show
  1. package/.github/copilot-instructions.md +11 -0
  2. package/.github/skills/trace-analyzer-extension/SKILL.md +3 -3
  3. package/.github/skills/trace-analyzer-extension/assets/extension-checklist.md +1 -1
  4. package/.github/skills/trace-analyzer-extension/references/analyzer-extension-workflow.md +1 -1
  5. package/.github/skills/trace-audit-reporting/SKILL.md +3 -3
  6. package/.github/skills/trace-audit-reporting/references/trace-analysis-workflow.md +1 -1
  7. package/.github/workflows/ci.yml +3 -3
  8. package/.github/workflows/deploy-pages.yml +6 -6
  9. package/.github/workflows/manual_release_pipeline.yml +3 -3
  10. package/.github/workflows/publish.yml +18 -19
  11. package/.github/workflows/release_dispatch.yml +3 -3
  12. package/package.json +26 -20
  13. package/plans/Flappy_Bird_Folder_Documentation_Pass.md +4 -4
  14. package/plans/README.md +24 -0
  15. package/plans/Roadmap.md +62 -40
  16. package/plans/analyze-trace-solid-split.plans.md +66 -0
  17. package/plans/architecture-solid-split.plans.md +9 -15
  18. package/plans/asciiMaze-typescript-repair.plans.md +1 -1
  19. package/plans/generate-docs-solid-split.plans.md +87 -0
  20. package/plans/methods-docs.plans.md +25 -1
  21. package/plans/methods-solid-split.plans.md +14 -14
  22. package/plans/neat-docs.plans.md +9 -1
  23. package/plans/neat-test-surface-repair.plans.md +1 -1
  24. package/plans/render-docs-html-solid-split.plans.md +68 -0
  25. package/plans/src-no-explicit-any-cleanup.plans.md +1 -1
  26. package/plans/utils-docs.plans.md +6 -1
  27. package/scripts/analyze-trace/analyze-trace.analysis.ts +479 -0
  28. package/scripts/analyze-trace/analyze-trace.constants.ts +35 -0
  29. package/scripts/analyze-trace/analyze-trace.io.ts +69 -0
  30. package/scripts/analyze-trace/analyze-trace.report.ts +100 -0
  31. package/scripts/analyze-trace/analyze-trace.shared.ts +116 -0
  32. package/scripts/analyze-trace/analyze-trace.ts +45 -0
  33. package/scripts/analyze-trace/analyze-trace.types.ts +72 -0
  34. package/scripts/assets/theme.css +80 -23
  35. package/scripts/copy-examples.ts +239 -0
  36. package/scripts/export-onnx.ts +223 -0
  37. package/scripts/generate-bench-tables.ts +378 -37
  38. package/scripts/generate-docs/generate-docs.constants.ts +107 -0
  39. package/scripts/generate-docs/generate-docs.order.ts +355 -0
  40. package/scripts/generate-docs/generate-docs.state.ts +31 -0
  41. package/scripts/generate-docs/generate-docs.targets.ts +165 -0
  42. package/scripts/generate-docs/generate-docs.ts +63 -0
  43. package/scripts/generate-docs/generate-docs.types.ts +112 -0
  44. package/scripts/generate-docs/output/generate-docs.output.folder-index.utils.ts +167 -0
  45. package/scripts/generate-docs/output/generate-docs.output.ordering.utils.ts +353 -0
  46. package/scripts/generate-docs/output/generate-docs.output.readme.utils.ts +420 -0
  47. package/scripts/generate-docs/output/generate-docs.output.ts +123 -0
  48. package/scripts/generate-docs/output/generate-docs.output.warnings.utils.ts +219 -0
  49. package/scripts/generate-docs/symbols/generate-docs.symbols.collection.utils.ts +365 -0
  50. package/scripts/generate-docs/symbols/generate-docs.symbols.jsdoc.utils.ts +373 -0
  51. package/scripts/generate-docs/symbols/generate-docs.symbols.normalize.utils.ts +155 -0
  52. package/scripts/generate-docs/symbols/generate-docs.symbols.render.utils.ts +149 -0
  53. package/scripts/generate-docs/symbols/generate-docs.symbols.signature.utils.ts +289 -0
  54. package/scripts/generate-docs/symbols/generate-docs.symbols.ts +11 -0
  55. package/scripts/mermaid-cli.mjs +102 -22
  56. package/scripts/mermaid-cli.ts +736 -0
  57. package/scripts/render-docs-html/render-docs-html.assets.ts +54 -0
  58. package/scripts/render-docs-html/render-docs-html.mermaid.ts +245 -0
  59. package/scripts/{render-docs-html.sidebar.ts → render-docs-html/render-docs-html.navigation.ts} +141 -144
  60. package/scripts/render-docs-html/render-docs-html.pages.ts +333 -0
  61. package/scripts/render-docs-html/render-docs-html.shared.ts +333 -0
  62. package/scripts/render-docs-html/render-docs-html.types.ts +42 -0
  63. package/scripts/render-docs-html.ts +23 -587
  64. package/scripts/run-docs.ts +238 -0
  65. package/scripts/write-dist-docs-pkg.ts +40 -0
  66. package/src/README.md +75 -75
  67. package/src/architecture/connection/README.md +5 -5
  68. package/src/architecture/layer/README.md +508 -508
  69. package/src/architecture/network/README.md +1458 -1458
  70. package/src/architecture/network/activate/README.md +694 -694
  71. package/src/architecture/network/bootstrap/README.md +77 -77
  72. package/src/architecture/network/connect/README.md +74 -74
  73. package/src/architecture/network/deterministic/README.md +135 -135
  74. package/src/architecture/network/evolve/README.md +364 -364
  75. package/src/architecture/network/gating/README.md +130 -130
  76. package/src/architecture/network/genetic/README.md +399 -399
  77. package/src/architecture/network/mutate/README.md +897 -897
  78. package/src/architecture/network/onnx/README.md +720 -720
  79. package/src/architecture/network/onnx/export/README.md +728 -728
  80. package/src/architecture/network/onnx/export/layers/README.md +450 -450
  81. package/src/architecture/network/onnx/import/README.md +618 -618
  82. package/src/architecture/network/onnx/schema/README.md +32 -32
  83. package/src/architecture/network/prune/README.md +245 -245
  84. package/src/architecture/network/remove/README.md +135 -135
  85. package/src/architecture/network/runtime/README.md +106 -106
  86. package/src/architecture/network/serialize/README.md +542 -542
  87. package/src/architecture/network/slab/README.md +608 -608
  88. package/src/architecture/network/standalone/README.md +212 -212
  89. package/src/architecture/network/stats/README.md +84 -84
  90. package/src/architecture/network/topology/README.md +465 -465
  91. package/src/architecture/network/training/README.md +200 -200
  92. package/src/architecture/node/README.md +5 -5
  93. package/src/architecture/nodePool/README.md +14 -14
  94. package/src/methods/README.md +99 -99
  95. package/src/methods/activation/README.md +189 -189
  96. package/src/methods/cost/README.md +131 -131
  97. package/src/methods/rate/README.md +86 -86
  98. package/src/multithreading/README.md +77 -77
  99. package/src/multithreading/workers/browser/README.md +8 -8
  100. package/src/multithreading/workers/node/README.md +8 -8
  101. package/src/neat/README.md +148 -148
  102. package/src/neat/adaptive/README.md +120 -120
  103. package/src/neat/adaptive/acceptance/README.md +40 -40
  104. package/src/neat/adaptive/complexity/README.md +137 -137
  105. package/src/neat/adaptive/core/README.md +197 -197
  106. package/src/neat/adaptive/lineage/README.md +90 -90
  107. package/src/neat/adaptive/mutation/README.md +284 -284
  108. package/src/neat/compat/README.md +43 -43
  109. package/src/neat/compat/core/README.md +90 -90
  110. package/src/neat/diversity/README.md +35 -35
  111. package/src/neat/diversity/core/README.md +88 -88
  112. package/src/neat/evaluate/README.md +85 -85
  113. package/src/neat/evaluate/auto-distance/README.md +75 -75
  114. package/src/neat/evaluate/entropy-compat/README.md +37 -37
  115. package/src/neat/evaluate/entropy-sharing/README.md +43 -43
  116. package/src/neat/evaluate/fitness/README.md +23 -23
  117. package/src/neat/evaluate/novelty/README.md +120 -120
  118. package/src/neat/evaluate/objectives/README.md +17 -17
  119. package/src/neat/evaluate/shared/README.md +94 -94
  120. package/src/neat/evolve/README.md +96 -96
  121. package/src/neat/evolve/adaptive/README.md +60 -60
  122. package/src/neat/evolve/objectives/README.md +63 -63
  123. package/src/neat/evolve/offspring/README.md +56 -56
  124. package/src/neat/evolve/population/README.md +171 -171
  125. package/src/neat/evolve/runtime/README.md +79 -79
  126. package/src/neat/evolve/speciation/README.md +74 -74
  127. package/src/neat/evolve/warnings/README.md +10 -10
  128. package/src/neat/export/README.md +114 -114
  129. package/src/neat/helpers/README.md +50 -50
  130. package/src/neat/init/README.md +9 -9
  131. package/src/neat/lineage/core/README.md +101 -101
  132. package/src/neat/multiobjective/category/README.md +74 -74
  133. package/src/neat/multiobjective/crowding/README.md +272 -272
  134. package/src/neat/multiobjective/dominance/README.md +171 -171
  135. package/src/neat/multiobjective/fronts/README.md +68 -68
  136. package/src/neat/multiobjective/metrics/README.md +43 -43
  137. package/src/neat/multiobjective/objectives/README.md +31 -31
  138. package/src/neat/multiobjective/shared/README.md +27 -27
  139. package/src/neat/mutation/README.md +97 -97
  140. package/src/neat/mutation/add-conn/README.md +115 -115
  141. package/src/neat/mutation/add-node/README.md +126 -126
  142. package/src/neat/mutation/flow/README.md +149 -149
  143. package/src/neat/mutation/repair/README.md +185 -185
  144. package/src/neat/mutation/select/README.md +117 -117
  145. package/src/neat/mutation/shared/README.md +32 -32
  146. package/src/neat/objectives/README.md +25 -25
  147. package/src/neat/objectives/core/README.md +67 -67
  148. package/src/neat/pruning/README.md +40 -40
  149. package/src/neat/pruning/core/README.md +171 -171
  150. package/src/neat/pruning/facade/README.md +32 -32
  151. package/src/neat/rng/README.md +104 -104
  152. package/src/neat/rng/core/README.md +137 -137
  153. package/src/neat/rng/facade/README.md +50 -50
  154. package/src/neat/selection/README.md +111 -111
  155. package/src/neat/selection/core/README.md +227 -227
  156. package/src/neat/selection/facade/README.md +61 -61
  157. package/src/neat/shared/README.md +163 -163
  158. package/src/neat/speciation/README.md +31 -31
  159. package/src/neat/speciation/threshold/README.md +35 -35
  160. package/src/neat/species/README.md +25 -25
  161. package/src/neat/species/core/README.md +20 -20
  162. package/src/neat/species/core/shared/README.md +18 -18
  163. package/src/neat/species/history/context/README.md +22 -22
  164. package/src/neat/telemetry/accessors/README.md +58 -58
  165. package/src/neat/telemetry/exports/README.md +233 -233
  166. package/src/neat/telemetry/facade/README.md +252 -252
  167. package/src/neat/telemetry/facade/archive/README.md +57 -57
  168. package/src/neat/telemetry/facade/buffer/README.md +43 -43
  169. package/src/neat/telemetry/facade/lineage/README.md +12 -12
  170. package/src/neat/telemetry/facade/objectives/README.md +44 -44
  171. package/src/neat/telemetry/facade/runtime/README.md +26 -26
  172. package/src/neat/telemetry/facade/species/README.md +27 -27
  173. package/src/neat/telemetry/metrics/README.md +696 -696
  174. package/src/neat/telemetry/recorder/README.md +57 -57
  175. package/src/neat/telemetry/types/README.md +32 -32
  176. package/src/neat/topology-intent/README.md +75 -75
  177. package/src/utils/README.md +193 -193
  178. package/test/examples/asciiMaze/browser-entry/README.md +92 -92
  179. package/test/examples/asciiMaze/dashboardManager/README.md +109 -109
  180. package/test/examples/asciiMaze/dashboardManager/telemetry/README.md +28 -28
  181. package/test/examples/asciiMaze/evolutionEngine/README.md +1527 -1527
  182. package/test/examples/asciiMaze/mazeMovement/README.md +105 -105
  183. package/test/examples/asciiMaze/mazeMovement/finalization/README.md +16 -16
  184. package/test/examples/asciiMaze/mazeMovement/policy/README.md +57 -57
  185. package/test/examples/asciiMaze/mazeMovement/runtime/README.md +52 -52
  186. package/test/examples/asciiMaze/mazeMovement/shaping/README.md +46 -46
  187. package/test/examples/flappy_bird/browser-entry/README.md +508 -508
  188. package/test/examples/flappy_bird/browser-entry/host/README.md +101 -101
  189. package/test/examples/flappy_bird/browser-entry/host/resize/README.md +144 -144
  190. package/test/examples/flappy_bird/browser-entry/network-view/README.md +194 -194
  191. package/test/examples/flappy_bird/browser-entry/playback/README.md +278 -278
  192. package/test/examples/flappy_bird/browser-entry/playback/background/README.md +129 -129
  193. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/README.md +502 -502
  194. package/test/examples/flappy_bird/browser-entry/playback/frame-render/README.md +139 -139
  195. package/test/examples/flappy_bird/browser-entry/playback/snapshot/README.md +10 -10
  196. package/test/examples/flappy_bird/browser-entry/playback/trail/README.md +43 -43
  197. package/test/examples/flappy_bird/browser-entry/playback/worker-channel/README.md +30 -30
  198. package/test/examples/flappy_bird/browser-entry/runtime/README.md +59 -59
  199. package/test/examples/flappy_bird/browser-entry/visualization/README.md +276 -276
  200. package/test/examples/flappy_bird/browser-entry/worker-channel/README.md +16 -16
  201. package/test/examples/flappy_bird/constants/README.md +1070 -1070
  202. package/test/examples/flappy_bird/environment/README.md +22 -22
  203. package/test/examples/flappy_bird/evaluation/README.md +32 -32
  204. package/test/examples/flappy_bird/evaluation/rollout/README.md +141 -141
  205. package/test/examples/flappy_bird/flappy-evolution-worker/README.md +425 -425
  206. package/test/examples/flappy_bird/simulation-shared/README.md +170 -170
  207. package/test/examples/flappy_bird/simulation-shared/observation/README.md +109 -109
  208. package/test/examples/flappy_bird/trainer/README.md +325 -325
  209. package/test/examples/flappy_bird/trainer/evaluation/README.md +74 -74
  210. package/scripts/analyze-trace.ts +0 -590
  211. package/scripts/copy-examples.mjs +0 -114
  212. package/scripts/export-onnx.mjs +0 -86
  213. package/scripts/generate-bench-tables.mjs +0 -182
  214. package/scripts/generate-docs.ts +0 -2900
  215. package/scripts/write-dist-docs-pkg.mjs +0 -16
@@ -0,0 +1,420 @@
1
+ /*
2
+ * Builds per-directory README markdown for the output boundary.
3
+ *
4
+ * This chapter owns the readable docs narrative: promoted file summaries,
5
+ * file section ordering, symbol rendering, and the markdown block structure
6
+ * that turns collected symbols into educational folder READMEs.
7
+ */
8
+
9
+ import * as path from 'path';
10
+
11
+ import { getCachedDirectoryDocsOrderConfig } from '../generate-docs.order.js';
12
+ import type {
13
+ GenerateDocsState,
14
+ RenderedFileSummary,
15
+ RenderedSymbol,
16
+ } from '../generate-docs.types.js';
17
+ import { renderSignatureBlock } from '../symbols/generate-docs.symbols.js';
18
+ import {
19
+ resolveHiddenDirectorySymbolNames,
20
+ resolveSortedDirectoryFiles,
21
+ resolveSortedTopLevelFileSectionSymbols,
22
+ resolveVisibleDirectoryFiles,
23
+ warnForConfiguredIntroFileIfNeeded,
24
+ } from './generate-docs.output.ordering.utils.js';
25
+
26
+ /**
27
+ * Builds the README markdown for one directory.
28
+ *
29
+ * One file summary may be promoted into the directory opening so the folder
30
+ * README starts with the strongest module-level narrative instead of repeating
31
+ * the same text inside the first file section.
32
+ *
33
+ * @param state - Shared docs-generator state.
34
+ * @param relativeDirectory - Directory path relative to the target root.
35
+ * @param fileSymbolMap - Symbols grouped by file within the directory.
36
+ * @param sourceDir - Absolute source root.
37
+ * @returns Markdown README content.
38
+ */
39
+ export function buildDirectoryReadme(
40
+ state: GenerateDocsState,
41
+ relativeDirectory: string,
42
+ fileSymbolMap: Map<string, RenderedSymbol[]>,
43
+ sourceDir: string,
44
+ ): string {
45
+ const title = (relativeDirectory || path.basename(sourceDir)).replace(
46
+ /\\/g,
47
+ '/',
48
+ );
49
+ const directoryBaseName = path.basename(relativeDirectory || sourceDir);
50
+ const directoryPath =
51
+ relativeDirectory === ''
52
+ ? sourceDir
53
+ : path.join(sourceDir, relativeDirectory);
54
+ const lines = [`# ${title}`, ''];
55
+
56
+ const sortedFiles = resolveSortedDirectoryFiles(
57
+ state,
58
+ directoryPath,
59
+ fileSymbolMap,
60
+ );
61
+ const hiddenSymbolNames = resolveHiddenDirectorySymbolNames(
62
+ state,
63
+ directoryPath,
64
+ fileSymbolMap,
65
+ );
66
+ const visibleSortedFiles = resolveVisibleDirectoryFiles(
67
+ state,
68
+ directoryPath,
69
+ sortedFiles,
70
+ );
71
+
72
+ const primaryDirectoryIntro = resolvePrimaryDirectoryIntro(
73
+ state,
74
+ directoryPath,
75
+ sortedFiles,
76
+ fileSymbolMap,
77
+ directoryBaseName,
78
+ );
79
+
80
+ if (primaryDirectoryIntro?.summary.description) {
81
+ lines.push(primaryDirectoryIntro.summary.description, '');
82
+ }
83
+
84
+ if (primaryDirectoryIntro?.summary.examples?.length) {
85
+ lines.push(
86
+ primaryDirectoryIntro.summary.examples.length === 1
87
+ ? 'Example:'
88
+ : 'Examples:',
89
+ '',
90
+ );
91
+
92
+ for (const example of primaryDirectoryIntro.summary.examples) {
93
+ lines.push(example, '');
94
+ }
95
+ }
96
+
97
+ for (const filePath of visibleSortedFiles) {
98
+ renderFileReadmeSection(
99
+ state,
100
+ lines,
101
+ filePath,
102
+ fileSymbolMap,
103
+ sourceDir,
104
+ hiddenSymbolNames,
105
+ primaryDirectoryIntro?.filePath === filePath,
106
+ );
107
+ }
108
+
109
+ return `${lines.join('\n').trim()}\n`;
110
+ }
111
+
112
+ /**
113
+ * Renders the README section for a single file.
114
+ *
115
+ * @param state - Shared docs-generator state.
116
+ * @param lines - Output line buffer.
117
+ * @param filePath - Absolute source file path.
118
+ * @param fileSymbolMap - Symbols grouped by file within the directory.
119
+ * @param sourceDir - Absolute source root.
120
+ * @param hiddenSymbolNames - Hidden symbol-name set for the current directory.
121
+ * @param suppressFileSummary - Whether the file summary was already promoted above.
122
+ * @returns Nothing.
123
+ */
124
+ function renderFileReadmeSection(
125
+ state: GenerateDocsState,
126
+ lines: string[],
127
+ filePath: string,
128
+ fileSymbolMap: Map<string, RenderedSymbol[]>,
129
+ sourceDir: string,
130
+ hiddenSymbolNames: ReadonlySet<string>,
131
+ suppressFileSummary: boolean = false,
132
+ ): void {
133
+ const visibleFileSymbols = (fileSymbolMap.get(filePath) ?? []).filter(
134
+ (symbol) => !hiddenSymbolNames.has(symbol.name),
135
+ );
136
+ const fileSummarySymbol = visibleFileSymbols.find(
137
+ (symbol) => symbol.kind === 'File',
138
+ );
139
+ const nonFileSummarySymbols = visibleFileSymbols.filter(
140
+ (symbol) => symbol.kind !== 'File',
141
+ );
142
+
143
+ if (
144
+ !fileSummarySymbol?.jsdoc.description &&
145
+ !fileSummarySymbol?.jsdoc.examples?.length &&
146
+ nonFileSummarySymbols.length === 0
147
+ ) {
148
+ return;
149
+ }
150
+
151
+ const relativeFilePath = path
152
+ .relative(sourceDir, filePath)
153
+ .replace(/\\/g, '/');
154
+ lines.push(`## ${relativeFilePath}`, '');
155
+
156
+ if (!suppressFileSummary && fileSummarySymbol?.jsdoc.description) {
157
+ lines.push(fileSummarySymbol.jsdoc.description, '');
158
+ }
159
+
160
+ if (!suppressFileSummary && fileSummarySymbol?.jsdoc.examples?.length) {
161
+ lines.push(
162
+ fileSummarySymbol.jsdoc.examples.length === 1 ? 'Example:' : 'Examples:',
163
+ '',
164
+ );
165
+
166
+ for (const example of fileSummarySymbol.jsdoc.examples) {
167
+ lines.push(example, '');
168
+ }
169
+ }
170
+
171
+ const fileBaseName = path.basename(filePath, '.ts');
172
+ const sortedTopLevelCandidates = nonFileSummarySymbols
173
+ .filter((symbol) => !symbol.parent)
174
+ .toSorted((left, right) =>
175
+ compareFileSectionSymbols(left, right, fileBaseName),
176
+ );
177
+ const topLevelSymbols = resolveSortedTopLevelFileSectionSymbols(
178
+ state,
179
+ filePath,
180
+ sortedTopLevelCandidates,
181
+ );
182
+ const symbolsByParent = groupSymbolsByParent(nonFileSummarySymbols);
183
+
184
+ for (const topLevelSymbol of topLevelSymbols) {
185
+ renderSymbolBlock(lines, topLevelSymbol, 3);
186
+
187
+ const childSymbols = symbolsByParent.get(topLevelSymbol.name);
188
+ if (!childSymbols) {
189
+ continue;
190
+ }
191
+
192
+ childSymbols.sort((left, right) => left.name.localeCompare(right.name));
193
+ for (const childSymbol of childSymbols) {
194
+ renderSymbolBlock(lines, childSymbol, 4);
195
+ }
196
+ }
197
+
198
+ for (const [parentName, childSymbols] of symbolsByParent) {
199
+ if (topLevelSymbols.some((symbol) => symbol.name === parentName)) {
200
+ continue;
201
+ }
202
+
203
+ lines.push(`### ${parentName}`, '');
204
+ childSymbols.sort((left, right) => left.name.localeCompare(right.name));
205
+ for (const childSymbol of childSymbols) {
206
+ renderSymbolBlock(lines, childSymbol, 4);
207
+ }
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Resolves the file-summary block that should be promoted into the directory
213
+ * README opening.
214
+ *
215
+ * Selection priority is:
216
+ * 1. `docs.order.json` `introFile` when configured and the referenced file has
217
+ * a renderable summary,
218
+ * 2. a directory entrypoint file matching the folder name or `index.ts`,
219
+ * 3. the first remaining sorted file with a renderable summary.
220
+ *
221
+ * @param state - Shared docs-generator state.
222
+ * @param directoryPath - Absolute directory path.
223
+ * @param sortedFiles - Files already ordered for directory README rendering.
224
+ * @param fileSymbolMap - Symbols grouped by file within the directory.
225
+ * @param directoryBaseName - Base directory name used for entrypoint prioritization.
226
+ * @returns File path and summary when a promotable directory intro exists.
227
+ */
228
+ function resolvePrimaryDirectoryIntro(
229
+ state: GenerateDocsState,
230
+ directoryPath: string,
231
+ sortedFiles: readonly string[],
232
+ fileSymbolMap: Map<string, RenderedSymbol[]>,
233
+ directoryBaseName: string,
234
+ ): { filePath: string; summary: RenderedFileSummary } | undefined {
235
+ const configuredIntroFile = getCachedDirectoryDocsOrderConfig(
236
+ state,
237
+ directoryPath,
238
+ )?.config.introFile;
239
+ if (configuredIntroFile) {
240
+ warnForConfiguredIntroFileIfNeeded(
241
+ state,
242
+ directoryPath,
243
+ configuredIntroFile,
244
+ sortedFiles,
245
+ );
246
+
247
+ const configuredIntroFilePath = sortedFiles.find(
248
+ (filePath) => path.basename(filePath) === configuredIntroFile,
249
+ );
250
+ if (configuredIntroFilePath) {
251
+ const configuredIntroSummary = resolveRenderedFileSummary(
252
+ configuredIntroFilePath,
253
+ fileSymbolMap,
254
+ );
255
+
256
+ if (
257
+ configuredIntroSummary.description ||
258
+ configuredIntroSummary.examples?.length
259
+ ) {
260
+ return {
261
+ filePath: configuredIntroFilePath,
262
+ summary: configuredIntroSummary,
263
+ };
264
+ }
265
+ }
266
+ }
267
+
268
+ const prioritizedFiles = sortedFiles.toSorted((leftFile, rightFile) => {
269
+ const leftBaseName = path.basename(leftFile, '.ts');
270
+ const rightBaseName = path.basename(rightFile, '.ts');
271
+ const leftIsDirectoryEntry =
272
+ leftBaseName === directoryBaseName || leftBaseName === 'index';
273
+ const rightIsDirectoryEntry =
274
+ rightBaseName === directoryBaseName || rightBaseName === 'index';
275
+
276
+ if (leftIsDirectoryEntry !== rightIsDirectoryEntry) {
277
+ return leftIsDirectoryEntry ? -1 : 1;
278
+ }
279
+
280
+ return sortedFiles.indexOf(leftFile) - sortedFiles.indexOf(rightFile);
281
+ });
282
+
283
+ for (const filePath of prioritizedFiles) {
284
+ const summary = resolveRenderedFileSummary(filePath, fileSymbolMap);
285
+
286
+ if (summary.description || summary.examples?.length) {
287
+ return { filePath, summary };
288
+ }
289
+ }
290
+
291
+ return undefined;
292
+ }
293
+
294
+ /**
295
+ * Resolves the rendered file-summary block for one collected source file.
296
+ *
297
+ * @param filePath - Absolute source file path.
298
+ * @param fileSymbolMap - Symbols grouped by file within the directory.
299
+ * @returns Rendered file summary values when present.
300
+ */
301
+ function resolveRenderedFileSummary(
302
+ filePath: string,
303
+ fileSymbolMap: Map<string, RenderedSymbol[]>,
304
+ ): RenderedFileSummary {
305
+ const fileSummarySymbol = (fileSymbolMap.get(filePath) ?? []).find(
306
+ (renderedSymbol) => renderedSymbol.kind === 'File',
307
+ );
308
+
309
+ return {
310
+ description: fileSummarySymbol?.jsdoc.description,
311
+ examples: fileSummarySymbol?.jsdoc.examples,
312
+ };
313
+ }
314
+
315
+ /**
316
+ * Groups symbols that have parent names.
317
+ *
318
+ * @param renderedSymbols - Symbols for one file.
319
+ * @returns Parent-keyed symbol map.
320
+ */
321
+ function groupSymbolsByParent(
322
+ renderedSymbols: readonly RenderedSymbol[],
323
+ ): Map<string, RenderedSymbol[]> {
324
+ const symbolsByParent = new Map<string, RenderedSymbol[]>();
325
+
326
+ for (const renderedSymbol of renderedSymbols.filter(
327
+ (symbol) => symbol.parent,
328
+ )) {
329
+ const childSymbols = symbolsByParent.get(renderedSymbol.parent!) ?? [];
330
+ childSymbols.push(renderedSymbol);
331
+ symbolsByParent.set(renderedSymbol.parent!, childSymbols);
332
+ }
333
+
334
+ return symbolsByParent;
335
+ }
336
+
337
+ /**
338
+ * Renders one symbol block into the README line buffer.
339
+ *
340
+ * @param lines - Output line buffer.
341
+ * @param renderedSymbol - Symbol to render.
342
+ * @param headingLevel - Markdown heading level.
343
+ * @returns Nothing.
344
+ */
345
+ function renderSymbolBlock(
346
+ lines: string[],
347
+ renderedSymbol: RenderedSymbol,
348
+ headingLevel: number,
349
+ ): void {
350
+ const symbolDescription =
351
+ renderedSymbol.jsdoc.description || renderedSymbol.jsdoc.summary;
352
+
353
+ lines.push(`${'#'.repeat(headingLevel)} ${renderedSymbol.name}`);
354
+
355
+ if (renderedSymbol.signature) {
356
+ lines.push(
357
+ '',
358
+ ...renderSignatureBlock(renderedSymbol.name, renderedSymbol.signature),
359
+ );
360
+ }
361
+
362
+ if (symbolDescription) {
363
+ lines.push('', symbolDescription);
364
+ }
365
+
366
+ if (renderedSymbol.jsdoc.deprecated) {
367
+ lines.push('', `**Deprecated:** ${renderedSymbol.jsdoc.deprecated}`);
368
+ }
369
+
370
+ if (renderedSymbol.jsdoc.params?.length) {
371
+ lines.push('', 'Parameters:');
372
+ for (const parameter of renderedSymbol.jsdoc.params) {
373
+ lines.push(
374
+ `- \`${parameter.name}\`${parameter.doc ? ` - ${parameter.doc}` : ''}`,
375
+ );
376
+ }
377
+ }
378
+
379
+ if (renderedSymbol.jsdoc.returns) {
380
+ lines.push('', `Returns: ${renderedSymbol.jsdoc.returns}`);
381
+ }
382
+
383
+ if (renderedSymbol.jsdoc.examples?.length) {
384
+ lines.push(
385
+ '',
386
+ renderedSymbol.jsdoc.examples.length === 1 ? 'Example:' : 'Examples:',
387
+ );
388
+
389
+ for (const example of renderedSymbol.jsdoc.examples) {
390
+ lines.push('', example);
391
+ }
392
+ }
393
+
394
+ lines.push('');
395
+ }
396
+
397
+ /**
398
+ * Compares file-section symbols using the stable fallback order.
399
+ *
400
+ * @param left - Left rendered symbol.
401
+ * @param right - Right rendered symbol.
402
+ * @param fileBaseName - Base file name without extension.
403
+ * @returns Negative when left sorts before right.
404
+ */
405
+ function compareFileSectionSymbols(
406
+ left: RenderedSymbol,
407
+ right: RenderedSymbol,
408
+ fileBaseName: string,
409
+ ): number {
410
+ const leftIsPrimary = !left.parent && left.name === fileBaseName;
411
+ const rightIsPrimary = !right.parent && right.name === fileBaseName;
412
+ if (leftIsPrimary !== rightIsPrimary) {
413
+ return leftIsPrimary ? -1 : 1;
414
+ }
415
+
416
+ return (
417
+ (left.parent || left.name).localeCompare(right.parent || right.name) ||
418
+ left.name.localeCompare(right.name)
419
+ );
420
+ }
@@ -0,0 +1,123 @@
1
+ /*
2
+ * Public output boundary for the folderized docs generator.
3
+ *
4
+ * This root keeps the exported emitters orchestration-first while narrower
5
+ * helper chapters inside the folder own README composition, folder-index
6
+ * rendering, ordering policy, and config warning details.
7
+ */
8
+
9
+ import fs from 'fs-extra';
10
+ import * as path from 'path';
11
+
12
+ import {
13
+ DOCS_DIR,
14
+ FOLDER_INDEX_FILE_NAME,
15
+ } from '../generate-docs.constants.js';
16
+ import { loadDirectoryDocsOrderConfig } from '../generate-docs.order.js';
17
+ import type {
18
+ DirectorySymbolMap,
19
+ DocsTargetConfig,
20
+ GenerateDocsState,
21
+ } from '../generate-docs.types.js';
22
+ import { buildFolderIndexMarkdown } from './generate-docs.output.folder-index.utils.js';
23
+ import { buildDirectoryReadme } from './generate-docs.output.readme.utils.js';
24
+
25
+ /**
26
+ * Emits directory README files to both docs output and the source tree.
27
+ *
28
+ * @param state - Shared docs-generator state.
29
+ * @param directorySymbolMap - Normalized directory symbol map.
30
+ * @param target - Target configuration.
31
+ * @returns Nothing.
32
+ */
33
+ export async function emitDirectoryDocs(
34
+ state: GenerateDocsState,
35
+ directorySymbolMap: DirectorySymbolMap,
36
+ target: DocsTargetConfig,
37
+ ): Promise<void> {
38
+ for (const [directoryPath, fileSymbolMap] of directorySymbolMap) {
39
+ const relativeDirectory = path.relative(target.sourceDir, directoryPath);
40
+ if (relativeDirectory.startsWith('..')) {
41
+ continue;
42
+ }
43
+
44
+ const outputDirectory =
45
+ relativeDirectory === ''
46
+ ? target.rootDocsDir
47
+ : path.join(target.docsDir, relativeDirectory);
48
+ const sourceDirectory =
49
+ relativeDirectory === ''
50
+ ? target.sourceDir
51
+ : path.join(target.sourceDir, relativeDirectory);
52
+
53
+ await loadDirectoryDocsOrderConfig(state, sourceDirectory);
54
+
55
+ const markdown = buildDirectoryReadme(
56
+ state,
57
+ relativeDirectory,
58
+ fileSymbolMap,
59
+ target.sourceDir,
60
+ );
61
+
62
+ await fs.ensureDir(outputDirectory);
63
+ await writeFileIfChanged(path.join(outputDirectory, 'README.md'), markdown);
64
+ await writeFileIfChanged(path.join(sourceDirectory, 'README.md'), markdown);
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Emits the folder index for targets that want a docs landing page.
70
+ *
71
+ * @param state - Shared docs-generator state.
72
+ * @param directorySymbolMap - Normalized directory symbol map.
73
+ * @param target - Target configuration.
74
+ * @returns Nothing.
75
+ */
76
+ export async function emitFolderIndex(
77
+ state: GenerateDocsState,
78
+ directorySymbolMap: DirectorySymbolMap,
79
+ target: DocsTargetConfig,
80
+ ): Promise<void> {
81
+ const relativeDirectories = [...directorySymbolMap.keys()]
82
+ .map((directoryPath) => path.relative(target.sourceDir, directoryPath))
83
+ .filter((relativeDirectory) => !relativeDirectory.startsWith('..'));
84
+
85
+ const directoriesWithConfigSupport = [
86
+ target.sourceDir,
87
+ ...relativeDirectories.map((relativeDirectory) =>
88
+ path.join(target.sourceDir, relativeDirectory),
89
+ ),
90
+ ];
91
+ await Promise.all(
92
+ [...new Set(directoriesWithConfigSupport)].map((directoryPath) =>
93
+ loadDirectoryDocsOrderConfig(state, directoryPath),
94
+ ),
95
+ );
96
+
97
+ const markdown = buildFolderIndexMarkdown(state, directorySymbolMap, target);
98
+ await writeFileIfChanged(
99
+ path.join(DOCS_DIR, FOLDER_INDEX_FILE_NAME),
100
+ markdown,
101
+ );
102
+ }
103
+
104
+ /**
105
+ * Writes a file only when its content changed.
106
+ *
107
+ * @param filePath - Output file path.
108
+ * @param content - Desired file content.
109
+ * @returns Nothing.
110
+ */
111
+ async function writeFileIfChanged(
112
+ filePath: string,
113
+ content: string,
114
+ ): Promise<void> {
115
+ if (await fs.pathExists(filePath)) {
116
+ const previousContent = await fs.readFile(filePath, 'utf8');
117
+ if (previousContent === content) {
118
+ return;
119
+ }
120
+ }
121
+
122
+ await fs.writeFile(filePath, content, 'utf8');
123
+ }