master
Avraham Sakal 4 months ago
parent 2a4ff2e509
commit 602ba72f75

@ -1,4 +1,5 @@
{ {
"editor.defaultFormatter": "biomejs.biome", "editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true "editor.formatOnSave": true,
"biome.configurationPath": "biome.json"
} }

@ -9,7 +9,18 @@
"recommended": true "recommended": true
} }
}, },
"formatter": {
"enabled": true,
"indentWidth": 2,
"indentStyle": "space"
},
"files": { "files": {
"ignore": ["dist/**", "*.js", "*.cjs", "*.mjs", "*.spec.ts"] "ignore": [
"dist/**",
"*.js",
"*.cjs",
"*.mjs",
"*.spec.ts"
]
} }
} }

@ -4,6 +4,7 @@ import { NavLink } from "@mantine/core";
export function Link({ href, label }: { href: string; label: string }) { export function Link({ href, label }: { href: string; label: string }) {
const pageContext = usePageContext(); const pageContext = usePageContext();
const { urlPathname } = pageContext; const { urlPathname } = pageContext;
const isActive = href === "/" ? urlPathname === href : urlPathname.startsWith(href); const isActive =
href === "/" ? urlPathname === href : urlPathname.startsWith(href);
return <NavLink href={href} label={label} active={isActive} />; return <NavLink href={href} label={label} active={isActive} />;
} }

@ -9,7 +9,9 @@ const database =
// - We use globalThis so that the database isn't reset upon HMR. // - We use globalThis so that the database isn't reset upon HMR.
// - The database is reset when restarting the server, use a proper database (SQLite/PostgreSQL/...) if you want persistent data. // - The database is reset when restarting the server, use a proper database (SQLite/PostgreSQL/...) if you want persistent data.
// biome-ignore lint: // biome-ignore lint:
((globalThis as unknown as { __database: { todos: TodoItem[] } }).__database ??= { todos: todosDefault }); ((
globalThis as unknown as { __database: { todos: TodoItem[] } }
).__database ??= { todos: todosDefault });
const { todos } = database; const { todos } = database;

@ -5,7 +5,9 @@ import { env } from "hono/adapter";
import { compress } from "hono/compress"; import { compress } from "hono/compress";
import app from "./hono-entry.js"; import app from "./hono-entry.js";
const envs = env<{ NODE_ENV?: string; PORT?: string }>({ env: {} } as unknown as Context<{ const envs = env<{ NODE_ENV?: string; PORT?: string }>({
env: {},
} as unknown as Context<{
Bindings: { NODE_ENV?: string; PORT?: string }; Bindings: { NODE_ENV?: string; PORT?: string };
}>); }>);

@ -1,4 +1,7 @@
import { authjsHandler, authjsSessionMiddleware } from "./server/authjs-handler"; import {
authjsHandler,
authjsSessionMiddleware,
} from "./server/authjs-handler";
import { vikeHandler } from "./server/vike-handler"; import { vikeHandler } from "./server/vike-handler";
import { Hono } from "hono"; import { Hono } from "hono";
import { createHandler, createMiddleware } from "@universal-middleware/hono"; import { createHandler, createMiddleware } from "@universal-middleware/hono";

@ -6,18 +6,29 @@ import theme from "./theme.js";
import logoUrl from "../assets/logo.svg"; import logoUrl from "../assets/logo.svg";
import { Link } from "../components/Link"; import { Link } from "../components/Link";
export default function LayoutDefault({ children }: { children: React.ReactNode }) { export default function LayoutDefault({
children,
}: { children: React.ReactNode }) {
const [opened, { toggle }] = useDisclosure(); const [opened, { toggle }] = useDisclosure();
return ( return (
<MantineProvider theme={theme}> <MantineProvider theme={theme}>
<AppShell <AppShell
header={{ height: 60 }} header={{ height: 60 }}
navbar={{ width: 300, breakpoint: "sm", collapsed: { mobile: !opened } }} navbar={{
padding="md" width: 300,
breakpoint: "sm",
collapsed: { mobile: !opened },
}}
padding="lg"
> >
<AppShell.Header> <AppShell.Header>
<Group h="100%" px="md"> <Group h="100%" px="md">
<Burger opened={opened} onClick={toggle} hiddenFrom="sm" size="sm" /> <Burger
opened={opened}
onClick={toggle}
hiddenFrom="sm"
size="sm"
/>
<a href="/"> <a href="/">
{" "} {" "}
<Image h={50} fit="contain" src={logoUrl} />{" "} <Image h={50} fit="contain" src={logoUrl} />{" "}

@ -3,7 +3,11 @@ import { Counter } from "./Counter.js";
export default function Page() { export default function Page() {
return ( return (
<> <>
<h1 css={{ fontWeight: "700", fontSize: "1.875rem", paddingBottom: "1rem" }}>My Vike app</h1> <h1
css={{ fontWeight: "700", fontSize: "1.875rem", paddingBottom: "1rem" }}
>
My Vike app
</h1>
This page is: This page is:
<ul> <ul>
<li>Rendered to HTML.</li> <li>Rendered to HTML.</li>

@ -10,7 +10,9 @@ export const data = async (pageContext: PageContextServer) => {
// https://vike.dev/useConfig // https://vike.dev/useConfig
const config = useConfig(); const config = useConfig();
const response = await fetch(`https://brillout.github.io/star-wars/api/films/${pageContext.routeParams.id}.json`); const response = await fetch(
`https://brillout.github.io/star-wars/api/films/${pageContext.routeParams.id}.json`,
);
let movie = (await response.json()) as MovieDetails; let movie = (await response.json()) as MovieDetails;
config({ config({

@ -14,7 +14,11 @@ export default function Page() {
))} ))}
</ol> </ol>
<p> <p>
Source: <a href="https://brillout.github.io/star-wars">brillout.github.io/star-wars</a>. Source:{" "}
<a href="https://brillout.github.io/star-wars">
brillout.github.io/star-wars
</a>
.
</p> </p>
</> </>
); );

@ -9,7 +9,9 @@ export const data = async () => {
// https://vike.dev/useConfig // https://vike.dev/useConfig
const config = useConfig(); const config = useConfig();
const response = await fetch("https://brillout.github.io/star-wars/api/films.json"); const response = await fetch(
"https://brillout.github.io/star-wars/api/films.json",
);
const moviesData = (await response.json()) as MovieDetails[]; const moviesData = (await response.json()) as MovieDetails[];
config({ config({

@ -6,6 +6,8 @@ export type Data = {
todo: { text: string }[]; todo: { text: string }[];
}; };
export default async function data(_pageContext: PageContextServer): Promise<Data> { export default async function data(
_pageContext: PageContextServer,
): Promise<Data> {
return { todo: todos }; return { todo: todos };
} }

@ -1,7 +1,11 @@
import { trpc } from "../../trpc/client"; import { trpc } from "../../trpc/client";
import { useState } from "react"; import { useState } from "react";
export function TodoList({ initialTodoItems }: { initialTodoItems: { text: string }[] }) { export function TodoList({
initialTodoItems,
}: {
initialTodoItems: { text: string }[];
}) {
const [todoItems, setTodoItems] = useState(initialTodoItems); const [todoItems, setTodoItems] = useState(initialTodoItems);
const [newTodo, setNewTodo] = useState(""); const [newTodo, setNewTodo] = useState("");
return ( return (
@ -16,6 +20,7 @@ export function TodoList({ initialTodoItems }: { initialTodoItems: { text: strin
<form <form
onSubmit={async (ev) => { onSubmit={async (ev) => {
ev.preventDefault(); ev.preventDefault();
if (newTodo.trim() === "") return;
// Optimistic UI update // Optimistic UI update
setTodoItems((prev) => [...prev, { text: newTodo }]); setTodoItems((prev) => [...prev, { text: newTodo }]);
@ -29,7 +34,11 @@ export function TodoList({ initialTodoItems }: { initialTodoItems: { text: strin
} }
}} }}
> >
<input type="text" onChange={(ev) => setNewTodo(ev.target.value)} value={newTodo} /> <input
type="text"
onChange={(ev) => setNewTodo(ev.target.value)}
value={newTodo}
/>
<button type="submit">Add to-do</button> <button type="submit">Add to-do</button>
</form> </form>
</div> </div>

@ -1,14 +1,27 @@
import { Auth, type AuthConfig, createActionURL, setEnvDefaults } from "@auth/core"; import {
Auth,
type AuthConfig,
createActionURL,
setEnvDefaults,
} from "@auth/core";
import CredentialsProvider from "@auth/core/providers/credentials"; import CredentialsProvider from "@auth/core/providers/credentials";
import type { Session } from "@auth/core/types"; import type { Session } from "@auth/core/types";
// TODO: stop using universal-middleware and directly integrate server middlewares instead and/or use vike-server https://vike.dev/server. (Bati generates boilerplates that use universal-middleware https://github.com/magne4000/universal-middleware to make Bati's internal logic easier. This is temporary and will be removed soon.) // TODO: stop using universal-middleware and directly integrate server middlewares instead and/or use vike-server https://vike.dev/server. (Bati generates boilerplates that use universal-middleware https://github.com/magne4000/universal-middleware to make Bati's internal logic easier. This is temporary and will be removed soon.)
import type { Get, UniversalHandler, UniversalMiddleware } from "@universal-middleware/core"; import type {
Get,
UniversalHandler,
UniversalMiddleware,
} from "@universal-middleware/core";
const env: Record<string, string | undefined> = const env: Record<string, string | undefined> =
typeof process?.env !== "undefined" typeof process?.env !== "undefined"
? process.env ? process.env
: import.meta && "env" in import.meta : import.meta && "env" in import.meta
? (import.meta as ImportMeta & { env: Record<string, string | undefined> }).env ? (
import.meta as ImportMeta & {
env: Record<string, string | undefined>;
}
).env
: {}; : {};
if (!globalThis.crypto) { if (!globalThis.crypto) {
@ -16,7 +29,9 @@ if (!globalThis.crypto) {
* Polyfill needed if Auth.js code runs on node18 * Polyfill needed if Auth.js code runs on node18
*/ */
Object.defineProperty(globalThis, "crypto", { Object.defineProperty(globalThis, "crypto", {
value: await import("node:crypto").then((crypto) => crypto.webcrypto as Crypto), value: await import("node:crypto").then(
(crypto) => crypto.webcrypto as Crypto,
),
writable: false, writable: false,
configurable: true, configurable: true,
}); });
@ -24,7 +39,9 @@ if (!globalThis.crypto) {
const authjsConfig = { const authjsConfig = {
basePath: "/api/auth", basePath: "/api/auth",
trustHost: Boolean(env.AUTH_TRUST_HOST ?? env.VERCEL ?? env.NODE_ENV !== "production"), trustHost: Boolean(
env.AUTH_TRUST_HOST ?? env.VERCEL ?? env.NODE_ENV !== "production",
),
// TODO: Replace secret {@see https://authjs.dev/reference/core#secret} // TODO: Replace secret {@see https://authjs.dev/reference/core#secret}
secret: "MY_SECRET", secret: "MY_SECRET",
providers: [ providers: [
@ -51,12 +68,24 @@ const authjsConfig = {
/** /**
* Retrieve Auth.js session from Request * Retrieve Auth.js session from Request
*/ */
export async function getSession(req: Request, config: Omit<AuthConfig, "raw">): Promise<Session | null> { export async function getSession(
req: Request,
config: Omit<AuthConfig, "raw">,
): Promise<Session | null> {
setEnvDefaults(process.env, config); setEnvDefaults(process.env, config);
const requestURL = new URL(req.url); const requestURL = new URL(req.url);
const url = createActionURL("session", requestURL.protocol, req.headers, process.env, config); const url = createActionURL(
"session",
requestURL.protocol,
req.headers,
process.env,
config,
);
const response = await Auth(new Request(url, { headers: { cookie: req.headers.get("cookie") ?? "" } }), config); const response = await Auth(
new Request(url, { headers: { cookie: req.headers.get("cookie") ?? "" } }),
config,
);
const { status = 200 } = response; const { status = 200 } = response;
@ -71,20 +100,21 @@ export async function getSession(req: Request, config: Omit<AuthConfig, "raw">):
* Add Auth.js session to context * Add Auth.js session to context
* @link {@see https://authjs.dev/getting-started/session-management/get-session} * @link {@see https://authjs.dev/getting-started/session-management/get-session}
**/ **/
export const authjsSessionMiddleware: Get<[], UniversalMiddleware> = () => async (request, context) => { export const authjsSessionMiddleware: Get<[], UniversalMiddleware> =
try { () => async (request, context) => {
return { try {
...context, return {
session: await getSession(request, authjsConfig), ...context,
}; session: await getSession(request, authjsConfig),
} catch (error) { };
console.debug("authjsSessionMiddleware:", error); } catch (error) {
return { console.debug("authjsSessionMiddleware:", error);
...context, return {
session: null, ...context,
}; session: null,
} };
}; }
};
/** /**
* Auth.js route * Auth.js route

@ -3,16 +3,22 @@ import { renderPage } from "vike/server";
// TODO: stop using universal-middleware and directly integrate server middlewares instead and/or use vike-server https://vike.dev/server. (Bati generates boilerplates that use universal-middleware https://github.com/magne4000/universal-middleware to make Bati's internal logic easier. This is temporary and will be removed soon.) // TODO: stop using universal-middleware and directly integrate server middlewares instead and/or use vike-server https://vike.dev/server. (Bati generates boilerplates that use universal-middleware https://github.com/magne4000/universal-middleware to make Bati's internal logic easier. This is temporary and will be removed soon.)
import type { Get, UniversalHandler } from "@universal-middleware/core"; import type { Get, UniversalHandler } from "@universal-middleware/core";
export const vikeHandler: Get<[], UniversalHandler> = () => async (request, context, runtime) => { export const vikeHandler: Get<[], UniversalHandler> =
const pageContextInit = { ...context, ...runtime, urlOriginal: request.url, headersOriginal: request.headers }; () => async (request, context, runtime) => {
const pageContext = await renderPage(pageContextInit); const pageContextInit = {
const response = pageContext.httpResponse; ...context,
...runtime,
urlOriginal: request.url,
headersOriginal: request.headers,
};
const pageContext = await renderPage(pageContextInit);
const response = pageContext.httpResponse;
const { readable, writable } = new TransformStream(); const { readable, writable } = new TransformStream();
response.pipe(writable); response.pipe(writable);
return new Response(readable, { return new Response(readable, {
status: response.statusCode, status: response.statusCode,
headers: response.headers, headers: response.headers,
}); });
}; };

@ -1,4 +1,5 @@
import { initTRPC } from "@trpc/server"; import { initTRPC } from "@trpc/server";
import { todos } from "../database/todoItems";
/** /**
* Initialization of tRPC backend * Initialization of tRPC backend
@ -26,6 +27,7 @@ export const appRouter = router({
}) })
.mutation(async (opts) => { .mutation(async (opts) => {
console.log("Received new todo", { text: opts.input }); console.log("Received new todo", { text: opts.input });
todos.push({ text: opts.input });
}), }),
}); });

@ -10,20 +10,10 @@
"noEmit": true, "noEmit": true,
"moduleResolution": "Bundler", "moduleResolution": "Bundler",
"target": "ES2022", "target": "ES2022",
"lib": [ "lib": ["DOM", "DOM.Iterable", "ESNext"],
"DOM", "types": ["vite/client", "vike-react", "vike-cloudflare/types"],
"DOM.Iterable",
"ESNext"
],
"types": [
"vite/client",
"vike-react",
"vike-cloudflare/types"
],
"jsx": "react-jsx", "jsx": "react-jsx",
"jsxImportSource": "react" "jsxImportSource": "react"
}, },
"exclude": [ "exclude": ["dist"]
"dist"
]
} }
Loading…
Cancel
Save