feat: when patch package does not specify a version, use locally installed version by default. (#6197)

close #6192
This commit is contained in:
await-ovo
2023-03-13 22:40:21 +08:00
committed by GitHub
parent 787c43dcc1
commit 185ab01adf
13 changed files with 404 additions and 61 deletions

View File

@@ -0,0 +1,8 @@
---
"@pnpm/plugin-commands-patching": patch
"@pnpm/audit": patch
"@pnpm/list": patch
"pnpm": patch
---
When patch package does not specify a version, use locally installed version by default [#6192](https://github.com/pnpm/pnpm/issues/6192).

2
.gitignore vendored
View File

@@ -47,3 +47,5 @@ RELEASE.md
## custom modules-dir fixture
__fixtures__/custom-modules-dir/**/fake_modules/
__fixtures__/custom-modules-dir/cache/
__fixtures__/custom-modules-dir/patches/

View File

@@ -6,7 +6,7 @@ import { Lockfile } from '@pnpm/lockfile-types'
import { DependenciesField } from '@pnpm/types'
import { lockfileToAuditTree } from './lockfileToAuditTree'
import { AuditReport } from './types'
import { searchForPackages, PackageNode } from '@pnpm/list'
import { searchForPackages, flattenSearchedPackages } from '@pnpm/list'
export * from './types'
@@ -95,28 +95,7 @@ async function searchPackagePaths (
pkg: string
) {
const pkgs = await searchForPackages([pkg], projectDirs, searchOpts)
const paths: string[] = []
for (const pkg of pkgs) {
_walker([
...(pkg.optionalDependencies ?? []),
...(pkg.dependencies ?? []),
...(pkg.devDependencies ?? []),
...(pkg.unsavedDependencies ?? []),
], path.relative(searchOpts.lockfileDir, pkg.path) || '.')
}
return paths
function _walker (packages: PackageNode[], depPath: string) {
for (const pkg of packages) {
const nextDepPath = `${depPath} > ${pkg.name}@${pkg.version}`
if (pkg.dependencies?.length) {
_walker(pkg.dependencies, nextDepPath)
} else {
paths.push(nextDepPath)
}
}
}
return flattenSearchedPackages(pkgs, { lockfileDir: searchOpts.lockfileDir }).map(({ depPath }) => depPath)
}
export class AuditEndpointNotExistsError extends PnpmError {

View File

@@ -37,13 +37,17 @@
"@pnpm/plugin-commands-patching": "workspace:*",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "3.5.0",
"@pnpm/test-fixtures": "workspace:*",
"@types/ramda": "0.28.20",
"@types/semver": "7.3.13",
"write-yaml-file": "^4.2.0"
},
"dependencies": {
"@pnpm/cli-utils": "workspace:*",
"@pnpm/config": "workspace:*",
"@pnpm/constants": "workspace:*",
"@pnpm/error": "workspace:*",
"@pnpm/modules-yaml": "workspace:*",
"@pnpm/parse-wanted-dependency": "workspace:*",
"@pnpm/patching.apply-patch": "workspace:*",
"@pnpm/pick-registry-for-package": "workspace:*",
@@ -51,10 +55,15 @@
"@pnpm/read-package-json": "workspace:*",
"@pnpm/read-project-manifest": "workspace:*",
"@pnpm/store-connection-manager": "workspace:*",
"@pnpm/lockfile-file": "workspace:*",
"@pnpm/lockfile-utils": "workspace:*",
"enquirer": "^2.3.6",
"escape-string-regexp": "^4.0.0",
"ramda": "npm:@pnpm/ramda@0.28.1",
"realpath-missing": "^1.1.0",
"render-help": "^1.0.3",
"safe-execa": "^0.1.3",
"semver": "^7.3.8",
"tempy": "^1.0.1"
},
"peerDependencies": {

View File

@@ -0,0 +1,72 @@
import path from 'path'
import { parseWantedDependency, ParseWantedDependencyResult } from '@pnpm/parse-wanted-dependency'
import { prompt } from 'enquirer'
import { readCurrentLockfile } from '@pnpm/lockfile-file'
import { nameVerFromPkgSnapshot } from '@pnpm/lockfile-utils'
import { PnpmError } from '@pnpm/error'
import { WANTED_LOCKFILE } from '@pnpm/constants'
import { readModulesManifest } from '@pnpm/modules-yaml'
import realpathMissing from 'realpath-missing'
import semver from 'semver'
import { Config } from '@pnpm/config'
type GetPatchedDependencyOptions = {
lockfileDir: string
} & Pick<Config, 'virtualStoreDir' | 'modulesDir'>
export async function getPatchedDependency (rawDependency: string, opts: GetPatchedDependencyOptions): Promise<ParseWantedDependencyResult> {
const dep = parseWantedDependency(rawDependency)
const { versions, preferredVersions } = await getVersionsFromLockfile(dep, opts)
if (!preferredVersions.length) {
throw new PnpmError(
'PATCH_VERSION_NOT_FOUND',
`Can not find ${rawDependency} in project ${opts.lockfileDir}, ${versions.length ? `you can specify currently installed version: ${versions.join(', ')}.` : `did you forget to install ${rawDependency}?`}`
)
}
dep.alias = dep.alias ?? rawDependency
if (preferredVersions.length > 1) {
const { version } = await prompt<{
version: string
}>({
type: 'select',
name: 'version',
message: 'Choose which version to patch',
choices: versions,
})
dep.pref = version
} else {
dep.pref = preferredVersions[0]
}
return dep
}
async function getVersionsFromLockfile (dep: ParseWantedDependencyResult, opts: GetPatchedDependencyOptions) {
const modulesDir = await realpathMissing(path.join(opts.lockfileDir, opts.modulesDir ?? 'node_modules'))
const modules = await readModulesManifest(modulesDir)
const lockfile = (modules?.virtualStoreDir && await readCurrentLockfile(modules.virtualStoreDir, {
ignoreIncompatible: true,
})) ?? null
if (!lockfile) {
throw new PnpmError(
'PATCH_NO_LOCKFILE',
`No ${WANTED_LOCKFILE} found: Cannot patch without a lockfile`
)
}
const pkgName = dep.alias && dep.pref ? dep.alias : (dep.pref ?? dep.alias)
const versions = Object.entries(lockfile.packages ?? {})
.map(([depPath, pkgSnapshot]) => nameVerFromPkgSnapshot(depPath, pkgSnapshot))
.filter(({ name }) => name === pkgName)
.map(({ version }) => version)
return {
versions,
preferredVersions: versions.filter(version => dep.alias && dep.pref ? semver.satisfies(version, dep.pref) : true),
}
}

View File

@@ -11,7 +11,9 @@ import pick from 'ramda/src/pick'
import renderHelp from 'render-help'
import tempy from 'tempy'
import { PnpmError } from '@pnpm/error'
import { writePackage, ParseWantedDependencyResult } from './writePackage'
import { ParseWantedDependencyResult } from '@pnpm/parse-wanted-dependency'
import { writePackage } from './writePackage'
import { getPatchedDependency } from './getPatchedDependency'
export function rcOptionsTypes () {
return pick([], allTypes)
@@ -48,7 +50,16 @@ export function help () {
})
}
export type PatchCommandOptions = Pick<Config, 'dir' | 'registries' | 'tag' | 'storeDir' | 'rootProjectManifest' | 'lockfileDir'> & CreateStoreControllerOptions & {
export type PatchCommandOptions = Pick<Config,
| 'dir'
| 'registries'
| 'tag'
| 'storeDir'
| 'rootProjectManifest'
| 'lockfileDir'
| 'modulesDir'
| 'virtualStoreDir'
> & CreateStoreControllerOptions & {
editDir?: string
reporter?: (logObj: LogBase) => void
ignoreExisting?: boolean
@@ -58,14 +69,24 @@ export async function handler (opts: PatchCommandOptions, params: string[]) {
if (opts.editDir && fs.existsSync(opts.editDir) && fs.readdirSync(opts.editDir).length > 0) {
throw new PnpmError('PATCH_EDIT_DIR_EXISTS', `The target directory already exists: '${opts.editDir}'`)
}
if (!params[0]) {
throw new PnpmError('MISSING_PACKAGE_NAME', '`pnpm patch` requires the package name')
}
const editDir = opts.editDir ?? tempy.directory()
const patchedDep = await writePackage(params[0], editDir, opts)
const lockfileDir = opts.lockfileDir ?? opts.dir ?? process.cwd()
const patchedDep = await getPatchedDependency(params[0], {
lockfileDir,
modulesDir: opts.modulesDir,
virtualStoreDir: opts.virtualStoreDir,
})
await writePackage(patchedDep, editDir, opts)
if (!opts.ignoreExisting && opts.rootProjectManifest?.pnpm?.patchedDependencies) {
tryPatchWithExistingPatchFile({
patchedDep,
patchedDir: editDir,
patchedDependencies: opts.rootProjectManifest.pnpm.patchedDependencies,
lockfileDir: opts.lockfileDir ?? opts.dir ?? process.cwd(),
lockfileDir,
})
}
return `You can now edit the following folder: ${editDir}

View File

@@ -11,6 +11,7 @@ import escapeStringRegexp from 'escape-string-regexp'
import renderHelp from 'render-help'
import tempy from 'tempy'
import { writePackage } from './writePackage'
import { parseWantedDependency } from '@pnpm/parse-wanted-dependency'
export const rcOptionsTypes = cliOptionsTypes
@@ -37,7 +38,7 @@ export async function handler (opts: install.InstallCommandOptions & Pick<Config
const patchedPkgManifest = await readPackageJsonFromDir(userDir)
const pkgNameAndVersion = `${patchedPkgManifest.name}@${patchedPkgManifest.version}`
const srcDir = tempy.directory()
await writePackage(pkgNameAndVersion, srcDir, opts)
await writePackage(parseWantedDependency(pkgNameAndVersion), srcDir, opts)
const patchContent = await diffFolders(srcDir, userDir)
const patchFileName = pkgNameAndVersion.replace('/', '__')
await fs.promises.writeFile(path.join(patchesDir, `${patchFileName}.patch`), patchContent, 'utf8')

View File

@@ -4,14 +4,11 @@ import {
CreateStoreControllerOptions,
} from '@pnpm/store-connection-manager'
import { pickRegistryForPackage } from '@pnpm/pick-registry-for-package'
import { parseWantedDependency, ParseWantedDependencyResult } from '@pnpm/parse-wanted-dependency'
export { ParseWantedDependencyResult }
import type { ParseWantedDependencyResult } from '@pnpm/parse-wanted-dependency'
export type WritePackageOptions = CreateStoreControllerOptions & Pick<Config, 'registries'>
export async function writePackage (pkg: string, dest: string, opts: WritePackageOptions): Promise<ParseWantedDependencyResult> {
const dep = parseWantedDependency(pkg)
export async function writePackage (dep: ParseWantedDependencyResult, dest: string, opts: WritePackageOptions) {
const store = await createOrConnectStoreController({
...opts,
packageImportMethod: 'clone-or-copy',
@@ -28,5 +25,4 @@ export async function writePackage (pkg: string, dest: string, opts: WritePackag
filesResponse,
force: true,
})
return dep
}

View File

@@ -10,31 +10,51 @@ import { patch, patchCommit } from '@pnpm/plugin-commands-patching'
import { readProjectManifest } from '@pnpm/read-project-manifest'
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
import { DEFAULT_OPTS } from './utils/index'
import { fixtures } from '@pnpm/test-fixtures'
import * as enquirer from 'enquirer'
jest.mock('enquirer', () => ({ prompt: jest.fn() }))
// eslint-disable-next-line
const prompt = enquirer.prompt as any
const f = fixtures(__dirname)
const customModulesDirFixture = f.find('custom-modules-dir')
const basePatchOption = {
pnpmHomeDir: '',
rawConfig: {
registry: `http://localhost:${REGISTRY_MOCK_PORT}/`,
},
registries: { default: `http://localhost:${REGISTRY_MOCK_PORT}/` },
userConfig: {},
virtualStoreDir: 'node_modules/.pnpm',
}
describe('patch and commit', () => {
let defaultPatchOption: patch.PatchCommandOptions
beforeEach(() => {
beforeEach(async () => {
prepare({
dependencies: {
'is-positive': '1.0.0',
},
})
const cacheDir = path.resolve('cache')
const storeDir = path.resolve('store')
defaultPatchOption = {
...basePatchOption,
cacheDir,
dir: process.cwd(),
pnpmHomeDir: '',
rawConfig: {
registry: `http://localhost:${REGISTRY_MOCK_PORT}/`,
},
registries: { default: `http://localhost:${REGISTRY_MOCK_PORT}/` },
storeDir,
userConfig: {},
}
await install.handler({
...DEFAULT_OPTS,
cacheDir,
storeDir,
dir: process.cwd(),
saveLockfile: true,
})
})
test('patch and commit', async () => {
@@ -55,6 +75,8 @@ describe('patch and commit', () => {
await patchCommit.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
frozenLockfile: false,
fixLockfile: true,
}, [patchDir])
const { manifest } = await readProjectManifest(process.cwd())
@@ -84,6 +106,8 @@ describe('patch and commit', () => {
await patchCommit.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
frozenLockfile: false,
fixLockfile: true,
}, [patchDir])
expect(fs.readFileSync('node_modules/is-positive/index.js', 'utf8')).toContain('// test patching')
@@ -111,6 +135,8 @@ describe('patch and commit', () => {
await patchCommit.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
frozenLockfile: false,
fixLockfile: true,
}, [patchDir])
expect(fs.readFileSync('node_modules/is-positive/index.js', 'utf8')).toContain('// test patching')
@@ -126,6 +152,8 @@ describe('patch and commit', () => {
await patchCommit.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
frozenLockfile: false,
fixLockfile: true,
}, [patchDir])
const { manifest } = await readProjectManifest(process.cwd())
@@ -171,6 +199,8 @@ describe('patch and commit', () => {
await patchCommit.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
frozenLockfile: false,
fixLockfile: true,
}, [patchDir])
const { manifest } = await readProjectManifest(process.cwd())
@@ -187,12 +217,101 @@ describe('patch and commit', () => {
expect(fs.existsSync(path.join(patchDir, 'license'))).toBe(true)
expect(fs.readFileSync(path.join(patchDir, 'index.js'), 'utf8')).not.toContain('// test patching')
})
test('patch throw an error if no package specified', async () => {
await expect(() => patch.handler({ ...defaultPatchOption }, []))
.rejects.toThrow('`pnpm patch` requires the package name')
})
test('should throw an error if no installed versions found for patched package', async () => {
await expect(() => patch.handler(defaultPatchOption, ['chalk']))
.rejects.toThrow(`Can not find chalk in project ${process.cwd()}, did you forget to install chalk?`)
})
test('should throw an error if no preferred versions found for patched package', async () => {
await expect(() => patch.handler(defaultPatchOption, ['is-positive@2.0.0']))
.rejects.toThrow(`Can not find is-positive@2.0.0 in project ${process.cwd()}, you can specify currently installed version: 1.0.0.`)
})
test('patch package with installed version', async () => {
const output = await patch.handler(defaultPatchOption, ['is-positive@1'])
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('1.0.0')
})
})
describe('prompt to choose version', () => {
let defaultPatchOption: patch.PatchCommandOptions
let cacheDir: string
let storeDir: string
beforeEach(() => {
prepare({
dependencies: {
ava: '5.2.0',
chalk: '4.1.2',
},
})
cacheDir = path.resolve('cache')
storeDir = path.resolve('store')
defaultPatchOption = {
...basePatchOption,
cacheDir,
dir: process.cwd(),
storeDir,
}
})
test('prompt to choose version if multiple version founded for patched package', async () => {
await install.handler({
...DEFAULT_OPTS,
cacheDir,
storeDir,
dir: process.cwd(),
saveLockfile: true,
})
prompt.mockResolvedValue({
version: '5.2.0',
})
const output = await patch.handler(defaultPatchOption, ['chalk'])
expect(prompt.mock.calls[0][0].choices).toEqual(expect.arrayContaining(['5.2.0', '4.1.2']))
prompt.mockClear()
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.2.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,
dir: process.cwd(),
frozenLockfile: false,
fixLockfile: true,
}, [patchDir])
const { manifest } = await readProjectManifest(process.cwd())
expect(manifest.pnpm?.patchedDependencies).toStrictEqual({
'chalk@5.2.0': 'patches/chalk@5.2.0.patch',
})
const patchContent = fs.readFileSync('patches/chalk@5.2.0.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', () => {
let defaultPatchOption: patch.PatchCommandOptions
beforeEach(() => {
beforeEach(async () => {
prepare({
dependencies: {
'safe-execa': '0.1.2',
@@ -203,16 +322,19 @@ describe('patching should work when there is a no EOL in the patched file', () =
const storeDir = path.resolve('store')
defaultPatchOption = {
...basePatchOption,
cacheDir,
dir: process.cwd(),
pnpmHomeDir: '',
rawConfig: {
registry: `http://localhost:${REGISTRY_MOCK_PORT}/`,
},
registries: { default: `http://localhost:${REGISTRY_MOCK_PORT}/` },
storeDir,
userConfig: {},
}
await install.handler({
...DEFAULT_OPTS,
cacheDir,
storeDir,
dir: process.cwd(),
saveLockfile: true,
})
})
it('should work when adding content on a newline', async () => {
const output = await patch.handler(defaultPatchOption, ['safe-execa@0.1.2'])
@@ -228,6 +350,8 @@ describe('patching should work when there is a no EOL in the patched file', () =
await patchCommit.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
frozenLockfile: false,
fixLockfile: true,
}, [userPatchDir])
const { manifest } = await readProjectManifest(process.cwd())
@@ -254,6 +378,8 @@ describe('patching should work when there is a no EOL in the patched file', () =
await patchCommit.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
frozenLockfile: false,
fixLockfile: true,
}, [userPatchDir])
const { manifest } = await readProjectManifest(process.cwd())
@@ -300,15 +426,10 @@ describe('patch and commit in workspaces', () => {
storeDir = path.resolve('store')
defaultPatchOption = {
...basePatchOption,
cacheDir,
dir: process.cwd(),
pnpmHomeDir: '',
rawConfig: {
registry: `http://localhost:${REGISTRY_MOCK_PORT}/`,
},
registries: { default: `http://localhost:${REGISTRY_MOCK_PORT}/` },
storeDir,
userConfig: {},
}
})
@@ -322,11 +443,11 @@ describe('patch and commit in workspaces', () => {
allProjects,
allProjectsGraph,
dir: process.cwd(),
lockfileDir: process.cwd(),
selectedProjectsGraph,
workspaceDir: process.cwd(),
saveLockfile: true,
})
const output = await patch.handler(defaultPatchOption, ['is-positive@1.0.0'])
const patchDir = getPatchDirFromPatchOutput(output)
const tempDir = os.tmpdir()
@@ -350,6 +471,8 @@ describe('patch and commit in workspaces', () => {
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
saveLockfile: true,
frozenLockfile: false,
fixLockfile: true,
}, [patchDir])
const { manifest } = await readProjectManifest(process.cwd())
@@ -366,6 +489,66 @@ describe('patch and commit in workspaces', () => {
})
})
describe('patch with custom modules-dir and virtual-store-dir', () => {
const cacheDir = path.resolve(customModulesDirFixture, 'cache')
const storeDir = path.resolve(customModulesDirFixture, 'store')
const defaultPatchOption = {
...basePatchOption,
cacheDir,
dir: customModulesDirFixture,
storeDir,
modulesDir: 'fake_modules',
virtualStoreDir: 'fake_modules/.fake_store',
}
test('should work with custom modules-dir and virtual-store-dir', async () => {
const manifest = fs.readFileSync(path.join(customModulesDirFixture, 'package.json'), 'utf8')
const lockfileYaml = fs.readFileSync(path.join(customModulesDirFixture, 'pnpm-lock.yaml'), 'utf8')
const { allProjects, allProjectsGraph, selectedProjectsGraph } = await readProjects(customModulesDirFixture, [])
await install.handler({
...DEFAULT_OPTS,
cacheDir,
storeDir,
dir: customModulesDirFixture,
lockfileDir: customModulesDirFixture,
allProjects,
allProjectsGraph,
selectedProjectsGraph,
workspaceDir: customModulesDirFixture,
saveLockfile: true,
modulesDir: 'fake_modules',
virtualStoreDir: 'fake_modules/.fake_store',
})
const output = await patch.handler(defaultPatchOption, ['is-positive@1'])
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('1.0.0')
fs.appendFileSync(path.join(patchDir, 'index.js'), '// test patching', 'utf8')
await patchCommit.handler({
...DEFAULT_OPTS,
dir: customModulesDirFixture,
saveLockfile: true,
frozenLockfile: false,
fixLockfile: true,
allProjects,
allProjectsGraph,
selectedProjectsGraph,
modulesDir: 'fake_modules',
virtualStoreDir: 'fake_modules/.fake_store',
lockfileDir: customModulesDirFixture,
workspaceDir: customModulesDirFixture,
}, [patchDir])
expect(fs.readFileSync(path.join(customModulesDirFixture, 'packages/bar/fake_modules/is-positive/index.js'), 'utf8')).toContain('// test patching')
// restore package.json and package-lock.yaml
fs.writeFileSync(path.join(customModulesDirFixture, 'package.json'), manifest, 'utf8')
fs.writeFileSync(path.join(customModulesDirFixture, 'pnpm-lock.yaml'), lockfileYaml, 'utf8')
})
})
function getPatchDirFromPatchOutput (output: string) {
const [firstLine] = output.split('\n')
return firstLine.substring(firstLine.indexOf(':') + 1).trim()

View File

@@ -12,6 +12,9 @@
{
"path": "../../__utils__/prepare"
},
{
"path": "../../__utils__/test-fixtures"
},
{
"path": "../../cli/cli-utils"
},
@@ -21,12 +24,24 @@
{
"path": "../../config/pick-registry-for-package"
},
{
"path": "../../lockfile/lockfile-file"
},
{
"path": "../../lockfile/lockfile-utils"
},
{
"path": "../../packages/constants"
},
{
"path": "../../packages/error"
},
{
"path": "../../packages/parse-wanted-dependency"
},
{
"path": "../../pkg-manager/modules-yaml"
},
{
"path": "../../pkg-manager/plugin-commands-installation"
},

30
pnpm-lock.yaml generated
View File

@@ -2486,12 +2486,24 @@ importers:
'@pnpm/config':
specifier: workspace:*
version: link:../../config/config
'@pnpm/constants':
specifier: workspace:*
version: link:../../packages/constants
'@pnpm/error':
specifier: workspace:*
version: link:../../packages/error
'@pnpm/lockfile-file':
specifier: workspace:*
version: link:../../lockfile/lockfile-file
'@pnpm/lockfile-utils':
specifier: workspace:*
version: link:../../lockfile/lockfile-utils
'@pnpm/logger':
specifier: ^5.0.0
version: 5.0.0
'@pnpm/modules-yaml':
specifier: workspace:*
version: link:../../pkg-manager/modules-yaml
'@pnpm/parse-wanted-dependency':
specifier: workspace:*
version: link:../../packages/parse-wanted-dependency
@@ -2513,18 +2525,27 @@ importers:
'@pnpm/store-connection-manager':
specifier: workspace:*
version: link:../../store/store-connection-manager
enquirer:
specifier: ^2.3.6
version: 2.3.6
escape-string-regexp:
specifier: ^4.0.0
version: 4.0.0
ramda:
specifier: npm:@pnpm/ramda@0.28.1
version: /@pnpm/ramda@0.28.1
realpath-missing:
specifier: ^1.1.0
version: 1.1.0
render-help:
specifier: ^1.0.3
version: 1.0.3
safe-execa:
specifier: ^0.1.3
version: 0.1.3
semver:
specifier: ^7.3.8
version: 7.3.8
tempy:
specifier: ^1.0.1
version: 1.0.1
@@ -2541,9 +2562,15 @@ importers:
'@pnpm/registry-mock':
specifier: 3.5.0
version: 3.5.0(typanion@3.12.1)
'@pnpm/test-fixtures':
specifier: workspace:*
version: link:../../__utils__/test-fixtures
'@types/ramda':
specifier: 0.28.20
version: 0.28.20
'@types/semver':
specifier: 7.3.13
version: 7.3.13
write-yaml-file:
specifier: ^4.2.0
version: 4.2.0
@@ -8006,7 +8033,7 @@ packages:
'@pnpm/find-workspace-dir': 5.0.1
'@pnpm/find-workspace-packages': 5.0.36(@pnpm/logger@5.0.0)(@yarnpkg/core@4.0.0-rc.14)(typanion@3.12.1)
'@pnpm/logger': 5.0.0
'@pnpm/types': 8.10.0
'@pnpm/types': 8.9.0
'@yarnpkg/core': 4.0.0-rc.14(typanion@3.12.1)
load-json-file: 7.0.1
meow: 10.1.5
@@ -8555,7 +8582,6 @@ packages:
/@pnpm/types@8.9.0:
resolution: {integrity: sha512-3MYHYm8epnciApn6w5Fzx6sepawmsNU7l6lvIq+ER22/DPSrr83YMhU/EQWnf4lORn2YyiXFj0FJSyJzEtIGmw==}
engines: {node: '>=14.6'}
dev: false
/@pnpm/util.lex-comparator@1.0.0:
resolution: {integrity: sha512-3aBQPHntVgk5AweBWZn+1I/fqZ9krK/w01197aYVkAJQGftb+BVWgEepxY5GChjSW12j52XX+CmfynYZ/p0DFQ==}

View File

@@ -168,7 +168,7 @@ export async function main (inputArgv: string[]) {
}
if (
(cmd === 'install' || cmd === 'import' || cmd === 'dedupe' || cmd === 'patch-commit') &&
(cmd === 'install' || cmd === 'import' || cmd === 'dedupe' || cmd === 'patch-commit' || cmd === 'patch') &&
typeof workspaceDir === 'string'
) {
cliOptions['recursive'] = true

View File

@@ -1,6 +1,7 @@
import path from 'path'
import { readProjectManifestOnly } from '@pnpm/read-project-manifest'
import { DependenciesField, Registries } from '@pnpm/types'
import { buildDependenciesHierarchy } from '@pnpm/reviewing.dependencies-hierarchy'
import { PackageNode, buildDependenciesHierarchy } from '@pnpm/reviewing.dependencies-hierarchy'
import { createPackagesSearcher } from './createPackagesSearcher'
import { renderJson } from './renderJson'
import { renderParseable } from './renderParseable'
@@ -18,6 +19,36 @@ const DEFAULTS = {
showExtraneous: true,
}
export function flattenSearchedPackages (pkgs: PackageDependencyHierarchy[], opts: {
lockfileDir: string
}) {
const flattedPkgs: Array<PackageDependencyHierarchy & { depPath: string }> = []
for (const pkg of pkgs) {
_walker([
...(pkg.optionalDependencies ?? []),
...(pkg.dependencies ?? []),
...(pkg.devDependencies ?? []),
...(pkg.unsavedDependencies ?? []),
], path.relative(opts.lockfileDir, pkg.path) || '.')
}
return flattedPkgs
function _walker (packages: PackageNode[], depPath: string) {
for (const pkg of packages) {
const nextDepPath = `${depPath} > ${pkg.name}@${pkg.version}`
if (pkg.dependencies?.length) {
_walker(pkg.dependencies, nextDepPath)
} else {
flattedPkgs.push({
depPath: nextDepPath,
...pkg,
})
}
}
}
}
export async function searchForPackages (
packages: string[],
projectPaths: string[],