136 lines
4.1 KiB
JavaScript
136 lines
4.1 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
// Helper function to collect all categories from the tree
|
|
const collectAllCategories = (categoryNode, categories = []) => {
|
|
if (!categoryNode) return categories;
|
|
|
|
// Add current category (skip root category 209)
|
|
if (categoryNode.id !== 209) {
|
|
// Extract subcategory IDs from children
|
|
const subcategoryIds = categoryNode.children
|
|
? categoryNode.children.map(child => child.id)
|
|
: [];
|
|
|
|
categories.push({
|
|
id: categoryNode.id,
|
|
name: categoryNode.name,
|
|
seoName: categoryNode.seoName,
|
|
parentId: categoryNode.parentId,
|
|
subcategories: subcategoryIds
|
|
});
|
|
}
|
|
|
|
// Recursively add children
|
|
if (categoryNode.children) {
|
|
for (const child of categoryNode.children) {
|
|
collectAllCategories(child, categories);
|
|
}
|
|
}
|
|
|
|
return categories;
|
|
};
|
|
|
|
// Advanced CSS minification and optimization
|
|
const optimizeCss = (cssContent) => {
|
|
if (!cssContent || typeof cssContent !== 'string') {
|
|
return '';
|
|
}
|
|
|
|
try {
|
|
let optimized = cssContent
|
|
// Remove comments (/* ... */)
|
|
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
// Remove unnecessary whitespace but preserve structure
|
|
.replace(/\s*{\s*/g, '{')
|
|
.replace(/;\s*}/g, '}')
|
|
.replace(/}\s*/g, '}')
|
|
.replace(/,\s*/g, ',')
|
|
.replace(/:\s*/g, ':')
|
|
.replace(/;\s*/g, ';')
|
|
// Remove empty rules
|
|
.replace(/[^}]*\{\s*\}/g, '')
|
|
// Normalize multiple spaces/tabs/newlines
|
|
.replace(/\s+/g, ' ')
|
|
// Remove leading/trailing whitespace
|
|
.trim();
|
|
|
|
// Remove redundant semicolons before closing braces
|
|
optimized = optimized.replace(/;+}/g, '}');
|
|
|
|
// Remove empty media queries
|
|
optimized = optimized.replace(/@media[^{]*\{\s*\}/g, '');
|
|
|
|
return optimized;
|
|
} catch (error) {
|
|
console.warn(`⚠️ CSS optimization failed: ${error.message}`);
|
|
return cssContent; // Return original if optimization fails
|
|
}
|
|
};
|
|
|
|
// Extract critical CSS selectors (basic implementation)
|
|
const extractCriticalCss = (cssContent, criticalSelectors = []) => {
|
|
if (!cssContent || !criticalSelectors.length) {
|
|
return { critical: '', nonCritical: cssContent };
|
|
}
|
|
|
|
try {
|
|
const rules = cssContent.match(/[^{}]+\{[^{}]*\}/g) || [];
|
|
let critical = '';
|
|
let nonCritical = '';
|
|
|
|
rules.forEach(rule => {
|
|
const selector = rule.split('{')[0].trim();
|
|
const isCritical = criticalSelectors.some(criticalSel => {
|
|
return selector.includes(criticalSel) ||
|
|
selector.includes('body') ||
|
|
selector.includes('html') ||
|
|
selector.includes(':root') ||
|
|
selector.includes('@font-face') ||
|
|
selector.includes('@import');
|
|
});
|
|
|
|
if (isCritical) {
|
|
critical += rule;
|
|
} else {
|
|
nonCritical += rule;
|
|
}
|
|
});
|
|
|
|
return {
|
|
critical: optimizeCss(critical),
|
|
nonCritical: optimizeCss(nonCritical)
|
|
};
|
|
} catch (error) {
|
|
console.warn(`⚠️ Critical CSS extraction failed: ${error.message}`);
|
|
return { critical: cssContent, nonCritical: '' };
|
|
}
|
|
};
|
|
|
|
const writeCombinedCssFile = (globalCssCollection, outputDir) => {
|
|
const combinedCss = Array.from(globalCssCollection).join('\n');
|
|
|
|
// Optimize the combined CSS
|
|
const optimizedCss = optimizeCss(combinedCss);
|
|
|
|
const cssFilePath = path.resolve(__dirname, '..', outputDir, 'prerender.css');
|
|
fs.writeFileSync(cssFilePath, optimizedCss);
|
|
|
|
const originalSize = combinedCss.length;
|
|
const optimizedSize = optimizedCss.length;
|
|
const savings = originalSize - optimizedSize;
|
|
const savingsPercent = originalSize > 0 ? Math.round((savings / originalSize) * 100) : 0;
|
|
|
|
console.log(`✅ Combined CSS file written to ${cssFilePath}`);
|
|
console.log(` - Total CSS rules: ${globalCssCollection.size}`);
|
|
console.log(` - Original size: ${Math.round(originalSize / 1024)}KB`);
|
|
console.log(` - Optimized size: ${Math.round(optimizedSize / 1024)}KB`);
|
|
console.log(` - Space saved: ${Math.round(savings / 1024)}KB (${savingsPercent}%)`);
|
|
};
|
|
|
|
module.exports = {
|
|
collectAllCategories,
|
|
writeCombinedCssFile,
|
|
optimizeCss,
|
|
extractCriticalCss
|
|
};
|