@xysfe/vite-plugin-dev-proxy 1.0.2 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +511 -145
- package/dist/index.cjs +390 -163
- package/dist/index.d.cts +178 -4
- package/dist/index.d.mts +178 -4
- package/dist/index.d.ts +178 -4
- package/dist/index.mjs +385 -163
- package/package.json +2 -3
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
3
5
|
const zlib = require('zlib');
|
|
4
6
|
const path = require('path');
|
|
5
7
|
const fs = require('fs');
|
|
@@ -9,205 +11,305 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
|
|
|
9
11
|
const zlib__default = /*#__PURE__*/_interopDefaultCompat(zlib);
|
|
10
12
|
const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
const SCRIPT_LINK_REGEX = /<(?:script[^>]*>.*?<\/script>|link[^>]*>)/g;
|
|
15
|
+
const ASSET_REGEX = /\.(js|mjs|ts|tsx|jsx|css|scss|sass|less|vue|json|woff2?|ttf|eot|ico|png|jpe?g|gif|svg|webp)(\?.*)?$/i;
|
|
16
|
+
const STATIC_PATH_REGEX = /^\/(static|assets|public|images|css|js)\//i;
|
|
17
|
+
const BYPASS_REGEX = /\.(vue|js|mjs|ts|tsx|jsx|css|scss|sass|less|json|png|jpe?g|gif|svg|webp|ico|woff2?|ttf|eot)$/i;
|
|
18
|
+
const REDIRECT_STATUS_MIN = 300;
|
|
19
|
+
const REDIRECT_STATUS_MAX = 400;
|
|
20
|
+
const DEFAULT_APP_DIV_REGEX = /<div[^>]*id=["']app["'][^>]*><\/div>/g;
|
|
21
|
+
const HTTPS_TO_HTTP_REGEX = /https:\/\/(localhost|127\.0\.0\.1|192\.168\.\d{1,3}\.\d{1,3}|10\.\d{1,3}\.\d{1,3}\.\d{1,3}|172\.(?:1[6-9]|2[0-9]|3[0-1])\.\d{1,3}\.\d{1,3})(:\d+)?/gi;
|
|
22
|
+
function createLogger(debug) {
|
|
23
|
+
return {
|
|
24
|
+
log: debug ? console.log.bind(console) : () => {
|
|
25
|
+
},
|
|
26
|
+
logError: debug ? console.error.bind(console) : () => {
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function normalizePath(path) {
|
|
31
|
+
return path.endsWith("/") ? path.slice(0, -1) : path;
|
|
32
|
+
}
|
|
33
|
+
function generateEntryScript(entry, staticPrefix) {
|
|
34
|
+
if (Array.isArray(entry)) {
|
|
35
|
+
return entry.map(
|
|
36
|
+
(e) => `<script crossorigin type="module" src="${staticPrefix + e}"><\/script>`
|
|
37
|
+
).join("\n");
|
|
38
|
+
}
|
|
39
|
+
return `<script crossorigin type="module" src="${staticPrefix + entry}"><\/script>`;
|
|
40
|
+
}
|
|
41
|
+
function rewriteCookies(headers, log2) {
|
|
42
|
+
const setCookie = headers["set-cookie"];
|
|
43
|
+
if (setCookie) {
|
|
44
|
+
headers["set-cookie"] = setCookie.map((cookie) => {
|
|
45
|
+
const rewrittenCookie = cookie.replace(/;\s*secure\s*(;|$)/gi, "$1").replace(/;\s*domain\s*=[^;]+(;|$)/gi, "$1").replace(/;\s*samesite\s*=[^;]+(;|$)/gi, "$1").replace(/;+/g, ";").replace(/;\s*$/g, "");
|
|
46
|
+
log2("[rewrittenCookie]", rewrittenCookie);
|
|
47
|
+
return rewrittenCookie;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return headers;
|
|
51
|
+
}
|
|
52
|
+
function decompressBuffer(buffer, encoding) {
|
|
53
|
+
if (encoding === "gzip") {
|
|
54
|
+
return zlib__default.gunzipSync(buffer);
|
|
55
|
+
} else if (encoding === "deflate") {
|
|
56
|
+
return zlib__default.inflateSync(buffer);
|
|
57
|
+
} else if (encoding === "br") {
|
|
58
|
+
return zlib__default.brotliDecompressSync(buffer);
|
|
59
|
+
}
|
|
60
|
+
return buffer;
|
|
61
|
+
}
|
|
62
|
+
function shouldClearScriptCss(match, clearRule) {
|
|
63
|
+
const srcMatch = match.match(/src="([^"]+)"/i);
|
|
64
|
+
const hrefMatch = match.match(/href="([^"]+)"/i);
|
|
65
|
+
const srcOrHref = srcMatch ? srcMatch[1] : hrefMatch ? hrefMatch[1] : null;
|
|
66
|
+
if (clearRule === "") {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
if (typeof clearRule === "string") {
|
|
70
|
+
return srcOrHref?.startsWith(clearRule) ?? false;
|
|
71
|
+
}
|
|
72
|
+
if (Array.isArray(clearRule)) {
|
|
73
|
+
return clearRule.some((prefix) => srcOrHref?.startsWith(prefix));
|
|
74
|
+
}
|
|
75
|
+
if (clearRule instanceof RegExp) {
|
|
76
|
+
return clearRule.test(match);
|
|
77
|
+
}
|
|
78
|
+
if (typeof clearRule === "function") {
|
|
79
|
+
return clearRule(match);
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
function injectEntryScript(html, fullEntry, developmentAgentOccupancy) {
|
|
84
|
+
if (developmentAgentOccupancy) {
|
|
85
|
+
return html.replace(developmentAgentOccupancy, fullEntry);
|
|
86
|
+
}
|
|
87
|
+
return html.replace(DEFAULT_APP_DIV_REGEX, (match) => `${match}${fullEntry}`);
|
|
88
|
+
}
|
|
89
|
+
function clearScriptCssTags(html, clearRule, log2) {
|
|
90
|
+
return html.replace(SCRIPT_LINK_REGEX, (match) => {
|
|
91
|
+
const isClear = shouldClearScriptCss(match, clearRule);
|
|
92
|
+
if (isClear) {
|
|
93
|
+
log2?.(`[clearScriptCssTags]: ${match}`);
|
|
94
|
+
}
|
|
95
|
+
return isClear ? "" : match;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function isRedirectResponse(proxyRes) {
|
|
99
|
+
return proxyRes.statusCode >= REDIRECT_STATUS_MIN && proxyRes.statusCode < REDIRECT_STATUS_MAX && !!proxyRes.headers.location;
|
|
100
|
+
}
|
|
101
|
+
function shouldProcessAsHtml(contentType, acceptHeader, requestUrl, isRedirect) {
|
|
102
|
+
const isNavigationRequest = acceptHeader.includes("text/html");
|
|
103
|
+
const isAssetRequest = ASSET_REGEX.test(requestUrl);
|
|
104
|
+
const isStaticPath = STATIC_PATH_REGEX.test(requestUrl);
|
|
105
|
+
return contentType.includes("text/html") && isNavigationRequest && !isAssetRequest && !isStaticPath && !isRedirect;
|
|
106
|
+
}
|
|
107
|
+
function matchesRemoteResource(url, remoteRule) {
|
|
108
|
+
if (typeof remoteRule === "string") {
|
|
109
|
+
return url.startsWith(remoteRule);
|
|
110
|
+
}
|
|
111
|
+
if (Array.isArray(remoteRule)) {
|
|
112
|
+
return remoteRule.some((prefix) => url.startsWith(prefix));
|
|
113
|
+
}
|
|
114
|
+
if (remoteRule instanceof RegExp) {
|
|
115
|
+
return remoteRule.test(url);
|
|
116
|
+
}
|
|
117
|
+
if (typeof remoteRule === "function") {
|
|
118
|
+
return remoteRule(url);
|
|
119
|
+
}
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
function shouldUseLocal(url, normalizedStaticPrefix, remotePrefixes) {
|
|
123
|
+
const pathname = url.split("?")[0];
|
|
124
|
+
const isLocalResource = normalizedStaticPrefix && url.startsWith(normalizedStaticPrefix) || url.startsWith("/@") || url.startsWith("/src") || url.startsWith("/node_modules") || url.includes(".hot-update.") || url.startsWith("/sockjs-node") || // Vue CLI/Webpack 热更新 WebSocket (3.x)
|
|
125
|
+
url.startsWith("/ws") || // Vue CLI/Webpack 热更新 WebSocket (4.x+/Vite)
|
|
126
|
+
url === "/" || BYPASS_REGEX.test(pathname);
|
|
127
|
+
const isRemoteResource = matchesRemoteResource(url, remotePrefixes);
|
|
128
|
+
return isLocalResource && !isRemoteResource;
|
|
129
|
+
}
|
|
130
|
+
function handleRedirect(proxyRes, req, res, appHost, log2, startTime) {
|
|
131
|
+
const redirectUrl = proxyRes.headers.location;
|
|
132
|
+
const host = req.headers.host;
|
|
133
|
+
const regex = new RegExp(appHost, "gi");
|
|
134
|
+
let location = redirectUrl.replace(regex, host || "");
|
|
135
|
+
location = location.replace(HTTPS_TO_HTTP_REGEX, "http://$1$2");
|
|
136
|
+
const headers = rewriteCookies({ ...proxyRes.headers }, log2);
|
|
137
|
+
headers.location = location;
|
|
138
|
+
res.writeHead(proxyRes.statusCode, headers);
|
|
139
|
+
res.end();
|
|
140
|
+
log2(
|
|
141
|
+
`Redirect handled: ${redirectUrl} -> ${location} (${Date.now() - startTime}ms)`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
function handleLibModeHtml(localIndexHtml, res, log2, logError, startTime) {
|
|
145
|
+
try {
|
|
146
|
+
const indexHtml = fs__default.readFileSync(
|
|
147
|
+
path.resolve(__dirname, localIndexHtml),
|
|
148
|
+
"utf-8"
|
|
149
|
+
);
|
|
150
|
+
res.writeHead(200, {
|
|
151
|
+
"Content-Type": "text/html; charset=utf-8"
|
|
152
|
+
});
|
|
153
|
+
res.end(indexHtml);
|
|
154
|
+
log2(`Local HTML served: ${localIndexHtml} (${Date.now() - startTime}ms)`);
|
|
155
|
+
} catch (err) {
|
|
156
|
+
logError("Failed to read local HTML:", err);
|
|
157
|
+
res.writeHead(500);
|
|
158
|
+
res.end("Failed to read local HTML");
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function handleHtmlResponse(proxyRes, req, res, context) {
|
|
162
|
+
const encoding = proxyRes.headers["content-encoding"];
|
|
163
|
+
const requestUrl = req.url || "";
|
|
164
|
+
const chunks = [];
|
|
165
|
+
proxyRes.on("data", (chunk) => {
|
|
166
|
+
if (chunk) {
|
|
167
|
+
chunks.push(chunk);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
proxyRes.on("end", () => {
|
|
171
|
+
try {
|
|
172
|
+
const buffer = Buffer.concat(chunks);
|
|
173
|
+
const decompressed = decompressBuffer(buffer, encoding);
|
|
174
|
+
let html = decompressed.toString("utf-8");
|
|
175
|
+
html = clearScriptCssTags(
|
|
176
|
+
html,
|
|
177
|
+
context.clearScriptCssPrefixes,
|
|
178
|
+
context.log
|
|
179
|
+
);
|
|
180
|
+
html = injectEntryScript(
|
|
181
|
+
html,
|
|
182
|
+
context.fullEntry,
|
|
183
|
+
context.developmentAgentOccupancy
|
|
184
|
+
);
|
|
185
|
+
const headers = rewriteCookies({ ...proxyRes.headers }, context.log);
|
|
186
|
+
headers["content-type"] = "text/html; charset=utf-8";
|
|
187
|
+
delete headers["content-encoding"];
|
|
188
|
+
delete headers["content-length"];
|
|
189
|
+
res.writeHead(200, headers);
|
|
190
|
+
res.end(html);
|
|
191
|
+
context.log(
|
|
192
|
+
`[HTML processed]: ${requestUrl} (${Date.now() - context.startTime}ms)`
|
|
193
|
+
);
|
|
194
|
+
} catch (err) {
|
|
195
|
+
context.logError("Decompress error:", err);
|
|
196
|
+
context.logError("Request URL:", requestUrl);
|
|
197
|
+
context.logError("Response headers:", proxyRes.headers);
|
|
198
|
+
res.writeHead(500);
|
|
199
|
+
res.end("Decompress error");
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
function validateOptions(options, pluginName) {
|
|
204
|
+
const { appHost } = options;
|
|
205
|
+
if (!appHost) {
|
|
206
|
+
throw new Error(`${pluginName}: appHost is required`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function processOptions(options, isVite) {
|
|
210
|
+
let {
|
|
14
211
|
https = true,
|
|
15
212
|
appHost = "",
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
bypassPrefixes = ["/static"],
|
|
20
|
-
// scriptCssPrefix = "",
|
|
213
|
+
localIndexHtml = "",
|
|
214
|
+
staticPrefix = "/dev/static",
|
|
215
|
+
remotePrefixes = ["/static/component"],
|
|
21
216
|
developmentAgentOccupancy = "",
|
|
22
217
|
clearScriptCssPrefixes = "",
|
|
23
|
-
entry = "/src/main.js",
|
|
218
|
+
entry = isVite ? "/src/main.js" : ["/js/chunk-vendors.js", "/js/app.js"],
|
|
24
219
|
debug = false
|
|
25
220
|
} = options;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
221
|
+
appHost = normalizePath(appHost);
|
|
222
|
+
const normalizedStaticPrefix = normalizePath(staticPrefix);
|
|
223
|
+
const fullEntry = generateEntryScript(entry, normalizedStaticPrefix);
|
|
224
|
+
const { log: log2, logError } = createLogger(debug);
|
|
225
|
+
return {
|
|
226
|
+
https,
|
|
227
|
+
appHost,
|
|
228
|
+
localIndexHtml,
|
|
229
|
+
normalizedStaticPrefix,
|
|
230
|
+
remotePrefixes,
|
|
231
|
+
developmentAgentOccupancy,
|
|
232
|
+
clearScriptCssPrefixes,
|
|
233
|
+
fullEntry,
|
|
234
|
+
log: log2,
|
|
235
|
+
logError
|
|
35
236
|
};
|
|
36
|
-
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function createProxyConfig(options) {
|
|
240
|
+
validateOptions(options, "vite-plugin-dev-proxy");
|
|
241
|
+
const {
|
|
242
|
+
https,
|
|
243
|
+
appHost,
|
|
244
|
+
localIndexHtml,
|
|
245
|
+
normalizedStaticPrefix,
|
|
246
|
+
remotePrefixes,
|
|
247
|
+
developmentAgentOccupancy,
|
|
248
|
+
clearScriptCssPrefixes,
|
|
249
|
+
fullEntry,
|
|
250
|
+
log,
|
|
251
|
+
logError
|
|
252
|
+
} = processOptions(options, true);
|
|
37
253
|
log("vite-plugin-dev-proxy: staticPrefix", normalizedStaticPrefix);
|
|
38
|
-
const
|
|
39
|
-
const scriptLinkRegex = /<(?:script[^>]*>.*?<\/script>|link[^>]*>)/g;
|
|
40
|
-
const assetRegex = /\.(js|mjs|ts|tsx|jsx|css|scss|sass|less|vue|json|woff2?|ttf|eot|ico|png|jpe?g|gif|svg|webp)(\?.*)?$/i;
|
|
41
|
-
const staticPathRegex = /^\/(static|assets|public|images|css|js)\//i;
|
|
42
|
-
const bypassRegex = /\.(vue|js|mjs|ts|tsx|jsx|css|scss|sass|less|json|png|jpe?g|gif|svg|webp|ico|woff2?|ttf|eot)$/i;
|
|
254
|
+
const protocol = https ? "https://" : "http://";
|
|
43
255
|
return {
|
|
44
256
|
"/": {
|
|
45
|
-
target: `${
|
|
257
|
+
target: `${protocol}${appHost}`,
|
|
46
258
|
changeOrigin: true,
|
|
47
259
|
secure: false,
|
|
48
260
|
cookieDomainRewrite: { "*": "localhost" },
|
|
49
261
|
selfHandleResponse: true,
|
|
50
|
-
configure: (proxy,
|
|
51
|
-
const rewriteCookies = (headers) => {
|
|
52
|
-
const setCookie = headers["set-cookie"];
|
|
53
|
-
if (setCookie) {
|
|
54
|
-
headers["set-cookie"] = setCookie.map((cookie) => {
|
|
55
|
-
let rewrittenCookie = cookie.replace(/;\s*secure\s*(;|$)/gi, "$1").replace(/;\s*domain\s*=[^;]+(;|$)/gi, "$1").replace(/;\s*samesite\s*=[^;]+(;|$)/gi, "$1").replace(/;+/g, ";").replace(/;\s*$/g, "");
|
|
56
|
-
log("vite-plugin-dev-proxy: rewrittenCookie", rewrittenCookie);
|
|
57
|
-
return rewrittenCookie;
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
return headers;
|
|
61
|
-
};
|
|
262
|
+
configure: (proxy, _options) => {
|
|
62
263
|
proxy.on(
|
|
63
264
|
"proxyRes",
|
|
64
265
|
(proxyRes, req, res) => {
|
|
65
266
|
const startTime = Date.now();
|
|
66
267
|
const contentType = proxyRes.headers["content-type"] || "";
|
|
67
|
-
const redirectUrl = proxyRes.headers.location;
|
|
68
268
|
const requestUrl = req.url || "";
|
|
69
269
|
const acceptHeader = req.headers.accept || "";
|
|
70
|
-
const isRedirect = proxyRes
|
|
270
|
+
const isRedirect = isRedirectResponse(proxyRes);
|
|
71
271
|
if (isRedirect) {
|
|
72
|
-
|
|
73
|
-
const regex = new RegExp(appHost, "gi");
|
|
74
|
-
let location = redirectUrl.replace(regex, host || "");
|
|
75
|
-
location = location.replace(
|
|
76
|
-
/https:\/\/(localhost|127\.0\.0\.1)(:\d+)?/gi,
|
|
77
|
-
"http://$1$2"
|
|
78
|
-
);
|
|
79
|
-
const headers2 = rewriteCookies({ ...proxyRes.headers });
|
|
80
|
-
headers2.location = location;
|
|
81
|
-
res.writeHead(proxyRes.statusCode, headers2);
|
|
82
|
-
res.end();
|
|
83
|
-
log(
|
|
84
|
-
`Redirect handled: ${redirectUrl} -> ${location} (${Date.now() - startTime}ms)`
|
|
85
|
-
);
|
|
272
|
+
handleRedirect(proxyRes, req, res, appHost, log, startTime);
|
|
86
273
|
return;
|
|
87
274
|
}
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
275
|
+
const shouldProcessHtml = shouldProcessAsHtml(
|
|
276
|
+
contentType,
|
|
277
|
+
acceptHeader,
|
|
278
|
+
requestUrl,
|
|
279
|
+
isRedirect
|
|
280
|
+
);
|
|
92
281
|
if (shouldProcessHtml) {
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
});
|
|
102
|
-
res.end(indexHtml);
|
|
103
|
-
log(
|
|
104
|
-
`Local HTML served: ${localIndexHtml}\uFF09 (${Date.now() - startTime}ms)`
|
|
105
|
-
);
|
|
106
|
-
} catch (err) {
|
|
107
|
-
logError("Failed to read local HTML:", err);
|
|
108
|
-
res.writeHead(500);
|
|
109
|
-
res.end("Failed to read local HTML");
|
|
110
|
-
}
|
|
282
|
+
if (localIndexHtml) {
|
|
283
|
+
handleLibModeHtml(
|
|
284
|
+
localIndexHtml,
|
|
285
|
+
res,
|
|
286
|
+
log,
|
|
287
|
+
logError,
|
|
288
|
+
startTime
|
|
289
|
+
);
|
|
111
290
|
return;
|
|
112
291
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
);
|
|
123
|
-
proxyRes.on("end", () => {
|
|
124
|
-
try {
|
|
125
|
-
let buffer = Buffer.concat(chunks);
|
|
126
|
-
const decompress = () => {
|
|
127
|
-
if (encoding === "gzip") {
|
|
128
|
-
return zlib__default.gunzipSync(buffer);
|
|
129
|
-
} else if (encoding === "deflate") {
|
|
130
|
-
return zlib__default.inflateSync(buffer);
|
|
131
|
-
} else if (encoding === "br") {
|
|
132
|
-
return zlib__default.brotliDecompressSync(buffer);
|
|
133
|
-
}
|
|
134
|
-
return buffer;
|
|
135
|
-
};
|
|
136
|
-
const decompressed = decompress();
|
|
137
|
-
let html = decompressed.toString("utf-8");
|
|
138
|
-
if (developmentAgentOccupancy) {
|
|
139
|
-
html = html.replace(
|
|
140
|
-
developmentAgentOccupancy,
|
|
141
|
-
`<script crossorigin type="module" src="${fullEntry}"><\/script>`
|
|
142
|
-
);
|
|
143
|
-
} else {
|
|
144
|
-
html = html.replace(
|
|
145
|
-
/<div[^>]*id=["']app["'][^>]*><\/div>/g,
|
|
146
|
-
(match) => `${match}<script crossorigin type="module" src="${fullEntry}"><\/script>`
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
clearScriptCssPrefixes;
|
|
150
|
-
html = html.replace(scriptLinkRegex, (match) => {
|
|
151
|
-
const srcMatch = match.match(/src="([^"]+)"/i);
|
|
152
|
-
const hrefMatch = match.match(/href="([^"]+)"/i);
|
|
153
|
-
const srcOrHref = srcMatch ? srcMatch[1] : hrefMatch ? hrefMatch[1] : null;
|
|
154
|
-
if (typeof clearScriptCssPrefixes === "string") {
|
|
155
|
-
if (srcOrHref?.startsWith(clearScriptCssPrefixes)) {
|
|
156
|
-
return "";
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
if (Array.isArray(clearScriptCssPrefixes)) {
|
|
160
|
-
if (clearScriptCssPrefixes.some(
|
|
161
|
-
(prefix) => srcOrHref?.startsWith(prefix)
|
|
162
|
-
)) {
|
|
163
|
-
return "";
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
if (clearScriptCssPrefixes instanceof RegExp) {
|
|
167
|
-
return clearScriptCssPrefixes.test(match) ? "" : match;
|
|
168
|
-
}
|
|
169
|
-
if (typeof clearScriptCssPrefixes === "function") {
|
|
170
|
-
return clearScriptCssPrefixes(match) ? "" : match;
|
|
171
|
-
}
|
|
172
|
-
return match;
|
|
173
|
-
});
|
|
174
|
-
if (html.indexOf(fullEntry) === -1) {
|
|
175
|
-
html = html.replace(
|
|
176
|
-
/<!--\sS 公共组件 提示信息\s-->/g,
|
|
177
|
-
`<script crossorigin type="module" src="${fullEntry}"><\/script>`
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
const headers2 = rewriteCookies({ ...proxyRes.headers });
|
|
181
|
-
headers2["content-type"] = "text/html; charset=utf-8";
|
|
182
|
-
delete headers2["content-encoding"];
|
|
183
|
-
delete headers2["content-length"];
|
|
184
|
-
res.writeHead(200, headers2);
|
|
185
|
-
res.end(html);
|
|
186
|
-
log(
|
|
187
|
-
`HTML processed: ${requestUrl} (${Date.now() - startTime}ms)`
|
|
188
|
-
);
|
|
189
|
-
} catch (err) {
|
|
190
|
-
logError("Decompress error:", err);
|
|
191
|
-
logError("Request URL:", requestUrl);
|
|
192
|
-
logError("Response headers:", proxyRes.headers);
|
|
193
|
-
res.writeHead(500);
|
|
194
|
-
res.end("Decompress error");
|
|
195
|
-
}
|
|
292
|
+
handleHtmlResponse(proxyRes, req, res, {
|
|
293
|
+
fullEntry,
|
|
294
|
+
developmentAgentOccupancy,
|
|
295
|
+
clearScriptCssPrefixes,
|
|
296
|
+
log,
|
|
297
|
+
logError,
|
|
298
|
+
startTime
|
|
196
299
|
});
|
|
197
300
|
return;
|
|
198
301
|
}
|
|
199
|
-
const headers = rewriteCookies({ ...proxyRes.headers });
|
|
302
|
+
const headers = rewriteCookies({ ...proxyRes.headers }, log);
|
|
200
303
|
res.writeHead(proxyRes.statusCode, headers);
|
|
201
304
|
proxyRes.pipe(res);
|
|
202
|
-
log(`Proxy request
|
|
305
|
+
log(`[Proxy request] ${requestUrl} (${Date.now() - startTime}ms)`);
|
|
203
306
|
}
|
|
204
307
|
);
|
|
205
308
|
},
|
|
206
309
|
bypass: (req) => {
|
|
207
310
|
const url = req.url || "";
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
log(`Bypass proxy: ${url}`);
|
|
311
|
+
if (shouldUseLocal(url, normalizedStaticPrefix, remotePrefixes)) {
|
|
312
|
+
log(`shouldUseLocal: ${url}`);
|
|
211
313
|
return url;
|
|
212
314
|
}
|
|
213
315
|
}
|
|
@@ -236,4 +338,129 @@ function viteDevProxy(options = {}) {
|
|
|
236
338
|
};
|
|
237
339
|
}
|
|
238
340
|
|
|
239
|
-
|
|
341
|
+
function createVueCliProxyConfig(options) {
|
|
342
|
+
validateOptions(options, "vue-cli-plugin-dev-proxy");
|
|
343
|
+
const {
|
|
344
|
+
https,
|
|
345
|
+
appHost,
|
|
346
|
+
localIndexHtml,
|
|
347
|
+
normalizedStaticPrefix,
|
|
348
|
+
remotePrefixes,
|
|
349
|
+
developmentAgentOccupancy,
|
|
350
|
+
clearScriptCssPrefixes,
|
|
351
|
+
fullEntry,
|
|
352
|
+
log,
|
|
353
|
+
logError
|
|
354
|
+
} = processOptions(options);
|
|
355
|
+
log("vue-cli-plugin-dev-proxy: staticPrefix", normalizedStaticPrefix);
|
|
356
|
+
const protocol = https ? "https://" : "http://";
|
|
357
|
+
return {
|
|
358
|
+
"/": {
|
|
359
|
+
target: `${protocol}${appHost}`,
|
|
360
|
+
changeOrigin: true,
|
|
361
|
+
secure: false,
|
|
362
|
+
ws: false,
|
|
363
|
+
//5.x 版本需要设置,否则会报错 Invalid frame header,4.x 版本不需要设置
|
|
364
|
+
cookieDomainRewrite: { "*": "localhost" },
|
|
365
|
+
selfHandleResponse: true,
|
|
366
|
+
onProxyReq: (proxyReq, req, res) => {
|
|
367
|
+
const upgradeHeader = req.headers.upgrade;
|
|
368
|
+
const isWebSocket = upgradeHeader && upgradeHeader.toLowerCase() === "websocket";
|
|
369
|
+
if (isWebSocket) {
|
|
370
|
+
log(
|
|
371
|
+
`[WebSocket] ${req.method} ${req.url} -> ${protocol}${proxyReq.getHeader("host")}${proxyReq.path}`
|
|
372
|
+
);
|
|
373
|
+
} else {
|
|
374
|
+
log(
|
|
375
|
+
`[proxyReq] ${req.method} ${req.url} -> ${protocol}${proxyReq.getHeader("host")}${proxyReq.path}`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
onError: (err, req, res) => {
|
|
380
|
+
const upgradeHeader = req.headers.upgrade;
|
|
381
|
+
const isWebSocket = upgradeHeader && upgradeHeader.toLowerCase() === "websocket";
|
|
382
|
+
if (isWebSocket) {
|
|
383
|
+
logError(`[WebSocket Error] ${req.url}:`, err.message);
|
|
384
|
+
} else {
|
|
385
|
+
logError(`[proxyError] ${req.url}:`, err.message);
|
|
386
|
+
}
|
|
387
|
+
if (!res.headersSent && !isWebSocket) {
|
|
388
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
389
|
+
res.end("Proxy Error: " + err.message);
|
|
390
|
+
}
|
|
391
|
+
},
|
|
392
|
+
onProxyRes: (proxyRes, req, res) => {
|
|
393
|
+
const startTime = Date.now();
|
|
394
|
+
const contentType = proxyRes.headers["content-type"] || "";
|
|
395
|
+
const requestUrl = req.url || "";
|
|
396
|
+
const acceptHeader = req.headers.accept || "";
|
|
397
|
+
log(
|
|
398
|
+
`[proxyRes] ${requestUrl} - Status: ${proxyRes.statusCode}, ContentType: ${contentType}`
|
|
399
|
+
);
|
|
400
|
+
const isRedirect = isRedirectResponse(proxyRes);
|
|
401
|
+
if (isRedirect) {
|
|
402
|
+
handleRedirect(proxyRes, req, res, appHost, log, startTime);
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
const shouldProcessHtml = shouldProcessAsHtml(
|
|
406
|
+
contentType,
|
|
407
|
+
acceptHeader,
|
|
408
|
+
requestUrl,
|
|
409
|
+
isRedirect
|
|
410
|
+
);
|
|
411
|
+
log(
|
|
412
|
+
`[shouldProcessHtml] ${shouldProcessHtml}, requestUrl: ${requestUrl}`
|
|
413
|
+
);
|
|
414
|
+
if (shouldProcessHtml) {
|
|
415
|
+
if (localIndexHtml) {
|
|
416
|
+
handleLibModeHtml(localIndexHtml, res, log, logError, startTime);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
handleHtmlResponse(proxyRes, req, res, {
|
|
420
|
+
fullEntry,
|
|
421
|
+
developmentAgentOccupancy,
|
|
422
|
+
clearScriptCssPrefixes,
|
|
423
|
+
log,
|
|
424
|
+
logError,
|
|
425
|
+
startTime
|
|
426
|
+
});
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
const headers = rewriteCookies({ ...proxyRes.headers }, log);
|
|
430
|
+
res.writeHead(proxyRes.statusCode, headers);
|
|
431
|
+
proxyRes.pipe(res);
|
|
432
|
+
log(`[Proxy request]: ${requestUrl} (${Date.now() - startTime}ms)`);
|
|
433
|
+
},
|
|
434
|
+
bypass: (req) => {
|
|
435
|
+
const url = req.url || "";
|
|
436
|
+
if (shouldUseLocal(url, normalizedStaticPrefix, remotePrefixes)) {
|
|
437
|
+
log(`[shouldUseLocal] ${url}`);
|
|
438
|
+
return url;
|
|
439
|
+
}
|
|
440
|
+
log(`[Proxy] ${url}`);
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
function vueCliDevProxy(options = {}) {
|
|
447
|
+
if (process.env.NODE_ENV !== "development") {
|
|
448
|
+
return {};
|
|
449
|
+
}
|
|
450
|
+
const proxyConfig = createVueCliProxyConfig(options);
|
|
451
|
+
return {
|
|
452
|
+
devServer: {
|
|
453
|
+
proxy: proxyConfig
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const index = {
|
|
459
|
+
VitePluginDevProxy: viteDevProxy,
|
|
460
|
+
VueCliPluginDevProxy: vueCliDevProxy
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
exports.VitePluginDevProxy = viteDevProxy;
|
|
464
|
+
exports.VueCliPluginDevProxy = vueCliDevProxy;
|
|
465
|
+
exports.createVueCliProxyConfig = createVueCliProxyConfig;
|
|
466
|
+
exports.default = index;
|