diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c330fbd --- /dev/null +++ b/.editorconfig @@ -0,0 +1,2 @@ +[*.js] +indent_style = tab diff --git a/index.js b/index.js index 6137235..df899fb 100644 --- a/index.js +++ b/index.js @@ -4,8 +4,8 @@ */ var fs = require("fs"); var path = require("path"); -var async = require("async"); var loaderUtils = require("loader-utils"); +var urlUtils = require("url"); // Matches only the last occurrence of sourceMappingURL var baseRegex = "\\s*[@#]\\s*sourceMappingURL\\s*=\\s*([^\\s]*)(?![\\S\\s]*sourceMappingURL)", @@ -14,98 +14,129 @@ var baseRegex = "\\s*[@#]\\s*sourceMappingURL\\s*=\\s*([^\\s]*)(?![\\S\\s]*sourc // Matches // .... comments regex2 = new RegExp("//"+baseRegex+"($|\n|\r\n?)"), // Matches DataUrls - regexDataUrl = /data:[^;\n]+(?:;charset=[^;\n]+)?;base64,([a-zA-Z0-9+/]+={0,2})/; + regexDataUrl = /data:[^;\n]+(?:;charset=[^;\n]+)?;base64,([a-zA-Z0-9+/]+={0,2})/, + // Matches url with scheme, doesn't match Windows disk + regexUrl = /[a-zA-Z]{2,}:/; + +const FILE_SCHEME = "file:"; + +const DEFAULT_OPTIONS = { + // Prevent the loader to rewrite all sources as absolute paths + keepRelativeSources: false +}; module.exports = function(input, inputMap) { + const options = Object.assign({}, DEFAULT_OPTIONS, loaderUtils.getOptions(this)); this.cacheable && this.cacheable(); - var resolve = this.resolve; var addDependency = this.addDependency; var emitWarning = this.emitWarning || function() {}; var match = input.match(regex1) || input.match(regex2); + var callback; if(match) { var url = match[1]; var dataUrlMatch = regexDataUrl.exec(url); - var callback = this.async(); + callback = this.async(); if(dataUrlMatch) { var mapBase64 = dataUrlMatch[1]; - var mapStr = (new Buffer(mapBase64, "base64")).toString(); + var mapStr = Buffer.from(mapBase64, "base64").toString(); var map; try { map = JSON.parse(mapStr) } catch (e) { - emitWarning("Cannot parse inline SourceMap '" + mapBase64.substr(0, 50) + "': " + e); + emitWarning(new Error("Cannot parse inline SourceMap '" + + mapBase64.substr(0, 50) + "': " + e)); return untouched(); } processMap(map, this.context, callback); } else { - resolve(this.context, loaderUtils.urlToRequest(url, true), function(err, result) { + resolveAbsolutePath(this.context, url, function(err, absoluteFilepath) { if(err) { - emitWarning("Cannot find SourceMap '" + url + "': " + err); + emitWarning(new Error("Cannot find SourceMap '" + url + "': " + err)); return untouched(); } - addDependency(result); - fs.readFile(result, "utf-8", function(err, content) { + fs.readFile(absoluteFilepath, "utf-8", function(err, content) { if(err) { - emitWarning("Cannot open SourceMap '" + result + "': " + err); + emitWarning(new Error("Cannot open SourceMap '" + absoluteFilepath + "': " + err)); return untouched(); } + addDependency(absoluteFilepath); var map; try { map = JSON.parse(content); } catch (e) { - emitWarning("Cannot parse SourceMap '" + url + "': " + e); + emitWarning(new Error("Cannot parse SourceMap '" + url + "': " + e)); return untouched(); } - processMap(map, path.dirname(result), callback); + processMap(map, path.dirname(absoluteFilepath), callback); }); }.bind(this)); - return; } } else { - var callback = this.callback; + callback = this.callback; return untouched(); } function untouched() { callback(null, input, inputMap); } + function resolveAbsolutePath(context, url, resolveAbsolutePathCallback) { + let filepath = url; + if(regexUrl.test(filepath) && !filepath.startsWith(FILE_SCHEME)) { + resolveAbsolutePathCallback("URL scheme not supported"); + return; + } + if(filepath.startsWith(FILE_SCHEME)) { + if(urlUtils.fileURLToPath) { + filepath = urlUtils.fileURLToPath(filepath); + } else { + resolveAbsolutePathCallback("file URL scheme support requires node 10.x"); + return; + } + } + resolveAbsolutePathCallback(null, path.resolve(context, filepath)); + } function processMap(map, context, callback) { - if(!map.sourcesContent || map.sourcesContent.length < map.sources.length) { - var sourcePrefix = map.sourceRoot ? map.sourceRoot + "/" : ""; - map.sources = map.sources.map(function(s) { return sourcePrefix + s; }); - 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, true), function(err, result) { + const sourcePrefix = map.sourceRoot ? map.sourceRoot + "/" : ""; + const sources = map.sources.map(function(s) { return sourcePrefix + s; }); + delete map.sourceRoot; + const sourcesContent = map.sourcesContent || []; + const sourcesPromises = sources.map((source, sourceIndex) => new Promise((resolveSource) => { + resolveAbsolutePath(context, source, function(err, absoluteFilepath) { + if(err) { + emitWarning(new Error("Cannot find source file '" + source + "': " + err)); + return resolveSource({ + source: source, + content: sourcesContent[sourceIndex] != null ? sourcesContent[sourceIndex] : null + }); + } + if(sourcesContent[sourceIndex] != null) { + return resolveSource({ + source: absoluteFilepath, + content: sourcesContent[sourceIndex] + }); + } + fs.readFile(absoluteFilepath, "utf-8", function(err, content) { if(err) { - emitWarning("Cannot find source file '" + source + "': " + err); - return callback(null, null); - } - addDependency(result); - fs.readFile(result, "utf-8", function(err, content) { - if(err) { - emitWarning("Cannot open source file '" + result + "': " + err); - return callback(null, null); - } - callback(null, { - source: result, - content: content + emitWarning(new Error("Cannot open source file '" + absoluteFilepath + "': " + err)); + return resolveSource({ + source: absoluteFilepath, + content: null }); - }); - }); - }, function(err, info) { - map.sourcesContent = map.sourcesContent || []; - info.forEach(function(res) { - if(res) { - map.sources[map.sourcesContent.length] = res.source; - map.sourcesContent.push(res.content); - } else { - map.sourcesContent.push(null); } + addDependency(absoluteFilepath); + resolveSource({ + source: absoluteFilepath, + content: content + }); }); - processMap(map, context, callback); }); - return; - } - callback(null, input.replace(match[0], ''), map); + })); + Promise.all(sourcesPromises) + .then((results) => { + if (!options.keepRelativeSources) { + map.sources = results.map(res => res.source); + } + map.sourcesContent = results.map(res => res.content); + callback(null, input.replace(match[0], ""), map); + }); } } diff --git a/package-lock.json b/package-lock.json index 98b8845..66f216c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,14 +61,6 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, - "async": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", - "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", - "requires": { - "lodash": "^4.14.0" - } - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -1030,7 +1022,7 @@ }, "json5": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz", "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" }, "jsonparse": { @@ -1099,7 +1091,8 @@ "lodash": { "version": "4.17.10", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true }, "lodash._reinterpolate": { "version": "3.0.0", diff --git a/package.json b/package.json index 83e35e7..d55c20e 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,6 @@ "release": "standard-version" }, "dependencies": { - "async": "^2.5.0", "loader-utils": "^1.1.0" }, "devDependencies": { diff --git a/test/fixtures/null-sourcesContent-source-map.js b/test/fixtures/null-sourcesContent-source-map.js new file mode 100644 index 0000000..3c5c45d --- /dev/null +++ b/test/fixtures/null-sourcesContent-source-map.js @@ -0,0 +1,2 @@ +with SourceMap +//#sourceMappingURL=null-sourcesContent-source-map.map \ No newline at end of file diff --git a/test/fixtures/null-sourcesContent-source-map.map b/test/fixtures/null-sourcesContent-source-map.map new file mode 100644 index 0000000..4a37daf --- /dev/null +++ b/test/fixtures/null-sourcesContent-source-map.map @@ -0,0 +1 @@ +{"version":3,"file":"null-sourcesContent-source-map.js","sources":["null-sourcesContent-source-map.txt"],"sourcesContent":[null],"mappings":"AAAA"} \ No newline at end of file diff --git a/test/fixtures/null-sourcesContent-source-map.txt b/test/fixtures/null-sourcesContent-source-map.txt new file mode 100644 index 0000000..3f2f36d --- /dev/null +++ b/test/fixtures/null-sourcesContent-source-map.txt @@ -0,0 +1 @@ +with SourceMap \ No newline at end of file diff --git a/test/fixtures/relative-sourceRoot-sourcesContent-source-map.js b/test/fixtures/relative-sourceRoot-sourcesContent-source-map.js new file mode 100644 index 0000000..cbd9fc1 --- /dev/null +++ b/test/fixtures/relative-sourceRoot-sourcesContent-source-map.js @@ -0,0 +1,2 @@ +with SourceMap +//#sourceMappingURL=relative-sourceRoot-sourcesContent-source-map.map \ No newline at end of file diff --git a/test/fixtures/relative-sourceRoot-sourcesContent-source-map.map b/test/fixtures/relative-sourceRoot-sourcesContent-source-map.map new file mode 100644 index 0000000..b103a97 --- /dev/null +++ b/test/fixtures/relative-sourceRoot-sourcesContent-source-map.map @@ -0,0 +1 @@ +{"version":3,"file":"relative-sourceRoot-sourcesContent-source-map.js","sourceRoot":"../fixtures/data/","sources":["relative-sourceRoot-sourcesContent-source-map.txt"],"sourcesContent":["with SourceMap"],"mappings":"AAAA"} \ No newline at end of file diff --git a/test/index.test.js b/test/index.test.js index 1191e24..308bcf9 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -3,21 +3,12 @@ var fs = require("fs"); var should = require("should"); var loader = require("../"); -function execLoader(filename, callback) { +const execLoader = (filename) => new Promise((resolvePromise, rejectPromise) => { var async = false; var deps = []; var warns = []; var context = { context: path.dirname(filename), - resolve: function(context, request, callback) { - process.nextTick(function() { - var p = path.isAbsolute(request) ? request : path.resolve(context, request); - if(fs.existsSync(p)) - callback(null, p); - else - callback(new Error("File not found")); - }); - }, addDependency: function(dep) { deps.push(dep); }, @@ -26,7 +17,11 @@ function execLoader(filename, callback) { }, callback: function(err, res, map) { async = true; - callback(err, res, map, deps, warns); + if(err) { + rejectPromise(err); + } else { + resolvePromise({res, map, deps, warns}); + } }, async: function() { async = true; @@ -36,199 +31,188 @@ function execLoader(filename, callback) { // Remove CRs to make test line ending invariant var fixtureContent = fs.readFileSync(filename, "utf-8").replace(/\r/g, ''); var res = loader.call(context, fixtureContent); - if(!async) return callback(null, res, null, deps, warns); -} + if(!async) return resolvePromise({res, map: null, deps, warns}); +}); 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(fixturesPath, "normal-file.js"), function(err, res, map, deps, warns) { - should.equal(err, null); - warns.should.be.eql([]); - should.equal(res, "without SourceMap"), - should.equal(map, null); - deps.should.be.eql([]); - done(); - }); - }); - - it("should process inlined SourceMaps", function(done) { - 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"), - map.should.be.eql({ - "version":3, - "file":"inline-source-map.js", - "sources":[ - "inline-source-map.txt" - ], - "sourcesContent":["with SourceMap"], - "mappings":"AAAA" + it("should leave normal files untouched", function() { + return execLoader(path.join(fixturesPath, "normal-file.js")) + .then(function({res, map, deps, warns}) { + warns.should.be.eql([]); + should.equal(res, "without SourceMap"); + should.equal(map, null); + deps.should.be.eql([]); }); - deps.should.be.eql([]); - done(); - }); }); - it("should process external SourceMaps", function(done) { - 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"), - map.should.be.eql({ - "version":3, - "file":"external-source-map.js", - "sources":[ - "external-source-map.txt" - ], - "sourcesContent":["with SourceMap"], - "mappings":"AAAA" + it("should process inlined SourceMaps", function() { + return execLoader(path.join(fixturesPath, "inline-source-map.js")) + .then(function({res, map, deps, warns}) { + warns.should.be.eql([]); + should.equal(res, "with SourceMap\n// comment"); + map.should.be.eql({ + "version":3, + "file":"inline-source-map.js", + "sources":[ + path.join(fixturesPath,"inline-source-map.txt") + ], + "sourcesContent":["with SourceMap"], + "mappings":"AAAA" + }); + deps.should.be.eql([]); }); - deps.should.be.eql([ - path.join(fixturesPath, "external-source-map.map") - ]); - done(); - }); }); - it("should process external SourceMaps (external sources)", function(done) { - 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"), - map.should.be.eql({ - "version":3, - "file":"external-source-map2.js", - "sources":[ - path.join(fixturesPath, "external-source-map2.txt") - ], - "sourcesContent":["with SourceMap"], - "mappings":"AAAA" + it("should process external SourceMaps", function() { + return execLoader(path.join(fixturesPath, "external-source-map.js")) + .then(function({res, map, deps, warns}) { + warns.should.be.eql([]); + should.equal(res, "with SourceMap\n// comment"); + map.should.be.eql({ + "version":3, + "file":"external-source-map.js", + "sources":[ + path.join(fixturesPath,"external-source-map.txt") + ], + "sourcesContent":["with SourceMap"], + "mappings":"AAAA" + }); + deps.should.be.eql([ + path.join(fixturesPath, "external-source-map.map") + ]); }); - deps.should.be.eql([ - 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(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"), + it("should process external SourceMaps (external sources)", function() { + return execLoader(path.join(fixturesPath, "external-source-map2.js")) + .then(function({res, map, deps, warns}) { + warns.should.be.eql([]); + should.equal(res, "with SourceMap\n// comment"); map.should.be.eql({ - "version": 3, - "file": "inline-source-map.js", - "sources": [ - "inline-source-map.txt" + "version":3, + "file":"external-source-map2.js", + "sources":[ + path.join(fixturesPath, "external-source-map2.txt") ], - "sourcesContent": ["with SourceMap"], - "mappings": "AAAA" + "sourcesContent":["with SourceMap"], + "mappings":"AAAA" }); - deps.should.be.eql([]); - done(); - }); + deps.should.be.eql([ + path.join(dataPath, "external-source-map2.map"), + path.join(fixturesPath, "external-source-map2.txt") + ]); + }); }); - it("should skip invalid base64 SourceMap", function (done) { - 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"); - should.equal(map, null); - deps.should.be.eql([]); - done(); - }); + it("should use last SourceMap directive", function () { + return execLoader(path.join(fixturesPath, "multi-source-map.js")) + .then(function({res, map, deps, warns}) { + warns.should.be.eql([]); + should.equal(res, "with SourceMap\nanInvalidDirective = \"\\n/*# sourceMappingURL=data:application/json;base64,\"+btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))+\" */\";\n// comment"); + map.should.be.eql({ + "version": 3, + "file": "inline-source-map.js", + "sources": [ + path.join(fixturesPath,"inline-source-map.txt") + ], + "sourcesContent": ["with SourceMap"], + "mappings": "AAAA" + }); + deps.should.be.eql([]); + }); }); - it("should warn on invalid base64 SourceMap", function (done) { - 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") - ); - should.equal(res, "without SourceMap\n// @sourceMappingURL=data:application/source-map;base64,invalid/base64=\n// comment"); - should.equal(map, null); - deps.should.be.eql([]); - done(); - }); + + it("should skip invalid base64 SourceMap", function () { + return execLoader(path.join(fixturesPath, "invalid-inline-source-map.js")) + .then(function({res, map, deps, warns}) { + warns.should.be.eql([]); + should.equal(res, "without SourceMap\n// @sourceMappingURL=data:application/source-map;base64,\"something invalid\"\n// comment"); + should.equal(map, null); + deps.should.be.eql([]); + }); + }); + it("should warn on invalid base64 SourceMap", function () { + return execLoader(path.join(fixturesPath, "invalid-inline-source-map2.js")) + .then(function({res, map, deps, warns}) { + warns.should.matchEach( + new RegExp("Cannot parse inline SourceMap 'invalid\/base64=': SyntaxError: Unexpected token") + ); + should.equal(res, "without SourceMap\n// @sourceMappingURL=data:application/source-map;base64,invalid/base64=\n// comment"); + should.equal(map, null); + deps.should.be.eql([]); + }); }); - it("should warn on invalid SourceMap", function (done) { - 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") - ); - should.equal(res, "with SourceMap\n//#sourceMappingURL=invalid-source-map.map\n// comment"); - should.equal(map, null); - deps.should.be.eql([ - path.join(fixturesPath, "invalid-source-map.map") - ]); - done(); - }); + it("should warn on invalid SourceMap", function () { + return execLoader(path.join(fixturesPath, "invalid-source-map.js")) + .then(function({res, map, deps, warns}) { + warns.should.matchEach( + new RegExp("Cannot parse SourceMap 'invalid-source-map.map': SyntaxError: Unexpected string in JSON at position 102") + ); + should.equal(res, "with SourceMap\n//#sourceMappingURL=invalid-source-map.map\n// comment"); + should.equal(map, null); + deps.should.be.eql([ + path.join(fixturesPath, "invalid-source-map.map") + ]); + }); }); - it("should warn on missing SourceMap", function(done) { - 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" - ]); - should.equal(res, "with SourceMap\n//#sourceMappingURL=missing-source-map.map\n// comment"), - should.equal(map, null); - deps.should.be.eql([]); - done(); - }); + it("should warn on missing SourceMap", function() { + return execLoader(path.join(fixturesPath, "missing-source-map.js")) + .then(function({res, map, deps, warns}) { + warns.should.matchEach( + new RegExp(`Cannot open SourceMap '${path.join(fixturesPath, 'missing-source-map.map')}':`) + ); + should.equal(res, "with SourceMap\n//#sourceMappingURL=missing-source-map.map\n// comment"); + should.equal(map, null); + deps.should.be.eql([]); + }); }); - it("should warn on missing source file", function(done) { - 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" - ]); - should.equal(res, "with SourceMap\n// comment"), - map.should.be.eql({ - "version":3, - "file":"missing-source-map2.js", - "sources":[ - "missing-source-map2.txt" - ], - "sourcesContent":[null], - "mappings":"AAAA" + it("should warn on missing source file", function() { + return execLoader(path.join(fixturesPath, "missing-source-map2.js")) + .then(function({res, map, deps, warns}) { + warns.should.matchEach( + new RegExp(`Cannot open source file '${path.join(fixturesPath, 'missing-source-map2.txt')}':`) + ); + should.equal(res, "with SourceMap\n// comment"); + map.should.be.eql({ + "version":3, + "file":"missing-source-map2.js", + "sources":[ + path.join(fixturesPath,"missing-source-map2.txt") + ], + "sourcesContent":[null], + "mappings":"AAAA" + }); + deps.should.be.eql([ + path.join(fixturesPath, "missing-source-map2.map") + ]); }); - deps.should.be.eql([ - path.join(fixturesPath, "missing-source-map2.map") - ]); - done(); - }); }); - it("should process inlined SourceMaps with charset", function(done) { - 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"), - map.should.be.eql({ - "version":3, - "file":"charset-inline-source-map.js", - "sources":[ - "charset-inline-source-map.txt" - ], - "sourcesContent":["with SourceMap"], - "mappings":"AAAA" + it("should process inlined SourceMaps with charset", function() { + return execLoader(path.join(fixturesPath, "charset-inline-source-map.js")) + .then(function({res, map, deps, warns}) { + warns.should.be.eql([]); + should.equal(res, "with SourceMap\n// comment"); + map.should.be.eql({ + "version":3, + "file":"charset-inline-source-map.js", + "sources":[ + path.join(fixturesPath,"charset-inline-source-map.txt") + ], + "sourcesContent":["with SourceMap"], + "mappings":"AAAA" + }); + deps.should.be.eql([]); }); - deps.should.be.eql([]); - done(); - }); }); - it("should support absolute sourceRoot paths in sourcemaps", (done) => { + it("should support absolute sourceRoot paths in sourcemaps", () => { const sourceRoot = path.join(fixturesPath); const javaScriptFilename = "absolute-sourceRoot-source-map.js"; const sourceFilename = "absolute-sourceRoot-source-map.txt"; @@ -247,12 +231,10 @@ describe("source-map-loader", function() { }; fs.writeFileSync(sourceMapPath, JSON.stringify(rawSourceMap)); - execLoader( - path.join(fixturesPath, javaScriptFilename), - (err, res, map, deps, warns) => { - should.equal(err, null); + return execLoader(path.join(fixturesPath, javaScriptFilename)) + .then(function({res, map, deps, warns}) { warns.should.be.eql([]); - should.equal(res, "with SourceMap\n// comment"), + should.equal(res, "with SourceMap\n// comment"); map.should.be.eql({ "version": 3, "file": javaScriptFilename, @@ -268,23 +250,20 @@ describe("source-map-loader", function() { sourceMapPath, rootRelativeSourcePath ]); - done(); } ); }); - it("should support relative sourceRoot paths in sourcemaps", (done) => { + it("should support relative sourceRoot paths in sourcemaps", () => { 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); + return execLoader(path.join(fixturesPath, javaScriptFilename)) + .then(function({res, map, deps, warns}) { warns.should.be.eql([]); - should.equal(res, "with SourceMap\n// comment"), + should.equal(res, "with SourceMap\n// comment"); map.should.be.eql({ "version": 3, "file": javaScriptFilename, @@ -300,7 +279,63 @@ describe("source-map-loader", function() { sourceMapPath, rootRelativeSourcePath ]); - done(); + } + ); + }); + + it("should support null value in sourcesContent", () => { + const javaScriptFilename = "null-sourcesContent-source-map.js"; + const sourceFilename = "null-sourcesContent-source-map.txt"; + const rootRelativeSourcePath = path.join(fixturesPath, sourceFilename); + const sourceMapPath = path.join(fixturesPath, "null-sourcesContent-source-map.map"); + + return execLoader(path.join(fixturesPath, javaScriptFilename)) + .then(function({res, map, deps, warns}) { + warns.should.be.eql([]); + should.equal(res, "with SourceMap\n"); + map.should.be.eql({ + "version": 3, + "file": javaScriptFilename, + "sources": [ + rootRelativeSourcePath + ], + "sourcesContent": [ + "with SourceMap" + ], + "mappings": "AAAA" + }); + deps.should.be.eql([ + sourceMapPath, + rootRelativeSourcePath + ]); + } + ); + }); + + it("should resolve relative sources path even with sourcesContent", () => { + const javaScriptFilename = "relative-sourceRoot-sourcesContent-source-map.js"; + const sourceFilename = "relative-sourceRoot-sourcesContent-source-map.txt"; + const rootRelativeSourcePath = path.join(dataPath, sourceFilename); + const sourceMapPath = path.join(fixturesPath, "relative-sourceRoot-sourcesContent-source-map.map"); + + return execLoader(path.join(fixturesPath, javaScriptFilename)) + .then(function({res, map, deps, warns}) { + warns.should.be.eql([]); + should.equal(res, "with SourceMap\n"); + map.should.be.eql({ + "version": 3, + "file": javaScriptFilename, + "sources": [ + rootRelativeSourcePath + ], + "sourcesContent": [ + "with SourceMap" + ], + "mappings": "AAAA" + }); + deps.should.be.eql([ + sourceMapPath + ]); } ); });