From 9e372321d356c901cc4c1fb66b3afb0fa093ca77 Mon Sep 17 00:00:00 2001 From: Fatme Date: Fri, 4 Jul 2014 09:39:07 +0300 Subject: [PATCH 1/7] Add bootstrap, options --- .gitignore | 6 +--- lib/bootstrap.ts | 5 +-- lib/commands/create-project-command.ts | 10 ++++++ lib/common | 2 +- lib/nativescript-cli.ts | 19 ++++++++++ lib/options.ts | 48 ++++++++++++++++++++++++++ 6 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 lib/commands/create-project-command.ts create mode 100644 lib/options.ts diff --git a/.gitignore b/.gitignore index 69dce8499d..5548e22248 100644 --- a/.gitignore +++ b/.gitignore @@ -26,12 +26,8 @@ scratch/ .idea/workspace.xml .idea/tasks.xml .idea/watcherTasks.xml - +.idea/ test-reports.xml npm-debug.log node_modules -resources/App_Resources -resources/Cordova -resources/ItemTemplates -resources/ProjectTemplates diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index e7a290a099..9ec2cace42 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -1,4 +1,5 @@ -global._ = require("underscore"); -global.$injector = require("./common/lib/yok").injector; +require("./common/bootstrap"); $injector.require("nativescript-cli", "./nativescript-cli"); + +$injector.requireCommand("create", "./commands/create-project-command"); diff --git a/lib/commands/create-project-command.ts b/lib/commands/create-project-command.ts new file mode 100644 index 0000000000..ed2b556878 --- /dev/null +++ b/lib/commands/create-project-command.ts @@ -0,0 +1,10 @@ +/// + +export class CreateProjectCommand implements ICommand { + execute(args: string[]): IFuture { + return (() => { + + }).future()(); + } +} +$injector.registerCommand("create", CreateProjectCommand); diff --git a/lib/common b/lib/common index a4294787f9..0778c0eacc 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit a4294787f9ce49b5de608ed02cfc08124594ab32 +Subproject commit 0778c0eacc68c4f606b82d3125e6b245f801f668 diff --git a/lib/nativescript-cli.ts b/lib/nativescript-cli.ts index bbb1df0206..07c23a93c0 100644 --- a/lib/nativescript-cli.ts +++ b/lib/nativescript-cli.ts @@ -1,10 +1,29 @@ /// import Fiber = require("fibers"); +import Future = require("fibers/future"); +import path = require("path"); require("./bootstrap"); +require("./options"); + +import errors = require("./common/errors"); +errors.installUncaughtExceptionListener(); + +$injector.register("config", {}); var fiber = Fiber(() => { + var commandDispatcher = $injector.resolve("commandDispatcher"); + commandDispatcher.setConfiguration({"CI_LOGGER": false}); + + if (process.argv[2] === "completion") { + commandDispatcher.completeCommand(); + } else { + commandDispatcher.dispatchCommand({}).wait(); + } + + $injector.dispose(); + Future.assertNoFutureLeftBehind(); }); global.__main_fiber__ = fiber; // leak fiber to prevent it from being GC'd and thus corrupting V8 fiber.run(); \ No newline at end of file diff --git a/lib/options.ts b/lib/options.ts new file mode 100644 index 0000000000..f586423879 --- /dev/null +++ b/lib/options.ts @@ -0,0 +1,48 @@ +/// + +import path = require("path"); + +var yargs: any = require("yargs"); + +var knownOpts:any = { + "log" : String, + "verbose" : Boolean, + "path" : String, + "version": Boolean, + "help": Boolean + }, + shorthands = { + "v" : "verbose", + "p" : "path" + }; + +Object.keys(knownOpts).forEach((opt) => { + var type = knownOpts[opt]; + if (type === String) { + yargs.string(opt); + } else if (type === Boolean) { + yargs.boolean(opt); + } +}); + +Object.keys(shorthands).forEach((key) => { + yargs.alias(key, shorthands[key]); +}); + +var parsed = yargs.argv, + defaultProfileDir = path.join(process.env.USERPROFILE || process.env.HOME || process.env.HOMEPATH, ".nativescript-cli"); + +Object.keys(parsed).forEach((opt) => { + if (knownOpts[opt] !== Boolean && typeof(parsed[opt]) === 'boolean') { + delete parsed[opt]; + } +}); + +parsed["profile-dir"] = parsed["profile-dir"] || defaultProfileDir; + +Object.keys(parsed).forEach((opt) => exports[opt] = parsed[opt]); + +exports.knownOpts = knownOpts; + +declare var exports:any; +export = exports; \ No newline at end of file From 0d76add165e8339092d9dc9e5196519f391a9af9 Mon Sep 17 00:00:00 2001 From: Fatme Date: Fri, 4 Jul 2014 16:31:19 +0300 Subject: [PATCH 2/7] Add create project command --- lib/commands/create-project-command.ts | 4 +- lib/definitions/project.d.ts | 7 ++++ lib/options.ts | 2 + lib/project-data.ts | 4 ++ lib/services/project-service.ts | 57 ++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 lib/definitions/project.d.ts create mode 100644 lib/project-data.ts create mode 100644 lib/services/project-service.ts diff --git a/lib/commands/create-project-command.ts b/lib/commands/create-project-command.ts index ed2b556878..05c7387243 100644 --- a/lib/commands/create-project-command.ts +++ b/lib/commands/create-project-command.ts @@ -1,9 +1,11 @@ /// export class CreateProjectCommand implements ICommand { + constructor(private $projectService: IProjectService) { } + execute(args: string[]): IFuture { return (() => { - + this.$projectService.createProject(args[0], args[1], JSON.parse(args[2])).wait(); }).future()(); } } diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts new file mode 100644 index 0000000000..860169ded4 --- /dev/null +++ b/lib/definitions/project.d.ts @@ -0,0 +1,7 @@ +interface IProjectService { + createProject(projectId: string, projectName: string, projectConfig?: IProjectConfig): IFuture; +} + +interface IProjectConfig { + customAppPath: string; +} \ No newline at end of file diff --git a/lib/options.ts b/lib/options.ts index f586423879..fb9ccba315 100644 --- a/lib/options.ts +++ b/lib/options.ts @@ -8,6 +8,8 @@ var knownOpts:any = { "log" : String, "verbose" : Boolean, "path" : String, + "copy-from": String, + "link-to": String, "version": Boolean, "help": Boolean }, diff --git a/lib/project-data.ts b/lib/project-data.ts new file mode 100644 index 0000000000..cc486f73eb --- /dev/null +++ b/lib/project-data.ts @@ -0,0 +1,4 @@ + +export class ProjectData { + +} \ No newline at end of file diff --git a/lib/services/project-service.ts b/lib/services/project-service.ts new file mode 100644 index 0000000000..670204c027 --- /dev/null +++ b/lib/services/project-service.ts @@ -0,0 +1,57 @@ +import path = require("path"); +import options = require("./../options"); + +export class ProjectService implements IProjectService { + private static DEFAULT_ID = "com.tns.helloNativeScript"; + private static DEFAULT_NAME = "HelloNativeScript"; + + constructor(private $logger: ILogger, + private $errors: IErrors, + private $fs: IFileSystem) { } + + public createProject(projectId: string, projectName: string, projectConfig?: IProjectConfig): IFuture { + return(() => { + var projectDir = path.resolve(options.path || "."); + + projectId = projectId || ProjectService.DEFAULT_ID; + projectName = projectName || ProjectService.DEFAULT_NAME; + projectConfig = projectConfig || {}; + + projectConfig.customAppPath = path.resolve(this.getCustomAppPath()); + + this.$logger.trace("Creating a new NativeScript project with name %s and id at location", projectName, projectId, projectDir); + + if(this.$fs.exists(projectDir).wait() && !this.isEmptyDir(projectDir)) { + this.$errors.fail("Path already exists and is not empty %s", projectDir); + } + + if(projectConfig.customAppPath) { + + } else { + // No custom app - use hello-world application + this.$logger.trace("Using NativeScript hello-world application"); + } + }).future(); + } + + private getCustomAppPath(): string { + var customAppPath = options["copy-from"] || options["link-to"]; + if(customAppPath) { + if(customAppPath.indexOf("http") >= 0) { + this.$errors.fail("Only local paths for custom app assets are supported."); + } + } + if(customAppPath.substr(0, 1) === '~') { + customAppPath = path.join(process.env.HOME, customAppPath.substr(1)); + } + + return customAppPath; + } + + private isEmptyDir(directoryPath: string): IFuture { + return(() => { + var directoryContent = this.$fs.readDirectory(directoryPath).wait(); + return directoryContent.length === 0; + }).future()(); + } +} \ No newline at end of file From 199f58a43e78e00148c26aafc422a403134211da Mon Sep 17 00:00:00 2001 From: Fatme Date: Mon, 7 Jul 2014 14:29:34 +0300 Subject: [PATCH 3/7] Add Cuteness service --- lib/bootstrap.ts | 3 ++ lib/commands/create-project-command.ts | 3 +- lib/common | 2 +- lib/definitions/project.d.ts | 6 ++- lib/definitions/shelljs.d.ts | 5 ++ lib/services/cuteness-service.ts | 69 ++++++++++++++++++++++++++ lib/services/project-service.ts | 49 +++++++++++------- package.json | 2 + 8 files changed, 118 insertions(+), 21 deletions(-) create mode 100644 lib/definitions/shelljs.d.ts create mode 100644 lib/services/cuteness-service.ts diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 9ec2cace42..2c99644e61 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -2,4 +2,7 @@ require("./common/bootstrap"); $injector.require("nativescript-cli", "./nativescript-cli"); +$injector.require("projectService", "./services/project-service"); $injector.requireCommand("create", "./commands/create-project-command"); + +$injector.require("cutenessService", "./services/cuteness-service"); diff --git a/lib/commands/create-project-command.ts b/lib/commands/create-project-command.ts index 05c7387243..65ff666bc0 100644 --- a/lib/commands/create-project-command.ts +++ b/lib/commands/create-project-command.ts @@ -5,7 +5,8 @@ export class CreateProjectCommand implements ICommand { execute(args: string[]): IFuture { return (() => { - this.$projectService.createProject(args[0], args[1], JSON.parse(args[2])).wait(); + var projectConfig = args[3] ? JSON.parse(args[3]) : {}; + this.$projectService.createProject(args[0], args[1], args[2], projectConfig).wait(); }).future()(); } } diff --git a/lib/common b/lib/common index 0778c0eacc..7c81b87f92 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 0778c0eacc68c4f606b82d3125e6b245f801f668 +Subproject commit 7c81b87f92212dee41872b3e3d301be620b62e0a diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index 860169ded4..c99c171e11 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -1,7 +1,11 @@ interface IProjectService { - createProject(projectId: string, projectName: string, projectConfig?: IProjectConfig): IFuture; + createProject(projectDir: string, projectId: string, projectName: string, projectConfig?: IProjectConfig): IFuture; } interface IProjectConfig { customAppPath: string; +} + +interface ICutenessService { + cutenessAppPath: IFuture; } \ No newline at end of file diff --git a/lib/definitions/shelljs.d.ts b/lib/definitions/shelljs.d.ts new file mode 100644 index 0000000000..99ea4dc36e --- /dev/null +++ b/lib/definitions/shelljs.d.ts @@ -0,0 +1,5 @@ +declare module "shelljs" { + function cp(arg: string, sourcePath: string, destinationPath: string): void; + function sed(arg: string, oldValue: any, newValue: string, filePath: string): void; + function mv(source: string[], destination: string); +} \ No newline at end of file diff --git a/lib/services/cuteness-service.ts b/lib/services/cuteness-service.ts new file mode 100644 index 0000000000..b366ec0851 --- /dev/null +++ b/lib/services/cuteness-service.ts @@ -0,0 +1,69 @@ +/// + +import util = require("util"); +import path = require("path"); +import shell = require("shelljs"); +var options = require("./../options"); +var helpers = require("./../common/helpers"); + +export class CutenessService implements ICutenessService { + private static GITHUB_REPOS_ENDPOINT = "https://api.github.com/orgs/Telerik/repos?per_page=100"; + private static GITHUB_CUTENESS_REFS_ENDPOINT = "https://api.github.com/repos/telerik/nativescript-sample-cuteness/git/refs"; + private static GITHUB_MASTER_REF_NAME = "refs/heads/master"; + private static GITHUB_CUTENESS_REPO_NAME = "nativescript-sample-cuteness"; + private static CUTENESS_PULL_FAILED_MESSAGE = "Failed to retrieve Cuteness application. Please try again a little bit later."; + private static CUTENESS_NAME = "Cuteness"; + + public constructor(private $httpClient: Server.IHttpClient, + private $logger: ILogger, + private $errors: IErrors, + private $fs: IFileSystem) { } + + public get cutenessAppPath(): IFuture { + return this.getCutenessAppPath(); + } + + private getCutenessAppPath(): IFuture { + return (() => { + try { + var repos = JSON.parse(this.$httpClient.httpRequest(CutenessService.GITHUB_REPOS_ENDPOINT).wait().body); + var refs = JSON.parse(this.$httpClient.httpRequest(CutenessService.GITHUB_CUTENESS_REFS_ENDPOINT).wait().body); + } catch(error) { + this.$logger.debug(error); + this.$errors.fail(CutenessService.CUTENESS_PULL_FAILED_MESSAGE); + } + + var cutenessRepo = _.find(repos, (repo: any) => { return repo.name == CutenessService.GITHUB_CUTENESS_REPO_NAME; }); + var cutenessUrl = util.format("%s/%s/%s", cutenessRepo.url, "zipball", cutenessRepo.default_branch); + + var masterRef = _.find(refs, (ref: any) => { return ref.ref == CutenessService.GITHUB_MASTER_REF_NAME}); + var latestMasterSha = masterRef.object.sha.substr(0, 7); + + var downloadDir = path.join(options["profile-dir"], CutenessService.CUTENESS_NAME); + if(!this.$fs.exists(downloadDir).wait()) { + this.$fs.createDirectory(downloadDir).wait(); + } + + var cutenessAppPath = path.join(downloadDir, util.format("telerik-%s-%s", CutenessService.GITHUB_CUTENESS_REPO_NAME, latestMasterSha)); + + if(this.$fs.exists(cutenessAppPath).wait()) { + this.$logger.trace("Cuteness app already exists. No need to download."); + } else { + this.$logger.trace("Downloading %s cuteness app", latestMasterSha); + + var file = this.$fs.createWriteStream(cutenessAppPath); + var fileEnd = this.$fs.futureFromEvent(file, "finish"); + + this.$httpClient.httpRequest({ url: cutenessUrl, pipeTo: file}).wait(); + fileEnd.wait(); + + this.$fs.unzip(cutenessAppPath, downloadDir).wait(); + + this.$logger.trace("Downloaded, unzipped and extracted %s ", cutenessAppPath); + } + + return path.join(cutenessAppPath, CutenessService.GITHUB_CUTENESS_REPO_NAME); + }).future()(); + } +} +$injector.register("cutenessService", CutenessService); \ No newline at end of file diff --git a/lib/services/project-service.ts b/lib/services/project-service.ts index 670204c027..11acda95b7 100644 --- a/lib/services/project-service.ts +++ b/lib/services/project-service.ts @@ -1,37 +1,48 @@ +/// + import path = require("path"); import options = require("./../options"); export class ProjectService implements IProjectService { - private static DEFAULT_ID = "com.tns.helloNativeScript"; - private static DEFAULT_NAME = "HelloNativeScript"; + private static DEFAULT_ID = "com.telerik.tns.Cuteness"; + private static DEFAULT_NAME = "Cuteness"; constructor(private $logger: ILogger, private $errors: IErrors, - private $fs: IFileSystem) { } + private $fs: IFileSystem, + private $cutenessService: ICutenessService) { } - public createProject(projectId: string, projectName: string, projectConfig?: IProjectConfig): IFuture { + public createProject(projectDir: string, projectId: string, projectName: string, projectConfig?: IProjectConfig): IFuture { return(() => { - var projectDir = path.resolve(options.path || "."); + if(!projectDir) { + this.$errors.fail("At least the project directory must be provided to create new project"); + } + + projectDir = projectDir || path.resolve(options.path || "."); projectId = projectId || ProjectService.DEFAULT_ID; projectName = projectName || ProjectService.DEFAULT_NAME; - projectConfig = projectConfig || {}; + projectConfig = projectConfig; - projectConfig.customAppPath = path.resolve(this.getCustomAppPath()); - - this.$logger.trace("Creating a new NativeScript project with name %s and id at location", projectName, projectId, projectDir); + var customAppPath = this.getCustomAppPath(); + if(customAppPath) { + projectConfig.customAppPath = path.resolve(customAppPath); + } - if(this.$fs.exists(projectDir).wait() && !this.isEmptyDir(projectDir)) { + if(this.$fs.exists(projectDir).wait() && !this.isEmptyDir(projectDir).wait()) { this.$errors.fail("Path already exists and is not empty %s", projectDir); } - if(projectConfig.customAppPath) { + this.$logger.trace("Creating a new NativeScript project with name %s and id at location", projectName, projectId, projectDir); + if(projectConfig.customAppPath) { + // TODO: } else { - // No custom app - use hello-world application - this.$logger.trace("Using NativeScript hello-world application"); + // No custom app - use Cuteness application + this.$logger.trace("Using NativeScript Cuteness application"); + var cutenessAppPath = this.$cutenessService.cutenessAppPath.wait(); } - }).future(); + }).future()(); } private getCustomAppPath(): string { @@ -40,9 +51,10 @@ export class ProjectService implements IProjectService { if(customAppPath.indexOf("http") >= 0) { this.$errors.fail("Only local paths for custom app assets are supported."); } - } - if(customAppPath.substr(0, 1) === '~') { - customAppPath = path.join(process.env.HOME, customAppPath.substr(1)); + + if(customAppPath.substr(0, 1) === '~') { + customAppPath = path.join(process.env.HOME, customAppPath.substr(1)); + } } return customAppPath; @@ -54,4 +66,5 @@ export class ProjectService implements IProjectService { return directoryContent.length === 0; }).future()(); } -} \ No newline at end of file +} +$injector.register("projectService", ProjectService); \ No newline at end of file diff --git a/package.json b/package.json index 7723e21b9b..fba5c7c496 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,8 @@ ], "dependencies": { "fibers": "https://github.com/icenium/node-fibers/tarball/master", + "filesize": "2.0.3", + "progress-stream": "0.5.0", "log4js": "0.6.9", "tabtab": "https://github.com/tailsu/node-tabtab/tarball/master", "underscore": "1.5.2", From cce6eadc828353f4a888de319e7a35f1930f13f0 Mon Sep 17 00:00:00 2001 From: Fatme Date: Mon, 7 Jul 2014 15:56:01 +0300 Subject: [PATCH 4/7] Implement basic project structure --- lib/commands/create-project-command.ts | 3 +- lib/definitions/project.d.ts | 8 +--- lib/services/cuteness-service.ts | 54 +++++++++++++-------- lib/services/project-service.ts | 66 +++++++++++++++++++++----- 4 files changed, 90 insertions(+), 41 deletions(-) diff --git a/lib/commands/create-project-command.ts b/lib/commands/create-project-command.ts index 65ff666bc0..4fe0df3b2c 100644 --- a/lib/commands/create-project-command.ts +++ b/lib/commands/create-project-command.ts @@ -5,8 +5,7 @@ export class CreateProjectCommand implements ICommand { execute(args: string[]): IFuture { return (() => { - var projectConfig = args[3] ? JSON.parse(args[3]) : {}; - this.$projectService.createProject(args[0], args[1], args[2], projectConfig).wait(); + this.$projectService.createProject(args[0], args[1]).wait(); }).future()(); } } diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index c99c171e11..41745a8d89 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -1,11 +1,7 @@ interface IProjectService { - createProject(projectDir: string, projectId: string, projectName: string, projectConfig?: IProjectConfig): IFuture; -} - -interface IProjectConfig { - customAppPath: string; + createProject(projectName: string, projectId: string): IFuture; } interface ICutenessService { - cutenessAppPath: IFuture; + cutenessPath: IFuture; } \ No newline at end of file diff --git a/lib/services/cuteness-service.ts b/lib/services/cuteness-service.ts index b366ec0851..e23d5ae9d2 100644 --- a/lib/services/cuteness-service.ts +++ b/lib/services/cuteness-service.ts @@ -19,15 +19,23 @@ export class CutenessService implements ICutenessService { private $errors: IErrors, private $fs: IFileSystem) { } - public get cutenessAppPath(): IFuture { - return this.getCutenessAppPath(); + public get cutenessPath(): IFuture { + return this.getCutenessPath(); } - private getCutenessAppPath(): IFuture { + private getCutenessPath(): IFuture { return (() => { + var latestMasterCommitSha = this.getLatestMasterCommitSha().wait(); + var downloadDir = path.join(options["profile-dir"], CutenessService.CUTENESS_NAME); + + var cutenessAppPath = path.join(downloadDir, util.format("telerik-%s-%s", CutenessService.GITHUB_CUTENESS_REPO_NAME, latestMasterCommitSha)); + if(this.$fs.exists(cutenessAppPath).wait()) { + this.$logger.trace("Cuteness app already exists. No need to download."); + return path.join(cutenessAppPath, CutenessService.GITHUB_CUTENESS_REPO_NAME); + } + try { var repos = JSON.parse(this.$httpClient.httpRequest(CutenessService.GITHUB_REPOS_ENDPOINT).wait().body); - var refs = JSON.parse(this.$httpClient.httpRequest(CutenessService.GITHUB_CUTENESS_REFS_ENDPOINT).wait().body); } catch(error) { this.$logger.debug(error); this.$errors.fail(CutenessService.CUTENESS_PULL_FAILED_MESSAGE); @@ -36,33 +44,39 @@ export class CutenessService implements ICutenessService { var cutenessRepo = _.find(repos, (repo: any) => { return repo.name == CutenessService.GITHUB_CUTENESS_REPO_NAME; }); var cutenessUrl = util.format("%s/%s/%s", cutenessRepo.url, "zipball", cutenessRepo.default_branch); - var masterRef = _.find(refs, (ref: any) => { return ref.ref == CutenessService.GITHUB_MASTER_REF_NAME}); - var latestMasterSha = masterRef.object.sha.substr(0, 7); - - var downloadDir = path.join(options["profile-dir"], CutenessService.CUTENESS_NAME); if(!this.$fs.exists(downloadDir).wait()) { this.$fs.createDirectory(downloadDir).wait(); } - var cutenessAppPath = path.join(downloadDir, util.format("telerik-%s-%s", CutenessService.GITHUB_CUTENESS_REPO_NAME, latestMasterSha)); + this.$logger.trace("Downloading %s cuteness app", latestMasterCommitSha); - if(this.$fs.exists(cutenessAppPath).wait()) { - this.$logger.trace("Cuteness app already exists. No need to download."); - } else { - this.$logger.trace("Downloading %s cuteness app", latestMasterSha); + var file = this.$fs.createWriteStream(cutenessAppPath); + var fileEnd = this.$fs.futureFromEvent(file, "finish"); - var file = this.$fs.createWriteStream(cutenessAppPath); - var fileEnd = this.$fs.futureFromEvent(file, "finish"); + this.$httpClient.httpRequest({ url: cutenessUrl, pipeTo: file}).wait(); + fileEnd.wait(); - this.$httpClient.httpRequest({ url: cutenessUrl, pipeTo: file}).wait(); - fileEnd.wait(); + this.$fs.unzip(cutenessAppPath, downloadDir).wait(); - this.$fs.unzip(cutenessAppPath, downloadDir).wait(); + this.$logger.trace("Downloaded, unzipped and extracted %s ", cutenessAppPath); - this.$logger.trace("Downloaded, unzipped and extracted %s ", cutenessAppPath); + return path.join(cutenessAppPath, CutenessService.GITHUB_CUTENESS_REPO_NAME); + }).future()(); + } + + private getLatestMasterCommitSha(): IFuture { + return (() => { + try { + var refs = JSON.parse(this.$httpClient.httpRequest(CutenessService.GITHUB_CUTENESS_REFS_ENDPOINT).wait().body); + } catch(error) { + this.$logger.debug(error); + this.$errors.fail(CutenessService.CUTENESS_PULL_FAILED_MESSAGE); } - return path.join(cutenessAppPath, CutenessService.GITHUB_CUTENESS_REPO_NAME); + var masterRef = _.find(refs, (ref: any) => { return ref.ref == CutenessService.GITHUB_MASTER_REF_NAME}); + var masterRefSha = masterRef.object.sha; + + return masterRefSha.toString().substr(0, 7); }).future()(); } } diff --git a/lib/services/project-service.ts b/lib/services/project-service.ts index 11acda95b7..3155ef2a2d 100644 --- a/lib/services/project-service.ts +++ b/lib/services/project-service.ts @@ -2,31 +2,31 @@ import path = require("path"); import options = require("./../options"); +import shell = require("shelljs"); export class ProjectService implements IProjectService { private static DEFAULT_ID = "com.telerik.tns.Cuteness"; - private static DEFAULT_NAME = "Cuteness"; + private static DEFAULT_NAME = "HelloNativescript"; + private static APP_FOLDER_NAME = "app"; constructor(private $logger: ILogger, private $errors: IErrors, private $fs: IFileSystem, private $cutenessService: ICutenessService) { } - public createProject(projectDir: string, projectId: string, projectName: string, projectConfig?: IProjectConfig): IFuture { + public createProject(projectName: string, projectId: string): IFuture { return(() => { - if(!projectDir) { - this.$errors.fail("At least the project directory must be provided to create new project"); - } - - projectDir = projectDir || path.resolve(options.path || "."); + var projectDir = path.resolve(options.path || "."); projectId = projectId || ProjectService.DEFAULT_ID; projectName = projectName || ProjectService.DEFAULT_NAME; - projectConfig = projectConfig; + + projectDir = path.join(projectDir, projectName); + this.$fs.createDirectory(projectDir).wait(); var customAppPath = this.getCustomAppPath(); if(customAppPath) { - projectConfig.customAppPath = path.resolve(customAppPath); + customAppPath = path.resolve(customAppPath); } if(this.$fs.exists(projectDir).wait() && !this.isEmptyDir(projectDir).wait()) { @@ -35,13 +35,53 @@ export class ProjectService implements IProjectService { this.$logger.trace("Creating a new NativeScript project with name %s and id at location", projectName, projectId, projectDir); - if(projectConfig.customAppPath) { - // TODO: + var appDirectory = path.join(projectDir, ProjectService.APP_FOLDER_NAME); + var appPath: string = null; + + if(customAppPath) { + this.$logger.trace("Using custom app from %s", customAppPath); + + // Make sure that the source app/ is not a direct ancestor of a target app/ + var relativePathFromSourceToTarget = path.relative(customAppPath, appDirectory); + var doesRelativePathGoUpAtLeastOneDir = relativePathFromSourceToTarget.split(path.sep)[0] == ".."; + if(!doesRelativePathGoUpAtLeastOneDir) { + this.$errors.fail("Project dir %s must not be created at/inside the template used to create the project %s.", projectDir, customAppPath); + } + this.$logger.trace("Copying custom app into %s", appDirectory); + appPath = customAppPath; } else { // No custom app - use Cuteness application this.$logger.trace("Using NativeScript Cuteness application"); - var cutenessAppPath = this.$cutenessService.cutenessAppPath.wait(); + var cutenessPath = this.$cutenessService.cutenessPath.wait(); + this.$logger.trace("Copying Cuteness application into %s", appDirectory); + appPath = cutenessPath; + } + + this.createProjectCore(projectDir, appPath, false).wait(); + }).future()(); + } + + private createProjectCore(projectDir: string, appPath: string, symlink?: boolean): IFuture { + return (() => { + if(!this.$fs.exists(projectDir).wait()) { + this.$fs.createDirectory(projectDir).wait(); + } + if(symlink) { + // TODO: Implement support for symlink the app folder instead of copying + } else { + var appDir = path.join(projectDir, ProjectService.APP_FOLDER_NAME); + this.$fs.createDirectory(appDir).wait(); + shell.cp('-R', path.join(appPath, "*"), appDir); } + this.createBasicProjectStructure(projectDir).wait(); + }).future()(); + } + + private createBasicProjectStructure(projectDir: string): IFuture { + return (() => { + this.$fs.createDirectory(path.join(projectDir, "platforms")).wait(); + this.$fs.createDirectory(path.join(projectDir, "tns_modules")).wait(); + this.$fs.createDirectory(path.join(projectDir, "hooks")).wait(); }).future()(); } @@ -49,7 +89,7 @@ export class ProjectService implements IProjectService { var customAppPath = options["copy-from"] || options["link-to"]; if(customAppPath) { if(customAppPath.indexOf("http") >= 0) { - this.$errors.fail("Only local paths for custom app assets are supported."); + this.$errors.fail("Only local paths for custom app are supported."); } if(customAppPath.substr(0, 1) === '~') { From a935cff21a546630031dc94b55bf588dfb2a857c Mon Sep 17 00:00:00 2001 From: Fatme Date: Tue, 8 Jul 2014 11:44:22 +0300 Subject: [PATCH 5/7] Rename CutenessService to ProjectTemplatesService --- .gitignore | 3 - lib/bootstrap.ts | 2 +- lib/definitions/project.d.ts | 4 +- lib/services/cuteness-service.ts | 83 ----------------------- lib/services/project-service.ts | 6 +- lib/services/project-templates-service.ts | 83 +++++++++++++++++++++++ 6 files changed, 89 insertions(+), 92 deletions(-) delete mode 100644 lib/services/cuteness-service.ts create mode 100644 lib/services/project-templates-service.ts diff --git a/.gitignore b/.gitignore index 5548e22248..73461007f0 100644 --- a/.gitignore +++ b/.gitignore @@ -23,9 +23,6 @@ pids logs results scratch/ -.idea/workspace.xml -.idea/tasks.xml -.idea/watcherTasks.xml .idea/ test-reports.xml diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 2c99644e61..e0562f2304 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -5,4 +5,4 @@ $injector.require("nativescript-cli", "./nativescript-cli"); $injector.require("projectService", "./services/project-service"); $injector.requireCommand("create", "./commands/create-project-command"); -$injector.require("cutenessService", "./services/cuteness-service"); +$injector.require("projectTemplatesService", "./services/project-templates-service"); diff --git a/lib/definitions/project.d.ts b/lib/definitions/project.d.ts index 41745a8d89..20934af393 100644 --- a/lib/definitions/project.d.ts +++ b/lib/definitions/project.d.ts @@ -2,6 +2,6 @@ interface IProjectService { createProject(projectName: string, projectId: string): IFuture; } -interface ICutenessService { - cutenessPath: IFuture; +interface IProjectTemplatesService { + defaultTemplatePath: IFuture; } \ No newline at end of file diff --git a/lib/services/cuteness-service.ts b/lib/services/cuteness-service.ts deleted file mode 100644 index e23d5ae9d2..0000000000 --- a/lib/services/cuteness-service.ts +++ /dev/null @@ -1,83 +0,0 @@ -/// - -import util = require("util"); -import path = require("path"); -import shell = require("shelljs"); -var options = require("./../options"); -var helpers = require("./../common/helpers"); - -export class CutenessService implements ICutenessService { - private static GITHUB_REPOS_ENDPOINT = "https://api.github.com/orgs/Telerik/repos?per_page=100"; - private static GITHUB_CUTENESS_REFS_ENDPOINT = "https://api.github.com/repos/telerik/nativescript-sample-cuteness/git/refs"; - private static GITHUB_MASTER_REF_NAME = "refs/heads/master"; - private static GITHUB_CUTENESS_REPO_NAME = "nativescript-sample-cuteness"; - private static CUTENESS_PULL_FAILED_MESSAGE = "Failed to retrieve Cuteness application. Please try again a little bit later."; - private static CUTENESS_NAME = "Cuteness"; - - public constructor(private $httpClient: Server.IHttpClient, - private $logger: ILogger, - private $errors: IErrors, - private $fs: IFileSystem) { } - - public get cutenessPath(): IFuture { - return this.getCutenessPath(); - } - - private getCutenessPath(): IFuture { - return (() => { - var latestMasterCommitSha = this.getLatestMasterCommitSha().wait(); - var downloadDir = path.join(options["profile-dir"], CutenessService.CUTENESS_NAME); - - var cutenessAppPath = path.join(downloadDir, util.format("telerik-%s-%s", CutenessService.GITHUB_CUTENESS_REPO_NAME, latestMasterCommitSha)); - if(this.$fs.exists(cutenessAppPath).wait()) { - this.$logger.trace("Cuteness app already exists. No need to download."); - return path.join(cutenessAppPath, CutenessService.GITHUB_CUTENESS_REPO_NAME); - } - - try { - var repos = JSON.parse(this.$httpClient.httpRequest(CutenessService.GITHUB_REPOS_ENDPOINT).wait().body); - } catch(error) { - this.$logger.debug(error); - this.$errors.fail(CutenessService.CUTENESS_PULL_FAILED_MESSAGE); - } - - var cutenessRepo = _.find(repos, (repo: any) => { return repo.name == CutenessService.GITHUB_CUTENESS_REPO_NAME; }); - var cutenessUrl = util.format("%s/%s/%s", cutenessRepo.url, "zipball", cutenessRepo.default_branch); - - if(!this.$fs.exists(downloadDir).wait()) { - this.$fs.createDirectory(downloadDir).wait(); - } - - this.$logger.trace("Downloading %s cuteness app", latestMasterCommitSha); - - var file = this.$fs.createWriteStream(cutenessAppPath); - var fileEnd = this.$fs.futureFromEvent(file, "finish"); - - this.$httpClient.httpRequest({ url: cutenessUrl, pipeTo: file}).wait(); - fileEnd.wait(); - - this.$fs.unzip(cutenessAppPath, downloadDir).wait(); - - this.$logger.trace("Downloaded, unzipped and extracted %s ", cutenessAppPath); - - return path.join(cutenessAppPath, CutenessService.GITHUB_CUTENESS_REPO_NAME); - }).future()(); - } - - private getLatestMasterCommitSha(): IFuture { - return (() => { - try { - var refs = JSON.parse(this.$httpClient.httpRequest(CutenessService.GITHUB_CUTENESS_REFS_ENDPOINT).wait().body); - } catch(error) { - this.$logger.debug(error); - this.$errors.fail(CutenessService.CUTENESS_PULL_FAILED_MESSAGE); - } - - var masterRef = _.find(refs, (ref: any) => { return ref.ref == CutenessService.GITHUB_MASTER_REF_NAME}); - var masterRefSha = masterRef.object.sha; - - return masterRefSha.toString().substr(0, 7); - }).future()(); - } -} -$injector.register("cutenessService", CutenessService); \ No newline at end of file diff --git a/lib/services/project-service.ts b/lib/services/project-service.ts index 3155ef2a2d..474f1a88dd 100644 --- a/lib/services/project-service.ts +++ b/lib/services/project-service.ts @@ -12,7 +12,7 @@ export class ProjectService implements IProjectService { constructor(private $logger: ILogger, private $errors: IErrors, private $fs: IFileSystem, - private $cutenessService: ICutenessService) { } + private $projectTemplatesService: IProjectTemplatesService) { } public createProject(projectName: string, projectId: string): IFuture { return(() => { @@ -52,9 +52,9 @@ export class ProjectService implements IProjectService { } else { // No custom app - use Cuteness application this.$logger.trace("Using NativeScript Cuteness application"); - var cutenessPath = this.$cutenessService.cutenessPath.wait(); + var defaultTemplatePath = this.$projectTemplatesService.defaultTemplatePath.wait(); this.$logger.trace("Copying Cuteness application into %s", appDirectory); - appPath = cutenessPath; + appPath = defaultTemplatePath; } this.createProjectCore(projectDir, appPath, false).wait(); diff --git a/lib/services/project-templates-service.ts b/lib/services/project-templates-service.ts new file mode 100644 index 0000000000..3cc15dfe3f --- /dev/null +++ b/lib/services/project-templates-service.ts @@ -0,0 +1,83 @@ +/// + +import util = require("util"); +import path = require("path"); +import shell = require("shelljs"); +var options = require("./../options"); +var helpers = require("./../common/helpers"); + +export class ProjectTemplatesService implements IProjectTemplatesService { + private static GITHUB_REPOS_ENDPOINT = "https://api.github.com/orgs/Telerik/repos?per_page=100"; + private static GITHUB_DEFAULT_TEMPLATE_REFS_ENDPOINT = "https://api.github.com/repos/telerik/nativescript-sample-cuteness/git/refs"; + private static GITHUB_MASTER_REF_NAME = "refs/heads/master"; + private static GITHUB_DEFAULT_TEMPLATE_REPO_NAME = "nativescript-sample-cuteness"; + private static DEFAULT_TEMPLATE_PULL_FAILED_MESSAGE = "Failed to retrieve Cuteness application. Please try again a little bit later."; + private static DEFAULT_TEMPLATE_NAME = "Cuteness"; + + public constructor(private $httpClient: Server.IHttpClient, + private $logger: ILogger, + private $errors: IErrors, + private $fs: IFileSystem) { } + + public get defaultTemplatePath(): IFuture { + return this.getDefaultTemplatePath(); + } + + private getDefaultTemplatePath(): IFuture { + return (() => { + var latestMasterCommitSha = this.getLatestMasterCommitSha().wait(); + var downloadDir = path.join(options["profile-dir"], ProjectTemplatesService.DEFAULT_TEMPLATE_NAME); + + var defaultTemplatePath = path.join(downloadDir, util.format("telerik-%s-%s", ProjectTemplatesService.GITHUB_DEFAULT_TEMPLATE_REPO_NAME, latestMasterCommitSha)); + if(this.$fs.exists(defaultTemplatePath).wait()) { + this.$logger.trace("Cuteness app already exists. No need to download."); + return path.join(defaultTemplatePath, ProjectTemplatesService.GITHUB_DEFAULT_TEMPLATE_REPO_NAME); + } + + try { + var repos = JSON.parse(this.$httpClient.httpRequest(ProjectTemplatesService.GITHUB_REPOS_ENDPOINT).wait().body); + } catch(error) { + this.$logger.debug(error); + this.$errors.fail(ProjectTemplatesService.DEFAULT_TEMPLATE_PULL_FAILED_MESSAGE); + } + + var defaultTemplateRepo = _.find(repos, (repo: any) => { return repo.name == ProjectTemplatesService.GITHUB_DEFAULT_TEMPLATE_REPO_NAME; }); + var defaultTemplateUrl = util.format("%s/%s/%s", defaultTemplateRepo.url, "zipball", defaultTemplateRepo.default_branch); + + if(!this.$fs.exists(downloadDir).wait()) { + this.$fs.createDirectory(downloadDir).wait(); + } + + this.$logger.trace("Downloading %s cuteness app", latestMasterCommitSha); + + var file = this.$fs.createWriteStream(defaultTemplatePath); + var fileEnd = this.$fs.futureFromEvent(file, "finish"); + + this.$httpClient.httpRequest({ url: defaultTemplateUrl, pipeTo: file}).wait(); + fileEnd.wait(); + + this.$fs.unzip(defaultTemplatePath, downloadDir).wait(); + + this.$logger.trace("Downloaded, unzipped and extracted %s ", defaultTemplatePath); + + return path.join(defaultTemplatePath, ProjectTemplatesService.GITHUB_DEFAULT_TEMPLATE_REPO_NAME); + }).future()(); + } + + private getLatestMasterCommitSha(): IFuture { + return (() => { + try { + var refs = JSON.parse(this.$httpClient.httpRequest(ProjectTemplatesService.GITHUB_DEFAULT_TEMPLATE_REFS_ENDPOINT).wait().body); + } catch(error) { + this.$logger.debug(error); + this.$errors.fail(ProjectTemplatesService.DEFAULT_TEMPLATE_PULL_FAILED_MESSAGE); + } + + var masterRef = _.find(refs, (ref: any) => { return ref.ref == ProjectTemplatesService.GITHUB_MASTER_REF_NAME}); + var masterRefSha = masterRef.object.sha; + + return masterRefSha.toString().substr(0, 7); + }).future()(); + } +} +$injector.register("projectTemplatesService", ProjectTemplatesService); \ No newline at end of file From 84c14d64413b0935d13a733aae37756d28aa47da Mon Sep 17 00:00:00 2001 From: Fatme Date: Tue, 8 Jul 2014 13:51:35 +0300 Subject: [PATCH 6/7] Changes after code review --- lib/common | 2 +- lib/definitions/osenv.d.ts | 3 +++ lib/nativescript-cli.ts | 3 +-- lib/project-data.ts | 4 ---- lib/services/project-service.ts | 3 ++- package.json | 1 + 6 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 lib/definitions/osenv.d.ts delete mode 100644 lib/project-data.ts diff --git a/lib/common b/lib/common index 7c81b87f92..591e8e6e8f 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 7c81b87f92212dee41872b3e3d301be620b62e0a +Subproject commit 591e8e6e8f94b21b41dd87971694994c12072ffb diff --git a/lib/definitions/osenv.d.ts b/lib/definitions/osenv.d.ts new file mode 100644 index 0000000000..e02de9be32 --- /dev/null +++ b/lib/definitions/osenv.d.ts @@ -0,0 +1,3 @@ +declare module "osenv" { + function home(); +} \ No newline at end of file diff --git a/lib/nativescript-cli.ts b/lib/nativescript-cli.ts index 07c23a93c0..6ca9de9979 100644 --- a/lib/nativescript-cli.ts +++ b/lib/nativescript-cli.ts @@ -10,11 +10,10 @@ require("./options"); import errors = require("./common/errors"); errors.installUncaughtExceptionListener(); -$injector.register("config", {}); +$injector.register("config", {"CI_LOGGER": false}); var fiber = Fiber(() => { var commandDispatcher = $injector.resolve("commandDispatcher"); - commandDispatcher.setConfiguration({"CI_LOGGER": false}); if (process.argv[2] === "completion") { commandDispatcher.completeCommand(); diff --git a/lib/project-data.ts b/lib/project-data.ts deleted file mode 100644 index cc486f73eb..0000000000 --- a/lib/project-data.ts +++ /dev/null @@ -1,4 +0,0 @@ - -export class ProjectData { - -} \ No newline at end of file diff --git a/lib/services/project-service.ts b/lib/services/project-service.ts index 474f1a88dd..761b503fe6 100644 --- a/lib/services/project-service.ts +++ b/lib/services/project-service.ts @@ -3,6 +3,7 @@ import path = require("path"); import options = require("./../options"); import shell = require("shelljs"); +import osenv = require("osenv"); export class ProjectService implements IProjectService { private static DEFAULT_ID = "com.telerik.tns.Cuteness"; @@ -93,7 +94,7 @@ export class ProjectService implements IProjectService { } if(customAppPath.substr(0, 1) === '~') { - customAppPath = path.join(process.env.HOME, customAppPath.substr(1)); + customAppPath = path.join(osenv.home(), customAppPath.substr(1)); } } diff --git a/package.json b/package.json index fba5c7c496..641efcd4eb 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "filesize": "2.0.3", "progress-stream": "0.5.0", "log4js": "0.6.9", + "osenv": "0.1.0", "tabtab": "https://github.com/tailsu/node-tabtab/tarball/master", "underscore": "1.5.2", "unzip": "0.1.9", From 8596e25cc65dc7bc94a204d49a0cead2f57bcd70 Mon Sep 17 00:00:00 2001 From: Fatme Date: Wed, 9 Jul 2014 10:00:02 +0300 Subject: [PATCH 7/7] Download hello world application from npm --- lib/bootstrap.ts | 4 +- lib/common | 2 +- lib/declarations.ts | 5 ++ lib/definitions/npm.d.ts | 5 ++ lib/definitions/tarball-extract.d.ts | 3 + lib/node-package-manager.ts | 36 ++++++++++++ lib/services/project-service.ts | 8 +-- lib/services/project-templates-service.ts | 70 +++++------------------ package.json | 4 +- 9 files changed, 75 insertions(+), 62 deletions(-) create mode 100644 lib/declarations.ts create mode 100644 lib/definitions/npm.d.ts create mode 100644 lib/definitions/tarball-extract.d.ts create mode 100644 lib/node-package-manager.ts diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index e0562f2304..5a9be57fce 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -3,6 +3,8 @@ require("./common/bootstrap"); $injector.require("nativescript-cli", "./nativescript-cli"); $injector.require("projectService", "./services/project-service"); +$injector.require("projectTemplatesService", "./services/project-templates-service"); + $injector.requireCommand("create", "./commands/create-project-command"); -$injector.require("projectTemplatesService", "./services/project-templates-service"); +$injector.require("nodePackageManager", "./node-package-manager"); diff --git a/lib/common b/lib/common index 591e8e6e8f..c4136d4754 160000 --- a/lib/common +++ b/lib/common @@ -1 +1 @@ -Subproject commit 591e8e6e8f94b21b41dd87971694994c12072ffb +Subproject commit c4136d4754ccdd63910fad9fe3180814083a4743 diff --git a/lib/declarations.ts b/lib/declarations.ts new file mode 100644 index 0000000000..9f63837d6e --- /dev/null +++ b/lib/declarations.ts @@ -0,0 +1,5 @@ +interface INodePackageManager { + load(config: any): IFuture; + executeCommand(command: string, arguments: string[]): IFuture; + cache: string; +} \ No newline at end of file diff --git a/lib/definitions/npm.d.ts b/lib/definitions/npm.d.ts new file mode 100644 index 0000000000..ce70940eab --- /dev/null +++ b/lib/definitions/npm.d.ts @@ -0,0 +1,5 @@ + declare module "npm" { + var cache: string; + var commands: any[]; + function load(config: Object, callback: (err: any, data: any) => void); +} \ No newline at end of file diff --git a/lib/definitions/tarball-extract.d.ts b/lib/definitions/tarball-extract.d.ts new file mode 100644 index 0000000000..950260c33e --- /dev/null +++ b/lib/definitions/tarball-extract.d.ts @@ -0,0 +1,3 @@ +declare module "tarball-extract" { + function extractTarball(sourceFile: string, destinationDir: string, callback: (err: any, data: any) => void); +} \ No newline at end of file diff --git a/lib/node-package-manager.ts b/lib/node-package-manager.ts new file mode 100644 index 0000000000..04c284df23 --- /dev/null +++ b/lib/node-package-manager.ts @@ -0,0 +1,36 @@ +/// + +import npm = require("npm"); +import Future = require("fibers/future"); +import shell = require("shelljs"); + +export class NodePackageManager implements INodePackageManager { + public get cache(): string { + return npm.cache; + } + + public load(config: any): IFuture { + var future = new Future(); + npm.load(config, (err) => { + if(err) { + future.throw(err); + } else { + future.return(); + } + }); + return future; + } + + public executeCommand(command: string, arguments: string[]): IFuture { + var future = new Future(); + npm.commands[command](arguments, (err, data) => { + if(err) { + future.throw(err); + } else { + future.return(data); + } + }); + return future; + } +} +$injector.register("nodePackageManager", NodePackageManager); \ No newline at end of file diff --git a/lib/services/project-service.ts b/lib/services/project-service.ts index 761b503fe6..884befb78a 100644 --- a/lib/services/project-service.ts +++ b/lib/services/project-service.ts @@ -6,7 +6,7 @@ import shell = require("shelljs"); import osenv = require("osenv"); export class ProjectService implements IProjectService { - private static DEFAULT_ID = "com.telerik.tns.Cuteness"; + private static DEFAULT_ID = "com.telerik.tns.HelloWorld"; private static DEFAULT_NAME = "HelloNativescript"; private static APP_FOLDER_NAME = "app"; @@ -51,10 +51,10 @@ export class ProjectService implements IProjectService { this.$logger.trace("Copying custom app into %s", appDirectory); appPath = customAppPath; } else { - // No custom app - use Cuteness application - this.$logger.trace("Using NativeScript Cuteness application"); + // No custom app - use nativescript hello world application + this.$logger.trace("Using NativeScript hello world application"); var defaultTemplatePath = this.$projectTemplatesService.defaultTemplatePath.wait(); - this.$logger.trace("Copying Cuteness application into %s", appDirectory); + this.$logger.trace("Copying Nativescript hello world application into %s", appDirectory); appPath = defaultTemplatePath; } diff --git a/lib/services/project-templates-service.ts b/lib/services/project-templates-service.ts index 3cc15dfe3f..fefa40deae 100644 --- a/lib/services/project-templates-service.ts +++ b/lib/services/project-templates-service.ts @@ -3,21 +3,19 @@ import util = require("util"); import path = require("path"); import shell = require("shelljs"); +import npm = require("npm"); var options = require("./../options"); var helpers = require("./../common/helpers"); +import Future = require("fibers/future"); export class ProjectTemplatesService implements IProjectTemplatesService { - private static GITHUB_REPOS_ENDPOINT = "https://api.github.com/orgs/Telerik/repos?per_page=100"; - private static GITHUB_DEFAULT_TEMPLATE_REFS_ENDPOINT = "https://api.github.com/repos/telerik/nativescript-sample-cuteness/git/refs"; - private static GITHUB_MASTER_REF_NAME = "refs/heads/master"; - private static GITHUB_DEFAULT_TEMPLATE_REPO_NAME = "nativescript-sample-cuteness"; - private static DEFAULT_TEMPLATE_PULL_FAILED_MESSAGE = "Failed to retrieve Cuteness application. Please try again a little bit later."; - private static DEFAULT_TEMPLATE_NAME = "Cuteness"; + private static NPM_DEFAULT_TEMPLATE_URL = "http://registry.npmjs.org/tns-template-hello-world/-/tns-template-hello-world-0.1.0.tgz"; + private static DEFAULT_TEMPLATE_DOWNLOAD_FAILED = "Failed to retrieve nativescript hello world application. Please try again a little bit later."; - public constructor(private $httpClient: Server.IHttpClient, - private $logger: ILogger, + public constructor(private $fs: IFileSystem, private $errors: IErrors, - private $fs: IFileSystem) { } + private $logger: ILogger, + private $nodePackageManager: INodePackageManager) { } public get defaultTemplatePath(): IFuture { return this.getDefaultTemplatePath(); @@ -25,58 +23,20 @@ export class ProjectTemplatesService implements IProjectTemplatesService { private getDefaultTemplatePath(): IFuture { return (() => { - var latestMasterCommitSha = this.getLatestMasterCommitSha().wait(); - var downloadDir = path.join(options["profile-dir"], ProjectTemplatesService.DEFAULT_TEMPLATE_NAME); - - var defaultTemplatePath = path.join(downloadDir, util.format("telerik-%s-%s", ProjectTemplatesService.GITHUB_DEFAULT_TEMPLATE_REPO_NAME, latestMasterCommitSha)); - if(this.$fs.exists(defaultTemplatePath).wait()) { - this.$logger.trace("Cuteness app already exists. No need to download."); - return path.join(defaultTemplatePath, ProjectTemplatesService.GITHUB_DEFAULT_TEMPLATE_REPO_NAME); - } - try { - var repos = JSON.parse(this.$httpClient.httpRequest(ProjectTemplatesService.GITHUB_REPOS_ENDPOINT).wait().body); - } catch(error) { + this.$nodePackageManager.load({"cache": path.join(options["profile-dir"], "npm_cache")}).wait(); + var info = this.$nodePackageManager.executeCommand("cache", ['add', ProjectTemplatesService.NPM_DEFAULT_TEMPLATE_URL]).wait(); + } catch (error) { this.$logger.debug(error); - this.$errors.fail(ProjectTemplatesService.DEFAULT_TEMPLATE_PULL_FAILED_MESSAGE); + this.$errors.fail(ProjectTemplatesService.DEFAULT_TEMPLATE_DOWNLOAD_FAILED); } - var defaultTemplateRepo = _.find(repos, (repo: any) => { return repo.name == ProjectTemplatesService.GITHUB_DEFAULT_TEMPLATE_REPO_NAME; }); - var defaultTemplateUrl = util.format("%s/%s/%s", defaultTemplateRepo.url, "zipball", defaultTemplateRepo.default_branch); - - if(!this.$fs.exists(downloadDir).wait()) { - this.$fs.createDirectory(downloadDir).wait(); - } - - this.$logger.trace("Downloading %s cuteness app", latestMasterCommitSha); - - var file = this.$fs.createWriteStream(defaultTemplatePath); - var fileEnd = this.$fs.futureFromEvent(file, "finish"); - - this.$httpClient.httpRequest({ url: defaultTemplateUrl, pipeTo: file}).wait(); - fileEnd.wait(); - - this.$fs.unzip(defaultTemplatePath, downloadDir).wait(); - - this.$logger.trace("Downloaded, unzipped and extracted %s ", defaultTemplatePath); - - return path.join(defaultTemplatePath, ProjectTemplatesService.GITHUB_DEFAULT_TEMPLATE_REPO_NAME); - }).future()(); - } - - private getLatestMasterCommitSha(): IFuture { - return (() => { - try { - var refs = JSON.parse(this.$httpClient.httpRequest(ProjectTemplatesService.GITHUB_DEFAULT_TEMPLATE_REFS_ENDPOINT).wait().body); - } catch(error) { - this.$logger.debug(error); - this.$errors.fail(ProjectTemplatesService.DEFAULT_TEMPLATE_PULL_FAILED_MESSAGE); - } + var packagePath = path.join(npm.cache, info.name, info.version); + var packageFileName = path.join(packagePath, "package.tgz"); - var masterRef = _.find(refs, (ref: any) => { return ref.ref == ProjectTemplatesService.GITHUB_MASTER_REF_NAME}); - var masterRefSha = masterRef.object.sha; + this.$fs.unzipTarball(packageFileName, packagePath).wait(); - return masterRefSha.toString().substr(0, 7); + return path.join(packagePath, "package"); }).future()(); } } diff --git a/package.json b/package.json index 641efcd4eb..e02d606e37 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,9 @@ "tabtab": "https://github.com/tailsu/node-tabtab/tarball/master", "underscore": "1.5.2", "unzip": "0.1.9", - "yargs": "1.2.2" + "yargs": "1.2.2", + "npm": "1.5.0-alpha-1", + "tarball-extract": "0.0.2" }, "analyze": true, "devDependencies": {