1 |
|
2 |
|
3 |
|
4 | var sourceMappingURL = require("source-map-url")
|
5 | var resolveUrl = require("./resolve-url")
|
6 | var urix = require("urix")
|
7 | var atob = require("atob")
|
8 |
|
9 |
|
10 |
|
11 | function callbackAsync(callback, error, result) {
|
12 | setImmediate(function() { callback(error, result) })
|
13 | }
|
14 |
|
15 | function parseMapToJSON(string) {
|
16 | return JSON.parse(string.replace(/^\)\]\}'/, ""))
|
17 | }
|
18 |
|
19 |
|
20 |
|
21 | function resolveSourceMap(code, codeUrl, read, callback) {
|
22 | var mapData
|
23 | try {
|
24 | mapData = resolveSourceMapHelper(code, codeUrl)
|
25 | } catch (error) {
|
26 | return callbackAsync(callback, error)
|
27 | }
|
28 | if (!mapData || mapData.map) {
|
29 | return callbackAsync(callback, null, mapData)
|
30 | }
|
31 | read(mapData.url, function(error, result) {
|
32 | if (error) {
|
33 | return callback(error)
|
34 | }
|
35 | try {
|
36 | mapData.map = parseMapToJSON(String(result))
|
37 | } catch (error) {
|
38 | return callback(error)
|
39 | }
|
40 | callback(null, mapData)
|
41 | })
|
42 | }
|
43 |
|
44 | function resolveSourceMapSync(code, codeUrl, read) {
|
45 | var mapData = resolveSourceMapHelper(code, codeUrl)
|
46 | if (!mapData || mapData.map) {
|
47 | return mapData
|
48 | }
|
49 | mapData.map = parseMapToJSON(String(read(mapData.url)))
|
50 | return mapData
|
51 | }
|
52 |
|
53 | var dataUriRegex = /^data:([^,;]*)(;[^,;]*)*(?:,(.*))?$/
|
54 | var jsonMimeTypeRegex = /^(?:application|text)\/json$/
|
55 |
|
56 | function resolveSourceMapHelper(code, codeUrl) {
|
57 | codeUrl = urix(codeUrl)
|
58 |
|
59 | var url = sourceMappingURL.getFrom(code)
|
60 | if (!url) {
|
61 | return null
|
62 | }
|
63 |
|
64 | var dataUri = url.match(dataUriRegex)
|
65 | if (dataUri) {
|
66 | var mimeType = dataUri[1]
|
67 | var lastParameter = dataUri[2]
|
68 | var encoded = dataUri[3]
|
69 | if (!jsonMimeTypeRegex.test(mimeType)) {
|
70 | throw new Error("Unuseful data uri mime type: " + (mimeType || "text/plain"))
|
71 | }
|
72 | return {
|
73 | sourceMappingURL: url,
|
74 | url: null,
|
75 | sourcesRelativeTo: codeUrl,
|
76 | map: parseMapToJSON(lastParameter === ";base64" ? atob(encoded) : decodeURIComponent(encoded))
|
77 | }
|
78 | }
|
79 |
|
80 | var mapUrl = resolveUrl(codeUrl, url)
|
81 | return {
|
82 | sourceMappingURL: url,
|
83 | url: mapUrl,
|
84 | sourcesRelativeTo: mapUrl,
|
85 | map: null
|
86 | }
|
87 | }
|
88 |
|
89 |
|
90 |
|
91 | function resolveSources(map, mapUrl, read, options, callback) {
|
92 | if (typeof options === "function") {
|
93 | callback = options
|
94 | options = {}
|
95 | }
|
96 | var pending = map.sources.length
|
97 | var errored = false
|
98 | var result = {
|
99 | sourcesResolved: [],
|
100 | sourcesContent: []
|
101 | }
|
102 |
|
103 | var done = function(error) {
|
104 | if (errored) {
|
105 | return
|
106 | }
|
107 | if (error) {
|
108 | errored = true
|
109 | return callback(error)
|
110 | }
|
111 | pending--
|
112 | if (pending === 0) {
|
113 | callback(null, result)
|
114 | }
|
115 | }
|
116 |
|
117 | resolveSourcesHelper(map, mapUrl, options, function(fullUrl, sourceContent, index) {
|
118 | result.sourcesResolved[index] = fullUrl
|
119 | if (typeof sourceContent === "string") {
|
120 | result.sourcesContent[index] = sourceContent
|
121 | callbackAsync(done, null)
|
122 | } else {
|
123 | read(fullUrl, function(error, source) {
|
124 | result.sourcesContent[index] = String(source)
|
125 | done(error)
|
126 | })
|
127 | }
|
128 | })
|
129 | }
|
130 |
|
131 | function resolveSourcesSync(map, mapUrl, read, options) {
|
132 | var result = {
|
133 | sourcesResolved: [],
|
134 | sourcesContent: []
|
135 | }
|
136 | resolveSourcesHelper(map, mapUrl, options, function(fullUrl, sourceContent, index) {
|
137 | result.sourcesResolved[index] = fullUrl
|
138 | if (read !== null) {
|
139 | if (typeof sourceContent === "string") {
|
140 | result.sourcesContent[index] = sourceContent
|
141 | } else {
|
142 | result.sourcesContent[index] = String(read(fullUrl))
|
143 | }
|
144 | }
|
145 | })
|
146 | return result
|
147 | }
|
148 |
|
149 | var endingSlash = /\/?$/
|
150 |
|
151 | function resolveSourcesHelper(map, mapUrl, options, fn) {
|
152 | options = options || {}
|
153 | mapUrl = urix(mapUrl)
|
154 | var fullUrl
|
155 | var sourceContent
|
156 | var sourceRoot
|
157 | for (var index = 0, len = map.sources.length; index < len; index++) {
|
158 | sourceRoot = null
|
159 | if (typeof options.sourceRoot === "string") {
|
160 | sourceRoot = options.sourceRoot
|
161 | } else if (typeof map.sourceRoot === "string" && options.sourceRoot !== false) {
|
162 | sourceRoot = map.sourceRoot
|
163 | }
|
164 |
|
165 |
|
166 | if (sourceRoot === null || sourceRoot === '') {
|
167 | fullUrl = resolveUrl(mapUrl, map.sources[index])
|
168 | } else {
|
169 |
|
170 |
|
171 |
|
172 | fullUrl = resolveUrl(mapUrl, sourceRoot.replace(endingSlash, "/"), map.sources[index])
|
173 | }
|
174 | sourceContent = (map.sourcesContent || [])[index]
|
175 | fn(fullUrl, sourceContent, index)
|
176 | }
|
177 | }
|
178 |
|
179 |
|
180 |
|
181 | function resolve(code, codeUrl, read, options, callback) {
|
182 | if (typeof options === "function") {
|
183 | callback = options
|
184 | options = {}
|
185 | }
|
186 | if (code === null) {
|
187 | var mapUrl = codeUrl
|
188 | read(mapUrl, function(error, result) {
|
189 | if (error) {
|
190 | return callback(error)
|
191 | }
|
192 | var map
|
193 | try {
|
194 | map = parseMapToJSON(String(result))
|
195 | } catch (error) {
|
196 | return callback(error)
|
197 | }
|
198 | _resolveSources({
|
199 | sourceMappingURL: null,
|
200 | url: mapUrl,
|
201 | sourcesRelativeTo: mapUrl,
|
202 | map: map
|
203 | })
|
204 | })
|
205 | } else {
|
206 | resolveSourceMap(code, codeUrl, read, function(error, mapData) {
|
207 | if (error) {
|
208 | return callback(error)
|
209 | }
|
210 | if (!mapData) {
|
211 | return callback(null, null)
|
212 | }
|
213 | _resolveSources(mapData)
|
214 | })
|
215 | }
|
216 |
|
217 | function _resolveSources(mapData) {
|
218 | resolveSources(mapData.map, mapData.sourcesRelativeTo, read, options, function(error, result) {
|
219 | if (error) {
|
220 | return callback(error)
|
221 | }
|
222 | mapData.sourcesResolved = result.sourcesResolved
|
223 | mapData.sourcesContent = result.sourcesContent
|
224 | callback(null, mapData)
|
225 | })
|
226 | }
|
227 | }
|
228 |
|
229 | function resolveSync(code, codeUrl, read, options) {
|
230 | var mapData
|
231 | if (code === null) {
|
232 | var mapUrl = codeUrl
|
233 | mapData = {
|
234 | sourceMappingURL: null,
|
235 | url: mapUrl,
|
236 | sourcesRelativeTo: mapUrl,
|
237 | map: parseMapToJSON(String(read(mapUrl)))
|
238 | }
|
239 | } else {
|
240 | mapData = resolveSourceMapSync(code, codeUrl, read)
|
241 | if (!mapData) {
|
242 | return null
|
243 | }
|
244 | }
|
245 | var result = resolveSourcesSync(mapData.map, mapData.sourcesRelativeTo, read, options)
|
246 | mapData.sourcesResolved = result.sourcesResolved
|
247 | mapData.sourcesContent = result.sourcesContent
|
248 | return mapData
|
249 | }
|
250 |
|
251 |
|
252 |
|
253 | module.exports = {
|
254 | resolveSourceMap: resolveSourceMap,
|
255 | resolveSourceMapSync: resolveSourceMapSync,
|
256 | resolveSources: resolveSources,
|
257 | resolveSourcesSync: resolveSourcesSync,
|
258 | resolve: resolve,
|
259 | resolveSync: resolveSync,
|
260 | parseMapToJSON: parseMapToJSON
|
261 | }
|