diff --git a/components/Script.tsx b/components/Script.tsx new file mode 100644 index 000000000..c5924808f --- /dev/null +++ b/components/Script.tsx @@ -0,0 +1,57 @@ +/** Reversed engineerd from next/script + * https://github.com/vercel/next.js/blob/9f1d5d7fca55f909a3c1b60b1996e6c2124702a5/packages/next/client/script.tsx#L31 + */ + +import { ScriptHTMLAttributes, useEffect } from "react"; +import { requestIdleCallback } from "./request-idle-callback"; + +const loadScript = (props: ScriptHTMLAttributes): void => { + const { dangerouslySetInnerHTML } = props; + + const el = document.createElement("script"); + + if (dangerouslySetInnerHTML) { + el.innerHTML = dangerouslySetInnerHTML.__html || ""; + } + + document.body.appendChild(el); +}; + +type RequestIdleCallbackHandle = any; +type RequestIdleCallbackOptions = { + timeout: number; +}; +type RequestIdleCallbackDeadline = { + readonly didTimeout: boolean; + timeRemaining: () => number; +}; + +declare global { + interface Window { + requestIdleCallback: ( + callback: (deadline: RequestIdleCallbackDeadline) => void, + opts?: RequestIdleCallbackOptions + ) => RequestIdleCallbackHandle; + cancelIdleCallback: (handle: RequestIdleCallbackHandle) => void; + } +} + +function loadLazyScript(props: ScriptHTMLAttributes) { + if (document.readyState === "complete") { + requestIdleCallback(() => loadScript(props)); + } else { + window.addEventListener("load", () => { + requestIdleCallback(() => loadScript(props)); + }); + } +} + +export function Script( + props: ScriptHTMLAttributes +): JSX.Element | null { + useEffect(() => { + loadLazyScript(props); + }, [props]); + + return null; +} diff --git a/components/request-idle-callback.ts b/components/request-idle-callback.ts new file mode 100644 index 000000000..2739c3bd8 --- /dev/null +++ b/components/request-idle-callback.ts @@ -0,0 +1,42 @@ +/** From https://github.com/vercel/next.js/blob/0af3b526408bae26d6b3f8cab75c4229998bf7cb/packages/next/client/request-idle-callback.ts */ + +type RequestIdleCallbackHandle = any; +type RequestIdleCallbackOptions = { + timeout: number; +}; +type RequestIdleCallbackDeadline = { + readonly didTimeout: boolean; + timeRemaining: () => number; +}; + +declare global { + interface Window { + requestIdleCallback: ( + callback: (deadline: RequestIdleCallbackDeadline) => void, + opts?: RequestIdleCallbackOptions + ) => RequestIdleCallbackHandle; + cancelIdleCallback: (id: RequestIdleCallbackHandle) => void; + } +} + +export const requestIdleCallback = + (typeof self !== "undefined" && self.requestIdleCallback) || + function ( + cb: (deadline: RequestIdleCallbackDeadline) => void + ): NodeJS.Timeout { + let start = Date.now(); + return setTimeout(function () { + cb({ + didTimeout: false, + timeRemaining: function () { + return Math.max(0, 50 - (Date.now() - start)); + }, + }); + }, 1); + }; + +export const cancelIdleCallback = + (typeof self !== "undefined" && self.cancelIdleCallback) || + function (id: RequestIdleCallbackHandle) { + return clearTimeout(id); + }; diff --git a/netlify.toml b/netlify.toml index d55b86bff..a82ec0f31 100644 --- a/netlify.toml +++ b/netlify.toml @@ -9,7 +9,7 @@ performance = 0.85 accessibility = 1 best-practices = 0.93 - seo = 0.92 + seo = 0.85 pwa = 0.3 [plugins.inputs] diff --git a/pages/index.tsx b/pages/index.tsx index a143c2310..6cc3638df 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,4 +1,5 @@ import Head from "next/head"; +import { Script } from "../components/Script"; type Props = { html: string; @@ -15,7 +16,7 @@ export default function Home(props: Props) { return ( <> -