source-map
Advanced tools
Comparing version 0.7.3 to 0.8.0-beta.0
# Change Log | ||
## 0.8.0-beta.0 | ||
### Breaking changes | ||
* [#350](https://github.com/mozilla/source-map/pull/350) - | ||
Change browser detection logic for WASM loading. | ||
* [#363](https://github.com/mozilla/source-map/pull/363) - | ||
Change WASM loading detection to rely on `package.json#browser` field. | ||
* [#362](https://github.com/mozilla/source-map/pull/362) - | ||
Remove the `dist/` bundle. | ||
* [#371](https://github.com/mozilla/source-map/pull/371) - | ||
Reimplement sourcemap URL processing using the WHATWG URL API. | ||
### Nonbreaking changes: | ||
* [#339](https://github.com/mozilla/source-map/pull/339) - | ||
Allow initializing the consumer `mappings.wasm` file as an `ArrayBuffer`. | ||
### Internal Improvements: | ||
* [#347](https://github.com/mozilla/source-map/pull/347) - | ||
Improve tests. | ||
* [#352](https://github.com/mozilla/source-map/pull/352) - | ||
Improve documentation. | ||
* [#361](https://github.com/mozilla/source-map/pull/361) - | ||
Use newer Webpack CLI when bundling. | ||
* [#364](https://github.com/mozilla/source-map/pull/364) - | ||
Convert `IndexedSourceMapConsumer` implementation to pass more through | ||
to `BasicSourceMapConsumer`. | ||
* [#366](https://github.com/mozilla/source-map/pull/366) - | ||
Normalize internal URL representation to be easier to follow. | ||
* [#341](https://github.com/mozilla/source-map/pull/341) - | ||
Use async functions to simplify `SourceMapConsumer.with` implementation. | ||
## 0.7.3 | ||
@@ -4,0 +39,0 @@ |
@@ -1,40 +0,25 @@ | ||
if (typeof fetch === "function") { | ||
// Web version of reading a wasm file into an array buffer. | ||
"use strict"; | ||
let mappingsWasmUrl = null; | ||
// Note: This file is replaced with "read-wasm-browser.js" when this module is | ||
// bundled with a packager that takes package.json#browser fields into account. | ||
module.exports = function readWasm() { | ||
if (typeof mappingsWasmUrl !== "string") { | ||
throw new Error("You must provide the URL of lib/mappings.wasm by calling " + | ||
"SourceMapConsumer.initialize({ 'lib/mappings.wasm': ... }) " + | ||
"before using SourceMapConsumer"); | ||
} | ||
const fs = require("fs"); | ||
const path = require("path"); | ||
return fetch(mappingsWasmUrl) | ||
.then(response => response.arrayBuffer()); | ||
}; | ||
module.exports = function readWasm() { | ||
return new Promise((resolve, reject) => { | ||
const wasmPath = path.join(__dirname, "mappings.wasm"); | ||
fs.readFile(wasmPath, null, (error, data) => { | ||
if (error) { | ||
reject(error); | ||
return; | ||
} | ||
module.exports.initialize = url => mappingsWasmUrl = url; | ||
} 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, "mappings.wasm"); | ||
fs.readFile(wasmPath, null, (error, data) => { | ||
if (error) { | ||
reject(error); | ||
return; | ||
} | ||
resolve(data.buffer); | ||
}); | ||
resolve(data.buffer); | ||
}); | ||
}; | ||
}); | ||
}; | ||
module.exports.initialize = _ => { | ||
console.debug("SourceMapConsumer.initialize is a no-op when running in node.js"); | ||
}; | ||
} | ||
module.exports.initialize = _ => { | ||
console.debug("SourceMapConsumer.initialize is a no-op when running in node.js"); | ||
}; |
@@ -67,38 +67,12 @@ /* -*- Mode: js; js-indent-level: 2; -*- */ | ||
*/ | ||
static with(rawSourceMap, sourceMapUrl, f) { | ||
// Note: The `acorn` version that `webpack` currently depends on doesn't | ||
// support `async` functions, and the nodes that we support don't all have | ||
// `.finally`. Therefore, this is written a bit more convolutedly than it | ||
// should really be. | ||
let consumer = null; | ||
const promise = new SourceMapConsumer(rawSourceMap, sourceMapUrl); | ||
return promise | ||
.then(c => { | ||
consumer = c; | ||
return f(c); | ||
}) | ||
.then(x => { | ||
if (consumer) { | ||
consumer.destroy(); | ||
} | ||
return x; | ||
}, e => { | ||
if (consumer) { | ||
consumer.destroy(); | ||
} | ||
throw e; | ||
}); | ||
static async with(rawSourceMap, sourceMapUrl, f) { | ||
const consumer = await new SourceMapConsumer(rawSourceMap, sourceMapUrl); | ||
try { | ||
return await f(consumer); | ||
} finally { | ||
consumer.destroy(); | ||
} | ||
} | ||
/** | ||
* Parse the mappings in a string in to a data structure which we can easily | ||
* query (the ordered arrays in the `this.__generatedMappings` and | ||
* `this.__originalMappings` properties). | ||
*/ | ||
_parseMappings(aStr, aSourceRoot) { | ||
throw new Error("Subclasses must implement _parseMappings"); | ||
} | ||
/** | ||
* Iterate over each mapping between an original source/line/column and a | ||
@@ -209,7 +183,7 @@ * generated line/column in this source map. | ||
const version = util.getArg(sourceMap, "version"); | ||
let sources = util.getArg(sourceMap, "sources"); | ||
const sources = util.getArg(sourceMap, "sources").map(String); | ||
// Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which | ||
// requires the array) to play nice here. | ||
const names = util.getArg(sourceMap, "names", []); | ||
let sourceRoot = util.getArg(sourceMap, "sourceRoot", null); | ||
const sourceRoot = util.getArg(sourceMap, "sourceRoot", null); | ||
const sourcesContent = util.getArg(sourceMap, "sourcesContent", null); | ||
@@ -225,22 +199,4 @@ const mappings = util.getArg(sourceMap, "mappings"); | ||
if (sourceRoot) { | ||
sourceRoot = util.normalize(sourceRoot); | ||
} | ||
that._sourceLookupCache = new Map(); | ||
sources = sources | ||
.map(String) | ||
// Some source maps produce relative source paths like "./foo.js" instead of | ||
// "foo.js". Normalize these first so that future comparisons will succeed. | ||
// See bugzil.la/1090768. | ||
.map(util.normalize) | ||
// Always ensure that absolute sources are internally stored relative to | ||
// the source root, if the source root is absolute. Not doing this would | ||
// be particularly problematic when the source root is a prefix of the | ||
// source (valid, but why??). See github issue #199 and bugzil.la/1188982. | ||
.map(function(source) { | ||
return sourceRoot && util.isAbsolute(sourceRoot) && util.isAbsolute(source) | ||
? util.relative(sourceRoot, source) | ||
: source; | ||
}); | ||
// Pass `true` below to allow duplicate names and sources. While source maps | ||
@@ -253,5 +209,5 @@ // are intended to be compressed and deduplicated, the TypeScript compiler | ||
that._absoluteSources = that._sources.toArray().map(function(s) { | ||
that._absoluteSources = ArraySet.fromArray(that._sources.toArray().map(function(s) { | ||
return util.computeSourceURL(sourceRoot, s, aSourceMapURL); | ||
}); | ||
}), true); | ||
@@ -280,19 +236,26 @@ that.sourceRoot = sourceRoot; | ||
_findSourceIndex(aSource) { | ||
let relativeSource = aSource; | ||
if (this.sourceRoot != null) { | ||
relativeSource = util.relative(this.sourceRoot, relativeSource); | ||
// In the most common usecases, we'll be constantly looking up the index for the same source | ||
// files, so we cache the index lookup to avoid constantly recomputing the full URLs. | ||
const cachedIndex = this._sourceLookupCache.get(aSource); | ||
if (typeof cachedIndex === "number") { | ||
return cachedIndex; | ||
} | ||
if (this._sources.has(relativeSource)) { | ||
return this._sources.indexOf(relativeSource); | ||
// Treat the source as map-relative overall by default. | ||
const sourceAsMapRelative = util.computeSourceURL(null, aSource, this._sourceMapURL); | ||
if (this._absoluteSources.has(sourceAsMapRelative)) { | ||
const index = this._absoluteSources.indexOf(sourceAsMapRelative); | ||
this._sourceLookupCache.set(aSource, index); | ||
return index; | ||
} | ||
// Maybe aSource is an absolute URL as returned by |sources|. In | ||
// this case we can't simply undo the transform. | ||
for (let i = 0; i < this._absoluteSources.length; ++i) { | ||
if (this._absoluteSources[i] == aSource) { | ||
return i; | ||
} | ||
// Fall back to treating the source as sourceRoot-relative. | ||
const sourceAsSourceRootRelative = util.computeSourceURL(this.sourceRoot, aSource, this._sourceMapURL); | ||
if (this._absoluteSources.has(sourceAsSourceRootRelative)) { | ||
const index = this._absoluteSources.indexOf(sourceAsSourceRootRelative); | ||
this._sourceLookupCache.set(aSource, index); | ||
return index; | ||
} | ||
// To avoid this cache growing forever, we do not cache lookup misses. | ||
return -1; | ||
@@ -315,3 +278,3 @@ } | ||
get sources() { | ||
return this._absoluteSources.slice(); | ||
return this._absoluteSources.toArray(); | ||
} | ||
@@ -321,3 +284,3 @@ | ||
if (this._mappingsPtr === 0) { | ||
this._parseMappings(this._mappings, this.sourceRoot); | ||
this._parseMappings(); | ||
} | ||
@@ -333,3 +296,4 @@ | ||
*/ | ||
_parseMappings(aStr, aSourceRoot) { | ||
_parseMappings() { | ||
const aStr = this._mappings; | ||
const size = aStr.length; | ||
@@ -377,3 +341,2 @@ | ||
const order = aOrder || SourceMapConsumer.GENERATED_ORDER; | ||
const sourceRoot = this.sourceRoot; | ||
@@ -383,4 +346,3 @@ this._wasm.withMappingCallback( | ||
if (mapping.source !== null) { | ||
mapping.source = this._sources.at(mapping.source); | ||
mapping.source = util.computeSourceURL(sourceRoot, mapping.source, this._sourceMapURL); | ||
mapping.source = this._absoluteSources.at(mapping.source); | ||
@@ -391,2 +353,5 @@ if (mapping.name !== null) { | ||
} | ||
if (this._computedColumnSpans && mapping.lastGeneratedColumn === null) { | ||
mapping.lastGeneratedColumn = Infinity; | ||
} | ||
@@ -532,4 +497,3 @@ aCallback.call(context, mapping); | ||
if (source !== null) { | ||
source = this._sources.at(source); | ||
source = util.computeSourceURL(this.sourceRoot, source, this._sourceMapURL); | ||
source = this._absoluteSources.at(source); | ||
} | ||
@@ -586,26 +550,2 @@ | ||
let relativeSource = aSource; | ||
if (this.sourceRoot != null) { | ||
relativeSource = util.relative(this.sourceRoot, relativeSource); | ||
} | ||
let url; | ||
if (this.sourceRoot != null | ||
&& (url = util.urlParse(this.sourceRoot))) { | ||
// XXX: file:// URIs and absolute paths lead to unexpected behavior for | ||
// many users. We can help them out when they expect file:// URIs to | ||
// behave like it would if they were running a local HTTP server. See | ||
// https://bugzilla.mozilla.org/show_bug.cgi?id=885597. | ||
const fileUriAbsPath = relativeSource.replace(/^file:\/\//, ""); | ||
if (url.scheme == "file" | ||
&& this._sources.has(fileUriAbsPath)) { | ||
return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]; | ||
} | ||
if ((!url.path || url.path == "/") | ||
&& this._sources.has("/" + relativeSource)) { | ||
return this.sourcesContent[this._sources.indexOf("/" + relativeSource)]; | ||
} | ||
} | ||
// This function is used recursively from | ||
@@ -619,3 +559,3 @@ // IndexedSourceMapConsumer.prototype.sourceContentFor. In that case, we | ||
throw new Error('"' + relativeSource + '" is not in the SourceMap.'); | ||
throw new Error('"' + aSource + '" is not in the SourceMap.'); | ||
} | ||
@@ -776,9 +716,2 @@ | ||
that._sources = new ArraySet(); | ||
that._names = new ArraySet(); | ||
that.__generatedMappings = null; | ||
that.__originalMappings = null; | ||
that.__generatedMappingsUnsorted = null; | ||
that.__originalMappingsUnsorted = null; | ||
let lastOffset = { | ||
@@ -823,75 +756,2 @@ line: -1, | ||
// `__generatedMappings` and `__originalMappings` are arrays that hold the | ||
// parsed mapping coordinates from the source map's "mappings" attribute. They | ||
// are lazily instantiated, accessed via the `_generatedMappings` and | ||
// `_originalMappings` getters respectively, and we only parse the mappings | ||
// and create these arrays once queried for a source location. We jump through | ||
// these hoops because there can be many thousands of mappings, and parsing | ||
// them is expensive, so we only want to do it if we must. | ||
// | ||
// Each object in the arrays is of the form: | ||
// | ||
// { | ||
// generatedLine: The line number in the generated code, | ||
// generatedColumn: The column number in the generated code, | ||
// source: The path to the original source file that generated this | ||
// chunk of code, | ||
// originalLine: The line number in the original source that | ||
// corresponds to this chunk of generated code, | ||
// originalColumn: The column number in the original source that | ||
// corresponds to this chunk of generated code, | ||
// name: The name of the original symbol which generated this chunk of | ||
// code. | ||
// } | ||
// | ||
// All properties except for `generatedLine` and `generatedColumn` can be | ||
// `null`. | ||
// | ||
// `_generatedMappings` is ordered by the generated positions. | ||
// | ||
// `_originalMappings` is ordered by the original positions. | ||
get _generatedMappings() { | ||
if (!this.__generatedMappings) { | ||
this._sortGeneratedMappings(); | ||
} | ||
return this.__generatedMappings; | ||
} | ||
get _originalMappings() { | ||
if (!this.__originalMappings) { | ||
this._sortOriginalMappings(); | ||
} | ||
return this.__originalMappings; | ||
} | ||
get _generatedMappingsUnsorted() { | ||
if (!this.__generatedMappingsUnsorted) { | ||
this._parseMappings(this._mappings, this.sourceRoot); | ||
} | ||
return this.__generatedMappingsUnsorted; | ||
} | ||
get _originalMappingsUnsorted() { | ||
if (!this.__originalMappingsUnsorted) { | ||
this._parseMappings(this._mappings, this.sourceRoot); | ||
} | ||
return this.__originalMappingsUnsorted; | ||
} | ||
_sortGeneratedMappings() { | ||
const mappings = this._generatedMappingsUnsorted; | ||
mappings.sort(util.compareByGeneratedPositionsDeflated); | ||
this.__generatedMappings = mappings; | ||
} | ||
_sortOriginalMappings() { | ||
const mappings = this._originalMappingsUnsorted; | ||
mappings.sort(util.compareByOriginalPositions); | ||
this.__originalMappings = mappings; | ||
} | ||
/** | ||
@@ -999,2 +859,12 @@ * The list of original sources. | ||
_findSectionIndex(source) { | ||
for (let i = 0; i < this._sections.length; i++) { | ||
const { consumer } = this._sections[i]; | ||
if (consumer._findSourceIndex(source) !== -1) { | ||
return i; | ||
} | ||
} | ||
return -1; | ||
} | ||
/** | ||
@@ -1019,22 +889,33 @@ * Returns the generated line and column information for the original source, | ||
generatedPositionFor(aArgs) { | ||
for (let i = 0; i < this._sections.length; i++) { | ||
const section = this._sections[i]; | ||
const index = this._findSectionIndex(util.getArg(aArgs, "source")); | ||
const section = index >= 0 ? this._sections[index] : null; | ||
const nextSection = | ||
index >= 0 && index + 1 < this._sections.length | ||
? this._sections[index + 1] | ||
: null; | ||
// Only consider this section if the requested source is in the list of | ||
// sources of the consumer. | ||
if (section.consumer._findSourceIndex(util.getArg(aArgs, "source")) === -1) { | ||
continue; | ||
const generatedPosition = | ||
section && section.consumer.generatedPositionFor(aArgs); | ||
if (generatedPosition && generatedPosition.line !== null) { | ||
const lineShift = section.generatedOffset.generatedLine - 1; | ||
const columnShift = section.generatedOffset.generatedColumn - 1; | ||
if (generatedPosition.line === 1) { | ||
generatedPosition.column += columnShift; | ||
if (typeof generatedPosition.lastColumn === "number") { | ||
generatedPosition.lastColumn += columnShift; | ||
} | ||
} | ||
const generatedPosition = section.consumer.generatedPositionFor(aArgs); | ||
if (generatedPosition) { | ||
const ret = { | ||
line: generatedPosition.line + | ||
(section.generatedOffset.generatedLine - 1), | ||
column: generatedPosition.column + | ||
(section.generatedOffset.generatedLine === generatedPosition.line | ||
? section.generatedOffset.generatedColumn - 1 | ||
: 0) | ||
}; | ||
return ret; | ||
if ( | ||
generatedPosition.lastColumn === Infinity && | ||
nextSection && | ||
generatedPosition.line === nextSection.generatedOffset.generatedLine | ||
) { | ||
generatedPosition.lastColumn = | ||
nextSection.generatedOffset.generatedColumn - 2; | ||
} | ||
generatedPosition.line += lineShift; | ||
return generatedPosition; | ||
} | ||
@@ -1044,202 +925,83 @@ | ||
line: null, | ||
column: null | ||
column: null, | ||
lastColumn: null | ||
}; | ||
} | ||
/** | ||
* Parse the mappings in a string in to a data structure which we can easily | ||
* query (the ordered arrays in the `this.__generatedMappings` and | ||
* `this.__originalMappings` properties). | ||
*/ | ||
_parseMappings(aStr, aSourceRoot) { | ||
const generatedMappings = this.__generatedMappingsUnsorted = []; | ||
const originalMappings = this.__originalMappingsUnsorted = []; | ||
for (let i = 0; i < this._sections.length; i++) { | ||
const section = this._sections[i]; | ||
allGeneratedPositionsFor(aArgs) { | ||
const index = this._findSectionIndex(util.getArg(aArgs, "source")); | ||
const section = index >= 0 ? this._sections[index] : null; | ||
const nextSection = | ||
index >= 0 && index + 1 < this._sections.length | ||
? this._sections[index + 1] | ||
: null; | ||
const sectionMappings = []; | ||
section.consumer.eachMapping(m => sectionMappings.push(m)); | ||
if (!section) return []; | ||
for (let j = 0; j < sectionMappings.length; j++) { | ||
const mapping = sectionMappings[j]; | ||
return section.consumer.allGeneratedPositionsFor(aArgs).map( | ||
generatedPosition => { | ||
const lineShift = section.generatedOffset.generatedLine - 1; | ||
const columnShift = section.generatedOffset.generatedColumn - 1; | ||
// TODO: test if null is correct here. The original code used | ||
// `source`, which would actually have gotten used as null because | ||
// var's get hoisted. | ||
// See: https://github.com/mozilla/source-map/issues/333 | ||
let source = util.computeSourceURL(section.consumer.sourceRoot, null, this._sourceMapURL); | ||
this._sources.add(source); | ||
source = this._sources.indexOf(source); | ||
if (generatedPosition.line === 1) { | ||
generatedPosition.column += columnShift; | ||
if (typeof generatedPosition.lastColumn === "number") { | ||
generatedPosition.lastColumn += columnShift; | ||
} | ||
} | ||
let name = null; | ||
if (mapping.name) { | ||
this._names.add(mapping.name); | ||
name = this._names.indexOf(mapping.name); | ||
if ( | ||
generatedPosition.lastColumn === Infinity && | ||
nextSection && | ||
generatedPosition.line === nextSection.generatedOffset.generatedLine | ||
) { | ||
generatedPosition.lastColumn = | ||
nextSection.generatedOffset.generatedColumn - 2; | ||
} | ||
generatedPosition.line += lineShift; | ||
// The mappings coming from the consumer for the section have | ||
// generated positions relative to the start of the section, so we | ||
// need to offset them to be relative to the start of the concatenated | ||
// generated file. | ||
const adjustedMapping = { | ||
source, | ||
generatedLine: mapping.generatedLine + | ||
(section.generatedOffset.generatedLine - 1), | ||
generatedColumn: mapping.generatedColumn + | ||
(section.generatedOffset.generatedLine === mapping.generatedLine | ||
? section.generatedOffset.generatedColumn - 1 | ||
: 0), | ||
originalLine: mapping.originalLine, | ||
originalColumn: mapping.originalColumn, | ||
name | ||
}; | ||
generatedMappings.push(adjustedMapping); | ||
if (typeof adjustedMapping.originalLine === "number") { | ||
originalMappings.push(adjustedMapping); | ||
} | ||
return generatedPosition; | ||
} | ||
} | ||
); | ||
} | ||
eachMapping(aCallback, aContext, aOrder) { | ||
const context = aContext || null; | ||
const order = aOrder || SourceMapConsumer.GENERATED_ORDER; | ||
this._sections.forEach((section, index) => { | ||
const nextSection = | ||
index + 1 < this._sections.length | ||
? this._sections[index + 1] | ||
: null; | ||
const { generatedOffset } = section; | ||
let mappings; | ||
switch (order) { | ||
case SourceMapConsumer.GENERATED_ORDER: | ||
mappings = this._generatedMappings; | ||
break; | ||
case SourceMapConsumer.ORIGINAL_ORDER: | ||
mappings = this._originalMappings; | ||
break; | ||
default: | ||
throw new Error("Unknown order of iteration."); | ||
} | ||
const lineShift = generatedOffset.generatedLine - 1; | ||
const columnShift = generatedOffset.generatedColumn - 1; | ||
const sourceRoot = this.sourceRoot; | ||
mappings.map(function(mapping) { | ||
let source = null; | ||
if (mapping.source !== null) { | ||
source = this._sources.at(mapping.source); | ||
source = util.computeSourceURL(sourceRoot, source, this._sourceMapURL); | ||
} | ||
return { | ||
source, | ||
generatedLine: mapping.generatedLine, | ||
generatedColumn: mapping.generatedColumn, | ||
originalLine: mapping.originalLine, | ||
originalColumn: mapping.originalColumn, | ||
name: mapping.name === null ? null : this._names.at(mapping.name) | ||
}; | ||
}, this).forEach(aCallback, context); | ||
} | ||
section.consumer.eachMapping(function(mapping) { | ||
if (mapping.generatedLine === 1) { | ||
mapping.generatedColumn += columnShift; | ||
/** | ||
* Find the mapping that best matches the hypothetical "needle" mapping that | ||
* we are searching for in the given "haystack" of mappings. | ||
*/ | ||
_findMapping(aNeedle, aMappings, aLineName, | ||
aColumnName, aComparator, aBias) { | ||
// To return the position we are searching for, we must first find the | ||
// mapping for the given position and then return the opposite position it | ||
// points to. Because the mappings are sorted, we can use binary search to | ||
// find the best mapping. | ||
if (aNeedle[aLineName] <= 0) { | ||
throw new TypeError("Line must be greater than or equal to 1, got " | ||
+ aNeedle[aLineName]); | ||
} | ||
if (aNeedle[aColumnName] < 0) { | ||
throw new TypeError("Column must be greater than or equal to 0, got " | ||
+ aNeedle[aColumnName]); | ||
} | ||
return binarySearch.search(aNeedle, aMappings, aComparator, aBias); | ||
} | ||
allGeneratedPositionsFor(aArgs) { | ||
const line = util.getArg(aArgs, "line"); | ||
// When there is no exact match, BasicSourceMapConsumer.prototype._findMapping | ||
// returns the index of the closest mapping less than the needle. By | ||
// setting needle.originalColumn to 0, we thus find the last mapping for | ||
// the given line, provided such a mapping exists. | ||
const needle = { | ||
source: util.getArg(aArgs, "source"), | ||
originalLine: line, | ||
originalColumn: util.getArg(aArgs, "column", 0) | ||
}; | ||
needle.source = this._findSourceIndex(needle.source); | ||
if (needle.source < 0) { | ||
return []; | ||
} | ||
if (needle.originalLine < 1) { | ||
throw new Error("Line numbers must be >= 1"); | ||
} | ||
if (needle.originalColumn < 0) { | ||
throw new Error("Column numbers must be >= 0"); | ||
} | ||
const mappings = []; | ||
let index = this._findMapping(needle, | ||
this._originalMappings, | ||
"originalLine", | ||
"originalColumn", | ||
util.compareByOriginalPositions, | ||
binarySearch.LEAST_UPPER_BOUND); | ||
if (index >= 0) { | ||
let mapping = this._originalMappings[index]; | ||
if (aArgs.column === undefined) { | ||
const originalLine = mapping.originalLine; | ||
// Iterate until either we run out of mappings, or we run into | ||
// a mapping for a different line than the one we found. Since | ||
// mappings are sorted, this is guaranteed to find all mappings for | ||
// the line we found. | ||
while (mapping && mapping.originalLine === originalLine) { | ||
let lastColumn = mapping.lastGeneratedColumn; | ||
if (this._computedColumnSpans && lastColumn === null) { | ||
lastColumn = Infinity; | ||
if (typeof mapping.lastGeneratedColumn === "number") { | ||
mapping.lastGeneratedColumn += columnShift; | ||
} | ||
mappings.push({ | ||
line: util.getArg(mapping, "generatedLine", null), | ||
column: util.getArg(mapping, "generatedColumn", null), | ||
lastColumn, | ||
}); | ||
} | ||
mapping = this._originalMappings[++index]; | ||
if ( | ||
mapping.lastGeneratedColumn === Infinity && | ||
nextSection && | ||
mapping.generatedLine === nextSection.generatedOffset.generatedLine | ||
) { | ||
mapping.lastGeneratedColumn = | ||
nextSection.generatedOffset.generatedColumn - 2; | ||
} | ||
} else { | ||
const originalColumn = mapping.originalColumn; | ||
mapping.generatedLine += lineShift; | ||
// Iterate until either we run out of mappings, or we run into | ||
// a mapping for a different line than the one we were searching for. | ||
// Since mappings are sorted, this is guaranteed to find all mappings for | ||
// the line we are searching for. | ||
while (mapping && | ||
mapping.originalLine === line && | ||
mapping.originalColumn == originalColumn) { | ||
let lastColumn = mapping.lastGeneratedColumn; | ||
if (this._computedColumnSpans && lastColumn === null) { | ||
lastColumn = Infinity; | ||
} | ||
mappings.push({ | ||
line: util.getArg(mapping, "generatedLine", null), | ||
column: util.getArg(mapping, "generatedColumn", null), | ||
lastColumn, | ||
}); | ||
aCallback.call(this, mapping); | ||
}, aContext, aOrder); | ||
}); | ||
} | ||
mapping = this._originalMappings[++index]; | ||
} | ||
} | ||
computeColumnSpans() { | ||
for (let i = 0; i < this._sections.length; i++) { | ||
this._sections[i].consumer.computeColumnSpans(); | ||
} | ||
return mappings; | ||
} | ||
@@ -1246,0 +1008,0 @@ |
636
lib/util.js
@@ -8,2 +8,4 @@ /* -*- Mode: js; js-indent-level: 2; -*- */ | ||
const URL = require("./url"); | ||
/** | ||
@@ -30,257 +32,2 @@ * This is a helper function for getting values from parameter/options | ||
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() { | ||
@@ -356,50 +103,27 @@ const obj = Object.create(null); | ||
/** | ||
* 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; | ||
function strcmp(aStr1, aStr2) { | ||
if (aStr1 === aStr2) { | ||
return 0; | ||
} | ||
cmp = mappingA.originalLine - mappingB.originalLine; | ||
if (cmp !== 0) { | ||
return cmp; | ||
if (aStr1 === null) { | ||
return 1; // aStr2 !== null | ||
} | ||
cmp = mappingA.originalColumn - mappingB.originalColumn; | ||
if (cmp !== 0 || onlyCompareOriginal) { | ||
return cmp; | ||
if (aStr2 === null) { | ||
return -1; // aStr1 !== null | ||
} | ||
cmp = mappingA.generatedColumn - mappingB.generatedColumn; | ||
if (cmp !== 0) { | ||
return cmp; | ||
if (aStr1 > aStr2) { | ||
return 1; | ||
} | ||
cmp = mappingA.generatedLine - mappingB.generatedLine; | ||
if (cmp !== 0) { | ||
return cmp; | ||
} | ||
return strcmp(mappingA.name, mappingB.name); | ||
return -1; | ||
} | ||
exports.compareByOriginalPositions = compareByOriginalPositions; | ||
/** | ||
* Comparator between two mappings with deflated source and name indices where | ||
* Comparator between two mappings with inflated source and name strings 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) { | ||
function compareByGeneratedPositionsInflated(mappingA, mappingB) { | ||
let cmp = mappingA.generatedLine - mappingB.generatedLine; | ||
@@ -411,3 +135,3 @@ if (cmp !== 0) { | ||
cmp = mappingA.generatedColumn - mappingB.generatedColumn; | ||
if (cmp !== 0 || onlyCompareGenerated) { | ||
if (cmp !== 0) { | ||
return cmp; | ||
@@ -433,68 +157,252 @@ } | ||
} | ||
exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated; | ||
exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated; | ||
function strcmp(aStr1, aStr2) { | ||
if (aStr1 === aStr2) { | ||
return 0; | ||
} | ||
/** | ||
* 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; | ||
if (aStr1 === null) { | ||
return 1; // aStr2 !== null | ||
} | ||
// We use 'http' as the base here because we want URLs processed relative | ||
// to the safe base to be treated as "special" URLs during parsing using | ||
// the WHATWG URL parsing. This ensures that backslash normalization | ||
// applies to the path and such. | ||
const PROTOCOL = "http:"; | ||
const PROTOCOL_AND_HOST = `${PROTOCOL}//host`; | ||
if (aStr2 === null) { | ||
return -1; // aStr1 !== null | ||
/** | ||
* Make it easy to create small utilities that tweak a URL's path. | ||
*/ | ||
function createSafeHandler(cb) { | ||
return input => { | ||
const type = getURLType(input); | ||
const base = buildSafeBase(input); | ||
const url = new URL(input, base); | ||
cb(url); | ||
const result = url.toString(); | ||
if (type === "absolute") { | ||
return result; | ||
} else if (type === "scheme-relative") { | ||
return result.slice(PROTOCOL.length); | ||
} else if (type === "path-absolute") { | ||
return result.slice(PROTOCOL_AND_HOST.length); | ||
} | ||
// This assumes that the callback will only change | ||
// the path, search and hash values. | ||
return computeRelativeURL(base, result); | ||
}; | ||
} | ||
function withBase(url, base) { | ||
return new URL(url, base).toString(); | ||
} | ||
function buildUniqueSegment(prefix, str) { | ||
let id = 0; | ||
do { | ||
const ident = prefix + (id++); | ||
if (str.indexOf(ident) === -1) return ident; | ||
} while (true); | ||
} | ||
function buildSafeBase(str) { | ||
const maxDotParts = str.split("..").length - 1; | ||
// If we used a segment that also existed in `str`, then we would be unable | ||
// to compute relative paths. For example, if `segment` were just "a": | ||
// | ||
// const url = "../../a/" | ||
// const base = buildSafeBase(url); // http://host/a/a/ | ||
// const joined = "http://host/a/"; | ||
// const result = relative(base, joined); | ||
// | ||
// Expected: "../../a/"; | ||
// Actual: "a/" | ||
// | ||
const segment = buildUniqueSegment("p", str); | ||
let base = `${PROTOCOL_AND_HOST}/`; | ||
for (let i = 0; i < maxDotParts; i++) { | ||
base += `${segment}/`; | ||
} | ||
return base; | ||
} | ||
if (aStr1 > aStr2) { | ||
return 1; | ||
const ABSOLUTE_SCHEME = /^[A-Za-z0-9\+\-\.]+:/; | ||
function getURLType(url) { | ||
if (url[0] === "/") { | ||
if (url[1] === "/") return "scheme-relative"; | ||
return "path-absolute"; | ||
} | ||
return -1; | ||
return ABSOLUTE_SCHEME.test(url) ? "absolute" : "path-relative"; | ||
} | ||
/** | ||
* Comparator between two mappings with inflated source and name strings where | ||
* the generated positions are compared. | ||
* Given two URLs that are assumed to be on the same | ||
* protocol/host/user/password build a relative URL from the | ||
* path, params, and hash values. | ||
* | ||
* @param rootURL The root URL that the target will be relative to. | ||
* @param targetURL The target that the relative URL points to. | ||
* @return A rootURL-relative, normalized URL value. | ||
*/ | ||
function compareByGeneratedPositionsInflated(mappingA, mappingB) { | ||
let cmp = mappingA.generatedLine - mappingB.generatedLine; | ||
if (cmp !== 0) { | ||
return cmp; | ||
function computeRelativeURL(rootURL, targetURL) { | ||
if (typeof rootURL === "string") rootURL = new URL(rootURL); | ||
if (typeof targetURL === "string") targetURL = new URL(targetURL); | ||
const targetParts = targetURL.pathname.split("/"); | ||
const rootParts = rootURL.pathname.split("/"); | ||
// If we've got a URL path ending with a "/", we remove it since we'd | ||
// otherwise be relative to the wrong location. | ||
if (rootParts.length > 0 && !rootParts[rootParts.length - 1]) { | ||
rootParts.pop(); | ||
} | ||
cmp = mappingA.generatedColumn - mappingB.generatedColumn; | ||
if (cmp !== 0) { | ||
return cmp; | ||
while ( | ||
targetParts.length > 0 && | ||
rootParts.length > 0 && | ||
targetParts[0] === rootParts[0] | ||
) { | ||
targetParts.shift(); | ||
rootParts.shift(); | ||
} | ||
cmp = strcmp(mappingA.source, mappingB.source); | ||
if (cmp !== 0) { | ||
return cmp; | ||
const relativePath = rootParts | ||
.map(() => "..") | ||
.concat(targetParts) | ||
.join("/"); | ||
return relativePath + targetURL.search + targetURL.hash; | ||
} | ||
/** | ||
* Given a URL, ensure that it is treated as a directory URL. | ||
* | ||
* @param url | ||
* @return A normalized URL value. | ||
*/ | ||
const ensureDirectory = createSafeHandler(url => { | ||
url.pathname = url.pathname.replace(/\/?$/, "/"); | ||
}); | ||
/** | ||
* Given a URL, strip off any filename if one is present. | ||
* | ||
* @param url | ||
* @return A normalized URL value. | ||
*/ | ||
const trimFilename = createSafeHandler(url => { | ||
url.href = new URL(".", url.toString()).toString(); | ||
}); | ||
/** | ||
* Normalize a given URL. | ||
* * Convert backslashes. | ||
* * Remove any ".." and "." segments. | ||
* | ||
* @param url | ||
* @return A normalized URL value. | ||
*/ | ||
const normalize = createSafeHandler(url => {}); | ||
exports.normalize = normalize; | ||
/** | ||
* Joins two paths/URLs. | ||
* | ||
* All returned URLs will be normalized. | ||
* | ||
* @param aRoot The root path or URL. Assumed to reference a directory. | ||
* @param aPath The path or URL to be joined with the root. | ||
* @return A joined and normalized URL value. | ||
*/ | ||
function join(aRoot, aPath) { | ||
const pathType = getURLType(aPath); | ||
const rootType = getURLType(aRoot); | ||
aRoot = ensureDirectory(aRoot); | ||
if (pathType === "absolute") { | ||
return withBase(aPath, undefined); | ||
} | ||
if (rootType === "absolute") { | ||
return withBase(aPath, aRoot); | ||
} | ||
cmp = mappingA.originalLine - mappingB.originalLine; | ||
if (cmp !== 0) { | ||
return cmp; | ||
if (pathType === "scheme-relative") { | ||
return normalize(aPath); | ||
} | ||
if (rootType === "scheme-relative") { | ||
return withBase(aPath, withBase(aRoot, PROTOCOL_AND_HOST)).slice(PROTOCOL.length); | ||
} | ||
cmp = mappingA.originalColumn - mappingB.originalColumn; | ||
if (cmp !== 0) { | ||
return cmp; | ||
if (pathType === "path-absolute") { | ||
return normalize(aPath); | ||
} | ||
if (rootType === "path-absolute") { | ||
return withBase(aPath, withBase(aRoot, PROTOCOL_AND_HOST)).slice(PROTOCOL_AND_HOST.length); | ||
} | ||
return strcmp(mappingA.name, mappingB.name); | ||
const base = buildSafeBase(aPath + aRoot); | ||
const newPath = withBase(aPath, withBase(aRoot, base)); | ||
return computeRelativeURL(base, newPath); | ||
} | ||
exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated; | ||
exports.join = join; | ||
/** | ||
* Strip any JSON XSSI avoidance prefix from the string (as documented | ||
* in the source maps specification), and then parse the string as | ||
* JSON. | ||
* Make a path relative to a URL or another path. If returning a | ||
* relative URL is not possible, the original target will be returned. | ||
* All returned URLs will be normalized. | ||
* | ||
* @param aRoot The root path or URL. | ||
* @param aPath The path or URL to be made relative to aRoot. | ||
* @return A rootURL-relative (if possible), normalized URL value. | ||
*/ | ||
function parseSourceMapInput(str) { | ||
return JSON.parse(str.replace(/^\)]}'[^\n]*\n/, "")); | ||
function relative(rootURL, targetURL) { | ||
const result = relativeIfPossible(rootURL, targetURL); | ||
return typeof result === "string" ? result : normalize(targetURL); | ||
} | ||
exports.parseSourceMapInput = parseSourceMapInput; | ||
exports.relative = relative; | ||
function relativeIfPossible(rootURL, targetURL) { | ||
const urlType = getURLType(rootURL); | ||
if (urlType !== getURLType(targetURL)) { | ||
return null; | ||
} | ||
const base = buildSafeBase(rootURL + targetURL); | ||
const root = new URL(rootURL, base); | ||
const target = new URL(targetURL, base); | ||
try { | ||
new URL("", target.toString()); | ||
} catch (err) { | ||
// Bail if the URL doesn't support things being relative to it, | ||
// For example, data: and blob: URLs. | ||
return null; | ||
} | ||
if ( | ||
target.protocol !== root.protocol || | ||
target.user !== root.user || | ||
target.password !== root.password || | ||
target.hostname !== root.hostname || | ||
target.port !== root.port | ||
) { | ||
return null; | ||
} | ||
return computeRelativeURL(root, target); | ||
} | ||
/** | ||
@@ -505,48 +413,32 @@ * Compute the URL of a source given the the source root, the source's | ||
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 source map spec states that "sourceRoot" and "sources" entries are to be appended. While | ||
// that is a little vague, implementations have generally interpreted that as joining the | ||
// URLs with a `/` between then, assuming the "sourceRoot" doesn't already end with one. | ||
// For example, | ||
// | ||
// 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); | ||
// sourceRoot: "some-dir", | ||
// sources: ["/some-path.js"] | ||
// | ||
// and | ||
// | ||
// sourceRoot: "some-dir/", | ||
// sources: ["/some-path.js"] | ||
// | ||
// must behave as "some-dir/some-path.js". | ||
// | ||
// With this library's the transition to a more URL-focused implementation, that behavior is | ||
// preserved here. To acheive that, we trim the "/" from absolute-path when a sourceRoot value | ||
// is present in order to make the sources entries behave as if they are relative to the | ||
// "sourceRoot", as they would have if the two strings were simply concated. | ||
if (sourceRoot && getURLType(sourceURL) === "path-absolute") { | ||
sourceURL = sourceURL.replace(/^\//, ""); | ||
} | ||
return normalize(sourceURL); | ||
let url = normalize(sourceURL || ""); | ||
// Parsing URLs can be expensive, so we only perform these joins when needed. | ||
if (sourceRoot) url = join(sourceRoot, url); | ||
if (sourceMapURL) url = join(trimFilename(sourceMapURL), url); | ||
return url; | ||
} | ||
exports.computeSourceURL = computeSourceURL; |
{ | ||
"name": "source-map", | ||
"description": "Generates and consumes source maps", | ||
"version": "0.7.3", | ||
"version": "0.8.0-beta.0", | ||
"homepage": "https://github.com/mozilla/source-map", | ||
@@ -51,8 +51,14 @@ "author": "Nick Fitzgerald <nfitzgerald@mozilla.com>", | ||
"types": "./source-map.d.ts", | ||
"browser": { | ||
"./lib/url.js": "./lib/url-browser.js", | ||
"./lib/read-wasm.js": "./lib/read-wasm-browser.js" | ||
}, | ||
"files": [ | ||
"source-map.js", | ||
"source-map.d.ts", | ||
"lib/", | ||
"dist/source-map.js" | ||
"lib/" | ||
], | ||
"publishConfig": { | ||
"tag": "next" | ||
}, | ||
"engines": { | ||
@@ -65,6 +71,3 @@ "node": ">= 8" | ||
"prebuild": "npm run lint", | ||
"build": "webpack --color", | ||
"pretest": "npm run build", | ||
"test": "node test/run-tests.js", | ||
"precoverage": "npm run build", | ||
"coverage": "nyc node test/run-tests.js", | ||
@@ -80,3 +83,3 @@ "setup": "mkdir -p coverage && cp -n .waiting.html coverage/index.html || true", | ||
"devDependencies": { | ||
"doctoc": "^0.15.0", | ||
"doctoc": "^1.3.1", | ||
"eslint": "^4.19.1", | ||
@@ -86,4 +89,3 @@ "live-server": "^1.2.0", | ||
"nyc": "^11.7.1", | ||
"watch": "^1.0.2", | ||
"webpack": "^3.10" | ||
"watch": "^1.0.2" | ||
}, | ||
@@ -93,3 +95,6 @@ "nyc": { | ||
}, | ||
"typings": "source-map" | ||
"typings": "source-map", | ||
"dependencies": { | ||
"whatwg-url": "^7.0.0" | ||
} | ||
} |
@@ -211,3 +211,3 @@ # Source Map | ||
* `"lib/mappings.wasm"`: A `String` containing the URL of the | ||
`lib/mappings.wasm` file. | ||
`lib/mappings.wasm` file, or an `ArrayBuffer` with the contents of `lib/mappings.wasm`. | ||
@@ -214,0 +214,0 @@ ```js |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
6
21
1
2
196661
1
2885
+ Addedwhatwg-url@^7.0.0
+ Addedlodash.sortby@4.7.0(transitive)
+ Addedpunycode@2.3.1(transitive)
+ Addedtr46@1.0.1(transitive)
+ Addedwebidl-conversions@4.0.2(transitive)
+ Addedwhatwg-url@7.1.0(transitive)