UNPKG

one

Version:

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

513 lines (512 loc) 20.4 kB
import { useCallback, useSyncExternalStore } from "react"; import { registerDevtoolsFunction } from "./devtools/registry.native.js"; import { useParams, usePathname } from "./hooks.native.js"; import { findNearestNotFoundRoute, setNotFoundState } from "./notFoundState.native.js"; import { useContextKey } from "./router/Route.native.js"; import { getContextKey } from "./router/matchers.native.js"; import { router } from "./router/imperative-api.native.js"; import { preloadedLoaderData, preloadingLoader, routeNode } from "./router/router.native.js"; import { ssrLoaderData } from "./server/ssrLoaderData.native.js"; import { subscribeToClientMatches, getClientMatchesSnapshot, updateMatchLoaderData } from "./useMatches.native.js"; import { getLoaderPath } from "./utils/cleanUrl.native.js"; import { LOADER_JS_POSTFIX_UNCACHED } from "./constants.native.js"; import { dynamicImport } from "./utils/dynamicImport.native.js"; import { weakKey } from "./utils/weakKey.native.js"; import { useServerContext } from "./vite/one-server-only.native.js"; import { setSSRLoaderData } from "./server/ssrLoaderData.native.js"; function _instanceof(left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) return !!right[Symbol.hasInstance](left);else return left instanceof right; } var LOADER_TIMEOUT = process.env.ONE_LOADER_TIMEOUT_MS ? +process.env.ONE_LOADER_TIMEOUT_MS : 6e4; var loaderTimingHistory = []; var MAX_TIMING_HISTORY = 50; var recordLoaderTiming = process.env.NODE_ENV === "development" ? function (entry) { loaderTimingHistory.unshift(entry); if (loaderTimingHistory.length > MAX_TIMING_HISTORY) loaderTimingHistory.pop(); if (typeof window !== "undefined" && typeof CustomEvent !== "undefined") { window.dispatchEvent(new CustomEvent("one-loader-timing", { detail: entry })); if (entry.error) window.dispatchEvent(new CustomEvent("one-error", { detail: { error: { message: entry.error, name: "LoaderError" }, route: { pathname: entry.path }, timestamp: Date.now(), type: "loader" } })); } } : void 0; function getLoaderTimingHistory() { return loaderTimingHistory; } registerDevtoolsFunction("getLoaderTimingHistory", getLoaderTimingHistory); registerDevtoolsFunction("recordLoaderTiming", recordLoaderTiming); var loaderState = {}; var subscribers = /* @__PURE__ */new Set(); function updateState(path, updates) { loaderState[path] = { ...loaderState[path], ...updates }; subscribers.forEach(function (callback) { callback(); }); } function subscribe(callback) { subscribers.add(callback); return function () { return subscribers.delete(callback); }; } function getLoaderState(path, preloadedData2) { if (!loaderState[path]) loaderState[path] = { data: preloadedData2, error: void 0, promise: void 0, state: "idle", hasLoadedOnce: !!preloadedData2 }; return loaderState[path]; } async function refetchLoader(pathname2) { var startTime2 = performance.now(); updateState(pathname2, { state: "loading", error: null }); try { var _dynamicImport2; var loaderJSUrl2 = getLoaderPath(pathname2, true, `${Date.now()}`); var moduleLoadStart2 = performance.now(); var module2 = await ((_dynamicImport2 = dynamicImport(loaderJSUrl2)) === null || _dynamicImport2 === void 0 ? void 0 : _dynamicImport2.catch(function () { return null; })); var moduleLoadTime2 = performance.now() - moduleLoadStart2; if (!(module2 === null || module2 === void 0 ? void 0 : module2.loader)) { updateState(pathname2, { data: void 0, state: "idle", hasLoadedOnce: true }); return; } var executionStart2 = performance.now(); var result2 = await module2.loader(); var executionTime2 = performance.now() - executionStart2; var totalTime2 = performance.now() - startTime2; if (result2 === null || result2 === void 0 ? void 0 : result2.__oneRedirect) { recordLoaderTiming === null || recordLoaderTiming === void 0 || recordLoaderTiming({ path: pathname2, startTime: startTime2, moduleLoadTime: moduleLoadTime2, executionTime: executionTime2, totalTime: totalTime2, source: "refetch" }); updateState(pathname2, { data: void 0, state: "idle", hasLoadedOnce: true }); router.replace(result2.__oneRedirect); return; } if ((result2 === null || result2 === void 0 ? void 0 : result2.__oneError) === 404) { recordLoaderTiming === null || recordLoaderTiming === void 0 || recordLoaderTiming({ path: pathname2, startTime: startTime2, moduleLoadTime: moduleLoadTime2, executionTime: executionTime2, totalTime: totalTime2, source: "refetch" }); var notFoundRoute2 = findNearestNotFoundRoute(pathname2, routeNode); setNotFoundState({ notFoundPath: result2.__oneNotFoundPath || "/+not-found", notFoundRouteNode: notFoundRoute2 || void 0, originalPath: pathname2 }); return; } updateState(pathname2, { data: result2, state: "idle", timestamp: Date.now(), hasLoadedOnce: true }); var currentMatches = getClientMatchesSnapshot(); var pageMatch = currentMatches[currentMatches.length - 1]; var normalizedPathname = pathname2.replace(/\/$/, "") || "/"; var normalizedMatchPathname = ((pageMatch === null || pageMatch === void 0 ? void 0 : pageMatch.pathname) || "").replace(/\/$/, "") || "/"; if (pageMatch && normalizedMatchPathname === normalizedPathname) updateMatchLoaderData(pageMatch.routeId, result2); recordLoaderTiming === null || recordLoaderTiming === void 0 || recordLoaderTiming({ path: pathname2, startTime: startTime2, moduleLoadTime: moduleLoadTime2, executionTime: executionTime2, totalTime: totalTime2, source: "refetch" }); } catch (err) { var totalTime1 = performance.now() - startTime2; updateState(pathname2, { error: err, state: "idle" }); recordLoaderTiming === null || recordLoaderTiming === void 0 || recordLoaderTiming({ path: pathname2, startTime: startTime2, totalTime: totalTime1, error: _instanceof(err, Error) ? err.message : String(err), source: "refetch" }); throw err; } } if (process.env.NODE_ENV === "development" && typeof window !== "undefined") window.__oneRefetchLoader = refetchLoader; async function refetchMatchLoader(routeId, currentPath2) { var _dynamicImport2; var module2 = await ((_dynamicImport2 = dynamicImport(getLoaderPath(currentPath2, true, `${Date.now()}`))) === null || _dynamicImport2 === void 0 ? void 0 : _dynamicImport2.catch(function () { return null; })); if (!(module2 === null || module2 === void 0 ? void 0 : module2.loader)) return; var result2 = await module2.loader(); if ((result2 === null || result2 === void 0 ? void 0 : result2.__oneRedirect) || (result2 === null || result2 === void 0 ? void 0 : result2.__oneError)) return; updateMatchLoaderData(routeId, result2); } function useLoaderState(loader) { var { loaderProps: loaderPropsFromServerContext, loaderData: loaderDataFromServerContext } = useServerContext() || {}; var params = useParams(); var pathname = usePathname(); var currentPath = pathname.replace(/\/index$/, "").replace(/\/$/, "") || "/"; if (typeof window === "undefined") { if (loader) { if (ssrLoaderData.has(loader)) return { data: ssrLoaderData.get(loader), refetch: async function () {}, state: "idle" }; var serverContext = useServerContext(); if (serverContext === null || serverContext === void 0 ? void 0 : serverContext.matches) { var contextKey = useContextKey(); var match = serverContext.matches.find(function (m) { return getContextKey(m.routeId) === contextKey; }); if (match && match.loaderData !== void 0) return { data: match.loaderData, refetch: async function () {}, state: "idle" }; } return { data: useAsyncFn(loader, loaderPropsFromServerContext || { path: pathname, params }), refetch: async function () {}, state: "idle" }; } if (loaderDataFromServerContext !== void 0) return { data: loaderDataFromServerContext, refetch: async function () {}, state: "idle" }; } var matchRouteId = loader ? function () { var result2 = loader(); return typeof result2 === "string" && result2.startsWith("./") ? result2 : null; }() : null; var clientMatches = useSyncExternalStore(subscribeToClientMatches, getClientMatchesSnapshot, getClientMatchesSnapshot); var serverContextPath = loaderPropsFromServerContext === null || loaderPropsFromServerContext === void 0 ? void 0 : loaderPropsFromServerContext.path; var preloadedData = serverContextPath === currentPath ? loaderDataFromServerContext : void 0; var loaderStateEntry = useSyncExternalStore(subscribe, function () { return getLoaderState(currentPath, preloadedData); }, function () { return getLoaderState(currentPath, preloadedData); }); var refetch = useCallback(function () { return refetchLoader(currentPath); }, [currentPath]); if (matchRouteId) { var _clientMatches_; var match = clientMatches.find(function (m) { return m.routeId === matchRouteId; }); var isPageMatch = clientMatches.length > 0 && ((_clientMatches_ = clientMatches[clientMatches.length - 1]) === null || _clientMatches_ === void 0 ? void 0 : _clientMatches_.routeId) === matchRouteId; var matchPathNormalized = ((match === null || match === void 0 ? void 0 : match.pathname) || "").replace(/\/$/, "") || "/"; var matchPathFresh = !isPageMatch || matchPathNormalized === currentPath; if (match && match.loaderData != null && matchPathFresh) return { data: match.loaderData, refetch: async function () { await refetchLoader(currentPath); var fresh = loaderState[currentPath]; if ((fresh === null || fresh === void 0 ? void 0 : fresh.data) != null) updateMatchLoaderData(matchRouteId, fresh.data); }, state: loaderStateEntry.state }; } if (!loader) return { refetch, state: loaderStateEntry.state }; if (!loaderStateEntry.data && !loaderStateEntry.promise && !loaderStateEntry.hasLoadedOnce) { var resolvedPreloadData = preloadedLoaderData[currentPath]; if (resolvedPreloadData != null) { delete preloadedLoaderData[currentPath]; delete preloadingLoader[currentPath]; loaderStateEntry.data = resolvedPreloadData; loaderStateEntry.hasLoadedOnce = true; } else if (preloadingLoader[currentPath]) { var promise = preloadingLoader[currentPath].then(function (val) { delete preloadingLoader[currentPath]; delete preloadedLoaderData[currentPath]; if (val != null) updateState(currentPath, { data: val, hasLoadedOnce: true, promise: void 0 });else updateState(currentPath, { promise: void 0 }); }).catch(function (err) { console.error(`Error running loader()`, err); delete preloadingLoader[currentPath]; updateState(currentPath, { error: err, promise: void 0 }); }); loaderStateEntry.promise = promise; } else { var loadData = async function () { var startTime = performance.now(); try { var _dynamicImport; var nativeLoaderJSUrl; if (process.env.NODE_ENV === "development") nativeLoaderJSUrl = `${getLoaderPath(currentPath, true)}?platform=native`;else { var { getURL } = require("./getURL"); nativeLoaderJSUrl = `${getURL()}/assets/${currentPath.slice(1).replace(/\/$/, "").replaceAll("_", "__").replaceAll("/", "_")}${LOADER_JS_POSTFIX_UNCACHED.replace(".js", ".native.js")}`; } try { var moduleLoadStart = performance.now(); var controller = new AbortController(); var timeoutId = setTimeout(function () { return controller.abort(); }, LOADER_TIMEOUT); var loaderJsCode; try { var loaderJsCodeResp = await fetch(nativeLoaderJSUrl, { signal: controller.signal }); if (!loaderJsCodeResp.ok) throw new Error(`Response not ok: ${loaderJsCodeResp.status}`); loaderJsCode = await loaderJsCodeResp.text(); } finally { clearTimeout(timeoutId); } var result = eval(`() => { var exports = {}; ${loaderJsCode}; return exports; }`)(); var moduleLoadTime = performance.now() - moduleLoadStart; if (typeof result.loader !== "function") throw new Error("Loader code isn't exporting a `loader` function"); var executionStart = performance.now(); var data = await result.loader(); var executionTime = performance.now() - executionStart; var totalTime = performance.now() - startTime; if (data === null || data === void 0 ? void 0 : data.__oneRedirect) { recordLoaderTiming === null || recordLoaderTiming === void 0 || recordLoaderTiming({ path: currentPath, startTime, moduleLoadTime, executionTime, totalTime, source: "initial" }); updateState(currentPath, { data: void 0, hasLoadedOnce: true, promise: void 0 }); router.replace(data.__oneRedirect); return; } if ((data === null || data === void 0 ? void 0 : data.__oneError) === 404) { recordLoaderTiming === null || recordLoaderTiming === void 0 || recordLoaderTiming({ path: currentPath, startTime, moduleLoadTime, executionTime, totalTime, source: "initial" }); var notFoundRoute = findNearestNotFoundRoute(currentPath, routeNode); setNotFoundState({ notFoundPath: data.__oneNotFoundPath || "/+not-found", notFoundRouteNode: notFoundRoute || void 0, originalPath: currentPath }); return; } updateState(currentPath, { data, hasLoadedOnce: true, promise: void 0 }); recordLoaderTiming === null || recordLoaderTiming === void 0 || recordLoaderTiming({ path: currentPath, startTime, moduleLoadTime, executionTime, totalTime, source: "initial" }); return; } catch (e) { console.error(`[one] native loader error for ${currentPath}:`, _instanceof(e, Error) ? e.message : e, `url: ${nativeLoaderJSUrl}`); var totalTime = performance.now() - startTime; updateState(currentPath, { data: {}, promise: void 0 }); recordLoaderTiming === null || recordLoaderTiming === void 0 || recordLoaderTiming({ path: currentPath, startTime, totalTime, error: _instanceof(e, Error) ? e.message : String(e), source: "initial" }); return; } var loaderJSUrl = getLoaderPath(currentPath, true); var moduleLoadStart = performance.now(); var module = await ((_dynamicImport = dynamicImport(loaderJSUrl)) === null || _dynamicImport === void 0 ? void 0 : _dynamicImport.catch(function () { return null; })); var moduleLoadTime = performance.now() - moduleLoadStart; if (!(module === null || module === void 0 ? void 0 : module.loader)) { updateState(currentPath, { data: void 0, hasLoadedOnce: true, promise: void 0 }); return; } var executionStart = performance.now(); var result = await module.loader(); var executionTime = performance.now() - executionStart; var totalTime = performance.now() - startTime; if (result === null || result === void 0 ? void 0 : result.__oneRedirect) { recordLoaderTiming === null || recordLoaderTiming === void 0 || recordLoaderTiming({ path: currentPath, startTime, moduleLoadTime, executionTime, totalTime, source: "initial" }); updateState(currentPath, { data: void 0, hasLoadedOnce: true, promise: void 0 }); router.replace(result.__oneRedirect); return; } if ((result === null || result === void 0 ? void 0 : result.__oneError) === 404) { recordLoaderTiming === null || recordLoaderTiming === void 0 || recordLoaderTiming({ path: currentPath, startTime, moduleLoadTime, executionTime, totalTime, source: "initial" }); var notFoundRoute = findNearestNotFoundRoute(currentPath, routeNode); setNotFoundState({ notFoundPath: result.__oneNotFoundPath || "/+not-found", notFoundRouteNode: notFoundRoute || void 0, originalPath: currentPath }); return; } updateState(currentPath, { data: result, hasLoadedOnce: true, promise: void 0 }); recordLoaderTiming === null || recordLoaderTiming === void 0 || recordLoaderTiming({ path: currentPath, startTime, moduleLoadTime, executionTime, totalTime, source: "initial" }); } catch (err) { var totalTime = performance.now() - startTime; updateState(currentPath, { error: err, promise: void 0 }); recordLoaderTiming === null || recordLoaderTiming === void 0 || recordLoaderTiming({ path: currentPath, startTime, totalTime, error: _instanceof(err, Error) ? err.message : String(err), source: "initial" }); } }; var promise = loadData(); loaderStateEntry.promise = promise; } } 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 }; } function useLoader(loader2) { var { data: data2 } = useLoaderState(loader2); return data2; } var results = /* @__PURE__ */new Map(); var started = /* @__PURE__ */new Map(); function resetLoaderState() { results.clear(); started.clear(); } function useAsyncFn(val, props) { var key = (val ? weakKey(val) : "") + JSON.stringify(props); if (val) { if (!started.get(key)) { started.set(key, true); var next = val(props); if (_instanceof(next, Promise)) next = next.then(function (final) { results.set(key, final); }).catch(function (err) { console.error(`Error running loader()`, err); results.set(key, void 0); }); results.set(key, next); } } var current = results.get(key); if (_instanceof(current, Promise)) throw current; return current; } export { getLoaderTimingHistory, refetchLoader, refetchMatchLoader, resetLoaderState, setSSRLoaderData, useLoader, useLoaderState }; //# sourceMappingURL=useLoader.native.js.map