11const { promisify } = require ( 'util' )
22const read = promisify ( require ( 'read' ) )
3+ const chalk = require ( 'chalk' )
34const mkdirp = require ( 'mkdirp-infer-owner' )
45const readPackageJson = require ( 'read-package-json-fast' )
56const Arborist = require ( '@npmcli/arborist' )
@@ -12,6 +13,7 @@ const npa = require('npm-package-arg')
1213const fileExists = require ( './utils/file-exists.js' )
1314const PATH = require ( './utils/path.js' )
1415const BaseCommand = require ( './base-command.js' )
16+ const getWorkspaces = require ( './workspaces/get-workspaces.js' )
1517
1618// it's like this:
1719//
@@ -38,6 +40,13 @@ const BaseCommand = require('./base-command.js')
3840// runScript({ pkg, event: 'npx', ... })
3941// process.env.npm_lifecycle_event = 'npx'
4042
43+ const nocolor = {
44+ reset : s => s ,
45+ bold : s => s ,
46+ dim : s => s ,
47+ green : s => s ,
48+ }
49+
4150class Exec extends BaseCommand {
4251 /* istanbul ignore next - see test/lib/load-all-commands.js */
4352 static get description ( ) {
@@ -60,29 +69,39 @@ class Exec extends BaseCommand {
6069 }
6170
6271 exec ( args , cb ) {
63- this . _exec ( args ) . then ( ( ) => cb ( ) ) . catch ( cb )
72+ const path = this . npm . localPrefix
73+ const runPath = process . cwd ( )
74+ this . _exec ( args , { path, runPath } ) . then ( ( ) => cb ( ) ) . catch ( cb )
75+ }
76+
77+ execWorkspaces ( args , filters , cb ) {
78+ this . _execWorkspaces ( args , filters ) . then ( ( ) => cb ( ) ) . catch ( cb )
6479 }
6580
6681 // When commands go async and we can dump the boilerplate exec methods this
6782 // can be named correctly
68- async _exec ( args ) {
83+ async _exec ( _args , { locationMsg , path , runPath } ) {
6984 const call = this . npm . config . get ( 'call' )
7085 const shell = this . npm . config . get ( 'shell' )
7186 // dereferenced because we manipulate it later
7287 const packages = [ ...this . npm . config . get ( 'package' ) ]
7388
74- if ( call && args . length )
89+ if ( call && _args . length )
7590 throw this . usage
7691
92+ const args = [ ..._args ]
7793 const pathArr = [ ...PATH ]
7894
7995 // nothing to maybe install, skip the arborist dance
8096 if ( ! call && ! args . length && ! packages . length ) {
8197 return await this . run ( {
8298 args,
8399 call,
100+ locationMsg,
84101 shell,
102+ path,
85103 pathArr,
104+ runPath,
86105 } )
87106 }
88107
@@ -105,7 +124,10 @@ class Exec extends BaseCommand {
105124 return await this . run ( {
106125 args,
107126 call,
127+ locationMsg,
128+ path,
108129 pathArr,
130+ runPath,
109131 shell,
110132 } )
111133 }
@@ -120,11 +142,11 @@ class Exec extends BaseCommand {
120142 // node_modules/${name}/package.json, and only pacote fetch if
121143 // that fails.
122144 const manis = await Promise . all ( packages . map ( async p => {
123- const spec = npa ( p , this . npm . localPrefix )
145+ const spec = npa ( p , path )
124146 if ( spec . type === 'tag' && spec . rawSpec === '' ) {
125147 // fall through to the pacote.manifest() approach
126148 try {
127- const pj = resolve ( this . npm . localPrefix , 'node_modules' , spec . name )
149+ const pj = resolve ( path , 'node_modules' , spec . name )
128150 return await readPackageJson ( pj )
129151 } catch ( er ) { }
130152 }
@@ -143,7 +165,7 @@ class Exec extends BaseCommand {
143165 // figure out whether we need to install stuff, or if local is fine
144166 const localArb = new Arborist ( {
145167 ...this . npm . flatOptions ,
146- path : this . npm . localPrefix ,
168+ path,
147169 } )
148170 const tree = await localArb . loadActual ( )
149171
@@ -195,16 +217,24 @@ class Exec extends BaseCommand {
195217 pathArr . unshift ( resolve ( installDir , 'node_modules/.bin' ) )
196218 }
197219
198- return await this . run ( { args, call, pathArr, shell } )
220+ return await this . run ( {
221+ args,
222+ call,
223+ locationMsg,
224+ path,
225+ pathArr,
226+ runPath,
227+ shell,
228+ } )
199229 }
200230
201- async run ( { args, call, pathArr, shell } ) {
231+ async run ( { args, call, locationMsg , path , pathArr, runPath , shell } ) {
202232 // turn list of args into command string
203233 const script = call || args . shift ( ) || shell
204234
205235 // do the fakey runScript dance
206236 // still should work if no package.json in cwd
207- const realPkg = await readPackageJson ( `${ this . npm . localPrefix } /package.json` )
237+ const realPkg = await readPackageJson ( `${ path } /package.json` )
208238 . catch ( ( ) => ( { } ) )
209239 const pkg = {
210240 ...realPkg ,
@@ -220,15 +250,27 @@ class Exec extends BaseCommand {
220250 if ( process . stdin . isTTY ) {
221251 if ( ciDetect ( ) )
222252 return this . npm . log . warn ( 'exec' , 'Interactive mode disabled in CI environment' )
223- this . npm . output ( `\nEntering npm script environment\nType 'exit' or ^D when finished\n` )
253+
254+ const color = this . npm . config . get ( 'color' )
255+ const colorize = color ? chalk : nocolor
256+
257+ locationMsg = locationMsg || ` at location:\n${ colorize . dim ( runPath ) } `
258+
259+ this . npm . output ( `${
260+ colorize . reset ( '\nEntering npm script environment' )
261+ } ${
262+ colorize . reset ( locationMsg )
263+ } ${
264+ colorize . bold ( '\nType \'exit\' or ^D when finished\n' )
265+ } `)
224266 }
225267 }
226268 return await runScript ( {
227269 ...this . npm . flatOptions ,
228270 pkg,
229271 banner : false ,
230272 // we always run in cwd, not --prefix
231- path : process . cwd ( ) ,
273+ path : runPath ,
232274 stdioString : true ,
233275 event : 'npx' ,
234276 args,
@@ -288,5 +330,28 @@ class Exec extends BaseCommand {
288330 . digest ( 'hex' )
289331 . slice ( 0 , 16 )
290332 }
333+
334+ async workspaces ( filters ) {
335+ return getWorkspaces ( filters , { path : this . npm . localPrefix } )
336+ }
337+
338+ async _execWorkspaces ( args , filters ) {
339+ const workspaces = await this . workspaces ( filters )
340+ const getLocationMsg = async path => {
341+ const color = this . npm . config . get ( 'color' )
342+ const colorize = color ? chalk : nocolor
343+ const { _id } = await readPackageJson ( `${ path } /package.json` )
344+ return ` in workspace ${ colorize . green ( _id ) } at location:\n${ colorize . dim ( path ) } `
345+ }
346+
347+ for ( const workspacePath of workspaces . values ( ) ) {
348+ const locationMsg = await getLocationMsg ( workspacePath )
349+ await this . _exec ( args , {
350+ locationMsg,
351+ path : workspacePath ,
352+ runPath : workspacePath ,
353+ } )
354+ }
355+ }
291356}
292357module . exports = Exec
0 commit comments