/
previous-map.js
142 lines (126 loc) · 4.12 KB
/
previous-map.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
let { dirname, join } = require('path')
let mozilla = require('source-map')
let fs = require('fs')
function fromBase64 (str) {
if (Buffer) {
return Buffer.from(str, 'base64').toString()
} else {
return window.atob(str)
}
}
/**
* Source map information from input CSS.
* For example, source map after Sass compiler.
*
* This class will automatically find source map in input CSS or in file system
* near input file (according `from` option).
*
* @example
* const root = postcss.parse(css, { from: 'a.sass.css' })
* root.input.map //=> PreviousMap
*/
class PreviousMap {
/**
* @param {string} css Input CSS source.
* @param {processOptions} [opts] {@link Processor#process} options.
*/
constructor (css, opts) {
this.loadAnnotation(css)
/**
* Was source map inlined by data-uri to input CSS.
*
* @type {boolean}
*/
this.inline = this.startWith(this.annotation, 'data:')
let prev = opts.map ? opts.map.prev : undefined
let text = this.loadMap(opts.from, prev)
if (text) this.text = text
}
/**
* Create a instance of `SourceMapGenerator` class
* from the `source-map` library to work with source map information.
*
* It is lazy method, so it will create object only on first call
* and then it will use cache.
*
* @return {SourceMapGenerator} Object with source map information.
*/
consumer () {
if (!this.consumerCache) {
this.consumerCache = new mozilla.SourceMapConsumer(this.text)
}
return this.consumerCache
}
/**
* Does source map contains `sourcesContent` with input source text.
*
* @return {boolean} Is `sourcesContent` present.
*/
withContent () {
return !!(this.consumer().sourcesContent &&
this.consumer().sourcesContent.length > 0)
}
startWith (string, start) {
if (!string) return false
return string.substr(0, start.length) === start
}
loadAnnotation (css) {
let match = css.match(/\/\*\s*# sourceMappingURL=(.*)\s*\*\//)
if (match) this.annotation = match[1].trim()
}
decodeInline (text) {
let baseCharsetUri = /^data:application\/json;charset=utf-?8;base64,/
let baseUri = /^data:application\/json;base64,/
let charsetUri = /^data:application\/json;charset=utf-?8,/
let uri = /^data:application\/json,/
if (charsetUri.test(text) || uri.test(text)) {
return decodeURIComponent(text.substr(RegExp.lastMatch.length))
}
if (baseCharsetUri.test(text) || baseUri.test(text)) {
return fromBase64(text.substr(RegExp.lastMatch.length))
}
let encoding = text.match(/data:application\/json;([^,]+),/)[1]
throw new Error('Unsupported source map encoding ' + encoding)
}
loadMap (file, prev) {
if (prev === false) return false
if (prev) {
if (typeof prev === 'string') {
return prev
} else if (typeof prev === 'function') {
let prevPath = prev(file)
if (prevPath && fs.existsSync && fs.existsSync(prevPath)) {
return fs.readFileSync(prevPath, 'utf-8').toString().trim()
} else {
throw new Error(
'Unable to load previous source map: ' + prevPath.toString())
}
} else if (prev instanceof mozilla.SourceMapConsumer) {
return mozilla.SourceMapGenerator.fromSourceMap(prev).toString()
} else if (prev instanceof mozilla.SourceMapGenerator) {
return prev.toString()
} else if (this.isMap(prev)) {
return JSON.stringify(prev)
} else {
throw new Error(
'Unsupported previous source map format: ' + prev.toString())
}
} else if (this.inline) {
return this.decodeInline(this.annotation)
} else if (this.annotation) {
let map = this.annotation
if (file) map = join(dirname(file), map)
this.root = dirname(map)
if (fs.existsSync && fs.existsSync(map)) {
return fs.readFileSync(map, 'utf-8').toString().trim()
} else {
return false
}
}
}
isMap (map) {
if (typeof map !== 'object') return false
return typeof map.mappings === 'string' || typeof map._mappings === 'string'
}
}
module.exports = PreviousMap