Skip to content

Commit 3c3fbfa

Browse files
committed
Wiring up existing source maps of transpiled codes
When `generateSourceMaps` option is enabled, r.js now detects if the module to be concatenated already declares a source map. It tries to load the existing source map, and translates line numbers of all mappings in that source map to those of the final output. This fixes most of requirejs#470, and works well as long as `optimize` option is set to `none`. Some work still remains to pass a correct `--in-source-map` option to UglifyJS when also uglifying. Currently, UglifyJS ignores r.js's carefully generated source map producing a bogus one if `optimize` option is set to `uglify2`. Used https://github.com/lydell/source-map-url (v0.2.0) for detecting sourceMappingURL= comments from the code.
1 parent 6391520 commit 3c3fbfa

File tree

3 files changed

+138
-17
lines changed

3 files changed

+138
-17
lines changed

build/jslib/build.js

+59-17
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ define(function (require) {
2424
env = require('env'),
2525
commonJs = require('commonJs'),
2626
SourceMapGenerator = require('source-map/source-map-generator'),
27+
SourceMapConsumer = require('source-map/source-map-consumer'),
28+
sourceMappingURL = require('source-map-url'),
2729
hasProp = lang.hasProp,
2830
getOwn = lang.getOwn,
2931
falseProp = lang.falseProp,
@@ -1910,27 +1912,67 @@ define(function (require) {
19101912
}
19111913
}
19121914

1915+
//See if the file already has a source map
1916+
var sourceMapConsumer = null;
1917+
try {
1918+
var existingSourceMapURL = sourceMappingURL.get(singleContents);
1919+
if (existingSourceMapURL) {
1920+
//Load referenced source map
1921+
var sourceMapContents = file.readFile(build.makeAbsPath(existingSourceMapURL, file.parent(path)));
1922+
var existingSourceMap = JSON.parse(String(sourceMapContents));
1923+
sourceMapConsumer = existingSourceMap ? new SourceMapConsumer.SourceMapConsumer(existingSourceMap) : null;
1924+
}
1925+
} catch (e) {
1926+
sourceMapConsumer = null;
1927+
}
1928+
//Remove the sourceMappingURL comment, so it doesn't
1929+
//interfere with the new one to be generated.
1930+
singleContents = sourceMappingURL.remove(singleContents);
1931+
19131932
sourceMapLineNumber = fileContents.split('\n').length - 1;
19141933
lineCount = singleContents.split('\n').length;
1915-
for (var i = 1; i <= lineCount; i += 1) {
1916-
sourceMapGenerator.addMapping({
1917-
generated: {
1918-
line: sourceMapLineNumber + i,
1919-
column: 0
1920-
},
1921-
original: {
1922-
line: i,
1923-
column: 0
1924-
},
1925-
source: sourceMapPath
1934+
if (sourceMapConsumer) {
1935+
sourceMapConsumer.eachMapping(function(m) {
1936+
if (m.generatedLine > lineCount)
1937+
return;
1938+
//Translate the line numbers in the existing source map
1939+
sourceMapGenerator.addMapping({
1940+
generated: {
1941+
line: m.generatedLine + sourceMapLineNumber,
1942+
column: m.generatedColumn
1943+
},
1944+
original: {
1945+
line: m.originalLine,
1946+
column: m.originalColumn
1947+
},
1948+
source: m.source,
1949+
name: m.name
1950+
});
19261951
});
1927-
}
19281952

1929-
//Store the content of the original in the source
1930-
//map since other transforms later like minification
1931-
//can mess up translating back to the original
1932-
//source.
1933-
sourceMapGenerator.setSourceContent(sourceMapPath, singleContents);
1953+
// TODO Preserve sourcesContent of existing source map
1954+
// TODO by calling sourceMapGenerator.setSourceContent() for each non-null item in sourceMapConsumer.sourcesContent
1955+
} else {
1956+
for (var i = 1; i <= lineCount; i += 1) {
1957+
sourceMapGenerator.addMapping({
1958+
generated: {
1959+
line: sourceMapLineNumber + i,
1960+
column: 0
1961+
},
1962+
original: {
1963+
line: i,
1964+
column: 0
1965+
},
1966+
source: sourceMapPath
1967+
});
1968+
}
1969+
1970+
//Store the content of the original in the source
1971+
//map since other transforms later like minification
1972+
//can mess up translating back to the original
1973+
//source.
1974+
sourceMapGenerator.setSourceContent(sourceMapPath, singleContents);
1975+
}
19341976
}
19351977

19361978
//Add the file to the final contents

build/jslib/source-map-url.js

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright 2014 Simon Lydell
2+
3+
void (function(root, factory) {
4+
if (typeof define === "function" && define.amd) {
5+
define(factory)
6+
} else if (typeof exports === "object") {
7+
module.exports = factory()
8+
} else {
9+
root.sourceMappingURL = factory()
10+
}
11+
}(this, function(undefined) {
12+
13+
var innerRegex = /[#@] sourceMappingURL=([^\s'"]*)/
14+
var newlineRegex = /\r\n?|\n/
15+
16+
var regex = RegExp(
17+
"(^|(?:" + newlineRegex.source + "))" +
18+
"(?:" +
19+
"/\\*" +
20+
"(?:\\s*(?:" + newlineRegex.source + ")(?://)?)?" +
21+
"(?:" + innerRegex.source + ")" +
22+
"\\s*" +
23+
"\\*/" +
24+
"|" +
25+
"//(?:" + innerRegex.source + ")" +
26+
")" +
27+
"\\s*(?:$|(?:" + newlineRegex.source + "))"
28+
)
29+
30+
function SourceMappingURL(commentSyntax) {
31+
this._commentSyntax = commentSyntax
32+
}
33+
34+
SourceMappingURL.prototype.regex = regex
35+
SourceMappingURL.prototype._innerRegex = innerRegex
36+
SourceMappingURL.prototype._newlineRegex = newlineRegex
37+
38+
SourceMappingURL.prototype.get = function(code) {
39+
var match = code.match(this.regex)
40+
if (!match) {
41+
return null
42+
}
43+
return match[2] || match[3] || ""
44+
}
45+
46+
SourceMappingURL.prototype.set = function(code, url, commentSyntax) {
47+
if (!commentSyntax) {
48+
commentSyntax = this._commentSyntax
49+
}
50+
// Use a newline present in the code, or fall back to '\n'.
51+
var newline = String(code.match(this._newlineRegex) || "\n")
52+
var open = commentSyntax[0], close = commentSyntax[1] || ""
53+
code = this.remove(code)
54+
return code + newline + open + "# sourceMappingURL=" + url + close
55+
}
56+
57+
SourceMappingURL.prototype.remove = function(code) {
58+
return code.replace(this.regex, "")
59+
}
60+
61+
SourceMappingURL.prototype.insertBefore = function(code, string) {
62+
var match = code.match(this.regex)
63+
if (match) {
64+
var hasNewline = Boolean(match[1])
65+
return code.slice(0, match.index) +
66+
string +
67+
(hasNewline ? "" : "\n") +
68+
code.slice(match.index)
69+
} else {
70+
return code + string
71+
}
72+
}
73+
74+
SourceMappingURL.prototype.SourceMappingURL = SourceMappingURL
75+
76+
return new SourceMappingURL(["/*", " */"])
77+
78+
}));

dist.js

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ var fs = require('fs'),
5757
'build/jslib/source-map/source-node.js',
5858
'build/jslib/source-map/util.js',
5959
'build/jslib/source-map.js',
60+
'build/jslib/source-map-url.js',
6061
'build/jslib/uglifyjs2.js',
6162
'build/jslib/parse.js',
6263
'build/jslib/transform.js',

0 commit comments

Comments
 (0)