jamsinclair / jSquash

Browser & Web Worker focussed wasm bundles derived from the Squoosh App.
Apache License 2.0
222 stars 14 forks source link

Provide way to override WASM file location #39

Closed swissspidy closed 10 months ago

swissspidy commented 10 months ago

Is your feature request related to a problem? Please describe.

Due to server restrictions in my project, it's not possible to load the WASM file from the same location as the JS is loaded.

Describe the solution you'd like

Providing a way to override the WASM file location by specifying a custom locateFile would help resolve my use case.

Describe alternatives you've considered

Right now I'm simply patching this package

diff --git a/node_modules/@jsquash/jpeg/encode.d.ts b/node_modules/@jsquash/jpeg/encode.d.ts
index 740244c..7d78f20 100644
--- a/node_modules/@jsquash/jpeg/encode.d.ts
+++ b/node_modules/@jsquash/jpeg/encode.d.ts
@@ -16,6 +16,6 @@
  * The jpeg options are defaulted to defaults from the meta.ts file.
  */
 import type { EncodeOptions } from './meta';
-export declare function init(module?: WebAssembly.Module): Promise<void>;
+export declare function init(module?: WebAssembly.Module, args?: Record<string, unknown>): Promise<void>;
 export default function encode(data: ImageData, options?: Partial<EncodeOptions>): Promise<ArrayBuffer>;
 //# sourceMappingURL=encode.d.ts.map
diff --git a/node_modules/@jsquash/jpeg/encode.js b/node_modules/@jsquash/jpeg/encode.js
index a6c65e8..3f90d3e 100644
--- a/node_modules/@jsquash/jpeg/encode.js
+++ b/node_modules/@jsquash/jpeg/encode.js
@@ -14,8 +14,8 @@ import mozjpeg_enc from './codec/enc/mozjpeg_enc';
 import { defaultOptions } from './meta';
 import { initEmscriptenModule } from './utils';
 let emscriptenModule;
-export async function init(module) {
-    emscriptenModule = initEmscriptenModule(mozjpeg_enc, module);
+export async function init(module, args) {
+    emscriptenModule = initEmscriptenModule(mozjpeg_enc, module, args);
 }
 export default async function encode(data, options = {}) {
     if (!emscriptenModule)
diff --git a/node_modules/@jsquash/jpeg/utils.js b/node_modules/@jsquash/jpeg/utils.js
index a00acf2..fd9927f 100644
--- a/node_modules/@jsquash/jpeg/utils.js
+++ b/node_modules/@jsquash/jpeg/utils.js
@@ -13,7 +13,7 @@
 /**
  * Notice: I (Jamie Sinclair) have modified this file to allow manual instantiation of the Wasm Module.
  */
-export function initEmscriptenModule(moduleFactory, wasmModule) {
+export function initEmscriptenModule(moduleFactory, wasmModule, args) {
     let instantiateWasm;
     if (wasmModule) {
         instantiateWasm = (imports, callback) => {
@@ -25,6 +25,7 @@ export function initEmscriptenModule(moduleFactory, wasmModule) {
     return moduleFactory({
         // Just to be safe, don't automatically invoke any wasm functions
         noInitialRun: true,
-        instantiateWasm
+        instantiateWasm,
+       ...args,
     });
 }

This way I can do:

import { default as encodeJpeg, init as initJpeg } from '@jsquash/jpeg/encode';

await initJpeg( undefined, {
    locateFile: () =>
        'https://cdn.jsdelivr.net/npm/@jsquash/jpeg@1.2.0/codec/enc/mozjpeg_enc.wasm',
} );

const buffer = await encodeJpeg( imageData );

Hacky, but works

Additional context

Allowing to override locateFile is common in similar projects such as mediainfo.js or ffmpeg.wasm.

jamsinclair commented 10 months ago

Great suggestion, I think this has been brought up a few times in some other issues.

I can't put any guarantees on when I might be able to work on this. If you or someone else would like to scaffold a Pull Request with the changes it would be a great help šŸ™‡

I would eventually like to refactor the whole init method syntax, but to keep the scope focused for the initial change I think the API should look like:

type InitOptions = EmscriptenWasm.ModuleOpts;

export async function init(module?: WebAssembly.Module, options?: InitOptions): Promise<void> {
    // ....
}

This would also allow others to modify other relevant emscripten module options that might suit their use case.

jamsinclair commented 10 months ago

Thanks @swissspidy I actually had some spare time to implement this. I went with the simplest approach to avoid a breaking change.

import { default as encodeJpeg, init as initJpeg } from '@jsquash/jpeg/encode';

await initJpeg( undefined, {
    locateFile: () =>
        'https://cdn.jsdelivr.net/npm/@jsquash/jpeg@1.4.0/codec/enc/mozjpeg_enc.wasm',
} );

const buffer = await encodeJpeg( imageData );

Your use case should hopefully work now with @jsquash/jpeg@1.4.0+ šŸ¤ž ā€“ keep me posted if it doesn't