diff --git a/dist/data/app-data.d.ts b/dist/data/app-data.d.ts index a0cd258..19b0109 100644 --- a/dist/data/app-data.d.ts +++ b/dist/data/app-data.d.ts @@ -4,4 +4,5 @@ export declare const AppData: { readonly BunextStaticFilesCacheExpiry: number; readonly ClientHMRPath: "__bunext_client_hmr__"; readonly BunextClientHydrationScriptID: "bunext-client-hydration-script"; + readonly BunextTmpFileExt: ".bunext_tmp.tsx"; }; diff --git a/dist/data/app-data.js b/dist/data/app-data.js index 6d31a38..727cbb0 100644 --- a/dist/data/app-data.js +++ b/dist/data/app-data.js @@ -4,4 +4,5 @@ export const AppData = { BunextStaticFilesCacheExpiry: 60 * 60 * 24 * 7, ClientHMRPath: "__bunext_client_hmr__", BunextClientHydrationScriptID: "bunext-client-hydration-script", + BunextTmpFileExt: ".bunext_tmp.tsx", }; diff --git a/dist/functions/bundler/api-routes-bundler.d.ts b/dist/functions/bundler/api-routes-bundler.d.ts new file mode 100644 index 0000000..3f712d6 --- /dev/null +++ b/dist/functions/bundler/api-routes-bundler.d.ts @@ -0,0 +1 @@ +export default function apiRoutesBundler(): Promise; diff --git a/dist/functions/bundler/api-routes-bundler.js b/dist/functions/bundler/api-routes-bundler.js new file mode 100644 index 0000000..76657df --- /dev/null +++ b/dist/functions/bundler/api-routes-bundler.js @@ -0,0 +1,40 @@ +import grabAllPages from "../../utils/grab-all-pages"; +import grabDirNames from "../../utils/grab-dir-names"; +import isDevelopment from "../../utils/is-development"; +import tailwindcss from "bun-plugin-tailwind"; +const { BUNX_CWD_MODULE_CACHE_DIR } = grabDirNames(); +export default async function apiRoutesBundler() { + const api_routes = grabAllPages({ api_only: true }); + const dev = isDevelopment(); + try { + const build = await Bun.build({ + entrypoints: api_routes.map((r) => r.local_path), + target: "bun", + format: "esm", + jsx: { + runtime: "automatic", + development: dev, + }, + minify: !dev, + define: { + "process.env.NODE_ENV": JSON.stringify(dev ? "development" : "production"), + }, + outdir: BUNX_CWD_MODULE_CACHE_DIR, + plugins: [tailwindcss], + naming: { + entry: "api/[dir]/[name].[ext]", + chunk: "api/[dir]/chunks/[hash].[ext]", + }, + // external: [ + // "react", + // "react-dom", + // "react-dom/client", + // "react/jsx-runtime", + // ], + splitting: true, + }); + } + catch (error) { + console.log(`API paths build ERROR:`, error); + } +} diff --git a/dist/functions/bundler/api-routes-context-bundler.d.ts b/dist/functions/bundler/api-routes-context-bundler.d.ts new file mode 100644 index 0000000..37b77f7 --- /dev/null +++ b/dist/functions/bundler/api-routes-context-bundler.d.ts @@ -0,0 +1 @@ +export default function apiRoutesContextBundler(): Promise; diff --git a/dist/functions/bundler/api-routes-context-bundler.js b/dist/functions/bundler/api-routes-context-bundler.js new file mode 100644 index 0000000..c1813ce --- /dev/null +++ b/dist/functions/bundler/api-routes-context-bundler.js @@ -0,0 +1,42 @@ +import * as esbuild from "esbuild"; +import grabAllPages from "../../utils/grab-all-pages"; +import grabDirNames from "../../utils/grab-dir-names"; +import isDevelopment from "../../utils/is-development"; +import tailwindEsbuildPlugin from "../server/web-pages/tailwind-esbuild-plugin"; +import apiRoutesCTXArtifactTracker from "./plugins/api-routes-ctx-artifact-tracker"; +const { BUNX_CWD_MODULE_CACHE_DIR } = grabDirNames(); +export default async function apiRoutesContextBundler() { + const pages = grabAllPages({ api_only: true }); + const dev = isDevelopment(); + if (global.API_ROUTES_BUNDLER_CTX) { + await global.API_ROUTES_BUNDLER_CTX.dispose(); + global.API_ROUTES_BUNDLER_CTX = undefined; + } + global.API_ROUTES_BUNDLER_CTX = await esbuild.context({ + entryPoints: pages.map((p) => p.local_path), + outdir: BUNX_CWD_MODULE_CACHE_DIR, + bundle: true, + minify: !dev, + format: "esm", + target: "esnext", + platform: "node", + define: { + "process.env.NODE_ENV": JSON.stringify(dev ? "development" : "production"), + }, + entryNames: "api/[dir]/[hash]", + metafile: true, + plugins: [ + tailwindEsbuildPlugin, + apiRoutesCTXArtifactTracker({ pages }), + ], + jsx: "automatic", + external: [ + "react", + "react-dom", + "react/jsx-runtime", + "react/jsx-dev-runtime", + "bun:*", + ], + }); + await global.API_ROUTES_BUNDLER_CTX.rebuild(); +} diff --git a/dist/functions/bundler/pages-ssr-context-bundler.js b/dist/functions/bundler/pages-ssr-context-bundler.js index a5be8e5..060895f 100644 --- a/dist/functions/bundler/pages-ssr-context-bundler.js +++ b/dist/functions/bundler/pages-ssr-context-bundler.js @@ -9,7 +9,7 @@ import ssrVirtualFilesPlugin from "./plugins/ssr-virtual-files-plugin"; import ssrCTXArtifactTracker from "./plugins/ssr-ctx-artifact-tracker"; const { BUNX_CWD_MODULE_CACHE_DIR } = grabDirNames(); export default async function pagesSSRContextBundler(params) { - const pages = grabAllPages(); + const pages = grabAllPages({ exclude_api: true }); const dev = isDevelopment(); if (global.SSR_BUNDLER_CTX) { await global.SSR_BUNDLER_CTX.dispose(); @@ -38,7 +38,7 @@ export default async function pagesSSRContextBundler(params) { bundle: true, minify: !dev, format: "esm", - target: "es2020", + target: "esnext", platform: "node", define: { "process.env.NODE_ENV": JSON.stringify(dev ? "development" : "production"), diff --git a/dist/functions/bundler/plugins/api-routes-ctx-artifact-tracker.d.ts b/dist/functions/bundler/plugins/api-routes-ctx-artifact-tracker.d.ts new file mode 100644 index 0000000..a8e931e --- /dev/null +++ b/dist/functions/bundler/plugins/api-routes-ctx-artifact-tracker.d.ts @@ -0,0 +1,7 @@ +import { type Plugin } from "esbuild"; +import type { PageFiles } from "../../../types"; +type Params = { + pages: PageFiles[]; +}; +export default function apiRoutesCTXArtifactTracker({ pages }: Params): Plugin; +export {}; diff --git a/dist/functions/bundler/plugins/api-routes-ctx-artifact-tracker.js b/dist/functions/bundler/plugins/api-routes-ctx-artifact-tracker.js new file mode 100644 index 0000000..917ee07 --- /dev/null +++ b/dist/functions/bundler/plugins/api-routes-ctx-artifact-tracker.js @@ -0,0 +1,62 @@ +import {} from "esbuild"; +import buildOnstartErrorHandler from "../build-on-start-error-handler"; +import path from "path"; +import grabDirNames from "../../../utils/grab-dir-names"; +import { log } from "../../../utils/log"; +let build_start = 0; +let build_starts = 0; +const MAX_BUILD_STARTS = 2; +const { ROOT_DIR } = grabDirNames(); +export default function apiRoutesCTXArtifactTracker({ pages }) { + const artifactTracker = { + name: "ssr-artifact-tracker", + setup(build) { + build.onStart(async () => { + build_starts++; + build_start = performance.now(); + if (build_starts == MAX_BUILD_STARTS) { + await buildOnstartErrorHandler(); + } + }); + build.onEnd((result) => { + if (result.errors.length > 0) { + console.log("result.errors", result.errors); + return; + } + const artifacts = Object.entries(result.metafile.outputs) + .filter(([, meta]) => meta.entryPoint) + .map(([outputPath, meta]) => { + const entrypoint = meta.entryPoint + ? path.join(ROOT_DIR, meta.entryPoint) + : undefined; + const target_page = pages.find((p) => p.local_path == entrypoint); + if (!target_page || !meta.entryPoint) { + return undefined; + } + const { file_name, local_path, url_path } = target_page; + return { + path: outputPath, + hash: path.basename(outputPath, path.extname(outputPath)), + type: "text/javascript", + entrypoint: meta.entryPoint, + file_name, + local_path, + url_path, + }; + }); + if (artifacts?.[0] && artifacts.length > 0) { + for (let i = 0; i < artifacts.length; i++) { + const artifact = artifacts[i]; + if (artifact?.local_path && + global.API_ROUTES_BUNDLER_CTX_MAP) { + global.API_ROUTES_BUNDLER_CTX_MAP[artifact.local_path] = artifact; + } + } + } + // const elapsed = (performance.now() - build_start).toFixed(0); + // log.success(`API Routes [Built] in ${elapsed}ms`); + }); + }, + }; + return artifactTracker; +} diff --git a/dist/functions/bundler/plugins/esbuild-ctx-artifact-tracker.js b/dist/functions/bundler/plugins/esbuild-ctx-artifact-tracker.js index f84d8e8..24350c6 100644 --- a/dist/functions/bundler/plugins/esbuild-ctx-artifact-tracker.js +++ b/dist/functions/bundler/plugins/esbuild-ctx-artifact-tracker.js @@ -3,6 +3,7 @@ import { log } from "../../../utils/log"; import grabArtifactsFromBundledResults from "../grab-artifacts-from-bundled-result"; import pagesSSRContextBundler from "../pages-ssr-context-bundler"; import buildOnstartErrorHandler from "../build-on-start-error-handler"; +import apiRoutesContextBundler from "../api-routes-context-bundler"; let build_start = 0; let build_starts = 0; const MAX_BUILD_STARTS = 2; @@ -46,6 +47,12 @@ export default function esbuildCTXArtifactTracker({ entryToPage, post_build_fn, else { pagesSSRContextBundler(); } + if (global.API_ROUTES_BUNDLER_CTX) { + global.API_ROUTES_BUNDLER_CTX.rebuild(); + } + else { + apiRoutesContextBundler(); + } }); }, }; diff --git a/dist/functions/bunext-init.d.ts b/dist/functions/bunext-init.d.ts index 10c82f1..7922da6 100644 --- a/dist/functions/bunext-init.d.ts +++ b/dist/functions/bunext-init.d.ts @@ -22,6 +22,9 @@ declare global { var SSR_BUNDLER_CTX_MAP: { [k: string]: BundlerCTXMap; } | undefined; + var API_ROUTES_BUNDLER_CTX_MAP: { + [k: string]: BundlerCTXMap; + } | undefined; var BUNDLER_REBUILDS: 0; var PAGES_SRC_WATCHER: FSWatcher | undefined; var CURRENT_VERSION: string | undefined; @@ -30,6 +33,7 @@ declare global { var SKIPPED_BROWSER_MODULES: Set; var BUNDLER_CTX: BuildContext | undefined; var SSR_BUNDLER_CTX: BuildContext | undefined; + var API_ROUTES_BUNDLER_CTX: BuildContext | undefined; var DIR_NAMES: ReturnType; var REACT_IMPORTS_MAP: { imports: Record; diff --git a/dist/functions/bunext-init.js b/dist/functions/bunext-init.js index f6f2bcd..f0863af 100644 --- a/dist/functions/bunext-init.js +++ b/dist/functions/bunext-init.js @@ -14,6 +14,7 @@ export default async function bunextInit() { global.HMR_CONTROLLERS = []; global.BUNDLER_CTX_MAP = {}; global.SSR_BUNDLER_CTX_MAP = {}; + global.API_ROUTES_BUNDLER_CTX_MAP = {}; global.BUNDLER_REBUILDS = 0; global.REBUILD_RETRIES = 0; global.PAGE_FILES = []; diff --git a/dist/functions/server/handle-routes.js b/dist/functions/server/handle-routes.js index 17fd046..00fdda6 100644 --- a/dist/functions/server/handle-routes.js +++ b/dist/functions/server/handle-routes.js @@ -24,11 +24,14 @@ export default async function ({ req }) { }, }); } - const routeParams = await grabRouteParams({ req }); + const routeParams = await grabRouteParams({ + req, + query: match.query, + }); let module; const now = Date.now(); - if (global.SSR_BUNDLER_CTX_MAP?.[match.filePath]?.path) { - const target_import = path.join(ROOT_DIR, global.SSR_BUNDLER_CTX_MAP[match.filePath].path); + if (is_dev && global.API_ROUTES_BUNDLER_CTX_MAP?.[match.filePath]?.path) { + const target_import = path.join(ROOT_DIR, global.API_ROUTES_BUNDLER_CTX_MAP[match.filePath].path); module = await import(`${target_import}?t=${now}`); } else { @@ -80,3 +83,31 @@ export default async function ({ req }) { } return undefined; } +// const relative_path = match.filePath.replace(API_DIR, ""); +// const relative_module_js_file = relative_path.replace(/\.tsx?$/, ".js"); +// const bun_module_file = path.join( +// BUNX_CWD_MODULE_CACHE_DIR, +// "api", +// relative_module_js_file, +// ); +// if (existsSync(bun_module_file)) { +// module = await import(`${bun_module_file}?t=${now}`); +// } else { +// const import_path = is_dev +// ? `${match.filePath}?t=${now}` +// : match.filePath; +// module = await import(import_path); +// } +// if (is_dev) { +// const tmp_path = `${match.filePath}.${now}${AppData["BunextTmpFileExt"]}`; +// cpSync(match.filePath, tmp_path); +// module = await import(`${tmp_path}?t=${now}`); +// try { +// unlinkSync(tmp_path); +// } catch (error) {} +// } else { +// // const import_path = is_dev ? `${match.filePath}?t=${now}` : match.filePath; +// module = await import(match.filePath); +// } +// const import_path = is_dev ? `${match.filePath}?t=${now}` : match.filePath; +// module = await import(import_path); diff --git a/dist/functions/server/watcher-esbuild-ctx.js b/dist/functions/server/watcher-esbuild-ctx.js index 38e5660..8c50ad8 100644 --- a/dist/functions/server/watcher-esbuild-ctx.js +++ b/dist/functions/server/watcher-esbuild-ctx.js @@ -5,6 +5,7 @@ import { log } from "../../utils/log"; import allPagesESBuildContextBundler from "../bundler/all-pages-esbuild-context-bundler"; import serverPostBuildFn from "./server-post-build-fn"; import fullRebuild from "./full-rebuild"; +import { AppData } from "../../data/app-data"; const { ROOT_DIR } = grabDirNames(); export default async function watcherEsbuildCTX() { const pages_src_watcher = watch(ROOT_DIR, { @@ -16,6 +17,9 @@ export default async function watcherEsbuildCTX() { if (filename.match(/^\.\w+/)) { return; } + if (filename.endsWith(AppData["BunextTmpFileExt"])) { + return; + } const full_file_path = path.join(ROOT_DIR, filename); const does_file_exist = existsSync(full_file_path); const file_stat = does_file_exist diff --git a/dist/utils/grab-all-pages.d.ts b/dist/utils/grab-all-pages.d.ts index 436fd54..ba8b97b 100644 --- a/dist/utils/grab-all-pages.d.ts +++ b/dist/utils/grab-all-pages.d.ts @@ -1,6 +1,7 @@ import type { PageFiles } from "../types"; type Params = { exclude_api?: boolean; + api_only?: 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 50491d5..fffef17 100644 --- a/dist/utils/grab-all-pages.js +++ b/dist/utils/grab-all-pages.js @@ -9,6 +9,9 @@ export default function grabAllPages(params) { if (params?.exclude_api) { return pages.filter((p) => !Boolean(p.url_path.startsWith("/api/"))); } + if (params?.api_only) { + return pages.filter((p) => Boolean(p.url_path.startsWith("/api/"))); + } return pages; } function grabPageDirRecursively({ page_dir }) { diff --git a/dist/utils/grab-route-params.d.ts b/dist/utils/grab-route-params.d.ts index bae616e..210274c 100644 --- a/dist/utils/grab-route-params.d.ts +++ b/dist/utils/grab-route-params.d.ts @@ -1,6 +1,7 @@ import type { BunxRouteParams } from "../types"; type Params = { req: Request; + query?: any; }; -export default function grabRouteParams({ req, }: Params): Promise; +export default function grabRouteParams({ req, query: passed_query, }: Params): Promise; export {}; diff --git a/dist/utils/grab-route-params.js b/dist/utils/grab-route-params.js index 03d5ebe..24717e8 100644 --- a/dist/utils/grab-route-params.js +++ b/dist/utils/grab-route-params.js @@ -1,5 +1,6 @@ +import _ from "lodash"; import deserializeQuery from "./deserialize-query"; -export default async function grabRouteParams({ req, }) { +export default async function grabRouteParams({ req, query: passed_query, }) { const url = new URL(req.url); const query = deserializeQuery(Object.fromEntries(url.searchParams)); const body = await (async () => { @@ -13,7 +14,7 @@ export default async function grabRouteParams({ req, }) { const routeParams = { req, url, - query, + query: _.merge(query, passed_query), body, server: global.SERVER, }; diff --git a/package.json b/package.json index 2f0eb7a..97a32d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@moduletrace/bunext", - "version": "1.0.71", + "version": "1.0.72", "main": "dist/index.js", "module": "index.ts", "dependencies": { diff --git a/src/data/app-data.ts b/src/data/app-data.ts index 8e4bdd2..f4fdbe8 100644 --- a/src/data/app-data.ts +++ b/src/data/app-data.ts @@ -4,4 +4,5 @@ export const AppData = { BunextStaticFilesCacheExpiry: 60 * 60 * 24 * 7, ClientHMRPath: "__bunext_client_hmr__", BunextClientHydrationScriptID: "bunext-client-hydration-script", + BunextTmpFileExt: ".bunext_tmp.tsx", } as const; diff --git a/src/functions/bundler/api-routes-bundler.ts b/src/functions/bundler/api-routes-bundler.ts new file mode 100644 index 0000000..e54213c --- /dev/null +++ b/src/functions/bundler/api-routes-bundler.ts @@ -0,0 +1,44 @@ +import grabAllPages from "../../utils/grab-all-pages"; +import grabDirNames from "../../utils/grab-dir-names"; +import isDevelopment from "../../utils/is-development"; +import tailwindcss from "bun-plugin-tailwind"; + +const { BUNX_CWD_MODULE_CACHE_DIR } = grabDirNames(); + +export default async function apiRoutesBundler() { + const api_routes = grabAllPages({ api_only: true }); + const dev = isDevelopment(); + + try { + const build = await Bun.build({ + entrypoints: api_routes.map((r) => r.local_path), + target: "bun", + format: "esm", + jsx: { + runtime: "automatic", + development: dev, + }, + minify: !dev, + define: { + "process.env.NODE_ENV": JSON.stringify( + dev ? "development" : "production", + ), + }, + outdir: BUNX_CWD_MODULE_CACHE_DIR, + plugins: [tailwindcss], + naming: { + entry: "api/[dir]/[name].[ext]", + chunk: "api/[dir]/chunks/[hash].[ext]", + }, + // external: [ + // "react", + // "react-dom", + // "react-dom/client", + // "react/jsx-runtime", + // ], + splitting: true, + }); + } catch (error) { + console.log(`API paths build ERROR:`, error); + } +} diff --git a/src/functions/bundler/api-routes-context-bundler.ts b/src/functions/bundler/api-routes-context-bundler.ts new file mode 100644 index 0000000..b8d6d7d --- /dev/null +++ b/src/functions/bundler/api-routes-context-bundler.ts @@ -0,0 +1,49 @@ +import * as esbuild from "esbuild"; +import grabAllPages from "../../utils/grab-all-pages"; +import grabDirNames from "../../utils/grab-dir-names"; +import isDevelopment from "../../utils/is-development"; +import tailwindEsbuildPlugin from "../server/web-pages/tailwind-esbuild-plugin"; +import apiRoutesCTXArtifactTracker from "./plugins/api-routes-ctx-artifact-tracker"; + +const { BUNX_CWD_MODULE_CACHE_DIR } = grabDirNames(); + +export default async function apiRoutesContextBundler() { + const pages = grabAllPages({ api_only: true }); + const dev = isDevelopment(); + + if (global.API_ROUTES_BUNDLER_CTX) { + await global.API_ROUTES_BUNDLER_CTX.dispose(); + global.API_ROUTES_BUNDLER_CTX = undefined; + } + + global.API_ROUTES_BUNDLER_CTX = await esbuild.context({ + entryPoints: pages.map((p) => p.local_path), + outdir: BUNX_CWD_MODULE_CACHE_DIR, + bundle: true, + minify: !dev, + format: "esm", + target: "esnext", + platform: "node", + define: { + "process.env.NODE_ENV": JSON.stringify( + dev ? "development" : "production", + ), + }, + entryNames: "api/[dir]/[hash]", + metafile: true, + plugins: [ + tailwindEsbuildPlugin, + apiRoutesCTXArtifactTracker({ pages }), + ], + jsx: "automatic", + external: [ + "react", + "react-dom", + "react/jsx-runtime", + "react/jsx-dev-runtime", + "bun:*", + ], + }); + + await global.API_ROUTES_BUNDLER_CTX.rebuild(); +} diff --git a/src/functions/bundler/pages-ssr-context-bundler.ts b/src/functions/bundler/pages-ssr-context-bundler.ts index 09cb180..cc7abc5 100644 --- a/src/functions/bundler/pages-ssr-context-bundler.ts +++ b/src/functions/bundler/pages-ssr-context-bundler.ts @@ -16,7 +16,7 @@ type Params = { }; export default async function pagesSSRContextBundler(params?: Params) { - const pages = grabAllPages(); + const pages = grabAllPages({ exclude_api: true }); const dev = isDevelopment(); if (global.SSR_BUNDLER_CTX) { @@ -52,7 +52,7 @@ export default async function pagesSSRContextBundler(params?: Params) { bundle: true, minify: !dev, format: "esm", - target: "es2020", + target: "esnext", platform: "node", define: { "process.env.NODE_ENV": JSON.stringify( diff --git a/src/functions/bundler/plugins/api-routes-ctx-artifact-tracker.ts b/src/functions/bundler/plugins/api-routes-ctx-artifact-tracker.ts new file mode 100644 index 0000000..288d3c0 --- /dev/null +++ b/src/functions/bundler/plugins/api-routes-ctx-artifact-tracker.ts @@ -0,0 +1,90 @@ +import { type Plugin } from "esbuild"; +import type { BundlerCTXMap, PageFiles } from "../../../types"; +import buildOnstartErrorHandler from "../build-on-start-error-handler"; +import path from "path"; +import grabDirNames from "../../../utils/grab-dir-names"; +import { log } from "../../../utils/log"; + +let build_start = 0; +let build_starts = 0; +const MAX_BUILD_STARTS = 2; + +const { ROOT_DIR } = grabDirNames(); + +type Params = { + pages: PageFiles[]; +}; + +export default function apiRoutesCTXArtifactTracker({ pages }: Params) { + const artifactTracker: Plugin = { + name: "ssr-artifact-tracker", + setup(build) { + build.onStart(async () => { + build_starts++; + build_start = performance.now(); + if (build_starts == MAX_BUILD_STARTS) { + await buildOnstartErrorHandler(); + } + }); + + build.onEnd((result) => { + if (result.errors.length > 0) { + console.log("result.errors", result.errors); + return; + } + + const artifacts: (BundlerCTXMap | undefined)[] = Object.entries( + result.metafile!.outputs, + ) + .filter(([, meta]) => meta.entryPoint) + .map(([outputPath, meta]) => { + const entrypoint = meta.entryPoint + ? path.join(ROOT_DIR, meta.entryPoint) + : undefined; + + const target_page = pages.find( + (p) => p.local_path == entrypoint, + ); + + if (!target_page || !meta.entryPoint) { + return undefined; + } + + const { file_name, local_path, url_path } = target_page; + + return { + path: outputPath, + hash: path.basename( + outputPath, + path.extname(outputPath), + ), + type: "text/javascript", + entrypoint: meta.entryPoint, + file_name, + local_path, + url_path, + }; + }); + + if (artifacts?.[0] && artifacts.length > 0) { + for (let i = 0; i < artifacts.length; i++) { + const artifact = artifacts[i]; + if ( + artifact?.local_path && + global.API_ROUTES_BUNDLER_CTX_MAP + ) { + global.API_ROUTES_BUNDLER_CTX_MAP[ + artifact.local_path + ] = artifact; + } + } + } + + // const elapsed = (performance.now() - build_start).toFixed(0); + // log.success(`API Routes [Built] in ${elapsed}ms`); + }); + }, + }; + + return artifactTracker; +} diff --git a/src/functions/bundler/plugins/esbuild-ctx-artifact-tracker.ts b/src/functions/bundler/plugins/esbuild-ctx-artifact-tracker.ts index 55bcdbb..c34d75e 100644 --- a/src/functions/bundler/plugins/esbuild-ctx-artifact-tracker.ts +++ b/src/functions/bundler/plugins/esbuild-ctx-artifact-tracker.ts @@ -4,6 +4,7 @@ import { log } from "../../../utils/log"; import grabArtifactsFromBundledResults from "../grab-artifacts-from-bundled-result"; import pagesSSRContextBundler from "../pages-ssr-context-bundler"; import buildOnstartErrorHandler from "../build-on-start-error-handler"; +import apiRoutesContextBundler from "../api-routes-context-bundler"; let build_start = 0; let build_starts = 0; @@ -69,6 +70,12 @@ export default function esbuildCTXArtifactTracker({ } else { pagesSSRContextBundler(); } + + if (global.API_ROUTES_BUNDLER_CTX) { + global.API_ROUTES_BUNDLER_CTX.rebuild(); + } else { + apiRoutesContextBundler(); + } }); }, }; diff --git a/src/functions/bunext-init.ts b/src/functions/bunext-init.ts index 5402efc..2194b1d 100644 --- a/src/functions/bunext-init.ts +++ b/src/functions/bunext-init.ts @@ -33,6 +33,7 @@ declare global { var LAST_BUILD_TIME: number; var BUNDLER_CTX_MAP: { [k: string]: BundlerCTXMap } | undefined; var SSR_BUNDLER_CTX_MAP: { [k: string]: BundlerCTXMap } | undefined; + var API_ROUTES_BUNDLER_CTX_MAP: { [k: string]: BundlerCTXMap } | undefined; var BUNDLER_REBUILDS: 0; var PAGES_SRC_WATCHER: FSWatcher | undefined; var CURRENT_VERSION: string | undefined; @@ -41,6 +42,7 @@ declare global { var SKIPPED_BROWSER_MODULES: Set; var BUNDLER_CTX: BuildContext | undefined; var SSR_BUNDLER_CTX: BuildContext | undefined; + var API_ROUTES_BUNDLER_CTX: BuildContext | undefined; var DIR_NAMES: ReturnType; var REACT_IMPORTS_MAP: { imports: Record }; var REACT_DOM_SERVER: any; @@ -57,6 +59,7 @@ export default async function bunextInit() { global.HMR_CONTROLLERS = []; global.BUNDLER_CTX_MAP = {}; global.SSR_BUNDLER_CTX_MAP = {}; + global.API_ROUTES_BUNDLER_CTX_MAP = {}; global.BUNDLER_REBUILDS = 0; global.REBUILD_RETRIES = 0; global.PAGE_FILES = []; diff --git a/src/functions/server/handle-routes.ts b/src/functions/server/handle-routes.ts index 469c4f5..21228e8 100644 --- a/src/functions/server/handle-routes.ts +++ b/src/functions/server/handle-routes.ts @@ -44,15 +44,18 @@ export default async function ({ req }: Params): Promise { ); } - const routeParams: BunxRouteParams = await grabRouteParams({ req }); + const routeParams: BunxRouteParams = await grabRouteParams({ + req, + query: match.query, + }); let module: any; const now = Date.now(); - if (global.SSR_BUNDLER_CTX_MAP?.[match.filePath]?.path) { + if (is_dev && global.API_ROUTES_BUNDLER_CTX_MAP?.[match.filePath]?.path) { const target_import = path.join( ROOT_DIR, - global.SSR_BUNDLER_CTX_MAP[match.filePath].path, + global.API_ROUTES_BUNDLER_CTX_MAP[match.filePath].path, ); module = await import(`${target_import}?t=${now}`); @@ -127,3 +130,35 @@ export default async function ({ req }: Params): Promise { return undefined; } + +// const relative_path = match.filePath.replace(API_DIR, ""); +// const relative_module_js_file = relative_path.replace(/\.tsx?$/, ".js"); +// const bun_module_file = path.join( +// BUNX_CWD_MODULE_CACHE_DIR, +// "api", +// relative_module_js_file, +// ); + +// if (existsSync(bun_module_file)) { +// module = await import(`${bun_module_file}?t=${now}`); +// } else { +// const import_path = is_dev +// ? `${match.filePath}?t=${now}` +// : match.filePath; +// module = await import(import_path); +// } + +// if (is_dev) { +// const tmp_path = `${match.filePath}.${now}${AppData["BunextTmpFileExt"]}`; +// cpSync(match.filePath, tmp_path); +// module = await import(`${tmp_path}?t=${now}`); +// try { +// unlinkSync(tmp_path); +// } catch (error) {} +// } else { +// // const import_path = is_dev ? `${match.filePath}?t=${now}` : match.filePath; +// module = await import(match.filePath); +// } + +// const import_path = is_dev ? `${match.filePath}?t=${now}` : match.filePath; +// module = await import(import_path); diff --git a/src/functions/server/watcher-esbuild-ctx.ts b/src/functions/server/watcher-esbuild-ctx.ts index 3b703ce..b1a2a33 100644 --- a/src/functions/server/watcher-esbuild-ctx.ts +++ b/src/functions/server/watcher-esbuild-ctx.ts @@ -5,6 +5,7 @@ import { log } from "../../utils/log"; import allPagesESBuildContextBundler from "../bundler/all-pages-esbuild-context-bundler"; import serverPostBuildFn from "./server-post-build-fn"; import fullRebuild from "./full-rebuild"; +import { AppData } from "../../data/app-data"; const { ROOT_DIR } = grabDirNames(); @@ -22,6 +23,10 @@ export default async function watcherEsbuildCTX() { return; } + if (filename.endsWith(AppData["BunextTmpFileExt"])) { + return; + } + const full_file_path = path.join(ROOT_DIR, filename); const does_file_exist = existsSync(full_file_path); const file_stat = does_file_exist diff --git a/src/utils/grab-all-pages.ts b/src/utils/grab-all-pages.ts index 781df55..44dd8a3 100644 --- a/src/utils/grab-all-pages.ts +++ b/src/utils/grab-all-pages.ts @@ -7,6 +7,7 @@ import pagePathTransform from "./page-path-transform"; type Params = { exclude_api?: boolean; + api_only?: boolean; }; export default function grabAllPages(params?: Params) { @@ -18,6 +19,10 @@ export default function grabAllPages(params?: Params) { return pages.filter((p) => !Boolean(p.url_path.startsWith("/api/"))); } + if (params?.api_only) { + return pages.filter((p) => Boolean(p.url_path.startsWith("/api/"))); + } + return pages; } diff --git a/src/utils/grab-route-params.ts b/src/utils/grab-route-params.ts index 956f36c..e626c5b 100644 --- a/src/utils/grab-route-params.ts +++ b/src/utils/grab-route-params.ts @@ -1,12 +1,15 @@ +import _ from "lodash"; import type { BunxRouteParams } from "../types"; import deserializeQuery from "./deserialize-query"; type Params = { req: Request; + query?: any; }; export default async function grabRouteParams({ req, + query: passed_query, }: Params): Promise { const url = new URL(req.url); @@ -23,7 +26,7 @@ export default async function grabRouteParams({ const routeParams: BunxRouteParams = { req, url, - query, + query: _.merge(query, passed_query), body, server: global.SERVER, };