Skip to content

Commit 96355ad

Browse files
committed
feat apilinks.json generator
Closes #152 Signed-off-by: flakey5 <[email protected]>
1 parent db50bb6 commit 96355ad

File tree

15 files changed

+645
-11
lines changed

15 files changed

+645
-11
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,6 @@ Options:
3939
-o, --output <path> Specify the relative or absolute output directory
4040
-v, --version <semver> Specify the target version of Node.js, semver compliant (default: "v22.6.0")
4141
-c, --changelog <url> Specify the path (file: or https://) to the CHANGELOG.md file (default: "https://raw.githubusercontent.com/nodejs/node/HEAD/CHANGELOG.md")
42-
-t, --target [mode...] Set the processing target modes (choices: "json-simple", "legacy-html", "legacy-html-all", "man-page", "legacy-json", "legacy-json-all")
42+
-t, --target [mode...] Set the processing target modes (choices: "json-simple", "legacy-html", "legacy-html-all", "man-page", "legacy-json", "legacy-json-all", "api-links")
4343
-h, --help display help for command
4444
```

bin/cli.mjs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,22 @@ program
6868
*/
6969
const { input, output, target = [], version, changelog } = program.opts();
7070

71-
const { loadFiles } = createLoader();
72-
const { parseApiDocs } = createParser();
71+
const { loadFiles, loadJsFiles } = createLoader();
72+
const { parseApiDocs, parseJsSources } = createParser();
7373

7474
const apiDocFiles = loadFiles(input);
7575

7676
const parsedApiDocs = await parseApiDocs(apiDocFiles);
7777

78-
const { runGenerators } = createGenerator(parsedApiDocs);
78+
const sourceFiles = loadJsFiles(
79+
parsedApiDocs
80+
.map(apiDoc => apiDoc.source_link_local)
81+
.filter(path => path !== undefined && path.endsWith('.js'))
82+
);
83+
84+
const parsedJsFiles = await parseJsSources(sourceFiles);
85+
86+
const { runGenerators } = createGenerator(parsedApiDocs, parsedJsFiles);
7987

8088
// Retrieves Node.js release metadata from a given Node.js version and CHANGELOG.md file
8189
const { getAllMajors } = createNodeReleases(changelog);

package-lock.json

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"prettier": "3.3.3"
3030
},
3131
"dependencies": {
32+
"acorn": "^8.14.0",
3233
"commander": "^12.1.0",
3334
"github-slugger": "^2.0.0",
3435
"glob": "^11.0.0",

src/generators.mjs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,20 @@ import availableGenerators from './generators/index.mjs';
1919
* the final generators in the chain.
2020
*
2121
* @param {ApiDocMetadataEntry} input The parsed API doc metadata entries
22+
* @param {Array<import('acorn').Program>} parsedJsFiles
2223
*/
23-
const createGenerator = input => {
24+
const createGenerator = (input, parsedJsFiles) => {
2425
/**
2526
* We store all the registered generators to be processed
2627
* within a Record, so we can access their results at any time whenever needed
2728
* (we store the Promises of the generator outputs)
2829
*
2930
* @type {{ [K in keyof AllGenerators]: ReturnType<AllGenerators[K]['generate']> }}
3031
*/
31-
const cachedGenerators = { ast: Promise.resolve(input) };
32+
const cachedGenerators = {
33+
ast: Promise.resolve(input),
34+
'ast-js': Promise.resolve(parsedJsFiles),
35+
};
3236

3337
/**
3438
* Runs the Generator engine with the provided top-level input and the given generator options

src/generators/api-links/index.mjs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
'use strict';
2+
3+
import { basename, dirname, join } from 'node:path';
4+
import { writeFile } from 'node:fs/promises';
5+
import { getGitRepository, getGitTag } from '../../utils/git.mjs';
6+
import { extractExports } from './utils/extractExports.mjs';
7+
import { findDefinitions } from './utils/findDefinitions.mjs';
8+
9+
/**
10+
* This generator is responsible for mapping publicly accessible functions in
11+
* Node.js to their source locations in the Node.js repository.
12+
*
13+
* This is a top-level generator. It takes in the raw AST tree of the JavaScript
14+
* source files. It outputs a `apilinks.json` file into the specified output
15+
* directory.
16+
*
17+
* @typedef {Array<JsProgram>} Input
18+
*
19+
* @type {import('../types.d.ts').GeneratorMetadata<Input, Record<string, string>>}
20+
*/
21+
export default {
22+
name: 'api-links',
23+
24+
version: '1.0.0',
25+
26+
description:
27+
'Creates a mapping of publicly accessible functions to their source locations in the Node.js repository.',
28+
29+
dependsOn: 'ast-js',
30+
31+
/**
32+
* Generates the `apilinks.json` file.
33+
*
34+
* @param {Input} input
35+
* @param {Partial<GeneratorOptions>} options
36+
*/
37+
async generate(input, { output }) {
38+
/**
39+
* @type {Record<string, string>}
40+
*/
41+
const definitions = {};
42+
43+
/**
44+
* @type {string}
45+
*/
46+
let baseGithubLink;
47+
48+
input.forEach(program => {
49+
/**
50+
* Mapping of definitions to their line number
51+
* @type {Record<string, number>}
52+
* @example { 'someclass.foo', 10 }
53+
*/
54+
const nameToLineNumberMap = {};
55+
56+
const programBasename = basename(program.path, '.js');
57+
58+
const exports = extractExports(
59+
program,
60+
programBasename,
61+
nameToLineNumberMap
62+
);
63+
64+
findDefinitions(program, programBasename, nameToLineNumberMap, exports);
65+
66+
if (!baseGithubLink) {
67+
const directory = dirname(program.path);
68+
69+
const repository = getGitRepository(directory);
70+
71+
const tag = getGitTag(directory);
72+
73+
baseGithubLink = `https://github.com/${repository}/blob/${tag}`;
74+
}
75+
76+
const githubLink =
77+
`${baseGithubLink}/lib/${programBasename}.js`.replaceAll('\\', '/');
78+
79+
Object.keys(nameToLineNumberMap).forEach(key => {
80+
const lineNumber = nameToLineNumberMap[key];
81+
82+
definitions[key] = `${githubLink}#L${lineNumber}`;
83+
});
84+
});
85+
86+
if (output) {
87+
await writeFile(
88+
join(output, 'apilinks.json'),
89+
JSON.stringify(definitions)
90+
);
91+
}
92+
93+
return definitions;
94+
},
95+
};

src/generators/api-links/types.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface ProgramExports {
2+
ctors: Array<string>;
3+
identifiers: Array<string>;
4+
}

0 commit comments

Comments
 (0)