Skip to content

Create project command #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 14, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,8 @@ pids
logs
results
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
10 changes: 8 additions & 2 deletions lib/bootstrap.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
global._ = require("underscore");
global.$injector = require("./common/lib/yok").injector;
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("npm", "./node-package-manager");
12 changes: 12 additions & 0 deletions lib/commands/create-project-command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
///<reference path="../.d.ts"/>

export class CreateProjectCommand implements ICommand {
constructor(private $projectService: IProjectService) { }

execute(args: string[]): IFuture<void> {
return (() => {
this.$projectService.createProject(args[0], args[1]).wait();
}).future<void>()();
}
}
$injector.registerCommand("create", CreateProjectCommand);
5 changes: 5 additions & 0 deletions lib/declarations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
interface INodePackageManager {
cache: string;
load(config?: any): IFuture<void>;
install(where: string, what: string): IFuture<any>;
}
5 changes: 5 additions & 0 deletions lib/definitions/npm.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
declare module "npm" {
var cache: string;
var commands: any[];
function load(config: Object, callback: (err: any, data: any) => void);
}
3 changes: 3 additions & 0 deletions lib/definitions/osenv.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare module "osenv" {
function home();
}
7 changes: 7 additions & 0 deletions lib/definitions/project.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
interface IProjectService {
createProject(projectName: string, projectId: string): IFuture<void>;
}

interface IProjectTemplatesService {
defaultTemplatePath: IFuture<string>;
}
5 changes: 5 additions & 0 deletions lib/definitions/shelljs.d.ts
Original file line number Diff line number Diff line change
@@ -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);
}
14 changes: 10 additions & 4 deletions lib/nativescript-cli.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
///<reference path=".d.ts"/>

import Fiber = require("fibers");
import Future = require("fibers/future");
import path = require("path");

require("./bootstrap");
require("./options");

var fiber = Fiber(() => {
});
global.__main_fiber__ = fiber; // leak fiber to prevent it from being GC'd and thus corrupting V8
fiber.run();
import errors = require("./common/errors");
errors.installUncaughtExceptionListener();

$injector.register("config", {"CI_LOGGER": false});

var dispatcher = $injector.resolve("dispatcher");
dispatcher.runMainFiber();
36 changes: 36 additions & 0 deletions lib/node-package-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
///<reference path=".d.ts"/>

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<void> {
var future = new Future<void>();
npm.load(config, (err) => {
if(err) {
future.throw(err);
} else {
future.return();
}
});
return future;
}

public install(where: string, what: string): IFuture<any> {
var future = new Future<any>();
npm.commands["install"](where, what, (err, data) => {
if(err) {
future.throw(err);
} else {
future.return(data);
}
});
return future;
}
}
$injector.register("npm", NodePackageManager);
29 changes: 29 additions & 0 deletions lib/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
///<reference path=".d.ts"/>

import path = require("path");
import helpers = require("./common/helpers");
import osenv = require("osenv");

var knownOpts:any = {
"log" : String,
"verbose" : Boolean,
"path" : String,
"copy-from": String,
"link-to": String,
"version": Boolean,
"help": Boolean
},
shorthands = {
"v" : "verbose",
"p" : "path"
};

var defaultProfileDir = path.join(osenv.home(), ".nativescript-cli");
var parsed = helpers.getParsedOptions(knownOpts, shorthands, defaultProfileDir);

Object.keys(parsed).forEach((opt) => exports[opt] = parsed[opt]);

exports.knownOpts = knownOpts;

declare var exports:any;
export = exports;
104 changes: 104 additions & 0 deletions lib/services/project-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
///<reference path="../.d.ts"/>

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.HelloWorld";
private static DEFAULT_NAME = "HelloNativescript";
private static APP_FOLDER_NAME = "app";

constructor(private $logger: ILogger,
private $errors: IErrors,
private $fs: IFileSystem,
private $projectTemplatesService: IProjectTemplatesService) { }

public createProject(projectName: string, projectId: string): IFuture<void> {
return(() => {
var projectDir = path.resolve(options.path || ".");

projectId = projectId || ProjectService.DEFAULT_ID;
projectName = projectName || ProjectService.DEFAULT_NAME;

projectDir = path.join(projectDir, projectName);
this.$fs.createDirectory(projectDir).wait();

var customAppPath = this.getCustomAppPath();
if(customAppPath) {
customAppPath = path.resolve(customAppPath);
}

if(this.$fs.exists(projectDir).wait() && !this.$fs.isEmptyDir(projectDir).wait()) {
this.$errors.fail("Path already exists and is not empty %s", projectDir);
}

this.$logger.trace("Creating a new NativeScript project with name %s and id at location", projectName, projectId, projectDir);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are missing some %ses here.


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 nativescript hello world application
this.$logger.trace("Using NativeScript hello world application");
var defaultTemplatePath = this.$projectTemplatesService.defaultTemplatePath.wait();
this.$logger.trace("Copying Nativescript hello world application into %s", appDirectory);
appPath = defaultTemplatePath;
}

this.createProjectCore(projectDir, appPath, false).wait();
}).future<void>()();
}

private createProjectCore(projectDir: string, appPath: string, symlink?: boolean): IFuture<void> {
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<void>()();
}

private createBasicProjectStructure(projectDir: string): IFuture<void> {
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<void>()();
}

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 are supported.");
}

if(customAppPath.substr(0, 1) === '~') {
customAppPath = path.join(osenv.home(), customAppPath.substr(1));
}
}

return customAppPath;
}
}
$injector.register("projectService", ProjectService);
37 changes: 37 additions & 0 deletions lib/services/project-templates-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
///<reference path="../.d.ts"/>

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 NPM_DEFAULT_TEMPLATE_NAME = "tns-template-hello-world";
private static NPM_LOAD_FAILED = "Failed to retrieve nativescript hello world application. Please try again a little bit later.";

public constructor(private $errors: IErrors,
private $logger: ILogger,
private $npm: INodePackageManager) { }

public get defaultTemplatePath(): IFuture<string> {
return this.getDefaultTemplatePath();
}

private getDefaultTemplatePath(): IFuture<string> {
return (() => {
try {
this.$npm.load().wait();
this.$npm.install(npm.cache, ProjectTemplatesService.NPM_DEFAULT_TEMPLATE_NAME).wait();
} catch (error) {
this.$logger.debug(error);
this.$errors.fail(ProjectTemplatesService.NPM_LOAD_FAILED);
}

return path.join(npm.cache, "node_modules", ProjectTemplatesService.NPM_DEFAULT_TEMPLATE_NAME);
}).future<string>()();
}
}
$injector.register("projectTemplatesService", ProjectTemplatesService);
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@
],
"dependencies": {
"fibers": "https://github.com/icenium/node-fibers/tarball/master",
"filesize": "2.0.3",
"progress-stream": "0.5.0",
"log4js": "0.6.9",
"osenv": "0.1.0",
"tabtab": "https://github.com/tailsu/node-tabtab/tarball/master",
"underscore": "1.5.2",
"unzip": "0.1.9",
"yargs": "1.2.2"
"yargs": "1.2.2",
"npm": "1.4.10"
},
"analyze": true,
"devDependencies": {
Expand Down