const EXTERNAL_URL_REGEX = /^(?:[a-z+]+:)?\/\//i; const perf = [ { code: "perf-use-image-component", title: "Use the Image component", message: "This image could be replaced with the Image component to improve performance.", selector: "img:not([data-image-component])", async match(element) { const src = element.getAttribute("src"); if (!src) return false; if (src.startsWith("data:")) return false; if (!EXTERNAL_URL_REGEX.test(src)) { const imageData = await fetch(src).then((response) => response.blob()); if (imageData.size < 20480) return false; } return true; } }, { code: "perf-use-loading-lazy", title: 'Use the loading="lazy" attribute', message: (element) => `This ${element.nodeName} tag is below the fold and could be lazy-loaded to improve performance.`, selector: 'img:not([loading]), img[loading="eager"], iframe:not([loading]), iframe[loading="eager"]', match(element) { const htmlElement = element; if (htmlElement.offsetTop < window.innerHeight) return false; return true; } }, { code: "perf-use-loading-eager", title: 'Use the loading="eager" attribute', message: (element) => `This ${element.nodeName} tag is above the fold and could be eagerly-loaded to improve performance.`, selector: 'img[loading="lazy"], iframe[loading="lazy"]', match(element) { const htmlElement = element; if (htmlElement.offsetTop > window.innerHeight) return false; return true; } }, { code: "perf-use-videos", title: "Use videos instead of GIFs for large animations", message: "This GIF could be replaced with a video to reduce its file size and improve performance.", selector: 'img[src$=".gif"]', async match(element) { const src = element.getAttribute("src"); if (!src) return false; if (EXTERNAL_URL_REGEX.test(src)) return false; if (!EXTERNAL_URL_REGEX.test(src)) { const imageData = await fetch(src).then((response) => response.blob()); if (imageData.size < 102400) return false; } return true; } }, { code: "perf-slow-component-server-render", title: "Server-rendered component took a long time to render", message: (element) => `This component took an unusually long time to render on the server (${getCleanRenderingTime( element.getAttribute("server-render-time") )}). This might be a sign that it's doing too much work on the server, or something is blocking rendering.`, selector: "astro-island[server-render-time]", match(element) { const serverRenderTime = element.getAttribute("server-render-time"); if (!serverRenderTime) return false; const renderingTime = parseFloat(serverRenderTime); if (Number.isNaN(renderingTime)) return false; return renderingTime > 500; } }, { code: "perf-slow-component-client-hydration", title: "Client-rendered component took a long time to hydrate", message: (element) => `This component took an unusually long time to render on the server (${getCleanRenderingTime( element.getAttribute("client-render-time") )}). This could be a sign that something is blocking the main thread and preventing the component from hydrating quickly.`, selector: "astro-island[client-render-time]", match(element) { const clientRenderTime = element.getAttribute("client-render-time"); if (!clientRenderTime) return false; const renderingTime = parseFloat(clientRenderTime); if (Number.isNaN(renderingTime)) return false; return renderingTime > 500; } } ]; function getCleanRenderingTime(time) { if (!time) return "unknown"; const renderingTime = parseFloat(time); if (Number.isNaN(renderingTime)) return "unknown"; return renderingTime.toFixed(2) + "s"; } export { perf };