-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvue.config.js
More file actions
248 lines (225 loc) · 9.42 KB
/
Copy pathvue.config.js
File metadata and controls
248 lines (225 loc) · 9.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
const path = require("path");
const webpack = require("webpack");
const autoprefixer = require("autoprefixer");
const tailwindPostcss = require("@tailwindcss/postcss");
const commonDomains = require("@jx3box/jx3box-common/data/jx3box.json");
const postcssConfig = path.resolve(__dirname, "./postcss.config.js");
const tailwindEntry = path.resolve(__dirname, "./src/assets/css/tailwind.css");
const pages = {
index: {
entry: "./src/main.js",
template: "./public/index.html",
filename: "index.html",
},
qqbot: {
entry: "./src/pages/qqbot/index.js",
template: "./public/index.html",
filename: "qqbot/index.html",
},
team: {
entry: "./src/pages/team/index.js",
template: "./public/index.html",
filename: "team/index.html",
},
};
module.exports = {
productionSourceMap: false,
transpileDependencies: true,
//❤️ define path for static files ~
publicPath: process.env.NODE_ENV === "development" ? "/" : process.env.STATIC_PATH + "/" + process.env.APP_NAME,
//🌈多页面配置,详见 https://cli.vuejs.org/zh/config/#pages
pages: pages,
//⚛️ Proxy ~
devServer: {
host: "localhost",
// 与 @jx3box/jx3box-common/js/api.js 对齐:
// 本地开发开启 `VUE_APP_PROXY_ENABLE=1` 后,会把请求 baseURL 切到 `${VUE_APP_PROXY_PREFIX}/${serviceKey}`
proxy: buildEnvProxy(),
allowedHosts: "all",
port: process.env.DEV_PORT || 12028,
hot: true,
liveReload: true,
// 不写死 HMR websocket 地址,避免默认端口被占用后 dev-server 自动顺延端口,
// 但客户端仍连回旧端口,进而出现反复断开、整页刷新。
// `src` 内源码本来就由 webpack 自己监听并走 HMR。
// 这里如果再用 dev-server 的 watchFiles 监听一次,会让 less/css 变更退化成整页 live reload。
// 只保留对 public 目录的额外监听,用于模板/静态资源变更时刷新页面。
watchFiles: ["public/**/*"],
},
// 依赖包(element-plus/theme-chalk 等)会输出大量 Sass deprecation 警告
// 这些不是运行错误,开启 quietDeps 让它们不刷屏(只保留项目自身的警告)
css: {
loaderOptions: {
sass: {
sassOptions: {
quietDeps: true,
},
},
scss: {
sassOptions: {
quietDeps: true,
},
},
},
},
//❤️ Webpack configuration
chainWebpack: (config) => {
config.module
.rule("vue")
.use("vue-loader")
.tap((options = {}) => ({
...options,
compilerOptions: {
...(options.compilerOptions || {}),
isCustomElement: (tag) => tag === "wc-waterfall",
},
}));
//💝 in-line small imgs ~
config.module.rule("images").set("parser", {
dataUrlCondition: {
maxSize: 4 * 1024, // 4KiB
},
});
// 💝 quick svg ~
config.module
.rule("svg")
.exclude.add(path.join(__dirname, "src/assets/img/icon")) // 排除自定义svg目录
.end();
config.module
.rule("icons") // 新规则
.test(/\.svg$/)
.include.add(path.join(__dirname, "src/assets/img/icon")) // 新规则应用于我们存放svg的目录
.end()
.use("svg-sprite-loader") // 用sprite-loader接卸
.loader("svg-sprite-loader")
.options({
symbolId: "icon-[name]",
})
.end();
//💝 in-line svg imgs ~
config.module.rule("vue").use("vue-svg-inline-loader").loader("vue-svg-inline-loader");
//💖 import common less var * mixin ~
const types = ["vue-modules", "vue", "normal-modules", "normal"];
types.forEach((type) => addStyleResource(config.module.rule("less").oneOf(type)));
// 依赖包源码可能携带仅供其自身开发使用的 PostCSS 配置。
// 统一指定本项目配置,避免构建 node_modules 源码时误加载依赖包的 devDependencies。
const styleRules = ["css", "postcss", "scss", "sass", "less", "stylus"];
styleRules.forEach((styleRule) => {
types.forEach((type) => {
config.module
.rule(styleRule)
.oneOf(type)
.use("postcss-loader")
.tap((options = {}) => ({
...options,
postcssOptions: {
config: postcssConfig,
},
}));
});
});
// Tailwind v4 需要保留其 base/preflight 和工具类,但只允许处理唯一入口。
// 普通 CSS 仍只经过 Autoprefixer,Less/SCSS 则使用上面的项目级配置。
types.forEach((type) => {
config.module
.rule("css")
.oneOf(type)
.use("postcss-loader")
.tap((options = {}) => ({
...options,
postcssOptions: (loaderContext) => ({
plugins: [
...(path.resolve(loaderContext.resourcePath) === tailwindEntry ? [tailwindPostcss()] : []),
autoprefixer,
],
}),
}));
});
config.externals = {
tinyMCE: "tinyMCE",
};
},
// 统一维护 configureWebpack,避免对象键重复导致前面的配置被后面的覆盖
configureWebpack: () => ({
// 某些机器上 less/css 变更不会稳定触发 fs 事件,开发环境改为轮询监听更稳妥
watchOptions:
process.env.NODE_ENV === "development"
? {
poll: Number(process.env.WEBPACK_WATCH_POLL || 1000),
aggregateTimeout: 300,
ignored: /node_modules/,
}
: undefined,
stats: {
warningsFilter: [/node_modules[\\\\/]+@jx3box[\\\\/]+jx3box-common[\\\\/]+/],
},
plugins: [
new webpack.DefinePlugin({
// 全局注入,用于 JS 或其他代码中
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
}),
],
}),
};
// 注入全局样式资源(变量、mixin 等)
// 本地css/var.less、mixin.less会覆盖node_modules里的同名文件,方便定制化
// 注意此类文件都是变量和mixin函数,不要写全局样式,否则可能会被重复注入多次
function addStyleResource(rule) {
var preload_styles = [];
preload_styles.push(
path.resolve(__dirname, "./node_modules/csslab/base.less"),
path.resolve(__dirname, "./node_modules/@jx3box/jx3box-common/css/var.less"),
path.resolve(__dirname, "./node_modules/@jx3box/jx3box-common/css/mixin.less"),
path.resolve(__dirname, "./src/assets/css/var.less"),
path.resolve(__dirname, "./src/assets/css/mixin.less")
);
rule.use("style-resource").loader("style-resources-loader").options({
patterns: preload_styles,
});
}
function normalizeTarget(value) {
if (!value) return "";
const trimmed = String(value).trim();
if (!trimmed) return "";
if (/^https?:\/\//i.test(trimmed)) return trimmed;
return `https://${trimmed.replace(/^\/+/, "")}`;
}
function escapeRegExp(str) {
return String(str).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function buildEnvProxy() {
const nodeEnv = String(process.env.NODE_ENV || "").toLowerCase();
if (nodeEnv && nodeEnv !== "development") return {};
// Vue CLI 加载 .env 的时机/覆盖关系可能导致这里读不到或读到意外值:
// - 明确设置为 false 才禁用
// - 未设置/无法读取时,仍然生成代理(仅在 devServer 生效)
const rawEnabled = String(process.env.VUE_APP_PROXY_ENABLE || "").toLowerCase();
const disabled = ["0", "false", "no", "off"].includes(rawEnabled);
if (disabled) return {};
const prefix = process.env.VUE_APP_PROXY_PREFIX || "/__proxy";
const mk = (serviceKey, target) => {
const normalized = normalizeTarget(target);
if (!normalized) return {};
const context = `${prefix}/${serviceKey}`;
const contextRe = new RegExp(`^${escapeRegExp(context)}`);
return {
[context]: {
target: normalized,
changeOrigin: true,
secure: false,
cookieDomainRewrite: "",
pathRewrite: (p) => p.replace(contextRe, ""),
},
};
};
const serviceTargets = {
cms: process.env.VUE_APP_CMS_API || commonDomains.__cms,
next: process.env.VUE_APP_NEXT_API || commonDomains.__next,
team: process.env.VUE_APP_TEAM_API || commonDomains.__team,
pay: process.env.VUE_APP_PAY_API || commonDomains.__pay,
lua: process.env.VUE_APP_LUA_API || commonDomains.__lua,
node: process.env.VUE_APP_NODE_API || commonDomains.__node,
helper: process.env.VUE_APP_HELPER_API || commonDomains.__helperUrl,
};
return Object.keys(serviceTargets).reduce((acc, key) => Object.assign(acc, mk(key, serviceTargets[key])), {});
}