@@ -11,6 +11,7 @@ import * as path from 'node:path';
1111import type { IPackageJson } from '@rushstack/node-core-library' ;
1212
1313import { syncNpmrc , type ILogger } from '../utilities/npmrcUtilities' ;
14+ import { convertCommandAndArgsToShell } from '../utilities/executionUtilities' ;
1415import type { RushConstants } from '../logic/RushConstants' ;
1516
1617export const RUSH_JSON_FILENAME : typeof RushConstants . rushJsonFilename = 'rush.json' ;
@@ -200,15 +201,14 @@ function _resolvePackageVersion(
200201 stdio : [ ]
201202 } ;
202203 const platformNpmPath : string = _getPlatformPath ( npmPath ) ;
203- const npmVersionSpawnResult : childProcess . SpawnSyncReturns < Buffer | string > = childProcess . spawnSync (
204- platformNpmPath ,
205- [ 'view' , `${ name } @${ version } ` , 'version' , '--no-update-notifier' , '--json' ] ,
206- spawnSyncOptions
207- ) ;
208-
209- if ( npmVersionSpawnResult . status !== 0 ) {
210- throw new Error ( `"npm view" returned error code ${ npmVersionSpawnResult . status } ` ) ;
211- }
204+
205+ const npmVersionSpawnResult : childProcess . SpawnSyncReturns < Buffer | string > =
206+ _runAsShellCommandAndConfirmSuccess (
207+ platformNpmPath ,
208+ [ 'view' , `${ name } @${ version } ` , 'version' , '--no-update-notifier' , '--json' ] ,
209+ spawnSyncOptions ,
210+ 'npm view'
211+ ) ;
212212
213213 const npmViewVersionOutput : string = npmVersionSpawnResult . stdout . toString ( ) ;
214214 const parsedVersionOutput : string | string [ ] = JSON . parse ( npmViewVersionOutput ) ;
@@ -355,22 +355,21 @@ function _installPackage(
355355 packageInstallFolder : string ,
356356 name : string ,
357357 version : string ,
358- command : 'install' | 'ci'
358+ npmCommand : 'install' | 'ci'
359359) : void {
360360 try {
361361 logger . info ( `Installing ${ name } ...` ) ;
362362 const npmPath : string = getNpmPath ( ) ;
363- const platformNpmPath : string = _getPlatformPath ( npmPath ) ;
364- const result : childProcess . SpawnSyncReturns < Buffer > = childProcess . spawnSync ( platformNpmPath , [ command ] , {
365- stdio : 'inherit' ,
366- cwd : packageInstallFolder ,
367- env : process . env
368- } ) ;
369-
370- if ( result . status !== 0 ) {
371- throw new Error ( `"npm ${ command } " encountered an error` ) ;
372- }
373-
363+ _runAsShellCommandAndConfirmSuccess (
364+ npmPath ,
365+ [ npmCommand ] ,
366+ {
367+ stdio : 'inherit' ,
368+ cwd : packageInstallFolder ,
369+ env : process . env
370+ } ,
371+ `npm ${ npmCommand } `
372+ ) ;
374373 logger . info ( `Successfully installed ${ name } @${ version } ` ) ;
375374 } catch ( e ) {
376375 throw new Error ( `Unable to install package: ${ e } ` ) ;
@@ -409,6 +408,40 @@ function _writeFlagFile(packageInstallFolder: string): void {
409408 }
410409}
411410
411+ /**
412+ * Run the specified command under the platform's shell and throw if it didn't succeed.
413+ */
414+ function _runAsShellCommandAndConfirmSuccess (
415+ command : string ,
416+ args : string [ ] ,
417+ options : childProcess . SpawnSyncOptions ,
418+ commandNameForLogging : string
419+ ) : childProcess . SpawnSyncReturns < string | Buffer < ArrayBufferLike > > {
420+ if ( _isWindows ( ) ) {
421+ ( { command, args } = convertCommandAndArgsToShell ( { command, args } ) ) ;
422+ }
423+
424+ const result : childProcess . SpawnSyncReturns < string | Buffer < ArrayBufferLike > > = childProcess . spawnSync (
425+ command ,
426+ args ,
427+ options
428+ ) ;
429+
430+ if ( result . status !== 0 ) {
431+ if ( result . status === undefined ) {
432+ if ( result . error ) {
433+ throw new Error ( `"${ commandNameForLogging } " failed: ${ result . error . message . toString ( ) } ` ) ;
434+ } else {
435+ throw new Error ( `"${ commandNameForLogging } " failed for an unknown reason` ) ;
436+ }
437+ } else {
438+ throw new Error ( `"${ commandNameForLogging } " returned error code ${ result . status } ` ) ;
439+ }
440+ }
441+
442+ return result ;
443+ }
444+
412445export function installAndRun (
413446 logger : ILogger ,
414447 packageName : string ,
@@ -456,12 +489,14 @@ export function installAndRun(
456489 const originalEnvPath : string = process . env . PATH || '' ;
457490 let result : childProcess . SpawnSyncReturns < Buffer > ;
458491 try {
459- // `npm` bin stubs on Windows are `.cmd` files
460- // Node.js will not directly invoke a `.cmd` file unless `shell` is set to `true`
461- const platformBinPath : string = _getPlatformPath ( binPath ) ;
492+ let command : string = binPath ;
493+ let args : string [ ] = packageBinArgs ;
494+ if ( _isWindows ( ) ) {
495+ ( { command, args } = convertCommandAndArgsToShell ( { command, args } ) ) ;
496+ }
462497
463498 process . env . PATH = [ binFolderPath , originalEnvPath ] . join ( path . delimiter ) ;
464- result = childProcess . spawnSync ( platformBinPath , packageBinArgs , {
499+ result = childProcess . spawnSync ( command , args , {
465500 stdio : 'inherit' ,
466501 windowsVerbatimArguments : false ,
467502 cwd : process . cwd ( ) ,
@@ -501,7 +536,8 @@ function _run(): void {
501536 throw new Error ( 'Unexpected exception: could not detect node path' ) ;
502537 }
503538
504- if ( path . basename ( scriptPath ) . toLowerCase ( ) !== 'install-run.js' ) {
539+ const scriptFileName : string = path . basename ( scriptPath ) . toLowerCase ( ) ;
540+ if ( scriptFileName !== 'install-run.js' && scriptFileName !== 'install-run' ) {
505541 // If install-run.js wasn't directly invoked, don't execute the rest of this function. Return control
506542 // to the script that (presumably) imported this file
507543
0 commit comments