8000 feat(optimizer): holdUntilCrawlEnd option (#15244) · vitejs/vite@b7c6629 · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Commit b7c6629

Browse files
authored
feat(optimizer): holdUntilCrawlEnd option (#15244)
1 parent aa7916a commit b7c6629

File tree

4 files changed

+124
-13
lines changed

4 files changed

+124
-13
lines changed

docs/config/dep-optimization-options.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ Certain options are omitted since changing them would not be compatible with Vit
6262

6363
Set to `true` to force dependency pre-bundling, ignoring previously cached optimized dependencies.
6464

65+
## optimizeDeps.holdUntilCrawlEnd
66+
67+
- **Experimental**
68+
- **Type:** `boolean`
69+
- **Default:** `true`
70+
71+
When enabled, it will hold the first optimized deps results until all static imports are crawled on cold start. This avoids the need for full-page reloads when new dependencies are discovered and they trigger the generation of new common chunks. If all dependencies are found by the scanner plus the explicitely defined ones in `include`, it is better to disable this option to let the browser process more requests in parallel.
72+
6573
## optimizeDeps.disabled
6674

6775
- **Deprecated**

packages/vite/src/node/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,7 @@ export async function resolveConfig(
766766
packageCache,
767767
createResolver,
768768
optimizeDeps: {
769+
holdUntilCrawlEnd: true,
769770
...optimizeDeps,
770771
esbuildOptions: {
771772
preserveSymlinks: resolveOptions.preserveSymlinks,

packages/vite/src/node/optimizer/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,17 @@ export interface DepOptimizationConfig {
132132
* @experimental
133133
*/
134134
noDiscovery?: boolean
135+
/**
136+
* When enabled, it will hold the first optimized deps results until all static
137+
* imports are crawled on cold start. This avoids the need for full-page reloads
138+
* when new dependencies are discovered and they trigger the generation of new
139+
* common chunks. If all dependencies are found by the scanner plus the explicitely
140+
* defined ones in `include`, it is better to disable this option to let the
141+
* browser process more requests in parallel.
142+
* @default true
143+
* @experimental
144+
*/
145+
holdUntilCrawlEnd?: boolean
135146
}
136147

137148
export type DepOptimizationOptions = DepOptimizationConfig & {

packages/vite/src/node/optimizer/optimizer.ts

Lines changed: 104 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ async function createDepsOptimizer(
9393
let metadata =
9494
cachedMetadata || initDepsOptimizerMetadata(config, ssr, sessionTimestamp)
9595

96+
const options = getDepOptimizationConfig(config, ssr)
97+
98+
const { noDiscovery, holdUntilCrawlEnd } = options
99+
96100
const depsOptimizer: DepsOptimizer = {
97101
metadata,
98102
registerMissingImport,
@@ -103,7 +107,7 @@ async function createDepsOptimizer(
103107
`${depInfo.file}?v=${depInfo.browserHash}`,
104108
delayDepsOptimizerUntil,
105109
close,
106-
options: getDepOptimizationConfig(config, ssr),
110+
options,
107111
}
108112

109113
depsOptimizerMap.set(config, depsOptimizer)
@@ -126,6 +130,23 @@ async function createDepsOptimizer(
126130
}
127131
}
128132

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+
129150
let depOptimizationProcessing = promiseWithResolvers<void>()
130151
let depOptimizationProcessingQueue: PromiseWithResolvers<void>[] = []
131152
const resolveEnqueuedProcessingPromises = () => {
@@ -140,6 +161,7 @@ async function createDepsOptimizer(
140161
let currentlyProcessing = false
141162

142163
let firstRunCalled = !!cachedMetadata
164+
let warnAboutMissedDependencies = false
143165

144166
// If this is a cold run, we wait for static imports discovered
145167
// from the first request before resolving to minimize full page reloads.
@@ -180,25 +202,25 @@ async function createDepsOptimizer(
180202

181203
// Initialize discovered deps with manually added optimizeDeps.include info
182204

183-
const deps: Record<string, string> = {}
184-
await addManuallyIncludedOptimizeDeps(deps, config, ssr)
205+
const manuallyIncludedDeps: Record<string, string> = {}
206+
await addManuallyIncludedOptimizeDeps(manuallyIncludedDeps, config, ssr)
185207

186-
const discovered = toDiscoveredDependencies(
208+
const manuallyIncludedDepsInfo = toDiscoveredDependencies(
187209
config,
188-
deps,
210+
manuallyIncludedDeps,
189211
ssr,
190212
sessionTimestamp,
191213
)
192214

193-
for (const depInfo of Object.values(discovered)) {
215+
for (const depInfo of Object.values(manuallyIncludedDepsInfo)) {
194216
addOptimizedDepInfo(metadata, 'discovered', {
195217
...depInfo,
196218
processing: depOptimizationProcessing.promise,
197219
})
198220
newDepsDiscovered = true
199221
}
200222

201-
if (config.optimizeDeps.noDiscovery) {
223+
if (noDiscovery) {
202224
// We don't need to scan for dependencies or wait for the static crawl to end
203225
// Run the first optimization run immediately
204226
runOptimizer()
@@ -214,6 +236,13 @@ async function createDepsOptimizer(
214236
const deps = await discover.result
215237
discover = undefined
216238

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+
217246
// Add these dependencies to the discovered list, as these are currently
218247
// used by the preAliasPlugin to support aliased and optimized deps.
219248
// This is also used by the CJS externalization heuristics in legacy mode
@@ -224,12 +253,31 @@ async function createDepsOptimizer(
224253
}
225254

226255
const knownDeps = prepareKnownDeps()
256+
startNextDiscoveredBatch()
227257

228258
// 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
232260
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+
}
233281
} catch (e) {
234282
logger.error(e.stack || e.message)
235283
} finally {
@@ -394,6 +442,16 @@ async function createDepsOptimizer(
394442
newDepsToLogHandle = setTimeout(() => {
395443
newDepsToLogHandle = undefined
396444
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+
}
397455
}, 2 * debounceMs)
398456
} else {
399457
debug(
@@ -426,6 +484,16 @@ async function createDepsOptimizer(
426484
if (newDepsToLogHandle) clearTimeout(newDepsToLogHandle)
427485
newDepsToLogHandle = undefined
428486
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+
}
429497
}
430498

431499
logger.info(
@@ -562,7 +630,7 @@ async function createDepsOptimizer(
562630

563631
function debouncedProcessing(timeout = debounceMs) {
564632
// Debounced rerun, let other missing dependencies be discovered before
565-
// the running next optimizeDeps
633+
// the next optimizeDeps run
566634
enqueuedRerun = undefined
567635
if (debounceProcessingHandle) clearTimeout(debounceProcessingHandle)
568636
if (newDepsToLogHandle) clearTimeout(newDepsToLogHandle)
@@ -593,8 +661,17 @@ async function createDepsOptimizer(
593661
await depsOptimizer.scanProcessing
594662

595663
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
598675
currentlyProcessing = false
599676

600677
const crawlDeps = Object.keys(metadata.discovered)
@@ -649,6 +726,20 @@ async function createDepsOptimizer(
649726
startNextDiscoveredBatch()
650727
runOptimizer(result)
651728
}
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+
}
652743
} else {
653744
const crawlDeps = Object.keys(metadata.discovered)
654745
currentlyProcessing = false

0 commit comments

Comments
 (0)
0