1// doc/api/addons.md has a bunch of code. Extract it for verification 2// that the C++ code compiles and the js code runs. 3// Add .gyp files which will be used to compile the C++ code. 4// Modify the require paths in the js code to pull from the build tree. 5// Triggered from the build-addons target in the Makefile and vcbuild.bat. 6 7import { mkdir, writeFile } from 'fs/promises'; 8 9import gfm from 'remark-gfm'; 10import remarkParse from 'remark-parse'; 11import { toVFile } from 'to-vfile'; 12import unified from 'unified'; 13 14const rootDir = new URL('../../', import.meta.url); 15const doc = new URL('./doc/api/addons.md', rootDir); 16const verifyDir = new URL('./test/addons/', rootDir); 17 18const file = toVFile.readSync(doc, 'utf8'); 19const tree = unified().use(remarkParse).use(gfm).parse(file); 20const addons = {}; 21let id = 0; 22let currentHeader; 23 24const validNames = /^\/\/\s+(.*\.(?:cc|h|js))[\r\n]/; 25tree.children.forEach((node) => { 26 if (node.type === 'heading') { 27 currentHeader = file.value.slice( 28 node.children[0].position.start.offset, 29 node.position.end.offset); 30 addons[currentHeader] = { files: {} }; 31 } else if (node.type === 'code') { 32 const match = node.value.match(validNames); 33 if (match !== null) { 34 addons[currentHeader].files[match[1]] = node.value; 35 } 36 } 37}); 38 39await Promise.all( 40 Object.keys(addons).flatMap( 41 (header) => verifyFiles(addons[header].files, header) 42 )); 43 44function verifyFiles(files, blockName) { 45 const fileNames = Object.keys(files); 46 47 // Must have a .cc and a .js to be a valid test. 48 if (!fileNames.some((name) => name.endsWith('.cc')) || 49 !fileNames.some((name) => name.endsWith('.js'))) { 50 return []; 51 } 52 53 blockName = blockName.toLowerCase().replace(/\s/g, '_').replace(/\W/g, ''); 54 const dir = new URL( 55 `./${String(++id).padStart(2, '0')}_${blockName}/`, 56 verifyDir, 57 ); 58 59 files = fileNames.map((name) => { 60 if (name === 'test.js') { 61 files[name] = `'use strict'; 62const common = require('../../common'); 63${files[name].replace( 64 "'./build/Release/addon'", 65 // eslint-disable-next-line no-template-curly-in-string 66 '`./build/${common.buildType}/addon`')} 67`; 68 } 69 return { 70 content: files[name], 71 name, 72 url: new URL(`./${name}`, dir), 73 }; 74 }); 75 76 files.push({ 77 url: new URL('./binding.gyp', dir), 78 content: JSON.stringify({ 79 targets: [ 80 { 81 target_name: 'addon', 82 sources: files.map(({ name }) => name), 83 includes: ['../common.gypi'], 84 }, 85 ] 86 }) 87 }); 88 89 const dirCreation = mkdir(dir); 90 91 return files.map(({ url, content }) => 92 dirCreation.then(() => writeFile(url, content))); 93} 94