-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvue.config.js
More file actions
298 lines (274 loc) · 10.9 KB
/
Copy pathvue.config.js
File metadata and controls
298 lines (274 loc) · 10.9 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
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
const pages = {
// root 入口:避免直接访问 `/` 时命中 public/index.html(未经过 HtmlWebpackPlugin 渲染)
// 从而出现 `<%= BASE_URL %>` 这类占位符导致的 400 Bad Request
index: {
title: "个人中心 - JX3BOX",
entry: "src/pages/index/index.js",
template: "public/index.html",
filename: "index.html",
},
account: {
title: "账号中心 - JX3BOX",
entry: "src/pages/account/index.js",
template: "public/app.html",
filename: "account/index.html",
},
dashboard: {
title: "个人中心 - JX3BOX",
entry: "src/pages/dashboard/index.js",
template: "public/index.html",
filename: "dashboard/index.html",
},
publish: {
title: "发布中心 - JX3BOX",
entry: "src/pages/publish/index.js",
template: "public/index.html",
filename: "publish/index.html",
},
author: {
title: "作者 - JX3BOX",
entry: "src/pages/author/index.js",
template: "public/index.html",
filename: "author/index.html",
},
card: {
title: "贺卡 - JX3BOX",
entry: "src/pages/card/index.js",
template: "public/app.html",
filename: "card/index.html",
},
vip: {
title: "会员中心 - JX3BOX",
entry: "src/pages/vip/index.js",
template: "public/index.html",
filename: "vip/index.html",
},
ai: {
title: "AI 编辑器预览 - JX3BOX",
entry: "src/pages/ai/index.js",
template: "public/app.html",
filename: "ai/index.html",
},
};
const path = require("path");
const events = require("events");
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");
// devServer proxy 会给 Server 挂多个 upgrade listener;条目多时会触发 Node 的 MaxListeners 告警
// 这里只提高开发环境的默认上限,避免误报(不影响功能)
if (String(process.env.NODE_ENV).toLowerCase() === "development") {
events.defaultMaxListeners = Math.max(events.defaultMaxListeners || 10, 50);
}
module.exports = {
productionSourceMap: false,
transpileDependencies: true,
//❤️ define path for static files ~
publicPath: process.env.BUILD_PREVIEW
? "/" + process.env.APP_NAME
: 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",
historyApiFallback: buildHistoryFallback(),
// 与 @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,
client: {
overlay: {
runtimeErrors: (error) => {
if (error.message === "ResizeObserver loop completed with undelivered notifications.") {
return false;
}
return true;
},
},
},
},
// 依赖包(element-plus/theme-chalk 等)会输出大量 Sass deprecation 警告
// 这些不是运行错误,开启 quietDeps 让它们不刷屏(只保留项目自身的警告)
css: {
loaderOptions: {
sass: {
sassOptions: {
quietDeps: true,
},
},
scss: {
sassOptions: {
quietDeps: true,
},
},
},
},
//❤️ Webpack configuration
chainWebpack: (config) => {
//💝 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 只处理唯一入口,普通 CSS/Less 不进入其增量缓存。
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: {
// 过滤依赖包里的已知兼容性 warning(不影响运行,但会刷屏)
stats: {
warningsFilter: [/node_modules[\\\\/]+@jx3box[\\\\/]+jx3box-common[\\\\/]+/],
},
plugins: [
new webpack.DefinePlugin({
// 全局注入,用于 JS 或其他代码中
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false,
}),
],
},
};
function buildHistoryFallback() {
const rewrites = Object.values(pages)
.filter((page) => page.filename && page.filename !== "index.html")
.map((page) => {
const prefix = "/" + page.filename.replace(/\/index\.html$/, "");
return {
from: new RegExp(`^${escapeRegExp(prefix)}(?:/.*)?$`),
to: page.filename.startsWith("/") ? page.filename : "/" + page.filename,
};
});
return {
disableDotRule: true,
rewrites,
};
}
// 注入全局样式资源(变量、mixin 等)
// 本地css/var.less、mixin.less会覆盖node_modules里的同名文件,方便定制化
// 注意此类文件都是变量和mixin函数,不要写全局样式,否则可能会被重复注入多次
function addStyleResource(rule) {
var preload_styles = [];
preload_styles.push(
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"),
path.resolve(__dirname, "./node_modules/csslab/base.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: "",
onProxyReq: (request) => {
// 本地开发时避免把 localhost 的来源头转发给上游,
// 某些支付/会员接口会直接按 Origin/Referer 拒绝请求。
request.setHeader("origin", "");
request.setHeader("referer", "");
},
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])), {});
}