@@ -321,8 +321,8 @@ export class iOSApplication extends ApplicationCommon implements IiOSApplication
321321 return this . _rootView ;
322322 }
323323
324- resetRootView ( view ?: View ) {
325- super . resetRootView ( view ) ;
324+ resetRootView ( entry ?: NavigationEntry | string ) {
325+ super . resetRootView ( entry ) ;
326326 this . setWindowContent ( ) ;
327327 }
328328
@@ -331,6 +331,42 @@ export class iOSApplication extends ApplicationCommon implements IiOSApplication
331331 this . started = true ;
332332
333333 if ( this . nativeApp ) {
334+ // During Vite HMR dev boot, the placeholder has already started
335+ // the app lifecycle. A second run() with an entry should replace
336+ // the placeholder root, NOT present a modal via runAsEmbeddedApp.
337+ // The HTTP ESM realm creates a separate @nativescript /core instance,
338+ // so JS-level patching of Application.run can't intercept this call.
339+ // Detect HMR mode via the placeholder's global flag and use
340+ // setWindowContent on the PRIMARY Application singleton (bundled realm)
341+ // which has the actual window and root view hierarchy.
342+ const g = globalThis as any ;
343+ if ( g . __NS_DEV_PLACEHOLDER_ROOT_VIEW__ || g . __NS_DEV_PLACEHOLDER_ROOT_EARLY__ ) {
344+ if ( entry ) {
345+ // Defer to next run loop tick so resetRootView executes outside the
346+ // HTTP ESM import context. Direct calls can fail with
347+ // "ReferenceError: __COMMONJS__ is not defined" because the JS
348+ // execution stack is in the HTTP realm when Builder loads modules.
349+ const resolvedEntry = typeof entry === 'string' ? { moduleName : entry } : entry ;
350+ setTimeout ( ( ) => {
351+ try {
352+ const primaryApp = g . Application ;
353+ if ( primaryApp && typeof primaryApp . resetRootView === 'function' ) {
354+ primaryApp . resetRootView ( resolvedEntry ) ;
355+ }
356+ } catch ( e ) {
357+ if ( __DEV__ ) console . warn ( '[app-ios] deferred resetRootView failed:' , e ) ;
358+ }
359+ delete g . __NS_DEV_PLACEHOLDER_ROOT_VIEW__ ;
360+ delete g . __NS_DEV_PLACEHOLDER_ROOT_EARLY__ ;
361+ } , 0 ) ;
362+ } else {
363+ // Framework (e.g. Angular) calls run() with no entry — it manages
364+ // root views itself via resetRootView(). No-op to avoid presenting
365+ // a modal via runAsEmbeddedApp or throwing "Main entry is missing".
366+ if ( __DEV__ ) console . info ( '[app-ios] run() called with no entry during HMR placeholder; framework manages root view' ) ;
367+ }
368+ return ;
369+ }
334370 this . runAsEmbeddedApp ( ) ;
335371 } else {
336372 this . runAsMainApp ( ) ;
@@ -1044,7 +1080,13 @@ export class iOSApplication extends ApplicationCommon implements IiOSApplication
10441080
10451081 // During initial scene startup we must wait for launch to be notified first.
10461082 // Some frameworks provide root content from launch handlers.
1047- if ( this . hasLaunched ( ) ) {
1083+ // Guard: skip setWindowContent when no main entry is configured yet.
1084+ // During Vite HMR dev boot, the placeholder calls Application.run() with
1085+ // no entry; the real entry is set later when the HTTP-loaded main module
1086+ // calls Application.run({ moduleName: ... }). Without this guard the
1087+ // scene handler would throw "Main entry is missing" and leave the window
1088+ // in a broken state (root view reset but no replacement created).
1089+ if ( this . hasLaunched ( ) && this . getMainEntry ( ) ) {
10481090 this . setWindowContent ( ) ;
10491091 }
10501092 }
@@ -1388,6 +1430,7 @@ global.__onLiveSyncCore = function (context?: ModuleContext) {
13881430} ;
13891431
13901432export * from './application-common' ;
1433+ export * from './application-interfaces' ;
13911434export const Application = iosApp ;
13921435export const AndroidApplication = undefined ;
13931436
0 commit comments