post-process.js.map 14.1 KB
Newer Older
1
{"version":3,"sources":["../../../next-server/lib/post-process.ts"],"names":["MAXIMUM_IMAGE_PRELOADS","IMAGE_PRELOAD_SIZE_THRESHOLD","middlewareRegistry","registerPostProcessor","name","middleware","condition","push","processHTML","html","data","options","root","document","callMiddleWare","inspectData","inspect","mutate","i","length","FontOptimizerMiddleware","markup","fontDefinitions","result","getFontDefinition","forEach","fontDef","url","nonce","fallBackLinkTag","indexOf","fontContent","replace","nonceStr","originalDom","querySelectorAll","filter","tag","getAttribute","hasAttribute","OPTIMIZED_FONT_PROVIDERS","some","dataHref","startsWith","element","ImageOptimizerMiddleware","imgPreloads","imagePreloadTags","imgHref","preloadTagAlreadyExists","reduce","acc","imgElements","eligibleImages","isImgEligible","imgEl","src","imgElement","imgSrc","sourceIsSupportedType","imageIsNotTooSmall","imageIsNotHidden","href","escapedHref","regex","RegExp","match","heightAttr","widthAttr","parseInt","err","activeElement","parentNode","includes","optimizeFonts","process","env","__NEXT_OPTIMIZE_FONTS","optimizeImages","__NEXT_OPTIMIZE_IMAGES"],"mappings":"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","sourcesContent":["import escapeRegexp from 'next/dist/compiled/escape-string-regexp'\nimport { parse, HTMLElement } from 'node-html-parser'\nimport { OPTIMIZED_FONT_PROVIDERS } from './constants'\n\n// const MIDDLEWARE_TIME_BUDGET = parseInt(process.env.__POST_PROCESS_MIDDLEWARE_TIME_BUDGET || '', 10) || 10\nconst MAXIMUM_IMAGE_PRELOADS = 2\nconst IMAGE_PRELOAD_SIZE_THRESHOLD = 2500\n\ntype postProcessOptions = {\n  optimizeFonts: boolean\n  optimizeImages: boolean\n}\n\ntype renderOptions = {\n  getFontDefinition?: (url: string) => string\n}\ninterface PostProcessMiddleware {\n  inspect: (originalDom: HTMLElement, options: renderOptions) => any\n  mutate: (markup: string, data: any, options: renderOptions) => Promise<string>\n}\n\ntype middlewareSignature = {\n  name: string\n  middleware: PostProcessMiddleware\n  condition: ((options: postProcessOptions) => boolean) | null\n}\n\nconst middlewareRegistry: Array<middlewareSignature> = []\n\nfunction registerPostProcessor(\n  name: string,\n  middleware: PostProcessMiddleware,\n  condition?: (options: postProcessOptions) => boolean\n) {\n  middlewareRegistry.push({ name, middleware, condition: condition || null })\n}\n\nasync function processHTML(\n  html: string,\n  data: renderOptions,\n  options: postProcessOptions\n): Promise<string> {\n  // Don't parse unless there's at least one processor middleware\n  if (!middlewareRegistry[0]) {\n    return html\n  }\n  const root: HTMLElement = parse(html)\n  let document = html\n  // Calls the middleware, with some instrumentation and logging\n  async function callMiddleWare(middleware: PostProcessMiddleware) {\n    // let timer = Date.now()\n    const inspectData = middleware.inspect(root, data)\n    document = await middleware.mutate(document, inspectData, data)\n    // timer = Date.now() - timer\n    // if (timer > MIDDLEWARE_TIME_BUDGET) {\n    // TODO: Identify a correct upper limit for the postprocess step\n    // and add a warning to disable the optimization\n    // }\n    return\n  }\n\n  for (let i = 0; i < middlewareRegistry.length; i++) {\n    let middleware = middlewareRegistry[i]\n    if (!middleware.condition || middleware.condition(options)) {\n      await callMiddleWare(middlewareRegistry[i].middleware)\n    }\n  }\n\n  return document\n}\n\nclass FontOptimizerMiddleware implements PostProcessMiddleware {\n  inspect(originalDom: HTMLElement, options: renderOptions) {\n    if (!options.getFontDefinition) {\n      return\n    }\n    const fontDefinitions: (string | undefined)[][] = []\n    // collecting all the requested font definitions\n    originalDom\n      .querySelectorAll('link')\n      .filter(\n        (tag: HTMLElement) =>\n          tag.getAttribute('rel') === 'stylesheet' &&\n          tag.hasAttribute('data-href') &&\n          OPTIMIZED_FONT_PROVIDERS.some((url) => {\n            const dataHref = tag.getAttribute('data-href')\n            return dataHref ? dataHref.startsWith(url) : false\n          })\n      )\n      .forEach((element: HTMLElement) => {\n        const url = element.getAttribute('data-href')\n        const nonce = element.getAttribute('nonce')\n\n        if (url) {\n          fontDefinitions.push([url, nonce])\n        }\n      })\n\n    return fontDefinitions\n  }\n  mutate = async (\n    markup: string,\n    fontDefinitions: string[][],\n    options: renderOptions\n  ) => {\n    let result = markup\n    if (!options.getFontDefinition) {\n      return markup\n    }\n\n    fontDefinitions.forEach((fontDef) => {\n      const [url, nonce] = fontDef\n      const fallBackLinkTag = `<link rel=\"stylesheet\" href=\"${url}\"/>`\n      if (\n        result.indexOf(`<style data-href=\"${url}\">`) > -1 ||\n        result.indexOf(fallBackLinkTag) > -1\n      ) {\n        // The font is already optimized and probably the response is cached\n        return\n      }\n      const fontContent = options.getFontDefinition\n        ? options.getFontDefinition(url as string)\n        : null\n      if (!fontContent) {\n        /**\n         * In case of unreachable font definitions, fallback to default link tag.\n         */\n        result = result.replace('</head>', `${fallBackLinkTag}</head>`)\n      } else {\n        const nonceStr = nonce ? ` nonce=\"${nonce}\"` : ''\n        result = result.replace(\n          '</head>',\n          `<style data-href=\"${url}\"${nonceStr}>${fontContent}</style></head>`\n        )\n      }\n    })\n\n    return result\n  }\n}\n\nclass ImageOptimizerMiddleware implements PostProcessMiddleware {\n  inspect(originalDom: HTMLElement) {\n    const imgPreloads = []\n    const imgElements = originalDom.querySelectorAll('img')\n    let eligibleImages: Array<HTMLElement> = []\n    for (let i = 0; i < imgElements.length; i++) {\n      if (isImgEligible(imgElements[i])) {\n        eligibleImages.push(imgElements[i])\n      }\n      if (eligibleImages.length >= MAXIMUM_IMAGE_PRELOADS) {\n        break\n      }\n    }\n\n    for (const imgEl of eligibleImages) {\n      const src = imgEl.getAttribute('src')\n      if (src) {\n        imgPreloads.push(src)\n      }\n    }\n\n    return imgPreloads\n  }\n  mutate = async (markup: string, imgPreloads: string[]) => {\n    let result = markup\n    let imagePreloadTags = imgPreloads\n      .filter((imgHref) => !preloadTagAlreadyExists(markup, imgHref))\n      .reduce(\n        (acc, imgHref) =>\n          acc + `<link rel=\"preload\" href=\"${imgHref}\" as=\"image\"/>`,\n        ''\n      )\n    return result.replace(\n      /<link rel=\"preload\"/,\n      `${imagePreloadTags}<link rel=\"preload\"`\n    )\n  }\n}\n\nfunction isImgEligible(imgElement: HTMLElement): boolean {\n  let imgSrc = imgElement.getAttribute('src')\n  return (\n    !!imgSrc &&\n    sourceIsSupportedType(imgSrc) &&\n    imageIsNotTooSmall(imgElement) &&\n    imageIsNotHidden(imgElement)\n  )\n}\n\nfunction preloadTagAlreadyExists(html: string, href: string) {\n  const escapedHref = escapeRegexp(href)\n  const regex = new RegExp(`<link[^>]*href[^>]*${escapedHref}`)\n  return html.match(regex)\n}\n\nfunction imageIsNotTooSmall(imgElement: HTMLElement): boolean {\n  // Skip images without both height and width--we don't know enough to say if\n  // they are too small\n  if (\n    !(imgElement.hasAttribute('height') && imgElement.hasAttribute('width'))\n  ) {\n    return true\n  }\n  try {\n    const heightAttr = imgElement.getAttribute('height')\n    const widthAttr = imgElement.getAttribute('width')\n    if (!heightAttr || !widthAttr) {\n      return true\n    }\n\n    if (\n      parseInt(heightAttr) * parseInt(widthAttr) <=\n      IMAGE_PRELOAD_SIZE_THRESHOLD\n    ) {\n      return false\n    }\n  } catch (err) {\n    return true\n  }\n  return true\n}\n\n// Traverse up the dom from each image to see if it or any of it's\n// ancestors have the hidden attribute.\nfunction imageIsNotHidden(imgElement: HTMLElement): boolean {\n  let activeElement = imgElement\n  while (activeElement.parentNode) {\n    if (activeElement.hasAttribute('hidden')) {\n      return false\n    }\n    activeElement = activeElement.parentNode as HTMLElement\n  }\n  return true\n}\n\n// Currently only filters out svg images--could be made more specific in the future.\nfunction sourceIsSupportedType(imgSrc: string): boolean {\n  return !imgSrc.includes('.svg')\n}\n\n// Initialization\nregisterPostProcessor(\n  'Inline-Fonts',\n  new FontOptimizerMiddleware(),\n  // Using process.env because passing Experimental flag through loader is not possible.\n  // @ts-ignore\n  (options) => options.optimizeFonts || process.env.__NEXT_OPTIMIZE_FONTS\n)\n\nregisterPostProcessor(\n  'Preload Images',\n  new ImageOptimizerMiddleware(),\n  // @ts-ignore\n  (options) => options.optimizeImages || process.env.__NEXT_OPTIMIZE_IMAGES\n)\n\nexport default processHTML\n"]}