source: frontend/node_modules/source-map/lib/source-map-generator.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

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