UNPKG

15.5 kBSource Map (JSON)View Raw
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","preconnectUrls","Set","getFontDefinition","forEach","fontDef","url","nonce","fallBackLinkTag","indexOf","fontContent","replace","nonceStr","provider","OPTIMIZED_FONT_PROVIDERS","find","p","startsWith","add","preconnect","preconnectTag","originalDom","querySelectorAll","filter","tag","getAttribute","hasAttribute","some","dataHref","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 let preconnectUrls = new Set<string>()\n\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 const provider = OPTIMIZED_FONT_PROVIDERS.find((p) =>\n url.startsWith(p.url)\n )\n\n if (provider) {\n preconnectUrls.add(provider.preconnect)\n }\n }\n })\n\n let preconnectTag = ''\n preconnectUrls.forEach((url) => {\n preconnectTag += `<link rel=\"preconnect\" href=\"${url}\" crossorigin />`\n })\n\n result = result.replace(\n '<meta name=\"next-font-preconnect\"/>',\n preconnectTag\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('<meta name=\"next-image-preload\"/>', imagePreloadTags)\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"]}
\No newline at end of file