@lovable.dev/vite-tanstack-config 2.13.0 → 2.14.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/dist/hmr-gate.cjs +155 -13
- package/dist/hmr-gate.d.cts +17 -3
- package/dist/hmr-gate.d.ts +17 -3
- package/dist/hmr-gate.js +155 -14
- package/package.json +1 -1
package/dist/hmr-gate.cjs
CHANGED
|
@@ -18,6 +18,25 @@ function gateStatusHeaders(req) {
|
|
|
18
18
|
return headers;
|
|
19
19
|
}
|
|
20
20
|
const DEV_SERVER_MODE_EVENT = "lovable:dev-server-mode";
|
|
21
|
+
const BOOT_PROGRESS_EVENT = "lovable:boot-progress";
|
|
22
|
+
const BOOT_PROGRESS_MIN_INTERVAL_MS = 500;
|
|
23
|
+
function firstBuildError(error) {
|
|
24
|
+
if (error == null || typeof error !== "object") return null;
|
|
25
|
+
const errors = error.errors;
|
|
26
|
+
const e = Array.isArray(errors) && errors.length > 0 ? errors[0] : error;
|
|
27
|
+
const message = typeof e.message === "string" ? e.message : "Build failed";
|
|
28
|
+
let file = null;
|
|
29
|
+
if (typeof e.loc?.file === "string") file = e.loc.file;
|
|
30
|
+
else if (typeof e.id === "string") file = e.id;
|
|
31
|
+
return {
|
|
32
|
+
message,
|
|
33
|
+
file,
|
|
34
|
+
line: typeof e.loc?.line === "number" ? e.loc.line : null
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function hookEnvironmentName(context) {
|
|
38
|
+
return context.environment?.name ?? "shared";
|
|
39
|
+
}
|
|
21
40
|
const EDIT_FREE_RELOAD_MARKER = "edit-free";
|
|
22
41
|
/** Host-controlled default for `fullReload`; an explicit option still wins. */
|
|
23
42
|
const FULL_RELOAD_ENV_VAR = "LOVABLE_HMR_FULL_RELOAD";
|
|
@@ -33,6 +52,57 @@ function hmrGatePlugin(options = {}) {
|
|
|
33
52
|
const lastFlushedHashes = /* @__PURE__ */ new Map();
|
|
34
53
|
let originalEmit = null;
|
|
35
54
|
let server = null;
|
|
55
|
+
let generation = null;
|
|
56
|
+
const transformsByEnv = /* @__PURE__ */ new Map();
|
|
57
|
+
const buildErrorsByEnv = /* @__PURE__ */ new Map();
|
|
58
|
+
let bootProgressSentAt = 0;
|
|
59
|
+
let bootProgressTimer = null;
|
|
60
|
+
let lastBootProgressKey = "";
|
|
61
|
+
function bootProgressData() {
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
const idle = (name) => {
|
|
64
|
+
const at = transformsByEnv.get(name)?.lastTransformAt;
|
|
65
|
+
return at === void 0 ? null : now - at;
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
ssrModules: transformsByEnv.get("ssr")?.ids.size ?? 0,
|
|
69
|
+
clientModules: transformsByEnv.get("client")?.ids.size ?? 0,
|
|
70
|
+
ssrIdleMs: idle("ssr"),
|
|
71
|
+
clientIdleMs: idle("client"),
|
|
72
|
+
buildError: buildErrorsByEnv.values().next().value ?? null
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function bootProgressKey(data) {
|
|
76
|
+
const error = data.buildError;
|
|
77
|
+
const errorKey = error ? `${error.message}:${error.file ?? ""}:${error.line ?? ""}` : "";
|
|
78
|
+
return `${data.ssrModules}:${data.clientModules}:${errorKey}`;
|
|
79
|
+
}
|
|
80
|
+
function broadcastBootProgress() {
|
|
81
|
+
const data = bootProgressData();
|
|
82
|
+
const key = bootProgressKey(data);
|
|
83
|
+
if (key === lastBootProgressKey) return;
|
|
84
|
+
lastBootProgressKey = key;
|
|
85
|
+
bootProgressSentAt = Date.now();
|
|
86
|
+
server?.ws.send({
|
|
87
|
+
type: "custom",
|
|
88
|
+
event: BOOT_PROGRESS_EVENT,
|
|
89
|
+
data
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function scheduleBootProgress() {
|
|
93
|
+
if (bootProgressTimer !== null) return;
|
|
94
|
+
const waitMs = bootProgressSentAt + BOOT_PROGRESS_MIN_INTERVAL_MS - Date.now();
|
|
95
|
+
if (waitMs <= 0) {
|
|
96
|
+
broadcastBootProgress();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
bootProgressTimer = setTimeout(() => {
|
|
100
|
+
bootProgressTimer = null;
|
|
101
|
+
broadcastBootProgress();
|
|
102
|
+
}, waitMs);
|
|
103
|
+
bootProgressTimer.unref?.();
|
|
104
|
+
}
|
|
105
|
+
let classicFailOpenTimer = null;
|
|
36
106
|
let transportMode = false;
|
|
37
107
|
let swallowedUpdates = 0;
|
|
38
108
|
let heldReload = false;
|
|
@@ -106,15 +176,55 @@ function hmrGatePlugin(options = {}) {
|
|
|
106
176
|
apply: "serve",
|
|
107
177
|
configResolved(config) {
|
|
108
178
|
transportMode = config.experimental?.bundledDev === true;
|
|
179
|
+
const rawGeneration = process.env.LOVABLE_SPAWN_GENERATION;
|
|
180
|
+
const parsed = rawGeneration === void 0 || rawGeneration === "" ? null : Number(rawGeneration);
|
|
181
|
+
generation = parsed !== null && Number.isSafeInteger(parsed) ? parsed : null;
|
|
182
|
+
},
|
|
183
|
+
transform(_code, id, options) {
|
|
184
|
+
const name = this.environment?.name ?? (options?.ssr === true ? "ssr" : "client");
|
|
185
|
+
let progress = transformsByEnv.get(name);
|
|
186
|
+
if (!progress) {
|
|
187
|
+
progress = {
|
|
188
|
+
ids: /* @__PURE__ */ new Set(),
|
|
189
|
+
lastTransformAt: 0
|
|
190
|
+
};
|
|
191
|
+
transformsByEnv.set(name, progress);
|
|
192
|
+
}
|
|
193
|
+
progress.ids.add(id);
|
|
194
|
+
progress.lastTransformAt = Date.now();
|
|
195
|
+
scheduleBootProgress();
|
|
196
|
+
return null;
|
|
197
|
+
},
|
|
198
|
+
buildStart() {
|
|
199
|
+
buildErrorsByEnv.delete(hookEnvironmentName(this));
|
|
200
|
+
scheduleBootProgress();
|
|
201
|
+
},
|
|
202
|
+
buildEnd(error) {
|
|
203
|
+
const name = hookEnvironmentName(this);
|
|
204
|
+
const parsed = error == null ? null : firstBuildError(error);
|
|
205
|
+
if (parsed === null) buildErrorsByEnv.delete(name);
|
|
206
|
+
else buildErrorsByEnv.set(name, parsed);
|
|
207
|
+
scheduleBootProgress();
|
|
109
208
|
},
|
|
110
209
|
configureServer(srv) {
|
|
111
210
|
server = srv;
|
|
211
|
+
transformsByEnv.clear();
|
|
212
|
+
buildErrorsByEnv.clear();
|
|
213
|
+
lastBootProgressKey = "";
|
|
112
214
|
srv.ws.on?.("connection", (socket) => {
|
|
113
215
|
try {
|
|
114
216
|
socket.send(JSON.stringify({
|
|
115
217
|
type: "custom",
|
|
116
218
|
event: DEV_SERVER_MODE_EVENT,
|
|
117
|
-
data: {
|
|
219
|
+
data: {
|
|
220
|
+
mode: transportMode ? "bundled" : "classic",
|
|
221
|
+
...generation !== null ? { generation } : {}
|
|
222
|
+
}
|
|
223
|
+
}));
|
|
224
|
+
socket.send(JSON.stringify({
|
|
225
|
+
type: "custom",
|
|
226
|
+
event: BOOT_PROGRESS_EVENT,
|
|
227
|
+
data: bootProgressData()
|
|
118
228
|
}));
|
|
119
229
|
} catch {}
|
|
120
230
|
});
|
|
@@ -239,6 +349,25 @@ function hmrGatePlugin(options = {}) {
|
|
|
239
349
|
}
|
|
240
350
|
const _emit = srv.watcher.emit.bind(srv.watcher);
|
|
241
351
|
originalEmit = _emit;
|
|
352
|
+
const flushPending = () => {
|
|
353
|
+
if (classicFailOpenTimer !== null) {
|
|
354
|
+
clearTimeout(classicFailOpenTimer);
|
|
355
|
+
classicFailOpenTimer = null;
|
|
356
|
+
}
|
|
357
|
+
const entries = Array.from(pending.entries());
|
|
358
|
+
pending.clear();
|
|
359
|
+
for (const [f] of entries) srv.moduleGraph.onFileChange(f);
|
|
360
|
+
if (entries.length > 0) {
|
|
361
|
+
for (const [f, events] of entries) {
|
|
362
|
+
for (const event of events) _emit(event, f);
|
|
363
|
+
const hash = fileContentHash(f);
|
|
364
|
+
if (hash === null) lastFlushedHashes.delete(f);
|
|
365
|
+
else lastFlushedHashes.set(f, hash);
|
|
366
|
+
}
|
|
367
|
+
if (fullReload) srv.ws.send({ type: "full-reload" });
|
|
368
|
+
}
|
|
369
|
+
return entries;
|
|
370
|
+
};
|
|
242
371
|
srv.watcher.emit = ((event, ...args) => {
|
|
243
372
|
if (typeof event === "string" && gatedEvents.has(event) && !isPassthrough(args[0])) {
|
|
244
373
|
const filePath = args[0];
|
|
@@ -246,6 +375,10 @@ function hmrGatePlugin(options = {}) {
|
|
|
246
375
|
const hash = fileContentHash(filePath);
|
|
247
376
|
if (hash !== null && hash === lastFlushedHashes.get(filePath)) {
|
|
248
377
|
pending.delete(filePath);
|
|
378
|
+
if (pending.size === 0 && classicFailOpenTimer !== null) {
|
|
379
|
+
clearTimeout(classicFailOpenTimer);
|
|
380
|
+
classicFailOpenTimer = null;
|
|
381
|
+
}
|
|
249
382
|
return false;
|
|
250
383
|
}
|
|
251
384
|
}
|
|
@@ -255,24 +388,20 @@ function hmrGatePlugin(options = {}) {
|
|
|
255
388
|
pending.set(filePath, events);
|
|
256
389
|
}
|
|
257
390
|
events.add(event);
|
|
391
|
+
if (classicFailOpenTimer === null) {
|
|
392
|
+
classicFailOpenTimer = setTimeout(() => {
|
|
393
|
+
classicFailOpenTimer = null;
|
|
394
|
+
flushPending();
|
|
395
|
+
}, maxHoldMs);
|
|
396
|
+
classicFailOpenTimer.unref?.();
|
|
397
|
+
}
|
|
258
398
|
return false;
|
|
259
399
|
}
|
|
260
400
|
return _emit(event, ...args);
|
|
261
401
|
});
|
|
262
402
|
srv.middlewares.use((req, res, next) => {
|
|
263
403
|
if (req.method !== "POST" || requestPathname(req) !== "/__hmr_flush") return next();
|
|
264
|
-
const entries =
|
|
265
|
-
pending.clear();
|
|
266
|
-
for (const [f] of entries) srv.moduleGraph.onFileChange(f);
|
|
267
|
-
if (entries.length > 0) {
|
|
268
|
-
for (const [f, events] of entries) {
|
|
269
|
-
for (const event of events) _emit(event, f);
|
|
270
|
-
const hash = fileContentHash(f);
|
|
271
|
-
if (hash === null) lastFlushedHashes.delete(f);
|
|
272
|
-
else lastFlushedHashes.set(f, hash);
|
|
273
|
-
}
|
|
274
|
-
if (fullReload) srv.ws.send({ type: "full-reload" });
|
|
275
|
-
}
|
|
404
|
+
const entries = flushPending();
|
|
276
405
|
const files = entries.map(([f]) => f);
|
|
277
406
|
const body = JSON.stringify({
|
|
278
407
|
flushed: files,
|
|
@@ -309,6 +438,18 @@ function hmrGatePlugin(options = {}) {
|
|
|
309
438
|
clearTimeout(reloadFailOpenTimer);
|
|
310
439
|
reloadFailOpenTimer = null;
|
|
311
440
|
}
|
|
441
|
+
if (classicFailOpenTimer !== null) {
|
|
442
|
+
clearTimeout(classicFailOpenTimer);
|
|
443
|
+
classicFailOpenTimer = null;
|
|
444
|
+
}
|
|
445
|
+
if (bootProgressTimer !== null) {
|
|
446
|
+
clearTimeout(bootProgressTimer);
|
|
447
|
+
bootProgressTimer = null;
|
|
448
|
+
}
|
|
449
|
+
transformsByEnv.clear();
|
|
450
|
+
buildErrorsByEnv.clear();
|
|
451
|
+
bootProgressSentAt = 0;
|
|
452
|
+
lastBootProgressKey = "";
|
|
312
453
|
bufferedReloadNeeded.clear();
|
|
313
454
|
pendingReplay.clear();
|
|
314
455
|
reloadNeededListener = null;
|
|
@@ -324,6 +465,7 @@ function hmrGatePlugin(options = {}) {
|
|
|
324
465
|
};
|
|
325
466
|
}
|
|
326
467
|
//#endregion
|
|
468
|
+
exports.BOOT_PROGRESS_EVENT = BOOT_PROGRESS_EVENT;
|
|
327
469
|
exports.DEV_SERVER_MODE_EVENT = DEV_SERVER_MODE_EVENT;
|
|
328
470
|
exports.FULL_RELOAD_ENV_VAR = FULL_RELOAD_ENV_VAR;
|
|
329
471
|
exports.hmrGatePlugin = hmrGatePlugin;
|
package/dist/hmr-gate.d.cts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { Plugin } from "vite";
|
|
2
2
|
//#region src/hmr-gate/index.d.ts
|
|
3
3
|
declare const DEV_SERVER_MODE_EVENT = "lovable:dev-server-mode";
|
|
4
|
+
declare const BOOT_PROGRESS_EVENT = "lovable:boot-progress";
|
|
5
|
+
interface BootProgressData {
|
|
6
|
+
ssrModules: number;
|
|
7
|
+
clientModules: number;
|
|
8
|
+
/** ms since the newest transform, measured on the server's own clock so the
|
|
9
|
+
browser never differences two machines' timestamps. */
|
|
10
|
+
ssrIdleMs: number | null;
|
|
11
|
+
clientIdleMs: number | null;
|
|
12
|
+
buildError: {
|
|
13
|
+
message: string;
|
|
14
|
+
file: string | null;
|
|
15
|
+
line: number | null;
|
|
16
|
+
} | null;
|
|
17
|
+
}
|
|
4
18
|
/** Host-controlled default for `fullReload`; an explicit option still wins. */
|
|
5
19
|
declare const FULL_RELOAD_ENV_VAR = "LOVABLE_HMR_FULL_RELOAD";
|
|
6
20
|
interface HmrGateOptions {
|
|
@@ -10,8 +24,8 @@ interface HmrGateOptions {
|
|
|
10
24
|
/** Bundled dev only: ms the gate stays open after a flush so the client's
|
|
11
25
|
* queued patch fetches drain. Default 1500. */
|
|
12
26
|
drainMs?: number;
|
|
13
|
-
/**
|
|
14
|
-
*
|
|
27
|
+
/** Max ms held work survives without a flush before the gate fail-opens
|
|
28
|
+
* (classic self-flushes; bundled dev releases parked patches). Default 240000. */
|
|
15
29
|
maxHoldMs?: number;
|
|
16
30
|
/** Bundled dev only: ms after server start during which full-reload frames
|
|
17
31
|
* (Vite's restart resync) pass through live. Default 5000. */
|
|
@@ -24,4 +38,4 @@ interface HmrGateOptions {
|
|
|
24
38
|
}
|
|
25
39
|
declare function hmrGatePlugin(options?: HmrGateOptions): Plugin;
|
|
26
40
|
//#endregion
|
|
27
|
-
export { DEV_SERVER_MODE_EVENT, FULL_RELOAD_ENV_VAR, HmrGateOptions, hmrGatePlugin };
|
|
41
|
+
export { BOOT_PROGRESS_EVENT, BootProgressData, DEV_SERVER_MODE_EVENT, FULL_RELOAD_ENV_VAR, HmrGateOptions, hmrGatePlugin };
|
package/dist/hmr-gate.d.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { Plugin } from "vite";
|
|
2
2
|
//#region src/hmr-gate/index.d.ts
|
|
3
3
|
declare const DEV_SERVER_MODE_EVENT = "lovable:dev-server-mode";
|
|
4
|
+
declare const BOOT_PROGRESS_EVENT = "lovable:boot-progress";
|
|
5
|
+
interface BootProgressData {
|
|
6
|
+
ssrModules: number;
|
|
7
|
+
clientModules: number;
|
|
8
|
+
/** ms since the newest transform, measured on the server's own clock so the
|
|
9
|
+
browser never differences two machines' timestamps. */
|
|
10
|
+
ssrIdleMs: number | null;
|
|
11
|
+
clientIdleMs: number | null;
|
|
12
|
+
buildError: {
|
|
13
|
+
message: string;
|
|
14
|
+
file: string | null;
|
|
15
|
+
line: number | null;
|
|
16
|
+
} | null;
|
|
17
|
+
}
|
|
4
18
|
/** Host-controlled default for `fullReload`; an explicit option still wins. */
|
|
5
19
|
declare const FULL_RELOAD_ENV_VAR = "LOVABLE_HMR_FULL_RELOAD";
|
|
6
20
|
interface HmrGateOptions {
|
|
@@ -10,8 +24,8 @@ interface HmrGateOptions {
|
|
|
10
24
|
/** Bundled dev only: ms the gate stays open after a flush so the client's
|
|
11
25
|
* queued patch fetches drain. Default 1500. */
|
|
12
26
|
drainMs?: number;
|
|
13
|
-
/**
|
|
14
|
-
*
|
|
27
|
+
/** Max ms held work survives without a flush before the gate fail-opens
|
|
28
|
+
* (classic self-flushes; bundled dev releases parked patches). Default 240000. */
|
|
15
29
|
maxHoldMs?: number;
|
|
16
30
|
/** Bundled dev only: ms after server start during which full-reload frames
|
|
17
31
|
* (Vite's restart resync) pass through live. Default 5000. */
|
|
@@ -24,4 +38,4 @@ interface HmrGateOptions {
|
|
|
24
38
|
}
|
|
25
39
|
declare function hmrGatePlugin(options?: HmrGateOptions): Plugin;
|
|
26
40
|
//#endregion
|
|
27
|
-
export { DEV_SERVER_MODE_EVENT, FULL_RELOAD_ENV_VAR, HmrGateOptions, hmrGatePlugin };
|
|
41
|
+
export { BOOT_PROGRESS_EVENT, BootProgressData, DEV_SERVER_MODE_EVENT, FULL_RELOAD_ENV_VAR, HmrGateOptions, hmrGatePlugin };
|
package/dist/hmr-gate.js
CHANGED
|
@@ -17,6 +17,25 @@ function gateStatusHeaders(req) {
|
|
|
17
17
|
return headers;
|
|
18
18
|
}
|
|
19
19
|
const DEV_SERVER_MODE_EVENT = "lovable:dev-server-mode";
|
|
20
|
+
const BOOT_PROGRESS_EVENT = "lovable:boot-progress";
|
|
21
|
+
const BOOT_PROGRESS_MIN_INTERVAL_MS = 500;
|
|
22
|
+
function firstBuildError(error) {
|
|
23
|
+
if (error == null || typeof error !== "object") return null;
|
|
24
|
+
const errors = error.errors;
|
|
25
|
+
const e = Array.isArray(errors) && errors.length > 0 ? errors[0] : error;
|
|
26
|
+
const message = typeof e.message === "string" ? e.message : "Build failed";
|
|
27
|
+
let file = null;
|
|
28
|
+
if (typeof e.loc?.file === "string") file = e.loc.file;
|
|
29
|
+
else if (typeof e.id === "string") file = e.id;
|
|
30
|
+
return {
|
|
31
|
+
message,
|
|
32
|
+
file,
|
|
33
|
+
line: typeof e.loc?.line === "number" ? e.loc.line : null
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function hookEnvironmentName(context) {
|
|
37
|
+
return context.environment?.name ?? "shared";
|
|
38
|
+
}
|
|
20
39
|
const EDIT_FREE_RELOAD_MARKER = "edit-free";
|
|
21
40
|
/** Host-controlled default for `fullReload`; an explicit option still wins. */
|
|
22
41
|
const FULL_RELOAD_ENV_VAR = "LOVABLE_HMR_FULL_RELOAD";
|
|
@@ -32,6 +51,57 @@ function hmrGatePlugin(options = {}) {
|
|
|
32
51
|
const lastFlushedHashes = /* @__PURE__ */ new Map();
|
|
33
52
|
let originalEmit = null;
|
|
34
53
|
let server = null;
|
|
54
|
+
let generation = null;
|
|
55
|
+
const transformsByEnv = /* @__PURE__ */ new Map();
|
|
56
|
+
const buildErrorsByEnv = /* @__PURE__ */ new Map();
|
|
57
|
+
let bootProgressSentAt = 0;
|
|
58
|
+
let bootProgressTimer = null;
|
|
59
|
+
let lastBootProgressKey = "";
|
|
60
|
+
function bootProgressData() {
|
|
61
|
+
const now = Date.now();
|
|
62
|
+
const idle = (name) => {
|
|
63
|
+
const at = transformsByEnv.get(name)?.lastTransformAt;
|
|
64
|
+
return at === void 0 ? null : now - at;
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
ssrModules: transformsByEnv.get("ssr")?.ids.size ?? 0,
|
|
68
|
+
clientModules: transformsByEnv.get("client")?.ids.size ?? 0,
|
|
69
|
+
ssrIdleMs: idle("ssr"),
|
|
70
|
+
clientIdleMs: idle("client"),
|
|
71
|
+
buildError: buildErrorsByEnv.values().next().value ?? null
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function bootProgressKey(data) {
|
|
75
|
+
const error = data.buildError;
|
|
76
|
+
const errorKey = error ? `${error.message}:${error.file ?? ""}:${error.line ?? ""}` : "";
|
|
77
|
+
return `${data.ssrModules}:${data.clientModules}:${errorKey}`;
|
|
78
|
+
}
|
|
79
|
+
function broadcastBootProgress() {
|
|
80
|
+
const data = bootProgressData();
|
|
81
|
+
const key = bootProgressKey(data);
|
|
82
|
+
if (key === lastBootProgressKey) return;
|
|
83
|
+
lastBootProgressKey = key;
|
|
84
|
+
bootProgressSentAt = Date.now();
|
|
85
|
+
server?.ws.send({
|
|
86
|
+
type: "custom",
|
|
87
|
+
event: BOOT_PROGRESS_EVENT,
|
|
88
|
+
data
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function scheduleBootProgress() {
|
|
92
|
+
if (bootProgressTimer !== null) return;
|
|
93
|
+
const waitMs = bootProgressSentAt + BOOT_PROGRESS_MIN_INTERVAL_MS - Date.now();
|
|
94
|
+
if (waitMs <= 0) {
|
|
95
|
+
broadcastBootProgress();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
bootProgressTimer = setTimeout(() => {
|
|
99
|
+
bootProgressTimer = null;
|
|
100
|
+
broadcastBootProgress();
|
|
101
|
+
}, waitMs);
|
|
102
|
+
bootProgressTimer.unref?.();
|
|
103
|
+
}
|
|
104
|
+
let classicFailOpenTimer = null;
|
|
35
105
|
let transportMode = false;
|
|
36
106
|
let swallowedUpdates = 0;
|
|
37
107
|
let heldReload = false;
|
|
@@ -105,15 +175,55 @@ function hmrGatePlugin(options = {}) {
|
|
|
105
175
|
apply: "serve",
|
|
106
176
|
configResolved(config) {
|
|
107
177
|
transportMode = config.experimental?.bundledDev === true;
|
|
178
|
+
const rawGeneration = process.env.LOVABLE_SPAWN_GENERATION;
|
|
179
|
+
const parsed = rawGeneration === void 0 || rawGeneration === "" ? null : Number(rawGeneration);
|
|
180
|
+
generation = parsed !== null && Number.isSafeInteger(parsed) ? parsed : null;
|
|
181
|
+
},
|
|
182
|
+
transform(_code, id, options) {
|
|
183
|
+
const name = this.environment?.name ?? (options?.ssr === true ? "ssr" : "client");
|
|
184
|
+
let progress = transformsByEnv.get(name);
|
|
185
|
+
if (!progress) {
|
|
186
|
+
progress = {
|
|
187
|
+
ids: /* @__PURE__ */ new Set(),
|
|
188
|
+
lastTransformAt: 0
|
|
189
|
+
};
|
|
190
|
+
transformsByEnv.set(name, progress);
|
|
191
|
+
}
|
|
192
|
+
progress.ids.add(id);
|
|
193
|
+
progress.lastTransformAt = Date.now();
|
|
194
|
+
scheduleBootProgress();
|
|
195
|
+
return null;
|
|
196
|
+
},
|
|
197
|
+
buildStart() {
|
|
198
|
+
buildErrorsByEnv.delete(hookEnvironmentName(this));
|
|
199
|
+
scheduleBootProgress();
|
|
200
|
+
},
|
|
201
|
+
buildEnd(error) {
|
|
202
|
+
const name = hookEnvironmentName(this);
|
|
203
|
+
const parsed = error == null ? null : firstBuildError(error);
|
|
204
|
+
if (parsed === null) buildErrorsByEnv.delete(name);
|
|
205
|
+
else buildErrorsByEnv.set(name, parsed);
|
|
206
|
+
scheduleBootProgress();
|
|
108
207
|
},
|
|
109
208
|
configureServer(srv) {
|
|
110
209
|
server = srv;
|
|
210
|
+
transformsByEnv.clear();
|
|
211
|
+
buildErrorsByEnv.clear();
|
|
212
|
+
lastBootProgressKey = "";
|
|
111
213
|
srv.ws.on?.("connection", (socket) => {
|
|
112
214
|
try {
|
|
113
215
|
socket.send(JSON.stringify({
|
|
114
216
|
type: "custom",
|
|
115
217
|
event: DEV_SERVER_MODE_EVENT,
|
|
116
|
-
data: {
|
|
218
|
+
data: {
|
|
219
|
+
mode: transportMode ? "bundled" : "classic",
|
|
220
|
+
...generation !== null ? { generation } : {}
|
|
221
|
+
}
|
|
222
|
+
}));
|
|
223
|
+
socket.send(JSON.stringify({
|
|
224
|
+
type: "custom",
|
|
225
|
+
event: BOOT_PROGRESS_EVENT,
|
|
226
|
+
data: bootProgressData()
|
|
117
227
|
}));
|
|
118
228
|
} catch {}
|
|
119
229
|
});
|
|
@@ -238,6 +348,25 @@ function hmrGatePlugin(options = {}) {
|
|
|
238
348
|
}
|
|
239
349
|
const _emit = srv.watcher.emit.bind(srv.watcher);
|
|
240
350
|
originalEmit = _emit;
|
|
351
|
+
const flushPending = () => {
|
|
352
|
+
if (classicFailOpenTimer !== null) {
|
|
353
|
+
clearTimeout(classicFailOpenTimer);
|
|
354
|
+
classicFailOpenTimer = null;
|
|
355
|
+
}
|
|
356
|
+
const entries = Array.from(pending.entries());
|
|
357
|
+
pending.clear();
|
|
358
|
+
for (const [f] of entries) srv.moduleGraph.onFileChange(f);
|
|
359
|
+
if (entries.length > 0) {
|
|
360
|
+
for (const [f, events] of entries) {
|
|
361
|
+
for (const event of events) _emit(event, f);
|
|
362
|
+
const hash = fileContentHash(f);
|
|
363
|
+
if (hash === null) lastFlushedHashes.delete(f);
|
|
364
|
+
else lastFlushedHashes.set(f, hash);
|
|
365
|
+
}
|
|
366
|
+
if (fullReload) srv.ws.send({ type: "full-reload" });
|
|
367
|
+
}
|
|
368
|
+
return entries;
|
|
369
|
+
};
|
|
241
370
|
srv.watcher.emit = ((event, ...args) => {
|
|
242
371
|
if (typeof event === "string" && gatedEvents.has(event) && !isPassthrough(args[0])) {
|
|
243
372
|
const filePath = args[0];
|
|
@@ -245,6 +374,10 @@ function hmrGatePlugin(options = {}) {
|
|
|
245
374
|
const hash = fileContentHash(filePath);
|
|
246
375
|
if (hash !== null && hash === lastFlushedHashes.get(filePath)) {
|
|
247
376
|
pending.delete(filePath);
|
|
377
|
+
if (pending.size === 0 && classicFailOpenTimer !== null) {
|
|
378
|
+
clearTimeout(classicFailOpenTimer);
|
|
379
|
+
classicFailOpenTimer = null;
|
|
380
|
+
}
|
|
248
381
|
return false;
|
|
249
382
|
}
|
|
250
383
|
}
|
|
@@ -254,24 +387,20 @@ function hmrGatePlugin(options = {}) {
|
|
|
254
387
|
pending.set(filePath, events);
|
|
255
388
|
}
|
|
256
389
|
events.add(event);
|
|
390
|
+
if (classicFailOpenTimer === null) {
|
|
391
|
+
classicFailOpenTimer = setTimeout(() => {
|
|
392
|
+
classicFailOpenTimer = null;
|
|
393
|
+
flushPending();
|
|
394
|
+
}, maxHoldMs);
|
|
395
|
+
classicFailOpenTimer.unref?.();
|
|
396
|
+
}
|
|
257
397
|
return false;
|
|
258
398
|
}
|
|
259
399
|
return _emit(event, ...args);
|
|
260
400
|
});
|
|
261
401
|
srv.middlewares.use((req, res, next) => {
|
|
262
402
|
if (req.method !== "POST" || requestPathname(req) !== "/__hmr_flush") return next();
|
|
263
|
-
const entries =
|
|
264
|
-
pending.clear();
|
|
265
|
-
for (const [f] of entries) srv.moduleGraph.onFileChange(f);
|
|
266
|
-
if (entries.length > 0) {
|
|
267
|
-
for (const [f, events] of entries) {
|
|
268
|
-
for (const event of events) _emit(event, f);
|
|
269
|
-
const hash = fileContentHash(f);
|
|
270
|
-
if (hash === null) lastFlushedHashes.delete(f);
|
|
271
|
-
else lastFlushedHashes.set(f, hash);
|
|
272
|
-
}
|
|
273
|
-
if (fullReload) srv.ws.send({ type: "full-reload" });
|
|
274
|
-
}
|
|
403
|
+
const entries = flushPending();
|
|
275
404
|
const files = entries.map(([f]) => f);
|
|
276
405
|
const body = JSON.stringify({
|
|
277
406
|
flushed: files,
|
|
@@ -308,6 +437,18 @@ function hmrGatePlugin(options = {}) {
|
|
|
308
437
|
clearTimeout(reloadFailOpenTimer);
|
|
309
438
|
reloadFailOpenTimer = null;
|
|
310
439
|
}
|
|
440
|
+
if (classicFailOpenTimer !== null) {
|
|
441
|
+
clearTimeout(classicFailOpenTimer);
|
|
442
|
+
classicFailOpenTimer = null;
|
|
443
|
+
}
|
|
444
|
+
if (bootProgressTimer !== null) {
|
|
445
|
+
clearTimeout(bootProgressTimer);
|
|
446
|
+
bootProgressTimer = null;
|
|
447
|
+
}
|
|
448
|
+
transformsByEnv.clear();
|
|
449
|
+
buildErrorsByEnv.clear();
|
|
450
|
+
bootProgressSentAt = 0;
|
|
451
|
+
lastBootProgressKey = "";
|
|
311
452
|
bufferedReloadNeeded.clear();
|
|
312
453
|
pendingReplay.clear();
|
|
313
454
|
reloadNeededListener = null;
|
|
@@ -323,4 +464,4 @@ function hmrGatePlugin(options = {}) {
|
|
|
323
464
|
};
|
|
324
465
|
}
|
|
325
466
|
//#endregion
|
|
326
|
-
export { DEV_SERVER_MODE_EVENT, FULL_RELOAD_ENV_VAR, hmrGatePlugin };
|
|
467
|
+
export { BOOT_PROGRESS_EVENT, DEV_SERVER_MODE_EVENT, FULL_RELOAD_ENV_VAR, hmrGatePlugin };
|