Skip to content

Commit 92de5c7

Browse files
committed
remove npm2 dependency + fixes after reviews
1 parent 94c534d commit 92de5c7

20 files changed

+365
-642
lines changed

lib/constants.ts

+21-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ export let TEST_RUNNER_NAME = "nativescript-unit-test-runner";
1616
export let LIVESYNC_EXCLUDED_FILE_PATTERNS = ["**/*.js.map", "**/*.ts"];
1717
export let XML_FILE_EXTENSION = ".xml";
1818

19+
export class PackageVersion {
20+
static NEXT = "next";
21+
static LATEST = "latest";
22+
}
23+
24+
export class SaveOptions {
25+
static PRODUCTION = "save";
26+
static DEV = "save-dev";
27+
static OPTIONAL = "save-optional";
28+
static EXACT = "save-exact";
29+
}
30+
1931
export class ReleaseType {
2032
static MAJOR = "major";
2133
static PREMAJOR = "premajor";
@@ -26,6 +38,14 @@ export class ReleaseType {
2638
static PRERELEASE = "prerelease";
2739
}
2840

41+
export let RESERVED_TEMPLATE_NAMES: IStringDictionary = {
42+
"default": "tns-template-hello-world",
43+
"tsc": "tns-template-hello-world-ts",
44+
"typescript": "tns-template-hello-world-ts",
45+
"ng": "tns-template-hello-world-ng",
46+
"angular": "tns-template-hello-world-ng"
47+
};
48+
2949
export class ITMSConstants {
3050
static ApplicationMetadataFile = "metadata.xml";
3151
static VerboseLoggingLevels = {
@@ -44,4 +64,4 @@ class ItunesConnectApplicationTypesClass implements IiTunesConnectApplicationTyp
4464
export let ItunesConnectApplicationTypes = new ItunesConnectApplicationTypesClass();
4565

4666
export let ANGULAR_NAME = "angular";
47-
export let TYPESCRIPT_NAME = "TypeScript";
67+
export let TYPESCRIPT_NAME = "typescript";

lib/declarations.ts

+7-14
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,21 @@
11
interface INodePackageManager {
2-
getCache(): string;
3-
load(config?: any): IFuture<void>;
42
install(packageName: string, pathToSave: string, config?: any): IFuture<any>;
53
uninstall(packageName: string, config?: any, path?: string): IFuture<any>;
6-
cache(packageName: string, version: string, cache?: any): IFuture<IDependencyData>;
7-
cacheUnpack(packageName: string, version: string, unpackTarget?: string): IFuture<void>;
8-
view(packageName: string, propertyName: string): IFuture<any>;
9-
executeNpmCommand(npmCommandName: string, currentWorkingDirectory: string): IFuture<any>;
10-
search(filter: string[], silent: boolean): IFuture<any>;
4+
view(packageName: string, config: any): IFuture<any>;
5+
search(filter: string[], config: any): IFuture<any>;
116
}
127

138
interface INpmInstallationManager {
14-
getCacheRootPath(): string;
15-
addToCache(packageName: string, version: string): IFuture<any>;
16-
cacheUnpack(packageName: string, version: string, unpackTarget?: string): IFuture<void>;
17-
install(packageName: string, options?: INpmInstallOptions): IFuture<string>;
9+
install(packageName: string, packageDir: string, options?: INpmInstallOptions): IFuture<any>;
1810
getLatestVersion(packageName: string): IFuture<string>;
11+
getNextVersion(packageName: string): IFuture<string>;
1912
getLatestCompatibleVersion(packageName: string): IFuture<string>;
20-
getCachedPackagePath(packageName: string, version: string): string;
2113
}
2214

2315
interface INpmInstallOptions {
2416
pathToSave?: string;
2517
version?: string;
18+
dependencyType?: string;
2619
}
2720

2821
interface IDependencyData {
@@ -81,7 +74,7 @@ interface IOptions extends ICommonOptions {
8174
frameworkName: string;
8275
frameworkPath: string;
8376
frameworkVersion: string;
84-
ignoreScripts: boolean;
77+
ignoreScripts: boolean; //npm flag
8578
disableNpmInstall: boolean;
8679
ipa: string;
8780
keyStoreAlias: string;
@@ -95,7 +88,7 @@ interface IOptions extends ICommonOptions {
9588
bundle: boolean;
9689
platformTemplate: string;
9790
port: Number;
98-
production: boolean;
91+
production: boolean; //npm flag
9992
sdk: string;
10093
symlink: boolean;
10194
tnsModulesVersion: string;

lib/definitions/project.d.ts

+2-12
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,6 @@ interface IProjectDataService {
2626
* Describes working with templates.
2727
*/
2828
interface IProjectTemplatesService {
29-
/**
30-
* Defines the path where unpacked default template can be found.
31-
*/
32-
defaultTemplatePath: IFuture<string>;
33-
3429
/**
3530
* Prepares template for project creation.
3631
* In case templateName is not provided, use defaultTemplatePath.
@@ -39,7 +34,7 @@ interface IProjectTemplatesService {
3934
* @param {string} templateName The name of the template.
4035
* @return {string} Path to the directory where extracted template can be found.
4136
*/
42-
prepareTemplate(templateName: string): IFuture<string>;
37+
prepareTemplate(templateName: string, projectDir: string): IFuture<string>;
4338
}
4439

4540
interface IPlatformProjectServiceBase {
@@ -77,12 +72,7 @@ interface IPlatformProjectService {
7772
prepareProject(): IFuture<void>;
7873
prepareAppResources(appResourcesDirectoryPath: string): IFuture<void>;
7974
isPlatformPrepared(projectRoot: string): IFuture<boolean>;
80-
canUpdatePlatform(currentVersion: string, newVersion: string): IFuture<boolean>;
81-
/**
82-
* Provides a platform specific update logic for the specified runtime versions.
83-
* @return true in cases when the update procedure should continue.
84-
*/
85-
updatePlatform(currentVersion: string, newVersion: string, canUpdate: boolean, addPlatform?: Function, removePlatform?: (platforms: string[]) => IFuture<void>): IFuture<boolean>;
75+
canUpdatePlatform(newInstalledModuleDir: string): IFuture<boolean>;
8676
preparePluginNativeCode(pluginData: IPluginData, options?: any): IFuture<void>;
8777
removePluginNativeCode(pluginData: IPluginData): IFuture<void>;
8878
afterPrepareAllPlugins(): IFuture<void>;

lib/node-package-manager.ts

+77-95
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import Future = require("fibers/future");
2-
import * as npm from "npm";
1+
import * as path from "path";
32

43
interface INpmOpts {
54
config?: any;
@@ -8,35 +7,13 @@ interface INpmOpts {
87
}
98

109
export class NodePackageManager implements INodePackageManager {
11-
constructor(private $childProcess: IChildProcess,
10+
constructor(private $fs: IFileSystem,
11+
private $hostInfo: IHostInfo,
12+
private $errors: IErrors,
13+
private $childProcess: IChildProcess,
1214
private $logger: ILogger,
1315
private $options: IOptions) { }
1416

15-
public getCache(): string {
16-
return npm.cache;
17-
}
18-
19-
public load(config?: any): IFuture<void> {
20-
if (npm.config.loaded) {
21-
let data = npm.config.sources.cli.data;
22-
Object.keys(data).forEach(k => delete data[k]);
23-
if (config) {
24-
_.assign(data, config);
25-
}
26-
return Future.fromResult();
27-
} else {
28-
let future = new Future<void>();
29-
npm.load(config, (err: Error) => {
30-
if (err) {
31-
future.throw(err);
32-
} else {
33-
future.return();
34-
}
35-
});
36-
return future;
37-
}
38-
}
39-
4017
public install(packageName: string, pathToSave: string, config?: any): IFuture<any> {
4118
return (() => {
4219
if (this.$options.disableNpmInstall) {
@@ -47,100 +24,105 @@ export class NodePackageManager implements INodePackageManager {
4724
config["ignore-scripts"] = true;
4825
}
4926

27+
let jsonContentBefore = this.$fs.readJson(path.join(pathToSave, "package.json")).wait();
28+
// let dependenciesBefore: Array<string> = [];
29+
let dependenciesBefore = _.keys(jsonContentBefore.dependencies).concat(_.keys(jsonContentBefore.devDependencies));
30+
31+
let flags = this.getFlagsString(config, true);
32+
let params = ["install"];
33+
if(packageName !== pathToSave) {
34+
params.push(packageName); //because npm install ${pwd} on mac tries to install itself as a dependency (windows and linux have no such issues)
35+
}
36+
params = params.concat(flags);
5037
try {
51-
return this.loadAndExecute("install", [pathToSave, packageName], { config: config }).wait();
38+
this.$childProcess.spawnFromEvent(this.getNpmExecutableName(), params, "close", { cwd: pathToSave }).wait();
5239
} catch (err) {
53-
if (err.code === "EPEERINVALID") {
40+
if (err.message && err.message.indexOf("EPEERINVALID") !== -1) {
5441
// Not installed peer dependencies are treated by npm 2 as errors, but npm 3 treats them as warnings.
5542
// We'll show them as warnings and let the user install them in case they are needed.
56-
// The strucutre of the error object in such case is:
57-
// { [Error: The package @angular/[email protected] does not satisfy its siblings' peerDependencies requirements!]
58-
// code: 'EPEERINVALID',
59-
// packageName: '@angular/core',
60-
// packageVersion: '2.1.0-beta.0',
61-
// peersDepending:
62-
// { '@angular/[email protected]': '2.1.0-beta.0',
63-
// '@angular/[email protected]': '2.1.0-beta.0',
64-
// '@angular/[email protected]': '2.1.0-beta.0',
65-
// '@angular/[email protected]': '2.1.0-beta.0',
66-
// '@angular/[email protected]': '2.1.0-beta.0',
67-
// '@angular/[email protected]': '2.1.0-beta.0',
68-
// '@angular/[email protected]': '2.1.0-beta.0',
69-
// '@angular/[email protected]': '2.1.0-beta.0',
70-
// '@ngrx/[email protected]': '^2.0.0',
71-
// '@ngrx/[email protected]': '^2.0.0',
72-
// '[email protected]': '~2.0.0' } }
7343
this.$logger.warn(err.message);
74-
this.$logger.trace("Required peerDependencies are: ", err.peersDepending);
7544
} else {
7645
// All other errors should be handled by the caller code.
7746
throw err;
7847
}
7948
}
49+
50+
let jsonContentAfter = this.$fs.readJson(path.join(pathToSave, "package.json")).wait();
51+
let dependenciesAfter = _.keys(jsonContentAfter.dependencies).concat(_.keys(jsonContentAfter.devDependencies));
52+
53+
/** This diff is done in case the installed pakcage is a URL address, a path to local directory or a .tgz file
54+
* in these cases we don't have the package name and we can't rely on "npm install --json"" option
55+
* to get the project name because we have to parse the output from the stdout and we have no controll over it (so other messages may be mangled in stdout)
56+
* The solution is to compare package.json project dependencies before and after install and get the name of the installed package,
57+
* even if it's installed through local path or URL. If command installes more than one package, only the package originally installed is returned.
58+
*/
59+
let dependencyDiff = _(jsonContentAfter.dependencies)
60+
.omitBy((val: string, key: string) => jsonContentBefore && jsonContentBefore.dependencies && jsonContentBefore.dependencies[key] && jsonContentBefore.dependencies[key] === val)
61+
.keys()
62+
.value();
63+
64+
let devDependencyDiff = _(jsonContentAfter.devDependencies)
65+
.omitBy((val: string, key: string) => jsonContentBefore && jsonContentBefore.devDependencies && jsonContentBefore.devDependencies[key] && jsonContentBefore.devDependencies[key] === val)
66+
.keys()
67+
.value();
68+
69+
let diff = dependencyDiff.concat(devDependencyDiff);
70+
71+
if(diff.length <= 0 && dependenciesBefore.length === dependenciesAfter.length && packageName !== pathToSave) {
72+
this.$errors.failWithoutHelp(`The plugin ${packageName} is already installed`);
73+
}
74+
if(diff.length <= 0 && dependenciesBefore.length !== dependenciesAfter.length) {
75+
this.$errors.failWithoutHelp(`Couldn't install package correctly`);
76+
}
77+
78+
return diff;
8079
}).future<any>()();
8180
}
8281

8382
public uninstall(packageName: string, config?: any, path?: string): IFuture<any> {
84-
return this.loadAndExecute("uninstall", [[packageName]], { config, path });
83+
let flags = this.getFlagsString(config, false);
84+
return this.$childProcess.exec(`npm uninstall ${packageName} ${flags}`, { cwd: path });
8585
}
8686

87-
public search(filter: string[], silent: boolean): IFuture<any> {
88-
let args = (<any[]>([filter] || [])).concat(silent);
89-
return this.loadAndExecute("search", args);
87+
public search(filter: string[], config: any): IFuture<any> {
88+
let args = (<any[]>([filter] || [])).concat(config.silent);
89+
return this.$childProcess.exec(`npm search ${args.join(" ")}`);
9090
}
9191

92-
public cache(packageName: string, version: string, config?: any): IFuture<IDependencyData> {
93-
// function cache (pkg, ver, where, scrub, cb)
94-
return this.loadAndExecute("cache", [packageName, version, undefined, false], { subCommandName: "add", config: config });
95-
}
96-
97-
public cacheUnpack(packageName: string, version: string, unpackTarget?: string): IFuture<void> {
98-
// function unpack (pkg, ver, unpackTarget, dMode, fMode, uid, gid, cb)
99-
return this.loadAndExecute("cache", [packageName, version, unpackTarget, null, null, null, null], { subCommandName: "unpack" });
92+
public view(packageName: string, config: any): IFuture<any> {
93+
return (() => {
94+
let flags = this.getFlagsString(config, false);
95+
let viewResult = this.$childProcess.exec(`npm view ${packageName} ${flags}`).wait();
96+
return JSON.parse(viewResult);
97+
}).future<any>()();
10098
}
10199

102-
public view(packageName: string, propertyName: string): IFuture<any> {
103-
return this.loadAndExecute("view", [[packageName, propertyName], [false]]);
104-
}
100+
private getNpmExecutableName(): string {
101+
let npmExecutableName = "npm";
105102

106-
public executeNpmCommand(npmCommandName: string, currentWorkingDirectory: string): IFuture<any> {
107-
return this.$childProcess.exec(npmCommandName, { cwd: currentWorkingDirectory });
108-
}
103+
if (this.$hostInfo.isWindows) {
104+
npmExecutableName += ".cmd";
105+
}
109106

110-
private loadAndExecute(commandName: string, args: any[], opts?: INpmOpts): IFuture<any> {
111-
return (() => {
112-
opts = opts || {};
113-
this.load(opts.config).wait();
114-
return this.executeCore(commandName, args, opts).wait();
115-
}).future<any>()();
107+
return npmExecutableName;
116108
}
117109

118-
private executeCore(commandName: string, args: any[], opts?: INpmOpts): IFuture<any> {
119-
let future = new Future<any>();
120-
let oldNpmPath: string = undefined;
121-
let callback = (err: Error, data: any) => {
122-
if (oldNpmPath) {
123-
(<any>npm).prefix = oldNpmPath;
124-
}
125-
126-
if (err) {
127-
future.throw(err);
128-
} else {
129-
future.return(data);
110+
private getFlagsString(config: any, asArray: boolean) : any{
111+
let array:Array<string> = [];
112+
for(let flag in config) {
113+
if(config[flag]) {
114+
if(flag==="dist-tags" || flag==="versions") {
115+
array.push(` ${flag}`);
116+
continue;
117+
}
118+
array.push(`--${flag}`);
130119
}
131-
};
132-
args.push(callback);
133-
134-
if (opts && opts.path) {
135-
oldNpmPath = npm.prefix;
136-
(<any>npm).prefix = opts.path;
120+
}
121+
if(asArray) {
122+
return array;
137123
}
138124

139-
let subCommandName: string = opts.subCommandName;
140-
let command = subCommandName ? npm.commands[commandName][subCommandName] : npm.commands[commandName];
141-
command.apply(this, args);
142-
143-
return future;
125+
return array.join(" ");
144126
}
145127
}
146128
$injector.register("npm", NodePackageManager);

0 commit comments

Comments
 (0)