@@ -614,7 +614,7 @@ func (c *linkerContext) generateChunksInParallel(additionalFiles []graph.OutputF
614
614
// Path substitution for the JSON metadata
615
615
var jsonMetadataChunk string
616
616
if c .options .NeedsMetafile {
617
- jsonMetadataChunkPieces := c .breakOutputIntoPieces (chunk .jsonMetadataChunkCallback (len (outputContents )))
617
+ jsonMetadataChunkPieces := c .breakJoinerIntoPieces (chunk .jsonMetadataChunkCallback (len (outputContents )))
618
618
jsonMetadataChunkBytes , _ := c .substituteFinalPaths (jsonMetadataChunkPieces , func (finalRelPathForImport string ) string {
619
619
return c .res .PrettyPath (logger.Path {Text : c .fs .Join (c .options .AbsOutputDir , finalRelPathForImport ), Namespace : "file" })
620
620
})
@@ -704,6 +704,37 @@ func (c *linkerContext) substituteFinalPaths(
704
704
return
705
705
}
706
706
707
+ func (c * linkerContext ) accurateFinalByteCount (output intermediateOutput , chunkFinalRelDir string ) int {
708
+ count := 0
709
+
710
+ // Note: The paths generated here must match "substituteFinalPaths" above
711
+ for _ , piece := range output .pieces {
712
+ count += len (piece .data )
713
+
714
+ switch piece .kind {
715
+ case outputPieceAssetIndex :
716
+ file := c .graph .Files [piece .index ]
717
+ if len (file .InputFile .AdditionalFiles ) != 1 {
718
+ panic ("Internal error" )
719
+ }
720
+ relPath , _ := c .fs .Rel (c .options .AbsOutputDir , file .InputFile .AdditionalFiles [0 ].AbsPath )
721
+
722
+ // Make sure to always use forward slashes, even on Windows
723
+ relPath = strings .ReplaceAll (relPath , "\\ " , "/" )
724
+
725
+ importPath := c .pathBetweenChunks (chunkFinalRelDir , relPath )
726
+ count += len (importPath )
727
+
728
+ case outputPieceChunkIndex :
729
+ chunk := c .chunks [piece .index ]
730
+ importPath := c .pathBetweenChunks (chunkFinalRelDir , chunk .finalRelPath )
731
+ count += len (importPath )
732
+ }
733
+ }
734
+
735
+ return count
736
+ }
737
+
707
738
func (c * linkerContext ) pathBetweenChunks (fromRelDir string , toRelPath string ) string {
708
739
// Join with the public path if it has been configured
709
740
if c .options .PublicPath != "" {
@@ -5036,12 +5067,12 @@ func (c *linkerContext) generateChunkJS(chunkIndex int, chunkWaitGroup *sync.Wai
5036
5067
var compileResultsForSourceMap []compileResultForSourceMap
5037
5068
var legalCommentList []string
5038
5069
var metaOrder []uint32
5039
- var metaByteCount map [string ] int
5070
+ var metaBytes map [uint32 ][][] byte
5040
5071
legalCommentSet := make (map [string ]bool )
5041
5072
prevFileNameComment := uint32 (0 )
5042
5073
if c .options .NeedsMetafile {
5043
5074
metaOrder = make ([]uint32 , 0 , len (compileResults ))
5044
- metaByteCount = make (map [string ] int , len (compileResults ))
5075
+ metaBytes = make (map [uint32 ][][] byte , len (compileResults ))
5045
5076
}
5046
5077
for _ , compileResult := range compileResults {
5047
5078
isRuntime := compileResult .sourceIndex == runtime .SourceIndex
@@ -5104,13 +5135,11 @@ func (c *linkerContext) generateChunkJS(chunkIndex int, chunkWaitGroup *sync.Wai
5104
5135
// Include this file in the metadata
5105
5136
if c .options .NeedsMetafile {
5106
5137
// Accumulate file sizes since a given file may be split into multiple parts
5107
- path := c .graph .Files [compileResult .sourceIndex ].InputFile .Source .PrettyPath
5108
- if count , ok := metaByteCount [path ]; ok {
5109
- metaByteCount [path ] = count + len (compileResult .JS )
5110
- } else {
5138
+ bytes , ok := metaBytes [compileResult .sourceIndex ]
5139
+ if ! ok {
5111
5140
metaOrder = append (metaOrder , compileResult .sourceIndex )
5112
- metaByteCount [path ] = len (compileResult .JS )
5113
5141
}
5142
+ metaBytes [compileResult .sourceIndex ] = append (bytes , compileResult .JS )
5114
5143
}
5115
5144
}
5116
5145
@@ -5148,7 +5177,7 @@ func (c *linkerContext) generateChunkJS(chunkIndex int, chunkWaitGroup *sync.Wai
5148
5177
}
5149
5178
5150
5179
// The JavaScript contents are done now that the source map comment is in
5151
- chunk .intermediateOutput = c .breakOutputIntoPieces (j )
5180
+ chunk .intermediateOutput = c .breakJoinerIntoPieces (j )
5152
5181
timer .End ("Join JavaScript files" )
5153
5182
5154
5183
if c .options .SourceMap != config .SourceMapNone {
@@ -5161,20 +5190,30 @@ func (c *linkerContext) generateChunkJS(chunkIndex int, chunkWaitGroup *sync.Wai
5161
5190
// End the metadata lazily. The final output size is not known until the
5162
5191
// final import paths are substituted into the output pieces generated below.
5163
5192
if c .options .NeedsMetafile {
5193
+ pieces := make ([][]intermediateOutput , len (metaOrder ))
5194
+ for i , sourceIndex := range metaOrder {
5195
+ slices := metaBytes [sourceIndex ]
5196
+ outputs := make ([]intermediateOutput , len (slices ))
5197
+ for j , slice := range slices {
5198
+ outputs [j ] = c .breakOutputIntoPieces (slice )
5199
+ }
5200
+ pieces [i ] = outputs
5201
+ }
5164
5202
chunk .jsonMetadataChunkCallback = func (finalOutputSize int ) helpers.Joiner {
5165
- isFirstMeta := true
5166
- for _ , sourceIndex := range metaOrder {
5167
- if isFirstMeta {
5168
- isFirstMeta = false
5169
- } else {
5203
+ finalRelDir := c .fs .Dir (chunk .finalRelPath )
5204
+ for i , sourceIndex := range metaOrder {
5205
+ if i > 0 {
5170
5206
jMeta .AddString ("," )
5171
5207
}
5172
- path := c .graph .Files [sourceIndex ].InputFile .Source .PrettyPath
5173
- extra := c .generateExtraDataForFileJS (sourceIndex )
5174
- jMeta .AddString (fmt .Sprintf ("\n %s: {\n \" bytesInOutput\" : %d\n %s}" ,
5175
- helpers .QuoteForJSON (path , c .options .ASCIIOnly ), metaByteCount [path ], extra ))
5208
+ count := 0
5209
+ for _ , output := range pieces [i ] {
5210
+ count += c .accurateFinalByteCount (output , finalRelDir )
5211
+ }
5212
+ jMeta .AddString (fmt .Sprintf ("\n %s: {\n \" bytesInOutput\" : %d\n }" ,
5213
+ helpers .QuoteForJSON (c .graph .Files [sourceIndex ].InputFile .Source .PrettyPath , c .options .ASCIIOnly ),
5214
+ count ))
5176
5215
}
5177
- if ! isFirstMeta {
5216
+ if len ( metaOrder ) > 0 {
5178
5217
jMeta .AddString ("\n " )
5179
5218
}
5180
5219
jMeta .AddString (fmt .Sprintf ("},\n \" bytes\" : %d\n }" , finalOutputSize ))
@@ -5461,7 +5500,6 @@ func (c *linkerContext) generateChunkCSS(chunkIndex int, chunkWaitGroup *sync.Wa
5461
5500
jMeta .AddString ("],\n \" inputs\" : {" )
5462
5501
}
5463
5502
}
5464
- isFirstMeta := true
5465
5503
5466
5504
// Concatenate the generated CSS chunks together
5467
5505
var compileResultsForSourceMap []compileResultForSourceMap
@@ -5507,18 +5545,6 @@ func (c *linkerContext) generateChunkCSS(chunkIndex int, chunkWaitGroup *sync.Wa
5507
5545
})
5508
5546
}
5509
5547
}
5510
-
5511
- // Include this file in the metadata
5512
- if c .options .NeedsMetafile {
5513
- if isFirstMeta {
5514
- isFirstMeta = false
5515
- } else {
5516
- jMeta .AddString ("," )
5517
- }
5518
- jMeta .AddString (fmt .Sprintf ("\n %s: {\n \" bytesInOutput\" : %d\n }" ,
5519
- helpers .QuoteForJSON (c .graph .Files [compileResult .sourceIndex ].InputFile .Source .PrettyPath , c .options .ASCIIOnly ),
5520
- len (compileResult .CSS )))
5521
- }
5522
5548
}
5523
5549
5524
5550
// Make sure the file ends with a newline
@@ -5531,7 +5557,7 @@ func (c *linkerContext) generateChunkCSS(chunkIndex int, chunkWaitGroup *sync.Wa
5531
5557
}
5532
5558
5533
5559
// The CSS contents are done now that the source map comment is in
5534
- chunk .intermediateOutput = c .breakOutputIntoPieces (j )
5560
+ chunk .intermediateOutput = c .breakJoinerIntoPieces (j )
5535
5561
timer .End ("Join CSS files" )
5536
5562
5537
5563
if c .options .SourceMap != config .SourceMapNone {
@@ -5544,8 +5570,21 @@ func (c *linkerContext) generateChunkCSS(chunkIndex int, chunkWaitGroup *sync.Wa
5544
5570
// End the metadata lazily. The final output size is not known until the
5545
5571
// final import paths are substituted into the output pieces generated below.
5546
5572
if c .options .NeedsMetafile {
5573
+ pieces := make ([]intermediateOutput , len (compileResults ))
5574
+ for i , compileResult := range compileResults {
5575
+ pieces [i ] = c .breakOutputIntoPieces (compileResult .CSS )
5576
+ }
5547
5577
chunk .jsonMetadataChunkCallback = func (finalOutputSize int ) helpers.Joiner {
5548
- if ! isFirstMeta {
5578
+ finalRelDir := c .fs .Dir (chunk .finalRelPath )
5579
+ for i , compileResult := range compileResults {
5580
+ if i > 0 {
5581
+ jMeta .AddString ("," )
5582
+ }
5583
+ jMeta .AddString (fmt .Sprintf ("\n %s: {\n \" bytesInOutput\" : %d\n }" ,
5584
+ helpers .QuoteForJSON (c .graph .Files [compileResult .sourceIndex ].InputFile .Source .PrettyPath , c .options .ASCIIOnly ),
5585
+ c .accurateFinalByteCount (pieces [i ], finalRelDir )))
5586
+ }
5587
+ if len (compileResults ) > 0 {
5549
5588
jMeta .AddString ("\n " )
5550
5589
}
5551
5590
jMeta .AddString (fmt .Sprintf ("},\n \" bytes\" : %d\n }" , finalOutputSize ))
@@ -5632,16 +5671,18 @@ func (c *linkerContext) appendIsolatedHashesForImportedChunks(
5632
5671
hash .Write (chunk .waitForIsolatedHash ())
5633
5672
}
5634
5673
5635
- func (c * linkerContext ) breakOutputIntoPieces (j helpers.Joiner ) intermediateOutput {
5674
+ func (c * linkerContext ) breakJoinerIntoPieces (j helpers.Joiner ) intermediateOutput {
5636
5675
// Optimization: If there can be no substitutions, just reuse the initial
5637
5676
// joiner that was used when generating the intermediate chunk output
5638
5677
// instead of creating another one and copying the whole file into it.
5639
5678
if ! j .Contains (c .uniqueKeyPrefix , c .uniqueKeyPrefixBytes ) {
5640
5679
return intermediateOutput {joiner : j }
5641
5680
}
5681
+ return c .breakOutputIntoPieces (j .Done ())
5682
+ }
5642
5683
5684
+ func (c * linkerContext ) breakOutputIntoPieces (output []byte ) intermediateOutput {
5643
5685
var pieces []outputPiece
5644
- output := j .Done ()
5645
5686
prefix := c .uniqueKeyPrefixBytes
5646
5687
for {
5647
5688
// Scan for the next piece boundary
0 commit comments