webpackwebpack-4webpack-5

Why are Webpack 5 externals returning an empty object?


I have a completely functional library package. I upgraded from Webpack 4 to Webpack 5, and all of a sudden my external packages return an empty object. I am at a complete loss of ideas. My config was already proper, including solution from:

Why webpack returns an empty object when requiring its output?

My module now exports this alone and i still have an issue:

import React from 'react';

const uxp = require('uxp');

export const ExportAsSection = () => {
  console.log("UXP TEST:")
  // empty object, although doing the same from a console shows the expected global object
  console.log(uxp)

  // Renders no issue
  return (
      <div>I am a panel</div>
  );
};

My relevant config is this:

const path = require('path');
const { merge } = require('webpack-merge');
const baseConfig = require('../../webpack.config');

const config = {
  context: __dirname,
  entry: './src/index.tsx',
  externals: {
    lodash: 'lodash',
    'react-dom': 'react-dom',
    react: 'react',
    uxp: 'commonjs2 uxp'
  },
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.(svg)$/i,
        use: [
          'babel-loader',
          {
            loader: 'react-svg-loader',
            options: {
              svgo: {
                plugins: [
                  {
                    convertPathData: { noSpaceAfterFlags: false },
                  },
                  {
                    mergePaths: false,
                  },
                  {
                    removeViewBox: false,
                  },
                ],
              },
            },
          },
        ],
      },
    ],
  },
  output: {
    filename: 'example-export.umd.js',
    path: path.resolve(__dirname, 'dist'),
    library: '@example/export',
  },
};

module.exports = merge(baseConfig, config);

The base config is this:

const webpack = require('webpack');
const path = require('path');
const git = require('git-rev-sync');

const dev = process.env.MODE === 'development' ? '-dev' : '';
const production = process.env.MODE === 'production';

const pluginRev = `${git.short()}${dev}`;

module.exports = {
  entry: './src/index.ts',
  mode: production ? 'production' : 'development',
  devtool: production ? false : 'eval-cheap-module-source-map',
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              configFile: path.resolve(__dirname, './babel.config.json'),
            },
          },
          {
            loader: 'ts-loader',
            options: {
              allowTsInNodeModules: true,
            },
          },
        ],
      },
    ],
  },
  plugins: [
    new webpack.DefinePlugin({
      PLUGIN_REV: JSON.stringify(pluginRev),
    }),
    new webpack.ProvidePlugin({
      Buffer: ['buffer', 'Buffer'],
    }),
  ],
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
    fallback: {
      buffer: require.resolve('buffer'),
    },
  },
  output: {
    libraryTarget: 'umd',
  },
};

The output UMD file also has this bit that looks seemingly correct:

/***/ "uxp":
/*!**********************!*\
  !*** external "uxp" ***!
  \**********************/
/***/ ((module) => {

module.exports = require("uxp");

/***/ }),

Update I also sanity checked by downgrading to Webpack 4, and was unable to reproduce issue. I then went back to Webpack 5, with my Webpack 5 configuration, and made devtool: false so it can build again from one conflicting, breaking change, and issue returned. So definitely something about upgrading here.

Update 2 I pared down all my code to just return a div from React so the UMD compiled JS was pretty readable. My UMD package is imported into a project using Webpack 4. When the compiled code is initially imported, self.require, window.require, and require all return what I expect. By the time webpack's require method is called, require no longer returns anything (altho self.require and window.require do. If I try to just do console.log(require) at that point, I also get an exception like __webpack_modules__[moduleId] although I can't find where on earth require would be overridden in such a way. I am wondering if require is manipulated one way via Webpack 4 and another via Webpack 5 and the conflict is causing my issues.


Solution

  • The issue is around how webpack overwrites require. Having a webpack 5 bundle within a webpack 4 app caused these issues. I switched my app to Vite specifically to work around this.