@@ -34,30 +34,104 @@ const maskLabel = mask => {
34
34
return label . join ( ', ' )
35
35
}
36
36
37
+ const subcommands = [
38
+ {
39
+ groups : [ 'ping' , 'registry' ] ,
40
+ title : 'npm ping' ,
41
+ cmd : 'checkPing' ,
42
+ } , {
43
+ groups : [ 'versions' ] ,
44
+ title : 'npm -v' ,
45
+ cmd : 'getLatestNpmVersion' ,
46
+ } , {
47
+ groups : [ 'versions' ] ,
48
+ title : 'node -v' ,
49
+ cmd : 'getLatestNodejsVersion' ,
50
+ } , {
51
+ groups : [ 'registry' ] ,
52
+ title : 'npm config get registry' ,
53
+ cmd : 'checkNpmRegistry' ,
54
+ } , {
55
+ groups : [ 'environment' ] ,
56
+ title : 'git executable in PATH' ,
57
+ cmd : 'getGitPath' ,
58
+ } , {
59
+ groups : [ 'environment' ] ,
60
+ title : 'global bin folder in PATH' ,
61
+ cmd : 'getBinPath' ,
62
+ } , {
63
+ groups : [ 'permissions' , 'cache' ] ,
64
+ title : 'Perms check on cached files' ,
65
+ cmd : 'checkCachePermission' ,
66
+ windows : false ,
67
+ } , {
68
+ groups : [ 'permissions' ] ,
69
+ title : 'Perms check on local node_modules' ,
70
+ cmd : 'checkLocalModulesPermission' ,
71
+ windows : false ,
72
+ } , {
73
+ groups : [ 'permissions' ] ,
74
+ title : 'Perms check on global node_modules' ,
75
+ cmd : 'checkGlobalModulesPermission' ,
76
+ windows : false ,
77
+ } , {
78
+ groups : [ 'permissions' ] ,
79
+ title : 'Perms check on local bin folder' ,
80
+ cmd : 'checkLocalBinPermission' ,
81
+ windows : false ,
82
+ } , {
83
+ groups : [ 'permissions' ] ,
84
+ title : 'Perms check on global bin folder' ,
85
+ cmd : 'checkGlobalBinPermission' ,
86
+ windows : false ,
87
+ } , {
88
+ groups : [ 'cache' ] ,
89
+ title : 'Verify cache contents' ,
90
+ cmd : 'verifyCachedFiles' ,
91
+ windows : false ,
92
+ } ,
93
+ // TODO:
94
+ // group === 'dependencies'?
95
+ // - ensure arborist.loadActual() runs without errors and no invalid edges
96
+ // - ensure package-lock.json matches loadActual()
97
+ // - verify loadActual without hidden lock file matches hidden lockfile
98
+ // group === '???'
99
+ // - verify all local packages have bins linked
100
+ // What is the fix for these?
101
+ ]
37
102
const BaseCommand = require ( '../base-command.js' )
38
103
class Doctor extends BaseCommand {
39
104
static description = 'Check your npm environment'
40
105
static name = 'doctor'
41
106
static params = [ 'registry' ]
42
107
static ignoreImplicitWorkspace = false
108
+ static usage = [ `[${ subcommands . flatMap ( s => s . groups )
109
+ . filter ( ( value , index , self ) => self . indexOf ( value ) === index )
110
+ . join ( '] [' ) } ]`]
111
+
112
+ static subcommands = subcommands
113
+
114
+ // minimum width of check column, enough for the word `Check`
115
+ #checkWidth = 5
43
116
44
117
async exec ( args ) {
45
118
log . info ( 'Running checkup' )
46
119
let allOk = true
47
120
48
121
const actions = this . actions ( args )
49
- this . checkWidth = actions . reduce ( ( length , item ) => Math . max ( item [ 0 ] . length , length ) , 5 )
122
+ this . #checkWidth = actions . reduce ( ( length , item ) =>
123
+ Math . max ( item . title . length , length ) , this . #checkWidth)
50
124
51
125
if ( ! this . npm . silent ) {
52
126
this . output ( [ 'Check' , 'Value' , 'Recommendation/Notes' ] . map ( h => this . npm . chalk . underline ( h ) ) )
53
127
}
54
128
// Do the actual work
55
- for ( const [ msg , fn , args ] of actions ) {
56
- const item = [ msg ]
129
+ for ( const { title , cmd } of actions ) {
130
+ const item = [ title ]
57
131
try {
58
- item . push ( true , await this [ fn ] ( ... args ) )
59
- } catch ( er ) {
60
- item . push ( false , er )
132
+ item . push ( true , await this [ cmd ] ( ) )
133
+ } catch ( err ) {
134
+ item . push ( false , err )
61
135
}
62
136
if ( ! item [ 1 ] ) {
63
137
allOk = false
@@ -72,11 +146,13 @@ class Doctor extends BaseCommand {
72
146
}
73
147
}
74
148
75
- if ( ! this . npm . silent ) {
76
- // this.npm.output(table(outTable, tableOpts))
77
- }
78
149
if ( ! allOk ) {
79
- throw new Error ( 'Some problems found. See above for recommendations.' )
150
+ if ( this . npm . silent ) {
151
+ /* eslint-disable-next-line max-len */
152
+ throw new Error ( 'Some problems found. Check logs or disable silent mode for recommendations.' )
153
+ } else {
154
+ throw new Error ( 'Some problems found. See above for recommendations.' )
155
+ }
80
156
}
81
157
}
82
158
@@ -144,15 +220,35 @@ class Doctor extends BaseCommand {
144
220
}
145
221
}
146
222
147
- async checkBinPath ( dir ) {
148
- const tracker = log . newItem ( 'checkBinPath ' , 1 )
149
- tracker . info ( 'checkBinPath ' , 'Finding npm global bin in your PATH' )
223
+ async getBinPath ( dir ) {
224
+ const tracker = log . newItem ( 'getBinPath ' , 1 )
225
+ tracker . info ( 'getBinPath ' , 'Finding npm global bin in your PATH' )
150
226
if ( ! process . env . PATH . includes ( this . npm . globalBin ) ) {
151
227
throw new Error ( `Add ${ this . npm . globalBin } to your $PATH` )
152
228
}
153
229
return this . npm . globalBin
154
230
}
155
231
232
+ async checkCachePermission ( ) {
233
+ return this . checkFilesPermission ( this . npm . cache , true , R_OK )
234
+ }
235
+
236
+ async checkLocalModulesPermission ( ) {
237
+ return this . checkFilesPermission ( this . npm . localDir , true , R_OK | W_OK , true )
238
+ }
239
+
240
+ async checkGlobalModulesPermission ( ) {
241
+ return this . checkFilesPermission ( this . npm . globalDir , false , R_OK )
242
+ }
243
+
244
+ async checkLocalBinPermission ( ) {
245
+ return this . checkFilesPermission ( this . npm . localBin , false , R_OK | W_OK | X_OK , true )
246
+ }
247
+
248
+ async checkGlobalBinPermission ( ) {
249
+ return this . checkFilesPermission ( this . npm . globalBin , false , X_OK )
250
+ }
251
+
156
252
async checkFilesPermission ( root , shouldOwn , mask , missingOk ) {
157
253
let ok = true
158
254
@@ -293,79 +389,22 @@ class Doctor extends BaseCommand {
293
389
'right-mid' : '' ,
294
390
middle : ' ' } ,
295
391
style : { 'padding-left' : 0 , 'padding-right' : 0 } ,
296
- colWidths : [ this . checkWidth , 6 ] ,
392
+ colWidths : [ this . # checkWidth, 6 ] ,
297
393
} )
298
394
t . push ( row )
299
395
this . npm . output ( t . toString ( ) )
300
396
}
301
397
302
- actions ( subcmds ) {
303
- const a = [ ]
304
- if ( ! subcmds . length || subcmds . includes ( 'ping' ) ) {
305
- a . push ( [ 'npm ping' , 'checkPing' , [ ] ] )
306
- }
307
- if ( ! subcmds . length || subcmds . includes ( 'versions' ) ) {
308
- a . push ( [ 'npm -v' , 'getLatestNpmVersion' , [ ] ] )
309
- a . push ( [ 'node -v' , 'getLatestNodejsVersion' , [ ] ] )
310
- }
311
- if ( ! subcmds . length || subcmds . includes ( 'registry' ) ) {
312
- a . push ( [ 'npm config get registry' , 'checkNpmRegistry' , [ ] ] )
313
- }
314
- if ( ! subcmds . length || subcmds . includes ( 'git' ) ) {
315
- a . push ( [ 'which git' , 'getGitPath' , [ ] ] )
316
- }
317
- if ( ! subcmds . length || subcmds . includes ( 'permissions' ) ) {
318
- if ( subcmds . includes ( 'permissions' ) && process . platform === 'win32' ) {
319
- log . warn ( 'Ignoring permissions checks for windows' )
320
- } else if ( process . platform !== 'win32' ) {
321
- a . push ( [
322
- 'Perms check on cached files' ,
323
- 'checkFilesPermission' ,
324
- [ this . npm . cache , true , R_OK ] ,
325
- ] )
326
- a . push ( [
327
- 'Perms check on local node_modules' ,
328
- 'checkFilesPermission' ,
329
- [ this . npm . localDir , true , R_OK | W_OK , true ] ,
330
- ] )
331
- a . push ( [
332
- 'Perms check on global node_modules' ,
333
- 'checkFilesPermission' ,
334
- [ this . npm . globalDir , false , R_OK ] ,
335
- ] )
336
- a . push ( [
337
- 'Perms check on local bin folder' ,
338
- 'checkFilesPermission' ,
339
- [ this . npm . localBin , false , R_OK | W_OK | X_OK , true ] ,
340
- ] )
341
- a . push ( [
342
- 'Perms check on global bin folder' ,
343
- 'checkFilesPermission' ,
344
- [ this . npm . globalBin , false , X_OK ] ,
345
- ] )
398
+ actions ( params ) {
399
+ return this . constructor . subcommands . filter ( subcmd => {
400
+ if ( process . platform === 'win32' && subcmd . windows === false ) {
401
+ return false
346
402
}
347
- }
348
- if ( ! subcmds . length || subcmds . includes ( 'cache' ) ) {
349
- a . push ( [
350
- 'Verify cache contents' ,
351
- 'verifyCachedFiles' ,
352
- [ this . npm . flatOptions . cache ] ,
353
- ] )
354
- }
355
-
356
- if ( ! subcmds . length || subcmds . includes ( 'environment' ) ) {
357
- a . push ( [ 'Global bin folder in PATH' , 'checkBinPath' , [ ] ] )
358
- }
359
-
360
- // TODO:
361
- // subcmd === 'dependencies'?
362
- // - ensure arborist.loadActual() runs without errors and no invalid edges
363
- // - ensure package-lock.json matches loadActual()
364
- // - verify loadActual without hidden lock file matches hidden lockfile
365
- // subcmd === '???'
366
- // - verify all local packages have bins linked
367
- // What is the fix for these?
368
- return a
403
+ if ( params . length ) {
404
+ return params . some ( param => subcmd . groups . includes ( param ) )
405
+ }
406
+ return true
407
+ } )
369
408
}
370
409
}
371
410
0 commit comments