...

Source file src/github.com/go-sourcemap/sourcemap/consumer.go

Documentation: github.com/go-sourcemap/sourcemap

     1  package sourcemap
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/url"
     7  	"path"
     8  	"sort"
     9  )
    10  
    11  type sourceMap struct {
    12  	Version        int               `json:"version"`
    13  	File           string            `json:"file"`
    14  	SourceRoot     string            `json:"sourceRoot"`
    15  	Sources        []string          `json:"sources"`
    16  	SourcesContent []string          `json:"sourcesContent"`
    17  	Names          []json.RawMessage `json:"names,string"`
    18  	Mappings       string            `json:"mappings"`
    19  
    20  	mappings []mapping
    21  }
    22  
    23  type v3 struct {
    24  	sourceMap
    25  	Sections []section `json:"sections"`
    26  }
    27  
    28  func (m *sourceMap) parse(sourcemapURL string) error {
    29  	if err := checkVersion(m.Version); err != nil {
    30  		return err
    31  	}
    32  
    33  	var sourceRootURL *url.URL
    34  	if m.SourceRoot != "" {
    35  		u, err := url.Parse(m.SourceRoot)
    36  		if err != nil {
    37  			return err
    38  		}
    39  		if u.IsAbs() {
    40  			sourceRootURL = u
    41  		}
    42  	} else if sourcemapURL != "" {
    43  		u, err := url.Parse(sourcemapURL)
    44  		if err != nil {
    45  			return err
    46  		}
    47  		if u.IsAbs() {
    48  			u.Path = path.Dir(u.Path)
    49  			sourceRootURL = u
    50  		}
    51  	}
    52  
    53  	for i, src := range m.Sources {
    54  		m.Sources[i] = m.absSource(sourceRootURL, src)
    55  	}
    56  
    57  	mappings, err := parseMappings(m.Mappings)
    58  	if err != nil {
    59  		return err
    60  	}
    61  
    62  	m.mappings = mappings
    63  	// Free memory.
    64  	m.Mappings = ""
    65  
    66  	return nil
    67  }
    68  
    69  func (m *sourceMap) absSource(root *url.URL, source string) string {
    70  	if path.IsAbs(source) {
    71  		return source
    72  	}
    73  
    74  	if u, err := url.Parse(source); err == nil && u.IsAbs() {
    75  		return source
    76  	}
    77  
    78  	if root != nil {
    79  		u := *root
    80  		u.Path = path.Join(u.Path, source)
    81  		return u.String()
    82  	}
    83  
    84  	if m.SourceRoot != "" {
    85  		return path.Join(m.SourceRoot, source)
    86  	}
    87  
    88  	return source
    89  }
    90  
    91  func (m *sourceMap) name(idx int) string {
    92  	if idx >= len(m.Names) {
    93  		return ""
    94  	}
    95  
    96  	raw := m.Names[idx]
    97  	if len(raw) == 0 {
    98  		return ""
    99  	}
   100  
   101  	if raw[0] == '"' && raw[len(raw)-1] == '"' {
   102  		var str string
   103  		if err := json.Unmarshal(raw, &str); err == nil {
   104  			return str
   105  		}
   106  	}
   107  
   108  	return string(raw)
   109  }
   110  
   111  type section struct {
   112  	Offset struct {
   113  		Line   int `json:"line"`
   114  		Column int `json:"column"`
   115  	} `json:"offset"`
   116  	Map *sourceMap `json:"map"`
   117  }
   118  
   119  type Consumer struct {
   120  	sourcemapURL string
   121  	file         string
   122  	sections     []section
   123  }
   124  
   125  func Parse(sourcemapURL string, b []byte) (*Consumer, error) {
   126  	v3 := new(v3)
   127  	err := json.Unmarshal(b, v3)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	if err := checkVersion(v3.Version); err != nil {
   133  		return nil, err
   134  	}
   135  
   136  	if len(v3.Sections) == 0 {
   137  		v3.Sections = append(v3.Sections, section{
   138  			Map: &v3.sourceMap,
   139  		})
   140  	}
   141  
   142  	for _, s := range v3.Sections {
   143  		err := s.Map.parse(sourcemapURL)
   144  		if err != nil {
   145  			return nil, err
   146  		}
   147  	}
   148  
   149  	reverse(v3.Sections)
   150  	return &Consumer{
   151  		sourcemapURL: sourcemapURL,
   152  		file:         v3.File,
   153  		sections:     v3.Sections,
   154  	}, nil
   155  }
   156  
   157  func (c *Consumer) SourcemapURL() string {
   158  	return c.sourcemapURL
   159  }
   160  
   161  // File returns an optional name of the generated code
   162  // that this source map is associated with.
   163  func (c *Consumer) File() string {
   164  	return c.file
   165  }
   166  
   167  // Source returns the original source, name, line, and column information
   168  // for the generated source's line and column positions.
   169  func (c *Consumer) Source(
   170  	genLine, genColumn int,
   171  ) (source, name string, line, column int, ok bool) {
   172  	for i := range c.sections {
   173  		s := &c.sections[i]
   174  		if s.Offset.Line < genLine ||
   175  			(s.Offset.Line+1 == genLine && s.Offset.Column <= genColumn) {
   176  			genLine -= s.Offset.Line
   177  			genColumn -= s.Offset.Column
   178  			return c.source(s.Map, genLine, genColumn)
   179  		}
   180  	}
   181  	return
   182  }
   183  
   184  func (c *Consumer) source(
   185  	m *sourceMap, genLine, genColumn int,
   186  ) (source, name string, line, column int, ok bool) {
   187  	i := sort.Search(len(m.mappings), func(i int) bool {
   188  		m := &m.mappings[i]
   189  		if int(m.genLine) == genLine {
   190  			return int(m.genColumn) >= genColumn
   191  		}
   192  		return int(m.genLine) >= genLine
   193  	})
   194  
   195  	// Mapping not found.
   196  	if i == len(m.mappings) {
   197  		return
   198  	}
   199  
   200  	match := &m.mappings[i]
   201  
   202  	// Fuzzy match.
   203  	if int(match.genLine) > genLine || int(match.genColumn) > genColumn {
   204  		if i == 0 {
   205  			return
   206  		}
   207  		match = &m.mappings[i-1]
   208  	}
   209  
   210  	if match.sourcesInd >= 0 {
   211  		source = m.Sources[match.sourcesInd]
   212  	}
   213  	if match.namesInd >= 0 {
   214  		name = m.name(int(match.namesInd))
   215  	}
   216  	line = int(match.sourceLine)
   217  	column = int(match.sourceColumn)
   218  	ok = true
   219  	return
   220  }
   221  
   222  // SourceContent returns the original source content for the source.
   223  func (c *Consumer) SourceContent(source string) string {
   224  	for i := range c.sections {
   225  		s := &c.sections[i]
   226  		for i, src := range s.Map.Sources {
   227  			if src == source {
   228  				if i < len(s.Map.SourcesContent) {
   229  					return s.Map.SourcesContent[i]
   230  				}
   231  				break
   232  			}
   233  		}
   234  	}
   235  	return ""
   236  }
   237  
   238  func checkVersion(version int) error {
   239  	if version == 3 || version == 0 {
   240  		return nil
   241  	}
   242  	return fmt.Errorf(
   243  		"sourcemap: got version=%d, but only 3rd version is supported",
   244  		version,
   245  	)
   246  }
   247  
   248  func reverse(ss []section) {
   249  	last := len(ss) - 1
   250  	for i := 0; i < len(ss)/2; i++ {
   251  		ss[i], ss[last-i] = ss[last-i], ss[i]
   252  	}
   253  }
   254  

View as plain text