create-react-app icon indicating copy to clipboard operation
create-react-app copied to clipboard

Duplicate path segment error in dynamic imports in Web Worker (react-scripts 5, webpack 5)

Open kenlyon opened this issue 2 years ago • 8 comments

Describe the bug

When my app attempts to dynamically import something via a Web Worker, the request has the static/js part of the path repeated, resulting in a 404 error.

I can reproduce this with a clean install of the typescript template of create-react-app.

The two main factors required are:

  1. A web worker (I'm using webpack 5's syntax with import.meta.url.)
  2. A homepage attribute set in package.json. If the app is served directly via localhost it seems the faulty part of the resolution doesn't happen. Our production environment uses a subdomain, though, so I need the homepage attribute,

Did you try recovering your dependencies?

I've reproduced this with a clean install. The dependencies are not an issue.

I'm using yarn 1.22.18. I believe the same problem would happen with npm.

$ yarn --version
1.22.18

Which terms did you search for in User Guide?

  • webpack 5
  • web worker

Environment

Environment Info:

  current version of create-react-app: 5.0.1
  running from C:\Users\<my-user-name>\AppData\Local\Yarn\Data\global\node_modules\create-react-app

  System:
    OS: Windows 10 10.0.19041
    CPU: (8) x64 Intel(R) Core(TM) i7-9700 CPU @ 3.00GHz    
  Binaries:
    Node: 14.19.0 - C:\Program Files\nodejs\node.EXE        
    Yarn: 1.22.18 - C:\Program Files\nodejs\yarn.CMD        
    npm: 6.14.16 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Chrome: Not Found
    Edge: Spartan (44.19041.1266.0), Chromium (99.0.1150.52)
    Internet Explorer: 11.0.19041.1202
  npmPackages:
    react: ^18.1.0 => 18.1.0 
    react-dom: ^18.1.0 => 18.1.0 
    react-scripts: 5.0.1 => 5.0.1 
  npmGlobalPackages:
    create-react-app: Not Found

Steps to reproduce

  1. Add a Web Worker to your project, as described in the webpack 5 docs.
  2. Within the Web Worker, use a dynamic import() statement to load something from elsewhere. It can be a local module or something from node_modules.
  3. Consume the web worker somewhere.
  4. You also need the homepage property set in package.json to reproduce this.

(I have fuller details below in the Reproducible demo section.)

Expected behavior

The dynamic import should work.

Actual behavior

The dynamic import fails with a 404 error. A network error is shown. The path includes static/js/static/js, where it should just be static/js.

Reproducible demo

  1. Make a new app using "create react app" with the TypeScript template:
    yarn create react-app example-app --template typescript
  2. Edit the package.json file, adding these two lines:
"type": "module",
"homepage": ".",
  1. Add a new file called src/OtherModule.ts with the following content:
export const message = "Message from another module.";
  1. Add a new file called src/TestWorker.worker.ts with the following content:
/* eslint no-restricted-globals: 0 */
const worker: Worker = self as any;

worker.addEventListener("message", async (event) => {
    const data = event.data;
    console.log("[Worker] Received:", data);
    // This dynamic import is put into a separate chunk by webpack.
    // In a production build, the loading of this module fails with a duplicate "static/js" in the path.
    const { message } = await import("./OtherModule");
    worker.postMessage(message);
});

export {};
  1. Add the following code to src/App.tsx, just before the return statement:
  useEffect(() => {
    const worker = new Worker(new URL("./TestWorker.worker.ts", import.meta.url));
    worker.onmessage = (event: MessageEvent<any>) => {
        console.log(`[App] Received:`, event.data);
    };
    // The expected behaviour is for the worker to send a message in response to this one.
    // In reality, there's an error loading one of the chunks.
    worker.postMessage("abc");
  });
  1. Add useEffect to the import from 'react' in src/App.tsx:
import React, { useEffect } from 'react';
  1. Build the app: yarn build
  2. Serve the build folder via a web server.
  3. Visit the site and open the console.

Expected behavior:

The console should show log output of the App and the Worker receiving messages from each other.

[Worker] Received: abc
[App] Received: Message from another module.

Actual behavior:

A network error is shown. The path includes static/js/static/js, where it should just be static/js.

image

[Worker] Received: abc
GET http://localhost/example-app/static/js/static/js/975.6b1d0bce.chunk.js
[HTTP/1.1 404 Not Found 0ms]
NetworkError: A network error occurred.

kenlyon avatar Jun 06 '22 19:06 kenlyon

I'm having the exact same problem.

I'm using node v16.13.2 and npm 8.1.2.

I tried poking around in the react scripts webpack config with no luck. I'm going to create a script that copies and pastes all the static/js files to static/js/static/js as a workaround for now.

Wicus avatar Jun 08 '22 07:06 Wicus

Same Issue for me. I just created the path so it would work for now. I"m using:

JacobCutshall avatar Jun 16 '22 12:06 JacobCutshall

I am implementing a workaround in the short term, too. I was able to safely determine which files needed to move to static/js/static/js which I did rather than copying everything.

In my case, only main.[hash].js and web-worker.[hash].chunk.js remained in the original location.

I named the latter like so:

new Worker(
    /* webpackChunkName: "web-worker" */
    new URL("./my-file-path.worker.ts", import.meta.url)
);

It's good to be unblocked, but I'd still be interested to see this fixed so I can remove my workaround.

kenlyon avatar Jun 23 '22 06:06 kenlyon

We are also seeing this bug. Removing homepage: '.' may not be an option for us. @JacobCutshall and @kenlyon , could you please explain to me your workaround? 🙏

kialam avatar Jul 12 '22 00:07 kialam

We are also seeing this bug. Removing homepage: '.' may not be an option for us. @JacobCutshall and @kenlyon , could you please explain to me your workaround? 🙏

@kialam I added a function that is called during my build process:

import fse from "fs-extra"; // You could also use node's own "fe" module for moving files.
import path from "path";
import { fileURLToPath, URL } from "url";

async function handleWebpackPathBug() {
    const repoRootUrl = new URL("..", import.meta.url);
    const repoRoot = fileURLToPath(repoRootUrl);
    const sourceFolder = path.join(repoRoot, "build/static/js");
    const destinationFolder = path.join(sourceFolder, "static/js");

    if (!fse.existsSync(sourceFolder)) {
        throw new Error(
            `Failed to handle webpack path bug. Source folder (${sourceFolder}) does not exist.`
        );
    }

    await fse.ensureDir(destinationFolder);
    const staticJsFiles = await fse.readdir(sourceFolder);

    // Move all .js files that do not start with "main." or "web-worker." as those two get correctly loaded directly from static/js.
    const filesToMove = staticJsFiles.filter((val) => /^(?!main\.|web-worker\.).+\.js$/.test(val));

    for (const file of filesToMove) {
        await fse.move(path.join(sourceFolder, file), path.join(destinationFolder, file), {
            overwrite: true,
        });
    }
}

kenlyon avatar Aug 04 '22 21:08 kenlyon

This can be resolved by setting publicPath='auto' in webpack, however this is not currently supported by CRA.

amcgee avatar Sep 30 '22 16:09 amcgee

@amcgee Do you know if support for publicPath='auto' has been added to CRA in the past year?

I've fallen foul of this bug again today as my workaround moved some new chunks that should have remained in the original static/js location.

kenlyon avatar Sep 06 '23 22:09 kenlyon

For anyone struggling at same issue, I solved it with CRACO and the following config on craco.config.js:

module.exports = {
  webpack: {
    configure: {
      output: {
        publicPath: 'auto',
      },
    },
  },
};

Is 2024 and still not supported by CRA.

felps-dev avatar Apr 16 '24 00:04 felps-dev