diff --git a/README.md b/README.md index c4b097c..5ef525e 100644 --- a/README.md +++ b/README.md @@ -742,6 +742,9 @@ const config: BunextConfig = { globalVars: { MY_API_URL: "https://api.example.com", }, + public_envs: { + BUNEXT_PUBLIC_APP_NAME: "My App", + }, development: false, // forced by the CLI; set manually if needed }; @@ -755,6 +758,7 @@ export default config; | `distDir` | `string` | `.bunext` | Internal artifact directory | | `assetsPrefix` | `string` | `_bunext/static` | URL prefix for static assets | | `globalVars` | `{ [k: string]: any }` | — | Variables injected globally at build time | +| `public_envs` | `Record` | — | Public env vars exposed to the client via `window.process.env` (see [Environment Variables](#environment-variables)) | | `development` | `boolean` | — | Overridden to `true` by `bunext dev` automatically | | `defaultCacheExpiry` | `number` | `3600` | Global page cache expiry in seconds | | `middleware` | `(params: BunextConfigMiddlewareParams) => Response \| undefined \| Promise<...>` | — | Global middleware — see [Middleware](#middleware) | @@ -909,9 +913,35 @@ bun run server.ts ## Environment Variables -| Variable | Description | -| -------- | ------------------------------------------------------- | -| `PORT` | Override the server port (takes precedence over config) | +| Variable | Description | +| ------------------ | ------------------------------------------------------------------------------------------------ | +| `PORT` | Override the server port (takes precedence over config) | +| `BUNEXT_PUBLIC_*` | Any env var prefixed with `BUNEXT_PUBLIC_` is exposed to the client via `window.process.env` | + +### Public Environment Variables + +Variables prefixed with `BUNEXT_PUBLIC_` are automatically injected into every page as `window.process.env`. You can also define public envs in config via `public_envs` (config values override env vars of the same name): + +```bash +# .env +BUNEXT_PUBLIC_API_URL=https://api.example.com +``` + +```ts +// bunext.config.ts +const config: BunextConfig = { + public_envs: { + BUNEXT_PUBLIC_APP_NAME: "My App", + }, +}; +``` + +```tsx +// Client component — available after hydration +const apiUrl = window.process.env.BUNEXT_PUBLIC_API_URL; +``` + +`window.process.env` always includes `NODE_ENV` (`"development"` or `"production"`). --- diff --git a/dist/functions/server/web-pages/generate-web-html.js b/dist/functions/server/web-pages/generate-web-html.js index ec27bc7..fbdf578 100644 --- a/dist/functions/server/web-pages/generate-web-html.js +++ b/dist/functions/server/web-pages/generate-web-html.js @@ -43,11 +43,13 @@ export default async function genWebHTML({ component: Main, pageProps, bundledMa const RootHead = root_module?.Head; const dev = isDevelopment(); const final_meta = _.merge(root_meta, page_meta); - // const public_envs = Object.keys(process.env).filter((e) => - // e.startsWith(`NEXT_PUBLIC_`), - // ); + const public_envs = Object.fromEntries(Object.entries(process.env).filter(([k]) => k.startsWith("BUNEXT_PUBLIC_"))); const client_process = { - env: {}, + env: { + NODE_ENV: dev ? "development" : "production", + ...public_envs, + ...global.CONFIG.public_envs, + }, }; let final_component = (_jsxs("html", { ...html_props, children: [_jsxs("head", { children: [_jsx("meta", { charSet: "utf-8", "data-bunext-head": true }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1.0", "data-bunext-head": true }), final_meta ? grabWebMetaHTML({ meta: final_meta }) : null, bundledMap?.css_path ? (_jsx("link", { rel: "stylesheet", href: `/${bundledMap.css_path}`, "data-bunext-head": true })) : null, _jsx("script", { dangerouslySetInnerHTML: { __html: `window.${ClientWindowPagePropsName} = ${serializedProps};\nwindow.process = ${JSON.stringify(client_process)}`, diff --git a/dist/types/index.d.ts b/dist/types/index.d.ts index 480b2ea..20f73ba 100644 --- a/dist/types/index.d.ts +++ b/dist/types/index.d.ts @@ -72,6 +72,10 @@ export type BunextConfig = { * absolute), or a RegEx pattern. */ exclude_watch_patterns?: (string | RegExp)[]; + /** + * Public environment variables to be passed to the client + */ + public_envs?: Record; }; export type BunextConfigMiddlewareParams = { req: Request; diff --git a/package.json b/package.json index 5a1ce44..e326ffb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@moduletrace/bunext", - "version": "1.0.103", + "version": "1.0.104", "main": "dist/index.js", "module": "index.ts", "dependencies": { diff --git a/src/functions/server/web-pages/generate-web-html.tsx b/src/functions/server/web-pages/generate-web-html.tsx index 18232aa..9c49198 100644 --- a/src/functions/server/web-pages/generate-web-html.tsx +++ b/src/functions/server/web-pages/generate-web-html.tsx @@ -70,12 +70,18 @@ export default async function genWebHTML({ const final_meta = _.merge(root_meta, page_meta); - // const public_envs = Object.keys(process.env).filter((e) => - // e.startsWith(`NEXT_PUBLIC_`), - // ); + const public_envs = Object.fromEntries( + Object.entries(process.env).filter(([k]) => + k.startsWith("BUNEXT_PUBLIC_"), + ), + ); const client_process = { - env: {}, + env: { + NODE_ENV: dev ? "development" : "production", + ...public_envs, + ...global.CONFIG.public_envs, + }, }; let final_component = ( diff --git a/src/types/index.ts b/src/types/index.ts index 0aad406..a27d179 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -89,6 +89,10 @@ export type BunextConfig = { * absolute), or a RegEx pattern. */ exclude_watch_patterns?: (string | RegExp)[]; + /** + * Public environment variables to be passed to the client + */ + public_envs?: Record; }; export type BunextConfigMiddlewareParams = {