Skip to content

Commit 0ec318d

Browse files
committed
feat: json, json-all generators
Signed-off-by: flakey5 <[email protected]>
1 parent 2cff615 commit 0ec318d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+5691
-1807
lines changed

.c8rc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"**/fixtures",
66
"src/generators/legacy-html/assets",
77
"src/generators/web/ui",
8-
"**/*.d.ts"
8+
"**/*.d.ts",
9+
"scripts/"
910
]
1011
}

.github/workflows/generate.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ jobs:
106106
input: './node/doc/api/*.md'
107107
compare: file-size
108108

109+
- target: json
110+
input: './node/doc/api/*.md'
111+
109112
- target: llms-txt
110113
input: './node/doc/api/*.md'
111114
compare: file-size

npm-shrinkwrap.json

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

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@
3636
"eslint-plugin-react-x": "^2.4.0",
3737
"husky": "^9.1.7",
3838
"lint-staged": "^16.2.7",
39-
"prettier": "3.7.4"
39+
"prettier": "3.7.4",
40+
"json-schema-to-typescript": "^15.0.4",
41+
"jsonschema": "^1.5.0"
4042
},
4143
"dependencies": {
4244
"@actions/core": "^2.0.1",
@@ -57,6 +59,7 @@
5759
"globals": "^16.5.0",
5860
"hast-util-to-string": "^3.0.1",
5961
"hastscript": "^9.0.1",
62+
"jsonc-parser": "^3.3.1",
6063
"lightningcss": "^1.30.2",
6164
"mdast-util-slice-markdown": "^2.0.1",
6265
"piscina": "^5.1.4",

scripts/generate-json-types.mjs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Generates the typedefs for the JSON generator from the JSON schema
5+
*/
6+
7+
import { readFile, writeFile } from 'node:fs/promises';
8+
import { join } from 'node:path';
9+
10+
import { compile } from 'json-schema-to-typescript';
11+
import { parse } from 'jsonc-parser';
12+
13+
const GENERATOR_DIR = join(
14+
import.meta.dirname,
15+
'..',
16+
'src',
17+
'generators',
18+
'json'
19+
);
20+
21+
const SCHEMA_PATH = join(GENERATOR_DIR, 'schema.jsonc');
22+
const TYPES_PATH = join(GENERATOR_DIR, 'generated.d.ts');
23+
24+
// Read the contents of the JSON schema
25+
const schemaString = await readFile(SCHEMA_PATH, 'utf8');
26+
27+
// Parse the JSON schema into an object
28+
const schema = await parse(schemaString);
29+
30+
// Compile the the JSON schema into TypeScript typedefs
31+
const typeDefs = await compile(schema, 'ApiDocSchema');
32+
33+
// Write the types to the expected output path
34+
await writeFile(TYPES_PATH, typeDefs);

src/generators/index.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import addonVerify from './addon-verify/index.mjs';
44
import apiLinks from './api-links/index.mjs';
55
import ast from './ast/index.mjs';
66
import astJs from './ast-js/index.mjs';
7+
import json from './json/index.mjs';
8+
import jsonAll from './json-all/index.mjs';
79
import jsonSimple from './json-simple/index.mjs';
810
import jsxAst from './jsx-ast/index.mjs';
911
import legacyHtml from './legacy-html/index.mjs';
@@ -30,6 +32,8 @@ export const publicGenerators = {
3032
'llms-txt': llmsTxt,
3133
sitemap,
3234
web,
35+
json,
36+
'json-all': jsonAll,
3337
};
3438

3539
// These ones are special since they don't produce standard output,
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import assert from 'node:assert';
2+
import { join } from 'node:path';
3+
import { test } from 'node:test';
4+
5+
import { Validator } from 'jsonschema';
6+
import { SemVer } from 'semver';
7+
8+
import { BASE_URL } from '../../../constants.mjs';
9+
import createGenerator from '../../../generators.mjs';
10+
import createMarkdownLoader from '../../../loaders/markdown.mjs';
11+
import createMarkdownParser from '../../../parsers/markdown.mjs';
12+
import { SCHEMA_FILENAME } from '../constants.mjs';
13+
import jsonAll from '../index.mjs';
14+
import { generateJsonSchema } from '../util/generateJsonSchema.mjs';
15+
16+
const FIXTURES_DIR = join(
17+
import.meta.dirname,
18+
'..',
19+
'json',
20+
'__tests__',
21+
'fixtures'
22+
);
23+
24+
const loader = createMarkdownLoader();
25+
const parser = createMarkdownParser();
26+
27+
test('generator output complies with json schema', async () => {
28+
const validator = new Validator();
29+
30+
const version = 'v1.2.3';
31+
32+
/**
33+
* @type {object}
34+
*/
35+
const schema = generateJsonSchema(version);
36+
37+
const input = [
38+
join(FIXTURES_DIR, 'text-doc.md'),
39+
join(FIXTURES_DIR, 'module.md'),
40+
];
41+
const files = await loader.loadFiles(input);
42+
const docs = await parser.parseApiDocs(files);
43+
44+
const { runGenerators } = createGenerator(docs);
45+
46+
const result = await runGenerators({
47+
generators: ['json-all'],
48+
input,
49+
output: undefined,
50+
version: new SemVer('v1.2.3'),
51+
releases: [],
52+
gitRef: 'a'.repeat(40),
53+
threads: 1,
54+
typeMap: {},
55+
});
56+
57+
assert.ok(validator.validate(result, schema).valid);
58+
});
59+
60+
test('combines json correctly', async () => {
61+
const version = 'v1.2.3';
62+
63+
/**
64+
* @type {Array<import('../../json/generated.d.ts').NodeJsAPIDocumentationSchema>}
65+
*/
66+
const jsonOutput = [
67+
{
68+
$schema: `https://nodejs.org/docs/${version}/api/node-doc-schema.json`,
69+
source: 'doc/api/some-module.md',
70+
type: 'module',
71+
'@name': 'Some Module',
72+
'@module': 'node:module',
73+
classes: [],
74+
},
75+
{
76+
$schema: `https://nodejs.org/docs/${version}/api/node-doc-schema.json`,
77+
source: 'doc/api/some-module.md',
78+
type: 'text',
79+
'@name': 'Some Module',
80+
description: 'asdf',
81+
text: [
82+
{
83+
type: 'text',
84+
'@name': 'asdf',
85+
description: 'bla bla bla',
86+
},
87+
],
88+
},
89+
];
90+
91+
const result = await jsonAll.generate(jsonOutput, {
92+
version: new SemVer(version),
93+
});
94+
95+
assert.deepStrictEqual(result, {
96+
$schema: `${BASE_URL}docs/${version}/api/${SCHEMA_FILENAME}`,
97+
modules: [
98+
{
99+
type: 'module',
100+
'@name': 'Some Module',
101+
'@module': 'node:module',
102+
classes: [],
103+
},
104+
],
105+
text: [
106+
{
107+
type: 'text',
108+
'@name': 'Some Module',
109+
description: 'asdf',
110+
text: [
111+
{
112+
type: 'text',
113+
'@name': 'asdf',
114+
description: 'bla bla bla',
115+
},
116+
],
117+
},
118+
],
119+
});
120+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const SCHEMA_FILENAME = 'node-doc-all-schema.json';

src/generators/json-all/index.mjs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
'use strict';
2+
3+
import { writeFile } from 'node:fs/promises';
4+
import { join } from 'node:path';
5+
6+
import { SCHEMA_FILENAME } from './constants.mjs';
7+
import { BASE_URL } from '../../constants.mjs';
8+
import { generateJsonSchema } from './util/generateJsonSchema.mjs';
9+
10+
/**
11+
* This generator is responsible for collecting the JSON output generated by
12+
* the `json` generator into a single JSON file.
13+
*
14+
* @typedef {Array<ApiDocMetadataEntry>} Input
15+
*
16+
* @type {GeneratorMetadata<Input, object>}
17+
*/
18+
export default {
19+
name: 'json-all',
20+
21+
// This should be kept in sync with the JSON schema version for this
22+
// generator AND the `json` generator
23+
version: '2.0.0',
24+
25+
description:
26+
'This generator is responsible for collecting the JSON output generated by the `json` generator into a single JSON file.',
27+
28+
dependsOn: 'json',
29+
30+
/**
31+
* Generates a JSON file.
32+
*
33+
* @param {Input} input
34+
* @param {Partial<GeneratorOptions>} param1
35+
* @returns {Promise<object>}
36+
*/
37+
async generate(input, { version, output }) {
38+
const versionString = `v${version.toString()}`;
39+
40+
const generatedValue = {
41+
$schema: `${BASE_URL}docs/${versionString}/api/${SCHEMA_FILENAME}`,
42+
modules: [],
43+
text: [],
44+
};
45+
46+
const propertiesToIgnore = ['$schema', 'source'];
47+
48+
input.forEach(section => {
49+
const copiedSection = {};
50+
51+
Object.keys(section).forEach(key => {
52+
if (!propertiesToIgnore.includes(key)) {
53+
copiedSection[key] = section[key];
54+
}
55+
});
56+
57+
switch (section.type) {
58+
case 'module':
59+
generatedValue.modules.push(copiedSection);
60+
break;
61+
case 'text':
62+
generatedValue.text.push(copiedSection);
63+
break;
64+
default:
65+
throw new TypeError(`unsupported root section type ${section.type}`);
66+
}
67+
});
68+
69+
if (output) {
70+
const schema = generateJsonSchema(versionString);
71+
72+
// Write the parsed JSON schema to the output directory
73+
await writeFile(join(output, SCHEMA_FILENAME), JSON.stringify(schema));
74+
}
75+
76+
return generatedValue;
77+
},
78+
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use strict';
2+
3+
import { BASE_URL } from '../../../constants.mjs';
4+
import { SCHEMA_FILENAME } from '../constants.mjs';
5+
import jsonAll from '../index.mjs';
6+
7+
/**
8+
* @param {string} version
9+
*/
10+
export function generateJsonSchema(version) {
11+
const jsonSchemaUrl = `${BASE_URL}/docs/${version}/api/${SCHEMA_FILENAME}`;
12+
13+
return {
14+
$schema: 'http://json-schema.org/draft-07/schema#',
15+
$id: `nodejs-api-doc-all@v${jsonAll.version}`,
16+
title: 'Node.js API Documentation Schema (All)',
17+
readOnly: true,
18+
19+
properties: {
20+
modules: {
21+
type: 'array',
22+
items: { $ref: `${jsonSchemaUrl}/#/definitions/Module` },
23+
},
24+
text: {
25+
type: 'array',
26+
items: { $ref: `${jsonSchemaUrl}/#/definitions/Text` },
27+
},
28+
},
29+
};
30+
}

0 commit comments

Comments
 (0)