@bleedingdev/modern-js-plugin-tanstack 3.9.0-ultramodern.8 → 3.9.3-ultramodern.1

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.
Files changed (112) hide show
  1. package/dist/cjs/cli/artifacts.js +15 -1
  2. package/dist/cjs/cli/index.js +13 -2
  3. package/dist/cjs/cli/routeMetadata.js +131 -0
  4. package/dist/cjs/cli/rscRouteLayer.js +86 -0
  5. package/dist/cjs/cli/tanstackTypes/canonicalRoutes.js +7 -7
  6. package/dist/cjs/cli/tanstackTypes/shared.js +0 -14
  7. package/dist/cjs/cli/tanstackTypes/source.js +28 -63
  8. package/dist/cjs/runtime/lifecycle.js +0 -3
  9. package/dist/cjs/runtime/loaderBridge.js +10 -4
  10. package/dist/cjs/runtime/navigation.js +102 -0
  11. package/dist/cjs/runtime/plugin.js +10 -1
  12. package/dist/cjs/runtime/plugin.node.js +89 -64
  13. package/dist/cjs/runtime/routeTree/construction.js +19 -61
  14. package/dist/cjs/runtime/routeTree/loaders.js +1 -1
  15. package/dist/cjs/runtime/routeTree/paths.js +0 -6
  16. package/dist/cjs/runtime/ssrManagedTags.js +2 -12
  17. package/dist/cjs/runtime/utils.js +1 -0
  18. package/dist/cjs/shared/routeDescriptor.js +60 -0
  19. package/dist/esm/cli/artifacts.mjs +13 -2
  20. package/dist/esm/cli/index.mjs +13 -11
  21. package/dist/esm/cli/routeMetadata.mjs +71 -0
  22. package/dist/esm/cli/rscRouteLayer.mjs +35 -0
  23. package/dist/esm/cli/tanstackTypes/canonicalRoutes.mjs +7 -7
  24. package/dist/esm/cli/tanstackTypes/shared.mjs +1 -9
  25. package/dist/esm/cli/tanstackTypes/source.mjs +29 -64
  26. package/dist/esm/runtime/lifecycle.mjs +1 -1
  27. package/dist/esm/runtime/loaderBridge.mjs +8 -5
  28. package/dist/esm/runtime/navigation.mjs +64 -0
  29. package/dist/esm/runtime/plugin.mjs +10 -1
  30. package/dist/esm/runtime/plugin.node.mjs +91 -66
  31. package/dist/esm/runtime/routeTree/construction.mjs +19 -61
  32. package/dist/esm/runtime/routeTree/loaders.mjs +2 -2
  33. package/dist/esm/runtime/routeTree/paths.mjs +1 -4
  34. package/dist/esm/runtime/ssrManagedTags.mjs +1 -8
  35. package/dist/esm/runtime/utils.mjs +1 -0
  36. package/dist/esm/shared/routeDescriptor.mjs +22 -0
  37. package/dist/esm-node/cli/artifacts.mjs +13 -2
  38. package/dist/esm-node/cli/index.mjs +13 -11
  39. package/dist/esm-node/cli/routeMetadata.mjs +72 -0
  40. package/dist/esm-node/cli/rscRouteLayer.mjs +36 -0
  41. package/dist/esm-node/cli/tanstackTypes/canonicalRoutes.mjs +7 -7
  42. package/dist/esm-node/cli/tanstackTypes/shared.mjs +1 -9
  43. package/dist/esm-node/cli/tanstackTypes/source.mjs +29 -64
  44. package/dist/esm-node/runtime/lifecycle.mjs +1 -1
  45. package/dist/esm-node/runtime/loaderBridge.mjs +8 -5
  46. package/dist/esm-node/runtime/navigation.mjs +65 -0
  47. package/dist/esm-node/runtime/plugin.mjs +10 -1
  48. package/dist/esm-node/runtime/plugin.node.mjs +91 -66
  49. package/dist/esm-node/runtime/routeTree/construction.mjs +19 -61
  50. package/dist/esm-node/runtime/routeTree/loaders.mjs +2 -2
  51. package/dist/esm-node/runtime/routeTree/paths.mjs +1 -4
  52. package/dist/esm-node/runtime/ssrManagedTags.mjs +1 -8
  53. package/dist/esm-node/runtime/utils.mjs +1 -0
  54. package/dist/esm-node/shared/routeDescriptor.mjs +23 -0
  55. package/dist/types/cli/artifacts.d.ts +16 -7
  56. package/dist/types/cli/index.d.ts +1 -1
  57. package/dist/types/cli/routeMetadata.d.ts +18 -0
  58. package/dist/types/cli/rscRouteLayer.d.ts +11 -0
  59. package/dist/types/cli/tanstackTypes/shared.d.ts +0 -2
  60. package/dist/types/cli/tanstackTypes/source.d.ts +3 -2
  61. package/dist/types/runtime/lifecycle.d.ts +1 -1
  62. package/dist/types/runtime/loaderBridge.d.ts +1 -0
  63. package/dist/types/runtime/navigation.d.ts +4 -0
  64. package/dist/types/runtime/pluginShared.d.ts +5 -0
  65. package/dist/types/runtime/routeTree/paths.d.ts +0 -2
  66. package/dist/types/runtime/ssrManagedTags.d.ts +0 -1
  67. package/dist/types/runtime/ssrTypes.d.ts +0 -7
  68. package/dist/types/shared/routeDescriptor.d.ts +26 -0
  69. package/package.json +32 -26
  70. package/src/cli/artifacts.ts +59 -8
  71. package/src/cli/index.ts +17 -4
  72. package/src/cli/routeMetadata.ts +121 -0
  73. package/src/cli/rscRouteLayer.ts +93 -0
  74. package/src/cli/tanstackTypes/canonicalRoutes.ts +17 -12
  75. package/src/cli/tanstackTypes/shared.ts +0 -14
  76. package/src/cli/tanstackTypes/source.ts +48 -120
  77. package/src/runtime/lifecycle.ts +0 -1
  78. package/src/runtime/loaderBridge.ts +9 -9
  79. package/src/runtime/navigation.ts +78 -0
  80. package/src/runtime/plugin.node.tsx +121 -91
  81. package/src/runtime/plugin.tsx +10 -0
  82. package/src/runtime/pluginShared.ts +12 -0
  83. package/src/runtime/routeTree/construction.ts +32 -112
  84. package/src/runtime/routeTree/loaders.ts +2 -4
  85. package/src/runtime/routeTree/paths.ts +0 -6
  86. package/src/runtime/ssrManagedTags.ts +0 -17
  87. package/src/runtime/ssrTypes.ts +0 -7
  88. package/src/runtime/utils.tsx +1 -0
  89. package/src/shared/routeDescriptor.ts +46 -0
  90. package/dist/esm/rslib-runtime~1.mjs +0 -18
  91. package/dist/esm-node/rslib-runtime~0.mjs +0 -19
  92. package/rslib.config.mts +0 -4
  93. package/rstest.config.mts +0 -53
  94. package/tests/router/cli.test.ts +0 -420
  95. package/tests/router/clientHydration.test.tsx +0 -200
  96. package/tests/router/dataMutation.test.tsx +0 -428
  97. package/tests/router/fixtures/router-gen.oxlint.json +0 -5
  98. package/tests/router/flightSerialization.roundtrip.test.tsx +0 -140
  99. package/tests/router/generateRouteArtifacts.test.ts +0 -146
  100. package/tests/router/loaderBridge.test.ts +0 -207
  101. package/tests/router/localisedIdentity.test.tsx +0 -95
  102. package/tests/router/prefetchLink.test.tsx +0 -122
  103. package/tests/router/prefetchLinkPreload.test.tsx +0 -65
  104. package/tests/router/routeHooks.test.ts +0 -49
  105. package/tests/router/routeTree.test.ts +0 -240
  106. package/tests/router/rsc.test.tsx +0 -480
  107. package/tests/router/serverPlugin.test.ts +0 -157
  108. package/tests/router/slotUsageSanitizer.test.ts +0 -23
  109. package/tests/router/ssrPreload.test.ts +0 -30
  110. package/tests/router/tanstackTypes.test.ts +0 -384
  111. package/tsconfig.json +0 -12
  112. package/tsconfig.tsgo.json +0 -6
@@ -1,122 +0,0 @@
1
- import { createMemoryHistory } from '@tanstack/history';
2
- import {
3
- createRootRoute,
4
- createRoute,
5
- createRouter,
6
- RouterProvider,
7
- } from '@tanstack/react-router';
8
- import { act, render, waitFor } from '@testing-library/react';
9
- import React from 'react';
10
- import { Link, NavLink } from '../../src/runtime/prefetchLink';
11
-
12
- // Real-router harness (no module mocking): STATIC_ACTIVE_PROPS (the
13
- // TanStack behavior this adapter works around) is injected by
14
- // useLinkProps() based on live route match state, which a mocked
15
- // useLinkProps() cannot reproduce faithfully. We tried spying on
16
- // useLinkProps via rstest.mock()+importActual while re-exporting the rest
17
- // of the module untouched, but rstest's module-mock hoisting resolves the
18
- // "actual" import for a module through its own in-progress mock
19
- // registration, so every named export (createRootRoute included) comes
20
- // back undefined -- see the sibling prefetchLinkPreload.test.tsx file for
21
- // the fully-mocked preload-mapping coverage instead. This file renders
22
- // through an actual createRouter()/RouterProvider tree with in-memory
23
- // history so the active/inactive aria-current behavior is exercised for
24
- // real.
25
- function buildRouter(options: {
26
- initialPath: string;
27
- linkProps?: Record<string, unknown>;
28
- useNavLink?: boolean;
29
- children?:
30
- | React.ReactNode
31
- | ((state: { isActive: boolean }) => React.ReactNode);
32
- }) {
33
- // The Link lives on the always-rendered root component (not a route
34
- // that's only matched at "/settings") so the "inactive link" case -
35
- // sitting at "/" while linking to "/settings" - actually mounts it.
36
- const rootRoute = createRootRoute({
37
- component: () => {
38
- const Component = options.useNavLink ? NavLink : Link;
39
- return (
40
- <Component to="/settings" {...(options.linkProps ?? {})}>
41
- {options.children ?? 'Settings'}
42
- </Component>
43
- );
44
- },
45
- });
46
- const homeRoute = createRoute({
47
- getParentRoute: () => rootRoute,
48
- path: '/',
49
- component: () => null,
50
- });
51
- const settingsRoute = createRoute({
52
- getParentRoute: () => rootRoute,
53
- path: '/settings',
54
- component: () => null,
55
- });
56
-
57
- const routeTree = rootRoute.addChildren([homeRoute, settingsRoute]);
58
-
59
- return createRouter({
60
- routeTree,
61
- history: createMemoryHistory({ initialEntries: [options.initialPath] }),
62
- });
63
- }
64
-
65
- async function renderLink(options: Parameters<typeof buildRouter>[0]) {
66
- const router = buildRouter(options);
67
- const utils = render(<RouterProvider router={router} />);
68
-
69
- const anchor = await waitFor(() => {
70
- const found = utils.container.querySelector('a[href="/settings"]');
71
- if (!found) {
72
- throw new Error('anchor not rendered yet');
73
- }
74
- return found as HTMLAnchorElement;
75
- });
76
-
77
- return { anchor, router, ...utils };
78
- }
79
-
80
- describe('tanstack prefetch link adapter - aria-current override', () => {
81
- it('lets caller aria-current="true" win over the TanStack-forced value', async () => {
82
- const { anchor } = await renderLink({
83
- initialPath: '/settings',
84
- linkProps: { 'aria-current': 'true' },
85
- });
86
- expect(anchor.getAttribute('aria-current')).toBe('true');
87
- expect(anchor.outerHTML.match(/aria-current=/g)).toHaveLength(1);
88
- });
89
-
90
- it('suppresses aria-current entirely when caller passes false', async () => {
91
- const { anchor } = await renderLink({
92
- initialPath: '/settings',
93
- linkProps: { 'aria-current': false },
94
- });
95
- expect(anchor.hasAttribute('aria-current')).toBe(false);
96
- });
97
-
98
- it('updates render-prop children across an inactive-to-active transition', async () => {
99
- const { anchor, router } = await renderLink({
100
- initialPath: '/',
101
- children: ({ isActive }: { isActive: boolean }) =>
102
- isActive ? 'Active settings' : 'Settings',
103
- });
104
-
105
- expect(anchor.textContent).toBe('Settings');
106
-
107
- await act(async () => {
108
- await router.navigate({ to: '/settings' });
109
- });
110
-
111
- await waitFor(() => expect(anchor.textContent).toBe('Active settings'));
112
- });
113
-
114
- it('passes the native anchor ref through the router hook', async () => {
115
- const ref = React.createRef<HTMLAnchorElement>();
116
- const { anchor } = await renderLink({
117
- initialPath: '/',
118
- linkProps: { ref, prefetch: 'none' },
119
- });
120
- expect(ref.current).toBe(anchor);
121
- });
122
- });
@@ -1,65 +0,0 @@
1
- import { render } from '@testing-library/react';
2
- import React from 'react';
3
- import { Link } from '../../src/runtime/prefetchLink';
4
-
5
- type CapturedOptions = {
6
- preload?: unknown;
7
- };
8
-
9
- type MockLinkAnchorProps = Record<string, unknown> & {
10
- href?: string;
11
- ref?: unknown;
12
- };
13
-
14
- let capturedOptions: CapturedOptions[] = [];
15
- let mockReturnProps: MockLinkAnchorProps = { href: '/settings' };
16
-
17
- rstest.mock('@tanstack/react-router', () => ({
18
- useLinkProps: (options: CapturedOptions) => {
19
- capturedOptions.push(options);
20
- return mockReturnProps;
21
- },
22
- }));
23
-
24
- describe('tanstack prefetch link adapter - preload mapping', () => {
25
- beforeEach(() => {
26
- capturedOptions = [];
27
- mockReturnProps = { href: '/settings' };
28
- });
29
-
30
- it('preserves an explicit false preload override', () => {
31
- render(
32
- <Link to="/settings" prefetch="render" preload={false}>
33
- Settings
34
- </Link>,
35
- );
36
-
37
- expect(capturedOptions[0]?.preload).toBe(false);
38
- });
39
-
40
- it('defaults to viewport preload when no prefetch is given', () => {
41
- render(<Link to="/settings">Settings</Link>);
42
-
43
- expect(capturedOptions[0]?.preload).toBe('viewport');
44
- });
45
-
46
- it('maps none prefetch to disabled TanStack preload', () => {
47
- render(
48
- <Link to="/settings" prefetch="none">
49
- Settings
50
- </Link>,
51
- );
52
-
53
- expect(capturedOptions[0]?.preload).toBe(false);
54
- });
55
-
56
- it('lets explicit preload re-enable prefetch=none', () => {
57
- render(
58
- <Link to="/settings" prefetch="none" preload="render">
59
- Settings
60
- </Link>,
61
- );
62
-
63
- expect(capturedOptions.map(o => o.preload)).toEqual(['render']);
64
- });
65
- });
@@ -1,49 +0,0 @@
1
- import { createMemoryHistory } from '@tanstack/history';
2
- import {
3
- createRootRoute,
4
- createRoute,
5
- createRouter,
6
- RouterProvider,
7
- } from '@tanstack/react-router';
8
- import React from 'react';
9
- import { renderToString } from 'react-dom/server';
10
- import { useMatches } from '../../src/runtime/routeHooks';
11
-
12
- function MatchIds() {
13
- const matches = useMatches();
14
- return React.createElement(
15
- 'output',
16
- { 'data-testid': 'route-ids' },
17
- matches.map(match => match.routeId).join(','),
18
- );
19
- }
20
-
21
- test('updates whole match objects after navigation with the structural-sharing guard', async () => {
22
- const rootRoute = createRootRoute({ component: MatchIds });
23
- const homeRoute = createRoute({
24
- getParentRoute: () => rootRoute,
25
- path: '/',
26
- component: () => null,
27
- });
28
- const settingsRoute = createRoute({
29
- getParentRoute: () => rootRoute,
30
- path: '/settings',
31
- component: () => null,
32
- });
33
- const router = createRouter({
34
- routeTree: rootRoute.addChildren([homeRoute, settingsRoute]),
35
- history: createMemoryHistory({ initialEntries: ['/'] }),
36
- defaultStructuralSharing: true,
37
- });
38
-
39
- await router.load();
40
- expect(
41
- renderToString(React.createElement(RouterProvider, { router })),
42
- ).toContain('__root__');
43
-
44
- await router.navigate({ to: '/settings' });
45
-
46
- expect(
47
- renderToString(React.createElement(RouterProvider, { router })),
48
- ).toContain('/settings');
49
- });
@@ -1,240 +0,0 @@
1
- import type { RouteObject } from '@modern-js/runtime-utils/router';
2
- import { createMemoryHistory } from '@tanstack/history';
3
- import {
4
- createRouter,
5
- Outlet,
6
- RouterProvider,
7
- redirect,
8
- } from '@tanstack/react-router';
9
- import type { ComponentType } from 'react';
10
- import { createElement, lazy } from 'react';
11
- import { renderToStaticMarkup, renderToString } from 'react-dom/server';
12
- import { Outlet as ModernOutlet } from '../../src/runtime/outlet';
13
- import { createRouteTreeFromRouteObjects } from '../../src/runtime/routeTree';
14
-
15
- type LoaderArgs = { params: Record<string, string> };
16
-
17
- type TestRouteObject = RouteObject & {
18
- children?: TestRouteObject[];
19
- lazyImport?: () => Promise<unknown>;
20
- };
21
-
22
- type TestRouter = {
23
- _serverResult?:
24
- | { type: 'redirect'; redirect: Response }
25
- | { type: 'render'; status: number };
26
- state: {
27
- matches: Array<{ error?: unknown; loaderData?: unknown; routeId: string }>;
28
- };
29
- };
30
-
31
- const nullComponent = () => null;
32
-
33
- const root = (
34
- children: TestRouteObject[],
35
- Component: RouteObject['Component'] = nullComponent,
36
- ): TestRouteObject[] => [{ id: 'root', path: '/', Component, children }];
37
-
38
- async function loadRouteTree(
39
- routes: TestRouteObject[],
40
- pathname: string,
41
- ): Promise<TestRouter> {
42
- const router = createRouter({
43
- routeTree: createRouteTreeFromRouteObjects(routes as RouteObject[]),
44
- history: createMemoryHistory({ initialEntries: [pathname] }),
45
- context: {
46
- request: new Request(`http://localhost${pathname}`),
47
- requestContext: {},
48
- },
49
- });
50
-
51
- await router.load();
52
- return router as unknown as TestRouter;
53
- }
54
-
55
- const matchOf = (router: TestRouter, routeId: string) =>
56
- router.state.matches.find(match => match.routeId === routeId);
57
-
58
- describe('tanstack route tree from RouteObject[]', () => {
59
- test('resolves loader data for root, dynamic and splat params', async () => {
60
- const routes: TestRouteObject[] = root(
61
- [
62
- {
63
- id: 'user',
64
- path: 'user/:id',
65
- loader: ({ params }: LoaderArgs) => ({ id: params.id }),
66
- Component: nullComponent,
67
- },
68
- {
69
- id: 'files',
70
- path: 'files/*',
71
- loader: ({ params }: LoaderArgs) => ({ value: params['*'] }),
72
- Component: nullComponent,
73
- },
74
- ],
75
- nullComponent,
76
- );
77
- routes[0].loader = () => ({ root: 'ok' });
78
-
79
- const userRouter = await loadRouteTree(routes, '/user/123');
80
- expect(matchOf(userRouter, '__root__')?.loaderData).toEqual({ root: 'ok' });
81
- expect(matchOf(userRouter, '/user/$id')?.loaderData).toEqual({ id: '123' });
82
-
83
- const splatRouter = await loadRouteTree(routes, '/files/a/b/c');
84
- expect(matchOf(splatRouter, '/files/$')?.loaderData).toEqual({
85
- value: 'a/b/c',
86
- });
87
- });
88
-
89
- test('keeps unnamed sibling pathless layouts distinct', async () => {
90
- const pathless = (path: string): TestRouteObject => ({
91
- Component: nullComponent,
92
- children: [
93
- { path, loader: () => ({ value: path }), Component: nullComponent },
94
- ],
95
- });
96
-
97
- const router = await loadRouteTree(
98
- [pathless('alpha'), pathless('beta')],
99
- '/beta',
100
- );
101
- const loaderData = router.state.matches
102
- .map(match => match.loaderData)
103
- .filter(Boolean);
104
-
105
- expect(loaderData).toContainEqual({ value: 'beta' });
106
- expect(loaderData).not.toContainEqual({ value: 'alpha' });
107
- });
108
-
109
- test('reports native TanStack unknown routes as HTTP 404', async () => {
110
- const router = await loadRouteTree(
111
- root([{ id: 'known', path: 'known', Component: nullComponent }]),
112
- '/missing',
113
- );
114
-
115
- expect(router._serverResult).toMatchObject({ type: 'render', status: 404 });
116
- });
117
-
118
- test('reports native TanStack loader redirects as server responses', async () => {
119
- const router = await loadRouteTree(
120
- root([
121
- {
122
- id: 'redirect',
123
- path: 'redirect',
124
- loader: () => {
125
- throw redirect({ to: '/target' });
126
- },
127
- Component: nullComponent,
128
- },
129
- { id: 'target', path: 'target', Component: nullComponent },
130
- ]),
131
- '/redirect',
132
- );
133
-
134
- if (router._serverResult?.type !== 'redirect') {
135
- throw new Error('Expected a TanStack redirect server result');
136
- }
137
- expect(router._serverResult.redirect.status).toBe(307);
138
- expect(router._serverResult.redirect.headers.get('Location')).toBe(
139
- '/target',
140
- );
141
- });
142
-
143
- test('renders Modern Outlet through TanStack native outlet', async () => {
144
- const router = await loadRouteTree(
145
- root(
146
- [
147
- {
148
- id: 'plain',
149
- path: 'plain',
150
- Component: () => createElement('main', null, 'Plain child route'),
151
- },
152
- ],
153
- () => createElement('section', null, createElement(ModernOutlet)),
154
- ),
155
- '/plain',
156
- );
157
-
158
- expect(
159
- renderToString(createElement(RouterProvider, { router } as never)),
160
- ).toContain('Plain child route');
161
- });
162
-
163
- for (const [shape, wrap] of [
164
- ['flat', (Component: ComponentType) => ({ default: Component })],
165
- [
166
- 'nested',
167
- (Component: ComponentType) => ({ default: { default: Component } }),
168
- ],
169
- ] as const) {
170
- test(`server renders lazy child routes from ${shape} ESM module defaults`, async () => {
171
- const LazyRouteComponent = () =>
172
- createElement('main', null, 'Lazy child route ready');
173
- const lazyImport = () => Promise.resolve(wrap(LazyRouteComponent));
174
- const router = await loadRouteTree(
175
- root(
176
- [
177
- {
178
- id: 'lazy',
179
- path: 'lazy',
180
- Component: lazy(
181
- lazyImport as () => Promise<{ default: ComponentType }>,
182
- ),
183
- lazyImport,
184
- },
185
- ],
186
- () => createElement('section', null, createElement(Outlet)),
187
- ),
188
- '/lazy',
189
- );
190
-
191
- expect(
192
- renderToStaticMarkup(
193
- createElement(RouterProvider, { router } as never),
194
- ),
195
- ).toContain('Lazy child route ready');
196
- });
197
- }
198
-
199
- test('normalizes Modern deferred loader data for TanStack SSR', async () => {
200
- const router = await loadRouteTree(
201
- root([
202
- {
203
- id: 'deferred',
204
- path: 'deferred',
205
- loader: () => ({
206
- __modern_deferred: true,
207
- data: { immediate: 'ok', later: Promise.resolve('done') },
208
- }),
209
- Component: nullComponent,
210
- },
211
- ]),
212
- '/deferred',
213
- );
214
- const loaderData = matchOf(router, '/deferred')?.loaderData as
215
- | { immediate: string; later: Promise<string> }
216
- | undefined;
217
-
218
- expect(loaderData?.immediate).toBe('ok');
219
- await expect(loaderData?.later).resolves.toBe('done');
220
- });
221
-
222
- test('preserves returned non-404 Response loaders as loader data', async () => {
223
- const response = new Response('route status payload', { status: 500 });
224
- const router = await loadRouteTree(
225
- root([
226
- {
227
- id: 'broken',
228
- path: 'broken',
229
- loader: () => response,
230
- Component: nullComponent,
231
- },
232
- ]),
233
- '/broken',
234
- );
235
-
236
- expect(router._serverResult).toMatchObject({ type: 'render', status: 200 });
237
- expect(matchOf(router, '/broken')?.loaderData).toBe(response);
238
- expect(matchOf(router, '/broken')?.error).toBeUndefined();
239
- });
240
- });