@reicek/neataptic-ts 0.1.25 → 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 (210) 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/package.json +19 -13
  8. package/plans/Flappy_Bird_Folder_Documentation_Pass.md +4 -4
  9. package/plans/README.md +24 -0
  10. package/plans/Roadmap.md +62 -40
  11. package/plans/analyze-trace-solid-split.plans.md +66 -0
  12. package/plans/architecture-solid-split.plans.md +9 -15
  13. package/plans/asciiMaze-typescript-repair.plans.md +1 -1
  14. package/plans/generate-docs-solid-split.plans.md +87 -0
  15. package/plans/methods-docs.plans.md +25 -1
  16. package/plans/methods-solid-split.plans.md +14 -14
  17. package/plans/neat-docs.plans.md +9 -1
  18. package/plans/neat-test-surface-repair.plans.md +1 -1
  19. package/plans/render-docs-html-solid-split.plans.md +68 -0
  20. package/plans/src-no-explicit-any-cleanup.plans.md +1 -1
  21. package/plans/utils-docs.plans.md +6 -1
  22. package/scripts/analyze-trace/analyze-trace.analysis.ts +479 -0
  23. package/scripts/analyze-trace/analyze-trace.constants.ts +35 -0
  24. package/scripts/analyze-trace/analyze-trace.io.ts +69 -0
  25. package/scripts/analyze-trace/analyze-trace.report.ts +100 -0
  26. package/scripts/analyze-trace/analyze-trace.shared.ts +116 -0
  27. package/scripts/analyze-trace/analyze-trace.ts +45 -0
  28. package/scripts/analyze-trace/analyze-trace.types.ts +72 -0
  29. package/scripts/assets/theme.css +80 -23
  30. package/scripts/copy-examples.ts +239 -0
  31. package/scripts/export-onnx.ts +223 -0
  32. package/scripts/generate-bench-tables.ts +378 -37
  33. package/scripts/generate-docs/generate-docs.constants.ts +107 -0
  34. package/scripts/generate-docs/generate-docs.order.ts +355 -0
  35. package/scripts/generate-docs/generate-docs.state.ts +31 -0
  36. package/scripts/generate-docs/generate-docs.targets.ts +165 -0
  37. package/scripts/generate-docs/generate-docs.ts +63 -0
  38. package/scripts/generate-docs/generate-docs.types.ts +112 -0
  39. package/scripts/generate-docs/output/generate-docs.output.folder-index.utils.ts +167 -0
  40. package/scripts/generate-docs/output/generate-docs.output.ordering.utils.ts +353 -0
  41. package/scripts/generate-docs/output/generate-docs.output.readme.utils.ts +420 -0
  42. package/scripts/generate-docs/output/generate-docs.output.ts +123 -0
  43. package/scripts/generate-docs/output/generate-docs.output.warnings.utils.ts +219 -0
  44. package/scripts/generate-docs/symbols/generate-docs.symbols.collection.utils.ts +365 -0
  45. package/scripts/generate-docs/symbols/generate-docs.symbols.jsdoc.utils.ts +373 -0
  46. package/scripts/generate-docs/symbols/generate-docs.symbols.normalize.utils.ts +155 -0
  47. package/scripts/generate-docs/symbols/generate-docs.symbols.render.utils.ts +149 -0
  48. package/scripts/generate-docs/symbols/generate-docs.symbols.signature.utils.ts +289 -0
  49. package/scripts/generate-docs/symbols/generate-docs.symbols.ts +11 -0
  50. package/scripts/mermaid-cli.mjs +102 -22
  51. package/scripts/mermaid-cli.ts +736 -0
  52. package/scripts/render-docs-html/render-docs-html.assets.ts +54 -0
  53. package/scripts/render-docs-html/render-docs-html.mermaid.ts +245 -0
  54. package/scripts/{render-docs-html.sidebar.ts → render-docs-html/render-docs-html.navigation.ts} +141 -144
  55. package/scripts/render-docs-html/render-docs-html.pages.ts +333 -0
  56. package/scripts/render-docs-html/render-docs-html.shared.ts +333 -0
  57. package/scripts/render-docs-html/render-docs-html.types.ts +42 -0
  58. package/scripts/render-docs-html.ts +23 -587
  59. package/scripts/run-docs.ts +238 -0
  60. package/scripts/write-dist-docs-pkg.ts +40 -0
  61. package/src/README.md +75 -75
  62. package/src/architecture/connection/README.md +5 -5
  63. package/src/architecture/layer/README.md +508 -508
  64. package/src/architecture/network/README.md +1458 -1458
  65. package/src/architecture/network/activate/README.md +694 -694
  66. package/src/architecture/network/bootstrap/README.md +77 -77
  67. package/src/architecture/network/connect/README.md +74 -74
  68. package/src/architecture/network/deterministic/README.md +135 -135
  69. package/src/architecture/network/evolve/README.md +364 -364
  70. package/src/architecture/network/gating/README.md +130 -130
  71. package/src/architecture/network/genetic/README.md +399 -399
  72. package/src/architecture/network/mutate/README.md +897 -897
  73. package/src/architecture/network/onnx/README.md +720 -720
  74. package/src/architecture/network/onnx/export/README.md +728 -728
  75. package/src/architecture/network/onnx/export/layers/README.md +450 -450
  76. package/src/architecture/network/onnx/import/README.md +618 -618
  77. package/src/architecture/network/onnx/schema/README.md +32 -32
  78. package/src/architecture/network/prune/README.md +245 -245
  79. package/src/architecture/network/remove/README.md +135 -135
  80. package/src/architecture/network/runtime/README.md +106 -106
  81. package/src/architecture/network/serialize/README.md +542 -542
  82. package/src/architecture/network/slab/README.md +608 -608
  83. package/src/architecture/network/standalone/README.md +212 -212
  84. package/src/architecture/network/stats/README.md +84 -84
  85. package/src/architecture/network/topology/README.md +465 -465
  86. package/src/architecture/network/training/README.md +200 -200
  87. package/src/architecture/node/README.md +5 -5
  88. package/src/architecture/nodePool/README.md +14 -14
  89. package/src/methods/README.md +99 -99
  90. package/src/methods/activation/README.md +189 -189
  91. package/src/methods/cost/README.md +131 -131
  92. package/src/methods/rate/README.md +86 -86
  93. package/src/multithreading/README.md +77 -77
  94. package/src/multithreading/workers/browser/README.md +8 -8
  95. package/src/multithreading/workers/node/README.md +8 -8
  96. package/src/neat/README.md +148 -148
  97. package/src/neat/adaptive/README.md +120 -120
  98. package/src/neat/adaptive/acceptance/README.md +40 -40
  99. package/src/neat/adaptive/complexity/README.md +137 -137
  100. package/src/neat/adaptive/core/README.md +197 -197
  101. package/src/neat/adaptive/lineage/README.md +90 -90
  102. package/src/neat/adaptive/mutation/README.md +284 -284
  103. package/src/neat/compat/README.md +43 -43
  104. package/src/neat/compat/core/README.md +90 -90
  105. package/src/neat/diversity/README.md +35 -35
  106. package/src/neat/diversity/core/README.md +88 -88
  107. package/src/neat/evaluate/README.md +85 -85
  108. package/src/neat/evaluate/auto-distance/README.md +75 -75
  109. package/src/neat/evaluate/entropy-compat/README.md +37 -37
  110. package/src/neat/evaluate/entropy-sharing/README.md +43 -43
  111. package/src/neat/evaluate/fitness/README.md +23 -23
  112. package/src/neat/evaluate/novelty/README.md +120 -120
  113. package/src/neat/evaluate/objectives/README.md +17 -17
  114. package/src/neat/evaluate/shared/README.md +94 -94
  115. package/src/neat/evolve/README.md +96 -96
  116. package/src/neat/evolve/adaptive/README.md +60 -60
  117. package/src/neat/evolve/objectives/README.md +63 -63
  118. package/src/neat/evolve/offspring/README.md +56 -56
  119. package/src/neat/evolve/population/README.md +171 -171
  120. package/src/neat/evolve/runtime/README.md +79 -79
  121. package/src/neat/evolve/speciation/README.md +74 -74
  122. package/src/neat/evolve/warnings/README.md +10 -10
  123. package/src/neat/export/README.md +114 -114
  124. package/src/neat/helpers/README.md +50 -50
  125. package/src/neat/init/README.md +9 -9
  126. package/src/neat/lineage/core/README.md +101 -101
  127. package/src/neat/multiobjective/category/README.md +74 -74
  128. package/src/neat/multiobjective/crowding/README.md +272 -272
  129. package/src/neat/multiobjective/dominance/README.md +171 -171
  130. package/src/neat/multiobjective/fronts/README.md +68 -68
  131. package/src/neat/multiobjective/metrics/README.md +43 -43
  132. package/src/neat/multiobjective/objectives/README.md +31 -31
  133. package/src/neat/multiobjective/shared/README.md +27 -27
  134. package/src/neat/mutation/README.md +97 -97
  135. package/src/neat/mutation/add-conn/README.md +115 -115
  136. package/src/neat/mutation/add-node/README.md +126 -126
  137. package/src/neat/mutation/flow/README.md +149 -149
  138. package/src/neat/mutation/repair/README.md +185 -185
  139. package/src/neat/mutation/select/README.md +117 -117
  140. package/src/neat/mutation/shared/README.md +32 -32
  141. package/src/neat/objectives/README.md +25 -25
  142. package/src/neat/objectives/core/README.md +67 -67
  143. package/src/neat/pruning/README.md +40 -40
  144. package/src/neat/pruning/core/README.md +171 -171
  145. package/src/neat/pruning/facade/README.md +32 -32
  146. package/src/neat/rng/README.md +104 -104
  147. package/src/neat/rng/core/README.md +137 -137
  148. package/src/neat/rng/facade/README.md +50 -50
  149. package/src/neat/selection/README.md +111 -111
  150. package/src/neat/selection/core/README.md +227 -227
  151. package/src/neat/selection/facade/README.md +61 -61
  152. package/src/neat/shared/README.md +163 -163
  153. package/src/neat/speciation/README.md +31 -31
  154. package/src/neat/speciation/threshold/README.md +35 -35
  155. package/src/neat/species/README.md +25 -25
  156. package/src/neat/species/core/README.md +20 -20
  157. package/src/neat/species/core/shared/README.md +18 -18
  158. package/src/neat/species/history/context/README.md +22 -22
  159. package/src/neat/telemetry/accessors/README.md +58 -58
  160. package/src/neat/telemetry/exports/README.md +233 -233
  161. package/src/neat/telemetry/facade/README.md +252 -252
  162. package/src/neat/telemetry/facade/archive/README.md +57 -57
  163. package/src/neat/telemetry/facade/buffer/README.md +43 -43
  164. package/src/neat/telemetry/facade/lineage/README.md +12 -12
  165. package/src/neat/telemetry/facade/objectives/README.md +44 -44
  166. package/src/neat/telemetry/facade/runtime/README.md +26 -26
  167. package/src/neat/telemetry/facade/species/README.md +27 -27
  168. package/src/neat/telemetry/metrics/README.md +696 -696
  169. package/src/neat/telemetry/recorder/README.md +57 -57
  170. package/src/neat/telemetry/types/README.md +32 -32
  171. package/src/neat/topology-intent/README.md +75 -75
  172. package/src/utils/README.md +193 -193
  173. package/test/examples/asciiMaze/browser-entry/README.md +92 -92
  174. package/test/examples/asciiMaze/dashboardManager/README.md +109 -109
  175. package/test/examples/asciiMaze/dashboardManager/telemetry/README.md +28 -28
  176. package/test/examples/asciiMaze/evolutionEngine/README.md +1527 -1527
  177. package/test/examples/asciiMaze/mazeMovement/README.md +105 -105
  178. package/test/examples/asciiMaze/mazeMovement/finalization/README.md +16 -16
  179. package/test/examples/asciiMaze/mazeMovement/policy/README.md +57 -57
  180. package/test/examples/asciiMaze/mazeMovement/runtime/README.md +52 -52
  181. package/test/examples/asciiMaze/mazeMovement/shaping/README.md +46 -46
  182. package/test/examples/flappy_bird/browser-entry/README.md +508 -508
  183. package/test/examples/flappy_bird/browser-entry/host/README.md +101 -101
  184. package/test/examples/flappy_bird/browser-entry/host/resize/README.md +144 -144
  185. package/test/examples/flappy_bird/browser-entry/network-view/README.md +194 -194
  186. package/test/examples/flappy_bird/browser-entry/playback/README.md +278 -278
  187. package/test/examples/flappy_bird/browser-entry/playback/background/README.md +129 -129
  188. package/test/examples/flappy_bird/browser-entry/playback/background/ground-grid/README.md +502 -502
  189. package/test/examples/flappy_bird/browser-entry/playback/frame-render/README.md +139 -139
  190. package/test/examples/flappy_bird/browser-entry/playback/snapshot/README.md +10 -10
  191. package/test/examples/flappy_bird/browser-entry/playback/trail/README.md +43 -43
  192. package/test/examples/flappy_bird/browser-entry/playback/worker-channel/README.md +30 -30
  193. package/test/examples/flappy_bird/browser-entry/runtime/README.md +59 -59
  194. package/test/examples/flappy_bird/browser-entry/visualization/README.md +276 -276
  195. package/test/examples/flappy_bird/browser-entry/worker-channel/README.md +16 -16
  196. package/test/examples/flappy_bird/constants/README.md +1070 -1070
  197. package/test/examples/flappy_bird/environment/README.md +22 -22
  198. package/test/examples/flappy_bird/evaluation/README.md +32 -32
  199. package/test/examples/flappy_bird/evaluation/rollout/README.md +141 -141
  200. package/test/examples/flappy_bird/flappy-evolution-worker/README.md +425 -425
  201. package/test/examples/flappy_bird/simulation-shared/README.md +170 -170
  202. package/test/examples/flappy_bird/simulation-shared/observation/README.md +109 -109
  203. package/test/examples/flappy_bird/trainer/README.md +325 -325
  204. package/test/examples/flappy_bird/trainer/evaluation/README.md +74 -74
  205. package/scripts/analyze-trace.ts +0 -590
  206. package/scripts/copy-examples.mjs +0 -114
  207. package/scripts/export-onnx.mjs +0 -86
  208. package/scripts/generate-bench-tables.mjs +0 -182
  209. package/scripts/generate-docs.ts +0 -2900
  210. 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
+ }