From 449f72a9b61c4644aac8015f47c493627e9322f4 Mon Sep 17 00:00:00 2001 From: Fatme Havaluova Date: Fri, 25 Jul 2014 11:38:26 +0300 Subject: [PATCH 1/2] Create, prepare and build commands for iOS --- .idea/nativescript-cli.iml | 1 + lib/declarations.ts | 4 +- lib/definitions/project.d.ts | 7 +- lib/definitions/semver.d.ts | 4 + lib/nativescript-cli.ts | 6 +- lib/node-package-manager.ts | 49 +++++++---- lib/options.ts | 1 + lib/services/android-project-service.ts | 70 +++++++--------- lib/services/ios-project-service.ts | 104 +++++++++++++++++++++-- lib/services/platform-service.ts | 64 +++++++++------ lib/services/project-service.ts | 2 +- package.json | 105 ++++++++++++------------ 12 files changed, 271 insertions(+), 146 deletions(-) create mode 100644 lib/definitions/semver.d.ts diff --git a/.idea/nativescript-cli.iml b/.idea/nativescript-cli.iml index 6b8184f8ef..6f39422452 100644 --- a/.idea/nativescript-cli.iml +++ b/.idea/nativescript-cli.iml @@ -4,6 +4,7 @@ + diff --git a/lib/declarations.ts b/lib/declarations.ts index 1d17b3ea3d..2b4bc9b0fe 100644 --- a/lib/declarations.ts +++ b/lib/declarations.ts @@ -1,7 +1,7 @@ interface INodePackageManager { cache: string; load(config?: any): IFuture; - install(packageName: string, pathToSave?: string): IFuture; + install(packageName: string, pathToSave?: string, version?: string): IFuture; } -interface IStaticConfig extends Config.IStaticConfig { } \ No newline at end of file +interface IStaticConfig extends Config.IStaticConfig { } diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index bebfed8352..9a3f890ac1 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -15,10 +15,11 @@ interface IProjectTemplatesService { } interface IPlatformProjectService { + platformData: IPlatformData; validate(): IFuture; createProject(projectRoot: string, frameworkDir: string): IFuture; - interpolateData(projectRoot: string): void; - afterCreateProject(projectRoot: string): void; - prepareProject(normalizedPlatformName: string, platforms: string[]): IFuture; + interpolateData(projectRoot: string): IFuture; + afterCreateProject(projectRoot: string): IFuture; + prepareProject(platformData: IPlatformData): IFuture; buildProject(projectRoot: string): IFuture; } \ No newline at end of file diff --git a/lib/definitions/semver.d.ts b/lib/definitions/semver.d.ts new file mode 100644 index 0000000000..95fa0ed93e --- /dev/null +++ b/lib/definitions/semver.d.ts @@ -0,0 +1,4 @@ +declare module "semver" { + function gt(version1: string, version2: string): boolean; + function lt(version1: string, version2: string): boolean; +} \ No newline at end of file diff --git a/lib/nativescript-cli.ts b/lib/nativescript-cli.ts index f314055c4b..a96b93a7d6 100644 --- a/lib/nativescript-cli.ts +++ b/lib/nativescript-cli.ts @@ -12,7 +12,11 @@ errors.installUncaughtExceptionListener(); $injector.register("config", { CI_LOGGER: false, - DEBUG: process.env.NATIVESCRIPT_DEBUG + PROJECT_FILE_NAME: ".tnsproject", + DEBUG: process.env.NATIVESCRIPT_DEBUG, + version: require("../package.json").version, + helpTextPath: path.join(__dirname, "../resources/help.txt"), + client: "tns" }); var dispatcher = $injector.resolve("dispatcher"); diff --git a/lib/node-package-manager.ts b/lib/node-package-manager.ts index a13132516e..6700730b3e 100644 --- a/lib/node-package-manager.ts +++ b/lib/node-package-manager.ts @@ -1,15 +1,18 @@ /// -import npm = require("npm"); import Future = require("fibers/future"); -import shell = require("shelljs"); +import npm = require("npm"); import path = require("path"); +import shell = require("shelljs"); +import helpers = require("./common/helpers"); export class NodePackageManager implements INodePackageManager { private static NPM_LOAD_FAILED = "Failed to retrieve data from npm. Please try again a little bit later."; + private static NPM_REGISTRY_URL = "http://registry.npmjs.org/"; constructor(private $logger: ILogger, - private $errors: IErrors) { } + private $errors: IErrors, + private $httpClient: Server.IHttpClient) { } public get cache(): string { return npm.cache; @@ -27,14 +30,24 @@ export class NodePackageManager implements INodePackageManager { return future; } - public install(packageName: string, pathToSave?: string): IFuture { + public install(packageName: string, pathToSave?: string, version?: string): IFuture { return (() => { - var action = (packageName: string) => { + try { + this.load().wait(); // It's obligatory to execute load before whatever npm function pathToSave = pathToSave || npm.cache; - this.installCore(pathToSave, packageName).wait(); - }; + var packageToInstall = packageName; + + if(version) { + this.validateVersion(packageName, version).wait(); + packageToInstall = packageName + "@" + version; + } + + this.installCore(pathToSave, packageToInstall).wait(); - this.tryExecuteAction(action, packageName).wait(); + } catch(error) { + this.$logger.debug(error); + this.$errors.fail(NodePackageManager.NPM_LOAD_FAILED); + } return path.join(pathToSave, "node_modules", packageName); @@ -53,14 +66,20 @@ export class NodePackageManager implements INodePackageManager { return future; } - private tryExecuteAction(action: (...args: any[]) => void, ...args: any[]): IFuture { + private getAvailableVersions(packageName: string): IFuture { return (() => { - try { - this.load().wait(); // It's obligatory to execute load before whatever npm function - action.apply(null, args); - } catch(error) { - this.$logger.debug(error); - this.$errors.fail(NodePackageManager.NPM_LOAD_FAILED); + var url = NodePackageManager.NPM_REGISTRY_URL + packageName; + var response = this.$httpClient.httpRequest(url).wait().body; + var json = JSON.parse(response); + return _.keys(json.versions); + }).future()(); + } + + private validateVersion(packageName: string, version: string): IFuture { + return (() => { + var versions = this.getAvailableVersions(packageName).wait(); + if(!_.contains(versions, version)) { + this.$errors.fail("Invalid version. Valid versions are: %s", helpers.formatListOfNames(versions, "and")); } }).future()(); } diff --git a/lib/options.ts b/lib/options.ts index 77dfaff124..d533b4fcde 100644 --- a/lib/options.ts +++ b/lib/options.ts @@ -12,6 +12,7 @@ var knownOpts:any = { "copy-from": String, "link-to": String, "release": String, + "device": Boolean, "version": Boolean, "help": Boolean }, diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 070d6760b1..5c905ec15a 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -16,6 +16,15 @@ class AndroidProjectService implements IPlatformProjectService { private $projectData: IProjectData, private $propertiesParser: IPropertiesParser) { } + public get platformData(): IPlatformData { + return { + frameworkPackageName: "tns-android", + normalizedPlatformName: "Android", + platformProjectService: this, + projectRoot: path.join(this.$projectData.platformsDir, "android") + }; + } + public validate(): IFuture { return (() => { this.validatePackageName(this.$projectData.projectId); @@ -61,54 +70,49 @@ class AndroidProjectService implements IPlatformProjectService { }).future()(); } - public afterCreateProject(projectRoot: string) { - var targetApi = this.getTarget(projectRoot).wait(); - this.$logger.trace("Android target: %s", targetApi); - this.runAndroidUpdate(projectRoot, targetApi).wait(); + public afterCreateProject(projectRoot: string): IFuture { + return (() => { + var targetApi = this.getTarget(projectRoot).wait(); + this.$logger.trace("Android target: %s", targetApi); + this.runAndroidUpdate(projectRoot, targetApi).wait(); + }).future()(); } - public prepareProject(normalizedPlatformName: string, platforms: string[]): IFuture { + public prepareProject(platformData: IPlatformData): IFuture { return (() => { - var platform = normalizedPlatformName.toLowerCase(); - var assetsDirectoryPath = path.join(this.$projectData.platformsDir, platform, "assets"); - var appResourcesDirectoryPath = path.join(assetsDirectoryPath, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); - shell.cp("-r", path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME), assetsDirectoryPath); + var appSourceDirectory = path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME); + var assetsDirectory = path.join(platformData.projectRoot, "assets"); + var resDirectory = path.join(platformData.projectRoot, "res"); + + shell.cp("-r", appSourceDirectory, assetsDirectory); + var appResourcesDirectoryPath = path.join(assetsDirectory, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); if (this.$fs.exists(appResourcesDirectoryPath).wait()) { - shell.cp("-r", path.join(appResourcesDirectoryPath, normalizedPlatformName, "*"), path.join(this.$projectData.platformsDir, platform, "res")); + shell.cp("-r", path.join(appResourcesDirectoryPath, platformData.normalizedPlatformName, "*"), resDirectory); this.$fs.deleteDirectory(appResourcesDirectoryPath).wait(); } - var files = helpers.enumerateFilesInDirectorySync(path.join(assetsDirectoryPath, constants.APP_FOLDER_NAME)); - var platformsAsString = platforms.join("|"); + return path.join(assetsDirectory, constants.APP_FOLDER_NAME); - _.each(files, fileName => { - var platformInfo = AndroidProjectService.parsePlatformSpecificFileName(path.basename(fileName), platformsAsString); - var shouldExcludeFile = platformInfo && platformInfo.platform !== platform; - if (shouldExcludeFile) { - this.$fs.deleteFile(fileName).wait(); - } else if (platformInfo && platformInfo.onDeviceName) { - this.$fs.rename(fileName, path.join(path.dirname(fileName), platformInfo.onDeviceName)).wait(); - } - }); - }).future()(); + }).future()(); } public buildProject(projectRoot: string): IFuture { return (() => { var buildConfiguration = options.release ? "release" : "debug"; var args = this.getAntArgs(buildConfiguration, projectRoot); - this.spawn('ant', args); + this.spawn('ant', args).wait(); }).future()(); } - private spawn(command: string, args: string[], options?: any): void { - if(helpers.isWindows()) { + private spawn(command: string, args: string[]): IFuture { + if (helpers.isWindows()) { args.unshift('/s', '/c', command); command = 'cmd'; } - this.$childProcess.spawn(command, args, {cwd: options, stdio: 'inherit'}); + var child = this.$childProcess.spawn(command, args, {stdio: "inherit"}); + return this.$fs.futureFromEvent(child, "close"); } private getAntArgs(configuration: string, projectRoot: string): string[] { @@ -123,7 +127,7 @@ class AndroidProjectService implements IPlatformProjectService { "--target", targetApi ]; - this.spawn("android update project", args); + this.spawn("android", ['update', 'project'].concat(args)).wait(); }).future()(); } @@ -208,17 +212,5 @@ class AndroidProjectService implements IPlatformProjectService { } }).future()(); } - - private static parsePlatformSpecificFileName(fileName: string, platforms: string): any { - var regex = util.format("^(.+?)\.(%s)(\..+?)$", platforms); - var parsed = fileName.toLowerCase().match(new RegExp(regex, "i")); - if (parsed) { - return { - platform: parsed[2], - onDeviceName: parsed[1] + parsed[3] - }; - } - return undefined; - } } $injector.register("androidProjectService", AndroidProjectService); \ No newline at end of file diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index 6aacd045b7..a9712bebc2 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -1,34 +1,126 @@ /// +import path = require("path"); +import shell = require("shelljs"); +import constants = require("./../constants"); +import helpers = require("./../common/helpers"); +import options = require("./../options"); + class IOSProjectService implements IPlatformProjectService { + private static XCODE_PROJECT_EXT_NAME = ".xcodeproj"; + private static XCODEBUILD_MIN_VERSION = "5.0"; + private static IOS_PROJECT_NAME_PLACEHOLDER = "__PROJECT_NAME__"; + + constructor(private $projectData: IProjectData, + private $fs: IFileSystem, + private $childProcess: IChildProcess, + private $errors: IErrors) { } + + public get platformData(): IPlatformData { + return { + frameworkPackageName: "tns-ios", + normalizedPlatformName: "iOS", + platformProjectService: this, + projectRoot: path.join(this.$projectData.platformsDir, "ios"), + targetedOS: ['darwin'] + }; + } + public validate(): IFuture { return (() => { + try { + this.$childProcess.exec("which xcodebuild").wait(); + } catch(error) { + this.$errors.fail("Xcode is not installed. Make sure you have Xcode installed and added to your PATH"); + } + + var xcodeBuildVersion = this.$childProcess.exec("xcodebuild -version | head -n 1 | sed -e 's/Xcode //'").wait(); + if(helpers.versionCompare(xcodeBuildVersion, IOSProjectService.XCODEBUILD_MIN_VERSION) < 0) { + this.$errors.fail("NativeScript can only run in Xcode version %s or greater", IOSProjectService.XCODEBUILD_MIN_VERSION); + } + + }).future()(); + } + + public createProject(projectRoot: string, frameworkDir: string): IFuture { + return (() => { + shell.cp("-r", path.join(frameworkDir, "*"), projectRoot); }).future()(); } - public interpolateData(): void { + public interpolateData(projectRoot: string): IFuture { + return (() => { + this.replaceFileName("-Info.plist", path.join(projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER)).wait(); + this.replaceFileName("-Prefix.pch", path.join(projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER)).wait(); + this.replaceFileName(IOSProjectService.XCODE_PROJECT_EXT_NAME, projectRoot).wait(); + + var pbxprojFilePath = path.join(projectRoot, this.$projectData.projectName + IOSProjectService.XCODE_PROJECT_EXT_NAME, "project.pbxproj"); + this.replaceFileContent(pbxprojFilePath).wait(); + }).future()(); + } + public afterCreateProject(projectRoot: string): IFuture { + return (() => { + this.$fs.rename(path.join(projectRoot, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER), + path.join(projectRoot, this.$projectData.projectName)).wait(); + }).future()(); } - public afterCreateProject(): void { + public prepareProject(platformData: IPlatformData): IFuture { + return (() => { + var appSourceDirectory = path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME); + var appDestinationDirectory = path.join(platformData.projectRoot, this.$projectData.projectName); + shell.cp("-r", appSourceDirectory, appDestinationDirectory); + return path.join(appDestinationDirectory, constants.APP_FOLDER_NAME); + }).future()(); } - public createProject(): IFuture { + public buildProject(projectRoot: string): IFuture { return (() => { + var basicArgs = [ + "-project", path.join(projectRoot, this.$projectData.projectName + ".xcodeproj"), + "-target", this.$projectData.projectName, + "-configuration", options.release, + "build" + ]; + var args = []; + if(options.device) { + args = basicArgs.concat([ + "-xcconfig", path.join(projectRoot, "build.xcconfig"), + "-sdk", "iphoneos", + "ARCHS=\"armv7 armv7s arm64\"", + "VALID_ARCHS=\"armv7 armv7s arm64\"", + "CONFIGURATION_BUILD_DIR=" + path.join(projectRoot, "build", "device") + ]); + } else { + args = basicArgs.concat([ + "-sdk", "iphonesimulator", + "-arch", "i386", + "VALID_ARCHS=\"i386\"", + "CONFIGURATION_BUILD_DIR=" + path.join(projectRoot, "build", "emulator") + ]); + } + + this.$childProcess.spawn("xcodebuild", args, {cwd: options, stdio: 'inherit'}); }).future()(); } - public prepareProject(normalizedPlatformName: string, platforms: string[]): IFuture { + private replaceFileContent(file: string): IFuture { return (() => { - + var fileContent = this.$fs.readText(file).wait(); + var replacedContent = helpers.stringReplaceAll(fileContent, IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER, this.$projectData.projectName); + this.$fs.writeFile(file, replacedContent).wait(); }).future()(); } - public buildProject(): IFuture { + private replaceFileName(fileNamePart: string, fileRootLocation: string): IFuture { return (() => { + var oldFileName = IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER + fileNamePart; + var newFileName = this.$projectData.projectName + fileNamePart; + this.$fs.rename(path.join(fileRootLocation, oldFileName), path.join(fileRootLocation, newFileName)).wait(); }).future()(); } } diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 82ecc46d62..df9464b77b 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -9,24 +9,12 @@ import helpers = require("./../common/helpers"); class PlatformsData implements IPlatformsData { private platformsData = {}; - constructor($projectData: IProjectData, - $androidProjectService: IPlatformProjectService, + constructor($androidProjectService: IPlatformProjectService, $iOSProjectService: IPlatformProjectService) { this.platformsData = { - ios: { - frameworkPackageName: "tns-ios", - normalizedPlatformName: "iOS", - platformProjectService: $iOSProjectService, - projectRoot: "", - targetedOS: ['darwin'] - }, - android: { - frameworkPackageName: "tns-android", - normalizedPlatformName: "Android", - platformProjectService: $androidProjectService, - projectRoot: path.join($projectData.platformsDir, "android") - } + ios: $iOSProjectService.platformData, + android: $androidProjectService.platformData } } @@ -45,10 +33,8 @@ export class PlatformService implements IPlatformService { private $fs: IFileSystem, private $logger: ILogger, private $npm: INodePackageManager, - private $projectService: IProjectService, private $projectData: IProjectData, - private $platformsData: IPlatformsData) { - } + private $platformsData: IPlatformsData) { } public addPlatforms(platforms: string[]): IFuture { return (() => { @@ -68,22 +54,21 @@ export class PlatformService implements IPlatformService { private addPlatform(platform: string): IFuture { return(() => { - platform = platform.split("@")[0]; + var parts = platform.split("@"); + platform = parts[0]; + var version = parts[1]; this.validatePlatform(platform); var platformPath = path.join(this.$projectData.platformsDir, platform); - - // TODO: Check for version compatability if the platform is in format platform@version. This should be done in PR for semanting versioning - if (this.$fs.exists(platformPath).wait()) { this.$errors.fail("Platform %s already added", platform); } - // Copy platform specific files in platforms dir var platformData = this.$platformsData.getPlatformData(platform); - var platformProjectService = platformData.platformProjectService; + // Copy platform specific files in platforms dir + var platformProjectService = platformData.platformProjectService; platformProjectService.validate().wait(); // Log the values for project @@ -95,8 +80,8 @@ export class PlatformService implements IPlatformService { this.$logger.out("Copying template files..."); // get path to downloaded framework package - var frameworkDir = this.$npm.install(this.$platformsData.getPlatformData(platform).frameworkPackageName, - path.join(this.$projectData.platformsDir, platform)).wait(); + var frameworkDir = this.$npm.install(platformData.frameworkPackageName, + path.join(this.$projectData.platformsDir, platform), version).wait(); frameworkDir = path.join(frameworkDir, constants.PROJECT_FRAMEWORK_FOLDER_NAME); try { @@ -116,6 +101,7 @@ export class PlatformService implements IPlatformService { platformData.platformProjectService.createProject(platformData.projectRoot, frameworkDir).wait(); // Need to remove unneeded node_modules folder + // One level up is the runtime module and one above is the node_modules folder. this.$fs.deleteDirectory(path.join("../", frameworkDir)).wait(); platformData.platformProjectService.interpolateData(platformData.projectRoot); @@ -152,7 +138,19 @@ export class PlatformService implements IPlatformService { var platformData = this.$platformsData.getPlatformData(platform); var platformProjectService = platformData.platformProjectService; - platformProjectService.prepareProject(platformData.normalizedPlatformName, this.$platformsData.platformsNames).wait(); + var appFilesLocation = platformProjectService.prepareProject(platformData).wait(); + var files = helpers.enumerateFilesInDirectorySync(appFilesLocation); + + _.each(files, fileName => { + var platformInfo = PlatformService.parsePlatformSpecificFileName(path.basename(fileName), this.$platformsData.platformsNames); + var shouldExcludeFile = platformInfo && platformInfo.platform !== platform; + if (shouldExcludeFile) { + this.$fs.deleteFile(fileName).wait(); + } else if (platformInfo && platformInfo.onDeviceName) { + this.$fs.rename(fileName, path.join(path.dirname(fileName), platformInfo.onDeviceName)).wait(); + } + }); + }).future()(); } @@ -231,5 +229,17 @@ export class PlatformService implements IPlatformService { return this.$fs.exists(path.join(this.$projectData.platformsDir, platform)).wait(); }).future()(); } + + private static parsePlatformSpecificFileName(fileName: string, platforms: string[]): any { + var regex = util.format("^(.+?)\.(%s)(\..+?)$", platforms.join("|")); + var parsed = fileName.toLowerCase().match(new RegExp(regex, "i")); + if (parsed) { + return { + platform: parsed[2], + onDeviceName: parsed[1] + parsed[3] + }; + } + return undefined; + } } $injector.register("platformService", PlatformService); diff --git a/lib/services/project-service.ts b/lib/services/project-service.ts index 5af2b55c12..5ed95e61a8 100644 --- a/lib/services/project-service.ts +++ b/lib/services/project-service.ts @@ -93,7 +93,7 @@ export class ProjectService implements IProjectService { // No custom app - use nativescript hello world application this.$logger.trace("Using NativeScript hello world application"); var defaultTemplatePath = this.$projectTemplatesService.defaultTemplatePath.wait(); - this.$logger.trace("Copying Nativescript hello world application into %s", appDirectory); + this.$logger.trace("Copying NativeScript hello world application into %s", appDirectory); appPath = defaultTemplatePath; } diff --git a/package.json b/package.json index bdc41a8f67..56b693ce5d 100644 --- a/package.json +++ b/package.json @@ -1,54 +1,55 @@ { - "name": "nativescript", - "preferGlobal": true, - "version": "0.1.0", - "author": "Telerik ", - "description": "Command-line interface for building NativeScript projects", - "bin": { - "tns": "./bin/nativescript.js", - "nativescript": "./bin/nativescript.js" - }, - "main": "./lib/nativescript-cli.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "https://github.com/NativeScript/nativescript-cli.git" - }, - "keywords": [ - "nativescript", - "telerik", - "mobile" - ], - "dependencies": { - "fibers": "https://github.com/icenium/node-fibers/tarball/master", - "filesize": "2.0.3", - "progress-stream": "0.5.0", - "log4js": "0.6.9", - "osenv": "0.1.0", - "tabtab": "https://github.com/tailsu/node-tabtab/tarball/master", - "underscore": "1.5.2", - "unzip": "0.1.9", - "yargs": "1.2.2", - "npm": "1.4.10", - "properties-parser": "0.2.3", - "watchr": "2.4.11", - "rimraf": "2.2.6", - "mkdirp": "0.3.5", - "shelljs": "0.3.0" - }, - "analyze": true, - "devDependencies": { - "grunt": "0.4.2", - "grunt-ts": "1.11.2", - "grunt-contrib-clean": "0.5.0", - "grunt-contrib-watch": "0.5.3", - "grunt-shell": "0.6.4", - "grunt-contrib-copy": "0.5.0" - }, - "license": "Apache-2.0", - "engines": { - "node": "~0.10.22" - } + "name": "nativescript", + "preferGlobal": true, + "version": "0.1.0", + "author": "Telerik ", + "description": "Command-line interface for building NativeScript projects", + "bin": { + "tns": "./bin/nativescript.js", + "nativescript": "./bin/nativescript.js" + }, + "main": "./lib/nativescript-cli.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/NativeScript/nativescript-cli.git" + }, + "keywords": [ + "nativescript", + "telerik", + "mobile" + ], + "dependencies": { + "fibers": "https://github.com/icenium/node-fibers/tarball/master", + "filesize": "2.0.3", + "progress-stream": "0.5.0", + "log4js": "0.6.9", + "osenv": "0.1.0", + "tabtab": "https://github.com/tailsu/node-tabtab/tarball/master", + "underscore": "1.5.2", + "unzip": "0.1.9", + "yargs": "1.2.2", + "npm": "1.4.10", + "properties-parser": "0.2.3", + "watchr": "2.4.11", + "rimraf": "2.2.6", + "mkdirp": "0.3.5", + "shelljs": "0.3.0", + "semver": "3.0.1" + }, + "analyze": true, + "devDependencies": { + "grunt": "0.4.2", + "grunt-ts": "1.11.2", + "grunt-contrib-clean": "0.5.0", + "grunt-contrib-watch": "0.5.3", + "grunt-shell": "0.6.4", + "grunt-contrib-copy": "0.5.0" + }, + "license": "Apache-2.0", + "engines": { + "node": "~0.10.22" + } } From d534ce07eb857eb8ffc87f45b7d2b569fab1f1fe Mon Sep 17 00:00:00 2001 From: Fatme Havaluova Date: Fri, 25 Jul 2014 11:38:26 +0300 Subject: [PATCH 2/2] Semantic versioning --- lib/constants.ts | 11 +++++++++++ lib/declarations.ts | 1 + lib/definitions/semver.d.ts | 12 +++++++++++- lib/node-package-manager.ts | 21 ++++++++++++++++----- 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/lib/constants.ts b/lib/constants.ts index 804e955644..032f6986d9 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -6,3 +6,14 @@ export var DEFAULT_PROJECT_NAME = "HelloNativescript"; export var APP_RESOURCES_FOLDER_NAME = "App_Resources"; export var PROJECT_FRAMEWORK_FOLDER_NAME = "framework"; +export class ReleaseType { + static MAJOR = "major"; + static PREMAJOR = "premajor"; + static MINOR = "minor"; + static PREMINOR = "preminor"; + static PATCH = "patch"; + static PREPATCH = "prepatch"; + static PRERELEASE = "prerelease"; +} + + diff --git a/lib/declarations.ts b/lib/declarations.ts index 2b4bc9b0fe..0f810c9316 100644 --- a/lib/declarations.ts +++ b/lib/declarations.ts @@ -5,3 +5,4 @@ interface INodePackageManager { } interface IStaticConfig extends Config.IStaticConfig { } + diff --git a/lib/definitions/semver.d.ts b/lib/definitions/semver.d.ts index 95fa0ed93e..ae84b5cbdf 100644 --- a/lib/definitions/semver.d.ts +++ b/lib/definitions/semver.d.ts @@ -1,4 +1,14 @@ + declare module "semver" { function gt(version1: string, version2: string): boolean; function lt(version1: string, version2: string): boolean; -} \ No newline at end of file + function valid(version: string): boolean; + function inc(version: string, release: string): string; + function inc(version: string, release: "major"): string; + function inc(version: string, release: 'premajor'): string; + function inc(version: string, release: 'minor'): string; + function inc(version: string, release: 'preminor'): string; + function inc(version: string, release: 'patch'): string; + function inc(version: string, release: 'prepatch'): string; + function inc(version: string, release: 'prerelease'): string; +} diff --git a/lib/node-package-manager.ts b/lib/node-package-manager.ts index 6700730b3e..21a8d2623e 100644 --- a/lib/node-package-manager.ts +++ b/lib/node-package-manager.ts @@ -3,8 +3,10 @@ import Future = require("fibers/future"); import npm = require("npm"); import path = require("path"); +import semver = require("semver"); import shell = require("shelljs"); import helpers = require("./common/helpers"); +import constants = require("./constants"); export class NodePackageManager implements INodePackageManager { private static NPM_LOAD_FAILED = "Failed to retrieve data from npm. Please try again a little bit later."; @@ -12,7 +14,8 @@ export class NodePackageManager implements INodePackageManager { constructor(private $logger: ILogger, private $errors: IErrors, - private $httpClient: Server.IHttpClient) { } + private $httpClient: Server.IHttpClient, + private $staticConfig: IStaticConfig) { } public get cache(): string { return npm.cache; @@ -43,7 +46,6 @@ export class NodePackageManager implements INodePackageManager { } this.installCore(pathToSave, packageToInstall).wait(); - } catch(error) { this.$logger.debug(error); this.$errors.fail(NodePackageManager.NPM_LOAD_FAILED); @@ -54,9 +56,18 @@ export class NodePackageManager implements INodePackageManager { }).future()(); } - private installCore(where: string, what: string): IFuture { - var future = new Future(); - npm.commands["install"](where, what, (err, data) => { + private installCore(packageName: string, pathToSave: string): IFuture { + var currentVersion = this.$staticConfig.version; + if(!semver.valid(currentVersion)) { + this.$errors.fail("Invalid version."); + } + + var incrementedVersion = semver.inc(currentVersion, constants.ReleaseType.MINOR); + packageName = packageName + "@" + "<" + incrementedVersion; + this.$logger.trace("Installing", packageName); + + var future = new Future(); + npm.commands["install"](pathToSave, packageName, (err, data) => { if(err) { future.throw(err); } else {