fix(quantus-crypto): strip wasm-bindgen's fetch-based init from the shipped glue
The package could not be bundled. Every webpack consumer failed with:
Module not found: Error: Can't resolve 'quantus_crypto_bg.wasm'
in node_modules/@quantus/crypto/generated
wasm-bindgen's async `__wbg_init` contains
module_or_path = new URL('quantus_crypto_bg.wasm', import.meta.url);
and webpack resolves `new URL(..., import.meta.url)` statically, at build time,
whether or not the branch can run. The file is not in the package — the wasm
ships base64'd in bytes.js, which is the entire point of this package — so the
build failed on a code path we never call.
node never sees it, which is why ten Rust tests, twelve consumer assertions and a
browser probe all passed while the package was unusable in a bundler. It took a
real extension build to surface, and that is the useful lesson: this package's
consumers bundle, and nothing in its own test suite does.
So the dead init is removed after bindgen runs. Shipping a second copy of the
wasm to satisfy a path we do not use would be the wide fix; deleting generated
code we never call is the narrow one.
The stripper asserts the shape it expects and throws if wasm-bindgen changes it,
rather than silently no-opping — a build that quietly stopped stripping would
ship the broken package again. It also re-checks that no reference to the .wasm
filename survives.
Published as 0.1.1.
Refs quantus/wasm#1, quantus/extension#2
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
},
|
||||
"sideEffects": false,
|
||||
"type": "module",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"fflate": "^0.8.2",
|
||||
|
||||
@@ -408,30 +408,4 @@ function initSync(module) {
|
||||
return __wbg_finalize_init(instance, module);
|
||||
}
|
||||
|
||||
async function __wbg_init(module_or_path) {
|
||||
if (wasm !== undefined) return wasm;
|
||||
|
||||
|
||||
if (module_or_path !== undefined) {
|
||||
if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
|
||||
({module_or_path} = module_or_path)
|
||||
} else {
|
||||
console.warn('using deprecated parameters for the initialization function; pass a single object instead')
|
||||
}
|
||||
}
|
||||
|
||||
if (module_or_path === undefined) {
|
||||
module_or_path = new URL('quantus_crypto_bg.wasm', import.meta.url);
|
||||
}
|
||||
const imports = __wbg_get_imports();
|
||||
|
||||
if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {
|
||||
module_or_path = fetch(module_or_path);
|
||||
}
|
||||
|
||||
const { instance, module } = await __wbg_load(await module_or_path, imports);
|
||||
|
||||
return __wbg_finalize_init(instance, module);
|
||||
}
|
||||
|
||||
export { initSync, __wbg_init as default };
|
||||
export { initSync };
|
||||
|
||||
@@ -35,6 +35,10 @@ echo "*** Converting to WASM"
|
||||
# The glue is a build artifact but is checked in, so the package can be built
|
||||
# without a Rust toolchain — the same reasoning as upstream checking in an empty
|
||||
# bytes.js. Copy it back so the two never drift.
|
||||
# Must run before the glue is copied into src/generated — see the script's own
|
||||
# comment for why the fetch path cannot ship.
|
||||
node ./scripts/strip-wasm-fetch-init.mjs packages/$PKG/build-wasm/$CRATE.js
|
||||
|
||||
echo "*** Updating checked-in bindings"
|
||||
cp packages/$PKG/build-wasm/$CRATE.js packages/$PKG/src/generated/$CRATE.js
|
||||
cp packages/$PKG/build-wasm/$CRATE.d.ts packages/$PKG/src/generated/$CRATE.d.ts
|
||||
|
||||
53
scripts/strip-wasm-fetch-init.mjs
Normal file
53
scripts/strip-wasm-fetch-init.mjs
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2026 @quantus/crypto authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Removes wasm-bindgen's async `__wbg_init` from the generated glue.
|
||||
//
|
||||
// We never call it — the whole point of this package is `initSync` over bytes
|
||||
// compiled into the JS, because the consumer is an MV3 service worker that
|
||||
// cannot usefully fetch. But `__wbg_init` contains
|
||||
//
|
||||
// module_or_path = new URL('quantus_crypto_bg.wasm', import.meta.url);
|
||||
//
|
||||
// and webpack resolves `new URL(..., import.meta.url)` statically, at build
|
||||
// time, whether or not the branch can ever run. The file is not in the package —
|
||||
// the wasm ships base64'd in bytes.js — so every bundling consumer fails with
|
||||
// "Can't resolve 'quantus_crypto_bg.wasm'". node never sees this, which is why
|
||||
// it took a real extension build to surface.
|
||||
//
|
||||
// Deleting dead generated code is the narrow fix; shipping a second copy of the
|
||||
// wasm to satisfy a code path we do not use would be the wide one.
|
||||
|
||||
import fs from 'node:fs';
|
||||
|
||||
const [file] = process.argv.slice(2);
|
||||
const source = fs.readFileSync(file, 'utf-8');
|
||||
|
||||
// Fail loudly rather than silently no-op if wasm-bindgen changes shape: a build
|
||||
// that quietly stopped stripping would ship the broken package again.
|
||||
const START = '\nasync function __wbg_init(module_or_path) {';
|
||||
const EXPORTS = 'export { initSync, __wbg_init as default };';
|
||||
|
||||
const start = source.indexOf(START);
|
||||
|
||||
if (start === -1 || !source.includes(EXPORTS)) {
|
||||
throw new Error(`${file}: wasm-bindgen output is not the shape this expects; re-check whether __wbg_init still needs stripping`);
|
||||
}
|
||||
|
||||
const end = source.indexOf('\n}\n', start);
|
||||
|
||||
if (end === -1) {
|
||||
throw new Error(`${file}: could not find the end of __wbg_init`);
|
||||
}
|
||||
|
||||
const stripped = source
|
||||
.slice(0, start)
|
||||
.concat(source.slice(end + 3))
|
||||
.replace(EXPORTS, 'export { initSync };');
|
||||
|
||||
if (stripped.includes('quantus_crypto_bg.wasm')) {
|
||||
throw new Error(`${file}: a reference to the .wasm file survived stripping`);
|
||||
}
|
||||
|
||||
fs.writeFileSync(file, stripped);
|
||||
console.log(`*** Stripped fetch-based init from ${file}`);
|
||||
Reference in New Issue
Block a user