From aeb58c5985e58f7ac42990a2e313bb413e2938ea Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 13 Oct 2026 12:24:54 +0200 Subject: [PATCH 1/2] build(nodejs): declare the wasm32-wasip1 package as an optional dependency cli 3.10 drops WASI packages from the root optionalDependencies when native targets exist, which would leave browser.js importing an uninstalled package. Ignore and clear the root facades pre-publish now generates. --- bindings/nodejs/.gitignore | 1 + bindings/nodejs/Makefile | 1 + bindings/nodejs/package.json | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/bindings/nodejs/.gitignore b/bindings/nodejs/.gitignore index aacfe4ff..74e32af8 100644 --- a/bindings/nodejs/.gitignore +++ b/bindings/nodejs/.gitignore @@ -5,3 +5,4 @@ index.d.ts *.wasm npm/ zen-engine.wasip1* +zen-engine.wasm32-wasip1.* diff --git a/bindings/nodejs/Makefile b/bindings/nodejs/Makefile index 481d9f9b..25b0ab2c 100644 --- a/bindings/nodejs/Makefile +++ b/bindings/nodejs/Makefile @@ -18,6 +18,7 @@ darwinArm: cp zen-engine.darwin-arm64.node npm/darwin-arm64/ prepareLocalPublish: + rm -f zen-engine.wasm32-wasip1.*.mjs zen-engine.wasm32-wasip1.*.mts yarn napi prepublish --skip-optional-publish yarn createNpmDirs diff --git a/bindings/nodejs/package.json b/bindings/nodejs/package.json index b7edabac..56837ae4 100644 --- a/bindings/nodejs/package.json +++ b/bindings/nodejs/package.json @@ -37,7 +37,8 @@ "fs": false, "asyncInit": true }, - "asyncRuntime": true + "asyncRuntime": true, + "optionalDependency": true } }, "keywords": [ From 6cfd8abb7b5083712241a8939339a24b2dd38ec6 Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 13 Oct 2026 13:38:38 +0200 Subject: [PATCH 2/2] build(nodejs): keep the root package free of the threadless wasm facade napi prepublish copies the 15.6MB wasm into the root package and rewrites files and exports for workerd consumers. Run it through prepublish.mjs, which keeps its optionalDependencies but restores files and exports and removes the generated root files, so the root tarball is back to ~20kB. --- bindings/nodejs/Makefile | 2 +- bindings/nodejs/package.json | 2 +- bindings/nodejs/prepublish.mjs | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 bindings/nodejs/prepublish.mjs diff --git a/bindings/nodejs/Makefile b/bindings/nodejs/Makefile index 25b0ab2c..2b526956 100644 --- a/bindings/nodejs/Makefile +++ b/bindings/nodejs/Makefile @@ -19,7 +19,7 @@ darwinArm: prepareLocalPublish: rm -f zen-engine.wasm32-wasip1.*.mjs zen-engine.wasm32-wasip1.*.mts - yarn napi prepublish --skip-optional-publish + node prepublish.mjs --skip-optional-publish yarn createNpmDirs publish: diff --git a/bindings/nodejs/package.json b/bindings/nodejs/package.json index 56837ae4..ff975c7d 100644 --- a/bindings/nodejs/package.json +++ b/bindings/nodejs/package.json @@ -102,7 +102,7 @@ "test:wasm": "cross-env NAPI_RS_FORCE_WASI=error NAPI_RS_WASI_FLAVOR=wasm32-wasip1 __ZEN_MOCK_UTC_TIME=2025-08-19T16:55:02.078Z jest -c jest.wasm.config.js", "artifacts": "napi artifacts -d ../../artifacts", "createNpmDirs": "napi create-npm-dirs && node enrich-npm-dirs.mjs", - "prepublishOnly": "napi prepublish --no-gh-release", + "prepublishOnly": "node prepublish.mjs --no-gh-release", "version": "napi version" }, "resolutions": { diff --git a/bindings/nodejs/prepublish.mjs b/bindings/nodejs/prepublish.mjs new file mode 100644 index 00000000..719127ff --- /dev/null +++ b/bindings/nodejs/prepublish.mjs @@ -0,0 +1,32 @@ +import { spawnSync } from 'node:child_process'; +import { readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; + +const packageJsonPath = join(import.meta.dirname, 'package.json'); +const readPackageJson = () => JSON.parse(readFileSync(packageJsonPath, 'utf8')); + +const before = readPackageJson(); + +const napi = spawnSync('napi', ['prepublish', ...process.argv.slice(2)], { + cwd: import.meta.dirname, + stdio: 'inherit', + shell: process.platform === 'win32', +}); +if (napi.status !== 0) process.exit(napi.status ?? 1); + +const after = readPackageJson(); +const keptFiles = new Set(before.files ?? []); +const generatedFiles = (after.files ?? []).filter((file) => !keptFiles.has(file)); + +const restoreField = (target, source, key) => { + if (source && Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key]; + else if (target) delete target[key]; +}; + +restoreField(after, before, 'files'); +restoreField(after, before, 'exports'); +if (after.publishConfig) restoreField(after.publishConfig, before.publishConfig, 'exports'); + +writeFileSync(packageJsonPath, `${JSON.stringify(after, null, 2)}\n`); + +for (const file of generatedFiles) rmSync(join(import.meta.dirname, file), { force: true });