How to package npm module with AudioWorklet?

286 views Asked by At

I've built an AudioWorklet that imports an npm module. This works during runtime but fails when compiled. My understanding (but new to Worklets and Web Workers) is that the AudioWorklet code needs to be self-contained and can't pull the import in production.

Can we compile the AudioWorklet with the npm imports at build time (or something else of better practice)? Using Vite for frontend compilation.

The following is a simplified example of my Worklet, but no real code to shorten the example.

import { ToolWASM } from 'tool/dist/wasm.es.js'
import Tool from 'tool/dist/core.es.js'

let tool = new Tool(ToolWASM)

class SampleProcess extends AudioWorkletProcessor {
constructor() {
    super()
    this.tool = tool
  }

  process() {
    this.tool.doSomething()
    return false
  }
}

registerProcessor('sample-processor', SampleProcess)
2

There are 2 answers

1
anvlkv On

From my attempts to do a similar trick with vite simply ctx.audioWorklet.addModule(new URL('./worklet.ts', import.meta.url)) does the job at least in development build.

To make it really work it should be handled as per vite's docs for workers. See import with query suffixes

import workletUrl from './worklet?worker&url'
import wasmUrl from 'shared/shared_bg.wasm?url'
0
dvoutt On

The solution I ended up coming up with used Rollup to achieve it. Ended up adding a basic Rollup setup to bundle the file during runtime and compile time. I did it this way as trying to incorporate it within Vite affected the plug-and-play setup Vite has with my Tauri app.

This approach allowed me to run my rollup completely separately and ahead of the building of the rest of the app.

The rollup.config.js was set up as follows.

import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
import replace from 'rollup-plugin-replace'

export default {
  input: 'src/worklets/file.js',

  output: {
    file: 'src/bundles/file-bundle.js',
    format: 'iife',
  },

  plugins: [
    resolve({
      browser: true,
      preferBuiltins: false,
    }),
    commonjs(),
    replace({
      'process.env.NODE_ENV': JSON.stringify('production'),
    }),
  ],
}

In my package.json created a few scripts that get run. For anyone curious, the build:<name> and dev:<name> are using npm-run-all, so the scripts you are seeing are part of several that run either sequentially (build) or parallel (dev).

"scripts": {
  ...
  "build:bundle": "yarn rollup",
  ...
  "dev:bundle": "yarn rollup --watch",
  ...
  "rollup": "rollup --c",
}