From db04d3b86e87c824fc481f5f16717927c544bd69 Mon Sep 17 00:00:00 2001 From: Nadya Atanasova Date: Fri, 8 Sep 2017 15:45:18 +0300 Subject: [PATCH 1/2] Separate native and JS prepare code Create interfaces to separate the prepare functionality in js and native parts. Ensure better isolation and encapsulation of project preparation process. --- lib/bootstrap.ts | 2 + lib/definitions/platform.d.ts | 13 ++ lib/services/platform-service.ts | 167 +----------------- lib/services/prepare-platform-js-service.ts | 94 ++++++++++ .../prepare-platform-native-service.ts | 86 +++++++++ lib/services/prepare-platform-service.ts | 26 +++ 6 files changed, 230 insertions(+), 158 deletions(-) create mode 100644 lib/services/prepare-platform-js-service.ts create mode 100644 lib/services/prepare-platform-native-service.ts create mode 100644 lib/services/prepare-platform-service.ts diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index aa52fd1939..08dcd977b3 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -22,6 +22,8 @@ $injector.require("tnsModulesService", "./services/tns-modules-service"); $injector.require("platformsData", "./platforms-data"); $injector.require("platformService", "./services/platform-service"); +$injector.require("preparePlatformJSService", "./services/prepare-platform-js-service"); +$injector.require("preparePlatformNativeService", "./services/prepare-platform-native-service"); $injector.require("debugDataService", "./services/debug-data-service"); $injector.requirePublicClass("debugService", "./services/debug-service"); diff --git a/lib/definitions/platform.d.ts b/lib/definitions/platform.d.ts index 535680ba60..3f1c6b1420 100644 --- a/lib/definitions/platform.d.ts +++ b/lib/definitions/platform.d.ts @@ -290,3 +290,16 @@ interface IBuildInfo { prepareTime: string; buildTime: string; } + +interface IPreparePlatformService extends NodeJS.EventEmitter { + addPlatform(platformData: IPlatformData, frameworkDir: string, installedVersion: string, projectData: IProjectData, config: IPlatformOptions, platformTemplate?: string, ): Promise; + preparePlatform(platform: string, platformData: IPlatformData, appFilesUpdaterOptions: IAppFilesUpdaterOptions, projectData: IProjectData, platformSpecificData: IPlatformSpecificData, changesInfo?: IProjectChangesInfo, filesToSync?: Array, projectFilesConfig?: IProjectFilesConfig): Promise; +} + +interface IPreparePlatformJSService extends IPreparePlatformService { + +} + +interface IPreparePlatformNativeService extends IPreparePlatformService { + +} \ No newline at end of file diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 22895ae564..491864e737 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -23,6 +23,8 @@ export class PlatformService extends EventEmitter implements IPlatformService { private _trackedProjectFilePath: string = null; constructor(private $devicesService: Mobile.IDevicesService, + private $preparePlatformNativeService: IPreparePlatformNativeService, + private $preparePlatformJSService: IPreparePlatformJSService, private $errors: IErrors, private $fs: IFileSystem, private $logger: ILogger, @@ -30,13 +32,11 @@ export class PlatformService extends EventEmitter implements IPlatformService { private $platformsData: IPlatformsData, private $projectDataService: IProjectDataService, private $hooksService: IHooksService, - private $nodeModulesBuilder: INodeModulesBuilder, private $pluginsService: IPluginsService, private $projectFilesManager: IProjectFilesManager, private $mobileHelper: Mobile.IMobileHelper, private $hostInfo: IHostInfo, private $devicePathProvider: IDevicePathProvider, - private $xmlValidator: IXmlValidator, private $npm: INodePackageManager, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, private $projectChangesService: IProjectChangesService, @@ -141,60 +141,17 @@ export class PlatformService extends EventEmitter implements IPlatformService { private async addPlatformCore(platformData: IPlatformData, frameworkDir: string, platformTemplate: string, projectData: IProjectData, config: IPlatformOptions, nativePrepare?: INativePrepare): Promise { const coreModuleData = this.$fs.readJson(path.join(frameworkDir, "..", "package.json")); const installedVersion = coreModuleData.version; - const customTemplateOptions = await this.getPathToPlatformTemplate(platformTemplate, platformData.frameworkPackageName, projectData.projectDir); - config.pathToTemplate = customTemplateOptions && customTemplateOptions.pathToTemplate; + + await this.$preparePlatformJSService.addPlatform(platformData, frameworkDir, installedVersion, projectData, config, platformTemplate); if (!nativePrepare || !nativePrepare.skipNativePrepare) { const platformDir = path.join(projectData.platformsDir, platformData.normalizedPlatformName.toLowerCase()); this.$fs.deleteDirectory(platformDir); - await this.addPlatformCoreNative(platformData, frameworkDir, installedVersion, projectData, config); - } - - const frameworkPackageNameData: any = { version: installedVersion }; - if (customTemplateOptions) { - frameworkPackageNameData.template = customTemplateOptions.selectedTemplate; + await this.$preparePlatformNativeService.addPlatform(platformData, frameworkDir, installedVersion, projectData, config); } - this.$projectDataService.setNSValue(projectData.projectDir, platformData.frameworkPackageName, frameworkPackageNameData); - const coreModuleName = coreModuleData.name; return coreModuleName; - - } - - private async addPlatformCoreNative(platformData: IPlatformData, frameworkDir: string, installedVersion: string, projectData: IProjectData, config: IPlatformOptions): Promise { - await platformData.platformProjectService.createProject(path.resolve(frameworkDir), installedVersion, projectData, config); - platformData.platformProjectService.ensureConfigurationFileInAppResources(projectData); - await platformData.platformProjectService.interpolateData(projectData, config); - platformData.platformProjectService.afterCreateProject(platformData.projectRoot, projectData); - } - - 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(projectDir, frameworkPackageName); - selectedTemplate = nativescriptPlatformData && nativescriptPlatformData.template; - } - - if (selectedTemplate) { - const tempDir = temp.mkdirSync("platform-template"); - this.$fs.writeJson(path.join(tempDir, constants.PACKAGE_JSON_FILE_NAME), {}); - try { - const npmInstallResult = await this.$npm.install(selectedTemplate, tempDir, { - disableNpmInstall: false, - frameworkPath: null, - ignoreScripts: false - }); - const pathToTemplate = path.join(tempDir, constants.NODE_MODULES_FOLDER_NAME, npmInstallResult.name); - return { selectedTemplate, pathToTemplate }; - } catch (err) { - this.$logger.trace("Error while trying to install specified template: ", err); - this.$errors.failWithoutHelp(`Unable to install platform template ${selectedTemplate}. Make sure the specified value is valid.`); - } - } - - return null; } public getInstalledPlatforms(projectData: IProjectData): string[] { @@ -216,8 +173,10 @@ export class PlatformService extends EventEmitter implements IPlatformService { public getPreparedPlatforms(projectData: IProjectData): string[] { return _.filter(this.$platformsData.platformsNames, p => { return this.isPlatformPrepared(p, projectData); }); } + public async preparePlatform(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, platformTemplate: string, projectData: IProjectData, config: IPlatformOptions, filesToSync?: Array, nativePrepare?: INativePrepare): Promise { const platformData = this.$platformsData.getPlatformData(platform, projectData); + const changesInfo = await this.initialPrepare(platform, platformData, appFilesUpdaterOptions, platformTemplate, projectData, config, nativePrepare); const requiresNativePrepare = (!nativePrepare || !nativePrepare.skipNativePrepare) && changesInfo.nativePlatformStatus === constants.NativePlatformStatus.requiresPrepare; @@ -249,25 +208,6 @@ export class PlatformService extends EventEmitter implements IPlatformService { } } - private async cleanProject(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, platformData: IPlatformData, projectData: IProjectData): Promise { - // android build artifacts need to be cleaned up - // when switching between debug, release and webpack builds - if (platform.toLowerCase() !== "android") { - return; - } - - const previousPrepareInfo = this.$projectChangesService.getPrepareInfo(platform, projectData); - if (!previousPrepareInfo) { - return; - } - - const { release: previousWasRelease, bundle: previousWasBundle } = previousPrepareInfo; - const { release: currentIsRelease, bundle: currentIsBundle } = appFilesUpdaterOptions; - if ((previousWasRelease !== currentIsRelease) || (previousWasBundle !== currentIsBundle)) { - await platformData.platformProjectService.cleanProject(platformData.projectRoot, projectData); - } - } - private async initialPrepare(platform: string, platformData: IPlatformData, appFilesUpdaterOptions: IAppFilesUpdaterOptions, platformTemplate: string, projectData: IProjectData, config: IPlatformOptions, nativePrepare?: INativePrepare): Promise { this.validatePlatform(platform, projectData); @@ -298,10 +238,10 @@ export class PlatformService extends EventEmitter implements IPlatformService { const platformData = this.$platformsData.getPlatformData(platform, projectData); const projectFilesConfig = helpers.getProjectFilesConfig({ isReleaseBuild: appFilesUpdaterOptions.release }); - await this.preparePlatformCoreJS(platform, platformData, appFilesUpdaterOptions, projectData, platformSpecificData, changesInfo, filesToSync, projectFilesConfig); + await this.$preparePlatformJSService.preparePlatform(platform, platformData, appFilesUpdaterOptions, projectData, platformSpecificData, changesInfo, filesToSync, projectFilesConfig); if (!nativePrepare || !nativePrepare.skipNativePrepare) { - await this.preparePlatformCoreNative(platform, platformData, appFilesUpdaterOptions, projectData, platformSpecificData, changesInfo, projectFilesConfig); + await this.$preparePlatformNativeService.preparePlatform(platform, platformData, appFilesUpdaterOptions, projectData, platformSpecificData, changesInfo, filesToSync, projectFilesConfig); } const directoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); @@ -315,95 +255,6 @@ export class PlatformService extends EventEmitter implements IPlatformService { this.$logger.out(`Project successfully prepared (${platform})`); } - private async preparePlatformCoreJS(platform: string, platformData: IPlatformData, appFilesUpdaterOptions: IAppFilesUpdaterOptions, projectData: IProjectData, platformSpecificData: IPlatformSpecificData, changesInfo?: IProjectChangesInfo, filesToSync?: Array, projectFilesConfig?: IProjectFilesConfig): Promise { - if (!changesInfo || changesInfo.appFilesChanged) { - await this.copyAppFiles(platformData, appFilesUpdaterOptions, projectData); - - // remove the App_Resources folder from the app/assets as here we're applying other files changes. - const appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); - const appResourcesDirectoryPath = path.join(appDestinationDirectoryPath, constants.APP_RESOURCES_FOLDER_NAME); - if (this.$fs.exists(appResourcesDirectoryPath)) { - this.$fs.deleteDirectory(appResourcesDirectoryPath); - } - } - - if (!changesInfo || changesInfo.modulesChanged) { - await this.copyTnsModules(platform, platformData, projectData, projectFilesConfig); - } - } - - public async preparePlatformCoreNative(platform: string, platformData: IPlatformData, appFilesUpdaterOptions: IAppFilesUpdaterOptions, projectData: IProjectData, platformSpecificData: IPlatformSpecificData, changesInfo?: IProjectChangesInfo, projectFilesConfig?: IProjectFilesConfig): Promise { - if (changesInfo.hasChanges) { - await this.cleanProject(platform, appFilesUpdaterOptions, platformData, projectData); - } - - if (!changesInfo || changesInfo.changesRequirePrepare) { - await this.copyAppFiles(platformData, appFilesUpdaterOptions, projectData); - this.copyAppResources(platformData, projectData); - await platformData.platformProjectService.prepareProject(projectData, platformSpecificData); - } - - if (!changesInfo || changesInfo.modulesChanged || appFilesUpdaterOptions.bundle) { - await this.$pluginsService.validate(platformData, projectData); - - const appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); - const lastModifiedTime = this.$fs.exists(appDestinationDirectoryPath) ? this.$fs.getFsStats(appDestinationDirectoryPath).mtime : null; - - const tnsModulesDestinationPath = path.join(appDestinationDirectoryPath, constants.TNS_MODULES_FOLDER_NAME); - // Process node_modules folder - await this.$nodeModulesBuilder.prepareNodeModules(tnsModulesDestinationPath, platform, lastModifiedTime, projectData, projectFilesConfig); - } - - if (!changesInfo || changesInfo.configChanged || changesInfo.modulesChanged) { - await platformData.platformProjectService.processConfigurationFilesFromAppResources(appFilesUpdaterOptions.release, projectData); - } - - platformData.platformProjectService.interpolateConfigurationFile(projectData, platformSpecificData); - this.$projectChangesService.setNativePlatformStatus(platform, projectData, - { nativePlatformStatus: constants.NativePlatformStatus.alreadyPrepared }); - } - - private async copyAppFiles(platformData: IPlatformData, appFilesUpdaterOptions: IAppFilesUpdaterOptions, projectData: IProjectData): Promise { - platformData.platformProjectService.ensureConfigurationFileInAppResources(projectData); - const appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); - - // Copy app folder to native project - this.$fs.ensureDirectoryExists(appDestinationDirectoryPath); - const appSourceDirectoryPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME); - - const appUpdater = new AppFilesUpdater(appSourceDirectoryPath, appDestinationDirectoryPath, appFilesUpdaterOptions, this.$fs); - appUpdater.updateApp(sourceFiles => { - this.$xmlValidator.validateXmlFiles(sourceFiles); - }); - } - - private copyAppResources(platformData: IPlatformData, projectData: IProjectData): void { - const appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); - const appResourcesDirectoryPath = path.join(appDestinationDirectoryPath, constants.APP_RESOURCES_FOLDER_NAME); - if (this.$fs.exists(appResourcesDirectoryPath)) { - platformData.platformProjectService.prepareAppResources(appResourcesDirectoryPath, projectData); - const 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, platformData: IPlatformData, projectData: IProjectData, projectFilesConfig?: IProjectFilesConfig): Promise { - const appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); - const lastModifiedTime = this.$fs.exists(appDestinationDirectoryPath) ? this.$fs.getFsStats(appDestinationDirectoryPath).mtime : null; - - try { - const tnsModulesDestinationPath = path.join(appDestinationDirectoryPath, constants.TNS_MODULES_FOLDER_NAME); - // Process node_modules folder - await this.$nodeModulesBuilder.prepareJSNodeModules(tnsModulesDestinationPath, platform, lastModifiedTime, projectData, projectFilesConfig); - } catch (error) { - this.$logger.debug(error); - shell.rm("-rf", appDestinationDirectoryPath); - this.$errors.failWithoutHelp(`Processing node_modules failed. ${error}`); - } - } - public async shouldBuild(platform: string, projectData: IProjectData, buildConfig: IBuildConfig, outputPath?: string): Promise { if (this.$projectChangesService.currentChanges.changesRequireBuild) { return true; diff --git a/lib/services/prepare-platform-js-service.ts b/lib/services/prepare-platform-js-service.ts new file mode 100644 index 0000000000..bb2ae88b40 --- /dev/null +++ b/lib/services/prepare-platform-js-service.ts @@ -0,0 +1,94 @@ +import * as constants from "../constants"; +import * as path from "path"; +import * as shell from "shelljs"; +import * as temp from "temp"; +import { PreparePlatformService } from "./prepare-platform-service"; + +temp.track(); + +export class PreparePlatformJSService extends PreparePlatformService implements IPreparePlatformNativeService { + + constructor($fs: IFileSystem, + $xmlValidator: IXmlValidator, + private $errors: IErrors, + private $logger: ILogger, + private $projectDataService: IProjectDataService, + private $nodeModulesBuilder: INodeModulesBuilder, + private $npm: INodePackageManager) { + super($fs, $xmlValidator); + } + + public async addPlatform(platformData: IPlatformData, frameworkDir: string, installedVersion: string, projectData: IProjectData, config: IPlatformOptions, platformTemplate: string, ): Promise { + const customTemplateOptions = await this.getPathToPlatformTemplate(platformTemplate, platformData.frameworkPackageName, projectData.projectDir); + config.pathToTemplate = customTemplateOptions && customTemplateOptions.pathToTemplate; + + const frameworkPackageNameData: any = { version: installedVersion }; + if (customTemplateOptions) { + frameworkPackageNameData.template = customTemplateOptions.selectedTemplate; + } + + this.$projectDataService.setNSValue(projectData.projectDir, platformData.frameworkPackageName, frameworkPackageNameData); + } + + public async preparePlatform(platform: string, platformData: IPlatformData, appFilesUpdaterOptions: IAppFilesUpdaterOptions, projectData: IProjectData, platformSpecificData: IPlatformSpecificData, changesInfo?: IProjectChangesInfo, filesToSync?: Array, projectFilesConfig?: IProjectFilesConfig): Promise { + if (!changesInfo || changesInfo.appFilesChanged) { + await this.copyAppFiles(platformData, appFilesUpdaterOptions, projectData); + + // remove the App_Resources folder from the app/assets as here we're applying other files changes. + const appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + const appResourcesDirectoryPath = path.join(appDestinationDirectoryPath, constants.APP_RESOURCES_FOLDER_NAME); + if (this.$fs.exists(appResourcesDirectoryPath)) { + this.$fs.deleteDirectory(appResourcesDirectoryPath); + } + } + + if (!changesInfo || changesInfo.modulesChanged) { + await this.copyTnsModules(platform, platformData, projectData, projectFilesConfig); + } + } + + 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(projectDir, frameworkPackageName); + selectedTemplate = nativescriptPlatformData && nativescriptPlatformData.template; + } + + if (selectedTemplate) { + const tempDir = temp.mkdirSync("platform-template"); + this.$fs.writeJson(path.join(tempDir, constants.PACKAGE_JSON_FILE_NAME), {}); + try { + const npmInstallResult = await this.$npm.install(selectedTemplate, tempDir, { + disableNpmInstall: false, + frameworkPath: null, + ignoreScripts: false + }); + const pathToTemplate = path.join(tempDir, constants.NODE_MODULES_FOLDER_NAME, npmInstallResult.name); + return { selectedTemplate, pathToTemplate }; + } catch (err) { + this.$logger.trace("Error while trying to install specified template: ", err); + this.$errors.failWithoutHelp(`Unable to install platform template ${selectedTemplate}. Make sure the specified value is valid.`); + } + } + + return null; + } + + private async copyTnsModules(platform: string, platformData: IPlatformData, projectData: IProjectData, projectFilesConfig?: IProjectFilesConfig): Promise { + const appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + const lastModifiedTime = this.$fs.exists(appDestinationDirectoryPath) ? this.$fs.getFsStats(appDestinationDirectoryPath).mtime : null; + + try { + const tnsModulesDestinationPath = path.join(appDestinationDirectoryPath, constants.TNS_MODULES_FOLDER_NAME); + // Process node_modules folder + await this.$nodeModulesBuilder.prepareJSNodeModules(tnsModulesDestinationPath, platform, lastModifiedTime, projectData, projectFilesConfig); + } catch (error) { + this.$logger.debug(error); + shell.rm("-rf", appDestinationDirectoryPath); + this.$errors.failWithoutHelp(`Processing node_modules failed. ${error}`); + } + } +} + +$injector.register("preparePlatformJSService", PreparePlatformJSService); diff --git a/lib/services/prepare-platform-native-service.ts b/lib/services/prepare-platform-native-service.ts new file mode 100644 index 0000000000..94e2a576b6 --- /dev/null +++ b/lib/services/prepare-platform-native-service.ts @@ -0,0 +1,86 @@ +import * as constants from "../constants"; +import * as path from "path"; +import * as shell from "shelljs"; +import { PreparePlatformService } from "./prepare-platform-service"; + +export class PreparePlatformNativeService extends PreparePlatformService implements IPreparePlatformNativeService { + + constructor($fs: IFileSystem, + $xmlValidator: IXmlValidator, + private $nodeModulesBuilder: INodeModulesBuilder, + private $pluginsService: IPluginsService, + private $projectChangesService: IProjectChangesService) { + super($fs, $xmlValidator); + } + + public async addPlatform(platformData: IPlatformData, frameworkDir: string, installedVersion: string, projectData: IProjectData, config: IPlatformOptions): Promise { + await platformData.platformProjectService.createProject(path.resolve(frameworkDir), installedVersion, projectData, config); + platformData.platformProjectService.ensureConfigurationFileInAppResources(projectData); + await platformData.platformProjectService.interpolateData(projectData, config); + platformData.platformProjectService.afterCreateProject(platformData.projectRoot, projectData); + } + + public async preparePlatform(platform: string, platformData: IPlatformData, appFilesUpdaterOptions: IAppFilesUpdaterOptions, projectData: IProjectData, platformSpecificData: IPlatformSpecificData, changesInfo?: IProjectChangesInfo, filesToSync?: Array, projectFilesConfig?: IProjectFilesConfig): Promise { + if (changesInfo.hasChanges) { + await this.cleanProject(platform, appFilesUpdaterOptions, platformData, projectData); + } + + if (!changesInfo || changesInfo.changesRequirePrepare) { + await this.copyAppFiles(platformData, appFilesUpdaterOptions, projectData); + this.copyAppResources(platformData, projectData); + await platformData.platformProjectService.prepareProject(projectData, platformSpecificData); + } + + if (!changesInfo || changesInfo.modulesChanged || appFilesUpdaterOptions.bundle) { + await this.$pluginsService.validate(platformData, projectData); + + const appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + const lastModifiedTime = this.$fs.exists(appDestinationDirectoryPath) ? this.$fs.getFsStats(appDestinationDirectoryPath).mtime : null; + + const tnsModulesDestinationPath = path.join(appDestinationDirectoryPath, constants.TNS_MODULES_FOLDER_NAME); + // Process node_modules folder + await this.$nodeModulesBuilder.prepareNodeModules(tnsModulesDestinationPath, platform, lastModifiedTime, projectData, projectFilesConfig); + } + + if (!changesInfo || changesInfo.configChanged || changesInfo.modulesChanged) { + await platformData.platformProjectService.processConfigurationFilesFromAppResources(appFilesUpdaterOptions.release, projectData); + } + + platformData.platformProjectService.interpolateConfigurationFile(projectData, platformSpecificData); + this.$projectChangesService.setNativePlatformStatus(platform, projectData, + { nativePlatformStatus: constants.NativePlatformStatus.alreadyPrepared }); + } + + private copyAppResources(platformData: IPlatformData, projectData: IProjectData): void { + const appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + const appResourcesDirectoryPath = path.join(appDestinationDirectoryPath, constants.APP_RESOURCES_FOLDER_NAME); + if (this.$fs.exists(appResourcesDirectoryPath)) { + platformData.platformProjectService.prepareAppResources(appResourcesDirectoryPath, projectData); + const appResourcesDestination = platformData.platformProjectService.getAppResourcesDestinationDirectoryPath(projectData); + this.$fs.ensureDirectoryExists(appResourcesDestination); + shell.cp("-Rf", path.join(appResourcesDirectoryPath, platformData.normalizedPlatformName, "*"), appResourcesDestination); + this.$fs.deleteDirectory(appResourcesDirectoryPath); + } + } + + private async cleanProject(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, platformData: IPlatformData, projectData: IProjectData): Promise { + // android build artifacts need to be cleaned up + // when switching between debug, release and webpack builds + if (platform.toLowerCase() !== "android") { + return; + } + + const previousPrepareInfo = this.$projectChangesService.getPrepareInfo(platform, projectData); + if (!previousPrepareInfo) { + return; + } + + const { release: previousWasRelease, bundle: previousWasBundle } = previousPrepareInfo; + const { release: currentIsRelease, bundle: currentIsBundle } = appFilesUpdaterOptions; + if ((previousWasRelease !== currentIsRelease) || (previousWasBundle !== currentIsBundle)) { + await platformData.platformProjectService.cleanProject(platformData.projectRoot, projectData); + } + } +} + +$injector.register("preparePlatformNativeService", PreparePlatformNativeService); diff --git a/lib/services/prepare-platform-service.ts b/lib/services/prepare-platform-service.ts new file mode 100644 index 0000000000..07f54a52ac --- /dev/null +++ b/lib/services/prepare-platform-service.ts @@ -0,0 +1,26 @@ +import * as constants from "../constants"; +import * as path from "path"; +import { AppFilesUpdater } from "./app-files-updater"; +import { EventEmitter } from "events"; + +export class PreparePlatformService extends EventEmitter { + + constructor(protected $fs: IFileSystem, + private $xmlValidator: IXmlValidator) { + super(); + } + + protected async copyAppFiles(platformData: IPlatformData, appFilesUpdaterOptions: IAppFilesUpdaterOptions, projectData: IProjectData): Promise { + platformData.platformProjectService.ensureConfigurationFileInAppResources(projectData); + const appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + + // Copy app folder to native project + this.$fs.ensureDirectoryExists(appDestinationDirectoryPath); + const appSourceDirectoryPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME); + + const appUpdater = new AppFilesUpdater(appSourceDirectoryPath, appDestinationDirectoryPath, appFilesUpdaterOptions, this.$fs); + appUpdater.updateApp(sourceFiles => { + this.$xmlValidator.validateXmlFiles(sourceFiles); + }); + } +} From 1dd1398357f11062a51488a31027cd551e623e73 Mon Sep 17 00:00:00 2001 From: Dimitar Kerezov Date: Mon, 23 Oct 2017 08:55:50 +0300 Subject: [PATCH 2/2] Introduce prepareJSApp hook * Extract all code that webpack needs to replace into a single method * Introduce a hook (`prepareJSApp`) for that method so it can be replaced * Add additional check for when not to delete `App_Resources` directory when preparing JS so that whenever preparing natively we don't copy all the files twice --- lib/commands/appstore-upload.ts | 13 +- lib/commands/build.ts | 11 +- lib/commands/deploy.ts | 12 +- lib/commands/prepare.ts | 11 +- lib/common | 2 +- lib/declarations.d.ts | 8 +- lib/definitions/livesync.d.ts | 15 ++- lib/definitions/platform.d.ts | 70 +++++++--- lib/options.ts | 1 + lib/services/analytics/analytics-service.ts | 2 +- .../livesync/livesync-command-helper.ts | 16 ++- lib/services/livesync/livesync-service.ts | 25 +++- lib/services/local-build-service.ts | 23 +++- lib/services/platform-service.ts | 123 ++++++++++++------ lib/services/prepare-platform-js-service.ts | 44 ++++--- .../prepare-platform-native-service.ts | 44 +++---- lib/services/prepare-platform-service.ts | 20 +-- lib/services/project-changes-service.ts | 2 +- lib/services/test-execution-service.ts | 23 +++- .../node-modules/node-modules-builder.ts | 6 +- test/npm-support.ts | 13 +- test/platform-commands.ts | 2 + test/platform-service.ts | 24 +++- test/stubs.ts | 4 +- test/tns-appstore-upload.ts | 4 +- 25 files changed, 369 insertions(+), 149 deletions(-) diff --git a/lib/commands/appstore-upload.ts b/lib/commands/appstore-upload.ts index 9968d0faf3..cccaf49a2d 100644 --- a/lib/commands/appstore-upload.ts +++ b/lib/commands/appstore-upload.ts @@ -57,6 +57,15 @@ export class PublishIOS implements ICommand { const platform = this.$devicePlatformsConstants.iOS; // No .ipa path provided, build .ipa on out own. const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; + const platformInfo: IPreparePlatformInfo = { + platform, + appFilesUpdaterOptions, + platformTemplate: this.$options.platformTemplate, + projectData: this.$projectData, + config: this.$options, + env: this.$options.env + }; + if (mobileProvisionIdentifier || codeSignIdentity) { const iOSBuildConfig: IBuildConfig = { projectDir: this.$options.path, @@ -70,12 +79,12 @@ export class PublishIOS implements ICommand { }; 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, appFilesUpdaterOptions, this.$options.platformTemplate, this.$projectData, this.$options); + await this.$platformService.preparePlatform(platformInfo); await this.$platformService.buildPlatform(platform, iOSBuildConfig, this.$projectData); ipaFilePath = this.$platformService.lastOutputPath(platform, iOSBuildConfig, 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, appFilesUpdaterOptions, this.$options.platformTemplate, this.$projectData, this.$options); + await this.$platformService.preparePlatform(platformInfo); const platformData = this.$platformsData.getPlatformData(platform, this.$projectData); const iOSProjectService = platformData.platformProjectService; diff --git a/lib/commands/build.ts b/lib/commands/build.ts index 97300a12db..db76690f4d 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -13,7 +13,16 @@ export class BuildCommandBase { public async executeCore(args: string[]): Promise { const platform = args[0].toLowerCase(); const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; - await this.$platformService.preparePlatform(platform, appFilesUpdaterOptions, this.$options.platformTemplate, this.$projectData, this.$options); + const platformInfo: IPreparePlatformInfo = { + platform, + appFilesUpdaterOptions, + platformTemplate: this.$options.platformTemplate, + projectData: this.$projectData, + config: this.$options, + env: this.$options.env + }; + + await this.$platformService.preparePlatform(platformInfo); this.$options.clean = true; const buildConfig: IBuildConfig = { buildForDevice: this.$options.forDevice, diff --git a/lib/commands/deploy.ts b/lib/commands/deploy.ts index 88e63ab1d7..80096dd8fb 100644 --- a/lib/commands/deploy.ts +++ b/lib/commands/deploy.ts @@ -30,7 +30,17 @@ export class DeployOnDeviceCommand implements ICommand { keyStorePassword: this.$options.keyStorePassword, keyStorePath: this.$options.keyStorePath }; - return this.$platformService.deployPlatform(args[0], appFilesUpdaterOptions, deployOptions, this.$projectData, this.$options); + + const deployPlatformInfo: IDeployPlatformInfo = { + platform: args[0], + appFilesUpdaterOptions, + deployOptions, + projectData: this.$projectData, + config: this.$options, + env: this.$options.env + }; + + return this.$platformService.deployPlatform(deployPlatformInfo); } public async canExecute(args: string[]): Promise { diff --git a/lib/commands/prepare.ts b/lib/commands/prepare.ts index 489ebcd358..46a6e2e1bc 100644 --- a/lib/commands/prepare.ts +++ b/lib/commands/prepare.ts @@ -11,7 +11,16 @@ export class PrepareCommand implements ICommand { public async execute(args: string[]): Promise { 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); + const platformInfo: IPreparePlatformInfo = { + platform: args[0], + appFilesUpdaterOptions, + platformTemplate: this.$options.platformTemplate, + projectData: this.$projectData, + config: this.$options, + env: this.$options.env + }; + + await this.$platformService.preparePlatform(platformInfo); } public async canExecute(args: string[]): Promise { diff --git a/lib/common b/lib/common index d20d42dbd4..c8a450bf11 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit d20d42dbd4bd04715de9725c811534448f3c470a +Subproject commit c8a450bf114c67125ebbedbd073f4ff5475a095c diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index c6f18218d7..f7d56c0187 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -374,7 +374,7 @@ interface IPort { port: Number; } -interface IOptions extends ICommonOptions, IBundle, IPlatformTemplate, IEmulator, IClean, IProvision, ITeamIdentifier, IAndroidReleaseOptions, INpmInstallConfigurationOptions, IPort { +interface IOptions extends ICommonOptions, IBundle, IPlatformTemplate, IEmulator, IClean, IProvision, ITeamIdentifier, IAndroidReleaseOptions, INpmInstallConfigurationOptions, IPort, IEnvOptions { all: boolean; client: boolean; compileSdk: number; @@ -395,11 +395,15 @@ interface IOptions extends ICommonOptions, IBundle, IPlatformTemplate, IEmulator chrome: boolean; } +interface IEnvOptions { + env: Object; +} + interface IAndroidBuildOptionsSettings extends IAndroidReleaseOptions, IRelease { } interface IAppFilesUpdaterOptions extends IBundle, IRelease { } -interface IPlatformBuildData extends IAppFilesUpdaterOptions, IBuildConfig { } +interface IPlatformBuildData extends IAppFilesUpdaterOptions, IBuildConfig, IEnvOptions { } interface IDeviceEmulator extends IEmulator, IDeviceIdentifier { } diff --git a/lib/definitions/livesync.d.ts b/lib/definitions/livesync.d.ts index 538bffe5a9..6062d02fa7 100644 --- a/lib/definitions/livesync.d.ts +++ b/lib/definitions/livesync.d.ts @@ -111,7 +111,7 @@ interface ILiveSyncDeviceInfo extends IOptionalOutputPath, IOptionalDebuggingOpt /** * Describes a LiveSync operation. */ -interface ILiveSyncInfo extends IProjectDir { +interface ILiveSyncInfo extends IProjectDir, IEnvOptions { /** * Defines if the watcher should be skipped. If not passed, fs.Watcher will be started. */ @@ -145,14 +145,17 @@ interface ILiveSyncBuildInfo extends IIsEmulator, IPlatform { pathToBuildItem: string; } +interface IProjectDataComposition { + projectData: IProjectData; +} + /** * Desribes object that can be passed to ensureLatestAppPackageIsInstalledOnDevice method. */ -interface IEnsureLatestAppPackageIsInstalledOnDeviceOptions { +interface IEnsureLatestAppPackageIsInstalledOnDeviceOptions extends IProjectDataComposition, IEnvOptions { device: Mobile.IDevice; preparedPlatforms: string[]; rebuiltInformation: ILiveSyncBuildInfo[]; - projectData: IProjectData; deviceBuildInfoDescriptor: ILiveSyncDeviceInfo; settings: ILatestAppPackageInstalledSettings; liveSyncData?: ILiveSyncInfo; @@ -273,8 +276,7 @@ interface IShouldSkipEmitLiveSyncNotification { interface IAttachDebuggerOptions extends IDebuggingAdditionalOptions, IEnableDebuggingDeviceOptions, IIsEmulator, IPlatform, IOptionalOutputPath { } -interface ILiveSyncWatchInfo { - projectData: IProjectData; +interface ILiveSyncWatchInfo extends IProjectDataComposition { filesToRemove: string[]; filesToSync: string[]; isReinstalled: boolean; @@ -289,8 +291,7 @@ interface ILiveSyncResultInfo { useLiveEdit?: boolean; } -interface IFullSyncInfo { - projectData: IProjectData; +interface IFullSyncInfo extends IProjectDataComposition { device: Mobile.IDevice; watch: boolean; syncAllFiles: boolean; diff --git a/lib/definitions/platform.d.ts b/lib/definitions/platform.d.ts index 3f1c6b1420..515a4501de 100644 --- a/lib/definitions/platform.d.ts +++ b/lib/definitions/platform.d.ts @@ -39,15 +39,10 @@ interface IPlatformService extends NodeJS.EventEmitter { * When there are changes to be prepared, it prepares the native project for the specified platform. * 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 {IAddPlatformCoreOptions} config Options required for project preparation/creation. - * @param {Array} filesToSync Files about to be synced to device. + * @param {IPreparePlatformInfo} platformInfo Options to control the preparation. * @returns {boolean} true indicates that the platform was prepared. */ - preparePlatform(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, platformTemplate: string, projectData: IProjectData, config: IPlatformOptions, filesToSync?: Array, nativePrepare?: INativePrepare): Promise; + preparePlatform(platformInfo: IPreparePlatformInfo): Promise; /** * Determines whether a build is necessary. A build is necessary when one of the following is true: @@ -106,14 +101,10 @@ interface IPlatformService extends NodeJS.EventEmitter { /** * 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 {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 {IAddPlatformCoreOptions} config Options required for project preparation/creation. + * @param {IDeployPlatformInfo} deployInfo Options required for project preparation and deployment. * @returns {void} */ - deployPlatform(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, deployOptions: IDeployPlatformOptions, projectData: IProjectData, config: IPlatformOptions): Promise; + deployPlatform(deployInfo: IDeployPlatformInfo): Promise; /** * Runs the application on specified platform. Assumes that the application is already build and installed. Fails if this is not true. @@ -276,9 +267,19 @@ interface IPlatformsData { getPlatformData(platform: string, projectData: IProjectData): IPlatformData; } +interface IAppFilesUpdaterOptionsComposition { + appFilesUpdaterOptions: IAppFilesUpdaterOptions; +} + +interface IJsNodeModulesData extends IPlatform, IProjectDataComposition, IAppFilesUpdaterOptionsComposition { + absoluteOutputPath: string; + lastModifiedTime: Date; + projectFilesConfig: IProjectFilesConfig; +} + interface INodeModulesBuilder { prepareNodeModules(absoluteOutputPath: string, platform: string, lastModifiedTime: Date, projectData: IProjectData, projectFilesConfig: IProjectFilesConfig): Promise; - prepareJSNodeModules(absoluteOutputPath: string, platform: string, lastModifiedTime: Date, projectData: IProjectData, projectFilesConfig: IProjectFilesConfig): Promise; + prepareJSNodeModules(jsNodeModulesData: IJsNodeModulesData): Promise; cleanNodeModules(absoluteOutputPath: string, platform: string): void; } @@ -291,15 +292,44 @@ interface IBuildInfo { buildTime: string; } -interface IPreparePlatformService extends NodeJS.EventEmitter { - addPlatform(platformData: IPlatformData, frameworkDir: string, installedVersion: string, projectData: IProjectData, config: IPlatformOptions, platformTemplate?: string, ): Promise; - preparePlatform(platform: string, platformData: IPlatformData, appFilesUpdaterOptions: IAppFilesUpdaterOptions, projectData: IProjectData, platformSpecificData: IPlatformSpecificData, changesInfo?: IProjectChangesInfo, filesToSync?: Array, projectFilesConfig?: IProjectFilesConfig): Promise; +interface IPlatformDataComposition { + platformData: IPlatformData; } -interface IPreparePlatformJSService extends IPreparePlatformService { +interface ICopyAppFilesData extends IProjectDataComposition, IAppFilesUpdaterOptionsComposition, IPlatformDataComposition { } + +interface IPreparePlatformService { + addPlatform(info: IAddPlatformInfo): Promise; + preparePlatform(config: IPreparePlatformJSInfo): Promise; +} +interface IAddPlatformInfo extends IProjectDataComposition, IPlatformDataComposition { + frameworkDir: string; + installedVersion: string; + config: IPlatformOptions; + platformTemplate?: string; } -interface IPreparePlatformNativeService extends IPreparePlatformService { +interface IPreparePlatformJSInfo extends IPreparePlatformCoreInfo, ICopyAppFilesData { + projectFilesConfig?: IProjectFilesConfig; +} -} \ No newline at end of file +interface IPreparePlatformCoreInfo extends IPreparePlatformInfoBase { + platformSpecificData: IPlatformSpecificData + changesInfo?: IProjectChangesInfo; +} + +interface IPreparePlatformInfo extends IPreparePlatformInfoBase, IPlatformConfig, IPlatformTemplate { } + +interface IPlatformConfig { + config: IPlatformOptions; +} + +interface IPreparePlatformInfoBase extends IPlatform, IAppFilesUpdaterOptionsComposition, IProjectDataComposition, IEnvOptions { + filesToSync?: string[]; + nativePrepare?: INativePrepare; +} + +interface IDeployPlatformInfo extends IPlatform, IAppFilesUpdaterOptionsComposition, IProjectDataComposition, IPlatformConfig, IEnvOptions { + deployOptions: IDeployPlatformOptions +} diff --git a/lib/options.ts b/lib/options.ts index e947c4cb9a..36c03e136e 100644 --- a/lib/options.ts +++ b/lib/options.ts @@ -15,6 +15,7 @@ export class Options extends commonOptionsLibPath.OptionsBase { forDevice: { type: OptionType.Boolean }, provision: { type: OptionType.Object }, client: { type: OptionType.Boolean, default: true }, + env: { type: OptionType.Object }, production: { type: OptionType.Boolean }, debugTransport: { type: OptionType.Boolean }, keyStorePath: { type: OptionType.String }, diff --git a/lib/services/analytics/analytics-service.ts b/lib/services/analytics/analytics-service.ts index 411e89b309..298de76d42 100644 --- a/lib/services/analytics/analytics-service.ts +++ b/lib/services/analytics/analytics-service.ts @@ -11,13 +11,13 @@ export class AnalyticsService extends AnalyticsServiceBase { constructor(protected $logger: ILogger, protected $options: IOptions, + protected $processService: IProcessService, $staticConfig: Config.IStaticConfig, $prompter: IPrompter, $userSettingsService: UserSettings.IUserSettingsService, $analyticsSettingsService: IAnalyticsSettingsService, $osInfo: IOsInfo, private $childProcess: IChildProcess, - protected $processService: IProcessService, private $projectDataService: IProjectDataService, private $mobileHelper: Mobile.IMobileHelper) { super($logger, $options, $staticConfig, $processService, $prompter, $userSettingsService, $analyticsSettingsService, $osInfo); diff --git a/lib/services/livesync/livesync-command-helper.ts b/lib/services/livesync/livesync-command-helper.ts index 219a86fc0a..7d3190bafc 100644 --- a/lib/services/livesync/livesync-command-helper.ts +++ b/lib/services/livesync/livesync-command-helper.ts @@ -9,7 +9,7 @@ export class LiveSyncCommandHelper implements ILiveSyncCommandHelper { private $platformsData: IPlatformsData, private $analyticsService: IAnalyticsService, private $errors: IErrors) { - this.$analyticsService.setShouldDispose(this.$options.justlaunch || !this.$options.watch); + this.$analyticsService.setShouldDispose(this.$options.justlaunch || !this.$options.watch); } public getPlatformsForOperation(platform: string): string[] { @@ -74,7 +74,8 @@ export class LiveSyncCommandHelper implements ILiveSyncCommandHelper { projectDir: this.$projectData.projectDir, skipWatcher: !this.$options.watch, watchAllFiles: this.$options.syncAllFiles, - clean: this.$options.clean + clean: this.$options.clean, + env: this.$options.env }; await this.$liveSyncService.liveSync(deviceDescriptors, liveSyncInfo); @@ -95,7 +96,16 @@ export class LiveSyncCommandHelper implements ILiveSyncCommandHelper { const availablePlatforms = this.getPlatformsForOperation(platform); for (const currentPlatform of availablePlatforms) { - await this.$platformService.deployPlatform(currentPlatform, this.$options, deployOptions, this.$projectData, this.$options); + const deployPlatformInfo: IDeployPlatformInfo = { + platform: currentPlatform, + appFilesUpdaterOptions: this.$options, + deployOptions, + projectData: this.$projectData, + config: this.$options, + env: this.$options.env + }; + + await this.$platformService.deployPlatform(deployPlatformInfo); await this.$platformService.startApplication(currentPlatform, runPlatformOptions, this.$projectData.projectId); this.$platformService.trackProjectType(this.$projectData); } diff --git a/lib/services/livesync/livesync-service.ts b/lib/services/livesync/livesync-service.ts index 4b669e07c0..ed6c301ddf 100644 --- a/lib/services/livesync/livesync-service.ts +++ b/lib/services/livesync/livesync-service.ts @@ -359,10 +359,21 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi options.preparedPlatforms.push(platform); const platformSpecificOptions = options.deviceBuildInfoDescriptor.platformSpecificOptions || {}; - await this.$platformService.preparePlatform(platform, { - bundle: false, - release: false, - }, null, options.projectData, platformSpecificOptions, options.modifiedFiles, nativePrepare); + const prepareInfo: IPreparePlatformInfo = { + platform, + appFilesUpdaterOptions: { + bundle: false, + release: false, + }, + projectData: options.projectData, + env: options.env, + nativePrepare: nativePrepare, + filesToSync: options.modifiedFiles, + platformTemplate: null, + config: platformSpecificOptions + }; + + await this.$platformService.preparePlatform(prepareInfo); } const buildResult = await this.installedCachedAppPackage(platform, options); @@ -447,7 +458,8 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi projectData, deviceBuildInfoDescriptor, liveSyncData, - settings + settings, + env: liveSyncData.env }, { skipNativePrepare: deviceBuildInfoDescriptor.skipNativePrepare }); const liveSyncResultInfo = await this.getLiveSyncService(platform).fullSync({ @@ -551,7 +563,8 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi projectData, deviceBuildInfoDescriptor, settings: latestAppPackageInstalledSettings, - modifiedFiles: allModifiedFiles + modifiedFiles: allModifiedFiles, + env: liveSyncData.env }, { skipNativePrepare: deviceBuildInfoDescriptor.skipNativePrepare }); const service = this.getLiveSyncService(device.deviceInfo.platform); diff --git a/lib/services/local-build-service.ts b/lib/services/local-build-service.ts index c2f77f40bc..7b8fe95465 100644 --- a/lib/services/local-build-service.ts +++ b/lib/services/local-build-service.ts @@ -16,13 +16,22 @@ export class LocalBuildService extends EventEmitter implements ILocalBuildServic } this.$projectData.initializeProjectData(platformBuildOptions.projectDir); - await this.$platformService.preparePlatform(platform, platformBuildOptions, platformTemplate, this.$projectData, { - provision: platformBuildOptions.provision, - teamId: platformBuildOptions.teamId, - sdk: null, - frameworkPath: null, - ignoreScripts: false - }); + const prepareInfo: IPreparePlatformInfo = { + platform, + appFilesUpdaterOptions: platformBuildOptions, + platformTemplate, + projectData: this.$projectData, + env: platformBuildOptions.env, + config: { + provision: platformBuildOptions.provision, + teamId: platformBuildOptions.teamId, + sdk: null, + frameworkPath: null, + ignoreScripts: false + } + }; + + await this.$platformService.preparePlatform(prepareInfo); const handler = (data: any) => { data.projectDir = platformBuildOptions.projectDir; this.emit(BUILD_OUTPUT_EVENT_NAME, data); diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 491864e737..5ab4d090a6 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -23,8 +23,8 @@ export class PlatformService extends EventEmitter implements IPlatformService { private _trackedProjectFilePath: string = null; constructor(private $devicesService: Mobile.IDevicesService, - private $preparePlatformNativeService: IPreparePlatformNativeService, - private $preparePlatformJSService: IPreparePlatformJSService, + private $preparePlatformNativeService: IPreparePlatformService, + private $preparePlatformJSService: IPreparePlatformService, private $errors: IErrors, private $fs: IFileSystem, private $logger: ILogger, @@ -142,12 +142,25 @@ export class PlatformService extends EventEmitter implements IPlatformService { const coreModuleData = this.$fs.readJson(path.join(frameworkDir, "..", "package.json")); const installedVersion = coreModuleData.version; - await this.$preparePlatformJSService.addPlatform(platformData, frameworkDir, installedVersion, projectData, config, platformTemplate); + await this.$preparePlatformJSService.addPlatform({ + platformData, + frameworkDir, + installedVersion, + projectData, + config, + platformTemplate + }); if (!nativePrepare || !nativePrepare.skipNativePrepare) { const platformDir = path.join(projectData.platformsDir, platformData.normalizedPlatformName.toLowerCase()); this.$fs.deleteDirectory(platformDir); - await this.$preparePlatformNativeService.addPlatform(platformData, frameworkDir, installedVersion, projectData, config); + await this.$preparePlatformNativeService.addPlatform({ + platformData, + frameworkDir, + installedVersion, + projectData, + config + }); } const coreModuleName = coreModuleData.name; @@ -174,15 +187,24 @@ export class PlatformService extends EventEmitter implements IPlatformService { return _.filter(this.$platformsData.platformsNames, p => { return this.isPlatformPrepared(p, projectData); }); } - public async preparePlatform(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, platformTemplate: string, projectData: IProjectData, config: IPlatformOptions, filesToSync?: Array, nativePrepare?: INativePrepare): Promise { - const platformData = this.$platformsData.getPlatformData(platform, projectData); - - const changesInfo = await this.initialPrepare(platform, platformData, appFilesUpdaterOptions, platformTemplate, projectData, config, nativePrepare); - const requiresNativePrepare = (!nativePrepare || !nativePrepare.skipNativePrepare) && changesInfo.nativePlatformStatus === constants.NativePlatformStatus.requiresPrepare; - - if (changesInfo.hasChanges || appFilesUpdaterOptions.bundle || requiresNativePrepare) { - await this.preparePlatformCore(platform, appFilesUpdaterOptions, projectData, config, changesInfo, filesToSync, nativePrepare); - this.$projectChangesService.savePrepareInfo(platform, projectData); + public async preparePlatform(platformInfo: IPreparePlatformInfo): Promise { + const platformData = this.$platformsData.getPlatformData(platformInfo.platform, platformInfo.projectData); + + const changesInfo = await this.initialPrepare(platformInfo.platform, platformData, platformInfo.appFilesUpdaterOptions, platformInfo.platformTemplate, platformInfo.projectData, platformInfo.config, platformInfo.nativePrepare); + const requiresNativePrepare = (!platformInfo.nativePrepare || !platformInfo.nativePrepare.skipNativePrepare) && changesInfo.nativePlatformStatus === constants.NativePlatformStatus.requiresPrepare; + + if (changesInfo.hasChanges || platformInfo.appFilesUpdaterOptions.bundle || requiresNativePrepare) { + await this.preparePlatformCore( + platformInfo.platform, + platformInfo.appFilesUpdaterOptions, + platformInfo.projectData, + platformInfo.config, + platformInfo.env, + changesInfo, + platformInfo.filesToSync, + platformInfo.nativePrepare, + ); + this.$projectChangesService.savePrepareInfo(platformInfo.platform, platformInfo.projectData); } else { this.$logger.out("Skipping prepare."); } @@ -233,15 +255,35 @@ export class PlatformService extends EventEmitter implements IPlatformService { /* Hooks are expected to use "filesToSync" parameter, as to give plugin authors additional information about the sync process.*/ @helpers.hook('prepare') - private async preparePlatformCore(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, projectData: IProjectData, platformSpecificData: IPlatformSpecificData, changesInfo?: IProjectChangesInfo, filesToSync?: Array, nativePrepare?: INativePrepare): Promise { + private async preparePlatformCore(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, projectData: IProjectData, platformSpecificData: IPlatformSpecificData, env: Object, changesInfo?: IProjectChangesInfo, filesToSync?: string[], nativePrepare?: INativePrepare): Promise { this.$logger.out("Preparing project..."); const platformData = this.$platformsData.getPlatformData(platform, projectData); const projectFilesConfig = helpers.getProjectFilesConfig({ isReleaseBuild: appFilesUpdaterOptions.release }); - await this.$preparePlatformJSService.preparePlatform(platform, platformData, appFilesUpdaterOptions, projectData, platformSpecificData, changesInfo, filesToSync, projectFilesConfig); + await this.$preparePlatformJSService.preparePlatform({ + platform, + platformData, + projectFilesConfig, + appFilesUpdaterOptions, + projectData, + platformSpecificData, + changesInfo, + filesToSync, + env + }); if (!nativePrepare || !nativePrepare.skipNativePrepare) { - await this.$preparePlatformNativeService.preparePlatform(platform, platformData, appFilesUpdaterOptions, projectData, platformSpecificData, changesInfo, filesToSync, projectFilesConfig); + await this.$preparePlatformNativeService.preparePlatform({ + platform, + platformData, + appFilesUpdaterOptions, + projectData, + platformSpecificData, + changesInfo, + filesToSync, + projectFilesConfig, + env + }); } const directoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); @@ -402,35 +444,42 @@ export class PlatformService extends EventEmitter implements IPlatformService { this.$logger.out(`Successfully installed on device with identifier '${device.deviceInfo.identifier}'.`); } - public async deployPlatform(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, deployOptions: IDeployPlatformOptions, projectData: IProjectData, config: IPlatformOptions): Promise { - await this.preparePlatform(platform, appFilesUpdaterOptions, deployOptions.platformTemplate, projectData, config); + public async deployPlatform(deployInfo: IDeployPlatformInfo): Promise { + await this.preparePlatform({ + platform: deployInfo.platform, + appFilesUpdaterOptions: deployInfo.appFilesUpdaterOptions, + platformTemplate: deployInfo.deployOptions.platformTemplate, + projectData: deployInfo.projectData, + config: deployInfo.config, + env: deployInfo.env + }); const options: Mobile.IDevicesServicesInitializationOptions = { - platform: platform, deviceId: deployOptions.device, emulator: deployOptions.emulator + platform: deployInfo.platform, deviceId: deployInfo.deployOptions.device, emulator: deployInfo.deployOptions.emulator }; await this.$devicesService.initialize(options); const action = async (device: Mobile.IDevice): Promise => { const buildConfig: IBuildConfig = { buildForDevice: !this.$devicesService.isiOSSimulator(device), - projectDir: deployOptions.projectDir, - release: deployOptions.release, - device: deployOptions.device, - provision: deployOptions.provision, - teamId: deployOptions.teamId, - keyStoreAlias: deployOptions.keyStoreAlias, - keyStoreAliasPassword: deployOptions.keyStoreAliasPassword, - keyStorePassword: deployOptions.keyStorePassword, - keyStorePath: deployOptions.keyStorePath, - clean: deployOptions.clean + projectDir: deployInfo.deployOptions.projectDir, + release: deployInfo.deployOptions.release, + device: deployInfo.deployOptions.device, + provision: deployInfo.deployOptions.provision, + teamId: deployInfo.deployOptions.teamId, + keyStoreAlias: deployInfo.deployOptions.keyStoreAlias, + keyStoreAliasPassword: deployInfo.deployOptions.keyStoreAliasPassword, + keyStorePassword: deployInfo.deployOptions.keyStorePassword, + keyStorePath: deployInfo.deployOptions.keyStorePath, + clean: deployInfo.deployOptions.clean }; - const shouldBuild = await this.shouldBuild(platform, projectData, buildConfig); + const shouldBuild = await this.shouldBuild(deployInfo.platform, deployInfo.projectData, buildConfig); if (shouldBuild) { - await this.buildPlatform(platform, buildConfig, projectData); + await this.buildPlatform(deployInfo.platform, buildConfig, deployInfo.projectData); } else { this.$logger.out("Skipping package build. No changes detected on the native side. This will be fast!"); } - if (deployOptions.forceInstall || shouldBuild || (await this.shouldInstall(device, projectData))) { - await this.installApplication(device, buildConfig, projectData); + if (deployInfo.deployOptions.forceInstall || shouldBuild || (await this.shouldInstall(device, deployInfo.projectData))) { + await this.installApplication(device, buildConfig, deployInfo.projectData); } else { this.$logger.out("Skipping install."); } @@ -438,12 +487,12 @@ export class PlatformService extends EventEmitter implements IPlatformService { await this.trackActionForPlatform({ action: constants.TrackActionNames.Deploy, platform: device.deviceInfo.platform, isForDevice: !device.isEmulator, deviceOsVersion: device.deviceInfo.version }); }; - if (deployOptions.device) { - const device = await this.$devicesService.getDevice(deployOptions.device); - deployOptions.device = device.deviceInfo.identifier; + if (deployInfo.deployOptions.device) { + const device = await this.$devicesService.getDevice(deployInfo.deployOptions.device); + deployInfo.deployOptions.device = device.deviceInfo.identifier; } - await this.$devicesService.execute(action, this.getCanExecuteAction(platform, deployOptions)); + await this.$devicesService.execute(action, this.getCanExecuteAction(deployInfo.platform, deployInfo.deployOptions)); } public async startApplication(platform: string, runOptions: IRunPlatformOptions, projectId: string): Promise { diff --git a/lib/services/prepare-platform-js-service.ts b/lib/services/prepare-platform-js-service.ts index bb2ae88b40..ad6fccc4be 100644 --- a/lib/services/prepare-platform-js-service.ts +++ b/lib/services/prepare-platform-js-service.ts @@ -2,48 +2,53 @@ import * as constants from "../constants"; import * as path from "path"; import * as shell from "shelljs"; import * as temp from "temp"; +import { hook } from "../common/helpers"; import { PreparePlatformService } from "./prepare-platform-service"; temp.track(); -export class PreparePlatformJSService extends PreparePlatformService implements IPreparePlatformNativeService { +export class PreparePlatformJSService extends PreparePlatformService implements IPreparePlatformService { constructor($fs: IFileSystem, $xmlValidator: IXmlValidator, + $hooksService: IHooksService, private $errors: IErrors, private $logger: ILogger, private $projectDataService: IProjectDataService, private $nodeModulesBuilder: INodeModulesBuilder, private $npm: INodePackageManager) { - super($fs, $xmlValidator); + super($fs, $hooksService, $xmlValidator); } - public async addPlatform(platformData: IPlatformData, frameworkDir: string, installedVersion: string, projectData: IProjectData, config: IPlatformOptions, platformTemplate: string, ): Promise { - const customTemplateOptions = await this.getPathToPlatformTemplate(platformTemplate, platformData.frameworkPackageName, projectData.projectDir); - config.pathToTemplate = customTemplateOptions && customTemplateOptions.pathToTemplate; + public async addPlatform(info: IAddPlatformInfo): Promise { + const customTemplateOptions = await this.getPathToPlatformTemplate(info.platformTemplate, info.platformData.frameworkPackageName, info.projectData.projectDir); + info.config.pathToTemplate = customTemplateOptions && customTemplateOptions.pathToTemplate; - const frameworkPackageNameData: any = { version: installedVersion }; + const frameworkPackageNameData: any = { version: info.installedVersion }; if (customTemplateOptions) { frameworkPackageNameData.template = customTemplateOptions.selectedTemplate; } - this.$projectDataService.setNSValue(projectData.projectDir, platformData.frameworkPackageName, frameworkPackageNameData); + this.$projectDataService.setNSValue(info.projectData.projectDir, info.platformData.frameworkPackageName, frameworkPackageNameData); } - public async preparePlatform(platform: string, platformData: IPlatformData, appFilesUpdaterOptions: IAppFilesUpdaterOptions, projectData: IProjectData, platformSpecificData: IPlatformSpecificData, changesInfo?: IProjectChangesInfo, filesToSync?: Array, projectFilesConfig?: IProjectFilesConfig): Promise { - if (!changesInfo || changesInfo.appFilesChanged) { - await this.copyAppFiles(platformData, appFilesUpdaterOptions, projectData); + @hook('prepareJSApp') + public async preparePlatform(config: IPreparePlatformJSInfo): Promise { + if (!config.changesInfo || config.changesInfo.appFilesChanged || config.changesInfo.changesRequirePrepare) { + await this.copyAppFiles(config); + } + if (config.changesInfo && !config.changesInfo.changesRequirePrepare) { // remove the App_Resources folder from the app/assets as here we're applying other files changes. - const appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + const appDestinationDirectoryPath = path.join(config.platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); const appResourcesDirectoryPath = path.join(appDestinationDirectoryPath, constants.APP_RESOURCES_FOLDER_NAME); if (this.$fs.exists(appResourcesDirectoryPath)) { this.$fs.deleteDirectory(appResourcesDirectoryPath); } } - if (!changesInfo || changesInfo.modulesChanged) { - await this.copyTnsModules(platform, platformData, projectData, projectFilesConfig); + if (!config.changesInfo || config.changesInfo.modulesChanged) { + await this.copyTnsModules(config.platform, config.platformData, config.projectData, config.appFilesUpdaterOptions, config.projectFilesConfig); } } @@ -75,14 +80,21 @@ export class PreparePlatformJSService extends PreparePlatformService implements return null; } - private async copyTnsModules(platform: string, platformData: IPlatformData, projectData: IProjectData, projectFilesConfig?: IProjectFilesConfig): Promise { + private async copyTnsModules(platform: string, platformData: IPlatformData, projectData: IProjectData, appFilesUpdaterOptions: IAppFilesUpdaterOptions, projectFilesConfig?: IProjectFilesConfig): Promise { const appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); const lastModifiedTime = this.$fs.exists(appDestinationDirectoryPath) ? this.$fs.getFsStats(appDestinationDirectoryPath).mtime : null; try { - const tnsModulesDestinationPath = path.join(appDestinationDirectoryPath, constants.TNS_MODULES_FOLDER_NAME); + const absoluteOutputPath = path.join(appDestinationDirectoryPath, constants.TNS_MODULES_FOLDER_NAME); // Process node_modules folder - await this.$nodeModulesBuilder.prepareJSNodeModules(tnsModulesDestinationPath, platform, lastModifiedTime, projectData, projectFilesConfig); + await this.$nodeModulesBuilder.prepareJSNodeModules({ + absoluteOutputPath, + platform, + lastModifiedTime, + projectData, + appFilesUpdaterOptions, + projectFilesConfig + }); } catch (error) { this.$logger.debug(error); shell.rm("-rf", appDestinationDirectoryPath); diff --git a/lib/services/prepare-platform-native-service.ts b/lib/services/prepare-platform-native-service.ts index 94e2a576b6..ca73ca73f8 100644 --- a/lib/services/prepare-platform-native-service.ts +++ b/lib/services/prepare-platform-native-service.ts @@ -3,51 +3,51 @@ import * as path from "path"; import * as shell from "shelljs"; import { PreparePlatformService } from "./prepare-platform-service"; -export class PreparePlatformNativeService extends PreparePlatformService implements IPreparePlatformNativeService { +export class PreparePlatformNativeService extends PreparePlatformService implements IPreparePlatformService { constructor($fs: IFileSystem, $xmlValidator: IXmlValidator, + $hooksService: IHooksService, private $nodeModulesBuilder: INodeModulesBuilder, private $pluginsService: IPluginsService, private $projectChangesService: IProjectChangesService) { - super($fs, $xmlValidator); + super($fs, $hooksService, $xmlValidator); } - public async addPlatform(platformData: IPlatformData, frameworkDir: string, installedVersion: string, projectData: IProjectData, config: IPlatformOptions): Promise { - await platformData.platformProjectService.createProject(path.resolve(frameworkDir), installedVersion, projectData, config); - platformData.platformProjectService.ensureConfigurationFileInAppResources(projectData); - await platformData.platformProjectService.interpolateData(projectData, config); - platformData.platformProjectService.afterCreateProject(platformData.projectRoot, projectData); + public async addPlatform(info: IAddPlatformInfo): Promise { + await info.platformData.platformProjectService.createProject(path.resolve(info.frameworkDir), info.installedVersion, info.projectData, info.config); + info.platformData.platformProjectService.ensureConfigurationFileInAppResources(info.projectData); + await info.platformData.platformProjectService.interpolateData(info.projectData, info.config); + info.platformData.platformProjectService.afterCreateProject(info.platformData.projectRoot, info.projectData); } - public async preparePlatform(platform: string, platformData: IPlatformData, appFilesUpdaterOptions: IAppFilesUpdaterOptions, projectData: IProjectData, platformSpecificData: IPlatformSpecificData, changesInfo?: IProjectChangesInfo, filesToSync?: Array, projectFilesConfig?: IProjectFilesConfig): Promise { - if (changesInfo.hasChanges) { - await this.cleanProject(platform, appFilesUpdaterOptions, platformData, projectData); + public async preparePlatform(config: IPreparePlatformJSInfo): Promise { + if (config.changesInfo.hasChanges) { + await this.cleanProject(config.platform, config.appFilesUpdaterOptions, config.platformData, config.projectData); } - if (!changesInfo || changesInfo.changesRequirePrepare) { - await this.copyAppFiles(platformData, appFilesUpdaterOptions, projectData); - this.copyAppResources(platformData, projectData); - await platformData.platformProjectService.prepareProject(projectData, platformSpecificData); + if (!config.changesInfo || config.changesInfo.changesRequirePrepare) { + this.copyAppResources(config.platformData, config.projectData); + await config.platformData.platformProjectService.prepareProject(config.projectData, config.platformSpecificData); } - if (!changesInfo || changesInfo.modulesChanged || appFilesUpdaterOptions.bundle) { - await this.$pluginsService.validate(platformData, projectData); + if (!config.changesInfo || config.changesInfo.modulesChanged || config.appFilesUpdaterOptions.bundle) { + await this.$pluginsService.validate(config.platformData, config.projectData); - const appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + const appDestinationDirectoryPath = path.join(config.platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); const lastModifiedTime = this.$fs.exists(appDestinationDirectoryPath) ? this.$fs.getFsStats(appDestinationDirectoryPath).mtime : null; const tnsModulesDestinationPath = path.join(appDestinationDirectoryPath, constants.TNS_MODULES_FOLDER_NAME); // Process node_modules folder - await this.$nodeModulesBuilder.prepareNodeModules(tnsModulesDestinationPath, platform, lastModifiedTime, projectData, projectFilesConfig); + await this.$nodeModulesBuilder.prepareNodeModules(tnsModulesDestinationPath, config.platform, lastModifiedTime, config.projectData, config.projectFilesConfig); } - if (!changesInfo || changesInfo.configChanged || changesInfo.modulesChanged) { - await platformData.platformProjectService.processConfigurationFilesFromAppResources(appFilesUpdaterOptions.release, projectData); + if (!config.changesInfo || config.changesInfo.configChanged || config.changesInfo.modulesChanged) { + await config.platformData.platformProjectService.processConfigurationFilesFromAppResources(config.appFilesUpdaterOptions.release, config.projectData); } - platformData.platformProjectService.interpolateConfigurationFile(projectData, platformSpecificData); - this.$projectChangesService.setNativePlatformStatus(platform, projectData, + config.platformData.platformProjectService.interpolateConfigurationFile(config.projectData, config.platformSpecificData); + this.$projectChangesService.setNativePlatformStatus(config.platform, config.projectData, { nativePlatformStatus: constants.NativePlatformStatus.alreadyPrepared }); } diff --git a/lib/services/prepare-platform-service.ts b/lib/services/prepare-platform-service.ts index 07f54a52ac..ab0067b765 100644 --- a/lib/services/prepare-platform-service.ts +++ b/lib/services/prepare-platform-service.ts @@ -1,24 +1,28 @@ import * as constants from "../constants"; import * as path from "path"; import { AppFilesUpdater } from "./app-files-updater"; -import { EventEmitter } from "events"; -export class PreparePlatformService extends EventEmitter { +export class PreparePlatformService { + // Type with hooks needs to have either $hooksService or $injector injected. + // In order to stop TypeScript from failing for not used $hooksService, use it here. + private get _hooksService(): IHooksService { + return this.$hooksService; + } constructor(protected $fs: IFileSystem, + private $hooksService: IHooksService, private $xmlValidator: IXmlValidator) { - super(); } - protected async copyAppFiles(platformData: IPlatformData, appFilesUpdaterOptions: IAppFilesUpdaterOptions, projectData: IProjectData): Promise { - platformData.platformProjectService.ensureConfigurationFileInAppResources(projectData); - const appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + protected async copyAppFiles(copyAppFilesData: ICopyAppFilesData): Promise { + copyAppFilesData.platformData.platformProjectService.ensureConfigurationFileInAppResources(copyAppFilesData.projectData); + const appDestinationDirectoryPath = path.join(copyAppFilesData.platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); // Copy app folder to native project this.$fs.ensureDirectoryExists(appDestinationDirectoryPath); - const appSourceDirectoryPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME); + const appSourceDirectoryPath = path.join(copyAppFilesData.projectData.projectDir, constants.APP_FOLDER_NAME); - const appUpdater = new AppFilesUpdater(appSourceDirectoryPath, appDestinationDirectoryPath, appFilesUpdaterOptions, this.$fs); + const appUpdater = new AppFilesUpdater(appSourceDirectoryPath, appDestinationDirectoryPath, copyAppFilesData.appFilesUpdaterOptions, this.$fs); appUpdater.updateApp(sourceFiles => { this.$xmlValidator.validateXmlFiles(sourceFiles); }); diff --git a/lib/services/project-changes-service.ts b/lib/services/project-changes-service.ts index 1baafc517c..e09593f014 100644 --- a/lib/services/project-changes-service.ts +++ b/lib/services/project-changes-service.ts @@ -69,7 +69,7 @@ export class ProjectChangesService implements IProjectChangesService { projectData, this.fileChangeRequiresBuild); - if (this._newFiles > 0) { + if (this._newFiles > 0 || this._changesInfo.nativeChanged) { this._changesInfo.modulesChanged = true; } const platformResourcesDir = path.join(projectData.appResourcesDirectoryPath, platformData.normalizedPlatformName); diff --git a/lib/services/test-execution-service.ts b/lib/services/test-execution-service.ts index 4694b0ea8f..ee432369ed 100644 --- a/lib/services/test-execution-service.ts +++ b/lib/services/test-execution-service.ts @@ -59,8 +59,16 @@ class TestExecutionService implements ITestExecutionService { const socketIoJs = (await this.$httpClient.httpRequest(socketIoJsUrl)).body; this.$fs.writeFile(path.join(projectDir, TestExecutionService.SOCKETIO_JS_FILE_NAME), socketIoJs); const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; + const preparePlatformInfo: IPreparePlatformInfo = { + platform, + appFilesUpdaterOptions, + platformTemplate: this.$options.platformTemplate, + projectData, + config: this.$options, + env: this.$options.env + }; - if (!await this.$platformService.preparePlatform(platform, appFilesUpdaterOptions, this.$options.platformTemplate, projectData, this.$options)) { + if (!await this.$platformService.preparePlatform(preparePlatformInfo)) { this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation."); } @@ -117,6 +125,7 @@ class TestExecutionService implements ITestExecutionService { projectDir: projectData.projectDir, skipWatcher: !this.$options.watch || this.$options.justlaunch, watchAllFiles: this.$options.syncAllFiles, + env: this.$options.env }; await this.$liveSyncService.liveSync(deviceDescriptors, liveSyncInfo); @@ -175,9 +184,18 @@ class TestExecutionService implements ITestExecutionService { } const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; + const preparePlatformInfo: IPreparePlatformInfo = { + platform, + appFilesUpdaterOptions, + platformTemplate: this.$options.platformTemplate, + projectData, + config: this.$options, + env: this.$options.env + }; + // 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, appFilesUpdaterOptions, this.$options.platformTemplate, projectData, this.$options)) { + if (!await this.$platformService.preparePlatform(preparePlatformInfo)) { this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation."); } @@ -232,6 +250,7 @@ class TestExecutionService implements ITestExecutionService { projectDir: projectData.projectDir, skipWatcher: !this.$options.watch || this.$options.justlaunch, watchAllFiles: this.$options.syncAllFiles, + env: this.$options.env }; await this.$liveSyncService.liveSync(deviceDescriptors, liveSyncInfo); diff --git a/lib/tools/node-modules/node-modules-builder.ts b/lib/tools/node-modules/node-modules-builder.ts index 8e84bb8031..f1607da12b 100644 --- a/lib/tools/node-modules/node-modules-builder.ts +++ b/lib/tools/node-modules/node-modules-builder.ts @@ -14,10 +14,10 @@ export class NodeModulesBuilder implements INodeModulesBuilder { await npmPluginPrepare.preparePlugins(productionDependencies, platform, projectData, projectFilesConfig); } - public async prepareJSNodeModules(absoluteOutputPath: string, platform: string, lastModifiedTime: Date, projectData: IProjectData, projectFilesConfig: IProjectFilesConfig): Promise { - const productionDependencies = this.initialPrepareNodeModules(absoluteOutputPath, platform, lastModifiedTime, projectData); + public async prepareJSNodeModules(jsNodeModulesData: IJsNodeModulesData): Promise { + const productionDependencies = this.initialPrepareNodeModules(jsNodeModulesData.absoluteOutputPath, jsNodeModulesData.platform, jsNodeModulesData.lastModifiedTime, jsNodeModulesData.projectData); const npmPluginPrepare: NpmPluginPrepare = this.$injector.resolve(NpmPluginPrepare); - await npmPluginPrepare.prepareJSPlugins(productionDependencies, platform, projectData, projectFilesConfig); + await npmPluginPrepare.prepareJSPlugins(productionDependencies, jsNodeModulesData.platform, jsNodeModulesData.projectData, jsNodeModulesData.projectFilesConfig); } public cleanNodeModules(absoluteOutputPath: string, platform: string): void { diff --git a/test/npm-support.ts b/test/npm-support.ts index b4757e924c..4f5f08d0a0 100644 --- a/test/npm-support.ts +++ b/test/npm-support.ts @@ -16,6 +16,8 @@ import NodeModulesLib = require("../lib/tools/node-modules/node-modules-builder" import PluginsServiceLib = require("../lib/services/plugins-service"); import ChildProcessLib = require("../lib/common/child-process"); import ProjectFilesManagerLib = require("../lib/common/services/project-files-manager"); +import { PreparePlatformNativeService } from "../lib/services/prepare-platform-native-service"; +import { PreparePlatformJSService } from "../lib/services/prepare-platform-js-service"; import { DeviceAppDataFactory } from "../lib/common/mobile/device-app-data/device-app-data-factory"; import { LocalToDevicePathDataFactory } from "../lib/common/mobile/local-to-device-path-data-factory"; import { MobileHelper } from "../lib/common/mobile/mobile-helper"; @@ -66,6 +68,8 @@ function createTestInjector(): IInjector { testInjector.register("commandsServiceProvider", { registerDynamicSubCommands: () => { /* intentionally left blank */ } }); + testInjector.register("preparePlatformNativeService", PreparePlatformNativeService); + testInjector.register("preparePlatformJSService", PreparePlatformJSService); testInjector.register("pluginVariablesService", {}); testInjector.register("deviceAppDataFactory", DeviceAppDataFactory); testInjector.register("localToDevicePathDataFactory", LocalToDevicePathDataFactory); @@ -194,7 +198,14 @@ async function preparePlatform(testInjector: IInjector): Promise { projectData.initializeProjectData(); const options: IOptions = testInjector.resolve("options"); - await platformService.preparePlatform("android", { bundle: options.bundle, release: options.release }, "", projectData, options); + await platformService.preparePlatform({ + platform: "android", + appFilesUpdaterOptions: { bundle: options.bundle, release: options.release }, + platformTemplate: "", + projectData, + config: options, + env: {} + }); } describe("Npm support tests", () => { diff --git a/test/platform-commands.ts b/test/platform-commands.ts index 07d11e6430..c14ce1cbd1 100644 --- a/test/platform-commands.ts +++ b/test/platform-commands.ts @@ -138,6 +138,8 @@ function createTestInjector() { testInjector.register("devicePlatformsConstants", DevicePlatformsConstants); testInjector.register("xmlValidator", XmlValidator); testInjector.register("npm", {}); + testInjector.register("preparePlatformNativeService", {}); + testInjector.register("preparePlatformJSService", {}); testInjector.register("childProcess", ChildProcessLib.ChildProcess); testInjector.register("projectChangesService", ProjectChangesLib.ProjectChangesService); testInjector.register("emulatorPlatformService", stubs.EmulatorPlatformService); diff --git a/test/platform-service.ts b/test/platform-service.ts index 5c12c376fa..4caf8b8992 100644 --- a/test/platform-service.ts +++ b/test/platform-service.ts @@ -16,6 +16,8 @@ import { ProjectFilesProvider } from "../lib/providers/project-files-provider"; import { MobilePlatformsCapabilities } from "../lib/mobile-platforms-capabilities"; import { DevicePlatformsConstants } from "../lib/common/mobile/device-platforms-constants"; import { XmlValidator } from "../lib/xml-validator"; +import { PreparePlatformNativeService } from "../lib/services/prepare-platform-native-service"; +import { PreparePlatformJSService } from "../lib/services/prepare-platform-js-service"; import * as ChildProcessLib from "../lib/common/child-process"; import ProjectChangesLib = require("../lib/services/project-changes-service"); import { Messages } from "../lib/common/messages/messages"; @@ -74,6 +76,8 @@ function createTestInjector() { testInjector.register("mobilePlatformsCapabilities", MobilePlatformsCapabilities); testInjector.register("devicePlatformsConstants", DevicePlatformsConstants); testInjector.register("xmlValidator", XmlValidator); + testInjector.register("preparePlatformNativeService", PreparePlatformNativeService); + testInjector.register("preparePlatformJSService", PreparePlatformJSService); testInjector.register("npm", { uninstall: async () => { return true; @@ -441,7 +445,14 @@ describe('Platform Service Tests', () => { platformService = testInjector.resolve("platformService"); const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: false, release: release }; - await platformService.preparePlatform(platformToTest, appFilesUpdaterOptions, "", projectData, { provision: null, teamId: null, sdk: null, frameworkPath: null, ignoreScripts: false }); + await platformService.preparePlatform({ + platform: platformToTest, + appFilesUpdaterOptions, + platformTemplate: "", + projectData, + config: { provision: null, teamId: null, sdk: null, frameworkPath: null, ignoreScripts: false }, + env: {} + }); } async function testPreparePlatform(platformToTest: string, release?: boolean): Promise { @@ -525,7 +536,7 @@ describe('Platform Service Tests', () => { const data: any = {}; if (platform.toLowerCase() === "ios") { data[path.join(appDestFolderPath, "app")] = { - missingFiles: ["test1.ios.js", "test2.android.js", "test2.js", "App_Resources"], + missingFiles: ["test1.ios.js", "test2.android.js", "test2.js"], presentFiles: ["test1.js", "test2-android-js", "test1-ios-js", "main.js"] }; @@ -868,7 +879,14 @@ describe('Platform Service Tests', () => { try { testInjector.resolve("$logger").warn = (text: string) => warnings += text; const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: false, release: false }; - await platformService.preparePlatform("android", appFilesUpdaterOptions, "", projectData, { provision: null, teamId: null, sdk: null, frameworkPath: null, ignoreScripts: false }); + await platformService.preparePlatform({ + platform: "android", + appFilesUpdaterOptions, + platformTemplate: "", + projectData, + config: { provision: null, teamId: null, sdk: null, frameworkPath: null, ignoreScripts: false }, + env: {} + }); } finally { testInjector.resolve("$logger").warn = oldLoggerWarner; } diff --git a/test/stubs.ts b/test/stubs.ts index 04f128f8dc..5d45aa8dc9 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -647,7 +647,7 @@ export class PlatformServiceStub extends EventEmitter implements IPlatformServic return Promise.resolve(); } - public preparePlatform(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, platformTemplate: string): Promise { + public preparePlatform(platformInfo: IPreparePlatformInfo): Promise { return Promise.resolve(true); } @@ -667,7 +667,7 @@ export class PlatformServiceStub extends EventEmitter implements IPlatformServic return Promise.resolve(); } - public deployPlatform(platform: string, appFilesUpdaterOptions: IAppFilesUpdaterOptions, deployOptions: IDeployPlatformOptions): Promise { + public deployPlatform(config: IDeployPlatformInfo): Promise { return Promise.resolve(); } diff --git a/test/tns-appstore-upload.ts b/test/tns-appstore-upload.ts index 50bb3afc3f..970a4d7771 100644 --- a/test/tns-appstore-upload.ts +++ b/test/tns-appstore-upload.ts @@ -98,8 +98,8 @@ class AppStore { expectPreparePlatform() { this.expectedPreparePlatformCalls = 1; - this.platformService.preparePlatform = (platform: string) => { - chai.assert.equal(platform, "iOS"); + this.platformService.preparePlatform = (platformInfo: IPreparePlatformInfo) => { + chai.assert.equal(platformInfo.platform, "iOS"); this.preparePlatformCalls++; return Promise.resolve(true); };