diff --git a/README.md b/README.md index b6c058b..9defe4f 100644 --- a/README.md +++ b/README.md @@ -63,10 +63,10 @@ The goal is a framework that is: ## Installation -Install Bunext as a dependency in your project: +Install Bunext directly from GitHub: ```bash -bun add bunext +bun add github:moduletrace/bunext ``` Install required peer dependencies: diff --git a/dist/build/build.d.ts b/dist/build/build.d.ts new file mode 100644 index 0000000..c1cf237 --- /dev/null +++ b/dist/build/build.d.ts @@ -0,0 +1,2 @@ +#!/usr/bin/env bun +export {}; diff --git a/dist/build/compile.d.ts b/dist/build/compile.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/build/compile.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/dist/build/lightningcss-wasm-shim.d.ts b/dist/build/lightningcss-wasm-shim.d.ts new file mode 100644 index 0000000..732638e --- /dev/null +++ b/dist/build/lightningcss-wasm-shim.d.ts @@ -0,0 +1,2 @@ +import { transform as wasmTransform, transformStyleAttribute as wasmTransformStyleAttribute, bundle as wasmBundle, bundleAsync as wasmBundleAsync, Features, browserslistToTargets, composeVisitors } from "lightningcss-wasm"; +export { wasmTransform as transform, wasmTransformStyleAttribute as transformStyleAttribute, wasmBundle as bundle, wasmBundleAsync as bundleAsync, Features, browserslistToTargets, composeVisitors, }; diff --git a/dist/commands/build/index.d.ts b/dist/commands/build/index.d.ts new file mode 100644 index 0000000..f202e7d --- /dev/null +++ b/dist/commands/build/index.d.ts @@ -0,0 +1,2 @@ +import { Command } from "commander"; +export default function (): Command; diff --git a/dist/commands/dev/index.d.ts b/dist/commands/dev/index.d.ts new file mode 100644 index 0000000..f202e7d --- /dev/null +++ b/dist/commands/dev/index.d.ts @@ -0,0 +1,2 @@ +import { Command } from "commander"; +export default function (): Command; diff --git a/dist/commands/index.d.ts b/dist/commands/index.d.ts new file mode 100644 index 0000000..0a9ae6a --- /dev/null +++ b/dist/commands/index.d.ts @@ -0,0 +1,26 @@ +#!/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[]; +} diff --git a/dist/commands/start/index.d.ts b/dist/commands/start/index.d.ts new file mode 100644 index 0000000..f202e7d --- /dev/null +++ b/dist/commands/start/index.d.ts @@ -0,0 +1,2 @@ +import { Command } from "commander"; +export default function (): Command; diff --git a/dist/data/app-data.d.ts b/dist/data/app-data.d.ts new file mode 100644 index 0000000..a0cd258 --- /dev/null +++ b/dist/data/app-data.d.ts @@ -0,0 +1,7 @@ +export declare const AppData: { + readonly DefaultCacheExpiryTimeSeconds: number; + readonly DefaultCronInterval: 30000; + readonly BunextStaticFilesCacheExpiry: number; + readonly ClientHMRPath: "__bunext_client_hmr__"; + readonly BunextClientHydrationScriptID: "bunext-client-hydration-script"; +}; diff --git a/dist/functions/bundler/all-pages-bundler.d.ts b/dist/functions/bundler/all-pages-bundler.d.ts new file mode 100644 index 0000000..4a9a1a9 --- /dev/null +++ b/dist/functions/bundler/all-pages-bundler.d.ts @@ -0,0 +1,10 @@ +import type { BundlerCTXMap } from "../../types"; +type Params = { + watch?: boolean; + exit_after_first_build?: boolean; + post_build_fn?: (params: { + artifacts: BundlerCTXMap[]; + }) => Promise; +}; +export default function allPagesBundler(params?: Params): Promise; +export {}; diff --git a/dist/functions/bundler/all-pages-bundler.js b/dist/functions/bundler/all-pages-bundler.js index 349b1b0..93c30a0 100644 --- a/dist/functions/bundler/all-pages-bundler.js +++ b/dist/functions/bundler/all-pages-bundler.js @@ -56,6 +56,7 @@ export default async function allPagesBundler(params) { } const elapsed = (performance.now() - buildStart).toFixed(0); log.success(`[Built] in ${elapsed}ms`); + global.RECOMPILING = false; if (params?.exit_after_first_build) { process.exit(); } @@ -84,6 +85,6 @@ export default async function allPagesBundler(params) { await ctx.rebuild(); if (params?.watch) { global.BUNDLER_CTX = ctx; - global.BUNDLER_CTX.watch(); + // global.BUNDLER_CTX.watch(); } } diff --git a/dist/functions/bundler/grab-artifacts-from-bundled-result.d.ts b/dist/functions/bundler/grab-artifacts-from-bundled-result.d.ts new file mode 100644 index 0000000..d99e746 --- /dev/null +++ b/dist/functions/bundler/grab-artifacts-from-bundled-result.d.ts @@ -0,0 +1,8 @@ +import * as esbuild from "esbuild"; +import type { BundlerCTXMap, PageFiles } from "../../types"; +type Params = { + result: esbuild.BuildResult; + pages: PageFiles[]; +}; +export default function grabArtifactsFromBundledResults({ result, pages, }: Params): BundlerCTXMap[] | undefined; +export {}; diff --git a/dist/functions/bundler/grab-client-hydration-script.d.ts b/dist/functions/bundler/grab-client-hydration-script.d.ts new file mode 100644 index 0000000..9e12275 --- /dev/null +++ b/dist/functions/bundler/grab-client-hydration-script.d.ts @@ -0,0 +1,5 @@ +type Params = { + page_local_path: string; +}; +export default function grabClientHydrationScript({ page_local_path }: Params): string; +export {}; diff --git a/dist/functions/bundler/grab-client-hydration-script.js b/dist/functions/bundler/grab-client-hydration-script.js index d214a5b..e4c0b7c 100644 --- a/dist/functions/bundler/grab-client-hydration-script.js +++ b/dist/functions/bundler/grab-client-hydration-script.js @@ -38,15 +38,17 @@ export default function grabClientHydrationScript({ page_local_path }) { // txt += `window.__JSX_RUNTIME__ = JSXRuntime;\n\n`; txt += `const pageProps = window.__PAGE_PROPS__ || {};\n`; if (does_root_exist) { - txt += `const component = \n`; + txt += `const component = \n`; } else { - txt += `const component = \n`; + txt += `const component = \n`; } txt += `if (window.${ClientRootComponentWindowName}?.render) {\n`; txt += ` window.${ClientRootComponentWindowName}.render(component);\n`; txt += `} else {\n`; - txt += ` const root = hydrateRoot(document.getElementById("${ClientRootElementIDName}"), component);\n\n`; + txt += ` const root = hydrateRoot(document.getElementById("${ClientRootElementIDName}"), component, { onRecoverableError: () => {\n\n`; + txt += ` console.log(\`Hydration Error.\`)\n\n`; + txt += ` } });\n\n`; txt += ` window.${ClientRootComponentWindowName} = root;\n`; txt += ` window.__BUNEXT_RERENDER__ = (NewPage) => {\n`; txt += ` const props = window.__PAGE_PROPS__ || {};\n`; diff --git a/dist/functions/cache/get-cache.d.ts b/dist/functions/cache/get-cache.d.ts new file mode 100644 index 0000000..6bf4d06 --- /dev/null +++ b/dist/functions/cache/get-cache.d.ts @@ -0,0 +1,6 @@ +type Params = { + key: string; + paradigm?: "html" | "json"; +}; +export default function getCache({ key, paradigm }: Params): string | undefined; +export {}; diff --git a/dist/functions/cache/grab-cache-names.d.ts b/dist/functions/cache/grab-cache-names.d.ts new file mode 100644 index 0000000..c181c68 --- /dev/null +++ b/dist/functions/cache/grab-cache-names.d.ts @@ -0,0 +1,9 @@ +type Params = { + key: string; + paradigm?: "html" | "json"; +}; +export default function grabCacheNames({ key, paradigm }: Params): { + cache_name: string; + cache_meta_name: string; +}; +export {}; diff --git a/dist/functions/cache/trim-all-cache.d.ts b/dist/functions/cache/trim-all-cache.d.ts new file mode 100644 index 0000000..f47ebb6 --- /dev/null +++ b/dist/functions/cache/trim-all-cache.d.ts @@ -0,0 +1 @@ +export default function trimAllCache(): Promise; diff --git a/dist/functions/cache/trim-cache-key.d.ts b/dist/functions/cache/trim-cache-key.d.ts new file mode 100644 index 0000000..54b6b89 --- /dev/null +++ b/dist/functions/cache/trim-cache-key.d.ts @@ -0,0 +1,6 @@ +import type { APIResponseObject } from "../../types"; +type Params = { + key: string; +}; +export default function trimCacheKey({ key, }: Params): Promise; +export {}; diff --git a/dist/functions/cache/write-cache.d.ts b/dist/functions/cache/write-cache.d.ts new file mode 100644 index 0000000..4b44e2b --- /dev/null +++ b/dist/functions/cache/write-cache.d.ts @@ -0,0 +1,9 @@ +import type { APIResponseObject } from "../../types"; +type Params = { + key: string; + value: string; + paradigm?: "html" | "json"; + expiry_seconds?: number; +}; +export default function writeCache({ key, value, paradigm, expiry_seconds, }: Params): Promise; +export {}; diff --git a/dist/functions/grab-config.d.ts b/dist/functions/grab-config.d.ts new file mode 100644 index 0000000..e71903a --- /dev/null +++ b/dist/functions/grab-config.d.ts @@ -0,0 +1,2 @@ +import type { BunextConfig } from "../types"; +export default function grabConfig(): Promise; diff --git a/dist/functions/init.d.ts b/dist/functions/init.d.ts new file mode 100644 index 0000000..83a22b1 --- /dev/null +++ b/dist/functions/init.d.ts @@ -0,0 +1 @@ +export default function (): Promise; diff --git a/dist/functions/server/bunext-req-handler.d.ts b/dist/functions/server/bunext-req-handler.d.ts new file mode 100644 index 0000000..da210f8 --- /dev/null +++ b/dist/functions/server/bunext-req-handler.d.ts @@ -0,0 +1,5 @@ +type Params = { + req: Request; +}; +export default function bunextRequestHandler({ req, }: Params): Promise; +export {}; diff --git a/dist/functions/server/bunext-req-handler.js b/dist/functions/server/bunext-req-handler.js new file mode 100644 index 0000000..f670fef --- /dev/null +++ b/dist/functions/server/bunext-req-handler.js @@ -0,0 +1,57 @@ +import grabAppPort from "../../utils/grab-app-port"; +import handleWebPages from "./web-pages/handle-web-pages"; +import handleRoutes from "./handle-routes"; +import isDevelopment from "../../utils/is-development"; +import grabConstants from "../../utils/grab-constants"; +import { AppData } from "../../data/app-data"; +import handleHmr from "./handle-hmr"; +import handleHmrUpdate from "./handle-hmr-update"; +import handlePublic from "./handle-public"; +import handleFiles from "./handle-files"; +export default async function bunextRequestHandler({ req, }) { + const is_dev = isDevelopment(); + try { + const url = new URL(req.url); + const { config } = grabConstants(); + let response = undefined; + if (config?.middleware) { + const middleware_res = await config.middleware({ + req, + url, + }); + if (typeof middleware_res == "object") { + return middleware_res; + } + } + if (url.pathname == `/${AppData["ClientHMRPath"]}`) { + response = await handleHmrUpdate({ req }); + } + else if (url.pathname === "/__hmr" && is_dev) { + response = await handleHmr({ req }); + } + else if (url.pathname.startsWith("/api/")) { + response = await handleRoutes({ req }); + } + else if (url.pathname.startsWith("/public/")) { + response = await handlePublic({ req }); + } + else if (url.pathname.match(/\..*$/)) { + response = await handleFiles({ req }); + } + else { + response = await handleWebPages({ req }); + } + if (!response) { + throw new Error(`No Response generated`); + } + if (is_dev) { + response.headers.set("Cache-Control", "no-cache, no-store, must-revalidate"); + } + return response; + } + catch (error) { + return new Response(`Server Error: ${error.message}`, { + status: 500, + }); + } +} diff --git a/dist/functions/server/cron.d.ts b/dist/functions/server/cron.d.ts new file mode 100644 index 0000000..3df8e61 --- /dev/null +++ b/dist/functions/server/cron.d.ts @@ -0,0 +1 @@ +export default function cron(): Promise; diff --git a/dist/functions/server/handle-files.d.ts b/dist/functions/server/handle-files.d.ts new file mode 100644 index 0000000..9130e3e --- /dev/null +++ b/dist/functions/server/handle-files.d.ts @@ -0,0 +1,5 @@ +type Params = { + req: Request; +}; +export default function ({ req }: Params): Promise; +export {}; diff --git a/dist/functions/server/handle-files.js b/dist/functions/server/handle-files.js index e578e42..6b84ee7 100644 --- a/dist/functions/server/handle-files.js +++ b/dist/functions/server/handle-files.js @@ -3,7 +3,7 @@ import path from "path"; import isDevelopment from "../../utils/is-development"; import { existsSync } from "fs"; const { PUBLIC_DIR } = grabDirNames(); -export default async function ({ req, server }) { +export default async function ({ req }) { try { const is_dev = isDevelopment(); const url = new URL(req.url); diff --git a/dist/functions/server/handle-hmr-update.d.ts b/dist/functions/server/handle-hmr-update.d.ts new file mode 100644 index 0000000..9130e3e --- /dev/null +++ b/dist/functions/server/handle-hmr-update.d.ts @@ -0,0 +1,5 @@ +type Params = { + req: Request; +}; +export default function ({ req }: Params): Promise; +export {}; diff --git a/dist/functions/server/handle-hmr-update.js b/dist/functions/server/handle-hmr-update.js index 542ed08..08f223a 100644 --- a/dist/functions/server/handle-hmr-update.js +++ b/dist/functions/server/handle-hmr-update.js @@ -4,8 +4,8 @@ import path from "path"; import grabRootFile from "./web-pages/grab-root-file"; import grabPageBundledReactComponent from "./web-pages/grab-page-bundled-react-component"; import writeHMRTsxModule from "./web-pages/write-hmr-tsx-module"; -const { PUBLIC_DIR, BUNX_HYDRATION_SRC_DIR } = grabDirNames(); -export default async function ({ req, server }) { +const { BUNX_HYDRATION_SRC_DIR } = grabDirNames(); +export default async function ({ req }) { try { const url = new URL(req.url); const target_href = url.searchParams.get("href"); diff --git a/dist/functions/server/handle-hmr.d.ts b/dist/functions/server/handle-hmr.d.ts new file mode 100644 index 0000000..9130e3e --- /dev/null +++ b/dist/functions/server/handle-hmr.d.ts @@ -0,0 +1,5 @@ +type Params = { + req: Request; +}; +export default function ({ req }: Params): Promise; +export {}; diff --git a/dist/functions/server/handle-hmr.js b/dist/functions/server/handle-hmr.js index 555097b..9a45446 100644 --- a/dist/functions/server/handle-hmr.js +++ b/dist/functions/server/handle-hmr.js @@ -1,7 +1,7 @@ import grabRouteParams from "../../utils/grab-route-params"; import grabConstants from "../../utils/grab-constants"; import grabRouter from "../../utils/grab-router"; -export default async function ({ req, server }) { +export default async function ({ req }) { const referer_url = new URL(req.headers.get("referer") || ""); const match = global.ROUTER.match(referer_url.pathname); const target_map = match?.filePath diff --git a/dist/functions/server/handle-public.d.ts b/dist/functions/server/handle-public.d.ts new file mode 100644 index 0000000..9130e3e --- /dev/null +++ b/dist/functions/server/handle-public.d.ts @@ -0,0 +1,5 @@ +type Params = { + req: Request; +}; +export default function ({ req }: Params): Promise; +export {}; diff --git a/dist/functions/server/handle-public.js b/dist/functions/server/handle-public.js index 1350519..0ff2ba9 100644 --- a/dist/functions/server/handle-public.js +++ b/dist/functions/server/handle-public.js @@ -2,8 +2,8 @@ import grabDirNames from "../../utils/grab-dir-names"; import path from "path"; import isDevelopment from "../../utils/is-development"; import { existsSync } from "fs"; -const { PUBLIC_DIR, BUNX_HYDRATION_SRC_DIR } = grabDirNames(); -export default async function ({ req, server }) { +const { PUBLIC_DIR } = grabDirNames(); +export default async function ({ req }) { try { const is_dev = isDevelopment(); const url = new URL(req.url); diff --git a/dist/functions/server/handle-routes.d.ts b/dist/functions/server/handle-routes.d.ts new file mode 100644 index 0000000..9130e3e --- /dev/null +++ b/dist/functions/server/handle-routes.d.ts @@ -0,0 +1,5 @@ +type Params = { + req: Request; +}; +export default function ({ req }: Params): Promise; +export {}; diff --git a/dist/functions/server/handle-routes.js b/dist/functions/server/handle-routes.js index 8a8b92a..81c8c46 100644 --- a/dist/functions/server/handle-routes.js +++ b/dist/functions/server/handle-routes.js @@ -1,8 +1,10 @@ import grabRouteParams from "../../utils/grab-route-params"; import grabConstants from "../../utils/grab-constants"; import grabRouter from "../../utils/grab-router"; -export default async function ({ req, server }) { +import isDevelopment from "../../utils/is-development"; +export default async function ({ req }) { const url = new URL(req.url); + const is_dev = isDevelopment(); const { MBInBytes, ServerDefaultRequestBodyLimitBytes } = grabConstants(); const router = grabRouter(); const match = router.match(url.pathname); @@ -19,7 +21,9 @@ export default async function ({ req, server }) { }); } const routeParams = await grabRouteParams({ req }); - const module = await import(match.filePath); + const now = Date.now(); + const import_path = is_dev ? `${match.filePath}?t=${now}` : match.filePath; + const module = await import(import_path); const config = module.config; const contentLength = req.headers.get("content-length"); if (contentLength) { @@ -40,7 +44,9 @@ export default async function ({ req, server }) { } const res = await module["default"]({ ...routeParams, - server, }); + if (is_dev) { + res.headers.set("Cache-Control", "no-cache, no-store, must-revalidate"); + } return res; } diff --git a/dist/functions/server/rebuild-bundler.d.ts b/dist/functions/server/rebuild-bundler.d.ts new file mode 100644 index 0000000..a401ae9 --- /dev/null +++ b/dist/functions/server/rebuild-bundler.d.ts @@ -0,0 +1 @@ +export default function rebuildBundler(): Promise; diff --git a/dist/functions/server/server-params-gen.d.ts b/dist/functions/server/server-params-gen.d.ts new file mode 100644 index 0000000..70a01fd --- /dev/null +++ b/dist/functions/server/server-params-gen.d.ts @@ -0,0 +1,6 @@ +import type { ServeOptions } from "bun"; +type Params = { + dev?: boolean; +}; +export default function (params?: Params): Promise; +export {}; diff --git a/dist/functions/server/server-params-gen.js b/dist/functions/server/server-params-gen.js index 2671b1a..6d56e6f 100644 --- a/dist/functions/server/server-params-gen.js +++ b/dist/functions/server/server-params-gen.js @@ -1,66 +1,15 @@ import grabAppPort from "../../utils/grab-app-port"; -import handleWebPages from "./web-pages/handle-web-pages"; -import handleRoutes from "./handle-routes"; import isDevelopment from "../../utils/is-development"; -import grabConstants from "../../utils/grab-constants"; -import { AppData } from "../../data/app-data"; -import handleHmr from "./handle-hmr"; -import handleHmrUpdate from "./handle-hmr-update"; -import handlePublic from "./handle-public"; -import handleFiles from "./handle-files"; +import bunextRequestHandler from "./bunext-req-handler"; export default async function (params) { const port = grabAppPort(); const is_dev = isDevelopment(); return { async fetch(req, server) { - try { - const url = new URL(req.url); - const { config } = grabConstants(); - let response = undefined; - if (config?.middleware) { - const middleware_res = await config.middleware({ - req, - url, - server, - }); - if (typeof middleware_res == "object") { - return middleware_res; - } - } - if (url.pathname == `/${AppData["ClientHMRPath"]}`) { - response = await handleHmrUpdate({ req, server }); - } - else if (url.pathname === "/__hmr" && is_dev) { - response = await handleHmr({ req, server }); - } - else if (url.pathname.startsWith("/api/")) { - response = await handleRoutes({ req, server }); - } - else if (url.pathname.startsWith("/public/")) { - response = await handlePublic({ req, server }); - } - else if (url.pathname.match(/\..*$/)) { - response = await handleFiles({ req, server }); - } - else { - response = await handleWebPages({ req }); - } - if (!response) { - throw new Error(`No Response generated`); - } - if (is_dev) { - response.headers.set("Cache-Control", "no-cache, no-store, must-revalidate"); - } - return response; - } - catch (error) { - return new Response(`Server Error: ${error.message}`, { - status: 500, - }); - } + return await bunextRequestHandler({ req }); }, port, - idleTimeout: 0, + // idleTimeout: 0, development: { hmr: true, }, diff --git a/dist/functions/server/server-post-build-fn.d.ts b/dist/functions/server/server-post-build-fn.d.ts new file mode 100644 index 0000000..6437337 --- /dev/null +++ b/dist/functions/server/server-post-build-fn.d.ts @@ -0,0 +1,6 @@ +import type { BundlerCTXMap } from "../../types"; +type Params = { + artifacts: BundlerCTXMap[]; +}; +export default function serverPostBuildFn({ artifacts }: Params): Promise; +export {}; diff --git a/dist/functions/server/start-server.d.ts b/dist/functions/server/start-server.d.ts new file mode 100644 index 0000000..2b36c1c --- /dev/null +++ b/dist/functions/server/start-server.d.ts @@ -0,0 +1,5 @@ +type Params = { + dev?: boolean; +}; +export default function startServer(params?: Params): Promise; +export {}; diff --git a/dist/functions/server/start-server.js b/dist/functions/server/start-server.js index d331752..bd42b9b 100644 --- a/dist/functions/server/start-server.js +++ b/dist/functions/server/start-server.js @@ -43,5 +43,11 @@ export default async function startServer(params) { 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/watcher.d.ts b/dist/functions/server/watcher.d.ts new file mode 100644 index 0000000..ae39fb1 --- /dev/null +++ b/dist/functions/server/watcher.d.ts @@ -0,0 +1 @@ +export default function watcher(): void; diff --git a/dist/functions/server/watcher.js b/dist/functions/server/watcher.js index 0e6b109..653a00d 100644 --- a/dist/functions/server/watcher.js +++ b/dist/functions/server/watcher.js @@ -11,8 +11,16 @@ export default function watcher() { }, async (event, filename) => { if (!filename) return; - if (event !== "rename") + if (event !== "rename") { + if (filename.match(/\.(tsx?|jsx?|css)$/) && + global.BUNDLER_CTX) { + if (global.RECOMPILING) + return; + global.RECOMPILING = true; + await global.BUNDLER_CTX.rebuild(); + } return; + } if (!filename.match(/^pages\//)) return; if (filename.match(/\/(--|\()/)) diff --git a/dist/functions/server/web-pages/generate-web-html.d.ts b/dist/functions/server/web-pages/generate-web-html.d.ts new file mode 100644 index 0000000..2825167 --- /dev/null +++ b/dist/functions/server/web-pages/generate-web-html.d.ts @@ -0,0 +1,2 @@ +import type { LivePageDistGenParams } from "../../../types"; +export default function genWebHTML({ component, pageProps, bundledMap, head: Head, module, meta, routeParams, debug, }: LivePageDistGenParams): Promise; diff --git a/dist/functions/server/web-pages/generate-web-page-response-from-component-return.d.ts b/dist/functions/server/web-pages/generate-web-page-response-from-component-return.d.ts new file mode 100644 index 0000000..f174f3d --- /dev/null +++ b/dist/functions/server/web-pages/generate-web-page-response-from-component-return.d.ts @@ -0,0 +1,2 @@ +import type { GrabPageComponentRes } from "../../../types"; +export default function generateWebPageResponseFromComponentReturn({ component, module, bundledMap, head, meta, routeParams, serverRes, debug, }: GrabPageComponentRes): Promise; diff --git a/dist/functions/server/web-pages/grab-file-path-module.d.ts b/dist/functions/server/web-pages/grab-file-path-module.d.ts new file mode 100644 index 0000000..664e86e --- /dev/null +++ b/dist/functions/server/web-pages/grab-file-path-module.d.ts @@ -0,0 +1,6 @@ +type Params = { + file_path: string; + out_file?: string; +}; +export default function grabFilePathModule({ file_path, out_file, }: Params): Promise; +export {}; diff --git a/dist/functions/server/web-pages/grab-page-bundled-react-component.d.ts b/dist/functions/server/web-pages/grab-page-bundled-react-component.d.ts new file mode 100644 index 0000000..403acbe --- /dev/null +++ b/dist/functions/server/web-pages/grab-page-bundled-react-component.d.ts @@ -0,0 +1,8 @@ +import type { GrabPageReactBundledComponentRes } from "../../../types"; +type Params = { + file_path: string; + root_file?: string; + server_res?: any; +}; +export default function grabPageBundledReactComponent({ file_path, root_file, server_res, }: Params): Promise; +export {}; diff --git a/dist/functions/server/web-pages/grab-page-bundled-react-component.js b/dist/functions/server/web-pages/grab-page-bundled-react-component.js index 50c437d..cf7f8ad 100644 --- a/dist/functions/server/web-pages/grab-page-bundled-react-component.js +++ b/dist/functions/server/web-pages/grab-page-bundled-react-component.js @@ -4,13 +4,13 @@ import grabTsxStringModule from "./grab-tsx-string-module"; export default async function grabPageBundledReactComponent({ file_path, root_file, server_res, }) { try { let tsx = ``; - const server_res_json = EJSON.stringify(server_res || {})?.replace(/"/g, '\\"'); + const server_res_json = JSON.stringify(EJSON.stringify(server_res || {}) ?? "{}"); if (root_file) { tsx += `import Root from "${root_file}"\n`; } tsx += `import Page from "${file_path}"\n`; tsx += `export default function Main() {\n\n`; - tsx += `const props = JSON.parse("${server_res_json}")\n\n`; + tsx += `const props = JSON.parse(${server_res_json})\n\n`; tsx += ` return (\n`; if (root_file) { tsx += ` \n`; diff --git a/dist/functions/server/web-pages/grab-page-component.d.ts b/dist/functions/server/web-pages/grab-page-component.d.ts new file mode 100644 index 0000000..b9756bc --- /dev/null +++ b/dist/functions/server/web-pages/grab-page-component.d.ts @@ -0,0 +1,8 @@ +import type { GrabPageComponentRes } from "../../../types"; +type Params = { + req?: Request; + file_path?: string; + debug?: boolean; +}; +export default function grabPageComponent({ req, file_path: passed_file_path, debug, }: Params): Promise; +export {}; diff --git a/dist/functions/server/web-pages/grab-page-component.js b/dist/functions/server/web-pages/grab-page-component.js index 595a082..fa9d42d 100644 --- a/dist/functions/server/web-pages/grab-page-component.js +++ b/dist/functions/server/web-pages/grab-page-component.js @@ -9,6 +9,7 @@ class NotFoundError extends Error { export default async function grabPageComponent({ req, file_path: passed_file_path, debug, }) { const url = req?.url ? new URL(req.url) : undefined; const router = global.ROUTER; + const now = Date.now(); let routeParams = undefined; try { routeParams = req ? await grabRouteParams({ req }) : undefined; @@ -42,7 +43,7 @@ export default async function grabPageComponent({ req, file_path: passed_file_pa log.info(`bundledMap:`, bundledMap); } const { root_file } = grabRootFile(); - const module = await import(file_path); + const module = await import(`${file_path}?t=${now}`); if (debug) { log.info(`module:`, module); } @@ -68,7 +69,10 @@ export default async function grabPageComponent({ req, file_path: passed_file_pa }; try { if (routeParams) { - const serverData = await module["server"]?.(routeParams); + const serverData = await module["server"]?.({ + ...routeParams, + query: { ...routeParams.query, ...match?.query }, + }); return { ...serverData, ...default_props, diff --git a/dist/functions/server/web-pages/grab-page-error-component.d.ts b/dist/functions/server/web-pages/grab-page-error-component.d.ts new file mode 100644 index 0000000..ac44258 --- /dev/null +++ b/dist/functions/server/web-pages/grab-page-error-component.d.ts @@ -0,0 +1,8 @@ +import type { BunxRouteParams, GrabPageComponentRes } from "../../../types"; +type Params = { + error?: any; + routeParams?: BunxRouteParams; + is404?: boolean; +}; +export default function grabPageErrorComponent({ error, routeParams, is404, }: Params): Promise; +export {}; diff --git a/dist/functions/server/web-pages/grab-root-file.d.ts b/dist/functions/server/web-pages/grab-root-file.d.ts new file mode 100644 index 0000000..1c68fd8 --- /dev/null +++ b/dist/functions/server/web-pages/grab-root-file.d.ts @@ -0,0 +1,3 @@ +export default function grabRootFile(): { + root_file: string | undefined; +}; diff --git a/dist/functions/server/web-pages/grab-tsx-string-module.d.ts b/dist/functions/server/web-pages/grab-tsx-string-module.d.ts new file mode 100644 index 0000000..cb0073e --- /dev/null +++ b/dist/functions/server/web-pages/grab-tsx-string-module.d.ts @@ -0,0 +1,6 @@ +type Params = { + tsx: string; + file_path: string; +}; +export default function grabTsxStringModule({ tsx, file_path, }: Params): Promise; +export {}; diff --git a/dist/functions/server/web-pages/grab-web-meta-html.d.ts b/dist/functions/server/web-pages/grab-web-meta-html.d.ts new file mode 100644 index 0000000..9d90be8 --- /dev/null +++ b/dist/functions/server/web-pages/grab-web-meta-html.d.ts @@ -0,0 +1,6 @@ +import type { BunextPageModuleMeta } from "../../../types"; +type Params = { + meta: BunextPageModuleMeta; +}; +export default function grabWebMetaHTML({ meta }: Params): string; +export {}; diff --git a/dist/functions/server/web-pages/grab-web-page-hydration-script.d.ts b/dist/functions/server/web-pages/grab-web-page-hydration-script.d.ts new file mode 100644 index 0000000..94fd282 --- /dev/null +++ b/dist/functions/server/web-pages/grab-web-page-hydration-script.d.ts @@ -0,0 +1,6 @@ +import type { BundlerCTXMap } from "../../../types"; +type Params = { + bundledMap?: BundlerCTXMap; +}; +export default function ({ bundledMap }: Params): Promise; +export {}; diff --git a/dist/functions/server/web-pages/grab-web-page-hydration-script.js b/dist/functions/server/web-pages/grab-web-page-hydration-script.js index 0ddd82f..5460992 100644 --- a/dist/functions/server/web-pages/grab-web-page-hydration-script.js +++ b/dist/functions/server/web-pages/grab-web-page-hydration-script.js @@ -3,8 +3,9 @@ export default async function ({ bundledMap }) { let script = ""; script += `console.log(\`Development Environment\`);\n\n`; script += `const hmr = new EventSource("/__hmr");\n`; + script += `window.addEventListener("beforeunload", () => hmr.close());\n`; script += `hmr.addEventListener("update", async (event) => {\n`; - script += ` if (event.data) {\n`; + script += ` if (event?.data) {\n`; script += ` console.log(\`HMR Changes Detected. Updating ...\`);\n`; script += ` try {\n`; script += ` const data = JSON.parse(event.data);\n`; diff --git a/dist/functions/server/web-pages/handle-web-pages.d.ts b/dist/functions/server/web-pages/handle-web-pages.d.ts new file mode 100644 index 0000000..a1c9b07 --- /dev/null +++ b/dist/functions/server/web-pages/handle-web-pages.d.ts @@ -0,0 +1,5 @@ +type Params = { + req: Request; +}; +export default function handleWebPages({ req, }: Params): Promise; +export {}; diff --git a/dist/functions/server/web-pages/tailwind-esbuild-plugin.d.ts b/dist/functions/server/web-pages/tailwind-esbuild-plugin.d.ts new file mode 100644 index 0000000..baff1e5 --- /dev/null +++ b/dist/functions/server/web-pages/tailwind-esbuild-plugin.d.ts @@ -0,0 +1,3 @@ +import * as esbuild from "esbuild"; +declare const tailwindEsbuildPlugin: esbuild.Plugin; +export default tailwindEsbuildPlugin; 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 new file mode 100644 index 0000000..89d9813 --- /dev/null +++ b/dist/functions/server/web-pages/write-hmr-tsx-module.d.ts @@ -0,0 +1,7 @@ +import type { BundlerCTXMap } from "../../../types"; +type Params = { + tsx: string; + out_file: string; +}; +export default function writeHMRTsxModule({ tsx, out_file }: Params): Promise | undefined>; +export {}; diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..5adcab7 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,5 @@ +import bunextRequestHandler from "./functions/server/bunext-req-handler"; +declare const bunext: { + bunextRequestHandler: typeof bunextRequestHandler; +}; +export default bunext; diff --git a/dist/index.js b/dist/index.js index c82740f..e3bb4c9 100755 --- a/dist/index.js +++ b/dist/index.js @@ -1,2 +1,5 @@ -const bunext = {}; +import bunextRequestHandler from "./functions/server/bunext-req-handler"; +const bunext = { + bunextRequestHandler, +}; export default bunext; diff --git a/dist/presets/bunext.config.d.ts b/dist/presets/bunext.config.d.ts new file mode 100644 index 0000000..5a9aa6d --- /dev/null +++ b/dist/presets/bunext.config.d.ts @@ -0,0 +1,2 @@ +declare const config: {}; +export default config; diff --git a/dist/presets/not-found.d.ts b/dist/presets/not-found.d.ts new file mode 100644 index 0000000..3e255d7 --- /dev/null +++ b/dist/presets/not-found.d.ts @@ -0,0 +1,2 @@ +import type { PropsWithChildren } from "react"; +export default function DefaultNotFoundPage({ children }: PropsWithChildren): import("react/jsx-runtime").JSX.Element; diff --git a/dist/presets/server-error.d.ts b/dist/presets/server-error.d.ts new file mode 100644 index 0000000..1397a23 --- /dev/null +++ b/dist/presets/server-error.d.ts @@ -0,0 +1,2 @@ +import type { PropsWithChildren } from "react"; +export default function DefaultServerErrorPage({ children, }: PropsWithChildren): import("react/jsx-runtime").JSX.Element; diff --git a/dist/types/index.d.ts b/dist/types/index.d.ts new file mode 100644 index 0000000..21a095a --- /dev/null +++ b/dist/types/index.d.ts @@ -0,0 +1,260 @@ +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; + middleware?: (params: BunextConfigMiddlewareParams) => Promise | Response | undefined; + defaultCacheExpiry?: number; +}; +export type BunextConfigMiddlewareParams = { + req: Request; + url: URL; +}; +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; + body?: any; + query?: any; + /** + * Intercept and Transform the response object + */ + resTransform?: (res: Response) => Promise | Response; + server?: Server; +}; +export interface PostInsertReturn { + fieldCount?: number; + affectedRows?: number; + insertId?: number; + serverStatus?: number; + warningCount?: number; + message?: string; + protocol41?: boolean; + changedRows?: number; +} +export type APIResponseObject = { + 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 = { + pageName: string; + page_file: string; +}; +export type LivePageDistGenParams = { + component: ReactNode; + head?: FC; + pageProps?: any; + module?: BunextPageModule; + bundledMap?: BundlerCTXMap; + meta?: BunextPageModuleMeta; + routeParams?: BunxRouteParams; + debug?: boolean; +}; +export type BunextPageHeadFCProps = { + serverRes: BunextPageModuleServerReturn; + ctx?: BunxRouteParams; +}; +export type BunextPageModule = { + default: FC; + server?: BunextPageServerFn; + meta?: BunextPageModuleMeta | BunextPageModuleMetaFn; + Head?: FC; + config?: BunextRouteConfig; +}; +export type BunextPageModuleMetaFn = (params: { + ctx: BunxRouteParams; + serverRes?: BunextPageModuleServerReturn; +}) => Promise; +export type BunextPageModuleMeta = { + title?: string; + description?: string; + keywords?: string | string[]; + author?: string; + robots?: string; + canonical?: string; + themeColor?: string; + og?: { + title?: string; + description?: string; + image?: string; + url?: string; + type?: string; + siteName?: string; + locale?: string; + }; + twitter?: { + card?: "summary" | "summary_large_image" | "app" | "player"; + title?: string; + description?: string; + image?: string; + site?: string; + creator?: string; + }; +}; +export type BunextPageServerFn = (ctx: Omit) => Promise>; +export type BunextRouteConfig = { + cachePage?: boolean; + /** + * Expiry time of the cache in seconds + */ + cacheExpiry?: number; +}; +export type BunextPageModuleServerReturn = { + props?: T; + query?: Q; + redirect?: BunextPageModuleServerRedirect; + responseOptions?: ResponseInit; + cachePage?: boolean; + /** + * Expiry time of the cache in seconds + */ + cacheExpiry?: number; + url?: BunextPageModuleServerReturnURLObject; +}; +export type BunextPageModuleServerReturnURLObject = URL & {}; +export type BunextPageModuleServerRedirect = { + destination: string; + permanent?: boolean; + status_code?: number; +}; +export type BunextPageModuleMetadata = { + title?: string; + description?: string; +}; +export type GrabPageComponentRes = { + component: JSX.Element; + serverRes?: BunextPageModuleServerReturn; + routeParams?: BunxRouteParams; + bundledMap?: BundlerCTXMap; + module: BunextPageModule; + meta?: BunextPageModuleMeta; + head?: FC; + debug?: boolean; +}; +export type GrabPageReactBundledComponentRes = { + component: JSX.Element; + server_res?: BunextPageModuleServerReturn; + tsx?: string; +}; +export type PageFiles = { + local_path: string; + url_path: string; + file_name: string; +}; +export type BundlerCTXMap = { + path: string; + hash: string; + type: string; + entrypoint: string; + local_path: string; + url_path: string; + file_name: string; + css_path?: string; +}; +export type GlobalHMRControllerObject = { + controller: ReadableStreamDefaultController; + page_url: string; + target_map?: BundlerCTXMap; +}; +export type BunextCacheFileMeta = { + date_created: number; + paradigm: "html" | "json"; + expiry_seconds?: number; +}; diff --git a/dist/utils/bundle.d.ts b/dist/utils/bundle.d.ts new file mode 100644 index 0000000..b5d3ae0 --- /dev/null +++ b/dist/utils/bundle.d.ts @@ -0,0 +1,31 @@ +import { type ExecSyncOptions } from "child_process"; +declare const BuildKeys: readonly [{ + readonly key: "production"; +}, { + readonly key: "bytecode"; +}, { + readonly key: "conditions"; +}, { + readonly key: "format"; +}, { + readonly key: "root"; +}, { + readonly key: "splitting"; +}, { + readonly key: "cdd-chunking"; +}]; +type Params = { + src: string; + out_dir: string; + entry_naming?: string; + minify?: boolean; + exec_options?: ExecSyncOptions; + debug?: boolean; + sourcemap?: boolean; + target?: "browser" | "node" | "bun"; + build_options?: { + [k in (typeof BuildKeys)[number]["key"]]: string | boolean; + }; +}; +export default function bundle({ out_dir, src, minify, exec_options, debug, entry_naming, sourcemap, target, build_options, }: Params): void; +export {}; diff --git a/dist/utils/deserialize-query.d.ts b/dist/utils/deserialize-query.d.ts new file mode 100644 index 0000000..d0930b8 --- /dev/null +++ b/dist/utils/deserialize-query.d.ts @@ -0,0 +1,8 @@ +/** + * # Convert Serialized Query back to object + */ +export default function deserializeQuery(query: string | { + [s: string]: any; +}): { + [s: string]: any; +}; diff --git a/dist/utils/ejson.d.ts b/dist/utils/ejson.d.ts new file mode 100644 index 0000000..4966a4a --- /dev/null +++ b/dist/utils/ejson.d.ts @@ -0,0 +1,17 @@ +/** + * # EJSON parse string + */ +declare function parse(string: string | null | number, reviver?: (this: any, key: string, value: any) => any): { + [s: string]: any; +} | { + [s: string]: any; +}[] | undefined; +/** + * # EJSON stringify object + */ +declare function stringify(value: any, replacer?: ((this: any, key: string, value: any) => any) | null, space?: string | number): string | undefined; +declare const EJSON: { + parse: typeof parse; + stringify: typeof stringify; +}; +export default EJSON; diff --git a/dist/utils/exit-with-error.d.ts b/dist/utils/exit-with-error.d.ts new file mode 100644 index 0000000..ca8e9db --- /dev/null +++ b/dist/utils/exit-with-error.d.ts @@ -0,0 +1 @@ +export default function exitWithError(msg: string, code?: number): void; diff --git a/dist/utils/grab-all-pages.d.ts b/dist/utils/grab-all-pages.d.ts new file mode 100644 index 0000000..436fd54 --- /dev/null +++ b/dist/utils/grab-all-pages.d.ts @@ -0,0 +1,6 @@ +import type { PageFiles } from "../types"; +type Params = { + exclude_api?: boolean; +}; +export default function grabAllPages(params?: Params): PageFiles[]; +export {}; diff --git a/dist/utils/grab-all-pages.js b/dist/utils/grab-all-pages.js index ae7355c..fb10ba1 100644 --- a/dist/utils/grab-all-pages.js +++ b/dist/utils/grab-all-pages.js @@ -26,7 +26,7 @@ function grabPageDirRecursively({ page_dir }) { if (page.match(new RegExp(`${AppNames["RootPagesComponentName"]}`))) { continue; } - if (page.match(/\(|\)|--/)) { + if (page.match(/\(|\)|--|\/api\//)) { continue; } const page_stat = statSync(full_page_path); diff --git a/dist/utils/grab-app-names.d.ts b/dist/utils/grab-app-names.d.ts new file mode 100644 index 0000000..2bb9e1e --- /dev/null +++ b/dist/utils/grab-app-names.d.ts @@ -0,0 +1,9 @@ +declare const AppNames: { + readonly defaultPort: 7000; + readonly defaultAssetPrefix: "_bunext/static"; + readonly name: "Bunext"; + readonly version: "1.0.1"; + readonly defaultDistDir: ".bunext"; + readonly RootPagesComponentName: "__root"; +}; +export default AppNames; diff --git a/dist/utils/grab-app-port.d.ts b/dist/utils/grab-app-port.d.ts new file mode 100644 index 0000000..809f284 --- /dev/null +++ b/dist/utils/grab-app-port.d.ts @@ -0,0 +1 @@ +export default function grabAppPort(): number; diff --git a/dist/utils/grab-assets-prefix.d.ts b/dist/utils/grab-assets-prefix.d.ts new file mode 100644 index 0000000..4602d7b --- /dev/null +++ b/dist/utils/grab-assets-prefix.d.ts @@ -0,0 +1 @@ +export default function grabAssetsPrefix(): string; diff --git a/dist/utils/grab-constants.d.ts b/dist/utils/grab-constants.d.ts new file mode 100644 index 0000000..5c81cd6 --- /dev/null +++ b/dist/utils/grab-constants.d.ts @@ -0,0 +1,9 @@ +export default function grabConstants(): { + readonly ClientRootElementIDName: "__bunext"; + readonly ClientWindowPagePropsName: "__PAGE_PROPS__"; + readonly MBInBytes: number; + readonly ServerDefaultRequestBodyLimitBytes: number; + readonly ClientRootComponentWindowName: "BUNEXT_ROOT"; + readonly MaxBundlerRebuilds: 5; + readonly config: import("../types").BunextConfig; +}; diff --git a/dist/utils/grab-dir-names.d.ts b/dist/utils/grab-dir-names.d.ts new file mode 100644 index 0000000..f8d7673 --- /dev/null +++ b/dist/utils/grab-dir-names.d.ts @@ -0,0 +1,21 @@ +export default function grabDirNames(): { + ROOT_DIR: string; + SRC_DIR: string; + PAGES_DIR: string; + API_DIR: string; + PUBLIC_DIR: string; + HYDRATION_DST_DIR: string; + BUNX_ROOT_DIR: string; + CONFIG_FILE: string; + BUNX_TMP_DIR: string; + BUNX_HYDRATION_SRC_DIR: string; + BUNX_ROOT_SRC_DIR: string; + BUNX_ROOT_PRESETS_DIR: string; + BUNX_ROOT_500_PRESET_COMPONENT: string; + BUNX_ROOT_500_FILE_NAME: string; + BUNX_ROOT_404_PRESET_COMPONENT: string; + BUNX_ROOT_404_FILE_NAME: string; + HYDRATION_DST_DIR_MAP_JSON_FILE: string; + BUNEXT_CACHE_DIR: string; + BUNX_CWD_MODULE_CACHE_DIR: string; +}; diff --git a/dist/utils/grab-origin.d.ts b/dist/utils/grab-origin.d.ts new file mode 100644 index 0000000..0d21d9a --- /dev/null +++ b/dist/utils/grab-origin.d.ts @@ -0,0 +1 @@ +export default function grabOrigin(): string; diff --git a/dist/utils/grab-page-name.d.ts b/dist/utils/grab-page-name.d.ts new file mode 100644 index 0000000..0022b88 --- /dev/null +++ b/dist/utils/grab-page-name.d.ts @@ -0,0 +1,5 @@ +type Params = { + path: string; +}; +export default function grabPageName(params: Params): string; +export {}; diff --git a/dist/utils/grab-route-params.d.ts b/dist/utils/grab-route-params.d.ts new file mode 100644 index 0000000..bae616e --- /dev/null +++ b/dist/utils/grab-route-params.d.ts @@ -0,0 +1,6 @@ +import type { BunxRouteParams } from "../types"; +type Params = { + req: Request; +}; +export default function grabRouteParams({ req, }: Params): Promise; +export {}; diff --git a/dist/utils/grab-router.d.ts b/dist/utils/grab-router.d.ts new file mode 100644 index 0000000..6d1730b --- /dev/null +++ b/dist/utils/grab-router.d.ts @@ -0,0 +1 @@ +export default function grabRouter(): import("bun").FileSystemRouter; diff --git a/dist/utils/is-development.d.ts b/dist/utils/is-development.d.ts new file mode 100644 index 0000000..e62e1e1 --- /dev/null +++ b/dist/utils/is-development.d.ts @@ -0,0 +1 @@ +export default function isDevelopment(): boolean; diff --git a/dist/utils/log.d.ts b/dist/utils/log.d.ts new file mode 100644 index 0000000..b9c9584 --- /dev/null +++ b/dist/utils/log.d.ts @@ -0,0 +1,10 @@ +export declare const log: { + 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; +}; diff --git a/dist/utils/numberfy.d.ts b/dist/utils/numberfy.d.ts new file mode 100644 index 0000000..ed2c11b --- /dev/null +++ b/dist/utils/numberfy.d.ts @@ -0,0 +1,2 @@ +export default function numberfy(num: any, decimals?: number): number; +export declare const _n: typeof numberfy; diff --git a/dist/utils/refresh-router.d.ts b/dist/utils/refresh-router.d.ts new file mode 100644 index 0000000..dfae8cb --- /dev/null +++ b/dist/utils/refresh-router.d.ts @@ -0,0 +1 @@ +export default function refreshRouter(): void; diff --git a/dist/utils/register-dev-plugin.d.ts b/dist/utils/register-dev-plugin.d.ts new file mode 100644 index 0000000..65d2f0d --- /dev/null +++ b/dist/utils/register-dev-plugin.d.ts @@ -0,0 +1 @@ +export default function registerDevPlugin(): void; diff --git a/src/functions/bundler/grab-client-hydration-script.ts b/src/functions/bundler/grab-client-hydration-script.ts index 7ff831c..6d54049 100644 --- a/src/functions/bundler/grab-client-hydration-script.ts +++ b/src/functions/bundler/grab-client-hydration-script.ts @@ -21,36 +21,13 @@ export default function grabClientHydrationScript({ page_local_path }: Params) { const does_root_exist = existsSync(root_component_path); - // let txt = ``; - // txt += `import { hydrateRoot } from "react-dom/client";\n`; - // if (does_root_exist) { - // txt += `import Root from "${root_component_path}";\n`; - // } - // txt += `import Page from "${page.local_path}";\n\n`; - // txt += `const pageProps = window.__PAGE_PROPS__ || {};\n`; - - // if (does_root_exist) { - // txt += `const component = \n`; - // } else { - // txt += `const component = \n`; - // } - // txt += `const root = hydrateRoot(document.getElementById("${ClientRootElementIDName}"), component);\n\n`; - // txt += `window.${ClientRootComponentWindowName} = root;\n`; - let txt = ``; - // txt += `import * as React from "react";\n`; - // txt += `import * as ReactDOM from "react-dom";\n`; - // txt += `import * as ReactDOMClient from "react-dom/client";\n`; - // txt += `import * as JSXRuntime from "react/jsx-runtime";\n`; + txt += `import { hydrateRoot, createElement } from "react-dom/client";\n`; if (does_root_exist) { txt += `import Root from "${root_component_path}";\n`; } txt += `import Page from "${page_local_path}";\n\n`; - // txt += `window.__REACT__ = React;\n`; - // txt += `window.__REACT_DOM__ = ReactDOM;\n`; - // txt += `window.__REACT_DOM_CLIENT__ = ReactDOMClient;\n`; - // txt += `window.__JSX_RUNTIME__ = JSXRuntime;\n\n`; txt += `const pageProps = window.__PAGE_PROPS__ || {};\n`; if (does_root_exist) { @@ -62,7 +39,9 @@ export default function grabClientHydrationScript({ page_local_path }: Params) { txt += `if (window.${ClientRootComponentWindowName}?.render) {\n`; txt += ` window.${ClientRootComponentWindowName}.render(component);\n`; txt += `} else {\n`; - txt += ` const root = hydrateRoot(document.getElementById("${ClientRootElementIDName}"), component);\n\n`; + txt += ` const root = hydrateRoot(document.getElementById("${ClientRootElementIDName}"), component, { onRecoverableError: () => {\n\n`; + txt += ` console.log(\`Hydration Error.\`)\n\n`; + txt += ` } });\n\n`; txt += ` window.${ClientRootComponentWindowName} = root;\n`; txt += ` window.__BUNEXT_RERENDER__ = (NewPage) => {\n`; @@ -71,14 +50,5 @@ export default function grabClientHydrationScript({ page_local_path }: Params) { txt += ` };\n`; txt += `}\n`; - // // HMR re-render helper - // if (does_root_exist) { - // txt += `window.__BUNEXT_RERENDER__ = (NewPage) => {\n`; - // txt += ` const props = window.__PAGE_PROPS__ || {};\n`; - // txt += ` root.render();\n`; - // txt += `};\n`; - // } else { - // } - return txt; } diff --git a/src/functions/server/bunext-req-handler.ts b/src/functions/server/bunext-req-handler.ts new file mode 100644 index 0000000..c1acaf0 --- /dev/null +++ b/src/functions/server/bunext-req-handler.ts @@ -0,0 +1,69 @@ +import handleWebPages from "./web-pages/handle-web-pages"; +import handleRoutes from "./handle-routes"; +import isDevelopment from "../../utils/is-development"; +import grabConstants from "../../utils/grab-constants"; +import { AppData } from "../../data/app-data"; +import handleHmr from "./handle-hmr"; +import handleHmrUpdate from "./handle-hmr-update"; +import handlePublic from "./handle-public"; +import handleFiles from "./handle-files"; + +type Params = { + req: Request; +}; + +export default async function bunextRequestHandler({ + req, +}: Params): Promise { + const is_dev = isDevelopment(); + + try { + const url = new URL(req.url); + + const { config } = grabConstants(); + + let response: Response | undefined = undefined; + + if (config?.middleware) { + const middleware_res = await config.middleware({ + req, + url, + }); + + if (typeof middleware_res == "object") { + return middleware_res; + } + } + + if (url.pathname == `/${AppData["ClientHMRPath"]}`) { + response = await handleHmrUpdate({ req }); + } else if (url.pathname === "/__hmr" && is_dev) { + response = await handleHmr({ req }); + } else if (url.pathname.startsWith("/api/")) { + response = await handleRoutes({ req }); + } else if (url.pathname.startsWith("/public/")) { + response = await handlePublic({ req }); + } else if (url.pathname.match(/\..*$/)) { + response = await handleFiles({ req }); + } else { + response = await handleWebPages({ req }); + } + + if (!response) { + throw new Error(`No Response generated`); + } + + if (is_dev) { + response.headers.set( + "Cache-Control", + "no-cache, no-store, must-revalidate", + ); + } + + return response; + } catch (error: any) { + return new Response(`Server Error: ${error.message}`, { + status: 500, + }); + } +} diff --git a/src/functions/server/handle-files.ts b/src/functions/server/handle-files.ts index e98232b..bed9707 100644 --- a/src/functions/server/handle-files.ts +++ b/src/functions/server/handle-files.ts @@ -1,4 +1,3 @@ -import type { Server } from "bun"; import grabDirNames from "../../utils/grab-dir-names"; import path from "path"; import isDevelopment from "../../utils/is-development"; @@ -8,10 +7,9 @@ const { PUBLIC_DIR } = grabDirNames(); type Params = { req: Request; - server: Server; }; -export default async function ({ req, server }: Params): Promise { +export default async function ({ req }: Params): Promise { try { const is_dev = isDevelopment(); const url = new URL(req.url); diff --git a/src/functions/server/handle-hmr-update.ts b/src/functions/server/handle-hmr-update.ts index 75fb3f9..3f46fc0 100644 --- a/src/functions/server/handle-hmr-update.ts +++ b/src/functions/server/handle-hmr-update.ts @@ -1,4 +1,3 @@ -import type { Server } from "bun"; import grabDirNames from "../../utils/grab-dir-names"; import { AppData } from "../../data/app-data"; import path from "path"; @@ -6,14 +5,13 @@ import grabRootFile from "./web-pages/grab-root-file"; import grabPageBundledReactComponent from "./web-pages/grab-page-bundled-react-component"; import writeHMRTsxModule from "./web-pages/write-hmr-tsx-module"; -const { PUBLIC_DIR, BUNX_HYDRATION_SRC_DIR } = grabDirNames(); +const { BUNX_HYDRATION_SRC_DIR } = grabDirNames(); type Params = { req: Request; - server: Server; }; -export default async function ({ req, server }: Params): Promise { +export default async function ({ req }: Params): Promise { try { const url = new URL(req.url); diff --git a/src/functions/server/handle-hmr.ts b/src/functions/server/handle-hmr.ts index 3b00b3c..1ad68ee 100644 --- a/src/functions/server/handle-hmr.ts +++ b/src/functions/server/handle-hmr.ts @@ -6,10 +6,9 @@ import grabRouter from "../../utils/grab-router"; type Params = { req: Request; - server: Server; }; -export default async function ({ req, server }: Params): Promise { +export default async function ({ req }: Params): Promise { const referer_url = new URL(req.headers.get("referer") || ""); const match = global.ROUTER.match(referer_url.pathname); diff --git a/src/functions/server/handle-public.ts b/src/functions/server/handle-public.ts index fb46f7d..92afa7f 100644 --- a/src/functions/server/handle-public.ts +++ b/src/functions/server/handle-public.ts @@ -1,17 +1,15 @@ -import type { Server } from "bun"; import grabDirNames from "../../utils/grab-dir-names"; import path from "path"; import isDevelopment from "../../utils/is-development"; import { existsSync } from "fs"; -const { PUBLIC_DIR, BUNX_HYDRATION_SRC_DIR } = grabDirNames(); +const { PUBLIC_DIR } = grabDirNames(); type Params = { req: Request; - server: Server; }; -export default async function ({ req, server }: Params): Promise { +export default async function ({ req }: Params): Promise { try { const is_dev = isDevelopment(); const url = new URL(req.url); diff --git a/src/functions/server/handle-routes.ts b/src/functions/server/handle-routes.ts index 8f304cb..81b5779 100644 --- a/src/functions/server/handle-routes.ts +++ b/src/functions/server/handle-routes.ts @@ -7,10 +7,9 @@ import isDevelopment from "../../utils/is-development"; type Params = { req: Request; - server: Server; }; -export default async function ({ req, server }: Params): Promise { +export default async function ({ req }: Params): Promise { const url = new URL(req.url); const is_dev = isDevelopment(); @@ -72,7 +71,6 @@ export default async function ({ req, server }: Params): Promise { const res: Response = await module["default"]({ ...routeParams, - server, } as BunxRouteParams); if (is_dev) { diff --git a/src/functions/server/server-params-gen.ts b/src/functions/server/server-params-gen.ts index b754d95..440c27e 100644 --- a/src/functions/server/server-params-gen.ts +++ b/src/functions/server/server-params-gen.ts @@ -1,14 +1,7 @@ import type { ServeOptions } from "bun"; import grabAppPort from "../../utils/grab-app-port"; -import handleWebPages from "./web-pages/handle-web-pages"; -import handleRoutes from "./handle-routes"; import isDevelopment from "../../utils/is-development"; -import grabConstants from "../../utils/grab-constants"; -import { AppData } from "../../data/app-data"; -import handleHmr from "./handle-hmr"; -import handleHmrUpdate from "./handle-hmr-update"; -import handlePublic from "./handle-public"; -import handleFiles from "./handle-files"; +import bunextRequestHandler from "./bunext-req-handler"; type Params = { dev?: boolean; @@ -21,59 +14,10 @@ export default async function (params?: Params): Promise { return { async fetch(req, server) { - try { - const url = new URL(req.url); - - const { config } = grabConstants(); - - let response: Response | undefined = undefined; - - if (config?.middleware) { - const middleware_res = await config.middleware({ - req, - url, - server, - }); - - if (typeof middleware_res == "object") { - return middleware_res; - } - } - - if (url.pathname == `/${AppData["ClientHMRPath"]}`) { - response = await handleHmrUpdate({ req, server }); - } else if (url.pathname === "/__hmr" && is_dev) { - response = await handleHmr({ req, server }); - } else if (url.pathname.startsWith("/api/")) { - response = await handleRoutes({ req, server }); - } else if (url.pathname.startsWith("/public/")) { - response = await handlePublic({ req, server }); - } else if (url.pathname.match(/\..*$/)) { - response = await handleFiles({ req, server }); - } else { - response = await handleWebPages({ req }); - } - - if (!response) { - throw new Error(`No Response generated`); - } - - if (is_dev) { - response.headers.set( - "Cache-Control", - "no-cache, no-store, must-revalidate", - ); - } - - return response; - } catch (error: any) { - return new Response(`Server Error: ${error.message}`, { - status: 500, - }); - } + return await bunextRequestHandler({ req }); }, port, - idleTimeout: 0, + // idleTimeout: 0, development: { hmr: true, }, diff --git a/src/functions/server/web-pages/generate-web-html.tsx b/src/functions/server/web-pages/generate-web-html.tsx index ee80103..b7654ec 100644 --- a/src/functions/server/web-pages/generate-web-html.tsx +++ b/src/functions/server/web-pages/generate-web-html.tsx @@ -62,7 +62,7 @@ export default async function genWebHTML({ } if (isDevelopment()) { - html += `\n`; + html += `\n`; } if (headHTML) { diff --git a/src/functions/server/web-pages/grab-web-page-hydration-script.tsx b/src/functions/server/web-pages/grab-web-page-hydration-script.tsx index b31c33c..ef44b84 100644 --- a/src/functions/server/web-pages/grab-web-page-hydration-script.tsx +++ b/src/functions/server/web-pages/grab-web-page-hydration-script.tsx @@ -5,31 +5,36 @@ type Params = { bundledMap?: BundlerCTXMap; }; -export default async function ({ bundledMap }: Params) { +export default async function (params?: Params) { let script = ""; script += `console.log(\`Development Environment\`);\n\n`; + script += `const _ce = console.error.bind(console);\n`; + script += `console.error = (...args) => {\n`; + script += ` if (typeof args[0] === "string" && args[0].includes("hydrat")) return;\n`; + script += ` _ce(...args);\n`; + script += `};\n\n`; + + script += `function __bunext_show_error(message, source, stack) {\n`; + script += ` const existing = document.getElementById("__bunext_error_overlay");\n`; + script += ` if (existing) existing.remove();\n`; + script += ` const overlay = document.createElement("div");\n`; + script += ` overlay.id = "__bunext_error_overlay";\n`; + script += ` overlay.style.cssText = "position:fixed;inset:0;z-index:99999;background:#1a1a1a;color:#ff6b6b;font-family:monospace;font-size:14px;padding:24px;overflow:auto;";\n`; + script += ` overlay.innerHTML = \`
Runtime Error
\${message}
\${source ? \`
\${source}
\` : ""}\${stack ? \`
\${stack}
\` : ""}
\`;\n`; + script += ` document.body.appendChild(overlay);\n`; + script += `}\n\n`; + script += `window.addEventListener("error", (e) => __bunext_show_error(e.message, e.filename ? e.filename + ":" + e.lineno + ":" + e.colno : "", e.error?.stack ?? ""));\n`; + script += `window.addEventListener("unhandledrejection", (e) => __bunext_show_error(String(e.reason?.message ?? e.reason), "", e.reason?.stack ?? ""));\n\n`; script += `const hmr = new EventSource("/__hmr");\n`; + script += `window.addEventListener("beforeunload", () => hmr.close());\n`; script += `hmr.addEventListener("update", async (event) => {\n`; script += ` if (event?.data) {\n`; script += ` console.log(\`HMR Changes Detected. Updating ...\`);\n`; script += ` try {\n`; + script += ` document.getElementById("__bunext_error_overlay")?.remove();\n`; script += ` const data = JSON.parse(event.data);\n`; - // script += ` console.log("data", data);\n`; - // script += ` const modulePath = \`/\${data.target_map.path}\`;\n\n`; - - // script += ` const modulePath = \`/${AppData["ClientHMRPath"]}?href=\${window.location.href}&t=\${Date.now()}\`;\n\n`; - // script += ` console.log("Fetching updated module ...", modulePath);\n\n`; - // script += ` const newModule = await import(modulePath);\n\n`; - // script += ` console.log("newModule", newModule);\n\n`; - // script += ` if (window.__BUNEXT_RERENDER__ && newModule.default) {\n`; - // script += ` window.__BUNEXT_RERENDER__(newModule.default);\n`; - // script += ` console.log(\`HMR: Component updated in-place\`);\n`; - // script += ` } else {\n`; - // script += ` console.warn(\`HMR: No re-render helper found, falling back to reload\`);\n`; - // // script += ` window.location.reload();\n`; - // script += ` }\n\n`; script += ` if (data.target_map.css_path) {\n`; script += ` const oldLink = document.querySelector('link[rel="stylesheet"]');\n`; @@ -60,75 +65,3 @@ export default async function ({ bundledMap }: Params) { return script; } - -// import grabDirNames from "../../../utils/grab-dir-names"; -// import type { BundlerCTXMap, PageDistGenParams } from "../../../types"; - -// const { BUNX_HYDRATION_SRC_DIR } = grabDirNames(); - -// type Params = { -// bundledMap?: BundlerCTXMap; -// }; - -// export default async function ({ bundledMap }: Params) { -// let script = ""; - -// // script += `import React from "react";\n`; -// // script += `import { hydrateRoot } from "react-dom/client";\n`; -// // script += `import App from "${page_file}";\n`; - -// // script += `declare global {\n`; -// // script += ` interface Window {\n`; -// // script += ` ${ClientWindowPagePropsName}: any;\n`; -// // script += ` }\n`; -// // script += `}\n`; - -// // script += `let root: any = null;\n\n`; -// // script += `const component = ;\n\n`; -// // script += `const container = document.getElementById("${ClientRootElementIDName}");\n\n`; -// // script += `if (container) {\n`; -// // script += ` root = hydrateRoot(container, component);\n`; -// // script += `}\n\n`; -// script += `console.log(\`Development Environment\`);\n`; -// // script += `console.log(import.meta);\n`; - -// // script += `if (import.meta.hot) {\n`; -// // script += ` console.log(\`HMR active\`);\n`; -// // script += ` import.meta.hot.dispose(() => {\n`; -// // script += ` console.log("dispose");\n`; -// // script += ` });\n`; -// // script += `}\n`; - -// script += `const hmr = new EventSource("/__hmr");\n`; -// script += `hmr.addEventListener("update", async (event) => {\n`; -// // script += ` console.log(\`HMR even received:\`, event);\n`; -// script += ` if (event.data) {\n`; -// script += ` console.log(\`HMR Changes Detected. Reloading ...\`);\n`; -// // script += ` console.log("event", event);\n`; -// // script += ` console.log("window.${ClientRootComponentWindowName}", window.${ClientRootComponentWindowName});\n\n`; -// // script += ` const event_data = JSON.parse(event.data);\n\n`; -// // script += ` const new_js_path = \`/\${event_data.target_map.path}\`;\n\n`; - -// // script += ` console.log("event_data", event_data);\n\n`; -// // script += ` console.log("new_js_path", new_js_path);\n\n`; - -// // script += ` if (window.${ClientRootComponentWindowName}) {\n`; -// // script += ` const new_component = await import(new_js_path);\n`; -// // script += ` window.${ClientRootComponentWindowName}.render(new_component);\n`; -// // script += ` }\n`; - -// // script += ` import("${page_file}?t=" + event.data.update).then((module) => {\n`; -// // script += ` root.render(module.default);\n`; -// // script += ` })\n`; -// // script += ` console.log("root", root);\n`; -// // script += ` root.unmount();\n`; -// // script += ` const container = document.getElementById("${ClientRootElementIDName}");\n\n`; -// // script += ` root = hydrateRoot(container!, component);\n`; -// // script += ` window.history.pushState({ page: 1 }, "New Page Title", \`\${window.location.pathname}?v=\${Date.now()}\`);\n`; -// // script += ` root.render(component);\n`; -// script += ` window.location.reload();\n`; -// script += ` }\n`; -// script += ` });\n`; - -// return script; -// } diff --git a/src/index.ts b/src/index.ts index c82740f..cda7c80 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1,6 @@ -const bunext = {}; +import bunextRequestHandler from "./functions/server/bunext-req-handler"; + +const bunext = { + bunextRequestHandler, +}; export default bunext; diff --git a/src/types/index.ts b/src/types/index.ts index e1abcef..6e3dfe7 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -57,7 +57,6 @@ export type BunextConfig = { export type BunextConfigMiddlewareParams = { req: Request; url: URL; - server: Server; }; export type GetRouteReturn = { diff --git a/tsconfig.json b/tsconfig.json index 2fdbdfc..ca2fe1c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,8 @@ "noUnusedLocals": false, "noUnusedParameters": false, "noPropertyAccessFromIndexSignature": false, - "outDir": "dist" + "outDir": "dist", + "declaration": true }, "include": ["src"], "exclude": ["node_modules", "dist"]