@@ -278,7 +278,12 @@ export function runNativeScriptAngularApp<T, K>(options: AppRunOptions<T, K>) {
278278 }
279279 const view = ref . injector . get ( APP_ROOT_VIEW ) as AppHostView | View ;
280280 const newRoot = view instanceof AppHostView ? view . content : view ;
281- console . log ( '[ng-hmr] setRootView: view from injector:' , view ?. constructor ?. name , 'newRoot:' , newRoot ?. constructor ?. name ) ;
281+ console . log (
282+ '[ng-hmr] setRootView: view from injector:' ,
283+ view ?. constructor ?. name ,
284+ 'newRoot:' ,
285+ newRoot ?. constructor ?. name ,
286+ ) ;
282287 console . log ( '[ng-hmr] setRootView: launchEventDone:' , launchEventDone , 'embedded:' , options . embedded ) ;
283288 if ( NativeScriptDebug . isLogEnabled ( ) ) {
284289 NativeScriptDebug . bootstrapLog ( `Setting RootView to ${ newRoot } ` ) ;
@@ -340,67 +345,92 @@ export function runNativeScriptAngularApp<T, K>(options: AppRunOptions<T, K>) {
340345 ref . destroy ( ) ;
341346 return ;
342347 }
343- mainModuleRef = ref ;
344-
345- // Expose ApplicationRef for HMR to trigger change detection
346- // Check for ApplicationRef by duck-typing since instanceof can fail across module realms
347- const refAny = ref as any ;
348- const isAppRef = refAny && typeof refAny . tick === 'function' && Array . isArray ( refAny . components ) ;
349- console . log ( '[ng-hmr] ref type check: isAppRef=' , isAppRef , 'has tick=' , typeof refAny ?. tick === 'function' , 'has components=' , Array . isArray ( refAny ?. components ) ) ;
350-
351- if ( isAppRef ) {
352- global [ '__NS_ANGULAR_APP_REF__' ] = ref ;
353- // Mark boot complete for the HMR system
354- global [ '__NS_HMR_BOOT_COMPLETE__' ] = true ;
355-
356- // Register bootstrapped components for HMR lookup
357- if ( ! global [ '__NS_ANGULAR_COMPONENTS__' ] ) {
358- global [ '__NS_ANGULAR_COMPONENTS__' ] = { } ;
348+
349+ // When Zone.js is active and we're outside the Angular zone (which
350+ // happens in HMR mode — the Promise .then() runs in the root zone),
351+ // wrap the completion handler inside NgZone.run() so that:
352+ // 1. resetRootView + component initialization happens inside the Angular zone
353+ // 2. ngrx effects, store dispatches, and signal-triggered actions run inside NgZone
354+ // 3. strictActionWithinNgZone checks pass for initial actions
355+ // In zoneless apps (no Zone.js), skip the wrapping entirely.
356+ const useZoneWrap = typeof Zone !== 'undefined' && ! NgZone . isInAngularZone ( ) ;
357+ const runInZone = ( fn : ( ) => void ) => {
358+ if ( useZoneWrap ) {
359+ ref . injector . get ( NgZone ) . run ( fn ) ;
360+ } else {
361+ fn ( ) ;
359362 }
360- // Get the component class from the first bootstrapped component
361- console . log ( '[ng-hmr] ApplicationRef components count:' , refAny . components ?. length ) ;
362- if ( refAny . components && refAny . components . length > 0 ) {
363- const componentRef = refAny . components [ 0 ] ;
364- console . log ( '[ng-hmr] componentRef:' , componentRef ?. constructor ?. name ) ;
365- console . log ( '[ng-hmr] componentRef.componentType:' , componentRef ?. componentType ?. name ) ;
366-
367- // For Angular 17+ standalone components, the component type is on componentRef.componentType
368- // For older Angular, try componentRef.instance.constructor
369- let componentType = componentRef ?. componentType ;
370- if ( ! componentType && componentRef ?. instance ) {
371- componentType = componentRef . instance . constructor ;
363+ } ;
364+ runInZone ( ( ) => {
365+ mainModuleRef = ref ;
366+
367+ // Expose ApplicationRef for HMR to trigger change detection
368+ // Check for ApplicationRef by duck-typing since instanceof can fail across module realms
369+ const refAny = ref as any ;
370+ const isAppRef = refAny && typeof refAny . tick === 'function' && Array . isArray ( refAny . components ) ;
371+ console . log (
372+ '[ng-hmr] ref type check: isAppRef=' ,
373+ isAppRef ,
374+ 'has tick=' ,
375+ typeof refAny ?. tick === 'function' ,
376+ 'has components=' ,
377+ Array . isArray ( refAny ?. components ) ,
378+ ) ;
379+
380+ if ( isAppRef ) {
381+ global [ '__NS_ANGULAR_APP_REF__' ] = ref ;
382+ // Mark boot complete for the HMR system
383+ global [ '__NS_HMR_BOOT_COMPLETE__' ] = true ;
384+
385+ // Register bootstrapped components for HMR lookup
386+ if ( ! global [ '__NS_ANGULAR_COMPONENTS__' ] ) {
387+ global [ '__NS_ANGULAR_COMPONENTS__' ] = { } ;
372388 }
373-
374- if ( componentType && componentType . name ) {
375- global [ '__NS_ANGULAR_COMPONENTS__' ] [ componentType . name ] = componentType ;
376- console . log ( '[ng-hmr] Registered component for HMR:' , componentType . name ) ;
389+ // Get the component class from the first bootstrapped component
390+ console . log ( '[ng-hmr] ApplicationRef components count:' , refAny . components ?. length ) ;
391+ if ( refAny . components && refAny . components . length > 0 ) {
392+ const componentRef = refAny . components [ 0 ] ;
393+ console . log ( '[ng-hmr] componentRef:' , componentRef ?. constructor ?. name ) ;
394+ console . log ( '[ng-hmr] componentRef.componentType:' , componentRef ?. componentType ?. name ) ;
395+
396+ // For Angular 17+ standalone components, the component type is on componentRef.componentType
397+ // For older Angular, try componentRef.instance.constructor
398+ let componentType = componentRef ?. componentType ;
399+ if ( ! componentType && componentRef ?. instance ) {
400+ componentType = componentRef . instance . constructor ;
401+ }
402+
403+ if ( componentType && componentType . name ) {
404+ global [ '__NS_ANGULAR_COMPONENTS__' ] [ componentType . name ] = componentType ;
405+ console . log ( '[ng-hmr] Registered component for HMR:' , componentType . name ) ;
406+ } else {
407+ console . log ( '[ng-hmr] Could not get componentType name' ) ;
408+ }
377409 } else {
378- console . log ( '[ng-hmr] Could not get componentType name ' ) ;
410+ console . log ( '[ng-hmr] No components in ApplicationRef ' ) ;
379411 }
380412 } else {
381- console . log ( '[ng-hmr] No components in ApplicationRef' ) ;
382- }
383- } else {
384- const appRef = ref . injector . get ( ApplicationRef , null ) ;
385- if ( appRef ) {
386- global [ '__NS_ANGULAR_APP_REF__' ] = appRef ;
387- // Mark boot complete for the HMR system
388- global [ '__NS_HMR_BOOT_COMPLETE__' ] = true ;
413+ const appRef = ref . injector . get ( ApplicationRef , null ) ;
414+ if ( appRef ) {
415+ global [ '__NS_ANGULAR_APP_REF__' ] = appRef ;
416+ // Mark boot complete for the HMR system
417+ global [ '__NS_HMR_BOOT_COMPLETE__' ] = true ;
418+ }
389419 }
390- }
391420
392- ( isAppRef ? refAny . components [ 0 ] : ref ) . onDestroy (
393- ( ) => ( mainModuleRef = mainModuleRef === ref ? null : mainModuleRef ) ,
394- ) ;
395- updatePlatformRef ( ref , reason ) ;
396- const styleTag = ref . injector . get ( NATIVESCRIPT_ROOT_MODULE_ID ) ;
397- ( isAppRef ? refAny . components [ 0 ] : ref ) . onDestroy ( ( ) => {
398- removeTaggedAdditionalCSS ( styleTag ) ;
421+ ( isAppRef ? refAny . components [ 0 ] : ref ) . onDestroy (
422+ ( ) => ( mainModuleRef = mainModuleRef === ref ? null : mainModuleRef ) ,
423+ ) ;
424+ updatePlatformRef ( ref , reason ) ;
425+ const styleTag = ref . injector . get ( NATIVESCRIPT_ROOT_MODULE_ID ) ;
426+ ( isAppRef ? refAny . components [ 0 ] : ref ) . onDestroy ( ( ) => {
427+ removeTaggedAdditionalCSS ( styleTag ) ;
428+ } ) ;
429+ bootstrapped = true ;
430+ onMainBootstrap ( ) ;
431+ emitModuleBootstrapEvent ( ref , 'main' , reason ) ;
432+ // bootstrapped component: (ref as any)._bootstrapComponents[0];
399433 } ) ;
400- bootstrapped = true ;
401- onMainBootstrap ( ) ;
402- emitModuleBootstrapEvent ( ref , 'main' , reason ) ;
403- // bootstrapped component: (ref as any)._bootstrapComponents[0];
404434 } ,
405435 ( err ) => {
406436 bootstrapped = true ;
@@ -544,7 +574,11 @@ export function runNativeScriptAngularApp<T, K>(options: AppRunOptions<T, K>) {
544574
545575 // Detect HMR environment (webpack or Vite)
546576 const isWebpackHot = ! ! import . meta[ 'webpackHot' ] ;
547- const isViteHot = ! ! import . meta[ 'hot' ] ;
577+ // import.meta.hot is available when code goes through Vite's transform pipeline.
578+ // When @nativescript /angular is pre-bundled in the vendor (esbuild), import.meta.hot
579+ // won't exist. Fall back to the global placeholder flag that the NativeScript Vite
580+ // HMR runtime sets during dev boot.
581+ const isViteHot = ! ! import . meta[ 'hot' ] || ! ! ( globalThis as any ) . __NS_DEV_PLACEHOLDER_ROOT_EARLY__ ;
548582 const isHotReloadEnabled = isWebpackHot || isViteHot ;
549583
550584 // Always expose HMR globals for both webpack and Vite HMR support
0 commit comments