Skip to content

Commit 57c0d73

Browse files
committed
chore: code review
1 parent 9d59006 commit 57c0d73

File tree

25 files changed

+222
-207
lines changed

25 files changed

+222
-207
lines changed

bin/cli.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import { Command, Option } from 'commander';
66

77
import commands from './commands/index.mjs';
88
import { errorWrap } from './utils.mjs';
9-
import logger, { LogLevel } from '../src/logger/index.mjs';
9+
import { LogLevel } from '../src/logger/constants.mjs';
10+
import logger from '../src/logger/index.mjs';
1011

1112
const logLevelOption = new Option('--log-level <level>', 'Log level')
1213
.choices(Object.keys(LogLevel))

bin/commands/generate.mjs

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,14 @@ import { NODE_CHANGELOG_URL, NODE_VERSION } from '../../src/constants.mjs';
77
import { publicGenerators } from '../../src/generators/index.mjs';
88
import createGenerator from '../../src/generators.mjs';
99
import logger from '../../src/logger/index.mjs';
10+
import { parseTypeMap } from '../../src/parsers/json.mjs';
1011
import { parseChangelog, parseIndex } from '../../src/parsers/markdown.mjs';
1112
import { DEFAULT_TYPE_MAP } from '../../src/utils/parser/constants.mjs';
12-
import { loadFromURL } from '../../src/utils/parser.mjs';
1313

1414
const availableGenerators = Object.keys(publicGenerators);
1515

1616
/**
17-
* @typedef {{
18-
* input: Array<string> | string;
19-
* ignore?: Array<string> | string;
20-
* target: Array<keyof publicGenerators>;
21-
* version: string;
22-
* changelog: string;
23-
* typeMap: string;
24-
* gitRef?: string;
25-
* threads?: number;
26-
* chunkSize?: number;
27-
* }} Options
28-
*/
29-
30-
/**
31-
* @type {import('../utils.mjs').Command}
17+
* @type {import('./types').Command}
3218
*/
3319
export default {
3420
description: 'Generate API docs',
@@ -60,7 +46,7 @@ export default {
6046
},
6147
threads: {
6248
flags: ['-p', '--threads <number>'],
63-
desc: 'Number of worker threads to use (minimum: 2)',
49+
desc: 'Number of threads to use (minimum: 1)',
6450
prompt: {
6551
type: 'text',
6652
message: 'How many threads to allow',
@@ -134,7 +120,20 @@ export default {
134120
},
135121
},
136122
},
123+
137124
/**
125+
* @typedef {Object} Options
126+
* @property {Array<string>|string} input - Specifies the glob/path for input files.
127+
* @property {Array<string>|string} [ignore] - Specifies the glob/path for ignoring files.
128+
* @property {Array<keyof AvailableGenerators>} target - Specifies the generator target mode.
129+
* @property {string} version - Specifies the target Node.js version.
130+
* @property {string} changelog - Specifies the path to the Node.js CHANGELOG.md file.
131+
* @property {string} typeMap - Specifies the path to the Node.js Type Map.
132+
* @property {string} index - Specifies the path to the index document.
133+
* @property {string} [gitRef] - Git ref/commit URL.
134+
* @property {number} [threads] - Number of threads to allow.
135+
* @property {number} [chunkSize] - Number of items to process per worker thread.
136+
*
138137
* Handles the action for generating API docs
139138
* @param {Options} opts - The options to generate API docs.
140139
* @returns {Promise<void>}
@@ -144,25 +143,19 @@ export default {
144143

145144
const { runGenerators } = createGenerator();
146145

147-
const releases = await parseChangelog(opts.changelog);
148-
149-
const typeMap = JSON.parse(await loadFromURL(opts.typeMap));
150-
151-
const index = opts.index && (await parseIndex(opts.index));
152-
153146
logger.debug('Starting generation', { targets: opts.target });
154147

155148
await runGenerators({
156149
generators: opts.target,
157150
input: opts.input,
158151
output: opts.output && resolve(opts.output),
159152
version: coerce(opts.version),
160-
releases,
153+
releases: await parseChangelog(opts.changelog),
161154
gitRef: opts.gitRef,
162-
threads: Math.max(parseInt(opts.threads, 10), 2),
163-
chunkSize: parseInt(opts.chunkSize, 10),
164-
index,
165-
typeMap,
155+
threads: Math.max(parseInt(opts.threads, 10), 1),
156+
chunkSize: Math.max(parseInt(opts.chunkSize, 10), 1),
157+
index: await parseIndex(opts.index),
158+
typeMap: await parseTypeMap(opts.typeMap),
166159
});
167160
},
168161
};

bin/commands/types.d.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* Represents a command-line option for the CLI.
3+
*/
4+
export interface Option {
5+
flags: string[];
6+
desc: string;
7+
prompt?: {
8+
type: 'text' | 'confirm' | 'select' | 'multiselect';
9+
message: string;
10+
variadic?: boolean;
11+
required?: boolean;
12+
initialValue?: boolean;
13+
options?: { label: string; value: string }[];
14+
};
15+
}
16+
17+
/**
18+
* Represents a command-line subcommand
19+
*/
20+
export interface Command {
21+
options: { [key: string]: Option };
22+
name: string;
23+
description: string;
24+
action: Function;
25+
}

src/generators/ast-js/index.mjs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ const { parseJsSource } = createJsParser();
1818
* so we're only parsing the Javascript sources when we need to.
1919
*
2020
* @typedef {unknown} Input
21+
* @typedef {Array<JsProgram>} Output
2122
*
22-
* @type {GeneratorMetadata<Input, Array<JsProgram>>}
23+
* @type {GeneratorMetadata<Input, Output>}
2324
*/
2425
export default {
2526
name: 'ast-js',
@@ -34,22 +35,32 @@ export default {
3435
*
3536
* @param {string[]} inputSlice - Sliced input paths for this chunk
3637
* @param {number[]} itemIndices - Indices into the sliced array
37-
* @returns {Promise<object[]>} Parsed JS AST objects for each file
38+
* @returns {Promise<Output>} Parsed JS AST objects for each file
3839
*/
3940
async processChunk(inputSlice, itemIndices) {
4041
const filePaths = itemIndices.map(idx => inputSlice[idx]);
4142

42-
const vfiles = await Promise.all(loadFiles(filePaths));
43+
const vfilesPromises = loadFiles(filePaths);
4344

44-
return Promise.all(vfiles.map(parseJsSource));
45+
const results = [];
46+
47+
for (const vfilePromise of vfilesPromises) {
48+
const vfile = await vfilePromise;
49+
50+
const parsed = await parseJsSource(vfile);
51+
52+
results.push(parsed);
53+
}
54+
55+
return results;
4556
},
4657

4758
/**
4859
* Generates a JavaScript AST from the input files.
4960
*
5061
* @param {Input} _ - Unused (files loaded from input paths)
5162
* @param {Partial<GeneratorOptions>} options
52-
* @returns {AsyncGenerator<Array<object>>}
63+
* @returns {AsyncGenerator<Output>}
5364
*/
5465
async *generate(_, { input = [], worker }) {
5566
const source = globSync(input).filter(path => extname(path) === '.js');

src/generators/ast/index.mjs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ const remarkProcessor = getRemark();
1616
* It parallelizes the parsing across worker threads for better performance.
1717
*
1818
* @typedef {undefined} Input
19+
* @typedef {Array<ParserOutput<import('mdast').Root>>} Output
1920
*
20-
* @type {GeneratorMetadata<Input, Array<ParserOutput<import('mdast').Root>>>}
21+
* @type {GeneratorMetadata<Input, Output>}
2122
*/
2223
export default {
2324
name: 'ast',
@@ -32,28 +33,33 @@ export default {
3233
*
3334
* @param {string[]} inputSlice - Sliced input paths for this chunk
3435
* @param {number[]} itemIndices - Indices into the sliced array
35-
* @returns {Promise<Array<ParserOutput<import('mdast').Root>>>}
36+
* @returns {Promise<Output>}
3637
*/
3738
async processChunk(inputSlice, itemIndices) {
3839
const filePaths = itemIndices.map(idx => inputSlice[idx]);
3940

40-
const vfiles = await Promise.all(loadFiles(filePaths));
41+
const vfilesPromises = loadFiles(filePaths);
4142

42-
return vfiles.map(vfile => {
43-
const tree = remarkProcessor.parse(vfile);
43+
const results = [];
4444

45-
const minimalVfile = { stem: vfile.stem, basename: vfile.basename };
45+
for (const vfilePromise of vfilesPromises) {
46+
const vfile = await vfilePromise;
4647

47-
return { file: minimalVfile, tree };
48-
});
48+
results.push({
49+
tree: remarkProcessor.parse(vfile),
50+
file: { stem: vfile.stem, basename: vfile.basename },
51+
});
52+
}
53+
54+
return results;
4955
},
5056

5157
/**
5258
* Generates AST trees from markdown input files.
5359
*
5460
* @param {Input} _ - Unused (top-level generator)
5561
* @param {Partial<GeneratorOptions>} options
56-
* @returns {AsyncGenerator<Array<ParserOutput<import('mdast').Root>>>}
62+
* @returns {AsyncGenerator<Output>}
5763
*/
5864
async *generate(_, { input = [], worker }) {
5965
const files = globSync(input).filter(path => extname(path) === '.md');

src/generators/jsx-ast/index.mjs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ const remarkRecma = getRemarkRecma();
1010
* Generator for converting MDAST to JSX AST.
1111
*
1212
* @typedef {Array<ApiDocMetadataEntry>} Input
13-
* @type {GeneratorMetadata<Input, string>}
13+
* @typedef {Array<import('./utils/buildContent.mjs').JSXContent>} Output
14+
*
15+
* @type {GeneratorMetadata<Input, Output>}
1416
*/
1517
export default {
1618
name: 'jsx-ast',
@@ -30,11 +32,8 @@ export default {
3032
*
3133
* @param {Array<{head: ApiDocMetadataEntry, entries: Array<ApiDocMetadataEntry>}>} slicedInput - Pre-sliced module data
3234
* @param {number[]} itemIndices - Indices of items to process
33-
* @param {object} options - Serializable options
34-
* @param {Array<[string, string]>} options.docPages - Pre-computed doc pages for sidebar
35-
* @param {Array<ApiDocReleaseEntry>} options.releases - Release information
36-
* @param {import('semver').SemVer} options.version - Target Node.js version
37-
* @returns {Promise<Array<import('estree-jsx').Program>>} JSX AST programs for each module
35+
* @param {{ docPages: Array<[string, string]>, releases: Array<ApiDocReleaseEntry>, version: import('semver').SemVer }} options - Serializable options
36+
* @returns {Promise<Output>} JSX AST programs for each module
3837
*/
3938
async processChunk(
4039
slicedInput,
@@ -66,7 +65,6 @@ export default {
6665
*
6766
* @param {Input} input
6867
* @param {Partial<GeneratorOptions>} options
69-
* @returns {AsyncGenerator<Array<string>>}
7068
*/
7169
async *generate(input, { index, releases, version, worker }) {
7270
const groupedModules = groupNodesByModule(input);

src/generators/legacy-html-all/index.mjs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,13 @@ import { replaceTemplateValues } from '../legacy-html/utils/replaceTemplateValue
1010
import tableOfContents from '../legacy-html/utils/tableOfContents.mjs';
1111

1212
/**
13-
* @typedef {{
14-
* api: string;
15-
* added: string;
16-
* section: string;
17-
* version: string;
18-
* toc: string;
19-
* nav: string;
20-
* content: string;
21-
* }} TemplateValues
22-
*
2313
* This generator generates the legacy HTML pages of the legacy API docs
2414
* for retro-compatibility and while we are implementing the new 'react' and 'html' generators.
2515
*
2616
* This generator is a top-level generator, and it takes the raw AST tree of the API doc files
2717
* and generates the HTML files to the specified output directory from the configuration settings
2818
*
29-
* @typedef {Array<TemplateValues>} Input
19+
* @typedef {Array<import('../legacy-html/types').TemplateValues>} Input
3020
*
3121
* @type {GeneratorMetadata<Input, string>}
3222
*/

src/generators/legacy-html/index.mjs

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,6 @@ const getHeading = name => ({ data: { depth: 1, name } });
2222
const remarkRehypeProcessor = getRemarkRehypeWithShiki();
2323

2424
/**
25-
* @typedef {{
26-
* api: string;
27-
* added: string;
28-
* section: string;
29-
* version: string;
30-
* toc: string;
31-
* nav: string;
32-
* content: string;
33-
* }} TemplateValues
3425
*
3526
* This generator generates the legacy HTML pages of the legacy API docs
3627
* for retro-compatibility and while we are implementing the new 'react' and 'html' generators.
@@ -39,8 +30,9 @@ const remarkRehypeProcessor = getRemarkRehypeWithShiki();
3930
* and generates the HTML files to the specified output directory from the configuration settings
4031
*
4132
* @typedef {Array<ApiDocMetadataEntry>} Input
33+
* @typedef {Array<import('./types').TemplateValues>} Output
4234
*
43-
* @type {GeneratorMetadata<Input, Array<TemplateValues>>}
35+
* @type {GeneratorMetadata<Input, Output>}
4436
*/
4537
export default {
4638
name: 'legacy-html',
@@ -59,10 +51,10 @@ export default {
5951
* Each item is pre-grouped {head, nodes, headNodes} - no need to
6052
* recompute groupNodesByModule for every chunk.
6153
*
62-
* @param {Array<{head: ApiDocMetadataEntry, nodes: ApiDocMetadataEntry[], headNodes: ApiDocMetadataEntry[]}>} slicedInput - Pre-sliced module data
54+
* @param {Array<{ head: ApiDocMetadataEntry, nodes: Array<ApiDocMetadataEntry>, headNodes: Array<ApiDocMetadataEntry> }> } slicedInput - Pre-sliced module data
6355
* @param {number[]} itemIndices - Indices into the sliced array
64-
* @param {{version: SemVer, parsedSideNav: string}} deps - Dependencies passed from generate()
65-
* @returns {Promise<TemplateValues[]>} Template objects for each processed module
56+
* @param {{ version: SemVer, parsedSideNav: string }} deps - Dependencies passed from generate()
57+
* @returns {Promise<Output>} Template objects for each processed module
6658
*/
6759
async processChunk(slicedInput, itemIndices, { version, parsedSideNav }) {
6860
const results = [];
@@ -110,7 +102,7 @@ export default {
110102
* Generates the legacy version of the API docs in HTML
111103
* @param {Input} input
112104
* @param {Partial<GeneratorOptions>} options
113-
* @returns {AsyncGenerator<Array<TemplateValues>>}
105+
* @returns {AsyncGenerator<Output>}
114106
*/
115107
async *generate(input, { index, releases, version, output, worker }) {
116108
const baseDir = import.meta.dirname;
@@ -134,19 +126,6 @@ export default {
134126
})
135127
);
136128

137-
// Create sliced input: each item contains head + its module's entries + headNodes reference
138-
// This avoids sending all ~4900 entries to every worker and recomputing groupings
139-
const slicedInput = headNodes.map(head => ({
140-
head,
141-
nodes: groupedModules.get(head.api),
142-
headNodes,
143-
}));
144-
145-
const deps = {
146-
version,
147-
parsedSideNav: String(parsedSideNav),
148-
};
149-
150129
if (output) {
151130
// Define the source folder for API docs assets
152131
const srcAssets = join(baseDir, 'assets');
@@ -161,12 +140,18 @@ export default {
161140
await safeCopy(srcAssets, assetsFolder);
162141
}
163142

143+
// Create sliced input: each item contains head + its module's entries + headNodes reference
144+
// This avoids sending all ~4900 entries to every worker and recomputing groupings
145+
const entries = headNodes.map(head => ({
146+
head,
147+
nodes: groupedModules.get(head.api),
148+
headNodes,
149+
}));
150+
151+
const deps = { version, parsedSideNav: String(parsedSideNav) };
152+
164153
// Stream chunks as they complete - HTML files are written immediately
165-
for await (const chunkResult of worker.stream(
166-
slicedInput,
167-
slicedInput,
168-
deps
169-
)) {
154+
for await (const chunkResult of worker.stream(entries, entries, deps)) {
170155
// Write files for this chunk in the generate method (main thread)
171156
if (output) {
172157
for (const template of chunkResult) {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export interface TemplateValues {
2+
api: string;
3+
added: string;
4+
section: string;
5+
version: string;
6+
toc: string;
7+
nav: string;
8+
content: string;
9+
}

0 commit comments

Comments
 (0)