From 15d56ca7d2196839c1d13a15fc214d6e81169e30 Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Thu, 19 Dec 2024 23:37:07 -0500 Subject: [PATCH] emit null source mappings for empty chunk content --- internal/linker/linker.go | 74 ++++++++++++++++++++++++--------- internal/sourcemap/sourcemap.go | 33 ++++++++++----- 2 files changed, 77 insertions(+), 30 deletions(-) diff --git a/internal/linker/linker.go b/internal/linker/linker.go index 219fe8e614b..a3271f204db 100644 --- a/internal/linker/linker.go +++ b/internal/linker/linker.go @@ -5849,6 +5849,16 @@ func (c *linkerContext) generateChunkJS(chunkIndex int, chunkWaitGroup *sync.Wai // Ignore empty source map chunks if compileResult.SourceMapChunk.ShouldIgnore { prevOffset.AdvanceBytes(compileResult.JS) + + // Include a null entry in the source map + if c.options.SourceMap != config.SourceMapNone { + if n := len(compileResultsForSourceMap); n > 0 && !compileResultsForSourceMap[n-1].isNullEntry { + compileResultsForSourceMap = append(compileResultsForSourceMap, compileResultForSourceMap{ + sourceIndex: compileResult.sourceIndex, + isNullEntry: true, + }) + } + } } else { prevOffset = sourcemap.LineColumnOffset{} @@ -6330,6 +6340,16 @@ func (c *linkerContext) generateChunkCSS(chunkIndex int, chunkWaitGroup *sync.Wa // Ignore empty source map chunks if compileResult.SourceMapChunk.ShouldIgnore { prevOffset.AdvanceBytes(compileResult.CSS) + + // Include a null entry in the source map + if c.options.SourceMap != config.SourceMapNone && compileResult.sourceIndex.IsValid() { + if n := len(compileResultsForSourceMap); n > 0 && !compileResultsForSourceMap[n-1].isNullEntry { + compileResultsForSourceMap = append(compileResultsForSourceMap, compileResultForSourceMap{ + sourceIndex: compileResult.sourceIndex.GetIndex(), + isNullEntry: true, + }) + } + } } else { prevOffset = sourcemap.LineColumnOffset{} @@ -6902,6 +6922,7 @@ type compileResultForSourceMap struct { sourceMapChunk sourcemap.Chunk generatedOffset sourcemap.LineColumnOffset sourceIndex uint32 + isNullEntry bool } func (c *linkerContext) generateSourceMapForChunk( @@ -6929,6 +6950,9 @@ func (c *linkerContext) generateSourceMapForChunk( continue } sourceIndexToSourcesIndex[result.sourceIndex] = nextSourcesIndex + if result.isNullEntry { + continue + } file := &c.graph.Files[result.sourceIndex] // Simple case: no nested source map @@ -7044,28 +7068,38 @@ func (c *linkerContext) generateSourceMapForChunk( startState.GeneratedColumn += prevColumnOffset } - // Append the precomputed source map chunk - sourcemap.AppendSourceMapChunk(&j, prevEndState, startState, chunk.Buffer) + if result.isNullEntry { + // Emit a "null" mapping + chunk.Buffer.Data = []byte("A") + sourcemap.AppendSourceMapChunk(&j, prevEndState, startState, chunk.Buffer) - // Generate the relative offset to start from next time - prevOriginalName := prevEndState.OriginalName - prevEndState = chunk.EndState - prevEndState.SourceIndex += sourcesIndex - if chunk.Buffer.FirstNameOffset.IsValid() { - prevEndState.OriginalName += totalQuotedNameLen + // Only the generated position was advanced + prevEndState.GeneratedLine = startState.GeneratedLine + prevEndState.GeneratedColumn = startState.GeneratedColumn } else { - // It's possible for a chunk to have mappings but for none of those - // mappings to have an associated name. The name is optional and is - // omitted when the mapping is for a non-name token or if the final - // and original names are the same. In that case we need to restore - // the previous original name end state since it wasn't modified after - // all. If we don't do this, then files after this will adjust their - // name offsets assuming that the previous generated mapping has this - // file's offset, which is wrong. - prevEndState.OriginalName = prevOriginalName - } - prevColumnOffset = chunk.FinalGeneratedColumn - totalQuotedNameLen += len(chunk.QuotedNames) + // Append the precomputed source map chunk + sourcemap.AppendSourceMapChunk(&j, prevEndState, startState, chunk.Buffer) + + // Generate the relative offset to start from next time + prevOriginalName := prevEndState.OriginalName + prevEndState = chunk.EndState + prevEndState.SourceIndex += sourcesIndex + if chunk.Buffer.FirstNameOffset.IsValid() { + prevEndState.OriginalName += totalQuotedNameLen + } else { + // It's possible for a chunk to have mappings but for none of those + // mappings to have an associated name. The name is optional and is + // omitted when the mapping is for a non-name token or if the final + // and original names are the same. In that case we need to restore + // the previous original name end state since it wasn't modified after + // all. If we don't do this, then files after this will adjust their + // name offsets assuming that the previous generated mapping has this + // file's offset, which is wrong. + prevEndState.OriginalName = prevOriginalName + } + prevColumnOffset = chunk.FinalGeneratedColumn + totalQuotedNameLen += len(chunk.QuotedNames) + } // If this was all one line, include the column offset from the start if prevEndState.GeneratedLine == 0 { diff --git a/internal/sourcemap/sourcemap.go b/internal/sourcemap/sourcemap.go index 93effc2102e..40888222d72 100644 --- a/internal/sourcemap/sourcemap.go +++ b/internal/sourcemap/sourcemap.go @@ -2,6 +2,7 @@ package sourcemap import ( "bytes" + "strings" "unicode/utf8" "github.com/evanw/esbuild/internal/ast" @@ -426,20 +427,28 @@ func AppendSourceMapChunk(j *helpers.Joiner, prevEndState SourceMapState, startS // case below instead. Original names are optional and are often omitted, so // we handle it uniformly by saving an index to the first original name, // which may or may not be a part of the first mapping. + var sourceIndex int + var originalLine int + var originalColumn int + omitSource := false generatedColumn, i := DecodeVLQ(buffer.Data, semicolons) - sourceIndex, i := DecodeVLQ(buffer.Data, i) - originalLine, i := DecodeVLQ(buffer.Data, i) - originalColumn, i := DecodeVLQ(buffer.Data, i) + if i == len(buffer.Data) || strings.IndexByte(",;", buffer.Data[i]) != -1 { + omitSource = true + } else { + sourceIndex, i = DecodeVLQ(buffer.Data, i) + originalLine, i = DecodeVLQ(buffer.Data, i) + originalColumn, i = DecodeVLQ(buffer.Data, i) + } // Rewrite the first mapping to be relative to the end state of the previous // chunk. We now know what the end state is because we're in the second pass // where all chunks have already been generated. - startState.SourceIndex += sourceIndex startState.GeneratedColumn += generatedColumn + startState.SourceIndex += sourceIndex startState.OriginalLine += originalLine startState.OriginalColumn += originalColumn prevEndState.HasOriginalName = false // This is handled separately below - rewritten, _ := appendMappingToBuffer(nil, j.LastByte(), prevEndState, startState) + rewritten, _ := appendMappingToBuffer(nil, j.LastByte(), prevEndState, startState, omitSource) j.AddBytes(rewritten) // Next, if there's an original name, we need to rewrite that as well to be @@ -458,7 +467,9 @@ func AppendSourceMapChunk(j *helpers.Joiner, prevEndState SourceMapState, startS j.AddBytes(buffer.Data[i:]) } -func appendMappingToBuffer(buffer []byte, lastByte byte, prevState SourceMapState, currentState SourceMapState) ([]byte, ast.Index32) { +func appendMappingToBuffer( + buffer []byte, lastByte byte, prevState SourceMapState, currentState SourceMapState, omitSource bool, +) ([]byte, ast.Index32) { // Put commas in between mappings if lastByte != 0 && lastByte != ';' && lastByte != '"' { buffer = append(buffer, ',') @@ -466,9 +477,11 @@ func appendMappingToBuffer(buffer []byte, lastByte byte, prevState SourceMapStat // Record the mapping (note that the generated line is recorded using ';' elsewhere) buffer = encodeVLQ(buffer, currentState.GeneratedColumn-prevState.GeneratedColumn) - buffer = encodeVLQ(buffer, currentState.SourceIndex-prevState.SourceIndex) - buffer = encodeVLQ(buffer, currentState.OriginalLine-prevState.OriginalLine) - buffer = encodeVLQ(buffer, currentState.OriginalColumn-prevState.OriginalColumn) + if !omitSource { + buffer = encodeVLQ(buffer, currentState.SourceIndex-prevState.SourceIndex) + buffer = encodeVLQ(buffer, currentState.OriginalLine-prevState.OriginalLine) + buffer = encodeVLQ(buffer, currentState.OriginalColumn-prevState.OriginalColumn) + } // Record the optional original name var nameOffset ast.Index32 @@ -820,7 +833,7 @@ func (b *ChunkBuilder) appendMappingWithoutRemapping(currentState SourceMapState } var nameOffset ast.Index32 - b.sourceMap, nameOffset = appendMappingToBuffer(b.sourceMap, lastByte, b.prevState, currentState) + b.sourceMap, nameOffset = appendMappingToBuffer(b.sourceMap, lastByte, b.prevState, currentState, false) prevOriginalName := b.prevState.OriginalName b.prevState = currentState if !currentState.HasOriginalName {