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); };