UNPKG

6.54 kBJavaScriptView Raw
1/**
2 * react-router v8.0.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";
12//#region lib/router/instrumentation.ts
13const UninstrumentedSymbol = Symbol("Uninstrumented");
14function getRouteInstrumentationUpdates(fns, route) {
15 let aggregated = {
16 lazy: [],
17 "lazy.loader": [],
18 "lazy.action": [],
19 "lazy.middleware": [],
20 middleware: [],
21 loader: [],
22 action: []
23 };
24 fns.forEach((fn) => fn({
25 id: route.id,
26 index: route.index,
27 path: route.path,
28 instrument(i) {
29 let keys = Object.keys(aggregated);
30 for (let key of keys) if (i[key]) aggregated[key].push(i[key]);
31 }
32 }));
33 let updates = {};
34 if (typeof route.lazy === "function" && aggregated.lazy.length > 0) {
35 let instrumented = wrapImpl(aggregated.lazy, route.lazy, () => void 0);
36 if (instrumented) updates.lazy = instrumented;
37 }
38 if (typeof route.lazy === "object") {
39 let lazyObject = route.lazy;
40 [
41 "middleware",
42 "loader",
43 "action"
44 ].forEach((key) => {
45 let lazyFn = lazyObject[key];
46 let instrumentations = aggregated[`lazy.${key}`];
47 if (typeof lazyFn === "function" && instrumentations.length > 0) {
48 let instrumented = wrapImpl(instrumentations, lazyFn, () => void 0);
49 if (instrumented) updates.lazy = Object.assign(updates.lazy || {}, { [key]: instrumented });
50 }
51 });
52 }
53 ["loader", "action"].forEach((key) => {
54 let handler = route[key];
55 if (typeof handler === "function" && aggregated[key].length > 0) {
56 let original = handler[UninstrumentedSymbol] ?? handler;
57 let instrumented = wrapImpl(aggregated[key], original, (...args) => getHandlerInfo(args[0]));
58 if (instrumented) {
59 if (key === "loader" && original.hydrate === true) instrumented.hydrate = true;
60 instrumented[UninstrumentedSymbol] = original;
61 updates[key] = instrumented;
62 }
63 }
64 });
65 if (route.middleware && route.middleware.length > 0 && aggregated.middleware.length > 0) updates.middleware = route.middleware.map((middleware) => {
66 let original = middleware[UninstrumentedSymbol] ?? middleware;
67 let instrumented = wrapImpl(aggregated.middleware, original, (...args) => getHandlerInfo(args[0]));
68 if (instrumented) {
69 instrumented[UninstrumentedSymbol] = original;
70 return instrumented;
71 }
72 return middleware;
73 });
74 return updates;
75}
76function instrumentClientSideRouter(router, fns) {
77 let aggregated = {
78 navigate: [],
79 fetch: []
80 };
81 fns.forEach((fn) => fn({ instrument(i) {
82 let keys = Object.keys(i);
83 for (let key of keys) if (i[key]) aggregated[key].push(i[key]);
84 } }));
85 if (aggregated.navigate.length > 0) {
86 let navigate = router.navigate[UninstrumentedSymbol] ?? router.navigate;
87 let instrumentedNavigate = wrapImpl(aggregated.navigate, navigate, (...args) => {
88 let [to, opts] = args;
89 return {
90 to: typeof to === "number" || typeof to === "string" ? to : to ? createPath(to) : ".",
91 ...getRouterInfo(router, opts ?? {})
92 };
93 });
94 if (instrumentedNavigate) {
95 instrumentedNavigate[UninstrumentedSymbol] = navigate;
96 router.navigate = instrumentedNavigate;
97 }
98 }
99 if (aggregated.fetch.length > 0) {
100 let fetch = router.fetch[UninstrumentedSymbol] ?? router.fetch;
101 let instrumentedFetch = wrapImpl(aggregated.fetch, fetch, (...args) => {
102 let [key, , href, opts] = args;
103 return {
104 href: href ?? ".",
105 fetcherKey: key,
106 ...getRouterInfo(router, opts ?? {})
107 };
108 });
109 if (instrumentedFetch) {
110 instrumentedFetch[UninstrumentedSymbol] = fetch;
111 router.fetch = instrumentedFetch;
112 }
113 }
114 return router;
115}
116function instrumentHandler(handler, fns) {
117 let aggregated = { request: [] };
118 fns.forEach((fn) => fn({ instrument(i) {
119 let keys = Object.keys(i);
120 for (let key of keys) if (i[key]) aggregated[key].push(i[key]);
121 } }));
122 let instrumentedHandler = handler;
123 if (aggregated.request.length > 0) instrumentedHandler = wrapImpl(aggregated.request, handler, (...args) => {
124 let [request, context] = args;
125 return {
126 request: getReadonlyRequest(request),
127 context: context != null ? getReadonlyContext(context) : context
128 };
129 });
130 return instrumentedHandler;
131}
132function wrapImpl(impls, handler, getInfo) {
133 if (impls.length === 0) return null;
134 return async (...args) => {
135 let result = await recurseRight(impls, getInfo(...args), () => handler(...args), impls.length - 1);
136 if (result.type === "error") throw result.value;
137 return result.value;
138 };
139}
140async function recurseRight(impls, info, handler, index) {
141 let impl = impls[index];
142 let result;
143 if (!impl) try {
144 result = {
145 type: "success",
146 value: await handler()
147 };
148 } catch (e) {
149 result = {
150 type: "error",
151 value: e
152 };
153 }
154 else {
155 let handlerPromise = void 0;
156 let callHandler = async () => {
157 if (handlerPromise) console.error("You cannot call instrumented handlers more than once");
158 else handlerPromise = recurseRight(impls, info, handler, index - 1);
159 result = await handlerPromise;
160 invariant(result, "Expected a result");
161 if (result.type === "error" && result.value instanceof Error) return {
162 status: "error",
163 error: result.value
164 };
165 return {
166 status: "success",
167 error: void 0
168 };
169 };
170 try {
171 await impl(callHandler, info);
172 } catch (e) {
173 console.error("An instrumentation function threw an error:", e);
174 }
175 if (!handlerPromise) await callHandler();
176 await handlerPromise;
177 }
178 if (result) return result;
179 return {
180 type: "error",
181 value: /* @__PURE__ */ new Error("No result assigned in instrumentation chain.")
182 };
183}
184function getHandlerInfo(args) {
185 let { request, context, params, pattern } = args;
186 return {
187 request: getReadonlyRequest(request),
188 params: { ...params },
189 pattern,
190 context: getReadonlyContext(context)
191 };
192}
193function getRouterInfo(router, opts) {
194 return {
195 currentUrl: createPath(router.state.location),
196 ..."formMethod" in opts ? { formMethod: opts.formMethod } : {},
197 ..."formEncType" in opts ? { formEncType: opts.formEncType } : {},
198 ..."formData" in opts ? { formData: opts.formData } : {},
199 ..."body" in opts ? { body: opts.body } : {}
200 };
201}
202function getReadonlyRequest(request) {
203 return {
204 method: request.method,
205 url: request.url,
206 headers: { get: (...args) => request.headers.get(...args) }
207 };
208}
209function getReadonlyContext(context) {
210 return { get: (ctx) => context.get(ctx) };
211}
212//#endregion
213export { getRouteInstrumentationUpdates, instrumentClientSideRouter, instrumentHandler };