@dotenvx/dotenvx 0.20.2 → 0.22.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/package.json +3 -2
- package/src/cli/actions/decrypt.js +5 -7
- package/src/cli/actions/genexample.js +27 -67
- package/src/cli/actions/run.js +8 -2
- package/src/cli/dotenvx.js +1 -0
- package/src/lib/helpers/findEnvFiles.js +27 -0
- package/src/lib/main.js +40 -92
- package/src/lib/services/encrypt.js +3 -13
- package/src/lib/services/genexample.js +94 -0
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
2
|
+
"version": "0.22.0",
|
|
3
3
|
"name": "@dotenvx/dotenvx",
|
|
4
4
|
"description": "a better dotenv–from the creator of `dotenv`",
|
|
5
5
|
"author": "@motdotla",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"clipboardy": "^2.3.0",
|
|
33
33
|
"commander": "^11.1.0",
|
|
34
34
|
"conf": "^10.2.0",
|
|
35
|
-
"dotenv": "16.4.5",
|
|
35
|
+
"dotenv": "^16.4.5",
|
|
36
36
|
"dotenv-expand": "^11.0.6",
|
|
37
37
|
"execa": "^5.1.1",
|
|
38
38
|
"glob": "^10.3.10",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"ora": "^5.4.1",
|
|
43
43
|
"undici": "^5.28.3",
|
|
44
44
|
"update-notifier": "^5.1.0",
|
|
45
|
+
"which": "^4.0.0",
|
|
45
46
|
"winston": "^3.11.0",
|
|
46
47
|
"xxhashjs": "^0.2.2"
|
|
47
48
|
},
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
|
+
const dotenv = require('dotenv')
|
|
2
3
|
|
|
3
|
-
const main = require('./../../lib/main')
|
|
4
4
|
const logger = require('./../../shared/logger')
|
|
5
5
|
const helpers = require('./../helpers')
|
|
6
6
|
const createSpinner = require('./../../shared/createSpinner')
|
|
7
|
-
const
|
|
7
|
+
const libDecrypt = require('./../../lib/helpers/decrypt')
|
|
8
8
|
|
|
9
9
|
const spinner = createSpinner('decrypting')
|
|
10
10
|
|
|
@@ -37,8 +37,8 @@ async function decrypt () {
|
|
|
37
37
|
process.exit(1)
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
const dotenvKeys =
|
|
41
|
-
const dotenvVault =
|
|
40
|
+
const dotenvKeys = dotenv.configDotenv({ path: keysFilepath }).parsed
|
|
41
|
+
const dotenvVault = dotenv.configDotenv({ path: vaultFilepath }).parsed
|
|
42
42
|
|
|
43
43
|
Object.entries(dotenvKeys).forEach(([dotenvKey, value]) => {
|
|
44
44
|
// determine environment
|
|
@@ -51,10 +51,8 @@ async function decrypt () {
|
|
|
51
51
|
|
|
52
52
|
// give warning if not found
|
|
53
53
|
if (ciphertext && ciphertext.length >= 1) {
|
|
54
|
-
const key = parseEncryptionKeyFromDotenvKey(value.trim())
|
|
55
|
-
|
|
56
54
|
// Decrypt
|
|
57
|
-
const decrypted =
|
|
55
|
+
const decrypted = libDecrypt(ciphertext, value.trim())
|
|
58
56
|
|
|
59
57
|
// envFilename
|
|
60
58
|
let envFilename = `.env.${environment}`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
|
-
const helpers = require('./../helpers')
|
|
3
2
|
const main = require('./../../lib/main')
|
|
3
|
+
const helpers = require('./../helpers')
|
|
4
4
|
const logger = require('./../../shared/logger')
|
|
5
5
|
const createSpinner = require('./../../shared/createSpinner')
|
|
6
6
|
|
|
@@ -8,84 +8,44 @@ const spinner = createSpinner('generating')
|
|
|
8
8
|
|
|
9
9
|
const ENCODING = 'utf8'
|
|
10
10
|
|
|
11
|
-
async function genexample () {
|
|
11
|
+
async function genexample (directory) {
|
|
12
12
|
spinner.start()
|
|
13
13
|
await helpers.sleep(500) // better dx
|
|
14
14
|
|
|
15
|
+
logger.debug(`directory: ${directory}`)
|
|
16
|
+
|
|
15
17
|
const options = this.opts()
|
|
16
18
|
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
spinner.fail('no .env* files found')
|
|
26
|
-
logger.help('? add one with [echo "HELLO=World" > .env] and then run [dotenvx genexample]')
|
|
27
|
-
process.exit(1)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const keys = new Set()
|
|
31
|
-
const addedKeys = new Set()
|
|
20
|
+
try {
|
|
21
|
+
const {
|
|
22
|
+
envExampleFile,
|
|
23
|
+
envFile,
|
|
24
|
+
exampleFilepath,
|
|
25
|
+
addedKeys
|
|
26
|
+
} = main.genexample(directory, options.envFile)
|
|
32
27
|
|
|
33
|
-
|
|
34
|
-
const filepath = helpers.resolvePath(envFilepath)
|
|
28
|
+
logger.verbose(`loading env from ${envFile}`)
|
|
35
29
|
|
|
36
|
-
|
|
30
|
+
// TODO: display pre-existing
|
|
31
|
+
// TODO: display added/appended/injected
|
|
37
32
|
|
|
38
|
-
|
|
39
|
-
const src = fs.readFileSync(filepath, { encoding: ENCODING })
|
|
40
|
-
const parsed = main.parse(src)
|
|
41
|
-
Object.keys(parsed).forEach(key => { keys.add(key) })
|
|
42
|
-
} catch (e) {
|
|
43
|
-
// calculate development help message depending on state of repo
|
|
44
|
-
const vaultFilepath = helpers.resolvePath('.env.vault')
|
|
45
|
-
let developmentHelp = '? in development: add one with [echo "HELLO=World" > .env] and re-run [dotenvx genexample]'
|
|
46
|
-
if (fs.existsSync(vaultFilepath)) {
|
|
47
|
-
developmentHelp = '? in development: use [dotenvx decrypt] to decrypt .env.vault to .env and then re-run [dotenvx genexample]'
|
|
48
|
-
}
|
|
33
|
+
fs.writeFileSync(exampleFilepath, envExampleFile, ENCODING)
|
|
49
34
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
case 'ENOENT':
|
|
53
|
-
logger.warn(`missing ${envFilepath} file (${filepath})`)
|
|
54
|
-
logger.help(developmentHelp)
|
|
55
|
-
break
|
|
56
|
-
|
|
57
|
-
// unhandled error
|
|
58
|
-
default:
|
|
59
|
-
logger.warn(e)
|
|
60
|
-
break
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const exampleFilename = '.env.example'
|
|
66
|
-
const exampleFilepath = helpers.resolvePath(exampleFilename)
|
|
67
|
-
if (!fs.existsSync(exampleFilepath)) {
|
|
68
|
-
logger.verbose(`creating ${exampleFilename}`)
|
|
69
|
-
fs.writeFileSync(exampleFilename, `# ${exampleFilename}\n`)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const currentEnvExample = (main.configDotenv({ path: exampleFilepath }).parsed || {})
|
|
73
|
-
const keysArray = Array.from(keys)
|
|
74
|
-
|
|
75
|
-
keysArray.forEach(key => {
|
|
76
|
-
if (key in currentEnvExample) {
|
|
77
|
-
logger.verbose(`pre-existing ${key} in ${exampleFilename}`)
|
|
35
|
+
if (addedKeys.length > 0) {
|
|
36
|
+
spinner.succeed(`updated .env.example (${addedKeys.length})`)
|
|
78
37
|
} else {
|
|
79
|
-
|
|
80
|
-
logger.verbose(`appending ${key} to ${exampleFilename}`)
|
|
81
|
-
fs.appendFileSync('.env.example', `${key}=""\n`, ENCODING)
|
|
38
|
+
spinner.done('no changes (.env.example)')
|
|
82
39
|
}
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
40
|
+
} catch (error) {
|
|
41
|
+
spinner.fail(error.message)
|
|
42
|
+
if (error.help) {
|
|
43
|
+
logger.help(error.help)
|
|
44
|
+
}
|
|
45
|
+
if (error.code) {
|
|
46
|
+
logger.debug(`ERROR_CODE: ${error.code}`)
|
|
47
|
+
}
|
|
48
|
+
process.exit(1)
|
|
89
49
|
}
|
|
90
50
|
}
|
|
91
51
|
|
package/src/cli/actions/run.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
2
|
const execa = require('execa')
|
|
3
|
+
const which = require('which')
|
|
3
4
|
const logger = require('./../../shared/logger')
|
|
4
5
|
|
|
5
6
|
const RunDefault = require('./../../lib/services/runDefault')
|
|
@@ -35,8 +36,13 @@ const executeCommand = async function (commandArgs, env) {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
try {
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
let systemCommandPath = commandArgs[0]
|
|
40
|
+
try {
|
|
41
|
+
systemCommandPath = which.sync(`${commandArgs[0]}`)
|
|
42
|
+
logger.debug(`expanding process command to [${systemCommandPath} ${commandArgs.slice(1).join(' ')}]`)
|
|
43
|
+
} catch (e) {
|
|
44
|
+
logger.debug(`could not expand process command. using [${systemCommandPath} ${commandArgs.slice(1).join(' ')}]`)
|
|
45
|
+
}
|
|
40
46
|
|
|
41
47
|
// commandProcess = execa(commandArgs[0], commandArgs.slice(1), {
|
|
42
48
|
commandProcess = execa(systemCommandPath, commandArgs.slice(1), {
|
package/src/cli/dotenvx.js
CHANGED
|
@@ -95,6 +95,7 @@ program.command('prebuild')
|
|
|
95
95
|
// dotenvx genexample
|
|
96
96
|
program.command('genexample')
|
|
97
97
|
.description('generate .env.example')
|
|
98
|
+
.argument('[directory]', 'directory to generate from', '.')
|
|
98
99
|
.option('-f, --env-file <paths...>', 'path(s) to your env file(s)', '.env')
|
|
99
100
|
.action(require('./actions/genexample'))
|
|
100
101
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
|
|
3
|
+
const RESERVED_ENV_FILES = ['.env.vault', '.env.project', '.env.keys', '.env.me', '.env.x', '.env.example']
|
|
4
|
+
|
|
5
|
+
function findEnvFiles (directory) {
|
|
6
|
+
try {
|
|
7
|
+
const files = fs.readdirSync(directory)
|
|
8
|
+
const envFiles = files.filter(file =>
|
|
9
|
+
file.startsWith('.env') &&
|
|
10
|
+
!file.endsWith('.previous') &&
|
|
11
|
+
!RESERVED_ENV_FILES.includes(file)
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
return envFiles
|
|
15
|
+
} catch (e) {
|
|
16
|
+
if (e.code === 'ENOENT') {
|
|
17
|
+
const error = new Error(`missing directory (${directory})`)
|
|
18
|
+
error.code = 'MISSING_DIRECTORY'
|
|
19
|
+
|
|
20
|
+
throw error
|
|
21
|
+
} else {
|
|
22
|
+
throw e
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = findEnvFiles
|
package/src/lib/main.js
CHANGED
|
@@ -6,27 +6,19 @@ const dotenvExpand = require('dotenv-expand')
|
|
|
6
6
|
const Encrypt = require('./services/encrypt')
|
|
7
7
|
const Ls = require('./services/ls')
|
|
8
8
|
const Get = require('./services/get')
|
|
9
|
+
const Genexample = require('./services/genexample')
|
|
9
10
|
|
|
11
|
+
// proxies to dotenv
|
|
10
12
|
const config = function (options) {
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
+
const env = dotenv.config(options)
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
} catch (e) {
|
|
18
|
-
switch (e.code) {
|
|
19
|
-
case 'DECRYPTION_FAILED':
|
|
20
|
-
// more helpful error when decryption fails
|
|
21
|
-
logger.error('[DECRYPTION_FAILED] Unable to decrypt .env.vault with DOTENV_KEY.')
|
|
22
|
-
logger.help('[DECRYPTION_FAILED] Run with debug flag [dotenvx run --debug -- yourcommand] or manually run [echo $DOTENV_KEY] to compare it to the one in .env.keys.')
|
|
23
|
-
logger.debug(`[DECRYPTION_FAILED] DOTENV_KEY is ${process.env.DOTENV_KEY}`)
|
|
24
|
-
process.exit(1)
|
|
25
|
-
break
|
|
26
|
-
default:
|
|
27
|
-
throw e
|
|
28
|
-
}
|
|
15
|
+
// if processEnv passed also pass to expand
|
|
16
|
+
if (options && options.processEnv) {
|
|
17
|
+
env.processEnv = options.processEnv
|
|
29
18
|
}
|
|
19
|
+
const expanded = dotenvExpand.expand(env)
|
|
20
|
+
|
|
21
|
+
return expanded
|
|
30
22
|
}
|
|
31
23
|
|
|
32
24
|
const configDotenv = function (options) {
|
|
@@ -34,79 +26,10 @@ const configDotenv = function (options) {
|
|
|
34
26
|
}
|
|
35
27
|
|
|
36
28
|
const parse = function (src) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
logger.debug(result)
|
|
40
|
-
|
|
41
|
-
return result
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const parseExpand = function (src, overload) {
|
|
45
|
-
const parsed = dotenv.parse(src)
|
|
46
|
-
|
|
47
|
-
// consider moving this logic straight into dotenv-expand
|
|
48
|
-
let inputParsed = {}
|
|
49
|
-
if (overload) {
|
|
50
|
-
inputParsed = { ...process.env, ...parsed }
|
|
51
|
-
} else {
|
|
52
|
-
inputParsed = { ...parsed, ...process.env }
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const expandPlease = {
|
|
56
|
-
processEnv: {},
|
|
57
|
-
parsed: inputParsed
|
|
58
|
-
}
|
|
59
|
-
const expanded = dotenvExpand.expand(expandPlease).parsed
|
|
60
|
-
|
|
61
|
-
// but then for logging only log the original keys existing in parsed. this feels unnecessarily complex - like dotenv-expand should support the ability to inject additional `process.env` or objects as it sees fit to the object it wants to expand
|
|
62
|
-
const result = {}
|
|
63
|
-
for (const key in parsed) {
|
|
64
|
-
result[key] = expanded[key]
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
logger.debug(result)
|
|
68
|
-
|
|
69
|
-
return result
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const inject = function (processEnv = {}, parsed = {}, overload = false) {
|
|
73
|
-
if (typeof parsed !== 'object') {
|
|
74
|
-
throw new Error('OBJECT_REQUIRED: Please check the parsed argument being passed to inject')
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const injected = new Set()
|
|
78
|
-
const preExisting = new Set()
|
|
79
|
-
|
|
80
|
-
// set processEnv
|
|
81
|
-
for (const key of Object.keys(parsed)) {
|
|
82
|
-
if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
|
|
83
|
-
if (overload === true) {
|
|
84
|
-
processEnv[key] = parsed[key]
|
|
85
|
-
injected.add(key)
|
|
86
|
-
|
|
87
|
-
logger.verbose(`${key} set`)
|
|
88
|
-
logger.debug(`${key} set to ${parsed[key]}`)
|
|
89
|
-
} else {
|
|
90
|
-
preExisting.add(key)
|
|
91
|
-
|
|
92
|
-
logger.verbose(`${key} pre-exists (protip: use --overload to override)`)
|
|
93
|
-
logger.debug(`${key} pre-exists as ${processEnv[key]} (protip: use --overload to override)`)
|
|
94
|
-
}
|
|
95
|
-
} else {
|
|
96
|
-
processEnv[key] = parsed[key]
|
|
97
|
-
injected.add(key)
|
|
98
|
-
|
|
99
|
-
logger.verbose(`${key} set`)
|
|
100
|
-
logger.debug(`${key} set to ${parsed[key]}`)
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return {
|
|
105
|
-
injected,
|
|
106
|
-
preExisting
|
|
107
|
-
}
|
|
29
|
+
return dotenv.parse(src)
|
|
108
30
|
}
|
|
109
31
|
|
|
32
|
+
// actions related
|
|
110
33
|
const encrypt = function (directory, envFile) {
|
|
111
34
|
return new Encrypt(directory, envFile).run()
|
|
112
35
|
}
|
|
@@ -115,18 +38,43 @@ const ls = function (directory, envFile) {
|
|
|
115
38
|
return new Ls(directory, envFile).run()
|
|
116
39
|
}
|
|
117
40
|
|
|
41
|
+
const genexample = function (directory, envFile) {
|
|
42
|
+
return new Genexample(directory, envFile).run()
|
|
43
|
+
}
|
|
44
|
+
|
|
118
45
|
const get = function (key, envFile, overload, all) {
|
|
119
46
|
return new Get(key, envFile, overload, all).run()
|
|
120
47
|
}
|
|
121
48
|
|
|
49
|
+
// misc/cleanup
|
|
50
|
+
const decrypt = function (encrypted, keyStr) {
|
|
51
|
+
try {
|
|
52
|
+
return dotenv.decrypt(encrypted, keyStr)
|
|
53
|
+
} catch (e) {
|
|
54
|
+
switch (e.code) {
|
|
55
|
+
case 'DECRYPTION_FAILED':
|
|
56
|
+
// more helpful error when decryption fails
|
|
57
|
+
logger.error('[DECRYPTION_FAILED] Unable to decrypt .env.vault with DOTENV_KEY.')
|
|
58
|
+
logger.help('[DECRYPTION_FAILED] Run with debug flag [dotenvx run --debug -- yourcommand] or manually run [echo $DOTENV_KEY] to compare it to the one in .env.keys.')
|
|
59
|
+
logger.debug(`[DECRYPTION_FAILED] DOTENV_KEY is ${process.env.DOTENV_KEY}`)
|
|
60
|
+
process.exit(1)
|
|
61
|
+
break
|
|
62
|
+
default:
|
|
63
|
+
throw e
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
122
68
|
module.exports = {
|
|
69
|
+
// dotenv proxies
|
|
123
70
|
config,
|
|
124
71
|
configDotenv,
|
|
125
|
-
decrypt,
|
|
126
72
|
parse,
|
|
127
|
-
|
|
128
|
-
inject,
|
|
73
|
+
// actions related
|
|
129
74
|
encrypt,
|
|
130
75
|
ls,
|
|
131
|
-
get
|
|
76
|
+
get,
|
|
77
|
+
genexample,
|
|
78
|
+
// misc/cleanup
|
|
79
|
+
decrypt
|
|
132
80
|
}
|
|
@@ -6,12 +6,13 @@ const DotenvKeys = require('./../helpers/dotenvKeys')
|
|
|
6
6
|
const DotenvVault = require('./../helpers/dotenvVault')
|
|
7
7
|
|
|
8
8
|
const ENCODING = 'utf8'
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
const findEnvFiles = require('../helpers/findEnvFiles')
|
|
10
11
|
|
|
11
12
|
class Encrypt {
|
|
12
13
|
constructor (directory = '.', envFile) {
|
|
13
14
|
this.directory = directory
|
|
14
|
-
this.envFile = envFile ||
|
|
15
|
+
this.envFile = envFile || findEnvFiles(directory)
|
|
15
16
|
// calculated
|
|
16
17
|
this.envKeysFilepath = path.resolve(this.directory, '.env.keys')
|
|
17
18
|
this.envVaultFilepath = path.resolve(this.directory, '.env.vault')
|
|
@@ -112,17 +113,6 @@ class Encrypt {
|
|
|
112
113
|
|
|
113
114
|
return dotenv.configDotenv(options).parsed
|
|
114
115
|
}
|
|
115
|
-
|
|
116
|
-
_findEnvFiles () {
|
|
117
|
-
const files = fs.readdirSync(this.directory)
|
|
118
|
-
const envFiles = files.filter(file =>
|
|
119
|
-
file.startsWith('.env') &&
|
|
120
|
-
!file.endsWith('.previous') &&
|
|
121
|
-
!RESERVED_ENV_FILES.includes(file)
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
return envFiles
|
|
125
|
-
}
|
|
126
116
|
}
|
|
127
117
|
|
|
128
118
|
module.exports = Encrypt
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const dotenv = require('dotenv')
|
|
4
|
+
|
|
5
|
+
const ENCODING = 'utf8'
|
|
6
|
+
|
|
7
|
+
const findEnvFiles = require('../helpers/findEnvFiles')
|
|
8
|
+
|
|
9
|
+
class Genexample {
|
|
10
|
+
constructor (directory = '.', envFile) {
|
|
11
|
+
this.directory = directory
|
|
12
|
+
this.envFile = envFile || findEnvFiles(directory)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
run () {
|
|
16
|
+
if (this.envFile.length < 1) {
|
|
17
|
+
const code = 'MISSING_ENV_FILES'
|
|
18
|
+
const message = 'no .env* files found'
|
|
19
|
+
const help = '? add one with [echo "HELLO=World" > .env] and then run [dotenvx genexample]'
|
|
20
|
+
|
|
21
|
+
const error = new Error(message)
|
|
22
|
+
error.code = code
|
|
23
|
+
error.help = help
|
|
24
|
+
throw error
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const keys = new Set()
|
|
28
|
+
const addedKeys = new Set()
|
|
29
|
+
const envFilepaths = this._envFilepaths()
|
|
30
|
+
|
|
31
|
+
for (const envFilepath of envFilepaths) {
|
|
32
|
+
const filepath = path.resolve(this.directory, envFilepath)
|
|
33
|
+
if (!fs.existsSync(filepath)) {
|
|
34
|
+
const code = 'MISSING_ENV_FILE'
|
|
35
|
+
const message = `file does not exist at [${filepath}]`
|
|
36
|
+
const help = `? add it with [echo "HELLO=World" > ${envFilepath}] and then run [dotenvx genexample]`
|
|
37
|
+
|
|
38
|
+
const error = new Error(message)
|
|
39
|
+
error.code = code
|
|
40
|
+
error.help = help
|
|
41
|
+
throw error
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const parsed = dotenv.configDotenv({ path: filepath }).parsed
|
|
45
|
+
for (const key of Object.keys(parsed)) {
|
|
46
|
+
keys.add(key)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let envExampleFile = ''
|
|
51
|
+
const exampleFilename = '.env.example'
|
|
52
|
+
const exampleFilepath = path.resolve(this.directory, exampleFilename)
|
|
53
|
+
if (!fs.existsSync(exampleFilepath)) {
|
|
54
|
+
envExampleFile += `# ${exampleFilename}\n`
|
|
55
|
+
} else {
|
|
56
|
+
envExampleFile = fs.readFileSync(exampleFilepath, ENCODING)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const currentEnvExample = dotenv.configDotenv({ path: exampleFilepath }).parsed
|
|
60
|
+
const injected = {}
|
|
61
|
+
const preExisted = {}
|
|
62
|
+
|
|
63
|
+
for (const key of [...keys]) {
|
|
64
|
+
if (key in currentEnvExample) {
|
|
65
|
+
preExisted[key] = currentEnvExample[key]
|
|
66
|
+
} else {
|
|
67
|
+
envExampleFile += `${key}=""\n`
|
|
68
|
+
|
|
69
|
+
addedKeys.add(key)
|
|
70
|
+
|
|
71
|
+
injected[key] = ''
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
envExampleFile,
|
|
77
|
+
envFile: this.envFile,
|
|
78
|
+
exampleFilepath,
|
|
79
|
+
addedKeys: [...addedKeys],
|
|
80
|
+
injected,
|
|
81
|
+
preExisted
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
_envFilepaths () {
|
|
86
|
+
if (!Array.isArray(this.envFile)) {
|
|
87
|
+
return [this.envFile]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return this.envFile
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
module.exports = Genexample
|