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