consumer.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package sourcemap
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/url"
  6. "path"
  7. "sort"
  8. "strconv"
  9. )
  10. type Consumer struct {
  11. sourceRootURL *url.URL
  12. smap *sourceMap
  13. mappings []mapping
  14. }
  15. func Parse(mapURL string, b []byte) (*Consumer, error) {
  16. smap := new(sourceMap)
  17. err := json.Unmarshal(b, smap)
  18. if err != nil {
  19. return nil, err
  20. }
  21. if smap.Version != 3 {
  22. return nil, fmt.Errorf(
  23. "sourcemap: got version=%d, but only 3rd version is supported",
  24. smap.Version,
  25. )
  26. }
  27. var sourceRootURL *url.URL
  28. if smap.SourceRoot != "" {
  29. u, err := url.Parse(smap.SourceRoot)
  30. if err != nil {
  31. return nil, err
  32. }
  33. if u.IsAbs() {
  34. sourceRootURL = u
  35. }
  36. } else if mapURL != "" {
  37. u, err := url.Parse(mapURL)
  38. if err != nil {
  39. return nil, err
  40. }
  41. if u.IsAbs() {
  42. u.Path = path.Dir(u.Path)
  43. sourceRootURL = u
  44. }
  45. }
  46. mappings, err := parseMappings(smap.Mappings)
  47. if err != nil {
  48. return nil, err
  49. }
  50. // Free memory.
  51. smap.Mappings = ""
  52. return &Consumer{
  53. sourceRootURL: sourceRootURL,
  54. smap: smap,
  55. mappings: mappings,
  56. }, nil
  57. }
  58. func (c *Consumer) File() string {
  59. return c.smap.File
  60. }
  61. func (c *Consumer) Source(genLine, genCol int) (source, name string, line, col int, ok bool) {
  62. i := sort.Search(len(c.mappings), func(i int) bool {
  63. m := &c.mappings[i]
  64. if m.genLine == genLine {
  65. return m.genCol >= genCol
  66. }
  67. return m.genLine >= genLine
  68. })
  69. // Mapping not found.
  70. if i == len(c.mappings) {
  71. return
  72. }
  73. match := &c.mappings[i]
  74. // Fuzzy match.
  75. if match.genLine > genLine || match.genCol > genCol {
  76. if i == 0 {
  77. return
  78. }
  79. match = &c.mappings[i-1]
  80. }
  81. if match.sourcesInd >= 0 {
  82. source = c.absSource(c.smap.Sources[match.sourcesInd])
  83. }
  84. if match.namesInd >= 0 {
  85. v := c.smap.Names[match.namesInd]
  86. switch v := v.(type) {
  87. case string:
  88. name = v
  89. case float64:
  90. name = strconv.FormatFloat(v, 'f', -1, 64)
  91. default:
  92. name = fmt.Sprint(v)
  93. }
  94. }
  95. line = match.sourceLine
  96. col = match.sourceCol
  97. ok = true
  98. return
  99. }
  100. func (c *Consumer) absSource(source string) string {
  101. if path.IsAbs(source) {
  102. return source
  103. }
  104. if u, err := url.Parse(source); err == nil && u.IsAbs() {
  105. return source
  106. }
  107. if c.sourceRootURL != nil {
  108. u := *c.sourceRootURL
  109. u.Path = path.Join(c.sourceRootURL.Path, source)
  110. return u.String()
  111. }
  112. if c.smap.SourceRoot != "" {
  113. return path.Join(c.smap.SourceRoot, source)
  114. }
  115. return source
  116. }