Skip to content

Commit 8f40d5c

Browse files
avivkellerflakey5ovflowd
authored
feat: legacy json generator (#142)
Co-authored-by: flakey5 <[email protected]> Co-authored-by: Claudio W <[email protected]>
1 parent 4963da2 commit 8f40d5c

21 files changed

+1136
-32
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")
42+
-t, --target [mode...] Set the processing target modes (choices: "json-simple", "legacy-html", "legacy-html-all", "man-page", "legacy-json", "legacy-json-all")
4343
-h, --help display help for command
4444
```

shiki.config.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export default {
3030
// Only register the languages that the API docs use
3131
// and override the JavaScript language with the aliases
3232
langs: [
33-
{ ...javaScriptLanguage[0], aliases: ['mjs', 'cjs', 'js'] },
33+
...httpLanguage,
3434
...jsonLanguage,
3535
...typeScriptLanguage,
3636
...shellScriptLanguage,
@@ -40,7 +40,7 @@ export default {
4040
...diffLanguage,
4141
...cLanguage,
4242
...cPlusPlusLanguage,
43-
...httpLanguage,
4443
...coffeeScriptLanguage,
44+
{ ...javaScriptLanguage[0], aliases: ['mjs', 'cjs', 'js'] },
4545
],
4646
};

src/constants.mjs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,14 @@ export const DOC_API_SLUGS_REPLACEMENTS = [
5858
// is a specific type of API Doc entry (e.g., Event, Class, Method, etc)
5959
// and to extract the inner content of said Heading to be used as the API doc entry name
6060
export const DOC_API_HEADING_TYPES = [
61-
{ type: 'method', regex: /^`?([A-Z]\w+(?:\.[A-Z]\w+)*\.\w+)\([^)]*\)`?$/i },
61+
{
62+
type: 'method',
63+
regex:
64+
// Group 1: foo[bar]()
65+
// Group 2: foo.bar()
66+
// Group 3: foobar()
67+
/^`?(?:\w*(?:(\[[^\]]+\])|(?:\.(\w+)))|(\w+))\([^)]*\)`?$/i,
68+
},
6269
{ type: 'event', regex: /^Event: +`?['"]?([^'"]+)['"]?`?$/i },
6370
{
6471
type: 'class',
@@ -71,11 +78,13 @@ export const DOC_API_HEADING_TYPES = [
7178
},
7279
{
7380
type: 'classMethod',
74-
regex: /^Static method: +`?([A-Z]\w+(?:\.[A-Z]\w+)*\.\w+)\([^)]*\)`?$/i,
81+
regex:
82+
/^Static method: +`?[A-Z]\w+(?:\.[A-Z]\w+)*(?:(\[\w+\.\w+\])|\.(\w+))\([^)]*\)`?$/i,
7583
},
7684
{
7785
type: 'property',
78-
regex: /^(?:Class property: +)?`?([A-Z]\w+(?:\.[A-Z]\w+)*\.\w+)`?$/i,
86+
regex:
87+
/^(?:Class property: +)?`?[A-Z]\w+(?:\.[A-Z]\w+)*(?:(\[\w+\.\w+\])|\.(\w+))`?$/i,
7988
},
8089
];
8190

src/generators/index.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@ import jsonSimple from './json-simple/index.mjs';
44
import legacyHtml from './legacy-html/index.mjs';
55
import legacyHtmlAll from './legacy-html-all/index.mjs';
66
import manPage from './man-page/index.mjs';
7+
import legacyJson from './legacy-json/index.mjs';
8+
import legacyJsonAll from './legacy-json-all/index.mjs';
79

810
export default {
911
'json-simple': jsonSimple,
1012
'legacy-html': legacyHtml,
1113
'legacy-html-all': legacyHtmlAll,
1214
'man-page': manPage,
15+
'legacy-json': legacyJson,
16+
'legacy-json-all': legacyJsonAll,
1317
};

src/generators/legacy-html/assets/api.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,6 @@
165165

166166
let code = '';
167167

168-
console.log(parentNode);
169-
170168
if (flavorToggle) {
171169
if (flavorToggle.checked) {
172170
code = parentNode.querySelector('.mjs').textContent;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
'use strict';
2+
3+
import { writeFile } from 'node:fs/promises';
4+
import { join } from 'node:path';
5+
6+
/**
7+
* This generator consolidates data from the `legacy-json` generator into a single
8+
* JSON file (`all.json`).
9+
*
10+
* @typedef {Array<import('../legacy-json/types.d.ts').Section>} Input
11+
*
12+
* @type {import('../types.d.ts').GeneratorMetadata<Input, import('./types.d.ts').Output>}
13+
*/
14+
export default {
15+
name: 'legacy-json-all',
16+
17+
version: '1.0.0',
18+
19+
description:
20+
'Generates the `all.json` file from the `legacy-json` generator, which includes all the modules in one single file.',
21+
22+
dependsOn: 'legacy-json',
23+
24+
/**
25+
* Generates the legacy JSON `all.json` file.
26+
*
27+
* @param {Input} input
28+
* @param {Partial<GeneratorOptions>} options
29+
*/
30+
async generate(input, { output }) {
31+
/**
32+
* The consolidated output object that will contain
33+
* combined data from all sections in the input.
34+
*
35+
* @type {import('./types.d.ts').Output}
36+
*/
37+
const generatedValue = {
38+
miscs: [],
39+
modules: [],
40+
classes: [],
41+
globals: [],
42+
methods: [],
43+
};
44+
45+
const propertiesToCopy = [
46+
'miscs',
47+
'modules',
48+
'classes',
49+
'globals',
50+
'methods',
51+
];
52+
53+
input.forEach(section => {
54+
// Copy the relevant properties from each section into our output
55+
propertiesToCopy.forEach(property => {
56+
if (section[property]) {
57+
generatedValue[property].push(...section[property]);
58+
}
59+
});
60+
});
61+
62+
if (output) {
63+
await writeFile(join(output, 'all.json'), JSON.stringify(generatedValue));
64+
}
65+
66+
return generatedValue;
67+
},
68+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import {
2+
MiscSection,
3+
Section,
4+
SignatureSection,
5+
ModuleSection,
6+
} from '../legacy-json/types';
7+
8+
export interface Output {
9+
miscs: Array<MiscSection>;
10+
modules: Array<Section>;
11+
classes: Array<SignatureSection>;
12+
globals: Array<ModuleSection | { type: 'global' }>;
13+
methods: Array<SignatureSection>;
14+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Grabs a method's return value
2+
export const RETURN_EXPRESSION = /^returns?\s*:?\s*/i;
3+
4+
// Grabs a method's name
5+
export const NAME_EXPRESSION = /^['`"]?([^'`": {]+)['`"]?\s*:?\s*/;
6+
7+
// Denotes a method's type
8+
export const TYPE_EXPRESSION = /^\{([^}]+)\}\s*/;
9+
10+
// Checks if there's a leading hyphen
11+
export const LEADING_HYPHEN = /^-\s*/;
12+
13+
// Grabs the default value if present
14+
export const DEFAULT_EXPRESSION = /\s*\*\*Default:\*\*\s*([^]+)$/i;
15+
16+
// Grabs the parameters from a method's signature
17+
// ex/ 'new buffer.Blob([sources[, options]])'.match(PARAM_EXPRESSION) === ['([sources[, options]])', '[sources[, options]]']
18+
export const PARAM_EXPRESSION = /\((.+)\);?$/;
19+
20+
// The plurals associated with each section type.
21+
export const SECTION_TYPE_PLURALS = {
22+
module: 'modules',
23+
misc: 'miscs',
24+
class: 'classes',
25+
method: 'methods',
26+
property: 'properties',
27+
global: 'globals',
28+
example: 'examples',
29+
ctor: 'signatures',
30+
classMethod: 'classMethods',
31+
event: 'events',
32+
var: 'vars',
33+
};
34+
35+
// The keys to not promote when promoting children.
36+
export const UNPROMOTED_KEYS = ['textRaw', 'name', 'type', 'desc', 'miscs'];

src/generators/legacy-json/index.mjs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
'use strict';
2+
3+
import { writeFile } from 'node:fs/promises';
4+
import { join } from 'node:path';
5+
import { groupNodesByModule } from '../../utils/generators.mjs';
6+
import { createSectionBuilder } from './utils/buildSection.mjs';
7+
8+
/**
9+
* This generator is responsible for generating the legacy JSON files for the
10+
* legacy API docs for retro-compatibility. It is to be replaced while we work
11+
* on the new schema for this file.
12+
*
13+
* This is a top-level generator, intaking the raw AST tree of the api docs.
14+
* It generates JSON files to the specified output directory given by the
15+
* config.
16+
*
17+
* @typedef {Array<ApiDocMetadataEntry>} Input
18+
*
19+
* @type {import('../types.d.ts').GeneratorMetadata<Input, import('./types.d.ts').Section[]>}
20+
*/
21+
export default {
22+
name: 'legacy-json',
23+
24+
version: '1.0.0',
25+
26+
description: 'Generates the legacy version of the JSON API docs.',
27+
28+
dependsOn: 'ast',
29+
30+
/**
31+
* Generates a legacy JSON file.
32+
*
33+
* @param {Input} input
34+
* @param {Partial<GeneratorOptions>} options
35+
*/
36+
async generate(input, { output }) {
37+
const buildSection = createSectionBuilder();
38+
39+
// This array holds all the generated values for each module
40+
const generatedValues = [];
41+
42+
const groupedModules = groupNodesByModule(input);
43+
44+
// Gets the first nodes of each module, which is considered the "head"
45+
const headNodes = input.filter(node => node.heading.depth === 1);
46+
47+
/**
48+
* @param {ApiDocMetadataEntry} head
49+
* @returns {import('./types.d.ts').ModuleSection}
50+
*/
51+
const processModuleNodes = head => {
52+
const nodes = groupedModules.get(head.api);
53+
54+
const section = buildSection(head, nodes);
55+
generatedValues.push(section);
56+
57+
return section;
58+
};
59+
60+
await Promise.all(
61+
headNodes.map(async node => {
62+
// Get the json for the node's section
63+
const section = processModuleNodes(node);
64+
65+
// Write it to the output file
66+
if (output) {
67+
await writeFile(
68+
join(output, `${node.api}.json`),
69+
JSON.stringify(section)
70+
);
71+
}
72+
})
73+
);
74+
75+
return generatedValues;
76+
},
77+
};

0 commit comments

Comments
 (0)