diff --git a/PublicAPI.md b/PublicAPI.md index 3832969abb..e29610ab9a 100644 --- a/PublicAPI.md +++ b/PublicAPI.md @@ -113,6 +113,32 @@ tns.projectService.createProject(projectSettings) +* `isValidNativeScriptProject(projectDir: string): boolean` - Checks if the specified path is a valid NativeScript project. Returns `true` in case the directory is a valid project, `false` otherwise. + +Sample usage: + + + + + + + + + +
+ JavaScript + + TypeScript +
+
+const isValidProject = tns.projectService.isValidNativeScriptProject("/tmp/myProject");
+
+
+
+const isValidProject = tns.projectService.isValidNativeScriptProject("/tmp/myProject");
+
+
+ ## How to add a new method to Public API CLI is designed as command line tool and when it is used as a library, it does not give you access to all of the methods. This is mainly implementation detail. Most of the CLI's code is created to work in command line, not as a library, so before adding method to public API, most probably it will require some modification. For example the `$options` injected module contains information about all `--` options passed on the terminal. When the CLI is used as a library, the options are not populated. Before adding method to public API, make sure its implementation does not rely on `$options`. diff --git a/lib/commands/appstore-upload.ts b/lib/commands/appstore-upload.ts index 33736455a2..89fa327030 100644 --- a/lib/commands/appstore-upload.ts +++ b/lib/commands/appstore-upload.ts @@ -73,7 +73,7 @@ export class PublishIOS implements ICommand { // 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, { provision: this.$options.provision, sdk: this.$options.sdk }); await this.$platformService.buildPlatform(platform, iOSBuildConfig, this.$projectData); - ipaFilePath = this.$platformService.lastOutputPath(platform, { isForDevice: iOSBuildConfig.buildForDevice }, 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, { provision: this.$options.provision, sdk: this.$options.sdk }); diff --git a/lib/commands/build.ts b/lib/commands/build.ts index 524656a0bc..d8107e9cb7 100644 --- a/lib/commands/build.ts +++ b/lib/commands/build.ts @@ -26,7 +26,7 @@ export class BuildCommandBase { }; await this.$platformService.buildPlatform(platform, buildConfig, this.$projectData); if (this.$options.copyTo) { - this.$platformService.copyLastOutput(platform, this.$options.copyTo, { isForDevice: this.$options.forDevice }, this.$projectData); + this.$platformService.copyLastOutput(platform, this.$options.copyTo, buildConfig, this.$projectData); } } } diff --git a/lib/commands/debug.ts b/lib/commands/debug.ts index d9388f8b77..a51b3af5ab 100644 --- a/lib/commands/debug.ts +++ b/lib/commands/debug.ts @@ -16,11 +16,6 @@ } public async execute(args: string[]): Promise { - if (this.$options.start) { - return this.debugService.debug(this.$projectData); - } - - const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; const deployOptions: IDeployPlatformOptions = { clean: this.$options.clean, device: this.$options.device, @@ -31,6 +26,15 @@ provision: this.$options.provision, teamId: this.$options.teamId }; + + const buildConfig: IBuildConfig = _.merge({ buildForDevice: this.$options.forDevice }, deployOptions); + + if (this.$options.start) { + return this.debugService.debug(this.$projectData, buildConfig); + } + + const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: this.$options.bundle, release: this.$options.release }; + await this.$platformService.deployPlatform(this.$devicesService.platform, appFilesUpdaterOptions, deployOptions, this.$projectData, { provision: this.$options.provision, sdk: this.$options.sdk }); this.$config.debugLivesync = true; let applicationReloadAction = async (deviceAppData: Mobile.IDeviceAppData): Promise => { @@ -45,7 +49,7 @@ await deviceAppData.device.applicationManager.stopApplication(applicationId); - await this.debugService.debug(this.$projectData); + await this.debugService.debug(this.$projectData, buildConfig); }; return this.$usbLiveSyncService.liveSync(this.$devicesService.platform, this.$projectData, applicationReloadAction); } diff --git a/lib/common b/lib/common index 137dc3cd5c..f0fec92382 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 137dc3cd5c9fcc8870ce484026ed90f3191cddd6 +Subproject commit f0fec923825f23c6ed85acfd8de3eff5c860560c diff --git a/lib/constants.ts b/lib/constants.ts index 93b1aa85b1..c3fa4e4904 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -15,6 +15,7 @@ export const TESTING_FRAMEWORKS = ['jasmine', 'mocha', 'qunit']; export const TEST_RUNNER_NAME = "nativescript-unit-test-runner"; export const LIVESYNC_EXCLUDED_FILE_PATTERNS = ["**/*.js.map", "**/*.ts"]; export const XML_FILE_EXTENSION = ".xml"; +export const PLATFORMS_DIR_NAME = "platforms"; export class PackageVersion { static NEXT = "next"; diff --git a/lib/definitions/debug.d.ts b/lib/definitions/debug.d.ts index 9d3026e655..e424594783 100644 --- a/lib/definitions/debug.d.ts +++ b/lib/definitions/debug.d.ts @@ -1,6 +1,6 @@ interface IDebugService { - debug(projectData: IProjectData): Promise; - debugStart(projectData: IProjectData): Promise; + debug(projectData: IProjectData, buildConfig: IBuildConfig): Promise; + debugStart(projectData: IProjectData, buildConfig: IBuildConfig): Promise; debugStop(): Promise platform: string; } \ No newline at end of file diff --git a/lib/definitions/platform.d.ts b/lib/definitions/platform.d.ts index 193bd93866..223fc2bfb1 100644 --- a/lib/definitions/platform.d.ts +++ b/lib/definitions/platform.d.ts @@ -1,6 +1,6 @@ interface IPlatformService extends NodeJS.EventEmitter { cleanPlatforms(platforms: string[], platformTemplate: string, projectData: IProjectData, platformSpecificData: IPlatformSpecificData, framework?: string): Promise; - + addPlatforms(platforms: string[], platformTemplate: string, projectData: IProjectData, platformSpecificData: IPlatformSpecificData, frameworkPath?: string): Promise; /** @@ -143,28 +143,37 @@ interface IPlatformService extends NodeJS.EventEmitter { /** * Returns information about the latest built application for device in the current project. * @param {IPlatformData} platformData Data describing the current platform. + * @param {IBuildConfig} buildConfig Defines if the build is for release configuration. * @returns {IApplicationPackage} Information about latest built application. */ - getLatestApplicationPackageForDevice(platformData: IPlatformData): IApplicationPackage; + getLatestApplicationPackageForDevice(platformData: IPlatformData, buildConfig: IBuildConfig): IApplicationPackage; /** * Returns information about the latest built application for simulator in the current project. * @param {IPlatformData} platformData Data describing the current platform. + * @param {IBuildConfig} buildConfig Defines if the build is for release configuration. * @returns {IApplicationPackage} Information about latest built application. */ - getLatestApplicationPackageForEmulator(platformData: IPlatformData): IApplicationPackage; + getLatestApplicationPackageForEmulator(platformData: IPlatformData, buildConfig: IBuildConfig): IApplicationPackage; /** * Copies latest build output to a specified location. * @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 {IBuildConfig} buildConfig Defines if the searched artifact should be for simulator and is it built for release. * @param {IProjectData} projectData DTO with information about the project. * @returns {void} */ - copyLastOutput(platform: string, targetPath: string, settings: {isForDevice: boolean}, projectData: IProjectData): void; + copyLastOutput(platform: string, targetPath: string, buildConfig: IBuildConfig, projectData: IProjectData): void; - lastOutputPath(platform: string, settings: { isForDevice: boolean }, projectData: IProjectData): string; + /** + * Gets the latest build output. + * @param {string} platform Mobile platform - Android, iOS. + * @param {IBuildConfig} buildConfig Defines if the searched artifact should be for simulator and is it built for release. + * @param {IProjectData} projectData DTO with information about the project. + * @returns {string} The path to latest built artifact. + */ + lastOutputPath(platform: string, buildConfig: IBuildConfig, projectData: IProjectData): string; /** * Reads contents of a file on device. @@ -209,8 +218,7 @@ interface IPlatformData { appDestinationDirectoryPath: string; deviceBuildOutputPath: string; emulatorBuildOutputPath?: string; - validPackageNamesForDevice: string[]; - validPackageNamesForEmulator?: string[]; + getValidPackageNames(buildOptions: { isReleaseBuild?: boolean, isForDevice?: boolean }): string[]; frameworkFilesExtensions: string[]; frameworkDirectoriesExtensions?: string[]; frameworkDirectoriesNames?: string[]; diff --git a/lib/definitions/project-changes.d.ts b/lib/definitions/project-changes.d.ts index b394fb92e8..cd5bae3cfa 100644 --- a/lib/definitions/project-changes.d.ts +++ b/lib/definitions/project-changes.d.ts @@ -19,8 +19,10 @@ interface IProjectChangesInfo { changesRequireBuild: boolean; } +interface IProjectChangesOptions extends IAppFilesUpdaterOptions, IProvision {} + interface IProjectChangesService { - checkForChanges(platform: string, projectData: IProjectData): IProjectChangesInfo; + checkForChanges(platform: string, projectData: IProjectData, buildOptions: IProjectChangesOptions): IProjectChangesInfo; getPrepareInfo(platform: string, projectData: IProjectData): IPrepareInfo; savePrepareInfo(platform: string, projectData: IProjectData): void; getPrepareInfoFilePath(platform: string, projectData: IProjectData): string; diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index bb180a357c..c08a61ab42 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -254,11 +254,10 @@ interface IPlatformProjectService extends NodeJS.EventEmitter { /** * 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[], projectData: IProjectData): Promise + cleanProject(projectRoot: string, projectData: IProjectData): Promise } interface IAndroidProjectPropertiesManager { diff --git a/lib/project-data.ts b/lib/project-data.ts index dc4ee0b10e..da438d88cc 100644 --- a/lib/project-data.ts +++ b/lib/project-data.ts @@ -9,8 +9,6 @@ interface IProjectType { } export class ProjectData implements IProjectData { - private static OLD_PROJECT_FILE_NAME = ".tnsproject"; - /** * NOTE: Order of the elements is important as the TypeScript dependencies are commonly included in Angular project as well. */ @@ -42,45 +40,51 @@ export class ProjectData implements IProjectData { constructor(private $fs: IFileSystem, private $errors: IErrors, - private $logger: ILogger, private $projectHelper: IProjectHelper, private $staticConfig: IStaticConfig, - private $options: IOptions) { } + private $options: IOptions, + private $logger: ILogger) { } - public initializeProjectData(projectDir? :string): void { + public initializeProjectData(projectDir?: string): void { projectDir = projectDir || this.$projectHelper.projectDir; // If no project found, projectDir should be null if (projectDir) { - this.initializeProjectDataCore(projectDir); + const projectFilePath = path.join(projectDir, this.$staticConfig.PROJECT_FILE_NAME); let data: any = null; - if (this.$fs.exists(this.projectFilePath)) { + if (this.$fs.exists(projectFilePath)) { let fileContent: any = null; try { - fileContent = this.$fs.readJson(this.projectFilePath); + fileContent = this.$fs.readJson(projectFilePath); data = fileContent[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE]; } catch (err) { - this.$errors.fail({ - formatStr: "The project file %s is corrupted." + EOL + - "Consider restoring an earlier version from your source control or backup." + EOL + - "Additional technical info: %s", - suppressCommandHelp: true - }, - this.projectFilePath, err.toString()); + this.$errors.failWithoutHelp(`The project file ${this.projectFilePath} is corrupted. ${EOL}` + + `Consider restoring an earlier version from your source control or backup.${EOL}` + + `Additional technical info: ${err.toString()}`); } if (data) { + this.projectDir = projectDir; + this.projectName = this.$projectHelper.sanitizeName(path.basename(projectDir)); + this.platformsDir = path.join(projectDir, constants.PLATFORMS_DIR_NAME); + this.projectFilePath = projectFilePath; + this.appDirectoryPath = path.join(projectDir, constants.APP_FOLDER_NAME); + this.appResourcesDirectoryPath = path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); this.projectId = data.id; this.dependencies = fileContent.dependencies; this.devDependencies = fileContent.devDependencies; this.projectType = this.getProjectType(); - } else { // This is the case when we have package.json file but nativescipt key is not presented in it - this.tryToUpgradeProject(); + + return; } } - } else { // This is the case when no project file found - this.tryToUpgradeProject(); } + + const currentDir = path.resolve("."); + this.$logger.trace(`Unable to find project. projectDir: ${projectDir}, options.path: ${this.$options.path}, ${currentDir}`); + + // This is the case when no project file found + this.$errors.fail("No project found at or above '%s' and neither was a --path specified.", projectDir || this.$options.path || currentDir); } private getProjectType(): string { @@ -97,49 +101,5 @@ export class ProjectData implements IProjectData { return detectedProjectType; } - - private throwNoProjectFoundError(): void { - this.$errors.fail("No project found at or above '%s' and neither was a --path specified.", this.$options.path || path.resolve(".")); - } - - private tryToUpgradeProject(): void { - let projectDir = this.projectDir || path.resolve(this.$options.path || "."); - let oldProjectFilePath = path.join(projectDir, ProjectData.OLD_PROJECT_FILE_NAME); - if (this.$fs.exists(oldProjectFilePath)) { - this.upgrade(projectDir, oldProjectFilePath); - } else { - this.throwNoProjectFoundError(); - } - } - - private upgrade(projectDir: string, oldProjectFilePath: string): void { - try { - let oldProjectData = this.$fs.readJson(oldProjectFilePath); - - let newProjectFilePath = this.projectFilePath || path.join(projectDir, this.$staticConfig.PROJECT_FILE_NAME); - let newProjectData = this.$fs.exists(newProjectFilePath) ? this.$fs.readJson(newProjectFilePath) : {}; - newProjectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE] = oldProjectData; - this.$fs.writeJson(newProjectFilePath, newProjectData); - this.projectId = newProjectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE].id; - - this.$fs.deleteFile(oldProjectFilePath); - } catch (err) { - this.$logger.out("An error occurred while upgrading your project."); - throw err; - } - - this.initializeProjectDataCore(projectDir); - - this.$logger.out("Successfully upgraded your project file."); - } - - private initializeProjectDataCore(projectDir: string): void { - this.projectDir = projectDir; - this.projectName = this.$projectHelper.sanitizeName(path.basename(projectDir)); - this.platformsDir = path.join(projectDir, "platforms"); - this.projectFilePath = path.join(projectDir, this.$staticConfig.PROJECT_FILE_NAME); - this.appDirectoryPath = path.join(projectDir, constants.APP_FOLDER_NAME); - this.appResourcesDirectoryPath = path.join(projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); - } } $injector.register("projectData", ProjectData); diff --git a/lib/providers/livesync-provider.ts b/lib/providers/livesync-provider.ts index 747cf03a8b..a175688275 100644 --- a/lib/providers/livesync-provider.ts +++ b/lib/providers/livesync-provider.ts @@ -45,10 +45,10 @@ export class LiveSyncProvider implements ILiveSyncProvider { 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; + return this.$platformService.getLatestApplicationPackageForEmulator(platformData, buildConfig).packageName; } - return this.$platformService.getLatestApplicationPackageForDevice(platformData).packageName; + return this.$platformService.getLatestApplicationPackageForDevice(platformData, buildConfig).packageName; } public async preparePlatformForSync(platform: string, provision: any, projectData: IProjectData): Promise { diff --git a/lib/services/android-debug-service.ts b/lib/services/android-debug-service.ts index 5f22534ea0..24ba6c0e1a 100644 --- a/lib/services/android-debug-service.ts +++ b/lib/services/android-debug-service.ts @@ -28,18 +28,18 @@ class AndroidDebugService implements IDebugService { this._device = newDevice; } - public async debug(projectData: IProjectData): Promise { + public async debug(projectData: IProjectData, buildConfig: IBuildConfig): Promise { return this.$options.emulator - ? this.debugOnEmulator(projectData) - : this.debugOnDevice(projectData); + ? this.debugOnEmulator(projectData, buildConfig) + : this.debugOnDevice(projectData, buildConfig); } - private async debugOnEmulator(projectData: IProjectData): Promise { + private async debugOnEmulator(projectData: IProjectData, buildConfig: IBuildConfig): 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(projectData); + await this.debugOnDevice(projectData, buildConfig); } private isPortAvailable(candidatePort: number): Promise { @@ -108,7 +108,7 @@ class AndroidDebugService implements IDebugService { return this.device.adb.executeCommand(["forward", `tcp:${local}`, `localabstract:${remote}`]); } - private async debugOnDevice(projectData: IProjectData): Promise { + private async debugOnDevice(projectData: IProjectData, buildConfig: IBuildConfig): Promise { let packageFile = ""; if (!this.$options.start && !this.$options.emulator) { @@ -117,7 +117,7 @@ class AndroidDebugService implements IDebugService { this.$options.forDevice = !!cachedDeviceOption; let platformData = this.$platformsData.getPlatformData(this.platform, projectData); - packageFile = this.$platformService.getLatestApplicationPackageForDevice(platformData).packageName; + packageFile = this.$platformService.getLatestApplicationPackageForDevice(platformData, buildConfig).packageName; this.$logger.out("Using ", packageFile); } @@ -170,7 +170,7 @@ class AndroidDebugService implements IDebugService { await this.debugStartCore(packageName); } - public async debugStart(projectData: IProjectData): Promise { + public async debugStart(projectData: IProjectData, buildConfig: IBuildConfig): Promise { await this.$devicesService.initialize({ platform: this.platform, deviceId: this.$options.device }); let action = (device: Mobile.IAndroidDevice): Promise => { this.device = device; diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 500d4831e6..8baaa94d97 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -5,6 +5,7 @@ import * as semver from "semver"; import * as projectServiceBaseLib from "./platform-project-service-base"; import { DeviceAndroidDebugBridge } from "../common/mobile/android/device-android-debug-bridge"; import { EOL } from "os"; +import { Configurations } from "../common/constants"; export class AndroidProjectService extends projectServiceBaseLib.PlatformProjectServiceBase implements IPlatformProjectService { private static VALUES_DIRNAME = "values"; @@ -57,12 +58,14 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject emulatorServices: this.$androidEmulatorServices, projectRoot: projectRoot, deviceBuildOutputPath: path.join(projectRoot, "build", "outputs", "apk"), - validPackageNamesForDevice: [ - `${packageName}-debug.apk`, - `${packageName}-release.apk`, - `${projectData.projectName}-debug.apk`, - `${projectData.projectName}-release.apk` - ], + getValidPackageNames: (buildOptions: { isReleaseBuild?: boolean, isForDevice?: boolean }): string[] => { + const buildMode = buildOptions.isReleaseBuild ? Configurations.Release.toLowerCase() : Configurations.Debug.toLowerCase(); + + return [ + `${packageName}-${buildMode}.apk`, + `${projectData.projectName}-${buildMode}.apk` + ]; + }, frameworkFilesExtensions: [".jar", ".dat", ".so"], configurationFileName: "AndroidManifest.xml", configurationFilePath: path.join(projectRoot, "src", "main", "AndroidManifest.xml"), @@ -262,7 +265,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject await this.spawn(gradleBin, buildOptions, { stdio: buildConfig.buildOutputStdio || "inherit", cwd: this.getPlatformData(projectData).projectRoot }, - { emitOptions: { eventName: constants.BUILD_OUTPUT_EVENT_NAME }, throwError: false }); + { emitOptions: { eventName: constants.BUILD_OUTPUT_EVENT_NAME }, throwError: true }); } 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."); @@ -376,7 +379,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject // check whether the dependency that's being removed has native code let pluginConfigDir = path.join(this.getPlatformData(projectData).projectRoot, "configurations", pluginData.name); if (this.$fs.exists(pluginConfigDir)) { - await this.cleanProject(this.getPlatformData(projectData).projectRoot, [], projectData); + await this.cleanProject(this.getPlatformData(projectData).projectRoot, projectData); } } catch (e) { if (e.code === "ENOENT") { @@ -407,12 +410,9 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject } } - // We don't need release options here - let buildOptions = this.getBuildOptions({ release: false }, projectData); - let projectRoot = this.getPlatformData(projectData).projectRoot; - await this.cleanProject(projectRoot, buildOptions, projectData); + await this.cleanProject(projectRoot, projectData); } } @@ -425,15 +425,16 @@ 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[], projectData: IProjectData): Promise { - options.unshift("clean"); + public async cleanProject(projectRoot: string, projectData: IProjectData): Promise { + const buildOptions = this.getBuildOptions({ release: false }, projectData); + buildOptions.unshift("clean"); let gradleBin = path.join(projectRoot, "gradlew"); if (this.$hostInfo.isWindows) { gradleBin += ".bat"; } - await this.spawn(gradleBin, options, { stdio: "inherit", cwd: this.getPlatformData(projectData).projectRoot }); + await this.spawn(gradleBin, buildOptions, { stdio: "inherit", cwd: this.getPlatformData(projectData).projectRoot }); } public async cleanDeviceTempFolder(deviceIdentifier: string, projectData: IProjectData): Promise { diff --git a/lib/services/ios-debug-service.ts b/lib/services/ios-debug-service.ts index 6f81637e98..6ef796bec8 100644 --- a/lib/services/ios-debug-service.ts +++ b/lib/services/ios-debug-service.ts @@ -39,7 +39,7 @@ class IOSDebugService implements IDebugService { return "ios"; } - public async debug(projectData: IProjectData): Promise { + public async debug(projectData: IProjectData, buildConfig: IBuildConfig): Promise { if (this.$options.debugBrk && this.$options.start) { this.$errors.failWithoutHelp("Expected exactly one of the --debug-brk or --start options."); } @@ -50,26 +50,26 @@ class IOSDebugService implements IDebugService { if (this.$options.emulator) { if (this.$options.debugBrk) { - return this.emulatorDebugBrk(projectData, true); + return this.emulatorDebugBrk(projectData, buildConfig, true); } else if (this.$options.start) { return this.emulatorStart(projectData); } else { - return this.emulatorDebugBrk(projectData); + return this.emulatorDebugBrk(projectData, buildConfig); } } else { if (this.$options.debugBrk) { - return this.deviceDebugBrk(projectData, true); + return this.deviceDebugBrk(projectData, buildConfig, true); } else if (this.$options.start) { return this.deviceStart(projectData); } else { - return this.deviceDebugBrk(projectData, false); + return this.deviceDebugBrk(projectData, buildConfig, false); } } } - public async debugStart(projectData: IProjectData): Promise { + public async debugStart(projectData: IProjectData, buildConfig: IBuildConfig): Promise { await this.$devicesService.initialize({ platform: this.platform, deviceId: this.$options.device }); - this.$devicesService.execute(async (device: Mobile.IiOSDevice) => await device.isEmulator ? this.emulatorDebugBrk(projectData) : this.debugBrkCore(device, projectData)); + this.$devicesService.execute(async (device: Mobile.IiOSDevice) => await device.isEmulator ? this.emulatorDebugBrk(projectData, buildConfig) : this.debugBrkCore(device, projectData)); } public async debugStop(): Promise { @@ -93,10 +93,10 @@ class IOSDebugService implements IDebugService { } } - private async emulatorDebugBrk(projectData: IProjectData, shouldBreak?: boolean): Promise { + private async emulatorDebugBrk(projectData: IProjectData, buildConfig: IBuildConfig, shouldBreak?: boolean): Promise { let platformData = this.$platformsData.getPlatformData(this.platform, projectData); - let emulatorPackage = this.$platformService.getLatestApplicationPackageForEmulator(platformData); + let emulatorPackage = this.$platformService.getLatestApplicationPackageForEmulator(platformData, buildConfig); let args = shouldBreak ? "--nativescript-debug-brk" : "--nativescript-debug-start"; let child_process = await this.$iOSEmulatorServices.runApplicationOnEmulator(emulatorPackage.packageName, { @@ -135,11 +135,11 @@ class IOSDebugService implements IDebugService { await iOSEmulator.postDarwinNotification(attachRequestMessage); } - private async deviceDebugBrk(projectData: IProjectData, shouldBreak?: boolean): Promise { + private async deviceDebugBrk(projectData: IProjectData, buildConfig: IBuildConfig, 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(projectData, shouldBreak); + return await this.emulatorDebugBrk(projectData, buildConfig, shouldBreak); } const runOptions: IRunPlatformOptions = { diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index bbdb69932b..48a16f9762 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -64,12 +64,13 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ projectRoot: projectRoot, deviceBuildOutputPath: path.join(projectRoot, "build", "device"), emulatorBuildOutputPath: path.join(projectRoot, "build", "emulator"), - validPackageNamesForDevice: [ - projectData.projectName + ".ipa" - ], - validPackageNamesForEmulator: [ - projectData.projectName + ".app" - ], + getValidPackageNames: (buildOptions: { isReleaseBuild?: boolean, isForDevice?: boolean }): string[] => { + if (buildOptions.isForDevice) { + return [projectData.projectName + ".ipa"]; + } + + return [projectData.projectName + ".app"]; + }, frameworkFilesExtensions: [".a", ".framework", ".bin"], frameworkDirectoriesExtensions: [".framework"], frameworkDirectoriesNames: ["Metadata", "metadataGenerator", "NativeScript", "internal"], @@ -339,7 +340,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ args, "exit", { stdio: buildConfig.buildOutputStdio || "inherit", cwd: this.getPlatformData(projectData).projectRoot }, - { emitOptions: { eventName: constants.BUILD_OUTPUT_EVENT_NAME }, throwError: false }); + { emitOptions: { eventName: constants.BUILD_OUTPUT_EVENT_NAME }, throwError: true }); // this.$logger.out("xcodebuild build succeded."); await this.createIpa(projectRoot, projectData, buildConfig.buildOutputStdio); @@ -665,7 +666,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f return { stderr: "", stdout: "", exitCode: 0 }; } - public async cleanProject(projectRoot: string, options: string[]): Promise { + public async cleanProject(projectRoot: string): Promise { return Promise.resolve(); } @@ -1170,6 +1171,10 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f teamId = teams[0].id; this.$logger.warn("Found and using the following development team installed on your system: " + teams[0].name + " (" + teams[0].id + ")"); } else if (teams.length > 0) { + if (!helpers.isInteractive()) { + this.$errors.failWithoutHelp(`Unable to determine default development team. Available development teams are: ${_.map(teams, team => team.id)}. Specify team in app/App_Resources/iOS/build.xcconfig file in the following way: DEVELOPMENT_TEAM = `); + } + let choices: string[] = []; for (let team of teams) { choices.push(team.name + " (" + team.id + ")"); diff --git a/lib/services/local-build-service.ts b/lib/services/local-build-service.ts index cea0053505..fedaeb1ce9 100644 --- a/lib/services/local-build-service.ts +++ b/lib/services/local-build-service.ts @@ -9,14 +9,14 @@ export class LocalBuildService extends EventEmitter { 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); + await this.$platformService.preparePlatform(platform, platformBuildOptions, platformTemplate, this.$projectData, { provision: platformBuildOptions.provision, sdk: null }); 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); + return this.$platformService.lastOutputPath(platform, platformBuildOptions, this.$projectData); } } diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 1880b754e7..841175a125 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -233,7 +233,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { await this.$pluginsService.validate(platformData, projectData); await this.ensurePlatformInstalled(platform, platformTemplate, projectData, platformSpecificData); - let changesInfo = this.$projectChangesService.checkForChanges(platform, projectData); + let changesInfo = this.$projectChangesService.checkForChanges(platform, projectData, { bundle: appFilesUpdaterOptions.bundle, release: appFilesUpdaterOptions.release, provision: platformSpecificData.provision }); this.$logger.trace("Changes info in prepare platform:", changesInfo); @@ -243,7 +243,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { let previousPrepareInfo = this.$projectChangesService.getPrepareInfo(platform, projectData); // clean up prepared plugins when not building for release if (previousPrepareInfo && previousPrepareInfo.release !== appFilesUpdaterOptions.release) { - await platformData.platformProjectService.cleanProject(platformData.projectRoot, [], projectData); + await platformData.platformProjectService.cleanProject(platformData.projectRoot, projectData); } } @@ -364,7 +364,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { if (!this.$fs.exists(outputPath)) { return true; } - let packageNames = forDevice ? platformData.validPackageNamesForDevice : platformData.validPackageNamesForEmulator; + let packageNames = platformData.getValidPackageNames({ isForDevice: forDevice }); let packages = this.getApplicationPackages(outputPath, packageNames); if (packages.length === 0) { return true; @@ -425,21 +425,21 @@ export class PlatformService extends EventEmitter implements IPlatformService { return !localBuildInfo || !deviceBuildInfo || deviceBuildInfo.buildTime !== localBuildInfo.buildTime; } - public async installApplication(device: Mobile.IDevice, options: IRelease, projectData: IProjectData): Promise { + public async installApplication(device: Mobile.IDevice, buildConfig: IBuildConfig, projectData: IProjectData): Promise { this.$logger.out("Installing..."); let platformData = this.$platformsData.getPlatformData(device.deviceInfo.platform, projectData); let packageFile = ""; if (this.$devicesService.isiOSSimulator(device)) { - packageFile = this.getLatestApplicationPackageForEmulator(platformData).packageName; + packageFile = this.getLatestApplicationPackageForEmulator(platformData, buildConfig).packageName; } else { - packageFile = this.getLatestApplicationPackageForDevice(platformData).packageName; + packageFile = this.getLatestApplicationPackageForDevice(platformData, buildConfig).packageName; } await platformData.platformProjectService.cleanDeviceTempFolder(device.deviceInfo.identifier, projectData); await device.applicationManager.reinstallApplication(projectData.projectId, packageFile); - if (!options.release) { + if (!buildConfig.release) { let deviceFilePath = await this.getDeviceBuildInfoFilePath(device, projectData); let buildInfoFilePath = this.getBuildOutputPath(device.deviceInfo.platform, platformData, { buildForDevice: !device.isEmulator }); let appIdentifier = projectData.projectId; @@ -475,7 +475,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { } if (deployOptions.forceInstall || shouldBuild || (await this.shouldInstall(device, projectData))) { - await this.installApplication(device, deployOptions, projectData); + await this.installApplication(device, buildConfig, projectData); } else { this.$logger.out("Skipping install."); } @@ -581,13 +581,13 @@ export class PlatformService extends EventEmitter implements IPlatformService { appUpdater.cleanDestinationApp(); } - public lastOutputPath(platform: string, settings: { isForDevice: boolean }, projectData: IProjectData): string { + public lastOutputPath(platform: string, buildConfig: IBuildConfig, projectData: IProjectData): string { let packageFile: string; let platformData = this.$platformsData.getPlatformData(platform, projectData); - if (settings.isForDevice) { - packageFile = this.getLatestApplicationPackageForDevice(platformData).packageName; + if (buildConfig.buildForDevice) { + packageFile = this.getLatestApplicationPackageForDevice(platformData, buildConfig).packageName; } else { - packageFile = this.getLatestApplicationPackageForEmulator(platformData).packageName; + packageFile = this.getLatestApplicationPackageForEmulator(platformData, buildConfig).packageName; } if (!packageFile || !this.$fs.exists(packageFile)) { this.$errors.failWithoutHelp("Unable to find built application. Try 'tns build %s'.", platform); @@ -595,11 +595,11 @@ export class PlatformService extends EventEmitter implements IPlatformService { return packageFile; } - public copyLastOutput(platform: string, targetPath: string, settings: { isForDevice: boolean }, projectData: IProjectData): void { + public copyLastOutput(platform: string, targetPath: string, buildConfig: IBuildConfig, projectData: IProjectData): void { platform = platform.toLowerCase(); targetPath = path.resolve(targetPath); - let packageFile = this.lastOutputPath(platform, settings, projectData); + let packageFile = this.lastOutputPath(platform, buildConfig, projectData); this.$fs.ensureDirectoryExists(path.dirname(targetPath)); @@ -742,12 +742,12 @@ export class PlatformService extends EventEmitter implements IPlatformService { return packages[0]; } - public getLatestApplicationPackageForDevice(platformData: IPlatformData): IApplicationPackage { - return this.getLatestApplicationPackage(platformData.deviceBuildOutputPath, platformData.validPackageNamesForDevice); + public getLatestApplicationPackageForDevice(platformData: IPlatformData, buildConfig: IBuildConfig): IApplicationPackage { + return this.getLatestApplicationPackage(platformData.deviceBuildOutputPath, platformData.getValidPackageNames({ isForDevice: true, isReleaseBuild: buildConfig.release })); } - public getLatestApplicationPackageForEmulator(platformData: IPlatformData): IApplicationPackage { - return this.getLatestApplicationPackage(platformData.emulatorBuildOutputPath || platformData.deviceBuildOutputPath, platformData.validPackageNamesForEmulator || platformData.validPackageNamesForDevice); + public getLatestApplicationPackageForEmulator(platformData: IPlatformData, buildConfig: IBuildConfig): IApplicationPackage { + return this.getLatestApplicationPackage(platformData.emulatorBuildOutputPath || platformData.deviceBuildOutputPath, platformData.getValidPackageNames({ isForDevice: false, isReleaseBuild: buildConfig.release })); } private async updatePlatform(platform: string, version: string, platformTemplate: string, projectData: IProjectData, platformSpecificData: IPlatformSpecificData): Promise { diff --git a/lib/services/project-changes-service.ts b/lib/services/project-changes-service.ts index 1c454ffbf2..befe87b170 100644 --- a/lib/services/project-changes-service.ts +++ b/lib/services/project-changes-service.ts @@ -38,7 +38,6 @@ export class ProjectChangesService implements IProjectChangesService { constructor( private $platformsData: IPlatformsData, private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, - private $options: IOptions, private $fs: IFileSystem) { } @@ -46,10 +45,10 @@ export class ProjectChangesService implements IProjectChangesService { return this._changesInfo; } - public checkForChanges(platform: string, projectData: IProjectData): IProjectChangesInfo { + public checkForChanges(platform: string, projectData: IProjectData, projectChangesOptions: IProjectChangesOptions): IProjectChangesInfo { let platformData = this.$platformsData.getPlatformData(platform, projectData); this._changesInfo = new ProjectChangesInfo(); - if (!this.ensurePrepareInfo(platform, projectData)) { + if (!this.ensurePrepareInfo(platform, projectData, projectChangesOptions)) { this._newFiles = 0; this._changesInfo.appFilesChanged = this.containsNewerFiles(projectData.appDirectoryPath, projectData.appResourcesDirectoryPath, projectData); this._changesInfo.packageChanged = this.filesChanged([path.join(projectData.projectDir, "package.json")]); @@ -77,7 +76,7 @@ export class ProjectChangesService implements IProjectChangesService { } } if (platform.toLowerCase() === this.$devicePlatformsConstants.iOS.toLowerCase()) { - const nextCommandProvisionUUID = this.$options.provision; + const nextCommandProvisionUUID = projectChangesOptions.provision; // We should consider reading here the provisioning profile UUID from the xcodeproj and xcconfig. const prevProvisionUUID = this._prepareInfo.iOSProvisioningProfileUUID; if (nextCommandProvisionUUID !== prevProvisionUUID) { @@ -86,13 +85,13 @@ export class ProjectChangesService implements IProjectChangesService { this._prepareInfo.iOSProvisioningProfileUUID = nextCommandProvisionUUID; } } - if (this.$options.bundle !== this._prepareInfo.bundle || this.$options.release !== this._prepareInfo.release) { + if (projectChangesOptions.bundle !== this._prepareInfo.bundle || projectChangesOptions.release !== this._prepareInfo.release) { this._changesInfo.appFilesChanged = true; this._changesInfo.appResourcesChanged = true; this._changesInfo.modulesChanged = true; this._changesInfo.configChanged = true; - this._prepareInfo.release = this.$options.release; - this._prepareInfo.bundle = this.$options.bundle; + this._prepareInfo.release = projectChangesOptions.release; + this._prepareInfo.bundle = projectChangesOptions.bundle; } if (this._changesInfo.packageChanged) { this._changesInfo.modulesChanged = true; @@ -134,7 +133,7 @@ export class ProjectChangesService implements IProjectChangesService { this.$fs.writeJson(prepareInfoFilePath, this._prepareInfo); } - private ensurePrepareInfo(platform: string, projectData: IProjectData): boolean { + private ensurePrepareInfo(platform: string, projectData: IProjectData, projectChangesOptions: IProjectChangesOptions): boolean { this._prepareInfo = this.getPrepareInfo(platform, projectData); if (this._prepareInfo) { let platformData = this.$platformsData.getPlatformData(platform, projectData); @@ -145,8 +144,8 @@ export class ProjectChangesService implements IProjectChangesService { } this._prepareInfo = { time: "", - bundle: this.$options.bundle, - release: this.$options.release, + bundle: projectChangesOptions.bundle, + release: projectChangesOptions.release, changesRequireBuild: true, changesRequireBuildTime: null }; diff --git a/lib/services/project-service.ts b/lib/services/project-service.ts index 5054616043..150430e873 100644 --- a/lib/services/project-service.ts +++ b/lib/services/project-service.ts @@ -73,11 +73,10 @@ export class ProjectService implements IProjectService { public isValidNativeScriptProject(pathToProject?: string): boolean { try { this.$projectData.initializeProjectData(pathToProject); + return !!this.$projectData.projectDir && !!this.$projectData.projectId; } catch (e) { return false; } - - return true; } private getDataFromJson(templatePath: string): any { diff --git a/lib/services/test-execution-service.ts b/lib/services/test-execution-service.ts index 40593cb418..099ec6343c 100644 --- a/lib/services/test-execution-service.ts +++ b/lib/services/test-execution-service.ts @@ -73,9 +73,10 @@ class TestExecutionService implements ITestExecutionService { await this.$usbLiveSyncService.liveSync(platform, projectData); if (this.$options.debugBrk) { + const buildConfig: IBuildConfig = _.merge({ buildForDevice: this.$options.forDevice }, deployOptions); this.$logger.info('Starting debugger...'); let debugService: IDebugService = this.$injector.resolve(`${platform}DebugService`); - await debugService.debugStart(projectData); + await debugService.debugStart(projectData, buildConfig); } resolve(); } catch (err) { @@ -127,20 +128,22 @@ class TestExecutionService implements ITestExecutionService { this.$errors.failWithoutHelp("Verify that listed files are well-formed and try again the operation."); } + 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 + }; + + const buildConfig: IBuildConfig = _.merge({ buildForDevice: this.$options.forDevice }, deployOptions); + if (this.$options.debugBrk) { - await this.getDebugService(platform).debug(projectData); + await this.getDebugService(platform).debug(projectData, buildConfig); } else { - 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, { provision: this.$options.provision, sdk: this.$options.sdk }); await this.$usbLiveSyncService.liveSync(platform, projectData); } diff --git a/test/nativescript-cli-lib.ts b/test/nativescript-cli-lib.ts index fc2162d217..f4fe159e97 100644 --- a/test/nativescript-cli-lib.ts +++ b/test/nativescript-cli-lib.ts @@ -14,9 +14,9 @@ describe("nativescript-cli-lib", () => { const publicApi: any = { deviceEmitter: null, - projectService: ["createProject"], - localBuildService: ["build"] - + projectService: ["createProject", "isValidNativeScriptProject"], + localBuildService: ["build"], + deviceLogProvider: null }; 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 3b5ab60f3d..261cfe9ad0 100644 --- a/test/npm-support.ts +++ b/test/npm-support.ts @@ -187,11 +187,12 @@ async function addDependencies(testInjector: IInjector, projectFolder: string, d } async function preparePlatform(testInjector: IInjector): Promise { - let platformService: IPlatformService = testInjector.resolve("platformService"); - let projectData: IProjectData = testInjector.resolve("projectData"); + const platformService: IPlatformService = testInjector.resolve("platformService"); + const projectData: IProjectData = testInjector.resolve("projectData"); projectData.initializeProjectData(); + const options: IOptions = testInjector.resolve("options"); - await platformService.preparePlatform("android", { bundle: false, release: false }, "", projectData, undefined); + await platformService.preparePlatform("android", { bundle: options.bundle, release: options.release }, "", projectData, { provision: options.provision, sdk: options.sdk }); } describe("Npm support tests", () => { diff --git a/test/platform-commands.ts b/test/platform-commands.ts index af5eec1d26..202f0d8c45 100644 --- a/test/platform-commands.ts +++ b/test/platform-commands.ts @@ -32,6 +32,7 @@ class PlatformData implements IPlatformData { emulatorServices: Mobile.IEmulatorPlatformServices = null; projectRoot = ""; deviceBuildOutputPath = ""; + getValidPackageNames = (buildOptions: {isForDevice?: boolean, isReleaseBuild?: boolean}) => [""]; validPackageNamesForDevice: string[] = []; frameworkFilesExtensions = [".jar", ".dat"]; appDestinationDirectoryPath = ""; diff --git a/test/platform-service.ts b/test/platform-service.ts index cd524e3a3c..7d7216d184 100644 --- a/test/platform-service.ts +++ b/test/platform-service.ts @@ -369,7 +369,7 @@ describe('Platform Service Tests', () => { platformService = testInjector.resolve("platformService"); const appFilesUpdaterOptions: IAppFilesUpdaterOptions = { bundle: false, release: release }; - await platformService.preparePlatform(platformToTest, appFilesUpdaterOptions, "", projectData, undefined); + await platformService.preparePlatform(platformToTest, appFilesUpdaterOptions, "", projectData, { provision: null, sdk: null }); let test1FileName = platformToTest.toLowerCase() === "ios" ? "test1.js" : "test2.js"; let test2FileName = platformToTest.toLowerCase() === "ios" ? "test2.js" : "test1.js"; @@ -446,7 +446,7 @@ 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, undefined); + await platformService.preparePlatform("android", appFilesUpdaterOptions, "", projectData, { provision: null, sdk: null }); } finally { testInjector.resolve("$logger").warn = oldLoggerWarner; } diff --git a/test/project-service.ts b/test/project-service.ts index bb2421bfc6..a4bac957e1 100644 --- a/test/project-service.ts +++ b/test/project-service.ts @@ -5,7 +5,6 @@ import { ChildProcess } from "../lib/common/child-process"; import * as ProjectServiceLib from "../lib/services/project-service"; import { ProjectNameService } from "../lib/services/project-name-service"; import * as ProjectDataServiceLib from "../lib/services/project-data-service"; -import * as ProjectDataLib from "../lib/project-data"; import * as ProjectHelperLib from "../lib/common/project-helper"; import { StaticConfig } from "../lib/config"; import * as NpmLib from "../lib/node-package-manager"; @@ -72,7 +71,7 @@ class ProjectIntegrationTest { let sourceDir = projectSourceDirectory; // Hidden files (starting with dots ".") are not copied. - let expectedFiles = fs.enumerateFilesInDirectorySync(sourceDir, (file, stat) => stat.isDirectory() || !_.startsWith(path.basename(file), ".") ); + let expectedFiles = fs.enumerateFilesInDirectorySync(sourceDir, (file, stat) => stat.isDirectory() || !_.startsWith(path.basename(file), ".")); let actualFiles = fs.enumerateFilesInDirectorySync(appDirectoryPath); assert.isTrue(actualFiles.length >= expectedFiles.length, "Files in created project must be at least as files in app dir."); @@ -459,158 +458,65 @@ describe("Project Service Tests", () => { }); }); -}); - -function createTestInjector() { - let testInjector = new yok.Yok(); - - testInjector.register("errors", stubs.ErrorsStub); - testInjector.register('logger', stubs.LoggerStub); - testInjector.register("projectService", ProjectServiceLib.ProjectService); - testInjector.register("projectHelper", ProjectHelperLib.ProjectHelper); - testInjector.register("projectTemplatesService", stubs.ProjectTemplatesService); - testInjector.register("projectNameValidator", mockProjectNameValidator); - - testInjector.register("fs", FileSystem); - testInjector.register("projectDataService", ProjectDataServiceLib.ProjectDataService); - - testInjector.register("staticConfig", StaticConfig); - testInjector.register("analyticsService", { track: async () => undefined }); - - testInjector.register("npmInstallationManager", NpmInstallationManager); - testInjector.register("httpClient", HttpClientLib.HttpClient); - testInjector.register("lockfile", stubs.LockFile); - - testInjector.register("childProcess", ChildProcess); - - testInjector.register('projectData', ProjectDataLib.ProjectData); - testInjector.register("options", Options); - testInjector.register("hostInfo", HostInfo); - - return testInjector; -} - -describe("project upgrade procedure tests", () => { - it("should throw error when no nativescript project folder specified", () => { - let testInjector = createTestInjector(); - let tempFolder = temp.mkdirSync("project upgrade"); - let options = testInjector.resolve("options"); - options.path = tempFolder; - let isErrorThrown = false; - - try { - 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; - assert.equal(expectedErrorMessage, err.toString()); - } - - assert.isTrue(isErrorThrown); - }); - it("should upgrade project when .tnsproject file exists but package.json file doesn't exist", () => { - let testInjector = createTestInjector(); - let fs: IFileSystem = testInjector.resolve("fs"); - - let tempFolder = temp.mkdirSync("projectUpgradeTest2"); - let options = testInjector.resolve("options"); - options.path = tempFolder; - let tnsProjectData = { - "id": "org.nativescript.Test", - "tns-ios": { - "version": "1.0.0" - }, - "description": "dummy", - "license": "MIT", - "readme": "dummy", - "repository": "dummy" - }; - let tnsProjectFilePath = path.join(tempFolder, ".tnsproject"); - fs.writeJson(tnsProjectFilePath, tnsProjectData); - let projectData: IProjectData = testInjector.resolve("projectData"); - projectData.initializeProjectData(); // This should trigger upgrade procedure - - let packageJsonFilePath = path.join(tempFolder, "package.json"); - let packageJsonFileContent = require(packageJsonFilePath); - assert.isTrue(fs.exists(packageJsonFilePath)); - assert.isFalse(fs.exists(tnsProjectFilePath)); - assert.deepEqual(tnsProjectData, packageJsonFileContent["nativescript"]); - }); - it("should upgrade project when .tnsproject and package.json exist but nativescript key is not presented in package.json file", () => { - let testInjector = createTestInjector(); - let fs: IFileSystem = testInjector.resolve("fs"); - - let tempFolder = temp.mkdirSync("projectUpgradeTest3"); - let options = testInjector.resolve("options"); - options.path = tempFolder; - let tnsProjectData = { - "id": "org.nativescript.Test", - "tns-ios": { - "version": "1.0.1" - } - }; - let packageJsonData = { - "name": "testModuleName", - "version": "0.0.0", - "dependencies": { - "myFirstDep": "0.0.1" - }, - "description": "dummy", - "license": "MIT", - "readme": "dummy", - "repository": "dummy" + describe("isValidNativeScriptProject", () => { + const getTestInjector = (): IInjector => { + const testInjector = new yok.Yok(); + testInjector.register("npm", {}); + testInjector.register("errors", {}); + testInjector.register("fs", {}); + testInjector.register("logger", {}); + testInjector.register("projectDataService", {}); + testInjector.register("projectNameService", {}); + testInjector.register("projectTemplatesService", {}); + testInjector.register("staticConfig", {}); + testInjector.register("projectHelper", {}); + + return testInjector; }; - let tnsProjectFilePath = path.join(tempFolder, ".tnsproject"); - fs.writeJson(tnsProjectFilePath, tnsProjectData); - let packageJsonFilePath = path.join(tempFolder, "package.json"); - fs.writeJson(packageJsonFilePath, packageJsonData); + it("returns true when initialize project data does not throw, projectDir and projectId are valid", () => { + const testInjector = getTestInjector(); + testInjector.register("projectData", { + projectDir: "projectDir", + projectId: "projectId", + initializeProjectData: (): void => undefined + }); - let projectData: IProjectData = testInjector.resolve("projectData"); - projectData.initializeProjectData(); // This should trigger upgrade procedure + const projectService: IProjectService = testInjector.resolve(ProjectServiceLib.ProjectService); + assert.isTrue(projectService.isValidNativeScriptProject("some-dir")); + }); - let packageJsonFileContent = require(packageJsonFilePath); - let expectedPackageJsonContent: any = packageJsonData; - expectedPackageJsonContent["nativescript"] = tnsProjectData; - assert.deepEqual(expectedPackageJsonContent, packageJsonFileContent); - }); - it("shouldn't upgrade project when .tnsproject and package.json exist and nativescript key is presented in package.json file", () => { - let testInjector = createTestInjector(); - let fs: IFileSystem = testInjector.resolve("fs"); + it("returns false when initialize project data throws", () => { + const testInjector = getTestInjector(); + testInjector.register("projectData", { + initializeProjectData: (): void => { throw new Error("err"); } + }); - let tempFolder = temp.mkdirSync("projectUpgradeTest4"); - let options = testInjector.resolve("options"); - options.path = tempFolder; - let tnsProjectData = { + const projectService: IProjectService = testInjector.resolve(ProjectServiceLib.ProjectService); + assert.isFalse(projectService.isValidNativeScriptProject("some-dir")); + }); - }; - let packageJsonData = { - "name": "testModuleName", - "version": "0.0.0", - "dependencies": { - "myFirstDep": "0.0.2" - }, - "nativescript": { - "id": "org.nativescript.Test", - "tns-ios": { - "version": "1.0.2" - } - }, - "description": "dummy", - "license": "MIT", - "readme": "dummy", - "repository": "dummy" - }; + it("returns false when initializeProjectData does not throw, but there's no projectDir set", () => { + const testInjector = getTestInjector(); + testInjector.register("projectData", { + projectId: "projectId", + initializeProjectData: (): void => undefined + }); - fs.writeJson(path.join(tempFolder, ".tnsproject"), tnsProjectData); - fs.writeJson(path.join(tempFolder, "package.json"), packageJsonData); - testInjector.resolve("projectData"); // This should trigger upgrade procedure + const projectService: IProjectService = testInjector.resolve(ProjectServiceLib.ProjectService); + assert.isFalse(projectService.isValidNativeScriptProject("some-dir")); + }); - let packageJsonFilePath = path.join(tempFolder, "package.json"); - let packageJsonFileContent = require(packageJsonFilePath); + it("returns false when initializeProjectData does not throw, but there's no projectId set", () => { + const testInjector = getTestInjector(); + testInjector.register("projectData", { + projectDir: "projectDir", + initializeProjectData: (): void => undefined + }); - assert.deepEqual(packageJsonData, packageJsonFileContent); + const projectService: IProjectService = testInjector.resolve(ProjectServiceLib.ProjectService); + assert.isFalse(projectService.isValidNativeScriptProject("some-dir")); + }); }); }); diff --git a/test/stubs.ts b/test/stubs.ts index 010f78b230..46585c861c 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -255,7 +255,7 @@ export class PlatformsDataStub extends EventEmitter implements IPlatformsData { normalizedPlatformName: "", appDestinationDirectoryPath: "", deviceBuildOutputPath: "", - validPackageNamesForDevice: [], + getValidPackageNames: (buildOptions: {isForDevice?: boolean, isReleaseBuild?: boolean}) => [], frameworkFilesExtensions: [], relativeToFrameworkConfigurationFilePath: "", fastLivesyncFileExtensions: [] @@ -276,7 +276,7 @@ export class PlatformProjectServiceStub extends EventEmitter implements IPlatfor emulatorServices: undefined, projectRoot: "", deviceBuildOutputPath: "", - validPackageNamesForDevice: [], + getValidPackageNames: (buildOptions: {isForDevice?: boolean, isReleaseBuild?: boolean}) => [], frameworkFilesExtensions: [], appDestinationDirectoryPath: "", relativeToFrameworkConfigurationFilePath: "", @@ -352,7 +352,7 @@ export class PlatformProjectServiceStub extends EventEmitter implements IPlatfor async stopServices(): Promise { return Promise.resolve({stderr: "", stdout: "", exitCode: 0}); } - async cleanProject(projectRoot: string, options: string[]): Promise { + async cleanProject(projectRoot: string, projectData: IProjectData): Promise { return Promise.resolve(); } } @@ -664,14 +664,14 @@ export class PlatformServiceStub extends EventEmitter implements IPlatformServic return null; } - public getLatestApplicationPackageForEmulator(platformData: IPlatformData): IApplicationPackage { + public getLatestApplicationPackageForEmulator(platformData: IPlatformData, buildConfig: IBuildConfig): IApplicationPackage { return null; } - public copyLastOutput(platform: string, targetPath: string, settings: { isForDevice: boolean }): void { + public copyLastOutput(platform: string, targetPath: string, buildConfig: IBuildConfig): void { } - public lastOutputPath(platform: string, settings: { isForDevice: boolean }): string { + public lastOutputPath(platform: string, buildConfig: IBuildConfig): string { return ""; }