dotenv-expand
Advanced tools
Comparing version 10.0.0 to 11.0.0
@@ -5,4 +5,23 @@ # Changelog | ||
## [Unreleased](https://github.com/motdotla/dotenv-expand/compare/v10.0.0...master) | ||
## [Unreleased](https://github.com/motdotla/dotenv-expand/compare/v11.0.0...master) | ||
## [11.0.0](https://github.com/motdotla/dotenv-expand/compare/v10.0.0...v11.0.0) (2024-02-10) | ||
### Added | ||
- Add typings for `import dotenv-expand/config` ([#99](https://github.com/motdotla/dotenv-expand/pull/99)) | ||
- Support expansion of dot in env variable names like `POSTGRESQL.BASE.USER` ([#93](https://github.com/motdotla/dotenv-expand/pull/93)) | ||
- Add `processEnv` option ([#105](https://github.com/motdotla/dotenv-expand/pull/105)) | ||
- Add support for default format of `${VAR-default}` ([#109](https://github.com/motdotla/dotenv-expand/pull/109)) | ||
### Changed | ||
- Do not expand prior `process.env` environment variables. NOTE: make sure to see updated README regarding `dotenv.config({ processEnv: {} })` ([#104](https://github.com/motdotla/dotenv-expand/pull/104)) | ||
- 🐞 handle `$var1$var2` ([#103](https://github.com/motdotla/dotenv-expand/issues/103), [#104](https://github.com/motdotla/dotenv-expand/pull/104)) | ||
- 🐞 fix fatal recursive error when variable defines value with same variable `VAR=$VAR` [#98](https://github.com/motdotla/dotenv-expand/issues/98) | ||
### Removed | ||
- Remove `ignoreProcessEnv` option (use `processEnv` option going forward) | ||
## [10.0.0](https://github.com/motdotla/dotenv-expand/compare/v9.0.0...v10.0.0) (2022-12-16) | ||
@@ -9,0 +28,0 @@ |
// TypeScript Version: 3.0 | ||
/// <reference types="node" /> | ||
export interface DotenvPopulateInput { | ||
[name: string]: string; | ||
} | ||
export interface DotenvParseInput { | ||
[name: string]: string; | ||
} | ||
export interface DotenvParseOutput { | ||
[name: string]: string; | ||
} | ||
export interface DotenvExpandOptions { | ||
ignoreProcessEnv?: boolean; | ||
error?: Error; | ||
parsed?: { | ||
[name: string]: string; | ||
} | ||
/** | ||
* Default: `process.env` | ||
* | ||
* Specify an object to write your secrets to. Defaults to process.env environment variables. | ||
* | ||
* example: `const processEnv = {}; require('dotenv').config({ processEnv: processEnv })` | ||
*/ | ||
processEnv?: DotenvPopulateInput; | ||
/** | ||
* Default: `object` | ||
* | ||
* Object coming from dotenv's parsed result. | ||
*/ | ||
parsed?: DotenvParseInput; | ||
} | ||
export interface DotenvExpandOutput { | ||
ignoreProcessEnv?: boolean; | ||
error?: Error; | ||
parsed?: { | ||
[name: string]: string; | ||
}; | ||
parsed?: DotenvParseOutput; | ||
} | ||
@@ -25,3 +46,3 @@ | ||
* | ||
* @param options - additional options. example: `{ ignoreProcessEnv: false, error: null, parsed: { { KEY: 'value' } }` | ||
* @param options - additional options. example: `{ processEnv: {}, error: null, parsed: { { KEY: 'value' } }` | ||
* @returns an object with a `parsed` key if successful or `error` key if an error occurred. example: { parsed: { KEY: 'value' } } | ||
@@ -28,0 +49,0 @@ * |
108
lib/main.js
'use strict' | ||
// like String.prototype.search but returns the last index | ||
function _searchLast (str, rgx) { | ||
const matches = Array.from(str.matchAll(rgx)) | ||
return matches.length > 0 ? matches.slice(-1)[0].index : -1 | ||
} | ||
// * / | ||
// * (\\)? # is it escaped with a backslash? | ||
// * (\$) # literal $ | ||
// * (?!\() # shouldnt be followed by parenthesis | ||
// * (\{?) # first brace wrap opening | ||
// * ([\w.]+) # key | ||
// * (?::-((?:\$\{(?:\$\{(?:\$\{[^}]*\}|[^}])*}|[^}])*}|[^}])+))? # optional default nested 3 times | ||
// * (\}?) # last brace warp closing | ||
// * /xi | ||
function _interpolate (envValue, environment, config) { | ||
// find the last unescaped dollar sign in the | ||
// value so that we can evaluate it | ||
const lastUnescapedDollarSignIndex = _searchLast(envValue, /(?!(?<=\\))\$/g) | ||
const DOTENV_SUBSTITUTION_REGEX = /(\\)?(\$)(?!\()(\{?)([\w.]+)(?::?-((?:\$\{(?:\$\{(?:\$\{[^}]*\}|[^}])*}|[^}])*}|[^}])+))?(\}?)/gi | ||
// If we couldn't match any unescaped dollar sign | ||
// let's return the string as is | ||
if (lastUnescapedDollarSignIndex === -1) return envValue | ||
function _resolveEscapeSequences (value) { | ||
return value.replace(/\\\$/g, '$') | ||
} | ||
// This is the right-most group of variables in the string | ||
const rightMostGroup = envValue.slice(lastUnescapedDollarSignIndex) | ||
function interpolate (value, processEnv, parsed) { | ||
return value.replace(DOTENV_SUBSTITUTION_REGEX, (match, escaped, dollarSign, openBrace, key, defaultValue, closeBrace) => { | ||
if (escaped === '\\') { | ||
return match.slice(1) | ||
} else { | ||
if (processEnv[key]) { | ||
return processEnv[key] | ||
} | ||
/** | ||
* This finds the inner most variable/group divided | ||
* by variable name and default value (if present) | ||
* ( | ||
* (?!(?<=\\))\$ // only match dollar signs that are not escaped | ||
* {? // optional opening curly brace | ||
* ([\w]+) // match the variable name | ||
* (?::-([^}\\]*))? // match an optional default value | ||
* }? // optional closing curly brace | ||
* ) | ||
*/ | ||
const matchGroup = /((?!(?<=\\))\${?([\w]+)(?::-([^}\\]*))?}?)/ | ||
const match = rightMostGroup.match(matchGroup) | ||
if (defaultValue) { | ||
if (defaultValue.startsWith('$')) { | ||
return interpolate(defaultValue, processEnv, parsed) | ||
} else { | ||
return defaultValue | ||
} | ||
} | ||
if (match != null) { | ||
const [, group, variableName, defaultValue] = match | ||
return parsed[key] || '' | ||
} | ||
}) | ||
} | ||
return _interpolate( | ||
envValue.replace( | ||
group, | ||
environment[variableName] || | ||
defaultValue || | ||
config.parsed[variableName] || | ||
'' | ||
), | ||
environment, | ||
config | ||
) | ||
function expand (options) { | ||
let processEnv = process.env | ||
if (options && options.processEnv != null) { | ||
processEnv = options.processEnv | ||
} | ||
return envValue | ||
} | ||
for (const key in options.parsed) { | ||
let value = options.parsed[key] | ||
function _resolveEscapeSequences (value) { | ||
return value.replace(/\\\$/g, '$') | ||
} | ||
// don't interpolate if it exists already in processEnv | ||
if (Object.prototype.hasOwnProperty.call(processEnv, key)) { | ||
value = processEnv[key] | ||
} else { | ||
value = interpolate(value, processEnv, options.parsed) | ||
} | ||
function expand (config) { | ||
// if ignoring process.env, use a blank object | ||
const environment = config.ignoreProcessEnv ? {} : process.env | ||
for (const configKey in config.parsed) { | ||
const value = Object.prototype.hasOwnProperty.call(environment, configKey) | ||
? environment[configKey] | ||
: config.parsed[configKey] | ||
config.parsed[configKey] = _resolveEscapeSequences( | ||
_interpolate(value, environment, config) | ||
) | ||
options.parsed[key] = _resolveEscapeSequences(value) | ||
} | ||
for (const processKey in config.parsed) { | ||
environment[processKey] = config.parsed[processKey] | ||
for (const processKey in options.parsed) { | ||
processEnv[processKey] = options.parsed[processKey] | ||
} | ||
return config | ||
return options | ||
} | ||
module.exports.expand = expand |
{ | ||
"name": "dotenv-expand", | ||
"version": "10.0.0", | ||
"version": "11.0.0", | ||
"description": "Expand environment variables using dotenv", | ||
@@ -21,3 +21,5 @@ "main": "lib/main.js", | ||
"pretest": "npm run lint && npm run dts-check", | ||
"test": "lab tests --coverage" | ||
"test": "tap tests/*.js --100 -Rspec", | ||
"prerelease": "npm test", | ||
"release": "standard-version" | ||
}, | ||
@@ -39,8 +41,6 @@ "repository": { | ||
"devDependencies": { | ||
"@hapi/lab": "^24.5.1", | ||
"@types/node": "^17.0.8", | ||
"dotenv": "16.0.3", | ||
"lab": "^14.3.4", | ||
"should": "^11.2.1", | ||
"@types/node": "^18.11.3", | ||
"standard": "^16.0.4", | ||
"standard-version": "^9.5.0", | ||
"tap": "^16.3.0", | ||
"typescript": "^4.5.4" | ||
@@ -50,3 +50,6 @@ }, | ||
"node": ">=12" | ||
}, | ||
"dependencies": { | ||
"dotenv": "^16.4.1" | ||
} | ||
} |
<div align="center"> | ||
🎉 announcing <a href="https://github.com/dotenvx/dotenvx">dotenvx</a>. <em>run anywhere, multi-environment, encrypted envs</em>. | ||
</div> | ||
| ||
<div align="center"> | ||
<p> | ||
@@ -21,19 +27,19 @@ <sup> | ||
<br> | ||
<a href="https://retool.com/?utm_source=sponsor&utm_campaign=dotenv"> | ||
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=dotenv&utm_source=github"> | ||
<div> | ||
<img src="https://res.cloudinary.com/dotenv-org/image/upload/c_scale,w_300/v1664466968/logo-full-black_vidfqf.png" width="270" alt="Retool"> | ||
<img src="https://res.cloudinary.com/dotenv-org/image/upload/c_scale,w_400/v1665605496/68747470733a2f2f73696e647265736f726875732e636f6d2f6173736574732f7468616e6b732f776f726b6f732d6c6f676f2d77686974652d62672e737667_zdmsbu.svg" width="270" alt="WorkOS"> | ||
</div> | ||
<b>Retool helps developers build custom internal software, like CRUD apps and admin panels, really fast.</b> | ||
<b>Your App, Enterprise Ready.</b> | ||
<div> | ||
<sup>Build UIs visually with flexible components, connect to any data source, and write business logic in JavaScript.</sup> | ||
<sup>Add Single Sign-On, Multi-Factor Auth, and more, in minutes instead of months.</sup> | ||
</div> | ||
</a> | ||
<br> | ||
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=dotenv&utm_source=github"> | ||
<br/> | ||
<a href="https://runalloy.com/?utm_source=github&utm_medium=referral&utm_campaign=1224_dotenv"> | ||
<div> | ||
<img src="https://res.cloudinary.com/dotenv-org/image/upload/c_scale,w_400/v1665605496/68747470733a2f2f73696e647265736f726875732e636f6d2f6173736574732f7468616e6b732f776f726b6f732d6c6f676f2d77686974652d62672e737667_zdmsbu.svg" width="270" alt="WorkOS"> | ||
<img src="https://res.cloudinary.com/dotenv-org/image/upload/c_crop,g_center,h_65,w_290,x_0,y_0/v1704258787/AlloyAutomation-logo_dqin8c.svg" width="370" alt="Alloy Automation"> | ||
</div> | ||
<b>Your App, Enterprise Ready.</b> | ||
<b>Launch user-facing integrations faster</b> | ||
<div> | ||
<sup>Add Single Sign-On, Multi-Factor Auth, and more, in minutes instead of months.</sup> | ||
<sup>Easily spin up hundreds of integrations. Sign up free or read our docs first</sup> | ||
</div> | ||
@@ -90,3 +96,3 @@ </a> | ||
var myEnv = dotenv.config() | ||
var myEnv = dotenv.config({ processEnv: {} }) // important to set processEnv: {}, otherwise expansion will be attempted on your already existing machine envs | ||
dotenvExpand.expand(myEnv) | ||
@@ -154,11 +160,12 @@ | ||
##### ignoreProcessEnv | ||
##### processEnv | ||
Default: `false` | ||
Default: `process.env` | ||
Turn off writing to `process.env`. | ||
Specify an object to write your secrets to. Defaults to `process.env` environment variables. | ||
```js | ||
const myObject = {} | ||
const dotenv = { | ||
ignoreProcessEnv: true, | ||
processEnv: myObject, | ||
parsed: { | ||
@@ -171,2 +178,3 @@ SHOULD_NOT_EXIST: 'testing' | ||
console.log(obj.SHOULD_NOT_EXIST) // testing | ||
console.log(myObject.SHOULD_NOT_EXIST) // testing | ||
console.log(process.env.SHOULD_NOT_EXIST) // undefined | ||
@@ -173,0 +181,0 @@ ``` |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
22573
5
15
108
203
1
+ Addeddotenv@^16.4.1
+ Addeddotenv@16.4.5(transitive)