webpack / webpack

A bundler for javascript and friends. Packs many modules into a few bundled assets. Code Splitting allows for loading parts of the application on demand. Through "loaders", modules can be CommonJs, AMD, ES6 modules, CSS, Images, JSON, Coffeescript, LESS, ... and your custom stuff.
https://webpack.js.org
MIT License
64.3k stars 8.73k forks source link

runtime.js cannot find bundles to require when code splitting among multiple bundles is enabled #18409

Open Arandhras opened 1 month ago

Arandhras commented 1 month ago

Bug report

I have encountered a bug while trying to split code into multiple bundles, while preventing duplication of said code. In other words, I want to create a "main" bundle which is the "true" entry point of the application, and one or more auxiliary bundles. To this end, I have followed the documentation present at this page.

What is the current behavior? The following is the config object contained in my webpack.config.js file:

const config = {
    entry: {
        lib: {
            import: './src/foo.js',
        },
        main: {
            import: './src/index.js',
            dependOn: ['lib']
        }
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js',
    },
    target: 'node',
    optimization: {
        runtimeChunk: 'single',
    },
};

After launching the npx webpack command and executing node .\dist\main.js, node throws the following Error:

Error: Cannot find module './undefined'
Require stack:
- d:\svi\webpack-codesplit-test\dist\runtime.js
- d:\svi\webpack-codesplit-test\dist\main.js
    at Module._resolveFilename (node:internal/modules/cjs/loader:1077:15)
    at Module._load (node:internal/modules/cjs/loader:922:27)
    at Module.require (node:internal/modules/cjs/loader:1143:19)
    at require (node:internal/modules/cjs/helpers:119:18)
    at __webpack_require__.f.require (d:\svi\webpack-codesplit-test\dist\runtime.js:110:28)
    at d:\svi\webpack-codesplit-test\dist\runtime.js:48:40
    at Array.reduce (<anonymous>)
    at __webpack_require__.e (d:\svi\webpack-codesplit-test\dist\runtime.js:47:67)
    at Array.map (<anonymous>)
    at __webpack_require__.X (d:\svi\webpack-codesplit-test\dist\runtime.js:74:22) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    'd:\\svi\\webpack-codesplit-test\\dist\\runtime.js',
    'd:\\svi\\webpack-codesplit-test\\dist\\main.js'
  ]
}

If the current behavior is a bug, please provide the steps to reproduce.

I have written a simple test project that is available at this GitHub repository. After downloading the project simply run npm run dev to get the required node_modules (webpack, webpack-cli and their dependencies) and then automatically execute node .\dist\main.js.

What is the expected behavior?

It seems that .\dist\runtime.js is broken, specifically in two points.

/******/    /* webpack/runtime/get javascript chunk filename */
/******/    (() => {
/******/        // This function allow to reference async chunks and sibling chunks for the entrypoint
/******/        __webpack_require__.u = (chunkId) => {
/******/            // return url for filenames based on template
/******/            return undefined;
/******/        };
/******/    })();

Why is this function returning undefined regardless of the chunkId being passed? In development mode, if I change the return line into

return `${chunkId}.js`

the require() that is immediately called after, is working.

Another issue I've identified, lies within this function:

/******/        var installChunk = (chunk) => {
/******/            var moreModules = chunk.modules, chunkIds = chunk.ids, runtime = chunk.runtime;
/******/            for(var moduleId in moreModules) {
/******/                if(__webpack_require__.o(moreModules, moduleId)) {
/******/                    __webpack_require__.m[moduleId] = moreModules[moduleId];
/******/                }
/******/            }
/******/            if(runtime) runtime(__webpack_require__);
/******/            for(var i = 0; i < chunkIds.length; i++)
/******/                installedChunks[chunkIds[i]] = 1;
/******/        
/******/        };

Sometimes the chunk argument being passed is an empty object {}, therefore chunkIds is undefined. In this case when the for loop is reached, accessing chunkIds.length results in an Error being thrown.

Other relevant information: webpack version: 5.91.0 Node.js version: v18.18.0 Operating System: Windows 10 Enterprise 22H2 Additional tools: none

alexander-akait commented 1 week ago

Do you build application or library? What is your target - node or web?

Arandhras commented 1 week ago

Do you build application or library? What is your target - node or web?

My target is node.

alexander-akait commented 1 week ago

Why do you need runtimeChunk for node?

alexander-akait commented 1 week ago

Yeah, it is a bug, something wrong with dependOn and optimization.runtimeChunk with node target

Feel free to send a fix

Arandhras commented 1 week ago

We are developing a server architecture where you have a "core bundle" (where the actual entry point of the application is) and several "client" ones, and we want to be able to split this code so that we can deploy the application with zero, one or just a few client bundles instead of all of them.

The rationale is twofold: 1) since we can have different on-premise deployments at different clients, and we do not wish to expose client-specific business logic to someone who isn't the intended user; 2) when updating our application deployments without altering the underlying node_modules, we would like to install only the core or client bundle(s) that actually got modified instead of the entirety of the codebase reduced to a single, monolithic bundle.