mirror of
https://github.com/pnpm/pnpm.git
synced 2026-01-11 00:18:32 -05:00
6
.changeset/violet-islands-wink.md
Normal file
6
.changeset/violet-islands-wink.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/plugin-commands-script-runners": patch
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
`exec` now also streams prefixed output when `--recursive` or `--parallel` is specified just as `run` does [#8065](https://github.com/pnpm/pnpm/issues/8065).
|
||||
@@ -47,6 +47,7 @@
|
||||
"@pnpm/command": "workspace:*",
|
||||
"@pnpm/common-cli-options-help": "workspace:*",
|
||||
"@pnpm/config": "workspace:*",
|
||||
"@pnpm/core-loggers": "workspace:*",
|
||||
"@pnpm/crypto.base32-hash": "workspace:*",
|
||||
"@pnpm/error": "workspace:*",
|
||||
"@pnpm/lifecycle": "workspace:*",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import path from 'path'
|
||||
import { docsUrl, type RecursiveSummary, throwOnCommandFail, readProjectManifestOnly } from '@pnpm/cli-utils'
|
||||
import { type LifecycleMessage, lifecycleLogger } from '@pnpm/core-loggers'
|
||||
import { type Config, types } from '@pnpm/config'
|
||||
import { makeNodeRequireOption } from '@pnpm/lifecycle'
|
||||
import { logger } from '@pnpm/logger'
|
||||
@@ -39,6 +40,7 @@ export function rcOptionsTypes (): Record<string, unknown> {
|
||||
'use-node-version',
|
||||
'unsafe-perm',
|
||||
'workspace-concurrency',
|
||||
'reporter-hide-prefix',
|
||||
], types),
|
||||
'shell-mode': Boolean,
|
||||
'resume-from': String,
|
||||
@@ -134,7 +136,7 @@ export async function handler (
|
||||
resumeFrom?: string
|
||||
reportSummary?: boolean
|
||||
implicitlyFellbackFromRun?: boolean
|
||||
} & Pick<Config, 'extraBinPaths' | 'extraEnv' | 'lockfileDir' | 'modulesDir' | 'dir' | 'userAgent' | 'recursive' | 'workspaceDir' | 'nodeOptions'>,
|
||||
} & Pick<Config, 'extraBinPaths' | 'extraEnv' | 'lockfileDir' | 'modulesDir' | 'dir' | 'userAgent' | 'recursive' | 'reporterHidePrefix' | 'workspaceDir' | 'nodeOptions'>,
|
||||
params: string[]
|
||||
): Promise<{ exitCode: number }> {
|
||||
// For backward compatibility
|
||||
@@ -184,6 +186,7 @@ export async function handler (
|
||||
'./node_modules/.bin',
|
||||
...opts.extraBinPaths,
|
||||
]
|
||||
const reporterShowPrefix = opts.recursive && !opts.reporterHidePrefix
|
||||
for (const chunk of chunks) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await Promise.all(chunk.map(async (prefix: string) =>
|
||||
@@ -205,12 +208,47 @@ export async function handler (
|
||||
prependPaths,
|
||||
userAgent: opts.userAgent,
|
||||
})
|
||||
await execa(params[0], params.slice(1), {
|
||||
cwd: prefix,
|
||||
env,
|
||||
stdio: 'inherit',
|
||||
shell: opts.shellMode ?? false,
|
||||
})
|
||||
const [cmd, ...args] = params
|
||||
if (reporterShowPrefix) {
|
||||
const manifest = await readProjectManifestOnly(prefix)
|
||||
const child = execa(cmd, args, {
|
||||
cwd: prefix,
|
||||
env,
|
||||
stdio: 'pipe',
|
||||
shell: opts.shellMode ?? false,
|
||||
})
|
||||
const lifecycleOpts = {
|
||||
wd: prefix,
|
||||
depPath: manifest.name ?? path.relative(opts.dir, prefix),
|
||||
stage: '(exec)',
|
||||
} satisfies Partial<LifecycleMessage>
|
||||
const logFn = (stdio: 'stdout' | 'stderr') => (data: unknown): void => {
|
||||
for (const line of String(data).split('\n')) {
|
||||
lifecycleLogger.debug({
|
||||
...lifecycleOpts,
|
||||
stdio,
|
||||
line,
|
||||
})
|
||||
}
|
||||
}
|
||||
child.stdout!.on('data', logFn('stdout'))
|
||||
child.stderr!.on('data', logFn('stderr'))
|
||||
void child.once('close', exitCode => {
|
||||
lifecycleLogger.debug({
|
||||
...lifecycleOpts,
|
||||
exitCode: exitCode ?? 1,
|
||||
optional: false,
|
||||
})
|
||||
})
|
||||
await child
|
||||
} else {
|
||||
await execa(cmd, args, {
|
||||
cwd: prefix,
|
||||
env,
|
||||
stdio: 'inherit',
|
||||
shell: opts.shellMode ?? false,
|
||||
})
|
||||
}
|
||||
result[prefix].status = 'passed'
|
||||
result[prefix].duration = getExecutionDuration(startTime)
|
||||
} catch (err: any) { // eslint-disable-line
|
||||
|
||||
121
exec/plugin-commands-script-runners/test/exec.logs.ts
Normal file
121
exec/plugin-commands-script-runners/test/exec.logs.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { readProjects } from '@pnpm/filter-workspace-packages'
|
||||
import { logger } from '@pnpm/logger'
|
||||
import { exec } from '@pnpm/plugin-commands-script-runners'
|
||||
import { preparePackages } from '@pnpm/prepare'
|
||||
import writeYamlFile from 'write-yaml-file'
|
||||
import { DEFAULT_OPTS } from './utils'
|
||||
|
||||
jest.mock('@pnpm/logger', () => {
|
||||
const debug = jest.fn()
|
||||
|
||||
return {
|
||||
logger: () => ({ debug }),
|
||||
}
|
||||
})
|
||||
|
||||
const lifecycleLogger = logger('lifecycle')
|
||||
|
||||
afterEach(() => {
|
||||
(lifecycleLogger.debug as jest.Mock).mockClear()
|
||||
})
|
||||
|
||||
test('pnpm exec --recursive prints prefixes', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
location: 'packages/foo',
|
||||
package: { name: 'foo' },
|
||||
},
|
||||
{
|
||||
location: 'packages/bar',
|
||||
package: { name: 'bar' },
|
||||
},
|
||||
])
|
||||
|
||||
await writeYamlFile('pnpm-workspace.yaml', {
|
||||
packages: ['packages/*'],
|
||||
})
|
||||
|
||||
const { selectedProjectsGraph } = await readProjects(process.cwd(), [])
|
||||
|
||||
const scriptFile = path.resolve('script.js')
|
||||
fs.writeFileSync(scriptFile, `
|
||||
console.log('hello from stdout')
|
||||
console.error('hello from stderr')
|
||||
console.log('name is ' + require(require('path').resolve('package.json')).name)
|
||||
`)
|
||||
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
bail: true,
|
||||
selectedProjectsGraph,
|
||||
}, [process.execPath, scriptFile])
|
||||
|
||||
for (const name of ['foo', 'bar']) {
|
||||
const loggerOpts = {
|
||||
wd: path.resolve('packages', name),
|
||||
depPath: name,
|
||||
stage: '(exec)',
|
||||
}
|
||||
expect(lifecycleLogger.debug).toHaveBeenCalledWith({
|
||||
...loggerOpts,
|
||||
line: 'hello from stdout',
|
||||
stdio: 'stdout',
|
||||
})
|
||||
expect(lifecycleLogger.debug).toHaveBeenCalledWith({
|
||||
...loggerOpts,
|
||||
line: 'hello from stderr',
|
||||
stdio: 'stderr',
|
||||
})
|
||||
expect(lifecycleLogger.debug).toHaveBeenCalledWith({
|
||||
...loggerOpts,
|
||||
line: `name is ${name}`,
|
||||
stdio: 'stdout',
|
||||
})
|
||||
expect(lifecycleLogger.debug).toHaveBeenCalledWith({
|
||||
...loggerOpts,
|
||||
optional: false,
|
||||
exitCode: 0,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
test('pnpm exec --recursive --reporter-hide-prefix does not print prefixes', async () => {
|
||||
preparePackages([
|
||||
{
|
||||
location: 'packages/foo',
|
||||
package: { name: 'foo' },
|
||||
},
|
||||
{
|
||||
location: 'packages/bar',
|
||||
package: { name: 'bar' },
|
||||
},
|
||||
])
|
||||
|
||||
await writeYamlFile('pnpm-workspace.yaml', {
|
||||
packages: ['packages/*'],
|
||||
})
|
||||
|
||||
const { selectedProjectsGraph } = await readProjects(process.cwd(), [])
|
||||
|
||||
const scriptFile = path.resolve('script.js')
|
||||
fs.writeFileSync(scriptFile, `
|
||||
console.log('hello from stdout')
|
||||
console.error('hello from stderr')
|
||||
console.log('name is ' + require(require('path').resolve('package.json')).name)
|
||||
`)
|
||||
|
||||
await exec.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
bail: true,
|
||||
reporterHidePrefix: true,
|
||||
selectedProjectsGraph,
|
||||
}, [process.execPath, scriptFile])
|
||||
|
||||
expect(lifecycleLogger.debug).not.toHaveBeenCalled()
|
||||
})
|
||||
@@ -27,6 +27,9 @@
|
||||
{
|
||||
"path": "../../config/config"
|
||||
},
|
||||
{
|
||||
"path": "../../packages/core-loggers"
|
||||
},
|
||||
{
|
||||
"path": "../../packages/crypto.base32-hash"
|
||||
},
|
||||
|
||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -1381,6 +1381,9 @@ importers:
|
||||
'@pnpm/config':
|
||||
specifier: workspace:*
|
||||
version: link:../../config/config
|
||||
'@pnpm/core-loggers':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/core-loggers
|
||||
'@pnpm/crypto.base32-hash':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/crypto.base32-hash
|
||||
|
||||
Reference in New Issue
Block a user