From 8cfa9d1b7a8e3edc4687fd345677988fa4a50206 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Mon, 27 Oct 2025 16:44:23 +0100 Subject: [PATCH 01/64] chore: Upgrade support to node 22 and 24 We are also upgrading the minimum engine to 22, and the dependencies we use for CodeQL to 24, as it's going to be our baseline from now on. --- .evergreen.yml | 43 ++++++++++++++---------------------- .github/workflows/codeql.yml | 8 +++---- .github/workflows/nodejs.yml | 5 +---- package.json | 2 +- 4 files changed, 22 insertions(+), 36 deletions(-) diff --git a/.evergreen.yml b/.evergreen.yml index a0edaa6..907e44f 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -14,7 +14,7 @@ functions: set -e set -x - export NODE_VERSION=20.13.0 + export NODE_VERSION=24.10.0 bash .evergreen/install-node.sh install: - command: shell.exec @@ -75,38 +75,31 @@ functions: TEST_NODE_VERSION="$TEST_NODE_VERSION" npm run test-ci tasks: - - name: test_n14 - commands: - - func: checkout - - func: install_node - - func: install - - func: test - vars: - node_version: "14.21.3" - - name: test_n16 + - name: test_n20 commands: - func: checkout - func: install_node - func: install - func: test vars: - node_version: "16.20.1" - - name: test_n18 + node_version: "20.19.5" + - name: test_n22 commands: - func: checkout - func: install_node - func: install - func: test vars: - node_version: "18.17.0" - - name: test_n20 + node_version: "22.21.0" + - name: test_n24 commands: - func: checkout - func: install_node - func: install - func: test vars: - node_version: "20.13.0" + node_version: "24.10.0" + - name: check commands: - func: checkout @@ -119,32 +112,28 @@ buildvariants: display_name: 'Ubuntu 20.04 x64' run_on: ubuntu2004-large tasks: - - test_n14 - - test_n16 - - test_n18 - test_n20 + - test_n22 + - test_n24 - check - name: macos_x64_test display_name: 'macOS 11.00 x64' run_on: macos-1100 tasks: - - test_n14 - - test_n16 - - test_n18 - test_n20 + - test_n22 + - test_n24 - name: macos_arm64_test display_name: 'macOS 11.00 arm64' run_on: macos-1100-arm64 tasks: - - test_n14 - - test_n16 - - test_n18 - test_n20 + - test_n22 + - test_n24 - name: windows_x64_test display_name: 'Windows x64' run_on: windows-vsCurrent-xlarge tasks: - - test_n14 - - test_n16 - - test_n18 - test_n20 + - test_n22 + - test_n24 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 574fcf1..3ad2497 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -42,13 +42,13 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - - name: Use Node.js v18.x + - name: Use Node.js v24.x if: matrix.language == 'cpp' - uses: actions/setup-node@8f152de45cc393bb48ce5d89d36b731f54556e65 # v4.0.0 + uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: - node-version: 18.x + node-version: 24.x - - name: Use Node.js v18.x + - name: Use Node.js v24.x if: matrix.language == 'cpp' run: | npm install diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 293c59b..b7c28dd 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -13,7 +13,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - node-version: [14.x, 16.x, 18.x, 20.x] + node-version: [20.x, 22.x, 24.x] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 @@ -22,9 +22,6 @@ jobs: with: check-latest: true node-version: ${{ matrix.node-version }} - - name: Install npm@8.x - if: ${{ matrix.node-version == '14.x' }} - run: npm install -g npm@8.x - name: Install Dependencies run: npm install - name: Test diff --git a/package.json b/package.json index 7d7f769..cda28fe 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "boxednode": "bin/boxednode.js" }, "engines": { - "node": ">= 12.4.0" + "node": ">= 22.21.0" }, "scripts": { "lint": "eslint **/*.ts bin/*.js", From 687c75bef60e0dab721a9a83380a2733b64729ee Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Mon, 27 Oct 2025 17:33:42 +0100 Subject: [PATCH 02/64] chore: use devtools toolchain on linux and upgrade macos deps --- .evergreen.yml | 4 ++-- .evergreen/install-node.sh | 5 ++++- .evergreen/use-node.sh | 8 +++++--- package.json | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.evergreen.yml b/.evergreen.yml index 907e44f..e995fec 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -118,14 +118,14 @@ buildvariants: - check - name: macos_x64_test display_name: 'macOS 11.00 x64' - run_on: macos-1100 + run_on: macos-13 tasks: - test_n20 - test_n22 - test_n24 - name: macos_arm64_test display_name: 'macOS 11.00 arm64' - run_on: macos-1100-arm64 + run_on: macos-13-arm64 tasks: - test_n20 - test_n22 diff --git a/.evergreen/install-node.sh b/.evergreen/install-node.sh index 8a35449..af3a519 100644 --- a/.evergreen/install-node.sh +++ b/.evergreen/install-node.sh @@ -3,11 +3,14 @@ set -e set -x +# so we use the devtools binaries first (for gcc/g++) +export PATH="/opt/devtools/bin:$PATH" + export BASEDIR="$PWD" mkdir -p .deps cd .deps -NVM_URL="https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh" +NVM_URL="https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh" # this needs to be explicitly exported for the nvm install below export NVM_DIR="$PWD/nvm" diff --git a/.evergreen/use-node.sh b/.evergreen/use-node.sh index b657e8c..2561de8 100644 --- a/.evergreen/use-node.sh +++ b/.evergreen/use-node.sh @@ -1,8 +1,10 @@ if [[ "$OS" == "Windows_NT" ]]; then - export PATH="$PWD/.deps/node/bin:$PATH" + export PATH="$PWD/.deps/node/bin:$PATH" else - export NVM_DIR="$PWD/.deps/nvm" - [ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh" + # so we use the devtools binaries first (for gcc/g++) + export PATH="/opt/devtools/bin:$PATH" + export NVM_DIR="$PWD/.deps/nvm" + [ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh" fi echo "updated PATH=$PATH" diff --git a/package.json b/package.json index cda28fe..137cf12 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "boxednode": "bin/boxednode.js" }, "engines": { - "node": ">= 22.21.0" + "node": ">= 20.19.5" }, "scripts": { "lint": "eslint **/*.ts bin/*.js", From 1062b9399b80b1648ffa50359374934ec04280c3 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Mon, 27 Oct 2025 17:56:06 +0100 Subject: [PATCH 03/64] chore: change display name, it's misleading --- .evergreen.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.evergreen.yml b/.evergreen.yml index e995fec..3e76ad7 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -117,14 +117,14 @@ buildvariants: - test_n24 - check - name: macos_x64_test - display_name: 'macOS 11.00 x64' + display_name: 'macOS 13.00 x64' run_on: macos-13 tasks: - test_n20 - test_n22 - test_n24 - name: macos_arm64_test - display_name: 'macOS 11.00 arm64' + display_name: 'macOS 13.00 arm64' run_on: macos-13-arm64 tasks: - test_n20 From 8254b4c107b50a6494ed056f157a14834dc034c3 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Thu, 30 Oct 2025 16:28:31 +0100 Subject: [PATCH 04/64] chore: add brotli as a dependency when compiling the test --- test/compile-main-template-only.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/test/compile-main-template-only.sh b/test/compile-main-template-only.sh index 7e4316a..3b40755 100755 --- a/test/compile-main-template-only.sh +++ b/test/compile-main-template-only.sh @@ -15,6 +15,7 @@ if [ ! -e main-template-build ]; then fi g++ \ + -Imain-template-build/deps/brotli/c/include/ \ -Imain-template-build/src \ -Imain-template-build/deps/v8/include \ -Imain-template-build/deps/uv/include \ From a695c62e8b3b53b41266cac9163208328ab1cc04 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Thu, 30 Oct 2025 16:44:26 +0100 Subject: [PATCH 05/64] chore: reuse devtools toolchain in Linux --- .evergreen.yml | 5 +++++ .evergreen/install-node.sh | 14 +++++++++----- .evergreen/use-node.sh | 10 ++++++++-- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/.evergreen.yml b/.evergreen.yml index 3e76ad7..b7b56d6 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -21,6 +21,8 @@ functions: params: working_dir: src shell: bash + env: + NODE_VERSION: ${node_version} script: | set -e set -x @@ -32,6 +34,8 @@ functions: params: working_dir: src shell: bash + env: + NODE_VERSION: ${node_version} script: | set -e set -x @@ -45,6 +49,7 @@ functions: working_dir: src shell: bash env: + NODE_VERSION: ${node_version} TEST_NODE_VERSION: ${node_version} OKTA_TEST_CONFIG: ${okta_test_config} OKTA_TEST_CREDENTIALS: ${okta_test_credentials} diff --git a/.evergreen/install-node.sh b/.evergreen/install-node.sh index af3a519..f788134 100644 --- a/.evergreen/install-node.sh +++ b/.evergreen/install-node.sh @@ -24,12 +24,16 @@ if [[ "$OS" == "Windows_NT" ]]; then mv -v node-v$NODE_VERSION-win-x64/* node/bin chmod a+x node/bin/* export PATH="$PWD/node/bin:$PATH" -# install Node.js on Linux/MacOS else - curl -o- $NVM_URL | bash - set +x - [ -s "${NVM_DIR}/nvm.sh" ] && source "${NVM_DIR}/nvm.sh" - nvm install --no-progress "$NODE_VERSION" + if [ uname = "Darwin" ] ; then # install Node.js on MacOS + curl -o- $NVM_URL | bash + set +x + [ -s "${NVM_DIR}/nvm.sh" ] && source "${NVM_DIR}/nvm.sh" + nvm install --no-progress "$NODE_VERSION" + else # Linux already has it's own toolchain in evergreen + NODE_MAJOR=$(echo $NODE_VERSION | awk -F . '{print $1}') + export PATH="/opt/devtools/node$NODE_MAJOR/bin:$PATH" + fi fi which node && node -v || echo "node not found, PATH=$PATH" diff --git a/.evergreen/use-node.sh b/.evergreen/use-node.sh index 2561de8..b4c097c 100644 --- a/.evergreen/use-node.sh +++ b/.evergreen/use-node.sh @@ -3,8 +3,14 @@ if [[ "$OS" == "Windows_NT" ]]; then else # so we use the devtools binaries first (for gcc/g++) export PATH="/opt/devtools/bin:$PATH" - export NVM_DIR="$PWD/.deps/nvm" - [ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh" + + if [ uname = "Darwin" ] ; then + export NVM_DIR="$PWD/.deps/nvm" + [ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh" + else + NODE_MAJOR=$(echo $NODE_VERSION | awk -F . '{print $1}') + export PATH="/opt/devtools/node$NODE_MAJOR/bin:$PATH" + fi fi echo "updated PATH=$PATH" From 9649c4ca2e7e1c7c3bc53baae0f0655fb99be657 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Thu, 30 Oct 2025 16:46:29 +0100 Subject: [PATCH 06/64] chore: fix typo --- .evergreen/install-node.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.evergreen/install-node.sh b/.evergreen/install-node.sh index f788134..7accd83 100644 --- a/.evergreen/install-node.sh +++ b/.evergreen/install-node.sh @@ -30,7 +30,7 @@ else set +x [ -s "${NVM_DIR}/nvm.sh" ] && source "${NVM_DIR}/nvm.sh" nvm install --no-progress "$NODE_VERSION" - else # Linux already has it's own toolchain in evergreen + else # Linux already has its own toolchain in evergreen NODE_MAJOR=$(echo $NODE_VERSION | awk -F . '{print $1}') export PATH="/opt/devtools/node$NODE_MAJOR/bin:$PATH" fi From 6bfb4c5e7b08e857fc582ee9a665955651877337 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Thu, 30 Oct 2025 16:56:33 +0100 Subject: [PATCH 07/64] chore: simplify by using a stable folder after installation --- .evergreen.yml | 3 --- .evergreen/install-node.sh | 3 ++- .evergreen/use-node.sh | 7 +++---- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.evergreen.yml b/.evergreen.yml index b7b56d6..f2811eb 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -21,8 +21,6 @@ functions: params: working_dir: src shell: bash - env: - NODE_VERSION: ${node_version} script: | set -e set -x @@ -49,7 +47,6 @@ functions: working_dir: src shell: bash env: - NODE_VERSION: ${node_version} TEST_NODE_VERSION: ${node_version} OKTA_TEST_CONFIG: ${okta_test_config} OKTA_TEST_CREDENTIALS: ${okta_test_credentials} diff --git a/.evergreen/install-node.sh b/.evergreen/install-node.sh index 7accd83..dd4e18e 100644 --- a/.evergreen/install-node.sh +++ b/.evergreen/install-node.sh @@ -32,7 +32,8 @@ else nvm install --no-progress "$NODE_VERSION" else # Linux already has its own toolchain in evergreen NODE_MAJOR=$(echo $NODE_VERSION | awk -F . '{print $1}') - export PATH="/opt/devtools/node$NODE_MAJOR/bin:$PATH" + ln -s "/opt/devtools/node$NODE_MAJOR/bin/" "$PWD/node/bin" + export PATH="$PWD/node/bin:$PATH" fi fi diff --git a/.evergreen/use-node.sh b/.evergreen/use-node.sh index b4c097c..ca98f9d 100644 --- a/.evergreen/use-node.sh +++ b/.evergreen/use-node.sh @@ -4,12 +4,11 @@ else # so we use the devtools binaries first (for gcc/g++) export PATH="/opt/devtools/bin:$PATH" - if [ uname = "Darwin" ] ; then + if [ uname = "Darwin" ] ; then # in OSX use nvm export NVM_DIR="$PWD/.deps/nvm" [ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh" - else - NODE_MAJOR=$(echo $NODE_VERSION | awk -F . '{print $1}') - export PATH="/opt/devtools/node$NODE_MAJOR/bin:$PATH" + else # In Linux, use .deps/node/bin because it was set up with symlink to an existing node in the toolchain + export PATH="$PWD/.deps/node/bin:$PATH" fi fi From 57719912778f14f7e8777258a425e956b8bf133d Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Thu, 30 Oct 2025 17:26:16 +0100 Subject: [PATCH 08/64] chore: Remove unsupported flags for r/o heaps According to a v8 commit, v8 removed the ability to build without shared r/o heaps. More info: https://chromium-review.googlesource.com/c/v8/v8/+/6038531 --- src/index.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/index.ts b/src/index.ts index c7c96c7..1cdb115 100644 --- a/src/index.ts +++ b/src/index.ts @@ -191,19 +191,6 @@ async function compileNode ( env: env }; - // Node.js 19.4.0 is currently the minimum version that has https://github.com/nodejs/node/pull/45887. - // We want to disable the shared-ro-heap flag since it would require - // all snapshots used by Node.js to be equal, something that we don't - // want to or need to guarantee as embedders. - const nodeVersion = await getNodeVersionFromSourceDirectory(sourcePath); - if (nodeVersion[0] > 19 || (nodeVersion[0] === 19 && nodeVersion[1] >= 4)) { - if (process.platform !== 'win32') { - buildArgs = ['--disable-shared-readonly-heap', ...buildArgs]; - } else { - buildArgs = ['no-shared-roheap', ...buildArgs]; - } - } - if (process.platform !== 'win32') { const configure: string[] = ['./configure', ...buildArgs]; for (const module of linkedJSModules) { From 6a13147073023cf70173eaa476b3e83c548fa84c Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Thu, 30 Oct 2025 17:30:52 +0100 Subject: [PATCH 09/64] chore: fix check in OSX --- .evergreen/install-node.sh | 2 +- .evergreen/use-node.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.evergreen/install-node.sh b/.evergreen/install-node.sh index dd4e18e..7223abd 100644 --- a/.evergreen/install-node.sh +++ b/.evergreen/install-node.sh @@ -25,7 +25,7 @@ if [[ "$OS" == "Windows_NT" ]]; then chmod a+x node/bin/* export PATH="$PWD/node/bin:$PATH" else - if [ uname = "Darwin" ] ; then # install Node.js on MacOS + if [ "$(uname -s)" == "Darwin" ] ; then # install Node.js on MacOS curl -o- $NVM_URL | bash set +x [ -s "${NVM_DIR}/nvm.sh" ] && source "${NVM_DIR}/nvm.sh" diff --git a/.evergreen/use-node.sh b/.evergreen/use-node.sh index ca98f9d..90e5792 100644 --- a/.evergreen/use-node.sh +++ b/.evergreen/use-node.sh @@ -4,7 +4,7 @@ else # so we use the devtools binaries first (for gcc/g++) export PATH="/opt/devtools/bin:$PATH" - if [ uname = "Darwin" ] ; then # in OSX use nvm + if [ "$(uname -s)" == "Darwin" ] ; then # in OSX use nvm export NVM_DIR="$PWD/.deps/nvm" [ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh" else # In Linux, use .deps/node/bin because it was set up with symlink to an existing node in the toolchain From d46ce11ed1df923bd3fc31f7adfa46b4f8926f41 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Fri, 31 Oct 2025 15:48:13 +0100 Subject: [PATCH 10/64] chore: use 22 until we have 24 --- .evergreen.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.evergreen.yml b/.evergreen.yml index f2811eb..a48061a 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -14,7 +14,7 @@ functions: set -e set -x - export NODE_VERSION=24.10.0 + export NODE_VERSION=22.17.0 bash .evergreen/install-node.sh install: - command: shell.exec From 059accd686203863af8657edfd94a114dee9ef83 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Fri, 31 Oct 2025 15:51:39 +0100 Subject: [PATCH 11/64] chore: create parent node folder, necessary for the symlink --- .evergreen/install-node.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.evergreen/install-node.sh b/.evergreen/install-node.sh index 7223abd..2d7b581 100644 --- a/.evergreen/install-node.sh +++ b/.evergreen/install-node.sh @@ -31,6 +31,7 @@ else [ -s "${NVM_DIR}/nvm.sh" ] && source "${NVM_DIR}/nvm.sh" nvm install --no-progress "$NODE_VERSION" else # Linux already has its own toolchain in evergreen + mkdir -p node NODE_MAJOR=$(echo $NODE_VERSION | awk -F . '{print $1}') ln -s "/opt/devtools/node$NODE_MAJOR/bin/" "$PWD/node/bin" export PATH="$PWD/node/bin:$PATH" From 0c3253862af1533e319d71aff1b4d917cf456287 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Fri, 31 Oct 2025 16:12:42 +0100 Subject: [PATCH 12/64] chore: upgrade node-gyp to the latest --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 137cf12..0b3ca45 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "cli-progress": "^3.8.2", "gyp-parser": "^1.0.4", "node-fetch": "^2.6.1", - "node-gyp": "^9.0.0", + "node-gyp": "^11.5.0", "pkg-up": "^3.1.0", "rimraf": "^3.0.2", "semver": "^7.3.2", From f3fbc0ad259fbbe81b5ea5e467640c987a336694 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Fri, 31 Oct 2025 16:51:03 +0100 Subject: [PATCH 13/64] chore: upgrade test template to node 24 and std=c++20 --- test/compile-main-template-only.sh | 3 ++- test/index.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/compile-main-template-only.sh b/test/compile-main-template-only.sh index 3b40755..4f12078 100755 --- a/test/compile-main-template-only.sh +++ b/test/compile-main-template-only.sh @@ -9,7 +9,7 @@ cd "$(dirname $0)/.." if [ ! -e main-template-build ]; then mkdir main-template-build pushd main-template-build - curl -O https://nodejs.org/dist/v20.12.0/node-v20.12.0.tar.xz + curl -O https://nodejs.org/dist/v24.10.0/node-v24.10.0.tar.xz tar --strip-components=1 -xf node-*.tar.xz popd fi @@ -24,6 +24,7 @@ g++ \ -DREPLACE_WITH_ENTRY_POINT='"placeholder"' \ -DBOXEDNODE_CODE_CACHE_MODE='"placeholder"' \ -DREPLACE_WITH_MAIN_SCRIPT_SOURCE_GETTER= \ + -std=c++20 \ -fPIC -shared \ -o main-template-build/out.so \ -include resources/add-node_api.h \ diff --git a/test/index.ts b/test/index.ts index f0fafbf..8c442bf 100644 --- a/test/index.ts +++ b/test/index.ts @@ -138,7 +138,7 @@ describe('basic functionality', () => { } }); - it('works with a N-API addon', async function () { + it.only('works with a N-API addon', async function () { if (semver.lt(version, '14.13.0')) { return this.skip(); // no N-API addon support available } From eb955bd94241c813130a6170feffa0e2838a00a9 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Fri, 31 Oct 2025 17:23:16 +0100 Subject: [PATCH 14/64] chore: remove assert(CanUseCustomSnapshotPerIsolate) We only use one EmbedderSnapshotData and one Isolate, so we are not hitting the corner case of having isolates with different snapshots. --- resources/main-template.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/main-template.cc b/resources/main-template.cc index c63b118..c7b936a 100644 --- a/resources/main-template.cc +++ b/resources/main-template.cc @@ -240,7 +240,6 @@ static int RunNodeInstance(MultiIsolatePlatform* platform, ArrayBufferAllocator::Create(); #ifdef BOXEDNODE_CONSUME_SNAPSHOT - assert(EmbedderSnapshotData::CanUseCustomSnapshotPerIsolate()); node::EmbedderSnapshotData::Pointer snapshot_blob; #ifdef NODE_VERSION_SUPPORTS_STRING_VIEW_SNAPSHOT if (const auto snapshot_blob_sv = boxednode::GetBoxednodeSnapshotBlobSV()) { From 59714b3d8b1458fb152942b4147a7890f8c5d84e Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Fri, 31 Oct 2025 17:26:47 +0100 Subject: [PATCH 15/64] chore: forgot to remove the test skip --- test/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/index.ts b/test/index.ts index 8c442bf..f0fafbf 100644 --- a/test/index.ts +++ b/test/index.ts @@ -138,7 +138,7 @@ describe('basic functionality', () => { } }); - it.only('works with a N-API addon', async function () { + it('works with a N-API addon', async function () { if (semver.lt(version, '14.13.0')) { return this.skip(); // no N-API addon support available } From 3703751ddbb44f73a2a5b423284f3f12bf09495e Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Fri, 31 Oct 2025 20:12:17 +0100 Subject: [PATCH 16/64] chore: unregister isolate *after* disposing isolate Refs: https://github.com/nodejs/node/commit/9aa1afb52741498345ec483ff72932520c5050ff Refs: https://github.com/nodejs/node/commit/5d3e1b555c0902db1e99577a3429cffedcf3bbdc --- resources/main-template.cc | 10 ++++++++-- test/index.ts | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/resources/main-template.cc b/resources/main-template.cc index c7b936a..5b20416 100644 --- a/resources/main-template.cc +++ b/resources/main-template.cc @@ -194,9 +194,9 @@ static int RunNodeInstance(MultiIsolatePlatform* platform, ); Isolate* isolate = setup->isolate(); + Locker locker(isolate); { - Locker locker(isolate); Isolate::Scope isolate_scope(isolate); HandleScope handle_scope(isolate); @@ -387,8 +387,14 @@ static int RunNodeInstance(MultiIsolatePlatform* platform, platform->AddIsolateFinishedCallback(isolate, [](void* data) { *static_cast(data) = true; }, &platform_finished); - platform->UnregisterIsolate(isolate); + + // https://github.com/nodejs/node/commit/5d3e1b555c0902db1e99577a3429cffedcf3bbdc +#if NODE_VERSION_AT_LEAST(24, 0, 0) + platform->DisposeIsolate(isolate); +#else isolate->Dispose(); + platform->UnregisterIsolate(isolate); +#endif // Wait until the platform has cleaned up all relevant resources. while (!platform_finished) diff --git a/test/index.ts b/test/index.ts index f0fafbf..cefee5b 100644 --- a/test/index.ts +++ b/test/index.ts @@ -218,7 +218,7 @@ describe('basic functionality', () => { it(`works with snapshot support (compressBlobs = ${compressBlobs})`, async function () { this.timeout(2 * 60 * 60 * 1000); // 2 hours await compileJSFileAsBinary({ - nodeVersionRange: '^20.13.0', + nodeVersionRange: version, sourceFile: path.resolve(__dirname, 'resources/snapshot-echo-args.js'), targetFile: path.resolve(__dirname, `resources/snapshot-echo-args${exeSuffix}`), useNodeSnapshot: true, From 8c9aa7235016e465aac808903edaed895f0f76e2 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sat, 1 Nov 2025 01:01:21 +0100 Subject: [PATCH 17/64] feat!: remove support code for older Node.js versions --- resources/main-template.cc | 445 +------------------------------------ src/helpers.ts | 4 - 2 files changed, 3 insertions(+), 446 deletions(-) diff --git a/resources/main-template.cc b/resources/main-template.cc index 5b20416..88720c6 100644 --- a/resources/main-template.cc +++ b/resources/main-template.cc @@ -23,46 +23,11 @@ using namespace node; using namespace v8; -// 18.11.0 is the minimum version that has https://github.com/nodejs/node/pull/44121 -#if !NODE_VERSION_AT_LEAST(18, 11, 0) -#define USE_OWN_LEGACY_PROCESS_INITIALIZATION 1 -#endif - -// 20.0.0 will have https://github.com/nodejs/node/pull/45888, possibly the PR -// will be backported to older versions but for now this is the one where we -// can be sure of its presence. -#if NODE_VERSION_AT_LEAST(20, 0, 0) -#define NODE_VERSION_SUPPORTS_EMBEDDER_SNAPSHOT 1 -#endif - -// 20.13.0 has https://github.com/nodejs/node/pull/52595 for better startup snapshot -// initialization performance. -#if NODE_VERSION_AT_LEAST(20, 13, 0) -#define NODE_VERSION_SUPPORTS_STRING_VIEW_SNAPSHOT 1 -#endif - // Snapshot config is supported since https://github.com/nodejs/node/pull/50453 -#if NODE_VERSION_AT_LEAST(20, 12, 0) && !defined(BOXEDNODE_SNAPSHOT_CONFIG_FLAGS) +#ifndef BOXEDNODE_SNAPSHOT_CONFIG_FLAGS #define BOXEDNODE_SNAPSHOT_CONFIG_FLAGS (SnapshotFlags::kWithoutCodeCache) #endif -// 18.1.0 is the current minimum version that has https://github.com/nodejs/node/pull/42809, -// which introduced crashes when using workers, and later 18.9.0 is the current -// minimum version to contain https://github.com/nodejs/node/pull/44252, which -// introcued crashes when using the vm module. -// We should be able to remove this restriction again once Node.js stops relying -// on global state for determining whether snapshots are enabled or not -// (after https://github.com/nodejs/node/pull/45888, hopefully). -#if NODE_VERSION_AT_LEAST(18, 1, 0) && !defined(NODE_VERSION_SUPPORTS_EMBEDDER_SNAPSHOT) -#define PASS_NO_NODE_SNAPSHOT_OPTION 1 -#endif - -#ifdef USE_OWN_LEGACY_PROCESS_INITIALIZATION -namespace boxednode { -void InitializeOncePerProcess(); -void TearDownOncePerProcess(); -} -#endif namespace boxednode { namespace { struct TimingEntry { @@ -88,9 +53,7 @@ void MarkTime(const char* category, const char* label) { Local GetBoxednodeMainScriptSource(Isolate* isolate); Local GetBoxednodeCodeCacheBuffer(Isolate* isolate); std::vector GetBoxednodeSnapshotBlobVector(); -#ifdef NODE_VERSION_SUPPORTS_STRING_VIEW_SNAPSHOT std::optional GetBoxednodeSnapshotBlobSV(); -#endif void GetTimingData(const FunctionCallbackInfo& info) { Isolate* isolate = info.GetIsolate(); @@ -187,10 +150,8 @@ static int RunNodeInstance(MultiIsolatePlatform* platform, platform, &errors, args, - exec_args -#ifdef BOXEDNODE_SNAPSHOT_CONFIG_FLAGS - , SnapshotConfig { BOXEDNODE_SNAPSHOT_CONFIG_FLAGS, std::nullopt } -#endif + exec_args, + SnapshotConfig { BOXEDNODE_SNAPSHOT_CONFIG_FLAGS, std::nullopt } ); Isolate* isolate = setup->isolate(); @@ -241,11 +202,9 @@ static int RunNodeInstance(MultiIsolatePlatform* platform, #ifdef BOXEDNODE_CONSUME_SNAPSHOT node::EmbedderSnapshotData::Pointer snapshot_blob; -#ifdef NODE_VERSION_SUPPORTS_STRING_VIEW_SNAPSHOT if (const auto snapshot_blob_sv = boxednode::GetBoxednodeSnapshotBlobSV()) { snapshot_blob = EmbedderSnapshotData::FromBlob(snapshot_blob_sv.value()); } -#endif if (!snapshot_blob) { std::vector snapshot_blob_vec = boxednode::GetBoxednodeSnapshotBlobVector(); boxednode::MarkTime("Node.js Instance", "Decoded snapshot"); @@ -414,22 +373,10 @@ static int BoxednodeMain(std::vector args) { if (args.size() > 0) { args.insert(args.begin() + 1, "--"); -#ifdef PASS_NO_NODE_SNAPSHOT_OPTION - args.insert(args.begin() + 1, "--no-node-snapshot"); -#endif } // Parse Node.js CLI options, and print any errors that have occurred while // trying to parse them. -#ifdef USE_OWN_LEGACY_PROCESS_INITIALIZATION - boxednode::InitializeOncePerProcess(); - int exit_code = node::InitializeNodeWithArgs(&args, &exec_args, &errors); - for (const std::string& error : errors) - fprintf(stderr, "%s: %s\n", args[0].c_str(), error.c_str()); - if (exit_code != 0) { - return exit_code; - } -#else #if OPENSSL_VERSION_MAJOR >= 3 if (args.size() > 1) args.insert(args.begin() + 1, "--openssl-shared-config"); @@ -448,7 +395,6 @@ static int BoxednodeMain(std::vector args) { } args = result->args(); exec_args = result->exec_args(); -#endif #ifdef BOXEDNODE_CONSUME_SNAPSHOT if (args.size() > 0) { @@ -470,13 +416,8 @@ static int BoxednodeMain(std::vector args) { int ret = RunNodeInstance(platform.get(), args, exec_args); V8::Dispose(); -#ifdef USE_OWN_LEGACY_PROCESS_INITIALIZATION - V8::ShutdownPlatform(); - boxednode::TearDownOncePerProcess(); -#else V8::DisposePlatform(); node::TearDownOncePerProcess(); -#endif return ret; } @@ -519,386 +460,6 @@ int main(int argc, char** argv) { } #endif -// The code below is mostly lifted directly from node.cc -#ifdef USE_OWN_LEGACY_PROCESS_INITIALIZATION - -#if defined(__APPLE__) || defined(__linux__) || defined(_WIN32) -#define NODE_USE_V8_WASM_TRAP_HANDLER 1 -#else -#define NODE_USE_V8_WASM_TRAP_HANDLER 0 -#endif - -#if NODE_USE_V8_WASM_TRAP_HANDLER -#if defined(_WIN32) -#include "v8-wasm-trap-handler-win.h" -#else -#include -#include "v8-wasm-trap-handler-posix.h" -#endif -#endif // NODE_USE_V8_WASM_TRAP_HANDLER - -#if NODE_USE_V8_WASM_TRAP_HANDLER && defined(_WIN32) -static PVOID old_vectored_exception_handler; -#endif - -#if defined(_MSC_VER) -#include -#include -#define STDIN_FILENO 0 -#else -#include -#include // getrlimit, setrlimit -#include // tcgetattr, tcsetattr -#include // STDIN_FILENO, STDERR_FILENO -#endif - -#include -#include - -namespace boxednode { - -#if HAVE_OPENSSL -static void CheckEntropy() { - for (;;) { - int status = RAND_status(); - assert(status >= 0); // Cannot fail. - if (status != 0) - break; - - // Give up, RAND_poll() not supported. - if (RAND_poll() == 0) - break; - } -} - -static bool EntropySource(unsigned char* buffer, size_t length) { - // Ensure that OpenSSL's PRNG is properly seeded. - CheckEntropy(); - // RAND_bytes() can return 0 to indicate that the entropy data is not truly - // random. That's okay, it's still better than V8's stock source of entropy, - // which is /dev/urandom on UNIX platforms and the current time on Windows. - return RAND_bytes(buffer, length) != -1; -} -#endif - -void ResetStdio(); - -#ifdef __POSIX__ -static constexpr unsigned kMaxSignal = 32; - -typedef void (*sigaction_cb)(int signo, siginfo_t* info, void* ucontext); - -void SignalExit(int signo, siginfo_t* info, void* ucontext) { - ResetStdio(); - raise(signo); -} -#endif - -#if NODE_USE_V8_WASM_TRAP_HANDLER -#if defined(_WIN32) -static LONG TrapWebAssemblyOrContinue(EXCEPTION_POINTERS* exception) { - if (v8::TryHandleWebAssemblyTrapWindows(exception)) { - return EXCEPTION_CONTINUE_EXECUTION; - } - return EXCEPTION_CONTINUE_SEARCH; -} -#else -static std::atomic previous_sigsegv_action; - -void TrapWebAssemblyOrContinue(int signo, siginfo_t* info, void* ucontext) { - if (!v8::TryHandleWebAssemblyTrapPosix(signo, info, ucontext)) { - sigaction_cb prev = previous_sigsegv_action.load(); - if (prev != nullptr) { - prev(signo, info, ucontext); - } else { - // Reset to the default signal handler, i.e. cause a hard crash. - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_DFL; - int ret = sigaction(signo, &sa, nullptr); - assert(ret == 0); - - ResetStdio(); - raise(signo); - } - } -} -#endif // defined(_WIN32) -#endif // NODE_USE_V8_WASM_TRAP_HANDLER - -#ifdef __POSIX__ -void RegisterSignalHandler(int signal, - sigaction_cb handler, - bool reset_handler) { - assert(handler != nullptr); -#if NODE_USE_V8_WASM_TRAP_HANDLER - if (signal == SIGSEGV) { - assert(previous_sigsegv_action.is_lock_free()); - assert(!reset_handler); - previous_sigsegv_action.store(handler); - return; - } -#endif // NODE_USE_V8_WASM_TRAP_HANDLER - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_sigaction = handler; - sa.sa_flags = reset_handler ? SA_RESETHAND : 0; - sigfillset(&sa.sa_mask); - int ret = sigaction(signal, &sa, nullptr); - assert(ret == 0); -} -#endif // __POSIX__ - -#ifdef __POSIX__ -static struct { - int flags; - bool isatty; - struct stat stat; - struct termios termios; -} stdio[1 + STDERR_FILENO]; -#endif // __POSIX__ - - -inline void PlatformInit() { -#ifdef __POSIX__ -#if HAVE_INSPECTOR - sigset_t sigmask; - sigemptyset(&sigmask); - sigaddset(&sigmask, SIGUSR1); - const int err = pthread_sigmask(SIG_SETMASK, &sigmask, nullptr); -#endif // HAVE_INSPECTOR - - // Make sure file descriptors 0-2 are valid before we start logging anything. - for (auto& s : stdio) { - const int fd = &s - stdio; - if (fstat(fd, &s.stat) == 0) - continue; - // Anything but EBADF means something is seriously wrong. We don't - // have to special-case EINTR, fstat() is not interruptible. - if (errno != EBADF) - assert(0); - if (fd != open("/dev/null", O_RDWR)) - assert(0); - if (fstat(fd, &s.stat) != 0) - assert(0); - } - -#if HAVE_INSPECTOR - CHECK_EQ(err, 0); -#endif // HAVE_INSPECTOR - - // TODO(addaleax): NODE_SHARED_MODE does not really make sense here. -#ifndef NODE_SHARED_MODE - // Restore signal dispositions, the parent process may have changed them. - struct sigaction act; - memset(&act, 0, sizeof(act)); - - // The hard-coded upper limit is because NSIG is not very reliable; on Linux, - // it evaluates to 32, 34 or 64, depending on whether RT signals are enabled. - // Counting up to SIGRTMIN doesn't work for the same reason. - for (unsigned nr = 1; nr < kMaxSignal; nr += 1) { - if (nr == SIGKILL || nr == SIGSTOP) - continue; - act.sa_handler = (nr == SIGPIPE || nr == SIGXFSZ) ? SIG_IGN : SIG_DFL; - int ret = sigaction(nr, &act, nullptr); - assert(ret == 0); - } -#endif // !NODE_SHARED_MODE - - // Record the state of the stdio file descriptors so we can restore it - // on exit. Needs to happen before installing signal handlers because - // they make use of that information. - for (auto& s : stdio) { - const int fd = &s - stdio; - int err; - - do - s.flags = fcntl(fd, F_GETFL); - while (s.flags == -1 && errno == EINTR); // NOLINT - assert(s.flags != -1); - - if (uv_guess_handle(fd) != UV_TTY) continue; - s.isatty = true; - - do - err = tcgetattr(fd, &s.termios); - while (err == -1 && errno == EINTR); // NOLINT - assert(err == 0); - } - - RegisterSignalHandler(SIGINT, SignalExit, true); - RegisterSignalHandler(SIGTERM, SignalExit, true); - -#if NODE_USE_V8_WASM_TRAP_HANDLER -#if defined(_WIN32) - { - constexpr ULONG first = TRUE; - old_vectored_exception_handler = - AddVectoredExceptionHandler(first, TrapWebAssemblyOrContinue); - } -#else - // Tell V8 to disable emitting WebAssembly - // memory bounds checks. This means that we have - // to catch the SIGSEGV in TrapWebAssemblyOrContinue - // and pass the signal context to V8. - { - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_sigaction = TrapWebAssemblyOrContinue; - sa.sa_flags = SA_SIGINFO; - int ret = sigaction(SIGSEGV, &sa, nullptr); - assert(ret == 0); - } -#endif // defined(_WIN32) - V8::EnableWebAssemblyTrapHandler(false); -#endif // NODE_USE_V8_WASM_TRAP_HANDLER - - // Raise the open file descriptor limit. - struct rlimit lim; - if (getrlimit(RLIMIT_NOFILE, &lim) == 0 && lim.rlim_cur != lim.rlim_max) { - // Do a binary search for the limit. - rlim_t min = lim.rlim_cur; - rlim_t max = 1 << 20; - // But if there's a defined upper bound, don't search, just set it. - if (lim.rlim_max != RLIM_INFINITY) { - min = lim.rlim_max; - max = lim.rlim_max; - } - do { - lim.rlim_cur = min + (max - min) / 2; - if (setrlimit(RLIMIT_NOFILE, &lim)) { - max = lim.rlim_cur; - } else { - min = lim.rlim_cur; - } - } while (min + 1 < max); - } -#endif // __POSIX__ -#ifdef _WIN32 - for (int fd = 0; fd <= 2; ++fd) { - auto handle = reinterpret_cast(_get_osfhandle(fd)); - if (handle == INVALID_HANDLE_VALUE || - GetFileType(handle) == FILE_TYPE_UNKNOWN) { - // Ignore _close result. If it fails or not depends on used Windows - // version. We will just check _open result. - _close(fd); - if (fd != _open("nul", _O_RDWR)) - assert(0); - } - } -#endif // _WIN32 -} - - -// Safe to call more than once and from signal handlers. -void ResetStdio() { - uv_tty_reset_mode(); -#ifdef __POSIX__ - for (auto& s : stdio) { - const int fd = &s - stdio; - - struct stat tmp; - if (-1 == fstat(fd, &tmp)) { - assert(errno == EBADF); // Program closed file descriptor. - continue; - } - - bool is_same_file = - (s.stat.st_dev == tmp.st_dev && s.stat.st_ino == tmp.st_ino); - if (!is_same_file) continue; // Program reopened file descriptor. - - int flags; - do - flags = fcntl(fd, F_GETFL); - while (flags == -1 && errno == EINTR); // NOLINT - assert(flags != -1); - - // Restore the O_NONBLOCK flag if it changed. - if (O_NONBLOCK & (flags ^ s.flags)) { - flags &= ~O_NONBLOCK; - flags |= s.flags & O_NONBLOCK; - - int err; - do - err = fcntl(fd, F_SETFL, flags); - while (err == -1 && errno == EINTR); // NOLINT - assert(err != -1); - } - - if (s.isatty) { - sigset_t sa; - int err, ret; - - // We might be a background job that doesn't own the TTY so block SIGTTOU - // before making the tcsetattr() call, otherwise that signal suspends us. - sigemptyset(&sa); - sigaddset(&sa, SIGTTOU); - - ret = pthread_sigmask(SIG_BLOCK, &sa, nullptr); - assert(ret == 0); - do - err = tcsetattr(fd, TCSANOW, &s.termios); - while (err == -1 && errno == EINTR); // NOLINT - ret = pthread_sigmask(SIG_UNBLOCK, &sa, nullptr); - assert(ret == 0); - - // Normally we expect err == 0. But if macOS App Sandbox is enabled, - // tcsetattr will fail with err == -1 and errno == EPERM. - if (err != 0) { - assert(err == -1 && errno == EPERM); - } - } - } -#endif // __POSIX__ -} - -static void InitializeOpenSSL() { -#if HAVE_OPENSSL && !defined(OPENSSL_IS_BORINGSSL) - // In the case of FIPS builds we should make sure - // the random source is properly initialized first. -#if OPENSSL_VERSION_MAJOR >= 3 - // Use OPENSSL_CONF environment variable is set. - const char* conf_file = getenv("OPENSSL_CONF"); - - OPENSSL_INIT_SETTINGS* settings = OPENSSL_INIT_new(); - OPENSSL_INIT_set_config_filename(settings, conf_file); - OPENSSL_INIT_set_config_appname(settings, "openssl_conf"); - OPENSSL_INIT_set_config_file_flags(settings, - CONF_MFLAGS_IGNORE_MISSING_FILE); - - OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, settings); - OPENSSL_INIT_free(settings); - - if (ERR_peek_error() != 0) { - fprintf(stderr, "OpenSSL configuration error:\n"); - ERR_print_errors_fp(stderr); - exit(1); - } -#else // OPENSSL_VERSION_MAJOR < 3 - if (FIPS_mode()) { - OPENSSL_init(); - } -#endif - V8::SetEntropySource(boxednode::EntropySource); -#endif -} - -void InitializeOncePerProcess() { - atexit(ResetStdio); - PlatformInit(); - InitializeOpenSSL(); -} - -void TearDownOncePerProcess() { -#if NODE_USE_V8_WASM_TRAP_HANDLER && defined(_WIN32) - RemoveVectoredExceptionHandler(old_vectored_exception_handler); -#endif -} - -} // namespace boxednode - -#endif // USE_OWN_LEGACY_PROCESS_INITIALIZATION - namespace boxednode { REPLACE_WITH_MAIN_SCRIPT_SOURCE_GETTER } diff --git a/src/helpers.ts b/src/helpers.ts index e559466..83fd6d9 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -113,7 +113,6 @@ export async function createUncompressedBlobDefinition (fnName: string, source: ${Uint8Array.prototype.toString.call(source) || '0'} }; -#ifdef NODE_VERSION_SUPPORTS_STRING_VIEW_SNAPSHOT std::optional ${fnName}SV() { return { { @@ -122,7 +121,6 @@ export async function createUncompressedBlobDefinition (fnName: string, source: } }; } -#endif std::vector ${fnName}Vector() { return std::vector( @@ -166,11 +164,9 @@ export async function createCompressedBlobDefinition (fnName: string, source: Ui return dst;`} } -#ifdef NODE_VERSION_SUPPORTS_STRING_VIEW_SNAPSHOT std::optional ${fnName}SV() { return {}; } -#endif ${blobTypedArrayAccessors(fnName, source.length)} `; From 0193614c51dd5a08a2c261e348b220cf212d5503 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sat, 1 Nov 2025 01:09:22 +0100 Subject: [PATCH 18/64] chore: add evergreen task for testing dchecks --- .evergreen.yml | 47 ++++++++++++++++++++++++++++++++++++++++++----- src/index.ts | 4 ++++ 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/.evergreen.yml b/.evergreen.yml index a48061a..ef6ee28 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -53,6 +53,7 @@ functions: AZURE_TEST_CONFIG: ${azure_test_config} AZURE_TEST_CREDENTIALS: ${azure_test_credentials} DISTRO_ID: ${distro_id} + BOXEDNODE_DCHECKS_ENABLED: ${dchecks_enabled} script: | set -e set -x @@ -101,7 +102,35 @@ tasks: - func: test vars: node_version: "24.10.0" - + + - name: test_n20_dchecks + commands: + - func: checkout + - func: install_node + - func: install + - func: test + vars: + node_version: "20.19.5" + dchecks_enabled: 1 + - name: test_n22_dchecks + commands: + - func: checkout + - func: install_node + - func: install + - func: test + vars: + node_version: "22.21.0" + dchecks_enabled: 1 + - name: test_n24_dchecks + commands: + - func: checkout + - func: install_node + - func: install + - func: test + vars: + node_version: "24.10.0" + dchecks_enabled: 1 + - name: check commands: - func: checkout @@ -118,16 +147,24 @@ buildvariants: - test_n22 - test_n24 - check + - name: amazon_arm64_dchecks + display_name: 'Amazon Linux 2023 arm64 (Node.js/V8 DCHECKs)' + run_on: amazon2023.3-arm64-xxlarge + tasks: + - test_n20_dchecks + - test_n22_dchecks + - test_n24_dchecks + - check - name: macos_x64_test - display_name: 'macOS 13.00 x64' - run_on: macos-13 + display_name: 'macOS 14.00 x64' + run_on: macos-14 tasks: - test_n20 - test_n22 - test_n24 - name: macos_arm64_test - display_name: 'macOS 13.00 arm64' - run_on: macos-13-arm64 + display_name: 'macOS 14.00 arm64' + run_on: macos-14-arm64 tasks: - test_n20 - test_n22 diff --git a/src/index.ts b/src/index.ts index 1cdb115..b5fc5d7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -191,6 +191,10 @@ async function compileNode ( env: env }; + if (process.env.BOXEDNODE_DCHECKS_ENABLED === '1') { + buildArgs = ['--debug-node', '--v8-with-dchecks', ...buildArgs]; + } + if (process.platform !== 'win32') { const configure: string[] = ['./configure', ...buildArgs]; for (const module of linkedJSModules) { From 58aa93c783fcdbd2ae9037ba709032bdd9bd0df5 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sat, 1 Nov 2025 12:59:47 +0100 Subject: [PATCH 19/64] chore: use ubuntu instead of amazon for dcheck runs --- .evergreen.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.evergreen.yml b/.evergreen.yml index ef6ee28..98588c3 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -147,14 +147,13 @@ buildvariants: - test_n22 - test_n24 - check - - name: amazon_arm64_dchecks - display_name: 'Amazon Linux 2023 arm64 (Node.js/V8 DCHECKs)' - run_on: amazon2023.3-arm64-xxlarge + - name: ubuntu_arm64_dchecks + display_name: 'Ubuntu 24.04 arm64 (Node.js/V8 DCHECKs)' + run_on: ubuntu2404-arm64-latest-xlarge tasks: - test_n20_dchecks - test_n22_dchecks - test_n24_dchecks - - check - name: macos_x64_test display_name: 'macOS 14.00 x64' run_on: macos-14 From 62befcaf71cad908df7b13a2a2bc0b28661b2fcc Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Mon, 3 Nov 2025 08:27:31 -0500 Subject: [PATCH 20/64] fixup: use fixed version of weak-napi --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0b3ca45..74d82ef 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "nyc": "^15.1.0", "ts-node": "^10.8.1", "typescript": "^4.0.3", - "weak-napi": "2.0.2" + "weak-napi": "node-ffi-napi/weak-napi#5449c78739d69aa286e43c7baf9819440b4544bb" }, "dependencies": { "@pkgjs/nv": "^0.2.1", From c97bb8e757be4263903250769b86851ecf01e6b3 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Mon, 3 Nov 2025 15:35:06 +0100 Subject: [PATCH 21/64] chore: upgrade to vs2022 to test --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index b5fc5d7..8c49de1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -239,7 +239,7 @@ async function compileNode ( // conflicting arguments have been passed manually. const vcbuildArgs: string[] = [...buildArgs, ...makeArgs, 'projgen']; if (!vcbuildArgs.includes('debug') && !vcbuildArgs.includes('release')) { vcbuildArgs.push('release'); } - if (!vcbuildArgs.some((arg) => /^vs/.test(arg))) { vcbuildArgs.push('vs2019'); } + if (!vcbuildArgs.some((arg) => /^vs/.test(arg))) { vcbuildArgs.push('vs2022'); } for (const module of linkedJSModules) { vcbuildArgs.push('link-module', module); From 9a90bfc902453773ba984dfd42bf596a941f8cfc Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Mon, 3 Nov 2025 16:35:52 +0100 Subject: [PATCH 22/64] chore: upgrade host so it has msvc 2022 --- .evergreen.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.evergreen.yml b/.evergreen.yml index 98588c3..e211007 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -170,7 +170,7 @@ buildvariants: - test_n24 - name: windows_x64_test display_name: 'Windows x64' - run_on: windows-vsCurrent-xlarge + run_on: windows-2022-latest-xlarge tasks: - test_n20 - test_n22 From 0b369ddf8483dc9816ca6421dd1564d672010807 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Mon, 3 Nov 2025 17:35:38 +0100 Subject: [PATCH 23/64] chore: typo in the evergreen distro --- .evergreen.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.evergreen.yml b/.evergreen.yml index e211007..3224e3b 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -170,7 +170,7 @@ buildvariants: - test_n24 - name: windows_x64_test display_name: 'Windows x64' - run_on: windows-2022-latest-xlarge + run_on: windows-2022-xlarge tasks: - test_n20 - test_n22 From b18ff4da43262cc7c9ecc9604b20b6e3cbbe9626 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Mon, 3 Nov 2025 12:33:34 -0500 Subject: [PATCH 24/64] chore(ci): use llvm from homebrew on evergreen macOS --- .evergreen.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.evergreen.yml b/.evergreen.yml index 3224e3b..6c34513 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -58,6 +58,24 @@ functions: set -e set -x + if [ `uname` == "Darwin" ]; then + # the CI macOS machines have an outdated Clang that + # cannot build recent Node.js versions, so we use + # the LLVM version installed via Homebrew + # (both on arm64 and x64) + + LLVM_PREFIX="$(brew --prefix llvm)" + export PATH="$LLVM_PREFIX/bin:$PATH" + export CC="$LLVM_PREFIX/bin/clang" + export CXX="$LLVM_PREFIX/bin/clang++" + export LDFLAGS="-L$LLVM_PREFIX/lib -L$LLVM_PREFIX/lib/c++ -L$LLVM_PREFIX/lib/unwind" + export CPPFLAGS="-I$LLVM_PREFIX/include" + export CMAKE_PREFIX_PATH="$LLVM_PREFIX" + + $CC --version + $CXX --version + fi + rm -rf /tmp/m && mkdir -pv /tmp/m # Node.js compilation can fail on long path prefixes trap "rm -rf /tmp/m" EXIT export TMP=/tmp/m From 80a6c5b669bdd4ceac03c67dec4db21974b20820 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Tue, 4 Nov 2025 15:44:08 +0100 Subject: [PATCH 25/64] chore: Use python3.11 in windows host --- .evergreen.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.evergreen.yml b/.evergreen.yml index 6c34513..2ea85e0 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -39,6 +39,10 @@ functions: set -x . .evergreen/use-node.sh + if [ "$OS" == "Windows_NT" ]; then + BASE_PYTHON="/cygdrive/c/python/Python311" + export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" + fi npm run build npm run lint test: @@ -85,7 +89,8 @@ functions: # able to compile OpenSSL with assembly support, # so we revert back to the slower version. if [ "$OS" == "Windows_NT" ]; then - export PATH="/cygdrive/c/python/Python310/Scripts:/cygdrive/c/python/Python310:/cygdrive/c/Python310/Scripts:/cygdrive/c/Python310:$PATH" + BASE_PYTHON="/cygdrive/c/python/Python311" + export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" export BOXEDNODE_CONFIGURE_ARGS='openssl-no-asm' elif uname -a | grep -q 'Darwin.*x86_64'; then export BOXEDNODE_CONFIGURE_ARGS='--openssl-no-asm' From 5f694285dc5e14456f40532baa8b1547312244a0 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Tue, 4 Nov 2025 15:50:46 +0100 Subject: [PATCH 26/64] chore: make sure python 3.11 is used everywhere, also when compiling --- .evergreen.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.evergreen.yml b/.evergreen.yml index 2ea85e0..a3ab0c1 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -14,6 +14,11 @@ functions: set -e set -x + if [ "$OS" == "Windows_NT" ]; then + BASE_PYTHON="/cygdrive/c/python/Python311" + export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" + fi + export NODE_VERSION=22.17.0 bash .evergreen/install-node.sh install: @@ -25,6 +30,11 @@ functions: set -e set -x + if [ "$OS" == "Windows_NT" ]; then + BASE_PYTHON="/cygdrive/c/python/Python311" + export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" + fi + . .evergreen/use-node.sh npm install check: From 6a202b417e56449103d03a4a6c01d4d01bedd775 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Tue, 4 Nov 2025 16:14:51 +0100 Subject: [PATCH 27/64] chore: make sure that the hash is updated so it uses the proper python version --- .evergreen.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.evergreen.yml b/.evergreen.yml index a3ab0c1..047324a 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -17,6 +17,7 @@ functions: if [ "$OS" == "Windows_NT" ]; then BASE_PYTHON="/cygdrive/c/python/Python311" export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" + hash -r fi export NODE_VERSION=22.17.0 @@ -33,6 +34,7 @@ functions: if [ "$OS" == "Windows_NT" ]; then BASE_PYTHON="/cygdrive/c/python/Python311" export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" + hash -r fi . .evergreen/use-node.sh @@ -52,6 +54,7 @@ functions: if [ "$OS" == "Windows_NT" ]; then BASE_PYTHON="/cygdrive/c/python/Python311" export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" + hash -r fi npm run build npm run lint @@ -102,6 +105,7 @@ functions: BASE_PYTHON="/cygdrive/c/python/Python311" export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" export BOXEDNODE_CONFIGURE_ARGS='openssl-no-asm' + hash -r elif uname -a | grep -q 'Darwin.*x86_64'; then export BOXEDNODE_CONFIGURE_ARGS='--openssl-no-asm' fi From 4ce4d231cbdf3106663d17bc82838022eb2aa863 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Tue, 4 Nov 2025 17:03:04 +0100 Subject: [PATCH 28/64] chore: also setup python and python3 env vars for node-gyp --- .evergreen.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.evergreen.yml b/.evergreen.yml index 047324a..c85fe88 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -16,6 +16,8 @@ functions: if [ "$OS" == "Windows_NT" ]; then BASE_PYTHON="/cygdrive/c/python/Python311" + export PYTHON="$BASE_PYTHON/python.exe" + export PYTHON3="$PYTHON" export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" hash -r fi @@ -33,6 +35,8 @@ functions: if [ "$OS" == "Windows_NT" ]; then BASE_PYTHON="/cygdrive/c/python/Python311" + export PYTHON="$BASE_PYTHON/python.exe" + export PYTHON3="$PYTHON" export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" hash -r fi @@ -53,6 +57,8 @@ functions: . .evergreen/use-node.sh if [ "$OS" == "Windows_NT" ]; then BASE_PYTHON="/cygdrive/c/python/Python311" + export PYTHON="$BASE_PYTHON/python.exe" + export PYTHON3="$PYTHON" export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" hash -r fi @@ -103,6 +109,8 @@ functions: # so we revert back to the slower version. if [ "$OS" == "Windows_NT" ]; then BASE_PYTHON="/cygdrive/c/python/Python311" + export PYTHON="$BASE_PYTHON/python.exe" + export PYTHON3="$PYTHON" export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" export BOXEDNODE_CONFIGURE_ARGS='openssl-no-asm' hash -r From 0d457334532a5edd1b3160f1e802a369fd5ed48e Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Tue, 4 Nov 2025 17:14:24 +0100 Subject: [PATCH 29/64] chore: use NODE_GYP_FORCE_PYTHON --- .evergreen.yml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/.evergreen.yml b/.evergreen.yml index c85fe88..8d12a5e 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -16,8 +16,7 @@ functions: if [ "$OS" == "Windows_NT" ]; then BASE_PYTHON="/cygdrive/c/python/Python311" - export PYTHON="$BASE_PYTHON/python.exe" - export PYTHON3="$PYTHON" + export NODE_GYP_FORCE_PYTHON="$BASE_PYTHON/python.exe" export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" hash -r fi @@ -35,8 +34,7 @@ functions: if [ "$OS" == "Windows_NT" ]; then BASE_PYTHON="/cygdrive/c/python/Python311" - export PYTHON="$BASE_PYTHON/python.exe" - export PYTHON3="$PYTHON" + export NODE_GYP_FORCE_PYTHON="$BASE_PYTHON/python.exe" export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" hash -r fi @@ -57,8 +55,7 @@ functions: . .evergreen/use-node.sh if [ "$OS" == "Windows_NT" ]; then BASE_PYTHON="/cygdrive/c/python/Python311" - export PYTHON="$BASE_PYTHON/python.exe" - export PYTHON3="$PYTHON" + export NODE_GYP_FORCE_PYTHON="$BASE_PYTHON/python.exe" export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" hash -r fi @@ -109,11 +106,10 @@ functions: # so we revert back to the slower version. if [ "$OS" == "Windows_NT" ]; then BASE_PYTHON="/cygdrive/c/python/Python311" - export PYTHON="$BASE_PYTHON/python.exe" - export PYTHON3="$PYTHON" + export NODE_GYP_FORCE_PYTHON="$BASE_PYTHON/python.exe" export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" - export BOXEDNODE_CONFIGURE_ARGS='openssl-no-asm' hash -r + export BOXEDNODE_CONFIGURE_ARGS='openssl-no-asm' elif uname -a | grep -q 'Darwin.*x86_64'; then export BOXEDNODE_CONFIGURE_ARGS='--openssl-no-asm' fi From 729269d0054b3fbb8ff00804e692b4519bac5a9b Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Tue, 4 Nov 2025 17:23:17 +0100 Subject: [PATCH 30/64] chore: use windows path and simplify --- .evergreen.yml | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/.evergreen.yml b/.evergreen.yml index 8d12a5e..0626fe5 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -15,10 +15,8 @@ functions: set -x if [ "$OS" == "Windows_NT" ]; then - BASE_PYTHON="/cygdrive/c/python/Python311" - export NODE_GYP_FORCE_PYTHON="$BASE_PYTHON/python.exe" - export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" - hash -r + export NODE_GYP_FORCE_PYTHON="C:\python\Python311\python.exe" + export PATH="/cygdrive/c/python/Python311/Scripts:/cygdrive/c/python/Python311:$PATH" fi export NODE_VERSION=22.17.0 @@ -33,10 +31,8 @@ functions: set -x if [ "$OS" == "Windows_NT" ]; then - BASE_PYTHON="/cygdrive/c/python/Python311" - export NODE_GYP_FORCE_PYTHON="$BASE_PYTHON/python.exe" - export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" - hash -r + export NODE_GYP_FORCE_PYTHON="C:\python\Python311\python.exe" + export PATH="/cygdrive/c/python/Python311/Scripts:/cygdrive/c/python/Python311:$PATH" fi . .evergreen/use-node.sh @@ -52,13 +48,12 @@ functions: set -e set -x - . .evergreen/use-node.sh if [ "$OS" == "Windows_NT" ]; then - BASE_PYTHON="/cygdrive/c/python/Python311" - export NODE_GYP_FORCE_PYTHON="$BASE_PYTHON/python.exe" - export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" - hash -r + export NODE_GYP_FORCE_PYTHON="C:\python\Python311\python.exe" + export PATH="/cygdrive/c/python/Python311/Scripts:/cygdrive/c/python/Python311:$PATH" fi + + . .evergreen/use-node.sh npm run build npm run lint test: @@ -105,10 +100,8 @@ functions: # able to compile OpenSSL with assembly support, # so we revert back to the slower version. if [ "$OS" == "Windows_NT" ]; then - BASE_PYTHON="/cygdrive/c/python/Python311" - export NODE_GYP_FORCE_PYTHON="$BASE_PYTHON/python.exe" - export PATH="$BASE_PYTHON/Scripts:$BASE_PYTHON:$PATH" - hash -r + export NODE_GYP_FORCE_PYTHON="C:\python\Python311\python.exe" + export PATH="/cygdrive/c/python/Python311/Scripts:/cygdrive/c/python/Python311:$PATH" export BOXEDNODE_CONFIGURE_ARGS='openssl-no-asm' elif uname -a | grep -q 'Darwin.*x86_64'; then export BOXEDNODE_CONFIGURE_ARGS='--openssl-no-asm' From 8757467929a628a8088df3150222396851382642 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Tue, 4 Nov 2025 18:00:25 +0100 Subject: [PATCH 31/64] chore: normalise to spaces --- .evergreen/use-node.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.evergreen/use-node.sh b/.evergreen/use-node.sh index 90e5792..1e94cee 100644 --- a/.evergreen/use-node.sh +++ b/.evergreen/use-node.sh @@ -5,10 +5,10 @@ else export PATH="/opt/devtools/bin:$PATH" if [ "$(uname -s)" == "Darwin" ] ; then # in OSX use nvm - export NVM_DIR="$PWD/.deps/nvm" - [ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh" + export NVM_DIR="$PWD/.deps/nvm" + [ -s "$NVM_DIR/nvm.sh" ] && source "$NVM_DIR/nvm.sh" else # In Linux, use .deps/node/bin because it was set up with symlink to an existing node in the toolchain - export PATH="$PWD/.deps/node/bin:$PATH" + export PATH="$PWD/.deps/node/bin:$PATH" fi fi From b27af71bf5c2cdea13445b29d6248245eb69828d Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Mon, 10 Nov 2025 13:02:43 +0100 Subject: [PATCH 32/64] chore: untabify, use spaces always --- .evergreen/install-node.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.evergreen/install-node.sh b/.evergreen/install-node.sh index 2d7b581..ec5627e 100644 --- a/.evergreen/install-node.sh +++ b/.evergreen/install-node.sh @@ -26,15 +26,15 @@ if [[ "$OS" == "Windows_NT" ]]; then export PATH="$PWD/node/bin:$PATH" else if [ "$(uname -s)" == "Darwin" ] ; then # install Node.js on MacOS - curl -o- $NVM_URL | bash - set +x - [ -s "${NVM_DIR}/nvm.sh" ] && source "${NVM_DIR}/nvm.sh" - nvm install --no-progress "$NODE_VERSION" + curl -o- $NVM_URL | bash + set +x + [ -s "${NVM_DIR}/nvm.sh" ] && source "${NVM_DIR}/nvm.sh" + nvm install --no-progress "$NODE_VERSION" else # Linux already has its own toolchain in evergreen - mkdir -p node - NODE_MAJOR=$(echo $NODE_VERSION | awk -F . '{print $1}') - ln -s "/opt/devtools/node$NODE_MAJOR/bin/" "$PWD/node/bin" - export PATH="$PWD/node/bin:$PATH" + mkdir -p node + NODE_MAJOR=$(echo $NODE_VERSION | awk -F . '{print $1}') + ln -s "/opt/devtools/node$NODE_MAJOR/bin/" "$PWD/node/bin" + export PATH="$PWD/node/bin:$PATH" fi fi From a03f2fa011d61a02e603ee10ac9d153f54936235 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Mon, 10 Nov 2025 15:37:28 +0100 Subject: [PATCH 33/64] chore: setup VC environment and upgrade command args for vcbuild --- .evergreen.yml | 14 ++++++++++++-- .evergreen/test-in-vcdev-env.bat | 4 ++++ src/index.ts | 3 ++- 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 .evergreen/test-in-vcdev-env.bat diff --git a/.evergreen.yml b/.evergreen.yml index 0626fe5..3a75ae1 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -108,8 +108,18 @@ functions: fi . .evergreen/use-node.sh - npm run build - TEST_NODE_VERSION="$TEST_NODE_VERSION" npm run test-ci + export TEST_NODE_VERSION="$TEST_NODE_VERSION" + if [ "$OS" == "Windows_NT" ]; then + # The CI machines we have for Windows don't have a working + # installation of VS2022, (vswhere.exe can't find the correct path) + # So we run the scripts to set up the environment manually, and they + # only work properly in CMD + WIN_PATH=$(cygpath -aw .evergreen/test-in-vcdev-env.bat) + cmd.exe /c "$WIN_PATH" + else + npm run build + npm run test-ci + fi tasks: - name: test_n20 diff --git a/.evergreen/test-in-vcdev-env.bat b/.evergreen/test-in-vcdev-env.bat new file mode 100644 index 0000000..738f2da --- /dev/null +++ b/.evergreen/test-in-vcdev-env.bat @@ -0,0 +1,4 @@ +CALL "C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvars64.bat" + +npm run build +npm run test-ci diff --git a/src/index.ts b/src/index.ts index 8c49de1..980673d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -237,8 +237,9 @@ async function compileNode ( // These defaults got things to work locally. We only include them if no // conflicting arguments have been passed manually. - const vcbuildArgs: string[] = [...buildArgs, ...makeArgs, 'projgen']; + const vcbuildArgs: string[] = [...buildArgs, ...makeArgs]; if (!vcbuildArgs.includes('debug') && !vcbuildArgs.includes('release')) { vcbuildArgs.push('release'); } + if (!vcbuildArgs.includes('x86') && !vcbuildArgs.includes('x64')) { vcbuildArgs.push('x64'); } if (!vcbuildArgs.some((arg) => /^vs/.test(arg))) { vcbuildArgs.push('vs2022'); } for (const module of linkedJSModules) { From e51e0b3a46f156c739e89f6c76e5436ee81f8349 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Mon, 10 Nov 2025 15:43:10 +0100 Subject: [PATCH 34/64] chore: stub APPDATA env var --- .evergreen/test-in-vcdev-env.bat | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.evergreen/test-in-vcdev-env.bat b/.evergreen/test-in-vcdev-env.bat index 738f2da..ec0f24f 100644 --- a/.evergreen/test-in-vcdev-env.bat +++ b/.evergreen/test-in-vcdev-env.bat @@ -1,4 +1,6 @@ CALL "C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvars64.bat" +REM APPDATA is empty in CMD, and npm requires it to at least be empty +SET APPDATA="" npm run build npm run test-ci From c5e237462f4832ff2331c0df45a079d33efe103d Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Mon, 10 Nov 2025 16:12:16 +0100 Subject: [PATCH 35/64] chore: adapt it to use CMD properly --- .evergreen.yml | 2 +- .evergreen/test-in-vcdev-env.bat | 8 ++++---- .evergreen/use-node.sh | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.evergreen.yml b/.evergreen.yml index 3a75ae1..5aa9589 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -20,7 +20,7 @@ functions: fi export NODE_VERSION=22.17.0 - bash .evergreen/install-node.sh + . .evergreen/install-node.sh install: - command: shell.exec params: diff --git a/.evergreen/test-in-vcdev-env.bat b/.evergreen/test-in-vcdev-env.bat index ec0f24f..c24d0dd 100644 --- a/.evergreen/test-in-vcdev-env.bat +++ b/.evergreen/test-in-vcdev-env.bat @@ -1,6 +1,6 @@ CALL "C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvars64.bat" -REM APPDATA is empty in CMD, and npm requires it to at least be empty -SET APPDATA="" +REM APPDATA is empty in CMD, and npm requires it to be a valid path +SET APPDATA="%TEMP%\npm-cache" -npm run build -npm run test-ci +CALL npm run build +CALL npm run test-ci diff --git a/.evergreen/use-node.sh b/.evergreen/use-node.sh index 1e94cee..04d521e 100644 --- a/.evergreen/use-node.sh +++ b/.evergreen/use-node.sh @@ -1,4 +1,5 @@ if [[ "$OS" == "Windows_NT" ]]; then + export APPDATA="$TEMP/npm-cache" export PATH="$PWD/.deps/node/bin:$PATH" else # so we use the devtools binaries first (for gcc/g++) From 6d8d132b728704475bf5065bd6cffa95e8b26e4c Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Mon, 10 Nov 2025 16:21:54 +0100 Subject: [PATCH 36/64] chore: fix order of parameters --- src/index.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/index.ts b/src/index.ts index 980673d..344f7c3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -237,11 +237,14 @@ async function compileNode ( // These defaults got things to work locally. We only include them if no // conflicting arguments have been passed manually. - const vcbuildArgs: string[] = [...buildArgs, ...makeArgs]; - if (!vcbuildArgs.includes('debug') && !vcbuildArgs.includes('release')) { vcbuildArgs.push('release'); } - if (!vcbuildArgs.includes('x86') && !vcbuildArgs.includes('x64')) { vcbuildArgs.push('x64'); } - if (!vcbuildArgs.some((arg) => /^vs/.test(arg))) { vcbuildArgs.push('vs2022'); } + const passedArgs: string[] = [...buildArgs, ...makeArgs]; + const vcbuildArgs: string[] = []; + if (!passedArgs.includes('debug') && !passedArgs.includes('release')) { vcbuildArgs.push('release'); } + if (!passedArgs.includes('x86') && !passedArgs.includes('x64')) { vcbuildArgs.push('x64'); } + if (!passedArgs.some((arg) => /^vs/.test(arg))) { vcbuildArgs.push('vs2022'); } + + vcbuildArgs.push(...passedArgs); for (const module of linkedJSModules) { vcbuildArgs.push('link-module', module); } From 96e56d2b5d9ab67c9221a45dc68ead82d0114514 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Mon, 10 Nov 2025 16:27:30 +0100 Subject: [PATCH 37/64] chore: we still need to projgen --- src/index.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/index.ts b/src/index.ts index 344f7c3..31b26ca 100644 --- a/src/index.ts +++ b/src/index.ts @@ -237,14 +237,11 @@ async function compileNode ( // These defaults got things to work locally. We only include them if no // conflicting arguments have been passed manually. - const passedArgs: string[] = [...buildArgs, ...makeArgs]; - const vcbuildArgs: string[] = []; + const vcbuildArgs: string[] = [...buildArgs, ...makeArgs, 'projgen']; + if (!vcbuildArgs.includes('debug') && !vcbuildArgs.includes('release')) { vcbuildArgs.push('release'); } + if (!vcbuildArgs.includes('x86') && !vcbuildArgs.includes('x64')) { vcbuildArgs.push('x64'); } + if (!vcbuildArgs.some((arg) => /^vs/.test(arg))) { vcbuildArgs.push('vs2022'); } - if (!passedArgs.includes('debug') && !passedArgs.includes('release')) { vcbuildArgs.push('release'); } - if (!passedArgs.includes('x86') && !passedArgs.includes('x64')) { vcbuildArgs.push('x64'); } - if (!passedArgs.some((arg) => /^vs/.test(arg))) { vcbuildArgs.push('vs2022'); } - - vcbuildArgs.push(...passedArgs); for (const module of linkedJSModules) { vcbuildArgs.push('link-module', module); } From 3ce25c4123077def20bbd4067c5fbaa43066d9fc Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Mon, 10 Nov 2025 17:41:39 +0100 Subject: [PATCH 38/64] chore: do not use $TEMP because files there can be deleted --- .evergreen/test-in-vcdev-env.bat | 4 ++-- .evergreen/use-node.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.evergreen/test-in-vcdev-env.bat b/.evergreen/test-in-vcdev-env.bat index c24d0dd..19a74c0 100644 --- a/.evergreen/test-in-vcdev-env.bat +++ b/.evergreen/test-in-vcdev-env.bat @@ -1,6 +1,6 @@ CALL "C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvars64.bat" REM APPDATA is empty in CMD, and npm requires it to be a valid path -SET APPDATA="%TEMP%\npm-cache" +SET APPDATA="npm-cache" CALL npm run build -CALL npm run test-ci +CALL npm run test-ic diff --git a/.evergreen/use-node.sh b/.evergreen/use-node.sh index 04d521e..d7c5082 100644 --- a/.evergreen/use-node.sh +++ b/.evergreen/use-node.sh @@ -1,5 +1,5 @@ if [[ "$OS" == "Windows_NT" ]]; then - export APPDATA="$TEMP/npm-cache" + export APPDATA="npm-cache" export PATH="$PWD/.deps/node/bin:$PATH" else # so we use the devtools binaries first (for gcc/g++) From 5ae7b1c80cfbea240b730a4a2e5219c6b463e12c Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Mon, 10 Nov 2025 17:48:45 +0100 Subject: [PATCH 39/64] chore: typo :( --- .evergreen/test-in-vcdev-env.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.evergreen/test-in-vcdev-env.bat b/.evergreen/test-in-vcdev-env.bat index 19a74c0..56232e7 100644 --- a/.evergreen/test-in-vcdev-env.bat +++ b/.evergreen/test-in-vcdev-env.bat @@ -3,4 +3,4 @@ REM APPDATA is empty in CMD, and npm requires it to be a valid path SET APPDATA="npm-cache" CALL npm run build -CALL npm run test-ic +CALL npm run test-ci From 8190e6645d95933c36b8616205cb06a89b3fffc9 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Tue, 11 Nov 2025 12:56:08 +0100 Subject: [PATCH 40/64] chore: upgrade to node 24 (the LTS) and increase timeout The build in Windows is slower, so we increase the timeout to avoid issues related to slow compilation --- .evergreen.yml | 2 +- test/index.ts | 23 +++++++++-------------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/.evergreen.yml b/.evergreen.yml index 5aa9589..ec55a86 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -19,7 +19,7 @@ functions: export PATH="/cygdrive/c/python/Python311/Scripts:/cygdrive/c/python/Python311:$PATH" fi - export NODE_VERSION=22.17.0 + export NODE_VERSION=24.10.0 . .evergreen/install-node.sh install: - command: shell.exec diff --git a/test/index.ts b/test/index.ts index cefee5b..2db2e3c 100644 --- a/test/index.ts +++ b/test/index.ts @@ -11,6 +11,9 @@ import { promises as fs } from 'fs'; const execFile = promisify(childProcess.execFile); const exeSuffix = process.platform === 'win32' ? '.exe' : ''; +// we are using 4h because compiling in windows might take more time +const DEFAULT_TIMEOUT = 4 * 60 * 60 * 1000; + describe('basic functionality', () => { // Test the currently running Node.js version. Other versions can be checked // manually that way, or through the CI matrix. @@ -18,7 +21,7 @@ describe('basic functionality', () => { describe(`On Node v${version}`, function () { it('works in a simple case', async function () { - this.timeout(2 * 60 * 60 * 1000); // 2 hours + this.timeout(DEFAULT_TIMEOUT); await compileJSFileAsBinary({ nodeVersionRange: version, sourceFile: path.resolve(__dirname, 'resources/example.js'), @@ -112,11 +115,7 @@ describe('basic functionality', () => { }); it('works with a Nan addon', async function () { - if (semver.lt(version, '12.19.0')) { - return this.skip(); // no addon support available - } - - this.timeout(2 * 60 * 60 * 1000); // 2 hours + this.timeout(DEFAULT_TIMEOUT); await compileJSFileAsBinary({ nodeVersionRange: version, sourceFile: path.resolve(__dirname, 'resources/example.js'), @@ -139,11 +138,7 @@ describe('basic functionality', () => { }); it('works with a N-API addon', async function () { - if (semver.lt(version, '14.13.0')) { - return this.skip(); // no N-API addon support available - } - - this.timeout(2 * 60 * 60 * 1000); // 2 hours + this.timeout(DEFAULT_TIMEOUT); await compileJSFileAsBinary({ nodeVersionRange: version, sourceFile: path.resolve(__dirname, 'resources/example.js'), @@ -166,7 +161,7 @@ describe('basic functionality', () => { }); it('passes through env vars and runs the pre-compile hook', async function () { - this.timeout(2 * 60 * 60 * 1000); // 2 hours + this.timeout(DEFAULT_TIMEOUT); let ranPreCompileHook = false; async function preCompileHook (nodeSourceTree: string) { ranPreCompileHook = true; @@ -189,7 +184,7 @@ describe('basic functionality', () => { }); it('works with code caching support', async function () { - this.timeout(2 * 60 * 60 * 1000); // 2 hours + this.timeout(DEFAULT_TIMEOUT); await compileJSFileAsBinary({ nodeVersionRange: version, sourceFile: path.resolve(__dirname, 'resources/example.js'), @@ -216,7 +211,7 @@ describe('basic functionality', () => { for (const compressBlobs of [false, true]) { it(`works with snapshot support (compressBlobs = ${compressBlobs})`, async function () { - this.timeout(2 * 60 * 60 * 1000); // 2 hours + this.timeout(DEFAULT_TIMEOUT); await compileJSFileAsBinary({ nodeVersionRange: version, sourceFile: path.resolve(__dirname, 'resources/snapshot-echo-args.js'), From dc20888738e17e3172f31c1c54cdcf9860dc1312 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Tue, 11 Nov 2025 12:58:28 +0100 Subject: [PATCH 41/64] chore: use the same base in all machines (22.17.0) --- .evergreen.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.evergreen.yml b/.evergreen.yml index ec55a86..5aa9589 100644 --- a/.evergreen.yml +++ b/.evergreen.yml @@ -19,7 +19,7 @@ functions: export PATH="/cygdrive/c/python/Python311/Scripts:/cygdrive/c/python/Python311:$PATH" fi - export NODE_VERSION=24.10.0 + export NODE_VERSION=22.17.0 . .evergreen/install-node.sh install: - command: shell.exec From 71190c71ddad7d2e6b15e51a24bc7e49250087cc Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Tue, 11 Nov 2025 13:09:27 +0100 Subject: [PATCH 42/64] chore: setup windows tests also in GHA for faster feedback This also adds a baseline: if the tests without special setup work here, it should work also in evergreen hosts if properly configured. --- .github/workflows/nodejs.yml | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index b7c28dd..a69891c 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -16,9 +16,9 @@ jobs: node-version: [20.x, 22.x, 24.x] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v6 with: check-latest: true node-version: ${{ matrix.node-version }} @@ -26,3 +26,34 @@ jobs: run: npm install - name: Test run: npm test + + test-windows: + name: Windows tests + strategy: + fail-fast: false + matrix: + os: [windows-latest] + node-version: [20.x, 22.x, 24.x] + vs-version: ['17'] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v5 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v6 + with: + check-latest: true + node-version: ${{ matrix.node-version }} + - uses: microsoft/setup-msbuild@v2 + name: Setup MSBuild + with: + vs-version: ${{ matrix.vs-version }} + msbuild-architecture: x64 + - uses: ilammy/msvc-dev-cmd@v1 + name: Setup MSVC DevTools + with: + arch: x64 + - name: Install Dependencies + run: npm install + - name: Test + run: npm test + From 1d3b039f70cf28b62aa36a33db8c2fb5dd3ed721 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Tue, 11 Nov 2025 13:30:09 +0100 Subject: [PATCH 43/64] chore: remove unused deps and remove setup of MSVC DevTools In theory vcbuild.bat should detect where the MSVC DevTools are located (using vswhere.exe) and setup them correctly. We are going to verify this behaviour. --- .github/workflows/nodejs.yml | 6 +----- package.json | 1 - test/index.ts | 1 - 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index a69891c..e8ee73f 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -34,7 +34,7 @@ jobs: matrix: os: [windows-latest] node-version: [20.x, 22.x, 24.x] - vs-version: ['17'] + vs-version: ['17'] # 17 => VS2022 runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v5 @@ -48,10 +48,6 @@ jobs: with: vs-version: ${{ matrix.vs-version }} msbuild-architecture: x64 - - uses: ilammy/msvc-dev-cmd@v1 - name: Setup MSVC DevTools - with: - arch: x64 - name: Install Dependencies run: npm install - name: Test diff --git a/package.json b/package.json index 74d82ef..4c4ba89 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,6 @@ "node-gyp": "^11.5.0", "pkg-up": "^3.1.0", "rimraf": "^3.0.2", - "semver": "^7.3.2", "tar": "^6.0.5", "yargs": "^16.0.3" } diff --git a/test/index.ts b/test/index.ts index 2db2e3c..534f5ce 100644 --- a/test/index.ts +++ b/test/index.ts @@ -3,7 +3,6 @@ import path from 'path'; import os from 'os'; import assert from 'assert'; import childProcess from 'child_process'; -import semver from 'semver'; import { promisify } from 'util'; import pkgUp from 'pkg-up'; import { promises as fs } from 'fs'; From 6c2f8ece14cf7d8d58f20ccfe5bc8ca06db3b291 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Tue, 11 Nov 2025 18:04:04 +0100 Subject: [PATCH 44/64] chore: support all architecture flags --- src/index.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 31b26ca..4c29d19 100644 --- a/src/index.ts +++ b/src/index.ts @@ -239,7 +239,13 @@ async function compileNode ( // conflicting arguments have been passed manually. const vcbuildArgs: string[] = [...buildArgs, ...makeArgs, 'projgen']; if (!vcbuildArgs.includes('debug') && !vcbuildArgs.includes('release')) { vcbuildArgs.push('release'); } - if (!vcbuildArgs.includes('x86') && !vcbuildArgs.includes('x64')) { vcbuildArgs.push('x64'); } + if (!vcbuildArgs.includes('x86') + && !vcbuildArgs.includes('x64') + && !vcbuildArgs.includes('ia32') + && !vcbuildArgs.includes('arm64') + ) { + vcbuildArgs.push('x64'); + } if (!vcbuildArgs.some((arg) => /^vs/.test(arg))) { vcbuildArgs.push('vs2022'); } for (const module of linkedJSModules) { From 20b8394bcd8b828c80d0628db9c058ea3154c866 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Tue, 11 Nov 2025 19:18:02 +0100 Subject: [PATCH 45/64] chore: increase timeout (default is 6h, builds can take more time) --- .github/workflows/nodejs.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index e8ee73f..12d3a65 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -9,6 +9,7 @@ defaults: jobs: test-posix: name: Unix tests + timeout-minutes: 480 # 8h strategy: fail-fast: false matrix: @@ -29,6 +30,7 @@ jobs: test-windows: name: Windows tests + timeout-minutes: 480 # 8h strategy: fail-fast: false matrix: From 5c60c2933bd4e1517aea1212096a361c6b0b78b5 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Tue, 11 Nov 2025 19:22:23 +0100 Subject: [PATCH 46/64] chore: fix linter issues --- src/index.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/index.ts b/src/index.ts index 4c29d19..86db873 100644 --- a/src/index.ts +++ b/src/index.ts @@ -239,12 +239,12 @@ async function compileNode ( // conflicting arguments have been passed manually. const vcbuildArgs: string[] = [...buildArgs, ...makeArgs, 'projgen']; if (!vcbuildArgs.includes('debug') && !vcbuildArgs.includes('release')) { vcbuildArgs.push('release'); } - if (!vcbuildArgs.includes('x86') - && !vcbuildArgs.includes('x64') - && !vcbuildArgs.includes('ia32') - && !vcbuildArgs.includes('arm64') - ) { - vcbuildArgs.push('x64'); + if (!vcbuildArgs.includes('x86') && + !vcbuildArgs.includes('x64') && + !vcbuildArgs.includes('ia32') && + !vcbuildArgs.includes('arm64') + ) { + vcbuildArgs.push('x64'); } if (!vcbuildArgs.some((arg) => /^vs/.test(arg))) { vcbuildArgs.push('vs2022'); } From 2512e1a3da00ca1719506673dca8c807683ff21c Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Wed, 12 Nov 2025 11:04:33 +0100 Subject: [PATCH 47/64] chore: use DEBUG, as it's faster, and it's good enough for a safety check --- .github/workflows/nodejs.yml | 4 ++-- src/index.ts | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 12d3a65..bfe541d 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -9,7 +9,6 @@ defaults: jobs: test-posix: name: Unix tests - timeout-minutes: 480 # 8h strategy: fail-fast: false matrix: @@ -30,7 +29,6 @@ jobs: test-windows: name: Windows tests - timeout-minutes: 480 # 8h strategy: fail-fast: false matrix: @@ -53,5 +51,7 @@ jobs: - name: Install Dependencies run: npm install - name: Test + env: + BOXEDNODE_MAKE_ARGS: "debug" run: npm test diff --git a/src/index.ts b/src/index.ts index 86db873..9340a51 100644 --- a/src/index.ts +++ b/src/index.ts @@ -251,9 +251,13 @@ async function compileNode ( for (const module of linkedJSModules) { vcbuildArgs.push('link-module', module); } - await spawnBuildCommand(['cmd', '/c', '.\\vcbuild.bat', ...vcbuildArgs], options); - return path.join(sourcePath, 'Release', 'node.exe'); + await spawnBuildCommand(['cmd', '/c', '.\\vcbuild.bat', ...vcbuildArgs], options); + if (vcbuildArgs.includes('debug')) { + return path.join(sourcePath, 'Debug', 'node.exe'); + } else { + return path.join(sourcePath, 'Release', 'node.exe'); + } } } From 484525b9a0b27f578045cca18c0042c2f3f33c2c Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Wed, 12 Nov 2025 14:35:03 +0100 Subject: [PATCH 48/64] chore: split each test in it's own job to avoid disk space issues --- .github/workflows/nodejs.yml | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index bfe541d..91df729 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -14,6 +14,13 @@ jobs: matrix: os: [ubuntu-latest] node-version: [20.x, 22.x, 24.x] + test-to-run: + - "works in a simple case" + - "works with a Nan addon" + - "works with a N-API addon" + - "passes through env vars and runs the pre-compile hook" + - "works with code caching support" + - "works with snapshot support" runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v5 @@ -21,11 +28,16 @@ jobs: uses: actions/setup-node@v6 with: check-latest: true + cache: npm node-version: ${{ matrix.node-version }} - name: Install Dependencies run: npm install + - name: Lint + run: npm run lint + - name: Build + run: npm run build - name: Test - run: npm test + run: npm run test-ci -- -g "${{ matrix.test-to-run }}" test-windows: name: Windows tests @@ -35,6 +47,13 @@ jobs: os: [windows-latest] node-version: [20.x, 22.x, 24.x] vs-version: ['17'] # 17 => VS2022 + test-to-run: + - "works in a simple case" + - "works with a Nan addon" + - "works with a N-API addon" + - "passes through env vars and runs the pre-compile hook" + - "works with code caching support" + - "works with snapshot support" runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v5 @@ -42,6 +61,7 @@ jobs: uses: actions/setup-node@v6 with: check-latest: true + cache: npm node-version: ${{ matrix.node-version }} - uses: microsoft/setup-msbuild@v2 name: Setup MSBuild @@ -50,8 +70,12 @@ jobs: msbuild-architecture: x64 - name: Install Dependencies run: npm install + - name: Lint + run: npm run lint + - name: Build + run: npm run build - name: Test + run: npm run test-ci -- -g "${{ matrix.test-to-run }}" env: BOXEDNODE_MAKE_ARGS: "debug" - run: npm test From 600635a1789520d45fae71a464b81bb08d14ae03 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Wed, 12 Nov 2025 14:40:24 +0100 Subject: [PATCH 49/64] chore: we can't use cache: npm because we don't have a package-lock --- .github/workflows/nodejs.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 91df729..af6f8e1 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -28,7 +28,6 @@ jobs: uses: actions/setup-node@v6 with: check-latest: true - cache: npm node-version: ${{ matrix.node-version }} - name: Install Dependencies run: npm install @@ -61,7 +60,6 @@ jobs: uses: actions/setup-node@v6 with: check-latest: true - cache: npm node-version: ${{ matrix.node-version }} - uses: microsoft/setup-msbuild@v2 name: Setup MSBuild From 60aacaff370993f14d99141d93bf90845f9ca6d9 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Wed, 12 Nov 2025 17:55:27 +0100 Subject: [PATCH 50/64] chore: delete precompiled headers on windows and node24> Compiling with a snapshot requires compiling twice: a base image and the image with the snapshot embedded. In that situation, clang-cl complains that there are precompiled headers that changed between compilations and does not refresh them, but kills the compilation process with an error. Due to this, before attempting the second compilation, we will delete all pch files. --- src/helpers.ts | 21 +++++++++++++++++++++ src/index.ts | 13 ++++++++++++- test/index.ts | 12 ++++++++++++ test/pchfixture/delete_1.pch | 0 test/pchfixture/inner/delete_2.pch | 0 test/pchfixture/no_delete.h | 0 6 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 test/pchfixture/delete_1.pch create mode 100644 test/pchfixture/inner/delete_2.pch create mode 100644 test/pchfixture/no_delete.h diff --git a/src/helpers.ts b/src/helpers.ts index 83fd6d9..3db957c 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -1,4 +1,5 @@ import { promises as fs } from 'fs'; +import path from 'path'; import { Logger } from './logger'; import crypto from 'crypto'; import childProcess from 'child_process'; @@ -77,6 +78,26 @@ export function npm (): string[] { } } +export async function deletePrecompiledHeadersInFolder (folder: string, { dryRun }: { dryRun: boolean }): Promise { + const files = await fs.readdir(folder); + const result: string[] = []; + + for (const file of files) { + const absolutePath = path.join(folder, file); + const stat = await fs.lstat(absolutePath); + if (stat.isDirectory()) { + const deletedFiles = await deletePrecompiledHeadersInFolder(absolutePath, { dryRun }); + result.push(...deletedFiles); + } else if (absolutePath.endsWith('.pch')) { + if (!dryRun) { + await fs.unlink(absolutePath); + } + result.push(absolutePath); + } + } + return result; +} + export function createCppJsStringDefinition (fnName: string, source: string): string { if (!source.length) { return `Local ${fnName}(Isolate* isolate) { return String::Empty(isolate); }`; diff --git a/src/index.ts b/src/index.ts index 9340a51..b9709dc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,7 +11,7 @@ import { promisify } from 'util'; import { promises as fs, createReadStream, createWriteStream } from 'fs'; import { AddonConfig, loadGYPConfig, storeGYPConfig, modifyAddonGyp } from './native-addons'; import { ExecutableMetadata, generateRCFile } from './executable-metadata'; -import { spawnBuildCommand, ProcessEnv, pipeline, createCppJsStringDefinition, createCompressedBlobDefinition, createUncompressedBlobDefinition } from './helpers'; +import { spawnBuildCommand, ProcessEnv, pipeline, createCppJsStringDefinition, createCompressedBlobDefinition, createUncompressedBlobDefinition, deletePrecompiledHeadersInFolder } from './helpers'; import { Readable } from 'stream'; import nv from '@pkgjs/nv'; import { fileURLToPath, URL } from 'url'; @@ -466,6 +466,17 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L throw new Error('Empty code cache/snapshot result'); } logger.stepCompleted(); + if (process.platform === 'win32' && nodeVersion[0] >= 24) { + // Compiling with a snapshot requires compiling twice: a base image + // and the image with the snapshot embedded. In that situation, clang-cl + // complains that there are precompiled headers that changed between + // compilations and does not refresh them, but kills the compilation + // process with an error. Due to this, before attempting the second + // compilation, we will delete all pch files. + logger.stepStarting('(win32) Deleting precompiled headers'); + await deletePrecompiledHeadersInFolder(nodeSourcePath, { dryRun: false }); + logger.stepCompleted(); + } binaryPath = await writeMainFileAndCompile(options.useNodeSnapshot ? { snapshotBlob: result, snapshotMode: 'consume' diff --git a/test/index.ts b/test/index.ts index 534f5ce..96513a9 100644 --- a/test/index.ts +++ b/test/index.ts @@ -6,6 +6,7 @@ import childProcess from 'child_process'; import { promisify } from 'util'; import pkgUp from 'pkg-up'; import { promises as fs } from 'fs'; +import { deletePrecompiledHeadersInFolder } from '../src/helpers'; const execFile = promisify(childProcess.execFile); const exeSuffix = process.platform === 'win32' ? '.exe' : ''; @@ -13,6 +14,17 @@ const exeSuffix = process.platform === 'win32' ? '.exe' : ''; // we are using 4h because compiling in windows might take more time const DEFAULT_TIMEOUT = 4 * 60 * 60 * 1000; +describe('deletePrecompiledHeadersInFolder', async function () { + it('deletes pch files recursively', async function () { + const pchfixture = path.join(__dirname, 'pchfixture'); + const deletedAbsolutePaths = await deletePrecompiledHeadersInFolder(pchfixture, { dryRun: true }); + const deletedFiles = deletedAbsolutePaths.map(absPath => path.relative(pchfixture, absPath)); + deletedFiles.sort(); + + assert(JSON.stringify(deletedFiles) === JSON.stringify(['delete_1.pch', 'inner/delete_2.pch'])); + }); +}); + describe('basic functionality', () => { // Test the currently running Node.js version. Other versions can be checked // manually that way, or through the CI matrix. diff --git a/test/pchfixture/delete_1.pch b/test/pchfixture/delete_1.pch new file mode 100644 index 0000000..e69de29 diff --git a/test/pchfixture/inner/delete_2.pch b/test/pchfixture/inner/delete_2.pch new file mode 100644 index 0000000..e69de29 diff --git a/test/pchfixture/no_delete.h b/test/pchfixture/no_delete.h new file mode 100644 index 0000000..e69de29 From afb5e0fea0fbf5768d633369118f81c84f967f4f Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Thu, 13 Nov 2025 12:34:22 +0100 Subject: [PATCH 51/64] chore: Remove test, now the behaviour is tested by the compiler This test was written so we could speed up the feedback loop. --- src/helpers.ts | 8 +++----- src/index.ts | 2 +- test/index.ts | 12 ------------ 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/src/helpers.ts b/src/helpers.ts index 3db957c..ca3e3b3 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -78,7 +78,7 @@ export function npm (): string[] { } } -export async function deletePrecompiledHeadersInFolder (folder: string, { dryRun }: { dryRun: boolean }): Promise { +export async function deletePrecompiledHeadersInFolder (folder: string): Promise { const files = await fs.readdir(folder); const result: string[] = []; @@ -86,12 +86,10 @@ export async function deletePrecompiledHeadersInFolder (folder: string, { dryRun const absolutePath = path.join(folder, file); const stat = await fs.lstat(absolutePath); if (stat.isDirectory()) { - const deletedFiles = await deletePrecompiledHeadersInFolder(absolutePath, { dryRun }); + const deletedFiles = await deletePrecompiledHeadersInFolder(absolutePath); result.push(...deletedFiles); } else if (absolutePath.endsWith('.pch')) { - if (!dryRun) { - await fs.unlink(absolutePath); - } + await fs.unlink(absolutePath); result.push(absolutePath); } } diff --git a/src/index.ts b/src/index.ts index b9709dc..b4bfab4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -474,7 +474,7 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L // process with an error. Due to this, before attempting the second // compilation, we will delete all pch files. logger.stepStarting('(win32) Deleting precompiled headers'); - await deletePrecompiledHeadersInFolder(nodeSourcePath, { dryRun: false }); + await deletePrecompiledHeadersInFolder(nodeSourcePath); logger.stepCompleted(); } binaryPath = await writeMainFileAndCompile(options.useNodeSnapshot ? { diff --git a/test/index.ts b/test/index.ts index 96513a9..534f5ce 100644 --- a/test/index.ts +++ b/test/index.ts @@ -6,7 +6,6 @@ import childProcess from 'child_process'; import { promisify } from 'util'; import pkgUp from 'pkg-up'; import { promises as fs } from 'fs'; -import { deletePrecompiledHeadersInFolder } from '../src/helpers'; const execFile = promisify(childProcess.execFile); const exeSuffix = process.platform === 'win32' ? '.exe' : ''; @@ -14,17 +13,6 @@ const exeSuffix = process.platform === 'win32' ? '.exe' : ''; // we are using 4h because compiling in windows might take more time const DEFAULT_TIMEOUT = 4 * 60 * 60 * 1000; -describe('deletePrecompiledHeadersInFolder', async function () { - it('deletes pch files recursively', async function () { - const pchfixture = path.join(__dirname, 'pchfixture'); - const deletedAbsolutePaths = await deletePrecompiledHeadersInFolder(pchfixture, { dryRun: true }); - const deletedFiles = deletedAbsolutePaths.map(absPath => path.relative(pchfixture, absPath)); - deletedFiles.sort(); - - assert(JSON.stringify(deletedFiles) === JSON.stringify(['delete_1.pch', 'inner/delete_2.pch'])); - }); -}); - describe('basic functionality', () => { // Test the currently running Node.js version. Other versions can be checked // manually that way, or through the CI matrix. From 33cba5b1f1a62733f6e13edf5130302317ca51cf Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Thu, 13 Nov 2025 12:45:18 +0100 Subject: [PATCH 52/64] chore: use rimraf instead of custom code, it should be easier to maintain --- src/helpers.ts | 18 ------------------ src/index.ts | 8 ++++---- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/src/helpers.ts b/src/helpers.ts index ca3e3b3..85095bc 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -78,24 +78,6 @@ export function npm (): string[] { } } -export async function deletePrecompiledHeadersInFolder (folder: string): Promise { - const files = await fs.readdir(folder); - const result: string[] = []; - - for (const file of files) { - const absolutePath = path.join(folder, file); - const stat = await fs.lstat(absolutePath); - if (stat.isDirectory()) { - const deletedFiles = await deletePrecompiledHeadersInFolder(absolutePath); - result.push(...deletedFiles); - } else if (absolutePath.endsWith('.pch')) { - await fs.unlink(absolutePath); - result.push(absolutePath); - } - } - return result; -} - export function createCppJsStringDefinition (fnName: string, source: string): string { if (!source.length) { return `Local ${fnName}(Isolate* isolate) { return String::Empty(isolate); }`; diff --git a/src/index.ts b/src/index.ts index b4bfab4..c31e2d6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,7 +11,7 @@ import { promisify } from 'util'; import { promises as fs, createReadStream, createWriteStream } from 'fs'; import { AddonConfig, loadGYPConfig, storeGYPConfig, modifyAddonGyp } from './native-addons'; import { ExecutableMetadata, generateRCFile } from './executable-metadata'; -import { spawnBuildCommand, ProcessEnv, pipeline, createCppJsStringDefinition, createCompressedBlobDefinition, createUncompressedBlobDefinition, deletePrecompiledHeadersInFolder } from './helpers'; +import { spawnBuildCommand, ProcessEnv, pipeline, createCppJsStringDefinition, createCompressedBlobDefinition, createUncompressedBlobDefinition } from './helpers'; import { Readable } from 'stream'; import nv from '@pkgjs/nv'; import { fileURLToPath, URL } from 'url'; @@ -473,8 +473,8 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L // compilations and does not refresh them, but kills the compilation // process with an error. Due to this, before attempting the second // compilation, we will delete all pch files. - logger.stepStarting('(win32) Deleting precompiled headers'); - await deletePrecompiledHeadersInFolder(nodeSourcePath); + logger.stepStarting('(win32) Deleting precompiled headers'); + await promisify(rimraf)(`${nodeSourcePath}/**/*.pch`, { glob: true }); logger.stepCompleted(); } binaryPath = await writeMainFileAndCompile(options.useNodeSnapshot ? { @@ -492,7 +492,7 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L logger.stepCompleted(); if (options.clean) { - logger.stepStarting('Cleaning temporary directory'); + logger.stepStarting('Cleaning temporary directory'); await promisify(rimraf)(options.tmpdir, { glob: false }); logger.stepCompleted(); } From 5e2b32bd3ce957e118c70fe6e79a6421b83e67f7 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Thu, 13 Nov 2025 13:12:52 +0100 Subject: [PATCH 53/64] chore: Generate the GHA workflow from a template Because we can keep adding more tests, maintaining the GHA workflow can become a bit cumbersome. This script generates the GHA file from a template, and adds all the test cases in a matrix. We do this because each test is expensive in time and disk space, so we do sometimes hit the GHA limit of 6h or the we fill the disk. --- .github/workflows/nodejs.yml | 20 +++------- .github/workflows/nodejs.yml.in | 67 +++++++++++++++++++++++++++++++++ package.json | 3 +- scripts/update-gha-workflow.sh | 36 ++++++++++++++++++ 4 files changed, 111 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/nodejs.yml.in create mode 100755 scripts/update-gha-workflow.sh diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index af6f8e1..7cc5737 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -1,3 +1,7 @@ +# This is a generated file. Please change .github/workflows/nodejs.yml.in +# and run the following command to update the GHA Workflow +# $> npm run update-gha-workflow +# -------------------- on: [pull_request] name: CI @@ -14,13 +18,7 @@ jobs: matrix: os: [ubuntu-latest] node-version: [20.x, 22.x, 24.x] - test-to-run: - - "works in a simple case" - - "works with a Nan addon" - - "works with a N-API addon" - - "passes through env vars and runs the pre-compile hook" - - "works with code caching support" - - "works with snapshot support" + test-to-run: ["works in a simple case","works with a Nan addon","works with a N-API addon","passes through env vars and runs the pre-compile hook","works with code caching support","works with snapshot support (compressBlobs = "] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v5 @@ -46,13 +44,7 @@ jobs: os: [windows-latest] node-version: [20.x, 22.x, 24.x] vs-version: ['17'] # 17 => VS2022 - test-to-run: - - "works in a simple case" - - "works with a Nan addon" - - "works with a N-API addon" - - "passes through env vars and runs the pre-compile hook" - - "works with code caching support" - - "works with snapshot support" + test-to-run: ["works in a simple case","works with a Nan addon","works with a N-API addon","passes through env vars and runs the pre-compile hook","works with code caching support","works with snapshot support (compressBlobs = "] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v5 diff --git a/.github/workflows/nodejs.yml.in b/.github/workflows/nodejs.yml.in new file mode 100644 index 0000000..2939321 --- /dev/null +++ b/.github/workflows/nodejs.yml.in @@ -0,0 +1,67 @@ +on: [pull_request] + +name: CI + +defaults: + run: + shell: bash + +jobs: + test-posix: + name: Unix tests + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + node-version: [20.x, 22.x, 24.x] + test-to-run: <> + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v5 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v6 + with: + check-latest: true + node-version: ${{ matrix.node-version }} + - name: Install Dependencies + run: npm install + - name: Lint + run: npm run lint + - name: Build + run: npm run build + - name: Test + run: npm run test-ci -- -g "${{ matrix.test-to-run }}" + + test-windows: + name: Windows tests + strategy: + fail-fast: false + matrix: + os: [windows-latest] + node-version: [20.x, 22.x, 24.x] + vs-version: ['17'] # 17 => VS2022 + test-to-run: <> + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v5 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v6 + with: + check-latest: true + node-version: ${{ matrix.node-version }} + - uses: microsoft/setup-msbuild@v2 + name: Setup MSBuild + with: + vs-version: ${{ matrix.vs-version }} + msbuild-architecture: x64 + - name: Install Dependencies + run: npm install + - name: Lint + run: npm run lint + - name: Build + run: npm run build + - name: Test + run: npm run test-ci -- -g "${{ matrix.test-to-run }}" + env: + BOXEDNODE_MAKE_ARGS: "debug" + diff --git a/package.json b/package.json index 4c4ba89..bdfbb55 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "test-ci": "nyc mocha --colors -r ts-node/register test/*.ts", "build": "npm run compile-ts && gen-esm-wrapper . ./.esm-wrapper.mjs", "prepack": "npm run build", - "compile-ts": "tsc -p tsconfig.json" + "compile-ts": "tsc -p tsconfig.json", + "update-gha-workflow": "./scripts/update-gha-workflow.sh" }, "keywords": [ "node.js", diff --git a/scripts/update-gha-workflow.sh b/scripts/update-gha-workflow.sh new file mode 100755 index 0000000..874b2d1 --- /dev/null +++ b/scripts/update-gha-workflow.sh @@ -0,0 +1,36 @@ +#/bin/bash + +set -e + +TEMPLATE_FILE=.github/workflows/nodejs.yml.in +OUTPUT_FILE=.github/workflows/nodejs.yml +TEST_SUITE=test/index.ts + +rm $OUTPUT_FILE + +echo "# This is a generated file. Please change $TEMPLATE_FILE" > $OUTPUT_FILE +echo "# and run the following command to update the GHA Workflow" >> $OUTPUT_FILE +echo "# $> npm run update-gha-workflow" >> $OUTPUT_FILE +echo "# --------------------" >> $OUTPUT_FILE + +# This AWK script seems complicated, but it's actually really simple: +# 1. It grabs all the lines with the "it" function +# 2. Removes quotes (single quotes and backticks) +# 3. If it contains a $, assumes it's a literal template, so keeps everything before the $ (for name matching) +# 4. Iterates over all matches and generates a single-line JSON array. +RESULT=$(awk ' +/it\(/ { + s = $0 + sub(/.*it\(\s*['\''`"]/, "", s) + if (s ~ /\$/) sub(/\$.*/, "", s) + sub(/['\''`"].*/, "", s) + names[++n] = s +} +END { + printf "[" + for (i = 1; i <= n; i++) { printf "\"%s\"%s", names[i], (i < n ? "," : "") } + print "]" +} +' $TEST_SUITE) + +sed "s/<>/$RESULT/g" $TEMPLATE_FILE >> $OUTPUT_FILE From 14075a2e1328c2cd55c42d82df6956e54d16bf95 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Thu, 13 Nov 2025 14:40:35 +0100 Subject: [PATCH 54/64] chore: Fix linter issues (spacing and unused deps) --- src/helpers.ts | 1 - src/index.ts | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/helpers.ts b/src/helpers.ts index 85095bc..83fd6d9 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -1,5 +1,4 @@ import { promises as fs } from 'fs'; -import path from 'path'; import { Logger } from './logger'; import crypto from 'crypto'; import childProcess from 'child_process'; diff --git a/src/index.ts b/src/index.ts index c31e2d6..83ed04b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -473,8 +473,8 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L // compilations and does not refresh them, but kills the compilation // process with an error. Due to this, before attempting the second // compilation, we will delete all pch files. - logger.stepStarting('(win32) Deleting precompiled headers'); - await promisify(rimraf)(`${nodeSourcePath}/**/*.pch`, { glob: true }); + logger.stepStarting('(win32) Deleting precompiled headers'); + await promisify(rimraf)(`${nodeSourcePath}/**/*.pch`, { glob: true }); logger.stepCompleted(); } binaryPath = await writeMainFileAndCompile(options.useNodeSnapshot ? { @@ -492,7 +492,7 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L logger.stepCompleted(); if (options.clean) { - logger.stepStarting('Cleaning temporary directory'); + logger.stepStarting('Cleaning temporary directory'); await promisify(rimraf)(options.tmpdir, { glob: false }); logger.stepCompleted(); } From 9be677b3988df95d84185cc1e963d8f32087171a Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Thu, 13 Nov 2025 14:45:09 +0100 Subject: [PATCH 55/64] chore: use -f instead of -g for the test runner It seems that -g expects a regular expression, and if the regular expression is invalid, mocha just fails with Error: null. Using -f, uses a substring comparison, which is what we need. --- .github/workflows/nodejs.yml | 6 +++--- .github/workflows/nodejs.yml.in | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 7cc5737..96f4400 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -34,7 +34,7 @@ jobs: - name: Build run: npm run build - name: Test - run: npm run test-ci -- -g "${{ matrix.test-to-run }}" + run: npm run test-ci -- -f "${{ matrix.test-to-run }}" test-windows: name: Windows tests @@ -65,7 +65,7 @@ jobs: - name: Build run: npm run build - name: Test - run: npm run test-ci -- -g "${{ matrix.test-to-run }}" + run: npm run test-ci -- -f "${{ matrix.test-to-run }}" env: BOXEDNODE_MAKE_ARGS: "debug" - + diff --git a/.github/workflows/nodejs.yml.in b/.github/workflows/nodejs.yml.in index 2939321..579c3ce 100644 --- a/.github/workflows/nodejs.yml.in +++ b/.github/workflows/nodejs.yml.in @@ -30,7 +30,7 @@ jobs: - name: Build run: npm run build - name: Test - run: npm run test-ci -- -g "${{ matrix.test-to-run }}" + run: npm run test-ci -- -f "${{ matrix.test-to-run }}" test-windows: name: Windows tests @@ -61,7 +61,7 @@ jobs: - name: Build run: npm run build - name: Test - run: npm run test-ci -- -g "${{ matrix.test-to-run }}" + run: npm run test-ci -- -f "${{ matrix.test-to-run }}" env: BOXEDNODE_MAKE_ARGS: "debug" From fda3cca061f56aa502a40eb60a1e0ad706863382 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Thu, 13 Nov 2025 14:54:13 +0100 Subject: [PATCH 56/64] chore: Remove pch files from compilation artifacts in tmp directory --- src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.ts b/src/index.ts index 83ed04b..3e0584c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -475,6 +475,7 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L // compilation, we will delete all pch files. logger.stepStarting('(win32) Deleting precompiled headers'); await promisify(rimraf)(`${nodeSourcePath}/**/*.pch`, { glob: true }); + await promisify(rimraf)(`${options.tmpdir}/**/*.pch`, { glob: true }); logger.stepCompleted(); } binaryPath = await writeMainFileAndCompile(options.useNodeSnapshot ? { From 4f31b14e2176faeff14f9aa991d172d2fe8f7382 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Thu, 13 Nov 2025 17:23:03 +0100 Subject: [PATCH 57/64] chore: Print the folder where we are deleting pchs from --- src/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 3e0584c..4ffecf0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -473,8 +473,10 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L // compilations and does not refresh them, but kills the compilation // process with an error. Due to this, before attempting the second // compilation, we will delete all pch files. - logger.stepStarting('(win32) Deleting precompiled headers'); + logger.stepStarting(`(win32) Deleting precompiled headers at ${nodeSourcePath}`); await promisify(rimraf)(`${nodeSourcePath}/**/*.pch`, { glob: true }); + logger.stepCompleted(); + logger.stepStarting(`(win32) Deleting precompiled headers at ${options.tmpdir}`); await promisify(rimraf)(`${options.tmpdir}/**/*.pch`, { glob: true }); logger.stepCompleted(); } From a5954aa998eb10449b7f340b4be89cee3e9a39f9 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Fri, 14 Nov 2025 02:28:19 +0100 Subject: [PATCH 58/64] chore: attempt to upgrade rimraf to latest version --- package.json | 2 +- src/index.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index bdfbb55..85f65f6 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "node-fetch": "^2.6.1", "node-gyp": "^11.5.0", "pkg-up": "^3.1.0", - "rimraf": "^3.0.2", + "rimraf": "^6.1.0", "tar": "^6.0.5", "yargs": "^16.0.3" } diff --git a/src/index.ts b/src/index.ts index 4ffecf0..d6c4fc5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ import tar from 'tar'; import path from 'path'; import zlib from 'zlib'; import os from 'os'; -import rimraf from 'rimraf'; +import { rimraf } from 'rimraf'; import crypto from 'crypto'; import { promisify } from 'util'; import { promises as fs, createReadStream, createWriteStream } from 'fs'; @@ -474,10 +474,10 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L // process with an error. Due to this, before attempting the second // compilation, we will delete all pch files. logger.stepStarting(`(win32) Deleting precompiled headers at ${nodeSourcePath}`); - await promisify(rimraf)(`${nodeSourcePath}/**/*.pch`, { glob: true }); + await rimraf(`${nodeSourcePath}/**/*.pch`, { glob: true }); logger.stepCompleted(); logger.stepStarting(`(win32) Deleting precompiled headers at ${options.tmpdir}`); - await promisify(rimraf)(`${options.tmpdir}/**/*.pch`, { glob: true }); + await rimraf(`${options.tmpdir}/**/*.pch`, { glob: true }); logger.stepCompleted(); } binaryPath = await writeMainFileAndCompile(options.useNodeSnapshot ? { @@ -496,7 +496,7 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L if (options.clean) { logger.stepStarting('Cleaning temporary directory'); - await promisify(rimraf)(options.tmpdir, { glob: false }); + await rimraf(options.tmpdir, { glob: false }); logger.stepCompleted(); } } From a199a1c672ac9250db28f67a6789daa607a797c0 Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Fri, 14 Nov 2025 02:36:21 +0100 Subject: [PATCH 59/64] chore: Use follow: true, so it follows symlinks and junctions --- src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index d6c4fc5..13f83ca 100644 --- a/src/index.ts +++ b/src/index.ts @@ -474,10 +474,10 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L // process with an error. Due to this, before attempting the second // compilation, we will delete all pch files. logger.stepStarting(`(win32) Deleting precompiled headers at ${nodeSourcePath}`); - await rimraf(`${nodeSourcePath}/**/*.pch`, { glob: true }); + await rimraf(`${nodeSourcePath}/**/*.pch`, { glob: { follow: true, nodir: true } }); logger.stepCompleted(); logger.stepStarting(`(win32) Deleting precompiled headers at ${options.tmpdir}`); - await rimraf(`${options.tmpdir}/**/*.pch`, { glob: true }); + await rimraf(`${options.tmpdir}/**/*.pch`, { glob: { follow: true, nodir: true } }); logger.stepCompleted(); } binaryPath = await writeMainFileAndCompile(options.useNodeSnapshot ? { From 9a03778ffb44b7ac911caf56bb5b3c1697f7e7dc Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Fri, 14 Nov 2025 14:26:19 +0100 Subject: [PATCH 60/64] chore: resolve the realpath of nodeSourcePath before deleting pchs nodeSourcePath in Windows refers to a user directory that contains an alias. For example, something like C:\Users\RUNNER~1 (in GHA) while MSVC uses the full name like C:\Users\runneradmin. This can make rimraf not delete the pch files properly. By resolving first, we make sure that both paths are absolute and unambiguous. --- src/index.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index 13f83ca..89ebbb0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -473,11 +473,9 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L // compilations and does not refresh them, but kills the compilation // process with an error. Due to this, before attempting the second // compilation, we will delete all pch files. - logger.stepStarting(`(win32) Deleting precompiled headers at ${nodeSourcePath}`); - await rimraf(`${nodeSourcePath}/**/*.pch`, { glob: { follow: true, nodir: true } }); - logger.stepCompleted(); - logger.stepStarting(`(win32) Deleting precompiled headers at ${options.tmpdir}`); - await rimraf(`${options.tmpdir}/**/*.pch`, { glob: { follow: true, nodir: true } }); + const resolvedNodeSourcePath = (await fs.realpath(nodeSourcePath)).replace(/\\/g, '/'); + logger.stepStarting(`(win32) Deleting precompiled headers at ${resolvedNodeSourcePath}`); + await rimraf(`${resolvedNodeSourcePath}/**/*.pch`, { glob: { follow: true, nodir: true } }); logger.stepCompleted(); } binaryPath = await writeMainFileAndCompile(options.useNodeSnapshot ? { From 5b9d8c2b646249596ed698f6e744d55fcb03cabc Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Mon, 17 Nov 2025 14:52:02 +0100 Subject: [PATCH 61/64] chore: Remove all directories that can contain compilation outputs clang-cl does not properly handle pch refresh when a source header changed after the pch file was generated --- src/index.ts | 26 +++++++------------------- test/pchfixture/delete_1.pch | 0 test/pchfixture/inner/delete_2.pch | 0 test/pchfixture/no_delete.h | 0 4 files changed, 7 insertions(+), 19 deletions(-) delete mode 100644 test/pchfixture/delete_1.pch delete mode 100644 test/pchfixture/inner/delete_2.pch delete mode 100644 test/pchfixture/no_delete.h diff --git a/src/index.ts b/src/index.ts index 89ebbb0..d43fecd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -352,25 +352,11 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L enableBindingsPatch })); - /** - * Since Node 20.x, external source code linked from `lib` directory started - * failing the Node.js build process because of the file being linked multiple - * times which is why we do not link the external files anymore from `lib` - * directory and instead from a different directory, `lib-boxednode`. This - * however does not work for any node version < 20 which is why we are - * conditionally generating the entry point and configure params here based on - * Node version. - */ - const { customCodeSource, customCodeConfigureParam, customCodeEntryPoint } = nodeVersion[0] >= 20 - ? { - customCodeSource: path.join(nodeSourcePath, 'lib-boxednode', `${namespace}.js`), - customCodeConfigureParam: `./lib-boxednode/${namespace}.js`, - customCodeEntryPoint: `lib-boxednode/${namespace}` - } : { - customCodeSource: path.join(nodeSourcePath, 'lib', namespace, `${namespace}.js`), - customCodeConfigureParam: `./lib/${namespace}/${namespace}.js`, - customCodeEntryPoint: `${namespace}/${namespace}` - }; + const { customCodeSource, customCodeConfigureParam, customCodeEntryPoint } = { + customCodeSource: path.join(nodeSourcePath, 'lib-boxednode', `${namespace}.js`), + customCodeConfigureParam: `./lib-boxednode/${namespace}.js`, + customCodeEntryPoint: `lib-boxednode/${namespace}` + }; await fs.mkdir(path.dirname(customCodeSource), { recursive: true }); await fs.writeFile(customCodeSource, entryPointTrampolineSource); @@ -476,6 +462,8 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L const resolvedNodeSourcePath = (await fs.realpath(nodeSourcePath)).replace(/\\/g, '/'); logger.stepStarting(`(win32) Deleting precompiled headers at ${resolvedNodeSourcePath}`); await rimraf(`${resolvedNodeSourcePath}/**/*.pch`, { glob: { follow: true, nodir: true } }); + await rimraf(`${resolvedNodeSourcePath}/**/out/`, { glob: { follow: true } }); + await rimraf(`${resolvedNodeSourcePath}/**/obj/`, { glob: { follow: true } }); logger.stepCompleted(); } binaryPath = await writeMainFileAndCompile(options.useNodeSnapshot ? { diff --git a/test/pchfixture/delete_1.pch b/test/pchfixture/delete_1.pch deleted file mode 100644 index e69de29..0000000 diff --git a/test/pchfixture/inner/delete_2.pch b/test/pchfixture/inner/delete_2.pch deleted file mode 100644 index e69de29..0000000 diff --git a/test/pchfixture/no_delete.h b/test/pchfixture/no_delete.h deleted file mode 100644 index e69de29..0000000 From 2a6fd18a8baa545ea3a116cd580c32d4660fda5b Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Mon, 17 Nov 2025 17:32:41 +0100 Subject: [PATCH 62/64] chore: try to delete the whole out directory for a fresh compilation --- src/index.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/index.ts b/src/index.ts index d43fecd..f2d900f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -352,11 +352,9 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L enableBindingsPatch })); - const { customCodeSource, customCodeConfigureParam, customCodeEntryPoint } = { - customCodeSource: path.join(nodeSourcePath, 'lib-boxednode', `${namespace}.js`), - customCodeConfigureParam: `./lib-boxednode/${namespace}.js`, - customCodeEntryPoint: `lib-boxednode/${namespace}` - }; + const customCodeSource = path.join(nodeSourcePath, 'lib-boxednode', `${namespace}.js`); + const customCodeConfigureParam = `./lib-boxednode/${namespace}.js`; + const customCodeEntryPoint = `lib-boxednode/${namespace}`; await fs.mkdir(path.dirname(customCodeSource), { recursive: true }); await fs.writeFile(customCodeSource, entryPointTrampolineSource); @@ -461,9 +459,7 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L // compilation, we will delete all pch files. const resolvedNodeSourcePath = (await fs.realpath(nodeSourcePath)).replace(/\\/g, '/'); logger.stepStarting(`(win32) Deleting precompiled headers at ${resolvedNodeSourcePath}`); - await rimraf(`${resolvedNodeSourcePath}/**/*.pch`, { glob: { follow: true, nodir: true } }); - await rimraf(`${resolvedNodeSourcePath}/**/out/`, { glob: { follow: true } }); - await rimraf(`${resolvedNodeSourcePath}/**/obj/`, { glob: { follow: true } }); + await rimraf(`${resolvedNodeSourcePath}/out`); logger.stepCompleted(); } binaryPath = await writeMainFileAndCompile(options.useNodeSnapshot ? { From a8b3e222413a32c92edda9526a8a96552d983bcf Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Mon, 17 Nov 2025 19:06:07 +0100 Subject: [PATCH 63/64] chore: Simplify naming, we now wipe out the whole out directory This will force Node.js to compile from scratch. It's slower, but it will only happens on Windows and it will be safer and more reliable there. --- src/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index f2d900f..5a6a6a4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -457,9 +457,9 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L // compilations and does not refresh them, but kills the compilation // process with an error. Due to this, before attempting the second // compilation, we will delete all pch files. - const resolvedNodeSourcePath = (await fs.realpath(nodeSourcePath)).replace(/\\/g, '/'); - logger.stepStarting(`(win32) Deleting precompiled headers at ${resolvedNodeSourcePath}`); - await rimraf(`${resolvedNodeSourcePath}/out`); + const nodeCompilationOutputDirectory = path.join((await fs.realpath(nodeSourcePath)), 'out'); + logger.stepStarting(`(win32) Wiping output directory at ${nodeCompilationOutputDirectory}`); + await rimraf(nodeCompilationOutputDirectory, { glob: false }); logger.stepCompleted(); } binaryPath = await writeMainFileAndCompile(options.useNodeSnapshot ? { From b947e7580d11afc0972036702dce92ab9bdb0d0f Mon Sep 17 00:00:00 2001 From: Kevin Mas Ruiz Date: Mon, 17 Nov 2025 20:44:53 +0100 Subject: [PATCH 64/64] chore: ensure that any output directory is cleaned up before compiling Right now it's failing on the first compilation of the second test, as we are not doing any clean up. To ensure that we are in a consistent state in Windows, before attempting compiling, we will make sure we don't have any old compilation artifact. This might slow down Windows compilations as they can't reuse cached files. --- src/index.ts | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/index.ts b/src/index.ts index 5a6a6a4..baa9154 100644 --- a/src/index.ts +++ b/src/index.ts @@ -378,6 +378,20 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L ? createCompressedBlobDefinition : createUncompressedBlobDefinition; + async function cleanUpTemporaryDirectoriesIfNecessary () { + if (process.platform === 'win32' && nodeVersion[0] >= 24) { + // Compiling with a snapshot requires compiling twice: a base image + // and the image with the snapshot embedded. In that situation, clang-cl + // complains that there are precompiled headers that changed between + // compilations and does not refresh them, but kills the compilation + // process with an error. Due to this, before attempting the second + // compilation, we will delete all pch files. + const nodeCompilationOutputDirectory = path.join((await fs.realpath(nodeSourcePath)), 'out'); + logger.stepStarting(`(win32) Wiping output directory at ${nodeCompilationOutputDirectory}`); + await rimraf(nodeCompilationOutputDirectory, { glob: false }); + logger.stepCompleted(); + } + } async function writeMainFileAndCompile ({ codeCacheBlob = new Uint8Array(0), codeCacheMode = 'ignore', @@ -433,6 +447,7 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L logger); } + await cleanUpTemporaryDirectoriesIfNecessary(); let binaryPath: string; if (!options.useCodeCache && !options.useNodeSnapshot) { binaryPath = await writeMainFileAndCompile(); @@ -450,18 +465,7 @@ async function compileJSFileAsBinaryImpl (options: CompilationOptions, logger: L throw new Error('Empty code cache/snapshot result'); } logger.stepCompleted(); - if (process.platform === 'win32' && nodeVersion[0] >= 24) { - // Compiling with a snapshot requires compiling twice: a base image - // and the image with the snapshot embedded. In that situation, clang-cl - // complains that there are precompiled headers that changed between - // compilations and does not refresh them, but kills the compilation - // process with an error. Due to this, before attempting the second - // compilation, we will delete all pch files. - const nodeCompilationOutputDirectory = path.join((await fs.realpath(nodeSourcePath)), 'out'); - logger.stepStarting(`(win32) Wiping output directory at ${nodeCompilationOutputDirectory}`); - await rimraf(nodeCompilationOutputDirectory, { glob: false }); - logger.stepCompleted(); - } + await cleanUpTemporaryDirectoriesIfNecessary(); binaryPath = await writeMainFileAndCompile(options.useNodeSnapshot ? { snapshotBlob: result, snapshotMode: 'consume'