node/tools/doc/addon-verify.mjs
Anna Henningsen fc28d9402f src: officially deprecate node::ObjectWrap
This interface has long been unmaintained and doesn't
match current best practices for C++ object management.

It's also simply not a good idea to have this header be
part of Node.js core; since it is an inline header, user
code will depend on the Node.js version it was compiled with,
and there is no way to adopt changes to this header
independently of changing the target Node.js version.

Better userland alternatives exist and have long been
more popular, notably, `node-addon-api` and, for use cases
where direct V8 API access is required, `nan`.

Signed-off-by: Anna Henningsen <anna@addaleax.net>
PR-URL: https://github.com/nodejs/node/pull/63756
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
2026-06-13 23:12:14 +00:00

107 lines
3 KiB
JavaScript
Executable file

#!/usr/bin/env node
// Extracts C++ addon examples from doc/api/addons.md into numbered test
// directories under test/addons/.
//
// Each code block to extract is preceded by a marker in the markdown:
//
// <!-- addon-verify-file worker_support/addon.cc [...] -->
// ```cpp
// #include <node.h>
// ...
// ```
//
// This produces test/addons/01_worker_support/addon.cc.
// Sections are numbered in order of first appearance.
import { mkdirSync, writeFileSync } from 'node:fs';
import { open } from 'node:fs/promises';
import { join } from 'node:path';
import { parseArgs } from 'node:util';
const { values } = parseArgs({
options: {
input: { type: 'string' },
output: { type: 'string' },
},
});
if (!values.input || !values.output) {
console.error('Usage: addon-verify.mjs --input <file> --output <dir>');
process.exit(1);
}
const src = await open(values.input, 'r');
const MARKER_RE = /^<!--\s*addon-verify-file\s+(?<filenames>((\S+?)\/(\S+)\s?)+)\s*-->$/;
const entries = [];
let nextBlockIsAddonVerifyFile = false;
let expectedClosingFenceMarker;
for await (const line of src.readLines()) {
if (expectedClosingFenceMarker) {
// We're inside a Addon snippet
if (line === expectedClosingFenceMarker) {
// End of the snippet
expectedClosingFenceMarker = null;
continue;
}
entries.at(-1).contentRef.content += `${line}\n`;
}
if (nextBlockIsAddonVerifyFile) {
if (line) {
expectedClosingFenceMarker = line.replace(/\w/g, '');
nextBlockIsAddonVerifyFile = false;
}
continue;
}
const match = MARKER_RE.exec(line);
if (match) {
nextBlockIsAddonVerifyFile = true;
const { filenames } = match.groups;
// Use a single contentRef for files with identical contents
const contentRef = { content: '' };
for (const filename of filenames.split(/\s+/).filter(Boolean)) {
const [dir, name] = filename.split('/');
entries.push({ dir, name, contentRef });
}
}
}
// Collect files grouped by section directory name.
const sections = Map.groupBy(entries, (e) => e.dir);
let idx = 0;
for (const [name, files] of sections) {
const dirName = `${String(++idx).padStart(2, '0')}_${name}`;
const dir = join(values.output, dirName);
mkdirSync(dir, { recursive: true });
for (const file of files) {
let { content } = file.contentRef;
if (file.name === 'test.js') {
content =
"'use strict';\n" +
"const common = require('../../common');\n" +
content.replace(
"'./build/Release/addon'",
// eslint-disable-next-line no-template-curly-in-string
'`./build/${common.buildType}/addon`',
);
}
writeFileSync(join(dir, file.name), content);
}
// Generate binding.gyp
const names = files.map((f) => f.name);
writeFileSync(join(dir, 'binding.gyp'), JSON.stringify({
targets: [{
target_name: 'addon',
sources: names,
includes: ['../common.gypi'],
}],
}));
console.log(`Generated ${dirName} with files: ${names.join(', ')}`);
}