UNPKG

bun-plugin-dotenvx

Version:

A Bun plugin for a seamless dotenvx experience.

236 lines (202 loc) 5.32 kB
import type { BunPlugin } from 'bun' import process from 'node:process' import * as dotenvx from '@dotenvx/dotenvx' export interface DotenvxPluginOptions { /** * Path(s) to your env file(s) * @default ['.env'] */ path?: string | string[] /** * Override existing env variables * @default false */ overload?: boolean /** * Exit with code 1 if any errors are encountered * @default false */ strict?: boolean /** * Ignore specific errors * @example ['MISSING_ENV_FILE'] */ ignore?: string[] /** * Path to your .env.keys file * @default '.env.keys' */ envKeysFile?: string /** * Load a .env convention * @example 'nextjs' */ convention?: 'nextjs' /** * Set log level * @default 'info' */ logLevel?: 'error' | 'warn' | 'info' | 'verbose' | 'debug' | 'success' | 'successv' | 'help' /** * Suppress all output (except errors) * @default false */ quiet?: boolean /** * Set log level to verbose * @default false */ verbose?: boolean /** * Set log level to debug * @default false */ debug?: boolean } // Global state to track if we should suppress dotenvx output let shouldSuppressDotenvx = false let originalConsoleLog: typeof console.log let originalConsoleInfo: typeof console.info let originalConsoleWarn: typeof console.warn /** * Check if we're running under launchpad context */ function isLaunchpadContext(): boolean { return !!( // Primary indicator: shell integration flag process.env.LAUNCHPAD_SHELL_INTEGRATION // Secondary indicators || process.env.LAUNCHPAD_ORIGINAL_PATH || process.argv.some(arg => arg.includes('launchpad')) || process.argv0?.includes('launchpad') || process.title?.includes('launchpad') || process.cwd().includes('/launchpad') || process.argv[1]?.includes('launchpad') ) } /** * Suppress all console output from dotenvx */ function suppressConsoleOutput(): void { if (shouldSuppressDotenvx) return // Already suppressed shouldSuppressDotenvx = true /* eslint-disable no-console */ originalConsoleLog = console.log originalConsoleInfo = console.info originalConsoleWarn = console.warn // Suppress all dotenvx output console.log = (...args: any[]) => { const message = args.join(' ') if (!message.includes('[dotenvx@')) { originalConsoleLog(...args) } } console.info = (...args: any[]) => { const message = args.join(' ') if (!message.includes('[dotenvx@')) { originalConsoleInfo(...args) } } console.warn = (...args: any[]) => { const message = args.join(' ') if (!message.includes('[dotenvx@')) { originalConsoleWarn(...args) } } /* eslint-enable no-console */ } /** * Restore original console methods */ function restoreConsoleOutput(): void { if (!shouldSuppressDotenvx) return // Not suppressed shouldSuppressDotenvx = false /* eslint-disable no-console */ console.log = originalConsoleLog console.info = originalConsoleInfo console.warn = originalConsoleWarn /* eslint-enable no-console */ } /** * Load dotenvx configuration with proper suppression */ function loadDotenvxConfig(options: DotenvxPluginOptions): void { const isLaunchpad = isLaunchpadContext() if (isLaunchpad) { suppressConsoleOutput() } try { const configOptions: any = { path: options.path || ['.env'], overload: options.overload, strict: options.strict, ignore: options.ignore, envKeysFile: options.envKeysFile, } // Handle log levels based on context if (isLaunchpad) { // Force error-only logging for launchpad configOptions.logLevel = 'error' } else { // Normal operation for non-launchpad usage if (options.quiet) { configOptions.logLevel = 'error' } else if (options.verbose) { configOptions.logLevel = 'verbose' } else if (options.debug) { configOptions.logLevel = 'debug' } else if (options.logLevel) { configOptions.logLevel = options.logLevel } } dotenvx.config(configOptions) } finally { if (isLaunchpad) { restoreConsoleOutput() } } } // Initialize dotenvx immediately when the module is loaded // This ensures it runs even when imported via preloader loadDotenvxConfig({}) /** * Create a dotenvx plugin with custom options */ export function createDotenvxPlugin(options: DotenvxPluginOptions = {}): BunPlugin { return { name: 'bun-plugin-dotenvx', async setup(build) { // Load dotenvx with the provided options loadDotenvxConfig(options) build.onLoad({ filter: /\.env.*$/ }, async (args) => { // Get the file path from the args const filePath = args.path // Load the specific .env file if it's being imported directly if (filePath) { loadDotenvxConfig({ ...options, path: [filePath], }) } // Return the environment variables const exports = process.env return { exports: { default: exports, ...exports, }, loader: 'object', } }) }, } } export * from './types' export default createDotenvxPlugin