eddev 2.0.0-beta.12 → 2.0.0-beta.121

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 (224) hide show
  1. package/css/editor-styles.css +4 -0
  2. package/dist/app/entry/HydrationOverlay.d.ts +1 -0
  3. package/dist/app/entry/HydrationOverlay.js +2 -0
  4. package/dist/app/entry/MetaTags.d.ts +7 -0
  5. package/dist/app/entry/MetaTags.js +17 -0
  6. package/dist/app/entry/boot-admin.js +9 -3
  7. package/dist/app/entry/hydration-script.d.ts +1 -0
  8. package/dist/app/entry/hydration-script.js +18 -0
  9. package/dist/app/entry/spa-root.js +3 -5
  10. package/dist/app/entry/ssr-root-client.d.ts +3 -1
  11. package/dist/app/entry/ssr-root-client.js +18 -6
  12. package/dist/app/entry/ssr-root.d.ts +3 -4
  13. package/dist/app/entry/ssr-root.js +19 -19
  14. package/dist/app/lib/admin/index.d.ts +2 -2
  15. package/dist/app/lib/admin/index.js +2 -2
  16. package/dist/app/lib/admin/installFieldTypes.js +3 -1
  17. package/dist/app/lib/admin/runWidgets.js +1 -1
  18. package/dist/app/lib/blocks/ContentBlocks.d.ts +1 -1
  19. package/dist/app/lib/blocks/ContentBlocks.js +5 -5
  20. package/dist/app/lib/blocks/EditableText.d.ts +15 -2
  21. package/dist/app/lib/blocks/EditableText.js +10 -4
  22. package/dist/app/lib/blocks/InnerBlocks.d.ts +21 -7
  23. package/dist/app/lib/blocks/InnerBlocks.js +75 -28
  24. package/dist/app/lib/blocks/block-utils.d.ts +2 -2
  25. package/dist/app/lib/blocks/block-utils.js +2 -2
  26. package/dist/app/lib/blocks/defineBlock.d.ts +3 -0
  27. package/dist/app/lib/blocks/defineBlock.js +7 -0
  28. package/dist/app/lib/blocks/editor/EditorHighlights.d.ts +7 -0
  29. package/dist/app/lib/blocks/editor/EditorHighlights.js +164 -0
  30. package/dist/app/lib/blocks/editor/EditorSupport.js +17 -12
  31. package/dist/app/lib/blocks/editor/ErrorBoundaryEditor.d.ts +1 -1
  32. package/dist/app/lib/blocks/editor/block-templates.d.ts +6 -0
  33. package/dist/app/lib/blocks/editor/block-templates.js +64 -0
  34. package/dist/app/lib/blocks/editor/create-block.d.ts +9 -0
  35. package/dist/app/lib/blocks/editor/create-block.js +13 -0
  36. package/dist/app/lib/blocks/editor/editor-config.d.ts +70 -7
  37. package/dist/app/lib/blocks/editor/editor-config.js +29 -62
  38. package/dist/app/lib/blocks/editor/installGutenbergHooks.d.ts +3 -0
  39. package/dist/app/lib/blocks/editor/installGutenbergHooks.js +105 -16
  40. package/dist/app/lib/blocks/editor/root-blocks.d.ts +6 -0
  41. package/dist/app/lib/blocks/editor/root-blocks.js +30 -0
  42. package/dist/app/lib/blocks/editor/usePostEditor.d.ts +1 -1
  43. package/dist/app/lib/blocks/index.d.ts +10 -9
  44. package/dist/app/lib/blocks/index.js +10 -9
  45. package/dist/app/lib/blocks/inline-editing.d.ts +9 -1
  46. package/dist/app/lib/blocks/inline-editing.js +7 -5
  47. package/dist/app/lib/devtools/components/BreakpointIndicator.js +1 -1
  48. package/dist/app/lib/devtools/components/DevUI.js +4 -3
  49. package/dist/app/lib/devtools/components/GridIndicator.d.ts +1 -0
  50. package/dist/app/lib/devtools/components/GridIndicator.js +29 -0
  51. package/dist/app/lib/devtools/hooks/usePersistState.d.ts +1 -1
  52. package/dist/app/lib/devtools/hooks/usePersistState.js +11 -2
  53. package/dist/app/lib/devtools/hooks/useTailwind.d.ts +2305 -1261
  54. package/dist/app/lib/devtools/hooks/useTailwind.js +1 -1
  55. package/dist/app/lib/devtools/index.d.ts +1 -1
  56. package/dist/app/lib/devtools/index.js +1 -1
  57. package/dist/app/lib/devtools/loader.js +8 -7
  58. package/dist/app/lib/devtools/useQueryDebug.d.ts +7 -1
  59. package/dist/app/lib/devtools/useQueryDebug.js +5 -8
  60. package/dist/app/lib/dynamic/dynamic.d.ts +1 -1
  61. package/dist/app/lib/dynamic/dynamic.js +5 -1
  62. package/dist/app/lib/dynamic/index.d.ts +1 -1
  63. package/dist/app/lib/dynamic/index.js +1 -1
  64. package/dist/app/lib/hooks/index.d.ts +4 -5
  65. package/dist/app/lib/hooks/index.js +4 -5
  66. package/dist/app/lib/hooks/queryUtils.d.ts +37 -3
  67. package/dist/app/lib/hooks/queryUtils.js +66 -26
  68. package/dist/app/lib/hooks/useAppData.js +12 -1
  69. package/dist/app/lib/hooks/useRPC.d.ts +0 -4
  70. package/dist/app/lib/hooks/useRPC.js +1 -8
  71. package/dist/app/lib/internal/finalize-rpc.d.ts +17 -0
  72. package/dist/app/lib/internal/finalize-rpc.js +3 -0
  73. package/dist/app/lib/internal/index.d.ts +5 -4
  74. package/dist/app/lib/internal/index.js +5 -4
  75. package/dist/app/lib/internal/read-admin-manifest.d.ts +1 -1
  76. package/dist/app/lib/legacy-stitches/createStitches.d.ts +21 -21
  77. package/dist/app/lib/legacy-stitches/createStitches.js +1 -1
  78. package/dist/app/lib/legacy-stitches/index.d.ts +1 -1
  79. package/dist/app/lib/legacy-stitches/index.js +1 -1
  80. package/dist/app/lib/routing/components/BackButton.d.ts +49 -0
  81. package/dist/app/lib/routing/components/BackButton.js +47 -0
  82. package/dist/app/lib/routing/components/BrowserRouter.d.ts +5 -1
  83. package/dist/app/lib/routing/components/BrowserRouter.js +98 -19
  84. package/dist/app/lib/routing/components/ClientOnly.d.ts +1 -1
  85. package/dist/app/lib/routing/components/ClientOnly.js +2 -2
  86. package/dist/app/lib/routing/components/Link.d.ts +1 -0
  87. package/dist/app/lib/routing/components/Link.js +11 -12
  88. package/dist/app/lib/routing/components/RouteRenderer.d.ts +1 -1
  89. package/dist/app/lib/routing/components/RouteRenderer.js +7 -6
  90. package/dist/app/lib/routing/components/SSRRouter.d.ts +2 -2
  91. package/dist/app/lib/routing/components/SSRRouter.js +5 -6
  92. package/dist/app/lib/routing/components/ScrollRestoration.js +5 -2
  93. package/dist/app/lib/routing/context.d.ts +8 -5
  94. package/dist/app/lib/routing/context.js +13 -96
  95. package/dist/app/lib/routing/hooks/useRestorableState.d.ts +2 -1
  96. package/dist/app/lib/routing/hooks/useRestorableState.js +2 -1
  97. package/dist/app/lib/routing/hooks/useRoute.d.ts +16 -1
  98. package/dist/app/lib/routing/hooks/useRoute.js +22 -1
  99. package/dist/app/lib/routing/hooks/useRouteMeta.d.ts +5 -0
  100. package/dist/app/lib/routing/hooks/useRouteMeta.js +9 -0
  101. package/dist/app/lib/routing/hooks/useRouteTransition.d.ts +1 -1
  102. package/dist/app/lib/routing/hooks/useRouteTransition.js +1 -1
  103. package/dist/app/lib/routing/hooks/useRouter.d.ts +1 -1
  104. package/dist/app/lib/routing/hooks/useRouter.js +1 -1
  105. package/dist/app/lib/routing/hooks/useRouterEvents.d.ts +1 -1
  106. package/dist/app/lib/routing/hooks/useRouterEvents.js +1 -1
  107. package/dist/app/lib/routing/hooks/useRouterState.d.ts +1 -1
  108. package/dist/app/lib/routing/hooks/useRouterState.js +1 -1
  109. package/dist/app/lib/routing/hooks/useSearchParams.js +2 -2
  110. package/dist/app/lib/routing/index.d.ts +14 -13
  111. package/dist/app/lib/routing/index.js +14 -13
  112. package/dist/app/lib/routing/loader.d.ts +2 -2
  113. package/dist/app/lib/routing/loader.js +20 -11
  114. package/dist/app/lib/routing/types.d.ts +37 -10
  115. package/dist/app/lib/routing/utils.d.ts +5 -2
  116. package/dist/app/lib/routing/utils.js +37 -4
  117. package/dist/app/lib/{hooks → runtime}/apiConfig.d.ts +6 -2
  118. package/dist/app/lib/runtime/apiConfig.js +6 -0
  119. package/dist/app/lib/runtime/errorHandling.d.ts +39 -0
  120. package/dist/app/lib/runtime/errorHandling.js +6 -0
  121. package/dist/app/lib/runtime/index.d.ts +2 -0
  122. package/dist/app/lib/runtime/index.js +2 -0
  123. package/dist/app/lib/views/index.d.ts +1 -1
  124. package/dist/app/lib/views/index.js +1 -1
  125. package/dist/app/server/defineRouter.d.ts +2 -0
  126. package/dist/app/server/defineRouter.js +4 -0
  127. package/dist/app/server/index.d.ts +5 -3
  128. package/dist/app/server/index.js +5 -3
  129. package/dist/app/server/proxy-wp-admin.d.ts +1 -2
  130. package/dist/app/server/proxy-wp-admin.js +41 -14
  131. package/dist/app/server/render-ssr-page.d.ts +27 -2
  132. package/dist/app/server/render-ssr-page.js +192 -12
  133. package/dist/app/server/rpc.d.ts +56 -0
  134. package/dist/app/server/rpc.js +18 -0
  135. package/dist/app/server/server-context.d.ts +44 -4
  136. package/dist/app/server/server-context.js +305 -28
  137. package/dist/app/server/utils/replace-host.d.ts +1 -1
  138. package/dist/app/server/utils/replace-host.js +10 -2
  139. package/dist/app/server/utils/swr-cache.d.ts +4 -0
  140. package/dist/app/server/utils/swr-cache.js +31 -0
  141. package/dist/app/utils/APIProvider.d.ts +2 -0
  142. package/dist/app/utils/APIProvider.js +5 -0
  143. package/dist/app/utils/BlockErrorBoundary.d.ts +19 -0
  144. package/dist/app/utils/BlockErrorBoundary.js +38 -0
  145. package/dist/app/utils/ErrorMessage.d.ts +5 -0
  146. package/dist/app/utils/ErrorMessage.js +14 -0
  147. package/dist/app/utils/RouteErrorBoundary.d.ts +18 -0
  148. package/dist/app/utils/RouteErrorBoundary.js +38 -0
  149. package/dist/app/utils/asset-capture.d.ts +2 -0
  150. package/dist/app/utils/asset-capture.js +5 -0
  151. package/dist/app/utils/hydration-debugger.d.ts +13 -0
  152. package/dist/app/utils/hydration-debugger.js +11 -0
  153. package/dist/app/utils/query-client.d.ts +2 -0
  154. package/dist/app/utils/query-client.js +5 -1
  155. package/dist/app/utils/query-monitor.d.ts +26 -0
  156. package/dist/app/utils/query-monitor.js +7 -0
  157. package/dist/app/utils/trpc-client.d.ts +2 -0
  158. package/dist/app/utils/trpc-client.js +39 -0
  159. package/dist/node/cli/cli-worker.js +10 -5
  160. package/dist/node/cli/cli.js +81 -11
  161. package/dist/node/cli/display/CLIApp.js +3 -6
  162. package/dist/node/cli/display/boot-cli-app.js +1 -1
  163. package/dist/node/cli/display/tools/CreateBlock.d.ts +1 -1
  164. package/dist/node/cli/display/tools/cli-tools.d.ts +1 -11
  165. package/dist/node/cli/display/tools/cli-tools.js +9 -9
  166. package/dist/node/cli/version.d.ts +1 -1
  167. package/dist/node/cli/version.js +1 -1
  168. package/dist/node/compiler/build-vinxi.js +2 -1
  169. package/dist/node/compiler/bundler.admin.d.ts +1 -1
  170. package/dist/node/compiler/bundler.admin.js +1 -1
  171. package/dist/node/compiler/bundler.frontend.js +1 -1
  172. package/dist/node/compiler/dev-server.js +10 -0
  173. package/dist/node/compiler/get-vite-config.d.ts +7 -1
  174. package/dist/node/compiler/get-vite-config.js +96 -16
  175. package/dist/node/compiler/vinxi-app.d.ts +12 -0
  176. package/dist/node/compiler/vinxi-app.js +139 -32
  177. package/dist/node/compiler/vinxi-codegen.js +345 -112
  178. package/dist/node/graphql/graphql-codegen.d.ts +11 -1
  179. package/dist/node/graphql/graphql-codegen.js +210 -33
  180. package/dist/node/graphql/graphql-schema-loader.d.ts +2 -1
  181. package/dist/node/graphql/graphql-schema-loader.js +5 -16
  182. package/dist/node/graphql/plugins/gql-plugin-queries.js +1 -1
  183. package/dist/node/graphql/query-files-loader.d.ts +3 -0
  184. package/dist/node/graphql/query-files-loader.js +5 -0
  185. package/dist/node/project/config.d.ts +159 -73
  186. package/dist/node/project/config.js +69 -20
  187. package/dist/node/project/env.d.ts +4 -0
  188. package/dist/node/project/env.js +1 -0
  189. package/dist/node/project/manifest/block-manifest.js +2 -1
  190. package/dist/node/project/manifest/manifest.d.ts +1 -0
  191. package/dist/node/project/manifest/manifest.js +14 -10
  192. package/dist/node/project/manifest/routes-manifest.d.ts +20 -0
  193. package/dist/node/project/manifest/routes-manifest.js +74 -0
  194. package/dist/node/project/manifest/view-manifest.js +1 -1
  195. package/dist/node/project/project.d.ts +11 -1
  196. package/dist/node/project/project.js +61 -7
  197. package/dist/node/project/wp-info.d.ts +1 -0
  198. package/dist/node/project/wp-info.js +13 -1
  199. package/dist/node/storybook/index.d.ts +2 -0
  200. package/dist/node/storybook/index.js +13 -0
  201. package/dist/node/types/block-type.d.ts +25 -20
  202. package/dist/node/types/block-type.js +1 -0
  203. package/dist/node/types/view-type.d.ts +7 -7
  204. package/dist/node/utils/fetch-wp.d.ts +1 -0
  205. package/dist/node/utils/fetch-wp.js +27 -0
  206. package/dist/node/utils/fs-codegen.d.ts +2 -0
  207. package/dist/node/utils/fs-codegen.js +2 -1
  208. package/dist/node/utils/is-deploying.js +1 -1
  209. package/dist/node/utils/report-builder.d.ts +6 -6
  210. package/dist/node/utils/self-signed-cert.d.ts +2 -0
  211. package/dist/node/utils/self-signed-cert.js +10 -2
  212. package/dist/node/utils/stateful-log.js +2 -0
  213. package/dist/node/utils/watch-file-tree.d.ts +17 -3
  214. package/dist/node/utils/watch-file-tree.js +12 -5
  215. package/package.json +21 -12
  216. package/types.app.d.ts +4 -2
  217. package/types.app.internal.d.ts +2 -2
  218. package/types.meta.d.ts +105 -0
  219. package/types.node.d.ts +3 -3
  220. package/dist/app/lib/blocks/ErrorBoundaryFrontend.d.ts +0 -15
  221. package/dist/app/lib/blocks/ErrorBoundaryFrontend.js +0 -35
  222. package/dist/app/lib/hooks/apiConfig.js +0 -4
  223. package/dist/app/lib/hooks/usePageLoad.d.ts +0 -6
  224. package/dist/app/lib/hooks/usePageLoad.js +0 -5
@@ -0,0 +1,49 @@
1
+ import { ReactNode } from "react";
2
+ import { RouteState } from "../types";
3
+ type BackButtonProps = {
4
+ /**
5
+ * An optional matching function, which will stop the render button from appearing unless the function returns true.
6
+ *
7
+ * The below example will only render a back button if the last route was a project archive.
8
+ *
9
+ * eg. `filter={(route) => route.view === 'archive-projects'}`
10
+ *
11
+ * @param route The last route in the history stack.
12
+ */
13
+ filter?: (route: RouteState) => boolean;
14
+ /**
15
+ * Whether to use the top-most "global" route, or the "context" route.
16
+ *
17
+ * The "global" route is the top-most route, reflected in the URL bar.
18
+ *
19
+ * The "context" route will be the route that is currently being rendered, which may be different to the global route when using modals/overlays/route stacks/transitions.
20
+ *
21
+ * @default "context"
22
+ */
23
+ mode?: "global" | "context";
24
+ /**
25
+ * An optional href, which will be used if no back route is found.
26
+ *
27
+ * When used, the back button will always render.
28
+ */
29
+ fallbackHref?: string;
30
+ /**
31
+ * A function to render the back button, if one should be rendered.
32
+ *
33
+ * Receives a `route` prop to further inspect the route, and an `onClick` function to navigate back.
34
+ *
35
+ * @param props
36
+ * @returns
37
+ */
38
+ render: (props: {
39
+ route: RouteState;
40
+ onClick: (e?: any) => void;
41
+ }) => ReactNode;
42
+ };
43
+ /**
44
+ * Display a back button that will navigate to the previous page in the router's history.
45
+ *
46
+ * This will allow you to render a back button on the condition that the back button will not send the user to a different website.
47
+ */
48
+ export declare function BackButton(props: BackButtonProps): ReactNode;
49
+ export {};
@@ -0,0 +1,47 @@
1
+ import { useMemo } from "react";
2
+ import { useRoute } from "../hooks/useRoute";
3
+ import { useRouter } from "../hooks/useRouter";
4
+ import { useRouterState } from "../hooks/useRouterState";
5
+ import { getLinkHandlerMode } from "../utils";
6
+ /**
7
+ * Display a back button that will navigate to the previous page in the router's history.
8
+ *
9
+ * This will allow you to render a back button on the condition that the back button will not send the user to a different website.
10
+ */
11
+ export function BackButton(props) {
12
+ const router = useRouter();
13
+ const prevRoute = useRouterState((state) => state.history[state.history.length - 2]);
14
+ const contextRoute = useRoute();
15
+ const activeRoute = useRouterState((state) => state.activeRoute);
16
+ const route = props.mode === "global" ? activeRoute : contextRoute;
17
+ const backAction = useMemo(() => {
18
+ if (prevRoute && (!props.filter || props.filter(prevRoute))) {
19
+ return (e) => {
20
+ const { mode } = getLinkHandlerMode(e, prevRoute.uri);
21
+ if (mode === "navigate") {
22
+ e.preventDefault();
23
+ history.back();
24
+ // history.length
25
+ }
26
+ else if (mode === "ignore") {
27
+ e.preventDefault();
28
+ }
29
+ };
30
+ }
31
+ else if (props.fallbackHref) {
32
+ return (e) => {
33
+ const { mode } = getLinkHandlerMode(e, props.fallbackHref);
34
+ if (mode === "navigate") {
35
+ e.preventDefault();
36
+ router.navigate(props.fallbackHref);
37
+ }
38
+ else if (mode === "ignore") {
39
+ e.preventDefault();
40
+ }
41
+ };
42
+ }
43
+ }, [route, props.filter, props.fallbackHref]);
44
+ if (backAction) {
45
+ return props.render ? props.render({ route: prevRoute, onClick: backAction }) : null;
46
+ }
47
+ }
@@ -1,4 +1,8 @@
1
1
  import { PropsWithChildren } from "react";
2
- type Props = PropsWithChildren<{}>;
2
+ import { type RouterAPI } from "../types.js";
3
+ type Props = PropsWithChildren<{
4
+ routerRef?: React.MutableRefObject<RouterAPI | null>;
5
+ onReady?: () => void;
6
+ }>;
3
7
  export declare function BrowserRouter(props: Props): import("react/jsx-runtime").JSX.Element;
4
8
  export {};
@@ -1,11 +1,20 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useRef, useState, useTransition } from "react";
3
- import { isRelative, parseURL, resolveURL } from "ufo";
4
- import { RouterContext, RouterStateContext } from "../context";
5
- import { RouteLoader } from "../loader";
6
- import { getLinkHandlerMode, normalizeRoute, parseQuery, stringifyRouteLink } from "../utils";
7
- import { AppRenderer } from "./RouteRenderer";
3
+ import { isEqual, isRelative, parseURL, resolveURL } from "ufo";
4
+ import { clientMetaTags, RouterContext, RouterStateContext } from "../context.js";
5
+ import { $routeMetaStore } from "../hooks/useRouteMeta.js";
6
+ import { RouteLoader } from "../loader.js";
7
+ import { getLinkHandlerMode, getRouteMeta, isSamePathname, normalizeRoute, parseQuery, stringifyRouteLink, } from "../utils.js";
8
+ import { AppRenderer } from "./RouteRenderer.js";
9
+ // Create a global RouteLoader instance
8
10
  const loader = new RouteLoader();
11
+ // Create a unique ID for each history entry
12
+ let historyStamp = String(new Date().getTime());
13
+ let historyIndex = 0;
14
+ function getHistoryId() {
15
+ return historyStamp + historyIndex++;
16
+ }
17
+ // Set up the initial route data
9
18
  let initialRoute;
10
19
  let initialRouteHydrated = false;
11
20
  if (env.client && !env.admin) {
@@ -23,7 +32,10 @@ if (env.client && !env.admin) {
23
32
  search: "",
24
33
  query: {},
25
34
  view: initialData.view,
35
+ meta: getRouteMeta(initialData),
26
36
  });
37
+ history.replaceState(historyStateForRoute(initialRoute), "", document.location.href);
38
+ $routeMetaStore.data = getRouteMeta(initialData);
27
39
  }
28
40
  function parseHrefPath(url) {
29
41
  if (isRelative(url)) {
@@ -54,14 +66,9 @@ function historyStateForRoute(route) {
54
66
  state: route.returnState,
55
67
  };
56
68
  }
57
- let index = 0;
58
- function getHistoryId() {
59
- return String(index++);
60
- }
61
69
  let lastRouterState = null;
62
70
  export function BrowserRouter(props) {
63
71
  const pendingRoute = useRef(null);
64
- // const [activeRoute, setActiveRoute] = useState<RouteState>(initialRoute)
65
72
  const [transitioning, startTransition] = useTransition();
66
73
  const [routerState, setRouterState] = useState(() => ({
67
74
  activeRoute: initialRoute,
@@ -79,6 +86,16 @@ export function BrowserRouter(props) {
79
86
  ...state,
80
87
  ...update,
81
88
  };
89
+ if (state?.activeRoute?.meta?.tags) {
90
+ if (env.serverless) {
91
+ clientMetaTags.setMetaTags(state.activeRoute.meta.tags ?? []);
92
+ }
93
+ else {
94
+ const title = state.activeRoute.meta.tags.find((tag) => tag.tagName === "title")?.inner;
95
+ if (title)
96
+ document.title = title;
97
+ }
98
+ }
82
99
  setRouterState(state);
83
100
  };
84
101
  const internals = {
@@ -181,11 +198,20 @@ export function BrowserRouter(props) {
181
198
  });
182
199
  if (cancelled)
183
200
  return;
184
- const data = await loader.loadRouteData(parseHrefPath(args.url));
185
- if (!data.view) {
186
- console.error("No `view` property in route data");
187
- return;
188
- }
201
+ const data = await loader.loadRouteData(parseHrefPath(args.url)).catch((err) => {
202
+ emitEvent({ type: "error", url: args.url, error: err, critical: true });
203
+ return {
204
+ view: "_error",
205
+ viewType: "react",
206
+ appData: loader.appData,
207
+ viewData: {
208
+ data: {
209
+ code: 500,
210
+ error: err,
211
+ },
212
+ },
213
+ };
214
+ });
189
215
  const lazyComponent = loader.getRouteComponent(data.view);
190
216
  if (!lazyComponent) {
191
217
  console.error(`No component found for view: ${data.view}`);
@@ -196,11 +222,12 @@ export function BrowserRouter(props) {
196
222
  hash: link.hash,
197
223
  search: "",
198
224
  query: link.query,
199
- pathname: link.pathname,
225
+ pathname: data.canonical ?? link.pathname,
200
226
  view: data.view,
201
227
  props: data.viewData?.data ?? {},
202
228
  component: lazyComponent,
203
229
  returnState: args.restoreState ?? {},
230
+ meta: getRouteMeta(data),
204
231
  });
205
232
  setState({
206
233
  pendingRoute: route,
@@ -229,6 +256,7 @@ export function BrowserRouter(props) {
229
256
  lastRoute: currentRoute,
230
257
  link,
231
258
  });
259
+ $routeMetaStore.data = route.meta;
232
260
  };
233
261
  startTransition(() => {
234
262
  pendingRoute.current = route;
@@ -247,6 +275,9 @@ export function BrowserRouter(props) {
247
275
  goingBack: false,
248
276
  });
249
277
  },
278
+ getState() {
279
+ return state;
280
+ },
250
281
  replaceHash(hash) {
251
282
  replaceRoute({
252
283
  ...getActiveRoute(),
@@ -257,6 +288,7 @@ export function BrowserRouter(props) {
257
288
  replaceRoute({
258
289
  ...getActiveRoute(),
259
290
  query,
291
+ returnState: captureState(),
260
292
  });
261
293
  },
262
294
  async prefetch(url) {
@@ -265,7 +297,7 @@ export function BrowserRouter(props) {
265
297
  if (loader.hasRouteData(link.pathname))
266
298
  return;
267
299
  emitEvent({ type: "preload:start", currentRoute: getActiveRoute(), link });
268
- const data = await loader.loadRouteData(link.pathname).then((data) => {
300
+ await loader.loadRouteData(link.pathname).then((data) => {
269
301
  emitEvent({ type: "preload:data-ready", currentRoute: getActiveRoute(), link, data });
270
302
  });
271
303
  }
@@ -297,8 +329,8 @@ export function BrowserRouter(props) {
297
329
  subscribers.delete(fn);
298
330
  };
299
331
  },
300
- handleClickEvent(e, originalHref) {
301
- const { mode, href } = getLinkHandlerMode(e, originalHref);
332
+ handleClickEvent(e, originalHref, preferBack) {
333
+ const { mode, href } = getLinkHandlerMode(e, originalHref, state.activeRoute);
302
334
  if (mode === "ignore") {
303
335
  e.preventDefault();
304
336
  return;
@@ -308,10 +340,53 @@ export function BrowserRouter(props) {
308
340
  }
309
341
  else if (mode === "navigate" && href) {
310
342
  e.preventDefault();
343
+ if (preferBack) {
344
+ const lastState = state.history.length > 1 && state.history[state.history.length - 2];
345
+ if (lastState) {
346
+ const doesMatch = preferBack === "exact" ? isEqual(href, lastState.uri) : isSamePathname(href, lastState.uri);
347
+ if (doesMatch) {
348
+ history.back();
349
+ return;
350
+ }
351
+ }
352
+ }
311
353
  api.navigate(href);
312
354
  }
313
355
  },
314
356
  emitEvent,
357
+ restoreRoute(route) {
358
+ const stack = state.history;
359
+ // Is the route in our history stack? (going back)
360
+ const index = stack.findIndex((item) => item.id === route.id);
361
+ if (index >= 0) {
362
+ return doRouteTransition({
363
+ url: route.uri,
364
+ route,
365
+ goingBack: true,
366
+ history: stack.slice(0, index),
367
+ restoreState: route.returnState,
368
+ shouldPush: false,
369
+ });
370
+ }
371
+ // Is the route in our history _cache_? (probably going forward)
372
+ if (historyCache.has(route.id)) {
373
+ return doRouteTransition({
374
+ url: route.uri,
375
+ route,
376
+ goingBack: false,
377
+ history: stack,
378
+ restoreState: route.returnState,
379
+ shouldPush: false,
380
+ });
381
+ }
382
+ return doRouteTransition({
383
+ url: route.uri,
384
+ goingBack: false,
385
+ history: stack,
386
+ restoreState: route.returnState,
387
+ shouldPush: false,
388
+ });
389
+ },
315
390
  };
316
391
  return {
317
392
  api,
@@ -340,6 +415,7 @@ export function BrowserRouter(props) {
340
415
  // Handle popState
341
416
  // const router = useRouterStore.getState()
342
417
  const onPopState = (e) => {
418
+ // console.log("Popped", e.state)
343
419
  internals.poppedState?.(document.location.href, e.state ?? {});
344
420
  };
345
421
  window.addEventListener("popstate", onPopState);
@@ -347,5 +423,8 @@ export function BrowserRouter(props) {
347
423
  window.removeEventListener("popstate", onPopState);
348
424
  };
349
425
  }, []);
426
+ useEffect(() => {
427
+ props.onReady?.();
428
+ }, []);
350
429
  return (_jsx(RouterContext.Provider, { value: api, children: _jsx(RouterStateContext.Provider, { value: routerState, children: _jsx(AppRenderer, {}) }) }));
351
430
  }
@@ -8,5 +8,5 @@ type Props = PropsWithChildren<{
8
8
  *
9
9
  * The optional 'fallback' prop can be used to show a loading state or placeholder content while the client bundle is loading.
10
10
  */
11
- export declare function ClientOnly(props: Props): string | number | boolean | import("react/jsx-runtime").JSX.Element | Iterable<import("react").ReactNode> | null;
11
+ export declare function ClientOnly(props: Props): string | number | boolean | Iterable<import("react").ReactNode> | import("react/jsx-runtime").JSX.Element | null;
12
12
  export {};
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Fragment } from "react";
3
- import { useIsSSR } from "../hooks/useIsSSR";
3
+ import { useIsSSR } from "../hooks/useIsSSR.js";
4
4
  /**
5
5
  * Renders children, but only on the client.
6
6
  *
@@ -8,7 +8,7 @@ import { useIsSSR } from "../hooks/useIsSSR";
8
8
  */
9
9
  export function ClientOnly(props) {
10
10
  const mounted = useIsSSR();
11
- if (!mounted)
11
+ if (mounted)
12
12
  return props.fallback ?? null;
13
13
  return _jsx(Fragment, { children: props.children });
14
14
  }
@@ -3,6 +3,7 @@ type Props<T extends ElementType = "a"> = NoInfer<Omit<ComponentPropsWithRef<T>,
3
3
  href?: string | null;
4
4
  target?: string | null;
5
5
  as?: T;
6
+ preferBack?: boolean | "exact";
6
7
  };
7
8
  export declare const Link: <T extends ElementType = "a">(props: Props<T>) => ReactElement;
8
9
  /**
@@ -1,13 +1,13 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { forwardRef, useMemo } from "react";
3
3
  import { parseURL, resolveURL, withoutTrailingSlash } from "ufo";
4
- import { useIsSSR } from "../hooks/useIsSSR";
5
- import { useRouter } from "../hooks/useRouter";
6
- import { isSameOrigin } from "../utils";
7
- import { useRoute } from "../hooks/useRoute";
8
- import { useRouterState } from "../hooks/useRouterState";
9
- export const Link = forwardRef((props, ref) => {
10
- const Comp = props.as || "a";
4
+ import { useIsSSR } from "../hooks/useIsSSR.js";
5
+ import { useRouter } from "../hooks/useRouter.js";
6
+ import { addTrailingSlash, isSameOrigin } from "../utils.js";
7
+ import { useRoute } from "../hooks/useRoute.js";
8
+ import { useRouterState } from "../hooks/useRouterState.js";
9
+ export const Link = forwardRef(({ preferBack, as, ...props }, ref) => {
10
+ const Comp = as || "a";
11
11
  if (env.admin) {
12
12
  return (_jsx(Comp, { ref: ref, ...props, href: props.href ?? undefined, onClick: (e) => {
13
13
  props.onClick?.(e);
@@ -15,17 +15,16 @@ export const Link = forwardRef((props, ref) => {
15
15
  } }));
16
16
  }
17
17
  else {
18
- const preload = useRouter((r) => r.preload);
19
- const handleClickEvent = useRouter((r) => r.handleClickEvent);
18
+ const router = useRouter();
20
19
  const state = useLinkState(props.href ?? "");
21
- return (_jsx(Comp, { ref: ref, "data-active": state.active ?? undefined, "data-child-active": state.childActive ?? undefined, "data-pending": state.pending ?? undefined, ...props, href: props.href ?? undefined, onMouseEnter: (e) => {
20
+ return (_jsx(Comp, { ref: ref, "data-active": state.active ?? undefined, "data-child-active": state.childActive ?? undefined, "data-pending": state.pending ?? undefined, ...props, href: addTrailingSlash(props.href), onMouseEnter: (e) => {
22
21
  if (props.onMouseEnter) {
23
22
  props.onMouseEnter(e);
24
23
  }
25
- preload(props.href);
24
+ router.preload(props.href);
26
25
  }, onClick: (e) => {
27
26
  props.onClick?.(e);
28
- handleClickEvent(e, props.href ?? undefined);
27
+ router.handleClickEvent(e, props.href ?? undefined, preferBack);
29
28
  } }));
30
29
  }
31
30
  });
@@ -1,4 +1,4 @@
1
- import { RouteState } from "../types";
1
+ import { RouteState } from "../types.js";
2
2
  export declare const AppRenderer: import("react").MemoExoticComponent<() => import("react/jsx-runtime").JSX.Element>;
3
3
  export declare function MainRoute(): import("react/jsx-runtime").JSX.Element;
4
4
  export declare function RouteDisplay(props: {
@@ -1,15 +1,16 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { memo, Suspense, useMemo } from "react";
3
- import { RouteItemContext } from "../context";
4
- import { useRouter } from "../hooks/useRouter";
5
- import { useRouterState } from "../hooks/useRouterState";
2
+ import { memo, useMemo } from "react";
3
+ import { RouteErrorBoundary } from "../../../utils/RouteErrorBoundary.js";
4
+ import { RouteItemContext } from "../context.js";
5
+ import { useRouter } from "../hooks/useRouter.js";
6
+ import { useRouterState } from "../hooks/useRouterState.js";
6
7
  export const AppRenderer = memo(() => {
7
8
  const { appData, appComponent: AppComponent } = useRouter().loader;
8
9
  const { activeRoute } = useRouterState();
9
10
  const app = useMemo(() => {
10
11
  return (_jsx(AppComponent, { ...appData, children: _jsx(MainRoute, {}) }));
11
12
  }, [AppComponent, appData]);
12
- return (_jsx(Suspense, { children: _jsx(RouteItemContext.Provider, { value: activeRoute, children: app }) }));
13
+ return _jsx(RouteItemContext.Provider, { value: activeRoute, children: app });
13
14
  });
14
15
  export function MainRoute() {
15
16
  const activeRoute = useRouterState((s) => s.activeRoute);
@@ -22,7 +23,7 @@ export function RouteDisplay(props) {
22
23
  }
23
24
  return useMemo(() => {
24
25
  let child = !!Component && _jsx(Component, { ...props.route.props });
25
- return _jsx(RouteItemContext.Provider, { value: props.route, children: child });
26
+ return (_jsx(RouteErrorBoundary, { route: props.route, children: _jsx(RouteItemContext.Provider, { value: props.route, children: child }) }));
26
27
  }, [Component, props.route, props.route]);
27
28
  }
28
29
  RouteDisplay.displayName = "RouteDisplay";
@@ -1,6 +1,6 @@
1
1
  import { PropsWithChildren } from "react";
2
- import { RouteLoader } from "../loader";
3
- import { RouteState } from "../types";
2
+ import { RouteLoader } from "../loader.js";
3
+ import { RouteState } from "../types.js";
4
4
  type Props = PropsWithChildren<{
5
5
  route: RouteState;
6
6
  loader: RouteLoader;
@@ -1,14 +1,13 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Suspense } from "react";
3
- import { RouterContext, RouterStateContext } from "../context";
4
- import { useRouter } from "../hooks/useRouter";
5
- import { useRouterState } from "../hooks/useRouterState";
6
- import { AppRenderer } from "./RouteRenderer";
2
+ import { RouterContext, RouterStateContext } from "../context.js";
3
+ import { useRouter } from "../hooks/useRouter.js";
4
+ import { useRouterState } from "../hooks/useRouterState.js";
5
+ import { AppRenderer } from "./RouteRenderer.js";
7
6
  export function SSRRouter(props) {
8
7
  const router = useRouter();
9
8
  const routerState = useRouterState();
10
9
  router.loader = props.loader;
11
10
  routerState.history = [props.route];
12
11
  routerState.activeRoute = props.route;
13
- return (_jsx(Suspense, { children: _jsx(RouterContext.Provider, { value: router, children: _jsx(RouterStateContext.Provider, { value: routerState, children: _jsx(AppRenderer, {}) }) }) }));
12
+ return (_jsx(RouterContext.Provider, { value: router, children: _jsx(RouterStateContext.Provider, { value: routerState, children: _jsx(AppRenderer, {}) }) }));
14
13
  }
@@ -1,4 +1,4 @@
1
- import { useRouterEvents } from "../hooks/useRouterEvents";
1
+ import { useRouterEvents } from "../hooks/useRouterEvents.js";
2
2
  /**
3
3
  * Enables scroll restoration when navigation backwards, as well as resetting scroll position when navigating forwards.
4
4
  */
@@ -11,7 +11,10 @@ export function ScrollRestoration(props) {
11
11
  }));
12
12
  useRouterEvents((event) => {
13
13
  if (event.type === "navigate:changed") {
14
- // console.log("Changed! Restoring scroll", event.currentRoute)
14
+ // If the pathname and search term are the same, then we're on the same page
15
+ const isSameRoute = event.currentRoute.pathname === event.lastRoute.pathname && event.currentRoute.search === event.lastRoute.search;
16
+ if (isSameRoute)
17
+ return;
15
18
  if (event.currentRoute.returnState) {
16
19
  restore({
17
20
  top: event.currentRoute.returnState.scrollTop ?? 0,
@@ -1,5 +1,8 @@
1
- import { Context } from "react";
2
- import { RouterAPI, RouterAPIState } from "./types";
3
- export declare const RouterContext: Context<RouterAPI>;
4
- export declare const RouterStateContext: Context<RouterAPIState>;
5
- export declare const RouteItemContext: Context<import("./types").TypedRouteState<"_unknown", {}>>;
1
+ import { RouteMetaTag, RouterAPI, RouterAPIState } from "./types.js";
2
+ export declare const RouterContext: import("react").Context<RouterAPI>;
3
+ export declare const RouterStateContext: import("react").Context<RouterAPIState>;
4
+ export declare const RouteItemContext: import("react").Context<import("./types.js").TypedRouteState<"_unknown", {}>>;
5
+ export declare const clientMetaTags: {
6
+ tags: null | RouteMetaTag[];
7
+ setMetaTags(tags: RouteMetaTag[]): void;
8
+ };
@@ -1,5 +1,6 @@
1
1
  import { createContext } from "react";
2
- import { RouteLoader } from "./loader";
2
+ import { RouteLoader } from "./loader.js";
3
+ import { proxy } from "valtio";
3
4
  const NOOP_ROUTE = {
4
5
  component: () => null,
5
6
  id: "initial",
@@ -12,6 +13,7 @@ const NOOP_ROUTE = {
12
13
  search: "",
13
14
  query: {},
14
15
  returnState: undefined,
16
+ meta: {},
15
17
  };
16
18
  export const RouterContext = createContext({
17
19
  loader: new RouteLoader(),
@@ -25,6 +27,10 @@ export const RouterContext = createContext({
25
27
  replaceQuery(query) { },
26
28
  handleClickEvent(e, originalHref) { },
27
29
  emitEvent(event) { },
30
+ getState() {
31
+ return null;
32
+ },
33
+ restoreRoute(route) { },
28
34
  });
29
35
  export const RouterStateContext = createContext({
30
36
  history: [NOOP_ROUTE],
@@ -32,98 +38,9 @@ export const RouterStateContext = createContext({
32
38
  blockers: [],
33
39
  });
34
40
  export const RouteItemContext = createContext(NOOP_ROUTE);
35
- // type StoreHook<State> = <Slice = State>(
36
- // selector?: (state: State) => Slice,
37
- // areEqual?: (a: Slice, b: Slice) => boolean,
38
- // ) => Slice
39
- // type Store<T> = {
40
- // subscribe(sub: (value: T) => void): () => void
41
- // value: T
42
- // update(next: T): void
43
- // }
44
- // function createStore<T>(initial: T): Store<T> {
45
- // let value = initial
46
- // const subscribers = new Set<(value: T) => void>()
47
- // return {
48
- // subscribe(sub: (value: T) => void) {
49
- // subscribers.add(sub)
50
- // return () => {
51
- // subscribers.delete(sub)
52
- // }
53
- // },
54
- // get value() {
55
- // return value
56
- // },
57
- // update(nextValue: T) {
58
- // value = nextValue
59
- // if (subscribers.size) {
60
- // console.log("UPDATE", value)
61
- // startTransition(() => {
62
- // for (let sub of subscribers) {
63
- // sub(value)
64
- // }
65
- // })
66
- // }
67
- // },
68
- // }
69
- // }
70
- // export function createStoreHook<T>(store: Store<T>): StoreHook<T> {
71
- // const hook = (selector: any) => {
72
- // const get = () => (selector ? selector(store.value) : store.value)
73
- // return useSyncExternalStore(store.subscribe, get, get)
74
- // }
75
- // return hook as StoreHook<T>
76
- // }
77
- // export function createStoreContextHook<T>(ctx: Context<Store<T>>): StoreHook<T> {
78
- // const hook = (selector: any) => {
79
- // const store = useContext(ctx)
80
- // const get = () => (selector ? selector(store.value) : store.value)
81
- // return useSyncExternalStore(store.subscribe, get, get)
82
- // }
83
- // return hook as StoreHook<T>
84
- // }
85
- // export const RouterContext = createContext<Store<RouterAPI>>(null!)
86
- // export const RouterStateContext = createContext<Store<RouterAPIState>>(null!)
87
- // export const RouteContext = createContext<Store<RouteState>>(null!)
88
- // const NOOP_ROUTE: RouteState = {
89
- // component: () => null,
90
- // id: "initial",
91
- // hash: "",
92
- // key: "initial",
93
- // pathname: "",
94
- // props: {},
95
- // uri: "",
96
- // view: "_unknown",
97
- // search: "",
98
- // query: {},
99
- // returnState: undefined,
100
- // }
101
- // export const createRouteItemStore = (initial?: RouteState) => {
102
- // return createStore<RouteState>(initial ?? NOOP_ROUTE)
103
- // }
104
- // export function createRouterAPIStore(initial?: RouterAPI) {
105
- // return createStore<RouterAPI>(
106
- // initial ?? {
107
- // loader: new RouteLoader(),
108
- // async navigate(url) {},
109
- // async prefetch(url) {},
110
- // async preload(url) {},
111
- // subscribe(fn) {
112
- // return () => {}
113
- // },
114
- // replaceHash(hash) {},
115
- // replaceQuery(query) {},
116
- // handleClickEvent(e, originalHref) {},
117
- // emitEvent(event) {},
118
- // },
119
- // )
120
- // }
121
- // export function createRouterStateStore(initial?: RouterAPIState) {
122
- // return createStore<RouterAPIState>(
123
- // initial ?? {
124
- // history: [],
125
- // activeRoute: NOOP_ROUTE,
126
- // blockers: [],
127
- // },
128
- // )
129
- // }
41
+ export const clientMetaTags = proxy({
42
+ tags: null,
43
+ setMetaTags(tags) {
44
+ clientMetaTags.tags = tags;
45
+ },
46
+ });
@@ -1,5 +1,6 @@
1
+ import { Dispatch } from "react";
1
2
  /**
2
3
  * Works exactly like useState, but allows back and forward navigation to restore the previous state.
3
4
  * You must pass a unique ID as an additional first parameter to ensure the state is restored correctly.
4
5
  */
5
- export declare function useRestorableState<T>(key: string, fallback?: T): any[];
6
+ export declare function useRestorableState<T>(key: string, fallback: T): [T, Dispatch<T>];
@@ -1,5 +1,6 @@
1
1
  import { useState } from "react";
2
- import { useRoute, useRouterEvents } from "..";
2
+ import { useRouterEvents } from "./useRouterEvents.js";
3
+ import { useRoute } from "./useRoute.js";
3
4
  /**
4
5
  * Works exactly like useState, but allows back and forward navigation to restore the previous state.
5
6
  * You must pass a unique ID as an additional first parameter to ensure the state is restored correctly.