.pnpmfile.mjs
pnpm을 사용하면 특수 기능 (후크)을 통해 설치 프로세스에 직접 연결할 수 있습니다. Hook은 .pnpmfile.mjs(ESM) 또는 .pnpmfile.cjs(CommonJS)라는 파일에 선언할 수 있습니다.
기본적으로 .pnpmfile.mjs는 lockfile과 같은 디렉터리에 있어야 합니다. 예를 들어, 공유 lockfile이 있는 워크스페이스에서는 .pnpmfile.mjs가 모노레포의 루트에 있어야 합니다.
In a workspace where each project has its own lockfile
(sharedWorkspaceLockfile: false), each project runs the hooks of the
pnpmfile in its own directory, then those of the pnpmfile in the root of the
monorepo.
후크#
요약#
| 후크 함수 | 처리 | 사용 |
|---|---|---|
hooks.readPackage(pkg, context): pkg |
pnpm이 의존성 패키지 매니페스트를 파싱한 후에 호출됩니다. | 종속성의 package.json을 변경할 수 있습니다. |
hooks.afterAllResolved(lockfile, context): lockfile |
의존성이 해결된 후에 호출됩니다. | lockfile을 변경할 수 있습니다. |
hooks.beforePacking(pkg): pkg |
패키지를 pack/publish하는 중 tarball을 생성하기 전에 호출됩니다 | 게시되는 package.json을 사용자 지정할 수 있습니다 |
resolvers |
패키지 해석 중에 호출됩니다. | 사용자 정의 패키지 리졸버를 등록할 수 있습니다. |
fetchers |
패키지를 가져오는 중에 호출됩니다. | 사용자 정의 패키지 페처를 등록할 수 있습니다. |
hooks.readPackage(pkg, context): pkg | Promise<pkg>#
Allows you to mutate a dependency's package.json after parsing and before
resolution. 이러한 변형은 파일시스템에 저장되지 않지만, lockfile에서 해결되는 내용과 이에 따라 설치되는 내용에 영향을 줍니다.
pnpm records a checksum of the pnpmfile in the lockfile. When the pnpmfile changes, pnpm resolves the dependencies again.
팁
package.json의 변경 사항을 파일 시스템에 저장해야 하는 경우, pnpm patch 명령을 사용하여 package.json 파일을 패치해야 합니다.
예를 들어 종속성의 bin 필드를 제거하려는 경우에 유용할 수 있습니다.
인수#
pkg- 패키지의 매니페스트입니다. 레지스트리의 응답 또는package.json내용입니다.context- 해당 단계의 컨텍스트 객체입니다.#log(msg)메서드를 사용하면 해당 단계에 대한 디버그 로그를 사용할 수 있습니다.
사용#
.pnpmfile.mjs 예시(종속성의 종속성을 변경):
function readPackage(pkg, context) {
// Override the manifest of foo@1.x after downloading it from the registry
if (pkg.name === 'foo' && pkg.version?.startsWith('1.')) {
// Replace bar@x.x.x with bar@2.0.0
pkg.dependencies = {
...pkg.dependencies,
bar: '^2.0.0'
}
context.log('bar@1 => bar@2 in dependencies of foo')
}
// This will change any packages using baz@x.x.x to use baz@1.2.3
if (pkg.dependencies?.baz) {
pkg.dependencies.baz = '1.2.3';
}
return pkg
}
export const hooks = {
readPackage
}
알려진 제약 사항#
readPackage를 통해 종속성의 매니페스트에서 scripts 필드를 제거해도 pnpm이 해당 종속성을 빌드하는 것을 막을 수는 없습니다. 종속성을 빌드할 때 pnpm은 패키지 아카이브에서 패키지의 package.json을 읽으며, 이는 hook의 영향을 받지 않습니다. To ignore a package's build, use the
allowBuilds field.
hooks.updateConfig(config): config | Promise<config>#
버전 10.8.0에서 추가됨
이 훅으로 pnpm에서 사용하는 설정을 수정할 수 있습니다. 이 hook은 configDependencies와 함께 사용할 때 가장 유용하며, 서로 다른 Git 저장소 간에 설정을 공유하고 재사용할 수 있습니다.
예를 들어, @pnpm/plugin-better-defaults는 updateConfig hook을 사용하여 엄선된 권장 설정 모음을 적용합니다.
사용 예#
export const hooks = {
updateConfig (config) {
return Object.assign(config, {
enablePrePostScripts: false,
optimisticRepeatInstall: true,
resolutionMode: 'lowest-direct',
verifyDepsBeforeRun: 'install',
})
}
}
Registry routing#
Since v11.23.0, the registry routing map passed to updateConfig is named
registriesByScope instead of registries. Its keys are package scopes,
such as @acme, and default for the main registry.
Returning registriesByScope replaces the routing map. Scopes omitted from
the returned map use its default registry, or the previously configured main
registry if default is omitted. The built-in @jsr route remains unless
explicitly overridden. Command-line routes take precedence. This hook changes
the fallback registry:
export const hooks = {
updateConfig (config) {
return {
...config,
registriesByScope: {
default: 'https://registry.example.com/',
},
}
},
}
To preserve existing scope routes while changing one route, spread
...config.registriesByScope into the returned map before the overrides.
Returning the old registries field no longer changes registry routing.
Update existing hooks to return registriesByScope instead.
Settings given on the command line#
Since v11.28.1, a setting given on the command line takes precedence over the
hook. The hook still receives the command-line value, but pnpm puts it back
after the hook returns, so changing it in the hook has no effect. This covers
flags such as --registry, --@acme:registry, --store-dir, and setting flags
such as --node-linker.
hooks.afterAllResolved(lockfile, context): lockfile | Promise<lockfile>#
직렬화되기 전에 lockfile 결과를 변경할 수 있습니다.
인수#
lockfile-pnpm-lock.yaml로 직렬화되는 lockfile 해석 객체입니다.context- 해당 단계의 컨텍스트 객체입니다.#log(msg)메서드를 사용하면 해당 단계에 대한 디버그 로그를 사용할 수 있습니다.
사용 예#
function afterAllResolved(lockfile, context) {
// ...
return lockfile
}
export const hooks = {
afterAllResolved
}
알려진 제약 사항#
lockfile로 할 수 있는 모든 것은 이 함수를 통해 수 있으며, lockfile의 기능을 확장할 수도 있습니다.
hooks.beforePacking(pkg): pkg | Promise<pkg>#
추가된 버전: v10.28.0
pnpm pack 또는 pnpm publish 중에 tarball로 패킹되기 전에 package.json 매니페스트를 수정할 수 있습니다. 로컬 개발용 package.json에 영향을 주지 않고 게시되는 패키지를 사용자 지정하는 데 유용합니다.
설치 중 종속성이 해석되는 방식을 수정하는 hooks.readPackage와 달리, beforePacking은 게시되는 tarball의 내용에만 영향을 줍니다.
인수#
pkg- 게시되는 tarball에 포함될 패키지 매니페스트 객체입니다.
사용 예#
function beforePacking(pkg) {
// Remove development-only fields from published package
delete pkg.devDependencies
delete pkg.scripts?.test
// Add publication metadata
pkg.publishedAt = new Date().toISOString()
// Modify package exports for production
if (pkg.name === 'my-package') {
pkg.main = './dist/index.js'
}
return pkg
}
export const hooks = {
beforePacking
}
참고
이 hook으로 수정한 내용은 tarball 내부의 package.json에만 영향을 줍니다. 로컬 package.json 파일은 변경되지 않습니다.
hooks.preResolution(options): Promise<void>#
이 훅은 프로젝트의 lockfile을 읽고 파싱한 후, 종속성을 해석하기 전에 실행됩니다. 이 훅으로 lockfile 객체를 수정할 수 있습니다.
인수#
options.existsCurrentLockfile-node_modules/.pnpm/lock.yaml에 lockfile이 있으면 true인 불리언 값입니다.options.currentLockfile-node_modules/.pnpm/lock.yaml의 lockfile 객체입니다.options.existsNonEmptyWantedLockfile-pnpm-lock.yaml에 lockfile이 있으면 true인 불리언 값입니다.options.wantedLockfile-pnpm-lock.yaml의 lockfile 객체입니다.options.lockfileDir- wanted lockfile이 있는 디렉터리입니다.options.storeDir- 스토어 디렉터리의 위치입니다.options.registries- 범위와 레지스트리 URL의 맵입니다.
hooks.importPackage(destinationDir, options): Promise<string | undefined>#
Deprecated in v11.23.0
importPackage is deprecated and will be removed in the next major version. pnpm warns when a pnpmfile defines it, and defining it opts the installation out of the parallel package importer, making installation slower. If you rely on this hook, comment on #14101.
이 hook을 사용하면 패키지가 node_modules에 기록되는 방식을 변경할 수 있습니다. 반환 값은 선택 사항이며 종속성을 가져오는 데 사용된 방식을 나타냅니다. 예: clone, hardlink.
인수#
destinationDir- 패키지가 기록될 대상 디렉터리입니다.options.disableRelinkLocalDirDepsoptions.filesMapoptions.forceoptions.resolvedFromoptions.keepModulesDir
hooks.filterLog(log): boolean#
filterLog returns false for a log entry to keep it out of pnpm's reporter
output. Use loglevel to choose the reporter's
overall verbosity.
hooks.fetchers#
버전 v11.0.0에서 제거됨
hooks.fetchers는 제거되었습니다. 대신 최상위 fetchers를 사용하세요. 새 API는 Custom Fetchers 섹션을 참조하세요.
파인더#
버전 10.16.0에서 추가됨
Finder 함수는 --find-by 플래그를 통해 pnpm list 및 pnpm why와 함께 사용됩니다.
예시:
export const finders = {
react17: (ctx) => {
return ctx.readManifest().peerDependencies?.react === "^17.0.0"
}
}
사용법:
pnpm why --find-by=react17
자세한 내용은 Finders를 참조하세요.
Custom resolvers and fetchers#
추가된 버전: v11.0.0
사용자 지정 resolver와 fetcher를 사용하면 새로운 패키지 식별자 체계(예: my-protocol:package-name)에 대한 사용자 지정 패키지 해석 및 가져오기 로직을 구현할 수 있습니다. 이들은 .pnpmfile.cjs에서 최상위 export로 등록됩니다:
module.exports = {
resolvers: [customResolver1, customResolver2],
fetchers: [customFetcher1, customFetcher2],
}
TypeScript interfaces#
interface CustomResolver {
canResolve?: (wantedDependency: WantedDependency) => boolean | Promise<boolean>
resolve?: (wantedDependency: WantedDependency, opts: ResolveOptions) => ResolveResult | Promise<ResolveResult>
shouldRefreshResolution?: (depPath: string, pkgSnapshot: PackageSnapshot) => boolean | Promise<boolean>
}
interface CustomFetcher {
canFetch?: (pkgId: string, resolution: Resolution) => boolean | Promise<boolean>
fetch?: (cafs: Cafs, resolution: Resolution, opts: FetchOptions, fetchers: Fetchers) => FetchResult | Promise<FetchResult>
}
Custom resolvers#
사용자 지정 resolver는 패키지 설명자(예: foo@^1.0.0)를 lockfile에 저장되는 해석 결과로 변환합니다.
Resolver interface#
사용자 정의 리졸버는 다음 메서드를 원하는 조합으로 구현할 수 있는 객체입니다:
canResolve(wantedDependency): boolean | Promise<boolean>#
이 리졸버가 주어진 wanted 종속성을 해석할 수 있는지 판단합니다.
인수:
wantedDependency- 다음을 포함하는 객체:alias- package.json에 표시되는 패키지 이름 또는 별칭bareSpecifier- 버전 범위, git URL, 파일 경로 또는 기타 지정자
반환값: 이 resolver가 패키지를 처리할 수 있으면 true, 그렇지 않으면 false입니다. 이는 resolve가 호출될지 여부를 결정합니다.
resolve(wantedDependency, opts): ResolveResult | Promise<ResolveResult>#
주어진 wanted 종속성을 구체적인 패키지 메타데이터와 해석 정보로 해석합니다.
인수:
wantedDependency- 원하는 종속성(canResolve와 동일)opts- 다음을 포함하는 객체:lockfileDir- lockfile이 있는 디렉터리projectDir- 프로젝트 루트 디렉터리preferredVersions- 패키지 이름과 선호 버전의 맵
반환값: 다음을 포함하는 객체:
id- 고유한 패키지 식별자(예:'custom-pkg@1.0.0')resolution- 해석 메타데이터입니다. 다음 중 하나일 수 있습니다:- 표준 해석, 예:
{ tarball: 'https://...', integrity: '...' } - 사용자 지정 해석:
{ type: 'custom:cdn', url: '...' }
- 표준 해석, 예:
사용자 정의 해석은 해당하는 사용자 정의 페처가 처리해야 합니다.
사용자 지정 해석 유형
사용자 지정 해석은 pnpm의 기본 제공 해석 유형과 구별하기 위해 type 필드에 custom: 접두사를 사용해야 합니다(예: custom:cdn, custom:artifactory).
shouldRefreshResolution(depPath, pkgSnapshot): boolean | Promise<boolean>#
true를 반환하면 "Lockfile is up to date" 최적화를 건너뛰고 모든 패키지의 전체 해석을 트리거합니다. 이는 시간 기반 캐시 무효화나 기타 사용자 정의 재해석 로직을 구현할 때 유용합니다.
인수:
depPath- 패키지 식별자 문자열(예:lodash@4.17.21)pkgSnapshot- 이 패키지의 lockfile 항목으로, 해석, 종속성 등에 직접 접근할 수 있습니다.
반환값: 재해석을 강제하려면 true, 그렇지 않으면 false입니다.
참고
frozen lockfile 설치 중에는 해석이 허용되지 않으므로 shouldRefreshResolution을 건너뜁니다.
Custom fetchers#
사용자 정의 페처는 사용자 정의 패키지 유형의 가져오기를 전적으로 처리하며, 사용자 정의 소스에서 패키지 내용을 다운로드하여 pnpm의 콘텐츠 주소 지정 파일 시스템에 저장합니다.
Fetcher interface#
사용자 정의 페처는 다음 메서드를 구현할 수 있는 객체입니다:
canFetch(pkgId, resolution): boolean | Promise<boolean>#
이 페처가 주어진 해석 정보로 패키지를 가져올 수 있는지 판단합니다.
인수:
pkgId- 해석 단계에서 얻은 고유한 패키지 식별자resolution- resolver의resolve메서드에서 반환된 해석 객체
반환값: 이 fetcher가 이 패키지를 가져올 수 있으면 true, 그렇지 않으면 false입니다.
fetch(cafs, resolution, opts, fetchers): FetchResult | CustomFetcherDelegation | Promise<FetchResult | CustomFetcherDelegation>#
패키지 파일을 가져오고 가져온 패키지에 대한 메타데이터를 반환합니다.
인수:
cafs- 파일 저장을 위한 콘텐츠 주소 지정 파일 시스템 인터페이스resolution- 해석 객체(canFetch에 전달된 것과 동일)opts- 다음을 포함한 가져오기 옵션:lockfileDir- lockfile이 있는 디렉터리filesIndexFile- 파일 인덱스 경로onStart- 가져오기가 시작될 때 호출되는 선택적 콜백onProgress- 선택적 진행 상황 콜백
fetchers- 위임을 위한 pnpm의 표준 fetcher를 포함하는 객체:remoteTarball- 원격 tarball용 fetcherlocalTarball- 로컬 tarball용 fetchergitHostedTarball- GitHub/GitLab/Bitbucket tarball용 fetcherdirectory- 로컬 디렉터리용 fetchergit- git 저장소용 fetcher
반환값: 가져오기 결과 또는 위임 envelope입니다.
가져오기 결과는 다음 속성을 가진 객체입니다:
filesIndex- 상대 파일 경로와 실제 위치의 맵입니다. 원격 패키지의 경우, 이는 pnpm의 콘텐츠 주소 지정 스토어(CAFS) 내 경로입니다. 로컬 패키지의 경우(local: true일 때) 디스크에 있는 파일의 절대 경로입니다.manifest- 선택 사항입니다. 가져온 패키지의 package.json입니다. 제공되지 않으면 pnpm이 필요할 때 디스크에서 읽습니다. 이를 제공하면 추가 파일 I/O 작업을 피할 수 있으므로, 매니페스트 데이터를 바로 사용할 수 있는 경우(예: 가져오는 중 이미 파싱한 경우) 권장됩니다.requiresBuild- 패키지에 실행해야 하는 빌드 스크립트가 있는지 여부를 나타내는 불리언 값입니다. 패키지에preinstall,install또는postinstall스크립트가 있거나binding.gyp또는.hooks/파일이 포함된 경우true로 설정하세요. 표준 페처는 매니페스트와 파일 목록을 사용하여 이를 자동으로 판단합니다.local- 선택 사항입니다. 패키지를 pnpm의 스토어로 복사하지 않고 디스크에서 직접 불러오려면true로 설정하세요.true이면filesIndex에 디스크에 있는 파일의 절대 경로가 포함되어야 하며, pnpm은 이를 복사하는 대신node_modules에 하드 링크합니다. 디렉터리 fetcher가 로컬 종속성(예:file:../my-package)을 처리하는 방식이 이와 같습니다.
내장 페처에 위임하기#
추가된 버전: v11.12.0
사용자 정의 페처는 패키지를 직접 가져오는 대신 작업을 pnpm에 다시 넘길 수 있습니다. 이를 위한 방법은 두 가지입니다.
{ delegate } envelope를 반환하세요. 가져오기 결과 대신, pnpm이 대신 가져와야 할 해석을 담은 단일 delegate 키가 있는 객체를 반환합니다. pnpm은 패키지의 해석 정보를 해당 형태로 다시 작성하고 내장 가져오기 경로를 실행합니다:
const customFetcher = {
canFetch: (pkgId, resolution) => resolution.type === 'custom:url',
fetch: (cafs, resolution) => ({
delegate: {
tarball: resolution.customUrl,
integrity: resolution.integrity,
},
}),
}
module.exports = { fetchers: [customFetcher] }
위임된 해석은 완전하고 가져올 수 있는 형태여야 합니다(예: { tarball, integrity }). 위임은 단일 단계입니다: 그 자체가 사용자 지정 유형인 delegate는 거부됩니다.
fetchers.*를 직접 호출하세요. fetchers 인수는 pnpm의 표준 fetcher를 제공하므로, 해석을 변환한 후 직접 호출할 수 있습니다.
이식성을 위해 envelope를 사용하세요
The { delegate } envelope is the only delegation form that works in every pnpm version. From pnpm 12, cafs and fetchers arrive as null, so a fetcher that should run on pnpm 11 and 12 must return the envelope rather than calling fetchers.*.
사용 예#
Basic custom resolver#
이 예시는 사용자 정의 레지스트리에서 패키지를 해석하는 사용자 정의 리졸버를 보여줍니다:
const customResolver = {
// Only handle packages with @company scope
canResolve: (wantedDependency) => {
return wantedDependency.alias.startsWith('@company/')
},
resolve: async (wantedDependency, opts) => {
// Fetch metadata from custom registry
const response = await fetch(
`https://custom-registry.company.com/${wantedDependency.alias}/${wantedDependency.bareSpecifier}`
)
const metadata = await response.json()
return {
id: `${metadata.name}@${metadata.version}`,
resolution: {
tarball: metadata.tarballUrl,
integrity: metadata.integrity
}
}
}
}
module.exports = {
resolvers: [customResolver]
}
Custom resolver and fetcher with shouldRefreshResolution#
이 예시는 사용자 정의 해석 유형과 시간 기반 캐시 무효화를 사용하여 함께 작동하는 리졸버와 페처를 보여줍니다:
const customResolver = {
canResolve: (wantedDependency) => {
return wantedDependency.alias.startsWith('company-cdn:')
},
resolve: async (wantedDependency, opts) => {
const actualName = wantedDependency.alias.replace('company-cdn:', '')
const version = await fetchVersionFromCompanyCDN(actualName, wantedDependency.bareSpecifier)
return {
id: `company-cdn:${actualName}@${version}`,
resolution: {
type: 'custom:cdn',
cdnUrl: `https://cdn.company.com/packages/${actualName}/${version}.tgz`,
cachedAt: Date.now(), // Custom metadata for shouldRefreshResolution
},
}
},
shouldRefreshResolution: (depPath, pkgSnapshot) => {
// Check custom metadata stored in the resolution
const cachedAt = pkgSnapshot.resolution?.cachedAt
if (cachedAt && Date.now() - cachedAt > 24 * 60 * 60 * 1000) {
return true // Re-resolve if cached more than 24 hours ago
}
return false
},
}
const customFetcher = {
canFetch: (pkgId, resolution) => {
return resolution.type === 'custom:cdn'
},
fetch: async (cafs, resolution, opts, fetchers) => {
// Delegate to pnpm's standard tarball fetcher
const tarballResolution = {
tarball: resolution.cdnUrl,
integrity: resolution.integrity,
}
return fetchers.remoteTarball(cafs, tarballResolution, opts)
},
}
module.exports = {
resolvers: [customResolver],
fetchers: [customFetcher],
}
Basic custom fetcher#
이 예시는 특정 패키지를 다른 소스에서 가져오는 사용자 정의 페처를 보여줍니다:
const customFetcher = {
canFetch: (pkgId, resolution) => {
return pkgId.startsWith('@company/')
},
fetch: async (cafs, resolution, opts, fetchers) => {
// Delegate to pnpm's tarball fetcher with modified URL
const tarballResolution = {
tarball: resolution.tarball.replace(
'https://registry.npmjs.org/',
'https://custom-registry.company.com/'
),
integrity: resolution.integrity
}
return fetchers.remoteTarball(cafs, tarballResolution, opts)
}
}
module.exports = {
fetchers: [customFetcher]
}
Custom resolution type with resolver and fetcher#
이 예시는 사용자 정의 해석 유형을 사용하여 함께 작동하는 사용자 정의 리졸버와 페처를 보여줍니다:
const customResolver = {
canResolve: (wantedDependency) => {
return wantedDependency.alias.startsWith('@internal/')
},
resolve: async (wantedDependency) => {
return {
id: `${wantedDependency.alias}@${wantedDependency.bareSpecifier}`,
resolution: {
type: 'custom:internal-directory',
directory: `/packages/${wantedDependency.alias}/${wantedDependency.bareSpecifier}`
}
}
}
}
const customFetcher = {
canFetch: (pkgId, resolution) => {
return resolution.type === 'custom:internal-directory'
},
fetch: async (cafs, resolution, opts, fetchers) => {
// Delegate to pnpm's directory fetcher for local packages
const directoryResolution = {
type: 'directory',
directory: resolution.directory
}
return fetchers.directory(cafs, directoryResolution, opts)
}
}
module.exports = {
resolvers: [customResolver],
fetchers: [customFetcher]
}
Priority and ordering#
여러 리졸버가 등록된 경우 순서대로 확인합니다. canResolve가 true를 반환하는 첫 번째 resolver가 해석에 사용됩니다. 동일한 규칙이 fetcher에도 적용됩니다: canFetch가 true를 반환하는 첫 번째 fetcher가 가져오기 단계에서 사용됩니다.
사용자 정의 리졸버는 pnpm의 내장 리졸버(npm, git, tarball 등)보다 먼저 시도되므로 패키지 해석을 완전히 제어할 수 있습니다.
Performance considerations#
canResolve(), canFetch(), shouldRefreshResolution()은 해석 중에 모든 종속성에 대해 호출되므로 비용이 적은 검사(이상적으로는 동기식)여야 합니다.
Related configuration#
ignorePnpmfile#
- 기본값: false
- 유형: Boolean
해당 pnpmfile은 무시됩니다. 설치 중에 어떤 스크립트도 실행되지 않도록 하고 싶을 때
--ignore-scripts와 함께 사용하면 유용합니다.
pnpmfile#
- 기본값: ['.pnpmfile.mjs']
- 유형: path[]
- 예시: ['.pnpm/.pnpmfile.mjs']
The location of the local pnpmfiles.
globalPnpmfile#
- 기본값: null
- 유형: path
- 예시: ~/.pnpm/global_pnpmfile.mjs
전역 pnpmfile의 위치입니다. 전역 pnpmfile은 설치하는 동안 모든 프로젝트에서 사용됩니다.
참고
로컬 pnpmfile를 사용하는 것이 좋습니다. pnpm을 기본 패키지 관리자로 사용하지 않는 프로젝트에서 pnpm을 사용하는 경우에만 전역 pnpmfile을 사용하십시오.