I have noticed some duplicate code that exists across modules in my build. I'd like to optimize my JavaScript by writing a webpack plugin to find instances of X code and replace it with a simplified version of Y code.
I have cooked up a simple webpack plugin that seems to be close, but it doesn't quite do what I want to. There's not much documentation on things like the proper use of webpack's ReplaceSource, or even what the right lifecycles are to hook into for this kind of operation. So what I have here is mostly cobbled together from reading webpack source code and poking around GitHub search.
const { ReplaceSource } = require('webpack-sources');
const codeMapEntries = Object.entries({
'const from = "complicated code from example";': 'const from = somethingSimpler;',
});
class ReplaceCodePlugin {
apply(compiler) {
compiler.plugin('compilation', (compilation) => {
compilation.plugin('optimize-modules', (modules) => {
modules.forEach((module) => {
if (module._source && module._source._value) {
let source;
codeMapEntries.forEach(([fromCode, toCode]) => {
const startPos = module._source._value.indexOf(fromCode);
if (startPos !== -1) {
if (!source) {
source = new ReplaceSource(module._source);
}
source.replace(
startPos,
startPos + fromCode.length - 1,
toCode
);
}
});
if (source) {
module._source = source;
}
}
});
});
});
}
}
module.exports = ReplaceCodePlugin;
This seems to kinda work for some simple cases, but something isn't correct here and it causes the code to be weirdly jumbled, which then causes our minifier to complain like this:
SyntaxError: Unexpected token keyword «if», expected punc «,»
3417 | }, {
3418 | key: 'componentWillUnmount',
> 3419 | value: ffalse if (!Waypoint.getWindow()) {
| ^
3420 | return;
3421 | }
3422 |
Which makes me believe that I'm not using ReplaceSource properly.
I've also noticed some code like the following appear, which seems very strange:
var ___webpack_require__r"Jmof"= require('some-package');
var _somePackage2 = _interopRequir__webpack_require__t"yu5W"kage);
I'm also not even sure if this is the right approach and am open to suggestions for alternative solutions.
I was able to make this work by using the optimize-chunk-assets compilation hook instead of the optimize-modules compilation hook. However, I don't really understand why one works and the other does not. For reference, here's the working version of my plugin:
const { ReplaceSource } = require('webpack-sources');
const codeMapEntries = Object.entries({
'const from = "complicated code from example";': 'const from = somethingSimpler;',
});
class ReplaceCodePlugin {
apply(compiler) {
compiler.plugin('compilation', (compilation) => {
compilation.plugin('optimize-chunk-assets', (chunks, callback) => {
function getAllIndices(str, searchStr) {
let i = -1;
const indices = [];
while ((i = str.indexOf(searchStr, i + 1)) !== -1) {
indices.push(i);
}
return indices;
}
chunks.forEach((chunk) => {
chunk.files.forEach((file) => {
let source;
const originalSource = compilation.assets[file];
codeMapEntries.forEach(([fromCode, toCode]) => {
const indices = getAllIndices(originalSource.source(), fromCode);
if (!indices.length) {
return;
}
if (!source) {
source = new ReplaceSource(originalSource);
}
indices.forEach((startPos) => {
const endPos = startPos + fromCode.length - 1;
source.replace(startPos, endPos, toCode);
});
});
if (source) {
// eslint-disable-next-line no-param-reassign
compilation.assets[file] = source;
}
callback();
});
});
});
});
}
}
module.exports = ReplaceCodePlugin;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With