@@ -38,6 +38,7 @@ type linkerContext struct {
38
38
fs fs.FS
39
39
res resolver.Resolver
40
40
graph graph.LinkerGraph
41
+ chunks []chunkInfo
41
42
42
43
// This helps avoid an infinite loop when matching imports to exports
43
44
cycleDetector []importTracker
@@ -313,8 +314,8 @@ func link(
313
314
}
314
315
}
315
316
316
- chunks := c .computeChunks ()
317
- c .computeCrossChunkDependencies (chunks )
317
+ c .computeChunks ()
318
+ c .computeCrossChunkDependencies ()
318
319
319
320
// Merge mangled properties before chunks are generated since the names must
320
321
// be consistent across all chunks, or the generated code will break
@@ -330,7 +331,7 @@ func link(
330
331
// won't hit concurrent map mutation hazards
331
332
js_ast .FollowAllSymbols (c .graph .Symbols )
332
333
333
- return c .generateChunksInParallel (chunks , additionalFiles )
334
+ return c .generateChunksInParallel (additionalFiles )
334
335
}
335
336
336
337
func (c * linkerContext ) mangleProps (mangleCache map [string ]interface {}) {
@@ -442,7 +443,7 @@ func (c *linkerContext) mangleProps(mangleCache map[string]interface{}) {
442
443
// Since that work hasn't been finished yet, cycles in the chunk import graph
443
444
// can cause initialization bugs. So let's forbid these cycles for now to guard
444
445
// against code splitting bugs that could cause us to generate buggy chunks.
445
- func (c * linkerContext ) enforceNoCyclicChunkImports (chunks [] chunkInfo ) {
446
+ func (c * linkerContext ) enforceNoCyclicChunkImports () {
446
447
var validate func (int , []int )
447
448
validate = func (chunkIndex int , path []int ) {
448
449
for _ , otherChunkIndex := range path {
@@ -452,7 +453,7 @@ func (c *linkerContext) enforceNoCyclicChunkImports(chunks []chunkInfo) {
452
453
}
453
454
}
454
455
path = append (path , chunkIndex )
455
- for _ , chunkImport := range chunks [chunkIndex ].crossChunkImports {
456
+ for _ , chunkImport := range c . chunks [chunkIndex ].crossChunkImports {
456
457
// Ignore cycles caused by dynamic "import()" expressions. These are fine
457
458
// because they don't necessarily cause initialization order issues and
458
459
// they don't indicate a bug in our chunk generation algorithm. They arise
@@ -462,44 +463,44 @@ func (c *linkerContext) enforceNoCyclicChunkImports(chunks []chunkInfo) {
462
463
}
463
464
}
464
465
}
465
- path := make ([]int , 0 , len (chunks ))
466
- for i := range chunks {
466
+ path := make ([]int , 0 , len (c . chunks ))
467
+ for i := range c . chunks {
467
468
validate (i , path )
468
469
}
469
470
}
470
471
471
- func (c * linkerContext ) generateChunksInParallel (chunks [] chunkInfo , additionalFiles []graph.OutputFile ) []graph.OutputFile {
472
+ func (c * linkerContext ) generateChunksInParallel (additionalFiles []graph.OutputFile ) []graph.OutputFile {
472
473
c .timer .Begin ("Generate chunks" )
473
474
defer c .timer .End ("Generate chunks" )
474
475
475
476
// Generate each chunk on a separate goroutine
476
477
generateWaitGroup := sync.WaitGroup {}
477
- generateWaitGroup .Add (len (chunks ))
478
- for chunkIndex := range chunks {
479
- switch chunks [chunkIndex ].chunkRepr .(type ) {
478
+ generateWaitGroup .Add (len (c . chunks ))
479
+ for chunkIndex := range c . chunks {
480
+ switch c . chunks [chunkIndex ].chunkRepr .(type ) {
480
481
case * chunkReprJS :
481
- go c .generateChunkJS (chunks , chunkIndex , & generateWaitGroup )
482
+ go c .generateChunkJS (chunkIndex , & generateWaitGroup )
482
483
case * chunkReprCSS :
483
- go c .generateChunkCSS (chunks , chunkIndex , & generateWaitGroup )
484
+ go c .generateChunkCSS (chunkIndex , & generateWaitGroup )
484
485
}
485
486
}
486
- c .enforceNoCyclicChunkImports (chunks )
487
+ c .enforceNoCyclicChunkImports ()
487
488
generateWaitGroup .Wait ()
488
489
489
490
// Compute the final hashes of each chunk. This can technically be done in
490
491
// parallel but it probably doesn't matter so much because we're not hashing
491
492
// that much data.
492
- visited := make ([]uint32 , len (chunks ))
493
+ visited := make ([]uint32 , len (c . chunks ))
493
494
var finalBytes []byte
494
- for chunkIndex := range chunks {
495
- chunk := & chunks [chunkIndex ]
495
+ for chunkIndex := range c . chunks {
496
+ chunk := & c . chunks [chunkIndex ]
496
497
var hashSubstitution * string
497
498
498
499
// Only wait for the hash if necessary
499
500
if config .HasPlaceholder (chunk .finalTemplate , config .HashPlaceholder ) {
500
501
// Compute the final hash using the isolated hashes of the dependencies
501
502
hash := xxhash .New ()
502
- c .appendIsolatedHashesForImportedChunks (hash , chunks , uint32 (chunkIndex ), visited , ^ uint32 (chunkIndex ))
503
+ c .appendIsolatedHashesForImportedChunks (hash , uint32 (chunkIndex ), visited , ^ uint32 (chunkIndex ))
503
504
finalBytes = hash .Sum (finalBytes [:0 ])
504
505
finalString := hashForFileName (finalBytes )
505
506
hashSubstitution = & finalString
@@ -514,9 +515,9 @@ func (c *linkerContext) generateChunksInParallel(chunks []chunkInfo, additionalF
514
515
// Generate the final output files by joining file pieces together
515
516
c .timer .Begin ("Generate final output files" )
516
517
var resultsWaitGroup sync.WaitGroup
517
- results := make ([][]graph.OutputFile , len (chunks ))
518
- resultsWaitGroup .Add (len (chunks ))
519
- for chunkIndex , chunk := range chunks {
518
+ results := make ([][]graph.OutputFile , len (c . chunks ))
519
+ resultsWaitGroup .Add (len (c . chunks ))
520
+ for chunkIndex , chunk := range c . chunks {
520
521
go func (chunkIndex int , chunk chunkInfo ) {
521
522
var outputFiles []graph.OutputFile
522
523
@@ -541,7 +542,7 @@ func (c *linkerContext) generateChunksInParallel(chunks []chunkInfo, additionalF
541
542
542
543
// Path substitution for the chunk itself
543
544
finalRelDir := c .fs .Dir (chunk .finalRelPath )
544
- outputContentsJoiner , outputSourceMapShifts := c .substituteFinalPaths (chunks , chunk .intermediateOutput ,
545
+ outputContentsJoiner , outputSourceMapShifts := c .substituteFinalPaths (chunk .intermediateOutput ,
545
546
func (finalRelPathForImport string ) string {
546
547
return c .pathBetweenChunks (finalRelDir , finalRelPathForImport )
547
548
})
@@ -613,8 +614,8 @@ func (c *linkerContext) generateChunksInParallel(chunks []chunkInfo, additionalF
613
614
// Path substitution for the JSON metadata
614
615
var jsonMetadataChunk string
615
616
if c .options .NeedsMetafile {
616
- jsonMetadataChunkPieces := c .breakOutputIntoPieces (chunk .jsonMetadataChunkCallback (len (outputContents )), uint32 ( len ( chunks )) )
617
- jsonMetadataChunkBytes , _ := c .substituteFinalPaths (chunks , jsonMetadataChunkPieces , func (finalRelPathForImport string ) string {
617
+ jsonMetadataChunkPieces := c .breakOutputIntoPieces (chunk .jsonMetadataChunkCallback (len (outputContents )))
618
+ jsonMetadataChunkBytes , _ := c .substituteFinalPaths (jsonMetadataChunkPieces , func (finalRelPathForImport string ) string {
618
619
return c .res .PrettyPath (logger.Path {Text : c .fs .Join (c .options .AbsOutputDir , finalRelPathForImport ), Namespace : "file" })
619
620
})
620
621
jsonMetadataChunk = string (jsonMetadataChunkBytes .Done ())
@@ -652,7 +653,6 @@ func (c *linkerContext) generateChunksInParallel(chunks []chunkInfo, additionalF
652
653
// between import paths), substitute the final import paths in and then join
653
654
// everything into a single byte buffer.
654
655
func (c * linkerContext ) substituteFinalPaths (
655
- chunks []chunkInfo ,
656
656
intermediateOutput intermediateOutput ,
657
657
modifyPath func (string ) string ,
658
658
) (j helpers.Joiner , shifts []sourcemap.SourceMapShift ) {
@@ -692,7 +692,7 @@ func (c *linkerContext) substituteFinalPaths(
692
692
shifts = append (shifts , shift )
693
693
694
694
case outputPieceChunkIndex :
695
- chunk := chunks [piece .index ]
695
+ chunk := c . chunks [piece .index ]
696
696
importPath := modifyPath (chunk .finalRelPath )
697
697
j .AddString (importPath )
698
698
shift .Before .AdvanceString (chunk .uniqueKey )
@@ -822,12 +822,12 @@ func pathRelativeToOutbase(
822
822
return
823
823
}
824
824
825
- func (c * linkerContext ) computeCrossChunkDependencies (chunks [] chunkInfo ) {
825
+ func (c * linkerContext ) computeCrossChunkDependencies () {
826
826
c .timer .Begin ("Compute cross-chunk dependencies" )
827
827
defer c .timer .End ("Compute cross-chunk dependencies" )
828
828
829
829
jsChunks := 0
830
- for _ , chunk := range chunks {
830
+ for _ , chunk := range c . chunks {
831
831
if _ , ok := chunk .chunkRepr .(* chunkReprJS ); ok {
832
832
jsChunks ++
833
833
}
@@ -843,13 +843,13 @@ func (c *linkerContext) computeCrossChunkDependencies(chunks []chunkInfo) {
843
843
dynamicImports map [int ]bool
844
844
}
845
845
846
- chunkMetas := make ([]chunkMeta , len (chunks ))
846
+ chunkMetas := make ([]chunkMeta , len (c . chunks ))
847
847
848
848
// For each chunk, see what symbols it uses from other chunks. Do this in
849
849
// parallel because it's the most expensive part of this function.
850
850
waitGroup := sync.WaitGroup {}
851
- waitGroup .Add (len (chunks ))
852
- for chunkIndex , chunk := range chunks {
851
+ waitGroup .Add (len (c . chunks ))
852
+ for chunkIndex , chunk := range c . chunks {
853
853
go func (chunkIndex int , chunk chunkInfo ) {
854
854
chunkMeta := & chunkMetas [chunkIndex ]
855
855
imports := make (map [js_ast.Ref ]bool )
@@ -872,7 +872,7 @@ func (c *linkerContext) computeCrossChunkDependencies(chunks []chunkInfo) {
872
872
record := & repr .AST .ImportRecords [importRecordIndex ]
873
873
if record .SourceIndex .IsValid () && c .isExternalDynamicImport (record , sourceIndex ) {
874
874
otherChunkIndex := c .graph .Files [record .SourceIndex .GetIndex ()].EntryPointChunkIndex
875
- record .Path .Text = chunks [otherChunkIndex ].uniqueKey
875
+ record .Path .Text = c . chunks [otherChunkIndex ].uniqueKey
876
876
record .SourceIndex = ast.Index32 {}
877
877
record .Flags |= ast .ShouldNotBeExternalInMetafile
878
878
@@ -988,8 +988,8 @@ func (c *linkerContext) computeCrossChunkDependencies(chunks []chunkInfo) {
988
988
waitGroup .Wait ()
989
989
990
990
// Mark imported symbols as exported in the chunk from which they are declared
991
- for chunkIndex := range chunks {
992
- chunk := & chunks [chunkIndex ]
991
+ for chunkIndex := range c . chunks {
992
+ chunk := & c . chunks [chunkIndex ]
993
993
chunkRepr , ok := chunk .chunkRepr .(* chunkReprJS )
994
994
if ! ok {
995
995
continue
@@ -1013,7 +1013,7 @@ func (c *linkerContext) computeCrossChunkDependencies(chunks []chunkInfo) {
1013
1013
// this entry point, even if there are no imports. We need to make sure
1014
1014
// these chunks are evaluated for their side effects too.
1015
1015
if chunk .isEntryPoint {
1016
- for otherChunkIndex , otherChunk := range chunks {
1016
+ for otherChunkIndex , otherChunk := range c . chunks {
1017
1017
if _ , ok := otherChunk .chunkRepr .(* chunkReprJS ); ok && chunkIndex != otherChunkIndex && otherChunk .entryBits .HasBit (chunk .entryPointBit ) {
1018
1018
imports := chunkRepr .importsFromOtherChunks [uint32 (otherChunkIndex )]
1019
1019
chunkRepr .importsFromOtherChunks [uint32 (otherChunkIndex )] = imports
@@ -1042,8 +1042,8 @@ func (c *linkerContext) computeCrossChunkDependencies(chunks []chunkInfo) {
1042
1042
// Generate cross-chunk exports. These must be computed before cross-chunk
1043
1043
// imports because of export alias renaming, which must consider all export
1044
1044
// aliases simultaneously to avoid collisions.
1045
- for chunkIndex := range chunks {
1046
- chunk := & chunks [chunkIndex ]
1045
+ for chunkIndex := range c . chunks {
1046
+ chunk := & c . chunks [chunkIndex ]
1047
1047
chunkRepr , ok := chunk .chunkRepr .(* chunkReprJS )
1048
1048
if ! ok {
1049
1049
continue
@@ -1078,16 +1078,16 @@ func (c *linkerContext) computeCrossChunkDependencies(chunks []chunkInfo) {
1078
1078
// Generate cross-chunk imports. These must be computed after cross-chunk
1079
1079
// exports because the export aliases must already be finalized so they can
1080
1080
// be embedded in the generated import statements.
1081
- for chunkIndex := range chunks {
1082
- chunk := & chunks [chunkIndex ]
1081
+ for chunkIndex := range c . chunks {
1082
+ chunk := & c . chunks [chunkIndex ]
1083
1083
chunkRepr , ok := chunk .chunkRepr .(* chunkReprJS )
1084
1084
if ! ok {
1085
1085
continue
1086
1086
}
1087
1087
1088
1088
var crossChunkPrefixStmts []js_ast.Stmt
1089
1089
1090
- for _ , crossChunkImport := range c .sortedCrossChunkImports (chunks , chunkRepr .importsFromOtherChunks ) {
1090
+ for _ , crossChunkImport := range c .sortedCrossChunkImports (chunkRepr .importsFromOtherChunks ) {
1091
1091
switch c .options .OutputFormat {
1092
1092
case config .FormatESModule :
1093
1093
var items []js_ast.ClauseItem
@@ -1137,12 +1137,12 @@ func (a crossChunkImportArray) Less(i int, j int) bool {
1137
1137
}
1138
1138
1139
1139
// Sort cross-chunk imports by chunk name for determinism
1140
- func (c * linkerContext ) sortedCrossChunkImports (chunks [] chunkInfo , importsFromOtherChunks map [uint32 ]crossChunkImportItemArray ) crossChunkImportArray {
1140
+ func (c * linkerContext ) sortedCrossChunkImports (importsFromOtherChunks map [uint32 ]crossChunkImportItemArray ) crossChunkImportArray {
1141
1141
result := make (crossChunkImportArray , 0 , len (importsFromOtherChunks ))
1142
1142
1143
1143
for otherChunkIndex , importItems := range importsFromOtherChunks {
1144
1144
// Sort imports from a single chunk by alias for determinism
1145
- otherChunk := & chunks [otherChunkIndex ]
1145
+ otherChunk := & c . chunks [otherChunkIndex ]
1146
1146
exportsToOtherChunks := otherChunk .chunkRepr .(* chunkReprJS ).exportsToOtherChunks
1147
1147
for i , item := range importItems {
1148
1148
importItems [i ].exportAlias = exportsToOtherChunks [item .ref ]
@@ -3142,7 +3142,7 @@ func (c *linkerContext) findImportedFilesInCSSOrder(entryPoints []uint32) (exter
3142
3142
return
3143
3143
}
3144
3144
3145
- func (c * linkerContext ) computeChunks () [] chunkInfo {
3145
+ func (c * linkerContext ) computeChunks () {
3146
3146
c .timer .Begin ("Compute chunks" )
3147
3147
defer c .timer .End ("Compute chunks" )
3148
3148
@@ -3358,7 +3358,7 @@ func (c *linkerContext) computeChunks() []chunkInfo {
3358
3358
})
3359
3359
}
3360
3360
3361
- return sortedChunks
3361
+ c . chunks = sortedChunks
3362
3362
}
3363
3363
3364
3364
type chunkOrder struct {
@@ -4775,10 +4775,10 @@ func (c *linkerContext) renameSymbolsInChunk(chunk *chunkInfo, filesInOrder []ui
4775
4775
return r
4776
4776
}
4777
4777
4778
- func (c * linkerContext ) generateChunkJS (chunks [] chunkInfo , chunkIndex int , chunkWaitGroup * sync.WaitGroup ) {
4778
+ func (c * linkerContext ) generateChunkJS (chunkIndex int , chunkWaitGroup * sync.WaitGroup ) {
4779
4779
defer c .recoverInternalError (chunkWaitGroup , runtime .SourceIndex )
4780
4780
4781
- chunk := & chunks [chunkIndex ]
4781
+ chunk := & c . chunks [chunkIndex ]
4782
4782
4783
4783
timer := c .timer .Fork ()
4784
4784
if timer != nil {
@@ -4855,7 +4855,7 @@ func (c *linkerContext) generateChunkJS(chunks []chunkInfo, chunkIndex int, chun
4855
4855
for i , chunkImport := range chunk .crossChunkImports {
4856
4856
crossChunkImportRecords [i ] = ast.ImportRecord {
4857
4857
Kind : chunkImport .importKind ,
4858
- Path : logger.Path {Text : chunks [chunkImport .chunkIndex ].uniqueKey },
4858
+ Path : logger.Path {Text : c . chunks [chunkImport .chunkIndex ].uniqueKey },
4859
4859
Flags : ast .ShouldNotBeExternalInMetafile ,
4860
4860
}
4861
4861
}
@@ -5027,7 +5027,7 @@ func (c *linkerContext) generateChunkJS(chunks []chunkInfo, chunkIndex int, chun
5027
5027
jMeta .AddString (fmt .Sprintf (" \" entryPoint\" : %s,\n " , helpers .QuoteForJSON (entryPoint , c .options .ASCIIOnly )))
5028
5028
}
5029
5029
if chunkRepr .hasCSSChunk {
5030
- jMeta .AddString (fmt .Sprintf (" \" cssBundle\" : %s,\n " , helpers .QuoteForJSON (chunks [chunkRepr .cssChunkIndex ].uniqueKey , c .options .ASCIIOnly )))
5030
+ jMeta .AddString (fmt .Sprintf (" \" cssBundle\" : %s,\n " , helpers .QuoteForJSON (c . chunks [chunkRepr .cssChunkIndex ].uniqueKey , c .options .ASCIIOnly )))
5031
5031
}
5032
5032
jMeta .AddString (" \" inputs\" : {" )
5033
5033
}
@@ -5148,7 +5148,7 @@ func (c *linkerContext) generateChunkJS(chunks []chunkInfo, chunkIndex int, chun
5148
5148
}
5149
5149
5150
5150
// The JavaScript contents are done now that the source map comment is in
5151
- chunk .intermediateOutput = c .breakOutputIntoPieces (j , uint32 ( len ( chunks )) )
5151
+ chunk .intermediateOutput = c .breakOutputIntoPieces (j )
5152
5152
timer .End ("Join JavaScript files" )
5153
5153
5154
5154
if c .options .SourceMap != config .SourceMapNone {
@@ -5261,10 +5261,10 @@ type compileResultCSS struct {
5261
5261
hasCharset bool
5262
5262
}
5263
5263
5264
- func (c * linkerContext ) generateChunkCSS (chunks [] chunkInfo , chunkIndex int , chunkWaitGroup * sync.WaitGroup ) {
5264
+ func (c * linkerContext ) generateChunkCSS (chunkIndex int , chunkWaitGroup * sync.WaitGroup ) {
5265
5265
defer c .recoverInternalError (chunkWaitGroup , runtime .SourceIndex )
5266
5266
5267
- chunk := & chunks [chunkIndex ]
5267
+ chunk := & c . chunks [chunkIndex ]
5268
5268
5269
5269
timer := c .timer .Fork ()
5270
5270
if timer != nil {
@@ -5531,7 +5531,7 @@ func (c *linkerContext) generateChunkCSS(chunks []chunkInfo, chunkIndex int, chu
5531
5531
}
5532
5532
5533
5533
// The CSS contents are done now that the source map comment is in
5534
- chunk .intermediateOutput = c .breakOutputIntoPieces (j , uint32 ( len ( chunks )) )
5534
+ chunk .intermediateOutput = c .breakOutputIntoPieces (j )
5535
5535
timer .End ("Join CSS files" )
5536
5536
5537
5537
if c .options .SourceMap != config .SourceMapNone {
@@ -5592,7 +5592,6 @@ func maybeAppendLegalComments(
5592
5592
5593
5593
func (c * linkerContext ) appendIsolatedHashesForImportedChunks (
5594
5594
hash hash.Hash ,
5595
- chunks []chunkInfo ,
5596
5595
chunkIndex uint32 ,
5597
5596
visited []uint32 ,
5598
5597
visitedKey uint32 ,
@@ -5605,11 +5604,11 @@ func (c *linkerContext) appendIsolatedHashesForImportedChunks(
5605
5604
return
5606
5605
}
5607
5606
visited [chunkIndex ] = visitedKey
5608
- chunk := & chunks [chunkIndex ]
5607
+ chunk := & c . chunks [chunkIndex ]
5609
5608
5610
5609
// Visit the other chunks that this chunk imports before visiting this chunk
5611
5610
for _ , chunkImport := range chunk .crossChunkImports {
5612
- c .appendIsolatedHashesForImportedChunks (hash , chunks , chunkImport .chunkIndex , visited , visitedKey )
5611
+ c .appendIsolatedHashesForImportedChunks (hash , chunkImport .chunkIndex , visited , visitedKey )
5613
5612
}
5614
5613
5615
5614
// Mix in hashes for referenced asset paths (i.e. the "file" loader)
@@ -5633,7 +5632,7 @@ func (c *linkerContext) appendIsolatedHashesForImportedChunks(
5633
5632
hash .Write (chunk .waitForIsolatedHash ())
5634
5633
}
5635
5634
5636
- func (c * linkerContext ) breakOutputIntoPieces (j helpers.Joiner , chunkCount uint32 ) intermediateOutput {
5635
+ func (c * linkerContext ) breakOutputIntoPieces (j helpers.Joiner ) intermediateOutput {
5637
5636
// Optimization: If there can be no substitutions, just reuse the initial
5638
5637
// joiner that was used when generating the intermediate chunk output
5639
5638
// instead of creating another one and copying the whole file into it.
@@ -5680,7 +5679,7 @@ func (c *linkerContext) breakOutputIntoPieces(j helpers.Joiner, chunkCount uint3
5680
5679
}
5681
5680
5682
5681
case outputPieceChunkIndex :
5683
- if index >= chunkCount {
5682
+ if index >= uint32 ( len ( c . chunks )) {
5684
5683
boundary = - 1
5685
5684
}
5686
5685
0 commit comments