From 05ce8d0ffec36e25ea664e6dca9d31a82c8e7b63 Mon Sep 17 00:00:00 2001 From: DimitarTachev Date: Thu, 20 Dec 2018 19:36:41 +0200 Subject: [PATCH 01/11] fix: fire the debugger attached event only when the app is restarted (allow debug + hmr in sidekick) --- lib/declarations.d.ts | 6 ++++- lib/definitions/debug.d.ts | 10 +++++++- lib/definitions/livesync.d.ts | 13 +++++++--- lib/services/android-device-debug-service.ts | 19 ++++++++------ lib/services/debug-service-base.ts | 2 +- lib/services/debug-service.ts | 21 ++++++---------- lib/services/ios-device-debug-service.ts | 8 ++++-- .../android-device-livesync-service.ts | 10 +++++--- ...android-device-livesync-sockets-service.ts | 7 +++++- .../livesync/ios-device-livesync-service.ts | 12 ++++++--- lib/services/livesync/livesync-service.ts | 25 +++++++++++++------ .../platform-livesync-service-base.ts | 10 +++++--- test/services/debug-service.ts | 7 +++--- test/stubs.ts | 2 +- 14 files changed, 102 insertions(+), 50 deletions(-) diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index 3bff6085fd..c069049d4b 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -473,10 +473,14 @@ interface IGenerateOptions { collection?: string; } -interface IDebugInformation extends IPort, Mobile.IDeviceIdentifier { +interface IDebugInformation extends IPort, Mobile.IDeviceIdentifier, IHasHasReconnected { url: string; } +interface IHasHasReconnected { + hasReconnected: boolean; +} + interface IPort { port: Number; } diff --git a/lib/definitions/debug.d.ts b/lib/definitions/debug.d.ts index 6c8e4eca67..48bbba18f8 100644 --- a/lib/definitions/debug.d.ts +++ b/lib/definitions/debug.d.ts @@ -100,6 +100,10 @@ interface IDebugOptions { * Defines if the handshake(AppLaunching notification) between CLI and runtime should be executed. The handshake is not needed when CLI retries to attach to the debugger. */ skipHandshake?: boolean; + /** + * Forces the debugger attach event to be emitted. + */ + forceDebuggerAttachedEvent?: boolean; } /** @@ -161,5 +165,9 @@ interface IDeviceDebugService extends IPlatform, NodeJS.EventEmitter { * @param {IDebugOptions} debugOptions Describe possible options to modify the behaivor of the debug operation, for example stop on the first line. * @returns {Promise} Full url where the frontend client may be connected. */ - debug(debugData: IAppDebugData, debugOptions: IDebugOptions): Promise; + debug(debugData: IAppDebugData, debugOptions: IDebugOptions): Promise; +} + +interface IDebugResultInfo extends IHasHasReconnected { + debugUrl: string; } diff --git a/lib/definitions/livesync.d.ts b/lib/definitions/livesync.d.ts index 36d153d452..0d2ad574e8 100644 --- a/lib/definitions/livesync.d.ts +++ b/lib/definitions/livesync.d.ts @@ -188,7 +188,7 @@ interface ILiveSyncEventData { applicationIdentifier?: string, projectDir: string, syncedFiles?: string[], - error? : Error, + error?: Error, notification?: string, isFullSync?: boolean } @@ -390,11 +390,18 @@ interface ITransferFilesOptions { interface IPlatformLiveSyncService { fullSync(syncInfo: IFullSyncInfo): Promise; liveSyncWatchAction(device: Mobile.IDevice, liveSyncInfo: ILiveSyncWatchInfo): Promise; - refreshApplication(projectData: IProjectData, liveSyncInfo: ILiveSyncResultInfo): Promise; + refreshApplication(projectData: IProjectData, liveSyncInfo: ILiveSyncResultInfo): Promise; prepareForLiveSync(device: Mobile.IDevice, data: IProjectDir, liveSyncInfo: ILiveSyncInfo, debugOptions: IDebugOptions): Promise; getDeviceLiveSyncService(device: Mobile.IDevice, projectData: IProjectData): INativeScriptDeviceLiveSyncService; } +interface IHasDidRestart { + didRestart: boolean; +} + +interface IRefreshApplicationInfo extends IHasDidRestart { +} + interface INativeScriptDeviceLiveSyncService extends IDeviceLiveSyncServiceBase { /** * Refreshes the application's content on a device @@ -405,7 +412,7 @@ interface INativeScriptDeviceLiveSyncService extends IDeviceLiveSyncServiceBase * @return {Promise} */ refreshApplication(projectData: IProjectData, - liveSyncInfo: ILiveSyncResultInfo): Promise; + liveSyncInfo: ILiveSyncResultInfo): Promise; /** * Removes specified files from a connected device diff --git a/lib/services/android-device-debug-service.ts b/lib/services/android-device-debug-service.ts index ac63e81278..110946dbef 100644 --- a/lib/services/android-device-debug-service.ts +++ b/lib/services/android-device-debug-service.ts @@ -24,7 +24,7 @@ export class AndroidDeviceDebugService extends DebugServiceBase implements IDevi this.deviceIdentifier = device.deviceInfo.identifier; } - public async debug(debugData: IDebugData, debugOptions: IDebugOptions): Promise { + public async debug(debugData: IDebugData, debugOptions: IDebugOptions): Promise { this._packageName = debugData.applicationIdentifier; const result = this.device.isEmulator ? await this.debugOnEmulator(debugData, debugOptions) @@ -59,7 +59,7 @@ export class AndroidDeviceDebugService extends DebugServiceBase implements IDevi return this.removePortForwarding(); } - private async debugOnEmulator(debugData: IDebugData, debugOptions: IDebugOptions): Promise { + private async debugOnEmulator(debugData: IDebugData, debugOptions: IDebugOptions): Promise { // Assure we've detected the emulator as device // For example in case deployOnEmulator had stated new emulator instance // we need some time to detect it. Let's force detection. @@ -97,7 +97,7 @@ export class AndroidDeviceDebugService extends DebugServiceBase implements IDevi return this.device.adb.executeCommand(["forward", `tcp:${local}`, `localabstract:${remote}`]); } - private async debugOnDevice(debugData: IDebugData, debugOptions: IDebugOptions): Promise { + private async debugOnDevice(debugData: IDebugData, debugOptions: IDebugOptions): Promise { let packageFile = ""; if (!debugOptions.start && !this.device.isEmulator) { @@ -113,27 +113,32 @@ export class AndroidDeviceDebugService extends DebugServiceBase implements IDevi projectName }; - const action = (device: Mobile.IAndroidDevice): Promise => this.debugCore(device, packageFile, appData, debugOptions); + const action = (device: Mobile.IAndroidDevice): Promise => this.debugCore(device, packageFile, appData, debugOptions); const deviceActionResult = await this.$devicesService.execute(action, this.getCanExecuteAction(this.deviceIdentifier)); return deviceActionResult[0].result; } - private async debugCore(device: Mobile.IAndroidDevice, packageFile: string, appData: Mobile.IApplicationData, debugOptions: IDebugOptions): Promise { + private async debugCore(device: Mobile.IAndroidDevice, packageFile: string, appData: Mobile.IApplicationData, debugOptions: IDebugOptions): Promise { + const result: IDebugResultInfo = { hasReconnected: false, debugUrl: null }; + if (debugOptions.stop) { await this.removePortForwarding(); - return null; + return result; } if (!debugOptions.start) { await this.debugStartCore(appData, debugOptions); + result.hasReconnected = true; } await this.validateRunningApp(device.deviceInfo.identifier, appData.appId); const debugPort = await this.getForwardedDebugPort(device.deviceInfo.identifier, appData.appId); await this.printDebugPort(device.deviceInfo.identifier, debugPort); - return this.getChromeDebugUrl(debugOptions, debugPort); + result.debugUrl = this.getChromeDebugUrl(debugOptions, debugPort); + + return result; } private async printDebugPort(deviceId: string, port: number): Promise { diff --git a/lib/services/debug-service-base.ts b/lib/services/debug-service-base.ts index 765a2a33ea..230fbb3d78 100644 --- a/lib/services/debug-service-base.ts +++ b/lib/services/debug-service-base.ts @@ -10,7 +10,7 @@ export abstract class DebugServiceBase extends EventEmitter implements IDeviceDe public abstract get platform(): string; - public abstract async debug(debugData: IDebugData, debugOptions: IDebugOptions): Promise; + public abstract async debug(debugData: IDebugData, debugOptions: IDebugOptions): Promise; public abstract async debugStart(debugData: IDebugData, debugOptions: IDebugOptions): Promise; diff --git a/lib/services/debug-service.ts b/lib/services/debug-service.ts index d9ae030dc5..0b5186ca72 100644 --- a/lib/services/debug-service.ts +++ b/lib/services/debug-service.ts @@ -40,12 +40,6 @@ export class DebugService extends EventEmitter implements IDebugService { } const debugOptions: IDebugOptions = _.cloneDeep(options); - - // TODO: Check if app is running. - // For now we can only check if app is running on Android. - // After we find a way to check on iOS we should use it here. - let result: string; - const debugService = this.getDeviceDebugService(device); if (!debugService) { this.$errors.failWithoutHelp(`Unsupported device OS: ${device.deviceInfo.platform}. You can debug your applications only on iOS or Android.`); @@ -62,9 +56,9 @@ export class DebugService extends EventEmitter implements IDebugService { } } - result = await debugService.debug(debugData, debugOptions); + const debugResultInfo = await debugService.debug(debugData, debugOptions); - return this.getDebugInformation(result, device.deviceInfo.identifier); + return this.getDebugInformation(debugResultInfo, device.deviceInfo.identifier); } public debugStop(deviceIdentifier: string): Promise { @@ -100,16 +94,17 @@ export class DebugService extends EventEmitter implements IDebugService { platformDebugService.on(CONNECTION_ERROR_EVENT_NAME, connectionErrorHandler); } - private getDebugInformation(fullUrl: string, deviceIdentifier: string): IDebugInformation { + private getDebugInformation(debugResultInfo: IDebugResultInfo, deviceIdentifier: string): IDebugInformation { const debugInfo: IDebugInformation = { - url: fullUrl, + url: debugResultInfo.debugUrl, port: 0, - deviceIdentifier + deviceIdentifier, + hasReconnected: debugResultInfo.hasReconnected }; - if (fullUrl) { + if (debugResultInfo.debugUrl) { const parseQueryString = true; - const wsQueryParam = parse(fullUrl, parseQueryString).query.ws; + const wsQueryParam = parse(debugResultInfo.debugUrl, parseQueryString).query.ws; const hostPortSplit = wsQueryParam && wsQueryParam.split(":"); debugInfo.port = hostPortSplit && +hostPortSplit[1]; } diff --git a/lib/services/ios-device-debug-service.ts b/lib/services/ios-device-debug-service.ts index fca655f481..88d6b481c9 100644 --- a/lib/services/ios-device-debug-service.ts +++ b/lib/services/ios-device-debug-service.ts @@ -38,7 +38,8 @@ export class IOSDeviceDebugService extends DebugServiceBase implements IDeviceDe return "ios"; } - public async debug(debugData: IDebugData, debugOptions: IDebugOptions): Promise { + public async debug(debugData: IDebugData, debugOptions: IDebugOptions): Promise { + const result: IDebugResultInfo = { hasReconnected: false, debugUrl: null }; this.validateOptions(debugOptions); await this.startDeviceLogProcess(debugData, debugOptions); @@ -46,9 +47,12 @@ export class IOSDeviceDebugService extends DebugServiceBase implements IDeviceDe if (!debugOptions.start) { await this.startApp(debugData, debugOptions); + result.hasReconnected = true; } - return this.wireDebuggerClient(debugData, debugOptions); + result.debugUrl = await this.wireDebuggerClient(debugData, debugOptions); + + return result; } public async debugStart(debugData: IDebugData, debugOptions: IDebugOptions): Promise { diff --git a/lib/services/livesync/android-device-livesync-service.ts b/lib/services/livesync/android-device-livesync-service.ts index 8c3d779d34..07345a146b 100644 --- a/lib/services/livesync/android-device-livesync-service.ts +++ b/lib/services/livesync/android-device-livesync-service.ts @@ -15,7 +15,7 @@ export class AndroidDeviceLiveSyncService extends AndroidDeviceLiveSyncServiceBa protected device: Mobile.IAndroidDevice, $filesHashService: IFilesHashService, $logger: ILogger) { - super($injector, $platformsData, $filesHashService, $logger, device); + super($injector, $platformsData, $filesHashService, $logger, device); } public async transferFilesOnDevice(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise { @@ -26,7 +26,8 @@ export class AndroidDeviceLiveSyncService extends AndroidDeviceLiveSyncServiceBa await this.device.fileSystem.transferDirectory(deviceAppData, localToDevicePaths, projectFilesPath); } - public async refreshApplication(projectData: IProjectData, liveSyncInfo: ILiveSyncResultInfo): Promise { + public async refreshApplication(projectData: IProjectData, liveSyncInfo: ILiveSyncResultInfo): Promise { + const result: IRefreshApplicationInfo = { didRestart: false }; const deviceAppData = liveSyncInfo.deviceAppData; const localToDevicePaths = liveSyncInfo.modifiedFilesData; const deviceProjectRootDirname = await this.$devicePathProvider.getDeviceProjectRootPath(liveSyncInfo.deviceAppData.device, { @@ -48,8 +49,11 @@ export class AndroidDeviceLiveSyncService extends AndroidDeviceLiveSyncServiceBa (localToDevicePath: Mobile.ILocalToDevicePathData) => !this.canExecuteFastSync(liveSyncInfo, localToDevicePath.getLocalPath(), projectData, this.device.deviceInfo.platform)); if (!canExecuteFastSync) { - return this.restartApplication(deviceAppData, projectData.projectName); + await this.restartApplication(deviceAppData, projectData.projectName); + result.didRestart = true; } + + return result; } private async cleanLivesyncDirectories(deviceAppData: Mobile.IDeviceAppData): Promise { diff --git a/lib/services/livesync/android-device-livesync-sockets-service.ts b/lib/services/livesync/android-device-livesync-sockets-service.ts index 43cd7f7fd1..4480bf080b 100644 --- a/lib/services/livesync/android-device-livesync-sockets-service.ts +++ b/lib/services/livesync/android-device-livesync-sockets-service.ts @@ -92,7 +92,8 @@ export class AndroidDeviceSocketsLiveSyncService extends AndroidDeviceLiveSyncSe return result; } - public async refreshApplication(projectData: IProjectData, liveSyncInfo: IAndroidLiveSyncResultInfo) { + public async refreshApplication(projectData: IProjectData, liveSyncInfo: IAndroidLiveSyncResultInfo): Promise { + const result: IRefreshApplicationInfo = { didRestart: false }; const canExecuteFastSync = !liveSyncInfo.isFullSync && this.canExecuteFastSyncForPaths(liveSyncInfo, liveSyncInfo.modifiedFilesData, projectData, this.device.deviceInfo.platform); if (!canExecuteFastSync || !liveSyncInfo.didRefresh) { await this.device.applicationManager.restartApplication({ appId: liveSyncInfo.deviceAppData.appIdentifier, projectName: projectData.projectName }); @@ -103,7 +104,11 @@ export class AndroidDeviceSocketsLiveSyncService extends AndroidDeviceLiveSyncSe this.$logger.trace("Failed to connect after app restart."); } } + + result.didRestart = true; } + + return result; } public async removeFiles(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], projectFilesPath: string): Promise { diff --git a/lib/services/livesync/ios-device-livesync-service.ts b/lib/services/livesync/ios-device-livesync-service.ts index 62e428c15d..03f693f0a9 100644 --- a/lib/services/livesync/ios-device-livesync-service.ts +++ b/lib/services/livesync/ios-device-livesync-service.ts @@ -37,12 +37,14 @@ export class IOSDeviceLiveSyncService extends DeviceLiveSyncServiceBase implemen await Promise.all(_.map(localToDevicePaths, localToDevicePathData => this.device.fileSystem.deleteFile(localToDevicePathData.getDevicePath(), deviceAppData.appIdentifier))); } - public async refreshApplication(projectData: IProjectData, liveSyncInfo: ILiveSyncResultInfo): Promise { + public async refreshApplication(projectData: IProjectData, liveSyncInfo: ILiveSyncResultInfo): Promise { + const result: IRefreshApplicationInfo = { didRestart: false }; const deviceAppData = liveSyncInfo.deviceAppData; const localToDevicePaths = liveSyncInfo.modifiedFilesData; if (liveSyncInfo.isFullSync) { await this.restartApplication(deviceAppData, projectData.projectName); - return; + result.didRestart = true; + return result; } let scriptRelatedFiles: Mobile.ILocalToDevicePathData[] = []; @@ -54,14 +56,18 @@ export class IOSDeviceLiveSyncService extends DeviceLiveSyncServiceBase implemen if (!canExecuteFastSync) { await this.restartApplication(deviceAppData, projectData.projectName); - return; + result.didRestart = true; + return result; } if (await this.setupSocketIfNeeded(projectData)) { await this.reloadPage(otherFiles); } else { await this.restartApplication(deviceAppData, projectData.projectName); + result.didRestart = true; } + + return result; } private async restartApplication(deviceAppData: Mobile.IDeviceAppData, projectName: string): Promise { diff --git a/lib/services/livesync/livesync-service.ts b/lib/services/livesync/livesync-service.ts index 69654a1264..aa2acd793d 100644 --- a/lib/services/livesync/livesync-service.ts +++ b/lib/services/livesync/livesync-service.ts @@ -156,7 +156,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi } } - private async refreshApplication(projectData: IProjectData, liveSyncResultInfo: ILiveSyncResultInfo, debugOpts?: IDebugOptions, outputPath?: string): Promise { + private async refreshApplication(projectData: IProjectData, liveSyncResultInfo: ILiveSyncResultInfo, debugOpts?: IDebugOptions, outputPath?: string): Promise { const deviceDescriptor = this.getDeviceDescriptor(liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier, projectData.projectDir); return deviceDescriptor && deviceDescriptor.debugggingEnabled ? @@ -164,12 +164,13 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi this.refreshApplicationWithoutDebug(projectData, liveSyncResultInfo, debugOpts, outputPath); } - private async refreshApplicationWithoutDebug(projectData: IProjectData, liveSyncResultInfo: ILiveSyncResultInfo, debugOpts?: IDebugOptions, outputPath?: string, settings?: IShouldSkipEmitLiveSyncNotification): Promise { + private async refreshApplicationWithoutDebug(projectData: IProjectData, liveSyncResultInfo: ILiveSyncResultInfo, debugOpts?: IDebugOptions, outputPath?: string, settings?: IShouldSkipEmitLiveSyncNotification): Promise { + let result: IRefreshApplicationInfo = { didRestart: false }; const platform = liveSyncResultInfo.deviceAppData.platform; const platformLiveSyncService = this.getLiveSyncService(platform); const applicationIdentifier = projectData.projectIdentifiers[platform.toLowerCase()]; try { - await platformLiveSyncService.refreshApplication(projectData, liveSyncResultInfo); + result = await platformLiveSyncService.refreshApplication(projectData, liveSyncResultInfo); } catch (err) { this.$logger.info(`Error while trying to start application ${applicationIdentifier} on device ${liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier}. Error is: ${err.message || err}`); const msg = `Unable to start application ${applicationIdentifier} on device ${liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier}. Try starting it manually.`; @@ -193,12 +194,14 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi deviceIdentifier: liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier, isFullSync: liveSyncResultInfo.isFullSync }); + + return result; } private async refreshApplicationWithDebug(projectData: IProjectData, liveSyncResultInfo: ILiveSyncResultInfo, debugOptions: IDebugOptions, outputPath?: string): Promise { + let didRestart = false; const deviceAppData = liveSyncResultInfo.deviceAppData; debugOptions = debugOptions || {}; - const deviceIdentifier = liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier; if (debugOptions.debugBrk) { await this.$debugService.debugStop(deviceIdentifier); @@ -211,13 +214,15 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi this.handleDeveloperDiskImageError(err, liveSyncResultInfo, projectData, debugOptions, outputPath); } } else { - await this.refreshApplicationWithoutDebug(projectData, liveSyncResultInfo, debugOptions, outputPath, { shouldSkipEmitLiveSyncNotification: true }); + const refreshInfo = await this.refreshApplicationWithoutDebug(projectData, liveSyncResultInfo, debugOptions, outputPath, { shouldSkipEmitLiveSyncNotification: true }); + didRestart = refreshInfo.didRestart; } // we do not stop the application when debugBrk is false, so we need to attach, instead of launch // if we try to send the launch request, the debugger port will not be printed and the command will timeout debugOptions.start = !debugOptions.debugBrk; + debugOptions.forceDebuggerAttachedEvent = didRestart; const deviceOption = { deviceIdentifier: liveSyncResultInfo.deviceAppData.device.deviceInfo.identifier, debugOptions: debugOptions, @@ -269,13 +274,17 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi debugData.pathToAppPackage = this.$platformService.lastOutputPath(settings.platform, buildConfig, projectData, settings.outputPath); const debugInfo = await this.$debugService.debug(debugData, settings.debugOptions); - const result = this.printDebugInformation(debugInfo); + const fireDebuggerAttachedEvent = settings.debugOptions.forceDebuggerAttachedEvent || debugInfo.hasReconnected; + const result = this.printDebugInformation(debugInfo, fireDebuggerAttachedEvent); return result; } - public printDebugInformation(debugInformation: IDebugInformation): IDebugInformation { + public printDebugInformation(debugInformation: IDebugInformation, fireDebuggerAttachedEvent: boolean = true): IDebugInformation { if (!!debugInformation.url) { - this.emit(DEBUGGER_ATTACHED_EVENT_NAME, debugInformation); + if (fireDebuggerAttachedEvent) { + this.emit(DEBUGGER_ATTACHED_EVENT_NAME, debugInformation); + } + this.$logger.info(`To start debugging, open the following URL in Chrome:${EOL}${debugInformation.url}${EOL}`.cyan); } diff --git a/lib/services/livesync/platform-livesync-service-base.ts b/lib/services/livesync/platform-livesync-service-base.ts index 5d290f016b..5d1148048c 100644 --- a/lib/services/livesync/platform-livesync-service-base.ts +++ b/lib/services/livesync/platform-livesync-service-base.ts @@ -27,12 +27,16 @@ export abstract class PlatformLiveSyncServiceBase { protected abstract _getDeviceLiveSyncService(device: Mobile.IDevice, data: IProjectData, frameworkVersion: string): INativeScriptDeviceLiveSyncService; - public async refreshApplication(projectData: IProjectData, liveSyncInfo: ILiveSyncResultInfo): Promise { + public async refreshApplication(projectData: IProjectData, liveSyncInfo: ILiveSyncResultInfo): Promise { + let result: IRefreshApplicationInfo = { didRestart: false }; + if (liveSyncInfo.isFullSync || liveSyncInfo.modifiedFilesData.length) { const deviceLiveSyncService = this.getDeviceLiveSyncService(liveSyncInfo.deviceAppData.device, projectData); this.$logger.info(`Refreshing application on device ${liveSyncInfo.deviceAppData.device.deviceInfo.identifier}...`); - await deviceLiveSyncService.refreshApplication(projectData, liveSyncInfo); + result = await deviceLiveSyncService.refreshApplication(projectData, liveSyncInfo); } + + return result; } public async fullSync(syncInfo: IFullSyncInfo): Promise { @@ -87,7 +91,7 @@ export abstract class PlatformLiveSyncServiceBase { const localToDevicePaths = await this.$projectFilesManager.createLocalToDevicePaths(deviceAppData, projectFilesPath, existingFiles, []); modifiedLocalToDevicePaths.push(...localToDevicePaths); - modifiedLocalToDevicePaths = await this.transferFiles(deviceAppData, localToDevicePaths, projectFilesPath, projectData, liveSyncInfo.liveSyncDeviceInfo, { isFullSync: false, force: liveSyncInfo.force}); + modifiedLocalToDevicePaths = await this.transferFiles(deviceAppData, localToDevicePaths, projectFilesPath, projectData, liveSyncInfo.liveSyncDeviceInfo, { isFullSync: false, force: liveSyncInfo.force }); } } diff --git a/test/services/debug-service.ts b/test/services/debug-service.ts index 93d5958203..5991165dcd 100644 --- a/test/services/debug-service.ts +++ b/test/services/debug-service.ts @@ -11,8 +11,8 @@ const fakeChromeDebugUrl = `fakeChromeDebugUrl?experiments=true&ws=localhost:${f const defaultDeviceIdentifier = "Nexus5"; class PlatformDebugService extends EventEmitter /* implements IPlatformDebugService */ { - public async debug(debugData: IDebugData, debugOptions: IDebugOptions): Promise { - return fakeChromeDebugUrl; + public async debug(debugData: IDebugData, debugOptions: IDebugOptions): Promise { + return { debugUrl: fakeChromeDebugUrl, hasReconnected: false }; } } @@ -226,7 +226,8 @@ describe("debugService", () => { assert.deepEqual(debugInfo, { url: fakeChromeDebugUrl, port: fakeChromeDebugPort, - deviceIdentifier: debugData.deviceIdentifier + deviceIdentifier: debugData.deviceIdentifier, + hasReconnected: false }); }); }); diff --git a/test/stubs.ts b/test/stubs.ts index 1a47e49c09..28a0ce35f8 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -645,7 +645,7 @@ function unexpected(msg: string): Error { } export class DebugServiceStub extends EventEmitter implements IDeviceDebugService { - public async debug(): Promise { + public async debug(): Promise { return; } From 89dd3aa63344566263b58b852adb79af67ca4174 Mon Sep 17 00:00:00 2001 From: fatme Date: Wed, 2 Jan 2019 11:54:50 +0200 Subject: [PATCH 02/11] fix: fix unhandled promise rejection error thrown from sidekick --- .../preview-app-livesync-service.ts | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/services/livesync/playground/preview-app-livesync-service.ts b/lib/services/livesync/playground/preview-app-livesync-service.ts index ed0780cc75..062562f793 100644 --- a/lib/services/livesync/playground/preview-app-livesync-service.ts +++ b/lib/services/livesync/playground/preview-app-livesync-service.ts @@ -26,20 +26,25 @@ export class PreviewAppLiveSyncService extends EventEmitter implements IPreviewA public async initialize(data: IPreviewAppLiveSyncData): Promise { await this.$previewSdkService.initialize(async (device: Device) => { - if (!device) { - this.$errors.failWithoutHelp("Sending initial preview files without a specified device is not supported."); - } + try { + if (!device) { + this.$errors.failWithoutHelp("Sending initial preview files without a specified device is not supported."); + } - if (this.deviceInitializationPromise[device.id]) { - return this.deviceInitializationPromise[device.id]; - } + if (this.deviceInitializationPromise[device.id]) { + return this.deviceInitializationPromise[device.id]; + } - this.deviceInitializationPromise[device.id] = this.getInitialFilesForDevice(data, device); - try { - const payloads = await this.deviceInitializationPromise[device.id]; - return payloads; - } finally { - this.deviceInitializationPromise[device.id] = null; + this.deviceInitializationPromise[device.id] = this.getInitialFilesForDevice(data, device); + try { + const payloads = await this.deviceInitializationPromise[device.id]; + return payloads; + } finally { + this.deviceInitializationPromise[device.id] = null; + } + } catch (err) { + this.$logger.error(err); + this.emit(PreviewAppLiveSyncEvents.PREVIEW_APP_LIVE_SYNC_ERROR, err); } }); } From 4db5bb39f45d607a60a082cb7d7c7fdef937ee83 Mon Sep 17 00:00:00 2001 From: fatme Date: Wed, 2 Jan 2019 17:05:09 +0200 Subject: [PATCH 03/11] chore: emit correctly PREVIEW_APP_LIVE_SYNC_ERROR --- .../playground/preview-app-livesync-service.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/services/livesync/playground/preview-app-livesync-service.ts b/lib/services/livesync/playground/preview-app-livesync-service.ts index 062562f793..4dae4f41e8 100644 --- a/lib/services/livesync/playground/preview-app-livesync-service.ts +++ b/lib/services/livesync/playground/preview-app-livesync-service.ts @@ -42,9 +42,14 @@ export class PreviewAppLiveSyncService extends EventEmitter implements IPreviewA } finally { this.deviceInitializationPromise[device.id] = null; } - } catch (err) { - this.$logger.error(err); - this.emit(PreviewAppLiveSyncEvents.PREVIEW_APP_LIVE_SYNC_ERROR, err); + } catch (error) { + this.$logger.error(error); + this.emit(PreviewAppLiveSyncEvents.PREVIEW_APP_LIVE_SYNC_ERROR, { + error, + data, + platform: device.platform, + deviceId: device.id + }); } }); } From 9ae934338e4d1be91db0464e8de953a0f91cd715 Mon Sep 17 00:00:00 2001 From: DimitarTachev Date: Wed, 19 Dec 2018 18:19:48 +0200 Subject: [PATCH 04/11] fix: log the proxy port even when returning a cached proxy instead of recreating it each time --- lib/declarations.d.ts | 3 +-- .../ios/app-debug-socket-proxy-factory.ts | 20 +++++++++++-------- lib/services/ios-device-debug-service.ts | 3 +-- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index c069049d4b..5f948e84b6 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -739,8 +739,7 @@ interface IAppDebugSocketProxyFactory extends NodeJS.EventEmitter { getTCPSocketProxy(deviceIdentifier: string, appId: string): any; addTCPSocketProxy(device: Mobile.IiOSDevice, appId: string): Promise; - getWebSocketProxy(deviceIdentifier: string, appId: string): any; - addWebSocketProxy(device: Mobile.IiOSDevice, appId: string): Promise; + ensureWebSocketProxy(device: Mobile.IiOSDevice, appId: string): Promise; removeAllProxies(): void; } diff --git a/lib/device-sockets/ios/app-debug-socket-proxy-factory.ts b/lib/device-sockets/ios/app-debug-socket-proxy-factory.ts index 7d932301f0..7e8f412728 100644 --- a/lib/device-sockets/ios/app-debug-socket-proxy-factory.ts +++ b/lib/device-sockets/ios/app-debug-socket-proxy-factory.ts @@ -20,10 +20,6 @@ export class AppDebugSocketProxyFactory extends EventEmitter implements IAppDebu return this.deviceTcpServers[`${deviceIdentifier}-${appId}`]; } - public getWebSocketProxy(deviceIdentifier: string, appId: string): ws.Server { - return this.deviceWebServers[`${deviceIdentifier}-${appId}`]; - } - public async addTCPSocketProxy(device: Mobile.IiOSDevice, appId: string): Promise { const cacheKey = `${device.deviceInfo.identifier}-${appId}`; const existingServer = this.deviceTcpServers[cacheKey]; @@ -84,7 +80,17 @@ export class AppDebugSocketProxyFactory extends EventEmitter implements IAppDebu return server; } - public async addWebSocketProxy(device: Mobile.IiOSDevice, appId: string): Promise { + public async ensureWebSocketProxy(device: Mobile.IiOSDevice, appId: string): Promise { + const existingWebProxy = this.deviceWebServers[`${device.deviceInfo.identifier}-${appId}`]; + const result = existingWebProxy || await this.addWebSocketProxy(device, appId); + + // TODO: do not remove till VSCode waits for this message in order to reattach + this.$logger.info("Opened localhost " + result.options.port); + + return result; + } + + private async addWebSocketProxy(device: Mobile.IiOSDevice, appId: string): Promise { const cacheKey = `${device.deviceInfo.identifier}-${appId}`; const existingServer = this.deviceWebServers[cacheKey]; if (existingServer) { @@ -152,12 +158,11 @@ export class AppDebugSocketProxyFactory extends EventEmitter implements IAppDebu appDebugSocket.on("close", () => { this.$logger.info("Backend socket closed!"); webSocket.close(); - server.close(); - delete this.deviceWebServers[cacheKey]; }); webSocket.on("close", () => { this.$logger.info('Frontend socket closed!'); + appDebugSocket.unpipe(packets); packets.destroy(); device.destroyDebugSocket(appId); if (!this.$options.watch) { @@ -167,7 +172,6 @@ export class AppDebugSocketProxyFactory extends EventEmitter implements IAppDebu }); - this.$logger.info("Opened localhost " + localPort); return server; } diff --git a/lib/services/ios-device-debug-service.ts b/lib/services/ios-device-debug-service.ts index 88d6b481c9..ed711ff184 100644 --- a/lib/services/ios-device-debug-service.ts +++ b/lib/services/ios-device-debug-service.ts @@ -164,8 +164,7 @@ export class IOSDeviceDebugService extends DebugServiceBase implements IDeviceDe if (debugOptions.chrome) { this.$logger.info("'--chrome' is the default behavior. Use --inspector to debug iOS applications using the Safari Web Inspector."); } - const existingWebProxy = this.$appDebugSocketProxyFactory.getWebSocketProxy(this.deviceIdentifier, debugData.applicationIdentifier); - const webSocketProxy = existingWebProxy || await this.$appDebugSocketProxyFactory.addWebSocketProxy(this.device, debugData.applicationIdentifier); + const webSocketProxy = await this.$appDebugSocketProxyFactory.ensureWebSocketProxy(this.device, debugData.applicationIdentifier); return this.getChromeDebugUrl(debugOptions, webSocketProxy.options.port); } From c8defbb27c5a623c00a9de644d32dfc6f3294e3f Mon Sep 17 00:00:00 2001 From: DimitarTachev Date: Fri, 4 Jan 2019 13:36:06 +0200 Subject: [PATCH 05/11] fix: fix stopApplication on Simulator while the app is on a breakpoint --- npm-shrinkwrap.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 57ab93eba3..fd6b5c244f 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -2932,7 +2932,7 @@ }, "string_decoder": { "version": "0.10.31", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" } } @@ -3981,9 +3981,9 @@ } }, "ios-sim-portable": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/ios-sim-portable/-/ios-sim-portable-4.0.5.tgz", - "integrity": "sha512-L/HSJDgR3roQDzKhx04Vpy+40vxTyHC+EbCAVu6OPGc3KK/axov4za2NuPib/0jAAYs83W1DnwEKR/m4Tj38VA==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ios-sim-portable/-/ios-sim-portable-4.0.6.tgz", + "integrity": "sha512-V4f5aiQDnikC/ERM+RD9Kj5gRPoIaXv8zt9Zq6hoe8amQa7PP3lY4zSzvVAp8H+Cfts6rtrAaSKLtGpVzoZRPw==", "requires": { "bplist-parser": "https://github.com/telerik/node-bplist-parser/tarball/master", "colors": "0.6.2", @@ -3996,7 +3996,7 @@ "dependencies": { "colors": { "version": "0.6.2", - "resolved": "http://registry.npmjs.org/colors/-/colors-0.6.2.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=" }, "is-fullwidth-code-point": { @@ -4029,7 +4029,7 @@ }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { "code-point-at": "^1.0.0", @@ -6502,7 +6502,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "requires": { "ret": "~0.1.10" diff --git a/package.json b/package.json index 7648f24e6a..b3c6e2f907 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "inquirer": "6.2.0", "ios-device-lib": "0.4.15", "ios-mobileprovision-finder": "1.0.10", - "ios-sim-portable": "4.0.5", + "ios-sim-portable": "4.0.6", "istextorbinary": "2.2.1", "jimp": "0.2.28", "lockfile": "1.0.3", From acc5a42feb2995dd5bf1f458576cff451dfb41e0 Mon Sep 17 00:00:00 2001 From: DimitarTachev Date: Fri, 4 Jan 2019 18:50:27 +0200 Subject: [PATCH 06/11] fix: suggest manual app start instead of crashing on ios debug socket connection errors --- .../ios/app-debug-socket-proxy-factory.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/device-sockets/ios/app-debug-socket-proxy-factory.ts b/lib/device-sockets/ios/app-debug-socket-proxy-factory.ts index 7e8f412728..18448d876a 100644 --- a/lib/device-sockets/ios/app-debug-socket-proxy-factory.ts +++ b/lib/device-sockets/ios/app-debug-socket-proxy-factory.ts @@ -110,21 +110,23 @@ export class AppDebugSocketProxyFactory extends EventEmitter implements IAppDebu const server = new ws.Server({ port: localPort, host: "localhost", - verifyClient: async (info: any, callback: Function) => { + verifyClient: async (info: any, callback: (res: boolean, code?: number, message?: string) => void) => { + let acceptHandshake = true; this.$logger.info("Frontend client connected."); let appDebugSocket; try { appDebugSocket = await device.getDebugSocket(appId); + this.$logger.info("Backend socket created."); + info.req["__deviceSocket"] = appDebugSocket; } catch (err) { err.deviceIdentifier = device.deviceInfo.identifier; this.$logger.trace(err); this.emit(CONNECTION_ERROR_EVENT_NAME, err); - this.$errors.failWithoutHelp(`Cannot connect to device socket.The error message is ${err.message} `); + acceptHandshake = false; + this.$logger.warn(`Cannot connect to device socket. The error message is '${err.message}'. Try starting the application manually.`); } - this.$logger.info("Backend socket created."); - info.req["__deviceSocket"] = appDebugSocket; - callback(true); + callback(acceptHandshake); } }); this.deviceWebServers[cacheKey] = server; From 59ccd8f485500e5b2d29de09a581f8fd082edba9 Mon Sep 17 00:00:00 2001 From: DimitarTachev Date: Fri, 4 Jan 2019 19:01:24 +0200 Subject: [PATCH 07/11] fix: require user interaction for developer disk image only in debug --- lib/definitions/livesync.d.ts | 6 ++---- lib/services/livesync/livesync-service.ts | 8 +++++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/definitions/livesync.d.ts b/lib/definitions/livesync.d.ts index 0d2ad574e8..a4274c147a 100644 --- a/lib/definitions/livesync.d.ts +++ b/lib/definitions/livesync.d.ts @@ -330,11 +330,9 @@ interface IEnableDebuggingDeviceOptions extends Mobile.IDeviceIdentifier, IOptio /** * Describes settings passed to livesync service in order to control event emitting during refresh application. */ -interface IShouldSkipEmitLiveSyncNotification { - /** - * Whether to skip emitting an event during refresh. Default is false. - */ +interface IRefreshApplicationSettings { shouldSkipEmitLiveSyncNotification: boolean; + shouldCheckDeveloperDiscImage: boolean; } /** diff --git a/lib/services/livesync/livesync-service.ts b/lib/services/livesync/livesync-service.ts index aa2acd793d..fd8dc63993 100644 --- a/lib/services/livesync/livesync-service.ts +++ b/lib/services/livesync/livesync-service.ts @@ -164,7 +164,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi this.refreshApplicationWithoutDebug(projectData, liveSyncResultInfo, debugOpts, outputPath); } - private async refreshApplicationWithoutDebug(projectData: IProjectData, liveSyncResultInfo: ILiveSyncResultInfo, debugOpts?: IDebugOptions, outputPath?: string, settings?: IShouldSkipEmitLiveSyncNotification): Promise { + private async refreshApplicationWithoutDebug(projectData: IProjectData, liveSyncResultInfo: ILiveSyncResultInfo, debugOpts?: IDebugOptions, outputPath?: string, settings?: IRefreshApplicationSettings): Promise { let result: IRefreshApplicationInfo = { didRestart: false }; const platform = liveSyncResultInfo.deviceAppData.platform; const platformLiveSyncService = this.getLiveSyncService(platform); @@ -184,7 +184,9 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi }); } - this.handleDeveloperDiskImageError(err, liveSyncResultInfo, projectData, debugOpts, outputPath); + if (settings && settings.shouldCheckDeveloperDiscImage) { + this.handleDeveloperDiskImageError(err, liveSyncResultInfo, projectData, debugOpts, outputPath); + } } this.emitLivesyncEvent(LiveSyncEvents.liveSyncExecuted, { @@ -214,7 +216,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi this.handleDeveloperDiskImageError(err, liveSyncResultInfo, projectData, debugOptions, outputPath); } } else { - const refreshInfo = await this.refreshApplicationWithoutDebug(projectData, liveSyncResultInfo, debugOptions, outputPath, { shouldSkipEmitLiveSyncNotification: true }); + const refreshInfo = await this.refreshApplicationWithoutDebug(projectData, liveSyncResultInfo, debugOptions, outputPath, { shouldSkipEmitLiveSyncNotification: true, shouldCheckDeveloperDiscImage: true }); didRestart = refreshInfo.didRestart; } From d1517e3ae61c399adfaa3e68623f6b9f4981e512 Mon Sep 17 00:00:00 2001 From: fatme Date: Mon, 7 Jan 2019 10:49:49 +0200 Subject: [PATCH 08/11] fix(preview-publicApi): Don't persist the plugin's warnings when compare plugins --- .../playground/preview-app-plugins-service.ts | 27 +++++----- .../playground/preview-app-plugins-service.ts | 49 ------------------- 2 files changed, 11 insertions(+), 65 deletions(-) diff --git a/lib/services/livesync/playground/preview-app-plugins-service.ts b/lib/services/livesync/playground/preview-app-plugins-service.ts index 3c8c80a178..1c52a6ae48 100644 --- a/lib/services/livesync/playground/preview-app-plugins-service.ts +++ b/lib/services/livesync/playground/preview-app-plugins-service.ts @@ -7,8 +7,6 @@ import { NODE_MODULES_DIR_NAME } from "../../../common/constants"; import { PLATFORMS_DIR_NAME, PACKAGE_JSON_FILE_NAME } from "../../../constants"; export class PreviewAppPluginsService implements IPreviewAppPluginsService { - private previewAppVersionWarnings: IDictionary = {}; - constructor(private $errors: IErrors, private $fs: IFileSystem, private $logger: ILogger, @@ -23,20 +21,17 @@ export class PreviewAppPluginsService implements IPreviewAppPluginsService { this.$errors.failWithoutHelp("No version of preview app provided."); } - if (!this.previewAppVersionWarnings[device.previewAppVersion]) { - const devicePlugins = this.getDevicePlugins(device); - const localPlugins = this.getLocalPlugins(data.projectDir); - const warnings = _.keys(localPlugins) - .map(localPlugin => { - const localPluginVersion = localPlugins[localPlugin]; - const devicePluginVersion = devicePlugins[localPlugin]; - return this.getWarningForPlugin(data, localPlugin, localPluginVersion, devicePluginVersion, device); - }) - .filter(item => !!item); - this.previewAppVersionWarnings[device.previewAppVersion] = warnings; - } - - return this.previewAppVersionWarnings[device.previewAppVersion]; + const devicePlugins = this.getDevicePlugins(device); + const localPlugins = this.getLocalPlugins(data.projectDir); + const warnings = _.keys(localPlugins) + .map(localPlugin => { + const localPluginVersion = localPlugins[localPlugin]; + const devicePluginVersion = devicePlugins[localPlugin]; + return this.getWarningForPlugin(data, localPlugin, localPluginVersion, devicePluginVersion, device); + }) + .filter(item => !!item); + + return warnings; } public async comparePluginsOnDevice(data: IPreviewAppLiveSyncData, device: Device): Promise { diff --git a/test/services/playground/preview-app-plugins-service.ts b/test/services/playground/preview-app-plugins-service.ts index 277b5be34b..776f72d5d3 100644 --- a/test/services/playground/preview-app-plugins-service.ts +++ b/test/services/playground/preview-app-plugins-service.ts @@ -86,55 +86,6 @@ function setup(localPlugins: IStringDictionary, previewAppPlugins: IStringDictio describe("previewAppPluginsService", () => { describe("comparePluginsOnDevice without bundle", () => { - it("should persist warnings per preview app's version", async () => { - const localPlugins = { - "nativescript-facebook": "2.2.3", - "nativescript-theme-core": "1.0.4", - "tns-core-modules": "4.2.0" - }; - const previewAppPlugins = { - "nativescript-theme-core": "2.0.4", - "tns-core-modules": "4.2.0" - }; - const injector = createTestInjector(localPlugins); - const previewAppPluginsService = injector.resolve("previewAppPluginsService"); - - let isGetDevicePluginsCalled = false; - const originalGetDevicePlugins = (previewAppPluginsService).getDevicePlugins; - (previewAppPluginsService).getDevicePlugins = (device: Device) => { - isGetDevicePluginsCalled = true; - return originalGetDevicePlugins(device); - }; - let isGetLocalPluginsCalled = false; - const originalGetLocalPlugins = (previewAppPluginsService).getLocalPlugins; - (previewAppPluginsService).getLocalPlugins = () => { - isGetLocalPluginsCalled = true; - return originalGetLocalPlugins.apply(previewAppPluginsService, [projectDir]); - }; - - const previewLiveSyncData = createPreviewLiveSyncData({ bundle: false }); - - await previewAppPluginsService.comparePluginsOnDevice(previewLiveSyncData, createDevice(JSON.stringify(previewAppPlugins))); - - const expectedWarnings = [ - util.format(PluginComparisonMessages.PLUGIN_NOT_INCLUDED_IN_PREVIEW_APP, "nativescript-facebook", deviceId), - util.format(PluginComparisonMessages.LOCAL_PLUGIN_WITH_DIFFERENCE_IN_MAJOR_VERSION, "nativescript-theme-core", "1.0.4", "2.0.4") - ]; - assert.isTrue(isGetDevicePluginsCalled); - assert.isTrue(isGetLocalPluginsCalled); - assert.deepEqual(warnParams, expectedWarnings); - - isGetDevicePluginsCalled = false; - isGetLocalPluginsCalled = false; - warnParams = []; - - await previewAppPluginsService.comparePluginsOnDevice(previewLiveSyncData, createDevice(JSON.stringify(previewAppPlugins))); - - assert.isFalse(isGetDevicePluginsCalled); - assert.isFalse(isGetLocalPluginsCalled); - assert.deepEqual(warnParams, expectedWarnings); - }); - const testCases = [ { name: "should show warning for plugin not included in preview app", From f7a4437604ed695138dd2575dea1e1a747700165 Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Mon, 7 Jan 2019 18:19:08 +0200 Subject: [PATCH 09/11] fix: do not show error from preview initialization when stop is called When preview is called with webpack from Public API, if you stop the livesync (via stopLiveSync method) while webpack is still working, in the output you'll see an error as webpack's child process exits with `null` exit code. However, this error does not have any meaning for the user, so we shouldn't raise it. Move the logging of the error to LiveSyncService's handler of `previewAppLiveSyncError` event - in the described case it will not be raised as LiveSyncService had already removed all listeners. --- lib/services/livesync/livesync-service.ts | 1 + .../livesync/playground/preview-app-livesync-service.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/services/livesync/livesync-service.ts b/lib/services/livesync/livesync-service.ts index fd8dc63993..54df97043e 100644 --- a/lib/services/livesync/livesync-service.ts +++ b/lib/services/livesync/livesync-service.ts @@ -140,6 +140,7 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi @cache() private attachToPreviewAppLiveSyncError(): void { this.$previewAppLiveSyncService.on(LiveSyncEvents.previewAppLiveSyncError, liveSyncData => { + this.$logger.error(liveSyncData.error); this.emit(LiveSyncEvents.previewAppLiveSyncError, liveSyncData); }); } diff --git a/lib/services/livesync/playground/preview-app-livesync-service.ts b/lib/services/livesync/playground/preview-app-livesync-service.ts index 4dae4f41e8..2883dd1ccf 100644 --- a/lib/services/livesync/playground/preview-app-livesync-service.ts +++ b/lib/services/livesync/playground/preview-app-livesync-service.ts @@ -43,7 +43,7 @@ export class PreviewAppLiveSyncService extends EventEmitter implements IPreviewA this.deviceInitializationPromise[device.id] = null; } } catch (error) { - this.$logger.error(error); + this.$logger.trace(`Error while sending files on device ${device && device.id}. Error is`, error); this.emit(PreviewAppLiveSyncEvents.PREVIEW_APP_LIVE_SYNC_ERROR, { error, data, From 7c07d2ee7dd2b8feb251a42d0fc263be9777ce18 Mon Sep 17 00:00:00 2001 From: Rosen Vladimirov Date: Mon, 7 Jan 2019 21:52:07 +0200 Subject: [PATCH 10/11] fix: handle `previewAppLiveSyncError` event in CLI Currently the `previewAppLiveSyncError` event is handled only when CLI is used as a library. Add handler when CLI is used from command line and use the same workflow in both cases. Attach to the event every time when a new operation is started. Currently, once LiveSync is stopped, we've removed all handlers and we were never attaching them again. Now we rely on the isInitialized property, so we'll attach the event handler whenever it is needed. --- lib/commands/preview.ts | 14 ++++---------- lib/definitions/livesync.d.ts | 7 +++++++ lib/services/livesync/livesync-service.ts | 11 ++++++----- test/stubs.ts | 4 ++++ 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/lib/commands/preview.ts b/lib/commands/preview.ts index f3bf388a30..7d8a4af6ec 100644 --- a/lib/commands/preview.ts +++ b/lib/commands/preview.ts @@ -19,17 +19,11 @@ export class PreviewCommand implements ICommand { this.$logger.info(message); }); - await this.$liveSyncService.liveSync([], { - syncToPreviewApp: true, - projectDir: this.$projectData.projectDir, - skipWatcher: !this.$options.watch, - watchAllFiles: this.$options.syncAllFiles, - clean: this.$options.clean, + await this.$liveSyncService.liveSyncToPreviewApp({ bundle: !!this.$options.bundle, - release: this.$options.release, - env: this.$options.env, - timeout: this.$options.timeout, - useHotModuleReload: this.$options.hmr + useHotModuleReload: this.$options.hmr, + projectDir: this.$projectData.projectDir, + env: this.$options.env }); await this.$previewQrCodeService.printLiveSyncQrCode({ useHotModuleReload: this.$options.hmr, link: this.$options.link }); diff --git a/lib/definitions/livesync.d.ts b/lib/definitions/livesync.d.ts index a4274c147a..212474a508 100644 --- a/lib/definitions/livesync.d.ts +++ b/lib/definitions/livesync.d.ts @@ -242,6 +242,13 @@ interface ILiveSyncService { */ liveSync(deviceDescriptors: ILiveSyncDeviceInfo[], liveSyncData: ILiveSyncInfo): Promise; + /** + * Starts LiveSync operation to Preview app. + * @param {IPreviewAppLiveSyncData} data Describes information about the current operation. + * @returns {Promise} Data of the QR code that should be used to start the LiveSync operation. + */ + liveSyncToPreviewApp(data: IPreviewAppLiveSyncData): Promise; + /** * Stops LiveSync operation for specified directory. * @param {string} projectDir The directory for which to stop the operation. diff --git a/lib/services/livesync/livesync-service.ts b/lib/services/livesync/livesync-service.ts index 54df97043e..38e3b8603d 100644 --- a/lib/services/livesync/livesync-service.ts +++ b/lib/services/livesync/livesync-service.ts @@ -137,12 +137,13 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi return currentDescriptors || []; } - @cache() private attachToPreviewAppLiveSyncError(): void { - this.$previewAppLiveSyncService.on(LiveSyncEvents.previewAppLiveSyncError, liveSyncData => { - this.$logger.error(liveSyncData.error); - this.emit(LiveSyncEvents.previewAppLiveSyncError, liveSyncData); - }); + if (!this.$usbLiveSyncService.isInitialized) { + this.$previewAppLiveSyncService.on(LiveSyncEvents.previewAppLiveSyncError, liveSyncData => { + this.$logger.error(liveSyncData.error); + this.emit(LiveSyncEvents.previewAppLiveSyncError, liveSyncData); + }); + } } private handleWarnings(liveSyncData: ILiveSyncInfo, projectData: IProjectData) { diff --git a/test/stubs.ts b/test/stubs.ts index 28a0ce35f8..b06e61e7b8 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -661,6 +661,10 @@ export class DebugServiceStub extends EventEmitter implements IDeviceDebugServic } export class LiveSyncServiceStub implements ILiveSyncService { + public async liveSyncToPreviewApp(data: IPreviewAppLiveSyncData): Promise { + return; + } + public async liveSync(deviceDescriptors: ILiveSyncDeviceInfo[], liveSyncData: ILiveSyncInfo): Promise { return; } From b87d619f957378d87107d2b2d8689c2addabf782 Mon Sep 17 00:00:00 2001 From: Kristian Dimitrov Date: Tue, 8 Jan 2019 16:48:28 +0200 Subject: [PATCH 11/11] fix: fix splashscreen generation --- resources/assets/image-definitions.json | 56 +++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/resources/assets/image-definitions.json b/resources/assets/image-definitions.json index 02289c926a..0e84684321 100644 --- a/resources/assets/image-definitions.json +++ b/resources/assets/image-definitions.json @@ -166,6 +166,14 @@ "filename": "LaunchScreen-AspectFill@3x.png", "resizeOperation": "blank", "scale": "3x" + }, + { + "width": 768, + "height": 1024, + "directory": "Assets.xcassets/LaunchScreen.AspectFill.imageset", + "filename": "LaunchScree.AspectFill@3x.png", + "resizeOperation": "blank", + "scale": "3x" } ], "splashCenterImages": [ @@ -184,6 +192,22 @@ "filename": "LaunchScreen-Center@2x.png", "resizeOperation": "overlayWith", "scale": "2x" + }, + { + "width": 384, + "height": 512, + "directory": "Assets.xcassets/LaunchScreen.Center.imageset", + "filename": "LaunchScreen-Center@3x.png", + "resizeOperation": "overlayWith", + "scale": "3x" + }, + { + "width": 384, + "height": 512, + "directory": "Assets.xcassets/LaunchScreen.Center.imageset", + "filename": "LaunchScreen.Center@3x.png", + "resizeOperation": "overlayWith", + "scale": "3x" } ], "splashImages": [ @@ -290,6 +314,38 @@ "filename": "Default-Landscape-X.png", "resizeOperation": "overlayWith", "scale": "1x" + }, + { + "width": 896, + "height": 414, + "directory": "Assets.xcassets/LaunchImage.launchimage", + "filename": "iPhone XR - Landscape iOS 12.png", + "resizeOperation": "overlayWith", + "scale": "2x" + }, + { + "width": 414, + "height": 896, + "directory": "Assets.xcassets/LaunchImage.launchimage", + "filename": "iPhone XR - Portarit iOS 12.png", + "resizeOperation": "overlayWith", + "scale": "2x" + }, + { + "width": 896, + "height": 414, + "directory": "Assets.xcassets/LaunchImage.launchimage", + "filename": "iPhone XS Max – Landscape iOS 12.png", + "resizeOperation": "overlayWith", + "scale": "3x" + }, + { + "width": 414, + "height": 896, + "directory": "Assets.xcassets/LaunchImage.launchimage", + "filename": "Phone XS Max - Portarit iOS 12.png", + "resizeOperation": "overlayWith", + "scale": "3x" } ] },