Skip to content

Commit f7d9fcf

Browse files
committed
fix(plugin-js-packages): parse yarn 4 audit output correctly
1 parent 73c2989 commit f7d9fcf

File tree

17 files changed

+258
-117
lines changed

17 files changed

+258
-117
lines changed

packages/plugin-js-packages/src/lib/package-managers/package-managers.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import type { PackageManagerId } from '../config.js';
22
import { npmPackageManager } from './npm/npm.js';
33
import { pnpmPackageManager } from './pnpm/pnpm.js';
44
import type { PackageManager } from './types.js';
5-
import { yarnv1PackageManager } from './yarn-classic/yarn-classic.js';
6-
import { yarnv2PackageManager } from './yarn-modern/yarn-modern.js';
5+
import { yarnClassicPackageManager } from './yarn-classic/yarn-classic.js';
6+
import { yarnModernPackageManager } from './yarn-modern/yarn-modern.js';
77

88
export const packageManagers: Record<PackageManagerId, PackageManager> = {
99
npm: npmPackageManager,
10-
'yarn-classic': yarnv1PackageManager,
11-
'yarn-modern': yarnv2PackageManager,
10+
'yarn-classic': yarnClassicPackageManager,
11+
'yarn-modern': yarnModernPackageManager,
1212
pnpm: pnpmPackageManager,
1313
};

packages/plugin-js-packages/src/lib/package-managers/yarn-classic/audit-result.ts

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ import { fromJsonLines } from '@code-pushup/utils';
22
import type { AuditResult, Vulnerability } from '../../runner/audit/types.js';
33
import { filterAuditResult } from '../../runner/utils.js';
44
import type {
5-
Yarnv1AuditAdvisory,
6-
Yarnv1AuditResultJson,
7-
Yarnv1AuditSummary,
5+
YarnClassicAuditAdvisory,
6+
YarnClassicAuditResultJson,
7+
YarnClassicAuditSummary,
88
} from './types.js';
99

10-
export function yarnv1ToAuditResult(output: string): AuditResult {
11-
const yarnv1Result = fromJsonLines<Yarnv1AuditResultJson>(output);
12-
const [yarnv1Advisory, yarnv1Summary] = validateYarnv1Result(yarnv1Result);
10+
export function yarnClassicToAuditResult(output: string): AuditResult {
11+
const yarnResult = fromJsonLines<YarnClassicAuditResultJson>(output);
12+
const [yarnAdvisories, yarnSummary] = validateYarnAuditResult(yarnResult);
1313

14-
const vulnerabilities = yarnv1Advisory.map(
14+
const vulnerabilities = yarnAdvisories.map(
1515
({ data: { resolution, advisory } }): Vulnerability => {
1616
const { id, path } = resolution;
1717
const directDependency = path.slice(0, path.indexOf('>'));
@@ -39,8 +39,8 @@ export function yarnv1ToAuditResult(output: string): AuditResult {
3939
);
4040

4141
const summary = {
42-
...yarnv1Summary.data.vulnerabilities,
43-
total: Object.values(yarnv1Summary.data.vulnerabilities).reduce(
42+
...yarnSummary.data.vulnerabilities,
43+
total: Object.values(yarnSummary.data.vulnerabilities).reduce(
4444
(acc, amount) => acc + amount,
4545
0,
4646
),
@@ -50,17 +50,15 @@ export function yarnv1ToAuditResult(output: string): AuditResult {
5050
return filterAuditResult({ vulnerabilities, summary }, 'id');
5151
}
5252

53-
function validateYarnv1Result(
54-
result: Yarnv1AuditResultJson,
55-
): [Yarnv1AuditAdvisory[], Yarnv1AuditSummary] {
53+
function validateYarnAuditResult(
54+
result: YarnClassicAuditResultJson,
55+
): [YarnClassicAuditAdvisory[], YarnClassicAuditSummary] {
5656
const summary = result.at(-1);
5757
if (summary?.type !== 'auditSummary') {
5858
throw new Error('Invalid Yarn v1 audit result - no summary found.');
5959
}
6060

61-
const vulnerabilities = result.filter(
62-
(item): item is Yarnv1AuditAdvisory => item.type === 'auditAdvisory',
63-
);
61+
const vulnerabilities = result.filter(item => item.type === 'auditAdvisory');
6462

6563
return [vulnerabilities, summary];
6664
}

packages/plugin-js-packages/src/lib/package-managers/yarn-classic/audit-result.unit.test.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { describe, expect, it } from 'vitest';
22
import { toJsonLines } from '@code-pushup/utils';
33
import type { AuditResult } from '../../runner/audit/types.js';
4-
import { yarnv1ToAuditResult } from './audit-result.js';
5-
import type { Yarnv1AuditAdvisory, Yarnv1AuditSummary } from './types.js';
4+
import { yarnClassicToAuditResult } from './audit-result.js';
5+
import type {
6+
YarnClassicAuditAdvisory,
7+
YarnClassicAuditSummary,
8+
} from './types.js';
69

7-
describe('yarnv1ToAuditResult', () => {
10+
describe('yarnClassicToAuditResult', () => {
811
it('should transform Yarn v1 audit to unified audit result', () => {
912
const advisory = {
1013
type: 'auditAdvisory',
@@ -19,7 +22,7 @@ describe('yarnv1ToAuditResult', () => {
1922
url: 'https://github.com/advisories',
2023
},
2124
},
22-
} satisfies Yarnv1AuditAdvisory;
25+
} satisfies YarnClassicAuditAdvisory;
2326
const summary = {
2427
type: 'auditSummary',
2528
data: {
@@ -31,10 +34,10 @@ describe('yarnv1ToAuditResult', () => {
3134
info: 0,
3235
},
3336
},
34-
} satisfies Yarnv1AuditSummary;
37+
} satisfies YarnClassicAuditSummary;
3538

3639
expect(
37-
yarnv1ToAuditResult(toJsonLines([advisory, summary])),
40+
yarnClassicToAuditResult(toJsonLines([advisory, summary])),
3841
).toEqual<AuditResult>({
3942
vulnerabilities: [
4043
{
@@ -57,7 +60,7 @@ describe('yarnv1ToAuditResult', () => {
5760
data: {},
5861
type: 'auditAdvisory',
5962
};
60-
expect(() => yarnv1ToAuditResult(toJsonLines([advisory]))).toThrow(
63+
expect(() => yarnClassicToAuditResult(toJsonLines([advisory]))).toThrow(
6164
'no summary found',
6265
);
6366
});

packages/plugin-js-packages/src/lib/package-managers/yarn-classic/constants.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import type { OutdatedDependency } from '../../runner/outdated/types.js';
2-
import type { Yarnv1FieldName } from './types.js';
2+
import type { YarnClassicFieldName } from './types.js';
33

44
export const outdatedtoFieldMapper: Record<
55
keyof OutdatedDependency,
6-
Yarnv1FieldName
6+
YarnClassicFieldName
77
> = {
88
name: 'Package',
99
current: 'Current',
@@ -12,7 +12,7 @@ export const outdatedtoFieldMapper: Record<
1212
url: 'URL',
1313
};
1414

15-
export const REQUIRED_OUTDATED_FIELDS: Yarnv1FieldName[] = [
15+
export const REQUIRED_OUTDATED_FIELDS: YarnClassicFieldName[] = [
1616
'Package',
1717
'Current',
1818
'Latest',

packages/plugin-js-packages/src/lib/package-managers/yarn-classic/outdated-result.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ import {
1313
outdatedtoFieldMapper,
1414
} from './constants.js';
1515
import {
16-
type Yarnv1FieldName,
17-
type Yarnv1OutdatedResultJson,
18-
yarnv1FieldNames,
16+
type YarnClassicFieldName,
17+
type YarnClassicOutdatedResultJson,
18+
yarnClassicFieldNames,
1919
} from './types.js';
2020

21-
export function yarnv1ToOutdatedResult(output: string): OutdatedResult {
22-
const yarnv1Outdated = fromJsonLines<Yarnv1OutdatedResultJson>(output);
23-
const fields = yarnv1Outdated[1]?.data.head ?? [];
24-
const dependencies = yarnv1Outdated[1]?.data.body ?? [];
21+
export function yarnClassicToOutdatedResult(output: string): OutdatedResult {
22+
const yarnOutdated = fromJsonLines<YarnClassicOutdatedResultJson>(output);
23+
const fields = yarnOutdated[1]?.data.head ?? [];
24+
const dependencies = yarnOutdated[1]?.data.body ?? [];
2525

2626
// no outdated dependencies
2727
if (dependencies.length === 0) {
@@ -46,24 +46,24 @@ export function yarnv1ToOutdatedResult(output: string): OutdatedResult {
4646
}
4747

4848
export function validateOutdatedFields(head: string[]) {
49-
const relevantFields = head.filter(isYarnv1FieldName);
49+
const relevantFields = head.filter(isYarnClassicFieldName);
5050
if (hasAllRequiredFields(relevantFields)) {
5151
return true;
5252
}
5353

5454
throw new Error(
5555
`Yarn v1 outdated: Template [${head.join(
5656
', ',
57-
)}] does not contain all required fields [${yarnv1FieldNames.join(', ')}]`,
57+
)}] does not contain all required fields [${yarnClassicFieldNames.join(', ')}]`,
5858
);
5959
}
6060

61-
function isYarnv1FieldName(value: string): value is Yarnv1FieldName {
62-
const names: readonly string[] = yarnv1FieldNames;
61+
function isYarnClassicFieldName(value: string): value is YarnClassicFieldName {
62+
const names: readonly string[] = yarnClassicFieldNames;
6363
return names.includes(value);
6464
}
6565

66-
function hasAllRequiredFields(head: Yarnv1FieldName[]) {
66+
function hasAllRequiredFields(head: YarnClassicFieldName[]) {
6767
return REQUIRED_OUTDATED_FIELDS.every(field => head.includes(field));
6868
}
6969

packages/plugin-js-packages/src/lib/package-managers/yarn-classic/outdated-result.unit.test.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ import { REQUIRED_OUTDATED_FIELDS } from './constants.js';
88
import {
99
getOutdatedFieldIndexes,
1010
validateOutdatedFields,
11-
yarnv1ToOutdatedResult,
11+
yarnClassicToOutdatedResult,
1212
} from './outdated-result.js';
13-
import type { Yarnv1FieldName } from './types.js';
13+
import type { YarnClassicFieldName } from './types.js';
1414

15-
describe('yarnv1ToOutdatedResult', () => {
15+
describe('yarnClassicToOutdatedResult', () => {
1616
const yarnInfo = { type: 'info', data: 'Colours' };
1717

1818
it('should transform Yarn v1 outdated to unified outdated result', () => {
@@ -25,13 +25,13 @@ describe('yarnv1ToOutdatedResult', () => {
2525
'Latest',
2626
'Package Type',
2727
'URL',
28-
] satisfies Yarnv1FieldName[],
28+
] satisfies YarnClassicFieldName[],
2929
body: [['nx', '16.8.1', '17.0.0', 'dependencies', 'https://nx.dev/']],
3030
},
3131
};
3232

3333
expect(
34-
yarnv1ToOutdatedResult(toJsonLines([yarnInfo, table])),
34+
yarnClassicToOutdatedResult(toJsonLines([yarnInfo, table])),
3535
).toEqual<OutdatedResult>([
3636
{
3737
name: 'nx',
@@ -62,7 +62,7 @@ describe('yarnv1ToOutdatedResult', () => {
6262
};
6363

6464
expect(
65-
yarnv1ToOutdatedResult(toJsonLines([yarnInfo, table])),
65+
yarnClassicToOutdatedResult(toJsonLines([yarnInfo, table])),
6666
).toEqual<OutdatedResult>([
6767
{
6868
name: 'cypress',
@@ -76,7 +76,9 @@ describe('yarnv1ToOutdatedResult', () => {
7676
it('should transform no dependencies to empty array', () => {
7777
const table = { type: 'table', data: { head: [], body: [] } };
7878

79-
expect(yarnv1ToOutdatedResult(toJsonLines([yarnInfo, table]))).toEqual([]);
79+
expect(yarnClassicToOutdatedResult(toJsonLines([yarnInfo, table]))).toEqual(
80+
[],
81+
);
8082
});
8183
});
8284

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { PackageAuditLevel } from '../../config.js';
22

33
// Subset of Yarn v1 audit JSON type
4-
export type Yarnv1AuditAdvisory = {
4+
export type YarnClassicAuditAdvisory = {
55
type: 'auditAdvisory';
66
data: {
77
resolution: {
@@ -19,36 +19,37 @@ export type Yarnv1AuditAdvisory = {
1919
};
2020
};
2121

22-
export type Yarnv1AuditSummary = {
22+
export type YarnClassicAuditSummary = {
2323
type: 'auditSummary';
2424
data: {
2525
vulnerabilities: Record<PackageAuditLevel, number>;
2626
};
2727
};
2828

29-
// NOTE: When rest operator can be at the beginning, the process will be much simpler
30-
export type Yarnv1AuditResultJson = [
31-
...Yarnv1AuditAdvisory[],
32-
Yarnv1AuditSummary,
29+
export type YarnClassicAuditResultJson = [
30+
...YarnClassicAuditAdvisory[],
31+
YarnClassicAuditSummary,
3332
];
3433

35-
export const yarnv1FieldNames = [
34+
export const yarnClassicFieldNames = [
3635
'Package',
3736
'Current',
3837
'Latest',
3938
'Package Type',
4039
'URL',
4140
] as const;
4241

43-
export type Yarnv1FieldName = (typeof yarnv1FieldNames)[number];
42+
export type YarnClassicFieldName = (typeof yarnClassicFieldNames)[number];
4443

45-
type Yarnv1Info = { type: 'info' };
46-
type Yarnv1Table = {
44+
type YarnClassicInfo = { type: 'info' };
45+
type YarnClassicTable = {
4746
type: 'table';
4847
data: {
4948
head: string[];
5049
body: string[][];
5150
};
5251
};
5352

54-
export type Yarnv1OutdatedResultJson = [Yarnv1Info, Yarnv1Table] | [];
53+
export type YarnClassicOutdatedResultJson =
54+
| [YarnClassicInfo, YarnClassicTable]
55+
| [];

packages/plugin-js-packages/src/lib/package-managers/yarn-classic/yarn-classic.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { dependencyGroupToLong } from '../../constants.js';
22
import { COMMON_AUDIT_ARGS, COMMON_OUTDATED_ARGS } from '../constants.js';
33
import type { PackageManager } from '../types.js';
4-
import { yarnv1ToAuditResult } from './audit-result.js';
5-
import { yarnv1ToOutdatedResult } from './outdated-result.js';
4+
import { yarnClassicToAuditResult } from './audit-result.js';
5+
import { yarnClassicToOutdatedResult } from './outdated-result.js';
66

7-
export const yarnv1PackageManager: PackageManager = {
7+
export const yarnClassicPackageManager: PackageManager = {
88
slug: 'yarn-classic',
99
name: 'Yarn v1',
1010
command: 'yarn',
@@ -21,10 +21,10 @@ export const yarnv1PackageManager: PackageManager = {
2121
dependencyGroupToLong[groupDep],
2222
],
2323
ignoreExitCode: true,
24-
unifyResult: yarnv1ToAuditResult,
24+
unifyResult: yarnClassicToAuditResult,
2525
},
2626
outdated: {
2727
commandArgs: COMMON_OUTDATED_ARGS,
28-
unifyResult: yarnv1ToOutdatedResult,
28+
unifyResult: yarnClassicToOutdatedResult,
2929
},
3030
};

0 commit comments

Comments
 (0)