developit/workerize-loader

Empty Worker returned after hot-reloading

Closed this issue ยท 16 comments

For some reason, no methods are being exposed.

After I run webpack, and then make changes to a file imported into Worker.js, the Worker file returns an empty Worker with no methods. I think hot-reloading chunks doesn't play well with this loader. Restarting Yarn and refreshing the browser seems to fix the issue.

Worker.js
import someCostlyFunction from './here'

export function labelGeofences (data) {
    return someCostlyFunction(data)
}
App.js
import Worker from 'workerize-loader?inline=true!./worker'
const worker = new Worker()
console.log(worker)
// => Worker {onmessage: null, onerror: null}
worker.labelGeofences(...)
// => Error, no such property

Strange! What version of Webpack?

@developit v3.10

I'm using a very basic setup. babel followed by workerize-loader

I'm also experiencing this all of a sudden. v1.0.2 seems to have broken webpack 3. I've set v1.0.1 in my package.json for now.

Ah interesting 1.0.1 works - I'll see if there's anything fishy in the diff.

@developit I think that is might be related to #32

UPD: I've tried v1.1 (before 71b1df8) and still facing the issue

It seems that issue is related to HMR. I'll try to describe my enviroment so that you could suggest where we may start bug detection.

I'm using vue-cli@2 "webpack" template (it's Vue version of create-react-app, but already ejected).

Used workerize loader like that:
src/my.js

import MyWorker from 'workerize-loader!./MyWorker';
const worker = new MyWorker();

No changes to default webpack config were made

.babelrc

{
  "presets": [
    ["env", {
      "modules": false,
      "useBuiltIns": true
    }],
    "stage-2"
  ],
  "plugins": ["transform-vue-jsx", "transform-runtime"],
}

In some time after few HMR updates MyWorker constructor always returns:
Worker {onmessage: null, onerror: null}

Hope that can help to identify issue. In near future I'll try to create a repository that reproduces the issue.

Based on @developit suggestion to play around with webpack.hot tried this solution.

In the file that proxies the worker getWorker.js:

import MyWorker from './MyWorker.worker';

let cache;

if (module.hot) {
  module.hot.accept(['./MyWorker.worker'], () => {
    if (cache) {
      // eslint-disable-next-line global-require
      const MyWorkerLatest = require('./MyWorker.worker');
      cache = new MyWorkerLatest();
    }
  });
}

export default () => {
  if (!cache) {
    cache = new MyWorker();
  }
  return cache;
};

I forgot to mention a detail: once the "bad" worker is returned for first time the environment is spoiled forever. Even after reloading page the worker is "bad". Only webpack-dev-server restart helps. Any ideas?

I'm also experiencing this issue. Even if I disable HMR in my project (hot: false for the dev server, remove the babel plugin and the HotModuleReplacementPlugin), the methods are still undefined after any recompile. Using workerize-loader requires me to restart the build after every change which is not sustainable. And since I am using Webpack 4 I cannot downgrade to 1.0.1.

@exarus @developit @janbaykara I found the issue! Turning off babel-loader's cacheDirectory option (set it to false) does the trick. This is enabled in CRA, and I assume most CRA derivatives like vue-cli.

Nevermind, turning off cacheDirectory worked for my simple test worker, but as soon as I started implementing what I really want to do I'm back to the same old behavior where no methods are present on the worker object after any recompile. The investigation continues...

Now I found the root fo the problem: the __workerizeExports property is not persisted on the compilation object between recompiles, resulting in an empty list of methods after the first compile. I know next to nothing about Webpack loader development, so this might not be the right way to do it, but when I save the list of methods to workerize in the parse hook on the CACHE object which is local to this loader, everything works. I'll make a PR with the change so you can see exactly what I mean.

wclr commented

I have the follwing situation:

const worker = require('workerize-loader!./worker')
  1. Something of the dependencies of ./worker is modified
  2. the "empty" worker is hot-reloaded (Uncaught TypeError: Object(...) is not a function).
  3. Then if I modify ./woker itself (just put comment). It hot-reloads ok.

Is it this bug? Is it going to be fixed?

Now I found the root fo the problem: the __workerizeExports property is not persisted on the compilation object between recompiles, resulting in an empty list of methods after the first compile. I know next to nothing about Webpack loader development, so this might not be the right way to do it, but when I save the list of methods to workerize in the parse hook on the CACHE object which is local to this loader, everything works. I'll make a PR with the change so you can see exactly what I mean.

I tried this (edited the index.js file in the dist/ folder locally) and it works. I haven't had to change the code in my worker to see if there are any side effects but the annoying issue is gone for now. I don't have to re-run my serve script on every change. Thanks.

I'm having the same issue (no babel) when using together with ts-loader:
{ test: /\.worker\.ts$/, loaders: [ 'workerize-loader', 'ts-loader' ] }

test.worker.ts

export async function getArray() {
    const arr: number[] = [ 1, 2, 3 ];
    return arr;
}

the methods array is empty and doesn't contain my getArray function.
I'm not using babel or hot reload, any idea how to make this work with typescript compilation?
Once i remove ts-loader from the list it works fine (without typescript of course).

Any updates on a solution for this? I am having this issue as well. It seems to only happen when I set webpack to production configuration.

@shayke Maybe it is not your ts-loader plugin but your tsconfig.json causes commonJS transform as described in the [About Babel] section in readme. Check if you have "module": "commonJS", in tsconfig.json. I encountered the same issue and make it work by modify this option to es6 (which requires additional modification because of ts check rule changed...)