bini-env 1.1.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,10 +18,11 @@ Powered by [Hono](https://hono.dev) — reads env vars from the Hono request con
18
18
 
19
19
  - **Hono-native** — `getEnv(c, key)` / `requireEnv(c, key)` read directly from the Hono request context
20
20
  - **Universal runtime** — CF Workers, Node, Bun, Deno, Vercel Edge, Netlify Edge — Hono's adapter handles all of it
21
- - **Zero dotenv** — no `.env` parsing at runtime; vars come from the host platform
21
+ - **Zero-config dev secrets** — non-prefixed `.env` values are always mirrored into `process.env` automatically during `vite dev` / `vite preview`, so Node-hosted Hono routes can read them with no manual loop
22
22
  - **Vite-native dev** — `.env` loading, file watching, and server restarts are handled by Vite itself
23
+ - **`envDir`-aware** — respects a custom `envDir` or `root` in your `vite.config.ts` instead of always assuming the working directory
23
24
  - **Dev banner** — prints `ß Bini.js (dev/preview)` and lists detected `.env` files on server start
24
- - **Prefix control** — `BINI_` and `VITE_` included out of the box, extend with your own
25
+ - **Fixed, predictable prefixes** — only `BINI_` and `VITE_` are ever exposed to the browser; there's no config surface that can accidentally widen it
25
26
  - **Tree-shakeable** — nothing lands in the client bundle that shouldn't
26
27
  - **Typed** — full TypeScript support with an exported `HonoContext` type
27
28
 
@@ -55,6 +56,8 @@ export default defineConfig({
55
56
  })
56
57
  ```
57
58
 
59
+ `biniEnv()` takes no options — there's nothing to configure.
60
+
58
61
  **2. Read env vars in your Hono handlers**
59
62
 
60
63
  ```ts
@@ -88,6 +91,8 @@ app.post('/hello', async (c) => {
88
91
  export default app
89
92
  ```
90
93
 
94
+ That's it — no manual `loadEnv` loop in `vite.config.ts` needed. `MY_API_KEY` just needs to exist in your `.env` file with no prefix, and `requireEnv` will find it during dev and preview.
95
+
91
96
  ---
92
97
 
93
98
  ## Usage Pattern
@@ -128,75 +133,127 @@ app.post('/example', async (c) => {
128
133
 
129
134
  ---
130
135
 
136
+ ## How It Works in Dev
137
+
138
+ When you run `vite dev` (or `vite preview`), `biniEnv()` automatically loads your `.env*` files with Vite's own `loadEnv` and mirrors every **non-prefixed** variable into `process.env`. This is what lets `getEnv` / `requireEnv` resolve server-side secrets inside Hono API routes without any extra setup — Hono's `env(c)` adapter reads from `process.env` on Node, and `process.env` is now populated for you.
139
+
140
+ It reads `.env` files from the same place Vite does: your configured `envDir` if you've set one, otherwise your `root`, otherwise the working directory. A custom `envDir` in `vite.config.ts` is respected automatically. If you've set `envDir: false` in your Vite config to disable `.env` loading entirely, `bini-env` respects that too and skips the mirror.
141
+
142
+ It happens silently in the background when it succeeds — no terminal log, no banner line, nothing to configure. Your existing dev server output (the `ß Bini.js` banner, detected `.env` files, local/network URLs) stays exactly as it was:
143
+
144
+ ```
145
+ ß Bini.js (dev)
146
+ ➜ Environments: .env.local, .env
147
+ ➜ Local: http://localhost:3000/
148
+ ➜ Network: http://192.168.1.7:3000/
149
+ ```
150
+
151
+ If loading or mirroring the `.env` file fails for any reason, `bini-env` prints a warning — but the dev server still boots either way, it never throws:
152
+
153
+ ```
154
+ 10:45:55 (!) [bini-env] Failed to inject .env into process.env: <reason>
155
+ ```
156
+
157
+ This happens automatically — there is no flag to turn it off. It's core behavior of `biniEnv()`, not an opt-in feature.
158
+
159
+ **Precedence:** a value already present in `process.env` (set by your OS, shell, or CI) always wins. `.env` file values only fill in variables that aren't already set — they never override real environment configuration.
160
+
161
+ **What gets mirrored:**
162
+
163
+ | Variable | Mirrored into `process.env`? |
164
+ |----------|------------------------------|
165
+ | No prefix (`DATABASE_URL`) | ✅ Yes |
166
+ | `BINI_` / `VITE_` prefixed | ❌ No — already handled via `import.meta.env` |
167
+ | Empty value (`KEY=`) | ❌ No — skipped, so `requireEnv` still fails loudly on a blank placeholder |
168
+ | Already set in the OS/CI environment | ❌ No — existing value is kept |
169
+
170
+ **Scope:**
171
+
172
+ - Runs only for `vite dev` and `vite preview` (`command === 'serve'`)
173
+ - **Never** runs for `vite build` — production bundles and `process.env` at build time are untouched
174
+ - **Production** uses your hosting platform's native env injection (`bini-server`, Vercel, Netlify, Cloudflare, etc.) — nothing extra to do there
175
+ - This behavior is always on — there is no option to disable it
176
+
177
+ **Migrating from a manual loop:** if your project already has this in `vite.config.ts`, you can delete it — `biniEnv()` now does it for you:
178
+
179
+ ```ts
180
+ // vite.config.ts — no longer needed, remove this
181
+ const env = loadEnv(mode, process.cwd(), '')
182
+ for (const [key, value] of Object.entries(env)) {
183
+ if (!key.startsWith('BINI_') && !key.startsWith('VITE_')) {
184
+ process.env[key] = value
185
+ }
186
+ }
187
+ ```
188
+
189
+ ```ts
190
+ // vite.config.ts — this is all you need now
191
+ export default defineConfig({
192
+ plugins: [biniEnv()]
193
+ })
194
+ ```
195
+
196
+ ---
197
+
131
198
  ## Environment Prefixes
132
199
 
133
200
  ### The Truth About Prefixes
134
201
 
135
- **Both `BINI_` and `VITE_` work exactly the same way.** Neither is special.
202
+ **Both `BINI_` and `VITE_` work exactly the same way.** Neither is special, and neither can be changed — `biniEnv()` always exposes exactly these two prefixes to the browser, nothing more.
136
203
 
137
- | Prefix | Exposed to Browser? | Default Behavior |
138
- |--------|--------------------|------------------|
139
- | `BINI_` | **Yes** | Included in `envPrefix` by default |
140
- | `VITE_` | **Yes** | Vite's default prefix (always included) |
141
- | Any prefix you add | **Yes** | If you add it to `envPrefix` |
142
- | No prefix | **No** | Not exposed to browser |
204
+ | Prefix | Exposed to Browser? |
205
+ |--------|--------------------|
206
+ | `BINI_` | **Yes** — always |
207
+ | `VITE_` | **Yes** — always (Vite's own default prefix) |
208
+ | No prefix | **No** — mirrored into `process.env` in dev/preview instead |
143
209
 
144
210
  ### What `biniEnv()` Actually Does
145
211
 
146
212
  ```ts
147
- // biniEnv() sets envPrefix to:
213
+ // biniEnv() always sets envPrefix to:
148
214
  envPrefix: ['BINI_', 'VITE_']
149
215
 
150
216
  // This tells Vite: "Expose ALL variables starting with BINI_ or VITE_ to import.meta.env"
217
+ // Everything else is mirrored into process.env during vite dev / preview
151
218
  ```
152
219
 
153
220
  **This means:**
154
221
  - ✅ `BINI_API_KEY` → available in browser via `import.meta.env.BINI_API_KEY`
155
222
  - ✅ `VITE_API_URL` → available in browser via `import.meta.env.VITE_API_URL`
156
- - ❌ `DATABASE_URL` → NOT available in browser (no prefix)
223
+ - ❌ `DATABASE_URL` → NOT available in browser (no prefix) — available server-side via `getEnv`/`requireEnv`
157
224
 
158
225
  ### How to Keep Secrets Secret
159
226
 
160
227
  **Use no prefix for secrets:**
161
228
 
162
229
  ```env
163
- # ✅ NOT exposed to browser
230
+ # ✅ NOT exposed to browser — mirrored into process.env for server-side use only
164
231
  DATABASE_URL=postgres://...
165
232
  STRIPE_SECRET_KEY=sk_live_...
166
233
  AWS_SECRET_KEY=...
167
234
  ```
168
235
 
169
- **If you want `BINI_` to be private, remove it from `envPrefix`:**
170
-
171
- ```ts
172
- // vite.config.ts
173
- biniEnv({ envPrefix: ['VITE_'] }) // Only VITE_ exposed
174
- // BINI_ is no longer exposed to the browser
175
- ```
236
+ That's the whole rule. Since the prefix list is fixed, there's no config mistake that can widen what gets exposed — anything without `BINI_` or `VITE_` in front of it simply never reaches the browser.
176
237
 
177
238
  ### Choosing the Right Prefix
178
239
 
179
- Since `BINI_` and `VITE_` are both exposed by default:
180
-
181
240
  ```env
182
241
  # ✅ Use no prefix for secrets
183
242
  DATABASE_URL=postgres://...
184
243
  STRIPE_SECRET_KEY=sk_live_...
185
244
 
186
- # ✅ Use any prefix for public config
245
+ # ✅ Use BINI_ or VITE_ for public config
187
246
  BINI_API_URL=https://api.example.com
188
247
  VITE_ANALYTICS_ID=UA-XXXX
189
- PUBLIC_APP_NAME=MyApp # If you add PUBLIC_ to envPrefix
190
248
  ```
191
249
 
192
250
  ### Prefix Summary
193
251
 
194
- | Prefix | Exposed to Browser? | Use For |
195
- |--------|--------------------|---------|
196
- | No prefix | ❌ Never | **SECRETS** — API keys, database URLs, tokens |
197
- | `BINI_` | ✅ Yes (by default) | Public config (same as VITE_) |
198
- | `VITE_` | ✅ Yes (always) | Public config (Vite's default) |
199
- | Custom prefix | ✅ If in `envPrefix` | Public config you want to control |
252
+ | Prefix | Exposed to Browser? | Mirrored to `process.env` (dev/preview)? | Use For |
253
+ |--------|--------------------|-------------------------------------------|---------|
254
+ | No prefix | ❌ Never | ✅ Yes (unless empty or already set) | **SECRETS** — API keys, database URLs, tokens |
255
+ | `BINI_` | ✅ Always | ❌ No | Public config |
256
+ | `VITE_` | ✅ Always | ❌ No | Public config (Vite's default) |
200
257
 
201
258
  ---
202
259
 
@@ -206,8 +263,8 @@ PUBLIC_APP_NAME=MyApp # If you add PUBLIC_ to envPrefix
206
263
 
207
264
  | Platform | Runtime | Env source | How Hono reads it |
208
265
  |----------|---------|------------|-------------------|
209
- | Node.js | Node | System env / platform dashboard | `process.env` ✅ |
210
- | Bun | Bun | System env / platform dashboard | `process.env` ✅ |
266
+ | Node.js | Node | System env / platform dashboard / `bini-env` dev mirror | `process.env` ✅ |
267
+ | Bun | Bun | System env / platform dashboard / `bini-env` dev mirror | `process.env` ✅ |
211
268
  | Vercel Edge | V8 isolate | Project settings → Env Vars | `process.env` ✅ |
212
269
  | Netlify Edge | Deno | Site settings → Env Vars | `Deno.env.get()` ✅ |
213
270
  | Cloudflare Workers | V8 isolate | `wrangler.toml` / dashboard | CF bindings via `c.env` ✅ |
@@ -221,16 +278,21 @@ PUBLIC_APP_NAME=MyApp # If you add PUBLIC_ to envPrefix
221
278
 
222
279
  ### The plugin — `biniEnv()`
223
280
 
224
- Does two things: tells Vite which env prefixes to expose to `import.meta.env`, and prints the `ß Bini.js` banner with detected `.env` files when the dev or preview server starts.
281
+ Does three things: tells Vite to expose `BINI_` and `VITE_` prefixed vars to `import.meta.env`, mirrors non-prefixed `.env` values into `process.env` during `vite dev` / `vite preview` so Hono routes can read them, and prints the `ß Bini.js` banner with detected `.env` files when the dev or preview server starts.
225
282
 
226
283
  ```ts
227
284
  // what biniEnv() does internally
228
- config() {
229
- return { envPrefix: ['BINI_', 'VITE_', ...yourExtras] }
285
+ config(userConfig, { command, mode }) {
286
+ if (command === 'serve') {
287
+ const envDir = userConfig.envDir ?? userConfig.root ?? process.cwd()
288
+ // mirrors non-prefixed, non-empty .env values into process.env
289
+ // silent on success, warns on failure — no opt-out
290
+ }
291
+ return { envPrefix: ['BINI_', 'VITE_'] }
230
292
  }
231
293
  ```
232
294
 
233
- On server start you will see:
295
+ The `process.env` mirror runs silently — it doesn't add any lines to the banner. On server start you will see the same banner as before:
234
296
 
235
297
  ```
236
298
  ß Bini.js (dev)
@@ -239,11 +301,11 @@ On server start you will see:
239
301
  ➜ Network: http://192.168.1.7:3000/
240
302
  ```
241
303
 
242
- Vite handles everything else natively: loading `.env`, `.env.local`, `.env.[mode]` files during dev, watching and restarting on change, injecting prefixed vars into `import.meta.env` at build time, and HMR when env files change. bini-env does not reimplement any of that.
304
+ Vite handles the rest natively: loading `.env`, `.env.local`, `.env.[mode]` files during dev, watching and restarting on change, injecting prefixed vars into `import.meta.env` at build time, and HMR when env files change.
243
305
 
244
306
  ### The env functions — `getEnv` / `requireEnv`
245
307
 
246
- Both functions read exclusively from `env(c)` via `hono/adapter` — the Hono request context. There are no `process.env` fallbacks. Every read is request-scoped and resolved by Hono's adapter for the current platform.
308
+ Both functions read exclusively from `env(c)` via `hono/adapter` — the Hono request context. There is no direct `process.env` access inside `getEnv`/`requireEnv` themselves; on Node, Hono's adapter reads from `process.env`, which is why the dev-mode mirror above is what makes non-prefixed secrets resolvable there. Every read is request-scoped and resolved by Hono's adapter for the current platform.
247
309
 
248
310
  `dotenv` is never used. In production, vars are set in your hosting platform's environment config.
249
311
 
@@ -300,19 +362,15 @@ On failure, the terminal will show:
300
362
  -> Set it in your platform's env config or hosting dashboard.
301
363
  ```
302
364
 
303
- ### `biniEnv(options?)`
365
+ ### `biniEnv()`
304
366
 
305
- Vite plugin. Registers `BINI_` and `VITE_` as env prefixes and optionally adds more.
367
+ Vite plugin. Takes no options. Exposes `BINI_` and `VITE_` prefixed vars to `import.meta.env`, and mirrors every other (non-empty) `.env` value into `process.env` during dev/preview.
306
368
 
307
369
  ```ts
308
370
  biniEnv()
309
- // or with extra prefixes
310
- biniEnv({ envPrefix: ['MY_PUBLIC_'] })
311
371
  ```
312
372
 
313
- | Option | Type | Default | Description |
314
- |--------|------|---------|-------------|
315
- | `envPrefix` | `string \| string[]` | `[]` | Extra prefixes to expose to `import.meta.env`, in addition to `BINI_` and `VITE_` |
373
+ There's nothing to configure — no prefix list to extend, no flag to disable the `process.env` mirror. It reads `.env` files from your `envDir` (or `root`, or the working directory), never runs on `vite build`, never overrides a value already set in `process.env`, and skips the mirror entirely if you've set `envDir: false`.
316
374
 
317
375
  ### `biniLogger`
318
376
 
@@ -352,30 +410,35 @@ function readDbConfig(c: HonoContext) {
352
410
  # ❌ BAD - This will be exposed to the browser!
353
411
  BINI_DATABASE_URL=postgres://...
354
412
 
355
- # ✅ GOOD - Not exposed to browser
413
+ # ✅ GOOD - Not exposed to browser, mirrored into process.env for server-side use
356
414
  DATABASE_URL=postgres://...
357
415
  ```
358
416
 
359
417
  ### Rule 2: Use No Prefix for Anything Secret
360
418
  ```env
361
- # ✅ Safe - Not exposed
419
+ # ✅ Safe - Not exposed, available server-side via getEnv/requireEnv
362
420
  DATABASE_URL=postgres://...
363
421
  STRIPE_SECRET=sk_live_...
364
422
  AWS_SECRET_KEY=...
365
423
  ```
366
424
 
367
- ### Rule 3: Use Any Prefix for Public Data
425
+ ### Rule 3: Use `BINI_` or `VITE_` for Public Data
368
426
  ```env
369
427
  # ✅ Safe - Public data
370
428
  BINI_API_URL=https://api.example.com
371
429
  VITE_GA_ID=UA-XXXXX
372
- PUBLIC_APP_NAME=MyApp
373
430
  ```
374
431
 
375
- ### Rule 4: Remove `BINI_` from envPrefix if You Want It Private
376
- ```ts
377
- // To make BINI_ private, remove it from envPrefix
378
- biniEnv({ envPrefix: ['VITE_'] })
432
+ ### Rule 4: Don't Leave Secret Placeholders Empty
433
+
434
+ An empty value (`API_KEY=`) is skipped by the `process.env` mirror, so `requireEnv` will correctly throw instead of silently succeeding with an empty string. Fill in real values, or remove the line entirely if it's genuinely unused.
435
+
436
+ ### Rule 5: Let OS/CI Env Vars Win in Dev
437
+
438
+ The `process.env` mirror never overrides a variable that's already set. If you need to override a `.env` value locally (e.g. pointing at a different database), export it in your shell before starting the dev server rather than editing `.env`:
439
+
440
+ ```bash
441
+ DATABASE_URL=postgres://staging... pnpm dev
379
442
  ```
380
443
 
381
444
  ---
@@ -384,11 +447,11 @@ biniEnv({ envPrefix: ['VITE_'] })
384
447
 
385
448
  | Metric | Dev | Prod |
386
449
  |--------|-----|------|
387
- | File reads | 0 | 0 |
388
- | Runtime cost | ~0ms | 0 |
450
+ | File reads | 1 (`loadEnv`, cached by Vite) | 0 |
451
+ | Runtime cost | ~0ms (mirror runs once at server start) | 0 |
389
452
  | Bundle impact | Minimal | Tree-shaken |
390
453
 
391
- No dotenv. No disk reads. No caching layer. `getEnv` is a direct call to Hono's adapter on every invocation — request-scoped and correct.
454
+ No dotenv. No per-request disk reads. No caching layer beyond Vite's own. `getEnv` is a direct call to Hono's adapter on every invocation — request-scoped and correct. The `process.env` mirror runs exactly once, when the dev or preview server starts.
392
455
 
393
456
  ---
394
457
 
@@ -396,15 +459,29 @@ No dotenv. No disk reads. No caching layer. `getEnv` is a direct call to Hono's
396
459
 
397
460
  ### "My BINI_ var is exposed to the browser!"
398
461
 
399
- Yes — by design. `BINI_` is included in `envPrefix` by default. Use no prefix for secrets.
462
+ Yes — by design. `BINI_` is always exposed, the same as `VITE_`. Use no prefix for secrets.
400
463
 
401
464
  ### "Env var is undefined in production"
402
465
 
403
- Vars set in `.env` files are only loaded by Vite during local development. In production, set your variables in your hosting platform's environment dashboard (Vercel, Netlify, Cloudflare, etc.).
466
+ Vars set in `.env` files are only loaded by Vite (and mirrored into `process.env`) during local development and preview. In production, set your variables in your hosting platform's environment dashboard (Vercel, Netlify, Cloudflare, etc.).
404
467
 
405
468
  ### "Works in dev, undefined in prod"
406
469
 
407
- Same as above. Local dev works because Vite loads `.env` files automatically. Production requires platform-level configuration.
470
+ Same as above. Local dev works because `biniEnv()` loads `.env` files and mirrors non-prefixed vars into `process.env` automatically. Production requires platform-level configuration.
471
+
472
+ ### "My .env value isn't taking effect in dev"
473
+
474
+ Two likely causes:
475
+ - Check your shell and CI environment first — the `process.env` mirror never overrides a variable that's already set there. Unset it in your shell, or override it directly in your shell instead of `.env`.
476
+ - Check the value isn't empty (`KEY=`) — empty values are skipped on purpose.
477
+
478
+ ### "requireEnv still throws even though my key is in .env"
479
+
480
+ If the value is `KEY=` with nothing after the `=`, it's treated as unset and skipped by design — this is intentional so a blank placeholder doesn't silently pass as configured. Give it a real value.
481
+
482
+ ### "bini-env isn't reading my .env file from the right folder"
483
+
484
+ `biniEnv()` reads from your Vite `envDir` if you've set one, otherwise your `root`, otherwise the working directory — the same resolution Vite itself uses. Double check `envDir`/`root` in `vite.config.ts` if your `.env` lives somewhere non-standard.
408
485
 
409
486
  ### "Cloudflare secret not found"
410
487
 
@@ -432,7 +509,7 @@ Add to your `tsconfig.json` or entry file:
432
509
 
433
510
  | Tool | Version | Notes |
434
511
  |------|---------|-------|
435
- | Vite | 8.x | `envPrefix` hook is fully compatible; empty prefix array is never passed |
512
+ | Vite | 8.x | `envPrefix` hook and `loadEnv` are fully compatible; empty prefix array is never passed |
436
513
  | Hono | 4.x | `hono/adapter` `env()` is stable across all 4.x releases |
437
514
  | TypeScript | 5.x | Full type support via exported `HonoContext` |
438
515
  | Node.js | ≥ 20.19 | Minimum required version |
@@ -447,4 +524,4 @@ PRs are welcome. The goal of this library is to stay minimal and runtime-cost-fr
447
524
 
448
525
  ## License
449
526
 
450
- MIT © Bini.js Team
527
+ MIT © Bini.js Team
package/dist/index.d.mts CHANGED
@@ -2,26 +2,7 @@ import { Context } from 'hono';
2
2
  import { Plugin } from 'vite';
3
3
  export { Plugin } from 'vite';
4
4
 
5
- /**
6
- * bini-env/src/index.ts
7
- *
8
- * Two responsibilities only:
9
- * 1. Tell Vite which env prefixes to expose to import.meta.env (biniEnv plugin)
10
- * 2. Read env vars from the Hono request context (getEnv / requireEnv)
11
- *
12
- * process.env is never used. All runtime env access goes through Hono's env() adapter,
13
- * which handles Node, Bun, Cloudflare Workers, Deno, and Vercel Edge natively.
14
- */
15
-
16
- /**
17
- * Accepts any Hono Context. Typed against Hono's own Context rather than a
18
- * loose duck type so misuse is caught at the call site.
19
- */
20
5
  type HonoContext = Context;
21
- interface BiniEnvPluginOptions {
22
- /** Extra prefixes to expose to import.meta.env, in addition to BINI_ and VITE_. */
23
- readonly envPrefix?: string | string[];
24
- }
25
6
  declare const biniLogger: {
26
7
  readonly info: (msg: string) => void;
27
8
  readonly warn: (msg: string) => void;
@@ -29,6 +10,6 @@ declare const biniLogger: {
29
10
  };
30
11
  declare function getEnv(c: HonoContext, key: string): string | undefined;
31
12
  declare function requireEnv(c: HonoContext, key: string): string;
32
- declare function biniEnv(options?: Readonly<BiniEnvPluginOptions>): Plugin;
13
+ declare function biniEnv(): Plugin;
33
14
 
34
- export { type BiniEnvPluginOptions, type HonoContext, biniEnv, biniLogger, getEnv, requireEnv };
15
+ export { type HonoContext, biniEnv, biniLogger, getEnv, requireEnv };
package/dist/index.d.ts CHANGED
@@ -2,26 +2,7 @@ import { Context } from 'hono';
2
2
  import { Plugin } from 'vite';
3
3
  export { Plugin } from 'vite';
4
4
 
5
- /**
6
- * bini-env/src/index.ts
7
- *
8
- * Two responsibilities only:
9
- * 1. Tell Vite which env prefixes to expose to import.meta.env (biniEnv plugin)
10
- * 2. Read env vars from the Hono request context (getEnv / requireEnv)
11
- *
12
- * process.env is never used. All runtime env access goes through Hono's env() adapter,
13
- * which handles Node, Bun, Cloudflare Workers, Deno, and Vercel Edge natively.
14
- */
15
-
16
- /**
17
- * Accepts any Hono Context. Typed against Hono's own Context rather than a
18
- * loose duck type so misuse is caught at the call site.
19
- */
20
5
  type HonoContext = Context;
21
- interface BiniEnvPluginOptions {
22
- /** Extra prefixes to expose to import.meta.env, in addition to BINI_ and VITE_. */
23
- readonly envPrefix?: string | string[];
24
- }
25
6
  declare const biniLogger: {
26
7
  readonly info: (msg: string) => void;
27
8
  readonly warn: (msg: string) => void;
@@ -29,6 +10,6 @@ declare const biniLogger: {
29
10
  };
30
11
  declare function getEnv(c: HonoContext, key: string): string | undefined;
31
12
  declare function requireEnv(c: HonoContext, key: string): string;
32
- declare function biniEnv(options?: Readonly<BiniEnvPluginOptions>): Plugin;
13
+ declare function biniEnv(): Plugin;
33
14
 
34
- export { type BiniEnvPluginOptions, type HonoContext, biniEnv, biniLogger, getEnv, requireEnv };
15
+ export { type HonoContext, biniEnv, biniLogger, getEnv, requireEnv };
package/dist/index.js CHANGED
@@ -51,7 +51,7 @@ function requireEnv(c, key) {
51
51
  }
52
52
  return val;
53
53
  }
54
- var DEFAULT_PREFIXES = ["BINI_", "VITE_"];
54
+ var ENV_PREFIXES = ["BINI_", "VITE_"];
55
55
  function detectEnvFiles(root) {
56
56
  try {
57
57
  const fs = __require("fs");
@@ -67,6 +67,21 @@ function detectEnvFiles(root) {
67
67
  return [];
68
68
  }
69
69
  }
70
+ function injectEnvIntoProcess(mode, envDir) {
71
+ if (typeof process === "undefined" || !process.env) return 0;
72
+ const { loadEnv } = __require("vite");
73
+ const loaded = loadEnv(mode, envDir, "");
74
+ let injected = 0;
75
+ for (const [key, value] of Object.entries(loaded)) {
76
+ const isPrefixed = ENV_PREFIXES.some((p) => key.startsWith(p));
77
+ if (isPrefixed) continue;
78
+ if (!value) continue;
79
+ if (process.env[key] !== void 0) continue;
80
+ process.env[key] = value;
81
+ injected++;
82
+ }
83
+ return injected;
84
+ }
70
85
  function printBanner(server, mode, root) {
71
86
  const original = server.printUrls.bind(server);
72
87
  server.printUrls = () => {
@@ -79,15 +94,21 @@ function printBanner(server, mode, root) {
79
94
  original();
80
95
  };
81
96
  }
82
- function biniEnv(options = {}) {
83
- const extraPrefixes = options.envPrefix ? Array.isArray(options.envPrefix) ? options.envPrefix : [options.envPrefix] : [];
84
- const envPrefix = [
85
- .../* @__PURE__ */ new Set([...DEFAULT_PREFIXES, ...extraPrefixes])
86
- ];
97
+ function biniEnv() {
87
98
  return {
88
99
  name: "bini-env",
89
- config() {
90
- return { envPrefix };
100
+ config(userConfig, { command, mode }) {
101
+ if (command === "serve" && userConfig.envDir !== false) {
102
+ const envDir = typeof userConfig.envDir === "string" ? userConfig.envDir : userConfig.root ?? process.cwd();
103
+ try {
104
+ injectEnvIntoProcess(mode, envDir);
105
+ } catch (err) {
106
+ biniLogger.warn(
107
+ `Failed to inject .env into process.env: ${err.message}`
108
+ );
109
+ }
110
+ }
111
+ return { envPrefix: [...ENV_PREFIXES] };
91
112
  },
92
113
  configureServer(server) {
93
114
  printBanner(server, "dev", server.config.root);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["env"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * bini-env/src/index.ts\n *\n * Two responsibilities only:\n * 1. Tell Vite which env prefixes to expose to import.meta.env (biniEnv plugin)\n * 2. Read env vars from the Hono request context (getEnv / requireEnv)\n *\n * process.env is never used. All runtime env access goes through Hono's env() adapter,\n * which handles Node, Bun, Cloudflare Workers, Deno, and Vercel Edge natively.\n */\n\nimport { env } from 'hono/adapter';\nimport type { Context } from 'hono';\nimport type { Plugin, UserConfig, ViteDevServer, PreviewServer } from 'vite';\n\n/* ==========================================================================\n ANSI helpers — used by biniLogger only\n ========================================================================== */\n\nconst C = {\n CYAN : '\\x1b[36m',\n RESET : '\\x1b[0m',\n YELLOW: '\\x1b[33m',\n RED : '\\x1b[31m',\n BOLD : '\\x1b[1m',\n DIM : '\\x1b[2m',\n} as const;\n\n/* ==========================================================================\n Public types\n ========================================================================== */\n\n/**\n * Accepts any Hono Context. Typed against Hono's own Context rather than a\n * loose duck type so misuse is caught at the call site.\n */\nexport type HonoContext = Context;\n\nexport interface BiniEnvPluginOptions {\n /** Extra prefixes to expose to import.meta.env, in addition to BINI_ and VITE_. */\n readonly envPrefix?: string | string[];\n}\n\n/* ==========================================================================\n Logger\n ========================================================================== */\n\nfunction _ts(): string {\n return `${C.DIM}${new Date().toLocaleTimeString('en-US', { hour12: false })}${C.RESET}`;\n}\n\nexport const biniLogger = {\n info(msg: string): void {\n console.log(`${_ts()} ${C.CYAN}${C.BOLD}[bini-env]${C.RESET} ${msg}`);\n },\n warn(msg: string): void {\n console.warn(\n `${_ts()} ${C.YELLOW}${C.BOLD}(!) [bini-env]${C.RESET} ${C.YELLOW}${msg}${C.RESET}`,\n );\n },\n error(msg: string, err?: unknown): void {\n const detail =\n err instanceof Error ? `\\n ${C.DIM}${err.message}${C.RESET}` : '';\n console.error(\n `${_ts()} ${C.RED}${C.BOLD}[bini-env] error${C.RESET} ${msg}${detail}`,\n );\n },\n} as const;\n\n/* ==========================================================================\n getEnv\n\n Reads exclusively from Hono's env() adapter. No process.env fallback.\n Hono handles all platform differences:\n Node / Bun → process.env\n Cloudflare → c.env bindings\n Deno / Netlify → Deno.env.get()\n Vercel Edge → process.env (V8 Node compat)\n ========================================================================== */\n\nexport function getEnv(c: HonoContext, key: string): string | undefined {\n const honoEnvs = env(c) as Record<string, string | undefined>;\n return honoEnvs[key];\n}\n\n/* ==========================================================================\n requireEnv\n\n Same as getEnv but throws immediately if the variable is missing or empty.\n Always handle the thrown error in your route's catch block.\n ========================================================================== */\n\nexport function requireEnv(c: HonoContext, key: string): string {\n const val = getEnv(c, key);\n\n if (!val) {\n biniLogger.error(\n `Missing required environment variable: \"${key}\"\\n` +\n ` ${C.DIM}-> Set it in your platform's env config or hosting dashboard.${C.RESET}`,\n );\n\n throw new Error(`[bini-env] Missing required environment variable: \"${key}\"`);\n }\n\n return val;\n}\n\n/* ==========================================================================\n biniEnv — Vite plugin\n\n Tells Vite which prefixes to expose to import.meta.env.\n Prints the ß Bini.js banner on dev and preview server start.\n Vite handles .env file loading, watching, server restarts, and HMR natively.\n ========================================================================== */\n\nconst DEFAULT_PREFIXES = ['BINI_', 'VITE_'] as const;\n\nfunction detectEnvFiles(root: string): string[] {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fs = require('node:fs') as typeof import('fs');\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const path = require('node:path') as typeof import('path');\n const nodeEnv = process.env.NODE_ENV ?? 'production';\n return [\n '.env.local',\n `.env.${nodeEnv}.local`,\n `.env.${nodeEnv}`,\n '.env',\n ].filter(f => fs.existsSync(path.join(root, f)));\n } catch {\n return [];\n }\n}\n\nfunction printBanner(server: ViteDevServer | PreviewServer, mode: 'dev' | 'preview', root: string): void {\n const original = server.printUrls.bind(server);\n server.printUrls = () => {\n console.log(`\\n ${C.CYAN}ß Bini.js${C.RESET} (${mode})`);\n const found = detectEnvFiles(root);\n if (found.length > 0) {\n console.log(` \\x1b[32m➜\\x1b[0m Environments: ${found.join(', ')}`);\n }\n original();\n };\n}\n\nexport function biniEnv(options: Readonly<BiniEnvPluginOptions> = {}): Plugin {\n const extraPrefixes = options.envPrefix\n ? (Array.isArray(options.envPrefix) ? options.envPrefix : [options.envPrefix])\n : [];\n\n const envPrefix = [\n ...new Set([...DEFAULT_PREFIXES, ...extraPrefixes]),\n ];\n\n return {\n name: 'bini-env',\n\n config(): UserConfig {\n return { envPrefix };\n },\n\n configureServer(server) {\n printBanner(server, 'dev', server.config.root);\n },\n\n configurePreviewServer(server) {\n printBanner(server, 'preview', server.config.root);\n },\n };\n}\n\n/* ==========================================================================\n Re-exports\n ========================================================================== */\n\nexport type { Plugin } from 'vite';"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["env"],"mappings":"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","file":"index.js","sourcesContent":["import { env } from 'hono/adapter';\nimport type { Context } from 'hono';\nimport type { Plugin, UserConfig, ViteDevServer, PreviewServer } from 'vite';\n\nconst C = {\n CYAN : '\\x1b[36m',\n RESET : '\\x1b[0m',\n YELLOW: '\\x1b[33m',\n RED : '\\x1b[31m',\n BOLD : '\\x1b[1m',\n DIM : '\\x1b[2m',\n} as const;\n\nexport type HonoContext = Context;\n\nfunction _ts(): string {\n return `${C.DIM}${new Date().toLocaleTimeString('en-US', { hour12: false })}${C.RESET}`;\n}\n\nexport const biniLogger = {\n info(msg: string): void {\n console.log(`${_ts()} ${C.CYAN}${C.BOLD}[bini-env]${C.RESET} ${msg}`);\n },\n warn(msg: string): void {\n console.warn(\n `${_ts()} ${C.YELLOW}${C.BOLD}(!) [bini-env]${C.RESET} ${C.YELLOW}${msg}${C.RESET}`,\n );\n },\n error(msg: string, err?: unknown): void {\n const detail =\n err instanceof Error ? `\\n ${C.DIM}${err.message}${C.RESET}` : '';\n console.error(\n `${_ts()} ${C.RED}${C.BOLD}[bini-env] error${C.RESET} ${msg}${detail}`,\n );\n },\n} as const;\n\nexport function getEnv(c: HonoContext, key: string): string | undefined {\n const honoEnvs = env(c) as Record<string, string | undefined>;\n return honoEnvs[key];\n}\n\nexport function requireEnv(c: HonoContext, key: string): string {\n const val = getEnv(c, key);\n\n if (!val) {\n biniLogger.error(\n `Missing required environment variable: \"${key}\"\\n` +\n ` ${C.DIM}-> Set it in your platform's env config or hosting dashboard.${C.RESET}`,\n );\n\n throw new Error(`[bini-env] Missing required environment variable: \"${key}\"`);\n }\n\n return val;\n}\n\nconst ENV_PREFIXES = ['BINI_', 'VITE_'] as const;\n\nfunction detectEnvFiles(root: string): string[] {\n try {\n const fs = require('node:fs') as typeof import('fs');\n const path = require('node:path') as typeof import('path');\n const nodeEnv = process.env.NODE_ENV ?? 'production';\n return [\n '.env.local',\n `.env.${nodeEnv}.local`,\n `.env.${nodeEnv}`,\n '.env',\n ].filter(f => fs.existsSync(path.join(root, f)));\n } catch {\n return [];\n }\n}\n\nfunction injectEnvIntoProcess(mode: string, envDir: string): number {\n if (typeof process === 'undefined' || !process.env) return 0;\n\n const { loadEnv } = require('vite') as typeof import('vite');\n const loaded = loadEnv(mode, envDir, '');\n let injected = 0;\n\n for (const [key, value] of Object.entries(loaded)) {\n const isPrefixed = ENV_PREFIXES.some((p) => key.startsWith(p));\n if (isPrefixed) continue;\n if (!value) continue;\n if (process.env[key] !== undefined) continue;\n process.env[key] = value;\n injected++;\n }\n\n return injected;\n}\n\nfunction printBanner(server: ViteDevServer | PreviewServer, mode: 'dev' | 'preview', root: string): void {\n const original = server.printUrls.bind(server);\n server.printUrls = () => {\n console.log(`\\n ${C.CYAN}ß Bini.js${C.RESET} (${mode})`);\n const found = detectEnvFiles(root);\n if (found.length > 0) {\n console.log(` \\x1b[32m➜\\x1b[0m Environments: ${found.join(', ')}`);\n }\n original();\n };\n}\n\nexport function biniEnv(): Plugin {\n return {\n name: 'bini-env',\n\n config(userConfig: UserConfig, { command, mode }): UserConfig {\n if (command === 'serve' && userConfig.envDir !== false) {\n const envDir =\n typeof userConfig.envDir === 'string'\n ? userConfig.envDir\n : userConfig.root ?? process.cwd();\n\n try {\n injectEnvIntoProcess(mode, envDir);\n } catch (err) {\n biniLogger.warn(\n `Failed to inject .env into process.env: ${(err as Error).message}`,\n );\n }\n }\n\n return { envPrefix: [...ENV_PREFIXES] };\n },\n\n configureServer(server) {\n printBanner(server, 'dev', server.config.root);\n },\n\n configurePreviewServer(server) {\n printBanner(server, 'preview', server.config.root);\n },\n };\n}\n\nexport type { Plugin } from 'vite';"]}
package/dist/index.mjs CHANGED
@@ -49,7 +49,7 @@ function requireEnv(c, key) {
49
49
  }
50
50
  return val;
51
51
  }
52
- var DEFAULT_PREFIXES = ["BINI_", "VITE_"];
52
+ var ENV_PREFIXES = ["BINI_", "VITE_"];
53
53
  function detectEnvFiles(root) {
54
54
  try {
55
55
  const fs = __require("fs");
@@ -65,6 +65,21 @@ function detectEnvFiles(root) {
65
65
  return [];
66
66
  }
67
67
  }
68
+ function injectEnvIntoProcess(mode, envDir) {
69
+ if (typeof process === "undefined" || !process.env) return 0;
70
+ const { loadEnv } = __require("vite");
71
+ const loaded = loadEnv(mode, envDir, "");
72
+ let injected = 0;
73
+ for (const [key, value] of Object.entries(loaded)) {
74
+ const isPrefixed = ENV_PREFIXES.some((p) => key.startsWith(p));
75
+ if (isPrefixed) continue;
76
+ if (!value) continue;
77
+ if (process.env[key] !== void 0) continue;
78
+ process.env[key] = value;
79
+ injected++;
80
+ }
81
+ return injected;
82
+ }
68
83
  function printBanner(server, mode, root) {
69
84
  const original = server.printUrls.bind(server);
70
85
  server.printUrls = () => {
@@ -77,15 +92,21 @@ function printBanner(server, mode, root) {
77
92
  original();
78
93
  };
79
94
  }
80
- function biniEnv(options = {}) {
81
- const extraPrefixes = options.envPrefix ? Array.isArray(options.envPrefix) ? options.envPrefix : [options.envPrefix] : [];
82
- const envPrefix = [
83
- .../* @__PURE__ */ new Set([...DEFAULT_PREFIXES, ...extraPrefixes])
84
- ];
95
+ function biniEnv() {
85
96
  return {
86
97
  name: "bini-env",
87
- config() {
88
- return { envPrefix };
98
+ config(userConfig, { command, mode }) {
99
+ if (command === "serve" && userConfig.envDir !== false) {
100
+ const envDir = typeof userConfig.envDir === "string" ? userConfig.envDir : userConfig.root ?? process.cwd();
101
+ try {
102
+ injectEnvIntoProcess(mode, envDir);
103
+ } catch (err) {
104
+ biniLogger.warn(
105
+ `Failed to inject .env into process.env: ${err.message}`
106
+ );
107
+ }
108
+ }
109
+ return { envPrefix: [...ENV_PREFIXES] };
89
110
  },
90
111
  configureServer(server) {
91
112
  printBanner(server, "dev", server.config.root);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * bini-env/src/index.ts\n *\n * Two responsibilities only:\n * 1. Tell Vite which env prefixes to expose to import.meta.env (biniEnv plugin)\n * 2. Read env vars from the Hono request context (getEnv / requireEnv)\n *\n * process.env is never used. All runtime env access goes through Hono's env() adapter,\n * which handles Node, Bun, Cloudflare Workers, Deno, and Vercel Edge natively.\n */\n\nimport { env } from 'hono/adapter';\nimport type { Context } from 'hono';\nimport type { Plugin, UserConfig, ViteDevServer, PreviewServer } from 'vite';\n\n/* ==========================================================================\n ANSI helpers — used by biniLogger only\n ========================================================================== */\n\nconst C = {\n CYAN : '\\x1b[36m',\n RESET : '\\x1b[0m',\n YELLOW: '\\x1b[33m',\n RED : '\\x1b[31m',\n BOLD : '\\x1b[1m',\n DIM : '\\x1b[2m',\n} as const;\n\n/* ==========================================================================\n Public types\n ========================================================================== */\n\n/**\n * Accepts any Hono Context. Typed against Hono's own Context rather than a\n * loose duck type so misuse is caught at the call site.\n */\nexport type HonoContext = Context;\n\nexport interface BiniEnvPluginOptions {\n /** Extra prefixes to expose to import.meta.env, in addition to BINI_ and VITE_. */\n readonly envPrefix?: string | string[];\n}\n\n/* ==========================================================================\n Logger\n ========================================================================== */\n\nfunction _ts(): string {\n return `${C.DIM}${new Date().toLocaleTimeString('en-US', { hour12: false })}${C.RESET}`;\n}\n\nexport const biniLogger = {\n info(msg: string): void {\n console.log(`${_ts()} ${C.CYAN}${C.BOLD}[bini-env]${C.RESET} ${msg}`);\n },\n warn(msg: string): void {\n console.warn(\n `${_ts()} ${C.YELLOW}${C.BOLD}(!) [bini-env]${C.RESET} ${C.YELLOW}${msg}${C.RESET}`,\n );\n },\n error(msg: string, err?: unknown): void {\n const detail =\n err instanceof Error ? `\\n ${C.DIM}${err.message}${C.RESET}` : '';\n console.error(\n `${_ts()} ${C.RED}${C.BOLD}[bini-env] error${C.RESET} ${msg}${detail}`,\n );\n },\n} as const;\n\n/* ==========================================================================\n getEnv\n\n Reads exclusively from Hono's env() adapter. No process.env fallback.\n Hono handles all platform differences:\n Node / Bun → process.env\n Cloudflare → c.env bindings\n Deno / Netlify → Deno.env.get()\n Vercel Edge → process.env (V8 Node compat)\n ========================================================================== */\n\nexport function getEnv(c: HonoContext, key: string): string | undefined {\n const honoEnvs = env(c) as Record<string, string | undefined>;\n return honoEnvs[key];\n}\n\n/* ==========================================================================\n requireEnv\n\n Same as getEnv but throws immediately if the variable is missing or empty.\n Always handle the thrown error in your route's catch block.\n ========================================================================== */\n\nexport function requireEnv(c: HonoContext, key: string): string {\n const val = getEnv(c, key);\n\n if (!val) {\n biniLogger.error(\n `Missing required environment variable: \"${key}\"\\n` +\n ` ${C.DIM}-> Set it in your platform's env config or hosting dashboard.${C.RESET}`,\n );\n\n throw new Error(`[bini-env] Missing required environment variable: \"${key}\"`);\n }\n\n return val;\n}\n\n/* ==========================================================================\n biniEnv — Vite plugin\n\n Tells Vite which prefixes to expose to import.meta.env.\n Prints the ß Bini.js banner on dev and preview server start.\n Vite handles .env file loading, watching, server restarts, and HMR natively.\n ========================================================================== */\n\nconst DEFAULT_PREFIXES = ['BINI_', 'VITE_'] as const;\n\nfunction detectEnvFiles(root: string): string[] {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const fs = require('node:fs') as typeof import('fs');\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const path = require('node:path') as typeof import('path');\n const nodeEnv = process.env.NODE_ENV ?? 'production';\n return [\n '.env.local',\n `.env.${nodeEnv}.local`,\n `.env.${nodeEnv}`,\n '.env',\n ].filter(f => fs.existsSync(path.join(root, f)));\n } catch {\n return [];\n }\n}\n\nfunction printBanner(server: ViteDevServer | PreviewServer, mode: 'dev' | 'preview', root: string): void {\n const original = server.printUrls.bind(server);\n server.printUrls = () => {\n console.log(`\\n ${C.CYAN}ß Bini.js${C.RESET} (${mode})`);\n const found = detectEnvFiles(root);\n if (found.length > 0) {\n console.log(` \\x1b[32m➜\\x1b[0m Environments: ${found.join(', ')}`);\n }\n original();\n };\n}\n\nexport function biniEnv(options: Readonly<BiniEnvPluginOptions> = {}): Plugin {\n const extraPrefixes = options.envPrefix\n ? (Array.isArray(options.envPrefix) ? options.envPrefix : [options.envPrefix])\n : [];\n\n const envPrefix = [\n ...new Set([...DEFAULT_PREFIXES, ...extraPrefixes]),\n ];\n\n return {\n name: 'bini-env',\n\n config(): UserConfig {\n return { envPrefix };\n },\n\n configureServer(server) {\n printBanner(server, 'dev', server.config.root);\n },\n\n configurePreviewServer(server) {\n printBanner(server, 'preview', server.config.root);\n },\n };\n}\n\n/* ==========================================================================\n Re-exports\n ========================================================================== */\n\nexport type { Plugin } from 'vite';"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import { env } from 'hono/adapter';\nimport type { Context } from 'hono';\nimport type { Plugin, UserConfig, ViteDevServer, PreviewServer } from 'vite';\n\nconst C = {\n CYAN : '\\x1b[36m',\n RESET : '\\x1b[0m',\n YELLOW: '\\x1b[33m',\n RED : '\\x1b[31m',\n BOLD : '\\x1b[1m',\n DIM : '\\x1b[2m',\n} as const;\n\nexport type HonoContext = Context;\n\nfunction _ts(): string {\n return `${C.DIM}${new Date().toLocaleTimeString('en-US', { hour12: false })}${C.RESET}`;\n}\n\nexport const biniLogger = {\n info(msg: string): void {\n console.log(`${_ts()} ${C.CYAN}${C.BOLD}[bini-env]${C.RESET} ${msg}`);\n },\n warn(msg: string): void {\n console.warn(\n `${_ts()} ${C.YELLOW}${C.BOLD}(!) [bini-env]${C.RESET} ${C.YELLOW}${msg}${C.RESET}`,\n );\n },\n error(msg: string, err?: unknown): void {\n const detail =\n err instanceof Error ? `\\n ${C.DIM}${err.message}${C.RESET}` : '';\n console.error(\n `${_ts()} ${C.RED}${C.BOLD}[bini-env] error${C.RESET} ${msg}${detail}`,\n );\n },\n} as const;\n\nexport function getEnv(c: HonoContext, key: string): string | undefined {\n const honoEnvs = env(c) as Record<string, string | undefined>;\n return honoEnvs[key];\n}\n\nexport function requireEnv(c: HonoContext, key: string): string {\n const val = getEnv(c, key);\n\n if (!val) {\n biniLogger.error(\n `Missing required environment variable: \"${key}\"\\n` +\n ` ${C.DIM}-> Set it in your platform's env config or hosting dashboard.${C.RESET}`,\n );\n\n throw new Error(`[bini-env] Missing required environment variable: \"${key}\"`);\n }\n\n return val;\n}\n\nconst ENV_PREFIXES = ['BINI_', 'VITE_'] as const;\n\nfunction detectEnvFiles(root: string): string[] {\n try {\n const fs = require('node:fs') as typeof import('fs');\n const path = require('node:path') as typeof import('path');\n const nodeEnv = process.env.NODE_ENV ?? 'production';\n return [\n '.env.local',\n `.env.${nodeEnv}.local`,\n `.env.${nodeEnv}`,\n '.env',\n ].filter(f => fs.existsSync(path.join(root, f)));\n } catch {\n return [];\n }\n}\n\nfunction injectEnvIntoProcess(mode: string, envDir: string): number {\n if (typeof process === 'undefined' || !process.env) return 0;\n\n const { loadEnv } = require('vite') as typeof import('vite');\n const loaded = loadEnv(mode, envDir, '');\n let injected = 0;\n\n for (const [key, value] of Object.entries(loaded)) {\n const isPrefixed = ENV_PREFIXES.some((p) => key.startsWith(p));\n if (isPrefixed) continue;\n if (!value) continue;\n if (process.env[key] !== undefined) continue;\n process.env[key] = value;\n injected++;\n }\n\n return injected;\n}\n\nfunction printBanner(server: ViteDevServer | PreviewServer, mode: 'dev' | 'preview', root: string): void {\n const original = server.printUrls.bind(server);\n server.printUrls = () => {\n console.log(`\\n ${C.CYAN}ß Bini.js${C.RESET} (${mode})`);\n const found = detectEnvFiles(root);\n if (found.length > 0) {\n console.log(` \\x1b[32m➜\\x1b[0m Environments: ${found.join(', ')}`);\n }\n original();\n };\n}\n\nexport function biniEnv(): Plugin {\n return {\n name: 'bini-env',\n\n config(userConfig: UserConfig, { command, mode }): UserConfig {\n if (command === 'serve' && userConfig.envDir !== false) {\n const envDir =\n typeof userConfig.envDir === 'string'\n ? userConfig.envDir\n : userConfig.root ?? process.cwd();\n\n try {\n injectEnvIntoProcess(mode, envDir);\n } catch (err) {\n biniLogger.warn(\n `Failed to inject .env into process.env: ${(err as Error).message}`,\n );\n }\n }\n\n return { envPrefix: [...ENV_PREFIXES] };\n },\n\n configureServer(server) {\n printBanner(server, 'dev', server.config.root);\n },\n\n configurePreviewServer(server) {\n printBanner(server, 'preview', server.config.root);\n },\n };\n}\n\nexport type { Plugin } from 'vite';"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bini-env",
3
- "version": "1.1.2",
3
+ "version": "2.0.0",
4
4
  "description": "Universal environment variable loader and Vite plugin for Bini.js — powered by Hono, works across Node.js, Bun, Deno, Vercel Edge, Netlify Edge, and Cloudflare Workers",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",