@nativescript/vite 1.0.5 → 1.0.6-next.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.
Files changed (39) hide show
  1. package/README.md +25 -3
  2. package/bin/cli.cjs +0 -0
  3. package/configuration/angular.js +1 -1
  4. package/configuration/angular.js.map +1 -1
  5. package/configuration/base.js +2 -2
  6. package/configuration/base.js.map +1 -1
  7. package/configuration/react.js +1 -1
  8. package/configuration/react.js.map +1 -1
  9. package/configuration/solid.js +1 -1
  10. package/configuration/solid.js.map +1 -1
  11. package/configuration/typescript.js +1 -1
  12. package/configuration/typescript.js.map +1 -1
  13. package/configuration/vue.js +1 -1
  14. package/configuration/vue.js.map +1 -1
  15. package/helpers/css-tree.js +1 -0
  16. package/helpers/css-tree.js.map +1 -1
  17. package/helpers/main-entry.js +5 -50
  18. package/helpers/main-entry.js.map +1 -1
  19. package/hmr/client/index.js +398 -146
  20. package/hmr/client/index.js.map +1 -1
  21. package/hmr/client/utils.d.ts +2 -0
  22. package/hmr/client/utils.js +179 -25
  23. package/hmr/client/utils.js.map +1 -1
  24. package/hmr/entry-runtime.js +1 -18
  25. package/hmr/entry-runtime.js.map +1 -1
  26. package/hmr/frameworks/vue/client/index.js +5 -1
  27. package/hmr/frameworks/vue/client/index.js.map +1 -1
  28. package/hmr/server/vite-plugin.js +6 -66
  29. package/hmr/server/vite-plugin.js.map +1 -1
  30. package/hmr/server/websocket.js +173 -52
  31. package/hmr/server/websocket.js.map +1 -1
  32. package/hmr/shared/runtime/http-only-boot.js +3 -20
  33. package/hmr/shared/runtime/http-only-boot.js.map +1 -1
  34. package/hmr/shared/vendor/manifest.js +5 -8
  35. package/hmr/shared/vendor/manifest.js.map +1 -1
  36. package/package.json +10 -1
  37. package/hmr/shared/runtime/hooks.d.ts +0 -17
  38. package/hmr/shared/runtime/hooks.js +0 -68
  39. package/hmr/shared/runtime/hooks.js.map +0 -1
@@ -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 } from './utils.js';
8
+ import { setHMRWsUrl, getHMRWsUrl, pendingModuleFetches, deriveHttpOrigin, setHttpOriginForVite, moduleFetchCache, requestModuleFromServer, getHttpOriginForVite, normalizeSpec, hmrMetrics, graph, setGraphVersion, getGraphVersion, getCurrentApp, getRootFrame, setCurrentApp, setRootFrame, getCore, attachDiagnosticsToFrame, logUiSnapshot } 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,6 +14,14 @@ 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 { }
17
25
  // Ensure core aliases are present on globalThis early so that /ns/rt exports resolve to functions
18
26
  // before any SFCs are evaluated during HTTP-only dev boot.
19
27
  function ensureCoreAliasesOnGlobalThis() {
@@ -38,11 +46,46 @@ function ensureCoreAliasesOnGlobalThis() {
38
46
  g.Page = P;
39
47
  }
40
48
  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
+ }
41
82
  }
42
83
  catch { }
43
84
  }
44
85
  // Apply once on module evaluation
45
86
  ensureCoreAliasesOnGlobalThis();
87
+ // Install low-level diagnostics for navigation and root replacement to trace duplicates and state
88
+ installDeepDiagnostics();
46
89
  /**
47
90
  * Flavor hooks
48
91
  */
@@ -50,12 +93,207 @@ import { installNsVueDevShims, ensureBackWrapperInstalled, getRootForVue, loadSf
50
93
  import { handleAngularHotUpdateMessage, installAngularHmrClientHooks } from '../frameworks/angular/client/index.js';
51
94
  switch (__NS_TARGET_FLAVOR__) {
52
95
  case 'vue':
96
+ if (VERBOSE) {
97
+ console.log('[hmr-client] installing nativescript-vue dev shims');
98
+ }
53
99
  installNsVueDevShims();
54
100
  break;
55
101
  case 'angular':
102
+ if (VERBOSE) {
103
+ try {
104
+ console.log('[hmr-client] Initializing Angular HMR shims');
105
+ }
106
+ catch { }
107
+ }
56
108
  installAngularHmrClientHooks();
57
109
  break;
58
110
  }
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
+ }
59
297
  // Track whether we've mounted an initial app root yet in HTTP-only boot
60
298
  let initialMounted = !!globalThis.__NS_HMR_BOOT_COMPLETE__;
61
299
  // Prevent duplicate initial-mount scheduling across rapid full-graph broadcasts and re-evaluations
@@ -65,7 +303,6 @@ let tsModuleSet = null;
65
303
  let tsMainId = null;
66
304
  const changedQueue = [];
67
305
  let processingQueue = false;
68
- let processingPromise = null;
69
306
  // Detect whether the early placeholder root is still active on screen
70
307
  function isPlaceholderActive() {
71
308
  try {
@@ -607,6 +844,7 @@ function __nsNavigateUsingApp(comp, opts = {}) {
607
844
  if (typeof GApp?.resetRootView === 'function' && typeof F === 'function') {
608
845
  GApp.resetRootView({
609
846
  create: () => {
847
+ var _a;
610
848
  const fr = new F();
611
849
  const navEntry = {
612
850
  create: () => buildTarget(),
@@ -617,7 +855,19 @@ function __nsNavigateUsingApp(comp, opts = {}) {
617
855
  fr.navigate(navEntry);
618
856
  }
619
857
  catch { }
858
+ try {
859
+ attachDiagnosticsToFrame(fr);
860
+ }
861
+ catch { }
620
862
  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 { }
621
871
  return fr;
622
872
  },
623
873
  });
@@ -625,8 +875,28 @@ function __nsNavigateUsingApp(comp, opts = {}) {
625
875
  }
626
876
  throw new Error('Application.resetRootView unavailable');
627
877
  }
878
+ try {
879
+ attachDiagnosticsToFrame(frame);
880
+ }
881
+ catch { }
628
882
  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 { }
629
889
  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 { }
630
900
  return undefined;
631
901
  }
632
902
  // Expose deterministic app navigation globally so /ns/rt can guarantee single-path navigation
@@ -644,81 +914,77 @@ async function processQueue() {
644
914
  }
645
915
  catch { }
646
916
  }, 150);
647
- return Promise.resolve();
917
+ return;
648
918
  }
649
919
  if (processingQueue)
650
- return processingPromise || Promise.resolve();
920
+ return;
651
921
  processingQueue = true;
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')
662
- continue;
663
- if (seen.has(id))
922
+ try {
923
+ // Simple deterministic drain of the queue. We currently focus on TS flavor
924
+ // by re-importing changed modules (to refresh their HTTP ESM copies) and
925
+ // then performing a root reset so the UI reflects the new code.
926
+ const seen = new Set();
927
+ const drained = [];
928
+ while (changedQueue.length) {
929
+ const id = changedQueue.shift();
930
+ if (!id || typeof id !== 'string')
931
+ continue;
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)
664
947
  continue;
665
- seen.add(id);
666
- drained.push(id);
948
+ if (VERBOSE)
949
+ console.log('[hmr][queue] re-import', { id, spec, url });
950
+ await import(/* @vite-ignore */ url);
667
951
  }
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) {
952
+ catch (e) {
953
+ if (VERBOSE)
954
+ console.warn('[hmr][queue] re-import failed for', id, e);
955
+ }
956
+ }
957
+ // After evaluating the batch, perform flavor-specific UI refresh.
958
+ switch (__NS_TARGET_FLAVOR__) {
959
+ case 'vue':
960
+ // Vue SFCs are handled via the registry update path; nothing to do here.
961
+ break;
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`.
674
966
  try {
675
- const spec = normalizeSpec(id);
676
- const url = await requestModuleFromServer(spec);
677
- if (!url)
678
- continue;
967
+ const g = globalThis;
968
+ const App = getCore('Application') || g.Application;
969
+ if (!App || typeof App.resetRootView !== 'function') {
970
+ if (VERBOSE)
971
+ console.warn('[hmr][queue] TS flavor: Application.resetRootView unavailable; skipping UI refresh');
972
+ break;
973
+ }
679
974
  if (VERBOSE)
680
- console.log('[hmr][queue] re-import', { id, spec, url });
681
- const mod = await import(/* @vite-ignore */ url);
975
+ console.log('[hmr][queue] TS flavor: resetRootView(app-root) after changes');
976
+ App.resetRootView({ moduleName: 'app-root' });
682
977
  }
683
978
  catch (e) {
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;
979
+ console.warn('[hmr][queue] TS flavor: resetRootView(app-root) failed', e);
713
980
  }
981
+ break;
714
982
  }
715
983
  }
716
- finally {
717
- processingQueue = false;
718
- processingPromise = null;
719
- }
720
- })();
721
- return processingPromise;
984
+ }
985
+ finally {
986
+ processingQueue = false;
987
+ }
722
988
  }
723
989
  let hmrSocket = null;
724
990
  // Track server-announced batches for each version so we can import in-order client-side
@@ -874,77 +1140,12 @@ async function handleHmrMessage(ev) {
874
1140
  catch {
875
1141
  return;
876
1142
  }
877
- // Notify optional app-level hook after an HMR batch is applied.
878
- function notifyAppHmrUpdate(kind, changedIds) {
879
- try {
880
- const hook = globalThis.__NS_HMR_ON_UPDATE__;
881
- if (typeof hook === 'function') {
882
- hook({ type: kind, version: getGraphVersion(), changedIds: changedIds || [], raw: msg });
883
- }
884
- }
885
- catch { }
886
- }
1143
+ if (VERBOSE)
1144
+ console.log('[hmr-client] msg', msg);
887
1145
  if (msg) {
888
1146
  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);
897
1147
  setGraphVersion(Number(msg.version || getGraphVersion() || 0));
898
1148
  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 { }
946
- const fullIds = Array.isArray(msg.modules) ? msg.modules.map((m) => m?.id).filter(Boolean) : [];
947
- notifyAppHmrUpdate('full-graph', fullIds);
948
1149
  return;
949
1150
  }
950
1151
  if (msg.type === 'ns:ts-module-registry') {
@@ -975,12 +1176,7 @@ async function handleHmrMessage(ev) {
975
1176
  return;
976
1177
  }
977
1178
  if (msg.type === 'ns:hmr-delta') {
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 { }
1179
+ setGraphVersion(Number(msg.newVersion || getGraphVersion() || 0));
984
1180
  try {
985
1181
  const ids = Array.isArray(msg.changed) ? msg.changed.map((c) => c?.id).filter(Boolean) : [];
986
1182
  if (ids.length)
@@ -988,14 +1184,6 @@ async function handleHmrMessage(ev) {
988
1184
  }
989
1185
  catch { }
990
1186
  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 { }
997
- const deltaIds = Array.isArray(msg.changed) ? msg.changed.map((c) => c?.id).filter(Boolean) : [];
998
- notifyAppHmrUpdate('delta', deltaIds);
999
1187
  return;
1000
1188
  }
1001
1189
  else if (handleAngularHotUpdateMessage(msg, { getCore, verbose: VERBOSE })) {
@@ -1109,6 +1297,25 @@ async function performResetRoot(newComponent) {
1109
1297
  ensureCoreAliasesOnGlobalThis();
1110
1298
  }
1111
1299
  catch { }
1300
+ try {
1301
+ if (VERBOSE)
1302
+ logUiSnapshot('pre-performResetRoot');
1303
+ }
1304
+ catch { }
1305
+ if (VERBOSE) {
1306
+ try {
1307
+ const g = globalThis;
1308
+ const vF = getCore('Frame');
1309
+ console.log('[hmr-client] alias check before remount', {
1310
+ globalFrameHasTopmost: typeof g?.Frame?.topmost === 'function',
1311
+ vendorFrameHasTopmost: typeof vF?.topmost === 'function',
1312
+ sameFrameRef: vF === g?.Frame,
1313
+ appHasReset: typeof g?.Application?.resetRootView === 'function',
1314
+ pageIsCtor: typeof g?.Page === 'function',
1315
+ });
1316
+ }
1317
+ catch { }
1318
+ }
1112
1319
  if (VERBOSE) {
1113
1320
  console.log('[hmr-client] Single-path: replace current root Page');
1114
1321
  console.log('[hmr-client] Component details:', {
@@ -1315,6 +1522,12 @@ async function performResetRoot(newComponent) {
1315
1522
  const isAuthoritativeFrame = !!existingAppFrame && existingAppFrame !== placeholderFrame;
1316
1523
  if (!hadPlaceholder && !isFrameRoot && isAuthoritativeFrame && typeof existingAppFrame.navigate === 'function') {
1317
1524
  try {
1525
+ if (VERBOSE)
1526
+ console.log('[hmr-client] navigating authoritative app Frame to new Page (no placeholder, smooth swap)');
1527
+ try {
1528
+ attachDiagnosticsToFrame(existingAppFrame);
1529
+ }
1530
+ catch { }
1318
1531
  const navEntry = {
1319
1532
  create: () => preparedRoot,
1320
1533
  clearHistory: true,
@@ -1384,6 +1597,7 @@ async function performResetRoot(newComponent) {
1384
1597
  // Use the previously created preparedRoot
1385
1598
  const entry = {
1386
1599
  create: () => {
1600
+ var _a, _b;
1387
1601
  if (!isFrameRoot) {
1388
1602
  const F = getCore('Frame');
1389
1603
  const fr = new F();
@@ -1398,30 +1612,68 @@ async function performResetRoot(newComponent) {
1398
1612
  };
1399
1613
  try {
1400
1614
  fr.navigate(navEntry);
1615
+ if (VERBOSE)
1616
+ console.log('[hmr-client] resetRootView:create navigated Frame');
1401
1617
  }
1402
1618
  catch (e) {
1403
1619
  console.warn('[hmr-client] resetRootView:create navigate failed', e);
1404
1620
  }
1621
+ try {
1622
+ attachDiagnosticsToFrame(fr);
1623
+ }
1624
+ catch { }
1405
1625
  setRootFrame(fr);
1626
+ try {
1627
+ (_a = getRootFrame()).__ns_tag || (_a.__ns_tag = Math.random().toString(36).slice(2));
1628
+ console.log('[diag][root] ROOT_FRAME set (new)', {
1629
+ tag: getRootFrame()?.__ns_tag,
1630
+ type: getRootFrame()?.constructor?.name,
1631
+ });
1632
+ }
1633
+ catch { }
1406
1634
  return fr;
1407
1635
  }
1408
1636
  else {
1409
1637
  const fr = preparedRoot;
1638
+ if (VERBOSE)
1639
+ console.log('[hmr-client] resetRootView:create using provided Frame', { type: fr?.constructor?.name });
1640
+ try {
1641
+ attachDiagnosticsToFrame(fr);
1642
+ }
1643
+ catch { }
1410
1644
  setRootFrame(fr);
1645
+ try {
1646
+ (_b = getRootFrame()).__ns_tag || (_b.__ns_tag = Math.random().toString(36).slice(2));
1647
+ console.log('[diag][root] ROOT_FRAME set (provided)', {
1648
+ tag: getRootFrame()?.__ns_tag,
1649
+ type: getRootFrame()?.constructor?.name,
1650
+ });
1651
+ }
1652
+ catch { }
1411
1653
  return fr;
1412
1654
  }
1413
1655
  },
1414
1656
  };
1657
+ if (VERBOSE)
1658
+ console.log('[hmr-client] invoking Application.resetRootView with entry (always)', { isFrameRoot, hadPlaceholder, isIOS });
1415
1659
  // Always use an entry with a create() function to avoid cross‑realm instanceof checks on Android.
1416
1660
  App2.resetRootView(entry);
1417
1661
  // After authoritative reset, it's safe to detach the early placeholder launch handler
1418
1662
  try {
1419
1663
  const restore = globalThis.__NS_DEV_RESTORE_PLACEHOLDER__;
1420
1664
  if (typeof restore === 'function') {
1665
+ if (VERBOSE)
1666
+ console.log('[hmr-client] restoring: detach early placeholder launch handler');
1421
1667
  restore();
1422
1668
  }
1423
1669
  }
1424
1670
  catch { }
1671
+ if (VERBOSE) {
1672
+ logUiSnapshot('post-resetRootView');
1673
+ console.log('[hmr-client] performResetRoot completed', {
1674
+ elapsedMs: Date.now() - tStart,
1675
+ });
1676
+ }
1425
1677
  return true;
1426
1678
  }
1427
1679
  catch (e) {