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