313 lines
5.4 KiB
Go
313 lines
5.4 KiB
Go
package sourcemap // import "gopkg.in/sourcemap.v1"
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/url"
|
|
"path"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"gopkg.in/sourcemap.v1/base64vlq"
|
|
)
|
|
|
|
type Consumer struct {
|
|
sourceRootURL *url.URL
|
|
smap *sourceMap
|
|
mappings []mapping
|
|
}
|
|
|
|
func Parse(mapURL string, b []byte) (*Consumer, error) {
|
|
smap := new(sourceMap)
|
|
err := json.Unmarshal(b, smap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if smap.Version != 3 {
|
|
return nil, errors.New("sourcemap: only 3rd version is supported")
|
|
}
|
|
|
|
var sourceRootURL *url.URL
|
|
if smap.SourceRoot != "" {
|
|
u, err := url.Parse(smap.SourceRoot)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if u.IsAbs() {
|
|
sourceRootURL = u
|
|
}
|
|
} else if mapURL != "" {
|
|
u, err := url.Parse(mapURL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if u.IsAbs() {
|
|
u.Path = path.Dir(u.Path)
|
|
sourceRootURL = u
|
|
}
|
|
}
|
|
|
|
mappings, err := parseMappings(smap.Mappings)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Free memory.
|
|
smap.Mappings = ""
|
|
|
|
return &Consumer{
|
|
sourceRootURL: sourceRootURL,
|
|
smap: smap,
|
|
mappings: mappings,
|
|
}, nil
|
|
}
|
|
|
|
func (c *Consumer) File() string {
|
|
return c.smap.File
|
|
}
|
|
|
|
func (c *Consumer) Source(genLine, genCol int) (source, name string, line, col int, ok bool) {
|
|
i := sort.Search(len(c.mappings), func(i int) bool {
|
|
m := &c.mappings[i]
|
|
if m.genLine == genLine {
|
|
return m.genCol >= genCol
|
|
}
|
|
return m.genLine >= genLine
|
|
})
|
|
|
|
// Mapping not found.
|
|
if i == len(c.mappings) {
|
|
return
|
|
}
|
|
|
|
match := &c.mappings[i]
|
|
|
|
// Fuzzy match.
|
|
if match.genCol > genCol && i > 0 {
|
|
match = &c.mappings[i-1]
|
|
}
|
|
|
|
if match.sourcesInd >= 0 {
|
|
source = c.absSource(c.smap.Sources[match.sourcesInd])
|
|
}
|
|
if match.namesInd >= 0 {
|
|
iv := c.smap.Names[match.namesInd]
|
|
switch v := iv.(type) {
|
|
case string:
|
|
name = v
|
|
case float64:
|
|
name = strconv.FormatFloat(v, 'f', -1, 64)
|
|
default:
|
|
name = fmt.Sprint(iv)
|
|
}
|
|
}
|
|
line = match.sourceLine
|
|
col = match.sourceCol
|
|
ok = true
|
|
return
|
|
}
|
|
|
|
func (c *Consumer) absSource(source string) string {
|
|
if path.IsAbs(source) {
|
|
return source
|
|
}
|
|
|
|
if u, err := url.Parse(source); err == nil && u.IsAbs() {
|
|
return source
|
|
}
|
|
|
|
if c.sourceRootURL != nil {
|
|
u := *c.sourceRootURL
|
|
u.Path = path.Join(c.sourceRootURL.Path, source)
|
|
return u.String()
|
|
}
|
|
|
|
if c.smap.SourceRoot != "" {
|
|
return path.Join(c.smap.SourceRoot, source)
|
|
}
|
|
|
|
return source
|
|
}
|
|
|
|
func (c *Consumer) SourceName(genLine, genCol int, genName string) (name string, ok bool) {
|
|
ind := sort.Search(len(c.mappings), func(i int) bool {
|
|
m := c.mappings[i]
|
|
if m.genLine == genLine {
|
|
return m.genCol >= genCol
|
|
}
|
|
return m.genLine >= genLine
|
|
})
|
|
|
|
// Mapping not found.
|
|
if ind == len(c.mappings) {
|
|
return "", false
|
|
}
|
|
|
|
for i := ind; i >= 0; i-- {
|
|
m := c.mappings[i]
|
|
if m.namesInd == -1 {
|
|
continue
|
|
}
|
|
if c.smap.Names[m.namesInd] == "" {
|
|
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
type fn func() (fn, error)
|
|
|
|
type sourceMap struct {
|
|
Version int `json:"version"`
|
|
File string `json:"file"`
|
|
SourceRoot string `json:"sourceRoot"`
|
|
Sources []string `json:"sources"`
|
|
Names []interface{} `json:"names"`
|
|
Mappings string `json:"mappings"`
|
|
}
|
|
|
|
type mapping struct {
|
|
genLine int
|
|
genCol int
|
|
sourcesInd int
|
|
sourceLine int
|
|
sourceCol int
|
|
namesInd int
|
|
}
|
|
|
|
type mappings struct {
|
|
rd *strings.Reader
|
|
dec *base64vlq.Decoder
|
|
|
|
genLine int
|
|
genCol int
|
|
sourcesInd int
|
|
sourceLine int
|
|
sourceCol int
|
|
namesInd int
|
|
|
|
value mapping
|
|
values []mapping
|
|
}
|
|
|
|
func parseMappings(s string) ([]mapping, error) {
|
|
rd := strings.NewReader(s)
|
|
m := &mappings{
|
|
rd: rd,
|
|
dec: base64vlq.NewDecoder(rd),
|
|
|
|
genLine: 1,
|
|
sourceLine: 1,
|
|
}
|
|
m.zeroValue()
|
|
err := m.parse()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return m.values, nil
|
|
}
|
|
|
|
func (m *mappings) parse() error {
|
|
next := m.parseGenCol
|
|
for {
|
|
c, err := m.rd.ReadByte()
|
|
if err == io.EOF {
|
|
m.pushValue()
|
|
return nil
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch c {
|
|
case ',':
|
|
m.pushValue()
|
|
next = m.parseGenCol
|
|
case ';':
|
|
m.pushValue()
|
|
|
|
m.genLine++
|
|
m.genCol = 0
|
|
|
|
next = m.parseGenCol
|
|
default:
|
|
m.rd.UnreadByte()
|
|
|
|
var err error
|
|
next, err = next()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (m *mappings) parseGenCol() (fn, error) {
|
|
n, err := m.dec.Decode()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m.genCol += n
|
|
m.value.genCol = m.genCol
|
|
return m.parseSourcesInd, nil
|
|
}
|
|
|
|
func (m *mappings) parseSourcesInd() (fn, error) {
|
|
n, err := m.dec.Decode()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m.sourcesInd += n
|
|
m.value.sourcesInd = m.sourcesInd
|
|
return m.parseSourceLine, nil
|
|
}
|
|
|
|
func (m *mappings) parseSourceLine() (fn, error) {
|
|
n, err := m.dec.Decode()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m.sourceLine += n
|
|
m.value.sourceLine = m.sourceLine
|
|
return m.parseSourceCol, nil
|
|
}
|
|
|
|
func (m *mappings) parseSourceCol() (fn, error) {
|
|
n, err := m.dec.Decode()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m.sourceCol += n
|
|
m.value.sourceCol = m.sourceCol
|
|
return m.parseNamesInd, nil
|
|
}
|
|
|
|
func (m *mappings) parseNamesInd() (fn, error) {
|
|
n, err := m.dec.Decode()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m.namesInd += n
|
|
m.value.namesInd = m.namesInd
|
|
return m.parseGenCol, nil
|
|
}
|
|
|
|
func (m *mappings) zeroValue() {
|
|
m.value = mapping{
|
|
genLine: m.genLine,
|
|
genCol: 0,
|
|
sourcesInd: -1,
|
|
sourceLine: 0,
|
|
sourceCol: 0,
|
|
namesInd: -1,
|
|
}
|
|
}
|
|
|
|
func (m *mappings) pushValue() {
|
|
m.values = append(m.values, m.value)
|
|
m.zeroValue()
|
|
}
|