@kaynooo/env-tools 0.5.0 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kaynooo/env-tools",
3
3
  "type": "module",
4
- "version": "0.5.0",
4
+ "version": "0.7.0",
5
5
  "description": "ev lists and edits .env files with secrets masked, redact masks secrets in any piped output.",
6
6
  "author": "Kaynooo <contact@kaynooo.fr>",
7
7
  "license": "MIT",
package/src/dotenv.ts CHANGED
@@ -13,7 +13,10 @@ const ASSIGNMENT = /^\s*(?:export\s+)?([A-Z_][\w.-]*)\s*=(.*)$/i
13
13
  const EXPORTED = /^\s*export\s/
14
14
  const INLINE_COMMENT = /\s+#.*$/
15
15
  const CARRIAGE_RETURN = /\r$/
16
- const ESCAPES: Record<string, string> = { 'n': '\n', 'r': '\r', 't': '\t', '"': '"', '\\': '\\' }
16
+ const ESCAPES: Record<string, string> = { 'n': '\n', 'r': '\r', 't': '\t', '"': '"', '\\': '\\', '$': '$' }
17
+ const LINE_BREAK = /[\r\n]/
18
+ // Bun does not unescape these inside double quotes, so a value holding one is written in single quotes.
19
+ const RAW_ONLY = /["\\]/
17
20
  const NEEDS_QUOTES = /[\s#"'\\$`]/
18
21
 
19
22
  // Finds the closing quote, skipping backslash escapes inside double quotes. Returns -1 when the value continues on the next line.
@@ -28,6 +31,11 @@ function closingQuote(text: string, quote: string): number {
28
31
  return -1
29
32
  }
30
33
 
34
+ // Bun reads `\$` as a literal dollar sign in every quoting, and expands a bare `$` even inside single quotes.
35
+ function unescapeDollar(text: string): string {
36
+ return text.replaceAll('\\$', '$')
37
+ }
38
+
31
39
  function unescapeDouble(text: string): string {
32
40
  let out = ''
33
41
 
@@ -65,7 +73,7 @@ export function parseDotenv(text: string): Dotenv {
65
73
  const quote = start[0]
66
74
 
67
75
  if (quote !== '"' && quote !== '\'' && quote !== '`') {
68
- lines.push({ raw: line, key, value: start.replace(INLINE_COMMENT, '').trim() })
76
+ lines.push({ raw: line, key, value: unescapeDollar(start.replace(INLINE_COMMENT, '').trim()) })
69
77
 
70
78
  continue
71
79
  }
@@ -83,7 +91,7 @@ export function parseDotenv(text: string): Dotenv {
83
91
 
84
92
  const inner = end === -1 ? body : body.slice(0, end)
85
93
 
86
- lines.push({ raw, key, value: quote === '"' ? unescapeDouble(inner) : inner })
94
+ lines.push({ raw, key, value: quote === '"' ? unescapeDouble(inner) : unescapeDollar(inner) })
87
95
  }
88
96
 
89
97
  return { lines }
@@ -109,7 +117,11 @@ export function formatAssignment(key: string, value: string): string {
109
117
  if (value === '' || !NEEDS_QUOTES.test(value))
110
118
  return `${key}=${value}`
111
119
 
112
- const escaped = value.replaceAll('\\', '\\\\').replaceAll('"', '\\"').replaceAll('\r', '\\r').replaceAll('\n', '\\n')
120
+ // Bun expands a bare `$` in every quoting, so each one is written as `\$`.
121
+ if (RAW_ONLY.test(value) && !value.includes('\'') && !LINE_BREAK.test(value))
122
+ return `${key}='${value.replaceAll('$', '\\$')}'`
123
+
124
+ const escaped = value.replaceAll('\\', '\\\\').replaceAll('"', '\\"').replaceAll('\r', '\\r').replaceAll('\n', '\\n').replaceAll('$', '\\$')
113
125
 
114
126
  return `${key}="${escaped}"`
115
127
  }