feat: merge readPackage hook from opts and pnpmfile (#5403)

Close #5306

Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
Grzegorz Abramczyk
2022-09-25 21:59:02 +02:00
committed by GitHub
parent 54540b1763
commit 51566e34b0
35 changed files with 776 additions and 383 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/plugin-commands-deploy": patch
"pnpm": patch
---
Hooks should be applied on `pnpm deploy` [#5306](https://github.com/pnpm/pnpm/issues/5306).

View File

@@ -0,0 +1,5 @@
---
"@pnpm/hooks.read-package-hook": major
---
First release.

View File

@@ -0,0 +1,6 @@
---
"@pnpm/core": major
"@pnpm/default-reporter": major
---
Accept an array of hooks.

View File

@@ -0,0 +1,6 @@
---
"@pnpm/core": patch
"@pnpm/plugin-commands-installation": patch
---
Combining readPackage hook from options and from pnpmfile

View File

@@ -0,0 +1,5 @@
---
"@pnpm/pnpmfile": major
---
Returns an array of hook functions.

View File

@@ -0,0 +1,5 @@
---
"@pnpm/get-context": major
---
Pass readPackageHook as a separate option not as a subproperty of `hooks`.

View File

@@ -26,6 +26,7 @@
"@pnpm/graph-sequencer": "1.0.0",
"@pnpm/headless": "workspace:*",
"@pnpm/hoist": "workspace:*",
"@pnpm/hooks.read-package-hook": "workspace:*",
"@pnpm/lifecycle": "workspace:*",
"@pnpm/link-bins": "workspace:*",
"@pnpm/lockfile-file": "workspace:*",
@@ -39,7 +40,6 @@
"@pnpm/normalize-registries": "workspace:*",
"@pnpm/npm-package-arg": "^1.0.0",
"@pnpm/package-requester": "workspace:*",
"@pnpm/parse-overrides": "workspace:*",
"@pnpm/parse-wanted-dependency": "workspace:*",
"@pnpm/prune-lockfile": "workspace:*",
"@pnpm/read-modules-dir": "workspace:*",
@@ -52,7 +52,6 @@
"@pnpm/symlink-dependency": "workspace:*",
"@pnpm/types": "workspace:*",
"@pnpm/which-version-is-pinned": "workspace:*",
"@yarnpkg/extensions": "1.2.0-rc.1",
"@zkochan/rimraf": "^2.1.2",
"dependency-path": "workspace:*",
"is-inner-link": "^4.0.0",

View File

@@ -1,7 +1,7 @@
import resolveDependencies, { getWantedDependencies } from '@pnpm/resolve-dependencies'
import { PeerDependencyIssuesByProjects } from '@pnpm/types'
import getContext, { GetContextOptions, ProjectOptions } from '@pnpm/get-context'
import { createReadPackageHook } from './install'
import { createReadPackageHook } from '@pnpm/hooks.read-package-hook'
import { getPreferredVersionsFromLockfile } from './install/getPreferredVersions'
import { InstallOptions } from './install/extendInstallOptions'
import { DEFAULT_REGISTRIES } from '@pnpm/normalize-registries'

View File

@@ -1,6 +1,7 @@
import { WANTED_LOCKFILE } from '@pnpm/constants'
import PnpmError from '@pnpm/error'
import { HoistingLimits } from '@pnpm/headless'
import { createReadPackageHook } from '@pnpm/hooks.read-package-hook'
import { Lockfile } from '@pnpm/lockfile-file'
import { IncludedDependencies } from '@pnpm/modules-yaml'
import normalizeRegistries, { DEFAULT_REGISTRIES } from '@pnpm/normalize-registries'
@@ -71,9 +72,9 @@ export interface StrictInstallOptions {
}
pruneLockfileImporters: boolean
hooks: {
readPackage?: ReadPackageHook
readPackage?: ReadPackageHook[]
preResolution?: (ctx: PreResolutionHookContext) => Promise<void>
afterAllResolved?: (lockfile: Lockfile) => Lockfile | Promise<Lockfile>
afterAllResolved?: Array<(lockfile: Lockfile) => Lockfile | Promise<Lockfile>>
}
sideEffectsCacheRead: boolean
sideEffectsCacheWrite: boolean
@@ -198,9 +199,13 @@ const defaults = async (opts: InstallOptions) => {
} as StrictInstallOptions
}
export type ProcessedInstallOptions = StrictInstallOptions & {
readPackageHook?: ReadPackageHook
}
export default async (
opts: InstallOptions
): Promise<StrictInstallOptions> => {
): Promise<ProcessedInstallOptions> => {
if (opts) {
for (const key in opts) {
if (opts[key] === undefined) {
@@ -212,11 +217,19 @@ export default async (
throw new PnpmError('CONFIG_CONFLICT_BUILT_DEPENDENCIES', 'Cannot have both neverBuiltDependencies and onlyBuiltDependencies')
}
const defaultOpts = await defaults(opts)
const extendedOpts = {
const extendedOpts: ProcessedInstallOptions = {
...defaultOpts,
...opts,
storeDir: defaultOpts.storeDir,
}
extendedOpts.readPackageHook = createReadPackageHook({
ignoreCompatibilityDb: extendedOpts.ignoreCompatibilityDb,
readPackageHook: extendedOpts.hooks?.readPackage,
overrides: extendedOpts.overrides,
lockfileDir: extendedOpts.lockfileDir,
packageExtensions: extendedOpts.packageExtensions,
peerDependencyRules: extendedOpts.peerDependencyRules,
})
if (extendedOpts.lockfileOnly) {
extendedOpts.ignoreScripts = true
if (!extendedOpts.useLockfile) {

View File

@@ -50,13 +50,10 @@ import {
import {
DependenciesField,
DependencyManifest,
PackageExtension,
PeerDependencyIssues,
PeerDependencyRules,
ProjectManifest,
ReadPackageHook,
} from '@pnpm/types'
import { packageExtensions as compatPackageExtensions } from '@yarnpkg/extensions'
import rimraf from '@zkochan/rimraf'
import isInnerLink from 'is-inner-link'
import pFilter from 'p-filter'
@@ -71,12 +68,9 @@ import unnest from 'ramda/src/unnest'
import parseWantedDependencies from '../parseWantedDependencies'
import removeDeps from '../uninstall/removeDeps'
import allProjectsAreUpToDate from './allProjectsAreUpToDate'
import createPackageExtender from './createPackageExtender'
import createVersionsOverrider from './createVersionsOverrider'
import createPeerDependencyPatcher from './createPeerDependencyPatcher'
import extendOptions, {
InstallOptions,
StrictInstallOptions,
ProcessedInstallOptions as StrictInstallOptions,
} from './extendInstallOptions'
import { getPreferredVersionsFromLockfile, getAllUniqueSpecs } from './getPreferredVersions'
import linkPackages from './link'
@@ -152,6 +146,9 @@ export type MutatedProject = ProjectOptions & DependenciesMutation
export type MutateModulesOptions = InstallOptions & {
preferredVersions?: PreferredVersions
hooks?: {
readPackage?: ReadPackageHook[] | ReadPackageHook
} | InstallOptions['hooks']
}
export async function mutateModules (
@@ -176,14 +173,6 @@ export async function mutateModules (
// When running install/update on a subset of projects, the root project might not be included,
// so reading its manifest explicitly here.
await safeReadProjectManifestOnly(opts.lockfileDir)
opts.hooks.readPackage = createReadPackageHook({
ignoreCompatibilityDb: opts.ignoreCompatibilityDb,
readPackageHook: opts.hooks.readPackage,
overrides: opts.overrides,
lockfileDir: opts.lockfileDir,
packageExtensions: opts.packageExtensions,
peerDependencyRules: opts.peerDependencyRules,
})
const ctx = await getContext(projects, opts)
@@ -571,55 +560,6 @@ export function createObjectChecksum (obj: Object) {
return crypto.createHash('md5').update(s).digest('hex')
}
export function createReadPackageHook (
{
ignoreCompatibilityDb,
lockfileDir,
overrides,
packageExtensions,
peerDependencyRules,
readPackageHook,
}: {
ignoreCompatibilityDb?: boolean
lockfileDir: string
overrides?: Record<string, string>
packageExtensions?: Record<string, PackageExtension>
peerDependencyRules?: PeerDependencyRules
readPackageHook?: ReadPackageHook
}
): ReadPackageHook | undefined {
const hooks: ReadPackageHook[] = []
if (!isEmpty(overrides ?? {})) {
hooks.push(createVersionsOverrider(overrides!, lockfileDir))
}
if (!ignoreCompatibilityDb) {
hooks.push(createPackageExtender(fromPairs(compatPackageExtensions)))
}
if (!isEmpty(packageExtensions ?? {})) {
hooks.push(createPackageExtender(packageExtensions!))
}
if (
peerDependencyRules != null &&
(
!isEmpty(peerDependencyRules.ignoreMissing) ||
!isEmpty(peerDependencyRules.allowedVersions) ||
!isEmpty(peerDependencyRules.allowAny)
)
) {
hooks.push(createPeerDependencyPatcher(peerDependencyRules))
}
if (hooks.length === 0) {
return readPackageHook
}
const readPackageAndExtend = hooks.length === 1
? hooks[0]
: pipeWith(async (f, res) => f(await res), hooks as any) as ReadPackageHook // eslint-disable-line @typescript-eslint/no-explicit-any
if (readPackageHook != null) {
return (async (manifest: ProjectManifest, dir?: string) => readPackageAndExtend(await readPackageHook(manifest, dir), dir)) as ReadPackageHook
}
return readPackageAndExtend
}
function cacheExpired (prunedAt: string, maxAgeInMinutes: number) {
return ((Date.now() - new Date(prunedAt).valueOf()) / (1000 * 60)) > maxAgeInMinutes
}
@@ -827,7 +767,9 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
engineStrict: opts.engineStrict,
force: opts.force,
forceFullResolution,
hooks: opts.hooks,
hooks: {
readPackage: opts.readPackageHook,
},
linkWorkspacePackagesDepth: opts.linkWorkspacePackagesDepth ?? (opts.saveWorkspaceProtocol ? 0 : -1),
lockfileDir: opts.lockfileDir,
nodeVersion: opts.nodeVersion,
@@ -882,7 +824,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
})
newLockfile = ((opts.hooks?.afterAllResolved) != null)
? await opts.hooks?.afterAllResolved(newLockfile)
? await pipeWith(async (f, res) => f(await res), opts.hooks.afterAllResolved as any)(newLockfile) as Lockfile // eslint-disable-line
: newLockfile
if (opts.updateLockfileMinorVersion) {

View File

@@ -33,8 +33,8 @@ test('readPackage, afterAllResolved hooks', async () => {
await addDependenciesToPackage({}, ['@pnpm.e2e/pkg-with-1-dep'], await testDefaults({
hooks: {
afterAllResolved,
readPackage: readPackageHook,
afterAllResolved: [afterAllResolved],
readPackage: [readPackageHook],
},
}))
@@ -71,8 +71,8 @@ test('readPackage, afterAllResolved async hooks', async () => {
await addDependenciesToPackage({}, ['@pnpm.e2e/pkg-with-1-dep'], await testDefaults({
hooks: {
afterAllResolved,
readPackage: readPackageHook,
afterAllResolved: [afterAllResolved],
readPackage: [readPackageHook],
},
}))
@@ -83,3 +83,42 @@ test('readPackage, afterAllResolved async hooks', async () => {
const wantedLockfile = await project.readLockfile()
expect(wantedLockfile['foo']).toEqual('foo')
})
test('readPackage hooks array', async () => {
const project = prepareEmpty()
// w/o the hook, 100.1.0 would be installed
await addDistTag({ package: '@pnpm.e2e/dep-of-pkg-with-1-dep', version: '100.1.0', distTag: 'latest' })
function readPackageHook1 (manifest: PackageManifest) {
switch (manifest.name) {
case '@pnpm.e2e/pkg-with-1-dep':
if (manifest.dependencies == null) {
throw new Error('@pnpm.e2e/pkg-with-1-dep expected to have a dependencies field')
}
manifest.dependencies['@pnpm.e2e/dep-of-pkg-with-1-dep'] = '50.0.0'
break
}
return manifest
}
function readPackageHook2 (manifest: PackageManifest) {
switch (manifest.name) {
case '@pnpm.e2e/pkg-with-1-dep':
if (manifest.dependencies == null) {
throw new Error('@pnpm.e2e/pkg-with-1-dep expected to have a dependencies field')
}
manifest.dependencies['@pnpm.e2e/dep-of-pkg-with-1-dep'] = '100.0.0'
break
}
return manifest
}
await addDependenciesToPackage({}, ['@pnpm.e2e/pkg-with-1-dep'], await testDefaults({
hooks: {
readPackage: [readPackageHook1, readPackageHook2],
},
}))
await project.storeHas('@pnpm.e2e/dep-of-pkg-with-1-dep', '100.0.0')
})

View File

@@ -63,6 +63,9 @@
{
"path": "../hoist"
},
{
"path": "../hooks.read-package-hook"
},
{
"path": "../lifecycle"
},
@@ -102,9 +105,6 @@
{
"path": "../package-store"
},
{
"path": "../parse-overrides"
},
{
"path": "../parse-wanted-dependency"
},

View File

@@ -194,7 +194,11 @@ 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 filterLogs = opts.context.config.hooks.filterLog
const filterFn = filterLogs.length === 1
? filterLogs[0]
: (log: logs.Log) => filterLogs.every!((filterLog) => filterLog(log))
other = other.pipe(filter(filterFn))
}
const log$ = {
context: Rx.from(contextPushStream),

View File

@@ -8,7 +8,7 @@ test('logger with filterLog hook', (done) => {
argv: ['install'],
config: {
hooks: {
filterLog: (log: Log) => {
filterLog: [(log: Log) => {
if (log.level === 'debug') {
return false
}
@@ -21,7 +21,7 @@ test('logger with filterLog hook', (done) => {
}
}
return true
},
}],
},
} as any, // eslint-disable-line
},
@@ -61,4 +61,4 @@ test('logger with filterLog hook', (done) => {
done()
subscription.unsubscribe()
}, 10)
})
})

View File

@@ -75,9 +75,7 @@ export interface GetContextOptions {
lockfileDir: string
modulesDir?: string
nodeLinker: 'isolated' | 'hoisted' | 'pnp'
hooks?: {
readPackage?: ReadPackageHook
}
readPackageHook?: ReadPackageHook
include?: IncludedDependencies
registries: Registries
storeDir: string
@@ -135,10 +133,10 @@ export default async function getContext<T> (
prefix: project.rootDir,
})
})
if ((opts.hooks?.readPackage) != null) {
if (opts.readPackageHook != null) {
for (const project of importersContext.projects) {
project.originalManifest = project.manifest
project.manifest = await opts.hooks.readPackage(clone(project.manifest), project.rootDir)
project.manifest = await opts.readPackageHook(clone(project.manifest), project.rootDir)
}
}
@@ -372,9 +370,7 @@ export async function getContextForSingleImporter (
lockfileDir: string
nodeLinker: 'isolated' | 'hoisted' | 'pnp'
modulesDir?: string
hooks?: {
readPackage?: ReadPackageHook
}
readPackageHook?: ReadPackageHook
include?: IncludedDependencies
dir: string
registries: Registries
@@ -463,7 +459,7 @@ export async function getContextForSingleImporter (
importerId,
include: opts.include ?? include,
lockfileDir: opts.lockfileDir,
manifest: await opts.hooks?.readPackage?.(manifest) ?? manifest,
manifest: await opts.readPackageHook?.(manifest) ?? manifest,
modulesDir,
modulesFile: modules,
pendingBuilds,

View File

@@ -0,0 +1,13 @@
# @pnpm/hooks.read-package-hook
> Creates the default package reader hook used by pnpm
## Installation
```sh
pnpm add @pnpm/hooks.read-package-hook
```
## License
MIT

View File

@@ -0,0 +1,3 @@
const config = require('../../jest.config.js');
module.exports = Object.assign({}, config, {});

View File

@@ -0,0 +1,51 @@
{
"name": "@pnpm/hooks.read-package-hook",
"version": "0.0.0",
"description": "Creates the default package reader hook used by pnpm",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"lib",
"!*.map"
],
"scripts": {
"lint": "eslint src/**/*.ts test/**/*.ts",
"_test": "jest",
"test": "pnpm run compile && pnpm run _test",
"prepublishOnly": "pnpm run compile",
"compile": "tsc --build && pnpm run lint --fix"
},
"repository": "https://github.com/pnpm/pnpm/blob/main/packages/hooks.read-package-hook",
"keywords": [
"pnpm7"
],
"engines": {
"node": ">=14.6"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/pnpm/pnpm/issues"
},
"homepage": "https://github.com/pnpm/pnpm/blob/main/packages/hooks.read-package-hook#readme",
"dependencies": {
"@pnpm/matcher": "workspace:*",
"@pnpm/parse-overrides": "workspace:*",
"@pnpm/parse-wanted-dependency": "workspace:*",
"@pnpm/types": "workspace:*",
"@yarnpkg/extensions": "1.2.0-rc.1",
"normalize-path": "^3.0.0",
"ramda": "npm:@pnpm/ramda@0.28.1",
"semver": "^7.3.7"
},
"devDependencies": {
"@pnpm/hooks.read-package-hook": "workspace:*",
"@types/normalize-path": "^3.0.0",
"@types/ramda": "0.28.15",
"@types/semver": "7.3.10",
"@yarnpkg/core": "4.0.0-rc.14"
},
"funding": "https://opencollective.com/pnpm",
"exports": {
".": "./lib/index.js"
}
}

View File

@@ -0,0 +1,66 @@
import { packageExtensions as compatPackageExtensions } from '@yarnpkg/extensions'
import {
PackageExtension,
PackageManifest,
PeerDependencyRules,
ProjectManifest,
ReadPackageHook,
} from '@pnpm/types'
import fromPairs from 'ramda/src/fromPairs'
import isEmpty from 'ramda/src/isEmpty'
import pipeWith from 'ramda/src/pipeWith'
import createPackageExtender from './createPackageExtender'
import createVersionsOverrider from './createVersionsOverrider'
import createPeerDependencyPatcher from './createPeerDependencyPatcher'
export function createReadPackageHook (
{
ignoreCompatibilityDb,
lockfileDir,
overrides,
packageExtensions,
peerDependencyRules,
readPackageHook,
}: {
ignoreCompatibilityDb?: boolean
lockfileDir: string
overrides?: Record<string, string>
packageExtensions?: Record<string, PackageExtension>
peerDependencyRules?: PeerDependencyRules
readPackageHook?: ReadPackageHook[] | ReadPackageHook
}
): ReadPackageHook | undefined {
const hooks: ReadPackageHook[] = []
if (!isEmpty(overrides ?? {})) {
hooks.push(createVersionsOverrider(overrides!, lockfileDir))
}
if (!ignoreCompatibilityDb) {
hooks.push(createPackageExtender(fromPairs(compatPackageExtensions)))
}
if (!isEmpty(packageExtensions ?? {})) {
hooks.push(createPackageExtender(packageExtensions!))
}
if (
peerDependencyRules != null &&
(
!isEmpty(peerDependencyRules.ignoreMissing) ||
!isEmpty(peerDependencyRules.allowedVersions) ||
!isEmpty(peerDependencyRules.allowAny)
)
) {
hooks.push(createPeerDependencyPatcher(peerDependencyRules))
}
if (Array.isArray(readPackageHook)) {
hooks.push(...readPackageHook)
} else if (readPackageHook) {
hooks.push(readPackageHook)
}
if (hooks.length === 0) {
return undefined
}
const readPackageAndExtend = hooks.length === 1
? hooks[0]
: ((pkg: PackageManifest | ProjectManifest, dir: string) => pipeWith(async (f, res) => f(await res, dir), hooks as any)(pkg)) as ReadPackageHook // eslint-disable-line @typescript-eslint/no-explicit-any
return readPackageAndExtend
}

View File

@@ -0,0 +1 @@
export { createReadPackageHook } from './createReadPackageHook'

View File

@@ -1,4 +1,4 @@
import createPackageExtender from '../../lib/install/createPackageExtender'
import createPackageExtender from '../lib/createPackageExtender'
const packageExtender = createPackageExtender({
foo: {

View File

@@ -1,4 +1,4 @@
import createPeerDependencyPatcher from '../../lib/install/createPeerDependencyPatcher'
import createPeerDependencyPatcher from '../lib/createPeerDependencyPatcher'
test('createPeerDependencyPatcher() ignores missing', () => {
const patcher = createPeerDependencyPatcher({

View File

@@ -1,5 +1,5 @@
import path from 'path'
import createVersionsOverrider from '../../lib/install/createVersionsOverrider'
import createVersionsOverrider from '../lib/createVersionsOverrider'
test('createVersionsOverrider() matches subranges', () => {
const overrider = createVersionsOverrider({

View File

@@ -0,0 +1,25 @@
{
"extends": "@pnpm/tsconfig",
"compilerOptions": {
"outDir": "lib",
"rootDir": "src"
},
"include": [
"src/**/*.ts",
"../../typings/**/*.d.ts"
],
"references": [
{
"path": "../matcher"
},
{
"path": "../parse-overrides"
},
{
"path": "../parse-wanted-dependency"
},
{
"path": "../types"
}
]
}

View File

@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"include": [
"src/**/*.ts",
"test/**/*.ts",
"../../typings/**/*.d.ts"
]
}

View File

@@ -73,15 +73,15 @@ export async function handler (
await rimraf(deployDir)
await fs.promises.mkdir(deployDir, { recursive: true })
await copyProject(deployedDir, deployDir)
const readPackageHook = opts.hooks?.readPackage
// eslint-disable-next-line
const newReadPackageHook = readPackageHook ? (async (pkg: any, context: any) => deployHook(await readPackageHook(pkg, context))) : deployHook
await install.handler({
...opts,
depth: Infinity,
hooks: {
...opts.hooks,
readPackage: newReadPackageHook,
readPackage: [
...(opts.hooks?.readPackage ?? []),
deployHook,
],
},
frozenLockfile: false,
preferFrozenLockfile: false,

View File

@@ -289,7 +289,17 @@ export default async function recursive (
const limitInstallation = pLimit(opts.workspaceConcurrency ?? 4)
await Promise.all(pkgPaths.map(async (rootDir: string) =>
limitInstallation(async () => {
const hooks = opts.ignorePnpmfile ? {} : requireHooks(rootDir, opts)
const hooks = opts.ignorePnpmfile
? {}
: (() => {
const pnpmfileHooks = requireHooks(rootDir, opts)
return {
...opts.hooks,
...pnpmfileHooks,
afterAllResolved: [...(pnpmfileHooks.afterAllResolved ?? []), ...(opts.hooks?.afterAllResolved ?? [])],
readPackage: [...(pnpmfileHooks.readPackage ?? []), ...(opts.hooks?.readPackage ?? [])],
}
})()
try {
if (opts.ignoredPackages?.has(rootDir)) {
return

View File

@@ -715,3 +715,45 @@ test('installing in monorepo with shared lockfile should work on virtual drives'
await projects['project-1'].has('is-positive')
})
test('pass readPackage with shared lockfile', async () => {
const projects = preparePackages([
{
name: 'project-1',
version: '1.0.0',
dependencies: {
'is-negative': '1.0.0',
},
},
{
name: 'project-2',
version: '1.0.0',
dependencies: {
'is-negative': '1.0.0',
},
},
])
await install.handler({
...DEFAULT_OPTS,
...await readProjects(process.cwd(), []),
dir: process.cwd(),
recursive: true,
workspaceDir: process.cwd(),
hooks: {
readPackage: [
(pkg) => ({
...pkg,
dependencies: {
'is-positive': '1.0.0',
},
}),
],
},
})
await projects['project-1'].has('is-positive')
await projects['project-1'].hasNot('is-negative')
await projects['project-2'].has('is-positive')
await projects['project-2'].hasNot('is-negative')
})

View File

@@ -18,7 +18,7 @@ export type PnpmOptions = Omit<Config, 'reporter'> & {
}
hooks?: {
readPackage?: ReadPackageHook
readPackage?: ReadPackageHook[]
}
ignoreFile?: (filename: string) => boolean

View File

@@ -30,10 +30,10 @@ type Cook<T extends (...args: any[]) => any> = (
) => ReturnType<T>
export interface CookedHooks {
readPackage?: Cook<Required<Hooks>['readPackage']>
readPackage?: Array<Cook<Required<Hooks>['readPackage']>>
preResolution?: Cook<Required<Hooks>['preResolution']>
afterAllResolved?: Cook<Required<Hooks>['afterAllResolved']>
filterLog?: Cook<Required<Hooks>['filterLog']>
afterAllResolved?: Array<Cook<Required<Hooks>['afterAllResolved']>>
filterLog?: Array<Cook<Required<Hooks>['filterLog']>>
importPackage?: ImportIndexedPackage
fetchers?: CustomFetchers
}
@@ -52,37 +52,31 @@ export default function requireHooks (
requirePnpmfile(path.join(prefix, '.pnpmfile.cjs'), prefix)
let hooks: Hooks = pnpmFile?.hooks
if (!globalHooks && !hooks) return {}
if (!globalHooks && !hooks) return { afterAllResolved: [], filterLog: [], readPackage: [] }
globalHooks = globalHooks || {}
hooks = hooks || {}
const cookedHooks: CookedHooks = {}
const cookedHooks: CookedHooks & Required<Pick<CookedHooks, 'filterLog'>> = {
afterAllResolved: [],
filterLog: [],
readPackage: [],
}
for (const hookName of ['readPackage', 'afterAllResolved']) {
if (globalHooks[hookName] && hooks[hookName]) {
const globalHookContext = createReadPackageHookContext(globalPnpmfile.filename, prefix, hookName)
const localHookContext = createReadPackageHookContext(pnpmFile.filename, prefix, hookName)
// the `arg` is a package manifest in case of readPackage() and a lockfile object in case of afterAllResolved()
cookedHooks[hookName] = async (arg: object) => {
return hooks[hookName](
await globalHooks[hookName](arg, globalHookContext),
localHookContext
)
}
} else if (globalHooks[hookName]) {
if (globalHooks[hookName]) {
const globalHook = globalHooks[hookName]
const context = createReadPackageHookContext(globalPnpmfile.filename, prefix, hookName)
cookedHooks[hookName] = (pkg: object) => globalHook(pkg, context)
} else if (hooks[hookName]) {
cookedHooks[hookName].push((pkg: object) => globalHook(pkg, context))
}
if (hooks[hookName]) {
const hook = hooks[hookName]
const context = createReadPackageHookContext(pnpmFile.filename, prefix, hookName)
cookedHooks[hookName] = (pkg: object) => hook(pkg, context)
cookedHooks[hookName].push((pkg: object) => hook(pkg, context))
}
}
const globalFilterLog = globalHooks.filterLog
const filterLog = hooks.filterLog
if (globalFilterLog != null && filterLog != null) {
cookedHooks.filterLog = (log: Log) => globalFilterLog(log) && filterLog(log)
} else {
cookedHooks.filterLog = globalFilterLog ?? filterLog
if (globalHooks.filterLog != null) {
cookedHooks.filterLog.push(globalHooks.filterLog)
}
if (hooks.filterLog != null) {
cookedHooks.filterLog.push(hooks.filterLog)
}
// `importPackage`, `preResolution` and `fetchers` can only be defined via a global pnpmfile

View File

@@ -1,4 +1,5 @@
import path from 'path'
import { Log } from '@pnpm/core-loggers'
import { requireHooks, requirePnpmfile, BadReadPackageHookError } from '@pnpm/pnpmfile'
test('ignoring a pnpmfile that exports undefined', () => {
@@ -29,12 +30,14 @@ test('filterLog hook combines with the global hook', () => {
const hooks = requireHooks(__dirname, { globalPnpmfile, pnpmfile })
expect(hooks.filterLog).toBeDefined()
expect(hooks.filterLog!({
expect(hooks.filterLog!.length).toBe(2)
const filterLog = (log: Log) => hooks.filterLog!.every((hook) => hook(log))
expect(filterLog({
name: 'pnpm:summary',
level: 'error',
prefix: 'test',
})).toBeTruthy()
expect(hooks.filterLog!({
expect(filterLog({
name: 'pnpm:summary',
level: 'debug',
prefix: 'test',

659
pnpm-lock.yaml generated
View File

File diff suppressed because it is too large Load Diff