UNPKG

remix-utils-rt

Version:

This package contains simple utility functions to use with [React Router](https://reactrouter.com/home).

108 lines 4.14 kB
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; import * as React from "react"; import { useLocation, useMatches } from "react-router"; import { useHydrated } from "./use-hydrated.js"; /** * Load scripts defined by each route in a single place, often in `root`. * @example * // Defines a `scripts` function in a route `handle` * export const handle: ExternalScriptsHandle<SerializeFrom<typeof loader>> = { * scripts(loaderData) { ... } * } * // Or define a scripts array directly * export const handle: ExternalScriptsHandle = { * scripts: [...] * } * // Then render ExternalScripts in your root * return <ExternalScripts /> */ export function ExternalScripts() { let scripts = useExternalScripts(); return (_jsx(_Fragment, { children: scripts.map((props) => { return _jsx(ExternalScript, { ...props }, props.src); }) })); } export function useExternalScripts() { let location = useLocation(); let matches = useMatches(); return React.useMemo(() => { let scripts = matches.flatMap((match, index, matches) => { if (!match.handle) return []; // ignore no-handle routes if (match.handle === null) return []; // ignore null handles if (typeof match.handle !== "object") return []; // and non error handles if (!("scripts" in match.handle)) return []; // and without scripts let scripts = match.handle.scripts; // if scripts is an array, suppose it's an array of script descriptors // and return it if (Array.isArray(scripts)) return scripts; // if it's not a function (and not an array), ignore it if (typeof scripts !== "function") return []; let result = scripts({ id: match.id, data: match.data, params: match.params, location, parentsData: matches.slice(0, index).map((match) => match.data), matches, }); if (Array.isArray(result)) return result; return []; }); let uniqueScripts = new Map(); for (let script of scripts) uniqueScripts.set(script.src, script); return [...uniqueScripts.values()]; }, [matches, location]); } export function ExternalScript({ src, preload = false, async = true, defer = true, crossOrigin, integrity, type, referrerPolicy, noModule, nonce, id, }) { let isHydrated = useHydrated(); let startsHydrated = React.useRef(isHydrated); React.useEffect(() => { if (!startsHydrated.current && isHydrated) return; let $script = document.createElement("script"); $script.src = src; let attributes = { async, defer, crossOrigin, integrity, type, referrerPolicy, noModule, nonce, id, }; for (let [key, value] of Object.entries(attributes)) { if (value) $script.setAttribute(key, value.toString()); } document.body.appendChild($script); return () => $script.remove(); }, [ async, crossOrigin, defer, integrity, isHydrated, noModule, nonce, referrerPolicy, src, type, id, ]); if (startsHydrated.current && isHydrated) return null; let rel = noModule ? "modulepreload" : "preload"; let as = noModule ? undefined : "script"; return (_jsxs(_Fragment, { children: [preload && (_jsx("link", { rel: rel, href: src, as: as, crossOrigin: crossOrigin, integrity: integrity, referrerPolicy: referrerPolicy })), _jsx("script", { id: id, src: src, defer: defer, async: async, type: type, noModule: noModule, nonce: nonce, crossOrigin: crossOrigin, integrity: integrity, referrerPolicy: referrerPolicy })] })); } //# sourceMappingURL=external-scripts.js.map