@spotpatch/vite 1.8.0 → 1.10.0
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 +115 -43
- package/dist/cli.js +90 -25
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +117 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +123 -9
- package/dist/index.js.map +1 -1
- package/dist/runtime-client.js +34 -34
- package/dist/runtime-data-flow-panel.js +26 -0
- package/dist/runtime-data-flow-prelude.js +1 -0
- package/dist/runtime-external-handoff-panel.js +28 -0
- package/dist/runtime-react-adapter.js +1 -1
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -20,26 +20,22 @@ SpotPatch runs only with the Vite development server. Production builds contain
|
|
|
20
20
|
### One-command setup (recommended)
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
# pnpm project
|
|
24
|
-
pnpm dlx @spotpatch/vite@latest setup
|
|
25
|
-
|
|
26
|
-
# npm project
|
|
27
23
|
npx --yes @spotpatch/vite@latest setup
|
|
28
24
|
```
|
|
29
25
|
|
|
30
|
-
|
|
26
|
+
This npm-bootstrap command supports both npm and pnpm projects. It fetches the registry's actual latest CLI, detects the project package manager, installs that CLI's exact SpotPatch version, updates a supported `vite.config.*`, and verifies the result. The exact-version handoff avoids pnpm 11's default 24-hour `minimumReleaseAge` policy silently resolving `@latest` to an older mature release.
|
|
27
|
+
|
|
28
|
+
The initializer supports configuration objects and object-returning `defineConfig` callbacks, while ambiguous dynamic configurations fail without writing the config.
|
|
31
29
|
|
|
32
|
-
For
|
|
30
|
+
For npm, installation and initialization can also be kept separate:
|
|
33
31
|
|
|
34
32
|
```bash
|
|
35
33
|
npm install --save-dev @spotpatch/vite@latest
|
|
36
34
|
npx spotpatch-vite init
|
|
37
|
-
|
|
38
|
-
# or
|
|
39
|
-
pnpm add -D @spotpatch/vite@latest
|
|
40
|
-
pnpm exec spotpatch-vite init
|
|
41
35
|
```
|
|
42
36
|
|
|
37
|
+
With pnpm 11, use the recommended setup command, install a trusted exact version, or wait until the release is 24 hours old. SpotPatch does not globally disable the project's supply-chain quarantine.
|
|
38
|
+
|
|
43
39
|
The initializer places SpotPatch before the React plugin so source markers are injected before React transforms the module:
|
|
44
40
|
|
|
45
41
|
```ts
|
|
@@ -49,11 +45,11 @@ import react from "@vitejs/plugin-react-swc";
|
|
|
49
45
|
import { defineConfig } from "vite";
|
|
50
46
|
|
|
51
47
|
export default defineConfig({
|
|
52
|
-
plugins: [spotPatch({ trustedFastMode: true }), react()],
|
|
48
|
+
plugins: [spotPatch({ dataFlow: {}, trustedFastMode: true }), react()],
|
|
53
49
|
});
|
|
54
50
|
```
|
|
55
51
|
|
|
56
|
-
When no safe local TypeScript check can be discovered, it generates `spotPatch()` and keeps Review mode. Unsupported dynamic configuration can still be integrated manually using the same plugin order.
|
|
52
|
+
When no safe local TypeScript check can be discovered, it generates `spotPatch({ dataFlow: {} })` and keeps Review mode. Unsupported dynamic configuration can still be integrated manually using the same plugin order.
|
|
57
53
|
|
|
58
54
|
Start the application normally:
|
|
59
55
|
|
|
@@ -63,6 +59,12 @@ pnpm dev
|
|
|
63
59
|
|
|
64
60
|
Select **Select element** in the bottom-right corner or press `Mod+Shift+S`. SpotPatch can collect multiple targets across same-project pages, preserve them through navigation and workbench close/reopen cycles, keep a separate instruction for each one, open the exact source location in Cursor or VS Code, and generate a structured prompt without requiring AI configuration.
|
|
65
61
|
|
|
62
|
+
### Component data flow (Beta)
|
|
63
|
+
|
|
64
|
+
The current `setup/init` writes `spotPatch({ dataFlow: {} })` automatically; use the same option for manual integration. The Vite + React 18 development-only **Data flow** and **Page APIs** tabs show proven method/path, parameter keys, source-consumed response fields, data destinations, actually dispatched requests, and unassigned current-page traffic. Query values and response bodies are not collected. A relationship is reported only when stable component/source/callsite/invocation evidence agrees; ambiguous traffic remains unknown or unassigned. Data-flow AI, safe JSON response inspection, and Next.js are not included in this Beta. See the repository's [exact implementation status](https://github.com/huanglvjing/spotpatch/blob/main/docs/%E6%8A%80%E6%9C%AF%E6%96%B9%E6%A1%88/%E7%BB%84%E4%BB%B6%E6%95%B0%E6%8D%AE%E9%93%BE%E8%B7%AF/13-Beta%E5%AE%9E%E7%8E%B0%E7%8A%B6%E6%80%81%E4%B8%8E%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C.md).
|
|
65
|
+
|
|
66
|
+
Supported adapters include direct/component-service `fetch`, Axios, React Query/TanStack Query callbacks, and experimental tRPC logical procedures. tRPC batch HTTP transport remains separate evidence and is never assigned by timing or URL similarity.
|
|
67
|
+
|
|
66
68
|
### Compatibility
|
|
67
69
|
|
|
68
70
|
| Dependency | Supported range |
|
|
@@ -87,6 +89,8 @@ spotPatch({
|
|
|
87
89
|
locale: "auto",
|
|
88
90
|
maxTargets: 8,
|
|
89
91
|
ai: false,
|
|
92
|
+
dataFlow: false,
|
|
93
|
+
externalAgent: false,
|
|
90
94
|
});
|
|
91
95
|
```
|
|
92
96
|
|
|
@@ -104,10 +108,42 @@ spotPatch({
|
|
|
104
108
|
| `locale` | `"auto"` | Resolves `en-US` or `zh-CN`. |
|
|
105
109
|
| `maxTargets` | `8` | Targets allowed in one change request by default. |
|
|
106
110
|
| `ai` | disabled or a detected complete environment | Optional provider and Agent settings. |
|
|
111
|
+
| `dataFlow` | `false` | Opt-in dispatch-only component data-flow Beta. |
|
|
112
|
+
| `externalAgent` | `false` | Opt-in development-only external Agent handoff; local-validation only. |
|
|
107
113
|
| `trustedFastMode` | `false` | Exposes Review/Trusted direct and discovers TypeScript for Review. |
|
|
108
114
|
|
|
109
115
|
The package exports the option types, AI provider types, Agent limits, and immutable defaults. See the [public API specification](https://github.com/huanglvjing/spotpatch/blob/main/docs/%E6%8A%80%E6%9C%AF%E6%96%B9%E6%A1%88/03-%E5%85%AC%E5%85%B1API%E4%B8%8E%E6%95%B0%E6%8D%AE%E6%A8%A1%E5%9E%8B.md) for the complete constraints.
|
|
110
116
|
|
|
117
|
+
### External Agent handoff (local validation)
|
|
118
|
+
|
|
119
|
+
Enable the development-only UI explicitly:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
spotPatch({ externalAgent: true });
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Run setup and active commands from the exact canonical project root that owns the running Vite development session. Setup is a dry run without `--write`:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
pnpm exec spotpatch-vite bridge setup --client claude --scope project --mode active --write
|
|
129
|
+
MCP_PROTOCOL_NEGOTIATION=legacy claude --dangerously-load-development-channels server:spotpatch
|
|
130
|
+
|
|
131
|
+
pnpm exec spotpatch-vite connect codex --allow-workspace-write
|
|
132
|
+
|
|
133
|
+
pnpm exec spotpatch-vite bridge setup --client cursor --scope project --write
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The active command forms support an explicit session when `bridge sessions --json` reports multiple sessions for that exact root:
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
spotpatch-vite bridge channel claude [--session <opaque-id>]
|
|
140
|
+
spotpatch-vite connect codex --allow-workspace-write [--session <opaque-id>]
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The Claude legacy environment belongs on the Claude host process. Claude Channels are a Research Preview, work only while the Channel-enabled session is running, and provide no completion acknowledgement; completion depends on Claude calling the SpotPatch result tool. Codex active mode is zero-setup: the Connector injects the project-local SpotPatch MCP entry into its App Server thread and does not create or modify `.codex/config.toml`. Codex can still load other MCP servers already enabled by the user's normal Codex configuration. The Connector accepts exactly `codex-cli 0.149.0`. `bridge setup --client codex ...` remains available only for optional Inbox use; Cursor and all generic MCP hosts remain Inbox-only.
|
|
144
|
+
|
|
145
|
+
This path is `local-validation`, not stable support. Automated fake-host and two-handoff tests exist, and a consecutive two-revision Codex flow has been manually validated on the recorded macOS/Next.js/Codex 0.149.0 environment. Real Claude Code consecutive delivery, repeatable real-host automation, and Windows process-tree cleanup remain `not-tested`; Cursor remains Inbox-only. See the [external Agent design and exact status](https://github.com/huanglvjing/spotpatch/blob/main/docs/%E6%8A%80%E6%9C%AF%E6%96%B9%E6%A1%88/%E5%A4%96%E9%83%A8Agent%E8%BF%9E%E6%8E%A5/00-%E7%B4%A2%E5%BC%95%E4%B8%8E%E5%86%B3%E7%AD%96%E6%91%98%E8%A6%81.md).
|
|
146
|
+
|
|
111
147
|
### Optional AI Agent
|
|
112
148
|
|
|
113
149
|
AI remains disabled unless every required provider value is available. The smallest setup uses a Git-ignored `.env.local` file:
|
|
@@ -157,8 +193,8 @@ The page defaults to Review. Choosing Trusted direct uses the exact SpotPatch so
|
|
|
157
193
|
|
|
158
194
|
### Troubleshooting
|
|
159
195
|
|
|
160
|
-
- **
|
|
161
|
-
- **Initializer rejects the config:** use a configuration object or a callback with one unambiguous object return. For conditional returns or dynamic plugin arrays, configure `spotPatch()` manually before the React plugin.
|
|
196
|
+
- **pnpm 11 installs an older version for `@latest`:** its default 24-hour `minimumReleaseAge` policy selects the newest mature release. Use the recommended `npx ... setup`, specify a trusted exact version, or wait 24 hours.
|
|
197
|
+
- **Initializer rejects the config:** use a configuration object or a callback with one unambiguous object return. For conditional returns or dynamic plugin arrays, configure `spotPatch({ dataFlow: {} })` manually before the React plugin.
|
|
162
198
|
- **No selection button:** confirm `spotPatch()` appears before the React plugin and that the app is running through `vite`/`vite dev`, not `vite preview`.
|
|
163
199
|
- **No exact source location:** confirm the component is authored in an included `.jsx` or `.tsx` file under `src`, or configure `include` explicitly.
|
|
164
200
|
- **AI is unavailable:** provide all three required environment values or set `ai: false`; partial environment configuration fails closed.
|
|
@@ -182,26 +218,22 @@ SpotPatch 只在 Vite 开发服务器中运行。生产构建不包含 SpotPatch
|
|
|
182
218
|
### 一条命令接入(推荐)
|
|
183
219
|
|
|
184
220
|
```bash
|
|
185
|
-
# pnpm 项目
|
|
186
|
-
pnpm dlx @spotpatch/vite@latest setup
|
|
187
|
-
|
|
188
|
-
# npm 项目
|
|
189
221
|
npx --yes @spotpatch/vite@latest setup
|
|
190
222
|
```
|
|
191
223
|
|
|
192
|
-
|
|
224
|
+
这条由 npm 引导的命令同时支持 npm 和 pnpm 项目。它先取得 registry 真正的最新 CLI,再识别项目包管理器、安装该 CLI 对应的 SpotPatch 精确版本、安全更新受支持的 `vite.config.*` 并验证结果。精确版本交接可以避免 pnpm 11 默认 24 小时 `minimumReleaseAge` 把 `@latest` 解析成较旧的成熟版本。
|
|
225
|
+
|
|
226
|
+
初始化器支持配置对象和返回对象的 `defineConfig` 回调;有歧义的动态配置会在不写入配置文件的情况下失败。
|
|
193
227
|
|
|
194
|
-
|
|
228
|
+
npm 项目也可以分开安装和初始化:
|
|
195
229
|
|
|
196
230
|
```bash
|
|
197
231
|
npm install --save-dev @spotpatch/vite@latest
|
|
198
232
|
npx spotpatch-vite init
|
|
199
|
-
|
|
200
|
-
# 或
|
|
201
|
-
pnpm add -D @spotpatch/vite@latest
|
|
202
|
-
pnpm exec spotpatch-vite init
|
|
203
233
|
```
|
|
204
234
|
|
|
235
|
+
pnpm 11 项目请使用推荐的 setup 命令、安装已确认的精确版本,或等待发布满 24 小时。SpotPatch 不会全局关闭项目的供应链隔离策略。
|
|
236
|
+
|
|
205
237
|
初始化器会将 SpotPatch 放在 React 插件之前,确保源码标记在 React 转换前注入:
|
|
206
238
|
|
|
207
239
|
```ts
|
|
@@ -211,11 +243,11 @@ import react from "@vitejs/plugin-react-swc";
|
|
|
211
243
|
import { defineConfig } from "vite";
|
|
212
244
|
|
|
213
245
|
export default defineConfig({
|
|
214
|
-
plugins: [spotPatch({ trustedFastMode: true }), react()],
|
|
246
|
+
plugins: [spotPatch({ dataFlow: {}, trustedFastMode: true }), react()],
|
|
215
247
|
});
|
|
216
248
|
```
|
|
217
249
|
|
|
218
|
-
无法发现安全的本地 TypeScript 检查时,初始化器会生成 `spotPatch()` 并保持审阅模式。不受支持的动态配置仍可按相同插件顺序手动接入。
|
|
250
|
+
无法发现安全的本地 TypeScript 检查时,初始化器会生成 `spotPatch({ dataFlow: {} })` 并保持审阅模式。不受支持的动态配置仍可按相同插件顺序手动接入。
|
|
219
251
|
|
|
220
252
|
照常启动应用:
|
|
221
253
|
|
|
@@ -225,6 +257,12 @@ pnpm dev
|
|
|
225
257
|
|
|
226
258
|
点击右下角的 **选择元素** 或按下 `Mod+Shift+S`。SpotPatch 支持跨同一项目的多个页面采集目标,在页面跳转及工作台关闭/重开后保留目标与独立修改要求,在 Cursor 或 VS Code 中打开精确位置,并且在完全不配置 AI 的情况下生成结构化 Prompt。
|
|
227
259
|
|
|
260
|
+
### 组件数据链路(Beta)
|
|
261
|
+
|
|
262
|
+
新版 `setup/init` 会自动写入 `spotPatch({ dataFlow: {} })`,手工接入时使用同一选项。Vite + React 18 开发期的 **数据链路** 与 **页面接口** 页签会显示有证据的 method/path、参数键、源码消费字段、数据去向、实际 dispatch 请求和当前页面未归属流量;不采集 query 值或响应体。只有稳定组件、源码、callsite 与 invocation 证据一致时才建立关联,歧义流量保持 unknown/unassigned。当前不包含 data-flow AI、安全 JSON 响应读取或 Next.js 支持。准确范围见仓库中的 [实现状态文档](https://github.com/huanglvjing/spotpatch/blob/main/docs/%E6%8A%80%E6%9C%AF%E6%96%B9%E6%A1%88/%E7%BB%84%E4%BB%B6%E6%95%B0%E6%8D%AE%E9%93%BE%E8%B7%AF/13-Beta%E5%AE%9E%E7%8E%B0%E7%8A%B6%E6%80%81%E4%B8%8E%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C.md)。
|
|
263
|
+
|
|
264
|
+
已支持的适配器包括组件直接/Service `fetch`、Axios、React Query/TanStack Query 回调和实验性的 tRPC 逻辑 procedure。tRPC batch HTTP 传输保持为独立证据,绝不按时间或 URL 相似度强行归属。
|
|
265
|
+
|
|
228
266
|
### 兼容范围
|
|
229
267
|
|
|
230
268
|
| 依赖 | 正式支持范围 |
|
|
@@ -249,27 +287,61 @@ spotPatch({
|
|
|
249
287
|
locale: "auto",
|
|
250
288
|
maxTargets: 8,
|
|
251
289
|
ai: false,
|
|
290
|
+
dataFlow: false,
|
|
291
|
+
externalAgent: false,
|
|
252
292
|
});
|
|
253
293
|
```
|
|
254
294
|
|
|
255
|
-
| 选项 | 默认值 | 说明
|
|
256
|
-
| ----------------- | ---------------------------- |
|
|
257
|
-
| `enabled` | `true` | 启用开发期插件。
|
|
258
|
-
| `include` | `src` 下 JSX/TSX | 允许注入源码标记的文件。
|
|
259
|
-
| `exclude` | 依赖、测试、Story 与生成目录 | 不进行转换的文件。
|
|
260
|
-
| `editor` | `"auto"` | 自动识别 Cursor 或 VS Code,也可显式固定。
|
|
261
|
-
| `redact` | `true` | 清洗采集上下文;强制保护的秘密类型不会因关闭而暴露。
|
|
262
|
-
| `budget` | 有界默认值 | 限制总量、DOM、CSS 和源码上下文大小。
|
|
263
|
-
| `shortcut` | `"Mod+Shift+S"` | 切换元素选择器。
|
|
264
|
-
| `allowLan` | `false` | 默认只允许 loopback Host 与 Origin。
|
|
265
|
-
| `debug` | `false` | 输出不包含凭据的开发诊断。
|
|
266
|
-
| `locale` | `"auto"` | 自动解析 `en-US` 或 `zh-CN`。
|
|
267
|
-
| `maxTargets` | `8` | 一次修改任务默认允许的目标数。
|
|
268
|
-
| `ai` | 关闭或检测到完整环境配置 | 可选 Provider 和 Agent 配置。
|
|
269
|
-
| `
|
|
295
|
+
| 选项 | 默认值 | 说明 |
|
|
296
|
+
| ----------------- | ---------------------------- | ---------------------------------------------------------- |
|
|
297
|
+
| `enabled` | `true` | 启用开发期插件。 |
|
|
298
|
+
| `include` | `src` 下 JSX/TSX | 允许注入源码标记的文件。 |
|
|
299
|
+
| `exclude` | 依赖、测试、Story 与生成目录 | 不进行转换的文件。 |
|
|
300
|
+
| `editor` | `"auto"` | 自动识别 Cursor 或 VS Code,也可显式固定。 |
|
|
301
|
+
| `redact` | `true` | 清洗采集上下文;强制保护的秘密类型不会因关闭而暴露。 |
|
|
302
|
+
| `budget` | 有界默认值 | 限制总量、DOM、CSS 和源码上下文大小。 |
|
|
303
|
+
| `shortcut` | `"Mod+Shift+S"` | 切换元素选择器。 |
|
|
304
|
+
| `allowLan` | `false` | 默认只允许 loopback Host 与 Origin。 |
|
|
305
|
+
| `debug` | `false` | 输出不包含凭据的开发诊断。 |
|
|
306
|
+
| `locale` | `"auto"` | 自动解析 `en-US` 或 `zh-CN`。 |
|
|
307
|
+
| `maxTargets` | `8` | 一次修改任务默认允许的目标数。 |
|
|
308
|
+
| `ai` | 关闭或检测到完整环境配置 | 可选 Provider 和 Agent 配置。 |
|
|
309
|
+
| `dataFlow` | `false` | 可选 dispatch-only 组件数据链路 Beta。 |
|
|
310
|
+
| `externalAgent` | `false` | 可选、仅开发期的外部 Agent 交接;当前仅 local-validation。 |
|
|
311
|
+
| `trustedFastMode` | `false` | 开放审阅/可信极速选择;TypeScript 检查供审阅模式使用。 |
|
|
270
312
|
|
|
271
313
|
本包导出选项类型、AI Provider 类型、Agent 限制和不可变默认值。完整约束见[公共 API 规范](https://github.com/huanglvjing/spotpatch/blob/main/docs/%E6%8A%80%E6%9C%AF%E6%96%B9%E6%A1%88/03-%E5%85%AC%E5%85%B1API%E4%B8%8E%E6%95%B0%E6%8D%AE%E6%A8%A1%E5%9E%8B.md)。
|
|
272
314
|
|
|
315
|
+
### 外部 Agent 交接(本地验证)
|
|
316
|
+
|
|
317
|
+
显式启用仅开发期的 UI:
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
spotPatch({ externalAgent: true });
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
必须在当前 Vite dev Session 所属的精确 canonical 项目根执行 setup 和主动命令。setup 不带 `--write` 时只是 dry-run:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
pnpm exec spotpatch-vite bridge setup --client claude --scope project --mode active --write
|
|
327
|
+
MCP_PROTOCOL_NEGOTIATION=legacy claude --dangerously-load-development-channels server:spotpatch
|
|
328
|
+
|
|
329
|
+
pnpm exec spotpatch-vite connect codex --allow-workspace-write
|
|
330
|
+
|
|
331
|
+
pnpm exec spotpatch-vite bridge setup --client cursor --scope project --write
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
若 `bridge sessions --json` 报告该精确项目根有多个 Session,主动子命令支持显式选择:
|
|
335
|
+
|
|
336
|
+
```text
|
|
337
|
+
spotpatch-vite bridge channel claude [--session <opaque-id>]
|
|
338
|
+
spotpatch-vite connect codex --allow-workspace-write [--session <opaque-id>]
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Claude legacy 环境变量必须设置在 Claude 宿主进程。Claude Channels 仍是 Research Preview,只在已启用 Channel 的会话运行时工作,且没有 completion ACK;完成状态依赖 Claude 调用 SpotPatch 结果 tool。Codex 主动模式为零配置:Connector 只把当前项目的 SpotPatch MCP 配置注入它拥有的 App Server thread,不创建也不修改 `.codex/config.toml`;Codex 仍可能按用户既有配置启动其他已启用 MCP server。Connector 当前只接受 `codex-cli 0.149.0`。`bridge setup --client codex ...` 仅保留为可选 Inbox 配置;Cursor 和所有普通 MCP 宿主仍为 Inbox-only。
|
|
342
|
+
|
|
343
|
+
该链路当前只是 `local-validation`,不是稳定支持。仓库有假宿主和连续两 Handoff 自动化测试,并已在记录的 macOS/Next.js/Codex 0.149.0 环境人工验证连续两个 revision。真实 Claude Code 连续投递、可重复真实宿主自动化和 Windows 进程树清理仍为 `not-tested`;Cursor 保持 Inbox-only。准确边界见[外部 Agent 方案与实现状态](https://github.com/huanglvjing/spotpatch/blob/main/docs/%E6%8A%80%E6%9C%AF%E6%96%B9%E6%A1%88/%E5%A4%96%E9%83%A8Agent%E8%BF%9E%E6%8E%A5/00-%E7%B4%A2%E5%BC%95%E4%B8%8E%E5%86%B3%E7%AD%96%E6%91%98%E8%A6%81.md)。
|
|
344
|
+
|
|
273
345
|
### 可选 AI Agent
|
|
274
346
|
|
|
275
347
|
只有全部必需 Provider 值可用时 AI 才会启用。最小配置放在 Git 忽略的 `.env.local` 中:
|
|
@@ -319,8 +391,8 @@ spotPatch({ trustedFastMode: true });
|
|
|
319
391
|
|
|
320
392
|
### 常见问题
|
|
321
393
|
|
|
322
|
-
-
|
|
323
|
-
- **初始化器拒绝配置:**请使用配置对象,或只含一个明确对象返回的回调。存在条件返回或动态插件数组时,手动将 `spotPatch()` 放到 React 插件之前。
|
|
394
|
+
- **pnpm 11 对 `@latest` 安装了旧版本:**默认 24 小时 `minimumReleaseAge` 会选择最新的成熟版本。请使用推荐的 `npx ... setup`、安装已确认的精确版本,或等待 24 小时。
|
|
395
|
+
- **初始化器拒绝配置:**请使用配置对象,或只含一个明确对象返回的回调。存在条件返回或动态插件数组时,手动将 `spotPatch({ dataFlow: {} })` 放到 React 插件之前。
|
|
324
396
|
- **没有选择元素按钮:**确认 `spotPatch()` 位于 React 插件之前,并且应用通过 `vite`/`vite dev` 而不是 `vite preview` 启动。
|
|
325
397
|
- **没有精确源码位置:**确认组件来自 include 范围内的 `.jsx` 或 `.tsx` 文件,默认范围是 `src`。
|
|
326
398
|
- **AI 不可用:**提供全部三个必需环境变量,或者显式设置 `ai: false`;不完整配置会安全失败。
|
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { readFile as readFile2 } from "fs/promises";
|
|
5
5
|
import { createRequire } from "module";
|
|
6
6
|
import path3 from "path";
|
|
7
|
+
import { runSpotPatchBridgeCli } from "@spotpatch/bridge";
|
|
7
8
|
|
|
8
9
|
// src/initializer.ts
|
|
9
10
|
import path from "path";
|
|
@@ -68,6 +69,16 @@ function insertStaticImport(magicString, program, statement) {
|
|
|
68
69
|
magicString.appendRight(offset, `
|
|
69
70
|
${statement}`);
|
|
70
71
|
}
|
|
72
|
+
function importQuote(source, program) {
|
|
73
|
+
const firstImport = importsOf(program)[0];
|
|
74
|
+
if (firstImport !== void 0) {
|
|
75
|
+
const quote = source[firstImport.source.start];
|
|
76
|
+
if (quote === '"' || quote === "'") {
|
|
77
|
+
return quote;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return '"';
|
|
81
|
+
}
|
|
71
82
|
function collectIdentifierNames(program) {
|
|
72
83
|
const names = /* @__PURE__ */ new Set();
|
|
73
84
|
new Visitor({
|
|
@@ -249,8 +260,12 @@ function childIndent(source, object) {
|
|
|
249
260
|
}
|
|
250
261
|
return `${lineIndentAt(source, object.start)} `;
|
|
251
262
|
}
|
|
252
|
-
function
|
|
253
|
-
|
|
263
|
+
function initializedPluginCall(pluginName, trustedFastMode) {
|
|
264
|
+
const options = [
|
|
265
|
+
"dataFlow: {}",
|
|
266
|
+
...trustedFastMode ? ["trustedFastMode: true"] : []
|
|
267
|
+
];
|
|
268
|
+
return `${pluginName}({ ${options.join(", ")} })`;
|
|
254
269
|
}
|
|
255
270
|
function directPluginCalls(plugins, pluginName) {
|
|
256
271
|
return plugins.elements.filter((element) => {
|
|
@@ -261,12 +276,12 @@ function directPluginCalls(plugins, pluginName) {
|
|
|
261
276
|
return callee.type === "Identifier" && callee.name === pluginName;
|
|
262
277
|
});
|
|
263
278
|
}
|
|
264
|
-
function
|
|
279
|
+
function enableInitializedOptions(magicString, source, call, trustedFastMode) {
|
|
265
280
|
if (call.arguments.length === 0) {
|
|
266
281
|
magicString.overwrite(
|
|
267
282
|
call.start,
|
|
268
283
|
call.end,
|
|
269
|
-
|
|
284
|
+
initializedPluginCall(source.slice(call.start, call.callee.end), trustedFastMode)
|
|
270
285
|
);
|
|
271
286
|
return;
|
|
272
287
|
}
|
|
@@ -278,33 +293,55 @@ function enableTrustedFastMode(magicString, source, call) {
|
|
|
278
293
|
if (value.type !== "ObjectExpression") {
|
|
279
294
|
throw new Error("SpotPatch init requires spotPatch options to be an object.");
|
|
280
295
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
296
|
+
if (value.properties.some((property) => property.type === "SpreadElement")) {
|
|
297
|
+
throw new Error(
|
|
298
|
+
"SpotPatch init cannot prove dataFlow through spread spotPatch options."
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
const dataFlowProperty = findProperty(value, "dataFlow");
|
|
302
|
+
const trustedFastModeProperty = findProperty(value, "trustedFastMode");
|
|
303
|
+
const missingProperties = [];
|
|
304
|
+
if (dataFlowProperty === void 0) {
|
|
305
|
+
missingProperties.push("dataFlow: {}");
|
|
306
|
+
} else {
|
|
307
|
+
const dataFlowValue = unwrapExpression(dataFlowProperty.value);
|
|
308
|
+
if (dataFlowValue.type === "Literal" && dataFlowValue.value === false) {
|
|
309
|
+
magicString.overwrite(dataFlowValue.start, dataFlowValue.end, "{}");
|
|
310
|
+
} else if (dataFlowValue.type !== "ObjectExpression") {
|
|
311
|
+
throw new Error(
|
|
312
|
+
"SpotPatch init requires dataFlow to be false or an options object."
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (trustedFastMode && trustedFastModeProperty === void 0) {
|
|
317
|
+
missingProperties.push("trustedFastMode: true");
|
|
318
|
+
} else if (trustedFastModeProperty !== void 0) {
|
|
319
|
+
const propertyValue = unwrapExpression(trustedFastModeProperty.value);
|
|
284
320
|
if (propertyValue.type !== "Literal" || typeof propertyValue.value !== "boolean") {
|
|
285
321
|
throw new Error(
|
|
286
322
|
"SpotPatch init requires trustedFastMode to be a boolean literal."
|
|
287
323
|
);
|
|
288
324
|
}
|
|
289
|
-
if (!propertyValue.value) {
|
|
325
|
+
if (trustedFastMode && !propertyValue.value) {
|
|
290
326
|
magicString.overwrite(propertyValue.start, propertyValue.end, "true");
|
|
291
327
|
}
|
|
292
|
-
return;
|
|
293
328
|
}
|
|
329
|
+
if (missingProperties.length === 0) return;
|
|
294
330
|
const indent = childIndent(source, value);
|
|
295
331
|
if (value.properties.length === 0) {
|
|
296
|
-
magicString.appendLeft(value.end - 1, "
|
|
332
|
+
magicString.appendLeft(value.end - 1, ` ${missingProperties.join(", ")} `);
|
|
297
333
|
} else {
|
|
298
334
|
magicString.appendLeft(
|
|
299
335
|
value.properties[0]?.start ?? value.end - 1,
|
|
300
|
-
|
|
336
|
+
`${missingProperties.join(`,
|
|
337
|
+
${indent}`)},
|
|
301
338
|
${indent}`
|
|
302
339
|
);
|
|
303
340
|
}
|
|
304
341
|
}
|
|
305
342
|
function addPluginCall(magicString, source, config, pluginName, trustedFastModeAvailable) {
|
|
306
343
|
const pluginsProperty = findProperty(config, "plugins");
|
|
307
|
-
const call =
|
|
344
|
+
const call = initializedPluginCall(pluginName, trustedFastModeAvailable);
|
|
308
345
|
if (pluginsProperty === void 0) {
|
|
309
346
|
const indent = childIndent(source, config);
|
|
310
347
|
if (config.properties.length === 0) {
|
|
@@ -329,14 +366,19 @@ ${indent}`
|
|
|
329
366
|
throw new Error("SpotPatch init found duplicate spotPatch plugins.");
|
|
330
367
|
}
|
|
331
368
|
if (existing[0] !== void 0) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
369
|
+
enableInitializedOptions(
|
|
370
|
+
magicString,
|
|
371
|
+
source,
|
|
372
|
+
existing[0],
|
|
373
|
+
trustedFastModeAvailable
|
|
374
|
+
);
|
|
335
375
|
return;
|
|
336
376
|
}
|
|
337
377
|
const first = value.elements.find((element) => element !== null);
|
|
338
378
|
if (first === void 0) {
|
|
339
379
|
magicString.appendLeft(value.end - 1, call);
|
|
380
|
+
} else if (!source.slice(value.start, first.start).includes("\n")) {
|
|
381
|
+
magicString.appendLeft(first.start, `${call}, `);
|
|
340
382
|
} else {
|
|
341
383
|
magicString.appendLeft(
|
|
342
384
|
first.start,
|
|
@@ -353,10 +395,11 @@ function transformViteConfig(absolutePath, source, trustedFastModeAvailable) {
|
|
|
353
395
|
const magicString = new MagicString(source);
|
|
354
396
|
if (existingPluginName === void 0) {
|
|
355
397
|
const specifier = pluginName === "spotPatch" ? "spotPatch" : `spotPatch as ${pluginName}`;
|
|
398
|
+
const quote = importQuote(source, program);
|
|
356
399
|
insertStaticImport(
|
|
357
400
|
magicString,
|
|
358
401
|
program,
|
|
359
|
-
`import { ${specifier} } from ${
|
|
402
|
+
`import { ${specifier} } from ${quote}${ADAPTER_PACKAGE_NAME}${quote};`
|
|
360
403
|
);
|
|
361
404
|
}
|
|
362
405
|
addPluginCall(magicString, source, config, pluginName, trustedFastModeAvailable);
|
|
@@ -433,6 +476,7 @@ async function checkViteIntegration(directory = process.cwd()) {
|
|
|
433
476
|
import { spawn } from "child_process";
|
|
434
477
|
import { access, readFile } from "fs/promises";
|
|
435
478
|
import path2 from "path";
|
|
479
|
+
var VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u;
|
|
436
480
|
async function pathExists(absolutePath) {
|
|
437
481
|
try {
|
|
438
482
|
await access(absolutePath);
|
|
@@ -481,18 +525,33 @@ async function detectPackageManager(appRoot = process.cwd(), userAgent = process
|
|
|
481
525
|
"SpotPatch setup could not determine npm or pnpm; run installation and init separately."
|
|
482
526
|
);
|
|
483
527
|
}
|
|
484
|
-
function createInstallCommand(packageManager, platform = process.platform) {
|
|
528
|
+
function createInstallCommand(packageManager, version, platform = process.platform) {
|
|
529
|
+
if (!VERSION_PATTERN.test(version)) {
|
|
530
|
+
throw new Error("SpotPatch setup could not determine its package version.");
|
|
531
|
+
}
|
|
532
|
+
const packageSpecifier = `@spotpatch/vite@${version}`;
|
|
485
533
|
return Object.freeze({
|
|
486
534
|
executable: platform === "win32" ? `${packageManager}.cmd` : packageManager,
|
|
487
535
|
arguments: Object.freeze(
|
|
488
|
-
packageManager === "pnpm" ? ["add", "-D",
|
|
536
|
+
packageManager === "pnpm" ? ["add", "-D", packageSpecifier] : ["install", "--save-dev", packageSpecifier]
|
|
489
537
|
)
|
|
490
538
|
});
|
|
491
539
|
}
|
|
492
|
-
async function
|
|
493
|
-
const
|
|
540
|
+
async function readCurrentAdapterVersion() {
|
|
541
|
+
const manifest = JSON.parse(
|
|
542
|
+
await readFile(new URL("../package.json", import.meta.url), "utf8")
|
|
543
|
+
);
|
|
544
|
+
const version = manifest.version;
|
|
545
|
+
if (typeof version !== "string" || !VERSION_PATTERN.test(version)) {
|
|
546
|
+
throw new Error("SpotPatch setup could not determine its package version.");
|
|
547
|
+
}
|
|
548
|
+
return version;
|
|
549
|
+
}
|
|
550
|
+
async function installCurrentAdapter(packageManager, appRoot = process.cwd()) {
|
|
551
|
+
const version = await readCurrentAdapterVersion();
|
|
552
|
+
const command = createInstallCommand(packageManager, version);
|
|
494
553
|
process.stdout.write(
|
|
495
|
-
`[spotpatch:vite] installing @spotpatch/vite
|
|
554
|
+
`[spotpatch:vite] installing @spotpatch/vite@${version} with ${packageManager}...
|
|
496
555
|
`
|
|
497
556
|
);
|
|
498
557
|
await new Promise((resolve, reject) => {
|
|
@@ -518,10 +577,10 @@ async function installLatestAdapter(packageManager, appRoot = process.cwd()) {
|
|
|
518
577
|
}
|
|
519
578
|
|
|
520
579
|
// src/cli.ts
|
|
521
|
-
var
|
|
580
|
+
var VERSION_PATTERN2 = /^(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z.-]+)?$/u;
|
|
522
581
|
function writeUsage() {
|
|
523
582
|
process.stderr.write(
|
|
524
|
-
"Usage: spotpatch-vite <setup|init|check>\n setup Install
|
|
583
|
+
"Usage: spotpatch-vite <setup|init|check|connect|bridge>\n setup Install this CLI's exact @spotpatch/vite version, then initialize it.\n init Preview and apply safe Vite integration changes.\n check Verify the Vite integration without writing files.\n connect codex Start the zero-setup Codex Agent connector.\n bridge Run the local external-Agent MCP, CLI, or setup commands.\n"
|
|
525
584
|
);
|
|
526
585
|
}
|
|
527
586
|
async function inspectViteProject(appRoot = process.cwd()) {
|
|
@@ -542,7 +601,7 @@ async function inspectViteProject(appRoot = process.cwd()) {
|
|
|
542
601
|
}
|
|
543
602
|
const manifest = JSON.parse(await readFile2(viteManifestPath, "utf8"));
|
|
544
603
|
const version = typeof manifest === "object" && manifest !== null && "version" in manifest && typeof manifest.version === "string" ? manifest.version : void 0;
|
|
545
|
-
const match = version === void 0 ? null :
|
|
604
|
+
const match = version === void 0 ? null : VERSION_PATTERN2.exec(version);
|
|
546
605
|
const major = Number(match?.[1]);
|
|
547
606
|
if (version === void 0 || match === null || !Number.isSafeInteger(major) || major < 5 || major >= 8) {
|
|
548
607
|
throw new Error(
|
|
@@ -589,7 +648,7 @@ async function runSetup(arguments_) {
|
|
|
589
648
|
throw new Error("SpotPatch setup does not accept positional arguments.");
|
|
590
649
|
}
|
|
591
650
|
const packageManager = await detectPackageManager();
|
|
592
|
-
await
|
|
651
|
+
await installCurrentAdapter(packageManager);
|
|
593
652
|
return runInit([]);
|
|
594
653
|
}
|
|
595
654
|
async function runCheck(arguments_) {
|
|
@@ -623,6 +682,12 @@ async function main(arguments_) {
|
|
|
623
682
|
if (command === "check") {
|
|
624
683
|
return runCheck(rest);
|
|
625
684
|
}
|
|
685
|
+
if (command === "bridge") {
|
|
686
|
+
return runSpotPatchBridgeCli(rest, { adapter: "vite" });
|
|
687
|
+
}
|
|
688
|
+
if (command === "connect") {
|
|
689
|
+
return runSpotPatchBridgeCli(arguments_, { adapter: "vite" });
|
|
690
|
+
}
|
|
626
691
|
writeUsage();
|
|
627
692
|
return 1;
|
|
628
693
|
}
|