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 __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
const isDevelopment = process.env.NODE_ENV !== 'production';
|
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||||
@@ -263,6 +331,7 @@ export default {
|
|||||||
generateStatsFile: true,
|
generateStatsFile: true,
|
||||||
statsFilename: 'bundle-stats.json',
|
statsFilename: 'bundle-stats.json',
|
||||||
}),
|
}),
|
||||||
|
new InlineCssPlugin(),
|
||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
devServer: {
|
devServer: {
|
||||||
allowedHosts: 'all',
|
allowedHosts: 'all',
|
||||||
|
|||||||
Reference in New Issue
Block a user