Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80766 views
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
if (typeof define !== 'function') {
8
var define = require('amdefine')(module, require);
9
}
10
define(function (require, exports, module) {
11
12
var base64VLQ = require('./base64-vlq');
13
var util = require('./util');
14
var ArraySet = require('./array-set').ArraySet;
15
16
/**
17
* An instance of the SourceMapGenerator represents a source map which is
18
* being built incrementally. You may pass an object with the following
19
* properties:
20
*
21
* - file: The filename of the generated source.
22
* - sourceRoot: A root for all relative URLs in this source map.
23
*/
24
function SourceMapGenerator(aArgs) {
25
if (!aArgs) {
26
aArgs = {};
27
}
28
this._file = util.getArg(aArgs, 'file', null);
29
this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null);
30
this._sources = new ArraySet();
31
this._names = new ArraySet();
32
this._mappings = [];
33
this._sourcesContents = null;
34
}
35
36
SourceMapGenerator.prototype._version = 3;
37
38
/**
39
* Creates a new SourceMapGenerator based on a SourceMapConsumer
40
*
41
* @param aSourceMapConsumer The SourceMap.
42
*/
43
SourceMapGenerator.fromSourceMap =
44
function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) {
45
var sourceRoot = aSourceMapConsumer.sourceRoot;
46
var generator = new SourceMapGenerator({
47
file: aSourceMapConsumer.file,
48
sourceRoot: sourceRoot
49
});
50
aSourceMapConsumer.eachMapping(function (mapping) {
51
var newMapping = {
52
generated: {
53
line: mapping.generatedLine,
54
column: mapping.generatedColumn
55
}
56
};
57
58
if (mapping.source) {
59
newMapping.source = mapping.source;
60
if (sourceRoot) {
61
newMapping.source = util.relative(sourceRoot, newMapping.source);
62
}
63
64
newMapping.original = {
65
line: mapping.originalLine,
66
column: mapping.originalColumn
67
};
68
69
if (mapping.name) {
70
newMapping.name = mapping.name;
71
}
72
}
73
74
generator.addMapping(newMapping);
75
});
76
aSourceMapConsumer.sources.forEach(function (sourceFile) {
77
var content = aSourceMapConsumer.sourceContentFor(sourceFile);
78
if (content) {
79
generator.setSourceContent(sourceFile, content);
80
}
81
});
82
return generator;
83
};
84
85
/**
86
* Add a single mapping from original source line and column to the generated
87
* source's line and column for this source map being created. The mapping
88
* object should have the following properties:
89
*
90
* - generated: An object with the generated line and column positions.
91
* - original: An object with the original line and column positions.
92
* - source: The original source file (relative to the sourceRoot).
93
* - name: An optional original token name for this mapping.
94
*/
95
SourceMapGenerator.prototype.addMapping =
96
function SourceMapGenerator_addMapping(aArgs) {
97
var generated = util.getArg(aArgs, 'generated');
98
var original = util.getArg(aArgs, 'original', null);
99
var source = util.getArg(aArgs, 'source', null);
100
var name = util.getArg(aArgs, 'name', null);
101
102
this._validateMapping(generated, original, source, name);
103
104
if (source && !this._sources.has(source)) {
105
this._sources.add(source);
106
}
107
108
if (name && !this._names.has(name)) {
109
this._names.add(name);
110
}
111
112
this._mappings.push({
113
generatedLine: generated.line,
114
generatedColumn: generated.column,
115
originalLine: original != null && original.line,
116
originalColumn: original != null && original.column,
117
source: source,
118
name: name
119
});
120
};
121
122
/**
123
* Set the source content for a source file.
124
*/
125
SourceMapGenerator.prototype.setSourceContent =
126
function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) {
127
var source = aSourceFile;
128
if (this._sourceRoot) {
129
source = util.relative(this._sourceRoot, source);
130
}
131
132
if (aSourceContent !== null) {
133
// Add the source content to the _sourcesContents map.
134
// Create a new _sourcesContents map if the property is null.
135
if (!this._sourcesContents) {
136
this._sourcesContents = {};
137
}
138
this._sourcesContents[util.toSetString(source)] = aSourceContent;
139
} else {
140
// Remove the source file from the _sourcesContents map.
141
// If the _sourcesContents map is empty, set the property to null.
142
delete this._sourcesContents[util.toSetString(source)];
143
if (Object.keys(this._sourcesContents).length === 0) {
144
this._sourcesContents = null;
145
}
146
}
147
};
148
149
/**
150
* Applies the mappings of a sub-source-map for a specific source file to the
151
* source map being generated. Each mapping to the supplied source file is
152
* rewritten using the supplied source map. Note: The resolution for the
153
* resulting mappings is the minimium of this map and the supplied map.
154
*
155
* @param aSourceMapConsumer The source map to be applied.
156
* @param aSourceFile Optional. The filename of the source file.
157
* If omitted, SourceMapConsumer's file property will be used.
158
* @param aSourceMapPath Optional. The dirname of the path to the source map
159
* to be applied. If relative, it is relative to the SourceMapConsumer.
160
* This parameter is needed when the two source maps aren't in the same
161
* directory, and the source map to be applied contains relative source
162
* paths. If so, those relative source paths need to be rewritten
163
* relative to the SourceMapGenerator.
164
*/
165
SourceMapGenerator.prototype.applySourceMap =
166
function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) {
167
// If aSourceFile is omitted, we will use the file property of the SourceMap
168
if (!aSourceFile) {
169
if (!aSourceMapConsumer.file) {
170
throw new Error(
171
'SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, ' +
172
'or the source map\'s "file" property. Both were omitted.'
173
);
174
}
175
aSourceFile = aSourceMapConsumer.file;
176
}
177
var sourceRoot = this._sourceRoot;
178
// Make "aSourceFile" relative if an absolute Url is passed.
179
if (sourceRoot) {
180
aSourceFile = util.relative(sourceRoot, aSourceFile);
181
}
182
// Applying the SourceMap can add and remove items from the sources and
183
// the names array.
184
var newSources = new ArraySet();
185
var newNames = new ArraySet();
186
187
// Find mappings for the "aSourceFile"
188
this._mappings.forEach(function (mapping) {
189
if (mapping.source === aSourceFile && mapping.originalLine) {
190
// Check if it can be mapped by the source map, then update the mapping.
191
var original = aSourceMapConsumer.originalPositionFor({
192
line: mapping.originalLine,
193
column: mapping.originalColumn
194
});
195
if (original.source !== null) {
196
// Copy mapping
197
mapping.source = original.source;
198
if (aSourceMapPath) {
199
mapping.source = util.join(aSourceMapPath, mapping.source)
200
}
201
if (sourceRoot) {
202
mapping.source = util.relative(sourceRoot, mapping.source);
203
}
204
mapping.originalLine = original.line;
205
mapping.originalColumn = original.column;
206
if (original.name !== null && mapping.name !== null) {
207
// Only use the identifier name if it's an identifier
208
// in both SourceMaps
209
mapping.name = original.name;
210
}
211
}
212
}
213
214
var source = mapping.source;
215
if (source && !newSources.has(source)) {
216
newSources.add(source);
217
}
218
219
var name = mapping.name;
220
if (name && !newNames.has(name)) {
221
newNames.add(name);
222
}
223
224
}, this);
225
this._sources = newSources;
226
this._names = newNames;
227
228
// Copy sourcesContents of applied map.
229
aSourceMapConsumer.sources.forEach(function (sourceFile) {
230
var content = aSourceMapConsumer.sourceContentFor(sourceFile);
231
if (content) {
232
if (aSourceMapPath) {
233
sourceFile = util.join(aSourceMapPath, sourceFile);
234
}
235
if (sourceRoot) {
236
sourceFile = util.relative(sourceRoot, sourceFile);
237
}
238
this.setSourceContent(sourceFile, content);
239
}
240
}, this);
241
};
242
243
/**
244
* A mapping can have one of the three levels of data:
245
*
246
* 1. Just the generated position.
247
* 2. The Generated position, original position, and original source.
248
* 3. Generated and original position, original source, as well as a name
249
* token.
250
*
251
* To maintain consistency, we validate that any new mapping being added falls
252
* in to one of these categories.
253
*/
254
SourceMapGenerator.prototype._validateMapping =
255
function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
256
aName) {
257
if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
258
&& aGenerated.line > 0 && aGenerated.column >= 0
259
&& !aOriginal && !aSource && !aName) {
260
// Case 1.
261
return;
262
}
263
else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
264
&& aOriginal && 'line' in aOriginal && 'column' in aOriginal
265
&& aGenerated.line > 0 && aGenerated.column >= 0
266
&& aOriginal.line > 0 && aOriginal.column >= 0
267
&& aSource) {
268
// Cases 2 and 3.
269
return;
270
}
271
else {
272
throw new Error('Invalid mapping: ' + JSON.stringify({
273
generated: aGenerated,
274
source: aSource,
275
original: aOriginal,
276
name: aName
277
}));
278
}
279
};
280
281
/**
282
* Serialize the accumulated mappings in to the stream of base 64 VLQs
283
* specified by the source map format.
284
*/
285
SourceMapGenerator.prototype._serializeMappings =
286
function SourceMapGenerator_serializeMappings() {
287
var previousGeneratedColumn = 0;
288
var previousGeneratedLine = 1;
289
var previousOriginalColumn = 0;
290
var previousOriginalLine = 0;
291
var previousName = 0;
292
var previousSource = 0;
293
var result = '';
294
var mapping;
295
296
// The mappings must be guaranteed to be in sorted order before we start
297
// serializing them or else the generated line numbers (which are defined
298
// via the ';' separators) will be all messed up. Note: it might be more
299
// performant to maintain the sorting as we insert them, rather than as we
300
// serialize them, but the big O is the same either way.
301
this._mappings.sort(util.compareByGeneratedPositions);
302
303
for (var i = 0, len = this._mappings.length; i < len; i++) {
304
mapping = this._mappings[i];
305
306
if (mapping.generatedLine !== previousGeneratedLine) {
307
previousGeneratedColumn = 0;
308
while (mapping.generatedLine !== previousGeneratedLine) {
309
result += ';';
310
previousGeneratedLine++;
311
}
312
}
313
else {
314
if (i > 0) {
315
if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) {
316
continue;
317
}
318
result += ',';
319
}
320
}
321
322
result += base64VLQ.encode(mapping.generatedColumn
323
- previousGeneratedColumn);
324
previousGeneratedColumn = mapping.generatedColumn;
325
326
if (mapping.source) {
327
result += base64VLQ.encode(this._sources.indexOf(mapping.source)
328
- previousSource);
329
previousSource = this._sources.indexOf(mapping.source);
330
331
// lines are stored 0-based in SourceMap spec version 3
332
result += base64VLQ.encode(mapping.originalLine - 1
333
- previousOriginalLine);
334
previousOriginalLine = mapping.originalLine - 1;
335
336
result += base64VLQ.encode(mapping.originalColumn
337
- previousOriginalColumn);
338
previousOriginalColumn = mapping.originalColumn;
339
340
if (mapping.name) {
341
result += base64VLQ.encode(this._names.indexOf(mapping.name)
342
- previousName);
343
previousName = this._names.indexOf(mapping.name);
344
}
345
}
346
}
347
348
return result;
349
};
350
351
SourceMapGenerator.prototype._generateSourcesContent =
352
function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) {
353
return aSources.map(function (source) {
354
if (!this._sourcesContents) {
355
return null;
356
}
357
if (aSourceRoot) {
358
source = util.relative(aSourceRoot, source);
359
}
360
var key = util.toSetString(source);
361
return Object.prototype.hasOwnProperty.call(this._sourcesContents,
362
key)
363
? this._sourcesContents[key]
364
: null;
365
}, this);
366
};
367
368
/**
369
* Externalize the source map.
370
*/
371
SourceMapGenerator.prototype.toJSON =
372
function SourceMapGenerator_toJSON() {
373
var map = {
374
version: this._version,
375
file: this._file,
376
sources: this._sources.toArray(),
377
names: this._names.toArray(),
378
mappings: this._serializeMappings()
379
};
380
if (this._sourceRoot) {
381
map.sourceRoot = this._sourceRoot;
382
}
383
if (this._sourcesContents) {
384
map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot);
385
}
386
387
return map;
388
};
389
390
/**
391
* Render the source map being generated to a string.
392
*/
393
SourceMapGenerator.prototype.toString =
394
function SourceMapGenerator_toString() {
395
return JSON.stringify(this);
396
};
397
398
exports.SourceMapGenerator = SourceMapGenerator;
399
400
});
401
402