title: How can I make webpack embed my *.wasm for use in a web worker?
body: <p>I have some rust code that compiles to web assembly using <code>wasm-pack</code> and <code>wasm-bindgen</code>. I want to call into this code from a web worklet/worker. The entire app should eventually be just one single *.js file, with everything else inlined.</p>
  <p>This is what I imagine my build process to look like:</p>
  <ol>
  <li>Use <code>wasm-pack</code> to compile the rust code to *.wasm and *.js bindings (this step works just fine)</li>
  <li>Use <code>webpack</code> to build a self-contained *.js file that I can load as a worklet/worker. The *.wasm must be included in this file. (this step fails)</li>
  <li>Use <code>webpack</code> again to build my final app/package, inlining the worklet/worker file from step 2. (this step works just fine)</li>
  </ol>
  <p>My problem is in step 2: I can't make <code>webpack</code> inline the *.wasm into the worklet/worker file.
  I tried this in my webpack config:</p>
  <pre class="lang-js prettyprint-override"><code>entry: {
      worker: {
          import: './src/worker.ts',
          filename: '../lib/worker.js',
      }
  },
  
  // ...
  
  module: {
      rules: [
      
          // ...
  
          {
              test: /\.wasm$/,
              // 1st option: type: 'webassembly/sync',
              // 2nd option: type: 'asset/inline',
          },
  
          // ...
  
      ],
  },
  </code></pre>
  <p>No matter what I do, <code>webpack</code> always emits two files, one <code>worker.js</code> with my worklet/worker script itself, and another one, <code>vendor_my_package_name_wasm_js.js</code> that contains just the *.wasm and its bindings. Obviously, when loading the <code>worker.js</code> as a web worker, it fails - the second file can't be loaded from the worker scope.</p>
  <p>My goal is to include everything in <code>worker.js</code> and NOT have a separate file emitted. But how do I do that?</p>
  <p><strong>Edit: Documenting steps towards a solution:</strong></p>
  <p>Webpack native wasm-loading doesn't seem to allow inlining the wasm file. We can try to use a regular raw-loader:</p>
  <pre class="lang-js prettyprint-override"><code>// in module.rules
  {
      test: /\.wasm$/,
      loader: 'raw-loader',
  },
  </code></pre>
  <p>This results in the following error:</p>
  <pre><code>ERROR in ./node_modules/my-module/my-wasm-file.wasm
  Module parse failed: magic header not detected
  File was processed with these loaders:
   * ../../node_modules/raw-loader/dist/cjs.js
  You may need an additional loader to handle the result of these loaders.
  </code></pre>
  <p>This happens because there's still an implicit default rule that kicks in. <a href="https://github.com/webpack/webpack/issues/6725#issuecomment-372182505" rel="nofollow noreferrer">We can disable it by overwriting the default rules</a> to only consider <code>json</code> and <code>js</code> files:</p>
  <pre class="lang-js prettyprint-override"><code>// in webpack.config.js
      module: {
          defaultRules: [
              {
                  type: 'javascript/auto',
                  resolve: {},
              },
              {
                  test: /\.json$/i,
                  type: 'json',
              },
          ],
          rules: [
              // ...
              {
                  test: /\.wasm$/,
                  loader: 'raw-loader',
              },
          ],
      },
  </code></pre>
  <p>Now we finally have our worker bundled into a single *.js file! However, when loading it, we end up in this error:</p>
  <pre><code>Uncaught ReferenceError: document is not defined
  </code></pre>
  <p>pointing to this piece of webpack-generated code:</p>
  <pre class="lang-js prettyprint-override"><code>/* webpack/runtime/jsonp chunk loading */
  /******/    (() =&gt; {
  /******/        __webpack_require__.b = document.baseURI || self.location.href; // &lt;&lt;&lt; error here
  /******/        
  /******/        // object to store loaded and loading chunks
  /******/        // undefined = chunk not loaded, null = chunk preloaded/prefetched
  /******/        // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded
  /******/        var installedChunks = {
  /******/            &quot;myModuleName&quot;: 0
  /******/        };
  /******/        
  /******/        // no chunk on demand loading
  /******/        
  /******/        // no prefetching
  /******/        
  /******/        // no preloaded
  /******/        
  /******/        // no HMR
  /******/        
  /******/        // no HMR manifest
  /******/        
  /******/        // no on chunks loaded
  /******/        
  /******/        // no jsonp function
  /******/    })();
  </code></pre>
  <p>For some reason webpack tries to support loading stuff dynamically (?). We can isolate the problem to this piece of code that was generated by <code>wasm-pack</code> as part of the javascript bindings when using the <code>--target=web</code> CLI argument:</p>
  <pre class="lang-js prettyprint-override"><code>async function init(input) {
      if (typeof input === 'undefined') {
          input = new URL('my_wasm_file.wasm', import.meta.url);
      }
      const imports = {};
      // ...
  </code></pre>
  <p>Apparently the possibility of having to generate a URL makes webpack rely on <code>document</code> which is not available when loading the worker script in the worker scope. Uncommenting the <code>new URL()</code> part makes the <code>document</code> reference disappear from the webpack output.</p>
  <p>Not sure where to go from here. Write my own wasm-loader? I worked on that for a while, base64 encoding the wasm file and inlining it as a string - but then I have to dramatically change the consumer code to manually load the wasm asynchronously. This means that I can't use the <code>wasm-bindgen</code> bindings anymore as they rely on either the <code>URL</code> part shown above (when using <code>--target=web</code>) or the bundling logic of webpack 5 (when using <code>--target=bundler</code>) which I can't get supported from my own simple wasm-loader attempts.
  Essentially that means that I have to provide my own JS bindings, which is inconvenient.</p>
  <p>There must be a better way - right?</p>