mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-23 23:29:17 -05:00
feat(patching): apply patch to all versions (#8337)
Related issue: #5686
This commit is contained in:
31
.changeset/dull-goats-fly.md
Normal file
31
.changeset/dull-goats-fly.md
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
"@pnpm/build-modules": major
|
||||
"@pnpm/core": minor
|
||||
"@pnpm/deps.graph-builder": major
|
||||
"@pnpm/headless": minor
|
||||
"@pnpm/lockfile.types": patch
|
||||
"@pnpm/patching.apply-patch": minor
|
||||
"@pnpm/patching.config": major
|
||||
"@pnpm/patching.types": major
|
||||
"@pnpm/plugin-commands-patching": minor
|
||||
"@pnpm/resolve-dependencies": major
|
||||
"@pnpm/types": major
|
||||
"pnpm": minor
|
||||
---
|
||||
|
||||
Add ability to apply patch to all versions:
|
||||
If the key of `pnpm.patchedDependencies` is a package name without a version (e.g. `pkg`), pnpm will attempt to apply the patch to all versions of
|
||||
the package, failure will be skipped.
|
||||
If it is a package name and an exact version (e.g. `pkg@x.y.z`), pnpm will attempt to apply the patch to that exact version only, failure will
|
||||
cause pnpm to fail.
|
||||
|
||||
If there's only one version of `pkg` installed, `pnpm patch pkg` and subsequent `pnpm patch-commit $edit_dir` will create an entry named `pkg` in
|
||||
`pnpm.patchedDependencies`. And pnpm will attempt to apply this patch to other versions of `pkg` in the future.
|
||||
|
||||
If there's multiple versions of `pkg` installed, `pnpm patch pkg` will ask which version to edit and whether to attempt to apply the patch to all.
|
||||
If the user chooses to apply the patch to all, `pnpm patch-commit $edit_dir` would create a `pkg` entry in `pnpm.patchedDependencies`.
|
||||
If the user chooses not to apply the patch to all, `pnpm patch-commit $edit_dir` would create a `pkg@x.y.z` entry in `pnpm.patchedDependencies` with
|
||||
`x.y.z` being the version the user chose to edit.
|
||||
|
||||
If the user runs `pnpm patch pkg@x.y.z` with `x.y.z` being the exact version of `pkg` that has been installed, `pnpm patch-commit $edit_dir` will always
|
||||
create a `pkg@x.y.z` entry in `pnpm.patchedDependencies`.
|
||||
2
deps/graph-builder/package.json
vendored
2
deps/graph-builder/package.json
vendored
@@ -46,6 +46,8 @@
|
||||
"@pnpm/lockfile.utils": "workspace:*",
|
||||
"@pnpm/modules-yaml": "workspace:*",
|
||||
"@pnpm/package-is-installable": "workspace:*",
|
||||
"@pnpm/patching.config": "workspace:*",
|
||||
"@pnpm/patching.types": "workspace:*",
|
||||
"@pnpm/store-controller-types": "workspace:*",
|
||||
"@pnpm/types": "workspace:*",
|
||||
"path-exists": "catalog:",
|
||||
|
||||
9
deps/graph-builder/src/lockfileToDepGraph.ts
vendored
9
deps/graph-builder/src/lockfileToDepGraph.ts
vendored
@@ -15,7 +15,9 @@ import {
|
||||
import { logger } from '@pnpm/logger'
|
||||
import { type IncludedDependencies } from '@pnpm/modules-yaml'
|
||||
import { packageIsInstallable } from '@pnpm/package-is-installable'
|
||||
import { type DepPath, type SupportedArchitectures, type PatchFile, type Registries, type PkgIdWithPatchHash, type ProjectId } from '@pnpm/types'
|
||||
import { getPatchInfo } from '@pnpm/patching.config'
|
||||
import { type PatchFile, type PatchInfo } from '@pnpm/patching.types'
|
||||
import { type DepPath, type SupportedArchitectures, type Registries, type PkgIdWithPatchHash, type ProjectId } from '@pnpm/types'
|
||||
import {
|
||||
type PkgRequestFetchResult,
|
||||
type FetchResponse,
|
||||
@@ -44,7 +46,7 @@ export interface DependenciesGraphNode {
|
||||
requiresBuild?: boolean
|
||||
hasBin: boolean
|
||||
filesIndexFile?: string
|
||||
patchFile?: PatchFile
|
||||
patch?: PatchInfo
|
||||
}
|
||||
|
||||
export interface DependenciesGraph {
|
||||
@@ -100,6 +102,7 @@ export async function lockfileToDepGraph (
|
||||
const directDependenciesByImporterId: DirectDependenciesByImporterId = {}
|
||||
if (lockfile.packages != null) {
|
||||
const pkgSnapshotByLocation: Record<string, PackageSnapshot> = {}
|
||||
const _getPatchInfo = getPatchInfo.bind(null, opts.patchedDependencies)
|
||||
await Promise.all(
|
||||
(Object.entries(lockfile.packages) as Array<[DepPath, PackageSnapshot]>).map(async ([depPath, pkgSnapshot]) => {
|
||||
if (opts.skipped.has(depPath)) return
|
||||
@@ -196,7 +199,7 @@ export async function lockfileToDepGraph (
|
||||
name: pkgName,
|
||||
optional: !!pkgSnapshot.optional,
|
||||
optionalDependencies: new Set(Object.keys(pkgSnapshot.optionalDependencies ?? {})),
|
||||
patchFile: opts.patchedDependencies?.[`${pkgName}@${pkgVersion}`],
|
||||
patch: _getPatchInfo(pkgName, pkgVersion),
|
||||
}
|
||||
pkgSnapshotByLocation[dir] = pkgSnapshot
|
||||
})
|
||||
|
||||
6
deps/graph-builder/tsconfig.json
vendored
6
deps/graph-builder/tsconfig.json
vendored
@@ -30,6 +30,12 @@
|
||||
{
|
||||
"path": "../../packages/types"
|
||||
},
|
||||
{
|
||||
"path": "../../patching/config"
|
||||
},
|
||||
{
|
||||
"path": "../../patching/types"
|
||||
},
|
||||
{
|
||||
"path": "../../pkg-manager/modules-yaml"
|
||||
},
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"@pnpm/lifecycle": "workspace:*",
|
||||
"@pnpm/link-bins": "workspace:*",
|
||||
"@pnpm/patching.apply-patch": "workspace:*",
|
||||
"@pnpm/patching.types": "workspace:*",
|
||||
"@pnpm/read-package-json": "workspace:*",
|
||||
"@pnpm/store-controller-types": "workspace:*",
|
||||
"@pnpm/types": "workspace:*",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { graphSequencer } from '@pnpm/deps.graph-sequencer'
|
||||
import { type PkgIdWithPatchHash, type DepPath, type PackageManifest, type PatchFile } from '@pnpm/types'
|
||||
import { type PatchInfo } from '@pnpm/patching.types'
|
||||
import { type PkgIdWithPatchHash, type DepPath, type PackageManifest } from '@pnpm/types'
|
||||
import filter from 'ramda/src/filter'
|
||||
|
||||
export interface DependenciesGraphNode<T extends string> {
|
||||
@@ -18,7 +19,7 @@ export interface DependenciesGraphNode<T extends string> {
|
||||
optionalDependencies: Set<string>
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
requiresBuild?: boolean | any // this is a dirty workaround added in https://github.com/pnpm/pnpm/pull/4898
|
||||
patchFile?: PatchFile
|
||||
patch?: PatchInfo
|
||||
}
|
||||
|
||||
export type DependenciesGraph<T extends string> = Record<T, DependenciesGraphNode<T>>
|
||||
@@ -41,7 +42,7 @@ export function buildSequence<T extends string> (
|
||||
}
|
||||
|
||||
function getSubgraphToBuild<T extends string> (
|
||||
graph: Record<string, Pick<DependenciesGraphNode<T>, 'children' | 'requiresBuild' | 'patchFile'>>,
|
||||
graph: Record<string, Pick<DependenciesGraphNode<T>, 'children' | 'requiresBuild' | 'patch'>>,
|
||||
entryNodes: T[],
|
||||
nodesToBuild: Set<T>,
|
||||
walked: Set<T>
|
||||
@@ -54,7 +55,7 @@ function getSubgraphToBuild<T extends string> (
|
||||
walked.add(depPath)
|
||||
const childShouldBeBuilt = getSubgraphToBuild(graph, Object.values(node.children), nodesToBuild, walked) ||
|
||||
node.requiresBuild ||
|
||||
node.patchFile != null
|
||||
node.patch != null
|
||||
if (childShouldBeBuilt) {
|
||||
nodesToBuild.add(depPath)
|
||||
currentShouldBeBuilt = true
|
||||
|
||||
@@ -67,7 +67,7 @@ export async function buildModules<T extends string> (
|
||||
const groups = chunks.map((chunk) => {
|
||||
chunk = chunk.filter((depPath) => {
|
||||
const node = depGraph[depPath]
|
||||
return (node.requiresBuild || node.patchFile != null) && !node.isBuilt
|
||||
return (node.requiresBuild || node.patch != null) && !node.isBuilt
|
||||
})
|
||||
if (opts.depsToBuild != null) {
|
||||
chunk = chunk.filter((depPath) => opts.depsToBuild!.has(depPath))
|
||||
@@ -127,9 +127,10 @@ async function buildDependency<T extends string> (
|
||||
}
|
||||
try {
|
||||
await linkBinsOfDependencies(depNode, depGraph, opts)
|
||||
const isPatched = depNode.patchFile?.path != null
|
||||
if (isPatched) {
|
||||
applyPatchToDir({ patchedDir: depNode.dir, patchFilePath: depNode.patchFile!.path })
|
||||
let isPatched = false
|
||||
if (depNode.patch) {
|
||||
const { file, strict } = depNode.patch
|
||||
isPatched = applyPatchToDir({ allowFailure: !strict, patchedDir: depNode.dir, patchFilePath: file.path })
|
||||
}
|
||||
const hasSideEffects = !opts.ignoreScripts && await runPostinstallHooks({
|
||||
depPath,
|
||||
@@ -148,7 +149,7 @@ async function buildDependency<T extends string> (
|
||||
if ((isPatched || hasSideEffects) && opts.sideEffectsCacheWrite) {
|
||||
try {
|
||||
const sideEffectsCacheKey = calcDepState(depGraph, opts.depsStateCache, depPath, {
|
||||
patchFileHash: depNode.patchFile?.hash,
|
||||
patchFileHash: depNode.patch?.file.hash,
|
||||
isBuilt: hasSideEffects,
|
||||
})
|
||||
await opts.storeController.upload(depNode.dir, {
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
{
|
||||
"path": "../../patching/apply-patch"
|
||||
},
|
||||
{
|
||||
"path": "../../patching/types"
|
||||
},
|
||||
{
|
||||
"path": "../../pkg-manager/link-bins"
|
||||
},
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
},
|
||||
"funding": "https://opencollective.com/pnpm",
|
||||
"dependencies": {
|
||||
"@pnpm/patching.types": "workspace:*",
|
||||
"@pnpm/types": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { type DependenciesMeta, type DepPath, type PatchFile, type ProjectId } from '@pnpm/types'
|
||||
import { type PatchFile } from '@pnpm/patching.types'
|
||||
import { type DependenciesMeta, type DepPath, type ProjectId } from '@pnpm/types'
|
||||
|
||||
export type { PatchFile, ProjectId }
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
"references": [
|
||||
{
|
||||
"path": "../../packages/types"
|
||||
},
|
||||
{
|
||||
"path": "../../patching/types"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -27,11 +27,6 @@ export interface SslConfig {
|
||||
|
||||
export type HoistedDependencies = Record<DepPath | ProjectId, Record<string, 'public' | 'private'>>
|
||||
|
||||
export interface PatchFile {
|
||||
path: string
|
||||
hash: string
|
||||
}
|
||||
|
||||
export type PkgResolutionId = string & { __brand: 'PkgResolutionId' }
|
||||
|
||||
export type PkgId = string & { __brand: 'PkgId' }
|
||||
|
||||
9
patching/apply-patch/__fixtures__/applicable.patch
Normal file
9
patching/apply-patch/__fixtures__/applicable.patch
Normal file
@@ -0,0 +1,9 @@
|
||||
diff --git a/patch-target.txt b/patch-target.txt
|
||||
index 27a8dc9..31cbe49 100644
|
||||
--- a/patch-target.txt
|
||||
+++ b/patch-target.txt
|
||||
@@ -1,3 +1,3 @@
|
||||
first line of patch-target
|
||||
second line of patch-target
|
||||
-third line of patch-target
|
||||
+final line of patch-target
|
||||
9
patching/apply-patch/__fixtures__/non-applicable.patch
Normal file
9
patching/apply-patch/__fixtures__/non-applicable.patch
Normal file
@@ -0,0 +1,9 @@
|
||||
diff --git a/patch-target.txt b/patch-target.txt
|
||||
index 27a8dc9..31cbe49 100644
|
||||
--- a/patch-target.txt
|
||||
+++ b/patch-target.txt
|
||||
@@ -1,3 +1,3 @@
|
||||
first line of patch-target
|
||||
second line of patch-target
|
||||
-FINAL line of patch-target
|
||||
+final line of patch-target
|
||||
3
patching/apply-patch/__fixtures__/patch-target.txt
Normal file
3
patching/apply-patch/__fixtures__/patch-target.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
first line of patch-target
|
||||
second line of patch-target
|
||||
third line of patch-target
|
||||
@@ -0,0 +1,3 @@
|
||||
first line of patch-target
|
||||
second line of patch-target
|
||||
final line of patch-target
|
||||
@@ -29,6 +29,9 @@
|
||||
"url": "https://github.com/pnpm/pnpm/issues"
|
||||
},
|
||||
"homepage": "https://github.com/pnpm/pnpm/blob/main/patching/apply-patch#readme",
|
||||
"peerDependencies": {
|
||||
"@pnpm/logger": "catalog:"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pnpm/error": "workspace:*",
|
||||
"@pnpm/patch-package": "catalog:"
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { applyPatch } from '@pnpm/patch-package/dist/applyPatches'
|
||||
import { globalWarn } from '@pnpm/logger'
|
||||
|
||||
export interface ApplyPatchToDirOpts {
|
||||
allowFailure?: boolean
|
||||
patchedDir: string
|
||||
patchFilePath: string
|
||||
}
|
||||
|
||||
export function applyPatchToDir (opts: ApplyPatchToDirOpts): void {
|
||||
export function applyPatchToDir (opts: ApplyPatchToDirOpts): boolean {
|
||||
// Ideally, we would just run "patch" or "git apply".
|
||||
// However, "patch" is not available on Windows and "git apply" is hard to execute on a subdirectory of an existing repository
|
||||
const cwd = process.cwd()
|
||||
@@ -25,6 +27,12 @@ export function applyPatchToDir (opts: ApplyPatchToDirOpts): void {
|
||||
process.chdir(cwd)
|
||||
}
|
||||
if (!success) {
|
||||
throw new PnpmError('PATCH_FAILED', `Could not apply patch ${opts.patchFilePath} to ${opts.patchedDir}`)
|
||||
const message = `Could not apply patch ${opts.patchFilePath} to ${opts.patchedDir}`
|
||||
if (opts.allowFailure) {
|
||||
globalWarn(message)
|
||||
} else {
|
||||
throw new PnpmError('PATCH_FAILED', message)
|
||||
}
|
||||
}
|
||||
return success
|
||||
}
|
||||
|
||||
@@ -1,25 +1,127 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { applyPatchToDir } from '@pnpm/patching.apply-patch'
|
||||
import { fixtures } from '@pnpm/test-fixtures'
|
||||
import { tempDir } from '@pnpm/prepare'
|
||||
import { globalWarn } from '@pnpm/logger'
|
||||
|
||||
const f = fixtures(__dirname)
|
||||
|
||||
describe('applyPatchToDir()', () => {
|
||||
jest.mock('@pnpm/logger', () => {
|
||||
const originalModule = jest.requireActual('@pnpm/logger')
|
||||
return {
|
||||
...originalModule,
|
||||
globalWarn: jest.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
;(globalWarn as jest.Mock).mockClear()
|
||||
})
|
||||
|
||||
function prepareDirToPatch () {
|
||||
const dir = tempDir()
|
||||
f.copy('patch-target.txt', path.join(dir, 'patch-target.txt'))
|
||||
return dir
|
||||
}
|
||||
|
||||
describe('applyPatchToDir() without allowFailure', () => {
|
||||
const allowFailure = false
|
||||
it('should succeed when patch is applicable', () => {
|
||||
const patchFilePath = f.find('applicable.patch')
|
||||
const successfullyPatched = f.find('successfully-patched.txt')
|
||||
const patchedDir = prepareDirToPatch()
|
||||
expect(
|
||||
applyPatchToDir({
|
||||
allowFailure,
|
||||
patchFilePath,
|
||||
patchedDir,
|
||||
})
|
||||
).toBe(true)
|
||||
const patchTarget = path.join(patchedDir, 'patch-target.txt')
|
||||
expect(fs.readFileSync(patchTarget, 'utf-8')).toBe(fs.readFileSync(successfullyPatched, 'utf-8'))
|
||||
})
|
||||
it('should fail when patch fails to apply', () => {
|
||||
const patchFilePath = f.find('non-applicable.patch')
|
||||
const patchedDir = prepareDirToPatch()
|
||||
expect(() => {
|
||||
applyPatchToDir({
|
||||
allowFailure,
|
||||
patchFilePath,
|
||||
patchedDir,
|
||||
})
|
||||
}).toThrow(`Could not apply patch ${patchFilePath} to ${patchedDir}`)
|
||||
expect(fs.readFileSync(path.join(patchedDir, 'patch-target.txt'), 'utf-8')).toBe(fs.readFileSync(f.find('patch-target.txt'), 'utf-8'))
|
||||
})
|
||||
it('should fail on invalid patch', () => {
|
||||
const patchFilePath = f.find('invalid.patch')
|
||||
expect(() => {
|
||||
applyPatchToDir({
|
||||
allowFailure,
|
||||
patchFilePath,
|
||||
patchedDir: tempDir(),
|
||||
})
|
||||
}).toThrowError(`Applying patch "${patchFilePath}" failed: hunk header integrity check failed`)
|
||||
}).toThrow(`Applying patch "${patchFilePath}" failed: hunk header integrity check failed`)
|
||||
})
|
||||
it('should fail if the patch file is not found', () => {
|
||||
expect(() => {
|
||||
applyPatchToDir({
|
||||
allowFailure,
|
||||
patchFilePath: 'does-not-exist.patch',
|
||||
patchedDir: tempDir(),
|
||||
})
|
||||
}).toThrowError('Patch file not found')
|
||||
}).toThrow('Patch file not found')
|
||||
})
|
||||
})
|
||||
|
||||
describe('applyPatchToDir() with allowFailure', () => {
|
||||
const allowFailure = true
|
||||
it('should succeed when patch is applicable', () => {
|
||||
const patchFilePath = f.find('applicable.patch')
|
||||
const successfullyPatched = f.find('successfully-patched.txt')
|
||||
const patchedDir = prepareDirToPatch()
|
||||
expect(
|
||||
applyPatchToDir({
|
||||
allowFailure,
|
||||
patchFilePath,
|
||||
patchedDir,
|
||||
})
|
||||
).toBe(true)
|
||||
const patchTarget = path.join(patchedDir, 'patch-target.txt')
|
||||
expect(fs.readFileSync(patchTarget, 'utf-8')).toBe(fs.readFileSync(successfullyPatched, 'utf-8'))
|
||||
})
|
||||
it('should warn when patch fails to apply', () => {
|
||||
const patchFilePath = f.find('non-applicable.patch')
|
||||
const patchedDir = prepareDirToPatch()
|
||||
expect(
|
||||
applyPatchToDir({
|
||||
allowFailure,
|
||||
patchFilePath,
|
||||
patchedDir,
|
||||
})
|
||||
).toBe(false)
|
||||
expect((globalWarn as jest.Mock).mock.calls).toStrictEqual([[
|
||||
`Could not apply patch ${patchFilePath} to ${patchedDir}`,
|
||||
]])
|
||||
expect(fs.readFileSync(path.join(patchedDir, 'patch-target.txt'), 'utf-8')).toBe(fs.readFileSync(f.find('patch-target.txt'), 'utf-8'))
|
||||
})
|
||||
it('should fail on invalid patch', () => {
|
||||
const patchFilePath = f.find('invalid.patch')
|
||||
expect(() => {
|
||||
applyPatchToDir({
|
||||
allowFailure,
|
||||
patchFilePath,
|
||||
patchedDir: tempDir(),
|
||||
})
|
||||
}).toThrow(`Applying patch "${patchFilePath}" failed: hunk header integrity check failed`)
|
||||
})
|
||||
it('should fail if the patch file is not found', () => {
|
||||
expect(() => {
|
||||
applyPatchToDir({
|
||||
allowFailure,
|
||||
patchFilePath: 'does-not-exist.patch',
|
||||
patchedDir: tempDir(),
|
||||
})
|
||||
}).toThrow('Patch file not found')
|
||||
})
|
||||
})
|
||||
|
||||
17
patching/config/README.md
Normal file
17
patching/config/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# @pnpm/patching.config
|
||||
|
||||
> Functions related to patching configurations
|
||||
|
||||
<!--@shields('npm')-->
|
||||
[](https://www.npmjs.com/package/@pnpm/patching.config)
|
||||
<!--/@-->
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
pnpm add @pnpm/patching.config
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
2
patching/config/jest.config.js
Normal file
2
patching/config/jest.config.js
Normal file
@@ -0,0 +1,2 @@
|
||||
module.exports = require('../../jest.config.js')
|
||||
|
||||
42
patching/config/package.json
Normal file
42
patching/config/package.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "@pnpm/patching.config",
|
||||
"version": "0.0.0",
|
||||
"description": "Functions related to patching configurations",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"files": [
|
||||
"lib",
|
||||
"!*.map"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18.12"
|
||||
},
|
||||
"scripts": {
|
||||
"_test": "jest",
|
||||
"test": "pnpm run compile && pnpm run _test",
|
||||
"prepublishOnly": "pnpm run compile",
|
||||
"compile": "tsc --build && pnpm run lint --fix",
|
||||
"lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\""
|
||||
},
|
||||
"repository": "https://github.com/pnpm/pnpm/blob/main/patching/config",
|
||||
"keywords": [
|
||||
"pnpm9",
|
||||
"pnpm",
|
||||
"patch"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/pnpm/pnpm/issues"
|
||||
},
|
||||
"homepage": "https://github.com/pnpm/pnpm/blob/main/patching/config#readme",
|
||||
"dependencies": {
|
||||
"@pnpm/patching.types": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@pnpm/patching.config": "workspace:*"
|
||||
},
|
||||
"funding": "https://opencollective.com/pnpm",
|
||||
"exports": {
|
||||
".": "./lib/index.js"
|
||||
}
|
||||
}
|
||||
29
patching/config/src/index.ts
Normal file
29
patching/config/src/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { type PatchFile, type PatchInfo } from '@pnpm/patching.types'
|
||||
|
||||
export interface ExtendedPatchInfo extends PatchInfo {
|
||||
key: string
|
||||
}
|
||||
|
||||
export function getPatchInfo (
|
||||
patchedDependencies: Record<string, PatchFile> | undefined,
|
||||
pkgName: string,
|
||||
pkgVersion: string
|
||||
): ExtendedPatchInfo | undefined {
|
||||
if (!patchedDependencies) return undefined
|
||||
const pkgNameAndVersion = `${pkgName}@${pkgVersion}`
|
||||
if (patchedDependencies[pkgNameAndVersion]) {
|
||||
return {
|
||||
file: patchedDependencies[pkgNameAndVersion],
|
||||
key: pkgNameAndVersion,
|
||||
strict: true,
|
||||
}
|
||||
}
|
||||
if (patchedDependencies[pkgName]) {
|
||||
return {
|
||||
file: patchedDependencies[pkgName],
|
||||
key: pkgName,
|
||||
strict: false,
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
75
patching/config/test/index.test.ts
Normal file
75
patching/config/test/index.test.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { getPatchInfo } from '../src/index'
|
||||
|
||||
test('getPatchInfo(undefined, ...) returns undefined', () => {
|
||||
expect(getPatchInfo(undefined, 'foo', '1.0.0')).toBeUndefined()
|
||||
})
|
||||
|
||||
test('getPatchInfo(_, name, version) returns strict=true if name@version exists', () => {
|
||||
expect(getPatchInfo({
|
||||
'foo@1.0.0': {
|
||||
path: 'patches/foo@1.0.0.patch',
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
}, 'foo', '1.0.0')).toStrictEqual({
|
||||
file: {
|
||||
path: 'patches/foo@1.0.0.patch',
|
||||
hash: expect.any(String),
|
||||
},
|
||||
key: 'foo@1.0.0',
|
||||
strict: true,
|
||||
})
|
||||
})
|
||||
|
||||
test('getPatchInfo(_, name, version) returns strict=false if name exists and name@version does not exist', () => {
|
||||
expect(getPatchInfo({
|
||||
foo: {
|
||||
path: 'patches/foo.patch',
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
}, 'foo', '1.0.0')).toStrictEqual({
|
||||
file: {
|
||||
path: 'patches/foo.patch',
|
||||
hash: expect.any(String),
|
||||
},
|
||||
key: 'foo',
|
||||
strict: false,
|
||||
})
|
||||
})
|
||||
|
||||
test('getPatchInfo(_, name, version) prioritizes name@version over name if both exist', () => {
|
||||
expect(getPatchInfo({
|
||||
foo: {
|
||||
path: 'patches/foo.patch',
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
'foo@1.0.0': {
|
||||
path: 'patches/foo@1.0.0.patch',
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
}, 'foo', '1.0.0')).toStrictEqual({
|
||||
file: {
|
||||
path: 'patches/foo@1.0.0.patch',
|
||||
hash: expect.any(String),
|
||||
},
|
||||
key: 'foo@1.0.0',
|
||||
strict: true,
|
||||
})
|
||||
})
|
||||
|
||||
test('getPatchInfo(_, name, version) does not access wrong name', () => {
|
||||
expect(getPatchInfo({
|
||||
'bar@1.0.0': {
|
||||
path: 'patches/bar@1.0.0.patch',
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
}, 'foo', '1.0.0')).toBeUndefined()
|
||||
})
|
||||
|
||||
test('getPatchInfo(_, name, version) does not access wrong version', () => {
|
||||
expect(getPatchInfo({
|
||||
'foo@2.0.0': {
|
||||
path: 'patches/foo@2.0.0.patch',
|
||||
hash: '00000000000000000000000000000000',
|
||||
},
|
||||
}, 'foo', '1.0.0')).toBeUndefined()
|
||||
})
|
||||
17
patching/config/test/tsconfig.json
Normal file
17
patching/config/test/tsconfig.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"composite": false,
|
||||
"noEmit": true,
|
||||
"rootDir": "."
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"../../../__typings__/**/*.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": ".."
|
||||
}
|
||||
]
|
||||
}
|
||||
16
patching/config/tsconfig.json
Normal file
16
patching/config/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "@pnpm/tsconfig",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"../../__typings__/**/*.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../types"
|
||||
}
|
||||
]
|
||||
}
|
||||
8
patching/config/tsconfig.lint.json
Normal file
8
patching/config/tsconfig.lint.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"test/**/*.ts",
|
||||
"../../__typings__/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -14,7 +14,9 @@ export type GetPatchedDependencyOptions = {
|
||||
lockfileDir: string
|
||||
} & Pick<Config, 'virtualStoreDir' | 'modulesDir'>
|
||||
|
||||
export async function getPatchedDependency (rawDependency: string, opts: GetPatchedDependencyOptions): Promise<ParseWantedDependencyResult> {
|
||||
export type GetPatchedDependencyResult = ParseWantedDependencyResult & { applyToAll: boolean }
|
||||
|
||||
export async function getPatchedDependency (rawDependency: string, opts: GetPatchedDependencyOptions): Promise<GetPatchedDependencyResult> {
|
||||
const dep = parseWantedDependency(rawDependency)
|
||||
|
||||
const { versions, preferredVersions } = await getVersionsFromLockfile(dep, opts)
|
||||
@@ -28,9 +30,10 @@ export async function getPatchedDependency (rawDependency: string, opts: GetPatc
|
||||
|
||||
dep.alias = dep.alias ?? rawDependency
|
||||
if (preferredVersions.length > 1) {
|
||||
const { version } = await prompt<{
|
||||
const { version, applyToAll } = await prompt<{
|
||||
version: string
|
||||
}>({
|
||||
applyToAll: boolean
|
||||
}>([{
|
||||
type: 'select',
|
||||
name: 'version',
|
||||
message: 'Choose which version to patch',
|
||||
@@ -44,13 +47,24 @@ export async function getPatchedDependency (rawDependency: string, opts: GetPatc
|
||||
const selectedVersion = preferredVersions.find(preferred => preferred.version === selected)!
|
||||
return selectedVersion.gitTarballUrl ?? selected
|
||||
},
|
||||
})
|
||||
dep.pref = version
|
||||
}, {
|
||||
type: 'confirm',
|
||||
name: 'applyToAll',
|
||||
message: 'Apply this patch to all versions?',
|
||||
}])
|
||||
return {
|
||||
...dep,
|
||||
applyToAll,
|
||||
pref: version,
|
||||
}
|
||||
} else {
|
||||
const preferred = preferredVersions[0]
|
||||
dep.pref = preferred.gitTarballUrl ?? preferred.version
|
||||
return {
|
||||
...dep,
|
||||
applyToAll: !dep.pref,
|
||||
pref: preferred.gitTarballUrl ?? preferred.version,
|
||||
}
|
||||
}
|
||||
return dep
|
||||
}
|
||||
|
||||
export interface LockfileVersion {
|
||||
|
||||
@@ -16,6 +16,7 @@ import { PnpmError } from '@pnpm/error'
|
||||
import { type ParseWantedDependencyResult } from '@pnpm/parse-wanted-dependency'
|
||||
import { writePackage } from './writePackage'
|
||||
import { getPatchedDependency } from './getPatchedDependency'
|
||||
import { writeEditDirState } from './stateFile'
|
||||
import { tryReadProjectManifest } from '@pnpm/read-project-manifest'
|
||||
|
||||
export function rcOptionsTypes (): Record<string, unknown> {
|
||||
@@ -86,6 +87,13 @@ export async function handler (opts: PatchCommandOptions, params: string[]): Pro
|
||||
|
||||
await writePackage(patchedDep, editDir, opts)
|
||||
|
||||
writeEditDirState({
|
||||
editDir,
|
||||
modulesDir: opts.modulesDir ?? 'node_modules',
|
||||
patchedPkg: params[0],
|
||||
applyToAll: patchedDep.applyToAll,
|
||||
})
|
||||
|
||||
if (!opts.ignoreExisting) {
|
||||
let rootProjectManifest = opts.rootProjectManifest
|
||||
if (!opts.sharedWorkspaceLockfile) {
|
||||
@@ -96,6 +104,7 @@ export async function handler (opts: PatchCommandOptions, params: string[]): Pro
|
||||
}
|
||||
if (rootProjectManifest?.pnpm?.patchedDependencies) {
|
||||
tryPatchWithExistingPatchFile({
|
||||
allowFailure: patchedDep.applyToAll,
|
||||
patchedDep,
|
||||
patchedDir: editDir,
|
||||
patchedDependencies: rootProjectManifest.pnpm.patchedDependencies,
|
||||
@@ -116,11 +125,13 @@ To commit your changes, run:
|
||||
|
||||
function tryPatchWithExistingPatchFile (
|
||||
{
|
||||
allowFailure,
|
||||
patchedDep,
|
||||
patchedDir,
|
||||
patchedDependencies,
|
||||
lockfileDir,
|
||||
}: {
|
||||
allowFailure: boolean
|
||||
patchedDep: ParseWantedDependencyResult
|
||||
patchedDir: string
|
||||
patchedDependencies: Record<string, string>
|
||||
@@ -138,5 +149,5 @@ function tryPatchWithExistingPatchFile (
|
||||
if (!fs.existsSync(existingPatchFilePath)) {
|
||||
throw new PnpmError('PATCH_FILE_NOT_FOUND', `Unable to find patch file ${existingPatchFilePath}`)
|
||||
}
|
||||
applyPatchToDir({ patchedDir, patchFilePath: existingPatchFilePath })
|
||||
applyPatchToDir({ patchedDir, patchFilePath: existingPatchFilePath, allowFailure })
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { docsUrl } from '@pnpm/cli-utils'
|
||||
import { type Config, types as allTypes } from '@pnpm/config'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { packlist } from '@pnpm/fs.packlist'
|
||||
import { install } from '@pnpm/plugin-commands-installation'
|
||||
import { readPackageJsonFromDir } from '@pnpm/read-package-json'
|
||||
@@ -18,6 +19,7 @@ import tempy from 'tempy'
|
||||
import { writePackage } from './writePackage'
|
||||
import { type ParseWantedDependencyResult, parseWantedDependency } from '@pnpm/parse-wanted-dependency'
|
||||
import { type GetPatchedDependencyOptions, getVersionsFromLockfile } from './getPatchedDependency'
|
||||
import { readEditDirState, deleteEditDirState } from './stateFile'
|
||||
|
||||
export const rcOptionsTypes = cliOptionsTypes
|
||||
|
||||
@@ -52,17 +54,35 @@ export async function handler (opts: PatchCommitCommandOptions, params: string[]
|
||||
const patchesDirName = normalizePath(path.normalize(opts.patchesDir ?? 'patches'))
|
||||
const patchesDir = path.join(lockfileDir, patchesDirName)
|
||||
const patchedPkgManifest = await readPackageJsonFromDir(userDir)
|
||||
const pkgNameAndVersion = `${patchedPkgManifest.name}@${patchedPkgManifest.version}`
|
||||
const gitTarballUrl = await getGitTarballUrlFromLockfile({
|
||||
alias: patchedPkgManifest.name,
|
||||
pref: patchedPkgManifest.version,
|
||||
}, {
|
||||
lockfileDir,
|
||||
modulesDir: opts.modulesDir,
|
||||
virtualStoreDir: opts.virtualStoreDir,
|
||||
const stateValue = readEditDirState({
|
||||
editDir: userDir,
|
||||
modulesDir: opts.modulesDir ?? 'node_modules',
|
||||
})
|
||||
if (!stateValue) {
|
||||
throw new PnpmError('INVALID_PATCH_DIR', `${userDir} is not a valid patch directory`, {
|
||||
hint: 'A valid patch directory should be created by `pnpm patch`',
|
||||
})
|
||||
}
|
||||
const { applyToAll } = stateValue
|
||||
const nameAndVersion = `${patchedPkgManifest.name}@${patchedPkgManifest.version}`
|
||||
const patchKey = applyToAll ? patchedPkgManifest.name : nameAndVersion
|
||||
let gitTarballUrl: string | undefined
|
||||
if (!applyToAll) {
|
||||
gitTarballUrl = await getGitTarballUrlFromLockfile({
|
||||
alias: patchedPkgManifest.name,
|
||||
pref: patchedPkgManifest.version,
|
||||
}, {
|
||||
lockfileDir,
|
||||
modulesDir: opts.modulesDir,
|
||||
virtualStoreDir: opts.virtualStoreDir,
|
||||
})
|
||||
}
|
||||
const srcDir = tempy.directory()
|
||||
await writePackage(parseWantedDependency(gitTarballUrl ? `${patchedPkgManifest.name}@${gitTarballUrl}` : pkgNameAndVersion), srcDir, opts)
|
||||
await writePackage(parseWantedDependency(gitTarballUrl ? `${patchedPkgManifest.name}@${gitTarballUrl}` : nameAndVersion), srcDir, opts)
|
||||
deleteEditDirState({
|
||||
editDir: userDir,
|
||||
modulesDir: opts.modulesDir ?? 'node_modules',
|
||||
})
|
||||
const patchedPkgDir = await preparePkgFilesForDiff(userDir)
|
||||
const patchContent = await diffFolders(srcDir, patchedPkgDir)
|
||||
|
||||
@@ -71,7 +91,7 @@ export async function handler (opts: PatchCommitCommandOptions, params: string[]
|
||||
}
|
||||
await fs.promises.mkdir(patchesDir, { recursive: true })
|
||||
|
||||
const patchFileName = pkgNameAndVersion.replace('/', '__')
|
||||
const patchFileName = patchKey.replace('/', '__')
|
||||
await fs.promises.writeFile(path.join(patchesDir, `${patchFileName}.patch`), patchContent, 'utf8')
|
||||
const { writeProjectManifest, manifest } = await tryReadProjectManifest(lockfileDir)
|
||||
|
||||
@@ -84,7 +104,7 @@ export async function handler (opts: PatchCommitCommandOptions, params: string[]
|
||||
} else if (!rootProjectManifest.pnpm.patchedDependencies) {
|
||||
rootProjectManifest.pnpm.patchedDependencies = {}
|
||||
}
|
||||
rootProjectManifest.pnpm.patchedDependencies![pkgNameAndVersion] = `${patchesDirName}/${patchFileName}.patch`
|
||||
rootProjectManifest.pnpm.patchedDependencies![patchKey] = `${patchesDirName}/${patchFileName}.patch`
|
||||
await writeProjectManifest(rootProjectManifest)
|
||||
|
||||
if (opts?.selectedProjectsGraph?.[lockfileDir]) {
|
||||
|
||||
78
patching/plugin-commands-patching/src/stateFile.ts
Normal file
78
patching/plugin-commands-patching/src/stateFile.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import util from 'util'
|
||||
|
||||
export type EditDir = string & { __brand: 'patch-edit-dir' }
|
||||
|
||||
export interface EditDirState {
|
||||
patchedPkg: string
|
||||
applyToAll: boolean
|
||||
}
|
||||
|
||||
export type State = Record<EditDir, EditDirState>
|
||||
|
||||
export interface EditDirKeyInput {
|
||||
editDir: string
|
||||
}
|
||||
|
||||
const createEditDirKey = (opts: EditDirKeyInput): EditDir => opts.editDir as EditDir
|
||||
|
||||
export interface ReadEditDirStateOptions extends EditDirKeyInput {
|
||||
modulesDir: string
|
||||
}
|
||||
|
||||
export function readEditDirState (opts: ReadEditDirStateOptions): EditDirState | undefined {
|
||||
const state = readStateFile(opts.modulesDir)
|
||||
if (!state) return undefined
|
||||
const key = createEditDirKey(opts)
|
||||
return state[key]
|
||||
}
|
||||
|
||||
export interface WriteEditDirStateOptions extends ReadEditDirStateOptions, EditDirState {}
|
||||
|
||||
export function writeEditDirState (opts: WriteEditDirStateOptions): void {
|
||||
modifyStateFile(opts.modulesDir, state => {
|
||||
const key = createEditDirKey(opts)
|
||||
state[key] = {
|
||||
patchedPkg: opts.patchedPkg,
|
||||
applyToAll: opts.applyToAll,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export interface DeleteEditDirStateOptions extends ReadEditDirStateOptions {}
|
||||
|
||||
export function deleteEditDirState (opts: DeleteEditDirStateOptions): void {
|
||||
modifyStateFile(opts.modulesDir, state => {
|
||||
const key = createEditDirKey(opts)
|
||||
delete state[key]
|
||||
})
|
||||
}
|
||||
|
||||
function modifyStateFile (modulesDir: string, modifyState: (state: State) => void): void {
|
||||
const filePath = getStateFilePath(modulesDir)
|
||||
let state = readStateFile(modulesDir)
|
||||
if (!state) {
|
||||
state = {}
|
||||
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
||||
}
|
||||
modifyState(state)
|
||||
fs.writeFileSync(filePath, JSON.stringify(state, undefined, 2))
|
||||
}
|
||||
|
||||
function readStateFile (modulesDir: string): State | undefined {
|
||||
let fileContent: string
|
||||
try {
|
||||
fileContent = fs.readFileSync(getStateFilePath(modulesDir), 'utf-8')
|
||||
} catch (err) {
|
||||
if (util.types.isNativeError(err) && 'code' in err && err.code === 'ENOENT') {
|
||||
return undefined
|
||||
}
|
||||
throw err
|
||||
}
|
||||
return JSON.parse(fileContent)
|
||||
}
|
||||
|
||||
function getStateFilePath (modulesDir: string): string {
|
||||
return path.join(modulesDir, '.pnpm_patches', 'state.json')
|
||||
}
|
||||
@@ -59,7 +59,7 @@ describe('patch and commit', () => {
|
||||
})
|
||||
})
|
||||
|
||||
test('patch and commit', async () => {
|
||||
test('patch and commit with exact version', async () => {
|
||||
const output = await patch.handler(defaultPatchOption, ['is-positive@1.0.0'])
|
||||
const patchDir = getPatchDirFromPatchOutput(output)
|
||||
const tempDir = os.tmpdir() // temp dir depends on the operating system (@see tempy)
|
||||
@@ -97,6 +97,44 @@ describe('patch and commit', () => {
|
||||
expect(fs.existsSync('node_modules/is-positive/license')).toBe(false)
|
||||
})
|
||||
|
||||
test('patch and commit without exact version', async () => {
|
||||
const output = await patch.handler(defaultPatchOption, ['is-positive'])
|
||||
const patchDir = getPatchDirFromPatchOutput(output)
|
||||
const tempDir = os.tmpdir() // temp dir depends on the operating system (@see tempy)
|
||||
|
||||
// store patch files in a temporary directory when not given editDir option
|
||||
expect(patchDir).toContain(tempDir)
|
||||
expect(fs.existsSync(patchDir)).toBe(true)
|
||||
|
||||
// sanity check to ensure that the license file contains the expected string
|
||||
expect(fs.readFileSync(path.join(patchDir, 'license'), 'utf8')).toContain('The MIT License (MIT)')
|
||||
|
||||
fs.appendFileSync(path.join(patchDir, 'index.js'), '// test patching', 'utf8')
|
||||
fs.unlinkSync(path.join(patchDir, 'license'))
|
||||
|
||||
await patchCommit.handler({
|
||||
...DEFAULT_OPTS,
|
||||
cacheDir,
|
||||
dir: process.cwd(),
|
||||
rootProjectManifestDir: process.cwd(),
|
||||
frozenLockfile: false,
|
||||
fixLockfile: true,
|
||||
storeDir,
|
||||
}, [patchDir])
|
||||
|
||||
const { manifest } = await readProjectManifest(process.cwd())
|
||||
expect(manifest.pnpm?.patchedDependencies).toStrictEqual({
|
||||
'is-positive': 'patches/is-positive.patch',
|
||||
})
|
||||
const patchContent = fs.readFileSync('patches/is-positive.patch', 'utf8')
|
||||
expect(patchContent).toContain('diff --git')
|
||||
expect(patchContent).toContain('// test patching')
|
||||
expect(fs.readFileSync('node_modules/is-positive/index.js', 'utf8')).toContain('// test patching')
|
||||
|
||||
expect(patchContent).not.toContain('The MIT License (MIT)')
|
||||
expect(fs.existsSync('node_modules/is-positive/license')).toBe(false)
|
||||
})
|
||||
|
||||
test('patch and commit with filtered files', async () => {
|
||||
const output = await patch.handler(defaultPatchOption, ['is-positive@1.0.0'])
|
||||
const patchDir = getPatchDirFromPatchOutput(output)
|
||||
@@ -336,6 +374,100 @@ describe('patch and commit', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('multiple versions', () => {
|
||||
let defaultPatchOption: patch.PatchCommandOptions
|
||||
let cacheDir: string
|
||||
let storeDir: string
|
||||
beforeEach(() => {
|
||||
prepare({
|
||||
dependencies: {
|
||||
'@pnpm.e2e/depends-on-console-log': '1.0.0',
|
||||
},
|
||||
})
|
||||
cacheDir = path.resolve('cache')
|
||||
storeDir = path.resolve('store')
|
||||
defaultPatchOption = {
|
||||
...basePatchOption,
|
||||
cacheDir,
|
||||
dir: process.cwd(),
|
||||
storeDir,
|
||||
}
|
||||
})
|
||||
|
||||
test('choosing apply to all should apply the patch to all applicable versions', async () => {
|
||||
await install.handler({
|
||||
...DEFAULT_OPTS,
|
||||
cacheDir,
|
||||
storeDir,
|
||||
dir: process.cwd(),
|
||||
saveLockfile: true,
|
||||
})
|
||||
prompt.mockResolvedValue({
|
||||
version: '1.0.0',
|
||||
applyToAll: true,
|
||||
})
|
||||
prompt.mockClear()
|
||||
const output = await patch.handler(defaultPatchOption, ['@pnpm.e2e/console-log'])
|
||||
|
||||
expect(prompt.mock.calls).toMatchObject([[[
|
||||
{
|
||||
type: 'select',
|
||||
name: 'version',
|
||||
choices: ['1.0.0', '2.0.0', '3.0.0'].map(x => ({ name: x, message: x, value: x })),
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'applyToAll',
|
||||
},
|
||||
]]])
|
||||
|
||||
const patchDir = getPatchDirFromPatchOutput(output)
|
||||
const fileToPatch = path.join(patchDir, 'index.js')
|
||||
const originalContent = fs.readFileSync(fileToPatch, 'utf-8')
|
||||
const patchedContent = originalContent.replace('first line', 'FIRST LINE')
|
||||
fs.writeFileSync(fileToPatch, patchedContent)
|
||||
|
||||
await patchCommit.handler({
|
||||
...DEFAULT_OPTS,
|
||||
cacheDir,
|
||||
dir: process.cwd(),
|
||||
rootProjectManifestDir: process.cwd(),
|
||||
frozenLockfile: false,
|
||||
fixLockfile: true,
|
||||
storeDir,
|
||||
}, [patchDir])
|
||||
|
||||
const { manifest } = await readProjectManifest(process.cwd())
|
||||
expect(manifest.pnpm?.patchedDependencies).toStrictEqual({
|
||||
'@pnpm.e2e/console-log': 'patches/@pnpm.e2e__console-log.patch',
|
||||
})
|
||||
|
||||
const patchFileContent = fs.readFileSync('patches/@pnpm.e2e__console-log.patch', 'utf-8')
|
||||
expect(patchFileContent).toContain('diff --git')
|
||||
expect(patchFileContent).toContain("\n+console.log('FIRST LINE')")
|
||||
expect(patchFileContent).toContain("\n-console.log('first line')")
|
||||
|
||||
expect(
|
||||
fs.readFileSync(
|
||||
'node_modules/.pnpm/@pnpm.e2e+depends-on-console-log@1.0.0/node_modules/console-log-1/index.js',
|
||||
'utf-8'
|
||||
)
|
||||
).toContain('FIRST LINE')
|
||||
expect(
|
||||
fs.readFileSync(
|
||||
'node_modules/.pnpm/@pnpm.e2e+depends-on-console-log@1.0.0/node_modules/console-log-2/index.js',
|
||||
'utf-8'
|
||||
)
|
||||
).toContain('FIRST LINE')
|
||||
expect(
|
||||
fs.readFileSync(
|
||||
'node_modules/.pnpm/@pnpm.e2e+depends-on-console-log@1.0.0/node_modules/console-log-3/index.js',
|
||||
'utf-8'
|
||||
)
|
||||
).toContain('FIRST LINE')
|
||||
})
|
||||
})
|
||||
|
||||
describe('prompt to choose version', () => {
|
||||
let defaultPatchOption: patch.PatchCommandOptions
|
||||
let cacheDir: string
|
||||
@@ -357,7 +489,7 @@ describe('prompt to choose version', () => {
|
||||
}
|
||||
})
|
||||
|
||||
test('prompt to choose version if multiple version founded for patched package', async () => {
|
||||
test('prompt to choose version if multiple versions found for patched package, no apply to all', async () => {
|
||||
await install.handler({
|
||||
...DEFAULT_OPTS,
|
||||
cacheDir,
|
||||
@@ -367,22 +499,33 @@ describe('prompt to choose version', () => {
|
||||
})
|
||||
prompt.mockResolvedValue({
|
||||
version: '5.3.0',
|
||||
applyToAll: false,
|
||||
})
|
||||
prompt.mockClear()
|
||||
const output = await patch.handler(defaultPatchOption, ['chalk'])
|
||||
|
||||
expect(prompt.mock.calls[0][0].choices).toEqual(expect.arrayContaining([
|
||||
expect(prompt.mock.calls).toMatchObject([[[
|
||||
{
|
||||
name: '4.1.2',
|
||||
message: '4.1.2',
|
||||
value: '4.1.2',
|
||||
type: 'select',
|
||||
name: 'version',
|
||||
choices: [
|
||||
{
|
||||
name: '4.1.2',
|
||||
message: '4.1.2',
|
||||
value: '4.1.2',
|
||||
},
|
||||
{
|
||||
name: '5.3.0',
|
||||
message: '5.3.0',
|
||||
value: '5.3.0',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: '5.3.0',
|
||||
message: '5.3.0',
|
||||
value: '5.3.0',
|
||||
type: 'confirm',
|
||||
name: 'applyToAll',
|
||||
},
|
||||
]))
|
||||
]]])
|
||||
|
||||
const patchDir = getPatchDirFromPatchOutput(output)
|
||||
const tempDir = os.tmpdir()
|
||||
@@ -412,6 +555,73 @@ describe('prompt to choose version', () => {
|
||||
expect(patchContent).toContain('// test patching')
|
||||
expect(fs.readFileSync('node_modules/.pnpm/ava@5.2.0/node_modules/chalk/source/index.js', 'utf8')).toContain('// test patching')
|
||||
})
|
||||
|
||||
test('prompt to choose version if multiple versions found for patched package, apply to all', async () => {
|
||||
await install.handler({
|
||||
...DEFAULT_OPTS,
|
||||
cacheDir,
|
||||
storeDir,
|
||||
dir: process.cwd(),
|
||||
saveLockfile: true,
|
||||
})
|
||||
prompt.mockResolvedValue({
|
||||
version: '5.3.0',
|
||||
applyToAll: true,
|
||||
})
|
||||
prompt.mockClear()
|
||||
const output = await patch.handler(defaultPatchOption, ['chalk'])
|
||||
|
||||
expect(prompt.mock.calls).toMatchObject([[[
|
||||
{
|
||||
type: 'select',
|
||||
name: 'version',
|
||||
choices: [
|
||||
{
|
||||
name: '4.1.2',
|
||||
message: '4.1.2',
|
||||
value: '4.1.2',
|
||||
},
|
||||
{
|
||||
name: '5.3.0',
|
||||
message: '5.3.0',
|
||||
value: '5.3.0',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'applyToAll',
|
||||
},
|
||||
]]])
|
||||
|
||||
const patchDir = getPatchDirFromPatchOutput(output)
|
||||
const tempDir = os.tmpdir()
|
||||
|
||||
expect(patchDir).toContain(tempDir)
|
||||
expect(fs.existsSync(patchDir)).toBe(true)
|
||||
expect(JSON.parse(fs.readFileSync(path.join(patchDir, 'package.json'), 'utf8')).version).toBe('5.3.0')
|
||||
expect(fs.existsSync(path.join(patchDir, 'source/index.js'))).toBe(true)
|
||||
|
||||
fs.appendFileSync(path.join(patchDir, 'source/index.js'), '// test patching', 'utf8')
|
||||
await patchCommit.handler({
|
||||
...DEFAULT_OPTS,
|
||||
cacheDir,
|
||||
dir: process.cwd(),
|
||||
rootProjectManifestDir: process.cwd(),
|
||||
frozenLockfile: false,
|
||||
fixLockfile: true,
|
||||
storeDir,
|
||||
}, [patchDir])
|
||||
|
||||
const { manifest } = await readProjectManifest(process.cwd())
|
||||
expect(manifest.pnpm?.patchedDependencies).toStrictEqual({
|
||||
chalk: 'patches/chalk.patch',
|
||||
})
|
||||
const patchContent = fs.readFileSync('patches/chalk.patch', 'utf8')
|
||||
expect(patchContent).toContain('diff --git')
|
||||
expect(patchContent).toContain('// test patching')
|
||||
expect(fs.readFileSync('node_modules/.pnpm/ava@5.2.0/node_modules/chalk/source/index.js', 'utf8')).toContain('// test patching')
|
||||
})
|
||||
})
|
||||
|
||||
describe('patching should work when there is a no EOL in the patched file', () => {
|
||||
@@ -455,7 +665,7 @@ describe('patching should work when there is a no EOL in the patched file', () =
|
||||
|
||||
await patchCommit.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
...defaultPatchOption,
|
||||
rootProjectManifestDir: process.cwd(),
|
||||
frozenLockfile: false,
|
||||
fixLockfile: true,
|
||||
@@ -484,7 +694,7 @@ describe('patching should work when there is a no EOL in the patched file', () =
|
||||
|
||||
await patchCommit.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
...defaultPatchOption,
|
||||
rootProjectManifestDir: process.cwd(),
|
||||
frozenLockfile: false,
|
||||
fixLockfile: true,
|
||||
@@ -740,22 +950,33 @@ describe('patch and commit in workspaces', () => {
|
||||
|
||||
prompt.mockResolvedValue({
|
||||
version: 'https://codeload.github.com/zkochan/hi/tar.gz/4cdebec76b7b9d1f6e219e06c42d92a6b8ea60cd',
|
||||
applyToAll: false,
|
||||
})
|
||||
prompt.mockClear()
|
||||
const output = await patch.handler(defaultPatchOption, ['hi'])
|
||||
expect(prompt.mock.calls[0][0].choices).toEqual(expect.arrayContaining([
|
||||
expect(prompt.mock.calls).toMatchObject([[[
|
||||
{
|
||||
name: '0.0.0',
|
||||
message: '0.0.0',
|
||||
value: '0.0.0',
|
||||
type: 'select',
|
||||
name: 'version',
|
||||
choices: [
|
||||
{
|
||||
name: '0.0.0',
|
||||
message: '0.0.0',
|
||||
value: '0.0.0',
|
||||
},
|
||||
{
|
||||
name: '1.0.0',
|
||||
message: '1.0.0',
|
||||
value: 'https://codeload.github.com/zkochan/hi/tar.gz/4cdebec76b7b9d1f6e219e06c42d92a6b8ea60cd',
|
||||
hint: 'Git Hosted',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: '1.0.0',
|
||||
message: '1.0.0',
|
||||
value: 'https://codeload.github.com/zkochan/hi/tar.gz/4cdebec76b7b9d1f6e219e06c42d92a6b8ea60cd',
|
||||
hint: 'Git Hosted',
|
||||
type: 'confirm',
|
||||
name: 'applyToAll',
|
||||
},
|
||||
]))
|
||||
]]])
|
||||
const patchDir = getPatchDirFromPatchOutput(output)
|
||||
expect(fs.existsSync(patchDir)).toBe(true)
|
||||
expect(fs.readFileSync(path.join(patchDir, 'index.js'), 'utf8')).toContain('module.exports = \'Hi\'')
|
||||
@@ -836,7 +1057,7 @@ describe('patch with custom modules-dir and virtual-store-dir', () => {
|
||||
|
||||
await patchCommit.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: customModulesDirFixture,
|
||||
...defaultPatchOption,
|
||||
rootProjectManifestDir: customModulesDirFixture,
|
||||
saveLockfile: true,
|
||||
frozenLockfile: false,
|
||||
|
||||
17
patching/types/README.md
Normal file
17
patching/types/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# @pnpm/patching.types
|
||||
|
||||
> Types related to patching
|
||||
|
||||
<!--@shields('npm')-->
|
||||
[](https://www.npmjs.com/package/@pnpm/patching.types)
|
||||
<!--/@-->
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
pnpm add @pnpm/patching.types
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
38
patching/types/package.json
Normal file
38
patching/types/package.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@pnpm/patching.types",
|
||||
"version": "0.0.0",
|
||||
"description": "Types related to patching",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"files": [
|
||||
"lib",
|
||||
"!*.map"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18.12"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "pnpm run compile",
|
||||
"prepublishOnly": "pnpm run compile",
|
||||
"compile": "tsc --build && pnpm run lint --fix",
|
||||
"lint": "eslint \"src/**/*.ts\""
|
||||
},
|
||||
"repository": "https://github.com/pnpm/pnpm/blob/main/patching/types",
|
||||
"keywords": [
|
||||
"pnpm9",
|
||||
"pnpm",
|
||||
"patch"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/pnpm/pnpm/issues"
|
||||
},
|
||||
"homepage": "https://github.com/pnpm/pnpm/blob/main/patching/types#readme",
|
||||
"devDependencies": {
|
||||
"@pnpm/patching.types": "workspace:*"
|
||||
},
|
||||
"funding": "https://opencollective.com/pnpm",
|
||||
"exports": {
|
||||
".": "./lib/index.js"
|
||||
}
|
||||
}
|
||||
9
patching/types/src/index.ts
Normal file
9
patching/types/src/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export interface PatchFile {
|
||||
path: string
|
||||
hash: string
|
||||
}
|
||||
|
||||
export interface PatchInfo {
|
||||
strict: boolean
|
||||
file: PatchFile
|
||||
}
|
||||
12
patching/types/tsconfig.json
Normal file
12
patching/types/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "@pnpm/tsconfig",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"../../__typings__/**/*.d.ts"
|
||||
],
|
||||
"references": []
|
||||
}
|
||||
8
patching/types/tsconfig.lint.json
Normal file
8
patching/types/tsconfig.lint.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"test/**/*.ts",
|
||||
"../../__typings__/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -463,7 +463,7 @@ async function linkAllPkgs (
|
||||
if (opts.sideEffectsCacheRead && files.sideEffects && !isEmpty(files.sideEffects)) {
|
||||
sideEffectsCacheKey = calcDepState(opts.depGraph, opts.depsStateCache, depNode.depPath, {
|
||||
isBuilt: !opts.ignoreScripts && depNode.requiresBuild,
|
||||
patchFileHash: depNode.patchFile?.hash,
|
||||
patchFileHash: depNode.patch?.file.hash,
|
||||
})
|
||||
}
|
||||
const { importMethod, isBuilt } = await storeController.importPackage(depNode.dir, {
|
||||
@@ -471,7 +471,7 @@ async function linkAllPkgs (
|
||||
filesResponse: files,
|
||||
force: opts.force,
|
||||
sideEffectsCacheKey,
|
||||
requiresBuild: depNode.patchFile != null || depNode.requiresBuild,
|
||||
requiresBuild: depNode.patch != null || depNode.requiresBuild,
|
||||
})
|
||||
if (importMethod) {
|
||||
progressLogger.debug({
|
||||
|
||||
@@ -84,6 +84,7 @@
|
||||
"@pnpm/modules-yaml": "workspace:*",
|
||||
"@pnpm/package-is-installable": "workspace:*",
|
||||
"@pnpm/package-requester": "workspace:*",
|
||||
"@pnpm/patching.config": "workspace:*",
|
||||
"@pnpm/pkg-manager.direct-dep-linker": "workspace:*",
|
||||
"@pnpm/read-package-json": "workspace:*",
|
||||
"@pnpm/read-project-manifest": "workspace:*",
|
||||
|
||||
@@ -27,11 +27,11 @@ import { linkBins, linkBinsOfPackages } from '@pnpm/link-bins'
|
||||
import {
|
||||
getLockfileImporterId,
|
||||
type Lockfile,
|
||||
type PatchFile,
|
||||
readCurrentLockfile,
|
||||
readWantedLockfile,
|
||||
writeLockfiles,
|
||||
writeCurrentLockfile,
|
||||
type PatchFile,
|
||||
} from '@pnpm/lockfile.fs'
|
||||
import { writePnpFile } from '@pnpm/lockfile-to-pnp'
|
||||
import {
|
||||
@@ -849,14 +849,14 @@ async function linkAllPkgs (
|
||||
if (opts.sideEffectsCacheRead && filesResponse.sideEffects && !isEmpty(filesResponse.sideEffects)) {
|
||||
sideEffectsCacheKey = calcDepState(opts.depGraph, opts.depsStateCache, depNode.dir, {
|
||||
isBuilt: !opts.ignoreScripts && depNode.requiresBuild,
|
||||
patchFileHash: depNode.patchFile?.hash,
|
||||
patchFileHash: depNode.patch?.file.hash,
|
||||
})
|
||||
}
|
||||
const { importMethod, isBuilt } = await storeController.importPackage(depNode.dir, {
|
||||
filesResponse,
|
||||
force: opts.force,
|
||||
disableRelinkLocalDirDeps: opts.disableRelinkLocalDirDeps,
|
||||
requiresBuild: depNode.patchFile != null || depNode.requiresBuild,
|
||||
requiresBuild: depNode.patch != null || depNode.requiresBuild,
|
||||
sideEffectsCacheKey,
|
||||
})
|
||||
if (importMethod) {
|
||||
|
||||
@@ -116,7 +116,7 @@ async function linkAllPkgsInOrder (
|
||||
if (opts.sideEffectsCacheRead && filesResponse.sideEffects && !isEmpty(filesResponse.sideEffects)) {
|
||||
sideEffectsCacheKey = _calcDepState(dir, {
|
||||
isBuilt: !opts.ignoreScripts && depNode.requiresBuild,
|
||||
patchFileHash: depNode.patchFile?.hash,
|
||||
patchFileHash: depNode.patch?.file.hash,
|
||||
})
|
||||
}
|
||||
// Limiting the concurrency here fixes an out of memory error.
|
||||
@@ -128,7 +128,7 @@ async function linkAllPkgsInOrder (
|
||||
force: true,
|
||||
disableRelinkLocalDirDeps: opts.disableRelinkLocalDirDeps,
|
||||
keepModulesDir: true,
|
||||
requiresBuild: depNode.patchFile != null || depNode.requiresBuild,
|
||||
requiresBuild: depNode.patch != null || depNode.requiresBuild,
|
||||
sideEffectsCacheKey,
|
||||
})
|
||||
if (importMethod) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import path from 'path'
|
||||
import {
|
||||
type Lockfile,
|
||||
type PackageSnapshot,
|
||||
type PatchFile,
|
||||
type ProjectSnapshot,
|
||||
} from '@pnpm/lockfile.fs'
|
||||
import {
|
||||
@@ -12,8 +13,9 @@ import {
|
||||
} from '@pnpm/lockfile.utils'
|
||||
import { type IncludedDependencies } from '@pnpm/modules-yaml'
|
||||
import { packageIsInstallable } from '@pnpm/package-is-installable'
|
||||
import { getPatchInfo } from '@pnpm/patching.config'
|
||||
import { safeReadPackageJsonFromDir } from '@pnpm/read-package-json'
|
||||
import { type DepPath, type SupportedArchitectures, type PatchFile, type ProjectId, type Registries } from '@pnpm/types'
|
||||
import { type DepPath, type SupportedArchitectures, type ProjectId, type Registries } from '@pnpm/types'
|
||||
import {
|
||||
type FetchPackageToStoreFunction,
|
||||
type StoreController,
|
||||
@@ -248,7 +250,7 @@ async function fetchDeps (
|
||||
name: pkgName,
|
||||
optional: !!pkgSnapshot.optional,
|
||||
optionalDependencies: new Set(Object.keys(pkgSnapshot.optionalDependencies ?? {})),
|
||||
patchFile: opts.patchedDependencies?.[`${pkgName}@${pkgVersion}`],
|
||||
patch: getPatchInfo(opts.patchedDependencies, pkgName, pkgVersion),
|
||||
}
|
||||
if (!opts.pkgLocationsByDepPath[depPath]) {
|
||||
opts.pkgLocationsByDepPath[depPath] = []
|
||||
|
||||
@@ -69,6 +69,9 @@
|
||||
{
|
||||
"path": "../../packages/types"
|
||||
},
|
||||
{
|
||||
"path": "../../patching/config"
|
||||
},
|
||||
{
|
||||
"path": "../../pkg-manifest/read-package-json"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
diff --git a/index.js b/index.js
|
||||
index 54b6705..6c0033d 100644
|
||||
--- a/index.js
|
||||
+++ b/index.js
|
||||
@@ -1,1 +1,1 @@
|
||||
-console.log('first line')
|
||||
+console.log('FIRST LINE')
|
||||
@@ -0,0 +1,7 @@
|
||||
diff --git a/index.js b/index.js
|
||||
index 54b6705..e6c6858 100644
|
||||
--- a/index.js
|
||||
+++ b/index.js
|
||||
@@ -1,1 +1,1 @@
|
||||
-console.log('second line')
|
||||
+console.log('SECOND LINE')
|
||||
@@ -0,0 +1,7 @@
|
||||
diff --git a/index.js b/index.js
|
||||
index 54b6705..ab68516 100644
|
||||
--- a/index.js
|
||||
+++ b/index.js
|
||||
@@ -1,1 +1,1 @@
|
||||
-console.log('third line')
|
||||
+console.log('THIRD LINE')
|
||||
@@ -0,0 +1,7 @@
|
||||
diff --git a/index.js b/index.js
|
||||
index 54b6705..1c7ffb7 100644
|
||||
--- a/index.js
|
||||
+++ b/index.js
|
||||
@@ -1,1 +1,1 @@
|
||||
-console.log('fourth line')
|
||||
+console.log('FOURTH LINE')
|
||||
@@ -0,0 +1,268 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { type ProjectManifest } from '@pnpm/types'
|
||||
import { globalWarn } from '@pnpm/logger'
|
||||
import { add, install } from '@pnpm/plugin-commands-installation'
|
||||
import { prepareEmpty } from '@pnpm/prepare'
|
||||
import { fixtures } from '@pnpm/test-fixtures'
|
||||
import { DEFAULT_OPTS } from './utils'
|
||||
|
||||
jest.mock('@pnpm/logger', () => {
|
||||
const originalModule = jest.requireActual('@pnpm/logger')
|
||||
return {
|
||||
...originalModule,
|
||||
globalWarn: jest.fn(),
|
||||
}
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
;(globalWarn as jest.Mock).mockClear()
|
||||
})
|
||||
|
||||
const f = fixtures(__dirname)
|
||||
|
||||
function addPatch (key: string, patchFixture: string, patchDest: string): void {
|
||||
fs.mkdirSync(path.dirname(patchDest), { recursive: true })
|
||||
fs.copyFileSync(patchFixture, patchDest)
|
||||
let manifestText = fs.readFileSync('package.json', 'utf-8')
|
||||
const manifest: ProjectManifest = JSON.parse(manifestText)
|
||||
manifest.pnpm = {
|
||||
...manifest.pnpm,
|
||||
patchedDependencies: {
|
||||
...manifest.pnpm?.patchedDependencies,
|
||||
[key]: patchDest,
|
||||
},
|
||||
}
|
||||
manifestText = JSON.stringify(manifest, undefined, 2) + '\n'
|
||||
fs.writeFileSync('package.json', manifestText)
|
||||
}
|
||||
|
||||
const unpatchedModulesDir = (v: 1 | 2 | 3) => `node_modules/.pnpm/@pnpm.e2e+console-log@${v}.0.0/node_modules`
|
||||
const unpatchedFilePath = (v: 1 | 2 | 3) => `${unpatchedModulesDir(v)}/@pnpm.e2e/console-log/index.js`
|
||||
const unpatchedFileContent = (v: 1 | 2 | 3) => fs.readFileSync(unpatchedFilePath(v), 'utf-8')
|
||||
const patchedModulesDir = 'node_modules/.pnpm/@pnpm.e2e+depends-on-console-log@1.0.0/node_modules'
|
||||
const patchedFilePath = (v: 1 | 2 | 3) => `${patchedModulesDir}/console-log-${v}/index.js`
|
||||
const patchedFileContent = (v: 1 | 2 | 3) => fs.readFileSync(patchedFilePath(v), 'utf-8')
|
||||
|
||||
test('bare package name as a patchedDependencies key should apply to all versions if all are applicable', async () => {
|
||||
const patchFixture = f.find('patchedDependencies/console-log-replace-1st-line.patch')
|
||||
prepareEmpty()
|
||||
|
||||
await add.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
}, ['@pnpm.e2e/depends-on-console-log@1.0.0'])
|
||||
fs.rmSync('pnpm-lock.yaml')
|
||||
|
||||
addPatch('@pnpm.e2e/console-log', patchFixture, 'patches/console-log.patch')
|
||||
|
||||
await install.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
frozenLockfile: false,
|
||||
})
|
||||
|
||||
{
|
||||
const text = patchedFileContent(1)
|
||||
expect(text).not.toBe(unpatchedFileContent(1))
|
||||
expect(text).toContain('FIRST LINE')
|
||||
expect(text).not.toContain('first line')
|
||||
expect(text).toContain('second line')
|
||||
expect(text.trim().split('\n').length).toBe(2)
|
||||
}
|
||||
|
||||
{
|
||||
const text = patchedFileContent(2)
|
||||
expect(text).not.toBe(unpatchedFileContent(2))
|
||||
expect(text).toContain('FIRST LINE')
|
||||
expect(text).not.toContain('first line')
|
||||
expect(text).toContain('second line')
|
||||
expect(text).toContain('third line')
|
||||
expect(text.trim().split('\n').length).toBe(3)
|
||||
}
|
||||
|
||||
{
|
||||
const text = patchedFileContent(3)
|
||||
expect(text).not.toBe(unpatchedFileContent(3))
|
||||
expect(text).toContain('FIRST LINE')
|
||||
expect(text).not.toContain('first line')
|
||||
expect(text).toContain('second line')
|
||||
expect(text).toContain('third line')
|
||||
expect(text).toContain('fourth line')
|
||||
expect(text.trim().split('\n').length).toBe(4)
|
||||
}
|
||||
|
||||
expect(globalWarn).not.toHaveBeenCalledWith(expect.stringContaining('Could not apply patch'))
|
||||
})
|
||||
|
||||
test('bare package name as a patchedDependencies key should apply to all possible versions and skip non-applicable versions', async () => {
|
||||
const patchFixture = f.find('patchedDependencies/console-log-replace-3rd-line.patch')
|
||||
prepareEmpty()
|
||||
|
||||
await add.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
}, ['@pnpm.e2e/depends-on-console-log@1.0.0'])
|
||||
fs.rmSync('pnpm-lock.yaml')
|
||||
|
||||
addPatch('@pnpm.e2e/console-log', patchFixture, 'patches/console-log.patch')
|
||||
|
||||
await install.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
frozenLockfile: false,
|
||||
})
|
||||
|
||||
// the common patch does not apply to v1
|
||||
expect(patchedFileContent(1)).toBe(unpatchedFileContent(1))
|
||||
expect(globalWarn).toHaveBeenCalledWith(expect.stringContaining(`Could not apply patch ${path.resolve('patches/console-log.patch')}`))
|
||||
|
||||
// the common patch applies to v2
|
||||
{
|
||||
const text = patchedFileContent(2)
|
||||
expect(text).not.toBe(unpatchedFileContent(2))
|
||||
expect(text).toContain('first line')
|
||||
expect(text).toContain('second line')
|
||||
expect(text).toContain('THIRD LINE')
|
||||
expect(text).not.toContain('third line')
|
||||
expect(text.trim().split('\n').length).toBe(3)
|
||||
}
|
||||
|
||||
// the common patch applies to v3
|
||||
{
|
||||
const text = patchedFileContent(3)
|
||||
expect(text).not.toBe(unpatchedFileContent(3))
|
||||
expect(text).toContain('first line')
|
||||
expect(text).toContain('second line')
|
||||
expect(text).toContain('THIRD LINE')
|
||||
expect(text).not.toContain('third line')
|
||||
expect(text).toContain('fourth line')
|
||||
expect(text.trim().split('\n').length).toBe(4)
|
||||
}
|
||||
})
|
||||
|
||||
test('package name with version is prioritized over bare package name as keys of patchedDependencies', async () => {
|
||||
const commonPatchFixture = f.find('patchedDependencies/console-log-replace-1st-line.patch')
|
||||
const specializedPatchFixture = f.find('patchedDependencies/console-log-replace-2nd-line.patch')
|
||||
prepareEmpty()
|
||||
|
||||
await add.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
}, ['@pnpm.e2e/depends-on-console-log@1.0.0'])
|
||||
fs.rmSync('pnpm-lock.yaml')
|
||||
|
||||
addPatch('@pnpm.e2e/console-log', commonPatchFixture, 'patches/console-log.patch')
|
||||
addPatch('@pnpm.e2e/console-log@2.0.0', specializedPatchFixture, 'patches/console-log@2.0.0.patch')
|
||||
|
||||
await install.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
frozenLockfile: false,
|
||||
})
|
||||
|
||||
// the common patch applies to v1
|
||||
{
|
||||
const text = patchedFileContent(1)
|
||||
expect(text).not.toBe(unpatchedFileContent(1))
|
||||
expect(text).toContain('FIRST LINE')
|
||||
expect(text).not.toContain('first line')
|
||||
expect(text).toContain('second line')
|
||||
expect(text.trim().split('\n').length).toBe(2)
|
||||
}
|
||||
|
||||
// the specialized patch applies to v2
|
||||
{
|
||||
const text = patchedFileContent(2)
|
||||
expect(text).not.toBe(unpatchedFileContent(2))
|
||||
expect(text).toContain('first line')
|
||||
expect(text).toContain('SECOND LINE')
|
||||
expect(text).not.toContain('second line')
|
||||
expect(text).toContain('third line')
|
||||
expect(text.trim().split('\n').length).toBe(3)
|
||||
}
|
||||
|
||||
// the common patch applies to v3
|
||||
{
|
||||
const text = patchedFileContent(3)
|
||||
expect(text).not.toBe(unpatchedFileContent(3))
|
||||
expect(text).toContain('FIRST LINE')
|
||||
expect(text).not.toContain('first line')
|
||||
expect(text).toContain('second line')
|
||||
expect(text).toContain('third line')
|
||||
expect(text).toContain('fourth line')
|
||||
expect(text.trim().split('\n').length).toBe(4)
|
||||
}
|
||||
|
||||
expect(globalWarn).not.toHaveBeenCalledWith(expect.stringContaining('Could not apply patch'))
|
||||
})
|
||||
|
||||
test('package name with version as a patchedDependencies key does not affect other versions', async () => {
|
||||
const patchFixture2 = f.find('patchedDependencies/console-log-replace-2nd-line.patch')
|
||||
const patchFixture3 = f.find('patchedDependencies/console-log-replace-4th-line.patch')
|
||||
prepareEmpty()
|
||||
|
||||
await add.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
}, ['@pnpm.e2e/depends-on-console-log@1.0.0'])
|
||||
fs.rmSync('pnpm-lock.yaml')
|
||||
|
||||
addPatch('@pnpm.e2e/console-log@2.0.0', patchFixture2, 'patches/console-log@2.0.0.patch')
|
||||
addPatch('@pnpm.e2e/console-log@3.0.0', patchFixture3, 'patches/console-log@3.0.0.patch')
|
||||
|
||||
await install.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
frozenLockfile: false,
|
||||
})
|
||||
|
||||
// v1 remains unpatched
|
||||
expect(patchedFileContent(1)).toBe(unpatchedFileContent(1))
|
||||
|
||||
// patch 2 applies to v2
|
||||
{
|
||||
const text = patchedFileContent(2)
|
||||
expect(text).not.toBe(unpatchedFileContent(2))
|
||||
expect(text).toContain('first line')
|
||||
expect(text).toContain('SECOND LINE')
|
||||
expect(text).not.toContain('second line')
|
||||
expect(text).toContain('third line')
|
||||
expect(text.trim().split('\n').length).toBe(3)
|
||||
}
|
||||
|
||||
// patch 3 applies to v3
|
||||
{
|
||||
const text = patchedFileContent(3)
|
||||
expect(text).not.toBe(unpatchedFileContent(3))
|
||||
expect(text).toContain('first line')
|
||||
expect(text).toContain('second line')
|
||||
expect(text).toContain('third line')
|
||||
expect(text).toContain('FOURTH LINE')
|
||||
expect(text).not.toContain('fourth line')
|
||||
expect(text.trim().split('\n').length).toBe(4)
|
||||
}
|
||||
})
|
||||
|
||||
test('failure to apply patch with package name and version would cause throw an error', async () => {
|
||||
const patchFixture = f.find('patchedDependencies/console-log-replace-4th-line.patch')
|
||||
prepareEmpty()
|
||||
|
||||
await add.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
}, ['@pnpm.e2e/depends-on-console-log@1.0.0'])
|
||||
fs.rmSync('pnpm-lock.yaml')
|
||||
|
||||
addPatch('@pnpm.e2e/console-log@1.0.0', patchFixture, 'patches/console-log@1.0.0.patch')
|
||||
|
||||
const promise = install.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
frozenLockfile: false,
|
||||
})
|
||||
await expect(promise).rejects.toHaveProperty(['message'], expect.stringContaining('Could not apply patch'))
|
||||
await expect(promise).rejects.toHaveProperty(['message'], expect.stringContaining(path.resolve('patches/console-log@1.0.0.patch')))
|
||||
|
||||
expect(patchedFileContent(1)).toBe(unpatchedFileContent(1))
|
||||
})
|
||||
@@ -40,6 +40,8 @@
|
||||
"@pnpm/lockfile.preferred-versions": "workspace:*",
|
||||
"@pnpm/manifest-utils": "workspace:*",
|
||||
"@pnpm/npm-resolver": "workspace:*",
|
||||
"@pnpm/patching.config": "workspace:*",
|
||||
"@pnpm/patching.types": "workspace:*",
|
||||
"@pnpm/pick-fetcher": "workspace:*",
|
||||
"@pnpm/pick-registry-for-package": "workspace:*",
|
||||
"@pnpm/lockfile.pruner": "workspace:*",
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
pkgSnapshotToResolution,
|
||||
} from '@pnpm/lockfile.utils'
|
||||
import { logger } from '@pnpm/logger'
|
||||
import { getPatchInfo } from '@pnpm/patching.config'
|
||||
import { pickRegistryForPackage } from '@pnpm/pick-registry-for-package'
|
||||
import {
|
||||
type DirectoryResolution,
|
||||
@@ -35,13 +36,13 @@ import {
|
||||
type SupportedArchitectures,
|
||||
type AllowedDeprecatedVersions,
|
||||
type PackageManifest,
|
||||
type PatchFile,
|
||||
type ReadPackageHook,
|
||||
type Registries,
|
||||
type PkgIdWithPatchHash,
|
||||
} from '@pnpm/types'
|
||||
import * as dp from '@pnpm/dependency-path'
|
||||
import { getPreferredVersionsFromLockfileAndManifests } from '@pnpm/lockfile.preferred-versions'
|
||||
import { type PatchFile, type PatchInfo } from '@pnpm/patching.types'
|
||||
import normalizePath from 'normalize-path'
|
||||
import exists from 'path-exists'
|
||||
import pDefer from 'p-defer'
|
||||
@@ -230,7 +231,7 @@ export interface ResolvedPackage {
|
||||
optionalDependencies: Set<string>
|
||||
hasBin: boolean
|
||||
hasBundledDependencies: boolean
|
||||
patchFile?: PatchFile
|
||||
patch?: PatchInfo
|
||||
prepare: boolean
|
||||
pkgIdWithPatchHash: PkgIdWithPatchHash
|
||||
requiresBuild?: boolean
|
||||
@@ -1352,11 +1353,10 @@ async function resolveDependency (
|
||||
throw new PnpmError('MISSING_PACKAGE_NAME', `Can't install ${wantedDependency.pref}: Missing package name`)
|
||||
}
|
||||
let pkgIdWithPatchHash = (pkgResponse.body.id.startsWith(`${pkg.name}@`) ? pkgResponse.body.id : `${pkg.name}@${pkgResponse.body.id}`) as PkgIdWithPatchHash
|
||||
const nameAndVersion = `${pkg.name}@${pkg.version}`
|
||||
const patchFile = ctx.patchedDependencies?.[nameAndVersion]
|
||||
if (patchFile) {
|
||||
ctx.appliedPatches.add(nameAndVersion)
|
||||
pkgIdWithPatchHash = `${pkgIdWithPatchHash}(patch_hash=${patchFile.hash})` as PkgIdWithPatchHash
|
||||
const patch = getPatchInfo(ctx.patchedDependencies, pkg.name, pkg.version)
|
||||
if (patch) {
|
||||
ctx.appliedPatches.add(patch.key)
|
||||
pkgIdWithPatchHash = `${pkgIdWithPatchHash}(patch_hash=${patch.file.hash})` as PkgIdWithPatchHash
|
||||
}
|
||||
|
||||
// We are building the dependency tree only until there are new packages
|
||||
@@ -1468,7 +1468,7 @@ async function resolveDependency (
|
||||
pkgIdWithPatchHash,
|
||||
force: ctx.force,
|
||||
hasBin,
|
||||
patchFile,
|
||||
patch,
|
||||
pkg,
|
||||
pkgResponse,
|
||||
prepare,
|
||||
@@ -1587,7 +1587,7 @@ function getResolvedPackage (
|
||||
force: boolean
|
||||
hasBin: boolean
|
||||
parentImporterId: string
|
||||
patchFile?: PatchFile
|
||||
patch?: PatchInfo
|
||||
pkg: PackageManifest
|
||||
pkgResponse: PackageResponse
|
||||
prepare: boolean
|
||||
@@ -1617,7 +1617,7 @@ function getResolvedPackage (
|
||||
name: options.pkg.name,
|
||||
optional: options.optional,
|
||||
optionalDependencies: new Set(Object.keys(options.pkg.optionalDependencies ?? {})),
|
||||
patchFile: options.patchFile,
|
||||
patch: options.patch,
|
||||
peerDependencies,
|
||||
prepare: options.prepare,
|
||||
prod: !options.wantedDependency.dev && !options.wantedDependency.optional,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { resolveFromCatalog } from '@pnpm/catalogs.resolver'
|
||||
import { type Catalogs } from '@pnpm/catalogs.types'
|
||||
import { type Lockfile, type PatchFile } from '@pnpm/lockfile.types'
|
||||
import { type Lockfile } from '@pnpm/lockfile.types'
|
||||
import { type PatchFile } from '@pnpm/patching.types'
|
||||
import { type PreferredVersions, type Resolution, type WorkspacePackages } from '@pnpm/resolver-base'
|
||||
import { type StoreController } from '@pnpm/store-controller-types'
|
||||
import {
|
||||
|
||||
@@ -143,7 +143,7 @@ function toLockfileDependency (
|
||||
if (pkg.hasBin) {
|
||||
result['hasBin'] = true
|
||||
}
|
||||
if (pkg.patchFile) {
|
||||
if (pkg.patch) {
|
||||
result['patched'] = true
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -51,6 +51,12 @@
|
||||
{
|
||||
"path": "../../packages/which-version-is-pinned"
|
||||
},
|
||||
{
|
||||
"path": "../../patching/config"
|
||||
},
|
||||
{
|
||||
"path": "../../patching/types"
|
||||
},
|
||||
{
|
||||
"path": "../../pkg-manifest/manifest-utils"
|
||||
},
|
||||
|
||||
93
pnpm-lock.yaml
generated
93
pnpm-lock.yaml
generated
@@ -55,8 +55,8 @@ catalogs:
|
||||
specifier: 0.0.0
|
||||
version: 0.0.0
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 3.39.0
|
||||
version: 3.39.0
|
||||
specifier: 3.40.0
|
||||
version: 3.40.0
|
||||
'@pnpm/semver-diff':
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0
|
||||
@@ -709,7 +709,7 @@ importers:
|
||||
version: 2.0.3
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 'catalog:'
|
||||
version: 3.39.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
version: 3.40.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
'@pnpm/tsconfig':
|
||||
specifier: workspace:*
|
||||
version: link:__utils__/tsconfig
|
||||
@@ -821,7 +821,7 @@ importers:
|
||||
version: link:../../pkg-manager/modules-yaml
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 'catalog:'
|
||||
version: 3.39.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
version: 3.40.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
'@pnpm/types':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/types
|
||||
@@ -855,7 +855,7 @@ importers:
|
||||
dependencies:
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 'catalog:'
|
||||
version: 3.39.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
version: 3.40.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
'@pnpm/store.cafs':
|
||||
specifier: workspace:*
|
||||
version: link:../../store/cafs
|
||||
@@ -1594,6 +1594,12 @@ importers:
|
||||
'@pnpm/package-is-installable':
|
||||
specifier: workspace:*
|
||||
version: link:../../config/package-is-installable
|
||||
'@pnpm/patching.config':
|
||||
specifier: workspace:*
|
||||
version: link:../../patching/config
|
||||
'@pnpm/patching.types':
|
||||
specifier: workspace:*
|
||||
version: link:../../patching/types
|
||||
'@pnpm/store-controller-types':
|
||||
specifier: workspace:*
|
||||
version: link:../../store/store-controller-types
|
||||
@@ -1814,6 +1820,9 @@ importers:
|
||||
'@pnpm/patching.apply-patch':
|
||||
specifier: workspace:*
|
||||
version: link:../../patching/apply-patch
|
||||
'@pnpm/patching.types':
|
||||
specifier: workspace:*
|
||||
version: link:../../patching/types
|
||||
'@pnpm/read-package-json':
|
||||
specifier: workspace:*
|
||||
version: link:../../pkg-manifest/read-package-json
|
||||
@@ -2036,7 +2045,7 @@ importers:
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 'catalog:'
|
||||
version: 3.39.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
version: 3.40.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
'@pnpm/test-fixtures':
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/test-fixtures
|
||||
@@ -2166,7 +2175,7 @@ importers:
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 'catalog:'
|
||||
version: 3.39.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
version: 3.40.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
'@pnpm/test-ipc-server':
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/test-ipc-server
|
||||
@@ -3101,6 +3110,9 @@ importers:
|
||||
|
||||
lockfile/types:
|
||||
dependencies:
|
||||
'@pnpm/patching.types':
|
||||
specifier: workspace:*
|
||||
version: link:../../patching/types
|
||||
'@pnpm/types':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/types
|
||||
@@ -3648,6 +3660,9 @@ importers:
|
||||
'@pnpm/error':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/error
|
||||
'@pnpm/logger':
|
||||
specifier: 'catalog:'
|
||||
version: 5.0.0
|
||||
'@pnpm/patch-package':
|
||||
specifier: 'catalog:'
|
||||
version: 0.0.0
|
||||
@@ -3662,6 +3677,16 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/test-fixtures
|
||||
|
||||
patching/config:
|
||||
dependencies:
|
||||
'@pnpm/patching.types':
|
||||
specifier: workspace:*
|
||||
version: link:../types
|
||||
devDependencies:
|
||||
'@pnpm/patching.config':
|
||||
specifier: workspace:*
|
||||
version: 'link:'
|
||||
|
||||
patching/plugin-commands-patching:
|
||||
dependencies:
|
||||
'@pnpm/cli-utils':
|
||||
@@ -3763,7 +3788,7 @@ importers:
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 'catalog:'
|
||||
version: 3.39.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
version: 3.40.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
'@pnpm/test-fixtures':
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/test-fixtures
|
||||
@@ -3783,6 +3808,12 @@ importers:
|
||||
specifier: 'catalog:'
|
||||
version: 5.0.0
|
||||
|
||||
patching/types:
|
||||
devDependencies:
|
||||
'@pnpm/patching.types':
|
||||
specifier: workspace:*
|
||||
version: 'link:'
|
||||
|
||||
pkg-manager/client:
|
||||
dependencies:
|
||||
'@pnpm/default-resolver':
|
||||
@@ -4041,7 +4072,7 @@ importers:
|
||||
version: link:../../pkg-manifest/read-package-json
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 'catalog:'
|
||||
version: 3.39.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
version: 3.40.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
'@pnpm/store-path':
|
||||
specifier: workspace:*
|
||||
version: link:../../store/store-path
|
||||
@@ -4260,6 +4291,9 @@ importers:
|
||||
'@pnpm/package-requester':
|
||||
specifier: workspace:*
|
||||
version: link:../package-requester
|
||||
'@pnpm/patching.config':
|
||||
specifier: workspace:*
|
||||
version: link:../../patching/config
|
||||
'@pnpm/pkg-manager.direct-dep-linker':
|
||||
specifier: workspace:*
|
||||
version: link:../direct-dep-linker
|
||||
@@ -4326,7 +4360,7 @@ importers:
|
||||
version: link:../read-projects-context
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 'catalog:'
|
||||
version: 3.39.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
version: 3.40.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
'@pnpm/store-path':
|
||||
specifier: workspace:*
|
||||
version: link:../../store/store-path
|
||||
@@ -4686,7 +4720,7 @@ importers:
|
||||
version: 'link:'
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 'catalog:'
|
||||
version: 3.39.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
version: 3.40.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
'@pnpm/test-fixtures':
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/test-fixtures
|
||||
@@ -4882,7 +4916,7 @@ importers:
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 'catalog:'
|
||||
version: 3.39.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
version: 3.40.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
'@pnpm/test-fixtures':
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/test-fixtures
|
||||
@@ -5075,6 +5109,12 @@ importers:
|
||||
'@pnpm/npm-resolver':
|
||||
specifier: workspace:*
|
||||
version: link:../../resolving/npm-resolver
|
||||
'@pnpm/patching.config':
|
||||
specifier: workspace:*
|
||||
version: link:../../patching/config
|
||||
'@pnpm/patching.types':
|
||||
specifier: workspace:*
|
||||
version: link:../../patching/types
|
||||
'@pnpm/pick-fetcher':
|
||||
specifier: workspace:*
|
||||
version: link:../../fetching/pick-fetcher
|
||||
@@ -5475,7 +5515,7 @@ importers:
|
||||
version: link:../pkg-manifest/read-project-manifest
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 'catalog:'
|
||||
version: 3.39.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
version: 3.40.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
'@pnpm/run-npm':
|
||||
specifier: workspace:*
|
||||
version: link:../exec/run-npm
|
||||
@@ -5747,7 +5787,7 @@ importers:
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 'catalog:'
|
||||
version: 3.39.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
version: 3.40.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
'@pnpm/workspace.filter-packages-from-dir':
|
||||
specifier: workspace:*
|
||||
version: link:../../workspace/filter-packages-from-dir
|
||||
@@ -5856,7 +5896,7 @@ importers:
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 'catalog:'
|
||||
version: 3.39.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
version: 3.40.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
'@pnpm/test-ipc-server':
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/test-ipc-server
|
||||
@@ -6434,7 +6474,7 @@ importers:
|
||||
version: link:../../pkg-manifest/read-package-json
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 'catalog:'
|
||||
version: 3.39.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
version: 3.40.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
'@pnpm/test-fixtures':
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/test-fixtures
|
||||
@@ -6501,7 +6541,7 @@ importers:
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 'catalog:'
|
||||
version: 3.39.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
version: 3.40.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
'@pnpm/workspace.filter-packages-from-dir':
|
||||
specifier: workspace:*
|
||||
version: link:../../workspace/filter-packages-from-dir
|
||||
@@ -6592,7 +6632,7 @@ importers:
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 'catalog:'
|
||||
version: 3.39.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
version: 3.40.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
'@pnpm/test-fixtures':
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/test-fixtures
|
||||
@@ -6930,7 +6970,7 @@ importers:
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 'catalog:'
|
||||
version: 3.39.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
version: 3.40.0(encoding@0.1.13)(typanion@3.14.0)
|
||||
'@types/archy':
|
||||
specifier: 'catalog:'
|
||||
version: 0.0.33
|
||||
@@ -7553,7 +7593,6 @@ packages:
|
||||
'@babel/plugin-proposal-dynamic-import@7.18.6':
|
||||
resolution: {integrity: sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-dynamic-import instead.
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0-0
|
||||
|
||||
@@ -8152,7 +8191,6 @@ packages:
|
||||
'@humanwhocodes/config-array@0.11.14':
|
||||
resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
|
||||
engines: {node: '>=10.10.0'}
|
||||
deprecated: Use @eslint/config-array instead
|
||||
|
||||
'@humanwhocodes/module-importer@1.0.1':
|
||||
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
|
||||
@@ -8160,7 +8198,6 @@ packages:
|
||||
|
||||
'@humanwhocodes/object-schema@2.0.3':
|
||||
resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==}
|
||||
deprecated: Use @eslint/object-schema instead
|
||||
|
||||
'@isaacs/cliui@8.0.2':
|
||||
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
|
||||
@@ -8507,8 +8544,8 @@ packages:
|
||||
resolution: {integrity: sha512-yxfJQayRXlmcs7eQJkNVz4yuZw6x3lYAoODeRCI0S0Ez7G2ql+zoOGeigIe2UxI2B8xN3WyDgZ4G2CqV7t5cBw==}
|
||||
engines: {node: '>=18.12'}
|
||||
|
||||
'@pnpm/registry-mock@3.39.0':
|
||||
resolution: {integrity: sha512-NzRI8ANvltBNUrgmTvoge5GkMZKaT8HvC2a0vQPof1bTxF0KZ/7UAK0Nv5atyT68YAniekBEeDEhLM9c0EMgtw==}
|
||||
'@pnpm/registry-mock@3.40.0':
|
||||
resolution: {integrity: sha512-9yJHuhHalHVHBhANoZwa6ZQhbcrXGWEULuAP+gA8XYQdTi1Mfn2EKJqZaZjs9pACoJauRh90e86X3/r6HlITpA==}
|
||||
engines: {node: '>=10.13'}
|
||||
hasBin: true
|
||||
|
||||
@@ -10174,7 +10211,6 @@ packages:
|
||||
|
||||
eslint-config-standard-with-typescript@39.1.1:
|
||||
resolution: {integrity: sha512-t6B5Ep8E4I18uuoYeYxINyqcXb2UbC0SOOTxRtBSt2JUs+EzeXbfe2oaiPs71AIdnoWhXDO2fYOHz8df3kV84A==}
|
||||
deprecated: Please use eslint-config-love, instead.
|
||||
peerDependencies:
|
||||
'@typescript-eslint/eslint-plugin': ^6.4.0
|
||||
eslint: ^8.0.1
|
||||
@@ -10682,7 +10718,6 @@ packages:
|
||||
|
||||
glob@7.2.3:
|
||||
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
|
||||
deprecated: Glob versions prior to v9 are no longer supported
|
||||
|
||||
glob@8.1.0:
|
||||
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
|
||||
@@ -10759,7 +10794,6 @@ packages:
|
||||
har-validator@5.1.5:
|
||||
resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==}
|
||||
engines: {node: '>=6'}
|
||||
deprecated: this library is no longer supported
|
||||
|
||||
hard-rejection@2.1.0:
|
||||
resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==}
|
||||
@@ -10944,7 +10978,6 @@ packages:
|
||||
|
||||
inflight@1.0.6:
|
||||
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
|
||||
deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
|
||||
|
||||
inherits@2.0.4:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
@@ -12663,7 +12696,6 @@ packages:
|
||||
request@2.88.0:
|
||||
resolution: {integrity: sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==}
|
||||
engines: {node: '>= 4'}
|
||||
deprecated: request has been deprecated, see https://github.com/request/request/issues/3142
|
||||
|
||||
request@2.88.2:
|
||||
resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==}
|
||||
@@ -13480,6 +13512,7 @@ packages:
|
||||
|
||||
uid-number@0.0.6:
|
||||
resolution: {integrity: sha512-c461FXIljswCuscZn67xq9PpszkPT6RjheWFQTgCyabJrTUozElanb0YEqv2UGgk247YpcJkFBuSGNvBlpXM9w==}
|
||||
deprecated: This package is no longer supported.
|
||||
|
||||
umask@1.1.0:
|
||||
resolution: {integrity: sha512-lE/rxOhmiScJu9L6RTNVgB/zZbF+vGC0/p6D3xnkAePI2o0sMyFG966iR5Ki50OI/0mNi2yaRnxfLsPmEZF/JA==}
|
||||
@@ -15343,7 +15376,7 @@ snapshots:
|
||||
sort-keys: 4.2.0
|
||||
strip-bom: 4.0.0
|
||||
|
||||
'@pnpm/registry-mock@3.39.0(encoding@0.1.13)(typanion@3.14.0)':
|
||||
'@pnpm/registry-mock@3.40.0(encoding@0.1.13)(typanion@3.14.0)':
|
||||
dependencies:
|
||||
anonymous-npm-registry-client: 0.2.0
|
||||
execa: 5.1.1
|
||||
|
||||
@@ -51,7 +51,7 @@ catalog:
|
||||
"@pnpm/npm-package-arg": ^1.0.0
|
||||
"@pnpm/os.env.path-extender": ^2.0.0
|
||||
"@pnpm/patch-package": 0.0.0
|
||||
"@pnpm/registry-mock": 3.39.0
|
||||
"@pnpm/registry-mock": 3.40.0
|
||||
"@pnpm/semver-diff": ^1.1.0
|
||||
"@pnpm/tabtab": ^0.5.4
|
||||
"@pnpm/util.lex-comparator": 3.0.0
|
||||
|
||||
Reference in New Issue
Block a user