Skip to content

Instantly share code, notes, and snippets.

@mbixby
Last active January 5, 2019 06:55
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mbixby/3dae2026a77a9a3d3de9 to your computer and use it in GitHub Desktop.
Save mbixby/3dae2026a77a9a3d3de9 to your computer and use it in GitHub Desktop.
Webpack Named Modules Plugin
var RequestShortener = require("webpack/lib/RequestShortener");
var _ = require("lodash");
/**
* TODO Docs
* @example new NamedModulesPlugin(/^\.\/app\/(viewmodels|records|adapters)/, /^\.\/app\/(.*)\.js$/)
*/
function NamedModulesPlugin(fileWhitelistRegex, replacementRegex) {
this.fileWhitelistRegex = fileWhitelistRegex;
this.replacementRegex = replacementRegex;
}
module.exports = NamedModulesPlugin;
NamedModulesPlugin.prototype.apply = function(compiler) {
if (!this.fileWhitelistRegex) throw new Error();
if (!this.replacementRegex) throw new Error();
var fileWhitelistRegex = this.fileWhitelistRegex;
var replacementRegex = this.replacementRegex;
var compilerContext = compiler.context;
compiler.plugin("this-compilation", function(compilation) {
var mainTemplate = compilation.mainTemplate;
mainTemplate.plugin("local-vars", function(source, chunk/*, hash*/) {
var self = this;
var buf = [];
buf.push(this.asString([
source,
"// The module cache",
"var installedModules = {};"
]));
buf.push("// Module request paths (NamedModulesPlugin)");
buf.push(this.requireFn + ".moduleNames = {");
chunk.modules.forEach(function(m){
var requestShortener = new RequestShortener(compilerContext);
var shortened = requestShortener.shorten(m.userRequest);
var isWhitelisted = shortened && shortened.match(fileWhitelistRegex);
if (isWhitelisted){
var name = shortened && shortened.replace(replacementRegex, '$1');
buf.push(self.indent(m.id + ": '" + name + "',"));
}
});
buf.push("};");
return this.asString(buf);
});
// TODO Refactor
// See monkey-patch below
var requirePlugin = function(source, chunk, hash) {
return this.asString([
source,
"// Check if module is in cache",
"if(installedModules[moduleId])",
this.indent("return installedModules[moduleId].exports;"),
"",
"// Create a new module (and put it into the cache)",
"var module = installedModules[moduleId] = {",
this.indent(this.applyPluginsWaterfall("module-obj", "", chunk, hash, "moduleId")),
"};",
"",
"// Execute the module function",
"modules[moduleId].call(module.exports, module, module.exports, " + this.renderRequireFunctionForModule(hash, chunk, "moduleId") + ");",
"",
"// Flag the module as loaded",
"module.loaded = true;",
"",
"// (NamedModulesPlugin) Set __moduleName__ on prototype of exported module",
"var moduleName = " + this.requireFn + ".moduleNames[moduleId];",
"if (moduleName && typeof module.exports === 'function'){",
this.indent("module.exports.prototype.__moduleId__ = moduleName;"),
"}",
"// Return the exports of the module",
"return module.exports;"
]);
};
requirePlugin.__namedModulesPlugin = true;
mainTemplate.plugin("require", requirePlugin);
});
};
// TODO Temporary
// Monkey-patch MainTemplate.prototype.applyPluginsWaterfall to throw away the original tapable "render" plugin (defined in MainTemplate constructor). We only want to use the "render" plugin defined above.
var MainTemplate = require("webpack/lib/MainTemplate");
var orig = MainTemplate.prototype.applyPluginsWaterfall;
MainTemplate.prototype.applyPluginsWaterfall = function(){
if (this._plugins["require"].length > 1){
this._plugins["require"] = _.filter(this._plugins["require"], '__namedModulesPlugin');
}
return orig.apply(this, arguments);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment