feat: --stream

Add new global option called `--stream`.

When used, the output from child processes is streamed to the console immediately, prefixed with the originating package directory. This allows output from different packages to be interleaved.

close #2147
close #1609
PR #2595
This commit is contained in:
Zoltan Kochan
2020-05-31 13:46:32 +03:00
committed by GitHub
parent bcd4aa1aa1
commit ffddf34a89
13 changed files with 154 additions and 6 deletions

View File

@@ -0,0 +1,5 @@
---
"@pnpm/common-cli-options-help": minor
---
Add `--stream` option description to the `UNIVERSAL_OPTIONS` array.

View File

@@ -0,0 +1,5 @@
---
"@pnpm/default-reporter": minor
---
Add new reporting option: `streamLifecycleOutput`. When `true`, the output from child processes is printed immediately and is never collapsed.

View File

@@ -0,0 +1,8 @@
---
"@pnpm/config": minor
"@pnpm/plugin-commands-script-runners": minor
"pnpm": minor
---
Add new global option called `--stream`.
When used, the output from child processes is streamed to the console immediately, prefixed with the originating package directory. This allows output from different packages to be interleaved.

View File

@@ -44,6 +44,10 @@ export const UNIVERSAL_OPTIONS = [
description: 'What level of logs to report. Any logs at or higher than the given level will be shown. Levels (lowest to highest): debug, info, warn, error. Or use "--silent" to turn off all logging.',
name: '--loglevel <level>',
},
{
description: 'Stream output from child processes immediately, prefixed with the originating package directory. This allows output from different packages to be interleaved.',
name: '--stream',
},
]
export const FILTERING = {
list: [

View File

@@ -39,6 +39,7 @@ export interface Config {
saveOptional?: boolean,
savePeer?: boolean,
saveWorkspaceProtocol?: boolean,
stream?: boolean,
production?: boolean,
fetchRetries?: number,
fetchRetryFactor?: number,

View File

@@ -75,6 +75,7 @@ export const types = Object.assign({
'sort': Boolean,
'store': String, // TODO: deprecate
'store-dir': String,
'stream': Boolean,
'strict-peer-dependencies': Boolean,
'use-beta-cli': Boolean,
'use-running-store-server': Boolean,

View File

@@ -15,6 +15,7 @@ export default function (
reportingOptions?: {
appendOnly?: boolean,
logLevel?: LogLevel,
streamLifecycleOutput?: boolean,
throttleProgress?: number,
outputMaxWidth?: number,
},
@@ -66,6 +67,7 @@ export function toOutput$ (
appendOnly?: boolean,
logLevel?: LogLevel,
outputMaxWidth?: number,
streamLifecycleOutput?: boolean,
throttleProgress?: number,
},
context: {
@@ -174,6 +176,7 @@ export function toOutput$ (
isRecursive: opts.context.config?.['recursive'] === true,
logLevel: opts.reportingOptions?.logLevel,
pnpmConfig: opts.context.config,
streamLifecycleOutput: opts.reportingOptions?.streamLifecycleOutput,
throttleProgress: opts.reportingOptions?.throttleProgress,
width: opts.reportingOptions?.outputMaxWidth,
}

View File

@@ -39,6 +39,7 @@ export default function (
isRecursive: boolean,
logLevel?: LogLevel,
pnpmConfig?: Config,
streamLifecycleOutput?: boolean,
throttleProgress?: number,
width?: number,
}
@@ -52,7 +53,7 @@ export default function (
throttleProgress: opts.throttleProgress,
}),
reportLifecycleScripts(log$, {
appendOnly: opts.appendOnly,
appendOnly: opts.appendOnly || opts.streamLifecycleOutput,
cwd,
width,
}),

View File

@@ -26,10 +26,8 @@ export default (
// When the reporter is not append-only, the length of output is limited
// in order to reduce flickering
if (opts.appendOnly) {
return most.of(
log$.lifecycle
.map((log: LifecycleLog) => ({ msg: formatLifecycleHideOverflowForAppendOnly(opts.cwd, log) }))
)
return log$.lifecycle
.map((log: LifecycleLog) => most.of({ msg: formatLifecycleHideOverflowForAppendOnly(opts.cwd, log) }))
}
const lifecycleMessages: {
[depPath: string]: {

View File

@@ -259,6 +259,125 @@ test('groups lifecycle output when append-only is used', t => {
})
})
test('groups lifecycle output when streamLifecycleOutput is used', t => {
const output$ = toOutput$({
context: { argv: ['install'] },
reportingOptions: {
outputMaxWidth: 79,
streamLifecycleOutput: true,
},
streamParser: createStreamParser(),
})
lifecycleLogger.debug({
depPath: 'packages/foo',
optional: false,
script: 'node foo',
stage: 'preinstall',
wd: 'packages/foo',
})
lifecycleLogger.debug({
depPath: 'packages/foo',
line: 'foo 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30',
stage: 'preinstall',
stdio: 'stdout',
wd: 'packages/foo',
})
lifecycleLogger.debug({
depPath: 'packages/foo',
exitCode: 1,
optional: true,
stage: 'preinstall',
wd: 'packages/foo',
})
lifecycleLogger.debug({
depPath: 'packages/foo',
optional: false,
script: 'node foo',
stage: 'postinstall',
wd: 'packages/foo',
})
lifecycleLogger.debug({
depPath: 'packages/foo',
line: 'foo I',
stage: 'postinstall',
stdio: 'stdout',
wd: 'packages/foo',
})
lifecycleLogger.debug({
depPath: 'packages/bar',
optional: false,
script: 'node bar',
stage: 'postinstall',
wd: 'packages/bar',
})
lifecycleLogger.debug({
depPath: 'packages/bar',
line: 'bar I',
stage: 'postinstall',
stdio: 'stdout',
wd: 'packages/bar',
})
lifecycleLogger.debug({
depPath: 'packages/foo',
line: 'foo II',
stage: 'postinstall',
stdio: 'stdout',
wd: 'packages/foo',
})
lifecycleLogger.debug({
depPath: 'packages/foo',
line: 'foo III',
stage: 'postinstall',
stdio: 'stdout',
wd: 'packages/foo',
})
lifecycleLogger.debug({
depPath: 'packages/qar',
optional: false,
script: 'node qar',
stage: 'install',
wd: 'packages/qar',
})
lifecycleLogger.debug({
depPath: 'packages/qar',
exitCode: 0,
optional: false,
stage: 'install',
wd: 'packages/qar',
})
lifecycleLogger.debug({
depPath: 'packages/foo',
exitCode: 0,
optional: false,
stage: 'postinstall',
wd: 'packages/foo',
})
t.plan(1)
output$.skip(11).take(1).map(normalizeNewline).subscribe({
complete: () => t.end(),
error: t.end,
next: (output: string) => {
t.equal(output, stripIndents`
packages/foo ${PREINSTALL}$ node foo
packages/foo ${PREINSTALL}: foo 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
packages/foo ${PREINSTALL}: Failed
packages/foo ${POSTINSTALL}$ node foo
packages/foo ${POSTINSTALL}: foo I
packages/bar ${POSTINSTALL}$ node bar
packages/bar ${POSTINSTALL}: bar I
packages/foo ${POSTINSTALL}: foo II
packages/foo ${POSTINSTALL}: foo III
packages/qar ${INSTALL}$ node qar
packages/qar ${INSTALL}: Done
packages/foo ${POSTINSTALL}: Done
`)
},
})
})
test('collapse lifecycle output when it has too many lines', t => {
const output$ = toOutput$({
context: { argv: ['install'] },

View File

@@ -1,6 +1,6 @@
import { docsUrl, readProjectManifestOnly } from '@pnpm/cli-utils'
import { CompletionFunc } from '@pnpm/command'
import { FILTERING } from '@pnpm/common-cli-options-help'
import { FILTERING, UNIVERSAL_OPTIONS } from '@pnpm/common-cli-options-help'
import { Config, types as allTypes } from '@pnpm/config'
import PnpmError from '@pnpm/error'
import runLifecycleHooks from '@pnpm/lifecycle'
@@ -69,6 +69,7 @@ export function help () {
shortAlias: '-r',
},
IF_PRESENT_OPTION_HELP,
...UNIVERSAL_OPTIONS,
],
},
FILTERING,

View File

@@ -34,6 +34,7 @@ export const GLOBAL_OPTIONS = R.pick([
'parseable',
'prefix',
'reporter',
'stream',
'workspace-packages',
], allTypes)

View File

@@ -22,6 +22,7 @@ export default (
reportingOptions: {
appendOnly: false,
logLevel: opts.config.loglevel as LogLevel,
streamLifecycleOutput: opts.config.stream,
throttleProgress: 200,
},
streamParser,