diff --git a/lib/commands/add-platform.ts b/lib/commands/add-platform.ts index 3d3379111b..5c15b254c5 100644 --- a/lib/commands/add-platform.ts +++ b/lib/commands/add-platform.ts @@ -1,11 +1,15 @@ export class AddPlatformCommand implements ICommand { public allowedParameters: ICommandParameter[] = []; - constructor(private $platformService: IPlatformService, - private $errors: IErrors) { } + constructor(private $options: IOptions, + private $platformService: IPlatformService, + private $projectData: IProjectData, + private $errors: IErrors) { + this.$projectData.initializeProjectData(); + } public async execute(args: string[]): Promise { - await this.$platformService.addPlatforms(args); + await this.$platformService.addPlatforms(args, this.$options.platformTemplate, this.$projectData); } public async canExecute(args: string[]): Promise { @@ -13,7 +17,7 @@ export class AddPlatformCommand implements ICommand { 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, this.$projectData)); return true; } diff --git a/lib/commands/appstore-upload.ts b/lib/commands/appstore-upload.ts index a0a79e2f79..1022d2a045 100644 --- a/lib/commands/appstore-upload.ts +++ b/lib/commands/appstore-upload.ts @@ -11,9 +11,12 @@ export class PublishIOS implements ICommand { private $injector: IInjector, private $itmsTransporterService: IITMSTransporterService, private $logger: ILogger, + private $projectData: IProjectData, private $options: IOptions, private $prompter: IPrompter, - private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants) { } + private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants) { + this.$projectData.initializeProjectData(); + } private get $platformsData(): IPlatformsData { return this.$injector.resolve("platformsData"); @@ -54,28 +57,34 @@ export class PublishIOS implements ICommand { if (!ipaFilePath) { let platform = this.$devicePlatformsConstants.iOS; // No .ipa path provided, build .ipa on out own. + const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; if (mobileProvisionIdentifier || codeSignIdentity) { let iOSBuildConfig: IiOSBuildConfig = { + projectDir: this.$options.path, + release: this.$options.release, + device: this.$options.device, + provision: this.$options.provision, + teamId: this.$options.teamId, 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 }); + await this.$platformService.preparePlatform(platform, appFilesUpdaterOptions, this.$options.platformTemplate, this.$projectData, this.$options.provision); + await this.$platformService.buildPlatform(platform, iOSBuildConfig, this.$projectData); + ipaFilePath = this.$platformService.lastOutputPath(platform, { isForDevice: iOSBuildConfig.buildForDevice }, this.$projectData); } 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); + await this.$platformService.preparePlatform(platform, appFilesUpdaterOptions, this.$options.platformTemplate, this.$projectData, this.$options.provision); - let platformData = this.$platformsData.getPlatformData(platform); + let platformData = this.$platformsData.getPlatformData(platform, this.$projectData); let iOSProjectService = platformData.platformProjectService; - let archivePath = await iOSProjectService.archive(platformData.projectRoot); + let archivePath = await iOSProjectService.archive(this.$projectData); this.$logger.info("Archive at: " + archivePath); - let exportPath = await iOSProjectService.exportArchive({ archivePath, teamID }); + let exportPath = await iOSProjectService.exportArchive(this.$projectData, { archivePath, teamID }); this.$logger.info("Export at: " + exportPath); ipaFilePath = exportPath; diff --git a/lib/commands/build.ts b/lib/commands/build.ts index 043bb5a800..9a495533e3 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -1,15 +1,28 @@ export class BuildCommandBase { constructor(protected $options: IOptions, + protected $projectData: IProjectData, protected $platformsData: IPlatformsData, - protected $platformService: IPlatformService) { } + protected $platformService: IPlatformService) { + this.$projectData.initializeProjectData(); + } public async executeCore(args: string[]): Promise { let platform = args[0].toLowerCase(); - await this.$platformService.preparePlatform(platform); + const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; + await this.$platformService.preparePlatform(platform, appFilesUpdaterOptions, this.$options.platformTemplate, this.$projectData, this.$options.provision); this.$options.clean = true; - await this.$platformService.buildPlatform(platform); + const buildConfig: IiOSBuildConfig = { + buildForDevice: this.$options.forDevice, + projectDir: this.$options.path, + clean: this.$options.clean, + teamId: this.$options.teamId, + device: this.$options.device, + provision: this.$options.provision, + release: this.$options.release + }; + await this.$platformService.buildPlatform(platform, buildConfig, this.$projectData); if (this.$options.copyTo) { - this.$platformService.copyLastOutput(platform, this.$options.copyTo, { isForDevice: this.$options.forDevice }); + this.$platformService.copyLastOutput(platform, this.$options.copyTo, { isForDevice: this.$options.forDevice }, this.$projectData); } } } @@ -18,9 +31,10 @@ export class BuildIosCommand extends BuildCommandBase implements ICommand { public allowedParameters: ICommandParameter[] = []; constructor(protected $options: IOptions, + $projectData: IProjectData, $platformsData: IPlatformsData, $platformService: IPlatformService) { - super($options, $platformsData, $platformService); + super($options, $projectData, $platformsData, $platformService); } public async execute(args: string[]): Promise { @@ -28,7 +42,7 @@ export class BuildIosCommand extends BuildCommandBase implements ICommand { } public canExecute(args: string[]): Promise { - return args.length === 0 && this.$platformService.validateOptions(this.$platformsData.availablePlatforms.iOS); + return args.length === 0 && this.$platformService.validateOptions(this.$options.provision, this.$projectData, this.$platformsData.availablePlatforms.iOS); } } @@ -38,10 +52,11 @@ export class BuildAndroidCommand extends BuildCommandBase implements ICommand { public allowedParameters: ICommandParameter[] = []; constructor(protected $options: IOptions, + $projectData: IProjectData, $platformsData: IPlatformsData, private $errors: IErrors, $platformService: IPlatformService) { - super($options, $platformsData, $platformService); + super($options, $projectData, $platformsData, $platformService); } public async execute(args: string[]): Promise { @@ -52,7 +67,7 @@ export class BuildAndroidCommand extends BuildCommandBase implements ICommand { 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); + return args.length === 0 && await this.$platformService.validateOptions(this.$options.provision, this.$projectData, this.$platformsData.availablePlatforms.Android); } } diff --git a/lib/commands/clean-app.ts b/lib/commands/clean-app.ts index 3188392ece..d11678ff3d 100644 --- a/lib/commands/clean-app.ts +++ b/lib/commands/clean-app.ts @@ -1,18 +1,23 @@ export class CleanAppCommandBase { constructor(protected $options: IOptions, - private $platformService: IPlatformService) { } + protected $projectData: IProjectData, + private $platformService: IPlatformService) { + this.$projectData.initializeProjectData(); + } public async execute(args: string[]): Promise { let platform = args[0].toLowerCase(); - return this.$platformService.cleanDestinationApp(platform); + const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; + return this.$platformService.cleanDestinationApp(platform, appFilesUpdaterOptions, this.$options.platformTemplate, this.$projectData); } } export class CleanAppIosCommand extends CleanAppCommandBase implements ICommand { constructor(protected $options: IOptions, private $platformsData: IPlatformsData, - $platformService: IPlatformService) { - super($options, $platformService); + $platformService: IPlatformService, + $projectData: IProjectData) { + super($options, $projectData, $platformService); } public allowedParameters: ICommandParameter[] = []; @@ -29,8 +34,9 @@ export class CleanAppAndroidCommand extends CleanAppCommandBase implements IComm constructor(protected $options: IOptions, private $platformsData: IPlatformsData, - $platformService: IPlatformService) { - super($options, $platformService); + $platformService: IPlatformService, + $projectData: IProjectData) { + super($options, $projectData, $platformService); } public async execute(args: string[]): Promise { diff --git a/lib/commands/debug.ts b/lib/commands/debug.ts index fd818d1910..0e2e5359f5 100644 --- a/lib/commands/debug.ts +++ b/lib/commands/debug.ts @@ -9,15 +9,29 @@ private $config: IConfiguration, private $usbLiveSyncService: ILiveSyncService, protected $platformService: IPlatformService, + protected $projectData: IProjectData, protected $options: IOptions, - protected $platformsData: IPlatformsData) { } + protected $platformsData: IPlatformsData) { + this.$projectData.initializeProjectData(); + } public async execute(args: string[]): Promise { if (this.$options.start) { - return this.debugService.debug(); + return this.debugService.debug(this.$projectData); } - await this.$platformService.deployPlatform(this.$devicesService.platform); + const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; + const deployOptions: IDeployPlatformOptions = { + clean: this.$options.clean, + device: this.$options.device, + emulator: this.$options.emulator, + platformTemplate: this.$options.platformTemplate, + projectDir: this.$options.path, + release: this.$options.release, + provision: this.$options.provision, + teamId: this.$options.teamId + }; + await this.$platformService.deployPlatform(this.$devicesService.platform, appFilesUpdaterOptions, deployOptions, this.$projectData, this.$options.provision); this.$config.debugLivesync = true; let applicationReloadAction = async (deviceAppData: Mobile.IDeviceAppData): Promise => { let projectData: IProjectData = this.$injector.resolve("projectData"); @@ -31,9 +45,9 @@ await deviceAppData.device.applicationManager.stopApplication(applicationId); - await this.debugService.debug(); + await this.debugService.debug(this.$projectData); }; - return this.$usbLiveSyncService.liveSync(this.$devicesService.platform, applicationReloadAction); + return this.$usbLiveSyncService.liveSync(this.$devicesService.platform, this.$projectData, applicationReloadAction); } public async canExecute(args: string[]): Promise { @@ -64,13 +78,14 @@ export class DebugIOSCommand extends DebugPlatformCommand { $usbLiveSyncService: ILiveSyncService, $platformService: IPlatformService, $options: IOptions, + $projectData: IProjectData, $platformsData: IPlatformsData) { - super($iOSDebugService, $devicesService, $injector, $logger, $devicePlatformsConstants, $config, $usbLiveSyncService, $platformService, $options, $platformsData); + super($iOSDebugService, $devicesService, $injector, $logger, $devicePlatformsConstants, $config, $usbLiveSyncService, $platformService, $projectData, $options, $platformsData); } public async canExecute(args: string[]): Promise { - return await super.canExecute(args) && await this.$platformService.validateOptions(this.$platformsData.availablePlatforms.iOS); + return await super.canExecute(args) && await this.$platformService.validateOptions(this.$options.provision, this.$projectData, this.$platformsData.availablePlatforms.iOS); } } @@ -86,13 +101,14 @@ export class DebugAndroidCommand extends DebugPlatformCommand { $usbLiveSyncService: ILiveSyncService, $platformService: IPlatformService, $options: IOptions, + $projectData: IProjectData, $platformsData: IPlatformsData) { - super($androidDebugService, $devicesService, $injector, $logger, $devicePlatformsConstants, $config, $usbLiveSyncService, $platformService, $options, $platformsData); + super($androidDebugService, $devicesService, $injector, $logger, $devicePlatformsConstants, $config, $usbLiveSyncService, $platformService, $projectData, $options, $platformsData); } public async canExecute(args: string[]): Promise { - return await super.canExecute(args) && await this.$platformService.validateOptions(this.$platformsData.availablePlatforms.Android); + return await super.canExecute(args) && await this.$platformService.validateOptions(this.$options.provision, this.$projectData, this.$platformsData.availablePlatforms.Android); } } diff --git a/lib/commands/deploy.ts b/lib/commands/deploy.ts index 9eaf4c8e96..a4ec685f50 100644 --- a/lib/commands/deploy.ts +++ b/lib/commands/deploy.ts @@ -4,11 +4,26 @@ export class DeployOnDeviceCommand implements ICommand { constructor(private $platformService: IPlatformService, private $platformCommandParameter: ICommandParameter, private $options: IOptions, + private $projectData: IProjectData, private $errors: IErrors, - private $mobileHelper: Mobile.IMobileHelper) { } + private $mobileHelper: Mobile.IMobileHelper) { + this.$projectData.initializeProjectData(); + } public async execute(args: string[]): Promise { - return this.$platformService.deployPlatform(args[0], true); + const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; + const deployOptions: IDeployPlatformOptions = { + clean: this.$options.clean, + device: this.$options.device, + projectDir: this.$options.path, + emulator: this.$options.emulator, + platformTemplate: this.$options.platformTemplate, + release: this.$options.release, + forceInstall: true, + provision: this.$options.provision, + teamId: this.$options.teamId + }; + return this.$platformService.deployPlatform(args[0], appFilesUpdaterOptions, deployOptions, this.$projectData, this.$options.provision); } public async canExecute(args: string[]): Promise { @@ -24,7 +39,7 @@ export class DeployOnDeviceCommand implements ICommand { this.$errors.fail("When producing a release build, you need to specify all --key-store-* options."); } - return this.$platformService.validateOptions(args[0]); + return this.$platformService.validateOptions(this.$options.provision, this.$projectData, args[0]); } } diff --git a/lib/commands/emulate.ts b/lib/commands/emulate.ts index 96321f5e37..df7a04bb90 100644 --- a/lib/commands/emulate.ts +++ b/lib/commands/emulate.ts @@ -1,17 +1,38 @@ export class EmulateCommandBase { - constructor(private $platformService: IPlatformService) { } + constructor(private $options: IOptions, + private $projectData: IProjectData, + private $platformService: IPlatformService) { + this.$projectData.initializeProjectData(); + } public async executeCore(args: string[]): Promise { - return this.$platformService.emulatePlatform(args[0]); + this.$options.emulator = true; + const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; + const emulateOptions: IEmulatePlatformOptions = { + avd: this.$options.avd, + clean: this.$options.clean, + device: this.$options.device, + release: this.$options.release, + emulator: this.$options.emulator, + projectDir: this.$options.path, + justlaunch: this.$options.justlaunch, + availableDevices: this.$options.availableDevices, + platformTemplate: this.$options.platformTemplate, + provision: this.$options.provision, + teamId: this.$options.teamId + }; + return this.$platformService.emulatePlatform(args[0], appFilesUpdaterOptions, emulateOptions, this.$projectData, this.$options.provision); } } export class EmulateIosCommand extends EmulateCommandBase implements ICommand { public allowedParameters: ICommandParameter[] = []; - constructor($platformService: IPlatformService, + constructor($options: IOptions, + $projectData: IProjectData, + $platformService: IPlatformService, private $platformsData: IPlatformsData) { - super($platformService); + super($options, $projectData, $platformService); } public async execute(args: string[]): Promise { @@ -22,9 +43,11 @@ export class EmulateIosCommand extends EmulateCommandBase implements ICommand { $injector.registerCommand("emulate|ios", EmulateIosCommand); export class EmulateAndroidCommand extends EmulateCommandBase implements ICommand { - constructor($platformService: IPlatformService, + constructor($options: IOptions, + $projectData: IProjectData, + $platformService: IPlatformService, private $platformsData: IPlatformsData) { - super($platformService); + super($options, $projectData, $platformService); } public allowedParameters: ICommandParameter[] = []; diff --git a/lib/commands/install.ts b/lib/commands/install.ts index eed0966053..6dde3308ff 100644 --- a/lib/commands/install.ts +++ b/lib/commands/install.ts @@ -4,7 +4,8 @@ export class InstallCommand implements ICommand { public enableHooks = false; public allowedParameters: ICommandParameter[] = [this.$stringParameter]; - constructor(private $platformsData: IPlatformsData, + constructor(private $options: IOptions, + private $platformsData: IPlatformsData, private $platformService: IPlatformService, private $projectData: IProjectData, private $projectDataService: IProjectDataService, @@ -12,7 +13,9 @@ export class InstallCommand implements ICommand { private $logger: ILogger, private $fs: IFileSystem, private $stringParameter: ICommandParameter, - private $npm: INodePackageManager) { } + private $npm: INodePackageManager) { + this.$projectData.initializeProjectData(); + } public async execute(args: string[]): Promise { return args[0] ? this.installModule(args[0]) : this.installProjectDependencies(); @@ -21,14 +24,14 @@ export class InstallCommand implements ICommand { private async installProjectDependencies(): Promise { let error: string = ""; - await this.$pluginsService.ensureAllDependenciesAreInstalled(); + await this.$pluginsService.ensureAllDependenciesAreInstalled(this.$projectData); for (let platform of this.$platformsData.platformsNames) { - let platformData = this.$platformsData.getPlatformData(platform); + let platformData = this.$platformsData.getPlatformData(platform, this.$projectData); const frameworkPackageData = this.$projectDataService.getNSValue(this.$projectData.projectDir, platformData.frameworkPackageName); if (frameworkPackageData && frameworkPackageData.version) { try { - await this.$platformService.addPlatforms([`${platform}@${frameworkPackageData.version}`]); + await this.$platformService.addPlatforms([`${platform}@${frameworkPackageData.version}`], this.$options.platformTemplate, this.$projectData); } catch (err) { error = `${error}${EOL}${err}`; } diff --git a/lib/commands/list-platforms.ts b/lib/commands/list-platforms.ts index 5faca7e5d9..a90326b717 100644 --- a/lib/commands/list-platforms.ts +++ b/lib/commands/list-platforms.ts @@ -4,13 +4,16 @@ export class ListPlatformsCommand implements ICommand { public allowedParameters: ICommandParameter[] = []; constructor(private $platformService: IPlatformService, - private $logger: ILogger) { } + private $projectData: IProjectData, + private $logger: ILogger) { + this.$projectData.initializeProjectData(); + } public async execute(args: string[]): Promise { - let installedPlatforms = this.$platformService.getInstalledPlatforms(); + let installedPlatforms = this.$platformService.getInstalledPlatforms(this.$projectData); if (installedPlatforms.length > 0) { - let preparedPlatforms = this.$platformService.getPreparedPlatforms(); + let preparedPlatforms = this.$platformService.getPreparedPlatforms(this.$projectData); if (preparedPlatforms.length > 0) { this.$logger.out("The project is prepared for: ", helpers.formatListOfNames(preparedPlatforms, "and")); } else { @@ -19,7 +22,7 @@ export class ListPlatformsCommand implements ICommand { this.$logger.out("Installed platforms: ", helpers.formatListOfNames(installedPlatforms, "and")); } else { - let formattedPlatformsList = helpers.formatListOfNames(this.$platformService.getAvailablePlatforms(), "and"); + let formattedPlatformsList = helpers.formatListOfNames(this.$platformService.getAvailablePlatforms(this.$projectData), "and"); this.$logger.out("Available platforms for this OS: ", formattedPlatformsList); this.$logger.out("No installed platforms found. Use $ tns platform add"); } diff --git a/lib/commands/livesync.ts b/lib/commands/livesync.ts index ff3fe45f46..4ac66d0176 100644 --- a/lib/commands/livesync.ts +++ b/lib/commands/livesync.ts @@ -4,17 +4,32 @@ export class LivesyncCommand implements ICommand { constructor(private $usbLiveSyncService: ILiveSyncService, private $mobileHelper: Mobile.IMobileHelper, private $platformService: IPlatformService, + private $projectData: IProjectData, private $errors: IErrors, private $logger: ILogger, - private $options: IOptions) { } + private $options: IOptions) { + this.$projectData.initializeProjectData(); + } public async execute(args: string[]): Promise { if (!this.$options.help && args[0]) { this.$logger.warn('This command is deprecated. It will be removed in the next version of NativeScript CLI. Use "$ tns run" command instead.'); } - await this.$platformService.deployPlatform(args[0]); - return this.$usbLiveSyncService.liveSync(args[0]); + const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; + const deployOptions: IDeployPlatformOptions = { + clean: this.$options.clean, + device: this.$options.device, + emulator: this.$options.emulator, + projectDir: this.$options.path, + platformTemplate: this.$options.platformTemplate, + release: this.$options.release, + provision: this.$options.provision, + teamId: this.$options.teamId + }; + + await this.$platformService.deployPlatform(args[0], appFilesUpdaterOptions, deployOptions, this.$projectData, this.$options.provision); + return this.$usbLiveSyncService.liveSync(args[0], this.$projectData); } public async canExecute(args: string[]): Promise { @@ -24,9 +39,9 @@ export class LivesyncCommand implements ICommand { let platform = args[0]; if (platform) { - return _.includes(this.$mobileHelper.platformNames, this.$mobileHelper.normalizePlatformName(platform)) && await this.$platformService.validateOptions(args[0]); + return _.includes(this.$mobileHelper.platformNames, this.$mobileHelper.normalizePlatformName(platform)) && await this.$platformService.validateOptions(this.$options.provision, this.$projectData, args[0]); } else { - return await this.$platformService.validateOptions(); + return await this.$platformService.validateOptions(this.$options.provision, this.$projectData); } } } diff --git a/lib/commands/platform-clean.ts b/lib/commands/platform-clean.ts index 32eb477c9c..939ec4a9aa 100644 --- a/lib/commands/platform-clean.ts +++ b/lib/commands/platform-clean.ts @@ -1,12 +1,16 @@ export class CleanCommand implements ICommand { public allowedParameters: ICommandParameter[] = []; - constructor(private $platformService: IPlatformService, - private $errors: IErrors) { } + constructor(private $options: IOptions, + private $projectData: IProjectData, + private $platformService: IPlatformService, + private $errors: IErrors) { + this.$projectData.initializeProjectData(); + } public async execute(args: string[]): Promise { - this.$platformService.removePlatforms(args); - await this.$platformService.addPlatforms(args); + await this.$platformService.removePlatforms(args, this.$projectData); + await this.$platformService.addPlatforms(args, this.$options.platformTemplate, this.$projectData); } public async canExecute(args: string[]): Promise { @@ -14,7 +18,7 @@ export class CleanCommand implements ICommand { 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, this.$projectData)); return true; } diff --git a/lib/commands/plugin/add-plugin.ts b/lib/commands/plugin/add-plugin.ts index 75f06f2a8e..ed9fd44d3d 100644 --- a/lib/commands/plugin/add-plugin.ts +++ b/lib/commands/plugin/add-plugin.ts @@ -2,10 +2,13 @@ export class AddPluginCommand implements ICommand { public allowedParameters: ICommandParameter[] = []; constructor(private $pluginsService: IPluginsService, - private $errors: IErrors) { } + private $projectData: IProjectData, + private $errors: IErrors) { + this.$projectData.initializeProjectData(); + } public async execute(args: string[]): Promise { - return this.$pluginsService.add(args[0]); + return this.$pluginsService.add(args[0], this.$projectData); } public async canExecute(args: string[]): Promise { @@ -13,7 +16,7 @@ export class AddPluginCommand implements ICommand { this.$errors.fail("You must specify plugin name."); } - let installedPlugins = await this.$pluginsService.getAllInstalledPlugins(); + let installedPlugins = await this.$pluginsService.getAllInstalledPlugins(this.$projectData); let pluginName = args[0].toLowerCase(); if (_.some(installedPlugins, (plugin: IPluginData) => plugin.name.toLowerCase() === pluginName)) { this.$errors.failWithoutHelp(`Plugin "${pluginName}" is already installed.`); diff --git a/lib/commands/plugin/list-plugins.ts b/lib/commands/plugin/list-plugins.ts index 761abac36f..cc5d52e480 100644 --- a/lib/commands/plugin/list-plugins.ts +++ b/lib/commands/plugin/list-plugins.ts @@ -4,10 +4,13 @@ export class ListPluginsCommand implements ICommand { public allowedParameters: ICommandParameter[] = []; constructor(private $pluginsService: IPluginsService, - private $logger: ILogger) { } + private $projectData: IProjectData, + private $logger: ILogger) { + this.$projectData.initializeProjectData(); + } public async execute(args: string[]): Promise { - let installedPlugins: IPackageJsonDepedenciesResult = this.$pluginsService.getDependenciesFromPackageJson(); + let installedPlugins: IPackageJsonDepedenciesResult = this.$pluginsService.getDependenciesFromPackageJson(this.$projectData.projectDir); let headers: string[] = ["Plugin", "Version"]; let dependenciesData: string[][] = this.createTableCells(installedPlugins.dependencies); diff --git a/lib/commands/plugin/remove-plugin.ts b/lib/commands/plugin/remove-plugin.ts index f74e479270..9bd07e9a28 100644 --- a/lib/commands/plugin/remove-plugin.ts +++ b/lib/commands/plugin/remove-plugin.ts @@ -4,10 +4,12 @@ export class RemovePluginCommand implements ICommand { constructor(private $pluginsService: IPluginsService, private $errors: IErrors, private $logger: ILogger, - private $projectData: IProjectData) { } + private $projectData: IProjectData) { + this.$projectData.initializeProjectData(); + } public async execute(args: string[]): Promise { - return this.$pluginsService.remove(args[0]); + return this.$pluginsService.remove(args[0], this.$projectData); } public async canExecute(args: string[]): Promise { @@ -18,7 +20,7 @@ export class RemovePluginCommand implements ICommand { 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(); + let installedPlugins = await this.$pluginsService.getAllInstalledPlugins(this.$projectData); pluginNames = installedPlugins.map(pl => pl.name); } catch (err) { this.$logger.trace("Error while installing plugins. Error is:", err); diff --git a/lib/commands/plugin/update-plugin.ts b/lib/commands/plugin/update-plugin.ts index 600a38d84b..b0d03d1941 100644 --- a/lib/commands/plugin/update-plugin.ts +++ b/lib/commands/plugin/update-plugin.ts @@ -1,18 +1,21 @@ export class UpdatePluginCommand implements ICommand { constructor(private $pluginsService: IPluginsService, - private $errors: IErrors) { } + private $projectData: IProjectData, + private $errors: IErrors) { + this.$projectData.initializeProjectData(); + } public async execute(args: string[]): Promise { let pluginNames = args; if (!pluginNames || args.length === 0) { - let installedPlugins = await this.$pluginsService.getAllInstalledPlugins(); + let installedPlugins = await this.$pluginsService.getAllInstalledPlugins(this.$projectData); pluginNames = installedPlugins.map(p => p.name); } - for (let p of pluginNames) { - await this.$pluginsService.remove(p); - await this.$pluginsService.add(p); + for (let pluginName of pluginNames) { + await this.$pluginsService.remove(pluginName, this.$projectData); + await this.$pluginsService.add(pluginName, this.$projectData); } } @@ -21,7 +24,7 @@ export class UpdatePluginCommand implements ICommand { return true; } - let installedPlugins = await this.$pluginsService.getAllInstalledPlugins(); + let installedPlugins = await this.$pluginsService.getAllInstalledPlugins(this.$projectData); let installedPluginNames: string[] = installedPlugins.map(pl => pl.name); let pluginName = args[0].toLowerCase(); diff --git a/lib/commands/prepare.ts b/lib/commands/prepare.ts index 0286f12365..14dba0bb42 100644 --- a/lib/commands/prepare.ts +++ b/lib/commands/prepare.ts @@ -1,15 +1,20 @@ export class PrepareCommand implements ICommand { public allowedParameters = [this.$platformCommandParameter]; - constructor(private $platformService: IPlatformService, - private $platformCommandParameter: ICommandParameter) { } + constructor(private $options: IOptions, + private $platformService: IPlatformService, + private $projectData: IProjectData, + private $platformCommandParameter: ICommandParameter) { + this.$projectData.initializeProjectData(); + } public async execute(args: string[]): Promise { - await this.$platformService.preparePlatform(args[0]); + const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; + await this.$platformService.preparePlatform(args[0], appFilesUpdaterOptions, this.$options.platformTemplate, this.$projectData, this.$options.provision); } public async canExecute(args: string[]): Promise { - return await this.$platformCommandParameter.validate(args[0]) && await this.$platformService.validateOptions(args[0]); + return await this.$platformCommandParameter.validate(args[0]) && await this.$platformService.validateOptions(this.$options.provision, this.$projectData, args[0]); } } diff --git a/lib/commands/remove-platform.ts b/lib/commands/remove-platform.ts index 9c388a0dac..2432525753 100644 --- a/lib/commands/remove-platform.ts +++ b/lib/commands/remove-platform.ts @@ -2,10 +2,13 @@ export class RemovePlatformCommand implements ICommand { public allowedParameters: ICommandParameter[] = []; constructor(private $platformService: IPlatformService, - private $errors: IErrors) { } + private $projectData: IProjectData, + private $errors: IErrors) { + this.$projectData.initializeProjectData(); + } - public async execute(args: string[]): Promise { - this.$platformService.removePlatforms(args); + public execute(args: string[]): Promise { + return this.$platformService.removePlatforms(args, this.$projectData); } public async canExecute(args: string[]): Promise { @@ -13,7 +16,7 @@ export class RemovePlatformCommand implements ICommand { 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, this.$projectData)); return true; } diff --git a/lib/commands/run.ts b/lib/commands/run.ts index ed07ec90e6..4dc2622bc6 100644 --- a/lib/commands/run.ts +++ b/lib/commands/run.ts @@ -1,20 +1,40 @@ export class RunCommandBase { constructor(protected $platformService: IPlatformService, protected $usbLiveSyncService: ILiveSyncService, - protected $options: IOptions) { } + protected $projectData: IProjectData, + protected $options: IOptions) { + this.$projectData.initializeProjectData(); + } public async executeCore(args: string[]): Promise { - await this.$platformService.deployPlatform(args[0]); + const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; + const deployOptions: IDeployPlatformOptions = { + clean: this.$options.clean, + device: this.$options.device, + emulator: this.$options.emulator, + projectDir: this.$options.path, + platformTemplate: this.$options.platformTemplate, + release: this.$options.release, + provision: this.$options.provision, + teamId: this.$options.teamId + }; + await this.$platformService.deployPlatform(args[0], appFilesUpdaterOptions, deployOptions, this.$projectData, this.$options.provision); if (this.$options.bundle) { this.$options.watch = false; } if (this.$options.release) { - return this.$platformService.runPlatform(args[0]); + const deployOpts: IRunPlatformOptions = { + device: this.$options.device, + emulator: this.$options.emulator, + justlaunch: this.$options.justlaunch, + }; + + return this.$platformService.runPlatform(args[0], deployOpts, this.$projectData); } - return this.$usbLiveSyncService.liveSync(args[0]); + return this.$usbLiveSyncService.liveSync(args[0], this.$projectData); } } @@ -24,8 +44,9 @@ export class RunIosCommand extends RunCommandBase implements ICommand { constructor($platformService: IPlatformService, private $platformsData: IPlatformsData, $usbLiveSyncService: ILiveSyncService, + $projectData: IProjectData, $options: IOptions) { - super($platformService, $usbLiveSyncService, $options); + super($platformService, $usbLiveSyncService, $projectData, $options); } public async execute(args: string[]): Promise { @@ -33,7 +54,7 @@ export class RunIosCommand extends RunCommandBase implements ICommand { } public async canExecute(args: string[]): Promise { - return args.length === 0 && await this.$platformService.validateOptions(this.$platformsData.availablePlatforms.iOS); + return args.length === 0 && await this.$platformService.validateOptions(this.$options.provision, this.$projectData, this.$platformsData.availablePlatforms.iOS); } } @@ -45,9 +66,10 @@ export class RunAndroidCommand extends RunCommandBase implements ICommand { constructor($platformService: IPlatformService, private $platformsData: IPlatformsData, $usbLiveSyncService: ILiveSyncService, + $projectData: IProjectData, $options: IOptions, private $errors: IErrors) { - super($platformService, $usbLiveSyncService, $options); + super($platformService, $usbLiveSyncService, $projectData, $options); } public async execute(args: string[]): Promise { @@ -58,7 +80,7 @@ export class RunAndroidCommand extends RunCommandBase implements ICommand { 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); + return args.length === 0 && await this.$platformService.validateOptions(this.$options.provision, this.$projectData, this.$platformsData.availablePlatforms.Android); } } diff --git a/lib/commands/test-init.ts b/lib/commands/test-init.ts index da9d3048c4..0593173dbc 100644 --- a/lib/commands/test-init.ts +++ b/lib/commands/test-init.ts @@ -16,7 +16,9 @@ class TestInitCommand implements ICommand { private $fs: IFileSystem, private $resources: IResourceLoader, private $pluginsService: IPluginsService, - private $logger: ILogger) { } + private $logger: ILogger) { + this.$projectData.initializeProjectData(); + } public async execute(args: string[]): Promise { let projectDir = this.$projectData.projectDir; @@ -37,7 +39,7 @@ class TestInitCommand implements ICommand { }); } - await this.$pluginsService.add('nativescript-unit-test-runner'); + await this.$pluginsService.add('nativescript-unit-test-runner', this.$projectData); let testsDir = path.join(projectDir, 'app/tests'); let shouldCreateSampleTests = true; diff --git a/lib/commands/test.ts b/lib/commands/test.ts index ed5519cb2f..9e6f197120 100644 --- a/lib/commands/test.ts +++ b/lib/commands/test.ts @@ -1,6 +1,9 @@ function RunTestCommandFactory(platform: string) { - return function RunTestCommand($testExecutionService: ITestExecutionService) { - this.execute = (args: string[]): Promise => $testExecutionService.startTestRunner(platform); + return function RunTestCommand( + $testExecutionService: ITestExecutionService, + $projectData: IProjectData) { + $projectData.initializeProjectData(); + this.execute = (args: string[]): Promise => $testExecutionService.startTestRunner(platform, $projectData); this.allowedParameters = []; }; } @@ -9,8 +12,9 @@ $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[]): Promise => $testExecutionService.startKarmaServer(platform); + return function RunKarmaTestCommand($testExecutionService: ITestExecutionService, $projectData: IProjectData) { + $projectData.initializeProjectData(); + this.execute = (args: string[]): Promise => $testExecutionService.startKarmaServer(platform, $projectData); this.allowedParameters = []; }; } diff --git a/lib/commands/update-platform.ts b/lib/commands/update-platform.ts index 9ec35f2886..5ca21e3a14 100644 --- a/lib/commands/update-platform.ts +++ b/lib/commands/update-platform.ts @@ -1,11 +1,15 @@ export class UpdatePlatformCommand implements ICommand { public allowedParameters: ICommandParameter[] = []; - constructor(private $platformService: IPlatformService, - private $errors: IErrors) { } + constructor(private $options: IOptions, + private $projectData: IProjectData, + private $platformService: IPlatformService, + private $errors: IErrors) { + this.$projectData.initializeProjectData(); + } public async execute(args: string[]): Promise { - await this.$platformService.updatePlatforms(args); + await this.$platformService.updatePlatforms(args, this.$options.platformTemplate, this.$projectData); } public async canExecute(args: string[]): Promise { @@ -13,7 +17,7 @@ export class UpdatePlatformCommand implements ICommand { 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], this.$projectData)); return true; } diff --git a/lib/commands/update.ts b/lib/commands/update.ts index 3466157e1e..7514ebd778 100644 --- a/lib/commands/update.ts +++ b/lib/commands/update.ts @@ -4,13 +4,16 @@ import * as shelljs from "shelljs"; export class UpdateCommand implements ICommand { public allowedParameters: ICommandParameter[] = []; - constructor(private $projectData: IProjectData, + constructor(private $options: IOptions, + private $projectData: IProjectData, private $platformService: IPlatformService, private $platformsData: IPlatformsData, private $pluginsService: IPluginsService, private $projectDataService: IProjectDataService, private $fs: IFileSystem, - private $logger: ILogger) { } + private $logger: ILogger) { + this.$projectData.initializeProjectData(); + } public async execute(args: string[]): Promise { let folders = ["lib", "hooks", "platforms", "node_modules"]; @@ -56,12 +59,12 @@ export class UpdateCommand implements ICommand { } private async executeCore(args: string[], folders: string[]): Promise { - let platforms = this.$platformService.getInstalledPlatforms(); - let availablePlatforms = this.$platformService.getAvailablePlatforms(); + let platforms = this.$platformService.getInstalledPlatforms(this.$projectData); + let availablePlatforms = this.$platformService.getAvailablePlatforms(this.$projectData); let packagePlatforms: string[] = []; for (let platform of availablePlatforms) { - let platformData = this.$platformsData.getPlatformData(platform); + let platformData = this.$platformsData.getPlatformData(platform, this.$projectData); const platformVersion = this.$projectDataService.getNSValue(this.$projectData.projectDir, platformData.frameworkPackageName); if (platformVersion) { packagePlatforms.push(platform); @@ -69,9 +72,9 @@ export class UpdateCommand implements ICommand { } } - this.$platformService.removePlatforms(platforms); - await this.$pluginsService.remove("tns-core-modules"); - await this.$pluginsService.remove("tns-core-modules-widgets"); + await this.$platformService.removePlatforms(platforms, this.$projectData); + await this.$pluginsService.remove("tns-core-modules", this.$projectData); + await this.$pluginsService.remove("tns-core-modules-widgets", this.$projectData); for (let folder of folders) { shelljs.rm("-fr", folder); @@ -80,16 +83,16 @@ export class UpdateCommand implements ICommand { platforms = platforms.concat(packagePlatforms); if (args.length === 1) { for (let platform of platforms) { - await this.$platformService.addPlatforms([platform + "@" + args[0]]); + await this.$platformService.addPlatforms([platform + "@" + args[0]], this.$options.platformTemplate, this.$projectData); } - await this.$pluginsService.add("tns-core-modules@" + args[0]); + await this.$pluginsService.add("tns-core-modules@" + args[0], this.$projectData); } else { - await this.$platformService.addPlatforms(platforms); - await this.$pluginsService.add("tns-core-modules"); + await this.$platformService.addPlatforms(platforms, this.$options.platformTemplate, this.$projectData); + await this.$pluginsService.add("tns-core-modules", this.$projectData); } - await this.$pluginsService.ensureAllDependenciesAreInstalled(); + await this.$pluginsService.ensureAllDependenciesAreInstalled(this.$projectData); } } diff --git a/lib/common b/lib/common index 1ecec5d2cf..24d47bbdf7 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 1ecec5d2cf5f242d2c87345d68022ecbcd35927c +Subproject commit 24d47bbdf7c794a575ad8ac950fa75e9c7a1784f diff --git a/lib/constants.ts b/lib/constants.ts index 53cfcffcbf..dfa44d3b3a 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -67,3 +67,4 @@ export const ItunesConnectApplicationTypes = new ItunesConnectApplicationTypesCl export const ANGULAR_NAME = "angular"; export const TYPESCRIPT_NAME = "typescript"; +export const BUILD_OUTPUT_EVENT_NAME = "buildOutput"; diff --git a/lib/declarations.ts b/lib/declarations.ts index 90d29d2565..2ce2edd905 100644 --- a/lib/declarations.ts +++ b/lib/declarations.ts @@ -51,23 +51,73 @@ interface IOpener { } interface ILiveSyncService { - liveSync(platform: string, applicationReloadAction?: (deviceAppData: Mobile.IDeviceAppData) => Promise): Promise; + liveSync(platform: string, projectData: IProjectData, applicationReloadAction?: (deviceAppData: Mobile.IDeviceAppData) => Promise): Promise; +} + +interface INativeScriptDeviceLiveSyncService extends IDeviceLiveSyncServiceBase { + /** + * Refreshes the application's content on a device + * @param {Mobile.IDeviceAppData} deviceAppData Information about the application and the device. + * @param {Mobile.ILocalToDevicePathData[]} localToDevicePaths Object containing a mapping of file paths from the system to the device. + * @param {boolean} forceExecuteFullSync If this is passed a full LiveSync is performed instead of an incremental one. + * @param {string} projectId Project identifier - for example org.nativescript.livesync. + * @return {Promise} + */ + refreshApplication(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], forceExecuteFullSync: boolean, projectId: string): Promise; + /** + * Removes specified files from a connected device + * @param {string} appIdentifier Application identifier. + * @param {Mobile.ILocalToDevicePathData[]} localToDevicePaths Object containing a mapping of file paths from the system to the device. + * @param {string} projectId Project identifier - for example org.nativescript.livesync. + * @return {Promise} + */ + removeFiles(appIdentifier: string, localToDevicePaths: Mobile.ILocalToDevicePathData[], projectId: string): Promise; + afterInstallApplicationAction?(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], projectId: string): Promise; } interface IPlatformLiveSyncService { - 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; + fullSync(projectData: IProjectData, postAction?: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise): Promise; + partialSync(event: string, filePath: string, dispatcher: IFutureDispatcher, afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise, projectData: IProjectData): Promise; + refreshApplication(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], isFullSync: boolean, projectId: string): Promise; +} + +interface IBundle { + bundle: boolean; +} + +interface IPlatformTemplate { + platformTemplate: string; +} + +interface IEmulator { + emulator: boolean; +} + +interface IClean { + clean: boolean; +} + +interface IProvision { + provision: any; } -interface IOptions extends ICommonOptions { +interface ITeamIdentifier { + teamId: string; +} + +interface IAndroidReleaseOptions { + keyStoreAlias?: string; + keyStoreAliasPassword?: string; + keyStorePassword?: string; + keyStorePath?: string; +} + +interface IOptions extends ICommonOptions, IBundle, IPlatformTemplate, IEmulator, IClean, IProvision, ITeamIdentifier, IAndroidReleaseOptions { all: boolean; - baseConfig: string; client: boolean; compileSdk: number; copyTo: string; debugTransport: boolean; - emulator: boolean; forDevice: boolean; framework: string; frameworkName: string; @@ -76,24 +126,39 @@ interface IOptions extends ICommonOptions { ignoreScripts: boolean; //npm flag disableNpmInstall: boolean; ipa: string; - keyStoreAlias: string; - keyStoreAliasPassword: string; - keyStorePassword: string; - keyStorePath: string; - ng: boolean; tsc: boolean; + ng: boolean; androidTypings: boolean; - bundle: boolean; - platformTemplate: string; port: Number; production: boolean; //npm flag sdk: string; - teamId: string; syncAllFiles: boolean; liveEdit: boolean; chrome: boolean; - clean: boolean; - provision: any; +} + +interface IAndroidBuildOptionsSettings extends IAndroidReleaseOptions, IRelease { } + +interface IAppFilesUpdaterOptions extends IBundle, IRelease { } + +interface IPlatformBuildData extends IAppFilesUpdaterOptions, IBuildConfig { } + +interface IDeviceEmulator extends IEmulator, IDeviceIdentifier { } + +interface IRunPlatformOptions extends IJustLaunch, IDeviceEmulator { } + +interface IDeployPlatformOptions extends IPlatformTemplate, IRelease, IClean, IDeviceEmulator, IProvision, ITeamIdentifier { + projectDir: string; + forceInstall?: boolean; +} + +interface IEmulatePlatformOptions extends IJustLaunch, IDeployPlatformOptions, IAvailableDevices, IAvd { +} + +interface IUpdatePlatformOptions extends IPlatformTemplate { + currentVersion: string; + newVersion: string; + canUpdate: boolean; } interface IInitService { @@ -229,13 +294,13 @@ interface ISocketProxyFactory { } interface IiOSNotification { - waitForDebug: string; - attachRequest: string; - appLaunching: string; - readyForAttach: string; - attachAvailabilityQuery: string; - alreadyConnected: string; - attachAvailable: string; + getWaitForDebug(projectId: string): string; + getAttachRequest(projectId: string): string; + getAppLaunching(projectId: string): string; + getReadyForAttach(projectId: string): string; + getAttachAvailabilityQuery(projectId: string): string; + getAlreadyConnected(projectId: string): string; + getAttachAvailable(projectId: string): string; } interface IiOSNotificationService { @@ -243,8 +308,8 @@ interface IiOSNotificationService { } interface IiOSSocketRequestExecutor { - executeLaunchRequest(device: Mobile.IiOSDevice, timeout: number, readyForAttachTimeout: number, shouldBreak?: boolean): Promise; - executeAttachRequest(device: Mobile.IiOSDevice, timeout: number): Promise; + executeLaunchRequest(device: Mobile.IiOSDevice, timeout: number, readyForAttachTimeout: number, projectId: string, shouldBreak?: boolean): Promise; + executeAttachRequest(device: Mobile.IiOSDevice, timeout: number, projectId: string): Promise; } /** diff --git a/lib/definitions/debug.d.ts b/lib/definitions/debug.d.ts index 7185c8c69b..9d3026e655 100644 --- a/lib/definitions/debug.d.ts +++ b/lib/definitions/debug.d.ts @@ -1,6 +1,6 @@ interface IDebugService { - debug(shouldBreak?: boolean): Promise; - debugStart(): Promise; + debug(projectData: IProjectData): Promise; + debugStart(projectData: IProjectData): 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 0282d043e3..bed97e0282 100644 --- a/lib/definitions/emulator-platform-service.d.ts +++ b/lib/definitions/emulator-platform-service.d.ts @@ -12,5 +12,5 @@ interface IEmulatorPlatformService { getEmulatorInfo(platform: string, nameOfId: string): Promise; getiOSEmulators(): Promise; getAndroidEmulators(): Promise; - startEmulator(info: IEmulatorInfo): Promise; + startEmulator(info: IEmulatorInfo, projectData: IProjectData): Promise; } \ No newline at end of file diff --git a/lib/definitions/platform.d.ts b/lib/definitions/platform.d.ts index f9af11ac6c..ccab0182a6 100644 --- a/lib/definitions/platform.d.ts +++ b/lib/definitions/platform.d.ts @@ -1,32 +1,36 @@ -interface IPlatformService { - addPlatforms(platforms: string[]): Promise; +interface IPlatformService extends NodeJS.EventEmitter { + addPlatforms(platforms: string[], platformTemplate: string, projectData: IProjectData): Promise; /** * Gets list of all installed platforms (the ones for which /platforms/ exists). + * @param {IProjectData} projectData DTO with information about the project. * @returns {string[]} List of currently installed platforms. */ - getInstalledPlatforms(): string[]; + getInstalledPlatforms(projectData: IProjectData): string[]; /** * Gets a list of all platforms that can be used on current OS, but are not installed at the moment. + * @param {IProjectData} projectData DTO with information about the project. * @returns {string[]} List of all available platforms. */ - getAvailablePlatforms(): string[]; + getAvailablePlatforms(projectData: IProjectData): string[]; /** * Returns a list of all currently prepared platforms. + * @param {IProjectData} projectData DTO with information about the project. * @returns {string[]} List of all prepared platforms. */ - getPreparedPlatforms(): string[]; + getPreparedPlatforms(projectData: IProjectData): string[]; /** * Remove platforms from specified project (`/platforms/` dir). * @param {string[]} platforms Platforms to be removed. - * @returns {void} + * @param {IProjectData} projectData DTO with information about the project. + * @returns {Promise} */ - removePlatforms(platforms: string[]): Promise; + removePlatforms(platforms: string[], projectData: IProjectData): Promise; - updatePlatforms(platforms: string[]): Promise; + updatePlatforms(platforms: string[], platformTemplate: string, projectData: IProjectData): Promise; /** * Ensures that the specified platform and its dependencies are installed. @@ -34,19 +38,24 @@ interface IPlatformService { * When finishes, prepare saves the .nsprepareinfo file in platform folder. * This file contains information about current project configuration and allows skipping unnecessary build, deploy and livesync steps. * @param {string} platform The platform to be prepared. + * @param {IAppFilesUpdaterOptions} appFilesUpdaterOptions Options needed to instantiate AppFilesUpdater class. + * @param {string} platformTemplate The name of the platform template. + * @param {IProjectData} projectData DTO with information about the project. + * @param {any} provision UUID of the provisioning profile used in iOS project preparation. * @returns {boolean} true indicates that the platform was prepared. */ - preparePlatform(platform: string, changesInfo?: IProjectChangesInfo): Promise; + preparePlatform(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, platformTemplate: string, projectData: IProjectData, provision: any): Promise; /** * Determines whether a build is necessary. A build is necessary when one of the following is true: * - there is no previous build. * - the .nsbuildinfo file in product folder points to an old prepare. * @param {string} platform The platform to build. + * @param {IProjectData} projectData DTO with information about the project. * @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): Promise; + shouldBuild(platform: string, projectData: IProjectData, buildConfig?: IBuildConfig): Promise; /** * Builds the native project for the specified platform for device or emulator. @@ -54,61 +63,74 @@ interface IPlatformService { * This file points to the prepare that was used to build the project and allows skipping unnecessary builds and deploys. * @param {string} platform The platform to build. * @param {IBuildConfig} buildConfig Indicates whether the build is for device or emulator. + * @param {IProjectData} projectData DTO with information about the project. * @returns {void} */ - buildPlatform(platform: string, buildConfig?: IBuildConfig): Promise; + buildPlatform(platform: string, buildConfig: IBuildConfig, projectData: IProjectData): 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. + * @param {IProjectData} projectData DTO with information about the project. * @returns {Promise} true indicates that the application should be installed. */ - shouldInstall(device: Mobile.IDevice): Promise; + shouldInstall(device: Mobile.IDevice, projectData: IProjectData): Promise; /** * Installs the application on specified device. * When finishes, saves .nsbuildinfo in application root folder to indicate the prepare that was used to build the app. * * .nsbuildinfo is not persisted when building for release. * @param {Mobile.IDevice} device The device where the application should be installed. + * @param {IRelease} options Whether the application was built in release configuration. + * @param {IProjectData} projectData DTO with information about the project. * @returns {void} */ - installApplication(device: Mobile.IDevice): Promise; + installApplication(device: Mobile.IDevice, options: IRelease, projectData: IProjectData): 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): Promise; + validateOptions(provision: any, projectData: IProjectData, platform?: string): Promise; /** * Executes prepare, build and installOnPlatform when necessary to ensure that the latest version of the app is installed on specified platform. * - When --clean option is specified it builds the app on every change. If not, build is executed only when there are native changes. * @param {string} platform The platform to deploy. - * @param {boolean} forceInstall When true, installs the application unconditionally. + * @param {IAppFilesUpdaterOptions} appFilesUpdaterOptions Options needed to instantiate AppFilesUpdater class. + * @param {IDeployPlatformOptions} deployOptions Various options that can manage the deploy operation. + * @param {IProjectData} projectData DTO with information about the project. + * @param {any} provision UUID of the provisioning profile used in iOS project preparation. * @returns {void} */ - deployPlatform(platform: string, forceInstall?: boolean): Promise; + deployPlatform(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, deployOptions: IDeployPlatformOptions, projectData: IProjectData, provision: any): 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. + * @param {IRunPlatformOptions} runOptions Various options that help manage the run operation. + * @param {IProjectData} projectData DTO with information about the project. * @returns {void} */ - runPlatform(platform: string): Promise; + runPlatform(platform: string, runOptions: IRunPlatformOptions, projectData: IProjectData): 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. + * @param {IAppFilesUpdaterOptions} appFilesUpdaterOptions Options needed to instantiate AppFilesUpdater class. + * @param {IEmulatePlatformOptions} emulateOptions Various options that can manage the emulate operation. + * @param {IProjectData} projectData DTO with information about the project. + * @param {any} provision UUID of the provisioning profile used in iOS project preparation. * @returns {void} */ - emulatePlatform(platform: string): Promise; + emulatePlatform(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, emulateOptions: IEmulatePlatformOptions, projectData: IProjectData, provision: any): Promise; - cleanDestinationApp(platform: string): Promise; - validatePlatformInstalled(platform: string): void; - validatePlatform(platform: string): void; + cleanDestinationApp(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, platformTemplate: string, projectData: IProjectData): Promise; + validatePlatformInstalled(platform: string, projectData: IProjectData): void; + validatePlatform(platform: string, projectData: IProjectData): void; /** * Returns information about the latest built application for device in the current project. @@ -129,27 +151,30 @@ interface IPlatformService { * @param {string} platform Mobile platform - Android, iOS. * @param {string} targetPath Destination where the build artifact should be copied. * @param {{isForDevice: boolean}} settings Defines if the searched artifact should be for simulator. + * @param {IProjectData} projectData DTO with information about the project. * @returns {void} */ - copyLastOutput(platform: string, targetPath: string, settings: {isForDevice: boolean}): void; + copyLastOutput(platform: string, targetPath: string, settings: {isForDevice: boolean}, projectData: IProjectData): void; - lastOutputPath(platform: string, settings: { isForDevice: boolean }): string; + lastOutputPath(platform: string, settings: { isForDevice: boolean }, projectData: IProjectData): string; /** * Reads contents of a file on device. * @param {Mobile.IDevice} device The device to read from. * @param {string} deviceFilePath The file path. + * @param {IProjectData} projectData DTO with information about the project. * @returns {string} The contents of the file or null when there is no such file. */ - readFile(device: Mobile.IDevice, deviceFilePath: string): Promise; + readFile(device: Mobile.IDevice, deviceFilePath: string, projectData: IProjectData): Promise; /** * Sends information to analytics for current project type. * The information is sent once per process for each project. * In long living process, where the project may change, each of the projects will be tracked after it's being opened. + * @param {IProjectData} projectData DTO with information about the project. * @returns {Promise} */ - trackProjectType(): Promise; + trackProjectType(projectData: IProjectData): Promise; } interface IPlatformData { @@ -176,11 +201,11 @@ interface IPlatformData { interface IPlatformsData { availablePlatforms: any; platformsNames: string[]; - getPlatformData(platform: string): IPlatformData; + getPlatformData(platform: string, projectData: IProjectData): IPlatformData; } interface INodeModulesBuilder { - prepareNodeModules(absoluteOutputPath: string, platform: string, lastModifiedTime: Date): Promise; + prepareNodeModules(absoluteOutputPath: string, platform: string, lastModifiedTime: Date, projectData: IProjectData): Promise; cleanNodeModules(absoluteOutputPath: string, platform: string): void; } diff --git a/lib/definitions/plugins.d.ts b/lib/definitions/plugins.d.ts index 7218812dd6..871548fc1a 100644 --- a/lib/definitions/plugins.d.ts +++ b/lib/definitions/plugins.d.ts @@ -1,16 +1,17 @@ interface IPluginsService { - add(plugin: string): Promise; // adds plugin by name, github url, local path and et. - remove(pluginName: string): Promise; // removes plugin only by name + add(plugin: string, projectData: IProjectData): Promise; // adds plugin by name, github url, local path and et. + remove(pluginName: string, projectData: IProjectData): Promise; // removes plugin only by name getAvailable(filter: string[]): Promise>; // gets all available plugins - prepare(pluginData: IDependencyData, platform: string): Promise; - getAllInstalledPlugins(): Promise; - ensureAllDependenciesAreInstalled(): Promise; + prepare(pluginData: IDependencyData, platform: string, projectData: IProjectData): Promise; + getAllInstalledPlugins(projectData: IProjectData): Promise; + ensureAllDependenciesAreInstalled(projectData: IProjectData): Promise; /** * Returns all dependencies and devDependencies from pacakge.json file. + * @param {string} projectData Root directory of the project. * @returns {IPackageJsonDepedenciesResult} */ - getDependenciesFromPackageJson(): IPackageJsonDepedenciesResult; + getDependenciesFromPackageJson(projectDir: string): IPackageJsonDepedenciesResult; } interface IPackageJsonDepedenciesResult { @@ -45,36 +46,40 @@ interface IPluginVariablesService { /** * Saves plugin variables in project package.json file. * @param {IPluginData} pluginData for the plugin. + * @param {IProjectData} projectData DTO with information about the project. * @return {Promise} */ - savePluginVariablesInProjectFile(pluginData: IPluginData): Promise; + savePluginVariablesInProjectFile(pluginData: IPluginData, projectData: IProjectData): Promise; /** * Removes plugin variables from project package.json file. * @param {string} pluginName Name of the plugin. + * @param {IProjectData} projectData DTO with information about the project. * @return {void} */ - removePluginVariablesFromProjectFile(pluginName: string): void; + removePluginVariablesFromProjectFile(pluginName: string, projectData: IProjectData): void; /** * Replaces all plugin variables with their corresponding values. * @param {IPluginData} pluginData for the plugin. * @param {pluginConfigurationFilePath} pluginConfigurationFilePath for the plugin. + * @param {IProjectData} projectData DTO with information about the project. * @return {Promise} */ - interpolatePluginVariables(pluginData: IPluginData, pluginConfigurationFilePath: string): Promise; + interpolatePluginVariables(pluginData: IPluginData, pluginConfigurationFilePath: string, projectData: IProjectData): Promise; /** * Replaces {nativescript.id} expression with the application identifier from package.json. * @param {pluginConfigurationFilePath} pluginConfigurationFilePath for the plugin. + * @param {IProjectData} projectData DTO with information about the project. * @return {void} */ - interpolateAppIdentifier(pluginConfigurationFilePath: string): void; + interpolateAppIdentifier(pluginConfigurationFilePath: string, projectData: IProjectData): void; /** * Replaces both plugin variables and appIdentifier */ - interpolate(pluginData: IPluginData, pluginConfigurationFilePath: string): Promise; + interpolate(pluginData: IPluginData, pluginConfigurationFilePath: string, projectData: IProjectData): Promise; /** * Returns the diff --git a/lib/definitions/project-changes.d.ts b/lib/definitions/project-changes.d.ts index a99b196a49..b394fb92e8 100644 --- a/lib/definitions/project-changes.d.ts +++ b/lib/definitions/project-changes.d.ts @@ -20,9 +20,9 @@ interface IProjectChangesInfo { } interface IProjectChangesService { - checkForChanges(platform: string): IProjectChangesInfo; - getPrepareInfo(platform: string): IPrepareInfo; - savePrepareInfo(platform: string): void; - getPrepareInfoFilePath(platform: string): string; + checkForChanges(platform: string, projectData: IProjectData): IProjectChangesInfo; + getPrepareInfo(platform: string, projectData: IProjectData): IPrepareInfo; + savePrepareInfo(platform: string, projectData: IProjectData): void; + getPrepareInfoFilePath(platform: string, projectData: IProjectData): string; currentChanges: IProjectChangesInfo; } \ No newline at end of file diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index 2fb091b980..902834edcf 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -57,6 +57,12 @@ interface IProjectData { appDirectoryPath: string; appResourcesDirectoryPath: string; projectType: string; + /** + * Initializes project data with the given project directory. If none supplied defaults to --path option or cwd. + * @param {string} projectDir Project root directory. + * @returns {void} + */ + initializeProjectData(projectDir? :string): void; } interface IProjectDataService { @@ -113,15 +119,21 @@ interface IPlatformProjectServiceBase { getPluginPlatformsFolderPath(pluginData: IPluginData, platform: string): string; } -interface IBuildConfig { - buildForDevice?: boolean; +interface IBuildForDevice { + buildForDevice: boolean; +} + +interface IBuildConfig extends IAndroidBuildOptionsSettings, IBuildForDevice { + projectDir: string; + clean?: boolean; architectures?: string[]; + buildOutputStdio?: string; } /** * Describes iOS-specific build configuration properties */ -interface IiOSBuildConfig extends IBuildConfig { +interface IiOSBuildConfig extends IBuildConfig, IRelease, IDeviceIdentifier, IProvision, ITeamIdentifier { /** * Identifier of the mobile provision which will be used for the build. If not set a provision will be selected automatically if possible. */ @@ -136,93 +148,108 @@ interface IiOSBuildConfig extends IBuildConfig { teamIdentifier?: string; } -interface IPlatformProjectService { - platformData: IPlatformData; - validate(): Promise; - createProject(frameworkDir: string, frameworkVersion: string, pathToTemplate?: string): Promise; - interpolateData(): Promise; - interpolateConfigurationFile(configurationFilePath?: string): Promise; +interface IPlatformProjectService extends NodeJS.EventEmitter { + getPlatformData(projectData: IProjectData): IPlatformData; + validate(projectData: IProjectData): Promise; + createProject(frameworkDir: string, frameworkVersion: string, projectData: IProjectData, pathToTemplate?: string): Promise; + interpolateData(projectData: IProjectData): Promise; + interpolateConfigurationFile(projectData: IProjectData, sdk?: string): Promise; /** * Executes additional actions after native project is created. * @param {string} projectRoot Path to the real NativeScript project. + * @param {IProjectData} projectData DTO with information about the project. * @returns {void} */ - afterCreateProject(projectRoot: string): void; + afterCreateProject(projectRoot: string, projectData: IProjectData): void; /** * Gets first chance to validate the options provided as command line arguments. + * @param {string} projectId Project identifier - for example org.nativescript.test. + * @param {any} provision UUID of the provisioning profile used in iOS option validation. + * @returns {void} */ - validateOptions(): Promise; + validateOptions(projectId?: string, provision?: any): Promise; - buildProject(projectRoot: string, buildConfig?: IBuildConfig): Promise; + buildProject(projectRoot: string, projectData: IProjectData, buildConfig: IBuildConfig): Promise; /** * Prepares images in Native project (for iOS). + * @param {IProjectData} projectData DTO with information about the project. + * @param {any} provision UUID of the provisioning profile used in iOS project preparation. * @returns {void} */ - prepareProject(): Promise; + prepareProject(projectData: IProjectData, provision: any): Promise; /** * Prepares App_Resources in the native project by clearing data from other platform and applying platform specific rules. * @param {string} appResourcesDirectoryPath The place in the native project where the App_Resources are copied first. + * @param {IProjectData} projectData DTO with information about the project. * @returns {void} */ - prepareAppResources(appResourcesDirectoryPath: string): void; + prepareAppResources(appResourcesDirectoryPath: string, projectData: IProjectData): void; /** * Defines if current platform is prepared (i.e. if /platforms/ dir exists). * @param {string} projectRoot The project directory (path where root's package.json is located). + * @param {IProjectData} projectData DTO with information about the project. * @returns {boolean} True in case platform is prepare (i.e. if /platforms/ dir exists), false otherwise. */ - isPlatformPrepared(projectRoot: string): boolean; + isPlatformPrepared(projectRoot: string, projectData: IProjectData): boolean; /** * Checks if current platform can be updated to a newer versions. * @param {string} newInstalledModuleDir Path to the native project. + * @param {IProjectData} projectData DTO with information about the project. * @return {boolean} True if platform can be updated. false otherwise. */ - canUpdatePlatform(newInstalledModuleDir: string): boolean; + canUpdatePlatform(newInstalledModuleDir: string, projectData: IProjectData): boolean; 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. + * @param {IProjectData} projectData DTO with information about the project. * @returns {void} */ - removePluginNativeCode(pluginData: IPluginData): Promise; + removePluginNativeCode(pluginData: IPluginData, projectData: IProjectData): Promise; - afterPrepareAllPlugins(): Promise; - beforePrepareAllPlugins(dependencies?: IDictionary): Promise; + afterPrepareAllPlugins(projectData: IProjectData): Promise; + beforePrepareAllPlugins(projectData: IProjectData, dependencies?: IDictionary): Promise; /** * Gets the path wheren App_Resources should be copied. * @returns {string} Path to native project, where App_Resources should be copied. */ - getAppResourcesDestinationDirectoryPath(): string; + getAppResourcesDestinationDirectoryPath(projectData: IProjectData): string; - deploy(deviceIdentifier: string): Promise; - processConfigurationFilesFromAppResources(): Promise; + deploy(deviceIdentifier: string, projectData: IProjectData): Promise; + processConfigurationFilesFromAppResources(release: boolean, projectData: IProjectData): Promise; /** * Ensures there is configuration file (AndroidManifest.xml, Info.plist) in app/App_Resources. + * @param {IProjectData} projectData DTO with information about the project. * @returns {void} */ - ensureConfigurationFileInAppResources(): void; + ensureConfigurationFileInAppResources(projectData: IProjectData): void; /** * Stops all running processes that might hold a lock on the filesystem. * Android: Gradle daemon processes are terminated. + * @param {string} projectRoot The root directory of the native project. * @returns {void} */ - stopServices(): Promise; + stopServices(projectRoot: string): Promise; /** * Removes build artifacts specific to the platform + * @param {string} projectRoot The root directory of the native project. + * @param {string[]} options Options that can be passed to clean command. + * @param {IProjectData} projectData DTO with information about the project. * @returns {void} */ - cleanProject(projectRoot: string, options: string[]): Promise + cleanProject(projectRoot: string, options: string[], projectData: IProjectData): Promise } interface IAndroidProjectPropertiesManager { @@ -232,8 +259,8 @@ interface IAndroidProjectPropertiesManager { } interface ITestExecutionService { - startTestRunner(platform: string): Promise; - startKarmaServer(platform: string): Promise; + startTestRunner(platform: string, projectData: IProjectData): Promise; + startKarmaServer(platform: string, projectData: IProjectData): Promise; } /** diff --git a/lib/device-sockets/ios/notification.ts b/lib/device-sockets/ios/notification.ts index 1e3eb365fe..7c44aa1858 100644 --- a/lib/device-sockets/ios/notification.ts +++ b/lib/device-sockets/ios/notification.ts @@ -7,38 +7,36 @@ export class IOSNotification implements IiOSNotification { private static ALREADY_CONNECTED_NOTIFICATION_NAME = "AlreadyConnected"; private static ATTACH_AVAILABLE_NOTIFICATION_NAME = "AttachAvailable"; - constructor(private $projectData: IProjectData) { } - - public get waitForDebug() { - return this.formatNotification(IOSNotification.WAIT_FOR_DEBUG_NOTIFICATION_NAME); + public getWaitForDebug(projectId: string) { + return this.formatNotification(IOSNotification.WAIT_FOR_DEBUG_NOTIFICATION_NAME, projectId); } - public get attachRequest(): string { - return this.formatNotification(IOSNotification.ATTACH_REQUEST_NOTIFICATION_NAME); + public getAttachRequest(projectId: string): string { + return this.formatNotification(IOSNotification.ATTACH_REQUEST_NOTIFICATION_NAME, projectId); } - public get appLaunching(): string { - return this.formatNotification(IOSNotification.APP_LAUNCHING_NOTIFICATION_NAME); + public getAppLaunching(projectId: string): string { + return this.formatNotification(IOSNotification.APP_LAUNCHING_NOTIFICATION_NAME, projectId); } - public get readyForAttach(): string { - return this.formatNotification(IOSNotification.READY_FOR_ATTACH_NOTIFICATION_NAME); + public getReadyForAttach(projectId: string): string { + return this.formatNotification(IOSNotification.READY_FOR_ATTACH_NOTIFICATION_NAME, projectId); } - public get attachAvailabilityQuery() { - return this.formatNotification(IOSNotification.ATTACH_AVAILABILITY_QUERY_NOTIFICATION_NAME); + public getAttachAvailabilityQuery(projectId: string) { + return this.formatNotification(IOSNotification.ATTACH_AVAILABILITY_QUERY_NOTIFICATION_NAME, projectId); } - public get alreadyConnected() { - return this.formatNotification(IOSNotification.ALREADY_CONNECTED_NOTIFICATION_NAME); + public getAlreadyConnected(projectId: string) { + return this.formatNotification(IOSNotification.ALREADY_CONNECTED_NOTIFICATION_NAME, projectId); } - public get attachAvailable() { - return this.formatNotification(IOSNotification.ATTACH_AVAILABLE_NOTIFICATION_NAME); + public getAttachAvailable(projectId: string) { + return this.formatNotification(IOSNotification.ATTACH_AVAILABLE_NOTIFICATION_NAME, projectId); } - private formatNotification(notification: string) { - return `${this.$projectData.projectId}:NativeScript.Debug.${notification}`; + private formatNotification(notification: string, projectId: string) { + return `${projectId}:NativeScript.Debug.${notification}`; } } $injector.register("iOSNotification", IOSNotification); diff --git a/lib/device-sockets/ios/socket-request-executor.ts b/lib/device-sockets/ios/socket-request-executor.ts index ee9e3601cf..2b5f2d3f78 100644 --- a/lib/device-sockets/ios/socket-request-executor.ts +++ b/lib/device-sockets/ios/socket-request-executor.ts @@ -5,65 +5,64 @@ export class IOSSocketRequestExecutor implements IiOSSocketRequestExecutor { private $injector: IInjector, private $iOSNotification: IiOSNotification, private $iOSNotificationService: IiOSNotificationService, - private $logger: ILogger, - private $projectData: IProjectData) { } + private $logger: ILogger) { } - public async executeAttachRequest(device: Mobile.IiOSDevice, timeout: number): Promise { + public async executeAttachRequest(device: Mobile.IiOSDevice, timeout: number, projectId: string): Promise { let npc = new iOSProxyServices.NotificationProxyClient(device, this.$injector); - let data = [this.$iOSNotification.alreadyConnected, this.$iOSNotification.readyForAttach, this.$iOSNotification.attachAvailable] + let data = [this.$iOSNotification.getAlreadyConnected(projectId), this.$iOSNotification.getReadyForAttach(projectId), this.$iOSNotification.getAttachAvailable(projectId)] .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.getAttachAvailabilityQuery(projectId)); 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.`); + this.$errors.failWithoutHelp(`The application ${projectId} does not appear to be running on ${device.deviceInfo.displayName} or is not built with debugging enabled.`); } switch (receivedNotification) { - case this.$iOSNotification.alreadyConnected: + case this.$iOSNotification.getAlreadyConnected(projectId): this.$errors.failWithoutHelp("A client is already connected."); break; - case this.$iOSNotification.attachAvailable: - await this.executeAttachAvailable(npc, timeout); + case this.$iOSNotification.getAttachAvailable(projectId): + await this.executeAttachAvailable(npc, timeout, projectId); break; - case this.$iOSNotification.readyForAttach: + case this.$iOSNotification.getReadyForAttach(projectId): break; } } - public async executeLaunchRequest(device: Mobile.IiOSDevice, timeout: number, readyForAttachTimeout: number, shouldBreak?: boolean): Promise { + public async executeLaunchRequest(device: Mobile.IiOSDevice, timeout: number, readyForAttachTimeout: number, projectId: string, shouldBreak?: boolean): Promise { let npc = new iOSProxyServices.NotificationProxyClient(device, this.$injector); try { - await this.$iOSNotificationService.awaitNotification(npc, this.$iOSNotification.appLaunching, timeout); + await this.$iOSNotificationService.awaitNotification(npc, this.$iOSNotification.getAppLaunching(projectId), timeout); process.nextTick(() => { if (shouldBreak) { - npc.postNotificationAndAttachForData(this.$iOSNotification.waitForDebug); + npc.postNotificationAndAttachForData(this.$iOSNotification.getWaitForDebug(projectId)); } - npc.postNotificationAndAttachForData(this.$iOSNotification.attachRequest); + npc.postNotificationAndAttachForData(this.$iOSNotification.getAttachRequest(projectId)); }); - await this.$iOSNotificationService.awaitNotification(npc, this.$iOSNotification.readyForAttach, readyForAttachTimeout); + await this.$iOSNotificationService.awaitNotification(npc, this.$iOSNotification.getReadyForAttach(projectId), readyForAttachTimeout); } catch (e) { this.$logger.trace(`Timeout error: ${e}`); this.$errors.failWithoutHelp("Timeout waiting for response from NativeScript runtime."); } } - private async executeAttachAvailable(npc: Mobile.INotificationProxyClient, timeout: number): Promise { - process.nextTick(() => npc.postNotificationAndAttachForData(this.$iOSNotification.attachRequest)); + private async executeAttachAvailable(npc: Mobile.INotificationProxyClient, timeout: number, projectId: string): Promise { + process.nextTick(() => npc.postNotificationAndAttachForData(this.$iOSNotification.getAttachRequest(projectId))); try { - await this.$iOSNotificationService.awaitNotification(npc, this.$iOSNotification.readyForAttach, timeout); + await this.$iOSNotificationService.awaitNotification(npc, this.$iOSNotification.getReadyForAttach(projectId), timeout); } catch (e) { - this.$errors.failWithoutHelp(`The application ${this.$projectData.projectId} timed out when performing the socket handshake.`); + this.$errors.failWithoutHelp(`The application ${projectId} timed out when performing the socket handshake.`); } } } diff --git a/lib/nativescript-cli-lib-bootstrap.ts b/lib/nativescript-cli-lib-bootstrap.ts index bfef2add8c..ed4d033ded 100644 --- a/lib/nativescript-cli-lib-bootstrap.ts +++ b/lib/nativescript-cli-lib-bootstrap.ts @@ -8,3 +8,4 @@ $injector.overrideAlreadyRequiredModule = true; $injector.requirePublic("companionAppsService", "./common/appbuilder/services/livesync/companion-apps-service"); $injector.requirePublicClass("deviceEmitter", "./common/appbuilder/device-emitter"); $injector.requirePublicClass("deviceLogProvider", "./common/appbuilder/device-log-provider"); +$injector.requirePublicClass("localBuildService", "./services/local-build-service"); diff --git a/lib/options.ts b/lib/options.ts index b999dff2bc..e3d79b569c 100644 --- a/lib/options.ts +++ b/lib/options.ts @@ -28,7 +28,6 @@ export class Options extends commonOptionsLibPath.OptionsBase { compileSdk: { type: OptionType.Number }, port: { type: OptionType.Number }, copyTo: { type: OptionType.String }, - baseConfig: { type: OptionType.String }, platformTemplate: { type: OptionType.String }, ng: { type: OptionType.Boolean }, tsc: { type: OptionType.Boolean }, @@ -60,6 +59,9 @@ export class Options extends commonOptionsLibPath.OptionsBase { // ignore the error - it is too early to use $logger here } } + + let that = (this); + that.watch = !that.justlaunch; } } $injector.register("options", Options); diff --git a/lib/platform-command-param.ts b/lib/platform-command-param.ts index 7fff623f30..b6ec67eb6b 100644 --- a/lib/platform-command-param.ts +++ b/lib/platform-command-param.ts @@ -1,8 +1,10 @@ export class PlatformCommandParameter implements ICommandParameter { - constructor(private $platformService: IPlatformService) { } + constructor(private $platformService: IPlatformService, + private $projectData: IProjectData) { } mandatory = true; async validate(value: string): Promise { - this.$platformService.validatePlatform(value); + this.$projectData.initializeProjectData(); + this.$platformService.validatePlatform(value, this.$projectData); return true; } } diff --git a/lib/platforms-data.ts b/lib/platforms-data.ts index 44b234eea7..bd82fa49ae 100644 --- a/lib/platforms-data.ts +++ b/lib/platforms-data.ts @@ -5,8 +5,8 @@ export class PlatformsData implements IPlatformsData { $iOSProjectService: IPlatformProjectService) { this.platformsData = { - ios: $iOSProjectService.platformData, - android: $androidProjectService.platformData + ios: $iOSProjectService, + android: $androidProjectService }; } @@ -14,8 +14,8 @@ export class PlatformsData implements IPlatformsData { return Object.keys(this.platformsData); } - public getPlatformData(platform: string): IPlatformData { - return this.platformsData[platform.toLowerCase()]; + public getPlatformData(platform: string, projectData: IProjectData): IPlatformData { + return this.platformsData[platform.toLowerCase()].getPlatformData(projectData); } public get availablePlatforms(): any { diff --git a/lib/project-data.ts b/lib/project-data.ts index e56c258c64..dc4ee0b10e 100644 --- a/lib/project-data.ts +++ b/lib/project-data.ts @@ -45,12 +45,10 @@ export class ProjectData implements IProjectData { private $logger: ILogger, private $projectHelper: IProjectHelper, private $staticConfig: IStaticConfig, - private $options: IOptions) { - this.initializeProjectData(); - } + private $options: IOptions) { } - private initializeProjectData(): void { - let projectDir = this.$projectHelper.projectDir; + public initializeProjectData(projectDir? :string): void { + projectDir = projectDir || this.$projectHelper.projectDir; // If no project found, projectDir should be null if (projectDir) { this.initializeProjectDataCore(projectDir); diff --git a/lib/providers/livesync-provider.ts b/lib/providers/livesync-provider.ts index 4c1d1f4142..ede556a0b6 100644 --- a/lib/providers/livesync-provider.ts +++ b/lib/providers/livesync-provider.ts @@ -32,9 +32,18 @@ export class LiveSyncProvider implements ILiveSyncProvider { }; } - 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); + public async buildForDevice(device: Mobile.IDevice, projectData: IProjectData): Promise { + let buildConfig: IiOSBuildConfig = { + buildForDevice: !device.isEmulator, + projectDir: this.$options.path, + release: this.$options.release, + teamId: this.$options.teamId, + device: this.$options.device, + provision: this.$options.provision, + }; + + await this.$platformService.buildPlatform(device.deviceInfo.platform, buildConfig, projectData); + let platformData = this.$platformsData.getPlatformData(device.deviceInfo.platform, projectData); if (device.isEmulator) { return this.$platformService.getLatestApplicationPackageForEmulator(platformData).packageName; } @@ -42,12 +51,13 @@ export class LiveSyncProvider implements ILiveSyncProvider { return this.$platformService.getLatestApplicationPackageForDevice(platformData).packageName; } - public async preparePlatformForSync(platform: string): Promise { - await this.$platformService.preparePlatform(platform); + public async preparePlatformForSync(platform: string, provision: any, projectData: IProjectData): Promise { + const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; + await this.$platformService.preparePlatform(platform, appFilesUpdaterOptions, this.$options.platformTemplate, projectData, provision); } - public canExecuteFastSync(filePath: string, platform: string): boolean { - let platformData = this.$platformsData.getPlatformData(platform); + public canExecuteFastSync(filePath: string, projectData: IProjectData, platform: string): boolean { + let platformData = this.$platformsData.getPlatformData(platform, projectData); let fastSyncFileExtensions = LiveSyncProvider.FAST_SYNC_FILE_EXTENSIONS.concat(platformData.fastLivesyncFileExtensions); return _.includes(fastSyncFileExtensions, path.extname(filePath)); } diff --git a/lib/providers/project-files-provider.ts b/lib/providers/project-files-provider.ts index 410295b761..afe422d537 100644 --- a/lib/providers/project-files-provider.ts +++ b/lib/providers/project-files-provider.ts @@ -5,7 +5,6 @@ import { ProjectFilesProviderBase } from "../common/services/project-files-provi export class ProjectFilesProvider extends ProjectFilesProviderBase { constructor(private $platformsData: IPlatformsData, - private $projectData: IProjectData, $mobileHelper: Mobile.IMobileHelper, $options:IOptions) { super($mobileHelper, $options); @@ -13,15 +12,15 @@ export class ProjectFilesProvider extends ProjectFilesProviderBase { private static INTERNAL_NONPROJECT_FILES = [ "**/*.ts" ]; - public mapFilePath(filePath: string, platform: string): string { - let platformData = this.$platformsData.getPlatformData(platform.toLowerCase()); + public mapFilePath(filePath: string, platform: string, projectData: IProjectData): string { + let platformData = this.$platformsData.getPlatformData(platform.toLowerCase(), projectData); let parsedFilePath = this.getPreparedFilePath(filePath); let mappedFilePath = ""; if (parsedFilePath.indexOf(constants.NODE_MODULES_FOLDER_NAME) > -1) { - let relativePath = path.relative(path.join(this.$projectData.projectDir, constants.NODE_MODULES_FOLDER_NAME), parsedFilePath); + let relativePath = path.relative(path.join(projectData.projectDir, constants.NODE_MODULES_FOLDER_NAME), parsedFilePath); mappedFilePath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, constants.TNS_MODULES_FOLDER_NAME, relativePath); } else { - mappedFilePath = path.join(platformData.appDestinationDirectoryPath, path.relative(this.$projectData.projectDir, parsedFilePath)); + mappedFilePath = path.join(platformData.appDestinationDirectoryPath, path.relative(projectData.projectDir, parsedFilePath)); } let appResourcesDirectoryPath = path.join(constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); @@ -31,9 +30,9 @@ export class ProjectFilesProvider extends ProjectFilesProviderBase { } if (parsedFilePath.indexOf(platformSpecificAppResourcesDirectoryPath) > -1) { - let appResourcesRelativePath = path.relative(path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, + let appResourcesRelativePath = path.relative(path.join(projectData.projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, platformData.normalizedPlatformName), parsedFilePath); - mappedFilePath = path.join(platformData.platformProjectService.getAppResourcesDestinationDirectoryPath(), appResourcesRelativePath); + mappedFilePath = path.join(platformData.platformProjectService.getAppResourcesDestinationDirectoryPath(projectData), appResourcesRelativePath); } return mappedFilePath; diff --git a/lib/services/android-debug-service.ts b/lib/services/android-debug-service.ts index badca8620f..cfc48dbf7d 100644 --- a/lib/services/android-debug-service.ts +++ b/lib/services/android-debug-service.ts @@ -10,7 +10,6 @@ class AndroidDebugService implements IDebugService { constructor(private $devicesService: Mobile.IDevicesService, private $platformService: IPlatformService, private $platformsData: IPlatformsData, - private $projectData: IProjectData, private $logger: ILogger, private $options: IOptions, private $errors: IErrors, @@ -29,18 +28,18 @@ class AndroidDebugService implements IDebugService { this._device = newDevice; } - public async debug(): Promise { + public async debug(projectData: IProjectData): Promise { return this.$options.emulator - ? this.debugOnEmulator() - : this.debugOnDevice(); + ? this.debugOnEmulator(projectData) + : this.debugOnDevice(projectData); } - private async debugOnEmulator(): Promise { + private async debugOnEmulator(projectData: IProjectData): 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(); + await this.debugOnDevice(projectData); } private isPortAvailable(candidatePort: number): Promise { @@ -109,7 +108,7 @@ class AndroidDebugService implements IDebugService { return this.device.adb.executeCommand(["forward", `tcp:${local}`, `localabstract:${remote}`]); } - private async debugOnDevice(): Promise { + private async debugOnDevice(projectData: IProjectData): Promise { let packageFile = ""; if (!this.$options.start && !this.$options.emulator) { @@ -117,14 +116,14 @@ class AndroidDebugService implements IDebugService { this.$options.forDevice = !!cachedDeviceOption; - let platformData = this.$platformsData.getPlatformData(this.platform); + let platformData = this.$platformsData.getPlatformData(this.platform, projectData); packageFile = this.$platformService.getLatestApplicationPackageForDevice(platformData).packageName; this.$logger.out("Using ", packageFile); } await this.$devicesService.initialize({ platform: this.platform, deviceId: this.$options.device }); - let action = (device: Mobile.IAndroidDevice): Promise => this.debugCore(device, packageFile, this.$projectData.projectId); + let action = (device: Mobile.IAndroidDevice): Promise => this.debugCore(device, packageFile, projectData.projectId); await this.$devicesService.execute(action); } @@ -168,14 +167,14 @@ class AndroidDebugService implements IDebugService { await this.device.applicationManager.uninstallApplication(packageName); await this.device.applicationManager.installApplication(packageFile); } - await this.debugStartCore(); + await this.debugStartCore(packageName); } - public async debugStart(): Promise { + public async debugStart(projectData: IProjectData): Promise { await this.$devicesService.initialize({ platform: this.platform, deviceId: this.$options.device }); let action = (device: Mobile.IAndroidDevice): Promise => { this.device = device; - return this.debugStartCore(); + return this.debugStartCore(projectData.projectId); }; await this.$devicesService.execute(action); @@ -186,9 +185,7 @@ class AndroidDebugService implements IDebugService { return; } - private async debugStartCore(): Promise { - let packageName = this.$projectData.projectId; - + private async debugStartCore(packageName: string): Promise { // 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. diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index fae35302e8..3095265267 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -27,8 +27,6 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject $fs: IFileSystem, private $hostInfo: IHostInfo, private $logger: ILogger, - private $options: IOptions, - $projectData: IProjectData, $projectDataService: IProjectDataService, private $sysInfo: ISysInfo, private $injector: IInjector, @@ -36,15 +34,21 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, private $config: IConfiguration, private $npm: INodePackageManager) { - super($fs, $projectData, $projectDataService); + super($fs, $projectDataService); this._androidProjectPropertiesManagers = Object.create(null); } + private _platformsDirCache: string = null; private _platformData: IPlatformData = null; - public get platformData(): IPlatformData { - if (!this._platformData) { - let projectRoot = path.join(this.$projectData.platformsDir, "android"); - let packageName = this.getProjectNameFromId(); + public getPlatformData(projectData: IProjectData): IPlatformData { + if (!projectData && !this._platformData) { + throw new Error("First call of getPlatformData without providing projectData."); + } + + if (projectData && projectData.platformsDir && this._platformsDirCache !== projectData.platformsDir) { + this._platformsDirCache = projectData.platformsDir; + let projectRoot = path.join(projectData.platformsDir, "android"); + let packageName = this.getProjectNameFromId(projectData); this._platformData = { frameworkPackageName: "tns-android", normalizedPlatformName: "Android", @@ -56,8 +60,8 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject validPackageNamesForDevice: [ `${packageName}-debug.apk`, `${packageName}-release.apk`, - `${this.$projectData.projectName}-debug.apk`, - `${this.$projectData.projectName}-release.apk` + `${projectData.projectName}-debug.apk`, + `${projectData.projectName}-release.apk` ], frameworkFilesExtensions: [".jar", ".dat", ".so"], configurationFileName: "AndroidManifest.xml", @@ -74,17 +78,17 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject return Promise.resolve(true); } - public getAppResourcesDestinationDirectoryPath(frameworkVersion?: string): string { - if (this.canUseGradle(frameworkVersion)) { - return path.join(this.platformData.projectRoot, "src", "main", "res"); + public getAppResourcesDestinationDirectoryPath(projectData: IProjectData, frameworkVersion?: string): string { + if (this.canUseGradle(projectData, frameworkVersion)) { + return path.join(this.getPlatformData(projectData).projectRoot, "src", "main", "res"); } - return path.join(this.platformData.projectRoot, "res"); + return path.join(this.getPlatformData(projectData).projectRoot, "res"); } - public async validate(): Promise { - this.validatePackageName(this.$projectData.projectId); - this.validateProjectName(this.$projectData.projectName); + public async validate(projectData: IProjectData): Promise { + this.validatePackageName(projectData.projectId); + this.validateProjectName(projectData.projectName); // this call will fail in case `android` is not set correctly. await this.$androidToolsInfo.getPathToAndroidExecutable({ showWarningsAsErrors: true }); @@ -94,37 +98,37 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject await this.$androidToolsInfo.validateJavacVersion(javaCompilerVersion, { showWarningsAsErrors: true }); } - public async createProject(frameworkDir: string, frameworkVersion: string, pathToTemplate?: string): Promise { + public async createProject(frameworkDir: string, frameworkVersion: string, projectData: IProjectData, 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.$fs.ensureDirectoryExists(this.platformData.projectRoot); + this.$fs.ensureDirectoryExists(this.getPlatformData(projectData).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"); + this.copy(this.getPlatformData(projectData).projectRoot, frameworkDir, "libs", "-R"); if (pathToTemplate) { - let mainPath = path.join(this.platformData.projectRoot, "src", "main"); + let mainPath = path.join(this.getPlatformData(projectData).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.getPlatformData(projectData).projectRoot, frameworkDir, "src", "-R"); } - this.copy(this.platformData.projectRoot, frameworkDir, "build.gradle settings.gradle build-tools", "-Rf"); + this.copy(this.getPlatformData(projectData).projectRoot, frameworkDir, "build.gradle settings.gradle build-tools", "-Rf"); try { - this.copy(this.platformData.projectRoot, frameworkDir, "gradle.properties", "-Rf"); + this.copy(this.getPlatformData(projectData).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.`); } - this.copy(this.platformData.projectRoot, frameworkDir, "gradle", "-R"); - this.copy(this.platformData.projectRoot, frameworkDir, "gradlew gradlew.bat", "-f"); + this.copy(this.getPlatformData(projectData).projectRoot, frameworkDir, "gradle", "-R"); + this.copy(this.getPlatformData(projectData).projectRoot, frameworkDir, "gradlew gradlew.bat", "-f"); - this.cleanResValues(targetSdkVersion, frameworkVersion); + this.cleanResValues(targetSdkVersion, projectData, frameworkVersion); let npmConfig = { "save": true, @@ -133,20 +137,20 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject "silent": true }; - let projectPackageJson: any = this.$fs.readJson(this.$projectData.projectFilePath); + let projectPackageJson: any = this.$fs.readJson(projectData.projectFilePath); for (let dependency of AndroidProjectService.REQUIRED_DEV_DEPENDENCIES) { let dependencyVersionInProject = (projectPackageJson.dependencies && projectPackageJson.dependencies[dependency.name]) || (projectPackageJson.devDependencies && projectPackageJson.devDependencies[dependency.name]); if (!dependencyVersionInProject) { - await this.$npm.install(`${dependency.name}@${dependency.version}`, this.$projectData.projectDir, npmConfig); + await this.$npm.install(`${dependency.name}@${dependency.version}`, 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); + let pathToPluginPackageJson = path.join(projectData.projectDir, constants.NODE_MODULES_FOLDER_NAME, dependency.name, constants.PACKAGE_JSON_FILE_NAME); dependencyVersionInProject = this.$fs.exists(pathToPluginPackageJson) && this.$fs.readJson(pathToPluginPackageJson).version; } @@ -157,8 +161,8 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject }; } - private cleanResValues(targetSdkVersion: number, frameworkVersion: string): void { - let resDestinationDir = this.getAppResourcesDestinationDirectoryPath(frameworkVersion); + private cleanResValues(targetSdkVersion: number, projectData: IProjectData, frameworkVersion: string): void { + let resDestinationDir = this.getAppResourcesDestinationDirectoryPath(projectData, frameworkVersion); let directoriesInResFolder = this.$fs.readDirectory(resDestinationDir); let directoriesToClean = directoriesInResFolder .map(dir => { @@ -179,37 +183,37 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject _.map(directoriesToClean, dir => this.$fs.deleteDirectory(dir)); } - public async interpolateData(): Promise { + public async interpolateData(projectData: IProjectData): Promise { // Interpolate the apilevel and package - await this.interpolateConfigurationFile(); + await this.interpolateConfigurationFile(projectData); - 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(projectData), 'values', 'strings.xml'); + shell.sed('-i', /__NAME__/, projectData.projectName, stringsFilePath); + shell.sed('-i', /__TITLE_ACTIVITY__/, 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.getPlatformData(projectData).projectRoot, "settings.gradle"); + shell.sed('-i', /__PROJECT_NAME__/, this.getProjectNameFromId(projectData), 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"); + let userAppGradleFilePath = path.join(projectData.appResourcesDirectoryPath, this.$devicePlatformsConstants.Android, "app.gradle"); try { - shell.sed('-i', /__PACKAGE__/, this.$projectData.projectId, userAppGradleFilePath); + shell.sed('-i', /__PACKAGE__/, projectData.projectId, userAppGradleFilePath); } catch (e) { this.$logger.warn(`\n${e}.\nCheck if you're using an outdated template and update it.`); } } - 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); + public async interpolateConfigurationFile(projectData: IProjectData, sdk?: string): Promise { + let manifestPath = this.getPlatformData(projectData).configurationFilePath; + shell.sed('-i', /__PACKAGE__/, projectData.projectId, manifestPath); + shell.sed('-i', /__APILEVEL__/, sdk || (await this.$androidToolsInfo.getToolsInfo()).compileSdkVersion.toString(), manifestPath); } - private getProjectNameFromId(): string { + private getProjectNameFromId(projectData: IProjectData): string { let id: string; - if (this.$projectData && this.$projectData.projectId) { - id = this.$projectData.projectId.split(".")[2]; + if (projectData && projectData.projectId) { + id = projectData.projectId.split(".")[2]; } return id; @@ -219,13 +223,13 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject return null; } - public canUpdatePlatform(newInstalledModuleDir: string): boolean { + public canUpdatePlatform(newInstalledModuleDir: string, projectData: IProjectData): boolean { return true; } - public async updatePlatform(currentVersion: string, newVersion: string, canUpdate: boolean, addPlatform?: Function, removePlatforms?: (platforms: string[]) => Promise): Promise { + public async updatePlatform(currentVersion: string, newVersion: string, canUpdate: boolean, projectData: IProjectData, addPlatform?: Function, removePlatforms?: (platforms: string[]) => Promise): Promise { if (semver.eq(newVersion, AndroidProjectService.MIN_RUNTIME_VERSION_WITH_GRADLE)) { - let platformLowercase = this.platformData.normalizedPlatformName.toLowerCase(); + let platformLowercase = this.getPlatformData(projectData).normalizedPlatformName.toLowerCase(); await removePlatforms([platformLowercase.split("@")[0]]); await addPlatform(platformLowercase); return false; @@ -234,9 +238,9 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject return true; } - public async buildProject(projectRoot: string, buildConfig?: IBuildConfig): Promise { - if (this.canUseGradle()) { - let buildOptions = await this.getBuildOptions(); + public async buildProject(projectRoot: string, projectData: IProjectData, buildConfig: IBuildConfig): Promise { + if (this.canUseGradle(projectData)) { + let buildOptions = await this.getBuildOptions(buildConfig, projectData); if (this.$logger.getLevel() === "TRACE") { buildOptions.unshift("--stacktrace"); buildOptions.unshift("--debug"); @@ -246,19 +250,26 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject 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 }); + + this.$childProcess.on(constants.BUILD_OUTPUT_EVENT_NAME, (data: any) => { + this.emit(constants.BUILD_OUTPUT_EVENT_NAME, data); + }); + await this.spawn(gradleBin, + buildOptions, + { stdio: buildConfig.buildOutputStdio || "inherit", cwd: this.getPlatformData(projectData).projectRoot }, + { emitOptions: { eventName: constants.BUILD_OUTPUT_EVENT_NAME }, throwError: false }); } 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."); } } - private async getBuildOptions(): Promise> { + private async getBuildOptions(settings: IAndroidBuildOptionsSettings, projectData: IProjectData): 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 targetSdk = this.getTargetFromAndroidManifest(projectData) || compileSdk; let buildToolsVersion = androidToolsInfo.buildToolsVersion; let appCompatVersion = androidToolsInfo.supportRepositoryVersion; let generateTypings = androidToolsInfo.generateTypings; @@ -270,23 +281,23 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject `-PgenerateTypings=${generateTypings}` ]; - if (this.$options.release) { + if (settings.release) { buildOptions.push("-Prelease"); - buildOptions.push(`-PksPath=${path.resolve(this.$options.keyStorePath)}`); - buildOptions.push(`-Palias=${this.$options.keyStoreAlias}`); - buildOptions.push(`-Ppassword=${this.$options.keyStoreAliasPassword}`); - buildOptions.push(`-PksPassword=${this.$options.keyStorePassword}`); + buildOptions.push(`-PksPath=${path.resolve(settings.keyStorePath)}`); + buildOptions.push(`-Palias=${settings.keyStoreAlias}`); + buildOptions.push(`-Ppassword=${settings.keyStoreAliasPassword}`); + buildOptions.push(`-PksPassword=${settings.keyStorePassword}`); } return buildOptions; } - public async buildForDeploy(projectRoot: string, buildConfig?: IBuildConfig): Promise { - return this.buildProject(projectRoot, buildConfig); + public async buildForDeploy(projectRoot: string, projectData: IProjectData, buildConfig?: IBuildConfig): Promise { + return this.buildProject(projectRoot, projectData, buildConfig); } - public isPlatformPrepared(projectRoot: string): boolean { - return this.$fs.exists(path.join(this.platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME)); + public isPlatformPrepared(projectRoot: string, projectData: IProjectData): boolean { + return this.$fs.exists(path.join(this.getPlatformData(projectData).appDestinationDirectoryPath, constants.APP_FOLDER_NAME)); } public getFrameworkFilesExtensions(): string[] { @@ -297,8 +308,8 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject // Intentionally left empty. } - public ensureConfigurationFileInAppResources(): void { - let originalAndroidManifestFilePath = path.join(this.$projectData.appResourcesDirectoryPath, this.$devicePlatformsConstants.Android, this.platformData.configurationFileName); + public ensureConfigurationFileInAppResources(projectData: IProjectData): void { + let originalAndroidManifestFilePath = path.join(projectData.appResourcesDirectoryPath, this.$devicePlatformsConstants.Android, this.getPlatformData(projectData).configurationFileName); let manifestExists = this.$fs.exists(originalAndroidManifestFilePath); @@ -307,29 +318,29 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject return; } // Overwrite the AndroidManifest from runtime. - this.$fs.copyFile(originalAndroidManifestFilePath, this.platformData.configurationFilePath); + this.$fs.copyFile(originalAndroidManifestFilePath, this.getPlatformData(projectData).configurationFilePath); } - public prepareAppResources(appResourcesDirectoryPath: string): void { - let resourcesDirPath = path.join(appResourcesDirectoryPath, this.platformData.normalizedPlatformName); + public prepareAppResources(appResourcesDirectoryPath: string, projectData: IProjectData): void { + let resourcesDirPath = path.join(appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName); let valuesDirRegExp = /^values/; let resourcesDirs = this.$fs.readDirectory(resourcesDirPath).filter(resDir => !resDir.match(valuesDirRegExp)); _.each(resourcesDirs, resourceDir => { - this.$fs.deleteDirectory(path.join(this.getAppResourcesDestinationDirectoryPath(), resourceDir)); + this.$fs.deleteDirectory(path.join(this.getAppResourcesDestinationDirectoryPath(projectData), resourceDir)); }); } - public async preparePluginNativeCode(pluginData: IPluginData): Promise { + public async preparePluginNativeCode(pluginData: IPluginData, projectData: IProjectData): Promise { let pluginPlatformsFolderPath = this.getPluginPlatformsFolderPath(pluginData, AndroidProjectService.ANDROID_PLATFORM_NAME); - await this.processResourcesFromPlugin(pluginData, pluginPlatformsFolderPath); + await this.processResourcesFromPlugin(pluginData, pluginPlatformsFolderPath, projectData); } public async processConfigurationFilesFromAppResources(): Promise { return; } - private async processResourcesFromPlugin(pluginData: IPluginData, pluginPlatformsFolderPath: string): Promise { - let configurationsDirectoryPath = path.join(this.platformData.projectRoot, "configurations"); + private async processResourcesFromPlugin(pluginData: IPluginData, pluginPlatformsFolderPath: string, projectData: IProjectData): Promise { + let configurationsDirectoryPath = path.join(this.getPlatformData(projectData).projectRoot, "configurations"); this.$fs.ensureDirectoryExists(configurationsDirectoryPath); let pluginConfigurationDirectoryPath = path.join(configurationsDirectoryPath, pluginData.name); @@ -337,14 +348,14 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject this.$fs.ensureDirectoryExists(pluginConfigurationDirectoryPath); // Copy all resources from plugin - let resourcesDestinationDirectoryPath = path.join(this.platformData.projectRoot, "src", pluginData.name); + let resourcesDestinationDirectoryPath = path.join(this.getPlatformData(projectData).projectRoot, "src", pluginData.name); this.$fs.ensureDirectoryExists(resourcesDestinationDirectoryPath); shell.cp("-Rf", path.join(pluginPlatformsFolderPath, "*"), resourcesDestinationDirectoryPath); 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); + await this.$pluginVariablesService.interpolate(pluginData, file, projectData); } } @@ -355,12 +366,12 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } } - public async removePluginNativeCode(pluginData: IPluginData): Promise { + public async removePluginNativeCode(pluginData: IPluginData, projectData: IProjectData): Promise { try { // check whether the dependency that's being removed has native code - let pluginConfigDir = path.join(this.platformData.projectRoot, "configurations", pluginData.name); + let pluginConfigDir = path.join(this.getPlatformData(projectData).projectRoot, "configurations", pluginData.name); if (this.$fs.exists(pluginConfigDir)) { - await this.cleanProject(this.platformData.projectRoot, []); + await this.cleanProject(this.getPlatformData(projectData).projectRoot, [], projectData); } } catch (e) { if (e.code === "ENOENT") { @@ -371,32 +382,32 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } } - public async afterPrepareAllPlugins(): Promise { + public async afterPrepareAllPlugins(projectData: IProjectData): Promise { return; } - public async beforePrepareAllPlugins(dependencies?: IDictionary): Promise { + public async beforePrepareAllPlugins(projectData: IProjectData, dependencies?: IDictionary): Promise { if (!this.$config.debugLivesync) { if (dependencies) { - let platformDir = path.join(this.$projectData.platformsDir, "android"); + let platformDir = path.join(projectData.platformsDir, "android"); let buildDir = path.join(platformDir, "build-tools"); let checkV8dependants = path.join(buildDir, "check-v8-dependants.js"); if (this.$fs.exists(checkV8dependants)) { let stringifiedDependencies = JSON.stringify(dependencies); - await this.spawn('node', [checkV8dependants, stringifiedDependencies, this.$projectData.platformsDir], { stdio: "inherit" }); + await this.spawn('node', [checkV8dependants, stringifiedDependencies, projectData.platformsDir], { stdio: "inherit" }); } } - let buildOptions = await this.getBuildOptions(); + // We don't need release options here + let buildOptions = await this.getBuildOptions({ release: false }, projectData); - let projectRoot = this.platformData.projectRoot; + let projectRoot = this.getPlatformData(projectData).projectRoot; - await this.cleanProject(projectRoot, buildOptions); + await this.cleanProject(projectRoot, buildOptions, projectData); } } - public async stopServices(): Promise { - let projectRoot = this.platformData.projectRoot; + public stopServices(projectRoot: string): Promise { let gradleBin = path.join(projectRoot, "gradlew"); if (this.$hostInfo.isWindows) { gradleBin += ".bat"; @@ -405,7 +416,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject return this.$childProcess.spawnFromEvent(gradleBin, ["--stop", "--quiet"], "close", { stdio: "inherit", cwd: projectRoot }); } - public async cleanProject(projectRoot: string, options: string[]): Promise { + public async cleanProject(projectRoot: string, options: string[], projectData: IProjectData): Promise { options.unshift("clean"); let gradleBin = path.join(projectRoot, "gradlew"); @@ -413,20 +424,20 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject gradleBin += ".bat"; } - await this.spawn(gradleBin, options, { stdio: "inherit", cwd: this.platformData.projectRoot }); + await this.spawn(gradleBin, options, { stdio: "inherit", cwd: this.getPlatformData(projectData).projectRoot }); } - public async deploy(deviceIdentifier: string): Promise { + public async deploy(deviceIdentifier: string, projectData: IProjectData): Promise { let adb = this.$injector.resolve(DeviceAndroidDebugBridge, { identifier: deviceIdentifier }); - let deviceRootPath = `/data/local/tmp/${this.$projectData.projectId}`; + let deviceRootPath = `/data/local/tmp/${projectData.projectId}`; await adb.executeShellCommand(["rm", "-rf", deviceRootPath]); } private _canUseGradle: boolean; - private canUseGradle(frameworkVersion?: string): boolean { + private canUseGradle(projectData: IProjectData, frameworkVersion?: string): boolean { if (!this._canUseGradle) { if (!frameworkVersion) { - const frameworkInfoInProjectFile = this.$projectDataService.getNSValue(this.$projectData.projectDir, this.platformData.frameworkPackageName); + const frameworkInfoInProjectFile = this.$projectDataService.getNSValue(projectData.projectDir, this.getPlatformData(projectData).frameworkPackageName); frameworkVersion = frameworkInfoInProjectFile && frameworkInfoInProjectFile.version; } @@ -441,8 +452,8 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject shell.cp(cpArg, paths, projectRoot); } - private async spawn(command: string, args: string[], opts?: any): Promise { - return this.$childProcess.spawnFromEvent(command, args, "close", opts || { stdio: "inherit" }); + private async spawn(command: string, args: string[], opts?: any, spawnOpts?: ISpawnFromEventOptions): Promise { + return this.$childProcess.spawnFromEvent(command, args, "close", opts || { stdio: "inherit" }, spawnOpts); } private validatePackageName(packageName: string): void { @@ -469,10 +480,10 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } } - private getTargetFromAndroidManifest(): string { + private getTargetFromAndroidManifest(projectData: IProjectData): string { let versionInManifest: string; - if (this.$fs.exists(this.platformData.configurationFilePath)) { - let targetFromAndroidManifest: string = this.$fs.readText(this.platformData.configurationFilePath); + if (this.$fs.exists(this.getPlatformData(projectData).configurationFilePath)) { + let targetFromAndroidManifest: string = this.$fs.readText(this.getPlatformData(projectData).configurationFilePath); if (targetFromAndroidManifest) { let match = targetFromAndroidManifest.match(/.*?android:targetSdkVersion=\"(.*?)\"/); if (match && match[1]) { diff --git a/lib/services/app-files-updater.ts b/lib/services/app-files-updater.ts index 9ea14993c1..dde5831ce4 100644 --- a/lib/services/app-files-updater.ts +++ b/lib/services/app-files-updater.ts @@ -7,7 +7,7 @@ export class AppFilesUpdater { constructor( private appSourceDirectoryPath: string, private appDestinationDirectoryPath: string, - public options: IOptions, + public options: { release: boolean; bundle: boolean }, public fs: IFileSystem ) { } diff --git a/lib/services/emulator-platform-service.ts b/lib/services/emulator-platform-service.ts index fe9e5cfdbf..cda415a577 100644 --- a/lib/services/emulator-platform-service.ts +++ b/lib/services/emulator-platform-service.ts @@ -10,13 +10,13 @@ export class EmulatorPlatformService implements IEmulatorPlatformService { private $options: IOptions, private $logger: ILogger) { } - public async startEmulator(info: IEmulatorInfo): Promise { + public async startEmulator(info: IEmulatorInfo, projectData: IProjectData): 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 platformData = platformsData.getPlatformData(info.platform, projectData); let emulatorServices = platformData.emulatorServices; emulatorServices.checkAvailability(); await emulatorServices.checkDependencies(); diff --git a/lib/services/init-service.ts b/lib/services/init-service.ts index 973ce986eb..1dad32e290 100644 --- a/lib/services/init-service.ts +++ b/lib/services/init-service.ts @@ -39,7 +39,6 @@ export class InitService implements IInitService { } try { - projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE]["id"] = await this.getProjectId(); if (this.$options.frameworkName && this.$options.frameworkVersion) { @@ -48,8 +47,10 @@ export class InitService implements IInitService { 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"); + let $projectData = this.$injector.resolve("projectData"); + $projectData.initializeProjectData(path.dirname(this.projectFilePath)); for (let platform of $platformsData.platformsNames) { - let platformData: IPlatformData = $platformsData.getPlatformData(platform); + let platformData: IPlatformData = $platformsData.getPlatformData(platform, $projectData); if (!platformData.targetedOS || (platformData.targetedOS && _.includes(platformData.targetedOS, process.platform))) { let currentPlatformData = projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE][platformData.frameworkPackageName] || {}; diff --git a/lib/services/ios-debug-service.ts b/lib/services/ios-debug-service.ts index 5790c4e99d..d38c3392a1 100644 --- a/lib/services/ios-debug-service.ts +++ b/lib/services/ios-debug-service.ts @@ -22,7 +22,6 @@ class IOSDebugService implements IDebugService { private $iOSEmulatorServices: Mobile.IEmulatorPlatformServices, private $devicesService: Mobile.IDevicesService, private $platformsData: IPlatformsData, - private $projectData: IProjectData, private $childProcess: IChildProcess, private $logger: ILogger, private $errors: IErrors, @@ -40,7 +39,7 @@ class IOSDebugService implements IDebugService { return "ios"; } - public async debug(): Promise { + public async debug(projectData: IProjectData): Promise { if (this.$options.debugBrk && this.$options.start) { this.$errors.failWithoutHelp("Expected exactly one of the --debug-brk or --start options."); } @@ -51,26 +50,26 @@ class IOSDebugService implements IDebugService { if (this.$options.emulator) { if (this.$options.debugBrk) { - return this.emulatorDebugBrk(true); + return this.emulatorDebugBrk(projectData, true); } else if (this.$options.start) { - return this.emulatorStart(); + return this.emulatorStart(projectData); } else { - return this.emulatorDebugBrk(); + return this.emulatorDebugBrk(projectData); } } else { if (this.$options.debugBrk) { - return this.deviceDebugBrk(true); + return this.deviceDebugBrk(projectData, true); } else if (this.$options.start) { - return this.deviceStart(); + return this.deviceStart(projectData); } else { - return this.deviceDebugBrk(false); + return this.deviceDebugBrk(projectData, false); } } } - public async debugStart(): Promise { + public async debugStart(projectData: IProjectData): 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)); + this.$devicesService.execute(async (device: Mobile.IiOSDevice) => await device.isEmulator ? this.emulatorDebugBrk(projectData) : this.debugBrkCore(device, projectData)); } public async debugStop(): Promise { @@ -94,15 +93,15 @@ class IOSDebugService implements IDebugService { } } - private async emulatorDebugBrk(shouldBreak?: boolean): Promise { - let platformData = this.$platformsData.getPlatformData(this.platform); + private async emulatorDebugBrk(projectData: IProjectData, shouldBreak?: boolean): Promise { + let platformData = this.$platformsData.getPlatformData(this.platform, projectData); 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, + args: args, appId: projectData.projectId, skipInstall: true }); @@ -111,8 +110,8 @@ class IOSDebugService implements IDebugService { lineStream.on('data', (line: NodeBuffer) => { let lineText = line.toString(); - if (lineText && _.startsWith(lineText, this.$projectData.projectId)) { - let pid = _.trimStart(lineText, this.$projectData.projectId + ": "); + if (lineText && _.startsWith(lineText, projectData.projectId)) { + let pid = _.trimStart(lineText, projectData.projectId + ": "); this._lldbProcess = this.$childProcess.spawn("lldb", ["-p", pid]); if (log4js.levels.TRACE.isGreaterThanOrEqualTo(this.$logger.getLevel())) { this._lldbProcess.stdout.pipe(process.stdout); @@ -124,52 +123,57 @@ class IOSDebugService implements IDebugService { } }); - await this.wireDebuggerClient(); + await this.wireDebuggerClient(projectData); } - private async emulatorStart(): Promise { - await this.wireDebuggerClient(); + private async emulatorStart(projectData: IProjectData): Promise { + await this.wireDebuggerClient(projectData); - let attachRequestMessage = this.$iOSNotification.attachRequest; + let attachRequestMessage = this.$iOSNotification.getAttachRequest(projectData.projectId); let iOSEmulator = this.$iOSEmulatorServices; await iOSEmulator.postDarwinNotification(attachRequestMessage); } - private async deviceDebugBrk(shouldBreak?: boolean): Promise { + private async deviceDebugBrk(projectData: IProjectData, 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); + return await this.emulatorDebugBrk(projectData, shouldBreak); } + const runOptions: IRunPlatformOptions = { + device: this.$options.device, + emulator: this.$options.emulator, + justlaunch: this.$options.justlaunch + }; // 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); + let action = this.$platformService.runPlatform(this.platform, runOptions, projectData); - await this.debugBrkCore(device, shouldBreak); + await this.debugBrkCore(device, projectData, shouldBreak); await action; }); } - private async debugBrkCore(device: Mobile.IiOSDevice, shouldBreak?: boolean): Promise { + private async debugBrkCore(device: Mobile.IiOSDevice, projectData: IProjectData, shouldBreak?: boolean): Promise { let timeout = this.$utils.getMilliSecondsTimeout(TIMEOUT_SECONDS); - await this.$iOSSocketRequestExecutor.executeLaunchRequest(device, timeout, timeout, shouldBreak); - await this.wireDebuggerClient(device); + await this.$iOSSocketRequestExecutor.executeLaunchRequest(device, timeout, timeout, projectData.projectId, shouldBreak); + await this.wireDebuggerClient(projectData, device); } - private async deviceStart(): Promise { + private async deviceStart(projectData: IProjectData): 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)); + this.$devicesService.execute(async (device: Mobile.IiOSDevice) => device.isEmulator ? await this.emulatorStart(projectData) : await this.deviceStartCore(device, projectData)); } - private async deviceStartCore(device: Mobile.IiOSDevice): Promise { + private async deviceStartCore(device: Mobile.IiOSDevice, projectData: IProjectData): Promise { let timeout = this.$utils.getMilliSecondsTimeout(TIMEOUT_SECONDS); - await this.$iOSSocketRequestExecutor.executeAttachRequest(device, timeout); - await this.wireDebuggerClient(device); + await this.$iOSSocketRequestExecutor.executeAttachRequest(device, timeout, projectData.projectId); + await this.wireDebuggerClient(projectData, device); } - private async wireDebuggerClient(device?: Mobile.IiOSDevice): Promise { + private async wireDebuggerClient(projectData: IProjectData, device?: Mobile.IiOSDevice): Promise { let factory = () => { let socket = device ? device.connectToPort(inspectorBackendPort) : net.connect(inspectorBackendPort); this._sockets.push(socket); @@ -183,18 +187,18 @@ class IOSDebugService implements IDebugService { 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()); + await this.openAppInspector(this._socketProxy.address(), projectData); } } - private async openAppInspector(fileDescriptor: string): Promise { + private async openAppInspector(fileDescriptor: string, projectData: IProjectData): Promise { if (this.$options.client) { - let inspectorPath = await this.$npmInstallationManager.getInspectorFromCache(inspectorNpmPackageName, this.$projectData.projectDir); + let inspectorPath = await this.$npmInstallationManager.getInspectorFromCache(inspectorNpmPackageName, projectData.projectDir); 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}'`; + let cmd = `open -a '${inspectorApplicationPath}' --args '${inspectorSourceLocation}' '${projectData.projectName}' '${fileDescriptor}'`; await this.$childProcess.exec(cmd); } else { this.$logger.info("Suppressing debugging client."); diff --git a/lib/services/ios-log-filter.ts b/lib/services/ios-log-filter.ts index 2619dedc45..a263252c41 100644 --- a/lib/services/ios-log-filter.ts +++ b/lib/services/ios-log-filter.ts @@ -3,13 +3,12 @@ import * as path from "path"; export class IOSLogFilter implements Mobile.IPlatformLogFilter { - private $projectData: IProjectData; private partialLine: string = null; constructor(private $fs: IFileSystem) { } - public filterData(data: string, logLevel: string, pid?: string): string { + public filterData(data: string, logLevel: string, projectDir: string, pid?: string): string { if (pid && data && data.indexOf(`[${pid}]`) === -1) { return null; @@ -41,15 +40,15 @@ export class IOSLogFilter implements Mobile.IPlatformLogFilter { let pidIndex = line.indexOf(searchString); if (pidIndex > 0) { line = line.substring(pidIndex + searchString.length, line.length); - this.getOriginalFileLocation(line); - result += this.getOriginalFileLocation(line) + "\n"; + this.getOriginalFileLocation(line, projectDir); + result += this.getOriginalFileLocation(line, projectDir) + "\n"; continue; } } if (skipLastLine && i === lines.length - 1) { this.partialLine = line; } else { - result += this.getOriginalFileLocation(line) + "\n"; + result += this.getOriginalFileLocation(line, projectDir) + "\n"; } } return result; @@ -58,7 +57,7 @@ export class IOSLogFilter implements Mobile.IPlatformLogFilter { return data; } - private getOriginalFileLocation(data: string): string { + private getOriginalFileLocation(data: string, projectDir: string): string { let fileString = "file:///"; let fileIndex = data.indexOf(fileString); @@ -66,23 +65,21 @@ export class IOSLogFilter implements Mobile.IPlatformLogFilter { 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 (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 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; + let sourceMapFile = path.join(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 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 < parts.length; i++) { - data += ":" + parts[i]; - } + for (let i = 3; i < parts.length; i++) { + data += ":" + parts[i]; } } } @@ -90,12 +87,5 @@ export class IOSLogFilter implements Mobile.IPlatformLogFilter { return data; } - - private ensureProjectData(): boolean { - if (!this.$projectData) { - this.$projectData = $injector.resolve("projectData"); - } - return !!this.$projectData; - } } $injector.register("iOSLogFilter", IOSLogFilter); diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index 71eb1b6f39..7c6359aa68 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -25,14 +25,12 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ return this.$injector.resolve("npmInstallationManager"); } - constructor($projectData: IProjectData, - $fs: IFileSystem, + constructor($fs: IFileSystem, private $childProcess: IChildProcess, private $cocoapodsService: ICocoaPodsService, private $errors: IErrors, private $logger: ILogger, private $iOSEmulatorServices: Mobile.IEmulatorPlatformServices, - private $options: IOptions, private $injector: IInjector, $projectDataService: IProjectDataService, private $prompter: IPrompter, @@ -43,41 +41,51 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ private $pluginVariablesService: IPluginVariablesService, private $xcprojService: IXcprojService, private $iOSProvisionService: IOSProvisionService) { - super($fs, $projectData, $projectDataService); - } - - public get platformData(): IPlatformData { - let projectRoot = path.join(this.$projectData.platformsDir, "ios"); - - return { - frameworkPackageName: "tns-ios", - normalizedPlatformName: "iOS", - appDestinationDirectoryPath: path.join(projectRoot, this.$projectData.projectName), - platformProjectService: this, - emulatorServices: this.$iOSEmulatorServices, - projectRoot: projectRoot, - deviceBuildOutputPath: path.join(projectRoot, "build", "device"), - emulatorBuildOutputPath: path.join(projectRoot, "build", "emulator"), - validPackageNamesForDevice: [ - this.$projectData.projectName + ".ipa" - ], - validPackageNamesForEmulator: [ - this.$projectData.projectName + ".app" - ], - frameworkFilesExtensions: [".a", ".framework", ".bin"], - frameworkDirectoriesExtensions: [".framework"], - frameworkDirectoriesNames: ["Metadata", "metadataGenerator", "NativeScript", "internal"], - targetedOS: ['darwin'], - configurationFileName: "Info.plist", - configurationFilePath: path.join(projectRoot, this.$projectData.projectName, this.$projectData.projectName + "-Info.plist"), - relativeToFrameworkConfigurationFilePath: path.join("__PROJECT_NAME__", "__PROJECT_NAME__-Info.plist"), - fastLivesyncFileExtensions: [".tiff", ".tif", ".jpg", "jpeg", "gif", ".png", ".bmp", ".BMPf", ".ico", ".cur", ".xbm"] // https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIImage_Class/ - }; + super($fs, $projectDataService); } - public async validateOptions(): Promise { - if (this.$options.provision === true) { - await this.$iOSProvisionService.list(); + private _platformsDirCache: string = null; + private _platformData: IPlatformData = null; + public getPlatformData(projectData: IProjectData): IPlatformData { + if (!projectData && !this._platformData) { + throw new Error("First call of getPlatformData without providing projectData."); + } + + if (projectData && projectData.platformsDir && this._platformsDirCache !== projectData.platformsDir) { + let projectRoot = path.join(projectData.platformsDir, "ios"); + + this._platformData = { + frameworkPackageName: "tns-ios", + normalizedPlatformName: "iOS", + appDestinationDirectoryPath: path.join(projectRoot, projectData.projectName), + platformProjectService: this, + emulatorServices: this.$iOSEmulatorServices, + projectRoot: projectRoot, + deviceBuildOutputPath: path.join(projectRoot, "build", "device"), + emulatorBuildOutputPath: path.join(projectRoot, "build", "emulator"), + validPackageNamesForDevice: [ + projectData.projectName + ".ipa" + ], + validPackageNamesForEmulator: [ + projectData.projectName + ".app" + ], + frameworkFilesExtensions: [".a", ".framework", ".bin"], + frameworkDirectoriesExtensions: [".framework"], + frameworkDirectoriesNames: ["Metadata", "metadataGenerator", "NativeScript", "internal"], + targetedOS: ['darwin'], + configurationFileName: "Info.plist", + configurationFilePath: path.join(projectRoot, projectData.projectName, projectData.projectName + "-Info.plist"), + relativeToFrameworkConfigurationFilePath: path.join("__PROJECT_NAME__", "__PROJECT_NAME__-Info.plist"), + fastLivesyncFileExtensions: [".tiff", ".tif", ".jpg", "jpeg", "gif", ".png", ".bmp", ".BMPf", ".ico", ".cur", ".xbm"] // https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIImage_Class/ + }; + } + + return this._platformData; + } + + public async validateOptions(projectId: string, provision: any): Promise { + if (provision === true) { + await this.$iOSProvisionService.list(projectId); this.$errors.failWithoutHelp("Please provide provisioning profile uuid or name with the --provision option."); return false; } @@ -85,14 +93,14 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ return true; } - public getAppResourcesDestinationDirectoryPath(): string { - let frameworkVersion = this.getFrameworkVersion(this.platformData.frameworkPackageName); + public getAppResourcesDestinationDirectoryPath(projectData: IProjectData): string { + let frameworkVersion = this.getFrameworkVersion(this.getPlatformData(projectData).frameworkPackageName, projectData.projectDir); if (semver.lt(frameworkVersion, "1.3.0")) { - return path.join(this.platformData.projectRoot, this.$projectData.projectName, "Resources", "icons"); + return path.join(this.getPlatformData(projectData).projectRoot, projectData.projectName, "Resources", "icons"); } - return path.join(this.platformData.projectRoot, this.$projectData.projectName, "Resources"); + return path.join(this.getPlatformData(projectData).projectRoot, projectData.projectName, "Resources"); } public async validate(): Promise { @@ -109,81 +117,81 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ } // 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)); + public async createProject(frameworkDir: string, frameworkVersion: string, projectData: IProjectData, pathToTemplate?: string): Promise { + this.$fs.ensureDirectoryExists(path.join(this.getPlatformData(projectData).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); + .forEach(dirName => shell.cp("-R", path.join(frameworkDir, dirName), this.getPlatformData(projectData).projectRoot)); + shell.cp("-rf", path.join(pathToTemplate, "*"), this.getPlatformData(projectData).projectRoot); } else { - shell.cp("-R", path.join(frameworkDir, "*"), this.platformData.projectRoot); + shell.cp("-R", path.join(frameworkDir, "*"), this.getPlatformData(projectData).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); + public async interpolateData(projectData: IProjectData): Promise { + let projectRootFilePath = path.join(this.getPlatformData(projectData).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("-Info.plist", projectRootFilePath, projectData); } - this.replaceFileName("-Prefix.pch", projectRootFilePath); + this.replaceFileName("-Prefix.pch", projectRootFilePath, projectData); - let xcschemeDirPath = path.join(this.platformData.projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + IOSProjectService.XCODE_PROJECT_EXT_NAME, "xcshareddata/xcschemes"); + let xcschemeDirPath = path.join(this.getPlatformData(projectData).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.replaceFileContent(xcschemeFilePath, projectData); this.$logger.debug("Checkpoint 1"); - this.replaceFileName(IOSProjectService.XCODE_SCHEME_EXT_NAME, xcschemeDirPath); + this.replaceFileName(IOSProjectService.XCODE_SCHEME_EXT_NAME, xcschemeDirPath, projectData); 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); + this.replaceFileName(IOSProjectService.XCODE_PROJECT_EXT_NAME, this.getPlatformData(projectData).projectRoot, projectData); - let pbxprojFilePath = this.pbxProjPath; - this.replaceFileContent(pbxprojFilePath); + let pbxprojFilePath = this.getPbxProjPath(projectData); + this.replaceFileContent(pbxprojFilePath, projectData); } - public interpolateConfigurationFile(configurationFilePath?: string): Promise { + public interpolateConfigurationFile(projectData: IProjectData, configurationFilePath?: string): Promise { return Promise.resolve(); } - public afterCreateProject(projectRoot: string): void { + public afterCreateProject(projectRoot: string, projectData: IProjectData): void { this.$fs.rename(path.join(projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER), - path.join(projectRoot, this.$projectData.projectName)); + path.join(projectRoot, projectData.projectName)); } /** * Archive the Xcode project to .xcarchive. * Returns the path to the .xcarchive. */ - 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"); + public async archive(projectData: IProjectData, options?: { archivePath?: string }): Promise { + let projectRoot = this.getPlatformData(projectData).projectRoot; + let archivePath = options && options.archivePath ? path.resolve(options.archivePath) : path.join(projectRoot, "/build/archive/", 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' }); + .concat(this.xcbuildProjectArgs(projectRoot, projectData, "scheme")); + await this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", { stdio: 'inherit' }); return archivePath; } /** * Exports .xcarchive for AppStore distribution. */ - public async exportArchive(options: { archivePath: string, exportDir?: string, teamID?: string }): Promise { - let projectRoot = this.platformData.projectRoot; + public async exportArchive(projectData: IProjectData, options: { archivePath: string, exportDir?: string, teamID?: string }): Promise { + let projectRoot = this.getPlatformData(projectData).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"); + let exportFile = path.join(exportPath, projectData.projectName + ".ipa"); // These are the options that you can set in the Xcode UI when exporting for AppStore deployment. let plistTemplate = ` @@ -215,33 +223,33 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ "-exportPath", exportPath, "-exportOptionsPlist", exportOptionsPlist ]; - await this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", { cwd: this.$options, stdio: 'inherit' }); + await this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", { stdio: 'inherit' }); return exportFile; } - private xcbuildProjectArgs(projectRoot: string, product?: "scheme" | "target"): string[] { - let xcworkspacePath = path.join(projectRoot, this.$projectData.projectName + ".xcworkspace"); + private xcbuildProjectArgs(projectRoot: string, projectData: IProjectData, product?: "scheme" | "target"): string[] { + let xcworkspacePath = path.join(projectRoot, projectData.projectName + ".xcworkspace"); if (this.$fs.exists(xcworkspacePath)) { - return ["-workspace", xcworkspacePath, product ? "-" + product : "-scheme", this.$projectData.projectName]; + return ["-workspace", xcworkspacePath, product ? "-" + product : "-scheme", projectData.projectName]; } else { - let xcodeprojPath = path.join(projectRoot, this.$projectData.projectName + ".xcodeproj"); - return ["-project", xcodeprojPath, product ? "-" + product : "-target", this.$projectData.projectName]; + let xcodeprojPath = path.join(projectRoot, projectData.projectName + ".xcodeproj"); + return ["-project", xcodeprojPath, product ? "-" + product : "-target", projectData.projectName]; } } - public async buildProject(projectRoot: string, buildConfig?: IiOSBuildConfig): Promise { + public async buildProject(projectRoot: string, projectData: IProjectData, buildConfig: IiOSBuildConfig): Promise { let basicArgs = [ - "-configuration", this.$options.release ? "Release" : "Debug", + "-configuration", buildConfig.release ? "Release" : "Debug", "build", 'SHARED_PRECOMPS_DIR=' + path.join(projectRoot, 'build', 'sharedpch') ]; - basicArgs = basicArgs.concat(this.xcbuildProjectArgs(projectRoot)); + basicArgs = basicArgs.concat(this.xcbuildProjectArgs(projectRoot, projectData)); // Starting from tns-ios 1.4 the xcconfig file is referenced in the project template - let frameworkVersion = this.getFrameworkVersion(this.platformData.frameworkPackageName); + let frameworkVersion = this.getFrameworkVersion(this.getPlatformData(projectData).frameworkPackageName, projectData.projectDir); if (semver.lt(frameworkVersion, "1.4.0")) { - basicArgs.push("-xcconfig", path.join(projectRoot, this.$projectData.projectName, "build.xcconfig")); + basicArgs.push("-xcconfig", path.join(projectRoot, projectData.projectName, "build.xcconfig")); } // if (this.$logger.getLevel() === "INFO") { @@ -251,24 +259,26 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ // } // } - let buildForDevice = this.$options.forDevice || (buildConfig && buildConfig.buildForDevice); - if (buildForDevice) { - await this.buildForDevice(projectRoot, basicArgs, buildConfig); + this.$childProcess.on(constants.BUILD_OUTPUT_EVENT_NAME, (data: any) => { + this.emit(constants.BUILD_OUTPUT_EVENT_NAME, data); + }); + if (buildConfig.buildForDevice) { + await this.buildForDevice(projectRoot, basicArgs, buildConfig, projectData); } else { - await this.buildForSimulator(projectRoot, basicArgs, buildConfig); + await this.buildForSimulator(projectRoot, basicArgs, projectData, buildConfig.buildOutputStdio); } } - private async buildForDevice(projectRoot: string, args: string[], buildConfig?: IiOSBuildConfig): Promise { + private async buildForDevice(projectRoot: string, args: string[], buildConfig: IiOSBuildConfig, projectData: IProjectData): 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 }); + if (!buildConfig.release && !buildConfig.architectures) { + await this.$devicesService.initialize({ platform: this.$devicePlatformsConstants.iOS.toLowerCase(), deviceId: buildConfig.device }); let instances = this.$devicesService.getDeviceInstances(); let devicesArchitectures = _(instances) .filter(d => this.$mobileHelper.isiOSPlatform(d.deviceInfo.platform) && d.deviceInfo.activeArchitecture) @@ -283,9 +293,6 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ if (devicesArchitectures.length > 1) { architectures.push('ONLY_ACTIVE_ARCH=NO'); } - if (!buildConfig) { - buildConfig = {}; - } buildConfig.architectures = architectures; } } @@ -299,7 +306,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ let xcodeBuildVersion = await this.getXcodeVersion(); if (helpers.versionCompare(xcodeBuildVersion, "8.0") >= 0) { - await this.setupSigningForDevice(projectRoot, buildConfig); + await this.setupSigningForDevice(projectRoot, buildConfig, projectData); } if (buildConfig && buildConfig.codeSignIdentity) { @@ -315,23 +322,27 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ } // this.$logger.out("xcodebuild..."); - await this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", { cwd: this.$options, stdio: 'inherit' }); + await this.$childProcess.spawnFromEvent("xcodebuild", + args, + "exit", + { stdio: buildConfig.buildOutputStdio || "inherit", cwd: this.getPlatformData(projectData).projectRoot }, + { emitOptions: { eventName: constants.BUILD_OUTPUT_EVENT_NAME }, throwError: false }); // this.$logger.out("xcodebuild build succeded."); - await this.createIpa(projectRoot); + await this.createIpa(projectRoot, projectData, buildConfig.buildOutputStdio); } - private async setupSigningFromProvision(projectRoot: string, buildConfig?: IiOSBuildConfig): Promise { - if (this.$options.provision) { - const pbxprojPath = path.join(projectRoot, this.$projectData.projectName + ".xcodeproj", "project.pbxproj"); + private async setupSigningFromProvision(projectRoot: string, projectData: IProjectData, provision?: any): Promise { + if (provision) { + const pbxprojPath = path.join(projectRoot, projectData.projectName + ".xcodeproj", "project.pbxproj"); const xcode = Xcode.open(pbxprojPath); - const signing = xcode.getSigning(this.$projectData.projectName); + const signing = xcode.getSigning(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) { + if (options.name !== provision && options.uuid !== provision) { shouldUpdateXcode = true; break; } @@ -344,14 +355,14 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ // 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 mobileprovision = await this.$iOSProvisionService.pick(provision, projectData.projectId); 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); + this.$errors.failWithoutHelp("Failed to find mobile provision with UUID or Name: " + provision); } - xcode.setManualSigningStyle(this.$projectData.projectName, { + xcode.setManualSigningStyle(projectData.projectName, { team: mobileprovision.TeamIdentifier && mobileprovision.TeamIdentifier.length > 0 ? mobileprovision.TeamIdentifier[0] : undefined, uuid: mobileprovision.UUID, name: mobileprovision.Name, @@ -369,28 +380,28 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ } } - private async setupSigningForDevice(projectRoot: string, buildConfig?: IiOSBuildConfig): Promise { - const pbxprojPath = path.join(projectRoot, this.$projectData.projectName + ".xcodeproj", "project.pbxproj"); + private async setupSigningForDevice(projectRoot: string, buildConfig: IiOSBuildConfig, projectData: IProjectData): Promise { + const pbxprojPath = path.join(projectRoot, projectData.projectName + ".xcodeproj", "project.pbxproj"); const xcode = Xcode.open(pbxprojPath); - const signing = xcode.getSigning(this.$projectData.projectName); + const signing = xcode.getSigning(projectData.projectName); - if ((this.readXCConfigProvisioningProfile() || this.readXCConfigProvisioningProfileForIPhoneOs()) && (!signing || signing.style !== "Manual")) { - xcode.setManualSigningStyle(this.$projectData.projectName); + if ((this.readXCConfigProvisioningProfile(projectData) || this.readXCConfigProvisioningProfileForIPhoneOs(projectData)) && (!signing || signing.style !== "Manual")) { + xcode.setManualSigningStyle(projectData.projectName); xcode.save(); - } else if (!this.$options.provision && !(signing && signing.style === "Manual" && !this.$options.teamId)) { + } else if (!buildConfig.provision && !(signing && signing.style === "Manual" && !buildConfig.teamId)) { if (buildConfig) { delete buildConfig.teamIdentifier; } - const teamId = await this.getDevelopmentTeam(); + const teamId = await this.getDevelopmentTeam(projectData, buildConfig.teamId); - xcode.setAutomaticSigningStyle(this.$projectData.projectName, teamId); + xcode.setAutomaticSigningStyle(projectData.projectName, teamId); xcode.save(); this.$logger.trace("Set Automatic signing style and team."); } } - private async buildForSimulator(projectRoot: string, args: string[], buildConfig?: IiOSBuildConfig): Promise { + private async buildForSimulator(projectRoot: string, args: string[], projectData: IProjectData, buildOutputStdio?: string): Promise { args = args.concat([ "-sdk", "iphonesimulator", "ARCHS=i386 x86_64", @@ -400,37 +411,41 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ "CODE_SIGN_IDENTITY=" ]); - await this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", { cwd: this.$options, stdio: 'inherit' }); + await this.$childProcess.spawnFromEvent("xcodebuild", args, "exit", + { stdio: buildOutputStdio || "inherit", cwd: this.getPlatformData(projectData).projectRoot }, + { emitOptions: { eventName: constants.BUILD_OUTPUT_EVENT_NAME }, throwError: false }); } - private async createIpa(projectRoot: string): Promise { + private async createIpa(projectRoot: string, projectData: IProjectData, buildOutputStdio?: 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") + path.join(buildOutputPath, projectData.projectName + ".app"), + "-o", path.join(buildOutputPath, 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' }); + await this.$childProcess.spawnFromEvent("xcrun", xcrunArgs, "exit", + { stdio: buildOutputStdio || "inherit", cwd: this.getPlatformData(projectData).projectRoot }, + { emitOptions: { eventName: constants.BUILD_OUTPUT_EVENT_NAME }, throwError: false }); // 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 isPlatformPrepared(projectRoot: string, projectData: IProjectData): boolean { + return this.$fs.exists(path.join(projectRoot, projectData.projectName, constants.APP_FOLDER_NAME)); } public deploy(deviceIdentifier: string): Promise { return Promise.resolve(); } - private async addFramework(frameworkPath: string): Promise { + private async addFramework(frameworkPath: string, projectData: IProjectData): Promise { await this.validateFramework(frameworkPath); - let project = this.createPbxProj(); + let project = this.createPbxProj(projectData); 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 "); @@ -441,36 +456,36 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ frameworkAddOptions["embed"] = true; } - let frameworkRelativePath = '$(SRCROOT)/' + this.getLibSubpathRelativeToProjectPath(frameworkPath); + let frameworkRelativePath = '$(SRCROOT)/' + this.getLibSubpathRelativeToProjectPath(frameworkPath, projectData); project.addFramework(frameworkRelativePath, frameworkAddOptions); - this.savePbxProj(project); + this.savePbxProj(project, projectData); } - private async addStaticLibrary(staticLibPath: string): Promise { + private async addStaticLibrary(staticLibPath: string, projectData: IProjectData): 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); + let project = this.createPbxProj(projectData); + let relativeStaticLibPath = this.getLibSubpathRelativeToProjectPath(staticLibPath, projectData); project.addFramework(relativeStaticLibPath); - let relativeHeaderSearchPath = path.join(this.getLibSubpathRelativeToProjectPath(headersSubpath)); + let relativeHeaderSearchPath = path.join(this.getLibSubpathRelativeToProjectPath(headersSubpath, projectData)); project.addToHeaderSearchPaths({ relativePath: relativeHeaderSearchPath }); this.generateModulemap(headersSubpath, libraryName); - this.savePbxProj(project); + this.savePbxProj(project, projectData); } - public canUpdatePlatform(installedModuleDir: string): boolean { - let currentXcodeProjectFile = this.buildPathToCurrentXcodeProjectFile(); + public canUpdatePlatform(installedModuleDir: string, projectData: IProjectData): boolean { + let currentXcodeProjectFile = this.buildPathToCurrentXcodeProjectFile(projectData); let currentXcodeProjectFileContent = this.$fs.readFile(currentXcodeProjectFile); let newXcodeProjectFile = this.buildPathToNewXcodeProjectFile(installedModuleDir); - this.replaceFileContent(newXcodeProjectFile); + this.replaceFileContent(newXcodeProjectFile, projectData); let newXcodeProjectFileContent = this.$fs.readFile(newXcodeProjectFile); let contentIsTheSame = currentXcodeProjectFileContent.toString() === newXcodeProjectFileContent.toString(); @@ -491,12 +506,12 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ * Here we check if **UILaunchStoryboardName** is set to **LaunchScreen** in the **platform/ios//-Info.plist**. * If it is, and no **LaunchScreen.storyboard** nor **.xib** is found in the project, we will create one. */ - private provideLaunchScreenIfMissing(): void { + private provideLaunchScreenIfMissing(projectData: IProjectData): void { try { this.$logger.trace("Checking if we need to provide compatability LaunchScreen.xib"); - let platformData = this.platformData; - let projectPath = path.join(platformData.projectRoot, this.$projectData.projectName); - let projectPlist = this.getInfoPlistPath(); + let platformData = this.getPlatformData(projectData); + let projectPath = path.join(platformData.projectRoot, projectData.projectName); + let projectPlist = this.getInfoPlistPath(projectData); let plistContent = plist.parse(this.$fs.readText(projectPlist)); let storyName = plistContent["UILaunchStoryboardName"]; this.$logger.trace(`Examining ${projectPlist} UILaunchStoryboardName: "${storyName}".`); @@ -532,7 +547,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ Consider updating the resources in app/App_Resources/iOS/. A good starting point would be to create a new project and diff the changes with your current one. Also the following repo may be helpful: https://github.com/NativeScript/template-hello-world/tree/master/App_Resources/iOS -We will now place an empty obsolete compatability white screen LauncScreen.xib for you in ${path.relative(this.$projectData.projectDir, compatabilityXibPath)} so your app may appear as it did in pre v2.1.0 versions of the ios runtime.`); +We will now place an empty obsolete compatability white screen LauncScreen.xib for you in ${path.relative(projectData.projectDir, compatabilityXibPath)} so your app may appear as it did in pre v2.1.0 versions of the ios runtime.`); let content = ` @@ -563,15 +578,15 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } } - public async prepareProject(): Promise { - if (this.$options.provision) { - let projectRoot = path.join(this.$projectData.platformsDir, "ios"); - await this.setupSigningFromProvision(projectRoot); + public async prepareProject(projectData: IProjectData, provision?: any): Promise { + if (provision) { + let projectRoot = path.join(projectData.platformsDir, "ios"); + await this.setupSigningFromProvision(projectRoot, provision); } - let project = this.createPbxProj(); + let project = this.createPbxProj(projectData); - this.provideLaunchScreenIfMissing(); + this.provideLaunchScreenIfMissing(projectData); let resources = project.pbxGroupByName("Resources"); @@ -582,50 +597,49 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f this.$logger.trace("Images from Xcode project"); this.$logger.trace(xcodeProjectImages); - let appResourcesImages = this.$fs.readDirectory(this.getAppResourcesDestinationDirectoryPath()); + let appResourcesImages = this.$fs.readDirectory(this.getAppResourcesDestinationDirectoryPath(projectData)); this.$logger.trace("Current images from App_Resources"); this.$logger.trace(appResourcesImages); let imagesToAdd = _.difference(appResourcesImages, xcodeProjectImages); this.$logger.trace(`New images to add into xcode project: ${imagesToAdd.join(", ")}`); - _.each(imagesToAdd, image => project.addResourceFile(path.relative(this.platformData.projectRoot, path.join(this.getAppResourcesDestinationDirectoryPath(), image)))); + _.each(imagesToAdd, image => project.addResourceFile(path.relative(this.getPlatformData(projectData).projectRoot, path.join(this.getAppResourcesDestinationDirectoryPath(projectData), image)))); let imagesToRemove = _.difference(xcodeProjectImages, appResourcesImages); this.$logger.trace(`Images to remove from xcode project: ${imagesToRemove.join(", ")}`); - _.each(imagesToRemove, image => project.removeResourceFile(path.join(this.getAppResourcesDestinationDirectoryPath(), image))); + _.each(imagesToRemove, image => project.removeResourceFile(path.join(this.getAppResourcesDestinationDirectoryPath(projectData), image))); - this.savePbxProj(project); + this.savePbxProj(project, projectData); } } - public prepareAppResources(appResourcesDirectoryPath: string): void { - let platformFolder = path.join(appResourcesDirectoryPath, this.platformData.normalizedPlatformName); + public prepareAppResources(appResourcesDirectoryPath: string, projectData: IProjectData): void { + let platformFolder = path.join(appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName); let filterFile = (filename: string) => this.$fs.deleteFile(path.join(platformFolder, filename)); - filterFile(this.platformData.configurationFileName); + filterFile(this.getPlatformData(projectData).configurationFileName); - this.$fs.deleteDirectory(this.getAppResourcesDestinationDirectoryPath()); + this.$fs.deleteDirectory(this.getAppResourcesDestinationDirectoryPath(projectData)); } - 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); + public async processConfigurationFilesFromAppResources(release: boolean, projectData: IProjectData): Promise { + await this.mergeInfoPlists(projectData); + await this.mergeProjectXcconfigFiles(release, projectData); + for (let pluginData of await this.getAllInstalledPlugins(projectData)) { + await this.$pluginVariablesService.interpolatePluginVariables(pluginData, this.getPlatformData(projectData).configurationFilePath, projectData); } - this.$pluginVariablesService.interpolateAppIdentifier(this.platformData.configurationFilePath); + this.$pluginVariablesService.interpolateAppIdentifier(this.getPlatformData(projectData).configurationFilePath, projectData); } - private getInfoPlistPath(): string { - return this.$options.baseConfig || - path.join( - this.$projectData.projectDir, - constants.APP_FOLDER_NAME, - constants.APP_RESOURCES_FOLDER_NAME, - this.platformData.normalizedPlatformName, - this.platformData.configurationFileName - ); + private getInfoPlistPath(projectData: IProjectData): string { + return path.join( + projectData.projectDir, + constants.APP_FOLDER_NAME, + constants.APP_RESOURCES_FOLDER_NAME, + this.getPlatformData(projectData).normalizedPlatformName, + this.getPlatformData(projectData).configurationFileName + ); } public ensureConfigurationFileInAppResources(): void { @@ -633,16 +647,16 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } public async stopServices(): Promise { - return Promise.resolve({stderr: "", stdout: "", exitCode: 0}); + return { stderr: "", stdout: "", exitCode: 0 }; } public async cleanProject(projectRoot: string, options: string[]): Promise { return Promise.resolve(); } - 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); + private async mergeInfoPlists(projectData: IProjectData): Promise { + let projectDir = projectData.projectDir; + let infoPlistPath = path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.getPlatformData(projectData).normalizedPlatformName, this.getPlatformData(projectData).configurationFileName); this.ensureConfigurationFileInAppResources(); if (!this.$fs.exists(infoPlistPath)) { @@ -664,15 +678,15 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f }); }; - let allPlugins = await this.getAllInstalledPlugins(); + let allPlugins = await this.getAllInstalledPlugins(projectData); for (let plugin of allPlugins) { - let pluginInfoPlistPath = path.join(plugin.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME), this.platformData.configurationFileName); + let pluginInfoPlistPath = path.join(plugin.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME), this.getPlatformData(projectData).configurationFileName); makePatch(pluginInfoPlistPath); } makePatch(infoPlistPath); - if (this.$projectData.projectId) { + if (projectData.projectId) { session.patch({ name: "CFBundleIdentifier from package.json nativescript.id", read: () => @@ -681,7 +695,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f CFBundleIdentifier - ${ this.$projectData.projectId} + ${ projectData.projectId} ` }); @@ -689,28 +703,28 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f let plistContent = session.build(); - this.$logger.trace("Info.plist: Write to: " + this.platformData.configurationFilePath); - this.$fs.writeFile(this.platformData.configurationFilePath, plistContent); + this.$logger.trace("Info.plist: Write to: " + this.getPlatformData(projectData).configurationFilePath); + this.$fs.writeFile(this.getPlatformData(projectData).configurationFilePath, plistContent); } - private getAllInstalledPlugins(): Promise { - return (this.$injector.resolve("pluginsService")).getAllInstalledPlugins(); + private getAllInstalledPlugins(projectData: IProjectData): Promise { + return (this.$injector.resolve("pluginsService")).getAllInstalledPlugins(projectData); } - private get xcodeprojPath(): string { - return path.join(this.platformData.projectRoot, this.$projectData.projectName + IOSProjectService.XCODE_PROJECT_EXT_NAME); + private getXcodeprojPath(projectData: IProjectData): string { + return path.join(this.getPlatformData(projectData).projectRoot, projectData.projectName + IOSProjectService.XCODE_PROJECT_EXT_NAME); } - private get projectPodFilePath(): string { - return path.join(this.platformData.projectRoot, "Podfile"); + private getProjectPodFilePath(projectData: IProjectData): string { + return path.join(this.getPlatformData(projectData).projectRoot, "Podfile"); } - private get pluginsDebugXcconfigFilePath(): string { - return path.join(this.platformData.projectRoot, "plugins-debug.xcconfig"); + private getPluginsDebugXcconfigFilePath(projectData: IProjectData): string { + return path.join(this.getPlatformData(projectData).projectRoot, "plugins-debug.xcconfig"); } - private get pluginsReleaseXcconfigFilePath(): string { - return path.join(this.platformData.projectRoot, "plugins-release.xcconfig"); + private getPluginsReleaseXcconfigFilePath(projectData: IProjectData): string { + return path.join(this.getPlatformData(projectData).projectRoot, "plugins-release.xcconfig"); } private replace(name: string): string { @@ -721,66 +735,66 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f return name.replace(/\\\"/g, "\""); } - private getLibSubpathRelativeToProjectPath(targetPath: string): string { - let frameworkPath = path.relative(this.platformData.projectRoot, targetPath); + private getLibSubpathRelativeToProjectPath(targetPath: string, projectData: IProjectData): string { + let frameworkPath = path.relative(this.getPlatformData(projectData).projectRoot, targetPath); return frameworkPath; } - private get pbxProjPath(): string { - return path.join(this.xcodeprojPath, "project.pbxproj"); + private getPbxProjPath(projectData: IProjectData): string { + return path.join(this.getXcodeprojPath(projectData), "project.pbxproj"); } - private createPbxProj(): any { - let project = new xcode.project(this.pbxProjPath); + private createPbxProj(projectData: IProjectData): any { + let project = new xcode.project(this.getPbxProjPath(projectData)); project.parseSync(); return project; } - private savePbxProj(project: any): void { - return this.$fs.writeFile(this.pbxProjPath, project.writeSync()); + private savePbxProj(project: any, projectData: IProjectData): void { + return this.$fs.writeFile(this.getPbxProjPath(projectData), project.writeSync()); } - public async preparePluginNativeCode(pluginData: IPluginData, opts?: any): Promise { + public async preparePluginNativeCode(pluginData: IPluginData, projectData: IProjectData, opts?: any): Promise { let pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME); - await this.prepareFrameworks(pluginPlatformsFolderPath, pluginData); - await this.prepareStaticLibs(pluginPlatformsFolderPath, pluginData); - await this.prepareCocoapods(pluginPlatformsFolderPath); + await this.prepareFrameworks(pluginPlatformsFolderPath, pluginData, projectData); + await this.prepareStaticLibs(pluginPlatformsFolderPath, pluginData, projectData); + await this.prepareCocoapods(pluginPlatformsFolderPath, projectData); } - public async removePluginNativeCode(pluginData: IPluginData): Promise { + public async removePluginNativeCode(pluginData: IPluginData, projectData: IProjectData): Promise { let pluginPlatformsFolderPath = pluginData.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME); - this.removeFrameworks(pluginPlatformsFolderPath, pluginData); - this.removeStaticLibs(pluginPlatformsFolderPath, pluginData); - this.removeCocoapods(pluginPlatformsFolderPath); + this.removeFrameworks(pluginPlatformsFolderPath, pluginData, projectData); + this.removeStaticLibs(pluginPlatformsFolderPath, pluginData, projectData); + this.removeCocoapods(pluginPlatformsFolderPath, projectData); } - public async afterPrepareAllPlugins(): Promise { - if (this.$fs.exists(this.projectPodFilePath)) { - let projectPodfileContent = this.$fs.readText(this.projectPodFilePath); + public async afterPrepareAllPlugins(projectData: IProjectData): Promise { + if (this.$fs.exists(this.getProjectPodFilePath(projectData))) { + let projectPodfileContent = this.$fs.readText(this.getProjectPodFilePath(projectData)); 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); + this.$cocoapodsService.mergePodfileHookContent(IOSProjectService.PODFILE_POST_INSTALL_SECTION_NAME, this.getProjectPodFilePath(projectData)); } - let xcuserDataPath = path.join(this.xcodeprojPath, "xcuserdata"); - let sharedDataPath = path.join(this.xcodeprojPath, "xcshareddata"); + let xcuserDataPath = path.join(this.getXcodeprojPath(projectData), "xcuserdata"); + let sharedDataPath = path.join(this.getXcodeprojPath(projectData), "xcshareddata"); if (!this.$fs.exists(xcuserDataPath) && !this.$fs.exists(sharedDataPath)) { this.$logger.info("Creating project scheme..."); await this.checkIfXcodeprojIsRequired(); - 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 }); + let createSchemeRubyScript = `ruby -e "require 'xcodeproj'; xcproj = Xcodeproj::Project.open('${projectData.projectName}.xcodeproj'); xcproj.recreate_user_schemes; xcproj.save"`; + await this.$childProcess.exec(createSchemeRubyScript, { cwd: this.getPlatformData(projectData).projectRoot }); } - await this.executePodInstall(); + await this.executePodInstall(projectData); } } @@ -793,8 +807,8 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f return this.getAllNativeLibrariesForPlugin(pluginData, IOSProjectService.IOS_PLATFORM_NAME, filterCallback); }; - private buildPathToCurrentXcodeProjectFile(): string { - return path.join(this.$projectData.platformsDir, "ios", `${this.$projectData.projectName}.xcodeproj`, "project.pbxproj"); + private buildPathToCurrentXcodeProjectFile(projectData: IProjectData): string { + return path.join(projectData.platformsDir, "ios", `${projectData.projectName}.xcodeproj`, "project.pbxproj"); } private buildPathToNewXcodeProjectFile(newModulesDir: string): string { @@ -829,20 +843,20 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f }); } - private replaceFileContent(file: string): void { + private replaceFileContent(file: string, projectData: IProjectData): void { let fileContent = this.$fs.readText(file); - let replacedContent = helpers.stringReplaceAll(fileContent, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER, this.$projectData.projectName); + let replacedContent = helpers.stringReplaceAll(fileContent, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER, projectData.projectName); this.$fs.writeFile(file, replacedContent); } - private replaceFileName(fileNamePart: string, fileRootLocation: string): void { + private replaceFileName(fileNamePart: string, fileRootLocation: string, projectData: IProjectData): void { let oldFileName = IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + fileNamePart; - let newFileName = this.$projectData.projectName + fileNamePart; + let newFileName = projectData.projectName + fileNamePart; this.$fs.rename(path.join(fileRootLocation, oldFileName), path.join(fileRootLocation, newFileName)); } - private async executePodInstall(): Promise { + private async executePodInstall(projectData: IProjectData): Promise { // Check availability try { await this.$childProcess.exec("gem which cocoapods"); @@ -855,7 +869,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f 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'] }); + let childProcess = await this.$childProcess.spawnFromEvent(podTool, ["install"], "close", { cwd: this.getPlatformData(projectData).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) => { @@ -873,33 +887,33 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } if ((await this.$xcprojService.getXcprojInfo()).shouldUseXcproj) { - await this.$childProcess.spawnFromEvent("xcproj", ["--project", this.xcodeprojPath, "touch"], "close"); + await this.$childProcess.spawnFromEvent("xcproj", ["--project", this.getXcodeprojPath(projectData), "touch"], "close"); } return childProcess; } - private async prepareFrameworks(pluginPlatformsFolderPath: string, pluginData: IPluginData): Promise { + private async prepareFrameworks(pluginPlatformsFolderPath: string, pluginData: IPluginData, projectData: IProjectData): Promise { for (let fileName of this.getAllLibsForPluginWithFileExtension(pluginData, ".framework")) { - await this.addFramework(path.join(pluginPlatformsFolderPath, fileName)); + await this.addFramework(path.join(pluginPlatformsFolderPath, fileName), projectData); } } - private async prepareStaticLibs(pluginPlatformsFolderPath: string, pluginData: IPluginData): Promise { + private async prepareStaticLibs(pluginPlatformsFolderPath: string, pluginData: IPluginData, projectData: IProjectData): Promise { for (let fileName of this.getAllLibsForPluginWithFileExtension(pluginData, ".a")) { - await this.addStaticLibrary(path.join(pluginPlatformsFolderPath, fileName)); + await this.addStaticLibrary(path.join(pluginPlatformsFolderPath, fileName), projectData); } } - private async prepareCocoapods(pluginPlatformsFolderPath: string, opts?: any): Promise { + private async prepareCocoapods(pluginPlatformsFolderPath: string, projectData: IProjectData, opts?: any): Promise { let pluginPodFilePath = path.join(pluginPlatformsFolderPath, "Podfile"); if (this.$fs.exists(pluginPodFilePath)) { let pluginPodFileContent = this.$fs.readText(pluginPodFilePath), pluginPodFilePreparedContent = this.buildPodfileContent(pluginPodFilePath, pluginPodFileContent), - projectPodFileContent = this.$fs.exists(this.projectPodFilePath) ? this.$fs.readText(this.projectPodFilePath) : ""; + projectPodFileContent = this.$fs.exists(this.getProjectPodFilePath(projectData)) ? this.$fs.readText(this.getProjectPodFilePath(projectData)) : ""; if (!~projectPodFileContent.indexOf(pluginPodFilePreparedContent)) { - let podFileHeader = this.$cocoapodsService.getPodfileHeader(this.$projectData.projectName), + let podFileHeader = this.$cocoapodsService.getPodfileHeader(projectData.projectName), podFileFooter = this.$cocoapodsService.getPodfileFooter(); if (_.startsWith(projectPodFileContent, podFileHeader)) { @@ -911,56 +925,56 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } let contentToWrite = `${podFileHeader}${projectPodFileContent}${pluginPodFilePreparedContent}${podFileFooter}`; - this.$fs.writeFile(this.projectPodFilePath, contentToWrite); + this.$fs.writeFile(this.getProjectPodFilePath(projectData), contentToWrite); - let project = this.createPbxProj(); - this.savePbxProj(project); + let project = this.createPbxProj(projectData); + this.savePbxProj(project, projectData); } } if (opts && opts.executePodInstall && this.$fs.exists(pluginPodFilePath)) { - await this.executePodInstall(); + await this.executePodInstall(projectData); } } - private removeFrameworks(pluginPlatformsFolderPath: string, pluginData: IPluginData): void { - let project = this.createPbxProj(); + private removeFrameworks(pluginPlatformsFolderPath: string, pluginData: IPluginData, projectData: IProjectData): void { + let project = this.createPbxProj(projectData); _.each(this.getAllLibsForPluginWithFileExtension(pluginData, ".framework"), fileName => { - let relativeFrameworkPath = this.getLibSubpathRelativeToProjectPath(fileName); + let relativeFrameworkPath = this.getLibSubpathRelativeToProjectPath(fileName, projectData); project.removeFramework(relativeFrameworkPath, { customFramework: true, embed: true }); }); - this.savePbxProj(project); + this.savePbxProj(project, projectData); } - private removeStaticLibs(pluginPlatformsFolderPath: string, pluginData: IPluginData): void { - let project = this.createPbxProj(); + private removeStaticLibs(pluginPlatformsFolderPath: string, pluginData: IPluginData, projectData: IProjectData): void { + let project = this.createPbxProj(projectData); _.each(this.getAllLibsForPluginWithFileExtension(pluginData, ".a"), fileName => { let staticLibPath = path.join(pluginPlatformsFolderPath, fileName); - let relativeStaticLibPath = this.getLibSubpathRelativeToProjectPath(path.basename(staticLibPath)); + let relativeStaticLibPath = this.getLibSubpathRelativeToProjectPath(path.basename(staticLibPath), projectData); project.removeFramework(relativeStaticLibPath); let headersSubpath = path.join("include", path.basename(staticLibPath, ".a")); - let relativeHeaderSearchPath = path.join(this.getLibSubpathRelativeToProjectPath(headersSubpath)); + let relativeHeaderSearchPath = path.join(this.getLibSubpathRelativeToProjectPath(headersSubpath, projectData)); project.removeFromHeaderSearchPaths({ relativePath: relativeHeaderSearchPath }); }); - this.savePbxProj(project); + this.savePbxProj(project, projectData); } - private removeCocoapods(pluginPlatformsFolderPath: string): void { + private removeCocoapods(pluginPlatformsFolderPath: string, projectData: IProjectData): void { let pluginPodFilePath = path.join(pluginPlatformsFolderPath, "Podfile"); - if (this.$fs.exists(pluginPodFilePath) && this.$fs.exists(this.projectPodFilePath)) { + if (this.$fs.exists(pluginPodFilePath) && this.$fs.exists(this.getProjectPodFilePath(projectData))) { let pluginPodFileContent = this.$fs.readText(pluginPodFilePath); - let projectPodFileContent = this.$fs.readText(this.projectPodFilePath); + let projectPodFileContent = this.$fs.readText(this.getProjectPodFilePath(projectData)); let contentToRemove = this.buildPodfileContent(pluginPodFilePath, pluginPodFileContent); projectPodFileContent = helpers.stringReplaceAll(projectPodFileContent, contentToRemove, ""); - if (projectPodFileContent.trim() === `use_frameworks!${os.EOL}${os.EOL}target "${this.$projectData.projectName}" do${os.EOL}${os.EOL}end`) { - this.$fs.deleteFile(this.projectPodFilePath); + if (projectPodFileContent.trim() === `use_frameworks!${os.EOL}${os.EOL}target "${projectData.projectName}" do${os.EOL}${os.EOL}end`) { + this.$fs.deleteFile(this.getProjectPodFilePath(projectData)); } else { - this.$fs.writeFile(this.projectPodFilePath, projectPodFileContent); + this.$fs.writeFile(this.getProjectPodFilePath(projectData), projectPodFileContent); } } } @@ -997,30 +1011,30 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f await this.$childProcess.exec(`ruby -e "${mergeScript}"`); } - private async mergeProjectXcconfigFiles(): Promise { - this.$fs.deleteFile(this.$options.release ? this.pluginsReleaseXcconfigFilePath : this.pluginsDebugXcconfigFilePath); + private async mergeProjectXcconfigFiles(release: boolean, projectData: IProjectData): Promise { + this.$fs.deleteFile(release ? this.getPluginsReleaseXcconfigFilePath(projectData) : this.getPluginsDebugXcconfigFilePath(projectData)); - let allPlugins: IPluginData[] = await (this.$injector.resolve("pluginsService")).getAllInstalledPlugins(); + let allPlugins: IPluginData[] = await (this.$injector.resolve("pluginsService")).getAllInstalledPlugins(projectData); 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); + await this.mergeXcconfigFiles(pluginXcconfigFilePath, release ? this.getPluginsReleaseXcconfigFilePath(projectData) : this.getPluginsDebugXcconfigFilePath(projectData)); } } - let appResourcesXcconfigPath = path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.platformData.normalizedPlatformName, "build.xcconfig"); + let appResourcesXcconfigPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); if (this.$fs.exists(appResourcesXcconfigPath)) { - await this.mergeXcconfigFiles(appResourcesXcconfigPath, this.$options.release ? this.pluginsReleaseXcconfigFilePath : this.pluginsDebugXcconfigFilePath); + await this.mergeXcconfigFiles(appResourcesXcconfigPath, release ? this.getPluginsReleaseXcconfigFilePath(projectData) : this.getPluginsDebugXcconfigFilePath(projectData)); } - let podFilesRootDirName = path.join("Pods", "Target Support Files", `Pods-${this.$projectData.projectName}`); - let podFolder = path.join(this.platformData.projectRoot, podFilesRootDirName); + let podFilesRootDirName = path.join("Pods", "Target Support Files", `Pods-${projectData.projectName}`); + let podFolder = path.join(this.getPlatformData(projectData).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); + if (release) { + await this.mergeXcconfigFiles(path.join(this.getPlatformData(projectData).projectRoot, podFilesRootDirName, `Pods-${projectData.projectName}.release.xcconfig`), this.getPluginsReleaseXcconfigFilePath(projectData)); } else { - await this.mergeXcconfigFiles(path.join(this.platformData.projectRoot, podFilesRootDirName, `Pods-${this.$projectData.projectName}.debug.xcconfig`), this.pluginsDebugXcconfigFilePath); + await this.mergeXcconfigFiles(path.join(this.getPlatformData(projectData).projectRoot, podFilesRootDirName, `Pods-${projectData.projectName}.debug.xcconfig`), this.getPluginsDebugXcconfigFilePath(projectData)); } } } @@ -1091,8 +1105,8 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f return null; } - private readXCConfig(flag: string): string { - let xcconfigFile = path.join(this.$projectData.appResourcesDirectoryPath, this.platformData.normalizedPlatformName, "build.xcconfig"); + private readXCConfig(flag: string, projectData: IProjectData): string { + let xcconfigFile = path.join(projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); if (this.$fs.exists(xcconfigFile)) { let text = this.$fs.readText(xcconfigFile); let teamId: string; @@ -1110,7 +1124,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } } - let fileName = path.join(this.platformData.projectRoot, "teamid"); + let fileName = path.join(this.getPlatformData(projectData).projectRoot, "teamid"); if (this.$fs.exists(fileName)) { return this.$fs.readText(fileName); } @@ -1118,25 +1132,20 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f return null; } - private readTeamId(): string { - return this.readXCConfig("DEVELOPMENT_TEAM"); + private readTeamId(projectData: IProjectData): string { + return this.readXCConfig("DEVELOPMENT_TEAM", projectData); } - private readXCConfigProvisioningProfile(): string { - return this.readXCConfig("PROVISIONING_PROFILE"); + private readXCConfigProvisioningProfile(projectData: IProjectData): string { + return this.readXCConfig("PROVISIONING_PROFILE", projectData); } - private readXCConfigProvisioningProfileForIPhoneOs(): string { - return this.readXCConfig("PROVISIONING_PROFILE[sdk=iphoneos*]"); + private readXCConfigProvisioningProfileForIPhoneOs(projectData: IProjectData): string { + return this.readXCConfig("PROVISIONING_PROFILE[sdk=iphoneos*]", projectData); } - private async getDevelopmentTeam(): Promise { - let teamId: string; - if (this.$options.teamId) { - teamId = this.$options.teamId; - } else { - teamId = this.readTeamId(); - } + private async getDevelopmentTeam(projectData: IProjectData, teamId?: string): Promise { + teamId = teamId || this.readTeamId(projectData); if (!teamId) { let teams = this.getDevelopmentTeams(); @@ -1161,11 +1170,11 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f 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"); + let xcconfigFile = path.join(projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); this.$fs.appendFile(xcconfigFile, "\nDEVELOPMENT_TEAM = " + teamId + "\n"); break; case 1: - this.$fs.writeFile(path.join(this.platformData.projectRoot, "teamid"), teamId); + this.$fs.writeFile(path.join(this.getPlatformData(projectData).projectRoot, "teamid"), teamId); break; default: break; diff --git a/lib/services/ios-provision-service.ts b/lib/services/ios-provision-service.ts index c7d65dd594..3ab4522a53 100644 --- a/lib/services/ios-provision-service.ts +++ b/lib/services/ios-provision-service.ts @@ -11,20 +11,19 @@ export class IOSProvisionService { private $logger: ILogger, private $options: IOptions, private $devicesService: Mobile.IDevicesService, - private $projectData: IProjectData, private $mobileHelper: Mobile.IMobileHelper) { } - public async pick(uuidOrName: string): Promise { - const match = (await this.queryProvisioningProfilesAndDevices()).match; + public async pick(uuidOrName: string, projectId: string): Promise { + const match = (await this.queryProvisioningProfilesAndDevices(projectId)).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 async list(): Promise { - const data = await this.queryProvisioningProfilesAndDevices(); + public async list(projectId: string): Promise { + const data = await this.queryProvisioningProfilesAndDevices(projectId); const devices = data.devices; const match = data.match; @@ -63,14 +62,14 @@ export class IOSProvisionService { } - private async queryProvisioningProfilesAndDevices(): Promise<{ devices: string[], match: mobileprovision.provision.Result }> { + private async queryProvisioningProfilesAndDevices(projectId: string): 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 + AppId: projectId }; let devices: string[] = []; diff --git a/lib/services/livesync/android-device-livesync-service.ts b/lib/services/livesync/android-device-livesync-service.ts index 75034bc919..238fbf7945 100644 --- a/lib/services/livesync/android-device-livesync-service.ts +++ b/lib/services/livesync/android-device-livesync-service.ts @@ -4,14 +4,13 @@ import * as helpers from "../../common/helpers"; import * as path from "path"; import * as net from "net"; -class AndroidLiveSyncService implements IDeviceLiveSyncService { +class AndroidLiveSyncService implements INativeScriptDeviceLiveSyncService { private static BACKEND_PORT = 18182; private device: Mobile.IAndroidDevice; constructor(_device: Mobile.IDevice, private $mobileHelper: Mobile.IMobileHelper, private $injector: IInjector, - private $projectData: IProjectData, private $androidDebugService: IDebugService, private $liveSyncProvider: ILiveSyncProvider) { this.device = (_device); @@ -66,7 +65,7 @@ class AndroidLiveSyncService implements IDeviceLiveSyncService { } } - public async removeFiles(appIdentifier: string, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise { + public async removeFiles(appIdentifier: string, localToDevicePaths: Mobile.ILocalToDevicePathData[], projectId: string): Promise { let deviceRootPath = this.getDeviceRootPath(appIdentifier); _.each(localToDevicePaths, localToDevicePathData => { let relativeUnixPath = _.trimStart(helpers.fromWindowsRelativePathToUnix(localToDevicePathData.getRelativeToProjectBasePath()), "/"); @@ -74,11 +73,11 @@ class AndroidLiveSyncService implements IDeviceLiveSyncService { this.device.adb.executeShellCommand(["mkdir", "-p", path.dirname(deviceFilePath), "&& await ", "touch", deviceFilePath]); }); - await this.deviceHashService.removeHashes(localToDevicePaths); + await this.getDeviceHashService(projectId).removeHashes(localToDevicePaths); } - public async afterInstallApplicationAction(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise { - await this.deviceHashService.uploadHashFileToDevice(localToDevicePaths); + public async afterInstallApplicationAction(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], projectId: string): Promise { + await this.getDeviceHashService(projectId).uploadHashFileToDevice(localToDevicePaths); return false; } @@ -114,10 +113,10 @@ class AndroidLiveSyncService implements IDeviceLiveSyncService { } private _deviceHashService: Mobile.IAndroidDeviceHashService; - private get deviceHashService(): Mobile.IAndroidDeviceHashService { + private getDeviceHashService(projectId: string): Mobile.IAndroidDeviceHashService { if (!this._deviceHashService) { let adb = this.$injector.resolve(DeviceAndroidDebugBridge, { identifier: this.device.deviceInfo.identifier }); - this._deviceHashService = this.$injector.resolve(AndroidDeviceHashService, { adb: adb, appIdentifier: this.$projectData.projectId }); + this._deviceHashService = this.$injector.resolve(AndroidDeviceHashService, { adb: adb, appIdentifier: projectId }); } return this._deviceHashService; diff --git a/lib/services/livesync/ios-device-livesync-service.ts b/lib/services/livesync/ios-device-livesync-service.ts index 8dc6831e69..1ba5c6ae7a 100644 --- a/lib/services/livesync/ios-device-livesync-service.ts +++ b/lib/services/livesync/ios-device-livesync-service.ts @@ -5,7 +5,7 @@ import * as net from "net"; let currentPageReloadId = 0; -class IOSLiveSyncService implements IDeviceLiveSyncService { +class IOSLiveSyncService implements INativeScriptDeviceLiveSyncService { private static BACKEND_PORT = 18181; private socket: net.Socket; private device: Mobile.IiOSDevice; @@ -33,13 +33,13 @@ class IOSLiveSyncService implements IDeviceLiveSyncService { return this.$options.watch; } - private async setupSocketIfNeeded(): Promise { + private async setupSocketIfNeeded(projectId: string): Promise { if (this.socket) { return true; } if (this.device.isEmulator) { - await this.$iOSEmulatorServices.postDarwinNotification(this.$iOSNotification.attachRequest); + await this.$iOSEmulatorServices.postDarwinNotification(this.$iOSNotification.getAttachRequest(projectId)); try { this.socket = await helpers.connectEventuallyUntilTimeout(() => net.connect(IOSLiveSyncService.BACKEND_PORT), 5000); } catch (e) { @@ -48,7 +48,7 @@ class IOSLiveSyncService implements IDeviceLiveSyncService { } } else { let timeout = 9000; - await this.$iOSSocketRequestExecutor.executeAttachRequest(this.device, timeout); + await this.$iOSSocketRequestExecutor.executeAttachRequest(this.device, timeout, projectId); this.socket = this.device.connectToPort(IOSLiveSyncService.BACKEND_PORT); } @@ -61,7 +61,7 @@ class IOSLiveSyncService implements IDeviceLiveSyncService { await Promise.all(_.map(localToDevicePaths, localToDevicePathData => this.device.fileSystem.deleteFile(localToDevicePathData.getDevicePath(), appIdentifier))); } - public async refreshApplication(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], forceExecuteFullSync: boolean): Promise { + public async refreshApplication(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], forceExecuteFullSync: boolean, projectId: string): Promise { if (forceExecuteFullSync) { await this.restartApplication(deviceAppData); return; @@ -79,7 +79,7 @@ class IOSLiveSyncService implements IDeviceLiveSyncService { return; } - if (await this.setupSocketIfNeeded()) { + if (await this.setupSocketIfNeeded(projectId)) { this.liveEdit(scriptFiles); await this.reloadPage(deviceAppData, otherFiles); } else { diff --git a/lib/services/livesync/livesync-service.ts b/lib/services/livesync/livesync-service.ts index 1e6e2389ef..17480a4d6f 100644 --- a/lib/services/livesync/livesync-service.ts +++ b/lib/services/livesync/livesync-service.ts @@ -13,7 +13,6 @@ class LiveSyncService implements ILiveSyncService { private $errors: IErrors, private $platformsData: IPlatformsData, private $platformService: IPlatformService, - private $projectData: IProjectData, private $projectDataService: IProjectDataService, private $prompter: IPrompter, private $injector: IInjector, @@ -25,14 +24,14 @@ class LiveSyncService implements ILiveSyncService { private $hooksService: IHooksService, private $processService: IProcessService) { } - private async ensureAndroidFrameworkVersion(platformData: IPlatformData): Promise { // TODO: this can be moved inside command or canExecute function - const frameworkVersion = this.$projectDataService.getNSValue(this.$projectData.projectDir, platformData.frameworkPackageName).version; + private async ensureAndroidFrameworkVersion(platformData: IPlatformData, projectData: IProjectData): Promise { // TODO: this can be moved inside command or canExecute function + const frameworkVersion = this.$projectDataService.getNSValue(projectData.projectDir, 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()]); + await this.$platformService.updatePlatforms([this.$devicePlatformsConstants.Android.toLowerCase()], this.$options.platformTemplate, projectData); } else { return; } @@ -44,7 +43,7 @@ class LiveSyncService implements ILiveSyncService { return this._isInitialized; } - public async liveSync(platform: string, applicationReloadAction?: (deviceAppData: Mobile.IDeviceAppData) => Promise): Promise { + public async liveSync(platform: string, projectData: IProjectData, applicationReloadAction?: (deviceAppData: Mobile.IDeviceAppData) => Promise): Promise { if (this.$options.justlaunch) { this.$options.watch = false; } @@ -52,22 +51,22 @@ class LiveSyncService implements ILiveSyncService { if (platform) { await this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }); - liveSyncData.push(await this.prepareLiveSyncData(platform)); + liveSyncData.push(await this.prepareLiveSyncData(platform, projectData)); } 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)); + liveSyncData.push(await this.prepareLiveSyncData(platform, projectData)); } else { await this.$devicesService.initialize({ skipInferPlatform: true }); await this.$devicesService.stopDeviceDetectionInterval(); - for (let installedPlatform of this.$platformService.getInstalledPlatforms()) { + for (let installedPlatform of this.$platformService.getInstalledPlatforms(projectData)) { if (this.$devicesService.getDevicesForPlatform(installedPlatform).length === 0) { await this.$devicesService.startEmulator(installedPlatform); } - liveSyncData.push(await this.prepareLiveSyncData(installedPlatform)); + liveSyncData.push(await this.prepareLiveSyncData(installedPlatform, projectData)); } } @@ -77,22 +76,22 @@ class LiveSyncService implements ILiveSyncService { this._isInitialized = true; // If we want before-prepare hooks to work properly, this should be set after preparePlatform function - await this.liveSyncCore(liveSyncData, applicationReloadAction); + await this.liveSyncCore(liveSyncData, applicationReloadAction, projectData); } - private async prepareLiveSyncData(platform: string): Promise { + private async prepareLiveSyncData(platform: string, projectData: IProjectData): Promise { platform = platform || this.$devicesService.platform; - let platformData = this.$platformsData.getPlatformData(platform.toLowerCase()); + let platformData = this.$platformsData.getPlatformData(platform.toLowerCase(), projectData); if (this.$mobileHelper.isAndroidPlatform(platform)) { - await this.ensureAndroidFrameworkVersion(platformData); + await this.ensureAndroidFrameworkVersion(platformData, projectData); } let liveSyncData: ILiveSyncData = { platform: platform, - appIdentifier: this.$projectData.projectId, + appIdentifier: projectData.projectId, projectFilesPath: path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME), - syncWorkingDirectory: this.$projectData.projectDir, + syncWorkingDirectory: projectData.projectDir, excludedProjectDirsAndFiles: this.$options.release ? constants.LIVESYNC_EXCLUDED_FILE_PATTERNS : [] }; @@ -100,29 +99,29 @@ class LiveSyncService implements ILiveSyncService { } @helpers.hook('livesync') - private async liveSyncCore(liveSyncData: ILiveSyncData[], applicationReloadAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise): Promise { - await this.$platformService.trackProjectType(); + private async liveSyncCore(liveSyncData: ILiveSyncData[], applicationReloadAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise, projectData: IProjectData): Promise { + await this.$platformService.trackProjectType(projectData); let watchForChangeActions: ((event: string, filePath: string, dispatcher: IFutureDispatcher) => Promise)[] = []; 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)); + service.partialSync(event, filePath, dispatcher, applicationReloadAction, projectData)); - await service.fullSync(applicationReloadAction); + await service.fullSync(projectData, applicationReloadAction); } if (this.$options.watch && !this.$options.justlaunch) { await this.$hooksService.executeBeforeHooks('watch'); - await this.partialSync(liveSyncData[0].syncWorkingDirectory, watchForChangeActions); + await this.partialSync(liveSyncData[0].syncWorkingDirectory, watchForChangeActions, projectData); } } - private partialSync(syncWorkingDirectory: string, onChangedActions: ((event: string, filePath: string, dispatcher: IFutureDispatcher) => Promise)[]): void { + private partialSync(syncWorkingDirectory: string, onChangedActions: ((event: string, filePath: string, dispatcher: IFutureDispatcher) => Promise)[], projectData: IProjectData): void { let that = this; let dependenciesBuilder = this.$injector.resolve(NodeModulesDependenciesBuilder, {}); - let productionDependencies = dependenciesBuilder.getProductionDependencies(this.$projectData.projectDir); + let productionDependencies = dependenciesBuilder.getProductionDependencies(projectData.projectDir); let pattern = ["app"]; if (this.$options.syncAllFiles) { diff --git a/lib/services/livesync/platform-livesync-service.ts b/lib/services/livesync/platform-livesync-service.ts index 316b2f269b..92fa192a2f 100644 --- a/lib/services/livesync/platform-livesync-service.ts +++ b/lib/services/livesync/platform-livesync-service.ts @@ -28,7 +28,7 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe this.liveSyncData = _liveSyncData; } - public async fullSync(postAction?: (deviceAppData: Mobile.IDeviceAppData) => Promise): Promise { + public async fullSync(projectData: IProjectData, postAction?: (deviceAppData: Mobile.IDeviceAppData) => Promise): Promise { let appIdentifier = this.liveSyncData.appIdentifier; let platform = this.liveSyncData.platform; let projectFilesPath = this.liveSyncData.projectFilesPath; @@ -36,10 +36,10 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe 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)) { + if (await this.shouldTransferAllFiles(platform, deviceAppData, projectData)) { 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), appIdentifier); + await device.fileSystem.putFile(this.$projectChangesService.getPrepareInfoFilePath(platform, projectData), await this.getLiveSyncInfoFilePath(deviceAppData), appIdentifier); } if (postAction) { @@ -48,22 +48,22 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe return; } - await this.refreshApplication(deviceAppData, localToDevicePaths, true); + await this.refreshApplication(deviceAppData, localToDevicePaths, true, projectData.projectId); await this.finishLivesync(deviceAppData); }; await this.$devicesService.execute(action, canExecute); } - public async partialSync(event: string, filePath: string, dispatcher: IFutureDispatcher, afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise): Promise { + public async partialSync(event: string, filePath: string, dispatcher: IFutureDispatcher, afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise, projectData: IProjectData): 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; } if (event === "add" || event === "addDir" || event === "change") { - this.batchSync(filePath, dispatcher, afterFileSyncAction); + this.batchSync(filePath, dispatcher, afterFileSyncAction, projectData); } else if (event === "unlink" || event === "unlinkDir") { - await this.syncRemovedFile(filePath, afterFileSyncAction); + await this.syncRemovedFile(filePath, afterFileSyncAction, projectData); } } @@ -75,10 +75,10 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe return isTheSamePlatformAction; } - public async refreshApplication(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], isFullSync: boolean): Promise { + public async refreshApplication(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[], isFullSync: boolean, projectId: string): Promise { let deviceLiveSyncService = this.resolveDeviceSpecificLiveSyncService(deviceAppData.device.deviceInfo.platform, deviceAppData.device); this.$logger.info("Refreshing application..."); - await deviceLiveSyncService.refreshApplication(deviceAppData, localToDevicePaths, isFullSync); + await deviceLiveSyncService.refreshApplication(deviceAppData, localToDevicePaths, isFullSync, projectId); } protected async finishLivesync(deviceAppData: Mobile.IDeviceAppData): Promise { @@ -97,7 +97,7 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe this.logFilesSyncInformation(localToDevicePaths, "Successfully transferred %s.", this.$logger.info); } - protected resolveDeviceSpecificLiveSyncService(platform: string, device: Mobile.IDevice): IDeviceLiveSyncService { + protected resolveDeviceSpecificLiveSyncService(platform: string, device: Mobile.IDevice): INativeScriptDeviceLiveSyncService { return this.$injector.resolve(this.$liveSyncProvider.deviceSpecificLiveSyncServices[platform.toLowerCase()], { _device: device }); } @@ -113,7 +113,7 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe return isFileExcluded; } - private batchSync(filePath: string, dispatcher: IFutureDispatcher, afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise): void { + private batchSync(filePath: string, dispatcher: IFutureDispatcher, afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise, projectData: IProjectData): void { let platformBatch: ISyncBatch = this.batch[this.liveSyncData.platform]; if (!platformBatch || !platformBatch.syncPending) { let done = async () => { @@ -122,10 +122,11 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe for (let platform in this.batch) { let batch = this.batch[platform]; await batch.syncFiles(async (filesToSync: string[]) => { - await this.$platformService.preparePlatform(this.liveSyncData.platform); + const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; + await this.$platformService.preparePlatform(this.liveSyncData.platform, appFilesUpdaterOptions, this.$options.platformTemplate, projectData, this.$options.provision); 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); + let action = this.getSyncAction(filesToSync, deviceFileAction, afterFileSyncAction, projectData); await this.$devicesService.execute(action, canExecute); }); } @@ -143,33 +144,41 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe } private async syncRemovedFile(filePath: string, - afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise): Promise { + afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise, projectData: IProjectData): 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); + return deviceLiveSyncService.removeFiles(this.liveSyncData.appIdentifier, localToDevicePaths, projectData.projectId); }; let canExecute = this.getCanExecuteAction(this.liveSyncData.platform, this.liveSyncData.appIdentifier); - let action = this.getSyncAction([filePath], deviceFilesAction, afterFileSyncAction); + let action = this.getSyncAction([filePath], deviceFilesAction, afterFileSyncAction, projectData); await this.$devicesService.execute(action, canExecute); } private getSyncAction( filesToSync: string[], fileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise, - afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise): (device: Mobile.IDevice) => Promise { + afterFileSyncAction: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise, + projectData: IProjectData): (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 buildConfig: IiOSBuildConfig = { + buildForDevice: !device.isEmulator, + projectDir: this.$options.path, + release: this.$options.release, + teamId: this.$options.teamId, + device: this.$options.device, + provision: this.$options.provision, + }; let platform = device.deviceInfo.platform; - if (this.$platformService.shouldBuild(platform, buildConfig)) { - await this.$platformService.buildPlatform(platform, buildConfig); + if (this.$platformService.shouldBuild(platform, projectData, buildConfig)) { + await this.$platformService.buildPlatform(platform, buildConfig, projectData); } - await this.$platformService.installApplication(device); + await this.$platformService.installApplication(device, buildConfig, projectData); deviceAppData = this.$deviceAppDataFactory.create(this.liveSyncData.appIdentifier, this.$mobileHelper.normalizePlatformName(this.liveSyncData.platform), device); isFullSync = true; } else { @@ -193,10 +202,10 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe } if (!afterFileSyncAction) { - await this.refreshApplication(deviceAppData, localToDevicePaths, isFullSync); + await this.refreshApplication(deviceAppData, localToDevicePaths, isFullSync, projectData.projectId); } - await device.fileSystem.putFile(this.$projectChangesService.getPrepareInfoFilePath(device.deviceInfo.platform), await this.getLiveSyncInfoFilePath(deviceAppData), this.liveSyncData.appIdentifier); + await device.fileSystem.putFile(this.$projectChangesService.getPrepareInfoFilePath(device.deviceInfo.platform, projectData), await this.getLiveSyncInfoFilePath(deviceAppData), this.liveSyncData.appIdentifier); await this.finishLivesync(deviceAppData); @@ -208,14 +217,14 @@ export abstract class PlatformLiveSyncServiceBase implements IPlatformLiveSyncSe return action; } - private async shouldTransferAllFiles(platform: string, deviceAppData: Mobile.IDeviceAppData): Promise { + private async shouldTransferAllFiles(platform: string, deviceAppData: Mobile.IDeviceAppData, projectData: IProjectData): Promise { try { if (this.$options.clean) { return false; } - let fileText = await this.$platformService.readFile(deviceAppData.device, await this.getLiveSyncInfoFilePath(deviceAppData)); + let fileText = await this.$platformService.readFile(deviceAppData.device, await this.getLiveSyncInfoFilePath(deviceAppData), projectData); let remoteLivesyncInfo: IPrepareInfo = JSON.parse(fileText); - let localPrepareInfo = this.$projectChangesService.getPrepareInfo(platform); + let localPrepareInfo = this.$projectChangesService.getPrepareInfo(platform, projectData); return remoteLivesyncInfo.time !== localPrepareInfo.time; } catch (e) { return true; diff --git a/lib/services/local-build-service.ts b/lib/services/local-build-service.ts new file mode 100644 index 0000000000..bfb18bf9bb --- /dev/null +++ b/lib/services/local-build-service.ts @@ -0,0 +1,24 @@ +// import { exportedPromise } from "../common/decorators"; +import { EventEmitter } from "events"; +import { BUILD_OUTPUT_EVENT_NAME } from "../constants"; + +export class LocalBuildService extends EventEmitter { + constructor(private $projectData: IProjectData, + private $platformService: IPlatformService) { + super(); + } + + public async build(platform: string, platformBuildOptions: IPlatformBuildData, platformTemplate?: string): Promise { + this.$projectData.initializeProjectData(platformBuildOptions.projectDir); + await this.$platformService.preparePlatform(platform, platformBuildOptions, platformTemplate, this.$projectData, undefined); + this.$platformService.on(BUILD_OUTPUT_EVENT_NAME, (data: any) => { + data.projectDir = platformBuildOptions.projectDir; + this.emit(BUILD_OUTPUT_EVENT_NAME, data); + }); + platformBuildOptions.buildOutputStdio = "pipe"; + await this.$platformService.buildPlatform(platform, platformBuildOptions, this.$projectData); + return this.$platformService.lastOutputPath(platform, { isForDevice: platformBuildOptions.buildForDevice }, this.$projectData); + } +} + +$injector.register("localBuildService", LocalBuildService); diff --git a/lib/services/platform-project-service-base.ts b/lib/services/platform-project-service-base.ts index c7b57de210..b2cb7f7805 100644 --- a/lib/services/platform-project-service-base.ts +++ b/lib/services/platform-project-service-base.ts @@ -1,7 +1,9 @@ -export class PlatformProjectServiceBase implements IPlatformProjectServiceBase { +import { EventEmitter } from "events"; + +export class PlatformProjectServiceBase extends EventEmitter implements IPlatformProjectServiceBase { constructor(protected $fs: IFileSystem, - protected $projectData: IProjectData, protected $projectDataService: IProjectDataService) { + super(); } public getPluginPlatformsFolderPath(pluginData: IPluginData, platform: string): string { @@ -22,7 +24,7 @@ export class PlatformProjectServiceBase implements IPlatformProjectServiceBase { return nativeLibraries; } - protected getFrameworkVersion(runtimePackageName: string): string { - return this.$projectDataService.getNSValue(this.$projectData.projectDir, runtimePackageName).version; + protected getFrameworkVersion(runtimePackageName: string, projectDir: string): string { + return this.$projectDataService.getNSValue(projectDir, runtimePackageName).version; } } diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index f455ca4739..50f7767172 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -3,6 +3,7 @@ import * as shell from "shelljs"; import * as constants from "../constants"; import * as helpers from "../common/helpers"; import * as semver from "semver"; +import { EventEmitter } from "events"; import { AppFilesUpdater } from "./app-files-updater"; import * as temp from "temp"; temp.track(); @@ -10,7 +11,7 @@ let clui = require("clui"); const buildInfoFileName = ".nsbuildinfo"; -export class PlatformService implements IPlatformService { +export class PlatformService extends EventEmitter 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 { @@ -25,10 +26,8 @@ export class PlatformService implements IPlatformService { private $logger: ILogger, private $npmInstallationManager: INpmInstallationManager, private $platformsData: IPlatformsData, - private $projectData: IProjectData, private $projectDataService: IProjectDataService, private $hooksService: IHooksService, - private $options: IOptions, private $nodeModulesBuilder: INodeModulesBuilder, private $pluginsService: IPluginsService, private $projectFilesManager: IProjectFilesManager, @@ -41,65 +40,67 @@ export class PlatformService implements IPlatformService { private $deviceAppDataFactory: Mobile.IDeviceAppDataFactory, private $projectChangesService: IProjectChangesService, private $emulatorPlatformService: IEmulatorPlatformService, - private $analyticsService: IAnalyticsService) { } + private $analyticsService: IAnalyticsService) { + super(); + } - public async addPlatforms(platforms: string[]): Promise { - let platformsDir = this.$projectData.platformsDir; + public async addPlatforms(platforms: string[], platformTemplate: string, projectData: IProjectData): Promise { + let platformsDir = projectData.platformsDir; this.$fs.ensureDirectoryExists(platformsDir); for (let platform of platforms) { - await this.addPlatform(platform.toLowerCase()); + await this.addPlatform(platform.toLowerCase(), platformTemplate, projectData); } } - private async addPlatform(platformParam: string): Promise { + private async addPlatform(platformParam: string, platformTemplate: string, projectData: IProjectData, frameworkPath?: string): Promise { let data = platformParam.split("@"), platform = data[0].toLowerCase(), version = data[1]; - this.validatePlatform(platform); + this.validatePlatform(platform, projectData); - let platformPath = path.join(this.$projectData.platformsDir, platform); + let platformPath = path.join(projectData.platformsDir, platform); if (this.$fs.exists(platformPath)) { this.$errors.failWithoutHelp("Platform %s already added", platform); } - let platformData = this.$platformsData.getPlatformData(platform); + let platformData = this.$platformsData.getPlatformData(platform, projectData); // Copy platform specific files in platforms dir let platformProjectService = platformData.platformProjectService; - await platformProjectService.validate(); + await platformProjectService.validate(projectData); // 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); + this.$logger.trace("Package: %s", projectData.projectId); + this.$logger.trace("Name: %s", projectData.projectName); this.$logger.out("Copying template files..."); let packageToInstall = ""; let npmOptions: IStringDictionary = { - pathToSave: path.join(this.$projectData.platformsDir, platform), + pathToSave: path.join(projectData.platformsDir, platform), dependencyType: "save" }; - if (!this.$options.frameworkPath) { + if (!frameworkPath) { packageToInstall = platformData.frameworkPackageName; npmOptions["version"] = version; } let spinner = new clui.Spinner("Installing " + packageToInstall); - let projectDir = this.$projectData.projectDir; + let projectDir = projectData.projectDir; try { spinner.start(); let downloadedPackagePath = await this.$npmInstallationManager.install(packageToInstall, 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); + let coreModuleName = await this.addPlatformCore(platformData, frameworkDir, platformTemplate, projectData); + await this.$npm.uninstall(coreModuleName, { save: true }, projectData.projectDir); } catch (err) { this.$fs.deleteDirectory(platformPath); throw err; @@ -111,36 +112,34 @@ export class PlatformService implements IPlatformService { } - private async addPlatformCore(platformData: IPlatformData, frameworkDir: string): Promise { + private async addPlatformCore(platformData: IPlatformData, frameworkDir: string, platformTemplate: string, projectData: IProjectData): Promise { let coreModuleData = this.$fs.readJson(path.join(frameworkDir, "../", "package.json")); let installedVersion = coreModuleData.version; let coreModuleName = coreModuleData.name; - let customTemplateOptions = await this.getPathToPlatformTemplate(this.$options.platformTemplate, platformData.frameworkPackageName); + let customTemplateOptions = await this.getPathToPlatformTemplate(platformTemplate, platformData.frameworkPackageName, projectData.projectDir); 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); + await platformData.platformProjectService.createProject(path.resolve(frameworkDir), installedVersion, projectData, pathToTemplate); + platformData.platformProjectService.ensureConfigurationFileInAppResources(projectData); + await platformData.platformProjectService.interpolateData(projectData); + platformData.platformProjectService.afterCreateProject(platformData.projectRoot, projectData); let frameworkPackageNameData: any = { version: installedVersion }; if (customTemplateOptions) { frameworkPackageNameData.template = customTemplateOptions.selectedTemplate; } - this.$projectDataService.setNSValue(this.$projectData.projectDir, platformData.frameworkPackageName, frameworkPackageNameData); + this.$projectDataService.setNSValue(projectData.projectDir, platformData.frameworkPackageName, frameworkPackageNameData); return coreModuleName; } - private async getPathToPlatformTemplate(selectedTemplate: string, frameworkPackageName: string): Promise { + private async getPathToPlatformTemplate(selectedTemplate: string, frameworkPackageName: string, projectDir: string): Promise<{ selectedTemplate: string, pathToTemplate: string }> { if (!selectedTemplate) { // read data from package.json's nativescript key // check the nativescript.tns-.template value - const nativescriptPlatformData = this.$projectDataService.getNSValue(this.$projectData.projectDir, frameworkPackageName); + const nativescriptPlatformData = this.$projectDataService.getNSValue(projectDir, frameworkPackageName); selectedTemplate = nativescriptPlatformData && nativescriptPlatformData.template; } @@ -167,34 +166,34 @@ export class PlatformService implements IPlatformService { return null; } - public getInstalledPlatforms(): string[] { - if (!this.$fs.exists(this.$projectData.platformsDir)) { + public getInstalledPlatforms(projectData: IProjectData): string[] { + if (!this.$fs.exists(projectData.platformsDir)) { return []; } - let subDirs = this.$fs.readDirectory(this.$projectData.platformsDir); + let subDirs = this.$fs.readDirectory(projectData.platformsDir); return _.filter(subDirs, p => this.$platformsData.platformsNames.indexOf(p) > -1); } - public getAvailablePlatforms(): string[] { - let installedPlatforms = this.getInstalledPlatforms(); + public getAvailablePlatforms(projectData: IProjectData): string[] { + let installedPlatforms = this.getInstalledPlatforms(projectData); return _.filter(this.$platformsData.platformsNames, p => { - return installedPlatforms.indexOf(p) < 0 && this.isPlatformSupportedForOS(p); // Only those not already installed + return installedPlatforms.indexOf(p) < 0 && this.isPlatformSupportedForOS(p, projectData); // Only those not already installed }); } - public getPreparedPlatforms(): string[] { - return _.filter(this.$platformsData.platformsNames, p => { return this.isPlatformPrepared(p); }); + public getPreparedPlatforms(projectData: IProjectData): string[] { + return _.filter(this.$platformsData.platformsNames, p => { return this.isPlatformPrepared(p, projectData); }); } - public async preparePlatform(platform: string): Promise { - this.validatePlatform(platform); + public async preparePlatform(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, platformTemplate: string, projectData: IProjectData, provision: any): Promise { + this.validatePlatform(platform, projectData); - await this.trackProjectType(); + await this.trackProjectType(projectData); //We need dev-dependencies here, so before-prepare hooks will be executed correctly. try { - await this.$pluginsService.ensureAllDependenciesAreInstalled(); + await this.$pluginsService.ensureAllDependenciesAreInstalled(projectData); } 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}`); @@ -202,7 +201,7 @@ export class PlatformService implements IPlatformService { // Need to check if any plugin requires Cocoapods to be installed. if (platform === "ios") { - for (let pluginData of await this.$pluginsService.getAllInstalledPlugins()) { + for (let pluginData of await this.$pluginsService.getAllInstalledPlugins(projectData)) { 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.`); @@ -210,24 +209,24 @@ export class PlatformService implements IPlatformService { } } - await this.ensurePlatformInstalled(platform); - let changesInfo = this.$projectChangesService.checkForChanges(platform); + await this.ensurePlatformInstalled(platform, platformTemplate, projectData); + let changesInfo = this.$projectChangesService.checkForChanges(platform, projectData); this.$logger.trace("Changes info in prepare platform:", changesInfo); if (changesInfo.hasChanges) { // android build artifacts need to be cleaned up when switching from release to debug builds if (platform.toLowerCase() === "android") { - let previousPrepareInfo = this.$projectChangesService.getPrepareInfo(platform); + let previousPrepareInfo = this.$projectChangesService.getPrepareInfo(platform, projectData); // clean up prepared plugins when not building for release - if (previousPrepareInfo && previousPrepareInfo.release !== this.$options.release) { - let platformData = this.$platformsData.getPlatformData(platform); - await platformData.platformProjectService.cleanProject(platformData.projectRoot, []); + if (previousPrepareInfo && previousPrepareInfo.release !== appFilesUpdaterOptions.release) { + let platformData = this.$platformsData.getPlatformData(platform, projectData); + await platformData.platformProjectService.cleanProject(platformData.projectRoot, [], projectData); } } - await this.preparePlatformCore(platform, changesInfo); - this.$projectChangesService.savePrepareInfo(platform); + await this.preparePlatformCore(platform, appFilesUpdaterOptions, projectData, provision); + this.$projectChangesService.savePrepareInfo(platform, projectData); } else { this.$logger.out("Skipping prepare."); } @@ -235,18 +234,18 @@ export class PlatformService implements IPlatformService { return true; } - public async validateOptions(platform?: string): Promise { + public async validateOptions(provision: any, projectData: IProjectData, 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(); + let platformData = this.$platformsData.getPlatformData(platform, projectData); + return await platformData.platformProjectService.validateOptions(provision); } 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(); + let platformData = this.$platformsData.getPlatformData(availablePlatform, projectData); + valid = valid && await platformData.platformProjectService.validateOptions(provision); } return valid; @@ -254,20 +253,20 @@ export class PlatformService implements IPlatformService { } @helpers.hook('prepare') - private async preparePlatformCore(platform: string, changesInfo?: IProjectChangesInfo): Promise { + private async preparePlatformCore(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, projectData: IProjectData, provision: any, changesInfo?: IProjectChangesInfo): Promise { this.$logger.out("Preparing project..."); - let platformData = this.$platformsData.getPlatformData(platform); + let platformData = this.$platformsData.getPlatformData(platform, projectData); if (!changesInfo || changesInfo.appFilesChanged) { - await this.copyAppFiles(platform); + await this.copyAppFiles(platform, appFilesUpdaterOptions, projectData); } if (!changesInfo || changesInfo.appResourcesChanged) { - this.copyAppResources(platform); - await platformData.platformProjectService.prepareProject(); + this.copyAppResources(platform, projectData); + await platformData.platformProjectService.prepareProject(projectData, provision); } if (!changesInfo || changesInfo.modulesChanged) { - await this.copyTnsModules(platform); + await this.copyTnsModules(platform, projectData); } let directoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); @@ -279,52 +278,51 @@ export class PlatformService implements IPlatformService { this.$projectFilesManager.processPlatformSpecificFiles(directoryPath, platform, excludedDirs); if (!changesInfo || changesInfo.configChanged || changesInfo.modulesChanged) { - this.applyBaseConfigOption(platformData); - await platformData.platformProjectService.processConfigurationFilesFromAppResources(); + await platformData.platformProjectService.processConfigurationFilesFromAppResources(appFilesUpdaterOptions.release, projectData); } - await platformData.platformProjectService.interpolateConfigurationFile(); + await platformData.platformProjectService.interpolateConfigurationFile(projectData); this.$logger.out("Project successfully prepared (" + platform + ")"); } - private async copyAppFiles(platform: string): Promise { - let platformData = this.$platformsData.getPlatformData(platform); - platformData.platformProjectService.ensureConfigurationFileInAppResources(); + private async copyAppFiles(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, projectData: IProjectData): Promise { + let platformData = this.$platformsData.getPlatformData(platform, projectData); + platformData.platformProjectService.ensureConfigurationFileInAppResources(projectData); 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); + let appSourceDirectoryPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME); - const appUpdater = new AppFilesUpdater(appSourceDirectoryPath, appDestinationDirectoryPath, this.$options, this.$fs); + const appUpdater = new AppFilesUpdater(appSourceDirectoryPath, appDestinationDirectoryPath, appFilesUpdaterOptions, this.$fs); appUpdater.updateApp(sourceFiles => { this.$xmlValidator.validateXmlFiles(sourceFiles); }); } - private copyAppResources(platform: string): void { - let platformData = this.$platformsData.getPlatformData(platform); + private copyAppResources(platform: string, projectData: IProjectData): void { + let platformData = this.$platformsData.getPlatformData(platform, projectData); let appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); let appResourcesDirectoryPath = path.join(appDestinationDirectoryPath, constants.APP_RESOURCES_FOLDER_NAME); if (this.$fs.exists(appResourcesDirectoryPath)) { - platformData.platformProjectService.prepareAppResources(appResourcesDirectoryPath); - let appResourcesDestination = platformData.platformProjectService.getAppResourcesDestinationDirectoryPath(); + platformData.platformProjectService.prepareAppResources(appResourcesDirectoryPath, projectData); + let appResourcesDestination = platformData.platformProjectService.getAppResourcesDestinationDirectoryPath(projectData); this.$fs.ensureDirectoryExists(appResourcesDestination); shell.cp("-Rf", path.join(appResourcesDirectoryPath, platformData.normalizedPlatformName, "*"), appResourcesDestination); this.$fs.deleteDirectory(appResourcesDirectoryPath); } } - private async copyTnsModules(platform: string): Promise { - let platformData = this.$platformsData.getPlatformData(platform); + private async copyTnsModules(platform: string, projectData: IProjectData): Promise { + let platformData = this.$platformsData.getPlatformData(platform, projectData); 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); + await this.$nodeModulesBuilder.prepareNodeModules(tnsModulesDestinationPath, platform, lastModifiedTime, projectData); } catch (error) { this.$logger.debug(error); shell.rm("-rf", appDestinationDirectoryPath); @@ -332,11 +330,11 @@ export class PlatformService implements IPlatformService { } } - public async shouldBuild(platform: string, buildConfig?: IBuildConfig): Promise { + public async shouldBuild(platform: string, projectData: IProjectData, buildConfig: IBuildConfig): Promise { if (this.$projectChangesService.currentChanges.changesRequireBuild) { return true; } - let platformData = this.$platformsData.getPlatformData(platform); + let platformData = this.$platformsData.getPlatformData(platform, projectData); let forDevice = !buildConfig || buildConfig.buildForDevice; let outputPath = forDevice ? platformData.deviceBuildOutputPath : platformData.emulatorBuildOutputPath; if (!this.$fs.exists(outputPath)) { @@ -347,12 +345,12 @@ export class PlatformService implements IPlatformService { if (packages.length === 0) { return true; } - let prepareInfo = this.$projectChangesService.getPrepareInfo(platform); + let prepareInfo = this.$projectChangesService.getPrepareInfo(platform, projectData); let buildInfo = this.getBuildInfo(platform, platformData, buildConfig); if (!prepareInfo || !buildInfo) { return true; } - if (this.$options.clean) { + if (buildConfig.clean) { return prepareInfo.time !== buildInfo.prepareTime; } if (prepareInfo.time === buildInfo.prepareTime) { @@ -361,24 +359,27 @@ export class PlatformService implements IPlatformService { return prepareInfo.changesRequireBuildTime !== buildInfo.prepareTime; } - public async trackProjectType(): Promise { + public async trackProjectType(projectData: IProjectData): Promise { // Track each project once per process. // In long living process, where we may work with multiple projects, we would like to track the information for each of them. - if (this.$projectData && (this.$projectData.projectFilePath !== this._trackedProjectFilePath)) { - this._trackedProjectFilePath = this.$projectData.projectFilePath; + if (projectData && (projectData.projectFilePath !== this._trackedProjectFilePath)) { + this._trackedProjectFilePath = projectData.projectFilePath; - await this.$analyticsService.track("Working with project type", this.$projectData.projectType); + await this.$analyticsService.track("Working with project type", projectData.projectType); } } - public async buildPlatform(platform: string, buildConfig?: IBuildConfig): Promise { + public async buildPlatform(platform: string, buildConfig: IBuildConfig, projectData: IProjectData): Promise { this.$logger.out("Building project..."); - await this.trackProjectType(); + await this.trackProjectType(projectData); - let platformData = this.$platformsData.getPlatformData(platform); - await platformData.platformProjectService.buildProject(platformData.projectRoot, buildConfig); - let prepareInfo = this.$projectChangesService.getPrepareInfo(platform); + let platformData = this.$platformsData.getPlatformData(platform, projectData); + platformData.platformProjectService.on(constants.BUILD_OUTPUT_EVENT_NAME, (data: any) => { + this.emit(constants.BUILD_OUTPUT_EVENT_NAME, data); + }); + await platformData.platformProjectService.buildProject(platformData.projectRoot, projectData, buildConfig); + let prepareInfo = this.$projectChangesService.getPrepareInfo(platform, projectData); let buildInfoFilePath = this.getBuildOutputPath(platform, platformData, buildConfig); let buildInfoFile = path.join(buildInfoFilePath, buildInfoFileName); let buildInfo: IBuildInfo = { @@ -389,20 +390,20 @@ export class PlatformService implements IPlatformService { this.$logger.out("Project successfully built."); } - public async shouldInstall(device: Mobile.IDevice): Promise { + public async shouldInstall(device: Mobile.IDevice, projectData: IProjectData): Promise { let platform = device.deviceInfo.platform; - let platformData = this.$platformsData.getPlatformData(platform); - if (!(await device.applicationManager.isApplicationInstalled(this.$projectData.projectId))) { + let platformData = this.$platformsData.getPlatformData(platform, projectData); + if (!(await device.applicationManager.isApplicationInstalled(projectData.projectId))) { return true; } - let deviceBuildInfo: IBuildInfo = await this.getDeviceBuildInfo(device); + let deviceBuildInfo: IBuildInfo = await this.getDeviceBuildInfo(device, projectData); let localBuildInfo = this.getBuildInfo(platform, platformData, { buildForDevice: !device.isEmulator }); return !localBuildInfo || !deviceBuildInfo || deviceBuildInfo.buildTime !== localBuildInfo.buildTime; } - public async installApplication(device: Mobile.IDevice): Promise { + public async installApplication(device: Mobile.IDevice, options: IRelease, projectData: IProjectData): Promise { this.$logger.out("Installing..."); - let platformData = this.$platformsData.getPlatformData(device.deviceInfo.platform); + let platformData = this.$platformsData.getPlatformData(device.deviceInfo.platform, projectData); let packageFile = ""; if (this.$devicesService.isiOSSimulator(device)) { packageFile = this.getLatestApplicationPackageForEmulator(platformData).packageName; @@ -410,14 +411,14 @@ export class PlatformService implements IPlatformService { packageFile = this.getLatestApplicationPackageForDevice(platformData).packageName; } - await platformData.platformProjectService.deploy(device.deviceInfo.identifier); + await platformData.platformProjectService.deploy(device.deviceInfo.identifier, projectData); - await device.applicationManager.reinstallApplication(this.$projectData.projectId, packageFile); + await device.applicationManager.reinstallApplication(projectData.projectId, packageFile); - if (!this.$options.release) { - let deviceFilePath = await this.getDeviceBuildInfoFilePath(device); + if (!options.release) { + let deviceFilePath = await this.getDeviceBuildInfoFilePath(device, projectData); let buildInfoFilePath = this.getBuildOutputPath(device.deviceInfo.platform, platformData, { buildForDevice: !device.isEmulator }); - let appIdentifier = this.$projectData.projectId; + let appIdentifier = projectData.projectId; await device.fileSystem.putFile(path.join(buildInfoFilePath, buildInfoFileName), deviceFilePath, appIdentifier); } @@ -425,108 +426,110 @@ export class PlatformService implements IPlatformService { this.$logger.out(`Successfully installed on device with identifier '${device.deviceInfo.identifier}'.`); } - public async deployPlatform(platform: string, forceInstall?: boolean): Promise { - await this.preparePlatform(platform); + public async deployPlatform(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, deployOptions: IDeployPlatformOptions, projectData: IProjectData, provision: any): Promise { + await this.preparePlatform(platform, appFilesUpdaterOptions, deployOptions.platformTemplate, projectData, provision); this.$logger.out("Searching for devices..."); - await this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }); + await this.$devicesService.initialize({ platform: platform, deviceId: deployOptions.device }); let action = async (device: Mobile.IDevice): Promise => { - let buildConfig: IBuildConfig = { buildForDevice: !this.$devicesService.isiOSSimulator(device) }; - let shouldBuild = await this.shouldBuild(platform, buildConfig); + let buildConfig: IiOSBuildConfig = { + buildForDevice: !this.$devicesService.isiOSSimulator(device), + projectDir: deployOptions.projectDir, + release: deployOptions.release, + device: deployOptions.device, + provision: deployOptions.provision, + teamId: deployOptions.teamId + }; + let shouldBuild = await this.shouldBuild(platform, projectData, buildConfig); if (shouldBuild) { - await this.buildPlatform(platform, buildConfig); + await this.buildPlatform(platform, buildConfig, projectData); } else { this.$logger.out("Skipping package build. No changes detected on the native side. This will be fast!"); } - if (forceInstall || shouldBuild || (await this.shouldInstall(device))) { - await this.installApplication(device); + if (deployOptions.forceInstall || shouldBuild || (await this.shouldInstall(device, projectData))) { + await this.installApplication(device, deployOptions, projectData); } else { this.$logger.out("Skipping install."); } }; - await this.$devicesService.execute(action, this.getCanExecuteAction(platform)); + await this.$devicesService.execute(action, this.getCanExecuteAction(platform, deployOptions)); } - public async runPlatform(platform: string): Promise { - await this.trackProjectType(); - - if (this.$options.justlaunch) { - this.$options.watch = false; - } + public async runPlatform(platform: string, runOptions: IRunPlatformOptions, projectData: IProjectData): Promise { + await this.trackProjectType(projectData); this.$logger.out("Starting..."); let action = async (device: Mobile.IDevice) => { - await device.applicationManager.startApplication(this.$projectData.projectId); + await device.applicationManager.startApplication(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)); + await this.$devicesService.initialize({ platform: platform, deviceId: runOptions.device }); + await this.$devicesService.execute(action, this.getCanExecuteAction(platform, runOptions)); } - public async emulatePlatform(platform: string): Promise { - if (this.$options.avd) { + public async emulatePlatform(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, emulateOptions: IEmulatePlatformOptions, projectData: IProjectData, provision: any): Promise { + if (emulateOptions.avd) { this.$logger.warn(`Option --avd is no longer supported. Please use --device instead!`); return Promise.resolve(); } - if (this.$options.availableDevices) { + if (emulateOptions.availableDevices) { return this.$emulatorPlatformService.listAvailableEmulators(platform); } - this.$options.emulator = true; - if (this.$options.device) { - let info = await this.$emulatorPlatformService.getEmulatorInfo(platform, this.$options.device); + if (emulateOptions.device) { + let info = await this.$emulatorPlatformService.getEmulatorInfo(platform, emulateOptions.device); if (info) { if (!info.isRunning) { - await this.$emulatorPlatformService.startEmulator(info); + await this.$emulatorPlatformService.startEmulator(info, projectData); } - this.$options.device = null; + emulateOptions.device = null; } else { - await this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }); + await this.$devicesService.initialize({ platform: platform, deviceId: emulateOptions.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((deviceInfo: Mobile.IDeviceInfo) => deviceInfo.identifier === emulateOptions.device); } if (found.length === 0) { - this.$errors.fail("Cannot find device with name: %s", this.$options.device); + this.$errors.fail("Cannot find device with name: %s", emulateOptions.device); } } } - await this.deployPlatform(platform); - return this.runPlatform(platform); + await this.deployPlatform(platform, appFilesUpdaterOptions, emulateOptions, projectData, provision); + return this.runPlatform(platform, emulateOptions, projectData); } - private getBuildOutputPath(platform: string, platformData: IPlatformData, buildConfig?: IBuildConfig): string { - let buildForDevice = buildConfig ? buildConfig.buildForDevice : this.$options.forDevice; + private getBuildOutputPath(platform: string, platformData: IPlatformData, options: IBuildForDevice): string { if (platform.toLowerCase() === this.$devicePlatformsConstants.iOS.toLowerCase()) { - return buildForDevice ? platformData.deviceBuildOutputPath : platformData.emulatorBuildOutputPath; + return options.buildForDevice ? platformData.deviceBuildOutputPath : platformData.emulatorBuildOutputPath; } + return platformData.deviceBuildOutputPath; } - private async getDeviceBuildInfoFilePath(device: Mobile.IDevice): Promise { - let deviceAppData = this.$deviceAppDataFactory.create(this.$projectData.projectId, device.deviceInfo.platform, device); + private async getDeviceBuildInfoFilePath(device: Mobile.IDevice, projectData: IProjectData): Promise { + let deviceAppData = this.$deviceAppDataFactory.create(projectData.projectId, device.deviceInfo.platform, device); let deviceRootPath = path.dirname(await deviceAppData.getDeviceProjectRootPath()); return helpers.fromWindowsRelativePathToUnix(path.join(deviceRootPath, buildInfoFileName)); } - private async getDeviceBuildInfo(device: Mobile.IDevice): Promise { - let deviceFilePath = await this.getDeviceBuildInfoFilePath(device); + private async getDeviceBuildInfo(device: Mobile.IDevice, projectData: IProjectData): Promise { + let deviceFilePath = await this.getDeviceBuildInfoFilePath(device, projectData); try { - return JSON.parse(await this.readFile(device, deviceFilePath)); + return JSON.parse(await this.readFile(device, deviceFilePath, projectData)); } catch (e) { return null; } } - private getBuildInfo(platform: string, platformData: IPlatformData, buildConfig: IBuildConfig): IBuildInfo { - let buildInfoFilePath = this.getBuildOutputPath(platform, platformData, buildConfig); + private getBuildInfo(platform: string, platformData: IPlatformData, options: IBuildForDevice): IBuildInfo { + let buildInfoFilePath = this.getBuildOutputPath(platform, platformData, options); let buildInfoFile = path.join(buildInfoFilePath, buildInfoFileName); if (this.$fs.exists(buildInfoFile)) { try { @@ -540,19 +543,19 @@ export class PlatformService implements IPlatformService { return null; } - public async cleanDestinationApp(platform: string): Promise { - await this.ensurePlatformInstalled(platform); + public async cleanDestinationApp(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, platformTemplate: string, projectData: IProjectData): Promise { + await this.ensurePlatformInstalled(platform, platformTemplate, projectData); - const appSourceDirectoryPath = path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME); - let platformData = this.$platformsData.getPlatformData(platform); + const appSourceDirectoryPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME); + let platformData = this.$platformsData.getPlatformData(platform, projectData); let appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); - const appUpdater = new AppFilesUpdater(appSourceDirectoryPath, appDestinationDirectoryPath, this.$options, this.$fs); + const appUpdater = new AppFilesUpdater(appSourceDirectoryPath, appDestinationDirectoryPath, appFilesUpdaterOptions, this.$fs); appUpdater.cleanDestinationApp(); } - public lastOutputPath(platform: string, settings: { isForDevice: boolean }): string { + public lastOutputPath(platform: string, settings: { isForDevice: boolean }, projectData: IProjectData): string { let packageFile: string; - let platformData = this.$platformsData.getPlatformData(platform); + let platformData = this.$platformsData.getPlatformData(platform, projectData); if (settings.isForDevice) { packageFile = this.getLatestApplicationPackageForDevice(platformData).packageName; } else { @@ -564,11 +567,11 @@ export class PlatformService implements IPlatformService { return packageFile; } - public copyLastOutput(platform: string, targetPath: string, settings: { isForDevice: boolean }): void { + public copyLastOutput(platform: string, targetPath: string, settings: { isForDevice: boolean }, projectData: IProjectData): void { platform = platform.toLowerCase(); targetPath = path.resolve(targetPath); - let packageFile = this.lastOutputPath(platform, settings); + let packageFile = this.lastOutputPath(platform, settings, projectData); this.$fs.ensureDirectoryExists(path.dirname(targetPath)); @@ -581,38 +584,38 @@ export class PlatformService implements IPlatformService { this.$logger.info(`Copied file '${packageFile}' to '${targetPath}'.`); } - public async removePlatforms(platforms: string[]): Promise { + public async removePlatforms(platforms: string[], projectData: IProjectData): Promise { for (let platform of platforms) { - this.validatePlatformInstalled(platform); - let platformData = this.$platformsData.getPlatformData(platform); + this.validatePlatformInstalled(platform, projectData); + let platformData = this.$platformsData.getPlatformData(platform, projectData); - await this.$platformsData.getPlatformData(platform).platformProjectService.stopServices(); + await platformData.platformProjectService.stopServices(platformData.projectRoot); - let platformDir = path.join(this.$projectData.platformsDir, platform); + let platformDir = path.join(projectData.platformsDir, platform); this.$fs.deleteDirectory(platformDir); - this.$projectDataService.removeNSProperty(this.$projectData.projectDir, platformData.frameworkPackageName); + this.$projectDataService.removeNSProperty(projectData.projectDir, platformData.frameworkPackageName); this.$logger.out(`Platform ${platform} successfully removed.`); } } - public async updatePlatforms(platforms: string[]): Promise { + public async updatePlatforms(platforms: string[], platformTemplate: string, projectData: IProjectData): Promise { for (let platformParam of platforms) { let data = platformParam.split("@"), platform = data[0], version = data[1]; - if (this.isPlatformInstalled(platform)) { - await this.updatePlatform(platform, version); + if (this.isPlatformInstalled(platform, projectData)) { + await this.updatePlatform(platform, version, platformTemplate, projectData); } else { - await this.addPlatform(platformParam); + await this.addPlatform(platformParam, platformTemplate, projectData); } }; } - private getCanExecuteAction(platform: string): any { + private getCanExecuteAction(platform: string, options: IDeviceEmulator): any { let canExecute = (currentDevice: Mobile.IDevice): boolean => { - if (this.$options.device && currentDevice && currentDevice.deviceInfo) { + if (options.device && currentDevice && currentDevice.deviceInfo) { let device = this.$devicesService.getDeviceByDeviceOption(); if (device && device.deviceInfo) { return currentDevice.deviceInfo.identifier === device.deviceInfo.identifier; @@ -620,7 +623,7 @@ export class PlatformService implements IPlatformService { } if (this.$mobileHelper.isiOSPlatform(platform) && this.$hostInfo.isDarwin) { - if (this.$devicesService.isOnlyiOSSimultorRunning() || this.$options.emulator || this.$devicesService.isiOSSimulator(currentDevice)) { + if (this.$devicesService.isOnlyiOSSimultorRunning() || options.emulator || this.$devicesService.isiOSSimulator(currentDevice)) { return true; } @@ -633,53 +636,53 @@ export class PlatformService implements IPlatformService { return canExecute; } - public validatePlatform(platform: string): void { + public validatePlatform(platform: string, projectData: IProjectData): void { if (!platform) { this.$errors.fail("No platform specified."); } platform = platform.split("@")[0].toLowerCase(); - if (!this.isValidPlatform(platform)) { + if (!this.isValidPlatform(platform, projectData)) { this.$errors.fail("Invalid platform %s. Valid platforms are %s.", platform, helpers.formatListOfNames(this.$platformsData.platformsNames)); } - if (!this.isPlatformSupportedForOS(platform)) { + if (!this.isPlatformSupportedForOS(platform, projectData)) { this.$errors.fail("Applications for platform %s can not be built on this OS - %s", platform, process.platform); } } - public validatePlatformInstalled(platform: string): void { - this.validatePlatform(platform); + public validatePlatformInstalled(platform: string, projectData: IProjectData): void { + this.validatePlatform(platform, projectData); - if (!this.isPlatformInstalled(platform)) { + if (!this.isPlatformInstalled(platform, projectData)) { this.$errors.fail("The platform %s is not added to this project. Please use 'tns platform add '", platform); } } - public async ensurePlatformInstalled(platform: string): Promise { - if (!this.isPlatformInstalled(platform)) { - await this.addPlatform(platform); + public async ensurePlatformInstalled(platform: string, platformTemplate: string, projectData: IProjectData): Promise { + if (!this.isPlatformInstalled(platform, projectData)) { + await this.addPlatform(platform, platformTemplate, projectData); } } - private isPlatformInstalled(platform: string): boolean { - return this.$fs.exists(path.join(this.$projectData.platformsDir, platform.toLowerCase())); + private isPlatformInstalled(platform: string, projectData: IProjectData): boolean { + return this.$fs.exists(path.join(projectData.platformsDir, platform.toLowerCase())); } - private isValidPlatform(platform: string) { - return this.$platformsData.getPlatformData(platform); + private isValidPlatform(platform: string, projectData: IProjectData) { + return this.$platformsData.getPlatformData(platform, projectData); } - private isPlatformSupportedForOS(platform: string): boolean { - let targetedOS = this.$platformsData.getPlatformData(platform).targetedOS; + private isPlatformSupportedForOS(platform: string, projectData: IProjectData): boolean { + let targetedOS = this.$platformsData.getPlatformData(platform, projectData).targetedOS; let res = !targetedOS || targetedOS.indexOf("*") >= 0 || targetedOS.indexOf(process.platform) >= 0; return res; } - private isPlatformPrepared(platform: string): boolean { - let platformData = this.$platformsData.getPlatformData(platform); - return platformData.platformProjectService.isPlatformPrepared(platformData.projectRoot); + private isPlatformPrepared(platform: string, projectData: IProjectData): boolean { + let platformData = this.$platformsData.getPlatformData(platform, projectData); + return platformData.platformProjectService.isPlatformPrepared(platformData.projectRoot, projectData); } private getApplicationPackages(buildOutputPath: string, validPackageNames: string[]): IApplicationPackage[] { @@ -719,28 +722,28 @@ export class PlatformService implements IPlatformService { return this.getLatestApplicationPackage(platformData.emulatorBuildOutputPath || platformData.deviceBuildOutputPath, platformData.validPackageNamesForEmulator || platformData.validPackageNamesForDevice); } - private async updatePlatform(platform: string, version: string): Promise { - let platformData = this.$platformsData.getPlatformData(platform); + private async updatePlatform(platform: string, version: string, platformTemplate: string, projectData: IProjectData): Promise { + let platformData = this.$platformsData.getPlatformData(platform, projectData); - let data = this.$projectDataService.getNSValue(this.$projectData.projectDir, platformData.frameworkPackageName); + let data = this.$projectDataService.getNSValue(projectData.projectDir, 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 installedModuleDir = await this.$npmInstallationManager.install(platformData.frameworkPackageName, 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); + let canUpdate = platformData.platformProjectService.canUpdatePlatform(installedModuleDir, projectData); + await this.$npm.uninstall(platformData.frameworkPackageName, { save: true }, 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); + await this.updatePlatformCore(platformData, { currentVersion, newVersion, canUpdate, platformTemplate }, projectData); } else if (semver.eq(currentVersion, newVersion)) { this.$errors.fail("Current and new version are the same."); } else { @@ -752,27 +755,19 @@ export class PlatformService implements IPlatformService { } - private async updatePlatformCore(platformData: IPlatformData, currentVersion: string, newVersion: string, canUpdate: boolean): Promise { + private async updatePlatformCore(platformData: IPlatformData, updateOptions: IUpdatePlatformOptions, projectData: IProjectData): Promise { let packageName = platformData.normalizedPlatformName.toLowerCase(); - await 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 { - if (this.$options.baseConfig) { - let newConfigFile = path.resolve(this.$options.baseConfig); - this.$logger.trace(`Replacing '${platformData.configurationFilePath}' with '${newConfigFile}'.`); - this.$fs.copyFile(newConfigFile, platformData.configurationFilePath); - } + await this.removePlatforms([packageName], projectData); + packageName = updateOptions.newVersion ? `${packageName}@${updateOptions.newVersion}` : packageName; + await this.addPlatform(packageName, updateOptions.platformTemplate, projectData); + this.$logger.out("Successfully updated to version ", updateOptions.newVersion); } - public async readFile(device: Mobile.IDevice, deviceFilePath: string): Promise { + public async readFile(device: Mobile.IDevice, deviceFilePath: string, projectData: IProjectData): Promise { temp.track(); let uniqueFilePath = temp.path({ suffix: ".tmp" }); try { - await device.fileSystem.getFile(deviceFilePath, this.$projectData.projectId, uniqueFilePath); + await device.fileSystem.getFile(deviceFilePath, projectData.projectId, uniqueFilePath); } catch (e) { return null; } diff --git a/lib/services/plugin-variables-service.ts b/lib/services/plugin-variables-service.ts index d58a9cdec5..2a85e515bc 100644 --- a/lib/services/plugin-variables-service.ts +++ b/lib/services/plugin-variables-service.ts @@ -5,7 +5,6 @@ export class PluginVariablesService implements IPluginVariablesService { constructor(private $errors: IErrors, private $pluginVariablesHelper: IPluginVariablesHelper, - private $projectData: IProjectData, private $projectDataService: IProjectDataService, private $prompter: IPrompter, private $fs: IFileSystem) { } @@ -14,42 +13,42 @@ export class PluginVariablesService implements IPluginVariablesService { return `${pluginName}-${PluginVariablesService.PLUGIN_VARIABLES_KEY}`; } - public async savePluginVariablesInProjectFile(pluginData: IPluginData): Promise { + public async savePluginVariablesInProjectFile(pluginData: IPluginData, projectData: IProjectData): 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; - }); + }, projectData); if (!_.isEmpty(values)) { - this.$projectDataService.setNSValue(this.$projectData.projectDir, this.getPluginVariablePropertyName(pluginData.name), values); + this.$projectDataService.setNSValue(projectData.projectDir, this.getPluginVariablePropertyName(pluginData.name), values); } } - public removePluginVariablesFromProjectFile(pluginName: string): void { - this.$projectDataService.removeNSProperty(this.$projectData.projectDir, this.getPluginVariablePropertyName(pluginName)); + public removePluginVariablesFromProjectFile(pluginName: string, projectData: IProjectData): void { + this.$projectDataService.removeNSProperty(projectData.projectDir, this.getPluginVariablePropertyName(pluginName)); } - public async interpolatePluginVariables(pluginData: IPluginData, pluginConfigurationFilePath: string): Promise { + public async interpolatePluginVariables(pluginData: IPluginData, pluginConfigurationFilePath: string, projectData: IProjectData): 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); - }); + }, projectData); this.$fs.writeFile(pluginConfigurationFilePath, pluginConfigurationFileContent); } - public interpolateAppIdentifier(pluginConfigurationFilePath: string): void { + public interpolateAppIdentifier(pluginConfigurationFilePath: string, projectData: IProjectData): void { let pluginConfigurationFileContent = this.$fs.readText(pluginConfigurationFilePath); - let newContent = this.interpolateCore("nativescript.id", this.$projectData.projectId, pluginConfigurationFileContent); + let newContent = this.interpolateCore("nativescript.id", projectData.projectId, pluginConfigurationFileContent); this.$fs.writeFile(pluginConfigurationFilePath, newContent); } - public async interpolate(pluginData: IPluginData, pluginConfigurationFilePath: string): Promise { - await this.interpolatePluginVariables(pluginData, pluginConfigurationFilePath); - this.interpolateAppIdentifier(pluginConfigurationFilePath); + public async interpolate(pluginData: IPluginData, pluginConfigurationFilePath: string, projectData: IProjectData): Promise { + await this.interpolatePluginVariables(pluginData, pluginConfigurationFilePath, projectData); + this.interpolateAppIdentifier(pluginConfigurationFilePath, projectData); } private interpolateCore(name: string, value: string, content: string): string { @@ -84,18 +83,18 @@ export class PluginVariablesService implements IPluginVariablesService { return value; } - private async executeForAllPluginVariables(pluginData: IPluginData, action: (pluginVariableData: IPluginVariableData) => Promise): Promise { + private async executeForAllPluginVariables(pluginData: IPluginData, action: (pluginVariableData: IPluginVariableData) => Promise, projectData: IProjectData): Promise { let pluginVariables = pluginData.pluginVariables; let pluginVariablesNames = _.keys(pluginVariables); - await Promise.all(_.map(pluginVariablesNames, pluginVariableName => action(this.createPluginVariableData(pluginData, pluginVariableName)))); + await Promise.all(_.map(pluginVariablesNames, pluginVariableName => action(this.createPluginVariableData(pluginData, pluginVariableName, projectData)))); } - private createPluginVariableData(pluginData: IPluginData, pluginVariableName: string): IPluginVariableData { + private createPluginVariableData(pluginData: IPluginData, pluginVariableName: string, projectData: IProjectData): IPluginVariableData { let variableData = pluginData.pluginVariables[pluginVariableName]; variableData.name = pluginVariableName; - const pluginVariableValues = this.$projectDataService.getNSValue(this.$projectData.projectDir, this.getPluginVariablePropertyName(pluginData.name)); + const pluginVariableValues = this.$projectDataService.getNSValue(projectData.projectDir, this.getPluginVariablePropertyName(pluginData.name)); variableData.value = pluginVariableValues ? pluginVariableValues[pluginVariableName] : undefined; return variableData; diff --git a/lib/services/plugins-service.ts b/lib/services/plugins-service.ts index 5f74f0af41..a217447e1c 100644 --- a/lib/services/plugins-service.ts +++ b/lib/services/plugins-service.ts @@ -9,9 +9,6 @@ export class PluginsService implements IPluginsService { private static NPM_CONFIG = { save: true }; - private get $projectData(): IProjectData { - return this.$injector.resolve("projectData"); - } private get $platformsData(): IPlatformsData { return this.$injector.resolve("platformsData"); } @@ -32,54 +29,54 @@ export class PluginsService implements IPluginsService { private $errors: IErrors, private $injector: IInjector) { } - public async add(plugin: string): Promise { - await this.ensure(); + public async add(plugin: string, projectData: IProjectData): Promise { + await this.ensure(projectData); 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 name = (await this.$npm.install(plugin, projectData.projectDir, PluginsService.NPM_CONFIG))[0]; + let pathToRealNpmPackageJson = path.join(projectData.projectDir, "node_modules", name, "package.json"); let realNpmPackageJson = this.$fs.readJson(pathToRealNpmPackageJson); if (realNpmPackageJson.nativescript) { - let pluginData = this.convertToPluginData(realNpmPackageJson); + let pluginData = this.convertToPluginData(realNpmPackageJson, projectData.projectDir); // Validate let action = async (pluginDestinationPath: string, platform: string, platformData: IPlatformData): Promise => { - this.isPluginDataValidForPlatform(pluginData, platform); + this.isPluginDataValidForPlatform(pluginData, platform, projectData); }; - this.executeForAllInstalledPlatforms(action); + this.executeForAllInstalledPlatforms(action, projectData); try { - await this.$pluginVariablesService.savePluginVariablesInProjectFile(pluginData); + await this.$pluginVariablesService.savePluginVariablesInProjectFile(pluginData, projectData); } catch (err) { // Revert package.json - this.$projectDataService.removeNSProperty(this.$projectData.projectDir, this.$pluginVariablesService.getPluginVariablePropertyName(pluginData.name)); - await this.$npm.uninstall(plugin, PluginsService.NPM_CONFIG, this.$projectData.projectDir); + this.$projectDataService.removeNSProperty(projectData.projectDir, this.$pluginVariablesService.getPluginVariablePropertyName(pluginData.name)); + await this.$npm.uninstall(plugin, PluginsService.NPM_CONFIG, projectData.projectDir); throw err; } this.$logger.out(`Successfully installed plugin ${realNpmPackageJson.name}.`); } else { - this.$npm.uninstall(realNpmPackageJson.name, { save: true }, this.$projectData.projectDir); + this.$npm.uninstall(realNpmPackageJson.name, { save: true }, 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 remove(pluginName: string): Promise { + public async remove(pluginName: string, projectData: IProjectData): Promise { let removePluginNativeCodeAction = async (modulesDestinationPath: string, platform: string, platformData: IPlatformData): Promise => { - let pluginData = this.convertToPluginData(this.getNodeModuleData(pluginName)); + let pluginData = this.convertToPluginData(this.getNodeModuleData(pluginName, projectData.projectDir), projectData.projectDir); - await platformData.platformProjectService.removePluginNativeCode(pluginData); + await platformData.platformProjectService.removePluginNativeCode(pluginData, projectData); }; - this.$pluginVariablesService.removePluginVariablesFromProjectFile(pluginName.toLowerCase()); - this.executeForAllInstalledPlatforms(removePluginNativeCodeAction); + this.$pluginVariablesService.removePluginVariablesFromProjectFile(pluginName.toLowerCase(), projectData); + this.executeForAllInstalledPlatforms(removePluginNativeCodeAction, projectData); - await this.executeNpmCommand(PluginsService.UNINSTALL_COMMAND_NAME, pluginName); + await this.executeNpmCommand(PluginsService.UNINSTALL_COMMAND_NAME, pluginName, projectData); let showMessage = true; let action = async (modulesDestinationPath: string, platform: string, platformData: IPlatformData): Promise => { @@ -89,7 +86,7 @@ export class PluginsService implements IPluginsService { showMessage = false; }; - this.executeForAllInstalledPlatforms(action); + this.executeForAllInstalledPlatforms(action, projectData); if (showMessage) { this.$logger.out(`Succsessfully removed plugin ${pluginName}`); @@ -101,23 +98,23 @@ export class PluginsService implements IPluginsService { return this.$npm.search(filter, { "silent": silent }); } - public async prepare(dependencyData: IDependencyData, platform: string): Promise { + public async prepare(dependencyData: IDependencyData, platform: string, projectData: IProjectData): Promise { platform = platform.toLowerCase(); - let platformData = this.$platformsData.getPlatformData(platform); - let pluginData = this.convertToPluginData(dependencyData); + let platformData = this.$platformsData.getPlatformData(platform, projectData); + let pluginData = this.convertToPluginData(dependencyData, projectData.projectDir); let appFolderExists = this.$fs.exists(path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME)); if (appFolderExists) { - this.preparePluginScripts(pluginData, platform); - await this.preparePluginNativeCode(pluginData, platform); + this.preparePluginScripts(pluginData, platform, projectData); + await this.preparePluginNativeCode(pluginData, platform, projectData); // Show message this.$logger.out(`Successfully prepared plugin ${pluginData.name} for ${platform}.`); } } - private preparePluginScripts(pluginData: IPluginData, platform: string): void { - let platformData = this.$platformsData.getPlatformData(platform); + private preparePluginScripts(pluginData: IPluginData, platform: string, projectData: IProjectData): void { + let platformData = this.$platformsData.getPlatformData(platform, projectData); let pluginScriptsDestinationPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, "tns_modules"); let scriptsDestinationExists = this.$fs.exists(pluginScriptsDestinationPath); if (!scriptsDestinationExists) { @@ -125,7 +122,7 @@ export class PluginsService implements IPluginsService { return; } - if (!this.isPluginDataValidForPlatform(pluginData, platform)) { + if (!this.isPluginDataValidForPlatform(pluginData, platform, projectData)) { return; } @@ -133,41 +130,41 @@ export class PluginsService implements IPluginsService { this.$projectFilesManager.processPlatformSpecificFiles(pluginScriptsDestinationPath, platform); } - private async preparePluginNativeCode(pluginData: IPluginData, platform: string): Promise { - let platformData = this.$platformsData.getPlatformData(platform); + private async preparePluginNativeCode(pluginData: IPluginData, platform: string, projectData: IProjectData): Promise { + let platformData = this.$platformsData.getPlatformData(platform, projectData); pluginData.pluginPlatformsFolderPath = (_platform: string) => path.join(pluginData.fullPath, "platforms", _platform); - await platformData.platformProjectService.preparePluginNativeCode(pluginData); + await platformData.platformProjectService.preparePluginNativeCode(pluginData, projectData); } - public async ensureAllDependenciesAreInstalled(): Promise { - let installedDependencies = this.$fs.exists(this.nodeModulesPath) ? this.$fs.readDirectory(this.nodeModulesPath) : []; + public async ensureAllDependenciesAreInstalled(projectData: IProjectData): Promise { + let installedDependencies = this.$fs.exists(this.getNodeModulesPath(projectData.projectDir)) ? this.$fs.readDirectory(this.getNodeModulesPath(projectData.projectDir)) : []; // 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)); + let contents = this.$fs.readDirectory(path.join(this.getNodeModulesPath(projectData.projectDir), scopedDependencyDir)); installedDependencies = installedDependencies.concat(contents.map(dependencyName => `${scopedDependencyDir}/${dependencyName}`)); }); - let packageJsonContent = this.$fs.readJson(this.getPackageJsonFilePath()); + let packageJsonContent = this.$fs.readJson(this.getPackageJsonFilePath(projectData.projectDir)); 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 }); + await this.$npm.install(projectData.projectDir, projectData.projectDir, { "ignore-scripts": this.$options.ignoreScripts }); } } - public async getAllInstalledPlugins(): Promise { - let nodeModules = (await this.getAllInstalledModules()).map(nodeModuleData => this.convertToPluginData(nodeModuleData)); + public async getAllInstalledPlugins(projectData: IProjectData): Promise { + let nodeModules = (await this.getAllInstalledModules(projectData)).map(nodeModuleData => this.convertToPluginData(nodeModuleData, projectData.projectDir)); return _.filter(nodeModules, nodeModuleData => nodeModuleData && nodeModuleData.isPlugin); } - public getDependenciesFromPackageJson(): IPackageJsonDepedenciesResult { - let packageJson = this.$fs.readJson(this.getPackageJsonFilePath()); + public getDependenciesFromPackageJson(projectDir: string): IPackageJsonDepedenciesResult { + let packageJson = this.$fs.readJson(this.getPackageJsonFilePath(projectDir)); let dependencies: IBasePluginData[] = this.getBasicPluginInformation(packageJson.dependencies); let devDependencies: IBasePluginData[] = this.getBasicPluginInformation(packageJson.devDependencies); @@ -185,26 +182,26 @@ export class PluginsService implements IPluginsService { })); } - private get nodeModulesPath(): string { - return path.join(this.$projectData.projectDir, "node_modules"); + private getNodeModulesPath(projectDir: string): string { + return path.join(projectDir, "node_modules"); } - private getPackageJsonFilePath(): string { - return path.join(this.$projectData.projectDir, "package.json"); + private getPackageJsonFilePath(projectDir: string): string { + return path.join(projectDir, "package.json"); } - private getPackageJsonFilePathForModule(moduleName: string): string { - return path.join(this.nodeModulesPath, moduleName, "package.json"); + private getPackageJsonFilePathForModule(moduleName: string, projectDir: string): string { + return path.join(this.getNodeModulesPath(projectDir), moduleName, "package.json"); } - private getDependencies(): string[] { - let packageJsonFilePath = this.getPackageJsonFilePath(); + private getDependencies(projectDir: string): string[] { + let packageJsonFilePath = this.getPackageJsonFilePath(projectDir); return _.keys(require(packageJsonFilePath).dependencies); } - private getNodeModuleData(module: string): INodeModuleData { // module can be modulePath or moduleName + private getNodeModuleData(module: string, projectDir: string): INodeModuleData { // module can be modulePath or moduleName if (!this.$fs.exists(module) || path.basename(module) !== "package.json") { - module = this.getPackageJsonFilePathForModule(module); + module = this.getPackageJsonFilePathForModule(module, projectDir); } let data = this.$fs.readJson(module); @@ -217,11 +214,11 @@ export class PluginsService implements IPluginsService { }; } - private convertToPluginData(cacheData: any): IPluginData { + private convertToPluginData(cacheData: any, projectDir: string): IPluginData { let pluginData: any = {}; pluginData.name = cacheData.name; pluginData.version = cacheData.version; - pluginData.fullPath = cacheData.directory || path.dirname(this.getPackageJsonFilePathForModule(cacheData.name)); + pluginData.fullPath = cacheData.directory || path.dirname(this.getPackageJsonFilePathForModule(cacheData.name, projectDir)); pluginData.isPlugin = !!cacheData.nativescript || !!cacheData.moduleInfo; pluginData.pluginPlatformsFolderPath = (platform: string) => path.join(pluginData.fullPath, "platforms", platform); let data = cacheData.nativescript || cacheData.moduleInfo; @@ -234,23 +231,23 @@ export class PluginsService implements IPluginsService { return pluginData; } - private async ensure(): Promise { - await this.ensureAllDependenciesAreInstalled(); - this.$fs.ensureDirectoryExists(this.nodeModulesPath); + private async ensure(projectData: IProjectData): Promise { + await this.ensureAllDependenciesAreInstalled(projectData); + this.$fs.ensureDirectoryExists(this.getNodeModulesPath(projectData.projectDir)); } - private async getAllInstalledModules(): Promise { - await this.ensure(); + private async getAllInstalledModules(projectData: IProjectData): Promise { + await this.ensure(projectData); - let nodeModules = this.getDependencies(); - return _.map(nodeModules, nodeModuleName => this.getNodeModuleData(nodeModuleName)); + let nodeModules = this.getDependencies(projectData.projectDir); + return _.map(nodeModules, nodeModuleName => this.getNodeModuleData(nodeModuleName, projectData.projectDir)); } - private async executeNpmCommand(npmCommandName: string, npmCommandArguments: string): Promise { + private async executeNpmCommand(npmCommandName: string, npmCommandArguments: string, projectData: IProjectData): Promise { if (npmCommandName === PluginsService.INSTALL_COMMAND_NAME) { - await this.$npm.install(npmCommandArguments, this.$projectData.projectDir, PluginsService.NPM_CONFIG); + await this.$npm.install(npmCommandArguments, projectData.projectDir, PluginsService.NPM_CONFIG); } else if (npmCommandName === PluginsService.UNINSTALL_COMMAND_NAME) { - await this.$npm.uninstall(npmCommandArguments, PluginsService.NPM_CONFIG, this.$projectData.projectDir); + await this.$npm.uninstall(npmCommandArguments, PluginsService.NPM_CONFIG, projectData.projectDir); } return this.parseNpmCommandResult(npmCommandArguments); @@ -260,28 +257,28 @@ export class PluginsService implements IPluginsService { return npmCommandResult.split("@")[0]; // returns plugin name } - private async executeForAllInstalledPlatforms(action: (_pluginDestinationPath: string, pl: string, _platformData: IPlatformData) => Promise): Promise { + private async executeForAllInstalledPlatforms(action: (_pluginDestinationPath: string, pl: string, _platformData: IPlatformData) => Promise, projectData: IProjectData): Promise { let availablePlatforms = _.keys(this.$platformsData.availablePlatforms); for (let platform of availablePlatforms) { - let isPlatformInstalled = this.$fs.exists(path.join(this.$projectData.platformsDir, platform.toLowerCase())); + let isPlatformInstalled = this.$fs.exists(path.join(projectData.platformsDir, platform.toLowerCase())); if (isPlatformInstalled) { - let platformData = this.$platformsData.getPlatformData(platform.toLowerCase()); + let platformData = this.$platformsData.getPlatformData(platform.toLowerCase(), projectData); let pluginDestinationPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, "tns_modules"); await action(pluginDestinationPath, platform.toLowerCase(), platformData); } }; } - private getInstalledFrameworkVersion(platform: string): string { - let platformData = this.$platformsData.getPlatformData(platform); - const frameworkData = this.$projectDataService.getNSValue(this.$projectData.projectDir, platformData.frameworkPackageName); + private getInstalledFrameworkVersion(platform: string, projectData: IProjectData): string { + let platformData = this.$platformsData.getPlatformData(platform, projectData); + const frameworkData = this.$projectDataService.getNSValue(projectData.projectDir, platformData.frameworkPackageName); return frameworkData.version; } - private isPluginDataValidForPlatform(pluginData: IPluginData, platform: string): boolean { + private isPluginDataValidForPlatform(pluginData: IPluginData, platform: string, projectData: IProjectData): boolean { let isValid = true; - let installedFrameworkVersion = this.getInstalledFrameworkVersion(platform); + let installedFrameworkVersion = this.getInstalledFrameworkVersion(platform, projectData); let pluginPlatformsData = pluginData.platformsData; if (pluginPlatformsData) { let pluginVersion = (pluginPlatformsData)[platform]; diff --git a/lib/services/project-changes-service.ts b/lib/services/project-changes-service.ts index 27fd8d69db..1c454ffbf2 100644 --- a/lib/services/project-changes-service.ts +++ b/lib/services/project-changes-service.ts @@ -37,7 +37,6 @@ export class ProjectChangesService implements IProjectChangesService { constructor( private $platformsData: IPlatformsData, - private $projectData: IProjectData, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, private $options: IOptions, private $fs: IFileSystem) { @@ -47,26 +46,26 @@ export class ProjectChangesService implements IProjectChangesService { return this._changesInfo; } - public checkForChanges(platform: string): IProjectChangesInfo { - let platformData = this.$platformsData.getPlatformData(platform); + public checkForChanges(platform: string, projectData: IProjectData): IProjectChangesInfo { + let platformData = this.$platformsData.getPlatformData(platform, projectData); this._changesInfo = new ProjectChangesInfo(); - if (!this.ensurePrepareInfo(platform)) { + if (!this.ensurePrepareInfo(platform, projectData)) { this._newFiles = 0; - this._changesInfo.appFilesChanged = this.containsNewerFiles(this.$projectData.appDirectoryPath, this.$projectData.appResourcesDirectoryPath); - this._changesInfo.packageChanged = this.filesChanged([path.join(this.$projectData.projectDir, "package.json")]); - this._changesInfo.appResourcesChanged = this.containsNewerFiles(this.$projectData.appResourcesDirectoryPath, null); + this._changesInfo.appFilesChanged = this.containsNewerFiles(projectData.appDirectoryPath, projectData.appResourcesDirectoryPath, projectData); + this._changesInfo.packageChanged = this.filesChanged([path.join(projectData.projectDir, "package.json")]); + this._changesInfo.appResourcesChanged = this.containsNewerFiles(projectData.appResourcesDirectoryPath, null, projectData); /*done because currently all node_modules are traversed, a possible improvement could be traversing only the production dependencies*/ this._changesInfo.nativeChanged = this.containsNewerFiles( - path.join(this.$projectData.projectDir, NODE_MODULES_FOLDER_NAME), - path.join(this.$projectData.projectDir, NODE_MODULES_FOLDER_NAME, "tns-ios-inspector"), + path.join(projectData.projectDir, NODE_MODULES_FOLDER_NAME), + path.join(projectData.projectDir, NODE_MODULES_FOLDER_NAME, "tns-ios-inspector"), + projectData, this.fileChangeRequiresBuild); if (this._newFiles > 0) { this._changesInfo.modulesChanged = true; } - let platformResourcesDir = path.join(this.$projectData.appResourcesDirectoryPath, platformData.normalizedPlatformName); + let platformResourcesDir = path.join(projectData.appResourcesDirectoryPath, platformData.normalizedPlatformName); if (platform === this.$devicePlatformsConstants.iOS.toLowerCase()) { - this._changesInfo.configChanged = this.filesChanged([ - this.$options.baseConfig || path.join(platformResourcesDir, platformData.configurationFileName), + this._changesInfo.configChanged = this.filesChanged([path.join(platformResourcesDir, platformData.configurationFileName), path.join(platformResourcesDir, "LaunchScreen.storyboard"), path.join(platformResourcesDir, "build.xcconfig") ]); @@ -111,14 +110,14 @@ export class ProjectChangesService implements IProjectChangesService { return this._changesInfo; } - public getPrepareInfoFilePath(platform: string): string { - let platformData = this.$platformsData.getPlatformData(platform); + public getPrepareInfoFilePath(platform: string, projectData: IProjectData): string { + let platformData = this.$platformsData.getPlatformData(platform, projectData); let prepareInfoFilePath = path.join(platformData.projectRoot, prepareInfoFileName); return prepareInfoFilePath; } - public getPrepareInfo(platform: string): IPrepareInfo { - let prepareInfoFilePath = this.getPrepareInfoFilePath(platform); + public getPrepareInfo(platform: string, projectData: IProjectData): IPrepareInfo { + let prepareInfoFilePath = this.getPrepareInfoFilePath(platform, projectData); let prepareInfo: IPrepareInfo = null; if (this.$fs.exists(prepareInfoFilePath)) { try { @@ -130,15 +129,15 @@ export class ProjectChangesService implements IProjectChangesService { return prepareInfo; } - public savePrepareInfo(platform: string): void { - let prepareInfoFilePath = this.getPrepareInfoFilePath(platform); + public savePrepareInfo(platform: string, projectData: IProjectData): void { + let prepareInfoFilePath = this.getPrepareInfoFilePath(platform, projectData); this.$fs.writeJson(prepareInfoFilePath, this._prepareInfo); } - private ensurePrepareInfo(platform: string): boolean { - this._prepareInfo = this.getPrepareInfo(platform); + private ensurePrepareInfo(platform: string, projectData: IProjectData): boolean { + this._prepareInfo = this.getPrepareInfo(platform, projectData); if (this._prepareInfo) { - let platformData = this.$platformsData.getPlatformData(platform); + let platformData = this.$platformsData.getPlatformData(platform, projectData); let prepareInfoFile = path.join(platformData.projectRoot, prepareInfoFileName); this._outputProjectMtime = this.$fs.getFsStats(prepareInfoFile).mtime.getTime(); this._outputProjectCTime = this.$fs.getFsStats(prepareInfoFile).ctime.getTime(); @@ -172,7 +171,7 @@ export class ProjectChangesService implements IProjectChangesService { return false; } - private containsNewerFiles(dir: string, skipDir: string, processFunc?: (filePath: string) => boolean): boolean { + private containsNewerFiles(dir: string, skipDir: string, projectData: IProjectData, processFunc?: (filePath: string, projectData: IProjectData) => boolean): boolean { let files = this.$fs.readDirectory(dir); for (let file of files) { let filePath = path.join(dir, file); @@ -191,8 +190,8 @@ export class ProjectChangesService implements IProjectChangesService { if (changed) { if (processFunc) { this._newFiles++; - let filePathRelative = path.relative(this.$projectData.projectDir, filePath); - if (processFunc.call(this, filePathRelative)) { + let filePathRelative = path.relative(projectData.projectDir, filePath); + if (processFunc.call(this, filePathRelative, projectData)) { return true; } } else { @@ -201,7 +200,7 @@ export class ProjectChangesService implements IProjectChangesService { } if (fileStats.isDirectory()) { - if (this.containsNewerFiles(filePath, skipDir, processFunc)) { + if (this.containsNewerFiles(filePath, skipDir, projectData, processFunc)) { return true; } } @@ -209,12 +208,12 @@ export class ProjectChangesService implements IProjectChangesService { return false; } - private fileChangeRequiresBuild(file: string) { + private fileChangeRequiresBuild(file: string, projectData: IProjectData) { if (path.basename(file) === "package.json") { return true; } - let projectDir = this.$projectData.projectDir; - if (_.startsWith(path.join(projectDir, file), this.$projectData.appResourcesDirectoryPath)) { + let projectDir = projectData.projectDir; + if (_.startsWith(path.join(projectDir, file), projectData.appResourcesDirectoryPath)) { return true; } if (_.startsWith(file, NODE_MODULES_FOLDER_NAME)) { diff --git a/lib/services/test-execution-service.ts b/lib/services/test-execution-service.ts index 668b74a905..e5a0985ecd 100644 --- a/lib/services/test-execution-service.ts +++ b/lib/services/test-execution-service.ts @@ -13,7 +13,6 @@ class TestExecutionService implements ITestExecutionService { private static SOCKETIO_JS_FILE_NAME = `node_modules/${constants.TEST_RUNNER_NAME}/socket.io.js`; constructor(private $injector: IInjector, - private $projectData: IProjectData, private $platformService: IPlatformService, private $platformsData: IPlatformsData, private $usbLiveSyncService: ILiveSyncService, @@ -33,14 +32,14 @@ class TestExecutionService implements ITestExecutionService { public platform: string; - public async startTestRunner(platform: string): Promise { + public async startTestRunner(platform: string, projectData: IProjectData): 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; + let platformData = this.$platformsData.getPlatformData(platform.toLowerCase(), projectData); + let projectDir = projectData.projectDir; await this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }); let projectFilesPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); @@ -54,18 +53,29 @@ class TestExecutionService implements ITestExecutionService { 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)) { + const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; + if (!await this.$platformService.preparePlatform(platform, appFilesUpdaterOptions, this.$options.platformTemplate, projectData, this.$options.provision)) { this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation."); } this.detourEntryPoint(projectFilesPath); - await this.$platformService.deployPlatform(platform); - await this.$usbLiveSyncService.liveSync(platform); + const deployOptions: IDeployPlatformOptions = { + clean: this.$options.clean, + device: this.$options.device, + projectDir: this.$options.path, + emulator: this.$options.emulator, + platformTemplate: this.$options.platformTemplate, + release: this.$options.release, + provision: this.$options.provision, + teamId: this.$options.teamId + }; + await this.$platformService.deployPlatform(platform, appFilesUpdaterOptions, deployOptions, projectData, this.$options.provision); + await this.$usbLiveSyncService.liveSync(platform, projectData); if (this.$options.debugBrk) { this.$logger.info('Starting debugger...'); let debugService: IDebugService = this.$injector.resolve(`${platform}DebugService`); - await debugService.debugStart(); + await debugService.debugStart(projectData); } resolve(); } catch (err) { @@ -78,7 +88,7 @@ class TestExecutionService implements ITestExecutionService { }); } - public async startKarmaServer(platform: string): Promise { + public async startKarmaServer(platform: string, projectData: IProjectData): Promise { platform = platform.toLowerCase(); this.platform = platform; @@ -87,12 +97,12 @@ class TestExecutionService implements ITestExecutionService { } // We need the dependencies installed here, so we can start the Karma server. - await this.$pluginsService.ensureAllDependenciesAreInstalled(); + await this.$pluginsService.ensureAllDependenciesAreInstalled(projectData); - let projectDir = this.$projectData.projectDir; + let projectDir = projectData.projectDir; await this.$devicesService.initialize({ platform: platform, deviceId: this.$options.device }); - let karmaConfig = this.getKarmaConfiguration(platform), + let karmaConfig = this.getKarmaConfiguration(platform, projectData), karmaRunner = this.$childProcess.fork(path.join(__dirname, "karma-execution.js")), launchKarmaTests = async (karmaData: any) => { this.$logger.trace("## Unit-testing: Parent process received message", karmaData); @@ -110,17 +120,29 @@ class TestExecutionService implements ITestExecutionService { this.$fs.writeFile(path.join(projectDir, TestExecutionService.CONFIG_FILE_NAME), configJs); } + const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; // Prepare the project AFTER the TestExecutionService.CONFIG_FILE_NAME file is created in node_modules // so it will be sent to device. - if (!await this.$platformService.preparePlatform(platform)) { + if (!await this.$platformService.preparePlatform(platform, appFilesUpdaterOptions, this.$options.platformTemplate, projectData, this.$options.provision)) { this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation."); } if (this.$options.debugBrk) { - await this.getDebugService(platform).debug(); + await this.getDebugService(platform).debug(projectData); } else { - await this.$platformService.deployPlatform(platform); - await this.$usbLiveSyncService.liveSync(platform); + const deployOptions: IDeployPlatformOptions = { + clean: this.$options.clean, + device: this.$options.device, + emulator: this.$options.emulator, + projectDir: this.$options.path, + platformTemplate: this.$options.platformTemplate, + release: this.$options.release, + provision: this.$options.provision, + teamId: this.$options.teamId + }; + + await this.$platformService.deployPlatform(platform, appFilesUpdaterOptions, deployOptions, projectData, this.$options.provision); + await this.$usbLiveSyncService.liveSync(platform, projectData); } }; @@ -184,10 +206,10 @@ class TestExecutionService implements ITestExecutionService { throw new Error(`Invalid platform ${platform}. Valid platforms are ${this.$devicePlatformsConstants.iOS} and ${this.$devicePlatformsConstants.Android}`); } - private getKarmaConfiguration(platform: string): any { + private getKarmaConfiguration(platform: string, projectData: IProjectData): any { let karmaConfig: any = { browsers: [platform], - configFile: path.join(this.$projectData.projectDir, 'karma.conf.js'), + configFile: path.join(projectData.projectDir, 'karma.conf.js'), _NS: { log: this.$logger.getLevel(), path: this.$options.path, @@ -214,7 +236,7 @@ class TestExecutionService implements ITestExecutionService { karmaConfig.browserNoActivityTimeout = 1000000000; } - karmaConfig.projectDir = this.$projectData.projectDir; + karmaConfig.projectDir = projectData.projectDir; this.$logger.debug(JSON.stringify(karmaConfig, null, 4)); return karmaConfig; diff --git a/lib/services/versions-service.ts b/lib/services/versions-service.ts index 7ce012e892..2334713cd8 100644 --- a/lib/services/versions-service.ts +++ b/lib/services/versions-service.ts @@ -41,7 +41,7 @@ class VersionsService implements IVersionsService { let tnsCoreModulesPath = path.join(nodeModulesPath, constants.TNS_CORE_MODULES_NAME); if (!this.$fs.exists(nodeModulesPath) || !this.$fs.exists(tnsCoreModulesPath)) { - await this.$pluginsService.ensureAllDependenciesAreInstalled(); + await this.$pluginsService.ensureAllDependenciesAreInstalled(this.projectData); } let currentTnsCoreModulesVersion = this.$fs.readJson(path.join(tnsCoreModulesPath, constants.PACKAGE_JSON_FILE_NAME)).version; @@ -142,7 +142,9 @@ class VersionsService implements IVersionsService { private getProjectData(): IProjectData { try { - return this.$injector.resolve("projectData"); + let projectData: IProjectData = this.$injector.resolve("projectData"); + projectData.initializeProjectData(); + return projectData; } catch (error) { return null; } diff --git a/lib/tools/node-modules/node-modules-builder.ts b/lib/tools/node-modules/node-modules-builder.ts index 7d3edb6139..2c2893dfb6 100644 --- a/lib/tools/node-modules/node-modules-builder.ts +++ b/lib/tools/node-modules/node-modules-builder.ts @@ -9,14 +9,13 @@ let glob = require("glob"); export class NodeModulesBuilder implements INodeModulesBuilder { constructor(private $fs: IFileSystem, - private $projectData: IProjectData, private $injector: IInjector, private $lockfile: ILockFile, private $options: IOptions ) { } - public async getChangedNodeModules(absoluteOutputPath: string, platform: string, lastModifiedTime?: Date): Promise { - let projectDir = this.$projectData.projectDir; + public async getChangedNodeModules(absoluteOutputPath: string, platform: string, projectData: IProjectData, lastModifiedTime?: Date): Promise { + let projectDir = projectData.projectDir; let isNodeModulesModified = false; let nodeModulesPath = path.join(projectDir, constants.NODE_MODULES_FOLDER_NAME); let nodeModules: any = {}; @@ -117,14 +116,14 @@ export class NodeModulesBuilder implements INodeModulesBuilder { }); } - public async prepareNodeModules(absoluteOutputPath: string, platform: string, lastModifiedTime: Date): Promise { + public async prepareNodeModules(absoluteOutputPath: string, platform: string, lastModifiedTime: Date, projectData: IProjectData): 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); + let productionDependencies = dependenciesBuilder.getProductionDependencies(projectData.projectDir); if (!this.$options.bundle) { const tnsModulesCopy = this.$injector.resolve(TnsModulesCopy, { @@ -136,7 +135,7 @@ export class NodeModulesBuilder implements INodeModulesBuilder { } const npmPluginPrepare: NpmPluginPrepare = this.$injector.resolve(NpmPluginPrepare); - await npmPluginPrepare.preparePlugins(productionDependencies, platform); + await npmPluginPrepare.preparePlugins(productionDependencies, platform, projectData); } public cleanNodeModules(absoluteOutputPath: string, platform: string): void { diff --git a/lib/tools/node-modules/node-modules-dest-copy.ts b/lib/tools/node-modules/node-modules-dest-copy.ts index 120215e1eb..de313c1f66 100644 --- a/lib/tools/node-modules/node-modules-dest-copy.ts +++ b/lib/tools/node-modules/node-modules-dest-copy.ts @@ -61,26 +61,26 @@ export class NpmPluginPrepare { ) { } - protected async beforePrepare(dependencies: IDictionary, platform: string): Promise { - await this.$platformsData.getPlatformData(platform).platformProjectService.beforePrepareAllPlugins(dependencies); + protected async beforePrepare(dependencies: IDictionary, platform: string, projectData: IProjectData): Promise { + await this.$platformsData.getPlatformData(platform, projectData).platformProjectService.beforePrepareAllPlugins(projectData, dependencies); } - protected async afterPrepare(dependencies: IDictionary, platform: string): Promise { - await this.$platformsData.getPlatformData(platform).platformProjectService.afterPrepareAllPlugins(); - this.writePreparedDependencyInfo(dependencies, platform); + protected async afterPrepare(dependencies: IDictionary, platform: string, projectData: IProjectData): Promise { + await this.$platformsData.getPlatformData(platform, projectData).platformProjectService.afterPrepareAllPlugins(projectData); + this.writePreparedDependencyInfo(dependencies, platform, projectData); } - private writePreparedDependencyInfo(dependencies: IDictionary, platform: string): void { + private writePreparedDependencyInfo(dependencies: IDictionary, platform: string, projectData: IProjectData): void { let prepareData: IDictionary = {}; _.values(dependencies).forEach(d => { prepareData[d.name] = true; }); - this.$fs.createDirectory(this.preparedPlatformsDir(platform)); - this.$fs.writeJson(this.preparedPlatformsFile(platform), prepareData, " ", "utf8"); + this.$fs.createDirectory(this.preparedPlatformsDir(platform, projectData)); + this.$fs.writeJson(this.preparedPlatformsFile(platform, projectData), prepareData, " ", "utf8"); } - private preparedPlatformsDir(platform: string): string { - const platformRoot = this.$platformsData.getPlatformData(platform).projectRoot; + private preparedPlatformsDir(platform: string, projectData: IProjectData): string { + const platformRoot = this.$platformsData.getPlatformData(platform, projectData).projectRoot; if (/android/i.test(platform)) { return path.join(platformRoot, "build", "intermediates"); } else if (/ios/i.test(platform)) { @@ -90,20 +90,20 @@ export class NpmPluginPrepare { } } - private preparedPlatformsFile(platform: string): string { - return path.join(this.preparedPlatformsDir(platform), "prepared-platforms.json"); + private preparedPlatformsFile(platform: string, projectData: IProjectData): string { + return path.join(this.preparedPlatformsDir(platform, projectData), "prepared-platforms.json"); } - protected getPreviouslyPreparedDependencies(platform: string): IDictionary { - if (!this.$fs.exists(this.preparedPlatformsFile(platform))) { + protected getPreviouslyPreparedDependencies(platform: string, projectData: IProjectData): IDictionary { + if (!this.$fs.exists(this.preparedPlatformsFile(platform, projectData))) { return {}; } - return this.$fs.readJson(this.preparedPlatformsFile(platform), "utf8"); + return this.$fs.readJson(this.preparedPlatformsFile(platform, projectData), "utf8"); } - private allPrepared(dependencies: IDictionary, platform: string): boolean { + private allPrepared(dependencies: IDictionary, platform: string, projectData: IProjectData): boolean { let result = true; - const previouslyPrepared = this.getPreviouslyPreparedDependencies(platform); + const previouslyPrepared = this.getPreviouslyPreparedDependencies(platform, projectData); _.values(dependencies).forEach(d => { if (!previouslyPrepared[d.name]) { result = false; @@ -112,20 +112,20 @@ export class NpmPluginPrepare { return result; } - public async preparePlugins(dependencies: IDictionary, platform: string): Promise { - if (_.isEmpty(dependencies) || this.allPrepared(dependencies, platform)) { + public async preparePlugins(dependencies: IDictionary, platform: string, projectData: IProjectData): Promise { + if (_.isEmpty(dependencies) || this.allPrepared(dependencies, platform, projectData)) { return; } - await this.beforePrepare(dependencies, platform); + await this.beforePrepare(dependencies, platform, projectData); for (let dependencyKey in dependencies) { const dependency = dependencies[dependencyKey]; let isPlugin = !!dependency.nativescript; if (isPlugin) { - await this.$pluginsService.prepare(dependency, platform); + await this.$pluginsService.prepare(dependency, platform, projectData); } } - await this.afterPrepare(dependencies, platform); + await this.afterPrepare(dependencies, platform, projectData); } } diff --git a/test/debug.ts b/test/debug.ts index 6c6061105c..1efdd6d5eb 100644 --- a/test/debug.ts +++ b/test/debug.ts @@ -34,7 +34,7 @@ function createTestInjector(): IInjector { testInjector.register("androidProjectService", AndroidProjectService); testInjector.register("androidToolsInfo", stubs.AndroidToolsInfoStub); testInjector.register("hostInfo", {}); - testInjector.register("projectData", { platformsDir: "" }); + testInjector.register("projectData", { platformsDir: "test", initializeProjectData: () => { /* empty */ } }); testInjector.register("projectDataService", {}); testInjector.register("sysInfo", {}); testInjector.register("mobileHelper", {}); @@ -78,8 +78,9 @@ describe("Debugger tests", () => { config.debugLivesync = true; let childProcess: stubs.ChildProcessStub = testInjector.resolve("childProcess"); let androidProjectService: IPlatformProjectService = testInjector.resolve("androidProjectService"); + let projectData: IProjectData = testInjector.resolve("projectData"); let spawnFromEventCount = childProcess.spawnFromEventCount; - await androidProjectService.beforePrepareAllPlugins(); + await androidProjectService.beforePrepareAllPlugins(projectData); assert.isTrue(spawnFromEventCount === 0); assert.isTrue(spawnFromEventCount === childProcess.spawnFromEventCount); }); @@ -89,8 +90,9 @@ describe("Debugger tests", () => { config.debugLivesync = false; let childProcess: stubs.ChildProcessStub = testInjector.resolve("childProcess"); let androidProjectService: IPlatformProjectService = testInjector.resolve("androidProjectService"); + let projectData: IProjectData = testInjector.resolve("projectData"); let spawnFromEventCount = childProcess.spawnFromEventCount; - await androidProjectService.beforePrepareAllPlugins(); + await androidProjectService.beforePrepareAllPlugins(projectData); assert.isTrue(childProcess.lastCommand.indexOf("gradle") !== -1); assert.isTrue(childProcess.lastCommandArgs[0] === "clean"); assert.isTrue(spawnFromEventCount === 0); diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index fc5e4352e4..979760468e 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -126,6 +126,7 @@ describe("iOSProjectService", () => { let testInjector = createTestInjector(projectPath, projectName); let iOSProjectService = testInjector.resolve("iOSProjectService"); + let projectData: IProjectData = testInjector.resolve("projectData"); let childProcess = testInjector.resolve("childProcess"); let xcodebuildExeced = false; @@ -156,9 +157,9 @@ describe("iOSProjectService", () => { return { run: async (): Promise => { if (hasCustomArchivePath) { - resultArchivePath = await iOSProjectService.archive({ archivePath: options.archivePath }); + resultArchivePath = await iOSProjectService.archive(projectData, { archivePath: options.archivePath }); } else { - resultArchivePath = await iOSProjectService.archive(); + resultArchivePath = await iOSProjectService.archive(projectData); } }, assert: () => { @@ -214,6 +215,7 @@ describe("iOSProjectService", () => { let testInjector = createTestInjector(projectPath, projectName); let iOSProjectService = testInjector.resolve("iOSProjectService"); + let projectData: IProjectData = testInjector.resolve("projectData"); let archivePath = path.join(projectPath, "platforms", "ios", "build", "archive", projectName + ".xcarchive"); @@ -241,7 +243,7 @@ describe("iOSProjectService", () => { return Promise.resolve(); }; - let resultIpa = await iOSProjectService.exportArchive({ archivePath, teamID: options.teamID }); + let resultIpa = await iOSProjectService.exportArchive(projectData, { 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"); @@ -311,8 +313,9 @@ describe("Cocoapods support", () => { return pluginPlatformsFolderPath; } }; + let projectData: IProjectData = testInjector.resolve("projectData"); - await iOSProjectService.preparePluginNativeCode(pluginData); + await iOSProjectService.preparePluginNativeCode(pluginData, projectData); let projectPodfilePath = path.join(platformsFolderPath, "Podfile"); assert.isTrue(fs.exists(projectPodfilePath)); @@ -381,8 +384,9 @@ describe("Cocoapods support", () => { return pluginPlatformsFolderPath; } }; + let projectData: IProjectData = testInjector.resolve("projectData"); - await iOSProjectService.preparePluginNativeCode(pluginData); + await iOSProjectService.preparePluginNativeCode(pluginData, projectData); let projectPodfilePath = path.join(platformsFolderPath, "Podfile"); assert.isTrue(fs.exists(projectPodfilePath)); @@ -397,7 +401,7 @@ describe("Cocoapods support", () => { .join("\n"); assert.equal(actualProjectPodfileContent, expectedProjectPodfileContent); - await iOSProjectService.removePluginNativeCode(pluginData); + await iOSProjectService.removePluginNativeCode(pluginData, projectData); assert.isFalse(fs.exists(projectPodfilePath)); }); @@ -474,8 +478,9 @@ describe("Relative paths", () => { let testInjector = createTestInjector(projectPath, projectName); createPackageJson(testInjector, projectPath, projectName); let iOSProjectService = testInjector.resolve("iOSProjectService"); + let projectData: IProjectData = testInjector.resolve("projectData"); - let result = iOSProjectService.getLibSubpathRelativeToProjectPath(subpath); + let result = iOSProjectService.getLibSubpathRelativeToProjectPath(subpath, projectData); assert.equal(result, path.join("..", "..", "sub", "path")); }); }); diff --git a/test/nativescript-cli-lib.ts b/test/nativescript-cli-lib.ts index 6281c29539..fc2162d217 100644 --- a/test/nativescript-cli-lib.ts +++ b/test/nativescript-cli-lib.ts @@ -14,7 +14,9 @@ describe("nativescript-cli-lib", () => { const publicApi: any = { deviceEmitter: null, - projectService: ["createProject"] + projectService: ["createProject"], + localBuildService: ["build"] + }; const pathToEntryPoint = path.join(__dirname, "..", "lib", "nativescript-cli-lib.js").replace(/\\/g, "\\\\"); diff --git a/test/npm-support.ts b/test/npm-support.ts index 652cf169f2..4ee208d307 100644 --- a/test/npm-support.ts +++ b/test/npm-support.ts @@ -184,8 +184,11 @@ async function addDependencies(testInjector: IInjector, projectFolder: string, d } async function preparePlatform(testInjector: IInjector): Promise { - let platformService = testInjector.resolve("platformService"); - return platformService.preparePlatform("android"); + let platformService: IPlatformService = testInjector.resolve("platformService"); + let projectData: IProjectData = testInjector.resolve("projectData"); + projectData.initializeProjectData(); + + await platformService.preparePlatform("android", { bundle: false, release: false }, "", projectData, undefined); } describe("Npm support tests", () => { diff --git a/test/platform-service.ts b/test/platform-service.ts index b287b21b44..23dfea7f4e 100644 --- a/test/platform-service.ts +++ b/test/platform-service.ts @@ -96,21 +96,23 @@ describe('Platform Service Tests', () => { it("should not fail if platform is not normalized", async () => { let fs = testInjector.resolve("fs"); fs.exists = () => false; + let projectData: IProjectData = testInjector.resolve("projectData"); - await platformService.addPlatforms(["Android"]); - await platformService.addPlatforms(["ANDROID"]); - await platformService.addPlatforms(["AnDrOiD"]); - await platformService.addPlatforms(["androiD"]); + await platformService.addPlatforms(["Android"], "", projectData); + await platformService.addPlatforms(["ANDROID"], "", projectData); + await platformService.addPlatforms(["AnDrOiD"], "", projectData); + await platformService.addPlatforms(["androiD"], "", projectData); - await platformService.addPlatforms(["iOS"]); - await platformService.addPlatforms(["IOS"]); - await platformService.addPlatforms(["IoS"]); - await platformService.addPlatforms(["iOs"]); + await platformService.addPlatforms(["iOS"], "", projectData); + await platformService.addPlatforms(["IOS"], "", projectData); + await platformService.addPlatforms(["IoS"], "", projectData); + await platformService.addPlatforms(["iOs"], "", projectData); }); it("should fail if platform is already installed", async () => { + let projectData: IProjectData = testInjector.resolve("projectData"); // By default fs.exists returns true, so the platforms directory should exists - await assert.isRejected(platformService.addPlatforms(["android"])); - await assert.isRejected(platformService.addPlatforms(["ios"])); + await assert.isRejected(platformService.addPlatforms(["android"], "", projectData)); + await assert.isRejected(platformService.addPlatforms(["ios"], "", projectData)); }); it("should fail if npm is unavalible", async () => { let fs = testInjector.resolve("fs"); @@ -119,9 +121,10 @@ describe('Platform Service Tests', () => { let errorMessage = "Npm is unavalible"; let npmInstallationManager = testInjector.resolve("npmInstallationManager"); npmInstallationManager.install = () => { throw new Error(errorMessage); }; + let projectData: IProjectData = testInjector.resolve("projectData"); try { - await platformService.addPlatforms(["android"]); + await platformService.addPlatforms(["android"], "", projectData); } catch (err) { assert.equal(errorMessage, err.message); } @@ -135,12 +138,13 @@ describe('Platform Service Tests', () => { let errorMessage = "Xcode is not installed or Xcode version is smaller that 5.0"; let platformsData = testInjector.resolve("platformsData"); let platformProjectService = platformsData.getPlatformData().platformProjectService; + let projectData: IProjectData = testInjector.resolve("projectData"); platformProjectService.validate = () => { throw new Error(errorMessage); }; try { - await platformService.addPlatforms(["ios"]); + await platformService.addPlatforms(["ios"], "", projectData); } catch (err) { assert.equal(errorMessage, err.message); } @@ -157,9 +161,10 @@ describe('Platform Service Tests', () => { platformProjectService.validate = () => { throw new Error(errorMessage); }; + let projectData: IProjectData = testInjector.resolve("projectData"); try { - await platformService.addPlatforms(["android"]); + await platformService.addPlatforms(["android"], "", projectData); } catch (err) { assert.equal(errorMessage, err.message); } @@ -171,17 +176,17 @@ describe('Platform Service Tests', () => { it("should fail when platforms are not added", async () => { const ExpectedErrorsCaught = 2; let errorsCaught = 0; - + let projectData: IProjectData = testInjector.resolve("projectData"); testInjector.resolve("fs").exists = () => false; try { - await platformService.removePlatforms(["android"]); + await platformService.removePlatforms(["android"], projectData); } catch (e) { errorsCaught++; } try { - await platformService.removePlatforms(["ios"]); + await platformService.removePlatforms(["ios"], projectData); } catch (e) { errorsCaught++; } @@ -189,11 +194,12 @@ describe('Platform Service Tests', () => { assert.isTrue(errorsCaught === ExpectedErrorsCaught); }); it("shouldn't fail when platforms are added", async () => { + let projectData: IProjectData = testInjector.resolve("projectData"); testInjector.resolve("fs").exists = () => false; - await platformService.addPlatforms(["android"]); + await platformService.addPlatforms(["android"], "", projectData); testInjector.resolve("fs").exists = () => true; - platformService.removePlatforms(["android"]); + await platformService.removePlatforms(["android"], projectData); }); }); @@ -209,8 +215,9 @@ describe('Platform Service Tests', () => { it("should fail when the versions are the same", async () => { let npmInstallationManager: INpmInstallationManager = testInjector.resolve("npmInstallationManager"); npmInstallationManager.getLatestVersion = async () => "0.2.0"; + let projectData: IProjectData = testInjector.resolve("projectData"); - await assert.isRejected(platformService.updatePlatforms(["android"])); + await assert.isRejected(platformService.updatePlatforms(["android"], "", projectData)); }); }); }); @@ -286,9 +293,8 @@ describe('Platform Service Tests', () => { projectData.projectDir = testDirData.tempFolder; platformService = testInjector.resolve("platformService"); - let options: IOptions = testInjector.resolve("options"); - options.release = release; - await platformService.preparePlatform(platformToTest); + const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: false, release: release }; + await platformService.preparePlatform(platformToTest, appFilesUpdaterOptions, "", projectData, undefined); let test1FileName = platformToTest.toLowerCase() === "ios" ? "test1.js" : "test2.js"; let test2FileName = platformToTest.toLowerCase() === "ios" ? "test2.js" : "test1.js"; @@ -364,7 +370,8 @@ describe('Platform Service Tests', () => { let warnings: string = ""; try { testInjector.resolve("$logger").warn = (text: string) => warnings += text; - await platformService.preparePlatform("android"); + const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: false, release: false }; + await platformService.preparePlatform("android", appFilesUpdaterOptions, "", projectData, undefined); } finally { testInjector.resolve("$logger").warn = oldLoggerWarner; } diff --git a/test/plugin-prepare.ts b/test/plugin-prepare.ts index 60604c9beb..73bcde4131 100644 --- a/test/plugin-prepare.ts +++ b/test/plugin-prepare.ts @@ -28,7 +28,7 @@ class TestNpmPluginPrepare extends NpmPluginPrepare { describe("Plugin preparation", () => { it("skips prepare if no plugins", async () => { const pluginPrepare = new TestNpmPluginPrepare({}); - await pluginPrepare.preparePlugins({}, "android"); + await pluginPrepare.preparePlugins({}, "android", null); assert.deepEqual({}, pluginPrepare.preparedDependencies); }); @@ -41,7 +41,7 @@ describe("Plugin preparation", () => { nativescript: null, } }; - await pluginPrepare.preparePlugins(testDependencies, "android"); + await pluginPrepare.preparePlugins(testDependencies, "android", null); assert.deepEqual({}, pluginPrepare.preparedDependencies); }); @@ -59,7 +59,7 @@ describe("Plugin preparation", () => { nativescript: null, } }; - await pluginPrepare.preparePlugins(testDependencies, "android"); + await pluginPrepare.preparePlugins(testDependencies, "android", null); 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 871510c1d2..3576c4d284 100644 --- a/test/plugin-variables-service.ts +++ b/test/plugin-variables-service.ts @@ -90,13 +90,15 @@ describe("Plugin Variables service", () => { let pluginVariables = { "MY_TEST_PLUGIN_VARIABLE": {} }; let pluginData = createPluginData(pluginVariables); - let pluginVariablesService = testInjector.resolve("pluginVariablesService"); + let pluginVariablesService: IPluginVariablesService = testInjector.resolve("pluginVariablesService"); + let projectData: IProjectData = testInjector.resolve("projectData"); + projectData.initializeProjectData(); let expectedError = `Unable to find value for MY_TEST_PLUGIN_VARIABLE plugin variable from ${pluginData.name} plugin. Ensure the --var option is specified or the plugin variable has default value.`; let actualError: string = null; try { - await pluginVariablesService.savePluginVariablesInProjectFile(pluginData); + await pluginVariablesService.savePluginVariablesInProjectFile(pluginData, projectData); } catch (err) { actualError = err.message; } @@ -113,11 +115,12 @@ describe("Plugin Variables service", () => { let pluginVariables = { "MY_APP_ID": {} }; let pluginData = createPluginData(pluginVariables); - let pluginVariablesService = testInjector.resolve("pluginVariablesService"); - await pluginVariablesService.savePluginVariablesInProjectFile(pluginData); + let pluginVariablesService: IPluginVariablesService = testInjector.resolve("pluginVariablesService"); + let projectData: IProjectData = testInjector.resolve("projectData"); + projectData.initializeProjectData(); + await pluginVariablesService.savePluginVariablesInProjectFile(pluginData, projectData); let fs = testInjector.resolve("fs"); - let projectData = testInjector.resolve("projectData"); let staticConfig: IStaticConfig = testInjector.resolve("staticConfig"); let projectFileContent = fs.readJson(path.join(projectData.projectDir, "package.json")); @@ -129,11 +132,13 @@ describe("Plugin Variables service", () => { let defaultPluginValue = "myDefaultValue"; let pluginVariables = { "MY_TEST_PLUGIN_VARIABLE": { defaultValue: defaultPluginValue } }; let pluginData = createPluginData(pluginVariables); - let pluginVariablesService = testInjector.resolve("pluginVariablesService"); - await pluginVariablesService.savePluginVariablesInProjectFile(pluginData); + let pluginVariablesService: IPluginVariablesService = testInjector.resolve("pluginVariablesService"); + let projectData = testInjector.resolve("projectData"); + projectData.initializeProjectData(); + + await pluginVariablesService.savePluginVariablesInProjectFile(pluginData, projectData); let fs = testInjector.resolve("fs"); - let projectData = testInjector.resolve("projectData"); let staticConfig: IStaticConfig = testInjector.resolve("staticConfig"); let projectFileContent = fs.readJson(path.join(projectData.projectDir, "package.json")); @@ -155,11 +160,12 @@ describe("Plugin Variables service", () => { let pluginVariables = { "APP_URL": {} }; let pluginData = createPluginData(pluginVariables); - let pluginVariablesService = testInjector.resolve("pluginVariablesService"); - await pluginVariablesService.savePluginVariablesInProjectFile(pluginData); + let pluginVariablesService: IPluginVariablesService = testInjector.resolve("pluginVariablesService"); + let projectData = testInjector.resolve("projectData"); + projectData.initializeProjectData(); + await pluginVariablesService.savePluginVariablesInProjectFile(pluginData, projectData); let fs = testInjector.resolve("fs"); - let projectData = testInjector.resolve("projectData"); let staticConfig: IStaticConfig = testInjector.resolve("staticConfig"); let projectFileContent = fs.readJson(path.join(projectData.projectDir, "package.json")); @@ -171,11 +177,13 @@ describe("Plugin Variables service", () => { let defaultPluginValue = "myAppNAme"; let pluginVariables = { "APP_NAME": { defaultValue: defaultPluginValue } }; let pluginData = createPluginData(pluginVariables); - let pluginVariablesService = testInjector.resolve("pluginVariablesService"); - await pluginVariablesService.savePluginVariablesInProjectFile(pluginData); + let pluginVariablesService: IPluginVariablesService = testInjector.resolve("pluginVariablesService"); + let projectData = testInjector.resolve("projectData"); + projectData.initializeProjectData(); + + await pluginVariablesService.savePluginVariablesInProjectFile(pluginData, projectData); let fs = testInjector.resolve("fs"); - let projectData = testInjector.resolve("projectData"); let staticConfig: IStaticConfig = testInjector.resolve("staticConfig"); let projectFileContent = fs.readJson(path.join(projectData.projectDir, "package.json")); @@ -191,11 +199,12 @@ describe("Plugin Variables service", () => { let pluginVariables = { "USERNAME": {} }; let pluginData = createPluginData(pluginVariables); - let pluginVariablesService = testInjector.resolve("pluginVariablesService"); - await pluginVariablesService.savePluginVariablesInProjectFile(pluginData); + let pluginVariablesService: IPluginVariablesService = testInjector.resolve("pluginVariablesService"); + let projectData = testInjector.resolve("projectData"); + projectData.initializeProjectData(); + await pluginVariablesService.savePluginVariablesInProjectFile(pluginData, projectData); let fs = testInjector.resolve("fs"); - let projectData = testInjector.resolve("projectData"); let staticConfig: IStaticConfig = testInjector.resolve("staticConfig"); let projectFileContent = fs.readJson(path.join(projectData.projectDir, "package.json")); @@ -214,12 +223,14 @@ describe("Plugin Variables service", () => { let filePath = path.join(tempFolder, "myfile"); fs.writeFile(filePath, ""); - let pluginVariablesService = testInjector.resolve("pluginVariablesService"); + let pluginVariablesService: IPluginVariablesService = testInjector.resolve("pluginVariablesService"); + let projectData = testInjector.resolve("projectData"); + projectData.initializeProjectData(); 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 { - await pluginVariablesService.interpolatePluginVariables(pluginData, filePath); + await pluginVariablesService.interpolatePluginVariables(pluginData, filePath, projectData); } catch (err) { error = err.message; } @@ -231,6 +242,7 @@ describe("Plugin Variables service", () => { let tempFolder = await createProjectFile(testInjector); let projectData: IProjectData = testInjector.resolve("projectData"); + projectData.initializeProjectData(); let fs: IFileSystem = testInjector.resolve("fs"); // Write plugin variables values to package.json file @@ -243,7 +255,7 @@ describe("Plugin Variables service", () => { let pluginVariables = { "FB_APP_NAME": {} }; let pluginData = createPluginData(pluginVariables); - let pluginVariablesService = testInjector.resolve("pluginVariablesService"); + let pluginVariablesService: IPluginVariablesService = testInjector.resolve("pluginVariablesService"); let pluginConfigurationFileContent = '' + '' + '' + @@ -253,7 +265,7 @@ describe("Plugin Variables service", () => { let filePath = path.join(tempFolder, "myfile"); fs.writeFile(filePath, pluginConfigurationFileContent); - await pluginVariablesService.interpolatePluginVariables(pluginData, filePath); + await pluginVariablesService.interpolatePluginVariables(pluginData, filePath, projectData); let result = fs.readText(filePath); let expectedResult = '' + @@ -270,6 +282,7 @@ describe("Plugin Variables service", () => { let tempFolder = await createProjectFile(testInjector); let projectData: IProjectData = testInjector.resolve("projectData"); + projectData.initializeProjectData(); let fs: IFileSystem = testInjector.resolve("fs"); // Write plugin variables values to package.json file @@ -282,7 +295,7 @@ describe("Plugin Variables service", () => { let pluginVariables = { "FB_APP_NAME": {} }; let pluginData = createPluginData(pluginVariables); - let pluginVariablesService = testInjector.resolve("pluginVariablesService"); + let pluginVariablesService: IPluginVariablesService = testInjector.resolve("pluginVariablesService"); let pluginConfigurationFileContent = '' + '' + '' + @@ -292,7 +305,7 @@ describe("Plugin Variables service", () => { let filePath = path.join(tempFolder, "myfile"); fs.writeFile(filePath, pluginConfigurationFileContent); - await pluginVariablesService.interpolatePluginVariables(pluginData, filePath); + await pluginVariablesService.interpolatePluginVariables(pluginData, filePath, projectData); let result = fs.readText(filePath); let expectedResult = '' + @@ -309,6 +322,7 @@ describe("Plugin Variables service", () => { let tempFolder = await createProjectFile(testInjector); let projectData: IProjectData = testInjector.resolve("projectData"); + projectData.initializeProjectData(); let fs: IFileSystem = testInjector.resolve("fs"); let packageJsonFilePath = path.join(projectData.projectDir, "package.json"); @@ -321,7 +335,7 @@ describe("Plugin Variables service", () => { let pluginVariables = { "FB_APP_NAME": {}, "FB_APP_URL": {} }; let pluginData = createPluginData(pluginVariables); - let pluginVariablesService = testInjector.resolve("pluginVariablesService"); + let pluginVariablesService: IPluginVariablesService = testInjector.resolve("pluginVariablesService"); let pluginConfigurationFileContent = '' + '' + '' + @@ -332,7 +346,7 @@ describe("Plugin Variables service", () => { let filePath = path.join(tempFolder, "myfile"); fs.writeFile(filePath, pluginConfigurationFileContent); - await pluginVariablesService.interpolatePluginVariables(pluginData, filePath); + await pluginVariablesService.interpolatePluginVariables(pluginData, filePath, projectData); let result = fs.readText(filePath); let expectedResult = '' + diff --git a/test/plugins-service.ts b/test/plugins-service.ts index 54bf3424a4..cc440a608d 100644 --- a/test/plugins-service.ts +++ b/test/plugins-service.ts @@ -137,8 +137,8 @@ function mockBeginCommand(testInjector: IInjector, expectedErrorMessage: string) async function addPluginWhenExpectingToFail(testInjector: IInjector, plugin: string, expectedErrorMessage: string, command?: string) { createProjectFile(testInjector); - let pluginsService = testInjector.resolve("pluginsService"); - pluginsService.getAllInstalledPlugins = () => { + let pluginsService: IPluginsService = testInjector.resolve("pluginsService"); + pluginsService.getAllInstalledPlugins = async (projectData: IProjectData) => { return [{ name: "" }]; @@ -210,8 +210,8 @@ describe("Plugins service", () => { projectData.dependencies[pluginName] = "^1.0.0"; fs.writeJson(projectFilePath, projectData); - let pluginsService = testInjector.resolve("pluginsService"); - pluginsService.getAllInstalledPlugins = () => { + let pluginsService: IPluginsService = testInjector.resolve("pluginsService"); + pluginsService.getAllInstalledPlugins = async (projData: IProjectData) => { return [{ name: "plugin1" }]; @@ -258,8 +258,10 @@ describe("Plugins service", () => { }; // Mock pluginsService - let pluginsService = testInjector.resolve("pluginsService"); - pluginsService.getAllInstalledPlugins = () => { + let pluginsService: IPluginsService = testInjector.resolve("pluginsService"); + let projectData: IProjectData = testInjector.resolve("projectData"); + projectData.initializeProjectData(); + pluginsService.getAllInstalledPlugins = async (projData: IProjectData) => { return [{ name: "" }]; @@ -275,7 +277,7 @@ describe("Plugins service", () => { }; }; - await pluginsService.add(pluginFolderPath); + await pluginsService.add(pluginFolderPath, projectData); assert.isTrue(isWarningMessageShown); }); @@ -283,8 +285,8 @@ describe("Plugins service", () => { let pluginName = "plugin1"; let projectFolder = createProjectFile(testInjector); - let pluginsService = testInjector.resolve("pluginsService"); - pluginsService.getAllInstalledPlugins = () => { + let pluginsService: IPluginsService = testInjector.resolve("pluginsService"); + pluginsService.getAllInstalledPlugins = async (projectData: IProjectData) => { return [{ name: "" }]; @@ -318,8 +320,8 @@ describe("Plugins service", () => { let pluginName = "plugin1"; let projectFolder = createProjectFile(testInjector); - let pluginsService = testInjector.resolve("pluginsService"); - pluginsService.getAllInstalledPlugins = () => { + let pluginsService: IPluginsService = testInjector.resolve("pluginsService"); + pluginsService.getAllInstalledPlugins = async (projectData: IProjectData) => { return [{ name: "" }]; @@ -366,8 +368,8 @@ describe("Plugins service", () => { let fs = testInjector.resolve("fs"); fs.writeJson(path.join(pluginFolderPath, "package.json"), pluginJsonData); - let pluginsService = testInjector.resolve("pluginsService"); - pluginsService.getAllInstalledPlugins = () => { + let pluginsService: IPluginsService = testInjector.resolve("pluginsService"); + pluginsService.getAllInstalledPlugins = async (projectData: IProjectData) => { return [{ name: "" }]; @@ -409,8 +411,8 @@ describe("Plugins service", () => { let fs = testInjector.resolve("fs"); fs.writeJson(path.join(pluginFolderPath, "package.json"), pluginJsonData); - let pluginsService = testInjector.resolve("pluginsService"); - pluginsService.getAllInstalledPlugins = () => { + let pluginsService: IPluginsService = testInjector.resolve("pluginsService"); + pluginsService.getAllInstalledPlugins = async (projectData: IProjectData) => { return [{ name: "" }]; @@ -449,8 +451,8 @@ describe("Plugins service", () => { let fs = testInjector.resolve("fs"); fs.writeJson(path.join(pluginFolderPath, "package.json"), pluginJsonData); - let pluginsService = testInjector.resolve("pluginsService"); - pluginsService.getAllInstalledPlugins = () => { + let pluginsService: IPluginsService = testInjector.resolve("pluginsService"); + pluginsService.getAllInstalledPlugins = async (projectData: IProjectData) => { return [{ name: "" }]; @@ -491,8 +493,8 @@ describe("Plugins service", () => { createAndroidManifestFile(projectFolder, fs); // Mock plugins service - let pluginsService = testInjector.resolve("pluginsService"); - pluginsService.getAllInstalledPlugins = () => { + let pluginsService: IPluginsService = testInjector.resolve("pluginsService"); + pluginsService.getAllInstalledPlugins = async (projectData: IProjectData) => { return [{ name: "" }]; @@ -516,6 +518,8 @@ describe("Plugins service", () => { // Ensure the pluginDestinationPath folder exists let pluginPlatformsDirPath = path.join(projectFolder, "node_modules", pluginName, "platforms", "android"); + let projectData: IProjectData = testInjector.resolve("projectData"); + projectData.initializeProjectData(); fs.ensureDirectoryExists(pluginPlatformsDirPath); // Creates invalid plugin's AndroidManifest.xml file @@ -531,7 +535,7 @@ describe("Plugins service", () => { `\n@#[line:1,col:39].` + `\n@#[line:1,col:39].`; mockBeginCommand(testInjector, expectedErrorMessage); - await pluginsService.prepare(pluginJsonData, "android"); + await pluginsService.prepare(pluginJsonData, "android", projectData); }); }); }); diff --git a/test/project-data.ts b/test/project-data.ts index b02399d165..44209529d7 100644 --- a/test/project-data.ts +++ b/test/project-data.ts @@ -51,6 +51,7 @@ describe("projectData", () => { projectHelper.projectDir = "projectDir"; const projectData: IProjectData = testInjector.resolve("projectData"); + projectData.initializeProjectData(); assert.deepEqual(projectData.projectType, expectedProjecType); }; diff --git a/test/project-files-provider.ts b/test/project-files-provider.ts index 3120e83145..8d21a50966 100644 --- a/test/project-files-provider.ts +++ b/test/project-files-provider.ts @@ -56,32 +56,38 @@ describe("project-files-provider", () => { describe("mapFilePath", () => { it("returns file path from prepared project when path from app dir is passed", () => { - let mappedFilePath = projectFilesProvider.mapFilePath(path.join(appSourceDir, "test.js"), "android"); + let projectData: IProjectData = testInjector.resolve("projectData"); + let mappedFilePath = projectFilesProvider.mapFilePath(path.join(appSourceDir, "test.js"), "android", projectData); assert.deepEqual(mappedFilePath, path.join(appDestinationDirectoryPath, "app", "test.js")); }); it("returns file path from prepared project when path from app/App_Resources/platform dir is passed", () => { - let mappedFilePath = projectFilesProvider.mapFilePath(path.join(appSourceDir, "App_Resources", "android", "test.js"), "android"); + let projectData: IProjectData = testInjector.resolve("projectData"); + let mappedFilePath = projectFilesProvider.mapFilePath(path.join(appSourceDir, "App_Resources", "android", "test.js"), "android", projectData); assert.deepEqual(mappedFilePath, path.join(appResourcesDestinationDirectoryPath, "test.js")); }); it("returns null when path from app/App_Resources/android dir is passed and iOS platform is specified", () => { - let mappedFilePath = projectFilesProvider.mapFilePath(path.join(appSourceDir, "App_Resources", "android", "test.js"), "iOS"); + let projectData: IProjectData = testInjector.resolve("projectData"); + let mappedFilePath = projectFilesProvider.mapFilePath(path.join(appSourceDir, "App_Resources", "android", "test.js"), "iOS", projectData); assert.deepEqual(mappedFilePath, null); }); it("returns null when path from app/App_Resources/ dir (not platform specific) is passed", () => { - let mappedFilePath = projectFilesProvider.mapFilePath(path.join(appSourceDir, "App_Resources", "test.js"), "android"); + let projectData: IProjectData = testInjector.resolve("projectData"); + let mappedFilePath = projectFilesProvider.mapFilePath(path.join(appSourceDir, "App_Resources", "test.js"), "android", projectData); assert.deepEqual(mappedFilePath, null); }); it("returns file path from prepared project when path from app dir is passed and it contains platform in its name", () => { - let mappedFilePath = projectFilesProvider.mapFilePath(path.join(appSourceDir, "test.android.js"), "android"); + let projectData: IProjectData = testInjector.resolve("projectData"); + let mappedFilePath = projectFilesProvider.mapFilePath(path.join(appSourceDir, "test.android.js"), "android", projectData); assert.deepEqual(mappedFilePath, path.join(appDestinationDirectoryPath, "app", "test.js")); }); it("returns file path from prepared project when path from app dir is passed and it contains configuration in its name", () => { - let mappedFilePath = projectFilesProvider.mapFilePath(path.join(appSourceDir, "test.debug.js"), "android"); + let projectData: IProjectData = testInjector.resolve("projectData"); + let mappedFilePath = projectFilesProvider.mapFilePath(path.join(appSourceDir, "test.debug.js"), "android", projectData); assert.deepEqual(mappedFilePath, path.join(appDestinationDirectoryPath, "app", "test.js")); }); }); diff --git a/test/project-service.ts b/test/project-service.ts index 749f709027..05fab99fe5 100644 --- a/test/project-service.ts +++ b/test/project-service.ts @@ -469,7 +469,8 @@ describe("project upgrade procedure tests", () => { let isErrorThrown = false; try { - testInjector.resolve("projectData"); // This should trigger upgrade procedure + let projectData: IProjectData = testInjector.resolve("projectData"); + projectData.initializeProjectData(); // This should trigger upgrade procedure } catch (err) { isErrorThrown = true; let expectedErrorMessage = "No project found at or above '%s' and neither was a --path specified.," + tempFolder; @@ -498,7 +499,8 @@ describe("project upgrade procedure tests", () => { let tnsProjectFilePath = path.join(tempFolder, ".tnsproject"); fs.writeJson(tnsProjectFilePath, tnsProjectData); - testInjector.resolve("projectData"); // This should trigger upgrade procedure + let projectData: IProjectData = testInjector.resolve("projectData"); + projectData.initializeProjectData(); // This should trigger upgrade procedure let packageJsonFilePath = path.join(tempFolder, "package.json"); let packageJsonFileContent = require(packageJsonFilePath); @@ -536,7 +538,8 @@ describe("project upgrade procedure tests", () => { let packageJsonFilePath = path.join(tempFolder, "package.json"); fs.writeJson(packageJsonFilePath, packageJsonData); - testInjector.resolve("projectData"); // This should trigger upgrade procedure + let projectData: IProjectData = testInjector.resolve("projectData"); + projectData.initializeProjectData(); // This should trigger upgrade procedure let packageJsonFileContent = require(packageJsonFilePath); let expectedPackageJsonContent: any = packageJsonData; diff --git a/test/stubs.ts b/test/stubs.ts index 265ea1267e..f3aa5bce99 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -2,6 +2,7 @@ import * as util from "util"; import * as chai from "chai"; +import { EventEmitter } from "events"; export class LoggerStub implements ILogger { setLevel(level: string): void { } @@ -237,12 +238,15 @@ export class ProjectDataStub implements IProjectData { appResourcesDirectoryPath: string; devDependencies: IStringDictionary; projectType: string; + initializeProjectData(projectDir?: string): void { + this.projectDir = this.projectDir || projectDir; + } } -export class PlatformsDataStub implements IPlatformsData { +export class PlatformsDataStub extends EventEmitter implements IPlatformsData { public platformsNames: string[]; - public getPlatformData(platform: string): IPlatformData { + public getPlatformData(platform: string, projectData: IProjectData): IPlatformData { return { frameworkPackageName: "", platformProjectService: new PlatformProjectServiceStub(), @@ -263,8 +267,8 @@ export class PlatformsDataStub implements IPlatformsData { } } -export class PlatformProjectServiceStub implements IPlatformProjectService { - get platformData(): IPlatformData { +export class PlatformProjectServiceStub extends EventEmitter implements IPlatformProjectService { + getPlatformData(projectData: IProjectData): IPlatformData { return { frameworkPackageName: "", normalizedPlatformName: "", @@ -463,7 +467,7 @@ function unexpected(msg: string): Error { } export class DebugServiceStub implements IDebugService { - public async debug(shouldBreak?: boolean): Promise { + public async debug(): Promise { return; } @@ -479,7 +483,7 @@ export class DebugServiceStub implements IDebugService { } export class LiveSyncServiceStub implements ILiveSyncService { - public async liveSync(platform: string, applicationReloadAction?: (deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]) => Promise): Promise { + public async liveSync(platform: string, projectData: IProjectData, applicationReloadAction?: (deviceAppData: Mobile.IDeviceAppData) => Promise): Promise { return; } } @@ -571,7 +575,7 @@ export class CommandsService implements ICommandsService { } } -export class PlatformServiceStub implements IPlatformService { +export class PlatformServiceStub extends EventEmitter implements IPlatformService { public validateOptions(): Promise { return Promise.resolve(true); @@ -601,11 +605,11 @@ export class PlatformServiceStub implements IPlatformService { return Promise.resolve(); } - public preparePlatform(platform: string, changesInfo?: IProjectChangesInfo): Promise { + public preparePlatform(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, platformTemplate: string): Promise { return Promise.resolve(true); } - public shouldBuild(platform: string, buildConfig?: IBuildConfig): Promise { + public shouldBuild(platform: string, projectData: IProjectData, buildConfig?: IBuildConfig): Promise { return Promise.resolve(true); } @@ -617,23 +621,23 @@ export class PlatformServiceStub implements IPlatformService { return true; } - public installApplication(device: Mobile.IDevice): Promise { + public installApplication(device: Mobile.IDevice, options: IRelease): Promise { return Promise.resolve(); } - public deployPlatform(platform: string, forceInstall?: boolean): Promise { + public deployPlatform(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, deployOptions: IDeployPlatformOptions): Promise { return Promise.resolve(); } - public runPlatform(platform: string): Promise { + public runPlatform(platform: string, runOptions: IRunPlatformOptions): Promise { return Promise.resolve(); } - public emulatePlatform(platform: string): Promise { + public emulatePlatform(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, emulateOptions: IEmulatePlatformOptions): Promise { return Promise.resolve(); } - public cleanDestinationApp(platform: string): Promise { + public cleanDestinationApp(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, platformTemplate: string): Promise { return Promise.resolve(); } diff --git a/test/tns-appstore-upload.ts b/test/tns-appstore-upload.ts index 0920b88fea..09c62c8457 100644 --- a/test/tns-appstore-upload.ts +++ b/test/tns-appstore-upload.ts @@ -1,6 +1,6 @@ -import { suite, test/*, only */ } from "mocha-typescript"; +import { suite, test/*, only*/ } from "mocha-typescript"; import { PublishIOS } from "../lib/commands/appstore-upload"; -import { PrompterStub, LoggerStub } from "./stubs"; +import { PrompterStub, LoggerStub, ProjectDataStub } from "./stubs"; import * as chai from "chai"; import * as yok from "../lib/common/yok"; @@ -16,6 +16,7 @@ class AppStore { command: ICommand; options: any; prompter: PrompterStub; + projectData: ProjectDataStub; platformService: any; iOSPlatformData: any; iOSProjectService: any; @@ -52,6 +53,7 @@ class AppStore { "logger": this.loggerService = new LoggerStub(), "options": this.options = {}, "prompter": this.prompter = new PrompterStub(), + "projectData": this.projectData = new ProjectDataStub(), "stringParameterBuilder": {}, "devicePlatformsConstants": { "iOS": "iOS" @@ -65,6 +67,7 @@ class AppStore { } } }); + this.projectData.initializeProjectData(this.iOSPlatformData.projectRoot); this.command = this.injector.resolveCommand("appstore"); } @@ -106,16 +109,16 @@ class AppStore { expectArchive() { this.expectedArchiveCalls = 1; - this.iOSProjectService.archive = (projectRoot: string) => { + this.iOSProjectService.archive = (projectData: IProjectData) => { this.archiveCalls++; - chai.assert.equal(projectRoot, "/Users/person/git/MyProject"); + chai.assert.equal(projectData.projectDir, "/Users/person/git/MyProject"); 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 = (projectData: IProjectData, 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) {