@solidjs/router 0.16.0 → 0.16.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 (65) hide show
  1. package/dist/data/action.js +6 -5
  2. package/dist/data/createAsync.js +3 -0
  3. package/dist/data/events.d.ts +8 -1
  4. package/dist/data/events.js +2 -2
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.jsx +1 -1
  7. package/dist/routers/HashRouter.js +1 -1
  8. package/dist/routers/MemoryRouter.js +1 -1
  9. package/dist/routers/Router.js +1 -1
  10. package/dist/routing.d.ts +2 -1
  11. package/dist/routing.js +1 -0
  12. package/dist/types.d.ts +1 -1
  13. package/dist/utils.d.ts +1 -0
  14. package/dist/utils.js +8 -0
  15. package/package.json +2 -2
  16. package/dist/src/components.d.ts +0 -31
  17. package/dist/src/components.jsx +0 -39
  18. package/dist/src/data/action.d.ts +0 -17
  19. package/dist/src/data/action.js +0 -163
  20. package/dist/src/data/action.spec.d.ts +0 -1
  21. package/dist/src/data/action.spec.js +0 -297
  22. package/dist/src/data/createAsync.d.ts +0 -32
  23. package/dist/src/data/createAsync.js +0 -96
  24. package/dist/src/data/createAsync.spec.d.ts +0 -1
  25. package/dist/src/data/createAsync.spec.js +0 -196
  26. package/dist/src/data/events.d.ts +0 -9
  27. package/dist/src/data/events.js +0 -123
  28. package/dist/src/data/events.spec.d.ts +0 -1
  29. package/dist/src/data/events.spec.js +0 -567
  30. package/dist/src/data/index.d.ts +0 -4
  31. package/dist/src/data/index.js +0 -4
  32. package/dist/src/data/query.d.ts +0 -23
  33. package/dist/src/data/query.js +0 -232
  34. package/dist/src/data/query.spec.d.ts +0 -1
  35. package/dist/src/data/query.spec.js +0 -354
  36. package/dist/src/data/response.d.ts +0 -4
  37. package/dist/src/data/response.js +0 -42
  38. package/dist/src/data/response.spec.d.ts +0 -1
  39. package/dist/src/data/response.spec.js +0 -165
  40. package/dist/src/index.d.ts +0 -7
  41. package/dist/src/index.jsx +0 -6
  42. package/dist/src/lifecycle.d.ts +0 -5
  43. package/dist/src/lifecycle.js +0 -69
  44. package/dist/src/routers/HashRouter.d.ts +0 -9
  45. package/dist/src/routers/HashRouter.js +0 -41
  46. package/dist/src/routers/MemoryRouter.d.ts +0 -24
  47. package/dist/src/routers/MemoryRouter.js +0 -57
  48. package/dist/src/routers/Router.d.ts +0 -9
  49. package/dist/src/routers/Router.js +0 -45
  50. package/dist/src/routers/StaticRouter.d.ts +0 -6
  51. package/dist/src/routers/StaticRouter.js +0 -15
  52. package/dist/src/routers/components.d.ts +0 -27
  53. package/dist/src/routers/components.jsx +0 -118
  54. package/dist/src/routers/createRouter.d.ts +0 -10
  55. package/dist/src/routers/createRouter.js +0 -41
  56. package/dist/src/routers/index.d.ts +0 -11
  57. package/dist/src/routers/index.js +0 -6
  58. package/dist/src/routing.d.ts +0 -175
  59. package/dist/src/routing.js +0 -560
  60. package/dist/src/types.d.ts +0 -200
  61. package/dist/src/types.js +0 -1
  62. package/dist/src/utils.d.ts +0 -13
  63. package/dist/src/utils.js +0 -185
  64. package/dist/test/helpers.d.ts +0 -6
  65. package/dist/test/helpers.js +0 -50
@@ -1,560 +0,0 @@
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";
4
- import { createBeforeLeave } from "./lifecycle.js";
5
- import { mockBase, createMemoObject, extractSearchParams, invariant, resolvePath, createMatcher, joinPaths, scoreRoute, mergeSearchString, expandOptionals } from "./utils.js";
6
- const MAX_REDIRECTS = 100;
7
- /** Consider this API opaque and internal. It is likely to change in the future. */
8
- export const RouterContextObj = createContext();
9
- export const RouteContextObj = createContext();
10
- export const useRouter = () => invariant(useContext(RouterContextObj), "<A> and 'use' router primitives can be only used inside a Route.");
11
- let TempRoute;
12
- export const useRoute = () => TempRoute || useContext(RouteContextObj) || useRouter().base;
13
- export const useResolvedPath = (path) => {
14
- const route = useRoute();
15
- return createMemo(() => route.resolvePath(path()));
16
- };
17
- export const useHref = (to) => {
18
- const router = useRouter();
19
- return createMemo(() => {
20
- const to_ = to();
21
- return to_ !== undefined ? router.renderPath(to_) : to_;
22
- });
23
- };
24
- /**
25
- * Retrieves method to do navigation. The method accepts a path to navigate to and an optional object with the following options:
26
- *
27
- * - resolve (*boolean*, default `true`): resolve the path against the current route
28
- * - replace (*boolean*, default `false`): replace the history entry
29
- * - scroll (*boolean*, default `true`): scroll to top after navigation
30
- * - state (*any*, default `undefined`): pass custom state to `location.state`
31
- *
32
- * **Note**: The state is serialized using the structured clone algorithm which does not support all object types.
33
- *
34
- * @example
35
- * ```js
36
- * const navigate = useNavigate();
37
- *
38
- * if (unauthorized) {
39
- * navigate("/login", { replace: true });
40
- * }
41
- * ```
42
- */
43
- export const useNavigate = () => useRouter().navigatorFactory();
44
- /**
45
- * Retrieves reactive `location` object useful for getting things like `pathname`.
46
- *
47
- * @example
48
- * ```js
49
- * const location = useLocation();
50
- *
51
- * const pathname = createMemo(() => parsePath(location.pathname));
52
- * ```
53
- */
54
- export const useLocation = () => useRouter().location;
55
- /**
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.
58
- *
59
- * @example
60
- * ```js
61
- * const isRouting = useIsRouting();
62
- *
63
- * return (
64
- * <div classList={{ "grey-out": isRouting() }}>
65
- * <MyAwesomeContent />
66
- * </div>
67
- * );
68
- * ```
69
- */
70
- export const useIsRouting = () => useRouter().isRouting;
71
- /**
72
- * usePreloadRoute returns a function that can be used to preload a route manual.
73
- * This is what happens automatically with link hovering and similar focus based behavior, but it is available here as an API.
74
- *
75
- * @example
76
- * ```js
77
- * const preload = usePreloadRoute();
78
- *
79
- * preload(`/users/settings`, { preloadData: true });
80
- * ```
81
- */
82
- export const usePreloadRoute = () => {
83
- const pre = useRouter().preloadRoute;
84
- return (url, options = {}) => pre(url instanceof URL ? url : new URL(url, mockBase), options.preloadData);
85
- };
86
- /**
87
- * `useMatch` takes an accessor that returns the path and creates a `Memo` that returns match information if the current path matches the provided path.
88
- * Useful for determining if a given path matches the current route.
89
- *
90
- * @example
91
- * ```js
92
- * const match = useMatch(() => props.href);
93
- *
94
- * return <div classList={{ active: Boolean(match()) }} />;
95
- * ```
96
- */
97
- export const useMatch = (path, matchFilters) => {
98
- const location = useLocation();
99
- const matchers = createMemo(() => expandOptionals(path()).map(path => createMatcher(path, undefined, matchFilters)));
100
- return createMemo(() => {
101
- for (const matcher of matchers()) {
102
- const match = matcher(location.pathname);
103
- if (match)
104
- return match;
105
- }
106
- });
107
- };
108
- /**
109
- * `useCurrentMatches` returns all the matches for the current matched route.
110
- * Useful for getting all the route information.
111
- *
112
- * @example
113
- * ```js
114
- * const matches = useCurrentMatches();
115
- *
116
- * const breadcrumbs = createMemo(() => matches().map(m => m.route.info.breadcrumb))
117
- * ```
118
- */
119
- export const useCurrentMatches = () => useRouter().matches;
120
- /**
121
- * Retrieves a reactive, store-like object containing the current route path parameters as defined in the Route.
122
- *
123
- * @example
124
- * ```js
125
- * const params = useParams();
126
- *
127
- * // fetch user based on the id path parameter
128
- * const [user] = createResource(() => params.id, fetchUser);
129
- * ```
130
- */
131
- export const useParams = () => useRouter().params;
132
- /**
133
- * Retrieves a tuple containing a reactive object to read the current location's query parameters and a method to update them.
134
- * The object is a proxy so you must access properties to subscribe to reactive updates.
135
- * **Note** that values will be strings and property names will retain their casing.
136
- *
137
- * The setter method accepts an object whose entries will be merged into the current query string.
138
- * Values `''`, `undefined` and `null` will remove the key from the resulting query string.
139
- * Updates will behave just like a navigation and the setter accepts the same optional second parameter as `navigate` and auto-scrolling is disabled by default.
140
- *
141
- * @examples
142
- * ```js
143
- * const [searchParams, setSearchParams] = useSearchParams();
144
- *
145
- * return (
146
- * <div>
147
- * <span>Page: {searchParams.page}</span>
148
- * <button
149
- * onClick={() =>
150
- * setSearchParams({ page: (parseInt(searchParams.page) || 0) + 1 })
151
- * }
152
- * >
153
- * Next Page
154
- * </button>
155
- * </div>
156
- * );
157
- * ```
158
- */
159
- export const useSearchParams = () => {
160
- const location = useLocation();
161
- const navigate = useNavigate();
162
- const setSearchParams = (params, options) => {
163
- const searchString = untrack(() => mergeSearchString(location.search, params) + location.hash);
164
- navigate(searchString, {
165
- scroll: false,
166
- resolve: false,
167
- ...options
168
- });
169
- };
170
- return [location.query, setSearchParams];
171
- };
172
- /**
173
- * useBeforeLeave takes a function that will be called prior to leaving a route.
174
- * The function will be called with:
175
- *
176
- * - from (*Location*): current location (before change).
177
- * - to (*string | number*): path passed to `navigate`.
178
- * - options (*NavigateOptions*): options passed to navigate.
179
- * - preventDefault (*function*): call to block the route change.
180
- * - defaultPrevented (*readonly boolean*): `true` if any previously called leave handlers called `preventDefault`.
181
- * - retry (*function*, force?: boolean ): call to retry the same navigation, perhaps after confirming with the user. Pass `true` to skip running the leave handlers again (i.e. force navigate without confirming).
182
- *
183
- * @example
184
- * ```js
185
- * useBeforeLeave((e: BeforeLeaveEventArgs) => {
186
- * if (form.isDirty && !e.defaultPrevented) {
187
- * // preventDefault to block immediately and prompt user async
188
- * e.preventDefault();
189
- * setTimeout(() => {
190
- * if (window.confirm("Discard unsaved changes - are you sure?")) {
191
- * // user wants to proceed anyway so retry with force=true
192
- * e.retry(true);
193
- * }
194
- * }, 100);
195
- * }
196
- * });
197
- * ```
198
- */
199
- export const useBeforeLeave = (listener) => {
200
- const s = useRouter().beforeLeave.subscribe({
201
- listener,
202
- location: useLocation(),
203
- navigate: useNavigate()
204
- });
205
- onCleanup(s);
206
- };
207
- export function createRoutes(routeDef, base = "") {
208
- const { component, preload, load, children, info } = routeDef;
209
- const isLeaf = !children || (Array.isArray(children) && !children.length);
210
- const shared = {
211
- key: routeDef,
212
- component,
213
- preload: preload || load,
214
- info
215
- };
216
- return asArray(routeDef.path).reduce((acc, originalPath) => {
217
- for (const expandedPath of expandOptionals(originalPath)) {
218
- const path = joinPaths(base, expandedPath);
219
- let pattern = isLeaf ? path : path.split("/*", 1)[0];
220
- pattern = pattern
221
- .split("/")
222
- .map((s) => {
223
- return s.startsWith(":") || s.startsWith("*") ? s : encodeURIComponent(s);
224
- })
225
- .join("/");
226
- acc.push({
227
- ...shared,
228
- originalPath,
229
- pattern,
230
- matcher: createMatcher(pattern, !isLeaf, routeDef.matchFilters)
231
- });
232
- }
233
- return acc;
234
- }, []);
235
- }
236
- export function createBranch(routes, index = 0) {
237
- return {
238
- routes,
239
- score: scoreRoute(routes[routes.length - 1]) * 10000 - index,
240
- matcher(location) {
241
- const matches = [];
242
- for (let i = routes.length - 1; i >= 0; i--) {
243
- const route = routes[i];
244
- const match = route.matcher(location);
245
- if (!match) {
246
- return null;
247
- }
248
- matches.unshift({
249
- ...match,
250
- route
251
- });
252
- }
253
- return matches;
254
- }
255
- };
256
- }
257
- function asArray(value) {
258
- return Array.isArray(value) ? value : [value];
259
- }
260
- export function createBranches(routeDef, base = "", stack = [], branches = []) {
261
- const routeDefs = asArray(routeDef);
262
- for (let i = 0, len = routeDefs.length; i < len; i++) {
263
- const def = routeDefs[i];
264
- if (def && typeof def === "object") {
265
- if (!def.hasOwnProperty("path"))
266
- def.path = "";
267
- const routes = createRoutes(def, base);
268
- for (const route of routes) {
269
- stack.push(route);
270
- const isEmptyArray = Array.isArray(def.children) && def.children.length === 0;
271
- if (def.children && !isEmptyArray) {
272
- createBranches(def.children, route.pattern, stack, branches);
273
- }
274
- else {
275
- const branch = createBranch([...stack], branches.length);
276
- branches.push(branch);
277
- }
278
- stack.pop();
279
- }
280
- }
281
- }
282
- // Stack will be empty on final return
283
- return stack.length ? branches : branches.sort((a, b) => b.score - a.score);
284
- }
285
- export function getRouteMatches(branches, location) {
286
- for (let i = 0, len = branches.length; i < len; i++) {
287
- const match = branches[i].matcher(location);
288
- if (match) {
289
- return match;
290
- }
291
- }
292
- return [];
293
- }
294
- function createLocation(path, state, queryWrapper) {
295
- const origin = new URL(mockBase);
296
- const url = createMemo(prev => {
297
- const path_ = path();
298
- try {
299
- return new URL(path_, origin);
300
- }
301
- catch (err) {
302
- console.error(`Invalid path ${path_}`);
303
- return prev;
304
- }
305
- }, origin, {
306
- equals: (a, b) => a.href === b.href
307
- });
308
- const pathname = createMemo(() => url().pathname);
309
- const search = createMemo(() => url().search, true);
310
- const hash = createMemo(() => url().hash);
311
- const key = () => "";
312
- const queryFn = on(search, () => extractSearchParams(url()));
313
- return {
314
- get pathname() {
315
- return pathname();
316
- },
317
- get search() {
318
- return search();
319
- },
320
- get hash() {
321
- return hash();
322
- },
323
- get state() {
324
- return state();
325
- },
326
- get key() {
327
- return key();
328
- },
329
- query: queryWrapper ? queryWrapper(queryFn) : createMemoObject(queryFn)
330
- };
331
- }
332
- let intent;
333
- export function getIntent() {
334
- return intent;
335
- }
336
- let inPreloadFn = false;
337
- export function getInPreloadFn() {
338
- return inPreloadFn;
339
- }
340
- export function setInPreloadFn(value) {
341
- inPreloadFn = value;
342
- }
343
- export function createRouterContext(integration, branches, getContext, options = {}) {
344
- const { signal: [source, setSource], utils = {} } = integration;
345
- const parsePath = utils.parsePath || (p => p);
346
- const renderPath = utils.renderPath || (p => p);
347
- const beforeLeave = utils.beforeLeave || createBeforeLeave();
348
- const basePath = resolvePath("", options.base || "");
349
- if (basePath === undefined) {
350
- throw new Error(`${basePath} is not a valid base path`);
351
- }
352
- else if (basePath && !source().value) {
353
- setSource({ value: basePath, replace: true, scroll: false });
354
- }
355
- const [isRouting, setIsRouting] = createSignal(false);
356
- // Keep track of last target, so that last call to transition wins
357
- 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);
389
- const location = createLocation(reference, state, utils.queryWrapper);
390
- const referrers = [];
391
- const submissions = createSignal(isServer ? initFromFlash() : []);
392
- const matches = createMemo(() => {
393
- if (typeof options.transformUrl === "function") {
394
- return getRouteMatches(branches(), options.transformUrl(location.pathname));
395
- }
396
- return getRouteMatches(branches(), location.pathname);
397
- });
398
- const buildParams = () => {
399
- const m = matches();
400
- const params = {};
401
- for (let i = 0; i < m.length; i++) {
402
- Object.assign(params, m[i].params);
403
- }
404
- return params;
405
- };
406
- const params = utils.paramsWrapper
407
- ? utils.paramsWrapper(buildParams, branches)
408
- : createMemoObject(buildParams);
409
- const baseRoute = {
410
- pattern: basePath,
411
- path: () => basePath,
412
- outlet: () => null,
413
- resolvePath(to) {
414
- return resolvePath(basePath, to);
415
- }
416
- };
417
- // Create a native transition, when source updates
418
- createRenderEffect(on(source, source => transition("native", source), { defer: true }));
419
- return {
420
- base: baseRoute,
421
- location,
422
- params,
423
- isRouting,
424
- renderPath,
425
- parsePath,
426
- navigatorFactory,
427
- matches,
428
- beforeLeave,
429
- preloadRoute,
430
- singleFlight: options.singleFlight === undefined ? true : options.singleFlight,
431
- submissions
432
- };
433
- function navigateFromRoute(route, to, options) {
434
- // Untrack in case someone navigates in an effect - don't want to track `reference` or route paths
435
- untrack(() => {
436
- if (typeof to === "number") {
437
- if (!to) {
438
- // A delta of 0 means stay at the current location, so it is ignored
439
- }
440
- else if (utils.go) {
441
- utils.go(to);
442
- }
443
- else {
444
- console.warn("Router integration does not support relative routing");
445
- }
446
- return;
447
- }
448
- const queryOnly = !to || to[0] === "?";
449
- const { replace, resolve, scroll, state: nextState } = {
450
- replace: false,
451
- resolve: !queryOnly,
452
- scroll: true,
453
- ...options
454
- };
455
- const resolvedTo = resolve
456
- ? route.resolvePath(to)
457
- : resolvePath((queryOnly && location.pathname) || "", to);
458
- if (resolvedTo === undefined) {
459
- throw new Error(`Path '${to}' is not a routable path`);
460
- }
461
- else if (referrers.length >= MAX_REDIRECTS) {
462
- throw new Error("Too many redirects");
463
- }
464
- const current = reference();
465
- if (resolvedTo !== current || nextState !== state()) {
466
- if (isServer) {
467
- const e = getRequestEvent();
468
- e && (e.response = { status: 302, headers: new Headers({ Location: resolvedTo }) });
469
- setSource({ value: resolvedTo, replace, scroll, state: nextState });
470
- }
471
- else if (beforeLeave.confirm(resolvedTo, options)) {
472
- referrers.push({ value: current, replace, scroll, state: state() });
473
- transition("navigate", {
474
- value: resolvedTo,
475
- state: nextState
476
- });
477
- }
478
- }
479
- });
480
- }
481
- function navigatorFactory(route) {
482
- // Workaround for vite issue (https://github.com/vitejs/vite/issues/3803)
483
- route = route || useContext(RouteContextObj) || baseRoute;
484
- return (to, options) => navigateFromRoute(route, to, options);
485
- }
486
- function navigateEnd(next) {
487
- const first = referrers[0];
488
- if (first) {
489
- setSource({
490
- ...next,
491
- replace: first.replace,
492
- scroll: first.scroll
493
- });
494
- referrers.length = 0;
495
- }
496
- }
497
- function preloadRoute(url, preloadData) {
498
- const matches = getRouteMatches(branches(), url.pathname);
499
- const prevIntent = intent;
500
- intent = "preload";
501
- for (let match in matches) {
502
- const { route, params } = matches[match];
503
- route.component &&
504
- route.component.preload &&
505
- route.component.preload();
506
- const { preload } = route;
507
- inPreloadFn = true;
508
- preloadData &&
509
- preload &&
510
- runWithOwner(getContext(), () => preload({
511
- params,
512
- location: {
513
- pathname: url.pathname,
514
- search: url.search,
515
- hash: url.hash,
516
- query: extractSearchParams(url),
517
- state: null,
518
- key: ""
519
- },
520
- intent: "preload"
521
- }));
522
- inPreloadFn = false;
523
- }
524
- intent = prevIntent;
525
- }
526
- function initFromFlash() {
527
- const e = getRequestEvent();
528
- return (e && e.router && e.router.submission ? [e.router.submission] : []);
529
- }
530
- }
531
- export function createRouteContext(router, parent, outlet, match) {
532
- const { base, location, params } = router;
533
- const { pattern, component, preload } = match().route;
534
- const path = createMemo(() => match().path);
535
- component &&
536
- component.preload &&
537
- component.preload();
538
- inPreloadFn = true;
539
- const data = preload ? preload({ params, location, intent: intent || "initial" }) : undefined;
540
- inPreloadFn = false;
541
- const route = {
542
- parent,
543
- pattern,
544
- path,
545
- outlet: () => component
546
- ? createComponent(component, {
547
- params,
548
- location,
549
- data,
550
- get children() {
551
- return outlet();
552
- }
553
- })
554
- : outlet(),
555
- resolvePath(to) {
556
- return resolvePath(base.path(), to, path());
557
- }
558
- };
559
- return route;
560
- }