CoolFace
Apppublic

Pinsave/counterstrike

sourceHugging Faceupdated 3mo agoView on Hugging Face
1likes
source-node.js414 linesDownload Raw Back to lib
1/* -*- Mode: js; js-indent-level: 2; -*- */2/*3 * Copyright 2011 Mozilla Foundation and contributors4 * Licensed under the New BSD license. See LICENSE or:5 * http://opensource.org/licenses/BSD-3-Clause6 */7 8var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator;9var util = require('./util');10 11// Matches a Windows-style `\r\n` newline or a `\n` newline used by all other12// operating systems these days (capturing the result).13var REGEX_NEWLINE = /(\r?\n)/;14 15// Newline character code for charCodeAt() comparisons16var NEWLINE_CODE = 10;17 18// Private symbol for identifying `SourceNode`s when multiple versions of19// the source-map library are loaded. This MUST NOT CHANGE across20// versions!21var isSourceNode = "$$$isSourceNode$$$";22 23/**24 * SourceNodes provide a way to abstract over interpolating/concatenating25 * snippets of generated JavaScript source code while maintaining the line and26 * 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 of32 *        generated JS, or other SourceNodes.33 * @param aName The original identifier.34 */35function 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 code50 * @param aSourceMapConsumer The SourceMap for the generated code51 * @param aRelativePath Optional. The path that relative sources in the52 *        SourceMapConsumer should be relative to.53 */54SourceNode.fromStringWithSourceMap =55  function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer, aRelativePath) {56    // The SourceNode we want to fill with the generated code57    // and the SourceMap58    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 lines62    // (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 mapping96        } else {97          // There is no new line in between.98          // Associate the code between "lastGeneratedColumn" and99          // "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, continue108          lastMapping = mapping;109          return;110        }111      }112      // We add the generated code until the first mapping113      // 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 mapping134      node.add(remainingLines.splice(remainingLinesIndex).join(""));135    }136 137    // Copy sourcesContent into SourceNode138    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 = aRelativePath155          ? 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 of170 *        SourceNode, or an array where each member is one of those things.171 */172SourceNode.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 " + aChunk186    );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 of195 *        SourceNode, or an array where each member is one of those things.196 */197SourceNode.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 " + aChunk209    );210  }211  return this;212};213 214/**215 * Walk over the tree of JS snippets in this node and its children. The216 * walking function is called once for each snippet of JS and is passed that217 * snippet and the its original associated source's line/column location.218 *219 * @param aFn The traversal function.220 */221SourceNode.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` between241 * each of `this.children`.242 *243 * @param aSep The separator.244 */245SourceNode.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. Useful263 * 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 */268SourceNode.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 SourceMapGenerator284 * in the sourcesContent field.285 *286 * @param aSourceFile The filename of the source file287 * @param aSourceContent The content of the source file288 */289SourceNode.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 each296 * source file content and is passed the filename and source content.297 *298 * @param aFn The traversal function.299 */300SourceNode.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 tree316 * and concatenates all the various snippets together to one string.317 */318SourceNode.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 source328 * map.329 */330SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) {331  var generated = {332    code: "",333    line: 1,334    column: 0335  };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 !== null345        && original.line !== null346        && original.column !== null) {347      if(lastOriginalSource !== original.source348         || lastOriginalLine !== original.line349         || lastOriginalColumn !== original.column350         || lastOriginalName !== original.name) {351        map.addMapping({352          source: original.source,353          original: {354            line: original.line,355            column: original.column356          },357          generated: {358            line: generated.line,359            column: generated.column360          },361          name: original.name362        });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.column374        }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 eol384        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.column393            },394            generated: {395              line: generated.line,396              column: generated.column397            },398            name: original.name399          });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 413exports.SourceNode = SourceNode;414