@@ -26,6 +26,11 @@ import { getDepsOptimizer, optimizedDepNeedsInterop } from '../optimizer'
26
26
import { removedPureCssFilesCache } from './css'
27
27
import { interopNamedImports } from './importAnalysis'
28
28
29
+ type FileDep = {
30
+ url : string
31
+ runtime : boolean
32
+ }
33
+
29
34
/**
30
35
* A flag for injected helpers. This flag will be set to `false` if the output
31
36
* target is not native es - so that injected helper logic can be conditionally
@@ -450,6 +455,26 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin {
450
455
const s = new MagicString ( code )
451
456
const rewroteMarkerStartPos = new Set ( ) // position of the leading double quote
452
457
458
+ const fileDeps : FileDep [ ] = [ ]
459
+ const addFileDep = (
460
+ url : string ,
461
+ runtime : boolean = false ,
462
+ ) : number => {
463
+ const index = fileDeps . findIndex ( ( dep ) => dep . url === url )
464
+ if ( index === - 1 ) {
465
+ return fileDeps . push ( { url, runtime } ) - 1
466
+ } else {
467
+ return index
468
+ }
469
+ }
470
+ const getFileDep = ( index : number ) : FileDep => {
471
+ const fileDep = fileDeps [ index ]
472
+ if ( ! fileDep ) {
473
+ throw new Error ( `Cannot find file dep at index ${ index } ` )
474
+ }
475
+ return fileDep
476
+ }
477
+
453
478
if ( imports . length ) {
454
479
for ( let index = 0 ; index < imports . length ; index ++ ) {
455
480
// To handle escape sequences in specifier strings, the .n field will be provided where possible.
@@ -467,7 +492,7 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin {
467
492
if ( rawUrl [ 0 ] === `"` && rawUrl [ rawUrl . length - 1 ] === `"` )
468
493
url = rawUrl . slice ( 1 , - 1 )
469
494
}
470
- const deps : Set < string > = new Set ( )
495
+ const deps : Set < number > = new Set ( )
471
496
let hasRemovedPureCssChunk = false
472
497
473
498
let normalizedFile : string | undefined = undefined
@@ -487,12 +512,12 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin {
487
512
analyzed . add ( filename )
488
513
const chunk = bundle [ filename ] as OutputChunk | undefined
489
514
if ( chunk ) {
490
- deps . add ( chunk . fileName )
515
+ deps . add ( addFileDep ( chunk . fileName ) )
491
516
chunk . imports . forEach ( addDeps )
492
517
// Ensure that the css imported by current chunk is loaded after the dependencies.
493
518
// So the style of current chunk won't be overwritten unexpectedly.
494
519
chunk . viteMetadata ! . importedCss . forEach ( ( file ) => {
495
- deps . add ( file )
520
+ deps . add ( addFileDep ( file ) )
496
521
} )
497
522
} else {
498
523
const removedPureCssFiles =
@@ -501,7 +526,7 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin {
501
526
if ( chunk ) {
502
527
if ( chunk . viteMetadata ! . importedCss . size ) {
503
528
chunk . viteMetadata ! . importedCss . forEach ( ( file ) => {
504
- deps . add ( file )
529
+ deps . add ( addFileDep ( file ) )
505
530
} )
506
531
hasRemovedPureCssChunk = true
507
532
}
@@ -535,74 +560,96 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin {
535
560
? modulePreload === false
536
561
? // CSS deps use the same mechanism as module preloads, so even if disabled,
537
562
// we still need to pass these deps to the preload helper in dynamic imports.
538
- [ ...deps ] . filter ( ( d ) => d . endsWith ( '.css' ) )
563
+ [ ...deps ] . filter ( ( d ) =>
564
+ getFileDep ( d ) . url . endsWith ( '.css' ) ,
565
+ )
539
566
: [ ...deps ]
540
567
: [ ]
541
568
542
- let renderedDeps : string [ ]
569
+ let renderedDeps : number [ ]
543
570
if ( normalizedFile && customModulePreloadPaths ) {
544
571
const { modulePreload } = config . build
545
572
const resolveDependencies = modulePreload
546
573
? modulePreload . resolveDependencies
547
574
: undefined
548
- let resolvedDeps : string [ ]
575
+ let resolvedDeps : number [ ]
549
576
if ( resolveDependencies ) {
550
577
// We can't let the user remove css deps as these aren't really preloads, they are just using
551
578
// the same mechanism as module preloads for this chunk
552
- const cssDeps : string [ ] = [ ]
553
- const otherDeps : string [ ] = [ ]
579
+ const cssDeps : number [ ] = [ ]
580
+ const otherDeps : number [ ] = [ ]
554
581
for ( const dep of depsArray ) {
555
- ; ( dep . endsWith ( '.css' ) ? cssDeps : otherDeps ) . push ( dep )
582
+ if ( getFileDep ( dep ) . url . endsWith ( '.css' ) ) {
583
+ cssDeps . push ( dep )
584
+ } else {
585
+ otherDeps . push ( dep )
586
+ }
556
587
}
557
588
resolvedDeps = [
558
- ...resolveDependencies ( normalizedFile , otherDeps , {
559
- hostId : file ,
560
- hostType : 'js' ,
561
- } ) ,
589
+ ...resolveDependencies (
590
+ normalizedFile ,
591
+ otherDeps . map ( ( otherDep ) => getFileDep ( otherDep ) . url ) ,
592
+ {
593
+ hostId : file ,
594
+ hostType : 'js' ,
595
+ } ,
596
+ ) . map ( ( otherDep ) => addFileDep ( otherDep ) ) ,
562
597
...cssDeps ,
563
598
]
564
599
} else {
565
600
resolvedDeps = depsArray
566
601
}
567
602
568
- renderedDeps = resolvedDeps . map ( ( dep : string ) => {
603
+ renderedDeps = resolvedDeps . map ( ( dep : number ) => {
569
604
const replacement = toOutputFilePathInJS (
570
- dep ,
605
+ getFileDep ( dep ) . url ,
571
606
'asset' ,
572
607
chunk . fileName ,
573
608
'js' ,
574
609
config ,
575
610
toRelativePath ,
576
611
)
577
- const replacementString =
578
- typeof replacement === 'string'
579
- ? JSON . stringify ( replacement )
580
- : replacement . runtime
581
612
582
- return replacementString
613
+ if ( typeof replacement === 'string' ) {
614
+ return addFileDep ( replacement )
615
+ }
616
+
617
+ return addFileDep ( replacement . runtime , true )
583
618
} )
584
619
} else {
585
620
renderedDeps = depsArray . map ( ( d ) =>
586
621
// Don't include the assets dir if the default asset file names
587
622
// are used, the path will be reconstructed by the import preload helper
588
- JSON . stringify (
589
- optimizeModulePreloadRelativePaths
590
- ? toRelativePath ( d , file )
591
- : d ,
592
- ) ,
623
+ optimizeModulePreloadRelativePaths
624
+ ? addFileDep ( toRelativePath ( getFileDep ( d ) . url , file ) )
625
+ : d ,
593
626
)
594
627
}
595
628
596
629
s . update (
597
630
markerStartPos ,
598
631
markerStartPos + preloadMarker . length + 2 ,
599
- `[${ renderedDeps . join ( ',' ) } ]` ,
632
+ `__vite__mapDeps( [${ renderedDeps . join ( ',' ) } ]) ` ,
600
633
)
601
634
rewroteMarkerStartPos . add ( markerStartPos )
602
635
}
603
636
}
604
637
}
605
638
639
+ const fileDepsCode = `[${ fileDeps
640
+ . map ( ( fileDep ) =>
641
+ fileDep . runtime ? fileDep . url : JSON . stringify ( fileDep . url ) ,
642
+ )
643
+ . join ( ',' ) } ]`
644
+
645
+ s . append ( `\
646
+ function __vite__mapDeps(indexes) {
647
+ if (!__vite__mapDeps.viteFileDeps) {
648
+ __vite__mapDeps.viteFileDeps = ${ fileDepsCode }
649
+ }
650
+ return indexes.map((i) => __vite__mapDeps.viteFileDeps[i])
651
+ }` )
652
+
606
653
// there may still be markers due to inlined dynamic imports, remove
607
654
// all the markers regardless
608
655
let markerStartPos = indexOfMatchInSlice ( code , preloadMarkerWithQuote )
0 commit comments