diff --git a/lib/android-tools-info.ts b/lib/android-tools-info.ts index 75f357d47a..8c3102669e 100644 --- a/lib/android-tools-info.ts +++ b/lib/android-tools-info.ts @@ -1,6 +1,6 @@ import * as path from "path"; import * as semver from "semver"; -import {EOL} from "os"; +import { EOL } from "os"; export class AndroidToolsInfo implements IAndroidToolsInfo { private static ANDROID_TARGET_PREFIX = "android"; @@ -37,165 +37,153 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { private $adb: Mobile.IAndroidDebugBridge, protected $staticConfig: Config.IStaticConfig) { } - public getPathToAndroidExecutable(options?: { showWarningsAsErrors: boolean }): IFuture { - return ((): string => { - if (options) { - this.showWarningsAsErrors = options.showWarningsAsErrors; - } - if (!this.pathToAndroidExecutable) { - if (this.validateAndroidHomeEnvVariable(this.androidHome)) { - let androidPath = path.join(this.androidHome, "tools", this.androidExecutableName); - if (!this.trySetAndroidPath(androidPath).wait() && !this.trySetAndroidPath(this.androidExecutableName).wait()) { - this.printMessage(`Unable to find "${this.androidExecutableName}" executable file. Make sure you have set ANDROID_HOME environment variable correctly.`); - } - } else { - this.$logger.trace("ANDROID_HOME environment variable is not set correctly."); + public async getPathToAndroidExecutable(options?: { showWarningsAsErrors: boolean }): Promise { + if (options) { + this.showWarningsAsErrors = options.showWarningsAsErrors; + } + if (!this.pathToAndroidExecutable) { + if (this.validateAndroidHomeEnvVariable(this.androidHome)) { + let androidPath = path.join(this.androidHome, "tools", this.androidExecutableName); + if (!await this.trySetAndroidPath(androidPath) && !await this.trySetAndroidPath(this.androidExecutableName)) { + this.printMessage(`Unable to find "${this.androidExecutableName}" executable file. Make sure you have set ANDROID_HOME environment variable correctly.`); } + } else { + this.$logger.trace("ANDROID_HOME environment variable is not set correctly."); } + } - return this.pathToAndroidExecutable; - }).future()(); + return this.pathToAndroidExecutable; } - private trySetAndroidPath(androidPath: string): IFuture { - return ((): boolean => { - let isAndroidPathCorrect = true; - try { - let result = this.$adb.executeCommand(["--help"], { returnChildProcess: true }).wait(); - if (result && result.stdout) { - this.$logger.trace(result.stdout); - this.pathToAndroidExecutable = androidPath; - } else { - this.$logger.trace(`Unable to find android executable from '${androidPath}'.`); - isAndroidPathCorrect = false; - } - } catch (err) { - this.$logger.trace(`Error occurred while checking androidExecutable from '${androidPath}'. ${err.message}`); + private async trySetAndroidPath(androidPath: string): Promise { + let isAndroidPathCorrect = true; + try { + let result = await this.$adb.executeCommand(["--help"], { returnChildProcess: true }); + if (result && result.stdout) { + this.$logger.trace(result.stdout); + this.pathToAndroidExecutable = androidPath; + } else { + this.$logger.trace(`Unable to find android executable from '${androidPath}'.`); isAndroidPathCorrect = false; } + } catch (err) { + this.$logger.trace(`Error occurred while checking androidExecutable from '${androidPath}'. ${err.message}`); + isAndroidPathCorrect = false; + } - return isAndroidPathCorrect; - }).future()(); + return isAndroidPathCorrect; } - public getToolsInfo(): IFuture { - return ((): IAndroidToolsInfoData => { - if (!this.toolsInfo) { - let infoData: IAndroidToolsInfoData = Object.create(null); - infoData.androidHomeEnvVar = this.androidHome; - infoData.compileSdkVersion = this.getCompileSdk().wait(); - infoData.buildToolsVersion = this.getBuildToolsVersion().wait(); - infoData.targetSdkVersion = this.getTargetSdk().wait(); - infoData.supportRepositoryVersion = this.getAndroidSupportRepositoryVersion().wait(); - infoData.generateTypings = this.shouldGenerateTypings(); - - this.toolsInfo = infoData; - } + public async getToolsInfo(): Promise { + if (!this.toolsInfo) { + let infoData: IAndroidToolsInfoData = Object.create(null); + infoData.androidHomeEnvVar = this.androidHome; + infoData.compileSdkVersion = await this.getCompileSdk(); + infoData.buildToolsVersion = await this.getBuildToolsVersion(); + infoData.targetSdkVersion = await this.getTargetSdk(); + infoData.supportRepositoryVersion = await this.getAndroidSupportRepositoryVersion(); + infoData.generateTypings = this.shouldGenerateTypings(); + + this.toolsInfo = infoData; + } - return this.toolsInfo; - }).future()(); + return this.toolsInfo; } - public validateInfo(options?: { showWarningsAsErrors: boolean, validateTargetSdk: boolean }): IFuture { - return ((): boolean => { - let detectedErrors = false; - this.showWarningsAsErrors = options && options.showWarningsAsErrors; - let toolsInfoData = this.getToolsInfo().wait(); - let isAndroidHomeValid = this.validateAndroidHomeEnvVariable(toolsInfoData.androidHomeEnvVar); - if (!toolsInfoData.compileSdkVersion) { - this.printMessage(`Cannot find a compatible Android SDK for compilation. To be able to build for Android, install Android SDK ${AndroidToolsInfo.MIN_REQUIRED_COMPILE_TARGET} or later.`, - "Run `$ android` to manage your Android SDK versions."); - detectedErrors = true; - } - - if (!toolsInfoData.buildToolsVersion) { - let buildToolsRange = this.getBuildToolsRange(); - let versionRangeMatches = buildToolsRange.match(/^.*?([\d\.]+)\s+.*?([\d\.]+)$/); - let message = `You can install any version in the following range: '${buildToolsRange}'.`; + public async validateInfo(options?: { showWarningsAsErrors: boolean, validateTargetSdk: boolean }): Promise { + let detectedErrors = false; + this.showWarningsAsErrors = options && options.showWarningsAsErrors; + let toolsInfoData = await this.getToolsInfo(); + let isAndroidHomeValid = this.validateAndroidHomeEnvVariable(toolsInfoData.androidHomeEnvVar); + if (!toolsInfoData.compileSdkVersion) { + this.printMessage(`Cannot find a compatible Android SDK for compilation. To be able to build for Android, install Android SDK ${AndroidToolsInfo.MIN_REQUIRED_COMPILE_TARGET} or later.`, + "Run `$ android` to manage your Android SDK versions."); + detectedErrors = true; + } - // Improve message in case buildToolsRange is something like: ">=22.0.0 <=22.0.0" - same numbers on both sides - if (versionRangeMatches && versionRangeMatches[1] && versionRangeMatches[2] && versionRangeMatches[1] === versionRangeMatches[2]) { - message = `You have to install version ${versionRangeMatches[1]}.`; - } + if (!toolsInfoData.buildToolsVersion) { + let buildToolsRange = this.getBuildToolsRange(); + let versionRangeMatches = buildToolsRange.match(/^.*?([\d\.]+)\s+.*?([\d\.]+)$/); + let message = `You can install any version in the following range: '${buildToolsRange}'.`; - let invalidBuildToolsAdditionalMsg = 'Run `android` from your command-line to install required `Android Build Tools`.'; - if (!isAndroidHomeValid) { - invalidBuildToolsAdditionalMsg += ' In case you already have them installed, make sure `ANDROID_HOME` environment variable is set correctly.'; - } + // Improve message in case buildToolsRange is something like: ">=22.0.0 <=22.0.0" - same numbers on both sides + if (versionRangeMatches && versionRangeMatches[1] && versionRangeMatches[2] && versionRangeMatches[1] === versionRangeMatches[2]) { + message = `You have to install version ${versionRangeMatches[1]}.`; + } - this.printMessage("You need to have the Android SDK Build-tools installed on your system. " + message, invalidBuildToolsAdditionalMsg); - detectedErrors = true; + let invalidBuildToolsAdditionalMsg = 'Run `android` from your command-line to install required `Android Build Tools`.'; + if (!isAndroidHomeValid) { + invalidBuildToolsAdditionalMsg += ' In case you already have them installed, make sure `ANDROID_HOME` environment variable is set correctly.'; } - if (!toolsInfoData.supportRepositoryVersion) { - let invalidSupportLibAdditionalMsg = 'Run `$ android` to manage the Android Support Repository.'; - if (!isAndroidHomeValid) { - invalidSupportLibAdditionalMsg += ' In case you already have it installed, make sure `ANDROID_HOME` environment variable is set correctly.'; - } - this.printMessage(`You need to have Android SDK ${AndroidToolsInfo.MIN_REQUIRED_COMPILE_TARGET} or later and the latest Android Support Repository installed on your system.`, invalidSupportLibAdditionalMsg); - detectedErrors = true; + this.printMessage("You need to have the Android SDK Build-tools installed on your system. " + message, invalidBuildToolsAdditionalMsg); + detectedErrors = true; + } + + if (!toolsInfoData.supportRepositoryVersion) { + let invalidSupportLibAdditionalMsg = 'Run `$ android` to manage the Android Support Repository.'; + if (!isAndroidHomeValid) { + invalidSupportLibAdditionalMsg += ' In case you already have it installed, make sure `ANDROID_HOME` environment variable is set correctly.'; } + this.printMessage(`You need to have Android SDK ${AndroidToolsInfo.MIN_REQUIRED_COMPILE_TARGET} or later and the latest Android Support Repository installed on your system.`, invalidSupportLibAdditionalMsg); + detectedErrors = true; + } - if (options && options.validateTargetSdk) { - let targetSdk = toolsInfoData.targetSdkVersion; - let newTarget = `${AndroidToolsInfo.ANDROID_TARGET_PREFIX}-${targetSdk}`; - if (!_.includes(AndroidToolsInfo.SUPPORTED_TARGETS, newTarget)) { - let supportedVersions = AndroidToolsInfo.SUPPORTED_TARGETS.sort(); - let minSupportedVersion = this.parseAndroidSdkString(_.first(supportedVersions)); - - if (targetSdk && (targetSdk < minSupportedVersion)) { - this.printMessage(`The selected Android target SDK ${newTarget} is not supported. You must target ${minSupportedVersion} or later.`); - detectedErrors = true; - } else if (!targetSdk || targetSdk > this.getMaxSupportedVersion()) { - this.$logger.warn(`Support for the selected Android target SDK ${newTarget} is not verified. Your Android app might not work as expected.`); - } + if (options && options.validateTargetSdk) { + let targetSdk = toolsInfoData.targetSdkVersion; + let newTarget = `${AndroidToolsInfo.ANDROID_TARGET_PREFIX}-${targetSdk}`; + if (!_.includes(AndroidToolsInfo.SUPPORTED_TARGETS, newTarget)) { + let supportedVersions = AndroidToolsInfo.SUPPORTED_TARGETS.sort(); + let minSupportedVersion = this.parseAndroidSdkString(_.first(supportedVersions)); + + if (targetSdk && (targetSdk < minSupportedVersion)) { + this.printMessage(`The selected Android target SDK ${newTarget} is not supported. You must target ${minSupportedVersion} or later.`); + detectedErrors = true; + } else if (!targetSdk || targetSdk > this.getMaxSupportedVersion()) { + this.$logger.warn(`Support for the selected Android target SDK ${newTarget} is not verified. Your Android app might not work as expected.`); } } + } - return detectedErrors || !isAndroidHomeValid; - }).future()(); + return detectedErrors || !isAndroidHomeValid; } - public validateJavacVersion(installedJavaVersion: string, options?: { showWarningsAsErrors: boolean }): IFuture { - return ((): boolean => { - let hasProblemWithJavaVersion = false; - if (options) { - this.showWarningsAsErrors = options.showWarningsAsErrors; - } - let additionalMessage = "You will not be able to build your projects for Android." + EOL - + "To be able to build for Android, verify that you have installed The Java Development Kit (JDK) and configured it according to system requirements as" + EOL + - " described in " + this.$staticConfig.SYS_REQUIREMENTS_LINK; - let matchingVersion = (installedJavaVersion || "").match(AndroidToolsInfo.VERSION_REGEX); - if (matchingVersion && matchingVersion[1]) { - if (semver.lt(matchingVersion[1], AndroidToolsInfo.MIN_JAVA_VERSION)) { - hasProblemWithJavaVersion = true; - this.printMessage(`Javac version ${installedJavaVersion} is not supported. You have to install at least ${AndroidToolsInfo.MIN_JAVA_VERSION}.`, additionalMessage); - } - } else { + public async validateJavacVersion(installedJavaVersion: string, options?: { showWarningsAsErrors: boolean }): Promise { + let hasProblemWithJavaVersion = false; + if (options) { + this.showWarningsAsErrors = options.showWarningsAsErrors; + } + let additionalMessage = "You will not be able to build your projects for Android." + EOL + + "To be able to build for Android, verify that you have installed The Java Development Kit (JDK) and configured it according to system requirements as" + EOL + + " described in " + this.$staticConfig.SYS_REQUIREMENTS_LINK; + let matchingVersion = (installedJavaVersion || "").match(AndroidToolsInfo.VERSION_REGEX); + if (matchingVersion && matchingVersion[1]) { + if (semver.lt(matchingVersion[1], AndroidToolsInfo.MIN_JAVA_VERSION)) { hasProblemWithJavaVersion = true; - this.printMessage("Error executing command 'javac'. Make sure you have installed The Java Development Kit (JDK) and set JAVA_HOME environment variable.", additionalMessage); + this.printMessage(`Javac version ${installedJavaVersion} is not supported. You have to install at least ${AndroidToolsInfo.MIN_JAVA_VERSION}.`, additionalMessage); } + } else { + hasProblemWithJavaVersion = true; + this.printMessage("Error executing command 'javac'. Make sure you have installed The Java Development Kit (JDK) and set JAVA_HOME environment variable.", additionalMessage); + } - return hasProblemWithJavaVersion; - }).future()(); + return hasProblemWithJavaVersion; } - public getPathToAdbFromAndroidHome(): IFuture { - return (() => { - if (this.androidHome) { - let pathToAdb = path.join(this.androidHome, "platform-tools", "adb"); - try { - this.$childProcess.execFile(pathToAdb, ["help"]).wait(); - return pathToAdb; - } catch (err) { - // adb does not exist, so ANDROID_HOME is not set correctly - // try getting default adb path (included in CLI package) - this.$logger.trace(`Error while executing '${pathToAdb} help'. Error is: ${err.message}`); - } + public async getPathToAdbFromAndroidHome(): Promise { + if (this.androidHome) { + let pathToAdb = path.join(this.androidHome, "platform-tools", "adb"); + try { + await this.$childProcess.execFile(pathToAdb, ["help"]); + return pathToAdb; + } catch (err) { + // adb does not exist, so ANDROID_HOME is not set correctly + // try getting default adb path (included in CLI package) + this.$logger.trace(`Error while executing '${pathToAdb} help'. Error is: ${err.message}`); } + } - return null; - }).future()(); + return null; } private shouldGenerateTypings(): boolean { @@ -223,40 +211,36 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { } } - private getCompileSdk(): IFuture { - return ((): number => { - if (!this.selectedCompileSdk) { - let userSpecifiedCompileSdk = this.$options.compileSdk; - if (userSpecifiedCompileSdk) { - let installedTargets = this.getInstalledTargets().wait(); - let androidCompileSdk = `${AndroidToolsInfo.ANDROID_TARGET_PREFIX}-${userSpecifiedCompileSdk}`; - if (!_.includes(installedTargets, androidCompileSdk)) { - this.$errors.failWithoutHelp(`You have specified '${userSpecifiedCompileSdk}' for compile sdk, but it is not installed on your system.`); - } + private async getCompileSdk(): Promise { + if (!this.selectedCompileSdk) { + let userSpecifiedCompileSdk = this.$options.compileSdk; + if (userSpecifiedCompileSdk) { + let installedTargets = await this.getInstalledTargets(); + let androidCompileSdk = `${AndroidToolsInfo.ANDROID_TARGET_PREFIX}-${userSpecifiedCompileSdk}`; + if (!_.includes(installedTargets, androidCompileSdk)) { + this.$errors.failWithoutHelp(`You have specified '${userSpecifiedCompileSdk}' for compile sdk, but it is not installed on your system.`); + } - this.selectedCompileSdk = userSpecifiedCompileSdk; - } else { - let latestValidAndroidTarget = this.getLatestValidAndroidTarget().wait(); - if (latestValidAndroidTarget) { - let integerVersion = this.parseAndroidSdkString(latestValidAndroidTarget); + this.selectedCompileSdk = userSpecifiedCompileSdk; + } else { + let latestValidAndroidTarget = await this.getLatestValidAndroidTarget(); + if (latestValidAndroidTarget) { + let integerVersion = this.parseAndroidSdkString(latestValidAndroidTarget); - if (integerVersion && integerVersion >= AndroidToolsInfo.MIN_REQUIRED_COMPILE_TARGET) { - this.selectedCompileSdk = integerVersion; - } + if (integerVersion && integerVersion >= AndroidToolsInfo.MIN_REQUIRED_COMPILE_TARGET) { + this.selectedCompileSdk = integerVersion; } } } + } - return this.selectedCompileSdk; - }).future()(); + return this.selectedCompileSdk; } - private getTargetSdk(): IFuture { - return ((): number => { - let targetSdk = this.$options.sdk ? parseInt(this.$options.sdk) : this.getCompileSdk().wait(); - this.$logger.trace(`Selected targetSdk is: ${targetSdk}`); - return targetSdk; - }).future()(); + private async getTargetSdk(): Promise { + let targetSdk = this.$options.sdk ? parseInt(this.$options.sdk) : await this.getCompileSdk(); + this.$logger.trace(`Selected targetSdk is: ${targetSdk}`); + return targetSdk; } private getMatchingDir(pathToDir: string, versionRange: string): string { @@ -289,75 +273,65 @@ export class AndroidToolsInfo implements IAndroidToolsInfo { return `${AndroidToolsInfo.REQUIRED_BUILD_TOOLS_RANGE_PREFIX} <=${this.getMaxSupportedVersion()}`; } - private getBuildToolsVersion(): IFuture { - return ((): string => { - let buildToolsVersion: string; - if (this.androidHome) { - let pathToBuildTools = path.join(this.androidHome, "build-tools"); - let buildToolsRange = this.getBuildToolsRange(); - buildToolsVersion = this.getMatchingDir(pathToBuildTools, buildToolsRange); - } + private async getBuildToolsVersion(): Promise { + let buildToolsVersion: string; + if (this.androidHome) { + let pathToBuildTools = path.join(this.androidHome, "build-tools"); + let buildToolsRange = this.getBuildToolsRange(); + buildToolsVersion = this.getMatchingDir(pathToBuildTools, buildToolsRange); + } - return buildToolsVersion; - }).future()(); + return buildToolsVersion; } - private getAppCompatRange(): IFuture { - return ((): string => { - let compileSdkVersion = this.getCompileSdk().wait(); - let requiredAppCompatRange: string; - if (compileSdkVersion) { - requiredAppCompatRange = `>=${compileSdkVersion} <${compileSdkVersion + 1}`; - } + private async getAppCompatRange(): Promise { + let compileSdkVersion = await this.getCompileSdk(); + let requiredAppCompatRange: string; + if (compileSdkVersion) { + requiredAppCompatRange = `>=${compileSdkVersion} <${compileSdkVersion + 1}`; + } - return requiredAppCompatRange; - }).future()(); + return requiredAppCompatRange; } - private getAndroidSupportRepositoryVersion(): IFuture { - return ((): string => { - let selectedAppCompatVersion: string; - let requiredAppCompatRange = this.getAppCompatRange().wait(); - if (this.androidHome && requiredAppCompatRange) { - let pathToAppCompat = path.join(this.androidHome, "extras", "android", "m2repository", "com", "android", "support", "appcompat-v7"); - selectedAppCompatVersion = this.getMatchingDir(pathToAppCompat, requiredAppCompatRange); - } + private async getAndroidSupportRepositoryVersion(): Promise { + let selectedAppCompatVersion: string; + let requiredAppCompatRange = await this.getAppCompatRange(); + if (this.androidHome && requiredAppCompatRange) { + let pathToAppCompat = path.join(this.androidHome, "extras", "android", "m2repository", "com", "android", "support", "appcompat-v7"); + selectedAppCompatVersion = this.getMatchingDir(pathToAppCompat, requiredAppCompatRange); + } - this.$logger.trace(`Selected AppCompat version is: ${selectedAppCompatVersion}`); - return selectedAppCompatVersion; - }).future()(); + this.$logger.trace(`Selected AppCompat version is: ${selectedAppCompatVersion}`); + return selectedAppCompatVersion; } - private getLatestValidAndroidTarget(): IFuture { - return (() => { - let installedTargets = this.getInstalledTargets().wait(); - return _.findLast(AndroidToolsInfo.SUPPORTED_TARGETS.sort(), supportedTarget => _.includes(installedTargets, supportedTarget)); - }).future()(); + private async getLatestValidAndroidTarget(): Promise { + let installedTargets = await this.getInstalledTargets(); + return _.findLast(AndroidToolsInfo.SUPPORTED_TARGETS.sort(), supportedTarget => _.includes(installedTargets, supportedTarget)); } private parseAndroidSdkString(androidSdkString: string): number { return parseInt(androidSdkString.replace(`${AndroidToolsInfo.ANDROID_TARGET_PREFIX}-`, "")); } - private getInstalledTargets(): IFuture { - return (() => { - if (!this.installedTargetsCache) { - try { - let pathToAndroidExecutable = this.getPathToAndroidExecutable().wait(); - if (pathToAndroidExecutable) { - let result = this.$childProcess.spawnFromEvent(pathToAndroidExecutable, ["list", "targets"], "close", {}, { throwError: false }).wait(); - if (result && result.stdout) { - this.$logger.trace(result.stdout); - this.installedTargetsCache = []; - result.stdout.replace(/id: \d+ or "(.+)"/g, (m: string, p1: string) => (this.installedTargetsCache.push(p1), m)); - } + private async getInstalledTargets(): Promise { + if (!this.installedTargetsCache) { + try { + let pathToAndroidExecutable = await this.getPathToAndroidExecutable(); + if (pathToAndroidExecutable) { + let result = await this.$childProcess.spawnFromEvent(pathToAndroidExecutable, ["list", "targets"], "close", {}, { throwError: false }); + if (result && result.stdout) { + this.$logger.trace(result.stdout); + this.installedTargetsCache = []; + result.stdout.replace(/id: \d+ or "(.+)"/g, (m: string, p1: string) => (this.installedTargetsCache.push(p1), m)); } - } catch (err) { - this.$logger.trace("Unable to get Android targets. Error is: " + err); } + } catch (err) { + this.$logger.trace("Unable to get Android targets. Error is: " + err); } - return this.installedTargetsCache; - }).future()(); + } + return this.installedTargetsCache; } private getMaxSupportedVersion(): number { diff --git a/lib/commands/add-platform.ts b/lib/commands/add-platform.ts index aa267acf0c..3d3379111b 100644 --- a/lib/commands/add-platform.ts +++ b/lib/commands/add-platform.ts @@ -1,25 +1,22 @@ export class AddPlatformCommand implements ICommand { + public allowedParameters: ICommandParameter[] = []; + constructor(private $platformService: IPlatformService, private $errors: IErrors) { } - execute(args: string[]): IFuture { - return (() => { - this.$platformService.addPlatforms(args).wait(); - }).future()(); + public async execute(args: string[]): Promise { + await this.$platformService.addPlatforms(args); } - allowedParameters: ICommandParameter[] = []; - - canExecute(args: string[]): IFuture { - return (() => { - if(!args || args.length === 0) { - this.$errors.fail("No platform specified. Please specify a platform to add"); - } + public async canExecute(args: string[]): Promise { + if (!args || args.length === 0) { + this.$errors.fail("No platform specified. Please specify a platform to add"); + } - _.each(args, arg => this.$platformService.validatePlatform(arg)); + _.each(args, arg => this.$platformService.validatePlatform(arg)); - return true; - }).future()(); + return true; } } + $injector.registerCommand("platform|add", AddPlatformCommand); diff --git a/lib/commands/appstore-list.ts b/lib/commands/appstore-list.ts index fece6d5240..2ba7b8a435 100644 --- a/lib/commands/appstore-list.ts +++ b/lib/commands/appstore-list.ts @@ -1,40 +1,37 @@ import { createTable } from "../common/helpers"; -import {StringCommandParameter} from "../common/command-params"; +import { StringCommandParameter } from "../common/command-params"; export class ListiOSApps implements ICommand { + public allowedParameters: ICommandParameter[] = [new StringCommandParameter(this.$injector), new StringCommandParameter(this.$injector)]; + constructor(private $injector: IInjector, private $itmsTransporterService: IITMSTransporterService, private $logger: ILogger, - private $prompter: IPrompter, - private $stringParameterBuilder: IStringParameterBuilder) { } - - public allowedParameters: ICommandParameter[] = [new StringCommandParameter(this.$injector), new StringCommandParameter(this.$injector)]; + private $prompter: IPrompter) { } - public execute(args: string[]): IFuture { - return (() => { - let username = args[0], - password = args[1]; + public async execute(args: string[]): Promise { + let username = args[0], + password = args[1]; - if(!username) { - username = this.$prompter.getString("Apple ID", { allowEmpty: false }).wait(); - } + if (!username) { + username = await this.$prompter.getString("Apple ID", { allowEmpty: false }); + } - if(!password) { - password = this.$prompter.getPassword("Apple ID password").wait(); - } + if (!password) { + password = await this.$prompter.getPassword("Apple ID password"); + } - let iOSApplications = this.$itmsTransporterService.getiOSApplications({username, password}).wait(); + let iOSApplications = await this.$itmsTransporterService.getiOSApplications({ username, password }); - if (!iOSApplications || !iOSApplications.length) { - this.$logger.out("Seems you don't have any applications yet."); - } else { - let table: any = createTable(["Application Name", "Bundle Identifier", "Version"], iOSApplications.map(element => { - return [element.name, element.bundleId, element.version]; - })); + if (!iOSApplications || !iOSApplications.length) { + this.$logger.out("Seems you don't have any applications yet."); + } else { + let table: any = createTable(["Application Name", "Bundle Identifier", "Version"], iOSApplications.map(element => { + return [element.name, element.bundleId, element.version]; + })); - this.$logger.out(table.toString()); - } - }).future()(); + this.$logger.out(table.toString()); + } } } diff --git a/lib/commands/appstore-upload.ts b/lib/commands/appstore-upload.ts index 1f8f061193..a0a79e2f79 100644 --- a/lib/commands/appstore-upload.ts +++ b/lib/commands/appstore-upload.ts @@ -1,22 +1,20 @@ -import {StringCommandParameter} from "../common/command-params"; +import { StringCommandParameter } from "../common/command-params"; import * as path from "path"; -import {IOSProjectService} from "../services/ios-project-service"; +import { IOSProjectService } from "../services/ios-project-service"; export class PublishIOS implements ICommand { + public allowedParameters: ICommandParameter[] = [new StringCommandParameter(this.$injector), new StringCommandParameter(this.$injector), + new StringCommandParameter(this.$injector), new StringCommandParameter(this.$injector)]; + constructor(private $errors: IErrors, - private $fs: IFileSystem, private $hostInfo: IHostInfo, private $injector: IInjector, private $itmsTransporterService: IITMSTransporterService, private $logger: ILogger, private $options: IOptions, private $prompter: IPrompter, - private $stringParameterBuilder: IStringParameterBuilder, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants) { } - public allowedParameters: ICommandParameter[] = [new StringCommandParameter(this.$injector), new StringCommandParameter(this.$injector), - new StringCommandParameter(this.$injector), new StringCommandParameter(this.$injector)]; - private get $platformsData(): IPlatformsData { return this.$injector.resolve("platformsData"); } @@ -27,81 +25,78 @@ export class PublishIOS implements ICommand { return this.$injector.resolve("platformService"); } - public execute(args: string[]): IFuture { - return (() => { - let username = args[0], - password = args[1], - mobileProvisionIdentifier = args[2], - codeSignIdentity = args[3], - teamID = this.$options.teamId, - ipaFilePath = this.$options.ipa ? path.resolve(this.$options.ipa) : null; - - if(!username) { - username = this.$prompter.getString("Apple ID", { allowEmpty: false }).wait(); - } - - if(!password) { - password = this.$prompter.getPassword("Apple ID password").wait(); - } - - if(!mobileProvisionIdentifier && !ipaFilePath) { - this.$logger.warn("No mobile provision identifier set. A default mobile provision will be used. You can set one in app/App_Resources/iOS/build.xcconfig"); + public async execute(args: string[]): Promise { + let username = args[0], + password = args[1], + mobileProvisionIdentifier = args[2], + codeSignIdentity = args[3], + teamID = this.$options.teamId, + ipaFilePath = this.$options.ipa ? path.resolve(this.$options.ipa) : null; + + if (!username) { + username = await this.$prompter.getString("Apple ID", { allowEmpty: false }); + } + + if (!password) { + password = await this.$prompter.getPassword("Apple ID password"); + } + + if (!mobileProvisionIdentifier && !ipaFilePath) { + this.$logger.warn("No mobile provision identifier set. A default mobile provision will be used. You can set one in app/App_Resources/iOS/build.xcconfig"); + } + + if (!codeSignIdentity && !ipaFilePath) { + this.$logger.warn("No code sign identity set. A default code sign identity will be used. You can set one in app/App_Resources/iOS/build.xcconfig"); + } + + this.$options.release = true; + + if (!ipaFilePath) { + let platform = this.$devicePlatformsConstants.iOS; + // No .ipa path provided, build .ipa on out own. + if (mobileProvisionIdentifier || codeSignIdentity) { + let iOSBuildConfig: IiOSBuildConfig = { + buildForDevice: true, + mobileProvisionIdentifier, + codeSignIdentity + }; + this.$logger.info("Building .ipa with the selected mobile provision and/or certificate."); + // This is not very correct as if we build multiple targets we will try to sign all of them using the signing identity here. + await this.$platformService.preparePlatform(platform); + await this.$platformService.buildPlatform(platform, iOSBuildConfig); + ipaFilePath = this.$platformService.lastOutputPath(platform, { isForDevice: iOSBuildConfig.buildForDevice }); + } else { + this.$logger.info("No .ipa, mobile provision or certificate set. Perfect! Now we'll build .xcarchive and let Xcode pick the distribution certificate and provisioning profile for you when exporting .ipa for AppStore submission."); + await this.$platformService.preparePlatform(platform); + + let platformData = this.$platformsData.getPlatformData(platform); + let iOSProjectService = platformData.platformProjectService; + + let archivePath = await iOSProjectService.archive(platformData.projectRoot); + this.$logger.info("Archive at: " + archivePath); + + let exportPath = await iOSProjectService.exportArchive({ archivePath, teamID }); + this.$logger.info("Export at: " + exportPath); + + ipaFilePath = exportPath; } - - if(!codeSignIdentity && !ipaFilePath) { - this.$logger.warn("No code sign identity set. A default code sign identity will be used. You can set one in app/App_Resources/iOS/build.xcconfig"); - } - - this.$options.release = true; - - if (!ipaFilePath) { - let platform = this.$devicePlatformsConstants.iOS; - // No .ipa path provided, build .ipa on out own. - if (mobileProvisionIdentifier || codeSignIdentity) { - let iOSBuildConfig: IiOSBuildConfig = { - buildForDevice: true, - mobileProvisionIdentifier, - codeSignIdentity - }; - this.$logger.info("Building .ipa with the selected mobile provision and/or certificate."); - // This is not very correct as if we build multiple targets we will try to sign all of them using the signing identity here. - this.$platformService.preparePlatform(platform).wait(); - this.$platformService.buildPlatform(platform, iOSBuildConfig).wait(); - ipaFilePath = this.$platformService.lastOutputPath(platform, { isForDevice: iOSBuildConfig.buildForDevice }); - } else { - this.$logger.info("No .ipa, mobile provision or certificate set. Perfect! Now we'll build .xcarchive and let Xcode pick the distribution certificate and provisioning profile for you when exporting .ipa for AppStore submission."); - this.$platformService.preparePlatform(platform).wait(); - - let platformData = this.$platformsData.getPlatformData(platform); - let iOSProjectService = platformData.platformProjectService; - - let archivePath = iOSProjectService.archive(platformData.projectRoot).wait(); - this.$logger.info("Archive at: " + archivePath); - - let exportPath = iOSProjectService.exportArchive({ archivePath, teamID }).wait(); - this.$logger.info("Export at: " + exportPath); - - ipaFilePath = exportPath; - } - } - - this.$itmsTransporterService.upload({ - username, - password, - ipaFilePath, - verboseLogging: this.$logger.getLevel() === "TRACE" - }).wait(); - }).future()(); + } + + await this.$itmsTransporterService.upload({ + username, + password, + ipaFilePath, + verboseLogging: this.$logger.getLevel() === "TRACE" + }); } - public canExecute(args: string[]): IFuture { - return (() => { - if (!this.$hostInfo.isDarwin) { - this.$errors.failWithoutHelp("This command is only available on Mac OS X."); - } + public async canExecute(args: string[]): Promise { + if (!this.$hostInfo.isDarwin) { + this.$errors.failWithoutHelp("This command is only available on Mac OS X."); + } - return true; - }).future()(); + return true; } } + $injector.registerCommand(["publish|ios", "appstore|upload"], PublishIOS); diff --git a/lib/commands/build.ts b/lib/commands/build.ts index cd0ee95dfd..043bb5a800 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -3,61 +3,57 @@ export class BuildCommandBase { protected $platformsData: IPlatformsData, protected $platformService: IPlatformService) { } - executeCore(args: string[]): IFuture { - return (() => { - let platform = args[0].toLowerCase(); - this.$platformService.preparePlatform(platform).wait(); - this.$options.clean = true; - this.$platformService.buildPlatform(platform).wait(); - if(this.$options.copyTo) { - this.$platformService.copyLastOutput(platform, this.$options.copyTo, {isForDevice: this.$options.forDevice}); - } - }).future()(); + public async executeCore(args: string[]): Promise { + let platform = args[0].toLowerCase(); + await this.$platformService.preparePlatform(platform); + this.$options.clean = true; + await this.$platformService.buildPlatform(platform); + if (this.$options.copyTo) { + this.$platformService.copyLastOutput(platform, this.$options.copyTo, { isForDevice: this.$options.forDevice }); + } } } -export class BuildIosCommand extends BuildCommandBase implements ICommand { +export class BuildIosCommand extends BuildCommandBase implements ICommand { + public allowedParameters: ICommandParameter[] = []; + constructor(protected $options: IOptions, - $platformsData: IPlatformsData, - $platformService: IPlatformService) { + $platformsData: IPlatformsData, + $platformService: IPlatformService) { super($options, $platformsData, $platformService); } - public allowedParameters: ICommandParameter[] = []; - - public execute(args: string[]): IFuture { + public async execute(args: string[]): Promise { return this.executeCore([this.$platformsData.availablePlatforms.iOS]); } - public canExecute(args: string[]): IFuture { - return (() => { - return args.length === 0 && this.$platformService.validateOptions(this.$platformsData.availablePlatforms.iOS).wait(); - }).future()(); + public canExecute(args: string[]): Promise { + return args.length === 0 && this.$platformService.validateOptions(this.$platformsData.availablePlatforms.iOS); } } + $injector.registerCommand("build|ios", BuildIosCommand); -export class BuildAndroidCommand extends BuildCommandBase implements ICommand { +export class BuildAndroidCommand extends BuildCommandBase implements ICommand { + public allowedParameters: ICommandParameter[] = []; + constructor(protected $options: IOptions, - $platformsData: IPlatformsData, - private $errors: IErrors, - $platformService: IPlatformService) { + $platformsData: IPlatformsData, + private $errors: IErrors, + $platformService: IPlatformService) { super($options, $platformsData, $platformService); } - public execute(args: string[]): IFuture { + public async execute(args: string[]): Promise { return this.executeCore([this.$platformsData.availablePlatforms.Android]); } - public allowedParameters: ICommandParameter[] = []; - - public canExecute(args: string[]): IFuture { - return (() => { - if (this.$options.release && (!this.$options.keyStorePath || !this.$options.keyStorePassword || !this.$options.keyStoreAlias || !this.$options.keyStoreAliasPassword)) { - this.$errors.fail("When producing a release build, you need to specify all --key-store-* options."); - } - return args.length === 0 && this.$platformService.validateOptions(this.$platformsData.availablePlatforms.Android).wait(); - }).future()(); + public async canExecute(args: string[]): Promise { + if (this.$options.release && (!this.$options.keyStorePath || !this.$options.keyStorePassword || !this.$options.keyStoreAlias || !this.$options.keyStoreAliasPassword)) { + this.$errors.fail("When producing a release build, you need to specify all --key-store-* options."); + } + return args.length === 0 && await this.$platformService.validateOptions(this.$platformsData.availablePlatforms.Android); } } + $injector.registerCommand("build|android", BuildAndroidCommand); diff --git a/lib/commands/clean-app.ts b/lib/commands/clean-app.ts index 6be21e7c80..3188392ece 100644 --- a/lib/commands/clean-app.ts +++ b/lib/commands/clean-app.ts @@ -2,39 +2,40 @@ export class CleanAppCommandBase { constructor(protected $options: IOptions, private $platformService: IPlatformService) { } - execute(args: string[]): IFuture { + public async execute(args: string[]): Promise { let platform = args[0].toLowerCase(); return this.$platformService.cleanDestinationApp(platform); } } -export class CleanAppIosCommand extends CleanAppCommandBase implements ICommand { +export class CleanAppIosCommand extends CleanAppCommandBase implements ICommand { constructor(protected $options: IOptions, - private $platformsData: IPlatformsData, - $platformService: IPlatformService) { + private $platformsData: IPlatformsData, + $platformService: IPlatformService) { super($options, $platformService); } public allowedParameters: ICommandParameter[] = []; - public execute(args: string[]): IFuture { + public async execute(args: string[]): Promise { return super.execute([this.$platformsData.availablePlatforms.iOS]); } } + $injector.registerCommand("clean-app|ios", CleanAppIosCommand); -export class CleanAppAndroidCommand extends CleanAppCommandBase implements ICommand { +export class CleanAppAndroidCommand extends CleanAppCommandBase implements ICommand { + public allowedParameters: ICommandParameter[] = []; + constructor(protected $options: IOptions, - private $platformsData: IPlatformsData, - private $errors: IErrors, - $platformService: IPlatformService) { + private $platformsData: IPlatformsData, + $platformService: IPlatformService) { super($options, $platformService); } - public allowedParameters: ICommandParameter[] = []; - - public execute(args: string[]): IFuture { + public async execute(args: string[]): Promise { return super.execute([this.$platformsData.availablePlatforms.Android]); } } + $injector.registerCommand("clean-app|android", CleanAppAndroidCommand); diff --git a/lib/commands/create-project.ts b/lib/commands/create-project.ts index 935a1eae4f..4e49a71343 100644 --- a/lib/commands/create-project.ts +++ b/lib/commands/create-project.ts @@ -1,33 +1,30 @@ import * as constants from "../constants"; export class CreateProjectCommand implements ICommand { + public enableHooks = false; + public allowedParameters: ICommandParameter[] = [this.$stringParameterBuilder.createMandatoryParameter("Project name cannot be empty.")]; + constructor(private $projectService: IProjectService, private $errors: IErrors, private $options: IOptions, private $stringParameterBuilder: IStringParameterBuilder) { } - public enableHooks = false; + public async execute(args: string[]): Promise { + if ((this.$options.tsc || this.$options.ng) && this.$options.template) { + this.$errors.fail("You cannot use --ng or --tsc options together with --template."); + } - execute(args: string[]): IFuture { - return (() => { - if ((this.$options.tsc || this.$options.ng) && this.$options.template) { - this.$errors.fail("You cannot use --ng or --tsc options together with --template."); - } + let selectedTemplate: string; + if (this.$options.tsc) { + selectedTemplate = constants.TYPESCRIPT_NAME; + } else if (this.$options.ng) { + selectedTemplate = constants.ANGULAR_NAME; + } else { + selectedTemplate = this.$options.template; + } - let selectedTemplate: string; - if (this.$options.tsc) { - selectedTemplate = constants.TYPESCRIPT_NAME; - } else if (this.$options.ng) { - selectedTemplate = constants.ANGULAR_NAME; - } else { - selectedTemplate = this.$options.template; - } - - this.$projectService.createProject(args[0], selectedTemplate).wait(); - }).future()(); + await this.$projectService.createProject(args[0], selectedTemplate); } - - public allowedParameters: ICommandParameter[] = [this.$stringParameterBuilder.createMandatoryParameter("Project name cannot be empty.")]; } $injector.registerCommand("create", CreateProjectCommand); diff --git a/lib/commands/debug.ts b/lib/commands/debug.ts index 70e3edb9f7..fd818d1910 100644 --- a/lib/commands/debug.ts +++ b/lib/commands/debug.ts @@ -1,9 +1,10 @@ export class DebugPlatformCommand implements ICommand { + public allowedParameters: ICommandParameter[] = []; + constructor(private debugService: IDebugService, private $devicesService: Mobile.IDevicesService, private $injector: IInjector, private $logger: ILogger, - private $childProcess: IChildProcess, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, private $config: IConfiguration, private $usbLiveSyncService: ILiveSyncService, @@ -11,50 +12,45 @@ protected $options: IOptions, protected $platformsData: IPlatformsData) { } - execute(args: string[]): IFuture { + public async execute(args: string[]): Promise { if (this.$options.start) { return this.debugService.debug(); } - this.$platformService.deployPlatform(this.$devicesService.platform).wait(); + await this.$platformService.deployPlatform(this.$devicesService.platform); this.$config.debugLivesync = true; - let applicationReloadAction = (deviceAppData: Mobile.IDeviceAppData): IFuture => { - return (() => { - let projectData: IProjectData = this.$injector.resolve("projectData"); + let applicationReloadAction = async (deviceAppData: Mobile.IDeviceAppData): Promise => { + let projectData: IProjectData = this.$injector.resolve("projectData"); + + await this.debugService.debugStop(); - this.debugService.debugStop().wait(); + let applicationId = deviceAppData.appIdentifier; + if (deviceAppData.device.isEmulator && deviceAppData.platform.toLowerCase() === this.$devicePlatformsConstants.iOS.toLowerCase()) { + applicationId = projectData.projectName; + } - let applicationId = deviceAppData.appIdentifier; - if (deviceAppData.device.isEmulator && deviceAppData.platform.toLowerCase() === this.$devicePlatformsConstants.iOS.toLowerCase()) { - applicationId = projectData.projectName; - } - deviceAppData.device.applicationManager.stopApplication(applicationId).wait(); + await deviceAppData.device.applicationManager.stopApplication(applicationId); - this.debugService.debug().wait(); - }).future()(); + await this.debugService.debug(); }; return this.$usbLiveSyncService.liveSync(this.$devicesService.platform, applicationReloadAction); } - allowedParameters: ICommandParameter[] = []; - - canExecute(args: string[]): IFuture { - return ((): boolean => { - this.$devicesService.initialize({ platform: this.debugService.platform, deviceId: this.$options.device }).wait(); - // Start emulator if --emulator is selected or no devices found. - if(this.$options.emulator || this.$devicesService.deviceCount === 0) { - return true; - } + public async canExecute(args: string[]): Promise { + await this.$devicesService.initialize({ platform: this.debugService.platform, deviceId: this.$options.device }); + // Start emulator if --emulator is selected or no devices found. + if (this.$options.emulator || this.$devicesService.deviceCount === 0) { + return true; + } - if (this.$devicesService.deviceCount > 1) { - // Starting debugger on emulator. - this.$options.emulator = true; + if (this.$devicesService.deviceCount > 1) { + // Starting debugger on emulator. + this.$options.emulator = true; - this.$logger.warn("Multiple devices found! Starting debugger on emulator. If you want to debug on specific device please select device with --device option.".yellow.bold); - } + this.$logger.warn("Multiple devices found! Starting debugger on emulator. If you want to debug on specific device please select device with --device option.".yellow.bold); + } - return true; - }).future()(); + return true; } } @@ -63,7 +59,6 @@ export class DebugIOSCommand extends DebugPlatformCommand { $devicesService: Mobile.IDevicesService, $injector: IInjector, $logger: ILogger, - $childProcess: IChildProcess, $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, $config: IConfiguration, $usbLiveSyncService: ILiveSyncService, @@ -71,15 +66,14 @@ export class DebugIOSCommand extends DebugPlatformCommand { $options: IOptions, $platformsData: IPlatformsData) { - super($iOSDebugService, $devicesService, $injector, $logger, $childProcess, $devicePlatformsConstants, $config, $usbLiveSyncService, $platformService, $options, $platformsData); + super($iOSDebugService, $devicesService, $injector, $logger, $devicePlatformsConstants, $config, $usbLiveSyncService, $platformService, $options, $platformsData); } - canExecute(args: string[]): IFuture { - return (() => { - return super.canExecute(args).wait() && this.$platformService.validateOptions(this.$platformsData.availablePlatforms.iOS).wait(); - }).future()(); + public async canExecute(args: string[]): Promise { + return await super.canExecute(args) && await this.$platformService.validateOptions(this.$platformsData.availablePlatforms.iOS); } } + $injector.registerCommand("debug|ios", DebugIOSCommand); export class DebugAndroidCommand extends DebugPlatformCommand { @@ -87,7 +81,6 @@ export class DebugAndroidCommand extends DebugPlatformCommand { $devicesService: Mobile.IDevicesService, $injector: IInjector, $logger: ILogger, - $childProcess: IChildProcess, $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, $config: IConfiguration, $usbLiveSyncService: ILiveSyncService, @@ -95,13 +88,12 @@ export class DebugAndroidCommand extends DebugPlatformCommand { $options: IOptions, $platformsData: IPlatformsData) { - super($androidDebugService, $devicesService, $injector, $logger, $childProcess, $devicePlatformsConstants, $config, $usbLiveSyncService, $platformService, $options, $platformsData); + super($androidDebugService, $devicesService, $injector, $logger, $devicePlatformsConstants, $config, $usbLiveSyncService, $platformService, $options, $platformsData); } - canExecute(args: string[]): IFuture { - return (() => { - return super.canExecute(args).wait() && this.$platformService.validateOptions(this.$platformsData.availablePlatforms.Android).wait(); - }).future()(); + public async canExecute(args: string[]): Promise { + return await super.canExecute(args) && await this.$platformService.validateOptions(this.$platformsData.availablePlatforms.Android); } } + $injector.registerCommand("debug|android", DebugAndroidCommand); diff --git a/lib/commands/deploy.ts b/lib/commands/deploy.ts index 3d3557644d..9eaf4c8e96 100644 --- a/lib/commands/deploy.ts +++ b/lib/commands/deploy.ts @@ -1,32 +1,31 @@ export class DeployOnDeviceCommand implements ICommand { + public allowedParameters: ICommandParameter[] = []; + constructor(private $platformService: IPlatformService, private $platformCommandParameter: ICommandParameter, - private $options: IOptions, - private $errors: IErrors, - private $mobileHelper: Mobile.IMobileHelper) { } + private $options: IOptions, + private $errors: IErrors, + private $mobileHelper: Mobile.IMobileHelper) { } - execute(args: string[]): IFuture { + public async execute(args: string[]): Promise { return this.$platformService.deployPlatform(args[0], true); } - public canExecute(args: string[]): IFuture { - return (() => { - if (!args || !args.length || args.length > 1) { - return false; - } + public async canExecute(args: string[]): Promise { + if (!args || !args.length || args.length > 1) { + return false; + } - if (!this.$platformCommandParameter.validate(args[0]).wait()) { - return false; - } + if (!(await this.$platformCommandParameter.validate(args[0]))) { + return false; + } - if (this.$mobileHelper.isAndroidPlatform(args[0]) && this.$options.release && (!this.$options.keyStorePath || !this.$options.keyStorePassword || !this.$options.keyStoreAlias || !this.$options.keyStoreAliasPassword)) { - this.$errors.fail("When producing a release build, you need to specify all --key-store-* options."); - } + if (this.$mobileHelper.isAndroidPlatform(args[0]) && this.$options.release && (!this.$options.keyStorePath || !this.$options.keyStorePassword || !this.$options.keyStoreAlias || !this.$options.keyStoreAliasPassword)) { + this.$errors.fail("When producing a release build, you need to specify all --key-store-* options."); + } - return this.$platformService.validateOptions(args[0]).wait(); - }).future()(); + return this.$platformService.validateOptions(args[0]); } - - allowedParameters: ICommandParameter[] = []; } + $injector.registerCommand("deploy", DeployOnDeviceCommand); diff --git a/lib/commands/devices.ts b/lib/commands/devices.ts index 4b88c687c7..a67704572b 100644 --- a/lib/commands/devices.ts +++ b/lib/commands/devices.ts @@ -1,17 +1,17 @@ export class DevicesCommand implements ICommand { + public allowedParameters: ICommandParameter[] = [this.$stringParameter]; constructor( private $stringParameter: ICommandParameter, private $emulatorPlatformService: IEmulatorPlatformService, private $options: IOptions) {} - public allowedParameters: ICommandParameter[] = [this.$stringParameter]; - - public execute(args: string[]): IFuture { + public async execute(args: string[]): Promise { if (this.$options.availableDevices) { return this.$emulatorPlatformService.listAvailableEmulators(args[0]); } return $injector.resolveCommand("device").execute(args); } } + $injector.registerCommand("devices", DevicesCommand); diff --git a/lib/commands/emulate.ts b/lib/commands/emulate.ts index a9b4b55a3f..96321f5e37 100644 --- a/lib/commands/emulate.ts +++ b/lib/commands/emulate.ts @@ -1,23 +1,24 @@ export class EmulateCommandBase { constructor(private $platformService: IPlatformService) { } - executeCore(args: string[]): IFuture { + public async executeCore(args: string[]): Promise { return this.$platformService.emulatePlatform(args[0]); } } -export class EmulateIosCommand extends EmulateCommandBase implements ICommand { +export class EmulateIosCommand extends EmulateCommandBase implements ICommand { + public allowedParameters: ICommandParameter[] = []; + constructor($platformService: IPlatformService, private $platformsData: IPlatformsData) { super($platformService); } - public allowedParameters: ICommandParameter[] = []; - - public execute(args: string[]): IFuture { + public async execute(args: string[]): Promise { return this.executeCore([this.$platformsData.availablePlatforms.iOS]); } } + $injector.registerCommand("emulate|ios", EmulateIosCommand); export class EmulateAndroidCommand extends EmulateCommandBase implements ICommand { @@ -28,8 +29,9 @@ export class EmulateAndroidCommand extends EmulateCommandBase implements IComman public allowedParameters: ICommandParameter[] = []; - public execute(args: string[]): IFuture { + public async execute(args: string[]): Promise { return this.executeCore([this.$platformsData.availablePlatforms.Android]); } } + $injector.registerCommand("emulate|android", EmulateAndroidCommand); diff --git a/lib/commands/generate-help.ts b/lib/commands/generate-help.ts index 8eb51f2256..71cfa998d4 100644 --- a/lib/commands/generate-help.ts +++ b/lib/commands/generate-help.ts @@ -1,11 +1,11 @@ export class GenerateHelpCommand implements ICommand { - constructor(private $htmlHelpService: IHtmlHelpService) { - } - public allowedParameters: ICommandParameter[] = []; - public execute(args: string[]): IFuture { + constructor(private $htmlHelpService: IHtmlHelpService) { } + + public async execute(args: string[]): Promise { return this.$htmlHelpService.generateHtmlPages(); } } + $injector.registerCommand("dev-generate-help", GenerateHelpCommand); diff --git a/lib/commands/info.ts b/lib/commands/info.ts index 6aeb7c66b5..2f74e72407 100644 --- a/lib/commands/info.ts +++ b/lib/commands/info.ts @@ -1,10 +1,11 @@ export class InfoCommand implements ICommand { - constructor(private $infoService: IInfoService) { } - public allowedParameters: ICommandParameter[] = []; - public execute(args: string[]): IFuture { + constructor(private $infoService: IInfoService) { } + + public async execute(args: string[]): Promise { return this.$infoService.printComponentsInfo(); } } + $injector.registerCommand("info", InfoCommand); diff --git a/lib/commands/init.ts b/lib/commands/init.ts index d68e2a42b7..a704c25022 100644 --- a/lib/commands/init.ts +++ b/lib/commands/init.ts @@ -1,11 +1,12 @@ export class InitCommand implements ICommand { - constructor(private $initService: IInitService) { } - public allowedParameters: ICommandParameter[] = []; public enableHooks = false; - public execute(args: string[]): IFuture { + constructor(private $initService: IInitService) { } + + public async execute(args: string[]): Promise { return this.$initService.initialize(); } } + $injector.registerCommand("init", InitCommand); diff --git a/lib/commands/install.ts b/lib/commands/install.ts index 9f9deb1ce4..95fbb13303 100644 --- a/lib/commands/install.ts +++ b/lib/commands/install.ts @@ -1,6 +1,9 @@ -import {EOL} from "os"; +import { EOL } from "os"; export class InstallCommand implements ICommand { + public enableHooks = false; + public allowedParameters: ICommandParameter[] = [this.$stringParameter]; + constructor(private $platformsData: IPlatformsData, private $platformService: IPlatformService, private $projectData: IProjectData, @@ -11,50 +14,43 @@ export class InstallCommand implements ICommand { private $stringParameter: ICommandParameter, private $npm: INodePackageManager) { } - public enableHooks = false; - - public allowedParameters: ICommandParameter[] = [this.$stringParameter]; - - public execute(args: string[]): IFuture { + public async execute(args: string[]): Promise { return args[0] ? this.installModule(args[0]) : this.installProjectDependencies(); } - private installProjectDependencies(): IFuture { - return (() => { - let error: string = ""; - - this.$pluginsService.ensureAllDependenciesAreInstalled().wait(); - - this.$projectDataService.initialize(this.$projectData.projectDir); - _.each(this.$platformsData.platformsNames, platform => { - let platformData = this.$platformsData.getPlatformData(platform); - let frameworkPackageData = this.$projectDataService.getValue(platformData.frameworkPackageName); - if (frameworkPackageData && frameworkPackageData.version) { - try { - this.$platformService.addPlatforms([`${platform}@${frameworkPackageData.version}`]).wait(); - } catch (err) { - error = `${error}${EOL}${err}`; - } - } - }); + private async installProjectDependencies(): Promise { + let error: string = ""; + + await this.$pluginsService.ensureAllDependenciesAreInstalled(); - if (error) { - this.$logger.error(error); + this.$projectDataService.initialize(this.$projectData.projectDir); + for (let platform of this.$platformsData.platformsNames) { + let platformData = this.$platformsData.getPlatformData(platform); + let frameworkPackageData = this.$projectDataService.getValue(platformData.frameworkPackageName); + if (frameworkPackageData && frameworkPackageData.version) { + try { + await this.$platformService.addPlatforms([`${platform}@${frameworkPackageData.version}`]); + } catch (err) { + error = `${error}${EOL}${err}`; + } } - }).future()(); + } + + if (error) { + this.$logger.error(error); + } } - private installModule(moduleName: string): IFuture { - return (() => { - let projectDir = this.$projectData.projectDir; + private async installModule(moduleName: string): Promise { + let projectDir = this.$projectData.projectDir; - let devPrefix = 'nativescript-dev-'; - if (!this.$fs.exists(moduleName) && moduleName.indexOf(devPrefix) !== 0) { - moduleName = devPrefix + moduleName; - } + let devPrefix = 'nativescript-dev-'; + if (!this.$fs.exists(moduleName) && moduleName.indexOf(devPrefix) !== 0) { + moduleName = devPrefix + moduleName; + } - this.$npm.install(moduleName, projectDir, { 'save-dev': true }).wait(); - }).future()(); + await this.$npm.install(moduleName, projectDir, { 'save-dev': true }); } } + $injector.registerCommand("install", InstallCommand); diff --git a/lib/commands/list-platforms.ts b/lib/commands/list-platforms.ts index 969a8ed9f9..5faca7e5d9 100644 --- a/lib/commands/list-platforms.ts +++ b/lib/commands/list-platforms.ts @@ -1,29 +1,29 @@ import * as helpers from "../common/helpers"; export class ListPlatformsCommand implements ICommand { + public allowedParameters: ICommandParameter[] = []; + constructor(private $platformService: IPlatformService, private $logger: ILogger) { } - execute(args: string[]): IFuture { - return (() => { - let installedPlatforms = this.$platformService.getInstalledPlatforms(); + public async execute(args: string[]): Promise { + let installedPlatforms = this.$platformService.getInstalledPlatforms(); - if(installedPlatforms.length > 0) { - let preparedPlatforms = this.$platformService.getPreparedPlatforms(); - if(preparedPlatforms.length > 0) { - this.$logger.out("The project is prepared for: ", helpers.formatListOfNames(preparedPlatforms, "and")); - } else { - this.$logger.out("The project is not prepared for any platform"); - } - this.$logger.out("Installed platforms: ", helpers.formatListOfNames(installedPlatforms, "and")); + if (installedPlatforms.length > 0) { + let preparedPlatforms = this.$platformService.getPreparedPlatforms(); + if (preparedPlatforms.length > 0) { + this.$logger.out("The project is prepared for: ", helpers.formatListOfNames(preparedPlatforms, "and")); } else { - let formattedPlatformsList = helpers.formatListOfNames(this.$platformService.getAvailablePlatforms(), "and"); - this.$logger.out("Available platforms for this OS: ", formattedPlatformsList); - this.$logger.out("No installed platforms found. Use $ tns platform add"); + this.$logger.out("The project is not prepared for any platform"); } - }).future()(); - } - allowedParameters: ICommandParameter[] = []; + this.$logger.out("Installed platforms: ", helpers.formatListOfNames(installedPlatforms, "and")); + } else { + let formattedPlatformsList = helpers.formatListOfNames(this.$platformService.getAvailablePlatforms(), "and"); + this.$logger.out("Available platforms for this OS: ", formattedPlatformsList); + this.$logger.out("No installed platforms found. Use $ tns platform add"); + } + } } + $injector.registerCommand("platform|*list", ListPlatformsCommand); diff --git a/lib/commands/livesync.ts b/lib/commands/livesync.ts index fd5da9b505..19468cdcbd 100644 --- a/lib/commands/livesync.ts +++ b/lib/commands/livesync.ts @@ -1,31 +1,28 @@ export class LivesyncCommand implements ICommand { - constructor(private $logger: ILogger, - private $usbLiveSyncService: ILiveSyncService, + public allowedParameters: ICommandParameter[] = []; + + constructor(private $usbLiveSyncService: ILiveSyncService, private $mobileHelper: Mobile.IMobileHelper, - private $options: IOptions, private $platformService: IPlatformService, private $errors: IErrors) { } - public execute(args: string[]): IFuture { - this.$platformService.deployPlatform(args[0]).wait(); + public async execute(args: string[]): Promise { + await this.$platformService.deployPlatform(args[0]); return this.$usbLiveSyncService.liveSync(args[0]); } - public canExecute(args: string[]): IFuture { - return (() => { - if (args.length >= 2) { - this.$errors.fail("Invalid number of arguments."); - } + public async canExecute(args: string[]): Promise { + if (args.length >= 2) { + this.$errors.fail("Invalid number of arguments."); + } - let platform = args[0]; - if (platform) { - return _.includes(this.$mobileHelper.platformNames, this.$mobileHelper.normalizePlatformName(platform)) && this.$platformService.validateOptions(args[0]).wait(); - } else { - return this.$platformService.validateOptions().wait(); - } - }).future()(); + let platform = args[0]; + if (platform) { + return _.includes(this.$mobileHelper.platformNames, this.$mobileHelper.normalizePlatformName(platform)) && await this.$platformService.validateOptions(args[0]); + } else { + return await this.$platformService.validateOptions(); + } } - - allowedParameters: ICommandParameter[] = []; } + $injector.registerCommand("livesync", LivesyncCommand); diff --git a/lib/commands/platform-clean.ts b/lib/commands/platform-clean.ts index a2320acd60..32eb477c9c 100644 --- a/lib/commands/platform-clean.ts +++ b/lib/commands/platform-clean.ts @@ -1,26 +1,23 @@ export class CleanCommand implements ICommand { + public allowedParameters: ICommandParameter[] = []; + constructor(private $platformService: IPlatformService, private $errors: IErrors) { } - public execute(args: string[]): IFuture { - return (() => { - this.$platformService.removePlatforms(args); - this.$platformService.addPlatforms(args).wait(); - }).future()(); + public async execute(args: string[]): Promise { + this.$platformService.removePlatforms(args); + await this.$platformService.addPlatforms(args); } - public canExecute(args: string[]): IFuture { - return (() => { - if(!args || args.length === 0) { - this.$errors.fail("No platform specified. Please specify a platform to clean"); - } + public async canExecute(args: string[]): Promise { + if (!args || args.length === 0) { + this.$errors.fail("No platform specified. Please specify a platform to clean"); + } - _.each(args, arg => this.$platformService.validatePlatform(arg)); + _.each(args, arg => this.$platformService.validatePlatform(arg)); - return true; - }).future()(); + return true; } - - allowedParameters: ICommandParameter[] = []; } + $injector.registerCommand("platform|clean", CleanCommand); diff --git a/lib/commands/plugin/add-plugin.ts b/lib/commands/plugin/add-plugin.ts index 439b3f9e50..75f06f2a8e 100644 --- a/lib/commands/plugin/add-plugin.ts +++ b/lib/commands/plugin/add-plugin.ts @@ -1,27 +1,26 @@ export class AddPluginCommand implements ICommand { + public allowedParameters: ICommandParameter[] = []; + constructor(private $pluginsService: IPluginsService, private $errors: IErrors) { } - execute(args: string[]): IFuture { + public async execute(args: string[]): Promise { return this.$pluginsService.add(args[0]); } - canExecute(args: string[]): IFuture { - return (() => { - if (!args[0]) { - this.$errors.fail("You must specify plugin name."); - } + public async canExecute(args: string[]): Promise { + if (!args[0]) { + this.$errors.fail("You must specify plugin name."); + } - let installedPlugins = this.$pluginsService.getAllInstalledPlugins().wait(); - let pluginName = args[0].toLowerCase(); - if (_.some(installedPlugins, (plugin: IPluginData) => plugin.name.toLowerCase() === pluginName)) { - this.$errors.failWithoutHelp(`Plugin "${pluginName}" is already installed.`); - } + let installedPlugins = await this.$pluginsService.getAllInstalledPlugins(); + let pluginName = args[0].toLowerCase(); + if (_.some(installedPlugins, (plugin: IPluginData) => plugin.name.toLowerCase() === pluginName)) { + this.$errors.failWithoutHelp(`Plugin "${pluginName}" is already installed.`); + } - return true; - }).future()(); + return true; } - - public allowedParameters: ICommandParameter[] = []; } + $injector.registerCommand(["plugin|add", "plugin|install"], AddPluginCommand); diff --git a/lib/commands/plugin/find-plugins.ts b/lib/commands/plugin/find-plugins.ts index f3889a5bbc..4d9c1c51d7 100644 --- a/lib/commands/plugin/find-plugins.ts +++ b/lib/commands/plugin/find-plugins.ts @@ -1,79 +1,72 @@ import { createTable, isInteractive } from "../../common/helpers"; import { NATIVESCRIPT_KEY_NAME } from "../../constants"; -import Future = require("fibers/future"); export class FindPluginsCommand implements ICommand { private static COUNT_OF_PLUGINS_TO_DISPLAY: number = 10; + public allowedParameters: ICommandParameter[] = []; + constructor(private $pluginsService: IPluginsService, - private $errors: IErrors, private $logger: ILogger, private $prompter: IPrompter, private $options: IOptions, private $progressIndicator: IProgressIndicator) { } - execute(args: string[]): IFuture { - return (() => { - let filter: string[] = this.prepareFilter(args); + public async execute(args: string[]): Promise { + let filter: string[] = this.prepareFilter(args); - let pluginsFuture: IFuture> = this.$pluginsService.getAvailable(filter); - if (this.$options.json) { - this.$logger.out(JSON.stringify(pluginsFuture.wait())); - return; - } + let pluginsPromise: Promise> = this.$pluginsService.getAvailable(filter); + if (this.$options.json) { + this.$logger.out(JSON.stringify(await pluginsPromise)); + return; + } - this.$logger.printInfoMessageOnSameLine("Searching npm please be patient..."); - this.$progressIndicator.showProgressIndicator(pluginsFuture, 500).wait(); - let plugins: IDictionary = pluginsFuture.get(); + this.$logger.printInfoMessageOnSameLine("Searching npm please be patient..."); + let plugins = await this.$progressIndicator.showProgressIndicator(pluginsPromise, 500); - this.showPlugins(plugins).wait(); - }).future()(); + await this.showPlugins(plugins); } - canExecute(args: string[]): IFuture { - return Future.fromResult(true); + public async canExecute(args: string[]): Promise { + return Promise.resolve(true); } - public allowedParameters: ICommandParameter[] = []; - - private showPlugins(plugins: IDictionary): IFuture { - return (() => { - let allPluginsNames: string[] = _.keys(plugins).sort(); + private async showPlugins(plugins: IDictionary): Promise { + let allPluginsNames: string[] = _.keys(plugins).sort(); - if (!allPluginsNames || !allPluginsNames.length) { - this.$logger.warn("No plugins found."); - return; - } + if (!allPluginsNames || !allPluginsNames.length) { + this.$logger.warn("No plugins found."); + return; + } - let count: number = this.$options.count || FindPluginsCommand.COUNT_OF_PLUGINS_TO_DISPLAY; + let count: number = this.$options.count || FindPluginsCommand.COUNT_OF_PLUGINS_TO_DISPLAY; - if (!isInteractive() || this.$options.all) { - count = allPluginsNames.length; - } + if (!isInteractive() || this.$options.all) { + count = allPluginsNames.length; + } - let data: string[][] = []; + let data: string[][] = []; - let pluginsToDisplay: string[] = allPluginsNames.splice(0, count); - let shouldDisplayMorePlugins: boolean = true; + let pluginsToDisplay: string[] = allPluginsNames.splice(0, count); + let shouldDisplayMorePlugins: boolean = true; - this.$logger.out("Available NativeScript plugins:"); + this.$logger.out("Available NativeScript plugins:"); - do { - data = this.createTableCells(plugins, pluginsToDisplay); + do { + data = this.createTableCells(plugins, pluginsToDisplay); - let table: any = this.createPluginsTable(data); + let table: any = this.createPluginsTable(data); - this.$logger.out(table.toString()); + this.$logger.out(table.toString()); - pluginsToDisplay = allPluginsNames.splice(0, count); + pluginsToDisplay = allPluginsNames.splice(0, count); - if (!pluginsToDisplay || pluginsToDisplay.length < 1) { - return; - } + if (!pluginsToDisplay || pluginsToDisplay.length < 1) { + return; + } - shouldDisplayMorePlugins = this.$prompter.confirm("Load more plugins?").wait(); - } while (shouldDisplayMorePlugins); - }).future()(); + shouldDisplayMorePlugins = await this.$prompter.confirm("Load more plugins?"); + } while (shouldDisplayMorePlugins); } private createPluginsTable(data: string[][]): any { @@ -98,7 +91,6 @@ export class FindPluginsCommand implements ICommand { .uniq() .value(); } - } $injector.registerCommand(["plugin|find", "plugin|search"], FindPluginsCommand); diff --git a/lib/commands/plugin/list-plugins.ts b/lib/commands/plugin/list-plugins.ts index 9d67e9706f..761abac36f 100644 --- a/lib/commands/plugin/list-plugins.ts +++ b/lib/commands/plugin/list-plugins.ts @@ -1,40 +1,38 @@ import { createTable } from "../../common/helpers"; export class ListPluginsCommand implements ICommand { + public allowedParameters: ICommandParameter[] = []; + constructor(private $pluginsService: IPluginsService, private $logger: ILogger) { } - allowedParameters: ICommandParameter[] = []; - - public execute(args: string[]): IFuture { - return (() => { - let installedPlugins: IPackageJsonDepedenciesResult = this.$pluginsService.getDependenciesFromPackageJson(); + public async execute(args: string[]): Promise { + let installedPlugins: IPackageJsonDepedenciesResult = this.$pluginsService.getDependenciesFromPackageJson(); - let headers: string[] = ["Plugin", "Version"]; - let dependenciesData: string[][] = this.createTableCells(installedPlugins.dependencies); + let headers: string[] = ["Plugin", "Version"]; + let dependenciesData: string[][] = this.createTableCells(installedPlugins.dependencies); - let dependenciesTable: any = createTable(headers, dependenciesData); - this.$logger.out("Dependencies:"); - this.$logger.out(dependenciesTable.toString()); + let dependenciesTable: any = createTable(headers, dependenciesData); + this.$logger.out("Dependencies:"); + this.$logger.out(dependenciesTable.toString()); - if (installedPlugins.devDependencies && installedPlugins.devDependencies.length) { - let devDependenciesData: string[][] = this.createTableCells(installedPlugins.devDependencies); + if (installedPlugins.devDependencies && installedPlugins.devDependencies.length) { + let devDependenciesData: string[][] = this.createTableCells(installedPlugins.devDependencies); - let devDependenciesTable: any = createTable(headers, devDependenciesData); + let devDependenciesTable: any = createTable(headers, devDependenciesData); - this.$logger.out("Dev Dependencies:"); - this.$logger.out(devDependenciesTable.toString()); - } else { - this.$logger.out("There are no dev dependencies."); - } + this.$logger.out("Dev Dependencies:"); + this.$logger.out(devDependenciesTable.toString()); + } else { + this.$logger.out("There are no dev dependencies."); + } - let viewDependenciesCommand: string = "npm view grep dependencies".cyan.toString(); - let viewDevDependenciesCommand: string = "npm view grep devDependencies".cyan.toString(); + let viewDependenciesCommand: string = "npm view grep dependencies".cyan.toString(); + let viewDevDependenciesCommand: string = "npm view grep devDependencies".cyan.toString(); - this.$logger.warn("NOTE:"); - this.$logger.warn(`If you want to check the dependencies of installed plugin use ${viewDependenciesCommand}`); - this.$logger.warn(`If you want to check the dev dependencies of installed plugin use ${viewDevDependenciesCommand}`); - }).future()(); + this.$logger.warn("NOTE:"); + this.$logger.warn(`If you want to check the dependencies of installed plugin use ${viewDependenciesCommand}`); + this.$logger.warn(`If you want to check the dev dependencies of installed plugin use ${viewDevDependenciesCommand}`); } private createTableCells(items: IBasePluginData[]): string[][] { diff --git a/lib/commands/plugin/remove-plugin.ts b/lib/commands/plugin/remove-plugin.ts index 75e7aeae16..f74e479270 100644 --- a/lib/commands/plugin/remove-plugin.ts +++ b/lib/commands/plugin/remove-plugin.ts @@ -1,38 +1,38 @@ export class RemovePluginCommand implements ICommand { + public allowedParameters: ICommandParameter[] = []; + constructor(private $pluginsService: IPluginsService, private $errors: IErrors, private $logger: ILogger, private $projectData: IProjectData) { } - execute(args: string[]): IFuture { + public async execute(args: string[]): Promise { return this.$pluginsService.remove(args[0]); } - canExecute(args: string[]): IFuture { - return (() => { - if(!args[0]) { - this.$errors.fail("You must specify plugin name."); - } - - let pluginNames: string[] = []; - try { - // try installing the plugins, so we can get information from node_modules about their native code, libs, etc. - let installedPlugins = this.$pluginsService.getAllInstalledPlugins().wait(); - pluginNames = installedPlugins.map(pl => pl.name); - } catch(err) { - this.$logger.trace("Error while installing plugins. Error is:", err); - pluginNames = _.keys(this.$projectData.dependencies); - } - - let pluginName = args[0].toLowerCase(); - if(!_.some(pluginNames, name => name.toLowerCase() === pluginName)) { - this.$errors.failWithoutHelp(`Plugin "${pluginName}" is not installed.`); - } - - return true; - }).future()(); + public async canExecute(args: string[]): Promise { + if (!args[0]) { + this.$errors.fail("You must specify plugin name."); + } + + let pluginNames: string[] = []; + try { + // try installing the plugins, so we can get information from node_modules about their native code, libs, etc. + let installedPlugins = await this.$pluginsService.getAllInstalledPlugins(); + pluginNames = installedPlugins.map(pl => pl.name); + } catch (err) { + this.$logger.trace("Error while installing plugins. Error is:", err); + pluginNames = _.keys(this.$projectData.dependencies); + } + + let pluginName = args[0].toLowerCase(); + if (!_.some(pluginNames, name => name.toLowerCase() === pluginName)) { + this.$errors.failWithoutHelp(`Plugin "${pluginName}" is not installed.`); + } + + return true; } - public allowedParameters: ICommandParameter[] = []; } + $injector.registerCommand("plugin|remove", RemovePluginCommand); diff --git a/lib/commands/plugin/update-plugin.ts b/lib/commands/plugin/update-plugin.ts index b3e6b49c1c..600a38d84b 100644 --- a/lib/commands/plugin/update-plugin.ts +++ b/lib/commands/plugin/update-plugin.ts @@ -2,40 +2,37 @@ export class UpdatePluginCommand implements ICommand { constructor(private $pluginsService: IPluginsService, private $errors: IErrors) { } - execute(args: string[]): IFuture { - return (() => { - let pluginNames = args; - - if (!pluginNames || args.length === 0) { - let installedPlugins = this.$pluginsService.getAllInstalledPlugins().wait(); - pluginNames = installedPlugins.map(p => p.name); - } - - _.each(pluginNames, p => { - this.$pluginsService.remove(p).wait(); - this.$pluginsService.add(p).wait(); - }); - }).future()(); + public async execute(args: string[]): Promise { + let pluginNames = args; + + if (!pluginNames || args.length === 0) { + let installedPlugins = await this.$pluginsService.getAllInstalledPlugins(); + pluginNames = installedPlugins.map(p => p.name); + } + + for (let p of pluginNames) { + await this.$pluginsService.remove(p); + await this.$pluginsService.add(p); + } } - canExecute(args: string[]): IFuture { - return (() => { - if (!args || args.length === 0) { - return true; - } + public async canExecute(args: string[]): Promise { + if (!args || args.length === 0) { + return true; + } - let installedPlugins = this.$pluginsService.getAllInstalledPlugins().wait(); - let installedPluginNames: string[] = installedPlugins.map(pl => pl.name); + let installedPlugins = await this.$pluginsService.getAllInstalledPlugins(); + let installedPluginNames: string[] = installedPlugins.map(pl => pl.name); - let pluginName = args[0].toLowerCase(); - if (!_.some(installedPluginNames, name => name.toLowerCase() === pluginName)) { - this.$errors.failWithoutHelp(`Plugin "${pluginName}" is not installed.`); - } + let pluginName = args[0].toLowerCase(); + if (!_.some(installedPluginNames, name => name.toLowerCase() === pluginName)) { + this.$errors.failWithoutHelp(`Plugin "${pluginName}" is not installed.`); + } - return true; - }).future()(); + return true; } public allowedParameters: ICommandParameter[] = []; } + $injector.registerCommand("plugin|update", UpdatePluginCommand); diff --git a/lib/commands/post-install.ts b/lib/commands/post-install.ts index 45bbeefe27..87263def0c 100644 --- a/lib/commands/post-install.ts +++ b/lib/commands/post-install.ts @@ -1,10 +1,9 @@ -import {PostInstallCommand} from "../common/commands/post-install"; -import * as emailValidator from "email-validator"; +import { PostInstallCommand } from "../common/commands/post-install"; +import * as emailValidator from "email-validator"; import * as queryString from "querystring"; import * as helpers from "../common/helpers"; export class PostInstallCliCommand extends PostInstallCommand { - private logger: ILogger; constructor($fs: IFileSystem, @@ -18,68 +17,65 @@ export class PostInstallCliCommand extends PostInstallCommand { $doctorService: IDoctorService, $analyticsService: IAnalyticsService, $logger: ILogger) { - super($fs, $staticConfig, $commandsService, $htmlHelpService, $options, $doctorService, $analyticsService, $logger); - this.logger = $logger; + super($fs, $staticConfig, $commandsService, $htmlHelpService, $options, $doctorService, $analyticsService, $logger); + this.logger = $logger; } - public execute(args: string[]): IFuture { - return (() => { - super.execute(args).wait(); + public async execute(args: string[]): Promise { + await super.execute(args); - if (this.shouldAskForEmail()) { - this.logger.out("Leave your e-mail address here to subscribe for NativeScript newsletter and product updates, tips and tricks:"); - let email = this.getEmail("(press Enter for blank)").wait(); - this.$userSettingsService.saveSetting("EMAIL_REGISTERED", true).wait(); - this.sendEmail(email); - } - }).future()(); + if (await this.shouldAskForEmail()) { + this.logger.out("Leave your e-mail address here to subscribe for NativeScript newsletter and product updates, tips and tricks:"); + let email = await this.getEmail("(press Enter for blank)"); + await this.$userSettingsService.saveSetting("EMAIL_REGISTERED", true); + await this.sendEmail(email); + } } - private shouldAskForEmail(): boolean { - return helpers.isInteractive() && process.env.CLI_NOPROMPT !== "1" && !this.$userSettingsService.getSettingValue("EMAIL_REGISTERED").wait(); + private async shouldAskForEmail(): Promise { + return helpers.isInteractive() && await process.env.CLI_NOPROMPT !== "1" && !this.$userSettingsService.getSettingValue("EMAIL_REGISTERED"); } - private getEmail(prompt: string, options?: IPrompterOptions): IFuture { - return (() => { - let schema: IPromptSchema = { - message: prompt, - type: "input", - name: "inputEmail", - validate: (value: any) => { - if (value === "" || emailValidator.validate(value)) { - return true; - } - return "Please provide a valid e-mail or simply leave it blank."; - }, - default: options && options.defaultAction - }; + private async getEmail(prompt: string, options?: IPrompterOptions): Promise { + let schema: IPromptSchema = { + message: prompt, + type: "input", + name: "inputEmail", + validate: (value: any) => { + if (value === "" || emailValidator.validate(value)) { + return true; + } + return "Please provide a valid e-mail or simply leave it blank."; + }, + default: options && options.defaultAction + }; - let result = this.$prompter.get([schema]).wait(); - return result.inputEmail; - }).future()(); + let result = await this.$prompter.get([schema]); + return result.inputEmail; } - private sendEmail(email: string): void { - if (email) { - let postData = queryString.stringify({ - 'elqFormName': "dev_uins_cli", - 'elqSiteID': '1325', - 'emailAddress': email, - 'elqCookieWrite': '0' - }); + private async sendEmail(email: string): Promise { + if (email) { + let postData = queryString.stringify({ + 'elqFormName': "dev_uins_cli", + 'elqSiteID': '1325', + 'emailAddress': email, + 'elqCookieWrite': '0' + }); - let options = { - url: 'https://s1325.t.eloqua.com/e/f2', - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': postData.length - }, + let options = { + url: 'https://s1325.t.eloqua.com/e/f2', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': postData.length + }, body: postData - }; + }; - this.$httpClient.httpRequest(options).wait(); + await this.$httpClient.httpRequest(options); } - } + } } + $injector.registerCommand("post-install-cli", PostInstallCliCommand); diff --git a/lib/commands/prepare.ts b/lib/commands/prepare.ts index 0a2b568b85..0286f12365 100644 --- a/lib/commands/prepare.ts +++ b/lib/commands/prepare.ts @@ -1,20 +1,16 @@ export class PrepareCommand implements ICommand { - constructor(private $errors: IErrors, - private $platformService: IPlatformService, + public allowedParameters = [this.$platformCommandParameter]; + + constructor(private $platformService: IPlatformService, private $platformCommandParameter: ICommandParameter) { } - execute(args: string[]): IFuture { - return (() => { - this.$platformService.preparePlatform(args[0]).wait(); - }).future()(); + public async execute(args: string[]): Promise { + await this.$platformService.preparePlatform(args[0]); } - public canExecute(args: string[]): IFuture { - return (() => { - return this.$platformCommandParameter.validate(args[0]).wait() && this.$platformService.validateOptions(args[0]).wait(); - }).future()(); + public async canExecute(args: string[]): Promise { + return await this.$platformCommandParameter.validate(args[0]) && await this.$platformService.validateOptions(args[0]); } - - allowedParameters = [this.$platformCommandParameter]; } + $injector.registerCommand("prepare", PrepareCommand); diff --git a/lib/commands/remove-platform.ts b/lib/commands/remove-platform.ts index bc46f14673..9c388a0dac 100644 --- a/lib/commands/remove-platform.ts +++ b/lib/commands/remove-platform.ts @@ -1,25 +1,22 @@ export class RemovePlatformCommand implements ICommand { + public allowedParameters: ICommandParameter[] = []; + constructor(private $platformService: IPlatformService, private $errors: IErrors) { } - execute(args: string[]): IFuture { - return (() => { - this.$platformService.removePlatforms(args); - }).future()(); + public async execute(args: string[]): Promise { + this.$platformService.removePlatforms(args); } - canExecute(args: string[]): IFuture { - return (() => { - if(!args || args.length === 0) { - this.$errors.fail("No platform specified. Please specify a platform to remove"); - } + public async canExecute(args: string[]): Promise { + if (!args || args.length === 0) { + this.$errors.fail("No platform specified. Please specify a platform to remove"); + } - _.each(args, arg => this.$platformService.validatePlatformInstalled(arg)); + _.each(args, arg => this.$platformService.validatePlatformInstalled(arg)); - return true; - }).future()(); + return true; } - - allowedParameters: ICommandParameter[] = []; } + $injector.registerCommand("platform|remove", RemovePlatformCommand); diff --git a/lib/commands/run.ts b/lib/commands/run.ts index fe8edb7409..1ad8b9b5fc 100644 --- a/lib/commands/run.ts +++ b/lib/commands/run.ts @@ -3,39 +3,39 @@ export class RunCommandBase { protected $usbLiveSyncService: ILiveSyncService, protected $options: IOptions) { } - public executeCore(args: string[]): IFuture { - this.$platformService.deployPlatform(args[0]).wait(); + public async executeCore(args: string[]): Promise { + await this.$platformService.deployPlatform(args[0]); if (this.$options.release) { return this.$platformService.runPlatform(args[0]); } - return this.$usbLiveSyncService.liveSync(args[0]); + return this.$usbLiveSyncService.liveSync(args[0]); } } export class RunIosCommand extends RunCommandBase implements ICommand { + public allowedParameters: ICommandParameter[] = []; + constructor($platformService: IPlatformService, private $platformsData: IPlatformsData, $usbLiveSyncService: ILiveSyncService, - $options: IOptions, - private $injector: IInjector) { + $options: IOptions) { super($platformService, $usbLiveSyncService, $options); } - public allowedParameters: ICommandParameter[] = []; - - public execute(args: string[]): IFuture { + public async execute(args: string[]): Promise { return this.executeCore([this.$platformsData.availablePlatforms.iOS]); } - public canExecute(args: string[]): IFuture { - return (() => { - return args.length === 0 && this.$platformService.validateOptions(this.$platformsData.availablePlatforms.iOS).wait(); - }).future()(); + public async canExecute(args: string[]): Promise { + return args.length === 0 && await this.$platformService.validateOptions(this.$platformsData.availablePlatforms.iOS); } } + $injector.registerCommand("run|ios", RunIosCommand); export class RunAndroidCommand extends RunCommandBase implements ICommand { + public allowedParameters: ICommandParameter[] = []; + constructor($platformService: IPlatformService, private $platformsData: IPlatformsData, $usbLiveSyncService: ILiveSyncService, @@ -44,19 +44,16 @@ export class RunAndroidCommand extends RunCommandBase implements ICommand { super($platformService, $usbLiveSyncService, $options); } - public allowedParameters: ICommandParameter[] = []; - - public execute(args: string[]): IFuture { + public async execute(args: string[]): Promise { return this.executeCore([this.$platformsData.availablePlatforms.Android]); } - public canExecute(args: string[]): IFuture { - return (() => { - if (this.$options.release && (!this.$options.keyStorePath || !this.$options.keyStorePassword || !this.$options.keyStoreAlias || !this.$options.keyStoreAliasPassword)) { - this.$errors.fail("When producing a release build, you need to specify all --key-store-* options."); - } - return args.length === 0 && this.$platformService.validateOptions(this.$platformsData.availablePlatforms.Android).wait(); - }).future()(); + public async canExecute(args: string[]): Promise { + if (this.$options.release && (!this.$options.keyStorePath || !this.$options.keyStorePassword || !this.$options.keyStoreAlias || !this.$options.keyStoreAliasPassword)) { + this.$errors.fail("When producing a release build, you need to specify all --key-store-* options."); + } + return args.length === 0 && await this.$platformService.validateOptions(this.$platformsData.availablePlatforms.Android); } } + $injector.registerCommand("run|android", RunAndroidCommand); diff --git a/lib/commands/test-init.ts b/lib/commands/test-init.ts index a060b29bf3..da9d3048c4 100644 --- a/lib/commands/test-init.ts +++ b/lib/commands/test-init.ts @@ -1,7 +1,13 @@ import * as path from 'path'; -import {TESTING_FRAMEWORKS} from '../constants'; +import { TESTING_FRAMEWORKS } from '../constants'; class TestInitCommand implements ICommand { + public allowedParameters: ICommandParameter[] = []; + + private frameworkDependencies: IDictionary = { + mocha: ['chai'], + }; + constructor(private $npm: INodePackageManager, private $projectData: IProjectData, private $errors: IErrors, @@ -10,66 +16,58 @@ class TestInitCommand implements ICommand { private $fs: IFileSystem, private $resources: IResourceLoader, private $pluginsService: IPluginsService, - private $logger: ILogger) { - } + private $logger: ILogger) { } - private frameworkDependencies:IDictionary = { - mocha: ['chai'], - }; + public async execute(args: string[]): Promise { + let projectDir = this.$projectData.projectDir; + + let frameworkToInstall = this.$options.framework || + await this.$prompter.promptForChoice('Select testing framework:', TESTING_FRAMEWORKS); + if (TESTING_FRAMEWORKS.indexOf(frameworkToInstall) === -1) { + this.$errors.fail(`Unknown or unsupported unit testing framework: ${frameworkToInstall}`); + } + + let dependencies = this.frameworkDependencies[frameworkToInstall] || []; + let modulesToInstall = ['karma', 'karma-' + frameworkToInstall, 'karma-nativescript-launcher'].concat(dependencies.map(f => 'karma-' + f)); - public execute(args: string[]) : IFuture { - return (() => { - let projectDir = this.$projectData.projectDir; - - let frameworkToInstall = this.$options.framework - || this.$prompter.promptForChoice('Select testing framework:', TESTING_FRAMEWORKS).wait(); - if (TESTING_FRAMEWORKS.indexOf(frameworkToInstall) === -1) { - this.$errors.fail(`Unknown or unsupported unit testing framework: ${frameworkToInstall}`); - } - - let dependencies = this.frameworkDependencies[frameworkToInstall] || []; - ['karma', 'karma-' + frameworkToInstall, 'karma-nativescript-launcher'] - .concat(dependencies.map(f => 'karma-' + f)) - .forEach(mod => { - this.$npm.install(mod, projectDir, { - 'save-dev': true, - optional: false, - }).wait(); + for (let mod of modulesToInstall) { + await this.$npm.install(mod, projectDir, { + 'save-dev': true, + optional: false, }); + } - this.$pluginsService.add('nativescript-unit-test-runner').wait(); + await this.$pluginsService.add('nativescript-unit-test-runner'); - let testsDir = path.join(projectDir, 'app/tests'); - let shouldCreateSampleTests = true; - if (this.$fs.exists(testsDir)) { - this.$logger.info('app/tests/ directory already exists, will not create an example test project.'); - shouldCreateSampleTests = false; - } + let testsDir = path.join(projectDir, 'app/tests'); + let shouldCreateSampleTests = true; + if (this.$fs.exists(testsDir)) { + this.$logger.info('app/tests/ directory already exists, will not create an example test project.'); + shouldCreateSampleTests = false; + } - this.$fs.ensureDirectoryExists(testsDir); + this.$fs.ensureDirectoryExists(testsDir); - let karmaConfTemplate = this.$resources.readText('test/karma.conf.js'); - let karmaConf = _.template(karmaConfTemplate)({ - frameworks: [frameworkToInstall].concat(dependencies) - .map(fw => `'${fw}'`) - .join(', ') - }); + let karmaConfTemplate = this.$resources.readText('test/karma.conf.js'); + let karmaConf = _.template(karmaConfTemplate)({ + frameworks: [frameworkToInstall].concat(dependencies) + .map(fw => `'${fw}'`) + .join(', ') + }); - this.$fs.writeFile(path.join(projectDir, 'karma.conf.js'), karmaConf); + this.$fs.writeFile(path.join(projectDir, 'karma.conf.js'), karmaConf); - let exampleFilePath = this.$resources.resolvePath(`test/example.${frameworkToInstall}.js`); + let exampleFilePath = this.$resources.resolvePath(`test/example.${frameworkToInstall}.js`); - if (shouldCreateSampleTests && this.$fs.exists(exampleFilePath)) { - this.$fs.copyFile(exampleFilePath, path.join(testsDir, 'example.js')); - this.$logger.info('\nExample test file created in app/tests/'.yellow); - } else { - this.$logger.info('\nPlace your test files under app/tests/'.yellow); - } + if (shouldCreateSampleTests && this.$fs.exists(exampleFilePath)) { + this.$fs.copyFile(exampleFilePath, path.join(testsDir, 'example.js')); + this.$logger.info('\nExample test file created in app/tests/'.yellow); + } else { + this.$logger.info('\nPlace your test files under app/tests/'.yellow); + } - this.$logger.info('Run your tests using the "$ tns test " command.'.yellow); - }).future()(); + this.$logger.info('Run your tests using the "$ tns test " command.'.yellow); } - - allowedParameters: ICommandParameter[] = []; } + $injector.registerCommand("test|init", TestInitCommand); diff --git a/lib/commands/test.ts b/lib/commands/test.ts index d597a00aa1..ed5519cb2f 100644 --- a/lib/commands/test.ts +++ b/lib/commands/test.ts @@ -1,16 +1,16 @@ function RunTestCommandFactory(platform: string) { return function RunTestCommand($testExecutionService: ITestExecutionService) { - this.execute = (args: string[]): IFuture => $testExecutionService.startTestRunner(platform); + this.execute = (args: string[]): Promise => $testExecutionService.startTestRunner(platform); this.allowedParameters = []; }; } -$injector.registerCommand("dev-test|android", RunTestCommandFactory('android')); -$injector.registerCommand("dev-test|ios", RunTestCommandFactory('iOS')); +$injector.registerCommand("dev-test|android", RunTestCommandFactory('android')); +$injector.registerCommand("dev-test|ios", RunTestCommandFactory('iOS')); function RunKarmaTestCommandFactory(platform: string) { return function RunKarmaTestCommand($testExecutionService: ITestExecutionService) { - this.execute = (args: string[]): IFuture => $testExecutionService.startKarmaServer(platform); + this.execute = (args: string[]): Promise => $testExecutionService.startKarmaServer(platform); this.allowedParameters = []; }; } diff --git a/lib/commands/update-platform.ts b/lib/commands/update-platform.ts index f29a3ffcfa..9ec35f2886 100644 --- a/lib/commands/update-platform.ts +++ b/lib/commands/update-platform.ts @@ -1,25 +1,22 @@ export class UpdatePlatformCommand implements ICommand { + public allowedParameters: ICommandParameter[] = []; + constructor(private $platformService: IPlatformService, - private $errors:IErrors) { } + private $errors: IErrors) { } - execute(args: string[]): IFuture { - return (() => { - this.$platformService.updatePlatforms(args).wait(); - }).future()(); + public async execute(args: string[]): Promise { + await this.$platformService.updatePlatforms(args); } - canExecute(args: string[]): IFuture { - return (() => { - if(!args || args.length === 0) { - this.$errors.fail("No platform specified. Please specify platforms to update."); - } + public async canExecute(args: string[]): Promise { + if (!args || args.length === 0) { + this.$errors.fail("No platform specified. Please specify platforms to update."); + } - _.each(args, arg => this.$platformService.validatePlatform(arg.split("@")[0])); + _.each(args, arg => this.$platformService.validatePlatform(arg.split("@")[0])); - return true; - }).future()(); + return true; } - - allowedParameters: ICommandParameter[] = []; } + $injector.registerCommand("platform|update", UpdatePlatformCommand); diff --git a/lib/commands/update.ts b/lib/commands/update.ts index 26c7ea6e7a..cbfd0c1d5c 100644 --- a/lib/commands/update.ts +++ b/lib/commands/update.ts @@ -2,93 +2,87 @@ import * as path from "path"; import * as shelljs from "shelljs"; export class UpdateCommand implements ICommand { - constructor( - private $projectData: IProjectData, + public allowedParameters: ICommandParameter[] = []; + + constructor(private $projectData: IProjectData, private $platformService: IPlatformService, private $platformsData: IPlatformsData, private $pluginsService: IPluginsService, private $projectDataService: IProjectDataService, - private $logger: ILogger, - private $options: IOptions, - private $errors: IErrors) { } + private $logger: ILogger) { } - public execute(args: string[]): IFuture { - return (() => { - let folders = [ "lib", "hooks", "platforms", "node_modules" ]; - let tmpDir = path.join(this.$projectData.projectDir, ".tmp_backup"); + public async execute(args: string[]): Promise { + let folders = ["lib", "hooks", "platforms", "node_modules"]; + let tmpDir = path.join(this.$projectData.projectDir, ".tmp_backup"); - try { - shelljs.rm("-fr", tmpDir); - shelljs.mkdir(tmpDir); - shelljs.cp(path.join(this.$projectData.projectDir, "package.json"), tmpDir); - for (let folder of folders) { - shelljs.cp("-rf", path.join(this.$projectData.projectDir, folder), tmpDir); - } - } catch(error) { - this.$logger.error("Could not backup project folders!"); - return; + try { + shelljs.rm("-fr", tmpDir); + shelljs.mkdir(tmpDir); + shelljs.cp(path.join(this.$projectData.projectDir, "package.json"), tmpDir); + for (let folder of folders) { + shelljs.cp("-rf", path.join(this.$projectData.projectDir, folder), tmpDir); } + } catch (error) { + this.$logger.error("Could not backup project folders!"); + return; + } - try { - this.executeCore(args, folders).wait(); - } catch (error) { - shelljs.cp("-f", path.join(tmpDir, "package.json"), this.$projectData.projectDir); - for (let folder of folders) { - shelljs.rm("-rf", path.join(this.$projectData.projectDir, folder)); - shelljs.cp("-fr", path.join(tmpDir, folder), this.$projectData.projectDir); - } - this.$logger.error("Could not update the project!"); - } finally { - shelljs.rm("-fr", tmpDir); + try { + await this.executeCore(args, folders); + } catch (error) { + shelljs.cp("-f", path.join(tmpDir, "package.json"), this.$projectData.projectDir); + for (let folder of folders) { + shelljs.rm("-rf", path.join(this.$projectData.projectDir, folder)); + shelljs.cp("-fr", path.join(tmpDir, folder), this.$projectData.projectDir); } - }).future()(); + this.$logger.error("Could not update the project!"); + } finally { + shelljs.rm("-fr", tmpDir); + } } - public canExecute(args: string[]): IFuture { - return (() => { - return args.length < 2 && this.$projectData.projectDir !== ""; - }).future()(); + public async canExecute(args: string[]): Promise { + return args.length < 2 && this.$projectData.projectDir !== ""; } - private executeCore(args: string[], folders: string[]): IFuture { - return (() => { - let platforms = this.$platformService.getInstalledPlatforms(); - let availablePlatforms = this.$platformService.getAvailablePlatforms(); - let packagePlatforms: string[] = []; + private async executeCore(args: string[], folders: string[]): Promise { + let platforms = this.$platformService.getInstalledPlatforms(); + let availablePlatforms = this.$platformService.getAvailablePlatforms(); + let packagePlatforms: string[] = []; - this.$projectDataService.initialize(this.$projectData.projectDir); - for (let platform of availablePlatforms) { - let platformData = this.$platformsData.getPlatformData(platform); - let platformVersion = this.$projectDataService.getValue(platformData.frameworkPackageName); - if (platformVersion) { - packagePlatforms.push(platform); - this.$projectDataService.removeProperty(platformData.frameworkPackageName); - } + this.$projectDataService.initialize(this.$projectData.projectDir); + for (let platform of availablePlatforms) { + let platformData = this.$platformsData.getPlatformData(platform); + let platformVersion = this.$projectDataService.getValue(platformData.frameworkPackageName); + if (platformVersion) { + packagePlatforms.push(platform); + this.$projectDataService.removeProperty(platformData.frameworkPackageName); } + } - this.$platformService.removePlatforms(platforms); - this.$pluginsService.remove("tns-core-modules").wait(); - this.$pluginsService.remove("tns-core-modules-widgets").wait(); + this.$platformService.removePlatforms(platforms); + await this.$pluginsService.remove("tns-core-modules"); + await this.$pluginsService.remove("tns-core-modules-widgets"); - for (let folder of folders) { - shelljs.rm("-fr", folder); - } + for (let folder of folders) { + shelljs.rm("-fr", folder); + } - platforms = platforms.concat(packagePlatforms); - if (args.length === 1) { - for (let platform of platforms) { - this.$platformService.addPlatforms([ platform+"@"+args[0] ]).wait(); - } - this.$pluginsService.add("tns-core-modules@" + args[0]).wait(); - } else { - this.$platformService.addPlatforms(platforms).wait(); - this.$pluginsService.add("tns-core-modules").wait(); + platforms = platforms.concat(packagePlatforms); + if (args.length === 1) { + for (let platform of platforms) { + await this.$platformService.addPlatforms([platform + "@" + args[0]]); } - this.$pluginsService.ensureAllDependenciesAreInstalled().wait(); - }).future()(); - } - allowedParameters: ICommandParameter[] = []; + await this.$pluginsService.add("tns-core-modules@" + args[0]); + } else { + await this.$platformService.addPlatforms(platforms); + await this.$pluginsService.add("tns-core-modules"); + } + + await this.$pluginsService.ensureAllDependenciesAreInstalled(); + } } + $injector.registerCommand("update", UpdateCommand); diff --git a/lib/common b/lib/common index 9b2d88f4b2..17a506a820 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 9b2d88f4b23247e601fccb35be45c411ea3ddf87 +Subproject commit 17a506a820016c2ec961c7b0d56c6e0554539900 diff --git a/lib/config.ts b/lib/config.ts index 0ad85ad651..64a5f64081 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -1,6 +1,6 @@ import * as path from "path"; -import {StaticConfigBase} from "./common/static-config-base"; -import {ConfigBase} from "./common/config-base"; +import { StaticConfigBase } from "./common/static-config-base"; +import { ConfigBase } from "./common/config-base"; export class Configuration extends ConfigBase implements IConfiguration { // User specific config CI_LOGGER = false; @@ -77,15 +77,13 @@ export class StaticConfig extends StaticConfigBase implements IStaticConfig { return path.join(__dirname, "bootstrap"); } - public getAdbFilePath(): IFuture { - return (() => { - if (!this._adbFilePath) { - let androidToolsInfo: IAndroidToolsInfo = this.$injector.resolve("androidToolsInfo"); - this._adbFilePath = androidToolsInfo.getPathToAdbFromAndroidHome().wait() || super.getAdbFilePath().wait(); - } + public async getAdbFilePath(): Promise { + if (!this._adbFilePath) { + let androidToolsInfo: IAndroidToolsInfo = this.$injector.resolve("androidToolsInfo"); + this._adbFilePath = await androidToolsInfo.getPathToAdbFromAndroidHome() || await super.getAdbFilePath(); + } - return this._adbFilePath; - }).future()(); + return this._adbFilePath; } } $injector.register("staticConfig", StaticConfig); diff --git a/lib/constants.ts b/lib/constants.ts index c2780f07cc..53cfcffcbf 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -1,27 +1,27 @@ -export let APP_FOLDER_NAME = "app"; -export let APP_RESOURCES_FOLDER_NAME = "App_Resources"; -export let PROJECT_FRAMEWORK_FOLDER_NAME = "framework"; -export let NATIVESCRIPT_KEY_NAME = "nativescript"; -export let NODE_MODULES_FOLDER_NAME = "node_modules"; -export let TNS_MODULES_FOLDER_NAME = "tns_modules"; -export let TNS_CORE_MODULES_NAME = "tns-core-modules"; -export let TNS_ANDROID_RUNTIME_NAME = "tns-android"; -export let TNS_IOS_RUNTIME_NAME = "tns-ios"; -export let PACKAGE_JSON_FILE_NAME = "package.json"; -export let NODE_MODULE_CACHE_PATH_KEY_NAME = "node-modules-cache-path"; -export let DEFAULT_APP_IDENTIFIER_PREFIX = "org.nativescript"; -export var LIVESYNC_EXCLUDED_DIRECTORIES = ["app_resources"]; -export var TESTING_FRAMEWORKS = ['jasmine', 'mocha', 'qunit']; -export let TEST_RUNNER_NAME = "nativescript-unit-test-runner"; -export let LIVESYNC_EXCLUDED_FILE_PATTERNS = ["**/*.js.map", "**/*.ts"]; -export let XML_FILE_EXTENSION = ".xml"; +export const APP_FOLDER_NAME = "app"; +export const APP_RESOURCES_FOLDER_NAME = "App_Resources"; +export const PROJECT_FRAMEWORK_FOLDER_NAME = "framework"; +export const NATIVESCRIPT_KEY_NAME = "nativescript"; +export const NODE_MODULES_FOLDER_NAME = "node_modules"; +export const TNS_MODULES_FOLDER_NAME = "tns_modules"; +export const TNS_CORE_MODULES_NAME = "tns-core-modules"; +export const TNS_ANDROID_RUNTIME_NAME = "tns-android"; +export const TNS_IOS_RUNTIME_NAME = "tns-ios"; +export const PACKAGE_JSON_FILE_NAME = "package.json"; +export const NODE_MODULE_CACHE_PATH_KEY_NAME = "node-modules-cache-path"; +export const DEFAULT_APP_IDENTIFIER_PREFIX = "org.nativescript"; +export const LIVESYNC_EXCLUDED_DIRECTORIES = ["app_resources"]; +export const TESTING_FRAMEWORKS = ['jasmine', 'mocha', 'qunit']; +export const TEST_RUNNER_NAME = "nativescript-unit-test-runner"; +export const LIVESYNC_EXCLUDED_FILE_PATTERNS = ["**/*.js.map", "**/*.ts"]; +export const XML_FILE_EXTENSION = ".xml"; export class PackageVersion { static NEXT = "next"; static LATEST = "latest"; } -export let PackageJsonKeysToKeep : Array = ["name", "main", "android", "version"]; +export const PackageJsonKeysToKeep: Array = ["name", "main", "android", "version"]; export class SaveOptions { static PRODUCTION = "save"; @@ -40,7 +40,7 @@ export class ReleaseType { static PRERELEASE = "prerelease"; } -export let RESERVED_TEMPLATE_NAMES: IStringDictionary = { +export const RESERVED_TEMPLATE_NAMES: IStringDictionary = { "default": "tns-template-hello-world", "tsc": "tns-template-hello-world-ts", "typescript": "tns-template-hello-world-ts", @@ -63,7 +63,7 @@ class ItunesConnectApplicationTypesClass implements IiTunesConnectApplicationTyp public Mac = "Mac OS X App"; } -export let ItunesConnectApplicationTypes = new ItunesConnectApplicationTypesClass(); +export const ItunesConnectApplicationTypes = new ItunesConnectApplicationTypesClass(); -export let ANGULAR_NAME = "angular"; -export let TYPESCRIPT_NAME = "typescript"; +export const ANGULAR_NAME = "angular"; +export const TYPESCRIPT_NAME = "typescript"; diff --git a/lib/declarations.ts b/lib/declarations.ts index 8b87f1c5b2..7227ac6110 100644 --- a/lib/declarations.ts +++ b/lib/declarations.ts @@ -1,16 +1,16 @@ interface INodePackageManager { - install(packageName: string, pathToSave: string, config?: any): IFuture; - uninstall(packageName: string, config?: any, path?: string): IFuture; - view(packageName: string, config: any): IFuture; - search(filter: string[], config: any): IFuture; + install(packageName: string, pathToSave: string, config?: any): Promise; + uninstall(packageName: string, config?: any, path?: string): Promise; + view(packageName: string, config: any): Promise; + search(filter: string[], config: any): Promise; } interface INpmInstallationManager { - install(packageName: string, packageDir: string, options?: INpmInstallOptions): IFuture; - getLatestVersion(packageName: string): IFuture; - getNextVersion(packageName: string): IFuture; - getLatestCompatibleVersion(packageName: string): IFuture; - getInspectorFromCache(inspectorNpmPackageName: string, projectDir: string): IFuture; + install(packageName: string, packageDir: string, options?: INpmInstallOptions): Promise; + getLatestVersion(packageName: string): Promise; + getNextVersion(packageName: string): Promise; + getLatestCompatibleVersion(packageName: string): Promise; + getInspectorFromCache(inspectorNpmPackageName: string, projectDir: string): Promise; } interface INpmInstallOptions { @@ -41,23 +41,23 @@ interface IApplicationPackage { } interface ILockFile { - lock(): IFuture; - unlock(): IFuture; - check(): IFuture; + lock(): void; + unlock(): void; + check(): boolean; } interface IOpener { - open(target: string, appname: string): void; + open(target: string, appname: string): void; } interface ILiveSyncService { - liveSync(platform: string, applicationReloadAction?: (deviceAppData: Mobile.IDeviceAppData) => IFuture): IFuture; + liveSync(platform: string, applicationReloadAction?: (deviceAppData: Mobile.IDeviceAppData) => Promise): Promise; } interface IPlatformLiveSyncService { - fullSync(postAction?: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => IFuture): IFuture; - partialSync(event: string, filePath: string, dispatcher: IFutureDispatcher, afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => IFuture): void; - refreshApplication(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], isFullSync: boolean): IFuture; + fullSync(postAction?: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise): Promise; + partialSync(event: string, filePath: string, dispatcher: IFutureDispatcher, afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise): Promise; + refreshApplication(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], isFullSync: boolean): Promise; } interface IOptions extends ICommonOptions { @@ -100,11 +100,11 @@ interface IOptions extends ICommonOptions { } interface IInitService { - initialize(): IFuture; + initialize(): Promise; } interface IInfoService { - printComponentsInfo(): IFuture; + printComponentsInfo(): Promise; } /** @@ -138,15 +138,15 @@ interface IITMSTransporterService { /** * Uploads an .ipa package to iTunes Connect. * @param {IITMSData} data Data needed to upload the package - * @return {IFuture} + * @return {Promise} */ - upload(data: IITMSData): IFuture; + upload(data: IITMSData): Promise; /** * Queries Apple's content delivery API to get the user's registered iOS applications. * @param {ICredentials} credentials Credentials for authentication with iTunes Connect. - * @return {IFuture} The user's iOS applications. + * @return {Promise} The user's iOS applications. */ - getiOSApplications(credentials: ICredentials): IFuture; + getiOSApplications(credentials: ICredentials): Promise; } /** @@ -158,14 +158,14 @@ interface IAndroidToolsInfo { * and ANDROID_HOME environement variable. * @return {IAndroidToolsInfoData} Information about installed Android Tools and SDKs. */ - getToolsInfo(): IFuture; + getToolsInfo(): Promise; /** * Validates the information about required Android tools and SDK versions. * @param {any} options Defines if the warning messages should treated as error and if the targetSdk value should be validated as well. * @return {boolean} True if there are detected issues, false otherwise. */ - validateInfo(options?: { showWarningsAsErrors: boolean, validateTargetSdk: boolean }): IFuture; + validateInfo(options?: { showWarningsAsErrors: boolean, validateTargetSdk: boolean }): Promise; /** * Validates the information about required JAVA version. @@ -173,7 +173,7 @@ interface IAndroidToolsInfo { * @param {any} options Defines if the warning messages should treated as error. * @return {boolean} True if there are detected issues, false otherwise. */ - validateJavacVersion(installedJavaVersion: string, options?: { showWarningsAsErrors: boolean }): IFuture; + validateJavacVersion(installedJavaVersion: string, options?: { showWarningsAsErrors: boolean }): Promise; /** * Returns the path to `android` executable. It should be `$ANDROID_HOME/tools/android`. @@ -181,13 +181,13 @@ interface IAndroidToolsInfo { * @param {any} options Defines if the warning messages should treated as error. * @return {string} Path to the `android` executable. */ - getPathToAndroidExecutable(options?: { showWarningsAsErrors: boolean }): IFuture; + getPathToAndroidExecutable(options?: { showWarningsAsErrors: boolean }): Promise; /** * Gets the path to `adb` executable from ANDROID_HOME. It should be `$ANDROID_HOME/platform-tools/adb` in case it exists. * @return {string} Path to the `adb` executable. In case it does not exists, null is returned. */ - getPathToAdbFromAndroidHome(): IFuture; + getPathToAdbFromAndroidHome(): Promise; } /** @@ -242,12 +242,12 @@ interface IiOSNotification { } interface IiOSNotificationService { - awaitNotification(npc: Mobile.INotificationProxyClient, notification: string, timeout: number): IFuture; + awaitNotification(npc: Mobile.INotificationProxyClient, notification: string, timeout: number): Promise; } interface IiOSSocketRequestExecutor { - executeLaunchRequest(device: Mobile.IiOSDevice, timeout: number, readyForAttachTimeout: number, shouldBreak?: boolean): IFuture; - executeAttachRequest(device: Mobile.IiOSDevice, timeout: number): IFuture; + executeLaunchRequest(device: Mobile.IiOSDevice, timeout: number, readyForAttachTimeout: number, shouldBreak?: boolean): Promise; + executeAttachRequest(device: Mobile.IiOSDevice, timeout: number): Promise; } /** @@ -275,33 +275,33 @@ interface IXmlValidator { interface IVersionsService { /** * Gets version information about nativescript-cli. - * @return {IFuture} The version information. + * @return {Promise} The version information. */ - getNativescriptCliVersion(): IFuture; + getNativescriptCliVersion(): Promise; /** * Gets version information about tns-core-modules. - * @return {IFuture} The version information. + * @return {Promise} The version information. */ - getTnsCoreModulesVersion(): IFuture; + getTnsCoreModulesVersion(): Promise; /** * Gets versions information about nativescript runtimes. - * @return {IFuture} The version information. + * @return {Promise} The version information. */ - getRuntimesVersions(): IFuture; + getRuntimesVersions(): Promise; /** * Gets versions information about the nativescript components with new. - * @return {IFuture} The version information. + * @return {Promise} The version information. */ - getComponentsForUpdate(): IFuture; + getComponentsForUpdate(): Promise; /** * Gets versions information about all nativescript components. - * @return {IFuture} The version information. + * @return {Promise} The version information. */ - getAllComponentsVersions(): IFuture; + getAllComponentsVersions(): Promise; /** * Creates table with versions information. @@ -319,9 +319,9 @@ interface IProjectNameService { * Ensures the passed project name is valid. If the project name is not valid prompts for actions. * @param {string} project name to be checked. * @param {IOptions} current command options. - * @return {IFuture} returns the selected name of the project. + * @return {Promise} returns the selected name of the project. */ - ensureValidName(projectName: string, validateOptions?: { force: boolean }): IFuture; + ensureValidName(projectName: string, validateOptions?: { force: boolean }): Promise; } /** @@ -332,14 +332,14 @@ interface IXcprojService { * Checks whether the system needs xcproj to execute ios builds successfully. * In case the system does need xcproj but does not have it, prints an error message. * @param {boolean} whether to fail with error message or not - * @return {IFuture} whether an error occurred or not. + * @return {Promise} whether an error occurred or not. */ - verifyXcproj(shouldFail: boolean): IFuture; + verifyXcproj(shouldFail: boolean): Promise; /** * Collects information about xcproj. - * @return {IFuture} collected info about xcproj. + * @return {Promise} collected info about xcproj. */ - getXcprojInfo(): IFuture; + getXcprojInfo(): Promise; } /** diff --git a/lib/definitions/debug.d.ts b/lib/definitions/debug.d.ts index ac4e21d845..7185c8c69b 100644 --- a/lib/definitions/debug.d.ts +++ b/lib/definitions/debug.d.ts @@ -1,6 +1,6 @@ interface IDebugService { - debug(shouldBreak?: boolean): IFuture; - debugStart(): IFuture; - debugStop(): IFuture + debug(shouldBreak?: boolean): Promise; + debugStart(): Promise; + debugStop(): Promise platform: string; } \ No newline at end of file diff --git a/lib/definitions/emulator-platform-service.d.ts b/lib/definitions/emulator-platform-service.d.ts index e10bb6edb9..0282d043e3 100644 --- a/lib/definitions/emulator-platform-service.d.ts +++ b/lib/definitions/emulator-platform-service.d.ts @@ -1,16 +1,16 @@ interface IEmulatorInfo { - name: string; - version: string; - platform: string; - id: string; - type: string; - isRunning?: boolean; + name: string; + version: string; + platform: string; + id: string; + type: string; + isRunning?: boolean; } interface IEmulatorPlatformService { - listAvailableEmulators(platform: string): IFuture; - getEmulatorInfo(platform: string, nameOfId: string): IFuture; - getiOSEmulators(): IFuture; - getAndroidEmulators(): IFuture; - startEmulator(info: IEmulatorInfo): IFuture; + listAvailableEmulators(platform: string): Promise; + getEmulatorInfo(platform: string, nameOfId: string): Promise; + getiOSEmulators(): Promise; + getAndroidEmulators(): Promise; + startEmulator(info: IEmulatorInfo): Promise; } \ No newline at end of file diff --git a/lib/definitions/libref.ts b/lib/definitions/libref.ts index e43f573038..27269e6806 100644 --- a/lib/definitions/libref.ts +++ b/lib/definitions/libref.ts @@ -1,6 +1,6 @@ interface ILibRef { - idx: number; - path: string; - adjustedPath?: string; - key?: string; + idx: number; + path: string; + adjustedPath?: string; + key?: string; } diff --git a/lib/definitions/lockfile.d.ts b/lib/definitions/lockfile.d.ts index 0f93009671..af62919fee 100644 --- a/lib/definitions/lockfile.d.ts +++ b/lib/definitions/lockfile.d.ts @@ -4,6 +4,14 @@ declare module "lockfile" { export function unlock(lockFilename: string, callback: (err: Error) => void): void; export function unlockSync(lockFilename: string): void; export function check(lockFilename: string, lockParams: ILockParams, callback: (err: Error, isLocked: boolean) => void): boolean; + export function checkSync(path: string, opts: Options): boolean; + + export interface Options { + wait?: number; + stale?: number; + retries?: number; + retryWait?: number; + } interface ILockSyncParams { retries: number; diff --git a/lib/definitions/platform.d.ts b/lib/definitions/platform.d.ts index a586db5126..0ab71ba8e3 100644 --- a/lib/definitions/platform.d.ts +++ b/lib/definitions/platform.d.ts @@ -1,5 +1,5 @@ interface IPlatformService { - addPlatforms(platforms: string[]): IFuture; + addPlatforms(platforms: string[]): Promise; /** * Gets list of all installed platforms (the ones for which /platforms/ exists). @@ -26,7 +26,7 @@ interface IPlatformService { */ removePlatforms(platforms: string[]): void; - updatePlatforms(platforms: string[]): IFuture; + updatePlatforms(platforms: string[]): Promise; /** * Ensures that the specified platform and its dependencies are installed. @@ -36,7 +36,7 @@ interface IPlatformService { * @param {string} platform The platform to be prepared. * @returns {boolean} true indicates that the platform was prepared. */ - preparePlatform(platform: string, changesInfo?: IProjectChangesInfo): IFuture; + preparePlatform(platform: string, changesInfo?: IProjectChangesInfo): Promise; /** * Determines whether a build is necessary. A build is necessary when one of the following is true: @@ -46,7 +46,7 @@ interface IPlatformService { * @param {IBuildConfig} buildConfig Indicates whether the build is for device or emulator. * @returns {boolean} true indicates that the platform should be build. */ - shouldBuild(platform: string, buildConfig?: IBuildConfig): IFuture; + shouldBuild(platform: string, buildConfig?: IBuildConfig): Promise; /** * Builds the native project for the specified platform for device or emulator. @@ -56,16 +56,16 @@ interface IPlatformService { * @param {IBuildConfig} buildConfig Indicates whether the build is for device or emulator. * @returns {void} */ - buildPlatform(platform: string, buildConfig?: IBuildConfig): IFuture; + buildPlatform(platform: string, buildConfig?: IBuildConfig): Promise; /** * Determines whether installation is necessary. It is necessary when one of the following is true: * - the application is not installed. * - the .nsbuildinfo file located in application root folder is different than the local .nsbuildinfo file * @param {Mobile.IDevice} device The device where the application should be installed. - * @returns {boolean} true indicates that the application should be installed. + * @returns {Promise} true indicates that the application should be installed. */ - shouldInstall(device: Mobile.IDevice): boolean; + shouldInstall(device: Mobile.IDevice): Promise; /** * Installs the application on specified device. @@ -74,14 +74,14 @@ interface IPlatformService { * @param {Mobile.IDevice} device The device where the application should be installed. * @returns {void} */ - installApplication(device: Mobile.IDevice): IFuture; + installApplication(device: Mobile.IDevice): Promise; /** * Gets first chance to validate the options provided as command line arguments. * If no platform is provided or a falsy (null, undefined, "", false...) platform is provided, * the options will be validated for all available platforms. */ - validateOptions(platform?: string): IFuture; + validateOptions(platform?: string): Promise; /** * Executes prepare, build and installOnPlatform when necessary to ensure that the latest version of the app is installed on specified platform. @@ -90,23 +90,23 @@ interface IPlatformService { * @param {boolean} forceInstall When true, installs the application unconditionally. * @returns {void} */ - deployPlatform(platform: string, forceInstall?: boolean): IFuture; + deployPlatform(platform: string, forceInstall?: boolean): Promise; /** * Runs the application on specified platform. Assumes that the application is already build and installed. Fails if this is not true. * @param {string} platform The platform where to start the application. * @returns {void} */ - runPlatform(platform: string): IFuture; + runPlatform(platform: string): Promise; /** * The emulate command. In addition to `run --emulator` command, it handles the `--available-devices` option to show the available devices. * @param {string} platform The platform to emulate. * @returns {void} */ - emulatePlatform(platform: string): IFuture; + emulatePlatform(platform: string): Promise; - cleanDestinationApp(platform: string): IFuture; + cleanDestinationApp(platform: string): Promise; validatePlatformInstalled(platform: string): void; validatePlatform(platform: string): void; @@ -141,7 +141,7 @@ interface IPlatformService { * @param {string} deviceFilePath The file path. * @returns {string} The contents of the file or null when there is no such file. */ - readFile(device: Mobile.IDevice, deviceFilePath: string): IFuture; + readFile(device: Mobile.IDevice, deviceFilePath: string): Promise; } interface IPlatformData { @@ -172,7 +172,7 @@ interface IPlatformsData { } interface INodeModulesBuilder { - prepareNodeModules(absoluteOutputPath: string, platform: string, lastModifiedTime: Date): IFuture; + prepareNodeModules(absoluteOutputPath: string, platform: string, lastModifiedTime: Date): Promise; cleanNodeModules(absoluteOutputPath: string, platform: string): void; } diff --git a/lib/definitions/plugins.d.ts b/lib/definitions/plugins.d.ts index e33ad891f1..7218812dd6 100644 --- a/lib/definitions/plugins.d.ts +++ b/lib/definitions/plugins.d.ts @@ -1,10 +1,10 @@ interface IPluginsService { - add(plugin: string): IFuture; // adds plugin by name, github url, local path and et. - remove(pluginName: string): IFuture; // removes plugin only by name - getAvailable(filter: string[]): IFuture>; // gets all available plugins - prepare(pluginData: IDependencyData, platform: string): IFuture; - getAllInstalledPlugins(): IFuture; - ensureAllDependenciesAreInstalled(): IFuture; + add(plugin: string): Promise; // adds plugin by name, github url, local path and et. + remove(pluginName: string): Promise; // removes plugin only by name + getAvailable(filter: string[]): Promise>; // gets all available plugins + prepare(pluginData: IDependencyData, platform: string): Promise; + getAllInstalledPlugins(): Promise; + ensureAllDependenciesAreInstalled(): Promise; /** * Returns all dependencies and devDependencies from pacakge.json file. @@ -45,9 +45,9 @@ interface IPluginVariablesService { /** * Saves plugin variables in project package.json file. * @param {IPluginData} pluginData for the plugin. - * @return {IFuture} + * @return {Promise} */ - savePluginVariablesInProjectFile(pluginData: IPluginData): IFuture; + savePluginVariablesInProjectFile(pluginData: IPluginData): Promise; /** * Removes plugin variables from project package.json file. @@ -60,9 +60,9 @@ interface IPluginVariablesService { * Replaces all plugin variables with their corresponding values. * @param {IPluginData} pluginData for the plugin. * @param {pluginConfigurationFilePath} pluginConfigurationFilePath for the plugin. - * @return {IFuture} + * @return {Promise} */ - interpolatePluginVariables(pluginData: IPluginData, pluginConfigurationFilePath: string): IFuture; + interpolatePluginVariables(pluginData: IPluginData, pluginConfigurationFilePath: string): Promise; /** * Replaces {nativescript.id} expression with the application identifier from package.json. @@ -74,12 +74,12 @@ interface IPluginVariablesService { /** * Replaces both plugin variables and appIdentifier */ - interpolate(pluginData: IPluginData, pluginConfigurationFilePath: string): IFuture; + interpolate(pluginData: IPluginData, pluginConfigurationFilePath: string): Promise; /** * Returns the * @param {string} pluginName for the plugin. - * @return {IFuture} returns the changed plugin configuration file content. + * @return {Promise} returns the changed plugin configuration file content. */ getPluginVariablePropertyName(pluginName: string): string; diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index c839ce8ea2..4fe21a94a7 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -1,6 +1,6 @@ interface IProjectService { - createProject(projectName: string, selectedTemplate?: string): IFuture; + createProject(projectName: string, selectedTemplate?: string): Promise; } interface IProjectData { @@ -59,7 +59,7 @@ interface IProjectTemplatesService { * @param {string} templateName The name of the template. * @return {string} Path to the directory where extracted template can be found. */ - prepareTemplate(templateName: string, projectDir: string): IFuture; + prepareTemplate(templateName: string, projectDir: string): Promise; } interface IPlatformProjectServiceBase { @@ -91,10 +91,10 @@ interface IiOSBuildConfig extends IBuildConfig { interface IPlatformProjectService { platformData: IPlatformData; - validate(): IFuture; - createProject(frameworkDir: string, frameworkVersion: string, pathToTemplate?: string): IFuture; - interpolateData(): IFuture; - interpolateConfigurationFile(configurationFilePath?: string): IFuture; + validate(): Promise; + createProject(frameworkDir: string, frameworkVersion: string, pathToTemplate?: string): Promise; + interpolateData(): Promise; + interpolateConfigurationFile(configurationFilePath?: string): Promise; /** * Executes additional actions after native project is created. @@ -106,15 +106,15 @@ interface IPlatformProjectService { /** * Gets first chance to validate the options provided as command line arguments. */ - validateOptions(): IFuture; + validateOptions(): Promise; - buildProject(projectRoot: string, buildConfig?: IBuildConfig): IFuture; + buildProject(projectRoot: string, buildConfig?: IBuildConfig): Promise; /** * Prepares images in Native project (for iOS). * @returns {void} */ - prepareProject(): void; + prepareProject(): Promise; /** * Prepares App_Resources in the native project by clearing data from other platform and applying platform specific rules. @@ -137,17 +137,17 @@ interface IPlatformProjectService { */ canUpdatePlatform(newInstalledModuleDir: string): boolean; - preparePluginNativeCode(pluginData: IPluginData, options?: any): IFuture; + preparePluginNativeCode(pluginData: IPluginData, options?: any): Promise; /** * Removes native code of a plugin (CocoaPods, jars, libs, src). * @param {IPluginData} Plugins data describing the plugin which should be cleaned. * @returns {void} */ - removePluginNativeCode(pluginData: IPluginData): void; + removePluginNativeCode(pluginData: IPluginData): Promise; - afterPrepareAllPlugins(): IFuture; - beforePrepareAllPlugins(dependencies?: IDictionary): IFuture; + afterPrepareAllPlugins(): Promise; + beforePrepareAllPlugins(dependencies?: IDictionary): Promise; /** * Gets the path wheren App_Resources should be copied. @@ -155,8 +155,8 @@ interface IPlatformProjectService { */ getAppResourcesDestinationDirectoryPath(): string; - deploy(deviceIdentifier: string): IFuture; - processConfigurationFilesFromAppResources(): IFuture; + deploy(deviceIdentifier: string): Promise; + processConfigurationFilesFromAppResources(): Promise; /** * Ensures there is configuration file (AndroidManifest.xml, Info.plist) in app/App_Resources. @@ -166,14 +166,14 @@ interface IPlatformProjectService { } interface IAndroidProjectPropertiesManager { - getProjectReferences(): IFuture; - addProjectReference(referencePath: string): IFuture; - removeProjectReference(referencePath: string): IFuture; + getProjectReferences(): Promise; + addProjectReference(referencePath: string): Promise; + removeProjectReference(referencePath: string): Promise; } interface ITestExecutionService { - startTestRunner(platform: string): IFuture; - startKarmaServer(platform: string): IFuture; + startTestRunner(platform: string): Promise; + startKarmaServer(platform: string): Promise; } /** diff --git a/lib/device-sockets/ios/socket-proxy-factory.ts b/lib/device-sockets/ios/socket-proxy-factory.ts index b224bbe699..0b43946424 100644 --- a/lib/device-sockets/ios/socket-proxy-factory.ts +++ b/lib/device-sockets/ios/socket-proxy-factory.ts @@ -7,8 +7,6 @@ import * as helpers from "../../common/helpers"; export class SocketProxyFactory implements ISocketProxyFactory { constructor(private $logger: ILogger, private $config: IConfiguration, - private $projectData: IProjectData, - private $projectDataService: IProjectDataService, private $options: IOptions) { } public createTCPSocketProxy(factory: () => net.Socket): any { @@ -61,7 +59,7 @@ export class SocketProxyFactory implements ISocketProxyFactory { let socketFileLocation = temp.path({ suffix: ".sock" }); server.listen(socketFileLocation); - if(!this.$options.client) { + if (!this.$options.client) { this.$logger.info("socket-file-location: " + socketFileLocation); } diff --git a/lib/device-sockets/ios/socket-request-executor.ts b/lib/device-sockets/ios/socket-request-executor.ts index 756c18d860..ee9e3601cf 100644 --- a/lib/device-sockets/ios/socket-request-executor.ts +++ b/lib/device-sockets/ios/socket-request-executor.ts @@ -1,4 +1,3 @@ -import * as helpers from "../../common/helpers"; import * as iOSProxyServices from "../../common/mobile/ios/device/ios-proxy-services"; export class IOSSocketRequestExecutor implements IiOSSocketRequestExecutor { @@ -9,68 +8,64 @@ export class IOSSocketRequestExecutor implements IiOSSocketRequestExecutor { private $logger: ILogger, private $projectData: IProjectData) { } - public executeAttachRequest(device: Mobile.IiOSDevice, timeout: number): IFuture { - return (() => { - let npc = new iOSProxyServices.NotificationProxyClient(device, this.$injector); + public async executeAttachRequest(device: Mobile.IiOSDevice, timeout: number): Promise { + let npc = new iOSProxyServices.NotificationProxyClient(device, this.$injector); - let data = [this.$iOSNotification.alreadyConnected, this.$iOSNotification.readyForAttach, this.$iOSNotification.attachAvailable] - .map((notification) => this.$iOSNotificationService.awaitNotification(npc, notification, timeout)), - alreadyConnected = data[0], - readyForAttach = data[1], - attachAvailable = data[2]; + let data = [this.$iOSNotification.alreadyConnected, this.$iOSNotification.readyForAttach, this.$iOSNotification.attachAvailable] + .map((notification) => this.$iOSNotificationService.awaitNotification(npc, notification, timeout)), + alreadyConnected = data[0], + readyForAttach = data[1], + attachAvailable = data[2]; - npc.postNotificationAndAttachForData(this.$iOSNotification.attachAvailabilityQuery); + npc.postNotificationAndAttachForData(this.$iOSNotification.attachAvailabilityQuery); - let receivedNotification: IFuture; - try { - receivedNotification = helpers.whenAny(alreadyConnected, readyForAttach, attachAvailable).wait(); - } catch (e) { - this.$errors.failWithoutHelp(`The application ${this.$projectData.projectId} does not appear to be running on ${device.deviceInfo.displayName} or is not built with debugging enabled.`); - } + let receivedNotification: string; + try { + receivedNotification = await Promise.race([alreadyConnected, readyForAttach, attachAvailable]); + } catch (e) { + this.$errors.failWithoutHelp(`The application ${this.$projectData.projectId} does not appear to be running on ${device.deviceInfo.displayName} or is not built with debugging enabled.`); + } - switch (receivedNotification) { - case alreadyConnected: - this.$errors.failWithoutHelp("A client is already connected."); - break; - case attachAvailable: - this.executeAttachAvailable(npc, timeout).wait(); - break; - case readyForAttach: - break; - } - }).future()(); + switch (receivedNotification) { + case this.$iOSNotification.alreadyConnected: + this.$errors.failWithoutHelp("A client is already connected."); + break; + case this.$iOSNotification.attachAvailable: + await this.executeAttachAvailable(npc, timeout); + break; + case this.$iOSNotification.readyForAttach: + break; + } } - public executeLaunchRequest(device: Mobile.IiOSDevice, timeout: number, readyForAttachTimeout: number, shouldBreak?: boolean): IFuture { - return (() => { - let npc = new iOSProxyServices.NotificationProxyClient(device, this.$injector); + public async executeLaunchRequest(device: Mobile.IiOSDevice, timeout: number, readyForAttachTimeout: number, shouldBreak?: boolean): Promise { + let npc = new iOSProxyServices.NotificationProxyClient(device, this.$injector); - try { - this.$iOSNotificationService.awaitNotification(npc, this.$iOSNotification.appLaunching, timeout).wait(); - process.nextTick(() => { - if(shouldBreak) { - npc.postNotificationAndAttachForData(this.$iOSNotification.waitForDebug ); - } - npc.postNotificationAndAttachForData(this.$iOSNotification.attachRequest); - }); + try { + await this.$iOSNotificationService.awaitNotification(npc, this.$iOSNotification.appLaunching, timeout); + process.nextTick(() => { + if (shouldBreak) { + npc.postNotificationAndAttachForData(this.$iOSNotification.waitForDebug); + } - this.$iOSNotificationService.awaitNotification(npc, this.$iOSNotification.readyForAttach, readyForAttachTimeout).wait(); - } catch(e) { - this.$logger.trace(`Timeout error: ${e}`); - this.$errors.failWithoutHelp("Timeout waiting for response from NativeScript runtime."); - } - }).future()(); + npc.postNotificationAndAttachForData(this.$iOSNotification.attachRequest); + }); + + await this.$iOSNotificationService.awaitNotification(npc, this.$iOSNotification.readyForAttach, readyForAttachTimeout); + } catch (e) { + this.$logger.trace(`Timeout error: ${e}`); + this.$errors.failWithoutHelp("Timeout waiting for response from NativeScript runtime."); + } } - private executeAttachAvailable(npc: Mobile.INotificationProxyClient, timeout: number): IFuture { - return (() => { - process.nextTick(() => npc.postNotificationAndAttachForData(this.$iOSNotification.attachRequest)); - try { - this.$iOSNotificationService.awaitNotification(npc, this.$iOSNotification.readyForAttach, timeout).wait(); - } catch (e) { - this.$errors.failWithoutHelp(`The application ${this.$projectData.projectId} timed out when performing the socket handshake.`); - } - }).future()(); + private async executeAttachAvailable(npc: Mobile.INotificationProxyClient, timeout: number): Promise { + process.nextTick(() => npc.postNotificationAndAttachForData(this.$iOSNotification.attachRequest)); + try { + await this.$iOSNotificationService.awaitNotification(npc, this.$iOSNotification.readyForAttach, timeout); + } catch (e) { + this.$errors.failWithoutHelp(`The application ${this.$projectData.projectId} timed out when performing the socket handshake.`); + } } } + $injector.register("iOSSocketRequestExecutor", IOSSocketRequestExecutor); diff --git a/lib/lockfile.ts b/lib/lockfile.ts index 4c7dd8f5ff..d9e0082274 100644 --- a/lib/lockfile.ts +++ b/lib/lockfile.ts @@ -1,4 +1,3 @@ -import Future = require("fibers/future"); import * as lockfile from "lockfile"; import * as path from "path"; @@ -12,44 +11,20 @@ export class LockFile implements ILockFile { private static LOCK_EXPIRY_PERIOD_SEC = 180; private static LOCK_PARAMS = { retryWait: 100, - retries: LockFile.LOCK_EXPIRY_PERIOD_SEC*10, - stale: LockFile.LOCK_EXPIRY_PERIOD_SEC*1000 + retries: LockFile.LOCK_EXPIRY_PERIOD_SEC * 10, + stale: LockFile.LOCK_EXPIRY_PERIOD_SEC * 1000 }; - public lock(): IFuture { - let future = new Future(); - lockfile.lock(this.lockFilePath, LockFile.LOCK_PARAMS, (err: Error) => { - if(err) { - future.throw(err); - } else { - future.return(); - } - }); - return future; + public lock(): void { + lockfile.lockSync(this.lockFilePath, LockFile.LOCK_PARAMS); } - public unlock(): IFuture { - let future = new Future(); - lockfile.unlock(this.lockFilePath, (err: Error) => { - if(err) { - future.throw(err); - } else { - future.return(); - } - }); - return future; + public unlock(): void { + lockfile.unlockSync(this.lockFilePath); } - public check(): IFuture { - let future = new Future(); - lockfile.check(this.lockFilePath, LockFile.LOCK_PARAMS, (err: Error, isLocked: boolean) => { - if(err) { - future.throw(err); - } else { - future.return(isLocked); - } - }); - return future; + public check(): boolean { + return lockfile.checkSync(this.lockFilePath, LockFile.LOCK_PARAMS); } } diff --git a/lib/mobile-platforms-capabilities.ts b/lib/mobile-platforms-capabilities.ts index 0cae98773f..fedcf13c2f 100644 --- a/lib/mobile-platforms-capabilities.ts +++ b/lib/mobile-platforms-capabilities.ts @@ -1,9 +1,7 @@ export class MobilePlatformsCapabilities implements Mobile.IPlatformsCapabilities { private platformCapabilities: IDictionary; - constructor(private $errors: IErrors) { } - - public getPlatformNames(): string[]{ + public getPlatformNames(): string[] { return _.keys(this.getAllCapabilities()); } diff --git a/lib/nativescript-cli.ts b/lib/nativescript-cli.ts index 4fc60a68d2..3d7434c688 100644 --- a/lib/nativescript-cli.ts +++ b/lib/nativescript-cli.ts @@ -1,13 +1,11 @@ require("./bootstrap"); -import * as fiber from "fibers"; -import Future = require("fibers/future"); import * as shelljs from "shelljs"; shelljs.config.silent = true; shelljs.config.fatal = true; -import {installUncaughtExceptionListener} from "./common/errors"; +import { installUncaughtExceptionListener } from "./common/errors"; installUncaughtExceptionListener(process.exit); -fiber(() => { +(async () => { let config: Config.IConfig = $injector.resolve("$config"); let err: IErrors = $injector.resolve("$errors"); err.printCallStack = config.DEBUG; @@ -18,11 +16,10 @@ fiber(() => { messages.pathsToMessageJsonFiles = [/* Place client-specific json message file paths here */]; if (process.argv[2] === "completion") { - commandDispatcher.completeCommand().wait(); + await commandDispatcher.completeCommand(); } else { - commandDispatcher.dispatchCommand().wait(); + await commandDispatcher.dispatchCommand(); } $injector.dispose(); - Future.assertNoFutureLeftBehind(); -}).run(); +})(); diff --git a/lib/node-package-manager.ts b/lib/node-package-manager.ts index 966b7a569e..4c9fc3a683 100644 --- a/lib/node-package-manager.ts +++ b/lib/node-package-manager.ts @@ -1,11 +1,5 @@ import * as path from "path"; -interface INpmOpts { - config?: any; - subCommandName?: string; - path?: string; -} - export class NodePackageManager implements INodePackageManager { constructor(private $fs: IFileSystem, private $hostInfo: IHostInfo, @@ -14,103 +8,99 @@ export class NodePackageManager implements INodePackageManager { private $logger: ILogger, private $options: IOptions) { } - public install(packageName: string, pathToSave: string, config?: any): IFuture { - return (() => { - if (this.$options.disableNpmInstall) { - return; - } - if (this.$options.ignoreScripts) { - config = config || {}; - config["ignore-scripts"] = true; - } + public async install(packageName: string, pathToSave: string, config?: any): Promise { + if (this.$options.disableNpmInstall) { + return; + } + if (this.$options.ignoreScripts) { + config = config || {}; + config["ignore-scripts"] = true; + } - let jsonContentBefore = this.$fs.readJson(path.join(pathToSave, "package.json")); - let dependenciesBefore = _.keys(jsonContentBefore.dependencies).concat(_.keys(jsonContentBefore.devDependencies)); + let jsonContentBefore = this.$fs.readJson(path.join(pathToSave, "package.json")); + let dependenciesBefore = _.keys(jsonContentBefore.dependencies).concat(_.keys(jsonContentBefore.devDependencies)); - let flags = this.getFlagsString(config, true); - let params = ["install"]; - if(packageName !== pathToSave) { - params.push(packageName); //because npm install ${pwd} on mac tries to install itself as a dependency (windows and linux have no such issues) - } - params = params.concat(flags); - let pwd = pathToSave; - //TODO: plamen5kov: workaround is here for a reason (remove whole file later) - if(this.$options.path) { - let relativePathFromCwdToSource = ""; - if(this.$options.frameworkPath) { - relativePathFromCwdToSource = path.relative(this.$options.frameworkPath, pathToSave); - if(this.$fs.exists(relativePathFromCwdToSource)) { - packageName = relativePathFromCwdToSource; - } + let flags = this.getFlagsString(config, true); + let params = ["install"]; + if (packageName !== pathToSave) { + params.push(packageName); //because npm install ${pwd} on mac tries to install itself as a dependency (windows and linux have no such issues) + } + params = params.concat(flags); + let pwd = pathToSave; + //TODO: plamen5kov: workaround is here for a reason (remove whole file later) + if (this.$options.path) { + let relativePathFromCwdToSource = ""; + if (this.$options.frameworkPath) { + relativePathFromCwdToSource = path.relative(this.$options.frameworkPath, pathToSave); + if (this.$fs.exists(relativePathFromCwdToSource)) { + packageName = relativePathFromCwdToSource; } } - try { - let spawnResult:ISpawnResult = this.$childProcess.spawnFromEvent(this.getNpmExecutableName(), params, "close", { cwd: pwd }).wait(); - this.$logger.out(spawnResult.stdout); - } catch (err) { - if (err.message && err.message.indexOf("EPEERINVALID") !== -1) { - // Not installed peer dependencies are treated by npm 2 as errors, but npm 3 treats them as warnings. - // We'll show them as warnings and let the user install them in case they are needed. - this.$logger.warn(err.message); - } else { - // All other errors should be handled by the caller code. - throw err; - } + } + try { + let spawnResult: ISpawnResult = await this.$childProcess.spawnFromEvent(this.getNpmExecutableName(), params, "close", { cwd: pwd }); + this.$logger.out(spawnResult.stdout); + } catch (err) { + if (err.message && err.message.indexOf("EPEERINVALID") !== -1) { + // Not installed peer dependencies are treated by npm 2 as errors, but npm 3 treats them as warnings. + // We'll show them as warnings and let the user install them in case they are needed. + this.$logger.warn(err.message); + } else { + // All other errors should be handled by the caller code. + throw err; } + } - let jsonContentAfter = this.$fs.readJson(path.join(pathToSave, "package.json")); - let dependenciesAfter = _.keys(jsonContentAfter.dependencies).concat(_.keys(jsonContentAfter.devDependencies)); - - /** This diff is done in case the installed pakcage is a URL address, a path to local directory or a .tgz file - * in these cases we don't have the package name and we can't rely on "npm install --json"" option - * to get the project name because we have to parse the output from the stdout and we have no controll over it (so other messages may be mangled in stdout) - * The solution is to compare package.json project dependencies before and after install and get the name of the installed package, - * even if it's installed through local path or URL. If command installes more than one package, only the package originally installed is returned. - */ - let dependencyDiff = _(jsonContentAfter.dependencies) - .omitBy((val: string, key: string) => jsonContentBefore && jsonContentBefore.dependencies && jsonContentBefore.dependencies[key] && jsonContentBefore.dependencies[key] === val) - .keys() - .value(); - - let devDependencyDiff = _(jsonContentAfter.devDependencies) - .omitBy((val: string, key: string) => jsonContentBefore && jsonContentBefore.devDependencies && jsonContentBefore.devDependencies[key] && jsonContentBefore.devDependencies[key] === val) - .keys() - .value(); - - let diff = dependencyDiff.concat(devDependencyDiff); - - if(diff.length <= 0 && dependenciesBefore.length === dependenciesAfter.length && packageName !== pathToSave) { - this.$errors.failWithoutHelp(`The plugin ${packageName} is already installed`); - } - if(diff.length <= 0 && dependenciesBefore.length !== dependenciesAfter.length) { - this.$errors.failWithoutHelp(`Couldn't install package correctly`); - } + let jsonContentAfter = this.$fs.readJson(path.join(pathToSave, "package.json")); + let dependenciesAfter = _.keys(jsonContentAfter.dependencies).concat(_.keys(jsonContentAfter.devDependencies)); + + /** This diff is done in case the installed pakcage is a URL address, a path to local directory or a .tgz file + * in these cases we don't have the package name and we can't rely on "npm install --json"" option + * to get the project name because we have to parse the output from the stdout and we have no controll over it (so other messages may be mangled in stdout) + * The solution is to compare package.json project dependencies before and after install and get the name of the installed package, + * even if it's installed through local path or URL. If command installes more than one package, only the package originally installed is returned. + */ + let dependencyDiff = _(jsonContentAfter.dependencies) + .omitBy((val: string, key: string) => jsonContentBefore && jsonContentBefore.dependencies && jsonContentBefore.dependencies[key] && jsonContentBefore.dependencies[key] === val) + .keys() + .value(); + + let devDependencyDiff = _(jsonContentAfter.devDependencies) + .omitBy((val: string, key: string) => jsonContentBefore && jsonContentBefore.devDependencies && jsonContentBefore.devDependencies[key] && jsonContentBefore.devDependencies[key] === val) + .keys() + .value(); + + let diff = dependencyDiff.concat(devDependencyDiff); + + if (diff.length <= 0 && dependenciesBefore.length === dependenciesAfter.length && packageName !== pathToSave) { + this.$errors.failWithoutHelp(`The plugin ${packageName} is already installed`); + } + if (diff.length <= 0 && dependenciesBefore.length !== dependenciesAfter.length) { + this.$errors.failWithoutHelp(`Couldn't install package correctly`); + } - return diff; - }).future()(); + return diff; } - public uninstall(packageName: string, config?: any, path?: string): IFuture { + public async uninstall(packageName: string, config?: any, path?: string): Promise { let flags = this.getFlagsString(config, false); return this.$childProcess.exec(`npm uninstall ${packageName} ${flags}`, { cwd: path }); } - public search(filter: string[], config: any): IFuture { + public async search(filter: string[], config: any): Promise { let args = (([filter] || [])).concat(config.silent); return this.$childProcess.exec(`npm search ${args.join(" ")}`); } - public view(packageName: string, config: any): IFuture { - return (() => { - let flags = this.getFlagsString(config, false); - let viewResult: any; - try { - viewResult = this.$childProcess.exec(`npm view ${packageName} ${flags}`).wait(); - } catch(e) { - this.$errors.failWithoutHelp(e); - } - return JSON.parse(viewResult); - }).future()(); + public async view(packageName: string, config: any): Promise { + let flags = this.getFlagsString(config, false); + let viewResult: any; + try { + viewResult = await this.$childProcess.exec(`npm view ${packageName} ${flags}`); + } catch (e) { + this.$errors.failWithoutHelp(e); + } + return JSON.parse(viewResult); } private getNpmExecutableName(): string { @@ -123,25 +113,26 @@ export class NodePackageManager implements INodePackageManager { return npmExecutableName; } - private getFlagsString(config: any, asArray: boolean) : any{ - let array:Array = []; - for(let flag in config) { + private getFlagsString(config: any, asArray: boolean): any { + let array: Array = []; + for (let flag in config) { if (flag === "global") { array.push(`--${flag}`); array.push(`${config[flag]}`); - } else if(config[flag]) { - if(flag==="dist-tags" || flag==="versions") { + } else if (config[flag]) { + if (flag === "dist-tags" || flag === "versions") { array.push(` ${flag}`); continue; } array.push(`--${flag}`); } } - if(asArray) { + if (asArray) { return array; } return array.join(" "); } } + $injector.register("npm", NodePackageManager); diff --git a/lib/npm-installation-manager.ts b/lib/npm-installation-manager.ts index 0f5b197cc6..2b3ac9581d 100644 --- a/lib/npm-installation-manager.ts +++ b/lib/npm-installation-manager.ts @@ -14,72 +14,61 @@ export class NpmInstallationManager implements INpmInstallationManager { private $staticConfig: IStaticConfig) { } - public getLatestVersion(packageName: string): IFuture { - return (() => { - return this.getVersion(packageName, constants.PackageVersion.LATEST).wait(); - }).future()(); + public async getLatestVersion(packageName: string): Promise { + return await this.getVersion(packageName, constants.PackageVersion.LATEST); } - public getNextVersion(packageName: string): IFuture { - return (() => { - return this.getVersion(packageName, constants.PackageVersion.NEXT).wait(); - }).future()(); + public async getNextVersion(packageName: string): Promise { + return await this.getVersion(packageName, constants.PackageVersion.NEXT); } - public getLatestCompatibleVersion(packageName: string): IFuture { - return (() => { + public async getLatestCompatibleVersion(packageName: string): Promise { - let cliVersionRange = `~${this.$staticConfig.version}`; - let latestVersion = this.getLatestVersion(packageName).wait(); - if (semver.satisfies(latestVersion, cliVersionRange)) { - return latestVersion; - } - let data = this.$npm.view(packageName, { json: true, "versions": true }).wait(); + let cliVersionRange = `~${this.$staticConfig.version}`; + let latestVersion = await this.getLatestVersion(packageName); + if (semver.satisfies(latestVersion, cliVersionRange)) { + return latestVersion; + } + let data = await this.$npm.view(packageName, { json: true, "versions": true }); - return semver.maxSatisfying(data, cliVersionRange) || latestVersion; - }).future()(); + return semver.maxSatisfying(data, cliVersionRange) || latestVersion; } - public install(packageName: string, projectDir: string, opts?: INpmInstallOptions): IFuture { - return (() => { + public async install(packageName: string, projectDir: string, opts?: INpmInstallOptions): Promise { - try { - let packageToInstall = this.$options.frameworkPath || packageName; - let pathToSave = projectDir; - let version = (opts && opts.version) || null; - let dependencyType = (opts && opts.dependencyType) || null; - - return this.installCore(packageToInstall, pathToSave, version, dependencyType).wait(); - } catch (error) { - this.$logger.debug(error); - this.$errors.fail("%s. Error: %s", NpmInstallationManager.NPM_LOAD_FAILED, error); - } + try { + let packageToInstall = this.$options.frameworkPath || packageName; + let pathToSave = projectDir; + let version = (opts && opts.version) || null; + let dependencyType = (opts && opts.dependencyType) || null; - }).future()(); + return await this.installCore(packageToInstall, pathToSave, version, dependencyType); + } catch (error) { + this.$logger.debug(error); + this.$errors.fail("%s. Error: %s", NpmInstallationManager.NPM_LOAD_FAILED, error); + } } - public getInspectorFromCache(inspectorNpmPackageName: string, projectDir: string): IFuture { - return (() => { - let inspectorPath = path.join(projectDir, "node_modules", inspectorNpmPackageName); - - // local installation takes precedence over cache - if (!this.inspectorAlreadyInstalled(inspectorPath)) { - let cachepath = this.$childProcess.exec("npm get cache").wait().trim(); - let version = this.getLatestCompatibleVersion(inspectorNpmPackageName).wait(); - let pathToPackageInCache = path.join(cachepath, inspectorNpmPackageName, version); - let pathToUnzippedInspector = path.join(pathToPackageInCache, "package"); - - if (!this.$fs.exists(pathToPackageInCache)) { - this.$childProcess.exec(`npm cache add ${inspectorNpmPackageName}@${version}`).wait(); - let inspectorTgzPathInCache = path.join(pathToPackageInCache, "package.tgz"); - this.$childProcess.exec(`tar -xf ${inspectorTgzPathInCache} -C ${pathToPackageInCache}`).wait(); - this.$childProcess.exec(`npm install --prefix ${pathToUnzippedInspector}`).wait(); - } - this.$logger.out("Using inspector from cache."); - return pathToUnzippedInspector; + public async getInspectorFromCache(inspectorNpmPackageName: string, projectDir: string): Promise { + let inspectorPath = path.join(projectDir, "node_modules", inspectorNpmPackageName); + + // local installation takes precedence over cache + if (!this.inspectorAlreadyInstalled(inspectorPath)) { + let cachepath = (await this.$childProcess.exec("npm get cache")).trim(); + let version = await this.getLatestCompatibleVersion(inspectorNpmPackageName); + let pathToPackageInCache = path.join(cachepath, inspectorNpmPackageName, version); + let pathToUnzippedInspector = path.join(pathToPackageInCache, "package"); + + if (!this.$fs.exists(pathToPackageInCache)) { + await this.$childProcess.exec(`npm cache add ${inspectorNpmPackageName}@${version}`); + let inspectorTgzPathInCache = path.join(pathToPackageInCache, "package.tgz"); + await this.$childProcess.exec(`tar -xf ${inspectorTgzPathInCache} -C ${pathToPackageInCache}`); + await this.$childProcess.exec(`npm install --prefix ${pathToUnzippedInspector}`); } - return inspectorPath; - }).future()(); + this.$logger.out("Using inspector from cache."); + return pathToUnzippedInspector; + } + return inspectorPath; } private inspectorAlreadyInstalled(pathToInspector: string): Boolean { @@ -89,28 +78,26 @@ export class NpmInstallationManager implements INpmInstallationManager { return false; } - private installCore(packageName: string, pathToSave: string, version: string, dependencyType: string): IFuture { - return (() => { - const possiblePackageName = path.resolve(packageName); - if (this.$fs.exists(possiblePackageName)) { - packageName = possiblePackageName; - } - if (packageName.indexOf(".tgz") >= 0) { - version = null; - } - // check if the packageName is url or local file and if it is, let npm install deal with the version - if (this.isURL(packageName) || this.$fs.exists(packageName)) { - version = null; - } else { - version = version || this.getLatestCompatibleVersion(packageName).wait(); - } + private async installCore(packageName: string, pathToSave: string, version: string, dependencyType: string): Promise { + const possiblePackageName = path.resolve(packageName); + if (this.$fs.exists(possiblePackageName)) { + packageName = possiblePackageName; + } + if (packageName.indexOf(".tgz") >= 0) { + version = null; + } + // check if the packageName is url or local file and if it is, let npm install deal with the version + if (this.isURL(packageName) || this.$fs.exists(packageName)) { + version = null; + } else { + version = version || await this.getLatestCompatibleVersion(packageName); + } - let installedModuleNames = this.npmInstall(packageName, pathToSave, version, dependencyType).wait(); - let installedPackageName = installedModuleNames[0]; + let installedModuleNames = await this.npmInstall(packageName, pathToSave, version, dependencyType); + let installedPackageName = installedModuleNames[0]; - let pathToInstalledPackage = path.join(pathToSave, "node_modules", installedPackageName); - return pathToInstalledPackage; - }).future()(); + let pathToInstalledPackage = path.join(pathToSave, "node_modules", installedPackageName); + return pathToInstalledPackage; } private isURL(str: string) { @@ -119,33 +106,29 @@ export class NpmInstallationManager implements INpmInstallationManager { return str.length < 2083 && url.test(str); } - private npmInstall(packageName: string, pathToSave: string, version: string, dependencyType: string): IFuture { - return (() => { - this.$logger.out("Installing ", packageName); + private async npmInstall(packageName: string, pathToSave: string, version: string, dependencyType: string): Promise { + this.$logger.out("Installing ", packageName); - packageName = packageName + (version ? `@${version}` : ""); + packageName = packageName + (version ? `@${version}` : ""); - let npmOptions: any = { silent: true }; + let npmOptions: any = { silent: true }; - if (dependencyType) { - npmOptions[dependencyType] = true; - } + if (dependencyType) { + npmOptions[dependencyType] = true; + } - return this.$npm.install(packageName, pathToSave, npmOptions).wait(); - }).future()(); + return await this.$npm.install(packageName, pathToSave, npmOptions); } /** * This function must not be used with packageName being a URL or local file, * because npm view doens't work with those */ - private getVersion(packageName: string, version: string): IFuture { - return (() => { - let data: any = this.$npm.view(packageName, { json: true, "dist-tags": true }).wait(); - this.$logger.trace("Using version %s. ", data[version]); + private async getVersion(packageName: string, version: string): Promise { + let data: any = await this.$npm.view(packageName, { json: true, "dist-tags": true }); + this.$logger.trace("Using version %s. ", data[version]); - return data[version]; - }).future()(); + return data[version]; } } $injector.register("npmInstallationManager", NpmInstallationManager); diff --git a/lib/platform-command-param.ts b/lib/platform-command-param.ts index d397b12103..7fff623f30 100644 --- a/lib/platform-command-param.ts +++ b/lib/platform-command-param.ts @@ -1,11 +1,9 @@ export class PlatformCommandParameter implements ICommandParameter { constructor(private $platformService: IPlatformService) { } mandatory = true; - validate(value: string): IFuture { - return (() => { - this.$platformService.validatePlatform(value); - return true; - }).future()(); + async validate(value: string): Promise { + this.$platformService.validatePlatform(value); + return true; } } $injector.register("platformCommandParameter", PlatformCommandParameter); diff --git a/lib/platforms-data.ts b/lib/platforms-data.ts index 621c4b1f46..44b234eea7 100644 --- a/lib/platforms-data.ts +++ b/lib/platforms-data.ts @@ -1,5 +1,5 @@ export class PlatformsData implements IPlatformsData { - private platformsData : { [index: string]: any } = {}; + private platformsData: { [index: string]: any } = {}; constructor($androidProjectService: IPlatformProjectService, $iOSProjectService: IPlatformProjectService) { diff --git a/lib/providers/commands-service-provider.ts b/lib/providers/commands-service-provider.ts index 5fde695d75..a4f465efbd 100644 --- a/lib/providers/commands-service-provider.ts +++ b/lib/providers/commands-service-provider.ts @@ -1,14 +1,12 @@ -import Future = require("fibers/future"); - export class CommandsServiceProvider implements ICommandsServiceProvider { public dynamicCommandsPrefix = ""; - public getDynamicCommands(): IFuture { - return Future.fromResult([]); + public async getDynamicCommands(): Promise { + return []; } - public generateDynamicCommands(): IFuture { - return Future.fromResult(); + public async generateDynamicCommands(): Promise { + return ; } public registerDynamicSubCommands(): void { diff --git a/lib/providers/device-app-data-provider.ts b/lib/providers/device-app-data-provider.ts index 2d36663348..1ee89f013f 100644 --- a/lib/providers/device-app-data-provider.ts +++ b/lib/providers/device-app-data-provider.ts @@ -1,13 +1,12 @@ import * as deviceAppDataBaseLib from "../common/mobile/device-app-data/device-app-data-base"; -import Future = require("fibers/future"); import * as path from "path"; -import {AndroidDeviceHashService} from "../common/mobile/android/android-device-hash-service"; -import {DeviceAndroidDebugBridge} from "../common/mobile/android/device-android-debug-bridge"; +import { AndroidDeviceHashService } from "../common/mobile/android/android-device-hash-service"; +import { DeviceAndroidDebugBridge } from "../common/mobile/android/device-android-debug-bridge"; const SYNC_DIR_NAME = "sync"; const FULLSYNC_DIR_NAME = "fullsync"; -export class IOSAppIdentifier extends deviceAppDataBaseLib.DeviceAppDataBase implements Mobile.IDeviceAppData { +export class IOSAppIdentifier extends deviceAppDataBaseLib.DeviceAppDataBase implements Mobile.IDeviceAppData { private static DEVICE_PROJECT_ROOT_PATH = "Library/Application Support/LiveSync/app"; private _deviceProjectRootPath: string = null; @@ -18,7 +17,7 @@ export class IOSAppIdentifier extends deviceAppDataBaseLib.DeviceAppDataBase imp super(_appIdentifier); } - public get deviceProjectRootPath(): string { + public async getDeviceProjectRootPath(): Promise { if (!this._deviceProjectRootPath) { if (this.device.isEmulator) { let applicationPath = this.$iOSSimResolver.iOSSim.getApplicationPath(this.device.deviceInfo.identifier, this.appIdentifier); @@ -28,7 +27,7 @@ export class IOSAppIdentifier extends deviceAppDataBaseLib.DeviceAppDataBase imp } } - return this.getDeviceProjectRootPath(this._deviceProjectRootPath); + return this._getDeviceProjectRootPath(this._deviceProjectRootPath); } public get deviceSyncZipPath(): string { @@ -39,8 +38,8 @@ export class IOSAppIdentifier extends deviceAppDataBaseLib.DeviceAppDataBase imp } } - public isLiveSyncSupported(): IFuture { - return Future.fromResult(true); + public async isLiveSyncSupported(): Promise { + return true; } } @@ -55,26 +54,24 @@ export class AndroidAppIdentifier extends deviceAppDataBaseLib.DeviceAppDataBase private _deviceProjectRootPath: string; - public get deviceProjectRootPath(): string { - if(!this._deviceProjectRootPath) { - let syncFolderName = this.getSyncFolderName().wait(); + public async getDeviceProjectRootPath(): Promise { + if (!this._deviceProjectRootPath) { + let syncFolderName = await this.getSyncFolderName(); this._deviceProjectRootPath = `/data/local/tmp/${this.appIdentifier}/${syncFolderName}`; } return this._deviceProjectRootPath; } - public isLiveSyncSupported(): IFuture { - return Future.fromResult(true); + public async isLiveSyncSupported(): Promise { + return true; } - private getSyncFolderName(): IFuture { - return ((): string =>{ - let adb = this.$injector.resolve(DeviceAndroidDebugBridge, { identifier: this.device.deviceInfo.identifier }); - let deviceHashService = this.$injector.resolve(AndroidDeviceHashService, {adb: adb, appIdentifier: this.appIdentifier}); - let hashFile = this.$options.force ? null : deviceHashService.doesShasumFileExistsOnDevice().wait(); - return this.$options.watch || hashFile ? SYNC_DIR_NAME : FULLSYNC_DIR_NAME; - }).future()(); + private async getSyncFolderName(): Promise { + let adb = this.$injector.resolve(DeviceAndroidDebugBridge, { identifier: this.device.deviceInfo.identifier }); + let deviceHashService: AndroidDeviceHashService = this.$injector.resolve(AndroidDeviceHashService, { adb: adb, appIdentifier: this.appIdentifier }); + let hashFile = this.$options.force ? null : await deviceHashService.doesShasumFileExistsOnDevice(); + return this.$options.watch || hashFile ? SYNC_DIR_NAME : FULLSYNC_DIR_NAME; } } diff --git a/lib/providers/livesync-provider.ts b/lib/providers/livesync-provider.ts index 2210b16105..4c1d1f4142 100644 --- a/lib/providers/livesync-provider.ts +++ b/lib/providers/livesync-provider.ts @@ -2,30 +2,29 @@ import * as path from "path"; import * as temp from "temp"; export class LiveSyncProvider implements ILiveSyncProvider { - constructor(private $androidLiveSyncServiceLocator: {factory: Function}, - private $iosLiveSyncServiceLocator: {factory: Function}, + constructor(private $androidLiveSyncServiceLocator: { factory: Function }, + private $iosLiveSyncServiceLocator: { factory: Function }, private $platformService: IPlatformService, private $platformsData: IPlatformsData, private $logger: ILogger, private $childProcess: IChildProcess, - private $options: IOptions, - private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants) { } + private $options: IOptions) { } - private static FAST_SYNC_FILE_EXTENSIONS = [".css", ".xml" ,".html"]; + private static FAST_SYNC_FILE_EXTENSIONS = [".css", ".xml", ".html"]; private deviceSpecificLiveSyncServicesCache: IDictionary = {}; public get deviceSpecificLiveSyncServices(): IDictionary { return { android: (_device: Mobile.IDevice, $injector: IInjector) => { - if(!this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier]) { - this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier] = $injector.resolve(this.$androidLiveSyncServiceLocator.factory, {_device: _device}); + if (!this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier]) { + this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier] = $injector.resolve(this.$androidLiveSyncServiceLocator.factory, { _device: _device }); } return this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier]; }, ios: (_device: Mobile.IDevice, $injector: IInjector) => { - if(!this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier]) { - this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier] = $injector.resolve(this.$iosLiveSyncServiceLocator.factory, {_device: _device}); + if (!this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier]) { + this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier] = $injector.resolve(this.$iosLiveSyncServiceLocator.factory, { _device: _device }); } return this.deviceSpecificLiveSyncServicesCache[_device.deviceInfo.identifier]; @@ -33,22 +32,18 @@ export class LiveSyncProvider implements ILiveSyncProvider { }; } - public buildForDevice(device: Mobile.IDevice): IFuture { - return (() => { - this.$platformService.buildPlatform(device.deviceInfo.platform, {buildForDevice: !device.isEmulator}).wait(); - let platformData = this.$platformsData.getPlatformData(device.deviceInfo.platform); - if (device.isEmulator) { - return this.$platformService.getLatestApplicationPackageForEmulator(platformData).packageName; - } + public async buildForDevice(device: Mobile.IDevice): Promise { + this.$platformService.buildPlatform(device.deviceInfo.platform, { buildForDevice: ! await device.isEmulator }); + let platformData = this.$platformsData.getPlatformData(device.deviceInfo.platform); + if (device.isEmulator) { + return this.$platformService.getLatestApplicationPackageForEmulator(platformData).packageName; + } - return this.$platformService.getLatestApplicationPackageForDevice(platformData).packageName; - }).future()(); + return this.$platformService.getLatestApplicationPackageForDevice(platformData).packageName; } - public preparePlatformForSync(platform: string): IFuture { - return (() => { - this.$platformService.preparePlatform(platform).wait(); - }).future()(); + public async preparePlatformForSync(platform: string): Promise { + await this.$platformService.preparePlatform(platform); } public canExecuteFastSync(filePath: string, platform: string): boolean { @@ -57,30 +52,28 @@ export class LiveSyncProvider implements ILiveSyncProvider { return _.includes(fastSyncFileExtensions, path.extname(filePath)); } - public transferFiles(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], projectFilesPath: string, isFullSync: boolean): IFuture { - return (() => { - if (deviceAppData.platform.toLowerCase() === "android" || !deviceAppData.deviceSyncZipPath || !isFullSync) { - deviceAppData.device.fileSystem.transferFiles(deviceAppData, localToDevicePaths).wait(); - } else { - temp.track(); - let tempZip = temp.path({prefix: "sync", suffix: ".zip"}); - this.$logger.trace("Creating zip file: " + tempZip); - - if (this.$options.syncAllFiles) { - this.$childProcess.spawnFromEvent("zip", [ "-r", "-0", tempZip, "app" ], "close", { cwd: path.dirname(projectFilesPath) }).wait(); - } else { - this.$logger.info("Skipping node_modules folder! Use the syncAllFiles option to sync files from this folder."); - this.$childProcess.spawnFromEvent("zip", [ "-r", "-0", tempZip, "app", "-x", "app/tns_modules/*" ], "close", { cwd: path.dirname(projectFilesPath) }).wait(); - } + public async transferFiles(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], projectFilesPath: string, isFullSync: boolean): Promise { + if (deviceAppData.platform.toLowerCase() === "android" || !deviceAppData.deviceSyncZipPath || !isFullSync) { + await deviceAppData.device.fileSystem.transferFiles(deviceAppData, localToDevicePaths); + } else { + temp.track(); + let tempZip = temp.path({ prefix: "sync", suffix: ".zip" }); + this.$logger.trace("Creating zip file: " + tempZip); - deviceAppData.device.fileSystem.transferFiles(deviceAppData, [{ - getLocalPath: () => tempZip, - getDevicePath: () => deviceAppData.deviceSyncZipPath, - getRelativeToProjectBasePath: () => "../sync.zip", - deviceProjectRootPath: deviceAppData.deviceProjectRootPath - }]).wait(); + if (this.$options.syncAllFiles) { + await this.$childProcess.spawnFromEvent("zip", ["-r", "-0", tempZip, "app"], "close", { cwd: path.dirname(projectFilesPath) }); + } else { + this.$logger.info("Skipping node_modules folder! Use the syncAllFiles option to sync files from this folder."); + await this.$childProcess.spawnFromEvent("zip", ["-r", "-0", tempZip, "app", "-x", "app/tns_modules/*"], "close", { cwd: path.dirname(projectFilesPath) }); } - }).future()(); + + deviceAppData.device.fileSystem.transferFiles(deviceAppData, [{ + getLocalPath: () => tempZip, + getDevicePath: () => deviceAppData.deviceSyncZipPath, + getRelativeToProjectBasePath: () => "../sync.zip", + deviceProjectRootPath: await deviceAppData.getDeviceProjectRootPath() + }]); + } } } $injector.register("liveSyncProvider", LiveSyncProvider); diff --git a/lib/services/analytics-service.ts b/lib/services/analytics-service.ts index 563c3b551c..e37ef56004 100644 --- a/lib/services/analytics-service.ts +++ b/lib/services/analytics-service.ts @@ -1,4 +1,4 @@ -import {AnalyticsServiceBase} from "../common/services/analytics-service-base"; +import { AnalyticsServiceBase } from "../common/services/analytics-service-base"; export class AnalyticsService extends AnalyticsServiceBase implements IAnalyticsService { private static ANALYTICS_FEATURE_USAGE_TRACKING_API_KEY = "9912cff308334c6d9ad9c33f76a983e3"; @@ -6,23 +6,20 @@ export class AnalyticsService extends AnalyticsServiceBase implements IAnalytics constructor(protected $logger: ILogger, protected $options: IOptions, $staticConfig: Config.IStaticConfig, - $errors: IErrors, $prompter: IPrompter, $userSettingsService: UserSettings.IUserSettingsService, $analyticsSettingsService: IAnalyticsSettingsService, $progressIndicator: IProgressIndicator, $osInfo: IOsInfo) { - super($logger, $options, $staticConfig, $errors, $prompter, $userSettingsService, $analyticsSettingsService, $progressIndicator, $osInfo); + super($logger, $options, $staticConfig, $prompter, $userSettingsService, $analyticsSettingsService, $progressIndicator, $osInfo); } - protected checkConsentCore(trackFeatureUsage: boolean): IFuture { - return (() => { - this.restartEqatecMonitor(AnalyticsService.ANALYTICS_FEATURE_USAGE_TRACKING_API_KEY).wait(); - super.checkConsentCore(trackFeatureUsage).wait(); + protected async checkConsentCore(trackFeatureUsage: boolean): Promise { + await this.restartEqatecMonitor(AnalyticsService.ANALYTICS_FEATURE_USAGE_TRACKING_API_KEY); + await super.checkConsentCore(trackFeatureUsage); - // Stop the monitor, so correct API_KEY will be used when features are tracked. - this.tryStopEqatecMonitor(); - }).future()(); + // Stop the monitor, so correct API_KEY will be used when features are tracked. + this.tryStopEqatecMonitor(); } } diff --git a/lib/services/analytics-settings-service.ts b/lib/services/analytics-settings-service.ts index fd99fda4c6..ef21d53a36 100644 --- a/lib/services/analytics-settings-service.ts +++ b/lib/services/analytics-settings-service.ts @@ -8,22 +8,20 @@ class AnalyticsSettingsService implements IAnalyticsSettingsService { private $staticConfig: IStaticConfig, private $logger: ILogger) { } - public canDoRequest(): IFuture { - return (() => { return true; }).future()(); + public async canDoRequest(): Promise { + return true; } - public getUserId(): IFuture { - return (() => { - let currentUserId = this.$userSettingsService.getSettingValue("USER_ID").wait(); - if(!currentUserId) { - currentUserId = createGUID(false); + public async getUserId(): Promise { + let currentUserId = await this.$userSettingsService.getSettingValue("USER_ID"); + if (!currentUserId) { + currentUserId = createGUID(false); - this.$logger.trace(`Setting new USER_ID: ${currentUserId}.`); - this.$userSettingsService.saveSetting("USER_ID", currentUserId).wait(); - } + this.$logger.trace(`Setting new USER_ID: ${currentUserId}.`); + await this.$userSettingsService.saveSetting("USER_ID", currentUserId); + } - return currentUserId; - }).future()(); + return currentUserId; } public getClientName(): string { @@ -34,18 +32,18 @@ class AnalyticsSettingsService implements IAnalyticsSettingsService { return "http://www.telerik.com/company/privacy-policy"; } - public getUserSessionsCount(projectName: string): IFuture { - return (() => { - let oldSessionCount = this.$userSettingsService.getSettingValue(AnalyticsSettingsService.SESSIONS_STARTED_OBSOLETE_KEY).wait(); - if(oldSessionCount) { - // remove the old property for sessions count - this.$userSettingsService.removeSetting(AnalyticsSettingsService.SESSIONS_STARTED_OBSOLETE_KEY).wait(); - } - return this.$userSettingsService.getSettingValue(this.getSessionsProjectKey(projectName)).wait() || oldSessionCount || 0; - }).future()(); + public async getUserSessionsCount(projectName: string): Promise { + let oldSessionCount = await this.$userSettingsService.getSettingValue(AnalyticsSettingsService.SESSIONS_STARTED_OBSOLETE_KEY); + + if (oldSessionCount) { + // remove the old property for sessions count + await this.$userSettingsService.removeSetting(AnalyticsSettingsService.SESSIONS_STARTED_OBSOLETE_KEY); + } + + return await this.$userSettingsService.getSettingValue(this.getSessionsProjectKey(projectName)) || oldSessionCount || 0; } - public setUserSessionsCount(count: number, projectName: string): IFuture { + public async setUserSessionsCount(count: number, projectName: string): Promise { return this.$userSettingsService.saveSetting(this.getSessionsProjectKey(projectName), count); } diff --git a/lib/services/android-debug-service.ts b/lib/services/android-debug-service.ts index 1403a8af60..35a754fc7b 100644 --- a/lib/services/android-debug-service.ts +++ b/lib/services/android-debug-service.ts @@ -1,8 +1,7 @@ import * as net from "net"; import * as os from "os"; -import Future = require("fibers/future"); import { sleep } from "../common/helpers"; -import {ChildProcess} from "child_process"; +import { ChildProcess } from "child_process"; class AndroidDebugService implements IDebugService { private _device: Mobile.IAndroidDevice = null; @@ -14,15 +13,13 @@ class AndroidDebugService implements IDebugService { private $projectData: IProjectData, private $logger: ILogger, private $options: IOptions, - private $childProcess: IChildProcess, - private $hostInfo: IHostInfo, private $errors: IErrors, - private $opener: IOpener, private $config: IConfiguration, - private $processService: IProcessService, private $androidDeviceDiscovery: Mobile.IDeviceDiscovery) { } - public get platform() { return "android"; } + public get platform() { + return "android"; + } private get device(): Mobile.IAndroidDevice { return this._device; @@ -32,193 +29,195 @@ class AndroidDebugService implements IDebugService { this._device = newDevice; } - public debug(): IFuture { + public async debug(): Promise { return this.$options.emulator ? this.debugOnEmulator() : this.debugOnDevice(); } - private debugOnEmulator(): IFuture { - return (() => { - // 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. - this.$androidDeviceDiscovery.startLookingForDevices().wait(); - this.debugOnDevice().wait(); - }).future()(); + private async debugOnEmulator(): 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. + await this.$androidDeviceDiscovery.startLookingForDevices(); + await this.debugOnDevice(); } - private isPortAvailable(candidatePort: number): IFuture { - let future = new Future(); - let server = net.createServer(); - server.on("error", (err: Error) => { - future.return(false); - }); - server.once("close", () => { - if (!future.isResolved()) { // "close" will be emitted right after "error" - future.return(true); - } - }); - server.on("listening", (err: Error) => { - if (err) { - future.return(false); - } - server.close(); - }); - server.listen(candidatePort, "localhost"); + private isPortAvailable(candidatePort: number): Promise { + return new Promise((resolve, reject) => { + let isResolved = false; + let server = net.createServer(); - return future; + server.on("error", (err: Error) => { + if (!isResolved) { + isResolved = true; + resolve(false); + } + }); + + server.once("close", () => { + if (!isResolved) { // "close" will be emitted right after "error" + isResolved = true; + resolve(true); + } + }); + + server.on("listening", (err: Error) => { + if (err && !isResolved) { + isResolved = true; + resolve(false); + } + + server.close(); + }); + + server.listen(candidatePort, "localhost"); + + }); } - private getForwardedLocalDebugPortForPackageName(deviceId: string, packageName: string): IFuture { - return (() => { - let port = -1; - let forwardsResult = this.device.adb.executeCommand(["forward", "--list"]).wait(); + private async getForwardedLocalDebugPortForPackageName(deviceId: string, packageName: string): Promise { + let port = -1; + let forwardsResult = await this.device.adb.executeCommand(["forward", "--list"]); - let unixSocketName = `${packageName}-inspectorServer`; + let unixSocketName = `${packageName}-inspectorServer`; - //matches 123a188909e6czzc tcp:40001 localabstract:org.nativescript.testUnixSockets-debug - let regexp = new RegExp(`(?:${deviceId} tcp:)([\\d]+)(?= localabstract:${unixSocketName})`, "g"); - let match = regexp.exec(forwardsResult); - if (match) { - port = parseInt(match[1]); - } else { - let candidatePort = 40000; - for (; !this.isPortAvailable(candidatePort).wait(); ++candidatePort) { - if (candidatePort > 65534) { - this.$errors.failWithoutHelp("Unable to find free local port."); - } - } - port = candidatePort; + //matches 123a188909e6czzc tcp:40001 localabstract:org.nativescript.testUnixSockets-debug + let regexp = new RegExp(`(?:${deviceId} tcp:)([\\d]+)(?= localabstract:${unixSocketName})`, "g"); + let match = regexp.exec(forwardsResult); - this.unixSocketForward(port, `${unixSocketName}`).wait(); + if (match) { + port = parseInt(match[1]); + } else { + let candidatePort = 40000; + + for (; ! await this.isPortAvailable(candidatePort); ++candidatePort) { + if (candidatePort > 65534) { + this.$errors.failWithoutHelp("Unable to find free local port."); + } } - return port; - }).future()(); + port = candidatePort; + + await this.unixSocketForward(port, `${unixSocketName}`); + } + + return port; } - private unixSocketForward(local: number, remote: string): IFuture { + private async unixSocketForward(local: number, remote: string): Promise { return this.device.adb.executeCommand(["forward", `tcp:${local}`, `localabstract:${remote}`]); } - private debugOnDevice(): IFuture { - return (() => { - let packageFile = ""; + private async debugOnDevice(): Promise { + let packageFile = ""; - if (!this.$options.start && !this.$options.emulator && !this.$options.getPort) { - let cachedDeviceOption = this.$options.forDevice; - this.$options.forDevice = true; - this.$options.forDevice = !!cachedDeviceOption; + if (!this.$options.start && !this.$options.emulator && !this.$options.getPort) { + let cachedDeviceOption = this.$options.forDevice; + this.$options.forDevice = true; - let platformData = this.$platformsData.getPlatformData(this.platform); - packageFile = this.$platformService.getLatestApplicationPackageForDevice(platformData).packageName; - this.$logger.out("Using ", packageFile); - } + this.$options.forDevice = !!cachedDeviceOption; + + let platformData = this.$platformsData.getPlatformData(this.platform); + packageFile = this.$platformService.getLatestApplicationPackageForDevice(platformData).packageName; + this.$logger.out("Using ", packageFile); + } + + await this.$devicesService.initialize({ platform: this.platform, deviceId: this.$options.device }); - this.$devicesService.initialize({ platform: this.platform, deviceId: this.$options.device }).wait(); - let action = (device: Mobile.IAndroidDevice): IFuture => { return this.debugCore(device, packageFile, this.$projectData.projectId); }; - this.$devicesService.execute(action).wait(); + let action = (device: Mobile.IAndroidDevice): Promise => this.debugCore(device, packageFile, this.$projectData.projectId); - }).future()(); + await this.$devicesService.execute(action); } - private debugCore(device: Mobile.IAndroidDevice, packageFile: string, packageName: string): IFuture { - return (() => { - this.device = device; + private async debugCore(device: Mobile.IAndroidDevice, packageFile: string, packageName: string): Promise { + this.device = device; - if (this.$options.getPort) { - this.printDebugPort(device.deviceInfo.identifier, packageName).wait(); - } else if (this.$options.start) { - this.attachDebugger(device.deviceInfo.identifier, packageName).wait(); - } else if (this.$options.stop) { - this.detachDebugger(packageName).wait(); - } else { - this.startAppWithDebugger(packageFile, packageName).wait(); - this.attachDebugger(device.deviceInfo.identifier, packageName).wait(); - } - }).future()(); + if (this.$options.getPort) { + await this.printDebugPort(device.deviceInfo.identifier, packageName); + } else if (this.$options.start) { + await this.attachDebugger(device.deviceInfo.identifier, packageName); + } else if (this.$options.stop) { + await this.detachDebugger(packageName); + } else { + await this.startAppWithDebugger(packageFile, packageName); + await this.attachDebugger(device.deviceInfo.identifier, packageName); + } } - private printDebugPort(deviceId: string, packageName: string): IFuture { - return (() => { - let port = this.getForwardedLocalDebugPortForPackageName(deviceId, packageName).wait(); - this.$logger.info("device: " + deviceId + " debug port: " + port + "\n"); - }).future()(); + private async printDebugPort(deviceId: string, packageName: string): Promise { + let port = await this.getForwardedLocalDebugPortForPackageName(deviceId, packageName); + this.$logger.info("device: " + deviceId + " debug port: " + port + "\n"); } - private attachDebugger(deviceId: string, packageName: string): IFuture { - return (() => { - let startDebuggerCommand = ["am", "broadcast", "-a", `\"${packageName}-debug\"`, "--ez", "enable", "true"]; - this.device.adb.executeShellCommand(startDebuggerCommand).wait(); + private async attachDebugger(deviceId: string, packageName: string): Promise { + let startDebuggerCommand = ["am", "broadcast", "-a", `\"${packageName}-debug\"`, "--ez", "enable", "true"]; + await this.device.adb.executeShellCommand(startDebuggerCommand); - if (this.$options.client) { - let port = this.getForwardedLocalDebugPortForPackageName(deviceId, packageName).wait(); - this.startDebuggerClient(port).wait(); - } - }).future()(); + if (this.$options.client) { + let port = await this.getForwardedLocalDebugPortForPackageName(deviceId, packageName); + this.startDebuggerClient(port); + } } - private detachDebugger(packageName: string): IFuture { + private detachDebugger(packageName: string): Promise { return this.device.adb.executeShellCommand(["am", "broadcast", "-a", `${packageName}-debug`, "--ez", "enable", "false"]); } - private startAppWithDebugger(packageFile: string, packageName: string): IFuture { - return (() => { - if (!this.$options.emulator && !this.$config.debugLivesync) { - this.device.applicationManager.uninstallApplication(packageName).wait(); - this.device.applicationManager.installApplication(packageFile).wait(); - } - this.debugStartCore().wait(); - }).future()(); + private async startAppWithDebugger(packageFile: string, packageName: string): Promise { + if (!this.$options.emulator && !this.$config.debugLivesync) { + await this.device.applicationManager.uninstallApplication(packageName); + await this.device.applicationManager.installApplication(packageFile); + } + await this.debugStartCore(); } - public debugStart(): IFuture { - return (() => { - this.$devicesService.initialize({ platform: this.platform, deviceId: this.$options.device }).wait(); - let action = (device: Mobile.IAndroidDevice): IFuture => { - this.device = device; - return this.debugStartCore(); - }; - this.$devicesService.execute(action).wait(); - }).future()(); + public async debugStart(): Promise { + await this.$devicesService.initialize({ platform: this.platform, deviceId: this.$options.device }); + let action = (device: Mobile.IAndroidDevice): Promise => { + this.device = device; + return this.debugStartCore(); + }; + + await this.$devicesService.execute(action); } - public debugStop(): IFuture { + public async debugStop(): Promise { this.stopDebuggerClient(); - return Future.fromResult(); + return; } - private debugStartCore(): IFuture { - return (() => { - let packageName = this.$projectData.projectId; + private async debugStartCore(): Promise { + let packageName = this.$projectData.projectId; - // Arguments passed to executeShellCommand must be in array ([]), but it turned out adb shell "arg with intervals" still works correctly. - // As we need to redirect output of a command on the device, keep using only one argument. - // We could rewrite this with two calls - touch and rm -f , but -f flag is not available on old Android, so rm call will fail when file does not exist. + // Arguments passed to executeShellCommand must be in array ([]), but it turned out adb shell "arg with intervals" still works correctly. + // As we need to redirect output of a command on the device, keep using only one argument. + // We could rewrite this with two calls - touch and rm -f , but -f flag is not available on old Android, so rm call will fail when file does not exist. - this.device.applicationManager.stopApplication(packageName).wait(); + await this.device.applicationManager.stopApplication(packageName); - if(this.$options.debugBrk) { - this.device.adb.executeShellCommand([`cat /dev/null > /data/local/tmp/${packageName}-debugbreak`]).wait(); - } - this.device.adb.executeShellCommand([`cat /dev/null > /data/local/tmp/${packageName}-debugger-started`]).wait(); + if (this.$options.debugBrk) { + await this.device.adb.executeShellCommand([`cat /dev/null > /data/local/tmp/${packageName}-debugbreak`]); + } - this.device.applicationManager.startApplication(packageName).wait(); + await this.device.adb.executeShellCommand([`cat /dev/null > /data/local/tmp/${packageName}-debugger-started`]); - this.waitForDebugger(packageName); + await this.device.applicationManager.startApplication(packageName); - }).future()(); + await this.waitForDebugger(packageName); } - private waitForDebugger(packageName: String) { + private async waitForDebugger(packageName: String): Promise { let waitText: string = `0 /data/local/tmp/${packageName}-debugger-started`; let maxWait = 12; let debugerStarted: boolean = false; while (maxWait > 0 && !debugerStarted) { - let forwardsResult = this.device.adb.executeShellCommand(["ls", "-s", `/data/local/tmp/${packageName}-debugger-started`]).wait(); + let forwardsResult = await this.device.adb.executeShellCommand(["ls", "-s", `/data/local/tmp/${packageName}-debugger-started`]); + maxWait--; + debugerStarted = forwardsResult.indexOf(waitText) === -1; + if (!debugerStarted) { sleep(500); } @@ -231,10 +230,8 @@ class AndroidDebugService implements IDebugService { } } - private startDebuggerClient(port: Number): IFuture { - return (() => { + private startDebuggerClient(port: Number): void { this.$logger.info(`To start debugging, open the following URL in Chrome:${os.EOL}chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=localhost:${port}${os.EOL}`.cyan); - }).future()(); } private stopDebuggerClient(): void { diff --git a/lib/services/android-project-properties-manager.ts b/lib/services/android-project-properties-manager.ts index 2715a66154..2d1b250779 100644 --- a/lib/services/android-project-properties-manager.ts +++ b/lib/services/android-project-properties-manager.ts @@ -7,60 +7,51 @@ export class AndroidProjectPropertiesManager implements IAndroidProjectPropertie private dirty = false; constructor(private $propertiesParser: IPropertiesParser, - private $fs: IFileSystem, private $logger: ILogger, directoryPath: string) { - this.filePath = path.join(directoryPath, "project.properties"); + this.filePath = path.join(directoryPath, "project.properties"); } - public getProjectReferences(): IFuture { - return (() => { - if(!this.projectReferences || this.dirty) { - let allProjectProperties = this.getAllProjectProperties().wait(); - let allProjectPropertiesKeys = _.keys(allProjectProperties); - this.projectReferences = _(allProjectPropertiesKeys) - .filter(key => _.startsWith(key, "android.library.reference.")) - .map(key => this.createLibraryReference(key, allProjectProperties[key])) - .value(); - } + public async getProjectReferences(): Promise { + if (!this.projectReferences || this.dirty) { + let allProjectProperties = await this.getAllProjectProperties(); + let allProjectPropertiesKeys = _.keys(allProjectProperties); + this.projectReferences = _(allProjectPropertiesKeys) + .filter(key => _.startsWith(key, "android.library.reference.")) + .map(key => this.createLibraryReference(key, allProjectProperties[key])) + .value(); + } - return this.projectReferences; - }).future()(); + return this.projectReferences; } - public addProjectReference(referencePath: string): IFuture { - return (() => { - let references = this.getProjectReferences().wait(); - let libRefExists = _.some(references, r => path.normalize(r.path) === path.normalize(referencePath)); - if(!libRefExists) { - this.addToPropertyList("android.library.reference", referencePath).wait(); - } - }).future()(); + public async addProjectReference(referencePath: string): Promise { + let references = await this.getProjectReferences(); + let libRefExists = _.some(references, r => path.normalize(r.path) === path.normalize(referencePath)); + if (!libRefExists) { + await this.addToPropertyList("android.library.reference", referencePath); + } } - public removeProjectReference(referencePath: string): IFuture { - return (() => { - let references = this.getProjectReferences().wait(); - let libRefExists = _.some(references, r => path.normalize(r.path) === path.normalize(referencePath)); - if(libRefExists) { - this.removeFromPropertyList("android.library.reference", referencePath).wait(); - } else { - this.$logger.error(`Could not find ${referencePath}.`); - } - }).future()(); + public async removeProjectReference(referencePath: string): Promise { + let references = await this.getProjectReferences(); + let libRefExists = _.some(references, r => path.normalize(r.path) === path.normalize(referencePath)); + if (libRefExists) { + await this.removeFromPropertyList("android.library.reference", referencePath); + } else { + this.$logger.error(`Could not find ${referencePath}.`); + } } - private createEditor(): IFuture { - return (() => { - return this._editor || this.$propertiesParser.createEditor(this.filePath).wait(); - }).future()(); + private async createEditor(): Promise { + return this._editor || await this.$propertiesParser.createEditor(this.filePath); } private buildKeyName(key: string, index: number): string { return `${key}.${index}`; } - private getAllProjectProperties(): IFuture { + private async getAllProjectProperties(): Promise { return this.$propertiesParser.read(this.filePath); } @@ -73,39 +64,38 @@ export class AndroidProjectPropertiesManager implements IAndroidProjectPropertie }; } - private addToPropertyList(key: string, value: string): IFuture { - return (() => { - let editor = this.createEditor().wait(); - let i = 1; - while (editor.get(this.buildKeyName(key, i))) { - i++; - } + private async addToPropertyList(key: string, value: string): Promise { + let editor = await this.createEditor(); + let i = 1; + while (editor.get(this.buildKeyName(key, i))) { + i++; + } - editor.set(this.buildKeyName(key, i), value); - this.$propertiesParser.saveEditor().wait(); - this.dirty = true; - }).future()(); + editor.set(this.buildKeyName(key, i), value); + await this.$propertiesParser.saveEditor(); + this.dirty = true; } - private removeFromPropertyList(key: string, value: string): IFuture { - return (() => { - let editor = this.createEditor().wait(); - let valueLowerCase = value.toLowerCase(); - let i = 1; - let currentValue: any; - while (currentValue = editor.get(this.buildKeyName(key, i))) { - if (currentValue.toLowerCase() === valueLowerCase) { - while (currentValue = editor.get(this.buildKeyName(key, i+1))) { - editor.set(this.buildKeyName(key, i), currentValue); - i++; - } - editor.set(this.buildKeyName(key, i)); - break; - } - i++; - } - this.$propertiesParser.saveEditor().wait(); - this.dirty = true; - }).future()(); + private async removeFromPropertyList(key: string, value: string): Promise { + let editor = await this.createEditor(); + let valueLowerCase = value.toLowerCase(); + let i = 1; + let currentValue: any; + while (currentValue = editor.get(this.buildKeyName(key, i))) { + if (currentValue.toLowerCase() === valueLowerCase) { + while (currentValue = editor.get(this.buildKeyName(key, i + 1))) { + editor.set(this.buildKeyName(key, i), currentValue); + i++; + } + + editor.set(this.buildKeyName(key, i)); + break; + } + + i++; + } + + await this.$propertiesParser.saveEditor(); + this.dirty = true; } } diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index e96f8b4f1f..3806a463b9 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -1,6 +1,5 @@ import * as path from "path"; import * as shell from "shelljs"; -import Future = require("fibers/future"); import * as constants from "../constants"; import * as semver from "semver"; import * as projectServiceBaseLib from "./platform-project-service-base"; @@ -32,13 +31,9 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject $projectData: IProjectData, $projectDataService: IProjectDataService, private $sysInfo: ISysInfo, - private $mobileHelper: Mobile.IMobileHelper, private $injector: IInjector, private $pluginVariablesService: IPluginVariablesService, - private $deviceAppDataFactory: Mobile.IDeviceAppDataFactory, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, - private $projectTemplatesService: IProjectTemplatesService, - private $xmlValidator: IXmlValidator, private $config: IConfiguration, private $npm: INodePackageManager) { super($fs, $projectData, $projectDataService); @@ -75,8 +70,8 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject return this._platformData; } - public validateOptions(): IFuture { - return Future.fromResult(true); + public validateOptions(): Promise { + return Promise.resolve(true); } public getAppResourcesDestinationDirectoryPath(frameworkVersion?: string): string { @@ -87,82 +82,79 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject return path.join(this.platformData.projectRoot, "res"); } - public validate(): IFuture { - return (() => { - this.validatePackageName(this.$projectData.projectId); - this.validateProjectName(this.$projectData.projectName); + public async validate(): Promise { + this.validatePackageName(this.$projectData.projectId); + this.validateProjectName(this.$projectData.projectName); - // this call will fail in case `android` is not set correctly. - this.$androidToolsInfo.getPathToAndroidExecutable({ showWarningsAsErrors: true }).wait(); - let javaCompilerVersion = this.$sysInfo.getJavaCompilerVersion().wait(); - this.$androidToolsInfo.validateJavacVersion(javaCompilerVersion, { showWarningsAsErrors: true }).wait(); - }).future()(); - } + // this call will fail in case `android` is not set correctly. + await this.$androidToolsInfo.getPathToAndroidExecutable({ showWarningsAsErrors: true }); - public createProject(frameworkDir: string, frameworkVersion: string, pathToTemplate?: string): IFuture { - return (() => { - if (semver.lt(frameworkVersion, AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE)) { - this.$errors.failWithoutHelp(`The NativeScript CLI requires Android runtime ${AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE} or later to work properly.`); - } + let javaCompilerVersion = await this.$sysInfo.getJavaCompilerVersion(); - this.$fs.ensureDirectoryExists(this.platformData.projectRoot); - this.$androidToolsInfo.validateInfo({ showWarningsAsErrors: true, validateTargetSdk: true }).wait(); - let androidToolsInfo = this.$androidToolsInfo.getToolsInfo().wait(); - let targetSdkVersion = androidToolsInfo.targetSdkVersion; - this.$logger.trace(`Using Android SDK '${targetSdkVersion}'.`); - this.copy(this.platformData.projectRoot, frameworkDir, "libs", "-R"); - - if (pathToTemplate) { - let mainPath = path.join(this.platformData.projectRoot, "src", "main"); - this.$fs.createDirectory(mainPath); - shell.cp("-R", path.join(path.resolve(pathToTemplate), "*"), mainPath); - } else { - this.copy(this.platformData.projectRoot, frameworkDir, "src", "-R"); - } - this.copy(this.platformData.projectRoot, frameworkDir, "build.gradle settings.gradle build-tools", "-Rf"); + await this.$androidToolsInfo.validateJavacVersion(javaCompilerVersion, { showWarningsAsErrors: true }); + } - try { - this.copy(this.platformData.projectRoot, frameworkDir, "gradle.properties", "-Rf"); - } catch(e) { - this.$logger.warn(`\n${e}\nIt's possible, the final .apk file will contain all architectures instead of the ones described in the abiFilters!\nYou can fix this by using the latest android platform.`); - } + public async createProject(frameworkDir: string, frameworkVersion: string, pathToTemplate?: string): Promise { + if (semver.lt(frameworkVersion, AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE)) { + this.$errors.failWithoutHelp(`The NativeScript CLI requires Android runtime ${AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE} or later to work properly.`); + } - this.copy(this.platformData.projectRoot, frameworkDir, "gradle", "-R"); - this.copy(this.platformData.projectRoot, frameworkDir, "gradlew gradlew.bat", "-f"); + this.$fs.ensureDirectoryExists(this.platformData.projectRoot); + await this.$androidToolsInfo.validateInfo({ showWarningsAsErrors: true, validateTargetSdk: true }); + let androidToolsInfo = await this.$androidToolsInfo.getToolsInfo(); + let targetSdkVersion = androidToolsInfo.targetSdkVersion; + this.$logger.trace(`Using Android SDK '${targetSdkVersion}'.`); + this.copy(this.platformData.projectRoot, frameworkDir, "libs", "-R"); + + if (pathToTemplate) { + let mainPath = path.join(this.platformData.projectRoot, "src", "main"); + this.$fs.createDirectory(mainPath); + shell.cp("-R", path.join(path.resolve(pathToTemplate), "*"), mainPath); + } else { + this.copy(this.platformData.projectRoot, frameworkDir, "src", "-R"); + } + this.copy(this.platformData.projectRoot, frameworkDir, "build.gradle settings.gradle build-tools", "-Rf"); - this.cleanResValues(targetSdkVersion, frameworkVersion); + try { + this.copy(this.platformData.projectRoot, frameworkDir, "gradle.properties", "-Rf"); + } catch (e) { + this.$logger.warn(`\n${e}\nIt's possible, the final .apk file will contain all architectures instead of the ones described in the abiFilters!\nYou can fix this by using the latest android platform.`); + } - let npmConfig = { - "save": true, - "save-dev": true, - "save-exact": true, - "silent": true - }; + this.copy(this.platformData.projectRoot, frameworkDir, "gradle", "-R"); + this.copy(this.platformData.projectRoot, frameworkDir, "gradlew gradlew.bat", "-f"); - let projectPackageJson: any = this.$fs.readJson(this.$projectData.projectFilePath); + this.cleanResValues(targetSdkVersion, frameworkVersion); - _.each(AndroidProjectService.REQUIRED_DEV_DEPENDENCIES, (dependency: any) => { - let dependencyVersionInProject = (projectPackageJson.dependencies && projectPackageJson.dependencies[dependency.name]) || - (projectPackageJson.devDependencies && projectPackageJson.devDependencies[dependency.name]); + let npmConfig = { + "save": true, + "save-dev": true, + "save-exact": true, + "silent": true + }; - if (!dependencyVersionInProject) { - this.$npm.install(`${dependency.name}@${dependency.version}`, this.$projectData.projectDir, npmConfig).wait(); - } else { - let cleanedVerson = semver.clean(dependencyVersionInProject); + let projectPackageJson: any = this.$fs.readJson(this.$projectData.projectFilePath); - // The plugin version is not valid. Check node_modules for the valid version. - if (!cleanedVerson) { - let pathToPluginPackageJson = path.join(this.$projectData.projectDir, constants.NODE_MODULES_FOLDER_NAME, dependency.name, constants.PACKAGE_JSON_FILE_NAME); - dependencyVersionInProject = this.$fs.exists(pathToPluginPackageJson) && this.$fs.readJson(pathToPluginPackageJson).version; - } + for (let dependency of AndroidProjectService.REQUIRED_DEV_DEPENDENCIES) { + let dependencyVersionInProject = (projectPackageJson.dependencies && projectPackageJson.dependencies[dependency.name]) || + (projectPackageJson.devDependencies && projectPackageJson.devDependencies[dependency.name]); - if (!semver.satisfies(dependencyVersionInProject || cleanedVerson, dependency.version)) { - this.$errors.failWithoutHelp(`Your project have installed ${dependency.name} version ${cleanedVerson} but Android platform requires version ${dependency.version}.`); - } + if (!dependencyVersionInProject) { + await this.$npm.install(`${dependency.name}@${dependency.version}`, this.$projectData.projectDir, npmConfig); + } else { + let cleanedVerson = semver.clean(dependencyVersionInProject); + + // The plugin version is not valid. Check node_modules for the valid version. + if (!cleanedVerson) { + let pathToPluginPackageJson = path.join(this.$projectData.projectDir, constants.NODE_MODULES_FOLDER_NAME, dependency.name, constants.PACKAGE_JSON_FILE_NAME); + dependencyVersionInProject = this.$fs.exists(pathToPluginPackageJson) && this.$fs.readJson(pathToPluginPackageJson).version; } - }); - }).future()(); + if (!semver.satisfies(dependencyVersionInProject || cleanedVerson, dependency.version)) { + this.$errors.failWithoutHelp(`Your project have installed ${dependency.name} version ${cleanedVerson} but Android platform requires version ${dependency.version}.`); + } + } + }; } private cleanResValues(targetSdkVersion: number, frameworkVersion: string): void { @@ -179,40 +171,39 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject && dir.sdkNum && (!targetSdkVersion || (targetSdkVersion < dir.sdkNum))) .map(dir => path.join(resDestinationDir, dir.dirName)); + this.$logger.trace("Directories to clean:"); + this.$logger.trace(directoriesToClean); + _.map(directoriesToClean, dir => this.$fs.deleteDirectory(dir)); } - // TODO: Remove IFuture, reason: writeJson - cannot until other implementation has async calls. - public interpolateData(): IFuture { - return (() => { - // Interpolate the apilevel and package - this.interpolateConfigurationFile().wait(); + public async interpolateData(): Promise { + // Interpolate the apilevel and package + await this.interpolateConfigurationFile(); - let stringsFilePath = path.join(this.getAppResourcesDestinationDirectoryPath(), 'values', 'strings.xml'); - shell.sed('-i', /__NAME__/, this.$projectData.projectName, stringsFilePath); - shell.sed('-i', /__TITLE_ACTIVITY__/, this.$projectData.projectName, stringsFilePath); + let stringsFilePath = path.join(this.getAppResourcesDestinationDirectoryPath(), 'values', 'strings.xml'); + shell.sed('-i', /__NAME__/, this.$projectData.projectName, stringsFilePath); + shell.sed('-i', /__TITLE_ACTIVITY__/, this.$projectData.projectName, stringsFilePath); - let gradleSettingsFilePath = path.join(this.platformData.projectRoot, "settings.gradle"); - shell.sed('-i', /__PROJECT_NAME__/, this.getProjectNameFromId(), gradleSettingsFilePath); + let gradleSettingsFilePath = path.join(this.platformData.projectRoot, "settings.gradle"); + shell.sed('-i', /__PROJECT_NAME__/, this.getProjectNameFromId(), gradleSettingsFilePath); - // will replace applicationId in app/App_Resources/Android/app.gradle if it has not been edited by the user - let userAppGradleFilePath = path.join(this.$projectData.appResourcesDirectoryPath, this.$devicePlatformsConstants.Android, "app.gradle"); - try { - shell.sed('-i', /__PACKAGE__/, this.$projectData.projectId, userAppGradleFilePath); - } catch(e) { - this.$logger.warn(`\n${e}.\nCheck if you're using an outdated template and update it.`); - } - }).future()(); + // will replace applicationId in app/App_Resources/Android/app.gradle if it has not been edited by the user + let userAppGradleFilePath = path.join(this.$projectData.appResourcesDirectoryPath, this.$devicePlatformsConstants.Android, "app.gradle"); + + try { + shell.sed('-i', /__PACKAGE__/, this.$projectData.projectId, userAppGradleFilePath); + } catch (e) { + this.$logger.warn(`\n${e}.\nCheck if you're using an outdated template and update it.`); + } } - public interpolateConfigurationFile(): IFuture { - return (() => { - let manifestPath = this.platformData.configurationFilePath; - shell.sed('-i', /__PACKAGE__/, this.$projectData.projectId, manifestPath); - shell.sed('-i', /__APILEVEL__/, this.$options.sdk || this.$androidToolsInfo.getToolsInfo().wait().compileSdkVersion.toString(), manifestPath); - }).future()(); + public async interpolateConfigurationFile(): Promise { + let manifestPath = this.platformData.configurationFilePath; + shell.sed('-i', /__PACKAGE__/, this.$projectData.projectId, manifestPath); + shell.sed('-i', /__APILEVEL__/, this.$options.sdk || (await this.$androidToolsInfo.getToolsInfo()).compileSdkVersion.toString(), manifestPath); } private getProjectNameFromId(): string { @@ -232,43 +223,40 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject return true; } - public updatePlatform(currentVersion: string, newVersion: string, canUpdate: boolean, addPlatform?: Function, removePlatforms?: (platforms: string[]) => IFuture): IFuture { - return (() => { - if (semver.eq(newVersion, AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE)) { - let platformLowercase = this.platformData.normalizedPlatformName.toLowerCase(); - removePlatforms([platformLowercase.split("@")[0]]).wait(); - addPlatform(platformLowercase).wait(); - return false; - } + public async updatePlatform(currentVersion: string, newVersion: string, canUpdate: boolean, addPlatform?: Function, removePlatforms?: (platforms: string[]) => Promise): Promise { + if (semver.eq(newVersion, AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE)) { + let platformLowercase = this.platformData.normalizedPlatformName.toLowerCase(); + await removePlatforms([platformLowercase.split("@")[0]]); + await addPlatform(platformLowercase); + return false; + } - return true; - }).future()(); + return true; } - public buildProject(projectRoot: string, buildConfig?: IBuildConfig): IFuture { - return (() => { - if (this.canUseGradle()) { - let buildOptions = this.getBuildOptions(); - if (this.$logger.getLevel() === "TRACE") { - buildOptions.unshift("--stacktrace"); - buildOptions.unshift("--debug"); - } - buildOptions.unshift("buildapk"); - let gradleBin = path.join(projectRoot, "gradlew"); - if (this.$hostInfo.isWindows) { - gradleBin += ".bat"; // cmd command line parsing rules are weird. Avoid issues with quotes. See https://github.com/apache/cordova-android/blob/master/bin/templates/cordova/lib/builders/GradleBuilder.js for another approach - } - this.spawn(gradleBin, buildOptions, { stdio: "inherit", cwd: this.platformData.projectRoot }).wait(); - } else { - this.$errors.failWithoutHelp("Cannot complete build because this project is ANT-based." + EOL + - "Run `tns platform remove android && tns platform add android` to switch to Gradle and try again."); + public async buildProject(projectRoot: string, buildConfig?: IBuildConfig): Promise { + if (this.canUseGradle()) { + let buildOptions = await this.getBuildOptions(); + if (this.$logger.getLevel() === "TRACE") { + buildOptions.unshift("--stacktrace"); + buildOptions.unshift("--debug"); } - }).future()(); + buildOptions.unshift("buildapk"); + let gradleBin = path.join(projectRoot, "gradlew"); + if (this.$hostInfo.isWindows) { + gradleBin += ".bat"; // cmd command line parsing rules are weird. Avoid issues with quotes. See https://github.com/apache/cordova-android/blob/master/bin/templates/cordova/lib/builders/GradleBuilder.js for another approach + } + await this.spawn(gradleBin, buildOptions, { stdio: "inherit", cwd: this.platformData.projectRoot }); + } else { + this.$errors.failWithoutHelp("Cannot complete build because this project is ANT-based." + EOL + + "Run `tns platform remove android && tns platform add android` to switch to Gradle and try again."); + } } - public getBuildOptions(): Array { - this.$androidToolsInfo.validateInfo({ showWarningsAsErrors: true, validateTargetSdk: true }).wait(); - let androidToolsInfo = this.$androidToolsInfo.getToolsInfo().wait(); + private async getBuildOptions(): Promise> { + await this.$androidToolsInfo.validateInfo({ showWarningsAsErrors: true, validateTargetSdk: true }); + + let androidToolsInfo = await this.$androidToolsInfo.getToolsInfo(); let compileSdk = androidToolsInfo.compileSdkVersion; let targetSdk = this.getTargetFromAndroidManifest() || compileSdk; let buildToolsVersion = androidToolsInfo.buildToolsVersion; @@ -293,7 +281,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject return buildOptions; } - public buildForDeploy(projectRoot: string, buildConfig?: IBuildConfig): IFuture { + public async buildForDeploy(projectRoot: string, buildConfig?: IBuildConfig): Promise { return this.buildProject(projectRoot, buildConfig); } @@ -305,7 +293,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject return [".jar", ".dat"]; } - public prepareProject(): void { + public async prepareProject(): Promise { // Intentionally left empty. } @@ -313,6 +301,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject let originalAndroidManifestFilePath = path.join(this.$projectData.appResourcesDirectoryPath, this.$devicePlatformsConstants.Android, this.platformData.configurationFileName); let manifestExists = this.$fs.exists(originalAndroidManifestFilePath); + if (!manifestExists) { this.$logger.warn('No manifest found in ' + originalAndroidManifestFilePath); return; @@ -330,52 +319,48 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject }); } - public preparePluginNativeCode(pluginData: IPluginData): IFuture { - return (() => { - let pluginPlatformsFolderPath = this.getPluginPlatformsFolderPath(pluginData, AndroidProjectService.ANDROID_PLATFORM_NAME); - this.processResourcesFromPlugin(pluginData, pluginPlatformsFolderPath).wait(); - }).future()(); + public async preparePluginNativeCode(pluginData: IPluginData): Promise { + let pluginPlatformsFolderPath = this.getPluginPlatformsFolderPath(pluginData, AndroidProjectService.ANDROID_PLATFORM_NAME); + await this.processResourcesFromPlugin(pluginData, pluginPlatformsFolderPath); } - public processConfigurationFilesFromAppResources(): IFuture { - return Future.fromResult(); + public async processConfigurationFilesFromAppResources(): Promise { + return; } - private processResourcesFromPlugin(pluginData: IPluginData, pluginPlatformsFolderPath: string): IFuture { - return (() => { - let configurationsDirectoryPath = path.join(this.platformData.projectRoot, "configurations"); - this.$fs.ensureDirectoryExists(configurationsDirectoryPath); + private async processResourcesFromPlugin(pluginData: IPluginData, pluginPlatformsFolderPath: string): Promise { + let configurationsDirectoryPath = path.join(this.platformData.projectRoot, "configurations"); + this.$fs.ensureDirectoryExists(configurationsDirectoryPath); - let pluginConfigurationDirectoryPath = path.join(configurationsDirectoryPath, pluginData.name); - if (this.$fs.exists(pluginPlatformsFolderPath)) { - this.$fs.ensureDirectoryExists(pluginConfigurationDirectoryPath); + let pluginConfigurationDirectoryPath = path.join(configurationsDirectoryPath, pluginData.name); + if (this.$fs.exists(pluginPlatformsFolderPath)) { + this.$fs.ensureDirectoryExists(pluginConfigurationDirectoryPath); - // Copy all resources from plugin - let resourcesDestinationDirectoryPath = path.join(this.platformData.projectRoot, "src", pluginData.name); - this.$fs.ensureDirectoryExists(resourcesDestinationDirectoryPath); - shell.cp("-Rf", path.join(pluginPlatformsFolderPath, "*"), resourcesDestinationDirectoryPath); + // Copy all resources from plugin + let resourcesDestinationDirectoryPath = path.join(this.platformData.projectRoot, "src", pluginData.name); + this.$fs.ensureDirectoryExists(resourcesDestinationDirectoryPath); + shell.cp("-Rf", path.join(pluginPlatformsFolderPath, "*"), resourcesDestinationDirectoryPath); - (this.$fs.enumerateFilesInDirectorySync(resourcesDestinationDirectoryPath, file => this.$fs.getFsStats(file).isDirectory() || path.extname(file) === constants.XML_FILE_EXTENSION) || []) - .forEach(file => { - this.$logger.trace(`Interpolate data for plugin file: ${file}`); - this.$pluginVariablesService.interpolate(pluginData, file).wait(); - }); + const filesForInterpolation = this.$fs.enumerateFilesInDirectorySync(resourcesDestinationDirectoryPath, file => this.$fs.getFsStats(file).isDirectory() || path.extname(file) === constants.XML_FILE_EXTENSION) || []; + for (let file of filesForInterpolation) { + this.$logger.trace(`Interpolate data for plugin file: ${file}`); + await this.$pluginVariablesService.interpolate(pluginData, file); } + } - // Copy include.gradle file - let includeGradleFilePath = path.join(pluginPlatformsFolderPath, "include.gradle"); - if (this.$fs.exists(includeGradleFilePath)) { - shell.cp("-f", includeGradleFilePath, pluginConfigurationDirectoryPath); - } - }).future()(); + // Copy include.gradle file + let includeGradleFilePath = path.join(pluginPlatformsFolderPath, "include.gradle"); + if (this.$fs.exists(includeGradleFilePath)) { + shell.cp("-f", includeGradleFilePath, pluginConfigurationDirectoryPath); + } } - public removePluginNativeCode(pluginData: IPluginData): void { + public async removePluginNativeCode(pluginData: IPluginData): Promise { try { // check whether the dependency that's being removed has native code let pluginConfigDir = path.join(this.platformData.projectRoot, "configurations", pluginData.name); if (this.$fs.exists(pluginConfigDir)) { - this.cleanProject(this.platformData.projectRoot, []).wait(); + await this.cleanProject(this.platformData.projectRoot, []); } } catch (e) { if (e.code === "ENOENT") { @@ -386,11 +371,11 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } } - public afterPrepareAllPlugins(): IFuture { - return Future.fromResult(); + public async afterPrepareAllPlugins(): Promise { + return; } - public beforePrepareAllPlugins(dependencies?: IDictionary): IFuture { + public async beforePrepareAllPlugins(dependencies?: IDictionary): Promise { if (!this.$config.debugLivesync) { if (dependencies) { let platformDir = path.join(this.$projectData.platformsDir, "android"); @@ -398,38 +383,33 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject let checkV8dependants = path.join(buildDir, "check-v8-dependants.js"); if (this.$fs.exists(checkV8dependants)) { let stringifiedDependencies = JSON.stringify(dependencies); - this.spawn('node', [checkV8dependants, stringifiedDependencies, this.$projectData.platformsDir], { stdio: "inherit" }).wait(); + await this.spawn('node', [checkV8dependants, stringifiedDependencies, this.$projectData.platformsDir], { stdio: "inherit" }); } } - let buildOptions = this.getBuildOptions(); + let buildOptions = await this.getBuildOptions(); let projectRoot = this.platformData.projectRoot; - this.cleanProject(projectRoot, buildOptions).wait(); + + await this.cleanProject(projectRoot, buildOptions); } - return Future.fromResult(); } - private cleanProject(projectRoot: string, options: string[]): IFuture { - return (() => { - options.unshift("clean"); + private async cleanProject(projectRoot: string, options: string[]): Promise { + options.unshift("clean"); - let gradleBin = path.join(projectRoot, "gradlew"); - if (this.$hostInfo.isWindows) { - gradleBin += ".bat"; - } + let gradleBin = path.join(projectRoot, "gradlew"); + if (this.$hostInfo.isWindows) { + gradleBin += ".bat"; + } - this.spawn(gradleBin, options, { stdio: "inherit", cwd: this.platformData.projectRoot }).wait(); - }).future()(); + await this.spawn(gradleBin, options, { stdio: "inherit", cwd: this.platformData.projectRoot }); } - public deploy(deviceIdentifier: string): IFuture { - return (() => { - let adb = this.$injector.resolve(DeviceAndroidDebugBridge, { identifier: deviceIdentifier }); - let deviceRootPath = `/data/local/tmp/${this.$projectData.projectId}`; - adb.executeShellCommand(["rm", "-rf", deviceRootPath]).wait(); - adb.executeShellCommand(["mkdir", deviceRootPath]).wait(); - }).future()(); + public async deploy(deviceIdentifier: string): Promise { + let adb = this.$injector.resolve(DeviceAndroidDebugBridge, { identifier: deviceIdentifier }); + let deviceRootPath = `/data/local/tmp/${this.$projectData.projectId}`; + await adb.executeShellCommand(["rm", "-rf", deviceRootPath]); } private _canUseGradle: boolean; @@ -452,7 +432,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject shell.cp(cpArg, paths, projectRoot); } - private spawn(command: string, args: string[], opts?: any): IFuture { + private async spawn(command: string, args: string[], opts?: any): Promise { return this.$childProcess.spawnFromEvent(command, args, "close", opts || { stdio: "inherit" }); } @@ -495,4 +475,5 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject return versionInManifest; } } + $injector.register("androidProjectService", AndroidProjectService); diff --git a/lib/services/app-files-updater.ts b/lib/services/app-files-updater.ts index 8027178ecc..9ea14993c1 100644 --- a/lib/services/app-files-updater.ts +++ b/lib/services/app-files-updater.ts @@ -69,7 +69,7 @@ export class AppFilesUpdater { } if (this.options.bundle) { - sourceFiles = sourceFiles.filter(file => minimatch(file, "**/App_Resources/**", {nocase: true})); + sourceFiles = sourceFiles.filter(file => minimatch(file, "**/App_Resources/**", { nocase: true })); } return sourceFiles; } diff --git a/lib/services/cocoapods-service.ts b/lib/services/cocoapods-service.ts index 9e3930c6ea..c07d49bf1a 100644 --- a/lib/services/cocoapods-service.ts +++ b/lib/services/cocoapods-service.ts @@ -1,4 +1,4 @@ -import {EOL} from "os"; +import { EOL } from "os"; interface IRubyFunction { functionName: string; diff --git a/lib/services/doctor-service.ts b/lib/services/doctor-service.ts index 0052105619..9d7f36949f 100644 --- a/lib/services/doctor-service.ts +++ b/lib/services/doctor-service.ts @@ -1,4 +1,4 @@ -import {EOL} from "os"; +import { EOL } from "os"; import * as semver from "semver"; import * as path from "path"; import * as helpers from "../common/helpers"; @@ -31,108 +31,106 @@ class DoctorService implements IDoctorService { private $versionsService: IVersionsService, private $xcprojService: IXcprojService) { } - public printWarnings(configOptions?: { trackResult: boolean }): IFuture { - return (() => { - let result = false; - let sysInfo = this.$sysInfo.getSysInfo(this.$staticConfig.pathToPackageJson).wait(); + public async printWarnings(configOptions?: { trackResult: boolean }): Promise { + let result = false; + let sysInfo = await this.$sysInfo.getSysInfo(this.$staticConfig.pathToPackageJson); - if (!sysInfo.adbVer) { - this.$logger.warn("WARNING: adb from the Android SDK is not installed or is not configured properly."); - this.$logger.out("For Android-related operations, the NativeScript CLI will use a built-in version of adb." + EOL - + "To avoid possible issues with the native Android emulator, Genymotion or connected" + EOL - + "Android devices, verify that you have installed the latest Android SDK and" + EOL - + "its dependencies as described in http://developer.android.com/sdk/index.html#Requirements" + EOL); + if (!sysInfo.adbVer) { + this.$logger.warn("WARNING: adb from the Android SDK is not installed or is not configured properly."); + this.$logger.out("For Android-related operations, the NativeScript CLI will use a built-in version of adb." + EOL + + "To avoid possible issues with the native Android emulator, Genymotion or connected" + EOL + + "Android devices, verify that you have installed the latest Android SDK and" + EOL + + "its dependencies as described in http://developer.android.com/sdk/index.html#Requirements" + EOL); - this.printPackageManagerTip(); - result = true; - } + this.printPackageManagerTip(); + result = true; + } - if (!sysInfo.androidInstalled) { - this.$logger.warn("WARNING: The Android SDK is not installed or is not configured properly."); - this.$logger.out("You will not be able to build your projects for Android and run them in the native emulator." + EOL - + "To be able to build for Android and run apps in the native emulator, verify that you have" + EOL - + "installed the latest Android SDK and its dependencies as described in http://developer.android.com/sdk/index.html#Requirements" + EOL - ); + if (!sysInfo.androidInstalled) { + this.$logger.warn("WARNING: The Android SDK is not installed or is not configured properly."); + this.$logger.out("You will not be able to build your projects for Android and run them in the native emulator." + EOL + + "To be able to build for Android and run apps in the native emulator, verify that you have" + EOL + + "installed the latest Android SDK and its dependencies as described in http://developer.android.com/sdk/index.html#Requirements" + EOL + ); + + this.printPackageManagerTip(); + result = true; + } - this.printPackageManagerTip(); + if (this.$hostInfo.isDarwin) { + if (!sysInfo.xcodeVer) { + this.$logger.warn("WARNING: Xcode is not installed or is not configured properly."); + this.$logger.out("You will not be able to build your projects for iOS or run them in the iOS Simulator." + EOL + + "To be able to build for iOS and run apps in the native emulator, verify that you have installed Xcode." + EOL); result = true; } - if (this.$hostInfo.isDarwin) { - if (!sysInfo.xcodeVer) { - this.$logger.warn("WARNING: Xcode is not installed or is not configured properly."); - this.$logger.out("You will not be able to build your projects for iOS or run them in the iOS Simulator." + EOL - + "To be able to build for iOS and run apps in the native emulator, verify that you have installed Xcode." + EOL); - result = true; - } + if (!sysInfo.xcodeprojGemLocation) { + this.$logger.warn("WARNING: xcodeproj gem is not installed or is not configured properly."); + this.$logger.out("You will not be able to build your projects for iOS." + EOL + + "To be able to build for iOS and run apps in the native emulator, verify that you have installed xcodeproj." + EOL); + result = true; + } - if (!sysInfo.xcodeprojGemLocation) { - this.$logger.warn("WARNING: xcodeproj gem is not installed or is not configured properly."); - this.$logger.out("You will not be able to build your projects for iOS." + EOL - + "To be able to build for iOS and run apps in the native emulator, verify that you have installed xcodeproj." + EOL); - result = true; - } + if (!sysInfo.cocoapodVer) { + this.$logger.warn("WARNING: CocoaPods is not installed or is not configured properly."); + this.$logger.out("You will not be able to build your projects for iOS if they contain plugin with CocoaPod file." + EOL + + "To be able to build such projects, verify that you have installed CocoaPods."); + result = true; + } - if (!sysInfo.cocoapodVer) { - this.$logger.warn("WARNING: CocoaPods is not installed or is not configured properly."); - this.$logger.out("You will not be able to build your projects for iOS if they contain plugin with CocoaPod file." + EOL - + "To be able to build such projects, verify that you have installed CocoaPods."); + if (sysInfo.xcodeVer && sysInfo.cocoapodVer) { + let problemWithCocoaPods = await this.verifyCocoaPods(); + if (problemWithCocoaPods) { + this.$logger.warn("WARNING: There was a problem with CocoaPods"); + this.$logger.out("Verify that CocoaPods are configured properly."); result = true; } + } - if (sysInfo.xcodeVer && sysInfo.cocoapodVer) { - let problemWithCocoaPods = this.verifyCocoaPods(); - if (problemWithCocoaPods) { - this.$logger.warn("WARNING: There was a problem with CocoaPods"); - this.$logger.out("Verify that CocoaPods are configured properly."); - result = true; - } - } - - if (sysInfo.cocoapodVer && semver.valid(sysInfo.cocoapodVer) && semver.lt(sysInfo.cocoapodVer, DoctorService.MIN_SUPPORTED_POD_VERSION)) { - this.$logger.warn(`WARNING: Your current CocoaPods version is earlier than ${DoctorService.MIN_SUPPORTED_POD_VERSION}.`); - this.$logger.out("You will not be able to build your projects for iOS if they contain plugin with CocoaPod file." + EOL - + `To be able to build such projects, verify that you have at least ${DoctorService.MIN_SUPPORTED_POD_VERSION} version installed.`); - result = true; - } + if (sysInfo.cocoapodVer && semver.valid(sysInfo.cocoapodVer) && semver.lt(sysInfo.cocoapodVer, DoctorService.MIN_SUPPORTED_POD_VERSION)) { + this.$logger.warn(`WARNING: Your current CocoaPods version is earlier than ${DoctorService.MIN_SUPPORTED_POD_VERSION}.`); + this.$logger.out("You will not be able to build your projects for iOS if they contain plugin with CocoaPod file." + EOL + + `To be able to build such projects, verify that you have at least ${DoctorService.MIN_SUPPORTED_POD_VERSION} version installed.`); + result = true; + } - if (this.$xcprojService.verifyXcproj(false).wait()) { - result = true; - } - } else { - this.$logger.out("NOTE: You can develop for iOS only on Mac OS X systems."); - this.$logger.out("To be able to work with iOS devices and projects, you need Mac OS X Mavericks or later." + EOL); + if (await this.$xcprojService.verifyXcproj(false)) { + result = true; } + } else { + this.$logger.out("NOTE: You can develop for iOS only on Mac OS X systems."); + this.$logger.out("To be able to work with iOS devices and projects, you need Mac OS X Mavericks or later." + EOL); + } - let androidToolsIssues = this.$androidToolsInfo.validateInfo().wait(); - let javaVersionIssue = this.$androidToolsInfo.validateJavacVersion(sysInfo.javacVersion).wait(); - let doctorResult = result || androidToolsIssues || javaVersionIssue; + let androidToolsIssues = await this.$androidToolsInfo.validateInfo(); + let javaVersionIssue = await this.$androidToolsInfo.validateJavacVersion(sysInfo.javacVersion); + let doctorResult = result || androidToolsIssues || javaVersionIssue; - if (!configOptions || configOptions.trackResult) { - this.$analyticsService.track("DoctorEnvironmentSetup", doctorResult ? "incorrect" : "correct").wait(); - } + if (!configOptions || configOptions.trackResult) { + await this.$analyticsService.track("DoctorEnvironmentSetup", doctorResult ? "incorrect" : "correct"); + } - if (doctorResult) { - this.$logger.info("There seem to be issues with your configuration."); - if (this.$hostInfo.isDarwin) { - this.promptForHelp(DoctorService.DarwinSetupDocsLink, DoctorService.DarwinSetupScriptLocation, []).wait(); - } else if (this.$hostInfo.isWindows) { - this.promptForHelp(DoctorService.WindowsSetupDocsLink, DoctorService.WindowsSetupScriptExecutable, DoctorService.WindowsSetupScriptArguments).wait(); - } else { - this.promptForDocs(DoctorService.LinuxSetupDocsLink).wait(); - } + if (doctorResult) { + this.$logger.info("There seem to be issues with your configuration."); + if (this.$hostInfo.isDarwin) { + await this.promptForHelp(DoctorService.DarwinSetupDocsLink, DoctorService.DarwinSetupScriptLocation, []); + } else if (this.$hostInfo.isWindows) { + await this.promptForHelp(DoctorService.WindowsSetupDocsLink, DoctorService.WindowsSetupScriptExecutable, DoctorService.WindowsSetupScriptArguments); + } else { + await this.promptForDocs(DoctorService.LinuxSetupDocsLink); } + } - let versionsInformation: IVersionInformation[] = []; - try { - versionsInformation = this.$versionsService.getComponentsForUpdate().wait(); - this.printVersionsInformation(versionsInformation); - } catch (err) { - this.$logger.error("Cannot get the latest versions information from npm. Please try again later."); - } + let versionsInformation: IVersionInformation[] = []; + try { + versionsInformation = await this.$versionsService.getComponentsForUpdate(); + this.printVersionsInformation(versionsInformation); + } catch (err) { + this.$logger.error("Cannot get the latest versions information from npm. Please try again later."); + } - return doctorResult; - }).future()(); + return doctorResult; } private printVersionsInformation(versionsInformation: IVersionInformation[]) { @@ -146,22 +144,18 @@ class DoctorService implements IDoctorService { } } - private promptForDocs(link: string): IFuture { - return (() => { - if (this.$prompter.confirm("Do you want to visit the official documentation?", () => helpers.isInteractive()).wait()) { - this.$opener.open(link); - } - }).future()(); + private async promptForDocs(link: string): Promise { + if (await this.$prompter.confirm("Do you want to visit the official documentation?", () => helpers.isInteractive())) { + this.$opener.open(link); + } } - private promptForHelp(link: string, commandName: string, commandArguments: string[]): IFuture { - return (() => { - this.promptForDocs(link).wait(); + private async promptForHelp(link: string, commandName: string, commandArguments: string[]): Promise { + await this.promptForDocs(link); - if (this.$prompter.confirm("Do you want to run the setup script?", () => helpers.isInteractive()).wait()) { - this.$childProcess.spawnFromEvent(commandName, commandArguments, "close", { stdio: "inherit" }).wait(); - } - }).future()(); + if (await this.$prompter.confirm("Do you want to run the setup script?", () => helpers.isInteractive())) { + await this.$childProcess.spawnFromEvent(commandName, commandArguments, "close", { stdio: "inherit" }); + } } private printPackageManagerTip() { @@ -172,7 +166,7 @@ class DoctorService implements IDoctorService { } } - private verifyCocoaPods(): boolean { + private async verifyCocoaPods(): Promise { this.$logger.out("Verifying CocoaPods. This may take more than a minute, please be patient."); let temp = require("temp"); @@ -187,7 +181,7 @@ class DoctorService implements IDoctorService { let spinner = new clui.Spinner("Installing iOS runtime."); try { spinner.start(); - this.$npm.install("tns-ios", projDir, { global: false, "ignore-scripts": true, production: true, save: true}).wait(); + await this.$npm.install("tns-ios", projDir, { global: false, "ignore-scripts": true, production: true, save: true }); spinner.stop(); let iosDir = path.join(projDir, "node_modules", "tns-ios", "framework"); this.$fs.writeFile( @@ -205,9 +199,8 @@ class DoctorService implements IDoctorService { { throwError: false } ); - this.$progressIndicator.showProgressIndicator(future, 5000).wait(); - let result = future.get(); - if(result.exitCode) { + let result = await this.$progressIndicator.showProgressIndicator(future, 5000); + if (result.exitCode) { this.$logger.out(result.stdout, result.stderr); return true; } diff --git a/lib/services/emulator-platform-service.ts b/lib/services/emulator-platform-service.ts index 2380768505..fe9e5cfdbf 100644 --- a/lib/services/emulator-platform-service.ts +++ b/lib/services/emulator-platform-service.ts @@ -1,213 +1,205 @@ import * as path from "path"; -import * as fiberBootstrap from "../common/fiber-bootstrap"; -import { createTable } from "../common/helpers"; -import Future = require("fibers/future"); +import { createTable, deferPromise } from "../common/helpers"; export class EmulatorPlatformService implements IEmulatorPlatformService { - constructor( - private $mobileHelper: Mobile.IMobileHelper, - private $childProcess: IChildProcess, - private $devicesService: Mobile.IDevicesService, - private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, - private $dispatcher: IFutureDispatcher, - private $options: IOptions, - private $logger: ILogger) {} - - public startEmulator(info: IEmulatorInfo): IFuture { - if (!info.isRunning) { - - if (this.$mobileHelper.isAndroidPlatform(info.platform)) { - this.$options.avd = this.$options.device; - this.$options.device = null; - let platformsData: IPlatformsData = $injector.resolve("platformsData"); - let platformData = platformsData.getPlatformData(info.platform); - let emulatorServices = platformData.emulatorServices; - emulatorServices.checkAvailability(); - emulatorServices.checkDependencies().wait(); - emulatorServices.startEmulator().wait(); - this.$options.avd = null; - return Future.fromResult(); - } - - if (this.$mobileHelper.isiOSPlatform(info.platform)) { - this.stopEmulator(info.platform).wait(); - let future = new Future(); - this.$childProcess.exec(`open -a Simulator --args -CurrentDeviceUDID ${info.id}`).wait(); - let timeoutFunc = () => { - fiberBootstrap.run(() => { - info = this.getEmulatorInfo("ios", info.id).wait(); - if (info.isRunning) { - this.$devicesService.initialize({ platform: info.platform, deviceId: info.id }).wait(); - let device = this.$devicesService.getDeviceByIdentifier(info.id); - device.applicationManager.checkForApplicationUpdates().wait(); - future.return(); - return; - } - setTimeout(timeoutFunc, 2000); - }); - }; - timeoutFunc(); - return future; - } - } - - return Future.fromResult(); - } - - private stopEmulator(platform: string): IFuture { - if (this.$mobileHelper.isiOSPlatform(platform)) { - return this.$childProcess.exec("pkill -9 -f Simulator"); - } - return Future.fromResult(); - } - - public getEmulatorInfo(platform: string, idOrName: string): IFuture { - return (() => { - - if (this.$mobileHelper.isAndroidPlatform(platform)) { - let androidEmulators = this.getAndroidEmulators().wait(); - let found = androidEmulators.filter((info:IEmulatorInfo) => info.id === idOrName); - if (found.length > 0) { - return found[0]; - } - this.$devicesService.initialize({platform: platform, deviceId: null, skipInferPlatform: true}).wait(); - let info:IEmulatorInfo = null; - let action = (device:Mobile.IDevice) => { - return (() => { - if (device.deviceInfo.identifier === idOrName) { - info = { - id: device.deviceInfo.identifier, - name: device.deviceInfo.displayName, - version: device.deviceInfo.version, - platform: "Android", - type: "emulator", - isRunning: true - }; - } - }).future()(); - }; - this.$devicesService.execute(action, undefined, {allowNoDevices: true}).wait(); - return info; - } - - if (this.$mobileHelper.isiOSPlatform(platform)) { - let emulators = this.getiOSEmulators().wait(); - let sdk: string = null; - let versionStart = idOrName.indexOf("("); - if (versionStart > 0) { - sdk = idOrName.substring(versionStart+1, idOrName.indexOf(")", versionStart)).trim(); - idOrName = idOrName.substring(0, versionStart-1).trim(); - } - let found = emulators.filter((info:IEmulatorInfo) => { - let sdkMatch = sdk ? info.version === sdk : true; - return sdkMatch && info.id === idOrName || info.name === idOrName; - }); - return found.length>0 ? found[0] : null; - } - - return null; - - }).future()(); - } - - public listAvailableEmulators(platform: string): IFuture { - return (() => { - let emulators: IEmulatorInfo[] = []; - if (!platform || this.$mobileHelper.isiOSPlatform(platform)) { - let iosEmulators = this.getiOSEmulators().wait(); - if (iosEmulators) { - emulators = emulators.concat(iosEmulators); - } + constructor( + private $mobileHelper: Mobile.IMobileHelper, + private $childProcess: IChildProcess, + private $devicesService: Mobile.IDevicesService, + private $options: IOptions, + private $logger: ILogger) { } + + public async startEmulator(info: IEmulatorInfo): Promise { + if (!info.isRunning) { + if (this.$mobileHelper.isAndroidPlatform(info.platform)) { + this.$options.avd = this.$options.device; + this.$options.device = null; + let platformsData: IPlatformsData = $injector.resolve("platformsData"); + let platformData = platformsData.getPlatformData(info.platform); + let emulatorServices = platformData.emulatorServices; + emulatorServices.checkAvailability(); + await emulatorServices.checkDependencies(); + await emulatorServices.startEmulator(); + this.$options.avd = null; + return; } - if (!platform || this.$mobileHelper.isAndroidPlatform(platform)) { - let androidEmulators = this.getAndroidEmulators().wait(); - if (androidEmulators) { - emulators = emulators.concat(androidEmulators); - } + + if (this.$mobileHelper.isiOSPlatform(info.platform)) { + await this.stopEmulator(info.platform); + let deferred = deferPromise(); + await this.$childProcess.exec(`open -a Simulator --args -CurrentDeviceUDID ${info.id}`); + let timeoutFunc = async () => { + info = await this.getEmulatorInfo("ios", info.id); + if (info.isRunning) { + await this.$devicesService.initialize({ platform: info.platform, deviceId: info.id }); + let device = this.$devicesService.getDeviceByIdentifier(info.id); + await device.applicationManager.checkForApplicationUpdates(); + deferred.resolve(); + return; + } + + setTimeout(timeoutFunc, 2000); + }; + await timeoutFunc(); + return deferred.promise; + } + } + } + + private async stopEmulator(platform: string): Promise { + if (this.$mobileHelper.isiOSPlatform(platform)) { + await this.$childProcess.exec("pkill -9 -f Simulator"); + } + } + + public async getEmulatorInfo(platform: string, idOrName: string): Promise { + if (this.$mobileHelper.isAndroidPlatform(platform)) { + let androidEmulators = await this.getAndroidEmulators(); + let found = androidEmulators.filter((info: IEmulatorInfo) => info.id === idOrName); + if (found.length > 0) { + return found[0]; + } + + await this.$devicesService.initialize({ platform: platform, deviceId: null, skipInferPlatform: true }); + let info: IEmulatorInfo = null; + let action = async (device: Mobile.IDevice) => { + if (device.deviceInfo.identifier === idOrName) { + info = { + id: device.deviceInfo.identifier, + name: device.deviceInfo.displayName, + version: device.deviceInfo.version, + platform: "Android", + type: "emulator", + isRunning: true + }; + } + }; + await this.$devicesService.execute(action, undefined, { allowNoDevices: true }); + return info; + } + + if (this.$mobileHelper.isiOSPlatform(platform)) { + let emulators = await this.getiOSEmulators(); + let sdk: string = null; + let versionStart = idOrName.indexOf("("); + if (versionStart > 0) { + sdk = idOrName.substring(versionStart + 1, idOrName.indexOf(")", versionStart)).trim(); + idOrName = idOrName.substring(0, versionStart - 1).trim(); + } + let found = emulators.filter((info: IEmulatorInfo) => { + let sdkMatch = sdk ? info.version === sdk : true; + return sdkMatch && info.id === idOrName || info.name === idOrName; + }); + return found.length > 0 ? found[0] : null; + } + + return null; + + } + + public async listAvailableEmulators(platform: string): Promise { + let emulators: IEmulatorInfo[] = []; + if (!platform || this.$mobileHelper.isiOSPlatform(platform)) { + let iosEmulators = await this.getiOSEmulators(); + if (iosEmulators) { + emulators = emulators.concat(iosEmulators); + } + } + + if (!platform || this.$mobileHelper.isAndroidPlatform(platform)) { + let androidEmulators = await this.getAndroidEmulators(); + if (androidEmulators) { + emulators = emulators.concat(androidEmulators); + } + } + + this.outputEmulators("\nAvailable emulators", emulators); + this.$logger.out("\nConnected devices & emulators"); + await $injector.resolveCommand("device").execute(platform ? [platform] : []); + } + + public async getiOSEmulators(): Promise { + let output = await this.$childProcess.exec("xcrun simctl list --json"); + let list = JSON.parse(output); + let emulators: IEmulatorInfo[] = []; + for (let osName in list["devices"]) { + if (osName.indexOf("iOS") === -1) { + continue; + } + let os = list["devices"][osName]; + let version = this.parseiOSVersion(osName); + for (let device of os) { + if (device["availability"] !== "(available)") { + continue; + } + let emulatorInfo: IEmulatorInfo = { + id: device["udid"], + name: device["name"], + isRunning: device["state"] === "Booted", + type: "simulator", + version: version, + platform: "iOS" + }; + emulators.push(emulatorInfo); } - this.outputEmulators("\nAvailable emulators", emulators); - this.$logger.out("\nConnected devices & emulators"); - $injector.resolveCommand("device").execute(platform ? [platform] : []).wait(); - }).future()(); - } - - public getiOSEmulators(): IFuture { - return (()=>{ - let output = this.$childProcess.exec("xcrun simctl list --json").wait(); - let list = JSON.parse(output); - let emulators: IEmulatorInfo[] = []; - for (let osName in list["devices"]) { - if (osName.indexOf("iOS") === -1) { - continue; - } - let os = list["devices"][osName]; - let version = this.parseiOSVersion(osName); - for (let device of os) { - if (device["availability"] !== "(available)") { - continue; - } - let emulatorInfo: IEmulatorInfo = { - id: device["udid"], - name: device["name"], - isRunning: device["state"] === "Booted", - type: "simulator", - version: version, - platform: "iOS" - }; - emulators.push(emulatorInfo); - } - } - return emulators; - }).future()(); - } - - public getAndroidEmulators(): IFuture { - return (() => { - let androidPath = path.join(process.env.ANDROID_HOME, "tools", "android"); - let text:string = this.$childProcess.exec(`"${androidPath}" list avd`).wait(); - let notLoadedIndex = text.indexOf("The following"); - if (notLoadedIndex > 0) { - text = text.substring(0, notLoadedIndex); - } - let textBlocks = text.split("---------"); - let emulators: IEmulatorInfo[] = []; - for (let block of textBlocks) { - let lines = block.split("\n"); - let info:IEmulatorInfo = { name: "", version: "", id: "", platform: "Android", type: "Emulator" }; - for (let line of lines) { - if (line.indexOf("Target") >= 0) { - info.version = line.substring(line.indexOf(":")+1).replace("Android", "").trim(); - } - if (line.indexOf("Name") >= 0) { - info.id = line.substring(line.indexOf(":")+1).trim(); - } - if (line.indexOf("Device") >= 0) { - info.name = line.substring(line.indexOf(":")+1).trim(); - } - info.isRunning = false; - } - emulators.push(info); - } - return emulators; - }).future()(); - } - - private parseiOSVersion(osName: string): string { - osName = osName.replace("com.apple.CoreSimulator.SimRuntime.iOS-", ""); - osName = osName.replace(/-/g, "."); - osName = osName.replace("iOS", ""); - osName = osName.trim(); - return osName; - } - - private outputEmulators(title: string, emulators: IEmulatorInfo[]) { - this.$logger.out(title); - let table: any = createTable(["Device Name", "Platform", "Version", "Device Identifier"], []); - for (let info of emulators) { - table.push([info.name, info.platform, info.version, info.id]); - } - this.$logger.out(table.toString()); - } + } + + return emulators; + } + + public async getAndroidEmulators(): Promise { + let androidPath = path.join(process.env.ANDROID_HOME, "tools", "android"); + let text: string = await this.$childProcess.exec(`"${androidPath}" list avd`); + let notLoadedIndex = text.indexOf("The following"); + if (notLoadedIndex > 0) { + text = text.substring(0, notLoadedIndex); + } + + let textBlocks = text.split("---------"); + let emulators: IEmulatorInfo[] = []; + for (let block of textBlocks) { + let lines = block.split("\n"); + let info: IEmulatorInfo = { name: "", version: "", id: "", platform: "Android", type: "Emulator" }; + for (let line of lines) { + if (line.indexOf("Target") >= 0) { + info.version = line.substring(line.indexOf(":") + 1).replace("Android", "").trim(); + } + + if (line.indexOf("Name") >= 0) { + info.id = line.substring(line.indexOf(":") + 1).trim(); + } + + if (line.indexOf("Device") >= 0) { + info.name = line.substring(line.indexOf(":") + 1).trim(); + } + + info.isRunning = false; + } + + emulators.push(info); + } + + return emulators; + } + + private parseiOSVersion(osName: string): string { + osName = osName.replace("com.apple.CoreSimulator.SimRuntime.iOS-", ""); + osName = osName.replace(/-/g, "."); + osName = osName.replace("iOS", ""); + osName = osName.trim(); + return osName; + } + + private outputEmulators(title: string, emulators: IEmulatorInfo[]) { + this.$logger.out(title); + let table: any = createTable(["Device Name", "Platform", "Version", "Device Identifier"], []); + for (let info of emulators) { + table.push([info.name, info.platform, info.version, info.id]); + } + + this.$logger.out(table.toString()); + } } + $injector.register("emulatorPlatformService", EmulatorPlatformService); diff --git a/lib/services/info-service.ts b/lib/services/info-service.ts index 24dbe4556a..40eb96a1b3 100644 --- a/lib/services/info-service.ts +++ b/lib/services/info-service.ts @@ -2,15 +2,13 @@ export class InfoService implements IInfoService { constructor(private $versionsService: IVersionsService, private $logger: ILogger) { } - public printComponentsInfo(): IFuture { - return (() => { - let allComponentsInfo = this.$versionsService.getAllComponentsVersions().wait(); + public async printComponentsInfo(): Promise { + let allComponentsInfo = await this.$versionsService.getAllComponentsVersions(); - let table: any = this.$versionsService.createTableWithVersionsInformation(allComponentsInfo); + let table: any = this.$versionsService.createTableWithVersionsInformation(allComponentsInfo); - this.$logger.out("All NativeScript components versions information"); - this.$logger.out(table.toString()); - }).future()(); + this.$logger.out("All NativeScript components versions information"); + this.$logger.out(table.toString()); } } diff --git a/lib/services/init-service.ts b/lib/services/init-service.ts index 0de83dff8c..d82129871b 100644 --- a/lib/services/init-service.ts +++ b/lib/services/init-service.ts @@ -24,57 +24,56 @@ export class InitService implements IInitService { private $npm: INodePackageManager, private $npmInstallationManager: INpmInstallationManager) { } - public initialize(): IFuture { - return (() => { - let projectData: any = {}; + public async initialize(): Promise { + let projectData: any = {}; - if (this.$fs.exists(this.projectFilePath)) { - projectData = this.$fs.readJson(this.projectFilePath); - } + if (this.$fs.exists(this.projectFilePath)) { + projectData = this.$fs.readJson(this.projectFilePath); + } + + let projectDataBackup = _.extend({}, projectData); + + if (!projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE]) { + projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE] = {}; + this.$fs.writeJson(this.projectFilePath, projectData); // We need to create package.json file here in order to prevent "No project found at or above and neither was a --path specified." when resolving platformsData + } - let projectDataBackup = _.extend({}, projectData); + try { - if (!projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE]) { - projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE] = {}; - this.$fs.writeJson(this.projectFilePath, projectData); // We need to create package.json file here in order to prevent "No project found at or above and neither was a --path specified." when resolving platformsData + projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE]["id"] = await this.getProjectId(); + + if (this.$options.frameworkName && this.$options.frameworkVersion) { + let currentPlatformData = projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE][this.$options.frameworkName] || {}; + + projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE][this.$options.frameworkName] = _.extend(currentPlatformData, this.buildVersionData(this.$options.frameworkVersion)); + } else { + let $platformsData = this.$injector.resolve("platformsData"); + for (let platform of $platformsData.platformsNames) { + let platformData: IPlatformData = $platformsData.getPlatformData(platform); + if (!platformData.targetedOS || (platformData.targetedOS && _.includes(platformData.targetedOS, process.platform))) { + let currentPlatformData = projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE][platformData.frameworkPackageName] || {}; + + projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE][platformData.frameworkPackageName] = _.extend(currentPlatformData, await this.getVersionData(platformData.frameworkPackageName)); + } + }; } - try { - - projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE]["id"] = this.getProjectId().wait(); - - if (this.$options.frameworkName && this.$options.frameworkVersion) { - let currentPlatformData = projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE][this.$options.frameworkName] || {}; - - projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE][this.$options.frameworkName] = _.extend(currentPlatformData, this.buildVersionData(this.$options.frameworkVersion)); - } else { - let $platformsData = this.$injector.resolve("platformsData"); - _.each($platformsData.platformsNames, platform => { - let platformData: IPlatformData = $platformsData.getPlatformData(platform); - if (!platformData.targetedOS || (platformData.targetedOS && _.includes(platformData.targetedOS, process.platform))) { - let currentPlatformData = projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE][platformData.frameworkPackageName] || {}; - - projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE][platformData.frameworkPackageName] = _.extend(currentPlatformData, this.getVersionData(platformData.frameworkPackageName).wait()); - } - }); - } - - let dependencies = projectData.dependencies; - if (!dependencies) { - projectData.dependencies = Object.create(null); - } - // In case console is interactive and --force is not specified, do not read the version from package.json, show all available versions to the user. - let tnsCoreModulesVersionInPackageJson = this.useDefaultValue ? projectData.dependencies[constants.TNS_CORE_MODULES_NAME] : null; - projectData.dependencies[constants.TNS_CORE_MODULES_NAME] = this.$options.tnsModulesVersion || tnsCoreModulesVersionInPackageJson || this.getVersionData(constants.TNS_CORE_MODULES_NAME).wait()["version"]; - - this.$fs.writeJson(this.projectFilePath, projectData); - } catch (err) { - this.$fs.writeJson(this.projectFilePath, projectDataBackup); - throw err; + let dependencies = projectData.dependencies; + if (!dependencies) { + projectData.dependencies = Object.create(null); } - this.$logger.out("Project successfully initialized."); - }).future()(); + // In case console is interactive and --force is not specified, do not read the version from package.json, show all available versions to the user. + let tnsCoreModulesVersionInPackageJson = this.useDefaultValue ? projectData.dependencies[constants.TNS_CORE_MODULES_NAME] : null; + projectData.dependencies[constants.TNS_CORE_MODULES_NAME] = this.$options.tnsModulesVersion || tnsCoreModulesVersionInPackageJson || (await this.getVersionData(constants.TNS_CORE_MODULES_NAME))["version"]; + + this.$fs.writeJson(this.projectFilePath, projectData); + } catch (err) { + this.$fs.writeJson(this.projectFilePath, projectDataBackup); + throw err; + } + + this.$logger.out("Project successfully initialized."); } private get projectFilePath(): string { @@ -86,40 +85,36 @@ export class InitService implements IInitService { return this._projectFilePath; } - private getProjectId(): IFuture { - return (() => { - if (this.$options.appid) { - return this.$options.appid; - } + private async getProjectId(): Promise { + if (this.$options.appid) { + return this.$options.appid; + } - let defaultAppId = this.$projectHelper.generateDefaultAppId(path.basename(path.dirname(this.projectFilePath)), constants.DEFAULT_APP_IDENTIFIER_PREFIX); - if (this.useDefaultValue) { - return defaultAppId; - } + let defaultAppId = this.$projectHelper.generateDefaultAppId(path.basename(path.dirname(this.projectFilePath)), constants.DEFAULT_APP_IDENTIFIER_PREFIX); + if (this.useDefaultValue) { + return defaultAppId; + } - return this.$prompter.getString("Id:", { defaultAction: () => defaultAppId }).wait(); - }).future()(); + return await this.$prompter.getString("Id:", { defaultAction: () => defaultAppId }); } - private getVersionData(packageName: string): IFuture { - return (() => { - let latestVersion = this.$npmInstallationManager.getLatestCompatibleVersion(packageName).wait(); + private async getVersionData(packageName: string): Promise { + let latestVersion = await this.$npmInstallationManager.getLatestCompatibleVersion(packageName); - if (this.useDefaultValue) { - return this.buildVersionData(latestVersion); - } + if (this.useDefaultValue) { + return this.buildVersionData(latestVersion); + } - let data:any = this.$npm.view(packageName, "versions").wait(); - let versions = _.filter(data[latestVersion].versions, (version: string) => semver.gte(version, InitService.MIN_SUPPORTED_FRAMEWORK_VERSIONS[packageName])); - if (versions.length === 1) { - this.$logger.info(`Only ${versions[0]} version is available for ${packageName}.`); - return this.buildVersionData(versions[0]); - } - let sortedVersions = versions.sort(helpers.versionCompare).reverse(); - //TODO: plamen5kov: don't offer versions from next (they are not available) - let version = this.$prompter.promptForChoice(`${packageName} version:`, sortedVersions).wait(); - return this.buildVersionData(version); - }).future()(); + let data: any = await this.$npm.view(packageName, "versions"); + let versions = _.filter(data[latestVersion].versions, (version: string) => semver.gte(version, InitService.MIN_SUPPORTED_FRAMEWORK_VERSIONS[packageName])); + if (versions.length === 1) { + this.$logger.info(`Only ${versions[0]} version is available for ${packageName}.`); + return this.buildVersionData(versions[0]); + } + let sortedVersions = versions.sort(helpers.versionCompare).reverse(); + //TODO: plamen5kov: don't offer versions from next (they are not available) + let version = await this.$prompter.promptForChoice(`${packageName} version:`, sortedVersions); + return this.buildVersionData(version); } private buildVersionData(version: string): IStringDictionary { diff --git a/lib/services/ios-debug-service.ts b/lib/services/ios-debug-service.ts index d0301e6dfb..5790c4e99d 100644 --- a/lib/services/ios-debug-service.ts +++ b/lib/services/ios-debug-service.ts @@ -3,7 +3,7 @@ import * as net from "net"; import * as path from "path"; import * as log4js from "log4js"; import * as os from "os"; -import {ChildProcess} from "child_process"; +import { ChildProcess } from "child_process"; import byline = require("byline"); const inspectorBackendPort = 18181; @@ -18,34 +18,29 @@ class IOSDebugService implements IDebugService { private _childProcess: ChildProcess; private _socketProxy: any; - constructor( - private $config: IConfiguration, - private $platformService: IPlatformService, + constructor(private $platformService: IPlatformService, private $iOSEmulatorServices: Mobile.IEmulatorPlatformServices, private $devicesService: Mobile.IDevicesService, private $platformsData: IPlatformsData, private $projectData: IProjectData, private $childProcess: IChildProcess, private $logger: ILogger, - private $fs: IFileSystem, private $errors: IErrors, - private $injector: IInjector, private $npmInstallationManager: INpmInstallationManager, private $options: IOptions, private $utils: IUtils, private $iOSNotification: IiOSNotification, private $iOSSocketRequestExecutor: IiOSSocketRequestExecutor, private $processService: IProcessService, - private $socketProxyFactory: ISocketProxyFactory, - private $npm: INodePackageManager) { - this.$processService.attachToProcessExitSignals(this, this.debugStop); - } + private $socketProxyFactory: ISocketProxyFactory) { + this.$processService.attachToProcessExitSignals(this, this.debugStop); + } public get platform(): string { return "ios"; } - public debug(): IFuture { + public async debug(): Promise { if (this.$options.debugBrk && this.$options.start) { this.$errors.failWithoutHelp("Expected exactly one of the --debug-brk or --start options."); } @@ -73,153 +68,136 @@ class IOSDebugService implements IDebugService { } } - public debugStart(): IFuture { - return (() => { - this.$devicesService.initialize({ platform: this.platform, deviceId: this.$options.device }).wait(); - this.$devicesService.execute((device: Mobile.IiOSDevice) => device.isEmulator ? this.emulatorDebugBrk() : this.debugBrkCore(device)).wait(); - }).future()(); + public async debugStart(): Promise { + await this.$devicesService.initialize({ platform: this.platform, deviceId: this.$options.device }); + this.$devicesService.execute(async (device: Mobile.IiOSDevice) => await device.isEmulator ? this.emulatorDebugBrk() : this.debugBrkCore(device)); } - public debugStop(): IFuture { - return (() => { - if (this._socketProxy) { - this._socketProxy.close(); - this._socketProxy = null; - } + public async debugStop(): Promise { + if (this._socketProxy) { + this._socketProxy.close(); + this._socketProxy = null; + } - _.forEach(this._sockets, socket => socket.destroy()); - this._sockets = []; + _.forEach(this._sockets, socket => socket.destroy()); + this._sockets = []; - if (this._lldbProcess) { - this._lldbProcess.stdin.write("process detach\n"); - this._lldbProcess.kill(); - this._lldbProcess = undefined; - } + if (this._lldbProcess) { + this._lldbProcess.stdin.write("process detach\n"); + this._lldbProcess.kill(); + this._lldbProcess = undefined; + } - if (this._childProcess) { - this._childProcess.kill(); - this._childProcess = undefined; - } - }).future()(); + if (this._childProcess) { + this._childProcess.kill(); + this._childProcess = undefined; + } } - private emulatorDebugBrk(shouldBreak?: boolean): IFuture { - return (() => { - let platformData = this.$platformsData.getPlatformData(this.platform); - let emulatorPackage = this.$platformService.getLatestApplicationPackageForEmulator(platformData); - - let args = shouldBreak ? "--nativescript-debug-brk" : "--nativescript-debug-start"; - let child_process = this.$iOSEmulatorServices.runApplicationOnEmulator(emulatorPackage.packageName, { - waitForDebugger: true, captureStdin: true, - args: args, appId: this.$projectData.projectId, - skipInstall: true - }).wait(); - let lineStream = byline(child_process.stdout); - this._childProcess = child_process; - - lineStream.on('data', (line: NodeBuffer) => { - let lineText = line.toString(); - if (lineText && _.startsWith(lineText, this.$projectData.projectId)) { - let pid = _.trimStart(lineText, this.$projectData.projectId + ": "); - this._lldbProcess = this.$childProcess.spawn("lldb", [ "-p", pid]); - if (log4js.levels.TRACE.isGreaterThanOrEqualTo(this.$logger.getLevel())) { - this._lldbProcess.stdout.pipe(process.stdout); - } - this._lldbProcess.stderr.pipe(process.stderr); - this._lldbProcess.stdin.write("process continue\n"); - } else { - process.stdout.write(line + "\n"); + private async emulatorDebugBrk(shouldBreak?: boolean): Promise { + let platformData = this.$platformsData.getPlatformData(this.platform); + + let emulatorPackage = this.$platformService.getLatestApplicationPackageForEmulator(platformData); + + let args = shouldBreak ? "--nativescript-debug-brk" : "--nativescript-debug-start"; + let child_process = await this.$iOSEmulatorServices.runApplicationOnEmulator(emulatorPackage.packageName, { + waitForDebugger: true, captureStdin: true, + args: args, appId: this.$projectData.projectId, + skipInstall: true + }); + + let lineStream = byline(child_process.stdout); + this._childProcess = child_process; + + lineStream.on('data', (line: NodeBuffer) => { + let lineText = line.toString(); + if (lineText && _.startsWith(lineText, this.$projectData.projectId)) { + let pid = _.trimStart(lineText, this.$projectData.projectId + ": "); + this._lldbProcess = this.$childProcess.spawn("lldb", ["-p", pid]); + if (log4js.levels.TRACE.isGreaterThanOrEqualTo(this.$logger.getLevel())) { + this._lldbProcess.stdout.pipe(process.stdout); } - }); + this._lldbProcess.stderr.pipe(process.stderr); + this._lldbProcess.stdin.write("process continue\n"); + } else { + process.stdout.write(line + "\n"); + } + }); - this.wireDebuggerClient().wait(); - }).future()(); + await this.wireDebuggerClient(); } - private emulatorStart(): IFuture { - return (() => { - this.wireDebuggerClient().wait(); + private async emulatorStart(): Promise { + await this.wireDebuggerClient(); - let attachRequestMessage = this.$iOSNotification.attachRequest; + let attachRequestMessage = this.$iOSNotification.attachRequest; - let iOSEmulator = this.$iOSEmulatorServices; - iOSEmulator.postDarwinNotification(attachRequestMessage).wait(); - }).future()(); + let iOSEmulator = this.$iOSEmulatorServices; + await iOSEmulator.postDarwinNotification(attachRequestMessage); } - private deviceDebugBrk(shouldBreak?: boolean): IFuture { - return (() => { - this.$devicesService.initialize({ platform: this.platform, deviceId: this.$options.device }).wait(); - this.$devicesService.execute((device: iOSDevice.IOSDevice) => (() => { - if (device.isEmulator) { - return this.emulatorDebugBrk(shouldBreak).wait(); - } - // we intentionally do not wait on this here, because if we did, we'd miss the AppLaunching notification - let action = this.$platformService.runPlatform(this.platform); - this.debugBrkCore(device, shouldBreak).wait(); - action.wait(); - }).future()()).wait(); - }).future()(); + private async deviceDebugBrk(shouldBreak?: boolean): Promise { + await this.$devicesService.initialize({ platform: this.platform, deviceId: this.$options.device }); + this.$devicesService.execute(async (device: iOSDevice.IOSDevice) => { + if (device.isEmulator) { + return await this.emulatorDebugBrk(shouldBreak); + } + + // we intentionally do not wait on this here, because if we did, we'd miss the AppLaunching notification + let action = this.$platformService.runPlatform(this.platform); + + await this.debugBrkCore(device, shouldBreak); + + await action; + }); } - private debugBrkCore(device: Mobile.IiOSDevice, shouldBreak?: boolean): IFuture { - return (() => { - let timeout = this.$utils.getMilliSecondsTimeout(TIMEOUT_SECONDS); - this.$iOSSocketRequestExecutor.executeLaunchRequest(device, timeout, timeout, shouldBreak).wait(); - this.wireDebuggerClient(device).wait(); - }).future()(); + private async debugBrkCore(device: Mobile.IiOSDevice, shouldBreak?: boolean): Promise { + let timeout = this.$utils.getMilliSecondsTimeout(TIMEOUT_SECONDS); + await this.$iOSSocketRequestExecutor.executeLaunchRequest(device, timeout, timeout, shouldBreak); + await this.wireDebuggerClient(device); } - private deviceStart(): IFuture { - return (() => { - this.$devicesService.initialize({ platform: this.platform, deviceId: this.$options.device }).wait(); - this.$devicesService.execute((device: Mobile.IiOSDevice) => device.isEmulator ? this.emulatorStart() : this.deviceStartCore(device)).wait(); - }).future()(); + private async deviceStart(): Promise { + await this.$devicesService.initialize({ platform: this.platform, deviceId: this.$options.device }); + this.$devicesService.execute(async (device: Mobile.IiOSDevice) => device.isEmulator ? await this.emulatorStart() : await this.deviceStartCore(device)); } - private deviceStartCore(device: Mobile.IiOSDevice): IFuture { - return (() => { - let timeout = this.$utils.getMilliSecondsTimeout(TIMEOUT_SECONDS); - this.$iOSSocketRequestExecutor.executeAttachRequest(device, timeout).wait(); - this.wireDebuggerClient(device).wait(); - }).future()(); + private async deviceStartCore(device: Mobile.IiOSDevice): Promise { + let timeout = this.$utils.getMilliSecondsTimeout(TIMEOUT_SECONDS); + await this.$iOSSocketRequestExecutor.executeAttachRequest(device, timeout); + await this.wireDebuggerClient(device); } - private wireDebuggerClient(device?: Mobile.IiOSDevice): IFuture { - return (() => { - let factory = () => { - let socket = device ? device.connectToPort(inspectorBackendPort) : net.connect(inspectorBackendPort); - this._sockets.push(socket); - return socket; - }; + private async wireDebuggerClient(device?: Mobile.IiOSDevice): Promise { + let factory = () => { + let socket = device ? device.connectToPort(inspectorBackendPort) : net.connect(inspectorBackendPort); + this._sockets.push(socket); + return socket; + }; - if (this.$options.chrome) { - this._socketProxy = this.$socketProxyFactory.createWebSocketProxy(factory); + if (this.$options.chrome) { + this._socketProxy = this.$socketProxyFactory.createWebSocketProxy(factory); - const commitSHA = "02e6bde1bbe34e43b309d4ef774b1168d25fd024"; // corresponds to 55.0.2883 Chrome version - this.$logger.info(`To start debugging, open the following URL in Chrome:${os.EOL}chrome-devtools://devtools/remote/serve_file/@${commitSHA}/inspector.html?experiments=true&ws=localhost:${this._socketProxy.options.port}${os.EOL}`.cyan); - } else { - this._socketProxy = this.$socketProxyFactory.createTCPSocketProxy(factory); - this.openAppInspector(this._socketProxy.address()).wait(); - } - }).future()(); + const commitSHA = "02e6bde1bbe34e43b309d4ef774b1168d25fd024"; // corresponds to 55.0.2883 Chrome version + this.$logger.info(`To start debugging, open the following URL in Chrome:${os.EOL}chrome-devtools://devtools/remote/serve_file/@${commitSHA}/inspector.html?experiments=true&ws=localhost:${this._socketProxy.options.port}${os.EOL}`.cyan); + } else { + this._socketProxy = this.$socketProxyFactory.createTCPSocketProxy(factory); + await this.openAppInspector(this._socketProxy.address()); + } } - private openAppInspector(fileDescriptor: string): IFuture { + private async openAppInspector(fileDescriptor: string): Promise { if (this.$options.client) { - return (() => { - let inspectorPath = this.$npmInstallationManager.getInspectorFromCache(inspectorNpmPackageName, this.$projectData.projectDir).wait(); + let inspectorPath = await this.$npmInstallationManager.getInspectorFromCache(inspectorNpmPackageName, this.$projectData.projectDir); - let inspectorSourceLocation = path.join(inspectorPath, inspectorUiDir, "Main.html"); - let inspectorApplicationPath = path.join(inspectorPath, inspectorAppName); + let inspectorSourceLocation = path.join(inspectorPath, inspectorUiDir, "Main.html"); + let inspectorApplicationPath = path.join(inspectorPath, inspectorAppName); - let cmd = `open -a '${inspectorApplicationPath}' --args '${inspectorSourceLocation}' '${this.$projectData.projectName}' '${fileDescriptor}'`; - this.$childProcess.exec(cmd).wait(); - }).future()(); + let cmd = `open -a '${inspectorApplicationPath}' --args '${inspectorSourceLocation}' '${this.$projectData.projectName}' '${fileDescriptor}'`; + await this.$childProcess.exec(cmd); } else { - return (() => { - this.$logger.info("Suppressing debugging client."); - }).future()(); + this.$logger.info("Suppressing debugging client."); } } } diff --git a/lib/services/ios-log-filter.ts b/lib/services/ios-log-filter.ts index 6c7f48300f..2619dedc45 100644 --- a/lib/services/ios-log-filter.ts +++ b/lib/services/ios-log-filter.ts @@ -1,13 +1,12 @@ let sourcemap = require("source-map"); import * as path from "path"; -import fs = require("fs"); export class IOSLogFilter implements Mobile.IPlatformLogFilter { private $projectData: IProjectData; private partialLine: string = null; - constructor(private $fs: IFileSystem, private $loggingLevels: Mobile.ILoggingLevels) { + constructor(private $fs: IFileSystem) { } public filterData(data: string, logLevel: string, pid?: string): string { @@ -16,12 +15,12 @@ export class IOSLogFilter implements Mobile.IPlatformLogFilter { return null; } - let skipLastLine = data[data.length-1] !== "\n"; + let skipLastLine = data[data.length - 1] !== "\n"; if (data) { let lines = data.split("\n"); let result = ""; - for (let i = 0; i if (pid) { - let searchString = "["+pid+"]: "; + let searchString = "[" + pid + "]: "; let pidIndex = line.indexOf(searchString); if (pidIndex > 0) { line = line.substring(pidIndex + searchString.length, line.length); @@ -47,7 +46,7 @@ export class IOSLogFilter implements Mobile.IPlatformLogFilter { continue; } } - if (skipLastLine && i === lines.length-1) { + if (skipLastLine && i === lines.length - 1) { this.partialLine = line; } else { result += this.getOriginalFileLocation(line) + "\n"; @@ -62,30 +61,33 @@ export class IOSLogFilter implements Mobile.IPlatformLogFilter { private getOriginalFileLocation(data: string): string { let fileString = "file:///"; let fileIndex = data.indexOf(fileString); + if (fileIndex >= 0) { let parts = data.substring(fileIndex + fileString.length).split(":"); if (parts.length >= 4) { let file = parts[0]; if (this.ensureProjectData()) { - let sourceMapFile = path.join(this.$projectData.projectDir, file + ".map"); - let row = parseInt(parts[1]); - let column = parseInt(parts[2]); - if (fs.existsSync(sourceMapFile)) { - let sourceMap = fs.readFileSync(sourceMapFile, "utf8"); + let sourceMapFile = path.join(this.$projectData.projectDir, file + ".map"); + let row = parseInt(parts[1]); + let column = parseInt(parts[2]); + if (this.$fs.exists(sourceMapFile)) { + let sourceMap = this.$fs.readText(sourceMapFile); let smc = new sourcemap.SourceMapConsumer(sourceMap); - let originalPosition = smc.originalPositionFor({line:row,column:column}); + let originalPosition = smc.originalPositionFor({ line: row, column: column }); let sourceFile = smc.sources.length > 0 ? file.replace(smc.file, smc.sources[0]) : file; data = data.substring(0, fileIndex + fileString.length) + sourceFile + ":" + originalPosition.line + ":" + originalPosition.column; - for (let i = 3; i { - let future = new Future(); + public async awaitNotification(npc: Mobile.INotificationProxyClient, notification: string, timeout: number): Promise { + return new Promise((resolve, reject) => { - let timeoutToken = setTimeout(() => { - detachObserver(); - future.throw(new Error(`Timeout receiving ${notification} notification.`)); - }, timeout); + let timeoutToken = setTimeout(() => { + detachObserver(); + reject(new Error(`Timeout receiving ${notification} notification.`)); + }, timeout); - function notificationObserver(_notification: string) { - clearTimeout(timeoutToken); - detachObserver(); - future.return(_notification); - } + function notificationObserver(_notification: string) { + clearTimeout(timeoutToken); + detachObserver(); + resolve(_notification); + } - function detachObserver() { - process.nextTick(() => npc.removeObserver(notification, notificationObserver)); - } + function detachObserver() { + process.nextTick(() => npc.removeObserver(notification, notificationObserver)); + } - npc.addObserver(notification, notificationObserver); + npc.addObserver(notification, notificationObserver); - return future; + }); } } $injector.register("iOSNotificationService", IOSNotificationService); diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index 5876d8342f..13e7207f85 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -6,7 +6,6 @@ import * as xcode from "xcode"; import * as constants from "../constants"; import * as helpers from "../common/helpers"; import * as projectServiceBaseLib from "./platform-project-service-base"; -import Future = require("fibers/future"); import { PlistSession } from "plist-merge-patch"; import { EOL } from "os"; import * as temp from "temp"; @@ -33,7 +32,6 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ private $errors: IErrors, private $logger: ILogger, private $iOSEmulatorServices: Mobile.IEmulatorPlatformServices, - private $iOSSimResolver: Mobile.IiOSSimResolver, private $options: IOptions, private $injector: IInjector, $projectDataService: IProjectDataService, @@ -77,15 +75,14 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ }; } - public validateOptions(): IFuture { - return (() => { - if (this.$options.provision === true) { - this.$iOSProvisionService.list().wait(); - this.$errors.failWithoutHelp("Please provide provisioning profile uuid or name with the --provision option."); - return false; - } - return true; - }).future()(); + public async validateOptions(): Promise { + if (this.$options.provision === true) { + await this.$iOSProvisionService.list(); + this.$errors.failWithoutHelp("Please provide provisioning profile uuid or name with the --provision option."); + return false; + } + + return true; } public getAppResourcesDestinationDirectoryPath(): string { @@ -98,72 +95,66 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ return path.join(this.platformData.projectRoot, this.$projectData.projectName, "Resources"); } - public validate(): IFuture { - return (() => { - try { - this.$childProcess.exec("which xcodebuild").wait(); - } catch (error) { - this.$errors.fail("Xcode is not installed. Make sure you have Xcode installed and added to your PATH"); - } - - let xcodeBuildVersion = this.getXcodeVersion(); - if (helpers.versionCompare(xcodeBuildVersion, IOSProjectService.XCODEBUILD_MIN_VERSION) < 0) { - this.$errors.fail("NativeScript can only run in Xcode version %s or greater", IOSProjectService.XCODEBUILD_MIN_VERSION); - } + public async validate(): Promise { + try { + await this.$childProcess.exec("which xcodebuild"); + } catch (error) { + this.$errors.fail("Xcode is not installed. Make sure you have Xcode installed and added to your PATH"); + } - }).future()(); + let xcodeBuildVersion = await this.getXcodeVersion(); + if (helpers.versionCompare(xcodeBuildVersion, IOSProjectService.XCODEBUILD_MIN_VERSION) < 0) { + this.$errors.fail("NativeScript can only run in Xcode version %s or greater", IOSProjectService.XCODEBUILD_MIN_VERSION); + } } - // TODO: Remove IFuture, reason: readDirectory - unable until androidProjectService has async operations. - public createProject(frameworkDir: string, frameworkVersion: string, pathToTemplate?: string): IFuture { - return (() => { - this.$fs.ensureDirectoryExists(path.join(this.platformData.projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER)); - if (pathToTemplate) { - // Copy everything except the template from the runtime - this.$fs.readDirectory(frameworkDir) - .filter(dirName => dirName.indexOf(IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER) === -1) - .forEach(dirName => shell.cp("-R", path.join(frameworkDir, dirName), this.platformData.projectRoot)); - shell.cp("-rf", path.join(pathToTemplate, "*"), this.platformData.projectRoot); - } else { - shell.cp("-R", path.join(frameworkDir, "*"), this.platformData.projectRoot); - } - }).future()(); + // TODO: Remove Promise, reason: readDirectory - unable until androidProjectService has async operations. + public async createProject(frameworkDir: string, frameworkVersion: string, pathToTemplate?: string): Promise { + this.$fs.ensureDirectoryExists(path.join(this.platformData.projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER)); + if (pathToTemplate) { + // Copy everything except the template from the runtime + this.$fs.readDirectory(frameworkDir) + .filter(dirName => dirName.indexOf(IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER) === -1) + .forEach(dirName => shell.cp("-R", path.join(frameworkDir, dirName), this.platformData.projectRoot)); + shell.cp("-rf", path.join(pathToTemplate, "*"), this.platformData.projectRoot); + } else { + shell.cp("-R", path.join(frameworkDir, "*"), this.platformData.projectRoot); + } + } - //TODO: plamen5kov: revisit this method, might have unnecessary/obsolete logic - public interpolateData(): IFuture { - return (() => { - let projectRootFilePath = path.join(this.platformData.projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER); - // Starting with NativeScript for iOS 1.6.0, the project Info.plist file resides not in the platform project, - // but in the hello-world app template as a platform specific resource. - if (this.$fs.exists(path.join(projectRootFilePath, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + "-Info.plist"))) { - this.replaceFileName("-Info.plist", projectRootFilePath); - } - this.replaceFileName("-Prefix.pch", projectRootFilePath); - - let xcschemeDirPath = path.join(this.platformData.projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + IOSProjectService.XCODE_PROJECT_EXT_NAME, "xcshareddata/xcschemes"); - let xcschemeFilePath = path.join(xcschemeDirPath, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + IOSProjectService.XCODE_SCHEME_EXT_NAME); - - if (this.$fs.exists(xcschemeFilePath)) { - this.$logger.debug("Found shared scheme at xcschemeFilePath, renaming to match project name."); - this.$logger.debug("Checkpoint 0"); - this.replaceFileContent(xcschemeFilePath); - this.$logger.debug("Checkpoint 1"); - this.replaceFileName(IOSProjectService.XCODE_SCHEME_EXT_NAME, xcschemeDirPath); - this.$logger.debug("Checkpoint 2"); - } else { - this.$logger.debug("Copying xcscheme from template not found at " + xcschemeFilePath); - } - this.replaceFileName(IOSProjectService.XCODE_PROJECT_EXT_NAME, this.platformData.projectRoot); + //TODO: plamen5kov: revisit this method, might have unnecessary/obsolete logic + public async interpolateData(): Promise { + let projectRootFilePath = path.join(this.platformData.projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER); + // Starting with NativeScript for iOS 1.6.0, the project Info.plist file resides not in the platform project, + // but in the hello-world app template as a platform specific resource. + if (this.$fs.exists(path.join(projectRootFilePath, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + "-Info.plist"))) { + this.replaceFileName("-Info.plist", projectRootFilePath); + } + this.replaceFileName("-Prefix.pch", projectRootFilePath); + + let xcschemeDirPath = path.join(this.platformData.projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + IOSProjectService.XCODE_PROJECT_EXT_NAME, "xcshareddata/xcschemes"); + let xcschemeFilePath = path.join(xcschemeDirPath, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + IOSProjectService.XCODE_SCHEME_EXT_NAME); + + if (this.$fs.exists(xcschemeFilePath)) { + this.$logger.debug("Found shared scheme at xcschemeFilePath, renaming to match project name."); + this.$logger.debug("Checkpoint 0"); + this.replaceFileContent(xcschemeFilePath); + this.$logger.debug("Checkpoint 1"); + this.replaceFileName(IOSProjectService.XCODE_SCHEME_EXT_NAME, xcschemeDirPath); + this.$logger.debug("Checkpoint 2"); + } else { + this.$logger.debug("Copying xcscheme from template not found at " + xcschemeFilePath); + } - let pbxprojFilePath = this.pbxProjPath; - this.replaceFileContent(pbxprojFilePath); + this.replaceFileName(IOSProjectService.XCODE_PROJECT_EXT_NAME, this.platformData.projectRoot); - }).future()(); + let pbxprojFilePath = this.pbxProjPath; + this.replaceFileContent(pbxprojFilePath); } - public interpolateConfigurationFile(configurationFilePath?: string): IFuture { - return Future.fromResult(); + public interpolateConfigurationFile(configurationFilePath?: string): Promise { + return Promise.resolve(); } public afterCreateProject(projectRoot: string): void { @@ -175,40 +166,37 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ * Archive the Xcode project to .xcarchive. * Returns the path to the .xcarchive. */ - public archive(options?: { archivePath?: string }): IFuture { - return (() => { - let projectRoot = this.platformData.projectRoot; - let archivePath = options && options.archivePath ? path.resolve(options.archivePath) : path.join(projectRoot, "/build/archive/", this.$projectData.projectName + ".xcarchive"); - let args = ["archive", "-archivePath", archivePath] - .concat(this.xcbuildProjectArgs(projectRoot, "scheme")); - this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", { cwd: this.$options, stdio: 'inherit' }).wait(); - return archivePath; - }).future()(); + public async archive(options?: { archivePath?: string }): Promise { + let projectRoot = this.platformData.projectRoot; + let archivePath = options && options.archivePath ? path.resolve(options.archivePath) : path.join(projectRoot, "/build/archive/", this.$projectData.projectName + ".xcarchive"); + let args = ["archive", "-archivePath", archivePath] + .concat(this.xcbuildProjectArgs(projectRoot, "scheme")); + await this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", { cwd: this.$options, stdio: 'inherit' }); + return archivePath; } /** * Exports .xcarchive for AppStore distribution. */ - public exportArchive(options: { archivePath: string, exportDir?: string, teamID?: string }): IFuture { - return (() => { - let projectRoot = this.platformData.projectRoot; - let archivePath = options.archivePath; - // The xcodebuild exportPath expects directory and writes the .ipa at that directory. - let exportPath = path.resolve(options.exportDir || path.join(projectRoot, "/build/archive")); - let exportFile = path.join(exportPath, this.$projectData.projectName + ".ipa"); - - // These are the options that you can set in the Xcode UI when exporting for AppStore deployment. - let plistTemplate = ` + public async exportArchive(options: { archivePath: string, exportDir?: string, teamID?: string }): Promise { + let projectRoot = this.platformData.projectRoot; + let archivePath = options.archivePath; + // The xcodebuild exportPath expects directory and writes the .ipa at that directory. + let exportPath = path.resolve(options.exportDir || path.join(projectRoot, "/build/archive")); + let exportFile = path.join(exportPath, this.$projectData.projectName + ".ipa"); + + // These are the options that you can set in the Xcode UI when exporting for AppStore deployment. + let plistTemplate = ` `; - if (options && options.teamID) { - plistTemplate += ` teamID + if (options && options.teamID) { + plistTemplate += ` teamID ${options.teamID} `; - } - plistTemplate += ` method + } + plistTemplate += ` method app-store uploadBitcode @@ -217,20 +205,19 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ `; - // Save the options... - temp.track(); - let exportOptionsPlist = temp.path({ prefix: "export-", suffix: ".plist" }); - this.$fs.writeFile(exportOptionsPlist, plistTemplate); + // Save the options... + temp.track(); + let exportOptionsPlist = temp.path({ prefix: "export-", suffix: ".plist" }); + this.$fs.writeFile(exportOptionsPlist, plistTemplate); - let args = ["-exportArchive", - "-archivePath", archivePath, - "-exportPath", exportPath, - "-exportOptionsPlist", exportOptionsPlist - ]; - this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", { cwd: this.$options, stdio: 'inherit' }).wait(); + let args = ["-exportArchive", + "-archivePath", archivePath, + "-exportPath", exportPath, + "-exportOptionsPlist", exportOptionsPlist + ]; + await this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", { cwd: this.$options, stdio: 'inherit' }); - return exportFile; - }).future()(); + return exportFile; } private xcbuildProjectArgs(projectRoot: string, product?: "scheme" | "target"): string[] { @@ -243,251 +230,237 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ } } - public buildProject(projectRoot: string, buildConfig?: IiOSBuildConfig): IFuture { - return (() => { - let basicArgs = [ - "-configuration", this.$options.release ? "Release" : "Debug", - "build", - 'SHARED_PRECOMPS_DIR=' + path.join(projectRoot, 'build', 'sharedpch') - ]; - basicArgs = basicArgs.concat(this.xcbuildProjectArgs(projectRoot)); + public async buildProject(projectRoot: string, buildConfig?: IiOSBuildConfig): Promise { + let basicArgs = [ + "-configuration", this.$options.release ? "Release" : "Debug", + "build", + 'SHARED_PRECOMPS_DIR=' + path.join(projectRoot, 'build', 'sharedpch') + ]; + basicArgs = basicArgs.concat(this.xcbuildProjectArgs(projectRoot)); - // Starting from tns-ios 1.4 the xcconfig file is referenced in the project template - let frameworkVersion = this.getFrameworkVersion(this.platformData.frameworkPackageName); - if (semver.lt(frameworkVersion, "1.4.0")) { - basicArgs.push("-xcconfig", path.join(projectRoot, this.$projectData.projectName, "build.xcconfig")); - } + // Starting from tns-ios 1.4 the xcconfig file is referenced in the project template + let frameworkVersion = this.getFrameworkVersion(this.platformData.frameworkPackageName); + if (semver.lt(frameworkVersion, "1.4.0")) { + basicArgs.push("-xcconfig", path.join(projectRoot, this.$projectData.projectName, "build.xcconfig")); + } - // if (this.$logger.getLevel() === "INFO") { - // let xcodeBuildVersion = this.getXcodeVersion(); - // if (helpers.versionCompare(xcodeBuildVersion, "8.0") >= 0) { - // basicArgs.push("-quiet"); - // } - // } + // if (this.$logger.getLevel() === "INFO") { + // let xcodeBuildVersion = this.getXcodeVersion(); + // if (helpers.versionCompare(xcodeBuildVersion, "8.0") >= 0) { + // basicArgs.push("-quiet"); + // } + // } - let buildForDevice = this.$options.forDevice || (buildConfig && buildConfig.buildForDevice); - if (buildForDevice) { - this.buildForDevice(projectRoot, basicArgs, buildConfig).wait(); - } else { - this.buildForSimulator(projectRoot, basicArgs, buildConfig).wait(); - } - }).future()(); - } - - private buildForDevice(projectRoot: string, args: string[], buildConfig?: IiOSBuildConfig): IFuture { - return (() => { - let defaultArchitectures = [ - 'ARCHS=armv7 arm64', - 'VALID_ARCHS=armv7 arm64' - ]; - - // build only for device specific architecture - if (!this.$options.release && !(buildConfig && buildConfig.architectures)) { - this.$devicesService.initialize({ platform: this.$devicePlatformsConstants.iOS.toLowerCase(), deviceId: this.$options.device }).wait(); - let instances = this.$devicesService.getDeviceInstances(); - let devicesArchitectures = _(instances) - .filter(d => this.$mobileHelper.isiOSPlatform(d.deviceInfo.platform) && d.deviceInfo.activeArchitecture) - .map(d => d.deviceInfo.activeArchitecture) - .uniq() - .value(); - if (devicesArchitectures.length > 0) { - let architectures = [ - `ARCHS=${devicesArchitectures.join(" ")}`, - `VALID_ARCHS=${devicesArchitectures.join(" ")}` - ]; - if (devicesArchitectures.length > 1) { - architectures.push('ONLY_ACTIVE_ARCH=NO'); - } - if (!buildConfig) { - buildConfig = {}; - } - buildConfig.architectures = architectures; + let buildForDevice = this.$options.forDevice || (buildConfig && buildConfig.buildForDevice); + if (buildForDevice) { + await this.buildForDevice(projectRoot, basicArgs, buildConfig); + } else { + await this.buildForSimulator(projectRoot, basicArgs, buildConfig); + } + + } + + private async buildForDevice(projectRoot: string, args: string[], buildConfig?: IiOSBuildConfig): Promise { + let defaultArchitectures = [ + 'ARCHS=armv7 arm64', + 'VALID_ARCHS=armv7 arm64' + ]; + + // build only for device specific architecture + if (!this.$options.release && !(buildConfig && buildConfig.architectures)) { + await this.$devicesService.initialize({ platform: this.$devicePlatformsConstants.iOS.toLowerCase(), deviceId: this.$options.device }); + let instances = this.$devicesService.getDeviceInstances(); + let devicesArchitectures = _(instances) + .filter(d => this.$mobileHelper.isiOSPlatform(d.deviceInfo.platform) && d.deviceInfo.activeArchitecture) + .map(d => d.deviceInfo.activeArchitecture) + .uniq() + .value(); + if (devicesArchitectures.length > 0) { + let architectures = [ + `ARCHS=${devicesArchitectures.join(" ")}`, + `VALID_ARCHS=${devicesArchitectures.join(" ")}` + ]; + if (devicesArchitectures.length > 1) { + architectures.push('ONLY_ACTIVE_ARCH=NO'); + } + if (!buildConfig) { + buildConfig = {}; } + buildConfig.architectures = architectures; } - args = args.concat((buildConfig && buildConfig.architectures) || defaultArchitectures); + } - args = args.concat([ - "-sdk", "iphoneos", - "CONFIGURATION_BUILD_DIR=" + path.join(projectRoot, "build", "device") - ]); + args = args.concat((buildConfig && buildConfig.architectures) || defaultArchitectures); - let xcodeBuildVersion = this.getXcodeVersion(); - if (helpers.versionCompare(xcodeBuildVersion, "8.0") >= 0) { - this.setupAutomaticSigning(projectRoot, buildConfig).wait(); - } + args = args.concat([ + "-sdk", "iphoneos", + "CONFIGURATION_BUILD_DIR=" + path.join(projectRoot, "build", "device") + ]); - if (buildConfig && buildConfig.codeSignIdentity) { - args.push(`CODE_SIGN_IDENTITY=${buildConfig.codeSignIdentity}`); - } + let xcodeBuildVersion = await this.getXcodeVersion(); + if (helpers.versionCompare(xcodeBuildVersion, "8.0") >= 0) { + await this.setupAutomaticSigning(projectRoot, buildConfig); + } - if (buildConfig && buildConfig.mobileProvisionIdentifier) { - args.push(`PROVISIONING_PROFILE=${buildConfig.mobileProvisionIdentifier}`); - } + if (buildConfig && buildConfig.codeSignIdentity) { + args.push(`CODE_SIGN_IDENTITY=${buildConfig.codeSignIdentity}`); + } - if (buildConfig && buildConfig.teamIdentifier) { - args.push(`DEVELOPMENT_TEAM=${buildConfig.teamIdentifier}`); - } + if (buildConfig && buildConfig.mobileProvisionIdentifier) { + args.push(`PROVISIONING_PROFILE=${buildConfig.mobileProvisionIdentifier}`); + } - // this.$logger.out("xcodebuild..."); - this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", { cwd: this.$options, stdio: 'inherit' }).wait(); - // this.$logger.out("xcodebuild build succeded."); + if (buildConfig && buildConfig.teamIdentifier) { + args.push(`DEVELOPMENT_TEAM=${buildConfig.teamIdentifier}`); + } - this.createIpa(projectRoot).wait(); + // this.$logger.out("xcodebuild..."); + await this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", { cwd: this.$options, stdio: 'inherit' }); + // this.$logger.out("xcodebuild build succeded."); - }).future()(); + await this.createIpa(projectRoot); } - private setupManualSigning(projectRoot: string, buildConfig?: IiOSBuildConfig): IFuture { - return (() => { - if (this.$options.provision) { - const pbxprojPath = path.join(projectRoot, this.$projectData.projectName + ".xcodeproj", "project.pbxproj"); - const xcode = Xcode.open(pbxprojPath); - const signing = xcode.getSigning(this.$projectData.projectName); + private async setupManualSigning(projectRoot: string, buildConfig?: IiOSBuildConfig): Promise { + if (this.$options.provision) { + const pbxprojPath = path.join(projectRoot, this.$projectData.projectName + ".xcodeproj", "project.pbxproj"); + const xcode = Xcode.open(pbxprojPath); + const signing = xcode.getSigning(this.$projectData.projectName); - let shouldUpdateXcode = false; - if (signing && signing.style === "Manual") { - for(let config in signing.configurations) { - let options = signing.configurations[config]; - if (options.name !== this.$options.provision && options.uuid !== this.$options.provision) { - shouldUpdateXcode = true; - break; - } + let shouldUpdateXcode = false; + if (signing && signing.style === "Manual") { + for (let config in signing.configurations) { + let options = signing.configurations[config]; + if (options.name !== this.$options.provision && options.uuid !== this.$options.provision) { + shouldUpdateXcode = true; + break; } - } else { - shouldUpdateXcode = true; } + } else { + shouldUpdateXcode = true; + } - if (shouldUpdateXcode) { - // This is slow, it read through 260 mobileprovision files on my machine and does quite some checking whether provisioning profiles and devices will match. - // That's why we try to avoid id by checking in the Xcode first. - const pickStart = Date.now(); - const mobileprovision = this.$iOSProvisionService.pick(this.$options.provision).wait(); - const pickEnd = Date.now(); - this.$logger.trace("Searched and " + (mobileprovision ? "found" : "failed to find ") + " matching provisioning profile. (" + (pickEnd - pickStart) + "ms.)"); - if (!mobileprovision) { - this.$errors.failWithoutHelp("Failed to find mobile provision with UUID or Name: " + this.$options.provision); - } - - xcode.setManualSigningStyle(this.$projectData.projectName, { - team: mobileprovision.TeamIdentifier && mobileprovision.TeamIdentifier.length > 0 ? mobileprovision.TeamIdentifier[0] : undefined, - uuid: mobileprovision.UUID, - name: mobileprovision.Name, - identity: mobileprovision.Type === "Development" ? "iPhone Developer" : "iPhone Distribution" - }); - xcode.save(); - - // this.cache(uuid); - this.$logger.trace("Set Manual signing style and provisioning profile."); - } else { - this.$logger.trace("The specified provisioning profile allready set in the Xcode."); + if (shouldUpdateXcode) { + // This is slow, it read through 260 mobileprovision files on my machine and does quite some checking whether provisioning profiles and devices will match. + // That's why we try to avoid id by checking in the Xcode first. + const pickStart = Date.now(); + const mobileprovision = await this.$iOSProvisionService.pick(this.$options.provision); + const pickEnd = Date.now(); + this.$logger.trace("Searched and " + (mobileprovision ? "found" : "failed to find ") + " matching provisioning profile. (" + (pickEnd - pickStart) + "ms.)"); + if (!mobileprovision) { + this.$errors.failWithoutHelp("Failed to find mobile provision with UUID or Name: " + this.$options.provision); } + + xcode.setManualSigningStyle(this.$projectData.projectName, { + team: mobileprovision.TeamIdentifier && mobileprovision.TeamIdentifier.length > 0 ? mobileprovision.TeamIdentifier[0] : undefined, + uuid: mobileprovision.UUID, + name: mobileprovision.Name, + identity: mobileprovision.Type === "Development" ? "iPhone Developer" : "iPhone Distribution" + }); + xcode.save(); + + // this.cache(uuid); + this.$logger.trace("Set Manual signing style and provisioning profile."); } else { - // read uuid from Xcode and cache... + this.$logger.trace("The specified provisioning profile allready set in the Xcode."); } - }).future()(); + } else { + // read uuid from Xcode and cache... + } } - private setupAutomaticSigning(projectRoot: string, buildConfig?: IiOSBuildConfig): IFuture { - return (() => { - const pbxprojPath = path.join(projectRoot, this.$projectData.projectName + ".xcodeproj", "project.pbxproj"); - const xcode = Xcode.open(pbxprojPath); - const signing = xcode.getSigning(this.$projectData.projectName); + private async setupAutomaticSigning(projectRoot: string, buildConfig?: IiOSBuildConfig): Promise { + const pbxprojPath = path.join(projectRoot, this.$projectData.projectName + ".xcodeproj", "project.pbxproj"); + const xcode = Xcode.open(pbxprojPath); + const signing = xcode.getSigning(this.$projectData.projectName); - if (!this.$options.provision && !(signing && signing.style === "Manual" && !this.$options.teamId)) { - if (buildConfig) { - delete buildConfig.teamIdentifier; - } + if (!this.$options.provision && !(signing && signing.style === "Manual" && !this.$options.teamId)) { + if (buildConfig) { + delete buildConfig.teamIdentifier; + } - const teamId = this.getDevelopmentTeam(); + const teamId = await this.getDevelopmentTeam(); - xcode.setAutomaticSigningStyle(this.$projectData.projectName, teamId); - xcode.save(); - this.$logger.trace("Set Automatic signing style and team."); - } - }).future()(); - } - - private buildForSimulator(projectRoot: string, args: string[], buildConfig?: IiOSBuildConfig): IFuture { - return (() => { - args = args.concat([ - "-sdk", "iphonesimulator", - "ARCHS=i386 x86_64", - "VALID_ARCHS=i386 x86_64", - "ONLY_ACTIVE_ARCH=NO", - "CONFIGURATION_BUILD_DIR=" + path.join(projectRoot, "build", "emulator"), - "CODE_SIGN_IDENTITY=" - ]); - - this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", { cwd: this.$options, stdio: 'inherit' }).wait(); - - }).future()(); - } - - private createIpa(projectRoot: string): IFuture { - return (() => { - let buildOutputPath = path.join(projectRoot, "build", "device"); - let xcrunArgs = [ - "-sdk", "iphoneos", - "PackageApplication", - path.join(buildOutputPath, this.$projectData.projectName + ".app"), - "-o", path.join(buildOutputPath, this.$projectData.projectName + ".ipa") - ]; - // if (this.$logger.getLevel() !== "INFO") { - xcrunArgs.push("-verbose"); - // } - // this.$logger.out("Packaging project..."); - this.$childProcess.spawnFromEvent("xcrun", xcrunArgs, "exit", { cwd: this.$options, stdio: 'inherit' }).wait(); - // this.$logger.out("Project package succeeded."); - }).future()(); + xcode.setAutomaticSigningStyle(this.$projectData.projectName, teamId); + xcode.save(); + this.$logger.trace("Set Automatic signing style and team."); + } + + } + + private async buildForSimulator(projectRoot: string, args: string[], buildConfig?: IiOSBuildConfig): Promise { + args = args.concat([ + "-sdk", "iphonesimulator", + "ARCHS=i386 x86_64", + "VALID_ARCHS=i386 x86_64", + "ONLY_ACTIVE_ARCH=NO", + "CONFIGURATION_BUILD_DIR=" + path.join(projectRoot, "build", "emulator"), + "CODE_SIGN_IDENTITY=" + ]); + + await this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", { cwd: this.$options, stdio: 'inherit' }); + } + + private async createIpa(projectRoot: string): Promise { + let buildOutputPath = path.join(projectRoot, "build", "device"); + let xcrunArgs = [ + "-sdk", "iphoneos", + "PackageApplication", + path.join(buildOutputPath, this.$projectData.projectName + ".app"), + "-o", path.join(buildOutputPath, this.$projectData.projectName + ".ipa") + ]; + // if (this.$logger.getLevel() !== "INFO") { + xcrunArgs.push("-verbose"); + // } + // this.$logger.out("Packaging project..."); + await this.$childProcess.spawnFromEvent("xcrun", xcrunArgs, "exit", { cwd: this.$options, stdio: 'inherit' }); + // this.$logger.out("Project package succeeded."); } public isPlatformPrepared(projectRoot: string): boolean { return this.$fs.exists(path.join(projectRoot, this.$projectData.projectName, constants.APP_FOLDER_NAME)); } - public deploy(deviceIdentifier: string): IFuture { - return Future.fromResult(); + public deploy(deviceIdentifier: string): Promise { + return Promise.resolve(); } - private addFramework(frameworkPath: string): IFuture { - return (() => { - this.validateFramework(frameworkPath).wait(); + private async addFramework(frameworkPath: string): Promise { + await this.validateFramework(frameworkPath); - let project = this.createPbxProj(); - let frameworkName = path.basename(frameworkPath, path.extname(frameworkPath)); - let frameworkBinaryPath = path.join(frameworkPath, frameworkName); - let isDynamic = _.includes(this.$childProcess.spawnFromEvent("otool", ["-Vh", frameworkBinaryPath], "close").wait().stdout, " DYLIB "); + let project = this.createPbxProj(); + let frameworkName = path.basename(frameworkPath, path.extname(frameworkPath)); + let frameworkBinaryPath = path.join(frameworkPath, frameworkName); + let isDynamic = _.includes((await this.$childProcess.spawnFromEvent("otool", ["-Vh", frameworkBinaryPath], "close")).stdout, " DYLIB "); - let frameworkAddOptions: xcode.Options = { customFramework: true }; + let frameworkAddOptions: xcode.Options = { customFramework: true }; - if (isDynamic) { - frameworkAddOptions["embed"] = true; - } + if (isDynamic) { + frameworkAddOptions["embed"] = true; + } + + let frameworkRelativePath = '$(SRCROOT)/' + this.getLibSubpathRelativeToProjectPath(frameworkPath); + project.addFramework(frameworkRelativePath, frameworkAddOptions); + this.savePbxProj(project); - let frameworkRelativePath = '$(SRCROOT)/' + this.getLibSubpathRelativeToProjectPath(frameworkPath); - project.addFramework(frameworkRelativePath, frameworkAddOptions); - this.savePbxProj(project); - }).future()(); } - private addStaticLibrary(staticLibPath: string): IFuture { - return (() => { - this.validateStaticLibrary(staticLibPath).wait(); - // Copy files to lib folder. - let libraryName = path.basename(staticLibPath, ".a"); - let headersSubpath = path.join(path.dirname(staticLibPath), "include", libraryName); + private async addStaticLibrary(staticLibPath: string): Promise { + await this.validateStaticLibrary(staticLibPath); + // Copy files to lib folder. + let libraryName = path.basename(staticLibPath, ".a"); + let headersSubpath = path.join(path.dirname(staticLibPath), "include", libraryName); - // Add static library to project file and setup header search paths - let project = this.createPbxProj(); - let relativeStaticLibPath = this.getLibSubpathRelativeToProjectPath(staticLibPath); - project.addFramework(relativeStaticLibPath); + // Add static library to project file and setup header search paths + let project = this.createPbxProj(); + let relativeStaticLibPath = this.getLibSubpathRelativeToProjectPath(staticLibPath); + project.addFramework(relativeStaticLibPath); - let relativeHeaderSearchPath = path.join(this.getLibSubpathRelativeToProjectPath(headersSubpath)); - project.addToHeaderSearchPaths({ relativePath: relativeHeaderSearchPath }); + let relativeHeaderSearchPath = path.join(this.getLibSubpathRelativeToProjectPath(headersSubpath)); + project.addToHeaderSearchPaths({ relativePath: relativeHeaderSearchPath }); - this.generateModulemap(headersSubpath, libraryName); - this.savePbxProj(project); - }).future()(); + this.generateModulemap(headersSubpath, libraryName); + this.savePbxProj(project); } public canUpdatePlatform(installedModuleDir: string): boolean { @@ -588,10 +561,10 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } } - public prepareProject(): void { + public async prepareProject(): Promise { if (this.$options.provision) { let projectRoot = path.join(this.$projectData.platformsDir, "ios"); - this.setupManualSigning(projectRoot).wait(); + await this.setupManualSigning(projectRoot); } let project = this.createPbxProj(); @@ -632,15 +605,14 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f this.$fs.deleteDirectory(this.getAppResourcesDestinationDirectoryPath()); } - public processConfigurationFilesFromAppResources(): IFuture { - return (() => { - this.mergeInfoPlists().wait(); - this.mergeProjectXcconfigFiles().wait(); - _(this.getAllInstalledPlugins().wait()) - .map(pluginData => this.$pluginVariablesService.interpolatePluginVariables(pluginData, this.platformData.configurationFilePath).wait()) - .value(); - this.$pluginVariablesService.interpolateAppIdentifier(this.platformData.configurationFilePath); - }).future()(); + public async processConfigurationFilesFromAppResources(): Promise { + await this.mergeInfoPlists(); + await this.mergeProjectXcconfigFiles(); + for (let pluginData of await this.getAllInstalledPlugins()) { + await this.$pluginVariablesService.interpolatePluginVariables(pluginData, this.platformData.configurationFilePath); + } + + this.$pluginVariablesService.interpolateAppIdentifier(this.platformData.configurationFilePath); } private getInfoPlistPath(): string { @@ -653,48 +625,48 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f this.platformData.configurationFileName ); } + public ensureConfigurationFileInAppResources(): void { return null; } - private mergeInfoPlists(): IFuture { - return (() => { - let projectDir = this.$projectData.projectDir; - let infoPlistPath = this.$options.baseConfig || path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.platformData.normalizedPlatformName, this.platformData.configurationFileName); - this.ensureConfigurationFileInAppResources(); + private async mergeInfoPlists(): Promise { + let projectDir = this.$projectData.projectDir; + let infoPlistPath = this.$options.baseConfig || path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.platformData.normalizedPlatformName, this.platformData.configurationFileName); + this.ensureConfigurationFileInAppResources(); + + if (!this.$fs.exists(infoPlistPath)) { + this.$logger.trace("Info.plist: No app/App_Resources/iOS/Info.plist found, falling back to pre-1.6.0 Info.plist behavior."); + return; + } - if (!this.$fs.exists(infoPlistPath)) { - this.$logger.trace("Info.plist: No app/App_Resources/iOS/Info.plist found, falling back to pre-1.6.0 Info.plist behavior."); + let session = new PlistSession({ log: (txt: string) => this.$logger.trace("Info.plist: " + txt) }); + let makePatch = (plistPath: string) => { + if (!this.$fs.exists(plistPath)) { + this.$logger.trace("No plist found at: " + plistPath); return; } - let session = new PlistSession({ log: (txt: string) => this.$logger.trace("Info.plist: " + txt) }); - let makePatch = (plistPath: string) => { - if (!this.$fs.exists(plistPath)) { - this.$logger.trace("No plist found at: " + plistPath); - return; - } - - this.$logger.trace("Schedule merge plist at: " + plistPath); - session.patch({ - name: path.relative(projectDir, plistPath), - read: () => this.$fs.readText(plistPath) - }); - }; + this.$logger.trace("Schedule merge plist at: " + plistPath); + session.patch({ + name: path.relative(projectDir, plistPath), + read: () => this.$fs.readText(plistPath) + }); + }; - let allPlugins = this.getAllInstalledPlugins().wait(); - for (let plugin of allPlugins) { - let pluginInfoPlistPath = path.join(plugin.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME), this.platformData.configurationFileName); - makePatch(pluginInfoPlistPath); - } + let allPlugins = await this.getAllInstalledPlugins(); + for (let plugin of allPlugins) { + let pluginInfoPlistPath = path.join(plugin.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME), this.platformData.configurationFileName); + makePatch(pluginInfoPlistPath); + } - makePatch(infoPlistPath); + makePatch(infoPlistPath); - if (this.$projectData.projectId) { - session.patch({ - name: "CFBundleIdentifier from package.json nativescript.id", - read: () => - ` + if (this.$projectData.projectId) { + session.patch({ + name: "CFBundleIdentifier from package.json nativescript.id", + read: () => + ` @@ -702,18 +674,16 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f ${ this.$projectData.projectId} ` - }); - } - - let plistContent = session.build(); + }); + } - this.$logger.trace("Info.plist: Write to: " + this.platformData.configurationFilePath); - this.$fs.writeFile(this.platformData.configurationFilePath, plistContent); + let plistContent = session.build(); - }).future()(); + this.$logger.trace("Info.plist: Write to: " + this.platformData.configurationFilePath); + this.$fs.writeFile(this.platformData.configurationFilePath, plistContent); } - private getAllInstalledPlugins(): IFuture { + private getAllInstalledPlugins(): Promise { return (this.$injector.resolve("pluginsService")).getAllInstalledPlugins(); } @@ -761,17 +731,15 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f return this.$fs.writeFile(this.pbxProjPath, project.writeSync()); } - public preparePluginNativeCode(pluginData: IPluginData, opts?: any): IFuture { - return (() => { - let pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME); + public async preparePluginNativeCode(pluginData: IPluginData, opts?: any): Promise { + let pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME); - this.prepareFrameworks(pluginPlatformsFolderPath, pluginData).wait(); - this.prepareStaticLibs(pluginPlatformsFolderPath, pluginData).wait(); - this.prepareCocoapods(pluginPlatformsFolderPath); - }).future()(); + await this.prepareFrameworks(pluginPlatformsFolderPath, pluginData); + await this.prepareStaticLibs(pluginPlatformsFolderPath, pluginData); + await this.prepareCocoapods(pluginPlatformsFolderPath); } - public removePluginNativeCode(pluginData: IPluginData): void { + public async removePluginNativeCode(pluginData: IPluginData): Promise { let pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME); this.removeFrameworks(pluginPlatformsFolderPath, pluginData); @@ -779,37 +747,35 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f this.removeCocoapods(pluginPlatformsFolderPath); } - public afterPrepareAllPlugins(): IFuture { - return (() => { - if (this.$fs.exists(this.projectPodFilePath)) { - let projectPodfileContent = this.$fs.readText(this.projectPodFilePath); - this.$logger.trace("Project Podfile content"); - this.$logger.trace(projectPodfileContent); - - let firstPostInstallIndex = projectPodfileContent.indexOf(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME); - if (firstPostInstallIndex !== -1 && firstPostInstallIndex !== projectPodfileContent.lastIndexOf(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME)) { - this.$cocoapodsService.mergePodfileHookContent(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME, this.projectPodFilePath); - } + public async afterPrepareAllPlugins(): Promise { + if (this.$fs.exists(this.projectPodFilePath)) { + let projectPodfileContent = this.$fs.readText(this.projectPodFilePath); + this.$logger.trace("Project Podfile content"); + this.$logger.trace(projectPodfileContent); - let xcuserDataPath = path.join(this.xcodeprojPath, "xcuserdata"); - let sharedDataPath = path.join(this.xcodeprojPath, "xcshareddata"); + let firstPostInstallIndex = projectPodfileContent.indexOf(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME); + if (firstPostInstallIndex !== -1 && firstPostInstallIndex !== projectPodfileContent.lastIndexOf(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME)) { + this.$cocoapodsService.mergePodfileHookContent(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME, this.projectPodFilePath); + } - if (!this.$fs.exists(xcuserDataPath) && !this.$fs.exists(sharedDataPath)) { - this.$logger.info("Creating project scheme..."); + let xcuserDataPath = path.join(this.xcodeprojPath, "xcuserdata"); + let sharedDataPath = path.join(this.xcodeprojPath, "xcshareddata"); - this.checkIfXcodeprojIsRequired().wait(); + if (!this.$fs.exists(xcuserDataPath) && !this.$fs.exists(sharedDataPath)) { + this.$logger.info("Creating project scheme..."); - let createSchemeRubyScript = `ruby -e "require 'xcodeproj'; xcproj = Xcodeproj::Project.open('${this.$projectData.projectName}.xcodeproj'); xcproj.recreate_user_schemes; xcproj.save"`; - this.$childProcess.exec(createSchemeRubyScript, { cwd: this.platformData.projectRoot }).wait(); - } + await this.checkIfXcodeprojIsRequired(); - this.executePodInstall().wait(); + let createSchemeRubyScript = `ruby -e "require 'xcodeproj'; xcproj = Xcodeproj::Project.open('${this.$projectData.projectName}.xcodeproj'); xcproj.recreate_user_schemes; xcproj.save"`; + await this.$childProcess.exec(createSchemeRubyScript, { cwd: this.platformData.projectRoot }); } - }).future()(); + + await this.executePodInstall(); + } } - public beforePrepareAllPlugins(): IFuture { - return Future.fromResult(); + public beforePrepareAllPlugins(): Promise { + return Promise.resolve(); } private getAllLibsForPluginWithFileExtension(pluginData: IPluginData, fileExtension: string): string[] { @@ -825,35 +791,32 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f return path.join(newModulesDir, constants.PROJECT_FRAMEWORK_FOLDER_NAME, `${IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER}.xcodeproj`, "project.pbxproj"); } - private validateFramework(libraryPath: string): IFuture { - return (() => { - let infoPlistPath = path.join(libraryPath, "Info.plist"); - if (!this.$fs.exists(infoPlistPath)) { - this.$errors.failWithoutHelp("The bundle at %s does not contain an Info.plist file.", libraryPath); - } + private async validateFramework(libraryPath: string): Promise { + let infoPlistPath = path.join(libraryPath, "Info.plist"); + if (!this.$fs.exists(infoPlistPath)) { + this.$errors.failWithoutHelp("The bundle at %s does not contain an Info.plist file.", libraryPath); + } + + let packageType = (await this.$childProcess.spawnFromEvent("/usr/libexec/PlistBuddy", ["-c", "Print :CFBundlePackageType", infoPlistPath], "close")).stdout.trim(); + if (packageType !== "FMWK") { + this.$errors.failWithoutHelp("The bundle at %s does not appear to be a dynamic framework.", libraryPath); + } - let packageType = this.$childProcess.spawnFromEvent("/usr/libexec/PlistBuddy", ["-c", "Print :CFBundlePackageType", infoPlistPath], "close").wait().stdout.trim(); - if (packageType !== "FMWK") { - this.$errors.failWithoutHelp("The bundle at %s does not appear to be a dynamic framework.", libraryPath); - } - }).future()(); } - private validateStaticLibrary(libraryPath: string): IFuture { - return (() => { - if (path.extname(libraryPath) !== ".a") { - this.$errors.failWithoutHelp(`The bundle at ${libraryPath} does not contain a valid static library in the '.a' file format.`); - } + private async validateStaticLibrary(libraryPath: string): Promise { + if (path.extname(libraryPath) !== ".a") { + this.$errors.failWithoutHelp(`The bundle at ${libraryPath} does not contain a valid static library in the '.a' file format.`); + } - let expectedArchs = ["armv7", "arm64", "i386"]; - let archsInTheFatFile = this.$childProcess.exec("lipo -i " + libraryPath).wait(); + let expectedArchs = ["armv7", "arm64", "i386"]; + let archsInTheFatFile = await this.$childProcess.exec("lipo -i " + libraryPath); - expectedArchs.forEach(expectedArch => { - if (archsInTheFatFile.indexOf(expectedArch) < 0) { - this.$errors.failWithoutHelp(`The static library at ${libraryPath} is not built for one or more of the following required architectures: ${expectedArchs.join(", ")}. The static library must be built for all required architectures.`); - } - }); - }).future()(); + expectedArchs.forEach(expectedArch => { + if (archsInTheFatFile.indexOf(expectedArch) < 0) { + this.$errors.failWithoutHelp(`The static library at ${libraryPath} is not built for one or more of the following required architectures: ${expectedArchs.join(", ")}. The static library must be built for all required architectures.`); + } + }); } private replaceFileContent(file: string): void { @@ -869,58 +832,56 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f this.$fs.rename(path.join(fileRootLocation, oldFileName), path.join(fileRootLocation, newFileName)); } - private executePodInstall(): IFuture { - return (() => { - // Check availability - try { - this.$childProcess.exec("gem which cocoapods").wait(); - this.$childProcess.exec("gem which xcodeproj").wait(); - } catch (e) { - this.$errors.failWithoutHelp("CocoaPods or ruby gem 'xcodeproj' is not installed. Run `sudo gem install cocoapods` and try again."); - } + private async executePodInstall(): Promise { + // Check availability + try { + await this.$childProcess.exec("gem which cocoapods"); + await this.$childProcess.exec("gem which xcodeproj"); + } catch (e) { + this.$errors.failWithoutHelp("CocoaPods or ruby gem 'xcodeproj' is not installed. Run `sudo gem install cocoapods` and try again."); + } - this.$xcprojService.verifyXcproj(true).wait(); + await this.$xcprojService.verifyXcproj(true); - this.$logger.info("Installing pods..."); - let podTool = this.$config.USE_POD_SANDBOX ? "sandbox-pod" : "pod"; - let childProcess = this.$childProcess.spawnFromEvent(podTool, ["install"], "close", { cwd: this.platformData.projectRoot, stdio: ['pipe', process.stdout, 'pipe'] }).wait(); - if (childProcess.stderr) { - let warnings = childProcess.stderr.match(/(\u001b\[(?:\d*;){0,5}\d*m[\s\S]+?\u001b\[(?:\d*;){0,5}\d*m)|(\[!\].*?\n)|(.*?warning.*)/gi); - _.each(warnings, (warning: string) => { - this.$logger.warnWithLabel(warning.replace("\n", "")); - }); + this.$logger.info("Installing pods..."); + let podTool = this.$config.USE_POD_SANDBOX ? "sandbox-pod" : "pod"; + let childProcess = await this.$childProcess.spawnFromEvent(podTool, ["install"], "close", { cwd: this.platformData.projectRoot, stdio: ['pipe', process.stdout, 'pipe'] }); + if (childProcess.stderr) { + let warnings = childProcess.stderr.match(/(\u001b\[(?:\d*;){0,5}\d*m[\s\S]+?\u001b\[(?:\d*;){0,5}\d*m)|(\[!\].*?\n)|(.*?warning.*)/gi); + _.each(warnings, (warning: string) => { + this.$logger.warnWithLabel(warning.replace("\n", "")); + }); - let errors = childProcess.stderr; - _.each(warnings, warning => { - errors = errors.replace(warning, ""); - }); + let errors = childProcess.stderr; + _.each(warnings, warning => { + errors = errors.replace(warning, ""); + }); - if (errors.trim()) { - this.$errors.failWithoutHelp(`Pod install command failed. Error output: ${errors}`); - } + if (errors.trim()) { + this.$errors.failWithoutHelp(`Pod install command failed. Error output: ${errors}`); } + } - if (this.$xcprojService.getXcprojInfo().wait().shouldUseXcproj) { - this.$childProcess.spawnFromEvent("xcproj", ["--project", this.xcodeprojPath, "touch"], "close").wait(); - } + if ((await this.$xcprojService.getXcprojInfo()).shouldUseXcproj) { + await this.$childProcess.spawnFromEvent("xcproj", ["--project", this.xcodeprojPath, "touch"], "close"); + } - return childProcess; - }).future()(); + return childProcess; } - private prepareFrameworks(pluginPlatformsFolderPath: string, pluginData: IPluginData): IFuture { - return (() => { - _.each(this.getAllLibsForPluginWithFileExtension(pluginData, ".framework"), fileName => this.addFramework(path.join(pluginPlatformsFolderPath, fileName)).wait()); - }).future()(); + private async prepareFrameworks(pluginPlatformsFolderPath: string, pluginData: IPluginData): Promise { + for (let fileName of this.getAllLibsForPluginWithFileExtension(pluginData, ".framework")) { + await this.addFramework(path.join(pluginPlatformsFolderPath, fileName)); + } } - private prepareStaticLibs(pluginPlatformsFolderPath: string, pluginData: IPluginData): IFuture { - return (() => { - _.each(this.getAllLibsForPluginWithFileExtension(pluginData, ".a"), fileName => this.addStaticLibrary(path.join(pluginPlatformsFolderPath, fileName)).wait()); - }).future()(); + private async prepareStaticLibs(pluginPlatformsFolderPath: string, pluginData: IPluginData): Promise { + for (let fileName of this.getAllLibsForPluginWithFileExtension(pluginData, ".a")) { + await this.addStaticLibrary(path.join(pluginPlatformsFolderPath, fileName)); + } } - private prepareCocoapods(pluginPlatformsFolderPath: string, opts?: any): void { + private async prepareCocoapods(pluginPlatformsFolderPath: string, opts?: any): Promise { let pluginPodFilePath = path.join(pluginPlatformsFolderPath, "Podfile"); if (this.$fs.exists(pluginPodFilePath)) { let pluginPodFileContent = this.$fs.readText(pluginPodFilePath), @@ -948,7 +909,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } if (opts && opts.executePodInstall && this.$fs.exists(pluginPodFilePath)) { - this.executePodInstall().wait(); + await this.executePodInstall(); } } @@ -1014,68 +975,62 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f this.$fs.writeFile(path.join(headersFolderPath, "module.modulemap"), modulemap); } - private mergeXcconfigFiles(pluginFile: string, projectFile: string): IFuture { - return (() => { - if (!this.$fs.exists(projectFile)) { - this.$fs.writeFile(projectFile, ""); - } + private async mergeXcconfigFiles(pluginFile: string, projectFile: string): Promise { + if (!this.$fs.exists(projectFile)) { + this.$fs.writeFile(projectFile, ""); + } - this.checkIfXcodeprojIsRequired().wait(); - let escapedProjectFile = projectFile.replace(/'/g, "\\'"), - escapedPluginFile = pluginFile.replace(/'/g, "\\'"), - mergeScript = `require 'xcodeproj'; Xcodeproj::Config.new('${escapedProjectFile}').merge(Xcodeproj::Config.new('${escapedPluginFile}')).save_as(Pathname.new('${escapedProjectFile}'))`; - this.$childProcess.exec(`ruby -e "${mergeScript}"`).wait(); - }).future()(); + await this.checkIfXcodeprojIsRequired(); + let escapedProjectFile = projectFile.replace(/'/g, "\\'"), + escapedPluginFile = pluginFile.replace(/'/g, "\\'"), + mergeScript = `require 'xcodeproj'; Xcodeproj::Config.new('${escapedProjectFile}').merge(Xcodeproj::Config.new('${escapedPluginFile}')).save_as(Pathname.new('${escapedProjectFile}'))`; + await this.$childProcess.exec(`ruby -e "${mergeScript}"`); } - private mergeProjectXcconfigFiles(): IFuture { - return (() => { - this.$fs.deleteFile(this.$options.release ? this.pluginsReleaseXcconfigFilePath : this.pluginsDebugXcconfigFilePath); + private async mergeProjectXcconfigFiles(): Promise { + this.$fs.deleteFile(this.$options.release ? this.pluginsReleaseXcconfigFilePath : this.pluginsDebugXcconfigFilePath); - let allPlugins: IPluginData[] = (this.$injector.resolve("pluginsService")).getAllInstalledPlugins().wait(); - for (let plugin of allPlugins) { - let pluginPlatformsFolderPath = plugin.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME); - let pluginXcconfigFilePath = path.join(pluginPlatformsFolderPath, "build.xcconfig"); - if (this.$fs.exists(pluginXcconfigFilePath)) { - this.mergeXcconfigFiles(pluginXcconfigFilePath, this.$options.release ? this.pluginsReleaseXcconfigFilePath : this.pluginsDebugXcconfigFilePath).wait(); - } + let allPlugins: IPluginData[] = await (this.$injector.resolve("pluginsService")).getAllInstalledPlugins(); + for (let plugin of allPlugins) { + let pluginPlatformsFolderPath = plugin.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME); + let pluginXcconfigFilePath = path.join(pluginPlatformsFolderPath, "build.xcconfig"); + if (this.$fs.exists(pluginXcconfigFilePath)) { + await this.mergeXcconfigFiles(pluginXcconfigFilePath, this.$options.release ? this.pluginsReleaseXcconfigFilePath : this.pluginsDebugXcconfigFilePath); } + } - let appResourcesXcconfigPath = path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.platformData.normalizedPlatformName, "build.xcconfig"); - if (this.$fs.exists(appResourcesXcconfigPath)) { - this.mergeXcconfigFiles(appResourcesXcconfigPath, this.$options.release ? this.pluginsReleaseXcconfigFilePath : this.pluginsDebugXcconfigFilePath).wait(); - } + let appResourcesXcconfigPath = path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.platformData.normalizedPlatformName, "build.xcconfig"); + if (this.$fs.exists(appResourcesXcconfigPath)) { + await this.mergeXcconfigFiles(appResourcesXcconfigPath, this.$options.release ? this.pluginsReleaseXcconfigFilePath : this.pluginsDebugXcconfigFilePath); + } - let podFilesRootDirName = path.join("Pods", "Target Support Files", `Pods-${this.$projectData.projectName}`); - let podFolder = path.join(this.platformData.projectRoot, podFilesRootDirName); - if (this.$fs.exists(podFolder)) { - if (this.$options.release) { - this.mergeXcconfigFiles(path.join(this.platformData.projectRoot, podFilesRootDirName, `Pods-${this.$projectData.projectName}.release.xcconfig`), this.pluginsReleaseXcconfigFilePath).wait(); - } else { - this.mergeXcconfigFiles(path.join(this.platformData.projectRoot, podFilesRootDirName, `Pods-${this.$projectData.projectName}.debug.xcconfig`), this.pluginsDebugXcconfigFilePath).wait(); - } + let podFilesRootDirName = path.join("Pods", "Target Support Files", `Pods-${this.$projectData.projectName}`); + let podFolder = path.join(this.platformData.projectRoot, podFilesRootDirName); + if (this.$fs.exists(podFolder)) { + if (this.$options.release) { + await this.mergeXcconfigFiles(path.join(this.platformData.projectRoot, podFilesRootDirName, `Pods-${this.$projectData.projectName}.release.xcconfig`), this.pluginsReleaseXcconfigFilePath); + } else { + await this.mergeXcconfigFiles(path.join(this.platformData.projectRoot, podFilesRootDirName, `Pods-${this.$projectData.projectName}.debug.xcconfig`), this.pluginsDebugXcconfigFilePath); } - }).future()(); + } } - private checkIfXcodeprojIsRequired(): IFuture { - return (() => { - let xcprojInfo = this.$xcprojService.getXcprojInfo().wait(); - if (xcprojInfo.shouldUseXcproj && !xcprojInfo.xcprojAvailable) { - let errorMessage = `You are using CocoaPods version ${xcprojInfo.cocoapodVer} which does not support Xcode ${xcprojInfo.xcodeVersion.major}.${xcprojInfo.xcodeVersion.minor} yet.${EOL}${EOL}You can update your cocoapods by running $sudo gem install cocoapods from a terminal.${EOL}${EOL}In order for the NativeScript CLI to be able to work correctly with this setup you need to install xcproj command line tool and add it to your PATH. Xcproj can be installed with homebrew by running $ brew install xcproj from the terminal`; + private async checkIfXcodeprojIsRequired(): Promise { + let xcprojInfo = await this.$xcprojService.getXcprojInfo(); + if (xcprojInfo.shouldUseXcproj && !xcprojInfo.xcprojAvailable) { + let errorMessage = `You are using CocoaPods version ${xcprojInfo.cocoapodVer} which does not support Xcode ${xcprojInfo.xcodeVersion.major}.${xcprojInfo.xcodeVersion.minor} yet.${EOL}${EOL}You can update your cocoapods by running $sudo gem install cocoapods from a terminal.${EOL}${EOL}In order for the NativeScript CLI to be able to work correctly with this setup you need to install xcproj command line tool and add it to your PATH. Xcproj can be installed with homebrew by running $ brew install xcproj from the terminal`; - this.$errors.failWithoutHelp(errorMessage); + this.$errors.failWithoutHelp(errorMessage); - return true; - } - }).future()(); + return true; + } } - private getXcodeVersion(): string { + private async getXcodeVersion(): Promise { let xcodeBuildVersion = ""; try { - xcodeBuildVersion = this.$childProcess.exec("xcodebuild -version | head -n 1 | sed -e 's/Xcode //'").wait(); + xcodeBuildVersion = await this.$childProcess.exec("xcodebuild -version | head -n 1 | sed -e 's/Xcode //'"); } catch (error) { this.$errors.fail("xcodebuild execution failed. Make sure that you have latest Xcode and tools installed."); } @@ -1088,7 +1043,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f return xcodeBuildVersion; } - private getDevelopmentTeams(): Array<{id:string, name: string}> { + private getDevelopmentTeams(): Array<{ id: string, name: string }> { let dir = path.join(process.env.HOME, "Library/MobileDevice/Provisioning Profiles/"); let files = this.$fs.readDirectory(dir); let teamIds: any = {}; @@ -1101,10 +1056,12 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f teamIds[teamId] = teamName; } } - let teamIdsArray = new Array<{id:string, name: string}>(); + + let teamIdsArray = new Array<{ id: string, name: string }>(); for (let teamId in teamIds) { - teamIdsArray.push({ id:teamId, name:teamIds[teamId] }); + teamIdsArray.push({ id: teamId, name: teamIds[teamId] }); } + return teamIdsArray; } @@ -1112,7 +1069,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f let findStr = "" + name + ""; let index = text.indexOf(findStr); if (index > 0) { - index = text.indexOf("", index+findStr.length); + index = text.indexOf("", index + findStr.length); if (index > 0) { index += "".length; let endIndex = text.indexOf("", index); @@ -1120,6 +1077,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f return result; } } + return null; } @@ -1132,7 +1090,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f line = line.replace(/\/(\/)[^\n]*$/, ""); if (line.indexOf("DEVELOPMENT_TEAM") >= 0) { teamId = line.split("=")[1].trim(); - if (teamId[teamId.length-1] === ';') { + if (teamId[teamId.length - 1] === ';') { teamId = teamId.slice(0, -1); } } @@ -1141,20 +1099,23 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f return teamId; } } + let fileName = path.join(this.platformData.projectRoot, "teamid"); if (this.$fs.exists(fileName)) { return this.$fs.readText(fileName); } + return null; } - private getDevelopmentTeam(): string { + private async getDevelopmentTeam(): Promise { let teamId: string; if (this.$options.teamId) { teamId = this.$options.teamId; } else { teamId = this.readTeamId(); } + if (!teamId) { let teams = this.getDevelopmentTeams(); this.$logger.warn("Xcode 8 requires a team id to be specified when building for device."); @@ -1167,7 +1128,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f for (let team of teams) { choices.push(team.name + " (" + team.id + ")"); } - let choice = this.$prompter.promptForChoice('Found multiple development teams, select one:', choices).wait(); + let choice = await this.$prompter.promptForChoice('Found multiple development teams, select one:', choices); teamId = teams[choices.indexOf(choice)].id; let choicesPersist = [ @@ -1175,7 +1136,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f "Yes, persist the team id in platforms folder.", "No, don't persist this setting." ]; - let choicePersist = this.$prompter.promptForChoice("Do you want to make teamId: "+teamId+" a persistent choice for your app?", choicesPersist).wait(); + let choicePersist = await this.$prompter.promptForChoice("Do you want to make teamId: " + teamId + " a persistent choice for your app?", choicesPersist); switch (choicesPersist.indexOf(choicePersist)) { case 0: let xcconfigFile = path.join(this.$projectData.appResourcesDirectoryPath, this.platformData.normalizedPlatformName, "build.xcconfig"); diff --git a/lib/services/ios-provision-service.ts b/lib/services/ios-provision-service.ts index 2aae681da1..c7d65dd594 100644 --- a/lib/services/ios-provision-service.ts +++ b/lib/services/ios-provision-service.ts @@ -15,87 +15,81 @@ export class IOSProvisionService { private $mobileHelper: Mobile.IMobileHelper) { } - public pick(uuidOrName: string): IFuture { - return (() => { - const match = this.queryProvisioningProfilesAndDevices().wait().match; - - return match.eligable.find(prov => prov.UUID === uuidOrName) - || match.eligable.find(prov => prov.Name === uuidOrName) - || match.nonEligable.find(prov => prov.UUID === uuidOrName) - || match.nonEligable.find(prov => prov.Name === uuidOrName); - }).future()(); + public async pick(uuidOrName: string): Promise { + const match = (await this.queryProvisioningProfilesAndDevices()).match; + return match.eligable.find(prov => prov.UUID === uuidOrName) + || match.eligable.find(prov => prov.Name === uuidOrName) + || match.nonEligable.find(prov => prov.UUID === uuidOrName) + || match.nonEligable.find(prov => prov.Name === uuidOrName); } - public list(): IFuture { - return (() => { - const data = this.queryProvisioningProfilesAndDevices().wait(), - devices = data.devices, - match = data.match; - - function formatSupportedDeviceCount(prov: mobileprovision.provision.MobileProvision) { - if (devices.length > 0 && prov.Type === "Development") { - return prov.ProvisionedDevices.reduce((count, device) => count + (devices.indexOf(device) >= 0 ? 1 : 0), 0) + "/" + devices.length + " targets"; - } else { - return ""; - } - } + public async list(): Promise { + const data = await this.queryProvisioningProfilesAndDevices(); + const devices = data.devices; + const match = data.match; - function formatTotalDeviceCount(prov: mobileprovision.provision.MobileProvision) { - if (prov.Type === "Development" && prov.ProvisionedDevices) { - return prov.ProvisionedDevices.length + " total"; - } else if (prov.Type === "AdHoc") { - return "all"; - } else { - return ""; - } + function formatSupportedDeviceCount(prov: mobileprovision.provision.MobileProvision) { + if (devices.length > 0 && prov.Type === "Development") { + return prov.ProvisionedDevices.reduce((count, device) => count + (devices.indexOf(device) >= 0 ? 1 : 0), 0) + "/" + devices.length + " targets"; + } else { + return ""; } + } - const table = createTable(["Provision Name / Provision UUID / App Id", "Team", "Type / Due", "Devices"], []); - - function pushProvision(prov: mobileprovision.provision.MobileProvision) { - table.push(["", "", "", ""]); - table.push(["\"" + prov.Name + "\"", prov.TeamName, prov.Type, formatTotalDeviceCount(prov)]); - table.push([prov.UUID, prov.TeamIdentifier && prov.TeamIdentifier.length > 0 ? "(" + prov.TeamIdentifier[0] + ")" : "", formatDate(prov.ExpirationDate), formatSupportedDeviceCount(prov)]); - table.push([prov.Entitlements["application-identifier"], "", "", ""]); + function formatTotalDeviceCount(prov: mobileprovision.provision.MobileProvision) { + if (prov.Type === "Development" && prov.ProvisionedDevices) { + return prov.ProvisionedDevices.length + " total"; + } else if (prov.Type === "AdHoc") { + return "all"; + } else { + return ""; } - match.eligable.forEach(prov => pushProvision(prov)); - - this.$logger.out(table.toString()); - this.$logger.out(); - this.$logger.out("There are also " + match.nonEligable.length + " non-eligable provisioning profiles."); - this.$logger.out(); + } - }).future()(); - } + const table = createTable(["Provision Name / Provision UUID / App Id", "Team", "Type / Due", "Devices"], []); - private queryProvisioningProfilesAndDevices(): IFuture<{ devices: string[], match: mobileprovision.provision.Result }> { - return (() => { - const certificates = mobileprovision.cert.read(); - const provisions = mobileprovision.provision.read(); + function pushProvision(prov: mobileprovision.provision.MobileProvision) { + table.push(["", "", "", ""]); + table.push(["\"" + prov.Name + "\"", prov.TeamName, prov.Type, formatTotalDeviceCount(prov)]); + table.push([prov.UUID, prov.TeamIdentifier && prov.TeamIdentifier.length > 0 ? "(" + prov.TeamIdentifier[0] + ")" : "", formatDate(prov.ExpirationDate), formatSupportedDeviceCount(prov)]); + table.push([prov.Entitlements["application-identifier"], "", "", ""]); + } + match.eligable.forEach(prov => pushProvision(prov)); - const query: mobileprovision.provision.Query = { - Certificates: certificates.valid, - Unique: true, - AppId: this.$projectData.projectId - }; + this.$logger.out(table.toString()); + this.$logger.out(); + this.$logger.out("There are also " + match.nonEligable.length + " non-eligable provisioning profiles."); + this.$logger.out(); - let devices: string[] = []; - if (this.$options.device) { - devices = [this.$options.device]; - } else { - this.$devicesService.initialize({ - platform: "ios" - }).wait(); - devices = _(this.$devicesService.getDeviceInstances()) - .filter(d => this.$mobileHelper.isiOSPlatform(d.deviceInfo.platform)) - .map(d => d.deviceInfo.identifier) - .toJSON(); - } - - const match = mobileprovision.provision.select(provisions, query); + } - return { devices, match }; - }).future<{ devices: string[], match: mobileprovision.provision.Result }>()(); + private async queryProvisioningProfilesAndDevices(): Promise<{ devices: string[], match: mobileprovision.provision.Result }> { + const certificates = mobileprovision.cert.read(); + const provisions = mobileprovision.provision.read(); + + const query: mobileprovision.provision.Query = { + Certificates: certificates.valid, + Unique: true, + AppId: this.$projectData.projectId + }; + + let devices: string[] = []; + if (this.$options.device) { + devices = [this.$options.device]; + } else { + await this.$devicesService.initialize({ + platform: "ios" + }); + devices = _(this.$devicesService.getDeviceInstances()) + .filter(d => this.$mobileHelper.isiOSPlatform(d.deviceInfo.platform)) + .map(d => d.deviceInfo.identifier) + .toJSON(); + } + + const match = mobileprovision.provision.select(provisions, query); + + return { devices, match }; } } + $injector.register("iOSProvisionService", IOSProvisionService); diff --git a/lib/services/itmstransporter-service.ts b/lib/services/itmstransporter-service.ts index 082f145489..c41969ffa0 100644 --- a/lib/services/itmstransporter-service.ts +++ b/lib/services/itmstransporter-service.ts @@ -1,9 +1,9 @@ import * as path from "path"; import * as temp from "temp"; -import {EOL} from "os"; -import {ITMSConstants} from "../constants"; -import {ItunesConnectApplicationTypes} from "../constants"; -import {quoteString, versionCompare} from "../common/helpers"; +import { EOL } from "os"; +import { ITMSConstants } from "../constants"; +import { ItunesConnectApplicationTypes } from "../constants"; +import { quoteString, versionCompare } from "../common/helpers"; export class ITMSTransporterService implements IITMSTransporterService { private _itmsTransporterPath: string = null; @@ -24,61 +24,57 @@ export class ITMSTransporterService implements IITMSTransporterService { return this.$injector.resolve("projectData"); } - public upload(data: IITMSData): IFuture { - return (() => { - if (!this.$hostInfo.isDarwin) { - this.$errors.failWithoutHelp("iOS publishing is only available on Mac OS X."); - } + public async upload(data: IITMSData): Promise { + if (!this.$hostInfo.isDarwin) { + this.$errors.failWithoutHelp("iOS publishing is only available on Mac OS X."); + } - temp.track(); - let itmsTransporterPath = this.getITMSTransporterPath().wait(), - ipaFileName = "app.ipa", - itmsDirectory = temp.mkdirSync("itms-"), - innerDirectory = path.join(itmsDirectory, "mybundle.itmsp"), - ipaFileLocation = path.join(innerDirectory, ipaFileName), - loggingLevel = data.verboseLogging ? ITMSConstants.VerboseLoggingLevels.Verbose : ITMSConstants.VerboseLoggingLevels.Informational, - bundleId = this.getBundleIdentifier(data.ipaFilePath).wait(), - iOSApplication = this.getiOSApplication(data.username, data.password, bundleId).wait(); + temp.track(); + let itmsTransporterPath = await this.getITMSTransporterPath(), + ipaFileName = "app.ipa", + itmsDirectory = temp.mkdirSync("itms-"), + innerDirectory = path.join(itmsDirectory, "mybundle.itmsp"), + ipaFileLocation = path.join(innerDirectory, ipaFileName), + loggingLevel = data.verboseLogging ? ITMSConstants.VerboseLoggingLevels.Verbose : ITMSConstants.VerboseLoggingLevels.Informational, + bundleId = await this.getBundleIdentifier(data.ipaFilePath), + iOSApplication = await this.getiOSApplication(data.username, data.password, bundleId); - this.$fs.createDirectory(innerDirectory); + this.$fs.createDirectory(innerDirectory); - this.$fs.copyFile(data.ipaFilePath, ipaFileLocation); + this.$fs.copyFile(data.ipaFilePath, ipaFileLocation); - let ipaFileHash = this.$fs.getFileShasum(ipaFileLocation, {algorithm: "md5"}).wait(), - ipaFileSize = this.$fs.getFileSize(ipaFileLocation), - metadata = this.getITMSMetadataXml(iOSApplication.adamId, ipaFileName, ipaFileHash, ipaFileSize); + let ipaFileHash = await this.$fs.getFileShasum(ipaFileLocation, { algorithm: "md5" }), + ipaFileSize = this.$fs.getFileSize(ipaFileLocation), + metadata = this.getITMSMetadataXml(iOSApplication.adamId, ipaFileName, ipaFileHash, ipaFileSize); - this.$fs.writeFile(path.join(innerDirectory, ITMSConstants.ApplicationMetadataFile), metadata); + this.$fs.writeFile(path.join(innerDirectory, ITMSConstants.ApplicationMetadataFile), metadata); - this.$childProcess.spawnFromEvent(itmsTransporterPath, ["-m", "upload", "-f", itmsDirectory, "-u", quoteString(data.username), "-p", quoteString(data.password), "-v", loggingLevel], "close", { stdio: "inherit" }).wait(); - }).future()(); + await this.$childProcess.spawnFromEvent(itmsTransporterPath, ["-m", "upload", "-f", itmsDirectory, "-u", quoteString(data.username), "-p", quoteString(data.password), "-v", loggingLevel], "close", { stdio: "inherit" }); } - public getiOSApplications(credentials: ICredentials): IFuture { - return ((): IiTunesConnectApplication[] => { - if (!this._itunesConnectApplications) { - let requestBody = this.getContentDeliveryRequestBody(credentials), - contentDeliveryResponse = this.$httpClient.httpRequest({ - url: "https://contentdelivery.itunes.apple.com/WebObjects/MZLabelService.woa/json/MZITunesProducerService", - method: "POST", - body: requestBody - }).wait(), - contentDeliveryBody: IContentDeliveryBody = JSON.parse(contentDeliveryResponse.body); - - if (!contentDeliveryBody.result.Success || !contentDeliveryBody.result.Applications) { - let errorMessage = ["Unable to connect to iTunes Connect"]; - if (contentDeliveryBody.result.Errors && contentDeliveryBody.result.Errors.length) { - errorMessage = errorMessage.concat(contentDeliveryBody.result.Errors); - } - - this.$errors.failWithoutHelp(errorMessage.join(EOL)); + public async getiOSApplications(credentials: ICredentials): Promise { + if (!this._itunesConnectApplications) { + let requestBody = this.getContentDeliveryRequestBody(credentials), + contentDeliveryResponse = await this.$httpClient.httpRequest({ + url: "https://contentdelivery.itunes.apple.com/WebObjects/MZLabelService.woa/json/MZITunesProducerService", + method: "POST", + body: requestBody + }), + contentDeliveryBody: IContentDeliveryBody = JSON.parse(contentDeliveryResponse.body); + + if (!contentDeliveryBody.result.Success || !contentDeliveryBody.result.Applications) { + let errorMessage = ["Unable to connect to iTunes Connect"]; + if (contentDeliveryBody.result.Errors && contentDeliveryBody.result.Errors.length) { + errorMessage = errorMessage.concat(contentDeliveryBody.result.Errors); } - this._itunesConnectApplications = contentDeliveryBody.result.Applications.filter(app => app.type === ItunesConnectApplicationTypes.iOS); + this.$errors.failWithoutHelp(errorMessage.join(EOL)); } - return this._itunesConnectApplications; - }).future()(); + this._itunesConnectApplications = contentDeliveryBody.result.Applications.filter(app => app.type === ItunesConnectApplicationTypes.iOS); + } + + return this._itunesConnectApplications; } /** @@ -88,21 +84,19 @@ export class ITMSTransporterService implements IITMSTransporterService { * @param {string} bundleId Application's Bundle Identifier * @return {IFuture} The iTunes Connect application. */ - private getiOSApplication(username: string, password: string, bundleId: string) : IFuture { - return (():IiTunesConnectApplication => { - let iOSApplications = this.getiOSApplications({username, password}).wait(); - if (!iOSApplications || !iOSApplications.length) { - this.$errors.failWithoutHelp(`Cannot find any registered applications for Apple ID ${username} in iTunes Connect.`); - } + private async getiOSApplication(username: string, password: string, bundleId: string): Promise { + let iOSApplications = await this.getiOSApplications({ username, password }); + if (!iOSApplications || !iOSApplications.length) { + this.$errors.failWithoutHelp(`Cannot find any registered applications for Apple ID ${username} in iTunes Connect.`); + } - let iOSApplication = _.find(iOSApplications, app => app.bundleId === bundleId); + let iOSApplication = _.find(iOSApplications, app => app.bundleId === bundleId); - if (!iOSApplication) { - this.$errors.failWithoutHelp(`Cannot find registered applications that match the specified identifier ${bundleId} in iTunes Connect.`); - } + if (!iOSApplication) { + this.$errors.failWithoutHelp(`Cannot find registered applications that match the specified identifier ${bundleId} in iTunes Connect.`); + } - return iOSApplication; - }).future()(); + return iOSApplication; } /** @@ -110,74 +104,70 @@ export class ITMSTransporterService implements IITMSTransporterService { * @param {string} ipaFileFullPath Optional full path to .ipa file * @return {IFuture} Application's bundle identifier. */ - private getBundleIdentifier(ipaFileFullPath?: string): IFuture { - return ((): string => { - if (!this._bundleIdentifier) { - if (!ipaFileFullPath) { - this._bundleIdentifier = this.$projectData.projectId; - } else { - if (!this.$fs.exists(ipaFileFullPath) || path.extname(ipaFileFullPath) !== ".ipa") { - this.$errors.failWithoutHelp(`Cannot use specified ipa file ${ipaFileFullPath}. File either does not exist or is not an ipa file.`); - } - - this.$logger.trace("--ipa set - extracting .ipa file to get app's bundle identifier"); - temp.track(); - let destinationDir = temp.mkdirSync("ipa-"); - this.$fs.unzip(ipaFileFullPath, destinationDir).wait(); - - let payloadDir = path.join(destinationDir, "Payload"); - let allApps = this.$fs.readDirectory(payloadDir); - - this.$logger.debug("ITMSTransporter .ipa Payload files:"); - allApps.forEach(f => this.$logger.debug(" - " + f)); - - allApps = allApps.filter(f => path.extname(f).toLowerCase() === ".app"); - if (allApps.length > 1) { - this.$errors.failWithoutHelp("In the .ipa the ITMSTransporter is uploading there is more than one .app file. We don't know which one to upload."); - } else if (allApps.length <= 0) { - this.$errors.failWithoutHelp("In the .ipa the ITMSTransporter is uploading there must be at least one .app file."); - } - let appFile = path.join(payloadDir, allApps[0]); - - let plistObject = this.$bplistParser.parseFile(path.join(appFile, "Info.plist")).wait(); - let bundleId = plistObject && plistObject[0] && plistObject[0].CFBundleIdentifier; - if (!bundleId) { - this.$errors.failWithoutHelp(`Unable to determine bundle identifier from ${ipaFileFullPath}.`); - } - - this.$logger.trace(`bundle identifier determined to be ${bundleId}`); - this._bundleIdentifier = bundleId; + private async getBundleIdentifier(ipaFileFullPath?: string): Promise { + if (!this._bundleIdentifier) { + if (!ipaFileFullPath) { + this._bundleIdentifier = this.$projectData.projectId; + } else { + if (!this.$fs.exists(ipaFileFullPath) || path.extname(ipaFileFullPath) !== ".ipa") { + this.$errors.failWithoutHelp(`Cannot use specified ipa file ${ipaFileFullPath}. File either does not exist or is not an ipa file.`); } - } - return this._bundleIdentifier; - }).future()(); - } + this.$logger.trace("--ipa set - extracting .ipa file to get app's bundle identifier"); + temp.track(); + let destinationDir = temp.mkdirSync("ipa-"); + await this.$fs.unzip(ipaFileFullPath, destinationDir); + + let payloadDir = path.join(destinationDir, "Payload"); + let allApps = this.$fs.readDirectory(payloadDir); + + this.$logger.debug("ITMSTransporter .ipa Payload files:"); + allApps.forEach(f => this.$logger.debug(" - " + f)); - private getITMSTransporterPath(): IFuture { - return ((): string => { - if (!this._itmsTransporterPath) { - let xcodePath = this.$xcodeSelectService.getContentsDirectoryPath().wait(), - xcodeVersion = this.$xcodeSelectService.getXcodeVersion().wait(), - result = path.join(xcodePath, "Applications", "Application Loader.app", "Contents"); - - xcodeVersion.patch = xcodeVersion.patch || "0"; - // iTMS Transporter's path has been modified in Xcode 6.3 - // https://github.com/nomad/shenzhen/issues/243 - if (xcodeVersion.major && xcodeVersion.minor && - versionCompare(xcodeVersion, "6.3.0") < 0) { - result = path.join(result, "MacOS"); + allApps = allApps.filter(f => path.extname(f).toLowerCase() === ".app"); + if (allApps.length > 1) { + this.$errors.failWithoutHelp("In the .ipa the ITMSTransporter is uploading there is more than one .app file. We don't know which one to upload."); + } else if (allApps.length <= 0) { + this.$errors.failWithoutHelp("In the .ipa the ITMSTransporter is uploading there must be at least one .app file."); } + let appFile = path.join(payloadDir, allApps[0]); - this._itmsTransporterPath = path.join(result, ITMSConstants.iTMSDirectoryName, "bin", ITMSConstants.iTMSExecutableName); + let plistObject = await this.$bplistParser.parseFile(path.join(appFile, "Info.plist")); + let bundleId = plistObject && plistObject[0] && plistObject[0].CFBundleIdentifier; + if (!bundleId) { + this.$errors.failWithoutHelp(`Unable to determine bundle identifier from ${ipaFileFullPath}.`); + } + + this.$logger.trace(`bundle identifier determined to be ${bundleId}`); + this._bundleIdentifier = bundleId; } + } + + return this._bundleIdentifier; + } - if(!this.$fs.exists(this._itmsTransporterPath)) { - this.$errors.failWithoutHelp('iTMS Transporter not found on this machine - make sure your Xcode installation is not damaged.'); + private async getITMSTransporterPath(): Promise { + if (!this._itmsTransporterPath) { + let xcodePath = await this.$xcodeSelectService.getContentsDirectoryPath(), + xcodeVersion = await this.$xcodeSelectService.getXcodeVersion(), + result = path.join(xcodePath, "Applications", "Application Loader.app", "Contents"); + + xcodeVersion.patch = xcodeVersion.patch || "0"; + // iTMS Transporter's path has been modified in Xcode 6.3 + // https://github.com/nomad/shenzhen/issues/243 + if (xcodeVersion.major && xcodeVersion.minor && + versionCompare(xcodeVersion, "6.3.0") < 0) { + result = path.join(result, "MacOS"); } - return this._itmsTransporterPath; - }).future()(); + this._itmsTransporterPath = path.join(result, ITMSConstants.iTMSDirectoryName, "bin", ITMSConstants.iTMSExecutableName); + } + + if (!this.$fs.exists(this._itmsTransporterPath)) { + this.$errors.failWithoutHelp('iTMS Transporter not found on this machine - make sure your Xcode installation is not damaged.'); + } + + return this._itmsTransporterPath; } private getContentDeliveryRequestBody(credentials: ICredentials): string { @@ -186,17 +176,17 @@ export class ITMSTransporterService implements IITMSTransporterService { // and if only one of these ends up missing the API returns // a response with 200 status code and an error return JSON.stringify({ - id: "1", // magic number - jsonrpc: "2.0", - method: "lookupSoftwareApplications", - params: { - Username: credentials.username, - Password: credentials.password, - Version: "2.9.1 (441)", - Application: "Application Loader", - OSIdentifier: "Mac OS X 10.8.5 (x86_64)" - } - }); + id: "1", // magic number + jsonrpc: "2.0", + method: "lookupSoftwareApplications", + params: { + Username: credentials.username, + Password: credentials.password, + Version: "2.9.1 (441)", + Application: "Application Loader", + OSIdentifier: "Mac OS X 10.8.5 (x86_64)" + } + }); } private getITMSMetadataXml(appleId: string, ipaFileName: string, ipaFileHash: string, ipaFileSize: number): string { diff --git a/lib/services/karma-execution.ts b/lib/services/karma-execution.ts index b51ecb5fcb..2223bef675 100644 --- a/lib/services/karma-execution.ts +++ b/lib/services/karma-execution.ts @@ -1,7 +1,7 @@ import * as path from "path"; process.on("message", (data: any) => { - if(data.karmaConfig) { + if (data.karmaConfig) { let pathToKarma = path.join(data.karmaConfig.projectDir, 'node_modules/karma'), KarmaServer = require(path.join(pathToKarma, 'lib/server')), karma = new KarmaServer(data.karmaConfig, (exitCode: number) => { diff --git a/lib/services/livesync/android-device-livesync-service.ts b/lib/services/livesync/android-device-livesync-service.ts index c58f101462..75034bc919 100644 --- a/lib/services/livesync/android-device-livesync-service.ts +++ b/lib/services/livesync/android-device-livesync-service.ts @@ -1,6 +1,5 @@ -import {DeviceAndroidDebugBridge} from "../../common/mobile/android/device-android-debug-bridge"; -import {AndroidDeviceHashService} from "../../common/mobile/android/android-device-hash-service"; -import Future = require("fibers/future"); +import { DeviceAndroidDebugBridge } from "../../common/mobile/android/device-android-debug-bridge"; +import { AndroidDeviceHashService } from "../../common/mobile/android/android-device-hash-service"; import * as helpers from "../../common/helpers"; import * as path from "path"; import * as net from "net"; @@ -10,9 +9,7 @@ class AndroidLiveSyncService implements IDeviceLiveSyncService { private device: Mobile.IAndroidDevice; constructor(_device: Mobile.IDevice, - private $fs: IFileSystem, private $mobileHelper: Mobile.IMobileHelper, - private $options: IOptions, private $injector: IInjector, private $projectData: IProjectData, private $androidDebugService: IDebugService, @@ -24,8 +21,8 @@ class AndroidLiveSyncService implements IDeviceLiveSyncService { return this.$androidDebugService; } - public refreshApplication(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], forceExecuteFullSync: boolean): IFuture { - let canExecuteFastSync = !forceExecuteFullSync && !_.some(localToDevicePaths, (localToDevicePath:any) => !this.$liveSyncProvider.canExecuteFastSync(localToDevicePath.getLocalPath(), deviceAppData.platform)); + public async refreshApplication(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], forceExecuteFullSync: boolean): Promise { + let canExecuteFastSync = !forceExecuteFullSync && !_.some(localToDevicePaths, (localToDevicePath: any) => !this.$liveSyncProvider.canExecuteFastSync(localToDevicePath.getLocalPath(), deviceAppData.platform)); if (canExecuteFastSync) { return this.reloadPage(deviceAppData, localToDevicePaths); @@ -34,92 +31,86 @@ class AndroidLiveSyncService implements IDeviceLiveSyncService { return this.restartApplication(deviceAppData); } - private restartApplication(deviceAppData: Mobile.IDeviceAppData): IFuture { - return (() => { - this.device.adb.executeShellCommand(["chmod", "777", deviceAppData.deviceProjectRootPath, `/data/local/tmp/${deviceAppData.appIdentifier}`]).wait(); + private async restartApplication(deviceAppData: Mobile.IDeviceAppData): Promise { + await this.device.adb.executeShellCommand(["chmod", "777", await deviceAppData.getDeviceProjectRootPath(), `/data/local/tmp/${deviceAppData.appIdentifier}`]); - let devicePathRoot = `/data/data/${deviceAppData.appIdentifier}/files`; - let devicePath = this.$mobileHelper.buildDevicePath(devicePathRoot, "code_cache", "secondary_dexes", "proxyThumb"); - this.device.adb.executeShellCommand(["rm", "-rf", devicePath]).wait(); + let devicePathRoot = `/data/data/${deviceAppData.appIdentifier}/files`; + let devicePath = this.$mobileHelper.buildDevicePath(devicePathRoot, "code_cache", "secondary_dexes", "proxyThumb"); + await this.device.adb.executeShellCommand(["rm", "-rf", devicePath]); - this.device.applicationManager.restartApplication(deviceAppData.appIdentifier).wait(); - }).future()(); + await this.device.applicationManager.restartApplication(deviceAppData.appIdentifier); } - public beforeLiveSyncAction(deviceAppData: Mobile.IDeviceAppData): IFuture { - return (() => { - let deviceRootPath = this.getDeviceRootPath(deviceAppData.appIdentifier), - deviceRootDir = path.dirname(deviceRootPath), - deviceRootBasename = path.basename(deviceRootPath), - listResult = this.device.adb.executeShellCommand(["ls", "-l", deviceRootDir]).wait(), - regex = new RegExp(`^-.*${deviceRootBasename}$`, "m"), - matchingFile = (listResult || "").match(regex); - - // Check if there is already a file with deviceRootBasename. If so, delete it as it breaks LiveSyncing. - if (matchingFile && matchingFile[0] && _.startsWith(matchingFile[0], '-')) { - this.device.adb.executeShellCommand(["rm", "-f", deviceRootPath]).wait(); - } - - this.device.adb.executeShellCommand(["rm", "-rf", this.$mobileHelper.buildDevicePath(deviceRootPath, "fullsync"), - this.$mobileHelper.buildDevicePath(deviceRootPath, "sync"), - this.$mobileHelper.buildDevicePath(deviceRootPath, "removedsync")]).wait(); - }).future()(); + public async beforeLiveSyncAction(deviceAppData: Mobile.IDeviceAppData): Promise { + let deviceRootPath = this.getDeviceRootPath(deviceAppData.appIdentifier), + deviceRootDir = path.dirname(deviceRootPath), + deviceRootBasename = path.basename(deviceRootPath), + listResult = await this.device.adb.executeShellCommand(["ls", "-l", deviceRootDir]), + regex = new RegExp(`^-.*${deviceRootBasename}$`, "m"), + matchingFile = (listResult || "").match(regex); + + // Check if there is already a file with deviceRootBasename. If so, delete it as it breaks LiveSyncing. + if (matchingFile && matchingFile[0] && _.startsWith(matchingFile[0], '-')) { + await this.device.adb.executeShellCommand(["rm", "-f", deviceRootPath]); + } + + this.device.adb.executeShellCommand(["rm", "-rf", this.$mobileHelper.buildDevicePath(deviceRootPath, "fullsync"), + this.$mobileHelper.buildDevicePath(deviceRootPath, "sync"), + await this.$mobileHelper.buildDevicePath(deviceRootPath, "removedsync")]); } - private reloadPage(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): IFuture { - return (() => { - this.device.adb.executeCommand(["forward", `tcp:${AndroidLiveSyncService.BACKEND_PORT.toString()}`, `localabstract:${deviceAppData.appIdentifier}-livesync`]).wait(); - if (!this.sendPageReloadMessage().wait()) { - this.restartApplication(deviceAppData).wait(); - } - }).future()(); + private async reloadPage(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise { + await this.device.adb.executeCommand(["forward", `tcp:${AndroidLiveSyncService.BACKEND_PORT.toString()}`, `localabstract:${deviceAppData.appIdentifier}-livesync`]); + if (!await this.sendPageReloadMessage()) { + await this.restartApplication(deviceAppData); + } } - public removeFiles(appIdentifier: string, localToDevicePaths: Mobile.ILocalToDevicePathData[]): IFuture { - return (() => { - let deviceRootPath = this.getDeviceRootPath(appIdentifier); - _.each(localToDevicePaths, localToDevicePathData => { - let relativeUnixPath = _.trimStart(helpers.fromWindowsRelativePathToUnix(localToDevicePathData.getRelativeToProjectBasePath()), "/"); - let deviceFilePath = this.$mobileHelper.buildDevicePath(deviceRootPath, "removedsync", relativeUnixPath); - this.device.adb.executeShellCommand(["mkdir", "-p", path.dirname(deviceFilePath), "&&", "touch", deviceFilePath]).wait(); - }); + public async removeFiles(appIdentifier: string, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise { + let deviceRootPath = this.getDeviceRootPath(appIdentifier); + _.each(localToDevicePaths, localToDevicePathData => { + let relativeUnixPath = _.trimStart(helpers.fromWindowsRelativePathToUnix(localToDevicePathData.getRelativeToProjectBasePath()), "/"); + let deviceFilePath = this.$mobileHelper.buildDevicePath(deviceRootPath, "removedsync", relativeUnixPath); + this.device.adb.executeShellCommand(["mkdir", "-p", path.dirname(deviceFilePath), "&& await ", "touch", deviceFilePath]); + }); - this.deviceHashService.removeHashes(localToDevicePaths).wait(); - }).future()(); + await this.deviceHashService.removeHashes(localToDevicePaths); } - public afterInstallApplicationAction(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): IFuture { - return (() => { - this.deviceHashService.uploadHashFileToDevice(localToDevicePaths).wait(); - return false; - }).future()(); + public async afterInstallApplicationAction(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise { + await this.deviceHashService.uploadHashFileToDevice(localToDevicePaths); + return false; } private getDeviceRootPath(appIdentifier: string): string { return `/data/local/tmp/${appIdentifier}`; } - private sendPageReloadMessage(): IFuture { - let future = new Future(); - let socket = new net.Socket(); - socket.connect(AndroidLiveSyncService.BACKEND_PORT, '127.0.0.1', () => { - socket.write(new Buffer([0, 0, 0, 1, 1])); - }); - socket.on("data", (data: any) => { - socket.destroy(); - future.return(true); - }); - socket.on("error", () => { - if (!future.isResolved()) { - future.return(false); - } - }); - socket.on("close", () => { - if (!future.isResolved()) { - future.return(false); - } + private async sendPageReloadMessage(): Promise { + return new Promise((resolve, reject) => { + let isResolved = false; + let socket = new net.Socket(); + + socket.connect(AndroidLiveSyncService.BACKEND_PORT, '127.0.0.1', () => { + socket.write(new Buffer([0, 0, 0, 1, 1])); + }); + socket.on("data", (data: any) => { + socket.destroy(); + resolve(true); + }); + socket.on("error", () => { + if (!isResolved) { + isResolved = true; + resolve(false); + } + }); + socket.on("close", () => { + if (!isResolved) { + isResolved = true; + resolve(false); + } + }); }); - return future; } private _deviceHashService: Mobile.IAndroidDeviceHashService; diff --git a/lib/services/livesync/ios-device-livesync-service.ts b/lib/services/livesync/ios-device-livesync-service.ts index 25c35ba479..8dc6831e69 100644 --- a/lib/services/livesync/ios-device-livesync-service.ts +++ b/lib/services/livesync/ios-device-livesync-service.ts @@ -2,7 +2,6 @@ import * as helpers from "../../common/helpers"; import * as constants from "../../constants"; import * as minimatch from "minimatch"; import * as net from "net"; -import Future = require("fibers/future"); let currentPageReloadId = 0; @@ -19,10 +18,10 @@ class IOSLiveSyncService implements IDeviceLiveSyncService { private $logger: ILogger, private $options: IOptions, private $iOSDebugService: IDebugService, - private $childProcess: IChildProcess, private $fs: IFileSystem, private $liveSyncProvider: ILiveSyncProvider, private $processService: IProcessService) { + this.device = (_device); } @@ -30,108 +29,97 @@ class IOSLiveSyncService implements IDeviceLiveSyncService { return this.$iOSDebugService; } - public afterInstallApplicationAction(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): IFuture { - return (() => { - return this.$options.watch; - }).future()(); - } + public async afterInstallApplicationAction(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise { + return this.$options.watch; + } - private setupSocketIfNeeded(): IFuture { - return (() => { - if (this.socket) { - return true; - } + private async setupSocketIfNeeded(): Promise { + if (this.socket) { + return true; + } - if (this.device.isEmulator) { - this.$iOSEmulatorServices.postDarwinNotification(this.$iOSNotification.attachRequest).wait(); - try { - this.socket = helpers.connectEventuallyUntilTimeout(() => net.connect(IOSLiveSyncService.BACKEND_PORT), 5000).wait(); - } catch (e) { - this.$logger.debug(e); - return false; - } - } else { - let timeout = 9000; - this.$iOSSocketRequestExecutor.executeAttachRequest(this.device, timeout).wait(); - this.socket = this.device.connectToPort(IOSLiveSyncService.BACKEND_PORT); + if (this.device.isEmulator) { + await this.$iOSEmulatorServices.postDarwinNotification(this.$iOSNotification.attachRequest); + try { + this.socket = await helpers.connectEventuallyUntilTimeout(() => net.connect(IOSLiveSyncService.BACKEND_PORT), 5000); + } catch (e) { + this.$logger.debug(e); + return false; } + } else { + let timeout = 9000; + await this.$iOSSocketRequestExecutor.executeAttachRequest(this.device, timeout); + this.socket = this.device.connectToPort(IOSLiveSyncService.BACKEND_PORT); + } - this.attachEventHandlers(); + this.attachEventHandlers(); - return true; - }).future()(); + return true; } - public removeFiles(appIdentifier: string, localToDevicePaths: Mobile.ILocalToDevicePathData[]): IFuture { - return (() => { - _.each(localToDevicePaths, localToDevicePathData => this.device.fileSystem.deleteFile(localToDevicePathData.getDevicePath(), appIdentifier)); - }).future()(); + public async removeFiles(appIdentifier: string, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise { + await Promise.all(_.map(localToDevicePaths, localToDevicePathData => this.device.fileSystem.deleteFile(localToDevicePathData.getDevicePath(), appIdentifier))); } - public refreshApplication(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], forceExecuteFullSync: boolean): IFuture { - return (() => { - if (forceExecuteFullSync) { - this.restartApplication(deviceAppData).wait(); - return; - } - let scriptRelatedFiles: Mobile.ILocalToDevicePathData[] = []; - let scriptFiles = _.filter(localToDevicePaths, localToDevicePath => _.endsWith(localToDevicePath.getDevicePath(), ".js")); - constants.LIVESYNC_EXCLUDED_FILE_PATTERNS.forEach(pattern => scriptRelatedFiles = _.concat(scriptRelatedFiles, localToDevicePaths.filter(file => minimatch(file.getDevicePath(), pattern, { nocase: true })))); + public async refreshApplication(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], forceExecuteFullSync: boolean): Promise { + if (forceExecuteFullSync) { + await this.restartApplication(deviceAppData); + return; + } - let otherFiles = _.difference(localToDevicePaths, _.concat(scriptFiles, scriptRelatedFiles)); - let shouldRestart = _.some(otherFiles, (localToDevicePath: Mobile.ILocalToDevicePathData) => !this.$liveSyncProvider.canExecuteFastSync(localToDevicePath.getLocalPath(), deviceAppData.platform)); + let scriptRelatedFiles: Mobile.ILocalToDevicePathData[] = []; + let scriptFiles = _.filter(localToDevicePaths, localToDevicePath => _.endsWith(localToDevicePath.getDevicePath(), ".js")); + constants.LIVESYNC_EXCLUDED_FILE_PATTERNS.forEach(pattern => scriptRelatedFiles = _.concat(scriptRelatedFiles, localToDevicePaths.filter(file => minimatch(file.getDevicePath(), pattern, { nocase: true })))); - if (shouldRestart || (!this.$options.liveEdit && scriptFiles.length)) { - this.restartApplication(deviceAppData).wait(); - return; - } + let otherFiles = _.difference(localToDevicePaths, _.concat(scriptFiles, scriptRelatedFiles)); + let shouldRestart = _.some(otherFiles, (localToDevicePath: Mobile.ILocalToDevicePathData) => !this.$liveSyncProvider.canExecuteFastSync(localToDevicePath.getLocalPath(), deviceAppData.platform)); - if (this.setupSocketIfNeeded().wait()) { - this.liveEdit(scriptFiles); - this.reloadPage(deviceAppData, otherFiles).wait(); - } else { - this.restartApplication(deviceAppData).wait(); - } - }).future()(); + if (shouldRestart || (!this.$options.liveEdit && scriptFiles.length)) { + await this.restartApplication(deviceAppData); + return; + } + + if (await this.setupSocketIfNeeded()) { + this.liveEdit(scriptFiles); + await this.reloadPage(deviceAppData, otherFiles); + } else { + await this.restartApplication(deviceAppData); + } } - private restartApplication(deviceAppData: Mobile.IDeviceAppData): IFuture { + private async restartApplication(deviceAppData: Mobile.IDeviceAppData): Promise { let projectData: IProjectData = this.$injector.resolve("projectData"); return this.device.applicationManager.restartApplication(deviceAppData.appIdentifier, projectData.projectName); } - private reloadPage(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): IFuture { - return (() => { - if (localToDevicePaths.length) { - let message = JSON.stringify({ - method: "Page.reload", - params: { - ignoreCache: false - }, - id: ++currentPageReloadId - }); + private async reloadPage(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise { + if (localToDevicePaths.length) { + let message = JSON.stringify({ + method: "Page.reload", + params: { + ignoreCache: false + }, + id: ++currentPageReloadId + }); - this.sendMessage(message).wait(); - } - }).future()(); + await this.sendMessage(message); + } } - private liveEdit(localToDevicePaths: Mobile.ILocalToDevicePathData[]): IFuture { - return (() => { - _.each(localToDevicePaths, localToDevicePath => { - let content = this.$fs.readText(localToDevicePath.getLocalPath()); - let message = JSON.stringify({ - method: "Debugger.setScriptSource", - params: { - scriptUrl: localToDevicePath.getRelativeToProjectBasePath(), - scriptSource: content - }, - id: ++currentPageReloadId - }); - - this.sendMessage(message).wait(); + private async liveEdit(localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise { + for (let localToDevicePath of localToDevicePaths) { + let content = this.$fs.readText(localToDevicePath.getLocalPath()); + let message = JSON.stringify({ + method: "Debugger.setScriptSource", + params: { + scriptUrl: localToDevicePath.getRelativeToProjectBasePath(), + scriptSource: content + }, + id: ++currentPageReloadId }); - }).future()(); + + await this.sendMessage(message); + } } private attachEventHandlers(): void { @@ -146,47 +134,47 @@ class IOSLiveSyncService implements IDeviceLiveSyncService { this.$logger.trace(`Socket error received: ${error}`); }); - this.socket.on("data", (data: NodeBuffer|string) => { + this.socket.on("data", (data: NodeBuffer | string) => { this.$logger.trace(`Socket sent data: ${data.toString()}`); }); } - private sendMessage(message: string): IFuture { - return (() => { - let socketWriteFuture = new Future(); - try { + private async sendMessage(message: string): Promise { + try { + await new Promise((resolve, reject) => { + let isResolved = false; let length = Buffer.byteLength(message, "utf16le"); let payload = new Buffer(length + 4); payload.writeInt32BE(length, 0); payload.write(message, 4, length, "utf16le"); this.socket.once("error", (error: Error) => { - if (!socketWriteFuture.isResolved()) { - socketWriteFuture.throw(error); + if (!isResolved) { + isResolved = true; + reject(error); } }); this.socket.write(payload, "utf16le", () => { this.socket.removeAllListeners("error"); - if (!socketWriteFuture.isResolved()) { - socketWriteFuture.return(); + if (!isResolved) { + isResolved = true; + resolve(); } }); - - socketWriteFuture.wait(); - } catch(error) { - this.$logger.trace("Error while sending message:", error); - this.destroySocket(); - } - }).future()(); + }); + } catch (error) { + this.$logger.trace("Error while sending message:", error); + this.destroySocket(); + } } private destroySocket(): void { - if(this.socket) { + if (this.socket) { this.socket.destroy(); this.socket = null; } } } -$injector.register("iosLiveSyncServiceLocator", {factory: IOSLiveSyncService}); +$injector.register("iosLiveSyncServiceLocator", { factory: IOSLiveSyncService }); diff --git a/lib/services/livesync/livesync-service.ts b/lib/services/livesync/livesync-service.ts index 368a1ff214..320f1a3fa5 100644 --- a/lib/services/livesync/livesync-service.ts +++ b/lib/services/livesync/livesync-service.ts @@ -2,7 +2,6 @@ import * as constants from "../../constants"; import * as helpers from "../../common/helpers"; import * as path from "path"; import * as semver from "semver"; -import * as fiberBootstrap from "../../common/fiber-bootstrap"; let choki = require("chokidar"); class LiveSyncService implements ILiveSyncService { @@ -16,7 +15,6 @@ class LiveSyncService implements ILiveSyncService { private $projectDataService: IProjectDataService, private $prompter: IPrompter, private $injector: IInjector, - private $liveSyncProvider: ILiveSyncProvider, private $mobileHelper: Mobile.IMobileHelper, private $devicesService: Mobile.IDevicesService, private $options: IOptions, @@ -25,66 +23,70 @@ class LiveSyncService implements ILiveSyncService { private $hooksService: IHooksService, private $processService: IProcessService) { } - private ensureAndroidFrameworkVersion(platformData: IPlatformData): IFuture { // TODO: this can be moved inside command or canExecute function - return (() => { - this.$projectDataService.initialize(this.$projectData.projectDir); - let frameworkVersion = this.$projectDataService.getValue(platformData.frameworkPackageName).version; - - if (platformData.normalizedPlatformName.toLowerCase() === this.$devicePlatformsConstants.Android.toLowerCase()) { - if (semver.lt(frameworkVersion, "1.2.1")) { - let shouldUpdate = this.$prompter.confirm("You need Android Runtime 1.2.1 or later for LiveSync to work properly. Do you want to update your runtime now?").wait(); - if (shouldUpdate) { - this.$platformService.updatePlatforms([this.$devicePlatformsConstants.Android.toLowerCase()]).wait(); - } else { - return; - } + private async ensureAndroidFrameworkVersion(platformData: IPlatformData): Promise { // TODO: this can be moved inside command or canExecute function + this.$projectDataService.initialize(this.$projectData.projectDir); + let frameworkVersion = this.$projectDataService.getValue(platformData.frameworkPackageName).version; + + if (platformData.normalizedPlatformName.toLowerCase() === this.$devicePlatformsConstants.Android.toLowerCase()) { + if (semver.lt(frameworkVersion, "1.2.1")) { + let shouldUpdate = await this.$prompter.confirm("You need Android Runtime 1.2.1 or later for LiveSync to work properly. Do you want to update your runtime now?"); + if (shouldUpdate) { + await this.$platformService.updatePlatforms([this.$devicePlatformsConstants.Android.toLowerCase()]); + } else { + return; } } - }).future()(); + } } public get isInitialized(): boolean { // This function is used from https://github.com/NativeScript/nativescript-dev-typescript/blob/master/lib/before-prepare.js#L4 return this._isInitialized; } - public liveSync(platform: string, applicationReloadAction?: (deviceAppData: Mobile.IDeviceAppData) => IFuture): IFuture { - return (() => { + public async liveSync(platform: string, applicationReloadAction?: (deviceAppData: Mobile.IDeviceAppData) => Promise): Promise { if (this.$options.justlaunch) { this.$options.watch = false; } - let liveSyncData: ILiveSyncData[] = []; - if (platform) { - this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }).wait(); - liveSyncData.push(this.prepareLiveSyncData(platform)); - } else if (this.$options.device) { - this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }).wait(); - platform = this.$devicesService.getDeviceByIdentifier(this.$options.device).deviceInfo.platform; - liveSyncData.push(this.prepareLiveSyncData(platform)); - } else { - this.$devicesService.initialize({ skipInferPlatform: true }).wait(); - this.$devicesService.stopDeviceDetectionInterval().wait(); - for(let installedPlatform of this.$platformService.getInstalledPlatforms()) { - if (this.$devicesService.getDevicesForPlatform(installedPlatform).length === 0) { - this.$devicesService.startEmulator(installedPlatform).wait(); - } - liveSyncData.push(this.prepareLiveSyncData(installedPlatform)); + let liveSyncData: ILiveSyncData[] = []; + + if (platform) { + await this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }); + liveSyncData.push(await this.prepareLiveSyncData(platform)); + } else if (this.$options.device) { + await this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }); + platform = this.$devicesService.getDeviceByIdentifier(this.$options.device).deviceInfo.platform; + liveSyncData.push(await this.prepareLiveSyncData(platform)); + } else { + await this.$devicesService.initialize({ skipInferPlatform: true }); + + await this.$devicesService.stopDeviceDetectionInterval(); + + for (let installedPlatform of this.$platformService.getInstalledPlatforms()) { + if (this.$devicesService.getDevicesForPlatform(installedPlatform).length === 0) { + await this.$devicesService.startEmulator(installedPlatform); } + + liveSyncData.push(await this.prepareLiveSyncData(installedPlatform)); } - if (liveSyncData.length === 0) { - this.$errors.fail("There are no platforms installed in this project. Please specify platform or install one by using `tns platform add` command!"); - } - this._isInitialized = true; // If we want before-prepare hooks to work properly, this should be set after preparePlatform function + } + + if (liveSyncData.length === 0) { + this.$errors.fail("There are no platforms installed in this project. Please specify platform or install one by using `tns platform add` command!"); + } + + this._isInitialized = true; // If we want before-prepare hooks to work properly, this should be set after preparePlatform function - this.liveSyncCore(liveSyncData, applicationReloadAction).wait(); - }).future()(); + await this.liveSyncCore(liveSyncData, applicationReloadAction); } - private prepareLiveSyncData(platform: string): ILiveSyncData { + private async prepareLiveSyncData(platform: string): Promise { platform = platform || this.$devicesService.platform; let platformData = this.$platformsData.getPlatformData(platform.toLowerCase()); + if (this.$mobileHelper.isAndroidPlatform(platform)) { - this.ensureAndroidFrameworkVersion(platformData).wait(); + await this.ensureAndroidFrameworkVersion(platformData); } + let liveSyncData: ILiveSyncData = { platform: platform, appIdentifier: this.$projectData.projectId, @@ -97,47 +99,46 @@ class LiveSyncService implements ILiveSyncService { } @helpers.hook('livesync') - private liveSyncCore(liveSyncData: ILiveSyncData[], applicationReloadAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => IFuture): IFuture { - return (() => { - let watchForChangeActions: ((event: string, filePath: string, dispatcher: IFutureDispatcher) => void)[] = []; - _.each(liveSyncData, (dataItem) => { - let service: IPlatformLiveSyncService = this.$injector.resolve("platformLiveSyncService", { _liveSyncData: dataItem }); - watchForChangeActions.push((event: string, filePath: string, dispatcher: IFutureDispatcher) => { - service.partialSync(event, filePath, dispatcher, applicationReloadAction); - }); - service.fullSync(applicationReloadAction).wait(); - }); + private async liveSyncCore(liveSyncData: ILiveSyncData[], applicationReloadAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise): Promise { + let watchForChangeActions: ((event: string, filePath: string, dispatcher: IFutureDispatcher) => Promise)[] = []; - if(this.$options.watch && !this.$options.justlaunch) { - this.$hooksService.executeBeforeHooks('watch').wait(); - this.partialSync(liveSyncData[0].syncWorkingDirectory, watchForChangeActions); - } - }).future()(); + for (let dataItem of liveSyncData) { + let service: IPlatformLiveSyncService = this.$injector.resolve("platformLiveSyncService", { _liveSyncData: dataItem }); + watchForChangeActions.push((event: string, filePath: string, dispatcher: IFutureDispatcher) => + service.partialSync(event, filePath, dispatcher, applicationReloadAction)); + + await service.fullSync(); + } + + if (this.$options.watch && !this.$options.justlaunch) { + await this.$hooksService.executeBeforeHooks('watch'); + await this.partialSync(liveSyncData[0].syncWorkingDirectory, watchForChangeActions); + } } - private partialSync(syncWorkingDirectory: string, onChangedActions: ((event: string, filePath: string, dispatcher: IFutureDispatcher) => void )[]): void { + private partialSync(syncWorkingDirectory: string, onChangedActions: ((event: string, filePath: string, dispatcher: IFutureDispatcher) => Promise)[]): void { let that = this; let pattern = ["app", "package.json", "node_modules"]; let watcher = choki.watch(pattern, { ignoreInitial: true, cwd: syncWorkingDirectory }).on("all", (event: string, filePath: string) => { - fiberBootstrap.run(() => { - that.$dispatcher.dispatch(() => (() => { - try { - filePath = path.join(syncWorkingDirectory, filePath); - for (let i = 0; i < onChangedActions.length; i++) { - onChangedActions[i](event, filePath, that.$dispatcher); - } - } catch (err) { - that.$logger.info(`Unable to sync file ${filePath}. Error is:${err.message}`.red.bold); - that.$logger.info("Try saving it again or restart the livesync operation."); + that.$dispatcher.dispatch(async () => { + try { + filePath = path.join(syncWorkingDirectory, filePath); + for (let i = 0; i < onChangedActions.length; i++) { + await onChangedActions[i](event, filePath, that.$dispatcher); } - }).future()()); + } catch (err) { + that.$logger.info(`Unable to sync file ${filePath}. Error is:${err.message}`.red.bold); + that.$logger.info("Try saving it again or restart the livesync operation."); + } }); }); this.$processService.attachToProcessExitSignals(this, () => { watcher.close(pattern); }); + this.$dispatcher.run(); } } + $injector.register("usbLiveSyncService", LiveSyncService); diff --git a/lib/services/livesync/platform-livesync-service.ts b/lib/services/livesync/platform-livesync-service.ts index f8e79852ec..b231b3c4d7 100644 --- a/lib/services/livesync/platform-livesync-service.ts +++ b/lib/services/livesync/platform-livesync-service.ts @@ -17,49 +17,43 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe private $logger: ILogger, private $options: IOptions, private $deviceAppDataFactory: Mobile.IDeviceAppDataFactory, - private $fs: IFileSystem, private $injector: IInjector, private $projectFilesManager: IProjectFilesManager, private $projectFilesProvider: IProjectFilesProvider, private $platformService: IPlatformService, - private $platformsData: IPlatformsData, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, - private $projectData: IProjectData, private $projectChangesService: IProjectChangesService, private $liveSyncProvider: ILiveSyncProvider) { this.liveSyncData = _liveSyncData; } - public fullSync(postAction?: (deviceAppData: Mobile.IDeviceAppData) => IFuture): IFuture { - return (() => { - let appIdentifier = this.liveSyncData.appIdentifier; - let platform = this.liveSyncData.platform; - let projectFilesPath = this.liveSyncData.projectFilesPath; - let canExecute = this.getCanExecuteAction(platform, appIdentifier); - let action = (device: Mobile.IDevice): IFuture => { - return (() => { - let deviceAppData = this.$deviceAppDataFactory.create(appIdentifier, this.$mobileHelper.normalizePlatformName(platform), device); - let localToDevicePaths: Mobile.ILocalToDevicePathData[] = null; - if (this.shouldTransferAllFiles(platform, deviceAppData)) { - localToDevicePaths = this.$projectFilesManager.createLocalToDevicePaths(deviceAppData, projectFilesPath, null, this.liveSyncData.excludedProjectDirsAndFiles); - this.transferFiles(deviceAppData, localToDevicePaths, this.liveSyncData.projectFilesPath, true).wait(); - device.fileSystem.putFile(this.$projectChangesService.getPrepareInfoFilePath(platform), this.getLiveSyncInfoFilePath(deviceAppData)).wait(); - } + public async fullSync(postAction?: (deviceAppData: Mobile.IDeviceAppData) => Promise): Promise { + let appIdentifier = this.liveSyncData.appIdentifier; + let platform = this.liveSyncData.platform; + let projectFilesPath = this.liveSyncData.projectFilesPath; + let canExecute = this.getCanExecuteAction(platform, appIdentifier); + let action = async (device: Mobile.IDevice): Promise => { + let deviceAppData = this.$deviceAppDataFactory.create(appIdentifier, this.$mobileHelper.normalizePlatformName(platform), device); + let localToDevicePaths: Mobile.ILocalToDevicePathData[] = null; + if (await this.shouldTransferAllFiles(platform, deviceAppData)) { + localToDevicePaths = await this.$projectFilesManager.createLocalToDevicePaths(deviceAppData, projectFilesPath, null, this.liveSyncData.excludedProjectDirsAndFiles); + await this.transferFiles(deviceAppData, localToDevicePaths, this.liveSyncData.projectFilesPath, true); + await device.fileSystem.putFile(this.$projectChangesService.getPrepareInfoFilePath(platform), await this.getLiveSyncInfoFilePath(deviceAppData)); + } - if (postAction) { - this.finishLivesync(deviceAppData).wait(); - return postAction(deviceAppData).wait(); - } + if (postAction) { + await this.finishLivesync(deviceAppData); + await postAction(deviceAppData); + return; + } - this.refreshApplication(deviceAppData, localToDevicePaths, true).wait(); - this.finishLivesync(deviceAppData).wait(); - }).future()(); - }; - this.$devicesService.execute(action, canExecute).wait(); - }).future()(); + await this.refreshApplication(deviceAppData, localToDevicePaths, true); + await this.finishLivesync(deviceAppData); + }; + await this.$devicesService.execute(action, canExecute); } - public partialSync(event: string, filePath: string, dispatcher: IFutureDispatcher, afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => IFuture): void { + public async partialSync(event: string, filePath: string, dispatcher: IFutureDispatcher, afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise): Promise { if (this.isFileExcluded(filePath, this.liveSyncData.excludedProjectDirsAndFiles)) { this.$logger.trace(`Skipping livesync for changed file ${filePath} as it is excluded in the patterns: ${this.liveSyncData.excludedProjectDirsAndFiles.join(", ")}`); return; @@ -68,7 +62,7 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe if (event === "add" || event === "change") { this.batchSync(filePath, dispatcher, afterFileSyncAction); } else if (event === "unlink") { - this.syncRemovedFile(filePath, afterFileSyncAction).wait(); + await this.syncRemovedFile(filePath, afterFileSyncAction); } } @@ -80,32 +74,26 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe return isTheSamePlatformAction; } - public refreshApplication(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], isFullSync: boolean): IFuture { - return (() => { - let deviceLiveSyncService = this.resolveDeviceSpecificLiveSyncService(deviceAppData.device.deviceInfo.platform, deviceAppData.device); - this.$logger.info("Refreshing application..."); - deviceLiveSyncService.refreshApplication(deviceAppData, localToDevicePaths, isFullSync).wait(); - }).future()(); + public async refreshApplication(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], isFullSync: boolean): Promise { + let deviceLiveSyncService = this.resolveDeviceSpecificLiveSyncService(deviceAppData.device.deviceInfo.platform, deviceAppData.device); + this.$logger.info("Refreshing application..."); + await deviceLiveSyncService.refreshApplication(deviceAppData, localToDevicePaths, isFullSync); } - protected finishLivesync(deviceAppData: Mobile.IDeviceAppData): IFuture { - return (() => { - // This message is important because it signals Visual Studio Code that livesync has finished and debugger can be attached. - this.$logger.info(`Successfully synced application ${deviceAppData.appIdentifier} on device ${deviceAppData.device.deviceInfo.identifier}.\n`); - }).future()(); + protected async finishLivesync(deviceAppData: Mobile.IDeviceAppData): Promise { + // This message is important because it signals Visual Studio Code that livesync has finished and debugger can be attached. + this.$logger.info(`Successfully synced application ${deviceAppData.appIdentifier} on device ${deviceAppData.device.deviceInfo.identifier}.\n`); } - protected transferFiles(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], projectFilesPath: string, isFullSync: boolean): IFuture { - return (() => { - this.$logger.info("Transferring project files..."); - let canTransferDirectory = isFullSync && (this.$devicesService.isAndroidDevice(deviceAppData.device) || this.$devicesService.isiOSSimulator(deviceAppData.device)); - if (canTransferDirectory) { - deviceAppData.device.fileSystem.transferDirectory(deviceAppData, localToDevicePaths, projectFilesPath).wait(); - } else { - this.$liveSyncProvider.transferFiles(deviceAppData, localToDevicePaths, projectFilesPath, isFullSync).wait(); - } - this.logFilesSyncInformation(localToDevicePaths, "Successfully transferred %s.", this.$logger.info); - }).future()(); + protected async transferFiles(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], projectFilesPath: string, isFullSync: boolean): Promise { + this.$logger.info("Transferring project files..."); + let canTransferDirectory = isFullSync && (this.$devicesService.isAndroidDevice(deviceAppData.device) || this.$devicesService.isiOSSimulator(deviceAppData.device)); + if (canTransferDirectory) { + await deviceAppData.device.fileSystem.transferDirectory(deviceAppData, localToDevicePaths, projectFilesPath); + } else { + await this.$liveSyncProvider.transferFiles(deviceAppData, localToDevicePaths, projectFilesPath, isFullSync); + } + this.logFilesSyncInformation(localToDevicePaths, "Successfully transferred %s.", this.$logger.info); } protected resolveDeviceSpecificLiveSyncService(platform: string, device: Mobile.IDevice): IDeviceLiveSyncService { @@ -124,29 +112,28 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe return isFileExcluded; } - private batchSync(filePath: string, dispatcher: IFutureDispatcher, afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => IFuture): void { + private batchSync(filePath: string, dispatcher: IFutureDispatcher, afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise): void { let platformBatch: ISyncBatch = this.batch[this.liveSyncData.platform]; if (!platformBatch || !platformBatch.syncPending) { - let done = () => { - return (() => { - dispatcher.dispatch(() => (() => { - try { - for (let platform in this.batch) { - let batch = this.batch[platform]; - batch.syncFiles(((filesToSync:string[]) => { - this.$platformService.preparePlatform(this.liveSyncData.platform).wait(); - let canExecute = this.getCanExecuteAction(this.liveSyncData.platform, this.liveSyncData.appIdentifier); - let deviceFileAction = (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => this.transferFiles(deviceAppData, localToDevicePaths, this.liveSyncData.projectFilesPath, !filePath); - let action = this.getSyncAction(filesToSync, deviceFileAction, afterFileSyncAction); - this.$devicesService.execute(action, canExecute).wait(); - }).future()).wait(); - } - } catch (err) { - this.$logger.warn(`Unable to sync files. Error is:`, err.message); + let done = async () => { + dispatcher.dispatch(async () => { + try { + for (let platform in this.batch) { + let batch = this.batch[platform]; + await batch.syncFiles(async (filesToSync: string[]) => { + await this.$platformService.preparePlatform(this.liveSyncData.platform); + let canExecute = this.getCanExecuteAction(this.liveSyncData.platform, this.liveSyncData.appIdentifier); + let deviceFileAction = (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => this.transferFiles(deviceAppData, localToDevicePaths, this.liveSyncData.projectFilesPath, !filePath); + let action = this.getSyncAction(filesToSync, deviceFileAction, afterFileSyncAction); + await this.$devicesService.execute(action, canExecute); + }); } - }).future()()); - }).future()(); + } catch (err) { + this.$logger.warn(`Unable to sync files. Error is:`, err.message); + } + }); }; + this.batch[this.liveSyncData.platform] = this.$injector.resolve(syncBatchLib.SyncBatch, { done: done }); this.livesyncData[this.liveSyncData.platform] = this.liveSyncData; } @@ -154,75 +141,73 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe this.batch[this.liveSyncData.platform].addFile(filePath); } - private syncRemovedFile(filePath: string, - afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => IFuture): IFuture { - return (() => { - let deviceFilesAction = (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => { - let deviceLiveSyncService = this.resolveDeviceSpecificLiveSyncService(this.liveSyncData.platform, deviceAppData.device); - return deviceLiveSyncService.removeFiles(this.liveSyncData.appIdentifier, localToDevicePaths); - }; - let canExecute = this.getCanExecuteAction(this.liveSyncData.platform, this.liveSyncData.appIdentifier); - let action = this.getSyncAction([filePath], deviceFilesAction, afterFileSyncAction); - this.$devicesService.execute(action, canExecute).wait(); - }).future()(); + private async syncRemovedFile(filePath: string, + afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise): Promise { + let deviceFilesAction = (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => { + let deviceLiveSyncService = this.resolveDeviceSpecificLiveSyncService(this.liveSyncData.platform, deviceAppData.device); + return deviceLiveSyncService.removeFiles(this.liveSyncData.appIdentifier, localToDevicePaths); + }; + let canExecute = this.getCanExecuteAction(this.liveSyncData.platform, this.liveSyncData.appIdentifier); + let action = this.getSyncAction([filePath], deviceFilesAction, afterFileSyncAction); + await this.$devicesService.execute(action, canExecute); } private getSyncAction( filesToSync: string[], - fileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => IFuture, - afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => IFuture): (device: Mobile.IDevice) => IFuture { - let action = (device: Mobile.IDevice): IFuture => { - return (() => { - let deviceAppData:Mobile.IDeviceAppData = null; - let localToDevicePaths:Mobile.ILocalToDevicePathData[] = null; - let isFullSync = false; - if (this.$options.clean || this.$projectChangesService.currentChanges.changesRequireBuild) { - let buildConfig: IBuildConfig = { buildForDevice: !device.isEmulator }; - let platform = device.deviceInfo.platform; - if (this.$platformService.shouldBuild(platform, buildConfig)) { - this.$platformService.buildPlatform(platform, buildConfig).wait(); - } - this.$platformService.installApplication(device).wait(); - deviceAppData = this.$deviceAppDataFactory.create(this.liveSyncData.appIdentifier, this.$mobileHelper.normalizePlatformName(this.liveSyncData.platform), device); - isFullSync = true; - } else { - deviceAppData = this.$deviceAppDataFactory.create(this.liveSyncData.appIdentifier, this.$mobileHelper.normalizePlatformName(this.liveSyncData.platform), device); - let mappedFiles = filesToSync.map((file: string) => this.$projectFilesProvider.mapFilePath(file, device.deviceInfo.platform)); - localToDevicePaths = this.$projectFilesManager.createLocalToDevicePaths(deviceAppData, this.liveSyncData.projectFilesPath, mappedFiles, this.liveSyncData.excludedProjectDirsAndFiles); - fileSyncAction(deviceAppData, localToDevicePaths).wait(); + fileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise, + afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise): (device: Mobile.IDevice) => Promise { + let action = async (device: Mobile.IDevice): Promise => { + let deviceAppData: Mobile.IDeviceAppData = null; + let localToDevicePaths: Mobile.ILocalToDevicePathData[] = null; + let isFullSync = false; + if (this.$options.clean || this.$projectChangesService.currentChanges.changesRequireBuild) { + let buildConfig: IBuildConfig = { buildForDevice: !device.isEmulator }; + let platform = device.deviceInfo.platform; + if (this.$platformService.shouldBuild(platform, buildConfig)) { + await this.$platformService.buildPlatform(platform, buildConfig); } - if (!afterFileSyncAction) { - this.refreshApplication(deviceAppData, localToDevicePaths, isFullSync).wait(); - } - device.fileSystem.putFile(this.$projectChangesService.getPrepareInfoFilePath(device.deviceInfo.platform), this.getLiveSyncInfoFilePath(deviceAppData)).wait(); - this.finishLivesync(deviceAppData).wait(); - if (afterFileSyncAction) { - afterFileSyncAction(deviceAppData, localToDevicePaths).wait(); - } - }).future()(); + await this.$platformService.installApplication(device); + deviceAppData = this.$deviceAppDataFactory.create(this.liveSyncData.appIdentifier, this.$mobileHelper.normalizePlatformName(this.liveSyncData.platform), device); + isFullSync = true; + } else { + deviceAppData = this.$deviceAppDataFactory.create(this.liveSyncData.appIdentifier, this.$mobileHelper.normalizePlatformName(this.liveSyncData.platform), device); + let mappedFiles = filesToSync.map((file: string) => this.$projectFilesProvider.mapFilePath(file, device.deviceInfo.platform)); + localToDevicePaths = await this.$projectFilesManager.createLocalToDevicePaths(deviceAppData, this.liveSyncData.projectFilesPath, mappedFiles, this.liveSyncData.excludedProjectDirsAndFiles); + await fileSyncAction(deviceAppData, localToDevicePaths); + } + if (!afterFileSyncAction) { + await this.refreshApplication(deviceAppData, localToDevicePaths, isFullSync); + } + await device.fileSystem.putFile(this.$projectChangesService.getPrepareInfoFilePath(device.deviceInfo.platform), await this.getLiveSyncInfoFilePath(deviceAppData)); + await this.finishLivesync(deviceAppData); + if (afterFileSyncAction) { + await afterFileSyncAction(deviceAppData, localToDevicePaths); + } }; + return action; } - private shouldTransferAllFiles(platform: string, deviceAppData: Mobile.IDeviceAppData) { + private async shouldTransferAllFiles(platform: string, deviceAppData: Mobile.IDeviceAppData): Promise { try { if (this.$options.clean) { return false; } - let fileText = this.$platformService.readFile(deviceAppData.device, this.getLiveSyncInfoFilePath(deviceAppData)).wait(); + let fileText = await this.$platformService.readFile(deviceAppData.device, await this.getLiveSyncInfoFilePath(deviceAppData)); let remoteLivesyncInfo: IPrepareInfo = JSON.parse(fileText); let localPrepareInfo = this.$projectChangesService.getPrepareInfo(platform); return remoteLivesyncInfo.time !== localPrepareInfo.time; - } catch(e) { + } catch (e) { return true; } } - private getLiveSyncInfoFilePath(deviceAppData: Mobile.IDeviceAppData) { - let deviceRootPath = deviceAppData.deviceProjectRootPath; + private async getLiveSyncInfoFilePath(deviceAppData: Mobile.IDeviceAppData): Promise { + let deviceRootPath = await deviceAppData.getDeviceProjectRootPath(); if (deviceAppData.device.deviceInfo.platform.toLowerCase() === this.$devicePlatformsConstants.Android.toLowerCase()) { deviceRootPath = path.dirname(deviceRootPath); } + let deviceFilePath = path.join(deviceRootPath, livesyncInfoFileName); return deviceFilePath; } @@ -237,4 +222,5 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe } } } + $injector.register("platformLiveSyncService", PlatformLiveSyncServiceBase); diff --git a/lib/services/platform-project-service-base.ts b/lib/services/platform-project-service-base.ts index d8da7981c1..9a0b683cbc 100644 --- a/lib/services/platform-project-service-base.ts +++ b/lib/services/platform-project-service-base.ts @@ -1,7 +1,7 @@ export class PlatformProjectServiceBase implements IPlatformProjectServiceBase { constructor(protected $fs: IFileSystem, - protected $projectData: IProjectData, - protected $projectDataService: IProjectDataService) { + protected $projectData: IProjectData, + protected $projectDataService: IProjectDataService) { } public getPluginPlatformsFolderPath(pluginData: IPluginData, platform: string): string { @@ -12,11 +12,11 @@ export class PlatformProjectServiceBase implements IPlatformProjectServiceBase { let pluginPlatformsFolderPath = this.getPluginPlatformsFolderPath(pluginData, platform), nativeLibraries: string[] = []; - if(pluginPlatformsFolderPath && this.$fs.exists(pluginPlatformsFolderPath)) { + if (pluginPlatformsFolderPath && this.$fs.exists(pluginPlatformsFolderPath)) { let platformsContents = this.$fs.readDirectory(pluginPlatformsFolderPath); nativeLibraries = _(platformsContents) - .filter(platformItemName => filter(platformItemName, pluginPlatformsFolderPath)) - .value(); + .filter(platformItemName => filter(platformItemName, pluginPlatformsFolderPath)) + .value(); } return nativeLibraries; diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 2e77af5733..a8d68d99d3 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -5,13 +5,17 @@ import * as helpers from "../common/helpers"; import * as semver from "semver"; import { AppFilesUpdater } from "./app-files-updater"; import * as temp from "temp"; -import Future = require("fibers/future"); temp.track(); let clui = require("clui"); const buildInfoFileName = ".nsbuildinfo"; export class PlatformService implements IPlatformService { + // Type with hooks needs to have either $hooksService or $injector injected. + // In order to stop TypeScript from failing for not used $hooksService, use it here. + private get _hooksService(): IHooksService { + return this.$hooksService; + } constructor(private $devicesService: Mobile.IDevicesService, private $errors: IErrors, @@ -21,9 +25,7 @@ export class PlatformService implements IPlatformService { private $platformsData: IPlatformsData, private $projectData: IProjectData, private $projectDataService: IProjectDataService, - private $prompter: IPrompter, private $hooksService: IHooksService, - private $commandsService: ICommandsService, private $options: IOptions, private $nodeModulesBuilder: INodeModulesBuilder, private $pluginsService: IPluginsService, @@ -33,143 +35,132 @@ export class PlatformService implements IPlatformService { private $xmlValidator: IXmlValidator, private $npm: INodePackageManager, private $sysInfo: ISysInfo, - private $staticConfig: Config.IStaticConfig, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, private $deviceAppDataFactory: Mobile.IDeviceAppDataFactory, private $projectChangesService: IProjectChangesService, - private $emulatorPlatformService: IEmulatorPlatformService, - private $childProcess: IChildProcess) { } - - public addPlatforms(platforms: string[]): IFuture { - return (() => { - let platformsDir = this.$projectData.platformsDir; - this.$fs.ensureDirectoryExists(platformsDir); + private $emulatorPlatformService: IEmulatorPlatformService) { } - _.each(platforms, platform => { - this.addPlatform(platform.toLowerCase()).wait(); - }); + public async addPlatforms(platforms: string[]): Promise { + let platformsDir = this.$projectData.platformsDir; + this.$fs.ensureDirectoryExists(platformsDir); - }).future()(); + for (let platform of platforms) { + await this.addPlatform(platform.toLowerCase()); + } } - private addPlatform(platformParam: string): IFuture { - return (() => { - let data = platformParam.split("@"), - platform = data[0].toLowerCase(), - version = data[1]; - - this.validatePlatform(platform); + private async addPlatform(platformParam: string): Promise { + let data = platformParam.split("@"), + platform = data[0].toLowerCase(), + version = data[1]; - let platformPath = path.join(this.$projectData.platformsDir, platform); + this.validatePlatform(platform); - if (this.$fs.exists(platformPath)) { - this.$errors.failWithoutHelp("Platform %s already added", platform); - } + let platformPath = path.join(this.$projectData.platformsDir, platform); - let platformData = this.$platformsData.getPlatformData(platform); + if (this.$fs.exists(platformPath)) { + this.$errors.failWithoutHelp("Platform %s already added", platform); + } - // Copy platform specific files in platforms dir - let platformProjectService = platformData.platformProjectService; - platformProjectService.validate().wait(); + let platformData = this.$platformsData.getPlatformData(platform); - // Log the values for project - this.$logger.trace("Creating NativeScript project for the %s platform", platform); - this.$logger.trace("Path: %s", platformData.projectRoot); - this.$logger.trace("Package: %s", this.$projectData.projectId); - this.$logger.trace("Name: %s", this.$projectData.projectName); + // Copy platform specific files in platforms dir + let platformProjectService = platformData.platformProjectService; + await platformProjectService.validate(); - this.$logger.out("Copying template files..."); + // Log the values for project + this.$logger.trace("Creating NativeScript project for the %s platform", platform); + this.$logger.trace("Path: %s", platformData.projectRoot); + this.$logger.trace("Package: %s", this.$projectData.projectId); + this.$logger.trace("Name: %s", this.$projectData.projectName); - let packageToInstall = ""; - let npmOptions: IStringDictionary = { - pathToSave: path.join(this.$projectData.platformsDir, platform), - dependencyType: "save" - }; + this.$logger.out("Copying template files..."); - if (!this.$options.frameworkPath) { - packageToInstall = platformData.frameworkPackageName; - npmOptions["version"] = version; - } + let packageToInstall = ""; + let npmOptions: IStringDictionary = { + pathToSave: path.join(this.$projectData.platformsDir, platform), + dependencyType: "save" + }; - let spinner = new clui.Spinner("Installing " + packageToInstall); - try { - spinner.start(); - let downloadedPackagePath = this.$npmInstallationManager.install(packageToInstall, this.$projectData.projectDir, npmOptions).wait(); - let frameworkDir = path.join(downloadedPackagePath, constants.PROJECT_FRAMEWORK_FOLDER_NAME); - frameworkDir = path.resolve(frameworkDir); + if (!this.$options.frameworkPath) { + packageToInstall = platformData.frameworkPackageName; + npmOptions["version"] = version; + } - let coreModuleName = this.addPlatformCore(platformData, frameworkDir).wait(); - this.$npm.uninstall(coreModuleName, { save: true }, this.$projectData.projectDir).wait(); - } catch (err) { - this.$fs.deleteDirectory(platformPath); - throw err; - } finally { - spinner.stop(); - } + let spinner = new clui.Spinner("Installing " + packageToInstall); + try { + spinner.start(); + let downloadedPackagePath = await this.$npmInstallationManager.install(packageToInstall, this.$projectData.projectDir, npmOptions); + let frameworkDir = path.join(downloadedPackagePath, constants.PROJECT_FRAMEWORK_FOLDER_NAME); + frameworkDir = path.resolve(frameworkDir); + + let coreModuleName = await this.addPlatformCore(platformData, frameworkDir); + await this.$npm.uninstall(coreModuleName, { save: true }, this.$projectData.projectDir); + } catch (err) { + this.$fs.deleteDirectory(platformPath); + throw err; + } finally { + spinner.stop(); + } - this.$logger.out("Project successfully created."); + this.$logger.out("Project successfully created."); - }).future()(); } - private addPlatformCore(platformData: IPlatformData, frameworkDir: string): IFuture { - return (() => { - let coreModuleData = this.$fs.readJson(path.join(frameworkDir, "../", "package.json")); - let installedVersion = coreModuleData.version; - let coreModuleName = coreModuleData.name; - - this.$projectDataService.initialize(this.$projectData.projectDir); - let customTemplateOptions = this.getPathToPlatformTemplate(this.$options.platformTemplate, platformData.frameworkPackageName).wait(); - let pathToTemplate = customTemplateOptions && customTemplateOptions.pathToTemplate; - platformData.platformProjectService.createProject(path.resolve(frameworkDir), installedVersion, pathToTemplate).wait(); - platformData.platformProjectService.ensureConfigurationFileInAppResources(); - platformData.platformProjectService.interpolateData().wait(); - platformData.platformProjectService.afterCreateProject(platformData.projectRoot); + private async addPlatformCore(platformData: IPlatformData, frameworkDir: string): Promise { + let coreModuleData = this.$fs.readJson(path.join(frameworkDir, "../", "package.json")); + let installedVersion = coreModuleData.version; + let coreModuleName = coreModuleData.name; - this.applyBaseConfigOption(platformData); - - let frameworkPackageNameData: any = { version: installedVersion }; - if (customTemplateOptions) { - frameworkPackageNameData.template = customTemplateOptions.selectedTemplate; - } - this.$projectDataService.setValue(platformData.frameworkPackageName, frameworkPackageNameData); + this.$projectDataService.initialize(this.$projectData.projectDir); + let customTemplateOptions = await this.getPathToPlatformTemplate(this.$options.platformTemplate, platformData.frameworkPackageName); + let pathToTemplate = customTemplateOptions && customTemplateOptions.pathToTemplate; + await platformData.platformProjectService.createProject(path.resolve(frameworkDir), installedVersion, pathToTemplate); + platformData.platformProjectService.ensureConfigurationFileInAppResources(); + await platformData.platformProjectService.interpolateData(); + platformData.platformProjectService.afterCreateProject(platformData.projectRoot); + + this.applyBaseConfigOption(platformData); + + let frameworkPackageNameData: any = { version: installedVersion }; + if (customTemplateOptions) { + frameworkPackageNameData.template = customTemplateOptions.selectedTemplate; + } + this.$projectDataService.setValue(platformData.frameworkPackageName, frameworkPackageNameData); - return coreModuleName; + return coreModuleName; - }).future()(); } - private getPathToPlatformTemplate(selectedTemplate: string, frameworkPackageName: string): IFuture { - return (() => { - if (!selectedTemplate) { - // read data from package.json's nativescript key - // check the nativescript.tns-.template value - let nativescriptPlatformData = this.$projectDataService.getValue(frameworkPackageName); - selectedTemplate = nativescriptPlatformData && nativescriptPlatformData.template; - } + private async getPathToPlatformTemplate(selectedTemplate: string, frameworkPackageName: string): Promise { + if (!selectedTemplate) { + // read data from package.json's nativescript key + // check the nativescript.tns-.template value + let nativescriptPlatformData = this.$projectDataService.getValue(frameworkPackageName); + selectedTemplate = nativescriptPlatformData && nativescriptPlatformData.template; + } - if (selectedTemplate) { - let tempDir = temp.mkdirSync("platform-template"); - try { - /* - * Output of npm.install is array of arrays. For example: - * [ [ 'test-android-platform-template@0.0.1', - * 'C:\\Users\\~1\\AppData\\Local\\Temp\\1\\platform-template11627-15560-rm3ngx\\node_modules\\test-android-platform-template', - * undefined, - * undefined, - * '..\\..\\..\\android-platform-template' ] ] - * Project successfully created. - */ - let pathToTemplate = this.$npm.install(selectedTemplate, tempDir).wait()[0]; - return { selectedTemplate, pathToTemplate }; - } catch (err) { - this.$logger.trace("Error while trying to install specified template: ", err); - this.$errors.failWithoutHelp(`Unable to install platform template ${selectedTemplate}. Make sure the specified value is valid.`); - } + if (selectedTemplate) { + let tempDir = temp.mkdirSync("platform-template"); + try { + /* + * Output of npm.install is array of arrays. For example: + * [ [ 'test-android-platform-template@0.0.1', + * 'C:\\Users\\~1\\AppData\\Local\\Temp\\1\\platform-template11627-15560-rm3ngx\\node_modules\\test-android-platform-template', + * undefined, + * undefined, + * '..\\..\\..\\android-platform-template' ] ] + * Project successfully created. + */ + let pathToTemplate = (await this.$npm.install(selectedTemplate, tempDir))[0]; + return { selectedTemplate, pathToTemplate }; + } catch (err) { + this.$logger.trace("Error while trying to install specified template: ", err); + this.$errors.failWithoutHelp(`Unable to install platform template ${selectedTemplate}. Make sure the specified value is valid.`); } + } - return null; - }).future()(); + return null; } public getInstalledPlatforms(): string[] { @@ -192,112 +183,105 @@ export class PlatformService implements IPlatformService { return _.filter(this.$platformsData.platformsNames, p => { return this.isPlatformPrepared(p); }); } - public preparePlatform(platform: string): IFuture { - return (() => { - this.validatePlatform(platform); + public async preparePlatform(platform: string): Promise { + this.validatePlatform(platform); - //We need dev-dependencies here, so before-prepare hooks will be executed correctly. - try { - this.$pluginsService.ensureAllDependenciesAreInstalled().wait(); - } catch (err) { - this.$logger.trace(err); - this.$errors.failWithoutHelp(`Unable to install dependencies. Make sure your package.json is valid and all dependencies are correct. Error is: ${err.message}`); - } + //We need dev-dependencies here, so before-prepare hooks will be executed correctly. + try { + await this.$pluginsService.ensureAllDependenciesAreInstalled(); + } catch (err) { + this.$logger.trace(err); + this.$errors.failWithoutHelp(`Unable to install dependencies. Make sure your package.json is valid and all dependencies are correct. Error is: ${err.message}`); + } - // Need to check if any plugin requires Cocoapods to be installed. - if (platform === "ios") { - _.each(this.$pluginsService.getAllInstalledPlugins().wait(), (pluginData: IPluginData) => { - if (this.$fs.exists(path.join(pluginData.pluginPlatformsFolderPath(platform), "Podfile")) && - !this.$sysInfo.getCocoapodVersion().wait()) { - this.$errors.failWithoutHelp(`${pluginData.name} has Podfile and you don't have Cocoapods installed or it is not configured correctly. Please verify Cocoapods can work on your machine.`); - } - }); + // Need to check if any plugin requires Cocoapods to be installed. + if (platform === "ios") { + for (let pluginData of await this.$pluginsService.getAllInstalledPlugins()) { + if (this.$fs.exists(path.join(pluginData.pluginPlatformsFolderPath(platform), "Podfile")) && + !(await this.$sysInfo.getCocoapodVersion())) { + this.$errors.failWithoutHelp(`${pluginData.name} has Podfile and you don't have Cocoapods installed or it is not configured correctly. Please verify Cocoapods can work on your machine.`); + } } + } - this.ensurePlatformInstalled(platform).wait(); - let changesInfo = this.$projectChangesService.checkForChanges(platform); - if (changesInfo.hasChanges) { - this.preparePlatformCore(platform, changesInfo).wait(); - this.$projectChangesService.savePrepareInfo(platform); - } else { - this.$logger.out("Skipping prepare."); - } + await this.ensurePlatformInstalled(platform); + let changesInfo = this.$projectChangesService.checkForChanges(platform); + if (changesInfo.hasChanges) { + await this.preparePlatformCore(platform, changesInfo); + this.$projectChangesService.savePrepareInfo(platform); + } else { + this.$logger.out("Skipping prepare."); + } - return true; - }).future()(); + return true; } - public validateOptions(platform?: string): IFuture { - return (() => { - if (platform) { - platform = this.$mobileHelper.normalizePlatformName(platform); - this.$logger.trace("Validate options for platform: " + platform); - let platformData = this.$platformsData.getPlatformData(platform); - return platformData.platformProjectService.validateOptions().wait(); - } else { - let valid = true; - for (let availablePlatform in this.$platformsData.availablePlatforms) { - this.$logger.trace("Validate options for platform: " + availablePlatform); - let platformData = this.$platformsData.getPlatformData(availablePlatform); - valid = valid && platformData.platformProjectService.validateOptions().wait(); - } - return valid; + public async validateOptions(platform?: string): Promise { + if (platform) { + platform = this.$mobileHelper.normalizePlatformName(platform); + this.$logger.trace("Validate options for platform: " + platform); + let platformData = this.$platformsData.getPlatformData(platform); + return await platformData.platformProjectService.validateOptions(); + } else { + let valid = true; + for (let availablePlatform in this.$platformsData.availablePlatforms) { + this.$logger.trace("Validate options for platform: " + availablePlatform); + let platformData = this.$platformsData.getPlatformData(availablePlatform); + valid = valid && await platformData.platformProjectService.validateOptions(); } - }).future()(); + + return valid; + } } @helpers.hook('prepare') - private preparePlatformCore(platform: string, changesInfo?: IProjectChangesInfo): IFuture { - return (() => { - this.$logger.out("Preparing project..."); + private async preparePlatformCore(platform: string, changesInfo?: IProjectChangesInfo): Promise { + this.$logger.out("Preparing project..."); - let platformData = this.$platformsData.getPlatformData(platform); + let platformData = this.$platformsData.getPlatformData(platform); - if (!changesInfo || changesInfo.appFilesChanged) { - this.copyAppFiles(platform).wait(); - } - if (!changesInfo || changesInfo.appResourcesChanged) { - this.copyAppResources(platform); - platformData.platformProjectService.prepareProject(); - } - if (!changesInfo || changesInfo.modulesChanged) { - this.copyTnsModules(platform).wait(); - } + if (!changesInfo || changesInfo.appFilesChanged) { + await this.copyAppFiles(platform); + } + if (!changesInfo || changesInfo.appResourcesChanged) { + this.copyAppResources(platform); + await platformData.platformProjectService.prepareProject(); + } + if (!changesInfo || changesInfo.modulesChanged) { + await this.copyTnsModules(platform); + } - let directoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); - let excludedDirs = [constants.APP_RESOURCES_FOLDER_NAME]; - if (!changesInfo || !changesInfo.modulesChanged) { - excludedDirs.push(constants.TNS_MODULES_FOLDER_NAME); - } + let directoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + let excludedDirs = [constants.APP_RESOURCES_FOLDER_NAME]; + if (!changesInfo || !changesInfo.modulesChanged) { + excludedDirs.push(constants.TNS_MODULES_FOLDER_NAME); + } - this.$projectFilesManager.processPlatformSpecificFiles(directoryPath, platform, excludedDirs); + this.$projectFilesManager.processPlatformSpecificFiles(directoryPath, platform, excludedDirs); - if (!changesInfo || changesInfo.configChanged || changesInfo.modulesChanged) { - this.applyBaseConfigOption(platformData); - platformData.platformProjectService.processConfigurationFilesFromAppResources().wait(); - } + if (!changesInfo || changesInfo.configChanged || changesInfo.modulesChanged) { + this.applyBaseConfigOption(platformData); + await platformData.platformProjectService.processConfigurationFilesFromAppResources(); + } - platformData.platformProjectService.interpolateConfigurationFile().wait(); + await platformData.platformProjectService.interpolateConfigurationFile(); - this.$logger.out("Project successfully prepared (" + platform + ")"); - }).future()(); + this.$logger.out("Project successfully prepared (" + platform + ")"); } - private copyAppFiles(platform: string): IFuture { - return (() => { - let platformData = this.$platformsData.getPlatformData(platform); - platformData.platformProjectService.ensureConfigurationFileInAppResources(); - let appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + private async copyAppFiles(platform: string): Promise { + let platformData = this.$platformsData.getPlatformData(platform); + platformData.platformProjectService.ensureConfigurationFileInAppResources(); + let appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); - // Copy app folder to native project - this.$fs.ensureDirectoryExists(appDestinationDirectoryPath); - let appSourceDirectoryPath = path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME); + // Copy app folder to native project + this.$fs.ensureDirectoryExists(appDestinationDirectoryPath); + let appSourceDirectoryPath = path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME); - const appUpdater = new AppFilesUpdater(appSourceDirectoryPath, appDestinationDirectoryPath, this.$options, this.$fs); - appUpdater.updateApp(sourceFiles => { - this.$xmlValidator.validateXmlFiles(sourceFiles); - }); - }).future()(); + const appUpdater = new AppFilesUpdater(appSourceDirectoryPath, appDestinationDirectoryPath, this.$options, this.$fs); + appUpdater.updateApp(sourceFiles => { + this.$xmlValidator.validateXmlFiles(sourceFiles); + }); } private copyAppResources(platform: string): void { @@ -313,171 +297,160 @@ export class PlatformService implements IPlatformService { } } - private copyTnsModules(platform: string): IFuture { - return (() => { - let platformData = this.$platformsData.getPlatformData(platform); - let appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); - let lastModifiedTime = this.$fs.exists(appDestinationDirectoryPath) ? this.$fs.getFsStats(appDestinationDirectoryPath).mtime : null; - - try { - let tnsModulesDestinationPath = path.join(appDestinationDirectoryPath, constants.TNS_MODULES_FOLDER_NAME); - // Process node_modules folder - this.$nodeModulesBuilder.prepareNodeModules(tnsModulesDestinationPath, platform, lastModifiedTime).wait(); - } catch (error) { - this.$logger.debug(error); - shell.rm("-rf", appDestinationDirectoryPath); - this.$errors.failWithoutHelp(`Processing node_modules failed. ${error}`); - } - }).future()(); + private async copyTnsModules(platform: string): Promise { + let platformData = this.$platformsData.getPlatformData(platform); + let appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + let lastModifiedTime = this.$fs.exists(appDestinationDirectoryPath) ? this.$fs.getFsStats(appDestinationDirectoryPath).mtime : null; + + try { + let tnsModulesDestinationPath = path.join(appDestinationDirectoryPath, constants.TNS_MODULES_FOLDER_NAME); + // Process node_modules folder + await this.$nodeModulesBuilder.prepareNodeModules(tnsModulesDestinationPath, platform, lastModifiedTime); + } catch (error) { + this.$logger.debug(error); + shell.rm("-rf", appDestinationDirectoryPath); + this.$errors.failWithoutHelp(`Processing node_modules failed. ${error}`); + } } - public shouldBuild(platform: string, buildConfig?: IBuildConfig): IFuture { - return (() => { + public async shouldBuild(platform: string, buildConfig?: IBuildConfig): Promise { if (this.$projectChangesService.currentChanges.changesRequireBuild) { - return true; - } - let platformData = this.$platformsData.getPlatformData(platform); - let forDevice = !buildConfig || buildConfig.buildForDevice; - let outputPath = forDevice ? platformData.deviceBuildOutputPath : platformData.emulatorBuildOutputPath; - if (!this.$fs.exists(outputPath)) { - return true; - } - let packageNames = forDevice ? platformData.validPackageNamesForDevice : platformData.validPackageNamesForEmulator; - let packages = this.getApplicationPackages(outputPath, packageNames); - if (packages.length === 0) { - return true; - } - let prepareInfo = this.$projectChangesService.getPrepareInfo(platform); - let buildInfo = this.getBuildInfo(platform, platformData, buildConfig); - if (!prepareInfo || !buildInfo) { - return true; - } - if (this.$options.clean) { - return prepareInfo.time !== buildInfo.prepareTime; - } - if (prepareInfo.time === buildInfo.prepareTime) { - return false; - } - return prepareInfo.changesRequireBuildTime !== buildInfo.prepareTime; - }).future()(); + return true; + } + let platformData = this.$platformsData.getPlatformData(platform); + let forDevice = !buildConfig || buildConfig.buildForDevice; + let outputPath = forDevice ? platformData.deviceBuildOutputPath : platformData.emulatorBuildOutputPath; + if (!this.$fs.exists(outputPath)) { + return true; + } + let packageNames = forDevice ? platformData.validPackageNamesForDevice : platformData.validPackageNamesForEmulator; + let packages = this.getApplicationPackages(outputPath, packageNames); + if (packages.length === 0) { + return true; + } + let prepareInfo = this.$projectChangesService.getPrepareInfo(platform); + let buildInfo = this.getBuildInfo(platform, platformData, buildConfig); + if (!prepareInfo || !buildInfo) { + return true; + } + if (this.$options.clean) { + return prepareInfo.time !== buildInfo.prepareTime; + } + if (prepareInfo.time === buildInfo.prepareTime) { + return false; + } + return prepareInfo.changesRequireBuildTime !== buildInfo.prepareTime; } - public buildPlatform(platform: string, buildConfig?: IBuildConfig): IFuture { - return (() => { - this.$logger.out("Building project..."); - let platformData = this.$platformsData.getPlatformData(platform); - platformData.platformProjectService.buildProject(platformData.projectRoot, buildConfig).wait(); - let prepareInfo = this.$projectChangesService.getPrepareInfo(platform); - let buildInfoFilePath = this.getBuildOutputPath(platform, platformData, buildConfig); - let buildInfoFile = path.join(buildInfoFilePath, buildInfoFileName); - let buildInfo: IBuildInfo = { - prepareTime: prepareInfo.changesRequireBuildTime, - buildTime: new Date().toString() - }; - this.$fs.writeJson(buildInfoFile, buildInfo); - this.$logger.out("Project successfully built."); - }).future()(); + public async buildPlatform(platform: string, buildConfig?: IBuildConfig): Promise { + this.$logger.out("Building project..."); + let platformData = this.$platformsData.getPlatformData(platform); + await platformData.platformProjectService.buildProject(platformData.projectRoot, buildConfig); + let prepareInfo = this.$projectChangesService.getPrepareInfo(platform); + let buildInfoFilePath = this.getBuildOutputPath(platform, platformData, buildConfig); + let buildInfoFile = path.join(buildInfoFilePath, buildInfoFileName); + let buildInfo: IBuildInfo = { + prepareTime: prepareInfo.changesRequireBuildTime, + buildTime: new Date().toString() + }; + this.$fs.writeJson(buildInfoFile, buildInfo); + this.$logger.out("Project successfully built."); } - public shouldInstall(device: Mobile.IDevice): boolean { + public async shouldInstall(device: Mobile.IDevice): Promise { let platform = device.deviceInfo.platform; let platformData = this.$platformsData.getPlatformData(platform); - if (!device.applicationManager.isApplicationInstalled(this.$projectData.projectId).wait()) { + if (!(await device.applicationManager.isApplicationInstalled(this.$projectData.projectId))) { return true; } - let deviceBuildInfo: IBuildInfo = this.getDeviceBuildInfo(device).wait(); + let deviceBuildInfo: IBuildInfo = await this.getDeviceBuildInfo(device); let localBuildInfo = this.getBuildInfo(platform, platformData, { buildForDevice: !device.isEmulator }); return !localBuildInfo || !deviceBuildInfo || deviceBuildInfo.buildTime !== localBuildInfo.buildTime; } - public installApplication(device: Mobile.IDevice): IFuture { - return (() => { - this.$logger.out("Installing..."); - let platformData = this.$platformsData.getPlatformData(device.deviceInfo.platform); - let packageFile = ""; - if (this.$devicesService.isiOSSimulator(device)) { - packageFile = this.getLatestApplicationPackageForEmulator(platformData).packageName; + public async installApplication(device: Mobile.IDevice): Promise { + this.$logger.out("Installing..."); + let platformData = this.$platformsData.getPlatformData(device.deviceInfo.platform); + let packageFile = ""; + if (this.$devicesService.isiOSSimulator(device)) { + packageFile = this.getLatestApplicationPackageForEmulator(platformData).packageName; + } else { + packageFile = this.getLatestApplicationPackageForDevice(platformData).packageName; + } + await platformData.platformProjectService.deploy(device.deviceInfo.identifier); + await device.applicationManager.reinstallApplication(this.$projectData.projectId, packageFile); + if (!this.$options.release) { + let deviceFilePath = await this.getDeviceBuildInfoFilePath(device); + let buildInfoFilePath = this.getBuildOutputPath(device.deviceInfo.platform, platformData, { buildForDevice: !device.isEmulator }); + await device.fileSystem.putFile(path.join(buildInfoFilePath, buildInfoFileName), deviceFilePath); + } + this.$logger.out(`Successfully installed on device with identifier '${device.deviceInfo.identifier}'.`); + } + + public async deployPlatform(platform: string, forceInstall?: boolean): Promise { + await this.preparePlatform(platform); + this.$logger.out("Searching for devices..."); + await this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }); + let action = async (device: Mobile.IDevice): Promise => { + let buildConfig: IBuildConfig = { buildForDevice: !this.$devicesService.isiOSSimulator(device) }; + let shouldBuild = await this.shouldBuild(platform, buildConfig); + if (shouldBuild) { + await this.buildPlatform(platform, buildConfig); } else { - packageFile = this.getLatestApplicationPackageForDevice(platformData).packageName; + this.$logger.out("Skipping package build. No changes detected on the native side. This will be fast!"); } - platformData.platformProjectService.deploy(device.deviceInfo.identifier).wait(); - device.applicationManager.reinstallApplication(this.$projectData.projectId, packageFile).wait(); - if (!this.$options.release) { - let deviceFilePath = this.getDeviceBuildInfoFilePath(device); - let buildInfoFilePath = this.getBuildOutputPath(device.deviceInfo.platform, platformData, { buildForDevice: !device.isEmulator }); - device.fileSystem.putFile(path.join(buildInfoFilePath, buildInfoFileName), deviceFilePath).wait(); + if (forceInstall || shouldBuild || (await this.shouldInstall(device))) { + await this.installApplication(device); + } else { + this.$logger.out("Skipping install."); } - this.$logger.out(`Successfully installed on device with identifier '${device.deviceInfo.identifier}'.`); - }).future()(); - } - - public deployPlatform(platform: string, forceInstall?: boolean): IFuture { - return (() => { - this.preparePlatform(platform).wait(); - this.$logger.out("Searching for devices..."); - this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }).wait(); - let action = (device: Mobile.IDevice): IFuture => { - return (() => { - let buildConfig: IBuildConfig = { buildForDevice: !this.$devicesService.isiOSSimulator(device) }; - let shouldBuild = this.shouldBuild(platform, buildConfig).wait(); - if (shouldBuild) { - this.buildPlatform(platform, buildConfig).wait(); - } else { - this.$logger.out("Skipping package build. No changes detected on the native side. This will be fast!"); - } - if (forceInstall || shouldBuild || this.shouldInstall(device)) { - this.installApplication(device).wait(); - } else { - this.$logger.out("Skipping install."); - } - }).future()(); - }; - this.$devicesService.execute(action, this.getCanExecuteAction(platform)).wait(); - }).future()(); - } - - public runPlatform(platform: string): IFuture { - return (() => { - this.$logger.out("Starting..."); - let action = (device: Mobile.IDevice) => { - return (() => { - device.applicationManager.startApplication(this.$projectData.projectId).wait(); - this.$logger.out(`Successfully started on device with identifier '${device.deviceInfo.identifier}'.`); - }).future()(); - }; - this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }).wait(); - this.$devicesService.execute(action, this.getCanExecuteAction(platform)).wait(); - }).future()(); + }; + await this.$devicesService.execute(action, this.getCanExecuteAction(platform)); + } + + public async runPlatform(platform: string): Promise { + this.$logger.out("Starting..."); + let action = async (device: Mobile.IDevice) => { + await device.applicationManager.startApplication(this.$projectData.projectId); + this.$logger.out(`Successfully started on device with identifier '${device.deviceInfo.identifier}'.`); + }; + await this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }); + await this.$devicesService.execute(action, this.getCanExecuteAction(platform)); } - public emulatePlatform(platform: string): IFuture { + public async emulatePlatform(platform: string): Promise { if (this.$options.avd) { this.$logger.warn(`Option --avd is no longer supported. Please use --device instead!`); - return Future.fromResult(); + return Promise.resolve(); } + if (this.$options.availableDevices) { return this.$emulatorPlatformService.listAvailableEmulators(platform); } + this.$options.emulator = true; if (this.$options.device) { - let info = this.$emulatorPlatformService.getEmulatorInfo(platform, this.$options.device).wait(); + let info = await this.$emulatorPlatformService.getEmulatorInfo(platform, this.$options.device); if (info) { if (!info.isRunning) { - this.$emulatorPlatformService.startEmulator(info).wait(); + await this.$emulatorPlatformService.startEmulator(info); } + this.$options.device = null; } else { - this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }).wait(); + await this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }); let found: Mobile.IDeviceInfo[] = []; if (this.$devicesService.hasDevices) { - found = this.$devicesService.getDevices().filter((device:Mobile.IDeviceInfo) => device.identifier === this.$options.device); + found = this.$devicesService.getDevices().filter((device: Mobile.IDeviceInfo) => device.identifier === this.$options.device); } + if (found.length === 0) { this.$errors.fail("Cannot find device with name: %s", this.$options.device); } } } - this.deployPlatform(platform).wait(); + + await this.deployPlatform(platform); return this.runPlatform(platform); } @@ -489,24 +462,23 @@ export class PlatformService implements IPlatformService { return platformData.deviceBuildOutputPath; } - private getDeviceBuildInfoFilePath(device: Mobile.IDevice): string { + private async getDeviceBuildInfoFilePath(device: Mobile.IDevice): Promise { let deviceAppData = this.$deviceAppDataFactory.create(this.$projectData.projectId, device.deviceInfo.platform, device); - let deviceRootPath = deviceAppData.deviceProjectRootPath; + let deviceRootPath = await deviceAppData.getDeviceProjectRootPath(); if (device.deviceInfo.platform.toLowerCase() === this.$devicePlatformsConstants.Android.toLowerCase()) { deviceRootPath = path.dirname(deviceRootPath); } + return path.join(deviceRootPath, buildInfoFileName); } - private getDeviceBuildInfo(device: Mobile.IDevice): IFuture { - return (() => { - let deviceFilePath = this.getDeviceBuildInfoFilePath(device); - try { - return JSON.parse(this.readFile(device, deviceFilePath).wait()); - } catch(e) { - return null; - }; - }).future()(); + private async getDeviceBuildInfo(device: Mobile.IDevice): Promise { + let deviceFilePath = await this.getDeviceBuildInfoFilePath(device); + try { + return JSON.parse(await this.readFile(device, deviceFilePath)); + } catch (e) { + return null; + } } private getBuildInfo(platform: string, platformData: IPlatformData, buildConfig: IBuildConfig): IBuildInfo { @@ -516,23 +488,22 @@ export class PlatformService implements IPlatformService { try { let buildInfoTime = this.$fs.readJson(buildInfoFile); return buildInfoTime; - } catch(e) { + } catch (e) { return null; } } + return null; } - public cleanDestinationApp(platform: string): IFuture { - return (() => { - this.ensurePlatformInstalled(platform).wait(); + public async cleanDestinationApp(platform: string): Promise { + await this.ensurePlatformInstalled(platform); - const appSourceDirectoryPath = path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME); - let platformData = this.$platformsData.getPlatformData(platform); - let appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); - const appUpdater = new AppFilesUpdater(appSourceDirectoryPath, appDestinationDirectoryPath, this.$options, this.$fs); - appUpdater.cleanDestinationApp(); - }).future()(); + const appSourceDirectoryPath = path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME); + let platformData = this.$platformsData.getPlatformData(platform); + let appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + const appUpdater = new AppFilesUpdater(appSourceDirectoryPath, appDestinationDirectoryPath, this.$options, this.$fs); + appUpdater.cleanDestinationApp(); } public lastOutputPath(platform: string, settings: { isForDevice: boolean }): string { @@ -581,20 +552,18 @@ export class PlatformService implements IPlatformService { }); } - public updatePlatforms(platforms: string[]): IFuture { - return (() => { - _.each(platforms, platformParam => { - let data = platformParam.split("@"), - platform = data[0], - version = data[1]; + public async updatePlatforms(platforms: string[]): Promise { + for (let platformParam of platforms) { + let data = platformParam.split("@"), + platform = data[0], + version = data[1]; - if (this.isPlatformInstalled(platform)) { - this.updatePlatform(platform, version).wait(); - } else { - this.addPlatform(platformParam).wait(); - } - }); - }).future()(); + if (this.isPlatformInstalled(platform)) { + await this.updatePlatform(platform, version); + } else { + await this.addPlatform(platformParam); + } + }; } private getCanExecuteAction(platform: string): any { @@ -644,12 +613,10 @@ export class PlatformService implements IPlatformService { } } - public ensurePlatformInstalled(platform: string): IFuture { - return (() => { - if (!this.isPlatformInstalled(platform)) { - this.addPlatform(platform).wait(); - } - }).future()(); + public async ensurePlatformInstalled(platform: string): Promise { + if (!this.isPlatformInstalled(platform)) { + await this.addPlatform(platform); + } } private isPlatformInstalled(platform: string): boolean { @@ -708,50 +675,46 @@ export class PlatformService implements IPlatformService { return this.getLatestApplicationPackage(platformData.emulatorBuildOutputPath || platformData.deviceBuildOutputPath, platformData.validPackageNamesForEmulator || platformData.validPackageNamesForDevice); } - private updatePlatform(platform: string, version: string): IFuture { - return (() => { - let platformData = this.$platformsData.getPlatformData(platform); - - this.$projectDataService.initialize(this.$projectData.projectDir); - let data = this.$projectDataService.getValue(platformData.frameworkPackageName); - let currentVersion = data && data.version ? data.version : "0.2.0"; - - let newVersion = version === constants.PackageVersion.NEXT ? - this.$npmInstallationManager.getNextVersion(platformData.frameworkPackageName).wait() : - version || this.$npmInstallationManager.getLatestCompatibleVersion(platformData.frameworkPackageName).wait(); - let installedModuleDir = this.$npmInstallationManager.install(platformData.frameworkPackageName, this.$projectData.projectDir, { version: newVersion, dependencyType: "save" }).wait(); - let cachedPackageData = this.$fs.readJson(path.join(installedModuleDir, "package.json")); - newVersion = (cachedPackageData && cachedPackageData.version) || newVersion; - - let canUpdate = platformData.platformProjectService.canUpdatePlatform(installedModuleDir); - this.$npm.uninstall(platformData.frameworkPackageName, { save: true }, this.$projectData.projectDir).wait(); - if (canUpdate) { - if (!semver.valid(newVersion)) { - this.$errors.fail("The version %s is not valid. The version should consists from 3 parts separated by dot.", newVersion); - } + private async updatePlatform(platform: string, version: string): Promise { + let platformData = this.$platformsData.getPlatformData(platform); - if (!semver.gt(currentVersion, newVersion)) { - this.updatePlatformCore(platformData, currentVersion, newVersion, canUpdate).wait(); - } else if (semver.eq(currentVersion, newVersion)) { - this.$errors.fail("Current and new version are the same."); - } else { - this.$errors.fail(`Your current version: ${currentVersion} is higher than the one you're trying to install ${newVersion}.`); - } + this.$projectDataService.initialize(this.$projectData.projectDir); + let data = this.$projectDataService.getValue(platformData.frameworkPackageName); + let currentVersion = data && data.version ? data.version : "0.2.0"; + + let newVersion = version === constants.PackageVersion.NEXT ? + await this.$npmInstallationManager.getNextVersion(platformData.frameworkPackageName) : + version || await this.$npmInstallationManager.getLatestCompatibleVersion(platformData.frameworkPackageName); + let installedModuleDir = await this.$npmInstallationManager.install(platformData.frameworkPackageName, this.$projectData.projectDir, { version: newVersion, dependencyType: "save" }); + let cachedPackageData = this.$fs.readJson(path.join(installedModuleDir, "package.json")); + newVersion = (cachedPackageData && cachedPackageData.version) || newVersion; + + let canUpdate = platformData.platformProjectService.canUpdatePlatform(installedModuleDir); + await this.$npm.uninstall(platformData.frameworkPackageName, { save: true }, this.$projectData.projectDir); + if (canUpdate) { + if (!semver.valid(newVersion)) { + this.$errors.fail("The version %s is not valid. The version should consists from 3 parts separated by dot.", newVersion); + } + + if (!semver.gt(currentVersion, newVersion)) { + await this.updatePlatformCore(platformData, currentVersion, newVersion, canUpdate); + } else if (semver.eq(currentVersion, newVersion)) { + this.$errors.fail("Current and new version are the same."); } else { - this.$errors.failWithoutHelp("Native Platform cannot be updated."); + this.$errors.fail(`Your current version: ${currentVersion} is higher than the one you're trying to install ${newVersion}.`); } + } else { + this.$errors.failWithoutHelp("Native Platform cannot be updated."); + } - }).future()(); } - private updatePlatformCore(platformData: IPlatformData, currentVersion: string, newVersion: string, canUpdate: boolean): IFuture { - return (() => { - let packageName = platformData.normalizedPlatformName.toLowerCase(); - this.removePlatforms([packageName]); - packageName = newVersion ? `${packageName}@${newVersion}` : packageName; - this.addPlatform(packageName).wait(); - this.$logger.out("Successfully updated to version ", newVersion); - }).future()(); + private async updatePlatformCore(platformData: IPlatformData, currentVersion: string, newVersion: string, canUpdate: boolean): Promise { + let packageName = platformData.normalizedPlatformName.toLowerCase(); + this.removePlatforms([packageName]); + packageName = newVersion ? `${packageName}@${newVersion}` : packageName; + await this.addPlatform(packageName); + this.$logger.out("Successfully updated to version ", newVersion); } private applyBaseConfigOption(platformData: IPlatformData): void { @@ -762,22 +725,21 @@ export class PlatformService implements IPlatformService { } } - public readFile(device: Mobile.IDevice, deviceFilePath: string): IFuture { - return (() => { - temp.track(); - let uniqueFilePath = temp.path({ suffix: ".tmp" }); - try { - device.fileSystem.getFile(deviceFilePath, uniqueFilePath).wait(); - } catch (e) { - return null; - } - if (this.$fs.exists(uniqueFilePath)) { - let text = this.$fs.readText(uniqueFilePath); - shell.rm(uniqueFilePath); - return text; - } + public async readFile(device: Mobile.IDevice, deviceFilePath: string): Promise { + temp.track(); + let uniqueFilePath = temp.path({ suffix: ".tmp" }); + try { + await device.fileSystem.getFile(deviceFilePath, uniqueFilePath); + } catch (e) { return null; - }).future()(); + } + if (this.$fs.exists(uniqueFilePath)) { + let text = this.$fs.readText(uniqueFilePath); + shell.rm(uniqueFilePath); + return text; + } + return null; } } + $injector.register("platformService", PlatformService); diff --git a/lib/services/plugin-variables-service.ts b/lib/services/plugin-variables-service.ts index c9fabc2620..147a0cb1c6 100644 --- a/lib/services/plugin-variables-service.ts +++ b/lib/services/plugin-variables-service.ts @@ -14,20 +14,18 @@ export class PluginVariablesService implements IPluginVariablesService { return `${pluginName}-${PluginVariablesService.PLUGIN_VARIABLES_KEY}`; } - public savePluginVariablesInProjectFile(pluginData: IPluginData): IFuture { - return (() => { - let values = Object.create(null); - this.executeForAllPluginVariables(pluginData, (pluginVariableData: IPluginVariableData) => { - let pluginVariableValue = this.getPluginVariableValue(pluginVariableData).wait(); - this.ensurePluginVariableValue(pluginVariableValue, `Unable to find value for ${pluginVariableData.name} plugin variable from ${pluginData.name} plugin. Ensure the --var option is specified or the plugin variable has default value.`); - values[pluginVariableData.name] = pluginVariableValue; - }); - - if (!_.isEmpty(values)) { - this.$projectDataService.initialize(this.$projectData.projectDir); - this.$projectDataService.setValue(this.getPluginVariablePropertyName(pluginData.name), values); - } - }).future()(); + public async savePluginVariablesInProjectFile(pluginData: IPluginData): Promise { + let values = Object.create(null); + await this.executeForAllPluginVariables(pluginData, async (pluginVariableData: IPluginVariableData) => { + let pluginVariableValue = await this.getPluginVariableValue(pluginVariableData); + this.ensurePluginVariableValue(pluginVariableValue, `Unable to find value for ${pluginVariableData.name} plugin variable from ${pluginData.name} plugin. Ensure the --var option is specified or the plugin variable has default value.`); + values[pluginVariableData.name] = pluginVariableValue; + }); + + if (!_.isEmpty(values)) { + this.$projectDataService.initialize(this.$projectData.projectDir); + this.$projectDataService.setValue(this.getPluginVariablePropertyName(pluginData.name), values); + } } public removePluginVariablesFromProjectFile(pluginName: string): void { @@ -35,15 +33,14 @@ export class PluginVariablesService implements IPluginVariablesService { this.$projectDataService.removeProperty(this.getPluginVariablePropertyName(pluginName)); } - public interpolatePluginVariables(pluginData: IPluginData, pluginConfigurationFilePath: string): IFuture { - return (() => { - let pluginConfigurationFileContent = this.$fs.readText(pluginConfigurationFilePath); - this.executeForAllPluginVariables(pluginData, (pluginVariableData: IPluginVariableData) => { - this.ensurePluginVariableValue(pluginVariableData.value, `Unable to find the value for ${pluginVariableData.name} plugin variable into project package.json file. Verify that your package.json file is correct and try again.`); - pluginConfigurationFileContent = this.interpolateCore(pluginVariableData.name, pluginVariableData.value, pluginConfigurationFileContent); - }); - this.$fs.writeFile(pluginConfigurationFilePath, pluginConfigurationFileContent); - }).future()(); + public async interpolatePluginVariables(pluginData: IPluginData, pluginConfigurationFilePath: string): Promise { + let pluginConfigurationFileContent = this.$fs.readText(pluginConfigurationFilePath); + await this.executeForAllPluginVariables(pluginData, async (pluginVariableData: IPluginVariableData) => { + this.ensurePluginVariableValue(pluginVariableData.value, `Unable to find the value for ${pluginVariableData.name} plugin variable into project package.json file. Verify that your package.json file is correct and try again.`); + pluginConfigurationFileContent = this.interpolateCore(pluginVariableData.name, pluginVariableData.value, pluginConfigurationFileContent); + }); + + this.$fs.writeFile(pluginConfigurationFilePath, pluginConfigurationFileContent); } public interpolateAppIdentifier(pluginConfigurationFilePath: string): void { @@ -52,11 +49,9 @@ export class PluginVariablesService implements IPluginVariablesService { this.$fs.writeFile(pluginConfigurationFilePath, newContent); } - public interpolate(pluginData: IPluginData, pluginConfigurationFilePath: string): IFuture { - return (() => { - this.interpolatePluginVariables(pluginData, pluginConfigurationFilePath).wait(); - this.interpolateAppIdentifier(pluginConfigurationFilePath); - }).future()(); + public async interpolate(pluginData: IPluginData, pluginConfigurationFilePath: string): Promise { + await this.interpolatePluginVariables(pluginData, pluginConfigurationFilePath); + this.interpolateAppIdentifier(pluginConfigurationFilePath); } private interpolateCore(name: string, value: string, content: string): string { @@ -69,34 +64,32 @@ export class PluginVariablesService implements IPluginVariablesService { } } - private getPluginVariableValue(pluginVariableData: IPluginVariableData): IFuture { - return (() => { - let pluginVariableName = pluginVariableData.name; - let value = this.$pluginVariablesHelper.getPluginVariableFromVarOption(pluginVariableName); - if (value) { - value = value[pluginVariableName]; - } else { - value = pluginVariableData.defaultValue; - if (!value && helpers.isInteractive()) { - let promptSchema = { - name: pluginVariableName, - type: "input", - message: `Enter value for ${pluginVariableName} variable:`, - validate: (val: string) => !!val ? true : 'Please enter a value!' - }; - let promptData = this.$prompter.get([promptSchema]).wait(); - value = promptData[pluginVariableName]; - } + private async getPluginVariableValue(pluginVariableData: IPluginVariableData): Promise { + let pluginVariableName = pluginVariableData.name; + let value = this.$pluginVariablesHelper.getPluginVariableFromVarOption(pluginVariableName); + if (value) { + value = value[pluginVariableName]; + } else { + value = pluginVariableData.defaultValue; + if (!value && helpers.isInteractive()) { + let promptSchema = { + name: pluginVariableName, + type: "input", + message: `Enter value for ${pluginVariableName} variable:`, + validate: (val: string) => !!val ? true : 'Please enter a value!' + }; + let promptData = await this.$prompter.get([promptSchema]); + value = promptData[pluginVariableName]; } + } - return value; - }).future()(); + return value; } - private executeForAllPluginVariables(pluginData: IPluginData, action: (pluginVariableData: IPluginVariableData) => void): void { + private async executeForAllPluginVariables(pluginData: IPluginData, action: (pluginVariableData: IPluginVariableData) => Promise): Promise { let pluginVariables = pluginData.pluginVariables; let pluginVariablesNames = _.keys(pluginVariables); - _.each(pluginVariablesNames, pluginVariableName => action(this.createPluginVariableData(pluginData, pluginVariableName))); + await Promise.all(_.map(pluginVariablesNames, pluginVariableName => action(this.createPluginVariableData(pluginData, pluginVariableName)))); } private createPluginVariableData(pluginData: IPluginData, pluginVariableName: string): IPluginVariableData { @@ -111,4 +104,5 @@ export class PluginVariablesService implements IPluginVariablesService { return variableData; } } + $injector.register("pluginVariablesService", PluginVariablesService); diff --git a/lib/services/plugins-service.ts b/lib/services/plugins-service.ts index 4e2aa35ec6..39ae55395f 100644 --- a/lib/services/plugins-service.ts +++ b/lib/services/plugins-service.ts @@ -27,100 +27,94 @@ export class PluginsService implements IPluginsService { constructor(private $npm: INodePackageManager, private $fs: IFileSystem, - private $childProcess: IChildProcess, private $options: IOptions, private $logger: ILogger, private $errors: IErrors, private $injector: IInjector) { } - public add(plugin: string): IFuture { - return (() => { - this.ensure().wait(); - const possiblePackageName = path.resolve(plugin); - if (possiblePackageName.indexOf(".tgz") !== -1 && this.$fs.exists(possiblePackageName)) { - plugin = possiblePackageName; - } - let name = this.$npm.install(plugin, this.$projectData.projectDir, PluginsService.NPM_CONFIG).wait()[0]; - let pathToRealNpmPackageJson = path.join(this.$projectData.projectDir, "node_modules", name, "package.json"); - let realNpmPackageJson = this.$fs.readJson(pathToRealNpmPackageJson); - - if (realNpmPackageJson.nativescript) { - let pluginData = this.convertToPluginData(realNpmPackageJson); - - // Validate - let action = (pluginDestinationPath: string, platform: string, platformData: IPlatformData): void => { - this.isPluginDataValidForPlatform(pluginData, platform); - }; - this.executeForAllInstalledPlatforms(action); - - try { - this.$pluginVariablesService.savePluginVariablesInProjectFile(pluginData).wait(); - } catch (err) { - // Revert package.json - this.$projectDataService.initialize(this.$projectData.projectDir); - this.$projectDataService.removeProperty(this.$pluginVariablesService.getPluginVariablePropertyName(pluginData.name)); - this.$npm.uninstall(plugin, PluginsService.NPM_CONFIG, this.$projectData.projectDir).wait(); - - throw err; - } - - this.$logger.out(`Successfully installed plugin ${realNpmPackageJson.name}.`); - } else { - this.$npm.uninstall(realNpmPackageJson.name, { save: true }, this.$projectData.projectDir); - this.$errors.failWithoutHelp(`${plugin} is not a valid NativeScript plugin. Verify that the plugin package.json file contains a nativescript key and try again.`); + public async add(plugin: string): Promise { + await this.ensure(); + const possiblePackageName = path.resolve(plugin); + if (possiblePackageName.indexOf(".tgz") !== -1 && this.$fs.exists(possiblePackageName)) { + plugin = possiblePackageName; + } + let name = (await this.$npm.install(plugin, this.$projectData.projectDir, PluginsService.NPM_CONFIG))[0]; + let pathToRealNpmPackageJson = path.join(this.$projectData.projectDir, "node_modules", name, "package.json"); + let realNpmPackageJson = this.$fs.readJson(pathToRealNpmPackageJson); + + if (realNpmPackageJson.nativescript) { + let pluginData = this.convertToPluginData(realNpmPackageJson); + + // Validate + let action = async (pluginDestinationPath: string, platform: string, platformData: IPlatformData): Promise => { + this.isPluginDataValidForPlatform(pluginData, platform); + }; + + this.executeForAllInstalledPlatforms(action); + + try { + await this.$pluginVariablesService.savePluginVariablesInProjectFile(pluginData); + } catch (err) { + // Revert package.json + this.$projectDataService.initialize(this.$projectData.projectDir); + this.$projectDataService.removeProperty(this.$pluginVariablesService.getPluginVariablePropertyName(pluginData.name)); + await this.$npm.uninstall(plugin, PluginsService.NPM_CONFIG, this.$projectData.projectDir); + + throw err; } - }).future()(); + this.$logger.out(`Successfully installed plugin ${realNpmPackageJson.name}.`); + } else { + this.$npm.uninstall(realNpmPackageJson.name, { save: true }, this.$projectData.projectDir); + this.$errors.failWithoutHelp(`${plugin} is not a valid NativeScript plugin. Verify that the plugin package.json file contains a nativescript key and try again.`); + } } - public remove(pluginName: string): IFuture { - return (() => { - let removePluginNativeCodeAction = (modulesDestinationPath: string, platform: string, platformData: IPlatformData): void => { - let pluginData = this.convertToPluginData(this.getNodeModuleData(pluginName)); + public async remove(pluginName: string): Promise { + let removePluginNativeCodeAction = async (modulesDestinationPath: string, platform: string, platformData: IPlatformData): Promise => { + let pluginData = this.convertToPluginData(this.getNodeModuleData(pluginName)); - platformData.platformProjectService.removePluginNativeCode(pluginData); - }; + await platformData.platformProjectService.removePluginNativeCode(pluginData); + }; - this.$pluginVariablesService.removePluginVariablesFromProjectFile(pluginName.toLowerCase()); - this.executeForAllInstalledPlatforms(removePluginNativeCodeAction); + this.$pluginVariablesService.removePluginVariablesFromProjectFile(pluginName.toLowerCase()); + this.executeForAllInstalledPlatforms(removePluginNativeCodeAction); - this.executeNpmCommand(PluginsService.UNINSTALL_COMMAND_NAME, pluginName).wait(); + await this.executeNpmCommand(PluginsService.UNINSTALL_COMMAND_NAME, pluginName); - let showMessage = true; - let action = (modulesDestinationPath: string, platform: string, platformData: IPlatformData): void => { - shelljs.rm("-rf", path.join(modulesDestinationPath, pluginName)); + let showMessage = true; + let action = async (modulesDestinationPath: string, platform: string, platformData: IPlatformData): Promise => { + shelljs.rm("-rf", path.join(modulesDestinationPath, pluginName)); - this.$logger.out(`Successfully removed plugin ${pluginName} for ${platform}.`); - showMessage = false; - }; - this.executeForAllInstalledPlatforms(action); + this.$logger.out(`Successfully removed plugin ${pluginName} for ${platform}.`); + showMessage = false; + }; - if (showMessage) { - this.$logger.out(`Succsessfully removed plugin ${pluginName}`); - } - }).future()(); + this.executeForAllInstalledPlatforms(action); + + if (showMessage) { + this.$logger.out(`Succsessfully removed plugin ${pluginName}`); + } } - public getAvailable(filter: string[]): IFuture> { + public getAvailable(filter: string[]): Promise> { let silent: boolean = true; return this.$npm.search(filter, { "silent": silent }); } - public prepare(dependencyData: IDependencyData, platform: string): IFuture { - return (() => { - platform = platform.toLowerCase(); - let platformData = this.$platformsData.getPlatformData(platform); - let pluginData = this.convertToPluginData(dependencyData); + public async prepare(dependencyData: IDependencyData, platform: string): Promise { + platform = platform.toLowerCase(); + let platformData = this.$platformsData.getPlatformData(platform); + let pluginData = this.convertToPluginData(dependencyData); - let appFolderExists = this.$fs.exists(path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME)); - if (appFolderExists) { - this.preparePluginScripts(pluginData, platform); - this.preparePluginNativeCode(pluginData, platform); + let appFolderExists = this.$fs.exists(path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME)); + if (appFolderExists) { + this.preparePluginScripts(pluginData, platform); + this.preparePluginNativeCode(pluginData, platform); - // Show message - this.$logger.out(`Successfully prepared plugin ${pluginData.name} for ${platform}.`); - } - }).future()(); + // Show message + this.$logger.out(`Successfully prepared plugin ${pluginData.name} for ${platform}.`); + } } private preparePluginScripts(pluginData: IPluginData, platform: string): void { @@ -140,41 +134,37 @@ export class PluginsService implements IPluginsService { this.$projectFilesManager.processPlatformSpecificFiles(pluginScriptsDestinationPath, platform); } - private preparePluginNativeCode(pluginData: IPluginData, platform: string): void { + private async preparePluginNativeCode(pluginData: IPluginData, platform: string): Promise { let platformData = this.$platformsData.getPlatformData(platform); pluginData.pluginPlatformsFolderPath = (_platform: string) => path.join(pluginData.fullPath, "platforms", _platform); - platformData.platformProjectService.preparePluginNativeCode(pluginData).wait(); + await platformData.platformProjectService.preparePluginNativeCode(pluginData); } - public ensureAllDependenciesAreInstalled(): IFuture { - return (() => { - let installedDependencies = this.$fs.exists(this.nodeModulesPath) ? this.$fs.readDirectory(this.nodeModulesPath) : []; - // Scoped dependencies are not on the root of node_modules, - // so we have to list the contents of all directories, starting with @ - // and add them to installed dependencies, so we can apply correct comparison against package.json's dependencies. - _(installedDependencies) - .filter(dependencyName => _.startsWith(dependencyName, "@")) - .each(scopedDependencyDir => { - let contents = this.$fs.readDirectory(path.join(this.nodeModulesPath, scopedDependencyDir)); - installedDependencies = installedDependencies.concat(contents.map(dependencyName => `${scopedDependencyDir}/${dependencyName}`)); - }); - - let packageJsonContent = this.$fs.readJson(this.getPackageJsonFilePath()); - let allDependencies = _.keys(packageJsonContent.dependencies).concat(_.keys(packageJsonContent.devDependencies)); - let notInstalledDependencies = _.difference(allDependencies, installedDependencies); - if (this.$options.force || notInstalledDependencies.length) { - this.$logger.trace("Npm install will be called from CLI. Force option is: ", this.$options.force, " Not installed dependencies are: ", notInstalledDependencies); - this.$npm.install(this.$projectData.projectDir, this.$projectData.projectDir, { "ignore-scripts": this.$options.ignoreScripts }).wait(); - } - }).future()(); + public async ensureAllDependenciesAreInstalled(): Promise { + let installedDependencies = this.$fs.exists(this.nodeModulesPath) ? this.$fs.readDirectory(this.nodeModulesPath) : []; + // Scoped dependencies are not on the root of node_modules, + // so we have to list the contents of all directories, starting with @ + // and add them to installed dependencies, so we can apply correct comparison against package.json's dependencies. + _(installedDependencies) + .filter(dependencyName => _.startsWith(dependencyName, "@")) + .each(scopedDependencyDir => { + let contents = this.$fs.readDirectory(path.join(this.nodeModulesPath, scopedDependencyDir)); + installedDependencies = installedDependencies.concat(contents.map(dependencyName => `${scopedDependencyDir}/${dependencyName}`)); + }); + + let packageJsonContent = this.$fs.readJson(this.getPackageJsonFilePath()); + let allDependencies = _.keys(packageJsonContent.dependencies).concat(_.keys(packageJsonContent.devDependencies)); + let notInstalledDependencies = _.difference(allDependencies, installedDependencies); + if (this.$options.force || notInstalledDependencies.length) { + this.$logger.trace("Npm install will be called from CLI. Force option is: ", this.$options.force, " Not installed dependencies are: ", notInstalledDependencies); + await this.$npm.install(this.$projectData.projectDir, this.$projectData.projectDir, { "ignore-scripts": this.$options.ignoreScripts }); + } } - public getAllInstalledPlugins(): IFuture { - return (() => { - let nodeModules = this.getAllInstalledModules().wait().map(nodeModuleData => this.convertToPluginData(nodeModuleData)); - return _.filter(nodeModules, nodeModuleData => nodeModuleData && nodeModuleData.isPlugin); - }).future()(); + public async getAllInstalledPlugins(): Promise { + let nodeModules = (await this.getAllInstalledModules()).map(nodeModuleData => this.convertToPluginData(nodeModuleData)); + return _.filter(nodeModules, nodeModuleData => nodeModuleData && nodeModuleData.isPlugin); } public getDependenciesFromPackageJson(): IPackageJsonDepedenciesResult { @@ -245,49 +235,42 @@ export class PluginsService implements IPluginsService { return pluginData; } - private ensure(): IFuture { - return (() => { - this.ensureAllDependenciesAreInstalled().wait(); - this.$fs.ensureDirectoryExists(this.nodeModulesPath); - }).future()(); + private async ensure(): Promise { + await this.ensureAllDependenciesAreInstalled(); + this.$fs.ensureDirectoryExists(this.nodeModulesPath); } - private getAllInstalledModules(): IFuture { - return (() => { - this.ensure().wait(); + private async getAllInstalledModules(): Promise { + await this.ensure(); - let nodeModules = this.getDependencies(); - return _.map(nodeModules, nodeModuleName => this.getNodeModuleData(nodeModuleName)); - }).future()(); + let nodeModules = this.getDependencies(); + return _.map(nodeModules, nodeModuleName => this.getNodeModuleData(nodeModuleName)); } - private executeNpmCommand(npmCommandName: string, npmCommandArguments: string): IFuture { - return (() => { - - if (npmCommandName === PluginsService.INSTALL_COMMAND_NAME) { - this.$npm.install(npmCommandArguments, this.$projectData.projectDir, PluginsService.NPM_CONFIG).wait(); - } else if (npmCommandName === PluginsService.UNINSTALL_COMMAND_NAME) { - this.$npm.uninstall(npmCommandArguments, PluginsService.NPM_CONFIG, this.$projectData.projectDir).wait(); - } + private async executeNpmCommand(npmCommandName: string, npmCommandArguments: string): Promise { + if (npmCommandName === PluginsService.INSTALL_COMMAND_NAME) { + await this.$npm.install(npmCommandArguments, this.$projectData.projectDir, PluginsService.NPM_CONFIG); + } else if (npmCommandName === PluginsService.UNINSTALL_COMMAND_NAME) { + await this.$npm.uninstall(npmCommandArguments, PluginsService.NPM_CONFIG, this.$projectData.projectDir); + } - return this.parseNpmCommandResult(npmCommandArguments); - }).future()(); + return this.parseNpmCommandResult(npmCommandArguments); } private parseNpmCommandResult(npmCommandResult: string): string { return npmCommandResult.split("@")[0]; // returns plugin name } - private executeForAllInstalledPlatforms(action: (_pluginDestinationPath: string, pl: string, _platformData: IPlatformData) => void): void { + private async executeForAllInstalledPlatforms(action: (_pluginDestinationPath: string, pl: string, _platformData: IPlatformData) => Promise): Promise { let availablePlatforms = _.keys(this.$platformsData.availablePlatforms); - _.each(availablePlatforms, platform => { + for (let platform of availablePlatforms) { let isPlatformInstalled = this.$fs.exists(path.join(this.$projectData.platformsDir, platform.toLowerCase())); if (isPlatformInstalled) { let platformData = this.$platformsData.getPlatformData(platform.toLowerCase()); let pluginDestinationPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, "tns_modules"); - action(pluginDestinationPath, platform.toLowerCase(), platformData); + await action(pluginDestinationPath, platform.toLowerCase(), platformData); } - }); + }; } private getInstalledFrameworkVersion(platform: string): string { @@ -316,4 +299,5 @@ export class PluginsService implements IPluginsService { return isValid; } } + $injector.register("pluginsService", PluginsService); diff --git a/lib/services/project-changes-service.ts b/lib/services/project-changes-service.ts index fdc4120102..f47705074c 100644 --- a/lib/services/project-changes-service.ts +++ b/lib/services/project-changes-service.ts @@ -1,5 +1,5 @@ import * as path from "path"; -import {NODE_MODULES_FOLDER_NAME} from "../constants"; +import { NODE_MODULES_FOLDER_NAME } from "../constants"; const prepareInfoFileName = ".nsprepareinfo"; @@ -14,16 +14,16 @@ class ProjectChangesInfo implements IProjectChangesInfo { public get hasChanges(): boolean { return this.packageChanged || - this.appFilesChanged || - this.appResourcesChanged || - this.modulesChanged || - this.configChanged; + this.appFilesChanged || + this.appResourcesChanged || + this.modulesChanged || + this.configChanged; } public get changesRequireBuild(): boolean { return this.packageChanged || - this.appResourcesChanged || - this.nativeChanged; + this.appResourcesChanged || + this.nativeChanged; } } @@ -184,7 +184,7 @@ export class ProjectChangesService implements IProjectChangesService { } if (changed) { if (processFunc) { - this._newFiles ++; + this._newFiles++; let filePathRelative = path.relative(this.$projectData.projectDir, filePath); if (processFunc.call(this, filePathRelative)) { return true; @@ -212,7 +212,7 @@ export class ProjectChangesService implements IProjectChangesService { } if (_.startsWith(file, NODE_MODULES_FOLDER_NAME)) { let filePath = file; - while(filePath !== NODE_MODULES_FOLDER_NAME) { + while (filePath !== NODE_MODULES_FOLDER_NAME) { filePath = path.dirname(filePath); let fullFilePath = path.join(projectDir, path.join(filePath, "package.json")); if (this.$fs.exists(fullFilePath)) { diff --git a/lib/services/project-data-service.ts b/lib/services/project-data-service.ts index c87c856b95..261b134f4e 100644 --- a/lib/services/project-data-service.ts +++ b/lib/services/project-data-service.ts @@ -9,9 +9,7 @@ export class ProjectDataService implements IProjectDataService { private projectFileIndent: string; constructor(private $fs: IFileSystem, - private $staticConfig: IStaticConfig, - private $errors: IErrors, - private $logger: ILogger) { + private $staticConfig: IStaticConfig) { } public initialize(projectDir: string): void { diff --git a/lib/services/project-name-service.ts b/lib/services/project-name-service.ts index dadae82265..3b685d9249 100644 --- a/lib/services/project-name-service.ts +++ b/lib/services/project-name-service.ts @@ -6,36 +6,34 @@ export class ProjectNameService implements IProjectNameService { private $logger: ILogger, private $prompter: IPrompter) { } - public ensureValidName(projectName: string, validateOptions?: { force: boolean }): IFuture { - return (() => { - if (validateOptions && validateOptions.force) { - return projectName; - } - - if (!this.$projectNameValidator.validate(projectName)) { - return this.promptForNewName("The project name is invalid.", projectName, validateOptions).wait(); - } + public async ensureValidName(projectName: string, validateOptions?: { force: boolean }): Promise { + if (validateOptions && validateOptions.force) { + return projectName; + } - let userCanInteract = isInteractive(); + if (!this.$projectNameValidator.validate(projectName)) { + return await this.promptForNewName("The project name is invalid.", projectName, validateOptions); + } - if (!this.checkIfNameStartsWithLetter(projectName)) { - if (!userCanInteract) { - this.$errors.fail("The project name does not start with letter and will fail to build for Android. If You want to create project with this name add --force to the create command."); - } + let userCanInteract = isInteractive(); - return this.promptForNewName("The project name does not start with letter and will fail to build for Android.", projectName, validateOptions).wait(); + if (!this.checkIfNameStartsWithLetter(projectName)) { + if (!userCanInteract) { + this.$errors.fail("The project name does not start with letter and will fail to build for Android. If You want to create project with this name add --force to the create command."); } - if (projectName.toUpperCase() === "APP") { - if (!userCanInteract) { - this.$errors.fail("You cannot build applications named 'app' in Xcode. Consider creating a project with different name. If You want to create project with this name add --force to the create command."); - } + return await this.promptForNewName("The project name does not start with letter and will fail to build for Android.", projectName, validateOptions); + } - return this.promptForNewName("You cannot build applications named 'app' in Xcode. Consider creating a project with different name.", projectName, validateOptions).wait(); + if (projectName.toUpperCase() === "APP") { + if (!userCanInteract) { + this.$errors.fail("You cannot build applications named 'app' in Xcode. Consider creating a project with different name. If You want to create project with this name add --force to the create command."); } - return projectName; - }).future()(); + return await this.promptForNewName("You cannot build applications named 'app' in Xcode. Consider creating a project with different name.", projectName, validateOptions); + } + + return projectName; } private checkIfNameStartsWithLetter(projectName: string): boolean { @@ -43,23 +41,19 @@ export class ProjectNameService implements IProjectNameService { return startsWithLetterExpression.test(projectName); } - private promptForNewName(warningMessage: string, projectName: string, validateOptions?: { force: boolean }): IFuture { - return (() => { - if (this.promptForForceNameConfirm(warningMessage).wait()) { - return projectName; - } + private async promptForNewName(warningMessage: string, projectName: string, validateOptions?: { force: boolean }): Promise { + if (await this.promptForForceNameConfirm(warningMessage)) { + return projectName; + } - let newProjectName = this.$prompter.getString("Enter the new project name:").wait(); - return this.ensureValidName(newProjectName, validateOptions).wait(); - }).future()(); + let newProjectName = await this.$prompter.getString("Enter the new project name:"); + return await this.ensureValidName(newProjectName, validateOptions); } - private promptForForceNameConfirm(warningMessage: string): IFuture { - return (() => { - this.$logger.warn(warningMessage); + private async promptForForceNameConfirm(warningMessage: string): Promise { + this.$logger.warn(warningMessage); - return this.$prompter.confirm("Do you want to create the project with this name?").wait(); - }).future()(); + return await this.$prompter.confirm("Do you want to create the project with this name?"); } } diff --git a/lib/services/project-service.ts b/lib/services/project-service.ts index b4f537f510..5a9fb74548 100644 --- a/lib/services/project-service.ts +++ b/lib/services/project-service.ts @@ -15,89 +15,86 @@ export class ProjectService implements IProjectService { private $projectTemplatesService: IProjectTemplatesService, private $options: IOptions) { } - public createProject(projectName: string, selectedTemplate?: string): IFuture { - return (() => { - if (!projectName) { - this.$errors.fail("You must specify when creating a new project."); - } + public async createProject(projectName: string, selectedTemplate?: string): Promise { + if (!projectName) { + this.$errors.fail("You must specify when creating a new project."); + } - projectName = this.$projectNameService.ensureValidName(projectName, { force: this.$options.force }).wait(); + projectName = await this.$projectNameService.ensureValidName(projectName, { force: this.$options.force }); - let projectId = this.$options.appid || this.$projectHelper.generateDefaultAppId(projectName, constants.DEFAULT_APP_IDENTIFIER_PREFIX); + let projectId = this.$options.appid || this.$projectHelper.generateDefaultAppId(projectName, constants.DEFAULT_APP_IDENTIFIER_PREFIX); - let projectDir = path.join(path.resolve(this.$options.path || "."), projectName); - this.$fs.createDirectory(projectDir); - if (this.$fs.exists(projectDir) && !this.$fs.isEmptyDir(projectDir)) { - this.$errors.fail("Path already exists and is not empty %s", projectDir); - } + let projectDir = path.join(path.resolve(this.$options.path || "."), projectName); + this.$fs.createDirectory(projectDir); + if (this.$fs.exists(projectDir) && !this.$fs.isEmptyDir(projectDir)) { + this.$errors.fail("Path already exists and is not empty %s", projectDir); + } - this.createPackageJson(projectDir, projectId); + this.createPackageJson(projectDir, projectId); - let customAppPath = this.getCustomAppPath(); - if (customAppPath) { - customAppPath = path.resolve(customAppPath); - if (!this.$fs.exists(customAppPath)) { - this.$errors.failWithoutHelp(`The specified path "${customAppPath}" doesn't exist. Check that you specified the path correctly and try again.`); - } - - let customAppContents = this.$fs.enumerateFilesInDirectorySync(customAppPath); - if (customAppContents.length === 0) { - this.$errors.failWithoutHelp(`The specified path "${customAppPath}" is empty directory.`); - } + let customAppPath = this.getCustomAppPath(); + if (customAppPath) { + customAppPath = path.resolve(customAppPath); + if (!this.$fs.exists(customAppPath)) { + this.$errors.failWithoutHelp(`The specified path "${customAppPath}" doesn't exist. Check that you specified the path correctly and try again.`); } - this.$logger.trace("Creating a new NativeScript project with name %s and id %s at location %s", projectName, projectId, projectDir); - - let projectAppDirectory = path.join(projectDir, constants.APP_FOLDER_NAME); - let appPath: string = null; - if (customAppPath) { - this.$logger.trace("Using custom app from %s", customAppPath); - - // Make sure that the source app/ is not a direct ancestor of a target app/ - let relativePathFromSourceToTarget = path.relative(customAppPath, projectAppDirectory); - // path.relative returns second argument if the paths are located on different disks - // so in this case we don't need to make the check for direct ancestor - if (relativePathFromSourceToTarget !== projectAppDirectory) { - let doesRelativePathGoUpAtLeastOneDir = relativePathFromSourceToTarget.split(path.sep)[0] === ".."; - if (!doesRelativePathGoUpAtLeastOneDir) { - this.$errors.fail("Project dir %s must not be created at/inside the template used to create the project %s.", projectDir, customAppPath); - } - } - this.$logger.trace("Copying custom app into %s", projectAppDirectory); - appPath = customAppPath; - } else { - let templatePath = this.$projectTemplatesService.prepareTemplate(selectedTemplate, projectDir).wait(); - this.$logger.trace(`Copying application from '${templatePath}' into '${projectAppDirectory}'.`); - let templatePackageJson = this.$fs.readJson(path.join(templatePath, "package.json")); - selectedTemplate = templatePackageJson.name; - appPath = templatePath; + let customAppContents = this.$fs.enumerateFilesInDirectorySync(customAppPath); + if (customAppContents.length === 0) { + this.$errors.failWithoutHelp(`The specified path "${customAppPath}" is empty directory.`); } + } + + this.$logger.trace("Creating a new NativeScript project with name %s and id %s at location %s", projectName, projectId, projectDir); - try { - //TODO: plamen5kov: move copy of template and npm uninstall in prepareTemplate logic - this.createProjectCore(projectDir, appPath, projectId).wait(); - let templatePackageJsonData = this.getDataFromJson(appPath); - this.mergeProjectAndTemplateProperties(projectDir, templatePackageJsonData); //merging dependencies from template (dev && prod) - this.removeMergedDependencies(projectDir, templatePackageJsonData); - this.$npm.install(projectDir, projectDir, { "ignore-scripts": this.$options.ignoreScripts }).wait(); - selectedTemplate = selectedTemplate || ""; - let templateName = (constants.RESERVED_TEMPLATE_NAMES[selectedTemplate.toLowerCase()] || selectedTemplate/*user template*/) || constants.RESERVED_TEMPLATE_NAMES["default"]; - this.$npm.uninstall(selectedTemplate, { save: true }, projectDir).wait(); - - // TODO: plamen5kov: remove later (put only so tests pass (need to fix tests)) - this.$logger.trace(`Using NativeScript verified template: ${templateName} with version undefined.`); - } catch (err) { - this.$fs.deleteDirectory(projectDir); - throw err; + let projectAppDirectory = path.join(projectDir, constants.APP_FOLDER_NAME); + let appPath: string = null; + if (customAppPath) { + this.$logger.trace("Using custom app from %s", customAppPath); + + // Make sure that the source app/ is not a direct ancestor of a target app/ + let relativePathFromSourceToTarget = path.relative(customAppPath, projectAppDirectory); + // path.relative returns second argument if the paths are located on different disks + // so in this case we don't need to make the check for direct ancestor + if (relativePathFromSourceToTarget !== projectAppDirectory) { + let doesRelativePathGoUpAtLeastOneDir = relativePathFromSourceToTarget.split(path.sep)[0] === ".."; + if (!doesRelativePathGoUpAtLeastOneDir) { + this.$errors.fail("Project dir %s must not be created at/inside the template used to create the project %s.", projectDir, customAppPath); + } } - this.$logger.printMarkdown("Project `%s` was successfully created.", projectName); + this.$logger.trace("Copying custom app into %s", projectAppDirectory); + appPath = customAppPath; + } else { + let templatePath = await this.$projectTemplatesService.prepareTemplate(selectedTemplate, projectDir); + this.$logger.trace(`Copying application from '${templatePath}' into '${projectAppDirectory}'.`); + let templatePackageJson = this.$fs.readJson(path.join(templatePath, "package.json")); + selectedTemplate = templatePackageJson.name; + appPath = templatePath; + } - }).future()(); + try { + //TODO: plamen5kov: move copy of template and npm uninstall in prepareTemplate logic + await this.createProjectCore(projectDir, appPath, projectId); + let templatePackageJsonData = this.getDataFromJson(appPath); + this.mergeProjectAndTemplateProperties(projectDir, templatePackageJsonData); //merging dependencies from template (dev && prod) + this.removeMergedDependencies(projectDir, templatePackageJsonData); + await this.$npm.install(projectDir, projectDir, { "ignore-scripts": this.$options.ignoreScripts }); + selectedTemplate = selectedTemplate || ""; + let templateName = (constants.RESERVED_TEMPLATE_NAMES[selectedTemplate.toLowerCase()] || selectedTemplate/*user template*/) || constants.RESERVED_TEMPLATE_NAMES["default"]; + await this.$npm.uninstall(selectedTemplate, { save: true }, projectDir); + + // TODO: plamen5kov: remove later (put only so tests pass (need to fix tests)) + this.$logger.trace(`Using NativeScript verified template: ${templateName} with version undefined.`); + } catch (err) { + this.$fs.deleteDirectory(projectDir); + throw err; + } + this.$logger.printMarkdown("Project `%s` was successfully created.", projectName); } private getDataFromJson(templatePath: string): any { let templatePackageJsonPath = path.join(templatePath, constants.PACKAGE_JSON_FILE_NAME); - if(this.$fs.exists(templatePackageJsonPath)) { + if (this.$fs.exists(templatePackageJsonPath)) { let templatePackageJsonData = this.$fs.readJson(templatePackageJsonPath); return templatePackageJsonData; } else { @@ -106,10 +103,10 @@ export class ProjectService implements IProjectService { return null; } - private removeMergedDependencies(projectDir: string, templatePackageJsonData: any) : void { + private removeMergedDependencies(projectDir: string, templatePackageJsonData: any): void { let extractedTemplatePackageJsonPath = path.join(projectDir, constants.APP_FOLDER_NAME, constants.PACKAGE_JSON_FILE_NAME); - for(let key in templatePackageJsonData) { - if(constants.PackageJsonKeysToKeep.indexOf(key) === -1) { + for (let key in templatePackageJsonData) { + if (constants.PackageJsonKeysToKeep.indexOf(key) === -1) { delete templatePackageJsonData[key]; } } @@ -119,11 +116,11 @@ export class ProjectService implements IProjectService { } private mergeProjectAndTemplateProperties(projectDir: string, templatePackageJsonData: any): void { - if(templatePackageJsonData) { + if (templatePackageJsonData) { let projectPackageJsonPath = path.join(projectDir, constants.PACKAGE_JSON_FILE_NAME); let projectPackageJsonData = this.$fs.readJson(projectPackageJsonPath); this.$logger.trace("Initial project package.json data: ", projectPackageJsonData); - if(projectPackageJsonData.dependencies || templatePackageJsonData.dependencies) { + if (projectPackageJsonData.dependencies || templatePackageJsonData.dependencies) { projectPackageJsonData.dependencies = this.mergeDependencies(projectPackageJsonData.dependencies, templatePackageJsonData.dependencies); } @@ -150,24 +147,22 @@ export class ProjectService implements IProjectService { return sortedDeps; } - private createProjectCore(projectDir: string, appSourcePath: string, projectId: string): IFuture { - return (() => { - this.$fs.ensureDirectoryExists(projectDir); + private async createProjectCore(projectDir: string, appSourcePath: string, projectId: string): Promise { + this.$fs.ensureDirectoryExists(projectDir); - let appDestinationPath = path.join(projectDir, constants.APP_FOLDER_NAME); - this.$fs.createDirectory(appDestinationPath); + let appDestinationPath = path.join(projectDir, constants.APP_FOLDER_NAME); + this.$fs.createDirectory(appDestinationPath); - shelljs.cp('-R', path.join(appSourcePath, "*"), appDestinationPath); + shelljs.cp('-R', path.join(appSourcePath, "*"), appDestinationPath); - this.$fs.createDirectory(path.join(projectDir, "platforms")); + this.$fs.createDirectory(path.join(projectDir, "platforms")); - let tnsModulesVersion = this.$options.tnsModulesVersion; - let packageName = constants.TNS_CORE_MODULES_NAME; - if (tnsModulesVersion) { - packageName = `${packageName}@${tnsModulesVersion}`; - } - this.$npm.install(packageName, projectDir, { save: true, "save-exact": true }).wait(); - }).future()(); + let tnsModulesVersion = this.$options.tnsModulesVersion; + let packageName = constants.TNS_CORE_MODULES_NAME; + if (tnsModulesVersion) { + packageName = `${packageName}@${tnsModulesVersion}`; + } + await this.$npm.install(packageName, projectDir, { save: true, "save-exact": true }); } private createPackageJson(projectDir: string, projectId: string): void { diff --git a/lib/services/project-templates-service.ts b/lib/services/project-templates-service.ts index 99c0acaee0..5e6896a297 100644 --- a/lib/services/project-templates-service.ts +++ b/lib/services/project-templates-service.ts @@ -6,39 +6,36 @@ temp.track(); export class ProjectTemplatesService implements IProjectTemplatesService { public constructor(private $errors: IErrors, - private $fs: IFileSystem, - private $logger: ILogger, - private $npm: INodePackageManager, - private $npmInstallationManager: INpmInstallationManager) { } + private $fs: IFileSystem, + private $logger: ILogger, + private $npmInstallationManager: INpmInstallationManager) { } - public prepareTemplate(originalTemplateName: string, projectDir: string): IFuture { - return ((): string => { - let realTemplatePath: string; - if(originalTemplateName) { - // support @ syntax - let data = originalTemplateName.split("@"), - name = data[0], - version = data[1]; + public async prepareTemplate(originalTemplateName: string, projectDir: string): Promise { + let realTemplatePath: string; + if (originalTemplateName) { + // support @ syntax + let data = originalTemplateName.split("@"), + name = data[0], + version = data[1]; - if(constants.RESERVED_TEMPLATE_NAMES[name.toLowerCase()]) { - realTemplatePath = this.prepareNativeScriptTemplate(constants.RESERVED_TEMPLATE_NAMES[name.toLowerCase()], version, projectDir).wait(); - } else { - // Use the original template name, specified by user as it may be case-sensitive. - realTemplatePath = this.prepareNativeScriptTemplate(name, version, projectDir).wait(); - } + if (constants.RESERVED_TEMPLATE_NAMES[name.toLowerCase()]) { + realTemplatePath = await this.prepareNativeScriptTemplate(constants.RESERVED_TEMPLATE_NAMES[name.toLowerCase()], version, projectDir); } else { - realTemplatePath = this.prepareNativeScriptTemplate(constants.RESERVED_TEMPLATE_NAMES["default"], null/*version*/, projectDir).wait(); + // Use the original template name, specified by user as it may be case-sensitive. + realTemplatePath = await this.prepareNativeScriptTemplate(name, version, projectDir); } + } else { + realTemplatePath = await this.prepareNativeScriptTemplate(constants.RESERVED_TEMPLATE_NAMES["default"], null/*version*/, projectDir); + } - if(realTemplatePath) { - //this removes dependencies from templates so they are not copied to app folder - this.$fs.deleteDirectory(path.join(realTemplatePath, constants.NODE_MODULES_FOLDER_NAME)); - return realTemplatePath; - } + if (realTemplatePath) { + //this removes dependencies from templates so they are not copied to app folder + this.$fs.deleteDirectory(path.join(realTemplatePath, constants.NODE_MODULES_FOLDER_NAME)); + return realTemplatePath; + } - this.$errors.failWithoutHelp("Unable to find the template in temp directory. " + - `Please open an issue at https://github.com/NativeScript/nativescript-cli/issues and send the output of the same command executed with --log trace.`); - }).future()(); + this.$errors.failWithoutHelp("Unable to find the template in temp directory. " + + `Please open an issue at https://github.com/NativeScript/nativescript-cli/issues and send the output of the same command executed with --log trace.`); } /** @@ -49,9 +46,9 @@ export class ProjectTemplatesService implements IProjectTemplatesService { * @param {string} version The version of the template specified by user. * @return {string} Path to the directory where the template is installed. */ - private prepareNativeScriptTemplate(templateName: string, version?: string, projectDir?: string): IFuture { + private async prepareNativeScriptTemplate(templateName: string, version?: string, projectDir?: string): Promise { this.$logger.trace(`Using NativeScript verified template: ${templateName} with version ${version}.`); - return this.$npmInstallationManager.install(templateName, projectDir, {version: version, dependencyType: "save"}); + return this.$npmInstallationManager.install(templateName, projectDir, { version: version, dependencyType: "save" }); } } $injector.register("projectTemplatesService", ProjectTemplatesService); diff --git a/lib/services/test-execution-service.ts b/lib/services/test-execution-service.ts index 4fb01eea36..ccaa2dd132 100644 --- a/lib/services/test-execution-service.ts +++ b/lib/services/test-execution-service.ts @@ -1,8 +1,6 @@ import * as constants from "../constants"; import * as path from 'path'; -import Future = require('fibers/future'); import * as os from 'os'; -import * as fiberBootstrap from "../common/fiber-bootstrap"; interface IKarmaConfigOptions { debugBrk: boolean; @@ -20,7 +18,6 @@ class TestExecutionService implements ITestExecutionService { private $platformsData: IPlatformsData, private $liveSyncProvider: ILiveSyncProvider, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, - private $resources: IResourceLoader, private $httpClient: Server.IHttpClient, private $config: IConfiguration, private $logger: ILogger, @@ -36,58 +33,51 @@ class TestExecutionService implements ITestExecutionService { public platform: string; - public startTestRunner(platform: string): IFuture { - return (() => { - this.platform = platform; - this.$options.justlaunch = true; - let blockingOperationFuture = new Future(); - process.on('message', (launcherConfig: any) => { - fiberBootstrap.run(() => { - try { - let platformData = this.$platformsData.getPlatformData(platform.toLowerCase()); - let projectDir = this.$projectData.projectDir; - this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }).wait(); - let projectFilesPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); - - let configOptions: IKarmaConfigOptions = JSON.parse(launcherConfig); - this.$options.debugBrk = configOptions.debugBrk; - this.$options.debugTransport = configOptions.debugTransport; - let configJs = this.generateConfig(this.$options.port.toString(), configOptions); - this.$fs.writeFile(path.join(projectDir, TestExecutionService.CONFIG_FILE_NAME), configJs); - - let socketIoJsUrl = `http://localhost:${this.$options.port}/socket.io/socket.io.js`; - let socketIoJs = this.$httpClient.httpRequest(socketIoJsUrl).wait().body; - this.$fs.writeFile(path.join(projectDir, TestExecutionService.SOCKETIO_JS_FILE_NAME), socketIoJs); - - if (!this.$platformService.preparePlatform(platform).wait()) { - this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation."); - } - this.detourEntryPoint(projectFilesPath); - - this.liveSyncProject(platform); - - if (this.$options.debugBrk) { - this.$logger.info('Starting debugger...'); - let debugService: IDebugService = this.$injector.resolve(`${platform}DebugService`); - debugService.debugStart().wait(); - } - blockingOperationFuture.return(); - } catch (err) { - // send the error to the real future - blockingOperationFuture.throw(err); + public async startTestRunner(platform: string): Promise { + this.platform = platform; + this.$options.justlaunch = true; + await new Promise((resolve, reject) => { + process.on('message', async (launcherConfig: any) => { + try { + let platformData = this.$platformsData.getPlatformData(platform.toLowerCase()); + let projectDir = this.$projectData.projectDir; + await this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }); + let projectFilesPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + + let configOptions: IKarmaConfigOptions = JSON.parse(launcherConfig); + this.$options.debugBrk = configOptions.debugBrk; + this.$options.debugTransport = configOptions.debugTransport; + let configJs = this.generateConfig(this.$options.port.toString(), configOptions); + this.$fs.writeFile(path.join(projectDir, TestExecutionService.CONFIG_FILE_NAME), configJs); + + let socketIoJsUrl = `http://localhost:${this.$options.port}/socket.io/socket.io.js`; + let socketIoJs = (await this.$httpClient.httpRequest(socketIoJsUrl)).body; + this.$fs.writeFile(path.join(projectDir, TestExecutionService.SOCKETIO_JS_FILE_NAME), socketIoJs); + + if (!await this.$platformService.preparePlatform(platform)) { + this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation."); } - }); + this.detourEntryPoint(projectFilesPath); + + this.liveSyncProject(platform); + + if (this.$options.debugBrk) { + this.$logger.info('Starting debugger...'); + let debugService: IDebugService = this.$injector.resolve(`${platform}DebugService`); + await debugService.debugStart(); + } + resolve(); + } catch (err) { + reject(err); + } }); // Tell the parent that we are ready to receive the data. process.send("ready"); - blockingOperationFuture.wait(); - }).future()(); + }); } - public startKarmaServer(platform: string): IFuture { - let karmaFuture = new Future(); - + public async startKarmaServer(platform: string): Promise { platform = platform.toLowerCase(); this.platform = platform; @@ -96,22 +86,20 @@ class TestExecutionService implements ITestExecutionService { } // We need the dependencies installed here, so we can start the Karma server. - this.$pluginsService.ensureAllDependenciesAreInstalled().wait(); + await this.$pluginsService.ensureAllDependenciesAreInstalled(); let projectDir = this.$projectData.projectDir; - this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }).wait(); + await this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }); let karmaConfig = this.getKarmaConfiguration(platform), - karmaRunner = this.$childProcess.fork(path.join(__dirname, "karma-execution.js")); - - karmaRunner.on("message", (karmaData: any) => { - fiberBootstrap.run(() => { + karmaRunner = this.$childProcess.fork(path.join(__dirname, "karma-execution.js")), + launchKarmaTests = async (karmaData: any) => { this.$logger.trace("## Unit-testing: Parent process received message", karmaData); let port: string; if (karmaData.url) { port = karmaData.url.port; let socketIoJsUrl = `http://${karmaData.url.host}/socket.io/socket.io.js`; - let socketIoJs = this.$httpClient.httpRequest(socketIoJsUrl).wait().body; + let socketIoJs = (await this.$httpClient.httpRequest(socketIoJsUrl)).body; this.$fs.writeFile(path.join(projectDir, TestExecutionService.SOCKETIO_JS_FILE_NAME), socketIoJs); } @@ -123,32 +111,39 @@ class TestExecutionService implements ITestExecutionService { // Prepare the project AFTER the TestExecutionService.CONFIG_FILE_NAME file is created in node_modules // so it will be sent to device. - if (!this.$platformService.preparePlatform(platform).wait()) { + if (!await this.$platformService.preparePlatform(platform)) { this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation."); } if (this.$options.debugBrk) { - this.getDebugService(platform).debug().wait(); + await this.getDebugService(platform).debug(); } else { - this.liveSyncProject(platform).wait(); + await this.liveSyncProject(platform); } - }); - }); + }; - karmaRunner.on("exit", (exitCode: number) => { - if (exitCode !== 0) { - //End our process with a non-zero exit code - const testError = new Error("Test run failed."); - testError.suppressCommandHelp = true; - karmaFuture.throw(testError); - } else { - karmaFuture.return(); - } + karmaRunner.on("message", (karmaData: any) => { + launchKarmaTests(karmaData) + .catch((result) => { + this.$logger.error(result); + process.exit(ErrorCodes.KARMA_FAIL); + }); }); - karmaRunner.send({ karmaConfig: karmaConfig }); + return new Promise((resolve, reject) => { + karmaRunner.on("exit", (exitCode: number) => { + if (exitCode !== 0) { + //End our process with a non-zero exit code + const testError = new Error("Test run failed."); + testError.suppressCommandHelp = true; + reject(testError); + } else { + resolve(); + } + }); - return karmaFuture; + karmaRunner.send({ karmaConfig: karmaConfig }); + }); } allowedParameters: ICommandParameter[] = []; @@ -223,22 +218,20 @@ class TestExecutionService implements ITestExecutionService { return karmaConfig; } - private liveSyncProject(platform: string): IFuture { - return (() => { - let platformData = this.$platformsData.getPlatformData(platform.toLowerCase()), - projectFilesPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); - - let liveSyncData: ILiveSyncData = { - platform: platform, - appIdentifier: this.$projectData.projectId, - projectFilesPath: projectFilesPath, - syncWorkingDirectory: path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME), - excludedProjectDirsAndFiles: this.$options.release ? constants.LIVESYNC_EXCLUDED_FILE_PATTERNS : [] - }; + private async liveSyncProject(platform: string): Promise { + let platformData = this.$platformsData.getPlatformData(platform.toLowerCase()), + projectFilesPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + + let liveSyncData: ILiveSyncData = { + platform: platform, + appIdentifier: this.$projectData.projectId, + projectFilesPath: projectFilesPath, + syncWorkingDirectory: path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME), + excludedProjectDirsAndFiles: this.$options.release ? constants.LIVESYNC_EXCLUDED_FILE_PATTERNS : [] + }; - let liveSyncService = this.$injector.resolve(this.$liveSyncProvider.platformSpecificLiveSyncServices[platform.toLowerCase()], { _liveSyncData: liveSyncData }); - liveSyncService.fullSync().wait(); - }).future()(); + let liveSyncService = this.$injector.resolve(this.$liveSyncProvider.platformSpecificLiveSyncServices[platform.toLowerCase()], { _liveSyncData: liveSyncData }); + await liveSyncService.fullSync(); } } $injector.register('testExecutionService', TestExecutionService); diff --git a/lib/services/versions-service.ts b/lib/services/versions-service.ts index 9a681bc2f2..7ce012e892 100644 --- a/lib/services/versions-service.ts +++ b/lib/services/versions-service.ts @@ -18,113 +18,103 @@ class VersionsService implements IVersionsService { this.projectData = this.getProjectData(); } - public getNativescriptCliVersion(): IFuture { - return (() => { - let currentCliVersion = this.$staticConfig.version; - let latestCliVersion = this.$npmInstallationManager.getLatestVersion(constants.NATIVESCRIPT_KEY_NAME).wait(); - - return { - componentName: constants.NATIVESCRIPT_KEY_NAME, - currentVersion: currentCliVersion, - latestVersion: latestCliVersion - }; - }).future()(); + public async getNativescriptCliVersion(): Promise { + let currentCliVersion = this.$staticConfig.version; + let latestCliVersion = await this.$npmInstallationManager.getLatestVersion(constants.NATIVESCRIPT_KEY_NAME); + + return { + componentName: constants.NATIVESCRIPT_KEY_NAME, + currentVersion: currentCliVersion, + latestVersion: latestCliVersion + }; } - public getTnsCoreModulesVersion(): IFuture { - return (() => { - let latestTnsCoreModulesVersion = this.$npmInstallationManager.getLatestVersion(constants.TNS_CORE_MODULES_NAME).wait(); - let nativescriptCoreModulesInfo: IVersionInformation = { - componentName: constants.TNS_CORE_MODULES_NAME, - latestVersion: latestTnsCoreModulesVersion - }; - - if (this.projectData) { - let nodeModulesPath = path.join(this.projectData.projectDir, constants.NODE_MODULES_FOLDER_NAME); - let tnsCoreModulesPath = path.join(nodeModulesPath, constants.TNS_CORE_MODULES_NAME); - if (!this.$fs.exists(nodeModulesPath) || - !this.$fs.exists(tnsCoreModulesPath)) { - this.$pluginsService.ensureAllDependenciesAreInstalled().wait(); - } - - let currentTnsCoreModulesVersion = this.$fs.readJson(path.join(tnsCoreModulesPath, constants.PACKAGE_JSON_FILE_NAME)).version; - nativescriptCoreModulesInfo.currentVersion = currentTnsCoreModulesVersion; + public async getTnsCoreModulesVersion(): Promise { + let latestTnsCoreModulesVersion = await this.$npmInstallationManager.getLatestVersion(constants.TNS_CORE_MODULES_NAME); + let nativescriptCoreModulesInfo: IVersionInformation = { + componentName: constants.TNS_CORE_MODULES_NAME, + latestVersion: latestTnsCoreModulesVersion + }; + + if (this.projectData) { + let nodeModulesPath = path.join(this.projectData.projectDir, constants.NODE_MODULES_FOLDER_NAME); + let tnsCoreModulesPath = path.join(nodeModulesPath, constants.TNS_CORE_MODULES_NAME); + if (!this.$fs.exists(nodeModulesPath) || + !this.$fs.exists(tnsCoreModulesPath)) { + await this.$pluginsService.ensureAllDependenciesAreInstalled(); } - return nativescriptCoreModulesInfo; - }).future()(); + let currentTnsCoreModulesVersion = this.$fs.readJson(path.join(tnsCoreModulesPath, constants.PACKAGE_JSON_FILE_NAME)).version; + nativescriptCoreModulesInfo.currentVersion = currentTnsCoreModulesVersion; + } + + return nativescriptCoreModulesInfo; } - public getRuntimesVersions(): IFuture { - return (() => { - let runtimes: string[] = [ - constants.TNS_ANDROID_RUNTIME_NAME, - constants.TNS_IOS_RUNTIME_NAME - ]; + public async getRuntimesVersions(): Promise { + let runtimes: string[] = [ + constants.TNS_ANDROID_RUNTIME_NAME, + constants.TNS_IOS_RUNTIME_NAME + ]; - let projectConfig: any; + let projectConfig: any; - if (this.projectData) { - projectConfig = this.$fs.readJson(this.projectData.projectFilePath); - } + if (this.projectData) { + projectConfig = this.$fs.readJson(this.projectData.projectFilePath); + } - let runtimesVersions: IVersionInformation[] = runtimes.map((runtime: string) => { - let latestRuntimeVersion = this.$npmInstallationManager.getLatestVersion(runtime).wait(); - let runtimeInformation: IVersionInformation = { - componentName: runtime, - latestVersion: latestRuntimeVersion - }; - - if (projectConfig) { - let projectRuntimeInformation = projectConfig.nativescript && projectConfig.nativescript[runtime]; - if (projectRuntimeInformation) { - let runtimeVersionInProject = projectRuntimeInformation.version; - runtimeInformation.currentVersion = runtimeVersionInProject; - } + let runtimesVersions: IVersionInformation[] = await Promise.all(runtimes.map(async (runtime: string) => { + let latestRuntimeVersion = await this.$npmInstallationManager.getLatestVersion(runtime); + let runtimeInformation: IVersionInformation = { + componentName: runtime, + latestVersion: latestRuntimeVersion + }; + + if (projectConfig) { + let projectRuntimeInformation = projectConfig.nativescript && projectConfig.nativescript[runtime]; + if (projectRuntimeInformation) { + let runtimeVersionInProject = projectRuntimeInformation.version; + runtimeInformation.currentVersion = runtimeVersionInProject; } + } - return runtimeInformation; - }); + return runtimeInformation; + })); - return runtimesVersions; - }).future()(); + return runtimesVersions; } - public getComponentsForUpdate(): IFuture { - return (() => { - let allComponents: IVersionInformation[] = this.getAllComponentsVersions().wait(); - let componentsForUpdate: IVersionInformation[] = []; + public async getComponentsForUpdate(): Promise { + let allComponents: IVersionInformation[] = await this.getAllComponentsVersions(); + let componentsForUpdate: IVersionInformation[] = []; - _.forEach(allComponents, (component: IVersionInformation) => { - if (component.currentVersion && this.hasUpdate(component)) { - componentsForUpdate.push(component); - } - }); + _.forEach(allComponents, (component: IVersionInformation) => { + if (component.currentVersion && this.hasUpdate(component)) { + componentsForUpdate.push(component); + } + }); - return componentsForUpdate; - }).future()(); + return componentsForUpdate; } - public getAllComponentsVersions(): IFuture { - return (() => { - let allComponents: IVersionInformation[] = []; + public async getAllComponentsVersions(): Promise { + let allComponents: IVersionInformation[] = []; - let nativescriptCliInformation: IVersionInformation = this.getNativescriptCliVersion().wait(); - if (nativescriptCliInformation) { - allComponents.push(nativescriptCliInformation); - } + let nativescriptCliInformation: IVersionInformation = await this.getNativescriptCliVersion(); + if (nativescriptCliInformation) { + allComponents.push(nativescriptCliInformation); + } - let nativescriptCoreModulesInformation: IVersionInformation = this.getTnsCoreModulesVersion().wait(); - if (nativescriptCoreModulesInformation) { - allComponents.push(nativescriptCoreModulesInformation); - } + let nativescriptCoreModulesInformation: IVersionInformation = await this.getTnsCoreModulesVersion(); + if (nativescriptCoreModulesInformation) { + allComponents.push(nativescriptCoreModulesInformation); + } - let runtimesVersions: IVersionInformation[] = this.getRuntimesVersions().wait(); + let runtimesVersions: IVersionInformation[] = await this.getRuntimesVersions(); - allComponents = allComponents.concat(runtimesVersions); + allComponents = allComponents.concat(runtimesVersions); - return allComponents; - }).future()(); + return allComponents; } public createTableWithVersionsInformation(versionsInformation: IVersionInformation[]): any { diff --git a/lib/services/xcproj-service.ts b/lib/services/xcproj-service.ts index 10405c2e07..0746d9fb65 100644 --- a/lib/services/xcproj-service.ts +++ b/lib/services/xcproj-service.ts @@ -1,6 +1,6 @@ import * as semver from "semver"; import * as helpers from "../common/helpers"; -import {EOL} from "os"; +import { EOL } from "os"; class XcprojService implements IXcprojService { private xcprojInfoCache: IXcprojInfo; @@ -8,64 +8,59 @@ class XcprojService implements IXcprojService { constructor(private $childProcess: IChildProcess, private $errors: IErrors, private $logger: ILogger, - private $staticConfig: IStaticConfig, private $sysInfo: ISysInfo, private $xcodeSelectService: IXcodeSelectService) { } - public verifyXcproj(shouldFail: boolean): IFuture { - return ((): boolean => { - let xcprojInfo = this.getXcprojInfo().wait(); - if (xcprojInfo.shouldUseXcproj && !xcprojInfo.xcprojAvailable) { - let errorMessage = `You are using CocoaPods version ${xcprojInfo.cocoapodVer} which does not support Xcode ${xcprojInfo.xcodeVersion.major}.${xcprojInfo.xcodeVersion.minor} yet.${EOL}${EOL}You can update your cocoapods by running $sudo gem install cocoapods from a terminal.${EOL}${EOL}In order for the NativeScript CLI to be able to work correctly with this setup you need to install xcproj command line tool and add it to your PATH. Xcproj can be installed with homebrew by running $ brew install xcproj from the terminal`; - if (shouldFail) { - this.$errors.failWithoutHelp(errorMessage); - } else { - this.$logger.warn(errorMessage); - } - - return true; + public async verifyXcproj(shouldFail: boolean): Promise { + let xcprojInfo = await this.getXcprojInfo(); + if (xcprojInfo.shouldUseXcproj && !xcprojInfo.xcprojAvailable) { + let errorMessage = `You are using CocoaPods version ${xcprojInfo.cocoapodVer} which does not support Xcode ${xcprojInfo.xcodeVersion.major}.${xcprojInfo.xcodeVersion.minor} yet.${EOL}${EOL}You can update your cocoapods by running $sudo gem install cocoapods from a terminal.${EOL}${EOL}In order for the NativeScript CLI to be able to work correctly with this setup you need to install xcproj command line tool and add it to your PATH. Xcproj can be installed with homebrew by running $ brew install xcproj from the terminal`; + if (shouldFail) { + this.$errors.failWithoutHelp(errorMessage); + } else { + this.$logger.warn(errorMessage); } - return false; - }).future()(); + return true; + } + + return false; } - public getXcprojInfo(): IFuture { - return ((): IXcprojInfo => { - if (!this.xcprojInfoCache) { - let cocoapodVer = this.$sysInfo.getCocoapodVersion().wait(), - xcodeVersion = this.$xcodeSelectService.getXcodeVersion().wait(); + public async getXcprojInfo(): Promise { + if (!this.xcprojInfoCache) { + let cocoapodVer = await this.$sysInfo.getCocoapodVersion(), + xcodeVersion = await this.$xcodeSelectService.getXcodeVersion(); - if(cocoapodVer && !semver.valid(cocoapodVer)) { - // Cocoapods betas have names like 1.0.0.beta.8 - // These 1.0.0 betas are not valid semver versions, but they are working fine with XCode 7.3 - // So get only the major.minor.patch version and consider them as 1.0.0 - cocoapodVer = _.take(cocoapodVer.split("."), 3).join("."); - } + if (cocoapodVer && !semver.valid(cocoapodVer)) { + // Cocoapods betas have names like 1.0.0.beta.8 + // These 1.0.0 betas are not valid semver versions, but they are working fine with XCode 7.3 + // So get only the major.minor.patch version and consider them as 1.0.0 + cocoapodVer = _.take(cocoapodVer.split("."), 3).join("."); + } - xcodeVersion.patch = xcodeVersion.patch || "0"; - // CocoaPods with version lower than 1.0.0 don't support Xcode 7.3 yet - // https://github.com/CocoaPods/CocoaPods/issues/2530#issuecomment-210470123 - // as a result of this all .pbxprojects touched by CocoaPods get converted to XML plist format - let shouldUseXcproj = cocoapodVer && !!(semver.lt(cocoapodVer, "1.0.0") && ~helpers.versionCompare(xcodeVersion, "7.3.0")), - xcprojAvailable: boolean; + xcodeVersion.patch = xcodeVersion.patch || "0"; + // CocoaPods with version lower than 1.0.0 don't support Xcode 7.3 yet + // https://github.com/CocoaPods/CocoaPods/issues/2530#issuecomment-210470123 + // as a result of this all .pbxprojects touched by CocoaPods get converted to XML plist format + let shouldUseXcproj = cocoapodVer && !!(semver.lt(cocoapodVer, "1.0.0") && ~helpers.versionCompare(xcodeVersion, "7.3.0")), + xcprojAvailable: boolean; - if (shouldUseXcproj) { - // if that's the case we can use xcproj gem to convert them back to ASCII plist format - try { - this.$childProcess.exec("xcproj --version").wait(); - xcprojAvailable = true; - } catch(e) { - xcprojAvailable = false; - } + if (shouldUseXcproj) { + // if that's the case we can use xcproj gem to convert them back to ASCII plist format + try { + await this.$childProcess.exec("xcproj --version"); + xcprojAvailable = true; + } catch (e) { + xcprojAvailable = false; } - - this.xcprojInfoCache = { cocoapodVer, xcodeVersion, shouldUseXcproj, xcprojAvailable }; } - return this.xcprojInfoCache; - }).future()(); + this.xcprojInfoCache = { cocoapodVer, xcodeVersion, shouldUseXcproj, xcprojAvailable }; + } + + return this.xcprojInfoCache; } } diff --git a/lib/sys-info.ts b/lib/sys-info.ts index 5eebed2305..d0c38cfc86 100644 --- a/lib/sys-info.ts +++ b/lib/sys-info.ts @@ -1,24 +1,23 @@ -import {SysInfoBase} from "./common/sys-info-base"; +import { SysInfoBase } from "./common/sys-info-base"; import * as path from "path"; export class SysInfo extends SysInfoBase { constructor(protected $childProcess: IChildProcess, - protected $hostInfo: IHostInfo, - protected $iTunesValidator: Mobile.IiTunesValidator, - protected $logger: ILogger, - protected $winreg: IWinReg, - private $androidToolsInfo: IAndroidToolsInfo) { + protected $hostInfo: IHostInfo, + protected $iTunesValidator: Mobile.IiTunesValidator, + protected $logger: ILogger, + protected $winreg: IWinReg, + private $androidToolsInfo: IAndroidToolsInfo) { super($childProcess, $hostInfo, $iTunesValidator, $logger, $winreg); } - public getSysInfo(pathToPackageJson: string, androidToolsInfo?: {pathToAdb: string, pathToAndroid: string}): IFuture { - return ((): ISysInfoData => { - let defaultAndroidToolsInfo = { - pathToAdb: this.$androidToolsInfo.getPathToAdbFromAndroidHome().wait(), - pathToAndroid: this.$androidToolsInfo.getPathToAndroidExecutable().wait() - }; - return super.getSysInfo(pathToPackageJson || path.join(__dirname, "..", "package.json"), androidToolsInfo || defaultAndroidToolsInfo).wait(); - }).future()(); + public async getSysInfo(pathToPackageJson: string, androidToolsInfo?: { pathToAdb: string, pathToAndroid: string }): Promise { + let defaultAndroidToolsInfo = { + pathToAdb: await this.$androidToolsInfo.getPathToAdbFromAndroidHome(), + pathToAndroid: await this.$androidToolsInfo.getPathToAndroidExecutable() + }; + + return super.getSysInfo(pathToPackageJson || await path.join(__dirname, "..", "package.json"), androidToolsInfo || defaultAndroidToolsInfo); } } $injector.register("sysInfo", SysInfo); diff --git a/lib/tools/node-modules/node-modules-builder.ts b/lib/tools/node-modules/node-modules-builder.ts index d7c358c0d1..7d3edb6139 100644 --- a/lib/tools/node-modules/node-modules-builder.ts +++ b/lib/tools/node-modules/node-modules-builder.ts @@ -1,116 +1,107 @@ import * as constants from "../../../lib/constants"; import * as path from "path"; import * as shelljs from "shelljs"; -import Future = require("fibers/future"); import { TnsModulesCopy, NpmPluginPrepare } from "./node-modules-dest-copy"; import { NodeModulesDependenciesBuilder } from "./node-modules-dependencies-builder"; -import * as fiberBootstrap from "../../common/fiber-bootstrap"; -import { sleep } from "../../../lib/common/helpers"; +import { sleep, deferPromise } from "../../../lib/common/helpers"; let glob = require("glob"); export class NodeModulesBuilder implements INodeModulesBuilder { constructor(private $fs: IFileSystem, private $projectData: IProjectData, - private $projectDataService: IProjectDataService, private $injector: IInjector, - private $logger: ILogger, private $lockfile: ILockFile, private $options: IOptions ) { } - public getChangedNodeModules(absoluteOutputPath: string, platform: string, lastModifiedTime?: Date): IFuture { - return (() => { - let projectDir = this.$projectData.projectDir; - let isNodeModulesModified = false; - let nodeModulesPath = path.join(projectDir, constants.NODE_MODULES_FOLDER_NAME); - let nodeModules: any = {}; - - if (lastModifiedTime) { - let future = new Future(); - - let match = new glob.Glob("node_modules/**", { - cwd: projectDir, - follow: true, - stat: true - }, (er: Error, files: string[]) => { - fiberBootstrap.run(() => { - - while (this.$lockfile.check().wait()) { - sleep(10); - } + public async getChangedNodeModules(absoluteOutputPath: string, platform: string, lastModifiedTime?: Date): Promise { + let projectDir = this.$projectData.projectDir; + let isNodeModulesModified = false; + let nodeModulesPath = path.join(projectDir, constants.NODE_MODULES_FOLDER_NAME); + let nodeModules: any = {}; + + if (lastModifiedTime) { + let defer = deferPromise(); + + let match = new glob.Glob("node_modules/**", { + cwd: projectDir, + follow: true, + stat: true + }, (er: Error, files: string[]) => { + while (this.$lockfile.check()) { + sleep(10); + } + + this.$lockfile.lock(); + if (er) { + if (!defer.isResolved()) { + defer.reject(er); + } - this.$lockfile.lock().wait(); - if (er) { - if (!future.isResolved()) { - future.throw(er); - } + this.$lockfile.unlock(); + match.abort(); + return; + } + for (let i = 0, l = files.length; i < l; i++) { + let file = files[i], + resolvedPath = path.join(projectDir, file), + relativePath = path.relative(projectDir, resolvedPath); + let stat = match.statCache[resolvedPath] || match.statCache[relativePath]; + if (!stat) { + match.statCache[resolvedPath] = stat = this.$fs.getFsStats(resolvedPath); + } - this.$lockfile.unlock().wait(); - match.abort(); - return; + if (stat.mtime <= lastModifiedTime) { + continue; + } + if (file === constants.NODE_MODULES_FOLDER_NAME) { + isNodeModulesModified = true; + this.$lockfile.unlock(); + match.abort(); + if (!defer.isResolved()) { + defer.resolve(); } - for (let i = 0, l = files.length; i < l; i++) { - let file = files[i], - resolvedPath = path.join(projectDir, file), - relativePath = path.relative(projectDir, resolvedPath); - let stat = match.statCache[resolvedPath] || match.statCache[relativePath]; - if (!stat) { - match.statCache[resolvedPath] = stat = this.$fs.getFsStats(resolvedPath); - } - if (stat.mtime <= lastModifiedTime) { - continue; - } - if (file === constants.NODE_MODULES_FOLDER_NAME) { - isNodeModulesModified = true; - this.$lockfile.unlock().wait(); - match.abort(); - if (!future.isResolved()) { - future.return(); - } - return; + return; + } + let rootModuleName = path.normalize(file).split(path.sep)[1]; + let rootModuleFullPath = path.join(nodeModulesPath, rootModuleName); + nodeModules[rootModuleFullPath] = rootModuleFullPath; + } + + this.$lockfile.unlock(); + }); + + match.on("end", () => { + if (!defer.isResolved()) { + let intervalId = setInterval(() => { + if (!this.$lockfile.check() || defer.isResolved()) { + if (!defer.isResolved()) { + defer.resolve(); } - let rootModuleName = path.normalize(file).split(path.sep)[1]; - let rootModuleFullPath = path.join(nodeModulesPath, rootModuleName); - nodeModules[rootModuleFullPath] = rootModuleFullPath; + clearInterval(intervalId); } - - this.$lockfile.unlock().wait(); - }); - }); - match.on("end", () => { - if (!future.isResolved()) { - let intervalId = setInterval(() => { - fiberBootstrap.run(() => { - if (!this.$lockfile.check().wait() || future.isResolved()) { - if (!future.isResolved()) { - future.return(); - } - clearInterval(intervalId); - } - }); - }, 100); - } - }); - - future.wait(); - } - - if (isNodeModulesModified && this.$fs.exists(absoluteOutputPath)) { - let currentPreparedTnsModules = this.$fs.readDirectory(absoluteOutputPath); - let tnsModulesPath = path.join(projectDir, constants.NODE_MODULES_FOLDER_NAME, constants.TNS_CORE_MODULES_NAME); - let tnsModulesInApp = this.$fs.readDirectory(tnsModulesPath); - let modulesToDelete = _.difference(currentPreparedTnsModules, tnsModulesInApp); - _.each(modulesToDelete, moduleName => this.$fs.deleteDirectory(path.join(absoluteOutputPath, moduleName))); - } - - if (!lastModifiedTime || isNodeModulesModified) { - this.expandScopedModules(nodeModulesPath, nodeModules); - } - - return nodeModules; - }).future()(); + }, 100); + } + }); + + await defer.promise; + } + + if (isNodeModulesModified && this.$fs.exists(absoluteOutputPath)) { + let currentPreparedTnsModules = this.$fs.readDirectory(absoluteOutputPath); + let tnsModulesPath = path.join(projectDir, constants.NODE_MODULES_FOLDER_NAME, constants.TNS_CORE_MODULES_NAME); + let tnsModulesInApp = this.$fs.readDirectory(tnsModulesPath); + let modulesToDelete = _.difference(currentPreparedTnsModules, tnsModulesInApp); + _.each(modulesToDelete, moduleName => this.$fs.deleteDirectory(path.join(absoluteOutputPath, moduleName))); + } + + if (!lastModifiedTime || isNodeModulesModified) { + this.expandScopedModules(nodeModulesPath, nodeModules); + } + + return nodeModules; } private expandScopedModules(nodeModulesPath: string, nodeModules: IStringDictionary): void { @@ -126,28 +117,26 @@ export class NodeModulesBuilder implements INodeModulesBuilder { }); } - public prepareNodeModules(absoluteOutputPath: string, platform: string, lastModifiedTime: Date): IFuture { - return (() => { - if (!this.$fs.exists(absoluteOutputPath)) { - // Force copying if the destination doesn't exist. - lastModifiedTime = null; - } - - let dependenciesBuilder = this.$injector.resolve(NodeModulesDependenciesBuilder, {}); - let productionDependencies = dependenciesBuilder.getProductionDependencies(this.$projectData.projectDir); - - if (!this.$options.bundle) { - const tnsModulesCopy = this.$injector.resolve(TnsModulesCopy, { - outputRoot: absoluteOutputPath - }); - tnsModulesCopy.copyModules(productionDependencies, platform); - } else { - this.cleanNodeModules(absoluteOutputPath, platform); - } - - const npmPluginPrepare = this.$injector.resolve(NpmPluginPrepare, {}); - npmPluginPrepare.preparePlugins(productionDependencies, platform); - }).future()(); + public async prepareNodeModules(absoluteOutputPath: string, platform: string, lastModifiedTime: Date): Promise { + if (!this.$fs.exists(absoluteOutputPath)) { + // Force copying if the destination doesn't exist. + lastModifiedTime = null; + } + + let dependenciesBuilder = this.$injector.resolve(NodeModulesDependenciesBuilder, {}); + let productionDependencies = dependenciesBuilder.getProductionDependencies(this.$projectData.projectDir); + + if (!this.$options.bundle) { + const tnsModulesCopy = this.$injector.resolve(TnsModulesCopy, { + outputRoot: absoluteOutputPath + }); + tnsModulesCopy.copyModules(productionDependencies, platform); + } else { + this.cleanNodeModules(absoluteOutputPath, platform); + } + + const npmPluginPrepare: NpmPluginPrepare = this.$injector.resolve(NpmPluginPrepare); + await npmPluginPrepare.preparePlugins(productionDependencies, platform); } public cleanNodeModules(absoluteOutputPath: string, platform: string): void { diff --git a/lib/tools/node-modules/node-modules-dependencies-builder.ts b/lib/tools/node-modules/node-modules-dependencies-builder.ts index 34dcef4a66..eb946c30ce 100644 --- a/lib/tools/node-modules/node-modules-dependencies-builder.ts +++ b/lib/tools/node-modules/node-modules-dependencies-builder.ts @@ -2,114 +2,114 @@ import * as path from "path"; import * as fs from "fs"; export class NodeModulesDependenciesBuilder implements INodeModulesDependenciesBuilder { - private projectPath: string; - private rootNodeModulesPath: string; - private resolvedDependencies: any[]; - private seen: any; - - public constructor(private $fs: IFileSystem) { - this.seen = {}; - this.resolvedDependencies = []; - } - - public getProductionDependencies(projectPath: string): any[] { - this.projectPath = projectPath; - this.rootNodeModulesPath = path.join(this.projectPath, "node_modules"); - - let projectPackageJsonpath = path.join(this.projectPath, "package.json"); - let packageJsonContent = this.$fs.readJson(projectPackageJsonpath); - - _.keys(packageJsonContent.dependencies).forEach(dependencyName => { - let depth = 0; - let directory = path.join(this.rootNodeModulesPath, dependencyName); - - // find and traverse child with name `key`, parent's directory -> dep.directory - this.traverseDependency(dependencyName, directory, depth); - }); - - return this.resolvedDependencies; - } - - private traverseDependency(name: string, currentModulePath: string, depth: number): void { - // Check if child has been extracted in the parent's node modules, AND THEN in `node_modules` - // Slower, but prevents copying wrong versions if multiple of the same module are installed - // Will also prevent copying project's devDependency's version if current module depends on another version - let modulePath = path.join(currentModulePath, "node_modules", name); // node_modules/parent/node_modules/ - let alternativeModulePath = path.join(this.rootNodeModulesPath, name); - - this.findModule(modulePath, alternativeModulePath, name, depth); - } - - private findModule(modulePath: string, alternativeModulePath: string, name: string, depth: number) { - let exists = this.moduleExists(modulePath); - - if (exists) { - if (this.seen[modulePath]) { - return; - } - - let dependency = this.addDependency(name, modulePath, depth + 1); - this.readModuleDependencies(modulePath, depth + 1, dependency); - } else { - modulePath = alternativeModulePath; // /node_modules/ - exists = this.moduleExists(modulePath); - - if (!exists) { - return; - } - - if (this.seen[modulePath]) { - return; - } - - let dependency = this.addDependency(name, modulePath, 0); - this.readModuleDependencies(modulePath, 0, dependency); - } - - this.seen[modulePath] = true; - } - - private readModuleDependencies(modulePath: string, depth: number, currentModule: any): void { - let packageJsonPath = path.join(modulePath, 'package.json'); - let packageJsonExists = fs.lstatSync(packageJsonPath).isFile(); - - if (packageJsonExists) { - let packageJsonContents = this.$fs.readJson(packageJsonPath); - - if (!!packageJsonContents.nativescript) { - // add `nativescript` property, necessary for resolving plugins - currentModule.nativescript = packageJsonContents.nativescript; - } - - _.keys(packageJsonContents.dependencies).forEach((dependencyName) => { - this.traverseDependency(dependencyName, modulePath, depth); - }); - } - } - - private addDependency(name: string, directory: string, depth: number): any { - let dependency: any = { - name, - directory, - depth - }; - - this.resolvedDependencies.push(dependency); - - return dependency; - } - - private moduleExists(modulePath: string): boolean { - try { - let exists = fs.lstatSync(modulePath); - if (exists.isSymbolicLink()) { - exists = fs.lstatSync(fs.realpathSync(modulePath)); - } - return exists.isDirectory(); - } catch (e) { - return false; - } - } + private projectPath: string; + private rootNodeModulesPath: string; + private resolvedDependencies: any[]; + private seen: any; + + public constructor(private $fs: IFileSystem) { + this.seen = {}; + this.resolvedDependencies = []; + } + + public getProductionDependencies(projectPath: string): any[] { + this.projectPath = projectPath; + this.rootNodeModulesPath = path.join(this.projectPath, "node_modules"); + + let projectPackageJsonpath = path.join(this.projectPath, "package.json"); + let packageJsonContent = this.$fs.readJson(projectPackageJsonpath); + + _.keys(packageJsonContent.dependencies).forEach(dependencyName => { + let depth = 0; + let directory = path.join(this.rootNodeModulesPath, dependencyName); + + // find and traverse child with name `key`, parent's directory -> dep.directory + this.traverseDependency(dependencyName, directory, depth); + }); + + return this.resolvedDependencies; + } + + private traverseDependency(name: string, currentModulePath: string, depth: number): void { + // Check if child has been extracted in the parent's node modules, AND THEN in `node_modules` + // Slower, but prevents copying wrong versions if multiple of the same module are installed + // Will also prevent copying project's devDependency's version if current module depends on another version + let modulePath = path.join(currentModulePath, "node_modules", name); // node_modules/parent/node_modules/ + let alternativeModulePath = path.join(this.rootNodeModulesPath, name); + + this.findModule(modulePath, alternativeModulePath, name, depth); + } + + private findModule(modulePath: string, alternativeModulePath: string, name: string, depth: number) { + let exists = this.moduleExists(modulePath); + + if (exists) { + if (this.seen[modulePath]) { + return; + } + + let dependency = this.addDependency(name, modulePath, depth + 1); + this.readModuleDependencies(modulePath, depth + 1, dependency); + } else { + modulePath = alternativeModulePath; // /node_modules/ + exists = this.moduleExists(modulePath); + + if (!exists) { + return; + } + + if (this.seen[modulePath]) { + return; + } + + let dependency = this.addDependency(name, modulePath, 0); + this.readModuleDependencies(modulePath, 0, dependency); + } + + this.seen[modulePath] = true; + } + + private readModuleDependencies(modulePath: string, depth: number, currentModule: any): void { + let packageJsonPath = path.join(modulePath, 'package.json'); + let packageJsonExists = fs.lstatSync(packageJsonPath).isFile(); + + if (packageJsonExists) { + let packageJsonContents = this.$fs.readJson(packageJsonPath); + + if (!!packageJsonContents.nativescript) { + // add `nativescript` property, necessary for resolving plugins + currentModule.nativescript = packageJsonContents.nativescript; + } + + _.keys(packageJsonContents.dependencies).forEach((dependencyName) => { + this.traverseDependency(dependencyName, modulePath, depth); + }); + } + } + + private addDependency(name: string, directory: string, depth: number): any { + let dependency: any = { + name, + directory, + depth + }; + + this.resolvedDependencies.push(dependency); + + return dependency; + } + + private moduleExists(modulePath: string): boolean { + try { + let exists = fs.lstatSync(modulePath); + if (exists.isSymbolicLink()) { + exists = fs.lstatSync(fs.realpathSync(modulePath)); + } + return exists.isDirectory(); + } catch (e) { + return false; + } + } } $injector.register("nodeModulesDependenciesBuilder", NodeModulesDependenciesBuilder); diff --git a/lib/tools/node-modules/node-modules-dest-copy.ts b/lib/tools/node-modules/node-modules-dest-copy.ts index 5ce6a6e095..120215e1eb 100644 --- a/lib/tools/node-modules/node-modules-dest-copy.ts +++ b/lib/tools/node-modules/node-modules-dest-copy.ts @@ -61,12 +61,12 @@ export class NpmPluginPrepare { ) { } - protected beforePrepare(dependencies: IDictionary, platform: string): void { - this.$platformsData.getPlatformData(platform).platformProjectService.beforePrepareAllPlugins(dependencies).wait(); + protected async beforePrepare(dependencies: IDictionary, platform: string): Promise { + await this.$platformsData.getPlatformData(platform).platformProjectService.beforePrepareAllPlugins(dependencies); } - protected afterPrepare(dependencies: IDictionary, platform: string): void { - this.$platformsData.getPlatformData(platform).platformProjectService.afterPrepareAllPlugins().wait(); + protected async afterPrepare(dependencies: IDictionary, platform: string): Promise { + await this.$platformsData.getPlatformData(platform).platformProjectService.afterPrepareAllPlugins(); this.writePreparedDependencyInfo(dependencies, platform); } @@ -112,18 +112,20 @@ export class NpmPluginPrepare { return result; } - public preparePlugins(dependencies: IDictionary, platform: string): void { + public async preparePlugins(dependencies: IDictionary, platform: string): Promise { if (_.isEmpty(dependencies) || this.allPrepared(dependencies, platform)) { return; } - this.beforePrepare(dependencies, platform); - _.each(dependencies, dependency => { + await this.beforePrepare(dependencies, platform); + for (let dependencyKey in dependencies) { + const dependency = dependencies[dependencyKey]; let isPlugin = !!dependency.nativescript; if (isPlugin) { - this.$pluginsService.prepare(dependency, platform).wait(); + await this.$pluginsService.prepare(dependency, platform); } - }); - this.afterPrepare(dependencies, platform); + } + + await this.afterPrepare(dependencies, platform); } } diff --git a/lib/xml-validator.ts b/lib/xml-validator.ts index 0a32f7ee6a..25d31dcf89 100644 --- a/lib/xml-validator.ts +++ b/lib/xml-validator.ts @@ -3,7 +3,7 @@ import * as constants from "./constants"; export class XmlValidator implements IXmlValidator { constructor(private $fs: IFileSystem, - private $logger: ILogger) { } + private $logger: ILogger) { } public validateXmlFiles(sourceFiles: string[]): boolean { let xmlHasErrors = false; @@ -24,7 +24,7 @@ export class XmlValidator implements IXmlValidator { public getXmlFileErrors(sourceFile: string): string { let errorOutput = ""; let fileContents = this.$fs.readText(sourceFile); - let domErrorHandler = (level:any, msg:string) => { + let domErrorHandler = (level: any, msg: string) => { errorOutput += level + EOL + msg + EOL; }; this.getDomParser(domErrorHandler).parseFromString(fileContents, "text/xml"); @@ -32,10 +32,10 @@ export class XmlValidator implements IXmlValidator { return errorOutput || null; } - private getDomParser(errorHandler: (level:any, msg:string) => void): any { + private getDomParser(errorHandler: (level: any, msg: string) => void): any { let DomParser = require("xmldom").DOMParser; let parser = new DomParser({ - locator:{}, + locator: {}, errorHandler: errorHandler }); diff --git a/package.json b/package.json index 61fb3dbd73..f1a8301fb6 100644 --- a/package.json +++ b/package.json @@ -39,14 +39,13 @@ "email-validator": "1.0.4", "esprima": "2.7.0", "ffi": "https://github.com/icenium/node-ffi/tarball/v2.0.0.4", - "fibers": "https://github.com/icenium/node-fibers/tarball/v1.0.15.0", "filesize": "3.1.2", "gaze": "1.1.0", "glob": "^7.0.3", "iconv-lite": "0.4.11", "inquirer": "0.9.0", "ios-mobileprovision-finder": "1.0.9", - "ios-sim-portable": "~1.6.0", + "ios-sim-portable": "~2.0.0", "lockfile": "1.0.1", "lodash": "4.13.1", "log4js": "1.0.1", @@ -82,24 +81,26 @@ }, "analyze": true, "devDependencies": { - "chai": "1.8.x", + "@types/chai": "3.4.34", + "@types/chai-as-promised": "0.0.29", + "chai": "3.5.0", + "chai-as-promised": "6.0.0", "grunt": "1.0.1", "grunt-contrib-clean": "1.0.0", "grunt-contrib-copy": "1.0.0", "grunt-contrib-watch": "1.0.0", "grunt-shell": "1.3.0", "grunt-ts": "6.0.0-beta.6", - "grunt-tslint": "3.3.0", + "grunt-tslint": "4.0.0", "istanbul": "0.4.5", "mocha": "3.1.2", - "mocha-fibers": "https://github.com/NativeScript/mocha-fibers.git", "mocha-typescript": "^1.0.4", "should": "7.0.2", - "tslint": "3.15.1", + "tslint": "4.3.1", "typescript": "2.1.4" }, "license": "Apache-2.0", "engines": { "node": ">=4.2.1 <5.0.0 || >=5.1.0 <8.0.0" } -} +} \ No newline at end of file diff --git a/test/android-project-properties-manager.ts b/test/android-project-properties-manager.ts index 19c1070d43..8b3a316e76 100644 --- a/test/android-project-properties-manager.ts +++ b/test/android-project-properties-manager.ts @@ -11,7 +11,7 @@ import * as yok from "../lib/common/yok"; import * as path from "path"; import temp = require("temp"); temp.track(); -import {assert} from "chai"; +import { assert } from "chai"; function createTestInjector(): IInjector { let testInjector = new yok.Yok(); @@ -28,7 +28,7 @@ function createTestInjector(): IInjector { } describe("Android project properties parser tests", () => { - it("adds project reference", () => { + it("adds project reference", async () => { let testInjector = createTestInjector(); let fs = testInjector.resolve("fs"); @@ -37,106 +37,107 @@ describe("Android project properties parser tests", () => { fs.writeFile(path.join(tempFolder, "project.properties"), projectPropertiesFileContent); let projectPropertiesManager: IAndroidProjectPropertiesManager = testInjector.resolve( - ProjectPropertiesManagerLib.AndroidProjectPropertiesManager, {directoryPath: tempFolder}); - projectPropertiesManager.addProjectReference("testValue").wait(); + ProjectPropertiesManagerLib.AndroidProjectPropertiesManager, { directoryPath: tempFolder }); + await projectPropertiesManager.addProjectReference("testValue"); let expectedContent = 'target=android-21' + '\n' + - 'android.library.reference.1=testValue'; + 'android.library.reference.1=testValue'; let actualContent = fs.readText(path.join(tempFolder, "project.properties")); assert.equal(expectedContent, actualContent); - assert.equal(1, _.keys(projectPropertiesManager.getProjectReferences().wait()).length); + assert.equal(1, _.keys(await projectPropertiesManager.getProjectReferences()).length); }); - it("adds project reference if another referencence already exists in project.properties file", () => { + + it("adds project reference if another referencence already exists in project.properties file", async () => { let testInjector = createTestInjector(); let fs = testInjector.resolve("fs"); let projectPropertiesFileContent = 'target=android-21' + '\n' + - 'android.library.reference.1=someValue'; + 'android.library.reference.1=someValue'; let tempFolder = temp.mkdirSync("AndroidProjectPropertiesManager"); fs.writeFile(path.join(tempFolder, "project.properties"), projectPropertiesFileContent); let projectPropertiesManager = testInjector.resolve( - ProjectPropertiesManagerLib.AndroidProjectPropertiesManager, {directoryPath: tempFolder}); - projectPropertiesManager.addProjectReference("testValue").wait(); + ProjectPropertiesManagerLib.AndroidProjectPropertiesManager, { directoryPath: tempFolder }); + await projectPropertiesManager.addProjectReference("testValue"); let expectedContent = ['target=android-21', - 'android.library.reference.1=someValue', - 'android.library.reference.2=testValue'].join('\n'); + 'android.library.reference.1=someValue', + 'android.library.reference.2=testValue'].join('\n'); let actualContent = fs.readText(path.join(tempFolder, "project.properties")); assert.equal(expectedContent, actualContent); - assert.equal(2, _.keys(projectPropertiesManager.getProjectReferences().wait()).length); + assert.equal(2, _.keys(await projectPropertiesManager.getProjectReferences()).length); }); - it("adds project reference if more than one references exist in project.properties file", () => { + it("adds project reference if more than one references exist in project.properties file", async () => { let testInjector = createTestInjector(); let fs = testInjector.resolve("fs"); let projectPropertiesFileContent = ['target=android-21', - 'android.library.reference.1=value1', - 'android.library.reference.2=value2', - 'android.library.reference.3=value3', - 'android.library.reference.4=value4', - 'android.library.reference.5=value5'].join('\n'); + 'android.library.reference.1=value1', + 'android.library.reference.2=value2', + 'android.library.reference.3=value3', + 'android.library.reference.4=value4', + 'android.library.reference.5=value5'].join('\n'); let tempFolder = temp.mkdirSync("AndroidProjectPropertiesManager"); fs.writeFile(path.join(tempFolder, "project.properties"), projectPropertiesFileContent); let projectPropertiesManager: IAndroidProjectPropertiesManager = testInjector.resolve( - ProjectPropertiesManagerLib.AndroidProjectPropertiesManager, {directoryPath: tempFolder}); - projectPropertiesManager.addProjectReference("testValue").wait(); + ProjectPropertiesManagerLib.AndroidProjectPropertiesManager, { directoryPath: tempFolder }); + await projectPropertiesManager.addProjectReference("testValue"); let expectedContent = projectPropertiesFileContent + '\n' + - 'android.library.reference.6=testValue'; + 'android.library.reference.6=testValue'; let actualContent = fs.readText(path.join(tempFolder, "project.properties")); assert.equal(expectedContent, actualContent); - assert.equal(6, _.keys(projectPropertiesManager.getProjectReferences().wait()).length); + assert.equal(6, _.keys(await projectPropertiesManager.getProjectReferences()).length); }); - it("removes project reference if only one reference exists", () => { + it("removes project reference if only one reference exists", async () => { let testInjector = createTestInjector(); let fs = testInjector.resolve("fs"); let projectPropertiesFileContent = 'android.library.reference.1=value1' + '\n' + - 'target=android-21'; + 'target=android-21'; let tempFolder = temp.mkdirSync("AndroidProjectPropertiesManager"); fs.writeFile(path.join(tempFolder, "project.properties"), projectPropertiesFileContent); let projectPropertiesManager: IAndroidProjectPropertiesManager = testInjector.resolve( - ProjectPropertiesManagerLib.AndroidProjectPropertiesManager, {directoryPath: tempFolder}); - projectPropertiesManager.removeProjectReference("value1").wait(); + ProjectPropertiesManagerLib.AndroidProjectPropertiesManager, { directoryPath: tempFolder }); + await projectPropertiesManager.removeProjectReference("value1"); let expectedContent = 'target=android-21'; let actualContent = fs.readText(path.join(tempFolder, "project.properties")); assert.equal(expectedContent, actualContent); - assert.equal(0, _.keys(projectPropertiesManager.getProjectReferences().wait()).length); + assert.equal(0, _.keys(await projectPropertiesManager.getProjectReferences()).length); }); - it("removes project reference when another references exist before and after the specified reference", () => { + it("removes project reference when another references exist before and after the specified reference", async () => { let testInjector = createTestInjector(); let fs = testInjector.resolve("fs"); let projectPropertiesFileContent = ['target=android-17', - 'android.library.reference.1=value1', - 'android.library.reference.2=value2', - 'android.library.reference.3=value3', - 'android.library.reference.4=value4', - 'android.library.reference.5=value5'].join('\n'); + 'android.library.reference.1=value1', + 'android.library.reference.2=value2', + 'android.library.reference.3=value3', + 'android.library.reference.4=value4', + 'android.library.reference.5=value5'].join('\n'); let tempFolder = temp.mkdirSync("AndroidProjectPropertiesManager"); fs.writeFile(path.join(tempFolder, "project.properties"), projectPropertiesFileContent); let projectPropertiesManager: IAndroidProjectPropertiesManager = testInjector.resolve( - ProjectPropertiesManagerLib.AndroidProjectPropertiesManager, {directoryPath: tempFolder}); - projectPropertiesManager.removeProjectReference("value3").wait(); + ProjectPropertiesManagerLib.AndroidProjectPropertiesManager, { directoryPath: tempFolder }); + await projectPropertiesManager.removeProjectReference("value3"); let expectedContent = ['target=android-17', - 'android.library.reference.1=value1', - 'android.library.reference.2=value2', - 'android.library.reference.3=value4', - 'android.library.reference.4=value5'].join('\n') + '\n'; + 'android.library.reference.1=value1', + 'android.library.reference.2=value2', + 'android.library.reference.3=value4', + 'android.library.reference.4=value5'].join('\n') + '\n'; let actualContent = fs.readText(path.join(tempFolder, "project.properties")); assert.equal(expectedContent, actualContent); - assert.equal(4, _.keys(projectPropertiesManager.getProjectReferences().wait()).length); + assert.equal(4, _.keys(await projectPropertiesManager.getProjectReferences()).length); }); }); diff --git a/test/app-files-updates.ts b/test/app-files-updates.ts index 5eb35e8f3e..bb3218e333 100644 --- a/test/app-files-updates.ts +++ b/test/app-files-updates.ts @@ -1,5 +1,5 @@ -import {assert} from "chai"; -import {AppFilesUpdater} from "../lib/services/app-files-updater"; +import { assert } from "chai"; +import { AppFilesUpdater } from "../lib/services/app-files-updater"; require("should"); @@ -30,7 +30,7 @@ describe("App files cleanup", () => { it("cleans up entire app when not bundling", () => { const updater = new CleanUpAppFilesUpdater([ "file1", "dir1/file2", "App_Resources/Android/blah.png" - ], {bundle: false}); + ], { bundle: false }); updater.clean(); assert.deepEqual(["file1", "dir1/file2", "App_Resources/Android/blah.png"], updater.deletedDestinationItems); }); @@ -38,7 +38,7 @@ describe("App files cleanup", () => { it("does not clean up destination when bundling", () => { const updater = new CleanUpAppFilesUpdater([ "file1", "dir1/file2", "App_Resources/Android/blah.png" - ], {bundle: true}); + ], { bundle: true }); updater.clean(); assert.deepEqual([], updater.deletedDestinationItems); }); @@ -67,7 +67,7 @@ describe("App files copy", () => { it("copies all app files when not bundling", () => { const updater = new CopyAppFilesUpdater([ "file1", "dir1/file2", "App_Resources/Android/blah.png" - ], {bundle: false}); + ], { bundle: false }); updater.copy(); assert.deepEqual(["file1", "dir1/file2", "App_Resources/Android/blah.png"], updater.copiedDestinationItems); }); @@ -75,7 +75,7 @@ describe("App files copy", () => { it("skips copying non-App_Resource files when bundling", () => { const updater = new CopyAppFilesUpdater([ "file1", "dir1/file2", "App_Resources/Android/blah.png" - ], {bundle: true}); + ], { bundle: true }); updater.copy(); assert.deepEqual(["App_Resources/Android/blah.png"], updater.copiedDestinationItems); }); diff --git a/test/cocoapods-service.ts b/test/cocoapods-service.ts index 80a17419c9..f8e11c5958 100644 --- a/test/cocoapods-service.ts +++ b/test/cocoapods-service.ts @@ -1,7 +1,7 @@ import * as yok from "../lib/common/yok"; -import {assert} from "chai"; -import {CocoaPodsService} from "../lib/services/cocoapods-service"; -import {EOL} from "os"; +import { assert } from "chai"; +import { CocoaPodsService } from "../lib/services/cocoapods-service"; +import { EOL } from "os"; interface IMergePodfileHooksTestCase { input: string; diff --git a/test/debug.ts b/test/debug.ts index b987c26154..6c6061105c 100644 --- a/test/debug.ts +++ b/test/debug.ts @@ -1,15 +1,14 @@ import * as stubs from "./stubs"; import * as yok from "../lib/common/yok"; -import {DebugAndroidCommand} from "../lib/commands/debug"; -import {assert} from "chai"; -import {Configuration, StaticConfig} from "../lib/config"; -import {Options} from "../lib/options"; -import {DevicePlatformsConstants} from "../lib/common/mobile/device-platforms-constants"; -import {FileSystem} from "../lib/common/file-system"; -import {AndroidProjectService} from "../lib/services/android-project-service"; -import {AndroidDebugBridge} from "../lib/common/mobile/android/android-debug-bridge"; -import {AndroidDebugBridgeResultHandler} from "../lib/common/mobile/android/android-debug-bridge-result-handler"; -import future = require("fibers/future"); +import { DebugAndroidCommand } from "../lib/commands/debug"; +import { assert } from "chai"; +import { Configuration, StaticConfig } from "../lib/config"; +import { Options } from "../lib/options"; +import { DevicePlatformsConstants } from "../lib/common/mobile/device-platforms-constants"; +import { FileSystem } from "../lib/common/file-system"; +import { AndroidProjectService } from "../lib/services/android-project-service"; +import { AndroidDebugBridge } from "../lib/common/mobile/android/android-debug-bridge"; +import { AndroidDebugBridgeResultHandler } from "../lib/common/mobile/android/android-debug-bridge-result-handler"; function createTestInjector(): IInjector { let testInjector: IInjector = new yok.Yok(); @@ -27,9 +26,9 @@ function createTestInjector(): IInjector { testInjector.register('errors', stubs.ErrorsStub); testInjector.register('hostInfo', {}); testInjector.register("analyticsService", { - trackException: () => { return future.fromResult(); }, - checkConsent: () => { return future.fromResult(); }, - trackFeature: () => { return future.fromResult(); } + trackException: async () => undefined, + checkConsent: async () => undefined, + trackFeature: async () => undefined }); testInjector.register("usbLiveSyncService", stubs.LiveSyncServiceStub); testInjector.register("androidProjectService", AndroidProjectService); @@ -65,33 +64,33 @@ describe("Debugger tests", () => { testInjector = createTestInjector(); }); - it("Ensures that debugLivesync flag is true when executing debug --watch command", () => { + it("Ensures that debugLivesync flag is true when executing debug --watch command", async () => { let debugCommand: ICommand = testInjector.resolve("debug|android"); let options: IOptions = testInjector.resolve("options"); options.watch = true; - debugCommand.execute(["android","--watch"]).wait(); - let config:IConfiguration = testInjector.resolve("config"); + await debugCommand.execute(["android", "--watch"]); + let config: IConfiguration = testInjector.resolve("config"); assert.isTrue(config.debugLivesync); - }); + }); - it("Ensures that beforePrepareAllPlugins will not call gradle when livesyncing", () => { - let config:IConfiguration = testInjector.resolve("config"); + it("Ensures that beforePrepareAllPlugins will not call gradle when livesyncing", async () => { + let config: IConfiguration = testInjector.resolve("config"); config.debugLivesync = true; let childProcess: stubs.ChildProcessStub = testInjector.resolve("childProcess"); let androidProjectService: IPlatformProjectService = testInjector.resolve("androidProjectService"); let spawnFromEventCount = childProcess.spawnFromEventCount; - androidProjectService.beforePrepareAllPlugins().wait(); + await androidProjectService.beforePrepareAllPlugins(); assert.isTrue(spawnFromEventCount === 0); assert.isTrue(spawnFromEventCount === childProcess.spawnFromEventCount); }); - it("Ensures that beforePrepareAllPlugins will call gradle with clean option when *NOT* livesyncing", () => { - let config:IConfiguration = testInjector.resolve("config"); + it("Ensures that beforePrepareAllPlugins will call gradle with clean option when *NOT* livesyncing", async () => { + let config: IConfiguration = testInjector.resolve("config"); config.debugLivesync = false; let childProcess: stubs.ChildProcessStub = testInjector.resolve("childProcess"); let androidProjectService: IPlatformProjectService = testInjector.resolve("androidProjectService"); let spawnFromEventCount = childProcess.spawnFromEventCount; - androidProjectService.beforePrepareAllPlugins().wait(); + await androidProjectService.beforePrepareAllPlugins(); assert.isTrue(childProcess.lastCommand.indexOf("gradle") !== -1); assert.isTrue(childProcess.lastCommandArgs[0] === "clean"); assert.isTrue(spawnFromEventCount === 0); diff --git a/test/definitions/should.d.ts b/test/definitions/should.d.ts index 8b4c21fc1e..7096cfcf7a 100644 --- a/test/definitions/should.d.ts +++ b/test/definitions/should.d.ts @@ -3,7 +3,7 @@ // Definitions by: Alex Varju , Maxime LUCE // Definitions: https://github.com/borisyankov/DefinitelyTyped -interface Object { +interface ShouldObject { should: ShouldAssertion; } diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index 0cbb090c5e..fc5e4352e4 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -1,4 +1,3 @@ -import Future = require("fibers/future"); import * as path from "path"; import * as ChildProcessLib from "../lib/common/child-process"; import * as ConfigLib from "../lib/config"; @@ -30,12 +29,12 @@ import temp = require("temp"); temp.track(); class IOSSimulatorDiscoveryMock extends DeviceDiscovery { - public startLookingForDevices(): IFuture { - return Future.fromResult(); + public async startLookingForDevices(): Promise { + return; } - public checkForDevices(): IFuture { - return Future.fromResult(); + public async checkForDevices(): Promise { + return; } } @@ -119,7 +118,7 @@ function readOption(args: string[], option: string): string { describe("iOSProjectService", () => { describe("archive", () => { - function setupArchive(options?: { archivePath?: string }): { run: () => void, assert: () => void } { + async function setupArchive(options?: { archivePath?: string }): Promise<{ run: () => Promise, assert: () => void }> { let hasCustomArchivePath = options && options.archivePath; let projectName = "projectDirectory"; @@ -149,33 +148,33 @@ describe("iOSProjectService", () => { expectOption(args, "-project", path.join(projectPath, "platforms", "ios", projectName + ".xcodeproj"), "Path to Xcode project is wrong."); expectOption(args, "-scheme", projectName, "The provided scheme is wrong."); - return Future.fromResult(); + return Promise.resolve(); }; let resultArchivePath: string; return { - run() { + run: async (): Promise => { if (hasCustomArchivePath) { - resultArchivePath = iOSProjectService.archive({ archivePath: options.archivePath }).wait(); + resultArchivePath = await iOSProjectService.archive({ archivePath: options.archivePath }); } else { - resultArchivePath = iOSProjectService.archive().wait(); + resultArchivePath = await iOSProjectService.archive(); } }, - assert() { + assert: () => { assert.ok(xcodebuildExeced, "Expected xcodebuild archive to be executed"); assert.equal(resultArchivePath, archivePath, "iOSProjectService.archive expected to return the path to the archive"); } }; } - it("by default exports xcodearchive to platforms/ios/build/archive/.xcarchive", () => { - let setup = setupArchive(); - setup.run(); + it("by default exports xcodearchive to platforms/ios/build/archive/.xcarchive", async () => { + let setup = await setupArchive(); + await setup.run(); setup.assert(); }); - it("can pass archivePath to xcodebuild -archivePath", () => { - let setup = setupArchive({ archivePath: "myarchive.xcarchive" }); - setup.run(); + it("can pass archivePath to xcodebuild -archivePath", async () => { + let setup = await setupArchive({ archivePath: "myarchive.xcarchive" }); + await setup.run(); setup.assert(); }); }); @@ -209,7 +208,7 @@ describe("iOSProjectService", () => { `; - function testExportArchive(options: { teamID?: string }, expectedPlistContent: string): void { + async function testExportArchive(options: { teamID?: string }, expectedPlistContent: string): Promise { let projectName = "projectDirectory"; let projectPath = temp.mkdirSync(projectName); @@ -239,10 +238,10 @@ describe("iOSProjectService", () => { // There may be better way to equal property lists assert.equal(plistContent, expectedPlistContent, "Mismatch in exportOptionsPlist content"); - return Future.fromResult(); + return Promise.resolve(); }; - let resultIpa = iOSProjectService.exportArchive({ archivePath, teamID: options.teamID }).wait(); + let resultIpa = await iOSProjectService.exportArchive({ archivePath, teamID: options.teamID }); let expectedIpa = path.join(projectPath, "platforms", "ios", "build", "archive", projectName + ".ipa"); assert.equal(resultIpa, expectedIpa, "Expected IPA at the specified location"); @@ -250,12 +249,12 @@ describe("iOSProjectService", () => { assert.ok(xcodebuildExeced, "Expected xcodebuild to be executed"); } - it("calls xcodebuild -exportArchive to produce .IPA", () => { - testExportArchive({}, noTeamPlist); + it("calls xcodebuild -exportArchive to produce .IPA", async () => { + await testExportArchive({}, noTeamPlist); }); - it("passes the --team-id option down the xcodebuild -exportArchive throug the -exportOptionsPlist", () => { - testExportArchive({ teamID: "MyTeam" }, myTeamPlist); + it("passes the --team-id option down the xcodebuild -exportArchive throug the -exportOptionsPlist", async () => { + await testExportArchive({ teamID: "MyTeam" }, myTeamPlist); }); }); }); @@ -264,7 +263,7 @@ describe("Cocoapods support", () => { if (require("os").platform() !== "darwin") { console.log("Skipping Cocoapods tests. They cannot work on windows"); } else { - it("adds plugin with Podfile", () => { + it("adds plugin with Podfile", async () => { let projectName = "projectDirectory"; let projectPath = temp.mkdirSync(projectName); @@ -287,11 +286,11 @@ describe("Cocoapods support", () => { fs.createDirectory(platformsFolderPath); let iOSProjectService = testInjector.resolve("iOSProjectService"); - iOSProjectService.prepareFrameworks = (pluginPlatformsFolderPath: string, pluginData: IPluginData): IFuture => { - return Future.fromResult(); + iOSProjectService.prepareFrameworks = (pluginPlatformsFolderPath: string, pluginData: IPluginData): Promise => { + return Promise.resolve(); }; - iOSProjectService.prepareStaticLibs = (pluginPlatformsFolderPath: string, pluginData: IPluginData): IFuture => { - return Future.fromResult(); + iOSProjectService.prepareStaticLibs = (pluginPlatformsFolderPath: string, pluginData: IPluginData): Promise => { + return Promise.resolve(); }; iOSProjectService.createPbxProj = () => { return { @@ -299,7 +298,7 @@ describe("Cocoapods support", () => { pbxXCBuildConfigurationSection: () => { return {}; }, }; }; - iOSProjectService.savePbxProj = (): IFuture => Future.fromResult(); + iOSProjectService.savePbxProj = (): Promise => Promise.resolve(); let pluginPath = temp.mkdirSync("pluginDirectory"); let pluginPlatformsFolderPath = path.join(pluginPath, "platforms", "ios"); @@ -313,7 +312,7 @@ describe("Cocoapods support", () => { } }; - iOSProjectService.preparePluginNativeCode(pluginData).wait(); + await iOSProjectService.preparePluginNativeCode(pluginData); let projectPodfilePath = path.join(platformsFolderPath, "Podfile"); assert.isTrue(fs.exists(projectPodfilePath)); @@ -328,7 +327,7 @@ describe("Cocoapods support", () => { .join("\n"); assert.equal(actualProjectPodfileContent, expectedProjectPodfileContent); }); - it("adds and removes plugin with Podfile", () => { + it("adds and removes plugin with Podfile", async () => { let projectName = "projectDirectory2"; let projectPath = temp.mkdirSync(projectName); @@ -351,17 +350,17 @@ describe("Cocoapods support", () => { fs.createDirectory(platformsFolderPath); let iOSProjectService = testInjector.resolve("iOSProjectService"); - iOSProjectService.prepareFrameworks = (pluginPlatformsFolderPath: string, pluginData: IPluginData): IFuture => { - return Future.fromResult(); + iOSProjectService.prepareFrameworks = (pluginPlatformsFolderPath: string, pluginData: IPluginData): Promise => { + return Promise.resolve(); }; - iOSProjectService.prepareStaticLibs = (pluginPlatformsFolderPath: string, pluginData: IPluginData): IFuture => { - return Future.fromResult(); + iOSProjectService.prepareStaticLibs = (pluginPlatformsFolderPath: string, pluginData: IPluginData): Promise => { + return Promise.resolve(); }; - iOSProjectService.removeFrameworks = (pluginPlatformsFolderPath: string, pluginData: IPluginData): IFuture => { - return Future.fromResult(); + iOSProjectService.removeFrameworks = (pluginPlatformsFolderPath: string, pluginData: IPluginData): Promise => { + return Promise.resolve(); }; - iOSProjectService.removeStaticLibs = (pluginPlatformsFolderPath: string, pluginData: IPluginData): IFuture => { - return Future.fromResult(); + iOSProjectService.removeStaticLibs = (pluginPlatformsFolderPath: string, pluginData: IPluginData): Promise => { + return Promise.resolve(); }; iOSProjectService.createPbxProj = () => { return { @@ -369,7 +368,7 @@ describe("Cocoapods support", () => { pbxXCBuildConfigurationSection: () => { return {}; }, }; }; - iOSProjectService.savePbxProj = (): IFuture => Future.fromResult(); + iOSProjectService.savePbxProj = (): Promise => Promise.resolve(); let pluginPath = temp.mkdirSync("pluginDirectory"); let pluginPlatformsFolderPath = path.join(pluginPath, "platforms", "ios"); @@ -383,7 +382,7 @@ describe("Cocoapods support", () => { } }; - iOSProjectService.preparePluginNativeCode(pluginData).wait(); + await iOSProjectService.preparePluginNativeCode(pluginData); let projectPodfilePath = path.join(platformsFolderPath, "Podfile"); assert.isTrue(fs.exists(projectPodfilePath)); @@ -398,7 +397,7 @@ describe("Cocoapods support", () => { .join("\n"); assert.equal(actualProjectPodfileContent, expectedProjectPodfileContent); - iOSProjectService.removePluginNativeCode(pluginData); + await iOSProjectService.removePluginNativeCode(pluginData); assert.isFalse(fs.exists(projectPodfilePath)); }); @@ -420,7 +419,7 @@ describe("Static libraries support", () => { let staticLibraryPath = path.join(path.join(temp.mkdirSync("pluginDirectory"), "platforms", "ios")); let staticLibraryHeadersPath = path.join(staticLibraryPath, "include", libraryName); - it("checks validation of header files", () => { + it("checks validation of header files", async () => { let iOSProjectService = testInjector.resolve("iOSProjectService"); fs.ensureDirectoryExists(staticLibraryHeadersPath); _.each(headers, header => { fs.writeFile(path.join(staticLibraryHeadersPath, header), ""); }); @@ -430,7 +429,7 @@ describe("Static libraries support", () => { let error: any; try { - iOSProjectService.validateStaticLibrary(path.join(staticLibraryPath, libraryName + ".a")).wait(); + await iOSProjectService.validateStaticLibrary(path.join(staticLibraryPath, libraryName + ".a")); } catch (err) { error = err; } diff --git a/test/mocha.opts b/test/mocha.opts index 420f1c1ea6..6541568281 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,4 +1,3 @@ ---ui mocha-fibers --recursive --reporter spec --require test/test-bootstrap.js diff --git a/test/npm-installation-manager.ts b/test/npm-installation-manager.ts index de732fa64f..a65ca053b4 100644 --- a/test/npm-installation-manager.ts +++ b/test/npm-installation-manager.ts @@ -1,4 +1,4 @@ -import {assert} from "chai"; +import { assert } from "chai"; import * as ConfigLib from "../lib/config"; import * as ErrorsLib from "../lib/common/errors"; import * as FsLib from "../lib/common/file-system"; @@ -7,7 +7,6 @@ import * as LoggerLib from "../lib/common/logger"; import * as NpmInstallationManagerLib from "../lib/npm-installation-manager"; import * as OptionsLib from "../lib/options"; import * as StaticConfigLib from "../lib/config"; -import Future = require("fibers/future"); import * as yok from "../lib/common/yok"; import ChildProcessLib = require("../lib/common/child-process"); @@ -16,7 +15,7 @@ function createTestInjector(): IInjector { testInjector.register("config", ConfigLib.Configuration); testInjector.register("logger", LoggerLib.Logger); - testInjector.register("lockfile", { }); + testInjector.register("lockfile", {}); testInjector.register("errors", ErrorsLib.Errors); testInjector.register("options", OptionsLib.Options); testInjector.register("fs", FsLib.FileSystem); @@ -31,14 +30,12 @@ function createTestInjector(): IInjector { function mockNpm(testInjector: IInjector, versions: string[], latestVersion: string) { testInjector.register("npm", { - view: (packageName: string, config: any) => { - return(() => { - if(config.versions) { - return versions; - } - - throw new Error(`Unable to find propertyName ${config}.`); - }).future()(); + view: async (packageName: string, config: any): Promise => { + if (config.versions) { + return versions; + } + + throw new Error(`Unable to find propertyName ${config}.`); } }); } @@ -67,35 +64,35 @@ interface ITestData { describe("Npm installation manager tests", () => { let testData: IDictionary = { - "when there's only one available version and it matches CLI's version": { + "when there's only one available version and it matches CLI's version": { versions: ["1.4.0"], packageLatestVersion: "1.4.0", cliVersion: "1.4.0", expectedResult: "1.4.0" }, - "when there's only one available version and it is higher than match CLI's version": { + "when there's only one available version and it is higher than match CLI's version": { versions: ["1.4.0"], packageLatestVersion: "1.4.0", cliVersion: "1.2.0", expectedResult: "1.4.0" }, - "when there's only one available version and it is lower than CLI's version": { + "when there's only one available version and it is lower than CLI's version": { versions: ["1.4.0"], packageLatestVersion: "1.4.0", cliVersion: "1.6.0", expectedResult: "1.4.0" }, - "when there are multiple package versions and the latest one matches ~":{ + "when there are multiple package versions and the latest one matches ~": { versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"], packageLatestVersion: "1.3.3", cliVersion: "1.3.0", expectedResult: "1.3.3" }, - "when there are multiple package versions and the latest one matches ~ when there are newer matching versions but they are not under latest tag":{ + "when there are multiple package versions and the latest one matches ~ when there are newer matching versions but they are not under latest tag": { versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"], packageLatestVersion: "1.3.2", cliVersion: "1.3.0", @@ -153,7 +150,7 @@ describe("Npm installation manager tests", () => { }; _.each(testData, (currentTestData: ITestData, testName: string) => { - it(`returns correct latest compatible version, ${testName}`, () => { + it(`returns correct latest compatible version, ${testName}`, async () => { let testInjector = createTestInjector(); mockNpm(testInjector, currentTestData.versions, currentTestData.packageLatestVersion); @@ -164,9 +161,9 @@ describe("Npm installation manager tests", () => { // Mock npmInstallationManager.getLatestVersion let npmInstallationManager = testInjector.resolve("npmInstallationManager"); - npmInstallationManager.getLatestVersion = (packageName: string) => Future.fromResult(currentTestData.packageLatestVersion); + npmInstallationManager.getLatestVersion = (packageName: string) => Promise.resolve(currentTestData.packageLatestVersion); - let actualLatestCompatibleVersion = npmInstallationManager.getLatestCompatibleVersion("").wait(); + let actualLatestCompatibleVersion = await npmInstallationManager.getLatestCompatibleVersion(""); assert.equal(actualLatestCompatibleVersion, currentTestData.expectedResult); }); }); diff --git a/test/npm-support.ts b/test/npm-support.ts index b888981e66..b69cd5e47d 100644 --- a/test/npm-support.ts +++ b/test/npm-support.ts @@ -25,7 +25,6 @@ import { MobilePlatformsCapabilities } from "../lib/mobile-platforms-capabilitie import { DevicePlatformsConstants } from "../lib/common/mobile/device-platforms-constants"; import { XmlValidator } from "../lib/xml-validator"; import { LockFile } from "../lib/lockfile"; -import Future = require("fibers/future"); import ProjectChangesLib = require("../lib/services/project-changes-service"); import path = require("path"); @@ -113,97 +112,93 @@ function createProject(testInjector: IInjector, dependencies?: any): string { return tempFolder; } -function setupProject(dependencies?: any): IFuture { - return (() => { - let testInjector = createTestInjector(); - let projectFolder = createProject(testInjector, dependencies); +async function setupProject(dependencies?: any): Promise { + let testInjector = createTestInjector(); + let projectFolder = createProject(testInjector, dependencies); - let fs = testInjector.resolve("fs"); + let fs = testInjector.resolve("fs"); - // Creates app folder - let appFolderPath = path.join(projectFolder, "app"); - fs.createDirectory(appFolderPath); - let appResourcesFolderPath = path.join(appFolderPath, "App_Resources"); - fs.createDirectory(appResourcesFolderPath); - fs.createDirectory(path.join(appResourcesFolderPath, "Android")); - fs.createDirectory(path.join(appResourcesFolderPath, "Android", "mockdir")); - fs.createDirectory(path.join(appFolderPath, "tns_modules")); - - // Creates platforms/android folder - let androidFolderPath = path.join(projectFolder, "platforms", "android"); - fs.ensureDirectoryExists(androidFolderPath); - - // Mock platform data - let appDestinationFolderPath = path.join(androidFolderPath, "assets"); - let platformsData = testInjector.resolve("platformsData"); - - platformsData.getPlatformData = (platform: string) => { - return { - appDestinationDirectoryPath: appDestinationFolderPath, - appResourcesDestinationDirectoryPath: path.join(appDestinationFolderPath, "app", "App_Resources"), - frameworkPackageName: "tns-android", - normalizedPlatformName: "Android", - projectRoot: projectFolder, - configurationFileName: "AndroidManifest.xml", - platformProjectService: { - prepareProject: (): any => null, - prepareAppResources: (): any => null, - afterPrepareAllPlugins: () => Future.fromResult(), - beforePrepareAllPlugins: () => Future.fromResult(), - getAppResourcesDestinationDirectoryPath: () => path.join(androidFolderPath, "src", "main", "res"), - processConfigurationFilesFromAppResources: () => Future.fromResult(), - ensureConfigurationFileInAppResources: (): any => null, - interpolateConfigurationFile: () => Future.fromResult(), - isPlatformPrepared: (projectRoot: string) => false - } - }; - }; + // Creates app folder + let appFolderPath = path.join(projectFolder, "app"); + fs.createDirectory(appFolderPath); + let appResourcesFolderPath = path.join(appFolderPath, "App_Resources"); + fs.createDirectory(appResourcesFolderPath); + fs.createDirectory(path.join(appResourcesFolderPath, "Android")); + fs.createDirectory(path.join(appResourcesFolderPath, "Android", "mockdir")); + fs.createDirectory(path.join(appFolderPath, "tns_modules")); + + // Creates platforms/android folder + let androidFolderPath = path.join(projectFolder, "platforms", "android"); + fs.ensureDirectoryExists(androidFolderPath); + // Mock platform data + let appDestinationFolderPath = path.join(androidFolderPath, "assets"); + let platformsData = testInjector.resolve("platformsData"); + + platformsData.getPlatformData = (platform: string) => { return { - testInjector: testInjector, - projectFolder: projectFolder, - appDestinationFolderPath: appDestinationFolderPath, + appDestinationDirectoryPath: appDestinationFolderPath, + appResourcesDestinationDirectoryPath: path.join(appDestinationFolderPath, "app", "App_Resources"), + frameworkPackageName: "tns-android", + normalizedPlatformName: "Android", + projectRoot: projectFolder, + configurationFileName: "AndroidManifest.xml", + platformProjectService: { + prepareProject: (): any => null, + prepareAppResources: (): any => null, + afterPrepareAllPlugins: () => Promise.resolve(), + beforePrepareAllPlugins: () => Promise.resolve(), + getAppResourcesDestinationDirectoryPath: () => path.join(androidFolderPath, "src", "main", "res"), + processConfigurationFilesFromAppResources: () => Promise.resolve(), + ensureConfigurationFileInAppResources: (): any => null, + interpolateConfigurationFile: () => Promise.resolve(), + isPlatformPrepared: (projectRoot: string) => false + } }; - }).future()(); + }; + + return { + testInjector: testInjector, + projectFolder: projectFolder, + appDestinationFolderPath: appDestinationFolderPath, + }; } -function addDependencies(testInjector: IInjector, projectFolder: string, dependencies: any, devDependencies?: any): IFuture { - return (() => { - let fs = testInjector.resolve("fs"); - let packageJsonPath = path.join(projectFolder, "package.json"); - let packageJsonData = fs.readJson(packageJsonPath); +async function addDependencies(testInjector: IInjector, projectFolder: string, dependencies: any, devDependencies?: any): Promise { + let fs = testInjector.resolve("fs"); + let packageJsonPath = path.join(projectFolder, "package.json"); + let packageJsonData = fs.readJson(packageJsonPath); - let currentDependencies = packageJsonData.dependencies; - _.extend(currentDependencies, dependencies); + let currentDependencies = packageJsonData.dependencies; + _.extend(currentDependencies, dependencies); - if (devDependencies) { - let currentDevDependencies = packageJsonData.devDependencies; - _.extend(currentDevDependencies, devDependencies); - } - fs.writeJson(packageJsonPath, packageJsonData); - }).future()(); + if (devDependencies) { + let currentDevDependencies = packageJsonData.devDependencies; + _.extend(currentDevDependencies, devDependencies); + } + fs.writeJson(packageJsonPath, packageJsonData); } -function preparePlatform(testInjector: IInjector): IFuture { +async function preparePlatform(testInjector: IInjector): Promise { let platformService = testInjector.resolve("platformService"); return platformService.preparePlatform("android"); } describe("Npm support tests", () => { let testInjector: IInjector, projectFolder: string, appDestinationFolderPath: string; - beforeEach(() => { - let projectSetup = setupProject().wait(); + beforeEach(async () => { + let projectSetup = await setupProject(); testInjector = projectSetup.testInjector; projectFolder = projectSetup.projectFolder; appDestinationFolderPath = projectSetup.appDestinationFolderPath; }); - it("Ensures that the installed dependencies are prepared correctly", () => { + it("Ensures that the installed dependencies are prepared correctly", async () => { let fs: IFileSystem = testInjector.resolve("fs"); // Setup - addDependencies(testInjector, projectFolder, { "bplist": "0.0.4" }).wait(); + await addDependencies(testInjector, projectFolder, { "bplist": "0.0.4" }); // Act - preparePlatform(testInjector).wait(); + await preparePlatform(testInjector); // Assert let tnsModulesFolderPath = path.join(appDestinationFolderPath, "app", "tns_modules"); @@ -217,33 +212,33 @@ describe("Npm support tests", () => { assert.isTrue(results.filter((val) => _.endsWith(val, "bplist-creator")).length === 1); assert.isTrue(results.filter((val) => _.endsWith(val, "bplist-parser")).length === 1); }); - it("Ensures that scoped dependencies are prepared correctly", () => { + it("Ensures that scoped dependencies are prepared correctly", async () => { // Setup let fs = testInjector.resolve("fs"); let scopedName = "@reactivex/rxjs"; let dependencies: any = {}; dependencies[scopedName] = "0.0.0-prealpha.3"; // Do not pass dependencies object as the sinopia cannot work with scoped dependencies. Instead move them manually. - addDependencies(testInjector, projectFolder, dependencies).wait(); + await addDependencies(testInjector, projectFolder, dependencies); // Act - preparePlatform(testInjector).wait(); + await preparePlatform(testInjector); // Assert let tnsModulesFolderPath = path.join(appDestinationFolderPath, "app", "tns_modules"); let scopedDependencyPath = path.join(tnsModulesFolderPath, "@reactivex", "rxjs"); assert.isTrue(fs.exists(scopedDependencyPath)); }); - it("Ensures that scoped dependencies are prepared correctly when are not in root level", () => { + it("Ensures that scoped dependencies are prepared correctly when are not in root level", async () => { // Setup let customPluginName = "plugin-with-scoped-dependency"; let customPluginDirectory = temp.mkdirSync("custom-plugin-directory"); let fs: IFileSystem = testInjector.resolve("fs"); - fs.unzip(path.join("resources", "test", `${customPluginName}.zip`), customPluginDirectory).wait(); + await fs.unzip(path.join("resources", "test", `${customPluginName}.zip`), customPluginDirectory); - addDependencies(testInjector, projectFolder, { "plugin-with-scoped-dependency": `file:${path.join(customPluginDirectory, customPluginName)}` }).wait(); + await addDependencies(testInjector, projectFolder, { "plugin-with-scoped-dependency": `file:${path.join(customPluginDirectory, customPluginName)}` }); // Act - preparePlatform(testInjector).wait(); + await preparePlatform(testInjector); // Assert let tnsModulesFolderPath = path.join(appDestinationFolderPath, "app", "tns_modules"); let results = fs.enumerateFilesInDirectorySync(tnsModulesFolderPath, (file, stat) => { @@ -257,22 +252,22 @@ describe("Npm support tests", () => { assert.isTrue(filteredResults.length === 1); }); - it("Ensures that tns_modules absent when bundling", () => { + it("Ensures that tns_modules absent when bundling", async () => { let fs = testInjector.resolve("fs"); let options = testInjector.resolve("options"); let tnsModulesFolderPath = path.join(appDestinationFolderPath, "app", "tns_modules"); try { options.bundle = false; - preparePlatform(testInjector).wait(); + await preparePlatform(testInjector); assert.isTrue(fs.exists(tnsModulesFolderPath), "tns_modules created first"); options.bundle = true; - preparePlatform(testInjector).wait(); + await preparePlatform(testInjector); assert.isFalse(fs.exists(tnsModulesFolderPath), "tns_modules deleted when bundling"); options.bundle = false; - preparePlatform(testInjector).wait(); + await preparePlatform(testInjector); assert.isTrue(fs.exists(tnsModulesFolderPath), "tns_modules recreated"); } finally { options.bundle = false; @@ -281,8 +276,8 @@ describe("Npm support tests", () => { }); describe("Flatten npm modules tests", () => { - it("Doesn't handle the dependencies of devDependencies", () => { - let projectSetup = setupProject({}).wait(); + it("Doesn't handle the dependencies of devDependencies", async () => { + let projectSetup = await setupProject({}); let testInjector = projectSetup.testInjector; let projectFolder = projectSetup.projectFolder; let appDestinationFolderPath = projectSetup.appDestinationFolderPath; @@ -293,9 +288,9 @@ describe("Flatten npm modules tests", () => { "gulp-jshint": "1.11.0" }; - addDependencies(testInjector, projectFolder, {}, devDependencies).wait(); + await addDependencies(testInjector, projectFolder, {}, devDependencies); - preparePlatform(testInjector).wait(); + await preparePlatform(testInjector); // Assert let fs = testInjector.resolve("fs"); diff --git a/test/platform-commands.ts b/test/platform-commands.ts index 3b1c1f604f..2a77198ef9 100644 --- a/test/platform-commands.ts +++ b/test/platform-commands.ts @@ -9,17 +9,17 @@ import * as CommandsServiceLib from "../lib/common/services/commands-service"; import * as optionsLib from "../lib/options"; import * as hostInfoLib from "../lib/common/host-info"; import * as ProjectFilesManagerLib from "../lib/common/services/project-files-manager"; -import {assert} from "chai"; -import {DeviceAppDataFactory} from "../lib/common/mobile/device-app-data/device-app-data-factory"; -import {LocalToDevicePathDataFactory} from "../lib/common/mobile/local-to-device-path-data-factory"; -import {MobileHelper} from "../lib/common/mobile/mobile-helper"; -import {ProjectFilesProvider} from "../lib/providers/project-files-provider"; -import {DeviceAppDataProvider} from "../lib/providers/device-app-data-provider"; -import {MobilePlatformsCapabilities} from "../lib/mobile-platforms-capabilities"; -import {DevicePlatformsConstants} from "../lib/common/mobile/device-platforms-constants"; +import { assert } from "chai"; +import { DeviceAppDataFactory } from "../lib/common/mobile/device-app-data/device-app-data-factory"; +import { LocalToDevicePathDataFactory } from "../lib/common/mobile/local-to-device-path-data-factory"; +import { MobileHelper } from "../lib/common/mobile/mobile-helper"; +import { ProjectFilesProvider } from "../lib/providers/project-files-provider"; +import { DeviceAppDataProvider } from "../lib/providers/device-app-data-provider"; +import { MobilePlatformsCapabilities } from "../lib/mobile-platforms-capabilities"; +import { DevicePlatformsConstants } from "../lib/common/mobile/device-platforms-constants"; import { XmlValidator } from "../lib/xml-validator"; import * as ChildProcessLib from "../lib/common/child-process"; -import {CleanCommand} from "../lib/commands/platform-clean"; +import { CleanCommand } from "../lib/commands/platform-clean"; import ProjectChangesLib = require("../lib/services/project-changes-service"); let isCommandExecuted = true; @@ -49,17 +49,15 @@ class ErrorsNoFailStub implements IErrors { throw new Error(); } - beginCommand(action: () => IFuture, printHelpCommand: () => IFuture): IFuture { - return (() => { - let result = false; - try { - result = action().wait(); - } catch(ex) { - /* intentionally left blank */ - } - - return result; - }).future()(); + async beginCommand(action: () => Promise, printHelpCommand: () => Promise): Promise { + let result = false; + try { + result = await action(); + } catch (ex) { + /* intentionally left blank */ + } + + return result; } executeAction(action: Function): any { @@ -75,7 +73,7 @@ class ErrorsNoFailStub implements IErrors { class PlatformsData implements IPlatformsData { platformsNames = ["android", "ios"]; getPlatformData(platform: string): IPlatformData { - if(_.includes(this.platformsNames, platform)) { + if (_.includes(this.platformsNames, platform)) { return new PlatformData(); } @@ -107,7 +105,7 @@ function createTestInjector() { testInjector.registerCommand("platform|add", PlatformAddCommandLib.AddPlatformCommand); testInjector.registerCommand("platform|remove", PlatformRemoveCommandLib.RemovePlatformCommand); testInjector.registerCommand("platform|update", PlatformUpdateCommandLib.UpdatePlatformCommand); - testInjector.register("lockfile", { }); + testInjector.register("lockfile", {}); testInjector.register("resources", {}); testInjector.register("commandsServiceProvider", { registerDynamicSubCommands: () => { /* intentionally left blank */ } @@ -121,11 +119,7 @@ function createTestInjector() { prepareNodeModulesFolder: () => { /* intentionally left blank */ } }); testInjector.register("pluginsService", { - getAllInstalledPlugins: () => { - return (() => { - return []; - }).future()(); - } + getAllInstalledPlugins: async () => [] }); testInjector.register("projectFilesManager", ProjectFilesManagerLib.ProjectFilesManager); testInjector.register("hooksService", stubs.HooksServiceStub); @@ -157,271 +151,255 @@ describe('Platform Service Tests', () => { describe("platform commands tests", () => { describe("#AddPlatformCommand", () => { - it("is not executed when platform is not passed", () => { + it("is not executed when platform is not passed", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|add", []).wait(); + await commandsService.tryExecuteCommand("platform|add", []); assert.isFalse(isCommandExecuted); }); - it("is not executed when platform is not valid", () => { + it("is not executed when platform is not valid", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { if (commandName !== "help") { - if (commandName !== "help") { - isCommandExecuted = true; - } + isCommandExecuted = true; } - return false; - }).future()(); + } + + return false; }; - commandsService.tryExecuteCommand("platform|add", ["invalidPlatform"]).wait(); + await commandsService.tryExecuteCommand("platform|add", ["invalidPlatform"]); assert.isFalse(isCommandExecuted); }); - it("is executed when platform is valid", () => { + it("is executed when platform is valid", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|add", ["android"]).wait(); + await commandsService.tryExecuteCommand("platform|add", ["android"]); assert.isTrue(isCommandExecuted); }); - it("is executed when all platforms are valid", () => { + it("is executed when all platforms are valid", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|add", ["android", "ios"]).wait(); + await commandsService.tryExecuteCommand("platform|add", ["android", "ios"]); assert.isTrue(isCommandExecuted); }); - it("is not executed when at least one platform is not valid", () => { + it("is not executed when at least one platform is not valid", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|add", ["ios", "invalid"]).wait(); + await commandsService.tryExecuteCommand("platform|add", ["ios", "invalid"]); assert.isFalse(isCommandExecuted); }); }); describe("#RemovePlatformCommand", () => { - it("is not executed when platform is not passed", () => { + it("is not executed when platform is not passed", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|remove", []).wait(); + await commandsService.tryExecuteCommand("platform|remove", []); assert.isFalse(isCommandExecuted); }); - it("is not executed when platform is not valid", () => { + it("is not executed when platform is not valid", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|remove", ["invalidPlatform"]).wait(); + await commandsService.tryExecuteCommand("platform|remove", ["invalidPlatform"]); assert.isFalse(isCommandExecuted); }); - it("is executed when platform is valid", () => { + it("is executed when platform is valid", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + return false; }; - commandsService.tryExecuteCommand("platform|remove", ["android"]).wait(); + await commandsService.tryExecuteCommand("platform|remove", ["android"]); assert.isTrue(isCommandExecuted); }); - it("is executed when all platforms are valid", () => { + it("is executed when all platforms are valid", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|remove", ["android", "ios"]).wait(); + await commandsService.tryExecuteCommand("platform|remove", ["android", "ios"]); assert.isTrue(isCommandExecuted); }); - it("is not executed when at least one platform is not valid", () => { + it("is not executed when at least one platform is not valid", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|remove", ["ios", "invalid"]).wait(); + await commandsService.tryExecuteCommand("platform|remove", ["ios", "invalid"]); assert.isFalse(isCommandExecuted); }); }); describe("#CleanPlatformCommand", () => { - it("is not executed when platform is not passed", () => { + it("is not executed when platform is not passed", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|clean", []).wait(); + await commandsService.tryExecuteCommand("platform|clean", []); assert.isFalse(isCommandExecuted); }); - it("is not executed when platform is not valid", () => { + it("is not executed when platform is not valid", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|clean", ["invalidPlatform"]).wait(); + await commandsService.tryExecuteCommand("platform|clean", ["invalidPlatform"]); assert.isFalse(isCommandExecuted); }); - it("is executed when platform is valid", () => { + it("is executed when platform is valid", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|add", ["android"]).wait(); - commandsService.tryExecuteCommand("platform|clean", ["android"]).wait(); + await commandsService.tryExecuteCommand("platform|add", ["android"]); + await commandsService.tryExecuteCommand("platform|clean", ["android"]); assert.isTrue(isCommandExecuted); }); - it("is not executed when platform is not added", () => { + it("is not executed when platform is not added", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|clean", ["android"]).wait(); + await commandsService.tryExecuteCommand("platform|clean", ["android"]); assert.isFalse(isCommandExecuted); }); - it("is executed when all platforms are valid", () => { + it("is executed when all platforms are valid", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|add", ["android"]).wait(); - commandsService.tryExecuteCommand("platform|add", ["ios"]).wait(); - commandsService.tryExecuteCommand("platform|clean", ["android", "ios"]).wait(); + await commandsService.tryExecuteCommand("platform|add", ["android"]); + await commandsService.tryExecuteCommand("platform|add", ["ios"]); + await commandsService.tryExecuteCommand("platform|clean", ["android", "ios"]); assert.isTrue(isCommandExecuted); }); - it("is not executed when at least on platform is not added", () => { + it("is not executed when at least on platform is not added", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|clean", ["android", "ios"]).wait(); + await commandsService.tryExecuteCommand("platform|clean", ["android", "ios"]); assert.isFalse(isCommandExecuted); }); - it("is not executed when at least one platform is not valid", () => { + it("is not executed when at least one platform is not valid", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|clean", ["ios", "invalid"]).wait(); + await commandsService.tryExecuteCommand("platform|clean", ["ios", "invalid"]); assert.isFalse(isCommandExecuted); }); - it("will call removePlatform and addPlatform on the platformService passing the provided platforms", () => { + it("will call removePlatform and addPlatform on the platformService passing the provided platforms", async () => { let platformActions: { action: string, platforms: string[] }[] = []; testInjector.registerCommand("platform|clean", CleanCommand); let cleanCommand = testInjector.resolveCommand("platform|clean"); @@ -430,13 +408,13 @@ describe('Platform Service Tests', () => { platformActions.push({ action: "removePlatforms", platforms }); }; - platformService.addPlatforms = (platforms: string[]) => { - return (() => { - platformActions.push({ action: "addPlatforms", platforms }); - }).future()(); + platformService.addPlatforms = async (platforms: string[]) => { + + platformActions.push({ action: "addPlatforms", platforms }); + }; - cleanCommand.execute(["ios"]).wait(); + await cleanCommand.execute(["ios"]); let expectedPlatformActions = [ { action: "removePlatforms", platforms: ["ios"] }, @@ -448,78 +426,73 @@ describe('Platform Service Tests', () => { }); describe("#UpdatePlatformCommand", () => { - it("is not executed when platform is not passed", () => { + it("is not executed when platform is not passed", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|update", []).wait(); + await commandsService.tryExecuteCommand("platform|update", []); assert.isFalse(isCommandExecuted); }); - it("is not executed when platform is not valid", () => { + it("is not executed when platform is not valid", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|update", ["invalidPlatform"]).wait(); + await commandsService.tryExecuteCommand("platform|update", ["invalidPlatform"]); assert.isFalse(isCommandExecuted); }); - it("is executed when platform is valid", () => { + it("is executed when platform is valid", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|update", ["android"]).wait(); + await commandsService.tryExecuteCommand("platform|update", ["android"]); assert.isTrue(isCommandExecuted); }); - it("is executed when all platforms are valid", () => { + it("is executed when all platforms are valid", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|update", ["android", "ios"]).wait(); + await commandsService.tryExecuteCommand("platform|update", ["android", "ios"]); assert.isTrue(isCommandExecuted); }); - it("is not executed when at least one platform is not valid", () => { + it("is not executed when at least one platform is not valid", async () => { isCommandExecuted = false; - commandsService.executeCommandUnchecked = (commandName: string): IFuture => { - return (() => { - if (commandName !== "help") { - isCommandExecuted = true; - } - return false; - }).future()(); + commandsService.executeCommandUnchecked = async (commandName: string): Promise => { + if (commandName !== "help") { + isCommandExecuted = true; + } + + return false; }; - commandsService.tryExecuteCommand("platform|update", ["ios", "invalid"]).wait(); + await commandsService.tryExecuteCommand("platform|update", ["ios", "invalid"]); assert.isFalse(isCommandExecuted); }); }); diff --git a/test/platform-service.ts b/test/platform-service.ts index 47161acbd0..806d290c33 100644 --- a/test/platform-service.ts +++ b/test/platform-service.ts @@ -7,7 +7,6 @@ import * as optionsLib from "../lib/options"; import * as hostInfoLib from "../lib/common/host-info"; import * as ProjectFilesManagerLib from "../lib/common/services/project-files-manager"; import * as path from "path"; -import Future = require("fibers/future"); import { assert } from "chai"; import { DeviceAppDataFactory } from "../lib/common/mobile/device-app-data/device-app-data-factory"; import { LocalToDevicePathDataFactory } from "../lib/common/mobile/local-to-device-path-data-factory"; @@ -47,17 +46,15 @@ function createTestInjector() { testInjector.register("staticConfig", StaticConfigLib.StaticConfig); testInjector.register("nodeModulesBuilder", { prepareNodeModules: () => { - return Future.fromResult(); + return Promise.resolve(); } }); testInjector.register("pluginsService", { getAllInstalledPlugins: () => { - return (() => { - return []; - }).future()(); + return []; }, ensureAllDependenciesAreInstalled: () => { - return Future.fromResult(); + return Promise.resolve(); } }); testInjector.register("projectFilesManager", ProjectFilesManagerLib.ProjectFilesManager); @@ -72,10 +69,8 @@ function createTestInjector() { testInjector.register("devicePlatformsConstants", DevicePlatformsConstants); testInjector.register("xmlValidator", XmlValidator); testInjector.register("npm", { - uninstall: () => { - return (() => { - return true; - }).future()(); + uninstall: async () => { + return true; } }); testInjector.register("childProcess", ChildProcessLib.ChildProcess); @@ -95,26 +90,26 @@ describe('Platform Service Tests', () => { describe("add platform unit tests", () => { describe("#add platform()", () => { - it("should not fail if platform is not normalized", () => { + it("should not fail if platform is not normalized", async () => { let fs = testInjector.resolve("fs"); fs.exists = () => false; - platformService.addPlatforms(["Android"]).wait(); - platformService.addPlatforms(["ANDROID"]).wait(); - platformService.addPlatforms(["AnDrOiD"]).wait(); - platformService.addPlatforms(["androiD"]).wait(); + await platformService.addPlatforms(["Android"]); + await platformService.addPlatforms(["ANDROID"]); + await platformService.addPlatforms(["AnDrOiD"]); + await platformService.addPlatforms(["androiD"]); - platformService.addPlatforms(["iOS"]).wait(); - platformService.addPlatforms(["IOS"]).wait(); - platformService.addPlatforms(["IoS"]).wait(); - platformService.addPlatforms(["iOs"]).wait(); + await platformService.addPlatforms(["iOS"]); + await platformService.addPlatforms(["IOS"]); + await platformService.addPlatforms(["IoS"]); + await platformService.addPlatforms(["iOs"]); }); - it("should fail if platform is already installed", () => { + it("should fail if platform is already installed", async () => { // By default fs.exists returns true, so the platforms directory should exists - (() => platformService.addPlatforms(["android"]).wait()).should.throw(); - (() => platformService.addPlatforms(["ios"]).wait()).should.throw(); + await assert.isRejected(platformService.addPlatforms(["android"])); + await assert.isRejected(platformService.addPlatforms(["ios"])); }); - it("should fail if npm is unavalible", () => { + it("should fail if npm is unavalible", async () => { let fs = testInjector.resolve("fs"); fs.exists = () => false; @@ -123,14 +118,14 @@ describe('Platform Service Tests', () => { npmInstallationManager.install = () => { throw new Error(errorMessage); }; try { - platformService.addPlatforms(["android"]).wait(); + await platformService.addPlatforms(["android"]); } catch (err) { assert.equal(errorMessage, err.message); } }); }); describe("#add platform(ios)", () => { - it("should call validate method", () => { + it("should call validate method", async () => { let fs = testInjector.resolve("fs"); fs.exists = () => false; @@ -142,14 +137,14 @@ describe('Platform Service Tests', () => { }; try { - platformService.addPlatforms(["ios"]).wait(); + await platformService.addPlatforms(["ios"]); } catch (err) { assert.equal(errorMessage, err.message); } }); }); describe("#add platform(android)", () => { - it("should fail if java, ant or android are not installed", () => { + it("should fail if java, ant or android are not installed", async () => { let fs = testInjector.resolve("fs"); fs.exists = () => false; @@ -161,7 +156,7 @@ describe('Platform Service Tests', () => { }; try { - platformService.addPlatforms(["android"]).wait(); + await platformService.addPlatforms(["android"]); } catch (err) { assert.equal(errorMessage, err.message); } @@ -175,9 +170,9 @@ describe('Platform Service Tests', () => { (() => platformService.removePlatforms(["android"])).should.throw(); (() => platformService.removePlatforms(["ios"])).should.throw(); }); - it("shouldn't fail when platforms are added", () => { + it("shouldn't fail when platforms are added", async () => { testInjector.resolve("fs").exists = () => false; - platformService.addPlatforms(["android"]).wait(); + await platformService.addPlatforms(["android"]); testInjector.resolve("fs").exists = () => true; platformService.removePlatforms(["android"]); @@ -187,17 +182,17 @@ describe('Platform Service Tests', () => { // TODO: Commented as it doesn't seem correct. Check what's the case and why it's been expected to fail. // describe("list platform unit tests", () => { // it("fails when platforms are not added", () => { - // assert.throws(() => platformService.getAvailablePlatforms().wait()); + // assert.throws(async () => await platformService.getAvailablePlatforms()); // }); // }); describe("update Platform", () => { describe("#updatePlatform(platform)", () => { - it("should fail when the versions are the same", () => { + it("should fail when the versions are the same", async () => { let npmInstallationManager: INpmInstallationManager = testInjector.resolve("npmInstallationManager"); - npmInstallationManager.getLatestVersion = () => (() => "0.2.0").future()(); + npmInstallationManager.getLatestVersion = async () => "0.2.0"; - (() => platformService.updatePlatforms(["android"]).wait()).should.throw(); + await assert.isRejected(platformService.updatePlatforms(["android"])); }); }); }); @@ -229,7 +224,7 @@ describe('Platform Service Tests', () => { return { tempFolder, appFolderPath, app1FolderPath, appDestFolderPath, appResourcesFolderPath }; } - function testPreparePlatform(platformToTest: string, release?: boolean) { + async function testPreparePlatform(platformToTest: string, release?: boolean) { let testDirData = prepareDirStructure(); // Add platform specific files to app and app1 folders @@ -256,14 +251,14 @@ describe('Platform Service Tests', () => { projectRoot: testDirData.tempFolder, platformProjectService: { prepareProject: (): any => null, - validate: () => Future.fromResult(), - createProject: (projectRoot: string, frameworkDir: string) => Future.fromResult(), - interpolateData: (projectRoot: string) => Future.fromResult(), + validate: () => Promise.resolve(), + createProject: (projectRoot: string, frameworkDir: string) => Promise.resolve(), + interpolateData: (projectRoot: string) => Promise.resolve(), afterCreateProject: (projectRoot: string): any => null, getAppResourcesDestinationDirectoryPath: () => "", - processConfigurationFilesFromAppResources: () => Future.fromResult(), + processConfigurationFilesFromAppResources: () => Promise.resolve(), ensureConfigurationFileInAppResources: (): any => null, - interpolateConfigurationFile: () => Future.fromResult(), + interpolateConfigurationFile: () => Promise.resolve(), isPlatformPrepared: (projectRoot: string) => false } }; @@ -275,7 +270,7 @@ describe('Platform Service Tests', () => { platformService = testInjector.resolve("platformService"); let options: IOptions = testInjector.resolve("options"); options.release = release; - platformService.preparePlatform(platformToTest).wait(); + await platformService.preparePlatform(platformToTest); let test1FileName = platformToTest.toLowerCase() === "ios" ? "test1.js" : "test2.js"; let test2FileName = platformToTest.toLowerCase() === "ios" ? "test2.js" : "test1.js"; @@ -296,23 +291,23 @@ describe('Platform Service Tests', () => { } } - it("should process only files in app folder when preparing for iOS platform", () => { - testPreparePlatform("iOS"); + it("should process only files in app folder when preparing for iOS platform", async () => { + await testPreparePlatform("iOS"); }); - it("should process only files in app folder when preparing for Android platform", () => { - testPreparePlatform("Android"); + it("should process only files in app folder when preparing for Android platform", async () => { + await testPreparePlatform("Android"); }); - it("should process only files in app folder when preparing for iOS platform", () => { - testPreparePlatform("iOS", true); + it("should process only files in app folder when preparing for iOS platform", async () => { + await testPreparePlatform("iOS", true); }); - it("should process only files in app folder when preparing for Android platform", () => { - testPreparePlatform("Android", true); + it("should process only files in app folder when preparing for Android platform", async () => { + await testPreparePlatform("Android", true); }); - it("invalid xml is caught", () => { + it("invalid xml is caught", async () => { require("colors"); let testDirData = prepareDirStructure(); @@ -330,14 +325,14 @@ describe('Platform Service Tests', () => { projectRoot: testDirData.tempFolder, platformProjectService: { prepareProject: (): any => null, - validate: () => Future.fromResult(), - createProject: (projectRoot: string, frameworkDir: string) => Future.fromResult(), - interpolateData: (projectRoot: string) => Future.fromResult(), + validate: () => Promise.resolve(), + createProject: (projectRoot: string, frameworkDir: string) => Promise.resolve(), + interpolateData: (projectRoot: string) => Promise.resolve(), afterCreateProject: (projectRoot: string): any => null, getAppResourcesDestinationDirectoryPath: () => "", - processConfigurationFilesFromAppResources: () => Future.fromResult(), + processConfigurationFilesFromAppResources: () => Promise.resolve(), ensureConfigurationFileInAppResources: (): any => null, - interpolateConfigurationFile: () => Future.fromResult(), + interpolateConfigurationFile: () => Promise.resolve(), isPlatformPrepared: (projectRoot: string) => false } }; @@ -351,7 +346,7 @@ describe('Platform Service Tests', () => { let warnings: string = ""; try { testInjector.resolve("$logger").warn = (text: string) => warnings += text; - platformService.preparePlatform("android").wait(); + await platformService.preparePlatform("android"); } finally { testInjector.resolve("$logger").warn = oldLoggerWarner; } diff --git a/test/plugin-prepare.ts b/test/plugin-prepare.ts index 5ca412cc36..60604c9beb 100644 --- a/test/plugin-prepare.ts +++ b/test/plugin-prepare.ts @@ -1,5 +1,5 @@ -import {assert} from "chai"; -import {NpmPluginPrepare} from "../lib/tools/node-modules/node-modules-dest-copy"; +import { assert } from "chai"; +import { NpmPluginPrepare } from "../lib/tools/node-modules/node-modules-dest-copy"; require("should"); @@ -14,26 +14,26 @@ class TestNpmPluginPrepare extends NpmPluginPrepare { return this.previouslyPrepared; } - protected beforePrepare(dependencies: IDictionary, platform: string): void { + protected async beforePrepare(dependencies: IDictionary, platform: string): Promise { _.values(dependencies).forEach(d => { this.preparedDependencies[d.name] = true; }); } - protected afterPrepare(dependencies: IDictionary, platform: string): void { + protected async afterPrepare(dependencies: IDictionary, platform: string): Promise { // DO NOTHING } } describe("Plugin preparation", () => { - it("skips prepare if no plugins", () => { + it("skips prepare if no plugins", async () => { const pluginPrepare = new TestNpmPluginPrepare({}); - pluginPrepare.preparePlugins({}, "android"); + await pluginPrepare.preparePlugins({}, "android"); assert.deepEqual({}, pluginPrepare.preparedDependencies); }); - it("skips prepare if every plugin prepared", () => { - const pluginPrepare = new TestNpmPluginPrepare({"tns-core-modules-widgets": true}); + it("skips prepare if every plugin prepared", async () => { + const pluginPrepare = new TestNpmPluginPrepare({ "tns-core-modules-widgets": true }); const testDependencies: IDictionary = { "0": { name: "tns-core-modules-widgets", @@ -41,12 +41,12 @@ describe("Plugin preparation", () => { nativescript: null, } }; - pluginPrepare.preparePlugins(testDependencies, "android"); + await pluginPrepare.preparePlugins(testDependencies, "android"); assert.deepEqual({}, pluginPrepare.preparedDependencies); }); - it("saves prepared plugins after preparation", () => { - const pluginPrepare = new TestNpmPluginPrepare({"tns-core-modules-widgets": true}); + it("saves prepared plugins after preparation", async () => { + const pluginPrepare = new TestNpmPluginPrepare({ "tns-core-modules-widgets": true }); const testDependencies: IDictionary = { "0": { name: "tns-core-modules-widgets", @@ -59,8 +59,8 @@ describe("Plugin preparation", () => { nativescript: null, } }; - pluginPrepare.preparePlugins(testDependencies, "android"); - const prepareData = {"tns-core-modules-widgets": true, "nativescript-calendar": true}; + await pluginPrepare.preparePlugins(testDependencies, "android"); + const prepareData = { "tns-core-modules-widgets": true, "nativescript-calendar": true }; assert.deepEqual(prepareData, pluginPrepare.preparedDependencies); }); }); diff --git a/test/plugin-variables-service.ts b/test/plugin-variables-service.ts index 17662cfb65..871510c1d2 100644 --- a/test/plugin-variables-service.ts +++ b/test/plugin-variables-service.ts @@ -1,17 +1,16 @@ -import {assert} from "chai"; -import {Errors} from "../lib/common/errors"; -import {FileSystem} from "../lib/common/file-system"; -import Future = require("fibers/future"); -import {HostInfo} from "../lib/common/host-info"; -import {Options} from "../lib/options"; -import {PluginVariablesHelper} from "../lib/common/plugin-variables-helper"; -import {PluginVariablesService} from "../lib/services/plugin-variables-service"; -import {ProjectData} from "../lib/project-data"; -import {ProjectDataService} from "../lib/services/project-data-service"; -import {ProjectHelper} from "../lib/common/project-helper"; -import {StaticConfig} from "../lib/config"; -import {MessagesService} from "../lib/common/services/messages-service"; -import {Yok} from '../lib/common/yok'; +import { assert } from "chai"; +import { Errors } from "../lib/common/errors"; +import { FileSystem } from "../lib/common/file-system"; +import { HostInfo } from "../lib/common/host-info"; +import { Options } from "../lib/options"; +import { PluginVariablesHelper } from "../lib/common/plugin-variables-helper"; +import { PluginVariablesService } from "../lib/services/plugin-variables-service"; +import { ProjectData } from "../lib/project-data"; +import { ProjectDataService } from "../lib/services/project-data-service"; +import { ProjectHelper } from "../lib/common/project-helper"; +import { StaticConfig } from "../lib/config"; +import { MessagesService } from "../lib/common/services/messages-service"; +import { Yok } from '../lib/common/yok'; import * as stubs from './stubs'; import * as path from "path"; import * as temp from "temp"; @@ -42,21 +41,19 @@ function createTestInjector(): IInjector { return testInjector; } -function createProjectFile(testInjector: IInjector): IFuture { - return (() => { - let tempFolder = temp.mkdirSync("pluginVariablesService"); +async function createProjectFile(testInjector: IInjector): Promise { + let tempFolder = temp.mkdirSync("pluginVariablesService"); - let options = testInjector.resolve("options"); - options.path = tempFolder; + let options = testInjector.resolve("options"); + options.path = tempFolder; - let projectData = { - "name": "myProject", - "nativescript": { } - }; - testInjector.resolve("fs").writeJson(path.join(tempFolder, "package.json"), projectData); + let projectData = { + "name": "myProject", + "nativescript": {} + }; + testInjector.resolve("fs").writeJson(path.join(tempFolder, "package.json"), projectData); - return tempFolder; - }).future()(); + return tempFolder; } function createPluginData(pluginVariables: any): IPluginData { @@ -88,10 +85,10 @@ describe("Plugin Variables service", () => { let helpers = require("./../lib/common/helpers"); helpers.isInteractive = () => false; }); - it("fails when no --var option and no default value are specified", () => { - createProjectFile(testInjector).wait(); + it("fails when no --var option and no default value are specified", async () => { + await createProjectFile(testInjector); - let pluginVariables = { "MY_TEST_PLUGIN_VARIABLE": { } }; + let pluginVariables = { "MY_TEST_PLUGIN_VARIABLE": {} }; let pluginData = createPluginData(pluginVariables); let pluginVariablesService = testInjector.resolve("pluginVariablesService"); @@ -99,25 +96,25 @@ describe("Plugin Variables service", () => { let actualError: string = null; try { - pluginVariablesService.savePluginVariablesInProjectFile(pluginData).wait(); - } catch(err) { + await pluginVariablesService.savePluginVariablesInProjectFile(pluginData); + } catch (err) { actualError = err.message; } assert.equal(expectedError, actualError); }); - it("does not fail when --var option is specified", () => { - createProjectFile(testInjector).wait(); + it("does not fail when --var option is specified", async () => { + await createProjectFile(testInjector); let pluginVariableValue = "myAppId"; testInjector.resolve("options").var = { "MY_APP_ID": pluginVariableValue }; - let pluginVariables = { "MY_APP_ID": { } }; + let pluginVariables = { "MY_APP_ID": {} }; let pluginData = createPluginData(pluginVariables); let pluginVariablesService = testInjector.resolve("pluginVariablesService"); - pluginVariablesService.savePluginVariablesInProjectFile(pluginData).wait(); + await pluginVariablesService.savePluginVariablesInProjectFile(pluginData); let fs = testInjector.resolve("fs"); let projectData = testInjector.resolve("projectData"); @@ -126,14 +123,14 @@ describe("Plugin Variables service", () => { let projectFileContent = fs.readJson(path.join(projectData.projectDir, "package.json")); assert.equal(pluginVariableValue, projectFileContent[staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE][`${pluginData.name}-variables`]["MY_APP_ID"]); }); - it("does not fail when default value is specified", () => { - createProjectFile(testInjector).wait(); + it("does not fail when default value is specified", async () => { + await createProjectFile(testInjector); let defaultPluginValue = "myDefaultValue"; let pluginVariables = { "MY_TEST_PLUGIN_VARIABLE": { defaultValue: defaultPluginValue } }; let pluginData = createPluginData(pluginVariables); let pluginVariablesService = testInjector.resolve("pluginVariablesService"); - pluginVariablesService.savePluginVariablesInProjectFile(pluginData).wait(); + await pluginVariablesService.savePluginVariablesInProjectFile(pluginData); let fs = testInjector.resolve("fs"); let projectData = testInjector.resolve("projectData"); @@ -149,19 +146,17 @@ describe("Plugin Variables service", () => { let helpers = require("./../lib/common/helpers"); helpers.isInteractive = () => true; }); - it("prompt for plugin variable value when no --var option and no default value are specified", () => { - createProjectFile(testInjector).wait(); + it("prompt for plugin variable value when no --var option and no default value are specified", async () => { + await createProjectFile(testInjector); let pluginVariableValue = "testAppURL"; let prompter = testInjector.resolve("prompter"); - prompter.get = () => { - return Future.fromResult({ "APP_URL": pluginVariableValue }); - }; + prompter.get = async () => ({ "APP_URL": pluginVariableValue }); - let pluginVariables = { "APP_URL": { } }; + let pluginVariables = { "APP_URL": {} }; let pluginData = createPluginData(pluginVariables); let pluginVariablesService = testInjector.resolve("pluginVariablesService"); - pluginVariablesService.savePluginVariablesInProjectFile(pluginData).wait(); + await pluginVariablesService.savePluginVariablesInProjectFile(pluginData); let fs = testInjector.resolve("fs"); let projectData = testInjector.resolve("projectData"); @@ -170,14 +165,14 @@ describe("Plugin Variables service", () => { let projectFileContent = fs.readJson(path.join(projectData.projectDir, "package.json")); assert.equal(pluginVariableValue, projectFileContent[staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE][`${pluginData.name}-variables`]["APP_URL"]); }); - it("does not prompt for plugin variable value when default value is specified", () => { - createProjectFile(testInjector).wait(); + it("does not prompt for plugin variable value when default value is specified", async () => { + await createProjectFile(testInjector); let defaultPluginValue = "myAppNAme"; let pluginVariables = { "APP_NAME": { defaultValue: defaultPluginValue } }; let pluginData = createPluginData(pluginVariables); let pluginVariablesService = testInjector.resolve("pluginVariablesService"); - pluginVariablesService.savePluginVariablesInProjectFile(pluginData).wait(); + await pluginVariablesService.savePluginVariablesInProjectFile(pluginData); let fs = testInjector.resolve("fs"); let projectData = testInjector.resolve("projectData"); @@ -186,18 +181,18 @@ describe("Plugin Variables service", () => { let projectFileContent = fs.readJson(path.join(projectData.projectDir, "package.json")); assert.equal(defaultPluginValue, projectFileContent[staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE][`${pluginData.name}-variables`]["APP_NAME"]); }); - it("does not prompt for plugin variable value when --var option is specified", () => { - createProjectFile(testInjector).wait(); + it("does not prompt for plugin variable value when --var option is specified", async () => { + await createProjectFile(testInjector); let pluginVariableValue = "pencho.goshko"; testInjector.resolve("options").var = { "USERNAME": pluginVariableValue }; - let pluginVariables = { "USERNAME": { } }; + let pluginVariables = { "USERNAME": {} }; let pluginData = createPluginData(pluginVariables); let pluginVariablesService = testInjector.resolve("pluginVariablesService"); - pluginVariablesService.savePluginVariablesInProjectFile(pluginData).wait(); + await pluginVariablesService.savePluginVariablesInProjectFile(pluginData); let fs = testInjector.resolve("fs"); let projectData = testInjector.resolve("projectData"); @@ -209,10 +204,10 @@ describe("Plugin Variables service", () => { }); describe("plugin interpolation", () => { - it("fails when the plugin value is undefined", () => { - let tempFolder = createProjectFile(testInjector).wait(); + it("fails when the plugin value is undefined", async () => { + let tempFolder = await createProjectFile(testInjector); - let pluginVariables = { "MY_VAR": { } }; + let pluginVariables = { "MY_VAR": {} }; let pluginData = createPluginData(pluginVariables); let fs: IFileSystem = testInjector.resolve("fs"); @@ -224,16 +219,16 @@ describe("Plugin Variables service", () => { let expectedError = "Unable to find the value for MY_VAR plugin variable into project package.json file. Verify that your package.json file is correct and try again."; let error: string = null; try { - pluginVariablesService.interpolatePluginVariables(pluginData, filePath).wait(); - } catch(err) { + await pluginVariablesService.interpolatePluginVariables(pluginData, filePath); + } catch (err) { error = err.message; } assert.equal(error, expectedError); }); - it("interpolates correctly plugin variable value", () => { - let tempFolder = createProjectFile(testInjector).wait(); + it("interpolates correctly plugin variable value", async () => { + let tempFolder = await createProjectFile(testInjector); let projectData: IProjectData = testInjector.resolve("projectData"); let fs: IFileSystem = testInjector.resolve("fs"); @@ -246,33 +241,33 @@ describe("Plugin Variables service", () => { }; fs.writeJson(packageJsonFilePath, data); - let pluginVariables = { "FB_APP_NAME": { } }; + let pluginVariables = { "FB_APP_NAME": {} }; let pluginData = createPluginData(pluginVariables); let pluginVariablesService = testInjector.resolve("pluginVariablesService"); let pluginConfigurationFileContent = '' + '' + - '' + - '' + - '' + + '' + + '' + + '' + ''; let filePath = path.join(tempFolder, "myfile"); fs.writeFile(filePath, pluginConfigurationFileContent); - pluginVariablesService.interpolatePluginVariables(pluginData, filePath).wait(); + await pluginVariablesService.interpolatePluginVariables(pluginData, filePath); let result = fs.readText(filePath); let expectedResult = '' + '' + - '' + - '' + - '' + + '' + + '' + + '' + ''; assert.equal(result, expectedResult); }); - it("interpolates correctly case sensive plugin variable value", () => { - let tempFolder = createProjectFile(testInjector).wait(); + it("interpolates correctly case sensive plugin variable value", async () => { + let tempFolder = await createProjectFile(testInjector); let projectData: IProjectData = testInjector.resolve("projectData"); let fs: IFileSystem = testInjector.resolve("fs"); @@ -285,33 +280,33 @@ describe("Plugin Variables service", () => { }; fs.writeJson(packageJsonFilePath, data); - let pluginVariables = { "FB_APP_NAME": { } }; + let pluginVariables = { "FB_APP_NAME": {} }; let pluginData = createPluginData(pluginVariables); let pluginVariablesService = testInjector.resolve("pluginVariablesService"); let pluginConfigurationFileContent = '' + '' + - '' + - '' + - '' + + '' + + '' + + '' + ''; let filePath = path.join(tempFolder, "myfile"); fs.writeFile(filePath, pluginConfigurationFileContent); - pluginVariablesService.interpolatePluginVariables(pluginData, filePath).wait(); + await pluginVariablesService.interpolatePluginVariables(pluginData, filePath); let result = fs.readText(filePath); let expectedResult = '' + '' + - '' + - '' + - '' + + '' + + '' + + '' + ''; assert.equal(result, expectedResult); }); - it("interpolates correctly more than one plugin variables values", () => { - let tempFolder = createProjectFile(testInjector).wait(); + it("interpolates correctly more than one plugin variables values", async () => { + let tempFolder = await createProjectFile(testInjector); let projectData: IProjectData = testInjector.resolve("projectData"); let fs: IFileSystem = testInjector.resolve("fs"); @@ -324,28 +319,28 @@ describe("Plugin Variables service", () => { }; fs.writeJson(packageJsonFilePath, data); - let pluginVariables = { "FB_APP_NAME": { }, "FB_APP_URL": { } }; + let pluginVariables = { "FB_APP_NAME": {}, "FB_APP_URL": {} }; let pluginData = createPluginData(pluginVariables); let pluginVariablesService = testInjector.resolve("pluginVariablesService"); let pluginConfigurationFileContent = '' + '' + - '' + - '' + - '' + - '' + + '' + + '' + + '' + + '' + ''; let filePath = path.join(tempFolder, "myfile"); fs.writeFile(filePath, pluginConfigurationFileContent); - pluginVariablesService.interpolatePluginVariables(pluginData, filePath).wait(); + await pluginVariablesService.interpolatePluginVariables(pluginData, filePath); let result = fs.readText(filePath); let expectedResult = '' + '' + - '' + - '' + - '' + - '' + + '' + + '' + + '' + + '' + ''; assert.equal(result, expectedResult); diff --git a/test/plugins-service.ts b/test/plugins-service.ts index 6ca71610c2..54bf3424a4 100644 --- a/test/plugins-service.ts +++ b/test/plugins-service.ts @@ -1,36 +1,35 @@ -import {Yok} from '../lib/common/yok'; -import future = require("fibers/future"); +import { Yok } from '../lib/common/yok'; import * as stubs from './stubs'; -import {NodePackageManager} from "../lib/node-package-manager"; -import {NpmInstallationManager} from "../lib/npm-installation-manager"; -import {FileSystem} from "../lib/common/file-system"; -import {ProjectData} from "../lib/project-data"; -import {ChildProcess} from "../lib/common/child-process"; -import {PlatformService} from '../lib/services/platform-service'; -import {Options} from "../lib/options"; -import {CommandsService} from "../lib/common/services/commands-service"; -import {StaticConfig} from "../lib/config"; -import {HostInfo} from "../lib/common/host-info"; -import {Errors} from "../lib/common/errors"; -import {ProjectHelper} from "../lib/common/project-helper"; -import {PlatformsData} from "../lib/platforms-data"; -import {ProjectDataService} from "../lib/services/project-data-service"; -import {ProjectFilesManager} from "../lib/common/services/project-files-manager"; -import {ResourceLoader} from "../lib/common/resource-loader"; -import {PluginsService} from "../lib/services/plugins-service"; -import {AddPluginCommand} from "../lib/commands/plugin/add-plugin"; -import {MessagesService} from "../lib/common/services/messages-service"; -import {NodeModulesBuilder} from "../lib/tools/node-modules/node-modules-builder"; -import {AndroidProjectService} from "../lib/services/android-project-service"; -import {AndroidToolsInfo} from "../lib/android-tools-info"; -import {assert} from "chai"; -import {DeviceAppDataFactory} from "../lib/common/mobile/device-app-data/device-app-data-factory"; -import {LocalToDevicePathDataFactory} from "../lib/common/mobile/local-to-device-path-data-factory"; -import {MobileHelper} from "../lib/common/mobile/mobile-helper"; -import {ProjectFilesProvider} from "../lib/providers/project-files-provider"; -import {DeviceAppDataProvider} from "../lib/providers/device-app-data-provider"; -import {MobilePlatformsCapabilities} from "../lib/mobile-platforms-capabilities"; -import {DevicePlatformsConstants} from "../lib/common/mobile/device-platforms-constants"; +import { NodePackageManager } from "../lib/node-package-manager"; +import { NpmInstallationManager } from "../lib/npm-installation-manager"; +import { FileSystem } from "../lib/common/file-system"; +import { ProjectData } from "../lib/project-data"; +import { ChildProcess } from "../lib/common/child-process"; +import { PlatformService } from '../lib/services/platform-service'; +import { Options } from "../lib/options"; +import { CommandsService } from "../lib/common/services/commands-service"; +import { StaticConfig } from "../lib/config"; +import { HostInfo } from "../lib/common/host-info"; +import { Errors } from "../lib/common/errors"; +import { ProjectHelper } from "../lib/common/project-helper"; +import { PlatformsData } from "../lib/platforms-data"; +import { ProjectDataService } from "../lib/services/project-data-service"; +import { ProjectFilesManager } from "../lib/common/services/project-files-manager"; +import { ResourceLoader } from "../lib/common/resource-loader"; +import { PluginsService } from "../lib/services/plugins-service"; +import { AddPluginCommand } from "../lib/commands/plugin/add-plugin"; +import { MessagesService } from "../lib/common/services/messages-service"; +import { NodeModulesBuilder } from "../lib/tools/node-modules/node-modules-builder"; +import { AndroidProjectService } from "../lib/services/android-project-service"; +import { AndroidToolsInfo } from "../lib/android-tools-info"; +import { assert } from "chai"; +import { DeviceAppDataFactory } from "../lib/common/mobile/device-app-data/device-app-data-factory"; +import { LocalToDevicePathDataFactory } from "../lib/common/mobile/local-to-device-path-data-factory"; +import { MobileHelper } from "../lib/common/mobile/mobile-helper"; +import { ProjectFilesProvider } from "../lib/providers/project-files-provider"; +import { DeviceAppDataProvider } from "../lib/providers/device-app-data-provider"; +import { MobilePlatformsCapabilities } from "../lib/mobile-platforms-capabilities"; +import { DevicePlatformsConstants } from "../lib/common/mobile/device-platforms-constants"; import { XmlValidator } from "../lib/xml-validator"; import StaticConfigLib = require("../lib/config"); import * as path from "path"; @@ -76,14 +75,14 @@ function createTestInjector() { testInjector.register("pluginsService", PluginsService); testInjector.register("analyticsService", { - trackException: () => { return future.fromResult(); }, - checkConsent: () => { return future.fromResult(); }, - trackFeature: () => { return future.fromResult(); } + trackException: () => { return Promise.resolve(); }, + checkConsent: () => { return Promise.resolve(); }, + trackFeature: () => { return Promise.resolve(); } }); testInjector.register("projectFilesManager", ProjectFilesManager); testInjector.register("pluginVariablesService", { - savePluginVariablesInProjectFile: (pluginData: IPluginData) => future.fromResult(), - interpolatePluginVariables: (pluginData: IPluginData, pluginConfigurationFileContent: string) => future.fromResult(pluginConfigurationFileContent) + savePluginVariablesInProjectFile: (pluginData: IPluginData) => Promise.resolve(), + interpolatePluginVariables: (pluginData: IPluginData, pluginConfigurationFileContent: string) => Promise.resolve(pluginConfigurationFileContent) }); testInjector.register("npmInstallationManager", NpmInstallationManager); @@ -95,7 +94,7 @@ function createTestInjector() { testInjector.register("mobilePlatformsCapabilities", MobilePlatformsCapabilities); testInjector.register("devicePlatformsConstants", DevicePlatformsConstants); testInjector.register("projectTemplatesService", { - defaultTemplate: future.fromResult("") + defaultTemplate: Promise.resolve("") }); testInjector.register("xmlValidator", XmlValidator); testInjector.register("config", StaticConfigLib.Configuration); @@ -125,38 +124,34 @@ function createProjectFile(testInjector: IInjector): string { function mockBeginCommand(testInjector: IInjector, expectedErrorMessage: string) { let errors = testInjector.resolve("errors"); - errors.beginCommand = (action: () => IFuture): IFuture => { - return (() => { - try { - return action().wait(); - } catch (err) { - isErrorThrown = true; - assert.equal(err.toString(), expectedErrorMessage); - } - }).future()(); + errors.beginCommand = async (action: () => Promise): Promise => { + try { + return await action(); + } catch (err) { + isErrorThrown = true; + assert.equal(err.toString(), expectedErrorMessage); + } }; } -function addPluginWhenExpectingToFail(testInjector: IInjector, plugin: string, expectedErrorMessage: string, command?: string) { +async function addPluginWhenExpectingToFail(testInjector: IInjector, plugin: string, expectedErrorMessage: string, command?: string) { createProjectFile(testInjector); let pluginsService = testInjector.resolve("pluginsService"); pluginsService.getAllInstalledPlugins = () => { - return (() => { - return [{ - name: "" - }]; - }).future()(); + return [{ + name: "" + }]; }; pluginsService.ensureAllDependenciesAreInstalled = () => { - return future.fromResult(); + return Promise.resolve(); }; mockBeginCommand(testInjector, "Exception: " + expectedErrorMessage); isErrorThrown = false; let commandsService = testInjector.resolve(CommandsService); - commandsService.tryExecuteCommand(`plugin|${command}`, [plugin]).wait(); + await commandsService.tryExecuteCommand(`plugin|${command}`, [plugin]); assert.isTrue(isErrorThrown); } @@ -196,14 +191,14 @@ describe("Plugins service", () => { }); _.each(commands, command => { - describe(`plugin ${command}}`, () => { - it("fails when no param is specified to plugin install command", () => { - addPluginWhenExpectingToFail(testInjector, null, "You must specify plugin name.", command); + describe(`plugin ${command}`, () => { + it("fails when no param is specified to plugin install command", async () => { + await addPluginWhenExpectingToFail(testInjector, null, "You must specify plugin name.", command); }); - it("fails when invalid nativescript plugin name is specified", () => { - addPluginWhenExpectingToFail(testInjector, "lodash", "lodash is not a valid NativeScript plugin. Verify that the plugin package.json file contains a nativescript key and try again.", command); + it("fails when invalid nativescript plugin name is specified", async () => { + await addPluginWhenExpectingToFail(testInjector, "lodash", "lodash is not a valid NativeScript plugin. Verify that the plugin package.json file contains a nativescript key and try again.", command); }); - it("fails when the plugin is already installed", () => { + it("fails when the plugin is already installed", async () => { let pluginName = "plugin1"; let projectFolder = createProjectFile(testInjector); let fs = testInjector.resolve("fs"); @@ -217,22 +212,20 @@ describe("Plugins service", () => { let pluginsService = testInjector.resolve("pluginsService"); pluginsService.getAllInstalledPlugins = () => { - return (() => { - return [{ - name: "plugin1" - }]; - }).future()(); + return [{ + name: "plugin1" + }]; }; mockBeginCommand(testInjector, "Exception: " + 'Plugin "plugin1" is already installed.'); isErrorThrown = false; let commandsService = testInjector.resolve(CommandsService); - commandsService.tryExecuteCommand(`plugin|${command}`, [pluginName]).wait(); + await commandsService.tryExecuteCommand(`plugin|${command}`, [pluginName]); assert.isTrue(isErrorThrown); }); - it("fails when the plugin does not support the installed framework", () => { + it("fails when the plugin does not support the installed framework", async () => { let isWarningMessageShown = false; let expectedWarningMessage = "mySamplePlugin 1.5.0 for android is not compatible with the currently installed framework version 1.4.0."; @@ -267,11 +260,9 @@ describe("Plugins service", () => { // Mock pluginsService let pluginsService = testInjector.resolve("pluginsService"); pluginsService.getAllInstalledPlugins = () => { - return (() => { - return [{ - name: "" - }]; - }).future()(); + return [{ + name: "" + }]; }; // Mock platformsData @@ -284,25 +275,23 @@ describe("Plugins service", () => { }; }; - pluginsService.add(pluginFolderPath).wait(); + await pluginsService.add(pluginFolderPath); assert.isTrue(isWarningMessageShown); }); - it("adds plugin by name", () => { + it("adds plugin by name", async () => { let pluginName = "plugin1"; let projectFolder = createProjectFile(testInjector); let pluginsService = testInjector.resolve("pluginsService"); pluginsService.getAllInstalledPlugins = () => { - return (() => { - return [{ - name: "" - }]; - }).future()(); + return [{ + name: "" + }]; }; let commandsService = testInjector.resolve(CommandsService); - commandsService.tryExecuteCommand(`plugin|${command}`, [pluginName]).wait(); + await commandsService.tryExecuteCommand(`plugin|${command}`, [pluginName]); let fs = testInjector.resolve("fs"); @@ -325,21 +314,19 @@ describe("Plugins service", () => { let expectedDependenciesExact = { "plugin1": "1.0.3" }; assert.isTrue(_.isEqual(actualDependencies, expectedDependencies) || _.isEqual(actualDependencies, expectedDependenciesExact)); }); - it("adds plugin by name and version", () => { + it("adds plugin by name and version", async () => { let pluginName = "plugin1"; let projectFolder = createProjectFile(testInjector); let pluginsService = testInjector.resolve("pluginsService"); pluginsService.getAllInstalledPlugins = () => { - return (() => { - return [{ - name: "" - }]; - }).future()(); + return [{ + name: "" + }]; }; let commandsService = testInjector.resolve(CommandsService); - commandsService.tryExecuteCommand(`plugin|${command}`, [pluginName + "@1.0.0"]).wait(); + await commandsService.tryExecuteCommand(`plugin|${command}`, [pluginName + "@1.0.0"]); let fs = testInjector.resolve("fs"); @@ -362,7 +349,7 @@ describe("Plugins service", () => { let expectedDependenciesExact = { "plugin1": "1.0.0" }; assert.isTrue(_.isEqual(actualDependencies, expectedDependencies) || _.isEqual(actualDependencies, expectedDependenciesExact)); }); - it("adds plugin by local path", () => { + it("adds plugin by local path", async () => { // Creates a plugin in tempFolder let pluginName = "mySamplePlugin"; let projectFolder = createProjectFile(testInjector); @@ -381,15 +368,13 @@ describe("Plugins service", () => { let pluginsService = testInjector.resolve("pluginsService"); pluginsService.getAllInstalledPlugins = () => { - return (() => { - return [{ - name: "" - }]; - }).future()(); + return [{ + name: "" + }]; }; let commandsService = testInjector.resolve(CommandsService); - commandsService.tryExecuteCommand(`plugin|${command}`, [pluginFolderPath]).wait(); + await commandsService.tryExecuteCommand(`plugin|${command}`, [pluginFolderPath]); // Assert that the all plugin's content is successfully added to node_modules folder let nodeModulesFolderPath = path.join(projectFolder, "node_modules"); @@ -404,7 +389,7 @@ describe("Plugins service", () => { it("adds plugin by github url", () => { // TODO: add test }); - it("doesn't install dev dependencies when --production option is specified", () => { + it("doesn't install dev dependencies when --production option is specified", async () => { // Creates a plugin in tempFolder let pluginName = "mySamplePlugin"; let projectFolder = createProjectFile(testInjector); @@ -426,11 +411,9 @@ describe("Plugins service", () => { let pluginsService = testInjector.resolve("pluginsService"); pluginsService.getAllInstalledPlugins = () => { - return (() => { - return [{ - name: "" - }]; - }).future()(); + return [{ + name: "" + }]; }; // Mock options @@ -438,12 +421,12 @@ describe("Plugins service", () => { options.production = true; let commandsService = testInjector.resolve(CommandsService); - commandsService.tryExecuteCommand(`plugin|${command}`, [pluginFolderPath]).wait(); + await commandsService.tryExecuteCommand(`plugin|${command}`, [pluginFolderPath]); let nodeModulesFolderPath = path.join(projectFolder, "node_modules"); assert.isFalse(fs.exists(path.join(nodeModulesFolderPath, pluginName, "node_modules", "grunt"))); }); - it("install dev dependencies when --production option is not specified", () => { + it("install dev dependencies when --production option is not specified", async () => { // Creates a plugin in tempFolder let pluginName = "mySamplePlugin"; let projectFolder = createProjectFile(testInjector); @@ -468,11 +451,9 @@ describe("Plugins service", () => { let pluginsService = testInjector.resolve("pluginsService"); pluginsService.getAllInstalledPlugins = () => { - return (() => { - return [{ - name: "" - }]; - }).future()(); + return [{ + name: "" + }]; }; // Mock options @@ -480,7 +461,7 @@ describe("Plugins service", () => { options.production = false; let commandsService = testInjector.resolve(CommandsService); - commandsService.tryExecuteCommand(`plugin|${command}`, [pluginFolderPath]).wait(); + await commandsService.tryExecuteCommand(`plugin|${command}`, [pluginFolderPath]); }); }); }); @@ -490,7 +471,7 @@ describe("Plugins service", () => { testInjector = createTestInjector(); testInjector.registerCommand("plugin|add", AddPluginCommand); }); - it("fails if the plugin contains incorrect xml", () => { + it("fails if the plugin contains incorrect xml", async () => { let pluginName = "mySamplePlugin"; let projectFolder = createProjectFile(testInjector); let pluginFolderPath = path.join(projectFolder, pluginName); @@ -512,11 +493,9 @@ describe("Plugins service", () => { // Mock plugins service let pluginsService = testInjector.resolve("pluginsService"); pluginsService.getAllInstalledPlugins = () => { - return (() => { - return [{ - name: "" - }]; - }).future()(); + return [{ + name: "" + }]; }; let appDestinationDirectoryPath = path.join(projectFolder, "platforms", "android"); @@ -530,7 +509,7 @@ describe("Plugins service", () => { configurationFileName: "AndroidManifest.xml", normalizedPlatformName: "Android", platformProjectService: { - preparePluginNativeCode: (pluginData: IPluginData) => future.fromResult() + preparePluginNativeCode: (pluginData: IPluginData) => Promise.resolve() } }; }; @@ -552,7 +531,7 @@ describe("Plugins service", () => { `\n@#[line:1,col:39].` + `\n@#[line:1,col:39].`; mockBeginCommand(testInjector, expectedErrorMessage); - pluginsService.prepare(pluginJsonData, "android").wait(); + await pluginsService.prepare(pluginJsonData, "android"); }); }); }); diff --git a/test/project-commands.ts b/test/project-commands.ts index 1e9ef51d99..a03d932984 100644 --- a/test/project-commands.ts +++ b/test/project-commands.ts @@ -3,18 +3,16 @@ import * as stubs from "./stubs"; import { CreateProjectCommand } from "../lib/commands/create-project"; import { StringParameterBuilder } from "../lib/common/command-params"; import * as constants from "../lib/constants"; -import {assert} from "chai"; +import { assert } from "chai"; let selectedTemplateName: string; let isProjectCreated: boolean; let dummyArgs = ["dummyArgsString"]; class ProjectServiceMock implements IProjectService { - createProject(projectName: string, selectedTemplate?: string): IFuture { - return (() => { - selectedTemplateName = selectedTemplate; - isProjectCreated = true; - }).future()(); + async createProject(projectName: string, selectedTemplate?: string): Promise { + selectedTemplateName = selectedTemplate; + isProjectCreated = true; } } @@ -57,78 +55,74 @@ describe("Project commands tests", () => { }); describe("#CreateProjectCommand", () => { - it("should not fail when using only --ng.", () => { + it("should not fail when using only --ng.", async () => { options.ng = true; - createProjectCommand.execute(dummyArgs).wait(); + await createProjectCommand.execute(dummyArgs); assert.isTrue(isProjectCreated); }); - it("should not fail when using only --tsc.", () => { + it("should not fail when using only --tsc.", async () => { options.tsc = true; - createProjectCommand.execute(dummyArgs).wait(); + await createProjectCommand.execute(dummyArgs); assert.isTrue(isProjectCreated); }); - it("should not fail when using only --template.", () => { + it("should not fail when using only --template.", async () => { options.template = "ng"; - createProjectCommand.execute(dummyArgs).wait(); + await createProjectCommand.execute(dummyArgs); assert.isTrue(isProjectCreated); }); - it("should set the template name correctly when used --ng.", () => { + it("should set the template name correctly when used --ng.", async () => { options.ng = true; - createProjectCommand.execute(dummyArgs).wait(); + await createProjectCommand.execute(dummyArgs); assert.deepEqual(selectedTemplateName, constants.ANGULAR_NAME); }); - it("should set the template name correctly when used --tsc.", () => { + it("should set the template name correctly when used --tsc.", async () => { options.tsc = true; - createProjectCommand.execute(dummyArgs).wait(); + await createProjectCommand.execute(dummyArgs); assert.deepEqual(selectedTemplateName, constants.TYPESCRIPT_NAME); }); - it("should not set the template name when --ng is not used.", () => { + it("should not set the template name when --ng is not used.", async () => { options.ng = false; - createProjectCommand.execute(dummyArgs).wait(); + await createProjectCommand.execute(dummyArgs); assert.isUndefined(selectedTemplateName); }); - it("should not set the template name when --tsc is not used.", () => { + it("should not set the template name when --tsc is not used.", async () => { options.tsc = false; - createProjectCommand.execute(dummyArgs).wait(); + await createProjectCommand.execute(dummyArgs); assert.isUndefined(selectedTemplateName); }); - it("should fail when --ng and --template are used simultaneously.", () => { + it("should fail when --ng and --template are used simultaneously.", async () => { options.ng = true; options.template = "ng"; - assert.throws(() => { - createProjectCommand.execute(dummyArgs).wait(); - }); + await assert.isRejected(createProjectCommand.execute(dummyArgs)); }); - it("should fail when --tsc and --template are used simultaneously.", () => { + it("should fail when --tsc and --template are used simultaneously.", async () => { options.tsc = true; options.template = "tsc"; - assert.throws(() => { - createProjectCommand.execute(dummyArgs).wait(); - }); + await assert.isRejected(createProjectCommand.execute(dummyArgs)); }); }); }); diff --git a/test/project-name-service.ts b/test/project-name-service.ts index 65363e201d..2fa9bb3989 100644 --- a/test/project-name-service.ts +++ b/test/project-name-service.ts @@ -1,8 +1,7 @@ -import {Yok} from "../lib/common/yok"; -import {ProjectNameService} from "../lib/services/project-name-service"; -import {assert} from "chai"; -import {ErrorsStub, LoggerStub} from "./stubs"; -import Future = require("fibers/future"); +import { Yok } from "../lib/common/yok"; +import { ProjectNameService } from "../lib/services/project-name-service"; +import { assert } from "chai"; +import { ErrorsStub, LoggerStub } from "./stubs"; let mockProjectNameValidator = { validate: () => true @@ -19,8 +18,8 @@ function createTestInjector(): IInjector { testInjector.register("errors", ErrorsStub); testInjector.register("logger", LoggerStub); testInjector.register("prompter", { - confirm: (message: string): IFuture => Future.fromResult(true), - getString: (message: string): IFuture => Future.fromResult(dummyString) + confirm: (message: string): Promise => Promise.resolve(true), + getString: (message: string): Promise => Promise.resolve(dummyString) }); return testInjector; @@ -37,39 +36,37 @@ describe("Project Name Service Tests", () => { projectNameService = testInjector.resolve("projectNameService"); }); - it("returns correct name when valid name is entered", () => { - let actualProjectName = projectNameService.ensureValidName(validProjectName).wait(); + it("returns correct name when valid name is entered", async () => { + let actualProjectName = await projectNameService.ensureValidName(validProjectName); assert.deepEqual(actualProjectName, validProjectName); }); _.each(invalidProjectNames, invalidProjectName => { - it(`returns correct name when "${invalidProjectName}" is entered several times and then valid name is entered`, () => { + it(`returns correct name when "${invalidProjectName}" is entered several times and then valid name is entered`, async () => { let prompter = testInjector.resolve("prompter"); - prompter.confirm = (message: string): IFuture => Future.fromResult(false); + prompter.confirm = (message: string): Promise => Promise.resolve(false); let incorrectInputsLimit = 5; let incorrectInputsCount = 0; - prompter.getString = (message: string): IFuture => { - return (() => { - if (incorrectInputsCount < incorrectInputsLimit) { - incorrectInputsCount++; + prompter.getString = async (message: string): Promise => { + if (incorrectInputsCount < incorrectInputsLimit) { + incorrectInputsCount++; - return invalidProjectName; - } else { - return validProjectName; - } - }).future()(); + return invalidProjectName; + } else { + return validProjectName; + } }; - let actualProjectName = projectNameService.ensureValidName(invalidProjectName).wait(); + let actualProjectName = await projectNameService.ensureValidName(invalidProjectName); assert.deepEqual(actualProjectName, validProjectName); }); - it(`returns the invalid name when "${invalidProjectName}" is entered and --force flag is present`, () => { - let actualProjectName = projectNameService.ensureValidName(validProjectName, { force: true }).wait(); + it(`returns the invalid name when "${invalidProjectName}" is entered and --force flag is present`, async () => { + let actualProjectName = await projectNameService.ensureValidName(validProjectName, { force: true }); assert.deepEqual(actualProjectName, validProjectName); }); diff --git a/test/project-service.ts b/test/project-service.ts index 0103125260..ba8a7ead66 100644 --- a/test/project-service.ts +++ b/test/project-service.ts @@ -19,7 +19,6 @@ import { assert } from "chai"; import { Options } from "../lib/options"; import { HostInfo } from "../lib/common/host-info"; import { ProjectTemplatesService } from "../lib/services/project-templates-service"; -import Future = require("fibers/future"); let mockProjectNameValidator = { validate: () => true @@ -38,60 +37,58 @@ class ProjectIntegrationTest { this.createTestInjector(); } - public createProject(projectName: string, template?: string): IFuture { + public async createProject(projectName: string, template?: string): Promise { let projectService = this.testInjector.resolve("projectService"); return projectService.createProject(projectName, template); } - public assertProject(tempFolder: string, projectName: string, appId: string, projectSourceDirectory?: string): IFuture { - return (() => { - let fs: IFileSystem = this.testInjector.resolve("fs"); - let projectDir = path.join(tempFolder, projectName); - let appDirectoryPath = path.join(projectDir, "app"); - let platformsDirectoryPath = path.join(projectDir, "platforms"); - let tnsProjectFilePath = path.join(projectDir, "package.json"); - let tnsModulesPath = path.join(projectDir, constants.NODE_MODULES_FOLDER_NAME, constants.TNS_CORE_MODULES_NAME); - let packageJsonContent = fs.readJson(tnsProjectFilePath); - let options = this.testInjector.resolve("options"); - - assert.isTrue(fs.exists(appDirectoryPath)); - assert.isTrue(fs.exists(platformsDirectoryPath)); - assert.isTrue(fs.exists(tnsProjectFilePath)); - assert.isTrue(fs.exists(tnsModulesPath)); - - assert.isFalse(fs.isEmptyDir(appDirectoryPath)); - assert.isTrue(fs.isEmptyDir(platformsDirectoryPath)); - - let actualAppId = packageJsonContent["nativescript"].id; - let expectedAppId = appId; - assert.equal(actualAppId, expectedAppId); - - let tnsCoreModulesRecord = packageJsonContent["dependencies"][constants.TNS_CORE_MODULES_NAME]; - assert.isTrue(tnsCoreModulesRecord !== null); - - let sourceDir = projectSourceDirectory || options.copyFrom; - - let expectedFiles = fs.enumerateFilesInDirectorySync(sourceDir); - let actualFiles = fs.enumerateFilesInDirectorySync(appDirectoryPath); - assert.isTrue(actualFiles.length >= expectedFiles.length, "Files in created project must be at least as files in app dir."); - _.each(expectedFiles, file => { - let relativeToProjectDir = helpers.getRelativeToRootPath(sourceDir, file); - let filePathInApp = path.join(appDirectoryPath, relativeToProjectDir); - assert.isTrue(fs.exists(filePathInApp), `File ${filePathInApp} does not exist.`); - }); + public async assertProject(tempFolder: string, projectName: string, appId: string, projectSourceDirectory?: string): Promise { + let fs: IFileSystem = this.testInjector.resolve("fs"); + let projectDir = path.join(tempFolder, projectName); + let appDirectoryPath = path.join(projectDir, "app"); + let platformsDirectoryPath = path.join(projectDir, "platforms"); + let tnsProjectFilePath = path.join(projectDir, "package.json"); + let tnsModulesPath = path.join(projectDir, constants.NODE_MODULES_FOLDER_NAME, constants.TNS_CORE_MODULES_NAME); + let packageJsonContent = fs.readJson(tnsProjectFilePath); + let options = this.testInjector.resolve("options"); + + assert.isTrue(fs.exists(appDirectoryPath)); + assert.isTrue(fs.exists(platformsDirectoryPath)); + assert.isTrue(fs.exists(tnsProjectFilePath)); + assert.isTrue(fs.exists(tnsModulesPath)); + + assert.isFalse(fs.isEmptyDir(appDirectoryPath)); + assert.isTrue(fs.isEmptyDir(platformsDirectoryPath)); + + let actualAppId = packageJsonContent["nativescript"].id; + let expectedAppId = appId; + assert.equal(actualAppId, expectedAppId); + + let tnsCoreModulesRecord = packageJsonContent["dependencies"][constants.TNS_CORE_MODULES_NAME]; + assert.isTrue(tnsCoreModulesRecord !== null); + + let sourceDir = projectSourceDirectory || options.copyFrom; + + let expectedFiles = fs.enumerateFilesInDirectorySync(sourceDir); + let actualFiles = fs.enumerateFilesInDirectorySync(appDirectoryPath); + assert.isTrue(actualFiles.length >= expectedFiles.length, "Files in created project must be at least as files in app dir."); + _.each(expectedFiles, file => { + let relativeToProjectDir = helpers.getRelativeToRootPath(sourceDir, file); + let filePathInApp = path.join(appDirectoryPath, relativeToProjectDir); + assert.isTrue(fs.exists(filePathInApp), `File ${filePathInApp} does not exist.`); + }); - // assert dependencies and devDependencies are copied from template to real project - let sourcePackageJsonContent = fs.readJson(path.join(sourceDir, "package.json")); - let missingDeps = _.difference(_.keys(sourcePackageJsonContent.dependencies), _.keys(packageJsonContent.dependencies)); - let missingDevDeps = _.difference(_.keys(sourcePackageJsonContent.devDependencies), _.keys(packageJsonContent.devDependencies)); - assert.deepEqual(missingDeps, [], `All dependencies from template must be copied to project's package.json. Missing ones are: ${missingDeps.join(", ")}.`); - assert.deepEqual(missingDevDeps, [], `All devDependencies from template must be copied to project's package.json. Missing ones are: ${missingDevDeps.join(", ")}.`); - - // assert App_Resources are prepared correctly - let appResourcesDir = path.join(appDirectoryPath, "App_Resources"); - let appResourcesContents = fs.readDirectory(appResourcesDir); - assert.deepEqual(appResourcesContents, ["Android", "iOS"], "Project's app/App_Resources must contain Android and iOS directories."); - }).future()(); + // assert dependencies and devDependencies are copied from template to real project + let sourcePackageJsonContent = fs.readJson(path.join(sourceDir, "package.json")); + let missingDeps = _.difference(_.keys(sourcePackageJsonContent.dependencies), _.keys(packageJsonContent.dependencies)); + let missingDevDeps = _.difference(_.keys(sourcePackageJsonContent.devDependencies), _.keys(packageJsonContent.devDependencies)); + assert.deepEqual(missingDeps, [], `All dependencies from template must be copied to project's package.json. Missing ones are: ${missingDeps.join(", ")}.`); + assert.deepEqual(missingDevDeps, [], `All devDependencies from template must be copied to project's package.json. Missing ones are: ${missingDevDeps.join(", ")}.`); + + // assert App_Resources are prepared correctly + let appResourcesDir = path.join(appDirectoryPath, "App_Resources"); + let appResourcesContents = fs.readDirectory(appResourcesDir); + assert.deepEqual(appResourcesContents, ["Android", "iOS"], "Project's app/App_Resources must contain Android and iOS directories."); } public dispose(): void { @@ -121,12 +118,10 @@ class ProjectIntegrationTest { this.testInjector.register("options", Options); this.testInjector.register("hostInfo", HostInfo); this.testInjector.register("prompter", { - confirm: (message: string): IFuture => Future.fromResult(true), - getString: (message: string): IFuture => { - return (() => { - hasPromptedForString = true; - return dummyString; - }).future()(); + confirm: async (message: string): Promise => true, + getString: async (message: string): Promise => { + hasPromptedForString = true; + return dummyString; } }); } @@ -134,12 +129,12 @@ class ProjectIntegrationTest { describe("Project Service Tests", () => { describe("project service integration tests", () => { - let defaultTemplatePath:string; - let defaultSpecificVersionTemplatePath:string; - let angularTemplatePath:string; + let defaultTemplatePath: string; + let defaultSpecificVersionTemplatePath: string; + let angularTemplatePath: string; let typescriptTemplatePath: string; - before(function() { + before(async () => { let projectIntegrationTest = new ProjectIntegrationTest(); let fs: IFileSystem = projectIntegrationTest.testInjector.resolve("fs"); let npmInstallationManager: INpmInstallationManager = projectIntegrationTest.testInjector.resolve("npmInstallationManager"); @@ -153,7 +148,7 @@ describe("Project Service Tests", () => { "readme": "dummy", "repository": "dummy" }); - npmInstallationManager.install("tns-template-hello-world", defaultTemplateDir, {dependencyType: "save"}).wait(); + await npmInstallationManager.install("tns-template-hello-world", defaultTemplateDir, { dependencyType: "save" }); defaultTemplatePath = path.join(defaultTemplateDir, "node_modules", "tns-template-hello-world"); fs.deleteDirectory(path.join(defaultTemplatePath, "node_modules")); @@ -166,7 +161,7 @@ describe("Project Service Tests", () => { "readme": "dummy", "repository": "dummy" }); - npmInstallationManager.install("tns-template-hello-world", defaultSpecificVersionTemplateDir, {version: "1.4.0", dependencyType: "save"}).wait(); + await npmInstallationManager.install("tns-template-hello-world", defaultSpecificVersionTemplateDir, { version: "1.4.0", dependencyType: "save" }); defaultSpecificVersionTemplatePath = path.join(defaultSpecificVersionTemplateDir, "node_modules", "tns-template-hello-world"); fs.deleteDirectory(path.join(defaultSpecificVersionTemplatePath, "node_modules")); @@ -179,7 +174,7 @@ describe("Project Service Tests", () => { "readme": "dummy", "repository": "dummy" }); - npmInstallationManager.install("tns-template-hello-world-ng", angularTemplateDir, {dependencyType: "save"}).wait(); + await npmInstallationManager.install("tns-template-hello-world-ng", angularTemplateDir, { dependencyType: "save" }); angularTemplatePath = path.join(angularTemplateDir, "node_modules", "tns-template-hello-world-ng"); fs.deleteDirectory(path.join(angularTemplatePath, "node_modules")); @@ -192,12 +187,12 @@ describe("Project Service Tests", () => { "readme": "dummy", "repository": "dummy" }); - npmInstallationManager.install("tns-template-hello-world-ts", typescriptTemplateDir, {dependencyType: "save"}).wait(); + await npmInstallationManager.install("tns-template-hello-world-ts", typescriptTemplateDir, { dependencyType: "save" }); typescriptTemplatePath = path.join(typescriptTemplateDir, "node_modules", "tns-template-hello-world-ts"); fs.deleteDirectory(path.join(typescriptTemplatePath, "node_modules")); }); - it("creates valid project from default template", () => { + it("creates valid project from default template", async () => { let projectIntegrationTest = new ProjectIntegrationTest(); let tempFolder = temp.mkdirSync("project"); let projectName = "myapp"; @@ -205,30 +200,30 @@ describe("Project Service Tests", () => { options.path = tempFolder; - projectIntegrationTest.createProject(projectName).wait(); - projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", defaultTemplatePath).wait(); + await projectIntegrationTest.createProject(projectName); + await projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", defaultTemplatePath); }); - it("creates valid project from default template when --template default is specified", () => { + it("creates valid project from default template when --template default is specified", async () => { let projectIntegrationTest = new ProjectIntegrationTest(); let tempFolder = temp.mkdirSync("project"); let projectName = "myapp"; let options = projectIntegrationTest.testInjector.resolve("options"); options.path = tempFolder; - projectIntegrationTest.createProject(projectName, "default").wait(); - projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", defaultTemplatePath).wait(); + await projectIntegrationTest.createProject(projectName, "default"); + await projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", defaultTemplatePath); }); - it("creates valid project from default template when --template default@version is specified", () => { + it("creates valid project from default template when --template default@version is specified", async () => { let projectIntegrationTest = new ProjectIntegrationTest(); let tempFolder = temp.mkdirSync("project"); let projectName = "myapp"; let options = projectIntegrationTest.testInjector.resolve("options"); options.path = tempFolder; - projectIntegrationTest.createProject(projectName, "default@1.4.0").wait(); - projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", defaultSpecificVersionTemplatePath).wait(); + await projectIntegrationTest.createProject(projectName, "default@1.4.0"); + await projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", defaultSpecificVersionTemplatePath); }); // it("creates valid project from typescript template", () => { @@ -238,9 +233,9 @@ describe("Project Service Tests", () => { // let options = projectIntegrationTest.testInjector.resolve("options"); // options.path = tempFolder; - // projectIntegrationTest.createProject(projectName, "typescript").wait(); + // projectIntegrationTest.createProject(projectName, "typescript"); - // projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", typescriptTemplatePath).wait(); + // await projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", typescriptTemplatePath); // }); // it("creates valid project from tsc template", () => { @@ -250,9 +245,9 @@ describe("Project Service Tests", () => { // let options = projectIntegrationTest.testInjector.resolve("options"); // options.path = tempFolder; - // projectIntegrationTest.createProject(projectName, "tsc").wait(); + // await projectIntegrationTest.createProject(projectName, "tsc"); - // projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", typescriptTemplatePath).wait(); + // await projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", typescriptTemplatePath); // }); // it("creates valid project from angular template", () => { @@ -262,9 +257,9 @@ describe("Project Service Tests", () => { // let options = projectIntegrationTest.testInjector.resolve("options"); // options.path = tempFolder; - // projectIntegrationTest.createProject(projectName, "angular").wait(); + // await projectIntegrationTest.createProject(projectName, "angular"); - // projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", angularTemplatePath).wait(); + // await projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", angularTemplatePath); // }); // it("creates valid project from ng template", () => { @@ -274,9 +269,9 @@ describe("Project Service Tests", () => { // let options = projectIntegrationTest.testInjector.resolve("options"); // options.path = tempFolder; - // projectIntegrationTest.createProject(projectName, "ng").wait(); + // await projectIntegrationTest.createProject(projectName, "ng"); - // projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", angularTemplatePath).wait(); + // await projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", angularTemplatePath); // }); // it("creates valid project from local directory template", () => { @@ -301,37 +296,37 @@ describe("Project Service Tests", () => { // "license": "MIT", // "readme": "dummy", // "repository": "dummy" - // }).wait(); - // fs.createDirectory(path.join(tempDir, "app", "App_Resources", "Android")).wait(); //copy App_Resources from somewhere - // fs.createDirectory(path.join(tempDir, "app", "App_Resources", "iOS")).wait(); + // }); + // fs.createDirectory(path.join(tempDir, "app", "App_Resources", "Android")); //copy App_Resources from somewhere + // fs.createDirectory(path.join(tempDir, "app", "App_Resources", "iOS")); - // projectIntegrationTest.createProject(projectName, tempDir).wait(); - // projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", tempDir).wait(); + // await projectIntegrationTest.createProject(projectName, tempDir); + // await projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", tempDir); // }); - it("creates valid project from tarball", () => { + it("creates valid project from tarball", async () => { let projectIntegrationTest = new ProjectIntegrationTest(); let tempFolder = temp.mkdirSync("projectLocalDir"); let projectName = "myapp"; let options = projectIntegrationTest.testInjector.resolve("options"); options.path = tempFolder; - projectIntegrationTest.createProject(projectName, "https://github.com/NativeScript/template-hello-world/tarball/master").wait(); - projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", defaultTemplatePath).wait(); + await projectIntegrationTest.createProject(projectName, "https://github.com/NativeScript/template-hello-world/tarball/master"); + await projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", defaultTemplatePath); }); - it("creates valid project from git url", () => { + it("creates valid project from git url", async () => { let projectIntegrationTest = new ProjectIntegrationTest(); let tempFolder = temp.mkdirSync("projectLocalDir"); let projectName = "myapp"; let options = projectIntegrationTest.testInjector.resolve("options"); options.path = tempFolder; - projectIntegrationTest.createProject(projectName, "https://github.com/NativeScript/template-hello-world.git").wait(); - projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", defaultTemplatePath).wait(); + await projectIntegrationTest.createProject(projectName, "https://github.com/NativeScript/template-hello-world.git"); + await projectIntegrationTest.assertProject(tempFolder, projectName, "org.nativescript.myapp", defaultTemplatePath); }); - it("creates valid project with specified id from default template", () => { + it("creates valid project with specified id from default template", async () => { let projectIntegrationTest = new ProjectIntegrationTest(); let tempFolder = temp.mkdirSync("project1"); let projectName = "myapp"; @@ -341,8 +336,8 @@ describe("Project Service Tests", () => { options.appid = "my.special.id"; - projectIntegrationTest.createProject(projectName).wait(); - projectIntegrationTest.assertProject(tempFolder, projectName, options.appid, defaultTemplatePath).wait(); + await projectIntegrationTest.createProject(projectName); + await projectIntegrationTest.assertProject(tempFolder, projectName, options.appid, defaultTemplatePath); }); describe("project name validation tests", () => { @@ -366,88 +361,84 @@ describe("Project Service Tests", () => { helpers.isInteractive = originalIsInteractive; }); - it("creates project when is interactive and incorrect name is specified and the --force option is set", () => { + it("creates project when is interactive and incorrect name is specified and the --force option is set", async () => { let projectName = invalidProjectName; options.force = true; options.path = tempFolder; - projectIntegrationTest.createProject(projectName).wait(); - projectIntegrationTest.assertProject(tempFolder, projectName, `org.nativescript.${projectName}`, defaultTemplatePath).wait(); + await projectIntegrationTest.createProject(projectName); + await projectIntegrationTest.assertProject(tempFolder, projectName, `org.nativescript.${projectName}`, defaultTemplatePath); }); - it("creates project when is interactive and incorrect name is specified and the user confirms to use the incorrect name", () => { + it("creates project when is interactive and incorrect name is specified and the user confirms to use the incorrect name", async () => { let projectName = invalidProjectName; - prompter.confirm = (message: string): IFuture => Future.fromResult(true); + prompter.confirm = (message: string): Promise => Promise.resolve(true); options.path = tempFolder; - projectIntegrationTest.createProject(projectName).wait(); - projectIntegrationTest.assertProject(tempFolder, projectName, `org.nativescript.${projectName}`, defaultTemplatePath).wait(); + await projectIntegrationTest.createProject(projectName); + await projectIntegrationTest.assertProject(tempFolder, projectName, `org.nativescript.${projectName}`, defaultTemplatePath); }); - it("prompts for new name when is interactive and incorrect name is specified and the user does not confirm to use the incorrect name", () => { + it("prompts for new name when is interactive and incorrect name is specified and the user does not confirm to use the incorrect name", async () => { let projectName = invalidProjectName; - prompter.confirm = (message: string): IFuture => Future.fromResult(false); + prompter.confirm = (message: string): Promise => Promise.resolve(false); options.path = tempFolder; - projectIntegrationTest.createProject(projectName).wait(); + await projectIntegrationTest.createProject(projectName); assert.isTrue(hasPromptedForString); }); - it("creates project when is interactive and incorrect name is specified and the user does not confirm to use the incorrect name and enters incorrect name again several times and then enters correct name", () => { + it("creates project when is interactive and incorrect name s specified and the user does not confirm to use the incorrect name and enters incorrect name again several times and then enters correct name", async () => { let projectName = invalidProjectName; - prompter.confirm = (message: string): IFuture => Future.fromResult(false); + prompter.confirm = (message: string): Promise => Promise.resolve(false); let incorrectInputsLimit = 5; let incorrectInputsCount = 0; - prompter.getString = (message: string): IFuture => { - return (() => { - if (incorrectInputsCount < incorrectInputsLimit) { - incorrectInputsCount++; - } else { - hasPromptedForString = true; + prompter.getString = async (message: string): Promise => { + if (incorrectInputsCount < incorrectInputsLimit) { + incorrectInputsCount++; + } else { + hasPromptedForString = true; - return validProjectName; - } + return validProjectName; + } - return projectName; - }).future()(); + return projectName; }; options.path = tempFolder; - projectIntegrationTest.createProject(projectName).wait(); + await projectIntegrationTest.createProject(projectName); assert.isTrue(hasPromptedForString); }); - it("does not create project when is not interactive and incorrect name is specified", () => { + it("does not create project when is not interactive and incorrect name is specified", async () => { let projectName = invalidProjectName; helpers.isInteractive = () => false; options.force = false; options.path = tempFolder; - assert.throws(() => { - projectIntegrationTest.createProject(projectName).wait(); - }); + await assert.isRejected(projectIntegrationTest.createProject(projectName)); }); - it("creates project when is not interactive and incorrect name is specified and the --force option is set", () => { + it("creates project when is not interactive and incorrect name is specified and the --force option is set", async () => { let projectName = invalidProjectName; helpers.isInteractive = () => false; options.force = true; options.path = tempFolder; - projectIntegrationTest.createProject(projectName).wait(); + await projectIntegrationTest.createProject(projectName); options.copyFrom = defaultTemplatePath; - projectIntegrationTest.assertProject(tempFolder, projectName, `org.nativescript.${projectName}`,null).wait(); + await projectIntegrationTest.assertProject(tempFolder, projectName, `org.nativescript.${projectName}`, null); }); }); diff --git a/test/project-templates-service.ts b/test/project-templates-service.ts index 872a915401..edd38ded32 100644 --- a/test/project-templates-service.ts +++ b/test/project-templates-service.ts @@ -1,28 +1,27 @@ -import {Yok} from "../lib/common/yok"; +import { Yok } from "../lib/common/yok"; import * as stubs from "./stubs"; -import {ProjectTemplatesService} from "../lib/services/project-templates-service"; -import * as assert from "assert"; -import Future = require("fibers/future"); +import { ProjectTemplatesService } from "../lib/services/project-templates-service"; +import { assert } from "chai"; import * as path from "path"; import temp = require("temp"); let isDeleteDirectoryCalledForNodeModulesDir = false; let nativeScriptValidatedTemplatePath = "nsValidatedTemplatePath"; -function createTestInjector(configuration?: {shouldNpmInstallThrow: boolean, npmInstallationDirContents: string[], npmInstallationDirNodeModulesContents: string[]}): IInjector { +function createTestInjector(configuration?: { shouldNpmInstallThrow: boolean, npmInstallationDirContents: string[], npmInstallationDirNodeModulesContents: string[] }): IInjector { let injector = new Yok(); injector.register("errors", stubs.ErrorsStub); injector.register("logger", stubs.LoggerStub); injector.register("fs", { readDirectory: (dirPath: string): string[] => { - if(dirPath.toLowerCase().indexOf("node_modules") !== -1) { + if (dirPath.toLowerCase().indexOf("node_modules") !== -1) { return configuration.npmInstallationDirNodeModulesContents; } return configuration.npmInstallationDirContents; }, deleteDirectory: (directory: string) => { - if(directory.indexOf("node_modules") !== -1) { + if (directory.indexOf("node_modules") !== -1) { isDeleteDirectoryCalledForNodeModulesDir = true; } } @@ -30,7 +29,7 @@ function createTestInjector(configuration?: {shouldNpmInstallThrow: boolean, npm }); injector.register("npm", { install: (packageName: string, pathToSave: string, config?: any) => { - if(configuration.shouldNpmInstallThrow) { + if (configuration.shouldNpmInstallThrow) { throw new Error("NPM install throws error."); } @@ -40,11 +39,11 @@ function createTestInjector(configuration?: {shouldNpmInstallThrow: boolean, npm injector.register("npmInstallationManager", { install: (packageName: string, options?: INpmInstallOptions) => { - if(configuration.shouldNpmInstallThrow) { + if (configuration.shouldNpmInstallThrow) { throw new Error("NPM install throws error."); } - return Future.fromResult(nativeScriptValidatedTemplatePath); + return Promise.resolve(nativeScriptValidatedTemplatePath); } }); @@ -61,39 +60,39 @@ describe("project-templates-service", () => { }); describe("prepareTemplate", () => { - describe("throws error", () =>{ - it("when npm install fails", () => { - testInjector = createTestInjector({shouldNpmInstallThrow: true, npmInstallationDirContents: [], npmInstallationDirNodeModulesContents: null}); + describe("throws error", () => { + it("when npm install fails", async () => { + testInjector = createTestInjector({ shouldNpmInstallThrow: true, npmInstallationDirContents: [], npmInstallationDirNodeModulesContents: null }); projectTemplatesService = testInjector.resolve("projectTemplatesService"); let tempFolder = temp.mkdirSync("preparetemplate"); - assert.throws(() => projectTemplatesService.prepareTemplate("invalidName", tempFolder).wait()); + await assert.isRejected(projectTemplatesService.prepareTemplate("invalidName", tempFolder)); }); }); describe("returns correct path to template", () => { - it("when reserved template name is used", () =>{ - testInjector = createTestInjector({shouldNpmInstallThrow: false, npmInstallationDirContents: [], npmInstallationDirNodeModulesContents: []}); + it("when reserved template name is used", async () => { + testInjector = createTestInjector({ shouldNpmInstallThrow: false, npmInstallationDirContents: [], npmInstallationDirNodeModulesContents: [] }); projectTemplatesService = testInjector.resolve("projectTemplatesService"); let tempFolder = temp.mkdirSync("preparetemplate"); - let actualPathToTemplate = projectTemplatesService.prepareTemplate("typescript", tempFolder).wait(); + let actualPathToTemplate = await projectTemplatesService.prepareTemplate("typescript", tempFolder); assert.strictEqual(path.basename(actualPathToTemplate), nativeScriptValidatedTemplatePath); assert.strictEqual(isDeleteDirectoryCalledForNodeModulesDir, true, "When correct path is returned, template's node_modules directory should be deleted."); }); - it("when reserved template name is used (case-insensitive test)", () =>{ - testInjector = createTestInjector({shouldNpmInstallThrow: false, npmInstallationDirContents: [], npmInstallationDirNodeModulesContents: []}); + it("when reserved template name is used (case-insensitive test)", async () => { + testInjector = createTestInjector({ shouldNpmInstallThrow: false, npmInstallationDirContents: [], npmInstallationDirNodeModulesContents: [] }); projectTemplatesService = testInjector.resolve("projectTemplatesService"); let tempFolder = temp.mkdirSync("preparetemplate"); - let actualPathToTemplate = projectTemplatesService.prepareTemplate("tYpEsCriPT", tempFolder).wait(); + let actualPathToTemplate = await projectTemplatesService.prepareTemplate("tYpEsCriPT", tempFolder); assert.strictEqual(path.basename(actualPathToTemplate), nativeScriptValidatedTemplatePath); assert.strictEqual(isDeleteDirectoryCalledForNodeModulesDir, true, "When correct path is returned, template's node_modules directory should be deleted."); }); - it("uses defaultTemplate when undefined is passed as parameter", () =>{ - testInjector = createTestInjector({shouldNpmInstallThrow: false, npmInstallationDirContents: [], npmInstallationDirNodeModulesContents: []}); + it("uses defaultTemplate when undefined is passed as parameter", async () => { + testInjector = createTestInjector({ shouldNpmInstallThrow: false, npmInstallationDirContents: [], npmInstallationDirNodeModulesContents: [] }); projectTemplatesService = testInjector.resolve("projectTemplatesService"); let tempFolder = temp.mkdirSync("preparetemplate"); - let actualPathToTemplate = projectTemplatesService.prepareTemplate(undefined, tempFolder).wait(); + let actualPathToTemplate = await projectTemplatesService.prepareTemplate(undefined, tempFolder); assert.strictEqual(path.basename(actualPathToTemplate), nativeScriptValidatedTemplatePath); assert.strictEqual(isDeleteDirectoryCalledForNodeModulesDir, true, "When correct path is returned, template's node_modules directory should be deleted."); }); diff --git a/test/stubs.ts b/test/stubs.ts index 9e40df9377..01db7709bd 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -1,6 +1,5 @@ /* tslint:disable:no-empty */ -import Future = require("fibers/future"); import * as util from "util"; import * as chai from "chai"; @@ -28,7 +27,7 @@ export class LoggerStub implements ILogger { } printInfoMessageOnSameLine(message: string): void { } - printMsgWithTimeout(message: string, timeout: number): IFuture { + async printMsgWithTimeout(message: string, timeout: number): Promise { return null; } @@ -36,11 +35,11 @@ export class LoggerStub implements ILogger { } export class FileSystemStub implements IFileSystem { - zipFiles(zipFile: string, files: string[], zipPathCallback: (path: string) => string): IFuture { + async zipFiles(zipFile: string, files: string[], zipPathCallback: (path: string) => string): Promise { return undefined; } - unzip(zipFile: string, destination: string): IFuture { + async unzip(zipFile: string, destination: string): Promise { return undefined; } @@ -52,15 +51,15 @@ export class FileSystemStub implements IFileSystem { return undefined; } - deleteDirectory(directory: string): IFuture { - return Future.fromResult(); + async deleteDirectory(directory: string): Promise { + return Promise.resolve(); } getFileSize(path: string): number { return undefined; } - futureFromEvent(eventEmitter: any, event: string): IFuture { + async futureFromEvent(eventEmitter: any, event: string): Promise { return undefined; } @@ -140,7 +139,7 @@ export class FileSystemStub implements IFileSystem { symlink(sourcePath: string, destinationPath: string): void { } - setCurrentUserAsOwner(path: string, owner: string): IFuture { + async setCurrentUserAsOwner(path: string, owner: string): Promise { return undefined; } @@ -152,11 +151,11 @@ export class FileSystemStub implements IFileSystem { return false; } - getFileShasum(fileName: string): IFuture { + async getFileShasum(fileName: string): Promise { return undefined; } - readStdin(): IFuture { + async readStdin(): Promise { return undefined; } @@ -187,7 +186,7 @@ export class ErrorsStub implements IErrors { throw new Error(message); } - beginCommand(action: () => IFuture, printHelpCommand: () => IFuture): IFuture { + async beginCommand(action: () => Promise, printHelpCommand: () => Promise): Promise { throw new Error("not supported"); } @@ -204,24 +203,24 @@ export class ErrorsStub implements IErrors { } export class NpmInstallationManagerStub implements INpmInstallationManager { - install(packageName: string, pathToSave?: string, version?: string): IFuture { - return Future.fromResult(""); + async install(packageName: string, pathToSave?: string, version?: string): Promise { + return Promise.resolve(""); } - getLatestVersion(packageName: string): IFuture { - return Future.fromResult(""); + async getLatestVersion(packageName: string): Promise { + return Promise.resolve(""); } - getNextVersion(packageName: string): IFuture { - return Future.fromResult(""); + async getNextVersion(packageName: string): Promise { + return Promise.resolve(""); } - getLatestCompatibleVersion(packageName: string): IFuture { - return Future.fromResult(""); + async getLatestCompatibleVersion(packageName: string): Promise { + return Promise.resolve(""); } - getInspectorFromCache(name: string, projectDir: string): IFuture { - return Future.fromResult(""); + async getInspectorFromCache(name: string, projectDir: string): Promise { + return Promise.resolve(""); } } @@ -281,31 +280,33 @@ export class PlatformProjectServiceStub implements IPlatformProjectService { getAppResourcesDestinationDirectoryPath(): string { return ""; } - validateOptions(): IFuture { - return Future.fromResult(true); + validateOptions(): Promise { + return Promise.resolve(true); } - validate(): IFuture { - return Future.fromResult(); + validate(): Promise { + return Promise.resolve(); } - createProject(projectRoot: string, frameworkDir: string): IFuture { - return Future.fromResult(); + async createProject(projectRoot: string, frameworkDir: string): Promise { + return Promise.resolve(); } - interpolateData(): IFuture { - return Future.fromResult(); + async interpolateData(): Promise { + return Promise.resolve(); } - interpolateConfigurationFile(): IFuture { - return Future.fromResult(); + async interpolateConfigurationFile(): Promise { + return Promise.resolve(); } afterCreateProject(projectRoot: string): void { return null; } - prepareProject(): void { } + prepareProject(): Promise { + return Promise.resolve(); + } - buildProject(projectRoot: string): IFuture { - return Future.fromResult(); + async buildProject(projectRoot: string): Promise { + return Promise.resolve(); } - buildForDeploy(projectRoot: string): IFuture { - return Future.fromResult(); + async buildForDeploy(projectRoot: string): Promise { + return Promise.resolve(); } isPlatformPrepared(projectRoot: string): boolean { return false; @@ -313,27 +314,28 @@ export class PlatformProjectServiceStub implements IPlatformProjectService { canUpdatePlatform(installedModulePath: string): boolean { return false; } - updatePlatform(currentVersion: string, newVersion: string, canUpdate: boolean): IFuture { - return Future.fromResult(true); + async updatePlatform(currentVersion: string, newVersion: string, canUpdate: boolean): Promise { + return Promise.resolve(true); } prepareAppResources(appResourcesDirectoryPath: string): void { } - preparePluginNativeCode(pluginData: IPluginData): IFuture { - return Future.fromResult(); + async preparePluginNativeCode(pluginData: IPluginData): Promise { + return Promise.resolve(); } - removePluginNativeCode(pluginData: IPluginData): void { } - afterPrepareAllPlugins(): IFuture { - return Future.fromResult(); + async removePluginNativeCode(pluginData: IPluginData): Promise { } + + async afterPrepareAllPlugins(): Promise { + return Promise.resolve(); } - beforePrepareAllPlugins(): IFuture { - return Future.fromResult(); + async beforePrepareAllPlugins(): Promise { + return Promise.resolve(); } - deploy(deviceIdentifier: string): IFuture { - return Future.fromResult(); + async deploy(deviceIdentifier: string): Promise { + return Promise.resolve(); } - processConfigurationFilesFromAppResources(): IFuture { - return Future.fromResult(); + async processConfigurationFilesFromAppResources(): Promise { + return Promise.resolve(); } ensureConfigurationFileInAppResources(): void { return null; @@ -343,8 +345,8 @@ export class PlatformProjectServiceStub implements IPlatformProjectService { export class ProjectDataService implements IProjectDataService { initialize(projectDir: string): void { } - getValue(propertyName: string): IFuture { - return Future.fromResult({}); + async getValue(propertyName: string): Promise { + return Promise.resolve({}); } setValue(key: string, value: any): void { } @@ -369,22 +371,17 @@ export class ProjectHelperStub implements IProjectHelper { } export class ProjectTemplatesService implements IProjectTemplatesService { - get defaultTemplatePath(): IFuture { - return Future.fromResult(""); - } - setProjectDir(projectDir: string): void { - } - prepareTemplate(templateName: string): IFuture { - return Future.fromResult(""); + async prepareTemplate(templateName: string): Promise { + return Promise.resolve(""); } } export class HooksServiceStub implements IHooksService { - executeBeforeHooks(commandName: string): IFuture { - return Future.fromResult(); + async executeBeforeHooks(commandName: string): Promise { + return Promise.resolve(); } - executeAfterHooks(commandName: string): IFuture { - return Future.fromResult(); + async executeAfterHooks(commandName: string): Promise { + return Promise.resolve(); } hookArgsName = "hookArgs"; @@ -392,16 +389,14 @@ export class HooksServiceStub implements IHooksService { export class LockFile { - check(): IFuture { - return (() => { return false; }).future()(); + async check(): Promise { + return false; } - lock(): IFuture { - return (() => { }).future()(); + async lock(): Promise { } - unlock(): IFuture { - return (() => { }).future()(); + async unlock(): Promise { } } @@ -416,29 +411,25 @@ export class PrompterStub implements IPrompter { } } - get(schemas: IPromptSchema[]): IFuture { + async get(schemas: IPromptSchema[]): Promise { throw unreachable(); } - getPassword(prompt: string, options?: IAllowEmpty): IFuture { + async getPassword(prompt: string, options?: IAllowEmpty): Promise { chai.assert.ok(prompt in this.passwords, `PrompterStub didn't expect to give password for: ${prompt}`); let result = this.passwords[prompt]; delete this.passwords[prompt]; - return (() => { - return result; - }).future()(); + return result; } - getString(prompt: string, options?: IPrompterOptions): IFuture { + async getString(prompt: string, options?: IPrompterOptions): Promise { chai.assert.ok(prompt in this.strings, `PrompterStub didn't expect to be asked for: ${prompt}`); let result = this.strings[prompt]; delete this.strings[prompt]; - return (() => { - return result; - }).future()(); + return result; } - promptForChoice(promptMessage: string, choices: any[]): IFuture { + async promptForChoice(promptMessage: string, choices: any[]): Promise { throw unreachable(); } - confirm(prompt: string, defaultAction?: () => boolean): IFuture { + async confirm(prompt: string, defaultAction?: () => boolean): Promise { throw unreachable(); } dispose(): void { @@ -466,52 +457,52 @@ function unexpected(msg: string): Error { } export class DebugServiceStub implements IDebugService { - public debug(shouldBreak?: boolean): IFuture { - return Future.fromResult(); + public async debug(shouldBreak?: boolean): Promise { + return; } - public debugStart(): IFuture { - return Future.fromResult(); + public async debugStart(): Promise { + return; } - public debugStop(): IFuture { - return Future.fromResult(); + public async debugStop(): Promise { + return; } public platform: string; } export class LiveSyncServiceStub implements ILiveSyncService { - public liveSync(platform: string, applicationReloadAction?: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => IFuture): IFuture { - return Future.fromResult(); + public async liveSync(platform: string, applicationReloadAction?: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise): Promise { + return; } } export class AndroidToolsInfoStub implements IAndroidToolsInfo { - public getToolsInfo(): IFuture { + public async getToolsInfo(): Promise { let infoData: IAndroidToolsInfoData = Object.create(null); infoData.androidHomeEnvVar = ""; infoData.compileSdkVersion = 23; infoData.buildToolsVersion = "23"; infoData.targetSdkVersion = 23; infoData.supportRepositoryVersion = "23"; - return Future.fromResult(infoData); + return infoData; } - public validateInfo(options?: { showWarningsAsErrors: boolean, validateTargetSdk: boolean }): IFuture { - return Future.fromResult(true); + public async validateInfo(options?: { showWarningsAsErrors: boolean, validateTargetSdk: boolean }): Promise { + return true; } - public validateJavacVersion(installedJavaVersion: string, options?: { showWarningsAsErrors: boolean }): IFuture { - return Future.fromResult(true); + public async validateJavacVersion(installedJavaVersion: string, options?: { showWarningsAsErrors: boolean }): Promise { + return true; } - public getPathToAndroidExecutable(options?: { showWarningsAsErrors: boolean }): IFuture { - return Future.fromResult(""); + public async getPathToAndroidExecutable(options?: { showWarningsAsErrors: boolean }): Promise { + return ""; } - getPathToAdbFromAndroidHome(): IFuture { - return Future.fromResult(""); + async getPathToAdbFromAndroidHome(): Promise { + return Promise.resolve(""); } } @@ -528,11 +519,11 @@ export class ChildProcessStub { return null; } - public spawnFromEvent(command: string, args: string[], event: string, options?: any, spawnFromEventOptions?: ISpawnFromEventOptions): IFuture { + public async spawnFromEvent(command: string, args: string[], event: string, options?: any, spawnFromEventOptions?: ISpawnFromEventOptions): Promise { this.spawnFromEventCount++; this.lastCommand = command; this.lastCommandArgs = args; - return Future.fromResult(null); + return null; } } @@ -558,30 +549,30 @@ export class ProjectChangesService implements IProjectChangesService { } export class CommandsService implements ICommandsService { - public allCommands(opts: {includeDevCommands: boolean}): string[] { + public allCommands(opts: { includeDevCommands: boolean }): string[] { return []; } - public tryExecuteCommand(commandName: string, commandArguments: string[]): IFuture { - return Future.fromResult(); + public tryExecuteCommand(commandName: string, commandArguments: string[]): Promise { + return Promise.resolve(); } - public executeCommandUnchecked(commandName: string, commandArguments: string[]): IFuture { - return Future.fromResult(true); + public executeCommandUnchecked(commandName: string, commandArguments: string[]): Promise { + return Promise.resolve(true); } - public completeCommand(): IFuture { - return Future.fromResult(true); + public completeCommand(): Promise { + return Promise.resolve(true); } } export class PlatformServiceStub implements IPlatformService { - public validateOptions(): IFuture { - return Future.fromResult(true); + public validateOptions(): Promise { + return Promise.resolve(true); } - public addPlatforms(platforms: string[]): IFuture { - return Future.fromResult(); + public addPlatforms(platforms: string[]): Promise { + return Promise.resolve(); } public getInstalledPlatforms(): string[] { @@ -600,44 +591,44 @@ export class PlatformServiceStub implements IPlatformService { } - public updatePlatforms(platforms: string[]): IFuture { - return Future.fromResult(); + public updatePlatforms(platforms: string[]): Promise { + return Promise.resolve(); } - public preparePlatform(platform: string, changesInfo?: IProjectChangesInfo): IFuture { - return Future.fromResult(true); + public preparePlatform(platform: string, changesInfo?: IProjectChangesInfo): Promise { + return Promise.resolve(true); } - public shouldBuild(platform: string, buildConfig?: IBuildConfig): IFuture { - return Future.fromResult(true); + public shouldBuild(platform: string, buildConfig?: IBuildConfig): Promise { + return Promise.resolve(true); } - public buildPlatform(platform: string, buildConfig?: IBuildConfig): IFuture { - return Future.fromResult(); + public buildPlatform(platform: string, buildConfig?: IBuildConfig): Promise { + return Promise.resolve(); } - public shouldInstall(device: Mobile.IDevice): boolean { + public async shouldInstall(device: Mobile.IDevice): Promise { return true; } - public installApplication(device: Mobile.IDevice): IFuture { - return Future.fromResult(); + public installApplication(device: Mobile.IDevice): Promise { + return Promise.resolve(); } - public deployPlatform(platform: string, forceInstall?: boolean): IFuture { - return Future.fromResult(); + public deployPlatform(platform: string, forceInstall?: boolean): Promise { + return Promise.resolve(); } - public runPlatform(platform: string): IFuture { - return Future.fromResult(); + public runPlatform(platform: string): Promise { + return Promise.resolve(); } - public emulatePlatform(platform: string): IFuture { - return Future.fromResult(); + public emulatePlatform(platform: string): Promise { + return Promise.resolve(); } - public cleanDestinationApp(platform: string): IFuture { - return Future.fromResult(); + public cleanDestinationApp(platform: string): Promise { + return Promise.resolve(); } public validatePlatformInstalled(platform: string): void { @@ -656,36 +647,36 @@ export class PlatformServiceStub implements IPlatformService { return null; } - public copyLastOutput(platform: string, targetPath: string, settings: {isForDevice: boolean}): void { + public copyLastOutput(platform: string, targetPath: string, settings: { isForDevice: boolean }): void { } public lastOutputPath(platform: string, settings: { isForDevice: boolean }): string { return ""; } - public readFile(device: Mobile.IDevice, deviceFilePath: string): IFuture { - return Future.fromResult(""); + public readFile(device: Mobile.IDevice, deviceFilePath: string): Promise { + return Promise.resolve(""); } } export class EmulatorPlatformService implements IEmulatorPlatformService { - public listAvailableEmulators(platform: string): IFuture { - return Future.fromResult(); + public listAvailableEmulators(platform: string): Promise { + return Promise.resolve(); } - public getEmulatorInfo(platform: string, nameOfId: string): IFuture { - return Future.fromResult(null); + public getEmulatorInfo(platform: string, nameOfId: string): Promise { + return Promise.resolve(null); } - public getiOSEmulators(): IFuture { - return Future.fromResult(null); + public getiOSEmulators(): Promise { + return Promise.resolve(null); } - public getAndroidEmulators(): IFuture { - return Future.fromResult(null); + public getAndroidEmulators(): Promise { + return Promise.resolve(null); } - public startEmulator(info: IEmulatorInfo): IFuture { - return Future.fromResult(); + public startEmulator(info: IEmulatorInfo): Promise { + return Promise.resolve(); } } diff --git a/test/test-bootstrap.ts b/test/test-bootstrap.ts index 19645874a9..949eeda893 100644 --- a/test/test-bootstrap.ts +++ b/test/test-bootstrap.ts @@ -3,6 +3,8 @@ shelljs.config.silent = true; shelljs.config.fatal = true; global._ = require("lodash"); global.$injector = require("../lib/common/yok").injector; +import { use } from "chai"; +use(require("chai-as-promised")); $injector.register("analyticsService", { trackException: (): {wait(): void} => { @@ -15,7 +17,3 @@ $injector.register("analyticsService", { // Converts the js callstack to typescript import errors = require("../lib/common/errors"); errors.installUncaughtExceptionListener(); - -process.on('exit', (code: number) => { - require("fibers/future").assertNoFutureLeftBehind(); -}); diff --git a/test/tns-appstore-upload.ts b/test/tns-appstore-upload.ts index 905a1a754a..0920b88fea 100644 --- a/test/tns-appstore-upload.ts +++ b/test/tns-appstore-upload.ts @@ -1,9 +1,8 @@ -import {suite, test/*, only */} from "mocha-typescript"; -import {PublishIOS} from "../lib/commands/appstore-upload"; -import {PrompterStub, LoggerStub} from "./stubs"; +import { suite, test/*, only */ } from "mocha-typescript"; +import { PublishIOS } from "../lib/commands/appstore-upload"; +import { PrompterStub, LoggerStub } from "./stubs"; import * as chai from "chai"; import * as yok from "../lib/common/yok"; -import future = require("fibers/future"); @suite("tns appstore") class AppStore { @@ -69,7 +68,7 @@ class AppStore { this.command = this.injector.resolveCommand("appstore"); } - initInjector(services?: { commands?: {[service:string]: any}, services?: {[service:string]: any}}) { + initInjector(services?: { commands?: { [service: string]: any }, services?: { [service: string]: any } }) { this.injector = new yok.Yok(); if (services) { for (let cmd in services.commands) { @@ -101,7 +100,7 @@ class AppStore { this.platformService.preparePlatform = (platform: string) => { chai.assert.equal(platform, "iOS"); this.preparePlatformCalls++; - return future.fromResult(true); + return Promise.resolve(true); }; } @@ -110,13 +109,13 @@ class AppStore { this.iOSProjectService.archive = (projectRoot: string) => { this.archiveCalls++; chai.assert.equal(projectRoot, "/Users/person/git/MyProject"); - return future.fromResult("/Users/person/git/MyProject/platforms/ios/archive/MyProject.xcarchive"); + return Promise.resolve("/Users/person/git/MyProject/platforms/ios/archive/MyProject.xcarchive"); }; } expectExportArchive(expectedOptions?: { teamID?: string }) { this.expectedExportArchiveCalls = 1; - this.iOSProjectService.exportArchive = (options?: { teamID?: string, archivePath?: string } ) => { + this.iOSProjectService.exportArchive = (options?: { teamID?: string, archivePath?: string }) => { this.exportArchiveCalls++; chai.assert.equal(options.archivePath, "/Users/person/git/MyProject/platforms/ios/archive/MyProject.xcarchive", "Expected xcarchive path to be the one that we just archived."); if (expectedOptions && expectedOptions.teamID) { @@ -124,7 +123,7 @@ class AppStore { } else { chai.assert.isUndefined(options.teamID, "Expected teamID in exportArchive to be undefined"); } - return future.fromResult("/Users/person/git/MyProject/platforms/ios/archive/MyProject.ipa"); + return Promise.resolve("/Users/person/git/MyProject/platforms/ios/archive/MyProject.ipa"); }; } @@ -136,37 +135,37 @@ class AppStore { chai.assert.equal(options.username, AppStore.itunesconnect.user); chai.assert.equal(options.password, AppStore.itunesconnect.pass); chai.assert.equal(options.verboseLogging, false); - return future.fromResult(); + return Promise.resolve(); }; } @test("without args, prompts for itunesconnect credentionals, prepares, archives and uploads") - noArgs() { + async noArgs() { this.expectItunesPrompt(); this.expectPreparePlatform(); this.expectArchive(); this.expectExportArchive(); this.expectITMSTransporterUpload(); - this.command.execute([]).wait(); + await this.command.execute([]); this.assert(); } @test("with command line itunesconnect credentionals, prepares, archives and uploads") - itunesconnectArgs() { + async itunesconnectArgs() { this.expectPreparePlatform(); this.expectArchive(); this.expectExportArchive(); this.expectITMSTransporterUpload(); - this.command.execute([AppStore.itunesconnect.user, AppStore.itunesconnect.pass]).wait(); + await this.command.execute([AppStore.itunesconnect.user, AppStore.itunesconnect.pass]); this.assert(); } @test("passes --team-id to xcodebuild exportArchive") - teamIdOption() { + async teamIdOption() { this.expectItunesPrompt(); this.expectPreparePlatform(); this.expectArchive(); @@ -175,7 +174,7 @@ class AppStore { this.options.teamId = "MyTeamID"; - this.command.execute([]).wait(); + await this.command.execute([]); this.assert(); } diff --git a/tsconfig.json b/tsconfig.json index 1672106f34..d3eeb57049 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,8 @@ "removeComments": false, "noImplicitAny": true, "experimentalDecorators": true, - "alwaysStrict": true + "alwaysStrict": true, + "noUnusedLocals": true }, "exclude": [ "node_modules", diff --git a/tslint.json b/tslint.json index 40b9f253e1..e37be6cc07 100644 --- a/tslint.json +++ b/tslint.json @@ -1,49 +1,71 @@ { "rules": { "class-name": true, - "curly": true, + "curly": true, "eofline": true, - "indent": true, + "indent": [ + true, + "tabs" + ], "interface-name": true, - "jsdoc-format": true, - "max-line-length": [false, 140], - "no-consecutive-blank-lines": true, - "no-construct": true, - "no-debugger": true, - "no-duplicate-key": true, + "jsdoc-format": true, + "max-line-length": [ + false, + 140 + ], + "no-consecutive-blank-lines": true, + "no-construct": true, + "no-debugger": true, "no-duplicate-variable": true, "no-shadowed-variable": true, "no-empty": true, - "no-eval": true, + "no-eval": true, "no-switch-case-fall-through": true, "no-trailing-whitespace": true, - "no-unreachable": true, "no-unused-expression": true, - "no-unused-variable": true, "no-use-before-declare": true, "no-var-keyword": true, "no-var-requires": false, "one-line": [ true, - "check-open-brace", "check-catch", - "check-else" - ], - "quotemark": [false, "double"], + "check-finally", + "check-else", + "check-open-brace", + "check-whitespace" + ], + "quotemark": [ + false, + "double" + ], "semicolon": true, + "space-before-function-paren": false, "switch-default": false, - "triple-equals": [true, "allow-null-check"], - "use-strict": true, - "variable-name": [false, "allow-leading-underscore"], - "whitespace": [ + "trailing-comma": [ false, + { + "multiline": "always", + "singleline": "always" + } + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typeof-compare": true, + "use-isnan": true, + "variable-name": [ + true, + "ban-keywords", + "allow-leading-underscore" + ], + "whitespace": [ + true, "check-branch", "check-decl", "check-operator", "check-module", - "check-separator", - "check-type", - "check-typecast" + "check-separator" ] } } \ No newline at end of file