@dotenvx/dotenvx 0.42.0 → 0.43.1

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/README.md CHANGED
@@ -32,7 +32,7 @@ brew install dotenvx/brew/dotenvx
32
32
  ```
33
33
  > * [other global ways to install](https://dotenvx.com/docs/install)
34
34
  >
35
- > Intall globally as a cli to unlock dotenv for ANY language, framework, or platform. 💥
35
+ > Install globally as a cli to unlock dotenv for ANY language, framework, or platform. 💥
36
36
  >
37
37
  > I am using (and recommending) this approach going forward. – [motdotla](https://github.com/motdotla)
38
38
 
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.42.0",
2
+ "version": "0.43.1",
3
3
  "name": "@dotenvx/dotenvx",
4
4
  "description": "a better dotenv–from the creator of `dotenv`",
5
5
  "author": "@motdotla",
@@ -0,0 +1,21 @@
1
+ const resolveHome = require('./resolveHome')
2
+
3
+ function dotenvOptionPaths (options) {
4
+ let optionPaths = ['.env']
5
+
6
+ if (options && options.path) {
7
+ if (!Array.isArray(options.path)) {
8
+ optionPaths = [resolveHome(options.path)]
9
+ } else {
10
+ optionPaths = [] // reset default
11
+
12
+ for (const filepath of options.path) {
13
+ optionPaths.push(resolveHome(filepath))
14
+ }
15
+ }
16
+ }
17
+
18
+ return optionPaths
19
+ }
20
+
21
+ module.exports = dotenvOptionPaths
@@ -7,13 +7,10 @@ function parseDecryptEvalExpand (src, privateKey = null) {
7
7
  // parse
8
8
  const parsed = dotenv.parse(src)
9
9
 
10
- // inline decrypt
11
- for (const key in parsed) {
12
- const value = parsed[key]
13
-
14
- // handle inline encrypted values
15
- if (privateKey && privateKey.length > 0) {
16
- // privateKey
10
+ // handle inline encrypted values
11
+ if (privateKey && privateKey.length > 0) {
12
+ for (const key in parsed) {
13
+ const value = parsed[key]
17
14
  parsed[key] = decryptValue(value, privateKey)
18
15
  }
19
16
  }
@@ -6,8 +6,19 @@ function replace (src, key, value) {
6
6
 
7
7
  const parsed = dotenv.parse(src)
8
8
  if (Object.prototype.hasOwnProperty.call(parsed, key)) {
9
- // replace
10
- const regex = new RegExp(`^${key}=(?:(["'\`])[^\\1]*\\1|[^\\n]*)(\\n[^A-Z0-9_].*)*`, 'm')
9
+ const regex = new RegExp(
10
+ `^${key}=` + // start of line with key
11
+ '(?:' + // begin non-capturing group for handling both quoted and unquoted values
12
+ '(["\'`])' + // capture opening quote (' or " or `)
13
+ '[^\\1]*' + // match any character except the quote captured initially
14
+ '\\1' + // match the same closing quote as captured at the start
15
+ '|' + // OR
16
+ '[^#\\n]*' + // match any characters until a # (comment) or newline
17
+ ')' + // end non-capturing group
18
+ '(\\n[^A-Z0-9_].*)*', // match subsequent lines that don't start with a letter, number, or underscore (continuation lines)
19
+ 'm' // apply multiline mode, so ^ and $ match start and end of lines, not just the whole string
20
+ )
21
+
11
22
  output = src.replace(regex, formatted)
12
23
  } else {
13
24
  // append
@@ -0,0 +1,12 @@
1
+ const os = require('os')
2
+ const path = require('path')
3
+
4
+ function resolveHome (filepath) {
5
+ if (filepath[0] === '~') {
6
+ return path.join(os.homedir(), filepath.slice(1))
7
+ }
8
+
9
+ return filepath
10
+ }
11
+
12
+ module.exports = resolveHome
package/src/lib/main.js CHANGED
@@ -1,10 +1,11 @@
1
+ const path = require('path')
1
2
  const logger = require('./../shared/logger')
2
3
  const dotenv = require('dotenv')
3
- const dotenvExpand = require('dotenv-expand')
4
4
 
5
5
  // services
6
6
  const Ls = require('./services/ls')
7
7
  const Get = require('./services/get')
8
+ const Run = require('./services/run')
8
9
  const Sets = require('./services/sets')
9
10
  const Status = require('./services/status')
10
11
  const Encrypt = require('./services/encrypt')
@@ -13,25 +14,119 @@ const Settings = require('./services/settings')
13
14
  const VaultEncrypt = require('./services/vaultEncrypt')
14
15
 
15
16
  // helpers
16
- const dotenvEval = require('./helpers/dotenvEval')
17
+ const dotenvOptionPaths = require('./helpers/dotenvOptionPaths')
17
18
 
18
19
  // proxies to dotenv
19
- const config = function (options) {
20
- const env = dotenv.config(options)
20
+ const config = function (options = {}) {
21
+ // allow user to set processEnv to write to
22
+ let processEnv = process.env
23
+ if (options && options.processEnv != null) {
24
+ processEnv = options.processEnv
25
+ }
26
+
27
+ // overload
28
+ const overload = options.overload || options.override
21
29
 
22
- // if processEnv passed also pass to expand
23
- if (options && options.processEnv) {
24
- env.processEnv = options.processEnv
30
+ // DOTENV_KEY
31
+ let DOTENV_KEY = process.env.DOTENV_KEY
32
+ if (options && options.DOTENV_KEY) {
33
+ DOTENV_KEY = options.DOTENV_KEY
25
34
  }
26
- const expanded = dotenvExpand.expand(env)
27
35
 
28
- // if processEnv passed also pass to eval
29
- if (options && options.processEnv) {
30
- expanded.processEnv = options.processEnv
36
+ // debug -> log level
37
+ if (options && options.debug) {
38
+ logger.level = 'debug'
39
+ logger.debug('setting log level to debug')
31
40
  }
32
- const evaluated = dotenvEval.eval(expanded)
33
41
 
34
- return evaluated
42
+ // build envs using user set option.path
43
+ const optionPaths = dotenvOptionPaths(options) // [ '.env' ]
44
+
45
+ try {
46
+ const envs = []
47
+ for (const optionPath of optionPaths) {
48
+ // if DOTENV_KEY is set then assume we are checking envVaultFile
49
+ if (DOTENV_KEY) {
50
+ envs.push({ type: 'envVaultFile', value: path.join(path.dirname(optionPath), '.env.vault') })
51
+ } else {
52
+ envs.push({ type: 'envFile', value: optionPath })
53
+ }
54
+ }
55
+
56
+ const {
57
+ processedEnvs,
58
+ readableFilepaths,
59
+ uniqueInjectedKeys
60
+ } = new Run(envs, overload, DOTENV_KEY, processEnv).run()
61
+
62
+ let lastError
63
+ const parsedAll = {}
64
+
65
+ for (const processedEnv of processedEnvs) {
66
+ if (processedEnv.type === 'envVaultFile') {
67
+ logger.verbose(`loading env from encrypted ${processedEnv.filepath} (${path.resolve(processedEnv.filepath)})`)
68
+ logger.debug(`decrypting encrypted env from ${processedEnv.filepath} (${path.resolve(processedEnv.filepath)})`)
69
+ }
70
+
71
+ if (processedEnv.type === 'envFile') {
72
+ logger.verbose(`loading env from ${processedEnv.filepath} (${path.resolve(processedEnv.filepath)})`)
73
+ }
74
+
75
+ if (processedEnv.error) {
76
+ lastError = processedEnv.error
77
+
78
+ if (processedEnv.error.code === 'MISSING_ENV_FILE') {
79
+ // do not warn for conventions (too noisy)
80
+ if (!options.convention) {
81
+ logger.warnv(processedEnv.error)
82
+ logger.help(`? add one with [echo "HELLO=World" > ${processedEnv.filepath}] and re-run [dotenvx run -- yourcommand]`)
83
+ }
84
+ } else {
85
+ logger.warnv(processedEnv.error)
86
+ }
87
+ } else {
88
+ Object.assign(parsedAll, processedEnv.injected)
89
+ Object.assign(parsedAll, processedEnv.preExisted) // preExisted 'wins'
90
+
91
+ // debug parsed
92
+ const parsed = processedEnv.parsed
93
+ logger.debug(parsed)
94
+
95
+ // verbose/debug injected key/value
96
+ const injected = processedEnv.injected
97
+ for (const [key, value] of Object.entries(injected)) {
98
+ logger.verbose(`${key} set`)
99
+ logger.debug(`${key} set to ${value}`)
100
+ }
101
+
102
+ // verbose/debug preExisted key/value
103
+ const preExisted = processedEnv.preExisted
104
+ for (const [key, value] of Object.entries(preExisted)) {
105
+ logger.verbose(`${key} pre-exists (protip: use --overload to override)`)
106
+ logger.debug(`${key} pre-exists as ${value} (protip: use --overload to override)`)
107
+ }
108
+ }
109
+ }
110
+
111
+ let msg = `injecting env (${uniqueInjectedKeys.length})`
112
+ if (readableFilepaths.length > 0) {
113
+ msg += ` from ${readableFilepaths.join(', ')}`
114
+ }
115
+ logger.successv(msg)
116
+
117
+ if (lastError) {
118
+ return { parsed: parsedAll, error: lastError }
119
+ } else {
120
+ return { parsed: parsedAll }
121
+ }
122
+ } catch (error) {
123
+ logger.error(error.message)
124
+ if (error.help) {
125
+ logger.help(error.help)
126
+ }
127
+
128
+ return { parsed: {}, error }
129
+ }
35
130
  }
36
131
 
37
132
  const configDotenv = function (options) {