Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit b36e55a

Browse filesBrowse files
joyeecheungaduh95
andcommitted
build: make test-addons dependency-free
`make test-addons` used to depend on a markdown parser and then doc-kit to extract C++ addon examples from addons.md by guessing the file contents based on headings. This is hacky and brittle. The introduction of doc-kit also means tests intended for verifying the binary like `make test-only` now need to support doc-building toolchains e.g. minifier, highlighter, and indirect dependencies that rely on prebuilt-addon/wasm, which defeats the purpose and makes it harder to run for experimental platforms. This patch adds explicit `<!-- addon-verify-file dir/filename -->` markers in addons.md to locate extractable code blocks, avoiding fragile heuristics based on heading text or code block order and eliminating the dependency with simpler parsing. Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com> PR-URL: #62388 Fixes: #62385 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Richard Lau <richard.lau@ibm.com>
1 parent bb6293a commit b36e55a
Copy full SHA for b36e55a

4 files changed

+151-7Lines changed: 151 additions & 7 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎Makefile‎

Copy file name to clipboardExpand all lines: Makefile
+4-6Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -391,17 +391,15 @@ DOC_KIT ?= tools/doc/node_modules/@node-core/doc-kit/bin/cli.mjs
391391

392392
node_use_openssl_and_icu = $(call available-node,"-p" \
393393
"process.versions.openssl != undefined && process.versions.icu != undefined")
394-
test/addons/.docbuildstamp: $(DOCBUILDSTAMP_PREREQS) tools/doc/node_modules
394+
test/addons/.docbuildstamp: $(DOCBUILDSTAMP_PREREQS) tools/doc/addon-verify.mjs
395395
@if [ "$(shell $(node_use_openssl_and_icu))" != "true" ]; then \
396396
echo "Skipping .docbuildstamp (no crypto and/or no ICU)"; \
397397
else \
398398
$(RM) -r test/addons/??_*/; \
399399
$(call available-node, \
400-
$(DOC_KIT) generate \
401-
-t addon-verify \
402-
-i doc/api/addons.md \
403-
-o test/addons/ \
404-
--type-map doc/type-map.json \
400+
tools/doc/addon-verify.mjs \
401+
--input doc/api/addons.md \
402+
--output test/addons/ \
405403
) \
406404
[ $$? -eq 0 ] && touch $@; \
407405
fi
Collapse file

‎doc/api/addons.md‎

Copy file name to clipboardExpand all lines: doc/api/addons.md
+44Lines changed: 44 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ such as any libuv handles registered by the addon.
280280

281281
The following `addon.cc` uses `AddEnvironmentCleanupHook`:
282282

283+
<!-- addon-verify-file worker_support/addon.cc -->
284+
283285
```cpp
284286
// addon.cc
285287
#include <node.h>
@@ -328,6 +330,8 @@ NODE_MODULE_INIT(/* exports, module, context */) {
328330
329331
Test in JavaScript by running:
330332
333+
<!-- addon-verify-file worker_support/test.js -->
334+
331335
```js
332336
// test.js
333337
require('./build/Release/addon');
@@ -526,6 +530,8 @@ code.
526530
The following example illustrates how to read function arguments passed from
527531
JavaScript and how to return a result:
528532

533+
<!-- addon-verify-file function_arguments/addon.cc -->
534+
529535
```cpp
530536
// addon.cc
531537
#include <node.h>
@@ -585,6 +591,8 @@ NODE_MODULE(NODE_GYP_MODULE_NAME, Init)
585591
586592
Once compiled, the example addon can be required and used from within Node.js:
587593
594+
<!-- addon-verify-file function_arguments/test.js -->
595+
588596
```js
589597
// test.js
590598
const addon = require('./build/Release/addon');
@@ -598,6 +606,8 @@ It is common practice within addons to pass JavaScript functions to a C++
598606
function and execute them from there. The following example illustrates how
599607
to invoke such callbacks:
600608

609+
<!-- addon-verify-file callbacks/addon.cc -->
610+
601611
```cpp
602612
// addon.cc
603613
#include <node.h>
@@ -641,6 +651,8 @@ property of `exports`.
641651
642652
To test it, run the following JavaScript:
643653
654+
<!-- addon-verify-file callbacks/test.js -->
655+
644656
```js
645657
// test.js
646658
const addon = require('./build/Release/addon');
@@ -659,6 +671,8 @@ Addons can create and return new objects from within a C++ function as
659671
illustrated in the following example. An object is created and returned with a
660672
property `msg` that echoes the string passed to `createObject()`:
661673

674+
<!-- addon-verify-file object_factory/addon.cc -->
675+
662676
```cpp
663677
// addon.cc
664678
#include <node.h>
@@ -698,6 +712,8 @@ NODE_MODULE(NODE_GYP_MODULE_NAME, Init)
698712
699713
To test it in JavaScript:
700714
715+
<!-- addon-verify-file object_factory/test.js -->
716+
701717
```js
702718
// test.js
703719
const addon = require('./build/Release/addon');
@@ -713,6 +729,8 @@ console.log(obj1.msg, obj2.msg);
713729
Another common scenario is creating JavaScript functions that wrap C++
714730
functions and returning those back to JavaScript:
715731

732+
<!-- addon-verify-file function_factory/addon.cc -->
733+
716734
```cpp
717735
// addon.cc
718736
#include <node.h>
@@ -760,6 +778,8 @@ NODE_MODULE(NODE_GYP_MODULE_NAME, Init)
760778
761779
To test:
762780
781+
<!-- addon-verify-file function_factory/test.js -->
782+
763783
```js
764784
// test.js
765785
const addon = require('./build/Release/addon');
@@ -774,6 +794,8 @@ console.log(fn());
774794
It is also possible to wrap C++ objects/classes in a way that allows new
775795
instances to be created using the JavaScript `new` operator:
776796

797+
<!-- addon-verify-file wrapping_c_objects/addon.cc -->
798+
777799
```cpp
778800
// addon.cc
779801
#include <node.h>
@@ -795,6 +817,8 @@ NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)
795817
796818
Then, in `myobject.h`, the wrapper class inherits from `node::ObjectWrap`:
797819
820+
<!-- addon-verify-file wrapping_c_objects/myobject.h -->
821+
798822
```cpp
799823
// myobject.h
800824
#ifndef MYOBJECT_H
@@ -828,6 +852,8 @@ In `myobject.cc`, implement the various methods that are to be exposed.
828852
In the following code, the method `plusOne()` is exposed by adding it to the
829853
constructor's prototype:
830854

855+
<!-- addon-verify-file wrapping_c_objects/myobject.cc -->
856+
831857
```cpp
832858
// myobject.cc
833859
#include "myobject.h"
@@ -931,6 +957,8 @@ To build this example, the `myobject.cc` file must be added to the
931957

932958
Test it with:
933959

960+
<!-- addon-verify-file wrapping_c_objects/test.js -->
961+
934962
```js
935963
// test.js
936964
const addon = require('./build/Release/addon');
@@ -968,6 +996,8 @@ const obj = addon.createObject();
968996

969997
First, the `createObject()` method is implemented in `addon.cc`:
970998

999+
<!-- addon-verify-file factory_of_wrapped_objects/addon.cc -->
1000+
9711001
```cpp
9721002
// addon.cc
9731003
#include <node.h>
@@ -1001,6 +1031,8 @@ In `myobject.h`, the static method `NewInstance()` is added to handle
10011031
instantiating the object. This method takes the place of using `new` in
10021032
JavaScript:
10031033
1034+
<!-- addon-verify-file factory_of_wrapped_objects/myobject.h -->
1035+
10041036
```cpp
10051037
// myobject.h
10061038
#ifndef MYOBJECT_H
@@ -1033,6 +1065,8 @@ class MyObject : public node::ObjectWrap {
10331065

10341066
The implementation in `myobject.cc` is similar to the previous example:
10351067

1068+
<!-- addon-verify-file factory_of_wrapped_objects/myobject.cc -->
1069+
10361070
```cpp
10371071
// myobject.cc
10381072
#include <node.h>
@@ -1147,6 +1181,8 @@ Once again, to build this example, the `myobject.cc` file must be added to the
11471181

11481182
Test it with:
11491183

1184+
<!-- addon-verify-file factory_of_wrapped_objects/test.js -->
1185+
11501186
```js
11511187
// test.js
11521188
const createObject = require('./build/Release/addon');
@@ -1175,6 +1211,8 @@ wrapped objects around by unwrapping them with the Node.js helper function
11751211
`node::ObjectWrap::Unwrap`. The following examples shows a function `add()`
11761212
that can take two `MyObject` objects as input arguments:
11771213

1214+
<!-- addon-verify-file passing_wrapped_objects_around/addon.cc -->
1215+
11781216
```cpp
11791217
// addon.cc
11801218
#include <node.h>
@@ -1224,6 +1262,8 @@ NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)
12241262
In `myobject.h`, a new public method is added to allow access to private values
12251263
after unwrapping the object.
12261264
1265+
<!-- addon-verify-file passing_wrapped_objects_around/myobject.h -->
1266+
12271267
```cpp
12281268
// myobject.h
12291269
#ifndef MYOBJECT_H
@@ -1256,6 +1296,8 @@ class MyObject : public node::ObjectWrap {
12561296

12571297
The implementation of `myobject.cc` remains similar to the previous version:
12581298

1299+
<!-- addon-verify-file passing_wrapped_objects_around/myobject.cc -->
1300+
12591301
```cpp
12601302
// myobject.cc
12611303
#include <node.h>
@@ -1340,6 +1382,8 @@ void MyObject::NewInstance(const FunctionCallbackInfo<Value>& args) {
13401382
13411383
Test it with:
13421384
1385+
<!-- addon-verify-file passing_wrapped_objects_around/test.js -->
1386+
13431387
```js
13441388
// test.js
13451389
const addon = require('./build/Release/addon');
Collapse file

‎tools/doc/addon-verify.mjs‎

Copy file name to clipboard
+102Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/usr/bin/env node
2+
3+
// Extracts C++ addon examples from doc/api/addons.md into numbered test
4+
// directories under test/addons/.
5+
//
6+
// Each code block to extract is preceded by a marker in the markdown:
7+
//
8+
// <!-- addon-verify-file worker_support/addon.cc -->
9+
// ```cpp
10+
// #include <node.h>
11+
// ...
12+
// ```
13+
//
14+
// This produces test/addons/01_worker_support/addon.cc.
15+
// Sections are numbered in order of first appearance.
16+
17+
import { mkdirSync, writeFileSync } from 'node:fs';
18+
import { open } from 'node:fs/promises';
19+
import { join } from 'node:path';
20+
import { parseArgs } from 'node:util';
21+
22+
const { values } = parseArgs({
23+
options: {
24+
input: { type: 'string' },
25+
output: { type: 'string' },
26+
},
27+
});
28+
29+
if (!values.input || !values.output) {
30+
console.error('Usage: addon-verify.mjs --input <file> --output <dir>');
31+
process.exit(1);
32+
}
33+
34+
const src = await open(values.input, 'r');
35+
36+
const MARKER_RE = /^<!--\s*addon-verify-file\s+(\S+?)\/(\S+)\s*-->$/;
37+
38+
const entries = [];
39+
let nextBlockIsAddonVerifyFile = false;
40+
let expectedClosingFenceMarker;
41+
for await (const line of src.readLines()) {
42+
if (expectedClosingFenceMarker) {
43+
// We're inside a Addon snippet
44+
if (line === expectedClosingFenceMarker) {
45+
// End of the snippet
46+
expectedClosingFenceMarker = null;
47+
continue;
48+
}
49+
50+
entries.at(-1).content += `${line}\n`;
51+
}
52+
if (nextBlockIsAddonVerifyFile) {
53+
if (line) {
54+
expectedClosingFenceMarker = line.replace(/\w/g, '');
55+
nextBlockIsAddonVerifyFile = false;
56+
}
57+
continue;
58+
}
59+
const match = MARKER_RE.exec(line);
60+
if (match) {
61+
nextBlockIsAddonVerifyFile = true;
62+
const [, dir, name] = match;
63+
entries.push({ dir, name, content: '' });
64+
}
65+
}
66+
67+
// Collect files grouped by section directory name.
68+
const sections = Map.groupBy(entries, (e) => e.dir);
69+
70+
let idx = 0;
71+
for (const [name, files] of sections) {
72+
const dirName = `${String(++idx).padStart(2, '0')}_${name}`;
73+
const dir = join(values.output, dirName);
74+
mkdirSync(dir, { recursive: true });
75+
76+
for (const file of files) {
77+
let content = file.content;
78+
if (file.name === 'test.js') {
79+
content =
80+
"'use strict';\n" +
81+
"const common = require('../../common');\n" +
82+
content.replace(
83+
"'./build/Release/addon'",
84+
// eslint-disable-next-line no-template-curly-in-string
85+
'`./build/${common.buildType}/addon`',
86+
);
87+
}
88+
writeFileSync(join(dir, file.name), content);
89+
}
90+
91+
// Generate binding.gyp
92+
const names = files.map((f) => f.name);
93+
writeFileSync(join(dir, 'binding.gyp'), JSON.stringify({
94+
targets: [{
95+
target_name: 'addon',
96+
sources: names,
97+
includes: ['../common.gypi'],
98+
}],
99+
}));
100+
101+
console.log(`Generated ${dirName} with files: ${names.join(', ')}`);
102+
}
Collapse file

‎vcbuild.bat‎

Copy file name to clipboardExpand all lines: vcbuild.bat
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,7 @@ for /d %%F in (test\addons\??_*) do (
687687
rd /s /q %%F
688688
)
689689
:: generate
690-
%doc_kit_exe% generate -t addon-verify -i "file://%~dp0doc\api\addons.md" -o "file://%~dp0test\addons" --type-map "file://%~dp0doc\type-map.json"
690+
"%node_exe%" tools\doc\addon-verify.mjs --input "%~dp0doc\api\addons.md" --output "%~dp0test\addons"
691691
if %errorlevel% neq 0 exit /b %errorlevel%
692692
:: building addons
693693
setlocal

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.