Skip to content

Commit 35a89cf

Browse files
flakey5avivkeller
authored andcommitted
feat(json): add legacy JSON generator
Co-Authored-By: flakey5 <[email protected]>
1 parent 7f2c125 commit 35a89cf

File tree

18 files changed

+942
-17
lines changed

18 files changed

+942
-17
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 & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,24 +58,31 @@ 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+
/^`?(?:(?:(?:(?:\\?_)+|\b)\w+\b|\\?\[[\w.]+\\?\])\.?)*((?:(?:(?:\\?_)+|\b)\w+\b|\\?\[[\w.]+\\?\]))\([^)]*\)`?$/i,
65+
},
6266
{ type: 'event', regex: /^Event: +`?['"]?([^'"]+)['"]?`?$/i },
6367
{
6468
type: 'class',
6569
regex:
66-
/^Class: +`?([A-Z]\w+(?:\.[A-Z]\w+)*(?: +extends +[A-Z]\w+(?:\.[A-Z]\w+)*)?)`?$/i,
70+
/^class: +`?((?:(?:(?:(?:\\?_)+|\b)\w+\b|\\?\[[\w.]+\\?\])\.?)*[A-Z]\w+)(?: +extends +(?:(?:(?:(?:\\?_)+|\b)\w+\b|\\?\[[\w.]+\\?\])\.?)*[A-Z]\w+)?`?$/i,
6771
},
6872
{
6973
type: 'ctor',
70-
regex: /^(?:Constructor: +)?`?new +([A-Z]\w+(?:\.[A-Z]\w+)*)\([^)]*\)`?$/i,
74+
regex:
75+
/^(?:Constructor: +)?`?new +((?:(?:(?:(?:\\?_)+|\b)\w+\b|\\?\[[\w.]+\\?\])\.?)*[A-Z]\w+)\([^)]*\)`?$/i,
7176
},
7277
{
7378
type: 'classMethod',
74-
regex: /^Static method: +`?([A-Z]\w+(?:\.[A-Z]\w+)*\.\w+)\([^)]*\)`?$/i,
79+
regex:
80+
/^Static method: +`?(?:(?:(?:(?:\\?_)+|\b)\w+\b|\\?\[[\w.]+\\?\])\.?)*((?:(?:(?:\\?_)+|\b)\w+\b|\\?\[[\w.]+\\?\]))\([^)]*\)`?$/i,
7581
},
7682
{
7783
type: 'property',
78-
regex: /^(?:Class property: +)?`?([A-Z]\w+(?:\.[A-Z]\w+)*\.\w+)`?$/i,
84+
regex:
85+
/^(?:Class property: +)?`?(?:(?:(?:(?:\\?_)+|\b)\w+\b|\\?\[[\w.]+\\?\])\.?)+((?:(?:(?:\\?_)+|\b)\w+\b|\\?\[[\w.]+\\?\]))`?$/i,
7986
},
8087
];
8188

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: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
'use strict';
2+
3+
import { writeFile } from 'node:fs/promises';
4+
import { join } from 'node:path';
5+
6+
/**
7+
* @typedef {Array<import('../legacy-json/types.d.ts').Section>} Input
8+
*
9+
* @type {import('../types.d.ts').GeneratorMetadata<Input, import('./types.d.ts').Output>}
10+
*/
11+
export default {
12+
name: 'legacy-json-all',
13+
14+
version: '1.0.0',
15+
16+
description:
17+
'Generates the `all.json` file from the `legacy-json` generator, which includes all the modules in one single file.',
18+
19+
dependsOn: 'legacy-json',
20+
21+
async generate(input, { output }) {
22+
/**
23+
* @type {import('./types.d.ts').Output}
24+
*/
25+
const generatedValue = {
26+
miscs: [],
27+
modules: [],
28+
classes: [],
29+
globals: [],
30+
methods: [],
31+
};
32+
33+
const propertiesToCopy = [
34+
'miscs',
35+
'modules',
36+
'classes',
37+
'globals',
38+
'methods',
39+
];
40+
41+
input.forEach(section => {
42+
// Copy the relevant properties from each section into our output
43+
propertiesToCopy.forEach(property => {
44+
if (section[property]) {
45+
generatedValue[property].push(...section[property]);
46+
}
47+
});
48+
});
49+
50+
await writeFile(
51+
join(output, 'all.json'),
52+
JSON.stringify(generatedValue),
53+
'utf8'
54+
);
55+
56+
return generatedValue;
57+
},
58+
};
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: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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 = /\((.+)\);?$/;
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+
import { groupNodesByModule } from '../../utils/generators.mjs';
6+
import buildSection 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+
async generate(input, { output }) {
31+
// This array holds all the generated values for each module
32+
const generatedValues = [];
33+
34+
const groupedModules = groupNodesByModule(input);
35+
36+
// Gets the first nodes of each module, which is considered the "head"
37+
const headNodes = input.filter(node => node.heading.depth === 1);
38+
39+
/**
40+
* @param {ApiDocMetadataEntry} head
41+
* @returns {import('./types.d.ts').ModuleSection}
42+
*/
43+
const processModuleNodes = head => {
44+
const nodes = groupedModules.get(head.api);
45+
46+
const section = buildSection(head, nodes);
47+
generatedValues.push(section);
48+
49+
return section;
50+
};
51+
52+
await Promise.all(
53+
headNodes.map(async node => {
54+
// Get the json for the node's section
55+
const section = processModuleNodes(node);
56+
57+
// Write it to the output file
58+
await writeFile(
59+
join(output, `${node.api}.json`),
60+
JSON.stringify(section),
61+
'utf8'
62+
);
63+
})
64+
);
65+
66+
return generatedValues;
67+
},
68+
};
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { ListItem } from 'mdast';
2+
3+
export interface HierarchizedEntry extends ApiDocMetadataEntry {
4+
hierarchyChildren: Array<ApiDocMetadataEntry>;
5+
}
6+
7+
export interface Meta {
8+
changes: Array<ApiDocMetadataChange>;
9+
added?: Array<string>;
10+
napiVersion?: Array<string>;
11+
deprecated?: Array<string>;
12+
removed?: Array<string>;
13+
}
14+
15+
export interface SectionBase {
16+
type: string;
17+
name: string;
18+
textRaw: string;
19+
displayName?: string;
20+
desc: string;
21+
shortDesc?: string;
22+
stability?: number;
23+
stabilityText?: string;
24+
meta?: Meta;
25+
}
26+
27+
export interface ModuleSection extends SectionBase {
28+
type: 'module';
29+
source: string;
30+
miscs?: Array<MiscSection>;
31+
modules?: Array<ModuleSection>;
32+
classes?: Array<SignatureSection>;
33+
methods?: Array<MethodSignature>;
34+
properties?: Array<PropertySection>;
35+
globals?: ModuleSection | { type: 'global' };
36+
signatures?: Array<SignatureSection>;
37+
}
38+
39+
export interface SignatureSection extends SectionBase {
40+
type: 'class' | 'ctor' | 'classMethod' | 'method';
41+
signatures: Array<MethodSignature>;
42+
}
43+
44+
export type Section =
45+
| SignatureSection
46+
| PropertySection
47+
| EventSection
48+
| MiscSection;
49+
50+
export interface Parameter {
51+
name: string;
52+
optional?: boolean;
53+
default?: string;
54+
}
55+
56+
export interface MethodSignature {
57+
params: Array<Parameter>;
58+
return?: string;
59+
}
60+
61+
export interface PropertySection extends SectionBase {
62+
type: 'property';
63+
[key: string]: string | undefined;
64+
}
65+
66+
export interface EventSection extends SectionBase {
67+
type: 'event';
68+
params: Array<ListItem>;
69+
}
70+
71+
export interface MiscSection extends SectionBase {
72+
type: 'misc';
73+
[key: string]: string | undefined;
74+
}
75+
76+
export interface List {
77+
textRaw: string;
78+
desc?: string;
79+
name: string;
80+
type?: string;
81+
default?: string;
82+
options?: List;
83+
}

0 commit comments

Comments
 (0)