const path = require('path');
const fs = require('fs');
const glob = require('glob');
// const webpack = require('webpack');
const devServerPort = process.env.VUE_APP_DEV_SERVER_PORT || 8090;
const PAGE_PATH = path.resolve(__dirname, './src/pages');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const StaticEnvConfigPlugin = require('@wecity/static-env-config').default;
const TIconReplaceWebpackPlugin = require('@gtff/tdesign-gt-vue/icon/plugin');
const AutoRouterPlugin = require('@gtff/tdesign-gt-vue/plugins/auto-router');
const CSSSplitWebpackPlugin = require('css-split-webpack-plugin').default;
const TerserPlugin = require('terser-webpack-plugin');
const mock = require('./mock');
const packageName = require('./package.json').name;
const LOCAL_DEV = process.env.VUE_APP_MODEL === 'local';
// 本地开发配置代理
// const PROXY = LOCAL_DEV ? require('./proxy') : {};
// 根据启动的模式使用不同的template
const template = LOCAL_DEV ? 'public/local.html' : 'public/index.html';
const isExtract = process.env.NODE_ENV === 'production' || process.env.VUE_APP_CSS_EXTRACT === 'true';
/**
* 获取页面入口集合 for devServer
*/
function getPagesEntities(pagePath) {
const entities = {};
glob.sync(pagePath).forEach((entity) => {
const moduleName = entity.split('/').slice(-1);
entities[moduleName] = entity;
});
// eg:
// {
// apply_admin: './src/pages/admin_manage/index',
// test: './src/pages/test/index'
// }
return entities;
}
const pages = getPagesEntities(`${PAGE_PATH}/*`);
const multiPage = {};
Object.keys(pages).forEach((page) => {
if (!Object.prototype.hasOwnProperty.call(pages, page)) {
return;
}
const configFile = `${PAGE_PATH}/${page}/config.js`;
const config = fs.existsSync(configFile) ? require(configFile) : {};
multiPage[page] = {
// page 的入口
entry: `src/pages/${page}/main.js`,
// 模板来源
template,
// 在 dist/index.html 的输出
filename: page === 'index' ? `${page}.html` : `${page}/index.html`,
// 当使用 title 选项时,
// template 中的 title 标签需要是
<%= htmlWebpackPlugin.options.title %>
title: process.env.VUE_APP_NAME || '可信碳信息网',
// 在这个页面中包含的块,默认情况下会包含
// 提取出来的通用 chunk 和 vendor chunk。
chunks: ['chunk-vendors', 'chunk-common', page],
...config,
};
});
/**
* 将对应环境的env的环境变量处理成常量,等待注入到static-env-config插件配置中去
* 只注入VUE_APP_开头的环境变量
*/
const STATIC_ENV_CONFIG = {};
Object.keys(process.env).forEach((key) => {
if (/^VUE_APP_/.test(key)) {
Object.assign(STATIC_ENV_CONFIG, {
[key]: process.env[key],
});
}
});
delete STATIC_ENV_CONFIG.VUE_APP_API_BASE_URL;
delete STATIC_ENV_CONFIG.VUE_APP_CDN_PATH;
delete STATIC_ENV_CONFIG.VUE_APP_ROUTER_BASE;
/**
* 修复生产模式build出来的css background image url(~@)路径不符合预期的问题
* @type {string[]}
*/
const styles = ['css', 'postcss', 'scss', 'sass', 'less', 'stylus'];
const modules = ['vue-modules', 'vue', 'normal-modules', 'normal'];
const configureWebpackPlugin = [];
/**
* 本地开发启用hard-source-webpack-plugin
*/
if (LOCAL_DEV) {
console.log('====>> 启动本地开发缓存');
configureWebpackPlugin.push(
new HardSourceWebpackPlugin({
// 缓存目录,支持相对目录或绝对目录
// 如果有设置VUE_APP_HARD_SOURCE_FOLDER目录则使用设置值
// 一般在coding流水线编译使用,把缓存设置到`/data/npm/`中,不会丢失
cacheDirectory: `${
(process.env.VUE_APP_HARD_SOURCE_FOLDER && path.join(process.env.VUE_APP_HARD_SOURCE_FOLDER, '/')) ||
path.join(process.cwd(), 'node_modules', '/')
}.cache/hard-source/[confighash]`,
// 基于package.json中的name来生成不同的hash串用于cacheDirectory的目录
// @param webpackConfig
configHash(webpackConfig) {
return require('node-object-hash')({ sort: false }).hash({ packageName, ...webpackConfig });
},
// 根据目录中的lock文件来决定是否重设缓存,如果设置false则需要手动删除
environmentHash: {
root: process.cwd(),
directories: [],
files: ['package-lock.json', 'yarn.lock'],
},
info: {
// 'none' or 'test'.
mode: 'none',
// 'debug', 'log', 'info', 'warn', or 'error'.
level: 'debug',
},
// 自动清除过期、过大缓存
cachePrune: {
// 缓存时间:默认7天
maxAge: 7 * 24 * 60 * 60 * 1000,
// 超过该处设置的阈值(默认500MB),则自动删除缓存
sizeThreshold: 500 * 1024 * 1024,
},
}),
new HardSourceWebpackPlugin.ExcludeModulePlugin([
{
// HardSource works with mini-css-extract-plugin but due to how
// mini-css emits assets, assets are not emitted on repeated builds with
// mini-css and hard-source together. Ignoring the mini-css loader
// modules, but not the other css loader modules, excludes the modules
// that mini-css needs rebuilt to output assets every time.
test: /mini-css-extract-plugin[\\/]dist[\\/]loader/,
},
]),
);
}
if (isExtract) {
configureWebpackPlugin.push(
new CSSSplitWebpackPlugin({
size: 4000,
preserve: true,
filename: 'assets_res/css/[name]-[part].[ext]',
}),
);
}
if (process.env.VUE_APP_AUTO_ROUTER === 'true') {
console.log('====>> 启动自动路由,若不需要请在.env.development文件把[VUE_APP_AUTO_ROUTER]变量删除或者设置为false');
configureWebpackPlugin.push(new AutoRouterPlugin({ pages: './src/pages/*/views' }));
}
const transpileDependencies = ['tdesign-icons-vue', '@gt4/common-front'];
if (LOCAL_DEV && process.env.VUE_APP_IE === 'true') {
transpileDependencies.push('sockjs-client');
}
module.exports = {
lintOnSave: LOCAL_DEV,
transpileDependencies,
publicPath: process.env.NODE_ENV === 'production' ? '/' : process.env.VUE_APP_CDN_PATH,
assetsDir: 'assets_res',
// 生产模式生成sourcemap
productionSourceMap: false,
css: {
sourceMap: false,
// 在真实ie9下调试dev需要开始样式分离才能正常加载样式
extract: isExtract,
},
chainWebpack: (config) => {
// alias注册
// eg:
// config.resolve.alias.set('@src', path.resolve(process.cwd(), 'src'));
/**
* 修复生产模式build出来的css background-image: url(~@)路径不符合预期的问题
* eg: alias to pages/index => ~@/pages/index
*/
if (process.env.NODE_ENV === 'production') {
styles.forEach((s) => {
modules.forEach((m) =>
config.module
.rule(s)
.oneOf(m)
.use('extract-css-loader')
.tap((options) => {
// Set whatever you want as publicPath
// options.publicPath = process.env.VUE_APP_CDN_PATH;
// Object.assign(options, {
// publicPath: './',
// });
return options;
}),
);
});
}
config.plugin('static-env-config').use(
new StaticEnvConfigPlugin({
// url: './env.config.js',
url: '/view/mhzc/env.config.js',
headResourceTags: [
{
tagName: 'link',
attributes: {
href: '/favicon.ico',
rel: 'icon',
},
},
],
config: {
// 这三个必须配置,默认从.env文件中来
ROUTER_PREFIX: process.env.VUE_APP_ROUTER_BASE,
API_PREFIX: process.env.VUE_APP_API_BASE_URL,
RESOURCE_PREFIX: process.env.VUE_APP_CDN_PATH,
// 把config里面的配置注入到最终的env.config.js中
...STATIC_ENV_CONFIG,
},
}),
);
// 移除prefetch插件
config.plugins.delete('prefetch-index');
},
configureWebpack: {
// 以下规则用于微前端
// 让主应用能正确识别微应用暴露出来的一些信息
output: {
library: `${packageName}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${packageName}`,
},
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
warnings: false,
drop_console: false,
drop_debugger: false,
},
},
}),
],
},
plugins: [
...configureWebpackPlugin,
// t-icon替换插件
new TIconReplaceWebpackPlugin({
staticPath: 'static_res',
}),
],
},
/**
* 本地开发服务配置
*/
devServer: {
port: devServerPort,
host: '0.0.0.0',
// 开发时勿用 ./dist:会与历史 npm run build 产物混用,history 子路径刷新易命中旧 HTML/JS
contentBase: path.resolve(__dirname, 'public'),
publicPath: process.env.VUE_APP_ROUTER_BASE,
https: process.env.VUE_APP_PROTOCOL_HTTPS === 'true',
compress: false,
disableHostCheck: true,
overlay: {
warnings: false,
errors: true,
},
headers: {
'X-Custom-Foo': 'bar',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
'Access-Control-Allow-Headers': 'X-Requested-With, content-type, Authorization',
'Content-Security-Policy': "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.bootcdn.net https://cdnjs.cloudflare.com",
},
// Vue CLI prepareProxy 用 pathname.match(代理键) 判断;键写 '/mhzc' 会变成匹配路径里任意位置的 /mhzc,
// 会误伤 SPA 路由 /view/mhzc/...,刷新时整页请求被转发到后端导致 Proxy error。必须用 ^ 限定为路径前缀。
proxy: {
'^/sso': {
target: 'http://192.168.110.29:9301',
// target: 'http://carbon.liantu.tech',
// target: 'http://10.23.20.13:94/',
changeOrigin: true,
},
'^/mhzc': {
target: 'http://192.168.110.29:9302',
// target: 'http://carbon.liantu.tech',
// target: 'http://10.23.20.13:94/',
changeOrigin: true,
},
'^/gxzx': {
// target: 'http://localhost:9303',
target: 'http://carbon.liantu.tech',
// target: 'http://10.23.20.13:94/',
changeOrigin: true,
},
'^/yygl': {
// target: 'http://localhost:20010',
target: 'http://carbon.liantu.tech',
// target: 'http://10.23.20.13:94/',
changeOrigin: true,
},
},
before(app) {
if (process.env.VUE_APP_MOCK === 'true') {
// 是否开启MOCK,默认开启,检查项目根目录下是否存在.env.development文件 内容为VUE_APP_MOCK=true
// 设置mock数据路由, 创建get或者post目录,该目录下的文件则为url请求地址
// app对象、请求方法、url前缀
// mock.setMock(app, 'get', process.env.VUE_APP_API_BASE_URL);
// app对象、请求方法、url前缀 eg: user_bulk__delete.json => /api/user/bulk_delete
// mock.setMock(app, 'post', process.env.VUE_APP_API_BASE_URL);
// mock.mockSubDirs(app, ['post'], '/abc') // 增加子目录,mock/xxx/post 目录下的文件为url请求地址 eg: user_bulk__delete.json => /xxx/abc/user/bulk_delete
// 设置mock数据路由, 对应目录下,创建get或者post目录,再请求目录下创建业务目录demo(没有层级层级的限制),该目录下的文件则为url请求地址 eg: fail.json => /demo/fail
// app对象、请求方法、url前缀
mock.setMockByBasePath(app, 'get', process.env.VUE_APP_API_BASE_URL);
mock.setMockByBasePath(app, 'post', process.env.VUE_APP_API_BASE_URL);
}
},
// ...PROXY,
},
/**
* 插件配置
*/
pluginOptions: {
// 添加了插件(@samhammer/vue-cli-plugin-stylelint), 所以需要配置
lintStyleOnBuild: true,
stylelint: {
fix: true, // boolean (default: true)
files: ['src/**/*.{vue,htm,html,css,sss,less,scss}'], // string | [string]
},
// 注入less全局变量
'style-resources-loader': {
preProcessor: 'less',
patterns: [path.resolve(__dirname, './src/styles/variables/*.less')],
},
},
/**
* 多页应用配置
*/
pages: multiPage,
};