dotenv
Advanced tools
Comparing version 14.3.2 to 15.0.0
@@ -5,4 +5,22 @@ # Changelog | ||
## [Unreleased](https://github.com/motdotla/dotenv/compare/v14.3.2...master) | ||
## [Unreleased](https://github.com/motdotla/dotenv/compare/v15.0.0...master) | ||
## [15.0.0](https://github.com/motdotla/dotenv/compare/v14.3.2...v15.0.0) (2022-01-31) | ||
`v15.0.0` is a major new release with some important breaking changes. | ||
### Added | ||
- _Breaking:_ Multiline parsing support (just works. no need for the flag.) | ||
### Changed | ||
- _Breaking:_ `#` marks the beginning of a comment (UNLESS the value is wrapped in quotes. Please update your `.env` files to wrap in quotes any values containing `#`. For example: `SECRET_HASH="something-with-a-#-hash"`). | ||
..Understandably, (as some teams have noted) this is tedious to do across the entire team. To make it less tedious, we recommend using [dotenv cli](https://github.com/dotenv-org/cli) going forward. It's an optional plugin that will keep your `.env` files in sync between machines, environments, or team members. | ||
### Removed | ||
- _Breaking:_ Remove multiline option (just works out of the box now. no need for the flag.) | ||
## [14.3.2](https://github.com/motdotla/dotenv/compare/v14.3.1...v14.3.2) (2022-01-25) | ||
@@ -9,0 +27,0 @@ |
@@ -1,2 +0,2 @@ | ||
const re = /^dotenv_config_(encoding|path|debug|override|multiline)=(.+)$/ | ||
const re = /^dotenv_config_(encoding|path|debug|override)=(.+)$/ | ||
@@ -3,0 +3,0 @@ module.exports = function optionMatcher (args) { |
@@ -20,6 +20,2 @@ // ../config.js accepts options via environment variables | ||
if (process.env.DOTENV_CONFIG_MULTILINE != null) { | ||
options.multiline = process.env.DOTENV_CONFIG_MULTILINE | ||
} | ||
module.exports = options |
// TypeScript Version: 3.0 | ||
/// <reference types="node" /> | ||
export interface DotenvParseOptions { | ||
/** | ||
* Default: `false` | ||
* | ||
* Turn on logging to help debug why certain keys or values are not being set as you expect. | ||
* | ||
* example: `dotenv.parse('KEY=value', { debug: true })` | ||
*/ | ||
debug?: boolean; | ||
/** | ||
* Default: `false` | ||
* | ||
* Turn on multiline line break parsing. | ||
* | ||
* example: | ||
* | ||
* MY_VAR="this | ||
* is | ||
* a | ||
* multiline | ||
* string" | ||
*/ | ||
multiline?: boolean; | ||
} | ||
export interface DotenvParseOutput { | ||
@@ -44,4 +18,3 @@ [name: string]: string; | ||
export function parse<T extends DotenvParseOutput = DotenvParseOutput>( | ||
src: string | Buffer, | ||
options?: DotenvParseOptions | ||
src: string | Buffer | ||
): T; | ||
@@ -85,17 +58,2 @@ | ||
override?: boolean; | ||
/** | ||
* Default: `false` | ||
* | ||
* Turn on multiline line break parsing. | ||
* | ||
* example: | ||
* | ||
* MY_VAR="this | ||
* is | ||
* a | ||
* multiline | ||
* string" | ||
*/ | ||
multiline?: boolean; | ||
} | ||
@@ -113,3 +71,3 @@ | ||
* | ||
* @param options - additional options. example: `{ path: './custom/path', encoding: 'latin1', debug: true, override: false, multiline: false }` | ||
* @param options - additional options. example: `{ path: './custom/path', encoding: 'latin1', debug: true, override: false }` | ||
* @returns an object with a `parsed` key if successful or `error` key if an error occurred. example: { parsed: { KEY: 'value' } } | ||
@@ -116,0 +74,0 @@ * |
105
lib/main.js
@@ -5,74 +5,38 @@ const fs = require('fs') | ||
function log (message) { | ||
console.log(`[dotenv][DEBUG] ${message}`) | ||
} | ||
const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg | ||
const NEWLINE = '\n' | ||
const RE_INI_KEY_VAL = /^\s*([\w.-]+)\s*=\s*("[^"]*"|'[^']*'|.*?)(\s+#.*)?$/ | ||
const RE_NEWLINES = /\\n/g | ||
const NEWLINES_MATCH = /\r\n|\n|\r/ | ||
// Parses src into an Object | ||
function parse (src, options) { | ||
const debug = Boolean(options && options.debug) | ||
const multiline = Boolean(options && options.multiline) | ||
// Parser src into an Object | ||
function parse (src) { | ||
const obj = {} | ||
// convert Buffers before splitting into lines and processing | ||
const lines = src.toString().split(NEWLINES_MATCH) | ||
// Convert buffer to string | ||
let lines = src.toString() | ||
for (let idx = 0; idx < lines.length; idx++) { | ||
let line = lines[idx] | ||
// Convert line breaks to same format | ||
lines = lines.replace(/\r\n?/mg, '\n') | ||
// matching "KEY' and 'VAL' in 'KEY=VAL' | ||
const keyValueArr = line.match(RE_INI_KEY_VAL) | ||
// matched? | ||
if (keyValueArr != null) { | ||
const key = keyValueArr[1] | ||
// default undefined or missing values to empty string | ||
let val = (keyValueArr[2] || '') | ||
let end = val.length - 1 | ||
const isDoubleQuoted = val[0] === '"' && val[end] === '"' | ||
const isSingleQuoted = val[0] === "'" && val[end] === "'" | ||
let match | ||
while ((match = LINE.exec(lines)) != null) { | ||
const key = match[1] | ||
const isMultilineDoubleQuoted = val[0] === '"' && val[end] !== '"' | ||
const isMultilineSingleQuoted = val[0] === "'" && val[end] !== "'" | ||
// Default undefined or null to empty string | ||
let value = (match[2] || '') | ||
// if parsing line breaks and the value starts with a quote | ||
if (multiline && (isMultilineDoubleQuoted || isMultilineSingleQuoted)) { | ||
const quoteChar = isMultilineDoubleQuoted ? '"' : "'" | ||
// Remove whitespace | ||
value = value.trim() | ||
val = val.substring(1) | ||
// Check if double quoted | ||
const maybeQuote = value[0] | ||
while (idx++ < lines.length - 1) { | ||
line = lines[idx] | ||
end = line.length - 1 | ||
if (line[end] === quoteChar) { | ||
val += NEWLINE + line.substring(0, end) | ||
break | ||
} | ||
val += NEWLINE + line | ||
} | ||
// if single or double quoted, remove quotes | ||
} else if (isSingleQuoted || isDoubleQuoted) { | ||
val = val.substring(1, end) | ||
// Remove surrounding quotes | ||
value = value.replace(/^(['"])([\s\S]+)\1$/mg, '$2') | ||
// if double quoted, expand newlines | ||
if (isDoubleQuoted) { | ||
val = val.replace(RE_NEWLINES, NEWLINE) | ||
} | ||
} else { | ||
// remove surrounding whitespace | ||
val = val.trim() | ||
} | ||
// Expand newlines if double quoted | ||
if (maybeQuote === '"') { | ||
value = value.replace(/\\n/g, '\n') | ||
value = value.replace(/\\r/g, '\r') | ||
} | ||
obj[key] = val | ||
} else if (debug) { | ||
const trimmedLine = line.trim() | ||
// ignore empty and commented lines | ||
if (trimmedLine.length && trimmedLine[0] !== '#') { | ||
log(`Failed to match key and value when parsing line ${idx + 1}: ${line}`) | ||
} | ||
} | ||
// Add to object | ||
obj[key] = value | ||
} | ||
@@ -83,3 +47,7 @@ | ||
function resolveHome (envPath) { | ||
function _log (message) { | ||
console.log(`[dotenv][DEBUG] ${message}`) | ||
} | ||
function _resolveHome (envPath) { | ||
return envPath[0] === '~' ? path.join(os.homedir(), envPath.slice(1)) : envPath | ||
@@ -94,7 +62,6 @@ } | ||
const override = Boolean(options && options.override) | ||
const multiline = Boolean(options && options.multiline) | ||
if (options) { | ||
if (options.path != null) { | ||
dotenvPath = resolveHome(options.path) | ||
dotenvPath = _resolveHome(options.path) | ||
} | ||
@@ -107,4 +74,4 @@ if (options.encoding != null) { | ||
try { | ||
// specifying an encoding returns a string instead of a buffer | ||
const parsed = DotenvModule.parse(fs.readFileSync(dotenvPath, { encoding }), { debug, multiline }) | ||
// Specifying an encoding returns a string instead of a buffer | ||
const parsed = DotenvModule.parse(fs.readFileSync(dotenvPath, { encoding })) | ||
@@ -121,5 +88,5 @@ Object.keys(parsed).forEach(function (key) { | ||
if (override === true) { | ||
log(`"${key}" is already defined in \`process.env\` and WAS overwritten`) | ||
_log(`"${key}" is already defined in \`process.env\` and WAS overwritten`) | ||
} else { | ||
log(`"${key}" is already defined in \`process.env\` and was NOT overwritten`) | ||
_log(`"${key}" is already defined in \`process.env\` and was NOT overwritten`) | ||
} | ||
@@ -133,3 +100,3 @@ } | ||
if (debug) { | ||
log(`Failed to load ${dotenvPath} ${e.message}`) | ||
_log(`Failed to load ${dotenvPath} ${e.message}`) | ||
} | ||
@@ -136,0 +103,0 @@ |
{ | ||
"name": "dotenv", | ||
"version": "14.3.2", | ||
"version": "15.0.0", | ||
"description": "Loads environment variables from .env file", | ||
@@ -5,0 +5,0 @@ "main": "lib/main.js", |
177
README.md
@@ -31,22 +31,13 @@ <p align="center"> | ||
Usage is easy! | ||
Create a `.env` file in the root of your project: | ||
### 1. Create a `.env` file in the **root directory** of your project. | ||
```dosini | ||
# .env file | ||
# | ||
# Add environment-specific variables on new lines in the form of NAME=VALUE | ||
# | ||
DB_HOST=localhost | ||
DB_USER=root | ||
DB_PASS=s1mpl3 | ||
S3_BUCKET="YOURS3BUCKET" | ||
SECRET_KEY="YOURSECRETKEYGOESHERE" | ||
``` | ||
### 2. As early as possible in your application, import and configure dotenv. | ||
As early as possible in your application, import and configure dotenv: | ||
```javascript | ||
// index.js | ||
require('dotenv').config() | ||
console.log(process.env) // remove this after you've confirmed it working | ||
@@ -58,3 +49,2 @@ ``` | ||
```javascript | ||
// index.mjs (ESM) | ||
import 'dotenv/config' // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import | ||
@@ -64,6 +54,4 @@ import express from 'express' | ||
### 3. That's it! 🎉 | ||
That's it. `process.env` now has the keys and values you defined in your `.env` file: | ||
`process.env` now has the keys and values you defined in your `.env` file. | ||
```javascript | ||
@@ -74,10 +62,78 @@ require('dotenv').config() | ||
const db = require('db') | ||
db.connect({ | ||
host: process.env.DB_HOST, | ||
username: process.env.DB_USER, | ||
password: process.env.DB_PASS | ||
}) | ||
s3.getBucketCors({Bucket: process.env.S3_BUCKET}, function(err, data) {}) | ||
``` | ||
### Multiline values | ||
If you need multiline variables, for example private keys, those are now supported (`>= v15.0.0`) with line breaks: | ||
```dosini | ||
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY----- | ||
... | ||
Kh9NV... | ||
... | ||
-----END DSA PRIVATE KEY-----" | ||
``` | ||
Alternatively, you can double quote strings and use the `\n` character: | ||
```dosini | ||
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\Kh9NV...\n-----END DSA PRIVATE KEY-----\n" | ||
``` | ||
### Comments | ||
Comments may be added to your file on their own line or inline: | ||
```dosini | ||
# This is a comment | ||
SECRET_KEY=YOURSECRETKEYGOESHERE # comment | ||
SECRET_HASH="something-with-a-#-hash" | ||
``` | ||
Comments begin where a `#` exists, so if your value contains a `#` please wrap it in quotes. This is a breaking change from `>= v15.0.0` and on. | ||
### Parsing | ||
The engine which parses the contents of your file containing environment variables is available to use. It accepts a String or Buffer and will return an Object with the parsed keys and values. | ||
```javascript | ||
const dotenv = require('dotenv') | ||
const buf = Buffer.from('BASIC=basic') | ||
const config = dotenv.parse(buf) // will return an object | ||
console.log(typeof config, config) // object { BASIC : 'basic' } | ||
``` | ||
### Preload | ||
You can use the `--require` (`-r`) [command line option](https://nodejs.org/api/cli.html#cli_r_require_module) to preload dotenv. By doing this, you do not need to require and load dotenv in your application code. | ||
```bash | ||
$ node -r dotenv/config your_script.js | ||
``` | ||
The configuration options below are supported as command line arguments in the format `dotenv_config_<option>=value` | ||
```bash | ||
$ node -r dotenv/config your_script.js dotenv_config_path=/custom/path/to/.env dotenv_config_debug=true | ||
``` | ||
Additionally, you can use environment variables to set configuration options. Command line arguments will precede these. | ||
```bash | ||
$ DOTENV_CONFIG_<OPTION>=value node -r dotenv/config your_script.js | ||
``` | ||
```bash | ||
$ DOTENV_CONFIG_ENCODING=latin1 DOTENV_CONFIG_DEBUG=true node -r dotenv/config your_script.js dotenv_config_path=/custom/path/to/.env | ||
``` | ||
### Variable Expansion | ||
You need to add the value of another variable in one of your variables? Use [dotenv-expand](https://github.com/motdotla/dotenv-expand). | ||
### Syncing | ||
You need to keep `.env` files in sync between machines, environments, or team members? Use [dotenv cli](https://github.com/dotenv-org/cli). | ||
## Examples | ||
@@ -101,2 +157,3 @@ | ||
* [express](https://github.com/dotenv-org/examples/tree/master/dotenv-express) | ||
* [nestjs](https://github.com/dotenv-org/examples/tree/master/dotenv-nestjs) | ||
@@ -170,23 +227,2 @@ ## Documentation | ||
##### Multiline | ||
Default: `false` | ||
Turn on multiline line break parsing. | ||
```js | ||
require('dotenv').config({ multiline: true }) | ||
``` | ||
This allows specifying multiline values in this format: | ||
``` | ||
PRIVATE_KEY="-----BEGIN PRIVATE KEY----- | ||
MIGT... | ||
7ure... | ||
-----END PRIVATE KEY-----" | ||
``` | ||
Ensure that the value begins with a single or double quote character, and it ends with the same character. | ||
### Parse | ||
@@ -221,47 +257,4 @@ | ||
##### Multiline | ||
Default: `false` | ||
Turn on multiline line break parsing. | ||
```js | ||
require('dotenv').config({ multiline: true }) | ||
``` | ||
This allows specifying multiline values in this format: | ||
``` | ||
PRIVATE_KEY="-----BEGIN PRIVATE KEY----- | ||
MIGT... | ||
7ure... | ||
-----END PRIVATE KEY-----" | ||
``` | ||
## Other Usage | ||
### Preload | ||
You can use the `--require` (`-r`) [command line option](https://nodejs.org/api/cli.html#cli_r_require_module) to preload dotenv. By doing this, you do not need to require and load dotenv in your application code. This is the preferred approach when using `import` instead of `require`. | ||
```bash | ||
$ node -r dotenv/config your_script.js | ||
``` | ||
The configuration options below are supported as command line arguments in the format `dotenv_config_<option>=value` | ||
```bash | ||
$ node -r dotenv/config your_script.js dotenv_config_path=/custom/path/to/.env dotenv_config_debug=true | ||
``` | ||
Additionally, you can use environment variables to set configuration options. Command line arguments will precede these. | ||
```bash | ||
$ DOTENV_CONFIG_<OPTION>=value node -r dotenv/config your_script.js | ||
``` | ||
```bash | ||
$ DOTENV_CONFIG_ENCODING=latin1 DOTENV_CONFIG_DEBUG=true node -r dotenv/config your_script.js dotenv_config_path=/custom/path/to/.env | ||
``` | ||
## FAQ | ||
@@ -303,3 +296,3 @@ | ||
- lines beginning with `#` are treated as comments | ||
- whitespace followed by `#` marks the beginning of an inline comment (unless when the value is wrapped in quotes) | ||
- `#` marks the beginning of a comment (unless when the value is wrapped in quotes) | ||
- empty values become empty strings (`EMPTY=` becomes `{EMPTY: ''}`) | ||
@@ -329,4 +322,8 @@ - inner quotes are maintained (think JSON) (`JSON={"foo": "bar"}` becomes `{JSON:"{\"foo\": \"bar\"}"`) | ||
React has dotenv built in but with a quirk. Preface your environment variables with `REACT_APP_`. See [this stack overflow](https://stackoverflow.com/questions/42182577/is-it-possible-to-use-dotenv-in-a-react-project) for more details. | ||
Your React code is run in Webpack, where the `fs` module or even the `process` global itself are not accessible out-of-the-box. `process.env` can only be injected through Webpack configuration. | ||
If you are using [`react-scripts`](https://www.npmjs.com/package/react-scripts), which is distributed through [`create-react-app`](https://create-react-app.dev/), it has dotenv built in but with a quirk. Preface your environment variables with `REACT_APP_`. See [this stack overflow](https://stackoverflow.com/questions/42182577/is-it-possible-to-use-dotenv-in-a-react-project) for more details. | ||
If you are using other frameworks (e.g. Next.js, Gatsby...), you need to consult their documentation for how to inject environment variables into the client. | ||
### Can I customize/write plugins for dotenv? | ||
@@ -394,3 +391,3 @@ | ||
There are also 2 alternatives to this approach: | ||
There are two alternatives to this approach: | ||
@@ -397,0 +394,0 @@ 1. Preload dotenv: `node --require dotenv/config index.js` (_Note: you do not need to `import` dotenv with this approach_) |
33402
186
408