@wang.gsjt/remote-dev-sync
v1.4.0
Published
Rspack/Webpack/Vite 通用插件,远程沙箱同步本地包源码并热更新
Readme
remote-dev-sync
远程沙箱同步本地包源码的开发工具,支持 Vite / Rspack / Webpack,带热更新。
适用场景
你在本地开发 npm 包(如 monorepo 中的若干子包),远程沙箱中的项目依赖这些包。你希望本地改代码后,远程沙箱能自动拉取最新的构建产物并热更新,不需要重新发包。
安装
# 全局安装(推荐,可直接使用 remote-dev-sync 命令)
npm install -g @wang.gsjt/remote-dev-sync
# 或项目内安装
npm install @wang.gsjt/remote-dev-sync --save-dev快速开始
第一步:本机一键启动
在你的 monorepo 根目录下:
# 生成配置文件模板
remote-dev-sync init编辑 .remote-dev.config.json:
{
"packages": ["@your-scope/package-a", "@your-scope/package-b"],
"serverPackages": ["@your-scope/server-pkg"],
"buildCommand": "yarn turbo dev {filters}",
"tunnel": true
}packages:前端包,远端通过 Vite resolveId 从.remote-dev-sync/加载serverPackages:服务端包,远端直接同步到node_modules/,供 NestJS 等 Node.js 服务使用
buildCommand 是本地构建 watch 的启动脚本,常见写法:
// turbo monorepo(yarn / pnpm)
{ "buildCommand": "yarn turbo dev {filters}" }
{ "buildCommand": "pnpm turbo dev {filters}" }
// lerna monorepo
{ "buildCommand": "lerna run dev --scope={packages}" }
// 单包项目,直接写死命令
{ "buildCommand": "npm run dev" }
{ "buildCommand": "tsup --watch" }{filters}会展开为--filter=@scope/pkg-a --filter=@scope/pkg-b{packages}会展开为"@scope/pkg-a" "@scope/pkg-b"- 不配置时自动检测:有
turbo.json则用turbo dev,否则跳过
monorepo不传则默认为配置文件所在目录。如果 monorepo 在别处,传绝对路径:"monorepo": "/path/to/monorepo"。
# 一键启动:构建 watch + 文件服务 + 隧道
remote-dev-sync start启动后会输出远程沙箱需要的配置:
========================================
READY!
========================================
Tunnel URL: https://xxx.trycloudflare.com
Local URL: http://localhost:8787
Remote .remote-dev.json:
{
"remote": "https://xxx.trycloudflare.com",
"packages": ["@your-scope/package-a"],
"mode": "ws"
}
========================================第二步:远程沙箱配置
在远程项目中安装:
npm install @wang.gsjt/remote-dev-sync --save-dev在远程项目根目录创建 .remote-dev.json,只需配置 remote 即可,packages 会自动从本机服务端获取:
{
"remote": "https://xxx.trycloudflare.com",
"mode": "ws"
}
packages是可选的。不配置时自动从本机服务端的GET /packages接口获取,与本机.remote-dev.config.json中的包列表保持一致。如果只想同步部分包,可以手动指定packages进行过滤。
建议将
.remote-dev.json和.remote-dev-sync/加入.gitignore。
在构建配置中加入插件:
Vite(vite.config.ts):
import { viteRemoteDev } from '@wang.gsjt/remote-dev-sync/plugins/vite';
export default defineConfig({
plugins: [
viteRemoteDev(), // 自动读取 .remote-dev.json
]
});Rspack / Webpack(rspack.config.js):
const { RemoteDevPlugin } = require('@wang.gsjt/remote-dev-sync');
module.exports = {
plugins: [
new RemoteDevPlugin(), // 自动读取 .remote-dev.json
]
};第三步:启动远程 dev server
npm run dev之后你在本机改代码:
- ws 模式:~300ms 内自动同步并热更新
- http 模式:按 poll 间隔自动同步并热更新
工作原理
本地 monorepo 远程沙箱
┌────────────────────┐ ┌─────────────────────────────┐
│ 修改源码 │ │ .remote-dev-sync/ │
│ ↓ │ │ └─ @my/pkg/lib/ │
│ turbo dev (watch) │ │ (同步过来的构建产物) │
│ ↓ │ │ │
│ lib/ 产物更新 │ HTTP/WS │ Vite resolveId hook │
│ ↓ │ ──────────→ │ ↓ 拦截 import, 指向同步目录 │
│ remote-dev-sync │ 隧道/直连 │ ↓ │
│ server (8787) │ │ HMR / full-reload │
└────────────────────┘ └─────────────────────────────┘Vite 插件策略:前端包不修改 node_modules,不使用 optimizeDeps.exclude。
同步文件写入 .remote-dev-sync/ 独立目录,通过 resolveId hook 按 package.json 的
exports 字段将 import 解析到同步目录。包的 CJS 依赖仍从 node_modules 正常解析,
由 Vite optimizer 预构建为 ESM,不存在兼容问题。
服务端包(serverPackages)直接同步到 node_modules/,供 NestJS 等 Node.js 运行时使用。
Node.js 没有 Vite 的 optimizer 缓存/CJS 兼容问题,直接写入 node_modules 是最简单可靠的方案。
每次启动时先清空 .remote-dev-sync/ 目录确保干净状态,再从 node_modules 复制 baseline(毫秒级),确保 Vite 立即可用,最后从远端拉取最新文件覆盖。
隧道支持
CLI 自动检测可用的隧道工具:
| 优先级 | 工具 | 协议 | 说明 |
|--------|------|------|------|
| 1 | cloudflared | HTTP/2 | Cloudflare Tunnel,免费、稳定(推荐 brew install cloudflared) |
| 2 | localtunnel | HTTPS | 免费但不太稳定,偶有 503/408 |
如果两者都不可用,可以手动启动隧道或使用内网直连。
内网直连(本机和沙箱在同一网络时,最稳定):
// .remote-dev.config.json
{ "packages": ["@my/pkg"], "tunnel": false }
// 远端 .remote-dev.json(packages 自动从服务端获取,无需重复配置)
{ "remote": "http://<本机IP>:8787", "mode": "ws" }配置参考
.remote-dev.config.json(本机)
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| packages | string[] | 是 | — | 要同步的前端包名列表 |
| serverPackages | string[] | 否 | [] | 服务端包名列表(同步到远端 node_modules/,供 NestJS 等 Node.js 服务使用) |
| monorepo | string | 否 | 配置文件所在目录 | Monorepo 根目录路径 |
| port | number | 否 | 8787 | 本地文件服务端口 |
| tunnel | boolean | 否 | true | 是否启动隧道(优先 cloudflared,fallback localtunnel) |
| buildCommand | string | 否 | 自动检测 | 构建 watch 命令,支持 {filters} 和 {packages} 模板变量 |
.remote-dev.json(远程沙箱)
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| remote | string | 是 | — | 本机文件服务地址(隧道 URL 或内网 IP) |
| packages | string[] | 否 | 自动从服务端获取 | 需要同步的包名列表。不配置时自动从本机 GET /packages 获取全部包 |
| mode | "ws" | "http" | 否 | "http" | 同步模式:ws=WebSocket 推送,http=轮询 |
| poll | number | 否 | 3000 | http 模式的轮询间隔(毫秒),ws 模式下忽略 |
也可以通过代码传参(优先级高于 .remote-dev.json):
viteRemoteDev({
remote: 'http://192.168.1.100:8787',
packages: ['@my/ui'],
mode: 'ws',
})CLI 命令
remote-dev-sync start — 一键启动(推荐)
remote-dev-sync start # 使用当前目录的 .remote-dev.config.json
remote-dev-sync start ./my-config.json # 指定配置文件remote-dev-sync serve — 手动参数启动
# 暴露 monorepo 中的所有包
remote-dev-sync serve --monorepo /path/to/monorepo --tunnel
# 只暴露特定包
remote-dev-sync serve --monorepo /path/to/monorepo --filter ui --filter utils
# 单包模式
remote-dev-sync serve --pkg @my/ui=/path/to/ui
# 同时执行构建命令
remote-dev-sync serve --monorepo /path/to/monorepo --exec "yarn turbo dev --filter=@my/pkg" --tunnel| 参数 | 说明 |
|------|------|
| --port <port> | 服务端口(默认 8787) |
| --host <host> | 绑定地址(默认 0.0.0.0) |
| --monorepo <path> | Monorepo 根目录,自动扫描子包 |
| --filter <keyword> | 按关键字过滤包名(可重复) |
| --pkg <name>=<path> | 手动指定包名=路径(可重复) |
| --exec <command> | 同时执行的命令(如构建 watch) |
| --tunnel | 启动隧道(优先 cloudflared,fallback localtunnel) |
remote-dev-sync init — 生成配置模板
remote-dev-sync init项目内安装时,以上命令用
npx remote-dev-sync代替。
常见问题
隧道不稳定(503/408/504)?
- 优先安装 cloudflared:
brew install cloudflared(比 localtunnel 稳定) - 如果公司网络封了 QUIC/UDP,CLI 会自动使用
--protocol http2回退 - 最稳定的方案是内网直连(
"tunnel": false)
隧道 URL 每次启动都变?
是的,免费隧道每次分配随机域名。启动后需要更新远端的 .remote-dev.json。
同步很慢?
- 默认只同步
lib/、dist/、package.json(构建产物),不同步源码 - 服务端支持 gzip 压缩,减少传输体积
- HTTP 请求内置 5xx/超时自动重试(指数退避)
远程报错 "Failed to resolve import"?
确保 .remote-dev-sync/ 目录下有文件。首次启动会自动从 node_modules 复制 baseline,
如果仍然报错,检查 node_modules 中是否已安装该包。
macOS 防火墙拦截?
内网直连模式下,需要给 Node.js 加防火墙白名单:
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --add $(which node)
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --unblockapp $(which node)