nucliweb / webperf-snippets

⚡️ 💾 Web Performance Snippets
https://webperf-snippets.nucliweb.net
MIT License
1.34k stars 72 forks source link

Export/import snippets #29

Open hhkaos opened 1 year ago

hhkaos commented 1 year ago

First of all, thanks @nucliweb for sharing and the great work! 😍

Would it make sense to have a file ready to be imported in DevTools? I found this: https://levelup.gitconnected.com/snippets-in-google-chrome-62636236b766#910f

nucliweb commented 1 year ago

Hi @hhkaos, of course, IMO makes sense.

The past week I asked about this https://twitter.com/nucliweb/status/1662583194888159232

hhkaos commented 1 year ago

Nice!

In the mean time, this is how I did it following these guidelines (script below):

https://github.com/nucliweb/webperf-snippets/assets/826965/1a300c42-fc14-421f-b1bc-f755f1df81be

Script

InspectorFrontendHost.setPreference("scriptSnippets", "[{\"name\":\"CWV - Largest Contentful Paint (LCP)\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/CoreWebVitals/LCP\\n\\n   Core Web Vitals - Largest Contentful Paint (LCP)\\n   The next scripts show information about the LCP(opens in a new tab) metric.\\n*/\\nconst po = new PerformanceObserver((list) => {\\n  let entries = list.getEntries();\\n\\n  entries = dedupe(entries, \\\"startTime\\\");\\n\\n  /**\\n   * Print all entries of LCP\\n   */\\n  entries.forEach((item, i) => {\\n    console.dir(item);\\n    console.log(\\n      `${i + 1} current LCP item : ${item.element}: ${item.startTime}`\\n    );\\n    /**\\n     * Highlight LCP elements on the page\\n     */\\n    item.element ? (item.element.style = \\\"border: 5px dotted blue;\\\") : \\\"\\\";\\n  });\\n\\n  /**\\n   * LCP is the lastEntry in getEntries Array\\n   */\\n  const lastEntry = entries[entries.length - 1];\\n  /**\\n   * Print final LCP\\n   */\\n  console.log(`LCP is: ${lastEntry.startTime}`);\\n});\\n\\n/**\\n * Start observing for largest-contentful-paint\\n * buffered true getEntries prior to this script execution\\n */\\npo.observe({ type: \\\"largest-contentful-paint\\\", buffered: true });\\n\\nfunction dedupe(arr, key) {\\n  return [...new Map(arr.map((item) => [item[key], item])).values()];\\n\"},{\"name\":\"CWV - Largest Contentful Paint Sub-Parts (LCP)\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/CoreWebVitals/LCP-Sub-Parts\\n\\n   Core Web Vitals - Largest Contentful Paint Sub-Parts (LCP)\\n   This script it's part of the Web Vitals Chrome Extension(opens in \\n   a new tab) and appear on the Optimize Largest Contentful Paint(opens \\n   in a new tab) post.\\n*/\\nconst LCP_SUB_PARTS = [\\n  'Time to first byte',\\n  'Resource load delay',\\n  'Resource load time',\\n  'Element render delay',\\n];\\n\\nnew PerformanceObserver((list) => {\\n  const lcpEntry = list.getEntries().at(-1);\\n  const navEntry = performance.getEntriesByType('navigation')[0];\\n  const lcpResEntry = performance\\n    .getEntriesByType('resource')\\n    .filter((e) => e.name === lcpEntry.url)[0];\\n\\n  const ttfb = navEntry.responseStart;\\n  const lcpRequestStart = Math.max(\\n    ttfb,\\n    lcpResEntry ? lcpResEntry.requestStart || lcpResEntry.startTime : 0\\n  );\\n  const lcpResponseEnd = Math.max(\\n    lcpRequestStart,\\n    lcpResEntry ? lcpResEntry.responseEnd : 0\\n  );\\n  const lcpRenderTime = Math.max(\\n    lcpResponseEnd,\\n    lcpEntry ? lcpEntry.startTime : 0\\n  );\\n\\n  LCP_SUB_PARTS.forEach((part) => performance.clearMeasures(part));\\n\\n  const lcpSubPartMeasures = [\\n    performance.measure(LCP_SUB_PARTS[0], {\\n      start: 0,\\n      end: ttfb,\\n    }),\\n    performance.measure(LCP_SUB_PARTS[1], {\\n      start: ttfb,\\n      end: lcpRequestStart,\\n    }),\\n    performance.measure(LCP_SUB_PARTS[2], {\\n      start: lcpRequestStart,\\n      end: lcpResponseEnd,\\n    }),\\n    performance.measure(LCP_SUB_PARTS[3], {\\n      start: lcpResponseEnd,\\n      end: lcpRenderTime,\\n    }),\\n  ];\\n\\n  // Log helpful debug information to the console.\\n  console.log('LCP value: ', lcpRenderTime);\\n  console.log('LCP element: ', lcpEntry.element, lcpEntry?.url);\\n  console.table(\\n    lcpSubPartMeasures.map((measure) => ({\\n      'LCP sub-part': measure.name,\\n      'Time (ms)': measure.duration,\\n      '% of LCP': `${\\n        Math.round((1000 * measure.duration) / lcpRenderTime) / 10\\n      }%`,\\n    }))\\n  );\\n}).observe({type: 'largest-contentful-paint', buffered: true});\"},{\"name\":\"CWV - Largest Contentful Paint, Quick BPP (image entropy) check\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/CoreWebVitals/LCP-Image-Entropy\\n\\n   Core Web Vitals - Largest Contentful Paint, Quick BPP (image entropy) check \\n   Context: Largest Contentful Paint change in Chrome 112 to ignore low-entropy images\\n\\n   This snippet is based on and with the permission Stoyan Stefanov.\\n\\n   With the script you can get a list of the BPP of all(1) images loaded on the site.\\n   (1) the images with source \\\"data:image\\\" and third-party images are ignored.\\n*/\\nconsole.table(\\n  [...document.images]\\n    .filter(\\n      (img) => img.currentSrc != \\\"\\\" && !img.currentSrc.includes(\\\"data:image\\\")\\n    )\\n    .map((img) => [\\n      img.currentSrc,\\n      (performance.getEntriesByName(img.currentSrc)[0]?.encodedBodySize * 8) /\\n        (img.width * img.height),\\n    ])\\n    .filter((img) => img[1] !== 0)\\n);\"},{\"name\":\"CWV - Cumulative Layout Shift (CLS)\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/CoreWebVitals/CLS\\n\\n   Core Web Vitals - Cumulative Layout Shift (CLS)\\n   This script displays the CLS value when the focus of the browser is switched \\n   to another tab, since the CLS is calculated during the lifetime of the page.\\n*/\\nlet cumulativeLayoutShiftScore = 0;\\nconst observer = new PerformanceObserver((list) => {\\nfor (const entry of list.getEntries()) {\\n  if (!entry.hadRecentInput) {\\n    cumulativeLayoutShiftScore += entry.value;\\n  }\\n}\\n});\\n\\nobserver.observe({ type: \\\"layout-shift\\\", buffered: true });\\n\\ndocument.addEventListener(\\\"visibilitychange\\\", () => {\\nif (document.visibilityState === \\\"hidden\\\") {\\n  observer.takeRecords();\\n  observer.disconnect();\\n\\n  console.log(`CLS: ${cumulativeLayoutShiftScore}`);\\n}\\n});\"},{\"name\":\"Interaction - Interactions\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/Interaction/Interactions\\n\\n   Interaction - Interactions\\n   This script it's part of the Web Vitals Chrome Extension(opens in a new tab) \\n   and allows you to track all interactions as you click around the page to help improve INP.\\n*/\\nconst valueToRating = (score) => score <= 200 ? 'good' : score <= 500 ? 'needs-improvement' : 'poor';\\n \\nconst COLOR_GOOD = '#0CCE6A';\\nconst COLOR_NEEDS_IMPROVEMENT = '#FFA400';\\nconst COLOR_POOR = '#FF4E42';\\nconst RATING_COLORS = {\\n  'good': COLOR_GOOD,\\n  'needs-improvement': COLOR_NEEDS_IMPROVEMENT,\\n  'poor': COLOR_POOR\\n};\\n \\nconst observer = new PerformanceObserver((list) => {\\n  const interactions = {};\\n \\n  for (const entry of list.getEntries().filter((entry) => entry.interactionId)) {\\n    interactions[entry.interactionId] = interactions[entry.interactionId] || [];\\n    interactions[entry.interactionId].push(entry);\\n  }\\n \\n  // Will report as a single interaction even if parts are in separate frames.\\n  // Consider splitting by animation frame.\\n  for (const interaction of Object.values(interactions)) {\\n    const entry = interaction.reduce((prev, curr) => prev.duration >= curr.duration ? prev : curr);\\n    const value = entry.duration;\\n    const rating = valueToRating(value);\\n \\n    const formattedValue = `${value.toFixed(0)} ms`;\\n    console.groupCollapsed(\\n      `Interaction tracking snippet %c${formattedValue} (${rating})`,\\n      `color: ${RATING_COLORS[rating] || 'inherit'}`\\n    );\\n    console.log('Interaction target:', entry.target);\\n \\n    for (let entry of interaction) {\\n      console.log(`Interaction event type: %c${entry.name}`, 'font-family: monospace');\\n \\n      // RenderTime is an estimate, because duration is rounded, and may get rounded down.\\n      // In rare cases it can be less than processingEnd and that breaks performance.measure().\\n      // Lets make sure its at least 4ms in those cases so you can just barely see it.\\n      const adjustedPresentationTime = Math.max(entry.processingEnd + 4, entry.startTime + entry.duration);\\n \\n      console.table([{\\n        subPartString: 'Input delay',\\n        'Time (ms)': Math.round(entry.processingStart - entry.startTime, 0),\\n      },\\n      {\\n        subPartString: 'Processing time',\\n        'Time (ms)': Math.round(entry.processingEnd - entry.processingStart, 0),\\n      },\\n      {\\n        subPartString: 'Presentation delay',\\n        'Time (ms)': Math.round(adjustedPresentationTime - entry.processingEnd, 0),\\n      }]);\\n    }\\n \\n    console.groupEnd();\\n \\n  }\\n});\\n \\nobserver.observe({\\n  type: 'event',\\n  durationThreshold: 0, // 16 minimum by spec\\n  buffered: true\\n});\"},{\"name\":\"Interaction - Layout Shift Loading and Interaction\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/Interaction/Layout-Shift-Loading-and-Interaction\\n\\n   Interaction - Layout Shift Loading and Interaction\\n   Print all the CLS metrics during page load and when the user interacts with the page:\\n*/\\nnew PerformanceObserver((entryList) => {\\n  console.log(entryList.getEntries());\\n}).observe({ type: \\\"layout-shift\\\", buffered: true });\"},{\"name\":\"Interaction - Long Task\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/Interaction/LongTask\\n\\n   Interaction - Long tasks\\n   To determine when long tasks happen, you can use PerformanceObserver\\n   (opens in a new tab) and register to observe entries of type longtask.\\n*/\\ntry {\\n  const po = new PerformanceObserver((list) => {\\n    for (const entry of list.getEntries()) {\\n      console.table(entry.toJSON());\\n    }\\n  });\\n  po.observe({ type: \\\"longtask\\\", buffered: true });\\n} catch (e) {\\n  console.error(`The browser doesn't support this API`);\\n}\"},{\"name\":\"Loading - TTFB - Measure the time to first byte, from the document\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/Loading/TTFB\\n\\n   Loading - Time To First Byte \\n   Measure the time to first byte, from the document\\n*/\\nnew PerformanceObserver((entryList) => {\\n  const [pageNav] = entryList.getEntriesByType(\\\"navigation\\\");\\n  console.log(`TTFB (ms): ${pageNav.responseStart}`);\\n}).observe({\\n  type: \\\"navigation\\\",\\n  buffered: true,\\n});\"},{\"name\":\"Loading - TTFB - Measure the time to first byte of all the resources loaded\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/Loading/TTFB\\n\\n   Loading - Time To First Byte \\n   Measure the time to first byte of all the resources loaded\\n*/\\nnew PerformanceObserver((entryList) => {\\n  const entries = entryList.getEntries();\\n  const resourcesLoaded = [...entries].map((entry) => {\\n    let obj = {};\\n    // Some resources may have a responseStart value of 0, due\\n    // to the resource being cached, or a cross-origin resource\\n    // being served without a Timing-Allow-Origin header set.\\n    if (entry.responseStart > 0) {\\n      obj = {\\n        \\\"TTFB (ms)\\\": entry.responseStart,\\n        Resource: entry.name,\\n      };\\n    }\\n    return obj;\\n  });\\n  console.table(resourcesLoaded);\\n}).observe({\\n  type: \\\"resource\\\",\\n  buffered: true,\\n});\"},{\"name\":\"Loading - Find Above The Fold Lazy Loaded Images\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/Loading/Find-Above-The-Fold-Lazy-Loades-Images\\n\\n   Loading - Find Above The Fold Lazy Loaded Images\\n   List all images that have loading=\\\"lazy\\\" or [data-src] (lazy loading via JS) above the fold\\n*/\\nfunction findATFLazyLoadedImages() {\\n  const lazy = document.querySelectorAll('[loading=\\\"lazy\\\"], [data-src]');\\n  let lazyImages = [];\\n  lazy.forEach((tag) => {\\n    const position = parseInt(tag.getBoundingClientRect().top);\\n    if (position < window.innerHeight && position !== 0) {\\n      lazyImages = [...lazyImages, tag];\\n    }\\n  });\\n  return lazyImages.length > 0 ? lazyImages : false;\\n}\\n \\nconsole.log(findATFLazyLoadedImages());\"},{\"name\":\"Loading - Find render-blocking resources\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/Loading/Find-render-blocking-resources\\n\\n   Loading - Find render-blocking resources\\n   List all resources that are blocking rendering.\\n   * It's currently Chromium only\\n*/\\nfunction RenderBlocking({startTime, duration, responseEnd, name, initiatorType}) {\\n  this.startTime = startTime\\n  this.duration = duration\\n  this.responseEnd = responseEnd\\n  this.name = name\\n  this.initiatorType = initiatorType\\n}\\n \\nfunction findRenderBlockingResources() {\\n  return window.performance.getEntriesByType('resource')\\n    .filter(({renderBlockingStatus}) => renderBlockingStatus === 'blocking')\\n    .map(({startTime, duration, responseEnd, name, initiatorType}) => new RenderBlocking({startTime, duration, responseEnd, name, initiatorType}));\\n}\\n \\nconsole.table(findRenderBlockingResources())\"},{\"name\":\"Loading - Find non Lazy Loaded Images outside of the viewport\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/Loading/Find-non-Lazy-Loaded-Images-outside-of-the-viewport\\n\\n   Loading - Find non Lazy Loaded Images outside of the viewport\\n   List all images that don't have loading=\\\"lazy\\\" or [data-src] (lazy \\n   loading via JS) and are not in the viewport when the page loads. \\n   This script will help you find candidates for lazy loading.\\n*/\\n// Execute it after the page has loaded without any user interaction (Scroll, click, etc)\\nfunction findImgCanidatesForLazyLoading() {\\n  let notLazyImages = document.querySelectorAll(\\n    'img:not([data-src]):not([loading=\\\"lazy\\\"])'\\n  );\\n  return Array.from(notLazyImages).filter((tag) => !isInViewport(tag));\\n}\\n \\nfunction isInViewport(tag) {\\n  let rect = tag.getBoundingClientRect();\\n  return (\\n    rect.bottom >= 0 &&\\n    rect.right >= 0 &&\\n    rect.top <= (window.innerHeight || document.documentElement.clientHeight) &&\\n    rect.left <= (window.innerWidth || document.documentElement.clientWidth)\\n  );\\n}\\n \\nconsole.log(\\n  \\\"Consider lazyloading the following images: \\\",\\n  findImgCanidatesForLazyLoading()\\n);\"},{\"name\":\"Loading - First And Third Party Script Info\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/Loading/First-And-Third-Party-Script-Info\\n\\n   Loading - First And Third Party Script Info\\n   List all scripts using PerformanceResourceTiming API and separating them by first and third party\\n*/\\n// ex: katespade.com - list firsty party subdomains in HOSTS array\\nconst HOSTS = [\\\"assets.katespade.com\\\"];\\n \\nfunction getScriptInfo() {\\n  const resourceListEntries = performance.getEntriesByType(\\\"resource\\\");\\n  // set for first party scripts\\n  const first = [];\\n  // set for third party scripts\\n  const third = [];\\n \\n  resourceListEntries.forEach((resource) => {\\n    // check for initiator type\\n    const value = \\\"initiatorType\\\" in resource;\\n    if (value) {\\n      if (resource.initiatorType === \\\"script\\\") {\\n        const { host } = new URL(resource.name);\\n        // check if resource url host matches location.host = first party script\\n        if (host === location.host || HOSTS.includes(host)) {\\n          const json = resource.toJSON();\\n          first.push({ ...json, type: \\\"First Party\\\" });\\n        } else {\\n          // add to third party script\\n          const json = resource.toJSON();\\n          third.push({ ...json, type: \\\"Third Party\\\" });\\n        }\\n      }\\n    }\\n  });\\n \\n  const scripts = {\\n    firstParty: [{ name: \\\"no data\\\" }],\\n    thirdParty: [{ name: \\\"no data\\\" }],\\n  };\\n \\n  if (first.length) {\\n    scripts.firstParty = first;\\n  }\\n \\n  if (third.length) {\\n    scripts.thirdParty = third;\\n  }\\n \\n  return scripts;\\n}\\n \\nconst { firstParty, thirdParty } = getScriptInfo();\\n \\nconsole.groupCollapsed(\\\"FIRST PARTY SCRIPTS\\\");\\nconsole.table(firstParty);\\nconsole.groupEnd();\\n \\nconsole.groupCollapsed(\\\"THIRD PARTY SCRIPTS\\\");\\nconsole.table(thirdParty);\\nconsole.groupEnd();\"},{\"name\":\"Loading - First And Third Party Script Timings\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/Loading/First-And-Third-Party-Script-Timings\\n\\n   Loading - First And Third Party Script Timings\\n   Run First And Third Party Script Info in the console first, then run this\\n*/\\nfunction createUniqueLists(firstParty, thirdParty) {\\n  function getUniqueListBy(arr, key) {\\n    return [...new Map(arr.map((item) => [item[key], item])).values()];\\n  }\\n \\n  const firstPartyList = getUniqueListBy(firstParty, [\\\"name\\\"]);\\n  const thirdPartyList = getUniqueListBy(thirdParty, [\\\"name\\\"]);\\n \\n  return { firstPartyList, thirdPartyList };\\n}\\n \\nconst { firstPartyList, thirdPartyList } = createUniqueLists(\\n  firstParty,\\n  thirdParty\\n);\\n \\nfunction calculateTimings(party, type) {\\n  const partyChoice = party === \\\"first\\\" ? firstParty : thirdParty;\\n \\n  const timingChoices = {\\n    DNS_TIME: [\\\"domainLookupEnd\\\", \\\"domainLookupStart\\\"],\\n    TCP_HANDSHAKE: [\\\"connectEnd\\\", \\\"connectStart\\\"],\\n    RESPONSE_TIME: [\\\"responseEnd\\\", \\\"responseStart\\\"],\\n    SECURE_CONNECTION_TIME: [\\\"connectEnd\\\", \\\"secureConnectionStart\\\", 0],\\n    FETCH_UNTIL_RESPONSE: [\\\"responseEnd\\\", \\\"fetchStart\\\", 0],\\n    REQ_START_UNTIL_RES_END: [\\\"responseEnd\\\", \\\"requestStart\\\", 0],\\n    START_UNTIL_RES_END: [\\\"responseEnd\\\", \\\"startTime\\\", 0],\\n    REDIRECT_TIME: [\\\"redirectEnd\\\", \\\"redirectStart\\\"],\\n  };\\n \\n  function handleChoices(timingEnd, timingStart, num) {\\n    if (!num) {\\n      return timingEnd - timingStart;\\n    }\\n \\n    if (timingStart > 0) {\\n      return timingEnd - timingStart;\\n    }\\n \\n    return 0;\\n  }\\n \\n  const timings = partyChoice.map((script) => {\\n    const [timingEnd, timingStart, num] = timingChoices[type];\\n    const endValue = script[timingEnd];\\n    const startValue = script[timingStart];\\n    return {\\n      name: script.name,\\n      [type]: handleChoices(endValue, startValue, num),\\n    };\\n  });\\n \\n  return timings;\\n}\\n \\n// Available Options\\nconst timingOptions = [\\n  \\\"DNS_TIME\\\",\\n  \\\"TCP_HANDSHAKE\\\",\\n  \\\"RESPONSE_TIME\\\",\\n  \\\"SECURE_CONNECTION_TIME\\\",\\n  \\\"FETCH_UNTIL_RESPONSE\\\",\\n  \\\"REQ_START_UNTIL_RES_END\\\",\\n  \\\"START_UNTIL_RES_END\\\",\\n  \\\"REDIRECT_TIME\\\",\\n];\\n \\n// run em all!\\n// https://developer.mozilla.org/en-US/docs/Web/API/Resource_Timing_API/Using_the_Resource_Timing_API#timing_resource_loading_phases\\n \\ntimingOptions.forEach((timing) => {\\n  console.groupCollapsed(`FIRST PARTY: ${timing}`);\\n  console.table(calculateTimings(\\\"first\\\", timing));\\n  console.groupEnd();\\n  console.groupCollapsed(`THIRD PARTY: ${timing}`);\\n  console.table(calculateTimings(\\\"third\\\", timing));\\n  console.groupEnd();\\n});\\n \\n// choose your battle - arg1 is string either \\\"first\\\" or \\\"third\\\", arg2 is string timing option listed above.\\n \\nconsole.table(calculateTimings(\\\"first\\\", \\\"REQ_START_UNTIL_RES_END\\\"));\"},{\"name\":\"Loading - Fonts Preloaded, Loaded, and Used Above The Fold\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/Loading/Fonts-Preloaded-Loaded-and-used-above-the-fold\\n\\n   Loading - Fonts Preloaded, Loaded, and Used Above The Fold\\n   List all the fonts preloaded via resources hints, all the fonts loaded via CSS, \\n   and all the fonts used in the viewport above the fold.\\n*/\\nconst linkElements = document.querySelectorAll(`link[rel=\\\"preload\\\"]`);\\nconst arrayLinks = Array.from(linkElements);\\nconst preloadedFonts = arrayLinks.filter((link) => link.as === \\\"font\\\");\\n \\nconsole.log(\\\"Fonts Preloaded via Resources Hints\\\");\\npreloadedFonts.forEach((font) => console.log(`▸ ${font.href}`));\\nconsole.log(\\\"\\\");\\n \\nconst loadedFonts = [\\n  ...new Set(\\n    Array.from(document.fonts.values())\\n      .map((font) => font)\\n      .filter((font) => font.status === \\\"loaded\\\")\\n      .map((font) => `${font.family} - ${font.weight} - ${font.style}`)\\n  ),\\n];\\n \\nconsole.log(\\\"Fonts and Weights Loaded in the Document\\\");\\nloadedFonts.forEach((font) => console.log(`▸ ${font}`));\\nconsole.log(\\\"\\\");\\n \\nconst childrenSlector =\\n  \\\"body * > *:not(script):not(style):not(link):not(source)\\\";\\nconst aboveFoldElements = Array.from(\\n  document.querySelectorAll(childrenSlector)\\n).filter((elm) => {\\n  const rect = elm.getBoundingClientRect();\\n  return (\\n    rect.top >= 0 &&\\n    rect.left >= 0 &&\\n    rect.bottom <=\\n      (window.innerHeight || document.documentElement.clientHeight) &&\\n    rect.right <= (window.innerWidth || document.documentElement.clientWidth)\\n  );\\n});\\n \\nconst usedFonts = Array.from(\\n  new Set(\\n    [...aboveFoldElements].map(\\n      (e) =>\\n        `${getComputedStyle(e).fontFamily} | ${\\n          getComputedStyle(e).fontWeight\\n        } | ${getComputedStyle(e).fontStyle}`\\n    )\\n  )\\n);\\n \\nconsole.log(\\\"Fonts and Weights Used Above the Fold\\\");\\nusedFonts.forEach((font) => console.log(`▸ ${font}`));\"},{\"name\":\"Loading - Get your <head> in order\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/Loading/Get-Your-Head-in-Order\\n\\n   Get your <head> in order\\n   How you order elements in the <head> can have an effect on the (perceived) performance of the page.\\n   This snippet code it's from capo.js(opens in a new tab) the Rick Viscomi(opens in a new tab) script\\n*/\\nconst linkElements = document.querySelectorAll(`link[rel=\\\"preload\\\"]`);\\nconst arrayLinks = Array.from(linkElements);\\nconst preloadedFonts = arrayLinks.filter((link) => link.as === \\\"font\\\");\\n \\nconsole.log(\\\"Fonts Preloaded via Resources Hints\\\");\\npreloadedFonts.forEach((font) => console.log(`▸ ${font.href}`));\\nconsole.log(\\\"\\\");\\n \\nconst loadedFonts = [\\n  ...new Set(\\n    Array.from(document.fonts.values())\\n      .map((font) => font)\\n      .filter((font) => font.status === \\\"loaded\\\")\\n      .map((font) => `${font.family} - ${font.weight} - ${font.style}`)\\n  ),\\n];\\n \\nconsole.log(\\\"Fonts and Weights Loaded in the Document\\\");\\nloadedFonts.forEach((font) => console.log(`▸ ${font}`));\\nconsole.log(\\\"\\\");\\n \\nconst childrenSlector =\\n  \\\"body * > *:not(script):not(style):not(link):not(source)\\\";\\nconst aboveFoldElements = Array.from(\\n  document.querySelectorAll(childrenSlector)\\n).filter((elm) => {\\n  const rect = elm.getBoundingClientRect();\\n  return (\\n    rect.top >= 0 &&\\n    rect.left >= 0 &&\\n    rect.bottom <=\\n      (window.innerHeight || document.documentElement.clientHeight) &&\\n    rect.right <= (window.innerWidth || document.documentElement.clientWidth)\\n  );\\n});\\n \\nconst usedFonts = Array.from(\\n  new Set(\\n    [...aboveFoldElements].map(\\n      (e) =>\\n        `${getComputedStyle(e).fontFamily} | ${\\n          getComputedStyle(e).fontWeight\\n        } | ${getComputedStyle(e).fontStyle}`\\n    )\\n  )\\n);\\n \\nconsole.log(\\\"Fonts and Weights Used Above the Fold\\\");\\nusedFonts.forEach((font) => console.log(`▸ ${font}`));\"},{\"name\":\"Loading - Inline CSS Info and Size\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/Loading/Inline-CSS-Info-and-Size\\n\\n   \\n   Loading - Inline CSS Info and Size\\n   Find all inline style tags and list them in a table with individual \\n   and total byte size. Customize the table below.\\n*/\\n \\n// Wait for the page to fully load\\n \\nfunction findAllInlineCSS() {\\n  const convertToKb = (bytes) => bytes / 1000;\\n  const inlineCSS = document.querySelectorAll(\\\"style\\\");\\n  let totalByteSize = 0;\\n  for (const css of [...inlineCSS]) {\\n    const html = css.innerHTML;\\n    const size = new Blob([html]).size;\\n    css.byteSizeInKb = convertToKb(size)\\n    totalByteSize += size;\\n  }\\n  // customize table here, can right click on header in console to sort table\\n  console.table(inlineCSS, [\\n    \\\"baseURI\\\",\\n    \\\"parentElement\\\",\\n    \\\"byteSizeInKb\\\",\\n    \\\"innerHTML\\\"\\n  ]);\\n  \\nconsole.log(`Total size: ${convertToKb(totalByteSize)} kB`);\\n}\\n \\nfindAllInlineCSS()\"},{\"name\":\"Loading - Inline Script Info and Size\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/Loading/Inline-Script-Info-and-Size\\n   \\n   Loading - Inline Script Info and Size\\n   Find all inline scripts on the page and list the scripts and count. Find the total byte size of all the inline scripts in the console.\\n*/\\nfunction findInlineScripts() {\\n    const inlineScripts = document.querySelectorAll([\\\"script:not([async]):not([defer]):not([src])\\\"])\\n    console.log(inlineScripts)\\n    console.log(`COUNT: ${inlineScripts.length}`)\\n    let totalByteSize = 0\\n    for (const script of [...inlineScripts]) {\\n      const html = script.innerHTML\\n      const size = new Blob([html]).size\\n      totalByteSize += size\\n    }\\n  console.log((totalByteSize / 1000) + \\\" kb\\\")\\n}\\n \\nfindInlineScripts()\\n \"},{\"name\":\"Loading - Inline Script Info and Size Including __NEXT_DATA__\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/Loading/Inline-Script-Info-and-Size-Including__NEXT_DATA\\n\\n   Loading - Inline Script Info and Size Including __NEXT_DATA__\\n   Find all inline scripts and their total size separately from __NEXT_DATA__ serialized JSON inline Script\\n*/\\n \\nfunction findInlineScriptsWithNextData() {\\n  const inlineScripts = document.querySelectorAll([\\n    \\\"script:not([async]):not([defer]):not([src])\\\"\\n  ]);\\n  console.log(inlineScripts);\\n  console.log(`COUNT: ${inlineScripts.length}`);\\n \\n  const byteSize = {\\n    NEXT_DATA_SIZE: 0,\\n    OTHER_SIZE: 0\\n  };\\n \\n  function getSize(script) {\\n    const html = script.innerHTML;\\n    return new Blob([html]).size;\\n  }\\n \\n  function convertToKb(bytes) {\\n    return bytes / 1000;\\n  }\\n \\n  for (const script of [...inlineScripts]) {\\n    if (script.id == \\\"__NEXT_DATA__\\\") {\\n      byteSize.NEXT_DATA_SIZE += getSize(script);\\n    } else {\\n      byteSize.OTHER_SIZE += getSize(script);\\n    }\\n  }\\n \\n  return {\\n    NEXT_DATA_SIZE: convertToKb(byteSize.NEXT_DATA_SIZE) + \\\" kb\\\",\\n    OTHER_SIZE: convertToKb(byteSize.OTHER_SIZE) + \\\" kb\\\",\\n    totalByteSize:\\n      convertToKb(byteSize.NEXT_DATA_SIZE) +\\n      convertToKb(byteSize.OTHER_SIZE) +\\n      \\\" kb\\\"\\n  };\\n}\\n \\nconsole.log(findInlineScriptsWithNextData());\"},{\"name\":\"Loading - Resources hints\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/Loading/Resource-Hits\\n\\n   Loading - Resources hints\\n   Check if the page has resources hints\\n*/\\nconst rels = [\\n  \\\"preload\\\",\\n  \\\"prefetch\\\",\\n  \\\"preconnect\\\",\\n  \\\"dns-prefetch\\\",\\n  \\\"preconnect dns-prefetch\\\",\\n  \\\"prerender\\\",\\n  \\\"modulepreload\\\",\\n];\\n \\nrels.forEach((element) => {\\n  const linkElements = document.querySelectorAll(`link[rel=\\\"${element}\\\"]`);\\n  const dot = linkElements.length > 0 ? \\\"🟩\\\" : \\\"🟥\\\";\\n  console.log(`${dot} ${element}`);\\n  linkElements.forEach((el) => console.log(el));\\n});\"},{\"name\":\"Loading - Scripts Loading\",\"content\":\"/*\\n   MIT License. Copyright (c) 2022 Joan León\\n   https://webperf-snippets.nucliweb.net/Loading/Script-Loading\\n\\n   Loading - Scripts Loading\\n   List all the <scripts> in the DOM and show a table to see if are loaded async and/or defer\\n*/\\nconst scripts = document.querySelectorAll(\\\"script[src]\\\");\\n \\nconst scriptsLoading = [...scripts].map((obj) => {\\n  let newObj = {};\\n  newObj = {\\n    src: obj.src,\\n    async: obj.async,\\n    defer: obj.defer,\\n    \\\"render blocking\\\": obj.async || obj.defer ? \\\"\\\" : \\\"🟥\\\",\\n  };\\n  return newObj;\\n});\\n \\nconsole.table(scriptsLoading);\"}]")
hhkaos commented 1 year ago

FYI: I have to review it

Screenshot 2023-06-08 at 15 59 54
jhadev commented 1 year ago

@nucliweb I have been looking into this, I'm not sure if you are working on it yet, but I should have something this week.

Was trying to batch import the entire repo first. @hhkaos is that were you were thinking? Basically what you did in one step.

hhkaos commented 1 year ago

I didn't do it automatically since I tried to add the copyright & added the links to the website, but I think it should be done that way (for reasons of maintainability). I'm more than happy if you want to take over it 😄 .

jhadev commented 1 year ago

@hhkaos Makes sense, what you did was super helpful. I have to dig up the link I found where there was some discrepancy as to where the snippets are stored. It has changed and Paul Irish commented on an SO thread awhile back mentioning this.

I was thinking of just making 2 scripts that can simply be run using Node. One for import and the other for export. If you and @nucliweb have a better idea, let me know. Thanks.

hhkaos commented 1 year ago

That's sound like a good idea to me

nucliweb commented 1 year ago

Thank you @jhadev for all your contributions, and @hhkaos for your proposal, @jhadev feel free to work on it.

jhadev commented 1 year ago

Of course, this repo is awesome. I remember when you just created it. Hope it keeps growing. I've had the other open issue for awhile which I think would be very unique addition. Using iOS/Mac shortcuts to be able run perf scripts on your own device in Safari is something I think people would find very helpful.

nucliweb commented 1 year ago

Of course, this repo is awesome. I remember when you just created it. Hope it keeps growing. I've had the other open issue for awhile which I think would be very unique addition. Using iOS/Mac shortcuts to be able run perf scripts on your own device in Safari is something I think people would find very helpful.

Yes, I think it is a topic we could study, it would be very useful.

jhadev commented 1 year ago

Update...

This is a lot harder than I expected. I cannot find a location for these snippets on Mac/Windows. I found something promising for Linux but I never tested it.

Looking for a cross platform solution I started diving into puppeteer. In order to interact with the console I found the need to undock a devtools pane and then create a new pane off that one. That works but since puppeteer can't touch devtools itself I'm a little lost. I'm also using puppeteer-extra for this so there's definitely more options I haven't explored. All that needs to be done is inspect straight the the console and I found something here

https://gist.github.com/frank-dspeed/9f4889beecd107d2fa50066b96af2937

This did not work.

But this is promising,

https://github.com/puppeteer/puppeteer/issues/1352

That lead me here

https://stackoverflow.com/questions/58473161/define-default-width-of-chrome-devtools-window

I also found the location and haven't tried that yet

https://stackoverflow.com/questions/67465469/where-are-chrome-devtools-snippets-stored-in-chrome-90

Now how to import/export?

https://github.com/bahmutov/code-snippets/issues/23

This from Paul Irish seemed to work for me from a devtools instance of devtools,but I was having trouble focusing the correct window and opening straight to the console.

Another thing to note is to make sure snippets that are already created are preserved.