webdiscus/html-bundler-webpack-plugin

Strange Interaction Between This Plugin and MUI

DylanVause opened this issue · 7 comments

Current behaviour

When I import any component from @mui, the build process appears to freeze. There is no output (to console or files), and when I waited quite some time for the build to progress it did not.

For example, this will build fine (takes ~3500 ms):

import { createRoot } from 'react-dom/client';
import './index.css';

const root = createRoot(document.getElementById('app')!);

root.render(
<div>
    <p>Hello!</p>
    <p>How are you?</p>
</div>
);

But this appears to freeze the plugin:

import { createRoot } from 'react-dom/client';
import './index.css';
import Stack from '@mui/material/Stack';

const root = createRoot(document.getElementById('app')!);

root.render(
<Stack>
    <p>Hello!</p>
    <p>How are you?</p>
</Stack>
);

Expected behaviour

My expectation is a successful build similar to the result when not using MUI (as described above).

Reproduction Example

I have a mini reproduction example here: https://github.com/DylanVause/mui-webpack-bug

Environment

  • OS: Windows 11
  • version of Node.js: v18.17.1
  • version of Webpack: 5.89.0
  • version of the Plugin: 3.4.0

Additional context

My Webpack config is in the reproduction repository, but I will include it here as well for convenience:

const path = require('path');
const HtmlBundlerPlugin = require('html-bundler-webpack-plugin');

module.exports = [
    {
        name: 'client',
        mode: 'development',
        output: {
            path: path.resolve(__dirname, 'client-dist'),
            clean: true
        },
        resolve: {
            alias: {
                client: path.resolve(__dirname, 'client')
            },
            extensions: ['.js', '.ts']
        },
        module: {
            rules: [
                {
                    test: /\.tsx?$/,
                    include: path.resolve(__dirname, 'client'),
                    exclude: /node_modules/,
                    use: [
                        {
                            loader: 'ts-loader',
                            options: {
                                transpileOnly: true,
                            },
                        },
                    ],
                },
                {
                    test: /\.css$/,
                    use: 'css-loader',
                    include: path.resolve(__dirname, 'client'),
                    exclude: /node_modules/,
                },
                {
                    test: /\.(ico|png|jp?g|svg)/,
                    type: 'asset/resource',
                    include: path.resolve(__dirname, 'client'),
                    exclude: /node_modules/,
                    generator: {
                        filename: 'img/[name].[hash:8][ext]'
                    }
                }
            ],
        },
        plugins: [
            new HtmlBundlerPlugin({
                entry: 'client/',
                js: {
                    filename: 'js/[name].[contenthash:8].js'
                },
                css: {
                    filename: 'css/[name].[contenthash:8].css'
                },
            })
        ],
        optimization: {
            usedExports: true,
        }
    },
];

By the way I absolutely love this plugin and apologize in advance if this 'bug' is just a result of me not knowing what I am doing.

Hello @DylanVause,

thanks for the issue report and the repo.
I will research this use case.

Hi @DylanVause

I can reproduce the issue and I will try to fix it.

Temporary Workaroud

Instead of importing the style file in .tsx file, define the style directly in html:

<!DOCTYPE html>
<html>
  <head>
    <title>Test</title>
   <!-- define your styles here -->
    <link href="./style.css" rel="stylesheet" />
  </head>
  <body>
    <h1>Hello World!</h1>
    <div id="app"></div>
    <script src="index.tsx"></script>
  </body>
</html>

Remove the imported style from tsx file:

import { createRoot } from 'react-dom/client';
import Stack from '@mui/material/Stack';
//import './index.css'; // <= remove from js/tsx file

const root = createRoot(document.getElementById('app')!);

root.render(
  <Stack>
    <p>Hello!</p>
    <p>How are you?</p>
  </Stack>
);

P.S. this is realy a hard bug and will be fixed

@webdiscus Thank you for the workaround. I explored the bug some more today and learned that commenting out either import '@mui/material/Stack'; OR commenting out import './index.css'; allows for a successful build. It is only when they are BOTH included that the build fails.

I have modified my example to better illustrate this point:

import '@mui/material/Stack';  // <= disable this line or
import './index.css';          // <= disable this line to build successfully
// ^^^ If both these lines are enabled it will fail to build, regardless of the content of index.css


import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('app')!);

root.render(
<div>
    <p>Hello!</p>
    <p>How are you?</p>
</div>
);
import '@mui/material/Stack';  // <= disable this line or
import './index.css';          // <= disable this line to build successfully

yes, that's exactly what I noticed: only works with MUI or CSS.

Thank you!

@DylanVause

I found the problem. Webpack create many circular dependencies for MUI modules, so walking through circular dependencies occurs an infinite loop. I wrote a new algorithm to find all CSS files imported in JS, that avoids circular dependencies. By me local your example already works. I must yet optimise the algorithm.
Also, I'm in process...

@DylanVause sorry for delay...

The issue is fixed in the version 3.4.4.
Can you please check it?

Thank you! I can confirm this fix works for me as well.