@nativescript/vite 1.0.5-rc.5 → 1.0.5
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/helpers/main-entry.js +25 -1
- package/helpers/main-entry.js.map +1 -1
- package/hmr/client/index.js +132 -398
- package/hmr/client/index.js.map +1 -1
- package/hmr/client/utils.d.ts +0 -2
- package/hmr/client/utils.js +25 -179
- package/hmr/client/utils.js.map +1 -1
- package/hmr/entry-runtime.js +18 -1
- package/hmr/entry-runtime.js.map +1 -1
- package/hmr/frameworks/vue/client/index.js +1 -5
- package/hmr/frameworks/vue/client/index.js.map +1 -1
- package/hmr/server/vite-plugin.js +32 -4
- package/hmr/server/vite-plugin.js.map +1 -1
- package/hmr/server/websocket.js +52 -173
- package/hmr/server/websocket.js.map +1 -1
- package/hmr/shared/runtime/hooks.d.ts +6 -3
- package/hmr/shared/runtime/hooks.js +56 -19
- package/hmr/shared/runtime/hooks.js.map +1 -1
- package/hmr/shared/runtime/http-only-boot.js +20 -3
- package/hmr/shared/runtime/http-only-boot.js.map +1 -1
- package/index.d.ts +0 -1
- package/index.js +0 -1
- package/index.js.map +1 -1
- package/package.json +1 -1
package/hmr/client/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Always resolve core classes and Application from the vendor realm or globalThis at runtime.
|
|
6
6
|
* The HMR client is evaluated via HTTP ESM on device; static imports would create secondary instances.
|
|
7
7
|
*/
|
|
8
|
-
import { setHMRWsUrl, getHMRWsUrl, pendingModuleFetches, deriveHttpOrigin, setHttpOriginForVite, moduleFetchCache, requestModuleFromServer, getHttpOriginForVite, normalizeSpec, hmrMetrics, graph, setGraphVersion, getGraphVersion, getCurrentApp, getRootFrame, setCurrentApp, setRootFrame, getCore
|
|
8
|
+
import { setHMRWsUrl, getHMRWsUrl, pendingModuleFetches, deriveHttpOrigin, setHttpOriginForVite, moduleFetchCache, requestModuleFromServer, getHttpOriginForVite, normalizeSpec, hmrMetrics, graph, setGraphVersion, getGraphVersion, getCurrentApp, getRootFrame, setCurrentApp, setRootFrame, getCore } from './utils.js';
|
|
9
9
|
import { handleCssUpdates } from './css-handler.js';
|
|
10
10
|
const VERBOSE = typeof __NS_ENV_VERBOSE__ !== 'undefined' && __NS_ENV_VERBOSE__;
|
|
11
11
|
const APP_ROOT_VIRTUAL = typeof __NS_APP_ROOT_VIRTUAL__ === 'string' && __NS_APP_ROOT_VIRTUAL__ ? __NS_APP_ROOT_VIRTUAL__ : '/src';
|
|
@@ -14,14 +14,6 @@ const APP_MAIN_ENTRY_SPEC = `${APP_VIRTUAL_WITH_SLASH}app.ts`;
|
|
|
14
14
|
// Policy: by default, let the app's own main entry mount initially; HMR client handles updates/remounts only.
|
|
15
15
|
// Flip this to true via global __NS_HMR_ALLOW_INITIAL_MOUNT__ if you need the client to perform the first mount.
|
|
16
16
|
const ALLOW_INITIAL_MOUNT = !!globalThis.__NS_HMR_ALLOW_INITIAL_MOUNT__;
|
|
17
|
-
// When verbose mode is enabled, also enable runtime nav diagnostics so /ns/rt logs are visible
|
|
18
|
-
try {
|
|
19
|
-
if (VERBOSE) {
|
|
20
|
-
globalThis.__NS_DEV_LOGS__ = true;
|
|
21
|
-
globalThis.__NS_VERBOSE_RT_NAV__ = true;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
catch { }
|
|
25
17
|
// Ensure core aliases are present on globalThis early so that /ns/rt exports resolve to functions
|
|
26
18
|
// before any SFCs are evaluated during HTTP-only dev boot.
|
|
27
19
|
function ensureCoreAliasesOnGlobalThis() {
|
|
@@ -46,46 +38,11 @@ function ensureCoreAliasesOnGlobalThis() {
|
|
|
46
38
|
g.Page = P;
|
|
47
39
|
}
|
|
48
40
|
catch { }
|
|
49
|
-
// Optional diagnostics: compare with vendor realm if available
|
|
50
|
-
if (VERBOSE) {
|
|
51
|
-
let vF, vA, vP;
|
|
52
|
-
try {
|
|
53
|
-
const reg = g.__nsVendorRegistry;
|
|
54
|
-
const vmod = reg?.get ? reg.get('@nativescript/core') : undefined;
|
|
55
|
-
const vns = (vmod && (vmod.default || vmod)) || vmod;
|
|
56
|
-
vF = vns?.Frame;
|
|
57
|
-
vA = vns?.Application;
|
|
58
|
-
vP = vns?.Page;
|
|
59
|
-
}
|
|
60
|
-
catch { }
|
|
61
|
-
try {
|
|
62
|
-
console.log('[hmr-client] core alias status', {
|
|
63
|
-
globalHas: { Frame: !!F, Application: !!A, Page: !!P },
|
|
64
|
-
globalMethods: {
|
|
65
|
-
FrameTopmost: typeof F?.topmost === 'function',
|
|
66
|
-
AppResetRoot: typeof A?.resetRootView === 'function',
|
|
67
|
-
},
|
|
68
|
-
sameRef: {
|
|
69
|
-
Frame: F && vF ? F === vF : undefined,
|
|
70
|
-
Application: A && vA ? A === vA : undefined,
|
|
71
|
-
Page: P && vP ? P === vP : undefined,
|
|
72
|
-
},
|
|
73
|
-
ctorNames: {
|
|
74
|
-
Frame: F?.name || F?.constructor?.name,
|
|
75
|
-
Application: A?.name || A?.constructor?.name,
|
|
76
|
-
Page: P?.name || P?.constructor?.name,
|
|
77
|
-
},
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
catch { }
|
|
81
|
-
}
|
|
82
41
|
}
|
|
83
42
|
catch { }
|
|
84
43
|
}
|
|
85
44
|
// Apply once on module evaluation
|
|
86
45
|
ensureCoreAliasesOnGlobalThis();
|
|
87
|
-
// Install low-level diagnostics for navigation and root replacement to trace duplicates and state
|
|
88
|
-
installDeepDiagnostics();
|
|
89
46
|
/**
|
|
90
47
|
* Flavor hooks
|
|
91
48
|
*/
|
|
@@ -93,207 +50,12 @@ import { installNsVueDevShims, ensureBackWrapperInstalled, getRootForVue, loadSf
|
|
|
93
50
|
import { handleAngularHotUpdateMessage, installAngularHmrClientHooks } from '../frameworks/angular/client/index.js';
|
|
94
51
|
switch (__NS_TARGET_FLAVOR__) {
|
|
95
52
|
case 'vue':
|
|
96
|
-
if (VERBOSE) {
|
|
97
|
-
console.log('[hmr-client] installing nativescript-vue dev shims');
|
|
98
|
-
}
|
|
99
53
|
installNsVueDevShims();
|
|
100
54
|
break;
|
|
101
55
|
case 'angular':
|
|
102
|
-
if (VERBOSE) {
|
|
103
|
-
try {
|
|
104
|
-
console.log('[hmr-client] Initializing Angular HMR shims');
|
|
105
|
-
}
|
|
106
|
-
catch { }
|
|
107
|
-
}
|
|
108
56
|
installAngularHmrClientHooks();
|
|
109
57
|
break;
|
|
110
58
|
}
|
|
111
|
-
// Global frame diagnostics: instrument Frame.navigate and Frame.topmost to detect
|
|
112
|
-
// navigation against non-authoritative frames across the app (helps gray-screen cases)
|
|
113
|
-
try {
|
|
114
|
-
const g = globalThis;
|
|
115
|
-
const F = getCore('Frame') || g.Frame;
|
|
116
|
-
if (F && F.prototype && !g.__NS_DEV_GLOBAL_FRAME_PATCHED__) {
|
|
117
|
-
const tag = (fr) => {
|
|
118
|
-
try {
|
|
119
|
-
if (!fr)
|
|
120
|
-
return;
|
|
121
|
-
if (!fr.__ns_tag)
|
|
122
|
-
fr.__ns_tag = Math.random().toString(36).slice(2);
|
|
123
|
-
}
|
|
124
|
-
catch { }
|
|
125
|
-
};
|
|
126
|
-
const proto = F.prototype;
|
|
127
|
-
const origNav = proto.navigate;
|
|
128
|
-
if (typeof origNav === 'function') {
|
|
129
|
-
proto.navigate = function __ns_diag_nav(entry) {
|
|
130
|
-
try {
|
|
131
|
-
tag(this);
|
|
132
|
-
console.log('[diag][global][frame.navigate]', {
|
|
133
|
-
tag: this.__ns_tag,
|
|
134
|
-
type: this?.constructor?.name,
|
|
135
|
-
hasCreate: !!entry?.create,
|
|
136
|
-
clearHistory: !!entry?.clearHistory,
|
|
137
|
-
animated: !!entry?.animated,
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
catch { }
|
|
141
|
-
return origNav.apply(this, arguments);
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
const origTop = typeof F.topmost === 'function' ? F.topmost.bind(F) : null;
|
|
145
|
-
if (origTop) {
|
|
146
|
-
F.topmost = function __ns_diag_topmost() {
|
|
147
|
-
const fr = origTop();
|
|
148
|
-
try {
|
|
149
|
-
tag(fr);
|
|
150
|
-
console.log('[diag][global][Frame.topmost]', {
|
|
151
|
-
tag: fr?.__ns_tag,
|
|
152
|
-
type: fr?.constructor?.name,
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
catch { }
|
|
156
|
-
return fr;
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
try {
|
|
160
|
-
g.__NS_DEV_GLOBAL_FRAME_PATCHED__ = true;
|
|
161
|
-
}
|
|
162
|
-
catch { }
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
catch { }
|
|
166
|
-
// --- Diagnostics helpers ----------------------------------------------------
|
|
167
|
-
function summarizeNavEntry(entry) {
|
|
168
|
-
try {
|
|
169
|
-
if (!entry)
|
|
170
|
-
return { kind: 'empty' };
|
|
171
|
-
if (typeof entry === 'string')
|
|
172
|
-
return { kind: 'string', moduleName: entry };
|
|
173
|
-
const hasCreate = typeof entry.create === 'function';
|
|
174
|
-
const moduleName = entry.moduleName;
|
|
175
|
-
const clearHistory = !!entry.clearHistory;
|
|
176
|
-
const animated = entry.animated;
|
|
177
|
-
const backstackVisible = entry.backstackVisible;
|
|
178
|
-
const contextKeys = Object.keys(entry.context || {});
|
|
179
|
-
return {
|
|
180
|
-
kind: 'entry',
|
|
181
|
-
hasCreate,
|
|
182
|
-
moduleName,
|
|
183
|
-
clearHistory,
|
|
184
|
-
animated,
|
|
185
|
-
backstackVisible,
|
|
186
|
-
contextKeys,
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
catch {
|
|
190
|
-
return { kind: 'unknown' };
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
function classifyResetArg(arg) {
|
|
194
|
-
try {
|
|
195
|
-
const ctorName = String(arg?.constructor?.name || '').replace(/^_+/, '');
|
|
196
|
-
const keys = Object.keys(arg || {});
|
|
197
|
-
const hasCreate = typeof arg?.create === 'function';
|
|
198
|
-
const hasModuleName = typeof arg?.moduleName === 'string';
|
|
199
|
-
const isFrameLike = !!arg && (ctorName === 'Frame' || /^Frame(\$\d+)?$/.test(ctorName) || (typeof arg?.navigate === 'function' && typeof arg?.addChild === 'function'));
|
|
200
|
-
const isPageLike = !!arg && (ctorName === 'Page' || /^Page(\$\d+)?$/.test(ctorName) || (typeof arg?.content !== 'undefined' && typeof arg?.addChild === 'function'));
|
|
201
|
-
return {
|
|
202
|
-
ctorName,
|
|
203
|
-
keys,
|
|
204
|
-
hasCreate,
|
|
205
|
-
hasModuleName,
|
|
206
|
-
isFrameLike,
|
|
207
|
-
isPageLike,
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
catch {
|
|
211
|
-
return { ctorName: 'unknown' };
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
function installDeepDiagnostics() {
|
|
215
|
-
if (!VERBOSE)
|
|
216
|
-
return;
|
|
217
|
-
const g = globalThis;
|
|
218
|
-
try {
|
|
219
|
-
// Patch Frame.navigate to log calls and a short stack
|
|
220
|
-
const F = getCore('Frame') || g.Frame;
|
|
221
|
-
if (F?.prototype && !F.prototype.__ns_diag_nav__) {
|
|
222
|
-
const orig = F.prototype.navigate;
|
|
223
|
-
if (typeof orig === 'function') {
|
|
224
|
-
F.prototype.__ns_diag_nav__ = true;
|
|
225
|
-
// Simple duplicate navigation suppression in dev: if the same target is navigated twice within a short window, ignore the 2nd.
|
|
226
|
-
F.prototype.navigate = function (...args) {
|
|
227
|
-
try {
|
|
228
|
-
const entry = args[0];
|
|
229
|
-
const summary = summarizeNavEntry(entry);
|
|
230
|
-
const stack = (new Error().stack || '').split('\n').slice(2, 8).join('\n');
|
|
231
|
-
console.log('[diag][Frame.navigate]', {
|
|
232
|
-
frameCtor: this?.constructor?.name,
|
|
233
|
-
summary,
|
|
234
|
-
stack,
|
|
235
|
-
});
|
|
236
|
-
try {
|
|
237
|
-
const gAny = globalThis;
|
|
238
|
-
const key = JSON.stringify({
|
|
239
|
-
k: 'nav',
|
|
240
|
-
m: summary.moduleName || '',
|
|
241
|
-
c: !!summary.hasCreate,
|
|
242
|
-
ch: !!summary.clearHistory,
|
|
243
|
-
a: !!summary.animated,
|
|
244
|
-
});
|
|
245
|
-
const now = Date.now();
|
|
246
|
-
const last = gAny.__NS_DIAG_LAST_NAV__;
|
|
247
|
-
if (last && last.key === key && now - last.t < 300) {
|
|
248
|
-
console.warn('[diag][Frame.navigate] duplicate nav suppressed (dev)', { withinMs: now - last.t, key });
|
|
249
|
-
return; // suppress duplicate
|
|
250
|
-
}
|
|
251
|
-
gAny.__NS_DIAG_LAST_NAV__ = { key, t: now };
|
|
252
|
-
}
|
|
253
|
-
catch { }
|
|
254
|
-
}
|
|
255
|
-
catch { }
|
|
256
|
-
return orig.apply(this, args);
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
catch { }
|
|
262
|
-
try {
|
|
263
|
-
// Wrap Application.resetRootView to log argument classification and stack
|
|
264
|
-
const App = getCore('Application') || g.Application;
|
|
265
|
-
const proto = App && Object.getPrototypeOf(App);
|
|
266
|
-
const orig = (App && App.resetRootView) || (proto && proto.resetRootView);
|
|
267
|
-
if (typeof orig === 'function' && !g.__NS_DIAG_RESET_WRAPPED__) {
|
|
268
|
-
const wrapped = function __ns_diag_resetRootView(entry) {
|
|
269
|
-
try {
|
|
270
|
-
const classification = classifyResetArg(entry);
|
|
271
|
-
const stack = (new Error().stack || '').split('\n').slice(2, 8).join('\n');
|
|
272
|
-
console.log('[diag][Application.resetRootView]', {
|
|
273
|
-
classification,
|
|
274
|
-
stack,
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
catch { }
|
|
278
|
-
return orig.call(this, entry);
|
|
279
|
-
};
|
|
280
|
-
try {
|
|
281
|
-
App.resetRootView = wrapped;
|
|
282
|
-
}
|
|
283
|
-
catch { }
|
|
284
|
-
try {
|
|
285
|
-
if (proto && typeof proto === 'object')
|
|
286
|
-
proto.resetRootView = wrapped;
|
|
287
|
-
}
|
|
288
|
-
catch { }
|
|
289
|
-
try {
|
|
290
|
-
g.__NS_DIAG_RESET_WRAPPED__ = true;
|
|
291
|
-
}
|
|
292
|
-
catch { }
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
catch { }
|
|
296
|
-
}
|
|
297
59
|
// Track whether we've mounted an initial app root yet in HTTP-only boot
|
|
298
60
|
let initialMounted = !!globalThis.__NS_HMR_BOOT_COMPLETE__;
|
|
299
61
|
// Prevent duplicate initial-mount scheduling across rapid full-graph broadcasts and re-evaluations
|
|
@@ -303,6 +65,7 @@ let tsModuleSet = null;
|
|
|
303
65
|
let tsMainId = null;
|
|
304
66
|
const changedQueue = [];
|
|
305
67
|
let processingQueue = false;
|
|
68
|
+
let processingPromise = null;
|
|
306
69
|
// Detect whether the early placeholder root is still active on screen
|
|
307
70
|
function isPlaceholderActive() {
|
|
308
71
|
try {
|
|
@@ -844,7 +607,6 @@ function __nsNavigateUsingApp(comp, opts = {}) {
|
|
|
844
607
|
if (typeof GApp?.resetRootView === 'function' && typeof F === 'function') {
|
|
845
608
|
GApp.resetRootView({
|
|
846
609
|
create: () => {
|
|
847
|
-
var _a;
|
|
848
610
|
const fr = new F();
|
|
849
611
|
const navEntry = {
|
|
850
612
|
create: () => buildTarget(),
|
|
@@ -855,19 +617,7 @@ function __nsNavigateUsingApp(comp, opts = {}) {
|
|
|
855
617
|
fr.navigate(navEntry);
|
|
856
618
|
}
|
|
857
619
|
catch { }
|
|
858
|
-
try {
|
|
859
|
-
attachDiagnosticsToFrame(fr);
|
|
860
|
-
}
|
|
861
|
-
catch { }
|
|
862
620
|
setRootFrame(fr);
|
|
863
|
-
try {
|
|
864
|
-
(_a = getRootFrame()).__ns_tag || (_a.__ns_tag = Math.random().toString(36).slice(2));
|
|
865
|
-
console.log('[diag][root] ROOT_FRAME set (app-nav)', {
|
|
866
|
-
tag: getRootFrame()?.__ns_tag,
|
|
867
|
-
type: getRootFrame()?.constructor?.name,
|
|
868
|
-
});
|
|
869
|
-
}
|
|
870
|
-
catch { }
|
|
871
621
|
return fr;
|
|
872
622
|
},
|
|
873
623
|
});
|
|
@@ -875,28 +625,8 @@ function __nsNavigateUsingApp(comp, opts = {}) {
|
|
|
875
625
|
}
|
|
876
626
|
throw new Error('Application.resetRootView unavailable');
|
|
877
627
|
}
|
|
878
|
-
try {
|
|
879
|
-
attachDiagnosticsToFrame(frame);
|
|
880
|
-
}
|
|
881
|
-
catch { }
|
|
882
628
|
const navEntry = { create: () => buildTarget(), ...(opts || {}) };
|
|
883
|
-
try {
|
|
884
|
-
const summary = summarizeNavEntry(navEntry);
|
|
885
|
-
if (VERBOSE)
|
|
886
|
-
console.log('[app-nav] navigate entry', summary);
|
|
887
|
-
}
|
|
888
|
-
catch { }
|
|
889
629
|
frame.navigate(navEntry);
|
|
890
|
-
try {
|
|
891
|
-
const top2 = (g.Frame && g.Frame.topmost && g.Frame.topmost()) || null;
|
|
892
|
-
const ctor2 = top2 && top2.constructor && top2.constructor.name;
|
|
893
|
-
if (VERBOSE)
|
|
894
|
-
console.log('[app-nav] after navigate', {
|
|
895
|
-
topCtor: ctor2,
|
|
896
|
-
hasTop: !!top2,
|
|
897
|
-
});
|
|
898
|
-
}
|
|
899
|
-
catch { }
|
|
900
630
|
return undefined;
|
|
901
631
|
}
|
|
902
632
|
// Expose deterministic app navigation globally so /ns/rt can guarantee single-path navigation
|
|
@@ -914,77 +644,81 @@ async function processQueue() {
|
|
|
914
644
|
}
|
|
915
645
|
catch { }
|
|
916
646
|
}, 150);
|
|
917
|
-
return;
|
|
647
|
+
return Promise.resolve();
|
|
918
648
|
}
|
|
919
649
|
if (processingQueue)
|
|
920
|
-
return;
|
|
650
|
+
return processingPromise || Promise.resolve();
|
|
921
651
|
processingQueue = true;
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
if (seen.has(id))
|
|
933
|
-
continue;
|
|
934
|
-
seen.add(id);
|
|
935
|
-
drained.push(id);
|
|
936
|
-
}
|
|
937
|
-
if (!drained.length)
|
|
938
|
-
return;
|
|
939
|
-
if (VERBOSE)
|
|
940
|
-
console.log('[hmr][queue] processing changed ids', drained);
|
|
941
|
-
// Evaluate changed modules best-effort; failures shouldn't completely break HMR.
|
|
942
|
-
for (const id of drained) {
|
|
943
|
-
try {
|
|
944
|
-
const spec = normalizeSpec(id);
|
|
945
|
-
const url = await requestModuleFromServer(spec);
|
|
946
|
-
if (!url)
|
|
652
|
+
processingPromise = (async () => {
|
|
653
|
+
try {
|
|
654
|
+
// Simple deterministic drain of the queue. We currently focus on TS flavor
|
|
655
|
+
// by re-importing changed modules (to refresh their HTTP ESM copies) and
|
|
656
|
+
// then performing a root reset so the UI reflects the new code.
|
|
657
|
+
const seen = new Set();
|
|
658
|
+
const drained = [];
|
|
659
|
+
while (changedQueue.length) {
|
|
660
|
+
const id = changedQueue.shift();
|
|
661
|
+
if (!id || typeof id !== 'string')
|
|
947
662
|
continue;
|
|
948
|
-
if (
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
catch (e) {
|
|
953
|
-
if (VERBOSE)
|
|
954
|
-
console.warn('[hmr][queue] re-import failed for', id, e);
|
|
663
|
+
if (seen.has(id))
|
|
664
|
+
continue;
|
|
665
|
+
seen.add(id);
|
|
666
|
+
drained.push(id);
|
|
955
667
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
case 'typescript': {
|
|
963
|
-
// For TS apps, always reset back to the conventional app root.
|
|
964
|
-
// This preserves the shell (Frame, ActionBar, etc.) that the app's
|
|
965
|
-
// own bootstrapping wires up via `Application.run`.
|
|
668
|
+
if (!drained.length)
|
|
669
|
+
return;
|
|
670
|
+
if (VERBOSE)
|
|
671
|
+
console.log('[hmr][queue] processing changed ids', drained);
|
|
672
|
+
// Evaluate changed modules best-effort; failures shouldn't completely break HMR.
|
|
673
|
+
for (const id of drained) {
|
|
966
674
|
try {
|
|
967
|
-
const
|
|
968
|
-
const
|
|
969
|
-
if (!
|
|
970
|
-
|
|
971
|
-
console.warn('[hmr][queue] TS flavor: Application.resetRootView unavailable; skipping UI refresh');
|
|
972
|
-
break;
|
|
973
|
-
}
|
|
675
|
+
const spec = normalizeSpec(id);
|
|
676
|
+
const url = await requestModuleFromServer(spec);
|
|
677
|
+
if (!url)
|
|
678
|
+
continue;
|
|
974
679
|
if (VERBOSE)
|
|
975
|
-
console.log('[hmr][queue]
|
|
976
|
-
|
|
680
|
+
console.log('[hmr][queue] re-import', { id, spec, url });
|
|
681
|
+
const mod = await import(/* @vite-ignore */ url);
|
|
977
682
|
}
|
|
978
683
|
catch (e) {
|
|
979
|
-
|
|
684
|
+
if (VERBOSE)
|
|
685
|
+
console.warn('[hmr][queue] re-import failed for', id, e);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
// After evaluating the batch, perform flavor-specific UI refresh.
|
|
689
|
+
switch (__NS_TARGET_FLAVOR__) {
|
|
690
|
+
case 'vue':
|
|
691
|
+
// Vue SFCs are handled via the registry update path; nothing to do here.
|
|
692
|
+
break;
|
|
693
|
+
case 'typescript': {
|
|
694
|
+
// For TS apps, always reset back to the conventional app root.
|
|
695
|
+
// This preserves the shell (Frame, ActionBar, etc.) that the app's
|
|
696
|
+
// own bootstrapping wires up via `Application.run`.
|
|
697
|
+
try {
|
|
698
|
+
const g = globalThis;
|
|
699
|
+
const App = getCore('Application') || g.Application;
|
|
700
|
+
if (!App || typeof App.resetRootView !== 'function') {
|
|
701
|
+
if (VERBOSE)
|
|
702
|
+
console.warn('[hmr][queue] TS flavor: Application.resetRootView unavailable; skipping UI refresh');
|
|
703
|
+
break;
|
|
704
|
+
}
|
|
705
|
+
if (VERBOSE)
|
|
706
|
+
console.log('[hmr][queue] TS flavor: resetRootView(app-root) after changes');
|
|
707
|
+
App.resetRootView({ moduleName: 'app-root' });
|
|
708
|
+
}
|
|
709
|
+
catch (e) {
|
|
710
|
+
console.warn('[hmr][queue] TS flavor: resetRootView(app-root) failed', e);
|
|
711
|
+
}
|
|
712
|
+
break;
|
|
980
713
|
}
|
|
981
|
-
break;
|
|
982
714
|
}
|
|
983
715
|
}
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
716
|
+
finally {
|
|
717
|
+
processingQueue = false;
|
|
718
|
+
processingPromise = null;
|
|
719
|
+
}
|
|
720
|
+
})();
|
|
721
|
+
return processingPromise;
|
|
988
722
|
}
|
|
989
723
|
let hmrSocket = null;
|
|
990
724
|
// Track server-announced batches for each version so we can import in-order client-side
|
|
@@ -1140,8 +874,6 @@ async function handleHmrMessage(ev) {
|
|
|
1140
874
|
catch {
|
|
1141
875
|
return;
|
|
1142
876
|
}
|
|
1143
|
-
if (VERBOSE)
|
|
1144
|
-
console.log('[hmr-client] msg', msg);
|
|
1145
877
|
// Notify optional app-level hook after an HMR batch is applied.
|
|
1146
878
|
function notifyAppHmrUpdate(kind, changedIds) {
|
|
1147
879
|
try {
|
|
@@ -1154,8 +886,63 @@ async function handleHmrMessage(ev) {
|
|
|
1154
886
|
}
|
|
1155
887
|
if (msg) {
|
|
1156
888
|
if (msg.type === 'ns:hmr-full-graph') {
|
|
889
|
+
// Bump a monotonic nonce so HTTP ESM imports can always be cache-busted per update.
|
|
890
|
+
try {
|
|
891
|
+
const g = globalThis;
|
|
892
|
+
g.__NS_HMR_IMPORT_NONCE__ = (typeof g.__NS_HMR_IMPORT_NONCE__ === 'number' ? g.__NS_HMR_IMPORT_NONCE__ : 0) + 1;
|
|
893
|
+
}
|
|
894
|
+
catch { }
|
|
895
|
+
// Capture previous graph snapshot so we can infer which modules changed.
|
|
896
|
+
const prevGraph = new Map(graph);
|
|
1157
897
|
setGraphVersion(Number(msg.version || getGraphVersion() || 0));
|
|
1158
898
|
applyFullGraph(msg);
|
|
899
|
+
// In some cases (e.g. server chooses full-graph resync / page reload), we won't
|
|
900
|
+
// receive a delta queue to re-import changed TS modules. Without re-import,
|
|
901
|
+
// HTTP ESM caching means module bodies (and side effects) won't re-run.
|
|
902
|
+
try {
|
|
903
|
+
const inferredChanged = [];
|
|
904
|
+
try {
|
|
905
|
+
for (const [id, next] of graph.entries()) {
|
|
906
|
+
const prev = prevGraph.get(id);
|
|
907
|
+
if (!prev || prev.hash !== next.hash)
|
|
908
|
+
inferredChanged.push(id);
|
|
909
|
+
}
|
|
910
|
+
// Removed modules are also "changed" but we don't import them.
|
|
911
|
+
}
|
|
912
|
+
catch { }
|
|
913
|
+
// Best-effort: only re-import real source modules (avoid .vue registry pipeline and virtual ids)
|
|
914
|
+
const toReimport = inferredChanged.filter((id) => {
|
|
915
|
+
if (!id || typeof id !== 'string')
|
|
916
|
+
return false;
|
|
917
|
+
if (/^\0|^\/\0/.test(id))
|
|
918
|
+
return false;
|
|
919
|
+
if (/plugin-vue:export-helper/.test(id))
|
|
920
|
+
return false;
|
|
921
|
+
if (/\.vue$/i.test(id))
|
|
922
|
+
return false;
|
|
923
|
+
if (id.endsWith(APP_MAIN_ENTRY_SPEC))
|
|
924
|
+
return false;
|
|
925
|
+
return true;
|
|
926
|
+
});
|
|
927
|
+
if (toReimport.length && VERBOSE)
|
|
928
|
+
console.log('[hmr][full-graph] inferred changed modules; re-importing', toReimport);
|
|
929
|
+
for (const id of toReimport) {
|
|
930
|
+
try {
|
|
931
|
+
const spec = normalizeSpec(id);
|
|
932
|
+
const url = await requestModuleFromServer(spec);
|
|
933
|
+
if (!url)
|
|
934
|
+
continue;
|
|
935
|
+
if (VERBOSE)
|
|
936
|
+
console.log('[hmr][full-graph] re-import', { id, spec, url });
|
|
937
|
+
await import(/* @vite-ignore */ url);
|
|
938
|
+
}
|
|
939
|
+
catch (e) {
|
|
940
|
+
if (VERBOSE)
|
|
941
|
+
console.warn('[hmr][full-graph] re-import failed for', id, e);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
catch { }
|
|
1159
946
|
const fullIds = Array.isArray(msg.modules) ? msg.modules.map((m) => m?.id).filter(Boolean) : [];
|
|
1160
947
|
notifyAppHmrUpdate('full-graph', fullIds);
|
|
1161
948
|
return;
|
|
@@ -1188,7 +975,12 @@ async function handleHmrMessage(ev) {
|
|
|
1188
975
|
return;
|
|
1189
976
|
}
|
|
1190
977
|
if (msg.type === 'ns:hmr-delta') {
|
|
1191
|
-
|
|
978
|
+
// Bump a monotonic nonce so HTTP ESM imports can always be cache-busted per update.
|
|
979
|
+
try {
|
|
980
|
+
const g = globalThis;
|
|
981
|
+
g.__NS_HMR_IMPORT_NONCE__ = (typeof g.__NS_HMR_IMPORT_NONCE__ === 'number' ? g.__NS_HMR_IMPORT_NONCE__ : 0) + 1;
|
|
982
|
+
}
|
|
983
|
+
catch { }
|
|
1192
984
|
try {
|
|
1193
985
|
const ids = Array.isArray(msg.changed) ? msg.changed.map((c) => c?.id).filter(Boolean) : [];
|
|
1194
986
|
if (ids.length)
|
|
@@ -1196,6 +988,12 @@ async function handleHmrMessage(ev) {
|
|
|
1196
988
|
}
|
|
1197
989
|
catch { }
|
|
1198
990
|
applyDelta(msg);
|
|
991
|
+
// Ensure queued module re-imports complete before notifying app hooks.
|
|
992
|
+
// Otherwise app-level handlers can run against stale module bodies due to HTTP ESM caching.
|
|
993
|
+
try {
|
|
994
|
+
await processQueue();
|
|
995
|
+
}
|
|
996
|
+
catch { }
|
|
1199
997
|
const deltaIds = Array.isArray(msg.changed) ? msg.changed.map((c) => c?.id).filter(Boolean) : [];
|
|
1200
998
|
notifyAppHmrUpdate('delta', deltaIds);
|
|
1201
999
|
return;
|
|
@@ -1311,25 +1109,6 @@ async function performResetRoot(newComponent) {
|
|
|
1311
1109
|
ensureCoreAliasesOnGlobalThis();
|
|
1312
1110
|
}
|
|
1313
1111
|
catch { }
|
|
1314
|
-
try {
|
|
1315
|
-
if (VERBOSE)
|
|
1316
|
-
logUiSnapshot('pre-performResetRoot');
|
|
1317
|
-
}
|
|
1318
|
-
catch { }
|
|
1319
|
-
if (VERBOSE) {
|
|
1320
|
-
try {
|
|
1321
|
-
const g = globalThis;
|
|
1322
|
-
const vF = getCore('Frame');
|
|
1323
|
-
console.log('[hmr-client] alias check before remount', {
|
|
1324
|
-
globalFrameHasTopmost: typeof g?.Frame?.topmost === 'function',
|
|
1325
|
-
vendorFrameHasTopmost: typeof vF?.topmost === 'function',
|
|
1326
|
-
sameFrameRef: vF === g?.Frame,
|
|
1327
|
-
appHasReset: typeof g?.Application?.resetRootView === 'function',
|
|
1328
|
-
pageIsCtor: typeof g?.Page === 'function',
|
|
1329
|
-
});
|
|
1330
|
-
}
|
|
1331
|
-
catch { }
|
|
1332
|
-
}
|
|
1333
1112
|
if (VERBOSE) {
|
|
1334
1113
|
console.log('[hmr-client] Single-path: replace current root Page');
|
|
1335
1114
|
console.log('[hmr-client] Component details:', {
|
|
@@ -1536,12 +1315,6 @@ async function performResetRoot(newComponent) {
|
|
|
1536
1315
|
const isAuthoritativeFrame = !!existingAppFrame && existingAppFrame !== placeholderFrame;
|
|
1537
1316
|
if (!hadPlaceholder && !isFrameRoot && isAuthoritativeFrame && typeof existingAppFrame.navigate === 'function') {
|
|
1538
1317
|
try {
|
|
1539
|
-
if (VERBOSE)
|
|
1540
|
-
console.log('[hmr-client] navigating authoritative app Frame to new Page (no placeholder, smooth swap)');
|
|
1541
|
-
try {
|
|
1542
|
-
attachDiagnosticsToFrame(existingAppFrame);
|
|
1543
|
-
}
|
|
1544
|
-
catch { }
|
|
1545
1318
|
const navEntry = {
|
|
1546
1319
|
create: () => preparedRoot,
|
|
1547
1320
|
clearHistory: true,
|
|
@@ -1611,7 +1384,6 @@ async function performResetRoot(newComponent) {
|
|
|
1611
1384
|
// Use the previously created preparedRoot
|
|
1612
1385
|
const entry = {
|
|
1613
1386
|
create: () => {
|
|
1614
|
-
var _a, _b;
|
|
1615
1387
|
if (!isFrameRoot) {
|
|
1616
1388
|
const F = getCore('Frame');
|
|
1617
1389
|
const fr = new F();
|
|
@@ -1626,68 +1398,30 @@ async function performResetRoot(newComponent) {
|
|
|
1626
1398
|
};
|
|
1627
1399
|
try {
|
|
1628
1400
|
fr.navigate(navEntry);
|
|
1629
|
-
if (VERBOSE)
|
|
1630
|
-
console.log('[hmr-client] resetRootView:create navigated Frame');
|
|
1631
1401
|
}
|
|
1632
1402
|
catch (e) {
|
|
1633
1403
|
console.warn('[hmr-client] resetRootView:create navigate failed', e);
|
|
1634
1404
|
}
|
|
1635
|
-
try {
|
|
1636
|
-
attachDiagnosticsToFrame(fr);
|
|
1637
|
-
}
|
|
1638
|
-
catch { }
|
|
1639
1405
|
setRootFrame(fr);
|
|
1640
|
-
try {
|
|
1641
|
-
(_a = getRootFrame()).__ns_tag || (_a.__ns_tag = Math.random().toString(36).slice(2));
|
|
1642
|
-
console.log('[diag][root] ROOT_FRAME set (new)', {
|
|
1643
|
-
tag: getRootFrame()?.__ns_tag,
|
|
1644
|
-
type: getRootFrame()?.constructor?.name,
|
|
1645
|
-
});
|
|
1646
|
-
}
|
|
1647
|
-
catch { }
|
|
1648
1406
|
return fr;
|
|
1649
1407
|
}
|
|
1650
1408
|
else {
|
|
1651
1409
|
const fr = preparedRoot;
|
|
1652
|
-
if (VERBOSE)
|
|
1653
|
-
console.log('[hmr-client] resetRootView:create using provided Frame', { type: fr?.constructor?.name });
|
|
1654
|
-
try {
|
|
1655
|
-
attachDiagnosticsToFrame(fr);
|
|
1656
|
-
}
|
|
1657
|
-
catch { }
|
|
1658
1410
|
setRootFrame(fr);
|
|
1659
|
-
try {
|
|
1660
|
-
(_b = getRootFrame()).__ns_tag || (_b.__ns_tag = Math.random().toString(36).slice(2));
|
|
1661
|
-
console.log('[diag][root] ROOT_FRAME set (provided)', {
|
|
1662
|
-
tag: getRootFrame()?.__ns_tag,
|
|
1663
|
-
type: getRootFrame()?.constructor?.name,
|
|
1664
|
-
});
|
|
1665
|
-
}
|
|
1666
|
-
catch { }
|
|
1667
1411
|
return fr;
|
|
1668
1412
|
}
|
|
1669
1413
|
},
|
|
1670
1414
|
};
|
|
1671
|
-
if (VERBOSE)
|
|
1672
|
-
console.log('[hmr-client] invoking Application.resetRootView with entry (always)', { isFrameRoot, hadPlaceholder, isIOS });
|
|
1673
1415
|
// Always use an entry with a create() function to avoid cross‑realm instanceof checks on Android.
|
|
1674
1416
|
App2.resetRootView(entry);
|
|
1675
1417
|
// After authoritative reset, it's safe to detach the early placeholder launch handler
|
|
1676
1418
|
try {
|
|
1677
1419
|
const restore = globalThis.__NS_DEV_RESTORE_PLACEHOLDER__;
|
|
1678
1420
|
if (typeof restore === 'function') {
|
|
1679
|
-
if (VERBOSE)
|
|
1680
|
-
console.log('[hmr-client] restoring: detach early placeholder launch handler');
|
|
1681
1421
|
restore();
|
|
1682
1422
|
}
|
|
1683
1423
|
}
|
|
1684
1424
|
catch { }
|
|
1685
|
-
if (VERBOSE) {
|
|
1686
|
-
logUiSnapshot('post-resetRootView');
|
|
1687
|
-
console.log('[hmr-client] performResetRoot completed', {
|
|
1688
|
-
elapsedMs: Date.now() - tStart,
|
|
1689
|
-
});
|
|
1690
|
-
}
|
|
1691
1425
|
return true;
|
|
1692
1426
|
}
|
|
1693
1427
|
catch (e) {
|