html-bundler-webpack-plugin icon indicating copy to clipboard operation
html-bundler-webpack-plugin copied to clipboard

HTML bundler v4 makes ESLint overflow with circular references

Open jakezatecky opened this issue 3 months ago • 4 comments

Current behaviour

After upgrading to v4, ESLint fails with a RangeError: Maximum call stack size exceeded whenever it loads a Webpack config that includes this plugin. This is when using the eslint-plugin-import's Webpack resolver.

Expected behaviour

This plugin should not introduce circular structure that break tools attempting to serialize/hash the Webpack config. ESLint (and its major plugins) should be able to consume the config without crashing.

Reproduction

High level reproduction steps:

  1. Create a Webpack config including this plugin.
  2. Install and configure ESLint with eslint-plugin-import.
  3. Put import/resolver to the Webpack config including this plugin:
import webpackConfig from './webpack.config.js';

export default {
   ...
    {
        files: [
            '**/*.{js,jsx}',
        ],
        ignores: ['./node_modules/**/*'],
        settings: {
            'import/resolver': {
                webpack: {
                    config: webpackConfig,
                },
            },
        },
    },
};
  1. Run ESLint and witness the process overflow with Maximum call stack size exceeded inside eslint-module-utils/hash.js.

I created a small repository reproducing this issue. Simply:

  1. Run npm run dev-server to verify that this plugin is building properly.
  2. Run npm run lint to replicate the range error

Environment

  • OS: Windows 11
  • Node.js: v20, v22, v24
  • Webpack: v5.101.3
  • Plugin: v4.21.1

Additional context

Removing this plugin from the Webpack config resolves any linting errors. This means that in my other projects, I can simply delete the plugin from the config file at runtime to avoid the issue. However, I should not have to do such a workaround.

Appreciation for the useful project

  • [ ] After the problem is resolved, do not forget to give a star ⭐

jakezatecky avatar Sep 12 '25 18:09 jakezatecky

I'm not affiliated with this project, but this sounds like a bug in ESLint.

davidmurdoch avatar Sep 12 '25 19:09 davidmurdoch

@jakezatecky

Thanks for the issue, I will research the problem and try to find a solution. Probably ESlint can't handle some tricky code in the plugin.

webdiscus avatar Sep 12 '25 19:09 webdiscus

@jakezatecky

The plugin itself works fine. The problem is in your eslint.config.js setup.

Problem 1

"Maximum call stack size exceeded" issue

Solution Provide the path to Webpack config file instead of imported object.

Problem 2

ESLint: Unable to resolve path to module 'html-bundler-webpack-plugin'. (import/no-unresolved)

Solution Use native Node resolution for webpack.config.js file to avoid self-referencing Webpack resolver.

Replace your config with this one, and it will work:

import js from '@eslint/js';
import importPlugin from 'eslint-plugin-import';
import globals from 'globals';

- import webpackConfig from './webpack.config.js';

export default [
    js.configs.recommended,
    importPlugin.flatConfigs.recommended,
    {
        files: [
            '**/*.{js,jsx}',
        ],
        ignores: ['./node_modules/**/*'],
        languageOptions: {
            ecmaVersion: 'latest',
            sourceType: 'module',
            globals: globals.browser,
        },
        settings: {
             // Account for webpack.resolve.module imports
             'import/resolver': {
                 webpack: {
-                     config: webpackConfig,
+                     config: 'webpack.config.js',  
                 },
             },
        },
    },

+  {
+      files: ['webpack.config.js'],
+      settings: {
+         'import/resolver': { node: true }, // <=  add Node resolution for webpack.config.js
+      },
+   },
];

Now, the npm run lint cmd works fine.

webdiscus avatar Sep 12 '25 20:09 webdiscus

Thank you for the quick reply. Sorry that it took this long to respond.

Problem 2 was entirely on me for not adequately recreating the minimal test case conditions. We normally specify node: { extensions: ['.js'] } for import/resolver in our other projects. I apologize.

Problem 1

For Problem 1, setting the config to your suggestion does not appear to resolve the issue on my end. It results in a different issue, due to the use of require to resolve webpack.config.js (instead of ESM's import). The following would appear for every file linted:

 1:1   error    Resolve error: Error [ERR_REQUIRE_ESM]: require() of ES Module

Even if the suggestion worked, pointing to the Webpack config file would limit our use of more dynamic configurations. This is not a major issue, but sometimes we might like to dynamically manipulate Webpack depending on the environment.

At the moment, we can sidestep Problem 1 entirely simply by remove this plugin from the final Webpack config object, since it is not really needed for ESLint:

import webpackConfig from './webpack.config.js';

...

delete webpackConfig.plugins;

Of course, this is not exactly clean or ideal.

jakezatecky avatar Sep 16 '25 00:09 jakezatecky

@jakezatecky

This is not an issue in the plugin, but a limitation in eslint-plugin-import's Webpack resolver. The circular reference problem occurs because the resolver performs a deep recursive hash of the entire Webpack config object, including plugin instances, which leads to a stack overflow. Since this is outside the plugin's control, unfortunately it can't be fixed here.

Therefore, I will close this issue.

webdiscus avatar Nov 30 '25 20:11 webdiscus