@alchemy.run/sigil 0.0.0-alpha.4 → 0.0.0-alpha.6
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/README.md +21 -9
- package/THIRD_PARTY_NOTICES.md +39 -1
- package/dist/Text-BobFKi74.d.ts +452 -0
- package/dist/ansi.d.ts +122 -131
- package/dist/ansi.js +86 -6
- package/dist/capabilities.d.ts +5 -0
- package/dist/capabilities.js +3 -0
- package/dist/cell-_ZVhbfl0.js +44 -0
- package/dist/color-CkbalRqK.js +2 -0
- package/dist/color-policy-BMzMwV7Q.d.ts +22 -0
- package/dist/color-policy-SVj1pYTA.js +560 -0
- package/dist/color-profile-DHhQHY55.js +36 -0
- package/dist/color-profile-u0Nhe9Nv.d.ts +97 -0
- package/dist/color.d.ts +21 -0
- package/dist/color.js +3 -0
- package/dist/cursor-position-D2LAkRG0.d.ts +7 -0
- package/dist/detect-Bh4yGP6w.d.ts +186 -0
- package/dist/detect-BuTXtY6e.js +373 -0
- package/dist/env-YVw64yZS.js +9 -0
- package/dist/escapes-CB_6CWOE.d.ts +72 -0
- package/dist/geometry-BxXOzJgo.d.ts +11 -0
- package/dist/index-DZ88EXJv.d.ts +21 -0
- package/dist/index.d.ts +143 -498
- package/dist/index.js +1026 -2244
- package/dist/osc-CCH7xDoS.js +71 -0
- package/dist/osc-Cn0fw77g.d.ts +23 -0
- package/dist/paint-C19minOS.d.ts +81 -0
- package/dist/query-vaIeGOkH.d.ts +152 -0
- package/dist/router.d.ts +392 -0
- package/dist/router.js +709 -0
- package/dist/sample-Cqw1bjUL.js +445 -0
- package/dist/screen-BOSLQ8fF.d.ts +49 -0
- package/dist/screen-CiPytswf.js +342 -0
- package/dist/screen.d.ts +5 -0
- package/dist/screen.js +5 -0
- package/dist/semantic-text-style-DIMzC7xt.js +91 -0
- package/dist/serialize-BTkAZgw1.js +79 -0
- package/dist/session-aZr9O8h3.js +665 -0
- package/dist/sgr-BhwaWAJB.js +246 -0
- package/dist/store-CgrG9K4y.d.ts +72 -0
- package/dist/string-width-CijQwpIk.js +69 -0
- package/dist/strip-BvU4toXG.js +6 -0
- package/dist/terminal.d.ts +118 -0
- package/dist/terminal.js +2 -0
- package/dist/tokenize-AjqbvtiT.js +1242 -0
- package/dist/tokenize-Dx1y_l5H.d.ts +57 -0
- package/dist/truncate-D31fhU6i.js +562 -0
- package/dist/use-focus-BzqAJi0n.js +1337 -0
- package/package.json +41 -9
- package/src/ansi/chalk.ts +5 -3
- package/src/ansi/escapes.ts +14 -0
- package/src/ansi/graphemes.ts +8 -0
- package/src/ansi/hyperlink.ts +44 -0
- package/src/ansi/index.ts +3 -1
- package/src/ansi/osc.ts +77 -0
- package/src/ansi/tokenize.ts +3 -4
- package/src/capabilities/color-policy.ts +34 -0
- package/src/capabilities/detect.ts +594 -0
- package/src/capabilities/index.ts +37 -0
- package/src/capabilities/query.ts +657 -0
- package/src/capabilities/store.ts +379 -0
- package/src/color/index.ts +3 -0
- package/src/color/paint.ts +169 -0
- package/src/color/palette.ts +48 -0
- package/src/color/sample.ts +323 -0
- package/src/color.ts +1 -0
- package/src/components/AnsiText.tsx +42 -0
- package/src/components/App.tsx +98 -10
- package/src/components/BackgroundContext.ts +2 -3
- package/src/components/Box.tsx +0 -8
- package/src/components/CursorContext.ts +1 -1
- package/src/components/Hyperlink.tsx +56 -0
- package/src/components/TerminalOscContext.ts +25 -0
- package/src/components/Text.tsx +22 -45
- package/src/components/Transform.tsx +1 -1
- package/src/dom.ts +11 -2
- package/src/global.d.ts +3 -0
- package/src/hooks/use-capabilities.ts +73 -0
- package/src/hooks/use-cursor.ts +2 -2
- package/src/hooks/use-terminal-osc.ts +59 -0
- package/src/index.ts +45 -1
- package/src/ink.tsx +213 -153
- package/src/{render-node-to-output.ts → paint-tree.ts} +75 -48
- package/src/reconciler.ts +24 -3
- package/src/render-background.ts +36 -15
- package/src/render-border.ts +94 -61
- package/src/render-frame.ts +83 -0
- package/src/render-to-string.ts +21 -6
- package/src/render.ts +15 -6
- package/src/router/components.tsx +343 -0
- package/src/router/context.ts +41 -0
- package/src/router/history.ts +194 -0
- package/src/router/hooks.tsx +391 -0
- package/src/router/index.ts +34 -0
- package/src/router/matcher.ts +571 -0
- package/src/screen/ansi.ts +184 -0
- package/src/screen/canvas.ts +160 -0
- package/src/screen/cell.ts +138 -0
- package/src/screen/color-profile.ts +47 -0
- package/src/screen/geometry.ts +9 -0
- package/src/screen/index.ts +6 -0
- package/src/screen/screen.ts +305 -0
- package/src/screen/serialize.ts +129 -0
- package/src/screen.ts +1 -0
- package/src/semantic-text-style.ts +118 -0
- package/src/squash-text-nodes.ts +2 -5
- package/src/structured-text.ts +325 -0
- package/src/styles.ts +19 -14
- package/src/terminal/index.ts +2 -0
- package/src/terminal/inline-presenter.ts +120 -0
- package/src/terminal/input.ts +86 -0
- package/src/terminal/render-scheduler.ts +37 -0
- package/src/terminal/screen-presenter.ts +188 -0
- package/src/terminal/session.ts +407 -0
- package/src/terminal.ts +1 -0
- package/src/testing/browser.ts +588 -0
- package/src/testing/emulators.ts +205 -0
- package/src/testing/explorer-app/index.html +12 -0
- package/src/testing/explorer-app/main.ts +381 -0
- package/src/testing/explorer-app/style.css +194 -0
- package/src/testing/explorer-app/tsconfig.json +15 -0
- package/src/testing/explorer-app/vite-env.d.ts +1 -0
- package/src/testing/index.ts +26 -0
- package/src/testing/keys.ts +56 -0
- package/src/testing/live.ts +85 -0
- package/src/testing/matchers.ts +70 -0
- package/src/testing/public.ts +94 -0
- package/src/testing/terminal.ts +349 -0
- package/src/testing/vitest.ts +157 -0
- package/src/transform-adapter.ts +14 -0
- package/src/wrap-text.ts +4 -0
- package/dist/sgr-CMfEpjSk.d.ts +0 -91
- package/dist/truncate-Cr6xVFMa.js +0 -2330
- package/src/ansi/supports-color.ts +0 -207
- package/src/colorize.ts +0 -60
- package/src/log-update.ts +0 -370
- package/src/output.ts +0 -308
- package/src/renderer.ts +0 -73
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/** @jsxImportSource react */
|
|
2
|
+
// Ported from react-router's declarative-mode hooks (lib/hooks.tsx, MIT),
|
|
3
|
+
// with the data-router branches removed.
|
|
4
|
+
import {
|
|
5
|
+
useCallback,
|
|
6
|
+
useContext,
|
|
7
|
+
useLayoutEffect,
|
|
8
|
+
useMemo,
|
|
9
|
+
useRef,
|
|
10
|
+
type ReactElement,
|
|
11
|
+
} from "react";
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
LocationContext,
|
|
15
|
+
NavigationContext,
|
|
16
|
+
OutletContext,
|
|
17
|
+
RouteContext,
|
|
18
|
+
} from "#/router/context.ts";
|
|
19
|
+
import {
|
|
20
|
+
parsePath,
|
|
21
|
+
type Location,
|
|
22
|
+
type NavigationType,
|
|
23
|
+
type Path,
|
|
24
|
+
type To,
|
|
25
|
+
} from "#/router/history.ts";
|
|
26
|
+
import {
|
|
27
|
+
getResolveToMatches,
|
|
28
|
+
joinPaths,
|
|
29
|
+
matchPath,
|
|
30
|
+
matchRoutes,
|
|
31
|
+
resolveTo,
|
|
32
|
+
type Params,
|
|
33
|
+
type PathMatch,
|
|
34
|
+
type PathPattern,
|
|
35
|
+
type RouteMatch,
|
|
36
|
+
type RouteObject,
|
|
37
|
+
} from "#/router/matcher.ts";
|
|
38
|
+
|
|
39
|
+
const warned = new Set<string>();
|
|
40
|
+
const warnOnce = (key: string, message: string): void => {
|
|
41
|
+
if (!warned.has(key)) {
|
|
42
|
+
warned.add(key);
|
|
43
|
+
console.warn(message);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
Returns `true` when rendered inside a `<MemoryRouter>`. Useful for components
|
|
49
|
+
that optionally integrate with routing.
|
|
50
|
+
*/
|
|
51
|
+
export const useInRouterContext = (): boolean => useContext(LocationContext) != null;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
Returns the current `Location`.
|
|
55
|
+
*/
|
|
56
|
+
export const useLocation = (): Location => {
|
|
57
|
+
const locationContext = useContext(LocationContext);
|
|
58
|
+
if (!locationContext) {
|
|
59
|
+
throw new Error("useLocation() may be used only in the context of a <MemoryRouter> component.");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return locationContext.location;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
Returns the type of navigation that produced the current location: `"POP"`,
|
|
67
|
+
`"PUSH"`, or `"REPLACE"`.
|
|
68
|
+
*/
|
|
69
|
+
export const useNavigationType = (): NavigationType => {
|
|
70
|
+
const locationContext = useContext(LocationContext);
|
|
71
|
+
if (!locationContext) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
"useNavigationType() may be used only in the context of a <MemoryRouter> component.",
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return locationContext.navigationType;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const useNavigationContext = (hookName: string) => {
|
|
81
|
+
const navigationContext = useContext(NavigationContext);
|
|
82
|
+
if (!navigationContext) {
|
|
83
|
+
throw new Error(`${hookName} may be used only in the context of a <MemoryRouter> component.`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return navigationContext;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export type NavigateOptions = {
|
|
90
|
+
/**
|
|
91
|
+
Replace the current entry in the navigation stack instead of pushing a new
|
|
92
|
+
one.
|
|
93
|
+
*/
|
|
94
|
+
replace?: boolean;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
Arbitrary state to attach to the destination location, readable via
|
|
98
|
+
`useLocation().state`. Held in memory, never serialized.
|
|
99
|
+
*/
|
|
100
|
+
state?: unknown;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
Navigates to a destination, or through the navigation stack when passed a
|
|
105
|
+
number (`navigate(-1)` goes back).
|
|
106
|
+
*/
|
|
107
|
+
export type NavigateFunction = {
|
|
108
|
+
(to: To, options?: NavigateOptions): void;
|
|
109
|
+
(delta: number): void;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
Returns a stable function for imperative navigation.
|
|
114
|
+
*/
|
|
115
|
+
export const useNavigate = (): NavigateFunction => {
|
|
116
|
+
const { navigator } = useNavigationContext("useNavigate()");
|
|
117
|
+
const { matches } = useContext(RouteContext);
|
|
118
|
+
const { pathname: locationPathname } = useLocation();
|
|
119
|
+
|
|
120
|
+
const routePathnamesJson = JSON.stringify(getResolveToMatches(matches));
|
|
121
|
+
|
|
122
|
+
// Navigating before the component has mounted (i.e. during render) is a
|
|
123
|
+
// side effect in render; ignore it. Use <Navigate> or an effect instead.
|
|
124
|
+
const activeRef = useRef(false);
|
|
125
|
+
useLayoutEffect(() => {
|
|
126
|
+
activeRef.current = true;
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return useCallback(
|
|
130
|
+
(to: To | number, options: NavigateOptions = {}) => {
|
|
131
|
+
if (!activeRef.current) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (typeof to === "number") {
|
|
136
|
+
navigator.go(to);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const path = resolveTo(to, JSON.parse(routePathnamesJson) as string[], locationPathname);
|
|
141
|
+
(options.replace ? navigator.replace : navigator.push)(path, options.state);
|
|
142
|
+
},
|
|
143
|
+
[navigator, routePathnamesJson, locationPathname],
|
|
144
|
+
);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
Returns whether the navigation stack has entries behind/ahead of the current
|
|
149
|
+
one — i.e. whether `navigate(-1)` / `navigate(1)` will move anywhere. Handy
|
|
150
|
+
for "Esc goes back, unless at root" bindings.
|
|
151
|
+
*/
|
|
152
|
+
export const useNavigationStack = (): { canGoBack: boolean; canGoForward: boolean } => {
|
|
153
|
+
const { navigator } = useNavigationContext("useNavigationStack()");
|
|
154
|
+
// Reading location subscribes this component to navigation changes, so the
|
|
155
|
+
// returned booleans stay fresh.
|
|
156
|
+
useLocation();
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
canGoBack: navigator.canGoBack(),
|
|
160
|
+
canGoForward: navigator.canGoForward(),
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
Returns the params from all dynamic segments matched by the current route and
|
|
166
|
+
its ancestors.
|
|
167
|
+
*/
|
|
168
|
+
export const useParams = <
|
|
169
|
+
ParamsOrKey extends Record<string, string | undefined> | string = string,
|
|
170
|
+
>(): Readonly<[ParamsOrKey] extends [string] ? Params<ParamsOrKey> : Partial<ParamsOrKey>> => {
|
|
171
|
+
const { matches } = useContext(RouteContext);
|
|
172
|
+
const routeMatch = matches[matches.length - 1];
|
|
173
|
+
return (routeMatch ? routeMatch.params : {}) as Readonly<
|
|
174
|
+
[ParamsOrKey] extends [string] ? Params<ParamsOrKey> : Partial<ParamsOrKey>
|
|
175
|
+
>;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
Matches a path pattern against the current location's pathname. Returns the
|
|
180
|
+
match (with params) or `null`.
|
|
181
|
+
*/
|
|
182
|
+
export const useMatch = <ParamKey extends string = string>(
|
|
183
|
+
pattern: PathPattern | string,
|
|
184
|
+
): PathMatch<ParamKey> | null => {
|
|
185
|
+
const { pathname } = useLocation();
|
|
186
|
+
return matchPath<ParamKey>(pattern, pathname);
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
Resolves a `To` value against the current route, exactly as `useNavigate`
|
|
191
|
+
would. Useful for building navigation UI.
|
|
192
|
+
*/
|
|
193
|
+
export const useResolvedPath = (to: To): Path => {
|
|
194
|
+
const { matches } = useContext(RouteContext);
|
|
195
|
+
const { pathname: locationPathname } = useLocation();
|
|
196
|
+
return resolveTo(to, getResolveToMatches(matches), locationPathname);
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
Returns the element for the child route at this level of the route hierarchy,
|
|
201
|
+
or `null` if there is none. Used internally by `<Outlet>`.
|
|
202
|
+
*/
|
|
203
|
+
export const useOutlet = (context?: unknown): ReactElement | null => {
|
|
204
|
+
const { outlet } = useContext(RouteContext);
|
|
205
|
+
if (outlet) {
|
|
206
|
+
return <OutletContext.Provider value={context}>{outlet}</OutletContext.Provider>;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return outlet;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
Returns the value passed to the nearest parent `<Outlet context={...}>`.
|
|
214
|
+
*/
|
|
215
|
+
export const useOutletContext = <Context = unknown,>(): Context =>
|
|
216
|
+
useContext(OutletContext) as Context;
|
|
217
|
+
|
|
218
|
+
type SearchParamsInit = string | string[][] | Record<string, string | string[]> | URLSearchParams;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
Creates a `URLSearchParams` from common initializer shapes, including
|
|
222
|
+
`{ key: ["a", "b"] }` for repeated keys.
|
|
223
|
+
*/
|
|
224
|
+
export const createSearchParams = (init: SearchParamsInit = ""): URLSearchParams =>
|
|
225
|
+
new URLSearchParams(
|
|
226
|
+
typeof init === "string" || Array.isArray(init) || init instanceof URLSearchParams
|
|
227
|
+
? init
|
|
228
|
+
: Object.keys(init).flatMap((key) => {
|
|
229
|
+
const value = init[key];
|
|
230
|
+
return Array.isArray(value)
|
|
231
|
+
? value.map((v) => [key, v] as [string, string])
|
|
232
|
+
: [[key, value] as [string, string]];
|
|
233
|
+
}),
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
type SetSearchParams = (
|
|
237
|
+
nextInit: SearchParamsInit | ((prev: URLSearchParams) => SearchParamsInit),
|
|
238
|
+
navigateOptions?: NavigateOptions,
|
|
239
|
+
) => void;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
Returns the current location's search params and a setter that navigates to
|
|
243
|
+
the same pathname with the new params.
|
|
244
|
+
*/
|
|
245
|
+
export const useSearchParams = (
|
|
246
|
+
defaultInit?: SearchParamsInit,
|
|
247
|
+
): [URLSearchParams, SetSearchParams] => {
|
|
248
|
+
const defaultSearchParamsRef = useRef(createSearchParams(defaultInit));
|
|
249
|
+
const hasSetSearchParamsRef = useRef(false);
|
|
250
|
+
|
|
251
|
+
const location = useLocation();
|
|
252
|
+
const searchParams = useMemo(() => {
|
|
253
|
+
const params = createSearchParams(location.search);
|
|
254
|
+
|
|
255
|
+
// Fill in defaults only until the setter is first used, so cleared params
|
|
256
|
+
// don't resurrect their default values.
|
|
257
|
+
if (!hasSetSearchParamsRef.current) {
|
|
258
|
+
for (const key of defaultSearchParamsRef.current.keys()) {
|
|
259
|
+
if (!params.has(key)) {
|
|
260
|
+
for (const value of defaultSearchParamsRef.current.getAll(key)) {
|
|
261
|
+
params.append(key, value);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return params;
|
|
268
|
+
}, [location.search]);
|
|
269
|
+
|
|
270
|
+
const navigate = useNavigate();
|
|
271
|
+
const setSearchParams = useCallback<SetSearchParams>(
|
|
272
|
+
(nextInit, navigateOptions) => {
|
|
273
|
+
const newSearchParams = createSearchParams(
|
|
274
|
+
typeof nextInit === "function" ? nextInit(new URLSearchParams(searchParams)) : nextInit,
|
|
275
|
+
);
|
|
276
|
+
hasSetSearchParamsRef.current = true;
|
|
277
|
+
navigate(`?${newSearchParams.toString()}`, navigateOptions);
|
|
278
|
+
},
|
|
279
|
+
[navigate, searchParams],
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
return [searchParams, setSearchParams];
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
Matches a set of route objects against the current location (or an override)
|
|
287
|
+
and returns the rendered element tree. The plain-object alternative to
|
|
288
|
+
`<Routes>`.
|
|
289
|
+
*/
|
|
290
|
+
export const useRoutes = (
|
|
291
|
+
routes: RouteObject[],
|
|
292
|
+
locationArg?: Partial<Location> | string,
|
|
293
|
+
): ReactElement | null => {
|
|
294
|
+
if (!useInRouterContext()) {
|
|
295
|
+
throw new Error("useRoutes() may be used only in the context of a <MemoryRouter> component.");
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const { matches: parentMatches } = useContext(RouteContext);
|
|
299
|
+
const routeMatch = parentMatches[parentMatches.length - 1];
|
|
300
|
+
const parentParams = routeMatch ? routeMatch.params : {};
|
|
301
|
+
const parentPathnameBase = routeMatch ? routeMatch.pathnameBase : "/";
|
|
302
|
+
const parentRoute = routeMatch?.route;
|
|
303
|
+
|
|
304
|
+
const locationFromContext = useLocation();
|
|
305
|
+
|
|
306
|
+
let location;
|
|
307
|
+
if (locationArg) {
|
|
308
|
+
const parsedLocationArg =
|
|
309
|
+
typeof locationArg === "string" ? parsePath(locationArg) : locationArg;
|
|
310
|
+
|
|
311
|
+
if (parentPathnameBase !== "/" && !parsedLocationArg.pathname?.startsWith(parentPathnameBase)) {
|
|
312
|
+
throw new Error(
|
|
313
|
+
`When overriding the location using \`<Routes location>\` or \`useRoutes(routes, location)\`, ` +
|
|
314
|
+
`the location pathname must begin with the portion of the pathname that was matched by ` +
|
|
315
|
+
`all parent routes. The current pathname base is "${parentPathnameBase}" but pathname ` +
|
|
316
|
+
`"${parsedLocationArg.pathname}" was given in the \`location\` prop.`,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
location = parsedLocationArg;
|
|
321
|
+
} else {
|
|
322
|
+
location = locationFromContext;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const pathname = location.pathname ?? "/";
|
|
326
|
+
|
|
327
|
+
let remainingPathname = pathname;
|
|
328
|
+
if (parentPathnameBase !== "/") {
|
|
329
|
+
const parentSegments = parentPathnameBase.replace(/^\//, "").split("/");
|
|
330
|
+
const segments = pathname.replace(/^\//, "").split("/");
|
|
331
|
+
remainingPathname = `/${segments.slice(parentSegments.length).join("/")}`;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const matches = matchRoutes(routes, { pathname: remainingPathname });
|
|
335
|
+
|
|
336
|
+
// Descendant <Routes> under a parent route without a trailing "*" can never
|
|
337
|
+
// see deeper paths — the parent stops matching as soon as the location goes
|
|
338
|
+
// past its own path.
|
|
339
|
+
const parentPath = parentRoute?.path ?? "";
|
|
340
|
+
if (parentRoute && !parentPath.endsWith("*")) {
|
|
341
|
+
warnOnce(
|
|
342
|
+
`descendant-routes:${routeMatch.pathname}`,
|
|
343
|
+
`You rendered descendant <Routes> (or called useRoutes()) at "${routeMatch.pathname}" ` +
|
|
344
|
+
`(under <Route path="${parentPath}">) but the parent route path has no trailing "*". ` +
|
|
345
|
+
`This means if you navigate deeper, the parent won't match anymore and therefore the ` +
|
|
346
|
+
`child routes will never render. Please change the parent to <Route path="${
|
|
347
|
+
parentPath === "/" ? "*" : `${parentPath}/*`
|
|
348
|
+
}">.`,
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (matches == null && !parentRoute) {
|
|
353
|
+
warnOnce(
|
|
354
|
+
`no-match:${pathname}${location.search ?? ""}`,
|
|
355
|
+
`No routes matched location "${pathname}${location.search ?? ""}".`,
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return renderMatches(
|
|
360
|
+
matches &&
|
|
361
|
+
matches.map((match) => ({
|
|
362
|
+
...match,
|
|
363
|
+
params: { ...parentParams, ...match.params },
|
|
364
|
+
pathname: joinPaths([parentPathnameBase, match.pathname]),
|
|
365
|
+
pathnameBase:
|
|
366
|
+
match.pathnameBase === "/"
|
|
367
|
+
? parentPathnameBase
|
|
368
|
+
: joinPaths([parentPathnameBase, match.pathnameBase]),
|
|
369
|
+
})),
|
|
370
|
+
parentMatches,
|
|
371
|
+
);
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
function renderMatches(
|
|
375
|
+
matches: RouteMatch[] | null,
|
|
376
|
+
parentMatches: RouteMatch[],
|
|
377
|
+
): ReactElement | null {
|
|
378
|
+
if (matches == null) {
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return matches.reduceRight<ReactElement | null>((outlet, match, index) => {
|
|
383
|
+
const matchesUpToHere = parentMatches.concat(matches.slice(0, index + 1));
|
|
384
|
+
|
|
385
|
+
return (
|
|
386
|
+
<RouteContext.Provider value={{ outlet, matches: matchesUpToHere }}>
|
|
387
|
+
{match.route.element ?? outlet}
|
|
388
|
+
</RouteContext.Provider>
|
|
389
|
+
);
|
|
390
|
+
}, null);
|
|
391
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// A purpose-built in-memory router for terminal apps. The matching engine and
|
|
2
|
+
// component model are ported from react-router's declarative mode (MIT), with
|
|
3
|
+
// everything URL- and DOM-specific removed: no basename, no hash, no percent
|
|
4
|
+
// encoding, no data APIs. Route paths are screen states, not URLs.
|
|
5
|
+
export type { InitialEntry, Location, NavigationType, Path, To } from "#/router/history.ts";
|
|
6
|
+
export { createPath, parsePath } from "#/router/history.ts";
|
|
7
|
+
export type { Params, PathMatch, PathPattern, RouteMatch, RouteObject } from "#/router/matcher.ts";
|
|
8
|
+
export { generatePath, matchPath, matchRoutes, resolvePath } from "#/router/matcher.ts";
|
|
9
|
+
export type { Navigator } from "#/router/context.ts";
|
|
10
|
+
export type { NavigateFunction, NavigateOptions } from "#/router/hooks.tsx";
|
|
11
|
+
export {
|
|
12
|
+
createSearchParams,
|
|
13
|
+
useInRouterContext,
|
|
14
|
+
useLocation,
|
|
15
|
+
useMatch,
|
|
16
|
+
useNavigate,
|
|
17
|
+
useNavigationStack,
|
|
18
|
+
useNavigationType,
|
|
19
|
+
useOutlet,
|
|
20
|
+
useOutletContext,
|
|
21
|
+
useParams,
|
|
22
|
+
useResolvedPath,
|
|
23
|
+
useRoutes,
|
|
24
|
+
useSearchParams,
|
|
25
|
+
} from "#/router/hooks.tsx";
|
|
26
|
+
export type {
|
|
27
|
+
LinkProps,
|
|
28
|
+
MemoryRouterProps,
|
|
29
|
+
NavigateProps,
|
|
30
|
+
OutletProps,
|
|
31
|
+
RouteProps,
|
|
32
|
+
RoutesProps,
|
|
33
|
+
} from "#/router/components.tsx";
|
|
34
|
+
export { Link, MemoryRouter, Navigate, Outlet, Route, Routes } from "#/router/components.tsx";
|