Search code examples
reactjsjestjscreate-react-appweb-worker

Web Workers in create-react-app: Jest error: Cannot use 'import.meta' outside a module


I used https://blog.logrocket.com/web-workers-react-typescript/ to build some simple Web Workers in an application created with create-react-app. In it, I have code like this:

const workerSimplerExample: Worker = useMemo(
    () => new Worker(new URL("../workers/simple-example.ts", import.meta.url), {type:"module"}),
    []
);

This generates a URL which works whether I'm in dev mode, or using the build directory in Production mode. However, no matter what I do, Jest throws errors when I run create-react-app's basic npm test or yarn test, with the error being: SyntaxError: Cannot use 'import.meta' outside a module

Any solutions I've found, (of which there aren't many, and none are very confident,) involve changing Jest or Bable settings in ways create-react-app doesn't allow unless I eject.

Has anyone figured out how to have Web Workers in React without breaking Jest or ejecting create-react-app?


Solution

  • To fix this, I figured out that I can isolate the call to new URL() and then replace the file which is loaded when Jest runs. Credit goes to this GitHub comment.

    First, isolate new URL(). My file is located at src/workers/simple-example-factory.ts

    const getWorker = (): Worker => {
        return new Worker(new URL('./simple-example', import.meta.url));
    }
    
    export default getWorker;
    

    Then, create a test double at src/workers/simple-example-factory.fake.ts and use a static file path.

    const getWorker = (): Worker => {
        return new Worker(new URL('./simple-example', ''));
    }
    
    export default getWorker;
    

    Then, you can get an instance of the work in your JSX/TSX files like so:

    import SimpleExampleFactory from "../workers/simple-example-factory";
    ...
    const workerSimplerExample: Worker = SimpleExampleFactory();
    

    At this point, the worker should still work in the browser. Now, to fix Jest, we have to tell it to load a different file. We can modify the Jest configuration through package.json when using create-react-app (See documentation.)

    package.json

    {
    ...
      "jest": {
        "moduleNameMapper": {
          "workers/(.*)-factory": "<rootDir>/src/workers/$1-factory.fake.ts"
        }
      },
    ...
    }