@nativescript/angular 21.0.1-alpha.2 → 21.0.1-alpha.3
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.
|
@@ -6,8 +6,8 @@ import { __decorate, __param, __metadata } from 'tslib';
|
|
|
6
6
|
import * as i1$2 from '@angular/common';
|
|
7
7
|
import { LocationStrategy, XhrFactory, CommonModule, ɵNullViewportScroller as _NullViewportScroller, ViewportScroller, PlatformLocation, DOCUMENT } from '@angular/common';
|
|
8
8
|
import * as i1$3 from '@angular/router';
|
|
9
|
-
import { DefaultUrlSerializer, Router, PRIMARY_OUTLET, NavigationEnd, ChildrenOutletContexts, ActivatedRoute, RouteReuseStrategy, RouterModule, provideRouter } from '@angular/router';
|
|
10
|
-
import { BehaviorSubject, Subject, fromEvent, defer, Observable as Observable$1 } from 'rxjs';
|
|
9
|
+
import { DefaultUrlSerializer, Router, PRIMARY_OUTLET, NavigationEnd, ChildrenOutletContexts, ActivatedRoute, NavigationCancel, NavigationError, NavigationStart, RouteReuseStrategy, RouterModule, provideRouter } from '@angular/router';
|
|
10
|
+
import { BehaviorSubject, Subject, ReplaySubject, fromEvent, defer, Observable as Observable$1 } from 'rxjs';
|
|
11
11
|
import { filter, map, take, distinctUntilChanged, startWith } from 'rxjs/operators';
|
|
12
12
|
import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
|
|
13
13
|
import { ɵBrowserAnimationBuilder as _BrowserAnimationBuilder, AnimationBuilder } from '@angular/animations';
|
|
@@ -256,6 +256,560 @@ function resetAngularHmrCompiledComponents(core) {
|
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Centralised dev-mode + HMR detection for `@nativescript/angular` helpers.
|
|
261
|
+
*
|
|
262
|
+
* The package ships HMR scaffolding (route tracker, route replay, modal
|
|
263
|
+
* preservation, compiled-component reset) that subscribes to long-lived
|
|
264
|
+
* router and bootstrap streams. None of that work belongs in a production
|
|
265
|
+
* binary — it would attach observers that never fire and keep references
|
|
266
|
+
* that confuse Angular's destroy logic.
|
|
267
|
+
*
|
|
268
|
+
* Every HMR helper consults {@link isAngularHmrEnabled} from its
|
|
269
|
+
* constructor. The check is intentionally cheap (no network, no I/O) so it
|
|
270
|
+
* is safe to call in dependency-injection factories and in fast paths.
|
|
271
|
+
*
|
|
272
|
+
* Detection cascade (returns the first match):
|
|
273
|
+
* 1. **Production build short-circuit** — `ngDevMode === false` means
|
|
274
|
+
* Angular built the app in production mode. We bail immediately.
|
|
275
|
+
* 2. **NativeScript Vite dev signal** — see
|
|
276
|
+
* {@link isNativeScriptViteHmrActive}. We accept either of the two
|
|
277
|
+
* persistent globals the NS Vite root-placeholder installer manages
|
|
278
|
+
* (`__NS_DEV_PLACEHOLDER_ROOT_EARLY__` during early boot,
|
|
279
|
+
* `__NS_HMR_BOOT_COMPLETE__` after the real app root commits) so
|
|
280
|
+
* services that are constructed *after* the placeholder has handed
|
|
281
|
+
* off — e.g. `NativeDialog` instantiated lazily when the user opens
|
|
282
|
+
* their first modal — still detect HMR correctly.
|
|
283
|
+
* 3. **Webpack HMR signal** — `globalThis.__webpack_require__` is set
|
|
284
|
+
* when the webpack runtime is loaded. Combined with the `ngDevMode`
|
|
285
|
+
* short-circuit above, its presence means "webpack dev". The
|
|
286
|
+
* production webpack runtime also sets the global, but `ngDevMode`
|
|
287
|
+
* would already be `false`, so the production case never reaches
|
|
288
|
+
* here.
|
|
289
|
+
*
|
|
290
|
+
* If none of these match, the caller should treat HMR as disabled and
|
|
291
|
+
* skip subscribing to disposal/bootstrap streams.
|
|
292
|
+
*
|
|
293
|
+
* The webpack signal lives on `globalThis` rather than `import.meta` so
|
|
294
|
+
* this file compiles cleanly under `--module commonjs` (the jest spec
|
|
295
|
+
* compiler) and under `--module esnext` (the library build).
|
|
296
|
+
*/
|
|
297
|
+
function isAngularHmrEnabled() {
|
|
298
|
+
if (typeof ngDevMode !== 'undefined' && ngDevMode === false) {
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
return isNativeScriptViteHmrActive() || isWebpackHmrActive();
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* True when the NativeScript Vite dev HMR runtime is active. This is the
|
|
305
|
+
* most reliable signal that the project's `nativescript.config.ts` set
|
|
306
|
+
* `bundler: 'vite'` AND we are running the dev server.
|
|
307
|
+
*
|
|
308
|
+
* The NS Vite root-placeholder installer manages two persistent globals:
|
|
309
|
+
* - `__NS_DEV_PLACEHOLDER_ROOT_EARLY__` is set the moment the placeholder
|
|
310
|
+
* runs (very early, before the real app boots), then **deleted** by
|
|
311
|
+
* `clearPlaceholderGlobals` once `tryFinalizeBootPlaceholder` succeeds.
|
|
312
|
+
* - `__NS_HMR_BOOT_COMPLETE__` is set in the same finalize step and is
|
|
313
|
+
* **never deleted** for the lifetime of the dev session.
|
|
314
|
+
*
|
|
315
|
+
* Callers run the gamut of timing — e.g. the route tracker is constructed
|
|
316
|
+
* during bootstrap (early flag still set) but `NativeDialog` is typically
|
|
317
|
+
* instantiated lazily when the user opens their first modal (early flag
|
|
318
|
+
* already cleared, complete flag set). Checking either global covers both
|
|
319
|
+
* windows. If we only checked the early flag, every late-instantiated
|
|
320
|
+
* service would silently no-op and HMR features (modal preservation,
|
|
321
|
+
* route replay) would appear broken in development.
|
|
322
|
+
*/
|
|
323
|
+
function isNativeScriptViteHmrActive() {
|
|
324
|
+
const g = globalThis;
|
|
325
|
+
return !!(g.__NS_DEV_PLACEHOLDER_ROOT_EARLY__ || g.__NS_HMR_BOOT_COMPLETE__);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* True when the webpack runtime is loaded. The webpack runtime sets
|
|
329
|
+
* `__webpack_require__` on `globalThis` whenever a webpack bundle is
|
|
330
|
+
* executing — both in dev and prod. Callers gate on
|
|
331
|
+
* {@link isAngularHmrEnabled} (not this directly) so the production
|
|
332
|
+
* short-circuit fires first.
|
|
333
|
+
*/
|
|
334
|
+
function isWebpackHmrActive() {
|
|
335
|
+
return typeof globalThis.__webpack_require__ === 'function';
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* True when Angular reports we are running with dev-mode flags. Useful
|
|
339
|
+
* for code paths that want to opt out of cost in production but don't
|
|
340
|
+
* care which bundler is running.
|
|
341
|
+
*/
|
|
342
|
+
function isAngularDevMode() {
|
|
343
|
+
if (typeof ngDevMode === 'undefined') {
|
|
344
|
+
return true;
|
|
345
|
+
}
|
|
346
|
+
return ngDevMode !== false;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
class NativeScriptDebug {
|
|
350
|
+
static { this.animationsTraceCategory = 'ns-animations'; }
|
|
351
|
+
static { this.rendererTraceCategory = 'ns-renderer'; }
|
|
352
|
+
static { this.viewUtilCategory = 'ns-view-util'; }
|
|
353
|
+
static { this.routerTraceCategory = 'ns-router'; }
|
|
354
|
+
static { this.routeReuseStrategyTraceCategory = 'ns-route-reuse-strategy'; }
|
|
355
|
+
static { this.listViewTraceCategory = 'ns-list-view'; }
|
|
356
|
+
static { this.bootstrapCategory = 'bootstrap'; }
|
|
357
|
+
static { this.hmrTraceCategory = 'ns-ng-hmr'; }
|
|
358
|
+
// TODO: migrate all usage to this - avoids extraneous method executions
|
|
359
|
+
static { this.enabled = Trace.isEnabled(); }
|
|
360
|
+
static isLogEnabled() {
|
|
361
|
+
return Trace.isEnabled();
|
|
362
|
+
}
|
|
363
|
+
static animationsLog(message) {
|
|
364
|
+
Trace.write(message, NativeScriptDebug.animationsTraceCategory);
|
|
365
|
+
}
|
|
366
|
+
static rendererLog(msg) {
|
|
367
|
+
Trace.write(msg, NativeScriptDebug.rendererTraceCategory);
|
|
368
|
+
}
|
|
369
|
+
static rendererError(message) {
|
|
370
|
+
Trace.write(message, NativeScriptDebug.rendererTraceCategory, Trace.messageType.error);
|
|
371
|
+
}
|
|
372
|
+
static viewUtilLog(msg) {
|
|
373
|
+
Trace.write(msg, NativeScriptDebug.viewUtilCategory);
|
|
374
|
+
}
|
|
375
|
+
static routerLog(message) {
|
|
376
|
+
Trace.write(message, NativeScriptDebug.routerTraceCategory);
|
|
377
|
+
}
|
|
378
|
+
static routerError(message) {
|
|
379
|
+
Trace.write(message, NativeScriptDebug.routerTraceCategory, Trace.messageType.error);
|
|
380
|
+
}
|
|
381
|
+
static routeReuseStrategyLog(message) {
|
|
382
|
+
Trace.write(message, NativeScriptDebug.routeReuseStrategyTraceCategory);
|
|
383
|
+
}
|
|
384
|
+
static styleError(message) {
|
|
385
|
+
Trace.write(message, Trace.categories.Style, Trace.messageType.error);
|
|
386
|
+
}
|
|
387
|
+
static listViewLog(message) {
|
|
388
|
+
Trace.write(message, NativeScriptDebug.listViewTraceCategory);
|
|
389
|
+
}
|
|
390
|
+
static listViewError(message) {
|
|
391
|
+
Trace.write(message, NativeScriptDebug.listViewTraceCategory, Trace.messageType.error);
|
|
392
|
+
}
|
|
393
|
+
static bootstrapLog(message) {
|
|
394
|
+
Trace.write(message, NativeScriptDebug.bootstrapCategory);
|
|
395
|
+
}
|
|
396
|
+
static bootstrapLogError(message) {
|
|
397
|
+
Trace.write(message, NativeScriptDebug.bootstrapCategory, Trace.messageType.error);
|
|
398
|
+
}
|
|
399
|
+
static hmrLog(message) {
|
|
400
|
+
Trace.write(message, NativeScriptDebug.hmrTraceCategory);
|
|
401
|
+
}
|
|
402
|
+
static hmrLogError(message) {
|
|
403
|
+
Trace.write(message, NativeScriptDebug.hmrTraceCategory, Trace.messageType.error);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Fresh-class registry for HMR.
|
|
409
|
+
*
|
|
410
|
+
* After an HMR reboot, every previously imported component module is
|
|
411
|
+
* re-evaluated. Each `@Component()`-decorated class becomes a *new* class
|
|
412
|
+
* object — it shares the source name (e.g. `ResourceModalComponent`) but
|
|
413
|
+
* has a different identity from the class the host code captured before
|
|
414
|
+
* the reboot.
|
|
415
|
+
*
|
|
416
|
+
* Helpers like `NativeDialog._restoreSingleDialog` need to re-open a
|
|
417
|
+
* captured modal *with the new class so the visual update applies*.
|
|
418
|
+
* Holding onto the pre-reboot class reference reopens the modal with
|
|
419
|
+
* the old metadata, which manifests as "the change appears the next
|
|
420
|
+
* time I close and re-open the modal myself, but not when HMR auto-
|
|
421
|
+
* reopens it."
|
|
422
|
+
*
|
|
423
|
+
* The mechanism is:
|
|
424
|
+
*
|
|
425
|
+
* 1. The Vite plugin `ns-component-hmr-register` (in
|
|
426
|
+
* `@nativescript/vite/configuration/angular`) injects a
|
|
427
|
+
* registration call at the end of every user `.ts` file that
|
|
428
|
+
* defines an `@Component`-decorated class:
|
|
429
|
+
*
|
|
430
|
+
* if (typeof globalThis.__NS_HMR_REGISTER_COMPONENT__ === 'function') {
|
|
431
|
+
* try { globalThis.__NS_HMR_REGISTER_COMPONENT__(
|
|
432
|
+
* 'ResourceModalComponent', ResourceModalComponent, import.meta.url
|
|
433
|
+
* ); } catch {}
|
|
434
|
+
* }
|
|
435
|
+
*
|
|
436
|
+
* 2. This module installs `__NS_HMR_REGISTER_COMPONENT__` on
|
|
437
|
+
* `globalThis` so module re-evaluations after an HMR reboot
|
|
438
|
+
* replace the previously-registered class with the fresh one.
|
|
439
|
+
*
|
|
440
|
+
* 3. HMR helpers (modal restore, route replay) read the registry
|
|
441
|
+
* via {@link getFreshComponentClass} to swap in the fresh class.
|
|
442
|
+
*
|
|
443
|
+
* This avoids patching `ɵɵdefineComponent` directly, which is exported
|
|
444
|
+
* as an immutable ESM namespace binding from `@angular/core` — patch
|
|
445
|
+
* attempts silently fail (the assignment is a no-op under strict
|
|
446
|
+
* mode) so the registry never gets populated. With the self-
|
|
447
|
+
* registration approach the binding stays untouched and we don't
|
|
448
|
+
* depend on Angular's internal export shape.
|
|
449
|
+
*
|
|
450
|
+
* Production short-circuit: the registrar is only installed when
|
|
451
|
+
* {@link isAngularHmrEnabled} reports dev + (vite | webpack). In a
|
|
452
|
+
* production build the global hook is never assigned and the Vite
|
|
453
|
+
* plugin only runs in `apply: 'serve'`, so the registration calls
|
|
454
|
+
* never reach the runtime.
|
|
455
|
+
*/
|
|
456
|
+
const REGISTRY_KEY$1 = '__NS_ANGULAR_HMR_CLASS_REGISTRY__';
|
|
457
|
+
const REGISTRY_META_KEY = '__NS_ANGULAR_HMR_CLASS_META__';
|
|
458
|
+
const REGISTRAR_HOOK = '__NS_HMR_REGISTER_COMPONENT__';
|
|
459
|
+
const REGISTRAR_INSTALLED_FLAG = '__NS_ANGULAR_HMR_REGISTRAR_INSTALLED__';
|
|
460
|
+
/**
|
|
461
|
+
* Diagnostic: counters that survive across HMR cycles via globalThis.
|
|
462
|
+
* Used to spot patterns like "the same class registered N times in a
|
|
463
|
+
* single cycle" or "a brand-new class object every cycle".
|
|
464
|
+
*/
|
|
465
|
+
const DIAG_KEY = '__NS_HMR_DIAG__';
|
|
466
|
+
function getDiag() {
|
|
467
|
+
const slot = globalThis;
|
|
468
|
+
if (!slot[DIAG_KEY]) {
|
|
469
|
+
slot[DIAG_KEY] = {
|
|
470
|
+
cycle: 0,
|
|
471
|
+
registerCalls: 0,
|
|
472
|
+
classIdentities: new Map(),
|
|
473
|
+
classRegisterCounts: new Map(),
|
|
474
|
+
classIds: new WeakMap(),
|
|
475
|
+
classIdNext: 1,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
return slot[DIAG_KEY];
|
|
479
|
+
}
|
|
480
|
+
/** Get/assign a short stable id for a class object. */
|
|
481
|
+
function getClassId(diag, cls) {
|
|
482
|
+
let id = diag.classIds.get(cls);
|
|
483
|
+
if (!id) {
|
|
484
|
+
id = `c${diag.classIdNext++}`;
|
|
485
|
+
diag.classIds.set(cls, id);
|
|
486
|
+
}
|
|
487
|
+
return id;
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Class-registry HMR diagnostic.
|
|
491
|
+
*/
|
|
492
|
+
function diagLog(message) {
|
|
493
|
+
if (!isAngularHmrEnabled())
|
|
494
|
+
return;
|
|
495
|
+
if (!NativeScriptDebug.isLogEnabled())
|
|
496
|
+
return;
|
|
497
|
+
NativeScriptDebug.hmrLog(`[class-registry] ${message}`);
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Log helper for "must surface at module-load time" messages — fires
|
|
501
|
+
* for any dev-mode build (not gated on the HMR-globals check) so the
|
|
502
|
+
* one-shot "registrar installed" line doesn't get suppressed by a
|
|
503
|
+
* module-load ordering race.
|
|
504
|
+
*/
|
|
505
|
+
function bootLog(message) {
|
|
506
|
+
if (!isAngularDevMode())
|
|
507
|
+
return;
|
|
508
|
+
if (!NativeScriptDebug.isLogEnabled())
|
|
509
|
+
return;
|
|
510
|
+
NativeScriptDebug.hmrLog(`[class-registry] ${message}`);
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Public so callers from application.ts can bump the cycle counter when
|
|
514
|
+
* a new HMR reboot starts. Kept as a free function (not a class method)
|
|
515
|
+
* to avoid forcing more imports on the application module.
|
|
516
|
+
*
|
|
517
|
+
* Self-heal: by the time we hit cycle bump, dev/HMR globals are
|
|
518
|
+
* definitely set (`__NS_HMR_BOOT_COMPLETE__` was set before the first
|
|
519
|
+
* HMR cycle ever runs). Some module-load orderings end up with
|
|
520
|
+
* `installAngularHmrComponentRegistrar()` called before the early
|
|
521
|
+
* placeholder global was set, so the registrar would have returned
|
|
522
|
+
* early and never installed the hook. Re-attempting here closes that
|
|
523
|
+
* window — `installAngularHmrComponentRegistrar()` is idempotent.
|
|
524
|
+
*/
|
|
525
|
+
function _hmrDiagBumpCycle() {
|
|
526
|
+
const diag = getDiag();
|
|
527
|
+
diag.cycle += 1;
|
|
528
|
+
diagLog(`---- cycle ${diag.cycle} start ----`);
|
|
529
|
+
installAngularHmrComponentRegistrar();
|
|
530
|
+
return diag.cycle;
|
|
531
|
+
}
|
|
532
|
+
/** Public for tests. */
|
|
533
|
+
function _hmrDiagSnapshot() {
|
|
534
|
+
const diag = getDiag();
|
|
535
|
+
return {
|
|
536
|
+
cycle: diag.cycle,
|
|
537
|
+
registerCalls: diag.registerCalls,
|
|
538
|
+
namesSeen: diag.classIdentities.size,
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
function getRegistry() {
|
|
542
|
+
const slot = globalThis;
|
|
543
|
+
let registry = slot[REGISTRY_KEY$1];
|
|
544
|
+
if (!registry) {
|
|
545
|
+
registry = new Map();
|
|
546
|
+
slot[REGISTRY_KEY$1] = registry;
|
|
547
|
+
}
|
|
548
|
+
return registry;
|
|
549
|
+
}
|
|
550
|
+
function getMetaRegistry() {
|
|
551
|
+
const slot = globalThis;
|
|
552
|
+
let registry = slot[REGISTRY_META_KEY];
|
|
553
|
+
if (!registry) {
|
|
554
|
+
registry = new Map();
|
|
555
|
+
slot[REGISTRY_META_KEY] = registry;
|
|
556
|
+
}
|
|
557
|
+
return registry;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Internal: write a class into the registry. Exposed for unit tests
|
|
561
|
+
* (which can call this directly to simulate a Vite-injected
|
|
562
|
+
* registration without spinning up the Vite plugin pipeline).
|
|
563
|
+
*
|
|
564
|
+
* Production callers should never need this — user code just calls
|
|
565
|
+
* the global `__NS_HMR_REGISTER_COMPONENT__` hook installed by
|
|
566
|
+
* {@link installAngularHmrComponentRegistrar}.
|
|
567
|
+
*/
|
|
568
|
+
function _registerComponentForHmr(name, cls, url = '') {
|
|
569
|
+
if (!name || typeof name !== 'string')
|
|
570
|
+
return;
|
|
571
|
+
if (cls === undefined || cls === null)
|
|
572
|
+
return;
|
|
573
|
+
const registry = getRegistry();
|
|
574
|
+
const meta = getMetaRegistry();
|
|
575
|
+
const previous = registry.get(name);
|
|
576
|
+
registry.set(name, cls);
|
|
577
|
+
meta.set(name, { url: url || '', cycle: getDiag().cycle });
|
|
578
|
+
const d = getDiag();
|
|
579
|
+
d.registerCalls += 1;
|
|
580
|
+
if (typeof cls === 'object' || typeof cls === 'function') {
|
|
581
|
+
const classId = getClassId(d, cls);
|
|
582
|
+
let identitySet = d.classIdentities.get(name);
|
|
583
|
+
if (!identitySet) {
|
|
584
|
+
identitySet = new Set();
|
|
585
|
+
d.classIdentities.set(name, identitySet);
|
|
586
|
+
}
|
|
587
|
+
identitySet.add(cls);
|
|
588
|
+
d.classRegisterCounts.set(name, (d.classRegisterCounts.get(name) ?? 0) + 1);
|
|
589
|
+
// Only log a small set of "interesting" components to keep noise
|
|
590
|
+
// manageable. Verbose mode is enabled by setting
|
|
591
|
+
// globalThis.__NS_HMR_DIAG_VERBOSE = true (e.g. from the user
|
|
592
|
+
// app's main.ts) when we want all names.
|
|
593
|
+
const verbose = !!globalThis.__NS_HMR_DIAG_VERBOSE;
|
|
594
|
+
const watchPattern = globalThis.__NS_HMR_DIAG_WATCH;
|
|
595
|
+
const matches = watchPattern instanceof RegExp ? watchPattern.test(name) : typeof watchPattern === 'string' ? name.includes(watchPattern) : /Modal|Dialog/.test(name);
|
|
596
|
+
if (verbose || matches) {
|
|
597
|
+
diagLog(`register name=${name} classId=${classId} sameAsPrev=${previous === cls} cycle=${d.cycle} totalIdentitiesForName=${identitySet.size} registerCountForName=${d.classRegisterCounts.get(name)} url=${url || '(none)'}`);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Install the cross-module `__NS_HMR_REGISTER_COMPONENT__` hook on
|
|
603
|
+
* `globalThis`. The Vite plugin `ns-component-hmr-register` injects
|
|
604
|
+
* a call to this hook at the end of every user `.ts` file that
|
|
605
|
+
* defines an `@Component`-decorated class, so re-evaluations after
|
|
606
|
+
* an HMR reboot keep the registry pointed at the live class.
|
|
607
|
+
*
|
|
608
|
+
* Idempotent: calling twice is a no-op (the second call sees the
|
|
609
|
+
* installed flag and returns).
|
|
610
|
+
*
|
|
611
|
+
* The hook is installed unconditionally — it's a single function
|
|
612
|
+
* reference on globalThis with negligible cost. Production builds
|
|
613
|
+
* never reach this code path because the Vite plugin
|
|
614
|
+
* `ns-component-hmr-register` runs only with `apply: 'serve'`, so
|
|
615
|
+
* the hook is never *called* in production. We previously gated
|
|
616
|
+
* this on `isAngularHmrEnabled()` but that check depends on
|
|
617
|
+
* NativeScript Vite globals (`__NS_DEV_PLACEHOLDER_ROOT_EARLY__` /
|
|
618
|
+
* `__NS_HMR_BOOT_COMPLETE__`) that are set imperatively from
|
|
619
|
+
* `main-entry.ts`. Module-load ordering can put `application.ts`
|
|
620
|
+
* evaluation *before* those globals are set in some startup paths,
|
|
621
|
+
* causing the install to silently no-op while the Vite plugin
|
|
622
|
+
* happily emits registration calls into user `.ts` files. The end
|
|
623
|
+
* result was an empty registry and `getFreshComponentClass` always
|
|
624
|
+
* reporting `reason=no-registry`. Removing the gate eliminates
|
|
625
|
+
* that race entirely.
|
|
626
|
+
*/
|
|
627
|
+
function installAngularHmrComponentRegistrar() {
|
|
628
|
+
const slot = globalThis;
|
|
629
|
+
if (slot[REGISTRAR_INSTALLED_FLAG]) {
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
// Define the hook BEFORE marking installed so concurrent module
|
|
633
|
+
// initializers see the function as soon as the flag is observable.
|
|
634
|
+
slot[REGISTRAR_HOOK] = (name, cls, url) => {
|
|
635
|
+
try {
|
|
636
|
+
_registerComponentForHmr(name, cls, typeof url === 'string' ? url : '');
|
|
637
|
+
}
|
|
638
|
+
catch (err) {
|
|
639
|
+
// Registration is best-effort — never break a user module load
|
|
640
|
+
// because of a registration failure.
|
|
641
|
+
diagLog(`registrar threw for ${name}: ${err?.message ?? err}`);
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
slot[REGISTRAR_INSTALLED_FLAG] = true;
|
|
645
|
+
bootLog('installAngularHmrComponentRegistrar installed global hook __NS_HMR_REGISTER_COMPONENT__');
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Look up the freshest registered component class for a given name, or
|
|
649
|
+
* `undefined` if no match. HMR helpers (e.g. dialog restore) call this
|
|
650
|
+
* with the captured class's `.name` to find the live class after a
|
|
651
|
+
* reboot. Returns `undefined` in production builds because the registrar
|
|
652
|
+
* is never installed there.
|
|
653
|
+
*/
|
|
654
|
+
function getFreshComponentClass(name) {
|
|
655
|
+
if (!name)
|
|
656
|
+
return undefined;
|
|
657
|
+
const slot = globalThis;
|
|
658
|
+
const registry = slot[REGISTRY_KEY$1];
|
|
659
|
+
// Diagnostics: log every lookup with the resolved class id so the
|
|
660
|
+
// restore path can be cross-referenced against register emissions.
|
|
661
|
+
if (registry) {
|
|
662
|
+
const value = registry.get(name);
|
|
663
|
+
const diag = getDiag();
|
|
664
|
+
const classId = value && (typeof value === 'object' || typeof value === 'function') ? getClassId(diag, value) : '(none)';
|
|
665
|
+
const knownNames = registry.size;
|
|
666
|
+
diagLog(`getFreshComponentClass name=${name} found=${!!value} classId=${classId} registrySize=${knownNames}`);
|
|
667
|
+
return value;
|
|
668
|
+
}
|
|
669
|
+
diagLog(`getFreshComponentClass name=${name} found=false reason=no-registry`);
|
|
670
|
+
return undefined;
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Look up the source URL (`import.meta.url`) recorded for a registered
|
|
674
|
+
* component. Used by HMR helpers that need to force a fresh import of
|
|
675
|
+
* a lazily-loaded module (e.g. modals whose static import chain doesn't
|
|
676
|
+
* walk the bootstrap path).
|
|
677
|
+
*
|
|
678
|
+
* Returns `undefined` if the name was never registered or if no URL was
|
|
679
|
+
* provided at registration time.
|
|
680
|
+
*/
|
|
681
|
+
function getRegisteredComponentUrl(name) {
|
|
682
|
+
if (!name)
|
|
683
|
+
return undefined;
|
|
684
|
+
const slot = globalThis;
|
|
685
|
+
const meta = slot[REGISTRY_META_KEY];
|
|
686
|
+
const entry = meta?.get(name);
|
|
687
|
+
return entry?.url || undefined;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Test/debug helper: clear all registered classes. Production callers
|
|
691
|
+
* never need this; the registry stays empty without the registrar.
|
|
692
|
+
*/
|
|
693
|
+
function clearAngularHmrClassRegistry() {
|
|
694
|
+
const slot = globalThis;
|
|
695
|
+
slot[REGISTRY_KEY$1] = undefined;
|
|
696
|
+
slot[REGISTRY_META_KEY] = undefined;
|
|
697
|
+
slot[REGISTRAR_INSTALLED_FLAG] = undefined;
|
|
698
|
+
slot[REGISTRAR_HOOK] = undefined;
|
|
699
|
+
// Also reset diag so test isolation isn't broken by counts leaking.
|
|
700
|
+
const diagSlot = globalThis;
|
|
701
|
+
diagSlot[DIAG_KEY] = undefined;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const REGISTRY_KEY = '__NS_HMR_EAGER_SERVICES__';
|
|
705
|
+
const REGISTER_KEY = '__NS_REGISTER_HMR_EAGER_SERVICE__';
|
|
706
|
+
/**
|
|
707
|
+
* Diagnostic helper.
|
|
708
|
+
*/
|
|
709
|
+
function eagerDiag(message) {
|
|
710
|
+
const g = globalThis;
|
|
711
|
+
const ngDev = (typeof g.ngDevMode === 'boolean') ? g.ngDevMode : true;
|
|
712
|
+
const viteHmr = !!g.__NS_DEV_PLACEHOLDER_ROOT_EARLY__ || !!g.__NS_HMR_BOOT_COMPLETE__;
|
|
713
|
+
if (!(ngDev && viteHmr))
|
|
714
|
+
return;
|
|
715
|
+
if (!NativeScriptDebug.isLogEnabled())
|
|
716
|
+
return;
|
|
717
|
+
NativeScriptDebug.hmrLog(`[eager] ${message}`);
|
|
718
|
+
}
|
|
719
|
+
function getStore() {
|
|
720
|
+
return globalThis;
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Returns the live (mutable) array of registered eager instantiators.
|
|
724
|
+
* Callers must not mutate it directly outside of the helpers in this
|
|
725
|
+
* module — use {@link registerHmrEagerInstantiator} or
|
|
726
|
+
* {@link clearHmrEagerInstantiators} instead.
|
|
727
|
+
*/
|
|
728
|
+
function getRegisteredHmrEagerInstantiators() {
|
|
729
|
+
const store = getStore();
|
|
730
|
+
const list = store[REGISTRY_KEY];
|
|
731
|
+
if (!Array.isArray(list)) {
|
|
732
|
+
const fresh = [];
|
|
733
|
+
store[REGISTRY_KEY] = fresh;
|
|
734
|
+
return fresh;
|
|
735
|
+
}
|
|
736
|
+
return list;
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Idempotently register an instantiator callback. Returns `true` when the
|
|
740
|
+
* callback was added, `false` when it was already present.
|
|
741
|
+
*/
|
|
742
|
+
function registerHmrEagerInstantiator(fn) {
|
|
743
|
+
if (typeof fn !== 'function') {
|
|
744
|
+
return false;
|
|
745
|
+
}
|
|
746
|
+
const list = getRegisteredHmrEagerInstantiators();
|
|
747
|
+
if (list.includes(fn)) {
|
|
748
|
+
eagerDiag(`registerHmrEagerInstantiator dedup (already present) listSize=${list.length}`);
|
|
749
|
+
return false;
|
|
750
|
+
}
|
|
751
|
+
list.push(fn);
|
|
752
|
+
eagerDiag(`registerHmrEagerInstantiator added newSize=${list.length} fnName=${fn.name || '(anon)'}`);
|
|
753
|
+
return true;
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Clear all registered instantiators. Tests use this to reset state
|
|
757
|
+
* between specs; production code should not call it.
|
|
758
|
+
*/
|
|
759
|
+
function clearHmrEagerInstantiators() {
|
|
760
|
+
const list = getRegisteredHmrEagerInstantiators();
|
|
761
|
+
list.length = 0;
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Install the cross-module registration entry point on `globalThis` so
|
|
765
|
+
* consumer modules (e.g. `dialog-services.ts`) can register without
|
|
766
|
+
* statically importing this file. Idempotent across multiple calls so
|
|
767
|
+
* application.ts can call it on every reboot without leaking state.
|
|
768
|
+
*/
|
|
769
|
+
function installHmrEagerRegistrar() {
|
|
770
|
+
const store = getStore();
|
|
771
|
+
if (typeof store[REGISTER_KEY] === 'function') {
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
store[REGISTER_KEY] = (fn) => {
|
|
775
|
+
registerHmrEagerInstantiator(fn);
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Invoke every registered instantiator with the bootstrapped injector.
|
|
780
|
+
* Per-callback exceptions are swallowed; pass `onError` to receive them
|
|
781
|
+
* for logging.
|
|
782
|
+
*/
|
|
783
|
+
function runHmrEagerInstantiators(injector, onError) {
|
|
784
|
+
if (!injector) {
|
|
785
|
+
eagerDiag(`runHmrEagerInstantiators called without injector — no-op`);
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
const list = getRegisteredHmrEagerInstantiators();
|
|
789
|
+
eagerDiag(`runHmrEagerInstantiators list.length=${list.length}`);
|
|
790
|
+
if (list.length === 0) {
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
for (let i = 0; i < list.length; i++) {
|
|
794
|
+
const fn = list[i];
|
|
795
|
+
try {
|
|
796
|
+
eagerDiag(`runHmrEagerInstantiators calling [${i}] ${fn.name || '(anon)'}`);
|
|
797
|
+
fn(injector);
|
|
798
|
+
}
|
|
799
|
+
catch (err) {
|
|
800
|
+
eagerDiag(`runHmrEagerInstantiators [${i}] threw: ${err?.message ?? err}`);
|
|
801
|
+
if (onError) {
|
|
802
|
+
try {
|
|
803
|
+
onError(err);
|
|
804
|
+
}
|
|
805
|
+
catch {
|
|
806
|
+
// The error reporter must not itself break the loop.
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
259
813
|
class NativeScriptLoadingService {
|
|
260
814
|
constructor() {
|
|
261
815
|
this.mainModuleReady$ = new BehaviorSubject(false);
|
|
@@ -356,64 +910,6 @@ function clearAngularHmrRouteConfigCaches(routes) {
|
|
|
356
910
|
return cleared;
|
|
357
911
|
}
|
|
358
912
|
|
|
359
|
-
class NativeScriptDebug {
|
|
360
|
-
static { this.animationsTraceCategory = 'ns-animations'; }
|
|
361
|
-
static { this.rendererTraceCategory = 'ns-renderer'; }
|
|
362
|
-
static { this.viewUtilCategory = 'ns-view-util'; }
|
|
363
|
-
static { this.routerTraceCategory = 'ns-router'; }
|
|
364
|
-
static { this.routeReuseStrategyTraceCategory = 'ns-route-reuse-strategy'; }
|
|
365
|
-
static { this.listViewTraceCategory = 'ns-list-view'; }
|
|
366
|
-
static { this.bootstrapCategory = 'bootstrap'; }
|
|
367
|
-
static { this.hmrTraceCategory = 'ns-ng-hmr'; }
|
|
368
|
-
// TODO: migrate all usage to this - avoids extraneous method executions
|
|
369
|
-
static { this.enabled = Trace.isEnabled(); }
|
|
370
|
-
static isLogEnabled() {
|
|
371
|
-
return Trace.isEnabled();
|
|
372
|
-
}
|
|
373
|
-
static animationsLog(message) {
|
|
374
|
-
Trace.write(message, NativeScriptDebug.animationsTraceCategory);
|
|
375
|
-
}
|
|
376
|
-
static rendererLog(msg) {
|
|
377
|
-
Trace.write(msg, NativeScriptDebug.rendererTraceCategory);
|
|
378
|
-
}
|
|
379
|
-
static rendererError(message) {
|
|
380
|
-
Trace.write(message, NativeScriptDebug.rendererTraceCategory, Trace.messageType.error);
|
|
381
|
-
}
|
|
382
|
-
static viewUtilLog(msg) {
|
|
383
|
-
Trace.write(msg, NativeScriptDebug.viewUtilCategory);
|
|
384
|
-
}
|
|
385
|
-
static routerLog(message) {
|
|
386
|
-
Trace.write(message, NativeScriptDebug.routerTraceCategory);
|
|
387
|
-
}
|
|
388
|
-
static routerError(message) {
|
|
389
|
-
Trace.write(message, NativeScriptDebug.routerTraceCategory, Trace.messageType.error);
|
|
390
|
-
}
|
|
391
|
-
static routeReuseStrategyLog(message) {
|
|
392
|
-
Trace.write(message, NativeScriptDebug.routeReuseStrategyTraceCategory);
|
|
393
|
-
}
|
|
394
|
-
static styleError(message) {
|
|
395
|
-
Trace.write(message, Trace.categories.Style, Trace.messageType.error);
|
|
396
|
-
}
|
|
397
|
-
static listViewLog(message) {
|
|
398
|
-
Trace.write(message, NativeScriptDebug.listViewTraceCategory);
|
|
399
|
-
}
|
|
400
|
-
static listViewError(message) {
|
|
401
|
-
Trace.write(message, NativeScriptDebug.listViewTraceCategory, Trace.messageType.error);
|
|
402
|
-
}
|
|
403
|
-
static bootstrapLog(message) {
|
|
404
|
-
Trace.write(message, NativeScriptDebug.bootstrapCategory);
|
|
405
|
-
}
|
|
406
|
-
static bootstrapLogError(message) {
|
|
407
|
-
Trace.write(message, NativeScriptDebug.bootstrapCategory, Trace.messageType.error);
|
|
408
|
-
}
|
|
409
|
-
static hmrLog(message) {
|
|
410
|
-
Trace.write(message, NativeScriptDebug.hmrTraceCategory);
|
|
411
|
-
}
|
|
412
|
-
static hmrLogError(message) {
|
|
413
|
-
Trace.write(message, NativeScriptDebug.hmrTraceCategory, Trace.messageType.error);
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
|
|
417
913
|
class FrameService {
|
|
418
914
|
// TODO: Add any methods that are needed to handle frame/page navigation
|
|
419
915
|
getFrame() {
|
|
@@ -1457,13 +1953,43 @@ function createAngularRootTransitionGuard(globalObj = globalThis, timers = { set
|
|
|
1457
1953
|
// This is crucial because HMR imports a fresh @angular/core with empty LView tracking
|
|
1458
1954
|
// We need to use the original one that has the registered LViews
|
|
1459
1955
|
rememberAngularCoreForHmr(i0, globalThis);
|
|
1956
|
+
// Install the cross-module HMR component registrar. The Vite plugin
|
|
1957
|
+
// `ns-component-hmr-register` injects a call to the global hook
|
|
1958
|
+
// `__NS_HMR_REGISTER_COMPONENT__` at the end of every user `.ts` file
|
|
1959
|
+
// that declares an `@Component`-decorated class. After an HMR reboot,
|
|
1960
|
+
// each re-evaluated module pushes its fresh class into the registry,
|
|
1961
|
+
// and HMR helpers (modal restore, route replay) read the registry via
|
|
1962
|
+
// `getFreshComponentClass` to re-attach to the *live* class instead of
|
|
1963
|
+
// a captured stale reference. Production short-circuits inside the
|
|
1964
|
+
// helper (the hook is never assigned).
|
|
1965
|
+
//
|
|
1966
|
+
// We install the registrar before any other module initialization can
|
|
1967
|
+
// reference the hook so a user module loaded synchronously alongside
|
|
1968
|
+
// `@nativescript/angular` always finds the function present.
|
|
1969
|
+
installAngularHmrComponentRegistrar();
|
|
1970
|
+
// Install the cross-module registration entry point used by HMR-aware
|
|
1971
|
+
// services (e.g. `NativeDialog`) to ask for eager construction after
|
|
1972
|
+
// every bootstrap. Idempotent: re-evaluations after HMR are no-ops.
|
|
1973
|
+
installHmrEagerRegistrar();
|
|
1460
1974
|
const angularHmrGlobal = globalThis;
|
|
1461
|
-
angularHmrGlobal.__NS_REMEMBER_ANGULAR_CORE__ = (core) =>
|
|
1975
|
+
angularHmrGlobal.__NS_REMEMBER_ANGULAR_CORE__ = (core) => {
|
|
1976
|
+
setAngularCoreForHmr(core, angularHmrGlobal);
|
|
1977
|
+
};
|
|
1462
1978
|
function disableRootViewHanding(view) {
|
|
1463
1979
|
view.__disable_root_view_handling = true;
|
|
1464
1980
|
}
|
|
1465
1981
|
const preAngularDisposal$ = new Subject();
|
|
1466
|
-
|
|
1982
|
+
/**
|
|
1983
|
+
* Stream that emits when an Angular module finishes bootstrapping. Modeled
|
|
1984
|
+
* as a `ReplaySubject(1)` so consumers (e.g. `NativeDialog`) instantiated
|
|
1985
|
+
* lazily — *after* the bootstrap event has already fired — still receive
|
|
1986
|
+
* the latest event and can react. Without buffering, a service that the
|
|
1987
|
+
* user app injects on first need (after bootstrap) would silently miss the
|
|
1988
|
+
* `hotreload` notification and skip HMR-only work like restoring captured
|
|
1989
|
+
* modal state. The buffer size of 1 means each new HMR cycle replaces the
|
|
1990
|
+
* cached event so cycle N's late subscribers don't see cycle N-1's event.
|
|
1991
|
+
*/
|
|
1992
|
+
const postAngularBootstrap$ = new ReplaySubject(1);
|
|
1467
1993
|
/**
|
|
1468
1994
|
* @deprecated
|
|
1469
1995
|
*/
|
|
@@ -1480,14 +2006,34 @@ if (import.meta['webpackHot']) {
|
|
|
1480
2006
|
};
|
|
1481
2007
|
}
|
|
1482
2008
|
function emitModuleBootstrapEvent(ref, name, reason) {
|
|
2009
|
+
if (NativeScriptDebug.isLogEnabled()) {
|
|
2010
|
+
NativeScriptDebug.hmrLog(`emitModuleBootstrapEvent name=${name} reason=${reason}`);
|
|
2011
|
+
}
|
|
2012
|
+
// Instantiate registered HMR-aware services *before* emitting so they
|
|
2013
|
+
// attach their subscriptions in the same JS task and are guaranteed to
|
|
2014
|
+
// observe the event being emitted. `postAngularBootstrap$` is also a
|
|
2015
|
+
// `ReplaySubject(1)`, so a service injected later still receives the
|
|
2016
|
+
// buffered event — the eager pass is the fast path that lets the
|
|
2017
|
+
// restore work begin in the same task as bootstrap completion.
|
|
2018
|
+
if (name === 'main') {
|
|
2019
|
+
runHmrEagerInstantiators(ref.injector, (err) => NativeScriptDebug.bootstrapLogError(`HMR eager instantiator threw: ${err?.message ?? err}`));
|
|
2020
|
+
}
|
|
1483
2021
|
postAngularBootstrap$.next({
|
|
1484
2022
|
moduleType: name,
|
|
1485
2023
|
reference: ref,
|
|
1486
2024
|
reason,
|
|
1487
2025
|
});
|
|
2026
|
+
if (NativeScriptDebug.isLogEnabled()) {
|
|
2027
|
+
NativeScriptDebug.hmrLog(`postAngularBootstrap$.next() emitted name=${name} reason=${reason}`);
|
|
2028
|
+
}
|
|
1488
2029
|
}
|
|
1489
2030
|
function destroyRef(ref, name, reason) {
|
|
1490
2031
|
if (ref) {
|
|
2032
|
+
const refKind = ref instanceof PlatformRef ? 'PlatformRef' : ref instanceof NgModuleRef ? 'NgModuleRef' : ref instanceof ApplicationRef ? 'ApplicationRef' : '(unknown)';
|
|
2033
|
+
const traceEnabled = NativeScriptDebug.isLogEnabled();
|
|
2034
|
+
if (traceEnabled) {
|
|
2035
|
+
NativeScriptDebug.hmrLog(`destroyRef kind=${refKind} name=${name ?? '(none)'} reason=${reason ?? '(none)'}`);
|
|
2036
|
+
}
|
|
1491
2037
|
if (ref instanceof PlatformRef) {
|
|
1492
2038
|
preAngularDisposal$.next({
|
|
1493
2039
|
moduleType: 'platform',
|
|
@@ -1503,6 +2049,9 @@ function destroyRef(ref, name, reason) {
|
|
|
1503
2049
|
});
|
|
1504
2050
|
}
|
|
1505
2051
|
ref.destroy();
|
|
2052
|
+
if (traceEnabled) {
|
|
2053
|
+
NativeScriptDebug.hmrLog(`destroyRef DONE kind=${refKind} name=${name ?? '(none)'}`);
|
|
2054
|
+
}
|
|
1506
2055
|
}
|
|
1507
2056
|
}
|
|
1508
2057
|
function runZoneSyncTask(fn) {
|
|
@@ -1985,9 +2534,14 @@ function runNativeScriptAngularApp(options) {
|
|
|
1985
2534
|
disposePlatform('hotreload');
|
|
1986
2535
|
};
|
|
1987
2536
|
global['__reboot_ng_modules__'] = (shouldDisposePlatform = false) => {
|
|
2537
|
+
// Bump the global HMR cycle counter so subsequent diagnostic log
|
|
2538
|
+
// lines (class registry, dialog services) can be cross-referenced
|
|
2539
|
+
// to a specific reboot. Counter is always incremented; the trace
|
|
2540
|
+
// category gates whether we surface it in the console.
|
|
2541
|
+
const cycleNum = _hmrDiagBumpCycle();
|
|
1988
2542
|
const traceEnabled = NativeScriptDebug.isLogEnabled();
|
|
1989
2543
|
if (traceEnabled) {
|
|
1990
|
-
NativeScriptDebug.hmrLog(`__reboot_ng_modules__ called shouldDisposePlatform=${shouldDisposePlatform} bootstrapId=${bootstrapId} hasMainModuleRef=${!!mainModuleRef}`);
|
|
2544
|
+
NativeScriptDebug.hmrLog(`__reboot_ng_modules__ called cycle=${cycleNum} shouldDisposePlatform=${shouldDisposePlatform} bootstrapId=${bootstrapId} hasMainModuleRef=${!!mainModuleRef}`);
|
|
1991
2545
|
}
|
|
1992
2546
|
try {
|
|
1993
2547
|
global['__NS_CAPTURE_ANGULAR_HMR_ROUTE__']?.();
|
|
@@ -1995,17 +2549,17 @@ function runNativeScriptAngularApp(options) {
|
|
|
1995
2549
|
catch { }
|
|
1996
2550
|
disposeLastModules('hotreload');
|
|
1997
2551
|
if (traceEnabled) {
|
|
1998
|
-
NativeScriptDebug.hmrLog(`after disposeLastModules bootstrapId=${bootstrapId}`);
|
|
2552
|
+
NativeScriptDebug.hmrLog(`after disposeLastModules cycle=${cycleNum} bootstrapId=${bootstrapId}`);
|
|
1999
2553
|
}
|
|
2000
2554
|
if (shouldDisposePlatform) {
|
|
2001
2555
|
disposePlatform('hotreload');
|
|
2002
2556
|
}
|
|
2003
2557
|
if (traceEnabled) {
|
|
2004
|
-
NativeScriptDebug.hmrLog(
|
|
2558
|
+
NativeScriptDebug.hmrLog(`calling bootstrapRoot cycle=${cycleNum}`);
|
|
2005
2559
|
}
|
|
2006
2560
|
bootstrapRoot('hotreload');
|
|
2007
2561
|
if (traceEnabled) {
|
|
2008
|
-
NativeScriptDebug.hmrLog(`bootstrapRoot returned bootstrapId=${bootstrapId}`);
|
|
2562
|
+
NativeScriptDebug.hmrLog(`bootstrapRoot returned cycle=${cycleNum} bootstrapId=${bootstrapId}`);
|
|
2009
2563
|
}
|
|
2010
2564
|
};
|
|
2011
2565
|
if (isWebpackHot) {
|
|
@@ -5530,6 +6084,22 @@ class NativeDialogConfig {
|
|
|
5530
6084
|
*/
|
|
5531
6085
|
this.closeOnNavigation = true;
|
|
5532
6086
|
this.nativeOptions = {};
|
|
6087
|
+
/**
|
|
6088
|
+
* When true, this dialog will be re-opened automatically on Angular HMR
|
|
6089
|
+
* reboots so the user does not lose context every time a related file
|
|
6090
|
+
* changes. The new dialog reuses the same component class and `data` payload
|
|
6091
|
+
* (provided via `data`); other config such as `nativeOptions` is preserved
|
|
6092
|
+
* verbatim.
|
|
6093
|
+
*
|
|
6094
|
+
* The original `dialogRef.afterClosed()` subject is wired to the restored
|
|
6095
|
+
* dialog so consumers `await openModal(...)` resolve normally when the user
|
|
6096
|
+
* eventually closes the restored modal.
|
|
6097
|
+
*
|
|
6098
|
+
* Only opens via component class are restorable — `TemplateRef` openings
|
|
6099
|
+
* carry references that don't survive an HMR reboot and are silently
|
|
6100
|
+
* skipped. Has no effect outside of HMR.
|
|
6101
|
+
*/
|
|
6102
|
+
this.preserveOnHmr = false;
|
|
5533
6103
|
// TODO(jelbourn): add configuration for lifecycle hooks, ARIA labelling.
|
|
5534
6104
|
}
|
|
5535
6105
|
}
|
|
@@ -5652,6 +6222,183 @@ class NativeDialogRef {
|
|
|
5652
6222
|
}
|
|
5653
6223
|
}
|
|
5654
6224
|
|
|
6225
|
+
const STASH_KEY = '__NS_ANGULAR_HMR_PENDING_MODALS__';
|
|
6226
|
+
function getStashSlot() {
|
|
6227
|
+
return globalThis;
|
|
6228
|
+
}
|
|
6229
|
+
/**
|
|
6230
|
+
* Pure helper: filter the open dialog list down to entries that opted in via
|
|
6231
|
+
* `preserveOnHmr` and that we actually know how to restore (component-class
|
|
6232
|
+
* openings, not template openings).
|
|
6233
|
+
*/
|
|
6234
|
+
function selectPreservableDialogs(openDialogs) {
|
|
6235
|
+
return openDialogs.filter((dialog) => isPreservable(dialog));
|
|
6236
|
+
}
|
|
6237
|
+
function isPreservable(dialog) {
|
|
6238
|
+
if (!dialog.config?.preserveOnHmr) {
|
|
6239
|
+
return false;
|
|
6240
|
+
}
|
|
6241
|
+
return typeof dialog.componentClass === 'function';
|
|
6242
|
+
}
|
|
6243
|
+
/**
|
|
6244
|
+
* Capture the open dialogs that opted into HMR preservation. Returns the
|
|
6245
|
+
* captured entries so callers can correlate counts in their logs.
|
|
6246
|
+
*/
|
|
6247
|
+
function captureDialogsForHmr(openDialogs) {
|
|
6248
|
+
const preservable = selectPreservableDialogs(openDialogs);
|
|
6249
|
+
if (preservable.length === 0) {
|
|
6250
|
+
clearPendingHmrDialogs();
|
|
6251
|
+
return [];
|
|
6252
|
+
}
|
|
6253
|
+
const captures = preservable.map(({ ref, componentClass, config }) => {
|
|
6254
|
+
const subject = readAfterClosedSubject(ref);
|
|
6255
|
+
// Capture the source name now — the class reference itself becomes
|
|
6256
|
+
// stale after the reboot, but the name is stable across realms and
|
|
6257
|
+
// is what the post-reboot registry is keyed on.
|
|
6258
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
6259
|
+
const componentName = componentClass?.name ?? '';
|
|
6260
|
+
return {
|
|
6261
|
+
// Asserted non-null in `isPreservable`.
|
|
6262
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
6263
|
+
componentClass: componentClass,
|
|
6264
|
+
componentName,
|
|
6265
|
+
config,
|
|
6266
|
+
graftAfterClosed: (value) => {
|
|
6267
|
+
if (!subject) {
|
|
6268
|
+
return;
|
|
6269
|
+
}
|
|
6270
|
+
try {
|
|
6271
|
+
if (!subject.closed) {
|
|
6272
|
+
subject.next(value);
|
|
6273
|
+
subject.complete();
|
|
6274
|
+
}
|
|
6275
|
+
}
|
|
6276
|
+
catch {
|
|
6277
|
+
// Swallow: the subject may have completed during dispose; nothing for us to do.
|
|
6278
|
+
}
|
|
6279
|
+
},
|
|
6280
|
+
};
|
|
6281
|
+
});
|
|
6282
|
+
globalThis[STASH_KEY] = captures;
|
|
6283
|
+
return captures;
|
|
6284
|
+
}
|
|
6285
|
+
/**
|
|
6286
|
+
* Drain the pending captures. The caller (the new `NativeDialog`) is expected
|
|
6287
|
+
* to re-open each entry and graft `afterClosed` back into the original
|
|
6288
|
+
* subject.
|
|
6289
|
+
*/
|
|
6290
|
+
function consumePendingHmrDialogs() {
|
|
6291
|
+
const slot = globalThis[STASH_KEY];
|
|
6292
|
+
if (!Array.isArray(slot)) {
|
|
6293
|
+
return [];
|
|
6294
|
+
}
|
|
6295
|
+
delete globalThis[STASH_KEY];
|
|
6296
|
+
return slot.filter((entry) => !!entry && typeof entry.componentClass === 'function');
|
|
6297
|
+
}
|
|
6298
|
+
/**
|
|
6299
|
+
* Remove the pending captures without restoring. Useful when a reboot happens
|
|
6300
|
+
* for reasons other than module replacement (e.g. platform dispose) and we
|
|
6301
|
+
* don't want stale modal state to leak into the next bootstrap.
|
|
6302
|
+
*/
|
|
6303
|
+
function clearPendingHmrDialogs() {
|
|
6304
|
+
delete globalThis[STASH_KEY];
|
|
6305
|
+
}
|
|
6306
|
+
/**
|
|
6307
|
+
* Test/debug helper: read the current stash without consuming it.
|
|
6308
|
+
*/
|
|
6309
|
+
function peekPendingHmrDialogs() {
|
|
6310
|
+
const slot = globalThis[STASH_KEY];
|
|
6311
|
+
return Array.isArray(slot) ? slot.slice() : [];
|
|
6312
|
+
}
|
|
6313
|
+
/**
|
|
6314
|
+
* Reach into the dialog ref's private `_afterClosed` subject. We touch the
|
|
6315
|
+
* private field intentionally — the ref class lives inside this package and
|
|
6316
|
+
* we want HMR restore to be a feature of the dialog system rather than a
|
|
6317
|
+
* reason to widen its public surface for everyone.
|
|
6318
|
+
*/
|
|
6319
|
+
function readAfterClosedSubject(ref) {
|
|
6320
|
+
const candidate = ref._afterClosed;
|
|
6321
|
+
if (!candidate || typeof candidate !== 'object') {
|
|
6322
|
+
return undefined;
|
|
6323
|
+
}
|
|
6324
|
+
if (typeof candidate.next !== 'function') {
|
|
6325
|
+
return undefined;
|
|
6326
|
+
}
|
|
6327
|
+
return candidate;
|
|
6328
|
+
}
|
|
6329
|
+
/**
|
|
6330
|
+
* Used by the resume-side: if the stash references something that can no
|
|
6331
|
+
* longer be opened (e.g. the component class is dead post-reload), we still
|
|
6332
|
+
* need to release its consumers so awaited promises don't dangle forever.
|
|
6333
|
+
*/
|
|
6334
|
+
function abortCapturedDialog(captured) {
|
|
6335
|
+
try {
|
|
6336
|
+
captured.graftAfterClosed(undefined);
|
|
6337
|
+
}
|
|
6338
|
+
catch {
|
|
6339
|
+
// Best-effort.
|
|
6340
|
+
}
|
|
6341
|
+
}
|
|
6342
|
+
|
|
6343
|
+
/**
|
|
6344
|
+
* Best-effort animation helpers used by the dialog HMR layer to make
|
|
6345
|
+
* the close + reopen round-trip feel like an in-place content refresh.
|
|
6346
|
+
*
|
|
6347
|
+
* They live in a tiny standalone module on purpose:
|
|
6348
|
+
*
|
|
6349
|
+
* - `dialog-services.ts` pulls in `@angular/core`, which Jest cannot
|
|
6350
|
+
* load in our spec runner without an extra ESM transform. By
|
|
6351
|
+
* keeping these helpers free of `@angular/core` we can unit-test
|
|
6352
|
+
* them in isolation (`dialog-hmr-animation.spec.ts`) while
|
|
6353
|
+
* `dialog-services.ts` re-exports them at the public API layer.
|
|
6354
|
+
* - The helpers are inherently best-effort: a missing
|
|
6355
|
+
* `_nativeModalRef`, a frozen `_modalAnimatedOptions` stack, or a
|
|
6356
|
+
* future `NativeDialogConfig` shape change must never break HMR
|
|
6357
|
+
* restore — we just fall back to the original animated behavior.
|
|
6358
|
+
*/
|
|
6359
|
+
/**
|
|
6360
|
+
* Mutate the top of `parentView._modalAnimatedOptions` to `false` for
|
|
6361
|
+
* the given candidate so the imminent native close runs un-animated.
|
|
6362
|
+
*
|
|
6363
|
+
* iOS reads `_modalAnimatedOptions.slice(-1)[0]` when dismissing a
|
|
6364
|
+
* modal (see core `view-common.ts` / `view/index.ios.ts`). The
|
|
6365
|
+
* Angular dialog service only pushes one entry per open call, so the
|
|
6366
|
+
* top entry is the exact flag that controls the dismiss we're about
|
|
6367
|
+
* to trigger as part of the HMR root-view replacement.
|
|
6368
|
+
*/
|
|
6369
|
+
function suppressNativeCloseAnimation(candidate) {
|
|
6370
|
+
if (!candidate.config?.preserveOnHmr) {
|
|
6371
|
+
return;
|
|
6372
|
+
}
|
|
6373
|
+
try {
|
|
6374
|
+
const modalRef = candidate.ref?._nativeModalRef;
|
|
6375
|
+
const parentView = modalRef?.parentView;
|
|
6376
|
+
const stack = parentView?._modalAnimatedOptions;
|
|
6377
|
+
if (Array.isArray(stack) && stack.length > 0) {
|
|
6378
|
+
stack[stack.length - 1] = false;
|
|
6379
|
+
}
|
|
6380
|
+
}
|
|
6381
|
+
catch {
|
|
6382
|
+
// Swallow: a missing `_nativeModalRef` / `_modalAnimatedOptions`
|
|
6383
|
+
// is acceptable — we just lose the no-animation optimisation.
|
|
6384
|
+
}
|
|
6385
|
+
}
|
|
6386
|
+
/**
|
|
6387
|
+
* Build a `NativeDialogConfig` clone of `original` whose
|
|
6388
|
+
* `nativeOptions.animated` is forced to `false`. Used when re-opening
|
|
6389
|
+
* a captured modal so the open animation matches the suppressed
|
|
6390
|
+
* close — together they make the HMR round-trip feel like a content
|
|
6391
|
+
* refresh instead of a close/reopen.
|
|
6392
|
+
*/
|
|
6393
|
+
function buildNonAnimatedRestoreConfig(original) {
|
|
6394
|
+
// Clone via `Object.assign` so consumers holding the original
|
|
6395
|
+
// config (e.g. caching it for re-open) don't see mutations from
|
|
6396
|
+
// the HMR pathway.
|
|
6397
|
+
const cloned = Object.assign(new NativeDialogConfig(), original);
|
|
6398
|
+
cloned.nativeOptions = { ...(original?.nativeOptions || {}), animated: false };
|
|
6399
|
+
return cloned;
|
|
6400
|
+
}
|
|
6401
|
+
|
|
5655
6402
|
let NativeModalRef = class NativeModalRef {
|
|
5656
6403
|
constructor(_config, _injector, location) {
|
|
5657
6404
|
this._config = _config;
|
|
@@ -5786,6 +6533,54 @@ NativeModalRef = __decorate([
|
|
|
5786
6533
|
* Use of this source code is governed by an MIT-style license that can be
|
|
5787
6534
|
* found in the LICENSE file at https://angular.io/license
|
|
5788
6535
|
*/
|
|
6536
|
+
/**
|
|
6537
|
+
* Dialog HMR lifecycle log.
|
|
6538
|
+
*/
|
|
6539
|
+
function hmrDialogLog(message) {
|
|
6540
|
+
if (!isAngularHmrEnabled()) {
|
|
6541
|
+
return;
|
|
6542
|
+
}
|
|
6543
|
+
if (!NativeScriptDebug.isLogEnabled()) {
|
|
6544
|
+
return;
|
|
6545
|
+
}
|
|
6546
|
+
NativeScriptDebug.hmrLog(`[dialog] ${message}`);
|
|
6547
|
+
}
|
|
6548
|
+
/**
|
|
6549
|
+
* Lower-level dialog HMR wiring trace (module-realm count, NativeDialog
|
|
6550
|
+
* instance count, registry hits/misses). Distinct from `hmrDialogLog`
|
|
6551
|
+
* for greppability — both fan into the same Trace category so a single
|
|
6552
|
+
* `Trace.setCategories(NativeScriptDebug.hmrTraceCategory)` toggle
|
|
6553
|
+
* surfaces them all.
|
|
6554
|
+
*/
|
|
6555
|
+
function hmrDialogDiag(message) {
|
|
6556
|
+
if (!isAngularHmrEnabled()) {
|
|
6557
|
+
return;
|
|
6558
|
+
}
|
|
6559
|
+
if (!NativeScriptDebug.isLogEnabled()) {
|
|
6560
|
+
return;
|
|
6561
|
+
}
|
|
6562
|
+
NativeScriptDebug.hmrLog(`[dialog-diag] ${message}`);
|
|
6563
|
+
}
|
|
6564
|
+
/**
|
|
6565
|
+
* Module-evaluation marker. Increments on every fresh evaluation of
|
|
6566
|
+
* `dialog-services.ts`. If we see this number rise on every HMR cycle,
|
|
6567
|
+
* the file is being re-evaluated (good). If it stays flat, the module
|
|
6568
|
+
* is being served from cache (bad — class identities won't change).
|
|
6569
|
+
*/
|
|
6570
|
+
const DIALOG_MODULE_DIAG_KEY = '__NS_HMR_DIAG_DIALOG_MODULE__';
|
|
6571
|
+
function getDialogModuleDiag() {
|
|
6572
|
+
const slot = globalThis;
|
|
6573
|
+
if (!slot[DIALOG_MODULE_DIAG_KEY]) {
|
|
6574
|
+
slot[DIALOG_MODULE_DIAG_KEY] = { evals: 0, instances: 0, lastEvalAt: 0 };
|
|
6575
|
+
}
|
|
6576
|
+
return slot[DIALOG_MODULE_DIAG_KEY];
|
|
6577
|
+
}
|
|
6578
|
+
{
|
|
6579
|
+
const md = getDialogModuleDiag();
|
|
6580
|
+
md.evals += 1;
|
|
6581
|
+
md.lastEvalAt = Date.now();
|
|
6582
|
+
hmrDialogDiag(`module-eval count=${md.evals} (file=dialog-services.ts) timestamp=${md.lastEvalAt}`);
|
|
6583
|
+
}
|
|
5789
6584
|
/** Injection token that can be used to access the data that was passed in to a dialog. */
|
|
5790
6585
|
const NATIVE_DIALOG_DATA = new InjectionToken('NativeDialogData');
|
|
5791
6586
|
/** Injection token that can be used to specify default dialog options. */
|
|
@@ -5799,6 +6594,14 @@ class NativeDialog {
|
|
|
5799
6594
|
this._openDialogsAtThisLevel = [];
|
|
5800
6595
|
this._afterAllClosedAtThisLevel = new Subject();
|
|
5801
6596
|
this._afterOpenedAtThisLevel = new Subject();
|
|
6597
|
+
/**
|
|
6598
|
+
* Maps each open dialog ref back to the `(componentClass, config)` pair it
|
|
6599
|
+
* was opened with so the HMR snapshot can replay the call later. Dialogs
|
|
6600
|
+
* opened with a `TemplateRef` are tracked with `componentClass: undefined`
|
|
6601
|
+
* — the HMR layer skips them automatically.
|
|
6602
|
+
*/
|
|
6603
|
+
this._openDialogMetadata = new WeakMap();
|
|
6604
|
+
this._hmrSubscriptions = [];
|
|
5802
6605
|
// TODO (jelbourn): tighten the typing right-hand side of this expression.
|
|
5803
6606
|
/**
|
|
5804
6607
|
* Stream that emits when all open dialog have finished closing.
|
|
@@ -5816,6 +6619,50 @@ class NativeDialog {
|
|
|
5816
6619
|
this._nativeModalType = NativeModalRef;
|
|
5817
6620
|
this._dialogDataToken = NATIVE_DIALOG_DATA;
|
|
5818
6621
|
this.locationStrategy = inject(NSLocationStrategy);
|
|
6622
|
+
// Bumps a global counter so we can detect duplicate or leaked
|
|
6623
|
+
// `NativeDialog` instances across HMR cycles. Field initialiser
|
|
6624
|
+
// ordering: this MUST run before `_initHmrLifecycle()` below so the
|
|
6625
|
+
// log line in that helper can include the assigned id.
|
|
6626
|
+
this._diagInstanceIdAssign = (() => {
|
|
6627
|
+
const md = getDialogModuleDiag();
|
|
6628
|
+
md.instances += 1;
|
|
6629
|
+
this._diagInstanceId = md.instances;
|
|
6630
|
+
hmrDialogDiag(`NativeDialog ctor instanceId=${md.instances} hasParentDialog=${!!this._parentDialog} moduleEvalCount=${md.evals}`);
|
|
6631
|
+
return null;
|
|
6632
|
+
})();
|
|
6633
|
+
// Initialise after every dependency above so the subscriptions can call
|
|
6634
|
+
// back into `this.open(...)` and `this.openDialogs` safely. The result is
|
|
6635
|
+
// unused — we just want a side-effect at construction time.
|
|
6636
|
+
this._hmrInitMarker = this._initHmrLifecycle();
|
|
6637
|
+
/**
|
|
6638
|
+
* Tracks whether a restore has already been scheduled for this
|
|
6639
|
+
* `NativeDialog` instance's lifetime. We only need to restore once
|
|
6640
|
+
* per HMR cycle — the rxjs `ReplaySubject(1)` for
|
|
6641
|
+
* `postAngularBootstrap$` delivers both the *previous* cycle's
|
|
6642
|
+
* cached event (replay on subscribe) **and** the *current* cycle's
|
|
6643
|
+
* fresh event, and the constructor stash peek can independently
|
|
6644
|
+
* notice pending work. Without this guard each of those triggers
|
|
6645
|
+
* would queue its own `setTimeout` and the logs would show two or
|
|
6646
|
+
* three "scheduling restore" lines per save.
|
|
6647
|
+
*
|
|
6648
|
+
* The guard is per-instance and the stash itself is the source of
|
|
6649
|
+
* truth: `_restorePendingDialogs` calls `consumePendingHmrDialogs()`
|
|
6650
|
+
* which atomically clears the stash, so even if the guard somehow
|
|
6651
|
+
* fired twice, only the first call would do real work.
|
|
6652
|
+
*/
|
|
6653
|
+
this._restoreScheduledForThisInstance = false;
|
|
6654
|
+
/**
|
|
6655
|
+
* Per-cycle guard to keep `_restorePendingDialogs` idempotent if more
|
|
6656
|
+
* than one subscriber fires for the same bootstrap event. The
|
|
6657
|
+
* regression we have seen (`postAngularBootstrap$ → restore` logged
|
|
6658
|
+
* twice in the same hot reload) was caused by the `NativeDialogModule`
|
|
6659
|
+
* also listing `NativeDialog` in its `providers` array. That has been
|
|
6660
|
+
* removed, but we keep this flag as a defensive net so a future stray
|
|
6661
|
+
* subscription does not consume the stash twice. The flag is reset
|
|
6662
|
+
* after the consume so subsequent HMR cycles can run their own
|
|
6663
|
+
* restore.
|
|
6664
|
+
*/
|
|
6665
|
+
this._restoreInFlight = false;
|
|
5819
6666
|
}
|
|
5820
6667
|
/** Keeps track of the currently-open dialogs. */
|
|
5821
6668
|
get openDialogs() {
|
|
@@ -5825,42 +6672,363 @@ class NativeDialog {
|
|
|
5825
6672
|
get afterOpened() {
|
|
5826
6673
|
return this._parentDialog ? this._parentDialog.afterOpened : this._afterOpenedAtThisLevel;
|
|
5827
6674
|
}
|
|
5828
|
-
_getAfterAllClosed() {
|
|
5829
|
-
const parent = this._parentDialog;
|
|
5830
|
-
return parent ? parent._getAfterAllClosed() : this._afterAllClosedAtThisLevel;
|
|
6675
|
+
_getAfterAllClosed() {
|
|
6676
|
+
const parent = this._parentDialog;
|
|
6677
|
+
return parent ? parent._getAfterAllClosed() : this._afterAllClosedAtThisLevel;
|
|
6678
|
+
}
|
|
6679
|
+
open(componentOrTemplateRef, config) {
|
|
6680
|
+
config = _applyConfigDefaults(config, this._defaultOptions || new NativeDialogConfig());
|
|
6681
|
+
if (config.id && this.getDialogById(config.id) && (typeof ngDevMode === 'undefined' || ngDevMode)) {
|
|
6682
|
+
throw Error(`Dialog with id "${config.id}" exists already. The dialog id must be unique.`);
|
|
6683
|
+
}
|
|
6684
|
+
const dialogRef = this._attachDialogContent(componentOrTemplateRef, config);
|
|
6685
|
+
this.openDialogs.push(dialogRef);
|
|
6686
|
+
this._openDialogMetadata.set(dialogRef, {
|
|
6687
|
+
componentClass: componentOrTemplateRef instanceof TemplateRef ? undefined : componentOrTemplateRef,
|
|
6688
|
+
config,
|
|
6689
|
+
});
|
|
6690
|
+
dialogRef.afterClosed().subscribe(() => this._removeOpenDialog(dialogRef));
|
|
6691
|
+
this.afterOpened.next(dialogRef);
|
|
6692
|
+
// Notify the dialog container that the content has been attached.
|
|
6693
|
+
// dialogContainer._initializeWithAttachedContent();
|
|
6694
|
+
return dialogRef;
|
|
6695
|
+
}
|
|
6696
|
+
/**
|
|
6697
|
+
* Closes all of the currently-open dialogs.
|
|
6698
|
+
*/
|
|
6699
|
+
closeAll() {
|
|
6700
|
+
this._closeDialogs(this.openDialogs);
|
|
6701
|
+
}
|
|
6702
|
+
/**
|
|
6703
|
+
* Finds an open dialog by its id.
|
|
6704
|
+
* @param id ID to use when looking up the dialog.
|
|
6705
|
+
*/
|
|
6706
|
+
getDialogById(id) {
|
|
6707
|
+
return this.openDialogs.find((dialog) => dialog.id === id);
|
|
6708
|
+
}
|
|
6709
|
+
ngOnDestroy() {
|
|
6710
|
+
hmrDialogDiag(`NativeDialog ngOnDestroy instanceId=${this._diagInstanceId} openCount=${this._openDialogsAtThisLevel.length} subCount=${this._hmrSubscriptions.length}`);
|
|
6711
|
+
// Only close the dialogs at this level on destroy
|
|
6712
|
+
// since the parent service may still be active.
|
|
6713
|
+
this._closeDialogs(this._openDialogsAtThisLevel);
|
|
6714
|
+
this._afterAllClosedAtThisLevel.complete();
|
|
6715
|
+
this._afterOpenedAtThisLevel.complete();
|
|
6716
|
+
for (const sub of this._hmrSubscriptions) {
|
|
6717
|
+
try {
|
|
6718
|
+
sub.unsubscribe();
|
|
6719
|
+
}
|
|
6720
|
+
catch {
|
|
6721
|
+
// Best-effort: tearing down the dialog service shouldn't prevent the
|
|
6722
|
+
// rest of the module disposal from completing.
|
|
6723
|
+
}
|
|
6724
|
+
}
|
|
6725
|
+
this._hmrSubscriptions = [];
|
|
6726
|
+
}
|
|
6727
|
+
/**
|
|
6728
|
+
* Wires up HMR capture/restore. Only the root-level dialog manages the
|
|
6729
|
+
* stash so a stack of `NativeDialog` instances inside a child injector
|
|
6730
|
+
* doesn't fight for it.
|
|
6731
|
+
*
|
|
6732
|
+
* Production short-circuit: `isAngularHmrEnabled()` returns `false` in
|
|
6733
|
+
* release builds and when no NS Vite / webpack HMR runtime is present,
|
|
6734
|
+
* so the long-lived subscriptions below never attach in shipping apps.
|
|
6735
|
+
*
|
|
6736
|
+
* `postAngularBootstrap$` is a `ReplaySubject(1)` (see `application.ts`)
|
|
6737
|
+
* which means a `NativeDialog` instantiated *after* the bootstrap event
|
|
6738
|
+
* has already fired (typical when the user app injects `NativeDialog`
|
|
6739
|
+
* lazily via a service like `view.service.ts`) still receives the
|
|
6740
|
+
* buffered event and runs the restore path.
|
|
6741
|
+
*/
|
|
6742
|
+
_initHmrLifecycle() {
|
|
6743
|
+
if (this._parentDialog) {
|
|
6744
|
+
hmrDialogDiag(`_initHmrLifecycle skipped (has parent dialog) instanceId=${this._diagInstanceId}`);
|
|
6745
|
+
return null;
|
|
6746
|
+
}
|
|
6747
|
+
if (!isAngularHmrEnabled()) {
|
|
6748
|
+
return null;
|
|
6749
|
+
}
|
|
6750
|
+
hmrDialogDiag(`_initHmrLifecycle wiring up subscriptions instanceId=${this._diagInstanceId} moduleEvalCount=${getDialogModuleDiag().evals}`);
|
|
6751
|
+
const dispose = preAngularDisposal$.subscribe((event) => {
|
|
6752
|
+
if (event.moduleType !== 'main' || event.reason !== 'hotreload') {
|
|
6753
|
+
return;
|
|
6754
|
+
}
|
|
6755
|
+
hmrDialogDiag(`preAngularDisposal$ fired (reason=${event.reason}) instanceId=${this._diagInstanceId}`);
|
|
6756
|
+
this._captureOpenDialogsForHmr();
|
|
6757
|
+
});
|
|
6758
|
+
const bootstrap = postAngularBootstrap$.subscribe((event) => {
|
|
6759
|
+
if (event.moduleType !== 'main' || event.reason !== 'hotreload') {
|
|
6760
|
+
return;
|
|
6761
|
+
}
|
|
6762
|
+
hmrDialogDiag(`postAngularBootstrap$ fired (reason=${event.reason}) instanceId=${this._diagInstanceId}`);
|
|
6763
|
+
this._maybeScheduleRestore(`postAngularBootstrap$ (reason=${event.reason})`);
|
|
6764
|
+
});
|
|
6765
|
+
this._hmrSubscriptions.push(dispose, bootstrap);
|
|
6766
|
+
// Belt-and-suspenders: even though `postAngularBootstrap$` replays
|
|
6767
|
+
// the last event for late subscribers, also peek the global stash
|
|
6768
|
+
// here. This catches the case where `NativeDialog` is instantiated
|
|
6769
|
+
// lazily — *after* `emitModuleBootstrapEvent` has fired and the
|
|
6770
|
+
// ReplaySubject's buffered event no longer matches the current
|
|
6771
|
+
// cycle. The `_maybeScheduleRestore` guard makes this a no-op when
|
|
6772
|
+
// the bootstrap subscriber already queued work.
|
|
6773
|
+
const pendingNow = peekPendingHmrDialogs();
|
|
6774
|
+
hmrDialogDiag(`_initHmrLifecycle stash peek pending=${pendingNow.length} instanceId=${this._diagInstanceId}`);
|
|
6775
|
+
if (pendingNow.length > 0) {
|
|
6776
|
+
this._maybeScheduleRestore(`stash peek on ctor: ${pendingNow.length} pending dialog(s)`);
|
|
6777
|
+
}
|
|
6778
|
+
return null;
|
|
6779
|
+
}
|
|
6780
|
+
/**
|
|
6781
|
+
* Schedule a restore exactly once per `NativeDialog` instance.
|
|
6782
|
+
*
|
|
6783
|
+
* The work is deferred to the next macrotask for two reasons:
|
|
6784
|
+
*
|
|
6785
|
+
* 1. `postAngularBootstrap$.next(...)` is fired from inside
|
|
6786
|
+
* `emitModuleBootstrapEvent`, which itself runs inside the
|
|
6787
|
+
* `bootstrapApplication` callback — so the call stack still
|
|
6788
|
+
* contains Angular's ApplicationRef bootstrap pipeline. Doing
|
|
6789
|
+
* `this.open(...)` synchronously re-entered Angular DI while a
|
|
6790
|
+
* `providedIn: 'root'` factory could still be on the resolution
|
|
6791
|
+
* stack, which surfaced as `NG0200: Circular dependency detected
|
|
6792
|
+
* for NativeDialog`. Yielding to a macrotask lets the bootstrap
|
|
6793
|
+
* stack fully unwind first.
|
|
6794
|
+
* 2. The eventual `parent.showModal(...)` cannot present onto a
|
|
6795
|
+
* view controller whose view is not yet in the iOS window
|
|
6796
|
+
* hierarchy (see `_scheduleRestoreOpenWhenReady` for the second
|
|
6797
|
+
* wait stage); a synchronous attempt would silently no-op
|
|
6798
|
+
* because iOS rejects the present without throwing.
|
|
6799
|
+
*/
|
|
6800
|
+
_maybeScheduleRestore(triggerDescription) {
|
|
6801
|
+
if (this._restoreScheduledForThisInstance) {
|
|
6802
|
+
hmrDialogDiag(`_maybeScheduleRestore SKIP duplicate trigger=${triggerDescription} instanceId=${this._diagInstanceId}`);
|
|
6803
|
+
return;
|
|
6804
|
+
}
|
|
6805
|
+
this._restoreScheduledForThisInstance = true;
|
|
6806
|
+
hmrDialogLog(`scheduling restore (trigger=${triggerDescription}) instanceId=${this._diagInstanceId}`);
|
|
6807
|
+
setTimeout(() => {
|
|
6808
|
+
void this._restorePendingDialogs();
|
|
6809
|
+
}, 0);
|
|
6810
|
+
}
|
|
6811
|
+
_captureOpenDialogsForHmr() {
|
|
6812
|
+
const candidates = this._openDialogsAtThisLevel.map((ref) => {
|
|
6813
|
+
const meta = this._openDialogMetadata.get(ref);
|
|
6814
|
+
return {
|
|
6815
|
+
ref,
|
|
6816
|
+
componentClass: meta?.componentClass,
|
|
6817
|
+
config: meta?.config ?? new NativeDialogConfig(),
|
|
6818
|
+
};
|
|
6819
|
+
});
|
|
6820
|
+
hmrDialogDiag(`_captureOpenDialogsForHmr instanceId=${this._diagInstanceId} candidates=${candidates.length} (${candidates.map((c) => `${c.componentClass?.name ?? '(template)'}|preserveOnHmr=${!!c.config?.preserveOnHmr}`).join(', ')})`);
|
|
6821
|
+
const captured = captureDialogsForHmr(candidates);
|
|
6822
|
+
if (captured.length > 0) {
|
|
6823
|
+
// Suppress the close animation that's about to fire as part of
|
|
6824
|
+
// `_closeAllModalViewsInternal()` during root-view replacement.
|
|
6825
|
+
// Without this the user sees a slide-down + slide-up flicker
|
|
6826
|
+
// wrapping every HMR reboot.
|
|
6827
|
+
for (const candidate of candidates) {
|
|
6828
|
+
suppressNativeCloseAnimation(candidate);
|
|
6829
|
+
}
|
|
6830
|
+
hmrDialogLog(`captured ${captured.length} dialog(s) for HMR restore [${captured.map((c) => c.componentName).join(', ')}]`);
|
|
6831
|
+
}
|
|
6832
|
+
else if (this._openDialogsAtThisLevel.length > 0) {
|
|
6833
|
+
hmrDialogLog(`skipped capture: ${this._openDialogsAtThisLevel.length} open dialog(s) but none preservable`);
|
|
6834
|
+
}
|
|
6835
|
+
if (captured.length > 0 && NativeScriptDebug.isLogEnabled()) {
|
|
6836
|
+
NativeScriptDebug.hmrLog(`captured ${captured.length} dialog(s) for HMR restore`);
|
|
6837
|
+
}
|
|
5831
6838
|
}
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
6839
|
+
async _restorePendingDialogs() {
|
|
6840
|
+
if (this._restoreInFlight) {
|
|
6841
|
+
hmrDialogLog('skipping restore: already in flight');
|
|
6842
|
+
return;
|
|
6843
|
+
}
|
|
6844
|
+
const pending = consumePendingHmrDialogs();
|
|
6845
|
+
if (pending.length === 0) {
|
|
6846
|
+
return;
|
|
6847
|
+
}
|
|
6848
|
+
this._restoreInFlight = true;
|
|
6849
|
+
hmrDialogLog(`restoring ${pending.length} dialog(s) after reboot [${pending.map((c) => c.componentName).join(', ')}]`);
|
|
6850
|
+
if (NativeScriptDebug.isLogEnabled()) {
|
|
6851
|
+
NativeScriptDebug.hmrLog(`restoring ${pending.length} dialog(s) after HMR reboot`);
|
|
6852
|
+
}
|
|
6853
|
+
try {
|
|
6854
|
+
for (const captured of pending) {
|
|
6855
|
+
this._restoreSingleDialog(captured);
|
|
6856
|
+
}
|
|
6857
|
+
}
|
|
6858
|
+
finally {
|
|
6859
|
+
this._restoreInFlight = false;
|
|
5836
6860
|
}
|
|
5837
|
-
const dialogRef = this._attachDialogContent(componentOrTemplateRef, config);
|
|
5838
|
-
this.openDialogs.push(dialogRef);
|
|
5839
|
-
dialogRef.afterClosed().subscribe(() => this._removeOpenDialog(dialogRef));
|
|
5840
|
-
this.afterOpened.next(dialogRef);
|
|
5841
|
-
// Notify the dialog container that the content has been attached.
|
|
5842
|
-
// dialogContainer._initializeWithAttachedContent();
|
|
5843
|
-
return dialogRef;
|
|
5844
6861
|
}
|
|
5845
6862
|
/**
|
|
5846
|
-
*
|
|
6863
|
+
* Resolve the freshest known class object for the captured component
|
|
6864
|
+
* name and re-open the dialog through the normal `open` path, but
|
|
6865
|
+
* only once the new root view is actually attached to the iOS window
|
|
6866
|
+
* hierarchy.
|
|
6867
|
+
*
|
|
6868
|
+
* Why a class lookup at all: an HMR reboot calls
|
|
6869
|
+
* `ɵresetCompiledComponents()` which clears each component's `ɵcmp`
|
|
6870
|
+
* field but **leaves the class identity unchanged**. The patched
|
|
6871
|
+
* `ɵɵdefineComponent` then re-registers the same class object under
|
|
6872
|
+
* the same source name when Angular re-renders the component. A
|
|
6873
|
+
* fresh-class lookup therefore returns either the same object the
|
|
6874
|
+
* stash captured (most common) or, on the rare occasion that the
|
|
6875
|
+
* source file's `@Component` decorator was re-evaluated into a brand
|
|
6876
|
+
* new class object (e.g. the user added `@Component(...)` to a new
|
|
6877
|
+
* exported symbol), the live one. Either way, a single check is
|
|
6878
|
+
* enough — the previous retry schedule was a no-op in 100 % of
|
|
6879
|
+
* observed cycles because the captured class IS the live class.
|
|
5847
6880
|
*/
|
|
5848
|
-
|
|
5849
|
-
|
|
6881
|
+
_restoreSingleDialog(captured) {
|
|
6882
|
+
const live = getFreshComponentClass(captured.componentName);
|
|
6883
|
+
const componentClass = live ?? captured.componentClass;
|
|
6884
|
+
const usingFresh = !!live && live !== captured.componentClass;
|
|
6885
|
+
// Detailed diagnostic to disambiguate the three reasons
|
|
6886
|
+
// `usingFreshClass=false` could log:
|
|
6887
|
+
// 1. liveDefined=false: the patched ɵɵdefineComponent never
|
|
6888
|
+
// registered a class for this name in the new realm. Most
|
|
6889
|
+
// likely cause: the component module was not re-evaluated
|
|
6890
|
+
// after the eviction (file not in `evictPaths`, or runtime
|
|
6891
|
+
// did not actually evict it).
|
|
6892
|
+
// 2. liveDefined=true but live === captured: the file WAS
|
|
6893
|
+
// re-evaluated, but the registry still hands back the same
|
|
6894
|
+
// class object. This shouldn't happen post-reboot for a
|
|
6895
|
+
// truly fresh realm; if it does, the captured snapshot was
|
|
6896
|
+
// taken from the same realm that's now serving the live
|
|
6897
|
+
// class — i.e. the disposal path is not actually disposing
|
|
6898
|
+
// the old realm before the new one boots.
|
|
6899
|
+
// 3. liveDefined=true and live !== captured: usingFresh path,
|
|
6900
|
+
// the registry IS doing its job. This is the success case.
|
|
6901
|
+
const liveDefined = !!live;
|
|
6902
|
+
const sameAsCapture = liveDefined && live === captured.componentClass;
|
|
6903
|
+
hmrDialogDiag(`_restoreSingleDialog name=${captured.componentName} liveDefined=${liveDefined} sameAsCapture=${sameAsCapture} usingFresh=${usingFresh} capturedFnName=${captured.componentClass?.name ?? '(none)'} liveFnName=${live?.name ?? '(none)'}`);
|
|
6904
|
+
this._scheduleRestoreOpenWhenReady(captured, componentClass, usingFresh);
|
|
5850
6905
|
}
|
|
5851
6906
|
/**
|
|
5852
|
-
*
|
|
5853
|
-
*
|
|
6907
|
+
* Maximum time we'll wait for the new root view to attach to the
|
|
6908
|
+
* iOS window before giving up and trying the open anyway. NS Vite's
|
|
6909
|
+
* worst observed reboot-to-rootview-loaded gap is ~250 ms; one
|
|
6910
|
+
* second leaves headroom for slower devices without leaving the
|
|
6911
|
+
* captured dialog stuck in the stash if something genuinely goes
|
|
6912
|
+
* wrong.
|
|
5854
6913
|
*/
|
|
5855
|
-
|
|
5856
|
-
|
|
6914
|
+
static { this._ROOT_VIEW_LOADED_TIMEOUT_MS = 1_000; }
|
|
6915
|
+
/**
|
|
6916
|
+
* Defer the actual `this.open(...)` call until the new root view's
|
|
6917
|
+
* underlying iOS `UIViewController.view` is in the window hierarchy.
|
|
6918
|
+
*
|
|
6919
|
+
* iOS silently rejects `presentViewControllerAnimatedCompletion` if
|
|
6920
|
+
* the parent controller's view is not yet attached to a `UIWindow`
|
|
6921
|
+
* (see `view/index.ios.ts::_showNativeModalView`, which logs to
|
|
6922
|
+
* `Trace` and returns without throwing). In dev that would surface
|
|
6923
|
+
* as a vanished modal with no actionable error.
|
|
6924
|
+
*
|
|
6925
|
+
* NS marks a view as `isLoaded === true` from
|
|
6926
|
+
* `UILayoutViewController.viewWillAppear`, which fires once iOS has
|
|
6927
|
+
* decided to add the view to its window. Listening for the
|
|
6928
|
+
* `loadedEvent` (one-shot) plus an extra macrotask gives UIKit a
|
|
6929
|
+
* chance to finish window attachment before we present.
|
|
6930
|
+
*/
|
|
6931
|
+
_scheduleRestoreOpenWhenReady(captured, componentClass, usingFresh) {
|
|
6932
|
+
const rootView = Application.getRootView();
|
|
6933
|
+
if (rootView && rootView.isLoaded) {
|
|
6934
|
+
// Even when isLoaded is already true we yield once: a previous
|
|
6935
|
+
// capture in the same hot reload cycle may have set isLoaded on
|
|
6936
|
+
// the *outgoing* root view and a new root view is about to take
|
|
6937
|
+
// over. A single setTimeout(0) gives `setWindowContent` a chance
|
|
6938
|
+
// to finish swapping `win.rootViewController` before we try to
|
|
6939
|
+
// present on top of it.
|
|
6940
|
+
setTimeout(() => this._performRestoreOpen(captured, componentClass, usingFresh), 0);
|
|
6941
|
+
return;
|
|
6942
|
+
}
|
|
6943
|
+
if (!rootView) {
|
|
6944
|
+
// No root view at all yet — extremely unusual at this point in
|
|
6945
|
+
// the bootstrap, but protect against it by polling. We use the
|
|
6946
|
+
// same timeout budget as the loaded path.
|
|
6947
|
+
this._pollForRootView(captured, componentClass, usingFresh, Date.now());
|
|
6948
|
+
return;
|
|
6949
|
+
}
|
|
6950
|
+
hmrDialogLog(`restore ${captured.componentName} waiting for root view loadedEvent`);
|
|
6951
|
+
let settled = false;
|
|
6952
|
+
const onLoaded = () => {
|
|
6953
|
+
if (settled)
|
|
6954
|
+
return;
|
|
6955
|
+
settled = true;
|
|
6956
|
+
try {
|
|
6957
|
+
rootView.off(View.loadedEvent, onLoaded);
|
|
6958
|
+
}
|
|
6959
|
+
catch {
|
|
6960
|
+
// off may throw on stale view bindings; the `settled` flag
|
|
6961
|
+
// already prevents a double-fire.
|
|
6962
|
+
}
|
|
6963
|
+
// Defer one tick after viewWillAppear so UIKit completes the
|
|
6964
|
+
// actual window attachment (`view.window` is set during the
|
|
6965
|
+
// view-controller transition that follows viewWillAppear).
|
|
6966
|
+
setTimeout(() => this._performRestoreOpen(captured, componentClass, usingFresh), 0);
|
|
6967
|
+
};
|
|
6968
|
+
try {
|
|
6969
|
+
rootView.once(View.loadedEvent, onLoaded);
|
|
6970
|
+
}
|
|
6971
|
+
catch {
|
|
6972
|
+
// If the event subscription fails (ancient core builds), fall
|
|
6973
|
+
// back to a tiny delay — better to attempt the open and have
|
|
6974
|
+
// iOS log a benign trace than to leak the dialog stash.
|
|
6975
|
+
setTimeout(() => onLoaded(), 50);
|
|
6976
|
+
}
|
|
6977
|
+
// Bound the wait so a never-loading root view can't permanently
|
|
6978
|
+
// pin the captured dialog in the stash.
|
|
6979
|
+
setTimeout(() => {
|
|
6980
|
+
if (settled)
|
|
6981
|
+
return;
|
|
6982
|
+
hmrDialogLog(`restore ${captured.componentName} root view never loaded within ${NativeDialog._ROOT_VIEW_LOADED_TIMEOUT_MS}ms; attempting open anyway`);
|
|
6983
|
+
onLoaded();
|
|
6984
|
+
}, NativeDialog._ROOT_VIEW_LOADED_TIMEOUT_MS);
|
|
6985
|
+
}
|
|
6986
|
+
_pollForRootView(captured, componentClass, usingFresh, startedAt) {
|
|
6987
|
+
const rootView = Application.getRootView();
|
|
6988
|
+
if (rootView) {
|
|
6989
|
+
this._scheduleRestoreOpenWhenReady(captured, componentClass, usingFresh);
|
|
6990
|
+
return;
|
|
6991
|
+
}
|
|
6992
|
+
if (Date.now() - startedAt > NativeDialog._ROOT_VIEW_LOADED_TIMEOUT_MS) {
|
|
6993
|
+
hmrDialogLog(`restore ${captured.componentName} aborted: no root view after ${NativeDialog._ROOT_VIEW_LOADED_TIMEOUT_MS}ms`);
|
|
6994
|
+
abortCapturedDialog(captured);
|
|
6995
|
+
return;
|
|
6996
|
+
}
|
|
6997
|
+
setTimeout(() => this._pollForRootView(captured, componentClass, usingFresh, startedAt), 16);
|
|
5857
6998
|
}
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
6999
|
+
_performRestoreOpen(captured, componentClass, usingFresh) {
|
|
7000
|
+
hmrDialogLog(`restore ${captured.componentName} usingFreshClass=${usingFresh}`);
|
|
7001
|
+
if (NativeScriptDebug.isLogEnabled() && usingFresh) {
|
|
7002
|
+
NativeScriptDebug.hmrLog(`HMR modal restore using fresh class for ${captured.componentName}`);
|
|
7003
|
+
}
|
|
7004
|
+
// Force the restored modal to open without animation so the round-
|
|
7005
|
+
// trip looks like an instant content refresh rather than a full
|
|
7006
|
+
// close-and-reopen sequence.
|
|
7007
|
+
const restoreConfig = buildNonAnimatedRestoreConfig(captured.config);
|
|
7008
|
+
try {
|
|
7009
|
+
const newRef = this.open(componentClass, restoreConfig);
|
|
7010
|
+
hmrDialogLog(`restore ${captured.componentName} → opened newRef.id=${newRef?.id ?? 'n/a'}`);
|
|
7011
|
+
newRef.afterClosed().subscribe({
|
|
7012
|
+
next: (value) => captured.graftAfterClosed(value),
|
|
7013
|
+
complete: () => captured.graftAfterClosed(undefined),
|
|
7014
|
+
});
|
|
7015
|
+
}
|
|
7016
|
+
catch (err) {
|
|
7017
|
+
abortCapturedDialog(captured);
|
|
7018
|
+
const message = err?.message ?? String(err);
|
|
7019
|
+
hmrDialogLog(`restore ${captured.componentName} FAILED: ${message}`);
|
|
7020
|
+
if (NativeScriptDebug.isLogEnabled()) {
|
|
7021
|
+
NativeScriptDebug.hmrLogError(`HMR modal restore failed: ${message}`);
|
|
7022
|
+
}
|
|
7023
|
+
}
|
|
7024
|
+
}
|
|
7025
|
+
/**
|
|
7026
|
+
* Test/debug helper: discard any captured modals without restoring them.
|
|
7027
|
+
* Mostly useful when projects want to opt out of restoration without
|
|
7028
|
+
* recompiling. Public callers should prefer `preserveOnHmr: false` instead.
|
|
7029
|
+
*/
|
|
7030
|
+
static _clearPendingHmrDialogs() {
|
|
7031
|
+
clearPendingHmrDialogs();
|
|
5864
7032
|
}
|
|
5865
7033
|
/**
|
|
5866
7034
|
* Attaches the user-provided component to the already-created dialog container.
|
|
@@ -5925,6 +7093,7 @@ class NativeDialog {
|
|
|
5925
7093
|
const index = this.openDialogs.indexOf(dialogRef);
|
|
5926
7094
|
if (index > -1) {
|
|
5927
7095
|
this.openDialogs.splice(index, 1);
|
|
7096
|
+
this._openDialogMetadata.delete(dialogRef);
|
|
5928
7097
|
// If all the dialogs were closed, remove/restore the `aria-hidden`
|
|
5929
7098
|
// to a the siblings and emit to the `afterAllClosed` stream.
|
|
5930
7099
|
if (!this.openDialogs.length) {
|
|
@@ -5961,6 +7130,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImpor
|
|
|
5961
7130
|
function _applyConfigDefaults(config, defaultOptions) {
|
|
5962
7131
|
return { ...defaultOptions, ...config };
|
|
5963
7132
|
}
|
|
7133
|
+
/**
|
|
7134
|
+
* Register `NativeDialog` with the application's HMR eager-instantiate
|
|
7135
|
+
* registry so the post-bootstrap pipeline forces an `injector.get()` on
|
|
7136
|
+
* the service in the same JS task as the bootstrap event. Without this,
|
|
7137
|
+
* `NativeDialog` is only constructed when something in user-app code
|
|
7138
|
+
* injects it (typically a wrapper service that opens modals). When that
|
|
7139
|
+
* injection happens lazily — on first user-driven modal open — captured
|
|
7140
|
+
* dialogs from a prior HMR cycle wait until the user reopens *something*
|
|
7141
|
+
* before the new realm even sees the stash. Eager instantiation makes
|
|
7142
|
+
* the restore work happen as early as possible during a hot reload while
|
|
7143
|
+
* staying gated to dev mode + HMR-active environments.
|
|
7144
|
+
*
|
|
7145
|
+
* Registered after the class declaration so the closure references the
|
|
7146
|
+
* fully-defined class rather than tripping the temporal dead zone.
|
|
7147
|
+
*
|
|
7148
|
+
* Idempotent: the registry de-dupes function references, so multiple
|
|
7149
|
+
* evaluations of this module across HMR cycles never accumulate stale
|
|
7150
|
+
* registrations.
|
|
7151
|
+
*/
|
|
7152
|
+
if (isAngularHmrEnabled()) {
|
|
7153
|
+
const added = registerHmrEagerInstantiator((injector) => {
|
|
7154
|
+
try {
|
|
7155
|
+
const inst = injector.get(NativeDialog, null);
|
|
7156
|
+
hmrDialogDiag(`eager-instantiator fired NativeDialog=${inst ? `instance#${inst._diagInstanceId}` : 'null'}`);
|
|
7157
|
+
}
|
|
7158
|
+
catch (err) {
|
|
7159
|
+
hmrDialogDiag(`eager-instantiator threw: ${err?.message ?? err}`);
|
|
7160
|
+
// Some user-app providers may not include `NativeDialog` (e.g. a
|
|
7161
|
+
// module that doesn't depend on the dialog feature). The registry
|
|
7162
|
+
// contract is "best effort": failing to find the token must be a
|
|
7163
|
+
// silent no-op so unrelated apps aren't penalized.
|
|
7164
|
+
}
|
|
7165
|
+
});
|
|
7166
|
+
hmrDialogDiag(`registerHmrEagerInstantiator added=${added} (false means already present in registry)`);
|
|
7167
|
+
}
|
|
5964
7168
|
|
|
5965
7169
|
/**
|
|
5966
7170
|
* @license
|
|
@@ -6036,17 +7240,42 @@ function getClosestDialog(element, openDialogs) {
|
|
|
6036
7240
|
return view ? openDialogs.find((dialog) => dialog.id === view['__ng_modal_id__']) : null;
|
|
6037
7241
|
}
|
|
6038
7242
|
|
|
7243
|
+
/**
|
|
7244
|
+
* Convenience module that re-exports the `NativeDialogCloseDirective` for
|
|
7245
|
+
* template-driven `[nativeDialogClose]` usage.
|
|
7246
|
+
*
|
|
7247
|
+
* **Important**: `NativeDialog` itself is **not** listed in this module's
|
|
7248
|
+
* `providers` array. The service is `@Injectable({ providedIn: 'root' })`,
|
|
7249
|
+
* which already registers a single root-level instance and is fully
|
|
7250
|
+
* tree-shakeable. Listing it here as well caused Angular to treat the
|
|
7251
|
+
* module-level provider and the `providedIn: 'root'` factory as
|
|
7252
|
+
* *separate* registrations once the module was pulled into a standalone
|
|
7253
|
+
* app via `importProvidersFrom(NativeDialogModule, ...)`. The duplicate
|
|
7254
|
+
* registration triggered:
|
|
7255
|
+
*
|
|
7256
|
+
* - Two `NativeDialog` instances in the same root environment injector,
|
|
7257
|
+
* each subscribing to `postAngularBootstrap$`, which produced
|
|
7258
|
+
* duplicate restore attempts during HMR.
|
|
7259
|
+
* - `NG0200: Circular dependency detected for NativeDialog` while a
|
|
7260
|
+
* captured modal was being re-opened during HMR restore, because the
|
|
7261
|
+
* second resolution started while the first was still in progress.
|
|
7262
|
+
*
|
|
7263
|
+
* Removing the redundant entry collapses both providers back into a
|
|
7264
|
+
* single root-level instance, which is what `providedIn: 'root'`
|
|
7265
|
+
* documents. App authors who explicitly wire `NativeDialog` themselves
|
|
7266
|
+
* (e.g. in a feature module's `providers`) keep working unchanged
|
|
7267
|
+
* because they're targeting the same class symbol.
|
|
7268
|
+
*/
|
|
6039
7269
|
class NativeDialogModule {
|
|
6040
7270
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: NativeDialogModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
6041
7271
|
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.0.9", ngImport: i0, type: NativeDialogModule, imports: [NativeDialogCloseDirective], exports: [NativeDialogCloseDirective] }); }
|
|
6042
|
-
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: NativeDialogModule
|
|
7272
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: NativeDialogModule }); }
|
|
6043
7273
|
}
|
|
6044
7274
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: NativeDialogModule, decorators: [{
|
|
6045
7275
|
type: NgModule,
|
|
6046
7276
|
args: [{
|
|
6047
7277
|
imports: [NativeDialogCloseDirective],
|
|
6048
7278
|
exports: [NativeDialogCloseDirective],
|
|
6049
|
-
providers: [NativeDialog],
|
|
6050
7279
|
}]
|
|
6051
7280
|
}] });
|
|
6052
7281
|
|
|
@@ -7925,9 +9154,37 @@ function cloneRoutesForBootstrap(routes) {
|
|
|
7925
9154
|
const CURRENT_ROUTE_KEY = '__NS_ANGULAR_HMR_CURRENT_ROUTE__';
|
|
7926
9155
|
const PENDING_START_PATH_KEY = '__NS_ANGULAR_HMR_PENDING_START_PATH__';
|
|
7927
9156
|
const CAPTURE_ROUTE_KEY = '__NS_CAPTURE_ANGULAR_HMR_ROUTE__';
|
|
9157
|
+
// Stack of normalized URLs that mirrors Angular Router's back-stack while the
|
|
9158
|
+
// app is running, and is snapshotted into `PENDING_HISTORY_KEY` when an HMR
|
|
9159
|
+
// reboot is about to fire. After the new module bootstraps, the router replay
|
|
9160
|
+
// hook walks the stack to rebuild the back-stack so users keep their back
|
|
9161
|
+
// navigation across HMR cycles.
|
|
9162
|
+
const HISTORY_KEY = '__NS_ANGULAR_HMR_ROUTE_HISTORY__';
|
|
9163
|
+
const PENDING_HISTORY_KEY = '__NS_ANGULAR_HMR_PENDING_HISTORY__';
|
|
9164
|
+
// Window flag set while the new bootstrap is mid-replay of a captured route
|
|
9165
|
+
// stack. User-app code can consult this to skip default navigations that
|
|
9166
|
+
// would otherwise stomp the route the framework is restoring (e.g. a
|
|
9167
|
+
// bottom-nav component that defaults to its first tab on init when no
|
|
9168
|
+
// signal-backed selection exists).
|
|
9169
|
+
const RESTORING_KEY = '__NS_ANGULAR_HMR_RESTORING_ROUTE__';
|
|
9170
|
+
const RESTORING_TARGET_KEY = '__NS_ANGULAR_HMR_RESTORING_ROUTE_TARGET__';
|
|
7928
9171
|
function getGlobalState() {
|
|
7929
9172
|
return globalThis;
|
|
7930
9173
|
}
|
|
9174
|
+
function readHistoryArray(key) {
|
|
9175
|
+
const g = getGlobalState();
|
|
9176
|
+
const raw = g[key];
|
|
9177
|
+
return Array.isArray(raw) ? raw.filter((entry) => typeof entry === 'string') : [];
|
|
9178
|
+
}
|
|
9179
|
+
function writeHistoryArray(key, history) {
|
|
9180
|
+
const g = getGlobalState();
|
|
9181
|
+
if (history.length > 0) {
|
|
9182
|
+
g[key] = history.slice();
|
|
9183
|
+
}
|
|
9184
|
+
else {
|
|
9185
|
+
delete g[key];
|
|
9186
|
+
}
|
|
9187
|
+
}
|
|
7931
9188
|
function normalizeAngularHmrRouteUrl(value) {
|
|
7932
9189
|
if (typeof value !== 'string') {
|
|
7933
9190
|
return null;
|
|
@@ -7965,8 +9222,31 @@ function captureAngularHmrPendingStartPath(value, source = 'hmr-reboot') {
|
|
|
7965
9222
|
return writeAngularHmrRouteState(value, { pending: true, source });
|
|
7966
9223
|
}
|
|
7967
9224
|
function readAngularHmrPendingStartPath() {
|
|
9225
|
+
// When a back-stack snapshot exists we boot to the bottom of the stack and
|
|
9226
|
+
// let `replayAngularHmrPendingForwardNavigations` walk the rest. Otherwise
|
|
9227
|
+
// fall back to the legacy single-URL slot so projects without history
|
|
9228
|
+
// tracking still land on the page they were viewing.
|
|
9229
|
+
const pendingHistory = readHistoryArray(PENDING_HISTORY_KEY);
|
|
9230
|
+
if (pendingHistory.length > 0) {
|
|
9231
|
+
// Open the restoring-route window so user-app default navigations
|
|
9232
|
+
// can step out of the framework's way until replay completes. The
|
|
9233
|
+
// forward-navigation walk in `NativeScriptAngularHmrRouteReplay`
|
|
9234
|
+
// closes the window after the final URL lands or fails. We pass
|
|
9235
|
+
// the deepest captured URL so consumers can compare against the
|
|
9236
|
+
// active router URL if they want fine-grained suppression.
|
|
9237
|
+
beginAngularHmrRouteRestore(pendingHistory[pendingHistory.length - 1]);
|
|
9238
|
+
return pendingHistory[0];
|
|
9239
|
+
}
|
|
7968
9240
|
const g = getGlobalState();
|
|
7969
|
-
|
|
9241
|
+
const fallback = normalizeAngularHmrRouteUrl(g[PENDING_START_PATH_KEY]?.url ?? g[PENDING_START_PATH_KEY]) || '';
|
|
9242
|
+
if (fallback) {
|
|
9243
|
+
// Single-URL fallback path: user-app code should still suppress
|
|
9244
|
+
// default navigations briefly — the new bootstrap is about to
|
|
9245
|
+
// navigate to `fallback`, so a default tab init that fires first
|
|
9246
|
+
// would still stomp it.
|
|
9247
|
+
beginAngularHmrRouteRestore(fallback);
|
|
9248
|
+
}
|
|
9249
|
+
return fallback;
|
|
7970
9250
|
}
|
|
7971
9251
|
function invokeAngularHmrRouteCapture() {
|
|
7972
9252
|
const g = getGlobalState();
|
|
@@ -7990,20 +9270,402 @@ function installAngularHmrRouteCaptureHook(capture) {
|
|
|
7990
9270
|
}
|
|
7991
9271
|
};
|
|
7992
9272
|
}
|
|
9273
|
+
// ---- back-stack history primitives ------------------------------------------
|
|
9274
|
+
/**
|
|
9275
|
+
* Push a URL onto the live back-stack mirror. The mirror is collapsed when the
|
|
9276
|
+
* incoming URL equals the top — Angular fires multiple `NavigationEnd` events
|
|
9277
|
+
* for the same URL during certain `replaceUrl` scenarios and we don't want to
|
|
9278
|
+
* inflate the stack.
|
|
9279
|
+
*/
|
|
9280
|
+
function pushAngularHmrRouteHistoryEntry(value) {
|
|
9281
|
+
const url = normalizeAngularHmrRouteUrl(value);
|
|
9282
|
+
if (!url) {
|
|
9283
|
+
return null;
|
|
9284
|
+
}
|
|
9285
|
+
const history = readHistoryArray(HISTORY_KEY);
|
|
9286
|
+
if (history.length > 0 && history[history.length - 1] === url) {
|
|
9287
|
+
return url;
|
|
9288
|
+
}
|
|
9289
|
+
history.push(url);
|
|
9290
|
+
writeHistoryArray(HISTORY_KEY, history);
|
|
9291
|
+
return url;
|
|
9292
|
+
}
|
|
9293
|
+
/**
|
|
9294
|
+
* Pop the top of the live back-stack mirror. Used when Angular reports a
|
|
9295
|
+
* `popstate`-triggered navigation so the mirror tracks back navigations.
|
|
9296
|
+
*/
|
|
9297
|
+
function popAngularHmrRouteHistoryEntry() {
|
|
9298
|
+
const history = readHistoryArray(HISTORY_KEY);
|
|
9299
|
+
if (history.length === 0) {
|
|
9300
|
+
return null;
|
|
9301
|
+
}
|
|
9302
|
+
const popped = history.pop() ?? null;
|
|
9303
|
+
writeHistoryArray(HISTORY_KEY, history);
|
|
9304
|
+
return popped;
|
|
9305
|
+
}
|
|
9306
|
+
/**
|
|
9307
|
+
* Replace the top of the live back-stack mirror. Used when Angular reports a
|
|
9308
|
+
* `NavigationEnd` with `replaceUrl=true`, e.g. canonical-redirect cycles.
|
|
9309
|
+
*/
|
|
9310
|
+
function replaceAngularHmrRouteHistoryTop(value) {
|
|
9311
|
+
const url = normalizeAngularHmrRouteUrl(value);
|
|
9312
|
+
if (!url) {
|
|
9313
|
+
return null;
|
|
9314
|
+
}
|
|
9315
|
+
const history = readHistoryArray(HISTORY_KEY);
|
|
9316
|
+
if (history.length === 0) {
|
|
9317
|
+
history.push(url);
|
|
9318
|
+
}
|
|
9319
|
+
else {
|
|
9320
|
+
history[history.length - 1] = url;
|
|
9321
|
+
}
|
|
9322
|
+
writeHistoryArray(HISTORY_KEY, history);
|
|
9323
|
+
return url;
|
|
9324
|
+
}
|
|
9325
|
+
/**
|
|
9326
|
+
* Read a defensive copy of the live back-stack mirror.
|
|
9327
|
+
*/
|
|
9328
|
+
function readAngularHmrRouteHistory() {
|
|
9329
|
+
return readHistoryArray(HISTORY_KEY);
|
|
9330
|
+
}
|
|
9331
|
+
/**
|
|
9332
|
+
* Reset the live back-stack mirror. Used by tests and on bootstrap when the
|
|
9333
|
+
* router cannot replay the captured stack so we don't carry stale entries
|
|
9334
|
+
* forward.
|
|
9335
|
+
*/
|
|
9336
|
+
function clearAngularHmrRouteHistory() {
|
|
9337
|
+
const g = getGlobalState();
|
|
9338
|
+
delete g[HISTORY_KEY];
|
|
9339
|
+
}
|
|
9340
|
+
/**
|
|
9341
|
+
* Snapshot the live back-stack mirror under the pending-history slot so the
|
|
9342
|
+
* next bootstrap can read it. Called from the HMR capture hook.
|
|
9343
|
+
*
|
|
9344
|
+
* The live mirror is cleared after the copy so the freshly bootstrapped app
|
|
9345
|
+
* starts from an empty back-stack. The replay walks the captured snapshot
|
|
9346
|
+
* via `NativeScriptAngularHmrRouteReplay` which fires `NavigationEnd` for
|
|
9347
|
+
* every URL it touches; the new tracker subscribes to those events and
|
|
9348
|
+
* naturally rebuilds the live mirror to match the snapshot. Without this
|
|
9349
|
+
* reset the live mirror would accumulate every URL the replay re-pushes
|
|
9350
|
+
* across HMR cycles, growing without bound and turning subsequent snapshots
|
|
9351
|
+
* into runaway forward-navigation walks (each replayed forward nav from
|
|
9352
|
+
* `/profile` back into `/talk` creates a fresh `TalkComponent` because
|
|
9353
|
+
* forward navigation never reuses the cache, so the leak shows up as
|
|
9354
|
+
* duplicated `Norrix is not enabled` / `BottomNavComponent Router Event:`
|
|
9355
|
+
* lines that double on every save).
|
|
9356
|
+
*
|
|
9357
|
+
* Returns the snapshot for diagnostics. Defensive: an empty live mirror
|
|
9358
|
+
* leaves the pending slot untouched so a single-page snapshot still works.
|
|
9359
|
+
*/
|
|
9360
|
+
function snapshotAngularHmrRouteHistory() {
|
|
9361
|
+
const live = readHistoryArray(HISTORY_KEY);
|
|
9362
|
+
if (live.length === 0) {
|
|
9363
|
+
return [];
|
|
9364
|
+
}
|
|
9365
|
+
writeHistoryArray(PENDING_HISTORY_KEY, live);
|
|
9366
|
+
// Clear the live mirror so the next bootstrap starts from a clean slate.
|
|
9367
|
+
// The replay will repopulate it via the new tracker's NavigationEnd
|
|
9368
|
+
// subscription as it walks the captured stack.
|
|
9369
|
+
writeHistoryArray(HISTORY_KEY, []);
|
|
9370
|
+
return live.slice();
|
|
9371
|
+
}
|
|
9372
|
+
/**
|
|
9373
|
+
* Read the snapshotted back-stack pending replay on the new bootstrap.
|
|
9374
|
+
*/
|
|
9375
|
+
function readAngularHmrPendingRouteHistory() {
|
|
9376
|
+
return readHistoryArray(PENDING_HISTORY_KEY);
|
|
9377
|
+
}
|
|
9378
|
+
/**
|
|
9379
|
+
* Read URLs to navigate forward through after the initial navigation finishes.
|
|
9380
|
+
* The first entry of the stack is the `START_PATH` consumed by the router; the
|
|
9381
|
+
* rest are forward navigations to push onto the new back-stack.
|
|
9382
|
+
*/
|
|
9383
|
+
function readAngularHmrPendingForwardNavigations() {
|
|
9384
|
+
const pending = readHistoryArray(PENDING_HISTORY_KEY);
|
|
9385
|
+
if (pending.length <= 1) {
|
|
9386
|
+
return [];
|
|
9387
|
+
}
|
|
9388
|
+
return pending.slice(1);
|
|
9389
|
+
}
|
|
9390
|
+
/**
|
|
9391
|
+
* Clear the pending snapshot. The router replay calls this once it finishes
|
|
9392
|
+
* walking the stack so subsequent reboots start fresh.
|
|
9393
|
+
*/
|
|
9394
|
+
function clearAngularHmrPendingRouteHistory() {
|
|
9395
|
+
const g = getGlobalState();
|
|
9396
|
+
delete g[PENDING_HISTORY_KEY];
|
|
9397
|
+
}
|
|
9398
|
+
// ---- restoring-route window flag --------------------------------------------
|
|
9399
|
+
/**
|
|
9400
|
+
* True while the Angular HMR layer is restoring a captured route stack
|
|
9401
|
+
* onto the freshly-bootstrapped router. The window opens just before
|
|
9402
|
+
* `START_PATH` resolves to a deep URL and closes once the router has
|
|
9403
|
+
* walked the entire forward navigation list (or aborted it).
|
|
9404
|
+
*
|
|
9405
|
+
* User-app code that runs default navigations on component init (e.g. a
|
|
9406
|
+
* bottom-nav defaulting to its first tab) can consult this flag to skip
|
|
9407
|
+
* its default navigation so the framework's restored route survives:
|
|
9408
|
+
*
|
|
9409
|
+
* ```ts
|
|
9410
|
+
* if (isAngularHmrRestoringRoute()) {
|
|
9411
|
+
* return; // framework is restoring a deeper route — leave it alone.
|
|
9412
|
+
* }
|
|
9413
|
+
* defaultTabNavigation();
|
|
9414
|
+
* ```
|
|
9415
|
+
*
|
|
9416
|
+
* Returns `false` outside of HMR or after the replay window has closed.
|
|
9417
|
+
* Production builds always see `false` because the framework never
|
|
9418
|
+
* opens the window there.
|
|
9419
|
+
*/
|
|
9420
|
+
function isAngularHmrRestoringRoute() {
|
|
9421
|
+
const g = getGlobalState();
|
|
9422
|
+
return g[RESTORING_KEY] === true;
|
|
9423
|
+
}
|
|
9424
|
+
/**
|
|
9425
|
+
* The target route the framework is currently restoring, or `null` when
|
|
9426
|
+
* no replay is in progress. Useful when the consumer wants to compare
|
|
9427
|
+
* against the current router URL.
|
|
9428
|
+
*/
|
|
9429
|
+
function getAngularHmrRestoringRoute() {
|
|
9430
|
+
const g = getGlobalState();
|
|
9431
|
+
const value = g[RESTORING_TARGET_KEY];
|
|
9432
|
+
return typeof value === 'string' && value ? value : null;
|
|
9433
|
+
}
|
|
9434
|
+
/**
|
|
9435
|
+
* Open the restoring-route window. Called by the framework when an HMR
|
|
9436
|
+
* bootstrap is about to navigate to a captured deep route — never call
|
|
9437
|
+
* this from user code.
|
|
9438
|
+
*
|
|
9439
|
+
* `targetUrl` is what the framework intends to land on; the value can
|
|
9440
|
+
* be read back via {@link getAngularHmrRestoringRoute}.
|
|
9441
|
+
*/
|
|
9442
|
+
function beginAngularHmrRouteRestore(targetUrl) {
|
|
9443
|
+
const g = getGlobalState();
|
|
9444
|
+
g[RESTORING_KEY] = true;
|
|
9445
|
+
if (targetUrl) {
|
|
9446
|
+
g[RESTORING_TARGET_KEY] = targetUrl;
|
|
9447
|
+
}
|
|
9448
|
+
else {
|
|
9449
|
+
delete g[RESTORING_TARGET_KEY];
|
|
9450
|
+
}
|
|
9451
|
+
}
|
|
9452
|
+
/**
|
|
9453
|
+
* Close the restoring-route window. Called by the framework when the
|
|
9454
|
+
* replay finishes (NavigationEnd reached, replay aborted, or no
|
|
9455
|
+
* pending stack existed in the first place).
|
|
9456
|
+
*/
|
|
9457
|
+
function endAngularHmrRouteRestore() {
|
|
9458
|
+
const g = getGlobalState();
|
|
9459
|
+
delete g[RESTORING_KEY];
|
|
9460
|
+
delete g[RESTORING_TARGET_KEY];
|
|
9461
|
+
}
|
|
9462
|
+
|
|
9463
|
+
/**
|
|
9464
|
+
* Grace period to keep `isAngularHmrRestoringRoute()` returning `true`
|
|
9465
|
+
* after `replayForwardNavigations()` finishes its last `navigateByUrl`.
|
|
9466
|
+
*
|
|
9467
|
+
* Why a grace period exists: NativeScript native views (TabView, BottomNavigation,
|
|
9468
|
+
* Frame, etc.) fire their `loaded` events asynchronously after the JS-side
|
|
9469
|
+
* `NavigationEnd`. User-app code wired to those events typically guards a
|
|
9470
|
+
* default navigation (e.g. "select first tab") with `isAngularHmrRestoringRoute()`.
|
|
9471
|
+
* If we close the window the instant the JS replay finishes, the loaded
|
|
9472
|
+
* event arrives a few hundred milliseconds later, the guard reports false,
|
|
9473
|
+
* and the default navigation stomps the freshly-restored route.
|
|
9474
|
+
*
|
|
9475
|
+
* 1000ms covers all the cases observed on iOS device + simulator without
|
|
9476
|
+
* leaving the window open long enough to interfere with genuine user
|
|
9477
|
+
* navigation. The fallback timeout (`fallback-timeout`) below is a safety
|
|
9478
|
+
* net for scenarios where this scheduled close never fires.
|
|
9479
|
+
*/
|
|
9480
|
+
const REPLAY_COMPLETED_GRACE_MS = 1000;
|
|
9481
|
+
/**
|
|
9482
|
+
* Replays the back-stack snapshot captured by `NativeScriptAngularHmrRouteTracker`
|
|
9483
|
+
* during HMR. The router's initial navigation already lands on the bottom of
|
|
9484
|
+
* the stack (`stack[0]`); this service walks `stack[1..n]` so the user keeps
|
|
9485
|
+
* back navigation across HMR cycles.
|
|
9486
|
+
*
|
|
9487
|
+
* The replay is single-shot per bootstrap. Any failure (cancelled navigation,
|
|
9488
|
+
* unrouteable URL) aborts the rest of the replay so we don't fight the router
|
|
9489
|
+
* — the user keeps whichever subset of the stack we successfully re-pushed.
|
|
9490
|
+
*/
|
|
9491
|
+
class NativeScriptAngularHmrRouteReplay {
|
|
9492
|
+
constructor(router) {
|
|
9493
|
+
this.router = router;
|
|
9494
|
+
if (!isAngularHmrEnabled()) {
|
|
9495
|
+
return;
|
|
9496
|
+
}
|
|
9497
|
+
const forwardNavigations = readAngularHmrPendingForwardNavigations();
|
|
9498
|
+
// The restoring window is opened by `readAngularHmrPendingStartPath()`
|
|
9499
|
+
// when `START_PATH` resolves to a deep route. If that path resolved
|
|
9500
|
+
// to nothing AND we have no forward navigations, there is nothing
|
|
9501
|
+
// to suppress and we must close the window if it was somehow left
|
|
9502
|
+
// open. Otherwise we keep it open until replay finishes.
|
|
9503
|
+
const restoringWindowOpen = isAngularHmrRestoringRoute();
|
|
9504
|
+
if (forwardNavigations.length === 0) {
|
|
9505
|
+
// Nothing to replay; clear the pending slot so a future navigation that
|
|
9506
|
+
// ends in the bootstrap window doesn't carry the snapshot forward.
|
|
9507
|
+
clearAngularHmrPendingRouteHistory();
|
|
9508
|
+
if (restoringWindowOpen) {
|
|
9509
|
+
// Single-URL restore (no back-stack to walk): keep the window
|
|
9510
|
+
// open until the initial navigation completes so user-app
|
|
9511
|
+
// default navigations don't fire before the framework's
|
|
9512
|
+
// restored URL settles. We then schedule the close with the
|
|
9513
|
+
// same grace period as the multi-URL replay path so async
|
|
9514
|
+
// native `loaded` handlers still see the flag.
|
|
9515
|
+
this.subscription = this.router.events
|
|
9516
|
+
.pipe(filter((event) => event instanceof NavigationEnd || event instanceof NavigationCancel || event instanceof NavigationError), take(1))
|
|
9517
|
+
.subscribe(() => this.scheduleRestoringWindowClose('initial-navigation-settled'));
|
|
9518
|
+
// Belt-and-braces: bootstrap can race with router init in
|
|
9519
|
+
// unusual cases. Close the window after a short timeout so we
|
|
9520
|
+
// never leave it stuck open and silently breaking default
|
|
9521
|
+
// navigations forever.
|
|
9522
|
+
this.windowFallbackTimeout = setTimeout(() => this.closeRestoringWindow('fallback-timeout'), 5000);
|
|
9523
|
+
}
|
|
9524
|
+
return;
|
|
9525
|
+
}
|
|
9526
|
+
if (NativeScriptDebug.isLogEnabled()) {
|
|
9527
|
+
NativeScriptDebug.hmrLog(`HMR back-stack replay queued: ${forwardNavigations.length} forward navigation(s)`);
|
|
9528
|
+
}
|
|
9529
|
+
this.subscription = this.router.events
|
|
9530
|
+
.pipe(filter((event) => event instanceof NavigationEnd || event instanceof NavigationCancel || event instanceof NavigationError), take(1))
|
|
9531
|
+
.subscribe((event) => {
|
|
9532
|
+
if (event instanceof NavigationEnd) {
|
|
9533
|
+
void this.replayForwardNavigations(forwardNavigations);
|
|
9534
|
+
}
|
|
9535
|
+
else {
|
|
9536
|
+
// Initial navigation never landed; replay would compound the problem.
|
|
9537
|
+
clearAngularHmrPendingRouteHistory();
|
|
9538
|
+
this.closeRestoringWindow('initial-navigation-failed');
|
|
9539
|
+
if (NativeScriptDebug.isLogEnabled()) {
|
|
9540
|
+
NativeScriptDebug.hmrLog('HMR back-stack replay skipped: initial navigation did not complete');
|
|
9541
|
+
}
|
|
9542
|
+
}
|
|
9543
|
+
});
|
|
9544
|
+
// Same belt-and-braces fallback as the single-URL path above.
|
|
9545
|
+
this.windowFallbackTimeout = setTimeout(() => this.closeRestoringWindow('fallback-timeout'), 10000);
|
|
9546
|
+
}
|
|
9547
|
+
ngOnDestroy() {
|
|
9548
|
+
this.subscription?.unsubscribe();
|
|
9549
|
+
if (this.windowFallbackTimeout !== undefined) {
|
|
9550
|
+
clearTimeout(this.windowFallbackTimeout);
|
|
9551
|
+
this.windowFallbackTimeout = undefined;
|
|
9552
|
+
}
|
|
9553
|
+
if (this.pendingCloseTimeout !== undefined) {
|
|
9554
|
+
clearTimeout(this.pendingCloseTimeout);
|
|
9555
|
+
this.pendingCloseTimeout = undefined;
|
|
9556
|
+
}
|
|
9557
|
+
// Defensive: never leave the restoring window open across module
|
|
9558
|
+
// destruction. A subsequent reboot would otherwise see it set and
|
|
9559
|
+
// suppress the next default navigation indefinitely.
|
|
9560
|
+
this.closeRestoringWindow('replay-service-destroyed');
|
|
9561
|
+
}
|
|
9562
|
+
closeRestoringWindow(reason) {
|
|
9563
|
+
if (this.pendingCloseTimeout !== undefined) {
|
|
9564
|
+
clearTimeout(this.pendingCloseTimeout);
|
|
9565
|
+
this.pendingCloseTimeout = undefined;
|
|
9566
|
+
}
|
|
9567
|
+
if (!isAngularHmrRestoringRoute()) {
|
|
9568
|
+
return;
|
|
9569
|
+
}
|
|
9570
|
+
endAngularHmrRouteRestore();
|
|
9571
|
+
if (this.windowFallbackTimeout !== undefined) {
|
|
9572
|
+
clearTimeout(this.windowFallbackTimeout);
|
|
9573
|
+
this.windowFallbackTimeout = undefined;
|
|
9574
|
+
}
|
|
9575
|
+
if (NativeScriptDebug.isLogEnabled()) {
|
|
9576
|
+
NativeScriptDebug.hmrLog(`HMR restoring-route window closed (${reason})`);
|
|
9577
|
+
}
|
|
9578
|
+
}
|
|
9579
|
+
/**
|
|
9580
|
+
* Schedule the restoring window to close after a small grace period
|
|
9581
|
+
* so that asynchronous user-app handlers (e.g. NativeScript native
|
|
9582
|
+
* `loaded` events on TabView / BottomNavigation / Frame) still observe
|
|
9583
|
+
* `isAngularHmrRestoringRoute() === true` and skip default navigations
|
|
9584
|
+
* that would otherwise stomp the freshly-restored route.
|
|
9585
|
+
*
|
|
9586
|
+
* The grace period is bounded by the existing `fallback-timeout` so
|
|
9587
|
+
* we never leave the flag set indefinitely even if `setTimeout` is
|
|
9588
|
+
* blocked by a misbehaving consumer.
|
|
9589
|
+
*/
|
|
9590
|
+
scheduleRestoringWindowClose(reason) {
|
|
9591
|
+
if (!isAngularHmrRestoringRoute()) {
|
|
9592
|
+
return;
|
|
9593
|
+
}
|
|
9594
|
+
if (this.pendingCloseTimeout !== undefined) {
|
|
9595
|
+
clearTimeout(this.pendingCloseTimeout);
|
|
9596
|
+
}
|
|
9597
|
+
this.pendingCloseTimeout = setTimeout(() => {
|
|
9598
|
+
this.pendingCloseTimeout = undefined;
|
|
9599
|
+
this.closeRestoringWindow(reason);
|
|
9600
|
+
}, REPLAY_COMPLETED_GRACE_MS);
|
|
9601
|
+
}
|
|
9602
|
+
async replayForwardNavigations(urls) {
|
|
9603
|
+
let aborted = false;
|
|
9604
|
+
try {
|
|
9605
|
+
for (const url of urls) {
|
|
9606
|
+
const succeeded = await this.router.navigateByUrl(url).catch(() => false);
|
|
9607
|
+
if (!succeeded) {
|
|
9608
|
+
aborted = true;
|
|
9609
|
+
if (NativeScriptDebug.isLogEnabled()) {
|
|
9610
|
+
NativeScriptDebug.hmrLog(`HMR back-stack replay aborted at ${url}`);
|
|
9611
|
+
}
|
|
9612
|
+
return;
|
|
9613
|
+
}
|
|
9614
|
+
if (NativeScriptDebug.isLogEnabled()) {
|
|
9615
|
+
NativeScriptDebug.hmrLog(`HMR back-stack replay navigated to ${url}`);
|
|
9616
|
+
}
|
|
9617
|
+
}
|
|
9618
|
+
}
|
|
9619
|
+
finally {
|
|
9620
|
+
clearAngularHmrPendingRouteHistory();
|
|
9621
|
+
this.scheduleRestoringWindowClose(aborted ? 'replay-aborted' : 'replay-completed');
|
|
9622
|
+
}
|
|
9623
|
+
}
|
|
9624
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: NativeScriptAngularHmrRouteReplay, deps: [{ token: i1$3.Router }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
9625
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: NativeScriptAngularHmrRouteReplay }); }
|
|
9626
|
+
}
|
|
9627
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: NativeScriptAngularHmrRouteReplay, decorators: [{
|
|
9628
|
+
type: Injectable
|
|
9629
|
+
}], ctorParameters: () => [{ type: i1$3.Router }] });
|
|
7993
9630
|
|
|
7994
9631
|
class NativeScriptAngularHmrRouteTracker {
|
|
7995
9632
|
constructor(router) {
|
|
7996
9633
|
this.router = router;
|
|
7997
|
-
|
|
9634
|
+
// Tracks whether the current `NavigationStart..NavigationEnd` pair was kicked
|
|
9635
|
+
// off by a popstate (frame.goBack / NSLocationStrategy.back) so that on
|
|
9636
|
+
// `NavigationEnd` we can pop our mirror instead of pushing a duplicate entry.
|
|
9637
|
+
this.currentNavigationIsPopstate = false;
|
|
9638
|
+
this.currentNavigationReplaceUrl = false;
|
|
9639
|
+
if (!isAngularHmrEnabled()) {
|
|
7998
9640
|
return;
|
|
7999
9641
|
}
|
|
8000
9642
|
this.disposeCaptureHook = this.installCaptureHook();
|
|
8001
9643
|
this.captureCurrentRoute('bootstrap');
|
|
8002
9644
|
this.subscription = this.router.events.subscribe((event) => {
|
|
9645
|
+
if (event instanceof NavigationStart) {
|
|
9646
|
+
this.currentNavigationIsPopstate = event.navigationTrigger === 'popstate';
|
|
9647
|
+
this.currentNavigationReplaceUrl = !!event.restoredState;
|
|
9648
|
+
return;
|
|
9649
|
+
}
|
|
8003
9650
|
if (event instanceof NavigationEnd) {
|
|
8004
|
-
|
|
9651
|
+
const url = event.urlAfterRedirects || event.url;
|
|
9652
|
+
writeAngularHmrRouteState(url, {
|
|
8005
9653
|
source: 'navigation-end',
|
|
8006
9654
|
});
|
|
9655
|
+
if (this.currentNavigationIsPopstate) {
|
|
9656
|
+
// The user (or NSLocationStrategy.back()) walked the back-stack down
|
|
9657
|
+
// by one page; mirror that by dropping the top of our snapshot so a
|
|
9658
|
+
// subsequent HMR reboot doesn't carry the popped page back into view.
|
|
9659
|
+
popAngularHmrRouteHistoryEntry();
|
|
9660
|
+
}
|
|
9661
|
+
else if (this.currentNavigationReplaceUrl) {
|
|
9662
|
+
replaceAngularHmrRouteHistoryTop(url);
|
|
9663
|
+
}
|
|
9664
|
+
else {
|
|
9665
|
+
pushAngularHmrRouteHistoryEntry(url);
|
|
9666
|
+
}
|
|
9667
|
+
this.currentNavigationIsPopstate = false;
|
|
9668
|
+
this.currentNavigationReplaceUrl = false;
|
|
8007
9669
|
}
|
|
8008
9670
|
});
|
|
8009
9671
|
}
|
|
@@ -8012,6 +9674,32 @@ class NativeScriptAngularHmrRouteTracker {
|
|
|
8012
9674
|
this.disposeCaptureHook?.();
|
|
8013
9675
|
}
|
|
8014
9676
|
captureCurrentRoute(source) {
|
|
9677
|
+
if (source === 'hmr-reboot') {
|
|
9678
|
+
// Snapshot the live mirror first so the bootstrap can replay forward
|
|
9679
|
+
// navigations to rebuild the back-stack. The pending single-URL slot
|
|
9680
|
+
// remains useful as a fallback when the snapshot turns out to be empty
|
|
9681
|
+
// (e.g. bootstrap-time HMR before the first NavigationEnd).
|
|
9682
|
+
snapshotAngularHmrRouteHistory();
|
|
9683
|
+
}
|
|
9684
|
+
else if (source === 'bootstrap') {
|
|
9685
|
+
// Seed the live mirror with the current URL so the very first HMR
|
|
9686
|
+
// before any user navigation still has a stack of size one to snapshot.
|
|
9687
|
+
//
|
|
9688
|
+
// Skip empty / root URLs: at ENVIRONMENT_INITIALIZER time the router
|
|
9689
|
+
// has not run its initial navigation yet so `router.url` is "/" (or
|
|
9690
|
+
// an empty string). Pushing that here would seed the mirror with a
|
|
9691
|
+
// noise entry that becomes the bottom of the next snapshot, which in
|
|
9692
|
+
// turn becomes the next bootstrap's `START_PATH`. The router then
|
|
9693
|
+
// boots to "/" → redirects to the real default route → fires an
|
|
9694
|
+
// extra `NavigationEnd` that re-enters the replay path. The first
|
|
9695
|
+
// genuine `NavigationEnd` arrives a moment later through the event
|
|
9696
|
+
// subscription below and seeds the mirror with the real URL, so
|
|
9697
|
+
// dropping the seed here is safe.
|
|
9698
|
+
const seedUrl = this.router.url;
|
|
9699
|
+
if (seedUrl && seedUrl !== '/') {
|
|
9700
|
+
pushAngularHmrRouteHistoryEntry(seedUrl);
|
|
9701
|
+
}
|
|
9702
|
+
}
|
|
8015
9703
|
return writeAngularHmrRouteState(this.router.url, {
|
|
8016
9704
|
pending: source === 'hmr-reboot',
|
|
8017
9705
|
source,
|
|
@@ -8020,10 +9708,6 @@ class NativeScriptAngularHmrRouteTracker {
|
|
|
8020
9708
|
installCaptureHook() {
|
|
8021
9709
|
return installAngularHmrRouteCaptureHook(() => this.captureCurrentRoute('hmr-reboot'));
|
|
8022
9710
|
}
|
|
8023
|
-
isHmrEnabled() {
|
|
8024
|
-
const g = globalThis;
|
|
8025
|
-
return !!g.__NS_DEV_PLACEHOLDER_ROOT_EARLY__ || typeof g.__reboot_ng_modules__ === 'function';
|
|
8026
|
-
}
|
|
8027
9711
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: NativeScriptAngularHmrRouteTracker, deps: [{ token: i1$3.Router }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
8028
9712
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.9", ngImport: i0, type: NativeScriptAngularHmrRouteTracker }); }
|
|
8029
9713
|
}
|
|
@@ -8057,10 +9741,11 @@ class NativeScriptRouterModule {
|
|
|
8057
9741
|
NSRouteReuseStrategy,
|
|
8058
9742
|
{ provide: RouteReuseStrategy, useExisting: NSRouteReuseStrategy },
|
|
8059
9743
|
NativeScriptAngularHmrRouteTracker,
|
|
9744
|
+
NativeScriptAngularHmrRouteReplay,
|
|
8060
9745
|
{
|
|
8061
9746
|
provide: APP_BOOTSTRAP_LISTENER,
|
|
8062
9747
|
multi: true,
|
|
8063
|
-
deps: [NativeScriptAngularHmrRouteTracker],
|
|
9748
|
+
deps: [NativeScriptAngularHmrRouteTracker, NativeScriptAngularHmrRouteReplay],
|
|
8064
9749
|
useFactory: () => () => undefined,
|
|
8065
9750
|
},
|
|
8066
9751
|
],
|
|
@@ -8103,11 +9788,13 @@ function provideNativeScriptRouter(routes, ...features) {
|
|
|
8103
9788
|
NSRouteReuseStrategy,
|
|
8104
9789
|
{ provide: RouteReuseStrategy, useExisting: NSRouteReuseStrategy },
|
|
8105
9790
|
NativeScriptAngularHmrRouteTracker,
|
|
9791
|
+
NativeScriptAngularHmrRouteReplay,
|
|
8106
9792
|
{
|
|
8107
9793
|
provide: ENVIRONMENT_INITIALIZER,
|
|
8108
9794
|
multi: true,
|
|
8109
9795
|
useValue: () => {
|
|
8110
9796
|
inject(NativeScriptAngularHmrRouteTracker);
|
|
9797
|
+
inject(NativeScriptAngularHmrRouteReplay);
|
|
8111
9798
|
},
|
|
8112
9799
|
},
|
|
8113
9800
|
// {provide: APP_BOOTSTRAP_LISTENER, multi: true, useFactory: getBootstrapListener},
|
|
@@ -8501,5 +10188,5 @@ function provideNativeScriptNgZone(options) {
|
|
|
8501
10188
|
* Generated bundle index. Do not edit.
|
|
8502
10189
|
*/
|
|
8503
10190
|
|
|
8504
|
-
export { APP_ROOT_VIEW, ActionBarComponent, ActionBarScope, ActionItemDirective, AndroidFilterComponent, AppHostAsyncView, AppHostView, AppleFilterComponent, BasePortalOutlet, BaseValueAccessor, COMMON_PROVIDERS, CdkPortal, CdkPortalOutlet, CheckedValueAccessor, CommentNode, ComponentPortal, DEVICE, DISABLE_ROOT_VIEW_HANDLING, DateValueAccessor, DetachedLoader, DomPortal, ENABLE_REUSABE_VIEWS, EmulatedRenderer, FrameDirective, FramePageComponent, FramePageModule, FrameService, IOSFilterComponent, InjectableAnimationEngine, InvisibleNode, ItemContext, ListViewComponent, ModalDialogParams, ModalDialogService, NAMESPACE_FILTERS, NATIVESCRIPT_MODULE_PROVIDERS, NATIVESCRIPT_MODULE_STATIC_PROVIDERS, NATIVESCRIPT_ROOT_MODULE_ID, NATIVE_DIALOG_DATA, NATIVE_DIALOG_DEFAULT_OPTIONS, NSEmptyOutletComponent, NSFileSystem, NSLocationStrategy, NSRouteReuseStrategy, NSRouterLink, NSRouterLinkActive, NativeDialog, NativeDialogCloseDirective, NativeDialogConfig, NativeDialogModule, NativeDialogRef, NativeDialog as NativeDialogService, NativeModalRef, NativeScriptAnimationDriver, NativeScriptAnimationPlayer, NativeScriptAnimationsModule, NativeScriptCommonModule, NativeScriptDocument, NativeScriptDomPortalOutlet, NativeScriptFormsModule, NativeScriptHttpClientModule, NativeScriptLoadingService, NativeScriptModule, NativeScriptNgSafeEvent, NativeScriptNgZone, NativeScriptRendererFactory, NativeScriptRendererHelperService, NativeScriptRouterModule, NativeScriptSanitizer, NativescriptXhrFactory, NavigationButtonDirective, NgViewRef, NsHttpBackEnd, NsTemplatedItem, NumberValueAccessor, Outlet, PAGE_FACTORY, PREVENT_CHANGE_EVENTS_DURING_CD, PREVENT_SPECIFIC_EVENTS_DURING_CD, PageDirective, PageRoute, PageRouterOutlet, PageService, PlatformNamespaceFilter, Portal, PortalModule, RootCompositeModule, RootViewProxy, RouterExtensions, START_PATH, SelectedIndexValueAccessor, TEMPLATED_ITEMS_COMPONENT, TabViewDirective, TabViewItemDirective, TemplateKeyDirective, TemplatePortal, TextNode, TextValueAccessor, TimeValueAccessor, VisionOSFilterComponent, bootstrapApplication, createKeyframeAnimation, customFrameComponentFactory, customFrameDirectiveFactory, customPageFactory, customPageFactoryFromFrame, dashCaseToCamelCase, defaultNavOptions, defaultPageFactory, defaultPageFactoryProvider, detachViewFromParent, disableRootViewHanding, errorHandler, extractSingleViewRecursive, frameMeta, generateDetachedLoader, generateFallbackRootView, generateNativeScriptView, generateRandomId, generateRootLayoutAndProxy, getFirstNativeLikeView, getItemViewRoot, getSingleViewRecursive, getViewClass, getViewMeta, instantiateDefaultStyleNormalizer, instantiateSupportedAnimationDriver, isBlank, isContentView, isDetachedElement, isInvisibleNode, isJsObject, isKnownView, isLayout, isListLikeIterable, isPresent, isView, onAfterLivesync, onBeforeLivesync, once, platformNativeScript, platformNativeScriptDynamic, postAngularBootstrap$, preAngularDisposal$, provideLocationStrategy, provideNativeScriptHttpClient, provideNativeScriptNgZone, provideNativeScriptRouter, registerElement, registerNativeScriptViewComponents, rootRoute, runNativeScriptAngularApp, throwIfAlreadyLoaded, throwNoPortalAttachedError, throwNullPortalError, throwNullPortalOutletError, throwPortalAlreadyAttachedError, throwPortalOutletAlreadyDisposedError, throwUnknownPortalTypeError, COMPONENT_VARIABLE as ɵCOMPONENT_VARIABLE, CONTENT_ATTR as ɵCONTENT_ATTR, HOST_ATTR as ɵHOST_ATTR, NativeScriptDebug as ɵNativeScriptAngularDebug, viewUtil as ɵViewUtil, actionBarMeta as ɵactionBarMeta, elementMap as ɵelementMap, isActionItem as ɵisActionItem, isNavigationButton as ɵisNavigationButton };
|
|
10191
|
+
export { APP_ROOT_VIEW, ActionBarComponent, ActionBarScope, ActionItemDirective, AndroidFilterComponent, AppHostAsyncView, AppHostView, AppleFilterComponent, BasePortalOutlet, BaseValueAccessor, COMMON_PROVIDERS, CdkPortal, CdkPortalOutlet, CheckedValueAccessor, CommentNode, ComponentPortal, DEVICE, DISABLE_ROOT_VIEW_HANDLING, DateValueAccessor, DetachedLoader, DomPortal, ENABLE_REUSABE_VIEWS, EmulatedRenderer, FrameDirective, FramePageComponent, FramePageModule, FrameService, IOSFilterComponent, InjectableAnimationEngine, InvisibleNode, ItemContext, ListViewComponent, ModalDialogParams, ModalDialogService, NAMESPACE_FILTERS, NATIVESCRIPT_MODULE_PROVIDERS, NATIVESCRIPT_MODULE_STATIC_PROVIDERS, NATIVESCRIPT_ROOT_MODULE_ID, NATIVE_DIALOG_DATA, NATIVE_DIALOG_DEFAULT_OPTIONS, NSEmptyOutletComponent, NSFileSystem, NSLocationStrategy, NSRouteReuseStrategy, NSRouterLink, NSRouterLinkActive, NativeDialog, NativeDialogCloseDirective, NativeDialogConfig, NativeDialogModule, NativeDialogRef, NativeDialog as NativeDialogService, NativeModalRef, NativeScriptAnimationDriver, NativeScriptAnimationPlayer, NativeScriptAnimationsModule, NativeScriptCommonModule, NativeScriptDocument, NativeScriptDomPortalOutlet, NativeScriptFormsModule, NativeScriptHttpClientModule, NativeScriptLoadingService, NativeScriptModule, NativeScriptNgSafeEvent, NativeScriptNgZone, NativeScriptRendererFactory, NativeScriptRendererHelperService, NativeScriptRouterModule, NativeScriptSanitizer, NativescriptXhrFactory, NavigationButtonDirective, NgViewRef, NsHttpBackEnd, NsTemplatedItem, NumberValueAccessor, Outlet, PAGE_FACTORY, PREVENT_CHANGE_EVENTS_DURING_CD, PREVENT_SPECIFIC_EVENTS_DURING_CD, PageDirective, PageRoute, PageRouterOutlet, PageService, PlatformNamespaceFilter, Portal, PortalModule, RootCompositeModule, RootViewProxy, RouterExtensions, START_PATH, SelectedIndexValueAccessor, TEMPLATED_ITEMS_COMPONENT, TabViewDirective, TabViewItemDirective, TemplateKeyDirective, TemplatePortal, TextNode, TextValueAccessor, TimeValueAccessor, VisionOSFilterComponent, bootstrapApplication, createKeyframeAnimation, customFrameComponentFactory, customFrameDirectiveFactory, customPageFactory, customPageFactoryFromFrame, dashCaseToCamelCase, defaultNavOptions, defaultPageFactory, defaultPageFactoryProvider, detachViewFromParent, disableRootViewHanding, errorHandler, extractSingleViewRecursive, frameMeta, generateDetachedLoader, generateFallbackRootView, generateNativeScriptView, generateRandomId, generateRootLayoutAndProxy, getAngularHmrRestoringRoute, getFirstNativeLikeView, getItemViewRoot, getSingleViewRecursive, getViewClass, getViewMeta, instantiateDefaultStyleNormalizer, instantiateSupportedAnimationDriver, isAngularHmrRestoringRoute, isBlank, isContentView, isDetachedElement, isInvisibleNode, isJsObject, isKnownView, isLayout, isListLikeIterable, isPresent, isView, onAfterLivesync, onBeforeLivesync, once, platformNativeScript, platformNativeScriptDynamic, postAngularBootstrap$, preAngularDisposal$, provideLocationStrategy, provideNativeScriptHttpClient, provideNativeScriptNgZone, provideNativeScriptRouter, registerElement, registerNativeScriptViewComponents, rootRoute, runNativeScriptAngularApp, throwIfAlreadyLoaded, throwNoPortalAttachedError, throwNullPortalError, throwNullPortalOutletError, throwPortalAlreadyAttachedError, throwPortalOutletAlreadyDisposedError, throwUnknownPortalTypeError, COMPONENT_VARIABLE as ɵCOMPONENT_VARIABLE, CONTENT_ATTR as ɵCONTENT_ATTR, HOST_ATTR as ɵHOST_ATTR, NativeScriptDebug as ɵNativeScriptAngularDebug, viewUtil as ɵViewUtil, actionBarMeta as ɵactionBarMeta, elementMap as ɵelementMap, isActionItem as ɵisActionItem, isNavigationButton as ɵisNavigationButton };
|
|
8505
10192
|
//# sourceMappingURL=nativescript-angular.mjs.map
|