Skip to content

feat: introduce addon-verify generator #174

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 2 commits into from
Jan 13, 2025
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", "legacy-json", "legacy-json-all")
-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")
-h, --help display help for command
```
1 change: 1 addition & 0 deletions src/generators/addon-verify/constants.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const EXTRACT_CODE_FILENAME_COMMENT = /^\/\/\s+(.*\.(?:cc|h|js))[\r\n]/;
106 changes: 106 additions & 0 deletions src/generators/addon-verify/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
'use strict';

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

import { visit } from 'unist-util-visit';

import { updateFilesForBuild } from './utils/updateFilesForBuild.mjs';
import { EXTRACT_CODE_FILENAME_COMMENT } from './constants.mjs';

/**
* Normalizes a section name.
*
* @param {string} sectionName Section name
* @returns {string}
*/
export function normalizeSectionName(sectionName) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be exported?

return sectionName.toLowerCase().replace(/\s/g, '_').replace(/\W/g, '');
}

/**
* This generator generates a file list from code blocks extracted from
* `doc/api/addons.md` to facilitate C++ compilation and JavaScript runtime
* validations.
*
* @typedef {Array<ApiDocMetadataEntry>} Input
*
* @type {import('../types.d.ts').GeneratorMetadata<Input, string>}
*/
export default {
name: 'addon-verify',

version: '1.0.0',

description:
'Generates a file list from code blocks extracted from `doc/api/addons.md` to facilitate C++ compilation and JavaScript runtime validations',

dependsOn: 'ast',

/**
* Generates a file list from code blocks.
*
* @param {Input} input
* @param {Partial<GeneratorOptions>} options
*/
async generate(input, { output }) {
const sectionsCodeBlocks = input.reduce((addons, node) => {
const sectionName = node.heading.data.name;

const content = node.content;

visit(content, childNode => {
if (childNode.type === 'code') {
const filename = childNode.value.match(EXTRACT_CODE_FILENAME_COMMENT);

if (filename === null) {
return;
}

if (!addons[sectionName]) {
addons[sectionName] = [];
}

addons[sectionName].push({
name: filename[1],
content: childNode.value,
});
}
});

return addons;
}, {});

const files = await Promise.all(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be broken down to separate functions on an utility file.

Object.entries(sectionsCodeBlocks)
.filter(([, files]) => {
// Must have a .cc and a .js to be a valid test.
return (
files.some(file => file.name.endsWith('.cc')) &&
files.some(file => file.name.endsWith('.js'))
);
})
.flatMap(async ([sectionName, files], index) => {
const newFiles = updateFilesForBuild(files);

if (output) {
const normalizedSectionName = normalizeSectionName(sectionName);

const identifier = String(index + 1).padStart(2, '0');

const folder = `${identifier}_${normalizedSectionName}`;

await mkdir(join(output, folder), { recursive: true });

newFiles.forEach(async ({ name, content }) => {
await writeFile(join(output, folder, name), content);
});
}

return newFiles;
})
);

return files;
},
};
41 changes: 41 additions & 0 deletions src/generators/addon-verify/utils/updateFilesForBuild.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Updates JavaScript files with correct require paths for the build tree
* and generates a `binding.gyp` file to compile C++ code.
*
* @param {{name: string, content: string}[]} files An array of file objects
* @returns {{name: string, content: string}[]}
*/
export const updateFilesForBuild = files => {
const updatedFiles = files.map(({ name, content }) => {
if (name === 'test.js') {
content = `'use strict';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, I wonder if there is a better way of doing it!

const common = require('../../common');
${content.replace(
"'./build/Release/addon'",

'`./build/${common.buildType}/addon`'
)}
`;
}

return {
name: name,
content: content,
};
});

updatedFiles.push({
name: 'binding.gyp',
content: JSON.stringify({
targets: [
{
target_name: 'addon',
sources: files.map(({ name }) => name),
includes: ['../common.gypi'],
},
],
}),
});

return updatedFiles;
};
2 changes: 2 additions & 0 deletions src/generators/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ 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';
import addonVerify from './addon-verify/index.mjs';

export default {
'json-simple': jsonSimple,
Expand All @@ -14,4 +15,5 @@ export default {
'man-page': manPage,
'legacy-json': legacyJson,
'legacy-json-all': legacyJsonAll,
'addon-verify': addonVerify,
};
Loading