Skip to content

feat: add support for optional env var replacements in .npmrc #8359

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: latest
Choose a base branch
from

Conversation

aczekajski
Copy link

This solves problem described in #8335 in a backwards-compatible way.

This PR adds possibility to have env var replacements in .npmrc configs written as ${VAR?} which will cause them to get replaced with an empty string if the variable is not defined. Old behavior where undefined variables are left unreplaced is not changed.

References

Fixes #8335

@aczekajski aczekajski requested a review from a team as a code owner June 12, 2025 13:00
@aczekajski aczekajski changed the title feat: add suppor for optional env var replacements in .npmrc feat: add support for optional env var replacements in .npmrc Jun 12, 2025
@wraithgar
Copy link
Member

I know linting checks for this but I also manually validated via https://devina.io/redos-checker

redos check for new regex changes showing there is no redos vulnerability

@owlstronaut owlstronaut self-assigned this Jul 1, 2025
Copy link
Contributor

@owlstronaut owlstronaut left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a great feature! Just a couple nits

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
variable name they can be forced to evaluate to an empty string instead.For
variable name they can be forced to evaluate to an empty string instead. For

@@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// optional "?" modifier can be used like this: ${ENV?} so in case of the variable being not defined, it evaluates into empty string
// optional "?" modifier can be used like this: ${ENV?} so in case of the variable being not defined, it evaluates into empty string.

Comment on lines 9 to +19
t.equal(envReplace('\\${foo}', env), '${foo}')
t.equal(envReplace('\\\\${foo}', env), '\\bar')
t.equal(envReplace('\\\\\\${foo}', env), '\\${foo}')
t.equal(envReplace('${baz}', env), '${baz}')
t.equal(envReplace('\\${baz}', env), '${baz}')
t.equal(envReplace('\\\\${baz}', env), '\\${baz}')
t.equal(envReplace('\\${foo?}', env), '${foo?}')
t.equal(envReplace('\\\\${foo?}', env), '\\bar')
t.equal(envReplace('${baz?}', env), '')
t.equal(envReplace('\\${baz?}', env), '${baz?}')
t.equal(envReplace('\\\\${baz?}', env), '\\')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion for a couple more tests here - and what do you think about adding a description, I find it way easier to read:

Suggested change
t.equal(envReplace('\\${foo}', env), '${foo}')
t.equal(envReplace('\\\\${foo}', env), '\\bar')
t.equal(envReplace('\\\\\\${foo}', env), '\\${foo}')
t.equal(envReplace('${baz}', env), '${baz}')
t.equal(envReplace('\\${baz}', env), '${baz}')
t.equal(envReplace('\\\\${baz}', env), '\\${baz}')
t.equal(envReplace('\\${foo?}', env), '${foo?}')
t.equal(envReplace('\\\\${foo?}', env), '\\bar')
t.equal(envReplace('${baz?}', env), '')
t.equal(envReplace('\\${baz?}', env), '${baz?}')
t.equal(envReplace('\\\\${baz?}', env), '\\')
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')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[BUG] Impossible to reliably passthrough NODE_OPTIONS in .npmrc
3 participants