remove unused packages

This commit is contained in:
Egor Aristov 2025-10-15 17:29:21 +03:00
parent 78201db012
commit f1795ab4d0
Signed by: egor3f
GPG Key ID: 40482A264AAEC85F
7 changed files with 0 additions and 1374 deletions

View File

@ -1,252 +0,0 @@
package sourcemap
import (
"errors"
"iter"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
)
type Mapping struct {
GeneratedLine int
GeneratedCharacter int
SourceIndex SourceIndex
SourceLine int
SourceCharacter int
NameIndex NameIndex
}
func (m *Mapping) Equals(other *Mapping) bool {
return m == other || m.GeneratedLine == other.GeneratedLine &&
m.GeneratedCharacter == other.GeneratedCharacter &&
m.SourceIndex == other.SourceIndex &&
m.SourceLine == other.SourceLine &&
m.SourceCharacter == other.SourceCharacter &&
m.NameIndex == other.NameIndex
}
func (m *Mapping) IsSourceMapping() bool {
return m.SourceIndex != MissingSource &&
m.SourceLine != MissingLineOrColumn &&
m.SourceCharacter != MissingLineOrColumn
}
const (
MissingSource SourceIndex = -1
MissingName NameIndex = -1
MissingLineOrColumn int = -1
)
type MappingsDecoder struct {
mappings string
done bool
pos int
generatedLine int
generatedCharacter int
sourceIndex SourceIndex
sourceLine int
sourceCharacter int
nameIndex NameIndex
error error
mappingPool core.Pool[Mapping]
}
func DecodeMappings(mappings string) *MappingsDecoder {
return &MappingsDecoder{mappings: mappings}
}
func (d *MappingsDecoder) MappingsString() string {
return d.mappings
}
func (d *MappingsDecoder) Pos() int {
return d.pos
}
func (d *MappingsDecoder) Error() error {
return d.error
}
func (d *MappingsDecoder) State() *Mapping {
return d.captureMapping(true /*hasSource*/, true /*hasName*/)
}
func (d *MappingsDecoder) Values() iter.Seq[*Mapping] {
return func(yield func(*Mapping) bool) {
for value, done := d.Next(); !done; value, done = d.Next() {
if !yield(value) {
break
}
}
}
}
func (d *MappingsDecoder) Next() (value *Mapping, done bool) {
for !d.done && d.pos < len(d.mappings) {
ch := d.mappings[d.pos]
if ch == ';' {
// new line
d.generatedLine++
d.generatedCharacter = 0
d.pos++
continue
}
if ch == ',' {
// Next entry is on same line - no action needed
d.pos++
continue
}
hasSource := false
hasName := false
d.generatedCharacter += d.base64VLQFormatDecode()
if d.hasReportedError() {
return d.stopIterating()
}
if d.generatedCharacter < 0 {
return d.setErrorAndStopIterating("Invalid generatedCharacter found")
}
if !d.isSourceMappingSegmentEnd() {
hasSource = true
d.sourceIndex += SourceIndex(d.base64VLQFormatDecode())
if d.hasReportedError() {
return d.stopIterating()
}
if d.sourceIndex < 0 {
return d.setErrorAndStopIterating("Invalid sourceIndex found")
}
if d.isSourceMappingSegmentEnd() {
return d.setErrorAndStopIterating("Unsupported Format: No entries after sourceIndex")
}
d.sourceLine += d.base64VLQFormatDecode()
if d.hasReportedError() {
return d.stopIterating()
}
if d.sourceLine < 0 {
return d.setErrorAndStopIterating("Invalid sourceLine found")
}
if d.isSourceMappingSegmentEnd() {
return d.setErrorAndStopIterating("Unsupported Format: No entries after sourceLine")
}
d.sourceCharacter += d.base64VLQFormatDecode()
if d.hasReportedError() {
return d.stopIterating()
}
if d.sourceCharacter < 0 {
return d.setErrorAndStopIterating("Invalid sourceCharacter found")
}
if !d.isSourceMappingSegmentEnd() {
hasName = true
d.nameIndex += NameIndex(d.base64VLQFormatDecode())
if d.hasReportedError() {
return d.stopIterating()
}
if d.nameIndex < 0 {
return d.setErrorAndStopIterating("Invalid nameIndex found")
}
if !d.isSourceMappingSegmentEnd() {
return d.setErrorAndStopIterating("Unsupported Error Format: Entries after nameIndex")
}
}
}
return d.captureMapping(hasSource, hasName), false
}
return d.stopIterating()
}
func (d *MappingsDecoder) captureMapping(hasSource bool, hasName bool) *Mapping {
mapping := d.mappingPool.New()
mapping.GeneratedLine = d.generatedLine
mapping.GeneratedCharacter = d.generatedCharacter
mapping.SourceIndex = core.IfElse(hasSource, d.sourceIndex, MissingSource)
mapping.SourceLine = core.IfElse(hasSource, d.sourceLine, MissingLineOrColumn)
mapping.SourceCharacter = core.IfElse(hasSource, d.sourceCharacter, MissingLineOrColumn)
mapping.NameIndex = core.IfElse(hasName, d.nameIndex, MissingName)
return mapping
}
func (d *MappingsDecoder) stopIterating() (*Mapping, bool) {
d.done = true
return nil, true
}
func (d *MappingsDecoder) setError(err string) {
d.error = errors.New(err)
}
func (d *MappingsDecoder) setErrorAndStopIterating(err string) (*Mapping, bool) {
d.setError(err)
return d.stopIterating()
}
func (d *MappingsDecoder) hasReportedError() bool {
return d.error != nil
}
func (d *MappingsDecoder) isSourceMappingSegmentEnd() bool {
return d.pos == len(d.mappings) || d.mappings[d.pos] == ',' || d.mappings[d.pos] == ';'
}
func (d *MappingsDecoder) base64VLQFormatDecode() int {
moreDigits := true
shiftCount := 0
value := 0
for ; moreDigits; d.pos++ {
if d.pos >= len(d.mappings) {
d.setError("Error in decoding base64VLQFormatDecode, past the mapping string")
return -1
}
// 6 digit number
currentByte := base64FormatDecode(d.mappings[d.pos])
if currentByte == -1 {
d.setError("Invalid character in VLQ")
return -1
}
// If msb is set, we still have more bits to continue
moreDigits = (currentByte & 32) != 0
// least significant 5 bits are the next msbs in the final value.
value = value | ((currentByte & 31) << shiftCount)
shiftCount += 5
}
// Least significant bit if 1 represents negative and rest of the msb is actual absolute value
if (value & 1) == 0 {
// + number
value = value >> 1
} else {
// - number
value = value >> 1
value = -value
}
return value
}
func base64FormatDecode(ch byte) int {
switch {
case ch >= 'A' && ch <= 'Z':
return int(ch - 'A')
case ch >= 'a' && ch <= 'z':
return int(ch - 'a' + 26)
case ch >= '0' && ch <= '9':
return int(ch - '0' + 52)
case ch == '+':
return 62
case ch == '/':
return 63
default:
return -1
}
}

View File

@ -1,360 +0,0 @@
package sourcemap
import (
"errors"
"slices"
"strings"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
"github.com/go-json-experiment/json"
)
type (
SourceIndex int
NameIndex int
)
const (
sourceIndexNotSet SourceIndex = -1
nameIndexNotSet NameIndex = -1
notSet int = -1
)
type Generator struct {
pathOptions tspath.ComparePathsOptions
file string
sourceRoot string
sourcesDirectoryPath string
rawSources []string
sources []string
sourceToSourceIndexMap map[string]SourceIndex
sourcesContent []*string
names []string
nameToNameIndexMap map[string]NameIndex
mappings strings.Builder
lastGeneratedLine int
lastGeneratedCharacter int
lastSourceIndex SourceIndex
lastSourceLine int
lastSourceCharacter int
lastNameIndex NameIndex
hasLast bool
pendingGeneratedLine int
pendingGeneratedCharacter int
pendingSourceIndex SourceIndex
pendingSourceLine int
pendingSourceCharacter int
pendingNameIndex NameIndex
hasPending bool
hasPendingSource bool
hasPendingName bool
}
type RawSourceMap struct {
Version int `json:"version"`
File string `json:"file"`
SourceRoot string `json:"sourceRoot"`
Sources []string `json:"sources"`
Names []string `json:"names"`
Mappings string `json:"mappings"`
SourcesContent []*string `json:"sourcesContent,omitzero"`
}
func NewGenerator(file string, sourceRoot string, sourcesDirectoryPath string, options tspath.ComparePathsOptions) *Generator {
return &Generator{
file: file,
sourceRoot: sourceRoot,
sourcesDirectoryPath: sourcesDirectoryPath,
pathOptions: options,
}
}
func (gen *Generator) Sources() []string { return gen.rawSources }
// Adds a source to the source map
func (gen *Generator) AddSource(fileName string) SourceIndex {
source := tspath.GetRelativePathToDirectoryOrUrl(
gen.sourcesDirectoryPath,
fileName,
true, /*isAbsolutePathAnUrl*/
gen.pathOptions,
)
sourceIndex, found := gen.sourceToSourceIndexMap[source]
if !found {
sourceIndex = SourceIndex(len(gen.sources))
gen.sources = append(gen.sources, source)
gen.rawSources = append(gen.rawSources, fileName)
if gen.sourceToSourceIndexMap == nil {
gen.sourceToSourceIndexMap = make(map[string]SourceIndex)
}
gen.sourceToSourceIndexMap[source] = sourceIndex
}
return sourceIndex
}
// Sets the content for a source
func (gen *Generator) SetSourceContent(sourceIndex SourceIndex, content string) error {
if sourceIndex < 0 || int(sourceIndex) >= len(gen.sources) {
return errors.New("sourceIndex is out of range")
}
for len(gen.sourcesContent) <= int(sourceIndex) {
gen.sourcesContent = append(gen.sourcesContent, nil)
}
gen.sourcesContent[sourceIndex] = &content
return nil
}
// Declares a name in the source map, returning the index of the name
func (gen *Generator) AddName(name string) NameIndex {
nameIndex, found := gen.nameToNameIndexMap[name]
if !found {
nameIndex = NameIndex(len(gen.names))
gen.names = append(gen.names, name)
if gen.nameToNameIndexMap == nil {
gen.nameToNameIndexMap = make(map[string]NameIndex)
}
gen.nameToNameIndexMap[name] = nameIndex
}
return nameIndex
}
func (gen *Generator) isNewGeneratedPosition(generatedLine int, generatedCharacter int) bool {
return !gen.hasPending ||
gen.pendingGeneratedLine != generatedLine ||
gen.pendingGeneratedCharacter != generatedCharacter
}
func (gen *Generator) isBacktrackingSourcePosition(sourceIndex SourceIndex, sourceLine int, sourceCharacter int) bool {
return sourceIndex != sourceIndexNotSet &&
sourceLine != notSet &&
sourceCharacter != notSet &&
gen.pendingSourceIndex == sourceIndex &&
(gen.pendingSourceLine > sourceLine ||
gen.pendingSourceLine == sourceLine && gen.pendingSourceCharacter > sourceCharacter)
}
func (gen *Generator) shouldCommitMapping() bool {
return gen.hasPending && (!gen.hasLast ||
gen.lastGeneratedLine != gen.pendingGeneratedLine ||
gen.lastGeneratedCharacter != gen.pendingGeneratedCharacter ||
gen.lastSourceIndex != gen.pendingSourceIndex ||
gen.lastSourceLine != gen.pendingSourceLine ||
gen.lastSourceCharacter != gen.pendingSourceCharacter ||
gen.lastNameIndex != gen.pendingNameIndex)
}
func (gen *Generator) appendMappingCharCode(charCode rune) {
gen.mappings.WriteRune(charCode)
}
func (gen *Generator) appendBase64VLQ(inValue int) {
// Add a new least significant bit that has the sign of the value.
// if negative number the least significant bit that gets added to the number has value 1
// else least significant bit value that gets added is 0
// eg. -1 changes to binary : 01 [1] => 3
// +1 changes to binary : 01 [0] => 2
if inValue < 0 {
inValue = ((-inValue) << 1) + 1
} else {
inValue = inValue << 1
}
// Encode 5 bits at a time starting from least significant bits
for {
currentDigit := inValue & 31 // 11111
inValue = inValue >> 5
if inValue > 0 {
// There are still more digits to decode, set the msb (6th bit)
currentDigit = currentDigit | 32
}
gen.appendMappingCharCode(base64FormatEncode(currentDigit))
if inValue <= 0 {
break
}
}
}
func (gen *Generator) commitPendingMapping() {
if !gen.shouldCommitMapping() {
return
}
// Line/Comma delimiters
if gen.lastGeneratedLine < gen.pendingGeneratedLine {
// Emit line delimiters
for {
gen.appendMappingCharCode(';')
gen.lastGeneratedLine++
if gen.lastGeneratedLine >= gen.pendingGeneratedLine {
break
}
}
// Only need to set this once
gen.lastGeneratedCharacter = 0
} else {
if gen.lastGeneratedLine != gen.pendingGeneratedLine {
// panic rather than error as an invariant has been violated
panic("generatedLine cannot backtrack")
}
// Emit comma to separate the entry
if gen.hasLast {
gen.appendMappingCharCode(',')
}
}
// 1. Relative generated character
gen.appendBase64VLQ(gen.pendingGeneratedCharacter - gen.lastGeneratedCharacter)
gen.lastGeneratedCharacter = gen.pendingGeneratedCharacter
if gen.hasPendingSource {
// 2. Relative sourceIndex
gen.appendBase64VLQ(int(gen.pendingSourceIndex - gen.lastSourceIndex))
gen.lastSourceIndex = gen.pendingSourceIndex
// 3. Relative source line
gen.appendBase64VLQ(gen.pendingSourceLine - gen.lastSourceLine)
gen.lastSourceLine = gen.pendingSourceLine
// 4. Relative source character
gen.appendBase64VLQ(gen.pendingSourceCharacter - gen.lastSourceCharacter)
gen.lastSourceCharacter = gen.pendingSourceCharacter
if gen.hasPendingName {
// 5. Relative nameIndex
gen.appendBase64VLQ(int(gen.pendingNameIndex - gen.lastNameIndex))
gen.lastNameIndex = gen.pendingNameIndex
}
}
gen.hasLast = true
}
func (gen *Generator) addMapping(generatedLine int, generatedCharacter int, sourceIndex SourceIndex, sourceLine int, sourceCharacter int, nameIndex NameIndex) {
if gen.isNewGeneratedPosition(generatedLine, generatedCharacter) ||
gen.isBacktrackingSourcePosition(sourceIndex, sourceLine, sourceCharacter) {
gen.commitPendingMapping()
gen.pendingGeneratedLine = generatedLine
gen.pendingGeneratedCharacter = generatedCharacter
gen.hasPendingSource = false
gen.hasPendingName = false
gen.hasPending = true
}
if sourceIndex != sourceIndexNotSet && sourceLine != notSet && sourceCharacter != notSet {
gen.pendingSourceIndex = sourceIndex
gen.pendingSourceLine = sourceLine
gen.pendingSourceCharacter = sourceCharacter
gen.hasPendingSource = true
if nameIndex != nameIndexNotSet {
gen.pendingNameIndex = nameIndex
gen.hasPendingName = true
}
}
}
// Adds a mapping without source information
func (gen *Generator) AddGeneratedMapping(generatedLine int, generatedCharacter int) error {
if generatedLine < gen.pendingGeneratedLine {
return errors.New("generatedLine cannot backtrack")
}
if generatedCharacter < 0 {
return errors.New("generatedCharacter cannot be negative")
}
gen.addMapping(generatedLine, generatedCharacter, sourceIndexNotSet, notSet /*sourceLine*/, notSet /*sourceCharacter*/, nameIndexNotSet)
return nil
}
// Adds a mapping with source information
func (gen *Generator) AddSourceMapping(generatedLine int, generatedCharacter int, sourceIndex SourceIndex, sourceLine int, sourceCharacter int) error {
if generatedLine < gen.pendingGeneratedLine {
return errors.New("generatedLine cannot backtrack")
}
if generatedCharacter < 0 {
return errors.New("generatedCharacter cannot be negative")
}
if sourceIndex < 0 || int(sourceIndex) >= len(gen.sources) {
return errors.New("sourceIndex is out of range")
}
if sourceLine < 0 {
return errors.New("sourceLine cannot be negative")
}
if sourceCharacter < 0 {
return errors.New("sourceCharacter cannot be negative")
}
gen.addMapping(generatedLine, generatedCharacter, sourceIndex, sourceLine, sourceCharacter, nameIndexNotSet)
return nil
}
// Adds a mapping with source and name information
func (gen *Generator) AddNamedSourceMapping(generatedLine int, generatedCharacter int, sourceIndex SourceIndex, sourceLine int, sourceCharacter int, nameIndex NameIndex) error {
if generatedLine < gen.pendingGeneratedLine {
return errors.New("generatedLine cannot backtrack")
}
if generatedCharacter < 0 {
return errors.New("generatedCharacter cannot be negative")
}
if sourceIndex < 0 || int(sourceIndex) >= len(gen.sources) {
return errors.New("sourceIndex is out of range")
}
if sourceLine < 0 {
return errors.New("sourceLine cannot be negative")
}
if sourceCharacter < 0 {
return errors.New("sourceCharacter cannot be negative")
}
if nameIndex < 0 || int(nameIndex) >= len(gen.names) {
return errors.New("nameIndex is out of range")
}
gen.addMapping(generatedLine, generatedCharacter, sourceIndex, sourceLine, sourceCharacter, nameIndex)
return nil
}
// Gets the source map as a `RawSourceMap` object
func (gen *Generator) RawSourceMap() *RawSourceMap {
gen.commitPendingMapping()
sources := slices.Clone(gen.sources)
if sources == nil {
sources = []string{}
}
names := slices.Clone(gen.names)
if names == nil {
names = []string{}
}
return &RawSourceMap{
Version: 3,
File: gen.file,
SourceRoot: gen.sourceRoot,
Sources: sources,
Names: names,
Mappings: gen.mappings.String(),
SourcesContent: slices.Clone(gen.sourcesContent),
}
}
// Gets the string representation of the source map
func (gen *Generator) String() string {
buf, err := json.Marshal(gen.RawSourceMap())
if err != nil {
panic(err.Error())
}
return string(buf)
}
func base64FormatEncode(value int) rune {
switch {
case value >= 0 && value < 26:
return 'A' + rune(value)
case value >= 26 && value < 52:
return 'a' + rune(value) - 26
case value >= 52 && value < 62:
return '0' + rune(value) - 52
case value == 62:
return '+'
case value == 63:
return '/'
default:
panic("not a base64 value")
}
}

View File

@ -1,383 +0,0 @@
package sourcemap
import (
"testing"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
"gotest.tools/v3/assert"
)
func TestSourceMapGenerator_Empty(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
sourceMap := gen.RawSourceMap()
assert.DeepEqual(t, sourceMap, &RawSourceMap{
Version: 3,
File: "main.js",
SourceRoot: "/",
Sources: []string{},
Mappings: "",
Names: []string{},
SourcesContent: nil,
})
}
func TestSourceMapGenerator_Empty_Serialized(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
actual := gen.String()
expected := `{"version":3,"file":"main.js","sourceRoot":"/","sources":[],"names":[],"mappings":""}`
assert.Equal(t, actual, expected)
}
func TestSourceMapGenerator_AddSource(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
sourceIndex := gen.AddSource("/main.ts")
sourceMap := gen.RawSourceMap()
assert.Equal(t, int(sourceIndex), 0)
assert.DeepEqual(t, sourceMap, &RawSourceMap{
Version: 3,
File: "main.js",
SourceRoot: "/",
Sources: []string{"main.ts"},
Mappings: "",
Names: []string{},
SourcesContent: nil,
})
}
func TestSourceMapGenerator_SetSourceContent(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
sourceIndex := gen.AddSource("/main.ts")
sourceContent := "foo"
assert.NilError(t, gen.SetSourceContent(sourceIndex, sourceContent))
sourceMap := gen.RawSourceMap()
assert.Equal(t, int(sourceIndex), 0)
assert.DeepEqual(t, sourceMap, &RawSourceMap{
Version: 3,
File: "main.js",
SourceRoot: "/",
Sources: []string{"main.ts"},
Mappings: "",
Names: []string{},
SourcesContent: []*string{&sourceContent},
})
}
func TestSourceMapGenerator_SetSourceContent_ForSecondSourceOnly(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
gen.AddSource("/skipped.ts")
sourceIndex := gen.AddSource("/main.ts")
sourceContent := "foo"
assert.NilError(t, gen.SetSourceContent(sourceIndex, sourceContent))
sourceMap := gen.RawSourceMap()
assert.Equal(t, int(sourceIndex), 1)
assert.DeepEqual(t, sourceMap, &RawSourceMap{
Version: 3,
File: "main.js",
SourceRoot: "/",
Sources: []string{"skipped.ts", "main.ts"},
Mappings: "",
Names: []string{},
SourcesContent: []*string{nil, &sourceContent},
})
}
func TestSourceMapGenerator_SetSourceContent_SourceIndexOutOfRange(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
assert.Error(t, gen.SetSourceContent(-1, ""), "sourceIndex is out of range")
assert.Error(t, gen.SetSourceContent(0, ""), "sourceIndex is out of range")
}
func TestSourceMapGenerator_SetSourceContent_ForSecondSourceOnly_Serialized(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
gen.AddSource("/skipped.ts")
sourceIndex := gen.AddSource("/main.ts")
sourceContent := "foo"
assert.NilError(t, gen.SetSourceContent(sourceIndex, sourceContent))
actual := gen.String()
expected := `{"version":3,"file":"main.js","sourceRoot":"/","sources":["skipped.ts","main.ts"],"names":[],"mappings":"","sourcesContent":[null,"foo"]}`
assert.Equal(t, actual, expected)
}
func TestSourceMapGenerator_AddName(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
nameIndex := gen.AddName("foo")
sourceMap := gen.RawSourceMap()
assert.Equal(t, int(nameIndex), 0)
assert.DeepEqual(t, sourceMap, &RawSourceMap{
Version: 3,
File: "main.js",
SourceRoot: "/",
Sources: []string{},
Mappings: "",
Names: []string{"foo"},
SourcesContent: nil,
})
}
func TestSourceMapGenerator_AddGeneratedMapping(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
assert.NilError(t, gen.AddGeneratedMapping(0, 0))
sourceMap := gen.RawSourceMap()
assert.DeepEqual(t, sourceMap, &RawSourceMap{
Version: 3,
File: "main.js",
SourceRoot: "/",
Sources: []string{},
Mappings: "A",
Names: []string{},
SourcesContent: nil,
})
}
func TestSourceMapGenerator_AddGeneratedMapping_OnSecondLineOnly(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
assert.NilError(t, gen.AddGeneratedMapping(1, 0))
sourceMap := gen.RawSourceMap()
assert.DeepEqual(t, sourceMap, &RawSourceMap{
Version: 3,
File: "main.js",
SourceRoot: "/",
Sources: []string{},
Mappings: ";A",
Names: []string{},
SourcesContent: nil,
})
}
func TestSourceMapGenerator_AddSourceMapping(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
sourceIndex := gen.AddSource("/main.ts")
assert.NilError(t, gen.AddSourceMapping(0, 0, sourceIndex, 0, 0))
sourceMap := gen.RawSourceMap()
assert.DeepEqual(t, sourceMap, &RawSourceMap{
Version: 3,
File: "main.js",
SourceRoot: "/",
Sources: []string{"main.ts"},
Mappings: "AAAA",
Names: []string{},
SourcesContent: nil,
})
}
func TestSourceMapGenerator_AddSourceMapping_NextGeneratedCharacter(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
sourceIndex := gen.AddSource("/main.ts")
assert.NilError(t, gen.AddSourceMapping(0, 0, sourceIndex, 0, 0))
assert.NilError(t, gen.AddSourceMapping(0, 1, sourceIndex, 0, 0))
sourceMap := gen.RawSourceMap()
assert.DeepEqual(t, sourceMap, &RawSourceMap{
Version: 3,
File: "main.js",
SourceRoot: "/",
Sources: []string{"main.ts"},
Mappings: "AAAA,CAAA",
Names: []string{},
SourcesContent: nil,
})
}
func TestSourceMapGenerator_AddSourceMapping_NextGeneratedAndSourceCharacter(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
sourceIndex := gen.AddSource("/main.ts")
assert.NilError(t, gen.AddSourceMapping(0, 0, sourceIndex, 0, 0))
assert.NilError(t, gen.AddSourceMapping(0, 1, sourceIndex, 0, 1))
sourceMap := gen.RawSourceMap()
assert.DeepEqual(t, sourceMap, &RawSourceMap{
Version: 3,
File: "main.js",
SourceRoot: "/",
Sources: []string{"main.ts"},
Mappings: "AAAA,CAAC",
Names: []string{},
SourcesContent: nil,
})
}
func TestSourceMapGenerator_AddSourceMapping_NextGeneratedLine(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
sourceIndex := gen.AddSource("/main.ts")
assert.NilError(t, gen.AddSourceMapping(0, 0, sourceIndex, 0, 0))
assert.NilError(t, gen.AddSourceMapping(1, 0, sourceIndex, 0, 0))
sourceMap := gen.RawSourceMap()
assert.DeepEqual(t, sourceMap, &RawSourceMap{
Version: 3,
File: "main.js",
SourceRoot: "/",
Sources: []string{"main.ts"},
Mappings: "AAAA;AAAA",
Names: []string{},
SourcesContent: nil,
})
}
func TestSourceMapGenerator_AddSourceMapping_PreviousSourceCharacter(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
sourceIndex := gen.AddSource("/main.ts")
assert.NilError(t, gen.AddSourceMapping(0, 0, sourceIndex, 0, 1))
assert.NilError(t, gen.AddSourceMapping(0, 1, sourceIndex, 0, 0))
sourceMap := gen.RawSourceMap()
assert.DeepEqual(t, sourceMap, &RawSourceMap{
Version: 3,
File: "main.js",
SourceRoot: "/",
Sources: []string{"main.ts"},
Mappings: "AAAC,CAAD",
Names: []string{},
SourcesContent: nil,
})
}
func TestSourceMapGenerator_AddNamedSourceMapping(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
sourceIndex := gen.AddSource("/main.ts")
nameIndex := gen.AddName("foo")
assert.NilError(t, gen.AddNamedSourceMapping(0, 0, sourceIndex, 0, 0, nameIndex))
sourceMap := gen.RawSourceMap()
assert.DeepEqual(t, sourceMap, &RawSourceMap{
Version: 3,
File: "main.js",
SourceRoot: "/",
Sources: []string{"main.ts"},
Mappings: "AAAAA",
Names: []string{"foo"},
SourcesContent: nil,
})
}
func TestSourceMapGenerator_AddNamedSourceMapping_WithPreviousName(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
sourceIndex := gen.AddSource("/main.ts")
nameIndex1 := gen.AddName("foo")
nameIndex2 := gen.AddName("bar")
assert.NilError(t, gen.AddNamedSourceMapping(0, 0, sourceIndex, 0, 0, nameIndex2))
assert.NilError(t, gen.AddNamedSourceMapping(0, 1, sourceIndex, 0, 0, nameIndex1))
sourceMap := gen.RawSourceMap()
assert.DeepEqual(t, sourceMap, &RawSourceMap{
Version: 3,
File: "main.js",
SourceRoot: "/",
Sources: []string{"main.ts"},
Mappings: "AAAAC,CAAAD",
Names: []string{"foo", "bar"},
SourcesContent: nil,
})
}
func TestSourceMapGenerator_AddGeneratedMapping_GeneratedLineCannotBacktrack(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
assert.NilError(t, gen.AddGeneratedMapping(1, 0))
assert.Error(t, gen.AddGeneratedMapping(0, 0), "generatedLine cannot backtrack")
}
func TestSourceMapGenerator_AddGeneratedMapping_GeneratedCharacterCannotBeNegative(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
assert.NilError(t, gen.AddGeneratedMapping(0, 0))
assert.Error(t, gen.AddGeneratedMapping(0, -1), "generatedCharacter cannot be negative")
}
func TestSourceMapGenerator_AddSourceMapping_GeneratedLineCannotBacktrack(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
sourceIndex := gen.AddSource("/main.ts")
assert.NilError(t, gen.AddSourceMapping(1, 0, sourceIndex, 0, 0))
assert.Error(t, gen.AddSourceMapping(0, 0, sourceIndex, 0, 0), "generatedLine cannot backtrack")
}
func TestSourceMapGenerator_AddSourceMapping_GeneratedCharacterCannotBeNegative(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
sourceIndex := gen.AddSource("/main.ts")
assert.NilError(t, gen.AddSourceMapping(0, 0, sourceIndex, 0, 0))
assert.Error(t, gen.AddSourceMapping(0, -1, sourceIndex, 0, 0), "generatedCharacter cannot be negative")
}
func TestSourceMapGenerator_AddSourceMapping_SourceIndexIsOutOfRange(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
assert.Error(t, gen.AddSourceMapping(0, 0, -1, 0, 0), "sourceIndex is out of range")
assert.Error(t, gen.AddSourceMapping(0, 0, 0, 0, 0), "sourceIndex is out of range")
}
func TestSourceMapGenerator_AddSourceMapping_SourceLineCannotBeNegative(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
sourceIndex := gen.AddSource("/main.ts")
assert.Error(t, gen.AddSourceMapping(0, 0, sourceIndex, -1, 0), "sourceLine cannot be negative")
}
func TestSourceMapGenerator_AddSourceMapping_SourceCharacterCannotBeNegative(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
sourceIndex := gen.AddSource("/main.ts")
assert.Error(t, gen.AddSourceMapping(0, 0, sourceIndex, 0, -1), "sourceCharacter cannot be negative")
}
func TestSourceMapGenerator_AddNamedSourceMapping_GeneratedLineCannotBacktrack(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
sourceIndex := gen.AddSource("/main.ts")
nameIndex := gen.AddName("foo")
assert.NilError(t, gen.AddNamedSourceMapping(1, 0, sourceIndex, 0, 0, nameIndex))
assert.Error(t, gen.AddNamedSourceMapping(0, 0, sourceIndex, 0, 0, nameIndex), "generatedLine cannot backtrack")
}
func TestSourceMapGenerator_AddNamedSourceMapping_GeneratedCharacterCannotBeNegative(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
sourceIndex := gen.AddSource("/main.ts")
nameIndex := gen.AddName("foo")
assert.NilError(t, gen.AddNamedSourceMapping(0, 0, sourceIndex, 0, 0, nameIndex))
assert.Error(t, gen.AddNamedSourceMapping(0, -1, sourceIndex, 0, 0, nameIndex), "generatedCharacter cannot be negative")
}
func TestSourceMapGenerator_AddNamedSourceMapping_SourceIndexIsOutOfRange(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
nameIndex := gen.AddName("foo")
assert.Error(t, gen.AddNamedSourceMapping(0, 0, -1, 0, 0, nameIndex), "sourceIndex is out of range")
assert.Error(t, gen.AddNamedSourceMapping(0, 0, 0, 0, 0, nameIndex), "sourceIndex is out of range")
}
func TestSourceMapGenerator_AddNamedSourceMapping_SourceLineCannotBeNegative(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
nameIndex := gen.AddName("foo")
sourceIndex := gen.AddSource("/main.ts")
assert.Error(t, gen.AddNamedSourceMapping(0, 0, sourceIndex, -1, 0, nameIndex), "sourceLine cannot be negative")
}
func TestSourceMapGenerator_AddNamedSourceMapping_SourceCharacterCannotBeNegative(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
nameIndex := gen.AddName("foo")
sourceIndex := gen.AddSource("/main.ts")
assert.Error(t, gen.AddNamedSourceMapping(0, 0, sourceIndex, 0, -1, nameIndex), "sourceCharacter cannot be negative")
}
func TestSourceMapGenerator_AddNamedSourceMapping_NameIndexIsOutOfRange(t *testing.T) {
t.Parallel()
gen := NewGenerator("main.js", "/", "/", tspath.ComparePathsOptions{})
sourceIndex := gen.AddSource("/main.ts")
assert.Error(t, gen.AddNamedSourceMapping(0, 0, sourceIndex, 0, 0, -1), "nameIndex is out of range")
assert.Error(t, gen.AddNamedSourceMapping(0, 0, sourceIndex, 0, 0, 0), "nameIndex is out of range")
}

View File

@ -1,30 +0,0 @@
package sourcemap
import "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
type ECMALineInfo struct {
text string
lineStarts core.ECMALineStarts
}
func CreateECMALineInfo(text string, lineStarts core.ECMALineStarts) *ECMALineInfo {
return &ECMALineInfo{
text: text,
lineStarts: lineStarts,
}
}
func (li *ECMALineInfo) LineCount() int {
return len(li.lineStarts)
}
func (li *ECMALineInfo) LineText(line int) string {
pos := li.lineStarts[line]
var end core.TextPos
if line+1 < len(li.lineStarts) {
end = li.lineStarts[line+1]
} else {
end = core.TextPos(len(li.text))
}
return li.text[pos:end]
}

View File

@ -1,9 +0,0 @@
package sourcemap
import "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
type Source interface {
Text() string
FileName() string
ECMALineMap() []core.TextPos
}

View File

@ -1,313 +0,0 @@
package sourcemap
import (
"encoding/base64"
"slices"
"strings"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/stringutil"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
"github.com/go-json-experiment/json"
)
type Host interface {
UseCaseSensitiveFileNames() bool
GetECMALineInfo(fileName string) *ECMALineInfo
ReadFile(fileName string) (string, bool)
}
// Similar to `Mapping`, but position-based.
type MappedPosition struct {
generatedPosition int
sourcePosition int
sourceIndex SourceIndex
nameIndex NameIndex
}
const (
missingPosition = -1
)
func (m *MappedPosition) isSourceMappedPosition() bool {
return m.sourceIndex != MissingSource && m.sourcePosition != missingPosition
}
type SourceMappedPosition = MappedPosition
// Maps source positions to generated positions and vice versa.
type DocumentPositionMapper struct {
useCaseSensitiveFileNames bool
sourceFileAbsolutePaths []string
sourceToSourceIndexMap map[string]SourceIndex
generatedAbsoluteFilePath string
generatedMappings []*MappedPosition
sourceMappings map[SourceIndex][]*SourceMappedPosition
}
func createDocumentPositionMapper(host Host, sourceMap *RawSourceMap, mapPath string) *DocumentPositionMapper {
mapDirectory := tspath.GetDirectoryPath(mapPath)
var sourceRoot string
if sourceMap.SourceRoot != "" {
sourceRoot = tspath.GetNormalizedAbsolutePath(sourceMap.SourceRoot, mapDirectory)
} else {
sourceRoot = mapDirectory
}
generatedAbsoluteFilePath := tspath.GetNormalizedAbsolutePath(sourceMap.File, mapDirectory)
sourceFileAbsolutePaths := core.Map(sourceMap.Sources, func(source string) string {
return tspath.GetNormalizedAbsolutePath(source, sourceRoot)
})
useCaseSensitiveFileNames := host.UseCaseSensitiveFileNames()
sourceToSourceIndexMap := make(map[string]SourceIndex, len(sourceFileAbsolutePaths))
for i, source := range sourceFileAbsolutePaths {
sourceToSourceIndexMap[tspath.GetCanonicalFileName(source, useCaseSensitiveFileNames)] = SourceIndex(i)
}
var decodedMappings []*MappedPosition
var generatedMappings []*MappedPosition
sourceMappings := make(map[SourceIndex][]*SourceMappedPosition)
// getDecodedMappings()
decoder := DecodeMappings(sourceMap.Mappings)
for mapping := range decoder.Values() {
// processMapping()
generatedPosition := -1
lineInfo := host.GetECMALineInfo(generatedAbsoluteFilePath)
if lineInfo != nil {
generatedPosition = scanner.ComputePositionOfLineAndCharacterEx(
lineInfo.lineStarts,
mapping.GeneratedLine,
mapping.GeneratedCharacter,
&lineInfo.text,
true, /*allowEdits*/
)
}
sourcePosition := -1
if mapping.IsSourceMapping() {
lineInfo := host.GetECMALineInfo(sourceFileAbsolutePaths[mapping.SourceIndex])
if lineInfo != nil {
pos := scanner.ComputePositionOfLineAndCharacterEx(
lineInfo.lineStarts,
mapping.SourceLine,
mapping.SourceCharacter,
&lineInfo.text,
true, /*allowEdits*/
)
sourcePosition = pos
}
}
decodedMappings = append(decodedMappings, &MappedPosition{
generatedPosition: generatedPosition,
sourceIndex: mapping.SourceIndex,
sourcePosition: sourcePosition,
nameIndex: mapping.NameIndex,
})
}
if decoder.Error() != nil {
decodedMappings = nil
}
// getSourceMappings()
for _, mapping := range decodedMappings {
if !mapping.isSourceMappedPosition() {
continue
}
sourceIndex := mapping.sourceIndex
list := sourceMappings[sourceIndex]
list = append(list, &SourceMappedPosition{
generatedPosition: mapping.generatedPosition,
sourceIndex: sourceIndex,
sourcePosition: mapping.sourcePosition,
nameIndex: mapping.nameIndex,
})
sourceMappings[sourceIndex] = list
}
for i, list := range sourceMappings {
slices.SortFunc(list, func(a, b *SourceMappedPosition) int {
debug.Assert(a.sourceIndex == b.sourceIndex, "All source mappings should have the same source index")
return a.sourcePosition - b.sourcePosition
})
sourceMappings[i] = core.DeduplicateSorted(list, func(a, b *SourceMappedPosition) bool {
return a.generatedPosition == b.generatedPosition &&
a.sourceIndex == b.sourceIndex &&
a.sourcePosition == b.sourcePosition
})
}
// getGeneratedMappings()
generatedMappings = decodedMappings
slices.SortFunc(generatedMappings, func(a, b *MappedPosition) int {
return a.generatedPosition - b.generatedPosition
})
generatedMappings = core.DeduplicateSorted(generatedMappings, func(a, b *MappedPosition) bool {
return a.generatedPosition == b.generatedPosition &&
a.sourceIndex == b.sourceIndex &&
a.sourcePosition == b.sourcePosition
})
return &DocumentPositionMapper{
useCaseSensitiveFileNames: useCaseSensitiveFileNames,
sourceFileAbsolutePaths: sourceFileAbsolutePaths,
sourceToSourceIndexMap: sourceToSourceIndexMap,
generatedAbsoluteFilePath: generatedAbsoluteFilePath,
generatedMappings: generatedMappings,
sourceMappings: sourceMappings,
}
}
type DocumentPosition struct {
FileName string
Pos int
}
func (d *DocumentPositionMapper) GetSourcePosition(loc *DocumentPosition) *DocumentPosition {
if d == nil {
return nil
}
if len(d.generatedMappings) == 0 {
return nil
}
targetIndex, _ := slices.BinarySearchFunc(d.generatedMappings, loc.Pos, func(m *MappedPosition, pos int) int {
return m.generatedPosition - pos
})
if targetIndex < 0 || targetIndex >= len(d.generatedMappings) {
return nil
}
mapping := d.generatedMappings[targetIndex]
if !mapping.isSourceMappedPosition() {
return nil
}
// Closest position
return &DocumentPosition{
FileName: d.sourceFileAbsolutePaths[mapping.sourceIndex],
Pos: mapping.sourcePosition,
}
}
func (d *DocumentPositionMapper) GetGeneratedPosition(loc *DocumentPosition) *DocumentPosition {
if d == nil {
return nil
}
sourceIndex, ok := d.sourceToSourceIndexMap[tspath.GetCanonicalFileName(loc.FileName, d.useCaseSensitiveFileNames)]
if !ok {
return nil
}
if sourceIndex < 0 || int(sourceIndex) >= len(d.sourceMappings) {
return nil
}
sourceMappings := d.sourceMappings[sourceIndex]
targetIndex, _ := slices.BinarySearchFunc(sourceMappings, loc.Pos, func(m *SourceMappedPosition, pos int) int {
return m.sourcePosition - pos
})
if targetIndex < 0 || targetIndex >= len(sourceMappings) {
return nil
}
mapping := sourceMappings[targetIndex]
if mapping.sourceIndex != sourceIndex {
return nil
}
// Closest position
return &DocumentPosition{
FileName: d.generatedAbsoluteFilePath,
Pos: mapping.generatedPosition,
}
}
func GetDocumentPositionMapper(host Host, generatedFileName string) *DocumentPositionMapper {
mapFileName := tryGetSourceMappingURL(host, generatedFileName)
if mapFileName != "" {
if base64Object, matched := tryParseBase64Url(mapFileName); matched {
if base64Object != "" {
if decoded, err := base64.StdEncoding.DecodeString(base64Object); err == nil {
return convertDocumentToSourceMapper(host, string(decoded), generatedFileName)
}
}
// Not a data URL we can parse, skip it
mapFileName = ""
}
}
var possibleMapLocations []string
if mapFileName != "" {
possibleMapLocations = append(possibleMapLocations, mapFileName)
}
possibleMapLocations = append(possibleMapLocations, generatedFileName+".map")
for _, location := range possibleMapLocations {
mapFileName := tspath.GetNormalizedAbsolutePath(location, tspath.GetDirectoryPath(generatedFileName))
if mapFileContents, ok := host.ReadFile(mapFileName); ok {
return convertDocumentToSourceMapper(host, mapFileContents, mapFileName)
}
}
return nil
}
func convertDocumentToSourceMapper(host Host, contents string, mapFileName string) *DocumentPositionMapper {
sourceMap := tryParseRawSourceMap(contents)
if sourceMap == nil || len(sourceMap.Sources) == 0 || sourceMap.File == "" || sourceMap.Mappings == "" {
// invalid map
return nil
}
// Don't support source maps that contain inlined sources
if core.Some(sourceMap.SourcesContent, func(s *string) bool { return s != nil }) {
return nil
}
return createDocumentPositionMapper(host, sourceMap, mapFileName)
}
func tryParseRawSourceMap(contents string) *RawSourceMap {
sourceMap := &RawSourceMap{}
err := json.Unmarshal([]byte(contents), sourceMap)
if err != nil {
return nil
}
if sourceMap.Version != 3 {
return nil
}
return sourceMap
}
func tryGetSourceMappingURL(host Host, fileName string) string {
lineInfo := host.GetECMALineInfo(fileName)
return TryGetSourceMappingURL(lineInfo)
}
// Equivalent to /^data:(?:application\/json;(?:charset=[uU][tT][fF]-8;)?base64,([A-Za-z0-9+/=]+)$)?/
func tryParseBase64Url(url string) (parseableUrl string, isBase64Url bool) {
var found bool
if url, found = strings.CutPrefix(url, `data:`); !found {
return "", false
}
if url, found = strings.CutPrefix(url, `application/json;`); !found {
return "", true
}
if url, found = strings.CutPrefix(url, `charset=`); found {
if !strings.EqualFold(url[:len(`utf-8;`)], `utf-8;`) {
return "", true
}
url = url[len(`utf-8;`):]
}
if url, found = strings.CutPrefix(url, `base64,`); !found {
return "", true
}
for _, r := range url {
if !(stringutil.IsASCIILetter(r) || stringutil.IsDigit(r) || r == '+' || r == '/' || r == '=') {
return "", true
}
}
return url, true
}

View File

@ -1,27 +0,0 @@
package sourcemap
import (
"strings"
"unicode"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/stringutil"
)
// Tries to find the sourceMappingURL comment at the end of a file.
func TryGetSourceMappingURL(lineInfo *ECMALineInfo) string {
for index := lineInfo.LineCount() - 1; index >= 0; index-- {
line := lineInfo.LineText(index)
line = strings.TrimLeftFunc(line, unicode.IsSpace)
line = strings.TrimRightFunc(line, stringutil.IsLineBreak)
if len(line) == 0 {
continue
}
if len(line) < 4 || !strings.HasPrefix(line, "//") || line[2] != '#' && line[2] != '@' || line[3] != ' ' {
break
}
if url, ok := strings.CutPrefix(line[4:], "sourceMappingURL="); ok {
return strings.TrimRightFunc(url, unicode.IsSpace)
}
}
return ""
}