Skip to content

Remove .tnsproject and introduce package.json #476

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
May 19, 2015
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
2 changes: 1 addition & 1 deletion lib/common
3 changes: 2 additions & 1 deletion lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ export class Configuration extends configBaseLib.ConfigBase implements IConfigur
$injector.register("config", Configuration);

export class StaticConfig extends staticConfigBaseLibPath.StaticConfigBase implements IStaticConfig {
public PROJECT_FILE_NAME = ".tnsproject";
public PROJECT_FILE_NAME = "package.json";
public CLIENT_NAME_KEY_IN_PROJECT_FILE = "nativescript";
public CLIENT_NAME = "tns";
public CLIENT_NAME_ALIAS = "NativeScript";
public ANALYTICS_API_KEY = "5752dabccfc54c4ab82aea9626b7338e";
Expand Down
72 changes: 63 additions & 9 deletions lib/project-data.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
///<reference path=".d.ts"/>
"use strict";

import constants = require("./constants");
import path = require("path");
import os = require("os");
import options = require("./common/options");

export class ProjectData implements IProjectData {
private static OLD_PROJECT_FILE_NAME = ".tnsproject";

public projectDir: string;
public platformsDir: string;
public projectFilePath: string;
Expand All @@ -13,37 +17,87 @@ export class ProjectData implements IProjectData {

constructor(private $fs: IFileSystem,
private $errors: IErrors,
private $logger: ILogger,
private $projectHelper: IProjectHelper,
private $staticConfig: IStaticConfig) {
this.initializeProjectData().wait();
}

private initializeProjectData(): IFuture<void> {
return(() => {
var projectDir = this.$projectHelper.projectDir;
let projectDir = this.$projectHelper.projectDir;
// If no project found, projectDir should be null
if(projectDir) {
this.projectDir = projectDir;
this.projectName = this.$projectHelper.sanitizeName(path.basename(projectDir));
this.platformsDir = path.join(projectDir, "platforms");
this.projectFilePath = path.join(projectDir, this.$staticConfig.PROJECT_FILE_NAME);
this.initializeProjectDataCore(projectDir);
let data: any = null;

if (this.$fs.exists(this.projectFilePath).wait()) {
try {
var fileContent = this.$fs.readJson(this.projectFilePath).wait();
this.projectId = fileContent.id;
let fileContent = this.$fs.readJson(this.projectFilePath).wait();
data = fileContent[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE];
} catch (err) {
this.$errors.fail({formatStr: "The project file %s is corrupted." + os.EOL +
"Consider restoring an earlier version from your source control or backup." + os.EOL +
"Additional technical info: %s",
suppressCommandHelp: true},
this.projectFilePath, err.toString());
}
}

if(data) {
this.projectId = data.id;
} else { // This is the case when we have package.json file but nativescipt key is not presented in it
this.tryToUpgradeProject().wait();
}
}
} else { // This is the case when no project file found
this.tryToUpgradeProject().wait();
}
}).future<void>()();
}

private throwNoProjectFoundError(): void {
this.$errors.fail("No project found at or above '%s' and neither was a --path specified.", options.path || path.resolve("."));
}

private tryToUpgradeProject(): IFuture<void> {
return (() => {
let projectDir = this.projectDir || path.resolve(options.path || ".");
let oldProjectFilePath = path.join(projectDir, ProjectData.OLD_PROJECT_FILE_NAME);
if(this.$fs.exists(oldProjectFilePath).wait()) {
this.upgrade(projectDir, oldProjectFilePath).wait();
} else {
this.$errors.fail("No project found at or above '%s' and neither was a --path specified.", process.cwd());
this.throwNoProjectFoundError();
}
}).future<void>()();
}

private upgrade(projectDir: string, oldProjectFilePath: string): IFuture<void> {
return (() => {
try {
let oldProjectData = this.$fs.readJson(oldProjectFilePath).wait();

let newProjectFilePath = this.projectFilePath || path.join(projectDir, this.$staticConfig.PROJECT_FILE_NAME);
let newProjectData = this.$fs.exists(newProjectFilePath).wait() ? this.$fs.readJson(newProjectFilePath).wait() : {};
newProjectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE] = oldProjectData;
this.$fs.writeJson(newProjectFilePath, newProjectData).wait();

this.$fs.deleteFile(oldProjectFilePath).wait();
} catch(err) {
this.$logger.out("An error occurred while upgrading your project.");
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider showing the err.Message / err.toString() to the user to help them troubleshoot the issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The error is thrown one line below so the user will see the whole error.

throw err;
}

this.initializeProjectDataCore(projectDir);

this.$logger.out("Successfully upgraded your project file.");
}).future<void>()();
}

private initializeProjectDataCore(projectDir: string): void {
this.projectDir = projectDir;
this.projectName = this.$projectHelper.sanitizeName(path.basename(projectDir));
this.platformsDir = path.join(projectDir, "platforms");
this.projectFilePath = path.join(projectDir, this.$staticConfig.PROJECT_FILE_NAME);
}
}
$injector.register("projectData", ProjectData);
28 changes: 17 additions & 11 deletions lib/services/project-data-service.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,54 @@
///<reference path="../.d.ts"/>
"use strict";

import constants = require("./../constants");
import path = require("path");
import assert = require("assert");

export class ProjectDataService implements IProjectDataService {
private projectFileName: string;
private projectFilePath: string;
private projectData: IDictionary<any>;

constructor(private $fs: IFileSystem,
private $staticConfig: IStaticConfig) {
private $staticConfig: IStaticConfig,
private $errors: IErrors,
private $logger: ILogger) {
}

public initialize(projectDir: string): void {
if(!this.projectFileName) {
this.projectFileName = path.join(projectDir, this.$staticConfig.PROJECT_FILE_NAME);
if(!this.projectFilePath) {
this.projectFilePath = path.join(projectDir, this.$staticConfig.PROJECT_FILE_NAME);
}
}

public getValue(propertyName: string): IFuture<any> {
return (() => {
this.loadProjectFile().wait();
return this.projectData ? this.projectData[propertyName] : null;
return this.projectData ? this.projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE][propertyName] : null;
}).future<string>()();
}

public setValue(key: string, value: any): IFuture<void> {
return (() => {
this.loadProjectFile().wait();
this.projectData[key] = value;
this.$fs.writeJson(this.projectFileName, this.projectData, "\t").wait();
if(!this.projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE]) {
this.projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE] = Object.create(null);
}
this.projectData[this.$staticConfig.CLIENT_NAME_KEY_IN_PROJECT_FILE][key] = value;
this.$fs.writeJson(this.projectFilePath, this.projectData, "\t").wait();
}).future<void>()();
}

private loadProjectFile(): IFuture<void> {
return (() => {
assert.ok(this.projectFileName, "Initialize method of projectDataService is not called");
assert.ok(this.projectFilePath, "Initialize method of projectDataService is not called");

if(!this.projectData) {
if(!this.$fs.exists(this.projectFileName).wait()) {
this.$fs.writeFile(this.projectFileName, null).wait();
if(!this.$fs.exists(this.projectFilePath).wait()) {
this.$fs.writeFile(this.projectFilePath, null).wait();
}

this.projectData = this.$fs.readJson(this.projectFileName).wait() || Object.create(null);
this.projectData = this.$fs.readJson(this.projectFilePath).wait() || Object.create(null);
}
}).future<void>()();
}
Expand Down
139 changes: 136 additions & 3 deletions test/project-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import stubs = require('./stubs');

import ProjectServiceLib = require("../lib/services/project-service");
import ProjectDataServiceLib = require("../lib/services/project-data-service");
import ProjectDataLib = require("../lib/project-data");
import ProjectHelperLib = require("../lib/common/project-helper");
import StaticConfigLib = require("../lib/config");
import NpmLib = require("../lib/node-package-manager");
import HttpClientLib = require("../lib/common/http-client");
import fsLib = require("../lib/common/file-system");
import platformServiceLib = require("../lib/services/platform-service");

import path = require("path");
import temp = require("temp");
Expand Down Expand Up @@ -64,7 +66,7 @@ class ProjectIntegrationTest {
var projectDir = path.join(tempFolder, projectName);
var appDirectoryPath = path.join(projectDir, "app");
var platformsDirectoryPath = path.join(projectDir, "platforms");
var tnsProjectFilePath = path.join(projectDir, ".tnsproject");
let tnsProjectFilePath = path.join(projectDir, "package.json");

assert.isTrue(fs.exists(appDirectoryPath).wait());
assert.isTrue(fs.exists(platformsDirectoryPath).wait());
Expand All @@ -73,7 +75,7 @@ class ProjectIntegrationTest {
assert.isFalse(fs.isEmptyDir(appDirectoryPath).wait());
assert.isTrue(fs.isEmptyDir(platformsDirectoryPath).wait());

var actualAppId = fs.readJson(tnsProjectFilePath).wait().id;
var actualAppId = fs.readJson(tnsProjectFilePath).wait()["nativescript"].id;
var expectedAppId = appId;
assert.equal(actualAppId, expectedAppId);

Expand Down Expand Up @@ -139,4 +141,135 @@ describe("Project Service Tests", () => {
projectIntegrationTest.assertProject(tempFolder, projectName, options.appid).wait();
});
});
});
});

function createTestInjector() {
var testInjector = new yok.Yok();

testInjector.register("errors", stubs.ErrorsStub);
testInjector.register('logger', stubs.LoggerStub);
testInjector.register("projectService", ProjectServiceLib.ProjectService);
testInjector.register("projectHelper", ProjectHelperLib.ProjectHelper);
testInjector.register("projectTemplatesService", stubs.ProjectTemplatesService);
testInjector.register("projectNameValidator", mockProjectNameValidator);

testInjector.register("fs", fsLib.FileSystem);
testInjector.register("projectDataService", ProjectDataServiceLib.ProjectDataService);

testInjector.register("staticConfig", StaticConfigLib.StaticConfig);

testInjector.register("npm", NpmLib.NodePackageManager);
testInjector.register("httpClient", HttpClientLib.HttpClient);
testInjector.register("config", {});
testInjector.register("lockfile", stubs.LockFile);

testInjector.register('projectData', ProjectDataLib.ProjectData);

return testInjector;
}

describe("project upgrade procedure tests", () => {
it("should throw error when no nativescript project folder specified", () => {
var testInjector = createTestInjector();
var tempFolder = temp.mkdirSync("project upgrade");
options.path = tempFolder;
var isErrorThrown = false;

try {
testInjector.resolve("projectData"); // This should trigger upgrade procedure
} catch(err) {
isErrorThrown = true;
var expectedErrorMessage = "No project found at or above '%s' and neither was a --path specified.," + tempFolder;
assert.equal(expectedErrorMessage, err.toString());
}

assert.isTrue(isErrorThrown);
});
it("should upgrade project when .tnsproject file exists but package.json file doesn't exist", () => {
var testInjector = createTestInjector();
var fs: IFileSystem = testInjector.resolve("fs");

var tempFolder = temp.mkdirSync("projectUpgradeTest2");
options.path = tempFolder;
var tnsProjectData = {
"id": "org.nativescript.Test",
"tns-ios": {
"version": "1.0.0"
}
};
var tnsProjectFilePath = path.join(tempFolder, ".tnsproject");
fs.writeJson(tnsProjectFilePath, tnsProjectData).wait();

testInjector.resolve("projectData"); // This should trigger upgrade procedure

var packageJsonFilePath = path.join(tempFolder, "package.json");
var packageJsonFileContent = require(packageJsonFilePath);
assert.isTrue(fs.exists(packageJsonFilePath).wait());
assert.isFalse(fs.exists(tnsProjectFilePath).wait());
assert.deepEqual(tnsProjectData, packageJsonFileContent["nativescript"]);
});
it("should upgrade project when .tnsproject and package.json exist but nativescript key is not presented in package.json file", () => {
var testInjector = createTestInjector();
var fs: IFileSystem = testInjector.resolve("fs");

var tempFolder = temp.mkdirSync("projectUpgradeTest3");
options.path = tempFolder;
var tnsProjectData = {
"id": "org.nativescript.Test",
"tns-ios": {
"version": "1.0.1"
}
};
var packageJsonData = {
"name": "testModuleName",
"version": "0.0.0",
"dependencies": {
"myFirstDep": "0.0.1"
}
}
let tnsProjectFilePath = path.join(tempFolder, ".tnsproject");
fs.writeJson(tnsProjectFilePath, tnsProjectData).wait();

var packageJsonFilePath = path.join(tempFolder, "package.json");
fs.writeJson(packageJsonFilePath, packageJsonData).wait();

testInjector.resolve("projectData"); // This should trigger upgrade procedure

var packageJsonFileContent = require(packageJsonFilePath);
var expectedPackageJsonContent: any = packageJsonData;
expectedPackageJsonContent["nativescript"] = tnsProjectData;
assert.deepEqual(expectedPackageJsonContent, packageJsonFileContent);
});
it("shouldn't upgrade project when .tnsproject and package.json exist and nativescript key is presented in package.json file", () => {
var testInjector = createTestInjector();
var fs: IFileSystem = testInjector.resolve("fs");

var tempFolder = temp.mkdirSync("projectUpgradeTest4");
options.path = tempFolder;
var tnsProjectData = {

};
var packageJsonData = {
"name": "testModuleName",
"version": "0.0.0",
"dependencies": {
"myFirstDep": "0.0.2"
},
"nativescript": {
"id": "org.nativescript.Test",
"tns-ios": {
"version": "1.0.2"
}
}
}

fs.writeJson(path.join(tempFolder, ".tnsproject"), tnsProjectData).wait();
fs.writeJson(path.join(tempFolder, "package.json"), packageJsonData).wait();
testInjector.resolve("projectData"); // This should trigger upgrade procedure

var packageJsonFilePath = path.join(tempFolder, "package.json");
var packageJsonFileContent = require(packageJsonFilePath);

assert.deepEqual(packageJsonData, packageJsonFileContent);
});
});