@solidjs/router 2.0.0-next.23 → 2.0.0-next.24
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 +2 -0
- package/dist/index.js +69 -22
- package/dist/routers/factory.jsx +70 -26
- package/dist/routing.js +3 -3
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -750,6 +750,8 @@ const isRouting = useIsRouting();
|
|
|
750
750
|
return <div classList={{ "grey-out": isRouting() }}>...</div>;
|
|
751
751
|
```
|
|
752
752
|
|
|
753
|
+
In Solid's dev and observe builds the router also declares every navigation to the attribution engine (`solid-js/attribution`): holds and re-runs caused by a navigation are named after the route pattern (`navigation to /users/:id`), timed from the user event that started it, and redirect hops fold onto the navigation they belong to. `attribution.navigations()` and `feedback().navigations` list them; nothing of this exists in production builds.
|
|
754
|
+
|
|
753
755
|
### useMatch
|
|
754
756
|
|
|
755
757
|
Tests a path *pattern you supply* against the current location; returns a memo of match information or `undefined`. It never consults the route tree — the pattern doesn't have to correspond to a defined route. The match's `params` are typed from the pattern, and a typed path node works too (a concrete URL — useful for "am I here" checks):
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getOwner, runWithOwner, createMemo, createRenderEffect, onCleanup, untrack, createContext, createSignal, useContext, NotReadyError, isPending, DEV, latest, createComponent, createRoot, Show, createEffect, sharedConfig, onSettled, getObserver, $TRACK, action as action$1 } from 'solid-js';
|
|
1
|
+
import { getOwner, runWithOwner, createMemo, createRenderEffect, onCleanup, untrack, createContext, createSignal, useContext, NotReadyError, isPending, DEV, latest, createComponent, createRoot, Show, createEffect, sharedConfig, OBSERVE, onSettled, getObserver, $TRACK, action as action$1 } from 'solid-js';
|
|
2
2
|
import { registerElementClaim, delegateEvents, isServer, getRequestEvent, hasFlashCookie, clearFlashCookie, createComponent as createComponent$1, memo, isServerFunction, getServerFunctionMetadata, getServerFunctionRPC, isResponseEnvelope, REVALIDATE_HEADER } from '@solidjs/web';
|
|
3
3
|
import { subscribeFlightData, decodeRedirectHeaderValue, REDIRECT_HEADER, decodeResponsePayload, parseServerFunctionUrl, createServerReference } from '@solidjs/web/server-functions';
|
|
4
4
|
import { decodeFlashCookie } from '@solidjs/web/server-functions/server';
|
|
@@ -1221,6 +1221,8 @@ function createRouterContext(integration, branches, getContext, options = {}) {
|
|
|
1221
1221
|
read = runWithOwner(routerOwner, () => createMemo(() => {
|
|
1222
1222
|
const result = resolveLazySubtree(record);
|
|
1223
1223
|
return result instanceof Promise ? result.then(() => undefined) : undefined;
|
|
1224
|
+
}, {
|
|
1225
|
+
name: "lazyRoutes"
|
|
1224
1226
|
}));
|
|
1225
1227
|
lazyReaders.set(record, read);
|
|
1226
1228
|
}
|
|
@@ -1252,6 +1254,8 @@ function createRouterContext(integration, branches, getContext, options = {}) {
|
|
|
1252
1254
|
}
|
|
1253
1255
|
}
|
|
1254
1256
|
return m;
|
|
1257
|
+
}, {
|
|
1258
|
+
name: "matches"
|
|
1255
1259
|
});
|
|
1256
1260
|
const routingPending = createMemo(() => isPending(() => {
|
|
1257
1261
|
try {
|
|
@@ -1261,7 +1265,9 @@ function createRouterContext(integration, branches, getContext, options = {}) {
|
|
|
1261
1265
|
}
|
|
1262
1266
|
location.search;
|
|
1263
1267
|
location.hash;
|
|
1264
|
-
})
|
|
1268
|
+
}), {
|
|
1269
|
+
name: "routingPending"
|
|
1270
|
+
});
|
|
1265
1271
|
const isRouting = () => routingPending() || isPending(source);
|
|
1266
1272
|
const transitionIntent = () => {
|
|
1267
1273
|
if (!isPending(source)) return;
|
|
@@ -2023,32 +2029,74 @@ function defineRoutes(routes) {
|
|
|
2023
2029
|
function defineRoute(route) {
|
|
2024
2030
|
return route;
|
|
2025
2031
|
}
|
|
2032
|
+
/**
|
|
2033
|
+
* What one location write is, for solid's observe tier: the parametrized
|
|
2034
|
+
* route it heads to, the params, where it came from, and — when the write is
|
|
2035
|
+
* the router chasing a redirect while the previous target is still pending —
|
|
2036
|
+
* which hop of that navigation it is. The engine times the navigation from
|
|
2037
|
+
* this declaration (or from the user event enclosing it) until its writes
|
|
2038
|
+
* are through, and names holds and re-runs after it.
|
|
2039
|
+
*
|
|
2040
|
+
* `name` and `params` are getters: the engine reads them when the navigation
|
|
2041
|
+
* settles, not when it starts, so a lazy route subtree that loaded during the
|
|
2042
|
+
* hold names the exact route it resolved to rather than its placeholder.
|
|
2043
|
+
* Redirect depth comes from `_navigation` (1 = a navigation, n = its
|
|
2044
|
+
* (n - 1)th redirect hop, -1 = the browser moved: back/forward, hash).
|
|
2045
|
+
*/
|
|
2046
|
+
function describeNavigation(match, next, from) {
|
|
2047
|
+
const pathname = new URL(next.value, mockBase).pathname;
|
|
2048
|
+
const matches = () => untrack(() => match(pathname));
|
|
2049
|
+
const ref = {
|
|
2050
|
+
kind: "navigation",
|
|
2051
|
+
to: next.value,
|
|
2052
|
+
from,
|
|
2053
|
+
get name() {
|
|
2054
|
+
const m = matches();
|
|
2055
|
+
return m.length ? m[m.length - 1].route.pattern || "/" : pathname;
|
|
2056
|
+
},
|
|
2057
|
+
get params() {
|
|
2058
|
+
const m = matches();
|
|
2059
|
+
return m.length ? mergeParams(m) : undefined;
|
|
2060
|
+
}
|
|
2061
|
+
};
|
|
2062
|
+
if (next._navigation !== undefined && next._navigation > 1) ref.redirect = next._navigation - 1;
|
|
2063
|
+
return ref;
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2026
2066
|
/** Wraps a history adapter in the integration signal the router core consumes. Must run under a reactive owner. */
|
|
2027
|
-
function createIntegration(history) {
|
|
2067
|
+
function createIntegration(history, match) {
|
|
2028
2068
|
let committing = false;
|
|
2029
2069
|
const wrap = value => typeof value === "string" ? {
|
|
2030
2070
|
value
|
|
2031
2071
|
} : value;
|
|
2032
2072
|
const [read, write] = createSignal(wrap(history.get()), {
|
|
2033
2073
|
equals: (a, b) => a.value === b.value && a.state === b.state && a._navigation === b._navigation,
|
|
2034
|
-
ownedWrite: true
|
|
2074
|
+
ownedWrite: true,
|
|
2075
|
+
name: "location"
|
|
2035
2076
|
});
|
|
2036
2077
|
const signal = [read, next => {
|
|
2037
2078
|
if (sharedConfig.registry && !sharedConfig.done) sharedConfig.done = true;
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2079
|
+
const commit = () => {
|
|
2080
|
+
write(next);
|
|
2081
|
+
if (next._navigation && next._navigation > 0) {
|
|
2082
|
+
// Register out of band so a destination error boundary replacing the
|
|
2083
|
+
// Router subtree cannot suppress the winning history commit.
|
|
2084
|
+
runWithOwner(null, () => onSettled(() => {
|
|
2085
|
+
if (read() !== next) return;
|
|
2086
|
+
committing = true;
|
|
2087
|
+
try {
|
|
2088
|
+
history.set(next);
|
|
2089
|
+
} finally {
|
|
2090
|
+
committing = false;
|
|
2091
|
+
}
|
|
2092
|
+
}));
|
|
2093
|
+
}
|
|
2094
|
+
};
|
|
2095
|
+
// Every client location write passes here — navigate(), a redirect hop,
|
|
2096
|
+
// the browser's own back/forward — so this is the one place the
|
|
2097
|
+
// navigation is declared. `read()` still holds the committed location
|
|
2098
|
+
// while a navigation is pending, which is the `from` a hop wants too.
|
|
2099
|
+
OBSERVE ? OBSERVE.attribution.withOrigin(describeNavigation(match, next, untrack(read).value), commit) : commit();
|
|
2052
2100
|
}];
|
|
2053
2101
|
history.init && onCleanup(history.init((value = history.get()) => {
|
|
2054
2102
|
if (committing) return;
|
|
@@ -2104,6 +2152,7 @@ function createRouter(config) {
|
|
|
2104
2152
|
return compiled;
|
|
2105
2153
|
};
|
|
2106
2154
|
const renderPath = config.history && config.history.utils && config.history.utils.renderPath || undefined;
|
|
2155
|
+
const matchPath = pathname => getRouteMatches(branches(), config.transformUrl ? config.transformUrl(pathname) : pathname);
|
|
2107
2156
|
function RouterComponent(props) {
|
|
2108
2157
|
// One router per app: the session (location, history, delegation, link
|
|
2109
2158
|
// claims, preloading) has a single owner, and a second instance would
|
|
@@ -2120,7 +2169,7 @@ function createRouter(config) {
|
|
|
2120
2169
|
restoration = createScrollRestoration();
|
|
2121
2170
|
history = withScrollRestoration(history || browserHistory(), restoration);
|
|
2122
2171
|
}
|
|
2123
|
-
const integration = isServer ? staticIntegration(props.url, config.history && config.history.utils) : createIntegration(history || browserHistory());
|
|
2172
|
+
const integration = isServer ? staticIntegration(props.url, config.history && config.history.utils) : createIntegration(history || browserHistory(), matchPath);
|
|
2124
2173
|
let context;
|
|
2125
2174
|
const routerState = createRouterContext(integration, branches, () => context, {
|
|
2126
2175
|
base: basePath,
|
|
@@ -2161,9 +2210,7 @@ function createRouter(config) {
|
|
|
2161
2210
|
routes: config.routes,
|
|
2162
2211
|
config,
|
|
2163
2212
|
match(url) {
|
|
2164
|
-
|
|
2165
|
-
const pathname = config.transformUrl ? config.transformUrl(u.pathname) : u.pathname;
|
|
2166
|
-
return getRouteMatches(branches(), pathname).map(({
|
|
2213
|
+
return matchPath(new URL(url, mockBase).pathname).map(({
|
|
2167
2214
|
route,
|
|
2168
2215
|
path,
|
|
2169
2216
|
params
|
package/dist/routers/factory.jsx
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/*@refresh skip*/
|
|
2
2
|
import { createSignal, getOwner, onCleanup, onSettled, runWithOwner, sharedConfig, untrack } from "solid-js";
|
|
3
|
-
// standalone
|
|
4
|
-
//
|
|
5
|
-
|
|
3
|
+
// standalone imports: `DEV` is undefined in solid's production build and
|
|
4
|
+
// `OBSERVE` outside its observe/dev builds, so app bundlers fold the
|
|
5
|
+
// `DEV &&` diagnostics and the `OBSERVE &&` attribution out of shipped bundles
|
|
6
|
+
import { DEV, OBSERVE } from "solid-js";
|
|
6
7
|
import { getRequestEvent, isServer } from "@solidjs/web";
|
|
7
8
|
import { setupLinkClaims } from "../claims.js";
|
|
8
9
|
import { setupNativeEvents } from "../data/events.js";
|
|
9
10
|
import { createPathsProxy } from "../paths.js";
|
|
10
|
-
import { createBranches, createRouterContext, getRouteMatches, registerFlightRouter, RouterContextObj, trackLazySubtrees, useOptionalContext } from "../routing.js";
|
|
11
|
+
import { createBranches, createRouterContext, getRouteMatches, mergeParams, registerFlightRouter, RouterContextObj, trackLazySubtrees, useOptionalContext } from "../routing.js";
|
|
11
12
|
import { mockBase } from "../utils.js";
|
|
12
13
|
import { Root, Routes } from "./components.jsx";
|
|
13
14
|
import { browserHistory } from "./history.js";
|
|
@@ -25,35 +26,79 @@ export function defineRoutes(routes) {
|
|
|
25
26
|
export function defineRoute(route) {
|
|
26
27
|
return route;
|
|
27
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* What one location write is, for solid's observe tier: the parametrized
|
|
31
|
+
* route it heads to, the params, where it came from, and — when the write is
|
|
32
|
+
* the router chasing a redirect while the previous target is still pending —
|
|
33
|
+
* which hop of that navigation it is. The engine times the navigation from
|
|
34
|
+
* this declaration (or from the user event enclosing it) until its writes
|
|
35
|
+
* are through, and names holds and re-runs after it.
|
|
36
|
+
*
|
|
37
|
+
* `name` and `params` are getters: the engine reads them when the navigation
|
|
38
|
+
* settles, not when it starts, so a lazy route subtree that loaded during the
|
|
39
|
+
* hold names the exact route it resolved to rather than its placeholder.
|
|
40
|
+
* Redirect depth comes from `_navigation` (1 = a navigation, n = its
|
|
41
|
+
* (n - 1)th redirect hop, -1 = the browser moved: back/forward, hash).
|
|
42
|
+
*/
|
|
43
|
+
function describeNavigation(match, next, from) {
|
|
44
|
+
const pathname = new URL(next.value, mockBase).pathname;
|
|
45
|
+
const matches = () => untrack(() => match(pathname));
|
|
46
|
+
const ref = {
|
|
47
|
+
kind: "navigation",
|
|
48
|
+
to: next.value,
|
|
49
|
+
from,
|
|
50
|
+
get name() {
|
|
51
|
+
const m = matches();
|
|
52
|
+
return m.length ? m[m.length - 1].route.pattern || "/" : pathname;
|
|
53
|
+
},
|
|
54
|
+
get params() {
|
|
55
|
+
const m = matches();
|
|
56
|
+
return m.length ? mergeParams(m) : undefined;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
if (next._navigation !== undefined && next._navigation > 1)
|
|
60
|
+
ref.redirect = next._navigation - 1;
|
|
61
|
+
return ref;
|
|
62
|
+
}
|
|
28
63
|
/** Wraps a history adapter in the integration signal the router core consumes. Must run under a reactive owner. */
|
|
29
|
-
function createIntegration(history) {
|
|
64
|
+
function createIntegration(history, match) {
|
|
30
65
|
let committing = false;
|
|
31
66
|
const wrap = (value) => (typeof value === "string" ? { value } : value);
|
|
32
67
|
const [read, write] = createSignal(wrap(history.get()), {
|
|
33
68
|
equals: (a, b) => a.value === b.value && a.state === b.state && a._navigation === b._navigation,
|
|
34
|
-
ownedWrite: true
|
|
69
|
+
ownedWrite: true,
|
|
70
|
+
name: "location"
|
|
35
71
|
});
|
|
36
72
|
const signal = [
|
|
37
73
|
read,
|
|
38
74
|
(next) => {
|
|
39
75
|
if (sharedConfig.registry && !sharedConfig.done)
|
|
40
76
|
sharedConfig.done = true;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
77
|
+
const commit = () => {
|
|
78
|
+
write(next);
|
|
79
|
+
if (next._navigation && next._navigation > 0) {
|
|
80
|
+
// Register out of band so a destination error boundary replacing the
|
|
81
|
+
// Router subtree cannot suppress the winning history commit.
|
|
82
|
+
runWithOwner(null, () => onSettled(() => {
|
|
83
|
+
if (read() !== next)
|
|
84
|
+
return;
|
|
85
|
+
committing = true;
|
|
86
|
+
try {
|
|
87
|
+
history.set(next);
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
committing = false;
|
|
91
|
+
}
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
// Every client location write passes here — navigate(), a redirect hop,
|
|
96
|
+
// the browser's own back/forward — so this is the one place the
|
|
97
|
+
// navigation is declared. `read()` still holds the committed location
|
|
98
|
+
// while a navigation is pending, which is the `from` a hop wants too.
|
|
99
|
+
OBSERVE
|
|
100
|
+
? OBSERVE.attribution.withOrigin(describeNavigation(match, next, untrack(read).value), commit)
|
|
101
|
+
: commit();
|
|
57
102
|
}
|
|
58
103
|
];
|
|
59
104
|
history.init &&
|
|
@@ -100,6 +145,7 @@ export function createRouter(config) {
|
|
|
100
145
|
return compiled;
|
|
101
146
|
};
|
|
102
147
|
const renderPath = (config.history && config.history.utils && config.history.utils.renderPath) || undefined;
|
|
148
|
+
const matchPath = (pathname) => getRouteMatches(branches(), config.transformUrl ? config.transformUrl(pathname) : pathname);
|
|
103
149
|
function RouterComponent(props) {
|
|
104
150
|
// One router per app: the session (location, history, delegation, link
|
|
105
151
|
// claims, preloading) has a single owner, and a second instance would
|
|
@@ -119,7 +165,7 @@ export function createRouter(config) {
|
|
|
119
165
|
}
|
|
120
166
|
const integration = isServer
|
|
121
167
|
? staticIntegration(props.url, config.history && config.history.utils)
|
|
122
|
-
: createIntegration(history || browserHistory());
|
|
168
|
+
: createIntegration(history || browserHistory(), matchPath);
|
|
123
169
|
let context;
|
|
124
170
|
const routerState = createRouterContext(integration, branches, () => context, {
|
|
125
171
|
base: basePath,
|
|
@@ -149,9 +195,7 @@ export function createRouter(config) {
|
|
|
149
195
|
routes: config.routes,
|
|
150
196
|
config,
|
|
151
197
|
match(url) {
|
|
152
|
-
|
|
153
|
-
const pathname = config.transformUrl ? config.transformUrl(u.pathname) : u.pathname;
|
|
154
|
-
return getRouteMatches(branches(), pathname).map(({ route, path, params }) => ({
|
|
198
|
+
return matchPath(new URL(url, mockBase).pathname).map(({ route, path, params }) => ({
|
|
155
199
|
path: route.originalPath,
|
|
156
200
|
pattern: route.pattern,
|
|
157
201
|
match: path,
|
package/dist/routing.js
CHANGED
|
@@ -647,7 +647,7 @@ export function createRouterContext(integration, branches, getContext, options =
|
|
|
647
647
|
read = runWithOwner(routerOwner, () => createMemo(() => {
|
|
648
648
|
const result = resolveLazySubtree(record);
|
|
649
649
|
return result instanceof Promise ? result.then(() => undefined) : undefined;
|
|
650
|
-
}));
|
|
650
|
+
}, { name: "lazyRoutes" }));
|
|
651
651
|
lazyReaders.set(record, read);
|
|
652
652
|
}
|
|
653
653
|
return read();
|
|
@@ -682,7 +682,7 @@ export function createRouterContext(integration, branches, getContext, options =
|
|
|
682
682
|
}
|
|
683
683
|
}
|
|
684
684
|
return m;
|
|
685
|
-
});
|
|
685
|
+
}, { name: "matches" });
|
|
686
686
|
const routingPending = createMemo(() => isPending(() => {
|
|
687
687
|
try {
|
|
688
688
|
matches();
|
|
@@ -693,7 +693,7 @@ export function createRouterContext(integration, branches, getContext, options =
|
|
|
693
693
|
}
|
|
694
694
|
location.search;
|
|
695
695
|
location.hash;
|
|
696
|
-
}));
|
|
696
|
+
}), { name: "routingPending" });
|
|
697
697
|
const isRouting = () => routingPending() || isPending(source);
|
|
698
698
|
const transitionIntent = () => {
|
|
699
699
|
if (!isPending(source))
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"Ryan Turnquist"
|
|
7
7
|
],
|
|
8
8
|
"license": "MIT",
|
|
9
|
-
"version": "2.0.0-next.
|
|
9
|
+
"version": "2.0.0-next.24",
|
|
10
10
|
"homepage": "https://github.com/solidjs/solid-router#readme",
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
@@ -38,21 +38,21 @@
|
|
|
38
38
|
"@rollup/plugin-node-resolve": "15.3.0",
|
|
39
39
|
"@rollup/plugin-terser": "0.4.4",
|
|
40
40
|
"@solidjs/vite-plugin": "3.0.0-next.35",
|
|
41
|
-
"@solidjs/web": "^2.0.0-rc.
|
|
41
|
+
"@solidjs/web": "^2.0.0-rc.8",
|
|
42
42
|
"@types/jest": "^29.5.14",
|
|
43
43
|
"@types/node": "^22.10.0",
|
|
44
44
|
"babel-preset-solid": "^2.0.0-rc.2",
|
|
45
45
|
"jsdom": "^25.0.1",
|
|
46
46
|
"prettier": "^3.4.1",
|
|
47
47
|
"rollup": "^4.27.4",
|
|
48
|
-
"solid-js": "^2.0.0-rc.
|
|
48
|
+
"solid-js": "^2.0.0-rc.8",
|
|
49
49
|
"typescript": "^5.7.2",
|
|
50
50
|
"vite": "^8.2.2",
|
|
51
51
|
"vitest": "^4.1.11"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"@solidjs/web": "^2.0.0-rc.
|
|
55
|
-
"solid-js": "^2.0.0-rc.
|
|
54
|
+
"@solidjs/web": "^2.0.0-rc.8",
|
|
55
|
+
"solid-js": "^2.0.0-rc.8"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "rm -rf dist && tsc && rollup -c",
|