PromucFlow_constructor/app/client/webpack/IconChunkNamingPlugin.js

70 lines
2.7 KiB
JavaScript
Raw Normal View History

/**
* We use `importSvg()` and `importRemixIcon()` utilities to code-split icons away.
* This plugin renames the chunks that contain these icons from
* 35140.dfd465bd.chunk.js
* to
* icon.dfd465bd.chunk.js
* to make it easier to filter them out in the network tab.
*
* Why do it here and not in `chunkFilename`, as canonically intended? Because
* we have lots of icons, and, as of Mar 2023, V8 (the JS engine in Chrome)
* cant handle that: https://bugs.chromium.org/p/v8/issues/detail?id=13887
*/
class IconChunkNamingPlugin {
apply(compiler) {
const webpackConfig = compiler.options;
if (
// checking only for the `[contenthash` part because
// the ending might be dynamic: `]` or `:8]` or `:some-other-number]`
!webpackConfig.output.chunkFilename.includes("[contenthash") &&
!webpackConfig.output.chunkFilename.includes("[chunkhash")
) {
// The plugin requires the chunkFilename to include [contenthash] or [chunkhash]. Thats because
// the plugin renames every icon chunk to "icon". If chunkFilename doesnt include the `[contenthash]` or `[chunkhash]`
// placeholder, wed end up with multiple chunks named "icon". This might result in overwriting
// the icons or (in development) even hanging the build process completely.
throw new Error(
"IconChunkNamingPlugin: the output.chunkFilename config must include [contenthash] " +
"or [chunkhash] to give every chunk file a unique name",
);
}
compiler.hooks.thisCompilation.tap(
"IconChunkNamingPlugin",
(compilation) => {
compilation.hooks.afterOptimizeChunks.tap(
"IconChunkNamingPlugin",
(chunks) => {
const { chunkGraph } = compilation;
// Walk over all chunks webpack emits
for (const chunk of chunks) {
// Get all modules in the current chunk
const chunkModules = chunkGraph.getChunkModules(chunk);
// Only if the chunk has exactly one module...
const hasOnlyOneModule = chunkModules.length === 1;
if (!hasOnlyOneModule) continue;
// ...and that module is either a SVG icon or a Remix icon...
const modulePath = chunkModules[0].resource;
const isSvgIcon =
modulePath.endsWith(".svg.js") || modulePath.endsWith(".svg");
const isRemixIcon = modulePath.match(
/node_modules[\\\/]remixicon-react[\\\/]/,
);
if (!isSvgIcon && !isRemixIcon) continue;
// ...then rename the chunk to "icon"
chunk.name = "icon";
}
},
);
},
);
}
}
module.exports = IconChunkNamingPlugin;