diff --git a/scripts/generate-skills.js b/scripts/generate-skills.js index e166b95..97a4b07 100755 --- a/scripts/generate-skills.js +++ b/scripts/generate-skills.js @@ -218,13 +218,6 @@ function extractThresholds(content) { return '' } -// Strip internal relative links (e.g. /CoreWebVitals/LCP-Sub-Parts) from markdown link text -function cleanLinks(text) { - return text - .replace(/\[([^\]]+)\]\(\/[^)]+\)/g, '$1') // remove internal links, keep text - .replace(/\[([^\]]+)\]\((https?:[^)]+)\)/g, '[$1]($2)') // keep external links -} - // Build metadata for a single snippet JS file function buildSnippetMeta(category, snippetFile) { const basename = path.basename(snippetFile, '.js') diff --git a/scripts/install-global.js b/scripts/install-global.js index 41e2cf5..ea2f276 100755 --- a/scripts/install-global.js +++ b/scripts/install-global.js @@ -25,7 +25,7 @@ function main() { console.log('1️⃣ Generating skills...') try { execSync('node scripts/generate-skills.js', { cwd: ROOT, stdio: 'inherit' }) - } catch (error) { + } catch { console.error('❌ Failed to generate skills') process.exit(1) } diff --git a/scripts/install-skills.js b/scripts/install-skills.js index 0f0e94b..337595e 100755 --- a/scripts/install-skills.js +++ b/scripts/install-skills.js @@ -26,7 +26,7 @@ function main() { console.log('1️⃣ Generating skills...') try { execSync('node scripts/generate-skills.js', { cwd: ROOT, stdio: 'inherit' }) - } catch (error) { + } catch { console.error('❌ Failed to generate skills') process.exit(1) } diff --git a/snippets/Loading/Back-Forward-Cache.js b/snippets/Loading/Back-Forward-Cache.js index 271e00c..0aec038 100644 --- a/snippets/Loading/Back-Forward-Cache.js +++ b/snippets/Loading/Back-Forward-Cache.js @@ -412,7 +412,7 @@ // Initialize checkRestoration(); - const eligibility = testEligibility(); + testEligibility(); // Display after a short delay to ensure pageshow event fires setTimeout(() => { diff --git a/snippets/Loading/CSS-Media-Queries-Analysis.js b/snippets/Loading/CSS-Media-Queries-Analysis.js index 81c114c..57dfe6d 100644 --- a/snippets/Loading/CSS-Media-Queries-Analysis.js +++ b/snippets/Loading/CSS-Media-Queries-Analysis.js @@ -142,14 +142,14 @@ async function analyzeCSSMediaQueries(minWidth = 768) { } }); } - } catch (e) { + } catch { // If CORS blocked, try to fetch the CSS if (sheet.href) { try { const response = await fetch(sheet.href); const cssText = await response.text(); parseMediaQueriesFromCSS(cssText, sheet.href, false); - } catch (fetchError) { + } catch { // Silently count CORS blocked files corsBlockedCount++; } @@ -485,6 +485,8 @@ async function analyzeCSSPerformanceImpact(minWidth = 768) { }; } +window.analyzeCSSPerformanceImpact = analyzeCSSPerformanceImpact; + // Run with default breakpoint (768px) (async () => { const result = await analyzeCSSMediaQueries(); diff --git a/snippets/Loading/Client-Side-Redirect-Detection.js b/snippets/Loading/Client-Side-Redirect-Detection.js index 86cdd12..2be467a 100644 --- a/snippets/Loading/Client-Side-Redirect-Detection.js +++ b/snippets/Loading/Client-Side-Redirect-Detection.js @@ -207,8 +207,6 @@ // Recommendations const hasDocumentNavigation = documentNavigations.length > 0; const hasSameOriginRedirect = sameOrigin && referrerPath !== currentPath && referrerPath !== ''; - const hasHighImpact = hasDocumentNavigation && documentNavigations.reduce((sum, nav) => sum + nav.duration, 0) > 1000; - if (hasDocumentNavigation || serverRedirects > 0) { console.log(''); console.log('%c💡 Recommendations:', 'font-weight: bold;'); diff --git a/snippets/Loading/Content-Visibility.js b/snippets/Loading/Content-Visibility.js index c87bc95..73195f6 100644 --- a/snippets/Loading/Content-Visibility.js +++ b/snippets/Loading/Content-Visibility.js @@ -36,7 +36,7 @@ function detectContentVisibility() { if (el.id) break; node = el.parentNode; } - } catch (err) { + } catch { // Do nothing... } return sel; @@ -188,7 +188,7 @@ function analyzeContentVisibilityOpportunities(options = {}) { if (el.id) break; node = el.parentNode; } - } catch (err) {} + } catch {} return sel; } @@ -271,7 +271,13 @@ function analyzeContentVisibilityOpportunities(options = {}) { console.log(""); // Show table without element reference - const tableData = opportunities.slice(0, 20).map(({ element, ...rest }) => rest); + const tableData = opportunities.slice(0, 20).map((opportunity) => ({ + selector: opportunity.selector, + height: opportunity.height, + distanceFromViewport: opportunity.distanceFromViewport, + childElements: opportunity.childElements, + estimatedSavings: opportunity.estimatedSavings, + })); console.table(tableData); if (opportunities.length > 20) { @@ -304,13 +310,21 @@ function analyzeContentVisibilityOpportunities(options = {}) { console.groupEnd(); return { - opportunities: opportunities.map(({ element, ...rest }) => rest), + opportunities: opportunities.map((opportunity) => ({ + selector: opportunity.selector, + height: opportunity.height, + distanceFromViewport: opportunity.distanceFromViewport, + childElements: opportunity.childElements, + estimatedSavings: opportunity.estimatedSavings, + })), totalElements: opportunities.length, highImpact: opportunities.filter((o) => o.estimatedSavings.startsWith("High")).length, elements: opportunities.map((o) => o.element), }; } +window.analyzeContentVisibilityOpportunities = analyzeContentVisibilityOpportunities; + // Run detection (() => { const cvResults = detectContentVisibility(); diff --git a/snippets/Loading/Critical-CSS-Detection.js b/snippets/Loading/Critical-CSS-Detection.js index ed039d0..ba6fc00 100644 --- a/snippets/Loading/Critical-CSS-Detection.js +++ b/snippets/Loading/Critical-CSS-Detection.js @@ -57,7 +57,7 @@ (s) => s.href === href ); if (sheet && sheet.cssRules) ruleCount = sheet.cssRules.length; - } catch (_) { + } catch { corsBlocked = true; } diff --git a/snippets/Loading/Find-Above-The-Fold-Lazy-Loaded-Images.js b/snippets/Loading/Find-Above-The-Fold-Lazy-Loaded-Images.js index 71490ac..1eb7583 100644 --- a/snippets/Loading/Find-Above-The-Fold-Lazy-Loaded-Images.js +++ b/snippets/Loading/Find-Above-The-Fold-Lazy-Loaded-Images.js @@ -19,7 +19,7 @@ if (el.id) break; node = el.parentNode; } - } catch (err) {} + } catch {} return sel; } @@ -54,7 +54,7 @@ if (entry && entry.transferSize) { return (entry.transferSize / 1024).toFixed(1) + " KB"; } - } catch (err) {} + } catch {} return null; } @@ -189,10 +189,19 @@ // Table of all problematic images console.group("📋 Lazy Loaded Images in Viewport"); - const tableData = results.lazyImages.map(({ element, area, position, ...rest }) => ({ - ...rest, - top: position.top + "px", - isLcpCandidate: rest.isLcpCandidate ? "⚠️ YES" : "no" + const tableData = results.lazyImages.map((image) => ({ + selector: image.selector, + lazyType: image.lazyType, + dimensions: image.dimensions, + top: image.position.top + "px", + distanceFromEdge: image.distanceFromEdge, + src: image.src, + srcset: image.srcset, + sizes: image.sizes, + alt: image.alt, + fetchPriority: image.fetchPriority, + fileSize: image.fileSize, + isLcpCandidate: image.isLcpCandidate ? "⚠️ YES" : "no", })); console.table(tableData); console.groupEnd(); @@ -233,7 +242,20 @@ withDataSrc: results.summary.withDataSrc, lcpAffected: results.summary.lcpAffected, }, - items: results.lazyImages.map(({ element, area, ...rest }) => rest), + items: results.lazyImages.map((image) => ({ + selector: image.selector, + lazyType: image.lazyType, + dimensions: image.dimensions, + position: image.position, + distanceFromEdge: image.distanceFromEdge, + src: image.src, + srcset: image.srcset, + sizes: image.sizes, + alt: image.alt, + fetchPriority: image.fetchPriority, + fileSize: image.fileSize, + isLcpCandidate: image.isLcpCandidate, + })), issues: [ ...(results.summary.lcpAffected ? [{ severity: "error", message: 'LCP candidate image has lazy loading — remove loading="lazy" and add fetchpriority="high"' }] : []), ...(results.lazyImages.filter(i => !i.isLcpCandidate).length > 0 ? [{ severity: "warning", message: `${results.lazyImages.filter(i => !i.isLcpCandidate).length} above-fold image(s) have lazy loading — remove loading="lazy"` }] : []), diff --git a/snippets/Loading/Find-Images-With-Lazy-and-Fetchpriority.js b/snippets/Loading/Find-Images-With-Lazy-and-Fetchpriority.js index c3b1fd4..b0b86f8 100644 --- a/snippets/Loading/Find-Images-With-Lazy-and-Fetchpriority.js +++ b/snippets/Loading/Find-Images-With-Lazy-and-Fetchpriority.js @@ -19,7 +19,7 @@ if (el.id) break; node = el.parentNode; } - } catch (err) {} + } catch {} return sel; } @@ -87,12 +87,18 @@ }); console.log("%c📋 Conflicting Elements:", "font-weight: bold;"); - console.table(tableData.map(({ element, ...rest }) => rest)); + console.table(tableData.map((item) => ({ + selector: item.selector, + dimensions: item.dimensions, + inViewport: item.inViewport, + isLcpCandidate: item.isLcpCandidate, + src: item.src, + }))); // Elements for inspection console.log(""); console.log("%c🔎 Elements for inspection:", "font-weight: bold;"); - tableData.forEach(({ element, selector, isLcpCandidate }, i) => { + tableData.forEach(({ element, isLcpCandidate }, i) => { const marker = isLcpCandidate === "⚠️ Yes" ? " 🚨 LCP" : ""; console.log(`${i + 1}.${marker}`, element); }); diff --git a/snippets/Loading/Find-non-Lazy-Loaded-Images-outside-of-the-viewport.js b/snippets/Loading/Find-non-Lazy-Loaded-Images-outside-of-the-viewport.js index a03c41c..880a08c 100644 --- a/snippets/Loading/Find-non-Lazy-Loaded-Images-outside-of-the-viewport.js +++ b/snippets/Loading/Find-non-Lazy-Loaded-Images-outside-of-the-viewport.js @@ -19,7 +19,7 @@ if (el.id) break; node = el.parentNode; } - } catch (err) {} + } catch {} return sel; } @@ -58,7 +58,7 @@ if (parent.matches(selector)) { return { hidden: true, reason: selector, container: getSelector(parent) }; } - } catch (e) {} + } catch {} } parent = parent.parentElement; } @@ -193,7 +193,14 @@ // Below the fold images if (results.belowFold.length > 0) { console.group(`📍 Below The Fold (${results.belowFold.length} images)`); - const tableData = results.belowFold.slice(0, 20).map(({ element, fullSrc, ...rest }) => rest); + const tableData = results.belowFold.slice(0, 20).map((img) => ({ + selector: img.selector, + src: img.src, + dimensions: img.dimensions, + size: img.size, + sizeFormatted: img.sizeFormatted, + distanceFromViewport: img.distanceFromViewport, + })); console.table(tableData); if (results.belowFold.length > 20) { console.log(`... and ${results.belowFold.length - 20} more images`); @@ -207,7 +214,15 @@ console.group(`🔒 In Hidden Containers (${results.hiddenContainers.length} images)`); console.log("Images in tabs, modals, carousels, or other hidden elements:"); console.log(""); - const tableData = results.hiddenContainers.slice(0, 15).map(({ element, fullSrc, distanceFromViewport, ...rest }) => rest); + const tableData = results.hiddenContainers.slice(0, 15).map((img) => ({ + selector: img.selector, + src: img.src, + dimensions: img.dimensions, + size: img.size, + sizeFormatted: img.sizeFormatted, + hiddenReason: img.hiddenReason, + container: img.container, + })); console.table(tableData); if (results.hiddenContainers.length > 15) { console.log(`... and ${results.hiddenContainers.length - 15} more images`); diff --git a/snippets/Loading/Fonts-Preloaded-Loaded-and-used-above-the-fold.js b/snippets/Loading/Fonts-Preloaded-Loaded-and-used-above-the-fold.js index 762096e..33976e1 100644 --- a/snippets/Loading/Fonts-Preloaded-Loaded-and-used-above-the-fold.js +++ b/snippets/Loading/Fonts-Preloaded-Loaded-and-used-above-the-fold.js @@ -103,10 +103,6 @@ normalizeFontFamily(f.name.replace(/\.(woff2?|ttf|otf|eot)$/i, "")) ); - const loadedFamilies = uniqueLoadedFonts.map((f) => - normalizeFontFamily(f.family) - ); - const usedFamilies = usedFonts.map((f) => normalizeFontFamily(f.family)); // Fonts preloaded but not used above the fold diff --git a/snippets/Loading/Inline-CSS-Info-and-Size.js b/snippets/Loading/Inline-CSS-Info-and-Size.js index 9eb2018..d4fdd6d 100644 --- a/snippets/Loading/Inline-CSS-Info-and-Size.js +++ b/snippets/Loading/Inline-CSS-Info-and-Size.js @@ -75,8 +75,8 @@ contentMap.get(normalized).push(s.index); }); const duplicates = Array.from(contentMap.entries()) - .filter(([_, indices]) => indices.length > 1) - .map(([_, indices]) => indices); + .filter(([, indices]) => indices.length > 1) + .map(([, indices]) => indices); // Rating based on total size let rating, ratingColor; diff --git a/snippets/Loading/Inline-Script-Info-and-Size.js b/snippets/Loading/Inline-Script-Info-and-Size.js index 8a68e59..a7c7158 100644 --- a/snippets/Loading/Inline-Script-Info-and-Size.js +++ b/snippets/Loading/Inline-Script-Info-and-Size.js @@ -105,8 +105,8 @@ } }); const duplicates = Array.from(contentMap.entries()) - .filter(([_, indices]) => indices.length > 1) - .map(([_, indices]) => indices); + .filter(([, indices]) => indices.length > 1) + .map(([, indices]) => indices); // Rating let rating, ratingColor; diff --git a/snippets/Loading/JS-Execution-Time-Breakdown.js b/snippets/Loading/JS-Execution-Time-Breakdown.js index 64c069e..87e018e 100644 --- a/snippets/Loading/JS-Execution-Time-Breakdown.js +++ b/snippets/Loading/JS-Execution-Time-Breakdown.js @@ -21,8 +21,6 @@ const domInteractive = nav?.domInteractive || 0; const domContentLoaded = nav?.domContentLoadedEventEnd || 0; const loadEvent = nav?.loadEventEnd || 0; - const fetchStart = nav?.fetchStart || 0; - // 2. Script resource timing (download phase) const scriptResources = performance .getEntriesByType("resource") diff --git a/snippets/Loading/Resource-Hints-Validation.js b/snippets/Loading/Resource-Hints-Validation.js index b1655d7..6046a6c 100644 --- a/snippets/Loading/Resource-Hints-Validation.js +++ b/snippets/Loading/Resource-Hints-Validation.js @@ -395,8 +395,8 @@ .filter(([domain, count]) => count >= 2 && !hintedDomains.has(domain)) .sort((a, b) => b[1] - a[1]); - const topPreconnects = missingHints.filter(([_, count]) => count >= 5).slice(0, 3); - const topDnsPrefetch = missingHints.filter(([_, count]) => count >= 2 && count < 5).slice(0, 5); + const topPreconnects = missingHints.filter(([, count]) => count >= 5).slice(0, 3); + const topDnsPrefetch = missingHints.filter(([, count]) => count >= 2 && count < 5).slice(0, 5); const shownHints = [...topPreconnects, ...topDnsPrefetch]; if (missingHints.length > 0) { diff --git a/snippets/Loading/SSR-Hydration-Data-Analysis.js b/snippets/Loading/SSR-Hydration-Data-Analysis.js index 4c7940c..3472b00 100644 --- a/snippets/Loading/SSR-Hydration-Data-Analysis.js +++ b/snippets/Loading/SSR-Hydration-Data-Analysis.js @@ -269,7 +269,7 @@ console.log(""); console.log("%c📦 Raw data object:", "font-weight: bold;"); console.log(data); - } catch (e) { + } catch { console.log("Could not parse JSON content"); } } diff --git a/snippets/Loading/Service-Worker-Analysis.js b/snippets/Loading/Service-Worker-Analysis.js index b72e436..3dbd26f 100644 --- a/snippets/Loading/Service-Worker-Analysis.js +++ b/snippets/Loading/Service-Worker-Analysis.js @@ -76,7 +76,7 @@ ' 💡 Enable with: registration.navigationPreload.enable()' ); } - } catch (_) { + } catch { // Access may be restricted } } @@ -183,7 +183,7 @@ } console.log(` Total entries: ${totalEntries}`); } - } catch (_) { + } catch { // Cross-origin restrictions may prevent cache access } } diff --git a/snippets/Loading/TTFB-Resources.js b/snippets/Loading/TTFB-Resources.js index 26f5869..63afabe 100644 --- a/snippets/Loading/TTFB-Resources.js +++ b/snippets/Loading/TTFB-Resources.js @@ -51,12 +51,12 @@ // Table (sorted by TTFB, slowest first) console.log(""); console.log("%cResources (sorted by TTFB, slowest first):", "font-weight: bold;"); - const tableData = resourcesData.slice(0, 25).map(({ fullUrl, ...rest }) => ({ - "TTFB (ms)": rest.ttfb.toFixed(0), - "Duration (ms)": rest.duration.toFixed(0), - Type: rest.type, - "3rd Party": rest.thirdParty ? "Yes" : "", - Resource: rest.resource, + const tableData = resourcesData.slice(0, 25).map((resource) => ({ + "TTFB (ms)": resource.ttfb.toFixed(0), + "Duration (ms)": resource.duration.toFixed(0), + Type: resource.type, + "3rd Party": resource.thirdParty ? "Yes" : "", + Resource: resource.resource, })); console.table(tableData); diff --git a/snippets/Loading/Validate-Preload-Async-Defer-Scripts.js b/snippets/Loading/Validate-Preload-Async-Defer-Scripts.js index fd86d53..3cd4ff4 100644 --- a/snippets/Loading/Validate-Preload-Async-Defer-Scripts.js +++ b/snippets/Loading/Validate-Preload-Async-Defer-Scripts.js @@ -95,17 +95,13 @@ // Determine natural priority let naturalPriority = "Medium/High"; - let executionPriority = "Low"; if (isAsync) { naturalPriority = "Lowest/Low"; - executionPriority = "High"; } else if (isDefer) { naturalPriority = "Lowest/Low"; - executionPriority = "VeryLow"; } else if (inHead) { naturalPriority = "Medium/High"; - executionPriority = "VeryHigh"; } // Check for anti-pattern: preload + async/defer