diff --git a/package-lock.json b/package-lock.json index fba79fb4..f649035f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "name": "@node-core/api-docs-tooling", "dependencies": { "commander": "^13.0.0", + "dedent": "^1.5.3", "github-slugger": "^2.0.0", "glob": "^11.0.0", "hast-util-to-string": "^3.0.1", @@ -930,6 +931,20 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", diff --git a/package.json b/package.json index 71f51fb3..88c464b3 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ }, "dependencies": { "commander": "^13.0.0", + "dedent": "^1.5.3", "github-slugger": "^2.0.0", "glob": "^11.0.0", "hast-util-to-string": "^3.0.1", diff --git a/src/generators/addon-verify/index.mjs b/src/generators/addon-verify/index.mjs index 4c9d3712..df242d74 100644 --- a/src/generators/addon-verify/index.mjs +++ b/src/generators/addon-verify/index.mjs @@ -5,18 +5,13 @@ import { join } from 'node:path'; import { visit } from 'unist-util-visit'; -import { updateFilesForBuild } from './utils/updateFilesForBuild.mjs'; +import { generateFileList } from './utils/generateFileList.mjs'; import { EXTRACT_CODE_FILENAME_COMMENT } from './constants.mjs'; - -/** - * Normalizes a section name. - * - * @param {string} sectionName Section name - * @returns {string} - */ -export function normalizeSectionName(sectionName) { - return sectionName.toLowerCase().replace(/\s/g, '_').replace(/\W/g, ''); -} +import { + generateSectionFolderName, + isBuildableSection, + normalizeSectionName, +} from './utils/section.mjs'; /** * This generator generates a file list from code blocks extracted from @@ -73,31 +68,26 @@ export default { const files = await Promise.all( 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); + .filter(([, codeBlocks]) => isBuildableSection(codeBlocks)) + .flatMap(async ([sectionName, codeBlocks], index) => { + const files = generateFileList(codeBlocks); if (output) { const normalizedSectionName = normalizeSectionName(sectionName); - const identifier = String(index + 1).padStart(2, '0'); - - const folder = `${identifier}_${normalizedSectionName}`; + const folderName = generateSectionFolderName( + normalizedSectionName, + index + ); - await mkdir(join(output, folder), { recursive: true }); + await mkdir(join(output, folderName), { recursive: true }); - newFiles.forEach(async ({ name, content }) => { - await writeFile(join(output, folder, name), content); + files.forEach(async ({ name, content }) => { + await writeFile(join(output, folderName, name), content); }); } - return newFiles; + return files; }) ); diff --git a/src/generators/addon-verify/utils/generateFileList.mjs b/src/generators/addon-verify/utils/generateFileList.mjs new file mode 100644 index 00000000..5a756d2c --- /dev/null +++ b/src/generators/addon-verify/utils/generateFileList.mjs @@ -0,0 +1,60 @@ +import dedent from 'dedent'; + +/** + * Updates JavaScript files with correct require paths for the build tree. + * + * @param {string} content Original code + * @returns {string} + */ +const updateJsRequirePaths = content => { + return dedent`'use strict'; +const common = require('../../common'); +${content.replace( + "'./build/Release/addon'", + '`./build/${common.buildType}/addon`' +)} + `; +}; + +/** + * Creates a binding.gyp configuration for C++ addon compilation. + * + * @param {string[]} sourceFiles List of source file names + * @returns {string} + */ +const createBindingGyp = sourceFiles => { + const config = { + targets: [ + { + target_name: 'addon', + sources: sourceFiles, + includes: ['../common.gypi'], + }, + ], + }; + + return JSON.stringify(config); +}; + +/** + * Generates required files list from section's code blocks for C++ addon + * compilation. + * + * @param {{name: string, content: string}[]} codeBlocks Array of code blocks + * @returns {{name: string, content: string}[]} + */ +export const generateFileList = codeBlocks => { + const files = codeBlocks.map(({ name, content }) => { + return { + name, + content: name === 'test.js' ? updateJsRequirePaths(content) : content, + }; + }); + + files.push({ + name: 'binding.gyp', + content: createBindingGyp(files.map(({ name }) => name)), + }); + + return files; +}; diff --git a/src/generators/addon-verify/utils/section.mjs b/src/generators/addon-verify/utils/section.mjs new file mode 100644 index 00000000..2b622e4b --- /dev/null +++ b/src/generators/addon-verify/utils/section.mjs @@ -0,0 +1,37 @@ +/** + * Checks if a section contains the required code blocks for building. + * A buildable section must contain at least one C++ (.cc) file and one + * JavaScript (.js) file. + * + * @param {Array<{name: string; content: string}>} codeBlocks Array of code blocks + * @returns {boolean} + */ +export const isBuildableSection = codeBlocks => { + return ( + codeBlocks.some(codeBlock => codeBlock.name.endsWith('.cc')) && + codeBlocks.some(codeBlock => codeBlock.name.endsWith('.js')) + ); +}; + +/** + * Normalizes a section name. + * + * @param {string} sectionName Original section name + * @returns {string} + */ +export const normalizeSectionName = sectionName => { + return sectionName.toLowerCase().replace(/\s/g, '_').replace(/\W/g, ''); +}; + +/** + * Generates a standardized folder name for a section. + * + * @param {string} sectionName Normalized section name + * @param {number} index Zero-based section index + * @returns {string} + */ +export const generateSectionFolderName = (sectionName, index) => { + const identifier = String(index + 1).padStart(2, '0'); + + return `${identifier}_${sectionName}`; +}; diff --git a/src/generators/addon-verify/utils/updateFilesForBuild.mjs b/src/generators/addon-verify/utils/updateFilesForBuild.mjs deleted file mode 100644 index 3ab57ac8..00000000 --- a/src/generators/addon-verify/utils/updateFilesForBuild.mjs +++ /dev/null @@ -1,41 +0,0 @@ -/** - * 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'; -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; -};