Skip to content

Commit

Permalink
emit null source mappings for empty chunk content
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Dec 20, 2024
1 parent 8d98f6f commit 15d56ca
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 30 deletions.
74 changes: 54 additions & 20 deletions internal/linker/linker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}

Expand Down Expand Up @@ -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{}

Expand Down Expand Up @@ -6902,6 +6922,7 @@ type compileResultForSourceMap struct {
sourceMapChunk sourcemap.Chunk
generatedOffset sourcemap.LineColumnOffset
sourceIndex uint32
isNullEntry bool
}

func (c *linkerContext) generateSourceMapForChunk(
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
33 changes: 23 additions & 10 deletions internal/sourcemap/sourcemap.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sourcemap

import (
"bytes"
"strings"
"unicode/utf8"

"github.com/evanw/esbuild/internal/ast"
Expand Down Expand Up @@ -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
Expand All @@ -458,17 +467,21 @@ 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, ',')
}

// 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
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 15d56ca

Please sign in to comment.