@reticlehq/vite-plugin 2.6.0 → 2.8.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 CHANGED
@@ -2,39 +2,57 @@
2
2
 
3
3
  One-line Vite integration for [Reticle](https://github.com/reticlehq/reticle). The plugin does the whole dev-time wiring for you:
4
4
 
5
- - **Source mapping** stamps `data-reticle-source="file:line:col"` on JSX host elements (via [`@reticlehq/babel-plugin`](https://www.npmjs.com/package/@reticlehq/babel-plugin)) so `reticle_inspect` can report the component's source file needed on React 19.
6
- - **Auto-connect** injects a dev-only `install(); reticle.connect()` so you don't touch your entry file.
7
- - **Production-safe by construction** `apply: 'serve'` means Vite drops the plugin entirely from `vite build`. There is no env gate to forget; instrumentation cannot reach a production bundle.
5
+ - **Source mapping**: stamps `data-reticle-source="file:line:col"` on JSX host elements (via [`@reticlehq/babel-plugin`](https://www.npmjs.com/package/@reticlehq/babel-plugin)) so `reticle_inspect` can report the component's source file. Needed on React 19.
6
+ - **Auto-connect**: injects a dev-only `install(); reticle.connect()` into your entry module, so you never touch the entry file yourself.
7
+ - **Svelte source mapping**: the same stamp on Svelte markup, applied before `@sveltejs/vite-plugin-svelte` compiles it.
8
+ - **Dependency pre-bundling**: declares the SDK's CJS runtime deps in `optimizeDeps` so the SDK loads on linked and monorepo setups.
9
+ - **Production-safe by construction**: `apply: 'serve'` means Vite drops the plugin entirely from `vite build`. There is no env gate to forget; instrumentation cannot reach a production web bundle.
8
10
 
9
- Usually installed via the umbrella package and imported from its `/vite` subpath:
11
+ ## Install
10
12
 
11
13
  ```bash
12
- npm i -D @reticlehq/core
14
+ npm i -D @reticlehq/react @reticlehq/vite-plugin
13
15
  ```
14
16
 
17
+ `@reticlehq/react` is the runtime kit the injected `connect()` imports (it re-exports the browser SDK). `vite >= 4` is a peer dependency.
18
+
19
+ ## Use
20
+
15
21
  ```ts
16
22
  // vite.config.ts
17
23
  import { defineConfig } from 'vite';
18
24
  import react from '@vitejs/plugin-react';
19
- import { reticle } from '@reticlehq/core/vite';
25
+ import { reticle } from '@reticlehq/vite-plugin';
20
26
 
21
27
  export default defineConfig({
22
- plugins: [react(), reticle()],
28
+ plugins: [reticle(), react()],
23
29
  });
24
30
  ```
25
31
 
26
- That is the entire integration no entry-file edit, no Babel-plugin wiring, no env gating. `npx @reticlehq/core init` adds this line for you automatically in a Vite project.
32
+ That is the entire integration: no entry-file edit, no Babel-plugin wiring, no env gating. `npx @reticlehq/server init` writes this line for you in a Vite project, inserting `reticle()` right after the opening `[`, which is why that is the order shown here.
33
+
34
+ Array order does not actually matter: the plugin declares `enforce: 'pre'`, so Vite runs it before `@vitejs/plugin-react` wherever you put it.
27
35
 
28
36
  ## Options
29
37
 
30
38
  ```ts
31
39
  reticle({
32
40
  port, // bridge WebSocket port; baked into connect() only when non-default
33
- session, // stable session label (defaults to the SDK's auto id)
41
+ session, // stable session label; defaults to a fresh per-tab id
42
+ projectId, // stable project identity; defaults to one derived from package.json name + root
34
43
  token, // auth token forwarded to connect() when the bridge requires one
35
- sourceMapping, // default true stamp data-reticle-source (harmless on React <=18)
36
- inject, // default true auto-inject reticle.connect()
44
+ root, // project root, so reported source paths are repo-relative
45
+ sdkVersion, // installed SDK version, so a skewed pair can name itself
46
+ sourceMapping, // default true; stamp data-reticle-source (harmless on React <=18)
47
+ inject, // default true; auto-inject reticle.connect()
48
+ captureNetworkBodies, // default false; record request/response bodies on reticle_network
49
+ desktop, // default false; also apply to `vite build`, for an Electron/Tauri renderer
50
+ onWarn, // where a diagnostic goes; defaults to the console
37
51
  });
38
52
  ```
39
53
 
40
- MIT.
54
+ `captureNetworkBodies` is off by default because a body is the one part of a request that routinely carries a card number, a token, or a customer's address. It is also settable as `VITE_RETICLE_CAPTURE_BODIES=1` for a single debugging session.
55
+
56
+ `desktop: true` makes the plugin apply to `vite build` as well and calls `connect()` with `allowInProduction`, because a packaged desktop renderer is a production build with no dev server. That means an instrumented production bundle, which a web app must never ship. Keep it behind your own dev-only build target.
57
+
58
+ Apache-2.0.
package/dist/index.d.ts CHANGED
@@ -136,13 +136,13 @@ export interface ReticleVitePlugin {
136
136
  */
137
137
  export interface ViteDevServerLike {
138
138
  middlewares: {
139
- use: (handler: (req: {
140
- url?: string;
141
- }, res: unknown, next: () => void) => void) => void;
139
+ use(handler: (req: {
140
+ url?: string | undefined;
141
+ }, res: unknown, next: () => void) => void): void;
142
142
  };
143
143
  moduleGraph: {
144
- getModuleById: (id: string) => object | undefined;
145
- invalidateModule: (mod: object) => void;
144
+ getModuleById(id: string): object | undefined;
145
+ invalidateModule(mod: object): void;
146
146
  };
147
147
  }
148
148
  interface HtmlTag {
@@ -190,6 +190,27 @@ export declare function readPairingToken(): string | undefined;
190
190
  export declare const RETICLE_DEV_MODULE_CANDIDATES: readonly ["src/reticle-dev.ts", "src/reticle-dev.js", "src/reticle-dev.tsx", "src/reticle-dev.jsx"];
191
191
  /** The app's dev module, as an importable path — or null when the app has none. */
192
192
  export declare function findDevModule(root: string, exists: (p: string) => boolean): string | null;
193
+ /**
194
+ * Which SDK package this app actually has, and whether `install()` applies.
195
+ *
196
+ * The injected connect used to name `@reticlehq/react` unconditionally. That is right for a React
197
+ * app and fatal for any other: `reticle init` gives a Vue or Svelte codebase the framework-neutral
198
+ * `@reticlehq/browser` — deliberately, because a package named `@reticlehq/react` with `react` in
199
+ * its peers has no business in a Vue app — and the injected import then names a package that is not
200
+ * installed, so nothing connects and the page reports no session with no obvious cause.
201
+ *
202
+ * Measured end to end on a pristine `npm create vite --template vue` app: init wrote every file
203
+ * correctly and the tab never dialled the daemon, because of this one specifier.
204
+ *
205
+ * The React kit WINS when both resolve: it is a superset (it re-exports the sensor and adds the
206
+ * adapter), so an app that has it wants component identity. `install()` is the adapter's alone and
207
+ * the sensor does not export it — naming it against the sensor would trade a missing module for a
208
+ * missing export.
209
+ */
210
+ export declare function installedSdk(appRoot: string, canResolve?: (dep: string) => boolean): {
211
+ specifier: string;
212
+ usesInstall: boolean;
213
+ };
193
214
  export declare function connectModuleSource(options: ReticleVitePluginOptions, devModule?: string | null): string;
194
215
  /**
195
216
  * Reticle Vite plugin. Add to your `plugins` array and the entire integration is done:
package/dist/index.js CHANGED
@@ -15,6 +15,8 @@ export const RETICLE_VITE_PLUGIN_NAME = 'reticle';
15
15
  // specifier yields both `reticle` (connect) and `install` (the React adapter). NOT `@reticlehq/core`
16
16
  // — that is the isomorphic foundation and exports neither.
17
17
  const RETICLE_PACKAGE = '@reticlehq/react';
18
+ /** The framework-neutral sensor, which a Vue or Svelte app gets instead. See installedSdk. */
19
+ const RETICLE_SENSOR = '@reticlehq/browser';
18
20
  /**
19
21
  * Compile-time global carrying the daemon's pairing token, for connects the plugin does not write
20
22
  * itself. The bridge requires the token even on localhost, and nothing in a browser can read the
@@ -245,9 +247,38 @@ export function findDevModule(root, exists) {
245
247
  }
246
248
  return null;
247
249
  }
250
+ /**
251
+ * Which SDK package this app actually has, and whether `install()` applies.
252
+ *
253
+ * The injected connect used to name `@reticlehq/react` unconditionally. That is right for a React
254
+ * app and fatal for any other: `reticle init` gives a Vue or Svelte codebase the framework-neutral
255
+ * `@reticlehq/browser` — deliberately, because a package named `@reticlehq/react` with `react` in
256
+ * its peers has no business in a Vue app — and the injected import then names a package that is not
257
+ * installed, so nothing connects and the page reports no session with no obvious cause.
258
+ *
259
+ * Measured end to end on a pristine `npm create vite --template vue` app: init wrote every file
260
+ * correctly and the tab never dialled the daemon, because of this one specifier.
261
+ *
262
+ * The React kit WINS when both resolve: it is a superset (it re-exports the sensor and adds the
263
+ * adapter), so an app that has it wants component identity. `install()` is the adapter's alone and
264
+ * the sensor does not export it — naming it against the sensor would trade a missing module for a
265
+ * missing export.
266
+ */
267
+ export function installedSdk(appRoot, canResolve = (dep) => null !== resolvableChain([dep], appRoot)) {
268
+ if (canResolve(RETICLE_PACKAGE))
269
+ return { specifier: RETICLE_PACKAGE, usesInstall: true };
270
+ if (canResolve(RETICLE_SENSOR))
271
+ return { specifier: RETICLE_SENSOR, usesInstall: false };
272
+ // Neither resolves: keep the historical name so the failure reads as "the SDK is not installed"
273
+ // rather than as a package nobody recognises.
274
+ return { specifier: RETICLE_PACKAGE, usesInstall: true };
275
+ }
248
276
  export function connectModuleSource(options, devModule = null) {
249
277
  const args = connectArgs(options);
250
- const base = `import { reticle, install } from '${RETICLE_PACKAGE}';\ninstall();\nreticle.connect(${args});\n`;
278
+ const sdk = installedSdk(options.root ?? process.cwd());
279
+ const named = sdk.usesInstall ? 'reticle, install' : 'reticle';
280
+ const call = sdk.usesInstall ? 'install();\n' : '';
281
+ const base = `import { ${named} } from '${sdk.specifier}';\n${call}reticle.connect(${args});\n`;
251
282
  // AFTER connect: registerStore subscribes through the live SDK, and registering before there is a
252
283
  // session to report into drops the first diffs.
253
284
  return null === devModule ? base : `${base}import('${devModule}');\n`;
@@ -406,7 +437,11 @@ export function reticle(options = {}) {
406
437
  // no WebSocket, no session, no console message. The FIRST load after `reticle init` —
407
438
  // the one the whole product is judged on — silently did nothing, and it worked on the
408
439
  // next refresh, which is the worst possible shape for a bug like this.
409
- RETICLE_PACKAGE,
440
+ //
441
+ // Whichever SDK this app actually has: naming `@reticlehq/react` in a Vue app that was
442
+ // given the sensor produces the exact boot warning the note below is about, for a
443
+ // package that is correctly absent.
444
+ installedSdk(appRoot).specifier,
410
445
  // Only in a form that resolves — see above; a name Vite cannot resolve produces a boot
411
446
  // warning that blames Reticle, and a forced re-optimization on every cold start.
412
447
  ...cjsDepIncludes(appRoot),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reticlehq/vite-plugin",
3
- "version": "2.6.0",
3
+ "version": "2.8.0",
4
4
  "description": "Vite plugin for Reticle: dev-only source-map stamping plus auto-injected reticle.connect(). apply:'serve' guarantees it never ships to production.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -35,8 +35,8 @@
35
35
  ],
36
36
  "dependencies": {
37
37
  "@babel/core": "^7.26.0",
38
- "@reticlehq/babel-plugin": "2.6.0",
39
- "@reticlehq/core": "2.6.0"
38
+ "@reticlehq/babel-plugin": "2.8.0",
39
+ "@reticlehq/core": "2.8.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/babel__core": "^7.20.5",