diff --git a/README.md b/README.md index 3ecb03c..4d06b52 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,8 @@ success Already up-to-date. | `--symlink` | Symlink type for `node_modules` ref | `junction` for Windows, `dir` otherwise | | | `--temp` | Directory for temporary assets | `/node_modules/.cache/yarn-audit-fix` | | | `--verbose` | Switch log level to verbose/debug | `false` | | +| `--exclude` | Array of glob patterns of packages to exclude from audit | | | +| `--ignore` | Array of glob patterns of advisory IDs to ignore in the audit report | | | ### ENV All mentioned above CLI options can be replaced with the corresponding env variables with leading **YAF** prefix. For example: @@ -338,6 +340,13 @@ yarn add yarn-audit-fix -D --ignore-engines ``` ### Response Code: 400 (Bad Request) + +In some cases **yarn npm audit** fails because the `yarn.lock` file contains a transitive dependency in unreadable format: +``` + 'example-dependency': 'npm:example-dependency@1.0.0' +``` + +This will results in: ```shell invoke yarn npm audit --all --json --recursive ➤ YN0035: Bad Request @@ -347,6 +356,10 @@ invoke yarn npm audit --all --json --recursive ``` https://github.com/yarnpkg/berry/issues/4117 +A workaround is available using the `exclude` option: +1. Update project **yarn** to >=3.3.0 (lower version doesn't support this parameter for **yarn npm audit**). +2. Apply `npx yarn-audit-fix --exclude example-dependency`. This will cause **yarn** to ignore `example-dependency` while creating the audit report. + ## Contributing Feel free to open any issues: bugs, feature requests or other questions. You're always welcome to suggest a PR. Just fork this repo, write some code, add some tests and push your changes. diff --git a/src/main/ts/cli.ts b/src/main/ts/cli.ts index af30a49..051b4ed 100644 --- a/src/main/ts/cli.ts +++ b/src/main/ts/cli.ts @@ -6,6 +6,18 @@ import { Command, Option } from 'commander' import { run } from './runner' +const parseMultipleValueArg = ( + value: string, + previous: string | string[] | undefined, +) => { + if (!previous) { + return value + } + + const previousArray = Array.isArray(previous) ? previous : [previous] + return previousArray.concat([value]) +} + const env = process.env const flags = new Command() .addOption( @@ -22,6 +34,12 @@ const flags = new Command() 'Get an idea of what audit fix will do', env.YAF_DRY_RUN, ) + .option( + '--exclude ', + 'Array of glob patterns of packages to exclude from audit', + parseMultipleValueArg, + env.YAF_EXCLUDE, + ) .addOption( new Option('--flow [flow]', 'Define how `yarn.lock` is modified') .choices(['convert', 'patch']) @@ -32,6 +50,12 @@ const flags = new Command() 'Have audit fix install semver-major updates to toplevel dependencies, not just semver-compatible ones', env.YAF_FORCE, ) + .option( + '--ignore ', + 'Array of glob patterns of advisory IDs to ignore in the audit report', + parseMultipleValueArg, + env.YAF_IGNORE, + ) .option( '--ignore-engines [bool]', 'Ignore engines check', diff --git a/src/main/ts/lockfile/v2.ts b/src/main/ts/lockfile/v2.ts index 1a9e9af..f03cb02 100644 --- a/src/main/ts/lockfile/v2.ts +++ b/src/main/ts/lockfile/v2.ts @@ -108,7 +108,13 @@ export const audit = ( }, }, } - const _flags = formatFlags(mapFlags(flags, mapping), 'groups', 'verbose') + const _flags = formatFlags( + mapFlags(flags, mapping), + 'exclude', + 'ignore', + 'groups', + 'verbose', + ) const report = invoke( bins.yarn, ['npm', 'audit', '--all', '--json', '--recursive', ..._flags], diff --git a/src/main/ts/stages.ts b/src/main/ts/stages.ts index b23e91c..7aac475 100644 --- a/src/main/ts/stages.ts +++ b/src/main/ts/stages.ts @@ -68,6 +68,14 @@ export const printRuntimeDigest: TCallback = ({ ) } + // NOTE yarn > v3.3.0 fixed plugin-npm-cli minor compatibility + // https://github.com/yarnpkg/berry/pull/4356#issuecomment-1316653931 + if (semver.gt('3.3.0', versions.yarn) && (flags.exclude || flags.ignore)) { + console.warn( + `This project yarn version ${versions.yarn} doesn't support the 'exclude' and 'ignore' flags. Please upgrade to yarn 3.3.0 or higher to use those flags`, + ) + } + if (semver.gt(versions.yafLatest, versions.yaf)) { console.warn( `yarn-audit-fix version ${versions.yaf} is out of date. Install the latest ${versions.yafLatest} for better results`, @@ -155,7 +163,9 @@ export const npmAuditFix: TCallback = ({ temp, flags, bins }) => { { ...defaultFlags, ...flags }, 'audit-level', 'dry-run', + 'exclude', 'force', + 'ignore', 'loglevel', 'legacy-peer-deps', 'only', diff --git a/src/main/ts/util.ts b/src/main/ts/util.ts index 03f6c1b..e57eb7c 100644 --- a/src/main/ts/util.ts +++ b/src/main/ts/util.ts @@ -71,10 +71,16 @@ export const formatFlags = (flags: TFlags, ...picklist: string[]): string[] => const flag = formatFlag(key) if (checkValue(key, value, omitlist, picklist)) { - memo.push(flag) + if (!Array.isArray(value)) { + memo.push(flag) - if (value !== true) { - memo.push(value) + if (value !== true) { + memo.push(String(value)) + } + } else { + value.forEach((val) => { + memo.push(flag, String(val)) + }) } } diff --git a/src/test/ts/runner.ts b/src/test/ts/runner.ts index 1d118a6..dc950d8 100644 --- a/src/test/ts/runner.ts +++ b/src/test/ts/runner.ts @@ -26,6 +26,8 @@ const noop = () => { } const fixtures = resolve(__dirname, '../fixtures/') const registryUrl = 'https://example.com' +const dependency = 'example-package' +const scopedDependency = '@scope/package' const strMatching = (start = '', end = '') => expect.stringMatching(new RegExp(`^${start}.+${end}$`)) const readFixture = (name: string): string => @@ -184,6 +186,10 @@ describe('yarn-audit-fix', () => { '--verbose', '--registry', registryUrl, + '--exclude', + dependency, + '--exclude', + scopedDependency, '--prefix', expect.stringMatching(temp), ].filter((v) => v !== undefined), @@ -241,7 +247,7 @@ describe('yarn-audit-fix', () => { it('invokes cmd queue with proper args', async () => { await run({ flow: 'patch', - temp + temp, }) checkTempAssets() @@ -282,6 +288,7 @@ describe('yarn-audit-fix', () => { 'package-lock-only': true, registry: registryUrl, flow: 'convert', + exclude: [dependency, scopedDependency], ignoreEngines: true, temp, }) @@ -315,6 +322,8 @@ describe('yarn-audit-fix', () => { '--package-lock-only=false', `--registry=${registryUrl}`, '--flow=convert', + `--exclude=${dependency}`, + `--exclude=${scopedDependency}`, '--ignore-engines', ) await reimport('../../main/ts/cli') diff --git a/src/test/ts/util.ts b/src/test/ts/util.ts index 8fe4b49..d91d091 100644 --- a/src/test/ts/util.ts +++ b/src/test/ts/util.ts @@ -89,6 +89,18 @@ describe('util', () => { ['force', 'audit-level', 'only', 'bar', 'b'], ['--force', '--audit-level', 'moderate', '--only', 'dev'], ], + [{ exclude: [] }, ['exclude'], []], + [ + { exclude: ['@scope/package'] }, + ['exclude'], + ['--exclude', '@scope/package'], + ], + [ + { exclude: ['@scope/package', 'another-package'] }, + ['exclude'], + ['--exclude', '@scope/package', '--exclude', 'another-package'], + ], + [{ verbose: true, exclude: [] }, [], ['--verbose']], ] cases.forEach(([input, picklist, output]) => {