@@ -3,8 +3,8 @@ import path from 'node:path'
3
3
import { performance } from 'node:perf_hooks'
4
4
import _debug from 'debug'
5
5
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'
8
8
import { init , parse } from 'es-module-lexer'
9
9
import { createFilter } from '@rollup/pluginutils'
10
10
import { getDepOptimizationConfig } from '../config'
@@ -248,7 +248,7 @@ export async function optimizeDeps(
248
248
249
249
const depsInfo = toDiscoveredDependencies ( config , deps , ssr )
250
250
251
- const result = await runOptimizeDeps ( config , depsInfo )
251
+ const result = await runOptimizeDeps ( config , depsInfo ) . result
252
252
253
253
await result . commit ( )
254
254
@@ -299,7 +299,7 @@ export async function optimizeServerSsrDeps(
299
299
300
300
const depsInfo = toDiscoveredDependencies ( config , deps , true )
301
301
302
- const result = await runOptimizeDeps ( config , depsInfo , true )
302
+ const result = await runOptimizeDeps ( config , depsInfo , true ) . result
303
303
304
304
await result . commit ( )
305
305
@@ -452,13 +452,15 @@ export function depsLogString(qualifiedIds: string[]): string {
452
452
* Internally, Vite uses this function to prepare a optimizeDeps run. When Vite starts, we can get
453
453
* the metadata and start the server without waiting for the optimizeDeps processing to be completed
454
454
*/
455
- export async function runOptimizeDeps (
455
+ export function runOptimizeDeps (
456
456
resolvedConfig : ResolvedConfig ,
457
457
depsInfo : Record < string , OptimizedDepInfo > ,
458
458
ssr : boolean = resolvedConfig . command === 'build' &&
459
459
! ! resolvedConfig . build . ssr ,
460
- ) : Promise < DepOptimizationResult > {
461
- const isBuild = resolvedConfig . command === 'build'
460
+ ) : {
461
+ cancel : ( ) => Promise < void >
462
+ result : Promise < DepOptimizationResult >
463
+ } {
462
464
const config : ResolvedConfig = {
463
465
...resolvedConfig ,
464
466
command : 'build' ,
@@ -496,22 +498,154 @@ export async function runOptimizeDeps(
496
498
497
499
const qualifiedIds = Object . keys ( depsInfo )
498
500
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 = ( ) => ( {
500
509
metadata,
501
510
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
+ }
502
516
// Write metadata file, delete `deps` folder and rename the `processing` folder to `deps`
503
517
// Processing is done, we can now replace the depsCacheDir with processingCacheDir
504
518
// Rewire the file paths from the temporal processing dir to the final deps cache dir
505
519
await removeDir ( depsCacheDir )
506
520
await renameDir ( processingCacheDir , depsCacheDir )
507
521
} ,
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 ( )
510
631
} ,
632
+ result,
511
633
}
634
+ }
512
635
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' ,
515
649
}
516
650
517
651
// esbuild generates nested directory output with lowest common ancestor base
@@ -588,9 +722,7 @@ export async function runOptimizeDeps(
588
722
}
589
723
plugins . push ( esbuildDepPlugin ( flatIdDeps , external , config , ssr ) )
590
724
591
- const start = performance . now ( )
592
-
593
- const result = await build ( {
725
+ const context = await esbuild . context ( {
594
726
absWorkingDir : process . cwd ( ) ,
595
727
entryPoints : Object . keys ( flatIdDeps ) ,
596
728
bundle : true ,
@@ -624,61 +756,7 @@ export async function runOptimizeDeps(
624
756
...esbuildOptions . supported ,
625
757
} ,
626
758
} )
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 }
682
760
}
683
761
684
762
export async function findKnownImports (
0 commit comments