@nativescript/vite 8.0.0-alpha.0 → 8.0.0-alpha.2
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/configuration/angular.d.ts +1 -1
- package/configuration/angular.js +286 -119
- package/configuration/angular.js.map +1 -1
- package/configuration/base.js +40 -35
- package/configuration/base.js.map +1 -1
- package/configuration/javascript.js +3 -3
- package/configuration/javascript.js.map +1 -1
- package/configuration/solid.js +7 -0
- package/configuration/solid.js.map +1 -1
- package/configuration/typescript.js +3 -3
- package/configuration/typescript.js.map +1 -1
- package/helpers/angular/angular-linker.js +39 -34
- package/helpers/angular/angular-linker.js.map +1 -1
- package/helpers/angular/inline-decorator-component-templates.d.ts +3 -0
- package/helpers/angular/inline-decorator-component-templates.js +400 -0
- package/helpers/angular/inline-decorator-component-templates.js.map +1 -0
- package/helpers/angular/shared-linker.d.ts +7 -0
- package/helpers/angular/shared-linker.js +37 -1
- package/helpers/angular/shared-linker.js.map +1 -1
- package/helpers/angular/synthesize-decorator-ctor-parameters.d.ts +1 -0
- package/helpers/angular/synthesize-decorator-ctor-parameters.js +256 -0
- package/helpers/angular/synthesize-decorator-ctor-parameters.js.map +1 -0
- package/helpers/angular/synthesize-injectable-factories.d.ts +3 -0
- package/helpers/angular/synthesize-injectable-factories.js +414 -0
- package/helpers/angular/synthesize-injectable-factories.js.map +1 -0
- package/helpers/esbuild-platform-resolver.js +5 -5
- package/helpers/esbuild-platform-resolver.js.map +1 -1
- package/helpers/external-configs.d.ts +9 -1
- package/helpers/external-configs.js +31 -6
- package/helpers/external-configs.js.map +1 -1
- package/helpers/import-meta-path.d.ts +4 -0
- package/helpers/import-meta-path.js +5 -0
- package/helpers/import-meta-path.js.map +1 -0
- package/helpers/import-specifier.d.ts +1 -0
- package/helpers/import-specifier.js +18 -0
- package/helpers/import-specifier.js.map +1 -0
- package/helpers/main-entry.d.ts +4 -2
- package/helpers/main-entry.js +86 -10
- package/helpers/main-entry.js.map +1 -1
- package/helpers/nativeclass-transform.js +8 -127
- package/helpers/nativeclass-transform.js.map +1 -1
- package/helpers/nativeclass-transformer-plugin.d.ts +12 -1
- package/helpers/nativeclass-transformer-plugin.js +175 -31
- package/helpers/nativeclass-transformer-plugin.js.map +1 -1
- package/helpers/preserve-imports.js +2 -17
- package/helpers/preserve-imports.js.map +1 -1
- package/hmr/client/css-handler.js +60 -20
- package/hmr/client/css-handler.js.map +1 -1
- package/hmr/client/index.js +453 -5
- package/hmr/client/index.js.map +1 -1
- package/hmr/entry-runtime.d.ts +10 -0
- package/hmr/entry-runtime.js +263 -21
- package/hmr/entry-runtime.js.map +1 -1
- package/hmr/frameworks/angular/client/index.js +22 -18
- package/hmr/frameworks/angular/client/index.js.map +1 -1
- package/hmr/frameworks/angular/server/linker.js +36 -2
- package/hmr/frameworks/angular/server/linker.js.map +1 -1
- package/hmr/helpers/ast-normalizer.js +22 -10
- package/hmr/helpers/ast-normalizer.js.map +1 -1
- package/hmr/server/constants.d.ts +1 -0
- package/hmr/server/constants.js +2 -0
- package/hmr/server/constants.js.map +1 -1
- package/hmr/server/core-sanitize.d.ts +42 -0
- package/hmr/server/core-sanitize.js +189 -0
- package/hmr/server/core-sanitize.js.map +1 -1
- package/hmr/server/import-map.d.ts +65 -0
- package/hmr/server/import-map.js +213 -0
- package/hmr/server/import-map.js.map +1 -0
- package/hmr/server/websocket.d.ts +79 -3
- package/hmr/server/websocket.js +1888 -233
- package/hmr/server/websocket.js.map +1 -1
- package/hmr/shared/runtime/dev-overlay.d.ts +38 -0
- package/hmr/shared/runtime/dev-overlay.js +664 -0
- package/hmr/shared/runtime/dev-overlay.js.map +1 -0
- package/hmr/shared/runtime/http-only-boot.d.ts +1 -0
- package/hmr/shared/runtime/http-only-boot.js +53 -6
- package/hmr/shared/runtime/http-only-boot.js.map +1 -1
- package/hmr/shared/runtime/module-provenance.d.ts +1 -0
- package/hmr/shared/runtime/module-provenance.js +66 -0
- package/hmr/shared/runtime/module-provenance.js.map +1 -0
- package/hmr/shared/runtime/platform-polyfills.d.ts +26 -0
- package/hmr/shared/runtime/platform-polyfills.js +122 -0
- package/hmr/shared/runtime/platform-polyfills.js.map +1 -0
- package/hmr/shared/runtime/root-placeholder.js +175 -53
- package/hmr/shared/runtime/root-placeholder.js.map +1 -1
- package/hmr/shared/runtime/vendor-bootstrap.js +51 -6
- package/hmr/shared/runtime/vendor-bootstrap.js.map +1 -1
- package/hmr/shared/vendor/manifest.d.ts +5 -0
- package/hmr/shared/vendor/manifest.js +339 -13
- package/hmr/shared/vendor/manifest.js.map +1 -1
- package/hmr/shared/vendor/registry.js +104 -7
- package/hmr/shared/vendor/registry.js.map +1 -1
- package/package.json +6 -2
- package/shims/solid-jsx-runtime.d.ts +7 -0
- package/shims/solid-jsx-runtime.js +17 -0
- package/shims/solid-jsx-runtime.js.map +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"css-handler.js","sourceRoot":"","sources":["../../../../../packages/vite/hmr/client/css-handler.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"css-handler.js","sourceRoot":"","sources":["../../../../../packages/vite/hmr/client/css-handler.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,GAAG,CAAC,CAAE,UAAkB,CAAC,kBAAkB,CAAC;AAEzD,SAAS,sBAAsB;IAC9B,IAAI,CAAC;QACJ,MAAM,OAAO,GAAI,UAAkB,CAAC,oBAAoB,CAAC;QACzD,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;YACnC,OAAO,OAAO,CAAC;QAChB,CAAC;IACF,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,OAAO,CAAC,IAAY;IAC5B,IAAI,CAAC;QACJ,MAAM,CAAC,GAAG,UAAiB,CAAC;QAC5B,MAAM,GAAG,GAAG,CAAC,CAAC,kBAAkB,CAAC;QACjC,IAAI,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAE,CAAC;YAC1C,MAAM,EAAE,GAAG,GAAG,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;YACzC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QAClD,CAAC;QACD,MAAM,GAAG,GAAG,CAAC,CAAC,iBAAiB,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,OAAO,CAAC;QAC9D,IAAI,OAAO,GAAG,KAAK,UAAU,EAAE,CAAC;YAC/B,MAAM,EAAE,GAAG,GAAG,CAAC,oBAAoB,CAAC,CAAC;YACrC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QAClD,CAAC;IACF,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,OAAQ,UAAkB,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,YAAY,CAAC,OAAe;IAC3C,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAO;IAE3D,IAAI,CAAC;QACJ,MAAM,oBAAoB,GAAG,sBAAsB,EAAE,CAAC;QACtD,IAAI,oBAAoB,EAAE,CAAC;YAC1B,oBAAoB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACpC,IAAI,OAAO;gBAAE,OAAO,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;YAC1E,OAAO;QACR,CAAC;QAED,MAAM,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;QAC3C,IAAI,WAAW,IAAI,WAAW,CAAC,MAAM,EAAE,CAAC;YACvC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC5B,OAAO,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;QACvD,CAAC;QACD,yDAAyD;QACzD,oEAAoE;QACpE,IAAI,CAAC;YACJ,MAAM,QAAQ,GAAG,WAAW,EAAE,WAAW,EAAE,EAAE,CAAC;YAC9C,IAAI,QAAQ,IAAI,OAAO,QAAQ,CAAC,iBAAiB,KAAK,UAAU,EAAE,CAAC;gBAClE,QAAQ,CAAC,iBAAiB,EAAE,CAAC;YAC9B,CAAC;iBAAM,IAAI,QAAQ,EAAE,CAAC;gBACrB,2DAA2D;gBAC3D,MAAM,GAAG,GAAG,QAAQ,CAAC,SAAS,IAAI,EAAE,CAAC;gBACrC,QAAQ,CAAC,SAAS,GAAG,GAAG,GAAG,GAAG,CAAC;gBAC/B,QAAQ,CAAC,SAAS,GAAG,GAAG,CAAC;YAC1B,CAAC;QACF,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACX,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,4BAA4B,EAAE,CAAC,EAAE,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACrE,CAAC;AACF,CAAC;AAED,eAAe;AACf,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW;IAC1C,IAAI,CAAC;QACJ,MAAM,CAAC,GAAG,UAAiB,CAAC;QAC5B,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;YACnC,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC/B,OAAO,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QACzB,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;QACpB,IAAI,IAAI,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAC5B,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAClC,CAAC;QACD,IAAI,OAAO;YAAE,OAAO,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;QAChE,OAAO,EAAE,CAAC;IACX,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,wBAAwB,EAAE,CAAC,EAAE,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAChE,OAAO,EAAE,CAAC;IACX,CAAC;AACF,CAAC;AAED,+BAA+B;AAC/B,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,UAAiB,EAAE,UAAkB;IAC3E,IAAI,OAAO;QAAE,OAAO,CAAC,IAAI,CAAC,4CAA4C,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IACnH,KAAK,MAAM,MAAM,IAAI,UAAU,EAAE,CAAC;QACjC,IAAI,CAAC;YACJ,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,YAAY,IAAI,EAAE,CAAC;YACzD,IAAI,CAAC,OAAO,IAAI,CAAC,UAAU;gBAAE,SAAS;YAEtC,qDAAqD;YACrD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YACjD,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;YAC9C,MAAM,GAAG,GAAG,GAAG,UAAU,GAAG,OAAO,GAAG,GAAG,cAAc,SAAS,EAAE,CAAC;YAEnE,IAAI,OAAO;gBAAE,OAAO,CAAC,IAAI,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAC;YACzD,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;YACrC,IAAI,OAAO,EAAE,CAAC;gBACb,YAAY,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC;QACF,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,yCAAyC,EAAE,CAAC,EAAE,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAClF,CAAC;IACF,CAAC;AACF,CAAC;AAED,2BAA2B;AAC3B,MAAM,UAAU,eAAe,CAAC,OAAe;IAC9C,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnD,YAAY,CAAC,OAAO,CAAC,CAAC;IACvB,CAAC;AACF,CAAC"}
|
package/hmr/client/index.js
CHANGED
|
@@ -43,10 +43,76 @@ function ensureCoreAliasesOnGlobalThis() {
|
|
|
43
43
|
}
|
|
44
44
|
// Apply once on module evaluation
|
|
45
45
|
ensureCoreAliasesOnGlobalThis();
|
|
46
|
+
function getHmrOverlayApi() {
|
|
47
|
+
try {
|
|
48
|
+
return globalThis.__NS_HMR_DEV_OVERLAY__ || null;
|
|
49
|
+
}
|
|
50
|
+
catch { }
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
function setConnectionOverlayStage(stage, detail) {
|
|
54
|
+
try {
|
|
55
|
+
const api = getHmrOverlayApi();
|
|
56
|
+
if (api && typeof api.setConnectionStage === 'function') {
|
|
57
|
+
api.setConnectionStage(stage, { detail });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch { }
|
|
61
|
+
}
|
|
62
|
+
function hideConnectionOverlay() {
|
|
63
|
+
try {
|
|
64
|
+
const api = getHmrOverlayApi();
|
|
65
|
+
if (api && typeof api.hide === 'function') {
|
|
66
|
+
api.hide('healthy');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch { }
|
|
70
|
+
}
|
|
71
|
+
let connectionOverlayTimer = null;
|
|
72
|
+
let connectionOverlayVisible = false;
|
|
73
|
+
let hasOpenedHmrSocket = false;
|
|
74
|
+
let awaitingHealthyHmrMessage = false;
|
|
75
|
+
let pendingConnectionOverlayStage = 'connecting';
|
|
76
|
+
let pendingConnectionOverlayDetail = '';
|
|
77
|
+
function clearConnectionOverlayTimer() {
|
|
78
|
+
if (connectionOverlayTimer) {
|
|
79
|
+
clearTimeout(connectionOverlayTimer);
|
|
80
|
+
connectionOverlayTimer = null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function showConnectionOverlayNow(stage, detail) {
|
|
84
|
+
pendingConnectionOverlayStage = stage;
|
|
85
|
+
pendingConnectionOverlayDetail = detail || '';
|
|
86
|
+
connectionOverlayVisible = true;
|
|
87
|
+
setConnectionOverlayStage(stage, detail);
|
|
88
|
+
}
|
|
89
|
+
function scheduleConnectionOverlay(stage, detail, delayMs = 1200) {
|
|
90
|
+
pendingConnectionOverlayStage = stage;
|
|
91
|
+
pendingConnectionOverlayDetail = detail || '';
|
|
92
|
+
clearConnectionOverlayTimer();
|
|
93
|
+
connectionOverlayTimer = setTimeout(() => {
|
|
94
|
+
showConnectionOverlayNow(pendingConnectionOverlayStage, pendingConnectionOverlayDetail);
|
|
95
|
+
}, delayMs);
|
|
96
|
+
}
|
|
97
|
+
function updateConnectionOverlay(stage, detail) {
|
|
98
|
+
pendingConnectionOverlayStage = stage;
|
|
99
|
+
pendingConnectionOverlayDetail = detail || '';
|
|
100
|
+
if (connectionOverlayVisible) {
|
|
101
|
+
showConnectionOverlayNow(stage, detail);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function markHmrConnectionHealthy() {
|
|
105
|
+
awaitingHealthyHmrMessage = false;
|
|
106
|
+
clearConnectionOverlayTimer();
|
|
107
|
+
if (connectionOverlayVisible) {
|
|
108
|
+
connectionOverlayVisible = false;
|
|
109
|
+
hideConnectionOverlay();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
46
112
|
/**
|
|
47
113
|
* Flavor hooks
|
|
48
114
|
*/
|
|
49
|
-
import { installNsVueDevShims, ensureBackWrapperInstalled, getRootForVue, loadSfcComponent, ensureVueGlobals, ensurePiniaOnApp, addSfcMapping, recordVuePayloadChanges, handleVueSfcRegistry, handleVueSfcRegistryUpdate } from '../frameworks/vue/client/index.js';
|
|
115
|
+
import { installNsVueDevShims, ensureBackWrapperInstalled, getRootForVue, loadSfcComponent, ensureVueGlobals, ensurePiniaOnApp, addSfcMapping, recordVuePayloadChanges, handleVueSfcRegistry, handleVueSfcRegistryUpdate, sfcArtifactMap } from '../frameworks/vue/client/index.js';
|
|
50
116
|
import { handleAngularHotUpdateMessage, installAngularHmrClientHooks } from '../frameworks/angular/client/index.js';
|
|
51
117
|
switch (__NS_TARGET_FLAVOR__) {
|
|
52
118
|
case 'vue':
|
|
@@ -60,6 +126,10 @@ switch (__NS_TARGET_FLAVOR__) {
|
|
|
60
126
|
let initialMounted = !!globalThis.__NS_HMR_BOOT_COMPLETE__;
|
|
61
127
|
// Prevent duplicate initial-mount scheduling across rapid full-graph broadcasts and re-evaluations
|
|
62
128
|
let initialMounting = !!globalThis.__NS_HMR_INITIAL_MOUNT_IN_PROGRESS__;
|
|
129
|
+
// Track whether the first full-graph has been received. Before the full-graph,
|
|
130
|
+
// delta messages are just the server discovering modules during initial boot —
|
|
131
|
+
// NOT actual code changes. Re-imports must be gated behind this flag.
|
|
132
|
+
let hasReceivedFullGraph = false;
|
|
63
133
|
// TypeScript flavor: track registry modules and inferred main id
|
|
64
134
|
let tsModuleSet = null;
|
|
65
135
|
let tsMainId = null;
|
|
@@ -94,10 +164,14 @@ function applyFullGraph(payload) {
|
|
|
94
164
|
console.log('[hmr][graph] full graph applied version', getGraphVersion(), 'modules=', graph.size);
|
|
95
165
|
// Guarded initial mount rescue: if app hasn't replaced the placeholder shortly after graph arrives,
|
|
96
166
|
// perform a one-time mount. This waits briefly to let the app's main entry start() run first to avoid double mounts.
|
|
167
|
+
// TypeScript flavor: skip the rescue entirely. The HTTP boot will load main.ts which
|
|
168
|
+
// calls Application.run() (patched to resetRootView) — the rescue is redundant and
|
|
169
|
+
// causes a double-mount race (rescue fires at 450ms, then main.ts fires ~1s later,
|
|
170
|
+
// causing a visual flash and leaving the app in an inconsistent state).
|
|
97
171
|
try {
|
|
98
172
|
const g = globalThis;
|
|
99
173
|
const bootDone = !!g.__NS_HMR_BOOT_COMPLETE__;
|
|
100
|
-
if (!bootDone && !initialMounted && !initialMounting && !g.__NS_HMR_RESCUE_SCHEDULED__) {
|
|
174
|
+
if (!bootDone && !initialMounted && !initialMounting && !g.__NS_HMR_RESCUE_SCHEDULED__ && __NS_TARGET_FLAVOR__ !== 'typescript') {
|
|
101
175
|
// simple snapshot helpers
|
|
102
176
|
const getTopmost = () => {
|
|
103
177
|
try {
|
|
@@ -227,6 +301,19 @@ function applyFullGraph(payload) {
|
|
|
227
301
|
}
|
|
228
302
|
}
|
|
229
303
|
}
|
|
304
|
+
// Fallback: when the module graph is empty (Vite 7+ may not populate it
|
|
305
|
+
// before the first full-graph broadcast), check the SFC artifact registry
|
|
306
|
+
// which is populated from the ns:vue-sfc-registry message.
|
|
307
|
+
if (!candidate && sfcArtifactMap.size > 0) {
|
|
308
|
+
for (const id of sfcArtifactMap.keys()) {
|
|
309
|
+
if (/\.vue$/i.test(id)) {
|
|
310
|
+
candidate = id;
|
|
311
|
+
if (VERBOSE)
|
|
312
|
+
console.log('[hmr][init] rescue candidate from SFC registry:', id);
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
230
317
|
break;
|
|
231
318
|
}
|
|
232
319
|
}
|
|
@@ -301,6 +388,17 @@ function applyFullGraph(payload) {
|
|
|
301
388
|
}
|
|
302
389
|
}
|
|
303
390
|
}
|
|
391
|
+
// Fallback: SFC registry (same as rescue mount above)
|
|
392
|
+
if (!candidate && sfcArtifactMap.size > 0) {
|
|
393
|
+
for (const id of sfcArtifactMap.keys()) {
|
|
394
|
+
if (/\.vue$/i.test(id)) {
|
|
395
|
+
candidate = id;
|
|
396
|
+
if (VERBOSE)
|
|
397
|
+
console.log('[hmr][init] initial mount candidate from SFC registry:', id);
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
304
402
|
break;
|
|
305
403
|
}
|
|
306
404
|
case 'typescript': {
|
|
@@ -445,6 +543,16 @@ function applyDelta(payload) {
|
|
|
445
543
|
console.log('[hmr][graph] delta applied newVersion', getGraphVersion(), 'changed=', (payload.changed || []).length, 'removed=', (payload.removed || []).length, 'baseVersion=', payload.baseVersion);
|
|
446
544
|
// Queue evaluation of changed modules (placeholder pipeline)
|
|
447
545
|
if (payload.changed?.length) {
|
|
546
|
+
// Gate: Before the first full-graph is received, delta messages are the server
|
|
547
|
+
// discovering modules during initial boot — NOT actual code changes. The entry-runtime
|
|
548
|
+
// already loaded all modules; re-importing them would cause duplicate Application.run(),
|
|
549
|
+
// router initialization, and modal conflicts. Only queue re-imports after the first
|
|
550
|
+
// full-graph confirms the graph is synced and subsequent deltas are real changes.
|
|
551
|
+
if (!hasReceivedFullGraph) {
|
|
552
|
+
if (VERBOSE)
|
|
553
|
+
console.log('[hmr][delta] skipping re-import queue (initial graph build, no full-graph yet)');
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
448
556
|
// HARD SUPPRESS: the very first delta (baseVersion 0) commonly includes the app main entry which is already evaluated during bootstrap.
|
|
449
557
|
// Importing it again with a cache-bust often produces a spurious module-not-found (timestamp param treated as distinct file).
|
|
450
558
|
const isInitial = payload.baseVersion === 0;
|
|
@@ -690,6 +798,163 @@ async function processQueue() {
|
|
|
690
798
|
case 'vue':
|
|
691
799
|
// Vue SFCs are handled via the registry update path; nothing to do here.
|
|
692
800
|
break;
|
|
801
|
+
case 'solid': {
|
|
802
|
+
// Solid .tsx components are self-accepting via solid-refresh's inline
|
|
803
|
+
// patchRegistry — re-importing them is sufficient. For non-component
|
|
804
|
+
// .ts utility modules, we must propagate up the import graph to find
|
|
805
|
+
// the .tsx/.jsx component boundaries and re-import those so their
|
|
806
|
+
// solid-refresh proxies pick up the new dependency values.
|
|
807
|
+
try {
|
|
808
|
+
// Build reverse index: dep id → list of importer ids
|
|
809
|
+
const reverseIndex = new Map();
|
|
810
|
+
for (const [id, mod] of graph) {
|
|
811
|
+
for (const dep of mod.deps) {
|
|
812
|
+
let arr = reverseIndex.get(dep);
|
|
813
|
+
if (!arr) {
|
|
814
|
+
arr = [];
|
|
815
|
+
reverseIndex.set(dep, arr);
|
|
816
|
+
}
|
|
817
|
+
arr.push(id);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
// BFS from each non-tsx changed module up to tsx/jsx boundaries
|
|
821
|
+
const boundaries = new Set();
|
|
822
|
+
for (const id of drained) {
|
|
823
|
+
if (/\.(tsx|jsx)$/i.test(id))
|
|
824
|
+
continue; // already self-accepting
|
|
825
|
+
const visited = new Set();
|
|
826
|
+
const queue = [id];
|
|
827
|
+
while (queue.length) {
|
|
828
|
+
const cur = queue.shift();
|
|
829
|
+
if (visited.has(cur))
|
|
830
|
+
continue;
|
|
831
|
+
visited.add(cur);
|
|
832
|
+
const importers = reverseIndex.get(cur);
|
|
833
|
+
if (!importers)
|
|
834
|
+
continue;
|
|
835
|
+
for (const imp of importers) {
|
|
836
|
+
if (/\.(tsx|jsx)$/i.test(imp)) {
|
|
837
|
+
boundaries.add(imp);
|
|
838
|
+
}
|
|
839
|
+
else {
|
|
840
|
+
queue.push(imp);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
// Re-import each boundary so solid-refresh patchRegistry fires.
|
|
846
|
+
// For route files (TanStack Router), capture the new Route export
|
|
847
|
+
// and patch the router's existing route with the fresh loader.
|
|
848
|
+
let routesPatchCount = 0;
|
|
849
|
+
let discoveredRouter = null;
|
|
850
|
+
// Discover router: try __ns_router global (set by createNativeScriptRouter),
|
|
851
|
+
// then scan globalThis for any router-shaped object with routesById.
|
|
852
|
+
const findRouter = () => {
|
|
853
|
+
if (discoveredRouter)
|
|
854
|
+
return discoveredRouter;
|
|
855
|
+
const g = globalThis;
|
|
856
|
+
if (g.__ns_router?.routesById)
|
|
857
|
+
return (discoveredRouter = g.__ns_router);
|
|
858
|
+
// Fallback: scan common global keys for router
|
|
859
|
+
for (const key of ['__ns_router', 'router', '__router']) {
|
|
860
|
+
if (g[key]?.routesById && g[key]?.invalidate)
|
|
861
|
+
return (discoveredRouter = g[key]);
|
|
862
|
+
}
|
|
863
|
+
return null;
|
|
864
|
+
};
|
|
865
|
+
// Convert boundary file path to TanStack Router fullPath.
|
|
866
|
+
// e.g. /src/routes/posts.$postId.tsx → /posts/$postId
|
|
867
|
+
const boundaryToFullPath = (bid) => {
|
|
868
|
+
const m = bid.match(/\/src\/routes\/(.+)\.(tsx|jsx|ts|js)$/i);
|
|
869
|
+
if (!m)
|
|
870
|
+
return null;
|
|
871
|
+
let p = m[1];
|
|
872
|
+
// Replace dots between segments with slashes (posts.$postId → posts/$postId)
|
|
873
|
+
p = p.replace(/\./g, '/');
|
|
874
|
+
// Handle index files
|
|
875
|
+
if (p === 'index')
|
|
876
|
+
return '/';
|
|
877
|
+
if (p.endsWith('/index'))
|
|
878
|
+
p = p.slice(0, -6);
|
|
879
|
+
// Strip leading - (TanStack pathless layout convention)
|
|
880
|
+
p = p.replace(/(^|\/)-([\w])/g, '$1$2');
|
|
881
|
+
return '/' + p;
|
|
882
|
+
};
|
|
883
|
+
// Find existing route by fullPath (since new Route has no id yet)
|
|
884
|
+
const findRouteByFullPath = (router, fp) => {
|
|
885
|
+
if (!router?.routesById)
|
|
886
|
+
return null;
|
|
887
|
+
for (const rid of Object.keys(router.routesById)) {
|
|
888
|
+
const r = router.routesById[rid];
|
|
889
|
+
if (r?.fullPath === fp)
|
|
890
|
+
return r;
|
|
891
|
+
}
|
|
892
|
+
return null;
|
|
893
|
+
};
|
|
894
|
+
for (const id of boundaries) {
|
|
895
|
+
if (seen.has(id))
|
|
896
|
+
continue;
|
|
897
|
+
try {
|
|
898
|
+
const spec = normalizeSpec(id);
|
|
899
|
+
const url = await requestModuleFromServer(spec);
|
|
900
|
+
if (!url)
|
|
901
|
+
continue;
|
|
902
|
+
if (VERBOSE)
|
|
903
|
+
console.log('[hmr][solid] propagated to boundary', { id, url });
|
|
904
|
+
const mod = await import(/* @vite-ignore */ url);
|
|
905
|
+
// Patch TanStack Router route loaders
|
|
906
|
+
try {
|
|
907
|
+
const newRoute = mod?.Route;
|
|
908
|
+
if (newRoute?.options?.loader) {
|
|
909
|
+
const router = findRouter();
|
|
910
|
+
const fullPath = boundaryToFullPath(id);
|
|
911
|
+
if (VERBOSE)
|
|
912
|
+
console.log('[hmr][solid][diag] route patch attempt', { id, fullPath, hasRouter: !!router, routesByIdKeys: router?.routesById ? Object.keys(router.routesById) : 'none' });
|
|
913
|
+
const existingRoute = fullPath && router ? findRouteByFullPath(router, fullPath) : null;
|
|
914
|
+
if (existingRoute?.options) {
|
|
915
|
+
existingRoute.options.loader = newRoute.options.loader;
|
|
916
|
+
if (newRoute.options.component)
|
|
917
|
+
existingRoute.options.component = newRoute.options.component;
|
|
918
|
+
routesPatchCount++;
|
|
919
|
+
if (VERBOSE)
|
|
920
|
+
console.log('[hmr][solid] patched route loader', existingRoute.id, 'fullPath=', fullPath);
|
|
921
|
+
}
|
|
922
|
+
else if (VERBOSE) {
|
|
923
|
+
console.log('[hmr][solid] no matching route for fullPath', fullPath);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
catch (e) {
|
|
928
|
+
if (VERBOSE)
|
|
929
|
+
console.warn('[hmr][solid] route patch error', id, e);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
catch (e) {
|
|
933
|
+
if (VERBOSE)
|
|
934
|
+
console.warn('[hmr][solid] boundary re-import failed', id, e);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
// Route loaders were patched with fresh closures. The data is
|
|
938
|
+
// correct in router.state.matches[].loaderData (confirmed via
|
|
939
|
+
// diagnostics), but the currently visible page doesn't re-render
|
|
940
|
+
// because the NativeScriptRouterProvider's Solid store flush only
|
|
941
|
+
// fires through history.subscribe. TODO: find the right mechanism
|
|
942
|
+
// to trigger a Solid reactive update for the active match stores.
|
|
943
|
+
if (routesPatchCount > 0 && VERBOSE) {
|
|
944
|
+
console.log('[hmr][solid] patched', routesPatchCount, 'route loaders (data correct in match state, pending UI refresh mechanism)');
|
|
945
|
+
}
|
|
946
|
+
if (VERBOSE) {
|
|
947
|
+
if (boundaries.size)
|
|
948
|
+
console.log('[hmr][solid] propagated non-component change to', boundaries.size, 'boundaries', Array.from(boundaries));
|
|
949
|
+
console.log('[hmr][queue] Solid: modules re-imported, solid-refresh handles reactive update', drained);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
catch (e) {
|
|
953
|
+
if (VERBOSE)
|
|
954
|
+
console.warn('[hmr][solid] propagation failed', e);
|
|
955
|
+
}
|
|
956
|
+
break;
|
|
957
|
+
}
|
|
693
958
|
case 'typescript': {
|
|
694
959
|
// For TS apps, always reset back to the conventional app root.
|
|
695
960
|
// This preserves the shell (Frame, ActionBar, etc.) that the app's
|
|
@@ -702,9 +967,124 @@ async function processQueue() {
|
|
|
702
967
|
console.warn('[hmr][queue] TS flavor: Application.resetRootView unavailable; skipping UI refresh');
|
|
703
968
|
break;
|
|
704
969
|
}
|
|
970
|
+
// Re-fetch changed XML/CSS files and update the bundled module registry
|
|
971
|
+
// so Builder.createViewFromEntry picks up fresh content.
|
|
972
|
+
const rawAssetIds = drained.filter((id) => /\.(xml|css|scss|sass|less)$/i.test(id));
|
|
973
|
+
if (rawAssetIds.length && typeof g.registerModule === 'function') {
|
|
974
|
+
const origin = getHttpOriginForVite() || deriveHttpOrigin(getHMRWsUrl());
|
|
975
|
+
if (origin) {
|
|
976
|
+
for (const id of rawAssetIds) {
|
|
977
|
+
try {
|
|
978
|
+
const spec = normalizeSpec(id);
|
|
979
|
+
// Fetch the raw file content directly from Vite's dev server.
|
|
980
|
+
// Use the project-relative path which Vite serves as static files.
|
|
981
|
+
const fetchUrl = origin + (spec.startsWith('/') ? spec : '/' + spec);
|
|
982
|
+
if (VERBOSE)
|
|
983
|
+
console.log('[hmr][queue] fetching raw asset', { id, fetchUrl });
|
|
984
|
+
const resp = await fetch(fetchUrl);
|
|
985
|
+
if (resp.ok) {
|
|
986
|
+
const rawContent = await resp.text();
|
|
987
|
+
// Register under all nickname variants the module registry uses.
|
|
988
|
+
// The bundler context registers XML as e.g., './main-page.xml' and 'main-page.xml'
|
|
989
|
+
const appVirtual = APP_VIRTUAL_WITH_SLASH.replace(/^\//, '');
|
|
990
|
+
let relPath = spec.startsWith('/') ? spec.slice(1) : spec;
|
|
991
|
+
if (relPath.startsWith(appVirtual))
|
|
992
|
+
relPath = relPath.slice(appVirtual.length);
|
|
993
|
+
const nicknames = ['./' + relPath, relPath];
|
|
994
|
+
// Also add without extension for CSS
|
|
995
|
+
const extIdx = relPath.lastIndexOf('.');
|
|
996
|
+
if (extIdx > 0) {
|
|
997
|
+
const baseName = relPath.slice(0, extIdx);
|
|
998
|
+
if (!relPath.endsWith('.xml'))
|
|
999
|
+
nicknames.push(baseName, './' + baseName);
|
|
1000
|
+
}
|
|
1001
|
+
for (const name of nicknames) {
|
|
1002
|
+
if (VERBOSE)
|
|
1003
|
+
console.log('[hmr][queue] re-registering module', name);
|
|
1004
|
+
g.registerModule(name, () => rawContent);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
else if (VERBOSE) {
|
|
1008
|
+
console.warn('[hmr][queue] raw asset fetch failed', id, resp.status);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
catch (e) {
|
|
1012
|
+
if (VERBOSE)
|
|
1013
|
+
console.warn('[hmr][queue] raw asset refresh failed for', id, e);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
// Determine if we can navigate in-place to a changed page
|
|
1019
|
+
// instead of resetting all the way back to app-root.
|
|
1020
|
+
// This keeps the user on the page they're editing for faster iteration.
|
|
1021
|
+
const changedXmlPages = drained
|
|
1022
|
+
.filter((id) => /\.xml$/i.test(id))
|
|
1023
|
+
.map((id) => {
|
|
1024
|
+
const spec = normalizeSpec(id);
|
|
1025
|
+
const appVirtual = APP_VIRTUAL_WITH_SLASH.replace(/^\//, '');
|
|
1026
|
+
let relPath = spec.startsWith('/') ? spec.slice(1) : spec;
|
|
1027
|
+
if (relPath.startsWith(appVirtual))
|
|
1028
|
+
relPath = relPath.slice(appVirtual.length);
|
|
1029
|
+
// Strip .xml extension to get the moduleName (e.g., 'pages/status-bar')
|
|
1030
|
+
return relPath.replace(/\.xml$/i, '');
|
|
1031
|
+
})
|
|
1032
|
+
.filter((m) => m && m !== 'app-root');
|
|
1033
|
+
// Resolve the topmost Frame from the bundled realm.
|
|
1034
|
+
// Frame.topmost() relies on an internal frameStack array, so we must
|
|
1035
|
+
// call it on the bundled-realm class. Multiple strategies to find it:
|
|
1036
|
+
const FrameClass = getCore('Frame') || g.Frame;
|
|
1037
|
+
let topFrame = null;
|
|
1038
|
+
// 1) Try the vendor-realm static topmost()
|
|
1039
|
+
try {
|
|
1040
|
+
topFrame = FrameClass?.topmost?.();
|
|
1041
|
+
}
|
|
1042
|
+
catch { }
|
|
1043
|
+
// 2) Try getting the root view from Application — if it's a Frame, use it
|
|
1044
|
+
if (!topFrame) {
|
|
1045
|
+
try {
|
|
1046
|
+
const rootView = App.getRootView?.() || App._rootView;
|
|
1047
|
+
if (rootView) {
|
|
1048
|
+
// rootView could be a Frame itself, or contain a Frame
|
|
1049
|
+
const isFrame = rootView.constructor?.name === 'Frame' || rootView.navigate;
|
|
1050
|
+
if (isFrame) {
|
|
1051
|
+
topFrame = rootView;
|
|
1052
|
+
}
|
|
1053
|
+
else if (rootView.getChildAt) {
|
|
1054
|
+
// Walk direct children looking for a Frame
|
|
1055
|
+
for (let i = 0; i < (rootView.getChildrenCount?.() || 0); i++) {
|
|
1056
|
+
const child = rootView.getChildAt(i);
|
|
1057
|
+
if (child?.constructor?.name === 'Frame' || child?.navigate) {
|
|
1058
|
+
topFrame = child;
|
|
1059
|
+
break;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
catch { }
|
|
1066
|
+
}
|
|
705
1067
|
if (VERBOSE)
|
|
706
|
-
console.log('[hmr][queue] TS
|
|
707
|
-
|
|
1068
|
+
console.log('[hmr][queue] TS: changedXmlPages=', changedXmlPages, 'topFrame=', !!topFrame);
|
|
1069
|
+
if (changedXmlPages.length > 0 && topFrame) {
|
|
1070
|
+
// Navigate the current frame to the changed page directly.
|
|
1071
|
+
// Use the last changed XML page (most specific).
|
|
1072
|
+
const moduleName = changedXmlPages[changedXmlPages.length - 1];
|
|
1073
|
+
if (VERBOSE)
|
|
1074
|
+
console.log('[hmr][queue] TS: navigating in-place to', moduleName);
|
|
1075
|
+
try {
|
|
1076
|
+
topFrame.navigate({ moduleName, clearHistory: false, animated: false });
|
|
1077
|
+
}
|
|
1078
|
+
catch (navErr) {
|
|
1079
|
+
console.warn('[hmr][queue] TS flavor: in-place navigate failed, falling back to resetRootView', navErr);
|
|
1080
|
+
App.resetRootView({ moduleName: 'app-root' });
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
else {
|
|
1084
|
+
if (VERBOSE)
|
|
1085
|
+
console.log('[hmr][queue] TS flavor: resetRootView(app-root) after changes');
|
|
1086
|
+
App.resetRootView({ moduleName: 'app-root' });
|
|
1087
|
+
}
|
|
708
1088
|
}
|
|
709
1089
|
catch (e) {
|
|
710
1090
|
console.warn('[hmr][queue] TS flavor: resetRootView(app-root) failed', e);
|
|
@@ -752,6 +1132,7 @@ function connectHmr() {
|
|
|
752
1132
|
console.log('[hmr-client] Already connecting to HMR WebSocket, skipping');
|
|
753
1133
|
return;
|
|
754
1134
|
}
|
|
1135
|
+
const overlayStage = hasOpenedHmrSocket ? 'reconnecting' : 'connecting';
|
|
755
1136
|
const baseUrl = getHMRWsUrl() || 'ws://localhost:5173/ns-hmr';
|
|
756
1137
|
const buildCandidates = (url) => {
|
|
757
1138
|
let candidates = [];
|
|
@@ -811,10 +1192,19 @@ function connectHmr() {
|
|
|
811
1192
|
let idx = 0;
|
|
812
1193
|
const tryNext = () => {
|
|
813
1194
|
if (idx >= candidates.length) {
|
|
1195
|
+
showConnectionOverlayNow('offline', 'Waiting for the Vite websocket to come back.');
|
|
814
1196
|
console.warn('[hmr-client] All WS candidates failed:', candidates.join(', '));
|
|
1197
|
+
setTimeout(connectHmr, 1500);
|
|
815
1198
|
return;
|
|
816
1199
|
}
|
|
817
1200
|
const url = candidates[idx++];
|
|
1201
|
+
const connectionDetail = `${overlayStage === 'reconnecting' ? 'Retrying' : 'Opening'} ${url}`;
|
|
1202
|
+
if (connectionOverlayVisible) {
|
|
1203
|
+
updateConnectionOverlay(overlayStage, connectionDetail);
|
|
1204
|
+
}
|
|
1205
|
+
else {
|
|
1206
|
+
scheduleConnectionOverlay(overlayStage, connectionDetail);
|
|
1207
|
+
}
|
|
818
1208
|
try {
|
|
819
1209
|
if (__NS_ENV_VERBOSE__)
|
|
820
1210
|
console.log('[hmr-client] Connecting to HMR WebSocket:', url);
|
|
@@ -836,6 +1226,12 @@ function connectHmr() {
|
|
|
836
1226
|
sock.onopen = () => {
|
|
837
1227
|
opened = true;
|
|
838
1228
|
clearTimeout(timeout);
|
|
1229
|
+
clearConnectionOverlayTimer();
|
|
1230
|
+
hasOpenedHmrSocket = true;
|
|
1231
|
+
awaitingHealthyHmrMessage = true;
|
|
1232
|
+
if (connectionOverlayVisible) {
|
|
1233
|
+
showConnectionOverlayNow('synchronizing', 'Connected. Synchronizing the HMR graph.');
|
|
1234
|
+
}
|
|
839
1235
|
VERBOSE && console.log('[hmr-client] Connected to HMR WebSocket');
|
|
840
1236
|
};
|
|
841
1237
|
sock.onmessage = handleHmrMessage;
|
|
@@ -854,6 +1250,7 @@ function connectHmr() {
|
|
|
854
1250
|
else {
|
|
855
1251
|
if (VERBOSE)
|
|
856
1252
|
console.log('[hmr-client] WebSocket closed (code', ev?.code, '), will reconnect…');
|
|
1253
|
+
scheduleConnectionOverlay('reconnecting', 'The websocket closed. Waiting to reconnect.', 700);
|
|
857
1254
|
// try to reconnect with full candidate list again
|
|
858
1255
|
setTimeout(connectHmr, 1000);
|
|
859
1256
|
}
|
|
@@ -874,6 +1271,9 @@ async function handleHmrMessage(ev) {
|
|
|
874
1271
|
catch {
|
|
875
1272
|
return;
|
|
876
1273
|
}
|
|
1274
|
+
if (awaitingHealthyHmrMessage && msg) {
|
|
1275
|
+
markHmrConnectionHealthy();
|
|
1276
|
+
}
|
|
877
1277
|
// Notify optional app-level hook after an HMR batch is applied.
|
|
878
1278
|
function notifyAppHmrUpdate(kind, changedIds) {
|
|
879
1279
|
try {
|
|
@@ -896,6 +1296,34 @@ async function handleHmrMessage(ev) {
|
|
|
896
1296
|
const prevGraph = new Map(graph);
|
|
897
1297
|
setGraphVersion(Number(msg.version || getGraphVersion() || 0));
|
|
898
1298
|
applyFullGraph(msg);
|
|
1299
|
+
hasReceivedFullGraph = true;
|
|
1300
|
+
// Gate: On first boot, the entry-runtime handles all initial module loading
|
|
1301
|
+
// (with the import map already configured). Don't re-import here — the graph
|
|
1302
|
+
// is stored above for future HMR delta comparisons, but modules are already
|
|
1303
|
+
// loaded correctly via the entry-runtime boot sequence.
|
|
1304
|
+
//
|
|
1305
|
+
// Two cases to catch:
|
|
1306
|
+
// 1. Boot still in progress (__NS_HMR_BOOT_COMPLETE__ is false)
|
|
1307
|
+
// 2. Boot already finished but this is the FIRST full-graph (prevGraph was
|
|
1308
|
+
// empty). The WebSocket often connects after entry-runtime finishes, so
|
|
1309
|
+
// boot is "complete" but we still shouldn't re-import — all modules were
|
|
1310
|
+
// just loaded fresh. Only re-import on subsequent full-graphs (reconnect
|
|
1311
|
+
// scenarios) where prevGraph already has entries.
|
|
1312
|
+
if (!globalThis.__NS_HMR_BOOT_COMPLETE__) {
|
|
1313
|
+
if (VERBOSE)
|
|
1314
|
+
console.info('[hmr][full-graph] skipping initial re-import (boot in progress)');
|
|
1315
|
+
const fullIds = Array.isArray(msg.modules) ? msg.modules.map((m) => m?.id).filter(Boolean) : [];
|
|
1316
|
+
notifyAppHmrUpdate('full-graph', fullIds);
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
if (prevGraph.size === 0) {
|
|
1320
|
+
if (VERBOSE)
|
|
1321
|
+
console.info('[hmr][full-graph] skipping re-import on first graph after boot (modules already fresh)');
|
|
1322
|
+
const fullIds = Array.isArray(msg.modules) ? msg.modules.map((m) => m?.id).filter(Boolean) : [];
|
|
1323
|
+
notifyAppHmrUpdate('full-graph', fullIds);
|
|
1324
|
+
return;
|
|
1325
|
+
}
|
|
1326
|
+
// Reconnect / resync case — re-import changed modules as normal.
|
|
899
1327
|
// In some cases (e.g. server chooses full-graph resync / page reload), we won't
|
|
900
1328
|
// receive a delta queue to re-import changed TS modules. Without re-import,
|
|
901
1329
|
// HTTP ESM caching means module bodies (and side effects) won't re-run.
|
|
@@ -1455,7 +1883,27 @@ export function initHmrClient(opts) {
|
|
|
1455
1883
|
}
|
|
1456
1884
|
g.__NS_HMR_CLIENT_ACTIVE__ = true;
|
|
1457
1885
|
ensureCoreAliasesOnGlobalThis();
|
|
1458
|
-
|
|
1886
|
+
// Defer WebSocket connection until boot completes to avoid native V8 crashes
|
|
1887
|
+
// caused by concurrent WebSocket message handling + HTTP fetch during early startup.
|
|
1888
|
+
// The WebSocket is only needed for HMR updates, not the initial boot sequence.
|
|
1889
|
+
if (g.__NS_HMR_BOOT_COMPLETE__) {
|
|
1890
|
+
connectHmr();
|
|
1891
|
+
}
|
|
1892
|
+
else {
|
|
1893
|
+
const waitForBoot = () => {
|
|
1894
|
+
if (globalThis.__NS_HMR_BOOT_COMPLETE__) {
|
|
1895
|
+
if (VERBOSE)
|
|
1896
|
+
console.log('[hmr-client] boot complete, connecting HMR WebSocket');
|
|
1897
|
+
connectHmr();
|
|
1898
|
+
}
|
|
1899
|
+
else {
|
|
1900
|
+
setTimeout(waitForBoot, 100);
|
|
1901
|
+
}
|
|
1902
|
+
};
|
|
1903
|
+
if (VERBOSE)
|
|
1904
|
+
console.log('[hmr-client] deferring WebSocket connection until boot completes');
|
|
1905
|
+
setTimeout(waitForBoot, 100);
|
|
1906
|
+
}
|
|
1459
1907
|
// Best-effort: install back wrapper even before first remount; original root may be captured later
|
|
1460
1908
|
switch (__NS_TARGET_FLAVOR__) {
|
|
1461
1909
|
case 'vue':
|