UNPKG

one

Version:

One is a new React Framework that makes Vite serve both native and web.

152 lines (151 loc) 5.29 kB
import { useCallback, useSyncExternalStore } from "react"; import { useParams, usePathname } from "./hooks.mjs"; import { preloadedLoaderData, preloadingLoader } from "./router/router.mjs"; import { getLoaderPath } from "./utils/cleanUrl.mjs"; import { dynamicImport } from "./utils/dynamicImport.mjs"; import { weakKey } from "./utils/weakKey.mjs"; import { useServerContext } from "./vite/one-server-only.mjs"; const loaderState = {}, subscribers = /* @__PURE__ */new Set(); function updateState(path, updates) { loaderState[path] = { ...loaderState[path], ...updates }, subscribers.forEach(callback => { callback(); }); } function subscribe(callback) { return subscribers.add(callback), () => subscribers.delete(callback); } function getLoaderState(path, preloadedData2) { return loaderState[path] || (loaderState[path] = { data: preloadedData2, error: void 0, promise: void 0, state: "idle", hasLoadedOnce: !!preloadedData2 }), loaderState[path]; } async function refetchLoader(pathname2) { updateState(pathname2, { state: "loading", error: null }); try { const cacheBust = `${Date.now()}`, loaderJSUrl2 = getLoaderPath(pathname2, !0, cacheBust), result2 = await (await dynamicImport(loaderJSUrl2)).loader(); updateState(pathname2, { data: result2, state: "idle", timestamp: Date.now(), hasLoadedOnce: !0 }); } catch (err) { throw updateState(pathname2, { error: err, state: "idle" }), err; } } function useLoaderState(loader) { const { loaderProps: loaderPropsFromServerContext, loaderData: loaderDataFromServerContext } = useServerContext() || {}, params = useParams(), pathname = usePathname(), currentPath = pathname.replace(/\/index$/, "").replace(/\/$/, "") || "/"; if (typeof window > "u" && loader) return { data: useAsyncFn(loader, loaderPropsFromServerContext || { path: pathname, params }), refetch: async () => {}, state: "idle" }; const serverContextPath = loaderPropsFromServerContext?.path, preloadedData = serverContextPath === currentPath ? loaderDataFromServerContext : void 0, loaderStateEntry = useSyncExternalStore(subscribe, () => getLoaderState(currentPath, preloadedData), () => getLoaderState(currentPath, preloadedData)), refetch = useCallback(() => refetchLoader(currentPath), [currentPath]); if (!loader) return { refetch, state: loaderStateEntry.state }; if (!loaderStateEntry.data && !loaderStateEntry.promise && !loaderStateEntry.hasLoadedOnce && loader) { const resolvedPreloadData = preloadedLoaderData[currentPath]; if (resolvedPreloadData !== void 0) delete preloadedLoaderData[currentPath], delete preloadingLoader[currentPath], loaderStateEntry.data = resolvedPreloadData, loaderStateEntry.hasLoadedOnce = !0;else if (preloadingLoader[currentPath]) { const promise2 = preloadingLoader[currentPath].then(val => { delete preloadingLoader[currentPath], delete preloadedLoaderData[currentPath], updateState(currentPath, { data: val, hasLoadedOnce: !0, promise: void 0 }); }).catch(err => { console.error("Error running loader()", err), delete preloadingLoader[currentPath], updateState(currentPath, { error: err, promise: void 0 }); }); loaderStateEntry.promise = promise2; } else { const loadData = async () => { try { const loaderJSUrl = getLoaderPath(currentPath, !0), module = await dynamicImport(loaderJSUrl), result = await module.loader(); updateState(currentPath, { data: result, hasLoadedOnce: !0, promise: void 0 }); } catch (err) { updateState(currentPath, { error: err, promise: void 0 }); } }, promise = loadData(); loaderStateEntry.promise = promise; } } if (loader) { if (loaderStateEntry.error && !loaderStateEntry.hasLoadedOnce) throw loaderStateEntry.error; if (loaderStateEntry.data === void 0 && loaderStateEntry.promise && !loaderStateEntry.hasLoadedOnce) throw loaderStateEntry.promise; return { data: loaderStateEntry.data, refetch, state: loaderStateEntry.state }; } else return { refetch, state: loaderStateEntry.state }; } function useLoader(loader2) { const { data: data2 } = useLoaderState(loader2); return data2; } const results = /* @__PURE__ */new Map(), started = /* @__PURE__ */new Map(); function useAsyncFn(val, props) { const key = (val ? weakKey(val) : "") + JSON.stringify(props); if (val && !started.get(key)) { started.set(key, !0); let next = val(props); next instanceof Promise && (next = next.then(final => { results.set(key, final); }).catch(err => { console.error("Error running loader()", err), results.set(key, void 0); })), results.set(key, next); } const current = results.get(key); if (current instanceof Promise) throw current; return current; } export { refetchLoader, useLoader, useLoaderState }; //# sourceMappingURL=useLoader.mjs.map