Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/lib/content/configuring-npm/npmrc.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@ The four relevant files are:
* npm builtin config file (`/path/to/npm/npmrc`)

All npm config files are an ini-formatted list of `key = value` parameters.
Environment variables can be replaced using `${VARIABLE_NAME}`. For
Environment variables can be replaced using `${VARIABLE_NAME}`. By default
if the variable is not defined, it is left unreplaced. By adding `?` after
variable name they can be forced to evaluate to an empty string instead. For
example:

```bash
cache = ${HOME}/.npm-packages
node-options = "${NODE_OPTIONS?} --use-system-ca"
```

Each of these files is loaded, and config options are resolved in priority
Expand Down
8 changes: 5 additions & 3 deletions workspaces/config/lib/env-replace.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// replace any ${ENV} values with the appropriate environ.
// optional "?" modifier can be used like this: ${ENV?} so in case of the variable being not defined, it evaluates into empty string.

const envExpr = /(?<!\\)(\\*)\$\{([^${}]+)\}/g
const envExpr = /(?<!\\)(\\*)\$\{([^${}?]+)(\?)?\}/g

module.exports = (f, env) => f.replace(envExpr, (orig, esc, name) => {
const val = env[name] !== undefined ? env[name] : `$\{${name}}`
module.exports = (f, env) => f.replace(envExpr, (orig, esc, name, modifier) => {
const fallback = modifier === '?' ? '' : `$\{${name}}`
const val = env[name] !== undefined ? env[name] : fallback

// consume the escape chars that are relevant.
if (esc.length % 2) {
Expand Down
20 changes: 15 additions & 5 deletions workspaces/config/test/env-replace.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,18 @@ const env = {
bar: 'baz',
}

t.equal(envReplace('\\${foo}', env), '${foo}')
t.equal(envReplace('\\\\${foo}', env), '\\bar')
t.equal(envReplace('${baz}', env), '${baz}')
t.equal(envReplace('\\${baz}', env), '${baz}')
t.equal(envReplace('\\\\${baz}', env), '\\${baz}')
t.equal(envReplace('${foo}', env), 'bar', 'replaces defined variable')
t.equal(envReplace('${foo?}', env), 'bar', 'replaces defined variable with ? modifier')
t.equal(envReplace('${foo}${bar}', env), 'barbaz', 'replaces multiple defined variables')
t.equal(envReplace('${foo?}${baz?}', env), 'bar', 'replaces mixed defined/undefined variables with ? modifier')
t.equal(envReplace('\\${foo}', env), '${foo}', 'escapes normal variable')
t.equal(envReplace('\\\\${foo}', env), '\\bar', 'double escape allows replacement')
t.equal(envReplace('\\\\\\${foo}', env), '\\${foo}', 'triple escape prevents replacement')
t.equal(envReplace('${baz}', env), '${baz}', 'leaves undefined variable unreplaced')
t.equal(envReplace('\\${baz}', env), '${baz}', 'escapes undefined variable')
t.equal(envReplace('\\\\${baz}', env), '\\${baz}', 'double escape with undefined variable')
t.equal(envReplace('\\${foo?}', env), '${foo?}', 'escapes optional variable')
t.equal(envReplace('\\\\${foo?}', env), '\\bar', 'double escape allows optional replacement')
t.equal(envReplace('${baz?}', env), '', 'replaces undefined variable with empty string when using ? modifier')
t.equal(envReplace('\\${baz?}', env), '${baz?}', 'escapes undefined optional variable')
t.equal(envReplace('\\\\${baz?}', env), '\\', 'double escape with undefined optional variable results in empty replacement')
Loading