remix-utils-rt
Version:
This package contains simple utility functions to use with [React Router](https://reactrouter.com/home).
108 lines • 4.14 kB
JavaScript
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