mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-23 23:29:17 -05:00
feat: merge readPackage hook from opts and pnpmfile (#5403)
Close #5306 Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
committed by
GitHub
parent
54540b1763
commit
51566e34b0
6
.changeset/breezy-weeks-rescue.md
Normal file
6
.changeset/breezy-weeks-rescue.md
Normal 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).
|
||||
5
.changeset/curvy-socks-press.md
Normal file
5
.changeset/curvy-socks-press.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/hooks.read-package-hook": major
|
||||
---
|
||||
|
||||
First release.
|
||||
6
.changeset/plenty-terms-count.md
Normal file
6
.changeset/plenty-terms-count.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/core": major
|
||||
"@pnpm/default-reporter": major
|
||||
---
|
||||
|
||||
Accept an array of hooks.
|
||||
6
.changeset/rich-grapes-jog.md
Normal file
6
.changeset/rich-grapes-jog.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/core": patch
|
||||
"@pnpm/plugin-commands-installation": patch
|
||||
---
|
||||
|
||||
Combining readPackage hook from options and from pnpmfile
|
||||
5
.changeset/silly-nails-remember.md
Normal file
5
.changeset/silly-nails-remember.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/pnpmfile": major
|
||||
---
|
||||
|
||||
Returns an array of hook functions.
|
||||
5
.changeset/tender-radios-hug.md
Normal file
5
.changeset/tender-radios-hug.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/get-context": major
|
||||
---
|
||||
|
||||
Pass readPackageHook as a separate option not as a subproperty of `hooks`.
|
||||
@@ -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",
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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')
|
||||
})
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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,
|
||||
|
||||
13
packages/hooks.read-package-hook/README.md
Normal file
13
packages/hooks.read-package-hook/README.md
Normal 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
|
||||
3
packages/hooks.read-package-hook/jest.config.js
Normal file
3
packages/hooks.read-package-hook/jest.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const config = require('../../jest.config.js');
|
||||
|
||||
module.exports = Object.assign({}, config, {});
|
||||
51
packages/hooks.read-package-hook/package.json
Normal file
51
packages/hooks.read-package-hook/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
1
packages/hooks.read-package-hook/src/index.ts
Normal file
1
packages/hooks.read-package-hook/src/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { createReadPackageHook } from './createReadPackageHook'
|
||||
@@ -1,4 +1,4 @@
|
||||
import createPackageExtender from '../../lib/install/createPackageExtender'
|
||||
import createPackageExtender from '../lib/createPackageExtender'
|
||||
|
||||
const packageExtender = createPackageExtender({
|
||||
foo: {
|
||||
@@ -1,4 +1,4 @@
|
||||
import createPeerDependencyPatcher from '../../lib/install/createPeerDependencyPatcher'
|
||||
import createPeerDependencyPatcher from '../lib/createPeerDependencyPatcher'
|
||||
|
||||
test('createPeerDependencyPatcher() ignores missing', () => {
|
||||
const patcher = createPeerDependencyPatcher({
|
||||
@@ -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({
|
||||
25
packages/hooks.read-package-hook/tsconfig.json
Normal file
25
packages/hooks.read-package-hook/tsconfig.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
8
packages/hooks.read-package-hook/tsconfig.lint.json
Normal file
8
packages/hooks.read-package-hook/tsconfig.lint.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"test/**/*.ts",
|
||||
"../../typings/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
})
|
||||
|
||||
@@ -18,7 +18,7 @@ export type PnpmOptions = Omit<Config, 'reporter'> & {
|
||||
}
|
||||
|
||||
hooks?: {
|
||||
readPackage?: ReadPackageHook
|
||||
readPackage?: ReadPackageHook[]
|
||||
}
|
||||
|
||||
ignoreFile?: (filename: string) => boolean
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
659
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user