Skip to content

Commit fbbf8fe

Browse files
patak-devsapphi-red
andauthoredMar 1, 2023
fix: properly clean up optimization temp folder (#12237)
Co-authored-by: 翠 / green <green@sapphi.red>
1 parent b93297e commit fbbf8fe

File tree

3 files changed

+165
-78
lines changed

3 files changed

+165
-78
lines changed
 

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

+148-70
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import path from 'node:path'
33
import { performance } from 'node:perf_hooks'
44
import _debug from 'debug'
55
import colors from 'picocolors'
6-
import type { BuildOptions as EsbuildBuildOptions } from 'esbuild'
7-
import { build } from 'esbuild'
6+
import type { BuildContext, BuildOptions as EsbuildBuildOptions } from 'esbuild'
7+
import esbuild, { build } from 'esbuild'
88
import { init, parse } from 'es-module-lexer'
99
import { createFilter } from '@rollup/pluginutils'
1010
import { getDepOptimizationConfig } from '../config'
@@ -248,7 +248,7 @@ export async function optimizeDeps(
248248

249249
const depsInfo = toDiscoveredDependencies(config, deps, ssr)
250250

251-
const result = await runOptimizeDeps(config, depsInfo)
251+
const result = await runOptimizeDeps(config, depsInfo).result
252252

253253
await result.commit()
254254

@@ -299,7 +299,7 @@ export async function optimizeServerSsrDeps(
299299

300300
const depsInfo = toDiscoveredDependencies(config, deps, true)
301301

302-
const result = await runOptimizeDeps(config, depsInfo, true)
302+
const result = await runOptimizeDeps(config, depsInfo, true).result
303303

304304
await result.commit()
305305

@@ -452,13 +452,15 @@ export function depsLogString(qualifiedIds: string[]): string {
452452
* Internally, Vite uses this function to prepare a optimizeDeps run. When Vite starts, we can get
453453
* the metadata and start the server without waiting for the optimizeDeps processing to be completed
454454
*/
455-
export async function runOptimizeDeps(
455+
export function runOptimizeDeps(
456456
resolvedConfig: ResolvedConfig,
457457
depsInfo: Record<string, OptimizedDepInfo>,
458458
ssr: boolean = resolvedConfig.command === 'build' &&
459459
!!resolvedConfig.build.ssr,
460-
): Promise<DepOptimizationResult> {
461-
const isBuild = resolvedConfig.command === 'build'
460+
): {
461+
cancel: () => Promise<void>
462+
result: Promise<DepOptimizationResult>
463+
} {
462464
const config: ResolvedConfig = {
463465
...resolvedConfig,
464466
command: 'build',
@@ -496,22 +498,154 @@ export async function runOptimizeDeps(
496498

497499
const qualifiedIds = Object.keys(depsInfo)
498500

499-
const processingResult: DepOptimizationResult = {
501+
let cleaned = false
502+
const cleanUp = () => {
503+
if (!cleaned) {
504+
cleaned = true
505+
fs.rmSync(processingCacheDir, { recursive: true, force: true })
506+
}
507+
}
508+
const createProcessingResult = () => ({
500509
metadata,
501510
async commit() {
511+
if (cleaned) {
512+
throw new Error(
513+
`Vite Internal Error: Can't commit optimizeDeps processing result, it has already been cancelled.`,
514+
)
515+
}
502516
// Write metadata file, delete `deps` folder and rename the `processing` folder to `deps`
503517
// Processing is done, we can now replace the depsCacheDir with processingCacheDir
504518
// Rewire the file paths from the temporal processing dir to the final deps cache dir
505519
await removeDir(depsCacheDir)
506520
await renameDir(processingCacheDir, depsCacheDir)
507521
},
508-
cancel() {
509-
fs.rmSync(processingCacheDir, { recursive: true, force: true })
522+
cancel: cleanUp,
523+
})
524+
525+
if (!qualifiedIds.length) {
526+
return {
527+
cancel: async () => cleanUp(),
528+
result: Promise.resolve(createProcessingResult()),
529+
}
530+
}
531+
532+
const start = performance.now()
533+
534+
const preparedRun = prepareEsbuildOptimizerRun(
535+
resolvedConfig,
536+
depsInfo,
537+
ssr,
538+
processingCacheDir,
539+
)
540+
541+
const result = preparedRun.then(({ context, idToExports }) => {
542+
return context
543+
.rebuild()
544+
.then((result) => {
545+
const meta = result.metafile!
546+
547+
// the paths in `meta.outputs` are relative to `process.cwd()`
548+
const processingCacheDirOutputPath = path.relative(
549+
process.cwd(),
550+
processingCacheDir,
551+
)
552+
553+
for (const id in depsInfo) {
554+
const output = esbuildOutputFromId(
555+
meta.outputs,
556+
id,
557+
processingCacheDir,
558+
)
559+
560+
const { exportsData, ...info } = depsInfo[id]
561+
addOptimizedDepInfo(metadata, 'optimized', {
562+
...info,
563+
// We only need to hash the output.imports in to check for stability, but adding the hash
564+
// and file path gives us a unique hash that may be useful for other things in the future
565+
fileHash: getHash(
566+
metadata.hash +
567+
depsInfo[id].file +
568+
JSON.stringify(output.imports),
569+
),
570+
browserHash: metadata.browserHash,
571+
// After bundling we have more information and can warn the user about legacy packages
572+
// that require manual configuration
573+
needsInterop: needsInterop(
574+
config,
575+
ssr,
576+
id,
577+
idToExports[id],
578+
output,
579+
),
580+
})
581+
}
582+
583+
for (const o of Object.keys(meta.outputs)) {
584+
if (!o.match(jsMapExtensionRE)) {
585+
const id = path
586+
.relative(processingCacheDirOutputPath, o)
587+
.replace(jsExtensionRE, '')
588+
const file = getOptimizedDepPath(id, resolvedConfig, ssr)
589+
if (
590+
!findOptimizedDepInfoInRecord(
591+
metadata.optimized,
592+
(depInfo) => depInfo.file === file,
593+
)
594+
) {
595+
addOptimizedDepInfo(metadata, 'chunks', {
596+
id,
597+
file,
598+
needsInterop: false,
599+
browserHash: metadata.browserHash,
600+
})
601+
}
602+
}
603+
}
604+
605+
const dataPath = path.join(processingCacheDir, '_metadata.json')
606+
writeFile(
607+
dataPath,
608+
stringifyDepsOptimizerMetadata(metadata, depsCacheDir),
609+
)
610+
611+
debug(`deps bundled in ${(performance.now() - start).toFixed(2)}ms`)
612+
613+
return createProcessingResult()
614+
})
615+
.finally(() => {
616+
return context.dispose().catch((e) => {
617+
config.logger.error('error happed during context.dispose', e)
618+
})
619+
})
620+
})
621+
622+
result.catch(() => {
623+
cleanUp()
624+
})
625+
626+
return {
627+
async cancel() {
628+
const { context } = await preparedRun
629+
await context.cancel()
630+
cleanUp()
510631
},
632+
result,
511633
}
634+
}
512635

513-
if (!qualifiedIds.length) {
514-
return processingResult
636+
async function prepareEsbuildOptimizerRun(
637+
resolvedConfig: ResolvedConfig,
638+
depsInfo: Record<string, OptimizedDepInfo>,
639+
ssr: boolean,
640+
processingCacheDir: string,
641+
): Promise<{
642+
context: BuildContext
643+
idToExports: Record<string, ExportsData>
644+
}> {
645+
const isBuild = resolvedConfig.command === 'build'
646+
const config: ResolvedConfig = {
647+
...resolvedConfig,
648+
command: 'build',
515649
}
516650

517651
// esbuild generates nested directory output with lowest common ancestor base
@@ -588,9 +722,7 @@ export async function runOptimizeDeps(
588722
}
589723
plugins.push(esbuildDepPlugin(flatIdDeps, external, config, ssr))
590724

591-
const start = performance.now()
592-
593-
const result = await build({
725+
const context = await esbuild.context({
594726
absWorkingDir: process.cwd(),
595727
entryPoints: Object.keys(flatIdDeps),
596728
bundle: true,
@@ -624,61 +756,7 @@ export async function runOptimizeDeps(
624756
...esbuildOptions.supported,
625757
},
626758
})
627-
628-
const meta = result.metafile!
629-
630-
// the paths in `meta.outputs` are relative to `process.cwd()`
631-
const processingCacheDirOutputPath = path.relative(
632-
process.cwd(),
633-
processingCacheDir,
634-
)
635-
636-
for (const id in depsInfo) {
637-
const output = esbuildOutputFromId(meta.outputs, id, processingCacheDir)
638-
639-
const { exportsData, ...info } = depsInfo[id]
640-
addOptimizedDepInfo(metadata, 'optimized', {
641-
...info,
642-
// We only need to hash the output.imports in to check for stability, but adding the hash
643-
// and file path gives us a unique hash that may be useful for other things in the future
644-
fileHash: getHash(
645-
metadata.hash + depsInfo[id].file + JSON.stringify(output.imports),
646-
),
647-
browserHash: metadata.browserHash,
648-
// After bundling we have more information and can warn the user about legacy packages
649-
// that require manual configuration
650-
needsInterop: needsInterop(config, ssr, id, idToExports[id], output),
651-
})
652-
}
653-
654-
for (const o of Object.keys(meta.outputs)) {
655-
if (!o.match(jsMapExtensionRE)) {
656-
const id = path
657-
.relative(processingCacheDirOutputPath, o)
658-
.replace(jsExtensionRE, '')
659-
const file = getOptimizedDepPath(id, resolvedConfig, ssr)
660-
if (
661-
!findOptimizedDepInfoInRecord(
662-
metadata.optimized,
663-
(depInfo) => depInfo.file === file,
664-
)
665-
) {
666-
addOptimizedDepInfo(metadata, 'chunks', {
667-
id,
668-
file,
669-
needsInterop: false,
670-
browserHash: metadata.browserHash,
671-
})
672-
}
673-
}
674-
}
675-
676-
const dataPath = path.join(processingCacheDir, '_metadata.json')
677-
writeFile(dataPath, stringifyDepsOptimizerMetadata(metadata, depsCacheDir))
678-
679-
debug(`deps bundled in ${(performance.now() - start).toFixed(2)}ms`)
680-
681-
return processingResult
759+
return { context, idToExports }
682760
}
683761

684762
export async function findKnownImports(

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

+14-7
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,13 @@ async function createDepsOptimizer(
160160
// If there wasn't a cache or it is outdated, we need to prepare a first run
161161
let firstRunCalled = !!cachedMetadata
162162

163-
let postScanOptimizationResult: Promise<DepOptimizationResult> | undefined
163+
let optimizationResult:
164+
| {
165+
cancel: () => Promise<void>
166+
result: Promise<DepOptimizationResult>
167+
}
168+
| undefined
169+
164170
let discover:
165171
| {
166172
cancel: () => Promise<void>
@@ -174,7 +180,7 @@ async function createDepsOptimizer(
174180
await Promise.allSettled([
175181
discover?.cancel(),
176182
depsOptimizer.scanProcessing,
177-
postScanOptimizationResult,
183+
optimizationResult?.cancel(),
178184
optimizingNewDeps,
179185
])
180186
}
@@ -240,7 +246,7 @@ async function createDepsOptimizer(
240246
// run on the background, but we wait until crawling has ended
241247
// to decide if we send this result to the browser or we need to
242248
// do another optimize step
243-
postScanOptimizationResult = runOptimizeDeps(config, knownDeps)
249+
optimizationResult = runOptimizeDeps(config, knownDeps)
244250
} catch (e) {
245251
logger.error(e.stack || e.message)
246252
} finally {
@@ -281,7 +287,8 @@ async function createDepsOptimizer(
281287

282288
startNextDiscoveredBatch()
283289

284-
return await runOptimizeDeps(config, knownDeps)
290+
optimizationResult = runOptimizeDeps(config, knownDeps)
291+
return await optimizationResult.result
285292
}
286293

287294
function prepareKnownDeps() {
@@ -604,9 +611,9 @@ async function createDepsOptimizer(
604611
// It normally should be over by the time crawling of user code ended
605612
await depsOptimizer.scanProcessing
606613

607-
if (!isBuild && postScanOptimizationResult) {
608-
const result = await postScanOptimizationResult
609-
postScanOptimizationResult = undefined
614+
if (!isBuild && optimizationResult) {
615+
const result = await optimizationResult.result
616+
optimizationResult = undefined
610617

611618
const scanDeps = Object.keys(result.metadata.optimized)
612619

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@ export function scanImports(config: ResolvedConfig): {
9898
}
9999
})
100100
.finally(() => {
101-
context.dispose()
101+
return context.dispose().catch((e) => {
102+
config.logger.error('error happed during context.dispose', e)
103+
})
102104
})
103105
})
104106
.catch(async (e) => {

0 commit comments

Comments
 (0)
Please sign in to comment.