-
-
Notifications
You must be signed in to change notification settings - Fork 197
/
Copy pathnode-package-manager.ts
128 lines (109 loc) · 4.81 KB
/
node-package-manager.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import * as path from "path";
interface INpmOpts {
config?: any;
subCommandName?: string;
path?: string;
}
export class NodePackageManager implements INodePackageManager {
constructor(private $fs: IFileSystem,
private $hostInfo: IHostInfo,
private $errors: IErrors,
private $childProcess: IChildProcess,
private $logger: ILogger,
private $options: IOptions) { }
public install(packageName: string, pathToSave: string, config?: any): IFuture<any> {
return (() => {
if (this.$options.disableNpmInstall) {
return;
}
if (this.$options.ignoreScripts) {
config = config || {};
config["ignore-scripts"] = true;
}
let jsonContentBefore = this.$fs.readJson(path.join(pathToSave, "package.json")).wait();
// let dependenciesBefore: Array<string> = [];
let dependenciesBefore = _.keys(jsonContentBefore.dependencies).concat(_.keys(jsonContentBefore.devDependencies));
let flags = this.getFlagsString(config, true);
let params = ["install"];
if(packageName !== pathToSave) {
params.push(packageName); //because npm install ${pwd} on mac tries to install itself as a dependency (windows and linux have no such issues)
}
params = params.concat(flags);
try {
this.$childProcess.spawnFromEvent(this.getNpmExecutableName(), params, "close", { cwd: pathToSave }).wait();
} catch (err) {
if (err.message && err.message.indexOf("EPEERINVALID") !== -1) {
// Not installed peer dependencies are treated by npm 2 as errors, but npm 3 treats them as warnings.
// We'll show them as warnings and let the user install them in case they are needed.
this.$logger.warn(err.message);
} else {
// All other errors should be handled by the caller code.
throw err;
}
}
let jsonContentAfter = this.$fs.readJson(path.join(pathToSave, "package.json")).wait();
let dependenciesAfter = _.keys(jsonContentAfter.dependencies).concat(_.keys(jsonContentAfter.devDependencies));
/** This diff is done in case the installed pakcage is a URL address, a path to local directory or a .tgz file
* in these cases we don't have the package name and we can't rely on "npm install --json"" option
* 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)
* The solution is to compare package.json project dependencies before and after install and get the name of the installed package,
* even if it's installed through local path or URL. If command installes more than one package, only the package originally installed is returned.
*/
let dependencyDiff = _(jsonContentAfter.dependencies)
.omitBy((val: string, key: string) => jsonContentBefore && jsonContentBefore.dependencies && jsonContentBefore.dependencies[key] && jsonContentBefore.dependencies[key] === val)
.keys()
.value();
let devDependencyDiff = _(jsonContentAfter.devDependencies)
.omitBy((val: string, key: string) => jsonContentBefore && jsonContentBefore.devDependencies && jsonContentBefore.devDependencies[key] && jsonContentBefore.devDependencies[key] === val)
.keys()
.value();
let diff = dependencyDiff.concat(devDependencyDiff);
if(diff.length <= 0 && dependenciesBefore.length === dependenciesAfter.length && packageName !== pathToSave) {
this.$errors.failWithoutHelp(`The plugin ${packageName} is already installed`);
}
if(diff.length <= 0 && dependenciesBefore.length !== dependenciesAfter.length) {
this.$errors.failWithoutHelp(`Couldn't install package correctly`);
}
return diff;
}).future<any>()();
}
public uninstall(packageName: string, config?: any, path?: string): IFuture<any> {
let flags = this.getFlagsString(config, false);
return this.$childProcess.exec(`npm uninstall ${packageName} ${flags}`, { cwd: path });
}
public search(filter: string[], config: any): IFuture<any> {
let args = (<any[]>([filter] || [])).concat(config.silent);
return this.$childProcess.exec(`npm search ${args.join(" ")}`);
}
public view(packageName: string, config: any): IFuture<any> {
return (() => {
let flags = this.getFlagsString(config, false);
let viewResult = this.$childProcess.exec(`npm view ${packageName} ${flags}`).wait();
return JSON.parse(viewResult);
}).future<any>()();
}
private getNpmExecutableName(): string {
let npmExecutableName = "npm";
if (this.$hostInfo.isWindows) {
npmExecutableName += ".cmd";
}
return npmExecutableName;
}
private getFlagsString(config: any, asArray: boolean) : any{
let array:Array<string> = [];
for(let flag in config) {
if(config[flag]) {
if(flag==="dist-tags" || flag==="versions") {
array.push(` ${flag}`);
continue;
}
array.push(`--${flag}`);
}
}
if(asArray) {
return array;
}
return array.join(" ");
}
}
$injector.register("npm", NodePackageManager);