@dotenvx/dotenvx 0.20.2 → 0.21.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 +2 -1
- 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 +33 -94
- 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.21.0",
|
|
3
3
|
"name": "@dotenvx/dotenvx",
|
|
4
4
|
"description": "a better dotenv–from the creator of `dotenv`",
|
|
5
5
|
"author": "@motdotla",
|
|
@@ -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
|
@@ -1,112 +1,26 @@
|
|
|
1
1
|
const logger = require('./../shared/logger')
|
|
2
2
|
const dotenv = require('dotenv')
|
|
3
|
-
const dotenvExpand = require('dotenv-expand')
|
|
4
3
|
|
|
5
4
|
// services
|
|
6
5
|
const Encrypt = require('./services/encrypt')
|
|
7
6
|
const Ls = require('./services/ls')
|
|
8
7
|
const Get = require('./services/get')
|
|
8
|
+
const Genexample = require('./services/genexample')
|
|
9
9
|
|
|
10
|
+
// proxies to dotenv
|
|
10
11
|
const config = function (options) {
|
|
11
12
|
return dotenv.config(options)
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
const decrypt = function (encrypted, keyStr) {
|
|
15
|
-
try {
|
|
16
|
-
return dotenv.decrypt(encrypted, keyStr)
|
|
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
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
15
|
const configDotenv = function (options) {
|
|
33
16
|
return dotenv.configDotenv(options)
|
|
34
17
|
}
|
|
35
18
|
|
|
36
19
|
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
|
-
}
|
|
20
|
+
return dotenv.parse(src)
|
|
108
21
|
}
|
|
109
22
|
|
|
23
|
+
// actions related
|
|
110
24
|
const encrypt = function (directory, envFile) {
|
|
111
25
|
return new Encrypt(directory, envFile).run()
|
|
112
26
|
}
|
|
@@ -115,18 +29,43 @@ const ls = function (directory, envFile) {
|
|
|
115
29
|
return new Ls(directory, envFile).run()
|
|
116
30
|
}
|
|
117
31
|
|
|
32
|
+
const genexample = function (directory, envFile) {
|
|
33
|
+
return new Genexample(directory, envFile).run()
|
|
34
|
+
}
|
|
35
|
+
|
|
118
36
|
const get = function (key, envFile, overload, all) {
|
|
119
37
|
return new Get(key, envFile, overload, all).run()
|
|
120
38
|
}
|
|
121
39
|
|
|
40
|
+
// misc/cleanup
|
|
41
|
+
const decrypt = function (encrypted, keyStr) {
|
|
42
|
+
try {
|
|
43
|
+
return dotenv.decrypt(encrypted, keyStr)
|
|
44
|
+
} catch (e) {
|
|
45
|
+
switch (e.code) {
|
|
46
|
+
case 'DECRYPTION_FAILED':
|
|
47
|
+
// more helpful error when decryption fails
|
|
48
|
+
logger.error('[DECRYPTION_FAILED] Unable to decrypt .env.vault with DOTENV_KEY.')
|
|
49
|
+
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.')
|
|
50
|
+
logger.debug(`[DECRYPTION_FAILED] DOTENV_KEY is ${process.env.DOTENV_KEY}`)
|
|
51
|
+
process.exit(1)
|
|
52
|
+
break
|
|
53
|
+
default:
|
|
54
|
+
throw e
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
122
59
|
module.exports = {
|
|
60
|
+
// dotenv proxies
|
|
123
61
|
config,
|
|
124
62
|
configDotenv,
|
|
125
|
-
decrypt,
|
|
126
63
|
parse,
|
|
127
|
-
|
|
128
|
-
inject,
|
|
64
|
+
// actions related
|
|
129
65
|
encrypt,
|
|
130
66
|
ls,
|
|
131
|
-
get
|
|
67
|
+
get,
|
|
68
|
+
genexample,
|
|
69
|
+
// misc/cleanup
|
|
70
|
+
decrypt
|
|
132
71
|
}
|
|
@@ -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
|