@@ -33,6 +33,7 @@ import {
3333 IHostInfo ,
3434} from "../../common/declarations" ;
3535import { ICleanupService } from "../../definitions/cleanup-service" ;
36+ import { ViteHmrPortService } from "../../contracts/vite-hmr-port-service" ;
3637import { injector } from "../../common/yok" ;
3738import {
3839 resolvePackagePath ,
@@ -79,23 +80,37 @@ export class BundlerCompilerService
7980 private $packageManager : IPackageManager ,
8081 private $packageInstallationManager : IPackageInstallationManager , // private $sharedEventBus: ISharedEventBus
8182 private $projectConfigService : IProjectConfigService ,
83+ private $viteHmrPortService : ViteHmrPortService ,
8284 ) {
8385 super ( ) ;
8486 }
8587
86- private getViteDistOutputPath ( projectDir : string ) : string {
87- return path . join (
88- projectDir ,
89- process . env . NS_VITE_DIST_DIR || VITE_DIST_FOLDER_NAME ,
88+ /**
89+ * Project-relative directory Vite stages its output in before the CLI
90+ * copies it into the platform app. Each platform gets its own directory
91+ * so concurrent iOS and Android sessions (separate terminals or one
92+ * `ns run`) never overwrite each other's bundle or vendor manifest.
93+ * `NS_VITE_DIST_DIR` overrides it verbatim.
94+ */
95+ private getViteDistRelativeDir ( platform : string ) : string {
96+ return (
97+ process . env . NS_VITE_DIST_DIR || `${ VITE_DIST_FOLDER_NAME } /${ platform } `
9098 ) ;
9199 }
92100
101+ private getViteDistOutputPath ( projectDir : string , platform : string ) : string {
102+ return path . join ( projectDir , this . getViteDistRelativeDir ( platform ) ) ;
103+ }
104+
93105 private getViteBuildPaths (
94106 platformData : IPlatformData ,
95107 projectData : IProjectData ,
96108 ) {
97109 return {
98- distOutput : this . getViteDistOutputPath ( projectData . projectDir ) ,
110+ distOutput : this . getViteDistOutputPath (
111+ projectData . projectDir ,
112+ platformData . platformNameLowerCase ,
113+ ) ,
99114 destDir : path . join (
100115 platformData . appDestinationDirectoryPath ,
101116 this . $options . hostProjectModuleName ,
@@ -566,6 +581,12 @@ export class BundlerCompilerService
566581 ...process . env ,
567582 NATIVESCRIPT_WEBPACK_ENV : JSON . stringify ( envData ) ,
568583 NATIVESCRIPT_BUNDLER_ENV : JSON . stringify ( envData ) ,
584+ ...( isVite
585+ ? await this . getViteChildEnv (
586+ platformData . platformNameLowerCase ,
587+ prepareData ,
588+ )
589+ : { } ) ,
569590 } ;
570591 if ( this . $hostInfo . isWindows ) {
571592 Object . assign ( options . env , { APPDATA : process . env . appData } ) ;
@@ -595,16 +616,46 @@ export class BundlerCompilerService
595616 return childProcess ;
596617 }
597618
598- private getViteHmrPort ( ) : number {
599- const fromEnv = Number ( process . env . NS_HMR_PORT ) ;
600- return Number . isFinite ( fromEnv ) && fromEnv > 0 ? fromEnv : 5173 ;
619+ /**
620+ * Whether this prepare runs the long-lived Vite HMR dev server (vite +
621+ * HMR + watch, not release).
622+ */
623+ private isViteHmrSession ( prepareData : IPrepareData ) : boolean {
624+ return (
625+ this . getBundler ( ) === "vite" &&
626+ ! ! prepareData . watch &&
627+ ! ! prepareData . hmr &&
628+ ! prepareData . release
629+ ) ;
630+ }
631+
632+ /**
633+ * Environment both Vite children (the build watcher and the dev server)
634+ * must share for a platform: the staging directory, and — for HMR
635+ * sessions — the dev-server port. The port is resolved here, once, and
636+ * handed to `@nativescript/vite` as `NS_HMR_PORT`, so the URLs baked into
637+ * `bundle.mjs`, the server's bind and the `adb reverse` tunnel all match.
638+ */
639+ private async getViteChildEnv (
640+ platform : string ,
641+ prepareData : IPrepareData ,
642+ ) : Promise < IStringDictionary > {
643+ const env : IStringDictionary = {
644+ NS_VITE_DIST_DIR : this . getViteDistRelativeDir ( platform ) ,
645+ } ;
646+ if ( this . isViteHmrSession ( prepareData ) ) {
647+ env . NS_HMR_PORT = String (
648+ await this . $viteHmrPortService . getPort ( platform ) ,
649+ ) ;
650+ }
651+ return env ;
601652 }
602653
603654 /**
604655 * Spawn and manage the Vite dev server (`vite serve`) for HMR.
605656 *
606657 * Why the CLI owns this. With Vite, HMR needs a long-lived dev server
607- * (HTTP + the `/ns-hmr` websocket on port 5173 ) that the device fetches
658+ * (HTTP + the `/ns-hmr` websocket) that the device fetches
608659 * modules and hot updates from — it is SEPARATE from the
609660 * `vite build --watch` process that emits the `bundle.mjs` bootstrap
610661 * baked into the app. Historically users wired this up themselves with
@@ -626,29 +677,16 @@ export class BundlerCompilerService
626677 prepareData : IPrepareData ,
627678 ) : Promise < void > {
628679 try {
629- if ( this . getBundler ( ) !== "vite" ) {
630- return ;
631- }
632- if ( ! prepareData . watch || ! prepareData . hmr || prepareData . release ) {
680+ if ( ! this . isViteHmrSession ( prepareData ) ) {
633681 return ;
634682 }
635683 const key = platformData . platformNameLowerCase ;
636684 if ( this . viteServeProcesses [ key ] ) {
637685 return ;
638686 }
639687
640- const port = this . getViteHmrPort ( ) ;
641- // One dev server per port. Simultaneous multi-platform HMR in a
642- // single CLI invocation would collide on 5173 — that case still
643- // needs a distinct NS_HMR_PORT per platform, so skip + warn rather
644- // than fail to bind.
645- const collidingPlatform = Object . keys ( this . viteServeProcesses ) [ 0 ] ;
646- if ( collidingPlatform ) {
647- this . $logger . warn (
648- `Vite dev server already running for '${ collidingPlatform } ' on port ${ port } ; skipping a second server for '${ key } '. For simultaneous multi-platform HMR, set a distinct NS_HMR_PORT per platform.` ,
649- ) ;
650- return ;
651- }
688+ const viteEnv = await this . getViteChildEnv ( key , prepareData ) ;
689+ const port = Number ( viteEnv . NS_HMR_PORT ) ;
652690
653691 const envData = this . buildEnvData (
654692 platformData . platformNameLowerCase ,
@@ -690,6 +728,7 @@ export class BundlerCompilerService
690728 env : {
691729 ...process . env ,
692730 NATIVESCRIPT_BUNDLER_ENV : JSON . stringify ( envData ) ,
731+ ...viteEnv ,
693732 } ,
694733 } ;
695734 if ( this . $hostInfo . isWindows ) {
0 commit comments