react / wstein / node_modules / react / node_modules / envify / node_modules / jstransform / node_modules / source-map / lib / source-map / source-node.js
80559 views/* -*- Mode: js; js-indent-level: 2; -*- */1/*2* Copyright 2011 Mozilla Foundation and contributors3* Licensed under the New BSD license. See LICENSE or:4* http://opensource.org/licenses/BSD-3-Clause5*/6if (typeof define !== 'function') {7var define = require('amdefine')(module, require);8}9define(function (require, exports, module) {1011var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator;12var util = require('./util');1314/**15* SourceNodes provide a way to abstract over interpolating/concatenating16* snippets of generated JavaScript source code while maintaining the line and17* column information associated with the original source code.18*19* @param aLine The original line number.20* @param aColumn The original column number.21* @param aSource The original source's filename.22* @param aChunks Optional. An array of strings which are snippets of23* generated JS, or other SourceNodes.24* @param aName The original identifier.25*/26function SourceNode(aLine, aColumn, aSource, aChunks, aName) {27this.children = [];28this.sourceContents = {};29this.line = aLine === undefined ? null : aLine;30this.column = aColumn === undefined ? null : aColumn;31this.source = aSource === undefined ? null : aSource;32this.name = aName === undefined ? null : aName;33if (aChunks != null) this.add(aChunks);34}3536/**37* Creates a SourceNode from generated code and a SourceMapConsumer.38*39* @param aGeneratedCode The generated code40* @param aSourceMapConsumer The SourceMap for the generated code41*/42SourceNode.fromStringWithSourceMap =43function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) {44// The SourceNode we want to fill with the generated code45// and the SourceMap46var node = new SourceNode();4748// The generated code49// Processed fragments are removed from this array.50var remainingLines = aGeneratedCode.split('\n');5152// We need to remember the position of "remainingLines"53var lastGeneratedLine = 1, lastGeneratedColumn = 0;5455// The generate SourceNodes we need a code range.56// To extract it current and last mapping is used.57// Here we store the last mapping.58var lastMapping = null;5960aSourceMapConsumer.eachMapping(function (mapping) {61if (lastMapping === null) {62// We add the generated code until the first mapping63// to the SourceNode without any mapping.64// Each line is added as separate string.65while (lastGeneratedLine < mapping.generatedLine) {66node.add(remainingLines.shift() + "\n");67lastGeneratedLine++;68}69if (lastGeneratedColumn < mapping.generatedColumn) {70var nextLine = remainingLines[0];71node.add(nextLine.substr(0, mapping.generatedColumn));72remainingLines[0] = nextLine.substr(mapping.generatedColumn);73lastGeneratedColumn = mapping.generatedColumn;74}75} else {76// We add the code from "lastMapping" to "mapping":77// First check if there is a new line in between.78if (lastGeneratedLine < mapping.generatedLine) {79var code = "";80// Associate full lines with "lastMapping"81do {82code += remainingLines.shift() + "\n";83lastGeneratedLine++;84lastGeneratedColumn = 0;85} while (lastGeneratedLine < mapping.generatedLine);86// When we reached the correct line, we add code until we87// reach the correct column too.88if (lastGeneratedColumn < mapping.generatedColumn) {89var nextLine = remainingLines[0];90code += nextLine.substr(0, mapping.generatedColumn);91remainingLines[0] = nextLine.substr(mapping.generatedColumn);92lastGeneratedColumn = mapping.generatedColumn;93}94// Create the SourceNode.95addMappingWithCode(lastMapping, code);96} else {97// There is no new line in between.98// Associate the code between "lastGeneratedColumn" and99// "mapping.generatedColumn" with "lastMapping"100var nextLine = remainingLines[0];101var code = nextLine.substr(0, mapping.generatedColumn -102lastGeneratedColumn);103remainingLines[0] = nextLine.substr(mapping.generatedColumn -104lastGeneratedColumn);105lastGeneratedColumn = mapping.generatedColumn;106addMappingWithCode(lastMapping, code);107}108}109lastMapping = mapping;110}, this);111// We have processed all mappings.112// Associate the remaining code in the current line with "lastMapping"113// and add the remaining lines without any mapping114addMappingWithCode(lastMapping, remainingLines.join("\n"));115116// Copy sourcesContent into SourceNode117aSourceMapConsumer.sources.forEach(function (sourceFile) {118var content = aSourceMapConsumer.sourceContentFor(sourceFile);119if (content) {120node.setSourceContent(sourceFile, content);121}122});123124return node;125126function addMappingWithCode(mapping, code) {127if (mapping === null || mapping.source === undefined) {128node.add(code);129} else {130node.add(new SourceNode(mapping.originalLine,131mapping.originalColumn,132mapping.source,133code,134mapping.name));135}136}137};138139/**140* Add a chunk of generated JS to this source node.141*142* @param aChunk A string snippet of generated JS code, another instance of143* SourceNode, or an array where each member is one of those things.144*/145SourceNode.prototype.add = function SourceNode_add(aChunk) {146if (Array.isArray(aChunk)) {147aChunk.forEach(function (chunk) {148this.add(chunk);149}, this);150}151else if (aChunk instanceof SourceNode || typeof aChunk === "string") {152if (aChunk) {153this.children.push(aChunk);154}155}156else {157throw new TypeError(158"Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk159);160}161return this;162};163164/**165* Add a chunk of generated JS to the beginning of this source node.166*167* @param aChunk A string snippet of generated JS code, another instance of168* SourceNode, or an array where each member is one of those things.169*/170SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) {171if (Array.isArray(aChunk)) {172for (var i = aChunk.length-1; i >= 0; i--) {173this.prepend(aChunk[i]);174}175}176else if (aChunk instanceof SourceNode || typeof aChunk === "string") {177this.children.unshift(aChunk);178}179else {180throw new TypeError(181"Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk182);183}184return this;185};186187/**188* Walk over the tree of JS snippets in this node and its children. The189* walking function is called once for each snippet of JS and is passed that190* snippet and the its original associated source's line/column location.191*192* @param aFn The traversal function.193*/194SourceNode.prototype.walk = function SourceNode_walk(aFn) {195var chunk;196for (var i = 0, len = this.children.length; i < len; i++) {197chunk = this.children[i];198if (chunk instanceof SourceNode) {199chunk.walk(aFn);200}201else {202if (chunk !== '') {203aFn(chunk, { source: this.source,204line: this.line,205column: this.column,206name: this.name });207}208}209}210};211212/**213* Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between214* each of `this.children`.215*216* @param aSep The separator.217*/218SourceNode.prototype.join = function SourceNode_join(aSep) {219var newChildren;220var i;221var len = this.children.length;222if (len > 0) {223newChildren = [];224for (i = 0; i < len-1; i++) {225newChildren.push(this.children[i]);226newChildren.push(aSep);227}228newChildren.push(this.children[i]);229this.children = newChildren;230}231return this;232};233234/**235* Call String.prototype.replace on the very right-most source snippet. Useful236* for trimming whitespace from the end of a source node, etc.237*238* @param aPattern The pattern to replace.239* @param aReplacement The thing to replace the pattern with.240*/241SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) {242var lastChild = this.children[this.children.length - 1];243if (lastChild instanceof SourceNode) {244lastChild.replaceRight(aPattern, aReplacement);245}246else if (typeof lastChild === 'string') {247this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement);248}249else {250this.children.push(''.replace(aPattern, aReplacement));251}252return this;253};254255/**256* Set the source content for a source file. This will be added to the SourceMapGenerator257* in the sourcesContent field.258*259* @param aSourceFile The filename of the source file260* @param aSourceContent The content of the source file261*/262SourceNode.prototype.setSourceContent =263function SourceNode_setSourceContent(aSourceFile, aSourceContent) {264this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent;265};266267/**268* Walk over the tree of SourceNodes. The walking function is called for each269* source file content and is passed the filename and source content.270*271* @param aFn The traversal function.272*/273SourceNode.prototype.walkSourceContents =274function SourceNode_walkSourceContents(aFn) {275for (var i = 0, len = this.children.length; i < len; i++) {276if (this.children[i] instanceof SourceNode) {277this.children[i].walkSourceContents(aFn);278}279}280281var sources = Object.keys(this.sourceContents);282for (var i = 0, len = sources.length; i < len; i++) {283aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]);284}285};286287/**288* Return the string representation of this source node. Walks over the tree289* and concatenates all the various snippets together to one string.290*/291SourceNode.prototype.toString = function SourceNode_toString() {292var str = "";293this.walk(function (chunk) {294str += chunk;295});296return str;297};298299/**300* Returns the string representation of this source node along with a source301* map.302*/303SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) {304var generated = {305code: "",306line: 1,307column: 0308};309var map = new SourceMapGenerator(aArgs);310var sourceMappingActive = false;311var lastOriginalSource = null;312var lastOriginalLine = null;313var lastOriginalColumn = null;314var lastOriginalName = null;315this.walk(function (chunk, original) {316generated.code += chunk;317if (original.source !== null318&& original.line !== null319&& original.column !== null) {320if(lastOriginalSource !== original.source321|| lastOriginalLine !== original.line322|| lastOriginalColumn !== original.column323|| lastOriginalName !== original.name) {324map.addMapping({325source: original.source,326original: {327line: original.line,328column: original.column329},330generated: {331line: generated.line,332column: generated.column333},334name: original.name335});336}337lastOriginalSource = original.source;338lastOriginalLine = original.line;339lastOriginalColumn = original.column;340lastOriginalName = original.name;341sourceMappingActive = true;342} else if (sourceMappingActive) {343map.addMapping({344generated: {345line: generated.line,346column: generated.column347}348});349lastOriginalSource = null;350sourceMappingActive = false;351}352chunk.split('').forEach(function (ch) {353if (ch === '\n') {354generated.line++;355generated.column = 0;356} else {357generated.column++;358}359});360});361this.walkSourceContents(function (sourceFile, sourceContent) {362map.setSourceContent(sourceFile, sourceContent);363});364365return { code: generated.code, map: map };366};367368exports.SourceNode = SourceNode;369370});371372373