Skip to content
Open
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
1 change: 1 addition & 0 deletions workspaces/config/lib/definitions/definitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -1353,6 +1353,7 @@ const definitions = {
hint: '<days>',
type: [null, Number],
exclusive: ['before'],
envExport: false,
description: `
If set, npm will build the npm tree such that only versions that were
available more than the given number of days ago will be installed. If
Expand Down
9 changes: 8 additions & 1 deletion workspaces/config/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -582,14 +582,21 @@ class Config {
}
} else {
conf.raw = obj
for (const [key, value] of Object.entries(obj)) {
outer: for (const [key, value] of Object.entries(obj)) {
const k = envReplace(key, this.env)
const v = this.parseField(value, k)
if (where !== 'default') {
this.#checkDeprecated(k)
if (this.definitions[key]?.exclusive) {
for (const exclusive of this.definitions[key].exclusive) {
if (!this.isDefault(exclusive)) {
// when loading from env, skip only if sibling was explicitly set via CLI
if (where === 'env') {
const cliData = this.data.get('cli').data
if (Object.hasOwn(cliData, exclusive)) {
continue outer
}
}
throw new TypeError(`--${key} cannot be provided when using --${exclusive}`)
}
}
Expand Down
74 changes: 74 additions & 0 deletions workspaces/config/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1437,6 +1437,80 @@ t.test('exclusive options conflict', async t => {
})
})

t.test('exclusive options both from env still conflict', async t => {
const path = t.testdir()
const config = new Config({
env: {
npm_config_aaa: 'true',
npm_config_zzz: 'true',
},
npmPath: __dirname,
argv: [
process.execPath,
__filename,
],
cwd: join(`${path}/project`),
shorthands,
definitions: {
...definitions,
...createDef('aaa', {
default: false,
type: Boolean,
description: 'aaa',
exclusive: ['zzz'],
}),
...createDef('zzz', {
default: false,
type: Boolean,
description: 'zzz',
exclusive: ['aaa'],
}),
},
flatten,
})
await t.rejects(config.load(), {
name: 'TypeError',
message: '--zzz cannot be provided when using --aaa',
})
})

t.test('exclusive env option is skipped when sibling is set via CLI', async t => {
const path = t.testdir()
const config = new Config({
env: {
npm_config_truth: 'true',
},
npmPath: __dirname,
argv: [
process.execPath,
__filename,
'--lie=true',
],
cwd: join(`${path}/project`),
shorthands,
definitions: {
...definitions,
...createDef('truth', {
default: false,
type: Boolean,
description: 'The Truth',
exclusive: ['lie'],
}),
...createDef('lie', {
default: false,
type: Boolean,
description: 'A Lie',
exclusive: ['truth'],
}),
},
flatten,
})
// should not throw — env `truth` is skipped because `lie` was set via CLI
await t.resolves(config.load())
t.equal(config.get('lie'), true, 'CLI lie is set')
t.equal(config.get('truth'), false, 'env truth is skipped, remains default')
})

t.test('env-replaced config from files is not clobbered when saving', async (t) => {
const path = t.testdir()
const opts = {
Expand Down
Loading