webpack.js.org
webpack.js.org copied to clipboard
MainTemplate plugin hooks stopped working in webpack 5 even though a compat layer is supposed to exist
Bug report
What is the current behavior?
The mainTemplate hooks that I used in a plugin in webpack 4 seem to no longer get called on the webpack runtime code in webpack 5.
If the current behavior is a bug, please provide the steps to reproduce.
function MyPlugin() {}
MyPlugin.prototype.apply = function (compiler) {
compiler.hooks.compilation.tap(
'MyPlugin',
function (compilation) {
compilation.mainTemplate.hooks.requireExtensions.tap(
'MyPlugin',
(source) =>
source.replace(
'Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });',
myNewCode
)
);
}
);
};
From what I can tell, the callback does get called still, but source is always empty, so my plugin doesn't actually do anything anymore. I've also tried different hooks on mainTemplate, but none of them seemed to work.
What is the expected behavior?
According to the migration guide, while we should be moving to the JavascriptModulesPlugin,
There is a compat-layer, so Main/Chunk/ModuleTemplate still exist, but only delegate tap calls to the new hook locations.
The new JavascriptModulesPlugin is not documented at all, so it's hard to follow the migration instructions. There are also no deprecation messages. But I would have assumed the old hooks would still work anyway while the compat layer exists. I tried messing with the renderMain hook in the JavascriptModulesPlugin, but it did not seem to be what I was looking for.
Other relevant information: webpack version: 5.36.2 Node.js version: 14.16.0 Operating System: OS X Catalina Additional tools:
For maintainers only:
- [ ] webpack-4
- [x] webpack-5
- [ ] bug
- [ ] critical-bug
- [ ] enhancement
- [x] documentation
- [ ] performance
- [ ] dependencies
- [ ] question
Unfortunately for some hooks there are no messages, we need update docs
@alexander-akait but aren't the old plugin hooks supposed to still work? I'm not sure this is just missing docs.
Also, I could look into making the docs PR though I'm unfamiliar with the hooks that would be required here
@bdwain some changes are really breaking changes and don't work with old API, anyway if you provide code of plugin I can help with migration
@alexander-akait i get that but the docs currently say this change was explicltly not supposed to break. but i guess if it was breaking then the docs just need updating.
the code of the plugin is in the original comment of this issue. it's pretty straightforward, i just dont know which hooks to use
I'm trying to modify this code in the webpack runtime
/******/ /* webpack/runtime/define property getters */
/******/ !function() {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = function(exports, definition) {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ }();
https://github.com/webpack/webpack/blob/master/lib/RuntimePlugin.js#L124
Thanks. Would you mind clarifying a bit how to migrate this old plugin to use this new hook?
The old one just got the source and was able to call String.replace on it. This new hook has a different signature.
I see the one you linked to is calling addRuntimeModule and generating the portion of the relevant portion template, but I'd like to modify that code, not generate new code.
If i was to generate new code, i'd need to have it not call the code you linked to. And then I'd also need to create a HelperRuntimeModule, which does not seem to be exported from webpack publicly.
I see the one you linked to is calling addRuntimeModule and generating the portion of the relevant portion template, but I'd like to modify that code, not generate new code.
in this case maybe better replace code in bundled files using compilation.hooks.processAssets, example https://github.com/webpack-contrib/terser-webpack-plugin/blob/master/src/index.js#L651
thanks! this worked!
MyPlugin.prototype.apply = function (compiler) {
compiler.hooks.compilation.tap(
'MyPlugin',
function (compilation) {
compilation.hooks.processAssets.tapPromise(
{
name: 'MyPlugin',
stage:
compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,
additionalAssets: true,
},
async (assets) => {
const oldSource = assets['runtime.js'];
const { ReplaceSource } = compiler.webpack.sources;
const newSource = new ReplaceSource(oldSource, 'MyPlugin');
const start = oldSource.source().indexOf(codeToReplace);
const end = start + codeToReplace.length;
newSource.replace(start, end, newCode, 'MyPlugin');
await compilation.updateAsset('runtime.js', newSource);
}
);
}
);
};
I will try to make a pr to document what I learned. Thanks again I appreciate the help.