@solidjs/router 2.0.0-next.34 → 2.0.0-next.36

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/dist/routing.js CHANGED
@@ -4,7 +4,7 @@ import { runWithOwner } from "solid-js";
4
4
  import { DEV } from "solid-js";
5
5
  import { createComponent, createContext, createMemo, createSignal, getOwner, isPending, latest, NotReadyError, untrack, useContext } from "solid-js";
6
6
  import { clearFlashCookie, getRequestEvent, hasFlashCookie, isServer } from "@solidjs/web";
7
- import { mockBase, comparablePath, createMemoObject, extractSearchParams, invariant, resolvePath, createMatcher, joinPaths, scoreRoute, mergeSearchString, expandOptionals, validateSearch } from "./utils.js";
7
+ import { mockBase, createMemoObject, extractSearchParams, invariant, matchLink, resolvePath, createMatcher, joinPaths, scoreRoute, mergeSearchString, expandOptionals, validateSearch } from "./utils.js";
8
8
  import { HREF } from "./paths.js";
9
9
  import { serverRouteOf, serverRouteArgs, serverRouteArgsEqual } from "./serverRouteShared.js";
10
10
  const MAX_REDIRECTS = 100;
@@ -227,22 +227,11 @@ export const useLinkState = (href, options = {}) => {
227
227
  const router = useRouter();
228
228
  const location = router.location;
229
229
  const to = useResolvedPath(() => String(href()));
230
- // trailing slashes are ignored so `/route` and `/route/` share state
231
- const path = createMemo(() => {
232
- const to_ = to();
233
- return to_ === undefined ? undefined : comparablePath(to_);
234
- });
235
- const matches = (loc) => {
236
- const path_ = path();
237
- if (path_ === undefined)
238
- return [false, false];
239
- const exact = loc === path_;
240
- return [exact || (!options.end && loc.startsWith(path_ + "/")), exact];
241
- };
242
- const state = createMemo(() => matches(decodeURI(comparablePath(location.pathname))));
230
+ const base = router.base.path();
231
+ const state = createMemo(() => matchLink(location, to(), base, options.end));
243
232
  return {
244
- active: () => state()[0],
245
- current: () => state()[1],
233
+ active: createMemo(() => state().active),
234
+ current: createMemo(() => state().current),
246
235
  // match the in-flight target explicitly (rather than active-while-routing)
247
236
  // so the answer is the same from pure reads and from effects, which
248
237
  // observe the committed location during a transition
@@ -250,7 +239,8 @@ export const useLinkState = (href, options = {}) => {
250
239
  state(); // location dependency: mid-flight target swaps recompute
251
240
  return (router.isRouting() &&
252
241
  !!router.pendingTarget &&
253
- matches(decodeURI(comparablePath(router.pendingTarget.value)))[0]);
242
+ matchLink({ pathname: router.pendingTarget.value, search: "" }, to(), base, options.end)
243
+ .active);
254
244
  })
255
245
  };
256
246
  };
package/dist/utils.d.ts CHANGED
@@ -1,8 +1,36 @@
1
1
  import type { StandardSchemaV1, MatchFilters, PathMatch, RouteDescription, SearchParams, SetSearchParams } from "./types.js";
2
2
  export declare const mockBase = "http://sr";
3
3
  export declare function normalizePath(path: string, omitSlash?: boolean): string;
4
- /** Pathname stripped of search/hash and trailing slash, lowercased — the form link matching compares. */
4
+ /**
5
+ * Pathname stripped of search/hash and trailing slash, percent-encoded the
6
+ * way `URL` (and so `location.pathname`) reports it, lowercased — the form
7
+ * link matching compares. Raw and encoded spellings (`/café`, `/caf%C3%A9`)
8
+ * meet; escapes are never decoded, so `%2F` is not a path separator.
9
+ */
5
10
  export declare const comparablePath: (path: string) => string;
11
+ /**
12
+ * Whether a URL pathname is the router's base path or under it, on a segment
13
+ * boundary: base `/app` covers `/app`, `/app/` and `/app/x`, not `/apple`.
14
+ * Case-insensitive; no base (`""` or `/`) covers every path.
15
+ */
16
+ export declare function isUnderBase(pathname: string, base: string): boolean;
17
+ /**
18
+ * The link-state rule shared by claimed anchors and `useLinkState`, given
19
+ * the location and a link's already-resolved target (path, optional query
20
+ * and hash):
21
+ *
22
+ * - `current` — same pathname and same query (parameter order and hash aside)
23
+ * - `active` — same pathname or one under it; the router's root (`base`, or
24
+ * `/` without one) only matches exactly, since it is a prefix of every
25
+ * page. `end` makes every link exact-only.
26
+ */
27
+ export declare function matchLink(location: {
28
+ pathname: string;
29
+ search: string;
30
+ }, target: string | undefined, base: string, end?: boolean): {
31
+ active: boolean;
32
+ current: boolean;
33
+ };
6
34
  export declare function resolvePath(base: string, path: string, from?: string): string | undefined;
7
35
  export declare function invariant<T>(value: T | null | undefined, message: string): T;
8
36
  export declare function joinPaths(from: string, to: string): string;
package/dist/utils.js CHANGED
@@ -6,8 +6,54 @@ export function normalizePath(path, omitSlash = false) {
6
6
  const s = path.replace(trimPathRegex, "$1");
7
7
  return s ? (omitSlash || /^[?#]/.test(s) ? s : "/" + s) : "";
8
8
  }
9
- /** Pathname stripped of search/hash and trailing slash, lowercased — the form link matching compares. */
10
- export const comparablePath = (path) => normalizePath(path.split(/[?#]/, 1)[0]).toLowerCase().replace(/\/$/, "");
9
+ /**
10
+ * Pathname stripped of search/hash and trailing slash, percent-encoded the
11
+ * way `URL` (and so `location.pathname`) reports it, lowercased — the form
12
+ * link matching compares. Raw and encoded spellings (`/café`, `/caf%C3%A9`)
13
+ * meet; escapes are never decoded, so `%2F` is not a path separator.
14
+ */
15
+ export const comparablePath = (path) => new URL(mockBase + normalizePath(path.split(/[?#]/, 1)[0])).pathname
16
+ .toLowerCase()
17
+ .replace(/\/$/, "");
18
+ /**
19
+ * Whether a URL pathname is the router's base path or under it, on a segment
20
+ * boundary: base `/app` covers `/app`, `/app/` and `/app/x`, not `/apple`.
21
+ * Case-insensitive; no base (`""` or `/`) covers every path.
22
+ */
23
+ export function isUnderBase(pathname, base) {
24
+ const b = base.toLowerCase().replace(/\/+$/, "");
25
+ const p = pathname.toLowerCase();
26
+ return !b || !p || p === b || p.startsWith(b + "/");
27
+ }
28
+ /** A query string as an order-independent comparable string. */
29
+ const comparableQuery = (search) => {
30
+ const params = new URLSearchParams(search);
31
+ params.sort();
32
+ return params.toString();
33
+ };
34
+ /**
35
+ * The link-state rule shared by claimed anchors and `useLinkState`, given
36
+ * the location and a link's already-resolved target (path, optional query
37
+ * and hash):
38
+ *
39
+ * - `current` — same pathname and same query (parameter order and hash aside)
40
+ * - `active` — same pathname or one under it; the router's root (`base`, or
41
+ * `/` without one) only matches exactly, since it is a prefix of every
42
+ * page. `end` makes every link exact-only.
43
+ */
44
+ export function matchLink(location, target, base, end) {
45
+ if (target === undefined)
46
+ return { active: false, current: false };
47
+ const loc = comparablePath(location.pathname);
48
+ const path = comparablePath(target);
49
+ const exact = loc === path;
50
+ const hashless = target.split("#", 1)[0];
51
+ const q = hashless.indexOf("?");
52
+ return {
53
+ active: exact || (!end && path !== "" && path !== comparablePath(base) && loc.startsWith(path + "/")),
54
+ current: exact && comparableQuery(location.search) === comparableQuery(q < 0 ? "" : hashless.slice(q))
55
+ };
56
+ }
11
57
  export function resolvePath(base, path, from) {
12
58
  if (hasSchemeRegex.test(path)) {
13
59
  return undefined;
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "Ryan Turnquist"
7
7
  ],
8
8
  "license": "MIT",
9
- "version": "2.0.0-next.34",
9
+ "version": "2.0.0-next.36",
10
10
  "homepage": "https://github.com/solidjs/solid-router#readme",
11
11
  "repository": {
12
12
  "type": "git",