* Expand environment variable value * ported from https://github.com/motdotla/dotenv-expand * @param {string} value value to expand * @param {Record<string, string | undefined>} processEnv process.env object * @param {Env} runningParsed running parsed object * @returns {string} expanded value
(value, processEnv, runningParsed)
| 96 | * @returns {string} expanded value |
| 97 | */ |
| 98 | function expandValue(value, processEnv, runningParsed) { |
| 99 | const env = { ...runningParsed, ...processEnv }; // process.env wins |
| 100 | |
| 101 | const regex = /(?<!\\)\$\{([^{}]+)\}|(?<!\\)\$([a-z_]\w*)/gi; |
| 102 | |
| 103 | let result = value; |
| 104 | /** @type {null | RegExpExecArray} */ |
| 105 | let match; |
| 106 | /** @type {Set<string>} */ |
| 107 | const seen = new Set(); // self-referential checker |
| 108 | |
| 109 | while ((match = regex.exec(result)) !== null) { |
| 110 | seen.add(result); |
| 111 | |
| 112 | const [template, bracedExpression, unbracedExpression] = match; |
| 113 | const expression = bracedExpression || unbracedExpression; |
| 114 | |
| 115 | // match the operators `:+`, `+`, `:-`, and `-` |
| 116 | const opRegex = /(:\+|\+|:-|-)/; |
| 117 | // find first match |
| 118 | const opMatch = expression.match(opRegex); |
| 119 | const splitter = opMatch ? opMatch[0] : null; |
| 120 | |
| 121 | const r = expression.split(/** @type {string} */ (splitter)); |
| 122 | // const r = splitter ? expression.split(splitter) : [expression]; |
| 123 | |
| 124 | /** @type {string} */ |
| 125 | let defaultValue; |
| 126 | /** @type {undefined | null | string} */ |
| 127 | let value; |
| 128 | |
| 129 | const key = r.shift(); |
| 130 | |
| 131 | if ([":+", "+"].includes(splitter || "")) { |
| 132 | defaultValue = env[key || ""] ? r.join(splitter || "") : ""; |
| 133 | value = null; |
| 134 | } else { |
| 135 | defaultValue = r.join(splitter || ""); |
| 136 | value = env[key || ""]; |
| 137 | } |
| 138 | |
| 139 | if (value) { |
| 140 | // self-referential check |
| 141 | result = seen.has(value) |
| 142 | ? result.replace(template, defaultValue) |
| 143 | : result.replace(template, value); |
| 144 | } else { |
| 145 | result = result.replace(template, defaultValue); |
| 146 | } |
| 147 | |
| 148 | // if the result equaled what was in process.env and runningParsed then stop expanding |
| 149 | if (result === runningParsed[key || ""]) { |
| 150 | break; |
| 151 | } |
| 152 | |
| 153 | regex.lastIndex = 0; // reset regex search position to re-evaluate after each replacement |
| 154 | } |
| 155 |