Closed raimund-schluessler closed 1 year ago
Good catch, thanks :+1: This is very annoying. Users shouldn't have to deal with this. There's a similar issue with providing web workers in packages. Ideally you would just bundle the wasm file with the package and somehow "include" it opaquely. But I don't know if there is an easy way to do that.
cc @dargmuesli @Sec-ant
Yep I didn't bundle the wasm file inside @sec-ant/zxing-wasm
or @sec-ant/barcode-detector
libs because the inline base64 data url would make the packages size very huge. I cannot provide a runtime fallback option either, because that also means I have to include the base64 url in some branch of the code. So I provided a default, relatively short and common cdn location, and exposed a function to let the users decide where to load their wasm files if they want to change this behavior.
If vue-qrcode-reader wants to hide this option away from the users and to not use a third-party server, the easy way would be downloading the wasm file from the CDN (version pinning is recommended), saving it somewhere in the project considered as assets, and importing it as a base64 url with Vite's asset importing query strings.
A small demo looks like this:
import wasmFile from "../public/zxing_reader.wasm?url";
import { setZXingModuleOverrides } from "@sec-ant/barcode-detector";
setZXingModuleOverrides({
locateFile: (path, prefix) => {
if (path.endsWith(".wasm")) {
return wasmFile;
}
return prefix + path;
},
});
const barcodeDetector: BarcodeDetector = new BarcodeDetector({
formats: ["qr_code"],
});
fetch(
"https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=Hello%20world!"
)
.then((resp) => resp.blob())
.then((imageFile) => barcodeDetector.detect(imageFile))
.then(console.log);
However, I suggest reexporting this setZXingModuleOverrides
function or encapsulating it as a simpler form to let the users have the ability to control where they want to serve the wasm file may be a better option. Additionally, you can also keep the package size small.
PS:
You may notice I have updated @sec-ant/zxing-wasm
and @sec-ant/barcode-detector
just now. That's because when I was testing, I realized I put @types/emscripten
in the dev depencies, so the argument of setZXingModuleOverrides
is not properly typed. The recent version updates (@sec-ant/zxing-wasm@2.1.2
and @sec-ant/barcode-detector@1.1.2
) fixed this problem.
@gruhn @Sec-ant Thanks for taking care and the extensive explanation and example code. I think exposing the setZXingModuleOverrides
function would be the best option, as it keeps the package size small and allows for the highest flexibility.
If I see it correctly, the wasm files are available as GitHub releases from https://github.com/Sec-ant/zxing-wasm-build/releases. So maybe it would even be possible to configure vite/webpack to handle the downloading automatically, somehow, for people that don't want to use the CDN, but their custom server.
@raimund-schluessler Those wasm files are automatically built by GitHub actions, they are kept to sync with the upstream ZXing Cpp source code. That means not all of the builds are consumed in the downstream @sec-ant/zxing-wasm
and they don't have a clear version mapping. So downloading those releases is a bad choice (If the version is mismatched, the wasm build may use a different api from what the js code expected). Actually the whole building and consuming process is somewhat amateur because I wasn't expecting other people will use this package π. Maybe I should use stricter version rules, content-hash filenames and some more powerful build tools.
Back to the subject, actually, you don't have to manually download the wasm file. The package manager should've already downloaded them for you when you install any of the downstream packages, e.g. vue-qrcode-reader
. You should find the file under this path: node_modules/@sec-ant/zxing-wasm/dist/reader/zxing_reader.wasm
. So you can import it like this and no longer worry about wrong versions:
import wasmFile from "../node_modules/@sec-ant/zxing-wasm/dist/reader/zxing_reader.wasm?url";
@Sec-ant Thanks a lot for the insights. I followed your suggestions and prepared a PR with updated packages and setZXingModuleOverrides
re-export. Still gotta do some testing.
released in v5.2.0: https://github.com/gruhn/vue-qrcode-reader/releases/tag/v5.2.0
@gruhn @Sec-ant Thanks a lot for your effort. It works nicely now.
With a strict CSP enabled, one just needs to adjust it to allow wasm-unsafe-eval
.
Just a little addendum how to copy the required wasm
file to the output directory with webpack:
//webpack.config.js
const CopyPlugin = require("copy-webpack-plugin")
webpackConfig.plugins.push(
new CopyPlugin({
patterns: [
{ from: "node_modules/@sec-ant/zxing-wasm/dist/reader/zxing_reader.wasm", to: "zxing_reader.wasm" },
],
}),
)
I think there's an underlying problem regarding the versions of vue-qrcode-reader
, and the zxing_reader.wasm
file (from the package @sec-ant/zxing-wasm
):
vue-qrcode-reader
doesn't pin the version of @sec-ant/barcode-detector
in its deps list, which could result in users having a newer version (with minor/patch updates because of the ^
caret sign) of @sec-ant/barcode-detector
and @sec-ant/zxing-wasm
in their node_modules
folder.
As vue-qrcode-reader
is published with everything bundled, including the js glue code (the part of code that interoperates with the .wasm
binary file) from @sec-ant/zxing-wasm
. As a result, if a user wants to host the zxing_reader.wasm
himself/herself and copies it from the node_modules
folder, the user may end up hosting a mismatched newer version of the .wasm
file, because the js glue code is hardcoded in the vue-qrcode-reader
dist bundle and therefore is immune from any updates inside node_modules
.
I don't think this is an issue of vue-qrcode-reader
, because "bundle and publishβ is actually quite the common way to publish a package in the js world. And package managers + semver
should have already taken into account of the breaking changes for us appropriately.
But wasm
is still a rather foreign object for most front end tooling, applications, and most importantly, for me. So I'm not very confident that I can always mark the breaking changes correctly in each update of @sec-ant/zxing-wasm
. And I cannot be sure the .wasm
and js glue code from different versions can work together even if there're only "minor" or "patch" differences between them.
So, what I want to suggest here is that:
For package users: Until we find a better way to address this issue, if you want to host the zxing_reader.wasm
file yourself, make sure you're using the correct version that matches with the js glue code bundled inside vue-qrcode-reader
. You can search zxing-wasm
inside the dist
folder of vue-qrcode-reader
in your node_modules
to double check.
For @gruhn : Maybe we should pin the version of barcode-detector
in the package.json
of vue-qrcode-reader
? I have just pinned the version of @sec-ant/zxing-wasm
in the package.json
of barcode-detector
. So if barcode-detector
is also pinned by vue-qrcode-reader
, then each version of vue-qrcode-reader
will only correspond with a single exact version of @sec-ant/zxing-wasm
.
For everyone: Is there a better, less error-prone way to solve this issue? I'm all ears. ππ
Pinning dependencies rocks
Sounds good π
Since
vue-qrcode-reader@4.0.0
the library uses@sec-ant/barcode-detector
. This in turn loads a@sec-ant/zxing-wasm
web assembly file via a HTTP request to a third-party server. This is problematic for servers with a strictCSP
policy preventing such requests.I see that
@sec-ant/barcode-detector
allows to set a custom URL to load the wasm file from, viasetZXingModuleOverrides
, see https://github.com/Sec-ant/barcode-detector#setzxingmoduleoverrides.Is there a way to use
vue-qrcode-reader
and set the custom URL for zxing?