feat!: remove old API for custom fetchers (#10464)

This commit is contained in:
Zoltan Kochan
2026-01-15 11:57:48 +01:00
committed by GitHub
parent a8fe2d5298
commit 5beece9615
7 changed files with 10 additions and 117 deletions

View File

@@ -0,0 +1,5 @@
---
"pnpm": major
---
Removed support for `hooks.fetchers`. We now have a new API for custom fetchers and resolvers via the `fetchers` field of `pnpmfile`.

View File

@@ -77,17 +77,3 @@ export interface Fetchers {
git: GitFetcher
binary: BinaryFetcher
}
interface CustomFetcherFactoryOptions {
defaultFetchers: Fetchers
}
export type CustomFetcherFactory<Fetcher> = (opts: CustomFetcherFactoryOptions) => Fetcher
export interface CustomFetchers {
localTarball?: CustomFetcherFactory<FetchFunction>
remoteTarball?: CustomFetcherFactory<FetchFunction>
gitHostedTarball?: CustomFetcherFactory<FetchFunction>
directory?: CustomFetcherFactory<DirectoryFetcher>
git?: CustomFetcherFactory<GitFetcher>
}

View File

@@ -1,7 +1,6 @@
import { type PreResolutionHook } from '@pnpm/hooks.types'
import { type LockfileObject } from '@pnpm/lockfile.types'
import { type Log } from '@pnpm/core-loggers'
import { type CustomFetchers } from '@pnpm/fetcher-base'
import { type ImportIndexedPackageAsync } from '@pnpm/store-controller-types'
export interface HookContext {
@@ -17,10 +16,6 @@ export interface Hooks {
afterAllResolved?: (lockfile: LockfileObject, context: HookContext) => LockfileObject | Promise<LockfileObject>
filterLog?: (log: Log) => boolean
importPackage?: ImportIndexedPackageAsync
/**
* @deprecated Use top-level `fetchers` export instead. This will be removed in a future version.
*/
fetchers?: CustomFetchers
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Flexible hook signature for any config object
updateConfig?: (config: any) => any
}

View File

@@ -3,7 +3,6 @@ import { PnpmError } from '@pnpm/error'
import { hookLogger } from '@pnpm/core-loggers'
import { createHashFromMultipleFiles } from '@pnpm/crypto.hash'
import pathAbsolute from 'path-absolute'
import type { CustomFetchers } from '@pnpm/fetcher-base'
import { type ImportIndexedPackageAsync } from '@pnpm/store-controller-types'
import { type ReadPackageHook, type BeforePackingHook, type BaseManifest } from '@pnpm/types'
import { type LockfileObject } from '@pnpm/lockfile.types'
@@ -40,7 +39,6 @@ export interface CookedHooks {
filterLog?: Array<Cook<Required<Hooks>['filterLog']>>
updateConfig?: Array<Cook<Required<Hooks>['updateConfig']>>
importPackage?: ImportIndexedPackageAsync
fetchers?: CustomFetchers
customResolvers?: CustomResolver[]
customFetchers?: CustomFetcher[]
calculatePnpmfileChecksum?: () => Promise<string>
@@ -129,7 +127,6 @@ export async function requireHooks (
}
let importProvider: string | undefined
let fetchersProvider: string | undefined
const finderProviders: Record<string, string> = {}
// process hooks in order
@@ -204,18 +201,6 @@ export async function requireHooks (
importProvider = file
cookedHooks.importPackage = fileHooks.importPackage
}
// fetchers: only one allowed
if (fileHooks.fetchers) {
if (fetchersProvider) {
throw new PnpmError(
'MULTIPLE_FETCHERS',
`fetchers hook defined in both ${fetchersProvider} and ${file}`
)
}
fetchersProvider = file
cookedHooks.fetchers = fileHooks.fetchers
}
}
// Process top-level resolvers and fetchers exports

View File

@@ -7,19 +7,17 @@ import { type AgentOptions, createFetchFromRegistry } from '@pnpm/fetch'
import { type SslConfig } from '@pnpm/types'
import { type CustomResolver, type CustomFetcher as CustomFetcherHook } from '@pnpm/hooks.types'
import { type FetchFromRegistry, type GetAuthHeader, type RetryTimeoutOptions } from '@pnpm/fetching-types'
import type { CustomFetchers, GitFetcher, DirectoryFetcher, BinaryFetcher } from '@pnpm/fetcher-base'
import type { GitFetcher, DirectoryFetcher, BinaryFetcher } from '@pnpm/fetcher-base'
import { createDirectoryFetcher } from '@pnpm/directory-fetcher'
import { createGitFetcher } from '@pnpm/git-fetcher'
import { createTarballFetcher, type TarballFetchers } from '@pnpm/tarball-fetcher'
import { createGetAuthHeaderByURI } from '@pnpm/network.auth-header'
import { createBinaryFetcher } from '@pnpm/fetching.binary-fetcher'
import { map as mapValues } from 'ramda'
export type { ResolveFunction }
export type ClientOptions = {
authConfig: Record<string, string>
customFetchers?: CustomFetchers
customResolvers?: CustomResolver[]
customFetcherHooks?: CustomFetcherHook[]
ignoreScripts?: boolean
@@ -49,7 +47,7 @@ export function createClient (opts: ClientOptions): Client {
const { resolve, clearCache: clearResolutionCache } = _createResolver(fetchFromRegistry, getAuthHeader, { ...opts, customResolvers: opts.customResolvers })
return {
fetchers: createFetchers(fetchFromRegistry, getAuthHeader, opts, opts.customFetchers),
fetchers: createFetchers(fetchFromRegistry, getAuthHeader, opts),
resolve,
clearResolutionCache,
}
@@ -71,11 +69,10 @@ type Fetchers = {
function createFetchers (
fetchFromRegistry: FetchFromRegistry,
getAuthHeader: GetAuthHeader,
opts: Pick<ClientOptions, 'rawConfig' | 'retry' | 'gitShallowHosts' | 'resolveSymlinksInInjectedDirs' | 'unsafePerm' | 'includeOnlyPackageFiles' | 'offline' | 'fetchMinSpeedKiBps'>,
customFetchers?: CustomFetchers
opts: Pick<ClientOptions, 'rawConfig' | 'retry' | 'gitShallowHosts' | 'resolveSymlinksInInjectedDirs' | 'unsafePerm' | 'includeOnlyPackageFiles' | 'offline' | 'fetchMinSpeedKiBps'>
): Fetchers {
const tarballFetchers = createTarballFetcher(fetchFromRegistry, getAuthHeader, opts)
const defaultFetchers = {
return {
...tarballFetchers,
...createGitFetcher(opts),
...createDirectoryFetcher({ resolveSymlinks: opts.resolveSymlinksInInjectedDirs, includeOnlyPackageFiles: opts.includeOnlyPackageFiles }),
@@ -86,14 +83,4 @@ function createFetchers (
rawConfig: opts.rawConfig,
}),
}
const overwrites = mapValues(
(factory: any) => factory({ defaultFetchers }), // eslint-disable-line @typescript-eslint/no-explicit-any
customFetchers ?? {} as any // eslint-disable-line @typescript-eslint/no-explicit-any
)
return {
...defaultFetchers,
...overwrites,
}
}

View File

@@ -3,7 +3,7 @@ import path from 'path'
import { createHash } from '@pnpm/crypto.hash'
import { type PackageManifest } from '@pnpm/types'
import { prepare, preparePackages } from '@pnpm/prepare'
import { REGISTRY_MOCK_PORT, getIntegrity } from '@pnpm/registry-mock'
import { getIntegrity } from '@pnpm/registry-mock'
import { loadJsonFileSync } from 'load-json-file'
import { sync as writeYamlFile } from 'write-yaml-file'
import { execPnpm, execPnpmSync } from './utils/index.js'
@@ -158,70 +158,6 @@ test('importPackage hooks', async () => {
])
})
test('should use default fetchers if no custom fetchers are defined', async () => {
const project = prepare()
const pnpmfile = `
const fs = require('fs')
module.exports = {
hooks: {
fetchers: {}
}
}
`
writeYamlFile('pnpm-workspace.yaml', {
globalPnpmfile: '.pnpmfile.cjs',
})
fs.writeFileSync('.pnpmfile.cjs', pnpmfile, 'utf8')
await execPnpm(['add', 'is-positive@1.0.0'])
project.cafsHas('is-positive', '1.0.0')
})
test('custom fetcher can call default fetcher', async () => {
const project = prepare()
const pnpmfile = `
const fs = require('fs')
module.exports = {
hooks: {
fetchers: {
remoteTarball: ({ defaultFetchers }) => {
return (cafs, resolution, opts) => {
fs.writeFileSync('args.json', JSON.stringify({ resolution, opts }), 'utf8')
return defaultFetchers.remoteTarball(cafs, resolution, opts)
}
}
}
}
}
`
writeYamlFile('pnpm-workspace.yaml', {
globalPnpmfile: '.pnpmfile.cjs',
})
fs.writeFileSync('.pnpmfile.cjs', pnpmfile, 'utf8')
await execPnpm(['add', 'is-positive@1.0.0'])
project.cafsHas('is-positive', '1.0.0')
const args = loadJsonFileSync<any>('args.json') // eslint-disable-line
expect(args.resolution).toEqual({
integrity: 'sha512-xxzPGZ4P2uN6rROUa5N9Z7zTX6ERuE0hs6GUOc/cKBLF2NqKc16UwqHMt3tFg4CO6EBTE5UecUasg+3jZx3Ckg==',
tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`,
})
expect(args.opts).toBeDefined()
})
test('adding or changing pnpmfile should change pnpmfileChecksum and module structure', async () => {
const project = prepare({
dependencies: {

View File

@@ -65,7 +65,6 @@ export async function createNewStoreController (
) && !opts.registrySupportsTimeField
)
const { resolve, fetchers, clearResolutionCache } = createClient({
customFetchers: opts.hooks?.fetchers,
customResolvers: opts.hooks?.customResolvers,
customFetcherHooks: opts.hooks?.customFetchers,
userConfig: opts.userConfig,