@solidjs/router 1.0.0-next.10 → 1.0.0-next.11
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/fs.d.ts +53 -0
- package/dist/fs.js +30 -0
- package/dist/index.js +31 -31
- package/dist/routers/scrollRestoration.d.ts +12 -4
- package/dist/routers/scrollRestoration.js +30 -31
- package/package.json +3 -2
package/dist/fs.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { RouteSectionComponent } from "./types.js";
|
|
2
|
+
/** A code-split module ref: delivered as a dynamic import. */
|
|
3
|
+
export interface FileRouteLazyRef<M = Record<string, unknown>> {
|
|
4
|
+
src: string;
|
|
5
|
+
import(): Promise<M>;
|
|
6
|
+
}
|
|
7
|
+
/** An eager module ref: its picked exports are imported statically. */
|
|
8
|
+
export interface FileRouteEagerRef<M = Record<string, unknown>> {
|
|
9
|
+
require(): M;
|
|
10
|
+
}
|
|
11
|
+
/** The shape of a nested route-manifest entry the adapter consumes. */
|
|
12
|
+
export interface FileRouteEntry {
|
|
13
|
+
path: string;
|
|
14
|
+
page?: boolean;
|
|
15
|
+
$component?: FileRouteLazyRef<any> | undefined;
|
|
16
|
+
$$route?: FileRouteEagerRef<any> | undefined;
|
|
17
|
+
children?: readonly FileRouteEntry[] | undefined;
|
|
18
|
+
}
|
|
19
|
+
type RouteConfigOf<E> = E extends {
|
|
20
|
+
$$route: {
|
|
21
|
+
require(): {
|
|
22
|
+
route: infer R;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
} ? R : {};
|
|
26
|
+
/**
|
|
27
|
+
* One manifest entry as a route definition. `children` is a required key on
|
|
28
|
+
* purpose: an optional key infers as `C | undefined`, and that union
|
|
29
|
+
* distributes `RoutePaths` into an uncallable shape.
|
|
30
|
+
*/
|
|
31
|
+
export type FileRouteFrom<E> = RouteConfigOf<E> & {
|
|
32
|
+
path: E extends {
|
|
33
|
+
path: infer P extends string;
|
|
34
|
+
} ? P : never;
|
|
35
|
+
component: E extends {
|
|
36
|
+
$component: object;
|
|
37
|
+
} ? RouteSectionComponent : undefined;
|
|
38
|
+
children: E extends {
|
|
39
|
+
children: infer C extends readonly FileRouteEntry[];
|
|
40
|
+
} ? FileRoutesFrom<C> : undefined;
|
|
41
|
+
};
|
|
42
|
+
export type FileRoutesFrom<T extends readonly FileRouteEntry[]> = {
|
|
43
|
+
[K in keyof T]: FileRouteFrom<T[K]>;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Converts the nested page entries of a file-system route manifest into
|
|
47
|
+
* route definitions: `$component` refs become code-split `lazy` components
|
|
48
|
+
* (their `src` doubles as the `moduleUrl` core resolves assets and islands
|
|
49
|
+
* against), and each entry's `route` config export is spread into its
|
|
50
|
+
* definition. Pass the result to `createRouter({ routes })`.
|
|
51
|
+
*/
|
|
52
|
+
export declare function fileRoutes<const T extends readonly FileRouteEntry[]>(entries: T): FileRoutesFrom<T>;
|
|
53
|
+
export {};
|
package/dist/fs.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { lazy } from "solid-js";
|
|
2
|
+
/**
|
|
3
|
+
* Converts the nested page entries of a file-system route manifest into
|
|
4
|
+
* route definitions: `$component` refs become code-split `lazy` components
|
|
5
|
+
* (their `src` doubles as the `moduleUrl` core resolves assets and islands
|
|
6
|
+
* against), and each entry's `route` config export is spread into its
|
|
7
|
+
* definition. Pass the result to `createRouter({ routes })`.
|
|
8
|
+
*/
|
|
9
|
+
export function fileRoutes(entries) {
|
|
10
|
+
const components = new Map();
|
|
11
|
+
const componentOf = (ref) => {
|
|
12
|
+
let component = components.get(ref.src);
|
|
13
|
+
if (!component) {
|
|
14
|
+
component = lazy(ref.import, ref.src);
|
|
15
|
+
components.set(ref.src, component);
|
|
16
|
+
}
|
|
17
|
+
return component;
|
|
18
|
+
};
|
|
19
|
+
const toRoute = (entry) => {
|
|
20
|
+
const config = (entry.$$route?.require().route ?? {});
|
|
21
|
+
return {
|
|
22
|
+
...config,
|
|
23
|
+
path: entry.path,
|
|
24
|
+
component: entry.$component ? componentOf(entry.$component) : undefined,
|
|
25
|
+
info: { ...config.info, filesystem: true },
|
|
26
|
+
children: entry.children ? entry.children.map(toRoute) : undefined
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
return entries.map(toRoute);
|
|
30
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1751,10 +1751,18 @@ const STORAGE_KEY = "solid-router:scroll";
|
|
|
1751
1751
|
* scroll time (rather than at exit) stays correct through `useBeforeLeave`
|
|
1752
1752
|
* blocked/reverted traversals. The map persists to sessionStorage on pagehide
|
|
1753
1753
|
* so restoration survives reloads, which `scrollRestoration = "manual"`
|
|
1754
|
-
* otherwise disables.
|
|
1755
|
-
*
|
|
1756
|
-
*
|
|
1757
|
-
*
|
|
1754
|
+
* otherwise disables.
|
|
1755
|
+
*
|
|
1756
|
+
* Restoration is a single scroll once routing settles — the same strategy
|
|
1757
|
+
* SvelteKit, TanStack Router and React Router use. Settling after the
|
|
1758
|
+
* transition commits is what makes the offset reachable; chasing a still-
|
|
1759
|
+
* growing document afterwards (a ResizeObserver re-asserting the offset as
|
|
1760
|
+
* content arrives) was tried and removed: no peer router does it, an
|
|
1761
|
+
* unbounded observer re-clamps the viewport to the bottom when the target is
|
|
1762
|
+
* never reachable (a list that is genuinely shorter now), and scroll-induced
|
|
1763
|
+
* layout changes can feed it back into itself. Content that commits after the
|
|
1764
|
+
* transition settles — an image without reserved space, a boundary below the
|
|
1765
|
+
* fold — keeps whatever offset the document can hold.
|
|
1758
1766
|
*/
|
|
1759
1767
|
function createScrollRestoration() {
|
|
1760
1768
|
window.history.scrollRestoration = "manual";
|
|
@@ -1768,19 +1776,11 @@ function createScrollRestoration() {
|
|
|
1768
1776
|
const depth = () => window.history.state && window.history.state._depth;
|
|
1769
1777
|
let programmatic = false;
|
|
1770
1778
|
let pending;
|
|
1771
|
-
let disconnect;
|
|
1772
|
-
const cancelGuard = () => {
|
|
1773
|
-
disconnect && disconnect();
|
|
1774
|
-
disconnect = undefined;
|
|
1775
|
-
};
|
|
1776
1779
|
const unbind = [bindEvent(window, "scroll", () => {
|
|
1777
1780
|
const d = depth();
|
|
1778
1781
|
if (d != null) positions[d] = window.scrollY;
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
pending = undefined;
|
|
1782
|
-
cancelGuard();
|
|
1783
|
-
}
|
|
1782
|
+
// the user took over — a pending restore would yank them
|
|
1783
|
+
if (!programmatic) pending = undefined;
|
|
1784
1784
|
}), bindEvent(window, "pagehide", () => {
|
|
1785
1785
|
try {
|
|
1786
1786
|
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(positions));
|
|
@@ -1791,19 +1791,11 @@ function createScrollRestoration() {
|
|
|
1791
1791
|
const y = positions[pending];
|
|
1792
1792
|
pending = undefined;
|
|
1793
1793
|
if (y == null) return;
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
// reachable once the document is tall enough to hold the offset
|
|
1800
|
-
return document.documentElement.scrollHeight - window.innerHeight >= y;
|
|
1801
|
-
};
|
|
1802
|
-
if (!attempt() && typeof ResizeObserver !== "undefined") {
|
|
1803
|
-
const observer = new ResizeObserver(() => attempt() && cancelGuard());
|
|
1804
|
-
observer.observe(document.documentElement);
|
|
1805
|
-
disconnect = () => observer.disconnect();
|
|
1806
|
-
}
|
|
1794
|
+
// flagged so the resulting scroll event is not mistaken for the user
|
|
1795
|
+
// taking over (which cancels a pending restore)
|
|
1796
|
+
programmatic = true;
|
|
1797
|
+
window.scrollTo(0, y);
|
|
1798
|
+
programmatic = false;
|
|
1807
1799
|
};
|
|
1808
1800
|
return {
|
|
1809
1801
|
/** When the adapter notifies a traversal: mark the target for restoration. */
|
|
@@ -1821,16 +1813,24 @@ function createScrollRestoration() {
|
|
|
1821
1813
|
// isRouting, which reports in-flight transitions — native pops
|
|
1822
1814
|
// included — and holds the restore until they commit. restore() no-ops
|
|
1823
1815
|
// unless a traversal marked a target, so push navigations are inert.
|
|
1816
|
+
// `transparent` keeps the effect invisible to the hydration id scheme —
|
|
1817
|
+
// same reasoning as the link-claims effect (claims.ts): this setup is
|
|
1818
|
+
// client-only, so an id-consuming node here has no server counterpart
|
|
1819
|
+
// and every hydration id allocated after it shifts by one child slot.
|
|
1820
|
+
// The visible failure is any <Loading> content that settled before the
|
|
1821
|
+
// shell flush (a cache hit, a preloaded query): its serialized value and
|
|
1822
|
+
// inlined markup are keyed under the server's ids, the shifted client
|
|
1823
|
+
// misses both, recomputes, and re-renders the route fresh — duplicating
|
|
1824
|
+
// the server DOM and leaving it inert.
|
|
1824
1825
|
createEffect(() => ({
|
|
1825
1826
|
url: router.location.pathname + router.location.search + router.location.hash,
|
|
1826
1827
|
routing: router.isRouting()
|
|
1827
1828
|
}), current => {
|
|
1828
1829
|
if (!current.routing) restore();
|
|
1830
|
+
}, {
|
|
1831
|
+
transparent: true
|
|
1829
1832
|
});
|
|
1830
|
-
onCleanup(() =>
|
|
1831
|
-
unbind.forEach(u => u());
|
|
1832
|
-
cancelGuard();
|
|
1833
|
-
});
|
|
1833
|
+
onCleanup(() => unbind.forEach(u => u()));
|
|
1834
1834
|
// reload/back_forward document loads land on an existing entry (a fresh
|
|
1835
1835
|
// navigation starts a new one and belongs at the top); the effect's
|
|
1836
1836
|
// initial run performs the restore after first render
|
|
@@ -11,10 +11,18 @@ import type { RouterHistory } from "./history.js";
|
|
|
11
11
|
* scroll time (rather than at exit) stays correct through `useBeforeLeave`
|
|
12
12
|
* blocked/reverted traversals. The map persists to sessionStorage on pagehide
|
|
13
13
|
* so restoration survives reloads, which `scrollRestoration = "manual"`
|
|
14
|
-
* otherwise disables.
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* otherwise disables.
|
|
15
|
+
*
|
|
16
|
+
* Restoration is a single scroll once routing settles — the same strategy
|
|
17
|
+
* SvelteKit, TanStack Router and React Router use. Settling after the
|
|
18
|
+
* transition commits is what makes the offset reachable; chasing a still-
|
|
19
|
+
* growing document afterwards (a ResizeObserver re-asserting the offset as
|
|
20
|
+
* content arrives) was tried and removed: no peer router does it, an
|
|
21
|
+
* unbounded observer re-clamps the viewport to the bottom when the target is
|
|
22
|
+
* never reachable (a list that is genuinely shorter now), and scroll-induced
|
|
23
|
+
* layout changes can feed it back into itself. Content that commits after the
|
|
24
|
+
* transition settles — an image without reserved space, a boundary below the
|
|
25
|
+
* fold — keeps whatever offset the document can hold.
|
|
18
26
|
*/
|
|
19
27
|
export declare function createScrollRestoration(): {
|
|
20
28
|
/** When the adapter notifies a traversal: mark the target for restoration. */
|
|
@@ -12,10 +12,18 @@ const STORAGE_KEY = "solid-router:scroll";
|
|
|
12
12
|
* scroll time (rather than at exit) stays correct through `useBeforeLeave`
|
|
13
13
|
* blocked/reverted traversals. The map persists to sessionStorage on pagehide
|
|
14
14
|
* so restoration survives reloads, which `scrollRestoration = "manual"`
|
|
15
|
-
* otherwise disables.
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* otherwise disables.
|
|
16
|
+
*
|
|
17
|
+
* Restoration is a single scroll once routing settles — the same strategy
|
|
18
|
+
* SvelteKit, TanStack Router and React Router use. Settling after the
|
|
19
|
+
* transition commits is what makes the offset reachable; chasing a still-
|
|
20
|
+
* growing document afterwards (a ResizeObserver re-asserting the offset as
|
|
21
|
+
* content arrives) was tried and removed: no peer router does it, an
|
|
22
|
+
* unbounded observer re-clamps the viewport to the bottom when the target is
|
|
23
|
+
* never reachable (a list that is genuinely shorter now), and scroll-induced
|
|
24
|
+
* layout changes can feed it back into itself. Content that commits after the
|
|
25
|
+
* transition settles — an image without reserved space, a boundary below the
|
|
26
|
+
* fold — keeps whatever offset the document can hold.
|
|
19
27
|
*/
|
|
20
28
|
export function createScrollRestoration() {
|
|
21
29
|
window.history.scrollRestoration = "manual";
|
|
@@ -30,21 +38,14 @@ export function createScrollRestoration() {
|
|
|
30
38
|
const depth = () => window.history.state && window.history.state._depth;
|
|
31
39
|
let programmatic = false;
|
|
32
40
|
let pending;
|
|
33
|
-
let disconnect;
|
|
34
|
-
const cancelGuard = () => {
|
|
35
|
-
disconnect && disconnect();
|
|
36
|
-
disconnect = undefined;
|
|
37
|
-
};
|
|
38
41
|
const unbind = [
|
|
39
42
|
bindEvent(window, "scroll", () => {
|
|
40
43
|
const d = depth();
|
|
41
44
|
if (d != null)
|
|
42
45
|
positions[d] = window.scrollY;
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
// the user took over — a pending restore would yank them
|
|
47
|
+
if (!programmatic)
|
|
45
48
|
pending = undefined;
|
|
46
|
-
cancelGuard();
|
|
47
|
-
}
|
|
48
49
|
}),
|
|
49
50
|
bindEvent(window, "pagehide", () => {
|
|
50
51
|
try {
|
|
@@ -60,19 +61,11 @@ export function createScrollRestoration() {
|
|
|
60
61
|
pending = undefined;
|
|
61
62
|
if (y == null)
|
|
62
63
|
return;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
// reachable once the document is tall enough to hold the offset
|
|
69
|
-
return document.documentElement.scrollHeight - window.innerHeight >= y;
|
|
70
|
-
};
|
|
71
|
-
if (!attempt() && typeof ResizeObserver !== "undefined") {
|
|
72
|
-
const observer = new ResizeObserver(() => attempt() && cancelGuard());
|
|
73
|
-
observer.observe(document.documentElement);
|
|
74
|
-
disconnect = () => observer.disconnect();
|
|
75
|
-
}
|
|
64
|
+
// flagged so the resulting scroll event is not mistaken for the user
|
|
65
|
+
// taking over (which cancels a pending restore)
|
|
66
|
+
programmatic = true;
|
|
67
|
+
window.scrollTo(0, y);
|
|
68
|
+
programmatic = false;
|
|
76
69
|
};
|
|
77
70
|
return {
|
|
78
71
|
/** When the adapter notifies a traversal: mark the target for restoration. */
|
|
@@ -92,17 +85,23 @@ export function createScrollRestoration() {
|
|
|
92
85
|
// isRouting, which reports in-flight transitions — native pops
|
|
93
86
|
// included — and holds the restore until they commit. restore() no-ops
|
|
94
87
|
// unless a traversal marked a target, so push navigations are inert.
|
|
88
|
+
// `transparent` keeps the effect invisible to the hydration id scheme —
|
|
89
|
+
// same reasoning as the link-claims effect (claims.ts): this setup is
|
|
90
|
+
// client-only, so an id-consuming node here has no server counterpart
|
|
91
|
+
// and every hydration id allocated after it shifts by one child slot.
|
|
92
|
+
// The visible failure is any <Loading> content that settled before the
|
|
93
|
+
// shell flush (a cache hit, a preloaded query): its serialized value and
|
|
94
|
+
// inlined markup are keyed under the server's ids, the shifted client
|
|
95
|
+
// misses both, recomputes, and re-renders the route fresh — duplicating
|
|
96
|
+
// the server DOM and leaving it inert.
|
|
95
97
|
createEffect(() => ({
|
|
96
98
|
url: router.location.pathname + router.location.search + router.location.hash,
|
|
97
99
|
routing: router.isRouting()
|
|
98
100
|
}), current => {
|
|
99
101
|
if (!current.routing)
|
|
100
102
|
restore();
|
|
101
|
-
});
|
|
102
|
-
onCleanup(() =>
|
|
103
|
-
unbind.forEach(u => u());
|
|
104
|
-
cancelGuard();
|
|
105
|
-
});
|
|
103
|
+
}, { transparent: true });
|
|
104
|
+
onCleanup(() => unbind.forEach(u => u()));
|
|
106
105
|
// reload/back_forward document loads land on an existing entry (a fresh
|
|
107
106
|
// navigation starts a new one and belongs at the top); the effect's
|
|
108
107
|
// initial run performs the restore after first render
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"Ryan Turnquist"
|
|
7
7
|
],
|
|
8
8
|
"license": "MIT",
|
|
9
|
-
"version": "1.0.0-next.
|
|
9
|
+
"version": "1.0.0-next.11",
|
|
10
10
|
"homepage": "https://github.com/solidjs/solid-router#readme",
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"solid": "./dist/index.jsx",
|
|
24
24
|
"default": "./dist/index.js"
|
|
25
25
|
},
|
|
26
|
-
"./server": "./dist/server.js"
|
|
26
|
+
"./server": "./dist/server.js",
|
|
27
|
+
"./fs": "./dist/fs.js"
|
|
27
28
|
},
|
|
28
29
|
"files": [
|
|
29
30
|
"dist"
|