@@ -50,7 +50,7 @@ class ApplicationBuildError extends Error {
50
50
this . name = 'ApplicationBuildError' ;
51
51
}
52
52
}
53
- function injectKarmaReporter ( context , buildOptions , karmaConfig , subscriber ) {
53
+ function injectKarmaReporter ( context , buildOptions , buildIterator , karmaConfig , subscriber ) {
54
54
const reporterName = 'angular-progress-notifier' ;
55
55
class ProgressNotifierReporter {
56
56
emitter ;
@@ -61,10 +61,14 @@ function injectKarmaReporter(context, buildOptions, karmaConfig, subscriber) {
61
61
}
62
62
startWatchingBuild ( ) {
63
63
void ( async ( ) => {
64
- for await ( const buildOutput of ( 0 , private_1 . buildApplicationInternal ) ( {
65
- ...buildOptions ,
66
- watch : true ,
67
- } , context ) ) {
64
+ // This is effectively "for await of but skip what's already consumed".
65
+ let isDone = false ; // to mark the loop condition as "not constant".
66
+ while ( ! isDone ) {
67
+ const { done, value : buildOutput } = await buildIterator . next ( ) ;
68
+ if ( done ) {
69
+ isDone = true ;
70
+ break ;
71
+ }
68
72
if ( buildOutput . kind === private_1 . ResultKind . Failure ) {
69
73
subscriber . next ( { success : false , message : 'Build failed' } ) ;
70
74
}
@@ -96,11 +100,11 @@ function injectKarmaReporter(context, buildOptions, karmaConfig, subscriber) {
96
100
} ) ;
97
101
}
98
102
function execute ( options , context , karmaOptions , transforms = { } ) {
99
- return ( 0 , rxjs_1 . from ) ( initializeApplication ( options , context , karmaOptions , transforms ) ) . pipe ( ( 0 , rxjs_1 . switchMap ) ( ( [ karma , karmaConfig , buildOptions ] ) => new rxjs_1 . Observable ( ( subscriber ) => {
103
+ return ( 0 , rxjs_1 . from ) ( initializeApplication ( options , context , karmaOptions , transforms ) ) . pipe ( ( 0 , rxjs_1 . switchMap ) ( ( [ karma , karmaConfig , buildOptions , buildIterator ] ) => new rxjs_1 . Observable ( ( subscriber ) => {
100
104
// If `--watch` is explicitly enabled or if we are keeping the Karma
101
105
// process running, we should hook Karma into the build.
102
- if ( options . watch ?? ! karmaConfig . singleRun ) {
103
- injectKarmaReporter ( context , buildOptions , karmaConfig , subscriber ) ;
106
+ if ( buildIterator ) {
107
+ injectKarmaReporter ( context , buildOptions , buildIterator , karmaConfig , subscriber ) ;
104
108
}
105
109
// Complete the observable once the Karma server returns.
106
110
const karmaServer = new karma . Server ( karmaConfig , ( exitCode ) => {
@@ -179,9 +183,10 @@ async function initializeApplication(options, context, karmaOptions, transforms
179
183
styles : options . styles ,
180
184
polyfills : normalizePolyfills ( options . polyfills ) ,
181
185
webWorkerTsConfig : options . webWorkerTsConfig ,
186
+ watch : options . watch ?? ! karmaOptions . singleRun ,
182
187
} ;
183
188
// Build tests with `application` builder, using test files as entry points.
184
- const buildOutput = await first ( ( 0 , private_1 . buildApplicationInternal ) ( buildOptions , context ) ) ;
189
+ const [ buildOutput , buildIterator ] = await first ( ( 0 , private_1 . buildApplicationInternal ) ( buildOptions , context ) , { cancel : ! buildOptions . watch } ) ;
185
190
if ( buildOutput . kind === private_1 . ResultKind . Failure ) {
186
191
throw new ApplicationBuildError ( 'Build failed' ) ;
187
192
}
@@ -193,22 +198,27 @@ async function initializeApplication(options, context, karmaOptions, transforms
193
198
karmaOptions . files ??= [ ] ;
194
199
karmaOptions . files . push (
195
200
// Serve polyfills first.
196
- { pattern : `${ outputPath } /polyfills.js` , type : 'module' } ,
201
+ { pattern : `${ outputPath } /polyfills.js` , type : 'module' , watched : false } ,
197
202
// Serve global setup script.
198
- { pattern : `${ outputPath } /${ mainName } .js` , type : 'module' } ,
203
+ { pattern : `${ outputPath } /${ mainName } .js` , type : 'module' , watched : false } ,
199
204
// Serve all source maps.
200
- { pattern : `${ outputPath } /*.map` , included : false } ) ;
205
+ { pattern : `${ outputPath } /*.map` , included : false , watched : false } ) ;
201
206
if ( hasChunkOrWorkerFiles ( buildOutput . files ) ) {
202
207
karmaOptions . files . push (
203
208
// Allow loading of chunk-* files but don't include them all on load.
204
- { pattern : `${ outputPath } /{chunk,worker}-*.js` , type : 'module' , included : false } ) ;
209
+ {
210
+ pattern : `${ outputPath } /{chunk,worker}-*.js` ,
211
+ type : 'module' ,
212
+ included : false ,
213
+ watched : false ,
214
+ } ) ;
205
215
}
206
216
karmaOptions . files . push (
207
217
// Serve remaining JS on page load, these are the test entrypoints.
208
- { pattern : `${ outputPath } /*.js` , type : 'module' } ) ;
218
+ { pattern : `${ outputPath } /*.js` , type : 'module' , watched : false } ) ;
209
219
if ( options . styles ?. length ) {
210
220
// Serve CSS outputs on page load, these are the global styles.
211
- karmaOptions . files . push ( { pattern : `${ outputPath } /*.css` , type : 'css' } ) ;
221
+ karmaOptions . files . push ( { pattern : `${ outputPath } /*.css` , type : 'css' , watched : false } ) ;
212
222
}
213
223
const parsedKarmaConfig = await karma . config . parseConfig ( options . karmaConfig && path . resolve ( context . workspaceRoot , options . karmaConfig ) , transforms . karmaOptions ? transforms . karmaOptions ( karmaOptions ) : karmaOptions , { promiseConfig : true , throwErrors : true } ) ;
214
224
// Remove the webpack plugin/framework:
@@ -232,7 +242,7 @@ async function initializeApplication(options, context, karmaOptions, transforms
232
242
! parsedKarmaConfig . reporters ?. some ( ( r ) => r === 'coverage' || r === 'coverage-istanbul' ) ) {
233
243
parsedKarmaConfig . reporters = ( parsedKarmaConfig . reporters ?? [ ] ) . concat ( [ 'coverage' ] ) ;
234
244
}
235
- return [ karma , parsedKarmaConfig , buildOptions ] ;
245
+ return [ karma , parsedKarmaConfig , buildOptions , buildIterator ] ;
236
246
}
237
247
function hasChunkOrWorkerFiles ( files ) {
238
248
return Object . keys ( files ) . some ( ( filename ) => {
@@ -264,9 +274,17 @@ async function writeTestFiles(files, testDir) {
264
274
} ) ;
265
275
}
266
276
/** Returns the first item yielded by the given generator and cancels the execution. */
267
- async function first ( generator ) {
277
+ async function first ( generator , { cancel } ) {
278
+ if ( ! cancel ) {
279
+ const iterator = generator [ Symbol . asyncIterator ] ( ) ;
280
+ const firstValue = await iterator . next ( ) ;
281
+ if ( firstValue . done ) {
282
+ throw new Error ( 'Expected generator to emit at least once.' ) ;
283
+ }
284
+ return [ firstValue . value , iterator ] ;
285
+ }
268
286
for await ( const value of generator ) {
269
- return value ;
287
+ return [ value , null ] ;
270
288
}
271
289
throw new Error ( 'Expected generator to emit at least once.' ) ;
272
290
}
0 commit comments