Skip to content

Support manual instantiation of wasm binary #156

@gvkhna

Description

@gvkhna

Problem

I'm choosing to import the wasm binary into the bundle that is produced by vite like so:

import MuPdfWasm from 'mupdf/dist/mupdf-wasm.wasm?arraybuffer'

Using the vite-plugin-arraybuffer. This has some obvious pros/cons but regardless it would be great if more packages supported this so I felt compelled to report this simultaneously across a few packages.

This is the glue/shim code that is required to override the default behavior of the library from trying to load the binary by finding it's path. In more modern (serverless) environments there may not be a filesystem at all and so all of this code needs to be altered in a brittle manner.

  if (globalThis.$libmupdf_wasm_Module) {
    globalThis.$libmupdf_wasm_Module = {
      instantiateWasm(
        imports,
        successCallback
      ) {
        try {
          WebAssembly.instantiate(MuPdfWasm, imports)
            .then((result) => {
              successCallback(result.instance)
            })
            .catch((er) => {
              throw err
            })

          return {}
        } catch (err) {
          return false
        }
      }
    }
  }

  mupdfInstance = await import('mupdf')
  log('MuPDF initialized')

Suggestion

While it's unclear the route to go through because the loaders are compiled by emscripten. I wanted to report the issue regardless, I think ideally some simple way to manually load the library in a controlled manner should be supported, opting out of environment introspection which is very hacky at this point.

For instance: In the case of CanvasKit, it does export it's emscripten loader but doesn't type the instantiateWasm function, only the locateFile function, and so it's easier and more straightforward to provide the binary. This is ideally the way to do it, to provide a means to manually instantiate the wasm and let developers choose how they want to handle it.

export default function CanvasKitInit(opts?: CanvasKitInitOptions): Promise<CanvasKit>;

export interface CanvasKitInitOptions {
    /**
     * This callback will be invoked when the CanvasKit loader needs to fetch a file (e.g.
     * the blob of WASM code). The correct url prefix should be applied.
     * @param file - the name of the file that is about to be loaded.
     */
    locateFile(file: string): string;
}

Loading CanvasKit is less hacky only requiring the following for instance:

import CanvasKitInitImport from 'canvaskit-wasm/bin/canvaskit.js'
import CanvasKitWasm from 'canvaskit-wasm/bin/canvaskit.wasm?arraybuffer'

export async function loadCanvasKit() {
    const canvasKit = await CanvasKitInit({
      instantiateWasm(imports, successCallback) {
        try {
          WebAssembly.instantiate(CanvasKitWasm, imports)
            .then((result) => {
              successCallback(result.instance)
            })
            .catch((err) => {
              throw err
            })
          return {}
        } catch (err) {
          return false
        }
      }
    })
...

Related: I'm going to link all of these together because they're addressing the same thing for different packages and the hassles involved for folks using bun/deno, and other means of working with the wasm binary that require jumping through hoops.

#147

lovell/sharp#3912

lovell/sharp#4371

https://issues.skia.org/u/1/issues/409942760

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions