• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

infernojs / inferno / #8072

26 Nov 2024 08:43PM UTC coverage: 92.865%. First build
#8072

travis-ci

1846 of 2117 branches covered (87.2%)

4061 of 4373 relevant lines covered (92.87%)

4141.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.69
/packages/inferno-router/src/resolveLoaders.ts
1
import { isNullOrUndef, isUndefined } from 'inferno-shared';
21✔
2
import { matchPath } from './matchPath';
21✔
3
import type { TLoaderData, TLoaderProps } from './Router';
4
import { Switch } from './Switch';
21✔
5
import { Route } from './Route';
21✔
6

7
export async function resolveLoaders(
25✔
8
  loaderEntries: TLoaderEntry[],
9
): Promise<Record<string, TLoaderData>> {
10
  const promises = loaderEntries.map(
25✔
11
    async ({ path, params, request, loader }) => {
12
      return await resolveEntry(path, params, request, loader);
28✔
13
    },
14
  );
15
  return await Promise.all(promises).then((result) => {
25✔
16
    return Object.fromEntries(result);
25✔
17
  });
18
}
19

20
interface TLoaderEntry {
21
  path: string;
22
  params: Record<string, any>;
23
  request: Request;
24
  controller: AbortController;
25
  loader: (props: TLoaderProps<any>) => Promise<TLoaderEntry>;
26
}
27

28
export function traverseLoaders(
167✔
29
  location: string,
30
  tree: any,
31
  base?: string,
32
): TLoaderEntry[] {
33
  return _traverseLoaders(location, tree, base, false);
167✔
34
}
35

36
function _isSwitch(node: any): boolean {
37
  // Using the same patterns as for _isRoute, but I don't have a test where
38
  // I pass a Switch via an array, but it is better to be consistent.
39
  return node?.type?.prototype instanceof Switch || node?.type === Switch;
619✔
40
}
41

42
function _isRoute(node: any): boolean {
43
  // So the === check is needed if routes are passed in an array,
44
  // the instanceof test if routes are passed as children to a Component
45
  // This feels inconsistent, but at least it works.
46
  return node?.type?.prototype instanceof Route || node?.type === Route;
688✔
47
}
48

49
// Optionally pass base param during SSR to get fully qualified request URI passed to loader in request param
50
function _traverseLoaders(
51
  location: string,
52
  tree: any,
53
  base?: string,
54
  parentIsSwitch = false,
×
55
): TLoaderEntry[] {
56
  // Make sure tree isn't null
57
  if (isNullOrUndef(tree)) return [];
786✔
58

59
  if (Array.isArray(tree)) {
779✔
60
    let hasMatch = false;
91✔
61
    const entriesOfArr = tree.reduce((res, node) => {
91✔
62
      if (parentIsSwitch && hasMatch) return res;
259✔
63

64
      const outpArr = _traverseLoaders(location, node, base, _isSwitch(node));
256✔
65
      if (parentIsSwitch && outpArr.length > 0) {
256✔
66
        hasMatch = true;
2✔
67
      }
68
      return [...res, ...outpArr];
256✔
69
    }, []);
70
    return entriesOfArr;
91✔
71
  }
72

73
  const outp: TLoaderEntry[] = [];
688✔
74
  if (_isRoute(tree) && tree.props) {
688✔
75
    // TODO: Should we check if we are in Router? It is defensive and could save a bit of time, but is it worth it?
76
    const {
77
      path,
78
      exact = false,
177✔
79
      strict = false,
215✔
80
      sensitive = false,
217✔
81
    } = tree.props;
217✔
82
    const match = matchPath(location, {
217✔
83
      exact,
84
      path,
85
      sensitive,
86
      strict,
87
    });
88

89
    // So we can bail out of recursion it this was a Route which didn't match
90
    if (!match) {
217✔
91
      return outp;
103✔
92
    } else if (!tree.context && tree.props?.loader && tree.props?.path) {
114✔
93
      // Add any loader on this node (but only on the VNode)
94
      const { params } = match;
28✔
95
      const controller = new AbortController();
28✔
96
      const request = createClientSideRequest(
28✔
97
        location,
98
        controller.signal,
99
        base,
100
      );
101

102
      outp.push({
28✔
103
        controller,
104
        loader: tree.props.loader,
105
        params,
106
        path,
107
        request,
108
      });
109
    }
110
  }
111

112
  // Traverse children
113
  const children = tree.children ?? tree.props?.children;
585✔
114
  if (isNullOrUndef(children)) return outp;
585✔
115

116
  const entries = _traverseLoaders(location, children, base, _isSwitch(tree));
363✔
117
  return [...outp, ...entries];
363✔
118
}
119

120
async function resolveEntry(path, params, request, loader): Promise<any> {
121
  return (
28✔
122
    loader({ params, request })
123
      .then(async (res: any) => {
124
        // This implementation is based on:
125
        // https://github.com/remix-run/react-router/blob/4f3ad7b96e6e0228cc952cd7eafe2c265c7393c7/packages/router/router.ts#L2787-L2879
126

127
        // Check if regular data object (from tests or initialData)
128
        if (typeof res.json !== 'function') {
24✔
129
          return [path, { res }];
22✔
130
        }
131

132
        const contentType = res.headers.get('Content-Type');
2✔
133
        let dataPromise: Promise<any>;
134
        // Check between word boundaries instead of startsWith() due to the last
135
        // paragraph of https://httpwg.org/specs/rfc9110.html#field.content-type
136
        if (contentType && /\bapplication\/json\b/.test(contentType)) {
2✔
137
          dataPromise = res.json();
1✔
138
        } else {
139
          dataPromise = res.text();
1✔
140
        }
141

142
        return await dataPromise
2✔
143
          .then((body) => {
144
            // We got a JSON error
145
            if (!res.ok) {
2!
146
              return [path, { err: body }];
×
147
            }
148
            // We got JSON response
149
            return [path, { res: body }];
2✔
150
          })
151
          // Could not parse JSON
152
          .catch((err) => [path, { err }]);
×
153
      })
154
      // Could not fetch data
155
      .catch((err) => [path, { err }])
4✔
156
  );
157
}
158

159
// From react-router
160
// NOTE: We don't currently support the submission param of createClientSideRequest which is why
161
// some of the related code is commented away
162

163
export type FormEncType =
164
  | 'application/x-www-form-urlencoded'
165
  | 'multipart/form-data';
166

167
export type MutationFormMethod = 'post' | 'put' | 'patch' | 'delete';
168
export type FormMethod = 'get' | MutationFormMethod;
169

170
// TODO: react-router supports submitting forms with loaders, this is related to that
171
// const validMutationMethodsArr: MutationFormMethod[] = [
172
//   "post",
173
//   "put",
174
//   "patch",
175
//   "delete",
176
// ];
177
// const validMutationMethods = new Set<MutationFormMethod>(
178
//   validMutationMethodsArr
179
// );
180

181
/**
182
 * @private
183
 * Internal interface to pass around for action submissions, not intended for
184
 * external consumption
185
 */
186
export interface Submission {
187
  formMethod: FormMethod;
188
  formAction: string;
189
  formEncType: FormEncType;
190
  formData: FormData;
191
}
192

193
const inBrowser = typeof window === 'undefined';
21✔
194
function createClientSideRequest(
195
  location: string | Location,
196
  signal: AbortSignal,
197
  // submission?: Submission
198
  base?: string,
199
): Request {
200
  const url =
201
    inBrowser || !isUndefined(base)
28!
202
      ? createClientSideURL(location, base)
203
      : location.toString();
204
  const init: RequestInit = { signal };
28✔
205

206
  // TODO: react-router supports submitting forms with loaders, but this needs more investigation
207
  // related code is commented out in this file
208
  // if (submission && isMutationMethod(submission.formMethod)) {
209
  //   let { formMethod, formEncType, formData } = submission;
210
  //   init.method = formMethod.toUpperCase();
211
  //   init.body =
212
  //     formEncType === "application/x-www-form-urlencoded"
213
  //       ? convertFormDataToSearchParams(formData)
214
  //       : formData;
215
  // }
216

217
  // Request is undefined when running tests
218
  if (process.env.NODE_ENV === 'test' && typeof Request === 'undefined') {
28✔
219
    // @ts-expect-error minimum to fix tests
220
    global.Request = class Request {
3✔
221
      public url;
222
      public signal;
223
      constructor(_url: URL | string, _init: RequestInit) {
224
        this.url = _url;
28✔
225
        this.signal = _init.signal;
28✔
226
      }
227
    };
228
  }
229

230
  // Content-Type is inferred (https://fetch.spec.whatwg.org/#dom-request)
231
  return new Request(url, init);
28✔
232
}
233

234
/**
235
 * Parses a string URL path into its separate pathname, search, and hash components.
236
 */
237

238
export function createClientSideURL(
×
239
  location: Location | string,
240
  base?: string,
241
): URL {
242
  if (base === undefined && typeof window !== 'undefined') {
×
243
    // window.location.origin is "null" (the literal string value) in Firefox
244
    // under certain conditions, notably when serving from a local HTML file
245
    // See https://bugzilla.mozilla.org/show_bug.cgi?id=878297
246
    base =
×
247
      window?.location?.origin !== 'null'
×
248
        ? window.location.origin
249
        : window.location.href;
250
  }
251

252
  const url = new URL(location.toString(), base);
×
253
  url.hash = '';
×
254
  return url;
×
255
}
256

257
// TODO: react-router supports submitting forms with loaders, this is related to that
258
// function isMutationMethod(method?: string): method is MutationFormMethod {
259
//   return validMutationMethods.has(method as MutationFormMethod);
260
// }
261

262
// function convertFormDataToSearchParams(formData: FormData): URLSearchParams {
263
//   let searchParams = new URLSearchParams();
264

265
//   for (let [key, value] of formData.entries()) {
266
//     // invariant(
267
//     //   typeof value === "string",
268
//     //   'File inputs are not supported with encType "application/x-www-form-urlencoded", ' +
269
//     //     'please use "multipart/form-data" instead.'
270
//     // );
271
//     if (typeof value === "string") {
272
//       searchParams.append(key, value);
273
//     }
274
//   }
275

276
//   return searchParams;
277
// }
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc