UNPKG

10.4 kBJavaScriptView Raw
1/**
2 * react-router v8.1.0
3 *
4 * Copyright (c) Remix Software Inc.
5 *
6 * This source code is licensed under the MIT license found in the
7 * LICENSE.md file in the root directory of this source tree.
8 *
9 * @license MIT
10 */
11import { createPath, invariant } from "./history.js";
12import { RouterContextProvider, createContext } from "./utils.js";
13//#region lib/router/instrumentation.ts
14const UninstrumentedSymbol = Symbol("Uninstrumented");
15const instrumentationResultMetaContext = createContext();
16let instrumentationClientResultMetaReceivers = /* @__PURE__ */ new WeakMap();
17function getRouteInstrumentationUpdates(fns, route) {
18 let aggregated = {
19 lazy: [],
20 "lazy.loader": [],
21 "lazy.action": [],
22 "lazy.middleware": [],
23 middleware: [],
24 loader: [],
25 action: []
26 };
27 fns.forEach((fn) => fn({
28 id: route.id,
29 index: route.index,
30 path: route.path,
31 instrument(i) {
32 if (i.lazy != null) aggregated.lazy.push(i.lazy);
33 if (i["lazy.loader"] != null) aggregated["lazy.loader"].push(i["lazy.loader"]);
34 if (i["lazy.action"] != null) aggregated["lazy.action"].push(i["lazy.action"]);
35 if (i["lazy.middleware"] != null) aggregated["lazy.middleware"].push(i["lazy.middleware"]);
36 if (i.middleware != null) aggregated.middleware.push(i.middleware);
37 if (i.loader != null) aggregated.loader.push(i.loader);
38 if (i.action != null) aggregated.action.push(i.action);
39 }
40 }));
41 let updates = {};
42 if (typeof route.lazy === "function" && aggregated.lazy.length > 0) {
43 let lazy = route.lazy;
44 updates.lazy = async (...args) => {
45 return throwOrReturnResult(await recurseRight(aggregated.lazy, void 0, () => lazy(...args), getInstrumentationInnerResult));
46 };
47 }
48 if (typeof route.lazy === "object") {
49 let lazyObject = route.lazy;
50 if (typeof lazyObject.middleware === "function" && aggregated["lazy.middleware"].length > 0) {
51 let middleware = lazyObject.middleware;
52 updates.lazy = Object.assign(updates.lazy || {}, { middleware: async (...args) => {
53 return throwOrReturnResult(await recurseRight(aggregated["lazy.middleware"], void 0, () => middleware(...args), getInstrumentationInnerResult));
54 } });
55 }
56 if (typeof lazyObject.loader === "function" && aggregated["lazy.loader"].length > 0) {
57 let loader = lazyObject.loader;
58 updates.lazy = Object.assign(updates.lazy || {}, { loader: async (...args) => {
59 return throwOrReturnResult(await recurseRight(aggregated["lazy.loader"], void 0, () => loader(...args), getInstrumentationInnerResult));
60 } });
61 }
62 if (typeof lazyObject.action === "function" && aggregated["lazy.action"].length > 0) {
63 let action = lazyObject.action;
64 updates.lazy = Object.assign(updates.lazy || {}, { action: async (...args) => {
65 return throwOrReturnResult(await recurseRight(aggregated["lazy.action"], void 0, () => action(...args), getInstrumentationInnerResult));
66 } });
67 }
68 }
69 if (typeof route.loader === "function" && aggregated.loader.length > 0) {
70 let original = getUninstrumentedHandler(route.loader);
71 let instrumented = async (...args) => {
72 return throwOrReturnResult(await recurseRight(aggregated.loader, getHandlerInfo(args[0]), () => original(...args), getInstrumentationInnerResult));
73 };
74 if (original.hydrate === true) instrumented.hydrate = true;
75 setUninstrumentedHandler(instrumented, original);
76 updates.loader = instrumented;
77 }
78 if (typeof route.action === "function" && aggregated.action.length > 0) {
79 let original = getUninstrumentedHandler(route.action);
80 let instrumented = async (...args) => {
81 return throwOrReturnResult(await recurseRight(aggregated.action, getHandlerInfo(args[0]), () => original(...args), getInstrumentationInnerResult));
82 };
83 setUninstrumentedHandler(instrumented, original);
84 updates.action = instrumented;
85 }
86 if (route.middleware && route.middleware.length > 0 && aggregated.middleware.length > 0) updates.middleware = route.middleware.map((middleware) => {
87 let original = getUninstrumentedHandler(middleware);
88 let instrumented = async (...args) => {
89 return throwOrReturnResult(await recurseRight(aggregated.middleware, getHandlerInfo(args[0]), () => original(...args), getInstrumentationInnerResult));
90 };
91 setUninstrumentedHandler(instrumented, original);
92 return instrumented;
93 });
94 return updates;
95}
96function instrumentClientSideRouter(router, fns) {
97 let aggregated = {
98 navigate: [],
99 fetch: []
100 };
101 fns.forEach((fn) => fn({ instrument(i) {
102 if (i.navigate != null) aggregated.navigate.push(i.navigate);
103 if (i.fetch != null) aggregated.fetch.push(i.fetch);
104 } }));
105 if (aggregated.navigate.length > 0) {
106 let navigate = getUninstrumentedHandler(router.navigate);
107 let instrumentedNavigate = async (...args) => {
108 let [to, opts] = args;
109 let meta;
110 let info = {
111 to: typeof to === "number" || typeof to === "string" ? to : to ? createPath(to) : ".",
112 ...getRouterInfo(router, opts ?? {})
113 };
114 return throwOrReturnResult(await recurseRight(aggregated.navigate, info, async () => {
115 if (typeof to === "number") return await navigate(...args);
116 let cleanup = setInstrumentationClientResultMetaReceiver(router, (value) => {
117 meta = value;
118 });
119 try {
120 return await navigate(...args);
121 } finally {
122 cleanup();
123 }
124 }, (result) => ({
125 ...getInstrumentationInnerResult(result),
126 meta
127 })));
128 };
129 setUninstrumentedHandler(instrumentedNavigate, navigate);
130 router.navigate = instrumentedNavigate;
131 }
132 if (aggregated.fetch.length > 0) {
133 let fetch = getUninstrumentedHandler(router.fetch);
134 let instrumentedFetch = async (...args) => {
135 let [key, _, href, opts] = args;
136 let meta;
137 return throwOrReturnResult(await recurseRight(aggregated.fetch, {
138 href: href ?? ".",
139 fetcherKey: key,
140 ...getRouterInfo(router, opts ?? {})
141 }, async () => {
142 let cleanup = setInstrumentationClientResultMetaReceiver(router, (value) => {
143 meta = value;
144 });
145 try {
146 return await fetch(...args);
147 } finally {
148 cleanup();
149 }
150 }, (result) => ({
151 ...getInstrumentationInnerResult(result),
152 meta
153 })));
154 };
155 setUninstrumentedHandler(instrumentedFetch, fetch);
156 router.fetch = instrumentedFetch;
157 }
158 return router;
159}
160function instrumentHandler(handler, fns) {
161 let aggregated = { request: [] };
162 fns.forEach((fn) => fn({ instrument(i) {
163 if (i.request != null) aggregated.request.push(i.request);
164 } }));
165 let instrumentedHandler = handler;
166 if (aggregated.request.length > 0) instrumentedHandler = async (...args) => {
167 let [request, context] = args;
168 let instrumentationContext = context ?? new RouterContextProvider();
169 return throwOrReturnResult(await recurseRight(aggregated.request, {
170 request: getReadonlyRequest(request),
171 context: getReadonlyContext(instrumentationContext)
172 }, () => handler(request, instrumentationContext), (result, info) => {
173 let meta;
174 try {
175 meta = info.context?.get(instrumentationResultMetaContext);
176 } catch {}
177 invariant(result.value instanceof Response, "Expected a Response from the request handler");
178 return {
179 ...getInstrumentationInnerResult(result),
180 statusCode: result.value.status,
181 meta
182 };
183 }));
184 };
185 return instrumentedHandler;
186}
187function getUninstrumentedHandler(handler) {
188 return handler[UninstrumentedSymbol] ?? handler;
189}
190function setUninstrumentedHandler(handler, uninstrumentedHandler) {
191 handler[UninstrumentedSymbol] = uninstrumentedHandler;
192}
193function setInstrumentationClientResultMetaReceiver(router, receiver) {
194 instrumentationClientResultMetaReceivers.set(router, receiver);
195 return () => {
196 if (instrumentationClientResultMetaReceivers.get(router) === receiver) instrumentationClientResultMetaReceivers.delete(router);
197 };
198}
199function consumeInstrumentationClientResultMetaReceiver(router) {
200 let receiver = instrumentationClientResultMetaReceivers.get(router);
201 instrumentationClientResultMetaReceivers.delete(router);
202 return receiver;
203}
204function throwOrReturnResult(result) {
205 if (result.type === "error") throw result.value;
206 return result.value;
207}
208async function recurseRight(impls, info, handler, getInnerResult, state = {
209 result: null,
210 innerResult: null
211}, index = impls.length - 1) {
212 let impl = impls[index];
213 if (!impl) {
214 try {
215 state.result = {
216 type: "success",
217 value: await handler()
218 };
219 } catch (e) {
220 state.result = {
221 type: "error",
222 value: e
223 };
224 }
225 state.innerResult = getInnerResult(state.result, info);
226 } else {
227 let handlerPromise = void 0;
228 let callHandler = async () => {
229 if (handlerPromise) console.error("You cannot call instrumented handlers more than once");
230 else handlerPromise = recurseRight(impls, info, handler, getInnerResult, state, index - 1);
231 await handlerPromise;
232 invariant(state.innerResult, "Expected an inner result");
233 return state.innerResult;
234 };
235 try {
236 await impl(callHandler, info);
237 } catch (e) {
238 console.error("An instrumentation function threw an error:", e);
239 }
240 if (!handlerPromise) await callHandler();
241 await handlerPromise;
242 }
243 if (state.result) return state.result;
244 state.result = {
245 type: "error",
246 value: /* @__PURE__ */ new Error("No result assigned in instrumentation chain.")
247 };
248 state.innerResult = getInnerResult(state.result, info);
249 return state.result;
250}
251function getInstrumentationInnerResult(result) {
252 if (result.type === "error" && result.value instanceof Error) return {
253 status: "error",
254 error: result.value
255 };
256 return {
257 status: "success",
258 error: void 0
259 };
260}
261function getHandlerInfo(args) {
262 let { request, context, params } = args;
263 return {
264 ...args,
265 request: getReadonlyRequest(request),
266 params: { ...params },
267 context: getReadonlyContext(context)
268 };
269}
270function getRouterInfo(router, opts) {
271 return {
272 currentUrl: createPath(router.state.location),
273 ..."formMethod" in opts ? { formMethod: opts.formMethod } : {},
274 ..."formEncType" in opts ? { formEncType: opts.formEncType } : {},
275 ..."formData" in opts ? { formData: opts.formData } : {},
276 ..."body" in opts ? { body: opts.body } : {}
277 };
278}
279function getReadonlyRequest(request) {
280 return {
281 method: request.method,
282 url: request.url,
283 headers: { get: (...args) => request.headers.get(...args) }
284 };
285}
286function getReadonlyContext(context) {
287 return { get: (ctx) => context.get(ctx) };
288}
289//#endregion
290export { consumeInstrumentationClientResultMetaReceiver, getRouteInstrumentationUpdates, instrumentClientSideRouter, instrumentHandler, instrumentationResultMetaContext };