Skip to content

Commit fba82d0

Browse files
authored
fix: respect optimize deps entries (#8489)
1 parent 51e9195 commit fba82d0

File tree

12 files changed

+141
-104
lines changed

12 files changed

+141
-104
lines changed

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

+7
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,18 @@ export type ExportsData = {
4545
export interface DepsOptimizer {
4646
metadata: DepOptimizationMetadata
4747
scanProcessing?: Promise<void>
48+
4849
registerMissingImport: (id: string, resolved: string) => OptimizedDepInfo
4950
run: () => void
51+
5052
isOptimizedDepFile: (id: string) => boolean
5153
isOptimizedDepUrl: (url: string) => boolean
5254
getOptimizedDepId: (depInfo: OptimizedDepInfo) => string
55+
56+
delayDepsOptimizerUntil: (id: string, done: () => Promise<any>) => void
57+
registerWorkersSource: (id: string) => void
58+
resetRegisteredIds: () => void
59+
5360
options: DepOptimizationOptions
5461
}
5562

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

+89
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import colors from 'picocolors'
22
import _debug from 'debug'
3+
import glob from 'fast-glob'
34
import { getHash } from '../utils'
5+
import { transformRequest } from '../server/transformRequest'
46
import type { ResolvedConfig, ViteDevServer } from '..'
57
import {
68
addOptimizedDepInfo,
@@ -65,6 +67,9 @@ export async function initDepsOptimizer(
6567
isOptimizedDepUrl: createIsOptimizedDepUrl(config),
6668
getOptimizedDepId: (depInfo: OptimizedDepInfo) =>
6769
isBuild ? depInfo.file : `${depInfo.file}?v=${depInfo.browserHash}`,
70+
registerWorkersSource,
71+
delayDepsOptimizerUntil,
72+
resetRegisteredIds,
6873
options: config.optimizeDeps
6974
}
7075

@@ -101,8 +106,12 @@ export async function initDepsOptimizer(
101106
let enqueuedRerun: (() => void) | undefined
102107
let currentlyProcessing = false
103108

109+
// Only pretransform optimizeDeps.entries on cold start
110+
let optimizeDepsEntriesVisited = !!cachedMetadata
111+
104112
// If there wasn't a cache or it is outdated, we need to prepare a first run
105113
let firstRunCalled = !!cachedMetadata
114+
106115
if (!cachedMetadata) {
107116
if (!scan) {
108117
// Initialize discovered deps with manually added optimizeDeps.include info
@@ -496,5 +505,85 @@ export async function initDepsOptimizer(
496505
}, timeout)
497506
}
498507

508+
const runOptimizerIfIdleAfterMs = 100
509+
510+
let registeredIds: { id: string; done: () => Promise<any> }[] = []
511+
let seenIds = new Set<string>()
512+
let workersSources = new Set<string>()
513+
let waitingOn: string | undefined
514+
515+
function resetRegisteredIds() {
516+
registeredIds = []
517+
seenIds = new Set<string>()
518+
workersSources = new Set<string>()
519+
waitingOn = undefined
520+
}
521+
522+
function registerWorkersSource(id: string): void {
523+
workersSources.add(id)
524+
if (waitingOn === id) {
525+
waitingOn = undefined
526+
}
527+
}
528+
529+
function delayDepsOptimizerUntil(id: string, done: () => Promise<any>): void {
530+
if (!depsOptimizer.isOptimizedDepFile(id) && !seenIds.has(id)) {
531+
seenIds.add(id)
532+
registeredIds.push({ id, done })
533+
runOptimizerWhenIdle()
534+
}
535+
if (server && !optimizeDepsEntriesVisited) {
536+
optimizeDepsEntriesVisited = true
537+
preTransformOptimizeDepsEntries(server)
538+
}
539+
}
540+
541+
function runOptimizerWhenIdle() {
542+
if (!waitingOn) {
543+
const next = registeredIds.pop()
544+
if (next) {
545+
waitingOn = next.id
546+
const afterLoad = () => {
547+
waitingOn = undefined
548+
if (registeredIds.length > 0) {
549+
runOptimizerWhenIdle()
550+
} else if (!workersSources.has(next.id)) {
551+
getDepsOptimizer(config)?.run()
552+
}
553+
}
554+
next
555+
.done()
556+
.then(() => {
557+
setTimeout(
558+
afterLoad,
559+
registeredIds.length > 0 ? 0 : runOptimizerIfIdleAfterMs
560+
)
561+
})
562+
.catch(afterLoad)
563+
}
564+
}
565+
}
566+
499567
return depsOptimizer
500568
}
569+
570+
export async function preTransformOptimizeDepsEntries(
571+
server: ViteDevServer
572+
): Promise<void> {
573+
const { config } = server
574+
const { entries } = config.optimizeDeps
575+
if (entries) {
576+
const explicitEntries = await glob(entries, {
577+
cwd: config.root,
578+
ignore: ['**/node_modules/**', `**/${config.build.outDir}/**`],
579+
absolute: true
580+
})
581+
// TODO: should we restrict the entries to JS and HTML like the
582+
// scanner did? I think we can let the user chose any entry
583+
for (const entry of explicitEntries) {
584+
transformRequest(entry, server, { ssr: false }).catch((e) => {
585+
config.logger.error(e.message)
586+
})
587+
}
588+
}
589+
}

packages/vite/src/node/plugins/importAnalysis.ts

+6-7
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,7 @@ import {
5353
optimizedDepNeedsInterop
5454
} from '../optimizer'
5555
import { checkPublicFile } from './asset'
56-
import {
57-
ERR_OUTDATED_OPTIMIZED_DEP,
58-
delayDepsOptimizerUntil
59-
} from './optimizedDeps'
56+
import { ERR_OUTDATED_OPTIMIZED_DEP } from './optimizedDeps'
6057
import { isCSSRequest, isDirectCSSRequest } from './css'
6158
import { browserExternalId } from './resolve'
6259

@@ -599,7 +596,9 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
599596
)
600597

601598
// pre-transform known direct imports
602-
// TODO: we should also crawl dynamic imports
599+
// TODO: should we also crawl dynamic imports? or the experience is good enough to allow
600+
// users to chose their tradeoffs by explicitily setting optimizeDeps.entries for the
601+
// most common dynamic imports
603602
if (config.server.preTransformRequests && staticImportedUrls.size) {
604603
staticImportedUrls.forEach(({ url, id }) => {
605604
url = unwrapId(removeImportQuery(url)).replace(
@@ -614,8 +613,8 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
614613
// Unexpected error, log the issue but avoid an unhandled exception
615614
config.logger.error(e.message)
616615
})
617-
if (!config.optimizeDeps.devScan) {
618-
delayDepsOptimizerUntil(config, id, () => request)
616+
if (depsOptimizer && !config.optimizeDeps.devScan) {
617+
depsOptimizer.delayDepsOptimizerUntil(id, () => request)
619618
}
620619
})
621620
}

packages/vite/src/node/plugins/optimizedDeps.ts

+2-92
Original file line numberDiff line numberDiff line change
@@ -13,100 +13,10 @@ export const ERR_OUTDATED_OPTIMIZED_DEP = 'ERR_OUTDATED_OPTIMIZED_DEP'
1313
const isDebug = process.env.DEBUG
1414
const debug = createDebugger('vite:optimize-deps')
1515

16-
const runOptimizerIfIdleAfterMs = 100
17-
18-
interface RunProcessingInfo {
19-
ids: { id: string; done: () => Promise<any> }[]
20-
seenIds: Set<string>
21-
workersSources: Set<string>
22-
waitingOn: string | undefined
23-
}
24-
25-
const runProcessingInfoMap = new WeakMap<ResolvedConfig, RunProcessingInfo>()
26-
27-
function initRunProcessingInfo(config: ResolvedConfig) {
28-
config = config.mainConfig || config
29-
const runProcessingInfo = {
30-
ids: [],
31-
seenIds: new Set<string>(),
32-
workersSources: new Set<string>(),
33-
waitingOn: undefined
34-
}
35-
runProcessingInfoMap.set(config, runProcessingInfo)
36-
return runProcessingInfo
37-
}
38-
39-
function getRunProcessingInfo(config: ResolvedConfig): RunProcessingInfo {
40-
return (
41-
runProcessingInfoMap.get(config.mainConfig || config) ??
42-
initRunProcessingInfo(config)
43-
)
44-
}
45-
46-
export function registerWorkersSource(
47-
config: ResolvedConfig,
48-
id: string
49-
): void {
50-
const info = getRunProcessingInfo(config)
51-
info.workersSources.add(id)
52-
if (info.waitingOn === id) {
53-
info.waitingOn = undefined
54-
}
55-
}
56-
57-
export function delayDepsOptimizerUntil(
58-
config: ResolvedConfig,
59-
id: string,
60-
done: () => Promise<any>
61-
): void {
62-
const info = getRunProcessingInfo(config)
63-
if (
64-
!getDepsOptimizer(config)?.isOptimizedDepFile(id) &&
65-
!info.seenIds.has(id)
66-
) {
67-
info.seenIds.add(id)
68-
info.ids.push({ id, done })
69-
runOptimizerWhenIdle(config)
70-
}
71-
}
72-
73-
function runOptimizerWhenIdle(config: ResolvedConfig) {
74-
const info = getRunProcessingInfo(config)
75-
if (!info.waitingOn) {
76-
const next = info.ids.pop()
77-
if (next) {
78-
info.waitingOn = next.id
79-
const afterLoad = () => {
80-
info.waitingOn = undefined
81-
if (info.ids.length > 0) {
82-
runOptimizerWhenIdle(config)
83-
} else if (!info.workersSources.has(next.id)) {
84-
getDepsOptimizer(config)?.run()
85-
}
86-
}
87-
next
88-
.done()
89-
.then(() => {
90-
setTimeout(
91-
afterLoad,
92-
info.ids.length > 0 ? 0 : runOptimizerIfIdleAfterMs
93-
)
94-
})
95-
.catch(afterLoad)
96-
}
97-
}
98-
}
99-
10016
export function optimizedDepsPlugin(config: ResolvedConfig): Plugin {
10117
return {
10218
name: 'vite:optimized-deps',
10319

104-
buildStart() {
105-
if (!config.isWorker) {
106-
initRunProcessingInfo(config)
107-
}
108-
},
109-
11020
async resolveId(id) {
11121
if (getDepsOptimizer(config)?.isOptimizedDepFile(id)) {
11222
return id
@@ -174,7 +84,7 @@ export function optimizedDepsBuildPlugin(config: ResolvedConfig): Plugin {
17484

17585
buildStart() {
17686
if (!config.isWorker) {
177-
initRunProcessingInfo(config)
87+
getDepsOptimizer(config)?.resetRegisteredIds()
17888
}
17989
},
18090

@@ -185,7 +95,7 @@ export function optimizedDepsBuildPlugin(config: ResolvedConfig): Plugin {
18595
},
18696

18797
transform(_code, id) {
188-
delayDepsOptimizerUntil(config, id, async () => {
98+
getDepsOptimizer(config)?.delayDepsOptimizerUntil(id, async () => {
18999
await this.load({ id })
190100
})
191101
},

packages/vite/src/node/plugins/worker.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import {
1313
parseRequest
1414
} from '../utils'
1515
import { onRollupWarning } from '../build'
16+
import { getDepsOptimizer } from '../optimizer'
1617
import { fileToUrl } from './asset'
17-
import { registerWorkersSource } from './optimizedDeps'
1818

1919
interface WorkerCache {
2020
// save worker all emit chunk avoid rollup make the same asset unique.
@@ -269,7 +269,7 @@ export function webWorkerPlugin(config: ResolvedConfig): Plugin {
269269
: 'module'
270270
const workerOptions = workerType === 'classic' ? '' : ',{type: "module"}'
271271
if (isBuild) {
272-
registerWorkersSource(config, id)
272+
getDepsOptimizer(config)?.registerWorkersSource(id)
273273
if (query.inline != null) {
274274
const chunk = await bundleWorkerEntry(config, id, query)
275275
// inline as blob data url

packages/vite/src/node/plugins/workerImportMetaUrl.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ import {
1212
parseRequest,
1313
transformResult
1414
} from '../utils'
15+
import { getDepsOptimizer } from '../optimizer'
1516
import type { WorkerType } from './worker'
1617
import { WORKER_FILE_ID, workerFileToUrl } from './worker'
1718
import { fileToUrl } from './asset'
18-
import { registerWorkersSource } from './optimizedDeps'
1919

2020
const ignoreFlagRE = /\/\*\s*@vite-ignore\s*\*\//
2121

@@ -123,7 +123,7 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
123123

124124
let url: string
125125
if (isBuild) {
126-
registerWorkersSource(config, id)
126+
getDepsOptimizer(config)?.registerWorkersSource(id)
127127
url = await workerFileToUrl(config, file, query)
128128
} else {
129129
url = await fileToUrl(cleanUrl(file), config, this)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// written in cjs, optimization should convert this to esm
2+
module.exports = 'added-in-entries'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "added-in-entries",
3+
"private": true,
4+
"version": "1.0.0",
5+
"main": "index.js"
6+
}

playground/optimize-deps/entry.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import msg from 'added-in-entries'
2+
3+
// This is an entry file that is added to optimizeDeps.entries
4+
// When the deps aren't cached, these entries are also processed
5+
// to discover dependencies in them. This should only be needed
6+
// for code splitted sections that are commonly visited after
7+
// first load where a full-reload wants to be avoided at the expense
8+
// of extra processing on cold start. Another option is to add
9+
// the missing dependencies to optimizeDeps.include directly
10+
11+
console.log(msg)

playground/optimize-deps/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"dep-with-builtin-module-cjs": "file:./dep-with-builtin-module-cjs",
2323
"dep-with-builtin-module-esm": "file:./dep-with-builtin-module-esm",
2424
"dep-with-dynamic-import": "file:./dep-with-dynamic-import",
25+
"added-in-entries": "file:./added-in-entries",
2526
"lodash-es": "^4.17.21",
2627
"nested-exclude": "file:./nested-exclude",
2728
"phoenix": "^1.6.10",

playground/optimize-deps/vite.config.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ module.exports = {
3333
}
3434
}
3535
]
36-
}
36+
},
37+
entries: ['entry.js']
3738
},
3839

3940
build: {

0 commit comments

Comments
 (0)