evikit 2.9.2 → 2.10.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 (4) hide show
  1. package/README.md +11 -19
  2. package/index.d.ts +19 -1
  3. package/index.js +41 -7
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -180,7 +180,7 @@ export function ErrorPage({ children, error }) {
180
180
  useTitle(status);
181
181
 
182
182
  return main(
183
- null,
183
+ { id: "main" },
184
184
  p({ class: "nav" }, a({ href: "/" }, i18n.gettext("Questions"))),
185
185
  h1({ class: "title" }, status),
186
186
  children,
@@ -221,9 +221,7 @@ export function api() {
221
221
  Add a Preact router at `src/app.js` (uncomment the routes one-by-one when we add them later):
222
222
 
223
223
  ```js
224
- import { hydrate, Router, ssr } from "evikit";
225
- import { h } from "preact";
226
- import { Route } from "preact-iso";
224
+ import { route, ssr } from "evikit";
227
225
 
228
226
  import { acceptLanguages } from "./lib/index.js";
229
227
  import { ErrorPage } from "./routes/error.js";
@@ -232,21 +230,11 @@ import { ErrorPage } from "./routes/error.js";
232
230
 
233
231
  acceptLanguages();
234
232
 
235
- /** @param {import("evikit").SsrProps} props */
236
- export const prerender = (props) => ssr(App, props);
237
-
238
- /** @type {import("evikit").SsrApp} */
239
- function App(props) {
240
- return h(
241
- Router,
242
- props,
243
- // h(Route, { component: HomePage, path: "/" }),
244
- // h(Route, { component: QuestionIdPage, path: "/question/:id" }),
245
- h(Route, { component: ErrorPage, default: true }),
246
- );
247
- }
248
-
249
- hydrate(App, "#app");
233
+ export const prerender = ssr(
234
+ { ErrorPage },
235
+ // route("/", HomePage),
236
+ // route("/question/:id", QuestionIdPage),
237
+ );
250
238
  ```
251
239
 
252
240
  #### HTML entry point
@@ -386,6 +374,8 @@ export const HomeGet = {
386
374
  };
387
375
  ```
388
376
 
377
+ > 😉 Note: for live refresh to know when data changes, use columns from `schema` in `outputs`, instead of writing `string()` or `number()` anew.
378
+
389
379
  Code the API server endpoint in `src/routes/api/server.js`:
390
380
 
391
381
  ```js
@@ -523,6 +513,8 @@ export function QuestionIdPage() {
523
513
  }
524
514
 
525
515
  return main(
516
+ { id: "main" },
517
+
526
518
  h1({ class: "title" }, title),
527
519
 
528
520
  // VOTE_FORM: replace with form view from the "Vote for choice" section.
package/index.d.ts CHANGED
@@ -14,6 +14,7 @@ export interface QueryState {
14
14
  data: any; // eslint-disable-line @typescript-eslint/no-explicit-any
15
15
  dataUpdatedAt: number;
16
16
  error: null | unknown;
17
+ fromEventSource: boolean;
17
18
  isInvalidated: boolean;
18
19
  status: "error" | "loading" | "success";
19
20
  }
@@ -61,6 +62,7 @@ export function dehydrate(
61
62
 
62
63
  export function getI18n(...langs: string[]): Promise<i18n.Gettext>;
63
64
 
65
+ /** @deprecated */
64
66
  export function hydrate(
65
67
  App: (props: {
66
68
  dehydratedState?: DehydratedState | undefined;
@@ -69,6 +71,12 @@ export function hydrate(
69
71
  renderTarget: string,
70
72
  ): Promise<void>;
71
73
 
74
+ export function route(
75
+ path: string,
76
+ component: () => VNode<any> | VNode<never>, // eslint-disable-line @typescript-eslint/no-explicit-any
77
+ ): { component: typeof component; path: string };
78
+
79
+ /** @deprecated */
72
80
  export function Router({
73
81
  children,
74
82
  dehydratedState,
@@ -80,8 +88,18 @@ export function Router({
80
88
  }): VNode<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
81
89
 
82
90
  /** @deprecated */
83
- export function ssr(App: SsrApp): (props: SsrProps) => Promise<SsrResponse>;
84
91
  export function ssr(App: SsrApp, props: SsrProps): Promise<SsrResponse>;
92
+ /** @deprecated */
93
+ export function ssr(App: SsrApp): (props: SsrProps) => Promise<SsrResponse>;
94
+ export function ssr(
95
+ props: {
96
+ ErrorPage: (props: {
97
+ children?: import("preact").ComponentChildren;
98
+ error?: unknown;
99
+ }) => VNode<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
100
+ },
101
+ ...routes: ReturnType<typeof route>[]
102
+ ): (props: SsrProps) => Promise<SsrResponse>;
85
103
 
86
104
  export function useI18n(): i18n.Gettext;
87
105
 
package/index.js CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  Router as IsoRouter,
7
7
  LocationProvider,
8
8
  prerender,
9
+ Route,
9
10
  } from "preact-iso";
10
11
  import { locationStub } from "preact-iso/prerender";
11
12
  import { useState } from "preact/hooks";
@@ -63,6 +64,14 @@ export async function hydrate(App, renderTarget) {
63
64
  }
64
65
  }
65
66
 
67
+ /**
68
+ * @param {string | null} path
69
+ * @param {() => import("preact").VNode} component
70
+ */
71
+ export function route(path, component) {
72
+ return { component, default: !path, path };
73
+ }
74
+
66
75
  /** @param {{ children?: import("preact").ComponentChildren, dehydratedState?: ReturnType<import("./query.js").dehydrate> | undefined, i18n: ReturnType<import("./i18n.js").useI18n> }} props */
67
76
  export function Router({ children, dehydratedState, i18n }) {
68
77
  const [queryClient] = useState(
@@ -91,16 +100,41 @@ export function Router({ children, dehydratedState, i18n }) {
91
100
  );
92
101
  }
93
102
 
103
+ /** @param {(import("./index.d.ts").SsrApp | import("./index.d.ts").SsrProps | { ErrorPage: () => import("preact").VNode } | ReturnType<import("./index.d.ts").route>)[]} args */
104
+ export function ssr(...args) {
105
+ if (typeof args[0] == "function") {
106
+ const App = args[0];
107
+ /** @type {import("./index.d.ts").SsrProps | undefined} */
108
+ const props = /** @type {any} */ (args[1]);
109
+ if (props) {
110
+ return ssrInner(App, props);
111
+ }
112
+ /** @param {import("./index.d.ts").SsrProps} props */
113
+ return (props) => ssrInner(App, props);
114
+ }
115
+ /** @type {() => import("preact").VNode} */
116
+ const ErrorPage = /** @type {any} */ (args[0]).ErrorPage;
117
+ /** @type {ReturnType<import("./index.d.ts").route>[]} */
118
+ const routes = /** @type {any} */ (args.slice(1));
119
+ /** @type {import("./index.d.ts").SsrApp} */
120
+ function App(props) {
121
+ return h(
122
+ Router,
123
+ props,
124
+ ...routes.map((route) => h(Route, route)),
125
+ h(Route, { component: ErrorPage, default: true }),
126
+ );
127
+ }
128
+ hydrate(App, "#app");
129
+ /** @param {import("./index.d.ts").SsrProps} props */
130
+ return (props) => ssrInner(App, props);
131
+ }
132
+
94
133
  /**
95
134
  * @param {(props: { dehydratedState?: ReturnType<import("./query.js").dehydrate> | undefined, i18n: ReturnType<import("./i18n.js").useI18n> }) => import("preact").VNode} App
96
- * @param {{ cookie?: string | undefined, lang?: string, url: string }} [data]
135
+ * @param {{ cookie?: string | undefined, lang?: string, url: string }} data
97
136
  */
98
- export async function ssr(App, data) {
99
- if (!data) {
100
- /** @param {NonNullable<typeof data>} data */
101
- return (data) => ssr(App, data);
102
- }
103
-
137
+ async function ssrInner(App, data) {
104
138
  const i18n = await getI18n(...(data.lang ? [data.lang] : []));
105
139
 
106
140
  const queryClient = new QueryClient({
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "evikit",
3
3
  "description": "Preact/SQLite SSR framework for small web apps",
4
- "version": "2.9.2",
4
+ "version": "2.10.1",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": "./index.js",