From 98d3cf7da7a225f667ff5877a9f0ab584fff8336 Mon Sep 17 00:00:00 2001 From: Benjamin Toby Date: Sat, 21 Mar 2026 07:42:38 +0100 Subject: [PATCH] Add features: custom server and websocket. Minor refactoring --- dist/commands/build/index.js | 12 +-- dist/commands/dev/index.js | 14 +-- dist/commands/index.d.ts | 26 +---- dist/commands/index.js | 16 --- dist/commands/start/index.js | 10 +- dist/functions/bunext-init.d.ts | 26 +++++ dist/functions/bunext-init.js | 61 +++++++++++ dist/functions/init.js | 8 +- dist/functions/server/server-params-gen.d.ts | 6 +- dist/functions/server/server-params-gen.js | 15 +-- dist/functions/server/start-server.d.ts | 6 +- dist/functions/server/start-server.js | 45 +------- .../web-pages/write-hmr-tsx-module.d.ts | 2 +- dist/index.d.ts | 12 +++ dist/index.js | 4 + dist/types/index.d.ts | 4 +- examples/custom-server/server.ts | 31 ++++++ examples/websocket/bunext.config.ts | 8 ++ examples/websocket/websocket.ts | 7 ++ src/commands/build/index.ts | 16 +-- src/commands/dev/index.ts | 18 +--- src/commands/index.ts | 51 --------- src/commands/start/index.ts | 12 +-- src/functions/bunext-init.ts | 102 ++++++++++++++++++ src/functions/init.ts | 11 +- src/functions/server/server-params-gen.ts | 19 ++-- src/functions/server/start-server.ts | 57 +--------- src/index.ts | 4 + src/types/index.ts | 4 +- 29 files changed, 321 insertions(+), 286 deletions(-) create mode 100644 dist/functions/bunext-init.d.ts create mode 100644 dist/functions/bunext-init.js create mode 100644 examples/custom-server/server.ts create mode 100644 examples/websocket/bunext.config.ts create mode 100644 examples/websocket/websocket.ts create mode 100644 src/functions/bunext-init.ts diff --git a/dist/commands/build/index.js b/dist/commands/build/index.js index 4bd5d74..e4e694b 100644 --- a/dist/commands/build/index.js +++ b/dist/commands/build/index.js @@ -1,21 +1,13 @@ import { Command } from "commander"; -import grabConfig from "../../functions/grab-config"; -import init from "../../functions/init"; import allPagesBundler from "../../functions/bundler/all-pages-bundler"; import { log } from "../../utils/log"; export default function () { return new Command("build") .description("Build Project") .action(async () => { - log.banner(); - log.build("Building Project ..."); process.env.NODE_ENV = "production"; - await init(); - const config = (await grabConfig()) || {}; - global.CONFIG = { - ...config, - development: true, - }; + process.env.BUILD = "true"; + log.build("Building Project ..."); allPagesBundler({ exit_after_first_build: true, }); diff --git a/dist/commands/dev/index.js b/dist/commands/dev/index.js index 849c67d..4cf18d0 100644 --- a/dist/commands/dev/index.js +++ b/dist/commands/dev/index.js @@ -1,20 +1,14 @@ import { Command } from "commander"; -import grabConfig from "../../functions/grab-config"; import startServer from "../../functions/server/start-server"; -import init from "../../functions/init"; import { log } from "../../utils/log"; +import bunextInit from "../../functions/bunext-init"; export default function () { return new Command("dev") .description("Run development server") .action(async () => { - log.banner(); + process.env.NODE_ENV == "development"; log.info("Running development server ..."); - await init(); - const config = (await grabConfig()) || {}; - global.CONFIG = { - ...config, - development: true, - }; - await startServer({ dev: true }); + await bunextInit(); + await startServer(); }); } diff --git a/dist/commands/index.d.ts b/dist/commands/index.d.ts index 0a9ae6a..c1cf237 100644 --- a/dist/commands/index.d.ts +++ b/dist/commands/index.d.ts @@ -1,26 +1,2 @@ #!/usr/bin/env bun -import { type Ora } from "ora"; -import type { BundlerCTXMap, BunextConfig, GlobalHMRControllerObject, PageFiles } from "../types"; -import type { FileSystemRouter, Server } from "bun"; -import type { BuildContext } from "esbuild"; -import type { FSWatcher } from "fs"; -/** - * # Declare Global Variables - */ -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: GlobalHMRControllerObject[]; - var LAST_BUILD_TIME: number; - var BUNDLER_CTX: BuildContext | undefined; - var BUNDLER_CTX_MAP: BundlerCTXMap[] | undefined; - var IS_FIRST_BUNDLE_READY: boolean; - var BUNDLER_REBUILDS: 0; - var PAGES_SRC_WATCHER: FSWatcher | undefined; - var CURRENT_VERSION: string | undefined; - var PAGE_FILES: PageFiles[]; -} +export {}; diff --git a/dist/commands/index.js b/dist/commands/index.js index 85f9e94..22263ad 100644 --- a/dist/commands/index.js +++ b/dist/commands/index.js @@ -2,23 +2,7 @@ import { program } from "commander"; import start from "./start"; import dev from "./dev"; -import ora, {} from "ora"; -import init from "../functions/init"; -import grabDirNames from "../utils/grab-dir-names"; import build from "./build"; -global.ORA_SPINNER = ora(); -global.ORA_SPINNER.clear(); -global.HMR_CONTROLLERS = []; -global.IS_FIRST_BUNDLE_READY = false; -global.BUNDLER_REBUILDS = 0; -global.PAGE_FILES = []; -await init(); -const { PAGES_DIR } = grabDirNames(); -const router = new Bun.FileSystemRouter({ - style: "nextjs", - dir: PAGES_DIR, -}); -global.ROUTER = router; /** * # Describe Program */ diff --git a/dist/commands/start/index.js b/dist/commands/start/index.js index 4bfc9f6..be8edc0 100644 --- a/dist/commands/start/index.js +++ b/dist/commands/start/index.js @@ -1,18 +1,14 @@ import { Command } from "commander"; -import grabConfig from "../../functions/grab-config"; import startServer from "../../functions/server/start-server"; -import init from "../../functions/init"; import { log } from "../../utils/log"; +import bunextInit from "../../functions/bunext-init"; export default function () { return new Command("start") .description("Start production server") .action(async () => { - log.banner(); - log.info("Starting production server ..."); process.env.NODE_ENV = "production"; - await init(); - const config = await grabConfig(); - global.CONFIG = { ...config }; + log.info("Starting production server ..."); + await bunextInit(); await startServer(); }); } diff --git a/dist/functions/bunext-init.d.ts b/dist/functions/bunext-init.d.ts new file mode 100644 index 0000000..57b22e7 --- /dev/null +++ b/dist/functions/bunext-init.d.ts @@ -0,0 +1,26 @@ +import { type Ora } from "ora"; +import type { BundlerCTXMap, BunextConfig, GlobalHMRControllerObject, PageFiles } from "../types"; +import type { FileSystemRouter, Server } from "bun"; +import type { BuildContext } from "esbuild"; +import { type FSWatcher } from "fs"; +/** + * # Declare Global Variables + */ +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: GlobalHMRControllerObject[]; + var LAST_BUILD_TIME: number; + var BUNDLER_CTX: BuildContext | undefined; + var BUNDLER_CTX_MAP: BundlerCTXMap[] | undefined; + var IS_FIRST_BUNDLE_READY: boolean; + var BUNDLER_REBUILDS: 0; + var PAGES_SRC_WATCHER: FSWatcher | undefined; + var CURRENT_VERSION: string | undefined; + var PAGE_FILES: PageFiles[]; +} +export default function bunextInit(): Promise; diff --git a/dist/functions/bunext-init.js b/dist/functions/bunext-init.js new file mode 100644 index 0000000..a01be6e --- /dev/null +++ b/dist/functions/bunext-init.js @@ -0,0 +1,61 @@ +import ora, {} from "ora"; +import grabDirNames from "../utils/grab-dir-names"; +import { readFileSync } from "fs"; +import init from "./init"; +import isDevelopment from "../utils/is-development"; +import allPagesBundler from "./bundler/all-pages-bundler"; +import serverPostBuildFn from "./server/server-post-build-fn"; +import watcher from "./server/watcher"; +import EJSON from "../utils/ejson"; +import { log } from "../utils/log"; +import cron from "./server/cron"; +export default async function bunextInit() { + log.banner(); + global.ORA_SPINNER = ora(); + global.ORA_SPINNER.clear(); + global.HMR_CONTROLLERS = []; + global.IS_FIRST_BUNDLE_READY = false; + global.BUNDLER_REBUILDS = 0; + global.PAGE_FILES = []; + await init(); + const { PAGES_DIR, HYDRATION_DST_DIR_MAP_JSON_FILE } = grabDirNames(); + const router = new Bun.FileSystemRouter({ + style: "nextjs", + dir: PAGES_DIR, + }); + global.ROUTER = router; + const is_dev = isDevelopment(); + if (is_dev) { + await allPagesBundler({ + watch: true, + post_build_fn: serverPostBuildFn, + }); + watcher(); + } + else { + const artifacts = EJSON.parse(readFileSync(HYDRATION_DST_DIR_MAP_JSON_FILE, "utf-8")); + if (!artifacts?.[0]) { + log.error("Please build first."); + process.exit(1); + } + global.BUNDLER_CTX_MAP = artifacts; + global.IS_FIRST_BUNDLE_READY = true; + cron(); + } + let bundle_ready_retries = 0; + const MAX_BUNDLE_READY_RETRIES = 10; + while (!global.IS_FIRST_BUNDLE_READY) { + if (bundle_ready_retries > MAX_BUNDLE_READY_RETRIES) { + log.error("Couldn't grab first bundle for dev environment"); + process.exit(1); + } + bundle_ready_retries++; + await Bun.sleep(500); + } + /** + * First Rebuild to Avoid errors + */ + if (is_dev && global.BUNDLER_CTX) { + await global.BUNDLER_CTX.rebuild(); + } +} diff --git a/dist/functions/init.js b/dist/functions/init.js index cd915c8..3fa9149 100644 --- a/dist/functions/init.js +++ b/dist/functions/init.js @@ -1,7 +1,8 @@ -import { existsSync, mkdirSync, statSync, writeFileSync } from "fs"; +import { existsSync, mkdirSync, writeFileSync } from "fs"; import grabDirNames from "../utils/grab-dir-names"; import { execSync } from "child_process"; import path from "path"; +import grabConfig from "./grab-config"; export default async function () { const dirNames = grabDirNames(); execSync(`rm -rf ${dirNames.BUNEXT_CACHE_DIR}`); @@ -26,4 +27,9 @@ export default async function () { writeFileSync(dir, basicConfig); } } + const config = (await grabConfig()) || {}; + global.CONFIG = { + ...config, + development: true, + }; } diff --git a/dist/functions/server/server-params-gen.d.ts b/dist/functions/server/server-params-gen.d.ts index 70a01fd..86b4965 100644 --- a/dist/functions/server/server-params-gen.d.ts +++ b/dist/functions/server/server-params-gen.d.ts @@ -1,6 +1,2 @@ import type { ServeOptions } from "bun"; -type Params = { - dev?: boolean; -}; -export default function (params?: Params): Promise; -export {}; +export default function (): Promise; diff --git a/dist/functions/server/server-params-gen.js b/dist/functions/server/server-params-gen.js index 6d56e6f..d8032d7 100644 --- a/dist/functions/server/server-params-gen.js +++ b/dist/functions/server/server-params-gen.js @@ -1,17 +1,20 @@ import grabAppPort from "../../utils/grab-app-port"; import isDevelopment from "../../utils/is-development"; import bunextRequestHandler from "./bunext-req-handler"; -export default async function (params) { +import grabConfig from "../grab-config"; +import _ from "lodash"; +export default async function () { const port = grabAppPort(); - const is_dev = isDevelopment(); + const development = isDevelopment(); + const config = await grabConfig(); return { async fetch(req, server) { return await bunextRequestHandler({ req }); }, port, - // idleTimeout: 0, - development: { - hmr: true, - }, + idleTimeout: development ? 0 : undefined, + development, + websocket: config?.websocket, + ..._.omit(config?.serverOptions || {}, ["fetch"]), }; } diff --git a/dist/functions/server/start-server.d.ts b/dist/functions/server/start-server.d.ts index 2b36c1c..c3ef94e 100644 --- a/dist/functions/server/start-server.d.ts +++ b/dist/functions/server/start-server.d.ts @@ -1,5 +1 @@ -type Params = { - dev?: boolean; -}; -export default function startServer(params?: Params): Promise; -export {}; +export default function startServer(): Promise; diff --git a/dist/functions/server/start-server.js b/dist/functions/server/start-server.js index bd42b9b..7ba5241 100644 --- a/dist/functions/server/start-server.js +++ b/dist/functions/server/start-server.js @@ -1,53 +1,10 @@ import _ from "lodash"; -import AppNames from "../../utils/grab-app-names"; import { log } from "../../utils/log"; -import allPagesBundler from "../bundler/all-pages-bundler"; import serverParamsGen from "./server-params-gen"; -import watcher from "./watcher"; -import serverPostBuildFn from "./server-post-build-fn"; -import grabDirNames from "../../utils/grab-dir-names"; -import EJSON from "../../utils/ejson"; -import { readFileSync } from "fs"; -import cron from "./cron"; -const { HYDRATION_DST_DIR_MAP_JSON_FILE } = grabDirNames(); -export default async function startServer(params) { - const { name } = AppNames; +export default async function startServer() { const serverParams = await serverParamsGen(); - if (params?.dev) { - await allPagesBundler({ - watch: true, - post_build_fn: serverPostBuildFn, - }); - watcher(); - } - else { - const artifacts = EJSON.parse(readFileSync(HYDRATION_DST_DIR_MAP_JSON_FILE, "utf-8")); - if (!artifacts?.[0]) { - log.error("Please build first."); - process.exit(1); - } - global.BUNDLER_CTX_MAP = artifacts; - global.IS_FIRST_BUNDLE_READY = true; - cron(); - } - let bundle_ready_retries = 0; - const MAX_BUNDLE_READY_RETRIES = 10; - while (!global.IS_FIRST_BUNDLE_READY) { - if (bundle_ready_retries > MAX_BUNDLE_READY_RETRIES) { - log.error("Couldn't grab first bundle for dev environment"); - process.exit(1); - } - bundle_ready_retries++; - await Bun.sleep(500); - } const server = Bun.serve(serverParams); global.SERVER = server; log.server(`http://localhost:${server.port}`); - /** - * First Rebuild to Avoid errors - */ - if (params?.dev && global.BUNDLER_CTX) { - await global.BUNDLER_CTX.rebuild(); - } return server; } diff --git a/dist/functions/server/web-pages/write-hmr-tsx-module.d.ts b/dist/functions/server/web-pages/write-hmr-tsx-module.d.ts index 89d9813..a3e16cb 100644 --- a/dist/functions/server/web-pages/write-hmr-tsx-module.d.ts +++ b/dist/functions/server/web-pages/write-hmr-tsx-module.d.ts @@ -3,5 +3,5 @@ type Params = { tsx: string; out_file: string; }; -export default function writeHMRTsxModule({ tsx, out_file }: Params): Promise | undefined>; +export default function writeHMRTsxModule({ tsx, out_file }: Params): Promise | undefined>; export {}; diff --git a/dist/index.d.ts b/dist/index.d.ts index 5adcab7..07d2dd0 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -1,5 +1,17 @@ +import bunextInit from "./functions/bunext-init"; import bunextRequestHandler from "./functions/server/bunext-req-handler"; declare const bunext: { bunextRequestHandler: typeof bunextRequestHandler; + bunextLog: { + info: (msg: string, log?: any) => void; + success: (msg: string, log?: any) => void; + error: (msg: string | Error) => void; + warn: (msg: string) => void; + build: (msg: string) => void; + watch: (msg: string) => void; + server: (url: string) => void; + banner: () => void; + }; + bunextInit: typeof bunextInit; }; export default bunext; diff --git a/dist/index.js b/dist/index.js index e3bb4c9..2ca2c7c 100755 --- a/dist/index.js +++ b/dist/index.js @@ -1,5 +1,9 @@ +import bunextInit from "./functions/bunext-init"; import bunextRequestHandler from "./functions/server/bunext-req-handler"; +import { log } from "./utils/log"; const bunext = { bunextRequestHandler, + bunextLog: log, + bunextInit, }; export default bunext; diff --git a/dist/types/index.d.ts b/dist/types/index.d.ts index 21a095a..b07c3e5 100644 --- a/dist/types/index.d.ts +++ b/dist/types/index.d.ts @@ -1,4 +1,4 @@ -import type { MatchedRoute, Server } from "bun"; +import type { MatchedRoute, ServeOptions, Server, WebSocketHandler } from "bun"; import type { FC, JSX, ReactNode } from "react"; export type ServerProps = { params: Record; @@ -46,6 +46,8 @@ export type BunextConfig = { development?: boolean; middleware?: (params: BunextConfigMiddlewareParams) => Promise | Response | undefined; defaultCacheExpiry?: number; + websocket?: WebSocketHandler; + serverOptions?: ServeOptions; }; export type BunextConfigMiddlewareParams = { req: Request; diff --git a/examples/custom-server/server.ts b/examples/custom-server/server.ts new file mode 100644 index 0000000..84bbc1a --- /dev/null +++ b/examples/custom-server/server.ts @@ -0,0 +1,31 @@ +import bunext from "../../dist"; // => @moduletrace/bunext + +const development = process.env.NODE_ENV == "development"; +const port = process.env.PORT || 3700; + +/** + * Initialize Bunext + */ +await bunext.bunextInit(); + +/** + * Start your custom server + */ +const server = Bun.serve({ + routes: { + "/*": { + async GET(req) { + return await bunext.bunextRequestHandler({ req }); + }, + }, + }, + /** + * Set this to prevent HMR timeout warnings in the + * browser console in development mode. + */ + idleTimeout: development ? 0 : undefined, + development, + port, +}); + +bunext.bunextLog.info(`Server running on http://localhost:${server.port} ...`); diff --git a/examples/websocket/bunext.config.ts b/examples/websocket/bunext.config.ts new file mode 100644 index 0000000..655bcc0 --- /dev/null +++ b/examples/websocket/bunext.config.ts @@ -0,0 +1,8 @@ +import type { BunextConfig } from "../../dist/types"; +import { BunextWebsocket } from "./websocket"; + +const config: BunextConfig = { + websocket: BunextWebsocket, +}; + +export default config; diff --git a/examples/websocket/websocket.ts b/examples/websocket/websocket.ts new file mode 100644 index 0000000..6ed1adc --- /dev/null +++ b/examples/websocket/websocket.ts @@ -0,0 +1,7 @@ +import type { WebSocketHandler } from "bun"; + +export const BunextWebsocket: WebSocketHandler = { + message(ws, message) { + console.log(`WS Message => ${message}`); + }, +}; diff --git a/src/commands/build/index.ts b/src/commands/build/index.ts index 62e62f9..4456027 100644 --- a/src/commands/build/index.ts +++ b/src/commands/build/index.ts @@ -1,7 +1,4 @@ import { Command } from "commander"; -import grabConfig from "../../functions/grab-config"; -import init from "../../functions/init"; -import type { BunextConfig } from "../../types"; import allPagesBundler from "../../functions/bundler/all-pages-bundler"; import { log } from "../../utils/log"; @@ -9,19 +6,10 @@ export default function () { return new Command("build") .description("Build Project") .action(async () => { - log.banner(); - log.build("Building Project ..."); - process.env.NODE_ENV = "production"; + process.env.BUILD = "true"; - await init(); - - const config: BunextConfig = (await grabConfig()) || {}; - - global.CONFIG = { - ...config, - development: true, - }; + log.build("Building Project ..."); allPagesBundler({ exit_after_first_build: true, diff --git a/src/commands/dev/index.ts b/src/commands/dev/index.ts index 5f7b7e1..34a97c3 100644 --- a/src/commands/dev/index.ts +++ b/src/commands/dev/index.ts @@ -1,26 +1,18 @@ import { Command } from "commander"; -import grabConfig from "../../functions/grab-config"; import startServer from "../../functions/server/start-server"; -import init from "../../functions/init"; -import type { BunextConfig } from "../../types"; import { log } from "../../utils/log"; +import bunextInit from "../../functions/bunext-init"; export default function () { return new Command("dev") .description("Run development server") .action(async () => { - log.banner(); + process.env.NODE_ENV == "development"; + log.info("Running development server ..."); - await init(); + await bunextInit(); - const config: BunextConfig = (await grabConfig()) || {}; - - global.CONFIG = { - ...config, - development: true, - }; - - await startServer({ dev: true }); + await startServer(); }); } diff --git a/src/commands/index.ts b/src/commands/index.ts index 3e666e3..40bedb3 100755 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -3,58 +3,7 @@ import { program } from "commander"; import start from "./start"; import dev from "./dev"; -import ora, { type Ora } from "ora"; -import type { - BundlerCTXMap, - BunextConfig, - GlobalHMRControllerObject, - PageFiles, -} from "../types"; -import type { FileSystemRouter, Server } from "bun"; -import init from "../functions/init"; -import grabDirNames from "../utils/grab-dir-names"; import build from "./build"; -import type { BuildContext } from "esbuild"; -import type { FSWatcher } from "fs"; - -/** - * # Declare Global Variables - */ -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: GlobalHMRControllerObject[]; - var LAST_BUILD_TIME: number; - var BUNDLER_CTX: BuildContext | undefined; - var BUNDLER_CTX_MAP: BundlerCTXMap[] | undefined; - var IS_FIRST_BUNDLE_READY: boolean; - var BUNDLER_REBUILDS: 0; - var PAGES_SRC_WATCHER: FSWatcher | undefined; - var CURRENT_VERSION: string | undefined; - var PAGE_FILES: PageFiles[]; -} - -global.ORA_SPINNER = ora(); -global.ORA_SPINNER.clear(); -global.HMR_CONTROLLERS = []; -global.IS_FIRST_BUNDLE_READY = false; -global.BUNDLER_REBUILDS = 0; -global.PAGE_FILES = []; - -await init(); - -const { PAGES_DIR } = grabDirNames(); - -const router = new Bun.FileSystemRouter({ - style: "nextjs", - dir: PAGES_DIR, -}); - -global.ROUTER = router; /** * # Describe Program diff --git a/src/commands/start/index.ts b/src/commands/start/index.ts index ad0c0e4..0a0aeea 100644 --- a/src/commands/start/index.ts +++ b/src/commands/start/index.ts @@ -1,23 +1,17 @@ import { Command } from "commander"; -import grabConfig from "../../functions/grab-config"; import startServer from "../../functions/server/start-server"; -import init from "../../functions/init"; import { log } from "../../utils/log"; +import bunextInit from "../../functions/bunext-init"; export default function () { return new Command("start") .description("Start production server") .action(async () => { - log.banner(); - log.info("Starting production server ..."); - process.env.NODE_ENV = "production"; - await init(); + log.info("Starting production server ..."); - const config = await grabConfig(); - - global.CONFIG = { ...config }; + await bunextInit(); await startServer(); }); diff --git a/src/functions/bunext-init.ts b/src/functions/bunext-init.ts new file mode 100644 index 0000000..820e82a --- /dev/null +++ b/src/functions/bunext-init.ts @@ -0,0 +1,102 @@ +import ora, { type Ora } from "ora"; +import type { + BundlerCTXMap, + BunextConfig, + GlobalHMRControllerObject, + PageFiles, +} from "../types"; +import type { FileSystemRouter, Server } from "bun"; +import grabDirNames from "../utils/grab-dir-names"; +import type { BuildContext } from "esbuild"; +import { readFileSync, type FSWatcher } from "fs"; +import init from "./init"; +import isDevelopment from "../utils/is-development"; +import allPagesBundler from "./bundler/all-pages-bundler"; +import serverPostBuildFn from "./server/server-post-build-fn"; +import watcher from "./server/watcher"; +import EJSON from "../utils/ejson"; +import { log } from "../utils/log"; +import cron from "./server/cron"; + +/** + * # Declare Global Variables + */ +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: GlobalHMRControllerObject[]; + var LAST_BUILD_TIME: number; + var BUNDLER_CTX: BuildContext | undefined; + var BUNDLER_CTX_MAP: BundlerCTXMap[] | undefined; + var IS_FIRST_BUNDLE_READY: boolean; + var BUNDLER_REBUILDS: 0; + var PAGES_SRC_WATCHER: FSWatcher | undefined; + var CURRENT_VERSION: string | undefined; + var PAGE_FILES: PageFiles[]; +} + +export default async function bunextInit() { + log.banner(); + + global.ORA_SPINNER = ora(); + global.ORA_SPINNER.clear(); + global.HMR_CONTROLLERS = []; + global.IS_FIRST_BUNDLE_READY = false; + global.BUNDLER_REBUILDS = 0; + global.PAGE_FILES = []; + + await init(); + + const { PAGES_DIR, HYDRATION_DST_DIR_MAP_JSON_FILE } = grabDirNames(); + + const router = new Bun.FileSystemRouter({ + style: "nextjs", + dir: PAGES_DIR, + }); + + global.ROUTER = router; + + const is_dev = isDevelopment(); + + if (is_dev) { + await allPagesBundler({ + watch: true, + post_build_fn: serverPostBuildFn, + }); + watcher(); + } else { + const artifacts = EJSON.parse( + readFileSync(HYDRATION_DST_DIR_MAP_JSON_FILE, "utf-8"), + ) as BundlerCTXMap[] | undefined; + if (!artifacts?.[0]) { + log.error("Please build first."); + process.exit(1); + } + global.BUNDLER_CTX_MAP = artifacts; + global.IS_FIRST_BUNDLE_READY = true; + cron(); + } + + let bundle_ready_retries = 0; + const MAX_BUNDLE_READY_RETRIES = 10; + + while (!global.IS_FIRST_BUNDLE_READY) { + if (bundle_ready_retries > MAX_BUNDLE_READY_RETRIES) { + log.error("Couldn't grab first bundle for dev environment"); + process.exit(1); + } + bundle_ready_retries++; + await Bun.sleep(500); + } + + /** + * First Rebuild to Avoid errors + */ + if (is_dev && global.BUNDLER_CTX) { + await global.BUNDLER_CTX.rebuild(); + } +} diff --git a/src/functions/init.ts b/src/functions/init.ts index c0acab9..0f12b4e 100644 --- a/src/functions/init.ts +++ b/src/functions/init.ts @@ -1,7 +1,9 @@ -import { existsSync, mkdirSync, statSync, writeFileSync } from "fs"; +import { existsSync, mkdirSync, writeFileSync } from "fs"; import grabDirNames from "../utils/grab-dir-names"; import { execSync } from "child_process"; import path from "path"; +import grabConfig from "./grab-config"; +import type { BunextConfig } from "../types"; export default async function () { const dirNames = grabDirNames(); @@ -39,4 +41,11 @@ export default async function () { writeFileSync(dir, basicConfig); } } + + const config: BunextConfig = (await grabConfig()) || {}; + + global.CONFIG = { + ...config, + development: true, + }; } diff --git a/src/functions/server/server-params-gen.ts b/src/functions/server/server-params-gen.ts index 440c27e..20b2f7e 100644 --- a/src/functions/server/server-params-gen.ts +++ b/src/functions/server/server-params-gen.ts @@ -2,24 +2,23 @@ import type { ServeOptions } from "bun"; import grabAppPort from "../../utils/grab-app-port"; import isDevelopment from "../../utils/is-development"; import bunextRequestHandler from "./bunext-req-handler"; +import grabConfig from "../grab-config"; +import _ from "lodash"; -type Params = { - dev?: boolean; -}; - -export default async function (params?: Params): Promise { +export default async function (): Promise { const port = grabAppPort(); - const is_dev = isDevelopment(); + const development = isDevelopment(); + const config = await grabConfig(); return { async fetch(req, server) { return await bunextRequestHandler({ req }); }, port, - // idleTimeout: 0, - development: { - hmr: true, - }, + idleTimeout: development ? 0 : undefined, + development, + websocket: config?.websocket, + ..._.omit(config?.serverOptions || {}, ["fetch"]), } as ServeOptions; } diff --git a/src/functions/server/start-server.ts b/src/functions/server/start-server.ts index c52cd52..cd08ee6 100644 --- a/src/functions/server/start-server.ts +++ b/src/functions/server/start-server.ts @@ -1,70 +1,15 @@ import _ from "lodash"; -import AppNames from "../../utils/grab-app-names"; import { log } from "../../utils/log"; -import allPagesBundler from "../bundler/all-pages-bundler"; import serverParamsGen from "./server-params-gen"; -import watcher from "./watcher"; -import serverPostBuildFn from "./server-post-build-fn"; -import grabDirNames from "../../utils/grab-dir-names"; -import EJSON from "../../utils/ejson"; -import { readFileSync } from "fs"; -import type { BundlerCTXMap } from "../../types"; -import cron from "./cron"; - -const { HYDRATION_DST_DIR_MAP_JSON_FILE } = grabDirNames(); - -type Params = { - dev?: boolean; -}; - -export default async function startServer(params?: Params) { - const { name } = AppNames; +export default async function startServer() { const serverParams = await serverParamsGen(); - if (params?.dev) { - await allPagesBundler({ - watch: true, - post_build_fn: serverPostBuildFn, - }); - watcher(); - } else { - const artifacts = EJSON.parse( - readFileSync(HYDRATION_DST_DIR_MAP_JSON_FILE, "utf-8"), - ) as BundlerCTXMap[] | undefined; - if (!artifacts?.[0]) { - log.error("Please build first."); - process.exit(1); - } - global.BUNDLER_CTX_MAP = artifacts; - global.IS_FIRST_BUNDLE_READY = true; - cron(); - } - - let bundle_ready_retries = 0; - const MAX_BUNDLE_READY_RETRIES = 10; - - while (!global.IS_FIRST_BUNDLE_READY) { - if (bundle_ready_retries > MAX_BUNDLE_READY_RETRIES) { - log.error("Couldn't grab first bundle for dev environment"); - process.exit(1); - } - bundle_ready_retries++; - await Bun.sleep(500); - } - const server = Bun.serve(serverParams); global.SERVER = server; log.server(`http://localhost:${server.port}`); - /** - * First Rebuild to Avoid errors - */ - if (params?.dev && global.BUNDLER_CTX) { - await global.BUNDLER_CTX.rebuild(); - } - return server; } diff --git a/src/index.ts b/src/index.ts index cda7c80..2232f9c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,10 @@ +import bunextInit from "./functions/bunext-init"; import bunextRequestHandler from "./functions/server/bunext-req-handler"; +import { log } from "./utils/log"; const bunext = { bunextRequestHandler, + bunextLog: log, + bunextInit, }; export default bunext; diff --git a/src/types/index.ts b/src/types/index.ts index 6e3dfe7..54f9a08 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,4 +1,4 @@ -import type { MatchedRoute, Server } from "bun"; +import type { MatchedRoute, ServeOptions, Server, WebSocketHandler } from "bun"; import type { FC, JSX, ReactNode } from "react"; export type ServerProps = { @@ -52,6 +52,8 @@ export type BunextConfig = { params: BunextConfigMiddlewareParams, ) => Promise | Response | undefined; defaultCacheExpiry?: number; + websocket?: WebSocketHandler; + serverOptions?: ServeOptions; }; export type BunextConfigMiddlewareParams = {