Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: ignore packages listed in package.json > pnpm.updateConfig.ignoreDependencies on update/outdated commands #5408

Merged
merged 24 commits into from Oct 2, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6b21dcd
feat: add update.ignore fields for pnpm field in package.json
Shinyaigeek Sep 25, 2022
6cbcb40
chore: add unit-tests for outdated command's handling of pnpm.update.…
Shinyaigeek Sep 25, 2022
6440cc0
chore: add unit-tests for update command's handling of pnpm.update.ig…
Shinyaigeek Sep 25, 2022
d8a0636
feat: ignore packages listed in pnpm.update.ignore field on outdated …
Shinyaigeek Sep 25, 2022
0573b75
feat: ignore packages listed in pnpm.update.ignore field on update co…
Shinyaigeek Sep 25, 2022
b717a82
chore: add changeset
Shinyaigeek Sep 25, 2022
bc400b1
refactor: rename pnpm.update.ignore -> pnpm.update.ignoreDependencies
Shinyaigeek Sep 27, 2022
405733e
chore: make this change minor change
Shinyaigeek Sep 27, 2022
9c5a267
feat: not ignore packages specified in parameter in update command
Shinyaigeek Sep 28, 2022
4123501
refactor: rename field pnpm.update -> pnpm.updateConfig
Shinyaigeek Sep 28, 2022
bc9321d
refactor: only read updateConfig from the root
zkochan Sep 28, 2022
c4a3730
feat: ignore packages listed in pnpm.update.ignore field on update co…
Shinyaigeek Sep 30, 2022
dfc8b5e
fixup! feat: ignore packages listed in pnpm.update.ignore field on up…
Shinyaigeek Sep 30, 2022
e939c47
fixup! feat: ignore packages listed in pnpm.update.ignore field on up…
Shinyaigeek Sep 30, 2022
62186c6
fixup! chore: add unit-tests for update command's handling of pnpm.up…
Shinyaigeek Sep 30, 2022
a2dbfaa
fixup! feat: ignore packages listed in pnpm.update.ignore field on up…
Shinyaigeek Oct 1, 2022
26918ac
refactor: update
zkochan Oct 1, 2022
bdd2f22
refactor: update
zkochan Oct 1, 2022
76b4073
refactor: update
zkochan Oct 1, 2022
a5f659b
refactor: update
zkochan Oct 2, 2022
d06f3e4
refactor: update
zkochan Oct 2, 2022
0237a40
refactor: update
zkochan Oct 2, 2022
1187055
refactor: update
zkochan Oct 2, 2022
2e4e8fd
refactor: update
zkochan Oct 2, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/rude-vans-build.md
@@ -0,0 +1,8 @@
---
"@pnpm/outdated": minor
"@pnpm/plugin-commands-installation": minor
"@pnpm/plugin-commands-outdated": minor
"@pnpm/types": minor
---

Ignore packages listed in package.json > pnpm.updateConfig.ignoreDependencies fields on update/outdated command [#5358](https://github.com/pnpm/pnpm/issues/5358)
15 changes: 15 additions & 0 deletions fixtures/with-pnpm-update-ignore/package.json
@@ -0,0 +1,15 @@
{
"name": "with-pnpm-update-ignore",
"version": "1.0.0",
"dependencies": {
"is-positive": "1.0.0",
"is-negative": "1.0.0"
},
"pnpm": {
"updateConfig": {
"ignoreDependencies": [
"is-positive"
]
}
}
}
21 changes: 21 additions & 0 deletions fixtures/with-pnpm-update-ignore/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions packages/outdated/src/outdated.ts
Expand Up @@ -34,6 +34,7 @@ export default async function outdated (
compatible?: boolean
currentLockfile: Lockfile | null
getLatestManifest: GetLatestManifestFunction
ignoreDependencies?: Set<string>
include?: IncludedDependencies
lockfileDir: string
manifest: ProjectManifest
Expand Down Expand Up @@ -74,6 +75,10 @@ export default async function outdated (
return
}

if (opts.ignoreDependencies?.has(alias)) {
return
}

const relativeDepPath = dp.refToRelative(ref, alias)

// ignoring linked packages
Expand Down
2 changes: 2 additions & 0 deletions packages/outdated/src/outdatedDepsOfProjects.ts
Expand Up @@ -18,6 +18,7 @@ export default async function outdatedDepsOfProjects (
args: string[],
opts: Omit<ManifestGetterOptions, 'fullMetadata' | 'lockfileDir'> & {
compatible?: boolean
ignoreDependencies?: Set<string>
include: IncludedDependencies
} & Partial<Pick<ManifestGetterOptions, 'fullMetadata' | 'lockfileDir'>>
): Promise<OutdatedPackage[][]> {
Expand All @@ -44,6 +45,7 @@ export default async function outdatedDepsOfProjects (
compatible: opts.compatible,
currentLockfile,
getLatestManifest,
ignoreDependencies: opts.ignoreDependencies,
include: opts.include,
lockfileDir,
manifest,
Expand Down
30 changes: 19 additions & 11 deletions packages/plugin-commands-installation/src/installDeps.ts
Expand Up @@ -199,34 +199,42 @@ when running add/update with the --workspace option')
}
}

const updateMatch = opts.update && (params.length > 0) ? createMatcher(params) : null
let currentInput = [...params]

if (opts.update) {
const ignoredPackages = (manifest.pnpm?.updateConfig?.ignoreDependencies ?? [])
currentInput = [...ignoredPackages.map(pkg => `!${pkg}`), ...currentInput]
Copy link
Member

@zkochan zkochan Sep 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we already discussed that the ignore list will only be active when pnpm update is called without args. So why do you append currentInput?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought it would be ok to append the ignored packages to the beginning of currentInput with ! because the successor params will override ! packages.

However, I have rethought that it is not friendly to ignore packages even if they are explicitly specified * params, so I will append only if the params are empty 🙏 .

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in dfc8b5e

}

const updateMatch = opts.update && (currentInput.length > 0) ? createMatcher(currentInput) : null
if (updateMatch != null) {
params = matchDependencies(updateMatch, manifest, includeDirect)
if (params.length === 0 && opts.depth === 0) {
currentInput = matchDependencies(updateMatch, manifest, includeDirect)
if (currentInput.length === 0 && opts.depth === 0) {
throw new PnpmError('NO_PACKAGE_IN_DEPENDENCIES',
'None of the specified packages were found in the dependencies.')
}
}

if (opts.update && opts.latest) {
if (!params || (params.length === 0)) {
params = updateToLatestSpecsFromManifest(manifest, includeDirect)
if (!currentInput || (currentInput.length === 0)) {
currentInput = updateToLatestSpecsFromManifest(manifest, includeDirect)
} else {
params = createLatestSpecs(params, manifest)
currentInput = createLatestSpecs(currentInput, manifest)
}
}
if (opts.workspace) {
if (!params || (params.length === 0)) {
params = updateToWorkspacePackagesFromManifest(manifest, includeDirect, workspacePackages)
if (!currentInput || (currentInput.length === 0)) {
currentInput = updateToWorkspacePackagesFromManifest(manifest, includeDirect, workspacePackages)
} else {
params = createWorkspaceSpecs(params, workspacePackages)
currentInput = createWorkspaceSpecs(currentInput, workspacePackages)
}
}
if (params?.length) {

if (currentInput?.length) {
const mutatedProject: MutatedProject = {
allowNew: opts.allowNew,
binsDir: opts.bin,
dependencySelectors: params,
dependencySelectors: currentInput,
manifest,
mutation: 'installSome',
peer: opts.savePeer,
Expand Down
13 changes: 11 additions & 2 deletions packages/plugin-commands-installation/src/recursive.ts
Expand Up @@ -173,8 +173,6 @@ export default async function recursive (
optionalDependencies: true,
}

const updateMatch = cmdFullName === 'update' && (params.length > 0) ? createMatcher(params) : null

// For a workspace with shared lockfile
if (opts.lockfileDir && ['add', 'install', 'remove', 'update', 'import'].includes(cmdFullName)) {
let importers = await getImporters()
Expand All @@ -201,6 +199,11 @@ export default async function recursive (
const modulesDir = localConfig.modulesDir ?? opts.modulesDir
const { manifest, writeProjectManifest } = manifestsByPath[rootDir]
let currentInput = [...params]
if (opts.update) {
const ignoredPackages = (manifest.pnpm?.updateConfig?.ignoreDependencies ?? [])
currentInput = [...ignoredPackages.map(pkg => `!${pkg}`), ...currentInput]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do append currentInput if it will be empty always?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as #5408 (comment)

}
const updateMatch = cmdFullName === 'update' && (currentInput.length > 0) ? createMatcher(currentInput) : null
if (updateMatch != null) {
currentInput = matchDependencies(updateMatch, manifest, includeDirect)
if ((currentInput.length === 0) && (typeof opts.depth === 'undefined' || opts.depth <= 0)) {
Expand All @@ -226,6 +229,7 @@ export default async function recursive (
currentInput = createWorkspaceSpecs(currentInput, workspacePackages)
}
}

writeProjectManifests.push(writeProjectManifest)
switch (mutation) {
case 'uninstallSome':
Expand Down Expand Up @@ -307,6 +311,11 @@ export default async function recursive (

const { manifest, writeProjectManifest } = manifestsByPath[rootDir]
let currentInput = [...params]
if (opts.update) {
const ignoredPackages = (manifest.pnpm?.updateConfig?.ignoreDependencies ?? [])
currentInput = [...ignoredPackages.map(pkg => `!${pkg}`), ...currentInput]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do append currentInput if it will be empty always?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did so because I did not want to make if statement’s condition complex, but this is not strong will so I changed if statement’s condition in this line as you say. a2dbfaa

}
const updateMatch = cmdFullName === 'update' && (currentInput.length > 0) ? createMatcher(currentInput) : null
if (updateMatch != null) {
currentInput = matchDependencies(updateMatch, manifest, includeDirect)
if (currentInput.length === 0) return
Expand Down
88 changes: 88 additions & 0 deletions packages/plugin-commands-installation/test/update/update.ts
Expand Up @@ -224,3 +224,91 @@ test('update should work normal when set empty string version', async () => {
expect(lockfile.devDependencies['@pnpm.e2e/foo']).toEqual('2.0.0')
expect(lockfile.devDependencies['@pnpm.e2e/peer-c']).toEqual('2.0.0')
})

test('ignore packages in package.json > Config.ignoreDependencies fields in update command', async () => {
await addDistTag({ package: '@pnpm.e2e/foo', version: '2.0.0', distTag: 'latest' })
await addDistTag({ package: '@pnpm.e2e/bar', version: '100.0.0', distTag: 'latest' })

const project = prepare({
dependencies: {
'@pnpm.e2e/foo': '2.0.0',
'@pnpm.e2e/bar': '100.0.0',
},
pnpm: {
updateConfig: {
ignoreDependencies: [
'@pnpm.e2e/foo',
],
},
},
})

await install.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
workspaceDir: process.cwd(),
})

const lockfile = await project.readLockfile()

expect(lockfile.packages['/@pnpm.e2e/foo/2.0.0']).toBeTruthy()
expect(lockfile.packages['/@pnpm.e2e/bar/100.0.0']).toBeTruthy()

await addDistTag({ package: '@pnpm.e2e/foo', version: '100.0.0', distTag: 'latest' })
await addDistTag({ package: '@pnpm.e2e/bar', version: '100.1.0', distTag: 'latest' })

await update.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
workspaceDir: process.cwd(),
Shinyaigeek marked this conversation as resolved.
Show resolved Hide resolved
})

const lockfileUpdated = await project.readLockfile()

expect(lockfileUpdated.packages['/@pnpm.e2e/foo/2.0.0']).toBeTruthy()
expect(lockfileUpdated.packages['/@pnpm.e2e/bar/100.1.0']).toBeTruthy()
})

test('not ignore packages if these are specified in parameter even if these are listed in package.json > pnpm.update.ignoreDependencies fields in update command', async () => {
await addDistTag({ package: '@pnpm.e2e/foo', version: '2.0.0', distTag: 'latest' })
await addDistTag({ package: '@pnpm.e2e/bar', version: '100.0.0', distTag: 'latest' })

const project = prepare({
dependencies: {
'@pnpm.e2e/foo': '2.0.0',
'@pnpm.e2e/bar': '100.0.0',
},
pnpm: {
updateConfig: {
ignoreDependencies: [
'@pnpm.e2e/foo',
],
},
},
})

await install.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
workspaceDir: process.cwd(),
})

const lockfile = await project.readLockfile()

expect(lockfile.packages['/@pnpm.e2e/foo/2.0.0']).toBeTruthy()
expect(lockfile.packages['/@pnpm.e2e/bar/100.0.0']).toBeTruthy()

await addDistTag({ package: '@pnpm.e2e/foo', version: '100.0.0', distTag: 'latest' })
await addDistTag({ package: '@pnpm.e2e/bar', version: '100.1.0', distTag: 'latest' })

await update.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
workspaceDir: process.cwd(),
}, ['@pnpm.e2e/foo@latest', '@pnpm.e2e/bar@latest'])

const lockfileUpdated = await project.readLockfile()

expect(lockfileUpdated.packages['/@pnpm.e2e/foo/100.0.0']).toBeTruthy()
expect(lockfileUpdated.packages['/@pnpm.e2e/bar/100.1.0']).toBeTruthy()
})
4 changes: 3 additions & 1 deletion packages/plugin-commands-outdated/src/outdated.ts
Expand Up @@ -167,15 +167,17 @@ export async function handler (
const pkgs = Object.values(opts.selectedProjectsGraph).map((wsPkg) => wsPkg.package)
return outdatedRecursive(pkgs, params, { ...opts, include })
}
const manifest = await readProjectManifestOnly(opts.dir, opts)
const packages = [
{
dir: opts.dir,
manifest: await readProjectManifestOnly(opts.dir, opts),
manifest,
},
]
const [outdatedPackages] = await outdatedDepsOfProjects(packages, params, {
...opts,
fullMetadata: opts.long,
ignoreDependencies: new Set(manifest?.pnpm?.updateConfig?.ignoreDependencies ?? []),
include,
retry: {
factor: opts.fetchRetryFactor,
Expand Down
2 changes: 2 additions & 0 deletions packages/plugin-commands-outdated/src/recursive.ts
Expand Up @@ -50,9 +50,11 @@ export default async (
opts: OutdatedCommandOptions & { include: IncludedDependencies }
) => {
const outdatedMap = {} as Record<string, OutdatedInWorkspace>
const rootManifest = pkgs.find(({ dir }) => dir === opts.lockfileDir ?? opts.dir)
const outdatedPackagesByProject = await outdatedDepsOfProjects(pkgs, params, {
...opts,
fullMetadata: opts.long,
ignoreDependencies: new Set(rootManifest?.manifest?.pnpm?.updateConfig?.ignoreDependencies ?? []),
retry: {
factor: opts.fetchRetryFactor,
maxTimeout: opts.fetchRetryMaxtimeout,
Expand Down
17 changes: 17 additions & 0 deletions packages/plugin-commands-outdated/test/index.ts
Expand Up @@ -15,6 +15,7 @@ const hasOutdatedDepsFixtureAndExternalLockfile = path.join(fixtures, 'has-outda
const hasNotOutdatedDepsFixture = path.join(fixtures, 'has-not-outdated-deps')
const hasMajorOutdatedDepsFixture = path.join(fixtures, 'has-major-outdated-deps')
const hasNoLockfileFixture = path.join(fixtures, 'has-no-lockfile')
const withPnpmUpdateIgnore = path.join(fixtures, 'with-pnpm-update-ignore')

const REGISTRY_URL = `http://localhost:${REGISTRY_MOCK_PORT}`

Expand Down Expand Up @@ -317,3 +318,19 @@ test('pnpm outdated: print only compatible versions', async () => {
└─────────────┴─────────┴────────┘
`)
})

test('ignore packages in package.json > pnpm.updateConfig.ignoreDependencies in outdated command', async () => {
const { output, exitCode } = await outdated.handler({
...OUTDATED_OPTIONS,
dir: withPnpmUpdateIgnore,
})

expect(exitCode).toBe(1)
expect(stripAnsi(output)).toBe(`\
┌─────────────┬─────────┬────────┐
│ Package │ Current │ Latest │
├─────────────┼─────────┼────────┤
│ is-negative │ 1.0.0 │ 2.1.0 │
└─────────────┴─────────┴────────┘
`)
})
3 changes: 3 additions & 0 deletions packages/types/src/package.ts
Expand Up @@ -131,6 +131,9 @@ export type ProjectManifest = BaseManifest & {
allowedDeprecatedVersions?: AllowedDeprecatedVersions
allowNonAppliedPatches?: boolean
patchedDependencies?: Record<string, string>
updateConfig?: {
ignoreDependencies?: string[]
}
}
private?: boolean
resolutions?: Record<string, string>
Expand Down