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,89 +1,91 @@
1
- /**
2
- * toiljs client runtime, published as `toiljs/client`. Re-exports the router (mount/Router/Link),
3
- * navigation hooks, prefetching, and the route types consumed by the compiler-generated entry.
4
- * Zero imports needed in user route files beyond this package.
5
- *
6
- * Internals are split by concern: route `types`, history-based `navigation`, the params
7
- * `params-context` + `hooks`, `lazy` component resolution, the `Link`/`Router` components,
8
- * `mount`, `match` (pure matcher), `prefetch` (link prefetcher), and `channel` (WebSocket helper).
9
- */
10
-
11
- export { mount } from './routing/mount.js';
12
- export { Router } from './routing/Router.js';
13
- export { Link } from './navigation/Link.js';
14
- export type { LinkProps } from './navigation/Link.js';
15
- export { NavLink, matchActive } from './navigation/NavLink.js';
16
- export type { NavLinkProps, NavLinkState } from './navigation/NavLink.js';
17
- export {
18
- navigate,
19
- back,
20
- forward,
21
- refresh,
22
- setViewTransitions,
23
- setTransitions,
24
- href,
25
- } from './navigation/navigation.js';
26
- export type { NavigateOptions } from './navigation/navigation.js';
27
- export {
28
- useParams,
29
- useNavigate,
30
- useLocation,
31
- usePathname,
32
- useSearchParams,
33
- useRouter,
34
- useNavigationPending,
35
- } from './routing/hooks.js';
36
- export type { RouterInstance } from './routing/hooks.js';
37
- export { useLoaderData, revalidate, invalidateLoaderData } from './routing/loader.js';
38
- export type {
39
- LoaderArgs,
40
- LoaderFunction,
41
- LoaderData,
42
- Revalidate,
43
- StaticParams,
44
- GenerateStaticParams,
45
- } from './routing/loader.js';
46
- export { useAction } from './routing/action.js';
47
- export type {
48
- UseActionOptions,
49
- ActionState,
50
- ActionHandle,
51
- RevalidateTarget,
52
- } from './routing/action.js';
53
- export { prefetch } from './navigation/prefetch.js';
54
- export type {
55
- RouteDef,
56
- LayoutLoader,
57
- LayoutComponentLoader,
58
- NotFoundLoader,
59
- RouteErrorProps,
60
- Register,
61
- RoutePath,
62
- Href,
63
- } from './types.js';
64
- export { matchRoute } from './routing/match.js';
65
- export type { RouteParams } from './routing/match.js';
66
- export { connectChannel, useChannel, resolveChannelUrl } from './channel/channel.js';
67
- export type { Channel, ChannelOptions, ChannelHook, ChannelData } from './channel/channel.js';
68
- export { useHead, useTitle, Head, mergeHead } from './head/head.js';
69
- export type { HeadSpec, MetaTag, LinkTag, ResolvedHead } from './head/head.js';
70
- export { resolveMetadata, useMetadata, Metadata } from './head/metadata.js';
71
- export type { GenerateMetadata, GenerateMetadataArgs, OpenGraph } from './head/metadata.js';
72
- export { searchPages, registerPages, getPages, pagePath } from './search/search.js';
73
- export type {
74
- PageMeta,
75
- PageSearchResult,
76
- PageSearchOptions,
77
- SearchField,
78
- SearchHints,
79
- } from './search/search.js';
80
- export { usePageSearch } from './search/use-page-search.js';
81
- export type { PageSearch } from './search/use-page-search.js';
82
- export { Image } from './components/Image.js';
83
- export type { ImageProps } from './components/Image.js';
84
- export { Script } from './components/Script.js';
85
- export type { ScriptProps, ScriptStrategy } from './components/Script.js';
86
- export { Form } from './components/Form.js';
87
- export type { FormProps } from './components/Form.js';
88
- export { Slot } from './components/Slot.js';
89
- export type { SlotProps } from './components/Slot.js';
1
+ /**
2
+ * toiljs client runtime, published as `toiljs/client`. Re-exports the router (mount/Router/Link),
3
+ * navigation hooks, prefetching, and the route types consumed by the compiler-generated entry.
4
+ * Zero imports needed in user route files beyond this package.
5
+ *
6
+ * Internals are split by concern: route `types`, history-based `navigation`, the params
7
+ * `params-context` + `hooks`, `lazy` component resolution, the `Link`/`Router` components,
8
+ * `mount`, `match` (pure matcher), `prefetch` (link prefetcher), and `channel` (WebSocket helper).
9
+ */
10
+
11
+ export { mount } from './routing/mount.js';
12
+ export { Router } from './routing/Router.js';
13
+ export { Link } from './navigation/Link.js';
14
+ export type { LinkProps } from './navigation/Link.js';
15
+ export { NavLink, matchActive } from './navigation/NavLink.js';
16
+ export type { NavLinkProps, NavLinkState } from './navigation/NavLink.js';
17
+ export {
18
+ navigate,
19
+ back,
20
+ forward,
21
+ refresh,
22
+ setViewTransitions,
23
+ setTransitions,
24
+ href,
25
+ } from './navigation/navigation.js';
26
+ export type { NavigateOptions } from './navigation/navigation.js';
27
+ export {
28
+ useParams,
29
+ useNavigate,
30
+ useLocation,
31
+ usePathname,
32
+ useSearchParams,
33
+ useRouter,
34
+ useNavigationPending,
35
+ } from './routing/hooks.js';
36
+ export type { RouterInstance } from './routing/hooks.js';
37
+ export { useLoaderData, revalidate, invalidateLoaderData } from './routing/loader.js';
38
+ export type {
39
+ LoaderArgs,
40
+ LoaderFunction,
41
+ LoaderData,
42
+ Revalidate,
43
+ StaticParams,
44
+ GenerateStaticParams,
45
+ } from './routing/loader.js';
46
+ export { useAction } from './routing/action.js';
47
+ export type {
48
+ UseActionOptions,
49
+ ActionState,
50
+ ActionHandle,
51
+ RevalidateTarget,
52
+ } from './routing/action.js';
53
+ export { prefetch } from './navigation/prefetch.js';
54
+ export type {
55
+ RouteDef,
56
+ LayoutLoader,
57
+ LayoutComponentLoader,
58
+ NotFoundLoader,
59
+ RouteErrorProps,
60
+ Register,
61
+ RoutePath,
62
+ Href,
63
+ } from './types.js';
64
+ export { matchRoute } from './routing/match.js';
65
+ export type { RouteParams } from './routing/match.js';
66
+ export { connectChannel, useChannel, resolveChannelUrl } from './channel/channel.js';
67
+ export type { Channel, ChannelOptions, ChannelHook, ChannelData } from './channel/channel.js';
68
+ export { useHead, useTitle, Head, mergeHead } from './head/head.js';
69
+ export type { HeadSpec, MetaTag, LinkTag, ResolvedHead } from './head/head.js';
70
+ export { resolveMetadata, useMetadata, Metadata } from './head/metadata.js';
71
+ export type { GenerateMetadata, GenerateMetadataArgs, OpenGraph } from './head/metadata.js';
72
+ export { searchPages, registerPages, getPages, pagePath } from './search/search.js';
73
+ export type {
74
+ PageMeta,
75
+ PageSearchResult,
76
+ PageSearchOptions,
77
+ SearchField,
78
+ SearchHints,
79
+ } from './search/search.js';
80
+ export { usePageSearch } from './search/use-page-search.js';
81
+ export type { PageSearch } from './search/use-page-search.js';
82
+ export { Image } from './components/Image.js';
83
+ export type { ImageProps } from './components/Image.js';
84
+ export { Script } from './components/Script.js';
85
+ export type { ScriptProps, ScriptStrategy } from './components/Script.js';
86
+ export { Form } from './components/Form.js';
87
+ export type { FormProps } from './components/Form.js';
88
+ export { Slot } from './components/Slot.js';
89
+ export type { SlotProps } from './components/Slot.js';
90
+ export { Server } from './rpc.js';
91
+ export { parseError } from './errors.js';
@@ -1,86 +1,86 @@
1
- import type { CSSProperties, ReactNode } from 'react';
2
-
3
- import { useLocation } from '../routing/hooks.js';
4
- import { Link, type LinkProps } from './Link.js';
5
-
6
- /** State passed to `NavLink`'s function-form `className` / `style` / `children`. */
7
- export interface NavLinkState {
8
- readonly isActive: boolean;
9
- }
10
-
11
- /**
12
- * Props for {@link NavLink}: all {@link LinkProps}, but `className` / `style` / `children` may also
13
- * be functions of the active state.
14
- */
15
- export interface NavLinkProps extends Omit<LinkProps, 'className' | 'style' | 'children'> {
16
- className?: string | ((state: NavLinkState) => string | undefined);
17
- style?: CSSProperties | ((state: NavLinkState) => CSSProperties | undefined);
18
- children?: ReactNode | ((state: NavLinkState) => ReactNode);
19
- /** Match `href` exactly; without it, sub-paths are also active. Default `false`. */
20
- end?: boolean;
21
- /** Class added when active (used with a string `className`). Default `"active"`. */
22
- activeClassName?: string;
23
- }
24
-
25
- function normalizePath(p: string): string {
26
- return p.length > 1 ? p.replace(/\/+$/, '') : p;
27
- }
28
-
29
- /**
30
- * Whether a link to `linkPath` is active for `currentPath`. Exact when `end`; otherwise a parent
31
- * path is active for its sub-paths (and `/` is active everywhere, matching React Router).
32
- */
33
- export function matchActive(linkPath: string, currentPath: string, end: boolean): boolean {
34
- const link = normalizePath(linkPath);
35
- const current = normalizePath(currentPath);
36
- if (current === link) return true;
37
- if (end) return false;
38
- if (link === '/') return true;
39
- return current.startsWith(link + '/');
40
- }
41
-
42
- /**
43
- * A {@link Link} that knows whether it points at the current location. Applies an active class
44
- * (default `"active"`) and `aria-current="page"` when active; `className` / `style` / `children`
45
- * may be functions of `{ isActive }`. Inherits Link's full anchor API and prefetching.
46
- */
47
- export function NavLink(props: NavLinkProps): ReactNode {
48
- const {
49
- href,
50
- className,
51
- style,
52
- children,
53
- end = false,
54
- activeClassName = 'active',
55
- ...rest
56
- } = props;
57
- const pathname = useLocation();
58
-
59
- let linkPath = href;
60
- try {
61
- linkPath = new URL(href, window.location.href).pathname;
62
- } catch {
63
- linkPath = href;
64
- }
65
- const isActive = matchActive(linkPath, pathname, end);
66
- const state: NavLinkState = { isActive };
67
-
68
- const resolvedClassName =
69
- typeof className === 'function'
70
- ? className(state)
71
- : [className, isActive ? activeClassName : undefined].filter(Boolean).join(' ') ||
72
- undefined;
73
- const resolvedStyle = typeof style === 'function' ? style(state) : style;
74
- const resolvedChildren = typeof children === 'function' ? children(state) : children;
75
-
76
- return (
77
- <Link
78
- {...rest}
79
- href={href}
80
- className={resolvedClassName}
81
- style={resolvedStyle}
82
- aria-current={isActive ? 'page' : undefined}>
83
- {resolvedChildren}
84
- </Link>
85
- );
86
- }
1
+ import type { CSSProperties, ReactNode } from 'react';
2
+
3
+ import { useLocation } from '../routing/hooks.js';
4
+ import { Link, type LinkProps } from './Link.js';
5
+
6
+ /** State passed to `NavLink`'s function-form `className` / `style` / `children`. */
7
+ export interface NavLinkState {
8
+ readonly isActive: boolean;
9
+ }
10
+
11
+ /**
12
+ * Props for {@link NavLink}: all {@link LinkProps}, but `className` / `style` / `children` may also
13
+ * be functions of the active state.
14
+ */
15
+ export interface NavLinkProps extends Omit<LinkProps, 'className' | 'style' | 'children'> {
16
+ className?: string | ((state: NavLinkState) => string | undefined);
17
+ style?: CSSProperties | ((state: NavLinkState) => CSSProperties | undefined);
18
+ children?: ReactNode | ((state: NavLinkState) => ReactNode);
19
+ /** Match `href` exactly; without it, sub-paths are also active. Default `false`. */
20
+ end?: boolean;
21
+ /** Class added when active (used with a string `className`). Default `"active"`. */
22
+ activeClassName?: string;
23
+ }
24
+
25
+ function normalizePath(p: string): string {
26
+ return p.length > 1 ? p.replace(/\/+$/, '') : p;
27
+ }
28
+
29
+ /**
30
+ * Whether a link to `linkPath` is active for `currentPath`. Exact when `end`; otherwise a parent
31
+ * path is active for its sub-paths (and `/` is active everywhere, matching React Router).
32
+ */
33
+ export function matchActive(linkPath: string, currentPath: string, end: boolean): boolean {
34
+ const link = normalizePath(linkPath);
35
+ const current = normalizePath(currentPath);
36
+ if (current === link) return true;
37
+ if (end) return false;
38
+ if (link === '/') return true;
39
+ return current.startsWith(link + '/');
40
+ }
41
+
42
+ /**
43
+ * A {@link Link} that knows whether it points at the current location. Applies an active class
44
+ * (default `"active"`) and `aria-current="page"` when active; `className` / `style` / `children`
45
+ * may be functions of `{ isActive }`. Inherits Link's full anchor API and prefetching.
46
+ */
47
+ export function NavLink(props: NavLinkProps): ReactNode {
48
+ const {
49
+ href,
50
+ className,
51
+ style,
52
+ children,
53
+ end = false,
54
+ activeClassName = 'active',
55
+ ...rest
56
+ } = props;
57
+ const pathname = useLocation();
58
+
59
+ let linkPath = href;
60
+ try {
61
+ linkPath = new URL(href, window.location.href).pathname;
62
+ } catch {
63
+ linkPath = href;
64
+ }
65
+ const isActive = matchActive(linkPath, pathname, end);
66
+ const state: NavLinkState = { isActive };
67
+
68
+ const resolvedClassName =
69
+ typeof className === 'function'
70
+ ? className(state)
71
+ : [className, isActive ? activeClassName : undefined].filter(Boolean).join(' ') ||
72
+ undefined;
73
+ const resolvedStyle = typeof style === 'function' ? style(state) : style;
74
+ const resolvedChildren = typeof children === 'function' ? children(state) : children;
75
+
76
+ return (
77
+ <Link
78
+ {...rest}
79
+ href={href}
80
+ className={resolvedClassName}
81
+ style={resolvedStyle}
82
+ aria-current={isActive ? 'page' : undefined}>
83
+ {resolvedChildren}
84
+ </Link>
85
+ );
86
+ }