@@ -93,6 +93,10 @@ async function createDepsOptimizer(
93
93
let metadata =
94
94
cachedMetadata || initDepsOptimizerMetadata ( config , ssr , sessionTimestamp )
95
95
96
+ const options = getDepOptimizationConfig ( config , ssr )
97
+
98
+ const { noDiscovery, holdUntilCrawlEnd } = options
99
+
96
100
const depsOptimizer : DepsOptimizer = {
97
101
metadata,
98
102
registerMissingImport,
@@ -103,7 +107,7 @@ async function createDepsOptimizer(
103
107
`${ depInfo . file } ?v=${ depInfo . browserHash } ` ,
104
108
delayDepsOptimizerUntil,
105
109
close,
106
- options : getDepOptimizationConfig ( config , ssr ) ,
110
+ options,
107
111
}
108
112
109
113
depsOptimizerMap . set ( config , depsOptimizer )
@@ -126,6 +130,23 @@ async function createDepsOptimizer(
126
130
}
127
131
}
128
132
133
+ let discoveredDepsWhileScanning : string [ ] = [ ]
134
+ const logDiscoveredDepsWhileScanning = ( ) => {
135
+ if ( discoveredDepsWhileScanning . length ) {
136
+ config . logger . info (
137
+ colors . green (
138
+ `✨ discovered while scanning: ${ depsLogString (
139
+ discoveredDepsWhileScanning ,
140
+ ) } `,
141
+ ) ,
142
+ {
143
+ timestamp : true ,
144
+ } ,
145
+ )
146
+ discoveredDepsWhileScanning = [ ]
147
+ }
148
+ }
149
+
129
150
let depOptimizationProcessing = promiseWithResolvers < void > ( )
130
151
let depOptimizationProcessingQueue : PromiseWithResolvers < void > [ ] = [ ]
131
152
const resolveEnqueuedProcessingPromises = ( ) => {
@@ -140,6 +161,7 @@ async function createDepsOptimizer(
140
161
let currentlyProcessing = false
141
162
142
163
let firstRunCalled = ! ! cachedMetadata
164
+ let warnAboutMissedDependencies = false
143
165
144
166
// If this is a cold run, we wait for static imports discovered
145
167
// from the first request before resolving to minimize full page reloads.
@@ -180,25 +202,25 @@ async function createDepsOptimizer(
180
202
181
203
// Initialize discovered deps with manually added optimizeDeps.include info
182
204
183
- const deps : Record < string , string > = { }
184
- await addManuallyIncludedOptimizeDeps ( deps , config , ssr )
205
+ const manuallyIncludedDeps : Record < string , string > = { }
206
+ await addManuallyIncludedOptimizeDeps ( manuallyIncludedDeps , config , ssr )
185
207
186
- const discovered = toDiscoveredDependencies (
208
+ const manuallyIncludedDepsInfo = toDiscoveredDependencies (
187
209
config ,
188
- deps ,
210
+ manuallyIncludedDeps ,
189
211
ssr ,
190
212
sessionTimestamp ,
191
213
)
192
214
193
- for ( const depInfo of Object . values ( discovered ) ) {
215
+ for ( const depInfo of Object . values ( manuallyIncludedDepsInfo ) ) {
194
216
addOptimizedDepInfo ( metadata , 'discovered' , {
195
217
...depInfo ,
196
218
processing : depOptimizationProcessing . promise ,
197
219
} )
198
220
newDepsDiscovered = true
199
221
}
200
222
201
- if ( config . optimizeDeps . noDiscovery ) {
223
+ if ( noDiscovery ) {
202
224
// We don't need to scan for dependencies or wait for the static crawl to end
203
225
// Run the first optimization run immediately
204
226
runOptimizer ( )
@@ -214,6 +236,13 @@ async function createDepsOptimizer(
214
236
const deps = await discover . result
215
237
discover = undefined
216
238
239
+ const manuallyIncluded = Object . keys ( manuallyIncludedDepsInfo )
240
+ discoveredDepsWhileScanning . push (
241
+ ...Object . keys ( metadata . discovered ) . filter (
242
+ ( dep ) => ! deps [ dep ] && ! manuallyIncluded . includes ( dep ) ,
243
+ ) ,
244
+ )
245
+
217
246
// Add these dependencies to the discovered list, as these are currently
218
247
// used by the preAliasPlugin to support aliased and optimized deps.
219
248
// This is also used by the CJS externalization heuristics in legacy mode
@@ -224,12 +253,31 @@ async function createDepsOptimizer(
224
253
}
225
254
226
255
const knownDeps = prepareKnownDeps ( )
256
+ startNextDiscoveredBatch ( )
227
257
228
258
// For dev, we run the scanner and the first optimization
229
- // run on the background, but we wait until crawling has ended
230
- // to decide if we send this result to the browser or we need to
231
- // do another optimize step
259
+ // run on the background
232
260
optimizationResult = runOptimizeDeps ( config , knownDeps , ssr )
261
+
262
+ // If the holdUntilCrawlEnd stratey is used, we wait until crawling has
263
+ // ended to decide if we send this result to the browser or we need to
264
+ // do another optimize step
265
+ if ( ! holdUntilCrawlEnd ) {
266
+ // If not, we release the result to the browser as soon as the scanner
267
+ // is done. If the scanner missed any dependency, and a new dependency
268
+ // is discovered while crawling static imports, then there will be a
269
+ // full-page reload if new common chunks are generated between the old
270
+ // and new optimized deps.
271
+ optimizationResult . result . then ( ( result ) => {
272
+ // Check if the crawling of static imports has already finished. In that
273
+ // case, the result is handled by the onCrawlEnd callback
274
+ if ( ! crawlEndFinder ) return
275
+
276
+ optimizationResult = undefined // signal that we'll be using the result
277
+
278
+ runOptimizer ( result )
279
+ } )
280
+ }
233
281
} catch ( e ) {
234
282
logger . error ( e . stack || e . message )
235
283
} finally {
@@ -394,6 +442,16 @@ async function createDepsOptimizer(
394
442
newDepsToLogHandle = setTimeout ( ( ) => {
395
443
newDepsToLogHandle = undefined
396
444
logNewlyDiscoveredDeps ( )
445
+ if ( warnAboutMissedDependencies ) {
446
+ logDiscoveredDepsWhileScanning ( )
447
+ config . logger . info (
448
+ colors . magenta (
449
+ `❗ add these dependencies to optimizeDeps.include to speed up cold start` ,
450
+ ) ,
451
+ { timestamp : true } ,
452
+ )
453
+ warnAboutMissedDependencies = false
454
+ }
397
455
} , 2 * debounceMs )
398
456
} else {
399
457
debug (
@@ -426,6 +484,16 @@ async function createDepsOptimizer(
426
484
if ( newDepsToLogHandle ) clearTimeout ( newDepsToLogHandle )
427
485
newDepsToLogHandle = undefined
428
486
logNewlyDiscoveredDeps ( )
487
+ if ( warnAboutMissedDependencies ) {
488
+ logDiscoveredDepsWhileScanning ( )
489
+ config . logger . info (
490
+ colors . magenta (
491
+ `❗ add these dependencies to optimizeDeps.include to avoid a full page reload during cold start` ,
492
+ ) ,
493
+ { timestamp : true } ,
494
+ )
495
+ warnAboutMissedDependencies = false
496
+ }
429
497
}
430
498
431
499
logger . info (
@@ -562,7 +630,7 @@ async function createDepsOptimizer(
562
630
563
631
function debouncedProcessing ( timeout = debounceMs ) {
564
632
// Debounced rerun, let other missing dependencies be discovered before
565
- // the running next optimizeDeps
633
+ // the next optimizeDeps run
566
634
enqueuedRerun = undefined
567
635
if ( debounceProcessingHandle ) clearTimeout ( debounceProcessingHandle )
568
636
if ( newDepsToLogHandle ) clearTimeout ( newDepsToLogHandle )
@@ -593,8 +661,17 @@ async function createDepsOptimizer(
593
661
await depsOptimizer . scanProcessing
594
662
595
663
if ( optimizationResult && ! config . optimizeDeps . noDiscovery ) {
596
- const result = await optimizationResult . result
597
- optimizationResult = undefined
664
+ // In the holdUntilCrawlEnd strategy, we don't release the result of the
665
+ // post-scanner optimize step to the browser until we reach this point
666
+ // If there are new dependencies, we do another optimize run, if not, we
667
+ // use the post-scanner optimize result
668
+ // If holdUntilCrawlEnd is false and we reach here, it means that the
669
+ // scan+optimize step finished after crawl end. We follow the same
670
+ // process as in the holdUntilCrawlEnd in this case.
671
+ const afterScanResult = optimizationResult . result
672
+ optimizationResult = undefined // signal that we'll be using the result
673
+
674
+ const result = await afterScanResult
598
675
currentlyProcessing = false
599
676
600
677
const crawlDeps = Object . keys ( metadata . discovered )
@@ -649,6 +726,20 @@ async function createDepsOptimizer(
649
726
startNextDiscoveredBatch ( )
650
727
runOptimizer ( result )
651
728
}
729
+ } else if ( ! holdUntilCrawlEnd ) {
730
+ // The post-scanner optimize result has been released to the browser
731
+ // If new deps have been discovered, issue a regular rerun of the
732
+ // optimizer. A full page reload may still be avoided if the new
733
+ // optimize result is compatible in this case
734
+ if ( newDepsDiscovered ) {
735
+ debug ?.(
736
+ colors . green (
737
+ `✨ new dependencies were found while crawling static imports, re-running optimizer` ,
738
+ ) ,
739
+ )
740
+ warnAboutMissedDependencies = true
741
+ debouncedProcessing ( 0 )
742
+ }
652
743
} else {
653
744
const crawlDeps = Object . keys ( metadata . discovered )
654
745
currentlyProcessing = false
0 commit comments