2024-01-01 21:11:32 +00:00
|
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
2024-01-11 06:03:46 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Parses and loads environment variables from a `.env` file into the current
|
|
|
|
|
* process, or stringify data into a `.env` file format.
|
|
|
|
|
*
|
2024-08-21 05:56:34 +00:00
|
|
|
|
* Note: The key needs to match the pattern /^[a-zA-Z_][a-zA-Z0-9_]*$/.
|
|
|
|
|
*
|
2024-09-19 23:29:31 +00:00
|
|
|
|
* ```ts ignore
|
2024-06-21 04:21:26 +00:00
|
|
|
|
* // Automatically load environment variables from a `.env` file
|
|
|
|
|
* import "@std/dotenv/load";
|
|
|
|
|
* ```
|
|
|
|
|
*
|
|
|
|
|
* ```ts
|
|
|
|
|
* import { parse, stringify } from "@std/dotenv";
|
refactor(assert,async,bytes,cli,collections,crypto,csv,data-structures,datetime,dotenv,encoding,expect,fmt,front-matter,fs,html,http,ini,internal,io,json,jsonc,log,media-types,msgpack,net,path,semver,streams,testing,text,toml,ulid,url,uuid,webgpu,yaml): import from `@std/assert` (#5199)
* refactor: import from `@std/assert`
* update
2024-06-30 08:30:10 +00:00
|
|
|
|
* import { assertEquals } from "@std/assert";
|
2024-06-21 04:21:26 +00:00
|
|
|
|
*
|
|
|
|
|
* assertEquals(parse("GREETING=hello world"), { GREETING: "hello world" });
|
|
|
|
|
* assertEquals(stringify({ GREETING: "hello world" }), "GREETING='hello world'");
|
|
|
|
|
* ```
|
|
|
|
|
*
|
2024-01-11 06:03:46 +00:00
|
|
|
|
* @module
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { parse } from "./parse.ts";
|
|
|
|
|
|
|
|
|
|
export * from "./stringify.ts";
|
|
|
|
|
export * from "./parse.ts";
|
|
|
|
|
|
|
|
|
|
/** Options for {@linkcode load} and {@linkcode loadSync}. */
|
|
|
|
|
export interface LoadOptions {
|
|
|
|
|
/**
|
|
|
|
|
* Optional path to `.env` file. To prevent the default value from being
|
|
|
|
|
* used, set to `null`.
|
|
|
|
|
*
|
|
|
|
|
* @default {"./.env"}
|
|
|
|
|
*/
|
|
|
|
|
envPath?: string | null;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set to `true` to export all `.env` variables to the current processes
|
|
|
|
|
* environment. Variables are then accessible via `Deno.env.get(<key>)`.
|
|
|
|
|
*
|
|
|
|
|
* @default {false}
|
|
|
|
|
*/
|
|
|
|
|
export?: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-21 04:21:26 +00:00
|
|
|
|
/**
|
|
|
|
|
* Works identically to {@linkcode load}, but synchronously.
|
|
|
|
|
*
|
|
|
|
|
* @example Usage
|
2024-09-19 23:29:31 +00:00
|
|
|
|
* ```ts ignore
|
2024-06-21 04:21:26 +00:00
|
|
|
|
* import { loadSync } from "@std/dotenv";
|
|
|
|
|
*
|
|
|
|
|
* const conf = loadSync();
|
|
|
|
|
* ```
|
|
|
|
|
*
|
|
|
|
|
* @param options Options for loading the environment variables.
|
|
|
|
|
* @returns The parsed environment variables.
|
|
|
|
|
*/
|
2024-01-11 06:03:46 +00:00
|
|
|
|
export function loadSync(
|
2024-06-21 04:21:26 +00:00
|
|
|
|
options: LoadOptions = {},
|
|
|
|
|
): Record<string, string> {
|
|
|
|
|
const {
|
2024-01-11 06:03:46 +00:00
|
|
|
|
envPath = ".env",
|
|
|
|
|
export: _export = false,
|
2024-06-21 04:21:26 +00:00
|
|
|
|
} = options;
|
2024-01-11 06:03:46 +00:00
|
|
|
|
const conf = envPath ? parseFileSync(envPath) : {};
|
|
|
|
|
|
|
|
|
|
if (_export) {
|
2024-02-01 21:34:42 +00:00
|
|
|
|
for (const [key, value] of Object.entries(conf)) {
|
2024-01-11 06:03:46 +00:00
|
|
|
|
if (Deno.env.get(key) !== undefined) continue;
|
2024-02-01 21:34:42 +00:00
|
|
|
|
Deno.env.set(key, value);
|
2024-01-11 06:03:46 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return conf;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-04 10:34:37 +00:00
|
|
|
|
/**
|
2023-08-29 07:36:26 +00:00
|
|
|
|
* Load environment variables from a `.env` file. Loaded variables are accessible
|
|
|
|
|
* in a configuration object returned by the `load()` function, as well as optionally
|
|
|
|
|
* exporting them to the process environment using the `export` option.
|
2022-11-25 11:40:23 +00:00
|
|
|
|
*
|
2024-01-31 22:19:46 +00:00
|
|
|
|
* Inspired by the node modules {@linkcode https://github.com/motdotla/dotenv | dotenv}
|
|
|
|
|
* and {@linkcode https://github.com/motdotla/dotenv-expand | dotenv-expand}.
|
2023-08-22 23:01:46 +00:00
|
|
|
|
*
|
2024-08-21 05:56:34 +00:00
|
|
|
|
* Note: The key needs to match the pattern /^[a-zA-Z_][a-zA-Z0-9_]*$/.
|
|
|
|
|
*
|
2023-08-22 23:01:46 +00:00
|
|
|
|
* ## Basic usage
|
2022-11-25 11:40:23 +00:00
|
|
|
|
* ```sh
|
|
|
|
|
* # .env
|
|
|
|
|
* GREETING=hello world
|
|
|
|
|
* ```
|
|
|
|
|
*
|
2023-08-22 23:01:46 +00:00
|
|
|
|
* Then import the environment variables using the `load` function.
|
2022-11-25 11:40:23 +00:00
|
|
|
|
*
|
2024-06-21 04:21:26 +00:00
|
|
|
|
* @example Basic usage
|
2024-09-19 23:29:31 +00:00
|
|
|
|
* ```ts ignore
|
2022-11-25 11:40:23 +00:00
|
|
|
|
* // app.ts
|
2024-04-29 02:57:30 +00:00
|
|
|
|
* import { load } from "@std/dotenv";
|
2022-11-25 11:40:23 +00:00
|
|
|
|
*
|
2024-06-21 04:21:26 +00:00
|
|
|
|
* console.log(await load({ export: true })); // { GREETING: "hello world" }
|
2023-08-22 23:01:46 +00:00
|
|
|
|
* console.log(Deno.env.get("GREETING")); // hello world
|
2022-11-25 11:40:23 +00:00
|
|
|
|
* ```
|
|
|
|
|
*
|
2023-08-22 23:01:46 +00:00
|
|
|
|
* Run this with `deno run --allow-read --allow-env app.ts`.
|
2022-11-25 11:40:23 +00:00
|
|
|
|
*
|
2023-08-22 23:01:46 +00:00
|
|
|
|
* .env files support blank lines, comments, multi-line values and more.
|
|
|
|
|
* See Parsing Rules below for more detail.
|
2022-11-25 11:40:23 +00:00
|
|
|
|
*
|
|
|
|
|
* ## Auto loading
|
2023-08-22 23:01:46 +00:00
|
|
|
|
* Import the `load.ts` module to auto-import from the `.env` file and into
|
|
|
|
|
* the process environment.
|
2022-11-25 11:40:23 +00:00
|
|
|
|
*
|
2024-06-21 04:21:26 +00:00
|
|
|
|
* @example Auto-loading
|
2024-09-19 23:29:31 +00:00
|
|
|
|
* ```ts ignore
|
2022-11-25 11:40:23 +00:00
|
|
|
|
* // app.ts
|
2024-04-29 02:57:30 +00:00
|
|
|
|
* import "@std/dotenv/load";
|
2022-11-25 11:40:23 +00:00
|
|
|
|
*
|
2023-08-22 23:01:46 +00:00
|
|
|
|
* console.log(Deno.env.get("GREETING")); // hello world
|
2022-11-25 11:40:23 +00:00
|
|
|
|
* ```
|
|
|
|
|
*
|
2023-08-29 07:36:26 +00:00
|
|
|
|
* Run this with `deno run --allow-read --allow-env app.ts`.
|
|
|
|
|
*
|
2023-08-22 23:01:46 +00:00
|
|
|
|
* ## Files
|
|
|
|
|
* Dotenv supports a number of different files, all of which are optional.
|
2023-08-29 07:36:26 +00:00
|
|
|
|
* File names and paths are configurable.
|
2023-08-22 23:01:46 +00:00
|
|
|
|
*
|
|
|
|
|
* |File|Purpose|
|
|
|
|
|
* |----|-------|
|
|
|
|
|
* |.env|primary file for storing key-value environment entries
|
|
|
|
|
*
|
|
|
|
|
* ## Configuration
|
|
|
|
|
*
|
|
|
|
|
* Loading environment files comes with a number of options passed into
|
|
|
|
|
* the `load()` function, all of which are optional.
|
|
|
|
|
*
|
|
|
|
|
* |Option|Default|Description
|
|
|
|
|
* |------|-------|-----------
|
2023-08-29 07:36:26 +00:00
|
|
|
|
* |envPath|./.env|Path and filename of the `.env` file. Use null to prevent the .env file from being loaded.
|
2024-07-19 04:08:39 +00:00
|
|
|
|
* |export|false|When true, this will export all environment variables in the `.env` file to the process environment (e.g. for use by `Deno.env.get()`) but only if they are not already set. If a variable is already in the process, the `.env` value is ignored.
|
2023-08-22 23:01:46 +00:00
|
|
|
|
*
|
|
|
|
|
* ### Example configuration
|
2024-06-21 04:21:26 +00:00
|
|
|
|
*
|
|
|
|
|
* @example Using with options
|
2024-09-19 23:29:31 +00:00
|
|
|
|
* ```ts ignore
|
2024-04-29 02:57:30 +00:00
|
|
|
|
* import { load } from "@std/dotenv";
|
2023-08-22 23:01:46 +00:00
|
|
|
|
*
|
|
|
|
|
* const conf = await load({
|
2024-06-21 04:21:26 +00:00
|
|
|
|
* envPath: "./.env_prod", // Uses .env_prod instead of .env
|
|
|
|
|
* export: true, // Exports all variables to the environment
|
|
|
|
|
* });
|
2023-08-22 23:01:46 +00:00
|
|
|
|
* ```
|
2023-07-28 06:19:41 +00:00
|
|
|
|
*
|
2023-08-29 07:36:26 +00:00
|
|
|
|
* ## Permissions
|
|
|
|
|
*
|
|
|
|
|
* At a minimum, loading the `.env` related files requires the `--allow-read` permission. Additionally, if
|
|
|
|
|
* you access the process environment, either through exporting your configuration or expanding variables
|
|
|
|
|
* in your `.env` file, you will need the `--allow-env` permission. E.g.
|
|
|
|
|
*
|
|
|
|
|
* ```sh
|
2024-07-19 04:08:39 +00:00
|
|
|
|
* deno run --allow-read=.env --allow-env=ENV1,ENV2 app.ts
|
2023-08-29 07:36:26 +00:00
|
|
|
|
* ```
|
|
|
|
|
*
|
2022-11-25 11:40:23 +00:00
|
|
|
|
* ## Parsing Rules
|
|
|
|
|
*
|
|
|
|
|
* The parsing engine currently supports the following rules:
|
|
|
|
|
*
|
|
|
|
|
* - Variables that already exist in the environment are not overridden with
|
|
|
|
|
* `export: true`
|
|
|
|
|
* - `BASIC=basic` becomes `{ BASIC: "basic" }`
|
|
|
|
|
* - empty lines are skipped
|
|
|
|
|
* - lines beginning with `#` are treated as comments
|
|
|
|
|
* - empty values become empty strings (`EMPTY=` becomes `{ EMPTY: "" }`)
|
|
|
|
|
* - single and double quoted values are escaped (`SINGLE_QUOTE='quoted'` becomes
|
|
|
|
|
* `{ SINGLE_QUOTE: "quoted" }`)
|
|
|
|
|
* - new lines are expanded in double quoted values (`MULTILINE="new\nline"`
|
|
|
|
|
* becomes
|
|
|
|
|
*
|
|
|
|
|
* ```
|
|
|
|
|
* { MULTILINE: "new\nline" }
|
|
|
|
|
* ```
|
|
|
|
|
*
|
|
|
|
|
* - inner quotes are maintained (think JSON) (`JSON={"foo": "bar"}` becomes
|
|
|
|
|
* `{ JSON: "{\"foo\": \"bar\"}" }`)
|
|
|
|
|
* - whitespace is removed from both ends of unquoted values (see more on
|
2024-01-31 22:19:46 +00:00
|
|
|
|
* {@linkcode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim | trim})
|
2022-11-25 11:40:23 +00:00
|
|
|
|
* (`FOO= some value` becomes `{ FOO: "some value" }`)
|
|
|
|
|
* - whitespace is preserved on both ends of quoted values (`FOO=" some value "`
|
|
|
|
|
* becomes `{ FOO: " some value " }`)
|
|
|
|
|
* - dollar sign with an environment key in or without curly braces in unquoted
|
|
|
|
|
* values will expand the environment key (`KEY=$KEY` or `KEY=${KEY}` becomes
|
|
|
|
|
* `{ KEY: "<KEY_VALUE_FROM_ENV>" }`)
|
|
|
|
|
* - escaped dollar sign with an environment key in unquoted values will escape the
|
|
|
|
|
* environment key rather than expand (`KEY=\$KEY` becomes `{ KEY: "\\$KEY" }`)
|
|
|
|
|
* - colon and a minus sign with a default value(which can also be another expand
|
|
|
|
|
* value) in expanding construction in unquoted values will first attempt to
|
|
|
|
|
* expand the environment key. If it’s not found, then it will return the default
|
|
|
|
|
* value (`KEY=${KEY:-default}` If KEY exists it becomes
|
|
|
|
|
* `{ KEY: "<KEY_VALUE_FROM_ENV>" }` If not, then it becomes
|
|
|
|
|
* `{ KEY: "default" }`. Also there is possible to do this case
|
|
|
|
|
* `KEY=${NO_SUCH_KEY:-${EXISTING_KEY:-default}}` which becomes
|
|
|
|
|
* `{ KEY: "<EXISTING_KEY_VALUE_FROM_ENV>" }`)
|
2024-06-21 04:21:26 +00:00
|
|
|
|
*
|
|
|
|
|
* @param options The options
|
|
|
|
|
* @returns The parsed environment variables
|
2022-05-04 10:34:37 +00:00
|
|
|
|
*/
|
2022-12-05 06:57:10 +00:00
|
|
|
|
export async function load(
|
2024-06-21 04:21:26 +00:00
|
|
|
|
options: LoadOptions = {},
|
|
|
|
|
): Promise<Record<string, string>> {
|
|
|
|
|
const {
|
2022-12-05 06:57:10 +00:00
|
|
|
|
envPath = ".env",
|
|
|
|
|
export: _export = false,
|
2024-06-21 04:21:26 +00:00
|
|
|
|
} = options;
|
2023-08-29 07:36:26 +00:00
|
|
|
|
const conf = envPath ? await parseFile(envPath) : {};
|
2022-12-05 06:57:10 +00:00
|
|
|
|
|
|
|
|
|
if (_export) {
|
2024-02-01 21:34:42 +00:00
|
|
|
|
for (const [key, value] of Object.entries(conf)) {
|
2022-02-23 08:49:15 +00:00
|
|
|
|
if (Deno.env.get(key) !== undefined) continue;
|
2024-02-01 21:34:42 +00:00
|
|
|
|
Deno.env.set(key, value);
|
2022-02-23 08:49:15 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return conf;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-07 16:51:21 +00:00
|
|
|
|
function parseFileSync(
|
|
|
|
|
filepath: string,
|
|
|
|
|
): Record<string, string> {
|
2022-02-23 08:49:15 +00:00
|
|
|
|
try {
|
2023-08-29 07:36:26 +00:00
|
|
|
|
return parse(Deno.readTextFileSync(filepath));
|
2022-02-23 08:49:15 +00:00
|
|
|
|
} catch (e) {
|
|
|
|
|
if (e instanceof Deno.errors.NotFound) return {};
|
|
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-07 16:51:21 +00:00
|
|
|
|
async function parseFile(
|
2022-09-06 12:28:51 +00:00
|
|
|
|
filepath: string,
|
2022-12-07 16:51:21 +00:00
|
|
|
|
): Promise<Record<string, string>> {
|
2022-02-23 08:49:15 +00:00
|
|
|
|
try {
|
2023-08-29 07:36:26 +00:00
|
|
|
|
return parse(await Deno.readTextFile(filepath));
|
2022-02-23 08:49:15 +00:00
|
|
|
|
} catch (e) {
|
|
|
|
|
if (e instanceof Deno.errors.NotFound) return {};
|
|
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
}
|