From 28ddc2f56ec67c5152a4a1b64ee3e94d8a571aa3 Mon Sep 17 00:00:00 2001 From: Fatme Havaluova Date: Tue, 23 Jun 2015 18:13:03 +0300 Subject: [PATCH 01/17] Implement install command https://github.com/NativeScript/nativescript-cli/issues/587 --- bin/nativescript.js | 0 lib/bootstrap.ts | 3 +- lib/commands/install.ts | 84 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 1 deletion(-) mode change 100644 => 100755 bin/nativescript.js create mode 100644 lib/commands/install.ts diff --git a/bin/nativescript.js b/bin/nativescript.js old mode 100644 new mode 100755 diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 7047dfe7d2..00e03f8d5e 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -58,4 +58,5 @@ $injector.require("broccoliPluginWrapper", "./tools/broccoli/broccoli-plugin-wra $injector.require("pluginsService", "./services/plugins-service"); $injector.requireCommand("plugin|add", "./commands/plugin/add-plugin"); -$injector.requireCommand("plugin|remove", "./commands/plugin/remove-plugin"); \ No newline at end of file +$injector.requireCommand("plugin|remove", "./commands/plugin/remove-plugin"); +$injector.requireCommand("install", "./commands/install"); \ No newline at end of file diff --git a/lib/commands/install.ts b/lib/commands/install.ts new file mode 100644 index 0000000000..6a718077be --- /dev/null +++ b/lib/commands/install.ts @@ -0,0 +1,84 @@ +/// +"use strict"; + +import path = require("path"); + +export class InstallCommand implements ICommand { + private _projectData: any; + + constructor(private $fs: IFileSystem, + private $errors: IErrors, + private $logger: ILogger, + private $options: IOptions, + private $injector: IInjector, + private $staticConfig: IStaticConfig) { } + + public enableHooks = false; + + public allowedParameters: ICommandParameter[] = []; + + public execute(args: string[]): IFuture { + return (() => { + let projectFilePath = this.getProjectFilePath(args[0]); + let projectData = this.getProjectData(projectFilePath).wait(); + let projectName = projectData.id.split(".")[2]; + + this.$injector.resolve("projectService").createProject(projectName).wait(); + + this.$options.path = path.join(this.$options.path || path.resolve("."), projectName); + + this.$logger.info("Adding platforms..."); + + let $platformsData = this.$injector.resolve("platformsData"); + let $platformService = this.$injector.resolve("platformService"); + _.each($platformsData.platformsNames, platform => { + let platformData = $platformsData.getPlatformData(platform); + let frameworkPackageData = projectData[platformData.frameworkPackageName]; + if(frameworkPackageData && frameworkPackageData.version) { + $platformService.addPlatforms([`${platform}@${frameworkPackageData.version}`]).wait(); + } + }); + + }).future()(); + } + + public canExecute(args: string[]): IFuture { + return (() => { + let projectFilePath = this.getProjectFilePath(args[0]); + let errorMessage = args[0] ? "The provided path doesn't contain package.json." : + "The current directory doesn't contain package.json file. Execute the command in directory which contains package.json file or specify the path to package.json file."; + + if(!this.$fs.exists(projectFilePath).wait()) { + this.$errors.failWithoutHelp(errorMessage); + } + + let projectData = this.getProjectData(projectFilePath).wait(); + if(!projectData) { + this.$errors.failWithoutHelp("Invalid project file. Verify that the specified package.json file contains a nativescript key and try again."); + } + + if(!projectData.id) { + this.$errors.failWithoutHelp("Invalid project file. Verify that the specified package.json file contains an id and try again."); + } + + return true; + }).future()(); + } + + private getProjectFilePath(providedPath: string): string { + let resolvedPath = path.resolve(providedPath || "."); + return path.basename(resolvedPath) === "package.json" ? resolvedPath : path.join(resolvedPath, "package.json"); + } + + private getProjectData(projectFilePath: string): IFuture { + return (() => { + if(!this._projectData) { + let fileContent = this.$fs.readJson(projectFilePath).wait(); + this._projectData = fileContent[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE]; + } + + return this._projectData; + }).future()(); + } +} +$injector.registerCommand("install", InstallCommand); \ No newline at end of file From 3536e66cd303634019c67e76861c752b76cf7570 Mon Sep 17 00:00:00 2001 From: Fatme Havaluova Date: Mon, 22 Jun 2015 19:56:18 +0300 Subject: [PATCH 02/17] Handle platform specific files in plugin --- lib/bootstrap.ts | 4 +- lib/declarations.ts | 4 ++ lib/services/platform-service.ts | 46 ++------------- lib/services/plugins-service.ts | 6 +- lib/services/project-files-manager.ts | 56 ++++++++++++++++++ test/npm-support.ts | 3 + test/platform-commands.ts | 2 + test/platform-service.ts | 6 +- test/plugins-service.ts | 2 + test/project-files-manager.ts | 81 +++++++++++++++++++++++++++ 10 files changed, 164 insertions(+), 46 deletions(-) create mode 100644 lib/services/project-files-manager.ts create mode 100644 test/project-files-manager.ts diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 00e03f8d5e..81657d844f 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -59,4 +59,6 @@ $injector.require("broccoliPluginWrapper", "./tools/broccoli/broccoli-plugin-wra $injector.require("pluginsService", "./services/plugins-service"); $injector.requireCommand("plugin|add", "./commands/plugin/add-plugin"); $injector.requireCommand("plugin|remove", "./commands/plugin/remove-plugin"); -$injector.requireCommand("install", "./commands/install"); \ No newline at end of file +$injector.requireCommand("install", "./commands/install"); + +$injector.require("projectFilesManager", "./services/project-files-manager"); diff --git a/lib/declarations.ts b/lib/declarations.ts index 031322e9c5..5f7abb2e7d 100644 --- a/lib/declarations.ts +++ b/lib/declarations.ts @@ -64,3 +64,7 @@ interface IOptions extends ICommonOptions { keyStoreAliasPassword: string; sdk: string; } + +interface IProjectFilesManager { + processPlatformSpecificFiles(directoryPath: string, platform: string, excludedDirs?: string[]): IFuture; +} \ No newline at end of file diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index bde1e242ce..192d539482 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -23,7 +23,8 @@ export class PlatformService implements IPlatformService { private $commandsService: ICommandsService, private $options: IOptions, private $broccoliBuilder: IBroccoliBuilder, - private $pluginsService: IPluginsService) { } + private $pluginsService: IPluginsService, + private $projectFilesManager: IProjectFilesManager) { } public addPlatforms(platforms: string[]): IFuture { return (() => { @@ -165,19 +166,9 @@ export class PlatformService implements IPlatformService { } // Process platform specific files - var contents = this.$fs.readDirectory(path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME)).wait(); - var files: string[] = []; - - _.each(contents, d => { - let filePath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, d); - let fsStat = this.$fs.getFsStats(filePath).wait(); - if(fsStat.isDirectory() && d !== constants.APP_RESOURCES_FOLDER_NAME) { - this.processPlatformSpecificFiles(platform, this.$fs.enumerateFilesInDirectorySync(filePath)).wait(); - } else if(fsStat.isFile()) { - files.push(filePath); - } - }); - this.processPlatformSpecificFiles(platform, files).wait(); + let directoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + let excludedDirs = [constants.APP_RESOURCES_FOLDER_NAME]; + this.$projectFilesManager.processPlatformSpecificFiles(directoryPath, platform, excludedDirs).wait(); // Process node_modules folder this.$pluginsService.ensureAllDependenciesAreInstalled().wait(); @@ -341,33 +332,6 @@ export class PlatformService implements IPlatformService { return platformData.platformProjectService.isPlatformPrepared(platformData.projectRoot); } - 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; - } - - private processPlatformSpecificFiles( platform: string, files: string[]): IFuture { - // Renames the files that have `platform` as substring and removes the files from other platform - return (() => { - _.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()(); - } - private getApplicationPackages(buildOutputPath: string, validPackageNames: string[]): IFuture { return (() => { // Get latest package that is produced from build diff --git a/lib/services/plugins-service.ts b/lib/services/plugins-service.ts index de68eebb41..b5e5b10559 100644 --- a/lib/services/plugins-service.ts +++ b/lib/services/plugins-service.ts @@ -24,7 +24,7 @@ export class PluginsService implements IPluginsService { private $options: IOptions, private $logger: ILogger, private $errors: IErrors, - private $injector: IInjector) { } + private $projectFilesManager: IProjectFilesManager) { } public add(plugin: string): IFuture { return (() => { @@ -101,7 +101,9 @@ export class PluginsService implements IPluginsService { if(this.$fs.exists(pluginPlatformsFolderPath).wait()) { shelljs.rm("-rf", pluginPlatformsFolderPath); - } + } + + this.$projectFilesManager.processPlatformSpecificFiles(pluginDestinationPath, platform).wait(); // TODO: Add libraries diff --git a/lib/services/project-files-manager.ts b/lib/services/project-files-manager.ts new file mode 100644 index 0000000000..40be8f4336 --- /dev/null +++ b/lib/services/project-files-manager.ts @@ -0,0 +1,56 @@ +/// +"use strict"; +import path = require("path"); +import util = require("util"); + +export class ProjectFilesManager implements IProjectFilesManager { + constructor(private $fs: IFileSystem, + private $platformsData: IPlatformsData) { } + + public processPlatformSpecificFiles(directoryPath: string, platform: string, excludedDirs?: string[]): IFuture { + return (() => { + var contents = this.$fs.readDirectory(directoryPath).wait(); + var files: string[] = []; + + _.each(contents, fileName => { + let filePath = path.join(directoryPath, fileName); + let fsStat = this.$fs.getFsStats(filePath).wait(); + if(fsStat.isDirectory() && !_.contains(excludedDirs, fileName)) { + this.processPlatformSpecificFilesCore(platform, this.$fs.enumerateFilesInDirectorySync(filePath)).wait(); + } else if(fsStat.isFile()) { + files.push(filePath); + } + }); + this.processPlatformSpecificFilesCore(platform, files).wait(); + + }).future()(); + } + + private processPlatformSpecificFilesCore(platform: string, files: string[]): IFuture { + // Renames the files that have `platform` as substring and removes the files from other platform + return (() => { + _.each(files, fileName => { + var platformInfo = ProjectFilesManager.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()(); + } + + private static parsePlatformSpecificFileName(fileName: string, platforms: string[]): any { + var regex = util.format("^(.+?)\\.(%s)(\\..+?)$", platforms.join("|")); + var parsed = fileName.match(new RegExp(regex, "i")); + if (parsed) { + return { + platform: parsed[2], + onDeviceName: parsed[1] + parsed[3] + }; + } + return undefined; + } +} +$injector.register("projectFilesManager", ProjectFilesManager); diff --git a/test/npm-support.ts b/test/npm-support.ts index d7a557f79e..ce66886dfa 100644 --- a/test/npm-support.ts +++ b/test/npm-support.ts @@ -19,6 +19,8 @@ import BroccoliBuilderLib = require("../lib/tools/broccoli/builder"); import NodeModulesTreeLib = require("../lib/tools/broccoli/trees/node-modules-tree"); import PluginsServiceLib = require("../lib/services/plugins-service"); import ChildProcessLib = require("../lib/common/child-process"); +import ProjectFilesManagerLib = require("../lib/services/project-files-manager"); +import Future = require("fibers/future"); import path = require("path"); import temp = require("temp"); @@ -53,6 +55,7 @@ function createTestInjector(): IInjector { testInjector.register("pluginsService", PluginsServiceLib.PluginsService); testInjector.register("npm", NpmLib.NodePackageManager); testInjector.register("childProcess", ChildProcessLib.ChildProcess); + testInjector.register("projectFilesManager", ProjectFilesManagerLib.ProjectFilesManager); testInjector.register("commandsServiceProvider", { registerDynamicSubCommands: () => {} }); diff --git a/test/platform-commands.ts b/test/platform-commands.ts index 3038102f2e..f5a56e7031 100644 --- a/test/platform-commands.ts +++ b/test/platform-commands.ts @@ -11,6 +11,7 @@ import StaticConfigLib = require("../lib/config"); import CommandsServiceLib = require("../lib/common/services/commands-service"); import optionsLib = require("../lib/options"); import hostInfoLib = require("../lib/common/host-info"); +import ProjectFilesManagerLib = require("../lib/services/project-files-manager"); import path = require("path"); import Future = require("fibers/future"); var assert = require("chai").assert; @@ -115,6 +116,7 @@ function createTestInjector() { }).future()(); } }); + testInjector.register("projectFilesManager", ProjectFilesManagerLib.ProjectFilesManager); return testInjector; } diff --git a/test/platform-service.ts b/test/platform-service.ts index ce2782d899..e2fc5ea3bf 100644 --- a/test/platform-service.ts +++ b/test/platform-service.ts @@ -18,6 +18,7 @@ import ProjectDataLib = require("../lib/project-data"); import ProjectHelperLib = require("../lib/common/project-helper"); import optionsLib = require("../lib/options"); import hostInfoLib = require("../lib/common/host-info"); +import ProjectFilesManagerLib = require("../lib/services/project-files-manager"); import path = require("path"); import Future = require("fibers/future"); @@ -63,6 +64,7 @@ function createTestInjector() { return (() => { }).future()(); } }); + testInjector.register("projectFilesManager", ProjectFilesManagerLib.ProjectFilesManager); return testInjector; } @@ -235,7 +237,7 @@ describe('Platform Service Tests', () => { // Asserts that the files in app folder are process as platform specific assert.isTrue(fs.exists(path.join(appDestFolderPath, "app" , "test1.js")).wait()); - assert.isTrue(fs.exists(path.join(appDestFolderPath, "app", "test1-js")).wait()); + assert.isFalse(fs.exists(path.join(appDestFolderPath, "app", "test1-js")).wait()); assert.isFalse(fs.exists(path.join(appDestFolderPath, "app", "test2.js")).wait()); assert.isFalse(fs.exists(path.join(appDestFolderPath, "app", "test2-js")).wait()); @@ -286,7 +288,7 @@ describe('Platform Service Tests', () => { // Asserts that the files in app folder are process as platform specific assert.isTrue(fs.exists(path.join(appDestFolderPath, "app" , "test2.js")).wait()); - assert.isTrue(fs.exists(path.join(appDestFolderPath, "app", "test2-js")).wait()); + assert.isFalse(fs.exists(path.join(appDestFolderPath, "app", "test2-js")).wait()); assert.isFalse(fs.exists(path.join(appDestFolderPath, "app", "test1.js")).wait()); assert.isFalse(fs.exists(path.join(appDestFolderPath, "app", "test1-js")).wait()); diff --git a/test/plugins-service.ts b/test/plugins-service.ts index 781410308b..fdb5cd2083 100644 --- a/test/plugins-service.ts +++ b/test/plugins-service.ts @@ -17,6 +17,7 @@ import ProjectHelperLib = require("../lib/common/project-helper"); import PlatformsDataLib = require("../lib/platforms-data"); import ProjectDataServiceLib = require("../lib/services/project-data-service"); import helpers = require("../lib/common/helpers"); +import ProjectFilesManagerLib = require("../lib/services/project-files-manager"); import os = require("os"); import PluginsServiceLib = require("../lib/services/plugins-service"); @@ -64,6 +65,7 @@ function createTestInjector() { checkConsent: () => { return (() => { }).future()(); }, trackFeature: () => { return (() => { }).future()(); } }); + testInjector.register("projectFilesManager", ProjectFilesManagerLib.ProjectFilesManager); return testInjector; } diff --git a/test/project-files-manager.ts b/test/project-files-manager.ts new file mode 100644 index 0000000000..8b4cf41997 --- /dev/null +++ b/test/project-files-manager.ts @@ -0,0 +1,81 @@ +/// +"use strict"; + +import yok = require('../lib/common/yok'); +import fsLib = require("../lib/common/file-system"); +import projectFilesManagerLib = require("../lib/services/project-files-manager"); +import hostInfoLib = require("../lib/common/host-info"); +import StaticConfigLib = require("../lib/config"); +import ErrorsLib = require("../lib/common/errors"); +import path = require("path"); +import temp = require("temp"); +temp.track(); + +var assert = require("chai").assert; + +function createTestInjector() { + let testInjector = new yok.Yok(); + testInjector.register("fs", fsLib.FileSystem); + testInjector.register("hostInfo", hostInfoLib.HostInfo); + testInjector.register("staticConfig", StaticConfigLib.StaticConfig); + testInjector.register("projectFilesManager", projectFilesManagerLib.ProjectFilesManager); + testInjector.register("errors", ErrorsLib.Errors); + testInjector.register("platformsData", { + platformsNames: ["ios", "android"] + }); + + return testInjector; +} + +function createFiles(testInjector: IInjector, filesToCreate: string[]): IFuture { + return (() => { + let fs = testInjector.resolve("fs"); + let directoryPath = temp.mkdirSync("Project Files Manager Tests"); + + _.each(filesToCreate, file => fs.writeFile(path.join(directoryPath, file), "").wait()); + + return directoryPath; + }).future()(); +} + +describe('Project Files Manager Tests', () => { + let testInjector: IInjector, projectFilesManager: IProjectFilesManager; + beforeEach(() => { + testInjector = createTestInjector(); + projectFilesManager = testInjector.resolve("projectFilesManager"); + }); + it("filters android specific files", () => { + let files = ["test.ios.x", "test.android.x"]; + let directoryPath = createFiles(testInjector, files).wait(); + + projectFilesManager.processPlatformSpecificFiles(directoryPath, "android").wait(); + + let fs = testInjector.resolve("fs"); + assert.isFalse(fs.exists(path.join(directoryPath, "test.ios.x")).wait()); + assert.isTrue(fs.exists(path.join(directoryPath, "test.x")).wait()); + assert.isFalse(fs.exists(path.join(directoryPath, "test.android.x")).wait()); + }); + it("filters ios specific files", () => { + let files = ["index.ios.html", "index1.android.html", "a.test"]; + let directoryPath = createFiles(testInjector, files).wait(); + + projectFilesManager.processPlatformSpecificFiles(directoryPath, "ios").wait(); + + let fs = testInjector.resolve("fs"); + assert.isFalse(fs.exists(path.join(directoryPath, "index1.android.html")).wait()); + assert.isFalse(fs.exists(path.join(directoryPath, "index1.html")).wait()); + assert.isTrue(fs.exists(path.join(directoryPath, "index.html")).wait()); + assert.isTrue(fs.exists(path.join(directoryPath, "a.test")).wait()); + }); + it("doesn't filter non platform specific files", () => { + let files = ["index1.js", "index2.js", "index3.js"]; + let directoryPath = createFiles(testInjector, files).wait(); + + projectFilesManager.processPlatformSpecificFiles(directoryPath, "ios").wait(); + + let fs = testInjector.resolve("fs"); + assert.isTrue(fs.exists(path.join(directoryPath, "index1.js")).wait()); + assert.isTrue(fs.exists(path.join(directoryPath, "index2.js")).wait()); + assert.isTrue(fs.exists(path.join(directoryPath, "index3.js")).wait()); + }); +}); \ No newline at end of file From ea045490d218e8cbdddaa02c624cb5708e3ce42b Mon Sep 17 00:00:00 2001 From: Fatme Havaluova Date: Tue, 23 Jun 2015 17:57:46 +0300 Subject: [PATCH 03/17] Enable image resources from App_Resources in iOS Fixes https://github.com/NativeScript/nativescript-cli/issues/520 --- lib/definitions/project.d.ts | 1 + lib/services/android-project-service.ts | 6 ++- lib/services/ios-project-service.ts | 60 ++++++++++++++++++++++--- lib/services/platform-service.ts | 3 +- package.json | 2 +- test/npm-support.ts | 5 ++- test/platform-service.ts | 10 ++++- test/stubs.ts | 4 +- 8 files changed, 78 insertions(+), 13 deletions(-) diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index c45de5fc97..fce8b05537 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -27,6 +27,7 @@ interface IPlatformProjectService { interpolateData(projectRoot: string): IFuture; afterCreateProject(projectRoot: string): IFuture; buildProject(projectRoot: string): IFuture; + prepareProject(): IFuture; isPlatformPrepared(projectRoot: string): IFuture; addLibrary(platformData: IPlatformData, libraryPath: string): IFuture; canUpdatePlatform(currentVersion: string, newVersion: string): IFuture; diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 9dfb88947e..6d4da5f874 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -51,7 +51,7 @@ class AndroidProjectService implements IPlatformProjectService { frameworkFilesExtensions: [".jar", ".dat", ".so"], configurationFileName: "AndroidManifest.xml", configurationFilePath: path.join(this.$projectData.platformsDir, "android", "AndroidManifest.xml"), - mergeXmlConfig: [{ "nodename": "manifest", "attrname": "*" }] + mergeXmlConfig: [{ "nodename": "manifest", "attrname": "*" }, { "application": "manifest", "attrname": "*" }] }; } @@ -273,6 +273,10 @@ class AndroidProjectService implements IPlatformProjectService { public getFrameworkFilesExtensions(): string[] { return [".jar", ".dat"]; } + + public prepareProject(): IFuture { + return (() => { }).future()(); + } private copy(projectRoot: string, frameworkDir: string, files: string, cpArg: string): IFuture { return (() => { diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index d0f1a5d9b5..bed5677ffb 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -182,13 +182,11 @@ class IOSProjectService implements IPlatformProjectService { this.$fs.ensureDirectoryExists(targetPath).wait(); shell.cp("-R", libraryPath, targetPath); - var pbxProjPath = path.join(platformData.projectRoot, this.$projectData.projectName + ".xcodeproj", "project.pbxproj"); - var project = new xcode.project(pbxProjPath); - project.parseSync(); + let project = this.createPbxProj(); project.addFramework(path.join(targetPath, frameworkName + ".framework"), { customFramework: true, embed: true }); project.updateBuildProperty("IPHONEOS_DEPLOYMENT_TARGET", "8.0"); - this.$fs.writeFile(pbxProjPath, project.writeSync()).wait(); + this.savePbxProj(project).wait(); this.$logger.info("The iOS Deployment Target is now 8.0 in order to support Cocoa Touch Frameworks."); }).future()(); } @@ -221,11 +219,63 @@ class IOSProjectService implements IPlatformProjectService { shell.cp("-R", path.join(cachedPackagePath, "*"), path.join(this.platformData.projectRoot, util.format("%s.xcodeproj", this.$projectData.projectName))); this.$logger.info("Copied from %s at %s.", cachedPackagePath, this.platformData.projectRoot); - var pbxprojFilePath = path.join(this.platformData.projectRoot, this.$projectData.projectName + IOSProjectService.XCODE_PROJECT_EXT_NAME, "project.pbxproj"); this.replaceFileContent(pbxprojFilePath).wait(); }).future()(); } + + public prepareProject(): IFuture { + return (() => { + let project = this.createPbxProj(); + let resources = project.pbxGroupByName("Resources"); + + if(resources) { + let references = project.pbxFileReferenceSection(); + + let xcodeProjectImages = _.map(resources.children, resource => this.replace(references[resource.value].name)); + this.$logger.trace("Images from Xcode project"); + this.$logger.trace(xcodeProjectImages); + + let appResourcesImages = this.$fs.readDirectory(this.platformData.appResourcesDestinationDirectoryPath).wait(); + this.$logger.trace("Current images from App_Resources"); + this.$logger.trace(appResourcesImages); + + let imagesToAdd = _.difference(appResourcesImages, xcodeProjectImages); + this.$logger.trace(`New images to add into xcode project: ${imagesToAdd.join(", ")}`); + _.each(imagesToAdd, image => project.addResourceFile(path.relative(this.platformData.projectRoot, path.join( this.platformData.appResourcesDestinationDirectoryPath, image)))); + + let imagesToRemove = _.difference(xcodeProjectImages, appResourcesImages); + this.$logger.trace(`Images to remove from xcode project: ${imagesToRemove.join(", ")}`); + _.each(imagesToRemove, image => project.removeResourceFile(path.join(this.platformData.appResourcesDestinationDirectoryPath, image))); + + this.savePbxProj(project).wait(); + } + + }).future()(); + } + + private replace(name: string): string { + if(_.startsWith(name, '"')) { + name = name.substr(1, name.length-2); + } + + return name.replace(/\\\"/g, "\""); + } + + private get pbxProjPath(): string { + return path.join(this.platformData.projectRoot, this.$projectData.projectName + ".xcodeproj", "project.pbxproj"); + } + + private createPbxProj(): any { + let project = new xcode.project(this.pbxProjPath); + project.parseSync(); + + return project; + } + + private savePbxProj(project: any): IFuture { + return this.$fs.writeFile(this.pbxProjPath, project.writeSync()); + } private buildPathToXcodeProjectFile(version: string): string { return path.join(this.$npmInstallationManager.getCachedPackagePath(this.platformData.frameworkPackageName, version), constants.PROJECT_FRAMEWORK_FOLDER_NAME, util.format("%s.xcodeproj", IOSProjectService.IOS_PROJECT_NAME_PLACEHOLDER), "project.pbxproj"); diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 192d539482..c40f96a3b2 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -161,7 +161,8 @@ export class PlatformService implements IPlatformService { this.$fs.ensureDirectoryExists(platformData.appResourcesDestinationDirectoryPath).wait(); // Should be deleted var appResourcesDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); if (this.$fs.exists(appResourcesDirectoryPath).wait()) { - shell.cp("-Rf", path.join(appResourcesDirectoryPath, platformData.normalizedPlatformName, "*"), platformData.appResourcesDestinationDirectoryPath); + this.$fs.deleteDirectory(platformData.appResourcesDestinationDirectoryPath).wait(); // Respect removed files + shell.cp("-R", path.join(appResourcesDirectoryPath, platformData.normalizedPlatformName, "*"), platformData.appResourcesDestinationDirectoryPath); this.$fs.deleteDirectory(appResourcesDirectoryPath).wait(); } diff --git a/package.json b/package.json index 7aa5fe18c0..af3e11c8fe 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "utf-8-validate": "https://github.com/telerik/utf-8-validate/tarball/master", "winreg": "0.0.12", "ws": "0.7.1", - "xcode": "https://github.com/NativeScript/node-xcode/archive/NativeScript-0.9.tar.gz", + "xcode": "https://github.com/NativeScript/node-xcode/archive/NativeScript-1.1.tar.gz", "xmldom": "0.1.19", "xmlhttprequest": "https://github.com/telerik/node-XMLHttpRequest/tarball/master", "xmlmerge-js": "0.2.4", diff --git a/test/npm-support.ts b/test/npm-support.ts index ce66886dfa..2a2e66686a 100644 --- a/test/npm-support.ts +++ b/test/npm-support.ts @@ -121,7 +121,10 @@ describe("Npm support tests", () => { appDestinationDirectoryPath: appDestinationFolderPath, appResourcesDestinationDirectoryPath: path.join(appDestinationFolderPath, "app", "App_Resources"), frameworkPackageName: "tns-android", - normalizedPlatformName: "Android" + normalizedPlatformName: "Android", + platformProjectService: { + prepareProject: () => Future.fromResult() + } } }; diff --git a/test/platform-service.ts b/test/platform-service.ts index e2fc5ea3bf..7429a1416b 100644 --- a/test/platform-service.ts +++ b/test/platform-service.ts @@ -225,7 +225,10 @@ describe('Platform Service Tests', () => { return { appDestinationDirectoryPath: appDestFolderPath, appResourcesDestinationDirectoryPath: appResourcesFolderPath, - normalizedPlatformName: "iOS" + normalizedPlatformName: "iOS", + platformProjectService: { + prepareProject: () => Future.fromResult() + } } }; @@ -276,7 +279,10 @@ describe('Platform Service Tests', () => { return { appDestinationDirectoryPath: appDestFolderPath, appResourcesDestinationDirectoryPath: appResourcesFolderPath, - normalizedPlatformName: "Android" + normalizedPlatformName: "Android", + platformProjectService: { + prepareProject: () => Future.fromResult() + } } }; diff --git a/test/stubs.ts b/test/stubs.ts index a3b45db324..7cb8a456e0 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -276,8 +276,8 @@ export class PlatformProjectServiceStub implements IPlatformProjectService { afterCreateProject(projectRoot: string): IFuture { return Future.fromResult(); } - prepareProject(platformData: IPlatformData): IFuture { - return Future.fromResult(""); + prepareProject(): IFuture { + return Future.fromResult(); } buildProject(projectRoot: string): IFuture { return Future.fromResult(); From d8affaea1ebca0964e30f41878688018b67b0d65 Mon Sep 17 00:00:00 2001 From: Fatme Havaluova Date: Fri, 26 Jun 2015 13:40:19 +0300 Subject: [PATCH 04/17] Fix broken android prepare --- lib/definitions/project.d.ts | 1 + lib/services/android-project-service.ts | 10 ++++++++++ lib/services/ios-project-service.ts | 4 ++++ lib/services/platform-service.ts | 4 ++-- test/npm-support.ts | 3 ++- test/stubs.ts | 3 +++ 6 files changed, 22 insertions(+), 3 deletions(-) diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index fce8b05537..3ac9337349 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -28,6 +28,7 @@ interface IPlatformProjectService { afterCreateProject(projectRoot: string): IFuture; buildProject(projectRoot: string): IFuture; prepareProject(): IFuture; + prepareAppResources(appResourcesDirectoryPath: string): IFuture; isPlatformPrepared(projectRoot: string): IFuture; addLibrary(platformData: IPlatformData, libraryPath: string): IFuture; canUpdatePlatform(currentVersion: string, newVersion: string): IFuture; diff --git a/lib/services/android-project-service.ts b/lib/services/android-project-service.ts index 6d4da5f874..1561015c85 100644 --- a/lib/services/android-project-service.ts +++ b/lib/services/android-project-service.ts @@ -181,6 +181,16 @@ class AndroidProjectService implements IPlatformProjectService { public isPlatformPrepared(projectRoot: string): IFuture { return this.$fs.exists(path.join(projectRoot, "assets", constants.APP_FOLDER_NAME)); } + + public prepareAppResources(appResourcesDirectoryPath: string): IFuture { + return (() => { + let resourcesDirPath = path.join(appResourcesDirectoryPath, this.platformData.normalizedPlatformName); + let resourcesDirs = this.$fs.readDirectory(resourcesDirPath).wait(); + _.each(resourcesDirs, resourceDir => { + this.$fs.deleteDirectory(path.join(this.platformData.appResourcesDestinationDirectoryPath, resourceDir)).wait(); + }); + }).future()(); + } private parseProjectProperties(projDir: string, destDir: string): void { let projProp = path.join(projDir, "project.properties"); diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index bed5677ffb..810244d4c0 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -254,6 +254,10 @@ class IOSProjectService implements IPlatformProjectService { }).future()(); } + public prepareAppResources(appResourcesDirectoryPath: string): IFuture { + return this.$fs.deleteDirectory(this.platformData.appResourcesDestinationDirectoryPath); + } + private replace(name: string): string { if(_.startsWith(name, '"')) { name = name.substr(1, name.length-2); diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index c40f96a3b2..ab56fb48f3 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -158,10 +158,10 @@ export class PlatformService implements IPlatformService { shell.cp("-R", appSourceDirectoryPath, platformData.appDestinationDirectoryPath); // Copy App_Resources to project root folder - this.$fs.ensureDirectoryExists(platformData.appResourcesDestinationDirectoryPath).wait(); // Should be deleted var appResourcesDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); + this.$fs.ensureDirectoryExists(platformData.appResourcesDestinationDirectoryPath).wait(); if (this.$fs.exists(appResourcesDirectoryPath).wait()) { - this.$fs.deleteDirectory(platformData.appResourcesDestinationDirectoryPath).wait(); // Respect removed files + platformData.platformProjectService.prepareAppResources(appResourcesDirectoryPath).wait(); shell.cp("-R", path.join(appResourcesDirectoryPath, platformData.normalizedPlatformName, "*"), platformData.appResourcesDestinationDirectoryPath); this.$fs.deleteDirectory(appResourcesDirectoryPath).wait(); } diff --git a/test/npm-support.ts b/test/npm-support.ts index 2a2e66686a..ff5d00cda6 100644 --- a/test/npm-support.ts +++ b/test/npm-support.ts @@ -123,7 +123,8 @@ describe("Npm support tests", () => { frameworkPackageName: "tns-android", normalizedPlatformName: "Android", platformProjectService: { - prepareProject: () => Future.fromResult() + prepareProject: () => Future.fromResult(), + prepareAppResources: () => Future.fromResult() } } }; diff --git a/test/stubs.ts b/test/stubs.ts index 7cb8a456e0..79b60c2a90 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -297,6 +297,9 @@ export class PlatformProjectServiceStub implements IPlatformProjectService { updatePlatform(currentVersion: string, newVersion: string): IFuture { return Future.fromResult(); } + prepareAppResources(appResourcesDirectoryPath: string): IFuture { + return Future.fromResult(); + } } export class ProjectDataService implements IProjectDataService { From f2b9f22a79f7e0983d86869e922328563baa160f Mon Sep 17 00:00:00 2001 From: Fatme Havaluova Date: Fri, 26 Jun 2015 15:56:43 +0300 Subject: [PATCH 05/17] Fix broken rebase --- lib/services/platform-service.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index ab56fb48f3..969a9c0045 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -165,6 +165,8 @@ export class PlatformService implements IPlatformService { shell.cp("-R", path.join(appResourcesDirectoryPath, platformData.normalizedPlatformName, "*"), platformData.appResourcesDestinationDirectoryPath); this.$fs.deleteDirectory(appResourcesDirectoryPath).wait(); } + + platformData.platformProjectService.prepareProject().wait(); // Process platform specific files let directoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); From 32d168a0a2332fe75c808895bffdeeb106430756 Mon Sep 17 00:00:00 2001 From: Fatme Havaluova Date: Fri, 26 Jun 2015 10:14:52 +0300 Subject: [PATCH 06/17] Fix symlinks that are added to output package and points to not existing location --- lib/services/platform-service.ts | 21 ++-- lib/tools/broccoli/broccoli.d.ts | 3 +- lib/tools/broccoli/builder.ts | 80 +++++++++++--- lib/tools/broccoli/node-modules-dest-copy.ts | 103 +++++++++---------- package.json | 3 + 5 files changed, 134 insertions(+), 76 deletions(-) diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 969a9c0045..2e780a8f29 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -149,17 +149,26 @@ export class PlatformService implements IPlatformService { platform = platform.toLowerCase(); var platformData = this.$platformsData.getPlatformData(platform); + let appDestinationDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME); + let lastModifiedTime = this.$fs.exists(appDestinationDirectoryPath).wait() ? + this.$fs.getFsStats(appDestinationDirectoryPath).wait().mtime : null; // Copy app folder to native project + this.$fs.ensureDirectoryExists(appDestinationDirectoryPath).wait(); var appSourceDirectoryPath = path.join(this.$projectData.projectDir, constants.APP_FOLDER_NAME); - + // Delete the destination app in order to prevent EEXIST errors when symlinks are used. - this.$fs.deleteDirectory(path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME)).wait(); - shell.cp("-R", appSourceDirectoryPath, platformData.appDestinationDirectoryPath); + let contents = this.$fs.readDirectory(appDestinationDirectoryPath).wait(); + + _(contents) + .filter(directoryName => directoryName !== "tns_modules") + .each(directoryName => this.$fs.deleteDirectory(path.join(appDestinationDirectoryPath, directoryName)).wait()) + .value(); + shell.cp("-Rf", appSourceDirectoryPath, platformData.appDestinationDirectoryPath); // Copy App_Resources to project root folder - var appResourcesDirectoryPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME); - this.$fs.ensureDirectoryExists(platformData.appResourcesDestinationDirectoryPath).wait(); + this.$fs.ensureDirectoryExists(platformData.appResourcesDestinationDirectoryPath).wait(); // Should be deleted + var appResourcesDirectoryPath = path.join(appDestinationDirectoryPath, constants.APP_RESOURCES_FOLDER_NAME); if (this.$fs.exists(appResourcesDirectoryPath).wait()) { platformData.platformProjectService.prepareAppResources(appResourcesDirectoryPath).wait(); shell.cp("-R", path.join(appResourcesDirectoryPath, platformData.normalizedPlatformName, "*"), platformData.appResourcesDestinationDirectoryPath); @@ -176,7 +185,7 @@ export class PlatformService implements IPlatformService { // Process node_modules folder this.$pluginsService.ensureAllDependenciesAreInstalled().wait(); var tnsModulesDestinationPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, PlatformService.TNS_MODULES_FOLDER_NAME); - this.$broccoliBuilder.prepareNodeModules(tnsModulesDestinationPath, this.$projectData.projectDir).wait(); + this.$broccoliBuilder.prepareNodeModules(tnsModulesDestinationPath, this.$projectData.projectDir, lastModifiedTime).wait(); this.$logger.out("Project successfully prepared"); }).future()(); diff --git a/lib/tools/broccoli/broccoli.d.ts b/lib/tools/broccoli/broccoli.d.ts index 4ca05877c3..93ae00eca4 100644 --- a/lib/tools/broccoli/broccoli.d.ts +++ b/lib/tools/broccoli/broccoli.d.ts @@ -152,7 +152,7 @@ interface BroccoliNode { } interface IBroccoliBuilder { - prepareNodeModules(outputPath: string, projectDir: string): IFuture; + prepareNodeModules(outputPath: string, projectDir: string, lastModifiedTime?: Date): IFuture; } interface IDiffResult { @@ -162,6 +162,7 @@ interface IDiffResult { interface IBroccoliPlugin { rebuild(diff: IDiffResult): any; + rebuildChangedDirectories?(changedDirectories: string[]): void; cleanup? () : void; } diff --git a/lib/tools/broccoli/builder.ts b/lib/tools/broccoli/builder.ts index 070b5259b8..2e5bc64330 100644 --- a/lib/tools/broccoli/builder.ts +++ b/lib/tools/broccoli/builder.ts @@ -5,29 +5,81 @@ let broccoli = require('broccoli'); let path = require('path'); import Future = require("fibers/future"); import {TreeDiffer} from './tree-differ'; -import destCopy = require('./node-modules-dest-copy'); +import destCopyLib = require('./node-modules-dest-copy'); + +var gulp = require("gulp"); +var vinylFilterSince = require('vinyl-filter-since') +var through = require("through2"); export class Builder implements IBroccoliBuilder { + private nodeModules: any = {}; + constructor(private $fs: IFileSystem, private $nodeModulesTree: INodeModulesTree, private $projectDataService: IProjectDataService, + private $injector: IInjector, private $logger: ILogger) { } - public prepareNodeModules(absoluteOutputPath: string, projectDir: string): IFuture { + public prepareNodeModules(absoluteOutputPath: string, projectDir: string, lastModifiedTime?: Date): IFuture { return (() => { - // TODO: figure out a better way for doing this - this.$projectDataService.initialize(projectDir); - let cachedNodeModulesPath = this.$projectDataService.getValue("node-modules-cache-path").wait(); - if (cachedNodeModulesPath && this.$fs.exists(cachedNodeModulesPath).wait()) { - let diffTree = new TreeDiffer(cachedNodeModulesPath); - let diffTreeResult = diffTree.diffTree(path.join(projectDir, absoluteOutputPath, "node_modules")); - - if(diffTreeResult.changedDirectories.length > 0 || diffTreeResult.removedDirectories.length > 0) { - this.rebuildNodeModulesTree(absoluteOutputPath, projectDir).wait(); - } - } else { - this.rebuildNodeModulesTree(absoluteOutputPath, projectDir).wait(); + let isNodeModulesModified = false; + let nodeModulesPath = path.join(projectDir, "node_modules"); + + if(lastModifiedTime) { + let pipeline = gulp.src(path.join(projectDir, "node_modules/**")) + .pipe(vinylFilterSince(lastModifiedTime)) + .pipe(through.obj( (chunk: any, enc: any, cb: Function) => { + if(chunk.path === nodeModulesPath) { + isNodeModulesModified = true; + } + + if(!isNodeModulesModified) { + let rootModuleName = chunk.path.split(nodeModulesPath)[1].split(path.sep)[1]; + let rootModuleFullPath = path.join(nodeModulesPath, rootModuleName); + this.nodeModules[rootModuleFullPath] = rootModuleFullPath; + } + + cb(null); + })) + .pipe(gulp.dest(absoluteOutputPath)); + + let future = new Future(); + + pipeline.on('end', (err: any, data: any) => { + if(err) { + future.throw(err); + } else { + future.return(); + } + }); + + future.wait(); + } + + if(isNodeModulesModified && this.$fs.exists(absoluteOutputPath).wait()) { + let currentPreparedTnsModules = this.$fs.readDirectory(absoluteOutputPath).wait(); + let tnsModulesInApp = this.$fs.readDirectory(path.join(projectDir, "app", "tns_modules")).wait(); + let modulesToDelete = _.difference(currentPreparedTnsModules, tnsModulesInApp); + _.each(modulesToDelete, moduleName => this.$fs.deleteDirectory(path.join(absoluteOutputPath, moduleName)).wait()) + } + + if(!lastModifiedTime || isNodeModulesModified) { + let nodeModulesDirectories = this.$fs.readDirectory(nodeModulesPath).wait(); + _.each(nodeModulesDirectories, nodeModuleDirectoryName => { + let nodeModuleFullPath = path.join(nodeModulesPath, nodeModuleDirectoryName); + this.nodeModules[nodeModuleFullPath] = nodeModuleFullPath; + }); } + + let destCopy = this.$injector.resolve(destCopyLib.DestCopy, { + inputPath: projectDir, + cachePath: "", + outputRoot: absoluteOutputPath, + projectDir: projectDir + }); + + destCopy.rebuildChangedDirectories(_.keys(this.nodeModules)); + }).future()(); } diff --git a/lib/tools/broccoli/node-modules-dest-copy.ts b/lib/tools/broccoli/node-modules-dest-copy.ts index 61b0f3e801..07fd62e4b5 100644 --- a/lib/tools/broccoli/node-modules-dest-copy.ts +++ b/lib/tools/broccoli/node-modules-dest-copy.ts @@ -14,57 +14,66 @@ import constants = require("./../../constants"); * and tees a copy to the given path outside the tmp dir. */ export class DestCopy implements IBroccoliPlugin { - constructor(private inputPath: string, private cachePath: string, private outputRoot: string, private projectDir: string) {} - - public rebuild(treeDiff: IDiffResult): void { - let dependencies = this.getDependencies(); - let devDependencies = this.getDevDependencies(this.projectDir); - - treeDiff.changedDirectories.forEach(changedDirectory => { - let changedDirectoryAbsolutePath = path.join(this.inputPath, constants.NODE_MODULES_FOLDER_NAME, changedDirectory); - let packageJsonFiles = [path.join(changedDirectoryAbsolutePath, "package.json")]; - let nodeModulesFolderPath = path.join(changedDirectoryAbsolutePath, "node_modules"); - packageJsonFiles = packageJsonFiles.concat(this.enumeratePackageJsonFilesSync(nodeModulesFolderPath)); - - _.each(packageJsonFiles, packageJsonFilePath => { - let fileContent = require(packageJsonFilePath); - let isPlugin = fileContent.nativescript; + private dependencies: IDictionary = null; + private devDependencies: IDictionary = null; - if(!devDependencies[fileContent.name]) { // Don't flatten dev dependencies - - let currentDependency = { - name: fileContent.name, - version: fileContent.version, - directory: path.dirname(packageJsonFilePath), - isPlugin: isPlugin - }; + constructor(private inputPath: string, private cachePath: string, private outputRoot: string, private projectDir: string) { + this.dependencies = Object.create(null); + this.devDependencies = this.getDevDependencies(projectDir); + } + + public rebuildChangedDirectories(changedDirectories: string[]): void { + _.each(changedDirectories, changedDirectoryAbsolutePath => { + let pathToPackageJson = path.join(changedDirectoryAbsolutePath, "package.json"); + if(fs.existsSync(pathToPackageJson)) { + let packageJsonFiles = [pathToPackageJson]; + let nodeModulesFolderPath = path.join(changedDirectoryAbsolutePath, "node_modules"); + packageJsonFiles = packageJsonFiles.concat(this.enumeratePackageJsonFilesSync(nodeModulesFolderPath)); - let addedDependency = dependencies[currentDependency.name]; - if (addedDependency) { - if (semver.gt(currentDependency.version, addedDependency.version)) { - let currentDependencyMajorVersion = semver.major(currentDependency.version); - let addedDependencyMajorVersion = semver.major(addedDependency.version); + _.each(packageJsonFiles, packageJsonFilePath => { + let fileContent = require(packageJsonFilePath); + let isPlugin = fileContent.nativescript; - let message = `The depedency located at ${addedDependency.directory} with version ${addedDependency.version} will be replaced with dependency located at ${currentDependency.directory} with version ${currentDependency.version}`; - let logger = $injector.resolve("$logger"); - currentDependencyMajorVersion === addedDependencyMajorVersion ? logger.out(message) : logger.warn(message); - - dependencies[currentDependency.name] = currentDependency; + if(!this.devDependencies[fileContent.name]) { // Don't flatten dev dependencies + + let currentDependency = { + name: fileContent.name, + version: fileContent.version, + directory: path.dirname(packageJsonFilePath), + isPlugin: isPlugin + }; + + let addedDependency = this.dependencies[currentDependency.name]; + if (addedDependency) { + if (semver.gt(currentDependency.version, addedDependency.version)) { + let currentDependencyMajorVersion = semver.major(currentDependency.version); + let addedDependencyMajorVersion = semver.major(addedDependency.version); + + let message = `The depedency located at ${addedDependency.directory} with version ${addedDependency.version} will be replaced with dependency located at ${currentDependency.directory} with version ${currentDependency.version}`; + let logger = $injector.resolve("$logger"); + currentDependencyMajorVersion === addedDependencyMajorVersion ? logger.out(message) : logger.warn(message); + + this.dependencies[currentDependency.name] = currentDependency; + } + } else { + this.dependencies[currentDependency.name] = currentDependency; } - } else { - dependencies[currentDependency.name] = currentDependency; } - } - }); + }); + } }); - _.each(dependencies, dependency => { - shelljs.cp("-R", dependency.directory, this.outputRoot); + _.each(this.dependencies, dependency => { + shelljs.cp("-Rf", dependency.directory, this.outputRoot); shelljs.rm("-rf", path.join(this.outputRoot, dependency.name, "node_modules")); if(dependency.isPlugin) { shelljs.rm("-rf", path.join(this.outputRoot, dependency.name, "platforms")); } }); + } + + public rebuild(treeDiff: IDiffResult): void { + this.rebuildChangedDirectories(treeDiff.changedDirectories); // Cache input tree let projectFilePath = path.join(this.projectDir, constants.PACKAGE_JSON_FILE_NAME); @@ -73,22 +82,6 @@ export class DestCopy implements IBroccoliPlugin { fs.writeFileSync(projectFilePath, JSON.stringify(projectFileContent, null, "\t"), { encoding: "utf8" }); } - private getDependencies(): IDictionary { - let result = Object.create(null); - if(fs.existsSync(this.outputRoot)) { - let dirs = fs.readdirSync(this.outputRoot); - _.each(dirs, dir => { - let filePath = path.join(dir, constants.PACKAGE_JSON_FILE_NAME); - if(fs.existsSync(filePath)) { - let fileContent = require(filePath); - result[fileContent.name] = fileContent; - } - }); - } - - return result; - } - private getDevDependencies(projectDir: string): IDictionary { let projectFilePath = path.join(projectDir, constants.PACKAGE_JSON_FILE_NAME); let projectFileContent = require(projectFilePath); diff --git a/package.json b/package.json index af3e11c8fe..c6919fbd43 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "fibers": "https://github.com/icenium/node-fibers/tarball/v1.0.5.1", "filesize": "2.0.3", "gaze": "0.5.1", + "gulp": "3.9.0", "iconv-lite": "0.4.4", "inquirer": "0.8.2", "ios-sim-portable": "1.0.8", @@ -65,7 +66,9 @@ "shelljs": "0.3.0", "tabtab": "https://github.com/Icenium/node-tabtab/tarball/master", "temp": "0.8.1", + "through2": "2.0.0", "utf-8-validate": "https://github.com/telerik/utf-8-validate/tarball/master", + "vinyl-filter-since": "2.0.0", "winreg": "0.0.12", "ws": "0.7.1", "xcode": "https://github.com/NativeScript/node-xcode/archive/NativeScript-1.1.tar.gz", From 7513ad641783020b847c7ca8e4b8e63380e7a0e5 Mon Sep 17 00:00:00 2001 From: Fatme Havaluova Date: Fri, 26 Jun 2015 19:32:11 +0300 Subject: [PATCH 07/17] Unit tests --- lib/services/platform-service.ts | 2 +- lib/tools/broccoli/broccoli.d.ts | 4 +- lib/tools/broccoli/builder.ts | 5 +- lib/tools/broccoli/node-modules-dest-copy.ts | 78 +++++++++++--------- test/npm-support.ts | 54 +++++++++++--- 5 files changed, 92 insertions(+), 51 deletions(-) diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 2e780a8f29..e47f318a92 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -185,7 +185,7 @@ export class PlatformService implements IPlatformService { // Process node_modules folder this.$pluginsService.ensureAllDependenciesAreInstalled().wait(); var tnsModulesDestinationPath = path.join(platformData.appDestinationDirectoryPath, constants.APP_FOLDER_NAME, PlatformService.TNS_MODULES_FOLDER_NAME); - this.$broccoliBuilder.prepareNodeModules(tnsModulesDestinationPath, this.$projectData.projectDir, lastModifiedTime).wait(); + this.$broccoliBuilder.prepareNodeModules(tnsModulesDestinationPath, this.$projectData.projectDir, platform, lastModifiedTime).wait(); this.$logger.out("Project successfully prepared"); }).future()(); diff --git a/lib/tools/broccoli/broccoli.d.ts b/lib/tools/broccoli/broccoli.d.ts index 93ae00eca4..7d4ba88820 100644 --- a/lib/tools/broccoli/broccoli.d.ts +++ b/lib/tools/broccoli/broccoli.d.ts @@ -152,7 +152,7 @@ interface BroccoliNode { } interface IBroccoliBuilder { - prepareNodeModules(outputPath: string, projectDir: string, lastModifiedTime?: Date): IFuture; + prepareNodeModules(outputPath: string, projectDir: string, platform: string, lastModifiedTime?: Date): IFuture; } interface IDiffResult { @@ -162,7 +162,7 @@ interface IDiffResult { interface IBroccoliPlugin { rebuild(diff: IDiffResult): any; - rebuildChangedDirectories?(changedDirectories: string[]): void; + rebuildChangedDirectories?(changedDirectories: string[], platform: string): void; cleanup? () : void; } diff --git a/lib/tools/broccoli/builder.ts b/lib/tools/broccoli/builder.ts index 2e5bc64330..9d8fba9437 100644 --- a/lib/tools/broccoli/builder.ts +++ b/lib/tools/broccoli/builder.ts @@ -20,7 +20,7 @@ export class Builder implements IBroccoliBuilder { private $injector: IInjector, private $logger: ILogger) { } - public prepareNodeModules(absoluteOutputPath: string, projectDir: string, lastModifiedTime?: Date): IFuture { + public prepareNodeModules(absoluteOutputPath: string, projectDir: string, platform: string, lastModifiedTime?: Date): IFuture { return (() => { let isNodeModulesModified = false; let nodeModulesPath = path.join(projectDir, "node_modules"); @@ -75,7 +75,8 @@ export class Builder implements IBroccoliBuilder { inputPath: projectDir, cachePath: "", outputRoot: absoluteOutputPath, - projectDir: projectDir + projectDir: projectDir, + platform: platform }); destCopy.rebuildChangedDirectories(_.keys(this.nodeModules)); diff --git a/lib/tools/broccoli/node-modules-dest-copy.ts b/lib/tools/broccoli/node-modules-dest-copy.ts index 07fd62e4b5..9f33ee8e8a 100644 --- a/lib/tools/broccoli/node-modules-dest-copy.ts +++ b/lib/tools/broccoli/node-modules-dest-copy.ts @@ -17,63 +17,68 @@ export class DestCopy implements IBroccoliPlugin { private dependencies: IDictionary = null; private devDependencies: IDictionary = null; - constructor(private inputPath: string, private cachePath: string, private outputRoot: string, private projectDir: string) { + constructor(private inputPath: string, + private cachePath: string, + private outputRoot: string, + private projectDir: string, + private platform: string, + private $fs: IFileSystem, + private $projectFilesManager: IProjectFilesManager) { this.dependencies = Object.create(null); this.devDependencies = this.getDevDependencies(projectDir); } - public rebuildChangedDirectories(changedDirectories: string[]): void { + public rebuildChangedDirectories(changedDirectories: string[], platform: string): void { _.each(changedDirectories, changedDirectoryAbsolutePath => { let pathToPackageJson = path.join(changedDirectoryAbsolutePath, "package.json"); - if(fs.existsSync(pathToPackageJson)) { - let packageJsonFiles = [pathToPackageJson]; - let nodeModulesFolderPath = path.join(changedDirectoryAbsolutePath, "node_modules"); - packageJsonFiles = packageJsonFiles.concat(this.enumeratePackageJsonFilesSync(nodeModulesFolderPath)); + let packageJsonFiles = fs.existsSync(pathToPackageJson) ? [pathToPackageJson] : []; + let nodeModulesFolderPath = path.join(changedDirectoryAbsolutePath, "node_modules"); + packageJsonFiles = packageJsonFiles.concat(this.enumeratePackageJsonFilesSync(nodeModulesFolderPath)); + + _.each(packageJsonFiles, packageJsonFilePath => { + let fileContent = require(packageJsonFilePath); + let isPlugin = fileContent.nativescript; + + if(!this.devDependencies[fileContent.name]) { // Don't flatten dev dependencies + + let currentDependency = { + name: fileContent.name, + version: fileContent.version, + directory: path.dirname(packageJsonFilePath), + isPlugin: isPlugin + }; - _.each(packageJsonFiles, packageJsonFilePath => { - let fileContent = require(packageJsonFilePath); - let isPlugin = fileContent.nativescript; + let addedDependency = this.dependencies[currentDependency.name]; + if (addedDependency) { + if (semver.gt(currentDependency.version, addedDependency.version)) { + let currentDependencyMajorVersion = semver.major(currentDependency.version); + let addedDependencyMajorVersion = semver.major(addedDependency.version); + + let message = `The depedency located at ${addedDependency.directory} with version ${addedDependency.version} will be replaced with dependency located at ${currentDependency.directory} with version ${currentDependency.version}`; + let logger = $injector.resolve("$logger"); + currentDependencyMajorVersion === addedDependencyMajorVersion ? logger.out(message) : logger.warn(message); - if(!this.devDependencies[fileContent.name]) { // Don't flatten dev dependencies - - let currentDependency = { - name: fileContent.name, - version: fileContent.version, - directory: path.dirname(packageJsonFilePath), - isPlugin: isPlugin - }; - - let addedDependency = this.dependencies[currentDependency.name]; - if (addedDependency) { - if (semver.gt(currentDependency.version, addedDependency.version)) { - let currentDependencyMajorVersion = semver.major(currentDependency.version); - let addedDependencyMajorVersion = semver.major(addedDependency.version); - - let message = `The depedency located at ${addedDependency.directory} with version ${addedDependency.version} will be replaced with dependency located at ${currentDependency.directory} with version ${currentDependency.version}`; - let logger = $injector.resolve("$logger"); - currentDependencyMajorVersion === addedDependencyMajorVersion ? logger.out(message) : logger.warn(message); - - this.dependencies[currentDependency.name] = currentDependency; - } - } else { this.dependencies[currentDependency.name] = currentDependency; } + } else { + this.dependencies[currentDependency.name] = currentDependency; } - }); - } + } + }); }); _.each(this.dependencies, dependency => { shelljs.cp("-Rf", dependency.directory, this.outputRoot); shelljs.rm("-rf", path.join(this.outputRoot, dependency.name, "node_modules")); if(dependency.isPlugin) { + this.$projectFilesManager.processPlatformSpecificFiles(path.join(this.outputRoot, dependency.name), platform).wait(); shelljs.rm("-rf", path.join(this.outputRoot, dependency.name, "platforms")); } }); } public rebuild(treeDiff: IDiffResult): void { - this.rebuildChangedDirectories(treeDiff.changedDirectories); + this.rebuildChangedDirectories(treeDiff.changedDirectories, ""); // Cache input tree let projectFilePath = path.join(this.projectDir, constants.PACKAGE_JSON_FILE_NAME); @@ -93,7 +98,10 @@ export class DestCopy implements IBroccoliPlugin { if(fs.existsSync(nodeModulesDirectoryPath)) { let contents = fs.readdirSync(nodeModulesDirectoryPath); for (let i = 0; i < contents.length; ++i) { - foundFiles.push(path.join(nodeModulesDirectoryPath, contents[i], "package.json")); + let packageJsonFilePath = path.join(nodeModulesDirectoryPath, contents[i], "package.json"); + if (fs.existsSync(packageJsonFilePath)) { + foundFiles.push(packageJsonFilePath); + } var directoryPath = path.join(nodeModulesDirectoryPath, contents[i], "node_modules"); if (fs.existsSync(directoryPath)) { diff --git a/test/npm-support.ts b/test/npm-support.ts index ff5d00cda6..24888880a0 100644 --- a/test/npm-support.ts +++ b/test/npm-support.ts @@ -88,15 +88,10 @@ function createProject(testInjector: IInjector, dependencies?: any): string { return tempFolder; } -describe("Npm support tests", () => { - it("Ensures that the installed dependencies are prepared correctly", () => { - let dependencies = { - "bplist": "0.0.4", - "lodash": "3.9.3" - }; - +function setupProject(): IFuture { + return (() => { let testInjector = createTestInjector(); - let projectFolder = createProject(testInjector, dependencies); + let projectFolder = createProject(testInjector); let fs = testInjector.resolve("fs"); @@ -112,7 +107,6 @@ describe("Npm support tests", () => { let androidFolderPath = path.join(projectFolder, "platforms", "android"); fs.ensureDirectoryExists(androidFolderPath).wait(); - // Mock platform data let appDestinationFolderPath = path.join(androidFolderPath, "assets"); let platformsData = testInjector.resolve("platformsData"); @@ -129,8 +123,45 @@ describe("Npm support tests", () => { } }; - let platformService = testInjector.resolve("platformService"); - platformService.preparePlatform("android").wait(); + return { + testInjector: testInjector, + projectFolder: projectFolder, + appDestinationFolderPath: appDestinationFolderPath, + }; + }).future()(); +} + +function addDependencies(testInjector: IInjector, projectFolder: string, dependencies: any): IFuture { + return (() => { + let fs = testInjector.resolve("fs"); + let packageJsonPath = path.join(projectFolder, "package.json"); + let packageJsonData = fs.readJson(packageJsonPath).wait(); + + let currentDependencies = packageJsonData.dependencies; + _.extend(currentDependencies, dependencies); + fs.writeJson(packageJsonPath, packageJsonData).wait(); + }).future()(); +} + +function preparePlatform(testInjector: IInjector): IFuture { + let platformService = testInjector.resolve("platformService"); + return platformService.preparePlatform("android"); +} + +describe("Npm support tests", () => { + let testInjector: IInjector, projectFolder: string, appDestinationFolderPath: string; + before(() => { + let projectSetup = setupProject().wait(); + testInjector = projectSetup.testInjector; + projectFolder = projectSetup.projectFolder; + appDestinationFolderPath = projectSetup.appDestinationFolderPath; + }); + it("Ensures that the installed dependencies are prepared correctly", () => { + // Setup + addDependencies(testInjector, projectFolder, {"bplist": "0.0.4"}).wait(); + + // Act + preparePlatform(testInjector).wait(); // Assert let tnsModulesFolderPath = path.join(appDestinationFolderPath, "app", "tns_modules"); @@ -139,6 +170,7 @@ describe("Npm support tests", () => { let bplistCreatorFolderPath = path.join(tnsModulesFolderPath, "bplist-creator"); let bplistParserFolderPath = path.join(tnsModulesFolderPath, "bplist-parser"); + let fs = testInjector.resolve("fs"); assert.isTrue(fs.exists(lodashFolderPath).wait()); assert.isTrue(fs.exists(bplistFolderPath).wait()); assert.isTrue(fs.exists(bplistCreatorFolderPath).wait()); From 30ed33f9da3f2acc6fcd453ea291e5006bc3c232 Mon Sep 17 00:00:00 2001 From: Fatme Date: Mon, 29 Jun 2015 19:54:17 +0300 Subject: [PATCH 08/17] Do not init new project when install command is executed --- lib/commands/install.ts | 74 ++++++----------------------------------- 1 file changed, 10 insertions(+), 64 deletions(-) diff --git a/lib/commands/install.ts b/lib/commands/install.ts index 6a718077be..bb2b0668fa 100644 --- a/lib/commands/install.ts +++ b/lib/commands/install.ts @@ -1,17 +1,13 @@ /// "use strict"; -import path = require("path"); - export class InstallCommand implements ICommand { private _projectData: any; - constructor(private $fs: IFileSystem, - private $errors: IErrors, - private $logger: ILogger, - private $options: IOptions, - private $injector: IInjector, - private $staticConfig: IStaticConfig) { } + constructor(private $platformsData: IPlatformsData, + private $platformService: IPlatformService, + private $projectData: IProjectData, + private $projectDataService: IProjectDataService) { } public enableHooks = false; @@ -19,66 +15,16 @@ export class InstallCommand implements ICommand { public execute(args: string[]): IFuture { return (() => { - let projectFilePath = this.getProjectFilePath(args[0]); - let projectData = this.getProjectData(projectFilePath).wait(); - let projectName = projectData.id.split(".")[2]; - - this.$injector.resolve("projectService").createProject(projectName).wait(); - - this.$options.path = path.join(this.$options.path || path.resolve("."), projectName); + this.$projectDataService.initialize(this.$projectData.projectDir); - this.$logger.info("Adding platforms..."); - - let $platformsData = this.$injector.resolve("platformsData"); - let $platformService = this.$injector.resolve("platformService"); - _.each($platformsData.platformsNames, platform => { - let platformData = $platformsData.getPlatformData(platform); - let frameworkPackageData = projectData[platformData.frameworkPackageName]; + _.each(this.$platformsData.platformsNames, platform => { + let platformData = this.$platformsData.getPlatformData(platform); + let frameworkPackageData = this.$projectDataService.getValue(platformData.frameworkPackageName).wait(); if(frameworkPackageData && frameworkPackageData.version) { - $platformService.addPlatforms([`${platform}@${frameworkPackageData.version}`]).wait(); + this.$platformService.addPlatforms([`${platform}@${frameworkPackageData.version}`]).wait(); } - }); - + }); }).future()(); } - - public canExecute(args: string[]): IFuture { - return (() => { - let projectFilePath = this.getProjectFilePath(args[0]); - let errorMessage = args[0] ? "The provided path doesn't contain package.json." : - "The current directory doesn't contain package.json file. Execute the command in directory which contains package.json file or specify the path to package.json file."; - - if(!this.$fs.exists(projectFilePath).wait()) { - this.$errors.failWithoutHelp(errorMessage); - } - - let projectData = this.getProjectData(projectFilePath).wait(); - if(!projectData) { - this.$errors.failWithoutHelp("Invalid project file. Verify that the specified package.json file contains a nativescript key and try again."); - } - - if(!projectData.id) { - this.$errors.failWithoutHelp("Invalid project file. Verify that the specified package.json file contains an id and try again."); - } - - return true; - }).future()(); - } - - private getProjectFilePath(providedPath: string): string { - let resolvedPath = path.resolve(providedPath || "."); - return path.basename(resolvedPath) === "package.json" ? resolvedPath : path.join(resolvedPath, "package.json"); - } - - private getProjectData(projectFilePath: string): IFuture { - return (() => { - if(!this._projectData) { - let fileContent = this.$fs.readJson(projectFilePath).wait(); - this._projectData = fileContent[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE]; - } - - return this._projectData; - }).future()(); - } } $injector.registerCommand("install", InstallCommand); \ No newline at end of file From fcc8fc6c0f63a9b7093b9fa7c67d2fce6b595635 Mon Sep 17 00:00:00 2001 From: Fatme Date: Mon, 29 Jun 2015 19:48:52 +0300 Subject: [PATCH 09/17] Doesn't handle the dependencies of devDependencies --- lib/tools/broccoli/node-modules-dest-copy.ts | 66 ++++++++++---------- test/npm-support.ts | 61 +++++++++++++++++- 2 files changed, 94 insertions(+), 33 deletions(-) diff --git a/lib/tools/broccoli/node-modules-dest-copy.ts b/lib/tools/broccoli/node-modules-dest-copy.ts index 9f33ee8e8a..45941440ec 100644 --- a/lib/tools/broccoli/node-modules-dest-copy.ts +++ b/lib/tools/broccoli/node-modules-dest-copy.ts @@ -30,41 +30,43 @@ export class DestCopy implements IBroccoliPlugin { public rebuildChangedDirectories(changedDirectories: string[], platform: string): void { _.each(changedDirectories, changedDirectoryAbsolutePath => { - let pathToPackageJson = path.join(changedDirectoryAbsolutePath, "package.json"); - let packageJsonFiles = fs.existsSync(pathToPackageJson) ? [pathToPackageJson] : []; - let nodeModulesFolderPath = path.join(changedDirectoryAbsolutePath, "node_modules"); - packageJsonFiles = packageJsonFiles.concat(this.enumeratePackageJsonFilesSync(nodeModulesFolderPath)); - - _.each(packageJsonFiles, packageJsonFilePath => { - let fileContent = require(packageJsonFilePath); - let isPlugin = fileContent.nativescript; - - if(!this.devDependencies[fileContent.name]) { // Don't flatten dev dependencies - - let currentDependency = { - name: fileContent.name, - version: fileContent.version, - directory: path.dirname(packageJsonFilePath), - isPlugin: isPlugin - }; + if(!this.devDependencies[path.basename(changedDirectoryAbsolutePath)]) { + let pathToPackageJson = path.join(changedDirectoryAbsolutePath, constants.PACKAGE_JSON_FILE_NAME); + let packageJsonFiles = fs.existsSync(pathToPackageJson) ? [pathToPackageJson] : []; + let nodeModulesFolderPath = path.join(changedDirectoryAbsolutePath, constants.NODE_MODULES_FOLDER_NAME); + packageJsonFiles = packageJsonFiles.concat(this.enumeratePackageJsonFilesSync(nodeModulesFolderPath)); - let addedDependency = this.dependencies[currentDependency.name]; - if (addedDependency) { - if (semver.gt(currentDependency.version, addedDependency.version)) { - let currentDependencyMajorVersion = semver.major(currentDependency.version); - let addedDependencyMajorVersion = semver.major(addedDependency.version); - - let message = `The depedency located at ${addedDependency.directory} with version ${addedDependency.version} will be replaced with dependency located at ${currentDependency.directory} with version ${currentDependency.version}`; - let logger = $injector.resolve("$logger"); - currentDependencyMajorVersion === addedDependencyMajorVersion ? logger.out(message) : logger.warn(message); + _.each(packageJsonFiles, packageJsonFilePath => { + let fileContent = require(packageJsonFilePath); + if(!this.devDependencies[fileContent.name]) { // Don't flatten dev dependencies + let isPlugin = fileContent.nativescript; + + let currentDependency = { + name: fileContent.name, + version: fileContent.version, + directory: path.dirname(packageJsonFilePath), + isPlugin: isPlugin + }; + + let addedDependency = this.dependencies[currentDependency.name]; + if (addedDependency) { + if (semver.gt(currentDependency.version, addedDependency.version)) { + let currentDependencyMajorVersion = semver.major(currentDependency.version); + let addedDependencyMajorVersion = semver.major(addedDependency.version); + + let message = `The depedency located at ${addedDependency.directory} with version ${addedDependency.version} will be replaced with dependency located at ${currentDependency.directory} with version ${currentDependency.version}`; + let logger = $injector.resolve("$logger"); + currentDependencyMajorVersion === addedDependencyMajorVersion ? logger.out(message) : logger.warn(message); + + this.dependencies[currentDependency.name] = currentDependency; + } + } else { this.dependencies[currentDependency.name] = currentDependency; } - } else { - this.dependencies[currentDependency.name] = currentDependency; } - } - }); + }); + } }); _.each(this.dependencies, dependency => { @@ -98,12 +100,12 @@ export class DestCopy implements IBroccoliPlugin { if(fs.existsSync(nodeModulesDirectoryPath)) { let contents = fs.readdirSync(nodeModulesDirectoryPath); for (let i = 0; i < contents.length; ++i) { - let packageJsonFilePath = path.join(nodeModulesDirectoryPath, contents[i], "package.json"); + let packageJsonFilePath = path.join(nodeModulesDirectoryPath, contents[i], constants.PACKAGE_JSON_FILE_NAME); if (fs.existsSync(packageJsonFilePath)) { foundFiles.push(packageJsonFilePath); } - var directoryPath = path.join(nodeModulesDirectoryPath, contents[i], "node_modules"); + var directoryPath = path.join(nodeModulesDirectoryPath, contents[i], constants.NODE_MODULES_FOLDER_NAME); if (fs.existsSync(directoryPath)) { this.enumeratePackageJsonFilesSync(directoryPath, foundFiles); } diff --git a/test/npm-support.ts b/test/npm-support.ts index 24888880a0..fff3f12ca7 100644 --- a/test/npm-support.ts +++ b/test/npm-support.ts @@ -83,6 +83,7 @@ function createProject(testInjector: IInjector, dependencies?: any): string { } }; packageJsonData["dependencies"] = dependencies; + packageJsonData["devDependencies"] = {}; testInjector.resolve("fs").writeJson(path.join(tempFolder, "package.json"), packageJsonData).wait(); return tempFolder; @@ -131,7 +132,7 @@ function setupProject(): IFuture { }).future()(); } -function addDependencies(testInjector: IInjector, projectFolder: string, dependencies: any): IFuture { +function addDependencies(testInjector: IInjector, projectFolder: string, dependencies: any, devDependencies?: any): IFuture { return (() => { let fs = testInjector.resolve("fs"); let packageJsonPath = path.join(projectFolder, "package.json"); @@ -139,6 +140,11 @@ function addDependencies(testInjector: IInjector, projectFolder: string, depende let currentDependencies = packageJsonData.dependencies; _.extend(currentDependencies, dependencies); + + if(devDependencies) { + let currentDevDependencies = packageJsonData.devDependencies; + _.extend(currentDevDependencies, devDependencies); + } fs.writeJson(packageJsonPath, packageJsonData).wait(); }).future()(); } @@ -176,4 +182,57 @@ describe("Npm support tests", () => { assert.isTrue(fs.exists(bplistCreatorFolderPath).wait()); assert.isTrue(fs.exists(bplistParserFolderPath).wait()); }); +}); + +describe("Flatten npm modules tests", () => { + it("Doesn't handle the dependencies of devDependencies", () => { + let projectSetup = setupProject().wait(); + let testInjector = projectSetup.testInjector; + let projectFolder = projectSetup.projectFolder; + let appDestinationFolderPath = projectSetup.appDestinationFolderPath; + + let devDependencies = { + "gulp": "3.9.0", + "gulp-jscs": "1.6.0", + "gulp-jshint": "1.11.0" + }; + + addDependencies(testInjector, projectFolder, {}, devDependencies).wait(); + + preparePlatform(testInjector).wait(); + + // Assert + let fs = testInjector.resolve("fs"); + let tnsModulesFolderPath = path.join(appDestinationFolderPath, "app", "tns_modules"); + + let lodashFolderPath = path.join(tnsModulesFolderPath, "lodash"); + assert.isTrue(fs.exists(lodashFolderPath).wait()); + + let gulpFolderPath = path.join(tnsModulesFolderPath, "gulp"); + assert.isFalse(fs.exists(gulpFolderPath).wait()); + + let gulpJscsFolderPath = path.join(tnsModulesFolderPath, "gulp-jscs"); + assert.isFalse(fs.exists(gulpJscsFolderPath).wait()); + + let gulpJshint = path.join(tnsModulesFolderPath, "gulp-jshint"); + assert.isFalse(fs.exists(gulpJshint).wait()); + + // Get all gulp dependencies + let gulpDependencies = fs.readDirectory(path.join(projectFolder, "node_modules", "gulp", "node_modules")).wait(); + _.each(gulpDependencies, dependency => { + assert.isFalse(fs.exists(path.join(tnsModulesFolderPath, dependency)).wait()); + }); + + // Get all gulp-jscs dependencies + let gulpJscsDependencies = fs.readDirectory(path.join(projectFolder, "node_modules", "gulp-jscs", "node_modules")).wait(); + _.each(gulpJscsDependencies, dependency => { + assert.isFalse(fs.exists(path.join(tnsModulesFolderPath, dependency)).wait()); + }); + + // Get all gulp-jshint dependencies + let gulpJshintDependencies = fs.readDirectory(path.join(projectFolder, "node_modules", "gulp-jshint", "node_modules")).wait(); + _.each(gulpJshintDependencies, dependency => { + assert.isFalse(fs.exists(path.join(tnsModulesFolderPath, dependency)).wait()); + }); + }); }); \ No newline at end of file From 501b6d3c6a6d6106f60a6dbacfc3f3a2a345f4d0 Mon Sep 17 00:00:00 2001 From: Fatme Havaluova Date: Wed, 1 Jul 2015 13:06:45 +0300 Subject: [PATCH 10/17] Bump version to 1.1.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c6919fbd43..951f7eec9d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "nativescript", "preferGlobal": true, - "version": "1.1.1", + "version": "1.1.2", "author": "Telerik ", "description": "Command-line interface for building NativeScript projects", "bin": { From 92392315c06c1a39b62ba3bc9b464ac8b95c963f Mon Sep 17 00:00:00 2001 From: Fatme Havaluova Date: Fri, 26 Jun 2015 12:00:57 +0300 Subject: [PATCH 11/17] Help for install command --- docs/man_pages/index.md | 3 ++- .../project/configuration/install.md | 27 +++++++++++++++++++ .../project/configuration/platform-add.md | 1 + .../project/configuration/platform-remove.md | 1 + .../project/configuration/platform-update.md | 1 + .../project/configuration/platform.md | 1 + .../project/configuration/prepare.md | 1 + 7 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 docs/man_pages/project/configuration/install.md diff --git a/docs/man_pages/index.md b/docs/man_pages/index.md index 50d4c7b827..3cbf625ba6 100644 --- a/docs/man_pages/index.md +++ b/docs/man_pages/index.md @@ -28,7 +28,8 @@ Command | Description [emulate ``](project/testing/emulate.html) | Deploys the project in the native emulator for the selected target platform. [run ``](project/testing/run.html) | Runs your project on a connected device or in the native emulator, if configured. [debug ``](project/testing/debug.html) | Debugs your project on a connected device. -[plugin](plugin.html) | Lists all installed plugins for your project or lets you manage the plugins for your project. +[install](project/configuration/install.html) | Installs all platforms and dependencies described in the `package.json` file in the current directory. +[plugin](lib-management/plugin.html) | Lets you manage the plugins for your project. ## Device Commands Command | Description diff --git a/docs/man_pages/project/configuration/install.md b/docs/man_pages/project/configuration/install.md new file mode 100644 index 0000000000..71adca50ee --- /dev/null +++ b/docs/man_pages/project/configuration/install.md @@ -0,0 +1,27 @@ +install +========== + +Usage | Synopsis +---|--- +General | `$ tns install [--path]` + +Installs all platforms and dependencies described in the `package.json` file in the current directory. + +<% if(isHtml) { %> +The `package.json` file must be a valid `package.json` describing the configuration of a NativeScript project. If missing or corrupted, you can recreate the file by running `$ tns init` in the directory of a NativeScript project. +<% } %> + +### Options +* `--path` - Specifies the directory where you want to create the project, if different from the current directory. The directory must be empty. + +<% if(isHtml) { %> +### Related Commands + +Command | Description +----------|---------- +[platform add](platform-add.html) | Configures the current project to target the selected platform. +[platform remove](platform-remove.html) | Removes the selected platform from the platforms that the project currently targets. +[platform update](platform-update.html) | Updates the NativeScript runtime for the specified platform. +[platform](platform.html) | Lists all platforms that the project currently targets. +[prepare](prepare.html) | Copies common and relevant platform-specific content from the app directory to the subdirectory for the selected target platform in the platforms directory. +<% } %> \ No newline at end of file diff --git a/docs/man_pages/project/configuration/platform-add.md b/docs/man_pages/project/configuration/platform-add.md index 6f728cc8c6..45cc27b025 100644 --- a/docs/man_pages/project/configuration/platform-add.md +++ b/docs/man_pages/project/configuration/platform-add.md @@ -31,6 +31,7 @@ To list only experimental versions for ios, run `$ npm view tns-ios dist-tags` Command | Description ----------|---------- +[install](install.html) | Installs all platforms and dependencies described in the `package.json` file in the current directory. [platform remove](platform-remove.html) | Removes the selected platform from the platforms that the project currently targets. [platform update](platform-update.html) | Updates the NativeScript runtime for the specified platform. [platform](platform.html) | Lists all platforms that the project currently targets. diff --git a/docs/man_pages/project/configuration/platform-remove.md b/docs/man_pages/project/configuration/platform-remove.md index 99d9a6b4a3..021f43e612 100644 --- a/docs/man_pages/project/configuration/platform-remove.md +++ b/docs/man_pages/project/configuration/platform-remove.md @@ -24,6 +24,7 @@ Removes the selected platform from the platforms that the project currently targ Command | Description ----------|---------- +[install](install.html) | Installs all platforms and dependencies described in the `package.json` file in the current directory. [platform add](platform-add.html) | Configures the current project to target the selected platform. [platform update](platform-update.html) | Updates the NativeScript runtime for the specified platform. [platform](platform.html) | Lists all platforms that the project currently targets. diff --git a/docs/man_pages/project/configuration/platform-update.md b/docs/man_pages/project/configuration/platform-update.md index ef43d2c102..79381aca70 100644 --- a/docs/man_pages/project/configuration/platform-update.md +++ b/docs/man_pages/project/configuration/platform-update.md @@ -29,6 +29,7 @@ To list only experimental versions for ios, run `$ npm view tns-ios dist-tags` Command | Description ----------|---------- +[install](install.html) | Installs all platforms and dependencies described in the `package.json` file in the current directory. [platform add](platform-add.html) | Configures the current project to target the selected platform. [platform remove](platform-remove.html) | Removes the selected platform from the platforms that the project currently targets. [platform](platform.html) | Lists all platforms that the project currently targets. diff --git a/docs/man_pages/project/configuration/platform.md b/docs/man_pages/project/configuration/platform.md index ddb6d4730d..0412312f95 100644 --- a/docs/man_pages/project/configuration/platform.md +++ b/docs/man_pages/project/configuration/platform.md @@ -12,6 +12,7 @@ Lists all platforms that the project currently targets. You can build and deploy Command | Description ----------|---------- +[install](install.html) | Installs all platforms and dependencies described in the `package.json` file in the current directory. [platform add](platform-add.html) | Configures the current project to target the selected platform. [platform remove](platform-remove.html) | Removes the selected platform from the platforms that the project currently targets. [platform update](platform-update.html) | Updates the NativeScript runtime for the specified platform. diff --git a/docs/man_pages/project/configuration/prepare.md b/docs/man_pages/project/configuration/prepare.md index bfca2d296c..ad076725d1 100644 --- a/docs/man_pages/project/configuration/prepare.md +++ b/docs/man_pages/project/configuration/prepare.md @@ -23,6 +23,7 @@ in the `platforms` directory. This lets you build the project with the SDK for t Command | Description ----------|---------- +[install](install.html) | Installs all platforms and dependencies described in the `package.json` file in the current directory. [platform add](platform-add.html) | Configures the current project to target the selected platform. [platform remove](platform-remove.html) | Removes the selected platform from the platforms that the project currently targets. [platform update](platform-update.html) | Updates the NativeScript runtime for the specified platform. From c00be109200fb52a34a6560e7713d0a4d77555ad Mon Sep 17 00:00:00 2001 From: Fatme Date: Mon, 29 Jun 2015 20:26:31 +0300 Subject: [PATCH 12/17] Init command --- docs/man_pages/project/creation/create.md | 11 +- docs/man_pages/project/creation/init.md | 21 ++++ lib/bootstrap.ts | 3 + lib/commands/init.ts | 16 +++ lib/common | 2 +- lib/constants.ts | 1 + lib/declarations.ts | 6 ++ lib/options.ts | 2 + lib/services/init-service.ts | 120 ++++++++++++++++++++++ lib/services/project-service.ts | 3 +- 10 files changed, 181 insertions(+), 4 deletions(-) create mode 100644 docs/man_pages/project/creation/init.md create mode 100644 lib/commands/init.ts create mode 100644 lib/services/init-service.ts diff --git a/docs/man_pages/project/creation/create.md b/docs/man_pages/project/creation/create.md index 152788c977..39e8c71bf0 100644 --- a/docs/man_pages/project/creation/create.md +++ b/docs/man_pages/project/creation/create.md @@ -16,4 +16,13 @@ Creates a new project for native development with NativeScript from the default * `` is the name of project. The specified name must meet the requirements of all platforms that you want to target. <% if(isConsole) { %>For more information about the `` requirements, run `$ tns help create`<% } %><% if(isHtml) { %>For projects that target Android, you can use uppercase or lowercase letters, numbers, and underscores. The name must start with a letter. For projects that target iOS, you can use uppercase or lowercase letters, numbers, and hyphens.<% } %> * `` is the application identifier for your project. It must be a domain name in reverse and must meet the requirements of all platforms that you want to target. If not specified, the application identifier is set to `org.nativescript.` <% if(isConsole) { %>For more information about the `` requirements, run `$ tns help create`<% } %><% if(isHtml) { %>For projects that target Android, you can use uppercase or lowercase letters, numbers, and underscores in the strings of the reversed domain name, separated by a dot. Strings must be separated by a dot and must start with a letter. For example: `com.nativescript.My_Andro1d_App` -For projects that target iOS, you can use uppercase or lowercase letters, numbers, and hyphens in the strings of the reversed domain name. Strings must be separated by a dot. For example: `com.nativescript.My-i0s-App`.<% } %> \ No newline at end of file +For projects that target iOS, you can use uppercase or lowercase letters, numbers, and hyphens in the strings of the reversed domain name. Strings must be separated by a dot. For example: `com.nativescript.My-i0s-App`.<% } %> + +<% if(isHtml) { %> +### Related Commands + +Command | Description +----------|---------- +[init](init.html) | Initializes a project for development. The command prompts you to provide your project configuration interactively and uses the information to create a new package.json file or update the existing one. +[install](install.html) | Installs all platforms and dependencies described in the `package.json` file in the current directory. +<% } %> \ No newline at end of file diff --git a/docs/man_pages/project/creation/init.md b/docs/man_pages/project/creation/init.md new file mode 100644 index 0000000000..1e05429b4e --- /dev/null +++ b/docs/man_pages/project/creation/init.md @@ -0,0 +1,21 @@ +init +========== + +Usage | Synopsis +---|--- +General | `$ tns init [--path ] [--force]` + +Initializes a project for development. The command prompts you to provide your project configuration interactively and uses the information to create a new `package.json` file or update the existing one. + +### Options +* `--path` - Specifies the directory where you want to initialize the project, if different from the current directory. The directory must be empty. +* `--force` - If set, applies the default project configuration and does not show the interactive prompt. The default project configuration targets the latest official runtimes and sets `org.nativescript.` for application identifier. + +<% if(isHtml) { %> +### Related Commands + +Command | Description +----------|---------- +[create](create.html) | Creates a new project for native development with NativeScript from the default template or from an existing NativeScript project. +[install](install.html) | Installs all platforms and dependencies described in the `package.json` file in the current directory. +<% } %> \ No newline at end of file diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 81657d844f..251c7b8cf3 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -61,4 +61,7 @@ $injector.requireCommand("plugin|add", "./commands/plugin/add-plugin"); $injector.requireCommand("plugin|remove", "./commands/plugin/remove-plugin"); $injector.requireCommand("install", "./commands/install"); +$injector.require("initService", "./services/init-service"); +$injector.requireCommand("init", "./commands/init"); + $injector.require("projectFilesManager", "./services/project-files-manager"); diff --git a/lib/commands/init.ts b/lib/commands/init.ts new file mode 100644 index 0000000000..52a2098f71 --- /dev/null +++ b/lib/commands/init.ts @@ -0,0 +1,16 @@ +/// +"use strict"; + +import Future = require("fibers/future"); + +export class InitCommand implements ICommand { + constructor(private $initService: IInitService) { } + + public allowedParameters: ICommandParameter[] = []; + public enableHooks = false; + + public execute(args: string[]): IFuture { + return this.$initService.initialize(); + } +} +$injector.registerCommand("init", InitCommand); \ No newline at end of file diff --git a/lib/common b/lib/common index 0e7c0aea84..c04a5ebac1 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 0e7c0aea84f2cba602bef98c7fb8ca418b63a40a +Subproject commit c04a5ebac1bf0aa7a53c6cd6bc9752ebcf9ef36d diff --git a/lib/constants.ts b/lib/constants.ts index a7c5e247e5..7d86bfbc49 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -7,6 +7,7 @@ export var NATIVESCRIPT_KEY_NAME = "nativescript"; export var NODE_MODULES_FOLDER_NAME = "node_modules"; export var PACKAGE_JSON_FILE_NAME = "package.json"; export var NODE_MODULE_CACHE_PATH_KEY_NAME = "node-modules-cache-path"; +export var DEFAULT_APP_IDENTIFIER_PREFIX = "org.nativescript"; export class ReleaseType { static MAJOR = "major"; diff --git a/lib/declarations.ts b/lib/declarations.ts index 5f7abb2e7d..e07aa0d439 100644 --- a/lib/declarations.ts +++ b/lib/declarations.ts @@ -50,6 +50,8 @@ interface IOpener { interface IOptions extends ICommonOptions { frameworkPath: string; + frameworkName: string; + frameworkVersion: string; copyFrom: string; linkTo: string; release: boolean; @@ -67,4 +69,8 @@ interface IOptions extends ICommonOptions { interface IProjectFilesManager { processPlatformSpecificFiles(directoryPath: string, platform: string, excludedDirs?: string[]): IFuture; +} + +interface IInitService { + initialize(): IFuture; } \ No newline at end of file diff --git a/lib/options.ts b/lib/options.ts index fc0b19069f..73e46c2c8d 100644 --- a/lib/options.ts +++ b/lib/options.ts @@ -13,6 +13,8 @@ export class Options extends commonOptionsLibPath.OptionsBase { $hostInfo: IHostInfo) { super({ frameworkPath: { type: OptionType.String }, + frameworkName: { type: OptionType.String }, + frameworkVersion: { type: OptionType.String }, copyFrom: { type: OptionType.String }, linkTo: { type: OptionType.String }, release: { type: OptionType.Boolean }, diff --git a/lib/services/init-service.ts b/lib/services/init-service.ts new file mode 100644 index 0000000000..e30f5963cd --- /dev/null +++ b/lib/services/init-service.ts @@ -0,0 +1,120 @@ +/// +"use strict"; + +import constants = require("./../constants"); +import helpers = require("./../common/helpers"); +import path = require("path"); +import semver = require("semver"); + +export class InitService implements IInitService { + private static MIN_SUPPORTED_FRAMEWORK_VERSIONS: IStringDictionary = { + "tns-ios": "1.1.0", + "tns-android": "1.1.0" + }; + + private _projectFilePath: string; + + constructor(private $fs: IFileSystem, + private $errors: IErrors, + private $logger: ILogger, + private $options: IOptions, + private $injector: IInjector, + private $staticConfig: IStaticConfig, + private $projectHelper: IProjectHelper, + private $prompter: IPrompter, + private $npm: INodePackageManager, + private $npmInstallationManager: INpmInstallationManager) { } + + public initialize(): IFuture { + return (() => { + let projectData: any = { }; + + if(this.$fs.exists(this.projectFilePath).wait()) { + projectData = this.$fs.readJson(this.projectFilePath).wait(); + } + + let projectDataBackup = _.extend({}, projectData); + + if(!projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE]) { + projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE] = { }; + this.$fs.writeJson(this.projectFilePath, projectData).wait(); // We need to create package.json file here in order to prevent "No project found at or above and neither was a --path specified." when resolving platformsData + } + + try { + + projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE]["id"] = this.getProjectId().wait(); + + if(this.$options.frameworkName && this.$options.frameworkVersion) { + projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE][this.$options.frameworkName] = this.buildVersionData(this.$options.frameworkVersion); + } else { + let $platformsData = this.$injector.resolve("platformsData"); + _.each($platformsData.platformsNames, platform => { + let platformData: IPlatformData = $platformsData.getPlatformData(platform); + if(!platformData.targetedOS || (platformData.targetedOS && _.contains(platformData.targetedOS, process.platform))) { + projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE][platformData.frameworkPackageName] = this.getVersionData(platformData.frameworkPackageName).wait(); + } + }); + } + + this.$fs.writeJson(this.projectFilePath, projectData).wait(); + } catch(err) { + this.$fs.writeJson(this.projectFilePath, projectDataBackup).wait(); + throw err; + } + + this.$logger.out("Project successfully initialized."); + }).future()(); + } + + private get projectFilePath(): string { + if(!this._projectFilePath) { + let projectDir = path.resolve(this.$options.path || "."); + this._projectFilePath = path.join(projectDir, constants.PACKAGE_JSON_FILE_NAME); + } + + return this._projectFilePath; + } + + private getProjectId(): IFuture { + return (() => { + if(this.$options.appid) { + return this.$options.appid; + } + + let defaultAppId = this.$projectHelper.generateDefaultAppId(path.basename(path.dirname(this.projectFilePath)), constants.DEFAULT_APP_IDENTIFIER_PREFIX); + if(this.useDefaultValue) { + return defaultAppId; + } + + return this.$prompter.getString("Id:", () => defaultAppId).wait(); + }).future()(); + } + + private getVersionData(packageName: string): IFuture { + return (() => { + let latestVersion = this.$npmInstallationManager.getLatestVersion(packageName).wait(); + if(this.useDefaultValue) { + return this.buildVersionData(latestVersion); + } + + let data = this.$npm.view(packageName, "versions").wait(); + let versions = _.filter(data[latestVersion].versions, (version: string) => semver.gte(version, InitService.MIN_SUPPORTED_FRAMEWORK_VERSIONS[packageName])); + if(versions.length === 1) { + this.$logger.info(`Only ${versions[0]} version is available for ${packageName} framework.`); + return this.buildVersionData(versions[0]); + } + let sortedVersions = versions.sort(helpers.versionCompare).reverse(); + let version = this.$prompter.promptForChoice(`${packageName} version:`, sortedVersions).wait(); + return this.buildVersionData(version); + }).future()(); + } + + private buildVersionData(version: string): IStringDictionary { + return { "version": version }; + } + + private get useDefaultValue(): boolean { + return !helpers.isInteractive() || this.$options.force; + } +} +$injector.register("initService", InitService); \ No newline at end of file diff --git a/lib/services/project-service.ts b/lib/services/project-service.ts index e27398cd8f..4aa73a2670 100644 --- a/lib/services/project-service.ts +++ b/lib/services/project-service.ts @@ -9,7 +9,6 @@ import shell = require("shelljs"); import util = require("util"); export class ProjectService implements IProjectService { - private static DEFAULT_APP_IDENTIFIER_PREFIX = "org.nativescript"; constructor(private $errors: IErrors, private $fs: IFileSystem, @@ -27,7 +26,7 @@ export class ProjectService implements IProjectService { } this.$projectNameValidator.validate(projectName); - var projectId = this.$options.appid || this.$projectHelper.generateDefaultAppId(projectName, ProjectService.DEFAULT_APP_IDENTIFIER_PREFIX); + var projectId = this.$options.appid || this.$projectHelper.generateDefaultAppId(projectName, constants.DEFAULT_APP_IDENTIFIER_PREFIX); var projectDir = path.join(path.resolve(this.$options.path || "."), projectName); this.$fs.createDirectory(projectDir).wait(); From aac6be6cb1b77c6932131eeb75bb8a7faad5d20b Mon Sep 17 00:00:00 2001 From: Fatme Havaluova Date: Wed, 1 Jul 2015 10:05:15 +0300 Subject: [PATCH 13/17] Validate platform in canExecute function --- lib/commands/prepare.ts | 4 +--- lib/commands/run.ts | 2 +- lib/services/platform-service.ts | 2 ++ 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/commands/prepare.ts b/lib/commands/prepare.ts index e96d511895..7f07f4644e 100644 --- a/lib/commands/prepare.ts +++ b/lib/commands/prepare.ts @@ -6,9 +6,7 @@ export class PrepareCommand implements ICommand { private $platformCommandParameter: ICommandParameter) { } execute(args: string[]): IFuture { - return (() => { - this.$platformService.preparePlatform(args[0]).wait(); - }).future()(); + return this.$platformService.preparePlatform(args[0]); } allowedParameters = [this.$platformCommandParameter]; diff --git a/lib/commands/run.ts b/lib/commands/run.ts index 6b81087957..7bbbb1e596 100644 --- a/lib/commands/run.ts +++ b/lib/commands/run.ts @@ -28,7 +28,7 @@ export class RunAndroidCommand extends RunCommandBase implements ICommand { private $platformsData: IPlatformsData) { super($platformService); } - + public allowedParameters: ICommandParameter[] = []; public execute(args: string[]): IFuture { diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index e47f318a92..cd56ec2a34 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -146,6 +146,8 @@ export class PlatformService implements IPlatformService { public preparePlatform(platform: string): IFuture { return (() => { + this.validatePlatform(platform); + platform = platform.toLowerCase(); var platformData = this.$platformsData.getPlatformData(platform); From e059ab59a0d919e60e05c743a64960baae55d695 Mon Sep 17 00:00:00 2001 From: Fatme Havaluova Date: Wed, 1 Jul 2015 16:39:02 +0300 Subject: [PATCH 14/17] Ensure all npm deps are installed when no platform is specified in package.json and aggregate errors --- lib/commands/install.ts | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/commands/install.ts b/lib/commands/install.ts index bb2b0668fa..e26e99aca9 100644 --- a/lib/commands/install.ts +++ b/lib/commands/install.ts @@ -7,7 +7,9 @@ export class InstallCommand implements ICommand { constructor(private $platformsData: IPlatformsData, private $platformService: IPlatformService, private $projectData: IProjectData, - private $projectDataService: IProjectDataService) { } + private $projectDataService: IProjectDataService, + private $pluginsService: IPluginsService, + private $logger: ILogger) { } public enableHooks = false; @@ -15,15 +17,27 @@ export class InstallCommand implements ICommand { public execute(args: string[]): IFuture { return (() => { - this.$projectDataService.initialize(this.$projectData.projectDir); + let error: string = ""; + this.$pluginsService.ensureAllDependenciesAreInstalled().wait(); + + this.$projectDataService.initialize(this.$projectData.projectDir); _.each(this.$platformsData.platformsNames, platform => { let platformData = this.$platformsData.getPlatformData(platform); let frameworkPackageData = this.$projectDataService.getValue(platformData.frameworkPackageName).wait(); if(frameworkPackageData && frameworkPackageData.version) { - this.$platformService.addPlatforms([`${platform}@${frameworkPackageData.version}`]).wait(); + try { + this.$platformService.addPlatforms([`${platform}@${frameworkPackageData.version}`]).wait(); + } catch(err) { + error += err; + } } }); + + if(error) { + this.$logger.error(error); + } + }).future()(); } } From 9339b840bdd6a5c6614b9c31fd7bd637be3c1834 Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Thu, 2 Jul 2015 09:44:50 +0300 Subject: [PATCH 15/17] Fix help for install command Fix help for install command, --path option description changed. --- docs/man_pages/project/configuration/install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/man_pages/project/configuration/install.md b/docs/man_pages/project/configuration/install.md index 71adca50ee..83ba723ea8 100644 --- a/docs/man_pages/project/configuration/install.md +++ b/docs/man_pages/project/configuration/install.md @@ -12,7 +12,7 @@ The `package.json` file must be a valid `package.json` describing the configurat <% } %> ### Options -* `--path` - Specifies the directory where you want to create the project, if different from the current directory. The directory must be empty. +* `--path` - Specifies the directory which contains the `package.json` file, if different from the current directory. <% if(isHtml) { %> ### Related Commands From b86ba537be7a90f4ae202fd8520a3883c689df2d Mon Sep 17 00:00:00 2001 From: Iva Koevska Date: Thu, 2 Jul 2015 10:57:20 +0300 Subject: [PATCH 16/17] Changelog for 1.1.2 --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f89ae32399..ca1e415ebb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,23 @@ NativeScript CLI Changelog ================ +1.1.2 (2015, July 2) +== + +### New + +* [Implemented #600](https://github.com/NativeScript/nativescript-cli/issues/600): Added the `$ tns init` command. You can use it to initialize a NativeScript project for development. The command recreates or updates the `package.json` file of the project. You can then run `$ tns install` to install the platforms and dependencies described in the `package.json` file. +* [Implemented #587](https://github.com/NativeScript/nativescript-cli/issues/587): Added the `$ tns install` command. You can use it to quickly install all platforms and dependencies described in a `package.json` file. + +### Fixed + +* [Fixed #606](https://github.com/NativeScript/nativescript-cli/issues/606): The NativeScript CLI lets you run ``-based commands on operating systems which do not support development for this platform. For example, you can run `$ tns build ios` on a Windows system. +* [Fixed #601](https://github.com/NativeScript/nativescript-cli/issues/601): The NativeScript CLI does not treat the dependencies of devDependencies as devDependencies. +* [Fixed #599](https://github.com/NativeScript/nativescript-cli/issues/599): The NativeScript CLI always creates a `tmp` directory in the current directory when you run any command and modifies the `package.json` file for the project. +* [Fixed #584](https://github.com/NativeScript/nativescript-cli/issues/584): The NativeScript CLI references the files in the project with their absolute paths instead of their relative paths. This might cause the project to stop working when transfered to another location and might cause issues with your application packages. +* [Fixed #578](https://github.com/NativeScript/nativescript-cli/issues/578): Platform-specific files in NativeScript plugins are not processed correctly. The NativeScript CLI copies them to the correct platform-specific directory but does not rename them correctly. +* [Fixed #520](https://github.com/NativeScript/nativescript-cli/issues/520): iOS resources from the `App_Resources` folder are not included in the native Xcode project and are not accessible in the application package. + 1.1.1 (2015, June 17) == From 0b638e79ce6bc70b82fa0fab460f5c2f278b4eb8 Mon Sep 17 00:00:00 2001 From: Fatme Havaluova Date: Thu, 2 Jul 2015 12:30:07 +0300 Subject: [PATCH 17/17] Update common lib --- lib/common | 2 +- lib/config.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/common b/lib/common index 3bb71cefcf..76ba70be6f 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 3bb71cefcf2fd3818e90f79d99ef03c6a983e59e +Subproject commit 76ba70be6f7b385bc1a30ad25f94f85c104762d3 diff --git a/lib/config.ts b/lib/config.ts index e33d9173b6..beb73ba12d 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -57,5 +57,9 @@ export class StaticConfig extends staticConfigBaseLibPath.StaticConfigBase imple public get HTML_CLI_HELPERS_DIR(): string { return path.join(__dirname, "../docs/helpers"); } + + public get pathToPackageJson(): string { + return path.join(__dirname, "..", "package.json"); + } } $injector.register("staticConfig", StaticConfig);