adding `output.devtoolNamespace` option

When loading multiple libraries built with webpack, you can run into
collisions of the sourcemap file paths. For examle, both have
"webpack:///src/index.js".

This change addresses the problem by introducing a new output option
`output.devtoolNamespace` which defaults to `output.library` when
not specified. The defaults moduleFilenameTemplates in all the
sourcemap plugins have been modified to start with:
"webpack://[namespace]/...", where [namespace] will be replaced by
the `output.devtoolNamespace`.

Notice that there are only two slashes following "webpack:" now.
This is to make it behave just as before when not building with a
namespace. When building with a namespace you only get the two
slashes, but from what I've seen the chrome dev tools only care
about the first 2 slashes anyways.

Discussed with sokra here:
https://github.com/webpack/webpack/issues/5767
This commit is contained in:
Stephan Badragan 2017-10-17 15:48:04 -07:00
parent 168f92330e
commit f925032d5a
12 changed files with 68 additions and 6 deletions

View File

@ -10,7 +10,7 @@ const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
class EvalDevToolModuleTemplatePlugin {
constructor(sourceUrlComment, moduleFilenameTemplate) {
this.sourceUrlComment = sourceUrlComment || "\n//# sourceURL=[url]";
this.moduleFilenameTemplate = moduleFilenameTemplate || "webpack:///[resourcePath]?[loaders]";
this.moduleFilenameTemplate = moduleFilenameTemplate || "webpack://[namespace]/[resourcePath]?[loaders]";
}
apply(moduleTemplate) {

View File

@ -11,7 +11,7 @@ class EvalSourceMapDevToolModuleTemplatePlugin {
constructor(compilation, options) {
this.compilation = compilation;
this.sourceMapComment = options.append || "//# sourceURL=[module]\n//# sourceMappingURL=[url]";
this.moduleFilenameTemplate = options.moduleFilenameTemplate || "webpack:///[resource-path]?[hash]";
this.moduleFilenameTemplate = options.moduleFilenameTemplate || "webpack://[namespace]/[resource-path]?[hash]";
this.options = options;
}

View File

@ -26,6 +26,8 @@ ModuleFilenameHelpers.ID = "[id]";
ModuleFilenameHelpers.REGEXP_ID = /\[id\]/gi;
ModuleFilenameHelpers.HASH = "[hash]";
ModuleFilenameHelpers.REGEXP_HASH = /\[hash\]/gi;
ModuleFilenameHelpers.NAMESPACE = "[namespace]";
ModuleFilenameHelpers.REGEXP_NAMESPACE = /\[namespace\]/gi;
function getAfter(str, token) {
const idx = str.indexOf(token);
@ -73,6 +75,7 @@ ModuleFilenameHelpers.createFilename = function createFilename(module, moduleFil
const allLoaders = getBefore(identifier, "!");
const query = getAfter(resource, "?");
const resourcePath = resource.substr(0, resource.length - query.length);
const namespace = this._moduleNamespace || "";
if(typeof moduleFilenameTemplate === "function") {
return moduleFilenameTemplate({
identifier: identifier,
@ -83,7 +86,8 @@ ModuleFilenameHelpers.createFilename = function createFilename(module, moduleFil
allLoaders: allLoaders,
query: query,
moduleId: moduleId,
hash: hash
hash: hash,
namespace: namespace
});
}
return moduleFilenameTemplate
@ -96,7 +100,8 @@ ModuleFilenameHelpers.createFilename = function createFilename(module, moduleFil
.replace(ModuleFilenameHelpers.REGEXP_LOADERS, loaders)
.replace(ModuleFilenameHelpers.REGEXP_QUERY, query)
.replace(ModuleFilenameHelpers.REGEXP_ID, moduleId)
.replace(ModuleFilenameHelpers.REGEXP_HASH, hash);
.replace(ModuleFilenameHelpers.REGEXP_HASH, hash)
.replace(ModuleFilenameHelpers.REGEXP_NAMESPACE, namespace);
};
ModuleFilenameHelpers.createFooter = function createFooter(module, requestShortener) {
@ -160,3 +165,7 @@ ModuleFilenameHelpers.matchObject = function matchObject(obj, str) {
if(ModuleFilenameHelpers.matchPart(str, obj.exclude)) return false;
return true;
};
ModuleFilenameHelpers.setModuleNamespace = function setModuleNamespace(moduleNamespace) {
this._moduleNamespace = moduleNamespace;
};

View File

@ -62,8 +62,8 @@ class SourceMapDevToolPlugin {
if(!options) options = {};
this.sourceMapFilename = options.filename;
this.sourceMappingURLComment = options.append === false ? false : options.append || "\n//# sourceMappingURL=[url]";
this.moduleFilenameTemplate = options.moduleFilenameTemplate || "webpack:///[resourcePath]";
this.fallbackModuleFilenameTemplate = options.fallbackModuleFilenameTemplate || "webpack:///[resourcePath]?[hash]";
this.moduleFilenameTemplate = options.moduleFilenameTemplate || "webpack://[namespace]/[resourcePath]";
this.fallbackModuleFilenameTemplate = options.fallbackModuleFilenameTemplate || "webpack://[namespace]/[resourcePath]?[hash]";
this.options = options;
}

View File

@ -46,6 +46,7 @@ const FlagDependencyExportsPlugin = require("./FlagDependencyExportsPlugin");
const SizeLimitsPlugin = require("./performance/SizeLimitsPlugin");
const ResolverFactory = require("enhanced-resolve").ResolverFactory;
const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
class WebpackOptionsApply extends OptionsApply {
constructor() {
@ -193,6 +194,12 @@ class WebpackOptionsApply extends OptionsApply {
ExternalsPlugin = require("./ExternalsPlugin");
compiler.apply(new ExternalsPlugin(options.output.libraryTarget, options.externals));
}
if(!options.output.devtoolNamespace) {
options.output.devtoolNamespace = options.output.library;
}
ModuleFilenameHelpers.setModuleNamespace(options.output.devtoolNamespace);
let noSources;
let legacy;
let modern;

View File

@ -326,6 +326,10 @@
}
]
},
"devtoolNamespace": {
"description": "Module namespace to use when interpolating filename template string for the sources array in a generated SourceMap. Defaults to `output.library` if not set. It's useful for avoiding runtime collisions in sourcemaps from multiple webpack projects built as libraries.",
"type": "string"
},
"filename": {
"description": "Specifies the name of each output file on disk. You must **not** specify an absolute path here! The `output.path` option determines the location on disk the files are written to, filename is used solely for naming the individual files.",
"type": "string",

View File

@ -0,0 +1,8 @@
it("should include webpack://mylibrary/./test.js in SourceMap", function() {
var fs = require("fs");
var source = fs.readFileSync(__filename + ".map", "utf-8");
var map = JSON.parse(source);
map.sources.should.containEql("webpack://mylibrary/./test.js");
});
require.include("./test.js");

View File

@ -0,0 +1,3 @@
var foo = {};
module.exports = foo;

View File

@ -0,0 +1,10 @@
module.exports = {
output: {
library: "mylibrary"
},
node: {
__dirname: false,
__filename: false
},
devtool: "cheap-source-map"
};

View File

@ -0,0 +1,8 @@
it("should include webpack://mynamespace/./test.js in SourceMap", function() {
var fs = require("fs");
var source = fs.readFileSync(__filename + ".map", "utf-8");
var map = JSON.parse(source);
map.sources.should.containEql("webpack://mynamespace/./test.js");
});
require.include("./test.js");

View File

@ -0,0 +1,3 @@
var foo = {};
module.exports = foo;

View File

@ -0,0 +1,10 @@
module.exports = {
output: {
devtoolNamespace: "mynamespace"
},
node: {
__dirname: false,
__filename: false
},
devtool: "cheap-source-map"
};