@solidjs/router 0.10.10 → 0.11.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.
package/README.md CHANGED
@@ -565,13 +565,13 @@ Are used to injecting the optimistic updates while actions are in flight. They e
565
565
 
566
566
  ```jsx
567
567
  type Submission<T, U> = {
568
- input: T;
569
- result: U;
570
- error: any;
571
- pending: boolean
572
- clear: () => {}
573
- retry: () => {}
574
- }
568
+ readonly input: T;
569
+ readonly result?: U;
570
+ readonly pending: boolean;
571
+ readonly url: string;
572
+ clear: () => void;
573
+ retry: () => void;
574
+ };
575
575
 
576
576
  const submissions = useSubmissions(action, (input) => filter(input));
577
577
  const submission = useSubmission(action, (input) => filter(input));
@@ -721,7 +721,7 @@ This is the main Router component for the browser.
721
721
 
722
722
  ### `<A>`
723
723
 
724
- Like the `<a>` tag but supports relative paths and active class styling (requires client side JavaScript).
724
+ Like the `<a>` tag but supports automatic apply of base path + relative paths and active class styling (requires client side JavaScript).
725
725
 
726
726
  The `<A>` tag has an `active` class if its href matches the current location, and `inactive` otherwise. **Note:** By default matching includes locations that are descendents (eg. href `/users` matches locations `/users` and `/users/123`), use the boolean `end` prop to prevent matching these. This is particularly useful for links to the root route `/` which would match everything.
727
727
 
@@ -1,5 +1,5 @@
1
1
  import type { JSX } from "solid-js";
2
- import type { Location, Navigator } from "./types";
2
+ import type { Location, Navigator } from "./types.js";
3
3
  declare module "solid-js" {
4
4
  namespace JSX {
5
5
  interface AnchorHTMLAttributes<T> {
@@ -1,6 +1,6 @@
1
1
  import { createMemo, mergeProps, splitProps } from "solid-js";
2
- import { useHref, useLocation, useNavigate, useResolvedPath } from "./routing";
3
- import { normalizePath } from "./utils";
2
+ import { useHref, useLocation, useNavigate, useResolvedPath } from "./routing.js";
3
+ import { normalizePath } from "./utils.js";
4
4
  export function A(props) {
5
5
  props = mergeProps({ inactiveClass: "inactive", activeClass: "active" }, props);
6
6
  const [, rest] = splitProps(props, [
@@ -17,17 +17,17 @@ export function A(props) {
17
17
  const isActive = createMemo(() => {
18
18
  const to_ = to();
19
19
  if (to_ === undefined)
20
- return false;
20
+ return [false, false];
21
21
  const path = normalizePath(to_.split(/[?#]/, 1)[0]).toLowerCase();
22
22
  const loc = normalizePath(location.pathname).toLowerCase();
23
- return props.end ? path === loc : loc.startsWith(path);
23
+ return [props.end ? path === loc : loc.startsWith(path), path === loc];
24
24
  });
25
25
  return (<a {...rest} href={href() || props.href} state={JSON.stringify(props.state)} classList={{
26
26
  ...(props.class && { [props.class]: true }),
27
- [props.inactiveClass]: !isActive(),
28
- [props.activeClass]: isActive(),
27
+ [props.inactiveClass]: !isActive()[0],
28
+ [props.activeClass]: isActive()[0],
29
29
  ...rest.classList
30
- }} link aria-current={isActive() ? "page" : undefined}/>);
30
+ }} link aria-current={isActive()[1] ? "page" : undefined}/>);
31
31
  }
32
32
  export function Navigate(props) {
33
33
  const navigate = useNavigate();
@@ -1,5 +1,5 @@
1
1
  import { JSX } from "solid-js";
2
- import { Submission } from "../types";
2
+ import { Submission } from "../types.js";
3
3
  export type Action<T extends Array<any>, U> = (T extends [FormData] | [] ? JSX.SerializableAttributeValue : unknown) & ((...vars: T) => Promise<U>) & {
4
4
  url: string;
5
5
  with<A extends any[], B extends any[]>(this: (this: any, ...args: [...A, ...B]) => Promise<U>, ...args: A): Action<B, U>;
@@ -1,8 +1,8 @@
1
1
  import { $TRACK, createMemo, createSignal, onCleanup, getOwner } from "solid-js";
2
2
  import { isServer } from "solid-js/web";
3
- import { useRouter } from "../routing";
4
- import { redirectStatusCodes, mockBase } from "../utils";
5
- import { cacheKeyOp, hashKey, revalidate } from "./cache";
3
+ import { useRouter } from "../routing.js";
4
+ import { redirectStatusCodes, mockBase } from "../utils.js";
5
+ import { cacheKeyOp, hashKey, revalidate } from "./cache.js";
6
6
  export const actions = /* #__PURE__ */ new Map();
7
7
  export function useSubmissions(fn, filter) {
8
8
  const router = useRouter();
@@ -1,6 +1,5 @@
1
- import { type Signal } from "solid-js";
2
1
  import { type ReconcileOptions } from "solid-js/store";
3
- type CacheEntry = [number, any, string, Set<Signal<number>>];
2
+ import { CacheEntry } from "../types.js";
4
3
  export declare function revalidate(key?: string | string[] | void, force?: boolean): Promise<void>;
5
4
  export declare function cacheKeyOp(key: string | string[] | void, fn: (cacheEntry: CacheEntry) => void): void;
6
5
  export type CachedFunction<T extends (...args: any) => U | Response, U> = T & {
@@ -10,7 +9,6 @@ export type CachedFunction<T extends (...args: any) => U | Response, U> = T & {
10
9
  export declare function cache<T extends (...args: any) => U | Response, U>(fn: T, name: string, options?: ReconcileOptions): CachedFunction<T, U>;
11
10
  export declare namespace cache {
12
11
  var set: (key: string, value: any) => void;
13
- var clear: () => any;
12
+ var clear: () => void;
14
13
  }
15
14
  export declare function hashKey<T extends Array<any>>(args: T): string;
16
- export {};
@@ -1,8 +1,8 @@
1
- import { createSignal, getOwner, onCleanup, sharedConfig, startTransition } from "solid-js";
1
+ import { createSignal, getListener, getOwner, onCleanup, sharedConfig, startTransition } from "solid-js";
2
2
  import { createStore, reconcile } from "solid-js/store";
3
3
  import { getRequestEvent, isServer } from "solid-js/web";
4
- import { useNavigate, getIntent } from "../routing";
5
- import { redirectStatusCodes } from "../utils";
4
+ import { useNavigate, getIntent } from "../routing.js";
5
+ import { redirectStatusCodes } from "../utils.js";
6
6
  const LocationHeader = "Location";
7
7
  const PRELOAD_TIMEOUT = 5000;
8
8
  const CACHE_TIMEOUT = 180000;
@@ -12,7 +12,7 @@ if (!isServer) {
12
12
  setInterval(() => {
13
13
  const now = Date.now();
14
14
  for (let [k, v] of cacheMap.entries()) {
15
- if (!v[3].size && now - v[0] > CACHE_TIMEOUT) {
15
+ if (!v[3].count && now - v[0] > CACHE_TIMEOUT) {
16
16
  cacheMap.delete(k);
17
17
  }
18
18
  }
@@ -21,17 +21,17 @@ if (!isServer) {
21
21
  function getCache() {
22
22
  if (!isServer)
23
23
  return cacheMap;
24
- const req = getRequestEvent() || sharedConfig.context;
24
+ const req = getRequestEvent();
25
25
  if (!req)
26
26
  throw new Error("Cannot find cache context");
27
- return req.routerCache || (req.routerCache = new Map());
27
+ return (req.router || (req.router = {})).cache || (req.router.cache = new Map());
28
28
  }
29
29
  export function revalidate(key, force = true) {
30
30
  return startTransition(() => {
31
31
  const now = Date.now();
32
32
  cacheKeyOp(key, entry => {
33
33
  force && (entry[0] = 0); //force cache miss
34
- revalidateSignals(entry[3], now); // retrigger live signals
34
+ entry[3][1](now); // retrigger live signals
35
35
  });
36
36
  });
37
37
  }
@@ -42,10 +42,6 @@ export function cacheKeyOp(key, fn) {
42
42
  fn(cacheMap.get(k));
43
43
  }
44
44
  }
45
- function revalidateSignals(set, time) {
46
- for (let s of set)
47
- s[1](time);
48
- }
49
45
  export function cache(fn, name, options) {
50
46
  const [store, setStore] = createStore({});
51
47
  // prioritize GET for server functions
@@ -59,16 +55,20 @@ export function cache(fn, name, options) {
59
55
  const now = Date.now();
60
56
  const key = name + hashKey(args);
61
57
  let cached = cache.get(key);
62
- let version;
63
- if (owner && !isServer) {
64
- version = createSignal(now, {
65
- equals: (p, v) => v - p < 50 // margin of error
66
- });
67
- onCleanup(() => cached[3].delete(version));
68
- version[0](); // track it;
58
+ let tracking;
59
+ if (getListener() && !isServer) {
60
+ tracking = true;
61
+ onCleanup(() => cached[3].count--);
69
62
  }
70
- if (cached && (isServer || intent === "native" || Date.now() - cached[0] < PRELOAD_TIMEOUT)) {
71
- version && cached[3].add(version);
63
+ if (cached &&
64
+ (isServer ||
65
+ intent === "native" ||
66
+ (cached[0] && cached[3].count) ||
67
+ Date.now() - cached[0] < PRELOAD_TIMEOUT)) {
68
+ if (tracking) {
69
+ cached[3].count++;
70
+ cached[3][0](); // track
71
+ }
72
72
  if (cached[2] === "preload" && intent !== "preload") {
73
73
  cached[0] = now;
74
74
  }
@@ -78,9 +78,7 @@ export function cache(fn, name, options) {
78
78
  "then" in cached[1]
79
79
  ? cached[1].then(handleResponse(false), handleResponse(true))
80
80
  : handleResponse(false)(cached[1]);
81
- !isServer &&
82
- intent === "navigate" &&
83
- startTransition(() => revalidateSignals(cached[3], cached[0])); // update version
81
+ !isServer && intent === "navigate" && startTransition(() => cached[3][1](cached[0])); // update version
84
82
  }
85
83
  return res;
86
84
  }
@@ -96,13 +94,16 @@ export function cache(fn, name, options) {
96
94
  cached[0] = now;
97
95
  cached[1] = res;
98
96
  cached[2] = intent;
99
- version && cached[3].add(version);
100
- if (!isServer && intent === "navigate") {
101
- startTransition(() => revalidateSignals(cached[3], cached[0])); // update version
102
- }
97
+ !isServer && intent === "navigate" && startTransition(() => cached[3][1](cached[0])); // update version
98
+ }
99
+ else {
100
+ cache.set(key, (cached = [now, res, intent, createSignal(now)]));
101
+ cached[3].count = 0;
102
+ }
103
+ if (tracking) {
104
+ cached[3].count++;
105
+ cached[3][0](); // track
103
106
  }
104
- else
105
- cache.set(key, (cached = [now, res, intent, new Set(version ? [version] : [])]));
106
107
  if (intent !== "preload") {
107
108
  res =
108
109
  "then" in res
@@ -149,21 +150,15 @@ cache.set = (key, value) => {
149
150
  const cache = getCache();
150
151
  const now = Date.now();
151
152
  let cached = cache.get(key);
152
- let version;
153
- if (getOwner()) {
154
- version = createSignal(now, {
155
- equals: (p, v) => v - p < 50 // margin of error
156
- });
157
- onCleanup(() => cached[3].delete(version));
158
- }
159
153
  if (cached) {
160
154
  cached[0] = now;
161
155
  cached[1] = value;
162
156
  cached[2] = "preload";
163
- version && cached[3].add(version);
164
157
  }
165
- else
166
- cache.set(key, (cached = [now, value, , new Set(version ? [version] : [])]));
158
+ else {
159
+ cache.set(key, (cached = [now, value, , createSignal(now)]));
160
+ cached[3].count = 0;
161
+ }
167
162
  };
168
163
  cache.clear = () => getCache().clear();
169
164
  function matchKey(key, keys) {
@@ -1,2 +1,2 @@
1
- import type { RouterContext } from "../types";
1
+ import type { RouterContext } from "../types.js";
2
2
  export declare function setupNativeEvents(preload?: boolean, explicitLinks?: boolean, actionBase?: string): (router: RouterContext) => void;
@@ -1,7 +1,7 @@
1
1
  import { delegateEvents } from "solid-js/web";
2
2
  import { onCleanup } from "solid-js";
3
- import { actions } from "./action";
4
- import { mockBase } from "../utils";
3
+ import { actions } from "./action.js";
4
+ import { mockBase } from "../utils.js";
5
5
  export function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "/_server") {
6
6
  return (router) => {
7
7
  const basePath = router.base.path();
@@ -1,4 +1,4 @@
1
- export { createAsync } from "./createAsync";
2
- export { action, useSubmission, useSubmissions, useAction, type Action } from "./action";
3
- export { cache, revalidate, type CachedFunction } from "./cache";
4
- export { redirect, reload, json } from "./response";
1
+ export { createAsync } from "./createAsync.js";
2
+ export { action, useSubmission, useSubmissions, useAction, type Action } from "./action.js";
3
+ export { cache, revalidate, type CachedFunction } from "./cache.js";
4
+ export { redirect, reload, json } from "./response.js";
@@ -1,4 +1,4 @@
1
- export { createAsync } from "./createAsync";
2
- export { action, useSubmission, useSubmissions, useAction } from "./action";
3
- export { cache, revalidate } from "./cache";
4
- export { redirect, reload, json } from "./response";
1
+ export { createAsync } from "./createAsync.js";
2
+ export { action, useSubmission, useSubmissions, useAction } from "./action.js";
3
+ export { cache, revalidate } from "./cache.js";
4
+ export { redirect, reload, json } from "./response.js";
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- export * from "./routers";
2
- export * from "./components";
3
- export * from "./lifecycle";
4
- export { useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing";
5
- export { mergeSearchString as _mergeSearchString } from "./utils";
6
- export * from "./data";
7
- export type { Location, LocationChange, NavigateOptions, Navigator, OutputMatch, Params, RouteSectionProps, RouteLoadFunc, RouteLoadFuncArgs, RouteDefinition, RouterIntegration, RouterUtils, SetParams, BeforeLeaveEventArgs } from "./types";
1
+ export * from "./routers/index.js";
2
+ export * from "./components.jsx";
3
+ export * from "./lifecycle.js";
4
+ export { useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing.js";
5
+ export { mergeSearchString as _mergeSearchString } from "./utils.js";
6
+ export * from "./data/index.js";
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
@@ -1,5 +1,5 @@
1
1
  import { isServer, getRequestEvent, createComponent as createComponent$1, delegateEvents, spread, mergeProps as mergeProps$1, template } from 'solid-js/web';
2
- import { getOwner, runWithOwner, createMemo, createContext, onCleanup, useContext, untrack, createSignal, createRenderEffect, on, startTransition, resetErrorBoundaries, createComponent, children, mergeProps, createRoot, Show, sharedConfig, $TRACK, splitProps, createResource } from 'solid-js';
2
+ import { getOwner, runWithOwner, createMemo, createContext, onCleanup, useContext, untrack, createSignal, createRenderEffect, on, startTransition, resetErrorBoundaries, createComponent, children, mergeProps, createRoot, Show, getListener, sharedConfig, $TRACK, splitProps, createResource } from 'solid-js';
3
3
  import { createStore, reconcile } from 'solid-js/store';
4
4
 
5
5
  function createBeforeLeave() {
@@ -36,6 +36,46 @@ function createBeforeLeave() {
36
36
  };
37
37
  }
38
38
 
39
+ // The following supports browser initiated blocking (eg back/forward)
40
+
41
+ let depth;
42
+ function saveCurrentDepth() {
43
+ if (!window.history.state || window.history.state._depth == null) {
44
+ window.history.replaceState({
45
+ ...window.history.state,
46
+ _depth: window.history.length - 1
47
+ }, "");
48
+ }
49
+ depth = window.history.state._depth;
50
+ }
51
+ if (!isServer) {
52
+ saveCurrentDepth();
53
+ }
54
+ function keepDepth(state) {
55
+ return {
56
+ ...state,
57
+ _depth: window.history.state && window.history.state._depth
58
+ };
59
+ }
60
+ function notifyIfNotBlocked(notify, block) {
61
+ let ignore = false;
62
+ return () => {
63
+ const prevDepth = depth;
64
+ saveCurrentDepth();
65
+ const delta = prevDepth == null ? null : depth - prevDepth;
66
+ if (ignore) {
67
+ ignore = false;
68
+ return;
69
+ }
70
+ if (delta && block(delta)) {
71
+ ignore = true;
72
+ window.history.go(-delta);
73
+ } else {
74
+ notify();
75
+ }
76
+ };
77
+ }
78
+
39
79
  const hasSchemeRegex = /^(?:[a-z0-9]+:)?\/\//i;
40
80
  const trimPathRegex = /^\/+|(\/)\/+$/g;
41
81
  const mockBase = "http://sr";
@@ -244,21 +284,21 @@ function createRoutes(routeDef, base = "") {
244
284
  component,
245
285
  load,
246
286
  children,
247
- metadata
287
+ info
248
288
  } = routeDef;
249
289
  const isLeaf = !children || Array.isArray(children) && !children.length;
250
290
  const shared = {
251
291
  key: routeDef,
252
292
  component,
253
293
  load,
254
- metadata
294
+ info
255
295
  };
256
296
  return asArray(routeDef.path).reduce((acc, path) => {
257
297
  for (const originalPath of expandOptionals(path)) {
258
298
  const path = joinPaths(base, originalPath);
259
299
  let pattern = isLeaf ? path : path.split("/*", 1)[0];
260
300
  pattern = pattern.split("/").map(s => {
261
- return s.startsWith(':') || s.startsWith('*') ? s : encodeURIComponent(s);
301
+ return s.startsWith(":") || s.startsWith("*") ? s : encodeURIComponent(s);
262
302
  }).join("/");
263
303
  acc.push({
264
304
  ...shared,
@@ -444,7 +484,7 @@ function createRouterContext(integration, getBranches, options = {}) {
444
484
  untrack(() => {
445
485
  if (typeof to === "number") {
446
486
  if (!to) ; else if (utils.go) {
447
- beforeLeave.confirm(to, options) && utils.go(to);
487
+ utils.go(to);
448
488
  } else {
449
489
  console.warn("Router integration does not support relative routing");
450
490
  }
@@ -471,12 +511,12 @@ function createRouterContext(integration, getBranches, options = {}) {
471
511
  if (resolvedTo !== current || nextState !== state()) {
472
512
  if (isServer) {
473
513
  const e = getRequestEvent();
474
- e && (e.response = new Response(null, {
514
+ e && (e.response = {
475
515
  status: 302,
476
- headers: {
516
+ headers: new Headers({
477
517
  Location: resolvedTo
478
- }
479
- }));
518
+ })
519
+ });
480
520
  setSource({
481
521
  value: resolvedTo,
482
522
  replace,
@@ -554,7 +594,7 @@ function createRouterContext(integration, getBranches, options = {}) {
554
594
  }
555
595
  function initFromFlash() {
556
596
  const e = getRequestEvent();
557
- return e && e.initialSubmission ? [e.initialSubmission] : [];
597
+ return e && e.router && e.router.submission ? [e.router.submission] : [];
558
598
  }
559
599
  }
560
600
  function createRouteContext(router, parent, outlet, match, params) {
@@ -623,7 +663,7 @@ function Routes(props) {
623
663
  const matches = createMemo(() => getRouteMatches(props.branches, props.routerState.location.pathname));
624
664
  if (isServer) {
625
665
  const e = getRequestEvent();
626
- e && (e.routerMatches || (e.routerMatches = [])).push(matches().map(({
666
+ e && ((e.router || (e.router = {})).matches || (e.router.matches = matches().map(({
627
667
  route,
628
668
  path,
629
669
  params
@@ -632,8 +672,8 @@ function Routes(props) {
632
672
  pattern: route.pattern,
633
673
  match: path,
634
674
  params,
635
- metadata: route.metadata
636
- })));
675
+ info: route.info
676
+ }))));
637
677
  }
638
678
  const params = createMemoObject(() => {
639
679
  const m = matches();
@@ -780,7 +820,7 @@ if (!isServer) {
780
820
  setInterval(() => {
781
821
  const now = Date.now();
782
822
  for (let [k, v] of cacheMap.entries()) {
783
- if (!v[3].size && now - v[0] > CACHE_TIMEOUT) {
823
+ if (!v[3].count && now - v[0] > CACHE_TIMEOUT) {
784
824
  cacheMap.delete(k);
785
825
  }
786
826
  }
@@ -788,29 +828,25 @@ if (!isServer) {
788
828
  }
789
829
  function getCache() {
790
830
  if (!isServer) return cacheMap;
791
- const req = getRequestEvent() || sharedConfig.context;
831
+ const req = getRequestEvent();
792
832
  if (!req) throw new Error("Cannot find cache context");
793
- return req.routerCache || (req.routerCache = new Map());
833
+ return (req.router || (req.router = {})).cache || (req.router.cache = new Map());
794
834
  }
795
835
  function revalidate(key, force = true) {
796
836
  return startTransition(() => {
797
837
  const now = Date.now();
798
838
  cacheKeyOp(key, entry => {
799
839
  force && (entry[0] = 0); //force cache miss
800
- revalidateSignals(entry[3], now); // retrigger live signals
840
+ entry[3][1](now); // retrigger live signals
801
841
  });
802
842
  });
803
843
  }
804
-
805
844
  function cacheKeyOp(key, fn) {
806
845
  key && !Array.isArray(key) && (key = [key]);
807
846
  for (let k of cacheMap.keys()) {
808
847
  if (key === undefined || matchKey(k, key)) fn(cacheMap.get(k));
809
848
  }
810
849
  }
811
- function revalidateSignals(set, time) {
812
- for (let s of set) s[1](time);
813
- }
814
850
  function cache(fn, name, options) {
815
851
  const [store, setStore] = createStore({});
816
852
  // prioritize GET for server functions
@@ -823,27 +859,24 @@ function cache(fn, name, options) {
823
859
  const now = Date.now();
824
860
  const key = name + hashKey(args);
825
861
  let cached = cache.get(key);
826
- let version;
827
- if (owner && !isServer) {
828
- version = createSignal(now, {
829
- equals: (p, v) => v - p < 50 // margin of error
830
- });
831
-
832
- onCleanup(() => cached[3].delete(version));
833
- version[0](); // track it;
862
+ let tracking;
863
+ if (getListener() && !isServer) {
864
+ tracking = true;
865
+ onCleanup(() => cached[3].count--);
834
866
  }
835
-
836
- if (cached && (isServer || intent === "native" || Date.now() - cached[0] < PRELOAD_TIMEOUT)) {
837
- version && cached[3].add(version);
867
+ if (cached && (isServer || intent === "native" || cached[0] && cached[3].count || Date.now() - cached[0] < PRELOAD_TIMEOUT)) {
868
+ if (tracking) {
869
+ cached[3].count++;
870
+ cached[3][0](); // track
871
+ }
838
872
  if (cached[2] === "preload" && intent !== "preload") {
839
873
  cached[0] = now;
840
874
  }
841
875
  let res = cached[1];
842
876
  if (intent !== "preload") {
843
877
  res = "then" in cached[1] ? cached[1].then(handleResponse(false), handleResponse(true)) : handleResponse(false)(cached[1]);
844
- !isServer && intent === "navigate" && startTransition(() => revalidateSignals(cached[3], cached[0])); // update version
878
+ !isServer && intent === "navigate" && startTransition(() => cached[3][1](cached[0])); // update version
845
879
  }
846
-
847
880
  return res;
848
881
  }
849
882
  let res = !isServer && sharedConfig.context && sharedConfig.load ? sharedConfig.load(key) // hydrating
@@ -858,11 +891,15 @@ function cache(fn, name, options) {
858
891
  cached[0] = now;
859
892
  cached[1] = res;
860
893
  cached[2] = intent;
861
- version && cached[3].add(version);
862
- if (!isServer && intent === "navigate") {
863
- startTransition(() => revalidateSignals(cached[3], cached[0])); // update version
864
- }
865
- } else cache.set(key, cached = [now, res, intent, new Set(version ? [version] : [])]);
894
+ !isServer && intent === "navigate" && startTransition(() => cached[3][1](cached[0])); // update version
895
+ } else {
896
+ cache.set(key, cached = [now, res, intent, createSignal(now)]);
897
+ cached[3].count = 0;
898
+ }
899
+ if (tracking) {
900
+ cached[3].count++;
901
+ cached[3][0](); // track
902
+ }
866
903
  if (intent !== "preload") {
867
904
  res = "then" in res ? res.then(handleResponse(false), handleResponse(true)) : handleResponse(false)(res);
868
905
  }
@@ -902,20 +939,14 @@ cache.set = (key, value) => {
902
939
  const cache = getCache();
903
940
  const now = Date.now();
904
941
  let cached = cache.get(key);
905
- let version;
906
- if (getOwner()) {
907
- version = createSignal(now, {
908
- equals: (p, v) => v - p < 50 // margin of error
909
- });
910
-
911
- onCleanup(() => cached[3].delete(version));
912
- }
913
942
  if (cached) {
914
943
  cached[0] = now;
915
944
  cached[1] = value;
916
945
  cached[2] = "preload";
917
- version && cached[3].add(version);
918
- } else cache.set(key, cached = [now, value,, new Set(version ? [version] : [])]);
946
+ } else {
947
+ cache.set(key, cached = [now, value,, createSignal(now)]);
948
+ cached[3].count = 0;
949
+ }
919
950
  };
920
951
  cache.clear = () => getCache().clear();
921
952
  function matchKey(key, keys) {
@@ -1148,11 +1179,13 @@ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "
1148
1179
 
1149
1180
  function Router(props) {
1150
1181
  if (isServer) return StaticRouter(props);
1182
+ const getSource = () => ({
1183
+ value: window.location.pathname + window.location.search + window.location.hash,
1184
+ state: window.history.state
1185
+ });
1186
+ const beforeLeave = createBeforeLeave();
1151
1187
  return createRouter({
1152
- get: () => ({
1153
- value: window.location.pathname + window.location.search + window.location.hash,
1154
- state: history.state
1155
- }),
1188
+ get: getSource,
1156
1189
  set({
1157
1190
  value,
1158
1191
  replace,
@@ -1160,16 +1193,27 @@ function Router(props) {
1160
1193
  state
1161
1194
  }) {
1162
1195
  if (replace) {
1163
- window.history.replaceState(state, "", value);
1196
+ window.history.replaceState(keepDepth(state), "", value);
1164
1197
  } else {
1165
1198
  window.history.pushState(state, "", value);
1166
1199
  }
1167
1200
  scrollToHash(window.location.hash.slice(1), scroll);
1201
+ saveCurrentDepth();
1168
1202
  },
1169
- init: notify => bindEvent(window, "popstate", () => notify()),
1203
+ init: notify => bindEvent(window, "popstate", notifyIfNotBlocked(notify, delta => {
1204
+ if (delta && delta < 0) {
1205
+ return !beforeLeave.confirm(delta);
1206
+ } else {
1207
+ const s = getSource();
1208
+ return !beforeLeave.confirm(s.value, {
1209
+ state: s.state
1210
+ });
1211
+ }
1212
+ })),
1170
1213
  create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase),
1171
1214
  utils: {
1172
- go: delta => window.history.go(delta)
1215
+ go: delta => window.history.go(delta),
1216
+ beforeLeave
1173
1217
  }
1174
1218
  })(props);
1175
1219
  }
@@ -1186,8 +1230,10 @@ function hashParser(str) {
1186
1230
  return to;
1187
1231
  }
1188
1232
  function HashRouter(props) {
1233
+ const getSource = () => window.location.hash.slice(1);
1234
+ const beforeLeave = createBeforeLeave();
1189
1235
  return createRouter({
1190
- get: () => window.location.hash.slice(1),
1236
+ get: getSource,
1191
1237
  set({
1192
1238
  value,
1193
1239
  replace,
@@ -1195,20 +1241,22 @@ function HashRouter(props) {
1195
1241
  state
1196
1242
  }) {
1197
1243
  if (replace) {
1198
- window.history.replaceState(state, "", "#" + value);
1244
+ window.history.replaceState(keepDepth(state), "", "#" + value);
1199
1245
  } else {
1200
1246
  window.location.hash = value;
1201
1247
  }
1202
1248
  const hashIndex = value.indexOf("#");
1203
1249
  const hash = hashIndex >= 0 ? value.slice(hashIndex + 1) : "";
1204
1250
  scrollToHash(hash, scroll);
1251
+ saveCurrentDepth();
1205
1252
  },
1206
- init: notify => bindEvent(window, "hashchange", () => notify()),
1253
+ init: notify => bindEvent(window, "hashchange", notifyIfNotBlocked(notify, delta => !beforeLeave.confirm(delta && delta < 0 ? delta : getSource()))),
1207
1254
  create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase),
1208
1255
  utils: {
1209
1256
  go: delta => window.history.go(delta),
1210
1257
  renderPath: path => `#${path}`,
1211
- parsePath: hashParser
1258
+ parsePath: hashParser,
1259
+ beforeLeave
1212
1260
  }
1213
1261
  })(props);
1214
1262
  }
@@ -1280,10 +1328,10 @@ function A(props) {
1280
1328
  const location = useLocation();
1281
1329
  const isActive = createMemo(() => {
1282
1330
  const to_ = to();
1283
- if (to_ === undefined) return false;
1331
+ if (to_ === undefined) return [false, false];
1284
1332
  const path = normalizePath(to_.split(/[?#]/, 1)[0]).toLowerCase();
1285
1333
  const loc = normalizePath(location.pathname).toLowerCase();
1286
- return props.end ? path === loc : loc.startsWith(path);
1334
+ return [props.end ? path === loc : loc.startsWith(path), path === loc];
1287
1335
  });
1288
1336
  return (() => {
1289
1337
  const _el$ = _tmpl$();
@@ -1299,14 +1347,14 @@ function A(props) {
1299
1347
  ...(props.class && {
1300
1348
  [props.class]: true
1301
1349
  }),
1302
- [props.inactiveClass]: !isActive(),
1303
- [props.activeClass]: isActive(),
1350
+ [props.inactiveClass]: !isActive()[0],
1351
+ [props.activeClass]: isActive()[0],
1304
1352
  ...rest.classList
1305
1353
  };
1306
1354
  },
1307
1355
  "link": "",
1308
1356
  get ["aria-current"]() {
1309
- return isActive() ? "page" : undefined;
1357
+ return isActive()[1] ? "page" : undefined;
1310
1358
  }
1311
1359
  }), false, false);
1312
1360
  return _el$;
@@ -1430,4 +1478,4 @@ function json(data, init) {
1430
1478
  return response;
1431
1479
  }
1432
1480
 
1433
- export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createBeforeLeave, createMemoryHistory, createRouter, json, redirect, reload, revalidate, useAction, useBeforeLeave, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
1481
+ export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createBeforeLeave, createMemoryHistory, createRouter, json, keepDepth, notifyIfNotBlocked, redirect, reload, revalidate, saveCurrentDepth, useAction, useBeforeLeave, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
package/dist/index.jsx CHANGED
@@ -1,6 +1,6 @@
1
- export * from "./routers";
2
- export * from "./components";
3
- export * from "./lifecycle";
4
- export { useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing";
5
- export { mergeSearchString as _mergeSearchString } from "./utils";
6
- export * from "./data";
1
+ export * from "./routers/index.js";
2
+ export * from "./components.jsx";
3
+ export * from "./lifecycle.js";
4
+ export { useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useBeforeLeave, } from "./routing.js";
5
+ export { mergeSearchString as _mergeSearchString } from "./utils.js";
6
+ export * from "./data/index.js";
@@ -1,2 +1,5 @@
1
- import { BeforeLeaveLifecycle } from "./types";
1
+ import { BeforeLeaveLifecycle, LocationChange } from "./types.js";
2
2
  export declare function createBeforeLeave(): BeforeLeaveLifecycle;
3
+ export declare function saveCurrentDepth(): void;
4
+ export declare function keepDepth(state: any): any;
5
+ export declare function notifyIfNotBlocked(notify: (value?: string | LocationChange) => void, block: (delta: number | null) => boolean): () => void;
package/dist/lifecycle.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { isServer } from "solid-js/web";
1
2
  export function createBeforeLeave() {
2
3
  let listeners = new Set();
3
4
  function subscribe(listener) {
@@ -30,3 +31,39 @@ export function createBeforeLeave() {
30
31
  confirm
31
32
  };
32
33
  }
34
+ // The following supports browser initiated blocking (eg back/forward)
35
+ let depth;
36
+ export function saveCurrentDepth() {
37
+ if (!window.history.state || window.history.state._depth == null) {
38
+ window.history.replaceState({ ...window.history.state, _depth: window.history.length - 1 }, "");
39
+ }
40
+ depth = window.history.state._depth;
41
+ }
42
+ if (!isServer) {
43
+ saveCurrentDepth();
44
+ }
45
+ export function keepDepth(state) {
46
+ return {
47
+ ...state,
48
+ _depth: window.history.state && window.history.state._depth
49
+ };
50
+ }
51
+ export function notifyIfNotBlocked(notify, block) {
52
+ let ignore = false;
53
+ return () => {
54
+ const prevDepth = depth;
55
+ saveCurrentDepth();
56
+ const delta = prevDepth == null ? null : depth - prevDepth;
57
+ if (ignore) {
58
+ ignore = false;
59
+ return;
60
+ }
61
+ if (delta && block(delta)) {
62
+ ignore = true;
63
+ window.history.go(-delta);
64
+ }
65
+ else {
66
+ notify();
67
+ }
68
+ };
69
+ }
@@ -1,5 +1,5 @@
1
1
  import type { JSX } from "solid-js";
2
- import type { BaseRouterProps } from "./components";
2
+ import type { BaseRouterProps } from "./components.js";
3
3
  export declare function hashParser(str: string): string;
4
4
  export type HashRouterProps = BaseRouterProps & {
5
5
  actionBase?: string;
@@ -1,5 +1,6 @@
1
- import { setupNativeEvents } from "../data/events";
2
- import { createRouter, scrollToHash, bindEvent } from "./createRouter";
1
+ import { setupNativeEvents } from "../data/events.js";
2
+ import { createRouter, scrollToHash, bindEvent } from "./createRouter.js";
3
+ import { createBeforeLeave, keepDepth, notifyIfNotBlocked, saveCurrentDepth } from "../lifecycle.js";
3
4
  export function hashParser(str) {
4
5
  const to = str.replace(/^.*?#/, "");
5
6
  // Hash-only hrefs like `#foo` from plain anchors will come in as `/#foo` whereas a link to
@@ -12,11 +13,13 @@ export function hashParser(str) {
12
13
  return to;
13
14
  }
14
15
  export function HashRouter(props) {
16
+ const getSource = () => window.location.hash.slice(1);
17
+ const beforeLeave = createBeforeLeave();
15
18
  return createRouter({
16
- get: () => window.location.hash.slice(1),
19
+ get: getSource,
17
20
  set({ value, replace, scroll, state }) {
18
21
  if (replace) {
19
- window.history.replaceState(state, "", "#" + value);
22
+ window.history.replaceState(keepDepth(state), "", "#" + value);
20
23
  }
21
24
  else {
22
25
  window.location.hash = value;
@@ -24,13 +27,15 @@ export function HashRouter(props) {
24
27
  const hashIndex = value.indexOf("#");
25
28
  const hash = hashIndex >= 0 ? value.slice(hashIndex + 1) : "";
26
29
  scrollToHash(hash, scroll);
30
+ saveCurrentDepth();
27
31
  },
28
- init: notify => bindEvent(window, "hashchange", () => notify()),
32
+ init: notify => bindEvent(window, "hashchange", notifyIfNotBlocked(notify, delta => !beforeLeave.confirm(delta && delta < 0 ? delta : getSource()))),
29
33
  create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase),
30
34
  utils: {
31
35
  go: delta => window.history.go(delta),
32
36
  renderPath: path => `#${path}`,
33
- parsePath: hashParser
37
+ parsePath: hashParser,
38
+ beforeLeave
34
39
  }
35
40
  })(props);
36
41
  }
@@ -1,5 +1,5 @@
1
- import type { LocationChange } from "../types";
2
- import type { BaseRouterProps } from "./components";
1
+ import type { LocationChange } from "../types.js";
2
+ import type { BaseRouterProps } from "./components.jsx";
3
3
  import type { JSX } from "solid-js";
4
4
  export type MemoryHistory = {
5
5
  get: () => string;
@@ -1,4 +1,4 @@
1
- import { createRouter, scrollToHash } from "./createRouter";
1
+ import { createRouter, scrollToHash } from "./createRouter.js";
2
2
  export function createMemoryHistory() {
3
3
  const entries = ["/"];
4
4
  let index = 0;
@@ -1,4 +1,4 @@
1
- import type { BaseRouterProps } from "./components";
1
+ import type { BaseRouterProps } from "./components.jsx";
2
2
  import type { JSX } from "solid-js";
3
3
  export type RouterProps = BaseRouterProps & {
4
4
  url?: string;
@@ -1,28 +1,41 @@
1
1
  import { isServer } from "solid-js/web";
2
- import { createRouter, scrollToHash, bindEvent } from "./createRouter";
3
- import { StaticRouter } from "./StaticRouter";
4
- import { setupNativeEvents } from "../data/events";
2
+ import { createRouter, scrollToHash, bindEvent } from "./createRouter.js";
3
+ import { StaticRouter } from "./StaticRouter.js";
4
+ import { setupNativeEvents } from "../data/events.js";
5
+ import { createBeforeLeave, keepDepth, notifyIfNotBlocked, saveCurrentDepth } from "../lifecycle.js";
5
6
  export function Router(props) {
6
7
  if (isServer)
7
8
  return StaticRouter(props);
9
+ const getSource = () => ({
10
+ value: window.location.pathname + window.location.search + window.location.hash,
11
+ state: window.history.state
12
+ });
13
+ const beforeLeave = createBeforeLeave();
8
14
  return createRouter({
9
- get: () => ({
10
- value: window.location.pathname + window.location.search + window.location.hash,
11
- state: history.state
12
- }),
15
+ get: getSource,
13
16
  set({ value, replace, scroll, state }) {
14
17
  if (replace) {
15
- window.history.replaceState(state, "", value);
18
+ window.history.replaceState(keepDepth(state), "", value);
16
19
  }
17
20
  else {
18
21
  window.history.pushState(state, "", value);
19
22
  }
20
23
  scrollToHash(window.location.hash.slice(1), scroll);
24
+ saveCurrentDepth();
21
25
  },
22
- init: notify => bindEvent(window, "popstate", () => notify()),
26
+ init: notify => bindEvent(window, "popstate", notifyIfNotBlocked(notify, delta => {
27
+ if (delta && delta < 0) {
28
+ return !beforeLeave.confirm(delta);
29
+ }
30
+ else {
31
+ const s = getSource();
32
+ return !beforeLeave.confirm(s.value, { state: s.state });
33
+ }
34
+ })),
23
35
  create: setupNativeEvents(props.preload, props.explicitLinks, props.actionBase),
24
36
  utils: {
25
- go: delta => window.history.go(delta)
37
+ go: delta => window.history.go(delta),
38
+ beforeLeave
26
39
  }
27
40
  })(props);
28
41
  }
@@ -1,4 +1,4 @@
1
- import { type BaseRouterProps } from "./components";
1
+ import { type BaseRouterProps } from "./components.jsx";
2
2
  import type { JSX } from "solid-js";
3
3
  export type StaticRouterProps = BaseRouterProps & {
4
4
  url?: string;
@@ -1,5 +1,5 @@
1
1
  import { getRequestEvent } from "solid-js/web";
2
- import { createRouterComponent } from "./components";
2
+ import { createRouterComponent } from "./components.jsx";
3
3
  function getPath(url) {
4
4
  const u = new URL(url);
5
5
  return u.pathname + u.search;
@@ -1,5 +1,5 @@
1
1
  import type { Component, JSX } from "solid-js";
2
- import type { MatchFilters, RouteLoadFunc, RouteDefinition, RouterIntegration, RouteSectionProps } from "../types";
2
+ import type { MatchFilters, RouteLoadFunc, RouteDefinition, RouterIntegration, RouteSectionProps } from "../types.ts";
3
3
  export type BaseRouterProps = {
4
4
  base?: string;
5
5
  /**
@@ -15,6 +15,6 @@ export type RouteProps<S extends string, T = unknown> = {
15
15
  load?: RouteLoadFunc<T>;
16
16
  matchFilters?: MatchFilters<S>;
17
17
  component?: Component<RouteSectionProps<T>>;
18
- metadata?: Record<string, any>;
18
+ info?: Record<string, any>;
19
19
  };
20
20
  export declare const Route: <S extends string, T = unknown>(props: RouteProps<S, T>) => JSX.Element;
@@ -1,8 +1,8 @@
1
1
  /*@refresh skip*/
2
2
  import { getRequestEvent, isServer } from "solid-js/web";
3
3
  import { children, createMemo, createRoot, mergeProps, on, Show } from "solid-js";
4
- import { createBranches, createRouteContext, createRouterContext, getRouteMatches, RouteContextObj, RouterContextObj } from "../routing";
5
- import { createMemoObject } from "../utils";
4
+ import { createBranches, createRouteContext, createRouterContext, getRouteMatches, RouteContextObj, RouterContextObj } from "../routing.js";
5
+ import { createMemoObject } from "../utils.js";
6
6
  export const createRouterComponent = (router) => (props) => {
7
7
  const { base } = props;
8
8
  const routeDefs = children(() => props.children);
@@ -18,13 +18,14 @@ function Routes(props) {
18
18
  if (isServer) {
19
19
  const e = getRequestEvent();
20
20
  e &&
21
- (e.routerMatches || (e.routerMatches = [])).push(matches().map(({ route, path, params }) => ({
22
- path: route.originalPath,
23
- pattern: route.pattern,
24
- match: path,
25
- params,
26
- metadata: route.metadata
27
- })));
21
+ ((e.router || (e.router = {})).matches ||
22
+ (e.router.matches = matches().map(({ route, path, params }) => ({
23
+ path: route.originalPath,
24
+ pattern: route.pattern,
25
+ match: path,
26
+ params,
27
+ info: route.info
28
+ }))));
28
29
  }
29
30
  const params = createMemoObject(() => {
30
31
  const m = matches();
@@ -1,10 +1,10 @@
1
- import type { LocationChange, RouterContext, RouterUtils } from "../types";
1
+ import type { LocationChange, RouterContext, RouterUtils } from "../types.ts";
2
2
  export declare function createRouter(config: {
3
3
  get: () => string | LocationChange;
4
4
  set: (next: LocationChange) => void;
5
5
  init?: (notify: (value?: string | LocationChange) => void) => () => void;
6
6
  create?: (router: RouterContext) => void;
7
7
  utils?: Partial<RouterUtils>;
8
- }): (props: import("./components").BaseRouterProps) => import("solid-js").JSX.Element;
8
+ }): (props: import("./components.jsx").BaseRouterProps) => import("solid-js").JSX.Element;
9
9
  export declare function bindEvent(target: EventTarget, type: string, handler: EventListener): () => void;
10
10
  export declare function scrollToHash(hash: string, fallbackTop?: boolean): void;
@@ -1,5 +1,5 @@
1
1
  import { createSignal, onCleanup } from "solid-js";
2
- import { createRouterComponent } from "./components";
2
+ import { createRouterComponent } from "./components.jsx";
3
3
  function intercept([value, setValue], get, set) {
4
4
  return [get ? () => get(value()) : value, set ? (v) => setValue(set(v)) : setValue];
5
5
  }
@@ -1,11 +1,11 @@
1
- export { Route } from "./components";
2
- export type { BaseRouterProps, RouteProps } from "./components";
3
- export { createRouter } from "./createRouter";
4
- export { Router } from "./Router";
5
- export type { RouterProps } from "./Router";
6
- export { HashRouter } from "./HashRouter";
7
- export type { HashRouterProps } from "./HashRouter";
8
- export { MemoryRouter, createMemoryHistory } from "./MemoryRouter";
9
- export type { MemoryRouterProps, MemoryHistory } from "./MemoryRouter";
10
- export { StaticRouter } from "./StaticRouter";
11
- export type { StaticRouterProps } from "./StaticRouter";
1
+ export { Route } from "./components.jsx";
2
+ export type { BaseRouterProps, RouteProps } from "./components.jsx";
3
+ export { createRouter } from "./createRouter.js";
4
+ export { Router } from "./Router.js";
5
+ export type { RouterProps } from "./Router.js";
6
+ export { HashRouter } from "./HashRouter.js";
7
+ export type { HashRouterProps } from "./HashRouter.js";
8
+ export { MemoryRouter, createMemoryHistory } from "./MemoryRouter.js";
9
+ export type { MemoryRouterProps, MemoryHistory } from "./MemoryRouter.js";
10
+ export { StaticRouter } from "./StaticRouter.js";
11
+ export type { StaticRouterProps } from "./StaticRouter.js";
@@ -1,6 +1,6 @@
1
- export { Route } from "./components";
2
- export { createRouter } from "./createRouter";
3
- export { Router } from "./Router";
4
- export { HashRouter } from "./HashRouter";
5
- export { MemoryRouter, createMemoryHistory } from "./MemoryRouter";
6
- export { StaticRouter } from "./StaticRouter";
1
+ export { Route } from "./components.jsx";
2
+ export { createRouter } from "./createRouter.js";
3
+ export { Router } from "./Router.js";
4
+ export { HashRouter } from "./HashRouter.js";
5
+ export { MemoryRouter, createMemoryHistory } from "./MemoryRouter.js";
6
+ export { StaticRouter } from "./StaticRouter.js";
package/dist/routing.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { JSX, Accessor } from "solid-js";
2
- import type { BeforeLeaveEventArgs, Branch, Intent, Location, MatchFilters, NavigateOptions, Navigator, Params, Route, RouteContext, RouteDefinition, RouteMatch, RouterContext, RouterIntegration, SetParams } from "./types";
2
+ import type { BeforeLeaveEventArgs, Branch, Intent, Location, MatchFilters, NavigateOptions, Navigator, Params, Route, RouteContext, RouteDefinition, RouteMatch, RouterContext, RouterIntegration, SetParams } from "./types.js";
3
3
  export declare const RouterContextObj: import("solid-js").Context<RouterContext | undefined>;
4
4
  export declare const RouteContextObj: import("solid-js").Context<RouteContext | undefined>;
5
5
  export declare const useRouter: () => RouterContext;
@@ -9,7 +9,7 @@ export declare const useHref: (to: () => string | undefined) => Accessor<string
9
9
  export declare const useNavigate: () => Navigator;
10
10
  export declare const useLocation: <S = unknown>() => Location<S>;
11
11
  export declare const useIsRouting: () => () => boolean;
12
- export declare const useMatch: <S extends string>(path: () => S, matchFilters?: MatchFilters<S> | undefined) => Accessor<import("./types").PathMatch | undefined>;
12
+ export declare const useMatch: <S extends string>(path: () => S, matchFilters?: MatchFilters<S> | undefined) => Accessor<import("./types.js").PathMatch | undefined>;
13
13
  export declare const useParams: <T extends Params>() => T;
14
14
  export declare const useSearchParams: <T extends Params>() => [Partial<T>, (params: SetParams, options?: Partial<NavigateOptions>) => void];
15
15
  export declare const useBeforeLeave: (listener: (e: BeforeLeaveEventArgs) => void) => void;
package/dist/routing.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createComponent, createContext, createMemo, createRenderEffect, createSignal, on, onCleanup, untrack, useContext, startTransition, resetErrorBoundaries } from "solid-js";
2
2
  import { isServer, getRequestEvent } from "solid-js/web";
3
- import { createBeforeLeave } from "./lifecycle";
4
- import { mockBase, createMemoObject, extractSearchParams, invariant, resolvePath, createMatcher, joinPaths, scoreRoute, mergeSearchString, expandOptionals } from "./utils";
3
+ import { createBeforeLeave } from "./lifecycle.js";
4
+ import { mockBase, createMemoObject, extractSearchParams, invariant, resolvePath, createMatcher, joinPaths, scoreRoute, mergeSearchString, expandOptionals } from "./utils.js";
5
5
  const MAX_REDIRECTS = 100;
6
6
  export const RouterContextObj = createContext();
7
7
  export const RouteContextObj = createContext();
@@ -56,21 +56,24 @@ export const useBeforeLeave = (listener) => {
56
56
  onCleanup(s);
57
57
  };
58
58
  export function createRoutes(routeDef, base = "") {
59
- const { component, load, children, metadata } = routeDef;
59
+ const { component, load, children, info } = routeDef;
60
60
  const isLeaf = !children || (Array.isArray(children) && !children.length);
61
61
  const shared = {
62
62
  key: routeDef,
63
63
  component,
64
64
  load,
65
- metadata
65
+ info
66
66
  };
67
67
  return asArray(routeDef.path).reduce((acc, path) => {
68
68
  for (const originalPath of expandOptionals(path)) {
69
69
  const path = joinPaths(base, originalPath);
70
70
  let pattern = isLeaf ? path : path.split("/*", 1)[0];
71
- pattern = pattern.split("/").map((s) => {
72
- return (s.startsWith(':') || s.startsWith('*')) ? s : encodeURIComponent(s);
73
- }).join("/");
71
+ pattern = pattern
72
+ .split("/")
73
+ .map((s) => {
74
+ return s.startsWith(":") || s.startsWith("*") ? s : encodeURIComponent(s);
75
+ })
76
+ .join("/");
74
77
  acc.push({
75
78
  ...shared,
76
79
  originalPath,
@@ -252,7 +255,7 @@ export function createRouterContext(integration, getBranches, options = {}) {
252
255
  // A delta of 0 means stay at the current location, so it is ignored
253
256
  }
254
257
  else if (utils.go) {
255
- beforeLeave.confirm(to, options) && utils.go(to);
258
+ utils.go(to);
256
259
  }
257
260
  else {
258
261
  console.warn("Router integration does not support relative routing");
@@ -276,8 +279,7 @@ export function createRouterContext(integration, getBranches, options = {}) {
276
279
  if (resolvedTo !== current || nextState !== state()) {
277
280
  if (isServer) {
278
281
  const e = getRequestEvent();
279
- e &&
280
- (e.response = new Response(null, { status: 302, headers: { Location: resolvedTo } }));
282
+ e && (e.response = { status: 302, headers: new Headers({ Location: resolvedTo }) });
281
283
  setSource({ value: resolvedTo, replace, scroll, state: nextState });
282
284
  }
283
285
  else if (beforeLeave.confirm(resolvedTo, options)) {
@@ -347,7 +349,7 @@ export function createRouterContext(integration, getBranches, options = {}) {
347
349
  }
348
350
  function initFromFlash() {
349
351
  const e = getRequestEvent();
350
- return e && e.initialSubmission ? [e.initialSubmission] : [];
352
+ return e && e.router && e.router.submission ? [e.router.submission] : [];
351
353
  }
352
354
  }
353
355
  export function createRouteContext(router, parent, outlet, match, params) {
package/dist/types.d.ts CHANGED
@@ -1,10 +1,16 @@
1
1
  import type { Component, JSX, Signal } from "solid-js";
2
2
  declare module "solid-js/web" {
3
3
  interface RequestEvent {
4
- response?: Response;
5
- routerMatches?: OutputMatch[][];
6
- routerCache?: Map<any, any>;
7
- initialSubmission?: Submission<any, any>;
4
+ response?: {
5
+ status?: number;
6
+ statusText?: string;
7
+ headers: Headers;
8
+ };
9
+ router?: {
10
+ matches?: OutputMatch[];
11
+ cache?: Map<string, CacheEntry>;
12
+ submission?: Submission<any, any>;
13
+ };
8
14
  serverOnly?: boolean;
9
15
  }
10
16
  }
@@ -61,7 +67,7 @@ export type RouteDefinition<S extends string | string[] = any, T = unknown> = {
61
67
  load?: RouteLoadFunc<T>;
62
68
  children?: RouteDefinition | RouteDefinition[];
63
69
  component?: Component<RouteSectionProps<T>>;
64
- metadata?: Record<string, any>;
70
+ info?: Record<string, any>;
65
71
  };
66
72
  export type MatchFilter = readonly string[] | RegExp | ((s: string) => boolean);
67
73
  export type PathParams<P extends string | readonly string[]> = P extends `${infer Head}/${infer Tail}` ? [...PathParams<Head>, ...PathParams<Tail>] : P extends `:${infer S}?` ? [S] : P extends `:${infer S}` ? [S] : P extends `*${infer S}` ? [S] : [];
@@ -80,7 +86,7 @@ export interface OutputMatch {
80
86
  pattern: string;
81
87
  match: string;
82
88
  params: Params;
83
- metadata?: Record<string, any>;
89
+ info?: Record<string, any>;
84
90
  }
85
91
  export interface Route {
86
92
  key: unknown;
@@ -90,7 +96,7 @@ export interface Route {
90
96
  load?: RouteLoadFunc;
91
97
  matcher: (location: string) => PathMatch | null;
92
98
  matchFilters?: MatchFilters;
93
- metadata?: Record<string, any>;
99
+ info?: Record<string, any>;
94
100
  }
95
101
  export interface Branch {
96
102
  routes: Route[];
@@ -151,3 +157,6 @@ export type Submission<T, U> = {
151
157
  export interface MaybePreloadableComponent extends Component {
152
158
  preload?: () => void;
153
159
  }
160
+ export type CacheEntry = [number, any, Intent | undefined, Signal<number> & {
161
+ count: number;
162
+ }];
package/dist/utils.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { MatchFilters, Params, PathMatch, Route, SetParams } from "./types";
1
+ import type { MatchFilters, Params, PathMatch, Route, SetParams } from "./types.ts";
2
2
  export declare const mockBase = "http://sr";
3
3
  export declare const redirectStatusCodes: Set<number>;
4
4
  export declare function normalizePath(path: string, omitSlash?: boolean): string;
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "Ryan Turnquist"
7
7
  ],
8
8
  "license": "MIT",
9
- "version": "0.10.10",
9
+ "version": "0.11.1",
10
10
  "homepage": "https://github.com/solidjs/solid-router#readme",
11
11
  "repository": {
12
12
  "type": "git",
@@ -29,34 +29,31 @@
29
29
  ],
30
30
  "sideEffects": false,
31
31
  "devDependencies": {
32
- "@babel/core": "^7.18.13",
33
- "@babel/preset-typescript": "^7.18.6",
34
- "@rollup/plugin-babel": "6.0.3",
35
- "@rollup/plugin-node-resolve": "15.0.1",
36
- "@rollup/plugin-terser": "0.2.0",
37
- "@types/jest": "^29.0.0",
38
- "@types/node": "^20.9.0",
39
- "babel-jest": "^29.0.1",
32
+ "@babel/core": "^7.23.9",
33
+ "@babel/preset-typescript": "^7.23.3",
34
+ "@changesets/cli": "^2.27.1",
35
+ "@rollup/plugin-babel": "6.0.4",
36
+ "@rollup/plugin-node-resolve": "15.2.3",
37
+ "@rollup/plugin-terser": "0.4.4",
38
+ "@types/jest": "^29.5.11",
39
+ "@types/node": "^20.11.14",
40
40
  "babel-preset-solid": "^1.8.6",
41
- "jest": "^29.0.1",
42
- "jest-environment-jsdom": "^29.2.1",
43
- "prettier": "^2.7.1",
44
- "rollup": "^3.7.5",
45
- "solid-jest": "^0.2.0",
41
+ "jsdom": "^24.0.0",
42
+ "prettier": "^2.7.0",
43
+ "rollup": "^4.9.6",
46
44
  "solid-js": "^1.8.7",
47
- "typescript": "^5.2.2"
45
+ "typescript": "^5.3.3",
46
+ "vite": "^5.0.12",
47
+ "vite-plugin-solid": "^2.9.1",
48
+ "vitest": "^1.2.2"
48
49
  },
49
50
  "peerDependencies": {
50
51
  "solid-js": "^1.8.6"
51
52
  },
52
- "jest": {
53
- "preset": "solid-jest/preset/browser"
54
- },
55
53
  "scripts": {
56
54
  "build": "tsc && rollup -c",
57
- "test": "jest && npm run test:types",
58
- "test:watch": "jest --watch",
59
- "test:coverage": "jest --coverage && npm run test:types",
55
+ "test": "vitest run && npm run test:types",
56
+ "test:watch": "vitest",
60
57
  "test:types": "tsc --project tsconfig.test.json",
61
58
  "pretty": "prettier --write \"{src,test}/**/*.{ts,tsx}\""
62
59
  }