@solidjs/router 0.14.8 → 0.14.10

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.
@@ -1,13 +1,13 @@
1
1
  import { JSX } from "solid-js";
2
2
  import type { Submission, SubmissionStub, NarrowResponse } from "../types.js";
3
- export type Action<T extends Array<any>, U> = (T extends [FormData] | [] ? JSX.SerializableAttributeValue : unknown) & ((...vars: T) => Promise<NarrowResponse<U>>) & {
3
+ export type Action<T extends Array<any>, U, V = T> = (T extends [FormData] | [] ? JSX.SerializableAttributeValue : unknown) & ((...vars: T) => Promise<NarrowResponse<U>>) & {
4
4
  url: string;
5
- with<A extends any[], B extends any[]>(this: (this: any, ...args: [...A, ...B]) => Promise<NarrowResponse<U>>, ...args: A): Action<B, U>;
5
+ with<A extends any[], B extends any[]>(this: (this: any, ...args: [...A, ...B]) => Promise<NarrowResponse<U>>, ...args: A): Action<B, U, V>;
6
6
  };
7
- export declare const actions: Map<string, Action<any, any>>;
8
- export declare function useSubmissions<T extends Array<any>, U>(fn: Action<T, U>, filter?: (arg: T) => boolean): Submission<T, NarrowResponse<U>>[] & {
7
+ export declare const actions: Map<string, Action<any, any, any>>;
8
+ export declare function useSubmissions<T extends Array<any>, U, V>(fn: Action<T, U, V>, filter?: (input: V) => boolean): Submission<T, NarrowResponse<U>>[] & {
9
9
  pending: boolean;
10
10
  };
11
- export declare function useSubmission<T extends Array<any>, U>(fn: Action<T, U>, filter?: (arg: T) => boolean): Submission<T, NarrowResponse<U>> | SubmissionStub;
12
- export declare function useAction<T extends Array<any>, U>(action: Action<T, U>): (...args: Parameters<Action<T, U>>) => Promise<NarrowResponse<U>>;
13
- export declare function action<T extends Array<any>, U = void>(fn: (...args: T) => Promise<U>, name?: string): Action<T, U>;
11
+ export declare function useSubmission<T extends Array<any>, U, V>(fn: Action<T, U, V>, filter?: (input: V) => boolean): Submission<T, NarrowResponse<U>> | SubmissionStub;
12
+ export declare function useAction<T extends Array<any>, U, V>(action: Action<T, U, V>): (...args: Parameters<Action<T, U, V>>) => Promise<NarrowResponse<U>>;
13
+ export declare function action<T extends Array<any>, U = void>(fn: (...args: T) => Promise<U>, name?: string): Action<T, U, T>;
@@ -6,7 +6,7 @@ import { cacheKeyOp, hashKey, revalidate, cache } from "./cache.js";
6
6
  export const actions = /* #__PURE__ */ new Map();
7
7
  export function useSubmissions(fn, filter) {
8
8
  const router = useRouter();
9
- const subs = createMemo(() => router.submissions[0]().filter(s => s.url === fn.toString() && (!filter || filter(s.input))));
9
+ const subs = createMemo(() => router.submissions[0]().filter(s => s.url === fn.base && (!filter || filter(s.input))));
10
10
  return new Proxy([], {
11
11
  get(_, property) {
12
12
  if (property === $TRACK)
@@ -83,6 +83,7 @@ export function action(fn, name) {
83
83
  const url = fn.url ||
84
84
  (name && `https://action/${name}`) ||
85
85
  (!isServer ? `https://action/${hashString(fn.toString())}` : "");
86
+ mutate.base = url;
86
87
  return toAction(mutate, url);
87
88
  }
88
89
  function toAction(fn, url) {
@@ -95,6 +96,7 @@ function toAction(fn, url) {
95
96
  const newFn = function (...passedArgs) {
96
97
  return fn.call(this, ...args, ...passedArgs);
97
98
  };
99
+ newFn.base = fn.base;
98
100
  const uri = new URL(url, mockBase);
99
101
  uri.searchParams.set("args", hashKey(args));
100
102
  return toAction(newFn, (uri.origin === "https://action" ? uri.origin : "") + uri.pathname + uri.search);
@@ -184,7 +184,7 @@ cache.set = (key, value) => {
184
184
  cache.clear = () => getCache().clear();
185
185
  function matchKey(key, keys) {
186
186
  for (let k of keys) {
187
- if (key.startsWith(k))
187
+ if (k && key.startsWith(k))
188
188
  return true;
189
189
  }
190
190
  return false;
@@ -59,7 +59,7 @@ export function setupNativeEvents(preload = true, explicitLinks = false, actionB
59
59
  return;
60
60
  const [a, url] = res;
61
61
  transformUrl && (url.pathname = transformUrl(url.pathname));
62
- router.preloadRoute(url, { preloadData: a.getAttribute("preload") !== "false" });
62
+ router.preloadRoute(url, a.getAttribute("preload") !== "false");
63
63
  }
64
64
  function handleAnchorMove(evt) {
65
65
  clearTimeout(preloadTimeout);
@@ -71,7 +71,7 @@ export function setupNativeEvents(preload = true, explicitLinks = false, actionB
71
71
  return;
72
72
  transformUrl && (url.pathname = transformUrl(url.pathname));
73
73
  preloadTimeout = setTimeout(() => {
74
- router.preloadRoute(url, { preloadData: a.getAttribute("preload") !== "false" });
74
+ router.preloadRoute(url, a.getAttribute("preload") !== "false");
75
75
  lastElement = a;
76
76
  }, 20);
77
77
  }
@@ -12,7 +12,7 @@ export function redirect(url, init = 302) {
12
12
  }
13
13
  const headers = new Headers(responseInit.headers);
14
14
  headers.set("Location", url);
15
- revalidate && headers.set("X-Revalidate", revalidate.toString());
15
+ revalidate !== undefined && headers.set("X-Revalidate", revalidate.toString());
16
16
  const response = new Response(null, {
17
17
  ...responseInit,
18
18
  headers: headers
@@ -22,7 +22,7 @@ export function redirect(url, init = 302) {
22
22
  export function reload(init = {}) {
23
23
  const { revalidate, ...responseInit } = init;
24
24
  const headers = new Headers(responseInit.headers);
25
- revalidate && headers.set("X-Revalidate", revalidate.toString());
25
+ revalidate !== undefined && headers.set("X-Revalidate", revalidate.toString());
26
26
  return new Response(null, {
27
27
  ...responseInit,
28
28
  headers
@@ -31,7 +31,7 @@ export function reload(init = {}) {
31
31
  export function json(data, init = {}) {
32
32
  const { revalidate, ...responseInit } = init;
33
33
  const headers = new Headers(responseInit.headers);
34
- revalidate && headers.set("X-Revalidate", revalidate.toString());
34
+ revalidate !== undefined && headers.set("X-Revalidate", revalidate.toString());
35
35
  headers.set("Content-Type", "application/json");
36
36
  const response = new Response(JSON.stringify(data), {
37
37
  ...responseInit,
package/dist/index.js CHANGED
@@ -111,7 +111,9 @@ function joinPaths(from, to) {
111
111
  function extractSearchParams(url) {
112
112
  const params = {};
113
113
  url.searchParams.forEach((value, key) => {
114
- params[key] = value;
114
+ if (key in params) {
115
+ if (Array.isArray(params[key])) params[key].push(value);else params[key] = [params[key], value];
116
+ } else params[key] = value;
115
117
  });
116
118
  return params;
117
119
  }
@@ -197,10 +199,18 @@ function createMemoObject(fn) {
197
199
  function mergeSearchString(search, params) {
198
200
  const merged = new URLSearchParams(search);
199
201
  Object.entries(params).forEach(([key, value]) => {
200
- if (value == null || value === "") {
202
+ if (value == null || value === "" || value instanceof Array && !value.length) {
201
203
  merged.delete(key);
202
204
  } else {
203
- merged.set(key, String(value));
205
+ if (value instanceof Array) {
206
+ // Delete all instances of the key before appending
207
+ merged.delete(key);
208
+ value.forEach(v => {
209
+ merged.append(key, String(v));
210
+ });
211
+ } else {
212
+ merged.set(key, String(value));
213
+ }
204
214
  }
205
215
  });
206
216
  const s = merged.toString();
@@ -244,7 +254,10 @@ const useHref = to => {
244
254
  const useNavigate = () => useRouter().navigatorFactory();
245
255
  const useLocation = () => useRouter().location;
246
256
  const useIsRouting = () => useRouter().isRouting;
247
- const usePreloadRoute = () => useRouter().preloadRoute;
257
+ const usePreloadRoute = () => {
258
+ const pre = useRouter().preloadRoute;
259
+ return (url, options = {}) => pre(url instanceof URL ? url : new URL(url, mockBase), options.preloadData);
260
+ };
248
261
  const useMatch = (path, matchFilters) => {
249
262
  const location = useLocation();
250
263
  const matchers = createMemo(() => expandOptionals(path()).map(path => createMatcher(path, undefined, matchFilters)));
@@ -585,7 +598,7 @@ function createRouterContext(integration, branches, getContext, options = {}) {
585
598
  referrers.length = 0;
586
599
  }
587
600
  }
588
- function preloadRoute(url, options = {}) {
601
+ function preloadRoute(url, preloadData) {
589
602
  const matches = getRouteMatches(branches(), url.pathname);
590
603
  const prevIntent = intent;
591
604
  intent = "preload";
@@ -599,7 +612,7 @@ function createRouterContext(integration, branches, getContext, options = {}) {
599
612
  preload
600
613
  } = route;
601
614
  inPreloadFn = true;
602
- options.preloadData && preload && runWithOwner(getContext(), () => preload({
615
+ preloadData && preload && runWithOwner(getContext(), () => preload({
603
616
  params,
604
617
  location: {
605
618
  pathname: url.pathname,
@@ -1032,7 +1045,7 @@ cache.set = (key, value) => {
1032
1045
  cache.clear = () => getCache().clear();
1033
1046
  function matchKey(key, keys) {
1034
1047
  for (let k of keys) {
1035
- if (key.startsWith(k)) return true;
1048
+ if (k && key.startsWith(k)) return true;
1036
1049
  }
1037
1050
  return false;
1038
1051
  }
@@ -1053,7 +1066,7 @@ function isPlainObject(obj) {
1053
1066
  const actions = /* #__PURE__ */new Map();
1054
1067
  function useSubmissions(fn, filter) {
1055
1068
  const router = useRouter();
1056
- const subs = createMemo(() => router.submissions[0]().filter(s => s.url === fn.toString() && (!filter || filter(s.input))));
1069
+ const subs = createMemo(() => router.submissions[0]().filter(s => s.url === fn.base && (!filter || filter(s.input))));
1057
1070
  return new Proxy([], {
1058
1071
  get(_, property) {
1059
1072
  if (property === $TRACK) return subs();
@@ -1124,6 +1137,7 @@ function action(fn, name) {
1124
1137
  return p.then(handler(), handler(true));
1125
1138
  }
1126
1139
  const url = fn.url || name && `https://action/${name}` || (!isServer ? `https://action/${hashString(fn.toString())}` : "");
1140
+ mutate.base = url;
1127
1141
  return toAction(mutate, url);
1128
1142
  }
1129
1143
  function toAction(fn, url) {
@@ -1135,6 +1149,7 @@ function toAction(fn, url) {
1135
1149
  const newFn = function (...passedArgs) {
1136
1150
  return fn.call(this, ...args, ...passedArgs);
1137
1151
  };
1152
+ newFn.base = fn.base;
1138
1153
  const uri = new URL(url, mockBase);
1139
1154
  uri.searchParams.set("args", hashKey(args));
1140
1155
  return toAction(newFn, (uri.origin === "https://action" ? uri.origin : "") + uri.pathname + uri.search);
@@ -1226,9 +1241,7 @@ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "
1226
1241
  if (!res) return;
1227
1242
  const [a, url] = res;
1228
1243
  transformUrl && (url.pathname = transformUrl(url.pathname));
1229
- router.preloadRoute(url, {
1230
- preloadData: a.getAttribute("preload") !== "false"
1231
- });
1244
+ router.preloadRoute(url, a.getAttribute("preload") !== "false");
1232
1245
  }
1233
1246
  function handleAnchorMove(evt) {
1234
1247
  clearTimeout(preloadTimeout);
@@ -1238,9 +1251,7 @@ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "
1238
1251
  if (lastElement === a) return;
1239
1252
  transformUrl && (url.pathname = transformUrl(url.pathname));
1240
1253
  preloadTimeout = setTimeout(() => {
1241
- router.preloadRoute(url, {
1242
- preloadData: a.getAttribute("preload") !== "false"
1243
- });
1254
+ router.preloadRoute(url, a.getAttribute("preload") !== "false");
1244
1255
  lastElement = a;
1245
1256
  }, 20);
1246
1257
  }
@@ -1612,7 +1623,7 @@ function redirect(url, init = 302) {
1612
1623
  }
1613
1624
  const headers = new Headers(responseInit.headers);
1614
1625
  headers.set("Location", url);
1615
- revalidate && headers.set("X-Revalidate", revalidate.toString());
1626
+ revalidate !== undefined && headers.set("X-Revalidate", revalidate.toString());
1616
1627
  const response = new Response(null, {
1617
1628
  ...responseInit,
1618
1629
  headers: headers
@@ -1625,7 +1636,7 @@ function reload(init = {}) {
1625
1636
  ...responseInit
1626
1637
  } = init;
1627
1638
  const headers = new Headers(responseInit.headers);
1628
- revalidate && headers.set("X-Revalidate", revalidate.toString());
1639
+ revalidate !== undefined && headers.set("X-Revalidate", revalidate.toString());
1629
1640
  return new Response(null, {
1630
1641
  ...responseInit,
1631
1642
  headers
@@ -1637,7 +1648,7 @@ function json(data, init = {}) {
1637
1648
  ...responseInit
1638
1649
  } = init;
1639
1650
  const headers = new Headers(responseInit.headers);
1640
- revalidate && headers.set("X-Revalidate", revalidate.toString());
1651
+ revalidate !== undefined && headers.set("X-Revalidate", revalidate.toString());
1641
1652
  headers.set("Content-Type", "application/json");
1642
1653
  const response = new Response(JSON.stringify(data), {
1643
1654
  ...responseInit,
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, RouteDescription, RouteContext, RouteDefinition, RouteMatch, RouterContext, RouterIntegration, SetParams } from "./types.js";
2
+ import type { BeforeLeaveEventArgs, Branch, Intent, Location, MatchFilters, NavigateOptions, Navigator, Params, RouteDescription, RouteContext, RouteDefinition, RouteMatch, RouterContext, RouterIntegration, SearchParams, SetSearchParams } 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,13 +9,13 @@ 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 usePreloadRoute: () => (url: URL, options: {
13
- preloadData?: boolean | undefined;
12
+ export declare const usePreloadRoute: () => (url: string | URL, options?: {
13
+ preloadData?: boolean;
14
14
  }) => void;
15
15
  export declare const useMatch: <S extends string>(path: () => S, matchFilters?: MatchFilters<S> | undefined) => Accessor<import("./types.js").PathMatch | undefined>;
16
16
  export declare const useCurrentMatches: () => () => RouteMatch[];
17
17
  export declare const useParams: <T extends Params>() => T;
18
- export declare const useSearchParams: <T extends Params>() => [Partial<T>, (params: SetParams, options?: Partial<NavigateOptions>) => void];
18
+ export declare const useSearchParams: <T extends SearchParams>() => [Partial<T>, (params: SetSearchParams, options?: Partial<NavigateOptions>) => void];
19
19
  export declare const useBeforeLeave: (listener: (e: BeforeLeaveEventArgs) => void) => void;
20
20
  export declare function createRoutes(routeDef: RouteDefinition, base?: string): RouteDescription[];
21
21
  export declare function createBranch(routes: RouteDescription[], index?: number): Branch;
package/dist/routing.js CHANGED
@@ -23,7 +23,10 @@ export const useHref = (to) => {
23
23
  export const useNavigate = () => useRouter().navigatorFactory();
24
24
  export const useLocation = () => useRouter().location;
25
25
  export const useIsRouting = () => useRouter().isRouting;
26
- export const usePreloadRoute = () => useRouter().preloadRoute;
26
+ export const usePreloadRoute = () => {
27
+ const pre = useRouter().preloadRoute;
28
+ return (url, options = {}) => pre(url instanceof URL ? url : new URL(url, mockBase), options.preloadData);
29
+ };
27
30
  export const useMatch = (path, matchFilters) => {
28
31
  const location = useLocation();
29
32
  const matchers = createMemo(() => expandOptionals(path()).map(path => createMatcher(path, undefined, matchFilters)));
@@ -348,7 +351,7 @@ export function createRouterContext(integration, branches, getContext, options =
348
351
  referrers.length = 0;
349
352
  }
350
353
  }
351
- function preloadRoute(url, options = {}) {
354
+ function preloadRoute(url, preloadData) {
352
355
  const matches = getRouteMatches(branches(), url.pathname);
353
356
  const prevIntent = intent;
354
357
  intent = "preload";
@@ -359,7 +362,7 @@ export function createRouterContext(integration, branches, getContext, options =
359
362
  route.component.preload();
360
363
  const { preload } = route;
361
364
  inPreloadFn = true;
362
- options.preloadData &&
365
+ preloadData &&
363
366
  preload &&
364
367
  runWithOwner(getContext(), () => preload({
365
368
  params,
package/dist/types.d.ts CHANGED
@@ -22,14 +22,16 @@ declare module "solid-js/web" {
22
22
  }
23
23
  }
24
24
  export type Params = Record<string, string>;
25
+ export type SearchParams = Record<string, string | string[]>;
25
26
  export type SetParams = Record<string, string | number | boolean | null | undefined>;
27
+ export type SetSearchParams = Record<string, string | string[] | number | number[] | boolean | boolean[] | null | undefined>;
26
28
  export interface Path {
27
29
  pathname: string;
28
30
  search: string;
29
31
  hash: string;
30
32
  }
31
33
  export interface Location<S = unknown> extends Path {
32
- query: Params;
34
+ query: SearchParams;
33
35
  state: Readonly<Partial<S>> | null;
34
36
  key: string;
35
37
  }
@@ -127,7 +129,7 @@ export interface RouterUtils {
127
129
  go(delta: number): void;
128
130
  beforeLeave: BeforeLeaveLifecycle;
129
131
  paramsWrapper: (getParams: () => Params, branches: () => Branch[]) => Params;
130
- queryWrapper: (getQuery: () => Params) => Params;
132
+ queryWrapper: (getQuery: () => SearchParams) => SearchParams;
131
133
  }
132
134
  export interface RouterContext {
133
135
  base: RouteContext;
@@ -139,9 +141,7 @@ export interface RouterContext {
139
141
  renderPath(path: string): string;
140
142
  parsePath(str: string): string;
141
143
  beforeLeave: BeforeLeaveLifecycle;
142
- preloadRoute: (url: URL, options: {
143
- preloadData?: boolean;
144
- }) => void;
144
+ preloadRoute: (url: URL, preloadData?: boolean) => void;
145
145
  singleFlight: boolean;
146
146
  submissions: Signal<Submission<any, any>[]>;
147
147
  }
package/dist/utils.d.ts CHANGED
@@ -1,12 +1,12 @@
1
- import type { MatchFilters, Params, PathMatch, RouteDescription, SetParams } from "./types.ts";
1
+ import type { MatchFilters, PathMatch, RouteDescription, SearchParams, SetSearchParams } from "./types.ts";
2
2
  export declare const mockBase = "http://sr";
3
3
  export declare function normalizePath(path: string, omitSlash?: boolean): string;
4
4
  export declare function resolvePath(base: string, path: string, from?: string): string | undefined;
5
5
  export declare function invariant<T>(value: T | null | undefined, message: string): T;
6
6
  export declare function joinPaths(from: string, to: string): string;
7
- export declare function extractSearchParams(url: URL): Params;
7
+ export declare function extractSearchParams(url: URL): SearchParams;
8
8
  export declare function createMatcher<S extends string>(path: S, partial?: boolean, matchFilters?: MatchFilters<S>): (location: string) => PathMatch | null;
9
9
  export declare function scoreRoute(route: RouteDescription): number;
10
10
  export declare function createMemoObject<T extends Record<string | symbol, unknown>>(fn: () => T): T;
11
- export declare function mergeSearchString(search: string, params: SetParams): string;
11
+ export declare function mergeSearchString(search: string, params: SetSearchParams): string;
12
12
  export declare function expandOptionals(pattern: string): string[];
package/dist/utils.js CHANGED
@@ -36,7 +36,14 @@ export function joinPaths(from, to) {
36
36
  export function extractSearchParams(url) {
37
37
  const params = {};
38
38
  url.searchParams.forEach((value, key) => {
39
- params[key] = value;
39
+ if (key in params) {
40
+ if (Array.isArray(params[key]))
41
+ params[key].push(value);
42
+ else
43
+ params[key] = [params[key], value];
44
+ }
45
+ else
46
+ params[key] = value;
40
47
  });
41
48
  return params;
42
49
  }
@@ -128,11 +135,20 @@ export function createMemoObject(fn) {
128
135
  export function mergeSearchString(search, params) {
129
136
  const merged = new URLSearchParams(search);
130
137
  Object.entries(params).forEach(([key, value]) => {
131
- if (value == null || value === "") {
138
+ if (value == null || value === "" || (value instanceof Array && !value.length)) {
132
139
  merged.delete(key);
133
140
  }
134
141
  else {
135
- merged.set(key, String(value));
142
+ if (value instanceof Array) {
143
+ // Delete all instances of the key before appending
144
+ merged.delete(key);
145
+ value.forEach(v => {
146
+ merged.append(key, String(v));
147
+ });
148
+ }
149
+ else {
150
+ merged.set(key, String(value));
151
+ }
136
152
  }
137
153
  });
138
154
  const s = merged.toString();
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "Ryan Turnquist"
7
7
  ],
8
8
  "license": "MIT",
9
- "version": "0.14.8",
9
+ "version": "0.14.10",
10
10
  "homepage": "https://github.com/solidjs/solid-router#readme",
11
11
  "repository": {
12
12
  "type": "git",