source: trip-planner-front/node_modules/source-map/lib/source-node.js@ 1ad8e64

Last change on this file since 1ad8e64 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 13.4 KB
RevLine 
[6a3a178]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 SourceMapGenerator = require("./source-map-generator").SourceMapGenerator;
9const util = require("./util");
10
11// Matches a Windows-style `\r\n` newline or a `\n` newline used by all other
12// operating systems these days (capturing the result).
13const REGEX_NEWLINE = /(\r?\n)/;
14
15// Newline character code for charCodeAt() comparisons
16const NEWLINE_CODE = 10;
17
18// Private symbol for identifying `SourceNode`s when multiple versions of
19// the source-map library are loaded. This MUST NOT CHANGE across
20// versions!
21const isSourceNode = "$$$isSourceNode$$$";
22
23/**
24 * SourceNodes provide a way to abstract over interpolating/concatenating
25 * snippets of generated JavaScript source code while maintaining the line and
26 * column information associated with the original source code.
27 *
28 * @param aLine The original line number.
29 * @param aColumn The original column number.
30 * @param aSource The original source's filename.
31 * @param aChunks Optional. An array of strings which are snippets of
32 * generated JS, or other SourceNodes.
33 * @param aName The original identifier.
34 */
35class SourceNode {
36 constructor(aLine, aColumn, aSource, aChunks, aName) {
37 this.children = [];
38 this.sourceContents = {};
39 this.line = aLine == null ? null : aLine;
40 this.column = aColumn == null ? null : aColumn;
41 this.source = aSource == null ? null : aSource;
42 this.name = aName == null ? null : aName;
43 this[isSourceNode] = true;
44 if (aChunks != null) this.add(aChunks);
45 }
46
47 /**
48 * Creates a SourceNode from generated code and a SourceMapConsumer.
49 *
50 * @param aGeneratedCode The generated code
51 * @param aSourceMapConsumer The SourceMap for the generated code
52 * @param aRelativePath Optional. The path that relative sources in the
53 * SourceMapConsumer should be relative to.
54 */
55 static fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer, aRelativePath) {
56 // The SourceNode we want to fill with the generated code
57 // and the SourceMap
58 const node = new SourceNode();
59
60 // All even indices of this array are one line of the generated code,
61 // while all odd indices are the newlines between two adjacent lines
62 // (since `REGEX_NEWLINE` captures its match).
63 // Processed fragments are accessed by calling `shiftNextLine`.
64 const remainingLines = aGeneratedCode.split(REGEX_NEWLINE);
65 let remainingLinesIndex = 0;
66 const shiftNextLine = function() {
67 const lineContents = getNextLine();
68 // The last line of a file might not have a newline.
69 const newLine = getNextLine() || "";
70 return lineContents + newLine;
71
72 function getNextLine() {
73 return remainingLinesIndex < remainingLines.length ?
74 remainingLines[remainingLinesIndex++] : undefined;
75 }
76 };
77
78 // We need to remember the position of "remainingLines"
79 let lastGeneratedLine = 1, lastGeneratedColumn = 0;
80
81 // The generate SourceNodes we need a code range.
82 // To extract it current and last mapping is used.
83 // Here we store the last mapping.
84 let lastMapping = null;
85 let nextLine;
86
87 aSourceMapConsumer.eachMapping(function(mapping) {
88 if (lastMapping !== null) {
89 // We add the code from "lastMapping" to "mapping":
90 // First check if there is a new line in between.
91 if (lastGeneratedLine < mapping.generatedLine) {
92 // Associate first line with "lastMapping"
93 addMappingWithCode(lastMapping, shiftNextLine());
94 lastGeneratedLine++;
95 lastGeneratedColumn = 0;
96 // The remaining code is added without mapping
97 } else {
98 // There is no new line in between.
99 // Associate the code between "lastGeneratedColumn" and
100 // "mapping.generatedColumn" with "lastMapping"
101 nextLine = remainingLines[remainingLinesIndex] || "";
102 const code = nextLine.substr(0, mapping.generatedColumn -
103 lastGeneratedColumn);
104 remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn -
105 lastGeneratedColumn);
106 lastGeneratedColumn = mapping.generatedColumn;
107 addMappingWithCode(lastMapping, code);
108 // No more remaining code, continue
109 lastMapping = mapping;
110 return;
111 }
112 }
113 // We add the generated code until the first mapping
114 // to the SourceNode without any mapping.
115 // Each line is added as separate string.
116 while (lastGeneratedLine < mapping.generatedLine) {
117 node.add(shiftNextLine());
118 lastGeneratedLine++;
119 }
120 if (lastGeneratedColumn < mapping.generatedColumn) {
121 nextLine = remainingLines[remainingLinesIndex] || "";
122 node.add(nextLine.substr(0, mapping.generatedColumn));
123 remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn);
124 lastGeneratedColumn = mapping.generatedColumn;
125 }
126 lastMapping = mapping;
127 }, this);
128 // We have processed all mappings.
129 if (remainingLinesIndex < remainingLines.length) {
130 if (lastMapping) {
131 // Associate the remaining code in the current line with "lastMapping"
132 addMappingWithCode(lastMapping, shiftNextLine());
133 }
134 // and add the remaining lines without any mapping
135 node.add(remainingLines.splice(remainingLinesIndex).join(""));
136 }
137
138 // Copy sourcesContent into SourceNode
139 aSourceMapConsumer.sources.forEach(function(sourceFile) {
140 const content = aSourceMapConsumer.sourceContentFor(sourceFile);
141 if (content != null) {
142 if (aRelativePath != null) {
143 sourceFile = util.join(aRelativePath, sourceFile);
144 }
145 node.setSourceContent(sourceFile, content);
146 }
147 });
148
149 return node;
150
151 function addMappingWithCode(mapping, code) {
152 if (mapping === null || mapping.source === undefined) {
153 node.add(code);
154 } else {
155 const source = aRelativePath
156 ? util.join(aRelativePath, mapping.source)
157 : mapping.source;
158 node.add(new SourceNode(mapping.originalLine,
159 mapping.originalColumn,
160 source,
161 code,
162 mapping.name));
163 }
164 }
165 }
166
167 /**
168 * Add a chunk of generated JS to this source node.
169 *
170 * @param aChunk A string snippet of generated JS code, another instance of
171 * SourceNode, or an array where each member is one of those things.
172 */
173 add(aChunk) {
174 if (Array.isArray(aChunk)) {
175 aChunk.forEach(function(chunk) {
176 this.add(chunk);
177 }, this);
178 } else if (aChunk[isSourceNode] || typeof aChunk === "string") {
179 if (aChunk) {
180 this.children.push(aChunk);
181 }
182 } else {
183 throw new TypeError(
184 "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
185 );
186 }
187 return this;
188 }
189
190 /**
191 * Add a chunk of generated JS to the beginning of this source node.
192 *
193 * @param aChunk A string snippet of generated JS code, another instance of
194 * SourceNode, or an array where each member is one of those things.
195 */
196 prepend(aChunk) {
197 if (Array.isArray(aChunk)) {
198 for (let i = aChunk.length - 1; i >= 0; i--) {
199 this.prepend(aChunk[i]);
200 }
201 } else if (aChunk[isSourceNode] || typeof aChunk === "string") {
202 this.children.unshift(aChunk);
203 } else {
204 throw new TypeError(
205 "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
206 );
207 }
208 return this;
209 }
210
211 /**
212 * Walk over the tree of JS snippets in this node and its children. The
213 * walking function is called once for each snippet of JS and is passed that
214 * snippet and the its original associated source's line/column location.
215 *
216 * @param aFn The traversal function.
217 */
218 walk(aFn) {
219 let chunk;
220 for (let i = 0, len = this.children.length; i < len; i++) {
221 chunk = this.children[i];
222 if (chunk[isSourceNode]) {
223 chunk.walk(aFn);
224 } else if (chunk !== "") {
225 aFn(chunk, { source: this.source,
226 line: this.line,
227 column: this.column,
228 name: this.name });
229 }
230 }
231 }
232
233 /**
234 * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between
235 * each of `this.children`.
236 *
237 * @param aSep The separator.
238 */
239 join(aSep) {
240 let newChildren;
241 let i;
242 const len = this.children.length;
243 if (len > 0) {
244 newChildren = [];
245 for (i = 0; i < len - 1; i++) {
246 newChildren.push(this.children[i]);
247 newChildren.push(aSep);
248 }
249 newChildren.push(this.children[i]);
250 this.children = newChildren;
251 }
252 return this;
253 }
254
255 /**
256 * Call String.prototype.replace on the very right-most source snippet. Useful
257 * for trimming whitespace from the end of a source node, etc.
258 *
259 * @param aPattern The pattern to replace.
260 * @param aReplacement The thing to replace the pattern with.
261 */
262 replaceRight(aPattern, aReplacement) {
263 const lastChild = this.children[this.children.length - 1];
264 if (lastChild[isSourceNode]) {
265 lastChild.replaceRight(aPattern, aReplacement);
266 } else if (typeof lastChild === "string") {
267 this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement);
268 } else {
269 this.children.push("".replace(aPattern, aReplacement));
270 }
271 return this;
272 }
273
274 /**
275 * Set the source content for a source file. This will be added to the SourceMapGenerator
276 * in the sourcesContent field.
277 *
278 * @param aSourceFile The filename of the source file
279 * @param aSourceContent The content of the source file
280 */
281 setSourceContent(aSourceFile, aSourceContent) {
282 this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent;
283 }
284
285 /**
286 * Walk over the tree of SourceNodes. The walking function is called for each
287 * source file content and is passed the filename and source content.
288 *
289 * @param aFn The traversal function.
290 */
291 walkSourceContents(aFn) {
292 for (let i = 0, len = this.children.length; i < len; i++) {
293 if (this.children[i][isSourceNode]) {
294 this.children[i].walkSourceContents(aFn);
295 }
296 }
297
298 const sources = Object.keys(this.sourceContents);
299 for (let i = 0, len = sources.length; i < len; i++) {
300 aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]);
301 }
302 }
303
304 /**
305 * Return the string representation of this source node. Walks over the tree
306 * and concatenates all the various snippets together to one string.
307 */
308 toString() {
309 let str = "";
310 this.walk(function(chunk) {
311 str += chunk;
312 });
313 return str;
314 }
315
316 /**
317 * Returns the string representation of this source node along with a source
318 * map.
319 */
320 toStringWithSourceMap(aArgs) {
321 const generated = {
322 code: "",
323 line: 1,
324 column: 0
325 };
326 const map = new SourceMapGenerator(aArgs);
327 let sourceMappingActive = false;
328 let lastOriginalSource = null;
329 let lastOriginalLine = null;
330 let lastOriginalColumn = null;
331 let lastOriginalName = null;
332 this.walk(function(chunk, original) {
333 generated.code += chunk;
334 if (original.source !== null
335 && original.line !== null
336 && original.column !== null) {
337 if (lastOriginalSource !== original.source
338 || lastOriginalLine !== original.line
339 || lastOriginalColumn !== original.column
340 || lastOriginalName !== original.name) {
341 map.addMapping({
342 source: original.source,
343 original: {
344 line: original.line,
345 column: original.column
346 },
347 generated: {
348 line: generated.line,
349 column: generated.column
350 },
351 name: original.name
352 });
353 }
354 lastOriginalSource = original.source;
355 lastOriginalLine = original.line;
356 lastOriginalColumn = original.column;
357 lastOriginalName = original.name;
358 sourceMappingActive = true;
359 } else if (sourceMappingActive) {
360 map.addMapping({
361 generated: {
362 line: generated.line,
363 column: generated.column
364 }
365 });
366 lastOriginalSource = null;
367 sourceMappingActive = false;
368 }
369 for (let idx = 0, length = chunk.length; idx < length; idx++) {
370 if (chunk.charCodeAt(idx) === NEWLINE_CODE) {
371 generated.line++;
372 generated.column = 0;
373 // Mappings end at eol
374 if (idx + 1 === length) {
375 lastOriginalSource = null;
376 sourceMappingActive = false;
377 } else if (sourceMappingActive) {
378 map.addMapping({
379 source: original.source,
380 original: {
381 line: original.line,
382 column: original.column
383 },
384 generated: {
385 line: generated.line,
386 column: generated.column
387 },
388 name: original.name
389 });
390 }
391 } else {
392 generated.column++;
393 }
394 }
395 });
396 this.walkSourceContents(function(sourceFile, sourceContent) {
397 map.setSourceContent(sourceFile, sourceContent);
398 });
399
400 return { code: generated.code, map };
401 }
402}
403
404exports.SourceNode = SourceNode;
Note: See TracBrowser for help on using the repository browser.