Skip to content

Commit 5d897e3

Browse files
committed
Fix overrides for direct dependencies in npm
1 parent 6e31c9f commit 5d897e3

File tree

1 file changed

+31
-8
lines changed

1 file changed

+31
-8
lines changed

src/commands/optimize.ts

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -189,25 +189,42 @@ async function addOverrides(
189189
if (overridesData) {
190190
overridesDataObjects.push(overridesData)
191191
}
192+
const aliasMap = new Map<string, string>()
192193
for (const { 1: data } of availableOverrides) {
193194
const { name: regPkgName, package: origPkgName, version } = data
194195
for (const { 1: depObj } of depEntries) {
195-
const pkgSpec = depObj[origPkgName]
196+
let pkgSpec = depObj[origPkgName]
196197
if (pkgSpec) {
197-
if (!pkgSpec.startsWith(`npm:${regPkgName}@`)) {
198+
// Add package aliases for direct dependencies to avoid npm EOVERRIDE errors.
199+
// https://docs.npmjs.com/cli/v8/using-npm/package-spec#aliases
200+
const overrideSpecPrefix = `npm:${regPkgName}@`
201+
if (!pkgSpec.startsWith(overrideSpecPrefix)) {
202+
aliasMap.set(regPkgName, pkgSpec)
203+
} else {
198204
packageNames.add(regPkgName)
199-
depObj[origPkgName] = `npm:${regPkgName}@^${version}`
205+
pkgSpec = `${overrideSpecPrefix}^${version}`
206+
depObj[origPkgName] = pkgSpec
200207
}
208+
aliasMap.set(origPkgName, pkgSpec)
201209
}
202210
}
203-
for (const { overrides } of overridesDataObjects) {
211+
for (const { type, overrides } of overridesDataObjects) {
204212
if (
205213
overrides &&
206214
!hasOwn(overrides, origPkgName) &&
207215
lockIncludes(lockSrc, origPkgName)
208216
) {
209217
packageNames.add(regPkgName)
210-
overrides[origPkgName] = `npm:${regPkgName}@^${semver.major(version)}`
218+
overrides[origPkgName] =
219+
// With npm you may not set an override for a package that you directly
220+
// depend on unless both the dependency and the override itself share
221+
// the exact same spec. To make this limitation easier to deal with,
222+
// overrides may also be defined as a reference to a spec for a direct
223+
// dependency by prefixing the name of the package you wish the version
224+
// to match with a $.
225+
// https://docs.npmjs.com/cli/v8/configuring-npm/package-json#overrides
226+
(type === 'npm' && aliasMap.has(origPkgName) && `$${origPkgName}`) ||
227+
`npm:${regPkgName}@^${semver.major(version)}`
211228
}
212229
}
213230
}
@@ -252,7 +269,9 @@ export const optimize: CliSubcommand = {
252269
}
253270
})
254271
if (!supported) {
255-
console.log(`✘ ${COMMAND_TITLE}: Package engines.node range is not supported`)
272+
console.log(
273+
`✘ ${COMMAND_TITLE}: Package engines.node range is not supported`
274+
)
256275
return
257276
}
258277
const lockName = lockPath ? path.basename(lockPath) : 'lock file'
@@ -265,7 +284,9 @@ export const optimize: CliSubcommand = {
265284
return
266285
}
267286
if (lockPath && path.relative(cwd, lockPath).startsWith('.')) {
268-
console.log(`⚠️ ${COMMAND_TITLE}: Package ${lockName} found at ${lockPath}`)
287+
console.log(
288+
`⚠️ ${COMMAND_TITLE}: Package ${lockName} found at ${lockPath}`
289+
)
269290
}
270291

271292
const aoState: AddOverridesState = {
@@ -326,7 +347,9 @@ export const optimize: CliSubcommand = {
326347
}
327348
} catch {
328349
spinner.stop()
329-
console.log(`✘ ${COMMAND_TITLE}: ${agent} install failed to update ${lockName}`)
350+
console.log(
351+
`✘ ${COMMAND_TITLE}: ${agent} install failed to update ${lockName}`
352+
)
330353
}
331354
}
332355
}

0 commit comments

Comments
 (0)