From a371d2902274c2a855f5648938809c5aa2a48791 Mon Sep 17 00:00:00 2001 From: hasanwhitesource Date: Thu, 28 Apr 2022 10:54:09 +0300 Subject: [PATCH 01/10] initial commit --- .../extract/__snapshots__/index.spec.ts.snap | 63 +++++++++++++++++ lib/modules/manager/npm/extract/index.spec.ts | 70 +++++++++++++++++++ lib/modules/manager/npm/extract/index.ts | 66 +++++++++++++++-- 3 files changed, 193 insertions(+), 6 deletions(-) diff --git a/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap b/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap index 780b7acd945d69..6ead7a125937e0 100644 --- a/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap +++ b/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap @@ -29,6 +29,69 @@ Object { } `; +exports[`modules/manager/npm/extract/index .extractPackageFile() extracts dependencies from overrides 1`] = ` +Object { + "constraints": Object {}, + "deps": Array [ + Object { + "currentValue": "18.0.5", + "datasource": "npm", + "depName": "@types/react", + "depType": "devDependencies", + "prettyDepType": "devDependency", + }, + Object { + "commitMessageTopic": "Node.js", + "currentValue": "8.9.2", + "datasource": "npm", + "depName": "node", + "depType": "overrides", + "prettyDepType": "overrides", + }, + Object { + "currentValue": "18.0.5", + "datasource": "npm", + "depName": "@types/react", + "depType": "overrides", + "prettyDepType": "overrides", + }, + Object { + "currentValue": "1.0.0", + "datasource": "npm", + "depType": "overrides", + "prettyDepType": "overrides", + }, + Object { + "currentValue": "1.0.0", + "datasource": "npm", + "depType": "overrides", + "prettyDepType": "overrides", + }, + Object { + "currentValue": "1.0.0", + "datasource": "npm", + "depType": "overrides", + "prettyDepType": "overrides", + }, + ], + "lernaClient": undefined, + "lernaPackages": undefined, + "managerData": Object { + "lernaJsonFile": undefined, + "yarnZeroInstall": false, + }, + "npmLock": undefined, + "npmrc": undefined, + "packageFileVersion": undefined, + "packageJsonName": undefined, + "packageJsonType": "app", + "pnpmShrinkwrap": undefined, + "skipInstalls": true, + "yarnLock": undefined, + "yarnWorkspacesPackages": undefined, +} +`; + exports[`modules/manager/npm/extract/index .extractPackageFile() extracts engines 1`] = ` Object { "constraints": Object { diff --git a/lib/modules/manager/npm/extract/index.spec.ts b/lib/modules/manager/npm/extract/index.spec.ts index 016f8cdc7274e0..94c9b9f51ae61d 100644 --- a/lib/modules/manager/npm/extract/index.spec.ts +++ b/lib/modules/manager/npm/extract/index.spec.ts @@ -669,6 +669,76 @@ describe('modules/manager/npm/extract/index', () => { ], }); }); + + it('extracts dependencies from overrides', async () => { + const content = `{ + "devDependencies": { + "@types/react": "18.0.5" + }, + "overrides": { + "node": "8.9.2", + "@types/react": "18.0.5", + "baz": { + "bar": { + "foo": "1.0.0" + } + }, + "foo2": { + ".": "1.0.0", + "bar2": "1.0.0" + } + } + }`; + const res = await npmExtract.extractPackageFile( + content, + 'package.json', + defaultConfig + ); + expect(res).toMatchSnapshot({ + deps: [ + { + depType: 'devDependencies', + depName: '@types/react', + currentValue: '18.0.5', + datasource: 'npm', + prettyDepType: 'devDependency', + }, + { + depType: 'overrides', + depName: 'node', + currentValue: '8.9.2', + datasource: 'npm', + commitMessageTopic: 'Node.js', + prettyDepType: 'overrides', + }, + { + depType: 'overrides', + depName: '@types/react', + currentValue: '18.0.5', + datasource: 'npm', + prettyDepType: 'overrides', + }, + { + prettyDepType: 'overrides', + depType: 'overrides', + currentValue: '1.0.0', + datasource: 'npm', + }, + { + prettyDepType: 'overrides', + depType: 'overrides', + currentValue: '1.0.0', + datasource: 'npm', + }, + { + prettyDepType: 'overrides', + depType: 'overrides', + currentValue: '1.0.0', + datasource: 'npm', + }, + ], + }); + }); }); describe('.postExtract()', () => { diff --git a/lib/modules/manager/npm/extract/index.ts b/lib/modules/manager/npm/extract/index.ts index 56e909caa40374..3483dbec13a3d9 100644 --- a/lib/modules/manager/npm/extract/index.ts +++ b/lib/modules/manager/npm/extract/index.ts @@ -172,6 +172,7 @@ export async function extractPackageFile( volta: 'volta', resolutions: 'resolutions', packageManager: 'packageManager', + overrides: 'overrides', }; const constraints: Record = {}; @@ -337,6 +338,55 @@ export async function extractPackageFile( return dep; } + /** + * Used when there is a json object in override block. + * i.e : + * "overrides": { + "foo": { + ".": "1.0.0", // override parent (foo) to be "1.0.0" + "bar": "1.0.0" // override bar to be 1.0.0 when its a child of foo + } + } + * object can be nested. i.e: + "overrides": { + "baz": { + "bar": { + "foo": "1.0.0" + } + } + } + * @param depType + * @param parentDep + * @param children + * @returns PackageDependency array + */ + function extractOverrideDeps( + depType: string, + parentDep: string, + children: NpmPackageDependency + ): PackageDependency[] { + const deps: PackageDependency[] = []; + // istanbul ignore if + if (is.nullOrUndefined(depType) || is.nullOrUndefined(children)) { + return deps; // extra safe check + } + + for (const [key, val] of Object.entries(children)) { + if (is.string(val)) { + let dep: PackageDependency = {}; + dep.prettyDepType = depType; + dep.depType = depType; + const depName = key === '.' ? parentDep : key; + dep = { ...dep, ...extractDependency(depType, depName, val) }; + deps.push(dep); + } else { + const depsOfObject = extractOverrideDeps(depType, parentDep, val); + deps.push(...depsOfObject); + } + } + return deps; + } + for (const depType of Object.keys(depTypes) as (keyof typeof depTypes)[]) { let dependencies = packageJson[depType]; if (dependencies) { @@ -362,13 +412,17 @@ export async function extractPackageFile( if (depName !== key) { dep.managerData = { key }; } - dep = { ...dep, ...extractDependency(depType, depName, val) }; - if (depName === 'node') { - // This is a special case for Node.js to group it together with other managers - dep.commitMessageTopic = 'Node.js'; + if (depType === 'overrides' && !is.string(val)) { + deps.push(...extractOverrideDeps(depType, depName, val)); + } else { + dep = { ...dep, ...extractDependency(depType, depName, val) }; + if (depName === 'node') { + // This is a special case for Node.js to group it together with other managers + dep.commitMessageTopic = 'Node.js'; + } + dep.prettyDepType = depTypes[depType]; + deps.push(dep); } - dep.prettyDepType = depTypes[depType]; - deps.push(dep); } } catch (err) /* istanbul ignore next */ { logger.debug({ fileName, depType, err }, 'Error parsing package.json'); From 26556772a66d14035b68908614c5b3140aa346fb Mon Sep 17 00:00:00 2001 From: hasanwhitesource Date: Thu, 28 Apr 2022 11:35:22 +0300 Subject: [PATCH 02/10] add dep type --- lib/modules/manager/npm/types.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/modules/manager/npm/types.ts b/lib/modules/manager/npm/types.ts index 1e7b6d5c93397e..e3c764e51e3802 100644 --- a/lib/modules/manager/npm/types.ts +++ b/lib/modules/manager/npm/types.ts @@ -68,4 +68,5 @@ export type NpmDepType = | 'devDependencies' | 'optionalDependencies' | 'peerDependencies' - | 'resolutions'; + | 'resolutions' + | 'overrides'; From 7cd9b7020c48e45aefca9f874f766b896c1f6302 Mon Sep 17 00:00:00 2001 From: hasanwhitesource Date: Thu, 28 Apr 2022 13:10:50 +0300 Subject: [PATCH 03/10] more types --- lib/modules/manager/npm/extract/types.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/modules/manager/npm/extract/types.ts b/lib/modules/manager/npm/extract/types.ts index 2ba3a6fc89437c..3876b490298b41 100644 --- a/lib/modules/manager/npm/extract/types.ts +++ b/lib/modules/manager/npm/extract/types.ts @@ -13,7 +13,7 @@ export interface NpmPackage extends PackageJson { _id?: any; dependenciesMeta?: DependenciesMeta; packageManager?: string; - + overrides?: OverrideDependency; volta?: PackageJson.Dependency; } @@ -31,3 +31,5 @@ export interface LockFile { export interface PnpmWorkspaceFile { packages: string[]; } + +export type OverrideDependency = Record; From 90132296a8dbd92f55675f299aaef7d89dd04ad1 Mon Sep 17 00:00:00 2001 From: hasanwhitesource Date: Thu, 28 Apr 2022 13:58:27 +0300 Subject: [PATCH 04/10] Fixed depName --- .../manager/npm/extract/__snapshots__/index.spec.ts.snap | 3 +++ lib/modules/manager/npm/extract/index.spec.ts | 3 +++ lib/modules/manager/npm/extract/index.ts | 1 + 3 files changed, 7 insertions(+) diff --git a/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap b/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap index 6ead7a125937e0..0933b1cd9c4611 100644 --- a/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap +++ b/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap @@ -58,18 +58,21 @@ Object { Object { "currentValue": "1.0.0", "datasource": "npm", + "depName": "foo", "depType": "overrides", "prettyDepType": "overrides", }, Object { "currentValue": "1.0.0", "datasource": "npm", + "depName": "foo2", "depType": "overrides", "prettyDepType": "overrides", }, Object { "currentValue": "1.0.0", "datasource": "npm", + "depName": "bar2", "depType": "overrides", "prettyDepType": "overrides", }, diff --git a/lib/modules/manager/npm/extract/index.spec.ts b/lib/modules/manager/npm/extract/index.spec.ts index 94c9b9f51ae61d..7740fffb84cd99 100644 --- a/lib/modules/manager/npm/extract/index.spec.ts +++ b/lib/modules/manager/npm/extract/index.spec.ts @@ -721,18 +721,21 @@ describe('modules/manager/npm/extract/index', () => { { prettyDepType: 'overrides', depType: 'overrides', + depName: 'foo', currentValue: '1.0.0', datasource: 'npm', }, { prettyDepType: 'overrides', depType: 'overrides', + depName: 'foo2', currentValue: '1.0.0', datasource: 'npm', }, { prettyDepType: 'overrides', depType: 'overrides', + depName: 'bar2', currentValue: '1.0.0', datasource: 'npm', }, diff --git a/lib/modules/manager/npm/extract/index.ts b/lib/modules/manager/npm/extract/index.ts index 3483dbec13a3d9..7dfea15ec73b56 100644 --- a/lib/modules/manager/npm/extract/index.ts +++ b/lib/modules/manager/npm/extract/index.ts @@ -377,6 +377,7 @@ export async function extractPackageFile( dep.prettyDepType = depType; dep.depType = depType; const depName = key === '.' ? parentDep : key; + dep.depName = depName; dep = { ...dep, ...extractDependency(depType, depName, val) }; deps.push(dep); } else { From 30e32720fba17814e896e960e6aa6894003db50c Mon Sep 17 00:00:00 2001 From: hasanwhitesource Date: Thu, 28 Apr 2022 16:08:38 +0300 Subject: [PATCH 05/10] Enhanced check --- lib/modules/manager/npm/extract/index.spec.ts | 3 +- lib/modules/manager/npm/extract/index.ts | 33 +++++++------------ 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/lib/modules/manager/npm/extract/index.spec.ts b/lib/modules/manager/npm/extract/index.spec.ts index 7740fffb84cd99..4c32b6f2b588d4 100644 --- a/lib/modules/manager/npm/extract/index.spec.ts +++ b/lib/modules/manager/npm/extract/index.spec.ts @@ -686,7 +686,8 @@ describe('modules/manager/npm/extract/index', () => { "foo2": { ".": "1.0.0", "bar2": "1.0.0" - } + }, + "emptyObject":{} } }`; const res = await npmExtract.extractPackageFile( diff --git a/lib/modules/manager/npm/extract/index.ts b/lib/modules/manager/npm/extract/index.ts index 7dfea15ec73b56..89d7e4a277a204 100644 --- a/lib/modules/manager/npm/extract/index.ts +++ b/lib/modules/manager/npm/extract/index.ts @@ -339,52 +339,41 @@ export async function extractPackageFile( } /** - * Used when there is a json object in override block. - * i.e : - * "overrides": { - "foo": { - ".": "1.0.0", // override parent (foo) to be "1.0.0" - "bar": "1.0.0" // override bar to be 1.0.0 when its a child of foo - } - } - * object can be nested. i.e: - "overrides": { - "baz": { - "bar": { - "foo": "1.0.0" - } - } - } + * Used when there is a json object as a value in overrides block. * @param depType * @param parentDep * @param children * @returns PackageDependency array */ - function extractOverrideDeps( + function extractOverrideDepsRec( depType: string, parentDep: string, children: NpmPackageDependency ): PackageDependency[] { const deps: PackageDependency[] = []; - // istanbul ignore if - if (is.nullOrUndefined(depType) || is.nullOrUndefined(children)) { - return deps; // extra safe check + if (!depType || !children || is.emptyObject(children)) { + return deps; } for (const [key, val] of Object.entries(children)) { if (is.string(val)) { + // val is a normal version let dep: PackageDependency = {}; dep.prettyDepType = depType; dep.depType = depType; + // special handling for "." key + // "." means the constraint is applied to the parent dep const depName = key === '.' ? parentDep : key; dep.depName = depName; dep = { ...dep, ...extractDependency(depType, depName, val) }; deps.push(dep); } else { - const depsOfObject = extractOverrideDeps(depType, parentDep, val); + // val is an object, run recursively. + const depsOfObject = extractOverrideDepsRec(depType, key, val); deps.push(...depsOfObject); } } + return deps; } @@ -414,7 +403,7 @@ export async function extractPackageFile( dep.managerData = { key }; } if (depType === 'overrides' && !is.string(val)) { - deps.push(...extractOverrideDeps(depType, depName, val)); + deps.push(...extractOverrideDepsRec(depType, depName, val)); } else { dep = { ...dep, ...extractDependency(depType, depName, val) }; if (depName === 'node') { From 0911876ab045d0cc5c6d4a6f4af4ef4c46a65c51 Mon Sep 17 00:00:00 2001 From: hasanwhitesource Date: Sun, 8 May 2022 08:40:55 +0300 Subject: [PATCH 06/10] refactor and handled update --- .../extract/__snapshots__/index.spec.ts.snap | 66 ------------------- lib/modules/manager/npm/extract/index.spec.ts | 23 +++++-- lib/modules/manager/npm/extract/index.ts | 41 ++++++------ lib/modules/manager/npm/extract/types.ts | 6 +- .../npm/update/dependency/index.spec.ts | 51 ++++++++++++++ .../manager/npm/update/dependency/index.ts | 57 ++++++++++++++-- lib/modules/manager/types.ts | 2 + 7 files changed, 147 insertions(+), 99 deletions(-) diff --git a/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap b/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap index 0933b1cd9c4611..780b7acd945d69 100644 --- a/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap +++ b/lib/modules/manager/npm/extract/__snapshots__/index.spec.ts.snap @@ -29,72 +29,6 @@ Object { } `; -exports[`modules/manager/npm/extract/index .extractPackageFile() extracts dependencies from overrides 1`] = ` -Object { - "constraints": Object {}, - "deps": Array [ - Object { - "currentValue": "18.0.5", - "datasource": "npm", - "depName": "@types/react", - "depType": "devDependencies", - "prettyDepType": "devDependency", - }, - Object { - "commitMessageTopic": "Node.js", - "currentValue": "8.9.2", - "datasource": "npm", - "depName": "node", - "depType": "overrides", - "prettyDepType": "overrides", - }, - Object { - "currentValue": "18.0.5", - "datasource": "npm", - "depName": "@types/react", - "depType": "overrides", - "prettyDepType": "overrides", - }, - Object { - "currentValue": "1.0.0", - "datasource": "npm", - "depName": "foo", - "depType": "overrides", - "prettyDepType": "overrides", - }, - Object { - "currentValue": "1.0.0", - "datasource": "npm", - "depName": "foo2", - "depType": "overrides", - "prettyDepType": "overrides", - }, - Object { - "currentValue": "1.0.0", - "datasource": "npm", - "depName": "bar2", - "depType": "overrides", - "prettyDepType": "overrides", - }, - ], - "lernaClient": undefined, - "lernaPackages": undefined, - "managerData": Object { - "lernaJsonFile": undefined, - "yarnZeroInstall": false, - }, - "npmLock": undefined, - "npmrc": undefined, - "packageFileVersion": undefined, - "packageJsonName": undefined, - "packageJsonType": "app", - "pnpmShrinkwrap": undefined, - "skipInstalls": true, - "yarnLock": undefined, - "yarnWorkspacesPackages": undefined, -} -`; - exports[`modules/manager/npm/extract/index .extractPackageFile() extracts engines 1`] = ` Object { "constraints": Object { diff --git a/lib/modules/manager/npm/extract/index.spec.ts b/lib/modules/manager/npm/extract/index.spec.ts index 4c32b6f2b588d4..cf13f0aa899f5a 100644 --- a/lib/modules/manager/npm/extract/index.spec.ts +++ b/lib/modules/manager/npm/extract/index.spec.ts @@ -679,6 +679,7 @@ describe('modules/manager/npm/extract/index', () => { "node": "8.9.2", "@types/react": "18.0.5", "baz": { + "node": "8.9.2", "bar": { "foo": "1.0.0" } @@ -695,7 +696,7 @@ describe('modules/manager/npm/extract/index', () => { 'package.json', defaultConfig ); - expect(res).toMatchSnapshot({ + expect(res).toMatchObject({ deps: [ { depType: 'devDependencies', @@ -720,23 +721,31 @@ describe('modules/manager/npm/extract/index', () => { prettyDepType: 'overrides', }, { - prettyDepType: 'overrides', - depType: 'overrides', + depName: 'node', + groupName: 'overrides', + parents: ['baz'], + commitMessageTopic: 'Node.js', + currentValue: '8.9.2', + datasource: 'npm', + }, + { depName: 'foo', + groupName: 'overrides', + parents: ['baz', 'bar'], currentValue: '1.0.0', datasource: 'npm', }, { - prettyDepType: 'overrides', - depType: 'overrides', depName: 'foo2', + groupName: 'overrides', + parents: ['foo2'], currentValue: '1.0.0', datasource: 'npm', }, { - prettyDepType: 'overrides', - depType: 'overrides', depName: 'bar2', + groupName: 'overrides', + parents: ['foo2'], currentValue: '1.0.0', datasource: 'npm', }, diff --git a/lib/modules/manager/npm/extract/index.ts b/lib/modules/manager/npm/extract/index.ts index 89d7e4a277a204..8d6f854520eebf 100644 --- a/lib/modules/manager/npm/extract/index.ts +++ b/lib/modules/manager/npm/extract/index.ts @@ -340,40 +340,41 @@ export async function extractPackageFile( /** * Used when there is a json object as a value in overrides block. - * @param depType - * @param parentDep - * @param children + * @param parents + * @param child * @returns PackageDependency array */ function extractOverrideDepsRec( - depType: string, - parentDep: string, - children: NpmPackageDependency + parents: string[], + child: NpmPackageDependency ): PackageDependency[] { const deps: PackageDependency[] = []; - if (!depType || !children || is.emptyObject(children)) { + if (!child || is.emptyObject(child)) { return deps; } - - for (const [key, val] of Object.entries(children)) { + for (const [key, val] of Object.entries(child)) { if (is.string(val)) { - // val is a normal version - let dep: PackageDependency = {}; - dep.prettyDepType = depType; - dep.depType = depType; + const dep: PackageDependency = {}; // special handling for "." key // "." means the constraint is applied to the parent dep - const depName = key === '.' ? parentDep : key; - dep.depName = depName; - dep = { ...dep, ...extractDependency(depType, depName, val) }; - deps.push(dep); + dep.depName = key === '.' ? parents[parents.length - 1] : key; + dep.groupName = 'overrides'; + dep.parents = parents.slice(); // set parents for dependency + if (dep.depName === 'node') { + dep.commitMessageTopic = 'Node.js'; + } + deps.push({ + ...dep, + ...extractDependency('overrides', dep.depName, val), + }); } else { // val is an object, run recursively. - const depsOfObject = extractOverrideDepsRec(depType, key, val); + parents.push(key); + const depsOfObject = extractOverrideDepsRec(parents, val); deps.push(...depsOfObject); } } - + parents.pop(); return deps; } @@ -403,7 +404,7 @@ export async function extractPackageFile( dep.managerData = { key }; } if (depType === 'overrides' && !is.string(val)) { - deps.push(...extractOverrideDepsRec(depType, depName, val)); + deps.push(...extractOverrideDepsRec([depName], val)); } else { dep = { ...dep, ...extractDependency(depType, depName, val) }; if (depName === 'node') { diff --git a/lib/modules/manager/npm/extract/types.ts b/lib/modules/manager/npm/extract/types.ts index 3876b490298b41..120709a99533c6 100644 --- a/lib/modules/manager/npm/extract/types.ts +++ b/lib/modules/manager/npm/extract/types.ts @@ -32,4 +32,8 @@ export interface PnpmWorkspaceFile { packages: string[]; } -export type OverrideDependency = Record; +export type OverrideDependency = Record; + +export interface RecursiveOverride { + [P: string]: RecursiveOverride; +} diff --git a/lib/modules/manager/npm/update/dependency/index.spec.ts b/lib/modules/manager/npm/update/dependency/index.spec.ts index c5a5187886110a..ba5227e811007e 100644 --- a/lib/modules/manager/npm/update/dependency/index.spec.ts +++ b/lib/modules/manager/npm/update/dependency/index.spec.ts @@ -296,5 +296,56 @@ describe('modules/manager/npm/update/dependency/index', () => { }); expect(testContent).toEqual(outputContent); }); + + it('handles override dependency', () => { + const upgrade = { + depType: 'overrides', + depName: 'typescript', + newValue: '0.60.0', + }; + const overrideDependencies = `{ + "overrides": { + "typescript": "0.0.5" + } + }`; + const expected = `{ + "overrides": { + "typescript": "0.60.0" + } + }`; + const testContent = npmUpdater.updateDependency({ + fileContent: overrideDependencies, + upgrade, + }); + expect(testContent).toEqual(expected); + }); + + it('handles override dependency object', () => { + const upgrade = { + depType: 'overrides', + depName: 'typescript', + newValue: '0.60.0', + parents: ['awesome-typescript-loader'], + }; + const overrideDependencies = `{ + "overrides": { + "awesome-typescript-loader": { + "typescript": "3.0.0" + } + } + }`; + const expected = `{ + "overrides": { + "awesome-typescript-loader": { + "typescript": "0.60.0" + } + } + }`; + const testContent = npmUpdater.updateDependency({ + fileContent: overrideDependencies, + upgrade, + }); + expect(testContent).toEqual(expected); + }); }); }); diff --git a/lib/modules/manager/npm/update/dependency/index.ts b/lib/modules/manager/npm/update/dependency/index.ts index 5eb8d9329a0efe..f7905741816bd4 100644 --- a/lib/modules/manager/npm/update/dependency/index.ts +++ b/lib/modules/manager/npm/update/dependency/index.ts @@ -3,7 +3,12 @@ import { logger } from '../../../../../logger'; import { escapeRegExp, regEx } from '../../../../../util/regex'; import { matchAt, replaceAt } from '../../../../../util/string'; import type { UpdateDependencyConfig } from '../../../types'; -import type { DependenciesMeta, NpmPackage } from '../../extract/types'; +import type { + DependenciesMeta, + NpmPackage, + OverrideDependency, + RecursiveOverride, +} from '../../extract/types'; import type { NpmDepType } from '../../types'; function renameObjKey( @@ -28,7 +33,8 @@ function replaceAsString( depType: NpmDepType | 'dependenciesMeta' | 'packageManager', depName: string, oldValue: string, - newValue: string + newValue: string, + parents?: string[] ): string { if (depType === 'packageManager') { parsedContents[depType] = newValue; @@ -46,6 +52,16 @@ function replaceAsString( newValue ); } + } else if (parents && depType === 'overrides') { + // there is an object as a value in overrides block + const { depObjectReference, overrideDepName } = overrideDepPosition( + parsedContents[depType]!, + parents, + depName + ); + if (depObjectReference) { + depObjectReference[overrideDepName] = newValue; + } } else { // The old value is the version of the dependency parsedContents[depType]![depName] = newValue; @@ -122,8 +138,18 @@ export function updateDependency({ if (depType === 'packageManager') { oldVersion = parsedContents[depType]; newValue = `${depName}@${newValue}`; + } else if (upgrade.parents && depType === 'overrides') { + // old version when there is an object as a value in overrides block + const { depObjectReference, overrideDepName } = overrideDepPosition( + parsedContents[depType]!, + upgrade.parents, + depName + ); + if (depObjectReference) { + oldVersion = depObjectReference[overrideDepName]!; + } } else { - oldVersion = parsedContents[depType as NpmDepType]![depName]; + oldVersion = parsedContents[depType as NpmDepType]![depName] as string; } if (oldVersion === newValue) { logger.trace('Version is already updated'); @@ -137,7 +163,8 @@ export function updateDependency({ depType as NpmDepType, depName, oldVersion!, - newValue! + newValue!, + upgrade.parents ); if (upgrade.newName) { newFileContent = replaceAsString( @@ -146,7 +173,8 @@ export function updateDependency({ depType as NpmDepType, depName, depName, - upgrade.newName + upgrade.newName, + upgrade.parents ); } /* eslint-enable @typescript-eslint/no-unnecessary-type-assertion */ @@ -223,3 +251,22 @@ export function updateDependency({ return null; } } +function overrideDepPosition( + overrideBlock: OverrideDependency, + parents: string[], + depName: string +): { + depObjectReference: Record; + overrideDepName: string; +} { + const lastParent = parents[parents.length - 1]; + let overrideDep: OverrideDependency = overrideBlock; + for (const parent of parents) { + if (overrideDep) { + overrideDep = overrideDep[parent]! as Record; + } + } + const overrideDepName = depName === lastParent ? '.' : depName; + const depObjectReference = overrideDep as Record; + return { depObjectReference, overrideDepName }; +} diff --git a/lib/modules/manager/types.ts b/lib/modules/manager/types.ts index 70936babec5447..61232fec7cf427 100644 --- a/lib/modules/manager/types.ts +++ b/lib/modules/manager/types.ts @@ -173,6 +173,7 @@ export interface PackageDependency> extends Package { editFile?: string; separateMinorPatch?: boolean; extractVersion?: string; + parents?: string[]; } export interface Upgrade> @@ -195,6 +196,7 @@ export interface Upgrade> isLockFileMaintenance?: boolean; isRemediation?: boolean; isVulnerabilityAlert?: boolean; + parents?: string[]; } export interface ArtifactError { From 4653d0ae23bd31c2a381bf137cf831df04176138 Mon Sep 17 00:00:00 2001 From: hasanwhitesource Date: Mon, 9 May 2022 13:33:12 +0300 Subject: [PATCH 07/10] refactor and changes with new interface --- lib/modules/manager/npm/extract/index.spec.ts | 4 --- lib/modules/manager/npm/extract/index.ts | 22 ++++++------ lib/modules/manager/npm/types.ts | 2 +- .../manager/npm/update/dependency/index.ts | 35 ++++++++++++------- lib/modules/manager/types.ts | 2 -- 5 files changed, 36 insertions(+), 29 deletions(-) diff --git a/lib/modules/manager/npm/extract/index.spec.ts b/lib/modules/manager/npm/extract/index.spec.ts index cf13f0aa899f5a..332a374335de5b 100644 --- a/lib/modules/manager/npm/extract/index.spec.ts +++ b/lib/modules/manager/npm/extract/index.spec.ts @@ -722,7 +722,6 @@ describe('modules/manager/npm/extract/index', () => { }, { depName: 'node', - groupName: 'overrides', parents: ['baz'], commitMessageTopic: 'Node.js', currentValue: '8.9.2', @@ -730,21 +729,18 @@ describe('modules/manager/npm/extract/index', () => { }, { depName: 'foo', - groupName: 'overrides', parents: ['baz', 'bar'], currentValue: '1.0.0', datasource: 'npm', }, { depName: 'foo2', - groupName: 'overrides', parents: ['foo2'], currentValue: '1.0.0', datasource: 'npm', }, { depName: 'bar2', - groupName: 'overrides', parents: ['foo2'], currentValue: '1.0.0', datasource: 'npm', diff --git a/lib/modules/manager/npm/extract/index.ts b/lib/modules/manager/npm/extract/index.ts index 1e528765e215e2..ecc88e5e8b3690 100644 --- a/lib/modules/manager/npm/extract/index.ts +++ b/lib/modules/manager/npm/extract/index.ts @@ -347,7 +347,7 @@ export async function extractPackageFile( */ function extractOverrideDepsRec( parents: string[], - child: NpmPackageDependency + child: NpmManagerData ): PackageDependency[] { const deps: PackageDependency[] = []; if (!child || is.emptyObject(child)) { @@ -355,15 +355,13 @@ export async function extractPackageFile( } for (const [key, val] of Object.entries(child)) { if (is.string(val)) { - const dep: PackageDependency = {}; + const dep: NpmManagerData = {}; // special handling for "." key // "." means the constraint is applied to the parent dep dep.depName = key === '.' ? parents[parents.length - 1] : key; - dep.groupName = 'overrides'; + dep.depType = 'overrides'; dep.parents = parents.slice(); // set parents for dependency - if (dep.depName === 'node') { - dep.commitMessageTopic = 'Node.js'; - } + nodeCommitTopic(dep); deps.push({ ...dep, ...extractDependency('overrides', dep.depName, val), @@ -408,10 +406,7 @@ export async function extractPackageFile( deps.push(...extractOverrideDepsRec([depName], val)); } else { dep = { ...dep, ...extractDependency(depType, depName, val) }; - if (depName === 'node') { - // This is a special case for Node.js to group it together with other managers - dep.commitMessageTopic = 'Node.js'; - } + nodeCommitTopic(dep); dep.prettyDepType = depTypes[depType]; deps.push(dep); } @@ -505,3 +500,10 @@ export async function extractAllPackageFiles( await postExtract(npmFiles, !!config.updateInternalDeps); return npmFiles; } + +function nodeCommitTopic(dep: NpmManagerData): void { + // This is a special case for Node.js to group it together with other managers + if (dep.depName === 'node') { + dep.commitMessageTopic = 'Node.js'; + } +} diff --git a/lib/modules/manager/npm/types.ts b/lib/modules/manager/npm/types.ts index c3f5c7789b4545..78f08b69a2cf14 100644 --- a/lib/modules/manager/npm/types.ts +++ b/lib/modules/manager/npm/types.ts @@ -74,7 +74,7 @@ export type NpmDepType = export interface NpmManagerData extends Record { hasPackageManager?: boolean; - lernaJsonFile?: string; + parents?: string[]; yarnZeroInstall?: boolean; } diff --git a/lib/modules/manager/npm/update/dependency/index.ts b/lib/modules/manager/npm/update/dependency/index.ts index f7905741816bd4..1fc18785bb40a4 100644 --- a/lib/modules/manager/npm/update/dependency/index.ts +++ b/lib/modules/manager/npm/update/dependency/index.ts @@ -1,3 +1,4 @@ +import is from '@sindresorhus/is'; import { dequal } from 'dequal'; import { logger } from '../../../../../logger'; import { escapeRegExp, regEx } from '../../../../../util/regex'; @@ -9,7 +10,7 @@ import type { OverrideDependency, RecursiveOverride, } from '../../extract/types'; -import type { NpmDepType } from '../../types'; +import type { NpmDepType, NpmManagerData } from '../../types'; function renameObjKey( oldObj: DependenciesMeta, @@ -133,20 +134,25 @@ export function updateDependency({ logger.debug(`npm.updateDependency(): ${depType}.${depName} = ${newValue}`); try { const parsedContents: NpmPackage = JSON.parse(fileContent); + let overrideDepParents: string[] | undefined = undefined; // Save the old version let oldVersion: string | undefined; if (depType === 'packageManager') { oldVersion = parsedContents[depType]; newValue = `${depName}@${newValue}`; - } else if (upgrade.parents && depType === 'overrides') { - // old version when there is an object as a value in overrides block - const { depObjectReference, overrideDepName } = overrideDepPosition( - parsedContents[depType]!, - upgrade.parents, - depName - ); - if (depObjectReference) { - oldVersion = depObjectReference[overrideDepName]!; + } else if (isOverrideObject(upgrade)) { + const npmOverrideDep: NpmManagerData = upgrade; + overrideDepParents = npmOverrideDep.parents; + if (overrideDepParents) { + // old version when there is an object as a value in overrides block + const { depObjectReference, overrideDepName } = overrideDepPosition( + parsedContents['overrides']!, + overrideDepParents, + depName + ); + if (depObjectReference) { + oldVersion = depObjectReference[overrideDepName]!; + } } } else { oldVersion = parsedContents[depType as NpmDepType]![depName] as string; @@ -164,7 +170,7 @@ export function updateDependency({ depName, oldVersion!, newValue!, - upgrade.parents + overrideDepParents ); if (upgrade.newName) { newFileContent = replaceAsString( @@ -174,7 +180,7 @@ export function updateDependency({ depName, depName, upgrade.newName, - upgrade.parents + overrideDepParents ); } /* eslint-enable @typescript-eslint/no-unnecessary-type-assertion */ @@ -259,6 +265,7 @@ function overrideDepPosition( depObjectReference: Record; overrideDepName: string; } { + // get override dep position when its nested in an object const lastParent = parents[parents.length - 1]; let overrideDep: OverrideDependency = overrideBlock; for (const parent of parents) { @@ -270,3 +277,7 @@ function overrideDepPosition( const depObjectReference = overrideDep as Record; return { depObjectReference, overrideDepName }; } + +function isOverrideObject(upgrade: NpmManagerData): boolean { + return !is.undefined(upgrade.parents) && upgrade.depType === 'overrides'; +} diff --git a/lib/modules/manager/types.ts b/lib/modules/manager/types.ts index 7ea62f7f849d10..0a4f314fad4eca 100644 --- a/lib/modules/manager/types.ts +++ b/lib/modules/manager/types.ts @@ -174,7 +174,6 @@ export interface PackageDependency> extends Package { editFile?: string; separateMinorPatch?: boolean; extractVersion?: string; - parents?: string[]; } export interface Upgrade> @@ -197,7 +196,6 @@ export interface Upgrade> isLockFileMaintenance?: boolean; isRemediation?: boolean; isVulnerabilityAlert?: boolean; - parents?: string[]; } export interface ArtifactError { From 45b85c9f97f4a266b5ef0c645430cff2a70f7e22 Mon Sep 17 00:00:00 2001 From: hasanwhitesource Date: Mon, 9 May 2022 15:47:48 +0300 Subject: [PATCH 08/10] simplify to type --- lib/modules/manager/npm/extract/types.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/modules/manager/npm/extract/types.ts b/lib/modules/manager/npm/extract/types.ts index 120709a99533c6..0312418f4e54bd 100644 --- a/lib/modules/manager/npm/extract/types.ts +++ b/lib/modules/manager/npm/extract/types.ts @@ -32,8 +32,6 @@ export interface PnpmWorkspaceFile { packages: string[]; } -export type OverrideDependency = Record; +export type OverrideDependency = Record; -export interface RecursiveOverride { - [P: string]: RecursiveOverride; -} +export type RecursiveOverride = string | { [_: string]: RecursiveOverride }; From 2349852ca995e09dfb9f4b95dc5409d0ffffa87e Mon Sep 17 00:00:00 2001 From: hasanwhitesource Date: Wed, 11 May 2022 15:06:45 +0300 Subject: [PATCH 09/10] refactoring --- lib/modules/manager/npm/extract/index.spec.ts | 8 ++--- lib/modules/manager/npm/extract/index.ts | 31 ++++++++++--------- lib/modules/manager/npm/types.ts | 3 +- .../npm/update/dependency/index.spec.ts | 2 +- .../manager/npm/update/dependency/index.ts | 12 ++++--- 5 files changed, 30 insertions(+), 26 deletions(-) diff --git a/lib/modules/manager/npm/extract/index.spec.ts b/lib/modules/manager/npm/extract/index.spec.ts index 332a374335de5b..73b181cca06575 100644 --- a/lib/modules/manager/npm/extract/index.spec.ts +++ b/lib/modules/manager/npm/extract/index.spec.ts @@ -722,26 +722,26 @@ describe('modules/manager/npm/extract/index', () => { }, { depName: 'node', - parents: ['baz'], + managerData: { parents: ['baz'] }, commitMessageTopic: 'Node.js', currentValue: '8.9.2', datasource: 'npm', }, { depName: 'foo', - parents: ['baz', 'bar'], + managerData: { parents: ['baz', 'bar'] }, currentValue: '1.0.0', datasource: 'npm', }, { depName: 'foo2', - parents: ['foo2'], + managerData: { parents: ['foo2'] }, currentValue: '1.0.0', datasource: 'npm', }, { depName: 'bar2', - parents: ['foo2'], + managerData: { parents: ['foo2'] }, currentValue: '1.0.0', datasource: 'npm', }, diff --git a/lib/modules/manager/npm/extract/index.ts b/lib/modules/manager/npm/extract/index.ts index ecc88e5e8b3690..3d5ba8e4a6a5b5 100644 --- a/lib/modules/manager/npm/extract/index.ts +++ b/lib/modules/manager/npm/extract/index.ts @@ -353,23 +353,26 @@ export async function extractPackageFile( if (!child || is.emptyObject(child)) { return deps; } - for (const [key, val] of Object.entries(child)) { - if (is.string(val)) { - const dep: NpmManagerData = {}; - // special handling for "." key + for (const [overrideName, versionValue] of Object.entries(child)) { + if (is.string(versionValue)) { + // special handling for "." override depenency name // "." means the constraint is applied to the parent dep - dep.depName = key === '.' ? parents[parents.length - 1] : key; - dep.depType = 'overrides'; - dep.parents = parents.slice(); // set parents for dependency - nodeCommitTopic(dep); + const currDepName = + overrideName === '.' ? parents[parents.length - 1] : overrideName; + const dep: PackageDependency = { + depName: currDepName, + depType: 'overrides', + managerData: { parents: parents.slice() }, // set parents for dependency + }; + setNodeCommitTopic(dep); deps.push({ ...dep, - ...extractDependency('overrides', dep.depName, val), + ...extractDependency('overrides', currDepName, versionValue), }); } else { - // val is an object, run recursively. - parents.push(key); - const depsOfObject = extractOverrideDepsRec(parents, val); + // versionValue is an object, run recursively. + parents.push(overrideName); + const depsOfObject = extractOverrideDepsRec(parents, versionValue); deps.push(...depsOfObject); } } @@ -406,7 +409,7 @@ export async function extractPackageFile( deps.push(...extractOverrideDepsRec([depName], val)); } else { dep = { ...dep, ...extractDependency(depType, depName, val) }; - nodeCommitTopic(dep); + setNodeCommitTopic(dep); dep.prettyDepType = depTypes[depType]; deps.push(dep); } @@ -501,7 +504,7 @@ export async function extractAllPackageFiles( return npmFiles; } -function nodeCommitTopic(dep: NpmManagerData): void { +function setNodeCommitTopic(dep: NpmManagerData): void { // This is a special case for Node.js to group it together with other managers if (dep.depName === 'node') { dep.commitMessageTopic = 'Node.js'; diff --git a/lib/modules/manager/npm/types.ts b/lib/modules/manager/npm/types.ts index 78f08b69a2cf14..ed7a7adbad7d53 100644 --- a/lib/modules/manager/npm/types.ts +++ b/lib/modules/manager/npm/types.ts @@ -69,8 +69,7 @@ export type NpmDepType = | 'optionalDependencies' | 'overrides' | 'peerDependencies' - | 'resolutions' - | 'overrides'; + | 'resolutions'; export interface NpmManagerData extends Record { hasPackageManager?: boolean; diff --git a/lib/modules/manager/npm/update/dependency/index.spec.ts b/lib/modules/manager/npm/update/dependency/index.spec.ts index ba5227e811007e..f746ba1644f3ae 100644 --- a/lib/modules/manager/npm/update/dependency/index.spec.ts +++ b/lib/modules/manager/npm/update/dependency/index.spec.ts @@ -325,7 +325,7 @@ describe('modules/manager/npm/update/dependency/index', () => { depType: 'overrides', depName: 'typescript', newValue: '0.60.0', - parents: ['awesome-typescript-loader'], + managerData: { parents: ['awesome-typescript-loader'] }, }; const overrideDependencies = `{ "overrides": { diff --git a/lib/modules/manager/npm/update/dependency/index.ts b/lib/modules/manager/npm/update/dependency/index.ts index 1fc18785bb40a4..08675e88e51e6c 100644 --- a/lib/modules/manager/npm/update/dependency/index.ts +++ b/lib/modules/manager/npm/update/dependency/index.ts @@ -3,7 +3,7 @@ import { dequal } from 'dequal'; import { logger } from '../../../../../logger'; import { escapeRegExp, regEx } from '../../../../../util/regex'; import { matchAt, replaceAt } from '../../../../../util/string'; -import type { UpdateDependencyConfig } from '../../../types'; +import type { UpdateDependencyConfig, Upgrade } from '../../../types'; import type { DependenciesMeta, NpmPackage, @@ -141,8 +141,7 @@ export function updateDependency({ oldVersion = parsedContents[depType]; newValue = `${depName}@${newValue}`; } else if (isOverrideObject(upgrade)) { - const npmOverrideDep: NpmManagerData = upgrade; - overrideDepParents = npmOverrideDep.parents; + overrideDepParents = managerData?.parents; if (overrideDepParents) { // old version when there is an object as a value in overrides block const { depObjectReference, overrideDepName } = overrideDepPosition( @@ -278,6 +277,9 @@ function overrideDepPosition( return { depObjectReference, overrideDepName }; } -function isOverrideObject(upgrade: NpmManagerData): boolean { - return !is.undefined(upgrade.parents) && upgrade.depType === 'overrides'; +function isOverrideObject(upgrade: Upgrade): boolean { + return ( + !is.undefined(upgrade.managerData?.parents) && + upgrade.depType === 'overrides' + ); } From 74aa510c94464da23328b84e38e458b14f39707a Mon Sep 17 00:00:00 2001 From: hasanwhitesource Date: Mon, 16 May 2022 11:52:39 +0300 Subject: [PATCH 10/10] added eslint ignore fix problem --- lib/modules/manager/npm/update/dependency/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/modules/manager/npm/update/dependency/index.ts b/lib/modules/manager/npm/update/dependency/index.ts index 08675e88e51e6c..c2231d7c560e15 100644 --- a/lib/modules/manager/npm/update/dependency/index.ts +++ b/lib/modules/manager/npm/update/dependency/index.ts @@ -154,6 +154,7 @@ export function updateDependency({ } } } else { + // eslint-disable @typescript-eslint/no-unnecessary-type-assertion oldVersion = parsedContents[depType as NpmDepType]![depName] as string; } if (oldVersion === newValue) { @@ -279,7 +280,7 @@ function overrideDepPosition( function isOverrideObject(upgrade: Upgrade): boolean { return ( - !is.undefined(upgrade.managerData?.parents) && + is.array(upgrade.managerData?.parents, is.nonEmptyStringAndNotWhitespace) && upgrade.depType === 'overrides' ); }