mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-23 23:29:17 -05:00
feat(pnpm): add filterLog hook to pnpmfile (#3802)
Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
7
.changeset/eight-humans-perform.md
Normal file
7
.changeset/eight-humans-perform.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@pnpm/default-reporter": minor
|
||||
"pnpm": minor
|
||||
"@pnpm/pnpmfile": minor
|
||||
---
|
||||
|
||||
New hook supported for filtering out info and warning logs: `filterLog(log) => boolean`.
|
||||
@@ -4,6 +4,7 @@ import os from 'os'
|
||||
import { LAYOUT_VERSION } from '@pnpm/constants'
|
||||
import PnpmError from '@pnpm/error'
|
||||
import globalBinDir from '@pnpm/global-bin-dir'
|
||||
import { requireHooks } from '@pnpm/pnpmfile'
|
||||
import camelcase from 'camelcase'
|
||||
import loadNpmConf from '@zkochan/npm-conf'
|
||||
import npmTypes from '@zkochan/npm-conf/lib/types'
|
||||
@@ -466,6 +467,10 @@ export default async (
|
||||
|
||||
pnpmConfig.workspaceConcurrency = getWorkspaceConcurrency(pnpmConfig.workspaceConcurrency)
|
||||
|
||||
if (!pnpmConfig.ignorePnpmfile) {
|
||||
pnpmConfig.hooks = requireHooks(pnpmConfig.lockfileDir ?? pnpmConfig.dir, pnpmConfig)
|
||||
}
|
||||
|
||||
return { config: pnpmConfig, warnings }
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Config } from '@pnpm/config'
|
||||
import * as logs from '@pnpm/core-loggers'
|
||||
import { LogLevel } from '@pnpm/logger'
|
||||
import * as Rx from 'rxjs'
|
||||
import { map, mergeAll } from 'rxjs/operators'
|
||||
import { filter, map, mergeAll } from 'rxjs/operators'
|
||||
import createDiffer from 'ansi-diff'
|
||||
import { EOL } from './constants'
|
||||
import mergeOutputs from './mergeOutputs'
|
||||
@@ -177,6 +177,10 @@ export function toOutput$ (
|
||||
}
|
||||
})
|
||||
}, 0)
|
||||
let other = Rx.from(otherPushStream)
|
||||
if (opts.context.config?.hooks?.filterLog != null) {
|
||||
other = other.pipe(filter(opts.context.config.hooks.filterLog))
|
||||
}
|
||||
const log$ = {
|
||||
context: Rx.from(contextPushStream),
|
||||
deprecation: Rx.from(deprecationPushStream),
|
||||
@@ -185,7 +189,7 @@ export function toOutput$ (
|
||||
installCheck: Rx.from(installCheckPushStream),
|
||||
lifecycle: Rx.from(lifecyclePushStream),
|
||||
link: Rx.from(linkPushStream),
|
||||
other: Rx.from(otherPushStream),
|
||||
other,
|
||||
packageImportMethod: Rx.from(packageImportMethodPushStream),
|
||||
packageManifest: Rx.from(packageManifestPushStream),
|
||||
progress: Rx.from(progressPushStream),
|
||||
|
||||
64
packages/default-reporter/test/filterLogHook.ts
Normal file
64
packages/default-reporter/test/filterLogHook.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { Log } from '@pnpm/core-loggers'
|
||||
import { toOutput$ } from '@pnpm/default-reporter'
|
||||
import logger, { createStreamParser } from '@pnpm/logger'
|
||||
|
||||
test('logger with filterLog hook', (done) => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: {
|
||||
hooks: {
|
||||
filterLog: (log: Log) => {
|
||||
if (log.level === 'debug') {
|
||||
return false
|
||||
}
|
||||
if (log.level === 'warn') {
|
||||
if (log.message === 'aaa') {
|
||||
return false
|
||||
}
|
||||
if (log.prefix === '/tmp') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
},
|
||||
} as any, // eslint-disable-line
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
|
||||
// debug level is filtered out
|
||||
logger.debug({
|
||||
message: 'debug message',
|
||||
})
|
||||
// message equals to 'aaa' is filtered out
|
||||
logger.warn({
|
||||
message: 'aaa',
|
||||
prefix: '/root',
|
||||
})
|
||||
logger.warn({
|
||||
message: 'bbb',
|
||||
prefix: '/root',
|
||||
})
|
||||
// prefix equals to '/tmp' is filtered out
|
||||
logger.warn({
|
||||
message: 'ccc',
|
||||
prefix: '/tmp',
|
||||
})
|
||||
|
||||
expect.assertions(1)
|
||||
|
||||
const subscription = output$.subscribe({
|
||||
complete: () => done(),
|
||||
error: done,
|
||||
next: (msg) => {
|
||||
expect(msg).toEqual(expect.stringContaining('bbb'))
|
||||
},
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
done()
|
||||
subscription.unsubscribe()
|
||||
}, 10)
|
||||
})
|
||||
@@ -51,7 +51,6 @@
|
||||
"@pnpm/plugin-commands-server": "workspace:3.0.35",
|
||||
"@pnpm/plugin-commands-setup": "workspace:1.1.7",
|
||||
"@pnpm/plugin-commands-store": "workspace:4.0.19",
|
||||
"@pnpm/pnpmfile": "workspace:1.0.5",
|
||||
"@pnpm/prepare": "workspace:0.0.26",
|
||||
"@pnpm/read-package-json": "workspace:5.0.4",
|
||||
"@pnpm/read-project-manifest": "workspace:2.0.5",
|
||||
|
||||
@@ -12,7 +12,6 @@ import findWorkspacePackages from '@pnpm/find-workspace-packages'
|
||||
import logger from '@pnpm/logger'
|
||||
import { ParsedCliArgs } from '@pnpm/parse-cli-args'
|
||||
import { node } from '@pnpm/plugin-commands-env'
|
||||
import { requireHooks } from '@pnpm/pnpmfile'
|
||||
import chalk from 'chalk'
|
||||
import checkForUpdates from './checkForUpdates'
|
||||
import pnpmCmds, { rcOptionsTypes } from './cmd'
|
||||
@@ -212,12 +211,6 @@ export default async function run (inputArgv: string[]) {
|
||||
// NOTE: we defer the next stage, otherwise reporter might not catch all the logs
|
||||
await new Promise<void>((resolve) => setTimeout(() => resolve(), 0))
|
||||
|
||||
// It would be nice to read the lockfile in @pnpm/config but in there reporting doesn't work yet
|
||||
// and some info logs are printed when hooks are required.
|
||||
if (!config.ignorePnpmfile) {
|
||||
config.hooks = requireHooks(config.lockfileDir ?? config.dir, config)
|
||||
}
|
||||
|
||||
if (!isCI && !selfUpdate && !config.offline && !config.preferOffline && !config.fallbackCommandUsed) {
|
||||
checkForUpdates(config).catch(() => { /* Ignore */ })
|
||||
}
|
||||
@@ -277,7 +270,7 @@ export default async function run (inputArgv: string[]) {
|
||||
}
|
||||
|
||||
function printError (message: string, hint?: string) {
|
||||
console.error(`${chalk.bgRed.black('\u2009ERROR\u2009')} ${chalk.red(message)}`)
|
||||
console.log(`${chalk.bgRed.black('\u2009ERROR\u2009')} ${chalk.red(message)}`)
|
||||
if (hint) {
|
||||
console.log(hint)
|
||||
}
|
||||
|
||||
@@ -84,10 +84,10 @@ test('pnpm fails when no command is specified', async () => {
|
||||
test('command fails when an unsupported flag is used', async () => {
|
||||
prepare()
|
||||
|
||||
const { status, stderr } = execPnpmSync(['update', '--save-dev'])
|
||||
const { status, stdout } = execPnpmSync(['update', '--save-dev'])
|
||||
|
||||
expect(status).toBe(1)
|
||||
expect(stderr.toString()).toMatch(/Unknown option: 'save-dev'/)
|
||||
expect(stdout.toString()).toMatch(/Unknown option: 'save-dev'/)
|
||||
})
|
||||
|
||||
test('command does not fail when a deprecated option is used', async () => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { PackageManifest } from '@pnpm/types'
|
||||
import prepare, { preparePackages } from '@pnpm/prepare'
|
||||
import loadJsonFile from 'load-json-file'
|
||||
import writeYamlFile from 'write-yaml-file'
|
||||
import { execPnpm } from './utils'
|
||||
import { execPnpm, execPnpmSync } from './utils'
|
||||
|
||||
test('readPackage hook in single project doesn\'t modify manifest', async () => {
|
||||
const project = prepare()
|
||||
@@ -79,3 +79,23 @@ test('readPackage hook in monorepo doesn\'t modify manifest', async () => {
|
||||
pkg = await loadJsonFile(path.resolve('project-a/package.json'))
|
||||
expect(pkg.dependencies).toBeFalsy() // remove & readPackage hook work
|
||||
})
|
||||
|
||||
test('filterLog hook filters peer dependency warning', async () => {
|
||||
prepare()
|
||||
const pnpmfile = `
|
||||
module.exports = { hooks: { filterLog } }
|
||||
function filterLog (log) {
|
||||
if (/requires a peer of rollup/.test(log.message)) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
`
|
||||
await fs.writeFile('.pnpmfile.cjs', pnpmfile, 'utf8')
|
||||
const result = execPnpmSync(['add', '@rollup/pluginutils@3.1.0'])
|
||||
|
||||
expect(result.status).toBe(0)
|
||||
expect(result.stdout.toString()).toEqual(
|
||||
expect.not.stringContaining('requires a peer of rollup')
|
||||
)
|
||||
})
|
||||
@@ -62,8 +62,8 @@ test('incorrect workspace manifest', async () => {
|
||||
|
||||
await writeYamlFile('pnpm-workspace.yml', { packages: ['**', '!store/**'] })
|
||||
|
||||
const { status, stderr } = execPnpmSync(['install'])
|
||||
expect(stderr.toString()).toMatch(/The workspace manifest file should be named "pnpm-workspace.yaml"/)
|
||||
const { status, stdout } = execPnpmSync(['install'])
|
||||
expect(stdout.toString()).toMatch(/The workspace manifest file should be named "pnpm-workspace.yaml"/)
|
||||
expect(status).toBe(1)
|
||||
})
|
||||
|
||||
|
||||
@@ -105,9 +105,6 @@
|
||||
{
|
||||
"path": "../plugin-commands-store"
|
||||
},
|
||||
{
|
||||
"path": "../pnpmfile"
|
||||
},
|
||||
{
|
||||
"path": "../read-package-json"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,7 @@ import { hookLogger } from '@pnpm/core-loggers'
|
||||
import logger from '@pnpm/logger'
|
||||
import pathAbsolute from 'path-absolute'
|
||||
import type { Lockfile } from '@pnpm/lockfile-types'
|
||||
import type { Log } from '@pnpm/core-loggers'
|
||||
import requirePnpmfile from './requirePnpmfile'
|
||||
|
||||
interface HookContext {
|
||||
@@ -13,6 +14,7 @@ interface Hooks {
|
||||
// eslint-disable-next-line
|
||||
readPackage?: (pkg: any, context: HookContext) => any
|
||||
afterAllResolved?: (lockfile: Lockfile, context: HookContext) => Lockfile
|
||||
filterLog?: (log: Log) => boolean
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
@@ -25,6 +27,7 @@ type Cook<T extends (...args: any[]) => any> = (
|
||||
export interface CookedHooks {
|
||||
readPackage?: Cook<Required<Hooks>['readPackage']>
|
||||
afterAllResolved?: Cook<Required<Hooks>['afterAllResolved']>
|
||||
filterLog?: Cook<Required<Hooks>['filterLog']>
|
||||
}
|
||||
|
||||
export default function requireHooks (
|
||||
@@ -51,7 +54,7 @@ export default function requireHooks (
|
||||
prefix,
|
||||
})
|
||||
}
|
||||
for (const hookName of ['readPackage', 'afterAllResolved']) {
|
||||
for (const hookName of ['readPackage', 'afterAllResolved', 'filterLog']) {
|
||||
if (globalHooks[hookName] && hooks[hookName]) {
|
||||
const globalHookContext = createReadPackageHookContext(globalPnpmfile.filename, prefix, hookName)
|
||||
const localHookContext = createReadPackageHookContext(pnpmFile.filename, prefix, hookName)
|
||||
|
||||
2
pnpm-lock.yaml
generated
2
pnpm-lock.yaml
generated
@@ -2513,7 +2513,6 @@ importers:
|
||||
'@pnpm/plugin-commands-server': workspace:3.0.35
|
||||
'@pnpm/plugin-commands-setup': workspace:1.1.7
|
||||
'@pnpm/plugin-commands-store': workspace:4.0.19
|
||||
'@pnpm/pnpmfile': workspace:1.0.5
|
||||
'@pnpm/prepare': workspace:0.0.26
|
||||
'@pnpm/read-package-json': workspace:5.0.4
|
||||
'@pnpm/read-project-manifest': workspace:2.0.5
|
||||
@@ -2606,7 +2605,6 @@ importers:
|
||||
'@pnpm/plugin-commands-server': link:../plugin-commands-server
|
||||
'@pnpm/plugin-commands-setup': link:../plugin-commands-setup
|
||||
'@pnpm/plugin-commands-store': link:../plugin-commands-store
|
||||
'@pnpm/pnpmfile': link:../pnpmfile
|
||||
'@pnpm/prepare': link:../../privatePackages/prepare
|
||||
'@pnpm/read-package-json': link:../read-package-json
|
||||
'@pnpm/read-project-manifest': link:../read-project-manifest
|
||||
|
||||
Reference in New Issue
Block a user