1
|
/* -*- Mode: js; js-indent-level: 2; -*- */
|
2
|
/*
|
3
|
* Copyright 2011 Mozilla Foundation and contributors
|
4
|
* Licensed under the New BSD license. See LICENSE or:
|
5
|
* http://opensource.org/licenses/BSD-3-Clause
|
6
|
*/
|
7
|
|
8
|
/**
|
9
|
* This is a helper function for getting values from parameter/options
|
10
|
* objects.
|
11
|
*
|
12
|
* @param args The object we are extracting values from
|
13
|
* @param name The name of the property we are getting.
|
14
|
* @param defaultValue An optional value to return if the property is missing
|
15
|
* from the object. If this is not specified and the property is missing, an
|
16
|
* error will be thrown.
|
17
|
*/
|
18
|
function getArg(aArgs, aName, aDefaultValue) {
|
19
|
if (aName in aArgs) {
|
20
|
return aArgs[aName];
|
21
|
} else if (arguments.length === 3) {
|
22
|
return aDefaultValue;
|
23
|
} else {
|
24
|
throw new Error('"' + aName + '" is a required argument.');
|
25
|
}
|
26
|
}
|
27
|
exports.getArg = getArg;
|
28
|
|
29
|
var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/;
|
30
|
var dataUrlRegexp = /^data:.+\,.+$/;
|
31
|
|
32
|
function urlParse(aUrl) {
|
33
|
var match = aUrl.match(urlRegexp);
|
34
|
if (!match) {
|
35
|
return null;
|
36
|
}
|
37
|
return {
|
38
|
scheme: match[1],
|
39
|
auth: match[2],
|
40
|
host: match[3],
|
41
|
port: match[4],
|
42
|
path: match[5]
|
43
|
};
|
44
|
}
|
45
|
exports.urlParse = urlParse;
|
46
|
|
47
|
function urlGenerate(aParsedUrl) {
|
48
|
var url = '';
|
49
|
if (aParsedUrl.scheme) {
|
50
|
url += aParsedUrl.scheme + ':';
|
51
|
}
|
52
|
url += '//';
|
53
|
if (aParsedUrl.auth) {
|
54
|
url += aParsedUrl.auth + '@';
|
55
|
}
|
56
|
if (aParsedUrl.host) {
|
57
|
url += aParsedUrl.host;
|
58
|
}
|
59
|
if (aParsedUrl.port) {
|
60
|
url += ":" + aParsedUrl.port
|
61
|
}
|
62
|
if (aParsedUrl.path) {
|
63
|
url += aParsedUrl.path;
|
64
|
}
|
65
|
return url;
|
66
|
}
|
67
|
exports.urlGenerate = urlGenerate;
|
68
|
|
69
|
/**
|
70
|
* Normalizes a path, or the path portion of a URL:
|
71
|
*
|
72
|
* - Replaces consecutive slashes with one slash.
|
73
|
* - Removes unnecessary '.' parts.
|
74
|
* - Removes unnecessary '<dir>/..' parts.
|
75
|
*
|
76
|
* Based on code in the Node.js 'path' core module.
|
77
|
*
|
78
|
* @param aPath The path or url to normalize.
|
79
|
*/
|
80
|
function normalize(aPath) {
|
81
|
var path = aPath;
|
82
|
var url = urlParse(aPath);
|
83
|
if (url) {
|
84
|
if (!url.path) {
|
85
|
return aPath;
|
86
|
}
|
87
|
path = url.path;
|
88
|
}
|
89
|
var isAbsolute = exports.isAbsolute(path);
|
90
|
|
91
|
var parts = path.split(/\/+/);
|
92
|
for (var part, up = 0, i = parts.length - 1; i >= 0; i--) {
|
93
|
part = parts[i];
|
94
|
if (part === '.') {
|
95
|
parts.splice(i, 1);
|
96
|
} else if (part === '..') {
|
97
|
up++;
|
98
|
} else if (up > 0) {
|
99
|
if (part === '') {
|
100
|
// The first part is blank if the path is absolute. Trying to go
|
101
|
// above the root is a no-op. Therefore we can remove all '..' parts
|
102
|
// directly after the root.
|
103
|
parts.splice(i + 1, up);
|
104
|
up = 0;
|
105
|
} else {
|
106
|
parts.splice(i, 2);
|
107
|
up--;
|
108
|
}
|
109
|
}
|
110
|
}
|
111
|
path = parts.join('/');
|
112
|
|
113
|
if (path === '') {
|
114
|
path = isAbsolute ? '/' : '.';
|
115
|
}
|
116
|
|
117
|
if (url) {
|
118
|
url.path = path;
|
119
|
return urlGenerate(url);
|
120
|
}
|
121
|
return path;
|
122
|
}
|
123
|
exports.normalize = normalize;
|
124
|
|
125
|
/**
|
126
|
* Joins two paths/URLs.
|
127
|
*
|
128
|
* @param aRoot The root path or URL.
|
129
|
* @param aPath The path or URL to be joined with the root.
|
130
|
*
|
131
|
* - If aPath is a URL or a data URI, aPath is returned, unless aPath is a
|
132
|
* scheme-relative URL: Then the scheme of aRoot, if any, is prepended
|
133
|
* first.
|
134
|
* - Otherwise aPath is a path. If aRoot is a URL, then its path portion
|
135
|
* is updated with the result and aRoot is returned. Otherwise the result
|
136
|
* is returned.
|
137
|
* - If aPath is absolute, the result is aPath.
|
138
|
* - Otherwise the two paths are joined with a slash.
|
139
|
* - Joining for example 'http://' and 'www.example.com' is also supported.
|
140
|
*/
|
141
|
function join(aRoot, aPath) {
|
142
|
if (aRoot === "") {
|
143
|
aRoot = ".";
|
144
|
}
|
145
|
if (aPath === "") {
|
146
|
aPath = ".";
|
147
|
}
|
148
|
var aPathUrl = urlParse(aPath);
|
149
|
var aRootUrl = urlParse(aRoot);
|
150
|
if (aRootUrl) {
|
151
|
aRoot = aRootUrl.path || '/';
|
152
|
}
|
153
|
|
154
|
// `join(foo, '//www.example.org')`
|
155
|
if (aPathUrl && !aPathUrl.scheme) {
|
156
|
if (aRootUrl) {
|
157
|
aPathUrl.scheme = aRootUrl.scheme;
|
158
|
}
|
159
|
return urlGenerate(aPathUrl);
|
160
|
}
|
161
|
|
162
|
if (aPathUrl || aPath.match(dataUrlRegexp)) {
|
163
|
return aPath;
|
164
|
}
|
165
|
|
166
|
// `join('http://', 'www.example.com')`
|
167
|
if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {
|
168
|
aRootUrl.host = aPath;
|
169
|
return urlGenerate(aRootUrl);
|
170
|
}
|
171
|
|
172
|
var joined = aPath.charAt(0) === '/'
|
173
|
? aPath
|
174
|
: normalize(aRoot.replace(/\/+$/, '') + '/' + aPath);
|
175
|
|
176
|
if (aRootUrl) {
|
177
|
aRootUrl.path = joined;
|
178
|
return urlGenerate(aRootUrl);
|
179
|
}
|
180
|
return joined;
|
181
|
}
|
182
|
exports.join = join;
|
183
|
|
184
|
exports.isAbsolute = function (aPath) {
|
185
|
return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp);
|
186
|
};
|
187
|
|
188
|
/**
|
189
|
* Make a path relative to a URL or another path.
|
190
|
*
|
191
|
* @param aRoot The root path or URL.
|
192
|
* @param aPath The path or URL to be made relative to aRoot.
|
193
|
*/
|
194
|
function relative(aRoot, aPath) {
|
195
|
if (aRoot === "") {
|
196
|
aRoot = ".";
|
197
|
}
|
198
|
|
199
|
aRoot = aRoot.replace(/\/$/, '');
|
200
|
|
201
|
// It is possible for the path to be above the root. In this case, simply
|
202
|
// checking whether the root is a prefix of the path won't work. Instead, we
|
203
|
// need to remove components from the root one by one, until either we find
|
204
|
// a prefix that fits, or we run out of components to remove.
|
205
|
var level = 0;
|
206
|
while (aPath.indexOf(aRoot + '/') !== 0) {
|
207
|
var index = aRoot.lastIndexOf("/");
|
208
|
if (index < 0) {
|
209
|
return aPath;
|
210
|
}
|
211
|
|
212
|
// If the only part of the root that is left is the scheme (i.e. http://,
|
213
|
// file:///, etc.), one or more slashes (/), or simply nothing at all, we
|
214
|
// have exhausted all components, so the path is not relative to the root.
|
215
|
aRoot = aRoot.slice(0, index);
|
216
|
if (aRoot.match(/^([^\/]+:\/)?\/*$/)) {
|
217
|
return aPath;
|
218
|
}
|
219
|
|
220
|
++level;
|
221
|
}
|
222
|
|
223
|
// Make sure we add a "../" for each component we removed from the root.
|
224
|
return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
|
225
|
}
|
226
|
exports.relative = relative;
|
227
|
|
228
|
var supportsNullProto = (function () {
|
229
|
var obj = Object.create(null);
|
230
|
return !('__proto__' in obj);
|
231
|
}());
|
232
|
|
233
|
function identity (s) {
|
234
|
return s;
|
235
|
}
|
236
|
|
237
|
/**
|
238
|
* Because behavior goes wacky when you set `__proto__` on objects, we
|
239
|
* have to prefix all the strings in our set with an arbitrary character.
|
240
|
*
|
241
|
* See https://github.com/mozilla/source-map/pull/31 and
|
242
|
* https://github.com/mozilla/source-map/issues/30
|
243
|
*
|
244
|
* @param String aStr
|
245
|
*/
|
246
|
function toSetString(aStr) {
|
247
|
if (isProtoString(aStr)) {
|
248
|
return '$' + aStr;
|
249
|
}
|
250
|
|
251
|
return aStr;
|
252
|
}
|
253
|
exports.toSetString = supportsNullProto ? identity : toSetString;
|
254
|
|
255
|
function fromSetString(aStr) {
|
256
|
if (isProtoString(aStr)) {
|
257
|
return aStr.slice(1);
|
258
|
}
|
259
|
|
260
|
return aStr;
|
261
|
}
|
262
|
exports.fromSetString = supportsNullProto ? identity : fromSetString;
|
263
|
|
264
|
function isProtoString(s) {
|
265
|
if (!s) {
|
266
|
return false;
|
267
|
}
|
268
|
|
269
|
var length = s.length;
|
270
|
|
271
|
if (length < 9 /* "__proto__".length */) {
|
272
|
return false;
|
273
|
}
|
274
|
|
275
|
if (s.charCodeAt(length - 1) !== 95 /* '_' */ ||
|
276
|
s.charCodeAt(length - 2) !== 95 /* '_' */ ||
|
277
|
s.charCodeAt(length - 3) !== 111 /* 'o' */ ||
|
278
|
s.charCodeAt(length - 4) !== 116 /* 't' */ ||
|
279
|
s.charCodeAt(length - 5) !== 111 /* 'o' */ ||
|
280
|
s.charCodeAt(length - 6) !== 114 /* 'r' */ ||
|
281
|
s.charCodeAt(length - 7) !== 112 /* 'p' */ ||
|
282
|
s.charCodeAt(length - 8) !== 95 /* '_' */ ||
|
283
|
s.charCodeAt(length - 9) !== 95 /* '_' */) {
|
284
|
return false;
|
285
|
}
|
286
|
|
287
|
for (var i = length - 10; i >= 0; i--) {
|
288
|
if (s.charCodeAt(i) !== 36 /* '$' */) {
|
289
|
return false;
|
290
|
}
|
291
|
}
|
292
|
|
293
|
return true;
|
294
|
}
|
295
|
|
296
|
/**
|
297
|
* Comparator between two mappings where the original positions are compared.
|
298
|
*
|
299
|
* Optionally pass in `true` as `onlyCompareGenerated` to consider two
|
300
|
* mappings with the same original source/line/column, but different generated
|
301
|
* line and column the same. Useful when searching for a mapping with a
|
302
|
* stubbed out mapping.
|
303
|
*/
|
304
|
function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
|
305
|
var cmp = mappingA.source - mappingB.source;
|
306
|
if (cmp !== 0) {
|
307
|
return cmp;
|
308
|
}
|
309
|
|
310
|
cmp = mappingA.originalLine - mappingB.originalLine;
|
311
|
if (cmp !== 0) {
|
312
|
return cmp;
|
313
|
}
|
314
|
|
315
|
cmp = mappingA.originalColumn - mappingB.originalColumn;
|
316
|
if (cmp !== 0 || onlyCompareOriginal) {
|
317
|
return cmp;
|
318
|
}
|
319
|
|
320
|
cmp = mappingA.generatedColumn - mappingB.generatedColumn;
|
321
|
if (cmp !== 0) {
|
322
|
return cmp;
|
323
|
}
|
324
|
|
325
|
cmp = mappingA.generatedLine - mappingB.generatedLine;
|
326
|
if (cmp !== 0) {
|
327
|
return cmp;
|
328
|
}
|
329
|
|
330
|
return mappingA.name - mappingB.name;
|
331
|
}
|
332
|
exports.compareByOriginalPositions = compareByOriginalPositions;
|
333
|
|
334
|
/**
|
335
|
* Comparator between two mappings with deflated source and name indices where
|
336
|
* the generated positions are compared.
|
337
|
*
|
338
|
* Optionally pass in `true` as `onlyCompareGenerated` to consider two
|
339
|
* mappings with the same generated line and column, but different
|
340
|
* source/name/original line and column the same. Useful when searching for a
|
341
|
* mapping with a stubbed out mapping.
|
342
|
*/
|
343
|
function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) {
|
344
|
var cmp = mappingA.generatedLine - mappingB.generatedLine;
|
345
|
if (cmp !== 0) {
|
346
|
return cmp;
|
347
|
}
|
348
|
|
349
|
cmp = mappingA.generatedColumn - mappingB.generatedColumn;
|
350
|
if (cmp !== 0 || onlyCompareGenerated) {
|
351
|
return cmp;
|
352
|
}
|
353
|
|
354
|
cmp = mappingA.source - mappingB.source;
|
355
|
if (cmp !== 0) {
|
356
|
return cmp;
|
357
|
}
|
358
|
|
359
|
cmp = mappingA.originalLine - mappingB.originalLine;
|
360
|
if (cmp !== 0) {
|
361
|
return cmp;
|
362
|
}
|
363
|
|
364
|
cmp = mappingA.originalColumn - mappingB.originalColumn;
|
365
|
if (cmp !== 0) {
|
366
|
return cmp;
|
367
|
}
|
368
|
|
369
|
return mappingA.name - mappingB.name;
|
370
|
}
|
371
|
exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated;
|
372
|
|
373
|
function strcmp(aStr1, aStr2) {
|
374
|
if (aStr1 === aStr2) {
|
375
|
return 0;
|
376
|
}
|
377
|
|
378
|
if (aStr1 > aStr2) {
|
379
|
return 1;
|
380
|
}
|
381
|
|
382
|
return -1;
|
383
|
}
|
384
|
|
385
|
/**
|
386
|
* Comparator between two mappings with inflated source and name strings where
|
387
|
* the generated positions are compared.
|
388
|
*/
|
389
|
function compareByGeneratedPositionsInflated(mappingA, mappingB) {
|
390
|
var cmp = mappingA.generatedLine - mappingB.generatedLine;
|
391
|
if (cmp !== 0) {
|
392
|
return cmp;
|
393
|
}
|
394
|
|
395
|
cmp = mappingA.generatedColumn - mappingB.generatedColumn;
|
396
|
if (cmp !== 0) {
|
397
|
return cmp;
|
398
|
}
|
399
|
|
400
|
cmp = strcmp(mappingA.source, mappingB.source);
|
401
|
if (cmp !== 0) {
|
402
|
return cmp;
|
403
|
}
|
404
|
|
405
|
cmp = mappingA.originalLine - mappingB.originalLine;
|
406
|
if (cmp !== 0) {
|
407
|
return cmp;
|
408
|
}
|
409
|
|
410
|
cmp = mappingA.originalColumn - mappingB.originalColumn;
|
411
|
if (cmp !== 0) {
|
412
|
return cmp;
|
413
|
}
|
414
|
|
415
|
return strcmp(mappingA.name, mappingB.name);
|
416
|
}
|
417
|
exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated;
|