Skip to content

Commit

Permalink
Update Generator
Browse files Browse the repository at this point in the history
  • Loading branch information
linkdotnet committed Nov 22, 2024
1 parent 43d3416 commit 9e65a35
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 27 deletions.
98 changes: 74 additions & 24 deletions tools/LinkDotNet.Blog.CriticalCSS/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,35 +26,85 @@ public static async Task<string> GenerateAsync(IReadOnlyCollection<string>urls)

var usedCss = await page.EvaluateAsync<string[]>(
"""
() => {
const styleSheets = Array.from(document.styleSheets);
const usedRules = new Set();
async () => {
const styleSheets = Array.from(document.styleSheets);
const usedRules = new Set();
const processedUrls = new Set();
const viewportHeight = window.innerHeight;
const elements = document.querySelectorAll('*');
const aboveFold = Array.from(elements).filter(el => {
const rect = el.getBoundingClientRect();
return rect.top < viewportHeight;
});
const viewportHeight = window.innerHeight;
const elements = document.querySelectorAll('*');
const aboveFold = Array.from(elements).filter(el => {
const rect = el.getBoundingClientRect();
return rect.top < viewportHeight;
});
styleSheets.forEach(sheet => {
try {
Array.from(sheet.cssRules).forEach(rule => {
if (rule.type === 1) {
aboveFold.forEach(el => {
if (el.matches(rule.selectorText)) {
usedRules.add(rule.cssText);
}
});
}
});
} catch (e) {
async function fetchExternalStylesheet(url) {
if (processedUrls.has(url)) return;
processedUrls.add(url);
try {
const response = await fetch(url);
const text = await response.text();
const blob = new Blob([text], { type: 'text/css' });
const styleSheet = new CSSStyleSheet();
await styleSheet.replace(text);
return styleSheet;
} catch (e) {
console.error('Failed to fetch:', url, e);
return null;
}
}
async function processStyleSheet(sheet) {
try {
if (sheet.href) {
const externalSheet = await fetchExternalStylesheet(sheet.href);
if (externalSheet) {
Array.from(externalSheet.cssRules).forEach(processRule);
}
});
}
Array.from(sheet.cssRules).forEach(processRule);
} catch (e) {
if (sheet.href) {
console.error('CORS issue with:', sheet.href);
}
}
}
return Array.from(usedRules);
function processRule(rule) {
switch (rule.type) {
case CSSRule.STYLE_RULE:
aboveFold.forEach(el => {
try {
if (el.matches(rule.selectorText)) {
usedRules.add(rule.cssText);
}
} catch (e) {}
});
break;
case CSSRule.MEDIA_RULE:
if (window.matchMedia(rule.conditionText).matches) {
Array.from(rule.cssRules).forEach(processRule);
}
break;
case CSSRule.IMPORT_RULE:
processStyleSheet(rule.styleSheet);
break;
case CSSRule.FONT_FACE_RULE:
case CSSRule.KEYFRAMES_RULE:
usedRules.add(rule.cssText);
break;
}
""");
}
for (const sheet of styleSheets) {
await processStyleSheet(sheet);
}
return Array.from(usedRules);
}
""");

foreach (var css in usedCss)
{
Expand Down
5 changes: 2 additions & 3 deletions tools/LinkDotNet.Blog.CriticalCSS/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,10 @@ static void OutputToLayout(string css, string? layoutPath)
const string styleTagPattern = "<style[^>]*>.*?</style>";
const string headEndTag = "</head>";

var newStyleTag = $"<style>{css}</style>";

layoutContent = Regex.IsMatch(layoutContent, styleTagPattern, RegexOptions.Singleline)
? Regex.Replace(layoutContent, styleTagPattern, newStyleTag, RegexOptions.Singleline)
: layoutContent.Replace(headEndTag, $"{newStyleTag}\n {headEndTag}", StringComparison.OrdinalIgnoreCase);
? Regex.Replace(layoutContent, styleTagPattern, css, RegexOptions.Singleline)
: layoutContent.Replace(headEndTag, $"{css}\n {headEndTag}", StringComparison.OrdinalIgnoreCase);

File.WriteAllText(layoutPath, layoutContent);
}
Expand Down

0 comments on commit 9e65a35

Please sign in to comment.