diff --git a/tools/LinkDotNet.Blog.CriticalCSS/Generator.cs b/tools/LinkDotNet.Blog.CriticalCSS/Generator.cs index 0333c13b..9357d0f3 100644 --- a/tools/LinkDotNet.Blog.CriticalCSS/Generator.cs +++ b/tools/LinkDotNet.Blog.CriticalCSS/Generator.cs @@ -26,35 +26,85 @@ public static async Task GenerateAsync(IReadOnlyCollectionurls) var usedCss = await page.EvaluateAsync( """ - () => { - 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) { diff --git a/tools/LinkDotNet.Blog.CriticalCSS/Program.cs b/tools/LinkDotNet.Blog.CriticalCSS/Program.cs index bf62155c..7047762a 100644 --- a/tools/LinkDotNet.Blog.CriticalCSS/Program.cs +++ b/tools/LinkDotNet.Blog.CriticalCSS/Program.cs @@ -110,11 +110,10 @@ static void OutputToLayout(string css, string? layoutPath) const string styleTagPattern = "]*>.*?"; const string headEndTag = ""; - var newStyleTag = $""; 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); }