PeerDepsExternalsPlugin breaks the build since Webpack5 upgrade, how to fix that?

Viewed 420

I upgraded my react app project to Webpack5. While upgrading I tackled not just Webpack, but also all its plugins, trying to check them one by one, in order to face less issues as possible. The thing is that I got almost immediately this issue (that does not occur if I remove new PeerDepsExternalsPlugin(), from the Webpack plugins' list. But any update was done on the lib peer-deps-externals-webpack-plugin still on version "^1.0.4"

This is the main webpack config:

module: {
    rules: [
      {
        test: /\.(js|jsx|tsx)(\?.*)?$/,
        exclude: [/node_modules/],
        use: 'babel-loader'
      },
      {
        test: /\.tsx?$/,
        exclude: [/node_modules/],
        use: 'ts-loader'
      },
      {
        include: path.resolve(__dirname, assetsPath),
        use: [
          {
            loader: 'file-loader',
            options: {
              context: path.resolve(__dirname, 'PRIVATE_PATH'),
              name: '[path][name].[ext]?hash=[hash:base64:5]'
            }
          }
        ]
      }
    ].filter(Boolean)
  },
  optimization: {
    minimizer: [
      new TerserPlugin({
        test: /\.min\.js$/
      })
    ]
  },
  plugins: [
    new webpack.DefinePlugin({
      ASSETS_PATH_PREFIX: `"${getFullAssetPathPrefix()}"`
    }),
    new PeerDepsExternalsPlugin(),
    ...extraPlugins,
    new LintFilename(
      /^(?!(icon|bg|img)-(?!(.*[_A-Z]|[\s]))).*(jpe?g|png|svg)$/,
      path.resolve(__dirname, 'assets/img'),
      'image'
    ),
    new CopyPlugin({
      patterns: [
        {
          from: path.resolve(__dirname, 'lib/**/*.d.ts'),
          to: 'types'
        }
      ]
    })
  ]

And this is the build error:

$ webpack --mode production
$ cross-env BABEL_ENV=es babel lib --extensions .js,.jsx,.ts,.tsx --out-dir dist/es
[build:js:frontend-es] Successfully compiled 125 files with Babel (4423ms).
[build:js:frontend-es] yarn run build:js:frontend-es exited with code 0
[build:js:frontend] internal/crypto/hash.js:84
[build:js:frontend]     throw new ERR_INVALID_ARG_TYPE(
[build:js:frontend]     ^
[build:js:frontend]
[build:js:frontend] TypeError [ERR_INVALID_ARG_TYPE]: The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received undefined
[build:js:frontend]     at Hash.update (internal/crypto/hash.js:84:11)
[build:js:frontend]     at BulkUpdateDecorator.update (/project/node_modules/webpack/lib/util/createHash.js:49:14)
[build:js:frontend]     at ExternalModule.updateHash (/project/node_modules/webpack/lib/ExternalModule.js:465:8)
[build:js:frontend]     at Compilation.createModuleHashes (/project/node_modules/webpack/lib/Compilation.js:2726:12)
[build:js:frontend]     at /project/node_modules/webpack/lib/Compilation.js:2064:11
[build:js:frontend]     at eval (eval at create (/project/node_modules/webpack/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)
[build:js:frontend]     at /project/node_modules/webpack/lib/optimize/ModuleConcatenationPlugin.js:449:32
[build:js:frontend]     at processTicksAndRejections (internal/process/task_queues.js:75:11) {
[build:js:frontend]   code: 'ERR_INVALID_ARG_TYPE'
[build:js:frontend] }

Any ideas? Thanks

1 Answers

I ran into this same issue, it turns out there's an open PR on the plugin's repo.

In the meantime, I swiped the code from that PR and saved it as external-plugin.js in my project root directory.

'use strict';

const ExternalModuleFactoryPlugin = require('webpack/lib/ExternalModuleFactoryPlugin');

class PeerDepsExternalsPlugin {
  apply(compiler) {
    const peerDependencies = getPeerDependencies();

    // webpack 5+
    if (typeof compiler.options.output.library === 'object') {
      compiler.hooks.compile.tap('PeerDepsExternalsPlugin', ({ normalModuleFactory }) => {
        new ExternalModuleFactoryPlugin(
          compiler.options.output.library.type,
          peerDependencies
        ).apply(normalModuleFactory);
      });
    }
    // webpack 4+
    else {
      compiler.hooks.compile.tap('compile', params => {
        new ExternalModuleFactoryPlugin(
          compiler.options.output.libraryTarget,
          peerDependencies
        ).apply(params.normalModuleFactory);
      });
    }
  }
}

function getPeerDependencies() {
  try {
    const { resolve } = require('path');
    const pkg = require(resolve(process.cwd(), 'package.json'));
    return Object.keys(pkg.peerDependencies);
  } catch(err) {
    return [];
  }
}

module.exports = PeerDepsExternalsPlugin;

and then in my webpack config, I changed the import to use this file instead of the package from npm:

const PeerDepsExternalsPlugin = require('./external-plugin');
Related