@nativescript/vite 0.0.2 → 1.0.1
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/README.md +37 -0
- package/bin/cli.cjs +32 -0
- package/configuration/angular.js +66 -175
- package/configuration/angular.js.map +1 -1
- package/configuration/base.js +18 -21
- package/configuration/base.js.map +1 -1
- package/configuration/javascript.js +6 -5
- package/configuration/javascript.js.map +1 -1
- package/configuration/typescript.js +6 -5
- package/configuration/typescript.js.map +1 -1
- package/helpers/{angular-linker.js → angular/angular-linker.js} +27 -19
- package/helpers/angular/angular-linker.js.map +1 -0
- package/helpers/angular/shared-linker.d.ts +4 -0
- package/helpers/angular/shared-linker.js +39 -0
- package/helpers/angular/shared-linker.js.map +1 -0
- package/helpers/angular/util.d.ts +1 -0
- package/helpers/angular/util.js +67 -0
- package/helpers/angular/util.js.map +1 -0
- package/helpers/global-defines.d.ts +2 -0
- package/helpers/global-defines.js +5 -0
- package/helpers/global-defines.js.map +1 -1
- package/helpers/init.d.ts +1 -0
- package/helpers/init.js +119 -0
- package/helpers/init.js.map +1 -0
- package/helpers/logging.js +4 -0
- package/helpers/logging.js.map +1 -1
- package/helpers/main-entry.js +12 -4
- package/helpers/main-entry.js.map +1 -1
- package/helpers/nativeclass-transform.js +1 -1
- package/helpers/nativeclass-transform.js.map +1 -1
- package/helpers/utils.d.ts +4 -0
- package/helpers/utils.js +55 -0
- package/helpers/utils.js.map +1 -1
- package/hmr/client/index.js +257 -84
- package/hmr/client/index.js.map +1 -1
- package/hmr/entry-runtime.js +1 -1
- package/hmr/entry-runtime.js.map +1 -1
- package/hmr/frameworks/angular/client/index.d.ts +8 -0
- package/hmr/frameworks/angular/client/index.js +59 -0
- package/hmr/frameworks/angular/client/index.js.map +1 -0
- package/hmr/frameworks/angular/server/linker.d.ts +1 -0
- package/hmr/frameworks/angular/server/linker.js +101 -0
- package/hmr/frameworks/angular/server/linker.js.map +1 -0
- package/hmr/frameworks/angular/server/strategy.js +106 -20
- package/hmr/frameworks/angular/server/strategy.js.map +1 -1
- package/hmr/frameworks/solid/server/strategy.d.ts +2 -0
- package/hmr/frameworks/solid/server/strategy.js +56 -0
- package/hmr/frameworks/solid/server/strategy.js.map +1 -0
- package/hmr/frameworks/typescript/server/strategy.d.ts +2 -0
- package/hmr/frameworks/typescript/server/strategy.js +125 -0
- package/hmr/frameworks/typescript/server/strategy.js.map +1 -0
- package/hmr/frameworks/vue/client/index.js +7 -3
- package/hmr/frameworks/vue/client/index.js.map +1 -1
- package/hmr/frameworks/vue/server/strategy.js +3 -4
- package/hmr/frameworks/vue/server/strategy.js.map +1 -1
- package/hmr/server/index.js +5 -1
- package/hmr/server/index.js.map +1 -1
- package/hmr/server/websocket.d.ts +6 -0
- package/hmr/server/websocket.js +117 -26
- package/hmr/server/websocket.js.map +1 -1
- package/hmr/shared/vendor/manifest.d.ts +1 -0
- package/hmr/shared/vendor/manifest.js +38 -14
- package/hmr/shared/vendor/manifest.js.map +1 -1
- package/index.js +11 -0
- package/index.js.map +1 -1
- package/package.json +12 -9
- package/shims/angular-animations-stub.d.ts +62 -2
- package/shims/angular-animations-stub.js +132 -6
- package/shims/angular-animations-stub.js.map +1 -1
- package/helpers/angular-linker.js.map +0 -1
- /package/helpers/{angular-linker.d.ts → angular/angular-linker.d.ts} +0 -0
package/hmr/client/index.js
CHANGED
|
@@ -8,6 +8,9 @@
|
|
|
8
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
|
+
const APP_ROOT_VIRTUAL = typeof __NS_APP_ROOT_VIRTUAL__ === 'string' && __NS_APP_ROOT_VIRTUAL__ ? __NS_APP_ROOT_VIRTUAL__ : '/src';
|
|
12
|
+
const APP_VIRTUAL_WITH_SLASH = APP_ROOT_VIRTUAL.endsWith('/') ? APP_ROOT_VIRTUAL : `${APP_ROOT_VIRTUAL}/`;
|
|
13
|
+
const APP_MAIN_ENTRY_SPEC = `${APP_VIRTUAL_WITH_SLASH}app.ts`;
|
|
11
14
|
// Policy: by default, let the app's own main entry mount initially; HMR client handles updates/remounts only.
|
|
12
15
|
// Flip this to true via global __NS_HMR_ALLOW_INITIAL_MOUNT__ if you need the client to perform the first mount.
|
|
13
16
|
const ALLOW_INITIAL_MOUNT = !!globalThis.__NS_HMR_ALLOW_INITIAL_MOUNT__;
|
|
@@ -87,6 +90,7 @@ installDeepDiagnostics();
|
|
|
87
90
|
* Flavor hooks
|
|
88
91
|
*/
|
|
89
92
|
import { installNsVueDevShims, ensureBackWrapperInstalled, getRootForVue, loadSfcComponent, ensureVueGlobals, ensurePiniaOnApp, addSfcMapping, recordVuePayloadChanges, handleVueSfcRegistry, handleVueSfcRegistryUpdate } from '../frameworks/vue/client/index.js';
|
|
93
|
+
import { handleAngularHotUpdateMessage, installAngularHmrClientHooks } from '../frameworks/angular/client/index.js';
|
|
90
94
|
switch (__NS_TARGET_FLAVOR__) {
|
|
91
95
|
case 'vue':
|
|
92
96
|
if (VERBOSE) {
|
|
@@ -101,6 +105,7 @@ switch (__NS_TARGET_FLAVOR__) {
|
|
|
101
105
|
}
|
|
102
106
|
catch { }
|
|
103
107
|
}
|
|
108
|
+
installAngularHmrClientHooks();
|
|
104
109
|
break;
|
|
105
110
|
}
|
|
106
111
|
// Global frame diagnostics: instrument Frame.navigate and Frame.topmost to detect
|
|
@@ -293,6 +298,9 @@ function installDeepDiagnostics() {
|
|
|
293
298
|
let initialMounted = !!globalThis.__NS_HMR_BOOT_COMPLETE__;
|
|
294
299
|
// Prevent duplicate initial-mount scheduling across rapid full-graph broadcasts and re-evaluations
|
|
295
300
|
let initialMounting = !!globalThis.__NS_HMR_INITIAL_MOUNT_IN_PROGRESS__;
|
|
301
|
+
// TypeScript flavor: track registry modules and inferred main id
|
|
302
|
+
let tsModuleSet = null;
|
|
303
|
+
let tsMainId = null;
|
|
296
304
|
const changedQueue = [];
|
|
297
305
|
let processingQueue = false;
|
|
298
306
|
// Detect whether the early placeholder root is still active on screen
|
|
@@ -322,7 +330,7 @@ function applyFullGraph(payload) {
|
|
|
322
330
|
if (VERBOSE)
|
|
323
331
|
console.log('[hmr][graph] full graph applied version', getGraphVersion(), 'modules=', graph.size);
|
|
324
332
|
// Guarded initial mount rescue: if app hasn't replaced the placeholder shortly after graph arrives,
|
|
325
|
-
// perform a one-time mount. This waits briefly to let
|
|
333
|
+
// perform a one-time mount. This waits briefly to let the app's main entry start() run first to avoid double mounts.
|
|
326
334
|
try {
|
|
327
335
|
const g = globalThis;
|
|
328
336
|
const bootDone = !!g.__NS_HMR_BOOT_COMPLETE__;
|
|
@@ -391,26 +399,72 @@ function applyFullGraph(payload) {
|
|
|
391
399
|
catch { }
|
|
392
400
|
return;
|
|
393
401
|
}
|
|
394
|
-
// If placeholder still active and no real root, try a one-time gentle initial mount
|
|
402
|
+
// If placeholder still active and no real root, try a one-time gentle initial mount.
|
|
403
|
+
// Vue: prefer SFCs; TypeScript: import the app main TS module and let performResetRoot handle it once.
|
|
395
404
|
const placeholderActive = isPlaceholderActive();
|
|
396
405
|
if (!placeholderActive)
|
|
397
406
|
return;
|
|
398
407
|
if (VERBOSE)
|
|
399
|
-
console.log('[hmr][init] placeholder persists after delay;
|
|
400
|
-
//
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
408
|
+
console.log('[hmr][init] placeholder persists after delay; evaluating rescue policy');
|
|
409
|
+
// Flavor-specific rescue handling
|
|
410
|
+
if (__NS_TARGET_FLAVOR__ === 'typescript') {
|
|
411
|
+
// For TS apps, perform a one-time resetRootView to the conventional
|
|
412
|
+
// app root module. This mimics what Application.run would do and
|
|
413
|
+
// replaces the placeholder with the real UI without trying to
|
|
414
|
+
// treat app.ts as a component.
|
|
415
|
+
try {
|
|
416
|
+
const App = getCore('Application') || g.Application;
|
|
417
|
+
if (App && typeof App.resetRootView === 'function') {
|
|
418
|
+
if (VERBOSE)
|
|
419
|
+
console.log('[hmr][init] TS flavor: performing rescue resetRootView to moduleName=app-root');
|
|
420
|
+
initialMounting = true;
|
|
421
|
+
try {
|
|
422
|
+
g.__NS_HMR_INITIAL_MOUNT_IN_PROGRESS__ = true;
|
|
423
|
+
}
|
|
424
|
+
catch { }
|
|
425
|
+
App.resetRootView({ moduleName: 'app-root' });
|
|
426
|
+
initialMounted = true;
|
|
427
|
+
try {
|
|
428
|
+
g.__NS_HMR_BOOT_COMPLETE__ = true;
|
|
429
|
+
}
|
|
430
|
+
catch { }
|
|
431
|
+
if (VERBOSE)
|
|
432
|
+
console.log('[hmr][init] TS rescue resetRootView complete');
|
|
433
|
+
}
|
|
434
|
+
else if (VERBOSE) {
|
|
435
|
+
console.warn('[hmr][init] TS flavor: Application.resetRootView unavailable; cannot perform rescue');
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
catch (e) {
|
|
439
|
+
console.warn('[hmr][init] TS rescue resetRootView failed', e);
|
|
440
|
+
}
|
|
441
|
+
finally {
|
|
442
|
+
initialMounting = false;
|
|
443
|
+
try {
|
|
444
|
+
g.__NS_HMR_INITIAL_MOUNT_IN_PROGRESS__ = false;
|
|
445
|
+
}
|
|
446
|
+
catch { }
|
|
447
|
+
}
|
|
448
|
+
return;
|
|
407
449
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
450
|
+
let candidate = null;
|
|
451
|
+
switch (__NS_TARGET_FLAVOR__) {
|
|
452
|
+
case 'vue': {
|
|
453
|
+
const appEntry = graph.get(APP_MAIN_ENTRY_SPEC);
|
|
454
|
+
if (appEntry && Array.isArray(appEntry.deps)) {
|
|
455
|
+
const vueDep = appEntry.deps.find((d) => typeof d === 'string' && /\.vue$/i.test(d));
|
|
456
|
+
if (vueDep)
|
|
457
|
+
candidate = vueDep;
|
|
413
458
|
}
|
|
459
|
+
if (!candidate) {
|
|
460
|
+
for (const id of graph.keys()) {
|
|
461
|
+
if (/\.vue$/i.test(id)) {
|
|
462
|
+
candidate = id;
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
break;
|
|
414
468
|
}
|
|
415
469
|
}
|
|
416
470
|
if (!candidate)
|
|
@@ -459,28 +513,36 @@ function applyFullGraph(payload) {
|
|
|
459
513
|
}
|
|
460
514
|
catch { }
|
|
461
515
|
// On first full graph, if we have not mounted yet, attempt an initial mount
|
|
462
|
-
// by choosing a likely
|
|
516
|
+
// by choosing a likely root component for the active flavor and performing a resetRootView with it.
|
|
463
517
|
try {
|
|
464
518
|
// Short-circuit if boot is complete or an initial mount is already underway (across realms/evals)
|
|
465
519
|
const bootDone = !!globalThis.__NS_HMR_BOOT_COMPLETE__;
|
|
466
520
|
const bootInProgress = !!globalThis.__NS_HMR_INITIAL_MOUNT_IN_PROGRESS__ || initialMounting;
|
|
467
|
-
// Only allow initial mount when explicitly enabled. Rely on the app's own
|
|
521
|
+
// Only allow initial mount when explicitly enabled. Rely on the app's own main entry start() for the first mount
|
|
468
522
|
// to avoid double-mount races that can cause duplicate navigation logs.
|
|
469
523
|
if (ALLOW_INITIAL_MOUNT && !initialMounted && !bootDone && !bootInProgress && !getCurrentApp() && !getRootFrame()) {
|
|
470
|
-
// Prefer the first .vue dependency of /src/app.ts, else first .vue in graph
|
|
471
524
|
let candidate = null;
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
if (
|
|
481
|
-
|
|
482
|
-
|
|
525
|
+
switch (__NS_TARGET_FLAVOR__) {
|
|
526
|
+
case 'vue': {
|
|
527
|
+
const appEntry = graph.get(APP_MAIN_ENTRY_SPEC);
|
|
528
|
+
if (appEntry && Array.isArray(appEntry.deps)) {
|
|
529
|
+
const vueDep = appEntry.deps.find((d) => typeof d === 'string' && /\.vue$/i.test(d));
|
|
530
|
+
if (vueDep)
|
|
531
|
+
candidate = vueDep;
|
|
532
|
+
}
|
|
533
|
+
if (!candidate) {
|
|
534
|
+
for (const id of graph.keys()) {
|
|
535
|
+
if (/\.vue$/i.test(id)) {
|
|
536
|
+
candidate = id;
|
|
537
|
+
break;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
483
540
|
}
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
case 'typescript': {
|
|
544
|
+
// For TS flavor, do not perform client-driven initial mount; rely on Application.run.
|
|
545
|
+
return;
|
|
484
546
|
}
|
|
485
547
|
}
|
|
486
548
|
if (candidate) {
|
|
@@ -493,7 +555,7 @@ function applyFullGraph(payload) {
|
|
|
493
555
|
(async () => {
|
|
494
556
|
try {
|
|
495
557
|
if (VERBOSE)
|
|
496
|
-
console.log('[hmr][init] mounting initial
|
|
558
|
+
console.log('[hmr][init] mounting initial root from', candidate, 'flavor=', __NS_TARGET_FLAVOR__);
|
|
497
559
|
// Android-only: avoid racing entry-runtime reset and Activity bring-up
|
|
498
560
|
try {
|
|
499
561
|
const g = globalThis;
|
|
@@ -526,7 +588,23 @@ function applyFullGraph(payload) {
|
|
|
526
588
|
}
|
|
527
589
|
}
|
|
528
590
|
catch { }
|
|
529
|
-
|
|
591
|
+
let comp = null;
|
|
592
|
+
switch (__NS_TARGET_FLAVOR__) {
|
|
593
|
+
case 'vue':
|
|
594
|
+
comp = await loadSfcComponent(candidate, 'initial_mount');
|
|
595
|
+
break;
|
|
596
|
+
case 'typescript':
|
|
597
|
+
try {
|
|
598
|
+
const url = await requestModuleFromServer(candidate);
|
|
599
|
+
const mod = await import(/* @vite-ignore */ url);
|
|
600
|
+
comp = mod && (mod.default || mod);
|
|
601
|
+
}
|
|
602
|
+
catch (e) {
|
|
603
|
+
if (VERBOSE)
|
|
604
|
+
console.warn('[hmr][init] TS initial mount failed to import', candidate, e);
|
|
605
|
+
}
|
|
606
|
+
break;
|
|
607
|
+
}
|
|
530
608
|
if (comp) {
|
|
531
609
|
const ok = await performResetRoot(comp);
|
|
532
610
|
if (ok) {
|
|
@@ -560,21 +638,31 @@ function applyFullGraph(payload) {
|
|
|
560
638
|
})();
|
|
561
639
|
}
|
|
562
640
|
else if (VERBOSE) {
|
|
563
|
-
console.warn('[hmr][init] no
|
|
641
|
+
console.warn('[hmr][init] no component found in graph to mount initially for flavor', __NS_TARGET_FLAVOR__);
|
|
564
642
|
}
|
|
565
643
|
}
|
|
566
644
|
}
|
|
567
645
|
catch { }
|
|
568
646
|
}
|
|
569
647
|
function applyDelta(payload) {
|
|
570
|
-
|
|
648
|
+
// If versions are out of sync, request a full graph resync, but still
|
|
649
|
+
// opportunistically queue the changed ids so the client can react once
|
|
650
|
+
// the resync arrives. This is especially important for simple TS flavors
|
|
651
|
+
// where we only need a signal that "something changed" to trigger a
|
|
652
|
+
// resetRootView-driven hot update.
|
|
653
|
+
const currentVersion = getGraphVersion();
|
|
654
|
+
const versionMatches = payload.baseVersion === currentVersion;
|
|
655
|
+
if (!versionMatches) {
|
|
571
656
|
if (VERBOSE)
|
|
572
|
-
console.warn('[hmr][graph] version mismatch requesting resync', payload.baseVersion,
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
657
|
+
console.warn('[hmr][graph] version mismatch requesting resync', payload.baseVersion, currentVersion);
|
|
658
|
+
try {
|
|
659
|
+
hmrSocket?.send(JSON.stringify({ type: 'ns:hmr-resync-request' }));
|
|
660
|
+
}
|
|
661
|
+
catch { }
|
|
662
|
+
}
|
|
663
|
+
if (versionMatches) {
|
|
664
|
+
setGraphVersion(payload.newVersion);
|
|
576
665
|
}
|
|
577
|
-
setGraphVersion(payload.newVersion);
|
|
578
666
|
const changed = payload.changed || [];
|
|
579
667
|
switch (__NS_TARGET_FLAVOR__) {
|
|
580
668
|
case 'vue':
|
|
@@ -582,17 +670,19 @@ function applyDelta(payload) {
|
|
|
582
670
|
break;
|
|
583
671
|
}
|
|
584
672
|
(payload.changed || []).forEach((m) => {
|
|
585
|
-
if (m
|
|
586
|
-
|
|
673
|
+
if (!m || !m.id)
|
|
674
|
+
return;
|
|
675
|
+
// Always update the local graph view with the announced module metadata
|
|
676
|
+
graph.set(m.id, { id: m.id, deps: m.deps || [], hash: m.hash || '' });
|
|
587
677
|
});
|
|
588
678
|
(payload.removed || []).forEach((r) => {
|
|
589
679
|
graph.delete(r);
|
|
590
680
|
});
|
|
591
681
|
if (VERBOSE)
|
|
592
|
-
console.log('[hmr][graph] delta applied newVersion', getGraphVersion(), 'changed=', (payload.changed || []).length, 'removed=', (payload.removed || []).length);
|
|
682
|
+
console.log('[hmr][graph] delta applied newVersion', getGraphVersion(), 'changed=', (payload.changed || []).length, 'removed=', (payload.removed || []).length, 'baseVersion=', payload.baseVersion);
|
|
593
683
|
// Queue evaluation of changed modules (placeholder pipeline)
|
|
594
684
|
if (payload.changed?.length) {
|
|
595
|
-
// HARD SUPPRESS: the very first delta (baseVersion 0) commonly includes
|
|
685
|
+
// HARD SUPPRESS: the very first delta (baseVersion 0) commonly includes the app main entry which is already evaluated during bootstrap.
|
|
596
686
|
// Importing it again with a cache-bust often produces a spurious module-not-found (timestamp param treated as distinct file).
|
|
597
687
|
const isInitial = payload.baseVersion === 0;
|
|
598
688
|
// Filter out virtual helper ids (e.g. '\0plugin-vue:export-helper') which we do not evaluate directly
|
|
@@ -620,6 +710,7 @@ function applyDelta(payload) {
|
|
|
620
710
|
if (VERBOSE)
|
|
621
711
|
console.warn('[hmr][prefetch] failed', e);
|
|
622
712
|
}
|
|
713
|
+
const isAppMainEntryId = (value) => value.endsWith(APP_MAIN_ENTRY_SPEC);
|
|
623
714
|
for (const id of realIds) {
|
|
624
715
|
// We now rely on SFC registry update events to trigger root resets for .vue files directly
|
|
625
716
|
if (/\.vue$/i.test(id)) {
|
|
@@ -627,16 +718,16 @@ function applyDelta(payload) {
|
|
|
627
718
|
console.log('[hmr][delta] skipping queue for .vue id; will handle on registry update', id);
|
|
628
719
|
continue;
|
|
629
720
|
}
|
|
630
|
-
if (isInitial &&
|
|
721
|
+
if (isInitial && isAppMainEntryId(id)) {
|
|
631
722
|
if (VERBOSE)
|
|
632
|
-
console.log(
|
|
723
|
+
console.log(`[hmr][delta] suppressing initial ${APP_MAIN_ENTRY_SPEC} evaluation`);
|
|
633
724
|
continue;
|
|
634
725
|
}
|
|
635
|
-
if (
|
|
726
|
+
if (isAppMainEntryId(id)) {
|
|
636
727
|
try {
|
|
637
728
|
const exists = globalThis.require?.(id) || globalThis.__nsGetModuleExports?.(id);
|
|
638
729
|
if (!exists && VERBOSE)
|
|
639
|
-
console.log(
|
|
730
|
+
console.log(`[hmr][delta] skipping unresolved ${APP_MAIN_ENTRY_SPEC} change`);
|
|
640
731
|
if (!exists)
|
|
641
732
|
continue;
|
|
642
733
|
}
|
|
@@ -828,8 +919,72 @@ async function processQueue() {
|
|
|
828
919
|
if (processingQueue)
|
|
829
920
|
return;
|
|
830
921
|
processingQueue = true;
|
|
831
|
-
|
|
832
|
-
|
|
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)
|
|
947
|
+
continue;
|
|
948
|
+
if (VERBOSE)
|
|
949
|
+
console.log('[hmr][queue] re-import', { id, spec, url });
|
|
950
|
+
await import(/* @vite-ignore */ url);
|
|
951
|
+
}
|
|
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`.
|
|
966
|
+
try {
|
|
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
|
+
}
|
|
974
|
+
if (VERBOSE)
|
|
975
|
+
console.log('[hmr][queue] TS flavor: resetRootView(app-root) after changes');
|
|
976
|
+
App.resetRootView({ moduleName: 'app-root' });
|
|
977
|
+
}
|
|
978
|
+
catch (e) {
|
|
979
|
+
console.warn('[hmr][queue] TS flavor: resetRootView(app-root) failed', e);
|
|
980
|
+
}
|
|
981
|
+
break;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
finally {
|
|
986
|
+
processingQueue = false;
|
|
987
|
+
}
|
|
833
988
|
}
|
|
834
989
|
let hmrSocket = null;
|
|
835
990
|
// Track server-announced batches for each version so we can import in-order client-side
|
|
@@ -993,6 +1148,33 @@ async function handleHmrMessage(ev) {
|
|
|
993
1148
|
applyFullGraph(msg);
|
|
994
1149
|
return;
|
|
995
1150
|
}
|
|
1151
|
+
if (msg.type === 'ns:ts-module-registry') {
|
|
1152
|
+
try {
|
|
1153
|
+
const mods = Array.isArray(msg.modules) ? msg.modules.filter((m) => typeof m === 'string') : [];
|
|
1154
|
+
if (mods.length) {
|
|
1155
|
+
if (!tsModuleSet)
|
|
1156
|
+
tsModuleSet = new Set();
|
|
1157
|
+
mods.forEach((m) => tsModuleSet.add(m));
|
|
1158
|
+
// Prefer explicit app main entry if present, else first module
|
|
1159
|
+
if (mods.includes(APP_MAIN_ENTRY_SPEC)) {
|
|
1160
|
+
tsMainId = APP_MAIN_ENTRY_SPEC;
|
|
1161
|
+
}
|
|
1162
|
+
else if (!tsMainId) {
|
|
1163
|
+
tsMainId = mods[0];
|
|
1164
|
+
}
|
|
1165
|
+
if (VERBOSE)
|
|
1166
|
+
console.log('[hmr-client][ts-registry] registered TS modules', {
|
|
1167
|
+
count: tsModuleSet.size,
|
|
1168
|
+
mainId: tsMainId,
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
catch (e) {
|
|
1173
|
+
if (VERBOSE)
|
|
1174
|
+
console.warn('[hmr-client][ts-registry] failed to record', e);
|
|
1175
|
+
}
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
996
1178
|
if (msg.type === 'ns:hmr-delta') {
|
|
997
1179
|
setGraphVersion(Number(msg.newVersion || getGraphVersion() || 0));
|
|
998
1180
|
try {
|
|
@@ -1004,41 +1186,7 @@ async function handleHmrMessage(ev) {
|
|
|
1004
1186
|
applyDelta(msg);
|
|
1005
1187
|
return;
|
|
1006
1188
|
}
|
|
1007
|
-
else if (msg
|
|
1008
|
-
try {
|
|
1009
|
-
if (VERBOSE)
|
|
1010
|
-
console.log('[hmr-client][angular] update', msg);
|
|
1011
|
-
// Minimal safe policy for Angular today: if the app provides a bootstrap
|
|
1012
|
-
// factory via globalThis.__NS_ANGULAR_BOOTSTRAP__, perform a full root
|
|
1013
|
-
// replacement. Otherwise, log a guidance message.
|
|
1014
|
-
const g = globalThis;
|
|
1015
|
-
const App = getCore('Application') || g.Application;
|
|
1016
|
-
const bootstrap = g.__NS_ANGULAR_BOOTSTRAP__;
|
|
1017
|
-
if (typeof App?.resetRootView === 'function' && typeof bootstrap === 'function') {
|
|
1018
|
-
if (VERBOSE)
|
|
1019
|
-
console.log('[hmr-client][angular] resetRootView via bootstrap factory');
|
|
1020
|
-
try {
|
|
1021
|
-
g.__NS_DEV_RESET_IN_PROGRESS__ = true;
|
|
1022
|
-
}
|
|
1023
|
-
catch { }
|
|
1024
|
-
App.resetRootView({ create: () => bootstrap() });
|
|
1025
|
-
setTimeout(() => {
|
|
1026
|
-
try {
|
|
1027
|
-
g.__NS_DEV_RESET_IN_PROGRESS__ = false;
|
|
1028
|
-
}
|
|
1029
|
-
catch { }
|
|
1030
|
-
}, 0);
|
|
1031
|
-
return;
|
|
1032
|
-
}
|
|
1033
|
-
if (VERBOSE)
|
|
1034
|
-
console.warn('[hmr-client][angular] No __NS_ANGULAR_BOOTSTRAP__ factory found; consider exposing one from main.ts');
|
|
1035
|
-
}
|
|
1036
|
-
catch (e) {
|
|
1037
|
-
try {
|
|
1038
|
-
console.warn('[hmr-client][angular] failed to handle update', e && (e.message || e));
|
|
1039
|
-
}
|
|
1040
|
-
catch { }
|
|
1041
|
-
}
|
|
1189
|
+
else if (handleAngularHotUpdateMessage(msg, { getCore, verbose: VERBOSE })) {
|
|
1042
1190
|
return;
|
|
1043
1191
|
}
|
|
1044
1192
|
}
|
|
@@ -1203,6 +1351,31 @@ async function performResetRoot(newComponent) {
|
|
|
1203
1351
|
case 'vue':
|
|
1204
1352
|
cachedRoot = getRootForVue(newComponent, state);
|
|
1205
1353
|
break;
|
|
1354
|
+
case 'typescript': {
|
|
1355
|
+
// For TS flavor, treat the component as a factory or direct NS view.
|
|
1356
|
+
let root = null;
|
|
1357
|
+
try {
|
|
1358
|
+
if (typeof newComponent === 'function') {
|
|
1359
|
+
root = newComponent();
|
|
1360
|
+
}
|
|
1361
|
+
else {
|
|
1362
|
+
root = newComponent;
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
catch (e) {
|
|
1366
|
+
console.warn('[hmr-client][ts] root factory invocation failed', e);
|
|
1367
|
+
}
|
|
1368
|
+
cachedRoot = root || {};
|
|
1369
|
+
// Heuristic: if the root "looks" like a Frame, prefer frame semantics
|
|
1370
|
+
try {
|
|
1371
|
+
const name = String(cachedRoot?.constructor?.name || '').replace(/^_+/, '');
|
|
1372
|
+
if (/^Frame(\$\d+)?$/.test(name)) {
|
|
1373
|
+
rootKind = 'frame';
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
catch { }
|
|
1377
|
+
break;
|
|
1378
|
+
}
|
|
1206
1379
|
}
|
|
1207
1380
|
return cachedRoot;
|
|
1208
1381
|
}
|
|
@@ -1524,7 +1697,7 @@ export function initHmrClient(opts) {
|
|
|
1524
1697
|
setHMRWsUrl(opts.wsUrl);
|
|
1525
1698
|
}
|
|
1526
1699
|
if (VERBOSE)
|
|
1527
|
-
console.log('[hmr-client] Initializing
|
|
1700
|
+
console.log('[hmr-client] Initializing HMR client', getHMRWsUrl() ? `(ws: ${getHMRWsUrl()})` : '');
|
|
1528
1701
|
// Prevent duplicate client initialization across re-evaluations
|
|
1529
1702
|
const g = globalThis;
|
|
1530
1703
|
if (g.__NS_HMR_CLIENT_ACTIVE__) {
|