fix nextjs linting hints and improve adapter to support config as function

This commit is contained in:
dswbx
2025-03-14 07:55:05 +01:00
parent 4f01004262
commit 1ea22248b4
7 changed files with 69 additions and 58 deletions

View File

@@ -1,15 +1,22 @@
import type { App } from "bknd"; import type { App } from "bknd";
import { type FrameworkBkndConfig, createFrameworkApp } from "bknd/adapter"; import { type FrameworkBkndConfig, createFrameworkApp } from "bknd/adapter";
import { getRuntimeKey, isNode } from "core/utils"; import { isNode } from "core/utils";
export type NextjsBkndConfig = FrameworkBkndConfig & { export type NextjsBkndConfig = FrameworkBkndConfig & {
cleanRequest?: { searchParams?: string[] }; cleanRequest?: { searchParams?: string[] };
}; };
type NextjsContext = {
env: Record<string, string | undefined>;
};
let app: App; let app: App;
let building: boolean = false; let building: boolean = false;
export async function getApp(config: NextjsBkndConfig) { export async function getApp<Args extends NextjsContext = NextjsContext>(
config: NextjsBkndConfig,
args?: Args,
) {
if (building) { if (building) {
while (building) { while (building) {
await new Promise((resolve) => setTimeout(resolve, 5)); await new Promise((resolve) => setTimeout(resolve, 5));
@@ -19,7 +26,7 @@ export async function getApp(config: NextjsBkndConfig) {
building = true; building = true;
if (!app) { if (!app) {
app = await createFrameworkApp(config); app = await createFrameworkApp(config, args);
await app.build(); await app.build();
} }
building = false; building = false;
@@ -52,7 +59,7 @@ function getCleanRequest(req: Request, cleanRequest: NextjsBkndConfig["cleanRequ
export function serve({ cleanRequest, ...config }: NextjsBkndConfig = {}) { export function serve({ cleanRequest, ...config }: NextjsBkndConfig = {}) {
return async (req: Request) => { return async (req: Request) => {
if (!app) { if (!app) {
app = await getApp(config); app = await getApp(config, { env: process.env ?? {} });
} }
const request = getCleanRequest(req, cleanRequest); const request = getCleanRequest(req, cleanRequest);
return app.fetch(request); return app.fetch(request);

View File

@@ -1,6 +1,5 @@
import Image from "next/image"; import Image from "next/image";
import type { ReactNode } from "react"; import { Footer } from "@/components/Footer";
import { Footer } from "./Footer";
export default async function Layout({ export default async function Layout({
children, children,
@@ -36,41 +35,3 @@ export default async function Layout({
</div> </div>
); );
} }
export const Buttons = () => (
<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Deploy now
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
href="https://docs.bknd.io/integration/nextjs"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
</div>
);
export const List = ({ items = [] }: { items: ReactNode[] }) => (
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
{items.map((item, i) => (
<li key={i} className={i < items.length - 1 ? "mb-2" : ""}>
{item}
</li>
))}
</ol>
);

View File

@@ -1,7 +1,7 @@
import { getApi } from "@/bknd"; import { getApi } from "@/bknd";
import { revalidatePath } from "next/cache"; import { revalidatePath } from "next/cache";
import { Fragment } from "react"; import { Fragment } from "react";
import { List } from "@/app/(main)/layout"; import { List } from "@/components/List";
export default async function Home() { export default async function Home() {
// without "{ verify: true }", this page can be static // without "{ verify: true }", this page can be static

View File

@@ -1,5 +1,7 @@
import { getApi } from "@/bknd"; import { getApi } from "@/bknd";
import { Buttons, List } from "../layout"; import { Buttons } from "@/components/Buttons";
import { List } from "@/components/List";
import Link from "next/link";
export default async function SSRPage() { export default async function SSRPage() {
const api = await getApi({ verify: true }); const api = await getApi({ verify: true });
@@ -11,21 +13,21 @@ export default async function SSRPage() {
<List items={data.map((todo) => todo.title)} /> <List items={data.map((todo) => todo.title)} />
<Buttons /> <Buttons />
<p> <div>
{user ? ( {user ? (
<> <>
Logged in as {user.email}.{" "} Logged in as {user.email}.{" "}
<a className="font-medium underline" href="/api/auth/logout"> <Link className="font-medium underline" href="/api/auth/logout">
Logout Logout
</a> </Link>
</> </>
) : ( ) : (
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<p> <p>
Not logged in.{" "} Not logged in.{" "}
<a className="font-medium underline" href="/admin/auth/login"> <Link className="font-medium underline" href="/admin/auth/login">
Login Login
</a> </Link>
</p> </p>
<p className="text-xs opacity-50"> <p className="text-xs opacity-50">
Sign in with:{" "} Sign in with:{" "}
@@ -39,7 +41,7 @@ export default async function SSRPage() {
</p> </p>
</div> </div>
)} )}
</p> </div>
</> </>
); );
} }

View File

@@ -0,0 +1,29 @@
import Image from "next/image";
export const Buttons = () => (
<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Deploy now
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
href="https://docs.bknd.io/integration/nextjs"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
</div>
);

View File

@@ -2,27 +2,28 @@
import Image from "next/image"; import Image from "next/image";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import Link from "next/link";
export function Footer() { export function Footer() {
const pathname = usePathname(); const pathname = usePathname();
return ( return (
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center"> <footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
<a <Link
className="flex items-center gap-2 hover:underline hover:underline-offset-4" className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href={pathname === "/" ? "/ssr" : "/"} href={pathname === "/" ? "/ssr" : "/"}
> >
<Image aria-hidden src="/file.svg" alt="File icon" width={16} height={16} /> <Image aria-hidden src="/file.svg" alt="File icon" width={16} height={16} />
{pathname === "/" ? "SSR" : "Home"} {pathname === "/" ? "SSR" : "Home"}
</a> </Link>
<a <Link
className="flex items-center gap-2 hover:underline hover:underline-offset-4" className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="/admin" href="/admin"
> >
<Image aria-hidden src="/window.svg" alt="Window icon" width={16} height={16} /> <Image aria-hidden src="/window.svg" alt="Window icon" width={16} height={16} />
Admin Admin
</a> </Link>
<a <Link
className="flex items-center gap-2 hover:underline hover:underline-offset-4" className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://bknd.io" href="https://bknd.io"
target="_blank" target="_blank"
@@ -30,7 +31,7 @@ export function Footer() {
> >
<Image aria-hidden src="/globe.svg" alt="Globe icon" width={16} height={16} /> <Image aria-hidden src="/globe.svg" alt="Globe icon" width={16} height={16} />
Go to bknd.io Go to bknd.io
</a> </Link>
</footer> </footer>
); );
} }

View File

@@ -0,0 +1,11 @@
import type { ReactNode } from "react";
export const List = ({ items = [] }: { items: ReactNode[] }) => (
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
{items.map((item, i) => (
<li key={i} className={i < items.length - 1 ? "mb-2" : ""}>
{item}
</li>
))}
</ol>
);