更新
This commit is contained in:
Generated
Vendored
+100
@@ -0,0 +1,100 @@
|
||||
/* -*- Mode: js; js-indent-level: 2; -*- */
|
||||
/*
|
||||
* Copyright 2011 Mozilla Foundation and contributors
|
||||
* Licensed under the New BSD license. See LICENSE or:
|
||||
* http://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
/**
|
||||
* A data structure which is a combination of an array and a set. Adding a new
|
||||
* member is O(1), testing for membership is O(1), and finding the index of an
|
||||
* element is O(1). Removing elements from the set is not supported. Only
|
||||
* strings are supported for membership.
|
||||
*/
|
||||
class ArraySet {
|
||||
constructor() {
|
||||
this._array = []
|
||||
this._set = new Map()
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method for creating ArraySet instances from an existing array.
|
||||
*/
|
||||
static fromArray(aArray, aAllowDuplicates) {
|
||||
const set = new ArraySet()
|
||||
for (let i = 0, len = aArray.length; i < len; i++) {
|
||||
set.add(aArray[i], aAllowDuplicates)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
/**
|
||||
* Return how many unique items are in this ArraySet. If duplicates have been
|
||||
* added, than those do not count towards the size.
|
||||
*
|
||||
* @returns Number
|
||||
*/
|
||||
size() {
|
||||
return this._set.size
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given string to this set.
|
||||
*
|
||||
* @param String aStr
|
||||
*/
|
||||
add(aStr, aAllowDuplicates) {
|
||||
const isDuplicate = this.has(aStr)
|
||||
const idx = this._array.length
|
||||
if (!isDuplicate || aAllowDuplicates) {
|
||||
this._array.push(aStr)
|
||||
}
|
||||
if (!isDuplicate) {
|
||||
this._set.set(aStr, idx)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the given string a member of this set?
|
||||
*
|
||||
* @param String aStr
|
||||
*/
|
||||
has(aStr) {
|
||||
return this._set.has(aStr)
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the index of the given string in the array?
|
||||
*
|
||||
* @param String aStr
|
||||
*/
|
||||
indexOf(aStr) {
|
||||
const idx = this._set.get(aStr)
|
||||
if (idx >= 0) {
|
||||
return idx
|
||||
}
|
||||
throw new Error('"' + aStr + '" is not in the set.')
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the element at the given index?
|
||||
*
|
||||
* @param Number aIdx
|
||||
*/
|
||||
at(aIdx) {
|
||||
if (aIdx >= 0 && aIdx < this._array.length) {
|
||||
return this._array[aIdx]
|
||||
}
|
||||
throw new Error('No element indexed by ' + aIdx)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array representation of this set (which has the proper indices
|
||||
* indicated by indexOf). Note that this is a copy of the internal array used
|
||||
* for storing the members so that no one can mess with internal state.
|
||||
*/
|
||||
toArray() {
|
||||
return this._array.slice()
|
||||
}
|
||||
}
|
||||
exports.ArraySet = ArraySet
|
||||
Generated
Vendored
+107
@@ -0,0 +1,107 @@
|
||||
/* -*- Mode: js; js-indent-level: 2; -*- */
|
||||
/*
|
||||
* Copyright 2011 Mozilla Foundation and contributors
|
||||
* Licensed under the New BSD license. See LICENSE or:
|
||||
* http://opensource.org/licenses/BSD-3-Clause
|
||||
*
|
||||
* Based on the Base 64 VLQ implementation in Closure Compiler:
|
||||
* https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java
|
||||
*
|
||||
* Copyright 2011 The Closure Compiler Authors. All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
const base64 = require('./base64')
|
||||
|
||||
// A single base 64 digit can contain 6 bits of data. For the base 64 variable
|
||||
// length quantities we use in the source map spec, the first bit is the sign,
|
||||
// the next four bits are the actual value, and the 6th bit is the
|
||||
// continuation bit. The continuation bit tells us whether there are more
|
||||
// digits in this value following this digit.
|
||||
//
|
||||
// Continuation
|
||||
// | Sign
|
||||
// | |
|
||||
// V V
|
||||
// 101011
|
||||
|
||||
const VLQ_BASE_SHIFT = 5
|
||||
|
||||
// binary: 100000
|
||||
const VLQ_BASE = 1 << VLQ_BASE_SHIFT
|
||||
|
||||
// binary: 011111
|
||||
const VLQ_BASE_MASK = VLQ_BASE - 1
|
||||
|
||||
// binary: 100000
|
||||
const VLQ_CONTINUATION_BIT = VLQ_BASE
|
||||
|
||||
/**
|
||||
* Converts from a two-complement value to a value where the sign bit is
|
||||
* placed in the least significant bit. For example, as decimals:
|
||||
* 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
|
||||
* 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
|
||||
*/
|
||||
function toVLQSigned(aValue) {
|
||||
return aValue < 0 ? (-aValue << 1) + 1 : (aValue << 1) + 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts to a two-complement value from a value where the sign bit is
|
||||
* placed in the least significant bit. For example, as decimals:
|
||||
* 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
|
||||
* 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function fromVLQSigned(aValue) {
|
||||
const isNegative = (aValue & 1) === 1
|
||||
const shifted = aValue >> 1
|
||||
return isNegative ? -shifted : shifted
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base 64 VLQ encoded value.
|
||||
*/
|
||||
exports.encode = function base64VLQ_encode(aValue) {
|
||||
let encoded = ''
|
||||
let digit
|
||||
|
||||
let vlq = toVLQSigned(aValue)
|
||||
|
||||
do {
|
||||
digit = vlq & VLQ_BASE_MASK
|
||||
vlq >>>= VLQ_BASE_SHIFT
|
||||
if (vlq > 0) {
|
||||
// There are still more digits in this value, so we must make sure the
|
||||
// continuation bit is marked.
|
||||
digit |= VLQ_CONTINUATION_BIT
|
||||
}
|
||||
encoded += base64.encode(digit)
|
||||
} while (vlq > 0)
|
||||
|
||||
return encoded
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/* -*- Mode: js; js-indent-level: 2; -*- */
|
||||
/*
|
||||
* Copyright 2011 Mozilla Foundation and contributors
|
||||
* Licensed under the New BSD license. See LICENSE or:
|
||||
* http://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
const intToCharMap =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('')
|
||||
|
||||
/**
|
||||
* Encode an integer in the range of 0 to 63 to a single base 64 digit.
|
||||
*/
|
||||
exports.encode = function (number) {
|
||||
if (0 <= number && number < intToCharMap.length) {
|
||||
return intToCharMap[number]
|
||||
}
|
||||
throw new TypeError('Must be between 0 and 63: ' + number)
|
||||
}
|
||||
Generated
Vendored
+113
@@ -0,0 +1,113 @@
|
||||
/* -*- Mode: js; js-indent-level: 2; -*- */
|
||||
/*
|
||||
* Copyright 2011 Mozilla Foundation and contributors
|
||||
* Licensed under the New BSD license. See LICENSE or:
|
||||
* http://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
exports.GREATEST_LOWER_BOUND = 1
|
||||
exports.LEAST_UPPER_BOUND = 2
|
||||
|
||||
/**
|
||||
* Recursive implementation of binary search.
|
||||
*
|
||||
* @param aLow Indices here and lower do not contain the needle.
|
||||
* @param aHigh Indices here and higher do not contain the needle.
|
||||
* @param aNeedle The element being searched for.
|
||||
* @param aHaystack The non-empty array being searched.
|
||||
* @param aCompare Function which takes two elements and returns -1, 0, or 1.
|
||||
* @param aBias Either 'binarySearch.GREATEST_LOWER_BOUND' or
|
||||
* 'binarySearch.LEAST_UPPER_BOUND'. Specifies whether to return the
|
||||
* closest element that is smaller than or greater than the one we are
|
||||
* searching for, respectively, if the exact element cannot be found.
|
||||
*/
|
||||
function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare, aBias) {
|
||||
// This function terminates when one of the following is true:
|
||||
//
|
||||
// 1. We find the exact element we are looking for.
|
||||
//
|
||||
// 2. We did not find the exact element, but we can return the index of
|
||||
// the next-closest element.
|
||||
//
|
||||
// 3. We did not find the exact element, and there is no next-closest
|
||||
// element than the one we are searching for, so we return -1.
|
||||
const mid = Math.floor((aHigh - aLow) / 2) + aLow
|
||||
const cmp = aCompare(aNeedle, aHaystack[mid], true)
|
||||
if (cmp === 0) {
|
||||
// Found the element we are looking for.
|
||||
return mid
|
||||
} else if (cmp > 0) {
|
||||
// Our needle is greater than aHaystack[mid].
|
||||
if (aHigh - mid > 1) {
|
||||
// The element is in the upper half.
|
||||
return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare, aBias)
|
||||
}
|
||||
|
||||
// The exact needle element was not found in this haystack. Determine if
|
||||
// we are in termination case (3) or (2) and return the appropriate thing.
|
||||
if (aBias == exports.LEAST_UPPER_BOUND) {
|
||||
return aHigh < aHaystack.length ? aHigh : -1
|
||||
}
|
||||
return mid
|
||||
}
|
||||
|
||||
// Our needle is less than aHaystack[mid].
|
||||
if (mid - aLow > 1) {
|
||||
// The element is in the lower half.
|
||||
return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare, aBias)
|
||||
}
|
||||
|
||||
// we are in termination case (3) or (2) and return the appropriate thing.
|
||||
if (aBias == exports.LEAST_UPPER_BOUND) {
|
||||
return mid
|
||||
}
|
||||
return aLow < 0 ? -1 : aLow
|
||||
}
|
||||
|
||||
/**
|
||||
* This is an implementation of binary search which will always try and return
|
||||
* the index of the closest element if there is no exact hit. This is because
|
||||
* mappings between original and generated line/col pairs are single points,
|
||||
* and there is an implicit region between each of them, so a miss just means
|
||||
* that you aren't on the very start of a region.
|
||||
*
|
||||
* @param aNeedle The element you are looking for.
|
||||
* @param aHaystack The array that is being searched.
|
||||
* @param aCompare A function which takes the needle and an element in the
|
||||
* array and returns -1, 0, or 1 depending on whether the needle is less
|
||||
* than, equal to, or greater than the element, respectively.
|
||||
* @param aBias Either 'binarySearch.GREATEST_LOWER_BOUND' or
|
||||
* 'binarySearch.LEAST_UPPER_BOUND'. Specifies whether to return the
|
||||
* closest element that is smaller than or greater than the one we are
|
||||
* searching for, respectively, if the exact element cannot be found.
|
||||
* Defaults to 'binarySearch.GREATEST_LOWER_BOUND'.
|
||||
*/
|
||||
exports.search = function search(aNeedle, aHaystack, aCompare, aBias) {
|
||||
if (aHaystack.length === 0) {
|
||||
return -1
|
||||
}
|
||||
|
||||
let index = recursiveSearch(
|
||||
-1,
|
||||
aHaystack.length,
|
||||
aNeedle,
|
||||
aHaystack,
|
||||
aCompare,
|
||||
aBias || exports.GREATEST_LOWER_BOUND
|
||||
)
|
||||
if (index < 0) {
|
||||
return -1
|
||||
}
|
||||
|
||||
// We have found either the exact element, or the next-closest element than
|
||||
// the one we are searching for. However, there may be more than one such
|
||||
// element. Make sure we always return the smallest of these.
|
||||
while (index - 1 >= 0) {
|
||||
if (aCompare(aHaystack[index], aHaystack[index - 1], true) !== 0) {
|
||||
break
|
||||
}
|
||||
--index
|
||||
}
|
||||
|
||||
return index
|
||||
}
|
||||
Generated
Vendored
+83
@@ -0,0 +1,83 @@
|
||||
/* -*- Mode: js; js-indent-level: 2; -*- */
|
||||
/*
|
||||
* Copyright 2014 Mozilla Foundation and contributors
|
||||
* Licensed under the New BSD license. See LICENSE or:
|
||||
* http://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
const util = require('./util')
|
||||
|
||||
/**
|
||||
* Determine whether mappingB is after mappingA with respect to generated
|
||||
* position.
|
||||
*/
|
||||
function generatedPositionAfter(mappingA, mappingB) {
|
||||
// Optimized for most common case
|
||||
const lineA = mappingA.generatedLine
|
||||
const lineB = mappingB.generatedLine
|
||||
const columnA = mappingA.generatedColumn
|
||||
const columnB = mappingB.generatedColumn
|
||||
return (
|
||||
lineB > lineA ||
|
||||
(lineB == lineA && columnB >= columnA) ||
|
||||
util.compareByGeneratedPositionsInflated(mappingA, mappingB) <= 0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A data structure to provide a sorted view of accumulated mappings in a
|
||||
* performance conscious manner. It trades a negligible overhead in general
|
||||
* case for a large speedup in case of mappings being added in order.
|
||||
*/
|
||||
class MappingList {
|
||||
constructor() {
|
||||
this._array = []
|
||||
this._sorted = true
|
||||
// Serves as infimum
|
||||
this._last = { generatedLine: -1, generatedColumn: 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate through internal items. This method takes the same arguments that
|
||||
* `Array.prototype.forEach` takes.
|
||||
*
|
||||
* NOTE: The order of the mappings is NOT guaranteed.
|
||||
*/
|
||||
unsortedForEach(aCallback, aThisArg) {
|
||||
this._array.forEach(aCallback, aThisArg)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given source mapping.
|
||||
*
|
||||
* @param Object aMapping
|
||||
*/
|
||||
add(aMapping) {
|
||||
if (generatedPositionAfter(this._last, aMapping)) {
|
||||
this._last = aMapping
|
||||
this._array.push(aMapping)
|
||||
} else {
|
||||
this._sorted = false
|
||||
this._array.push(aMapping)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the flat, sorted array of mappings. The mappings are sorted by
|
||||
* generated position.
|
||||
*
|
||||
* WARNING: This method returns internal data without copying, for
|
||||
* performance. The return value must NOT be mutated, and should be treated as
|
||||
* an immutable borrow. If you want to take ownership, you must make your own
|
||||
* copy.
|
||||
*/
|
||||
toArray() {
|
||||
if (!this._sorted) {
|
||||
this._array.sort(util.compareByGeneratedPositionsInflated)
|
||||
this._sorted = true
|
||||
}
|
||||
return this._array
|
||||
}
|
||||
}
|
||||
|
||||
exports.MappingList = MappingList
|
||||
Generated
Vendored
BIN
Binary file not shown.
Generated
Vendored
+51
@@ -0,0 +1,51 @@
|
||||
/* Determine browser vs node environment by testing the default top level context. Solution courtesy of: https://stackoverflow.com/questions/17575790/environment-detection-node-js-or-browser */
|
||||
if (__PLATFORM_WEB__) {
|
||||
// Web version of reading a wasm file into an array buffer.
|
||||
|
||||
let mappingsWasm = null
|
||||
|
||||
module.exports = function readWasm() {
|
||||
if (typeof mappingsWasm === 'string') {
|
||||
return fetch(mappingsWasm).then((response) => response.arrayBuffer())
|
||||
}
|
||||
if (mappingsWasm instanceof ArrayBuffer) {
|
||||
return Promise.resolve(mappingsWasm)
|
||||
}
|
||||
throw new Error(
|
||||
'You must provide the string URL or ArrayBuffer contents ' +
|
||||
'of lib/mappings.wasm by calling ' +
|
||||
"SourceMapConsumer.initialize({ 'lib/mappings.wasm': ... }) " +
|
||||
'before using SourceMapConsumer'
|
||||
)
|
||||
}
|
||||
|
||||
module.exports.initialize = (input) => (mappingsWasm = input)
|
||||
} else {
|
||||
// Node version of reading a wasm file into an array buffer.
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
module.exports = function readWasm() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const wasmPath = path.join(
|
||||
__dirname,
|
||||
'../lib/source-map/lib',
|
||||
'mappings.wasm'
|
||||
)
|
||||
fs.readFile(wasmPath, null, (error, data) => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
return
|
||||
}
|
||||
|
||||
resolve(data.buffer)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
module.exports.initialize = (_) => {
|
||||
console.debug(
|
||||
'SourceMapConsumer.initialize is a no-op when running in node.js'
|
||||
)
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+1302
File diff suppressed because it is too large
Load Diff
Generated
Vendored
+439
@@ -0,0 +1,439 @@
|
||||
/* -*- Mode: js; js-indent-level: 2; -*- */
|
||||
/*
|
||||
* Copyright 2011 Mozilla Foundation and contributors
|
||||
* Licensed under the New BSD license. See LICENSE or:
|
||||
* http://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
const base64VLQ = require('./base64-vlq')
|
||||
const util = require('./util')
|
||||
const ArraySet = require('./array-set').ArraySet
|
||||
const MappingList = require('./mapping-list').MappingList
|
||||
|
||||
/**
|
||||
* An instance of the SourceMapGenerator represents a source map which is
|
||||
* being built incrementally. You may pass an object with the following
|
||||
* properties:
|
||||
*
|
||||
* - file: The filename of the generated source.
|
||||
* - sourceRoot: A root for all relative URLs in this source map.
|
||||
*/
|
||||
class SourceMapGenerator {
|
||||
constructor(aArgs) {
|
||||
if (!aArgs) {
|
||||
aArgs = {}
|
||||
}
|
||||
this._file = util.getArg(aArgs, 'file', null)
|
||||
this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null)
|
||||
this._skipValidation = util.getArg(aArgs, 'skipValidation', false)
|
||||
this._sources = new ArraySet()
|
||||
this._names = new ArraySet()
|
||||
this._mappings = new MappingList()
|
||||
this._sourcesContents = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SourceMapGenerator based on a SourceMapConsumer
|
||||
*
|
||||
* @param aSourceMapConsumer The SourceMap.
|
||||
*/
|
||||
static fromSourceMap(aSourceMapConsumer) {
|
||||
const sourceRoot = aSourceMapConsumer.sourceRoot
|
||||
const generator = new SourceMapGenerator({
|
||||
file: aSourceMapConsumer.file,
|
||||
sourceRoot,
|
||||
})
|
||||
aSourceMapConsumer.eachMapping(function (mapping) {
|
||||
const newMapping = {
|
||||
generated: {
|
||||
line: mapping.generatedLine,
|
||||
column: mapping.generatedColumn,
|
||||
},
|
||||
}
|
||||
|
||||
if (mapping.source != null) {
|
||||
newMapping.source = mapping.source
|
||||
if (sourceRoot != null) {
|
||||
newMapping.source = util.relative(sourceRoot, newMapping.source)
|
||||
}
|
||||
|
||||
newMapping.original = {
|
||||
line: mapping.originalLine,
|
||||
column: mapping.originalColumn,
|
||||
}
|
||||
|
||||
if (mapping.name != null) {
|
||||
newMapping.name = mapping.name
|
||||
}
|
||||
}
|
||||
|
||||
generator.addMapping(newMapping)
|
||||
})
|
||||
aSourceMapConsumer.sources.forEach(function (sourceFile) {
|
||||
let sourceRelative = sourceFile
|
||||
if (sourceRoot !== null) {
|
||||
sourceRelative = util.relative(sourceRoot, sourceFile)
|
||||
}
|
||||
|
||||
if (!generator._sources.has(sourceRelative)) {
|
||||
generator._sources.add(sourceRelative)
|
||||
}
|
||||
|
||||
const content = aSourceMapConsumer.sourceContentFor(sourceFile)
|
||||
if (content != null) {
|
||||
generator.setSourceContent(sourceFile, content)
|
||||
}
|
||||
})
|
||||
return generator
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single mapping from original source line and column to the generated
|
||||
* source's line and column for this source map being created. The mapping
|
||||
* object should have the following properties:
|
||||
*
|
||||
* - generated: An object with the generated line and column positions.
|
||||
* - original: An object with the original line and column positions.
|
||||
* - source: The original source file (relative to the sourceRoot).
|
||||
* - name: An optional original token name for this mapping.
|
||||
*/
|
||||
addMapping(aArgs) {
|
||||
const generated = util.getArg(aArgs, 'generated')
|
||||
const original = util.getArg(aArgs, 'original', null)
|
||||
let source = util.getArg(aArgs, 'source', null)
|
||||
let name = util.getArg(aArgs, 'name', null)
|
||||
|
||||
if (!this._skipValidation) {
|
||||
this._validateMapping(generated, original, source, name)
|
||||
}
|
||||
|
||||
if (source != null) {
|
||||
source = String(source)
|
||||
if (!this._sources.has(source)) {
|
||||
this._sources.add(source)
|
||||
}
|
||||
}
|
||||
|
||||
if (name != null) {
|
||||
name = String(name)
|
||||
if (!this._names.has(name)) {
|
||||
this._names.add(name)
|
||||
}
|
||||
}
|
||||
|
||||
this._mappings.add({
|
||||
generatedLine: generated.line,
|
||||
generatedColumn: generated.column,
|
||||
originalLine: original != null && original.line,
|
||||
originalColumn: original != null && original.column,
|
||||
source,
|
||||
name,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the source content for a source file.
|
||||
*/
|
||||
setSourceContent(aSourceFile, aSourceContent) {
|
||||
let source = aSourceFile
|
||||
if (this._sourceRoot != null) {
|
||||
source = util.relative(this._sourceRoot, source)
|
||||
}
|
||||
|
||||
if (aSourceContent != null) {
|
||||
// Add the source content to the _sourcesContents map.
|
||||
// Create a new _sourcesContents map if the property is null.
|
||||
if (!this._sourcesContents) {
|
||||
this._sourcesContents = Object.create(null)
|
||||
}
|
||||
this._sourcesContents[util.toSetString(source)] = aSourceContent
|
||||
} else if (this._sourcesContents) {
|
||||
// Remove the source file from the _sourcesContents map.
|
||||
// If the _sourcesContents map is empty, set the property to null.
|
||||
delete this._sourcesContents[util.toSetString(source)]
|
||||
if (Object.keys(this._sourcesContents).length === 0) {
|
||||
this._sourcesContents = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the mappings of a sub-source-map for a specific source file to the
|
||||
* source map being generated. Each mapping to the supplied source file is
|
||||
* rewritten using the supplied source map. Note: The resolution for the
|
||||
* resulting mappings is the minimium of this map and the supplied map.
|
||||
*
|
||||
* @param aSourceMapConsumer The source map to be applied.
|
||||
* @param aSourceFile Optional. The filename of the source file.
|
||||
* If omitted, SourceMapConsumer's file property will be used.
|
||||
* @param aSourceMapPath Optional. The dirname of the path to the source map
|
||||
* to be applied. If relative, it is relative to the SourceMapConsumer.
|
||||
* This parameter is needed when the two source maps aren't in the same
|
||||
* directory, and the source map to be applied contains relative source
|
||||
* paths. If so, those relative source paths need to be rewritten
|
||||
* relative to the SourceMapGenerator.
|
||||
*/
|
||||
applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) {
|
||||
let sourceFile = aSourceFile
|
||||
// If aSourceFile is omitted, we will use the file property of the SourceMap
|
||||
if (aSourceFile == null) {
|
||||
if (aSourceMapConsumer.file == null) {
|
||||
throw new Error(
|
||||
'SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, ' +
|
||||
'or the source map\'s "file" property. Both were omitted.'
|
||||
)
|
||||
}
|
||||
sourceFile = aSourceMapConsumer.file
|
||||
}
|
||||
const sourceRoot = this._sourceRoot
|
||||
// Make "sourceFile" relative if an absolute Url is passed.
|
||||
if (sourceRoot != null) {
|
||||
sourceFile = util.relative(sourceRoot, sourceFile)
|
||||
}
|
||||
// Applying the SourceMap can add and remove items from the sources and
|
||||
// the names array.
|
||||
const newSources =
|
||||
this._mappings.toArray().length > 0 ? new ArraySet() : this._sources
|
||||
const newNames = new ArraySet()
|
||||
|
||||
// Find mappings for the "sourceFile"
|
||||
this._mappings.unsortedForEach(function (mapping) {
|
||||
if (mapping.source === sourceFile && mapping.originalLine != null) {
|
||||
// Check if it can be mapped by the source map, then update the mapping.
|
||||
const original = aSourceMapConsumer.originalPositionFor({
|
||||
line: mapping.originalLine,
|
||||
column: mapping.originalColumn,
|
||||
})
|
||||
if (original.source != null) {
|
||||
// Copy mapping
|
||||
mapping.source = original.source
|
||||
if (aSourceMapPath != null) {
|
||||
mapping.source = util.join(aSourceMapPath, mapping.source)
|
||||
}
|
||||
if (sourceRoot != null) {
|
||||
mapping.source = util.relative(sourceRoot, mapping.source)
|
||||
}
|
||||
mapping.originalLine = original.line
|
||||
mapping.originalColumn = original.column
|
||||
if (original.name != null) {
|
||||
mapping.name = original.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const source = mapping.source
|
||||
if (source != null && !newSources.has(source)) {
|
||||
newSources.add(source)
|
||||
}
|
||||
|
||||
const name = mapping.name
|
||||
if (name != null && !newNames.has(name)) {
|
||||
newNames.add(name)
|
||||
}
|
||||
}, this)
|
||||
this._sources = newSources
|
||||
this._names = newNames
|
||||
|
||||
// Copy sourcesContents of applied map.
|
||||
aSourceMapConsumer.sources.forEach(function (srcFile) {
|
||||
const content = aSourceMapConsumer.sourceContentFor(srcFile)
|
||||
if (content != null) {
|
||||
if (aSourceMapPath != null) {
|
||||
srcFile = util.join(aSourceMapPath, srcFile)
|
||||
}
|
||||
if (sourceRoot != null) {
|
||||
srcFile = util.relative(sourceRoot, srcFile)
|
||||
}
|
||||
this.setSourceContent(srcFile, content)
|
||||
}
|
||||
}, this)
|
||||
}
|
||||
|
||||
/**
|
||||
* A mapping can have one of the three levels of data:
|
||||
*
|
||||
* 1. Just the generated position.
|
||||
* 2. The Generated position, original position, and original source.
|
||||
* 3. Generated and original position, original source, as well as a name
|
||||
* token.
|
||||
*
|
||||
* To maintain consistency, we validate that any new mapping being added falls
|
||||
* in to one of these categories.
|
||||
*/
|
||||
_validateMapping(aGenerated, aOriginal, aSource, aName) {
|
||||
// When aOriginal is truthy but has empty values for .line and .column,
|
||||
// it is most likely a programmer error. In this case we throw a very
|
||||
// specific error message to try to guide them the right way.
|
||||
// For example: https://github.com/Polymer/polymer-bundler/pull/519
|
||||
if (
|
||||
aOriginal &&
|
||||
typeof aOriginal.line !== 'number' &&
|
||||
typeof aOriginal.column !== 'number'
|
||||
) {
|
||||
throw new Error(
|
||||
'original.line and original.column are not numbers -- you probably meant to omit ' +
|
||||
'the original mapping entirely and only map the generated position. If so, pass ' +
|
||||
'null for the original mapping instead of an object with empty or null values.'
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
aGenerated &&
|
||||
'line' in aGenerated &&
|
||||
'column' in aGenerated &&
|
||||
aGenerated.line > 0 &&
|
||||
aGenerated.column >= 0 &&
|
||||
!aOriginal &&
|
||||
!aSource &&
|
||||
!aName
|
||||
) {
|
||||
// Case 1.
|
||||
} else if (
|
||||
aGenerated &&
|
||||
'line' in aGenerated &&
|
||||
'column' in aGenerated &&
|
||||
aOriginal &&
|
||||
'line' in aOriginal &&
|
||||
'column' in aOriginal &&
|
||||
aGenerated.line > 0 &&
|
||||
aGenerated.column >= 0 &&
|
||||
aOriginal.line > 0 &&
|
||||
aOriginal.column >= 0 &&
|
||||
aSource
|
||||
) {
|
||||
// Cases 2 and 3.
|
||||
} else {
|
||||
throw new Error(
|
||||
'Invalid mapping: ' +
|
||||
JSON.stringify({
|
||||
generated: aGenerated,
|
||||
source: aSource,
|
||||
original: aOriginal,
|
||||
name: aName,
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the accumulated mappings in to the stream of base 64 VLQs
|
||||
* specified by the source map format.
|
||||
*/
|
||||
_serializeMappings() {
|
||||
let previousGeneratedColumn = 0
|
||||
let previousGeneratedLine = 1
|
||||
let previousOriginalColumn = 0
|
||||
let previousOriginalLine = 0
|
||||
let previousName = 0
|
||||
let previousSource = 0
|
||||
let result = ''
|
||||
let next
|
||||
let mapping
|
||||
let nameIdx
|
||||
let sourceIdx
|
||||
|
||||
const mappings = this._mappings.toArray()
|
||||
for (let i = 0, len = mappings.length; i < len; i++) {
|
||||
mapping = mappings[i]
|
||||
next = ''
|
||||
|
||||
if (mapping.generatedLine !== previousGeneratedLine) {
|
||||
previousGeneratedColumn = 0
|
||||
while (mapping.generatedLine !== previousGeneratedLine) {
|
||||
next += ';'
|
||||
previousGeneratedLine++
|
||||
}
|
||||
} else if (i > 0) {
|
||||
if (
|
||||
!util.compareByGeneratedPositionsInflated(mapping, mappings[i - 1])
|
||||
) {
|
||||
continue
|
||||
}
|
||||
next += ','
|
||||
}
|
||||
|
||||
next += base64VLQ.encode(
|
||||
mapping.generatedColumn - previousGeneratedColumn
|
||||
)
|
||||
previousGeneratedColumn = mapping.generatedColumn
|
||||
|
||||
if (mapping.source != null) {
|
||||
sourceIdx = this._sources.indexOf(mapping.source)
|
||||
next += base64VLQ.encode(sourceIdx - previousSource)
|
||||
previousSource = sourceIdx
|
||||
|
||||
// lines are stored 0-based in SourceMap spec version 3
|
||||
next += base64VLQ.encode(
|
||||
mapping.originalLine - 1 - previousOriginalLine
|
||||
)
|
||||
previousOriginalLine = mapping.originalLine - 1
|
||||
|
||||
next += base64VLQ.encode(
|
||||
mapping.originalColumn - previousOriginalColumn
|
||||
)
|
||||
previousOriginalColumn = mapping.originalColumn
|
||||
|
||||
if (mapping.name != null) {
|
||||
nameIdx = this._names.indexOf(mapping.name)
|
||||
next += base64VLQ.encode(nameIdx - previousName)
|
||||
previousName = nameIdx
|
||||
}
|
||||
}
|
||||
|
||||
result += next
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
_generateSourcesContent(aSources, aSourceRoot) {
|
||||
return aSources.map(function (source) {
|
||||
if (!this._sourcesContents) {
|
||||
return null
|
||||
}
|
||||
if (aSourceRoot != null) {
|
||||
source = util.relative(aSourceRoot, source)
|
||||
}
|
||||
const key = util.toSetString(source)
|
||||
return Object.prototype.hasOwnProperty.call(this._sourcesContents, key)
|
||||
? this._sourcesContents[key]
|
||||
: null
|
||||
}, this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Externalize the source map.
|
||||
*/
|
||||
toJSON() {
|
||||
const map = {
|
||||
version: this._version,
|
||||
sources: this._sources.toArray(),
|
||||
names: this._names.toArray(),
|
||||
mappings: this._serializeMappings(),
|
||||
}
|
||||
if (this._file != null) {
|
||||
map.file = this._file
|
||||
}
|
||||
if (this._sourceRoot != null) {
|
||||
map.sourceRoot = this._sourceRoot
|
||||
}
|
||||
if (this._sourcesContents) {
|
||||
map.sourcesContent = this._generateSourcesContent(
|
||||
map.sources,
|
||||
map.sourceRoot
|
||||
)
|
||||
}
|
||||
|
||||
return map
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the source map being generated to a string.
|
||||
*/
|
||||
toString() {
|
||||
return JSON.stringify(this.toJSON())
|
||||
}
|
||||
}
|
||||
|
||||
SourceMapGenerator.prototype._version = 3
|
||||
exports.SourceMapGenerator = SourceMapGenerator
|
||||
Generated
Vendored
+430
@@ -0,0 +1,430 @@
|
||||
/* -*- Mode: js; js-indent-level: 2; -*- */
|
||||
/*
|
||||
* Copyright 2011 Mozilla Foundation and contributors
|
||||
* Licensed under the New BSD license. See LICENSE or:
|
||||
* http://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
const SourceMapGenerator = require('./source-map-generator').SourceMapGenerator
|
||||
const util = require('./util')
|
||||
|
||||
// Matches a Windows-style `\r\n` newline or a `\n` newline used by all other
|
||||
// operating systems these days (capturing the result).
|
||||
const REGEX_NEWLINE = /(\r?\n)/
|
||||
|
||||
// Newline character code for charCodeAt() comparisons
|
||||
const NEWLINE_CODE = 10
|
||||
|
||||
// Private symbol for identifying `SourceNode`s when multiple versions of
|
||||
// the source-map library are loaded. This MUST NOT CHANGE across
|
||||
// versions!
|
||||
const isSourceNode = '$$$isSourceNode$$$'
|
||||
|
||||
/**
|
||||
* SourceNodes provide a way to abstract over interpolating/concatenating
|
||||
* snippets of generated JavaScript source code while maintaining the line and
|
||||
* column information associated with the original source code.
|
||||
*
|
||||
* @param aLine The original line number.
|
||||
* @param aColumn The original column number.
|
||||
* @param aSource The original source's filename.
|
||||
* @param aChunks Optional. An array of strings which are snippets of
|
||||
* generated JS, or other SourceNodes.
|
||||
* @param aName The original identifier.
|
||||
*/
|
||||
class SourceNode {
|
||||
constructor(aLine, aColumn, aSource, aChunks, aName) {
|
||||
this.children = []
|
||||
this.sourceContents = {}
|
||||
this.line = aLine == null ? null : aLine
|
||||
this.column = aColumn == null ? null : aColumn
|
||||
this.source = aSource == null ? null : aSource
|
||||
this.name = aName == null ? null : aName
|
||||
this[isSourceNode] = true
|
||||
if (aChunks != null) this.add(aChunks)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SourceNode from generated code and a SourceMapConsumer.
|
||||
*
|
||||
* @param aGeneratedCode The generated code
|
||||
* @param aSourceMapConsumer The SourceMap for the generated code
|
||||
* @param aRelativePath Optional. The path that relative sources in the
|
||||
* SourceMapConsumer should be relative to.
|
||||
*/
|
||||
static fromStringWithSourceMap(
|
||||
aGeneratedCode,
|
||||
aSourceMapConsumer,
|
||||
aRelativePath
|
||||
) {
|
||||
// The SourceNode we want to fill with the generated code
|
||||
// and the SourceMap
|
||||
const node = new SourceNode()
|
||||
|
||||
// All even indices of this array are one line of the generated code,
|
||||
// while all odd indices are the newlines between two adjacent lines
|
||||
// (since `REGEX_NEWLINE` captures its match).
|
||||
// Processed fragments are accessed by calling `shiftNextLine`.
|
||||
const remainingLines = aGeneratedCode.split(REGEX_NEWLINE)
|
||||
let remainingLinesIndex = 0
|
||||
const shiftNextLine = function () {
|
||||
const lineContents = getNextLine()
|
||||
// The last line of a file might not have a newline.
|
||||
const newLine = getNextLine() || ''
|
||||
return lineContents + newLine
|
||||
|
||||
function getNextLine() {
|
||||
return remainingLinesIndex < remainingLines.length
|
||||
? remainingLines[remainingLinesIndex++]
|
||||
: undefined
|
||||
}
|
||||
}
|
||||
|
||||
// We need to remember the position of "remainingLines"
|
||||
let lastGeneratedLine = 1,
|
||||
lastGeneratedColumn = 0
|
||||
|
||||
// The generate SourceNodes we need a code range.
|
||||
// To extract it current and last mapping is used.
|
||||
// Here we store the last mapping.
|
||||
let lastMapping = null
|
||||
let nextLine
|
||||
|
||||
aSourceMapConsumer.eachMapping(function (mapping) {
|
||||
if (lastMapping !== null) {
|
||||
// We add the code from "lastMapping" to "mapping":
|
||||
// First check if there is a new line in between.
|
||||
if (lastGeneratedLine < mapping.generatedLine) {
|
||||
// Associate first line with "lastMapping"
|
||||
addMappingWithCode(lastMapping, shiftNextLine())
|
||||
lastGeneratedLine++
|
||||
lastGeneratedColumn = 0
|
||||
// The remaining code is added without mapping
|
||||
} else {
|
||||
// There is no new line in between.
|
||||
// Associate the code between "lastGeneratedColumn" and
|
||||
// "mapping.generatedColumn" with "lastMapping"
|
||||
nextLine = remainingLines[remainingLinesIndex] || ''
|
||||
const code = nextLine.substr(
|
||||
0,
|
||||
mapping.generatedColumn - lastGeneratedColumn
|
||||
)
|
||||
remainingLines[remainingLinesIndex] = nextLine.substr(
|
||||
mapping.generatedColumn - lastGeneratedColumn
|
||||
)
|
||||
lastGeneratedColumn = mapping.generatedColumn
|
||||
addMappingWithCode(lastMapping, code)
|
||||
// No more remaining code, continue
|
||||
lastMapping = mapping
|
||||
return
|
||||
}
|
||||
}
|
||||
// We add the generated code until the first mapping
|
||||
// to the SourceNode without any mapping.
|
||||
// Each line is added as separate string.
|
||||
while (lastGeneratedLine < mapping.generatedLine) {
|
||||
node.add(shiftNextLine())
|
||||
lastGeneratedLine++
|
||||
}
|
||||
if (lastGeneratedColumn < mapping.generatedColumn) {
|
||||
nextLine = remainingLines[remainingLinesIndex] || ''
|
||||
node.add(nextLine.substr(0, mapping.generatedColumn))
|
||||
remainingLines[remainingLinesIndex] = nextLine.substr(
|
||||
mapping.generatedColumn
|
||||
)
|
||||
lastGeneratedColumn = mapping.generatedColumn
|
||||
}
|
||||
lastMapping = mapping
|
||||
}, this)
|
||||
// We have processed all mappings.
|
||||
if (remainingLinesIndex < remainingLines.length) {
|
||||
if (lastMapping) {
|
||||
// Associate the remaining code in the current line with "lastMapping"
|
||||
addMappingWithCode(lastMapping, shiftNextLine())
|
||||
}
|
||||
// and add the remaining lines without any mapping
|
||||
node.add(remainingLines.splice(remainingLinesIndex).join(''))
|
||||
}
|
||||
|
||||
// Copy sourcesContent into SourceNode
|
||||
aSourceMapConsumer.sources.forEach(function (sourceFile) {
|
||||
const content = aSourceMapConsumer.sourceContentFor(sourceFile)
|
||||
if (content != null) {
|
||||
if (aRelativePath != null) {
|
||||
sourceFile = util.join(aRelativePath, sourceFile)
|
||||
}
|
||||
node.setSourceContent(sourceFile, content)
|
||||
}
|
||||
})
|
||||
|
||||
return node
|
||||
|
||||
function addMappingWithCode(mapping, code) {
|
||||
if (mapping === null || mapping.source === undefined) {
|
||||
node.add(code)
|
||||
} else {
|
||||
const source = aRelativePath
|
||||
? util.join(aRelativePath, mapping.source)
|
||||
: mapping.source
|
||||
node.add(
|
||||
new SourceNode(
|
||||
mapping.originalLine,
|
||||
mapping.originalColumn,
|
||||
source,
|
||||
code,
|
||||
mapping.name
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a chunk of generated JS to this source node.
|
||||
*
|
||||
* @param aChunk A string snippet of generated JS code, another instance of
|
||||
* SourceNode, or an array where each member is one of those things.
|
||||
*/
|
||||
add(aChunk) {
|
||||
if (Array.isArray(aChunk)) {
|
||||
aChunk.forEach(function (chunk) {
|
||||
this.add(chunk)
|
||||
}, this)
|
||||
} else if (aChunk[isSourceNode] || typeof aChunk === 'string') {
|
||||
if (aChunk) {
|
||||
this.children.push(aChunk)
|
||||
}
|
||||
} else {
|
||||
throw new TypeError(
|
||||
'Expected a SourceNode, string, or an array of SourceNodes and strings. Got ' +
|
||||
aChunk
|
||||
)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a chunk of generated JS to the beginning of this source node.
|
||||
*
|
||||
* @param aChunk A string snippet of generated JS code, another instance of
|
||||
* SourceNode, or an array where each member is one of those things.
|
||||
*/
|
||||
prepend(aChunk) {
|
||||
if (Array.isArray(aChunk)) {
|
||||
for (let i = aChunk.length - 1; i >= 0; i--) {
|
||||
this.prepend(aChunk[i])
|
||||
}
|
||||
} else if (aChunk[isSourceNode] || typeof aChunk === 'string') {
|
||||
this.children.unshift(aChunk)
|
||||
} else {
|
||||
throw new TypeError(
|
||||
'Expected a SourceNode, string, or an array of SourceNodes and strings. Got ' +
|
||||
aChunk
|
||||
)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk over the tree of JS snippets in this node and its children. The
|
||||
* walking function is called once for each snippet of JS and is passed that
|
||||
* snippet and the its original associated source's line/column location.
|
||||
*
|
||||
* @param aFn The traversal function.
|
||||
*/
|
||||
walk(aFn) {
|
||||
let chunk
|
||||
for (let i = 0, len = this.children.length; i < len; i++) {
|
||||
chunk = this.children[i]
|
||||
if (chunk[isSourceNode]) {
|
||||
chunk.walk(aFn)
|
||||
} else if (chunk !== '') {
|
||||
aFn(chunk, {
|
||||
source: this.source,
|
||||
line: this.line,
|
||||
column: this.column,
|
||||
name: this.name,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between
|
||||
* each of `this.children`.
|
||||
*
|
||||
* @param aSep The separator.
|
||||
*/
|
||||
join(aSep) {
|
||||
let newChildren
|
||||
let i
|
||||
const len = this.children.length
|
||||
if (len > 0) {
|
||||
newChildren = []
|
||||
for (i = 0; i < len - 1; i++) {
|
||||
newChildren.push(this.children[i])
|
||||
newChildren.push(aSep)
|
||||
}
|
||||
newChildren.push(this.children[i])
|
||||
this.children = newChildren
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Call String.prototype.replace on the very right-most source snippet. Useful
|
||||
* for trimming whitespace from the end of a source node, etc.
|
||||
*
|
||||
* @param aPattern The pattern to replace.
|
||||
* @param aReplacement The thing to replace the pattern with.
|
||||
*/
|
||||
replaceRight(aPattern, aReplacement) {
|
||||
const lastChild = this.children[this.children.length - 1]
|
||||
if (lastChild[isSourceNode]) {
|
||||
lastChild.replaceRight(aPattern, aReplacement)
|
||||
} else if (typeof lastChild === 'string') {
|
||||
this.children[this.children.length - 1] = lastChild.replace(
|
||||
aPattern,
|
||||
aReplacement
|
||||
)
|
||||
} else {
|
||||
this.children.push(''.replace(aPattern, aReplacement))
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the source content for a source file. This will be added to the SourceMapGenerator
|
||||
* in the sourcesContent field.
|
||||
*
|
||||
* @param aSourceFile The filename of the source file
|
||||
* @param aSourceContent The content of the source file
|
||||
*/
|
||||
setSourceContent(aSourceFile, aSourceContent) {
|
||||
this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk over the tree of SourceNodes. The walking function is called for each
|
||||
* source file content and is passed the filename and source content.
|
||||
*
|
||||
* @param aFn The traversal function.
|
||||
*/
|
||||
walkSourceContents(aFn) {
|
||||
for (let i = 0, len = this.children.length; i < len; i++) {
|
||||
if (this.children[i][isSourceNode]) {
|
||||
this.children[i].walkSourceContents(aFn)
|
||||
}
|
||||
}
|
||||
|
||||
const sources = Object.keys(this.sourceContents)
|
||||
for (let i = 0, len = sources.length; i < len; i++) {
|
||||
aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the string representation of this source node. Walks over the tree
|
||||
* and concatenates all the various snippets together to one string.
|
||||
*/
|
||||
toString() {
|
||||
let str = ''
|
||||
this.walk(function (chunk) {
|
||||
str += chunk
|
||||
})
|
||||
return str
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of this source node along with a source
|
||||
* map.
|
||||
*/
|
||||
toStringWithSourceMap(aArgs) {
|
||||
const generated = {
|
||||
code: '',
|
||||
line: 1,
|
||||
column: 0,
|
||||
}
|
||||
const map = new SourceMapGenerator(aArgs)
|
||||
let sourceMappingActive = false
|
||||
let lastOriginalSource = null
|
||||
let lastOriginalLine = null
|
||||
let lastOriginalColumn = null
|
||||
let lastOriginalName = null
|
||||
this.walk(function (chunk, original) {
|
||||
generated.code += chunk
|
||||
if (
|
||||
original.source !== null &&
|
||||
original.line !== null &&
|
||||
original.column !== null
|
||||
) {
|
||||
if (
|
||||
lastOriginalSource !== original.source ||
|
||||
lastOriginalLine !== original.line ||
|
||||
lastOriginalColumn !== original.column ||
|
||||
lastOriginalName !== original.name
|
||||
) {
|
||||
map.addMapping({
|
||||
source: original.source,
|
||||
original: {
|
||||
line: original.line,
|
||||
column: original.column,
|
||||
},
|
||||
generated: {
|
||||
line: generated.line,
|
||||
column: generated.column,
|
||||
},
|
||||
name: original.name,
|
||||
})
|
||||
}
|
||||
lastOriginalSource = original.source
|
||||
lastOriginalLine = original.line
|
||||
lastOriginalColumn = original.column
|
||||
lastOriginalName = original.name
|
||||
sourceMappingActive = true
|
||||
} else if (sourceMappingActive) {
|
||||
map.addMapping({
|
||||
generated: {
|
||||
line: generated.line,
|
||||
column: generated.column,
|
||||
},
|
||||
})
|
||||
lastOriginalSource = null
|
||||
sourceMappingActive = false
|
||||
}
|
||||
for (let idx = 0, length = chunk.length; idx < length; idx++) {
|
||||
if (chunk.charCodeAt(idx) === NEWLINE_CODE) {
|
||||
generated.line++
|
||||
generated.column = 0
|
||||
// Mappings end at eol
|
||||
if (idx + 1 === length) {
|
||||
lastOriginalSource = null
|
||||
sourceMappingActive = false
|
||||
} else if (sourceMappingActive) {
|
||||
map.addMapping({
|
||||
source: original.source,
|
||||
original: {
|
||||
line: original.line,
|
||||
column: original.column,
|
||||
},
|
||||
generated: {
|
||||
line: generated.line,
|
||||
column: generated.column,
|
||||
},
|
||||
name: original.name,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
generated.column++
|
||||
}
|
||||
}
|
||||
})
|
||||
this.walkSourceContents(function (sourceFile, sourceContent) {
|
||||
map.setSourceContent(sourceFile, sourceContent)
|
||||
})
|
||||
|
||||
return { code: generated.code, map }
|
||||
}
|
||||
}
|
||||
|
||||
exports.SourceNode = SourceNode
|
||||
+555
@@ -0,0 +1,555 @@
|
||||
/* -*- Mode: js; js-indent-level: 2; -*- */
|
||||
/*
|
||||
* Copyright 2011 Mozilla Foundation and contributors
|
||||
* Licensed under the New BSD license. See LICENSE or:
|
||||
* http://opensource.org/licenses/BSD-3-Clause
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is a helper function for getting values from parameter/options
|
||||
* objects.
|
||||
*
|
||||
* @param args The object we are extracting values from
|
||||
* @param name The name of the property we are getting.
|
||||
* @param defaultValue An optional value to return if the property is missing
|
||||
* from the object. If this is not specified and the property is missing, an
|
||||
* error will be thrown.
|
||||
*/
|
||||
function getArg(aArgs, aName, aDefaultValue) {
|
||||
if (aName in aArgs) {
|
||||
return aArgs[aName]
|
||||
} else if (arguments.length === 3) {
|
||||
return aDefaultValue
|
||||
}
|
||||
throw new Error('"' + aName + '" is a required argument.')
|
||||
}
|
||||
exports.getArg = getArg
|
||||
|
||||
const urlRegexp =
|
||||
/^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/
|
||||
const dataUrlRegexp = /^data:.+\,.+$/
|
||||
|
||||
function urlParse(aUrl) {
|
||||
const match = aUrl.match(urlRegexp)
|
||||
if (!match) {
|
||||
return null
|
||||
}
|
||||
return {
|
||||
scheme: match[1],
|
||||
auth: match[2],
|
||||
host: match[3],
|
||||
port: match[4],
|
||||
path: match[5],
|
||||
}
|
||||
}
|
||||
exports.urlParse = urlParse
|
||||
|
||||
function urlGenerate(aParsedUrl) {
|
||||
let url = ''
|
||||
if (aParsedUrl.scheme) {
|
||||
url += aParsedUrl.scheme + ':'
|
||||
}
|
||||
url += '//'
|
||||
if (aParsedUrl.auth) {
|
||||
url += aParsedUrl.auth + '@'
|
||||
}
|
||||
if (aParsedUrl.host) {
|
||||
url += aParsedUrl.host
|
||||
}
|
||||
if (aParsedUrl.port) {
|
||||
url += ':' + aParsedUrl.port
|
||||
}
|
||||
if (aParsedUrl.path) {
|
||||
url += aParsedUrl.path
|
||||
}
|
||||
return url
|
||||
}
|
||||
exports.urlGenerate = urlGenerate
|
||||
|
||||
const MAX_CACHED_INPUTS = 32
|
||||
|
||||
/**
|
||||
* Takes some function `f(input) -> result` and returns a memoized version of
|
||||
* `f`.
|
||||
*
|
||||
* We keep at most `MAX_CACHED_INPUTS` memoized results of `f` alive. The
|
||||
* memoization is a dumb-simple, linear least-recently-used cache.
|
||||
*/
|
||||
function lruMemoize(f) {
|
||||
const cache = []
|
||||
|
||||
return function (input) {
|
||||
for (let i = 0; i < cache.length; i++) {
|
||||
if (cache[i].input === input) {
|
||||
const temp = cache[0]
|
||||
cache[0] = cache[i]
|
||||
cache[i] = temp
|
||||
return cache[0].result
|
||||
}
|
||||
}
|
||||
|
||||
const result = f(input)
|
||||
|
||||
cache.unshift({
|
||||
input,
|
||||
result,
|
||||
})
|
||||
|
||||
if (cache.length > MAX_CACHED_INPUTS) {
|
||||
cache.pop()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a path, or the path portion of a URL:
|
||||
*
|
||||
* - Replaces consecutive slashes with one slash.
|
||||
* - Removes unnecessary '.' parts.
|
||||
* - Removes unnecessary '<dir>/..' parts.
|
||||
*
|
||||
* Based on code in the Node.js 'path' core module.
|
||||
*
|
||||
* @param aPath The path or url to normalize.
|
||||
*/
|
||||
const normalize = lruMemoize(function normalize(aPath) {
|
||||
let path = aPath
|
||||
const url = urlParse(aPath)
|
||||
if (url) {
|
||||
if (!url.path) {
|
||||
return aPath
|
||||
}
|
||||
path = url.path
|
||||
}
|
||||
const isAbsolute = exports.isAbsolute(path)
|
||||
|
||||
// Split the path into parts between `/` characters. This is much faster than
|
||||
// using `.split(/\/+/g)`.
|
||||
const parts = []
|
||||
let start = 0
|
||||
let i = 0
|
||||
while (true) {
|
||||
start = i
|
||||
i = path.indexOf('/', start)
|
||||
if (i === -1) {
|
||||
parts.push(path.slice(start))
|
||||
break
|
||||
} else {
|
||||
parts.push(path.slice(start, i))
|
||||
while (i < path.length && path[i] === '/') {
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let up = 0
|
||||
for (i = parts.length - 1; i >= 0; i--) {
|
||||
const part = parts[i]
|
||||
if (part === '.') {
|
||||
parts.splice(i, 1)
|
||||
} else if (part === '..') {
|
||||
up++
|
||||
} else if (up > 0) {
|
||||
if (part === '') {
|
||||
// The first part is blank if the path is absolute. Trying to go
|
||||
// above the root is a no-op. Therefore we can remove all '..' parts
|
||||
// directly after the root.
|
||||
parts.splice(i + 1, up)
|
||||
up = 0
|
||||
} else {
|
||||
parts.splice(i, 2)
|
||||
up--
|
||||
}
|
||||
}
|
||||
}
|
||||
path = parts.join('/')
|
||||
|
||||
if (path === '') {
|
||||
path = isAbsolute ? '/' : '.'
|
||||
}
|
||||
|
||||
if (url) {
|
||||
url.path = path
|
||||
return urlGenerate(url)
|
||||
}
|
||||
return path
|
||||
})
|
||||
exports.normalize = normalize
|
||||
|
||||
/**
|
||||
* Joins two paths/URLs.
|
||||
*
|
||||
* @param aRoot The root path or URL.
|
||||
* @param aPath The path or URL to be joined with the root.
|
||||
*
|
||||
* - If aPath is a URL or a data URI, aPath is returned, unless aPath is a
|
||||
* scheme-relative URL: Then the scheme of aRoot, if any, is prepended
|
||||
* first.
|
||||
* - Otherwise aPath is a path. If aRoot is a URL, then its path portion
|
||||
* is updated with the result and aRoot is returned. Otherwise the result
|
||||
* is returned.
|
||||
* - If aPath is absolute, the result is aPath.
|
||||
* - Otherwise the two paths are joined with a slash.
|
||||
* - Joining for example 'http://' and 'www.example.com' is also supported.
|
||||
*/
|
||||
function join(aRoot, aPath) {
|
||||
if (aRoot === '') {
|
||||
aRoot = '.'
|
||||
}
|
||||
if (aPath === '') {
|
||||
aPath = '.'
|
||||
}
|
||||
const aPathUrl = urlParse(aPath)
|
||||
const aRootUrl = urlParse(aRoot)
|
||||
if (aRootUrl) {
|
||||
aRoot = aRootUrl.path || '/'
|
||||
}
|
||||
|
||||
// `join(foo, '//www.example.org')`
|
||||
if (aPathUrl && !aPathUrl.scheme) {
|
||||
if (aRootUrl) {
|
||||
aPathUrl.scheme = aRootUrl.scheme
|
||||
}
|
||||
return urlGenerate(aPathUrl)
|
||||
}
|
||||
|
||||
if (aPathUrl || aPath.match(dataUrlRegexp)) {
|
||||
return aPath
|
||||
}
|
||||
|
||||
// `join('http://', 'www.example.com')`
|
||||
if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {
|
||||
aRootUrl.host = aPath
|
||||
return urlGenerate(aRootUrl)
|
||||
}
|
||||
|
||||
const joined =
|
||||
aPath.charAt(0) === '/'
|
||||
? aPath
|
||||
: normalize(aRoot.replace(/\/+$/, '') + '/' + aPath)
|
||||
|
||||
if (aRootUrl) {
|
||||
aRootUrl.path = joined
|
||||
return urlGenerate(aRootUrl)
|
||||
}
|
||||
return joined
|
||||
}
|
||||
exports.join = join
|
||||
|
||||
exports.isAbsolute = function (aPath) {
|
||||
return aPath.charAt(0) === '/' || urlRegexp.test(aPath)
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a path relative to a URL or another path.
|
||||
*
|
||||
* @param aRoot The root path or URL.
|
||||
* @param aPath The path or URL to be made relative to aRoot.
|
||||
*/
|
||||
function relative(aRoot, aPath) {
|
||||
if (aRoot === '') {
|
||||
aRoot = '.'
|
||||
}
|
||||
|
||||
aRoot = aRoot.replace(/\/$/, '')
|
||||
|
||||
// It is possible for the path to be above the root. In this case, simply
|
||||
// checking whether the root is a prefix of the path won't work. Instead, we
|
||||
// need to remove components from the root one by one, until either we find
|
||||
// a prefix that fits, or we run out of components to remove.
|
||||
let level = 0
|
||||
while (aPath.indexOf(aRoot + '/') !== 0) {
|
||||
const index = aRoot.lastIndexOf('/')
|
||||
if (index < 0) {
|
||||
return aPath
|
||||
}
|
||||
|
||||
// If the only part of the root that is left is the scheme (i.e. http://,
|
||||
// file:///, etc.), one or more slashes (/), or simply nothing at all, we
|
||||
// have exhausted all components, so the path is not relative to the root.
|
||||
aRoot = aRoot.slice(0, index)
|
||||
if (aRoot.match(/^([^\/]+:\/)?\/*$/)) {
|
||||
return aPath
|
||||
}
|
||||
|
||||
++level
|
||||
}
|
||||
|
||||
// Make sure we add a "../" for each component we removed from the root.
|
||||
return Array(level + 1).join('../') + aPath.substr(aRoot.length + 1)
|
||||
}
|
||||
exports.relative = relative
|
||||
|
||||
const supportsNullProto = (function () {
|
||||
const obj = Object.create(null)
|
||||
return !('__proto__' in obj)
|
||||
})()
|
||||
|
||||
function identity(s) {
|
||||
return s
|
||||
}
|
||||
|
||||
/**
|
||||
* Because behavior goes wacky when you set `__proto__` on objects, we
|
||||
* have to prefix all the strings in our set with an arbitrary character.
|
||||
*
|
||||
* See https://github.com/mozilla/source-map/pull/31 and
|
||||
* https://github.com/mozilla/source-map/issues/30
|
||||
*
|
||||
* @param String aStr
|
||||
*/
|
||||
function toSetString(aStr) {
|
||||
if (isProtoString(aStr)) {
|
||||
return '$' + aStr
|
||||
}
|
||||
|
||||
return aStr
|
||||
}
|
||||
exports.toSetString = supportsNullProto ? identity : toSetString
|
||||
|
||||
function fromSetString(aStr) {
|
||||
if (isProtoString(aStr)) {
|
||||
return aStr.slice(1)
|
||||
}
|
||||
|
||||
return aStr
|
||||
}
|
||||
exports.fromSetString = supportsNullProto ? identity : fromSetString
|
||||
|
||||
function isProtoString(s) {
|
||||
if (!s) {
|
||||
return false
|
||||
}
|
||||
|
||||
const length = s.length
|
||||
|
||||
if (length < 9 /* "__proto__".length */) {
|
||||
return false
|
||||
}
|
||||
|
||||
/* eslint-disable no-multi-spaces */
|
||||
if (
|
||||
s.charCodeAt(length - 1) !== 95 /* '_' */ ||
|
||||
s.charCodeAt(length - 2) !== 95 /* '_' */ ||
|
||||
s.charCodeAt(length - 3) !== 111 /* 'o' */ ||
|
||||
s.charCodeAt(length - 4) !== 116 /* 't' */ ||
|
||||
s.charCodeAt(length - 5) !== 111 /* 'o' */ ||
|
||||
s.charCodeAt(length - 6) !== 114 /* 'r' */ ||
|
||||
s.charCodeAt(length - 7) !== 112 /* 'p' */ ||
|
||||
s.charCodeAt(length - 8) !== 95 /* '_' */ ||
|
||||
s.charCodeAt(length - 9) !== 95 /* '_' */
|
||||
) {
|
||||
return false
|
||||
}
|
||||
/* eslint-enable no-multi-spaces */
|
||||
|
||||
for (let i = length - 10; i >= 0; i--) {
|
||||
if (s.charCodeAt(i) !== 36 /* '$' */) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator between two mappings where the original positions are compared.
|
||||
*
|
||||
* Optionally pass in `true` as `onlyCompareGenerated` to consider two
|
||||
* mappings with the same original source/line/column, but different generated
|
||||
* line and column the same. Useful when searching for a mapping with a
|
||||
* stubbed out mapping.
|
||||
*/
|
||||
function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
|
||||
let cmp = strcmp(mappingA.source, mappingB.source)
|
||||
if (cmp !== 0) {
|
||||
return cmp
|
||||
}
|
||||
|
||||
cmp = mappingA.originalLine - mappingB.originalLine
|
||||
if (cmp !== 0) {
|
||||
return cmp
|
||||
}
|
||||
|
||||
cmp = mappingA.originalColumn - mappingB.originalColumn
|
||||
if (cmp !== 0 || onlyCompareOriginal) {
|
||||
return cmp
|
||||
}
|
||||
|
||||
cmp = mappingA.generatedColumn - mappingB.generatedColumn
|
||||
if (cmp !== 0) {
|
||||
return cmp
|
||||
}
|
||||
|
||||
cmp = mappingA.generatedLine - mappingB.generatedLine
|
||||
if (cmp !== 0) {
|
||||
return cmp
|
||||
}
|
||||
|
||||
return strcmp(mappingA.name, mappingB.name)
|
||||
}
|
||||
exports.compareByOriginalPositions = compareByOriginalPositions
|
||||
|
||||
/**
|
||||
* Comparator between two mappings with deflated source and name indices where
|
||||
* the generated positions are compared.
|
||||
*
|
||||
* Optionally pass in `true` as `onlyCompareGenerated` to consider two
|
||||
* mappings with the same generated line and column, but different
|
||||
* source/name/original line and column the same. Useful when searching for a
|
||||
* mapping with a stubbed out mapping.
|
||||
*/
|
||||
function compareByGeneratedPositionsDeflated(
|
||||
mappingA,
|
||||
mappingB,
|
||||
onlyCompareGenerated
|
||||
) {
|
||||
let cmp = mappingA.generatedLine - mappingB.generatedLine
|
||||
if (cmp !== 0) {
|
||||
return cmp
|
||||
}
|
||||
|
||||
cmp = mappingA.generatedColumn - mappingB.generatedColumn
|
||||
if (cmp !== 0 || onlyCompareGenerated) {
|
||||
return cmp
|
||||
}
|
||||
|
||||
cmp = strcmp(mappingA.source, mappingB.source)
|
||||
if (cmp !== 0) {
|
||||
return cmp
|
||||
}
|
||||
|
||||
cmp = mappingA.originalLine - mappingB.originalLine
|
||||
if (cmp !== 0) {
|
||||
return cmp
|
||||
}
|
||||
|
||||
cmp = mappingA.originalColumn - mappingB.originalColumn
|
||||
if (cmp !== 0) {
|
||||
return cmp
|
||||
}
|
||||
|
||||
return strcmp(mappingA.name, mappingB.name)
|
||||
}
|
||||
exports.compareByGeneratedPositionsDeflated =
|
||||
compareByGeneratedPositionsDeflated
|
||||
|
||||
function strcmp(aStr1, aStr2) {
|
||||
if (aStr1 === aStr2) {
|
||||
return 0
|
||||
}
|
||||
|
||||
if (aStr1 === null) {
|
||||
return 1 // aStr2 !== null
|
||||
}
|
||||
|
||||
if (aStr2 === null) {
|
||||
return -1 // aStr1 !== null
|
||||
}
|
||||
|
||||
if (aStr1 > aStr2) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator between two mappings with inflated source and name strings where
|
||||
* the generated positions are compared.
|
||||
*/
|
||||
function compareByGeneratedPositionsInflated(mappingA, mappingB) {
|
||||
let cmp = mappingA.generatedLine - mappingB.generatedLine
|
||||
if (cmp !== 0) {
|
||||
return cmp
|
||||
}
|
||||
|
||||
cmp = mappingA.generatedColumn - mappingB.generatedColumn
|
||||
if (cmp !== 0) {
|
||||
return cmp
|
||||
}
|
||||
|
||||
cmp = strcmp(mappingA.source, mappingB.source)
|
||||
if (cmp !== 0) {
|
||||
return cmp
|
||||
}
|
||||
|
||||
cmp = mappingA.originalLine - mappingB.originalLine
|
||||
if (cmp !== 0) {
|
||||
return cmp
|
||||
}
|
||||
|
||||
cmp = mappingA.originalColumn - mappingB.originalColumn
|
||||
if (cmp !== 0) {
|
||||
return cmp
|
||||
}
|
||||
|
||||
return strcmp(mappingA.name, mappingB.name)
|
||||
}
|
||||
exports.compareByGeneratedPositionsInflated =
|
||||
compareByGeneratedPositionsInflated
|
||||
|
||||
/**
|
||||
* Strip any JSON XSSI avoidance prefix from the string (as documented
|
||||
* in the source maps specification), and then parse the string as
|
||||
* JSON.
|
||||
*/
|
||||
function parseSourceMapInput(str) {
|
||||
return JSON.parse(str.replace(/^\)]}'[^\n]*\n/, ''))
|
||||
}
|
||||
exports.parseSourceMapInput = parseSourceMapInput
|
||||
|
||||
/**
|
||||
* Compute the URL of a source given the the source root, the source's
|
||||
* URL, and the source map's URL.
|
||||
*/
|
||||
function computeSourceURL(sourceRoot, sourceURL, sourceMapURL) {
|
||||
sourceURL = sourceURL || ''
|
||||
|
||||
if (sourceRoot) {
|
||||
// This follows what Chrome does.
|
||||
if (sourceRoot[sourceRoot.length - 1] !== '/' && sourceURL[0] !== '/') {
|
||||
sourceRoot += '/'
|
||||
}
|
||||
// The spec says:
|
||||
// Line 4: An optional source root, useful for relocating source
|
||||
// files on a server or removing repeated values in the
|
||||
// “sources” entry. This value is prepended to the individual
|
||||
// entries in the “source” field.
|
||||
sourceURL = sourceRoot + sourceURL
|
||||
}
|
||||
|
||||
// Historically, SourceMapConsumer did not take the sourceMapURL as
|
||||
// a parameter. This mode is still somewhat supported, which is why
|
||||
// this code block is conditional. However, it's preferable to pass
|
||||
// the source map URL to SourceMapConsumer, so that this function
|
||||
// can implement the source URL resolution algorithm as outlined in
|
||||
// the spec. This block is basically the equivalent of:
|
||||
// new URL(sourceURL, sourceMapURL).toString()
|
||||
// ... except it avoids using URL, which wasn't available in the
|
||||
// older releases of node still supported by this library.
|
||||
//
|
||||
// The spec says:
|
||||
// If the sources are not absolute URLs after prepending of the
|
||||
// “sourceRoot”, the sources are resolved relative to the
|
||||
// SourceMap (like resolving script src in a html document).
|
||||
if (sourceMapURL) {
|
||||
const parsed = urlParse(sourceMapURL)
|
||||
if (!parsed) {
|
||||
throw new Error('sourceMapURL could not be parsed')
|
||||
}
|
||||
if (parsed.path) {
|
||||
// Strip the last path component, but keep the "/".
|
||||
const index = parsed.path.lastIndexOf('/')
|
||||
if (index >= 0) {
|
||||
parsed.path = parsed.path.substring(0, index + 1)
|
||||
}
|
||||
}
|
||||
sourceURL = join(urlGenerate(parsed), sourceURL)
|
||||
}
|
||||
|
||||
return normalize(sourceURL)
|
||||
}
|
||||
exports.computeSourceURL = computeSourceURL
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
const readWasm = require('./read-wasm')
|
||||
|
||||
/**
|
||||
* Provide the JIT with a nice shape / hidden class.
|
||||
*/
|
||||
function Mapping() {
|
||||
this.generatedLine = 0
|
||||
this.generatedColumn = 0
|
||||
this.lastGeneratedColumn = null
|
||||
this.source = null
|
||||
this.originalLine = null
|
||||
this.originalColumn = null
|
||||
this.name = null
|
||||
}
|
||||
|
||||
let cachedWasm = null
|
||||
|
||||
module.exports = function wasm() {
|
||||
if (cachedWasm) {
|
||||
return cachedWasm
|
||||
}
|
||||
|
||||
const callbackStack = []
|
||||
|
||||
cachedWasm = readWasm()
|
||||
.then((buffer) => {
|
||||
return WebAssembly.instantiate(buffer, {
|
||||
env: {
|
||||
mapping_callback(
|
||||
generatedLine,
|
||||
generatedColumn,
|
||||
|
||||
hasLastGeneratedColumn,
|
||||
lastGeneratedColumn,
|
||||
|
||||
hasOriginal,
|
||||
source,
|
||||
originalLine,
|
||||
originalColumn,
|
||||
|
||||
hasName,
|
||||
name
|
||||
) {
|
||||
const mapping = new Mapping()
|
||||
// JS uses 1-based line numbers, wasm uses 0-based.
|
||||
mapping.generatedLine = generatedLine + 1
|
||||
mapping.generatedColumn = generatedColumn
|
||||
|
||||
if (hasLastGeneratedColumn) {
|
||||
// JS uses inclusive last generated column, wasm uses exclusive.
|
||||
mapping.lastGeneratedColumn = lastGeneratedColumn - 1
|
||||
}
|
||||
|
||||
if (hasOriginal) {
|
||||
mapping.source = source
|
||||
// JS uses 1-based line numbers, wasm uses 0-based.
|
||||
mapping.originalLine = originalLine + 1
|
||||
mapping.originalColumn = originalColumn
|
||||
|
||||
if (hasName) {
|
||||
mapping.name = name
|
||||
}
|
||||
}
|
||||
|
||||
callbackStack[callbackStack.length - 1](mapping)
|
||||
},
|
||||
|
||||
start_all_generated_locations_for() {
|
||||
console.time('all_generated_locations_for')
|
||||
},
|
||||
end_all_generated_locations_for() {
|
||||
console.timeEnd('all_generated_locations_for')
|
||||
},
|
||||
|
||||
start_compute_column_spans() {
|
||||
console.time('compute_column_spans')
|
||||
},
|
||||
end_compute_column_spans() {
|
||||
console.timeEnd('compute_column_spans')
|
||||
},
|
||||
|
||||
start_generated_location_for() {
|
||||
console.time('generated_location_for')
|
||||
},
|
||||
end_generated_location_for() {
|
||||
console.timeEnd('generated_location_for')
|
||||
},
|
||||
|
||||
start_original_location_for() {
|
||||
console.time('original_location_for')
|
||||
},
|
||||
end_original_location_for() {
|
||||
console.timeEnd('original_location_for')
|
||||
},
|
||||
|
||||
start_parse_mappings() {
|
||||
console.time('parse_mappings')
|
||||
},
|
||||
end_parse_mappings() {
|
||||
console.timeEnd('parse_mappings')
|
||||
},
|
||||
|
||||
start_sort_by_generated_location() {
|
||||
console.time('sort_by_generated_location')
|
||||
},
|
||||
end_sort_by_generated_location() {
|
||||
console.timeEnd('sort_by_generated_location')
|
||||
},
|
||||
|
||||
start_sort_by_original_location() {
|
||||
console.time('sort_by_original_location')
|
||||
},
|
||||
end_sort_by_original_location() {
|
||||
console.timeEnd('sort_by_original_location')
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
.then((Wasm) => {
|
||||
return {
|
||||
exports: Wasm.instance.exports,
|
||||
withMappingCallback: (mappingCallback, f) => {
|
||||
callbackStack.push(mappingCallback)
|
||||
try {
|
||||
f()
|
||||
} finally {
|
||||
callbackStack.pop()
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
.then(null, (e) => {
|
||||
cachedWasm = null
|
||||
throw e
|
||||
})
|
||||
|
||||
return cachedWasm
|
||||
}
|
||||
Reference in New Issue
Block a user