@xysfe/vite-plugin-dev-proxy 1.0.4 → 1.0.6
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/CHANGELOG.md +160 -0
- package/README.md +20 -6
- package/dist/index.cjs +12 -7
- package/dist/index.mjs +12 -7
- package/package.json +1 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# 更新日志
|
|
2
|
+
|
|
3
|
+
所有重要的项目变更都将记录在此文件中。
|
|
4
|
+
|
|
5
|
+
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
|
|
6
|
+
版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
7
|
+
|
|
8
|
+
## [1.0.1] - 2024-01-30
|
|
9
|
+
|
|
10
|
+
### 修复 (Fixed)
|
|
11
|
+
|
|
12
|
+
- **WebSocket 热更新支持** - 修复 Vue CLI/Vite 热更新 WebSocket 被错误代理的问题
|
|
13
|
+
- 自动排除 `/ws` 路径(Vite HMR WebSocket)
|
|
14
|
+
- 自动排除 `/sockjs-node/*` 路径(Vue CLI HMR WebSocket)
|
|
15
|
+
- 解决 `ECONNRESET` 错误和连接失败问题
|
|
16
|
+
- 无需手动配置 WebSocket 代理
|
|
17
|
+
|
|
18
|
+
### 改进 (Improved)
|
|
19
|
+
|
|
20
|
+
- **零配置 WebSocket** - 所有 HMR WebSocket 自动在本地处理,无需额外配置
|
|
21
|
+
- **更好的默认行为** - `shouldUseLocal` 函数现在默认识别所有常见的热更新路径
|
|
22
|
+
|
|
23
|
+
### 文档 (Documentation)
|
|
24
|
+
|
|
25
|
+
- 新增 [WebSocket 热更新修复说明](./examples/WEBSOCKET-HMR-FIX.md)
|
|
26
|
+
- 新增 [WebSocket 故障排查指南](./examples/WEBSOCKET-TROUBLESHOOTING.md)
|
|
27
|
+
- 更新 README 特性列表,添加 WebSocket 热更新说明
|
|
28
|
+
|
|
29
|
+
### 技术细节
|
|
30
|
+
|
|
31
|
+
**修改文件**:`src/core.ts`
|
|
32
|
+
|
|
33
|
+
**修改前**:
|
|
34
|
+
```typescript
|
|
35
|
+
url.startsWith("/sockjs-node/info") || // 只排除 /sockjs-node/info
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**修改后**:
|
|
39
|
+
```typescript
|
|
40
|
+
url.startsWith("/sockjs-node") || // 排除所有 /sockjs-node/*
|
|
41
|
+
url.startsWith("/ws") || // 排除 /ws
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
这个修复确保以下路径自动在本地处理:
|
|
45
|
+
- `/ws` - Vite WebSocket
|
|
46
|
+
- `/ws?*` - 带查询参数的 WebSocket
|
|
47
|
+
- `/sockjs-node/info` - SockJS 信息端点
|
|
48
|
+
- `/sockjs-node/178/h54tlptm/websocket` - SockJS WebSocket
|
|
49
|
+
- `/sockjs-node/*` - 所有 SockJS 路径
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## [1.0.0] - 2024-01-28
|
|
54
|
+
|
|
55
|
+
### 新增 (Added)
|
|
56
|
+
|
|
57
|
+
- 🎉 **首次发布** - dev-proxy-plugin 正式发布
|
|
58
|
+
|
|
59
|
+
### 核心特性
|
|
60
|
+
|
|
61
|
+
- ✅ **双框架支持** - 同时支持 Vite 和 Vue CLI
|
|
62
|
+
- ✅ **智能代理** - 自动代理远程服务器的 HTML、API 等请求
|
|
63
|
+
- ✅ **脚本注入** - 自动注入本地入口脚本到远程 HTML
|
|
64
|
+
- ✅ **脚本清理** - 灵活清除远程 HTML 中不需要的脚本和样式
|
|
65
|
+
- ✅ **Cookie 处理** - 自动重写 Cookie,解决本地开发跨域问题
|
|
66
|
+
- ✅ **重定向处理** - 智能处理 HTTP 重定向,自动转换为本地地址
|
|
67
|
+
- ✅ **解压缩支持** - 支持 gzip、deflate、brotli 压缩格式
|
|
68
|
+
- ✅ **灵活配置** - 支持字符串、数组、函数、正则等多种配置方式
|
|
69
|
+
|
|
70
|
+
### 配置选项
|
|
71
|
+
|
|
72
|
+
#### ProxyOptions
|
|
73
|
+
|
|
74
|
+
- `appHost` - 远程服务器地址(必填)
|
|
75
|
+
- `https` - 是否使用 HTTPS 协议(默认 `true`)
|
|
76
|
+
- `staticPrefix` - 静态资源路径前缀(默认 `'/dev/static'`)
|
|
77
|
+
- `remotePrefixes` - 远程资源路径规则(支持 string, string[], Function, RegExp)
|
|
78
|
+
- `clearScriptCssPrefixes` - 清除脚本/CSS 的规则(支持 string, string[], Function, RegExp)
|
|
79
|
+
- `entry` - 本地入口文件路径(支持单个或多个)
|
|
80
|
+
- `developmentAgentOccupancy` - 自定义占位符
|
|
81
|
+
- `isLib` - 库模式(默认 `false`)
|
|
82
|
+
- `localIndexHtml` - 本地 HTML 文件路径(默认 `'index.html'`)
|
|
83
|
+
- `debug` - 是否开启调试模式(默认 `false`)
|
|
84
|
+
|
|
85
|
+
### 项目结构
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
src/
|
|
89
|
+
├── core.ts # 核心共享逻辑(~530 行)
|
|
90
|
+
├── vite-cli.ts # Vite 插件(~160 行)
|
|
91
|
+
├── vue-cli-plugin-dev-proxy.ts # Vue CLI 插件(~180 行)
|
|
92
|
+
└── index.ts # 入口文件
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 文档
|
|
96
|
+
|
|
97
|
+
- ✅ 完整的中文 README
|
|
98
|
+
- ✅ remotePrefixes 详细用法指南
|
|
99
|
+
- ✅ 示例项目(Vite、Vue CLI、Webpack)
|
|
100
|
+
- ✅ TypeScript 类型定义和 JSDoc 注释
|
|
101
|
+
|
|
102
|
+
### 工具函数
|
|
103
|
+
|
|
104
|
+
核心模块提供 20+ 个工具函数:
|
|
105
|
+
|
|
106
|
+
- `createLogger` - 创建日志函数
|
|
107
|
+
- `normalizePath` - 路径标准化
|
|
108
|
+
- `generateEntryScript` - 生成入口脚本
|
|
109
|
+
- `rewriteCookies` - Cookie 重写
|
|
110
|
+
- `decompressBuffer` - 解压缩
|
|
111
|
+
- `shouldClearScriptCss` - 判断是否清除标签
|
|
112
|
+
- `injectEntryScript` - 注入脚本
|
|
113
|
+
- `clearScriptCssTags` - 清除标签
|
|
114
|
+
- `isRedirectResponse` - 判断重定向
|
|
115
|
+
- `shouldProcessAsHtml` - 判断处理HTML
|
|
116
|
+
- `matchesRemoteResource` - 匹配远程资源
|
|
117
|
+
- `shouldUseLocal` - 判断使用本地
|
|
118
|
+
- `handleRedirect` - 处理重定向
|
|
119
|
+
- `handleLibModeHtml` - 处理库模式HTML
|
|
120
|
+
- `handleHtmlResponse` - 处理HTML响应
|
|
121
|
+
- `validateOptions` - 验证配置
|
|
122
|
+
- `processOptions` - 处理配置
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 版本说明
|
|
127
|
+
|
|
128
|
+
### 版本号规则
|
|
129
|
+
|
|
130
|
+
遵循 [语义化版本 2.0.0](https://semver.org/lang/zh-CN/):
|
|
131
|
+
|
|
132
|
+
- **主版本号(Major)**:不兼容的 API 修改
|
|
133
|
+
- **次版本号(Minor)**:向后兼容的功能性新增
|
|
134
|
+
- **修订号(Patch)**:向后兼容的问题修正
|
|
135
|
+
|
|
136
|
+
### 版本标签
|
|
137
|
+
|
|
138
|
+
- `[Unreleased]` - 未发布的变更
|
|
139
|
+
- `[X.Y.Z]` - 具体版本号和发布日期
|
|
140
|
+
|
|
141
|
+
### 变更类型
|
|
142
|
+
|
|
143
|
+
- `Added` - 新增功能
|
|
144
|
+
- `Changed` - 对现有功能的变更
|
|
145
|
+
- `Deprecated` - 已弃用的功能
|
|
146
|
+
- `Removed` - 移除的功能
|
|
147
|
+
- `Fixed` - 任何 bug 修复
|
|
148
|
+
- `Security` - 安全性修复
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 链接
|
|
153
|
+
|
|
154
|
+
- [npm 包](https://www.npmjs.com/package/dev-proxy-plugin)
|
|
155
|
+
- [GitHub 仓库](https://github.com/CNLHB/dev-proxy-plugin)
|
|
156
|
+
- [问题反馈](https://github.com/CNLHB/dev-proxy-plugin/issues)
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
Made with ❤️ by [aiwa](https://cnlhb.github.io/blog/)
|
package/README.md
CHANGED
|
@@ -64,16 +64,30 @@ export default defineConfig({
|
|
|
64
64
|
```javascript
|
|
65
65
|
// vue.config.js
|
|
66
66
|
const { VueCliPluginDevProxy } = require("@xysfe/vite-plugin-dev-proxy");
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
appHost: "
|
|
67
|
+
const staticPrefix = "/dev/static";
|
|
68
|
+
const devProxy = VueCliPluginDevProxy({
|
|
69
|
+
appHost: "beta-internal.cxmuc.com",
|
|
70
70
|
https: true,
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
staticPrefix: staticPrefix,
|
|
72
|
+
clearScriptCssPrefixes: ["//sslstatic.xiaoyusan.com/contract"],
|
|
73
73
|
remotePrefixes: ["/static/component"],
|
|
74
|
-
|
|
74
|
+
developmentAgentOccupancy: "",
|
|
75
|
+
// entry: dynamicEntry,
|
|
75
76
|
debug: true,
|
|
76
77
|
});
|
|
78
|
+
|
|
79
|
+
module.exports = {
|
|
80
|
+
outputDir: outputDir,
|
|
81
|
+
indexPath: indexPath,
|
|
82
|
+
publicPath: staticPrefix,
|
|
83
|
+
lintOnSave: lint,
|
|
84
|
+
runtimeCompiler: true,
|
|
85
|
+
productionSourceMap,
|
|
86
|
+
crossorigin: "anonymous",
|
|
87
|
+
devServer: {
|
|
88
|
+
proxy: devProxy.devServer.proxy,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
77
91
|
```
|
|
78
92
|
|
|
79
93
|
## 📚 配置选项
|
package/dist/index.cjs
CHANGED
|
@@ -123,7 +123,8 @@ function shouldUseLocal(url, normalizedStaticPrefix, remotePrefixes) {
|
|
|
123
123
|
const pathname = url.split("?")[0];
|
|
124
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
125
|
url.startsWith("/ws") || // Vue CLI/Webpack 热更新 WebSocket (4.x+/Vite)
|
|
126
|
-
url === "/" ||
|
|
126
|
+
// url === "/" || // 根路径处理,默认也走远程吧,走本地不处理,默认都读本地的html vue-cli配置的 indexPath: indexPath,
|
|
127
|
+
BYPASS_REGEX.test(pathname);
|
|
127
128
|
const isRemoteResource = matchesRemoteResource(url, remotePrefixes);
|
|
128
129
|
return isLocalResource && !isRemoteResource;
|
|
129
130
|
}
|
|
@@ -172,16 +173,16 @@ function handleHtmlResponse(proxyRes, req, res, context) {
|
|
|
172
173
|
const buffer = Buffer.concat(chunks);
|
|
173
174
|
const decompressed = decompressBuffer(buffer, encoding);
|
|
174
175
|
let html = decompressed.toString("utf-8");
|
|
175
|
-
html = injectEntryScript(
|
|
176
|
-
html,
|
|
177
|
-
context.fullEntry,
|
|
178
|
-
context.developmentAgentOccupancy
|
|
179
|
-
);
|
|
180
176
|
html = clearScriptCssTags(
|
|
181
177
|
html,
|
|
182
178
|
context.clearScriptCssPrefixes,
|
|
183
179
|
context.log
|
|
184
180
|
);
|
|
181
|
+
html = injectEntryScript(
|
|
182
|
+
html,
|
|
183
|
+
context.fullEntry,
|
|
184
|
+
context.developmentAgentOccupancy
|
|
185
|
+
);
|
|
185
186
|
const headers = rewriteCookies({ ...proxyRes.headers }, context.log);
|
|
186
187
|
headers["content-type"] = "text/html; charset=utf-8";
|
|
187
188
|
delete headers["content-encoding"];
|
|
@@ -445,7 +446,11 @@ function createVueCliProxyConfig(options) {
|
|
|
445
446
|
}
|
|
446
447
|
function vueCliDevProxy(options = {}) {
|
|
447
448
|
if (process.env.NODE_ENV !== "development") {
|
|
448
|
-
return {
|
|
449
|
+
return {
|
|
450
|
+
devServer: {
|
|
451
|
+
proxy: {}
|
|
452
|
+
}
|
|
453
|
+
};
|
|
449
454
|
}
|
|
450
455
|
const proxyConfig = createVueCliProxyConfig(options);
|
|
451
456
|
return {
|
package/dist/index.mjs
CHANGED
|
@@ -114,7 +114,8 @@ function shouldUseLocal(url, normalizedStaticPrefix, remotePrefixes) {
|
|
|
114
114
|
const pathname = url.split("?")[0];
|
|
115
115
|
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)
|
|
116
116
|
url.startsWith("/ws") || // Vue CLI/Webpack 热更新 WebSocket (4.x+/Vite)
|
|
117
|
-
url === "/" ||
|
|
117
|
+
// url === "/" || // 根路径处理,默认也走远程吧,走本地不处理,默认都读本地的html vue-cli配置的 indexPath: indexPath,
|
|
118
|
+
BYPASS_REGEX.test(pathname);
|
|
118
119
|
const isRemoteResource = matchesRemoteResource(url, remotePrefixes);
|
|
119
120
|
return isLocalResource && !isRemoteResource;
|
|
120
121
|
}
|
|
@@ -163,16 +164,16 @@ function handleHtmlResponse(proxyRes, req, res, context) {
|
|
|
163
164
|
const buffer = Buffer.concat(chunks);
|
|
164
165
|
const decompressed = decompressBuffer(buffer, encoding);
|
|
165
166
|
let html = decompressed.toString("utf-8");
|
|
166
|
-
html = injectEntryScript(
|
|
167
|
-
html,
|
|
168
|
-
context.fullEntry,
|
|
169
|
-
context.developmentAgentOccupancy
|
|
170
|
-
);
|
|
171
167
|
html = clearScriptCssTags(
|
|
172
168
|
html,
|
|
173
169
|
context.clearScriptCssPrefixes,
|
|
174
170
|
context.log
|
|
175
171
|
);
|
|
172
|
+
html = injectEntryScript(
|
|
173
|
+
html,
|
|
174
|
+
context.fullEntry,
|
|
175
|
+
context.developmentAgentOccupancy
|
|
176
|
+
);
|
|
176
177
|
const headers = rewriteCookies({ ...proxyRes.headers }, context.log);
|
|
177
178
|
headers["content-type"] = "text/html; charset=utf-8";
|
|
178
179
|
delete headers["content-encoding"];
|
|
@@ -436,7 +437,11 @@ function createVueCliProxyConfig(options) {
|
|
|
436
437
|
}
|
|
437
438
|
function vueCliDevProxy(options = {}) {
|
|
438
439
|
if (process.env.NODE_ENV !== "development") {
|
|
439
|
-
return {
|
|
440
|
+
return {
|
|
441
|
+
devServer: {
|
|
442
|
+
proxy: {}
|
|
443
|
+
}
|
|
444
|
+
};
|
|
440
445
|
}
|
|
441
446
|
const proxyConfig = createVueCliProxyConfig(options);
|
|
442
447
|
return {
|
package/package.json
CHANGED