toiljs 0.0.15 → 0.0.19

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.
Files changed (273) hide show
  1. package/.babelrc +13 -13
  2. package/.gitattributes +2 -2
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +38 -38
  4. package/.github/ISSUE_TEMPLATE/bug_report.yml +90 -90
  5. package/.github/ISSUE_TEMPLATE/config.yml +8 -8
  6. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
  7. package/.github/PULL_REQUEST_TEMPLATE.md +43 -43
  8. package/.github/changelog-config.json +45 -45
  9. package/.github/dependabot.yml +27 -27
  10. package/.github/workflows/ci.yml +191 -191
  11. package/.prettierrc.json +11 -11
  12. package/.vscode/settings.json +9 -9
  13. package/CHANGELOG.md +116 -5
  14. package/LICENSE +187 -187
  15. package/README.md +524 -315
  16. package/as-pect.asconfig.json +34 -34
  17. package/as-pect.config.js +65 -65
  18. package/assets/logo.svg +36 -36
  19. package/build/backend/.tsbuildinfo +1 -1
  20. package/build/backend/index.d.ts +1 -0
  21. package/build/backend/index.js +20 -1
  22. package/build/cli/.tsbuildinfo +1 -1
  23. package/build/cli/index.js +1320 -696
  24. package/build/client/.tsbuildinfo +1 -1
  25. package/build/client/dev/devtools.d.ts +6 -0
  26. package/build/client/dev/devtools.js +479 -0
  27. package/build/client/dev/error-overlay.d.ts +9 -0
  28. package/build/client/dev/error-overlay.js +19 -4
  29. package/build/client/errors.d.ts +1 -0
  30. package/build/client/errors.js +3 -0
  31. package/build/client/index.d.ts +2 -0
  32. package/build/client/index.js +2 -0
  33. package/build/client/navigation/prefetch.d.ts +1 -0
  34. package/build/client/navigation/prefetch.js +35 -0
  35. package/build/client/routing/Router.js +1 -1
  36. package/build/client/routing/hooks.js +6 -2
  37. package/build/client/routing/loader.d.ts +23 -0
  38. package/build/client/routing/loader.js +53 -7
  39. package/build/client/routing/mount.js +4 -3
  40. package/build/client/rpc.d.ts +1 -0
  41. package/build/client/rpc.js +37 -0
  42. package/build/compiler/.tsbuildinfo +1 -1
  43. package/build/compiler/config.d.ts +16 -0
  44. package/build/compiler/config.js +9 -0
  45. package/build/compiler/docs.js +78 -21
  46. package/build/compiler/generate.js +5 -4
  47. package/build/compiler/index.d.ts +3 -2
  48. package/build/compiler/index.js +2 -2
  49. package/build/compiler/plugin.js +228 -0
  50. package/build/compiler/prerender.d.ts +1 -0
  51. package/build/compiler/prerender.js +1 -1
  52. package/build/compiler/seo.d.ts +1 -1
  53. package/build/compiler/seo.js +20 -5
  54. package/build/compiler/ssg.js +39 -2
  55. package/build/compiler/vite.js +25 -0
  56. package/build/io/.tsbuildinfo +1 -1
  57. package/build/io/codec.d.ts +54 -0
  58. package/build/io/codec.js +143 -0
  59. package/build/io/index.d.ts +1 -2
  60. package/build/io/index.js +1 -2
  61. package/build/logger/.tsbuildinfo +1 -1
  62. package/build/shared/.tsbuildinfo +1 -1
  63. package/eslint.config.js +48 -48
  64. package/examples/basic/client/404.tsx +11 -11
  65. package/examples/basic/client/components/.gitkeep +1 -1
  66. package/examples/basic/client/global-error.tsx +13 -13
  67. package/examples/basic/client/layout.tsx +25 -25
  68. package/examples/basic/client/public/images/.gitkeep +1 -1
  69. package/examples/basic/client/public/images/logo.svg +36 -36
  70. package/examples/basic/client/public/robots.txt +2 -2
  71. package/examples/basic/client/routes/docs/[...slug].tsx +12 -12
  72. package/examples/basic/client/routes/features/error/error.tsx +16 -16
  73. package/examples/basic/client/routes/features/index.tsx +1 -1
  74. package/examples/basic/client/routes/features/template/b.tsx +14 -14
  75. package/examples/basic/client/routes/files/[[...slug]].tsx +21 -21
  76. package/examples/basic/client/routes/gallery/layout.tsx +13 -13
  77. package/examples/basic/client/routes/io.tsx +23 -24
  78. package/examples/basic/client/routes/loader-demo/loading.tsx +13 -13
  79. package/examples/basic/client/routes/rest.tsx +74 -0
  80. package/examples/basic/client/routes/rpc.tsx +43 -0
  81. package/examples/basic/client/routes/search.tsx +61 -61
  82. package/examples/basic/client/toil.tsx +5 -5
  83. package/package.json +167 -148
  84. package/presets/eslint.js +88 -88
  85. package/presets/no-uint8array-tostring.js +200 -200
  86. package/presets/prettier-plugin.js +51 -0
  87. package/presets/prettier.json +19 -18
  88. package/presets/tsconfig.json +37 -37
  89. package/server/runtime/README.md +97 -0
  90. package/server/runtime/abort/abort.ts +27 -0
  91. package/server/runtime/env/Server.ts +61 -0
  92. package/server/runtime/envelope.ts +191 -0
  93. package/server/runtime/exports/index.ts +52 -0
  94. package/server/runtime/handlers/ToilHandler.ts +34 -0
  95. package/server/runtime/index.ts +26 -0
  96. package/server/runtime/lang/Potential.ts +5 -0
  97. package/server/runtime/memory.ts +81 -0
  98. package/server/runtime/request.ts +55 -0
  99. package/server/runtime/response.ts +86 -0
  100. package/server/runtime/rest/Rest.ts +39 -0
  101. package/server/runtime/rest/RestHandler.ts +20 -0
  102. package/server/runtime/rest/RouteContext.ts +82 -0
  103. package/server/runtime/rest/match.ts +48 -0
  104. package/server/runtime/tsconfig.json +7 -0
  105. package/src/backend/index.ts +202 -160
  106. package/src/cli/create.ts +15 -5
  107. package/src/cli/diagnostics.ts +81 -0
  108. package/src/cli/doctor.ts +384 -7
  109. package/src/cli/index.ts +11 -2
  110. package/src/cli/proc.ts +50 -50
  111. package/src/cli/updates.ts +69 -69
  112. package/src/cli/validate.ts +31 -31
  113. package/src/client/channel/channel.ts +146 -146
  114. package/src/client/components/Form.tsx +65 -65
  115. package/src/client/components/Script.tsx +113 -113
  116. package/src/client/components/Slot.tsx +21 -21
  117. package/src/client/dev/devtools.tsx +1018 -0
  118. package/src/client/dev/error-overlay.tsx +30 -4
  119. package/src/client/errors.ts +11 -0
  120. package/src/client/head/head.ts +167 -167
  121. package/src/client/head/metadata.ts +112 -112
  122. package/src/client/index.ts +91 -89
  123. package/src/client/navigation/NavLink.tsx +86 -86
  124. package/src/client/navigation/navigation.ts +235 -235
  125. package/src/client/navigation/prefetch.ts +169 -130
  126. package/src/client/navigation/scroll.ts +53 -53
  127. package/src/client/routing/Router.tsx +8 -2
  128. package/src/client/routing/action.ts +122 -122
  129. package/src/client/routing/error-boundary.tsx +43 -43
  130. package/src/client/routing/hooks.ts +21 -6
  131. package/src/client/routing/loader.ts +325 -235
  132. package/src/client/routing/match.ts +47 -47
  133. package/src/client/routing/mount.tsx +54 -52
  134. package/src/client/routing/params-context.ts +10 -10
  135. package/src/client/routing/slot-context.ts +7 -7
  136. package/src/client/rpc.ts +64 -0
  137. package/src/client/search/search.ts +189 -189
  138. package/src/client/search/use-page-search.ts +73 -73
  139. package/src/client/types.ts +73 -73
  140. package/src/compiler/config.ts +221 -182
  141. package/src/compiler/docs.ts +285 -228
  142. package/src/compiler/generate.ts +395 -394
  143. package/src/compiler/index.ts +66 -57
  144. package/src/compiler/pages.ts +70 -70
  145. package/src/compiler/plugin.ts +258 -2
  146. package/src/compiler/prerender.ts +156 -156
  147. package/src/compiler/seo.ts +417 -390
  148. package/src/compiler/ssg.ts +171 -126
  149. package/src/compiler/vite.ts +34 -0
  150. package/src/io/FastMap.ts +151 -127
  151. package/src/io/FastSet.ts +15 -1
  152. package/src/io/codec.ts +217 -0
  153. package/src/io/index.ts +10 -11
  154. package/src/io/lengths.ts +14 -14
  155. package/src/io/types.ts +19 -18
  156. package/src/logger/index.ts +22 -22
  157. package/src/shared/index.ts +10 -10
  158. package/std/client/index.d.ts +15 -15
  159. package/std/client/package.json +3 -3
  160. package/test/assembly/example.spec.ts +17 -7
  161. package/test/channel.test.ts +21 -21
  162. package/test/doctor.test.ts +65 -0
  163. package/test/dom/Link.test.tsx +47 -47
  164. package/test/dom/NavLink.test.tsx +37 -37
  165. package/test/dom/error-overlay.test.tsx +44 -44
  166. package/test/dom/loader.test.tsx +121 -121
  167. package/test/dom/navigation.test.ts +59 -59
  168. package/test/dom/revalidate.test.tsx +38 -38
  169. package/test/dom/route-head.test.tsx +78 -78
  170. package/test/dom/router-loading.test.tsx +44 -44
  171. package/test/dom/scroll.test.ts +56 -56
  172. package/test/dom/use-metadata.test.tsx +58 -58
  173. package/test/errors.test.ts +21 -0
  174. package/test/io.test.ts +117 -93
  175. package/test/navlink.test.ts +28 -28
  176. package/test/placeholder.test.ts +9 -9
  177. package/test/prettier-plugin.test.ts +46 -0
  178. package/test/routes.test.ts +76 -76
  179. package/test/rpc.test.ts +50 -0
  180. package/test/seo.test.ts +175 -164
  181. package/test/slot-layouts.test.ts +69 -69
  182. package/test/ssg.test.ts +36 -36
  183. package/test/update.test.ts +44 -44
  184. package/test/validate.test.ts +42 -42
  185. package/tests/data-parity/generated-parity.ts +99 -0
  186. package/tests/data-parity/parity.ts +80 -0
  187. package/tests/data-parity/spec.ts +46 -0
  188. package/toil-routes.d.ts +7 -0
  189. package/tsconfig.backend.json +13 -13
  190. package/tsconfig.base.json +35 -35
  191. package/tsconfig.cli.json +13 -13
  192. package/tsconfig.client.json +14 -14
  193. package/tsconfig.compiler.json +13 -13
  194. package/tsconfig.io.json +12 -12
  195. package/tsconfig.json +22 -22
  196. package/tsconfig.logger.json +12 -12
  197. package/tsconfig.server.json +10 -10
  198. package/tsconfig.shared.json +12 -12
  199. package/vitest.config.ts +26 -26
  200. package/.idea/codeStyles/Project.xml +0 -54
  201. package/.idea/codeStyles/codeStyleConfig.xml +0 -5
  202. package/.idea/inspectionProfiles/Project_Default.xml +0 -6
  203. package/.idea/modules.xml +0 -8
  204. package/.idea/prettier.xml +0 -7
  205. package/.idea/toiljs.iml +0 -8
  206. package/.idea/vcs.xml +0 -6
  207. package/.toil/entry.tsx +0 -9
  208. package/.toil/index.html +0 -12
  209. package/.toil/routes.ts +0 -9
  210. package/build/cli/configure.d.ts +0 -16
  211. package/build/cli/configure.js +0 -272
  212. package/build/cli/create.d.ts +0 -16
  213. package/build/cli/create.js +0 -420
  214. package/build/cli/diagnostics.d.ts +0 -55
  215. package/build/cli/diagnostics.js +0 -333
  216. package/build/cli/doctor.d.ts +0 -6
  217. package/build/cli/doctor.js +0 -249
  218. package/build/cli/features.d.ts +0 -25
  219. package/build/cli/features.js +0 -107
  220. package/build/cli/index.d.ts +0 -2
  221. package/build/cli/proc.d.ts +0 -6
  222. package/build/cli/proc.js +0 -31
  223. package/build/cli/ui.d.ts +0 -9
  224. package/build/cli/ui.js +0 -75
  225. package/build/cli/update.d.ts +0 -7
  226. package/build/cli/update.js +0 -117
  227. package/build/cli/updates.d.ts +0 -10
  228. package/build/cli/updates.js +0 -45
  229. package/build/cli/validate.d.ts +0 -4
  230. package/build/cli/validate.js +0 -19
  231. package/build/client/Link.d.ts +0 -8
  232. package/build/client/Link.js +0 -44
  233. package/build/client/NavLink.d.ts +0 -14
  234. package/build/client/NavLink.js +0 -37
  235. package/build/client/Router.d.ts +0 -7
  236. package/build/client/Router.js +0 -55
  237. package/build/client/channel.d.ts +0 -23
  238. package/build/client/channel.js +0 -94
  239. package/build/client/error-boundary.d.ts +0 -16
  240. package/build/client/error-boundary.js +0 -19
  241. package/build/client/head.d.ts +0 -26
  242. package/build/client/head.js +0 -87
  243. package/build/client/hooks.d.ts +0 -17
  244. package/build/client/hooks.js +0 -48
  245. package/build/client/lazy.d.ts +0 -16
  246. package/build/client/lazy.js +0 -53
  247. package/build/client/match.d.ts +0 -2
  248. package/build/client/match.js +0 -32
  249. package/build/client/mount.d.ts +0 -2
  250. package/build/client/mount.js +0 -13
  251. package/build/client/navigation.d.ts +0 -13
  252. package/build/client/navigation.js +0 -97
  253. package/build/client/params-context.d.ts +0 -2
  254. package/build/client/params-context.js +0 -2
  255. package/build/client/prefetch.d.ts +0 -11
  256. package/build/client/prefetch.js +0 -100
  257. package/build/client/runtime.d.ts +0 -31
  258. package/build/client/runtime.js +0 -112
  259. package/build/client/scroll.d.ts +0 -8
  260. package/build/client/scroll.js +0 -36
  261. package/build/io/BinaryReader.d.ts +0 -44
  262. package/build/io/BinaryReader.js +0 -244
  263. package/build/io/BinaryWriter.d.ts +0 -44
  264. package/build/io/BinaryWriter.js +0 -297
  265. package/build/server/release.wasm +0 -0
  266. package/build/server/release.wat +0 -9
  267. package/src/io/BinaryReader.ts +0 -340
  268. package/src/io/BinaryWriter.ts +0 -385
  269. package/src/server/index.ts +0 -10
  270. package/src/server/main.ts +0 -13
  271. package/src/server/tsconfig.json +0 -4
  272. package/toil-env.d.ts +0 -16
  273. package/toilconfig.json +0 -30
@@ -1,94 +0,0 @@
1
- import { useCallback, useEffect, useRef, useState } from 'react';
2
- export function resolveChannelUrl(path = '/_toil', location = window.location) {
3
- const scheme = location.protocol === 'https:' ? 'wss:' : 'ws:';
4
- const normalized = path.startsWith('/') ? path : `/${path}`;
5
- return `${scheme}//${location.host}${normalized}`;
6
- }
7
- export function connectChannel(onMessage, options = {}) {
8
- const url = options.url ?? resolveChannelUrl(options.path);
9
- const reconnect = options.reconnect ?? true;
10
- const delay = options.reconnectDelay ?? 1000;
11
- let socket = null;
12
- let stopped = false;
13
- let timer;
14
- const open = () => {
15
- const ws = new WebSocket(url);
16
- ws.binaryType = 'arraybuffer';
17
- socket = ws;
18
- ws.addEventListener('message', (event) => {
19
- if (typeof event.data === 'string')
20
- onMessage(event.data);
21
- else if (event.data instanceof ArrayBuffer)
22
- onMessage(event.data);
23
- });
24
- ws.addEventListener('close', () => {
25
- if (!stopped && reconnect)
26
- timer = setTimeout(open, delay);
27
- });
28
- };
29
- open();
30
- return {
31
- send: (data) => {
32
- if (socket && socket.readyState === WebSocket.OPEN)
33
- socket.send(data);
34
- },
35
- close: () => {
36
- stopped = true;
37
- if (timer !== undefined)
38
- clearTimeout(timer);
39
- socket?.close();
40
- },
41
- };
42
- }
43
- export function useChannel(options = {}) {
44
- const { path, url, reconnect, reconnectDelay } = options;
45
- const [connected, setConnected] = useState(false);
46
- const [messages, setMessages] = useState([]);
47
- const socketRef = useRef(null);
48
- useEffect(() => {
49
- const target = url ?? resolveChannelUrl(path);
50
- const shouldReconnect = reconnect ?? true;
51
- const delay = reconnectDelay ?? 1000;
52
- let stopped = false;
53
- let timer;
54
- const open = () => {
55
- const ws = new WebSocket(target);
56
- ws.binaryType = 'arraybuffer';
57
- socketRef.current = ws;
58
- ws.addEventListener('open', () => {
59
- if (!stopped)
60
- setConnected(true);
61
- });
62
- ws.addEventListener('message', (event) => {
63
- if (typeof event.data === 'string') {
64
- const data = event.data;
65
- setMessages((prev) => [...prev, data]);
66
- }
67
- else if (event.data instanceof ArrayBuffer) {
68
- const data = event.data;
69
- setMessages((prev) => [...prev, data]);
70
- }
71
- });
72
- ws.addEventListener('close', () => {
73
- if (stopped)
74
- return;
75
- setConnected(false);
76
- if (shouldReconnect)
77
- timer = setTimeout(open, delay);
78
- });
79
- };
80
- open();
81
- return () => {
82
- stopped = true;
83
- if (timer !== undefined)
84
- clearTimeout(timer);
85
- socketRef.current?.close();
86
- };
87
- }, [path, url, reconnect, reconnectDelay]);
88
- const send = useCallback((data) => {
89
- const socket = socketRef.current;
90
- if (socket && socket.readyState === WebSocket.OPEN)
91
- socket.send(data);
92
- }, []);
93
- return { connected, messages, send };
94
- }
@@ -1,16 +0,0 @@
1
- import { Component, type ComponentType, type ReactNode } from 'react';
2
- import type { RouteErrorProps } from './types.js';
3
- interface ErrorBoundaryProps {
4
- readonly fallback: ComponentType<RouteErrorProps>;
5
- readonly children: ReactNode;
6
- }
7
- interface ErrorBoundaryState {
8
- readonly error: Error | null;
9
- }
10
- export declare class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
11
- state: ErrorBoundaryState;
12
- static getDerivedStateFromError(error: Error): ErrorBoundaryState;
13
- reset: () => void;
14
- render(): ReactNode;
15
- }
16
- export {};
@@ -1,19 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Component, Suspense } from 'react';
3
- export class ErrorBoundary extends Component {
4
- state = { error: null };
5
- static getDerivedStateFromError(error) {
6
- return { error };
7
- }
8
- reset = () => {
9
- this.setState({ error: null });
10
- };
11
- render() {
12
- const { error } = this.state;
13
- if (error) {
14
- const Fallback = this.props.fallback;
15
- return (_jsx(Suspense, { fallback: null, children: _jsx(Fallback, { error: error, reset: this.reset }) }));
16
- }
17
- return this.props.children;
18
- }
19
- }
@@ -1,26 +0,0 @@
1
- export interface MetaTag {
2
- readonly name?: string;
3
- readonly property?: string;
4
- readonly content: string;
5
- readonly [attr: string]: string | undefined;
6
- }
7
- export interface LinkTag {
8
- readonly rel: string;
9
- readonly href: string;
10
- readonly [attr: string]: string | undefined;
11
- }
12
- export interface HeadSpec {
13
- readonly title?: string;
14
- readonly titleTemplate?: string;
15
- readonly meta?: readonly MetaTag[];
16
- readonly link?: readonly LinkTag[];
17
- }
18
- export interface ResolvedHead {
19
- readonly title?: string;
20
- readonly meta: MetaTag[];
21
- readonly link: LinkTag[];
22
- }
23
- export declare function mergeHead(specs: readonly HeadSpec[]): ResolvedHead;
24
- export declare function useHead(spec: HeadSpec): void;
25
- export declare function useTitle(title: string): void;
26
- export declare function Head(props: HeadSpec): null;
@@ -1,87 +0,0 @@
1
- import { useEffect } from 'react';
2
- function metaKey(m) {
3
- if (m.name !== undefined)
4
- return `name:${m.name}`;
5
- if (m.property !== undefined)
6
- return `property:${m.property}`;
7
- return `meta:${JSON.stringify(m)}`;
8
- }
9
- export function mergeHead(specs) {
10
- let title;
11
- let titleTemplate;
12
- const meta = new Map();
13
- const link = new Map();
14
- for (const spec of specs) {
15
- if (spec.title !== undefined)
16
- title = spec.title;
17
- if (spec.titleTemplate !== undefined)
18
- titleTemplate = spec.titleTemplate;
19
- for (const m of spec.meta ?? [])
20
- meta.set(metaKey(m), m);
21
- for (const l of spec.link ?? [])
22
- link.set(`${l.rel}:${l.href}`, l);
23
- }
24
- const resolvedTitle = title !== undefined && titleTemplate !== undefined
25
- ? titleTemplate.replace('%s', title)
26
- : title;
27
- return { title: resolvedTitle, meta: [...meta.values()], link: [...link.values()] };
28
- }
29
- const entries = new Map();
30
- let order = [];
31
- let seq = 0;
32
- let baseTitle = null;
33
- function setAttrs(el, attrs) {
34
- el.setAttribute('data-toil-head', '');
35
- for (const [key, value] of Object.entries(attrs)) {
36
- if (value !== undefined)
37
- el.setAttribute(key, value);
38
- }
39
- }
40
- function apply() {
41
- if (typeof document === 'undefined')
42
- return;
43
- if (baseTitle === null)
44
- baseTitle = document.title;
45
- const resolved = mergeHead(order.map((id) => entries.get(id)).filter((s) => !!s));
46
- document.title = resolved.title ?? baseTitle;
47
- for (const stale of document.head.querySelectorAll('[data-toil-head]'))
48
- stale.remove();
49
- for (const m of resolved.meta) {
50
- const el = document.createElement('meta');
51
- setAttrs(el, m);
52
- document.head.appendChild(el);
53
- }
54
- for (const l of resolved.link) {
55
- const el = document.createElement('link');
56
- setAttrs(el, l);
57
- document.head.appendChild(el);
58
- }
59
- }
60
- function addHead(spec) {
61
- const id = ++seq;
62
- entries.set(id, spec);
63
- order.push(id);
64
- apply();
65
- return id;
66
- }
67
- function removeHead(id) {
68
- entries.delete(id);
69
- order = order.filter((x) => x !== id);
70
- apply();
71
- }
72
- export function useHead(spec) {
73
- const json = JSON.stringify(spec);
74
- useEffect(() => {
75
- const id = addHead(JSON.parse(json));
76
- return () => {
77
- removeHead(id);
78
- };
79
- }, [json]);
80
- }
81
- export function useTitle(title) {
82
- useHead({ title });
83
- }
84
- export function Head(props) {
85
- useHead(props);
86
- return null;
87
- }
@@ -1,17 +0,0 @@
1
- import type { RouteParams } from './match.js';
2
- import { navigate, type NavigateOptions } from './navigation.js';
3
- export interface RouterInstance {
4
- push(href: string, options?: NavigateOptions): void;
5
- replace(href: string): void;
6
- back(): void;
7
- forward(): void;
8
- refresh(): void;
9
- prefetch(href: string): void;
10
- }
11
- export declare function useParams(): RouteParams;
12
- export declare function useNavigate(): typeof navigate;
13
- export declare function useRouter(): RouterInstance;
14
- export declare function useLocation(): string;
15
- export declare function usePathname(): string;
16
- export declare function useSearchParams(): URLSearchParams;
17
- export declare function useNavigationPending(): boolean;
@@ -1,48 +0,0 @@
1
- import { startTransition, useContext, useEffect, useMemo, useReducer, useSyncExternalStore, } from 'react';
2
- import { back, forward, isNavigationPending, navigate, refresh, subscribeLocation, subscribePending, } from './navigation.js';
3
- import { ParamsContext } from './params-context.js';
4
- import { prefetch } from './prefetch.js';
5
- const ROUTER = {
6
- push: (href, options) => {
7
- navigate(href, options);
8
- },
9
- replace: (href) => {
10
- navigate(href, { replace: true });
11
- },
12
- back,
13
- forward,
14
- refresh,
15
- prefetch,
16
- };
17
- export function useParams() {
18
- return useContext(ParamsContext);
19
- }
20
- export function useNavigate() {
21
- return navigate;
22
- }
23
- export function useRouter() {
24
- return ROUTER;
25
- }
26
- function useLocationSubscription() {
27
- const [, forceUpdate] = useReducer((n) => n + 1, 0);
28
- useEffect(() => subscribeLocation(() => {
29
- startTransition(() => {
30
- forceUpdate();
31
- });
32
- }), []);
33
- }
34
- export function useLocation() {
35
- useLocationSubscription();
36
- return window.location.pathname;
37
- }
38
- export function usePathname() {
39
- return useLocation();
40
- }
41
- export function useSearchParams() {
42
- useLocationSubscription();
43
- const search = window.location.search;
44
- return useMemo(() => new URLSearchParams(search), [search]);
45
- }
46
- export function useNavigationPending() {
47
- return useSyncExternalStore(subscribePending, isNavigationPending, () => false);
48
- }
@@ -1,16 +0,0 @@
1
- import { type ComponentType, type ReactNode } from 'react';
2
- import type { LayoutComponentLoader, LayoutLoader, NotFoundLoader, RouteDef, RouteErrorProps } from './types.js';
3
- type Loader<P> = () => Promise<{
4
- default: ComponentType<P>;
5
- }>;
6
- export declare function loadingComponent(loader: Loader<object>): ComponentType<object>;
7
- export declare function errorComponent(loader: Loader<RouteErrorProps>): ComponentType<RouteErrorProps>;
8
- export declare function pageComponent(route: RouteDef): ComponentType;
9
- export declare function resolveLayout(loader: NonNullable<LayoutLoader>): ComponentType<{
10
- children?: ReactNode;
11
- }>;
12
- export declare function nestedLayout(loader: LayoutComponentLoader): ComponentType<{
13
- children?: ReactNode;
14
- }>;
15
- export declare function resolveNotFound(loader: NonNullable<NotFoundLoader>): ComponentType;
16
- export {};
@@ -1,53 +0,0 @@
1
- import { lazy } from 'react';
2
- function memoLazy(cache, loader) {
3
- let component = cache.get(loader);
4
- if (!component) {
5
- component = lazy(loader);
6
- cache.set(loader, component);
7
- }
8
- return component;
9
- }
10
- const loadingCache = new Map();
11
- export function loadingComponent(loader) {
12
- return memoLazy(loadingCache, loader);
13
- }
14
- const errorCache = new Map();
15
- export function errorComponent(loader) {
16
- return memoLazy(errorCache, loader);
17
- }
18
- const pageCache = new Map();
19
- export function pageComponent(route) {
20
- let component = pageCache.get(route);
21
- if (!component) {
22
- component = lazy(route.load);
23
- pageCache.set(route, component);
24
- }
25
- return component;
26
- }
27
- let layoutComponent = null;
28
- let layoutLoader = null;
29
- export function resolveLayout(loader) {
30
- if (layoutLoader !== loader || !layoutComponent) {
31
- layoutComponent = lazy(loader);
32
- layoutLoader = loader;
33
- }
34
- return layoutComponent;
35
- }
36
- const nestedLayoutCache = new Map();
37
- export function nestedLayout(loader) {
38
- let component = nestedLayoutCache.get(loader);
39
- if (!component) {
40
- component = lazy(loader);
41
- nestedLayoutCache.set(loader, component);
42
- }
43
- return component;
44
- }
45
- let notFoundComponent = null;
46
- let notFoundLoader = null;
47
- export function resolveNotFound(loader) {
48
- if (notFoundLoader !== loader || !notFoundComponent) {
49
- notFoundComponent = lazy(loader);
50
- notFoundLoader = loader;
51
- }
52
- return notFoundComponent;
53
- }
@@ -1,2 +0,0 @@
1
- export type RouteParams = Record<string, string>;
2
- export declare function matchRoute(pattern: string, pathname: string): RouteParams | null;
@@ -1,32 +0,0 @@
1
- export function matchRoute(pattern, pathname) {
2
- const patternSegs = pattern.split('/').filter(Boolean);
3
- const pathSegs = pathname.split('/').filter(Boolean);
4
- const params = {};
5
- for (let i = 0; i < patternSegs.length; i++) {
6
- const p = patternSegs[i];
7
- if (p.startsWith('**')) {
8
- params[p.slice(2)] = pathSegs
9
- .slice(i)
10
- .map((s) => decodeURIComponent(s))
11
- .join('/');
12
- return params;
13
- }
14
- if (p.startsWith('*')) {
15
- const rest = pathSegs.slice(i);
16
- if (rest.length === 0)
17
- return null;
18
- params[p.slice(1)] = rest.map((s) => decodeURIComponent(s)).join('/');
19
- return params;
20
- }
21
- if (i >= pathSegs.length)
22
- return null;
23
- const value = pathSegs[i];
24
- if (p.startsWith(':')) {
25
- params[p.slice(1)] = decodeURIComponent(value);
26
- }
27
- else if (p !== value) {
28
- return null;
29
- }
30
- }
31
- return patternSegs.length === pathSegs.length ? params : null;
32
- }
@@ -1,2 +0,0 @@
1
- import type { LayoutLoader, NotFoundLoader, RouteDef } from './types.js';
2
- export declare function mount(routes: RouteDef[], layout?: LayoutLoader, notFound?: NotFoundLoader): void;
@@ -1,13 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { createRoot } from 'react-dom/client';
3
- import { initNavigation } from './navigation.js';
4
- import { startPrefetcher } from './prefetch.js';
5
- import { Router } from './Router.js';
6
- export function mount(routes, layout = null, notFound = null) {
7
- const el = document.getElementById('root');
8
- if (!el)
9
- throw new Error('toil: #root element not found');
10
- initNavigation();
11
- createRoot(el).render(_jsx(Router, { routes: routes, layout: layout, notFound: notFound }));
12
- startPrefetcher(routes);
13
- }
@@ -1,13 +0,0 @@
1
- export declare function settleNavigation(): void;
2
- export declare function isNavigationPending(): boolean;
3
- export declare function subscribePending(listener: () => void): () => void;
4
- export interface NavigateOptions {
5
- readonly replace?: boolean;
6
- readonly scroll?: boolean;
7
- }
8
- export declare function initNavigation(): void;
9
- export declare function navigate(href: string, options?: NavigateOptions): void;
10
- export declare function back(): void;
11
- export declare function forward(): void;
12
- export declare function refresh(): void;
13
- export declare function subscribeLocation(listener: () => void): () => void;
@@ -1,97 +0,0 @@
1
- import { enableManualScrollRestoration, planScroll, rememberScroll, } from './scroll.js';
2
- const listeners = new Set();
3
- let popstateBound = false;
4
- let keyCounter = 0;
5
- let currentKey = 'initial';
6
- function nextKey() {
7
- keyCounter += 1;
8
- return `t${String(keyCounter)}`;
9
- }
10
- function notify() {
11
- for (const listener of listeners)
12
- listener();
13
- }
14
- let startedTick = 0;
15
- let committedTick = 0;
16
- const pendingListeners = new Set();
17
- function emitPending() {
18
- for (const listener of pendingListeners)
19
- listener();
20
- }
21
- function beginNavigation() {
22
- startedTick += 1;
23
- emitPending();
24
- }
25
- export function settleNavigation() {
26
- if (committedTick !== startedTick) {
27
- committedTick = startedTick;
28
- emitPending();
29
- }
30
- }
31
- export function isNavigationPending() {
32
- return startedTick !== committedTick;
33
- }
34
- export function subscribePending(listener) {
35
- pendingListeners.add(listener);
36
- return () => {
37
- pendingListeners.delete(listener);
38
- };
39
- }
40
- export function initNavigation() {
41
- enableManualScrollRestoration();
42
- const state = window.history.state;
43
- if (state?.__toilKey) {
44
- currentKey = state.__toilKey;
45
- }
46
- else {
47
- currentKey = nextKey();
48
- window.history.replaceState({ ...state, __toilKey: currentKey }, '');
49
- }
50
- }
51
- export function navigate(href, options) {
52
- beginNavigation();
53
- rememberScroll(currentKey);
54
- let hash = '';
55
- try {
56
- hash = new URL(href, window.location.href).hash;
57
- }
58
- catch {
59
- hash = '';
60
- }
61
- if (options?.replace) {
62
- window.history.replaceState({ __toilKey: currentKey }, '', href);
63
- }
64
- else {
65
- currentKey = nextKey();
66
- window.history.pushState({ __toilKey: currentKey }, '', href);
67
- }
68
- planScroll({ hash, toTop: options?.scroll !== false });
69
- notify();
70
- }
71
- export function back() {
72
- window.history.back();
73
- }
74
- export function forward() {
75
- window.history.forward();
76
- }
77
- export function refresh() {
78
- notify();
79
- }
80
- function handlePopState(event) {
81
- beginNavigation();
82
- rememberScroll(currentKey);
83
- const state = event.state;
84
- currentKey = state?.__toilKey ?? 'initial';
85
- planScroll({ restoreKey: currentKey, hash: window.location.hash, toTop: false });
86
- notify();
87
- }
88
- export function subscribeLocation(listener) {
89
- if (!popstateBound) {
90
- window.addEventListener('popstate', handlePopState);
91
- popstateBound = true;
92
- }
93
- listeners.add(listener);
94
- return () => {
95
- listeners.delete(listener);
96
- };
97
- }
@@ -1,2 +0,0 @@
1
- import type { RouteParams } from './match.js';
2
- export declare const ParamsContext: import("react").Context<RouteParams>;
@@ -1,2 +0,0 @@
1
- import { createContext } from 'react';
2
- export const ParamsContext = createContext({});
@@ -1,11 +0,0 @@
1
- import type { RouteDef } from './types.js';
2
- declare global {
3
- interface Navigator {
4
- readonly connection?: {
5
- readonly saveData?: boolean;
6
- readonly effectiveType?: string;
7
- };
8
- }
9
- }
10
- export declare function prefetch(href: string): void;
11
- export declare function startPrefetcher(routes: RouteDef[]): void;
@@ -1,100 +0,0 @@
1
- import { matchRoute } from './match.js';
2
- let routeTable = [];
3
- const warmed = new WeakSet();
4
- let io = null;
5
- let mo = null;
6
- function routeForHref(href) {
7
- let url;
8
- try {
9
- url = new URL(href, window.location.href);
10
- }
11
- catch {
12
- return null;
13
- }
14
- if (url.origin !== window.location.origin)
15
- return null;
16
- for (const route of routeTable) {
17
- if (matchRoute(route.pattern, url.pathname))
18
- return route;
19
- }
20
- return null;
21
- }
22
- function warm(route) {
23
- if (warmed.has(route))
24
- return;
25
- warmed.add(route);
26
- void route.load().catch(() => {
27
- warmed.delete(route);
28
- });
29
- }
30
- export function prefetch(href) {
31
- const route = routeForHref(href);
32
- if (route)
33
- warm(route);
34
- }
35
- function isPrefetchable(a) {
36
- if (a.target && a.target !== '_self')
37
- return false;
38
- if (a.hasAttribute('download'))
39
- return false;
40
- if (a.dataset.noPrefetch !== undefined)
41
- return false;
42
- return true;
43
- }
44
- function observeAnchor(a) {
45
- if (!io || !isPrefetchable(a) || !routeForHref(a.href))
46
- return;
47
- io.observe(a);
48
- }
49
- function scan(root) {
50
- root.querySelectorAll('a[href]').forEach((el) => {
51
- if (el instanceof HTMLAnchorElement)
52
- observeAnchor(el);
53
- });
54
- }
55
- function shouldSkipForConnection() {
56
- const c = navigator.connection;
57
- if (!c)
58
- return false;
59
- return c.saveData === true || c.effectiveType === 'slow-2g' || c.effectiveType === '2g';
60
- }
61
- export function startPrefetcher(routes) {
62
- routeTable = routes;
63
- if (typeof window === 'undefined' ||
64
- typeof IntersectionObserver === 'undefined' ||
65
- typeof MutationObserver === 'undefined' ||
66
- io) {
67
- return;
68
- }
69
- if (shouldSkipForConnection())
70
- return;
71
- io = new IntersectionObserver((entries) => {
72
- for (const entry of entries) {
73
- if (!entry.isIntersecting)
74
- continue;
75
- const a = entry.target;
76
- if (a instanceof HTMLAnchorElement) {
77
- io?.unobserve(a);
78
- prefetch(a.href);
79
- }
80
- }
81
- }, { rootMargin: '200px' });
82
- mo = new MutationObserver((mutations) => {
83
- for (const m of mutations) {
84
- for (const node of m.addedNodes) {
85
- if (node instanceof HTMLAnchorElement)
86
- observeAnchor(node);
87
- else if (node instanceof Element)
88
- scan(node);
89
- }
90
- }
91
- });
92
- const begin = () => {
93
- scan(document);
94
- mo?.observe(document.body, { childList: true, subtree: true });
95
- };
96
- if (typeof requestIdleCallback === 'function')
97
- requestIdleCallback(begin);
98
- else
99
- setTimeout(begin, 200);
100
- }