1
- import * as path from "path" ;
1
+ import Future = require( "fibers/future" ) ;
2
+ import * as npm from "npm" ;
2
3
3
4
interface INpmOpts {
4
5
config ?: any ;
@@ -7,13 +8,35 @@ interface INpmOpts {
7
8
}
8
9
9
10
export class NodePackageManager implements INodePackageManager {
10
- constructor ( private $fs : IFileSystem ,
11
- private $hostInfo : IHostInfo ,
12
- private $errors : IErrors ,
13
- private $childProcess : IChildProcess ,
11
+ constructor ( private $childProcess : IChildProcess ,
14
12
private $logger : ILogger ,
15
13
private $options : IOptions ) { }
16
14
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
+
17
40
public install ( packageName : string , pathToSave : string , config ?: any ) : IFuture < any > {
18
41
return ( ( ) => {
19
42
if ( this . $options . disableNpmInstall ) {
@@ -24,105 +47,100 @@ export class NodePackageManager implements INodePackageManager {
24
47
config [ "ignore-scripts" ] = true ;
25
48
}
26
49
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 ) ;
37
50
try {
38
- this . $childProcess . spawnFromEvent ( this . getNpmExecutableName ( ) , params , "close" , { cwd : pathToSave } ) . wait ( ) ;
51
+ return this . loadAndExecute ( "install" , [ pathToSave , packageName ] , { config : config } ) . wait ( ) ;
39
52
} catch ( err ) {
40
- if ( err . message && err . message . indexOf ( "EPEERINVALID" ) !== - 1 ) {
53
+ if ( err . code === "EPEERINVALID" ) {
41
54
// Not installed peer dependencies are treated by npm 2 as errors, but npm 3 treats them as warnings.
42
55
// 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' } }
43
73
this . $logger . warn ( err . message ) ;
74
+ this . $logger . trace ( "Required peerDependencies are: " , err . peersDepending ) ;
44
75
} else {
45
76
// All other errors should be handled by the caller code.
46
77
throw err ;
47
78
}
48
79
}
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 ;
79
80
} ) . future < any > ( ) ( ) ;
80
81
}
81
82
82
83
public uninstall ( packageName : string , config ?: any , path ?: string ) : IFuture < any > {
83
- let flags = this . getFlagsString ( config , false ) ;
84
- return this . $childProcess . exec ( `npm uninstall ${ packageName } ${ flags } ` , { cwd : path } ) ;
84
+ return this . loadAndExecute ( "uninstall" , [ [ packageName ] ] , { config, path } ) ;
85
85
}
86
86
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 ( " " ) } ` ) ;
87
+ public search ( filter : string [ ] , silent : boolean ) : IFuture < any > {
88
+ let args = ( < any [ ] > ( [ filter ] || [ ] ) ) . concat ( silent ) ;
89
+ return this . loadAndExecute ( " search" , args ) ;
90
90
}
91
91
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 > ( ) ( ) ;
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 } ) ;
98
95
}
99
96
100
- private getNpmExecutableName ( ) : string {
101
- let npmExecutableName = "npm" ;
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" } ) ;
100
+ }
102
101
103
- if ( this . $hostInfo . isWindows ) {
104
- npmExecutableName += ".cmd" ;
105
- }
102
+ public view ( packageName : string , propertyName : string ) : IFuture < any > {
103
+ return this . loadAndExecute ( "view" , [ [ packageName , propertyName ] , [ false ] ] ) ;
104
+ }
106
105
107
- return npmExecutableName ;
106
+ public executeNpmCommand ( npmCommandName : string , currentWorkingDirectory : string ) : IFuture < any > {
107
+ return this . $childProcess . exec ( npmCommandName , { cwd : currentWorkingDirectory } ) ;
108
108
}
109
109
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 } ` ) ;
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 > ( ) ( ) ;
116
+ }
117
+
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 ;
119
124
}
125
+
126
+ if ( err ) {
127
+ future . throw ( err ) ;
128
+ } else {
129
+ future . return ( data ) ;
130
+ }
131
+ } ;
132
+ args . push ( callback ) ;
133
+
134
+ if ( opts && opts . path ) {
135
+ oldNpmPath = npm . prefix ;
136
+ ( < any > npm ) . prefix = opts . path ;
120
137
}
121
- if ( asArray ) {
122
- return array ;
123
- }
124
138
125
- return array . join ( " " ) ;
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 ;
126
144
}
127
145
}
128
146
$injector . register ( "npm" , NodePackageManager ) ;
0 commit comments