Skip to content

Commit ba071f5

Browse files
authored
feat: introduce addon-verify generator (#174)
* feat: introduce addon-verify generator * refactor: nits
1 parent c86657b commit ba071f5

File tree

5 files changed

+151
-1
lines changed

5 files changed

+151
-1
lines changed

README.md

+1-1
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", "legacy-json", "legacy-json-all")
42+
-t, --target [mode...] Set the processing target modes (choices: "json-simple", "legacy-html", "legacy-html-all", "man-page", "legacy-json", "legacy-json-all", "addon-verify")
4343
-h, --help display help for command
4444
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const EXTRACT_CODE_FILENAME_COMMENT = /^\/\/\s+(.*\.(?:cc|h|js))[\r\n]/;

src/generators/addon-verify/index.mjs

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
'use strict';
2+
3+
import { mkdir, writeFile } from 'node:fs/promises';
4+
import { join } from 'node:path';
5+
6+
import { visit } from 'unist-util-visit';
7+
8+
import { updateFilesForBuild } from './utils/updateFilesForBuild.mjs';
9+
import { EXTRACT_CODE_FILENAME_COMMENT } from './constants.mjs';
10+
11+
/**
12+
* Normalizes a section name.
13+
*
14+
* @param {string} sectionName Section name
15+
* @returns {string}
16+
*/
17+
export function normalizeSectionName(sectionName) {
18+
return sectionName.toLowerCase().replace(/\s/g, '_').replace(/\W/g, '');
19+
}
20+
21+
/**
22+
* This generator generates a file list from code blocks extracted from
23+
* `doc/api/addons.md` to facilitate C++ compilation and JavaScript runtime
24+
* validations.
25+
*
26+
* @typedef {Array<ApiDocMetadataEntry>} Input
27+
*
28+
* @type {import('../types.d.ts').GeneratorMetadata<Input, string>}
29+
*/
30+
export default {
31+
name: 'addon-verify',
32+
33+
version: '1.0.0',
34+
35+
description:
36+
'Generates a file list from code blocks extracted from `doc/api/addons.md` to facilitate C++ compilation and JavaScript runtime validations',
37+
38+
dependsOn: 'ast',
39+
40+
/**
41+
* Generates a file list from code blocks.
42+
*
43+
* @param {Input} input
44+
* @param {Partial<GeneratorOptions>} options
45+
*/
46+
async generate(input, { output }) {
47+
const sectionsCodeBlocks = input.reduce((addons, node) => {
48+
const sectionName = node.heading.data.name;
49+
50+
const content = node.content;
51+
52+
visit(content, childNode => {
53+
if (childNode.type === 'code') {
54+
const filename = childNode.value.match(EXTRACT_CODE_FILENAME_COMMENT);
55+
56+
if (filename === null) {
57+
return;
58+
}
59+
60+
if (!addons[sectionName]) {
61+
addons[sectionName] = [];
62+
}
63+
64+
addons[sectionName].push({
65+
name: filename[1],
66+
content: childNode.value,
67+
});
68+
}
69+
});
70+
71+
return addons;
72+
}, {});
73+
74+
const files = await Promise.all(
75+
Object.entries(sectionsCodeBlocks)
76+
.filter(([, files]) => {
77+
// Must have a .cc and a .js to be a valid test.
78+
return (
79+
files.some(file => file.name.endsWith('.cc')) &&
80+
files.some(file => file.name.endsWith('.js'))
81+
);
82+
})
83+
.flatMap(async ([sectionName, files], index) => {
84+
const newFiles = updateFilesForBuild(files);
85+
86+
if (output) {
87+
const normalizedSectionName = normalizeSectionName(sectionName);
88+
89+
const identifier = String(index + 1).padStart(2, '0');
90+
91+
const folder = `${identifier}_${normalizedSectionName}`;
92+
93+
await mkdir(join(output, folder), { recursive: true });
94+
95+
newFiles.forEach(async ({ name, content }) => {
96+
await writeFile(join(output, folder, name), content);
97+
});
98+
}
99+
100+
return newFiles;
101+
})
102+
);
103+
104+
return files;
105+
},
106+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Updates JavaScript files with correct require paths for the build tree
3+
* and generates a `binding.gyp` file to compile C++ code.
4+
*
5+
* @param {{name: string, content: string}[]} files An array of file objects
6+
* @returns {{name: string, content: string}[]}
7+
*/
8+
export const updateFilesForBuild = files => {
9+
const updatedFiles = files.map(({ name, content }) => {
10+
if (name === 'test.js') {
11+
content = `'use strict';
12+
const common = require('../../common');
13+
${content.replace(
14+
"'./build/Release/addon'",
15+
16+
'`./build/${common.buildType}/addon`'
17+
)}
18+
`;
19+
}
20+
21+
return {
22+
name: name,
23+
content: content,
24+
};
25+
});
26+
27+
updatedFiles.push({
28+
name: 'binding.gyp',
29+
content: JSON.stringify({
30+
targets: [
31+
{
32+
target_name: 'addon',
33+
sources: files.map(({ name }) => name),
34+
includes: ['../common.gypi'],
35+
},
36+
],
37+
}),
38+
});
39+
40+
return updatedFiles;
41+
};

src/generators/index.mjs

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import legacyHtmlAll from './legacy-html-all/index.mjs';
66
import manPage from './man-page/index.mjs';
77
import legacyJson from './legacy-json/index.mjs';
88
import legacyJsonAll from './legacy-json-all/index.mjs';
9+
import addonVerify from './addon-verify/index.mjs';
910

1011
export default {
1112
'json-simple': jsonSimple,
@@ -14,4 +15,5 @@ export default {
1415
'man-page': manPage,
1516
'legacy-json': legacyJson,
1617
'legacy-json-all': legacyJsonAll,
18+
'addon-verify': addonVerify,
1719
};

0 commit comments

Comments
 (0)