UNPKG

webpack-builder-react-xd

Version:

小盾安全前端团队构建器

362 lines (348 loc) 11.3 kB
const webpack = require('webpack'); const chalk = require('chalk'); const fs = require('fs'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const ProgressBarPlugin = require('progress-bar-webpack-plugin'); const CssUrlRelativePlugin = require('css-url-relative-plugin'); const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; const MinniCssExtractPlugin = require('mini-css-extract-plugin'); const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); const CopyFiles = require('./plugins/CopyFiles'); const paths = require('./paths'); const babelConfig = require('./babelConfig'); const { getEntrys } = require('./entrys'); const clientEnvironment = require('./env'); const { getAlais, getPublicPath, getBuilderConfig, createEnvironmentHash } = require('./hlper'); const { LOG_VALUE_COLOR, LOG_LABEL_COLOR } = require('./constant'); const builderConfig = getBuilderConfig(); const env = clientEnvironment(); const publicPath = getPublicPath(); const isEnvDevelopment = env.raw.NODE_ENV === 'development'; const isEnvProduction = env.raw.NODE_ENV === 'production'; // 生产环境将css提取到单独的文件,生产环境和足强架子微前端需要,不能和style-loader一起使用 const doExtract = isEnvProduction || process.env.MICRO; const getStyleLoaders = (cssOptions, preProcessor) => { return [ doExtract && MinniCssExtractPlugin.loader, !doExtract && require.resolve('style-loader'), // builderConfig.typescript && require.resolve('css-modules-typescript-loader'), { loader: require.resolve('css-loader'), options: cssOptions, }, { loader: require.resolve('postcss-loader'), options: { postcssOptions: { ident: 'postcss', config: false, plugins: [ 'postcss-flexbugs-fixes', 'postcss-nested', [ 'postcss-preset-env', { autoprefixer: { flexbox: 'no-2009', }, stage: 3, }, ], 'postcss-normalize', ], }, }, }, preProcessor, ].filter(Boolean); }; /** @type {import('webpack').Configuration} */ const config = { entry: paths.appIndex, mode: env.raw.NODE_ENV, cache: { type: 'filesystem', version: createEnvironmentHash(env.raw), cacheDirectory: paths.appWebpackCache, store: 'pack', buildDependencies: { defaultWebpack: ['webpack/lib/'], config: [__filename], tsconfig: [paths.appTsConfig].filter(f => fs.existsSync(f) ), }, }, output: { path: paths.appDist, filename: 'js/[name].js', chunkFilename: 'js/[name].js', assetModuleFilename: 'media/[name]-[hash:6][ext]', publicPath, }, resolve: { // 解析模块的可选项 extensions: ['.mjs', '.js', '.jsx', '.json', '.ts', '.tsx', '.less'], // 用到文件的扩展名 // 优化模块查找路径,src优先于 node_modules modules: [paths.appSrc, 'node_modules'], alias: getAlais(), }, module: { rules: [{ // 使用oneOf, 会依次配置loader, 如果未匹配到的会默认使用最后的 file-loader oneOf: [ // TODO: Merge this config once `image/avif` is in the mime-db // https://github.com/jshttp/mime-db { test: [/\.avif$/], type: 'asset', mimetype: 'image/avif', }, // 加载图片 { test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/], type: 'asset/resource', generator: { filename: 'images/[name]_[hash:6][ext]' } }, { test: /\.svg$/, use: [ { loader: require.resolve('@svgr/webpack'), options: { prettier: false, svgo: false, svgoConfig: { plugins: [{ removeViewBox: false }], }, titleProp: true, ref: true, }, }, { loader: require.resolve('file-loader'), options: { name: 'media/[name].[ext]', }, }, ], issuer: { and: [/\.(ts|tsx|js|jsx|md|mdx)$/], }, }, // 加载js { test: /\.(js|jsx|ts|tsx)$/, exclude: /\/node_modules\//, include: paths.appSrc, // 精确指定要处理的目录 loader: require.resolve('babel-loader'), options: babelConfig, }, // 支持引用html模板 { test: /\.(html|htm|tpl)$/, loader: require.resolve('html-loader'), }, // src/*.css -> css module { test: /\.css$/, exclude: /\/node_modules\//, use: getStyleLoaders( { importLoaders: 1, modules: { mode: 'local', exportLocalsConvention: 'camelCase', localIdentName: '[name]__[local]__[hash:base64:5]', }, } ), }, // ./node_modules/*.css -> 全局css // 没有被上面 css 匹配上的 src/*.css -> 全局css { test: /\.css$/, include: [paths.appNodeModules], use: getStyleLoaders({ importLoaders: 1, }), // Don't consider CSS imports dead code even if the // containing package claims to have no side effects. // Remove this when webpack adds a warning or an error for this. // See https://github.com/webpack/webpack/issues/6571 sideEffects: true, }, { test: /\.less$/, exclude: /\/node_modules\//, use: getStyleLoaders( { importLoaders: 1, modules: { mode: 'local', exportLocalsConvention: 'camelCase', localIdentName: '[name]__[local]__[hash:base64:5]', }, }, { loader: require.resolve('less-loader'), options: { lessOptions: { javascriptEnabled: true, }, }, } ), }, // ./node_modules/*.less -> 全局less { test: /\.less$/, include: [paths.appNodeModules], use: getStyleLoaders( { importLoaders: 1, }, { loader: require.resolve('less-loader'), options: { lessOptions: { modifyVars: { 'primary-color': '#244ba8', 'link-color': '#244ba8', 'font-size-base': '12px', 'text-color': 'rgba(0,0,0,.65)', }, javascriptEnabled: true, }, }, } ), sideEffects: true, }, { exclude: [/^$/, /\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/], type: 'asset/resource', }, ], }], }, plugins: [ new webpack.DefinePlugin(env.stringified), // 处理css路径问题 new CssUrlRelativePlugin(), // 显示构建进度 new ProgressBarPlugin(), // 处理路径大小写问题 new CaseSensitivePathsPlugin(), // 处理 .locale文件 // 先注释起来吧,要支持国际化 // new webpack.IgnorePlugin({ // resourceRegExp: /^\.\/locale$/, // contextRegExp: /moment$/, // }), // 静态资源输出 new CopyWebpackPlugin({ patterns: [ { from: paths.appAssets, to: paths.appDistAssets }, ], }), ].filter(Boolean), // 定义sourcemap 配置 devtool: 'cheap-module-source-map', // 关闭性能提示 performance: { hints: false, }, }; console.log(chalk.hex(LOG_LABEL_COLOR).bold('INFO:'), `当前构建模式为 ${chalk.hex(LOG_VALUE_COLOR).bold(env.raw.NODE_ENV)} 模式`); if (doExtract) { config.plugins.push( new MinniCssExtractPlugin({ filename: 'css/[name].css', ignoreOrder: true, }) ); } if (isEnvDevelopment) { // 热更新 config.plugins.push(new ReactRefreshWebpackPlugin({ overlay: false, })); } if (isEnvProduction) { const TerserPlugin = require('terser-webpack-plugin'); const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); config.optimization = { minimize: true, minimizer: [ new TerserPlugin( { parallel: true, terserOptions: { // https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions compress: { comparisons: false, }, safari10: true, format: { comments: false, }, }, extractComments: false, } ), new CssMinimizerPlugin(), ], }; // 开启性能提示 config.performance = {}; // sourceMap 开启nosources config.devtool = 'nosources-source-map'; } // 支持ts if (builderConfig.typescript) { console.log(chalk.hex(LOG_LABEL_COLOR).bold('INFO:'), `当前构建已开启 ${chalk.hex(LOG_VALUE_COLOR).bold('TypeScript')} 支持`); // eslint 检查 config.plugins.push( new ForkTsCheckerWebpackPlugin({ async: true, }) ); } // 开启 profile 分析 if (process.env.PROFILE) { console.log(chalk.hex(LOG_LABEL_COLOR).bold('INFO:'), `当前构建已开启 ${chalk.hex(LOG_VALUE_COLOR).bold('profile')} 分析`); config.plugins.push(new BundleAnalyzerPlugin()); } // 开启微前端构建 if (env.raw.MICRO) { if (process.MICRO_CONFIG.mode === 'SINGLE') { console.log(chalk.hex(LOG_LABEL_COLOR).bold('INFO:'), `当前构建已开启 微前端 ${chalk.hex(LOG_VALUE_COLOR).bold('SINGLE(单一入口)')} 构建模式`); getEntrys(config); } else { console.log(chalk.hex(LOG_LABEL_COLOR).bold('INFO:'), `当前构建已开启 微前端 ${chalk.hex(LOG_VALUE_COLOR).bold('UNITY(统一入口)')} 构建模式`); getEntrys(config); } if (isEnvProduction) { config.plugins.push(new CopyFiles()); } } else if (builderConfig.entry === 'MPA') { console.log(chalk.hex(LOG_LABEL_COLOR).bold('INFO:'), `当前构建已开启 ${chalk.hex(LOG_VALUE_COLOR).bold('MPA')} 模式`); // 自动生成html模板 getEntrys(config, 'MPA'); } else { console.log(chalk.hex(LOG_LABEL_COLOR).bold('INFO:'), `当前构建已开启 ${chalk.hex(LOG_VALUE_COLOR).bold('SPA')} 模式`); getEntrys(config, 'SPA'); } const entryInfo = !(env.raw.MICRO && process.MICRO_CONFIG.mode === 'UNITY') ? JSON.stringify(config.entry) : JSON.stringify(config.entry) .split(',') .join('\n ') .replace('{', '\n ') .replace('}', ''); console.log(chalk.hex(LOG_LABEL_COLOR).bold('INFO:'), `当前构建入口为 ${chalk.hex(LOG_VALUE_COLOR).bold(entryInfo)}`); module.exports = config;