Skip to content

Commit

Permalink
feat(json): add legacy JSON generator
Browse files Browse the repository at this point in the history
Co-Authored-By: flakey5 <[email protected]>
  • Loading branch information
flakey5 authored and RedYetiDev committed Nov 16, 2024
1 parent fb68333 commit 3a4b27b
Show file tree
Hide file tree
Showing 20 changed files with 920 additions and 30 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ Options:
-o, --output <path> Specify the relative or absolute output directory
-v, --version <semver> Specify the target version of Node.js, semver compliant (default: "v22.6.0")
-c, --changelog <url> Specify the path (file: or https://) to the CHANGELOG.md file (default: "https://raw.githubusercontent.com/nodejs/node/HEAD/CHANGELOG.md")
-t, --target [mode...] Set the processing target modes (choices: "json-simple", "legacy-html", "legacy-html-all", "man-page")
-t, --target [mode...] Set the processing target modes (choices: "json-simple", "legacy-html", "legacy-html-all", "man-page", "legacy-json", "legacy-json-all")
-h, --help display help for command
```
4 changes: 2 additions & 2 deletions shiki.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default {
// Only register the languages that the API docs use
// and override the JavaScript language with the aliases
langs: [
{ ...javaScriptLanguage[0], aliases: ['mjs', 'cjs', 'js'] },
...httpLanguage,
...jsonLanguage,
...typeScriptLanguage,
...shellScriptLanguage,
Expand All @@ -40,7 +40,7 @@ export default {
...diffLanguage,
...cLanguage,
...cPlusPlusLanguage,
...httpLanguage,
...coffeeScriptLanguage,
{ ...javaScriptLanguage[0], aliases: ['mjs', 'cjs', 'js'] },
],
};
15 changes: 12 additions & 3 deletions src/constants.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,14 @@ export const DOC_API_SLUGS_REPLACEMENTS = [
// is a specific type of API Doc entry (e.g., Event, Class, Method, etc)
// and to extract the inner content of said Heading to be used as the API doc entry name
export const DOC_API_HEADING_TYPES = [
{ type: 'method', regex: /^`?([A-Z]\w+(?:\.[A-Z]\w+)*\.\w+)\([^)]*\)`?$/i },
{
type: 'method',
regex:
// Group 1: foo[bar]()
// Group 2: foo.bar()
// Group 3: foobar()
/^`?(?:\w*(?:(\[[^\]]+\])|(?:\.(\w+)))|(\w+))\([^)]*\)`?$/i,
},
{ type: 'event', regex: /^Event: +`?['"]?([^'"]+)['"]?`?$/i },
{
type: 'class',
Expand All @@ -71,11 +78,13 @@ export const DOC_API_HEADING_TYPES = [
},
{
type: 'classMethod',
regex: /^Static method: +`?([A-Z]\w+(?:\.[A-Z]\w+)*\.\w+)\([^)]*\)`?$/i,
regex:
/^Static method: +`?[A-Z]\w+(?:\.[A-Z]\w+)*(?:(\[\w+\.\w+\])|\.(\w+))\([^)]*\)`?$/i,
},
{
type: 'property',
regex: /^(?:Class property: +)?`?([A-Z]\w+(?:\.[A-Z]\w+)*\.\w+)`?$/i,
regex:
/^(?:Class property: +)?`?[A-Z]\w+(?:\.[A-Z]\w+)*(?:(\[\w+\.\w+\])|\.(\w+))`?$/i,
},
];

Expand Down
4 changes: 4 additions & 0 deletions src/generators/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import jsonSimple from './json-simple/index.mjs';
import legacyHtml from './legacy-html/index.mjs';
import legacyHtmlAll from './legacy-html-all/index.mjs';
import manPage from './man-page/index.mjs';
import legacyJson from './legacy-json/index.mjs';
import legacyJsonAll from './legacy-json-all/index.mjs';

export default {
'json-simple': jsonSimple,
'legacy-html': legacyHtml,
'legacy-html-all': legacyHtmlAll,
'man-page': manPage,
'legacy-json': legacyJson,
'legacy-json-all': legacyJsonAll,
};
2 changes: 0 additions & 2 deletions src/generators/legacy-html/assets/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,6 @@

let code = '';

console.log(parentNode);

if (flavorToggle) {
if (flavorToggle.checked) {
code = parentNode.querySelector('.mjs').textContent;
Expand Down
54 changes: 54 additions & 0 deletions src/generators/legacy-json-all/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use strict';

import { writeFile } from 'node:fs/promises';
import { join } from 'node:path';

/**
* @typedef {Array<import('../legacy-json/types.d.ts').Section>} Input
*
* @type {import('../types.d.ts').GeneratorMetadata<Input, import('./types.d.ts').Output>}
*/
export default {
name: 'legacy-json-all',

version: '1.0.0',

description:
'Generates the `all.json` file from the `legacy-json` generator, which includes all the modules in one single file.',

dependsOn: 'legacy-json',

async generate(input, { output }) {
/**
* @type {import('./types.d.ts').Output}
*/
const generatedValue = {
miscs: [],
modules: [],
classes: [],
globals: [],
methods: [],
};

const propertiesToCopy = [
'miscs',
'modules',
'classes',
'globals',
'methods',
];

input.forEach(section => {
// Copy the relevant properties from each section into our output
propertiesToCopy.forEach(property => {
if (section[property]) {
generatedValue[property].push(...section[property]);
}
});
});

await writeFile(join(output, 'all.json'), JSON.stringify(generatedValue));

return generatedValue;
},
};
14 changes: 14 additions & 0 deletions src/generators/legacy-json-all/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {
MiscSection,
Section,
SignatureSection,
ModuleSection,
} from '../legacy-json/types';

export interface Output {
miscs: Array<MiscSection>;
modules: Array<Section>;
classes: Array<SignatureSection>;
globals: Array<ModuleSection | { type: 'global' }>;
methods: Array<SignatureSection>;
}
18 changes: 18 additions & 0 deletions src/generators/legacy-json/constants.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Grabs a method's return value
export const RETURN_EXPRESSION = /^returns?\s*:?\s*/i;

// Grabs a method's name
export const NAME_EXPRESSION = /^['`"]?([^'`": {]+)['`"]?\s*:?\s*/;

// Denotes a method's type
export const TYPE_EXPRESSION = /^\{([^}]+)\}\s*/;

// Checks if there's a leading hyphen
export const LEADING_HYPHEN = /^-\s*/;

// Grabs the default value if present
export const DEFAULT_EXPRESSION = /\s*\*\*Default:\*\*\s*([^]+)$/i;

// Grabs the parameters from a method's signature
// ex/ 'new buffer.Blob([sources[, options]])'.match(PARAM_EXPRESSION) === ['([sources[, options]])', '[sources[, options]]']
export const PARAM_EXPRESSION = /\((.+)\);?$/;
67 changes: 67 additions & 0 deletions src/generators/legacy-json/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
'use strict';

import { writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { groupNodesByModule } from '../../utils/generators.mjs';
import buildSection from './utils/buildSection.mjs';

/**
* This generator is responsible for generating the legacy JSON files for the
* legacy API docs for retro-compatibility. It is to be replaced while we work
* on the new schema for this file.
*
* This is a top-level generator, intaking the raw AST tree of the api docs.
* It generates JSON files to the specified output directory given by the
* config.
*
* @typedef {Array<ApiDocMetadataEntry>} Input
*
* @type {import('../types.d.ts').GeneratorMetadata<Input, import('./types.d.ts').Section[]>}
*/
export default {
name: 'legacy-json',

version: '1.0.0',

description: 'Generates the legacy version of the JSON API docs.',

dependsOn: 'ast',

async generate(input, { output }) {
// This array holds all the generated values for each module
const generatedValues = [];

const groupedModules = groupNodesByModule(input);

// Gets the first nodes of each module, which is considered the "head"
const headNodes = input.filter(node => node.heading.depth === 1);

/**
* @param {ApiDocMetadataEntry} head
* @returns {import('./types.d.ts').ModuleSection}
*/
const processModuleNodes = head => {
const nodes = groupedModules.get(head.api);

const section = buildSection(head, nodes);
generatedValues.push(section);

return section;
};

await Promise.all(
headNodes.map(async node => {
// Get the json for the node's section
const section = processModuleNodes(node);

// Write it to the output file
await writeFile(
join(output, `${node.api}.json`),
JSON.stringify(section)
);
})
);

return generatedValues;
},
};
83 changes: 83 additions & 0 deletions src/generators/legacy-json/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { ListItem } from 'mdast';

export interface HierarchizedEntry extends ApiDocMetadataEntry {
hierarchyChildren: Array<ApiDocMetadataEntry>;
}

export interface Meta {
changes: Array<ApiDocMetadataChange>;
added?: Array<string>;
napiVersion?: Array<string>;
deprecated?: Array<string>;
removed?: Array<string>;
}

export interface SectionBase {
type: string;
name: string;
textRaw: string;
displayName?: string;
desc: string;
shortDesc?: string;
stability?: number;
stabilityText?: string;
meta?: Meta;
}

export interface ModuleSection extends SectionBase {
type: 'module';
source: string;
miscs?: Array<MiscSection>;
modules?: Array<ModuleSection>;
classes?: Array<SignatureSection>;
methods?: Array<MethodSignature>;
properties?: Array<PropertySection>;
globals?: ModuleSection | { type: 'global' };
signatures?: Array<SignatureSection>;
}

export interface SignatureSection extends SectionBase {
type: 'class' | 'ctor' | 'classMethod' | 'method';
signatures: Array<MethodSignature>;
}

export type Section =
| SignatureSection
| PropertySection
| EventSection
| MiscSection;

export interface Parameter {
name: string;
optional?: boolean;
default?: string;
}

export interface MethodSignature {
params: Array<Parameter>;
return?: string;
}

export interface PropertySection extends SectionBase {
type: 'property';
[key: string]: string | undefined;
}

export interface EventSection extends SectionBase {
type: 'event';
params: Array<ListItem>;
}

export interface MiscSection extends SectionBase {
type: 'misc';
[key: string]: string | undefined;
}

export interface List {
textRaw: string;
desc?: string;
name: string;
type?: string;
default?: string;
options?: List;
}
Loading

0 comments on commit 3a4b27b

Please sign in to comment.