UNPKG

@wibetter/akfun

Version:

前端脚手架:支持Vue技术栈和react技术栈

339 lines (330 loc) 12.7 kB
const path = require('path'); const fs = require('fs'); const webpack = require('webpack'); // const tsImportPluginFactory = require('ts-import-plugin'); // 按需加载lib库组件代码 const StyleLintPlugin = require('stylelint-webpack-plugin'); // const VueLoaderPlugin = require('vue-loader/lib/plugin'); // vue2.0 const { VueLoaderPlugin } = require('vue-loader'); // vue3.0 const nodeExternals = require('webpack-node-externals'); // const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin'); const ProgressBarPlugin = require('progress-bar-webpack-plugin'); const ESLintPlugin = require('eslint-webpack-plugin'); const utils = require('./loaderUtils'); const vueLoaderConfig = require('./vue-loader.conf'); const { resolve, resolveToCurrentRoot } = require('../utils/pathUtils'); const getProjectDir = require('../utils/getProjectDir'); const catchVuePages = require('../utils/catchVuePages'); // 用于获取当前项目中的vue单文件 // 引入当前项目配置文件 const projectConfig = require('../config/index'); const babelConfig = require('../config/babel.config'); // Babel的配置文件 const { buildBanner } = require('../utils/akfunParams'); const getJsEntries = require('../utils/jsEntries'); const { isArray, isFunction } = require('../utils/typeof'); // 生成构建头部信息 const BannerPack = new webpack.BannerPlugin({ banner: buildBanner, entryOnly: true // 只在入口 chunks 文件中添加 }); /** * webpack.base.conf.js * 主要用于设置 rules 和 通用插件 * _curEnvConfig: 执行环境中的配置,比如:dev、build、build2lib等; * _akfunConfig:完整的配置对象 */ module.exports = (_curEnvConfig, _akfunConfig) => { const curEnvConfig = _curEnvConfig || {}; // 用于接收当前运行环境配置变量 let config = _akfunConfig || projectConfig; // 默认使用执行命令目录下的配置数据 const curWebpackConfig = config.webpack; // 获取当前项目目录 const curProjectDir = getProjectDir(curWebpackConfig.projectDir); // 判断是否有自定义 Babel plugins if (isArray(curWebpackConfig.babelPlugins)) { // 添加自定义babel插件 babelConfig.plugins.push(...curWebpackConfig.babelPlugins); } else if (isFunction(curWebpackConfig.babelPlugins)) { // 处理自定义babel插件 curWebpackConfig.babelPlugins(babelConfig.plugins); } const webpackConfig = { stats: { // cachedModules: false, // providedExports: true, // warnings: false }, entry: curWebpackConfig.entry, // target: 'web', // <=== 默认为 'web',可省略 target: ['web', 'es5'], // 使用共同的特性子集 // target: false, // 不使用任何插件 /* 内置变量列表: id: chunk的唯一标识,从0开始; name: chunk的名称; hash: chunk的唯一标识的Hash值; chunkhash: chunk内容的Hash值; 其中hash和chunkhash的长度是可以指定的,[hash:8]代表取8位的Hash值,默认是20位。 */ output: { filename: '[name].js' }, /** * 当webpack试图去加载模块的时候,它默认是查找以 .js 结尾的文件的 */ resolve: curWebpackConfig.resolve, externals: curWebpackConfig.ignoreNodeModules ? [ nodeExternals({ importType: 'commonjs', allowlist: curWebpackConfig.allowList ? curWebpackConfig.allowList : [] }) ].concat(curWebpackConfig.externals) : curWebpackConfig.externals, module: { rules: [ { test: /\.vue$/, use: [ { loader: 'vue-loader', options: vueLoaderConfig // 配置vue-loader相关的loader插件 } ] }, // 关于ts的检测:https://ts.xcatliu.com/engineering/lint.html { test: /\.tsx?$/, use: [ { loader: 'babel-loader', options: babelConfig }, { loader: 'ts-loader', options: { // configFile: path.resolve(__dirname, '../config/tsconfig.json') compilerOptions: { declaration: curWebpackConfig.createDeclaration || false, outDir: curEnvConfig.assetsRoot || './dist' } } } ], // exclude: /node_modules/, include: curProjectDir // [resolve('src')], }, { test: /\.(jsx?)$/, use: [ { loader: 'babel-loader', options: babelConfig } ], // exclude: /node_modules/, include: curProjectDir // [resolve('src')], }, { // 图片资源 /* url-loader 功能类似于 file-loader,在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL。 */ test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, type: 'asset', issuer: /\.s?css$/, parser: { dataUrlCondition: { maxSize: 2 * 1024 //data转成url的条件,也就是转成bas64的条件,maxSize相当于limit } }, generator: { // filename,和output中设置assetModuleFilename一样,将资源打包至img文件夹 filename: utils.assetsPath('img/[name].[hash:7][ext]') //[name]指原来的名字,[hash:6]取哈希的前六位,[ext]指原来的扩展名 } }, { test: /\.svg$/, issuer: /\.[jt]sx?$/, use: ['@svgr/webpack'] }, { // 视频音频资源 test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, type: 'asset', parser: { dataUrlCondition: { maxSize: 2 * 1024 //data转成url的条件,也就是转成bas64的条件,maxSize相当于limit } }, generator: { // filename,和output中设置assetModuleFilename一样,将资源打包至imgs文件夹 filename: utils.assetsPath('media/[name].[hash:7][ext]') //[name]指原来的名字,[hash:6]取哈希的前六位,[ext]指原来的扩展名 } }, { // 字体资源 test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, type: 'asset', parser: { dataUrlCondition: { maxSize: 2 * 1024 //data转成url的条件,也就是转成bas64的条件,maxSize相当于limit } }, generator: { // filename,和output中设置assetModuleFilename一样,将资源打包至fonts目录中 filename: utils.assetsPath('fonts/[name].[hash:7][ext]') //[name]指原来的名字,[hash:6]取哈希的前六位,[ext]指原来的扩展名 } }, { test: /\.(js|ts|tsx|jsx|vue|css|html)$/, loader: 'params-replace-loader', // exclude: [/node_modules/, resolve('src/mock/data')], // 排除不需要进行校验的文件夹 include: curProjectDir, // [resolve('src')], options: config.envParams }, { test: /\.(html)$/, use: { loader: 'html-loader', options: { minimize: true, sources: false // Enables/Disables sources handling } } } ] }, plugins: [ BannerPack, new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(curEnvConfig.NODE_ENV) }), // 请确保引入这个插件来施展魔法 new VueLoaderPlugin(), // new FriendlyErrorsPlugin(), new ProgressBarPlugin() ] }; // 优先使用执行环境中的配置 if (curEnvConfig.ignoreNodeModules !== undefined) { const allowList = curEnvConfig.allowList || curWebpackConfig.allowList; const externals = curEnvConfig.externals || curWebpackConfig.external || []; webpackConfig.externals = curEnvConfig.ignoreNodeModules ? [ nodeExternals({ importType: 'commonjs', allowlist: allowList || [] }) ].concat(externals) : externals; } // 集成构建入口相关的配置(优先级更高) if (curEnvConfig.entry) { webpackConfig.entry = curEnvConfig.entry; // 会覆盖config.webpack.entry的配置 } // 多页面多模板支持能力 let entryConfig = webpackConfig.entry || {}; // 获取构建入口配置 const entryFiles = (entryConfig && Object.keys(entryConfig)) || []; if ( !webpackConfig.entry || JSON.stringify(webpackConfig.entry) === '{}' || entryFiles.length === 0 ) { // 如果当前构建入口为空,则自动从'./src/pages/'中获取入口文件 webpackConfig.entry = getJsEntries(); } else if (webpackConfig.entry && entryFiles.length === 1) { /** * 只有一个构建入口文件,且项目中不存在此文件,则自动从'./src/pages/'中获取构建入口文件 */ const filename = entryFiles[0]; let entryFilePath = entryConfig[filename]; // 当前entryFilePath可能是一个地址字符串,也可能是一个存储多个文件地址的数组 if (isArray(entryFilePath)) { // 如果是数组则自动获取最后一个文件地址 entryFilePath = entryFilePath[entryFilePath.length - 1]; } if (!fs.existsSync(entryFilePath)) { // 如果仅有的构建入口文件不存在,则自动从'./src/pages/'中获取入口文件 const curJsEntries = getJsEntries(); webpackConfig.entry = curJsEntries ? curJsEntries : webpackConfig.entry; } } // 是否开启ESLint if (config.settings.enableESLint) { // ts类型 webpackConfig.plugins.push( new ESLintPlugin({ // context: resolve('src'), extensions: ['ts', 'tsx'], // include: curProjectDir, // [resolve('src')], // exclude: 'node_modules', cache: true, fix: config.settings.enableESLintFix || false, formatter: require('eslint-friendly-formatter'), overrideConfigFile: path.resolve(__dirname, '../config/.eslintrc.ts.js') }) ); // 通用js类型 webpackConfig.plugins.push( new ESLintPlugin({ extensions: ['js', 'jsx'], // include: curProjectDir, // [resolve('src')], // exclude: 'node_modules', cache: true, fix: config.settings.enableESLintFix || false, formatter: require('eslint-friendly-formatter'), overrideConfigFile: path.resolve(__dirname, '../config/.eslintrc.js') }) ); // vue单文件类型 webpackConfig.plugins.push( new ESLintPlugin({ extensions: ['vue'], // include: curProjectDir, // [resolve('src')], // exclude: 'node_modules', cache: true, fix: config.settings.enableESLintFix || false, formatter: require('eslint-friendly-formatter'), overrideConfigFile: path.resolve(__dirname, '../config/.eslintrc.vue.js') }) ); } // 是否开启StyleLint: 用于验证scss文件里面的style规范 if (config.settings.enableStyleLint) { const vuePagesObj = catchVuePages(); // 判断项目中是否有vue文件 if (vuePagesObj && Object.keys(vuePagesObj).length > 0) { // 校验vue单文件里面的样式规范 webpackConfig.plugins.push( new StyleLintPlugin({ files: ['src/**/*.vue'], // quiet: true, cache: true, cacheLocation: resolveToCurrentRoot('./node_modules/stylelint/.vue-cache'), fix: config.settings.enableStyleLintFix, configFile: path.resolve(__dirname, '../config/.stylelintrc-vue') }) ); } // 校验scss等样式文件里面的样式规范 webpackConfig.plugins.push( new StyleLintPlugin({ files: 'src/**/*.s?(a|c)ss', // quiet: true, cache: true, cacheLocation: resolveToCurrentRoot('./node_modules/stylelint/.cache'), fix: config.settings.enableStyleLintFix, configFile: path.resolve(__dirname, '../config/.stylelintrc') }) ); } // 判断是否有自定义plugins if (curWebpackConfig.plugins && isArray(curWebpackConfig.plugins)) { // 添加自定义webpack插件 webpackConfig.plugins.push(...curWebpackConfig.plugins); } // 判断是否有自定义loader if (curWebpackConfig.moduleRules && isArray(curWebpackConfig.moduleRules)) { // 添加自定义自定义loader webpackConfig.module.rules.push(...curWebpackConfig.moduleRules); } return webpackConfig; };