Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Here are some of the benefits of using `dotenv-run`:
| Core | [@dotenv-run/core](#dotenv-runcore) | ✅ |
| ESBuild | [@dotenv-run/esbuild](#dotenv-runesbuild) | ✅ |
| Rollup | [@dotenv-run/rollup](#dotenv-runrollup) | ✅ |
| Vite | [@dotenv-run/rollup](#dotenv-runrollup) | ✅ |
| Vite | [@dotenv-run/vite](#dotenv-runvite) | ✅ |
| Node.js preload | @dotenv-run/load | ✅ |
| Angular | [@ngx-env/builder](#ngx-envbuilder) | ✅ |

Expand All @@ -31,6 +31,7 @@ Here are some of the benefits of using `dotenv-run`:
- [@dotenv-run/core](#dotenv-runcore)
- [@dotenv-run/esbuild](#dotenv-runesbuild)
- [@ngx-env/builder](#ngx-envbuilder)
- [Testimonials](#testimonials)
- [Demos](#demos)
- [Quick start](#quick-start-1)
- [@dotenv-run/webpack](#dotenv-runwebpack)
Expand Down Expand Up @@ -275,6 +276,19 @@ export default {
};
```


[`@dotenv-run/vite`](https://www.npmjs.com/package/@dotenv-run/vite) is a plugin for vite that can be used to inject environment variables into your applications.

```js
import env from "@dotenv-run/vite";

export default {
envPrefix: 'MY_PREFIX_',
envDir: './my-env-directory',
plugins: [env()],
};
```

## Credits

- [dotenv](https://github.com/motdotla/dotenv)
Expand Down
2 changes: 1 addition & 1 deletion docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default defineConfig({
{ label: 'Node.js', link: '/integrations/loader/' },
{ label: 'Rollup', link: '/integrations/rollup/' },
{ label: 'Webpack', link: '/integrations/webpack/' },
// { label: 'Vite', link: '/integrations/vite/' },
{ label: 'Vite', link: '/integrations/vite/' },
// { label: 'Babel', link: '/integrations/webpack/' },
// { label: 'Jest', link: '/integrations/jest/' },
// { label: 'SWC', link: '/integrations/swc/' },
Expand Down
26 changes: 25 additions & 1 deletion docs/src/content/docs/integrations/vite.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
---
title: Vite
---
---

## Install

```console
npm add @dotenv-run/vite --save-dev
```

## Usage

Create a `vite.config.js` [configuration file](https://vite.dev/config) and import the plugin:

```js
import env from "@dotenv-run/vite";

export default {
envPrefix: 'MY_PREFIX_',
envDir: './my-env-directory',
plugins: [env()],
};
```

Then call `vite` or `vite build` either via the [CLI](https://vite.dev/guide/cli.html).

The available options are similar to those supported by [`@dotenv-run/core`](https://www.npmjs.com/package/@dotenv-run/core), but this plugin seamlessly integrates with Vite by automatically deriving the root, prefix, and environment values from its standard configuration, ensuring a more cohesive experience. For more details, refer to the API section.
30 changes: 22 additions & 8 deletions packages/core/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as fs from "fs";
import * as path from "path";
import { DotenvRun, build } from "./build.js";
import { expand } from "./expand.js";
import type { DotenvRunOptions } from "./options.js";
import type { DotenvRunOptions, Prefix } from "./options.js";
import { getAbsoluteEnvPath, getPathsDownTo, isSubfolder } from "./utils.js";
import { findRootPath } from "./root.js";

Expand Down Expand Up @@ -52,12 +52,15 @@ function print(options: DotenvRunOptions, envPaths: string[], values: Env) {
console.log("---------------------------------\n");
}

function filter(env: Env, prefix: RegExp, nodeEnv: boolean): Env {
function filter(env: Env, prefixes: RegExp[], nodeEnv: boolean): Env {
const hasMatchingPrefix = (key: string) =>
prefixes.some((prefix) => prefix.test(key));

return Object.keys(env)
.filter(
(key) =>
env[key] !== undefined &&
((nodeEnv && key === "NODE_ENV") || prefix.test(key))
((nodeEnv && key === "NODE_ENV") || hasMatchingPrefix(key))
)
.sort() // sort keys to make output more deterministic
.reduce<Env>((env, key) => {
Expand Down Expand Up @@ -104,6 +107,21 @@ function paths({ environment, root, cwd, files }: DotenvRunOptions): {
};
}

function prefixes(prefix: Prefix | Prefix[] | undefined): RegExp[] {
if (prefix == null) {
return null;
}

if (!Array.isArray(prefix)) {
prefix = [prefix];
}

const coerceRegExp = (value: string | RegExp): RegExp =>
typeof value === "string" ? new RegExp(value, "i") : value;

return prefix.map(coerceRegExp);
}

export function env({
cwd = process.cwd(),
environment = process.env.NODE_ENV,
Expand Down Expand Up @@ -134,11 +152,7 @@ export function env({
expand(envPaths, dotenv);
const processEnv = process.env;
const values = prefix
? filter(
processEnv,
typeof prefix === "string" ? new RegExp(prefix, "i") : prefix,
nodeEnv
)
? filter(processEnv, prefixes(prefix), nodeEnv)
: processEnv;
const allValues = { ...values, ...builtIn };
if (verbose) {
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/options.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import type { DotenvConfigOptions } from "dotenv";
import { Dict } from "./build";

export type Prefix = string | RegExp;

export interface DotenvRunOptions {
cwd?: string; // Path to current working directory
dotenv?: DotenvConfigOptions;
environment?: string; // Environment to load
files?: string[]; // Environment files to load
prefix?: string | RegExp; // Filter keys to inject
prefix?: Prefix | Prefix[]; // Filter keys to inject
unsecure?: boolean; // Display environment variables in debug output
root?: string; // Path to root workspace
nodeEnv?: boolean; // Node environment
Expand Down
33 changes: 33 additions & 0 deletions packages/vite/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# @dotenv-run/vite

- ✅ Load environment variables from the command line `API_BASE=/v1/ vite`
- ✅ Load environment variables from `.env` files
- ✅ Expand environment variables `API_URL=$API_BASE/users`
- ✅ Define environment variables for a specific environment (e.g. `.env.production`)
- ✅ Load priorities of `.env.*` files (e.g. `.env.production` > `.env`)
- ✅ Hierarchical cascading configuration in monorepo projects ([Nx](https://nx.dev), [Turbo](https://turbo.build/), etc.)
`apps/next-app/.env` > `apps/.env` > `.env`

## Install

```sh
npm add @dotenv-run/vite --save-dev
```

## Usage

Create a `vite.config.js` [configuration file](https://vite.dev/config) and import the plugin:

```js
import env from "@dotenv-run/vite";

export default {
envPrefix: 'MY_PREFIX_',
envDir: './my-env-directory',
plugins: [env()],
};
```

Then call `vite` or `vite build` either via the [CLI](https://vite.dev/guide/cli.html).

The available options are similar to those supported by [`@dotenv-run/core`](https://www.npmjs.com/package/@dotenv-run/core), but this plugin seamlessly integrates with Vite by automatically deriving the root, prefix, and environment values from its standard configuration, ensuring a more cohesive experience. For more details, refer to the API section.
36 changes: 36 additions & 0 deletions packages/vite/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "@dotenv-run/vite",
"version": "1.0.0",
"description": "Run your scripts with dotenv variables",
"homepage": "https://github.com/chihab/dotenv-run",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"type": "module",
"scripts": {
"dev": "tsc -w",
"build": "tsc"
},
"files": [
"dist",
"README.md"
],
"keywords": [
"dotenv",
"run",
"cli",
"vite",
"vite-plugin"
],
"author": "Iacopo Ciao <[email protected]>",
"license": "ISC",
"dependencies": {
"@dotenv-run/core": "workspace:^1.3.6",
"@rollup/plugin-replace": "^5.0.7",
"lodash-es": "^4.17.21",
"vite": "^6.2.0"
},
"devDependencies": {
"@types/lodash-es": "^4.17.12",
"@types/node": "^16.18.112"
}
}
6 changes: 6 additions & 0 deletions packages/vite/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const DEFAULT_PREFIX = 'VITE_';

export const DEFAULT_ENV_FILES = [
/** vault file */ `.env.vault`,
/** default file */ `.env`,
];
61 changes: 61 additions & 0 deletions packages/vite/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { env as loadEnv } from "@dotenv-run/core";
import { Plugin } from "vite";
import { viteEnvPrefixToPrefix } from "./mapper.js";
import { sanitizeOptions, ViteDotenvRunOptions } from "./options.js";
import replace from "@rollup/plugin-replace";
import { DEFAULT_ENV_FILES } from "./constants.js";

/**
* Vite plugin to load environment variables from .env files using `@dotenv-run/core`.
*
* This plugin seamlessly integrates with Vite by automatically deriving the root,
* prefix and environment options from Vite's `envDir`, `envPrefix` and `mode`,
* ensuring a more cohesive experience.
*
* @param {ViteDotenvRunOptions} [options] - Options for configuring the plugin.
* See {@link ViteDotenvRunOptions} for more details.
*
* @returns {Plugin} Vite plugin object that enhances the Vite configuration.
*
* @example
* // Usage in a Vite config file
* import dotenvRun from 'vite-plugin-dotenv-run';
*
* export default {
* envDir: '../..',
* envPrefix: ['VITE_', 'CUSTOM_'],
* plugins: [
* dotenvRun(),
* ],
* };
*/
const dotenvRun = (options?: ViteDotenvRunOptions): Plugin => {
options = sanitizeOptions(options);
const files = options?.files ?? DEFAULT_ENV_FILES;

return {
name: "vite-plugin-dotenv-run",
config: (config, configEnv) => {
const prefix = viteEnvPrefixToPrefix(config.envPrefix);

const { full } = loadEnv({
files,
prefix,
root: config.envDir,
environment: configEnv.mode,
...options,
});

return {
...config,
...replace({
preventAssignment: true,
values: full,
}),
};
},
};
};

export { dotenvRun, ViteDotenvRunOptions };
export default dotenvRun;
54 changes: 54 additions & 0 deletions packages/vite/src/mapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {
castArray,
isEmpty,
isNil,
negate,
} from "lodash-es";
import { DEFAULT_PREFIX } from "./constants.js";
import { Prefix } from "@dotenv-run/core";

/**
* Converts a Vite `envPrefix` configuration value into a usable prefix or RegExp for @dotenv-run/core.
*
* @param {string | string[] | undefined} prefixes - The prefix or list of prefixes to filter environment variables.
* @returns {Prefix | Prefix[]} - A single prefix as a string if only one is provided, or a RegExp if multiple prefixes are given.
*
* @throws {Error} If an empty string (`''`) is included in the prefixes, as this could expose all environment variables.
*
* @example
* viteEnvPrefixToPrefix("VITE_") // Returns: "VITE_"
* viteEnvPrefixToPrefix(["VITE_", "CUSTOM_"]) // Returns: /^VITE_|CUSTOM_/
* viteEnvPrefixToPrefix(undefined) // Returns: DEFAULT_PREFIX
*
* @see {@link https://vite.dev/config/shared-options.html#envprefix Vite Documentation on envPrefix}
*
* @security
* The `envPrefix` option should **never** be set to an empty string (`''`),
* as this will expose **all** environment variables, potentially leaking sensitive information.
* Vite has a built-in safeguard that throws an error when detecting `''` as a prefix.
*
* If you need to expose an unprefixed environment variable, use the `define` option instead:
*
* ```
* define: {
* "process.env.MY_VAR": JSON.stringify(process.env.MY_VAR)
* }
* ```
*/
export const viteEnvPrefixToPrefix = (
prefixes: string | string[] | undefined
): Prefix | Prefix[] => {
prefixes = castArray(prefixes).filter(negate(isNil));

if (isEmpty(prefixes)) {
return DEFAULT_PREFIX;
}

if (prefixes.includes("")) {
throw new Error(
`envPrefix option contains value '', which could lead unexpected exposure of sensitive information.`
);
}

return prefixes;
};
23 changes: 23 additions & 0 deletions packages/vite/src/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { DotenvRunOptions } from "@dotenv-run/core";
import { pick } from "lodash-es";

/**
* Options for configuring the @dotenv-run/vite plugin.
*
* @interface ViteDotenvRunOptions
* @extends {Pick<DotenvRunOptions, 'verbose' | 'unsecure' | 'builtIn' | 'global' | 'nodeEnv' | 'runtime' | 'dotenv' | 'files'>}
*
* @property {DotenvConfigOptions} [dotenv] - Options for configuring dotenv.
* @property {string[]} [files] - Environment files to load. Defaults to `['.env.vault', '.env']`.
* @property {boolean} [unsecure] - Display environment variables in debug output.
* @property {boolean} [nodeEnv] - Node environment.
* @property {boolean} [verbose] - Print verbose output.
* @property {Dict} [builtIn] - Built-in environment variables.
* @property {boolean} [runtime] - Whether to use runtime variables.
* @property {string} [global] - Global variable name.
*/
export type ViteDotenvRunOptions = Pick<DotenvRunOptions, 'verbose' | 'unsecure' | 'builtIn' | 'global' | 'nodeEnv' | 'runtime' | 'dotenv' | 'files'>;

export const sanitizeOptions = <T extends ViteDotenvRunOptions>(options?: T): ViteDotenvRunOptions | undefined => {
return pick(options, 'verbose', 'unsecure', 'builtIn', 'global', 'nodeEnv', 'runtime', 'dotenv', 'files');
}
20 changes: 20 additions & 0 deletions packages/vite/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"include": ["src"],
"compilerOptions": {
"outDir": "dist",
"moduleResolution": "node",
"module": "ES2022",
"strict": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"strictPropertyInitialization": false,
"strictNullChecks": false,
"pretty": true,
"sourceMap": true,
"declaration": true,
"skipLibCheck": true
},
"exclude": ["node_modules"],
"compileOnSave": false,
"buildOnSave": false
}
Loading