@dotenvx/dotenvx 1.22.2 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,7 +2,13 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
- ## [Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.22.2...main)
5
+ ## [Unreleased](https://github.com/dotenvx/dotenvx/compare/v1.23.0...main)
6
+
7
+ ## 1.23.0
8
+
9
+ ### Added
10
+
11
+ * deeper variable expansion support and protection against self-referencing variables 🛡️ ([#439](https://github.com/dotenvx/dotenvx/pull/439))
6
12
 
7
13
  ## 1.22.2
8
14
 
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.22.2",
2
+ "version": "1.23.0",
3
3
  "name": "@dotenvx/dotenvx",
4
4
  "description": "a better dotenv–from the creator of `dotenv`",
5
5
  "author": "@motdotla",
@@ -1,44 +1,39 @@
1
- // * /
2
- // * (\\)? # is it escaped with a backslash?
3
- // * (\$) # literal $
4
- // * (?!\() # shouldnt be followed by parenthesis
5
- // * (\{?) # first brace wrap opening
6
- // * ([\w.]+) # key
7
- // * (?::-((?:\$\{(?:\$\{(?:\$\{[^}]*\}|[^}])*}|[^}])*}|[^}])+))? # optional default nested 3 times
8
- // * (\}?) # last brace warp closing
9
- // * /xi
10
-
11
- const DOTENV_SUBSTITUTION_REGEX = /(\\)?(\$)(?!\()(\{?)([\w.]+)(?::?-((?:\$\{(?:\$\{(?:\$\{[^}]*\}|[^}])*}|[^}])*}|[^}])+))?(\}?)/gi
12
-
13
1
  function _resolveEscapeSequences (value) {
14
2
  return value.replace(/\\\$/g, '$')
15
3
  }
16
4
 
17
- function interpolate (value, lookups) {
18
- return value.replace(DOTENV_SUBSTITUTION_REGEX, (match, escaped, dollarSign, openBrace, key, defaultValue, closeBrace) => {
19
- if (escaped === '\\') {
20
- return match.slice(1)
21
- } else {
22
- if (lookups[key]) {
23
- // avoid recursion from EXPAND_SELF=$EXPAND_SELF
24
- if (lookups[key] === value) {
25
- return lookups[key]
26
- } else {
27
- return interpolate(lookups[key], lookups)
28
- }
29
- }
5
+ function interpolate (value, env) {
6
+ const regex = /(?<!\\)\${([^{}]+)}|(?<!\\)\$([A-Za-z_][A-Za-z0-9_]*)/g
30
7
 
31
- if (defaultValue) {
32
- if (defaultValue.startsWith('$')) {
33
- return interpolate(defaultValue, lookups)
34
- } else {
35
- return defaultValue
36
- }
37
- }
8
+ let result = value
9
+ let match
10
+ const seen = new Set() // self-referential checker
11
+
12
+ while ((match = regex.exec(result)) !== null) {
13
+ seen.add(result)
14
+
15
+ const [template, bracedExpression, unbracedExpression] = match
16
+ const expression = bracedExpression || unbracedExpression
17
+ const r = expression.split(/:-|-/)
18
+ const key = r.shift()
19
+ const defaultValue = r.join('-')
20
+ const value = env[key]
38
21
 
39
- return ''
22
+ if (value) {
23
+ // self-referential check
24
+ if (seen.has(value)) {
25
+ result = result.replace(template, defaultValue)
26
+ } else {
27
+ result = result.replace(template, value)
28
+ }
29
+ } else {
30
+ result = result.replace(template, defaultValue)
40
31
  }
41
- })
32
+
33
+ regex.lastIndex = 0 // reset regex search position to re-evaluate after each replacement
34
+ }
35
+
36
+ return result
42
37
  }
43
38
 
44
39
  function expand (options) {