I\'m working on a webpack plugin and can\'t figure out how to modify a module during the build. What I\'m trying to do:
Another idea comes to my mind. What if do some preprocessing before webpack compilation?
This temp file could be a virtual file. May refer to https://github.com/rmarscher/virtual-module-webpack-plugin/ .
... be used if you generated file contents at build time that needs to be consumed as a module by your source code, but you don't want to write this file to disk.
Based on looking at how Webpack's official plugins (such as DefinePlugin
) modify module code, I believe the best way to do this is:
buildModule
, with module.addDependency()
.compilation.dependencyTemplates.set()
.apply
method, use source.replace()
or source.insert()
to make your modifications (where source
is the second argument)—see the ReplaceSource docs.In terms of compilation hooks, the templates are invoked immediately after beforeChunkAssets. Modifying the source in this way preserves SourceMaps.
const Dependency = require('webpack/lib/Dependency');
class MyDependency extends Dependency {
// Use the constructor to save any information you need for later
constructor(module) {
super();
this.module = module;
}
}
MyDependency.Template = class MyDependencyTemplate {
apply(dep, source) {
// dep is the MyDependency instance, so the module is dep.module
source.insert(0, 'console.log("Hello, plugin world!");');
}
};
module.exports = class MyPlugin {
apply(compiler) {
compiler.hooks.compilation.tap('MyPluginName', compilation => {
compilation.dependencyTemplates.set(
MyDependency,
new MyDependency.Template()
);
compilation.hooks.buildModule.tap('MyPluginName', module => {
module.addDependency(new MyDependency(module));
});
});
}
};
I figured out how to do this in a pretty painless fashion.
Things I had wrong:
rebuildModule()
isn't a good idea, because this re-loads the module from scratch: the file's source is loaded and passed through any applicable loaders, and the _source
property of the module
object is eventually reassigned when that process is finished.
rebuildModule()
at this point would actually be great if there were a way to modify the module source as it was being loaded in this call (i.e. dynamically assign a loader function that's only used on this rebuild). We'd then be able to take advantage of the sourceMap behavior that happens when a module's source is loaded (see below)How I got it working:
compilation
's 'seal' plugin, iterate through the compilation's module
s and find the one you wantmodule._source._value += extraCode;
reparse the module:
module.parse.parse(module._source.source(), {
current: module,
module.module,
compilation: compilation,
options: compilation.options
});
The parsing is taken from NormalModule's build
method, which is called more or or less immediately after the source has been loaded during the normal module build process.
This implementation gets the modified and parsed source into my final output. Since there's some sourceMap stuff in NormalModuleMixin's doBuild
method, and since I'm adding to the source after those functions have been called, I assume the sourceMap will be messed up now. So, next step is getting the sourceMap to reflect the code addition. Not sure whether to try and manually update the sourceMap or look into the idea above, trying to dynamically apply a loader and call rebuildModule() instead of parsing.
If you know a better way of doing any of the above, please let me know!