Skip to content

Commit bcc781a

Browse files
authored
fix: move run-script banners to stderr when in json mode (#7439)
Fixes #7354
1 parent 7e349f4 commit bcc781a

File tree

5 files changed

+45
-12
lines changed

5 files changed

+45
-12
lines changed

docs/bin/build.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const run = require('../lib/build.js')
1616
const { paths } = require('../lib/index')
1717

1818
run(paths)
19-
.then((res) => console.log(`Wrote ${res.length} files`))
19+
.then((res) => console.error(`Wrote ${res.length} files`))
2020
.catch((err) => {
2121
process.exitCode = 1
2222
console.error(err)

lib/utils/display.js

+15-9
Original file line numberDiff line numberDiff line change
@@ -267,16 +267,22 @@ class Display {
267267
if (this.#outputState.buffering) {
268268
this.#outputState.buffer.push([level, meta, ...args])
269269
} else {
270-
// HACK: if it looks like the banner and we are in a state where we hide the
271-
// banner then dont write any output. This hack can be replaced with proc-log.META
272-
const isBanner = args.length === 1 &&
273-
typeof args[0] === 'string' &&
274-
args[0].startsWith('\n> ') &&
275-
args[0].endsWith('\n')
276-
const hideBanner = this.#silent || ['exec', 'explore'].includes(this.#command)
277-
if (!(isBanner && hideBanner)) {
278-
this.#writeOutput(level, meta, ...args)
270+
// HACK: Check if the argument looks like a run-script banner. This can be
271+
// replaced with proc-log.META in @npmcli/run-script
272+
if (typeof args[0] === 'string' && args[0].startsWith('\n> ') && args[0].endsWith('\n')) {
273+
if (this.#silent || ['exec', 'explore'].includes(this.#command)) {
274+
// Silent mode and some specific commands always hide run script banners
275+
break
276+
} else if (this.#json) {
277+
// In json mode, change output to stderr since we dont want to break json
278+
// parsing on stdout if the user is piping to jq or something.
279+
// XXX: in a future (breaking?) change it might make sense for run-script to
280+
// always output these banners with proc-log.output.error if we think they
281+
// align closer with "logging" instead of "output"
282+
level = output.KEYS.error
283+
}
279284
}
285+
this.#writeOutput(level, meta, ...args)
280286
}
281287
break
282288
}

smoke-tests/test/pack-json-output.js

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
const t = require('tap')
3+
const setup = require('./fixtures/setup.js')
4+
5+
t.test('pack --json returns only json on stdout', async t => {
6+
const { npmLocal } = await setup(t)
7+
8+
const { stderr, stdout } = await npmLocal('pack', '--json')
9+
10+
t.match(stderr, /> npm@.* prepack\n/, 'stderr has banner')
11+
t.ok(JSON.parse(stdout), 'stdout can be parsed as json')
12+
})

tap-snapshots/test/lib/commands/pack.js.test.cjs

+12-1
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,24 @@ Array [
109109
"name": "@myscope/test-package",
110110
"shasum": "{sha}",
111111
"size": "{size}",
112-
"unpackedSize": 50,
112+
"unpackedSize": 88,
113113
"version": "1.0.0",
114114
},
115115
],
116116
]
117117
`
118118

119+
exports[`test/lib/commands/pack.js TAP should log scoped package output as valid json > stderr has banners 1`] = `
120+
Array [
121+
String(
122+
123+
> @myscope/test-package@1.0.0 prepack
124+
> echo prepack!
125+
126+
),
127+
]
128+
`
129+
119130
exports[`test/lib/commands/pack.js TAP should pack current directory with no arguments > logs pack contents 1`] = `
120131
Array [
121132
"package: test-package@1.0.0",

test/lib/commands/pack.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -72,18 +72,22 @@ t.test('should log output as valid json', async t => {
7272
})
7373

7474
t.test('should log scoped package output as valid json', async t => {
75-
const { npm, outputs, logs } = await loadMockNpm(t, {
75+
const { npm, outputs, outputErrors, logs } = await loadMockNpm(t, {
7676
prefixDir: {
7777
'package.json': JSON.stringify({
7878
name: '@myscope/test-package',
7979
version: '1.0.0',
80+
scripts: {
81+
prepack: 'echo prepack!',
82+
},
8083
}),
8184
},
8285
config: { json: true },
8386
})
8487
await npm.exec('pack', [])
8588
const filename = 'myscope-test-package-1.0.0.tgz'
8689
t.matchSnapshot(outputs.map(JSON.parse), 'outputs as json')
90+
t.matchSnapshot(outputErrors, 'stderr has banners')
8791
t.matchSnapshot(logs.notice, 'logs pack contents')
8892
t.ok(fs.statSync(path.resolve(npm.prefix, filename)))
8993
})

0 commit comments

Comments
 (0)