@solidjs/router 0.13.2 → 0.13.3

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 CHANGED
@@ -30,6 +30,7 @@ It supports all of Solid's SSR methods and has Solid's transitions baked in, so
30
30
  - [useSearchParams](#usesearchparams)
31
31
  - [useIsRouting](#useisrouting)
32
32
  - [useMatch](#usematch)
33
+ - [useCurrentMatches](#useCurrentMatches)
33
34
  - [useBeforeLeave](#usebeforeleave)
34
35
  - [SPAs in Deployed Environments](#spas-in-deployed-environments)
35
36
 
@@ -429,7 +430,7 @@ Keep in mind these are completely optional. To use but showcase the power of our
429
430
  To prevent duplicate fetching and to trigger handle refetching we provide a cache api. That takes a function and returns the same function.
430
431
 
431
432
  ```jsx
432
- const getUser = cache((id) => {
433
+ const getUser = cache(async (id) => {
433
434
  return (await fetch(`/api/users${id}`)).json()
434
435
  }, "users") // used as cache key + serialized arguments
435
436
  ```
@@ -860,6 +861,16 @@ const match = useMatch(() => props.href);
860
861
  return <div classList={{ active: Boolean(match()) }} />;
861
862
  ```
862
863
 
864
+ ### useCurrentMatches
865
+
866
+ `useCurrentMatches` returns all the matches for the current matched route. Useful for getting all the route information.
867
+
868
+ For example if you stored breadcrumbs on your route definition you could retrieve them like so:
869
+ ```js
870
+ const matches = useCurrentMatches();
871
+ const breadcrumbs = createMemo(() => matches.map(m => m.route.info.breadcrumb))
872
+ ```
873
+
863
874
  ### useBeforeLeave
864
875
 
865
876
  `useBeforeLeave` takes a function that will be called prior to leaving a route. The function will be called with:
@@ -104,23 +104,18 @@ function toAction(fn, url) {
104
104
  const hashString = (s) => s.split("").reduce((a, b) => ((a << 5) - a + b.charCodeAt(0)) | 0, 0);
105
105
  async function handleResponse(response, error, navigate) {
106
106
  let data;
107
+ let custom;
107
108
  let keys;
108
- let invalidateKeys;
109
+ let flightKeys;
109
110
  if (response instanceof Response) {
110
111
  if (response.headers.has("X-Revalidate"))
111
- keys = invalidateKeys = response.headers.get("X-Revalidate").split(",");
112
+ keys = response.headers.get("X-Revalidate").split(",");
112
113
  if (response.customBody) {
113
- data = await response.customBody();
114
+ data = custom = await response.customBody();
114
115
  if (response.headers.has("X-Single-Flight")) {
115
- keys || (keys = []);
116
- invalidateKeys || (invalidateKeys = []);
117
- Object.keys(data).forEach(key => {
118
- if (key === "_$value")
119
- return;
120
- keys.push(key);
121
- cache.set(key, data[key]);
122
- });
123
116
  data = data._$value;
117
+ delete custom._$value;
118
+ flightKeys = Object.keys(custom);
124
119
  }
125
120
  }
126
121
  if (response.headers.has("Location")) {
@@ -138,7 +133,9 @@ async function handleResponse(response, error, navigate) {
138
133
  else
139
134
  data = response;
140
135
  // invalidate
141
- cacheKeyOp(invalidateKeys, entry => (entry[0] = 0));
136
+ cacheKeyOp(keys, entry => (entry[0] = 0));
137
+ // set cache
138
+ flightKeys && flightKeys.forEach(k => cache.set(k, custom[k]));
142
139
  // trigger revalidation
143
140
  await revalidate(keys, false);
144
141
  return data != null ? { data } : undefined;
@@ -1,6 +1,6 @@
1
1
  import { createSignal, getListener, getOwner, onCleanup, sharedConfig, startTransition } from "solid-js";
2
2
  import { getRequestEvent, isServer } from "solid-js/web";
3
- import { useNavigate, getIntent } from "../routing.js";
3
+ import { useNavigate, getIntent, getInLoadFn } from "../routing.js";
4
4
  const LocationHeader = "Location";
5
5
  const PRELOAD_TIMEOUT = 5000;
6
6
  const CACHE_TIMEOUT = 180000;
@@ -47,6 +47,7 @@ export function cache(fn, name) {
47
47
  const cachedFn = ((...args) => {
48
48
  const cache = getCache();
49
49
  const intent = getIntent();
50
+ const inLoadFn = getInLoadFn();
50
51
  const owner = getOwner();
51
52
  const navigate = owner ? useNavigate() : undefined;
52
53
  const now = Date.now();
@@ -86,13 +87,15 @@ export function cache(fn, name) {
86
87
  cached[0] = now;
87
88
  }
88
89
  let res = cached[1];
89
- if (intent !== "preload") {
90
+ if (!inLoadFn) {
90
91
  res =
91
92
  "then" in cached[1]
92
93
  ? cached[1].then(handleResponse(false), handleResponse(true))
93
94
  : handleResponse(false)(cached[1]);
94
95
  !isServer && intent === "navigate" && startTransition(() => cached[3][1](cached[0])); // update version
95
96
  }
97
+ else
98
+ "then" in res && res.catch(() => { });
96
99
  return res;
97
100
  }
98
101
  let res = !isServer && sharedConfig.context && sharedConfig.has(key)
@@ -117,12 +120,14 @@ export function cache(fn, name) {
117
120
  if (e && e.router.dataOnly)
118
121
  return (e.router.data[key] = res);
119
122
  }
120
- if (intent !== "preload") {
123
+ if (!inLoadFn) {
121
124
  res =
122
125
  "then" in res
123
126
  ? res.then(handleResponse(false), handleResponse(true))
124
127
  : handleResponse(false)(res);
125
128
  }
129
+ else
130
+ "then" in res && res.catch(() => { });
126
131
  // serialize on server
127
132
  if (isServer &&
128
133
  sharedConfig.context &&
@@ -1,2 +1,2 @@
1
1
  import type { RouterContext } from "../types.js";
2
- export declare function setupNativeEvents(preload?: boolean, explicitLinks?: boolean, actionBase?: string): (router: RouterContext) => void;
2
+ export declare function setupNativeEvents(preload?: boolean, explicitLinks?: boolean, actionBase?: string, transformUrl?: (url: string) => string): (router: RouterContext) => void;
@@ -2,7 +2,7 @@ import { delegateEvents } from "solid-js/web";
2
2
  import { onCleanup } from "solid-js";
3
3
  import { actions } from "./action.js";
4
4
  import { mockBase } from "../utils.js";
5
- export function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "/_server") {
5
+ export function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "/_server", transformUrl) {
6
6
  return (router) => {
7
7
  const basePath = router.base.path();
8
8
  const navigateFromRoute = router.navigatorFactory(router.base);
@@ -57,6 +57,9 @@ export function setupNativeEvents(preload = true, explicitLinks = false, actionB
57
57
  if (!res)
58
58
  return;
59
59
  const [a, url] = res;
60
+ if (typeof transformUrl === "function") {
61
+ url.pathname = transformUrl(url.pathname);
62
+ }
60
63
  if (!preloadTimeout[url.pathname])
61
64
  router.preloadRoute(url, a.getAttribute("preload") !== "false");
62
65
  }
@@ -65,6 +68,9 @@ export function setupNativeEvents(preload = true, explicitLinks = false, actionB
65
68
  if (!res)
66
69
  return;
67
70
  const [a, url] = res;
71
+ if (typeof transformUrl === "function") {
72
+ url.pathname = transformUrl(url.pathname);
73
+ }
68
74
  if (preloadTimeout[url.pathname])
69
75
  return;
70
76
  preloadTimeout[url.pathname] = setTimeout(() => {
@@ -77,6 +83,9 @@ export function setupNativeEvents(preload = true, explicitLinks = false, actionB
77
83
  if (!res)
78
84
  return;
79
85
  const [, url] = res;
86
+ if (typeof transformUrl === "function") {
87
+ url.pathname = transformUrl(url.pathname);
88
+ }
80
89
  if (preloadTimeout[url.pathname]) {
81
90
  clearTimeout(preloadTimeout[url.pathname]);
82
91
  delete preloadTimeout[url.pathname];
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export * from "./routers/index.js";
2
2
  export * from "./components.jsx";
3
3
  export * from "./lifecycle.js";
4
- export { useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing.js";
4
+ export { useHref, useIsRouting, useLocation, useMatch, useCurrentMatches, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing.js";
5
5
  export { mergeSearchString as _mergeSearchString } from "./utils.js";
6
6
  export * from "./data/index.js";
7
7
  export type { Location, LocationChange, NavigateOptions, Navigator, OutputMatch, Params, RouteSectionProps, RouteLoadFunc, RouteLoadFuncArgs, RouteDefinition, RouterIntegration, RouterUtils, SetParams, BeforeLeaveEventArgs } from "./types.js";
package/dist/index.js CHANGED
@@ -256,6 +256,7 @@ const useMatch = (path, matchFilters) => {
256
256
  }
257
257
  });
258
258
  };
259
+ const useCurrentMatches = () => useRouter().matches();
259
260
  const useParams = () => useRouter().params;
260
261
  const useSearchParams = () => {
261
262
  const location = useLocation();
@@ -406,6 +407,13 @@ let intent;
406
407
  function getIntent() {
407
408
  return intent;
408
409
  }
410
+ let inLoadFn = false;
411
+ function getInLoadFn() {
412
+ return inLoadFn;
413
+ }
414
+ function setInLoadFn(value) {
415
+ inLoadFn = value;
416
+ }
409
417
  function createRouterContext(integration, branches, getContext, options = {}) {
410
418
  const {
411
419
  signal: [source, setSource],
@@ -438,7 +446,12 @@ function createRouterContext(integration, branches, getContext, options = {}) {
438
446
  const location = createLocation(reference, state);
439
447
  const referrers = [];
440
448
  const submissions = createSignal(isServer ? initFromFlash() : []);
441
- const matches = createMemo(() => getRouteMatches(branches(), location.pathname));
449
+ const matches = createMemo(() => {
450
+ if (typeof options.transformUrl === "function") {
451
+ return getRouteMatches(branches(), options.transformUrl(location.pathname));
452
+ }
453
+ return getRouteMatches(branches(), location.pathname);
454
+ });
442
455
  const params = createMemoObject(() => {
443
456
  const m = matches();
444
457
  const params = {};
@@ -462,17 +475,15 @@ function createRouterContext(integration, branches, getContext, options = {}) {
462
475
  } = source();
463
476
  // Untrack this whole block so `start` doesn't cause Solid's Listener to be preserved
464
477
  untrack(() => {
465
- if (value !== reference()) {
466
- start(() => {
467
- intent = "native";
468
- setReference(value);
469
- setState(state);
470
- resetErrorBoundaries();
471
- submissions[1]([]);
472
- }).then(() => {
473
- intent = undefined;
474
- });
475
- }
478
+ start(() => {
479
+ intent = "native";
480
+ if (value !== reference()) setReference(value);
481
+ setState(state);
482
+ resetErrorBoundaries();
483
+ submissions[1]([]);
484
+ }).then(() => {
485
+ intent = undefined;
486
+ });
476
487
  });
477
488
  });
478
489
  return {
@@ -590,6 +601,7 @@ function createRouterContext(integration, branches, getContext, options = {}) {
590
601
  const {
591
602
  load
592
603
  } = route;
604
+ inLoadFn = true;
593
605
  preloadData && load && runWithOwner(getContext(), () => load({
594
606
  params,
595
607
  location: {
@@ -602,6 +614,7 @@ function createRouterContext(integration, branches, getContext, options = {}) {
602
614
  },
603
615
  intent: "preload"
604
616
  }));
617
+ inLoadFn = false;
605
618
  }
606
619
  intent = prevIntent;
607
620
  }
@@ -623,11 +636,13 @@ function createRouteContext(router, parent, outlet, match) {
623
636
  } = match().route;
624
637
  const path = createMemo(() => match().path);
625
638
  component && component.preload && component.preload();
639
+ inLoadFn = true;
626
640
  const data = load ? load({
627
641
  params,
628
642
  location,
629
643
  intent: intent || "initial"
630
644
  }) : undefined;
645
+ inLoadFn = false;
631
646
  const route = {
632
647
  parent,
633
648
  pattern,
@@ -656,7 +671,8 @@ const createRouterComponent = router => props => {
656
671
  let context;
657
672
  const routerState = createRouterContext(router, branches, () => context, {
658
673
  base,
659
- singleFlight: props.singleFlight
674
+ singleFlight: props.singleFlight,
675
+ transformUrl: props.transformUrl
660
676
  });
661
677
  router.create && router.create(routerState);
662
678
  return createComponent$1(RouterContextObj.Provider, {
@@ -685,11 +701,15 @@ const createRouterComponent = router => props => {
685
701
  function Root(props) {
686
702
  const location = props.routerState.location;
687
703
  const params = props.routerState.params;
688
- const data = createMemo(() => props.load && untrack(() => props.load({
689
- params,
690
- location,
691
- intent: "preload"
692
- })));
704
+ const data = createMemo(() => props.load && untrack(() => {
705
+ setInLoadFn(true);
706
+ props.load({
707
+ params,
708
+ location,
709
+ intent: getIntent() || "initial"
710
+ });
711
+ setInLoadFn(false);
712
+ }));
693
713
  return createComponent$1(Show, {
694
714
  get when() {
695
715
  return props.root;
@@ -821,7 +841,7 @@ function createRouter(config) {
821
841
  value
822
842
  } : value;
823
843
  const signal = intercept(createSignal(wrap(config.get()), {
824
- equals: (a, b) => a.value === b.value
844
+ equals: (a, b) => a.value === b.value && a.state === b.state
825
845
  }), undefined, next => {
826
846
  !ignore && config.set(next);
827
847
  return next;
@@ -856,8 +876,9 @@ function getPath(url) {
856
876
  }
857
877
  function StaticRouter(props) {
858
878
  let e;
879
+ const url = props.url || (e = getRequestEvent()) && getPath(e.request.url) || "";
859
880
  const obj = {
860
- value: props.url || (e = getRequestEvent()) && getPath(e.request.url) || ""
881
+ value: props.transformUrl ? props.transformUrl(url) : url
861
882
  };
862
883
  return createRouterComponent({
863
884
  signal: [() => obj, next => Object.assign(obj, next)]
@@ -907,6 +928,7 @@ function cache(fn, name) {
907
928
  const cachedFn = (...args) => {
908
929
  const cache = getCache();
909
930
  const intent = getIntent();
931
+ const inLoadFn = getInLoadFn();
910
932
  const owner = getOwner();
911
933
  const navigate = owner ? useNavigate() : undefined;
912
934
  const now = Date.now();
@@ -940,10 +962,10 @@ function cache(fn, name) {
940
962
  cached[0] = now;
941
963
  }
942
964
  let res = cached[1];
943
- if (intent !== "preload") {
965
+ if (!inLoadFn) {
944
966
  res = "then" in cached[1] ? cached[1].then(handleResponse(false), handleResponse(true)) : handleResponse(false)(cached[1]);
945
967
  !isServer && intent === "navigate" && startTransition(() => cached[3][1](cached[0])); // update version
946
- }
968
+ } else "then" in res && res.catch(() => {});
947
969
  return res;
948
970
  }
949
971
  let res = !isServer && sharedConfig.context && sharedConfig.has(key) ? sharedConfig.load(key) // hydrating
@@ -965,9 +987,9 @@ function cache(fn, name) {
965
987
  const e = getRequestEvent();
966
988
  if (e && e.router.dataOnly) return e.router.data[key] = res;
967
989
  }
968
- if (intent !== "preload") {
990
+ if (!inLoadFn) {
969
991
  res = "then" in res ? res.then(handleResponse(false), handleResponse(true)) : handleResponse(false)(res);
970
- }
992
+ } else "then" in res && res.catch(() => {});
971
993
  // serialize on server
972
994
  if (isServer && sharedConfig.context && sharedConfig.context.async && !sharedConfig.context.noHydrate) {
973
995
  const e = getRequestEvent();
@@ -1132,21 +1154,17 @@ function toAction(fn, url) {
1132
1154
  const hashString = s => s.split("").reduce((a, b) => (a << 5) - a + b.charCodeAt(0) | 0, 0);
1133
1155
  async function handleResponse(response, error, navigate) {
1134
1156
  let data;
1157
+ let custom;
1135
1158
  let keys;
1136
- let invalidateKeys;
1159
+ let flightKeys;
1137
1160
  if (response instanceof Response) {
1138
- if (response.headers.has("X-Revalidate")) keys = invalidateKeys = response.headers.get("X-Revalidate").split(",");
1161
+ if (response.headers.has("X-Revalidate")) keys = response.headers.get("X-Revalidate").split(",");
1139
1162
  if (response.customBody) {
1140
- data = await response.customBody();
1163
+ data = custom = await response.customBody();
1141
1164
  if (response.headers.has("X-Single-Flight")) {
1142
- keys || (keys = []);
1143
- invalidateKeys || (invalidateKeys = []);
1144
- Object.keys(data).forEach(key => {
1145
- if (key === "_$value") return;
1146
- keys.push(key);
1147
- cache.set(key, data[key]);
1148
- });
1149
1165
  data = data._$value;
1166
+ delete custom._$value;
1167
+ flightKeys = Object.keys(custom);
1150
1168
  }
1151
1169
  }
1152
1170
  if (response.headers.has("Location")) {
@@ -1161,7 +1179,9 @@ async function handleResponse(response, error, navigate) {
1161
1179
  error: response
1162
1180
  };else data = response;
1163
1181
  // invalidate
1164
- cacheKeyOp(invalidateKeys, entry => entry[0] = 0);
1182
+ cacheKeyOp(keys, entry => entry[0] = 0);
1183
+ // set cache
1184
+ flightKeys && flightKeys.forEach(k => cache.set(k, custom[k]));
1165
1185
  // trigger revalidation
1166
1186
  await revalidate(keys, false);
1167
1187
  return data != null ? {
@@ -1169,7 +1189,7 @@ async function handleResponse(response, error, navigate) {
1169
1189
  } : undefined;
1170
1190
  }
1171
1191
 
1172
- function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "/_server") {
1192
+ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "/_server", transformUrl) {
1173
1193
  return router => {
1174
1194
  const basePath = router.base.path();
1175
1195
  const navigateFromRoute = router.navigatorFactory(router.base);
@@ -1209,12 +1229,18 @@ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "
1209
1229
  const res = handleAnchor(evt);
1210
1230
  if (!res) return;
1211
1231
  const [a, url] = res;
1232
+ if (typeof transformUrl === "function") {
1233
+ url.pathname = transformUrl(url.pathname);
1234
+ }
1212
1235
  if (!preloadTimeout[url.pathname]) router.preloadRoute(url, a.getAttribute("preload") !== "false");
1213
1236
  }
1214
1237
  function handleAnchorIn(evt) {
1215
1238
  const res = handleAnchor(evt);
1216
1239
  if (!res) return;
1217
1240
  const [a, url] = res;
1241
+ if (typeof transformUrl === "function") {
1242
+ url.pathname = transformUrl(url.pathname);
1243
+ }
1218
1244
  if (preloadTimeout[url.pathname]) return;
1219
1245
  preloadTimeout[url.pathname] = setTimeout(() => {
1220
1246
  router.preloadRoute(url, a.getAttribute("preload") !== "false");
@@ -1225,6 +1251,9 @@ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "
1225
1251
  const res = handleAnchor(evt);
1226
1252
  if (!res) return;
1227
1253
  const [, url] = res;
1254
+ if (typeof transformUrl === "function") {
1255
+ url.pathname = transformUrl(url.pathname);
1256
+ }
1228
1257
  if (preloadTimeout[url.pathname]) {
1229
1258
  clearTimeout(preloadTimeout[url.pathname]);
1230
1259
  delete preloadTimeout[url.pathname];
@@ -1278,10 +1307,13 @@ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "
1278
1307
 
1279
1308
  function Router(props) {
1280
1309
  if (isServer) return StaticRouter(props);
1281
- const getSource = () => ({
1282
- value: window.location.pathname + window.location.search + window.location.hash,
1283
- state: window.history.state
1284
- });
1310
+ const getSource = () => {
1311
+ const url = window.location.pathname + window.location.search;
1312
+ return {
1313
+ value: props.transformUrl ? props.transformUrl(url) + window.location.hash : url + window.location.hash,
1314
+ state: window.history.state
1315
+ };
1316
+ };
1285
1317
  const beforeLeave = createBeforeLeave();
1286
1318
  return createRouter({
1287
1319
  get: getSource,
@@ -1309,7 +1341,7 @@ function Router(props) {
1309
1341
  });
1310
1342
  }
1311
1343
  })),
1312
- create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase),
1344
+ create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase, props.transformUrl),
1313
1345
  utils: {
1314
1346
  go: delta => window.history.go(delta),
1315
1347
  beforeLeave
@@ -1607,4 +1639,4 @@ function json(data, init = {}) {
1607
1639
  return response;
1608
1640
  }
1609
1641
 
1610
- export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createAsyncStore, createBeforeLeave, createMemoryHistory, createRouter, json, keepDepth, notifyIfNotBlocked, redirect, reload, revalidate, saveCurrentDepth, useAction, useBeforeLeave, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
1642
+ export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createAsyncStore, createBeforeLeave, createMemoryHistory, createRouter, json, keepDepth, notifyIfNotBlocked, redirect, reload, revalidate, saveCurrentDepth, useAction, useBeforeLeave, useCurrentMatches, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
package/dist/index.jsx CHANGED
@@ -1,6 +1,6 @@
1
1
  export * from "./routers/index.js";
2
2
  export * from "./components.jsx";
3
3
  export * from "./lifecycle.js";
4
- export { useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing.js";
4
+ export { useHref, useIsRouting, useLocation, useMatch, useCurrentMatches, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing.js";
5
5
  export { mergeSearchString as _mergeSearchString } from "./utils.js";
6
6
  export * from "./data/index.js";
@@ -6,10 +6,13 @@ import { createBeforeLeave, keepDepth, notifyIfNotBlocked, saveCurrentDepth } fr
6
6
  export function Router(props) {
7
7
  if (isServer)
8
8
  return StaticRouter(props);
9
- const getSource = () => ({
10
- value: window.location.pathname + window.location.search + window.location.hash,
11
- state: window.history.state
12
- });
9
+ const getSource = () => {
10
+ const url = window.location.pathname + window.location.search;
11
+ return {
12
+ value: props.transformUrl ? props.transformUrl(url) + window.location.hash : url + window.location.hash,
13
+ state: window.history.state
14
+ };
15
+ };
13
16
  const beforeLeave = createBeforeLeave();
14
17
  return createRouter({
15
18
  get: getSource,
@@ -32,7 +35,7 @@ export function Router(props) {
32
35
  return !beforeLeave.confirm(s.value, { state: s.state });
33
36
  }
34
37
  })),
35
- create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase),
38
+ create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase, props.transformUrl),
36
39
  utils: {
37
40
  go: delta => window.history.go(delta),
38
41
  beforeLeave
@@ -6,8 +6,9 @@ function getPath(url) {
6
6
  }
7
7
  export function StaticRouter(props) {
8
8
  let e;
9
+ const url = props.url || ((e = getRequestEvent()) && getPath(e.request.url)) || "";
9
10
  const obj = {
10
- value: props.url || ((e = getRequestEvent()) && getPath(e.request.url)) || ""
11
+ value: props.transformUrl ? props.transformUrl(url) : url,
11
12
  };
12
13
  return createRouterComponent({
13
14
  signal: [() => obj, next => Object.assign(obj, next)]
@@ -9,6 +9,7 @@ export type BaseRouterProps = {
9
9
  rootLoad?: RouteLoadFunc;
10
10
  singleFlight?: boolean;
11
11
  children?: JSX.Element | RouteDefinition | RouteDefinition[];
12
+ transformUrl?: (url: string) => string;
12
13
  };
13
14
  export declare const createRouterComponent: (router: RouterIntegration) => (props: BaseRouterProps) => JSX.Element;
14
15
  export type RouteProps<S extends string, T = unknown> = {
@@ -1,7 +1,7 @@
1
1
  /*@refresh skip*/
2
2
  import { getRequestEvent, isServer } from "solid-js/web";
3
3
  import { children, createMemo, createRoot, getOwner, mergeProps, on, Show, untrack } from "solid-js";
4
- import { createBranches, createRouteContext, createRouterContext, getRouteMatches, RouteContextObj, RouterContextObj } from "../routing.js";
4
+ import { createBranches, createRouteContext, createRouterContext, getIntent, getRouteMatches, RouteContextObj, RouterContextObj, setInLoadFn } from "../routing.js";
5
5
  export const createRouterComponent = (router) => (props) => {
6
6
  const { base } = props;
7
7
  const routeDefs = children(() => props.children);
@@ -9,7 +9,8 @@ export const createRouterComponent = (router) => (props) => {
9
9
  let context;
10
10
  const routerState = createRouterContext(router, branches, () => context, {
11
11
  base,
12
- singleFlight: props.singleFlight
12
+ singleFlight: props.singleFlight,
13
+ transformUrl: props.transformUrl,
13
14
  });
14
15
  router.create && router.create(routerState);
15
16
  return (<RouterContextObj.Provider value={routerState}>
@@ -22,7 +23,12 @@ export const createRouterComponent = (router) => (props) => {
22
23
  function Root(props) {
23
24
  const location = props.routerState.location;
24
25
  const params = props.routerState.params;
25
- const data = createMemo(() => props.load && untrack(() => props.load({ params, location, intent: "preload" })));
26
+ const data = createMemo(() => props.load &&
27
+ untrack(() => {
28
+ setInLoadFn(true);
29
+ props.load({ params, location, intent: getIntent() || "initial" });
30
+ setInLoadFn(false);
31
+ }));
26
32
  return (<Show when={props.root} keyed fallback={props.children}>
27
33
  {Root => (<Root params={params} location={location} data={data()}>
28
34
  {props.children}
@@ -18,7 +18,9 @@ function querySelector(selector) {
18
18
  export function createRouter(config) {
19
19
  let ignore = false;
20
20
  const wrap = (value) => (typeof value === "string" ? { value } : value);
21
- const signal = intercept(createSignal(wrap(config.get()), { equals: (a, b) => a.value === b.value }), undefined, next => {
21
+ const signal = intercept(createSignal(wrap(config.get()), {
22
+ equals: (a, b) => a.value === b.value && a.state === b.state
23
+ }), undefined, next => {
22
24
  !ignore && config.set(next);
23
25
  return next;
24
26
  });
package/dist/routing.d.ts CHANGED
@@ -10,6 +10,7 @@ export declare const useNavigate: () => Navigator;
10
10
  export declare const useLocation: <S = unknown>() => Location<S>;
11
11
  export declare const useIsRouting: () => () => boolean;
12
12
  export declare const useMatch: <S extends string>(path: () => S, matchFilters?: MatchFilters<S> | undefined) => Accessor<import("./types.js").PathMatch | undefined>;
13
+ export declare const useCurrentMatches: () => RouteMatch[];
13
14
  export declare const useParams: <T extends Params>() => T;
14
15
  export declare const useSearchParams: <T extends Params>() => [Partial<T>, (params: SetParams, options?: Partial<NavigateOptions>) => void];
15
16
  export declare const useBeforeLeave: (listener: (e: BeforeLeaveEventArgs) => void) => void;
@@ -19,8 +20,11 @@ export declare function createBranches(routeDef: RouteDefinition | RouteDefiniti
19
20
  export declare function getRouteMatches(branches: Branch[], location: string): RouteMatch[];
20
21
  export declare function createLocation(path: Accessor<string>, state: Accessor<any>): Location;
21
22
  export declare function getIntent(): Intent | undefined;
23
+ export declare function getInLoadFn(): boolean;
24
+ export declare function setInLoadFn(value: boolean): void;
22
25
  export declare function createRouterContext(integration: RouterIntegration, branches: () => Branch[], getContext?: () => any, options?: {
23
26
  base?: string;
24
27
  singleFlight?: boolean;
28
+ transformUrl?: (url: string) => string;
25
29
  }): RouterContext;
26
30
  export declare function createRouteContext(router: RouterContext, parent: RouteContext, outlet: () => JSX.Element, match: () => RouteMatch): RouteContext;
package/dist/routing.js CHANGED
@@ -34,6 +34,7 @@ export const useMatch = (path, matchFilters) => {
34
34
  }
35
35
  });
36
36
  };
37
+ export const useCurrentMatches = () => useRouter().matches();
37
38
  export const useParams = () => useRouter().params;
38
39
  export const useSearchParams = () => {
39
40
  const location = useLocation();
@@ -184,6 +185,13 @@ let intent;
184
185
  export function getIntent() {
185
186
  return intent;
186
187
  }
188
+ let inLoadFn = false;
189
+ export function getInLoadFn() {
190
+ return inLoadFn;
191
+ }
192
+ export function setInLoadFn(value) {
193
+ inLoadFn = value;
194
+ }
187
195
  export function createRouterContext(integration, branches, getContext, options = {}) {
188
196
  const { signal: [source, setSource], utils = {} } = integration;
189
197
  const parsePath = utils.parsePath || (p => p);
@@ -211,7 +219,12 @@ export function createRouterContext(integration, branches, getContext, options =
211
219
  const location = createLocation(reference, state);
212
220
  const referrers = [];
213
221
  const submissions = createSignal(isServer ? initFromFlash() : []);
214
- const matches = createMemo(() => getRouteMatches(branches(), location.pathname));
222
+ const matches = createMemo(() => {
223
+ if (typeof options.transformUrl === "function") {
224
+ return getRouteMatches(branches(), options.transformUrl(location.pathname));
225
+ }
226
+ return getRouteMatches(branches(), location.pathname);
227
+ });
215
228
  const params = createMemoObject(() => {
216
229
  const m = matches();
217
230
  const params = {};
@@ -232,17 +245,16 @@ export function createRouterContext(integration, branches, getContext, options =
232
245
  const { value, state } = source();
233
246
  // Untrack this whole block so `start` doesn't cause Solid's Listener to be preserved
234
247
  untrack(() => {
235
- if (value !== reference()) {
236
- start(() => {
237
- intent = "native";
248
+ start(() => {
249
+ intent = "native";
250
+ if (value !== reference())
238
251
  setReference(value);
239
- setState(state);
240
- resetErrorBoundaries();
241
- submissions[1]([]);
242
- }).then(() => {
243
- intent = undefined;
244
- });
245
- }
252
+ setState(state);
253
+ resetErrorBoundaries();
254
+ submissions[1]([]);
255
+ }).then(() => {
256
+ intent = undefined;
257
+ });
246
258
  });
247
259
  });
248
260
  return {
@@ -343,6 +355,7 @@ export function createRouterContext(integration, branches, getContext, options =
343
355
  route.component.preload &&
344
356
  route.component.preload();
345
357
  const { load } = route;
358
+ inLoadFn = true;
346
359
  preloadData &&
347
360
  load &&
348
361
  runWithOwner(getContext(), () => load({
@@ -357,6 +370,7 @@ export function createRouterContext(integration, branches, getContext, options =
357
370
  },
358
371
  intent: "preload"
359
372
  }));
373
+ inLoadFn = false;
360
374
  }
361
375
  intent = prevIntent;
362
376
  }
@@ -374,7 +388,9 @@ export function createRouteContext(router, parent, outlet, match) {
374
388
  component &&
375
389
  component.preload &&
376
390
  component.preload();
391
+ inLoadFn = true;
377
392
  const data = load ? load({ params, location, intent: intent || "initial" }) : undefined;
393
+ inLoadFn = false;
378
394
  const route = {
379
395
  parent,
380
396
  pattern,
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "Ryan Turnquist"
7
7
  ],
8
8
  "license": "MIT",
9
- "version": "0.13.2",
9
+ "version": "0.13.3",
10
10
  "homepage": "https://github.com/solidjs/solid-router#readme",
11
11
  "repository": {
12
12
  "type": "git",