@solidjs/router 0.16.1 → 0.17.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/lifecycle.js CHANGED
@@ -1,4 +1,4 @@
1
- import { isServer } from "solid-js/web";
1
+ import { isServer } from "@solidjs/web";
2
2
  export function createBeforeLeave() {
3
3
  let listeners = new Set();
4
4
  function subscribe(listener) {
@@ -1,4 +1,4 @@
1
- import { isServer } from "solid-js/web";
1
+ import { isServer } from "@solidjs/web";
2
2
  import { createRouter, scrollToHash, bindEvent } from "./createRouter.js";
3
3
  import { StaticRouter } from "./StaticRouter.js";
4
4
  import { setupNativeEvents } from "../data/events.js";
@@ -1,4 +1,4 @@
1
- import { getRequestEvent } from "solid-js/web";
1
+ import { getRequestEvent } from "@solidjs/web";
2
2
  import { createRouterComponent } from "./components.jsx";
3
3
  function getPath(url) {
4
4
  const u = new URL(url);
@@ -10,8 +10,6 @@ export type BaseRouterProps = {
10
10
  singleFlight?: boolean;
11
11
  children?: JSX.Element | RouteDefinition | RouteDefinition[];
12
12
  transformUrl?: (url: string) => string;
13
- /** @deprecated use rootPreload */
14
- rootLoad?: RoutePreloadFunc;
15
13
  };
16
14
  export declare const createRouterComponent: (router: RouterIntegration) => (props: BaseRouterProps) => JSX.Element;
17
15
  export type RouteProps<S extends string, T = unknown> = {
@@ -21,7 +19,5 @@ export type RouteProps<S extends string, T = unknown> = {
21
19
  matchFilters?: MatchFilters<S>;
22
20
  component?: Component<RouteSectionProps<T>>;
23
21
  info?: Record<string, any>;
24
- /** @deprecated use preload */
25
- load?: RoutePreloadFunc<T>;
26
22
  };
27
23
  export declare const Route: <S extends string, T = unknown>(props: RouteProps<S, T>) => JSX.Element;
@@ -1,6 +1,6 @@
1
1
  /*@refresh skip*/
2
- import { children, createMemo, createRoot, getOwner, mergeProps, on, Show, untrack } from "solid-js";
3
- import { getRequestEvent, isServer } from "solid-js/web";
2
+ import { children, createMemo, createRoot, getOwner, merge, untrack } from "solid-js";
3
+ import { getRequestEvent, isServer } from "@solidjs/web";
4
4
  import { createBranches, createRouteContext, createRouterContext, getIntent, getRouteMatches, RouteContextObj, RouterContextObj, setInPreloadFn } from "../routing.js";
5
5
  export const createRouterComponent = (router) => (props) => {
6
6
  const { base } = props;
@@ -13,12 +13,12 @@ export const createRouterComponent = (router) => (props) => {
13
13
  transformUrl: props.transformUrl,
14
14
  });
15
15
  router.create && router.create(routerState);
16
- return (<RouterContextObj.Provider value={routerState}>
17
- <Root routerState={routerState} root={props.root} preload={props.rootPreload || props.rootLoad}>
16
+ return (<RouterContextObj value={routerState}>
17
+ <Root routerState={routerState} root={props.root} preload={props.rootPreload}>
18
18
  {(context = getOwner()) && null}
19
19
  <Routes routerState={routerState} branches={branches()}/>
20
20
  </Root>
21
- </RouterContextObj.Provider>);
21
+ </RouterContextObj>);
22
22
  };
23
23
  function Root(props) {
24
24
  const location = props.routerState.location;
@@ -29,11 +29,13 @@ function Root(props) {
29
29
  props.preload({ params, location, intent: getIntent() || "initial" });
30
30
  setInPreloadFn(false);
31
31
  }));
32
- return (<Show when={props.root} keyed fallback={props.children}>
33
- {Root => (<Root params={params} location={location} data={data()}>
34
- {props.children}
35
- </Root>)}
36
- </Show>);
32
+ const RootComp = props.root;
33
+ if (RootComp) {
34
+ return (<RootComp params={params} location={location} data={data()}>
35
+ {props.children}
36
+ </RootComp>);
37
+ }
38
+ return props.children;
37
39
  }
38
40
  function Routes(props) {
39
41
  if (isServer) {
@@ -54,11 +56,14 @@ function Routes(props) {
54
56
  }
55
57
  const disposers = [];
56
58
  let root;
57
- const routeStates = createMemo(on(props.routerState.matches, (nextMatches, prevMatches, prev) => {
58
- let equal = prevMatches && nextMatches.length === prevMatches.length;
59
+ let prevMatches;
60
+ const routeStates = createMemo((prev) => {
61
+ const nextMatches = props.routerState.matches();
62
+ const previousMatches = prevMatches;
63
+ let equal = previousMatches && nextMatches.length === previousMatches.length;
59
64
  const next = [];
60
65
  for (let i = 0, len = nextMatches.length; i < len; i++) {
61
- const prevMatch = prevMatches && prevMatches[i];
66
+ const prevMatch = previousMatches && previousMatches[i];
62
67
  const nextMatch = nextMatches[i];
63
68
  if (prev && prevMatch && nextMatch.route.key === prevMatch.route.key) {
64
69
  next[i] = prev[i];
@@ -70,7 +75,7 @@ function Routes(props) {
70
75
  }
71
76
  createRoot(dispose => {
72
77
  disposers[i] = dispose;
73
- next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()[i + 1]), () => {
78
+ next[i] = createRouteContext(props.routerState, next[i - 1] || props.routerState.base, createOutlet(() => routeStates()?.[i + 1]), () => {
74
79
  const routeMatches = props.routerState.matches();
75
80
  return routeMatches[i] ?? routeMatches[0];
76
81
  });
@@ -79,21 +84,27 @@ function Routes(props) {
79
84
  }
80
85
  disposers.splice(nextMatches.length).forEach(dispose => dispose());
81
86
  if (prev && equal) {
87
+ prevMatches = nextMatches;
82
88
  return prev;
83
89
  }
84
90
  root = next[0];
91
+ prevMatches = nextMatches;
85
92
  return next;
86
- }));
93
+ }, undefined);
87
94
  return createOutlet(() => routeStates() && root)();
88
95
  }
89
96
  const createOutlet = (child) => {
90
- return () => (<Show when={child()} keyed>
91
- {child => <RouteContextObj.Provider value={child}>{child.outlet()}</RouteContextObj.Provider>}
92
- </Show>);
97
+ return () => {
98
+ const c = child();
99
+ if (c) {
100
+ return <RouteContextObj value={c}>{c.outlet()}</RouteContextObj>;
101
+ }
102
+ return undefined;
103
+ };
93
104
  };
94
105
  export const Route = (props) => {
95
106
  const childRoutes = children(() => props.children);
96
- return mergeProps(props, {
107
+ return merge(props, {
97
108
  get children() {
98
109
  return childRoutes();
99
110
  }
package/dist/routing.d.ts CHANGED
@@ -39,8 +39,8 @@ export declare const useNavigate: () => Navigator;
39
39
  */
40
40
  export declare const useLocation: <S = unknown>() => Location<S>;
41
41
  /**
42
- * Retrieves signal that indicates whether the route is currently in a *Transition*.
43
- * Useful for showing stale/pending state when the route resolution is *Suspended* during concurrent rendering.
42
+ * Retrieves a signal that indicates whether the router is currently processing a navigation.
43
+ * Useful for showing pending navigation state while the next route and its data settle.
44
44
  *
45
45
  * @example
46
46
  * ```js
package/dist/routing.js CHANGED
@@ -1,15 +1,23 @@
1
- import { runWithOwner, batch } from "solid-js";
2
- import { createComponent, createContext, createMemo, createRenderEffect, createSignal, on, onCleanup, untrack, useContext, startTransition, resetErrorBoundaries } from "solid-js";
3
- import { isServer, getRequestEvent } from "solid-js/web";
1
+ import { flush, runWithOwner } from "solid-js";
2
+ import { createComponent, createContext, createMemo, createSignal, onCleanup, untrack, useContext } from "solid-js";
3
+ import { isServer, getRequestEvent } from "@solidjs/web";
4
4
  import { createBeforeLeave } from "./lifecycle.js";
5
5
  import { mockBase, createMemoObject, extractSearchParams, invariant, resolvePath, createMatcher, joinPaths, scoreRoute, mergeSearchString, expandOptionals } from "./utils.js";
6
6
  const MAX_REDIRECTS = 100;
7
7
  /** Consider this API opaque and internal. It is likely to change in the future. */
8
8
  export const RouterContextObj = createContext();
9
9
  export const RouteContextObj = createContext();
10
+ function useOptionalContext(context) {
11
+ try {
12
+ return useContext(context);
13
+ }
14
+ catch {
15
+ return undefined;
16
+ }
17
+ }
10
18
  export const useRouter = () => invariant(useContext(RouterContextObj), "<A> and 'use' router primitives can be only used inside a Route.");
11
19
  let TempRoute;
12
- export const useRoute = () => TempRoute || useContext(RouteContextObj) || useRouter().base;
20
+ export const useRoute = () => TempRoute || useOptionalContext(RouteContextObj) || useRouter().base;
13
21
  export const useResolvedPath = (path) => {
14
22
  const route = useRoute();
15
23
  return createMemo(() => route.resolvePath(path()));
@@ -53,8 +61,8 @@ export const useNavigate = () => useRouter().navigatorFactory();
53
61
  */
54
62
  export const useLocation = () => useRouter().location;
55
63
  /**
56
- * Retrieves signal that indicates whether the route is currently in a *Transition*.
57
- * Useful for showing stale/pending state when the route resolution is *Suspended* during concurrent rendering.
64
+ * Retrieves a signal that indicates whether the router is currently processing a navigation.
65
+ * Useful for showing pending navigation state while the next route and its data settle.
58
66
  *
59
67
  * @example
60
68
  * ```js
@@ -205,12 +213,12 @@ export const useBeforeLeave = (listener) => {
205
213
  onCleanup(s);
206
214
  };
207
215
  export function createRoutes(routeDef, base = "") {
208
- const { component, preload, load, children, info } = routeDef;
216
+ const { component, preload, children, info } = routeDef;
209
217
  const isLeaf = !children || (Array.isArray(children) && !children.length);
210
218
  const shared = {
211
219
  key: routeDef,
212
220
  component,
213
- preload: preload || load,
221
+ preload,
214
222
  info
215
223
  };
216
224
  return asArray(routeDef.path).reduce((acc, originalPath) => {
@@ -309,7 +317,7 @@ function createLocation(path, state, queryWrapper) {
309
317
  const search = createMemo(() => url().search, true);
310
318
  const hash = createMemo(() => url().hash);
311
319
  const key = () => "";
312
- const queryFn = on(search, () => extractSearchParams(url()));
320
+ const queryFn = createMemo(() => extractSearchParams(url()));
313
321
  return {
314
322
  get pathname() {
315
323
  return pathname();
@@ -352,43 +360,32 @@ export function createRouterContext(integration, branches, getContext, options =
352
360
  else if (basePath && !source().value) {
353
361
  setSource({ value: basePath, replace: true, scroll: false });
354
362
  }
355
- const [isRouting, setIsRouting] = createSignal(false);
356
- // Keep track of last target, so that last call to transition wins
363
+ const [isRouting, setIsRouting] = createSignal(false, { pureWrite: true });
364
+ // Navigate override written from event handlers.
365
+ const [navigateTarget, setNavigateTarget] = createSignal(undefined, {
366
+ pureWrite: true
367
+ });
368
+ // Keep track of last target, so that last call to navigate wins
357
369
  let lastTransitionTarget;
358
- // Transition the location to a new value
359
- const transition = (newIntent, newTarget) => {
360
- if (newTarget.value === reference() && newTarget.state === state())
361
- return;
362
- if (lastTransitionTarget === undefined)
363
- setIsRouting(true);
364
- intent = newIntent;
365
- lastTransitionTarget = newTarget;
366
- startTransition(() => {
367
- if (lastTransitionTarget !== newTarget)
368
- return;
369
- setReference(lastTransitionTarget.value);
370
- setState(lastTransitionTarget.state);
371
- resetErrorBoundaries();
372
- if (!isServer)
373
- submissions[1](subs => subs.filter(s => s.pending));
374
- }).finally(() => {
375
- if (lastTransitionTarget !== newTarget)
376
- return;
377
- // Batch, in order for isRouting and final source update to happen together
378
- batch(() => {
379
- intent = undefined;
380
- if (newIntent === "navigate")
381
- navigateEnd(lastTransitionTarget);
382
- setIsRouting(false);
383
- lastTransitionTarget = undefined;
384
- });
385
- });
386
- };
387
- const [reference, setReference] = createSignal(source().value);
388
- const [state, setState] = createSignal(source().state);
370
+ // source() remains canonical for native history changes; navigateTarget()
371
+ // temporarily overrides it for in-flight programmatic navigation.
372
+ const reference = createMemo(() => {
373
+ const nav = navigateTarget();
374
+ if (nav !== undefined)
375
+ return nav.value;
376
+ return source().value;
377
+ });
378
+ const state = createMemo(() => {
379
+ const nav = navigateTarget();
380
+ if (nav !== undefined)
381
+ return nav.state;
382
+ return source().state;
383
+ });
389
384
  const location = createLocation(reference, state, utils.queryWrapper);
390
385
  const referrers = [];
391
- const submissions = createSignal(isServer ? initFromFlash() : []);
386
+ const submissions = createSignal(isServer ? initFromFlash() : [], {
387
+ pureWrite: true
388
+ });
392
389
  const matches = createMemo(() => {
393
390
  if (typeof options.transformUrl === "function") {
394
391
  return getRouteMatches(branches(), options.transformUrl(location.pathname));
@@ -414,8 +411,6 @@ export function createRouterContext(integration, branches, getContext, options =
414
411
  return resolvePath(basePath, to);
415
412
  }
416
413
  };
417
- // Create a native transition, when source updates
418
- createRenderEffect(on(source, source => transition("native", source), { defer: true }));
419
414
  return {
420
415
  base: baseRoute,
421
416
  location,
@@ -470,17 +465,35 @@ export function createRouterContext(integration, branches, getContext, options =
470
465
  }
471
466
  else if (beforeLeave.confirm(resolvedTo, options)) {
472
467
  referrers.push({ value: current, replace, scroll, state: state() });
473
- transition("navigate", {
468
+ const newTarget = {
474
469
  value: resolvedTo,
475
470
  state: nextState
476
- });
471
+ };
472
+ if (lastTransitionTarget === undefined) {
473
+ setIsRouting(true);
474
+ flush();
475
+ }
476
+ intent = "navigate";
477
+ lastTransitionTarget = newTarget;
478
+ if (lastTransitionTarget === newTarget) {
479
+ setNavigateTarget({ ...lastTransitionTarget });
480
+ queueMicrotask(() => {
481
+ if (lastTransitionTarget !== newTarget)
482
+ return;
483
+ intent = undefined;
484
+ navigateEnd(lastTransitionTarget);
485
+ setNavigateTarget(undefined);
486
+ setIsRouting(false);
487
+ lastTransitionTarget = undefined;
488
+ });
489
+ }
477
490
  }
478
491
  }
479
492
  });
480
493
  }
481
494
  function navigatorFactory(route) {
482
495
  // Workaround for vite issue (https://github.com/vitejs/vite/issues/3803)
483
- route = route || useContext(RouteContextObj) || baseRoute;
496
+ route = route || useOptionalContext(RouteContextObj) || baseRoute;
484
497
  return (to, options) => navigateFromRoute(route, to, options);
485
498
  }
486
499
  function navigateEnd(next) {
@@ -525,7 +538,15 @@ export function createRouterContext(integration, branches, getContext, options =
525
538
  }
526
539
  function initFromFlash() {
527
540
  const e = getRequestEvent();
528
- return (e && e.router && e.router.submission ? [e.router.submission] : []);
541
+ if (!(e && e.router && e.router.submission))
542
+ return [];
543
+ return [
544
+ {
545
+ ...e.router.submission,
546
+ clear() { },
547
+ retry() { }
548
+ }
549
+ ];
529
550
  }
530
551
  }
531
552
  export function createRouteContext(router, parent, outlet, match) {
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Component, JSX, Signal } from "solid-js";
2
- declare module "solid-js/web" {
2
+ declare module "@solidjs/web" {
3
3
  interface RequestEvent {
4
4
  response: {
5
5
  status?: number;
@@ -12,6 +12,7 @@ declare module "solid-js/web" {
12
12
  submission?: {
13
13
  input: any;
14
14
  result: any;
15
+ error?: any;
15
16
  url: string;
16
17
  };
17
18
  dataOnly?: boolean | string[];
@@ -78,8 +79,6 @@ export type RouteDefinition<S extends string | string[] = any, T = unknown> = {
78
79
  children?: RouteDefinition | RouteDefinition[];
79
80
  component?: Component<RouteSectionProps<T>>;
80
81
  info?: Record<string, any>;
81
- /** @deprecated use preload */
82
- load?: RoutePreloadFunc;
83
82
  };
84
83
  export type MatchFilter = readonly string[] | RegExp | ((s: string) => boolean);
85
84
  export type PathParams<P extends string | readonly string[]> = P extends `${infer Head}/${infer Tail}` ? [...PathParams<Head>, ...PathParams<Tail>] : P extends `:${infer S}?` ? [S] : P extends `:${infer S}` ? [S] : P extends `*${infer S}` ? [S] : [];
@@ -166,20 +165,10 @@ export type Submission<T, U> = {
166
165
  readonly input: T;
167
166
  readonly result?: U;
168
167
  readonly error: any;
169
- readonly pending: boolean;
170
168
  readonly url: string;
171
169
  clear: () => void;
172
170
  retry: () => void;
173
171
  };
174
- export type SubmissionStub = {
175
- readonly input: undefined;
176
- readonly result: undefined;
177
- readonly error: undefined;
178
- readonly pending: undefined;
179
- readonly url: undefined;
180
- clear: () => void;
181
- retry: () => void;
182
- };
183
172
  export interface MaybePreloadableComponent extends Component {
184
173
  preload?: () => void;
185
174
  }
@@ -194,7 +183,3 @@ export type CustomResponse<T> = Omit<Response, "clone"> & {
194
183
  customBody: () => T;
195
184
  clone(...args: readonly unknown[]): CustomResponse<T>;
196
185
  };
197
- /** @deprecated */
198
- export type RouteLoadFunc = RoutePreloadFunc;
199
- /** @deprecated */
200
- export type RouteLoadFuncArgs = RoutePreloadFuncArgs;
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "Ryan Turnquist"
7
7
  ],
8
8
  "license": "MIT",
9
- "version": "0.16.1",
9
+ "version": "0.17.0-next.0",
10
10
  "homepage": "https://github.com/solidjs/solid-router#readme",
11
11
  "repository": {
12
12
  "type": "git",
@@ -29,6 +29,7 @@
29
29
  ],
30
30
  "sideEffects": false,
31
31
  "devDependencies": {
32
+ "@solidjs/web": "2.0.0-beta.3",
32
33
  "@babel/core": "^7.26.0",
33
34
  "@babel/preset-typescript": "^7.26.0",
34
35
  "@changesets/cli": "^2.27.10",
@@ -37,18 +38,19 @@
37
38
  "@rollup/plugin-terser": "0.4.4",
38
39
  "@types/jest": "^29.5.14",
39
40
  "@types/node": "^22.10.0",
40
- "babel-preset-solid": "^1.9.3",
41
+ "babel-preset-solid": "2.0.0-beta.3",
41
42
  "jsdom": "^25.0.1",
42
43
  "prettier": "^3.4.1",
43
44
  "rollup": "^4.27.4",
44
- "solid-js": "^1.9.3",
45
+ "solid-js": "2.0.0-beta.3",
45
46
  "typescript": "^5.7.2",
46
47
  "vite": "^6.0.0",
47
- "vite-plugin-solid": "^2.11.0",
48
+ "vite-plugin-solid": "3.0.0-next.2",
48
49
  "vitest": "^2.1.6"
49
50
  },
50
51
  "peerDependencies": {
51
- "solid-js": "^1.8.6"
52
+ "@solidjs/web": "^2.0.0-beta.3",
53
+ "solid-js": "^2.0.0-beta.3"
52
54
  },
53
55
  "scripts": {
54
56
  "build": "rm -rf dist && tsc && rollup -c",
@@ -1,32 +0,0 @@
1
- import { type ReconcileOptions } from "solid-js/store";
2
- /**
3
- * As `createAsync` and `createAsyncStore` are wrappers for `createResource`,
4
- * this type allows to support `latest` field for these primitives.
5
- * It will be removed in the future.
6
- */
7
- export type AccessorWithLatest<T> = {
8
- (): T;
9
- latest: T;
10
- };
11
- export declare function createAsync<T>(fn: (prev: T) => Promise<T>, options: {
12
- name?: string;
13
- initialValue: T;
14
- deferStream?: boolean;
15
- }): AccessorWithLatest<T>;
16
- export declare function createAsync<T>(fn: (prev: T | undefined) => Promise<T>, options?: {
17
- name?: string;
18
- initialValue?: T;
19
- deferStream?: boolean;
20
- }): AccessorWithLatest<T | undefined>;
21
- export declare function createAsyncStore<T>(fn: (prev: T) => Promise<T>, options: {
22
- name?: string;
23
- initialValue: T;
24
- deferStream?: boolean;
25
- reconcile?: ReconcileOptions;
26
- }): AccessorWithLatest<T>;
27
- export declare function createAsyncStore<T>(fn: (prev: T | undefined) => Promise<T>, options?: {
28
- name?: string;
29
- initialValue?: T;
30
- deferStream?: boolean;
31
- reconcile?: ReconcileOptions;
32
- }): AccessorWithLatest<T | undefined>;
@@ -1,96 +0,0 @@
1
- /**
2
- * This is mock of the eventual Solid 2.0 primitive. It is not fully featured.
3
- */
4
- import { createResource, sharedConfig, untrack, catchError } from "solid-js";
5
- import { createStore, reconcile, unwrap } from "solid-js/store";
6
- import { isServer } from "solid-js/web";
7
- import { setFunctionName } from "../utils.js";
8
- export function createAsync(fn, options) {
9
- let resource;
10
- let prev = () => !resource || resource.state === "unresolved" ? undefined : resource.latest;
11
- [resource] = createResource(() => subFetch(fn, catchError(() => untrack(prev), () => undefined)), v => v, options);
12
- const resultAccessor = (() => resource());
13
- if (options?.name)
14
- setFunctionName(resultAccessor, options.name);
15
- Object.defineProperty(resultAccessor, "latest", {
16
- get() {
17
- return resource.latest;
18
- }
19
- });
20
- return resultAccessor;
21
- }
22
- export function createAsyncStore(fn, options = {}) {
23
- let resource;
24
- let prev = () => !resource || resource.state === "unresolved"
25
- ? undefined
26
- : unwrap(resource.latest);
27
- [resource] = createResource(() => subFetch(fn, catchError(() => untrack(prev), () => undefined)), v => v, {
28
- ...options,
29
- storage: (init) => createDeepSignal(init, options.reconcile)
30
- });
31
- const resultAccessor = (() => resource());
32
- Object.defineProperty(resultAccessor, "latest", {
33
- get() {
34
- return resource.latest;
35
- }
36
- });
37
- return resultAccessor;
38
- }
39
- function createDeepSignal(value, options) {
40
- const [store, setStore] = createStore({
41
- value: structuredClone(value)
42
- });
43
- return [
44
- () => store.value,
45
- (v) => {
46
- typeof v === "function" && (v = v());
47
- setStore("value", reconcile(structuredClone(v), options));
48
- return store.value;
49
- }
50
- ];
51
- }
52
- // mock promise while hydrating to prevent fetching
53
- class MockPromise {
54
- static all() {
55
- return new MockPromise();
56
- }
57
- static allSettled() {
58
- return new MockPromise();
59
- }
60
- static any() {
61
- return new MockPromise();
62
- }
63
- static race() {
64
- return new MockPromise();
65
- }
66
- static reject() {
67
- return new MockPromise();
68
- }
69
- static resolve() {
70
- return new MockPromise();
71
- }
72
- catch() {
73
- return new MockPromise();
74
- }
75
- then() {
76
- return new MockPromise();
77
- }
78
- finally() {
79
- return new MockPromise();
80
- }
81
- }
82
- function subFetch(fn, prev) {
83
- if (isServer || !sharedConfig.context)
84
- return fn(prev);
85
- const ogFetch = fetch;
86
- const ogPromise = Promise;
87
- try {
88
- window.fetch = () => new MockPromise();
89
- Promise = MockPromise;
90
- return fn(prev);
91
- }
92
- finally {
93
- window.fetch = ogFetch;
94
- Promise = ogPromise;
95
- }
96
- }