@pracht/core 0.1.0 → 0.2.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.
package/README.md CHANGED
@@ -16,6 +16,10 @@ npm install @pracht/core preact preact-render-to-string
16
16
  - `route()` — declare a route with path, component, loader, and rendering mode
17
17
  - `group()` — group routes under a shared shell or middleware
18
18
 
19
+ Route modules may export the page as a function default export or as a named
20
+ `Component` export. Named exports such as `loader`, `head`, `ErrorBoundary`, and
21
+ `getStaticPaths` keep their special route-module behavior.
22
+
19
23
  ### Server
20
24
 
21
25
  - `handlePrachtRequest()` — server renderer that produces full HTML with hydration markers
package/dist/index.d.mts CHANGED
@@ -169,7 +169,8 @@ type LoaderFn<TContext = any, TData = unknown> = (args: LoaderArgs<TContext>) =>
169
169
  interface RouteModule<TContext = any, TLoader extends LoaderLike = undefined> {
170
170
  loader?: LoaderFn<TContext>;
171
171
  head?: (args: HeadArgs<TLoader, TContext>) => MaybePromise<HeadMetadata>;
172
- Component: FunctionComponent<RouteComponentProps<TLoader>>;
172
+ Component?: FunctionComponent<RouteComponentProps<TLoader>>;
173
+ default?: FunctionComponent<RouteComponentProps<TLoader>>;
173
174
  ErrorBoundary?: FunctionComponent<ErrorBoundaryProps>;
174
175
  getStaticPaths?: () => MaybePromise<RouteParams[]>;
175
176
  }
@@ -234,6 +235,14 @@ declare function forwardRef<P = {}>(fn: ((props: P, ref: any) => any) & {
234
235
  ref?: any;
235
236
  }>;
236
237
  //#endregion
238
+ //#region src/hydration.d.ts
239
+ /**
240
+ * Returns `true` once the initial hydration (including all Suspense
241
+ * boundaries) has fully resolved. During SSR and hydration this returns
242
+ * `false`.
243
+ */
244
+ declare function useIsHydrated(): boolean;
245
+ //#endregion
237
246
  //#region src/runtime.d.ts
238
247
  type PrachtRuntimeDiagnosticPhase = "match" | "middleware" | "loader" | "action" | "render" | "api";
239
248
  interface PrachtRuntimeDiagnostics {
@@ -384,4 +393,4 @@ interface InitClientRouterOptions {
384
393
  }
385
394
  declare function initClientRouter(options: InitClientRouterOptions): Promise<void>;
386
395
  //#endregion
387
- export { type ApiConfig, type ApiRouteHandler, type ApiRouteMatch, type ApiRouteModule, type BaseRouteArgs, type DataModule, type ErrorBoundaryProps, Form, type FormProps, type GroupDefinition, type GroupMeta, type HandlePrachtRequestOptions, type HeadArgs, type HeadMetadata, type HttpMethod, type ISGManifestEntry, type InitClientRouterOptions, type LoaderArgs, type LoaderData, type LoaderFn, type Location, type MiddlewareArgs, type MiddlewareFn, type MiddlewareModule, type MiddlewareResult, type ModuleImporter, type ModuleRef, type ModuleRegistry, type NavigateFn, type PrachtApp, type PrachtAppConfig, PrachtHttpError, type PrachtHydrationState, type PrachtRuntimeDiagnosticPhase, type PrachtRuntimeDiagnostics, PrachtRuntimeProvider, type PrefetchStrategy, type PrerenderAppOptions, type PrerenderAppResult, type PrerenderResult, type Register, type RenderMode, type ResolvedApiRoute, type ResolvedPrachtApp, type ResolvedRoute, type RouteComponentProps, type RouteConfig, type RouteDefinition, type RouteMatch, type RouteMeta, type RouteModule, type RouteParams, type RouteRevalidate, type RouteStateResult, type RouteTreeNode, type SerializedRouteError, type ShellModule, type ShellProps, type StartAppOptions, Suspense, type TimeRevalidatePolicy, applyDefaultSecurityHeaders, buildPathFromSegments, defineApp, forwardRef, group, handlePrachtRequest, initClientRouter, lazy, matchApiRoute, matchAppRoute, prerenderApp, readHydrationState, resolveApiRoutes, resolveApp, route, startApp, timeRevalidate, useLocation, useNavigate, useParams, useRevalidate, useRevalidateRoute, useRouteData };
396
+ export { type ApiConfig, type ApiRouteHandler, type ApiRouteMatch, type ApiRouteModule, type BaseRouteArgs, type DataModule, type ErrorBoundaryProps, Form, type FormProps, type GroupDefinition, type GroupMeta, type HandlePrachtRequestOptions, type HeadArgs, type HeadMetadata, type HttpMethod, type ISGManifestEntry, type InitClientRouterOptions, type LoaderArgs, type LoaderData, type LoaderFn, type Location, type MiddlewareArgs, type MiddlewareFn, type MiddlewareModule, type MiddlewareResult, type ModuleImporter, type ModuleRef, type ModuleRegistry, type NavigateFn, type PrachtApp, type PrachtAppConfig, PrachtHttpError, type PrachtHydrationState, type PrachtRuntimeDiagnosticPhase, type PrachtRuntimeDiagnostics, PrachtRuntimeProvider, type PrefetchStrategy, type PrerenderAppOptions, type PrerenderAppResult, type PrerenderResult, type Register, type RenderMode, type ResolvedApiRoute, type ResolvedPrachtApp, type ResolvedRoute, type RouteComponentProps, type RouteConfig, type RouteDefinition, type RouteMatch, type RouteMeta, type RouteModule, type RouteParams, type RouteRevalidate, type RouteStateResult, type RouteTreeNode, type SerializedRouteError, type ShellModule, type ShellProps, type StartAppOptions, Suspense, type TimeRevalidatePolicy, applyDefaultSecurityHeaders, buildPathFromSegments, defineApp, forwardRef, group, handlePrachtRequest, initClientRouter, lazy, matchApiRoute, matchAppRoute, prerenderApp, readHydrationState, resolveApiRoutes, resolveApp, route, startApp, timeRevalidate, useIsHydrated, useLocation, useNavigate, useParams, useRevalidate, useRevalidateRoute, useRouteData };
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { a as matchApiRoute, c as resolveApp, i as group, l as route, n as buildPathFromSegments, o as matchAppRoute, r as defineApp, s as resolveApiRoutes, u as timeRevalidate } from "./app-Cep0el7c.mjs";
2
2
  import { createContext, h, hydrate, options, render } from "preact";
3
- import { Suspense, lazy } from "preact-suspense";
4
3
  import { useContext, useEffect, useMemo, useState } from "preact/hooks";
4
+ import { Suspense, lazy } from "preact-suspense";
5
5
  //#region src/forwardRef.ts
6
6
  let oldDiffHook = options.__b;
7
7
  options.__b = (vnode) => {
@@ -27,6 +27,54 @@ function forwardRef(fn) {
27
27
  return Forwarded;
28
28
  }
29
29
  //#endregion
30
+ //#region src/hydration.ts
31
+ const MODE_HYDRATE = 32;
32
+ let _hydrating = false;
33
+ let _suspensionCount = 0;
34
+ let _hydrated = false;
35
+ const oldCatchError = options.__e;
36
+ options.__e = (err, newVNode, oldVNode, errorInfo) => {
37
+ if (_hydrating && !_hydrated && err && err.then) {
38
+ if (!!(newVNode && newVNode.__u && newVNode.__u & MODE_HYDRATE) || !!(newVNode && newVNode.__h)) {
39
+ _suspensionCount++;
40
+ let settled = false;
41
+ const onSettled = () => {
42
+ if (settled) return;
43
+ settled = true;
44
+ _suspensionCount--;
45
+ };
46
+ err.then(onSettled, onSettled);
47
+ }
48
+ }
49
+ if (oldCatchError) oldCatchError(err, newVNode, oldVNode, errorInfo);
50
+ };
51
+ const oldCommit = options.__c;
52
+ options.__c = (vnode, commitQueue) => {
53
+ if (_hydrating && !_hydrated && _suspensionCount <= 0) {
54
+ _hydrated = true;
55
+ _hydrating = false;
56
+ }
57
+ if (oldCommit) oldCommit(vnode, commitQueue);
58
+ };
59
+ /**
60
+ * Mark the start of a hydration pass. Call this right before `hydrate()`.
61
+ */
62
+ function markHydrating() {
63
+ if (!_hydrated) _hydrating = true;
64
+ }
65
+ /**
66
+ * Returns `true` once the initial hydration (including all Suspense
67
+ * boundaries) has fully resolved. During SSR and hydration this returns
68
+ * `false`.
69
+ */
70
+ function useIsHydrated() {
71
+ const [hydrated, setHydrated] = useState(_hydrated);
72
+ useEffect(() => {
73
+ setHydrated(true);
74
+ }, []);
75
+ return hydrated;
76
+ }
77
+ //#endregion
30
78
  //#region src/runtime.ts
31
79
  const SAFE_METHODS = new Set(["GET", "HEAD"]);
32
80
  const HYDRATION_STATE_ELEMENT_ID = "pracht-state";
@@ -305,9 +353,10 @@ async function handlePrachtRequest(options) {
305
353
  modulePreloadUrls
306
354
  }));
307
355
  }
308
- if (!routeModule.Component) throw new Error("Route has no Component export");
356
+ const DefaultComponent = typeof routeModule.default === "function" ? routeModule.default : void 0;
357
+ const Component = routeModule.Component ?? DefaultComponent;
358
+ if (!Component) throw new Error("Route has no Component or default export");
309
359
  const { renderToStringAsync } = await import("preact-render-to-string");
310
- const Component = routeModule.Component;
311
360
  const Shell = shellModule?.Shell;
312
361
  const componentProps = {
313
362
  data,
@@ -868,7 +917,8 @@ async function initClientRouter(options) {
868
917
  let Shell = null;
869
918
  const resolvedShell = await (shellModPromise ?? startShellImport(match));
870
919
  if (resolvedShell) Shell = resolvedShell.Shell;
871
- const Component = state.error ? routeMod.ErrorBoundary : routeMod.Component;
920
+ const DefaultComponent = typeof routeMod.default === "function" ? routeMod.default : void 0;
921
+ const Component = state.error ? routeMod.ErrorBoundary : routeMod.Component ?? DefaultComponent;
872
922
  if (!Component) return null;
873
923
  const props = state.error ? { error: deserializeRouteError(state.error) } : {
874
924
  data: state.data,
@@ -980,7 +1030,10 @@ async function initClientRouter(options) {
980
1030
  }
981
1031
  const tree = await buildRouteTree(initialMatch, state, void 0, initialShellPromise);
982
1032
  if (tree) if (initialMatch.route.render === "spa") render(tree, root);
983
- else hydrate(tree, root);
1033
+ else {
1034
+ markHydrating();
1035
+ hydrate(tree, root);
1036
+ }
984
1037
  }
985
1038
  document.addEventListener("click", (e) => {
986
1039
  const anchor = e.target.closest?.("a");
@@ -1080,4 +1133,4 @@ var PrachtHttpError = class extends Error {
1080
1133
  }
1081
1134
  };
1082
1135
  //#endregion
1083
- export { Form, PrachtHttpError, PrachtRuntimeProvider, Suspense, applyDefaultSecurityHeaders, buildPathFromSegments, defineApp, forwardRef, group, handlePrachtRequest, initClientRouter, lazy, matchApiRoute, matchAppRoute, prerenderApp, readHydrationState, resolveApiRoutes, resolveApp, route, startApp, timeRevalidate, useLocation, useNavigate, useParams, useRevalidate, useRevalidateRoute, useRouteData };
1136
+ export { Form, PrachtHttpError, PrachtRuntimeProvider, Suspense, applyDefaultSecurityHeaders, buildPathFromSegments, defineApp, forwardRef, group, handlePrachtRequest, initClientRouter, lazy, matchApiRoute, matchAppRoute, prerenderApp, readHydrationState, resolveApiRoutes, resolveApp, route, startApp, timeRevalidate, useIsHydrated, useLocation, useNavigate, useParams, useRevalidate, useRevalidateRoute, useRouteData };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pracht/core",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "homepage": "https://github.com/JoviDeCroock/pracht/tree/main/packages/framework",
5
5
  "bugs": {
6
6
  "url": "https://github.com/JoviDeCroock/pracht/issues"