From 1d9ea08907f56e9f6030d0359d1783a200aab32e Mon Sep 17 00:00:00 2001 From: martin Date: Tue, 25 Aug 2026 13:22:27 -0700 Subject: [PATCH] fix: keep dry-run output valid json (#9823) ## Summary - suppress human-readable reify diff lines when JSON output is requested - preserve detailed diff output for non-JSON `--dry-run` and `--long` commands - cover both `--dry-run --json` and `--long --json` ## Background Detailed dry-run diff output was introduced in npm 10.4.0 by #7133 without accounting for JSON mode. The structured JSON summary already contains the add, remove, and change details, so the extra text is redundant and makes stdout invalid JSON. Related to #8567, with the regression coverage requested during review. ## Testing - `tap --no-coverage --no-check-coverage test/lib/utils/reify-output.js` - `eslint lib/utils/reify-output.js test/lib/utils/reify-output.js` Fixes #8565 Copilot-Session: 48e629f8-eb5a-467c-970b-feb313628c00 (cherry picked from commit a8c9b2fe25342967be48eba3f792218c6becca8a) --- lib/utils/reify-output.js | 3 ++- test/lib/utils/reify-output.js | 47 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/lib/utils/reify-output.js b/lib/utils/reify-output.js index fa229a318d26e..0ee5cb0ca8a7e 100644 --- a/lib/utils/reify-output.js +++ b/lib/utils/reify-output.js @@ -44,7 +44,8 @@ const reifyOutput = (npm, arb, extras = {}) => { } if (diff) { - const showDiff = npm.config.get('dry-run') || npm.config.get('long') + const showDiff = !npm.flatOptions.json && + (npm.config.get('dry-run') || npm.config.get('long')) const chalk = npm.chalk depth({ diff --git a/test/lib/utils/reify-output.js b/test/lib/utils/reify-output.js index ee9201482a756..a0f0b3300203f 100644 --- a/test/lib/utils/reify-output.js +++ b/test/lib/utils/reify-output.js @@ -440,6 +440,53 @@ t.test('prints dedupe difference on dry-run', async t => { t.matchSnapshot(out, 'diff table') }) +t.test('prints only json for dry-run and long', async t => { + for (const flag of ['dry-run', 'long']) { + await t.test(flag, async t => { + const out = await mockReify(t, { + actualTree: { + inventory: { + has: () => true, + }, + children: [], + }, + diff: { + children: [ + { + action: 'ADD', + ideal: { + path: 'test/foo', + name: 'foo', + package: { version: '1.0.0' }, + }, + }, + ], + }, + }, { + [flag]: true, + json: true, + }) + + t.strictSame(JSON.parse(out), { + add: [ + { + name: 'foo', + version: '1.0.0', + path: 'test/foo', + }, + ], + added: 1, + audited: 0, + change: [], + changed: 0, + funding: 0, + remove: [], + removed: 0, + }) + }) + } +}) + t.test('prints dedupe difference on long', async t => { const mock = { actualTree: {