Failed to asynchronously load component with loadable components using Webpack and React

2k Views Asked by At

The error

I get the following error when (what appears to be) vendor scripts that try downloading:

index.js:1 loadable-components: failed to asynchronously load component 
{fileName: undefined, chunkName: undefined, error: 'Loading chunk 1117 failed.\n(missing: http://localhost:3002/1117.a60143c46b916ef6a52d.vendor.js)'}

I don't know if this helps but when I don't obfuscate the file names, 1117 is the node_modules styled-components script.

The resource exists in the public directory in the correct output path and according to the network tab it gets downloaded successfully.

Script 1117.a60143c46b916ef6a52d.vendor.js  200 script  astronomix.js:1 13.5 kB 11 ms

Troubleshooting

  • I have a UI library with loadable-components exports to create the chunks.
  • The client consuming apps throw this error which started happening yesterday.
  • I reverted the code back a few weeks for both apps and it still broke.
  • I created a quick CRA and it breaks there too.
  • I replaced loadable-components with React.lazy and I still encountered issues.
  • I thought there was an error with one of the components, I isolated it by creating only one component without Styled Components. This breaks unless I remove code splitting. Components render correctly then.

Webpack config

const path = require('path');
const postcss = require('postcss');
const cssnano = require('cssnano');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { LicenseWebpackPlugin } = require('license-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const webpack = require('webpack');
const PUBLIC_PATH = process.env.PUBLIC_PATH || '/';

module.exports = {
  mode: 'production',
  devtool: false,
  entry: {
    library: './src/components/index.js',
  },
  output: {
    publicPath: PUBLIC_PATH,
    filename: '[name].js',
    sourceMapFilename: '[id].[contenthash].lib.map',
    chunkFilename: '[id].[contenthash].lib.js',
    path: path.resolve(__dirname, 'dist/'),
    library: 'MyLibrary', 
    libraryTarget: 'umd',
    globalObject: 'this',
    assetModuleFilename: 'images/[name].[contenthash][ext][query]',
  },
  externals: {
    react: {
      root: 'React',
      commonjs: 'react',
      commonjs2: 'react',
      amd: 'react',
    },
    'react-dom': {
      root: 'ReactDOM',
      commonjs: 'react-dom',
      commonjs2: 'react-dom',
      amd: 'ReactDOM',
    },
  },
  resolve: {
    extensions: ['.js'],
    alias: {
      react: path.resolve('./node_modules/react'),
      'react-dom': path.resolve('./node_modules/react-dom'),
    },
  },
  optimization: {
    usedExports: true,
    minimize: true,
    emitOnErrors: true,
    removeAvailableModules: true,
    concatenateModules: true,
    moduleIds: 'deterministic',
    splitChunks: {
      chunks: 'async',
      minSize: 20000,
      minRemainingSize: 0,
      minChunks: 1,
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      enforceSizeThreshold: 50000,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          reuseExistingChunk: false,
          filename: (pathData) => {
            return `${pathData.chunk.id}.[contenthash].vendor.js`;
          },
        },
        default: {
          reuseExistingChunk: false,
        },
      },
    },
    minimizer: [
      new MiniCssExtractPlugin({
        filename: (pathData) => {
          return `${pathData.chunk.name}.[contenthash].min.css`;
        },
        chunkFilename: (pathData) => {
          return `${pathData.chunk.id}.[contenthash].min.css`;
        },
      }),
      new TerserPlugin({
        terserOptions: {
          format: {
            // Tell terser to remove all comments except for the banner added via LicenseWebpackPlugin.
            // This can be customized further to allow other types of comments to show up in the final js file as well.
            // See the terser documentation for format.comments options for more details.
            comments: (astNode, comment) => {
              return comment.value.startsWith('! licenses are at ');
            },
          },
          // This option was added to fix the following error in 10n6:
          // Uncaught TypeError: Super expression must either be null or a function
          keep_fnames: true,
        },
        extractComments: false, // prevents TerserPlugin from extracting a [chunkName].js.LICENSE.txt file
      }),
    ],
  },
  plugins: [
    // This makes it possible for us to safely use env vars on our code
    new webpack.DefinePlugin({
      // https://webpack.js.org/guides/public-path/#root
      'process.env.PUBLIC_PATH': JSON.stringify(PUBLIC_PATH),
    }),
    new CleanWebpackPlugin(),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: 'src/vendors/**/*.css',
          to: './css/[name].[contenthash].min[ext]',
          transform: (content, path) => {
            return postcss([cssnano])
              .process(content, {
                from: path,
              })
              .then((result) => {
                return result.css;
              });
          },
        },
      ],
    }),
    new LicenseWebpackPlugin({
      addBanner: true,
      renderBanner: (filename, modules) => {
        return `/*! licenses are at ${filename}*/`;
      },
      handleMissingLicenseText: (packageName, licenseType) => {
        console.log(`No license found for ${packageName}`);
        return 'UNKNOWN';
      },
      licenseTextOverrides: {
        'styled-components':
          'https://github.com/styled-components/styled-components/blob/main/LICENSE',
        'use-onclickoutside': 'No license found',
        'use-latest': 'No license found',
        'are-passive-events-supported': 'No license found',
        'react-select':
          'MIT | https://github.com/JedWatson/react-select/blob/master/LICENSE',
      },
    }),
  ],
  module: {
    rules: [
      { oneOf: [{ type: 'javascript/auto' }] },
      {
        test: /\.(js|jsx)$/,
        exclude: [/\/node_modules\//, /\/demos\//],
        loader: 'babel-loader',
      },
      {
        test: /\.(png|jpg|jpeg|webp|gif|svg|ico)$/,
        type: 'asset/resource',
      },
      {
        test: /\.(s*)css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
      },
    ],
  },
};

Dependencies

...
"devDependencies": {
    "@babel/core": "7.18.10",
    "@babel/plugin-proposal-class-properties": "7.18.6",
    "@babel/plugin-transform-react-jsx": "7.18.10",
    "@babel/preset-env": "7.18.10",
    "@babel/preset-react": "7.18.6",
    "@loadable/component": "5.15.2",
    "@react-theming/storybook-addon": "1.1.7",
    "@storybook/addon-actions": "6.5.10",
    "@storybook/addon-console": "1.2.3",
    "@storybook/addon-docs": "6.5.10",
    "@storybook/addon-essentials": "6.5.10",
    "@storybook/addon-links": "6.5.10",
    "@storybook/addon-storyshots": "6.5.10",
    "@storybook/builder-webpack5": "6.5.10",
    "@storybook/manager-webpack5": "6.5.10",
    "@storybook/node-logger": "6.5.10",
    "@storybook/preset-create-react-app": "4.1.2",
    "@storybook/react": "6.5.10",
    "@wojtekmaj/enzyme-adapter-react-17": "0.6.7",
    "babel-jest": "28.1.3",
    "babel-loader": "8.2.5",
    "babel-plugin-styled-components": "2.0.7",
    "babel-plugin-transform-react-jsx": "6.24.1",
    "bootstrap": "5.2.0",
    "classnames": "2.3.1",
    "clean-webpack-plugin": "4.0.0",
    "copy-webpack-plugin": "11.0.0",
    "core-js": "3.24.1",
    "css-loader": "6.7.1",
    "css-minimizer-webpack-plugin": "4.0.0",
    "cssnano": "5.1.12",
    "date-fns": "2.29.1",
    "enzyme": "3.11.0",
    "enzyme-to-json": "3.6.2",
    "eslint": "8.21.0",
    "eslint-config-prettier": "8.5.0",
    "eslint-plugin-import": "2.26.0",
    "eslint-plugin-jsx-a11y": "6.6.1",
    "eslint-plugin-react": "7.30.1",
    "eslint-plugin-storybook": "0.6.3",
    "jest": "28.1.3",
    "jest-environment-enzyme": "7.1.2",
    "jest-environment-jsdom": "28.1.3",
    "jest-enzyme": "7.1.2",
    "jest-styled-components": "7.0.8",
    "license-webpack-plugin": "4.0.2",
    "mini-css-extract-plugin": "2.6.1",
    "node-sass": "7.0.1",
    "numeral": "2.0.6",
    "prettier": "2.7.1",
    "prop-types": "15.8.1",
    "ramda": "0.28.0",
    "react-bootstrap": "2.4.0",
    "react-helmet": "6.1.0",
    "react-icons": "4.4.0",
    "react-is": "18.2.0",
    "react-places-autocomplete": "7.3.0",
    "react-scripts": "5.0.1",
    "react-select": "5.4.0",
    "react-test-renderer": "17.0.2",
    "regenerator-runtime": "0.13.9",
    "sass": "1.54.2",
    "sass-loader": "13.0.2",
    "styled-components": "5.3.5",
    "terser-webpack-plugin": "5.3.3",
    "use-onclickoutside": "0.4.1",
    "webpack": "5.74.0",
    "webpack-cli": "4.10.0"
  },
  "peerDependencies": {
    "react": "17.0.2",
    "react-dom": "17.0.2"
  }
...

Code splitting

import loadable from '@loadable/component';

global.SC_DISABLE_SPEEDY = true;

export const Theme = loadable(() => import('./Theme'), {
  resolveComponent: (component) => component.default,
});

...

Other

Related question but did not assist in solving the problem loadable-components: failed to asynchronously load component. Let me know if you need anything else.


Edits

Stacktrace

Uncaught ChunkLoadError: Loading chunk 2526 failed.
(missing: http://localhost:3002/2526.4c1d9fbd8f9d0aa16a08.vendor.js)
    at __webpack_require__.f.j (library.js:1:1)
    at library.js:1:1
    at Array.reduce (<anonymous>)
    at __webpack_require__.e (library.js:1:1)
    at h.resolveComponent [as requireAsync] (library.js:1:1)
    at cachedLoad (library.js:1:1)
    at InnerLoadable.resolveAsync (library.js:1:1)
    at InnerLoadable.loadAsync (library.js:1:1)
    at InnerLoadable.componentDidMount (library.js:1:1)
    at commitLifeCycles (react-dom.development.js:20663:1)
    at commitLayoutEffects (react-dom.development.js:23426:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:3945:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:3994:1)
    at invokeGuardedCallback (react-dom.development.js:4056:1)
    at commitRootImpl (react-dom.development.js:23151:1)
    at unstable_runWithPriority (scheduler.development.js:468:1)
    at runWithPriority$1 (react-dom.development.js:11276:1)
    at commitRoot (react-dom.development.js:22990:1)
    at performSyncWorkOnRoot (react-dom.development.js:22329:1)
    at scheduleUpdateOnFiber (react-dom.development.js:21881:1)
    at updateContainer (react-dom.development.js:25482:1)
    at react-dom.development.js:26021:1
    at unbatchedUpdates (react-dom.development.js:22431:1)
    at legacyRenderSubtreeIntoContainer (react-dom.development.js:26020:1)
    at Object.render (react-dom.development.js:26103:1)
    at Module.<anonymous> (index.js:5:1)
    at ./src/index.js (index.js:7:1)
    at __webpack_require__ (bootstrap:851:1)
    at fn (bootstrap:150:1)
    at 1 (index.js:7:1)
    at __webpack_require__ (bootstrap:851:1)
    at checkDeferredModules (bootstrap:45:1)
    at Array.webpackJsonpCallback [as push] (bootstrap:32:1)
    at main.chunk.js:1:71
1

There are 1 best solutions below

0
On

I deleted the node_modules in the library directory and then reinstalled with npm install. I believe the cached node modules must have gone wonky with the numerous git reverts I made.