Skip to content

Commit

Permalink
emit null source mappings for empty chunk content
Browse files Browse the repository at this point in the history
evanw committed Dec 20, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 8d98f6f commit 15d56ca
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
@@ -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 {
33 changes: 23 additions & 10 deletions internal/sourcemap/sourcemap.go
Original file line number Diff line number Diff line change
@@ -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,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
@@ -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 {

0 comments on commit 15d56ca

Please sign in to comment.