diff --git a/bun.lock b/bun.lock index e6a00ba..1998789 100644 --- a/bun.lock +++ b/bun.lock @@ -5,12 +5,14 @@ "name": "bun-next", "dependencies": { "commander": "^14.0.2", + "micromatch": "^4.0.8", "ora": "^9.0.0", "react": "^19.2.0", "react-dom": "^19.2.0", }, "devDependencies": { "@types/bun": "latest", + "@types/micromatch": "^4.0.10", "@types/node": "^24.10.0", "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", @@ -21,8 +23,12 @@ }, }, "packages": { + "@types/braces": ["@types/braces@3.0.5", "", {}, "sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w=="], + "@types/bun": ["@types/bun@1.2.3", "", { "dependencies": { "bun-types": "1.2.3" } }, "sha512-054h79ipETRfjtsCW9qJK8Ipof67Pw9bodFWmkfkaUaRiIQ1dIV2VTlheshlBx3mpKr0KeK8VqnMMCtgN9rQtw=="], + "@types/micromatch": ["@types/micromatch@4.0.10", "", { "dependencies": { "@types/braces": "*" } }, "sha512-5jOhFDElqr4DKTrTEbnW8DZ4Hz5LRUEmyrGpCMrD/NphYv3nUnaF08xmSLx1rGGnyEs/kFnhiw6dCgcDqMr5PQ=="], + "@types/node": ["@types/node@24.10.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A=="], "@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="], @@ -33,6 +39,8 @@ "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + "bun-types": ["bun-types@1.2.3", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-P7AeyTseLKAvgaZqQrvp3RqFM3yN9PlcLuSTe7SoJOfZkER73mLdT2vEQi8U64S1YvM/ldcNiQjn0Sn7H9lGgg=="], "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], @@ -45,20 +53,28 @@ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], "is-interactive": ["is-interactive@2.0.0", "", {}, "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ=="], + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + "is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], "log-symbols": ["log-symbols@7.0.1", "", { "dependencies": { "is-unicode-supported": "^2.0.0", "yoctocolors": "^2.1.1" } }, "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg=="], + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], "onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], "ora": ["ora@9.0.0", "", { "dependencies": { "chalk": "^5.6.2", "cli-cursor": "^5.0.0", "cli-spinners": "^3.2.0", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.1.0", "log-symbols": "^7.0.1", "stdin-discarder": "^0.2.2", "string-width": "^8.1.0", "strip-ansi": "^7.1.2" } }, "sha512-m0pg2zscbYgWbqRR6ABga5c3sZdEon7bSgjnlXC64kxtxLOyjRcbbUkLj7HFyy/FTD+P2xdBWu8snGhYI0jc4A=="], + "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="], "react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="], @@ -75,6 +91,8 @@ "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + "typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="], "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], diff --git a/commands/build/index.ts b/commands/build/index.ts deleted file mode 100644 index faf85e6..0000000 --- a/commands/build/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Command } from "commander"; -import grabConfig from "../../functions/grab-config"; - -export default function () { - return new Command("build") - .description("Build project") - .action(async () => { - console.log(`Building project ...`); - - const config = await grabConfig(); - global.CONFIG = config; - }); -} diff --git a/commands/dev/index.ts b/commands/dev/index.ts index 2228b96..bad8d17 100644 --- a/commands/dev/index.ts +++ b/commands/dev/index.ts @@ -1,7 +1,8 @@ import { Command } from "commander"; -import grabConfig from "../../functions/grab-config"; -import grabDirNames from "../../utils/grab-dir-names"; -import AppNames from "../../utils/grab-app-names"; +import grabConfig from "../../src/functions/grab-config"; +import startServer from "../../src/functions/server/start-server"; +import init from "../../src/functions/init"; +import type { BunextConfig } from "../../src/types"; export default function () { return new Command("dev") @@ -9,34 +10,15 @@ export default function () { .action(async () => { console.log(`Running development server ...`); - const config = await grabConfig(); - global.CONFIG = config; + await init(); - const { entrypoint } = grabDirNames(); - const { defaultDistDir } = AppNames; + const config: BunextConfig = (await grabConfig()) || {}; - let buildCmd = ["bun"]; + global.CONFIG = { + ...config, + development: true, + }; - buildCmd.push( - "build", - entrypoint, - "--outdir", - config.distDir || defaultDistDir, - "--watch" - ); - - const spawnedProcess = Bun.spawn({ - cmd: buildCmd, - }); - - const exitCode = await spawnedProcess.exited; - - // Bun.build({ - // entrypoints: [entrypoint], - // outdir: config.distDir || defaultDistDir, - // minify: true, - // }); - - // await startServer(); + await startServer({ dev: true }); }); } diff --git a/commands/init/index.ts b/commands/init/index.ts deleted file mode 100644 index 430ae2a..0000000 --- a/commands/init/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Command } from "commander"; -import AppNames from "../../utils/grab-app-names"; - -export default function () { - return new Command("init") - .description("Initialize project") - .action(async () => { - console.log(`Initializing ${AppNames.name} ...`); - }); -} diff --git a/commands/start/index.ts b/commands/start/index.ts index dc36b6f..4b98854 100644 --- a/commands/start/index.ts +++ b/commands/start/index.ts @@ -1,5 +1,7 @@ import { Command } from "commander"; -import grabConfig from "../../functions/grab-config"; +import grabConfig from "../../src/functions/grab-config"; +import startServer from "../../src/functions/server/start-server"; +import init from "../../src/functions/init"; export default function () { return new Command("start") @@ -7,7 +9,12 @@ export default function () { .action(async () => { console.log(`Starting production server ...`); + await init(); + const config = await grabConfig(); - global.CONFIG = config; + + global.CONFIG = { ...config }; + + await startServer(); }); } diff --git a/functions/grab-config.ts b/functions/grab-config.ts deleted file mode 100644 index 29943b9..0000000 --- a/functions/grab-config.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { existsSync } from "fs"; -import type { BunextConfig } from "../types"; -import grabDirNames from "../utils/grab-dir-names"; -import exitWithError from "../utils/exit-with-error"; - -export default async function grabConfig(): Promise { - const { configFile } = grabDirNames(); - - if (!existsSync(configFile)) { - exitWithError(`Config file \`${configFile}\` doesn't exist!`); - } - - const config = (await import(configFile)).default as BunextConfig; - - if (!config) { - exitWithError( - `Config file \`${configFile}\` is invalid! Please provide a valid default export in your config file.` - ); - } - - return config; -} diff --git a/functions/server/grab-route-content.tsx b/functions/server/grab-route-content.tsx deleted file mode 100644 index fbda103..0000000 --- a/functions/server/grab-route-content.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import type { GetRouteReturn } from "../../types"; -import AppNames from "../../utils/grab-app-names"; -import ReactDOMServer from "react-dom/server"; - -type Params = { - url: URL; - route: GetRouteReturn; - req: Request; -}; - -export default async function grabRouteContent({ - url, - route, - req, -}: Params): Promise { - const config = global.CONFIG; - const { name } = AppNames; - - let html = `Welcome to ${name} ...`; - - if (route.component) { - html = ReactDOMServer.renderToString(); - return new Response(html, { - headers: { - "Content-Type": "text/html; charset=utf-8", - }, - }); - } - - return new Response(html); -} diff --git a/functions/server/start-server.ts b/functions/server/start-server.ts deleted file mode 100644 index 0a1eefb..0000000 --- a/functions/server/start-server.ts +++ /dev/null @@ -1,47 +0,0 @@ -import AppNames from "../../utils/grab-app-names"; -import grabAppPort from "../../utils/grab-app-port"; -import getRoute from "../router/get-route"; -import grabRouteContent from "./grab-route-content"; - -export default async function startServer() { - const config = global.CONFIG; - const port = grabAppPort(); - const { name } = AppNames; - - const server = Bun.serve({ - async fetch(req) { - try { - const url = new URL(req.url); - - const route = await getRoute({ route: url.pathname }); - - if (!route) { - return new Response(`Route ${url.pathname} not Found!`, { - status: 404, - }); - } - - const response = await grabRouteContent({ req, route, url }); - - if (response) { - return response; - } - - return new Response(`No Response!`, { - status: 404, - }); - } catch (error: any) { - return new Response(`Server Error: ${error.message}`, { - status: 500, - }); - } - }, - port, - development: true, - }); - - global.SERVER = server; - console.log(`${name} Server Running on Port ${server.port} ...`); - - return server; -} diff --git a/index.ts b/index.ts index 9e5e2c4..739ba48 100644 --- a/index.ts +++ b/index.ts @@ -1,13 +1,13 @@ -#!/usr/bin/env node +#!/usr/bin/env bun import { program } from "commander"; import start from "./commands/start"; import dev from "./commands/dev"; -import build from "./commands/build"; -import init from "./commands/init"; import ora, { type Ora } from "ora"; -import type { BunextConfig } from "./types"; -import type { Server } from "bun"; +import type { BunextConfig } from "./src/types"; +import type { FileSystemRouter, Server } from "bun"; +import init from "./src/functions/init"; +import grabDirNames from "./src/utils/grab-dir-names"; /** * # Declare Global Variables @@ -16,10 +16,26 @@ declare global { var ORA_SPINNER: Ora; var CONFIG: BunextConfig; var SERVER: Server | undefined; + var RECOMPILING: boolean; + var WATCHER_TIMEOUT: any; + var ROUTER: FileSystemRouter; + var HMR_CONTROLLERS: Set>; } global.ORA_SPINNER = ora(); global.ORA_SPINNER.clear(); +global.HMR_CONTROLLERS = new Set(); + +await init(); + +const { ROUTES_DIR } = grabDirNames(); + +const router = new Bun.FileSystemRouter({ + style: "nextjs", + dir: ROUTES_DIR, +}); + +global.ROUTER = router; /** * # Describe Program @@ -32,10 +48,8 @@ program /** * # Declare Commands */ -program.addCommand(start()); program.addCommand(dev()); -program.addCommand(build()); -program.addCommand(init()); +program.addCommand(start()); /** * # Handle Unavailable Commands diff --git a/package.json b/package.json index 3c33d58..9f423fc 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ }, "devDependencies": { "@types/bun": "latest", + "@types/micromatch": "^4.0.10", "@types/node": "^24.10.0", "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2" @@ -24,6 +25,7 @@ }, "dependencies": { "commander": "^14.0.2", + "micromatch": "^4.0.8", "ora": "^9.0.0", "react": "^19.2.0", "react-dom": "^19.2.0" diff --git a/src/functions/grab-config.ts b/src/functions/grab-config.ts new file mode 100644 index 0000000..df7a4cd --- /dev/null +++ b/src/functions/grab-config.ts @@ -0,0 +1,26 @@ +import { existsSync } from "fs"; +import type { BunextConfig } from "../types"; +import grabDirNames from "../utils/grab-dir-names"; +import exitWithError from "../utils/exit-with-error"; + +export default async function grabConfig(): Promise { + try { + const { CONFIG_FILE } = grabDirNames(); + + if (!existsSync(CONFIG_FILE)) { + exitWithError(`Config file \`${CONFIG_FILE}\` doesn't exist!`); + } + + const config = (await import(CONFIG_FILE)).default as BunextConfig; + + if (!config) { + exitWithError( + `Config file \`${CONFIG_FILE}\` is invalid! Please provide a valid default export in your config file.` + ); + } + + return config; + } catch (error) { + return undefined; + } +} diff --git a/src/functions/init.ts b/src/functions/init.ts new file mode 100644 index 0000000..7f0129c --- /dev/null +++ b/src/functions/init.ts @@ -0,0 +1,31 @@ +import { existsSync, mkdirSync, statSync, writeFileSync } from "fs"; +import grabDirNames from "../utils/grab-dir-names"; + +export default async function () { + const dirNames = grabDirNames(); + + const keys = Object.keys(dirNames) as (keyof ReturnType< + typeof grabDirNames + >)[]; + + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const dir = dirNames[key]; + + // const stat = statSync(dir); + + if (!existsSync(dir) && !dir.match(/\.\w+$/)) { + mkdirSync(dir, { recursive: true }); + continue; + } + + if (key == "CONFIG_FILE" && !existsSync(dir)) { + let basicConfig = ``; + + basicConfig += `const config = {};\n`; + basicConfig += `export default config;\n`; + + writeFileSync(dir, basicConfig); + } + } +} diff --git a/functions/router/get-route.ts b/src/functions/router/get-route.ts similarity index 77% rename from functions/router/get-route.ts rename to src/functions/router/get-route.ts index 3510369..0ed58b4 100644 --- a/functions/router/get-route.ts +++ b/src/functions/router/get-route.ts @@ -2,6 +2,7 @@ import grabDirNames from "../../utils/grab-dir-names"; import type { GetRouteReturn } from "../../types"; import grabAssetsPrefix from "../../utils/grab-assets-prefix"; import grabOrigin from "../../utils/grab-origin"; +import grabRouter from "../../utils/grab-router"; type Params = { route: string; @@ -10,21 +11,13 @@ type Params = { export default async function getRoute({ route, }: Params): Promise { - const { pagesDir } = grabDirNames(); + const { ROUTES_DIR } = grabDirNames(); if (route.match(/\(/)) { return null; } - const assetPrefix = grabAssetsPrefix(); - const origin = grabOrigin(); - - const router = new Bun.FileSystemRouter({ - style: "nextjs", - dir: pagesDir, - origin, - assetPrefix, - }); + const router = grabRouter(); const match = router.match(route); diff --git a/src/functions/server/handle-routes.ts b/src/functions/server/handle-routes.ts new file mode 100644 index 0000000..a893e50 --- /dev/null +++ b/src/functions/server/handle-routes.ts @@ -0,0 +1,69 @@ +import type { Server } from "bun"; +import type { + APIResponseObject, + BunextServerRouteConfig, + BunxRouteParams, +} from "../../types"; +import grabRouteParams from "../../utils/grab-route-params"; +import grabConstants from "../../utils/grab-constants"; +import grabRouter from "../../utils/grab-router"; + +type Params = { + req: Request; + server: Server; +}; + +export default async function ({ + req, + server, +}: Params): Promise { + const url = new URL(req.url); + + const { MBInBytes, ServerDefaultRequestBodyLimitBytes } = + await grabConstants(); + + const router = grabRouter(); + + const match = router.match(url.pathname); + + if (!match?.filePath) { + const errMsg = `Route ${url.pathname} not found`; + + console.error(errMsg); + + return { + success: false, + status: 401, + msg: errMsg, + }; + } + + const routeParams: BunxRouteParams = await grabRouteParams({ req, server }); + + const module = await import(match.filePath); + const config = module.config as BunextServerRouteConfig | undefined; + + const contentLength = req.headers.get("content-length"); + + if (contentLength) { + const size = parseInt(contentLength, 10); + + if ( + (config?.maxRequestBodyMB && + size > config.maxRequestBodyMB * MBInBytes) || + size > ServerDefaultRequestBodyLimitBytes + ) { + return { + success: false, + status: 413, + msg: "Request Body Too Large!", + }; + } + } + + const res: APIResponseObject = await module["default"]( + routeParams as BunxRouteParams + ); + + return res; +} diff --git a/src/functions/server/server-params-gen.ts b/src/functions/server/server-params-gen.ts new file mode 100644 index 0000000..0edf440 --- /dev/null +++ b/src/functions/server/server-params-gen.ts @@ -0,0 +1,74 @@ +import path from "path"; +import type { ServeOptions } from "bun"; +import grabAppPort from "../../utils/grab-app-port"; +import grabDirNames from "../../utils/grab-dir-names"; +import handleWebPages from "./web-pages/handle-web-pages"; +import handleRoutes from "./handle-routes"; +import isDevelopment from "../../utils/is-development"; + +const port = grabAppPort(); +const { PUBLIC_DIR } = grabDirNames(); + +type Params = { + dev?: boolean; +}; + +export default async function (params?: Params): Promise { + return { + async fetch(req, server) { + try { + const url = new URL(req.url); + + if (url.pathname === "/__hmr" && isDevelopment()) { + let controller: ReadableStreamDefaultController; + const stream = new ReadableStream({ + start(c) { + controller = c; + global.HMR_CONTROLLERS.add(c); + }, + cancel() { + global.HMR_CONTROLLERS.delete(controller); + }, + }); + + return new Response(stream, { + headers: { + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache", + Connection: "keep-alive", + }, + }); + } else if (url.pathname.startsWith("/api/")) { + const res = await handleRoutes({ req, server }); + + return new Response(JSON.stringify(res), { + status: res?.status, + headers: { + "Content-Type": "application/json", + }, + }); + } else if (url.pathname.startsWith("/public/")) { + const file = Bun.file( + path.join( + PUBLIC_DIR, + url.pathname.replace(/^\/public/, ""), + ), + ); + + return new Response(file); + } else { + return await handleWebPages({ req, server }); + } + } catch (error: any) { + return new Response(`Server Error: ${error.message}`, { + status: 500, + }); + } + }, + port, + development: isDevelopment() && { + hmr: true, + console: true, + }, + } as ServeOptions; +} diff --git a/src/functions/server/start-server.ts b/src/functions/server/start-server.ts new file mode 100644 index 0000000..2e44333 --- /dev/null +++ b/src/functions/server/start-server.ts @@ -0,0 +1,27 @@ +import AppNames from "../../utils/grab-app-names"; +import serverParamsGen from "./server-params-gen"; +import watcher from "./watcher"; + +type Params = { + dev?: boolean; +}; + +export default async function startServer(params?: Params) { + const { name } = AppNames; + + const serverParams = await serverParamsGen(); + + const server = Bun.serve(serverParams); + + global.SERVER = server; + + console.log( + `${name} Server Running on http://localhost:${server.port} ...`, + ); + + if (params?.dev) { + watcher(); + } + + return server; +} diff --git a/src/functions/server/watcher.tsx b/src/functions/server/watcher.tsx new file mode 100644 index 0000000..0abe40e --- /dev/null +++ b/src/functions/server/watcher.tsx @@ -0,0 +1,127 @@ +import { watch } from "fs"; +import grabDirNames from "../../utils/grab-dir-names"; +import writeWebPageHydrationScript from "./web-pages/write-web-page-hydration-script"; +import grabPageName from "../../utils/grab-page-name"; +import path from "path"; +import { execSync } from "child_process"; +import serverParamsGen from "./server-params-gen"; +import grabRouter from "../../utils/grab-router"; +import type { FC } from "react"; + +const { ROOT_DIR, BUNX_HYDRATION_SRC_DIR, HYDRATION_DST_DIR, ROUTES_DIR } = + grabDirNames(); + +export default function watcher() { + watch( + ROOT_DIR, + { recursive: true, persistent: true }, + async (event, filename) => { + if (global.RECOMPILING) return; + if (!filename) return; + if (filename.match(/ /)) return; + if (filename.match(/^node_modules\//)) return; + if (filename.match(/\.bunext|\/public\//)) return; + + if (filename.match(/\/routes\//)) { + if (event == "change") { + clearTimeout(global.WATCHER_TIMEOUT); + + global.RECOMPILING = true; + + const fullPath = path.join(ROOT_DIR, filename); + + const pageName = grabPageName({ path: fullPath }); + + // const router = grabRouter(); + // const match = router.match(fullPath); + + // if (match?.filePath) { + // const module = await import(match.filePath); + + // const serverRes = await (async () => { + // try { + // return await module["server"](); + // } catch (error) { + // return {}; + // } + // })(); + + // const Component = module.default as FC; + // const component = ; + + // await writeWebPageHydrationScript({ + // pageName, + // component, + // }); + // } + + let cmd = `bun build`; + cmd += ` ${BUNX_HYDRATION_SRC_DIR}/${pageName}.tsx --outdir ${HYDRATION_DST_DIR}`; + cmd += ` --minify`; + + execSync(cmd, { stdio: "inherit" }); + + global.ROUTER = new Bun.FileSystemRouter({ + style: "nextjs", + dir: ROUTES_DIR, + }); + + const encoder = new TextEncoder(); + const msg = encoder.encode(`event: update\ndata: reload\n\n`); + + for (const controller of global.HMR_CONTROLLERS) { + controller.enqueue(msg); + } + global.RECOMPILING = false; + } else if (event == "rename") { + await reloadServer(); + } + } else if (filename.match(/\.(js|ts|tsx|jsx)$/)) { + clearTimeout(global.WATCHER_TIMEOUT); + await reloadServer(); + } + }, + ); + + // watch(BUNX_HYDRATION_SRC_DIR, async (event, filename) => { + // if (!filename) return; + + // const targetFile = path.join(BUNX_HYDRATION_SRC_DIR, filename); + + // await Bun.build({ + // entrypoints: [targetFile], + // outdir: HYDRATION_DST_DIR, + // minify: true, + // target: "browser", + // format: "esm", + // }); + + // global.SERVER?.publish("__bun_hmr", "update"); + + // setTimeout(() => { + // global.RECOMPILING = false; + // }, 200); + // }); + + // watch(HYDRATION_DST_DIR, async (event, filename) => { + // const encoder = new TextEncoder(); + // global.HMR_CONTROLLER?.enqueue(encoder.encode(`event: update\ndata: reload\n\n`)); + // global.RECOMPILING = false; + // }); + + // let cmd = `bun build`; + + // cmd += ` ${BUNX_HYDRATION_SRC_DIR}/*.tsx --outdir ${HYDRATION_DST_DIR}`; + // cmd += ` --watch --minify`; + + // execSync(cmd, { stdio: "inherit" }); +} + +async function reloadServer() { + const serverParams = await serverParamsGen(); + + console.log(`Reloading Server ...`); + + global.SERVER?.stop(); + global.SERVER = Bun.serve(serverParams); +} diff --git a/src/functions/server/web-pages/generate-web-html.tsx b/src/functions/server/web-pages/generate-web-html.tsx new file mode 100644 index 0000000..ce01024 --- /dev/null +++ b/src/functions/server/web-pages/generate-web-html.tsx @@ -0,0 +1,50 @@ +import path from "path"; +import { renderToString } from "react-dom/server"; +import grabContants from "../../../utils/grab-constants"; +import EJSON from "../../../utils/ejson"; +import type { PageDistGenParams } from "../../../types"; +import isDevelopment from "../../../utils/is-development"; + +export default async function genWebHTML({ + component, + pageProps, + pageName, + module, +}: PageDistGenParams) { + const { ClientRootElementIDName, ClientWindowPagePropsName } = + await grabContants(); + + const componentHTML = renderToString(component); + + const SCRIPT_SRC = path.join("/public/routes", pageName + ".js"); + + let html = `\n`; + + if (isDevelopment()) { + html += `\n`; + } + + html += `\n`; + html += ` \n`; + html += ` \n`; + html += ` React SSR with Bun\n`; + html += ` \n`; + html += ` \n`; + html += `
${componentHTML}
\n`; + html += ` \n`; + html += ` \n`; + + html += ` \n`; + html += `\n`; + + return html; +} diff --git a/src/functions/server/web-pages/handle-web-pages.tsx b/src/functions/server/web-pages/handle-web-pages.tsx new file mode 100644 index 0000000..4c23bca --- /dev/null +++ b/src/functions/server/web-pages/handle-web-pages.tsx @@ -0,0 +1,63 @@ +import type { FC } from "react"; +import grabDirNames from "../../../utils/grab-dir-names"; +import type { Server } from "bun"; +import grabPageName from "../../../utils/grab-page-name"; +import grabRouteParams from "../../../utils/grab-route-params"; +import genWebHTML from "./generate-web-html"; +import grabRouter from "../../../utils/grab-router"; +import type { BunextPageModule } from "../../../types"; + +type Params = { + req: Request; + server: Server; +}; + +export default async function ({ req, server }: Params): Promise { + const url = new URL(req.url); + + try { + const router = grabRouter(); + const match = router.match(url.pathname); + + if (!match?.filePath) { + const errMsg = `Page ${url.pathname} not found`; + console.error(errMsg); + throw new Error(errMsg); + } + + const pageName = grabPageName({ path: match.filePath }); + + const module: BunextPageModule = await import(match.filePath); + // const config = module.config as ServerRouteConfig | undefined; + + const routeParams = await grabRouteParams({ req, server }); + + const serverRes = await (async () => { + try { + return await module["server"]?.(routeParams); + } catch (error) { + return {}; + } + })(); + + const Component = module.default as FC; + const component = ; + + const html = await genWebHTML({ + component, + pageProps: serverRes, + pageName, + module, + }); + + return new Response(html, { + headers: { + "Content-Type": "text/html", + }, + }); + } catch (error) { + return new Response(`Page Not Found`, { + status: 404, + }); + } +} diff --git a/src/functions/server/web-pages/write-web-page-hydration-script.tsx b/src/functions/server/web-pages/write-web-page-hydration-script.tsx new file mode 100644 index 0000000..705839a --- /dev/null +++ b/src/functions/server/web-pages/write-web-page-hydration-script.tsx @@ -0,0 +1,41 @@ +import { writeFileSync } from "fs"; +import path from "path"; +import grabDirNames from "../../../utils/grab-dir-names"; +import grabContants from "../../../utils/grab-constants"; +import genWebHTML from "./generate-web-html"; +import type { PageDistGenParams } from "../../../types"; + +const { BUNX_HYDRATION_SRC_DIR, HYDRATION_DST_DIR } = grabDirNames(); + +export default async function (params: PageDistGenParams) { + const { ClientRootElementIDName, ClientWindowPagePropsName } = + await grabContants(); + + const PAGE_DIST_DIR = path.join(HYDRATION_DST_DIR, params.pageName); + + const pageSrcTs = `index.tsx`; + + let script = ""; + + script += `import React from "react";\n`; + script += `import { hydrateRoot } from "react-dom/client";\n`; + script += `import App from "./";\n`; + + script += `declare global {\n`; + script += ` interface Window {\n`; + script += ` ${ClientWindowPagePropsName}: any;\n`; + script += ` }\n`; + script += `}\n`; + + script += `const container = document.getElementById("${ClientRootElementIDName}");\n`; + script += `hydrateRoot(container, );\n`; + + const SRC_WRITE_FILE = path.join(PAGE_DIST_DIR, pageSrcTs); + writeFileSync(SRC_WRITE_FILE, script, "utf-8"); + + let html = await genWebHTML(params); + const pageHtml = `index.html`; + + const HTML_WRITE_FILE = path.join(PAGE_DIST_DIR, pageHtml); + writeFileSync(HTML_WRITE_FILE, html, "utf-8"); +} diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..316df5f --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,143 @@ +import type { MatchedRoute, Server } from "bun"; +import type { FC, JSX, ReactNode } from "react"; + +export type ServerProps = { + params: Record; + searchParams: Record; + headers: Headers; + cookies: Record; + body: any; + method: string; + url: string; + pathname: string; + query: Record; + search: string; + hash: string; +}; + +export type StaticProps = { + params: Record; + searchParams: Record; + headers: Headers; + cookies: Record; + body: any; + method: string; + url: string; + pathname: string; + query: Record; + search: string; + hash: string; +}; + +export type StaticPaths = string[]; + +export type StaticParams = Record; + +export type PageModule = { + component: React.ComponentType; + serverProps: ServerProps; + staticProps: StaticProps; + staticPaths: StaticPaths; + staticParams: StaticParams; +}; + +export type BunextConfig = { + distDir?: string; + assetsPrefix?: string; + origin?: string; + globalVars?: { [k: string]: any }; + port?: number; + development?: boolean; +}; + +export type GetRouteReturn = { + match: MatchedRoute; + module: PageModule; + component: React.ComponentType; + serverProps: ServerProps; + staticProps: StaticProps; + staticPaths: StaticPaths; + staticParams: StaticParams; +}; + +export type BunxRouteParams = { + req: Request; + url: URL; + server: Server; + body?: any; + query?: any; +}; + +export interface PostInsertReturn { + fieldCount?: number; + affectedRows?: number; + insertId?: number; + serverStatus?: number; + warningCount?: number; + message?: string; + protocol41?: boolean; + changedRows?: number; +} + +export type APIResponseObject< + T extends { [k: string]: any } = { [k: string]: any }, +> = { + success: boolean; + payload?: T[] | null; + singleRes?: T | null; + stringRes?: string | null; + numberRes?: number | null; + postInsertReturn?: PostInsertReturn | null; + payloadBase64?: string; + payloadThumbnailBase64?: string; + payloadURL?: string; + payloadThumbnailURL?: string; + error?: any; + msg?: string; + queryObject?: any; + countQueryObject?: any; + status?: number; + count?: number; + errors?: any[]; + debug?: any; + batchPayload?: any[][] | null; + errorData?: any; + token?: string; + csrf?: string; + cookieNames?: any; + key?: string; + userId?: string | number; + code?: string; + createdAt?: number; + email?: string; + requestOptions?: any; + logoutUser?: boolean; + redirect?: string; +}; + +export type BunextServerRouteConfig = { + maxRequestBodyMB?: number; +}; + +export type PageDistGenParams = { + component: ReactNode; + pageProps?: any; + module?: BunextPageModule; + pageName: string; +}; + +export type BunextPageModule = { + default: FC; + server?: ( + routeParams: BunxRouteParams, + ) => Promise; +}; + +export type BunextPageModuleServerReturn = { + props?: any; +}; + +export type BunextPageModuleMetadata = { + title?: string; + description?: string; +}; diff --git a/src/utils/deserialize-query.ts b/src/utils/deserialize-query.ts new file mode 100644 index 0000000..3e4e4e8 --- /dev/null +++ b/src/utils/deserialize-query.ts @@ -0,0 +1,28 @@ +import EJSON from "./ejson"; + +/** + * # Convert Serialized Query back to object + */ +export default function deserializeQuery( + query: string | { [s: string]: any } +): { + [s: string]: any; +} { + let queryObject: { [s: string]: any } = + typeof query == "object" ? query : Object(EJSON.parse(query)); + + const keys = Object.keys(queryObject); + + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const value = queryObject[key]; + + if (typeof value == "string") { + if (value.match(/^\{|^\[/)) { + queryObject[key] = EJSON.parse(value); + } + } + } + + return queryObject; +} diff --git a/src/utils/ejson.ts b/src/utils/ejson.ts new file mode 100644 index 0000000..b4f0528 --- /dev/null +++ b/src/utils/ejson.ts @@ -0,0 +1,38 @@ +/** + * # EJSON parse string + */ +function parse( + string: string | null | number, + reviver?: (this: any, key: string, value: any) => any +): { [s: string]: any } | { [s: string]: any }[] | undefined { + if (!string) return undefined; + if (typeof string == "object") return string; + if (typeof string !== "string") return undefined; + try { + return JSON.parse(string, reviver); + } catch (error) { + return undefined; + } +} + +/** + * # EJSON stringify object + */ +function stringify( + value: any, + replacer?: ((this: any, key: string, value: any) => any) | null, + space?: string | number +): string | undefined { + try { + return JSON.stringify(value, replacer || undefined, space); + } catch (error) { + return undefined; + } +} + +const EJSON = { + parse, + stringify, +}; + +export default EJSON; diff --git a/utils/exit-with-error.ts b/src/utils/exit-with-error.ts similarity index 100% rename from utils/exit-with-error.ts rename to src/utils/exit-with-error.ts diff --git a/utils/grab-app-names.ts b/src/utils/grab-app-names.ts similarity index 100% rename from utils/grab-app-names.ts rename to src/utils/grab-app-names.ts diff --git a/src/utils/grab-app-port.ts b/src/utils/grab-app-port.ts new file mode 100644 index 0000000..f69cd31 --- /dev/null +++ b/src/utils/grab-app-port.ts @@ -0,0 +1,20 @@ +import AppNames from "./grab-app-names"; +import numberfy from "./numberfy"; + +export default function grabAppPort() { + const { defaultPort } = AppNames; + + try { + if (process.env.PORT) { + return numberfy(process.env.PORT); + } + + if (global.CONFIG.port) { + return global.CONFIG.port; + } + + return numberfy(defaultPort); + } catch (error) { + return numberfy(defaultPort); + } +} diff --git a/utils/grab-assets-prefix.ts b/src/utils/grab-assets-prefix.ts similarity index 100% rename from utils/grab-assets-prefix.ts rename to src/utils/grab-assets-prefix.ts diff --git a/src/utils/grab-constants.ts b/src/utils/grab-constants.ts new file mode 100644 index 0000000..44ba275 --- /dev/null +++ b/src/utils/grab-constants.ts @@ -0,0 +1,19 @@ +import path from "path"; +import grabConfig from "../functions/grab-config"; + +export default async function grabConstants() { + const config = await grabConfig(); + const MB_IN_BYTES = 1024 * 1024; + + const ClientWindowPagePropsName = "__PAGE_PROPS__"; + const ClientRootElementIDName = "__bunext"; + + const ServerDefaultRequestBodyLimitBytes = MB_IN_BYTES * 10; + + return { + ClientRootElementIDName, + ClientWindowPagePropsName, + MBInBytes: MB_IN_BYTES, + ServerDefaultRequestBodyLimitBytes, + }; +} diff --git a/src/utils/grab-dir-names.ts b/src/utils/grab-dir-names.ts new file mode 100644 index 0000000..437820a --- /dev/null +++ b/src/utils/grab-dir-names.ts @@ -0,0 +1,83 @@ +import path from "path"; + +export default function grabDirNames() { + const ROOT_DIR = process.cwd(); + const SRC_DIR = path.join(ROOT_DIR, "src"); + const ROUTES_DIR = path.join(SRC_DIR, "routes"); + const API_DIR = path.join(ROUTES_DIR, "api"); + const PUBLIC_DIR = path.join(ROOT_DIR, "public"); + const HYDRATION_DST_DIR = path.join(PUBLIC_DIR, "routes"); + const CONFIG_FILE = path.join(ROOT_DIR, "bunext.config.ts"); + + const BUNX_CWD_DIR = path.resolve(ROOT_DIR, ".bunext"); + const BUNX_TMP_DIR = path.resolve(BUNX_CWD_DIR, ".tmp"); + const BUNX_HYDRATION_SRC_DIR = path.resolve( + BUNX_CWD_DIR, + "client", + "hydration-src" + ); + + const BUNX_ROOT_DIR = path.resolve(__dirname, "../../"); + + return { + ROOT_DIR, + SRC_DIR, + ROUTES_DIR, + API_DIR, + PUBLIC_DIR, + HYDRATION_DST_DIR, + BUNX_ROOT_DIR, + CONFIG_FILE, + BUNX_TMP_DIR, + BUNX_HYDRATION_SRC_DIR, + }; +} + +// const rootDir = params?.dir || process.cwd(); +// const appDir = path.resolve(__dirname, ".."); +// const entrypoint = path.join( +// appDir, +// "functions", +// "server", +// "start-server.ts" +// ); + +// const bunextDir = path.join(rootDir, ".bunext"); +// const bunextClientDir = path.join(bunextDir, "client"); +// const bunextClientRoutesDir = path.join(bunextClientDir, "routes"); +// const bunextClientRoutesSrcDir = path.join(bunextClientRoutesDir, "src"); +// const bunextClientRoutesDstDir = path.join(bunextClientRoutesDir, "dst"); + +// const bunextServerDir = path.join(bunextDir, "server"); +// const bunextServerPagesDir = path.join(bunextServerDir, "pages"); + +// const publicDir = path.join(rootDir, "public"); +// const configFile = path.join(rootDir, "bunext.config.ts"); + +// const srcDir = path.join(rootDir, "src"); +// const pagesDir = path.join(srcDir, "pages"); +// const componentsDir = path.join(srcDir, "components"); +// const stylesDir = path.join(srcDir, "styles"); +// const utilsDir = path.join(srcDir, "utils"); +// const typesDir = path.join(srcDir, "types"); + +// return { +// rootDir, +// pagesDir, +// componentsDir, +// publicDir, +// stylesDir, +// utilsDir, +// typesDir, +// configFile, +// appDir, +// entrypoint, +// srcDir, +// bunextDir, +// bunextClientDir, +// bunextClientRoutesDir, +// bunextClientRoutesSrcDir, +// bunextClientRoutesDstDir, +// bunextServerDir, +// bunextServerPagesDir, +// }; diff --git a/utils/grab-origin.ts b/src/utils/grab-origin.ts similarity index 100% rename from utils/grab-origin.ts rename to src/utils/grab-origin.ts diff --git a/src/utils/grab-page-name.ts b/src/utils/grab-page-name.ts new file mode 100644 index 0000000..96eb227 --- /dev/null +++ b/src/utils/grab-page-name.ts @@ -0,0 +1,18 @@ +type Params = { + path: string; +}; + +export default function grabPageName(params: Params) { + const pathArr = params.path.split("/"); + + const routesIndex = pathArr.findIndex((p) => p == "routes"); + + const newPathArr = [...pathArr].slice(routesIndex + 1); + + const filename = newPathArr + .filter((p) => Boolean(p.match(/./))) + .map((p) => p.replace(/\.\w+$/, "").replace(/[^a-z]/g, "")) + .join("-"); + + return filename; +} diff --git a/src/utils/grab-route-params.ts b/src/utils/grab-route-params.ts new file mode 100644 index 0000000..a38e7fc --- /dev/null +++ b/src/utils/grab-route-params.ts @@ -0,0 +1,35 @@ +import type { Server } from "bun"; +import type { BunxRouteParams } from "../types"; +import deserializeQuery from "./deserialize-query"; + +type Params = { + req: Request; + server: Server; +}; + +export default async function grabRouteParams({ + req, + server, +}: Params): Promise { + const url = new URL(req.url); + + const query = deserializeQuery(Object.fromEntries(url.searchParams)); + + const body = await (async () => { + try { + return req.method == "GET" ? undefined : await req.json(); + } catch (error) { + return {}; + } + })(); + + const routeParams: BunxRouteParams = { + req, + url, + server, + query, + body, + }; + + return routeParams; +} diff --git a/src/utils/grab-router.ts b/src/utils/grab-router.ts new file mode 100644 index 0000000..699efe7 --- /dev/null +++ b/src/utils/grab-router.ts @@ -0,0 +1,14 @@ +import grabDirNames from "./grab-dir-names"; + +export default function grabRouter() { + const { ROUTES_DIR } = grabDirNames(); + + if (process.env.NODE_ENV == "production") { + return global.ROUTER; + } + + return new Bun.FileSystemRouter({ + style: "nextjs", + dir: ROUTES_DIR, + }); +} diff --git a/src/utils/is-development.ts b/src/utils/is-development.ts new file mode 100644 index 0000000..108a411 --- /dev/null +++ b/src/utils/is-development.ts @@ -0,0 +1,7 @@ +export default function isDevelopment() { + const config = global.CONFIG; + + if (config.development) return true; + + return false; +} diff --git a/utils/numberfy.ts b/src/utils/numberfy.ts similarity index 100% rename from utils/numberfy.ts rename to src/utils/numberfy.ts diff --git a/tsconfig.json b/tsconfig.json index 238655f..48b634e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,27 +1,30 @@ { - "compilerOptions": { - // Enable latest features - "lib": ["ESNext", "DOM"], - "target": "ESNext", - "module": "ESNext", - "moduleDetection": "force", - "jsx": "react-jsx", - "allowJs": true, + "compilerOptions": { + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, - // Bundler mode - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "noEmit": true, + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, - // Best practices - "strict": true, - "skipLibCheck": true, - "noFallthroughCasesInSwitch": true, + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, - // Some stricter flags (disabled by default) - "noUnusedLocals": false, - "noUnusedParameters": false, - "noPropertyAccessFromIndexSignature": false - } + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false, + "outDir": "dist" + }, + "include": ["src", "commands", "index.ts"], + "exclude": ["node_modules", "dist"] } diff --git a/types/index.ts b/types/index.ts deleted file mode 100644 index d46ce4b..0000000 --- a/types/index.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { MatchedRoute } from "bun"; - -export type ServerProps = { - params: Record; - searchParams: Record; - headers: Headers; - cookies: Record; - body: any; - method: string; - url: string; - pathname: string; - query: Record; - search: string; - hash: string; -}; - -export type StaticProps = { - params: Record; - searchParams: Record; - headers: Headers; - cookies: Record; - body: any; - method: string; - url: string; - pathname: string; - query: Record; - search: string; - hash: string; -}; - -export type StaticPaths = string[]; - -export type StaticParams = Record; - -export type PageModule = { - component: React.ComponentType; - serverProps: ServerProps; - staticProps: StaticProps; - staticPaths: StaticPaths; - staticParams: StaticParams; -}; - -export type BunextConfig = { - distDir?: string; - assetsPrefix?: string; - origin?: string; - globalVars?: { [k: string]: any }; - port?: number; -}; - -export type GetRouteReturn = { - match: MatchedRoute; - module: PageModule; - component: React.ComponentType; - serverProps: ServerProps; - staticProps: StaticProps; - staticPaths: StaticPaths; - staticParams: StaticParams; -}; diff --git a/utils/grab-app-port.ts b/utils/grab-app-port.ts deleted file mode 100644 index 7f50510..0000000 --- a/utils/grab-app-port.ts +++ /dev/null @@ -1,16 +0,0 @@ -import AppNames from "./grab-app-names"; -import numberfy from "./numberfy"; - -export default function grabAppPort() { - if (process.env.PORT) { - return numberfy(process.env.PORT); - } - - if (global.CONFIG.port) { - return global.CONFIG.port; - } - - const { defaultPort } = AppNames; - - return numberfy(defaultPort); -} diff --git a/utils/grab-dir-names.ts b/utils/grab-dir-names.ts deleted file mode 100644 index c61ec9f..0000000 --- a/utils/grab-dir-names.ts +++ /dev/null @@ -1,37 +0,0 @@ -import path from "path"; - -type Params = { - dir?: string; -}; - -export default function grabDirNames(params?: Params) { - const rootDir = params?.dir || process.cwd(); - const appDir = path.resolve(__dirname, ".."); - const entrypoint = path.join( - appDir, - "functions", - "server", - "start-server.ts" - ); - - const pagesDir = path.join(rootDir, "pages"); - const componentsDir = path.join(rootDir, "components"); - const publicDir = path.join(rootDir, "public"); - const stylesDir = path.join(rootDir, "styles"); - const utilsDir = path.join(rootDir, "utils"); - const typesDir = path.join(rootDir, "types"); - const configFile = path.join(rootDir, "bunext.config.ts"); - - return { - rootDir, - pagesDir, - componentsDir, - publicDir, - stylesDir, - utilsDir, - typesDir, - configFile, - appDir, - entrypoint, - }; -}