Runtime error appeared after updating to webpack 5. TypeError: Cannot read properties of undefined (reading 'default')

Viewed 7128

After upgrading my webpack from v4 to v5, I got this error that is getting me a hard time debugging.

Uncaught TypeError: Cannot read properties of undefined (reading 'default')
    at Module.UserEntity (main.9e5d0727.js:3998)
    at Module.<anonymous> (main.9e5d0727.js:5952)
    at __webpack_require__ (runtime-main.9e5d0727.js:28)
    at fn (runtime-main.9e5d0727.js:308)
    at Module.<anonymous> (main.9e5d0727.js:5645)
    at __webpack_require__ (runtime-main.9e5d0727.js:28)
    at fn (runtime-main.9e5d0727.js:308)
    at Module.<anonymous> (main.9e5d0727.js:4022)
    at __webpack_require__ (runtime-main.9e5d0727.js:28)
    at fn (runtime-main.9e5d0727.js:308)

I compared my Sources tab between v4 and v5 webpack versions and I saw a difference in files that are generating using this command cross-env NODE_ENV=local webpack serve --config ./config/webpack.config.js --progress --color

Webpack v4 Webpack v5

Here is my current webpack.config.js

const path = require('path');
const process = require('process');
const NODE_ENV = process.env.NODE_ENV;

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const RemoveWebpackPlugin = require('remove-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');

const styledComponentsTransformer = require('typescript-plugin-styled-components').default;
const webpack = require('webpack');

const configLocal = require(`./environments/env.local.js`);
const configCommon = require(`./environments/env.common.js`);

const rootPath = process.cwd();
const resolvePath = path.resolve.bind(rootPath);

const production = ['prod', 'production', 'master'].includes(NODE_ENV) ? 'production' : undefined;
const stage = ['staging', 'stage'].includes(NODE_ENV) ? 'staging' : undefined;
const development = ['development', 'dev'].includes(NODE_ENV) ? 'development' : undefined;
const local = 'local';
const ENVIRONMENT = production || stage || development || local;

const STAFF_OAUTH = process.env.STAFF_OAUTH || configLocal.STAFF_OAUTH;
const BACKEND_URL = process.env.BACKEND_URL ? `"${process.env.BACKEND_URL}"` : configLocal.BACKEND_URL;

const environment = {
  ...configCommon,
  ENVIRONMENT: `"${ENVIRONMENT}"`,
  SENTRY_URL: process.env.SENTRY_URL ? `"${process.env.SENTRY_URL}"` : undefined,
  BACKEND_URL,
  STAFF_OAUTH,
  SC_DISABLE_SPEEDY: true
};

const entryPoint = './src/index.tsx';

const config = {
  mode: production || stage ? 'production' : ENVIRONMENT === local ? 'none' : 'development',
  entry: production ? [entryPoint, require.resolve('./ym')] : entryPoint,

  output: {
    path: path.resolve(__dirname, '..', 'build'),
    filename: 'static/js/[name].[fullhash:8].js',
    chunkFilename: 'static/js/[name].[chunkhash:8].js',
    publicPath: '/'
  },

  resolve: {
    extensions: ['.tsx', '.ts', '.js', '.jsx'],
    modules: ['node_modules', resolvePath('src')],
    fallback: { fs: false }
  },

  module: {
    rules: [
      {
        test: /\.tsx?$/,
        include: resolvePath('src'),
        exclude: /node_modules/,
        use: [
          'thread-loader',
          {
            loader: 'ts-loader',
            options: {
              getCustomTransformers: () => ({
                before: [styledComponentsTransformer()]
              }),
              happyPackMode: true,
              transpileOnly: true
            }
          }
        ]
      },
      {
        test: /\.raw\.svg$/,
        use: [
          {
            loader: 'raw-loader',
            options: {
              esModule: false
            }
          }
        ]
      },
      {
        test: /\.(png|svg|jpg|gif)$/,
        exclude: /\.raw\.svg$/,
        loader: 'url-loader',
        options: {
          esModule: false,
          limit: 10000,
          name: 'static/media/[name].[contenthash].[ext]'
        }
      },
      {
        test: /\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1
            }
          }
        ]
      }
    ]
  },

  optimization: {
    minimize: !!(production || stage),
    minimizer: [new TerserPlugin()],
    runtimeChunk: {
      name: entrypoint => `runtime-${entrypoint.name}`
    },
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  },

  ignoreWarnings: [/export .* was not found in/],

  plugins: [
    // fix "process is not defined" error
    new webpack.ProvidePlugin({
      process: 'process/browser'
    }),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: path.resolve(__dirname, '..', 'public'),
          to: path.resolve(__dirname, '..', 'build')
        }
      ]
    }),
    new HtmlWebpackPlugin({ template: 'src/index.html' }),
    new webpack.DefinePlugin(environment),
    new RemoveWebpackPlugin([path.resolve(__dirname, '..', 'build'), path.resolve(__dirname, '..', '.cache')], 'hide')
  ],

  devServer: {
    static: path.resolve(__dirname, '..'),
    hot: true,
    port: 3000,
    host: 'localhost',
    historyApiFallback: true
    // watchOptions: {
    //   ignored: resolvePath('cypress')
    // }
  },

  performance: {
    hints: false
  }
};

if (ENVIRONMENT === development || ENVIRONMENT === local) {
  config.devtool = 'source-map';
}

if (ENVIRONMENT === local) {
  const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');

  config.plugins.push(
    new ForkTsCheckerWebpackPlugin({
      eslint: {
        files: './src/**/*.{ts,tsx,js,jsx}'
      },
      typescript: {
        diagnosticOptions: {
          semantic: true,
          syntactic: true
        }
      }
    })
  );
} else {
  const WorkboxPlugin = require('workbox-webpack-plugin');

  config.plugins.push(
    new WorkboxPlugin.GenerateSW({
      clientsClaim: true,
      skipWaiting: false,
      mode: production || stage ? 'production' : undefined,
      exclude: [/\.map$/, /asset-manifest\.json$/, /LICENSE/]
    })
  );
}

module.exports = config;

And my package.json file

{
  "name": "dispatcher-service-front",
  "version": "1.27.0",
  "license": "UNLICENSED",
  "scripts": {
    "cypress:open": "cypress open",
    "start": "cross-env NODE_ENV=local webpack serve --config ./config/webpack.config.js --progress --color",
    "start:ci": "webpack serve --config ./config/webpack.config.js",
    "build": "webpack --config ./config/webpack.config.js",
    "analyze": "webpack --profile --json > stats.json && webpack-bundle-analyzer stats.json"
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.{ts,tsx}": "eslint -c .eslintrc.staged.js"
  },
  "dependencies": {
    "@types/node": "^16.0.0",
    "@types/react": "^16.14.1",
    "@types/react-dom": "^16.9.13",
    "@types/react-infinite-scroller": "^1.2.1",
    "@types/react-router-dom": "^5.1.5",
    "@types/uuid": "^8.3.1",
    "computed-async-mobx": "^6.1.0",
    "dayjs": "^1.10.6",
    "mobx": "^5.15.4",
    "mobx-react": "^6.2.2",
    "mobx-utils": "^5.5.7",
    "pik-front-utils": "git+https://gitlab+deploy-token-9:p1C2k__vanbHWx_ntuM_@git.pik.ru/pik-software/pik-front-utils.git#semver:1.0.3",
    "pik-ui-kit": "git+https://gitlab+deploy-token-8:gqgws4edpujq2MyATRR7@git.pik.ru/pik-software/pik-ui-kit.git#semver:1.10.3",
    "process": "^0.11.10",
    "react": "^16.14.0",
    "react-dom": "^16.14.0",
    "react-infinite-scroller": "^1.2.4",
    "react-is": "^17.0.2",
    "react-router-dom": "^5.2.0",
    "reflect-metadata": "^0.1.13",
    "sanitize.css": "^12.0.1",
    "serializr": "^2.0.5",
    "styled-components": "^5.2.1",
    "typescript": "^4.3.0",
    "typescript-ioc": "^3.2.2",
    "uuid": "^8.3.2"
  },
  "devDependencies": {
    "@types/styled-components": "^5.1.11",
    "@typescript-eslint/eslint-plugin": "^4.28.2",
    "@typescript-eslint/parser": "^4.28.2",
    "copy-webpack-plugin": "^9.1.0",
    "cross-env": "^7.0.2",
    "css-loader": "^6.5.1",
    "cypress": "^8.6.0",
    "cypress-file-upload": "^5.0.8",
    "eslint": "^7.30.0",
    "eslint-plugin-cypress": "^2.11.3",
    "eslint-plugin-import": "^2.23.4",
    "eslint-plugin-jsdoc": "^35.4.1",
    "eslint-plugin-prefer-arrow": "^1.2.3",
    "eslint-plugin-react": "^7.24.0",
    "eslint-plugin-react-hooks": "^4.2.0",
    "file-loader": "^6.2.0",
    "fork-ts-checker-webpack-plugin": "^6.4.0",
    "html-webpack-plugin": "^5.5.0",
    "husky": "^4.3.8",
    "lint-staged": "^11.0.0",
    "raw-loader": "^4.0.2",
    "remove-webpack-plugin": "^1.2.2",
    "style-loader": "^3.3.1",
    "terser-webpack-plugin": "^5.2.5",
    "thread-loader": "^3.0.4",
    "ts-loader": "^9.2.6",
    "typescript-plugin-styled-components": "^2.0.0",
    "url-loader": "^4.1.1",
    "webpack": "^5.64.0",
    "webpack-bundle-analyzer": "^4.5.0",
    "webpack-cli": "^4.9.1",
    "webpack-dev-server": "^4.5.0",
    "workbox-webpack-plugin": "^6.4.1"
  }
}

My file structure looks like this

I changed a few things according to webpack 5 migration guide

  1. In config.output.filename i changed 'static/js/[name].[hash:8].js' => 'static/js/[name].[fullhash:8].js'

  2. Removed node: { fs: 'empty' } and added fallback: { fs: false } in config.output.resolve

  3. stats.warningFilter renamed to ignoreWarnings

  4. Added this plugin to fix "process is not defined" error

     new webpack.ProvidePlugin({
       process: 'process/browser'
     }),
    
  5. Redirected index.html file from public to src folder and changed a corresponding template in HtmlWebpackPlugin. I did that because there was a conflict on this file between CopyWebpackPlugin and HtmlWebpackPlugin

  6. devServer.contentBase renamed to devServer.static

  7. Commented devServer.watchOptions for now, because watchOptions key is no longer available

  8. Removed two plugins new webpack.HotModuleReplacementPlugin() and new HardSourceWebpackPlugin({ cacheDirectory: path.resolve(__dirname, '..', '.cache')

That's it. Also, I compared how do files build between v4 and v5 versions using webpack --config ./config/webpack.config.js and there was no difference at all! Except in index.html file script tag were moved to <head>

Thanks for taking your time reading this, I will appreciate any help!

3 Answers

Thanks for your answers. In my case the problem was in importing class and use it as a type and for some reason it was making the error.

I fixed it by changing

import { UserEntity } from 'models';

to

import type { UserEntity } from 'models';

After that my runtime error disappeared

For my version of this error, the issue seemed to be that I was importing a file with an alias in webpack from within the same directory.

To give an example, I had this directory setup:

~/utils (~ is aliased in my webpack)
  /fileWithTheImport.ts (
  /fileToImport.ts
  /index.ts (exports * from the other two files)

I was using import { named } from ~/utils initially and seeing the error.

Once I changed that import to import { named } from './fileToImport.ts' the error resolved and things worked as expected.

In your case

I see that it's having a hard time reading 'default', which would mean to me that the default import for UserEntity may have a similar problem to what I had.

I would try to remove any alias paths if UserEntity is in the same directory and hopefully your issue will go away.

I fixed it by webpack-cli version upgrade

Related