Skip to content

feat: legacy json generator #142

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
68 changes: 68 additions & 0 deletions src/generators/legacy-json-all/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
'use strict';

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

/**
* This generator consolidates data from the `legacy-json` generator into a single
* JSON file (`all.json`).
*
* @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',

/**
* Generates the legacy JSON `all.json` file.
*
* @param {Input} input
* @param {Partial<GeneratorOptions>} options
*/
async generate(input, { output }) {
/**
* The consolidated output object that will contain
* combined data from all sections in the input.
*
* @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]);
}
});
});

if (output) {
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>;
}
36 changes: 36 additions & 0 deletions src/generators/legacy-json/constants.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// 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 = /\((.+)\);?$/;

// The plurals associated with each section type.
export const SECTION_TYPE_PLURALS = {
module: 'modules',
misc: 'miscs',
class: 'classes',
method: 'methods',
property: 'properties',
global: 'globals',
example: 'examples',
ctor: 'signatures',
classMethod: 'classMethods',
event: 'events',
var: 'vars',
};

// The keys to not promote when promoting children.
export const UNPROMOTED_KEYS = ['textRaw', 'name', 'type', 'desc', 'miscs'];
77 changes: 77 additions & 0 deletions src/generators/legacy-json/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
'use strict';

import { writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { groupNodesByModule } from '../../utils/generators.mjs';
import { createSectionBuilder } 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',

/**
* Generates a legacy JSON file.
*
* @param {Input} input
* @param {Partial<GeneratorOptions>} options
*/
async generate(input, { output }) {
const buildSection = createSectionBuilder();

// 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
if (output) {
await writeFile(
join(output, `${node.api}.json`),
JSON.stringify(section)
);
}
})
);

return generatedValues;
},
};
Loading
Loading