-
-
Notifications
You must be signed in to change notification settings - Fork 197
/
Copy pathnode-package-manager.ts
143 lines (123 loc) · 5.51 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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import * as path from "path";
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 async install(packageName: string, pathToSave: string, config?: any): Promise<any> {
if (this.$options.disableNpmInstall) {
return;
}
if (this.$options.ignoreScripts) {
config = config || {};
config["ignore-scripts"] = true;
}
let packageJsonPath = path.join(pathToSave, "package.json");
let jsonContentBefore = this.$fs.readJson(packageJsonPath);
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);
let pwd = pathToSave;
//TODO: plamen5kov: workaround is here for a reason (remove whole file later)
if (this.$options.path) {
let relativePathFromCwdToSource = "";
if (this.$options.frameworkPath) {
relativePathFromCwdToSource = path.relative(this.$options.frameworkPath, pathToSave);
if (this.$fs.exists(relativePathFromCwdToSource)) {
packageName = relativePathFromCwdToSource;
}
}
}
try {
let spawnResult: ISpawnResult = await this.$childProcess.spawnFromEvent(this.getNpmExecutableName(), params, "close", { cwd: pwd, stdio: "inherit" });
this.$logger.out(spawnResult.stdout);
} 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.
// Revert package.json contents to preserve valid state
this.$fs.writeJson(packageJsonPath, jsonContentBefore);
throw err;
}
}
let jsonContentAfter = this.$fs.readJson(path.join(pathToSave, "package.json"));
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.$logger.warn(`The plugin ${packageName} is already installed`);
}
if (diff.length <= 0 && dependenciesBefore.length !== dependenciesAfter.length) {
this.$logger.warn(`Couldn't install package ${packageName} correctly`);
}
return diff;
}
public async uninstall(packageName: string, config?: any, path?: string): Promise<any> {
let flags = this.getFlagsString(config, false);
return this.$childProcess.exec(`npm uninstall ${packageName} ${flags}`, { cwd: path });
}
public async search(filter: string[], config: any): Promise<any> {
let args = (<any[]>([filter] || [])).concat(config.silent);
return this.$childProcess.exec(`npm search ${args.join(" ")}`);
}
public async view(packageName: string, config: Object): Promise<any> {
const wrappedConfig = _.extend({}, config, { json: true }); // always require view response as JSON
let flags = this.getFlagsString(wrappedConfig, false);
let viewResult: any;
try {
viewResult = await this.$childProcess.exec(`npm view ${packageName} ${flags}`);
} catch (e) {
this.$errors.failWithoutHelp(e);
}
return JSON.parse(viewResult);
}
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 (flag === "global") {
array.push(`--${flag}`);
array.push(`${config[flag]}`);
} else 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);