Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

map.sources converted to absolute paths + other changes #75

Closed
wants to merge 8 commits into from
2 changes: 2 additions & 0 deletions .editorconfig
@@ -0,0 +1,2 @@
[*.js]
indent_style = tab
127 changes: 79 additions & 48 deletions index.js
Expand Up @@ -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)",
Expand All @@ -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);
});
}
}
13 changes: 3 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Expand Up @@ -12,7 +12,6 @@
"release": "standard-version"
},
"dependencies": {
"async": "^2.5.0",
"loader-utils": "^1.1.0"
},
"devDependencies": {
Expand Down
2 changes: 2 additions & 0 deletions test/fixtures/null-sourcesContent-source-map.js
@@ -0,0 +1,2 @@
with SourceMap
//#sourceMappingURL=null-sourcesContent-source-map.map
1 change: 1 addition & 0 deletions test/fixtures/null-sourcesContent-source-map.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions test/fixtures/null-sourcesContent-source-map.txt
@@ -0,0 +1 @@
with SourceMap
@@ -0,0 +1,2 @@
with SourceMap
//#sourceMappingURL=relative-sourceRoot-sourcesContent-source-map.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.