@@ -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+ export 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 ,
@@ -285,26 +307,25 @@ export function silentNpm(
285307 {
286308 silent : true ,
287309 cwd : ( options as { cwd ?: string } | undefined ) ?. cwd ,
288- env : extractNpmEnv ( ) ,
289310 } ,
290311 'npm' ,
291312 params ,
292313 ) ;
293314 } else {
294- return _exec ( { silent : true , env : extractNpmEnv ( ) } , 'npm' , args as string [ ] ) ;
315+ return _exec ( { silent : true } , 'npm' , args as string [ ] ) ;
295316 }
296317}
297318
298319export function silentYarn ( ...args : string [ ] ) {
299- return _exec ( { silent : true , env : extractNpmEnv ( ) } , 'yarn' , args ) ;
320+ return _exec ( { silent : true } , 'yarn' , args ) ;
300321}
301322
302323export function npm ( ...args : string [ ] ) {
303- return _exec ( { env : extractNpmEnv ( ) } , 'npm' , args ) ;
324+ return _exec ( { } , 'npm' , args ) ;
304325}
305326
306327export function node ( ...args : string [ ] ) {
307- return _exec ( { } , 'node' , args ) ;
328+ return _exec ( { } , process . execPath , args ) ;
308329}
309330
310331export function git ( ...args : string [ ] ) {
@@ -314,3 +335,50 @@ export function git(...args: string[]) {
314335export function silentGit ( ...args : string [ ] ) {
315336 return _exec ( { silent : true } , 'git' , args ) ;
316337}
338+
339+ /**
340+ * Launch the given entry in an child process isolated to the test environment.
341+ *
342+ * The test environment includes the local NPM registry, isolated NPM globals,
343+ * the PATH variable only referencing the local node_modules and local NPM
344+ * registry (not the test runner or standard global node_modules).
345+ */
346+ export async function launchTestProcess ( entry : string , ...args : any [ ] ) {
347+ const tempRoot : string = getGlobalVariable ( 'tmp-root' ) ;
348+
349+ // Extract explicit environment variables for the test process.
350+ const env = {
351+ ...extractNpmEnv ( ) ,
352+ ...getGlobalVariablesEnv ( ) ,
353+ ...getNpmSandboxEnv ( ) ,
354+ } ;
355+
356+ // Modify the PATH environment variable...
357+ let paths = env . PATH . split ( delimiter ) ;
358+
359+ // Only include paths within the sandboxed test environment or external
360+ // non angular-cli paths such as /usr/bin for generic commands.
361+ paths = paths . filter ( ( p ) => p . startsWith ( tempRoot ) || ! p . includes ( 'angular-cli' ) ) ;
362+
363+ // Ensure the custom npm global bin is on the PATH
364+ // https://docs.npmjs.com/cli/v8/configuring-npm/folders#executables
365+ if ( process . platform . startsWith ( 'win' ) ) {
366+ paths . unshift ( env . NPM_CONFIG_PREFIX ) ;
367+ } else {
368+ paths . unshift ( join ( env . NPM_CONFIG_PREFIX , 'bin' ) ) ;
369+ }
370+
371+ // Add the project node modules bin to the front of the path
372+ paths . unshift ( join ( process . cwd ( ) , 'node_modules' , '.bin' ) ) ;
373+
374+ env . PATH = paths . join ( delimiter ) ;
375+
376+ return _exec (
377+ {
378+ env,
379+ cwd : process . cwd ( ) ,
380+ } ,
381+ process . execPath ,
382+ [ resolve ( __dirname , 'run_test_process' ) , entry , ...args ] ,
383+ ) ;
384+ }
0 commit comments