UNPKG

@edwardxyt/gws-cli

Version:

这是一个web脚手架工具,用于生成基于webpack5,生成typescript+react17+mobx5+reactRouter6的应用。初衷是要解决多入口,多环境。单独编译单独运行的脚手架。做到小而美。拒绝锦上添花。

540 lines (532 loc) 18.6 kB
const webpack = require('webpack'); const fs = require("fs"); const path = require('path'); const detect = require('detect-port'); const dayjs = require('dayjs'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const ESLintPlugin = require('eslint-webpack-plugin'); const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); const ReactRefreshTypeScript = require('react-refresh-typescript'); const FriendlyErrorsWebpackPlugin = require('@soda/friendly-errors-webpack-plugin'); //终端日志美化工具 const chalk = require('chalk'); const log = console.log; // [================================加载全局配置文件================================] log(` ${chalk.white(`webpack:development`)} - ${chalk.green(`加载全局文件`)} `); let rootDir = path.resolve(__dirname, '../'); let app_config = require('.')(rootDir); // [================================end================================] // [================================webpack配置参数================================] let initWebpackConfig = (detectPort) => { return { entry: { app: [...app_config.main], }, output: { publicPath: '/', 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 属性 devtool: 'cheap-module-source-map', // inline-source-map mode: 'development', resolve: app_config.resolve, // externals: app_config.externals, // 注意抽包的类库不可以在此包含 stats: { preset: 'minimal', colors: true, source: true, errorDetails: true, }, cache: { type: 'filesystem', // cache.type:缓存类型,值为 memory 或 filesystem,分别代表基于内存的临时缓存,以及基于文件系统的持久化缓存 version: app_config.environmentHash, cacheDirectory: path.resolve(app_config.node_module_dir, '.cacheD'), store: 'pack', buildDependencies: { defaultWebpack: ['webpack/lib/'], config: [__filename], tsconfig: [path.resolve(app_config.rootDir, 'tsconfig.json')].filter(f => fs.existsSync(f) ), }, }, infrastructureLogging: { level: 'none', }, optimization: { 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: [ { enforce: 'pre', exclude: /@babel(?:\/|\\{1,2})runtime/, test: /\.(js|mjs|jsx|ts|tsx|css)$/, loader: require.resolve('source-map-loader'), }, { test: /\.(tsx?|jsx|js)$/, exclude: /node_modules/, use: [ { loader: 'babel-loader', options: { cacheDirectory: true // cacheDirectory:babel-loader 在执行的时候,可能会产生一些运行期间重复的公共文件,造成代码体积大冗余,同时也会减慢编译效率,所以开启该配置将这些公共文件缓存起来,下次编译就会加快很多 }, }, { loader: 'ts-loader', options: { getCustomTransformers: () => ({ before: [ReactRefreshTypeScript()], }), transpileOnly: true, }, } ], }, { test: /\.ejs$/, include: /templates/, loader: 'ejs-loader', options: { esModule: false, }, }, { test: /\.(le|c)ss$/i, use: [ { loader: 'style-loader', }, { loader: 'css-loader', }, { loader: 'less-loader', }, { 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 FriendlyErrorsWebpackPlugin(), new ESLintPlugin({ context: app_config.src, files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.json'], exclude: '/node_modules/', emitError: true, failOnWarning: true, fix: true }), // 使用ProvidePlugin加载的模块在使用时将不再需要import和require进行引入 new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery', 'window.jQuery': 'jquery', }), new ReactRefreshWebpackPlugin(), // HMR 实际上只开启 hot:true 就会自动识别有无声明该插件,没有则自动引入,但是怕有隐藏问题这里还是手动加上了 // new webpack.HotModuleReplacementPlugin(), // 允许创建一个在编译时可以配置的全局常量 new webpack.DefinePlugin(app_config.inject), // 打包时忽略本地化内容 // 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 }, inject: true, // true或'body'所有javascript资源都将放置在body元素的底部 // favicon: path.resolve('public/favicon.ico'), chunks: [ 'chartsVendor', 'reactVendor', 'antdVendor', 'utilsVendor', 'vendors', 'app', ], // entry中的 app 入口才会被打包 }), ], devServer: { headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': '*', 'Access-Control-Allow-Headers': '*', }, // allowedHosts: [], // 该选项允许将允许访问开发服务器的服务列入白名单。 compress: true, // 启用http compression(gzip)进行数据压缩传输 port: detectPort, host: app_config.IP, client: { logging: 'verbose', progress: true, overlay: { // 当出现编译错误或警告时,在浏览器中显示全屏覆盖。 errors: true, warnings: true, }, }, historyApiFallback: true, // { index: `${app_config.dist}/${app_config.entry}/index.html`} proxy: { '/api': { target: 'http://localhost:3000', pathRewrite: { '^/api': '' }, }, }, open: true, hot: 'only', // HMR时 填true }, performance: false, // Turn off performance processing }; } // [================================end================================] module.exports = async () => { const port = app_config.devServer.port; const _port = await detect(port) if (port == _port) { log(` ${chalk.blue(`[ === 启动webpack-dev-server === ]`)} 端口号: ${chalk.yellow(`${port} 没有被占用,放心使用。`)} 服务器运行在: ${chalk.red(`http://${app_config.IP}:${port}`)} `); } else { log(` ${chalk.blue(`[ === 启动webpack-dev-server === ]`)} 端口号: ${chalk.yellow(`${port} 已占用, 尝试使用端口号: ${_port}!`)} 服务器运行在: ${chalk.red(`http://${app_config.IP}:${_port}`)} `); } log(` ${chalk.blue(`[ == 动态配置项 == ]`)} port: ${chalk.green(`${_port}`)} `); return { entry: { app: [...app_config.main], }, output: { publicPath: '/', 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 属性 devtool: 'cheap-module-source-map', // inline-source-map mode: 'development', resolve: app_config.resolve, // externals: app_config.externals, // 注意抽包的类库不可以在此包含 stats: { preset: 'minimal', colors: true, source: true, errorDetails: true, }, cache: { type: 'filesystem', // cache.type:缓存类型,值为 memory 或 filesystem,分别代表基于内存的临时缓存,以及基于文件系统的持久化缓存 version: app_config.environmentHash, cacheDirectory: path.resolve(app_config.node_module_dir, '.cacheD'), store: 'pack', buildDependencies: { defaultWebpack: ['webpack/lib/'], config: [__filename], tsconfig: [path.resolve(app_config.rootDir, 'tsconfig.json')].filter(f => fs.existsSync(f) ), }, }, infrastructureLogging: { level: 'none', }, optimization: { 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: [ { enforce: 'pre', exclude: /@babel(?:\/|\\{1,2})runtime/, test: /\.(js|mjs|jsx|ts|tsx|css)$/, loader: require.resolve('source-map-loader'), }, { test: /\.(tsx?|jsx|js)$/i, use: [ { loader: 'babel-loader', options: { cacheDirectory: true // cacheDirectory:babel-loader 在执行的时候,可能会产生一些运行期间重复的公共文件,造成代码体积大冗余,同时也会减慢编译效率,所以开启该配置将这些公共文件缓存起来,下次编译就会加快很多 }, }, { loader: 'ts-loader', options: { getCustomTransformers: () => ({ before: [ReactRefreshTypeScript()], }), transpileOnly: true, }, } ], exclude: /node_modules/, }, { test: /\.ejs$/i, loader: 'ejs-loader', options: { esModule: false, }, exclude: /node_modules/, }, { test: /\.(le|c)ss$/i, use: [ { loader: 'style-loader', }, { loader: 'css-loader', options: { }, }, { loader: 'postcss-loader', }, { loader: 'less-loader', options: { lessOptions: { strictMath: true, }, }, }, ], }, // 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?)$/i, type: 'asset/resource', }, { test: /\.md$/i, type: 'asset/source' } ], }, plugins: [ new FriendlyErrorsWebpackPlugin(), new ESLintPlugin({ context: app_config.src, files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.json'], exclude: '/node_modules/', emitError: true, failOnWarning: true, fix: true }), // 使用ProvidePlugin加载的模块在使用时将不再需要import和require进行引入 // 由于qiankun引入时一来jquery 所以下面代码需要注释 // new webpack.ProvidePlugin({ // $: 'jquery', // jQuery: 'jquery', // 'window.jQuery': 'jquery', // }), new ReactRefreshWebpackPlugin(), // HMR 实际上只开启 hot:true 就会自动识别有无声明该插件,没有则自动引入,但是怕有隐藏问题这里还是手动加上了 // new webpack.HotModuleReplacementPlugin(), // 允许创建一个在编译时可以配置的全局常量 new webpack.DefinePlugin(app_config.inject), // 打包时忽略本地化内容 // 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 }, inject: true, // true或'body'所有javascript资源都将放置在body元素的底部 // favicon: path.resolve('public/favicon.ico'), chunks: [ 'chartsVendor', 'reactVendor', 'antdVendor', 'utilsVendor', 'vendors', 'app', ], // entry中的 app 入口才会被打包 }), ], devServer: { headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': '*', 'Access-Control-Allow-Headers': '*', }, // allowedHosts: [], // 该选项允许将允许访问开发服务器的服务列入白名单。 compress: true, // 启用http compression(gzip)进行数据压缩传输 port: _port, host: app_config.IP, client: { logging: 'verbose', progress: true, overlay: { // 当出现编译错误或警告时,在浏览器中显示全屏覆盖。 errors: true, warnings: true, }, }, historyApiFallback: true, // { index: `${app_config.dist}/${app_config.entry}/index.html`} proxy: { '/api': { target: 'http://localhost:3000', pathRewrite: { '^/api': '' }, }, }, open: true, hot: 'only', // HMR时 填true }, performance: false, // Turn off performance processing }; };