@skyvexsoftware/stratos-sdk 0.2.2 → 0.3.1
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.
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin that fixes cross-origin HMR for plugins loaded inside Stratos.
|
|
3
|
+
*
|
|
4
|
+
* Three problems when loading plugins via cross-origin import():
|
|
5
|
+
*
|
|
6
|
+
* 1. Absolute-path imports (/@vite/client, /@react-refresh) resolve to the
|
|
7
|
+
* PAGE origin (the shell), not the plugin's dev server. This includes
|
|
8
|
+
* imports injected by Vite's importAnalysis (which runs AFTER all user
|
|
9
|
+
* transform plugins).
|
|
10
|
+
*
|
|
11
|
+
* 2. The HMR client's __BASE__ defaults to "/" so module re-fetches during
|
|
12
|
+
* HMR go to the shell's origin.
|
|
13
|
+
*
|
|
14
|
+
* 3. @vitejs/plugin-react's preamble (which initializes React Fast Refresh)
|
|
15
|
+
* is injected via transformIndexHtml — but plugins don't have HTML files,
|
|
16
|
+
* so the preamble never runs. Without it, Fast Refresh silently fails.
|
|
17
|
+
*
|
|
18
|
+
* This plugin fixes all three using two hooks:
|
|
19
|
+
* - transform (enforce: post): patches __BASE__, rewrites imports our
|
|
20
|
+
* transform can see, and injects the React Refresh preamble
|
|
21
|
+
* - configureServer middleware: intercepts FINAL module responses to rewrite
|
|
22
|
+
* /@vite/client imports added by importAnalysis (which runs after all
|
|
23
|
+
* user transforms)
|
|
24
|
+
*/
|
|
25
|
+
import type { Plugin } from "vite";
|
|
26
|
+
export declare function hmrBaseRewrite(): Plugin;
|
|
27
|
+
//# sourceMappingURL=hmr-base-rewrite.d.ts.map
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin that fixes cross-origin HMR for plugins loaded inside Stratos.
|
|
3
|
+
*
|
|
4
|
+
* Three problems when loading plugins via cross-origin import():
|
|
5
|
+
*
|
|
6
|
+
* 1. Absolute-path imports (/@vite/client, /@react-refresh) resolve to the
|
|
7
|
+
* PAGE origin (the shell), not the plugin's dev server. This includes
|
|
8
|
+
* imports injected by Vite's importAnalysis (which runs AFTER all user
|
|
9
|
+
* transform plugins).
|
|
10
|
+
*
|
|
11
|
+
* 2. The HMR client's __BASE__ defaults to "/" so module re-fetches during
|
|
12
|
+
* HMR go to the shell's origin.
|
|
13
|
+
*
|
|
14
|
+
* 3. @vitejs/plugin-react's preamble (which initializes React Fast Refresh)
|
|
15
|
+
* is injected via transformIndexHtml — but plugins don't have HTML files,
|
|
16
|
+
* so the preamble never runs. Without it, Fast Refresh silently fails.
|
|
17
|
+
*
|
|
18
|
+
* This plugin fixes all three using two hooks:
|
|
19
|
+
* - transform (enforce: post): patches __BASE__, rewrites imports our
|
|
20
|
+
* transform can see, and injects the React Refresh preamble
|
|
21
|
+
* - configureServer middleware: intercepts FINAL module responses to rewrite
|
|
22
|
+
* /@vite/client imports added by importAnalysis (which runs after all
|
|
23
|
+
* user transforms)
|
|
24
|
+
*/
|
|
25
|
+
export function hmrBaseRewrite() {
|
|
26
|
+
let devOrigin = "";
|
|
27
|
+
return {
|
|
28
|
+
name: "stratos-hmr-base-rewrite",
|
|
29
|
+
apply: "serve",
|
|
30
|
+
enforce: "post",
|
|
31
|
+
configureServer(server) {
|
|
32
|
+
server.httpServer?.once("listening", () => {
|
|
33
|
+
const addr = server.httpServer?.address();
|
|
34
|
+
if (addr && typeof addr === "object") {
|
|
35
|
+
const host = addr.address === "::" ||
|
|
36
|
+
addr.address === "::1" ||
|
|
37
|
+
addr.address === "0.0.0.0" ||
|
|
38
|
+
addr.address === "127.0.0.1"
|
|
39
|
+
? "localhost"
|
|
40
|
+
: addr.address;
|
|
41
|
+
devOrigin = `http://${host}:${addr.port}`;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
// Intercept module responses AFTER Vite's transformMiddleware
|
|
45
|
+
// (which includes importAnalysis) to rewrite /@vite/client imports
|
|
46
|
+
// that were injected post-transform. We wrap res.end() on every
|
|
47
|
+
// request so we can modify the final response body.
|
|
48
|
+
// Prevent Electron from caching /@vite/client across page loads.
|
|
49
|
+
// Stale cached modules break HMR because the __BASE__ and __WS_TOKEN__
|
|
50
|
+
// injections from the current server instance aren't in the cache.
|
|
51
|
+
server.middlewares.use((req, res, next) => {
|
|
52
|
+
if (req.url?.includes("@vite/client") ||
|
|
53
|
+
req.url?.includes("@react-refresh")) {
|
|
54
|
+
res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
|
|
55
|
+
}
|
|
56
|
+
next();
|
|
57
|
+
});
|
|
58
|
+
// Rewrite /@vite/client imports in module responses. Vite's
|
|
59
|
+
// importAnalysis injects these AFTER all user transform plugins,
|
|
60
|
+
// so we intercept the final response via res.end().
|
|
61
|
+
server.middlewares.use((_req, res, next) => {
|
|
62
|
+
if (!devOrigin)
|
|
63
|
+
return next();
|
|
64
|
+
const originalEnd = res.end.bind(res);
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
66
|
+
res.end = function (chunk, encoding, cb) {
|
|
67
|
+
if (typeof chunk === "string" && chunk.includes("/@vite/client")) {
|
|
68
|
+
chunk = chunk.replace(/(?<=['"])(\/@vite\/client)(?=['"])/g, `${devOrigin}/@vite/client`);
|
|
69
|
+
}
|
|
70
|
+
return originalEnd(chunk, encoding, cb);
|
|
71
|
+
};
|
|
72
|
+
next();
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
transform(code, id) {
|
|
76
|
+
if (!devOrigin)
|
|
77
|
+
return null;
|
|
78
|
+
// 1. Patch Vite's HMR client: replace __BASE__ with full origin
|
|
79
|
+
if (id.includes("vite/dist/client/client.mjs")) {
|
|
80
|
+
const patched = code.replace(/__BASE__/g, `"${devOrigin}/"`);
|
|
81
|
+
if (patched !== code) {
|
|
82
|
+
return { code: patched, map: null };
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
// Skip node_modules
|
|
87
|
+
if (id.includes("/node_modules/"))
|
|
88
|
+
return null;
|
|
89
|
+
let patched = code;
|
|
90
|
+
// 2. Rewrite /@vite/* and /@react-refresh imports to full URLs
|
|
91
|
+
// (catches imports from @vitejs/plugin-react and CSS transforms)
|
|
92
|
+
patched = patched.replace(/(from\s+["'])(\/(?:@vite\/[^"']+|@react-refresh))(["'])/g, `$1${devOrigin}$2$3`);
|
|
93
|
+
patched = patched.replace(/(import\s*\(\s*["'])(\/(?:@vite\/[^"']+|@react-refresh))(["'])/g, `$1${devOrigin}$2$3`);
|
|
94
|
+
// 3. Inject React Fast Refresh preamble into the entry file.
|
|
95
|
+
if (id.endsWith("/src/ui/index.tsx") || id.endsWith("/src/ui/index.ts")) {
|
|
96
|
+
const preamble = [
|
|
97
|
+
`import __StratosRefresh__ from "${devOrigin}/@react-refresh";`,
|
|
98
|
+
`__StratosRefresh__.injectIntoGlobalHook(window);`,
|
|
99
|
+
`window.$RefreshReg$ = () => {};`,
|
|
100
|
+
`window.$RefreshSig$ = () => (type) => type;`,
|
|
101
|
+
`window.__vite_plugin_react_preamble_installed__ = true;`,
|
|
102
|
+
].join("\n");
|
|
103
|
+
patched = preamble + "\n" + patched;
|
|
104
|
+
}
|
|
105
|
+
if (patched !== code) {
|
|
106
|
+
return { code: patched, map: null };
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=hmr-base-rewrite.js.map
|
|
@@ -22,6 +22,7 @@ import { UI_EXTERNALS } from "./externals.js";
|
|
|
22
22
|
import { serveExternals } from "./serve-externals.js";
|
|
23
23
|
import { stratosDevServer } from "./stratos-dev-server.js";
|
|
24
24
|
import { cssInject } from "./css-inject.js";
|
|
25
|
+
import { hmrBaseRewrite } from "./hmr-base-rewrite.js";
|
|
25
26
|
/** Modules external in background builds (available in Electron main process) */
|
|
26
27
|
const BG_EXTERNALS = ["electron", "socket.io-client"];
|
|
27
28
|
/** Node.js built-in modules to externalize in background builds */
|
|
@@ -204,30 +205,27 @@ function createUIConfig(pluginDir, entry, extraConfig) {
|
|
|
204
205
|
const isProduction = process.env.NODE_ENV === "production";
|
|
205
206
|
return {
|
|
206
207
|
root: pluginDir,
|
|
208
|
+
// Skip WebSocket token validation so /@vite/client can connect from
|
|
209
|
+
// a cross-origin Electron renderer. The renderer sends its own Origin
|
|
210
|
+
// header (e.g. http://localhost:5173), which triggers Vite's token
|
|
211
|
+
// check and rejects the connection. Safe for local dev servers.
|
|
212
|
+
legacy: { skipWebSocketTokenCheck: true },
|
|
207
213
|
server: {
|
|
208
|
-
cors:
|
|
209
|
-
origin: [
|
|
210
|
-
"http://localhost:2066",
|
|
211
|
-
"http://127.0.0.1:2066",
|
|
212
|
-
/^http:\/\/localhost:\d+$/,
|
|
213
|
-
],
|
|
214
|
-
},
|
|
214
|
+
cors: true,
|
|
215
215
|
hmr: {
|
|
216
|
-
// When modules are loaded via
|
|
216
|
+
// When modules are loaded via cross-origin import(), Vite's HMR
|
|
217
217
|
// client needs to connect back to THIS dev server's WebSocket directly.
|
|
218
|
-
// Without this, it would try to derive the WS URL from the custom
|
|
219
|
-
// protocol scheme, which doesn't work.
|
|
220
218
|
protocol: "ws",
|
|
221
219
|
host: "localhost",
|
|
222
220
|
},
|
|
223
221
|
},
|
|
224
222
|
optimizeDeps: {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
223
|
+
// Exclude non-React externals from pre-bundling (they come from the shell).
|
|
224
|
+
// React packages are NOT excluded — they need to be pre-bundled so that
|
|
225
|
+
// @vitejs/plugin-react can resolve react-refresh for Fast Refresh.
|
|
226
|
+
// serveExternals() still rewrites all source-level React imports to
|
|
227
|
+
// window.__stratos_modules__, so the pre-bundled React is never loaded.
|
|
228
|
+
exclude: UI_EXTERNALS.filter((id) => !id.startsWith("react") && id !== "react-dom"),
|
|
231
229
|
},
|
|
232
230
|
build: {
|
|
233
231
|
outDir: path.resolve(pluginDir, "dist/ui"),
|
|
@@ -252,6 +250,7 @@ function createUIConfig(pluginDir, entry, extraConfig) {
|
|
|
252
250
|
...extraConfig,
|
|
253
251
|
plugins: [
|
|
254
252
|
serveExternals(),
|
|
253
|
+
hmrBaseRewrite(),
|
|
255
254
|
stratosDevServer({ pluginDir }),
|
|
256
255
|
stratosExternals(),
|
|
257
256
|
cssInject(),
|
|
@@ -20,12 +20,19 @@ import { io as ioClient } from "socket.io-client";
|
|
|
20
20
|
*/
|
|
21
21
|
export function stratosDevServer(options) {
|
|
22
22
|
const { pluginDir } = options;
|
|
23
|
-
// Shared state between
|
|
23
|
+
// Shared state between hooks
|
|
24
24
|
let socket = null;
|
|
25
25
|
let pluginId = "unknown";
|
|
26
|
+
let wsToken = "";
|
|
26
27
|
return {
|
|
27
28
|
name: "stratos-dev-server",
|
|
28
29
|
apply: "serve",
|
|
30
|
+
configResolved(config) {
|
|
31
|
+
// Capture the WebSocket token generated by Vite. The shell needs this
|
|
32
|
+
// to connect to the plugin's HMR WebSocket for live updates.
|
|
33
|
+
wsToken =
|
|
34
|
+
config.webSocketToken ?? "";
|
|
35
|
+
},
|
|
29
36
|
configureServer(server) {
|
|
30
37
|
const stratosPort = options.stratosPort ??
|
|
31
38
|
(process.env.STRATOS_PORT
|
|
@@ -92,6 +99,8 @@ export function stratosDevServer(options) {
|
|
|
92
99
|
devUrl,
|
|
93
100
|
capabilities: ["ui", "hot-reload"],
|
|
94
101
|
distPath,
|
|
102
|
+
sourceRoot: pluginDir,
|
|
103
|
+
wsToken,
|
|
95
104
|
});
|
|
96
105
|
});
|
|
97
106
|
socket.on("dev:plugin-mounted", (data) => {
|
|
@@ -185,10 +194,10 @@ export function stratosDevServer(options) {
|
|
|
185
194
|
process.once("SIGTERM", cleanup);
|
|
186
195
|
// Start connecting after the HTTP server is listening
|
|
187
196
|
server.httpServer?.once("listening", () => {
|
|
188
|
-
// Set the HMR port dynamically so Vite's HMR client connects
|
|
189
|
-
// directly to this dev server's WebSocket (cross-origin is OK for WS).
|
|
190
197
|
const addr = server.httpServer?.address();
|
|
191
198
|
if (addr && typeof addr === "object") {
|
|
199
|
+
// Set the HMR port dynamically so Vite's HMR client connects
|
|
200
|
+
// directly to this dev server's WebSocket.
|
|
192
201
|
server.config.server.hmr =
|
|
193
202
|
typeof server.config.server.hmr === "object"
|
|
194
203
|
? {
|
|
@@ -201,19 +210,13 @@ export function stratosDevServer(options) {
|
|
|
201
210
|
connect();
|
|
202
211
|
});
|
|
203
212
|
},
|
|
204
|
-
//
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
pluginId,
|
|
212
|
-
timestamp: Date.now(),
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
},
|
|
213
|
+
// Vite handles all HMR natively — CSS via style injection, JS/TSX via
|
|
214
|
+
// React Fast Refresh (when @vitejs/plugin-react is configured).
|
|
215
|
+
// The hmrBaseRewrite plugin patches the HMR client's base URL so
|
|
216
|
+
// cross-origin module re-fetching works correctly.
|
|
217
|
+
//
|
|
218
|
+
// Background source changes are still handled separately via
|
|
219
|
+
// fs.watch + dev:plugin-bg-updated (see configureServer above).
|
|
217
220
|
};
|
|
218
221
|
}
|
|
219
222
|
//# sourceMappingURL=stratos-dev-server.js.map
|