.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / snapdragon / node_modules / source-map / lib / source-node.js
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 var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator;
9 var 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).
13 var REGEX_NEWLINE = /(\r?\n)/;
14
15 // Newline character code for charCodeAt() comparisons
16 var 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!
21 var 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  */
35 function SourceNode(aLine, aColumn, aSource, aChunks, aName) {
36   this.children = [];
37   this.sourceContents = {};
38   this.line = aLine == null ? null : aLine;
39   this.column = aColumn == null ? null : aColumn;
40   this.source = aSource == null ? null : aSource;
41   this.name = aName == null ? null : aName;
42   this[isSourceNode] = true;
43   if (aChunks != null) this.add(aChunks);
44 }
45
46 /**
47  * Creates a SourceNode from generated code and a SourceMapConsumer.
48  *
49  * @param aGeneratedCode The generated code
50  * @param aSourceMapConsumer The SourceMap for the generated code
51  * @param aRelativePath Optional. The path that relative sources in the
52  *        SourceMapConsumer should be relative to.
53  */
54 SourceNode.fromStringWithSourceMap =
55   function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer, aRelativePath) {
56     // The SourceNode we want to fill with the generated code
57     // and the SourceMap
58     var 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     var remainingLines = aGeneratedCode.split(REGEX_NEWLINE);
65     var remainingLinesIndex = 0;
66     var shiftNextLine = function() {
67       var lineContents = getNextLine();
68       // The last line of a file might not have a newline.
69       var 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     var 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     var lastMapping = null;
85
86     aSourceMapConsumer.eachMapping(function (mapping) {
87       if (lastMapping !== null) {
88         // We add the code from "lastMapping" to "mapping":
89         // First check if there is a new line in between.
90         if (lastGeneratedLine < mapping.generatedLine) {
91           // Associate first line with "lastMapping"
92           addMappingWithCode(lastMapping, shiftNextLine());
93           lastGeneratedLine++;
94           lastGeneratedColumn = 0;
95           // The remaining code is added without mapping
96         } else {
97           // There is no new line in between.
98           // Associate the code between "lastGeneratedColumn" and
99           // "mapping.generatedColumn" with "lastMapping"
100           var nextLine = remainingLines[remainingLinesIndex];
101           var code = nextLine.substr(0, mapping.generatedColumn -
102                                         lastGeneratedColumn);
103           remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn -
104                                               lastGeneratedColumn);
105           lastGeneratedColumn = mapping.generatedColumn;
106           addMappingWithCode(lastMapping, code);
107           // No more remaining code, continue
108           lastMapping = mapping;
109           return;
110         }
111       }
112       // We add the generated code until the first mapping
113       // to the SourceNode without any mapping.
114       // Each line is added as separate string.
115       while (lastGeneratedLine < mapping.generatedLine) {
116         node.add(shiftNextLine());
117         lastGeneratedLine++;
118       }
119       if (lastGeneratedColumn < mapping.generatedColumn) {
120         var nextLine = remainingLines[remainingLinesIndex];
121         node.add(nextLine.substr(0, mapping.generatedColumn));
122         remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn);
123         lastGeneratedColumn = mapping.generatedColumn;
124       }
125       lastMapping = mapping;
126     }, this);
127     // We have processed all mappings.
128     if (remainingLinesIndex < remainingLines.length) {
129       if (lastMapping) {
130         // Associate the remaining code in the current line with "lastMapping"
131         addMappingWithCode(lastMapping, shiftNextLine());
132       }
133       // and add the remaining lines without any mapping
134       node.add(remainingLines.splice(remainingLinesIndex).join(""));
135     }
136
137     // Copy sourcesContent into SourceNode
138     aSourceMapConsumer.sources.forEach(function (sourceFile) {
139       var content = aSourceMapConsumer.sourceContentFor(sourceFile);
140       if (content != null) {
141         if (aRelativePath != null) {
142           sourceFile = util.join(aRelativePath, sourceFile);
143         }
144         node.setSourceContent(sourceFile, content);
145       }
146     });
147
148     return node;
149
150     function addMappingWithCode(mapping, code) {
151       if (mapping === null || mapping.source === undefined) {
152         node.add(code);
153       } else {
154         var source = aRelativePath
155           ? util.join(aRelativePath, mapping.source)
156           : mapping.source;
157         node.add(new SourceNode(mapping.originalLine,
158                                 mapping.originalColumn,
159                                 source,
160                                 code,
161                                 mapping.name));
162       }
163     }
164   };
165
166 /**
167  * Add a chunk of generated JS to this source node.
168  *
169  * @param aChunk A string snippet of generated JS code, another instance of
170  *        SourceNode, or an array where each member is one of those things.
171  */
172 SourceNode.prototype.add = function SourceNode_add(aChunk) {
173   if (Array.isArray(aChunk)) {
174     aChunk.forEach(function (chunk) {
175       this.add(chunk);
176     }, this);
177   }
178   else if (aChunk[isSourceNode] || typeof aChunk === "string") {
179     if (aChunk) {
180       this.children.push(aChunk);
181     }
182   }
183   else {
184     throw new TypeError(
185       "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
186     );
187   }
188   return this;
189 };
190
191 /**
192  * Add a chunk of generated JS to the beginning of this source node.
193  *
194  * @param aChunk A string snippet of generated JS code, another instance of
195  *        SourceNode, or an array where each member is one of those things.
196  */
197 SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) {
198   if (Array.isArray(aChunk)) {
199     for (var i = aChunk.length-1; i >= 0; i--) {
200       this.prepend(aChunk[i]);
201     }
202   }
203   else if (aChunk[isSourceNode] || typeof aChunk === "string") {
204     this.children.unshift(aChunk);
205   }
206   else {
207     throw new TypeError(
208       "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
209     );
210   }
211   return this;
212 };
213
214 /**
215  * Walk over the tree of JS snippets in this node and its children. The
216  * walking function is called once for each snippet of JS and is passed that
217  * snippet and the its original associated source's line/column location.
218  *
219  * @param aFn The traversal function.
220  */
221 SourceNode.prototype.walk = function SourceNode_walk(aFn) {
222   var chunk;
223   for (var i = 0, len = this.children.length; i < len; i++) {
224     chunk = this.children[i];
225     if (chunk[isSourceNode]) {
226       chunk.walk(aFn);
227     }
228     else {
229       if (chunk !== '') {
230         aFn(chunk, { source: this.source,
231                      line: this.line,
232                      column: this.column,
233                      name: this.name });
234       }
235     }
236   }
237 };
238
239 /**
240  * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between
241  * each of `this.children`.
242  *
243  * @param aSep The separator.
244  */
245 SourceNode.prototype.join = function SourceNode_join(aSep) {
246   var newChildren;
247   var i;
248   var len = this.children.length;
249   if (len > 0) {
250     newChildren = [];
251     for (i = 0; i < len-1; i++) {
252       newChildren.push(this.children[i]);
253       newChildren.push(aSep);
254     }
255     newChildren.push(this.children[i]);
256     this.children = newChildren;
257   }
258   return this;
259 };
260
261 /**
262  * Call String.prototype.replace on the very right-most source snippet. Useful
263  * for trimming whitespace from the end of a source node, etc.
264  *
265  * @param aPattern The pattern to replace.
266  * @param aReplacement The thing to replace the pattern with.
267  */
268 SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) {
269   var lastChild = this.children[this.children.length - 1];
270   if (lastChild[isSourceNode]) {
271     lastChild.replaceRight(aPattern, aReplacement);
272   }
273   else if (typeof lastChild === 'string') {
274     this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement);
275   }
276   else {
277     this.children.push(''.replace(aPattern, aReplacement));
278   }
279   return this;
280 };
281
282 /**
283  * Set the source content for a source file. This will be added to the SourceMapGenerator
284  * in the sourcesContent field.
285  *
286  * @param aSourceFile The filename of the source file
287  * @param aSourceContent The content of the source file
288  */
289 SourceNode.prototype.setSourceContent =
290   function SourceNode_setSourceContent(aSourceFile, aSourceContent) {
291     this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent;
292   };
293
294 /**
295  * Walk over the tree of SourceNodes. The walking function is called for each
296  * source file content and is passed the filename and source content.
297  *
298  * @param aFn The traversal function.
299  */
300 SourceNode.prototype.walkSourceContents =
301   function SourceNode_walkSourceContents(aFn) {
302     for (var i = 0, len = this.children.length; i < len; i++) {
303       if (this.children[i][isSourceNode]) {
304         this.children[i].walkSourceContents(aFn);
305       }
306     }
307
308     var sources = Object.keys(this.sourceContents);
309     for (var i = 0, len = sources.length; i < len; i++) {
310       aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]);
311     }
312   };
313
314 /**
315  * Return the string representation of this source node. Walks over the tree
316  * and concatenates all the various snippets together to one string.
317  */
318 SourceNode.prototype.toString = function SourceNode_toString() {
319   var str = "";
320   this.walk(function (chunk) {
321     str += chunk;
322   });
323   return str;
324 };
325
326 /**
327  * Returns the string representation of this source node along with a source
328  * map.
329  */
330 SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) {
331   var generated = {
332     code: "",
333     line: 1,
334     column: 0
335   };
336   var map = new SourceMapGenerator(aArgs);
337   var sourceMappingActive = false;
338   var lastOriginalSource = null;
339   var lastOriginalLine = null;
340   var lastOriginalColumn = null;
341   var lastOriginalName = null;
342   this.walk(function (chunk, original) {
343     generated.code += chunk;
344     if (original.source !== null
345         && original.line !== null
346         && original.column !== null) {
347       if(lastOriginalSource !== original.source
348          || lastOriginalLine !== original.line
349          || lastOriginalColumn !== original.column
350          || lastOriginalName !== original.name) {
351         map.addMapping({
352           source: original.source,
353           original: {
354             line: original.line,
355             column: original.column
356           },
357           generated: {
358             line: generated.line,
359             column: generated.column
360           },
361           name: original.name
362         });
363       }
364       lastOriginalSource = original.source;
365       lastOriginalLine = original.line;
366       lastOriginalColumn = original.column;
367       lastOriginalName = original.name;
368       sourceMappingActive = true;
369     } else if (sourceMappingActive) {
370       map.addMapping({
371         generated: {
372           line: generated.line,
373           column: generated.column
374         }
375       });
376       lastOriginalSource = null;
377       sourceMappingActive = false;
378     }
379     for (var idx = 0, length = chunk.length; idx < length; idx++) {
380       if (chunk.charCodeAt(idx) === NEWLINE_CODE) {
381         generated.line++;
382         generated.column = 0;
383         // Mappings end at eol
384         if (idx + 1 === length) {
385           lastOriginalSource = null;
386           sourceMappingActive = false;
387         } else if (sourceMappingActive) {
388           map.addMapping({
389             source: original.source,
390             original: {
391               line: original.line,
392               column: original.column
393             },
394             generated: {
395               line: generated.line,
396               column: generated.column
397             },
398             name: original.name
399           });
400         }
401       } else {
402         generated.column++;
403       }
404     }
405   });
406   this.walkSourceContents(function (sourceFile, sourceContent) {
407     map.setSourceContent(sourceFile, sourceContent);
408   });
409
410   return { code: generated.code, map: map };
411 };
412
413 exports.SourceNode = SourceNode;