@solidjs/router 0.10.8 → 0.10.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,7 +1,7 @@
1
1
  import { $TRACK, createMemo, createSignal, onCleanup, getOwner } from "solid-js";
2
2
  import { isServer } from "solid-js/web";
3
3
  import { useRouter } from "../routing";
4
- import { redirectStatusCodes } from "../utils";
4
+ import { redirectStatusCodes, mockBase } from "../utils";
5
5
  import { cacheKeyOp, hashKey, revalidate } from "./cache";
6
6
  export const actions = /* #__PURE__ */ new Map();
7
7
  export function useSubmissions(fn, filter) {
@@ -80,7 +80,7 @@ function toAction(fn, url) {
80
80
  const newFn = function (...passedArgs) {
81
81
  return fn.call(this, ...args, ...passedArgs);
82
82
  };
83
- const uri = new URL(url, "http://sar");
83
+ const uri = new URL(url, mockBase);
84
84
  uri.searchParams.set("args", hashKey(args));
85
85
  return toAction(newFn, (uri.origin === "https://action" ? uri.origin : "") + uri.pathname + uri.search);
86
86
  };
@@ -48,6 +48,9 @@ function revalidateSignals(set, time) {
48
48
  }
49
49
  export function cache(fn, name, options) {
50
50
  const [store, setStore] = createStore({});
51
+ // prioritize GET for server functions
52
+ if (fn.GET)
53
+ fn = fn.GET;
51
54
  const cachedFn = ((...args) => {
52
55
  const cache = getCache();
53
56
  const intent = getIntent();
@@ -75,7 +78,9 @@ export function cache(fn, name, options) {
75
78
  "then" in cached[1]
76
79
  ? cached[1].then(handleResponse(false), handleResponse(true))
77
80
  : handleResponse(false)(cached[1]);
78
- !isServer && intent === "navigate" && startTransition(() => revalidateSignals(cached[3], cached[0])); // update version
81
+ !isServer &&
82
+ intent === "navigate" &&
83
+ startTransition(() => revalidateSignals(cached[3], cached[0])); // update version
79
84
  }
80
85
  return res;
81
86
  }
@@ -106,22 +111,26 @@ export function cache(fn, name, options) {
106
111
  }
107
112
  return res;
108
113
  function handleResponse(error) {
109
- return (v) => {
110
- if (v instanceof Response && redirectStatusCodes.has(v.status)) {
111
- if (navigate) {
112
- startTransition(() => {
113
- let url = v.headers.get(LocationHeader);
114
- if (url && url.startsWith("/")) {
115
- navigate(url, {
116
- replace: true
117
- });
118
- }
119
- else if (!isServer && url) {
120
- window.location.href = url;
121
- }
122
- });
114
+ return async (v) => {
115
+ if (v instanceof Response) {
116
+ if (redirectStatusCodes.has(v.status)) {
117
+ if (navigate) {
118
+ startTransition(() => {
119
+ let url = v.headers.get(LocationHeader);
120
+ if (url && url.startsWith("/")) {
121
+ navigate(url, {
122
+ replace: true
123
+ });
124
+ }
125
+ else if (!isServer && url) {
126
+ window.location.href = url;
127
+ }
128
+ });
129
+ }
130
+ return;
123
131
  }
124
- return;
132
+ if (v.customBody)
133
+ v = await v.customBody();
125
134
  }
126
135
  if (error)
127
136
  throw v;
@@ -1,6 +1,7 @@
1
1
  import { delegateEvents } from "solid-js/web";
2
2
  import { onCleanup } from "solid-js";
3
3
  import { actions } from "./action";
4
+ import { mockBase } from "../utils";
4
5
  export function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "/_server") {
5
6
  return (router) => {
6
7
  const basePath = router.base.path();
@@ -20,7 +21,7 @@ export function setupNativeEvents(preload = true, explicitLinks = false, actionB
20
21
  const a = evt
21
22
  .composedPath()
22
23
  .find(el => el instanceof Node && el.nodeName.toUpperCase() === "A");
23
- if (!a || (explicitLinks && !a.getAttribute("link")))
24
+ if (!a || (explicitLinks && !a.hasAttribute("link")))
24
25
  return;
25
26
  const svg = isSvg(a);
26
27
  const href = svg ? a.href.baseVal : a.href;
@@ -83,12 +84,13 @@ export function setupNativeEvents(preload = true, explicitLinks = false, actionB
83
84
  }
84
85
  function handleFormSubmit(evt) {
85
86
  let actionRef = evt.submitter && evt.submitter.hasAttribute("formaction")
86
- ? evt.submitter.formAction
87
- : evt.target.action;
87
+ ? evt.submitter.getAttribute("formaction")
88
+ : evt.target.getAttribute("action");
88
89
  if (!actionRef)
89
90
  return;
90
91
  if (!actionRef.startsWith("https://action/")) {
91
- const url = new URL(actionRef);
92
+ // normalize server actions
93
+ const url = new URL(actionRef, mockBase);
92
94
  actionRef = router.parsePath(url.pathname + url.search);
93
95
  if (!actionRef.startsWith(actionBase))
94
96
  return;
@@ -1,4 +1,4 @@
1
1
  export { createAsync } from "./createAsync";
2
2
  export { action, useSubmission, useSubmissions, useAction, type Action } from "./action";
3
3
  export { cache, revalidate, type CachedFunction } from "./cache";
4
- export { redirect, reload } from "./response";
4
+ export { redirect, reload, json } from "./response";
@@ -1,4 +1,4 @@
1
1
  export { createAsync } from "./createAsync";
2
2
  export { action, useSubmission, useSubmissions, useAction } from "./action";
3
3
  export { cache, revalidate } from "./cache";
4
- export { redirect, reload } from "./response";
4
+ export { redirect, reload, json } from "./response";
@@ -1,5 +1,6 @@
1
1
  export type RouterResponseInit = ResponseInit & {
2
2
  revalidate?: string | string[];
3
3
  };
4
- export declare function redirect(url: string, init?: number | RouterResponseInit): Response;
5
- export declare function reload(init: RouterResponseInit): Response;
4
+ export declare function redirect(url: string, init?: number | RouterResponseInit): never;
5
+ export declare function reload(init: RouterResponseInit): never;
6
+ export declare function json<T>(data: T, init?: Omit<ResponseInit, "body">): T;
@@ -23,6 +23,18 @@ export function reload(init) {
23
23
  const { revalidate, ...responseInit } = init;
24
24
  return new Response(null, {
25
25
  ...responseInit,
26
- ...(revalidate ? { headers: new Headers(responseInit.headers).set("X-Revalidate", revalidate.toString()) } : {})
26
+ ...(revalidate
27
+ ? { headers: new Headers(responseInit.headers).set("X-Revalidate", revalidate.toString()) }
28
+ : {})
27
29
  });
28
30
  }
31
+ export function json(data, init) {
32
+ const headers = new Headers((init || {}).headers);
33
+ headers.set("Content-Type", "application/json");
34
+ const response = new Response(JSON.stringify(data), {
35
+ ...init,
36
+ headers
37
+ });
38
+ response.customBody = () => data;
39
+ return response;
40
+ }
package/dist/index.js CHANGED
@@ -38,6 +38,7 @@ function createBeforeLeave() {
38
38
 
39
39
  const hasSchemeRegex = /^(?:[a-z0-9]+:)?\/\//i;
40
40
  const trimPathRegex = /^\/+|(\/)\/+$/g;
41
+ const mockBase = "http://sr";
41
42
  const redirectStatusCodes = new Set([204, 301, 302, 303, 307, 308]);
42
43
  function normalizePath(path, omitSlash = false) {
43
44
  const s = path.replace(trimPathRegex, "$1");
@@ -255,7 +256,10 @@ function createRoutes(routeDef, base = "") {
255
256
  return asArray(routeDef.path).reduce((acc, path) => {
256
257
  for (const originalPath of expandOptionals(path)) {
257
258
  const path = joinPaths(base, originalPath);
258
- const pattern = isLeaf ? path : path.split("/*", 1)[0];
259
+ let pattern = isLeaf ? path : path.split("/*", 1)[0];
260
+ pattern = pattern.split("/").map(s => {
261
+ return s.startsWith(':') || s.startsWith('*') ? s : encodeURIComponent(s);
262
+ }).join("/");
259
263
  acc.push({
260
264
  ...shared,
261
265
  originalPath,
@@ -324,7 +328,7 @@ function getRouteMatches(branches, location) {
324
328
  return [];
325
329
  }
326
330
  function createLocation(path, state) {
327
- const origin = new URL("http://sar");
331
+ const origin = new URL(mockBase);
328
332
  const url = createMemo(prev => {
329
333
  const path_ = path();
330
334
  try {
@@ -809,6 +813,8 @@ function revalidateSignals(set, time) {
809
813
  }
810
814
  function cache(fn, name, options) {
811
815
  const [store, setStore] = createStore({});
816
+ // prioritize GET for server functions
817
+ if (fn.GET) fn = fn.GET;
812
818
  const cachedFn = (...args) => {
813
819
  const cache = getCache();
814
820
  const intent = getIntent();
@@ -862,21 +868,24 @@ function cache(fn, name, options) {
862
868
  }
863
869
  return res;
864
870
  function handleResponse(error) {
865
- return v => {
866
- if (v instanceof Response && redirectStatusCodes.has(v.status)) {
867
- if (navigate) {
868
- startTransition(() => {
869
- let url = v.headers.get(LocationHeader);
870
- if (url && url.startsWith("/")) {
871
- navigate(url, {
872
- replace: true
873
- });
874
- } else if (!isServer && url) {
875
- window.location.href = url;
876
- }
877
- });
871
+ return async v => {
872
+ if (v instanceof Response) {
873
+ if (redirectStatusCodes.has(v.status)) {
874
+ if (navigate) {
875
+ startTransition(() => {
876
+ let url = v.headers.get(LocationHeader);
877
+ if (url && url.startsWith("/")) {
878
+ navigate(url, {
879
+ replace: true
880
+ });
881
+ } else if (!isServer && url) {
882
+ window.location.href = url;
883
+ }
884
+ });
885
+ }
886
+ return;
878
887
  }
879
- return;
888
+ if (v.customBody) v = await v.customBody();
880
889
  }
881
890
  if (error) throw v;
882
891
  if (isServer) return v;
@@ -1000,7 +1009,7 @@ function toAction(fn, url) {
1000
1009
  const newFn = function (...passedArgs) {
1001
1010
  return fn.call(this, ...args, ...passedArgs);
1002
1011
  };
1003
- const uri = new URL(url, "http://sar");
1012
+ const uri = new URL(url, mockBase);
1004
1013
  uri.searchParams.set("args", hashKey(args));
1005
1014
  return toAction(newFn, (uri.origin === "https://action" ? uri.origin : "") + uri.pathname + uri.search);
1006
1015
  };
@@ -1045,7 +1054,7 @@ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "
1045
1054
  function handleAnchor(evt) {
1046
1055
  if (evt.defaultPrevented || evt.button !== 0 || evt.metaKey || evt.altKey || evt.ctrlKey || evt.shiftKey) return;
1047
1056
  const a = evt.composedPath().find(el => el instanceof Node && el.nodeName.toUpperCase() === "A");
1048
- if (!a || explicitLinks && !a.getAttribute("link")) return;
1057
+ if (!a || explicitLinks && !a.hasAttribute("link")) return;
1049
1058
  const svg = isSvg(a);
1050
1059
  const href = svg ? a.href.baseVal : a.href;
1051
1060
  const target = svg ? a.target.baseVal : a.target;
@@ -1096,10 +1105,11 @@ function setupNativeEvents(preload = true, explicitLinks = false, actionBase = "
1096
1105
  }
1097
1106
  }
1098
1107
  function handleFormSubmit(evt) {
1099
- let actionRef = evt.submitter && evt.submitter.hasAttribute("formaction") ? evt.submitter.formAction : evt.target.action;
1108
+ let actionRef = evt.submitter && evt.submitter.hasAttribute("formaction") ? evt.submitter.getAttribute("formaction") : evt.target.getAttribute("action");
1100
1109
  if (!actionRef) return;
1101
1110
  if (!actionRef.startsWith("https://action/")) {
1102
- const url = new URL(actionRef);
1111
+ // normalize server actions
1112
+ const url = new URL(actionRef, mockBase);
1103
1113
  actionRef = router.parsePath(url.pathname + url.search);
1104
1114
  if (!actionRef.startsWith(actionBase)) return;
1105
1115
  }
@@ -1409,5 +1419,15 @@ function reload(init) {
1409
1419
  } : {})
1410
1420
  });
1411
1421
  }
1422
+ function json(data, init) {
1423
+ const headers = new Headers((init || {}).headers);
1424
+ headers.set("Content-Type", "application/json");
1425
+ const response = new Response(JSON.stringify(data), {
1426
+ ...init,
1427
+ headers
1428
+ });
1429
+ response.customBody = () => data;
1430
+ return response;
1431
+ }
1412
1432
 
1413
- export { A, HashRouter, MemoryRouter, Navigate, Route, Router, StaticRouter, mergeSearchString as _mergeSearchString, action, cache, createAsync, createBeforeLeave, createMemoryHistory, createRouter, redirect, reload, revalidate, useAction, useBeforeLeave, useHref, useIsRouting, useLocation, useMatch, useNavigate, useParams, useResolvedPath, useSearchParams, useSubmission, useSubmissions };
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 };
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
3
  import { createBeforeLeave } from "./lifecycle";
4
- import { createMemoObject, extractSearchParams, invariant, resolvePath, createMatcher, joinPaths, scoreRoute, mergeSearchString, expandOptionals } from "./utils";
4
+ import { mockBase, createMemoObject, extractSearchParams, invariant, resolvePath, createMatcher, joinPaths, scoreRoute, mergeSearchString, expandOptionals } from "./utils";
5
5
  const MAX_REDIRECTS = 100;
6
6
  export const RouterContextObj = createContext();
7
7
  export const RouteContextObj = createContext();
@@ -67,7 +67,10 @@ export function createRoutes(routeDef, base = "") {
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
- const pattern = isLeaf ? path : path.split("/*", 1)[0];
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
74
  acc.push({
72
75
  ...shared,
73
76
  originalPath,
@@ -137,7 +140,7 @@ export function getRouteMatches(branches, location) {
137
140
  return [];
138
141
  }
139
142
  export function createLocation(path, state) {
140
- const origin = new URL("http://sar");
143
+ const origin = new URL(mockBase);
141
144
  const url = createMemo(prev => {
142
145
  const path_ = path();
143
146
  try {
package/dist/utils.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { MatchFilters, Params, PathMatch, Route, SetParams } from "./types";
2
+ export declare const mockBase = "http://sr";
2
3
  export declare const redirectStatusCodes: Set<number>;
3
4
  export declare function normalizePath(path: string, omitSlash?: boolean): string;
4
5
  export declare function resolvePath(base: string, path: string, from?: string): string | undefined;
package/dist/utils.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { createMemo, getOwner, runWithOwner } from "solid-js";
2
2
  const hasSchemeRegex = /^(?:[a-z0-9]+:)?\/\//i;
3
3
  const trimPathRegex = /^\/+|(\/)\/+$/g;
4
+ export const mockBase = "http://sr";
4
5
  export const redirectStatusCodes = new Set([204, 301, 302, 303, 307, 308]);
5
6
  export function normalizePath(path, omitSlash = false) {
6
7
  const s = path.replace(trimPathRegex, "$1");
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "Ryan Turnquist"
7
7
  ],
8
8
  "license": "MIT",
9
- "version": "0.10.8",
9
+ "version": "0.10.10",
10
10
  "homepage": "https://github.com/solidjs/solid-router#readme",
11
11
  "repository": {
12
12
  "type": "git",