Skip to content

Commit 1405e7d

Browse files
committed
feat(plugin-js-packages): use group per check, audit per dependency group
1 parent ba392bc commit 1405e7d

File tree

6 files changed

+186
-76
lines changed

6 files changed

+186
-76
lines changed

packages/plugin-js-packages/README.md

+40-22
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
📦 **Code PushUp plugin for JavaScript packages.** 🛡️
88

9-
This plugin allows you to list outdated dependencies and run audit for known vulnerabilities.
9+
This plugin checks for known vulnerabilities and outdated dependencies.
1010
It supports the following package managers:
1111

1212
- [NPM](https://docs.npmjs.com/)
@@ -17,9 +17,7 @@ It supports the following package managers:
1717

1818
1. If you haven't already, install [@code-pushup/cli](../cli/README.md) and create a configuration file.
1919

20-
2. Insert plugin configuration. By default, `audit` and `outdated` commands will be run.
21-
22-
Default configuration will look as follows:
20+
2. Insert plugin configuration with your package manager. By default, both `audit` and `outdated` checks will be run. The result should look as follows:
2321

2422
```js
2523
import jsPackagesPlugin from '@code-pushup/js-packages-plugin';
@@ -28,14 +26,12 @@ It supports the following package managers:
2826
// ...
2927
plugins: [
3028
// ...
31-
await jsPackagesPlugin(),
29+
await jsPackagesPlugin({ packageManager: 'npm' }), // replace with your package manager
3230
],
3331
};
3432
```
3533

36-
You may run this plugin with a custom configuration for any supported package manager or command.
37-
38-
A custom configuration will look similarly to the following:
34+
You may run this plugin with a custom configuration for any supported package manager or command. A custom configuration will look similarly to the following:
3935

4036
```js
4137
import jsPackagesPlugin from '@code-pushup/js-packages-plugin';
@@ -49,7 +45,7 @@ It supports the following package managers:
4945
};
5046
```
5147

52-
3. (Optional) Reference individual audits or the provided plugin group which you wish to include in custom categories (use `npx code-pushup print-config` to list audits and groups).
48+
3. (Optional) Reference individual audits or the provided plugin groups which you wish to include in custom categories (use `npx code-pushup print-config` to list audits and groups).
5349

5450
💡 Assign weights based on what influence each command should have on the overall category score (assign weight 0 to only include as extra info, without influencing category score).
5551

@@ -58,17 +54,30 @@ It supports the following package managers:
5854
// ...
5955
categories: [
6056
{
61-
slug: 'dependencies',
62-
title: 'Package dependencies',
57+
slug: 'security',
58+
title: 'Security',
6359
refs: [
6460
{
6561
type: 'group',
66-
plugin: 'npm-package-manager', // replace prefix with your package manager
62+
plugin: 'npm-audit', // replace prefix with your package manager
6763
slug: 'js-packages',
6864
weight: 1,
6965
},
7066
],
7167
},
68+
{
69+
slug: 'up-to-date',
70+
title: 'Up-to-date tools',
71+
refs: [
72+
{
73+
type: 'group',
74+
plugin: 'npm-outdated', // replace prefix with your package manager
75+
slug: 'js-packages',
76+
weight: 1,
77+
},
78+
// ...
79+
],
80+
},
7281
// ...
7382
],
7483
};
@@ -82,16 +91,13 @@ It supports the following package managers:
8291

8392
The plugin accepts the following parameters:
8493

85-
- (optional) `packageManager`: The package manager you are using. Supported values: `npm`, `yarn-classic` (v1), `yarn-modern` (v2+), `pnpm`. Default is `npm`.
94+
- `packageManager`: The package manager you are using. Supported values: `npm`, `yarn-classic` (v1), `yarn-modern` (v2+), `pnpm`.
8695
- (optional) `checks`: Array of checks to be run. Supported commands: `audit`, `outdated`. Both are configured by default.
8796
- (optional) `auditLevelMapping`: If you wish to set a custom level of issue severity based on audit vulnerability level, you may do so here. Any omitted values will be filled in by defaults. Audit levels are: `critical`, `high`, `moderate`, `low` and `info`. Issue severities are: `error`, `warn` and `info`. By default the mapping is as follows: `critical` and `high``error`; `moderate` and `low``warning`; `info``info`.
8897

89-
> [!NOTE]
90-
> All parameters are optional so the plugin can be called with no arguments in the default setting.
91-
9298
### Audits and group
9399

94-
This plugin provides a group for convenient declaration in your config. When defined this way, all measured coverage type audits have the same weight.
100+
This plugin provides a group per check for a convenient declaration in your config.
95101

96102
```ts
97103
// ...
@@ -103,7 +109,13 @@ This plugin provides a group for convenient declaration in your config. When def
103109
{
104110
type: 'group',
105111
plugin: 'js-packages',
106-
slug: 'npm-package-manager', // replace prefix with your package manager
112+
slug: 'npm-audit', // replace prefix with your package manager
113+
weight: 1,
114+
},
115+
{
116+
type: 'group',
117+
plugin: 'js-packages',
118+
slug: 'npm-outdated', // replace prefix with your package manager
107119
weight: 1,
108120
},
109121
// ...
@@ -113,7 +125,7 @@ This plugin provides a group for convenient declaration in your config. When def
113125
],
114126
```
115127

116-
Each package manager command still has its own audit. So when you want to include a subset of commands or assign different weights to them, you can do so in the following way:
128+
Each dependency group has its own audit. If you want to check only a subset of dependencies (e.g. run audit and outdated for production dependencies) or assign different weights to them, you can do so in the following way:
117129

118130
```ts
119131
// ...
@@ -125,15 +137,21 @@ Each package manager command still has its own audit. So when you want to includ
125137
{
126138
type: 'audit',
127139
plugin: 'js-packages',
128-
slug: 'npm-audit', // replace prefix with your package manager
140+
slug: 'npm-audit-prod', // replace prefix with your package manager
129141
weight: 2,
130142
},
131-
{
143+
{
132144
type: 'audit',
133145
plugin: 'js-packages',
134-
slug: 'npm-outdated', // replace prefix with your package manager
146+
slug: 'npm-audit-dev', // replace prefix with your package manager
135147
weight: 1,
136148
},
149+
{
150+
type: 'audit',
151+
plugin: 'js-packages',
152+
slug: 'npm-outdated-prod', // replace prefix with your package manager
153+
weight: 2,
154+
},
137155
// ...
138156
],
139157
},

packages/plugin-js-packages/src/lib/config.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,7 @@ export const jsPackagesPluginConfigSchema = z.object({
4949
})
5050
.min(1)
5151
.default(['audit', 'outdated']),
52-
packageManager: packageManagerSchema
53-
.describe('Package manager to be used. Defaults to npm')
54-
.default('npm'),
52+
packageManager: packageManagerSchema.describe('Package manager to be used.'),
5553
auditLevelMapping: z
5654
.record(packageAuditLevelSchema, issueSeveritySchema, {
5755
description:
@@ -68,3 +66,5 @@ export type JSPackagesPluginConfig = z.input<
6866
export type FinalJSPackagesPluginConfig = z.infer<
6967
typeof jsPackagesPluginConfigSchema
7068
>;
69+
70+
export type PackageDependencyType = 'prod' | 'dev' | 'optional';

packages/plugin-js-packages/src/lib/config.unit.test.ts

+14-5
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,17 @@ describe('jsPackagesPluginConfigSchema', () => {
2020
});
2121

2222
it('should accept a minimal JS package configuration', () => {
23-
expect(() => jsPackagesPluginConfigSchema.parse({})).not.toThrow();
23+
expect(() =>
24+
jsPackagesPluginConfigSchema.parse({
25+
packageManager: 'pnpm',
26+
} satisfies JSPackagesPluginConfig),
27+
).not.toThrow();
2428
});
2529

2630
it('should fill in default values', () => {
27-
const config = jsPackagesPluginConfigSchema.parse({});
31+
const config = jsPackagesPluginConfigSchema.parse({
32+
packageManager: 'npm',
33+
});
2834
expect(config).toEqual<FinalJSPackagesPluginConfig>({
2935
checks: ['audit', 'outdated'],
3036
packageManager: 'npm',
@@ -39,9 +45,12 @@ describe('jsPackagesPluginConfigSchema', () => {
3945
});
4046

4147
it('should throw for no passed commands', () => {
42-
expect(() => jsPackagesPluginConfigSchema.parse({ checks: [] })).toThrow(
43-
'too_small',
44-
);
48+
expect(() =>
49+
jsPackagesPluginConfigSchema.parse({
50+
packageManager: 'yarn-classic',
51+
checks: [],
52+
}),
53+
).toThrow('too_small');
4554
});
4655
});
4756

packages/plugin-js-packages/src/lib/utils.ts renamed to packages/plugin-js-packages/src/lib/constants.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { MaterialIcon } from '@code-pushup/models';
2-
import { PackageManager } from './config';
2+
import { PackageDependencyType, PackageManager } from './config';
33

44
export const pkgManagerNames: Record<PackageManager, string> = {
55
npm: 'NPM',
@@ -25,7 +25,7 @@ export const auditDocs: Record<PackageManager, string> = {
2525
npm: 'https://docs.npmjs.com/cli/commands/npm-audit',
2626
'yarn-classic': 'https://classic.yarnpkg.com/docs/cli/audit',
2727
'yarn-modern': 'https://yarnpkg.com/cli/npm/audit',
28-
pnpm: 'https://pnpm.io/',
28+
pnpm: 'https://pnpm.io/cli/audit/',
2929
};
3030

3131
export const outdatedDocs: Record<PackageManager, string> = {
@@ -34,3 +34,10 @@ export const outdatedDocs: Record<PackageManager, string> = {
3434
'yarn-modern': 'https://github.com/mskelton/yarn-plugin-outdated',
3535
pnpm: 'https://pnpm.io/cli/outdated',
3636
};
37+
38+
export const dependencyDocs: Record<PackageDependencyType, string> = {
39+
prod: 'https://classic.yarnpkg.com/docs/dependency-types#toc-dependencies',
40+
dev: 'https://classic.yarnpkg.com/docs/dependency-types#toc-devdependencies',
41+
optional:
42+
'https://classic.yarnpkg.com/docs/dependency-types#toc-optionaldependencies',
43+
};

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

+88-31
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,39 @@ import { name, version } from '../../package.json';
55
import {
66
JSPackagesPluginConfig,
77
PackageCommand,
8+
PackageDependencyType,
9+
PackageManager,
810
jsPackagesPluginConfigSchema,
911
} from './config';
10-
import { createRunnerConfig } from './runner';
1112
import {
1213
auditDocs,
14+
dependencyDocs,
1315
outdatedDocs,
1416
pkgManagerDocs,
1517
pkgManagerIcons,
1618
pkgManagerNames,
17-
} from './utils';
19+
} from './constants';
20+
import { createRunnerConfig } from './runner';
1821

1922
/**
2023
* Instantiates Code PushUp JS packages plugin for core config.
2124
*
2225
* @example
23-
* import coveragePlugin from '@code-pushup/js-packages-plugin'
26+
* import jsPackagesPlugin from '@code-pushup/js-packages-plugin'
2427
*
2528
* export default {
2629
* // ... core config ...
2730
* plugins: [
2831
* // ... other plugins ...
29-
* await jsPackagesPlugin()
32+
* await jsPackagesPlugin({ packageManager: 'npm' })
3033
* ]
3134
* }
3235
*
3336
* @returns Plugin configuration.
3437
*/
38+
3539
export async function jsPackagesPlugin(
36-
config: JSPackagesPluginConfig = {},
40+
config: JSPackagesPluginConfig,
3741
): Promise<PluginConfig> {
3842
const jsPackagesPluginConfig = jsPackagesPluginConfigSchema.parse(config);
3943
const pkgManager = jsPackagesPluginConfig.packageManager;
@@ -44,43 +48,96 @@ export async function jsPackagesPlugin(
4448
'bin.js',
4549
);
4650

47-
const audits: Record<PackageCommand, Audit> = {
51+
return {
52+
slug: 'js-packages',
53+
title: 'Plugin for JS packages',
54+
icon: pkgManagerIcons[pkgManager],
55+
description:
56+
'This plugin runs audit to uncover vulnerabilities and lists outdated dependencies. It supports npm, yarn classic and berry, pnpm package managers.',
57+
docsUrl: pkgManagerDocs[pkgManager],
58+
packageName: name,
59+
version,
60+
audits: createAudits(pkgManager, checks),
61+
groups: createGroups(pkgManager, checks),
62+
runner: await createRunnerConfig(runnerScriptPath, jsPackagesPluginConfig),
63+
};
64+
}
65+
66+
function createGroups(
67+
pkgManager: PackageManager,
68+
checks: PackageCommand[],
69+
): Group[] {
70+
const groups: Record<PackageCommand, Group> = {
4871
audit: {
4972
slug: `${pkgManager}-audit`,
5073
title: `${pkgManagerNames[pkgManager]} audit`,
51-
description: `Lists ${pkgManagerNames[pkgManager]} audit vulnerabilities.`,
74+
description: `Group containing ${pkgManagerNames[pkgManager]} vulnerabilities.`,
5275
docsUrl: auditDocs[pkgManager],
76+
refs: [
77+
// eslint-disable-next-line no-magic-numbers
78+
{ slug: `${pkgManager}-audit-prod`, weight: 8 },
79+
{ slug: `${pkgManager}-audit-dev`, weight: 1 },
80+
{ slug: `${pkgManager}-audit-optional`, weight: 1 },
81+
],
5382
},
5483
outdated: {
5584
slug: `${pkgManager}-outdated`,
5685
title: `${pkgManagerNames[pkgManager]} outdated dependencies`,
57-
description: `Lists ${pkgManagerNames[pkgManager]} outdated dependencies.`,
86+
description: `Group containing outdated ${pkgManagerNames[pkgManager]} dependencies.`,
5887
docsUrl: outdatedDocs[pkgManager],
88+
refs: [
89+
// eslint-disable-next-line no-magic-numbers
90+
{ slug: `${pkgManager}-outdated-prod`, weight: 8 },
91+
{ slug: `${pkgManager}-outdated-dev`, weight: 1 },
92+
{ slug: `${pkgManager}-outdated-optional`, weight: 1 },
93+
],
5994
},
6095
};
6196

62-
const group: Group = {
63-
slug: `${pkgManager}-package-manager`,
64-
title: `${pkgManagerNames[pkgManager]} package manager`,
65-
description: `Group containing both audit and dependencies command audits for the ${pkgManagerNames[pkgManager]} package manager.`,
66-
docsUrl: pkgManagerDocs[pkgManager],
67-
refs: checks.map(check => ({
68-
slug: `${pkgManager}-${check}`,
69-
weight: 1,
70-
})),
71-
};
97+
return checks.map(check => groups[check]);
98+
}
7299

73-
return {
74-
slug: 'js-packages',
75-
title: 'Plugin for JS packages',
76-
icon: pkgManagerIcons[pkgManager],
77-
description:
78-
'This plugin runs audit to uncover vulnerabilities and lists outdated dependencies. It supports npm, yarn classic and berry, pnpm package managers.',
79-
docsUrl: pkgManagerDocs[pkgManager],
80-
packageName: name,
81-
version,
82-
audits: checks.map(check => audits[check]),
83-
groups: [group],
84-
runner: await createRunnerConfig(runnerScriptPath, jsPackagesPluginConfig),
85-
};
100+
function createAudits(
101+
pkgManager: PackageManager,
102+
checks: PackageCommand[],
103+
): Audit[] {
104+
return checks.flatMap(check => [
105+
{
106+
slug: `${pkgManager}-${check}-prod`,
107+
title: getAuditTitle(pkgManager, check, 'prod'),
108+
description: getAuditDescription(check, 'prod'),
109+
docsUrl: dependencyDocs.prod,
110+
},
111+
{
112+
slug: `${pkgManager}-${check}-dev`,
113+
title: getAuditTitle(pkgManager, check, 'dev'),
114+
description: getAuditDescription(check, 'dev'),
115+
docsUrl: dependencyDocs.dev,
116+
},
117+
{
118+
slug: `${pkgManager}-${check}-optional`,
119+
title: getAuditTitle(pkgManager, check, 'optional'),
120+
description: getAuditDescription(check, 'optional'),
121+
docsUrl: dependencyDocs.optional,
122+
},
123+
]);
124+
}
125+
126+
function getAuditTitle(
127+
pkgManager: PackageManager,
128+
check: PackageCommand,
129+
dependencyType: PackageDependencyType,
130+
) {
131+
return check === 'audit'
132+
? `Vulnerabilities for ${pkgManagerNames[pkgManager]} ${dependencyType} dependencies.`
133+
: `Outdated ${pkgManagerNames[pkgManager]} ${dependencyType} dependencies.`;
134+
}
135+
136+
function getAuditDescription(
137+
check: PackageCommand,
138+
dependencyType: PackageDependencyType,
139+
) {
140+
return check === 'audit'
141+
? `Runs security audit on ${dependencyType} dependencies.`
142+
: `Checks for outdated ${dependencyType} dependencies`;
86143
}

0 commit comments

Comments
 (0)