UNPKG

3.92 kBJavaScriptView Raw
1'use strict'
2
3let { SourceMapConsumer, SourceMapGenerator } = require('source-map-js')
4let { existsSync, readFileSync } = require('fs')
5let { dirname, join } = require('path')
6
7function fromBase64(str) {
8 if (Buffer) {
9 return Buffer.from(str, 'base64').toString()
10 } else {
11 /* c8 ignore next 2 */
12 return window.atob(str)
13 }
14}
15
16class PreviousMap {
17 constructor(css, opts) {
18 if (opts.map === false) return
19 this.loadAnnotation(css)
20 this.inline = this.startWith(this.annotation, 'data:')
21
22 let prev = opts.map ? opts.map.prev : undefined
23 let text = this.loadMap(opts.from, prev)
24 if (!this.mapFile && opts.from) {
25 this.mapFile = opts.from
26 }
27 if (this.mapFile) this.root = dirname(this.mapFile)
28 if (text) this.text = text
29 }
30
31 consumer() {
32 if (!this.consumerCache) {
33 this.consumerCache = new SourceMapConsumer(this.text)
34 }
35 return this.consumerCache
36 }
37
38 decodeInline(text) {
39 let baseCharsetUri = /^data:application\/json;charset=utf-?8;base64,/
40 let baseUri = /^data:application\/json;base64,/
41 let charsetUri = /^data:application\/json;charset=utf-?8,/
42 let uri = /^data:application\/json,/
43
44 if (charsetUri.test(text) || uri.test(text)) {
45 return decodeURIComponent(text.substr(RegExp.lastMatch.length))
46 }
47
48 if (baseCharsetUri.test(text) || baseUri.test(text)) {
49 return fromBase64(text.substr(RegExp.lastMatch.length))
50 }
51
52 let encoding = text.match(/data:application\/json;([^,]+),/)[1]
53 throw new Error('Unsupported source map encoding ' + encoding)
54 }
55
56 getAnnotationURL(sourceMapString) {
57 return sourceMapString.replace(/^\/\*\s*# sourceMappingURL=/, '').trim()
58 }
59
60 isMap(map) {
61 if (typeof map !== 'object') return false
62 return (
63 typeof map.mappings === 'string' ||
64 typeof map._mappings === 'string' ||
65 Array.isArray(map.sections)
66 )
67 }
68
69 loadAnnotation(css) {
70 let comments = css.match(/\/\*\s*# sourceMappingURL=/gm)
71 if (!comments) return
72
73 // sourceMappingURLs from comments, strings, etc.
74 let start = css.lastIndexOf(comments.pop())
75 let end = css.indexOf('*/', start)
76
77 if (start > -1 && end > -1) {
78 // Locate the last sourceMappingURL to avoid pickin
79 this.annotation = this.getAnnotationURL(css.substring(start, end))
80 }
81 }
82
83 loadFile(path) {
84 this.root = dirname(path)
85 if (existsSync(path)) {
86 this.mapFile = path
87 return readFileSync(path, 'utf-8').toString().trim()
88 }
89 }
90
91 loadMap(file, prev) {
92 if (prev === false) return false
93
94 if (prev) {
95 if (typeof prev === 'string') {
96 return prev
97 } else if (typeof prev === 'function') {
98 let prevPath = prev(file)
99 if (prevPath) {
100 let map = this.loadFile(prevPath)
101 if (!map) {
102 throw new Error(
103 'Unable to load previous source map: ' + prevPath.toString()
104 )
105 }
106 return map
107 }
108 } else if (prev instanceof SourceMapConsumer) {
109 return SourceMapGenerator.fromSourceMap(prev).toString()
110 } else if (prev instanceof SourceMapGenerator) {
111 return prev.toString()
112 } else if (this.isMap(prev)) {
113 return JSON.stringify(prev)
114 } else {
115 throw new Error(
116 'Unsupported previous source map format: ' + prev.toString()
117 )
118 }
119 } else if (this.inline) {
120 return this.decodeInline(this.annotation)
121 } else if (this.annotation) {
122 let map = this.annotation
123 if (file) map = join(dirname(file), map)
124 return this.loadFile(map)
125 }
126 }
127
128 startWith(string, start) {
129 if (!string) return false
130 return string.substr(0, start.length) === start
131 }
132
133 withContent() {
134 return !!(
135 this.consumer().sourcesContent &&
136 this.consumer().sourcesContent.length > 0
137 )
138 }
139}
140
141module.exports = PreviousMap
142PreviousMap.default = PreviousMap