@edwardxyt/gws-cli
Version:
这是一个web脚手架工具,用于生成基于webpack5,生成typescript+react17+mobx5+reactRouter6的应用。初衷是要解决多入口,多环境。单独编译单独运行的脚手架。做到小而美。拒绝锦上添花。
325 lines (321 loc) • 11.5 kB
JavaScript
const webpack = require('webpack');
const fs = require("fs");
const path = require('path');
const dayjs = require('dayjs');
const chalk = require('chalk');
const log = console.log;
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin'); //复制静态资源
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); //压缩css
const TerserPlugin = require('terser-webpack-plugin'); // 压缩js
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); //提取到.css文件里
const CompressionWebpackPlugin = require('compression-webpack-plugin'); //gzip压缩
const { GenerateSW } = require('workbox-webpack-plugin'); // 引入 PWA 插件
const BundleAnalyzerPlugin =
require('webpack-bundle-analyzer').BundleAnalyzerPlugin; // 图形工具
const ProgressBarPlugin = require('progress-bar-webpack-plugin'); // 进度条
// 加载全局配置文件
log(`
${chalk.white(`webpack:watch`)} - ${chalk.green(`加载全局文件`)}
`);
let rootDir = path.resolve(__dirname, '../');
let app_config = require('.')(rootDir);
module.exports = async () => {
{
// 启动调试工具
if (app_config.console) {
app_config.main.push(app_config.console);
echo('开启Vconsole');
}
}
return {
entry: {
app: [...app_config.main],
},
output: {
publicPath: app_config.cdn_path || '/', // 需要cdn 就开启
filename: 'scripts/[name].[contenthash:8].js',
path: `${app_config.dist}/${app_config.entry}`,
chunkFilename: 'scripts/[name].[contenthash:8].chunk.js',
assetModuleFilename: 'media/[name].[hash][ext]',
},
target: 'web', // 配置 package.json 的 browserslist 字段会导致 webpack-dev-server 的热更新功能直接失效,为了避免这种情况需要给 webpack 配上 target 属性
bail: true, // 在第一个错误出现时抛出失败结果,而不是容忍它。
devtool: 'inline-source-map', //source-map
mode: 'production',
resolve: app_config.resolve,
// externals: app_config.externals, // 注意抽包的类库不可以在此包含
watchOptions: {
poll: 1000, // 轮询间隔时间
aggregateTimeout: 500, // 防抖(在输入时间停止刷新计时)
ignored: /node_modules/,
},
cache: {
type: 'filesystem',
version: app_config.environmentHash,
cacheDirectory: path.resolve(app_config.node_module_dir, '.cacheW'),
store: 'pack',
buildDependencies: {
defaultWebpack: ['webpack/lib/'],
config: [__filename],
tsconfig: [path.resolve(app_config.rootDir, 'tsconfig.json')].filter(f =>
fs.existsSync(f)
),
},
},
infrastructureLogging: {
level: 'none',
},
stats: {
preset: 'normal',
colors: true,
source: true,
errorDetails: true,
},
optimization: {
minimize: true,
minimizer: [
new CssMinimizerPlugin(), // 就像 optimize-css-assets-webpack-plugin 一样,但在 source maps 和 assets 中使用查询字符串会更加准确,支持缓存和并发模式下运行。
new TerserPlugin({
extractComments: false,
parallel: true, // 使用多进程并发运行以提高构建速度
terserOptions: {
mangle: {
safari10: true, // 混淆,默认也是开的,mangle也是可以配置很多选项的
},
parse: {
ecma: 8,
},
compress: {
ecma: 5,
inline: 2,
warnings: false,
drop_console: true, //传true就是干掉所有的console.*这些函数的调用.
drop_debugger: true, //干掉那些debugger;
comparisons: false,
pure_funcs: ['console.log'], // 如果你要干掉特定的函数比如console.info ,又想删掉后保留其参数中的副作用,那用pure_funcs来处理
},
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
}),
],
splitChunks: {
cacheGroups: {
chartsVendor: {
test: app_config.library.chartsVendor.test,
priority: app_config.library.chartsVendor.priority,
name: 'chartsVendor',
chunks: 'all',
},
reactVendor: {
test: app_config.library.reactVendor.test,
priority: app_config.library.reactVendor.priority,
name: 'reactVendor',
chunks: 'all',
},
antdVendor: {
test: app_config.library.antdVendor.test,
priority: app_config.library.antdVendor.priority,
name: 'antdVendor',
chunks: 'all',
},
utilsVendor: {
test: app_config.library.utilsVendor.test,
priority: app_config.library.utilsVendor.priority,
name: 'utilsVendor',
chunks: 'all',
},
vendors: {
test: app_config.library.vendors.test,
priority: app_config.library.vendors.priority,
name: 'vendors',
chunks: 'all',
},
},
},
},
module: {
strictExportPresence: true, // 将缺失的导出提示成错误而不是警告
rules: [
{
test: /\.(tsx?|jsx|js)$/,
use: [
{
loader: 'babel-loader',
options: { cacheDirectory: true }, // cacheDirectory:babel-loader 在执行的时候,可能会产生一些运行期间重复的公共文件,造成代码体积大冗余,同时也会减慢编译效率,所以开启该配置将这些公共文件缓存起来,下次编译就会加快很多
},
{
loader: 'ts-loader',
},
{
loader: 'source-map-loader',
},
],
exclude: /node_modules/,
},
{
test: /\.ejs$/,
loader: 'ejs-loader',
options: {
esModule: false,
},
},
{
test: /\.(le|c)ss$/,
use: [
{
loader: MiniCssExtractPlugin.loader, // 非DEV环境,这里替换style-loader,即可提取css文件
},
{
loader: 'css-loader',
options: {
modules: {
mode: 'global',
localIdentName: '[name]__[local]--[hash:base64:8]',
},
},
},
{
loader: 'less-loader',
options: {
lessOptions: {
strictMath: true,
},
},
},
{
loader: 'postcss-loader',
},
],
},
// webpack5 已内置资源模块,因此无需再下载 file-loader、url-loader
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
type: 'asset', // asset:自动选择导出为单独文件或者 dataURL形式(默认为8KB). 之前有url-loader设置asset size limit 限制实现
parser: {
dataUrlCondition: {
// dataUrlCondition:指定资源大小(单位字节)
maxSize: 4 * 1024,
},
},
},
{
test: /\.(eot|svg|ttf|woff|woff2?)$/,
type: 'asset/resource',
},
{
test: /\.md$/,
type: 'asset/source'
}
],
},
plugins: [
// 打包进度条
new ProgressBarPlugin(),
// 打包后在.js/.css页头的时间
// hash :[hash]
// chunkhash :[chunkhash]
// name :[name]
// filebase :[filebase]
// query :[query]
// file :[file]
new webpack.BannerPlugin({
banner: `@Last Modified time ${dayjs().format('YYYY-MM-DD HH:mm:ss')}`,
}),
// 图形查看编译结果
new BundleAnalyzerPlugin(),
// 解决 hash 频繁变动的问题
new webpack.ids.HashedModuleIdsPlugin({
context: __dirname,
hashFunction: 'sha256',
hashDigest: 'hex',
hashDigestLength: 20,
}),
// 允许创建一个在编译时可以配置的全局常量
new webpack.DefinePlugin(app_config.inject),
// 使用ProvidePlugin加载的模块在使用时将不再需要import和require进行引入
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
}),
// 和 style-loader 功能一样,只是打包后会单独生成 css 文件而非直接写在 html 文件中,用于生产环境,开发环境不需要另外生成文件使用 style-loader 即可
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:8].css',
chunkFilename: 'css/[name].[contenthash:8].chunk.css',
}),
// 打包时忽略本地化内容
// Moment.js是一个非常流行的库,它捆绑了大型locale文件
//如果你不使用Moment.js,你可以删除它:
new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/,
}),
// 使用模板引擎生成html
new HtmlWebpackPlugin({
title: '生成的HTML文档的标题',
filename: 'index.html',
template: app_config.template_path,
templateParameters: {
foo: 'bar',
COMPILED_AT: dayjs().format('YYYY-MM-DD HH:mm:ss'),
env: app_config.inject.__ENV__,
debug: app_config.inject.__DEBUG__,
api_path: app_config.inject.__API__,
meta: '',
},
minify: {
// 压缩 HTML 文件
removeComments: true, // 移除 HTML 中的注释
collapseWhitespace: true, // 删除空白符与换行符
minifyCSS: true, // 压缩内联 css
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyURLs: true,
},
inject: true, // true或'body'所有javascript资源都将放置在body元素的底部
// favicon: path.resolve('public/favicon.ico'),
chunks: [
'chartsVendor',
'reactVendor',
'antdVendor',
'utilsVendor',
'vendors',
'app',
], // entry中的 app 入口才会被打包
}),
// gzip压缩
new CompressionWebpackPlugin({
filename: '[path][base].gz',
algorithm: 'gzip',
test: /\.(js|css)(\?.*)?$/i, // CSS并没有生效,因为提取mini-css-extract-plugin没提供此功能
threshold: 10240,
minRatio: 0.8,
deleteOriginalAssets: false, // 是否删除源文件,默认: false
}),
// 配置 PWA
new GenerateSW({
clientsClaim: true,
skipWaiting: true,
}),
// 静态文件处理
new CopyPlugin({
patterns: [
{
from: path.join(app_config.entryDir, 'README.md'),
to: path.join(app_config.dist, app_config.entry),
},
],
}),
],
performance: false, // Turn off performance processing
};
};