77 */
88
99import { Logger , PathMappings , process as mainNgcc } from '@angular/compiler-cli/ngcc' ;
10+ import { spawnSync } from 'child_process' ;
1011import { accessSync , constants , existsSync } from 'fs' ;
1112import * as path from 'path' ;
1213import * as ts from 'typescript' ;
@@ -49,6 +50,52 @@ export class NgccProcessor {
4950 }
5051 }
5152
53+ /** Process the entire node modules tree. */
54+ process ( ) {
55+ const timeLabel = 'NgccProcessor.process' ;
56+ time ( timeLabel ) ;
57+
58+ const corePackage = this . tryResolvePackage ( '@angular/core' , this . _nodeModulesDirectory ) ;
59+
60+ // If the package.json is read only we should skip calling NGCC.
61+ // With Bazel when running under sandbox the filesystem is read-only.
62+ if ( corePackage && isReadOnlyFile ( corePackage ) ) {
63+ timeEnd ( timeLabel ) ;
64+
65+ return ;
66+ }
67+
68+ // We spawn instead of using the API because:
69+ // - NGCC Async uses clustering which is problematic when used via the API which means
70+ // that we cannot setup multiple cluster masters with different options.
71+ // - We will not be able to have concurrent builds otherwise Ex: App-Shell,
72+ // as NGCC will create a lock file for both builds and it will cause builds to fails.
73+ const { status, error } = spawnSync (
74+ process . execPath ,
75+ [
76+ require . resolve ( '@angular/compiler-cli/ngcc/main-ngcc.js' ) ,
77+ '--source' , /** basePath */
78+ this . _nodeModulesDirectory ,
79+ '--properties' , /** propertiesToConsider */
80+ ...this . propertiesToConsider ,
81+ '--first-only' , /** compileAllFormats */
82+ '--create-ivy-entry-points' , /** createNewEntryPointFormats */
83+ '--async' ,
84+ ] ,
85+ {
86+ stdio : [ 'inherit' , process . stderr , process . stderr ] ,
87+ } ,
88+ ) ;
89+
90+ if ( status !== 0 ) {
91+ const errorMessage = error ?. message || '' ;
92+ throw new Error ( errorMessage + `NGCC failed${ errorMessage ? ', see above' : '' } .` ) ;
93+ }
94+
95+ timeEnd ( timeLabel ) ;
96+ }
97+
98+ /** Process a module and it's depedencies. */
5299 processModule (
53100 moduleName : string ,
54101 resolvedModule : ts . ResolvedModule | ts . ResolvedTypeReferenceDirective ,
@@ -61,18 +108,9 @@ export class NgccProcessor {
61108 }
62109
63110 const packageJsonPath = this . tryResolvePackage ( moduleName , resolvedFileName ) ;
64- if ( ! packageJsonPath ) {
65- // add it to processed so the second time round we skip this.
66- this . _processedModules . add ( resolvedFileName ) ;
67-
68- return ;
69- }
70-
71111 // If the package.json is read only we should skip calling NGCC.
72112 // With Bazel when running under sandbox the filesystem is read-only.
73- try {
74- accessSync ( packageJsonPath , constants . W_OK ) ;
75- } catch {
113+ if ( ! packageJsonPath || isReadOnlyFile ( packageJsonPath ) ) {
76114 // add it to processed so the second time round we skip this.
77115 this . _processedModules . add ( resolvedFileName ) ;
78116
@@ -145,9 +183,9 @@ class NgccLogger implements Logger {
145183 constructor (
146184 private readonly compilationWarnings : ( Error | string ) [ ] ,
147185 private readonly compilationErrors : ( Error | string ) [ ] ,
148- ) { }
186+ ) { }
149187
150- debug ( ..._args : string [ ] ) { }
188+ debug ( ..._args : string [ ] ) { }
151189
152190 info ( ...args : string [ ] ) {
153191 // Log to stderr because it's a progress-like info message.
@@ -162,3 +200,13 @@ class NgccLogger implements Logger {
162200 this . compilationErrors . push ( new Error ( args . join ( ' ' ) ) ) ;
163201 }
164202}
203+
204+ function isReadOnlyFile ( fileName : string ) : boolean {
205+ try {
206+ accessSync ( fileName , constants . W_OK ) ;
207+
208+ return false ;
209+ } catch {
210+ return true ;
211+ }
212+ }
0 commit comments