Skip to content

Commit 54825b8

Browse files
committed
Change sourceRoot resolution to match the spec
The source-map library has long implemented sourceRoot resolution using URL resolution. However, the source map specification is reasonably clear that concatenation is to be used instead. There is more information in these bugs: mozilla#62 https://bugzilla.mozilla.org/show_bug.cgi?id=977463 This patch changes the resolution to match what Chrome does. This is an incompatible change; but, I believe, better for interoperability. This patch also adds an optional sourceMapURL parameter to the SourceMapConsumer constructor. This allows us to implement the full source URL resolution algorithm in the source-map library. Fixes mozilla#62
1 parent 20ab726 commit 54825b8

16 files changed

+1118
-266
lines changed

dist/source-map.debug.js

Lines changed: 97 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/source-map.js

Lines changed: 96 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,59 @@ return /******/ (function(modules) { // webpackBootstrap
11541154
}
11551155
exports.parseSourceMapInput = parseSourceMapInput;
11561156

1157+
/**
1158+
* Compute the URL of a source given the the source root, the source's
1159+
* URL, and the source map's URL.
1160+
*/
1161+
function computeSourceURL(sourceRoot, sourceURL, sourceMapURL) {
1162+
sourceURL = sourceURL || '';
1163+
1164+
if (sourceRoot) {
1165+
// This follows what Chrome does.
1166+
if (sourceRoot[sourceRoot.length - 1] !== '/' && sourceURL[0] !== '/') {
1167+
sourceRoot += '/';
1168+
}
1169+
// The spec says:
1170+
// Line 4: An optional source root, useful for relocating source
1171+
// files on a server or removing repeated values in the
1172+
// “sources” entry. This value is prepended to the individual
1173+
// entries in the “source” field.
1174+
sourceURL = sourceRoot + sourceURL;
1175+
}
1176+
1177+
// Historically, SourceMapConsumer did not take the sourceMapURL as
1178+
// a parameter. This mode is still somewhat supported, which is why
1179+
// this code block is conditional. However, it's preferable to pass
1180+
// the source map URL to SourceMapConsumer, so that this function
1181+
// can implement the source URL resolution algorithm as outlined in
1182+
// the spec. This block is basically the equivalent of:
1183+
// new URL(sourceURL, sourceMapURL).toString()
1184+
// ... except it avoids using URL, which wasn't available in the
1185+
// older releases of node still supported by this library.
1186+
//
1187+
// The spec says:
1188+
// If the sources are not absolute URLs after prepending of the
1189+
// “sourceRoot”, the sources are resolved relative to the
1190+
// SourceMap (like resolving script src in a html document).
1191+
if (sourceMapURL) {
1192+
var parsed = urlParse(sourceMapURL);
1193+
if (!parsed) {
1194+
throw new Error("sourceMapURL could not be parsed");
1195+
}
1196+
if (parsed.path) {
1197+
// Strip the last path component, but keep the "/".
1198+
var index = parsed.path.lastIndexOf('/');
1199+
if (index >= 0) {
1200+
parsed.path = parsed.path.substring(0, index + 1);
1201+
}
1202+
}
1203+
sourceURL = join(urlGenerate(parsed), sourceURL);
1204+
}
1205+
1206+
return normalize(sourceURL);
1207+
}
1208+
exports.computeSourceURL = computeSourceURL;
1209+
11571210

11581211
/***/ }),
11591212
/* 5 */
@@ -1384,15 +1437,15 @@ return /******/ (function(modules) { // webpackBootstrap
13841437
var base64VLQ = __webpack_require__(2);
13851438
var quickSort = __webpack_require__(9).quickSort;
13861439

1387-
function SourceMapConsumer(aSourceMap) {
1440+
function SourceMapConsumer(aSourceMap, aSourceMapURL) {
13881441
var sourceMap = aSourceMap;
13891442
if (typeof aSourceMap === 'string') {
13901443
sourceMap = util.parseSourceMapInput(aSourceMap);
13911444
}
13921445

13931446
return sourceMap.sections != null
1394-
? new IndexedSourceMapConsumer(sourceMap)
1395-
: new BasicSourceMapConsumer(sourceMap);
1447+
? new IndexedSourceMapConsumer(sourceMap, aSourceMapURL)
1448+
: new BasicSourceMapConsumer(sourceMap, aSourceMapURL);
13961449
}
13971450

13981451
SourceMapConsumer.fromSourceMap = function(aSourceMap) {
@@ -1518,9 +1571,7 @@ return /******/ (function(modules) { // webpackBootstrap
15181571
var sourceRoot = this.sourceRoot;
15191572
mappings.map(function (mapping) {
15201573
var source = mapping.source === null ? null : this._sources.at(mapping.source);
1521-
if (source != null && sourceRoot != null) {
1522-
source = util.join(sourceRoot, source);
1523-
}
1574+
source = util.computeSourceURL(sourceRoot, source, this._sourceMapURL);
15241575
return {
15251576
source: source,
15261577
generatedLine: mapping.generatedLine,
@@ -1634,7 +1685,7 @@ return /******/ (function(modules) { // webpackBootstrap
16341685
* query for information about the original file positions by giving it a file
16351686
* position in the generated source.
16361687
*
1637-
* The only parameter is the raw source map (either as a JSON string, or
1688+
* The first parameter is the raw source map (either as a JSON string, or
16381689
* already parsed to an object). According to the spec, source maps have the
16391690
* following attributes:
16401691
*
@@ -1657,9 +1708,13 @@ return /******/ (function(modules) { // webpackBootstrap
16571708
* mappings: "AA,AB;;ABCDE;"
16581709
* }
16591710
*
1711+
* The second parameter, if given, is a string whose value is the URL
1712+
* at which the source map was found. This URL is used to compute the
1713+
* sources array.
1714+
*
16601715
* [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
16611716
*/
1662-
function BasicSourceMapConsumer(aSourceMap) {
1717+
function BasicSourceMapConsumer(aSourceMap, aSourceMapURL) {
16631718
var sourceMap = aSourceMap;
16641719
if (typeof aSourceMap === 'string') {
16651720
sourceMap = util.parseSourceMapInput(aSourceMap);
@@ -1711,6 +1766,7 @@ return /******/ (function(modules) { // webpackBootstrap
17111766
this.sourceRoot = sourceRoot;
17121767
this.sourcesContent = sourcesContent;
17131768
this._mappings = mappings;
1769+
this._sourceMapURL = aSourceMapURL;
17141770
this.file = file;
17151771
}
17161772

@@ -1722,10 +1778,12 @@ return /******/ (function(modules) { // webpackBootstrap
17221778
*
17231779
* @param SourceMapGenerator aSourceMap
17241780
* The source map that will be consumed.
1781+
* @param String aSourceMapURL
1782+
* The URL at which the source map can be found (optional)
17251783
* @returns BasicSourceMapConsumer
17261784
*/
17271785
BasicSourceMapConsumer.fromSourceMap =
1728-
function SourceMapConsumer_fromSourceMap(aSourceMap) {
1786+
function SourceMapConsumer_fromSourceMap(aSourceMap, aSourceMapURL) {
17291787
var smc = Object.create(BasicSourceMapConsumer.prototype);
17301788

17311789
var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
@@ -1734,6 +1792,7 @@ return /******/ (function(modules) { // webpackBootstrap
17341792
smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
17351793
smc.sourceRoot);
17361794
smc.file = aSourceMap._file;
1795+
smc._sourceMapURL = aSourceMapURL;
17371796

17381797
// Because we are modifying the entries (by converting string sources and
17391798
// names to indices into the sources and names ArraySets), we have to make
@@ -1781,7 +1840,7 @@ return /******/ (function(modules) { // webpackBootstrap
17811840
Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', {
17821841
get: function () {
17831842
return this._sources.toArray().map(function (s) {
1784-
return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s;
1843+
return util.computeSourceURL(this.sourceRoot, s, this._sourceMapURL);
17851844
}, this);
17861845
}
17871846
});
@@ -2005,9 +2064,7 @@ return /******/ (function(modules) { // webpackBootstrap
20052064
var source = util.getArg(mapping, 'source', null);
20062065
if (source !== null) {
20072066
source = this._sources.at(source);
2008-
if (this.sourceRoot != null) {
2009-
source = util.join(this.sourceRoot, source);
2010-
}
2067+
source = util.computeSourceURL(this.sourceRoot, source, this._sourceMapURL);
20112068
}
20122069
var name = util.getArg(mapping, 'name', null);
20132070
if (name !== null) {
@@ -2054,12 +2111,23 @@ return /******/ (function(modules) { // webpackBootstrap
20542111
return null;
20552112
}
20562113

2114+
var relativeSource = aSource;
20572115
if (this.sourceRoot != null) {
2058-
aSource = util.relative(this.sourceRoot, aSource);
2116+
relativeSource = util.relative(this.sourceRoot, relativeSource);
2117+
}
2118+
2119+
if (this._sources.has(relativeSource)) {
2120+
return this.sourcesContent[this._sources.indexOf(relativeSource)];
20592121
}
20602122

2061-
if (this._sources.has(aSource)) {
2062-
return this.sourcesContent[this._sources.indexOf(aSource)];
2123+
// Maybe aSource is an absolute URL as returned by |sources|. In
2124+
// this case we can't simply undo the transform.
2125+
var sourceArray = this.sources;
2126+
var i;
2127+
for (i = 0; i < sourceArray.length; ++i) {
2128+
if (sourceArray[i] == aSource) {
2129+
return this.sourcesContent[i];
2130+
}
20632131
}
20642132

20652133
var url;
@@ -2069,15 +2137,15 @@ return /******/ (function(modules) { // webpackBootstrap
20692137
// many users. We can help them out when they expect file:// URIs to
20702138
// behave like it would if they were running a local HTTP server. See
20712139
// https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
2072-
var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
2140+
var fileUriAbsPath = relativeSource.replace(/^file:\/\//, "");
20732141
if (url.scheme == "file"
20742142
&& this._sources.has(fileUriAbsPath)) {
20752143
return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
20762144
}
20772145

20782146
if ((!url.path || url.path == "/")
2079-
&& this._sources.has("/" + aSource)) {
2080-
return this.sourcesContent[this._sources.indexOf("/" + aSource)];
2147+
&& this._sources.has("/" + relativeSource)) {
2148+
return this.sourcesContent[this._sources.indexOf("/" + relativeSource)];
20812149
}
20822150
}
20832151

@@ -2089,7 +2157,7 @@ return /******/ (function(modules) { // webpackBootstrap
20892157
return null;
20902158
}
20912159
else {
2092-
throw new Error('"' + aSource + '" is not in the SourceMap.');
2160+
throw new Error('"' + relativeSource + '" is not in the SourceMap.');
20932161
}
20942162
};
20952163

@@ -2173,7 +2241,7 @@ return /******/ (function(modules) { // webpackBootstrap
21732241
* that it takes "indexed" source maps (i.e. ones with a "sections" field) as
21742242
* input.
21752243
*
2176-
* The only parameter is a raw source map (either as a JSON string, or already
2244+
* The first parameter is a raw source map (either as a JSON string, or already
21772245
* parsed to an object). According to the spec for indexed source maps, they
21782246
* have the following attributes:
21792247
*
@@ -2210,9 +2278,13 @@ return /******/ (function(modules) { // webpackBootstrap
22102278
* }],
22112279
* }
22122280
*
2281+
* The second parameter, if given, is a string whose value is the URL
2282+
* at which the source map was found. This URL is used to compute the
2283+
* sources array.
2284+
*
22132285
* [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
22142286
*/
2215-
function IndexedSourceMapConsumer(aSourceMap) {
2287+
function IndexedSourceMapConsumer(aSourceMap, aSourceMapURL) {
22162288
var sourceMap = aSourceMap;
22172289
if (typeof aSourceMap === 'string') {
22182290
sourceMap = util.parseSourceMapInput(aSourceMap);
@@ -2255,7 +2327,7 @@ return /******/ (function(modules) { // webpackBootstrap
22552327
generatedLine: offsetLine + 1,
22562328
generatedColumn: offsetColumn + 1
22572329
},
2258-
consumer: new SourceMapConsumer(util.getArg(s, 'map'))
2330+
consumer: new SourceMapConsumer(util.getArg(s, 'map'), aSourceMapURL)
22592331
}
22602332
});
22612333
}
@@ -2441,9 +2513,7 @@ return /******/ (function(modules) { // webpackBootstrap
24412513
var mapping = sectionMappings[j];
24422514

24432515
var source = section.consumer._sources.at(mapping.source);
2444-
if (section.consumer.sourceRoot !== null) {
2445-
source = util.join(section.consumer.sourceRoot, source);
2446-
}
2516+
source = util.computeSourceURL(section.consumer.sourceRoot, source, this._sourceMapURL);
24472517
this._sources.add(source);
24482518
source = this._sources.indexOf(source);
24492519

dist/source-map.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/source-map.min.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)