66 * found in the LICENSE file at https://angular.io/license
77 */
88
9- import { logging } from '@angular-devkit/core' ;
10- import { spawnSync } from 'child_process' ;
9+ import { spawn , spawnSync } from 'child_process' ;
1110import { existsSync , mkdtempSync , readFileSync , realpathSync , writeFileSync } from 'fs' ;
1211import { tmpdir } from 'os' ;
1312import { join , resolve } from 'path' ;
1413import * as rimraf from 'rimraf' ;
1514import { PackageManager } from '../lib/config/workspace-schema' ;
16- import { colors } from '../utilities/color' ;
1715import { NgAddSaveDepedency } from '../utilities/package-metadata' ;
16+ import { Spinner } from './spinner' ;
1817
1918interface PackageManagerOptions {
2019 silent : string ;
@@ -24,14 +23,13 @@ interface PackageManagerOptions {
2423 noLockfile : string ;
2524}
2625
27- export function installPackage (
26+ export async function installPackage (
2827 packageName : string ,
29- logger : logging . Logger | undefined ,
3028 packageManager : PackageManager = PackageManager . Npm ,
3129 save : Exclude < NgAddSaveDepedency , false > = true ,
3230 extraArgs : string [ ] = [ ] ,
3331 cwd = process . cwd ( ) ,
34- ) {
32+ ) : Promise < 1 | 0 > {
3533 const packageManagerArgs = getPackageManagerArguments ( packageManager ) ;
3634
3735 const installArgs : string [ ] = [
@@ -40,40 +38,48 @@ export function installPackage(
4038 packageManagerArgs . silent ,
4139 ] ;
4240
43- logger ?. info ( colors . green ( `Installing packages for tooling via ${ packageManager } .` ) ) ;
41+ const spinner = new Spinner ( ) ;
42+ spinner . start ( 'Installing package...' ) ;
4443
4544 if ( save === 'devDependencies' ) {
4645 installArgs . push ( packageManagerArgs . saveDev ) ;
4746 }
47+ const bufferedOutput : { stream : NodeJS . WriteStream ; data : Buffer } [ ] = [ ] ;
4848
49- const { status, stderr, stdout, error } = spawnSync (
50- packageManager ,
51- [ ...installArgs , ...extraArgs ] ,
52- {
49+ return new Promise ( ( resolve , reject ) => {
50+ const childProcess = spawn ( packageManager , [ ...installArgs , ...extraArgs ] , {
5351 stdio : 'pipe' ,
5452 shell : true ,
55- encoding : 'utf8' ,
5653 cwd,
57- } ,
58- ) ;
59-
60- if ( status !== 0 ) {
61- let errorMessage = ( ( error && error . message ) || stderr || stdout || '' ) . trim ( ) ;
62- if ( errorMessage ) {
63- errorMessage += '\n' ;
64- }
65- throw new Error ( errorMessage + `Package install failed${ errorMessage ? ', see above' : '' } .` ) ;
66- }
67-
68- logger ?. info ( colors . green ( `Installed packages for tooling via ${ packageManager } .` ) ) ;
54+ } ) . on ( 'close' , ( code : number ) => {
55+ if ( code === 0 ) {
56+ spinner . succeed ( 'Package successfully installed.' ) ;
57+ resolve ( 0 ) ;
58+ } else {
59+ spinner . stop ( ) ;
60+ bufferedOutput . forEach ( ( { stream, data } ) => stream . write ( data ) ) ;
61+ spinner . fail ( 'Package install failed, see above.' ) ;
62+ reject ( 1 ) ;
63+ }
64+ } ) ;
65+
66+ childProcess . stdout ?. on ( 'data' , ( data : Buffer ) =>
67+ bufferedOutput . push ( { stream : process . stdout , data : data } ) ,
68+ ) ;
69+ childProcess . stderr ?. on ( 'data' , ( data : Buffer ) =>
70+ bufferedOutput . push ( { stream : process . stderr , data : data } ) ,
71+ ) ;
72+ } ) ;
6973}
7074
71- export function installTempPackage (
75+ export async function installTempPackage (
7276 packageName : string ,
73- logger : logging . Logger | undefined ,
7477 packageManager : PackageManager = PackageManager . Npm ,
7578 extraArgs ?: string [ ] ,
76- ) : string {
79+ ) : Promise < {
80+ status : 1 | 0 ;
81+ tempPath : string ;
82+ } > {
7783 const tempPath = mkdtempSync ( join ( realpathSync ( tmpdir ( ) ) , 'angular-cli-packages-' ) ) ;
7884
7985 // clean up temp directory on process exit
@@ -113,23 +119,26 @@ export function installTempPackage(
113119 packageManagerArgs . noLockfile ,
114120 ] ;
115121
116- installPackage ( packageName , logger , packageManager , true , installArgs , tempPath ) ;
117-
118- return tempNodeModules ;
122+ return {
123+ status : await installPackage ( packageName , packageManager , true , installArgs , tempPath ) ,
124+ tempPath,
125+ } ;
119126}
120127
121- export function runTempPackageBin (
128+ export async function runTempPackageBin (
122129 packageName : string ,
123- logger : logging . Logger ,
124130 packageManager : PackageManager = PackageManager . Npm ,
125131 args : string [ ] = [ ] ,
126- ) : number {
127- const tempNodeModulesPath = installTempPackage ( packageName , logger , packageManager ) ;
132+ ) : Promise < number > {
133+ const { status : code , tempPath } = await installTempPackage ( packageName , packageManager ) ;
134+ if ( code !== 0 ) {
135+ return code ;
136+ }
128137
129138 // Remove version/tag etc... from package name
130139 // Ex: @angular /cli@latest -> @angular/cli
131140 const packageNameNoVersion = packageName . substring ( 0 , packageName . lastIndexOf ( '@' ) ) ;
132- const pkgLocation = join ( tempNodeModulesPath , packageNameNoVersion ) ;
141+ const pkgLocation = join ( tempPath , packageNameNoVersion ) ;
133142 const packageJsonPath = join ( pkgLocation , 'package.json' ) ;
134143
135144 // Get a binary location for this package
0 commit comments