feat: add InlineCssPlugin to inline CSS assets in production builds

This commit is contained in:
sebseb7
2025-07-20 14:13:39 +02:00
parent f748056568
commit b2474a595c

View File

@@ -102,6 +102,74 @@ const CopyAssetsPlugin = {
},
};
// Custom plugin to inline CSS instead of loading externally
class InlineCssPlugin {
apply(compiler) {
compiler.hooks.compilation.tap('InlineCssPlugin', (compilation) => {
HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync(
'InlineCssPlugin',
(data, cb) => {
// Only inline CSS in production mode
if (isDevelopment) {
cb(null, data);
return;
}
// Find CSS assets and inline them
let inlinedCss = '';
const cssAssets = [];
// Get CSS assets from compilation
Object.keys(compilation.assets).forEach(assetName => {
if (assetName.endsWith('.css')) {
const cssContent = compilation.assets[assetName].source();
inlinedCss += cssContent + '\n';
cssAssets.push(assetName);
// Remove CSS asset from compilation to prevent external file generation
delete compilation.assets[assetName];
}
});
if (inlinedCss.trim()) {
// Remove existing CSS link tags from HTML
data.html = data.html.replace(/<link[^>]*href="[^"]*\.css"[^>]*>/g, '');
// Extract font URLs from CSS for preloading
const fontUrls = [];
const fontMatches = inlinedCss.match(/url\(([^)]+\.ttf)\)/g);
if (fontMatches) {
fontMatches.forEach(match => {
const fontUrl = match.replace(/url\(([^)]+)\)/, '$1').replace(/['"]/g, '');
if (!fontUrls.includes(fontUrl)) {
fontUrls.push(fontUrl);
}
});
}
// Add font preload links
let fontPreloads = '';
fontUrls.forEach(fontUrl => {
fontPreloads += `<link rel="preload" href="${fontUrl}" as="font" type="font/truetype" crossorigin>\n`;
});
// Add inlined CSS to head
const styleTag = `<style type="text/css">${inlinedCss.trim()}</style>`;
data.html = data.html.replace('</head>', `${fontPreloads}${styleTag}\n</head>`);
console.log(`✅ Inlined CSS assets: ${cssAssets.join(', ')} (${Math.round(inlinedCss.length / 1024)}KB)`);
if (fontUrls.length > 0) {
console.log(`✅ Added font preloads: ${fontUrls.length} fonts`);
}
}
cb(null, data);
}
);
});
}
}
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const isDevelopment = process.env.NODE_ENV !== 'production';
@@ -263,6 +331,7 @@ export default {
generateStatsFile: true,
statsFilename: 'bundle-stats.json',
}),
new InlineCssPlugin(),
].filter(Boolean),
devServer: {
allowedHosts: 'all',