Skip to content

Commit 21381f4

Browse files
committed
feat(windows): apply webpack HMR updates without restarting the app
tryRefreshApplication returned false for webpack, so every change restarted the app even though the hot-update files were already in bin\app. After a sync, the CLI now writes an ns-livesync marker into the app's LocalState. The debug host watches for it and calls global.__onLiveSync(), which runs webpack's hot update (NativeScript/windows#32). The CLI only does this when the host has written ns-livesync-ready, so older hosts still restart. shouldRestart now also restarts on a full sync, as Android and iOS do. That is how the CLI recovers when a hot update fails.
1 parent 3c839a5 commit 21381f4

2 files changed

Lines changed: 49 additions & 9 deletions

File tree

‎lib/common/mobile/windows/windows-application-manager.ts‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import { IBuildData } from "../../../definitions/build";
1010
export const WINDOWS_INSPECTOR_MARKER = "ns-inspector";
1111
export const WINDOWS_DEBUGBREAK_MARKER = "ns-debugbreak";
1212
export const WINDOWS_DEBUGGER_STARTED_MARKER = "ns-debugger-started";
13+
// Written by a running debug host that watches for WINDOWS_LIVESYNC_MARKER.
14+
export const WINDOWS_LIVESYNC_READY_MARKER = "ns-livesync-ready";
15+
export const WINDOWS_LIVESYNC_MARKER = "ns-livesync";
1316

1417
export class WindowsApplicationManager extends ApplicationManagerBase {
1518
private _runningPid: number | null = null;
@@ -373,6 +376,28 @@ export class WindowsApplicationManager extends ApplicationManagerBase {
373376
return this._localStatePath(pfn, fileName);
374377
}
375378

379+
/**
380+
* Asks the running app to apply the files just synced into bin\app: the debug host watches
381+
* LocalState for the marker and calls global.__onLiveSync(), which runs webpack's hot update.
382+
* Returns false when the app's host does not watch for it (EXE targets, older runtimes).
383+
*/
384+
public async signalLiveSync(appId: string): Promise<boolean> {
385+
const pfn = await this._resolvePackageFamilyName(appId);
386+
const readyPath = this._localStatePath(pfn, WINDOWS_LIVESYNC_READY_MARKER);
387+
const markerPath = this._localStatePath(pfn, WINDOWS_LIVESYNC_MARKER);
388+
if (!readyPath || !markerPath || !fs.existsSync(readyPath)) {
389+
return false;
390+
}
391+
try {
392+
// New content on every sync, so a marker the host has not consumed yet still changes.
393+
fs.writeFileSync(markerPath, String(Date.now()), "utf8");
394+
return true;
395+
} catch (e) {
396+
this.$logger.trace(`[Windows] Could not write ${WINDOWS_LIVESYNC_MARKER} marker: ${e}`);
397+
return false;
398+
}
399+
}
400+
376401
private _localStatePath(pfn: string, fileName: string): string | null {
377402
const localAppData = process.env.LOCALAPPDATA;
378403
if (!localAppData) return null;

‎lib/services/livesync/windows-device-livesync-service.ts‎

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { spawnSync } from "child_process";
33
import { DeviceLiveSyncServiceBase } from "./device-livesync-service-base";
44
import { IPlatformsDataService } from "../../definitions/platform";
55
import { IProjectData } from "../../definitions/project";
6+
import { WindowsApplicationManager } from "../../common/mobile/windows/windows-application-manager";
67

78
export class WindowsDeviceLiveSyncService
89
extends DeviceLiveSyncServiceBase
@@ -40,22 +41,36 @@ export class WindowsDeviceLiveSyncService
4041
_projectData: IProjectData,
4142
liveSyncInfo: ILiveSyncResultInfo,
4243
): Promise<boolean> {
43-
return !liveSyncInfo.useHotModuleReload || !!liveSyncInfo.waitForDebugger;
44+
// A full sync (also the CLI's recovery after a failed hot update) needs a fresh start.
45+
return (
46+
!liveSyncInfo.useHotModuleReload ||
47+
!!liveSyncInfo.isFullSync ||
48+
!!liveSyncInfo.waitForDebugger
49+
);
4450
}
4551

4652
public async tryRefreshApplication(
4753
projectData: IProjectData,
4854
liveSyncInfo: ILiveSyncResultInfo,
4955
): Promise<boolean> {
50-
// Vite HMR: a running app pulls every change from the dev server over its WebSocket
51-
// (HTTP ES modules), so a synced file never needs a restart. But an app that isn't
52-
// running yet (first sync after install) must still be launched. Webpack HMR isn't
53-
// implemented for Windows: signal a restart.
56+
// An app that isn't running yet (first sync after install) must still be launched.
57+
if (
58+
!liveSyncInfo.useHotModuleReload ||
59+
!this.isAppRunning(projectData.projectName)
60+
) {
61+
return false;
62+
}
63+
// Vite HMR: the app pulls every change from the dev server over its WebSocket
64+
// (HTTP ES modules), so a synced file never needs a restart.
65+
if (projectData.bundler === "vite") {
66+
return true;
67+
}
68+
// Webpack HMR: the hot-update files are already in bin\app; tell the app to apply them.
69+
const appId =
70+
projectData.projectIdentifiers?.["windows"] ?? projectData.projectId;
5471
return (
55-
projectData.bundler === "vite" &&
56-
!!liveSyncInfo.useHotModuleReload &&
57-
this.isAppRunning(projectData.projectName)
58-
);
72+
this.device.applicationManager as WindowsApplicationManager
73+
).signalLiveSync(appId);
5974
}
6075

6176
private isAppRunning(processName: string): boolean {

0 commit comments

Comments
 (0)