UNPKG

react-router

Version:
214 lines (213 loc) • 6.54 kB
/** * react-router v8.0.0 * * Copyright (c) Remix Software Inc. * * This source code is licensed under the MIT license found in the * LICENSE.md file in the root directory of this source tree. * * @license MIT */ import { createPath, invariant } from "./history.js"; //#region lib/router/instrumentation.ts const UninstrumentedSymbol = Symbol("Uninstrumented"); function getRouteInstrumentationUpdates(fns, route) { let aggregated = { lazy: [], "lazy.loader": [], "lazy.action": [], "lazy.middleware": [], middleware: [], loader: [], action: [] }; fns.forEach((fn) => fn({ id: route.id, index: route.index, path: route.path, instrument(i) { let keys = Object.keys(aggregated); for (let key of keys) if (i[key]) aggregated[key].push(i[key]); } })); let updates = {}; if (typeof route.lazy === "function" && aggregated.lazy.length > 0) { let instrumented = wrapImpl(aggregated.lazy, route.lazy, () => void 0); if (instrumented) updates.lazy = instrumented; } if (typeof route.lazy === "object") { let lazyObject = route.lazy; [ "middleware", "loader", "action" ].forEach((key) => { let lazyFn = lazyObject[key]; let instrumentations = aggregated[`lazy.${key}`]; if (typeof lazyFn === "function" && instrumentations.length > 0) { let instrumented = wrapImpl(instrumentations, lazyFn, () => void 0); if (instrumented) updates.lazy = Object.assign(updates.lazy || {}, { [key]: instrumented }); } }); } ["loader", "action"].forEach((key) => { let handler = route[key]; if (typeof handler === "function" && aggregated[key].length > 0) { let original = handler[UninstrumentedSymbol] ?? handler; let instrumented = wrapImpl(aggregated[key], original, (...args) => getHandlerInfo(args[0])); if (instrumented) { if (key === "loader" && original.hydrate === true) instrumented.hydrate = true; instrumented[UninstrumentedSymbol] = original; updates[key] = instrumented; } } }); if (route.middleware && route.middleware.length > 0 && aggregated.middleware.length > 0) updates.middleware = route.middleware.map((middleware) => { let original = middleware[UninstrumentedSymbol] ?? middleware; let instrumented = wrapImpl(aggregated.middleware, original, (...args) => getHandlerInfo(args[0])); if (instrumented) { instrumented[UninstrumentedSymbol] = original; return instrumented; } return middleware; }); return updates; } function instrumentClientSideRouter(router, fns) { let aggregated = { navigate: [], fetch: [] }; fns.forEach((fn) => fn({ instrument(i) { let keys = Object.keys(i); for (let key of keys) if (i[key]) aggregated[key].push(i[key]); } })); if (aggregated.navigate.length > 0) { let navigate = router.navigate[UninstrumentedSymbol] ?? router.navigate; let instrumentedNavigate = wrapImpl(aggregated.navigate, navigate, (...args) => { let [to, opts] = args; return { to: typeof to === "number" || typeof to === "string" ? to : to ? createPath(to) : ".", ...getRouterInfo(router, opts ?? {}) }; }); if (instrumentedNavigate) { instrumentedNavigate[UninstrumentedSymbol] = navigate; router.navigate = instrumentedNavigate; } } if (aggregated.fetch.length > 0) { let fetch = router.fetch[UninstrumentedSymbol] ?? router.fetch; let instrumentedFetch = wrapImpl(aggregated.fetch, fetch, (...args) => { let [key, , href, opts] = args; return { href: href ?? ".", fetcherKey: key, ...getRouterInfo(router, opts ?? {}) }; }); if (instrumentedFetch) { instrumentedFetch[UninstrumentedSymbol] = fetch; router.fetch = instrumentedFetch; } } return router; } function instrumentHandler(handler, fns) { let aggregated = { request: [] }; fns.forEach((fn) => fn({ instrument(i) { let keys = Object.keys(i); for (let key of keys) if (i[key]) aggregated[key].push(i[key]); } })); let instrumentedHandler = handler; if (aggregated.request.length > 0) instrumentedHandler = wrapImpl(aggregated.request, handler, (...args) => { let [request, context] = args; return { request: getReadonlyRequest(request), context: context != null ? getReadonlyContext(context) : context }; }); return instrumentedHandler; } function wrapImpl(impls, handler, getInfo) { if (impls.length === 0) return null; return async (...args) => { let result = await recurseRight(impls, getInfo(...args), () => handler(...args), impls.length - 1); if (result.type === "error") throw result.value; return result.value; }; } async function recurseRight(impls, info, handler, index) { let impl = impls[index]; let result; if (!impl) try { result = { type: "success", value: await handler() }; } catch (e) { result = { type: "error", value: e }; } else { let handlerPromise = void 0; let callHandler = async () => { if (handlerPromise) console.error("You cannot call instrumented handlers more than once"); else handlerPromise = recurseRight(impls, info, handler, index - 1); result = await handlerPromise; invariant(result, "Expected a result"); if (result.type === "error" && result.value instanceof Error) return { status: "error", error: result.value }; return { status: "success", error: void 0 }; }; try { await impl(callHandler, info); } catch (e) { console.error("An instrumentation function threw an error:", e); } if (!handlerPromise) await callHandler(); await handlerPromise; } if (result) return result; return { type: "error", value: /* @__PURE__ */ new Error("No result assigned in instrumentation chain.") }; } function getHandlerInfo(args) { let { request, context, params, pattern } = args; return { request: getReadonlyRequest(request), params: { ...params }, pattern, context: getReadonlyContext(context) }; } function getRouterInfo(router, opts) { return { currentUrl: createPath(router.state.location), ..."formMethod" in opts ? { formMethod: opts.formMethod } : {}, ..."formEncType" in opts ? { formEncType: opts.formEncType } : {}, ..."formData" in opts ? { formData: opts.formData } : {}, ..."body" in opts ? { body: opts.body } : {} }; } function getReadonlyRequest(request) { return { method: request.method, url: request.url, headers: { get: (...args) => request.headers.get(...args) } }; } function getReadonlyContext(context) { return { get: (ctx) => context.get(ctx) }; } //#endregion export { getRouteInstrumentationUpdates, instrumentClientSideRouter, instrumentHandler };