diff --git a/app/src/adapter/vite/dev-server-config.ts b/app/src/adapter/vite/dev-server-config.ts new file mode 100644 index 0000000..372e470 --- /dev/null +++ b/app/src/adapter/vite/dev-server-config.ts @@ -0,0 +1,14 @@ +export const devServerConfig = { + entry: "./server.ts", + exclude: [ + /.*\.tsx?($|\?)/, + /^(?!.*\/__admin).*\.(s?css|less)($|\?)/, + // exclude except /api + /^(?!.*\/api).*\.(ico|mp4|jpg|jpeg|svg|png|vtt|mp3|js)($|\?)/, + /^\/@.+$/, + /\/components.*?\.json.*/, // @todo: improve + /^\/(public|assets|static)\/.+/, + /^\/node_modules\/.*/ + ] as any, + injectClientScript: false +} as const; diff --git a/app/src/adapter/vite/vite.adapter.ts b/app/src/adapter/vite/vite.adapter.ts index 2f6b3e9..c8cb43d 100644 --- a/app/src/adapter/vite/vite.adapter.ts +++ b/app/src/adapter/vite/vite.adapter.ts @@ -2,8 +2,10 @@ import { serveStatic } from "@hono/node-server/serve-static"; import { type DevServerOptions, default as honoViteDevServer } from "@hono/vite-dev-server"; import { type RuntimeBkndConfig, createRuntimeApp } from "adapter"; import type { App } from "bknd"; +import { devServerConfig } from "./dev-server-config"; export type ViteBkndConfig = RuntimeBkndConfig & { + mode?: "cached" | "fresh"; setAdminHtml?: boolean; forceDev?: boolean | { mainPath: string }; html?: string; @@ -29,6 +31,7 @@ async function createApp(config: ViteBkndConfig = {}, env?: any) { return await createRuntimeApp( { ...config, + registerLocalMedia: true, adminOptions: config.setAdminHtml === false ? undefined @@ -44,7 +47,7 @@ async function createApp(config: ViteBkndConfig = {}, env?: any) { ); } -export function serveFresh(config: ViteBkndConfig = {}) { +export function serveFresh(config: Omit = {}) { return { async fetch(request: Request, env: any, ctx: ExecutionContext) { const app = await createApp(config, env); @@ -54,7 +57,7 @@ export function serveFresh(config: ViteBkndConfig = {}) { } let app: App; -export function serveCached(config: ViteBkndConfig = {}) { +export function serveCached(config: Omit = {}) { return { async fetch(request: Request, env: any, ctx: ExecutionContext) { if (!app) { @@ -66,20 +69,13 @@ export function serveCached(config: ViteBkndConfig = {}) { }; } +export function serve({ mode, ...config }: ViteBkndConfig = {}) { + return mode === "fresh" ? serveFresh(config) : serveCached(config); +} + export function devServer(options: DevServerOptions) { return honoViteDevServer({ - entry: "./server.ts", - exclude: [ - /.*\.tsx?($|\?)/, - /^(?!.*\/__admin).*\.(s?css|less)($|\?)/, - // exclude except /api - /^(?!.*\/api).*\.(ico|mp4|jpg|jpeg|svg|png|vtt|mp3|js)($|\?)/, - /^\/@.+$/, - /\/components.*?\.json.*/, // @todo: improve - /^\/(public|assets|static)\/.+/, - /^\/node_modules\/.*/ - ], - injectClientScript: false, + ...devServerConfig, ...options }); } diff --git a/app/vite.config.ts b/app/vite.config.ts index 1ec13c0..5580f35 100644 --- a/app/vite.config.ts +++ b/app/vite.config.ts @@ -1,48 +1,29 @@ import devServer from "@hono/vite-dev-server"; import react from "@vitejs/plugin-react"; -import { defineConfig, loadEnv } from "vite"; +import { defineConfig } from "vite"; import tsconfigPaths from "vite-tsconfig-paths"; +import { devServerConfig } from "./src/adapter/vite/dev-server-config"; // https://vitejs.dev/config/ -export default defineConfig(async () => { - /** - * DEVELOPMENT MODE - */ - if (process.env.NODE_ENV === "development") { - return { - define: { - __isDev: "1" - }, - clearScreen: false, - publicDir: "./src/admin/assets", - server: { - host: true, - port: 28623, - hmr: { - overlay: true - } - }, - plugins: [ - react(), - tsconfigPaths(), - devServer({ - entry: "./vite.dev.ts", - exclude: [ - // We need to override this option since the default setting doesn't fit - /.*\.tsx?($|\?)/, - /^(?!.*\/__admin).*\.(s?css|less)($|\?)/, - /^(?!.*\/api).*\.(svg|png)($|\?)/, // exclude except /api - /^\/@.+$/, - /^\/favicon\.ico$/, - /^\/(public|assets|static)\/.+/, - /^\/node_modules\/.*/ - ], - //injectClientScript: true - injectClientScript: false // This option is buggy, disable it and inject the code manually - }) - ] - }; - } - - throw new Error("Don't use vite for building in production"); +export default defineConfig({ + define: { + __isDev: "1" + }, + clearScreen: false, + publicDir: "./src/admin/assets", + server: { + host: true, + port: 28623, + hmr: { + overlay: true + } + }, + plugins: [ + react(), + tsconfigPaths(), + devServer({ + ...devServerConfig, + entry: "./vite.dev.ts" + }) + ] }); diff --git a/app/vite.dev.ts b/app/vite.dev.ts index 6050997..b48e450 100644 --- a/app/vite.dev.ts +++ b/app/vite.dev.ts @@ -1,10 +1,4 @@ -import { serveStatic } from "@hono/node-server/serve-static"; -import { createClient } from "@libsql/client/node"; -import { App, registries } from "./src"; -import { LibsqlConnection } from "./src/data"; -import { StorageLocalAdapter } from "./src/media/storage/adapters/StorageLocalAdapter"; - -registries.media.register("local", StorageLocalAdapter); +import { serve } from "./src/adapter/vite"; const credentials = { url: import.meta.env.VITE_DB_URL!, @@ -14,22 +8,10 @@ if (!credentials.url) { throw new Error("Missing VITE_DB_URL env variable. Add it to .env file"); } -const connection = new LibsqlConnection(createClient(credentials)); - -export default { - async fetch(request: Request) { - const app = App.create({ connection }); - - app.emgr.onEvent( - App.Events.AppBuiltEvent, - async () => { - app.registerAdminController({ forceDev: true }); - app.module.server.client.get("/assets/*", serveStatic({ root: "./" })); - }, - "sync" - ); - await app.build(); - - return app.fetch(request); - } -}; +export default serve({ + connection: { + type: "libsql", + config: credentials + }, + forceDev: true +}); diff --git a/docs/integration/vite.mdx b/docs/integration/vite.mdx new file mode 100644 index 0000000..749788b --- /dev/null +++ b/docs/integration/vite.mdx @@ -0,0 +1,116 @@ +--- +title: 'Vite' +description: 'Run bknd inside Vite' +--- +import InstallBknd from '/snippets/install-bknd.mdx'; + +Vite is a powerful toolkit to accelerate your local development. + +## Installation +Create a new vite project by following the [official guide](https://vite.dev/guide/#scaffolding-your-first-vite-project) +and then install bknd as a dependency: + + +Additionally, install required dependencies: +```bash +npm install @hono/vite-dev-server +``` + +## Serve the API +To serve the **bknd** API, you first have to create a local server file for you vite environment. +Create a `server.ts` file: +```ts +import { serve } from "bknd/adapter/vite"; + +// the configuration given is optional +export default serve({ + mode: "cached", // that's the default + connection: { + type: "libsql", + config: { + url: ":memory:" + } + } +}) +``` +For more information about the connection object, refer to the [Setup](/setup/introduction) guide. + +You can also run your vite server in `mode: "fresh"`, this will re-create the app on every fetch. +This is only useful for when working on the `bknd` repository directly. + +Next, adjust your `vite.config.ts` to look like the following: +```ts +import { devServer } from "bknd/adapter/vite"; +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; +import tsconfigPaths from "vite-tsconfig-paths"; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + react(), + tsconfigPaths(), + devServer({ + // point to your previously created server file + entry: "./server.ts" + }) + ] +}); +``` + +Now you can start your application using `npm run dev`. Now opening http://localhost:5174/ +looks like an empty project. That's because we only registered the API, head over to +http://localhost:5174/api/system/config to see **bknd** respond. + +## Serve the Admin UI +After adding the API, you can easily add the Admin UI by simply returning it in your `App.tsx`. +Replace all of its content with the following: + +```tsx +import { Admin } from "bknd/ui"; +import "bknd/dist/styles.css"; + +export default function App() { + return +} +``` + +Now http://localhost:5174/ should give you the Admin UI. + +## Customizations +This is just the bare minimum and may not always fulfill your requirements. There are a few +options you can make use of to adjust it according to your setup. + +### Use custom HTML to serve the Admin UI +There might be cases you want to be sure to be in control over the HTML that is being used. +`bknd` generates it automatically, but you use your own one as follows: + +```ts server.ts +import { serve, addViteScript } from "bknd/adapter/vite"; +import { readFile } from "node:fs/promises" + +let html = await readFile("./index.html", "utf-8"); + +// add vite scripts +html = addViteScript(html); + +// then add it as an option +export default serve({ html }) +``` + +The vite scripts has to be added manually currently, as adding them automatically with +`@hono/vite-dev-server` is buggy. This may change in the future. + +### Use a custom entry point +By default, the entry point `/src/main.tsx` is used and should fit most cases. If that's not you, +you can supply a different one like so: +```ts server.ts +import { serve } from "bknd/adapter/vite"; + +// the configuration given is optional +export default serve({ + forceDev: { + mainPath: "/src/special.tsx" + } +}); +``` \ No newline at end of file diff --git a/docs/introduction.mdx b/docs/introduction.mdx index fe65e6a..b568eb0 100644 --- a/docs/introduction.mdx +++ b/docs/introduction.mdx @@ -82,6 +82,15 @@ in the future, so stay tuned! } href="/integration/node" /> + + + + + } + href="/integration/vite" + /> diff --git a/docs/mint.json b/docs/mint.json index 1298f0f..e848206 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -89,6 +89,7 @@ "integration/astro", "integration/node", "integration/deno", + "integration/vite", "integration/docker" ] },