Skip to content

Commit 22f594e

Browse files
committed
refactor: introduce separate run-on-devices-controller
1 parent 73ee306 commit 22f594e

File tree

11 files changed

+258
-139
lines changed

11 files changed

+258
-139
lines changed

PublicAPI.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
Public API
23
==
34

lib/bootstrap.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,18 @@ $injector.require("platformValidationService", "./services/platform/platform-val
4141
$injector.require("platformCommandsService", "./services/platform/platform-commands-service");
4242
$injector.require("platformWatcherService", "./services/platform/platform-watcher-service");
4343

44+
$injector.require("buildArtefactsService", "./services/build-artefacts-service");
45+
46+
$injector.require("runOnDevicesDataService", "./services/run-on-devices-data-service");
4447
$injector.require("deviceInstallAppService", "./services/device/device-install-app-service");
4548
$injector.require("deviceRefreshAppService", "./services/device/device-refresh-app-service");
4649

4750
$injector.require("workflowDataService", "./services/workflow/workflow-data-service");
4851

4952
$injector.require("mainController", "./controllers/main-controller");
50-
$injector.require("runOnDeviceController", "./controllers/run-on-device");
53+
$injector.require("runOnDevicesController", "./controllers/run-on-devices-controller");
5154

52-
$injector.require("buildArtefactsService", "./services/build-artefacts-service");
55+
$injector.require("liveSyncServiceResolver", "./resolvers/livesync-service-resolver");
5356

5457
$injector.require("debugDataService", "./services/debug-data-service");
5558
$injector.requirePublicClass("debugService", "./services/debug-service");

lib/constants.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -277,12 +277,12 @@ export class AndroidAppBundleMessages {
277277
public static ANDROID_APP_BUNDLE_PUBLISH_DOCS_MESSAGE = "How to use Android App Bundle for publishing: https://docs.nativescript.org/tooling/publishing/publishing-android-apps#android-app-bundle";
278278
}
279279

280-
export const LiveSyncEvents = {
281-
liveSyncStopped: "liveSyncStopped",
280+
export const RunOnDeviceEvents = {
281+
runOnDeviceStopped: "runOnDeviceStopped",
282282
// In case we name it error, EventEmitter expects instance of Error to be raised and will also raise uncaught exception in case there's no handler
283-
liveSyncError: "liveSyncError",
283+
runOnDeviceError: "runOnDeviceError",
284284
previewAppLiveSyncError: PreviewAppLiveSyncEvents.PREVIEW_APP_LIVE_SYNC_ERROR,
285-
liveSyncExecuted: "liveSyncExecuted",
286-
liveSyncStarted: "liveSyncStarted",
287-
liveSyncNotification: "notify"
285+
runOnDeviceExecuted: "runOnDeviceExecuted",
286+
runOnDeviceStarted: "runOnDeviceStarted",
287+
runOnDeviceNotification: "notify"
288288
};

lib/controllers/main-controller.ts

Lines changed: 55 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,30 @@
11
import { AddPlatformService } from "../services/platform/add-platform-service";
22
import { BuildPlatformService } from "../services/platform/build-platform-service";
33
import { DeviceInstallAppService } from "../services/device/device-install-app-service";
4-
import { DeviceRefreshAppService } from "../services/device/device-refresh-app-service";
54
import { EventEmitter } from "events";
6-
import { FILES_CHANGE_EVENT_NAME, INITIAL_SYNC_EVENT_NAME, LiveSyncEvents } from "../constants";
5+
import { FILES_CHANGE_EVENT_NAME, INITIAL_SYNC_EVENT_NAME, RunOnDeviceEvents } from "../constants";
76
import { PreparePlatformService } from "../services/platform/prepare-platform-service";
87
import { WorkflowDataService } from "../services/workflow/workflow-data-service";
9-
10-
const deviceDescriptorPrimaryKey = "identifier";
8+
import { RunOnDevicesController } from "./run-on-devices-controller";
9+
import { RunOnDevicesDataService } from "../services/run-on-devices-data-service";
10+
import { cache } from "../common/decorators";
11+
import { DeviceDiscoveryEventNames } from "../common/constants";
1112

1213
export class MainController extends EventEmitter {
13-
private liveSyncProcessesInfo: IDictionary<ILiveSyncProcessInfo> = {};
14-
1514
constructor(
1615
private $addPlatformService: AddPlatformService,
1716
private $buildPlatformService: BuildPlatformService,
1817
private $deviceInstallAppService: DeviceInstallAppService,
19-
private $deviceRefreshAppService: DeviceRefreshAppService,
2018
private $devicesService: Mobile.IDevicesService,
2119
private $errors: IErrors,
2220
private $hooksService: IHooksService,
23-
private $injector: IInjector,
2421
private $logger: ILogger,
25-
private $mobileHelper: Mobile.IMobileHelper,
2622
private $platformWatcherService: IPlatformWatcherService,
2723
private $pluginsService: IPluginsService,
2824
private $preparePlatformService: PreparePlatformService,
2925
private $projectDataService: IProjectDataService,
26+
private $runOnDevicesController: RunOnDevicesController,
27+
private $runOnDevicesDataService: RunOnDevicesDataService,
3028
private $workflowDataService: WorkflowDataService
3129
) { super(); }
3230

@@ -46,7 +44,7 @@ export class MainController extends EventEmitter {
4644
return result;
4745
}
4846

49-
public async deployPlatform(projectDir: string, deviceDescriptors: ILiveSyncDeviceInfo[], liveSyncInfo: ILiveSyncInfo): Promise<void> {
47+
public async deployOnDevices(projectDir: string, deviceDescriptors: ILiveSyncDeviceInfo[], liveSyncInfo: ILiveSyncInfo): Promise<void> {
5048
const platforms = this.$devicesService.getPlatformsFromDeviceDescriptors(deviceDescriptors);
5149

5250
for (const platform of platforms) {
@@ -62,7 +60,7 @@ export class MainController extends EventEmitter {
6260
await this.$devicesService.execute(executeAction, (device: Mobile.IDevice) => true);
6361
}
6462

65-
public async runPlatform(projectDir: string, deviceDescriptors: ILiveSyncDeviceInfo[], liveSyncInfo: ILiveSyncInfo): Promise<void> {
63+
public async runOnDevices(projectDir: string, deviceDescriptors: ILiveSyncDeviceInfo[], liveSyncInfo: ILiveSyncInfo): Promise<void> {
6664
const projectData = this.$projectDataService.getProjectData(projectDir);
6765
await this.initializeSetup(projectData);
6866

@@ -73,68 +71,34 @@ export class MainController extends EventEmitter {
7371
await this.$addPlatformService.addPlatformIfNeeded(nativePlatformData, projectData, addPlatformData);
7472
}
7573

76-
this.setLiveSyncProcessInfo(projectDir, liveSyncInfo, deviceDescriptors);
74+
// TODO: Consider to handle correctly the descriptors when livesync is executed for second time for the same projectDir
7775

78-
const shouldStartWatcher = !liveSyncInfo.skipWatcher && (liveSyncInfo.syncToPreviewApp || this.liveSyncProcessesInfo[projectData.projectDir].deviceDescriptors.length);
76+
this.$runOnDevicesDataService.persistData(projectDir, liveSyncInfo, deviceDescriptors);
77+
78+
const shouldStartWatcher = !liveSyncInfo.skipWatcher && (liveSyncInfo.syncToPreviewApp || this.$runOnDevicesDataService.hasDeviceDescriptors(projectDir));
7979
if (shouldStartWatcher) {
80+
this.handleRunOnDeviceEvents(projectDir);
81+
8082
this.$platformWatcherService.on(INITIAL_SYNC_EVENT_NAME, async (data: IInitialSyncEventData) => {
81-
const executeAction = async (device: Mobile.IDevice) => {
82-
const deviceDescriptor = _.find(deviceDescriptors, dd => dd.identifier === device.deviceInfo.identifier);
83-
await this.syncInitialDataOnDevice(device, deviceDescriptor, projectData, liveSyncInfo);
84-
};
85-
const canExecuteAction = (device: Mobile.IDevice) => device.deviceInfo.platform.toLowerCase() === data.platform.toLowerCase() && _.some(deviceDescriptors, deviceDescriptor => deviceDescriptor.identifier === device.deviceInfo.identifier);
86-
await this.addActionToChain(projectData.projectDir, () => this.$devicesService.execute(executeAction, canExecuteAction));
83+
await this.$runOnDevicesController.syncInitialDataOnDevice(data, projectData, liveSyncInfo, deviceDescriptors);
8784
});
8885
this.$platformWatcherService.on(FILES_CHANGE_EVENT_NAME, async (data: IFilesChangeEventData) => {
89-
const executeAction = async (device: Mobile.IDevice) => {
90-
const deviceDescriptor = _.find(deviceDescriptors, dd => dd.identifier === device.deviceInfo.identifier);
91-
await this.syncChangedDataOnDevice(device, deviceDescriptor, data, projectData, liveSyncInfo);
92-
};
93-
const canExecuteAction = (device: Mobile.IDevice) => {
94-
const liveSyncProcessInfo = this.liveSyncProcessesInfo[projectData.projectDir];
95-
return (data.platform.toLowerCase() === device.deviceInfo.platform.toLowerCase()) && liveSyncProcessInfo && _.some(liveSyncProcessInfo.deviceDescriptors, deviceDescriptor => deviceDescriptor.identifier === device.deviceInfo.identifier);
96-
};
97-
await this.addActionToChain(projectData.projectDir, () => this.$devicesService.execute(executeAction, canExecuteAction));
86+
await this.$runOnDevicesController.syncChangedDataOnDevice(data, projectData, liveSyncInfo, deviceDescriptors);
9887
});
9988

10089
for (const platform of platforms) {
10190
const { nativePlatformData, preparePlatformData } = this.$workflowDataService.createWorkflowData(platform, projectDir, liveSyncInfo);
10291
await this.$platformWatcherService.startWatcher(nativePlatformData, projectData, preparePlatformData);
10392
}
10493
}
105-
}
106-
107-
private async syncInitialDataOnDevice(device: Mobile.IDevice, deviceDescriptor: ILiveSyncDeviceInfo, projectData: IProjectData, liveSyncInfo: ILiveSyncInfo): Promise<void> {
108-
const { nativePlatformData: platformData, buildPlatformData } = this.$workflowDataService.createWorkflowData(device.deviceInfo.platform, projectData.projectDir, liveSyncInfo);
109-
110-
try {
111-
const outputPath = deviceDescriptor.outputPath || platformData.getBuildOutputPath(buildPlatformData);
112-
const packageFilePath = await this.$buildPlatformService.buildPlatformIfNeeded(platformData, projectData, buildPlatformData, outputPath);
11394

114-
await this.$deviceInstallAppService.installOnDeviceIfNeeded(device, platformData, projectData, buildPlatformData, packageFilePath, outputPath);
95+
// TODO: Consider how to handle --justlaunch
11596

116-
// TODO: Consider to improve this
117-
const platformLiveSyncService = this.getLiveSyncService(platformData.platformNameLowerCase);
118-
const { force, useHotModuleReload, skipWatcher } = liveSyncInfo;
119-
const liveSyncResultInfo = await platformLiveSyncService.fullSync({ force, useHotModuleReload, projectData, device, watch: !skipWatcher, liveSyncDeviceInfo: deviceDescriptor });
120-
121-
await this.$deviceRefreshAppService.refreshApplication(deviceDescriptor, projectData, liveSyncResultInfo, platformLiveSyncService, this);
122-
} catch (err) {
123-
this.$logger.warn(`Unable to apply changes on device: ${device.deviceInfo.identifier}. Error is: ${err.message}.`);
124-
125-
this.emitLivesyncEvent(LiveSyncEvents.liveSyncError, {
126-
error: err,
127-
deviceIdentifier: device.deviceInfo.identifier,
128-
projectDir: projectData.projectDir,
129-
applicationIdentifier: projectData.projectIdentifiers[platformData.platformNameLowerCase]
130-
});
131-
132-
await this.stopLiveSync(projectData.projectDir, [device.deviceInfo.identifier], { shouldAwaitAllActions: false });
133-
}
97+
this.attachDeviceLostHandler();
13498
}
13599

136-
public async stopLiveSync(projectDir: string, deviceIdentifiers?: string[], stopOptions?: { shouldAwaitAllActions: boolean }): Promise<void> {
137-
const liveSyncProcessInfo = this.liveSyncProcessesInfo[projectDir];
100+
public async stopRunOnDevices(projectDir: string, deviceIdentifiers?: string[], stopOptions?: { shouldAwaitAllActions: boolean }): Promise<void> {
101+
const liveSyncProcessInfo = this.$runOnDevicesDataService.getData(projectDir);
138102
if (liveSyncProcessInfo && !liveSyncProcessInfo.isStopped) {
139103
// In case we are coming from error during livesync, the current action is the one that erred (but we are still executing it),
140104
// so we cannot await it as this will cause infinite loop.
@@ -180,59 +144,37 @@ export class MainController extends EventEmitter {
180144
await liveSyncProcessInfo.currentSyncAction;
181145
}
182146

183-
// Emit LiveSync stopped when we've really stopped.
147+
// Emit RunOnDevice stopped when we've really stopped.
184148
_.each(removedDeviceIdentifiers, deviceIdentifier => {
185-
this.emitLivesyncEvent(LiveSyncEvents.liveSyncStopped, { projectDir, deviceIdentifier });
149+
this.emit(RunOnDeviceEvents.runOnDeviceStopped, { projectDir, deviceIdentifier });
186150
});
187151
}
188152
}
189153

190-
public emitLivesyncEvent(event: string, livesyncData: ILiveSyncEventData): boolean {
191-
this.$logger.trace(`Will emit event ${event} with data`, livesyncData);
192-
return this.emit(event, livesyncData);
154+
public getRunOnDeviceDescriptors(projectDir: string): ILiveSyncDeviceInfo[] {
155+
return this.$runOnDevicesDataService.getDeviceDescriptors(projectDir);
193156
}
194157

195-
private async syncChangedDataOnDevice(device: Mobile.IDevice, deviceDescriptor: ILiveSyncDeviceInfo, data: IFilesChangeEventData, projectData: IProjectData, liveSyncInfo: ILiveSyncInfo): Promise<void> {
196-
console.log("syncChangedDataOnDevice================ ", data);
197-
const { nativePlatformData, buildPlatformData } = this.$workflowDataService.createWorkflowData(device.deviceInfo.platform, projectData.projectDir, liveSyncInfo);
198-
199-
if (data.hasNativeChanges) {
200-
// TODO: Consider to handle nativePluginsChange here (aar rebuilt)
201-
await this.$buildPlatformService.buildPlatform(nativePlatformData, projectData, buildPlatformData);
202-
}
203-
204-
const platformLiveSyncService = this.getLiveSyncService(device.deviceInfo.platform);
205-
const liveSyncResultInfo = await platformLiveSyncService.liveSyncWatchAction(device, {
206-
liveSyncDeviceInfo: deviceDescriptor,
207-
projectData,
208-
filesToRemove: [],
209-
filesToSync: data.files,
210-
isReinstalled: false,
211-
hmrData: null, // platformHmrData,
212-
useHotModuleReload: liveSyncInfo.useHotModuleReload,
213-
force: liveSyncInfo.force,
214-
connectTimeout: 1000
158+
private handleRunOnDeviceEvents(projectDir: string): void {
159+
this.$runOnDevicesController.on(RunOnDeviceEvents.runOnDeviceError, async data => {
160+
this.emit(RunOnDeviceEvents.runOnDeviceError, data);
161+
await this.stopRunOnDevices(projectDir, [data.deviceIdentifier], { shouldAwaitAllActions: false });
215162
});
216163

217-
await this.$deviceRefreshAppService.refreshApplication(deviceDescriptor, projectData, liveSyncResultInfo, platformLiveSyncService, this);
218-
}
219-
220-
public getLiveSyncDeviceDescriptors(projectDir: string): ILiveSyncDeviceInfo[] {
221-
const liveSyncProcessesInfo = this.liveSyncProcessesInfo[projectDir] || <ILiveSyncProcessInfo>{};
222-
const currentDescriptors = liveSyncProcessesInfo.deviceDescriptors;
223-
return currentDescriptors || [];
164+
this.$runOnDevicesController.on(RunOnDeviceEvents.runOnDeviceStarted, data => {
165+
this.emit(RunOnDeviceEvents.runOnDeviceStarted, data);
166+
});
224167
}
225168

226-
private setLiveSyncProcessInfo(projectDir: string, liveSyncInfo: ILiveSyncInfo, deviceDescriptors: ILiveSyncDeviceInfo[]): void {
227-
this.liveSyncProcessesInfo[projectDir] = this.liveSyncProcessesInfo[projectDir] || Object.create(null);
228-
this.liveSyncProcessesInfo[projectDir].actionsChain = this.liveSyncProcessesInfo[projectDir].actionsChain || Promise.resolve();
229-
this.liveSyncProcessesInfo[projectDir].currentSyncAction = this.liveSyncProcessesInfo[projectDir].actionsChain;
230-
this.liveSyncProcessesInfo[projectDir].isStopped = false;
231-
this.liveSyncProcessesInfo[projectDir].syncToPreviewApp = liveSyncInfo.syncToPreviewApp;
169+
// TODO: expose previewOnDevice() method { }
170+
// TODO: enableDebugging -> mainController
171+
// TODO: disableDebugging -> mainController
172+
// TODO: attachDebugger -> mainController
173+
// mainController.runOnDevices(), runOnDevicesController.on("event", () => {})
232174

233-
const currentDeviceDescriptors = this.getLiveSyncDeviceDescriptors(projectDir);
234-
this.liveSyncProcessesInfo[projectDir].deviceDescriptors = _.uniqBy(currentDeviceDescriptors.concat(deviceDescriptors), deviceDescriptorPrimaryKey);
235-
}
175+
// debugOnDevicesController.enableDebugging()
176+
// debugOnDevicesController.disableDebugging()
177+
// debugOnDevicesController.attachDebugger
236178

237179
private async initializeSetup(projectData: IProjectData): Promise<void> {
238180
try {
@@ -243,30 +185,21 @@ export class MainController extends EventEmitter {
243185
}
244186
}
245187

246-
private async addActionToChain<T>(projectDir: string, action: () => Promise<T>): Promise<T> {
247-
const liveSyncInfo = this.liveSyncProcessesInfo[projectDir];
248-
if (liveSyncInfo) {
249-
liveSyncInfo.actionsChain = liveSyncInfo.actionsChain.then(async () => {
250-
if (!liveSyncInfo.isStopped) {
251-
liveSyncInfo.currentSyncAction = action();
252-
const res = await liveSyncInfo.currentSyncAction;
253-
return res;
254-
}
255-
});
256-
257-
const result = await liveSyncInfo.actionsChain;
258-
return result;
259-
}
260-
}
261-
262-
private getLiveSyncService(platform: string): IPlatformLiveSyncService {
263-
if (this.$mobileHelper.isiOSPlatform(platform)) {
264-
return this.$injector.resolve("iOSLiveSyncService");
265-
} else if (this.$mobileHelper.isAndroidPlatform(platform)) {
266-
return this.$injector.resolve("androidLiveSyncService");
267-
}
268-
269-
this.$errors.failWithoutHelp(`Invalid platform ${platform}. Supported platforms are: ${this.$mobileHelper.platformNames.join(", ")}`);
188+
@cache()
189+
private attachDeviceLostHandler(): void {
190+
this.$devicesService.on(DeviceDiscoveryEventNames.DEVICE_LOST, async (device: Mobile.IDevice) => {
191+
this.$logger.trace(`Received ${DeviceDiscoveryEventNames.DEVICE_LOST} event in LiveSync service for ${device.deviceInfo.identifier}. Will stop LiveSync operation for this device.`);
192+
193+
// for (const projectDir in this.liveSyncProcessesInfo) {
194+
// try {
195+
// if (_.find(this.liveSyncProcessesInfo[projectDir].deviceDescriptors, d => d.identifier === device.deviceInfo.identifier)) {
196+
// await this.stopLiveSync(projectDir, [device.deviceInfo.identifier]);
197+
// }
198+
// } catch (err) {
199+
// this.$logger.warn(`Unable to stop LiveSync operation for ${device.deviceInfo.identifier}.`, err);
200+
// }
201+
// }
202+
});
270203
}
271204
}
272205
$injector.register("mainController", MainController);

lib/controllers/run-on-device-controller.ts

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)