@nativescript/vite 8.0.0-alpha.34 → 8.0.0-alpha.36
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/base.js +9 -1
- package/configuration/base.js.map +1 -1
- package/configuration/javascript.js +5 -3
- package/configuration/javascript.js.map +1 -1
- package/configuration/typescript.js +3 -2
- package/configuration/typescript.js.map +1 -1
- package/helpers/global-defines.d.ts +23 -0
- package/helpers/global-defines.js +36 -1
- package/helpers/global-defines.js.map +1 -1
- package/helpers/logging.js +2 -1
- package/helpers/logging.js.map +1 -1
- package/helpers/main-entry.d.ts +1 -0
- package/helpers/main-entry.js +18 -21
- package/helpers/main-entry.js.map +1 -1
- package/hmr/client/css-handler.js +4 -3
- package/hmr/client/css-handler.js.map +1 -1
- package/hmr/client/hmr-pending-overlay.d.ts +0 -14
- package/hmr/client/hmr-pending-overlay.js +13 -3
- package/hmr/client/hmr-pending-overlay.js.map +1 -1
- package/hmr/client/index.js +269 -39
- package/hmr/client/index.js.map +1 -1
- package/hmr/client/utils.js +7 -3
- package/hmr/client/utils.js.map +1 -1
- package/hmr/entry-runtime.js +5 -5
- package/hmr/entry-runtime.js.map +1 -1
- package/hmr/frameworks/angular/client/index.js +34 -10
- package/hmr/frameworks/angular/client/index.js.map +1 -1
- package/hmr/frameworks/vue/client/index.js +14 -10
- package/hmr/frameworks/vue/client/index.js.map +1 -1
- package/hmr/frameworks/vue/client/vue-sfc-update-overlay.d.ts +0 -33
- package/hmr/frameworks/vue/client/vue-sfc-update-overlay.js +12 -3
- package/hmr/frameworks/vue/client/vue-sfc-update-overlay.js.map +1 -1
- package/hmr/helpers/ast-normalizer.js +2 -1
- package/hmr/helpers/ast-normalizer.js.map +1 -1
- package/hmr/server/websocket-core-bridge.js +2 -1
- package/hmr/server/websocket-core-bridge.js.map +1 -1
- package/hmr/server/websocket.js +2 -1
- package/hmr/server/websocket.js.map +1 -1
- package/hmr/shared/runtime/boot-progress.js +3 -2
- package/hmr/shared/runtime/boot-progress.js.map +1 -1
- package/hmr/shared/runtime/boot-timeline.js +2 -12
- package/hmr/shared/runtime/boot-timeline.js.map +1 -1
- package/hmr/shared/runtime/dev-overlay.js +2 -1
- package/hmr/shared/runtime/dev-overlay.js.map +1 -1
- package/hmr/shared/runtime/global-scope.d.ts +18 -0
- package/hmr/shared/runtime/global-scope.js +21 -0
- package/hmr/shared/runtime/global-scope.js.map +1 -0
- package/hmr/shared/runtime/hooks.js +2 -1
- package/hmr/shared/runtime/hooks.js.map +1 -1
- package/hmr/shared/runtime/http-only-boot.js +7 -6
- package/hmr/shared/runtime/http-only-boot.js.map +1 -1
- package/hmr/shared/runtime/module-provenance.js +3 -2
- package/hmr/shared/runtime/module-provenance.js.map +1 -1
- package/hmr/shared/runtime/root-placeholder-view.js +2 -1
- package/hmr/shared/runtime/root-placeholder-view.js.map +1 -1
- package/hmr/shared/runtime/root-placeholder.js +5 -4
- package/hmr/shared/runtime/root-placeholder.js.map +1 -1
- package/hmr/shared/runtime/session-bootstrap.js +10 -9
- package/hmr/shared/runtime/session-bootstrap.js.map +1 -1
- package/hmr/shared/runtime/vendor-bootstrap.js +2 -1
- package/hmr/shared/runtime/vendor-bootstrap.js.map +1 -1
- package/hmr/shared/vendor/manifest-loader.js +2 -1
- package/hmr/shared/vendor/manifest-loader.js.map +1 -1
- package/hmr/vendor-bootstrap.d.ts +1 -3
- package/hmr/vendor-bootstrap.js +4 -6
- package/hmr/vendor-bootstrap.js.map +1 -1
- package/package.json +1 -1
- package/runtime/core-aliases-early.js +2 -3
- package/runtime/core-aliases-early.js.map +1 -1
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* (`__NS_HMR_PROGRESS_OVERLAY_ENABLED__`) and detail-string formatting
|
|
13
13
|
* can be unit-tested without booting the full HMR client.
|
|
14
14
|
*/
|
|
15
|
+
import { getGlobalScope } from '../shared/runtime/global-scope.js';
|
|
15
16
|
export function buildHmrPendingDetail(filePath) {
|
|
16
17
|
if (!filePath || filePath === '<unknown>')
|
|
17
18
|
return 'Preparing update';
|
|
@@ -21,12 +22,21 @@ export function applyHmrPendingFrame(filePath, deps) {
|
|
|
21
22
|
const enabled = typeof deps.overlayEnabled === 'boolean'
|
|
22
23
|
? deps.overlayEnabled
|
|
23
24
|
: (() => {
|
|
25
|
+
// Define substitution does not reach this raw-served file — fall
|
|
26
|
+
// back to the globalThis seed planted by the entry's defines-seed
|
|
27
|
+
// module before defaulting to enabled.
|
|
24
28
|
try {
|
|
25
|
-
|
|
29
|
+
if (typeof __NS_HMR_PROGRESS_OVERLAY_ENABLED__ === 'boolean')
|
|
30
|
+
return __NS_HMR_PROGRESS_OVERLAY_ENABLED__;
|
|
26
31
|
}
|
|
27
|
-
catch {
|
|
28
|
-
|
|
32
|
+
catch { }
|
|
33
|
+
try {
|
|
34
|
+
const seeded = getGlobalScope().__NS_HMR_PROGRESS_OVERLAY_ENABLED__;
|
|
35
|
+
if (typeof seeded === 'boolean')
|
|
36
|
+
return seeded;
|
|
29
37
|
}
|
|
38
|
+
catch { }
|
|
39
|
+
return true;
|
|
30
40
|
})();
|
|
31
41
|
if (!enabled)
|
|
32
42
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hmr-pending-overlay.js","sourceRoot":"","sources":["../../../../../packages/vite/hmr/client/hmr-pending-overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;
|
|
1
|
+
{"version":3,"file":"hmr-pending-overlay.js","sourceRoot":"","sources":["../../../../../packages/vite/hmr/client/hmr-pending-overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAkBnE,MAAM,UAAU,qBAAqB,CAAC,QAA4B;IACjE,IAAI,CAAC,QAAQ,IAAI,QAAQ,KAAK,WAAW;QAAE,OAAO,kBAAkB,CAAC;IACrE,OAAO,YAAY,QAAQ,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,QAA4B,EAAE,IAA2B;IAC7F,MAAM,OAAO,GACZ,OAAO,IAAI,CAAC,cAAc,KAAK,SAAS;QACvC,CAAC,CAAC,IAAI,CAAC,cAAc;QACrB,CAAC,CAAC,CAAC,GAAG,EAAE;YACN,iEAAiE;YACjE,kEAAkE;YAClE,uCAAuC;YACvC,IAAI,CAAC;gBACJ,IAAI,OAAO,mCAAmC,KAAK,SAAS;oBAAE,OAAO,mCAAmC,CAAC;YAC1G,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YACV,IAAI,CAAC;gBACJ,MAAM,MAAM,GAAG,cAAc,EAAE,CAAC,mCAAmC,CAAC;gBACpE,IAAI,OAAO,MAAM,KAAK,SAAS;oBAAE,OAAO,MAAM,CAAC;YAChD,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;YACV,OAAO,IAAI,CAAC;QACb,CAAC,CAAC,EAAE,CAAC;IACR,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAC3B,IAAI,GAAsB,CAAC;IAC3B,IAAI,CAAC;QACJ,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;IACD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,CAAC,cAAc,KAAK,UAAU;QAAE,OAAO,KAAK,CAAC;IACnE,IAAI,CAAC;QACJ,GAAG,CAAC,cAAc,CAAC,UAAU,EAAE,EAAE,MAAM,EAAE,qBAAqB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC5E,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC"}
|
package/hmr/client/index.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { setHMRWsUrl, getHMRWsUrl, pendingModuleFetches, deriveHttpOrigin, setHttpOriginForVite, moduleFetchCache, requestModuleFromServer, getHttpOriginForVite, normalizeSpec, hmrMetrics, graph, setGraphVersion, getGraphVersion, getCurrentApp, getRootFrame, setCurrentApp, setRootFrame, getCore, hasExplicitEviction, invalidateModulesByUrls, buildEvictionUrls, emitHmrModeBannerOnce } from './utils.js';
|
|
9
9
|
import { handleCssUpdates } from './css-handler.js';
|
|
10
10
|
import { buildCssApplyingDetail, buildCssAppliedDetail } from './css-update-overlay.js';
|
|
11
|
+
import { getGlobalScope } from '../shared/runtime/global-scope.js';
|
|
11
12
|
const VERBOSE = typeof __NS_ENV_VERBOSE__ !== 'undefined' && __NS_ENV_VERBOSE__;
|
|
12
13
|
function resolveTargetFlavor() {
|
|
13
14
|
try {
|
|
@@ -17,7 +18,7 @@ function resolveTargetFlavor() {
|
|
|
17
18
|
}
|
|
18
19
|
catch { }
|
|
19
20
|
try {
|
|
20
|
-
const g =
|
|
21
|
+
const g = getGlobalScope();
|
|
21
22
|
if (typeof g.__NS_TARGET_FLAVOR__ === 'string' && g.__NS_TARGET_FLAVOR__) {
|
|
22
23
|
return g.__NS_TARGET_FLAVOR__;
|
|
23
24
|
}
|
|
@@ -41,7 +42,11 @@ try {
|
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
44
|
catch { }
|
|
44
|
-
|
|
45
|
+
// Define substitution does NOT reach this file (served raw from node_modules),
|
|
46
|
+
// so prefer the globalThis seed planted by the entry's defines-seed module —
|
|
47
|
+
// the '/src' literal is a last-resort default and is WRONG for 'app/'-rooted
|
|
48
|
+
// projects.
|
|
49
|
+
const APP_ROOT_VIRTUAL = (typeof __NS_APP_ROOT_VIRTUAL__ === 'string' && __NS_APP_ROOT_VIRTUAL__) || (typeof getGlobalScope().__NS_APP_ROOT_VIRTUAL__ === 'string' && getGlobalScope().__NS_APP_ROOT_VIRTUAL__) || '/src';
|
|
45
50
|
const APP_VIRTUAL_WITH_SLASH = APP_ROOT_VIRTUAL.endsWith('/') ? APP_ROOT_VIRTUAL : `${APP_ROOT_VIRTUAL}/`;
|
|
46
51
|
const APP_MAIN_ENTRY_SPEC = `${APP_VIRTUAL_WITH_SLASH}app.ts`;
|
|
47
52
|
// Policy: by default, let the app's own main entry mount initially; HMR client handles updates/remounts only.
|
|
@@ -118,7 +123,7 @@ function setUpdateOverlayStage(stage, info) {
|
|
|
118
123
|
// A module-local Set would not be shared across instances; the global one
|
|
119
124
|
// is.
|
|
120
125
|
function getNsSolidHmrListenerSet() {
|
|
121
|
-
const g =
|
|
126
|
+
const g = getGlobalScope();
|
|
122
127
|
let set = g.__ns_solid_hmr_listener_set;
|
|
123
128
|
if (!set) {
|
|
124
129
|
set = new Set();
|
|
@@ -148,7 +153,7 @@ function nsSolidHmrEmit(ev) {
|
|
|
148
153
|
}
|
|
149
154
|
}
|
|
150
155
|
try {
|
|
151
|
-
const g =
|
|
156
|
+
const g = getGlobalScope();
|
|
152
157
|
g.__ns_solid_hmr_subscribe = nsSolidHmrSubscribe;
|
|
153
158
|
// Eagerly create the listener set so the global exists at module load time.
|
|
154
159
|
getNsSolidHmrListenerSet();
|
|
@@ -246,7 +251,7 @@ let processingPromise = null;
|
|
|
246
251
|
// Detect whether the early placeholder root is still active on screen
|
|
247
252
|
function isPlaceholderActive() {
|
|
248
253
|
try {
|
|
249
|
-
const g =
|
|
254
|
+
const g = getGlobalScope();
|
|
250
255
|
if (g.__NS_DEV_PLACEHOLDER_ROOT_VIEW__)
|
|
251
256
|
return true;
|
|
252
257
|
if (g.__NS_DEV_PLACEHOLDER_ROOT_EARLY__)
|
|
@@ -276,7 +281,7 @@ function applyFullGraph(payload) {
|
|
|
276
281
|
// causes a double-mount race (rescue fires at 450ms, then main.ts fires ~1s later,
|
|
277
282
|
// causing a visual flash and leaving the app in an inconsistent state).
|
|
278
283
|
try {
|
|
279
|
-
const g =
|
|
284
|
+
const g = getGlobalScope();
|
|
280
285
|
const bootDone = !!g.__NS_HMR_BOOT_COMPLETE__;
|
|
281
286
|
if (!bootDone && !initialMounted && !initialMounting && !g.__NS_HMR_RESCUE_SCHEDULED__ && TARGET_FLAVOR !== 'typescript') {
|
|
282
287
|
// simple snapshot helpers
|
|
@@ -462,7 +467,7 @@ function applyFullGraph(payload) {
|
|
|
462
467
|
console.log('[hmr][init] mounting initial root from', candidate, 'flavor=', TARGET_FLAVOR);
|
|
463
468
|
// Android-only: avoid racing entry-runtime reset and Activity bring-up
|
|
464
469
|
try {
|
|
465
|
-
const g =
|
|
470
|
+
const g = getGlobalScope();
|
|
466
471
|
const App = getCore('Application') || g.Application;
|
|
467
472
|
const isAndroid = !!(App && App.android !== undefined);
|
|
468
473
|
if (isAndroid) {
|
|
@@ -633,7 +638,7 @@ function applyDelta(payload) {
|
|
|
633
638
|
}
|
|
634
639
|
if (isAppMainEntryId(id)) {
|
|
635
640
|
try {
|
|
636
|
-
const exists =
|
|
641
|
+
const exists = getGlobalScope().require?.(id) || globalThis.__nsGetModuleExports?.(id);
|
|
637
642
|
if (!exists && VERBOSE)
|
|
638
643
|
console.log(`[hmr][delta] skipping unresolved ${APP_MAIN_ENTRY_SPEC} change`);
|
|
639
644
|
if (!exists)
|
|
@@ -651,7 +656,7 @@ function applyDelta(payload) {
|
|
|
651
656
|
}
|
|
652
657
|
// Deterministic navigation using the current Vue app instance rather than vendor-held rootApp.
|
|
653
658
|
function __nsNavigateUsingApp(comp, opts = {}) {
|
|
654
|
-
const g =
|
|
659
|
+
const g = getGlobalScope();
|
|
655
660
|
CLIENT_STRATEGY?.beforeNavigateBuild?.();
|
|
656
661
|
const AppFactory = g.createApp;
|
|
657
662
|
const RootCtor = g.NSVRoot;
|
|
@@ -777,6 +782,184 @@ try {
|
|
|
777
782
|
globalThis.__nsNavigateUsingApp = __nsNavigateUsingApp;
|
|
778
783
|
}
|
|
779
784
|
catch { }
|
|
785
|
+
const openModalRecords = [];
|
|
786
|
+
let modalTrackingInstalled = false;
|
|
787
|
+
/**
|
|
788
|
+
* Map a served/graph module id (e.g. `/app/modal-page.xml`) to its app-root
|
|
789
|
+
* relative path (`modal-page.xml`). Single mapping point — the raw-asset
|
|
790
|
+
* re-registration, page-navigation targets, and modal matching all derive
|
|
791
|
+
* from this; keep them in sync by construction.
|
|
792
|
+
*/
|
|
793
|
+
function toAppRelativePath(id) {
|
|
794
|
+
try {
|
|
795
|
+
const spec = normalizeSpec(id);
|
|
796
|
+
const appVirtual = APP_VIRTUAL_WITH_SLASH.replace(/^\//, '');
|
|
797
|
+
let relPath = spec.startsWith('/') ? spec.slice(1) : spec;
|
|
798
|
+
if (relPath.startsWith(appVirtual))
|
|
799
|
+
relPath = relPath.slice(appVirtual.length);
|
|
800
|
+
return relPath || null;
|
|
801
|
+
}
|
|
802
|
+
catch {
|
|
803
|
+
return null;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
/** App-root relative module name (no extension) for page-shaped files, else null. */
|
|
807
|
+
function toAppModuleName(id) {
|
|
808
|
+
const relPath = toAppRelativePath(id);
|
|
809
|
+
if (!relPath || !/\.(xml|ts|js)$/i.test(relPath))
|
|
810
|
+
return null;
|
|
811
|
+
return relPath.replace(/\.(xml|ts|js)$/i, '');
|
|
812
|
+
}
|
|
813
|
+
function ensureModalTracking() {
|
|
814
|
+
if (modalTrackingInstalled)
|
|
815
|
+
return;
|
|
816
|
+
try {
|
|
817
|
+
const View = getCore('View') || getGlobalScope().View;
|
|
818
|
+
const proto = View?.prototype;
|
|
819
|
+
if (!proto || typeof proto.showModal !== 'function')
|
|
820
|
+
return;
|
|
821
|
+
const orig = proto.showModal;
|
|
822
|
+
if (orig.__nsHmrModalTracked) {
|
|
823
|
+
modalTrackingInstalled = true;
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
const wrapped = function (...args) {
|
|
827
|
+
const result = orig.apply(this, args);
|
|
828
|
+
try {
|
|
829
|
+
if (typeof args[0] === 'string' && result) {
|
|
830
|
+
// Mirror core's getModalOptions arg shapes: (moduleName, options)
|
|
831
|
+
// or the deprecated positional form.
|
|
832
|
+
const options = args.length === 2 && args[1] && typeof args[1] === 'object' ? args[1] : { context: args[1], closeCallback: args[2], fullscreen: args[3], animated: args[4], stretched: args[5] };
|
|
833
|
+
const moduleName = String(args[0])
|
|
834
|
+
.replace(/^\.\//, '')
|
|
835
|
+
.replace(/\.(xml|ts|js)$/i, '');
|
|
836
|
+
openModalRecords.push({ moduleName, options, parent: this, modal: result });
|
|
837
|
+
if (VERBOSE)
|
|
838
|
+
console.log('[hmr][modal] tracked open modal', moduleName);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
catch { }
|
|
842
|
+
return result;
|
|
843
|
+
};
|
|
844
|
+
wrapped.__nsHmrModalTracked = true;
|
|
845
|
+
proto.showModal = wrapped;
|
|
846
|
+
modalTrackingInstalled = true;
|
|
847
|
+
}
|
|
848
|
+
catch (e) {
|
|
849
|
+
if (VERBOSE)
|
|
850
|
+
console.warn('[hmr][modal] tracking install failed', e);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Enumerate the modals that are currently presented AND were opened by module
|
|
855
|
+
* name, with everything needed to re-present them.
|
|
856
|
+
*
|
|
857
|
+
* Source of truth is core's live modal stack (`_getRootModalViews()`):
|
|
858
|
+
* - `modal._moduleName` — set by the Builder on every createViewFromEntry
|
|
859
|
+
* view (longstanding, used by livesync), so available on any core.
|
|
860
|
+
* - `modal._modalOptions` — the original ShowModalOptions, stored by core's
|
|
861
|
+
* `_showNativeModalView` (newer cores). For older cores the showModal
|
|
862
|
+
* wrap's records (see ensureModalTracking) fill the gap.
|
|
863
|
+
* - `modal._modalParent` — the presenting view.
|
|
864
|
+
* Stale wrap records are pruned against the live stack while we're here.
|
|
865
|
+
*/
|
|
866
|
+
function getOpenStringModuleModals() {
|
|
867
|
+
const out = [];
|
|
868
|
+
try {
|
|
869
|
+
const App = getCore('Application');
|
|
870
|
+
const root = App?.getRootView?.() || App?._rootView;
|
|
871
|
+
const stack = root?._getRootModalViews?.() || [];
|
|
872
|
+
// Prune wrap records whose modal is gone (keeps the fallback list small).
|
|
873
|
+
for (let i = openModalRecords.length - 1; i >= 0; i--) {
|
|
874
|
+
if (!stack.includes(openModalRecords[i].modal)) {
|
|
875
|
+
openModalRecords.splice(i, 1);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
for (const modal of stack) {
|
|
879
|
+
const record = openModalRecords.find((r) => r.modal === modal);
|
|
880
|
+
const rawModuleName = typeof modal?._moduleName === 'string' && modal._moduleName ? modal._moduleName : record?.moduleName;
|
|
881
|
+
const parent = modal?._modalParent || record?.parent;
|
|
882
|
+
const options = modal?._modalOptions || record?.options;
|
|
883
|
+
if (!rawModuleName || !parent)
|
|
884
|
+
continue;
|
|
885
|
+
const moduleName = String(rawModuleName)
|
|
886
|
+
.replace(/^\.\//, '')
|
|
887
|
+
.replace(/\.(xml|ts|js)$/i, '');
|
|
888
|
+
out.push({ moduleName, options, parent, modal });
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
catch (e) {
|
|
892
|
+
if (VERBOSE)
|
|
893
|
+
console.warn('[hmr][modal] open-modal enumeration failed', e);
|
|
894
|
+
}
|
|
895
|
+
return out;
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Close and re-present an open modal so it rebuilds from the freshly
|
|
899
|
+
* re-registered XML/code-behind. Core clears the modal stack synchronously on
|
|
900
|
+
* close but the NATIVE dismissal completes asynchronously; iOS refuses a
|
|
901
|
+
* present while a dismissal is in flight. Newer cores fire `closedModally` on
|
|
902
|
+
* the modal at exactly that completion point — preferred signal. Older cores
|
|
903
|
+
* fall back to polling `isLoaded` (flipped by `_tearDownUI` in the same
|
|
904
|
+
* completion callback).
|
|
905
|
+
*/
|
|
906
|
+
async function reshowOpenModal(record) {
|
|
907
|
+
const { parent, modal, moduleName, options } = record;
|
|
908
|
+
await new Promise((resolve) => {
|
|
909
|
+
let settled = false;
|
|
910
|
+
const finish = () => {
|
|
911
|
+
if (!settled) {
|
|
912
|
+
settled = true;
|
|
913
|
+
resolve();
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
let eventArmed = false;
|
|
917
|
+
try {
|
|
918
|
+
if (typeof modal.once === 'function') {
|
|
919
|
+
modal.once('closedModally', finish);
|
|
920
|
+
eventArmed = true;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
catch { }
|
|
924
|
+
// Poll fallback (also the safety net if the event never fires —
|
|
925
|
+
// e.g. an interactive-dismiss cancellation).
|
|
926
|
+
const deadline = Date.now() + 2000;
|
|
927
|
+
const poll = () => {
|
|
928
|
+
if (settled)
|
|
929
|
+
return;
|
|
930
|
+
let stillLoaded = false;
|
|
931
|
+
try {
|
|
932
|
+
stillLoaded = !!modal.isLoaded;
|
|
933
|
+
}
|
|
934
|
+
catch { }
|
|
935
|
+
if ((!eventArmed && !stillLoaded) || Date.now() > deadline) {
|
|
936
|
+
finish();
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
setTimeout(poll, 50);
|
|
940
|
+
};
|
|
941
|
+
setTimeout(poll, 50);
|
|
942
|
+
try {
|
|
943
|
+
modal.closeModal();
|
|
944
|
+
}
|
|
945
|
+
catch (e) {
|
|
946
|
+
if (VERBOSE)
|
|
947
|
+
console.warn('[hmr][modal] close failed for', moduleName, e);
|
|
948
|
+
finish();
|
|
949
|
+
}
|
|
950
|
+
});
|
|
951
|
+
// One settle beat so the platform finishes releasing the presentation
|
|
952
|
+
// before the new present begins.
|
|
953
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
954
|
+
try {
|
|
955
|
+
parent.showModal(moduleName, { ...(options || {}), animated: false });
|
|
956
|
+
if (VERBOSE)
|
|
957
|
+
console.log('[hmr][modal] re-presented', moduleName);
|
|
958
|
+
}
|
|
959
|
+
catch (e) {
|
|
960
|
+
console.warn('[hmr][modal] re-present failed for', moduleName, e);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
780
963
|
async function processQueue() {
|
|
781
964
|
if (!globalThis.__NS_HMR_BOOT_COMPLETE__) {
|
|
782
965
|
if (VERBOSE)
|
|
@@ -857,6 +1040,27 @@ async function processQueue() {
|
|
|
857
1040
|
if (VERBOSE)
|
|
858
1041
|
console.log('[hmr][queue] re-import', { id, spec, url });
|
|
859
1042
|
const mod = await import(/* @vite-ignore */ url);
|
|
1043
|
+
// TS/XML flavor: refresh the bundler module registry with the fresh
|
|
1044
|
+
// exports so Builder.createViewFromEntry / loadModule('<page>')
|
|
1045
|
+
// resolves the NEW code-behind (tap handlers, page events) instead
|
|
1046
|
+
// of the stale module captured in the boot bundle. Without this,
|
|
1047
|
+
// XML re-renders pick up new markup but keep old behavior.
|
|
1048
|
+
if (TARGET_FLAVOR === 'typescript' && mod && /\.(ts|js)$/i.test(id)) {
|
|
1049
|
+
try {
|
|
1050
|
+
const g = getGlobalScope();
|
|
1051
|
+
const moduleName = toAppModuleName(id);
|
|
1052
|
+
if (moduleName && typeof g.registerModule === 'function') {
|
|
1053
|
+
g.registerModule(moduleName, () => mod);
|
|
1054
|
+
g.registerModule('./' + moduleName, () => mod);
|
|
1055
|
+
if (VERBOSE)
|
|
1056
|
+
console.log('[hmr][queue] re-registered code-behind', moduleName);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
catch (e) {
|
|
1060
|
+
if (VERBOSE)
|
|
1061
|
+
console.warn('[hmr][queue] code-behind re-register failed for', id, e);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
860
1064
|
}
|
|
861
1065
|
catch (e) {
|
|
862
1066
|
if (VERBOSE)
|
|
@@ -976,7 +1180,7 @@ async function processQueue() {
|
|
|
976
1180
|
const findRouter = () => {
|
|
977
1181
|
if (discoveredRouter)
|
|
978
1182
|
return discoveredRouter;
|
|
979
|
-
const g =
|
|
1183
|
+
const g = getGlobalScope();
|
|
980
1184
|
if (g.__ns_router?.routesById)
|
|
981
1185
|
return (discoveredRouter = g.__ns_router);
|
|
982
1186
|
// Fallback: scan common global keys for router
|
|
@@ -1137,7 +1341,7 @@ async function processQueue() {
|
|
|
1137
1341
|
// This preserves the shell (Frame, ActionBar, etc.) that the app's
|
|
1138
1342
|
// own bootstrapping wires up via `Application.run`.
|
|
1139
1343
|
try {
|
|
1140
|
-
const g =
|
|
1344
|
+
const g = getGlobalScope();
|
|
1141
1345
|
const App = getCore('Application') || g.Application;
|
|
1142
1346
|
if (!App || typeof App.resetRootView !== 'function') {
|
|
1143
1347
|
if (VERBOSE)
|
|
@@ -1163,10 +1367,9 @@ async function processQueue() {
|
|
|
1163
1367
|
const rawContent = await resp.text();
|
|
1164
1368
|
// Register under all nickname variants the module registry uses.
|
|
1165
1369
|
// The bundler context registers XML as e.g., './main-page.xml' and 'main-page.xml'
|
|
1166
|
-
const
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
relPath = relPath.slice(appVirtual.length);
|
|
1370
|
+
const relPath = toAppRelativePath(id);
|
|
1371
|
+
if (!relPath)
|
|
1372
|
+
continue;
|
|
1170
1373
|
const nicknames = ['./' + relPath, relPath];
|
|
1171
1374
|
// Also add without extension for CSS
|
|
1172
1375
|
const extIdx = relPath.lastIndexOf('.');
|
|
@@ -1192,21 +1395,24 @@ async function processQueue() {
|
|
|
1192
1395
|
}
|
|
1193
1396
|
}
|
|
1194
1397
|
}
|
|
1398
|
+
// Modal-aware refresh: pages currently PRESENTED AS MODALS must be
|
|
1399
|
+
// closed + re-presented in place — navigating the top frame to a
|
|
1400
|
+
// modal's page would push it as a frame page, and resetRootView
|
|
1401
|
+
// would dismiss the modal entirely. State comes from core's live
|
|
1402
|
+
// modal stack (_moduleName/_modalOptions/_modalParent); the
|
|
1403
|
+
// showModal wrap only backfills options on older cores.
|
|
1404
|
+
ensureModalTracking();
|
|
1405
|
+
const openModals = getOpenStringModuleModals();
|
|
1406
|
+
const changedModuleNames = new Set(drained.map(toAppModuleName).filter(Boolean));
|
|
1407
|
+
const modalsToReshow = openModals.filter((record) => changedModuleNames.has(record.moduleName));
|
|
1408
|
+
const reshowModuleNames = new Set(modalsToReshow.map((record) => record.moduleName));
|
|
1195
1409
|
// Determine if we can navigate in-place to a changed page
|
|
1196
1410
|
// instead of resetting all the way back to app-root.
|
|
1197
1411
|
// This keeps the user on the page they're editing for faster iteration.
|
|
1198
1412
|
const changedXmlPages = drained
|
|
1199
1413
|
.filter((id) => /\.xml$/i.test(id))
|
|
1200
|
-
.map((id) =>
|
|
1201
|
-
|
|
1202
|
-
const appVirtual = APP_VIRTUAL_WITH_SLASH.replace(/^\//, '');
|
|
1203
|
-
let relPath = spec.startsWith('/') ? spec.slice(1) : spec;
|
|
1204
|
-
if (relPath.startsWith(appVirtual))
|
|
1205
|
-
relPath = relPath.slice(appVirtual.length);
|
|
1206
|
-
// Strip .xml extension to get the moduleName (e.g., 'pages/status-bar')
|
|
1207
|
-
return relPath.replace(/\.xml$/i, '');
|
|
1208
|
-
})
|
|
1209
|
-
.filter((m) => m && m !== 'app-root');
|
|
1414
|
+
.map((id) => toAppModuleName(id))
|
|
1415
|
+
.filter((m) => m && m !== 'app-root' && !reshowModuleNames.has(m));
|
|
1210
1416
|
// Resolve the topmost Frame from the bundled realm.
|
|
1211
1417
|
// Frame.topmost() relies on an internal frameStack array, so we must
|
|
1212
1418
|
// call it on the bundled-realm class. Multiple strategies to find it:
|
|
@@ -1242,7 +1448,7 @@ async function processQueue() {
|
|
|
1242
1448
|
catch { }
|
|
1243
1449
|
}
|
|
1244
1450
|
if (VERBOSE)
|
|
1245
|
-
console.log('[hmr][queue] TS: changedXmlPages=', changedXmlPages, 'topFrame=', !!topFrame);
|
|
1451
|
+
console.log('[hmr][queue] TS: changedXmlPages=', changedXmlPages, 'topFrame=', !!topFrame, 'modalsToReshow=', modalsToReshow.length);
|
|
1246
1452
|
if (changedXmlPages.length > 0 && topFrame) {
|
|
1247
1453
|
// Navigate the current frame to the changed page directly.
|
|
1248
1454
|
// Use the last changed XML page (most specific).
|
|
@@ -1257,11 +1463,21 @@ async function processQueue() {
|
|
|
1257
1463
|
App.resetRootView({ moduleName: 'app-root' });
|
|
1258
1464
|
}
|
|
1259
1465
|
}
|
|
1260
|
-
else {
|
|
1466
|
+
else if (modalsToReshow.length === 0) {
|
|
1467
|
+
// No frame page to refresh and no open modal owns the change —
|
|
1468
|
+
// fall back to a full root reset. (Skipped when an open modal is
|
|
1469
|
+
// being re-presented below: resetRootView would dismiss it.)
|
|
1261
1470
|
if (VERBOSE)
|
|
1262
1471
|
console.log('[hmr][queue] TS flavor: resetRootView(app-root) after changes');
|
|
1263
1472
|
App.resetRootView({ moduleName: 'app-root' });
|
|
1264
1473
|
}
|
|
1474
|
+
// Re-present any open modals whose XML/code-behind changed. The
|
|
1475
|
+
// modules were already re-registered above (raw XML assets + fresh
|
|
1476
|
+
// code-behind exports), so the re-presented modal rebuilds from
|
|
1477
|
+
// the new content while the page beneath it stays put.
|
|
1478
|
+
for (const record of modalsToReshow) {
|
|
1479
|
+
await reshowOpenModal(record);
|
|
1480
|
+
}
|
|
1265
1481
|
}
|
|
1266
1482
|
catch (e) {
|
|
1267
1483
|
console.warn('[hmr][queue] TS flavor: resetRootView(app-root) failed', e);
|
|
@@ -1331,7 +1547,7 @@ function connectHmr() {
|
|
|
1331
1547
|
// Build ordered host candidates with preference to the active HTTP origin
|
|
1332
1548
|
const orderedHosts = [];
|
|
1333
1549
|
try {
|
|
1334
|
-
const g =
|
|
1550
|
+
const g = getGlobalScope();
|
|
1335
1551
|
const httpOrigin = g && typeof g.__NS_HTTP_ORIGIN__ === 'string' ? g.__NS_HTTP_ORIGIN__ : undefined;
|
|
1336
1552
|
if (httpOrigin) {
|
|
1337
1553
|
try {
|
|
@@ -1511,7 +1727,7 @@ async function handleHmrMessage(ev) {
|
|
|
1511
1727
|
if (msg.type === 'ns:hmr-full-graph') {
|
|
1512
1728
|
// Bump a monotonic nonce so HTTP ESM imports can always be cache-busted per update.
|
|
1513
1729
|
try {
|
|
1514
|
-
const g =
|
|
1730
|
+
const g = getGlobalScope();
|
|
1515
1731
|
g.__NS_HMR_IMPORT_NONCE__ = (typeof g.__NS_HMR_IMPORT_NONCE__ === 'number' ? g.__NS_HMR_IMPORT_NONCE__ : 0) + 1;
|
|
1516
1732
|
}
|
|
1517
1733
|
catch { }
|
|
@@ -1636,7 +1852,7 @@ async function handleHmrMessage(ev) {
|
|
|
1636
1852
|
if (msg.type === 'ns:hmr-delta') {
|
|
1637
1853
|
// Bump a monotonic nonce so HTTP ESM imports can always be cache-busted per update.
|
|
1638
1854
|
try {
|
|
1639
|
-
const g =
|
|
1855
|
+
const g = getGlobalScope();
|
|
1640
1856
|
g.__NS_HMR_IMPORT_NONCE__ = (typeof g.__NS_HMR_IMPORT_NONCE__ === 'number' ? g.__NS_HMR_IMPORT_NONCE__ : 0) + 1;
|
|
1641
1857
|
}
|
|
1642
1858
|
catch { }
|
|
@@ -1854,8 +2070,8 @@ function normalizeComponent(input, nameHint) {
|
|
|
1854
2070
|
// If provided a render function, wrap with defineComponent
|
|
1855
2071
|
if (typeof input === 'function') {
|
|
1856
2072
|
CLIENT_STRATEGY?.beforeNavigateBuild?.();
|
|
1857
|
-
const comp =
|
|
1858
|
-
?
|
|
2073
|
+
const comp = getGlobalScope().defineComponent
|
|
2074
|
+
? getGlobalScope().defineComponent({
|
|
1859
2075
|
name: nameHint || input.name || 'AnonymousSFC',
|
|
1860
2076
|
render: input,
|
|
1861
2077
|
})
|
|
@@ -1865,8 +2081,8 @@ function normalizeComponent(input, nameHint) {
|
|
|
1865
2081
|
// If object has a render function property
|
|
1866
2082
|
if (input?.render && typeof input.render === 'function') {
|
|
1867
2083
|
CLIENT_STRATEGY?.beforeNavigateBuild?.();
|
|
1868
|
-
const comp =
|
|
1869
|
-
?
|
|
2084
|
+
const comp = getGlobalScope().defineComponent
|
|
2085
|
+
? getGlobalScope().defineComponent({
|
|
1870
2086
|
name: nameHint || input.name || 'AnonymousSFC',
|
|
1871
2087
|
render: input.render,
|
|
1872
2088
|
})
|
|
@@ -1965,7 +2181,7 @@ async function performResetRoot(newComponent) {
|
|
|
1965
2181
|
return factory;
|
|
1966
2182
|
}
|
|
1967
2183
|
// Android readiness before any root changes
|
|
1968
|
-
const App = getCore('Application') ||
|
|
2184
|
+
const App = getCore('Application') || getGlobalScope().Application;
|
|
1969
2185
|
const isAndroid = !!(App && App.android !== undefined);
|
|
1970
2186
|
if (isAndroid) {
|
|
1971
2187
|
const isReady = () => {
|
|
@@ -2067,7 +2283,7 @@ async function performResetRoot(newComponent) {
|
|
|
2067
2283
|
}
|
|
2068
2284
|
catch { }
|
|
2069
2285
|
try {
|
|
2070
|
-
const AppAny = getCore('Application') ||
|
|
2286
|
+
const AppAny = getCore('Application') || getGlobalScope().Application;
|
|
2071
2287
|
isIOS = !!(AppAny && AppAny.ios !== undefined);
|
|
2072
2288
|
}
|
|
2073
2289
|
catch { }
|
|
@@ -2077,7 +2293,7 @@ async function performResetRoot(newComponent) {
|
|
|
2077
2293
|
// - Otherwise (subsequent HMR updates with an authoritative Frame already in place), re-use the
|
|
2078
2294
|
// current app Frame and navigate to the new Page. This avoids a brief flash that can occur
|
|
2079
2295
|
// when swapping the entire root view on Android. The placeholder is never involved here.
|
|
2080
|
-
const gAnyForPolicy =
|
|
2296
|
+
const gAnyForPolicy = getGlobalScope();
|
|
2081
2297
|
const placeholderFrame = (() => {
|
|
2082
2298
|
try {
|
|
2083
2299
|
return gAnyForPolicy.__NS_DEV_PLACEHOLDER_ROOT_VIEW__ || null;
|
|
@@ -2118,7 +2334,7 @@ async function performResetRoot(newComponent) {
|
|
|
2118
2334
|
console.log('[hmr-client] full root replacement via resetRootView (placeholder will be discarded)', { isFrameRoot, isIOS, hadPlaceholder });
|
|
2119
2335
|
// Fallback or preferred path: resetRootView with a creator that builds a fresh Frame and navigates to the new Page
|
|
2120
2336
|
try {
|
|
2121
|
-
const App2 = getCore('Application') ||
|
|
2337
|
+
const App2 = getCore('Application') || getGlobalScope().Application;
|
|
2122
2338
|
if (!App2 || typeof App2.resetRootView !== 'function') {
|
|
2123
2339
|
console.warn('[hmr-client] Application.resetRootView unavailable');
|
|
2124
2340
|
return false;
|
|
@@ -2137,7 +2353,7 @@ async function performResetRoot(newComponent) {
|
|
|
2137
2353
|
if (VERBOSE)
|
|
2138
2354
|
console.warn('[hmr-client] iOS Application.window is boolean false; attempting to clear cached window');
|
|
2139
2355
|
try {
|
|
2140
|
-
const g =
|
|
2356
|
+
const g = getGlobalScope();
|
|
2141
2357
|
const reg = g.__nsVendorRegistry;
|
|
2142
2358
|
const req = reg?.get ? g.__nsVendorRequire || g.__nsRequire || g.require : g.__nsRequire || g.require;
|
|
2143
2359
|
let helpers = null;
|
|
@@ -2241,6 +2457,20 @@ export function initHmrClient(opts) {
|
|
|
2241
2457
|
}
|
|
2242
2458
|
g.__NS_HMR_CLIENT_ACTIVE__ = true;
|
|
2243
2459
|
ensureCoreAliasesOnGlobalThis();
|
|
2460
|
+
// XML flavor: record string-module modals from the moment the client is up
|
|
2461
|
+
// so an already-open modal can be re-presented when its files change.
|
|
2462
|
+
// Installed at init (not first-update time) because the wrap can only
|
|
2463
|
+
// observe showModal calls made AFTER it lands. Retried briefly because
|
|
2464
|
+
// getCore('View') may not resolve until the vendor realm finishes booting.
|
|
2465
|
+
if (TARGET_FLAVOR === 'typescript') {
|
|
2466
|
+
const tryInstallModalTracking = (attempts) => {
|
|
2467
|
+
ensureModalTracking();
|
|
2468
|
+
if (!modalTrackingInstalled && attempts > 0) {
|
|
2469
|
+
setTimeout(() => tryInstallModalTracking(attempts - 1), 250);
|
|
2470
|
+
}
|
|
2471
|
+
};
|
|
2472
|
+
tryInstallModalTracking(40);
|
|
2473
|
+
}
|
|
2244
2474
|
// Defer WebSocket connection until boot completes to avoid native V8 crashes
|
|
2245
2475
|
// caused by concurrent WebSocket message handling + HTTP fetch during early startup.
|
|
2246
2476
|
// The WebSocket is only needed for HMR updates, not the initial boot sequence.
|