bini-env 1.0.5 → 1.0.6

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Binidu Ranasinghe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,227 @@
1
+ # bini-env
2
+
3
+ ![npm](https://img.shields.io/npm/v/bini-env?color=cyan&style=flat-square)
4
+ ![npm downloads](https://img.shields.io/npm/dm/bini-env?style=flat-square)
5
+ ![license](https://img.shields.io/npm/l/bini-env?style=flat-square)
6
+ ![vite](https://img.shields.io/badge/vite-plugin-646CFF?style=flat-square&logo=vite)
7
+ ![typescript](https://img.shields.io/badge/TypeScript-ready-3178C6?style=flat-square&logo=typescript)
8
+
9
+ Universal environment variable loader and Vite plugin for Bini.js. Automatically loads `.env` files in development, reads env vars from the host in production, and works across Node.js, Deno, Bun, and Vite edge functions — with zero configuration.
10
+
11
+ ## Features
12
+
13
+ - **Universal `getEnv()` / `requireEnv()`** — read env vars in any runtime (Node.js, Deno, Bun, Vite client)
14
+ - **Auto `.env` loading** — no manual `dotenv` setup needed in API routes
15
+ - **`BINI_` prefix** — replaces Vite's `VITE_` prefix with `BINI_` automatically
16
+ - **Branded startup banner** — replaces Vite's default header with a clean Bini.js output
17
+ - **Supports dev and preview modes**
18
+ - **Safe for edge bundlers** — dotenv is loaded via dynamic import, never statically resolved
19
+
20
+ ---
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ pnpm add bini-env
26
+ # or
27
+ npm install bini-env
28
+ # or
29
+ yarn add bini-env
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Vite Plugin Setup
35
+
36
+ ```ts
37
+ // vite.config.ts
38
+ import { defineConfig } from 'vite';
39
+ import biniEnv from 'bini-env';
40
+
41
+ export default defineConfig({
42
+ plugins: [biniEnv()]
43
+ });
44
+ ```
45
+
46
+ That's it. Once added:
47
+ - Your `.env` is auto-loaded for API routes in dev
48
+ - Client code uses `import.meta.env.BINI_*` instead of `VITE_*`
49
+ - The startup banner is replaced with the Bini.js header
50
+
51
+ ---
52
+
53
+ ## Environment Variables
54
+
55
+ ### `.env` file
56
+
57
+ ```env
58
+ # Client-side vars — accessible via import.meta.env.BINI_*
59
+ BINI_FIREBASE_API_KEY=your_key
60
+ BINI_FIREBASE_AUTH_DOMAIN=your_domain
61
+
62
+ # Server-side vars — accessible via getEnv() in API routes
63
+ SMTP_USER=user@smtp.example.com
64
+ SMTP_PASS=your_password
65
+ FROM_EMAIL=App Name <noreply@example.com>
66
+ ```
67
+
68
+ ### In API routes
69
+
70
+ ```ts
71
+ import { getEnv, requireEnv } from 'bini-env';
72
+
73
+ // Returns string | undefined
74
+ const smtpUser = getEnv('SMTP_USER');
75
+
76
+ // Returns string — throws a clear error if missing
77
+ const smtpPass = requireEnv('SMTP_PASS');
78
+ ```
79
+
80
+ ### In client code (React / Vue / etc.)
81
+
82
+ ```ts
83
+ // Works because bini-env sets envPrefix: 'BINI_' automatically
84
+ const apiKey = import.meta.env.BINI_FIREBASE_API_KEY;
85
+ ```
86
+
87
+ ---
88
+
89
+ ## How env loading works by environment
90
+
91
+ | Environment | How vars are loaded | dotenv used? |
92
+ |---|---|---|
93
+ | Dev (Node.js / bini-router) | Auto-loaded from `.env` by bini-env | ✅ Yes (automatic) |
94
+ | Preview (`vite preview`) | Auto-loaded from `.env` by bini-env | ✅ Yes (automatic) |
95
+ | Production (Netlify / Vercel) | Injected by host dashboard | ❌ No |
96
+ | Production (bini-server / VPS) | Set via server env or hosting panel | ❌ No |
97
+ | Deno / Edge Functions | Read from `Deno.env` natively | ❌ No |
98
+
99
+ You never need to import or configure `dotenv` manually.
100
+
101
+ ---
102
+
103
+ ## Startup Output
104
+
105
+ **Dev mode**
106
+ ```
107
+ ß Bini.js (dev)
108
+ ➜ Environments: .env.local, .env
109
+ ➜ Local: http://localhost:3000/
110
+ ➜ Network: http://192.168.1.10:3000/
111
+ ```
112
+
113
+ **Preview mode**
114
+ ```
115
+ ß Bini.js (preview)
116
+ ➜ Environments: .env.local, .env
117
+ ➜ Local: http://localhost:4173/
118
+ ➜ Network: http://192.168.1.10:4173/
119
+ ```
120
+
121
+ ---
122
+
123
+ ## Plugin Options
124
+
125
+ | Option | Type | Default | Description |
126
+ |---|---|---|---|
127
+ | `enabled` | `boolean` | `true` | Enable or disable the plugin |
128
+ | `clearViteHeader` | `boolean` | `true` | Replace Vite's default startup header |
129
+ | `logo` | `string` | `'ß'` | Custom logo or text shown in the banner |
130
+ | `envPrefix` | `string` | `'BINI_'` | Prefix for client-side env vars in `import.meta.env` |
131
+
132
+ ```ts
133
+ biniEnv({
134
+ logo: '🚀',
135
+ clearViteHeader: true,
136
+ envPrefix: 'BINI_', // change to 'VITE_' to keep Vite's default
137
+ })
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Detected `.env` Files
143
+
144
+ The plugin checks for the following files in your project root (in priority order):
145
+
146
+ 1. `.env.local`
147
+ 2. `.env.[NODE_ENV].local`
148
+ 3. `.env.[NODE_ENV]`
149
+ 4. `.env`
150
+
151
+ ---
152
+
153
+ ## API Reference
154
+
155
+ ### `getEnv(key: string): string | undefined`
156
+
157
+ Read an environment variable in any runtime. Checks `Deno.env` → `process.env` → `import.meta.env` in order.
158
+
159
+ ```ts
160
+ import { getEnv } from 'bini-env';
161
+
162
+ const smtpUser = getEnv('SMTP_USER');
163
+ ```
164
+
165
+ ### `requireEnv(key: string): string`
166
+
167
+ Same as `getEnv` but throws a descriptive error if the variable is missing or empty. Use this for required secrets.
168
+
169
+ ```ts
170
+ import { requireEnv } from 'bini-env';
171
+
172
+ const smtpPass = requireEnv('SMTP_PASS');
173
+ // Throws: [bini-env] Missing required environment variable: "SMTP_PASS".
174
+ // → In development: add it to your .env file.
175
+ // → In production: add it to your hosting dashboard environment variables.
176
+ ```
177
+
178
+ ### `loadEnv(projectRoot?: string): Promise<void>`
179
+
180
+ Manually trigger `.env` loading. Called automatically by the Vite plugin — you should not need this.
181
+
182
+ ### `detectEnvFiles(projectRoot?: string): DetectedEnvFile[]`
183
+
184
+ Scan a directory for `.env` files and return an array of matches.
185
+
186
+ ```ts
187
+ import { detectEnvFiles } from 'bini-env';
188
+
189
+ const files = detectEnvFiles('/my/project');
190
+ // [{ name: '.env.local', path: '/my/project/.env.local' }, ...]
191
+ ```
192
+
193
+ ---
194
+
195
+ ## Types
196
+
197
+ ```ts
198
+ interface BiniEnvPluginOptions {
199
+ enabled?: boolean;
200
+ clearViteHeader?: boolean;
201
+ logo?: string;
202
+ envPrefix?: string;
203
+ }
204
+
205
+ interface DetectedEnvFile {
206
+ name: string;
207
+ path: string;
208
+ }
209
+ ```
210
+
211
+ ---
212
+
213
+ ## Compatibility
214
+
215
+ | Runtime | Supported |
216
+ |---|---|
217
+ | Node.js 18+ | ✅ |
218
+ | Bun | ✅ |
219
+ | Deno / Edge Functions | ✅ |
220
+ | Vite 7 | ✅ |
221
+ | Vite 8 | ✅ |
222
+
223
+ ---
224
+
225
+ ## License
226
+
227
+ MIT
package/dist/index.cjs CHANGED
@@ -1,166 +1,177 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+ var path = require('path');
5
+
6
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
+
8
+ var fs__default = /*#__PURE__*/_interopDefault(fs);
9
+ var path__default = /*#__PURE__*/_interopDefault(path);
29
10
 
30
11
  // src/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
33
- default: () => biniEnvPlugin,
34
- detectEnvFiles: () => detectEnvFiles,
35
- formatEnvFilesForDisplay: () => formatEnvFilesForDisplay
36
- });
37
- module.exports = __toCommonJS(index_exports);
38
- var import_fs = __toESM(require("fs"), 1);
39
- var import_path = __toESM(require("path"), 1);
40
12
  var BINI_LOGO = "\xDF";
13
+ var DEFAULT_ENV_PREFIX = "BINI_";
41
14
  var DEFAULT_OPTIONS = {
42
15
  enabled: true,
43
16
  clearViteHeader: true,
44
- logo: BINI_LOGO
17
+ logo: BINI_LOGO,
18
+ envPrefix: DEFAULT_ENV_PREFIX
45
19
  };
46
20
  var COLORS = {
47
21
  CYAN: "\x1B[36m",
48
22
  RESET: "\x1B[0m",
49
- GREEN: "\x1B[32m",
50
- YELLOW: "\x1B[33m",
51
- DIM: "\x1B[2m"
23
+ GREEN: "\x1B[32m"
52
24
  };
25
+ function getEnv(key) {
26
+ const deno = globalThis.Deno;
27
+ if (deno) {
28
+ try {
29
+ const val = deno.env.get(key);
30
+ if (val !== void 0) return val;
31
+ } catch {
32
+ }
33
+ }
34
+ if (typeof process !== "undefined" && process.env) {
35
+ if (process.env[key] !== void 0) return process.env[key];
36
+ }
37
+ try {
38
+ const metaEnv = undefined;
39
+ if (metaEnv) {
40
+ if (metaEnv[key] !== void 0) return metaEnv[key];
41
+ if (metaEnv[`BINI_${key}`] !== void 0) return metaEnv[`BINI_${key}`];
42
+ }
43
+ } catch {
44
+ }
45
+ return void 0;
46
+ }
47
+ function requireEnv(key) {
48
+ const val = getEnv(key);
49
+ if (!val) {
50
+ throw new Error(
51
+ `[bini-env] Missing required environment variable: "${key}".
52
+ \u2192 In development: add it to your .env file.
53
+ \u2192 In production: add it to your hosting dashboard environment variables.`
54
+ );
55
+ }
56
+ return val;
57
+ }
58
+ var _envLoaded = false;
59
+ async function loadEnv(projectRoot) {
60
+ if (_envLoaded) return;
61
+ if (globalThis.Deno) {
62
+ _envLoaded = true;
63
+ return;
64
+ }
65
+ if (typeof process === "undefined" || !process.env) {
66
+ _envLoaded = true;
67
+ return;
68
+ }
69
+ if (process.env.NODE_ENV === "production") {
70
+ _envLoaded = true;
71
+ return;
72
+ }
73
+ try {
74
+ const root = projectRoot ?? process.cwd();
75
+ const envPath = path__default.default.join(root, ".env");
76
+ if (!fs__default.default.existsSync(envPath)) {
77
+ _envLoaded = true;
78
+ return;
79
+ }
80
+ const dotenv = await import('dotenv');
81
+ dotenv.config({ path: envPath });
82
+ } catch {
83
+ }
84
+ _envLoaded = true;
85
+ }
53
86
  function detectEnvFiles(projectRoot = process.cwd()) {
54
- const nodeEnv = process.env.NODE_ENV || "development";
55
- const envFiles = [
87
+ const nodeEnv = process.env.NODE_ENV ?? "development";
88
+ const candidates = [
56
89
  ".env.local",
57
90
  `.env.${nodeEnv}.local`,
58
91
  `.env.${nodeEnv}`,
59
92
  ".env"
60
93
  ];
61
94
  const found = [];
62
- for (const file of envFiles) {
63
- const filePath = import_path.default.join(projectRoot, file);
64
- if (import_fs.default.existsSync(filePath)) {
65
- found.push({
66
- name: file,
67
- path: filePath
68
- });
95
+ for (const file of candidates) {
96
+ const filePath = path__default.default.join(projectRoot, file);
97
+ if (fs__default.default.existsSync(filePath)) {
98
+ found.push({ name: file, path: filePath });
69
99
  }
70
100
  }
71
101
  return found;
72
102
  }
73
- function formatEnvFilesForDisplay(envFiles) {
74
- if (envFiles.length === 0) {
75
- return "";
76
- }
77
- const envString = envFiles.map((f) => f.name).join(", ");
78
- return `${COLORS.GREEN}\u279C${COLORS.RESET} Environments: ${envString}`;
79
- }
80
103
  function clearLine() {
81
104
  process.stdout.write("\x1B[2K\r");
82
105
  }
83
- function biniEnvPlugin(options = {}) {
84
- const resolvedOptions = Object.freeze({
85
- ...DEFAULT_OPTIONS,
86
- ...options
87
- });
88
- const { enabled, clearViteHeader, logo } = resolvedOptions;
89
- let config;
90
- let isPreviewMode = false;
106
+ function printResolvedUrls(urls) {
107
+ if (!urls) return;
108
+ if (urls.local.length > 0)
109
+ console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Local: ${COLORS.CYAN}${urls.local[0]}${COLORS.RESET}`);
110
+ if (urls.network.length > 0)
111
+ console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Network: ${COLORS.CYAN}${urls.network[0]}${COLORS.RESET}`);
112
+ }
113
+ function biniEnv(options = {}) {
114
+ const resolvedOptions = Object.freeze({ ...DEFAULT_OPTIONS, ...options });
115
+ const { enabled, clearViteHeader, logo, envPrefix } = resolvedOptions;
116
+ let resolvedConfig;
91
117
  let serverStarted = false;
92
118
  let previewStarted = false;
93
119
  return {
94
120
  name: "vite-plugin-bini-env",
95
121
  apply: "serve",
96
- configResolved(resolvedConfig) {
97
- config = resolvedConfig;
98
- if (resolvedConfig.command === "serve") {
99
- isPreviewMode = process.env.VITE_PREVIEW === "true" || process.argv.includes("preview") || false;
100
- }
122
+ // Set BINI_ as the env prefix so import.meta.env.BINI_* works in client code
123
+ config() {
124
+ return { envPrefix };
125
+ },
126
+ configResolved(cfg) {
127
+ resolvedConfig = cfg;
101
128
  },
102
129
  configureServer(server) {
103
130
  if (!enabled) return;
104
- const originalPrintUrls = server.printUrls;
131
+ if (server.httpServer) {
132
+ server.httpServer.once("listening", () => {
133
+ void loadEnv(resolvedConfig?.root ?? process.cwd());
134
+ });
135
+ } else {
136
+ void loadEnv(resolvedConfig?.root ?? process.cwd());
137
+ }
138
+ const originalPrintUrls = server.printUrls.bind(server);
105
139
  server.printUrls = () => {
106
- if (!serverStarted) {
107
- serverStarted = true;
108
- if (clearViteHeader) {
109
- clearLine();
110
- }
111
- console.log(`
140
+ if (serverStarted) return;
141
+ serverStarted = true;
142
+ if (clearViteHeader) clearLine();
143
+ console.log(`
112
144
  ${COLORS.CYAN}${logo} Bini.js${COLORS.RESET} (dev)`);
113
- const found = detectEnvFiles(config.root);
114
- if (found.length > 0) {
115
- const envString = found.map((f) => f.name).join(", ");
116
- console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Environments: ${envString}`);
117
- }
118
- if (!clearViteHeader) {
119
- originalPrintUrls.call(server);
120
- } else {
121
- const urls = server.resolvedUrls;
122
- if (urls) {
123
- if (urls.local.length > 0) {
124
- console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Local: ${COLORS.CYAN}${urls.local[0]}${COLORS.RESET}`);
125
- }
126
- if (urls.network.length > 0) {
127
- console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Network: ${COLORS.CYAN}${urls.network[0]}${COLORS.RESET}`);
128
- }
129
- }
130
- }
145
+ const found = detectEnvFiles(resolvedConfig?.root);
146
+ if (found.length > 0) {
147
+ const envString = found.map((f) => f.name).join(", ");
148
+ console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Environments: ${envString}`);
149
+ }
150
+ if (!clearViteHeader) {
151
+ originalPrintUrls();
152
+ } else {
153
+ printResolvedUrls(server.resolvedUrls);
131
154
  }
132
155
  };
133
156
  },
134
157
  configurePreviewServer(server) {
135
158
  if (!enabled) return;
136
- isPreviewMode = true;
137
- const originalPrintUrls = server.printUrls;
159
+ const originalPrintUrls = server.printUrls.bind(server);
138
160
  server.printUrls = () => {
139
- if (!previewStarted) {
140
- previewStarted = true;
141
- if (clearViteHeader) {
142
- clearLine();
143
- }
144
- console.log(`
161
+ if (previewStarted) return;
162
+ previewStarted = true;
163
+ if (clearViteHeader) clearLine();
164
+ console.log(`
145
165
  ${COLORS.CYAN}${logo} Bini.js${COLORS.RESET} (preview)`);
146
- const found = detectEnvFiles(config.root);
147
- if (found.length > 0) {
148
- const envString = found.map((f) => f.name).join(", ");
149
- console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Environments: ${envString}`);
150
- }
151
- if (!clearViteHeader) {
152
- originalPrintUrls.call(server);
153
- } else {
154
- const urls = server.resolvedUrls;
155
- if (urls) {
156
- if (urls.local.length > 0) {
157
- console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Local: ${COLORS.CYAN}${urls.local[0]}${COLORS.RESET}`);
158
- }
159
- if (urls.network.length > 0) {
160
- console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Network: ${COLORS.CYAN}${urls.network[0]}${COLORS.RESET}`);
161
- }
162
- }
163
- }
166
+ const found = detectEnvFiles(resolvedConfig?.root);
167
+ if (found.length > 0) {
168
+ const envString = found.map((f) => f.name).join(", ");
169
+ console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Environments: ${envString}`);
170
+ }
171
+ if (!clearViteHeader) {
172
+ originalPrintUrls();
173
+ } else {
174
+ printResolvedUrls(server.resolvedUrls);
164
175
  }
165
176
  };
166
177
  },
@@ -170,9 +181,11 @@ function biniEnvPlugin(options = {}) {
170
181
  }
171
182
  };
172
183
  }
173
- // Annotate the CommonJS export names for ESM import in node:
174
- 0 && (module.exports = {
175
- detectEnvFiles,
176
- formatEnvFilesForDisplay
177
- });
184
+
185
+ exports.biniEnv = biniEnv;
186
+ exports.detectEnvFiles = detectEnvFiles;
187
+ exports.getEnv = getEnv;
188
+ exports.loadEnv = loadEnv;
189
+ exports.requireEnv = requireEnv;
190
+ //# sourceMappingURL=index.cjs.map
178
191
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// src/index.ts\r\nimport fs from 'fs';\r\nimport path from 'path';\r\nimport type { Plugin, ResolvedConfig } from 'vite';\r\n\r\n// Types\r\nexport interface BiniEnvPluginOptions {\r\n /**\r\n * Enable/disable the plugin\r\n * @default true\r\n * @readonly\r\n */\r\n readonly enabled?: boolean;\r\n \r\n /**\r\n * Remove Vite's default header\r\n * @default true\r\n * @readonly\r\n */\r\n readonly clearViteHeader?: boolean;\r\n \r\n /**\r\n * Custom logo/text to display\r\n * @default 'ß'\r\n * @readonly\r\n */\r\n readonly logo?: string;\r\n}\r\n\r\nexport interface DetectedEnvFile {\r\n readonly name: string;\r\n readonly path: string;\r\n}\r\n\r\n// Constants\r\nconst BINI_LOGO = 'ß' as const;\r\nconst DEFAULT_OPTIONS = {\r\n enabled: true,\r\n clearViteHeader: true,\r\n logo: BINI_LOGO,\r\n} as const;\r\n\r\n// Color definitions\r\nconst COLORS = {\r\n CYAN: '\\x1b[36m',\r\n RESET: '\\x1b[0m',\r\n GREEN: '\\x1b[32m',\r\n YELLOW: '\\x1b[33m',\r\n DIM: '\\x1b[2m'\r\n} as const;\r\n\r\n/**\r\n * Detect which .env files exist in project root\r\n */\r\nexport function detectEnvFiles(projectRoot: string = process.cwd()): DetectedEnvFile[] {\r\n const nodeEnv = process.env.NODE_ENV || 'development';\r\n\r\n const envFiles = [\r\n '.env.local',\r\n `.env.${nodeEnv}.local`,\r\n `.env.${nodeEnv}`,\r\n '.env'\r\n ] as const;\r\n\r\n const found: DetectedEnvFile[] = [];\r\n\r\n for (const file of envFiles) {\r\n const filePath = path.join(projectRoot, file);\r\n if (fs.existsSync(filePath)) {\r\n found.push({\r\n name: file,\r\n path: filePath\r\n });\r\n }\r\n }\r\n\r\n return found;\r\n}\r\n\r\n/**\r\n * Format environment files for console display\r\n */\r\nexport function formatEnvFilesForDisplay(envFiles: DetectedEnvFile[]): string {\r\n if (envFiles.length === 0) {\r\n return '';\r\n }\r\n\r\n const envString = envFiles.map(f => f.name).join(', ');\r\n return `${COLORS.GREEN}➜${COLORS.RESET} Environments: ${envString}`;\r\n}\r\n\r\n/**\r\n * Clear the current line in console\r\n */\r\nfunction clearLine(): void {\r\n process.stdout.write('\\x1b[2K\\r');\r\n}\r\n\r\n/**\r\n * Main plugin function\r\n */\r\nexport default function biniEnvPlugin(options: Readonly<BiniEnvPluginOptions> = {}): Plugin {\r\n // Merge with defaults — user cannot override core behaviour\r\n const resolvedOptions = Object.freeze({\r\n ...DEFAULT_OPTIONS,\r\n ...options,\r\n });\r\n\r\n const { enabled, clearViteHeader, logo } = resolvedOptions;\r\n\r\n let config: ResolvedConfig;\r\n let isPreviewMode = false;\r\n let serverStarted = false;\r\n let previewStarted = false;\r\n\r\n return {\r\n name: 'vite-plugin-bini-env',\r\n \r\n apply: 'serve',\r\n \r\n configResolved(resolvedConfig: ResolvedConfig) {\r\n config = resolvedConfig;\r\n \r\n if (resolvedConfig.command === 'serve') {\r\n isPreviewMode = process.env.VITE_PREVIEW === 'true' || \r\n process.argv.includes('preview') ||\r\n false;\r\n }\r\n },\r\n\r\n configureServer(server) {\r\n if (!enabled) return;\r\n\r\n const originalPrintUrls = server.printUrls;\r\n \r\n server.printUrls = () => {\r\n if (!serverStarted) {\r\n serverStarted = true;\r\n \r\n if (clearViteHeader) {\r\n clearLine();\r\n }\r\n \r\n console.log(`\\n ${COLORS.CYAN}${logo} Bini.js${COLORS.RESET} (dev)`);\r\n\r\n const found = detectEnvFiles(config.root);\r\n if (found.length > 0) {\r\n const envString = found.map(f => f.name).join(', ');\r\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Environments: ${envString}`);\r\n }\r\n \r\n if (!clearViteHeader) {\r\n originalPrintUrls.call(server);\r\n } else {\r\n const urls = server.resolvedUrls;\r\n if (urls) {\r\n if (urls.local.length > 0) {\r\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Local: ${COLORS.CYAN}${urls.local[0]}${COLORS.RESET}`);\r\n }\r\n if (urls.network.length > 0) {\r\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Network: ${COLORS.CYAN}${urls.network[0]}${COLORS.RESET}`);\r\n }\r\n }\r\n }\r\n }\r\n };\r\n },\r\n\r\n configurePreviewServer(server) {\r\n if (!enabled) return;\r\n \r\n isPreviewMode = true;\r\n \r\n const originalPrintUrls = server.printUrls;\r\n \r\n server.printUrls = () => {\r\n if (!previewStarted) {\r\n previewStarted = true;\r\n \r\n if (clearViteHeader) {\r\n clearLine();\r\n }\r\n \r\n console.log(`\\n ${COLORS.CYAN}${logo} Bini.js${COLORS.RESET} (preview)`);\r\n\r\n const found = detectEnvFiles(config.root);\r\n if (found.length > 0) {\r\n const envString = found.map(f => f.name).join(', ');\r\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Environments: ${envString}`);\r\n }\r\n \r\n if (!clearViteHeader) {\r\n originalPrintUrls.call(server);\r\n } else {\r\n const urls = server.resolvedUrls;\r\n if (urls) {\r\n if (urls.local.length > 0) {\r\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Local: ${COLORS.CYAN}${urls.local[0]}${COLORS.RESET}`);\r\n }\r\n if (urls.network.length > 0) {\r\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Network: ${COLORS.CYAN}${urls.network[0]}${COLORS.RESET}`);\r\n }\r\n }\r\n }\r\n }\r\n };\r\n },\r\n\r\n buildEnd() {\r\n serverStarted = false;\r\n previewStarted = false;\r\n }\r\n };\r\n}\r\n\r\nexport type { Plugin } from 'vite';"],"mappings":"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","names":["path","fs"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["path","fs"],"mappings":"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","file":"index.cjs","sourcesContent":["// src/index.ts\nimport fs from 'fs';\nimport path from 'path';\nimport type { Plugin, ResolvedConfig, UserConfig } from 'vite';\n\n/* ==========================================================================\n Global type declarations for cross-runtime compatibility\n ========================================================================== */\n\n// Deno runtime type — declared as an interface so we can safely cast\n// without relying on a global that TypeScript doesn't know about\ninterface DenoEnv {\n get(key: string): string | undefined;\n toObject(): Record<string, string>;\n}\ninterface DenoNamespace {\n env: DenoEnv;\n}\n\n// Vite augments ImportMeta with .env\n// Using triple-slash reference so it works both inside and outside Vite projects\n/// <reference types=\"vite/client\" />\n\n/* ==========================================================================\n Types\n ========================================================================== */\n\nexport interface BiniEnvPluginOptions {\n /**\n * Enable/disable the plugin\n * @default true\n */\n readonly enabled?: boolean;\n\n /**\n * Remove Vite's default header\n * @default true\n */\n readonly clearViteHeader?: boolean;\n\n /**\n * Custom logo/text to display\n * @default 'ß'\n */\n readonly logo?: string;\n\n /**\n * Override the env var prefix exposed to client code.\n * Defaults to 'BINI_' — so BINI_MY_VAR is accessible via import.meta.env.BINI_MY_VAR\n * Set to 'VITE_' to keep Vite's default behaviour.\n * @default 'BINI_'\n */\n readonly envPrefix?: string;\n}\n\nexport interface DetectedEnvFile {\n readonly name: string;\n readonly path: string;\n}\n\n/* ==========================================================================\n Constants\n ========================================================================== */\n\nconst BINI_LOGO = 'ß' as const;\nconst DEFAULT_ENV_PREFIX = 'BINI_' as const;\n\nconst DEFAULT_OPTIONS = {\n enabled: true,\n clearViteHeader: true,\n logo: BINI_LOGO,\n envPrefix: DEFAULT_ENV_PREFIX,\n} as const;\n\nconst COLORS = {\n CYAN: '\\x1b[36m',\n RESET: '\\x1b[0m',\n GREEN: '\\x1b[32m',\n} as const;\n\n/* ==========================================================================\n getEnv — Universal env var accessor\n Works in: Node.js, Deno (edge functions), Bun, Vite client\n ========================================================================== */\n\n/**\n * Read an environment variable in any runtime.\n *\n * Priority order:\n * 1. Deno.env — Netlify edge functions / Deno runtime\n * 2. process.env — Node.js / Bun / bini-server\n * 3. import.meta.env — Vite client (BINI_-prefixed vars)\n *\n * Usage in API routes:\n * import { getEnv } from 'bini-env';\n * const smtpUser = getEnv('SMTP_USER');\n */\nexport function getEnv(key: string): string | undefined {\n // 1. Deno runtime (Netlify edge functions)\n const deno = (globalThis as unknown as { Deno?: DenoNamespace }).Deno;\n if (deno) {\n try {\n const val = deno.env.get(key);\n if (val !== undefined) return val;\n } catch {\n // Deno env access denied — fall through\n }\n }\n\n // 2. Node.js / Bun / bini-server\n if (typeof process !== 'undefined' && process.env) {\n if (process.env[key] !== undefined) return process.env[key];\n }\n\n // 3. Vite injected (import.meta.env) — BINI_-prefixed client vars\n // Wrapped in try/catch because import.meta.env doesn't exist in Node/Deno\n try {\n const metaEnv = import.meta.env as Record<string, string | undefined> | undefined;\n if (metaEnv) {\n if (metaEnv[key] !== undefined) return metaEnv[key];\n // Try with BINI_ prefix automatically\n if (metaEnv[`BINI_${key}`] !== undefined) return metaEnv[`BINI_${key}`];\n }\n } catch {\n // import.meta.env not available in this runtime — ignore\n }\n\n return undefined;\n}\n\n/**\n * Read an environment variable and throw if it's missing or empty.\n * Use this for required secrets so you get a clear error at startup\n * instead of a cryptic crash later.\n *\n * Usage:\n * const smtpPass = requireEnv('SMTP_PASS');\n */\nexport function requireEnv(key: string): string {\n const val = getEnv(key);\n if (!val) {\n throw new Error(\n `[bini-env] Missing required environment variable: \"${key}\".\\n` +\n ` → In development: add it to your .env file.\\n` +\n ` → In production: add it to your hosting dashboard environment variables.`\n );\n }\n return val;\n}\n\n/* ==========================================================================\n loadEnv — Auto dotenv loader for bini-router API routes (Node.js only)\n Called internally by the Vite plugin. Safe to call multiple times.\n ========================================================================== */\n\nlet _envLoaded = false;\n\n/**\n * Auto-load .env into process.env for Node.js API route contexts.\n *\n * Behaviour by environment:\n * - Deno (edge functions) → no-op, Deno/Netlify injects env natively\n * - NODE_ENV=production → no-op, host injects env vars automatically\n * - Node.js dev → loads .env via dotenv (dynamic import, safe for bundlers)\n *\n * Called automatically by bini-env Vite plugin. You should not need to call this manually.\n */\nexport async function loadEnv(projectRoot?: string): Promise<void> {\n // Already loaded — skip\n if (_envLoaded) return;\n\n // Deno runtime — env is handled natively, skip\n if ((globalThis as unknown as { Deno?: DenoNamespace }).Deno) {\n _envLoaded = true;\n return;\n }\n\n // Not in a Node-like runtime — skip\n if (typeof process === 'undefined' || !process.env) {\n _envLoaded = true;\n return;\n }\n\n // Production — host injects env vars, dotenv not needed\n if (process.env.NODE_ENV === 'production') {\n _envLoaded = true;\n return;\n }\n\n try {\n const root = projectRoot ?? process.cwd();\n const envPath = path.join(root, '.env');\n\n if (!fs.existsSync(envPath)) {\n _envLoaded = true;\n return;\n }\n\n // Dynamic import so Deno/edge bundlers never try to statically resolve dotenv\n const dotenv = await import('dotenv');\n dotenv.config({ path: envPath });\n } catch {\n // dotenv not installed or read failed — silently skip.\n // requireEnv() will surface clear errors if vars are missing when used.\n }\n\n _envLoaded = true;\n}\n\n/* ==========================================================================\n detectEnvFiles — used by the Vite plugin display\n ========================================================================== */\n\n/**\n * Detect which .env files exist in the project root, in Vite's priority order.\n */\nexport function detectEnvFiles(projectRoot: string = process.cwd()): DetectedEnvFile[] {\n const nodeEnv = process.env.NODE_ENV ?? 'development';\n\n const candidates = [\n '.env.local',\n `.env.${nodeEnv}.local`,\n `.env.${nodeEnv}`,\n '.env',\n ] as const;\n\n const found: DetectedEnvFile[] = [];\n\n for (const file of candidates) {\n const filePath = path.join(projectRoot, file);\n if (fs.existsSync(filePath)) {\n found.push({ name: file, path: filePath });\n }\n }\n\n return found;\n}\n\n/* ==========================================================================\n Vite plugin\n ========================================================================== */\n\nfunction clearLine(): void {\n process.stdout.write('\\x1b[2K\\r');\n}\n\nfunction printResolvedUrls(urls: { local: string[]; network: string[] } | null | undefined): void {\n if (!urls) return;\n if (urls.local.length > 0)\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Local: ${COLORS.CYAN}${urls.local[0]}${COLORS.RESET}`);\n if (urls.network.length > 0)\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Network: ${COLORS.CYAN}${urls.network[0]}${COLORS.RESET}`);\n}\n\n/**\n * Bini.js Vite plugin.\n *\n * What it does:\n * - Sets envPrefix to 'BINI_' so client code uses import.meta.env.BINI_MY_VAR\n * - Auto-loads .env for API routes in dev via loadEnv()\n * - Replaces Vite's startup output with the Bini.js branded header\n */\nexport function biniEnv(options: Readonly<BiniEnvPluginOptions> = {}): Plugin {\n const resolvedOptions = Object.freeze({ ...DEFAULT_OPTIONS, ...options });\n const { enabled, clearViteHeader, logo, envPrefix } = resolvedOptions;\n\n let resolvedConfig: ResolvedConfig;\n let serverStarted = false;\n let previewStarted = false;\n\n return {\n name: 'vite-plugin-bini-env',\n\n apply: 'serve',\n\n // Set BINI_ as the env prefix so import.meta.env.BINI_* works in client code\n config(): UserConfig {\n return { envPrefix };\n },\n\n configResolved(cfg: ResolvedConfig) {\n resolvedConfig = cfg;\n },\n\n configureServer(server) {\n if (!enabled) return;\n\n // Auto-load .env after server starts — guarantees resolvedConfig is available\n // Falls back to immediate call in middleware mode where httpServer is null\n if (server.httpServer) {\n server.httpServer.once('listening', () => {\n void loadEnv(resolvedConfig?.root ?? process.cwd());\n });\n } else {\n // Middleware mode — httpServer is null, call immediately\n void loadEnv(resolvedConfig?.root ?? process.cwd());\n }\n\n const originalPrintUrls = server.printUrls.bind(server);\n\n server.printUrls = () => {\n if (serverStarted) return;\n serverStarted = true;\n\n if (clearViteHeader) clearLine();\n console.log(`\\n ${COLORS.CYAN}${logo} Bini.js${COLORS.RESET} (dev)`);\n\n const found = detectEnvFiles(resolvedConfig?.root);\n if (found.length > 0) {\n const envString = found.map((f) => f.name).join(', ');\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Environments: ${envString}`);\n }\n\n if (!clearViteHeader) {\n originalPrintUrls();\n } else {\n printResolvedUrls(server.resolvedUrls);\n }\n };\n },\n\n configurePreviewServer(server) {\n if (!enabled) return;\n\n const originalPrintUrls = server.printUrls.bind(server);\n\n server.printUrls = () => {\n if (previewStarted) return;\n previewStarted = true;\n\n if (clearViteHeader) clearLine();\n console.log(`\\n ${COLORS.CYAN}${logo} Bini.js${COLORS.RESET} (preview)`);\n\n const found = detectEnvFiles(resolvedConfig?.root);\n if (found.length > 0) {\n const envString = found.map((f) => f.name).join(', ');\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Environments: ${envString}`);\n }\n\n if (!clearViteHeader) {\n originalPrintUrls();\n } else {\n printResolvedUrls(server.resolvedUrls);\n }\n };\n },\n\n buildEnd() {\n serverStarted = false;\n previewStarted = false;\n },\n };\n}\n\n// Named export: import { biniEnv } from 'bini-env'\nexport type { Plugin } from 'vite';"]}
package/dist/index.d.cts CHANGED
@@ -5,37 +5,75 @@ interface BiniEnvPluginOptions {
5
5
  /**
6
6
  * Enable/disable the plugin
7
7
  * @default true
8
- * @readonly
9
8
  */
10
9
  readonly enabled?: boolean;
11
10
  /**
12
11
  * Remove Vite's default header
13
12
  * @default true
14
- * @readonly
15
13
  */
16
14
  readonly clearViteHeader?: boolean;
17
15
  /**
18
16
  * Custom logo/text to display
19
17
  * @default 'ß'
20
- * @readonly
21
18
  */
22
19
  readonly logo?: string;
20
+ /**
21
+ * Override the env var prefix exposed to client code.
22
+ * Defaults to 'BINI_' — so BINI_MY_VAR is accessible via import.meta.env.BINI_MY_VAR
23
+ * Set to 'VITE_' to keep Vite's default behaviour.
24
+ * @default 'BINI_'
25
+ */
26
+ readonly envPrefix?: string;
23
27
  }
24
28
  interface DetectedEnvFile {
25
29
  readonly name: string;
26
30
  readonly path: string;
27
31
  }
28
32
  /**
29
- * Detect which .env files exist in project root
33
+ * Read an environment variable in any runtime.
34
+ *
35
+ * Priority order:
36
+ * 1. Deno.env — Netlify edge functions / Deno runtime
37
+ * 2. process.env — Node.js / Bun / bini-server
38
+ * 3. import.meta.env — Vite client (BINI_-prefixed vars)
39
+ *
40
+ * Usage in API routes:
41
+ * import { getEnv } from 'bini-env';
42
+ * const smtpUser = getEnv('SMTP_USER');
30
43
  */
31
- declare function detectEnvFiles(projectRoot?: string): DetectedEnvFile[];
44
+ declare function getEnv(key: string): string | undefined;
45
+ /**
46
+ * Read an environment variable and throw if it's missing or empty.
47
+ * Use this for required secrets so you get a clear error at startup
48
+ * instead of a cryptic crash later.
49
+ *
50
+ * Usage:
51
+ * const smtpPass = requireEnv('SMTP_PASS');
52
+ */
53
+ declare function requireEnv(key: string): string;
54
+ /**
55
+ * Auto-load .env into process.env for Node.js API route contexts.
56
+ *
57
+ * Behaviour by environment:
58
+ * - Deno (edge functions) → no-op, Deno/Netlify injects env natively
59
+ * - NODE_ENV=production → no-op, host injects env vars automatically
60
+ * - Node.js dev → loads .env via dotenv (dynamic import, safe for bundlers)
61
+ *
62
+ * Called automatically by bini-env Vite plugin. You should not need to call this manually.
63
+ */
64
+ declare function loadEnv(projectRoot?: string): Promise<void>;
32
65
  /**
33
- * Format environment files for console display
66
+ * Detect which .env files exist in the project root, in Vite's priority order.
34
67
  */
35
- declare function formatEnvFilesForDisplay(envFiles: DetectedEnvFile[]): string;
68
+ declare function detectEnvFiles(projectRoot?: string): DetectedEnvFile[];
36
69
  /**
37
- * Main plugin function
70
+ * Bini.js Vite plugin.
71
+ *
72
+ * What it does:
73
+ * - Sets envPrefix to 'BINI_' so client code uses import.meta.env.BINI_MY_VAR
74
+ * - Auto-loads .env for API routes in dev via loadEnv()
75
+ * - Replaces Vite's startup output with the Bini.js branded header
38
76
  */
39
- declare function biniEnvPlugin(options?: Readonly<BiniEnvPluginOptions>): Plugin;
77
+ declare function biniEnv(options?: Readonly<BiniEnvPluginOptions>): Plugin;
40
78
 
41
- export { type BiniEnvPluginOptions, type DetectedEnvFile, biniEnvPlugin as default, detectEnvFiles, formatEnvFilesForDisplay };
79
+ export { type BiniEnvPluginOptions, type DetectedEnvFile, biniEnv, detectEnvFiles, getEnv, loadEnv, requireEnv };
package/dist/index.d.ts CHANGED
@@ -5,37 +5,75 @@ interface BiniEnvPluginOptions {
5
5
  /**
6
6
  * Enable/disable the plugin
7
7
  * @default true
8
- * @readonly
9
8
  */
10
9
  readonly enabled?: boolean;
11
10
  /**
12
11
  * Remove Vite's default header
13
12
  * @default true
14
- * @readonly
15
13
  */
16
14
  readonly clearViteHeader?: boolean;
17
15
  /**
18
16
  * Custom logo/text to display
19
17
  * @default 'ß'
20
- * @readonly
21
18
  */
22
19
  readonly logo?: string;
20
+ /**
21
+ * Override the env var prefix exposed to client code.
22
+ * Defaults to 'BINI_' — so BINI_MY_VAR is accessible via import.meta.env.BINI_MY_VAR
23
+ * Set to 'VITE_' to keep Vite's default behaviour.
24
+ * @default 'BINI_'
25
+ */
26
+ readonly envPrefix?: string;
23
27
  }
24
28
  interface DetectedEnvFile {
25
29
  readonly name: string;
26
30
  readonly path: string;
27
31
  }
28
32
  /**
29
- * Detect which .env files exist in project root
33
+ * Read an environment variable in any runtime.
34
+ *
35
+ * Priority order:
36
+ * 1. Deno.env — Netlify edge functions / Deno runtime
37
+ * 2. process.env — Node.js / Bun / bini-server
38
+ * 3. import.meta.env — Vite client (BINI_-prefixed vars)
39
+ *
40
+ * Usage in API routes:
41
+ * import { getEnv } from 'bini-env';
42
+ * const smtpUser = getEnv('SMTP_USER');
30
43
  */
31
- declare function detectEnvFiles(projectRoot?: string): DetectedEnvFile[];
44
+ declare function getEnv(key: string): string | undefined;
45
+ /**
46
+ * Read an environment variable and throw if it's missing or empty.
47
+ * Use this for required secrets so you get a clear error at startup
48
+ * instead of a cryptic crash later.
49
+ *
50
+ * Usage:
51
+ * const smtpPass = requireEnv('SMTP_PASS');
52
+ */
53
+ declare function requireEnv(key: string): string;
54
+ /**
55
+ * Auto-load .env into process.env for Node.js API route contexts.
56
+ *
57
+ * Behaviour by environment:
58
+ * - Deno (edge functions) → no-op, Deno/Netlify injects env natively
59
+ * - NODE_ENV=production → no-op, host injects env vars automatically
60
+ * - Node.js dev → loads .env via dotenv (dynamic import, safe for bundlers)
61
+ *
62
+ * Called automatically by bini-env Vite plugin. You should not need to call this manually.
63
+ */
64
+ declare function loadEnv(projectRoot?: string): Promise<void>;
32
65
  /**
33
- * Format environment files for console display
66
+ * Detect which .env files exist in the project root, in Vite's priority order.
34
67
  */
35
- declare function formatEnvFilesForDisplay(envFiles: DetectedEnvFile[]): string;
68
+ declare function detectEnvFiles(projectRoot?: string): DetectedEnvFile[];
36
69
  /**
37
- * Main plugin function
70
+ * Bini.js Vite plugin.
71
+ *
72
+ * What it does:
73
+ * - Sets envPrefix to 'BINI_' so client code uses import.meta.env.BINI_MY_VAR
74
+ * - Auto-loads .env for API routes in dev via loadEnv()
75
+ * - Replaces Vite's startup output with the Bini.js branded header
38
76
  */
39
- declare function biniEnvPlugin(options?: Readonly<BiniEnvPluginOptions>): Plugin;
77
+ declare function biniEnv(options?: Readonly<BiniEnvPluginOptions>): Plugin;
40
78
 
41
- export { type BiniEnvPluginOptions, type DetectedEnvFile, biniEnvPlugin as default, detectEnvFiles, formatEnvFilesForDisplay };
79
+ export { type BiniEnvPluginOptions, type DetectedEnvFile, biniEnv, detectEnvFiles, getEnv, loadEnv, requireEnv };
package/dist/index.js CHANGED
@@ -1,130 +1,170 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
1
4
  // src/index.ts
2
- import fs from "fs";
3
- import path from "path";
4
5
  var BINI_LOGO = "\xDF";
6
+ var DEFAULT_ENV_PREFIX = "BINI_";
5
7
  var DEFAULT_OPTIONS = {
6
8
  enabled: true,
7
9
  clearViteHeader: true,
8
- logo: BINI_LOGO
10
+ logo: BINI_LOGO,
11
+ envPrefix: DEFAULT_ENV_PREFIX
9
12
  };
10
13
  var COLORS = {
11
14
  CYAN: "\x1B[36m",
12
15
  RESET: "\x1B[0m",
13
- GREEN: "\x1B[32m",
14
- YELLOW: "\x1B[33m",
15
- DIM: "\x1B[2m"
16
+ GREEN: "\x1B[32m"
16
17
  };
18
+ function getEnv(key) {
19
+ const deno = globalThis.Deno;
20
+ if (deno) {
21
+ try {
22
+ const val = deno.env.get(key);
23
+ if (val !== void 0) return val;
24
+ } catch {
25
+ }
26
+ }
27
+ if (typeof process !== "undefined" && process.env) {
28
+ if (process.env[key] !== void 0) return process.env[key];
29
+ }
30
+ try {
31
+ const metaEnv = import.meta.env;
32
+ if (metaEnv) {
33
+ if (metaEnv[key] !== void 0) return metaEnv[key];
34
+ if (metaEnv[`BINI_${key}`] !== void 0) return metaEnv[`BINI_${key}`];
35
+ }
36
+ } catch {
37
+ }
38
+ return void 0;
39
+ }
40
+ function requireEnv(key) {
41
+ const val = getEnv(key);
42
+ if (!val) {
43
+ throw new Error(
44
+ `[bini-env] Missing required environment variable: "${key}".
45
+ \u2192 In development: add it to your .env file.
46
+ \u2192 In production: add it to your hosting dashboard environment variables.`
47
+ );
48
+ }
49
+ return val;
50
+ }
51
+ var _envLoaded = false;
52
+ async function loadEnv(projectRoot) {
53
+ if (_envLoaded) return;
54
+ if (globalThis.Deno) {
55
+ _envLoaded = true;
56
+ return;
57
+ }
58
+ if (typeof process === "undefined" || !process.env) {
59
+ _envLoaded = true;
60
+ return;
61
+ }
62
+ if (process.env.NODE_ENV === "production") {
63
+ _envLoaded = true;
64
+ return;
65
+ }
66
+ try {
67
+ const root = projectRoot ?? process.cwd();
68
+ const envPath = path.join(root, ".env");
69
+ if (!fs.existsSync(envPath)) {
70
+ _envLoaded = true;
71
+ return;
72
+ }
73
+ const dotenv = await import('dotenv');
74
+ dotenv.config({ path: envPath });
75
+ } catch {
76
+ }
77
+ _envLoaded = true;
78
+ }
17
79
  function detectEnvFiles(projectRoot = process.cwd()) {
18
- const nodeEnv = process.env.NODE_ENV || "development";
19
- const envFiles = [
80
+ const nodeEnv = process.env.NODE_ENV ?? "development";
81
+ const candidates = [
20
82
  ".env.local",
21
83
  `.env.${nodeEnv}.local`,
22
84
  `.env.${nodeEnv}`,
23
85
  ".env"
24
86
  ];
25
87
  const found = [];
26
- for (const file of envFiles) {
88
+ for (const file of candidates) {
27
89
  const filePath = path.join(projectRoot, file);
28
90
  if (fs.existsSync(filePath)) {
29
- found.push({
30
- name: file,
31
- path: filePath
32
- });
91
+ found.push({ name: file, path: filePath });
33
92
  }
34
93
  }
35
94
  return found;
36
95
  }
37
- function formatEnvFilesForDisplay(envFiles) {
38
- if (envFiles.length === 0) {
39
- return "";
40
- }
41
- const envString = envFiles.map((f) => f.name).join(", ");
42
- return `${COLORS.GREEN}\u279C${COLORS.RESET} Environments: ${envString}`;
43
- }
44
96
  function clearLine() {
45
97
  process.stdout.write("\x1B[2K\r");
46
98
  }
47
- function biniEnvPlugin(options = {}) {
48
- const resolvedOptions = Object.freeze({
49
- ...DEFAULT_OPTIONS,
50
- ...options
51
- });
52
- const { enabled, clearViteHeader, logo } = resolvedOptions;
53
- let config;
54
- let isPreviewMode = false;
99
+ function printResolvedUrls(urls) {
100
+ if (!urls) return;
101
+ if (urls.local.length > 0)
102
+ console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Local: ${COLORS.CYAN}${urls.local[0]}${COLORS.RESET}`);
103
+ if (urls.network.length > 0)
104
+ console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Network: ${COLORS.CYAN}${urls.network[0]}${COLORS.RESET}`);
105
+ }
106
+ function biniEnv(options = {}) {
107
+ const resolvedOptions = Object.freeze({ ...DEFAULT_OPTIONS, ...options });
108
+ const { enabled, clearViteHeader, logo, envPrefix } = resolvedOptions;
109
+ let resolvedConfig;
55
110
  let serverStarted = false;
56
111
  let previewStarted = false;
57
112
  return {
58
113
  name: "vite-plugin-bini-env",
59
114
  apply: "serve",
60
- configResolved(resolvedConfig) {
61
- config = resolvedConfig;
62
- if (resolvedConfig.command === "serve") {
63
- isPreviewMode = process.env.VITE_PREVIEW === "true" || process.argv.includes("preview") || false;
64
- }
115
+ // Set BINI_ as the env prefix so import.meta.env.BINI_* works in client code
116
+ config() {
117
+ return { envPrefix };
118
+ },
119
+ configResolved(cfg) {
120
+ resolvedConfig = cfg;
65
121
  },
66
122
  configureServer(server) {
67
123
  if (!enabled) return;
68
- const originalPrintUrls = server.printUrls;
124
+ if (server.httpServer) {
125
+ server.httpServer.once("listening", () => {
126
+ void loadEnv(resolvedConfig?.root ?? process.cwd());
127
+ });
128
+ } else {
129
+ void loadEnv(resolvedConfig?.root ?? process.cwd());
130
+ }
131
+ const originalPrintUrls = server.printUrls.bind(server);
69
132
  server.printUrls = () => {
70
- if (!serverStarted) {
71
- serverStarted = true;
72
- if (clearViteHeader) {
73
- clearLine();
74
- }
75
- console.log(`
133
+ if (serverStarted) return;
134
+ serverStarted = true;
135
+ if (clearViteHeader) clearLine();
136
+ console.log(`
76
137
  ${COLORS.CYAN}${logo} Bini.js${COLORS.RESET} (dev)`);
77
- const found = detectEnvFiles(config.root);
78
- if (found.length > 0) {
79
- const envString = found.map((f) => f.name).join(", ");
80
- console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Environments: ${envString}`);
81
- }
82
- if (!clearViteHeader) {
83
- originalPrintUrls.call(server);
84
- } else {
85
- const urls = server.resolvedUrls;
86
- if (urls) {
87
- if (urls.local.length > 0) {
88
- console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Local: ${COLORS.CYAN}${urls.local[0]}${COLORS.RESET}`);
89
- }
90
- if (urls.network.length > 0) {
91
- console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Network: ${COLORS.CYAN}${urls.network[0]}${COLORS.RESET}`);
92
- }
93
- }
94
- }
138
+ const found = detectEnvFiles(resolvedConfig?.root);
139
+ if (found.length > 0) {
140
+ const envString = found.map((f) => f.name).join(", ");
141
+ console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Environments: ${envString}`);
142
+ }
143
+ if (!clearViteHeader) {
144
+ originalPrintUrls();
145
+ } else {
146
+ printResolvedUrls(server.resolvedUrls);
95
147
  }
96
148
  };
97
149
  },
98
150
  configurePreviewServer(server) {
99
151
  if (!enabled) return;
100
- isPreviewMode = true;
101
- const originalPrintUrls = server.printUrls;
152
+ const originalPrintUrls = server.printUrls.bind(server);
102
153
  server.printUrls = () => {
103
- if (!previewStarted) {
104
- previewStarted = true;
105
- if (clearViteHeader) {
106
- clearLine();
107
- }
108
- console.log(`
154
+ if (previewStarted) return;
155
+ previewStarted = true;
156
+ if (clearViteHeader) clearLine();
157
+ console.log(`
109
158
  ${COLORS.CYAN}${logo} Bini.js${COLORS.RESET} (preview)`);
110
- const found = detectEnvFiles(config.root);
111
- if (found.length > 0) {
112
- const envString = found.map((f) => f.name).join(", ");
113
- console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Environments: ${envString}`);
114
- }
115
- if (!clearViteHeader) {
116
- originalPrintUrls.call(server);
117
- } else {
118
- const urls = server.resolvedUrls;
119
- if (urls) {
120
- if (urls.local.length > 0) {
121
- console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Local: ${COLORS.CYAN}${urls.local[0]}${COLORS.RESET}`);
122
- }
123
- if (urls.network.length > 0) {
124
- console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Network: ${COLORS.CYAN}${urls.network[0]}${COLORS.RESET}`);
125
- }
126
- }
127
- }
159
+ const found = detectEnvFiles(resolvedConfig?.root);
160
+ if (found.length > 0) {
161
+ const envString = found.map((f) => f.name).join(", ");
162
+ console.log(` ${COLORS.GREEN}\u279C${COLORS.RESET} Environments: ${envString}`);
163
+ }
164
+ if (!clearViteHeader) {
165
+ originalPrintUrls();
166
+ } else {
167
+ printResolvedUrls(server.resolvedUrls);
128
168
  }
129
169
  };
130
170
  },
@@ -134,9 +174,7 @@ function biniEnvPlugin(options = {}) {
134
174
  }
135
175
  };
136
176
  }
137
- export {
138
- biniEnvPlugin as default,
139
- detectEnvFiles,
140
- formatEnvFilesForDisplay
141
- };
177
+
178
+ export { biniEnv, detectEnvFiles, getEnv, loadEnv, requireEnv };
179
+ //# sourceMappingURL=index.js.map
142
180
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// src/index.ts\r\nimport fs from 'fs';\r\nimport path from 'path';\r\nimport type { Plugin, ResolvedConfig } from 'vite';\r\n\r\n// Types\r\nexport interface BiniEnvPluginOptions {\r\n /**\r\n * Enable/disable the plugin\r\n * @default true\r\n * @readonly\r\n */\r\n readonly enabled?: boolean;\r\n \r\n /**\r\n * Remove Vite's default header\r\n * @default true\r\n * @readonly\r\n */\r\n readonly clearViteHeader?: boolean;\r\n \r\n /**\r\n * Custom logo/text to display\r\n * @default 'ß'\r\n * @readonly\r\n */\r\n readonly logo?: string;\r\n}\r\n\r\nexport interface DetectedEnvFile {\r\n readonly name: string;\r\n readonly path: string;\r\n}\r\n\r\n// Constants\r\nconst BINI_LOGO = 'ß' as const;\r\nconst DEFAULT_OPTIONS = {\r\n enabled: true,\r\n clearViteHeader: true,\r\n logo: BINI_LOGO,\r\n} as const;\r\n\r\n// Color definitions\r\nconst COLORS = {\r\n CYAN: '\\x1b[36m',\r\n RESET: '\\x1b[0m',\r\n GREEN: '\\x1b[32m',\r\n YELLOW: '\\x1b[33m',\r\n DIM: '\\x1b[2m'\r\n} as const;\r\n\r\n/**\r\n * Detect which .env files exist in project root\r\n */\r\nexport function detectEnvFiles(projectRoot: string = process.cwd()): DetectedEnvFile[] {\r\n const nodeEnv = process.env.NODE_ENV || 'development';\r\n\r\n const envFiles = [\r\n '.env.local',\r\n `.env.${nodeEnv}.local`,\r\n `.env.${nodeEnv}`,\r\n '.env'\r\n ] as const;\r\n\r\n const found: DetectedEnvFile[] = [];\r\n\r\n for (const file of envFiles) {\r\n const filePath = path.join(projectRoot, file);\r\n if (fs.existsSync(filePath)) {\r\n found.push({\r\n name: file,\r\n path: filePath\r\n });\r\n }\r\n }\r\n\r\n return found;\r\n}\r\n\r\n/**\r\n * Format environment files for console display\r\n */\r\nexport function formatEnvFilesForDisplay(envFiles: DetectedEnvFile[]): string {\r\n if (envFiles.length === 0) {\r\n return '';\r\n }\r\n\r\n const envString = envFiles.map(f => f.name).join(', ');\r\n return `${COLORS.GREEN}➜${COLORS.RESET} Environments: ${envString}`;\r\n}\r\n\r\n/**\r\n * Clear the current line in console\r\n */\r\nfunction clearLine(): void {\r\n process.stdout.write('\\x1b[2K\\r');\r\n}\r\n\r\n/**\r\n * Main plugin function\r\n */\r\nexport default function biniEnvPlugin(options: Readonly<BiniEnvPluginOptions> = {}): Plugin {\r\n // Merge with defaults — user cannot override core behaviour\r\n const resolvedOptions = Object.freeze({\r\n ...DEFAULT_OPTIONS,\r\n ...options,\r\n });\r\n\r\n const { enabled, clearViteHeader, logo } = resolvedOptions;\r\n\r\n let config: ResolvedConfig;\r\n let isPreviewMode = false;\r\n let serverStarted = false;\r\n let previewStarted = false;\r\n\r\n return {\r\n name: 'vite-plugin-bini-env',\r\n \r\n apply: 'serve',\r\n \r\n configResolved(resolvedConfig: ResolvedConfig) {\r\n config = resolvedConfig;\r\n \r\n if (resolvedConfig.command === 'serve') {\r\n isPreviewMode = process.env.VITE_PREVIEW === 'true' || \r\n process.argv.includes('preview') ||\r\n false;\r\n }\r\n },\r\n\r\n configureServer(server) {\r\n if (!enabled) return;\r\n\r\n const originalPrintUrls = server.printUrls;\r\n \r\n server.printUrls = () => {\r\n if (!serverStarted) {\r\n serverStarted = true;\r\n \r\n if (clearViteHeader) {\r\n clearLine();\r\n }\r\n \r\n console.log(`\\n ${COLORS.CYAN}${logo} Bini.js${COLORS.RESET} (dev)`);\r\n\r\n const found = detectEnvFiles(config.root);\r\n if (found.length > 0) {\r\n const envString = found.map(f => f.name).join(', ');\r\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Environments: ${envString}`);\r\n }\r\n \r\n if (!clearViteHeader) {\r\n originalPrintUrls.call(server);\r\n } else {\r\n const urls = server.resolvedUrls;\r\n if (urls) {\r\n if (urls.local.length > 0) {\r\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Local: ${COLORS.CYAN}${urls.local[0]}${COLORS.RESET}`);\r\n }\r\n if (urls.network.length > 0) {\r\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Network: ${COLORS.CYAN}${urls.network[0]}${COLORS.RESET}`);\r\n }\r\n }\r\n }\r\n }\r\n };\r\n },\r\n\r\n configurePreviewServer(server) {\r\n if (!enabled) return;\r\n \r\n isPreviewMode = true;\r\n \r\n const originalPrintUrls = server.printUrls;\r\n \r\n server.printUrls = () => {\r\n if (!previewStarted) {\r\n previewStarted = true;\r\n \r\n if (clearViteHeader) {\r\n clearLine();\r\n }\r\n \r\n console.log(`\\n ${COLORS.CYAN}${logo} Bini.js${COLORS.RESET} (preview)`);\r\n\r\n const found = detectEnvFiles(config.root);\r\n if (found.length > 0) {\r\n const envString = found.map(f => f.name).join(', ');\r\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Environments: ${envString}`);\r\n }\r\n \r\n if (!clearViteHeader) {\r\n originalPrintUrls.call(server);\r\n } else {\r\n const urls = server.resolvedUrls;\r\n if (urls) {\r\n if (urls.local.length > 0) {\r\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Local: ${COLORS.CYAN}${urls.local[0]}${COLORS.RESET}`);\r\n }\r\n if (urls.network.length > 0) {\r\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Network: ${COLORS.CYAN}${urls.network[0]}${COLORS.RESET}`);\r\n }\r\n }\r\n }\r\n }\r\n };\r\n },\r\n\r\n buildEnd() {\r\n serverStarted = false;\r\n previewStarted = false;\r\n }\r\n };\r\n}\r\n\r\nexport type { Plugin } from 'vite';"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// src/index.ts\nimport fs from 'fs';\nimport path from 'path';\nimport type { Plugin, ResolvedConfig, UserConfig } from 'vite';\n\n/* ==========================================================================\n Global type declarations for cross-runtime compatibility\n ========================================================================== */\n\n// Deno runtime type — declared as an interface so we can safely cast\n// without relying on a global that TypeScript doesn't know about\ninterface DenoEnv {\n get(key: string): string | undefined;\n toObject(): Record<string, string>;\n}\ninterface DenoNamespace {\n env: DenoEnv;\n}\n\n// Vite augments ImportMeta with .env\n// Using triple-slash reference so it works both inside and outside Vite projects\n/// <reference types=\"vite/client\" />\n\n/* ==========================================================================\n Types\n ========================================================================== */\n\nexport interface BiniEnvPluginOptions {\n /**\n * Enable/disable the plugin\n * @default true\n */\n readonly enabled?: boolean;\n\n /**\n * Remove Vite's default header\n * @default true\n */\n readonly clearViteHeader?: boolean;\n\n /**\n * Custom logo/text to display\n * @default 'ß'\n */\n readonly logo?: string;\n\n /**\n * Override the env var prefix exposed to client code.\n * Defaults to 'BINI_' — so BINI_MY_VAR is accessible via import.meta.env.BINI_MY_VAR\n * Set to 'VITE_' to keep Vite's default behaviour.\n * @default 'BINI_'\n */\n readonly envPrefix?: string;\n}\n\nexport interface DetectedEnvFile {\n readonly name: string;\n readonly path: string;\n}\n\n/* ==========================================================================\n Constants\n ========================================================================== */\n\nconst BINI_LOGO = 'ß' as const;\nconst DEFAULT_ENV_PREFIX = 'BINI_' as const;\n\nconst DEFAULT_OPTIONS = {\n enabled: true,\n clearViteHeader: true,\n logo: BINI_LOGO,\n envPrefix: DEFAULT_ENV_PREFIX,\n} as const;\n\nconst COLORS = {\n CYAN: '\\x1b[36m',\n RESET: '\\x1b[0m',\n GREEN: '\\x1b[32m',\n} as const;\n\n/* ==========================================================================\n getEnv — Universal env var accessor\n Works in: Node.js, Deno (edge functions), Bun, Vite client\n ========================================================================== */\n\n/**\n * Read an environment variable in any runtime.\n *\n * Priority order:\n * 1. Deno.env — Netlify edge functions / Deno runtime\n * 2. process.env — Node.js / Bun / bini-server\n * 3. import.meta.env — Vite client (BINI_-prefixed vars)\n *\n * Usage in API routes:\n * import { getEnv } from 'bini-env';\n * const smtpUser = getEnv('SMTP_USER');\n */\nexport function getEnv(key: string): string | undefined {\n // 1. Deno runtime (Netlify edge functions)\n const deno = (globalThis as unknown as { Deno?: DenoNamespace }).Deno;\n if (deno) {\n try {\n const val = deno.env.get(key);\n if (val !== undefined) return val;\n } catch {\n // Deno env access denied — fall through\n }\n }\n\n // 2. Node.js / Bun / bini-server\n if (typeof process !== 'undefined' && process.env) {\n if (process.env[key] !== undefined) return process.env[key];\n }\n\n // 3. Vite injected (import.meta.env) — BINI_-prefixed client vars\n // Wrapped in try/catch because import.meta.env doesn't exist in Node/Deno\n try {\n const metaEnv = import.meta.env as Record<string, string | undefined> | undefined;\n if (metaEnv) {\n if (metaEnv[key] !== undefined) return metaEnv[key];\n // Try with BINI_ prefix automatically\n if (metaEnv[`BINI_${key}`] !== undefined) return metaEnv[`BINI_${key}`];\n }\n } catch {\n // import.meta.env not available in this runtime — ignore\n }\n\n return undefined;\n}\n\n/**\n * Read an environment variable and throw if it's missing or empty.\n * Use this for required secrets so you get a clear error at startup\n * instead of a cryptic crash later.\n *\n * Usage:\n * const smtpPass = requireEnv('SMTP_PASS');\n */\nexport function requireEnv(key: string): string {\n const val = getEnv(key);\n if (!val) {\n throw new Error(\n `[bini-env] Missing required environment variable: \"${key}\".\\n` +\n ` → In development: add it to your .env file.\\n` +\n ` → In production: add it to your hosting dashboard environment variables.`\n );\n }\n return val;\n}\n\n/* ==========================================================================\n loadEnv — Auto dotenv loader for bini-router API routes (Node.js only)\n Called internally by the Vite plugin. Safe to call multiple times.\n ========================================================================== */\n\nlet _envLoaded = false;\n\n/**\n * Auto-load .env into process.env for Node.js API route contexts.\n *\n * Behaviour by environment:\n * - Deno (edge functions) → no-op, Deno/Netlify injects env natively\n * - NODE_ENV=production → no-op, host injects env vars automatically\n * - Node.js dev → loads .env via dotenv (dynamic import, safe for bundlers)\n *\n * Called automatically by bini-env Vite plugin. You should not need to call this manually.\n */\nexport async function loadEnv(projectRoot?: string): Promise<void> {\n // Already loaded — skip\n if (_envLoaded) return;\n\n // Deno runtime — env is handled natively, skip\n if ((globalThis as unknown as { Deno?: DenoNamespace }).Deno) {\n _envLoaded = true;\n return;\n }\n\n // Not in a Node-like runtime — skip\n if (typeof process === 'undefined' || !process.env) {\n _envLoaded = true;\n return;\n }\n\n // Production — host injects env vars, dotenv not needed\n if (process.env.NODE_ENV === 'production') {\n _envLoaded = true;\n return;\n }\n\n try {\n const root = projectRoot ?? process.cwd();\n const envPath = path.join(root, '.env');\n\n if (!fs.existsSync(envPath)) {\n _envLoaded = true;\n return;\n }\n\n // Dynamic import so Deno/edge bundlers never try to statically resolve dotenv\n const dotenv = await import('dotenv');\n dotenv.config({ path: envPath });\n } catch {\n // dotenv not installed or read failed — silently skip.\n // requireEnv() will surface clear errors if vars are missing when used.\n }\n\n _envLoaded = true;\n}\n\n/* ==========================================================================\n detectEnvFiles — used by the Vite plugin display\n ========================================================================== */\n\n/**\n * Detect which .env files exist in the project root, in Vite's priority order.\n */\nexport function detectEnvFiles(projectRoot: string = process.cwd()): DetectedEnvFile[] {\n const nodeEnv = process.env.NODE_ENV ?? 'development';\n\n const candidates = [\n '.env.local',\n `.env.${nodeEnv}.local`,\n `.env.${nodeEnv}`,\n '.env',\n ] as const;\n\n const found: DetectedEnvFile[] = [];\n\n for (const file of candidates) {\n const filePath = path.join(projectRoot, file);\n if (fs.existsSync(filePath)) {\n found.push({ name: file, path: filePath });\n }\n }\n\n return found;\n}\n\n/* ==========================================================================\n Vite plugin\n ========================================================================== */\n\nfunction clearLine(): void {\n process.stdout.write('\\x1b[2K\\r');\n}\n\nfunction printResolvedUrls(urls: { local: string[]; network: string[] } | null | undefined): void {\n if (!urls) return;\n if (urls.local.length > 0)\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Local: ${COLORS.CYAN}${urls.local[0]}${COLORS.RESET}`);\n if (urls.network.length > 0)\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Network: ${COLORS.CYAN}${urls.network[0]}${COLORS.RESET}`);\n}\n\n/**\n * Bini.js Vite plugin.\n *\n * What it does:\n * - Sets envPrefix to 'BINI_' so client code uses import.meta.env.BINI_MY_VAR\n * - Auto-loads .env for API routes in dev via loadEnv()\n * - Replaces Vite's startup output with the Bini.js branded header\n */\nexport function biniEnv(options: Readonly<BiniEnvPluginOptions> = {}): Plugin {\n const resolvedOptions = Object.freeze({ ...DEFAULT_OPTIONS, ...options });\n const { enabled, clearViteHeader, logo, envPrefix } = resolvedOptions;\n\n let resolvedConfig: ResolvedConfig;\n let serverStarted = false;\n let previewStarted = false;\n\n return {\n name: 'vite-plugin-bini-env',\n\n apply: 'serve',\n\n // Set BINI_ as the env prefix so import.meta.env.BINI_* works in client code\n config(): UserConfig {\n return { envPrefix };\n },\n\n configResolved(cfg: ResolvedConfig) {\n resolvedConfig = cfg;\n },\n\n configureServer(server) {\n if (!enabled) return;\n\n // Auto-load .env after server starts — guarantees resolvedConfig is available\n // Falls back to immediate call in middleware mode where httpServer is null\n if (server.httpServer) {\n server.httpServer.once('listening', () => {\n void loadEnv(resolvedConfig?.root ?? process.cwd());\n });\n } else {\n // Middleware mode — httpServer is null, call immediately\n void loadEnv(resolvedConfig?.root ?? process.cwd());\n }\n\n const originalPrintUrls = server.printUrls.bind(server);\n\n server.printUrls = () => {\n if (serverStarted) return;\n serverStarted = true;\n\n if (clearViteHeader) clearLine();\n console.log(`\\n ${COLORS.CYAN}${logo} Bini.js${COLORS.RESET} (dev)`);\n\n const found = detectEnvFiles(resolvedConfig?.root);\n if (found.length > 0) {\n const envString = found.map((f) => f.name).join(', ');\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Environments: ${envString}`);\n }\n\n if (!clearViteHeader) {\n originalPrintUrls();\n } else {\n printResolvedUrls(server.resolvedUrls);\n }\n };\n },\n\n configurePreviewServer(server) {\n if (!enabled) return;\n\n const originalPrintUrls = server.printUrls.bind(server);\n\n server.printUrls = () => {\n if (previewStarted) return;\n previewStarted = true;\n\n if (clearViteHeader) clearLine();\n console.log(`\\n ${COLORS.CYAN}${logo} Bini.js${COLORS.RESET} (preview)`);\n\n const found = detectEnvFiles(resolvedConfig?.root);\n if (found.length > 0) {\n const envString = found.map((f) => f.name).join(', ');\n console.log(` ${COLORS.GREEN}➜${COLORS.RESET} Environments: ${envString}`);\n }\n\n if (!clearViteHeader) {\n originalPrintUrls();\n } else {\n printResolvedUrls(server.resolvedUrls);\n }\n };\n },\n\n buildEnd() {\n serverStarted = false;\n previewStarted = false;\n },\n };\n}\n\n// Named export: import { biniEnv } from 'bini-env'\nexport type { Plugin } from 'vite';"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bini-env",
3
- "version": "1.0.5",
4
- "description": "Vite plugin for Bini.js environment display",
3
+ "version": "1.0.6",
4
+ "description": "Universal environment variable loader and Vite plugin for Bini.js — works in Node.js, Deno, Bun, and Vite edge functions",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -17,19 +17,39 @@
17
17
  "dist",
18
18
  "README.md"
19
19
  ],
20
+ "scripts": {
21
+ "build": "tsup",
22
+ "dev": "tsup --watch",
23
+ "prepublishOnly": "npm run build"
24
+ },
20
25
  "keywords": [
21
26
  "vite",
22
27
  "vite-plugin",
23
- "bini.js",
24
28
  "bini",
29
+ "bini-js",
30
+ "bini.js",
25
31
  "environment",
26
32
  "env",
27
- "dotenv"
33
+ "dotenv",
34
+ "deno",
35
+ "edge-functions",
36
+ "cross-runtime"
28
37
  ],
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "https://github.com/Binidu01/Bini-Env.git"
41
+ },
42
+ "homepage": "https://github.com/Binidu01/Bini-Env#readme",
43
+ "bugs": {
44
+ "url": "https://github.com/Binidu01/Bini-Env/issues"
45
+ },
29
46
  "author": "Bini.js Team",
30
47
  "license": "MIT",
31
48
  "peerDependencies": {
32
- "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
49
+ "vite": ">=7.0.0"
50
+ },
51
+ "dependencies": {
52
+ "dotenv": "^17.3.1"
33
53
  },
34
54
  "devDependencies": {
35
55
  "@types/node": "^22.15.3",
@@ -49,13 +69,14 @@
49
69
  "clean": true,
50
70
  "splitting": false,
51
71
  "sourcemap": true,
52
- "minify": false
72
+ "minify": false,
73
+ "external": [
74
+ "vite",
75
+ "fs",
76
+ "path"
77
+ ]
53
78
  },
54
79
  "engines": {
55
80
  "node": ">=18.0.0"
56
- },
57
- "scripts": {
58
- "build": "tsup",
59
- "dev": "tsup --watch"
60
81
  }
61
82
  }
package/Readme.md DELETED
@@ -1,135 +0,0 @@
1
- # bini-env
2
-
3
- ![npm](https://img.shields.io/npm/v/bini-env?color=cyan&style=flat-square)
4
- ![npm downloads](https://img.shields.io/npm/dm/bini-env?style=flat-square)
5
- ![license](https://img.shields.io/npm/l/bini-env?style=flat-square)
6
- ![vite](https://img.shields.io/badge/vite-plugin-646CFF?style=flat-square&logo=vite)
7
- ![typescript](https://img.shields.io/badge/TypeScript-ready-3178C6?style=flat-square&logo=typescript)
8
-
9
- A lightweight Vite plugin that enhances your dev and preview server output with a clean startup banner and detected `.env` files.
10
-
11
- ## Features
12
-
13
- - Replaces Vite's default header with a clean custom banner
14
- - Automatically detects and displays active `.env` files
15
- - Supports `dev` and `preview` modes
16
- - Fully customizable logo and behavior
17
-
18
- ## Installation
19
- ```bash
20
- npm install bini-env --save-dev
21
- # or
22
- pnpm add -D bini-env
23
- # or
24
- yarn add -D bini-env
25
- ```
26
-
27
- ## Usage
28
- ```ts
29
- // vite.config.ts
30
- import { defineConfig } from 'vite';
31
- import biniEnv from 'bini-env';
32
-
33
- export default defineConfig({
34
- plugins: [biniEnv()]
35
- });
36
- ```
37
-
38
- ## Output Example
39
-
40
- **Dev mode**
41
- ```
42
- ß Bini.js (dev)
43
- ➜ Environments: .env.local, .env
44
- ➜ Local: http://localhost:5173/
45
- ➜ Network: http://192.168.1.10:5173/
46
- ```
47
-
48
- **Preview mode**
49
- ```
50
- ß Bini.js (preview)
51
- ➜ Environments: .env.local, .env
52
- ➜ Local: http://localhost:4173/
53
- ➜ Network: http://192.168.1.10:4173/
54
- ```
55
-
56
- ## Options
57
-
58
- | Option | Type | Default | Description |
59
- |--------|------|---------|-------------|
60
- | `enabled` | `boolean` | `true` | Enable or disable the plugin |
61
- | `clearViteHeader` | `boolean` | `true` | Replace Vite's default startup header |
62
- | `logo` | `string` | `'ß'` | Custom logo or text shown in the banner |
63
-
64
- ### Example with options
65
- ```ts
66
- biniEnv({
67
- logo: '🚀',
68
- clearViteHeader: true,
69
- })
70
- ```
71
-
72
- ## Detected `.env` Files
73
-
74
- The plugin checks for the following files in your project root (in priority order):
75
-
76
- 1. `.env.local`
77
- 2. `.env.[NODE_ENV].local`
78
- 3. `.env.[NODE_ENV]`
79
- 4. `.env`
80
-
81
- Any files found are displayed in the banner on startup.
82
-
83
- ## Modes
84
-
85
- | Mode | Trigger | Banner |
86
- |------|---------|--------|
87
- | Development | `vite dev` | `ß Bini.js (dev)` |
88
- | Preview | `vite preview` | `ß Bini.js (preview)` |
89
-
90
- > **Note:** `bini-env` is a server-only plugin (`apply: 'serve'`) and does not run during `vite build`.
91
-
92
- ## API
93
-
94
- The following utilities are exported for use outside of the plugin if needed.
95
-
96
- ### `detectEnvFiles(projectRoot?: string): DetectedEnvFile[]`
97
-
98
- Scans a directory for `.env` files and returns an array of matches.
99
- ```ts
100
- import { detectEnvFiles } from 'bini-env';
101
-
102
- const files = detectEnvFiles('/my/project');
103
- // [{ name: '.env.local', path: '/my/project/.env.local' }, ...]
104
- ```
105
-
106
- Defaults to `process.cwd()` if no path is provided.
107
-
108
- ### `formatEnvFilesForDisplay(envFiles: DetectedEnvFile[]): string`
109
-
110
- Formats a list of detected env files into a display-ready console string.
111
- ```ts
112
- import { detectEnvFiles, formatEnvFilesForDisplay } from 'bini-env';
113
-
114
- const files = detectEnvFiles();
115
- console.log(formatEnvFilesForDisplay(files));
116
- // ➜ Environments: .env
117
- ```
118
-
119
- ## Types
120
- ```ts
121
- interface BiniEnvPluginOptions {
122
- enabled?: boolean;
123
- clearViteHeader?: boolean;
124
- logo?: string;
125
- }
126
-
127
- interface DetectedEnvFile {
128
- name: string;
129
- path: string;
130
- }
131
- ```
132
-
133
- ## License
134
-
135
- MIT