feat: add InlineCssPlugin to inline CSS assets in production builds
This commit is contained in:
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user