webpack less packaging result cache occasionally fails

106 views Asked by At

When I upgrade webpack4.x to 5.88.1, I use the cache function of webpack to improve the build speed. But in some cases that cannot be reproduced stably, there will be errors in the less compiled code cached by webpack.

For example, my less code is:

.hover-visible(@cls) {
  & .@{cls} {
    visibility: hidden;
  }

  &:hover {
    & .@{cls} {
      visibility: visible;
    }
  }
}

.fx-resize-pane {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;

  &.expand {
    .left-pane {
      .hover-visible(sidebar-toggle);
    }
  }
}

In node_modules/.cache/webpack/0.pack, The cached code is as follows:

.fx-resize-pane {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}
.expand .left-pane {
  visibility: hidden;
}
.expand .left-pane:hover {
  visibility: visible;
}

It's very difficult for me to understand where the error occurred,and never had this kind of problem without upgrading webpack5. Nore importantly, this kind of problem cannot be reproduced stably, so I can't debug the code。

enter image description here enter image description here

version:

  • "webpack": "5.88.1"
  • "webpack-cli": "5.1.4"
  • "less": "3.11.1"
  • "less-loader": "11.1.2"

config:

// webpack.config.common.js
const path = require('path');
const _ = require('lodash');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const PugWebpackPlugin = require('html-webpack-pug-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const glob = require('glob');
const PugSizeWebpackPlugin = require("./plugins/webpack.plugins.pug.size");
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const basePath = path.resolve(__dirname, '../');
const outputPath = path.join(basePath, 'dist');
const htmlPluginArray = [];
const WebpackPluginsPublicPathPlugin = require('./plugins/webpack.plugins.public.path');
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
const LIB_PRIORITY = 100;
const ASSET_PRIORITY = 10;


function getEntry() {
    const entry = {};
    // 读取入口
    glob.sync(path.resolve(basePath, 'views/*/*.pug')).forEach(function (filePath) {
        const [p, appName, fileName] = filePath.match(/\/views\/(.+)\/(.+).pug/);
        const entryPath = path.join(basePath, 'views/' + appName + '/index.tsx');
        if (!['base', 'auth-redirect', 'report-edit'].includes(appName)) {
            entry[appName] = [
                path.resolve(basePath, './build/entry/index.js'),
                'core-js',
                entryPath
            ];
        }
        // 多页面配置
        htmlPluginArray.push({
            template: path.join(basePath, 'views/' + appName + '/' + fileName + '.pug'),
            filename: fileName + '.pug',
            chunks: [appName]
        });
    });
    return entry;
}

function getHtmlPlugin() {
    return htmlPluginArray.map(function (config) {
        return new HtmlWebpackPlugin({
            template: config.template,
            filename: config.filename,
            chunks: config.chunks,
            minify: false,
            inject: 'body'
        });
    });
}

let commonConfig = {
    entry: getEntry(),
    output: {
        filename: '[name].js',
        chunkFilename: '[name].js',
        path: outputPath,
        publicPath: '/pc/',
        pathinfo: false
    },
    cache: {
        type: "filesystem",
        buildDependencies: {
            config: [__filename]
        }
    },
    resolve: {
        extensions: ['.js', '.jsx', '.ts', '.tsx'],
        alias: {
            '~': path.resolve(basePath, 'src'),
            '~crm': path.resolve(basePath, 'src/pages/crm'),
            '~form-design': path.resolve(basePath, 'src/pages/form-edit/container/form-design'),
            'jquery': path.resolve(basePath, 'node_modules/jquery/dist/jquery.slim.min.js'),
            'socket.io-client': path.resolve(basePath, 'node_modules/socket.io-client/dist/socket.io.js'),
            '@fx-ui/jdy-design/icons': path.resolve(basePath, 'node_modules/@fx-ui/jdy-design/lib/icons')
        }
    },
    module: {
        rules: [
            {
                test: /.worker.js$/,
                use: [
                    {
                        loader: 'worker-loader',
                        options: {
                            inline: 'fallback'
                        }
                    }],
                include: path.join(basePath, 'src/workers')
            },
            {
                test: /.tsx?$/,
                use: [
                    'thread-loader',
                    'babel-loader',
                    {
                        loader: 'esbuild-loader',
                        options: {
                            target: 'es2015',
                            tsconfigRaw: require('../tsconfig.json')
                        }
                    }
                ],
                exclude: /node_modules/
            },
            {
                test: /.jsx?$/,
                include: [/[\\/]node_modules[\\/](.*tslib|d3|@antv.*)/],
                exclude: [
                    /[\\/]node_modules[\\/](?!.*tslib|d3|@antv).*/
                ],
                use: ['babel-loader']
            },
            {
                test: /\.css/,
                use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader']
            },
            {
                test: /\.less$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'postcss-loader',
                    'less-loader',
                    {
                        loader: 'style-resources-loader',
                        options: {
                            patterns: [path.join(basePath, 'src/styles/lib/index.less')]
                        }
                    }
                ],
                exclude: /node_modules/
            },
            {
                test: /\.(ttf|eot|woff|woff2)(\?.+)?$/,
                type: 'asset/resource',
                generator: {
                    filename: '[contenthash:12][ext]'
                }
            },
            {
                test: /\.(jpe?g|png|gif|svg)(\?.+)?$/,
                type: 'asset',
                parser: {
                    dataUrlCondition: { maxSize: 8192 }
                },
                generator: {
                    filename: '[contenthash:12][ext]'
                },
                exclude: [path.join(basePath, 'asset'), path.join(basePath, 'src/assets/svgicons')]
            },
            {
                test: /\.svg$/,
                use: [
                    'babel-loader',
                    {
                        loader: '@svgr/webpack',
                        options: {
                            babel: false,
                            icon: true
                        }
                    }
                ],
                include: path.join(basePath, 'src/assets/svgicons')
            }
        ]
    },
    plugins: [
        new CleanWebpackPlugin({
            cleanAfterEveryBuildPatterns: ['!*.pug']
        }),
        new webpack.ProvidePlugin({
            $: 'jquery',
            jQuery: 'jquery',
            "React": "react"
        }),
        new webpack.DefinePlugin({
            APP_ENV: JSON.stringify(process.env.APP_ENV)
        }),
        new MonacoWebpackPlugin({
            languages: ['json', 'javascript', 'python', 'typescript']
        }),
        ...getHtmlPlugin(),
        new PugWebpackPlugin()
    ],
    stats: {
        children: false,
        entrypoints: false,
        modules: false,
    },
    optimization: {
        moduleIds: 'deterministic',
        runtimeChunk: {
            name: 'manifest'
        },
        removeAvailableModules: false,
        removeEmptyChunks: false,
        splitChunks: {
            chunks: 'async',
            cacheGroups: {
                // ......
            }
        }
    }
};


module.exports = commonConfig;
//webpack.dev.config.js
const { merge } = require('webpack-merge');
const commonConfig = require('./webpack.config.common');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const chalk = require('chalk');
const path = require('path');

const devConfig = merge(commonConfig, {
    output: {
        filename: '[name].js',
        chunkFilename: '[name].js',
    },
    mode: 'development',
    devtool: 'eval-cheap-module-source-map',
    watch: true,
    watchOptions: {
        ignored: /node_modules/
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].css',
            ignoreOrder: true
        }),
        // 显示打包进度
        new ProgressBarPlugin({
            width: 100,
            format: `webpack build [:bar] ${chalk.green.bold(':percent')} (:elapsed seconds)`,
            clear: false
        })
    ]
});

module.exports = devConfig

Why does this happen and how to fix it

1

There are 1 answers

0
luckydoge On

I have solved the problem, the reason is the problem of Less version, when I upgraded the version of less to 4.1.3, the problem was solved.