kiru 0.49.2 → 0.50.0-preview.1

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 (143) hide show
  1. package/dist/components/errorBoundary.d.ts +7 -0
  2. package/dist/components/errorBoundary.d.ts.map +1 -0
  3. package/dist/components/errorBoundary.js +6 -0
  4. package/dist/components/errorBoundary.js.map +1 -0
  5. package/dist/components/index.d.ts +2 -0
  6. package/dist/components/index.d.ts.map +1 -1
  7. package/dist/components/index.js +2 -0
  8. package/dist/components/index.js.map +1 -1
  9. package/dist/components/lazy.d.ts.map +1 -1
  10. package/dist/components/lazy.js +11 -136
  11. package/dist/components/lazy.js.map +1 -1
  12. package/dist/components/suspense.d.ts +34 -0
  13. package/dist/components/suspense.d.ts.map +1 -0
  14. package/dist/components/suspense.js +110 -0
  15. package/dist/components/suspense.js.map +1 -0
  16. package/dist/constants.d.ts +4 -2
  17. package/dist/constants.d.ts.map +1 -1
  18. package/dist/constants.js +4 -2
  19. package/dist/constants.js.map +1 -1
  20. package/dist/hmr.d.ts +1 -0
  21. package/dist/hmr.d.ts.map +1 -1
  22. package/dist/hmr.js +7 -0
  23. package/dist/hmr.js.map +1 -1
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +1 -0
  27. package/dist/index.js.map +1 -1
  28. package/dist/recursiveRender.d.ts +6 -0
  29. package/dist/recursiveRender.d.ts.map +1 -0
  30. package/dist/recursiveRender.js +109 -0
  31. package/dist/recursiveRender.js.map +1 -0
  32. package/dist/renderToString.d.ts.map +1 -1
  33. package/dist/renderToString.js +10 -59
  34. package/dist/renderToString.js.map +1 -1
  35. package/dist/router/config.d.ts +3 -0
  36. package/dist/router/config.d.ts.map +1 -0
  37. package/dist/router/config.js +13 -0
  38. package/dist/router/config.js.map +1 -0
  39. package/dist/router/context.d.ts +15 -0
  40. package/dist/router/context.d.ts.map +1 -0
  41. package/dist/router/context.js +11 -0
  42. package/dist/router/context.js.map +1 -0
  43. package/dist/router/errors.d.ts +4 -0
  44. package/dist/router/errors.d.ts.map +1 -0
  45. package/dist/router/errors.js +7 -0
  46. package/dist/router/errors.js.map +1 -0
  47. package/dist/router/fileRouter.d.ts +48 -0
  48. package/dist/router/fileRouter.d.ts.map +1 -0
  49. package/dist/router/fileRouter.js +311 -0
  50. package/dist/router/fileRouter.js.map +1 -0
  51. package/dist/router/globals.d.ts +5 -0
  52. package/dist/router/globals.d.ts.map +1 -0
  53. package/dist/router/globals.js +4 -0
  54. package/dist/router/globals.js.map +1 -0
  55. package/dist/router/index.d.ts +7 -0
  56. package/dist/router/index.d.ts.map +1 -0
  57. package/dist/router/index.js +7 -0
  58. package/dist/router/index.js.map +1 -0
  59. package/dist/router/link.d.ts +8 -0
  60. package/dist/router/link.d.ts.map +1 -0
  61. package/dist/router/link.js +15 -0
  62. package/dist/router/link.js.map +1 -0
  63. package/dist/router/types.d.ts +63 -0
  64. package/dist/router/types.d.ts.map +1 -0
  65. package/dist/router/types.internal.d.ts +12 -0
  66. package/dist/router/types.internal.d.ts.map +1 -0
  67. package/dist/router/types.internal.js +2 -0
  68. package/dist/router/types.internal.js.map +1 -0
  69. package/dist/router/types.js +2 -0
  70. package/dist/router/types.js.map +1 -0
  71. package/dist/scheduler.d.ts.map +1 -1
  72. package/dist/scheduler.js +35 -10
  73. package/dist/scheduler.js.map +1 -1
  74. package/dist/ssr/server.d.ts +4 -1
  75. package/dist/ssr/server.d.ts.map +1 -1
  76. package/dist/ssr/server.js +50 -82
  77. package/dist/ssr/server.js.map +1 -1
  78. package/dist/types.d.ts +10 -2
  79. package/dist/types.d.ts.map +1 -1
  80. package/dist/types.utils.d.ts +6 -7
  81. package/dist/types.utils.d.ts.map +1 -1
  82. package/dist/utils/runtime.d.ts +1 -1
  83. package/dist/utils/runtime.d.ts.map +1 -1
  84. package/dist/utils/runtime.js.map +1 -1
  85. package/dist/utils/vdom.d.ts +3 -1
  86. package/dist/utils/vdom.d.ts.map +1 -1
  87. package/dist/utils/vdom.js +6 -5
  88. package/dist/utils/vdom.js.map +1 -1
  89. package/package.json +4 -8
  90. package/src/components/errorBoundary.ts +16 -0
  91. package/src/components/index.ts +2 -0
  92. package/src/components/lazy.ts +12 -169
  93. package/src/components/suspense.ts +191 -0
  94. package/src/constants.ts +6 -2
  95. package/src/hmr.ts +8 -0
  96. package/src/index.ts +1 -0
  97. package/src/recursiveRender.ts +127 -0
  98. package/src/renderToString.ts +10 -73
  99. package/src/router/config.ts +15 -0
  100. package/src/router/context.ts +23 -0
  101. package/src/router/errors.ts +6 -0
  102. package/src/router/fileRouter.ts +475 -0
  103. package/src/router/globals.ts +5 -0
  104. package/src/router/index.ts +6 -0
  105. package/src/router/link.ts +32 -0
  106. package/src/router/types.internal.ts +14 -0
  107. package/src/router/types.ts +81 -0
  108. package/src/scheduler.ts +45 -12
  109. package/src/ssr/server.ts +58 -95
  110. package/src/types.ts +11 -2
  111. package/src/types.utils.ts +6 -11
  112. package/src/utils/runtime.ts +1 -1
  113. package/src/utils/vdom.ts +11 -4
  114. package/dist/components/router/index.d.ts +0 -3
  115. package/dist/components/router/index.d.ts.map +0 -1
  116. package/dist/components/router/index.js +0 -3
  117. package/dist/components/router/index.js.map +0 -1
  118. package/dist/components/router/route.d.ts +0 -46
  119. package/dist/components/router/route.d.ts.map +0 -1
  120. package/dist/components/router/route.js +0 -8
  121. package/dist/components/router/route.js.map +0 -1
  122. package/dist/components/router/router.d.ts +0 -62
  123. package/dist/components/router/router.d.ts.map +0 -1
  124. package/dist/components/router/router.js +0 -181
  125. package/dist/components/router/router.js.map +0 -1
  126. package/dist/components/router/routerUtils.d.ts +0 -5
  127. package/dist/components/router/routerUtils.d.ts.map +0 -1
  128. package/dist/components/router/routerUtils.js +0 -39
  129. package/dist/components/router/routerUtils.js.map +0 -1
  130. package/dist/ssr/hydrationBoundary.d.ts +0 -27
  131. package/dist/ssr/hydrationBoundary.d.ts.map +0 -1
  132. package/dist/ssr/hydrationBoundary.js +0 -30
  133. package/dist/ssr/hydrationBoundary.js.map +0 -1
  134. package/dist/ssr/index.d.ts +0 -2
  135. package/dist/ssr/index.d.ts.map +0 -1
  136. package/dist/ssr/index.js +0 -2
  137. package/dist/ssr/index.js.map +0 -1
  138. package/src/components/router/index.ts +0 -2
  139. package/src/components/router/route.ts +0 -51
  140. package/src/components/router/router.ts +0 -280
  141. package/src/components/router/routerUtils.ts +0 -49
  142. package/src/ssr/hydrationBoundary.ts +0 -63
  143. package/src/ssr/index.ts +0 -1
@@ -1,39 +0,0 @@
1
- export { routeMatchesPath, parsePathParams, parseSearchParams };
2
- function routeMatchesPath(dynamicPathSegments, realPathSegments, fallthrough) {
3
- if (!fallthrough && dynamicPathSegments.length < realPathSegments.length) {
4
- return false;
5
- }
6
- for (let i = 0; i < dynamicPathSegments.length; i++) {
7
- const segment = dynamicPathSegments[i];
8
- if (segment.startsWith(":")) {
9
- continue;
10
- }
11
- else if (segment !== realPathSegments[i]) {
12
- return false;
13
- }
14
- }
15
- return true;
16
- }
17
- function parsePathParams(dynamicPathSegments, realPathSegments) {
18
- const params = {};
19
- for (let i = 0; i < dynamicPathSegments.length; i++) {
20
- const segment = dynamicPathSegments[i];
21
- if (segment.startsWith(":")) {
22
- params[segment.slice(1)] = realPathSegments[i];
23
- }
24
- }
25
- return params;
26
- }
27
- function parseSearchParams(search) {
28
- const parsed = {};
29
- const str = search.split("?")[1];
30
- if (!str || str === "")
31
- return parsed;
32
- const parts = str.split("&");
33
- for (let i = 0; i < parts.length; i++) {
34
- const [key, val] = parts[i].split("=");
35
- parsed[key] = val;
36
- }
37
- return parsed;
38
- }
39
- //# sourceMappingURL=routerUtils.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"routerUtils.js","sourceRoot":"","sources":["../../../src/components/router/routerUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EAAE,CAAA;AAE/D,SAAS,gBAAgB,CACvB,mBAA6B,EAC7B,gBAA0B,EAC1B,WAAqB;IAErB,IAAI,CAAC,WAAW,IAAI,mBAAmB,CAAC,MAAM,GAAG,gBAAgB,CAAC,MAAM,EAAE,CAAC;QACzE,OAAO,KAAK,CAAA;IACd,CAAC;IAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,mBAAmB,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpD,MAAM,OAAO,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAA;QACtC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,SAAQ;QACV,CAAC;aAAM,IAAI,OAAO,KAAK,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3C,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,eAAe,CACtB,mBAA6B,EAC7B,gBAA0B;IAE1B,MAAM,MAAM,GAA2B,EAAE,CAAA;IACzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,mBAAmB,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpD,MAAM,OAAO,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAA;QACtC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAA;QAChD,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAc;IACvC,MAAM,MAAM,GAA2B,EAAE,CAAA;IACzC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;IAChC,IAAI,CAAC,GAAG,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,MAAM,CAAA;IAErC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACtC,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,CAAA;IACnB,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -1,27 +0,0 @@
1
- type EventsArray = (keyof GlobalEventHandlersEventMap)[];
2
- export declare const HYDRATION_BOUNDARY_MARKER = "kiru:h-boundary";
3
- export declare const DEFAULT_INTERACTION_EVENTS: ["pointerdown", "keydown", "focus", "input"];
4
- export type HydrationBoundaryMode = "eager" | "interaction";
5
- export type HydrationBoundaryProps<T extends HydrationBoundaryMode> = {
6
- /**
7
- * Determines the strategy to use when hydrating the boundary.
8
- * - `eager`: hydrate immediately.
9
- * - `interaction`: hydrate upon the first user interaction.
10
- * @default "eager"
11
- */
12
- mode?: T;
13
- children: JSX.Children;
14
- } & (T extends "interaction" ? {
15
- /**
16
- * List of events that will trigger the hydration.
17
- * @default ["pointerdown", "keydown", "focus", "input"]
18
- */
19
- events?: EventsArray;
20
- } : {});
21
- export declare const HydrationBoundaryContext: Kiru.Context<{
22
- mode: HydrationBoundaryMode;
23
- events: string[];
24
- }>;
25
- export declare function Experimental_HydrationBoundary<T extends HydrationBoundaryMode>(props: HydrationBoundaryProps<T>): Kiru.VNode;
26
- export {};
27
- //# sourceMappingURL=hydrationBoundary.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hydrationBoundary.d.ts","sourceRoot":"","sources":["../../src/ssr/hydrationBoundary.ts"],"names":[],"mappings":"AAKA,KAAK,WAAW,GAAG,CAAC,MAAM,2BAA2B,CAAC,EAAE,CAAA;AAExD,eAAO,MAAM,yBAAyB,oBAAoB,CAAA;AAC1D,eAAO,MAAM,0BAA0B,8CAKP,CAAA;AAEhC,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,aAAa,CAAA;AAC3D,MAAM,MAAM,sBAAsB,CAAC,CAAC,SAAS,qBAAqB,IAAI;IACpE;;;;;OAKG;IACH,IAAI,CAAC,EAAE,CAAC,CAAA;IACR,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAA;CACvB,GAAG,CAAC,CAAC,SAAS,aAAa,GACxB;IACE;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,GACD,EAAE,CAAC,CAAA;AAEP,eAAO,MAAM,wBAAwB;UAC7B,qBAAqB;YACnB,MAAM,EAAE;EACT,CAAA;AAET,wBAAgB,8BAA8B,CAAC,CAAC,SAAS,qBAAqB,EAC5E,KAAK,EAAE,sBAAsB,CAAC,CAAC,CAAC,cAqBjC"}
@@ -1,30 +0,0 @@
1
- import { createContext } from "../context.js";
2
- import { $HYDRATION_BOUNDARY } from "../constants.js";
3
- import { createElement, Fragment } from "../element.js";
4
- import { renderMode } from "../globals.js";
5
- export const HYDRATION_BOUNDARY_MARKER = "kiru:h-boundary";
6
- export const DEFAULT_INTERACTION_EVENTS = [
7
- "pointerdown",
8
- "keydown",
9
- "focus",
10
- "input",
11
- ];
12
- export const HydrationBoundaryContext = createContext(null);
13
- export function Experimental_HydrationBoundary(props) {
14
- const provider = createElement(HydrationBoundaryContext.Provider, {
15
- value: {
16
- mode: props.mode || "eager",
17
- // @ts-expect-error this is fine
18
- events: props.events ?? DEFAULT_INTERACTION_EVENTS,
19
- },
20
- }, createElement($HYDRATION_BOUNDARY, props));
21
- if (renderMode.current === "string" || renderMode.current === "stream") {
22
- /**
23
- * in order to ensure consistent tree structure, we're simulating
24
- * the generated loader + wrapper components here.
25
- */
26
- return Fragment({ children: Fragment({ children: provider }) });
27
- }
28
- return provider;
29
- }
30
- //# sourceMappingURL=hydrationBoundary.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hydrationBoundary.js","sourceRoot":"","sources":["../../src/ssr/hydrationBoundary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAI1C,MAAM,CAAC,MAAM,yBAAyB,GAAG,iBAAiB,CAAA;AAC1D,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,aAAa;IACb,SAAS;IACT,OAAO;IACP,OAAO;CACuB,CAAA;AAsBhC,MAAM,CAAC,MAAM,wBAAwB,GAAG,aAAa,CAGlD,IAAK,CAAC,CAAA;AAET,MAAM,UAAU,8BAA8B,CAC5C,KAAgC;IAEhC,MAAM,QAAQ,GAAG,aAAa,CAC5B,wBAAwB,CAAC,QAAQ,EACjC;QACE,KAAK,EAAE;YACL,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,OAAO;YAC3B,gCAAgC;YAChC,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,0BAA0B;SACnD;KACF,EACD,aAAa,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAC1C,CAAA;IACD,IAAI,UAAU,CAAC,OAAO,KAAK,QAAQ,IAAI,UAAU,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACvE;;;WAGG;QACH,OAAO,QAAQ,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAA;IACjE,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC"}
@@ -1,2 +0,0 @@
1
- export * from "./hydrationBoundary.js";
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ssr/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAA"}
package/dist/ssr/index.js DELETED
@@ -1,2 +0,0 @@
1
- export * from "./hydrationBoundary.js";
2
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ssr/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAA"}
@@ -1,2 +0,0 @@
1
- export { Router, useRouter, navigate, Link, type LinkProps } from "./router.js"
2
- export { Route } from "./route.js"
@@ -1,51 +0,0 @@
1
- import { isVNode } from "../../utils/index.js"
2
-
3
- interface RouteProps {
4
- /**
5
- * The path to match.
6
- * @example
7
- * ```tsx
8
- * <Router>
9
- * <Route path="/" element={<h1>Home</h1>} />
10
- * <Route path="/:id" element={<UserProfile />} />
11
- * </Router>
12
- * //
13
- * const UserProfile = () => {
14
- * const router = useRouter()
15
- * const { id } = router.params
16
- * return <h1>{id}</h1>
17
- * }
18
- * ```
19
- */
20
- path: string
21
- /**
22
- * Allow url with additional segments being matched. Useful with nested routers.
23
- * @example
24
- * ```tsx
25
- * <Route path="/profile" fallthrough element={<UserProfile />} />
26
- * //
27
- * const UserProfile = () => {
28
- * return (
29
- * <Router>
30
- * <Route path="/" element={<UserDetails />} />
31
- * <Route path="/update" element={<UserUpdateForm />} />
32
- * </Router>
33
- * )
34
- * }
35
- * ```
36
- */
37
- fallthrough?: boolean
38
- /**
39
- * The element to render.
40
- */
41
- element: JSX.Element
42
- }
43
- export function Route({ element }: RouteProps) {
44
- return element
45
- }
46
-
47
- export function isRoute(
48
- thing: unknown
49
- ): thing is Kiru.VNode & { props: RouteProps } {
50
- return isVNode(thing) && thing.type === Route
51
- }
@@ -1,280 +0,0 @@
1
- import { createElement } from "../../element.js"
2
- import {
3
- useState,
4
- useMemo,
5
- useContext,
6
- useLayoutEffect,
7
- useRef,
8
- } from "../../hooks/index.js"
9
- import { __DEV__ } from "../../env.js"
10
- import { createContext } from "../../context.js"
11
- import { isRoute, Route } from "./route.js"
12
- import { noop } from "../../utils/index.js"
13
- import { node } from "../../globals.js"
14
- import type { ElementProps } from "../../types.js"
15
- import { flushSync, nextIdle } from "../../scheduler.js"
16
- import {
17
- parsePathParams,
18
- parseSearchParams,
19
- routeMatchesPath,
20
- } from "./routerUtils.js"
21
-
22
- export interface LinkProps extends Omit<ElementProps<"a">, "href"> {
23
- /**
24
- * The relative path to navigate to. If `inherit` is true,
25
- * the path will be relative to the parent <Route> component.
26
- */
27
- to: string
28
- /**
29
- * Event handler called when the link is clicked.
30
- * If you call `e.preventDefault()`, the navigation will not happen.
31
- */
32
- onclick?: (e: Event) => void
33
- /**
34
- * Specifies whether to replace the current history entry
35
- * instead of adding a new one.
36
- */
37
- replace?: boolean
38
- /**
39
- * If true, the path used for `to` will be relative to the parent <Route> component.
40
- * @default false
41
- */
42
- inherit?: boolean
43
- }
44
- export function Link({ to, onclick, replace, inherit, ...props }: LinkProps) {
45
- const router = useContext(RouterContext, false)
46
-
47
- const href = useMemo(() => {
48
- if (!inherit || router.isDefault) return to
49
- const parentPath = Object.entries(router.params).reduce(
50
- (acc, [k, v]) => acc.replace(`:${k}`, v),
51
- router.routePath
52
- )
53
- if (to === "/") return parentPath
54
- return (parentPath + to).replaceAll(/\/+/g, "/")
55
- }, [router.params, to, inherit])
56
-
57
- return createElement("a", {
58
- ...props,
59
- href,
60
- onclick: (e: Event) => {
61
- onclick?.(e)
62
- if (e.defaultPrevented) return
63
- e.preventDefault()
64
- navigate(href, { replace })
65
- },
66
- })
67
- }
68
-
69
- type RouterCtx = {
70
- transitionsEnabled: boolean
71
- viewTransition: Kiru.RefObject<ViewTransition>
72
- queueSyncNav: (callback: () => void) => void
73
- params: Record<string, string>
74
- query: Record<string, string>
75
- routePath: string
76
- basePath?: string
77
- isDefault: boolean
78
- }
79
- const RouterContext = createContext<RouterCtx>({
80
- transitionsEnabled: false,
81
- viewTransition: { current: null },
82
- queueSyncNav: noop,
83
- params: {},
84
- query: {},
85
- routePath: "/",
86
- isDefault: true,
87
- })
88
- RouterContext.displayName = "Router"
89
-
90
- function setQuery(query: Record<string, string>) {
91
- const url = new URL(window.location.href)
92
- Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v))
93
- window.history.pushState({}, "", url.toString())
94
- window.dispatchEvent(new PopStateEvent("popstate", { state: {} }))
95
- }
96
-
97
- /**
98
- * Gets state and methods provided by a parent <Router>.
99
- *
100
- * @see https://kirujs.dev/docs/api/routing
101
- */
102
- export function useRouter() {
103
- const { viewTransition, params, query } = useContext(RouterContext)
104
- return { viewTransition, params, query, setQuery }
105
- }
106
-
107
- export function navigate(to: string, options?: { replace?: boolean }) {
108
- const doNav = () => {
109
- window.history[options?.replace ? "replaceState" : "pushState"]({}, "", to)
110
- window.dispatchEvent(new PopStateEvent("popstate", { state: {} }))
111
- }
112
- // not called during render, just do the navigation
113
- if (!node.current) return doNav(), null
114
-
115
- const routerCtx = useContext(RouterContext, false)
116
- if (routerCtx.isDefault) {
117
- /**
118
- * called from a non-router-decendant - postpone
119
- * until next tick to avoid race conditions
120
- */
121
- return nextIdle(doNav), null
122
- }
123
- /**
124
- * set the value of our router's syncNavCallback,
125
- * causing it to be executed synchronously
126
- * during the router's useLayoutEffect.
127
- * consecutive calls to navigate will overwrite
128
- * the previous value.
129
- */
130
- return routerCtx.queueSyncNav(doNav), null
131
- }
132
-
133
- export interface RouterProps {
134
- /**
135
- * Base path for all routes in this router. Use this
136
- * to add a prefix to all routes
137
- */
138
- basePath?: string
139
- /**
140
- * Enable ViewTransition API for navigations
141
- */
142
- transition?: boolean
143
- /**
144
- * Children to render - the only supported children are <Route> components
145
- */
146
- children?: JSX.Children
147
- }
148
- const initLoc = () => ({
149
- pathname: window.location.pathname,
150
- search: window.location.search,
151
- })
152
-
153
- /**
154
- * Main router component.
155
- *
156
- * @see https://kirujs.dev/docs/api/routing
157
- */
158
- export function Router(props: RouterProps) {
159
- const viewTransition = useRef<ViewTransition | null>(null)
160
- const syncNavCallback = useRef<(() => void) | null>(null)
161
- const parentRouterContext = useContext(RouterContext, false)
162
- const dynamicParentPath = parentRouterContext.isDefault
163
- ? null
164
- : parentRouterContext.routePath
165
- const dynamicParentPathSegments = useMemo(
166
- () => dynamicParentPath?.split("/").filter(Boolean) || [],
167
- [dynamicParentPath]
168
- )
169
-
170
- const [loc, setLoc] = useState(initLoc)
171
- const query = useMemo(() => parseSearchParams(loc.search), [loc.search])
172
- const realPathSegments = useMemo(
173
- () => loc.pathname.split("/").filter(Boolean),
174
- [loc.pathname]
175
- )
176
-
177
- useLayoutEffect(() => {
178
- const handler = () => {
179
- if (
180
- !document.startViewTransition ||
181
- !props.transition ||
182
- !parentRouterContext.isDefault
183
- ) {
184
- return setLoc({
185
- pathname: window.location.pathname,
186
- search: window.location.search,
187
- })
188
- }
189
-
190
- viewTransition.current = document.startViewTransition(() => {
191
- setLoc({
192
- pathname: window.location.pathname,
193
- search: window.location.search,
194
- })
195
- flushSync()
196
- })
197
- viewTransition.current.finished.then(() => {
198
- viewTransition.current = null
199
- })
200
- }
201
- window.addEventListener("popstate", handler)
202
- return () => window.removeEventListener("popstate", handler)
203
- }, [])
204
-
205
- useLayoutEffect(() => {
206
- if (syncNavCallback.current) {
207
- syncNavCallback.current()
208
- syncNavCallback.current = null
209
- }
210
- })
211
-
212
- type RouteComponent = Kiru.VNode & {
213
- props: Kiru.InferProps<typeof Route>
214
- }
215
- let fallbackRoute: RouteComponent | undefined
216
- let route: RouteComponent | undefined
217
- const _children = (
218
- Array.isArray(props.children) ? props.children : [props.children]
219
- ).flat()
220
-
221
- for (const child of _children) {
222
- if (!isRoute(child)) continue
223
-
224
- if (child.props.path === "*") {
225
- if (__DEV__) {
226
- if (fallbackRoute) {
227
- console.warn(
228
- "[kiru]: More than one fallback route defined. Only the last one will be used."
229
- )
230
- }
231
- }
232
- fallbackRoute = child
233
- continue
234
- }
235
- const dynamicChildPathSegments = ((props.basePath || "") + child.props.path)
236
- .split("/")
237
- .filter(Boolean)
238
- if (
239
- routeMatchesPath(
240
- dynamicParentPathSegments.concat(dynamicChildPathSegments),
241
- realPathSegments,
242
- child.props.fallthrough
243
- )
244
- ) {
245
- route = child
246
- break
247
- }
248
- }
249
-
250
- let parsedParams = {}
251
- if (route) {
252
- const dynamicChildPathSegments = ((props.basePath || "") + route.props.path)
253
- .split("/")
254
- .filter(Boolean)
255
- parsedParams = parsePathParams(
256
- dynamicParentPathSegments.concat(dynamicChildPathSegments),
257
- realPathSegments
258
- )
259
- }
260
- const params = { ...parentRouterContext.params, ...parsedParams }
261
-
262
- return RouterContext.Provider({
263
- value: {
264
- params,
265
- query,
266
- routePath:
267
- (dynamicParentPath || "") +
268
- (props.basePath || "") +
269
- (route?.props.path || ""),
270
- basePath: props.basePath,
271
- isDefault: false,
272
- queueSyncNav: (callback: () => void) => {
273
- syncNavCallback.current = callback
274
- },
275
- viewTransition: viewTransition,
276
- transitionsEnabled: !!props.transition,
277
- },
278
- children: route ?? fallbackRoute ?? null,
279
- })
280
- }
@@ -1,49 +0,0 @@
1
- export { routeMatchesPath, parsePathParams, parseSearchParams }
2
-
3
- function routeMatchesPath(
4
- dynamicPathSegments: string[],
5
- realPathSegments: string[],
6
- fallthrough?: boolean
7
- ) {
8
- if (!fallthrough && dynamicPathSegments.length < realPathSegments.length) {
9
- return false
10
- }
11
-
12
- for (let i = 0; i < dynamicPathSegments.length; i++) {
13
- const segment = dynamicPathSegments[i]
14
- if (segment.startsWith(":")) {
15
- continue
16
- } else if (segment !== realPathSegments[i]) {
17
- return false
18
- }
19
- }
20
-
21
- return true
22
- }
23
-
24
- function parsePathParams(
25
- dynamicPathSegments: string[],
26
- realPathSegments: string[]
27
- ) {
28
- const params: Record<string, string> = {}
29
- for (let i = 0; i < dynamicPathSegments.length; i++) {
30
- const segment = dynamicPathSegments[i]
31
- if (segment.startsWith(":")) {
32
- params[segment.slice(1)] = realPathSegments[i]
33
- }
34
- }
35
- return params
36
- }
37
-
38
- function parseSearchParams(search: string) {
39
- const parsed: Record<string, string> = {}
40
- const str = search.split("?")[1]
41
- if (!str || str === "") return parsed
42
-
43
- const parts = str.split("&")
44
- for (let i = 0; i < parts.length; i++) {
45
- const [key, val] = parts[i].split("=")
46
- parsed[key] = val
47
- }
48
- return parsed
49
- }
@@ -1,63 +0,0 @@
1
- import { createContext } from "../context.js"
2
- import { $HYDRATION_BOUNDARY } from "../constants.js"
3
- import { createElement, Fragment } from "../element.js"
4
- import { renderMode } from "../globals.js"
5
-
6
- type EventsArray = (keyof GlobalEventHandlersEventMap)[]
7
-
8
- export const HYDRATION_BOUNDARY_MARKER = "kiru:h-boundary"
9
- export const DEFAULT_INTERACTION_EVENTS = [
10
- "pointerdown",
11
- "keydown",
12
- "focus",
13
- "input",
14
- ] as const satisfies EventsArray
15
-
16
- export type HydrationBoundaryMode = "eager" | "interaction"
17
- export type HydrationBoundaryProps<T extends HydrationBoundaryMode> = {
18
- /**
19
- * Determines the strategy to use when hydrating the boundary.
20
- * - `eager`: hydrate immediately.
21
- * - `interaction`: hydrate upon the first user interaction.
22
- * @default "eager"
23
- */
24
- mode?: T
25
- children: JSX.Children
26
- } & (T extends "interaction"
27
- ? {
28
- /**
29
- * List of events that will trigger the hydration.
30
- * @default ["pointerdown", "keydown", "focus", "input"]
31
- */
32
- events?: EventsArray
33
- }
34
- : {})
35
-
36
- export const HydrationBoundaryContext = createContext<{
37
- mode: HydrationBoundaryMode
38
- events: string[]
39
- }>(null!)
40
-
41
- export function Experimental_HydrationBoundary<T extends HydrationBoundaryMode>(
42
- props: HydrationBoundaryProps<T>
43
- ) {
44
- const provider = createElement(
45
- HydrationBoundaryContext.Provider,
46
- {
47
- value: {
48
- mode: props.mode || "eager",
49
- // @ts-expect-error this is fine
50
- events: props.events ?? DEFAULT_INTERACTION_EVENTS,
51
- },
52
- },
53
- createElement($HYDRATION_BOUNDARY, props)
54
- )
55
- if (renderMode.current === "string" || renderMode.current === "stream") {
56
- /**
57
- * in order to ensure consistent tree structure, we're simulating
58
- * the generated loader + wrapper components here.
59
- */
60
- return Fragment({ children: Fragment({ children: provider }) })
61
- }
62
- return provider
63
- }
package/src/ssr/index.ts DELETED
@@ -1 +0,0 @@
1
- export * from "./hydrationBoundary.js"