+ );
+}
diff --git a/package.json b/package.json
index aa7eeb3..075e8a3 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"validate": "npm run lint && npm run build",
+ "prepare": "git config core.hooksPath .githooks",
"test": "echo \"No tests configured\""
},
"keywords": [
diff --git a/pages/_meta.json b/pages/_meta.json
index 8c473cd..9282303 100644
--- a/pages/_meta.json
+++ b/pages/_meta.json
@@ -22,5 +22,8 @@
},
"CLI": {
"title": "CLI"
+ },
+ "visualizer": {
+ "title": "Visualizer"
}
}
diff --git a/pages/visualizer.mdx b/pages/visualizer.mdx
new file mode 100644
index 0000000..d821995
--- /dev/null
+++ b/pages/visualizer.mdx
@@ -0,0 +1,17 @@
+---
+type: tool
+---
+
+import { SnippetVisualizer } from "../components/SnippetVisualizer";
+
+# Snippet Visualizer
+
+Paste the return value of any snippet to generate a formatted report, useful for sites that block extended DevTools output (e.g., Akamai-protected sites).
+
+**How to get the data:**
+
+1. Run any snippet in the DevTools console
+2. Right-click the result object → **"Copy object"** — then paste below
+3. Or run `JSON.stringify(result)` and paste the output
+
+
diff --git a/public/llms-full.txt b/public/llms-full.txt
index d12ba42..516c4cb 100644
--- a/public/llms-full.txt
+++ b/public/llms-full.txt
@@ -1589,8 +1589,6 @@ Quick check for Largest Contentful Paint, a Core Web Vital that measures loading
return { script: "LCP", status: "error", error: "No LCP entries buffered" };
}
- printLCP(lastLcpEntry);
-
const lcpActivationStart = getActivationStart();
const lcpValue = Math.round(Math.max(0, lastLcpEntry.startTime - lcpActivationStart));
const lcpRating = valueToRating(lcpValue);
@@ -3068,6 +3066,15 @@ Audits HTTP caching strategies across all page resources, identifying resources
// --- Phase 7: Console output ---
+ if (entries.length === 0) {
+ console.group("%c🔍 Cache Strategy Analysis", "font-weight: bold; font-size: 14px;");
+ console.warn("⚠️ No resources found in the Performance API buffer.");
+ console.log(" This usually means the site called performance.clearResourceTimings() to manage memory.");
+ console.log(" Try: reload the page and run this snippet immediately after load.");
+ console.groupEnd();
+ return { script: "Cache-Strategy-Analysis", status: "no-data", count: 0 };
+ }
+
const allAntiPatterns = entries.flatMap((e) =>
e.antiPatterns.map((ap) => ({ ...ap, resource: e.shortName, url: e.url }))
);
@@ -3347,7 +3354,7 @@ Audits HTTP caching strategies across all page resources, identifying resources
"🟡 Replace Expires headers with Cache-Control: max-age — Expires is an outdated mechanism and less reliable."
);
}
- if (cacheEfficiencyPercent < 50) {
+ if (entries.length > 0 && cacheEfficiencyPercent < 50) {
recommendations.push(
`🔴 Cache efficiency is low (${cacheEfficiencyPercent}%). Review caching strategy for all static assets.`
);
@@ -5554,7 +5561,7 @@ Identifies images that are loaded eagerly but not visible in the initial viewpor
## Find render-blocking resources
URL: https://webperf-snippets.nucliweb.net/Loading/Find-render-blocking-resources
-Identifies resources that block the browser from rendering the page. These resources must be fully downloaded and processed before the browser can display any content, directly impacting First Contentful Paint (FCP) and Largest Contentful Paint (LCP). Why this matters: Render-blocking resources are the primary cause of slow initial page renders. Users see a blank white screen while CSS and JavaScript files download and parse. On slow connections or mobile networks, this can add several seconds to your load time. Eliminating or deferring render-blocking resources is one of the highest-impact optimizations you can make. What are render-blocking resources?
+Identifies resources that block the browser from rendering the page. These resources must be fully downloaded and processed before the browser can display any content, directly impacting First Contentful Paint (FCP) and Largest Contentful Paint (LCP). Render-blocking resources are the primary cause of slow initial page renders. Users see a blank white screen while CSS and JavaScript files download and parse. On slow connections or mobile networks, this can add several seconds to your load time. Eliminating or deferring render-blocking resources is one of the highest-impact optimizations you can make.
```js
// Find render-blocking resources
@@ -5590,6 +5597,15 @@ Identifies resources that block the browser from rendering the page. These resou
})
.sort((a, b) => b.responseEnd - a.responseEnd);
+ const lastBlockingEnd = blockingResources.length
+ ? Math.max(...blockingResources.map((r) => r.responseEnd))
+ : 0;
+ const totalSizeBytes = blockingResources.reduce((sum, r) => sum + r.size, 0);
+ const byType = blockingResources.reduce((acc, r) => {
+ acc[r.type] = (acc[r.type] || 0) + 1;
+ return acc;
+ }, {});
+
console.group("%c🚧 Render-Blocking Resources", "font-weight: bold; font-size: 14px;");
if (blockingResources.length === 0) {
@@ -5604,14 +5620,6 @@ Identifies resources that block the browser from rendering the page. These resou
console.log(" • Scripts use async or defer attributes");
console.log(" • Critical resources are optimized");
} else {
- // Calculate metrics
- const lastBlockingEnd = Math.max(...blockingResources.map((r) => r.responseEnd));
- const totalSize = blockingResources.reduce((sum, r) => sum + r.size, 0);
- const byType = blockingResources.reduce((acc, r) => {
- acc[r.type] = (acc[r.type] || 0) + 1;
- return acc;
- }, {});
-
// Summary
console.log(
`%c⚠️ Found ${blockingResources.length} render-blocking resource(s)`,
@@ -5622,8 +5630,8 @@ Identifies resources that block the browser from rendering the page. These resou
console.log("%c📊 Impact Summary:", "font-weight: bold;");
console.log(` Rendering blocked until: ${lastBlockingEnd.toFixed(0)}ms`);
console.log(` Total blocking resources: ${blockingResources.length}`);
- if (totalSize > 0) {
- const sizeKB = (totalSize / 1024).toFixed(1);
+ if (totalSizeBytes > 0) {
+ const sizeKB = (totalSizeBytes / 1024).toFixed(1);
console.log(` Total size: ${sizeKB} KB`);
}
console.log(` By type: ${Object.entries(byType).map(([k, v]) => `${k} (${v})`).join(", ")}`);
@@ -5693,9 +5701,6 @@ Identifies resources that block the browser from rendering the page. These resou
console.groupEnd();
- const lastBlockingEnd = blockingResources.length ? Math.max(...blockingResources.map((r) => r.responseEnd)) : 0;
- const totalSizeBytes = blockingResources.reduce((sum, r) => sum + r.size, 0);
- const byType = blockingResources.reduce((acc, r) => { acc[r.type] = (acc[r.type] || 0) + 1; return acc; }, {});
return {
script: "Find-render-blocking-resources",
status: "ok",
diff --git a/public/llms.txt b/public/llms.txt
index 372e477..c243974 100644
--- a/public/llms.txt
+++ b/public/llms.txt
@@ -64,7 +64,7 @@
Identifies images that are loaded eagerly but not visible in the initial viewport, representing wasted bandwidth and parsing time that delays page interactivity. The snippet analyzes all `` elements to find optimization opportunities for lazy loading. Images outside the viewport that load immediately: - Waste bandwidth by downloading resources users may never see - Block the main thread during decoding and rendering - Delay LCP by competing for network and CPU resources - Increase memory usage unnecessarily This script detects images without `loading="lazy"` or `[data-src]` attributes that are positioned outside the initial viewport, including images in hidden containers (tabs, modals, carousels). It also identifies the LCP candidate to ensure you don't accidentally lazy-load it.
- [Find render-blocking resources](https://webperf-snippets.nucliweb.net/Loading/Find-render-blocking-resources)
- Identifies resources that block the browser from rendering the page. These resources must be fully downloaded and processed before the browser can display any content, directly impacting First Contentful Paint (FCP) and Largest Contentful Paint (LCP). Why this matters: Render-blocking resources are the primary cause of slow initial page renders. Users see a blank white screen while CSS and JavaScript files download and parse. On slow connections or mobile networks, this can add several seconds to your load time. Eliminating or deferring render-blocking resources is one of the highest-impact optimizations you can make. What are render-blocking resources?
+ Identifies resources that block the browser from rendering the page. These resources must be fully downloaded and processed before the browser can display any content, directly impacting First Contentful Paint (FCP) and Largest Contentful Paint (LCP). Render-blocking resources are the primary cause of slow initial page renders. Users see a blank white screen while CSS and JavaScript files download and parse. On slow connections or mobile networks, this can add several seconds to your load time. Eliminating or deferring render-blocking resources is one of the highest-impact optimizations you can make.
- [First And Third Party Script Info](https://webperf-snippets.nucliweb.net/Loading/First-And-Third-Party-Script-Info)
Analyzes all scripts loaded on the page, separating them into first-party (your domain) and third-party (external) scripts. This helps identify the performance impact of external dependencies. Why this matters:
diff --git a/scripts/check-consistency.js b/scripts/check-consistency.js
index 56d99e2..13a02df 100644
--- a/scripts/check-consistency.js
+++ b/scripts/check-consistency.js
@@ -19,6 +19,7 @@ const CATEGORY_SKILLS = {
}
const ROOT_EDITORIAL_PAGES = new Set(['index'])
+const NON_SNIPPET_PAGE_TYPES = new Set(['guide', 'tool'])
function readFile(filePath) {
return fs.readFileSync(filePath, 'utf8')
@@ -81,17 +82,12 @@ function getCategoryPages(category) {
function verifySourceToPageMapping(errors) {
for (const category of Object.keys(CATEGORY_SKILLS)) {
for (const snippetFile of getSnippetFiles(category)) {
- let found = false
-
- for (const mdxFile of getCategoryPages(category)) {
+ const isImported = getCategoryPages(category).some((mdxFile) => {
const content = readFile(path.join(PAGES_DIR, category, mdxFile))
- if (content.includes(`/snippets/${category}/${snippetFile}?raw`)) {
- found = true
- break
- }
- }
+ return content.includes(`/snippets/${category}/${snippetFile}?raw`)
+ })
- if (!found) {
+ if (!isImported) {
errors.push(`Missing MDX page import for snippet ${category}/${snippetFile}`)
}
}
@@ -106,10 +102,10 @@ function verifyPageToSourceMapping(errors) {
const content = readFile(path.join(PAGES_DIR, rootPage))
const frontmatter = parseFrontmatter(content)
- if (frontmatter.type === 'guide') continue
+ if (NON_SNIPPET_PAGE_TYPES.has(frontmatter.type)) continue
if (getSnippetImports(content).length === 0) {
- errors.push(`Root page ${rootPage} has no snippet import and is not marked with type: guide`)
+ errors.push(`Root page ${rootPage} has no snippet import and is not marked with type: guide or type: tool`)
}
}
@@ -121,11 +117,11 @@ function verifyPageToSourceMapping(errors) {
const content = readFile(path.join(PAGES_DIR, category, mdxFile))
const frontmatter = parseFrontmatter(content)
- if (frontmatter.type === 'guide') continue
+ if (NON_SNIPPET_PAGE_TYPES.has(frontmatter.type)) continue
const imports = getSnippetImports(content)
if (imports.length === 0) {
- errors.push(`Page ${category}/${mdxFile} has no snippet import and is not marked with type: guide`)
+ errors.push(`Page ${category}/${mdxFile} has no snippet import and is not marked with type: guide or type: tool`)
continue
}