Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generated ES module trying to import already-removed empty JS chunk #15653

Closed
evisong opened this issue Apr 11, 2022 · 8 comments
Closed

Generated ES module trying to import already-removed empty JS chunk #15653

evisong opened this issue Apr 11, 2022 · 8 comments

Comments

@evisong
Copy link

evisong commented Apr 11, 2022

Bug report

What is the current behavior?

I'm leveraging Webpack 5's ESM support to generate ESM libraries, in the meanwhile I use mini-css-extract-plugin to extract all CSS to a single file. With some typical config, I succeed in the code-splitting in a "multi JS + single CSS" style.

However, although the mini-css-extract-plugin only generates a single CSS file, the generated entry ESM is still trying to import the JS file with the same chunk name. This will cause runtime error in browsers. The ESM will fail to initialize due to the following error:

Failed to load resource: the server responded with a status of 404 (Not Found)    :8080/styles.js:1

This is very similar to #11671, the key difference in this issue is outputModule: true I think. I've done some debugging on my own but no lock, any guidance will be appreciated. Thanks in advance.

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

Please checkout https://github.com/evisong/testcase-webpack-esm-css-extract,

yarn
yarn start
$ webpack serve --mode development --no-hot
<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:8080/
<i> [webpack-dev-server] On Your Network (IPv4): http://172.24.49.91:8080/
<i> [webpack-dev-server] On Your Network (IPv6): http://[fe80::1]:8080/
<i> [webpack-dev-server] Content not from webpack is served from 'public, dist' directory
<i> [webpack-dev-middleware] wait until bundle finished: /
asset vendors-node_modules_webpack-dev-server_client_index_js_protocol_ws_3A_hostname_0_0_0_0_port_-262e74.js 193 KiB [emitted] [javascript module] (id hint: vendors)
asset runtime.js 4.93 KiB [emitted] [javascript module] (name: runtime)
asset esm.js 4.36 KiB [emitted] [javascript module] (name: esm)
asset styles.css 493 bytes [emitted] (name: styles) (id hint: styles)
Entrypoint esm 203 KiB = runtime.js 4.93 KiB styles.css 493 bytes vendors-node_modules_webpack-dev-server_client_index_js_protocol_ws_3A_hostname_0_0_0_0_port_-262e74.js 193 KiB esm.js 4.36 KiB
runtime modules 3.73 KiB 15 modules
orphan modules 3.23 KiB [orphan] 4 modules
javascript modules 154 KiB
  modules by path ./node_modules/ 154 KiB 20 modules
  modules by path ./src/ 437 bytes
    modules by path ./src/*.js 337 bytes 3 modules
    modules by path ./src/*.css 100 bytes
      ./src/app-a.css 50 bytes [built] [code generated]
      ./src/app-b.css 50 bytes [built] [code generated]
css modules 67 bytes
  css ./node_modules/css-loader/dist/cjs.js!./src/app-a.css 33 bytes [built] [code generated]
  css ./node_modules/css-loader/dist/cjs.js!./src/app-b.css 34 bytes [built] [code generated]
webpack 5.72.0 compiled successfully in 514 ms

Visit http://localhost:8080/ in browser, the DevTools -> Console shows an error:

Failed to load resource: the server responded with a status of 404 (Not Found)    :8080/styles.js:1

Double-check in DevTools -> Network, the resource is missing:

Request URL: http://localhost:8080/styles.js
Request Method: GET
Status Code: 404 Not Found
Remote Address: [::1]:8080
Referrer Policy: strict-origin-when-cross-origin

But its reference appears at the bottom the http://localhost:8080/esm.js:

// ...
import * as __webpack_chunk_0__ from "./styles.js";
__webpack_require__.C(__webpack_chunk_0__);
// ...

What is the expected behavior?

The entry ESM bundle http://localhost:8080/esm.js should not import anything from the nonexisting styles.js.

According to a quick debug, in webpack/lib/esm/ModuleChunkFormatPlugin.js, the import code for styles chunk (containing both javascript and css/mini-extract modules) is rendered just like the other chunks. However the empty styles.js file is somehow dropped (not sure by Webpack or MiniCssExtractPlugin), but ModuleChunkFormatPlugin is not aware of its nonexistence.

Other relevant information:
webpack version: 5.72.0
Node.js version: 14.18.2
Operating System: macOS Monterey v12.0
Additional tools: yarn v1.22.18

@evisong
Copy link
Author

evisong commented Apr 12, 2022

I've tried to add a quick&dirty check in the following loop, it works. With this change, generated esm.js is not importing the nonexisting styles.js anymore. I'm not sure if this is the right place to do the filtering. If it's the right direction to go, I'll be happy to contribute a PR accordingly.

for (const chunk of chunks) {
if (loadedChunks.has(chunk)) continue;
loadedChunks.add(chunk);
startupSource.add(
`import * as __webpack_chunk_${index}__ from ${JSON.stringify(
getRelativePath(chunk)
)};\n`
);
startupSource.add(
`${RuntimeGlobals.externalInstallChunk}(__webpack_chunk_${index}__);\n`
);
index++;
}

 for (const chunk of chunks) {
   if (loadedChunks.has(chunk)) continue;
+  // bypass if there is no javascript typed modules behind current chunk
+  if (Array.from(chunk.modulesIterable).every(m => !(m.type && m.type.startsWith('javascript')))) continue;
   loadedChunks.add(chunk);
   startupSource.add(
     `import * as __webpack_chunk_${index}__ from ${JSON.stringify(
       getRelativePath(chunk)
   )};\n`

Thanks.

@evisong
Copy link
Author

evisong commented Apr 13, 2022

For anyone who runs into the same issue, before this is fixed in Webpack officially, you may consider my workaround below.

const webpack = require('webpack');

const { JavascriptModulesPlugin } = webpack.javascript;
const PLUGIN_NAME = 'FixEsmImportPlugin';

class FixEsmImportPlugin {
  /**
   * Apply the plugin
   * @param {import('webpack').Compiler} compiler
   */
  apply(compiler) {
    const removeEmptyChunks = compiler.options.optimization.removeEmptyChunks !== false;
    compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
      const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation);
      hooks.renderChunk.tap(PLUGIN_NAME, (_modules, renderContext) => {
        const { chunk, chunkGraph } = renderContext;
        const moduleAndEntrypoints = chunkGraph.getChunkEntryModulesWithChunkGroupIterable(chunk);
        for (const [_module, entrypoint] of moduleAndEntrypoints) {
          for (const chunk of entrypoint.chunks) {
            if (
              removeEmptyChunks &&
              chunk !== entrypoint.getRuntimeChunk() &&
              !chunkGraph.getChunkModulesIterableBySourceType(chunk, 'javascript')
            ) {
              entrypoint.removeChunk(chunk);
              console.debug(`[${PLUGIN_NAME}] Removed empty chunk with id "${chunk.id}" before rendering startupSource`);
            }
          }
        }
      });
    });
  }
}

This plugin runs right before ModuleChunkFormatPlugin, remove the empty chunks (containing no javacript modules) so that the latter only renders import code for existing JS chunk files in outputs.

@webpack-bot
Copy link
Contributor

This issue had no activity for at least three months.

It's subject to automatic issue closing if there is no activity in the next 15 days.

@webpack-bot
Copy link
Contributor

Issue was closed because of inactivity.

If you think this is still a valid issue, please file a new issue with additional information.

@vankop vankop reopened this Jul 29, 2022
@evisong
Copy link
Author

evisong commented Aug 2, 2022

Hi, @tommie, I'm the submitter of guybedford/es-module-shims#295 you've recently resolved, great work indeed!

Excuse me I'm at-ing you here, not really asking for help, but just FYI in case you might run into similar issues when using ESM with Webpack.

@snitin315
Copy link
Member

I believe this same as #16040 and has been fixed by #16809

cc @alexander-akait @TheLarkInn @evisong

@alexander-akait
Copy link
Member

Thank you, I will check it

@alexander-akait
Copy link
Member

Fixed by #16809

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Shipped
Development

No branches or pull requests

5 participants