@dotenvx/dotenvx 1.75.1 → 2.0.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.
Files changed (85) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/README.md +4 -114
  3. package/package.json +8 -7
  4. package/src/cli/actions/decrypt.js +35 -17
  5. package/src/cli/actions/doctor.js +2 -2
  6. package/src/cli/actions/encrypt.js +64 -85
  7. package/src/cli/actions/ext/genexample.js +2 -2
  8. package/src/cli/actions/get.js +16 -7
  9. package/src/cli/actions/keypair.js +55 -42
  10. package/src/cli/actions/login.js +4 -3
  11. package/src/cli/actions/normalizeArmorAliases.js +9 -0
  12. package/src/cli/actions/run.js +65 -22
  13. package/src/cli/actions/set.js +51 -37
  14. package/src/cli/dotenvx.js +6 -21
  15. package/src/lib/api/{postKeypair.js → postArmorKeyring.js} +4 -14
  16. package/src/lib/{helpers/envResolution → conventions}/environment.js +1 -1
  17. package/src/lib/conventions/filepaths.js +9 -0
  18. package/src/lib/conventions/keynames.js +38 -0
  19. package/src/lib/helpers/catchAndLog.js +4 -1
  20. package/src/lib/helpers/cryptography/index.js +0 -8
  21. package/src/lib/helpers/cryptography/mutateKeysSrc.js +5 -21
  22. package/src/lib/helpers/envResolution/index.js +1 -1
  23. package/src/lib/helpers/errors.js +13 -0
  24. package/src/lib/helpers/keypairMetadata.js +1 -1
  25. package/src/lib/main.d.ts +2 -39
  26. package/src/lib/main.js +82 -45
  27. package/src/lib/providers/armor/index.js +29 -0
  28. package/src/lib/providers/index.js +46 -0
  29. package/src/lib/providers/worker.js +6 -0
  30. package/src/lib/resolvers/envs.js +302 -0
  31. package/src/lib/resolvers/get.js +69 -0
  32. package/src/lib/resolvers/keypair.js +81 -0
  33. package/src/lib/resolvers/ls.js +41 -0
  34. package/src/lib/services/armorDown.js +2 -2
  35. package/src/lib/services/{armorKeypair.js → armorKeyring.js} +14 -51
  36. package/src/lib/services/armorMove.js +2 -2
  37. package/src/lib/services/armorPull.js +2 -2
  38. package/src/lib/services/armorPush.js +2 -2
  39. package/src/lib/services/armorUp.js +2 -2
  40. package/src/lib/services/genexample.js +5 -6
  41. package/src/lib/services/login.js +25 -0
  42. package/src/lib/services/prebuild.js +9 -3
  43. package/src/lib/services/precommit.js +9 -3
  44. package/src/lib/transforms/decrypt.js +83 -0
  45. package/src/lib/transforms/encrypt.js +179 -0
  46. package/src/lib/transforms/set.js +179 -0
  47. package/src/shared/logger.js +5 -0
  48. package/src/cli/actions/normalizeArmorOptions.js +0 -10
  49. package/src/cli/actions/rotate.js +0 -87
  50. package/src/lib/helpers/append.js +0 -62
  51. package/src/lib/helpers/cryptography/armorKeypair.js +0 -42
  52. package/src/lib/helpers/cryptography/armorKeypairSync.js +0 -55
  53. package/src/lib/helpers/cryptography/encryptValue.js +0 -7
  54. package/src/lib/helpers/cryptography/localKeypair.js +0 -14
  55. package/src/lib/helpers/cryptography/mutateKeysSrcSync.js +0 -38
  56. package/src/lib/helpers/cryptography/provision.js +0 -56
  57. package/src/lib/helpers/cryptography/provisionSync.js +0 -51
  58. package/src/lib/helpers/cryptography/provisionWithPrivateKey.js +0 -26
  59. package/src/lib/helpers/escapeDollarSigns.js +0 -5
  60. package/src/lib/helpers/escapeForRegex.js +0 -5
  61. package/src/lib/helpers/evalKeyValue.js +0 -23
  62. package/src/lib/helpers/isIgnoringDotenvKeys.js +0 -19
  63. package/src/lib/helpers/keyResolution/index.js +0 -16
  64. package/src/lib/helpers/keyResolution/keyNamesForEnvFile.js +0 -24
  65. package/src/lib/helpers/keyResolution/keyValues.js +0 -99
  66. package/src/lib/helpers/keyResolution/keyValuesFromEnvSrc.js +0 -85
  67. package/src/lib/helpers/keyResolution/keyValuesSync.js +0 -103
  68. package/src/lib/helpers/keyResolution/readFileKey.js +0 -19
  69. package/src/lib/helpers/keyResolution/readFileKeySync.js +0 -17
  70. package/src/lib/helpers/keyResolution/readProcessKey.js +0 -7
  71. package/src/lib/helpers/localDisplayPath.js +0 -11
  72. package/src/lib/helpers/parse.js +0 -214
  73. package/src/lib/helpers/pluralize.js +0 -10
  74. package/src/lib/helpers/quotes.js +0 -36
  75. package/src/lib/helpers/replace.js +0 -82
  76. package/src/lib/helpers/resolveEscapeSequences.js +0 -5
  77. package/src/lib/services/decrypt.js +0 -156
  78. package/src/lib/services/encrypt.js +0 -213
  79. package/src/lib/services/get.js +0 -76
  80. package/src/lib/services/keypair.js +0 -60
  81. package/src/lib/services/loginPoll.js +0 -36
  82. package/src/lib/services/ls.js +0 -57
  83. package/src/lib/services/rotate.js +0 -197
  84. package/src/lib/services/run.js +0 -255
  85. package/src/lib/services/sets.js +0 -328
@@ -1,85 +0,0 @@
1
- const path = require('path')
2
- const { scan } = require('@dotenvx/primitives')
3
-
4
- const readFileKeySync = require('./readFileKeySync')
5
- const armorKeypairSync = require('../cryptography/armorKeypairSync')
6
-
7
- const PUBLIC_KEY_SCHEMA = 'DOTENV_PUBLIC_KEY'
8
- const PRIVATE_KEY_SCHEMA = 'DOTENV_PRIVATE_KEY'
9
-
10
- function readProcessKey (processEnv, keyName) {
11
- if (processEnv[keyName] && processEnv[keyName].length > 0) {
12
- return processEnv[keyName]
13
- }
14
- }
15
-
16
- function publicKeyNameFromEnvSrc (envParsed) {
17
- for (const keyName of Object.keys(envParsed)) {
18
- if (keyName === PUBLIC_KEY_SCHEMA || keyName.startsWith(PUBLIC_KEY_SCHEMA)) {
19
- return keyName
20
- }
21
- }
22
-
23
- return null
24
- }
25
-
26
- function finalValue (parsed, keyName) {
27
- const values = parsed[keyName]
28
- return values ? values[values.length - 1] : undefined
29
- }
30
-
31
- function keyValuesFromEnvSrc (src, privateKeyName = null, opts = {}) {
32
- let keysFilepath = opts.keysFilepath || null
33
- const noArmor = opts.noArmor === true
34
- const processEnv = opts.processEnv || process.env
35
- const { parsed: envParsed } = scan(src)
36
-
37
- const publicKeyName = publicKeyNameFromEnvSrc(envParsed)
38
- const publicKeyValue = publicKeyName ? readProcessKey(processEnv, publicKeyName) || finalValue(envParsed, publicKeyName) || null : null
39
-
40
- if (!privateKeyName && publicKeyName) {
41
- privateKeyName = publicKeyName.replace(PUBLIC_KEY_SCHEMA, PRIVATE_KEY_SCHEMA)
42
- }
43
-
44
- let privateKeyValue = null
45
- let privateKeySource = null
46
- if (privateKeyName) {
47
- privateKeyValue = readProcessKey(processEnv, privateKeyName)
48
-
49
- if (!privateKeyValue) {
50
- if (keysFilepath) {
51
- keysFilepath = path.resolve(keysFilepath)
52
- } else {
53
- keysFilepath = path.resolve('.env.keys')
54
- }
55
-
56
- privateKeyValue = readFileKeySync(privateKeyName, keysFilepath)
57
- }
58
- }
59
-
60
- if (!noArmor && !privateKeyValue && publicKeyValue && publicKeyValue.length > 0) {
61
- const armorOptions = {}
62
- if (opts.token) {
63
- armorOptions.token = opts.token
64
- }
65
- if (opts.command) {
66
- armorOptions.command = opts.command
67
- }
68
- const kp = armorKeypairSync(publicKeyValue, armorOptions)
69
- privateKeyValue = kp.privateKey
70
- privateKeySource = 'armor'
71
- }
72
-
73
- const result = {
74
- publicKeyValue: publicKeyValue || null,
75
- privateKeyValue: privateKeyValue || null,
76
- privateKeyName: privateKeyName || null
77
- }
78
- if (privateKeySource) {
79
- result.privateKeySource = privateKeySource
80
- }
81
-
82
- return result
83
- }
84
-
85
- module.exports = keyValuesFromEnvSrc
@@ -1,103 +0,0 @@
1
- const path = require('path')
2
- const { scan } = require('@dotenvx/primitives')
3
-
4
- const fsx = require('./../fsx')
5
- const keyNamesForEnvFile = require('./keyNamesForEnvFile')
6
- const readProcessKey = require('./readProcessKey')
7
- const readFileKeySync = require('./readFileKeySync')
8
- const armorKeypairSync = require('../cryptography/armorKeypairSync')
9
-
10
- function invertForPrivateKeyName (filepath) {
11
- const PUBLIC_KEY_SCHEMA = 'DOTENV_PUBLIC_KEY'
12
- const PRIVATE_KEY_SCHEMA = 'DOTENV_PRIVATE_KEY'
13
-
14
- if (!fsx.existsSync(filepath)) {
15
- return null
16
- }
17
-
18
- const envSrc = fsx.readFileXSync(filepath)
19
- const { parsed: envParsed } = scan(envSrc)
20
-
21
- let publicKeyName
22
- for (const keyName of Object.keys(envParsed)) {
23
- if (keyName === PUBLIC_KEY_SCHEMA || keyName.startsWith(PUBLIC_KEY_SCHEMA)) {
24
- publicKeyName = keyName // find DOTENV_PUBLIC_KEY* in filename
25
- }
26
- }
27
-
28
- if (publicKeyName) {
29
- return publicKeyName.replace(PUBLIC_KEY_SCHEMA, PRIVATE_KEY_SCHEMA) // return inverted (DOTENV_PUBLIC_KEY* -> DOTENV_PRIVATE_KEY*) if found
30
- }
31
-
32
- return null
33
- }
34
-
35
- function keyValuesSync (filepath, opts = {}) {
36
- let keysFilepath = opts.keysFilepath || null
37
- const noArmor = opts.noArmor === true
38
- const names = keyNamesForEnvFile(filepath)
39
- const publicKeyName = names.publicKeyName // DOTENV_PUBLIC_KEY_${ENVIRONMENT}
40
- let privateKeyName = names.privateKeyName // DOTENV_PRIVATE_KEY_${ENVIRONMENT}
41
-
42
- let publicKey = null
43
- let privateKey = null
44
- let privateKeySource = null
45
-
46
- // public key: process.env first, then .env*
47
- publicKey = readProcessKey(publicKeyName)
48
- if (!publicKey) {
49
- publicKey = readFileKeySync(publicKeyName, filepath) || null
50
- }
51
-
52
- // private key: process.env first, then .env.keys, then invert public key
53
- privateKey = readProcessKey(privateKeyName)
54
- if (!privateKey) {
55
- if (keysFilepath) { // user specified -fk flag
56
- keysFilepath = path.resolve(keysFilepath)
57
- } else {
58
- keysFilepath = path.resolve(path.dirname(filepath), '.env.keys') // typical scenario
59
- }
60
-
61
- privateKey = readFileKeySync(privateKeyName, keysFilepath)
62
- }
63
- // invert
64
- if (!privateKey) {
65
- privateKeyName = invertForPrivateKeyName(filepath)
66
- if (privateKeyName) {
67
- privateKey = readProcessKey(privateKeyName)
68
- if (!privateKey) {
69
- privateKey = readFileKeySync(privateKeyName, keysFilepath)
70
- }
71
- }
72
- }
73
-
74
- // armor
75
- if (!noArmor && !privateKey && publicKey && publicKey.length > 0) {
76
- const armorOptions = { envFilepath: filepath }
77
- if (opts.token) {
78
- armorOptions.token = opts.token
79
- }
80
- if (opts.command) {
81
- armorOptions.command = opts.command
82
- }
83
- if (opts.noSpinner) {
84
- armorOptions.noSpinner = true
85
- }
86
-
87
- const kp = armorKeypairSync(publicKey, armorOptions)
88
- privateKey = kp.privateKey
89
- privateKeySource = 'armor'
90
- }
91
-
92
- const result = {
93
- publicKeyValue: publicKey || null, // important to make sure name is rendered
94
- privateKeyValue: privateKey || null // important to make sure name is rendered
95
- }
96
- if (privateKeySource) {
97
- result.privateKeySource = privateKeySource
98
- }
99
-
100
- return result
101
- }
102
-
103
- module.exports = keyValuesSync
@@ -1,19 +0,0 @@
1
- const fsx = require('./../fsx')
2
- const { scan } = require('@dotenvx/primitives')
3
-
4
- async function readFileKey (keyName, filepath) {
5
- if (!(await fsx.exists(filepath))) {
6
- return undefined
7
- }
8
-
9
- const src = await fsx.readFileX(filepath)
10
- const { parsed } = scan(src)
11
- const values = parsed[keyName]
12
-
13
- if (values && values.length > 0) {
14
- const value = values[values.length - 1]
15
- return value || undefined
16
- }
17
- }
18
-
19
- module.exports = readFileKey
@@ -1,17 +0,0 @@
1
- const fsx = require('./../fsx')
2
- const { scan } = require('@dotenvx/primitives')
3
-
4
- function readFileKeySync (keyName, filepath) {
5
- if (fsx.existsSync(filepath)) {
6
- const src = fsx.readFileXSync(filepath)
7
- const { parsed } = scan(src)
8
- const values = parsed[keyName]
9
-
10
- if (values && values.length > 0) {
11
- const value = values[values.length - 1]
12
- return value || undefined
13
- }
14
- }
15
- }
16
-
17
- module.exports = readFileKeySync
@@ -1,7 +0,0 @@
1
- function readProcessKey (keyName) {
2
- if (process.env[keyName] && process.env[keyName].length > 0) {
3
- return process.env[keyName]
4
- }
5
- }
6
-
7
- module.exports = readProcessKey
@@ -1,11 +0,0 @@
1
- const path = require('path')
2
-
3
- function localDisplayPath (filepath) {
4
- if (!filepath) return '.env.keys'
5
- if (!path.isAbsolute(filepath)) return filepath
6
-
7
- const relative = path.relative(process.cwd(), filepath)
8
- return relative || path.basename(filepath)
9
- }
10
-
11
- module.exports = localDisplayPath
@@ -1,214 +0,0 @@
1
- const decryptKeyValue = require('./cryptography/decryptKeyValue')
2
- const { encrypted } = require('@dotenvx/primitives')
3
- const evalKeyValue = require('./evalKeyValue')
4
- const resolveEscapeSequences = require('./resolveEscapeSequences')
5
-
6
- class Parse {
7
- static LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg
8
-
9
- constructor (src, privateKey = null, processEnv = process.env, overload = false, privateKeyName = null) {
10
- this.src = src
11
- this.privateKey = privateKey
12
- this.privateKeyName = privateKeyName
13
- this.processEnv = processEnv
14
- this.overload = overload
15
-
16
- this.parsed = {}
17
- this.preExisted = {}
18
- this.injected = {}
19
- this.errors = []
20
-
21
- // for use with progressive expansion
22
- this.runningParsed = {}
23
- // for use with stopping expansion for literals
24
- this.literals = {}
25
- }
26
-
27
- run () {
28
- const lines = this.getLines()
29
-
30
- let match
31
- while ((match = Parse.LINE.exec(lines)) !== null) {
32
- const key = match[1]
33
- const value = match[2]
34
- const quote = this.quote(value) // must be raw match
35
- this.parsed[key] = this.clean(value, quote) // file value
36
-
37
- if (!this.overload && this.inProcessEnv(key)) {
38
- this.parsed[key] = this.processEnv[key] // use process.env pre-existing value
39
- }
40
-
41
- // decrypt
42
- try {
43
- this.parsed[key] = this.decrypt(key, this.parsed[key])
44
- } catch (e) {
45
- this.errors.push(e)
46
- }
47
-
48
- const encryptedPrefixed = encrypted(this.parsed[key]) // do not eval encrypted prefix
49
-
50
- // eval empty, double, or backticks
51
- let evaled = false
52
- if (!encryptedPrefixed && quote !== "'" && (!this.inProcessEnv(key) || this.processEnv[key] === this.parsed[key])) {
53
- const priorEvaled = this.parsed[key]
54
- // eval
55
- try {
56
- this.parsed[key] = this.eval(key, priorEvaled)
57
- } catch (e) {
58
- this.errors.push(e)
59
- }
60
- if (priorEvaled !== this.parsed[key]) {
61
- evaled = true
62
- }
63
- }
64
-
65
- // expand empty, double, or backticks
66
- if (!encryptedPrefixed && !evaled && quote !== "'" && (!this.processEnv[key] || this.overload)) {
67
- this.parsed[key] = resolveEscapeSequences(this.expand(this.parsed[key]))
68
- }
69
-
70
- if (quote === "'") {
71
- this.literals[key] = this.parsed[key]
72
- }
73
-
74
- // for use with progressive expansion
75
- this.runningParsed[key] = this.parsed[key]
76
-
77
- if (Object.prototype.hasOwnProperty.call(this.processEnv, key) && !this.overload) {
78
- this.preExisted[key] = this.processEnv[key] // track preExisted
79
- } else {
80
- this.injected[key] = this.parsed[key] // track injected
81
- }
82
- }
83
-
84
- return {
85
- parsed: this.parsed,
86
- processEnv: this.processEnv,
87
- injected: this.injected,
88
- preExisted: this.preExisted,
89
- errors: this.errors
90
- }
91
- }
92
-
93
- trimmer (value) {
94
- // Default undefined or null to empty string
95
- return (value || '').trim()
96
- }
97
-
98
- quote (value) {
99
- const v = this.trimmer(value)
100
- const maybeQuote = v[0]
101
- let q = ''
102
- switch (maybeQuote) {
103
- // single
104
- case "'":
105
- q = "'"
106
- break
107
- // double
108
- case '"':
109
- q = '"'
110
- break
111
- // backtick
112
- case '`':
113
- q = '`'
114
- break
115
- // empty
116
- default:
117
- q = ''
118
- }
119
-
120
- return q
121
- }
122
-
123
- clean (value, _quote) {
124
- let v = this.trimmer(value)
125
-
126
- // Remove surrounding quotes
127
- v = v.replace(/^(['"`])([\s\S]*)\1$/mg, '$2')
128
-
129
- // Expand newlines if double quoted
130
- if (_quote === '"') {
131
- v = v.replace(/\\n/g, '\n') // newline
132
- v = v.replace(/\\r/g, '\r') // carriage return
133
- v = v.replace(/\\t/g, '\t') // tabs
134
- }
135
-
136
- return v
137
- }
138
-
139
- decrypt (key, value) {
140
- return decryptKeyValue(key, value, this.privateKeyName, this.privateKey)
141
- }
142
-
143
- eval (key, value) {
144
- return evalKeyValue(key, value, this.processEnv, this.runningParsed)
145
- }
146
-
147
- expand (value) {
148
- let env = { ...this.runningParsed, ...this.processEnv } // typically process.env wins
149
- if (this.overload) {
150
- env = { ...this.processEnv, ...this.runningParsed } // parsed wins
151
- }
152
-
153
- const regex = /(?<!\\)\${([^{}]+)}|(?<!\\)\$([A-Za-z_][A-Za-z0-9_]*)/g
154
-
155
- let result = value
156
- let match
157
-
158
- while ((match = regex.exec(result)) !== null) {
159
- const [template, bracedExpression, unbracedExpression] = match
160
- const expression = bracedExpression || unbracedExpression
161
-
162
- // match the operators `:+`, `+`, `:-`, and `-`
163
- const opRegex = /(:\+|\+|:-|-)/
164
- // find first match
165
- const opMatch = expression.match(opRegex)
166
- const splitter = opMatch ? opMatch[0] : null
167
-
168
- const r = expression.split(splitter)
169
-
170
- let defaultValue
171
- let value
172
- const key = r.shift()
173
-
174
- if ([':+', '+'].includes(splitter)) {
175
- defaultValue = env[key] ? r.join(splitter) : ''
176
- value = null
177
- } else {
178
- defaultValue = r.join(splitter)
179
- value = env[key]
180
- }
181
-
182
- if (value) {
183
- result = result.replace(template, value)
184
- } else {
185
- result = result.replace(template, defaultValue)
186
- }
187
-
188
- // if the result equaled what was in env then stop expanding - handle self-referential check as well
189
- if (result === env[key]) {
190
- break
191
- }
192
-
193
- // if the result came from what was a literal value then stop expanding
194
- // BUT only if the literal value contains expansion patterns (${...} or $VAR)
195
- if (this.literals[key] && /\$\{[^}]+\}|\$[A-Za-z_][A-Za-z0-9_]*/.test(this.literals[key])) {
196
- break
197
- }
198
-
199
- regex.lastIndex = 0 // reset regex search position to re-evaluate after each replacement
200
- }
201
-
202
- return result
203
- }
204
-
205
- inProcessEnv (key) {
206
- return Object.prototype.hasOwnProperty.call(this.processEnv, key)
207
- }
208
-
209
- getLines () {
210
- return (this.src || '').toString().replace(/\r\n?/mg, '\n') // Convert buffer to string and Convert line breaks to same format
211
- }
212
- }
213
-
214
- module.exports = Parse
@@ -1,10 +0,0 @@
1
- function pluralize (word, count) {
2
- // simple pluralization: add 's' at the end
3
- if (count === 0 || count > 1) {
4
- return word + 's'
5
- } else {
6
- return word
7
- }
8
- }
9
-
10
- module.exports = pluralize
@@ -1,36 +0,0 @@
1
- const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg
2
-
3
- function quotes (src) {
4
- const obj = {}
5
- // Convert buffer to string
6
- let lines = src.toString()
7
-
8
- // Convert line breaks to same format
9
- lines = lines.replace(/\r\n?/mg, '\n')
10
-
11
- let match
12
- while ((match = LINE.exec(lines)) != null) {
13
- const key = match[1]
14
-
15
- // Default undefined or null to empty string
16
- let value = (match[2] || '')
17
-
18
- // Remove whitespace
19
- value = value.trim()
20
-
21
- // Check if double quoted
22
- const maybeQuote = value[0]
23
-
24
- value = value.replace(/^(['"`])([\s\S]*)\1$/mg, '$2')
25
-
26
- if (maybeQuote === value[0]) {
27
- obj[key] = ''
28
- } else {
29
- obj[key] = maybeQuote
30
- }
31
- }
32
-
33
- return obj
34
- }
35
-
36
- module.exports = quotes
@@ -1,82 +0,0 @@
1
- const { scan } = require('@dotenvx/primitives')
2
- const escapeForRegex = require('./escapeForRegex')
3
-
4
- function replaceExistingValue (src, key, originalValue, replaceValue) {
5
- const escapedKey = escapeForRegex(key)
6
- const escapedOriginalValue = escapeForRegex(originalValue)
7
-
8
- // conditionally enforce end of line
9
- let enforceEndOfLine = ''
10
- if (escapedOriginalValue === '') {
11
- enforceEndOfLine = '$' // EMPTY scenario
12
- }
13
-
14
- const currentPart = new RegExp(
15
- '^' + // start of line
16
- '(\\s*)?' + // spaces
17
- '(export\\s+)?' + // export
18
- escapedKey + // KEY
19
- '\\s*=\\s*' + // spaces (KEY = value)
20
- '(["\'`]?)' + // open quote
21
- escapedOriginalValue + // escaped value
22
- '\\3' + // close quote
23
- enforceEndOfLine
24
- ,
25
- 'gm' // (g)lobal (m)ultiline
26
- )
27
-
28
- return src.replace(currentPart, function (match, spaces = '', exportPart = '', quote = '') {
29
- let newPart = `${key}=${quote}${replaceValue}${quote}`
30
-
31
- // if empty quote and consecutive newlines
32
- const newlineMatch = src.match(new RegExp(`${escapedKey}\\s*=\\s*\n\n`, 'm')) // match any consecutive newline scenario for a blank value
33
- if (escapedOriginalValue === '' && quote === '' && newlineMatch) {
34
- const newlineCount = (newlineMatch[0].match(/\n/g)).length - 1
35
- for (let i = 0; i < newlineCount; i++) {
36
- newPart += '\n' // re-append the extra newline to preserve user's format choice
37
- }
38
- }
39
-
40
- return `${spaces}${exportPart}${newPart}`
41
- })
42
- }
43
-
44
- function replace (src, key, replaceValue) {
45
- let output
46
- let newPart = ''
47
-
48
- const { parsed } = scan(src, { expandDoubleQuotedNewlines: false, convertWindowsNewlines: false })
49
- if (Object.prototype.hasOwnProperty.call(parsed, key)) {
50
- const allValues = parsed[key]
51
- let duplicateOutput = src
52
- const replacements = Array.isArray(replaceValue) ? replaceValue : allValues.map(() => replaceValue)
53
- const replacementByValue = new Map()
54
-
55
- allValues.forEach((value, index) => {
56
- if (!replacementByValue.has(value)) {
57
- replacementByValue.set(value, replacements[index])
58
- }
59
- })
60
-
61
- for (const [value, replacement] of replacementByValue) {
62
- duplicateOutput = replaceExistingValue(duplicateOutput, key, value, replacement)
63
- }
64
-
65
- return duplicateOutput
66
- } else {
67
- newPart += `${key}="${replaceValue}"`
68
-
69
- // append
70
- if (src.endsWith('\n')) {
71
- newPart = newPart + '\n'
72
- } else {
73
- newPart = '\n' + newPart
74
- }
75
-
76
- output = src + newPart
77
- }
78
-
79
- return output
80
- }
81
-
82
- module.exports = replace
@@ -1,5 +0,0 @@
1
- function resolveEscapeSequences (value) {
2
- return value.replace(/\\\$/g, '$')
3
- }
4
-
5
- module.exports = resolveEscapeSequences