From e2fdbfd1f1098de54afb93dfeb757a4608523f2f Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Tue, 14 Aug 2018 02:27:59 -0700 Subject: [PATCH] fix(index): resolve source maps with root-relative paths correctly (#68) --- index.js | 6 +- test/fixtures/.gitignore | 1 + .../absolute-sourceRoot-source-map.js | 3 + .../absolute-sourceRoot-source-map.txt | 2 + .../data/relative-sourceRoot-source-map.txt | 2 + .../relative-sourceRoot-source-map.js | 3 + .../relative-sourceRoot-source-map.map | 1 + test/index.test.js | 124 +++++++++++++++--- 8 files changed, 121 insertions(+), 21 deletions(-) create mode 100644 test/fixtures/.gitignore create mode 100644 test/fixtures/absolute-sourceRoot-source-map.js create mode 100644 test/fixtures/absolute-sourceRoot-source-map.txt create mode 100644 test/fixtures/data/relative-sourceRoot-source-map.txt create mode 100644 test/fixtures/relative-sourceRoot-source-map.js create mode 100644 test/fixtures/relative-sourceRoot-source-map.map diff --git a/index.js b/index.js index 5419ac3..85a4913 100644 --- a/index.js +++ b/index.js @@ -35,11 +35,11 @@ module.exports = function(input, inputMap) { map = JSON.parse(mapStr) } catch (e) { emitWarning("Cannot parse inline SourceMap '" + mapBase64.substr(0, 50) + "': " + e); - return untouched(); + return untouched(); } processMap(map, this.context, callback); } else { - resolve(this.context, loaderUtils.urlToRequest(url), function(err, result) { + resolve(this.context, loaderUtils.urlToRequest(url, true), function(err, result) { if(err) { emitWarning("Cannot find SourceMap '" + url + "': " + err); return untouched(); @@ -76,7 +76,7 @@ module.exports = function(input, inputMap) { delete map.sourceRoot; var missingSources = map.sourcesContent ? map.sources.slice(map.sourcesContent.length) : map.sources; async.map(missingSources, function(source, callback) { - resolve(context, loaderUtils.urlToRequest(source), function(err, result) { + resolve(context, loaderUtils.urlToRequest(source, true), function(err, result) { if(err) { emitWarning("Cannot find source file '" + source + "': " + err); return callback(null, null); diff --git a/test/fixtures/.gitignore b/test/fixtures/.gitignore new file mode 100644 index 0000000..46b6e08 --- /dev/null +++ b/test/fixtures/.gitignore @@ -0,0 +1 @@ +absolute-sourceRoot-source-map.map diff --git a/test/fixtures/absolute-sourceRoot-source-map.js b/test/fixtures/absolute-sourceRoot-source-map.js new file mode 100644 index 0000000..e10ac75 --- /dev/null +++ b/test/fixtures/absolute-sourceRoot-source-map.js @@ -0,0 +1,3 @@ +with SourceMap +//#sourceMappingURL=absolute-sourceRoot-source-map.map +// comment \ No newline at end of file diff --git a/test/fixtures/absolute-sourceRoot-source-map.txt b/test/fixtures/absolute-sourceRoot-source-map.txt new file mode 100644 index 0000000..7509bf5 --- /dev/null +++ b/test/fixtures/absolute-sourceRoot-source-map.txt @@ -0,0 +1,2 @@ +with SourceMap +// comment \ No newline at end of file diff --git a/test/fixtures/data/relative-sourceRoot-source-map.txt b/test/fixtures/data/relative-sourceRoot-source-map.txt new file mode 100644 index 0000000..7509bf5 --- /dev/null +++ b/test/fixtures/data/relative-sourceRoot-source-map.txt @@ -0,0 +1,2 @@ +with SourceMap +// comment \ No newline at end of file diff --git a/test/fixtures/relative-sourceRoot-source-map.js b/test/fixtures/relative-sourceRoot-source-map.js new file mode 100644 index 0000000..2d1807d --- /dev/null +++ b/test/fixtures/relative-sourceRoot-source-map.js @@ -0,0 +1,3 @@ +with SourceMap +//#sourceMappingURL=relative-sourceRoot-source-map.map +// comment \ No newline at end of file diff --git a/test/fixtures/relative-sourceRoot-source-map.map b/test/fixtures/relative-sourceRoot-source-map.map new file mode 100644 index 0000000..09fb0d2 --- /dev/null +++ b/test/fixtures/relative-sourceRoot-source-map.map @@ -0,0 +1 @@ +{"version":3,"file":"relative-sourceRoot-source-map.js","sourceRoot":"../fixtures/data/","sources":["relative-sourceRoot-source-map.txt"],"mappings":"AAAA"} \ No newline at end of file diff --git a/test/index.test.js b/test/index.test.js index 819f036..1191e24 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -11,7 +11,7 @@ function execLoader(filename, callback) { context: path.dirname(filename), resolve: function(context, request, callback) { process.nextTick(function() { - var p = path.join(context, request); + var p = path.isAbsolute(request) ? request : path.resolve(context, request); if(fs.existsSync(p)) callback(null, p); else @@ -40,8 +40,11 @@ function execLoader(filename, callback) { } describe("source-map-loader", function() { + const fixturesPath = path.join(__dirname, "fixtures"); + const dataPath = path.join(fixturesPath, "data"); + it("should leave normal files untouched", function(done) { - execLoader(path.join(__dirname, "fixtures", "normal-file.js"), function(err, res, map, deps, warns) { + execLoader(path.join(fixturesPath, "normal-file.js"), function(err, res, map, deps, warns) { should.equal(err, null); warns.should.be.eql([]); should.equal(res, "without SourceMap"), @@ -50,8 +53,9 @@ describe("source-map-loader", function() { done(); }); }); + it("should process inlined SourceMaps", function(done) { - execLoader(path.join(__dirname, "fixtures", "inline-source-map.js"), function(err, res, map, deps, warns) { + execLoader(path.join(fixturesPath, "inline-source-map.js"), function(err, res, map, deps, warns) { should.equal(err, null); warns.should.be.eql([]); should.equal(res, "with SourceMap\n// comment"), @@ -68,8 +72,9 @@ describe("source-map-loader", function() { done(); }); }); + it("should process external SourceMaps", function(done) { - execLoader(path.join(__dirname, "fixtures", "external-source-map.js"), function(err, res, map, deps, warns) { + execLoader(path.join(fixturesPath, "external-source-map.js"), function(err, res, map, deps, warns) { should.equal(err, null); warns.should.be.eql([]); should.equal(res, "with SourceMap\n// comment"), @@ -83,13 +88,14 @@ describe("source-map-loader", function() { "mappings":"AAAA" }); deps.should.be.eql([ - path.join(__dirname, "fixtures", "external-source-map.map") + path.join(fixturesPath, "external-source-map.map") ]); done(); }); }); + it("should process external SourceMaps (external sources)", function(done) { - execLoader(path.join(__dirname, "fixtures", "external-source-map2.js"), function(err, res, map, deps, warns) { + execLoader(path.join(fixturesPath, "external-source-map2.js"), function(err, res, map, deps, warns) { should.equal(err, null); warns.should.be.eql([]); should.equal(res, "with SourceMap\n// comment"), @@ -97,20 +103,21 @@ describe("source-map-loader", function() { "version":3, "file":"external-source-map2.js", "sources":[ - path.join(__dirname, "fixtures", "external-source-map2.txt") + path.join(fixturesPath, "external-source-map2.txt") ], "sourcesContent":["with SourceMap"], "mappings":"AAAA" }); deps.should.be.eql([ - path.join(__dirname, "fixtures", "data", "external-source-map2.map"), - path.join(__dirname, "fixtures", "external-source-map2.txt") + path.join(dataPath, "external-source-map2.map"), + path.join(fixturesPath, "external-source-map2.txt") ]); done(); }); }); + it("should use last SourceMap directive", function (done) { - execLoader(path.join(__dirname, "fixtures", "multi-source-map.js"), function (err, res, map, deps, warns) { + execLoader(path.join(fixturesPath, "multi-source-map.js"), function (err, res, map, deps, warns) { should.equal(err, null); warns.should.be.eql([]); should.equal(res, "with SourceMap\nanInvalidDirective = \"\\n/*# sourceMappingURL=data:application/json;base64,\"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+\" */\";\n// comment"), @@ -127,8 +134,9 @@ describe("source-map-loader", function() { done(); }); }); + it("should skip invalid base64 SourceMap", function (done) { - execLoader(path.join(__dirname, "fixtures", "invalid-inline-source-map.js"), function (err, res, map, deps, warns) { + execLoader(path.join(fixturesPath, "invalid-inline-source-map.js"), function (err, res, map, deps, warns) { should.equal(err, null); warns.should.be.eql([]); should.equal(res, "without SourceMap\n// @sourceMappingURL=data:application/source-map;base64,\"something invalid\"\n// comment"); @@ -138,7 +146,7 @@ describe("source-map-loader", function() { }); }); it("should warn on invalid base64 SourceMap", function (done) { - execLoader(path.join(__dirname, "fixtures", "invalid-inline-source-map2.js"), function (err, res, map, deps, warns) { + execLoader(path.join(fixturesPath, "invalid-inline-source-map2.js"), function (err, res, map, deps, warns) { should.equal(err, null); warns.should.matchEach( new RegExp("Cannot parse inline SourceMap 'invalid\/base64=': SyntaxError: Unexpected token") @@ -149,8 +157,9 @@ describe("source-map-loader", function() { done(); }); }); + it("should warn on invalid SourceMap", function (done) { - execLoader(path.join(__dirname, "fixtures", "invalid-source-map.js"), function (err, res, map, deps, warns) { + execLoader(path.join(fixturesPath, "invalid-source-map.js"), function (err, res, map, deps, warns) { should.equal(err, null); warns.should.matchEach( new RegExp("Cannot parse SourceMap 'invalid-source-map.map': SyntaxError: Unexpected string in JSON at position 102") @@ -158,13 +167,14 @@ describe("source-map-loader", function() { should.equal(res, "with SourceMap\n//#sourceMappingURL=invalid-source-map.map\n// comment"); should.equal(map, null); deps.should.be.eql([ - path.join(__dirname, "fixtures", "invalid-source-map.map") + path.join(fixturesPath, "invalid-source-map.map") ]); done(); }); }); + it("should warn on missing SourceMap", function(done) { - execLoader(path.join(__dirname, "fixtures", "missing-source-map.js"), function(err, res, map, deps, warns) { + execLoader(path.join(fixturesPath, "missing-source-map.js"), function(err, res, map, deps, warns) { should.equal(err, null); warns.should.be.eql([ "Cannot find SourceMap 'missing-source-map.map': Error: File not found" @@ -175,8 +185,9 @@ describe("source-map-loader", function() { done(); }); }); + it("should warn on missing source file", function(done) { - execLoader(path.join(__dirname, "fixtures", "missing-source-map2.js"), function(err, res, map, deps, warns) { + execLoader(path.join(fixturesPath, "missing-source-map2.js"), function(err, res, map, deps, warns) { should.equal(err, null); warns.should.be.eql([ "Cannot find source file 'missing-source-map2.txt': Error: File not found" @@ -192,14 +203,14 @@ describe("source-map-loader", function() { "mappings":"AAAA" }); deps.should.be.eql([ - path.join(__dirname, "fixtures", "missing-source-map2.map") + path.join(fixturesPath, "missing-source-map2.map") ]); done(); }); }); it("should process inlined SourceMaps with charset", function(done) { - execLoader(path.join(__dirname, "fixtures", "charset-inline-source-map.js"), function(err, res, map, deps, warns) { + execLoader(path.join(fixturesPath, "charset-inline-source-map.js"), function(err, res, map, deps, warns) { should.equal(err, null); warns.should.be.eql([]); should.equal(res, "with SourceMap\n// comment"), @@ -216,4 +227,81 @@ describe("source-map-loader", function() { done(); }); }); + + it("should support absolute sourceRoot paths in sourcemaps", (done) => { + const sourceRoot = path.join(fixturesPath); + const javaScriptFilename = "absolute-sourceRoot-source-map.js"; + const sourceFilename = "absolute-sourceRoot-source-map.txt"; + const rootRelativeSourcePath = path.join(sourceRoot, sourceFilename); + const sourceMapPath = path.join(sourceRoot, "absolute-sourceRoot-source-map.map"); + + // Create the sourcemap file + const rawSourceMap = { + "version": 3, + "file": javaScriptFilename, + "sourceRoot": sourceRoot, + "sources": [ + sourceFilename + ], + "mappings": "AAAA" + }; + fs.writeFileSync(sourceMapPath, JSON.stringify(rawSourceMap)); + + execLoader( + path.join(fixturesPath, javaScriptFilename), + (err, res, map, deps, warns) => { + should.equal(err, null); + warns.should.be.eql([]); + should.equal(res, "with SourceMap\n// comment"), + map.should.be.eql({ + "version": 3, + "file": javaScriptFilename, + "sources": [ + rootRelativeSourcePath + ], + "sourcesContent": [ + "with SourceMap\n// comment" + ], + "mappings": "AAAA" + }); + deps.should.be.eql([ + sourceMapPath, + rootRelativeSourcePath + ]); + done(); + } + ); + }); + + it("should support relative sourceRoot paths in sourcemaps", (done) => { + const javaScriptFilename = "relative-sourceRoot-source-map.js"; + const sourceFilename = "relative-sourceRoot-source-map.txt"; + const rootRelativeSourcePath = path.join(dataPath, sourceFilename); + const sourceMapPath = path.join(fixturesPath, "relative-sourceRoot-source-map.map"); + + execLoader( + path.join(fixturesPath, javaScriptFilename), + (err, res, map, deps, warns) => { + should.equal(err, null); + warns.should.be.eql([]); + should.equal(res, "with SourceMap\n// comment"), + map.should.be.eql({ + "version": 3, + "file": javaScriptFilename, + "sources": [ + rootRelativeSourcePath + ], + "sourcesContent": [ + "with SourceMap\n// comment" + ], + "mappings": "AAAA" + }); + deps.should.be.eql([ + sourceMapPath, + rootRelativeSourcePath + ]); + done(); + } + ); + }); }); \ No newline at end of file