feat(pnpm): add filterLog hook to pnpmfile (#3802)

Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
Cheng
2021-10-10 07:41:28 +08:00
committed by GitHub
parent 1efaaf706b
commit ef9d2719a1
12 changed files with 112 additions and 22 deletions

View 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`.

View File

@@ -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 }
}

View File

@@ -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),

View 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)
})

View File

@@ -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",

View File

@@ -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)
}

View File

@@ -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 () => {

View File

@@ -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')
)
})

View File

@@ -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)
})

View File

@@ -105,9 +105,6 @@
{
"path": "../plugin-commands-store"
},
{
"path": "../pnpmfile"
},
{
"path": "../read-package-json"
},

View File

@@ -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
View File

@@ -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