@@ -3,12 +3,14 @@ import { SpawnOptions } from 'child_process';
33import * as child_process from 'child_process' ;
44import { concat , defer , EMPTY , from } from 'rxjs' ;
55import { repeat , takeLast } from 'rxjs/operators' ;
6- import { getGlobalVariable } from './env' ;
6+ import { getGlobalVariable , getGlobalVariablesEnv } from './env' ;
77import { catchError } from 'rxjs/operators' ;
8+ import { delimiter , join , resolve } from 'path' ;
89const treeKill = require ( 'tree-kill' ) ;
910
1011interface ExecOptions {
1112 silent ?: boolean ;
13+ announce ?: boolean ;
1214 waitForMatch ?: RegExp ;
1315 env ?: { [ varname : string ] : string } ;
1416 stdin ?: string ;
@@ -24,18 +26,22 @@ export type ProcessOutput = {
2426 stderr : string ;
2527} ;
2628
27- function _exec ( options : ExecOptions , cmd : string , args : string [ ] ) : Promise < ProcessOutput > {
29+ async function _exec ( options : ExecOptions , cmd : string , args : string [ ] ) : Promise < ProcessOutput > {
2830 // Create a separate instance to prevent unintended global changes to the color configuration
2931 // Create function is not defined in the typings. See: https://github.com/doowb/ansi-colors/pull/44
3032 const colors = ( ansiColors as typeof ansiColors & { create : ( ) => typeof ansiColors } ) . create ( ) ;
3133
34+ const announce = options . announce ?? true ;
35+
3236 let stdout = '' ;
3337 let stderr = '' ;
3438 const cwd = options . cwd ?? process . cwd ( ) ;
3539 const env = options . env ;
36- console . log (
37- `==========================================================================================` ,
38- ) ;
40+ if ( announce ) {
41+ console . log (
42+ `==========================================================================================` ,
43+ ) ;
44+ }
3945
4046 args = args . filter ( ( x ) => x !== undefined ) ;
4147 const flags = [
@@ -46,9 +52,15 @@ function _exec(options: ExecOptions, cmd: string, args: string[]): Promise<Proce
4652 . join ( ', ' )
4753 . replace ( / ^ ( .+ ) $ / , ' [$1]' ) ; // Proper formatting.
4854
49- console . log ( colors . blue ( `Running \`${ cmd } ${ args . map ( ( x ) => `"${ x } "` ) . join ( ' ' ) } \`${ flags } ...` ) ) ;
50- console . log ( colors . blue ( `CWD: ${ cwd } ` ) ) ;
51- console . log ( colors . blue ( `ENV: ${ JSON . stringify ( env ) } ` ) ) ;
55+ if ( announce ) {
56+ console . log (
57+ colors . blue ( `Running \`${ cmd } ${ args . map ( ( x ) => `"${ x } "` ) . join ( ' ' ) } \`${ flags } ...` ) ,
58+ ) ;
59+ console . log ( colors . blue ( `CWD: ${ cwd } ` ) ) ;
60+
61+ console . log ( colors . blue ( `ENV: ${ JSON . stringify ( env ) } ` ) ) ;
62+ }
63+
5264 const spawnOptions : SpawnOptions = {
5365 cwd,
5466 ...( env ? { env } : { } ) ,
@@ -140,7 +152,7 @@ function _exec(options: ExecOptions, cmd: string, args: string[]): Promise<Proce
140152 } ) ;
141153}
142154
143- export function extractNpmEnv ( ) {
155+ export function extractNpmEnv ( ) : { [ k : string ] : string } {
144156 return Object . keys ( process . env )
145157 . filter ( ( v ) => NPM_CONFIG_RE . test ( v ) )
146158 . reduce (
@@ -154,6 +166,16 @@ export function extractNpmEnv() {
154166 ) ;
155167}
156168
169+ function getNpmSandboxEnv ( ) : { [ k : string ] : string } {
170+ const tempRoot : string = getGlobalVariable ( 'tmp-root' ) ;
171+ const npmModulesPrefix : string = getGlobalVariable ( 'npm-root' ) ;
172+
173+ return {
174+ NPM_CONFIG_USERCONFIG : join ( tempRoot , '.npmrc' ) ,
175+ NPM_CONFIG_PREFIX : npmModulesPrefix ,
176+ } ;
177+ }
178+
157179export function waitForAnyProcessOutputToMatch (
158180 match : RegExp ,
159181 timeout = 30000 ,
@@ -299,22 +321,21 @@ export function silentNpm(
299321 {
300322 silent : true ,
301323 cwd : ( options as { cwd ?: string } | undefined ) ?. cwd ,
302- env : extractNpmEnv ( ) ,
303324 } ,
304325 'npm' ,
305326 params ,
306327 ) ;
307328 } else {
308- return _exec ( { silent : true , env : extractNpmEnv ( ) } , 'npm' , args as string [ ] ) ;
329+ return _exec ( { silent : true } , 'npm' , args as string [ ] ) ;
309330 }
310331}
311332
312333export function silentYarn ( ...args : string [ ] ) {
313- return _exec ( { silent : true , env : extractNpmEnv ( ) } , 'yarn' , args ) ;
334+ return _exec ( { silent : true } , 'yarn' , args ) ;
314335}
315336
316337export function npm ( ...args : string [ ] ) {
317- return _exec ( { env : extractNpmEnv ( ) } , 'npm' , args ) ;
338+ return _exec ( { } , 'npm' , args ) ;
318339}
319340
320341export function node ( ...args : string [ ] ) {
@@ -328,3 +349,40 @@ export function git(...args: string[]) {
328349export function silentGit ( ...args : string [ ] ) {
329350 return _exec ( { silent : true } , 'git' , args ) ;
330351}
352+
353+ /**
354+ * Launch the given entry in an child process isolated to the test environment.
355+ *
356+ * The test environment includes the local NPM registry, isolated NPM globals,
357+ * the PATH variable only referencing the local node_modules and local NPM
358+ * registry (not the test runner or standard global node_modules).
359+ */
360+ export async function launchTestProcess ( entry : string , ...args : any [ ] ) {
361+ const tempRoot : string = getGlobalVariable ( 'tmp-root' ) ;
362+
363+ // Extract explicit environment variables for the test process.
364+ const env = {
365+ ...extractNpmEnv ( ) ,
366+ ...getGlobalVariablesEnv ( ) ,
367+ ...getNpmSandboxEnv ( ) ,
368+ } ;
369+
370+ // Modify the PATH environment variable...
371+ let paths = process . env . PATH . split ( delimiter ) ;
372+
373+ // Only include paths within the sandboxed test environment or external
374+ // non angular-cli paths such as /usr/bin for generic commands.
375+ paths = paths . filter ( ( p ) => p . startsWith ( tempRoot ) || ! p . includes ( 'angular-cli' ) ) ;
376+
377+ // Ensure the custom npm global bin is on the PATH
378+ // https://docs.npmjs.com/cli/v8/configuring-npm/folders#executables
379+ if ( process . platform . startsWith ( 'win' ) ) {
380+ paths . unshift ( env . NPM_CONFIG_PREFIX ) ;
381+ } else {
382+ paths . unshift ( join ( env . NPM_CONFIG_PREFIX , 'bin' ) ) ;
383+ }
384+
385+ env . PATH = paths . join ( delimiter ) ;
386+
387+ return _exec ( { env } , process . execPath , [ resolve ( __dirname , 'run_test_process' ) , entry , ...args ] ) ;
388+ }
0 commit comments