vite-elysia-forge 0.0.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 ADDED
@@ -0,0 +1,130 @@
1
+ # vite-elysia-forge
2
+
3
+ <svg width="200" height="120" viewBox="0 0 200 120" xmlns="http://www.w3.org/2000/svg">
4
+ <!-- Vite logo representation -->
5
+ <defs>
6
+ <linearGradient id="viteGradient" x1="0%" y1="0%" x2="100%" y2="100%">
7
+ <stop offset="0%" style="stop-color:#646cff;stop-opacity:1" />
8
+ <stop offset="100%" style="stop-color:#535bf2;stop-opacity:1" />
9
+ </linearGradient>
10
+ </defs>
11
+
12
+ <!-- Vite symbol -->
13
+ <polygon points="50,20 70,60 50,100 30,60" fill="url(#viteGradient)" />
14
+
15
+ <!-- Arrow connecting to Elysia -->
16
+ <line x1="80" y1="60" x2="120" y2="60" stroke="#646cff" stroke-width="3" marker-end="url(#arrowhead)" />
17
+ <defs>
18
+ <marker id="arrowhead" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
19
+ <polygon points="0 0, 10 3, 0 6" fill="#646cff" />
20
+ </marker>
21
+ </defs>
22
+
23
+ <!-- Elysia symbol (simplified) -->
24
+ <circle cx="150" cy="30" r="15" fill="#e0db55" />
25
+ <circle cx="150" cy="90" r="15" fill="#e0db55" />
26
+ <rect x="135" y="50" width="30" height="20" fill="#e0db55" />
27
+ </svg>
28
+
29
+ Vite middleware plugin that hot-reloads an Elysia API module and forwards `/api` requests to it during local development.
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ bun install
35
+ ```
36
+
37
+ ## Quick Start
38
+
39
+ 1. Place your Elysia handler at `src/server/api.ts` (default path).
40
+ 2. In your Vite config, register the plugin:
41
+
42
+ ```ts
43
+ import { defineConfig } from "vite";
44
+ import elysiaPlugin from "vite-elysia-forge";
45
+
46
+ export default defineConfig({
47
+ plugins: [
48
+ elysiaPlugin({
49
+ serverFile: "/server/api.ts",
50
+ }),
51
+ ],
52
+ });
53
+ ```
54
+
55
+ 3. Run Vite as usual, and hit `/api/*` routes. The plugin will reload the Elysia module when the file changes.
56
+
57
+ ## Starting the App
58
+
59
+ To build and start the app:
60
+
61
+ ```bash
62
+ bun run build
63
+ bun start
64
+ ```
65
+
66
+ `bun run build` compiles the plugin for production, and `bun start` runs the built application.
67
+
68
+ ## Configuration
69
+
70
+ | Option | Type | Default | Description |
71
+ | ------------ | ------ | ------------ | ------------------------------------------------------- |
72
+ | `serverURL` | string | `"/server/"` | URL prefix to your API module (leading slash required). |
73
+ | `serverFile` | string | `"api.ts"` | Filename of the Elysia module to load and hot-reload. |
74
+
75
+ ## Expectations for the API module
76
+
77
+ Your API module should export `api` with a `handle(request: Request) => Promise<Response>` signature.
78
+
79
+ ```ts
80
+ export const api = new Elysia({
81
+ prefix: "/api",
82
+ });
83
+
84
+ api.get("/", () => "hello from elysia");
85
+
86
+ export default api;
87
+ ```
88
+
89
+ ## Production Usage
90
+
91
+ When building for production, you need to build both the Vite frontend and the Elysia backend. This package provides a CLI to handle this for you.
92
+
93
+ 1. Update your `package.json` build script:
94
+
95
+ ```json
96
+ {
97
+ "scripts": {
98
+ "build": "vite-elysia-forge build",
99
+ "start": "bun dist/server.js"
100
+ }
101
+ }
102
+ ```
103
+
104
+ 2. Run the build:
105
+
106
+ ```bash
107
+ bun run build
108
+ ```
109
+
110
+ This will:
111
+
112
+ 1. Run `vite build` to compile your frontend to `dist/`.
113
+ 2. Automatically generate a temporary entry file that imports your API from `src/server/api.ts` (default).
114
+ 3. Bundle the server into a single file at `dist/server.js`.
115
+
116
+ If your API is located elsewhere, you can specify the path:
117
+
118
+ ```bash
119
+ vite-elysia-forge build src/my-api.ts
120
+ ```
121
+
122
+ 3. Start the server:
123
+
124
+ ```bash
125
+ bun start
126
+ ```
127
+
128
+ ## Authors
129
+
130
+ - Chijioke Udokporo ([@chijiokeudokporo](https://github.com/chijioke-udokporo))
package/dist/cli.cjs ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env bun
2
+ 'use strict';var child_process=require('child_process'),fs=require('fs'),path=require('path');async function g(o="src/server/api.ts"){let n=path.resolve(process.cwd(),o);fs.existsSync(n)||(console.error(`\u274C API entry file "${o}" not found.`),console.error(' By default, vite-elysia-forge looks for "src/server/api.ts".'),console.error(" If your API is located elsewhere, please specify the path:"),console.error(" $ vite-elysia-forge build <path-to-your-api-file>"),process.exit(1)),console.log("\u{1F4E6} Building Vite app...");let r=child_process.spawnSync("bun",["x","vite","build"],{stdio:"inherit",env:{...process.env,NODE_ENV:"production"}});r.status!==0&&(console.error("\u274C Vite build failed"),process.exit(r.status||1)),console.log("\u{1F95F} Building Elysia server for Bun...");let s=path.resolve(process.cwd(),".output");fs.existsSync(s)||fs.mkdirSync(s,{recursive:true});let t=path.resolve(s,".temp-prod.ts"),e=path.relative(s,n);e=e.split(path.sep).join("/"),e.startsWith(".")||(e="./"+e);let u=`
3
+ import { startServer } from "@chijioke-udokporo/vite-elysia-forge/production";
4
+ import { api } from "${e}";
5
+
6
+ startServer({
7
+ api,
8
+ port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
9
+ distDir: "dist",
10
+ });
11
+ `;fs.writeFileSync(t,u);try{let i=await Bun.build({entrypoints:[t],outdir:"dist",target:"bun",minify:!0,naming:"server.js"});if(!i.success){console.error("\u274C Server build failed");for(let a of i.logs)console.error(a);process.exit(1);}}catch(i){console.error("\u274C Failed to build server. Ensure you are running this command with Bun."),console.error(i),process.exit(1);}finally{fs.existsSync(t)&&fs.unlinkSync(t);}console.log("\u2705 Build complete!"),console.log(" Run your production server with:"),console.log(" $ bun dist/server.js");}if(undefined){let o=process.argv.slice(2);if(o[0]==="build"){let r=o[1];g(r);}else console.log("Usage: vite-elysia-forge build [api-entry]"),console.log(" api-entry: Path to your API entry file (default: src/server/api.ts)");}exports.build=g;//# sourceMappingURL=cli.cjs.map
12
+ //# sourceMappingURL=cli.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts"],"names":["build","apiEntry","absoluteApiEntry","resolve","existsSync","viteBuild","spawnSync","outputDir","mkdirSync","tempEntry","relativeApiEntry","relative","sep","tempContent","writeFileSync","result","log","e","unlinkSync","args","entry"],"mappings":"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","file":"cli.cjs","sourcesContent":["#!/usr/bin/env bun\nimport { spawnSync } from \"node:child_process\";\nimport { existsSync, writeFileSync, unlinkSync, mkdirSync } from \"node:fs\";\nimport { resolve, relative, sep } from \"node:path\";\n\nexport async function build(apiEntry: string = \"src/server/api.ts\") {\n const absoluteApiEntry = resolve(process.cwd(), apiEntry);\n\n if (!existsSync(absoluteApiEntry)) {\n console.error(`❌ API entry file \"${apiEntry}\" not found.`);\n console.error(` By default, vite-elysia-forge looks for \"src/server/api.ts\".`);\n console.error(` If your API is located elsewhere, please specify the path:`);\n console.error(` $ vite-elysia-forge build <path-to-your-api-file>`);\n process.exit(1);\n }\n\n console.log(\"📦 Building Vite app...\");\n // Run vite build\n const viteBuild = spawnSync(\"bun\", [\"x\", \"vite\", \"build\"], {\n stdio: \"inherit\",\n env: { ...process.env, NODE_ENV: \"production\" },\n });\n\n if (viteBuild.status !== 0) {\n console.error(\"❌ Vite build failed\");\n process.exit(viteBuild.status || 1);\n }\n\n console.log(\"🥟 Building Elysia server for Bun...\");\n\n // Create a temporary entry file\n const outputDir = resolve(process.cwd(), \".output\");\n if (!existsSync(outputDir)) {\n mkdirSync(outputDir, { recursive: true });\n }\n const tempEntry = resolve(outputDir, \".temp-prod.ts\");\n\n // Calculate relative path from outputDir to api entry\n let relativeApiEntry = relative(outputDir, absoluteApiEntry);\n // Normalize path separators for imports (Windows support)\n relativeApiEntry = relativeApiEntry.split(sep).join(\"/\");\n if (!relativeApiEntry.startsWith(\".\")) {\n relativeApiEntry = \"./\" + relativeApiEntry;\n }\n\n const tempContent = `\nimport { startServer } from \"@chijioke-udokporo/vite-elysia-forge/production\";\nimport { api } from \"${relativeApiEntry}\";\n\nstartServer({\n api,\n port: process.env.PORT ? parseInt(process.env.PORT) : 3000,\n distDir: \"dist\",\n});\n`;\n\n writeFileSync(tempEntry, tempContent);\n\n // We use Bun.build to bundle the server\n // This requires the script to be run with Bun\n try {\n const result = await Bun.build({\n entrypoints: [tempEntry],\n outdir: \"dist\", // Output to dist alongside vite assets\n target: \"bun\",\n minify: true,\n naming: \"server.js\", // Fixed name for simplicity\n });\n\n if (!result.success) {\n console.error(\"❌ Server build failed\");\n for (const log of result.logs) {\n console.error(log);\n }\n process.exit(1);\n }\n } catch (e) {\n console.error(\"❌ Failed to build server. Ensure you are running this command with Bun.\");\n console.error(e);\n process.exit(1);\n } finally {\n // Clean up temp file\n if (existsSync(tempEntry)) {\n unlinkSync(tempEntry);\n }\n }\n\n console.log(\"✅ Build complete!\");\n console.log(\" Run your production server with:\");\n console.log(\" $ bun dist/server.js\");\n}\n\nif (import.meta.main) {\n const args = process.argv.slice(2);\n const command = args[0];\n\n if (command === \"build\") {\n const entry = args[1];\n build(entry);\n } else {\n console.log(\"Usage: vite-elysia-forge build [api-entry]\");\n console.log(\" api-entry: Path to your API entry file (default: src/server/api.ts)\");\n }\n}\n"]}
package/dist/cli.d.cts ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bun
2
+ declare function build(apiEntry?: string): Promise<void>;
3
+
4
+ export { build };
package/dist/cli.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bun
2
+ declare function build(apiEntry?: string): Promise<void>;
3
+
4
+ export { build };
package/dist/cli.js ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env bun
2
+ import {spawnSync}from'child_process';import {existsSync,mkdirSync,writeFileSync,unlinkSync}from'fs';import {resolve,relative,sep}from'path';async function g(o="src/server/api.ts"){let n=resolve(process.cwd(),o);existsSync(n)||(console.error(`\u274C API entry file "${o}" not found.`),console.error(' By default, vite-elysia-forge looks for "src/server/api.ts".'),console.error(" If your API is located elsewhere, please specify the path:"),console.error(" $ vite-elysia-forge build <path-to-your-api-file>"),process.exit(1)),console.log("\u{1F4E6} Building Vite app...");let r=spawnSync("bun",["x","vite","build"],{stdio:"inherit",env:{...process.env,NODE_ENV:"production"}});r.status!==0&&(console.error("\u274C Vite build failed"),process.exit(r.status||1)),console.log("\u{1F95F} Building Elysia server for Bun...");let s=resolve(process.cwd(),".output");existsSync(s)||mkdirSync(s,{recursive:true});let t=resolve(s,".temp-prod.ts"),e=relative(s,n);e=e.split(sep).join("/"),e.startsWith(".")||(e="./"+e);let u=`
3
+ import { startServer } from "@chijioke-udokporo/vite-elysia-forge/production";
4
+ import { api } from "${e}";
5
+
6
+ startServer({
7
+ api,
8
+ port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
9
+ distDir: "dist",
10
+ });
11
+ `;writeFileSync(t,u);try{let i=await Bun.build({entrypoints:[t],outdir:"dist",target:"bun",minify:!0,naming:"server.js"});if(!i.success){console.error("\u274C Server build failed");for(let a of i.logs)console.error(a);process.exit(1);}}catch(i){console.error("\u274C Failed to build server. Ensure you are running this command with Bun."),console.error(i),process.exit(1);}finally{existsSync(t)&&unlinkSync(t);}console.log("\u2705 Build complete!"),console.log(" Run your production server with:"),console.log(" $ bun dist/server.js");}if(import.meta.main){let o=process.argv.slice(2);if(o[0]==="build"){let r=o[1];g(r);}else console.log("Usage: vite-elysia-forge build [api-entry]"),console.log(" api-entry: Path to your API entry file (default: src/server/api.ts)");}export{g as build};//# sourceMappingURL=cli.js.map
12
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts"],"names":["build","apiEntry","absoluteApiEntry","resolve","existsSync","viteBuild","spawnSync","outputDir","mkdirSync","tempEntry","relativeApiEntry","relative","sep","tempContent","writeFileSync","result","log","e","unlinkSync","args","entry"],"mappings":"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","file":"cli.js","sourcesContent":["#!/usr/bin/env bun\nimport { spawnSync } from \"node:child_process\";\nimport { existsSync, writeFileSync, unlinkSync, mkdirSync } from \"node:fs\";\nimport { resolve, relative, sep } from \"node:path\";\n\nexport async function build(apiEntry: string = \"src/server/api.ts\") {\n const absoluteApiEntry = resolve(process.cwd(), apiEntry);\n\n if (!existsSync(absoluteApiEntry)) {\n console.error(`❌ API entry file \"${apiEntry}\" not found.`);\n console.error(` By default, vite-elysia-forge looks for \"src/server/api.ts\".`);\n console.error(` If your API is located elsewhere, please specify the path:`);\n console.error(` $ vite-elysia-forge build <path-to-your-api-file>`);\n process.exit(1);\n }\n\n console.log(\"📦 Building Vite app...\");\n // Run vite build\n const viteBuild = spawnSync(\"bun\", [\"x\", \"vite\", \"build\"], {\n stdio: \"inherit\",\n env: { ...process.env, NODE_ENV: \"production\" },\n });\n\n if (viteBuild.status !== 0) {\n console.error(\"❌ Vite build failed\");\n process.exit(viteBuild.status || 1);\n }\n\n console.log(\"🥟 Building Elysia server for Bun...\");\n\n // Create a temporary entry file\n const outputDir = resolve(process.cwd(), \".output\");\n if (!existsSync(outputDir)) {\n mkdirSync(outputDir, { recursive: true });\n }\n const tempEntry = resolve(outputDir, \".temp-prod.ts\");\n\n // Calculate relative path from outputDir to api entry\n let relativeApiEntry = relative(outputDir, absoluteApiEntry);\n // Normalize path separators for imports (Windows support)\n relativeApiEntry = relativeApiEntry.split(sep).join(\"/\");\n if (!relativeApiEntry.startsWith(\".\")) {\n relativeApiEntry = \"./\" + relativeApiEntry;\n }\n\n const tempContent = `\nimport { startServer } from \"@chijioke-udokporo/vite-elysia-forge/production\";\nimport { api } from \"${relativeApiEntry}\";\n\nstartServer({\n api,\n port: process.env.PORT ? parseInt(process.env.PORT) : 3000,\n distDir: \"dist\",\n});\n`;\n\n writeFileSync(tempEntry, tempContent);\n\n // We use Bun.build to bundle the server\n // This requires the script to be run with Bun\n try {\n const result = await Bun.build({\n entrypoints: [tempEntry],\n outdir: \"dist\", // Output to dist alongside vite assets\n target: \"bun\",\n minify: true,\n naming: \"server.js\", // Fixed name for simplicity\n });\n\n if (!result.success) {\n console.error(\"❌ Server build failed\");\n for (const log of result.logs) {\n console.error(log);\n }\n process.exit(1);\n }\n } catch (e) {\n console.error(\"❌ Failed to build server. Ensure you are running this command with Bun.\");\n console.error(e);\n process.exit(1);\n } finally {\n // Clean up temp file\n if (existsSync(tempEntry)) {\n unlinkSync(tempEntry);\n }\n }\n\n console.log(\"✅ Build complete!\");\n console.log(\" Run your production server with:\");\n console.log(\" $ bun dist/server.js\");\n}\n\nif (import.meta.main) {\n const args = process.argv.slice(2);\n const command = args[0];\n\n if (command === \"build\") {\n const entry = args[1];\n build(entry);\n } else {\n console.log(\"Usage: vite-elysia-forge build [api-entry]\");\n console.log(\" api-entry: Path to your API entry file (default: src/server/api.ts)\");\n }\n}\n"]}
package/dist/index.cjs ADDED
@@ -0,0 +1,3 @@
1
+ 'use strict';var path=require('path'),P=require('pino');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var P__default=/*#__PURE__*/_interopDefault(P);var M=process.env.NODE_ENV==="production",f=P__default.default({name:"vite-elysia-forge",transport:M?void 0:{target:"pino-pretty",options:{colorize:true}}});function B({serverFile:m="/server/api.ts"}){return {name:"vite-elysia-forge",async configureServer(n){let c=m,y=path.resolve(n.config.root,c.slice(1)),p=async()=>(await n.ssrLoadModule(c)).api,g=await p();n.watcher.add(y),n.watcher.on("change",async e=>{let o=await n.moduleGraph.getModuleByUrl(c);if(!o)return;let r=n.moduleGraph.getModulesByFile(e);if(!r||r.size===0)return;let i=false,a=new Set,s=[...r];for(;s.length>0;){let t=s.shift();if(!(!t||!t.id||a.has(t.id))){if(a.add(t.id),t.id===o.id){i=true;break}for(let l of t.importers)s.push(l);}}if(i)try{n.moduleGraph.invalidateModule(o),g=await p(),f.info("Reloaded Elysia API module");}catch(t){f.error(`Failed to reload Elysia API: ${t}`);}}),n.middlewares.use(async(e,o,r)=>{if(!e.url?.startsWith("/api"))return r();try{let i="http",a=e.headers.host||"localhost:3000",s=`${i}://${a}${e.url}`,t;if(e.method!=="GET"&&e.method!=="HEAD"){let d=[];for await(let h of e)d.push(h);t=Buffer.concat(d).toString();}let l=new Request(s,{method:e.method,headers:e.headers,body:t}),u=await g.handle(l);o.statusCode=u.status,u.headers.forEach((d,h)=>{o.setHeader(h,d);});let w=await u.text();o.end(w);}catch(i){f.error(`Elysia error: ${i}`),o.statusCode=500,o.end("Internal Server Error");}});}}}var A=B;
2
+ module.exports=A;//# sourceMappingURL=index.cjs.map
3
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["isProduction","logger","Pino","elysiaPlugin","serverFile","server","apiModulePath","apiFile","resolve","loadApi","api","file","entryMod","changedMods","isDependency","seen","queue","node","importer","error","req","res","next","protocol","host","url","body","chunks","chunk","request","response","value","key","responseBody","index_default"],"mappings":"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","file":"index.cjs","sourcesContent":["import { resolve } from \"node:path\";\nimport Pino from \"pino\";\nimport type { Plugin, ModuleNode } from \"vite\";\n\nconst isProduction = process.env.NODE_ENV === \"production\";\n\nconst logger = Pino({\n name: \"vite-elysia-forge\",\n transport: isProduction\n ? undefined\n : {\n target: \"pino-pretty\",\n options: { colorize: true },\n },\n});\n\ninterface ConfigOptions {\n /**\n * The URL path to the server API module.\n * @default \"/server/api.ts\"\n */\n\n serverFile?: string;\n}\n\nfunction elysiaPlugin({ serverFile = \"/server/api.ts\" }: ConfigOptions): Plugin {\n return {\n name: \"vite-elysia-forge\",\n async configureServer(server) {\n const apiModulePath = serverFile;\n const apiFile = resolve(server.config.root, apiModulePath.slice(1));\n\n const loadApi = async () => {\n const mod = await server.ssrLoadModule(apiModulePath);\n return mod.api as { handle: (request: Request) => Promise<Response> };\n };\n\n let api = await loadApi();\n\n server.watcher.add(apiFile);\n server.watcher.on(\"change\", async (file) => {\n const entryMod = await server.moduleGraph.getModuleByUrl(apiModulePath);\n if (!entryMod) return;\n\n const changedMods = server.moduleGraph.getModulesByFile(file);\n if (!changedMods || changedMods.size === 0) return;\n\n let isDependency = false;\n const seen = new Set<string>();\n const queue = [...changedMods];\n\n while (queue.length > 0) {\n const node = queue.shift();\n if (!node || !node.id || seen.has(node.id)) continue;\n seen.add(node.id);\n\n if (node.id === entryMod.id) {\n isDependency = true;\n break;\n }\n\n for (const importer of node.importers) {\n queue.push(importer);\n }\n }\n\n if (!isDependency) return;\n\n try {\n server.moduleGraph.invalidateModule(entryMod);\n api = await loadApi();\n logger.info(\"Reloaded Elysia API module\");\n } catch (error) {\n logger.error(`Failed to reload Elysia API: ${error}`);\n }\n });\n\n server.middlewares.use(async (req, res, next) => {\n // Only handle /api routes\n if (!req.url?.startsWith(\"/api\")) {\n return next();\n }\n\n try {\n // Build the full URL\n const protocol = \"http\";\n const host = req.headers.host || \"localhost:3000\";\n const url = `${protocol}://${host}${req.url}`;\n\n // Collect body for non-GET requests\n let body: string | undefined;\n if (req.method !== \"GET\" && req.method !== \"HEAD\") {\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(chunk);\n }\n body = Buffer.concat(chunks).toString();\n }\n\n // Create a Request object for Elysia\n const request = new Request(url, {\n method: req.method,\n headers: req.headers as Record<string, string>,\n body: body,\n });\n\n // Handle with Elysia\n const response = await api.handle(request);\n\n // Send response\n res.statusCode = response.status;\n response.headers.forEach((value: string, key: string) => {\n res.setHeader(key, value);\n });\n\n const responseBody = await response.text();\n res.end(responseBody);\n } catch (error) {\n logger.error(`Elysia error: ${error}`);\n res.statusCode = 500;\n res.end(\"Internal Server Error\");\n }\n });\n },\n };\n}\n\nexport default elysiaPlugin;\n"]}
@@ -0,0 +1,12 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ interface ConfigOptions {
4
+ /**
5
+ * The URL path to the server API module.
6
+ * @default "/server/api.ts"
7
+ */
8
+ serverFile?: string;
9
+ }
10
+ declare function elysiaPlugin({ serverFile }: ConfigOptions): Plugin;
11
+
12
+ export { elysiaPlugin as default };
@@ -0,0 +1,12 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ interface ConfigOptions {
4
+ /**
5
+ * The URL path to the server API module.
6
+ * @default "/server/api.ts"
7
+ */
8
+ serverFile?: string;
9
+ }
10
+ declare function elysiaPlugin({ serverFile }: ConfigOptions): Plugin;
11
+
12
+ export { elysiaPlugin as default };
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ import {resolve}from'path';import P from'pino';var M=process.env.NODE_ENV==="production",f=P({name:"vite-elysia-forge",transport:M?void 0:{target:"pino-pretty",options:{colorize:true}}});function B({serverFile:m="/server/api.ts"}){return {name:"vite-elysia-forge",async configureServer(n){let c=m,y=resolve(n.config.root,c.slice(1)),p=async()=>(await n.ssrLoadModule(c)).api,g=await p();n.watcher.add(y),n.watcher.on("change",async e=>{let o=await n.moduleGraph.getModuleByUrl(c);if(!o)return;let r=n.moduleGraph.getModulesByFile(e);if(!r||r.size===0)return;let i=false,a=new Set,s=[...r];for(;s.length>0;){let t=s.shift();if(!(!t||!t.id||a.has(t.id))){if(a.add(t.id),t.id===o.id){i=true;break}for(let l of t.importers)s.push(l);}}if(i)try{n.moduleGraph.invalidateModule(o),g=await p(),f.info("Reloaded Elysia API module");}catch(t){f.error(`Failed to reload Elysia API: ${t}`);}}),n.middlewares.use(async(e,o,r)=>{if(!e.url?.startsWith("/api"))return r();try{let i="http",a=e.headers.host||"localhost:3000",s=`${i}://${a}${e.url}`,t;if(e.method!=="GET"&&e.method!=="HEAD"){let d=[];for await(let h of e)d.push(h);t=Buffer.concat(d).toString();}let l=new Request(s,{method:e.method,headers:e.headers,body:t}),u=await g.handle(l);o.statusCode=u.status,u.headers.forEach((d,h)=>{o.setHeader(h,d);});let w=await u.text();o.end(w);}catch(i){f.error(`Elysia error: ${i}`),o.statusCode=500,o.end("Internal Server Error");}});}}}var A=B;
2
+ export{A as default};//# sourceMappingURL=index.js.map
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":["isProduction","logger","Pino","elysiaPlugin","serverFile","server","apiModulePath","apiFile","resolve","loadApi","api","file","entryMod","changedMods","isDependency","seen","queue","node","importer","error","req","res","next","protocol","host","url","body","chunks","chunk","request","response","value","key","responseBody","index_default"],"mappings":"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","file":"index.js","sourcesContent":["import { resolve } from \"node:path\";\nimport Pino from \"pino\";\nimport type { Plugin, ModuleNode } from \"vite\";\n\nconst isProduction = process.env.NODE_ENV === \"production\";\n\nconst logger = Pino({\n name: \"vite-elysia-forge\",\n transport: isProduction\n ? undefined\n : {\n target: \"pino-pretty\",\n options: { colorize: true },\n },\n});\n\ninterface ConfigOptions {\n /**\n * The URL path to the server API module.\n * @default \"/server/api.ts\"\n */\n\n serverFile?: string;\n}\n\nfunction elysiaPlugin({ serverFile = \"/server/api.ts\" }: ConfigOptions): Plugin {\n return {\n name: \"vite-elysia-forge\",\n async configureServer(server) {\n const apiModulePath = serverFile;\n const apiFile = resolve(server.config.root, apiModulePath.slice(1));\n\n const loadApi = async () => {\n const mod = await server.ssrLoadModule(apiModulePath);\n return mod.api as { handle: (request: Request) => Promise<Response> };\n };\n\n let api = await loadApi();\n\n server.watcher.add(apiFile);\n server.watcher.on(\"change\", async (file) => {\n const entryMod = await server.moduleGraph.getModuleByUrl(apiModulePath);\n if (!entryMod) return;\n\n const changedMods = server.moduleGraph.getModulesByFile(file);\n if (!changedMods || changedMods.size === 0) return;\n\n let isDependency = false;\n const seen = new Set<string>();\n const queue = [...changedMods];\n\n while (queue.length > 0) {\n const node = queue.shift();\n if (!node || !node.id || seen.has(node.id)) continue;\n seen.add(node.id);\n\n if (node.id === entryMod.id) {\n isDependency = true;\n break;\n }\n\n for (const importer of node.importers) {\n queue.push(importer);\n }\n }\n\n if (!isDependency) return;\n\n try {\n server.moduleGraph.invalidateModule(entryMod);\n api = await loadApi();\n logger.info(\"Reloaded Elysia API module\");\n } catch (error) {\n logger.error(`Failed to reload Elysia API: ${error}`);\n }\n });\n\n server.middlewares.use(async (req, res, next) => {\n // Only handle /api routes\n if (!req.url?.startsWith(\"/api\")) {\n return next();\n }\n\n try {\n // Build the full URL\n const protocol = \"http\";\n const host = req.headers.host || \"localhost:3000\";\n const url = `${protocol}://${host}${req.url}`;\n\n // Collect body for non-GET requests\n let body: string | undefined;\n if (req.method !== \"GET\" && req.method !== \"HEAD\") {\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(chunk);\n }\n body = Buffer.concat(chunks).toString();\n }\n\n // Create a Request object for Elysia\n const request = new Request(url, {\n method: req.method,\n headers: req.headers as Record<string, string>,\n body: body,\n });\n\n // Handle with Elysia\n const response = await api.handle(request);\n\n // Send response\n res.statusCode = response.status;\n response.headers.forEach((value: string, key: string) => {\n res.setHeader(key, value);\n });\n\n const responseBody = await response.text();\n res.end(responseBody);\n } catch (error) {\n logger.error(`Elysia error: ${error}`);\n res.statusCode = 500;\n res.end(\"Internal Server Error\");\n }\n });\n },\n };\n}\n\nexport default elysiaPlugin;\n"]}
@@ -0,0 +1,3 @@
1
+ 'use strict';var path=require('path');var f=t=>{let i=t.port||3e3,r=path.resolve(process.cwd(),t.distDir||"dist"),u=path.join(r,t.htmlFile||"index.html"),l=t.apiPrefix||"/api",c=t.api;if(typeof Bun>"u")throw new Error("This production server utility requires Bun.");Bun.serve({port:i,async fetch(n){let s=new URL(n.url);if(s.pathname.startsWith(l))return c.handle(n);let e=s.pathname;e==="/"&&(e="/index.html");let p=path.join(r,e.startsWith("/")?e.slice(1):e),o=Bun.file(p);return await o.exists()?new Response(o):n.method==="GET"?new Response(Bun.file(u)):new Response("Not Found",{status:404})}}),console.log(`Production server running at http://localhost:${i}`);};
2
+ exports.startServer=f;//# sourceMappingURL=production.cjs.map
3
+ //# sourceMappingURL=production.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/production.ts"],"names":["startServer","options","port","dist","resolve","indexHtml","join","apiPrefix","api","req","url","path","filePath","file"],"mappings":"sCAUO,IAAMA,CAAAA,CAAeC,CAAAA,EAA+B,CACzD,IAAMC,CAAAA,CAAOD,CAAAA,CAAQ,MAAQ,GAAA,CACvBE,CAAAA,CAAOC,YAAAA,CAAQ,OAAA,CAAQ,GAAA,EAAI,CAAGH,CAAAA,CAAQ,OAAA,EAAW,MAAM,CAAA,CACvDI,CAAAA,CAAYC,SAAAA,CAAKH,CAAAA,CAAMF,CAAAA,CAAQ,QAAA,EAAY,YAAY,EACvDM,CAAAA,CAAYN,CAAAA,CAAQ,SAAA,EAAa,MAAA,CACjCO,CAAAA,CAAMP,CAAAA,CAAQ,GAAA,CAEpB,GAAI,OAAO,GAAA,CAAQ,GAAA,CACjB,MAAM,IAAI,KAAA,CAAM,8CAA8C,CAAA,CAGhE,IAAI,KAAA,CAAM,CACR,IAAA,CAAAC,CAAAA,CACA,MAAM,KAAA,CAAMO,CAAAA,CAAK,CACf,IAAMC,CAAAA,CAAM,IAAI,GAAA,CAAID,CAAAA,CAAI,GAAG,CAAA,CAG3B,GAAIC,CAAAA,CAAI,SAAS,UAAA,CAAWH,CAAS,CAAA,CACnC,OAAOC,CAAAA,CAAI,MAAA,CAAOC,CAAG,CAAA,CAIvB,IAAIE,CAAAA,CAAOD,CAAAA,CAAI,QAAA,CACXC,CAAAA,GAAS,GAAA,GAAKA,CAAAA,CAAO,aAAA,CAAA,CAEzB,IAAMC,CAAAA,CAAWN,SAAAA,CAAKH,CAAAA,CAAMQ,CAAAA,CAAK,UAAA,CAAW,GAAG,CAAA,CAAIA,CAAAA,CAAK,MAAM,CAAC,CAAA,CAAIA,CAAI,CAAA,CACjEE,CAAAA,CAAO,GAAA,CAAI,IAAA,CAAKD,CAAQ,EAE9B,OAAI,MAAMC,CAAAA,CAAK,MAAA,EAAO,CACb,IAAI,QAAA,CAASA,CAAI,CAAA,CAItBJ,CAAAA,CAAI,MAAA,GAAW,KAAA,CACV,IAAI,QAAA,CAAS,GAAA,CAAI,IAAA,CAAKJ,CAAS,CAAC,CAAA,CAGlC,IAAI,QAAA,CAAS,WAAA,CAAa,CAAE,MAAA,CAAQ,GAAI,CAAC,CAClD,CACF,CAAC,CAAA,CAED,OAAA,CAAQ,GAAA,CAAI,CAAA,8CAAA,EAAiDH,CAAI,EAAE,EACrE","file":"production.cjs","sourcesContent":["import { resolve, join } from \"node:path\";\n\ninterface ProductionOptions {\n port?: number;\n distDir?: string;\n htmlFile?: string;\n apiPrefix?: string;\n api: { handle: (request: Request) => Promise<Response> } | any;\n}\n\nexport const startServer = (options: ProductionOptions) => {\n const port = options.port || 3000;\n const dist = resolve(process.cwd(), options.distDir || \"dist\");\n const indexHtml = join(dist, options.htmlFile || \"index.html\");\n const apiPrefix = options.apiPrefix || \"/api\";\n const api = options.api;\n\n if (typeof Bun === \"undefined\") {\n throw new Error(\"This production server utility requires Bun.\");\n }\n\n Bun.serve({\n port,\n async fetch(req) {\n const url = new URL(req.url);\n\n // 1. Handle API requests\n if (url.pathname.startsWith(apiPrefix)) {\n return api.handle(req);\n }\n\n // 2. Serve static files\n let path = url.pathname;\n if (path === \"/\") path = \"/index.html\";\n\n const filePath = join(dist, path.startsWith(\"/\") ? path.slice(1) : path);\n const file = Bun.file(filePath);\n\n if (await file.exists()) {\n return new Response(file);\n }\n\n // 3. SPA Fallback\n if (req.method === \"GET\") {\n return new Response(Bun.file(indexHtml));\n }\n\n return new Response(\"Not Found\", { status: 404 });\n },\n });\n\n console.log(`Production server running at http://localhost:${port}`);\n};\n"]}
@@ -0,0 +1,12 @@
1
+ interface ProductionOptions {
2
+ port?: number;
3
+ distDir?: string;
4
+ htmlFile?: string;
5
+ apiPrefix?: string;
6
+ api: {
7
+ handle: (request: Request) => Promise<Response>;
8
+ } | any;
9
+ }
10
+ declare const startServer: (options: ProductionOptions) => void;
11
+
12
+ export { startServer };
@@ -0,0 +1,12 @@
1
+ interface ProductionOptions {
2
+ port?: number;
3
+ distDir?: string;
4
+ htmlFile?: string;
5
+ apiPrefix?: string;
6
+ api: {
7
+ handle: (request: Request) => Promise<Response>;
8
+ } | any;
9
+ }
10
+ declare const startServer: (options: ProductionOptions) => void;
11
+
12
+ export { startServer };
@@ -0,0 +1,3 @@
1
+ import {resolve,join}from'path';var f=t=>{let i=t.port||3e3,r=resolve(process.cwd(),t.distDir||"dist"),u=join(r,t.htmlFile||"index.html"),l=t.apiPrefix||"/api",c=t.api;if(typeof Bun>"u")throw new Error("This production server utility requires Bun.");Bun.serve({port:i,async fetch(n){let s=new URL(n.url);if(s.pathname.startsWith(l))return c.handle(n);let e=s.pathname;e==="/"&&(e="/index.html");let p=join(r,e.startsWith("/")?e.slice(1):e),o=Bun.file(p);return await o.exists()?new Response(o):n.method==="GET"?new Response(Bun.file(u)):new Response("Not Found",{status:404})}}),console.log(`Production server running at http://localhost:${i}`);};
2
+ export{f as startServer};//# sourceMappingURL=production.js.map
3
+ //# sourceMappingURL=production.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/production.ts"],"names":["startServer","options","port","dist","resolve","indexHtml","join","apiPrefix","api","req","url","path","filePath","file"],"mappings":"gCAUO,IAAMA,CAAAA,CAAeC,CAAAA,EAA+B,CACzD,IAAMC,CAAAA,CAAOD,CAAAA,CAAQ,MAAQ,GAAA,CACvBE,CAAAA,CAAOC,OAAAA,CAAQ,OAAA,CAAQ,GAAA,EAAI,CAAGH,CAAAA,CAAQ,OAAA,EAAW,MAAM,CAAA,CACvDI,CAAAA,CAAYC,IAAAA,CAAKH,CAAAA,CAAMF,CAAAA,CAAQ,QAAA,EAAY,YAAY,EACvDM,CAAAA,CAAYN,CAAAA,CAAQ,SAAA,EAAa,MAAA,CACjCO,CAAAA,CAAMP,CAAAA,CAAQ,GAAA,CAEpB,GAAI,OAAO,GAAA,CAAQ,GAAA,CACjB,MAAM,IAAI,KAAA,CAAM,8CAA8C,CAAA,CAGhE,IAAI,KAAA,CAAM,CACR,IAAA,CAAAC,CAAAA,CACA,MAAM,KAAA,CAAMO,CAAAA,CAAK,CACf,IAAMC,CAAAA,CAAM,IAAI,GAAA,CAAID,CAAAA,CAAI,GAAG,CAAA,CAG3B,GAAIC,CAAAA,CAAI,SAAS,UAAA,CAAWH,CAAS,CAAA,CACnC,OAAOC,CAAAA,CAAI,MAAA,CAAOC,CAAG,CAAA,CAIvB,IAAIE,CAAAA,CAAOD,CAAAA,CAAI,QAAA,CACXC,CAAAA,GAAS,GAAA,GAAKA,CAAAA,CAAO,aAAA,CAAA,CAEzB,IAAMC,CAAAA,CAAWN,IAAAA,CAAKH,CAAAA,CAAMQ,CAAAA,CAAK,UAAA,CAAW,GAAG,CAAA,CAAIA,CAAAA,CAAK,MAAM,CAAC,CAAA,CAAIA,CAAI,CAAA,CACjEE,CAAAA,CAAO,GAAA,CAAI,IAAA,CAAKD,CAAQ,EAE9B,OAAI,MAAMC,CAAAA,CAAK,MAAA,EAAO,CACb,IAAI,QAAA,CAASA,CAAI,CAAA,CAItBJ,CAAAA,CAAI,MAAA,GAAW,KAAA,CACV,IAAI,QAAA,CAAS,GAAA,CAAI,IAAA,CAAKJ,CAAS,CAAC,CAAA,CAGlC,IAAI,QAAA,CAAS,WAAA,CAAa,CAAE,MAAA,CAAQ,GAAI,CAAC,CAClD,CACF,CAAC,CAAA,CAED,OAAA,CAAQ,GAAA,CAAI,CAAA,8CAAA,EAAiDH,CAAI,EAAE,EACrE","file":"production.js","sourcesContent":["import { resolve, join } from \"node:path\";\n\ninterface ProductionOptions {\n port?: number;\n distDir?: string;\n htmlFile?: string;\n apiPrefix?: string;\n api: { handle: (request: Request) => Promise<Response> } | any;\n}\n\nexport const startServer = (options: ProductionOptions) => {\n const port = options.port || 3000;\n const dist = resolve(process.cwd(), options.distDir || \"dist\");\n const indexHtml = join(dist, options.htmlFile || \"index.html\");\n const apiPrefix = options.apiPrefix || \"/api\";\n const api = options.api;\n\n if (typeof Bun === \"undefined\") {\n throw new Error(\"This production server utility requires Bun.\");\n }\n\n Bun.serve({\n port,\n async fetch(req) {\n const url = new URL(req.url);\n\n // 1. Handle API requests\n if (url.pathname.startsWith(apiPrefix)) {\n return api.handle(req);\n }\n\n // 2. Serve static files\n let path = url.pathname;\n if (path === \"/\") path = \"/index.html\";\n\n const filePath = join(dist, path.startsWith(\"/\") ? path.slice(1) : path);\n const file = Bun.file(filePath);\n\n if (await file.exists()) {\n return new Response(file);\n }\n\n // 3. SPA Fallback\n if (req.method === \"GET\") {\n return new Response(Bun.file(indexHtml));\n }\n\n return new Response(\"Not Found\", { status: 404 });\n },\n });\n\n console.log(`Production server running at http://localhost:${port}`);\n};\n"]}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "vite-elysia-forge",
3
+ "version": "0.0.1",
4
+ "description": "A Vite plugin to seamlessly integrate ElysiaJS for full-stack development with Bun runtime.",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=18.0.0",
8
+ "bun": ">=1.0.0"
9
+ },
10
+ "main": "dist/index.cjs",
11
+ "module": "dist/index.cjs",
12
+ "types": "dist/index.d.ts",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/Chijioke-Udokporo/vite-elysia-forge.git"
16
+ },
17
+ "author": "chijioke-udokporo",
18
+ "private": false,
19
+ "scripts": {
20
+ "build": "bun run test && tsup",
21
+ "test": "bun test"
22
+ },
23
+ "dependencies": {
24
+ "pino": "^10.1.0",
25
+ "pino-pretty": "^13.1.3"
26
+ },
27
+ "devDependencies": {
28
+ "@types/bun": "latest",
29
+ "@types/node": "^22.7.4",
30
+ "tsup": "^8.5.1"
31
+ },
32
+ "peerDependencies": {
33
+ "typescript": "^5"
34
+ },
35
+ "bin": {
36
+ "vite-elysia-forge": "./dist/cli.js"
37
+ },
38
+ "exports": {
39
+ ".": {
40
+ "types": "./dist/index.d.ts",
41
+ "import": "./dist/index.js",
42
+ "require": "./dist/index.cjs"
43
+ },
44
+ "./production": {
45
+ "types": "./dist/production.d.ts",
46
+ "import": "./dist/production.js",
47
+ "require": "./dist/production.cjs"
48
+ }
49
+ },
50
+ "files": [
51
+ "dist",
52
+ "README.md"
53
+ ]
54
+ }