timarney / react-app-rewired

Override create-react-app webpack configs without ejecting
MIT License
9.81k stars 425 forks source link

Multiple entries rewire error: Cannot read property 'filter' of undefined #421

Closed vl4d1m1r4 closed 4 years ago

vl4d1m1r4 commented 4 years ago

I need to have multiple entries with create-react-app, but I also don't want to eject. There is a minimal reproducible sample repo here: https://github.com/vl4d1m1r4/cra-multi-entry. The modifications follow the weback v4 configuration documentation and consist only of adding a separate entry for admin.js and separate HtmlWebpackPlugin to add the bundle in different admin.html.

When doing yarn start it just hangs on starting development environment. When running with yarn build it shows the following error:

yarn build
yarn run v1.19.1
$ react-app-rewired build
Creating an optimized production build...
Failed to compile.

Cannot read property 'filter' of undefined

error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Any hints on what might be the issue?

dawnmist commented 4 years ago

A huge thank you for the reproducible sample - it enabled me to at least narrow down where the issue was occurring (which would have been impossible without it).

Since the yarn build gave some output rather than just seeming to hang, I've tried tracing through the build function calls. As far as I have been able to trace, it's failing inside Webpack when Webpack tries to emit (write out) the files produced by the compilation, but it isn't making into the actual per-file emit code (so either prior to stepping through the list of files to emit, or maybe in a beforeEmit hook).

Inside node_modules/webpack/Compiler.js, in the function emitAssets:

I was able to get the list of file names it was supposed to be trying to emit, which looked sensible:

[
  "static/media/logo.25bf045c.svg",
  "static/js/0.b4c4e099.chunk.js",
  "static/css/admin.b100e6da.chunk.css",
  "static/js/admin.73e3d81d.chunk.js",
  "static/css/index.b100e6da.chunk.css",
  "static/js/index.23cf0cae.chunk.js",
  "static/js/runtime-admin.8b8bd490.js",
  "static/js/runtime-index.ea3b42c9.js",
  "static/css/index.b100e6da.chunk.css.map",
  "static/css/admin.b100e6da.chunk.css.map",
  "static/js/0.b4c4e099.chunk.js.map",
  "static/js/admin.73e3d81d.chunk.js.map",
  "static/js/index.23cf0cae.chunk.js.map",
  "static/js/runtime-admin.8b8bd490.js.map",
  "static/js/runtime-index.ea3b42c9.js.map"
]

I haven't been able to work out what gets called with the hooks.emit function, so I couldn't trace what was happening any further.

My guess is that a plugin that has defined a compilation step for hooks.emit (or maybe hooks.beforeEmit) is throwing the error. It didn't seem to be HtmlWebpackPlugin - I tried checking that one and the emit function there was running to completion.

Possible directions to go from here: check the other plugins (e.g. the css one) for any additional config they may need for the multiple entry fields, or if a specific version is required for multiple entry fields to work.

I believe that something somewhere inside the Webpack emit hook is expecting to have/use an array, but instead the variable used is undefined and the function using that variable isn't testing whether it exists prior to trying to filter it.

React-app-rewired itself isn't responsible for the error - it's being generated inside the Webpack compiler so it is likely to be either a missing configuration snippet for a plugin, a plugin that isn't compatible with multiple entry points, or a bug in Webpack or a plugin. Best of luck finding the problem from here, hopefully the above helps you to know what to look for/ask about upstream.

vl4d1m1r4 commented 4 years ago

Thank you very much for the hints! I was finally able to find the issue. The problem was in the ManifestPlugin configuration from CRA. It assumes that the entry point will be called main, the default from Webpack:

     // Generate an asset manifest file with the following content:
      // - "files" key: Mapping of all asset filenames to their corresponding
      //   output file so that tools can pick it up without having to parse
      //   `index.html`
      // - "entrypoints" key: Array of files which are included in `index.html`,
      //   can be used to reconstruct the HTML if necessary
      new ManifestPlugin({
        fileName: 'asset-manifest.json',
        publicPath: publicPath,
        generate: (seed, files, entrypoints) => {
          const manifestFiles = files.reduce((manifest, file) => {
            manifest[file.name] = file.path;
            return manifest;
          }, seed);
          const entrypointFiles = entrypoints.main.filter(  // <--- This line here
            fileName => !fileName.endsWith('.map')
          );

          return {
            files: manifestFiles,
            entrypoints: entrypointFiles,
          };
        },
      }),

I was able to make a successful build after adapting the generate method to take into consideration the multiple entries:

...
const entrypointFiles = {};
Object.keys(entrypoints).forEach(entrypoint => {
  entrypointFiles[entrypoint] = entrypoints[entrypoint].filter(fileName => !fileName.endsWith('.map'));
});
...

I will check tomorrow if the generated manifest is useful with the change and update the issue.

Here is the commit on the sample repository for full details: 8c62e91

dawnmist commented 4 years ago

Fantastic! Thank you for the update on what the cause of the issue was - it can serve as a reference for anyone else that tries to set up multiple entry points. :)

zx88cvb commented 4 years ago

Solve my problem perfectly!

bigkrp commented 4 years ago

Thank you very much for the hints! I was finally able to find the issue. The problem was in the ManifestPlugin configuration from CRA. It assumes that the entry point will be called main, the default from Webpack:

    // Generate an asset manifest file with the following content:
     // - "files" key: Mapping of all asset filenames to their corresponding
     //   output file so that tools can pick it up without having to parse
     //   `index.html`
     // - "entrypoints" key: Array of files which are included in `index.html`,
     //   can be used to reconstruct the HTML if necessary
     new ManifestPlugin({
       fileName: 'asset-manifest.json',
       publicPath: publicPath,
       generate: (seed, files, entrypoints) => {
         const manifestFiles = files.reduce((manifest, file) => {
           manifest[file.name] = file.path;
           return manifest;
         }, seed);
         const entrypointFiles = entrypoints.main.filter(  // <--- This line here
           fileName => !fileName.endsWith('.map')
         );

         return {
           files: manifestFiles,
           entrypoints: entrypointFiles,
         };
       },
     }),

I was able to make a successful build after adapting the generate method to take into consideration the multiple entries:

...
const entrypointFiles = {};
Object.keys(entrypoints).forEach(entrypoint => {
  entrypointFiles[entrypoint] = entrypoints[entrypoint].filter(fileName => !fileName.endsWith('.map'));
});
...

I will check tomorrow if the generated manifest is useful with the change and update the issue.

Here is the commit on the sample repository for full details: 8c62e91

Thanks! Really helped with my problem!

I use a similar interface for two different applications and build them in one script.

tibotiber commented 3 years ago

Thank you very much for this. It has helped me update rewire-entry to be CRA3 compatible and publish rewire-js-entry. The old lib is not maintained anymore.

buggy-95 commented 2 years ago

Thank a lot.

igraziatto commented 2 years ago

It's 2022 and this thread has helped me with a similar issue. Thank you!

vkxedeen commented 6 months ago

It's still actual

camillalo commented 6 months ago

这是来自QQ邮箱的假期自动回复邮件。您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。