vendor tsgo
This commit is contained in:
379
tools/tsgo/internal/sourcemap/generator.go
Normal file
379
tools/tsgo/internal/sourcemap/generator.go
Normal file
@@ -0,0 +1,379 @@
|
||||
package sourcemap
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/json"
|
||||
"github.com/microsoft/typescript-go/internal/tspath"
|
||||
)
|
||||
|
||||
type (
|
||||
SourceIndex int
|
||||
NameIndex int
|
||||
)
|
||||
|
||||
const (
|
||||
sourceIndexNotSet SourceIndex = -1
|
||||
nameIndexNotSet NameIndex = -1
|
||||
notSet int = -1
|
||||
notSetUTF16 core.UTF16Offset = -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 core.UTF16Offset
|
||||
lastSourceIndex SourceIndex
|
||||
lastSourceLine int
|
||||
lastSourceCharacter core.UTF16Offset
|
||||
lastNameIndex NameIndex
|
||||
hasLast bool
|
||||
pendingGeneratedLine int
|
||||
pendingGeneratedCharacter core.UTF16Offset
|
||||
pendingSourceIndex SourceIndex
|
||||
pendingSourceLine int
|
||||
pendingSourceCharacter core.UTF16Offset
|
||||
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 core.UTF16Offset) bool {
|
||||
return !gen.hasPending ||
|
||||
gen.pendingGeneratedLine != generatedLine ||
|
||||
gen.pendingGeneratedCharacter != generatedCharacter
|
||||
}
|
||||
|
||||
func (gen *Generator) isBacktrackingSourcePosition(sourceIndex SourceIndex, sourceLine int, sourceCharacter core.UTF16Offset) bool {
|
||||
return sourceIndex != sourceIndexNotSet &&
|
||||
sourceLine != notSet &&
|
||||
sourceCharacter != notSetUTF16 &&
|
||||
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(int(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(int(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 core.UTF16Offset, sourceIndex SourceIndex, sourceLine int, sourceCharacter core.UTF16Offset, 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 != notSetUTF16 {
|
||||
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 core.UTF16Offset) 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*/, notSetUTF16 /*sourceCharacter*/, nameIndexNotSet)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Adds a mapping with source information
|
||||
func (gen *Generator) AddSourceMapping(generatedLine int, generatedCharacter core.UTF16Offset, sourceIndex SourceIndex, sourceLine int, sourceCharacter core.UTF16Offset) 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 core.UTF16Offset, sourceIndex SourceIndex, sourceLine int, sourceCharacter core.UTF16Offset, 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),
|
||||
}
|
||||
}
|
||||
|
||||
func (gen *Generator) bytes() []byte {
|
||||
buf, err := json.Marshal(gen.RawSourceMap())
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
// Gets the string representation of the source map
|
||||
func (gen *Generator) String() string {
|
||||
return string(gen.bytes())
|
||||
}
|
||||
|
||||
func (gen *Generator) Base64DataURL() string {
|
||||
const prefix = "data:application/json;base64,"
|
||||
data := gen.bytes()
|
||||
var sb strings.Builder
|
||||
sb.Grow(len(prefix) + base64.StdEncoding.EncodedLen(len(data)))
|
||||
sb.WriteString(prefix)
|
||||
encoder := base64.NewEncoder(base64.StdEncoding, &sb)
|
||||
_, _ = encoder.Write(data)
|
||||
encoder.Close()
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user