UNPKG

5.94 kBJavaScriptView Raw
1// Copyright 2014 Simon Lydell
2// X11 (“MIT”) Licensed. (See LICENSE.)
3
4var sourceMappingURL = require("source-map-url")
5var resolveUrl = require("./resolve-url")
6var urix = require("urix")
7var atob = require("atob")
8
9
10
11function callbackAsync(callback, error, result) {
12 setImmediate(function() { callback(error, result) })
13}
14
15function 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
44function parseMapToJSON(string) {
45 return JSON.parse(string.replace(/^\)\]\}'/, ""))
46}
47
48
49
50function 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
74function 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
84var dataUriRegex = /^data:([^,;]*)(;[^,;]*)*(?:,(.*))?$/
85var jsonMimeTypeRegex = /^(?:application|text)\/json$/
86
87function 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
122function 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
155function 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
168var endingSlash = /\/?$/
169
170function 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 // Make sure that the sourceRoot ends with a slash, so that `/scripts/subdir` becomes
177 // `/scripts/subdir/<source>`, not `/scripts/<source>`. Pointing to a file as source root
178 // does not make sense.
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
190function 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
209function 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
221module.exports = {
222 resolveSourceMap: resolveSourceMap,
223 resolveSourceMapSync: resolveSourceMapSync,
224 resolveSources: resolveSources,
225 resolveSourcesSync: resolveSourcesSync,
226 resolve: resolve,
227 resolveSync: resolveSync
228}