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 all 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.

7 changes: 5 additions & 2 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 @@ -69,8 +70,10 @@ export default async function outdated (
pkgs.map(async (alias) => {
const ref = opts.wantedLockfile!.importers[importerId][depType]![alias]

// ignoring linked packages. (For backward compatibility)
if (ref.startsWith('file:')) {
if (
ref.startsWith('file:') || // ignoring linked packages. (For backward compatibility)
opts.ignoreDependencies?.has(alias)
) {
return
}

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
8 changes: 7 additions & 1 deletion packages/plugin-commands-installation/jest.config.js
@@ -1 +1,7 @@
module.exports = require('../../jest.config.js')
module.exports = {
...require('../../jest.config.js'),
// This is a temporary workaround.
// Currently, multiple tests use the @pnpm.e2e/foo package and they change it's dist-tags.
// These tests are in separate files, so sometimes they will simultaneously set the dist tag and fail because they expect different versions to be tagged.
maxWorkers: 1,
}
15 changes: 13 additions & 2 deletions packages/plugin-commands-installation/src/installDeps.ts
Expand Up @@ -24,7 +24,7 @@ import getOptionsFromRootManifest from './getOptionsFromRootManifest'
import getPinnedVersion from './getPinnedVersion'
import getSaveType from './getSaveType'
import getNodeExecPath from './nodeExecPath'
import recursive, { createMatcher, matchDependencies } from './recursive'
import recursive, { createMatcher, matchDependencies, makeIgnorePatterns, UpdateDepsMatcher } from './recursive'
import updateToLatestSpecsFromManifest, { createLatestSpecs } from './updateToLatestSpecsFromManifest'
import { createWorkspaceSpecs, updateToWorkspacePackagesFromManifest } from './updateWorkspaceDependencies'

Expand Down Expand Up @@ -199,7 +199,18 @@ when running add/update with the --workspace option')
}
}

const updateMatch = opts.update && (params.length > 0) ? createMatcher(params) : null
let updateMatch: UpdateDepsMatcher | null
if (opts.update) {
if (params.length === 0) {
const ignoreDeps = manifest.pnpm?.updateConfig?.ignoreDependencies
if (ignoreDeps?.length) {
params = makeIgnorePatterns(ignoreDeps)
}
}
updateMatch = params.length ? createMatcher(params) : null
} else {
updateMatch = null
}
if (updateMatch != null) {
params = matchDependencies(updateMatch, manifest, includeDirect)
if (params.length === 0 && opts.depth === 0) {
Expand Down
22 changes: 19 additions & 3 deletions packages/plugin-commands-installation/src/recursive.ts
Expand Up @@ -173,8 +173,18 @@ export default async function recursive (
optionalDependencies: true,
}

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

let updateMatch: UpdateDepsMatcher | null
if (cmdFullName === 'update') {
if (params.length === 0) {
const ignoreDeps = manifestsByPath[opts.workspaceDir]?.manifest?.pnpm?.updateConfig?.ignoreDependencies
if (ignoreDeps?.length) {
params = makeIgnorePatterns(ignoreDeps)
}
}
updateMatch = params.length ? createMatcher(params) : null
} else {
updateMatch = null
}
// For a workspace with shared lockfile
if (opts.lockfileDir && ['add', 'install', 'remove', 'update', 'import'].includes(cmdFullName)) {
let importers = await getImporters()
Expand Down Expand Up @@ -499,7 +509,9 @@ export function matchDependencies (
return matchedDeps
}

export function createMatcher (params: string[]) {
export type UpdateDepsMatcher = (input: string) => string | null

export function createMatcher (params: string[]): UpdateDepsMatcher {
const patterns: string[] = []
const specs: string[] = []
for (const param of params) {
Expand All @@ -519,3 +531,7 @@ export function createMatcher (params: string[]) {
return specs[index]
}
}

export function makeIgnorePatterns (ignoredDependencies: string[]): string[] {
return ignoredDependencies.map(depName => `!${depName}`)
}
89 changes: 89 additions & 0 deletions packages/plugin-commands-installation/test/update/update.ts
Expand Up @@ -224,3 +224,92 @@ 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 > updateConfig.ignoreDependencies fields in update command', async () => {
await addDistTag({ package: '@pnpm.e2e/foo', version: '100.0.0', distTag: 'latest' })
await addDistTag({ package: '@pnpm.e2e/bar', version: '100.0.0', distTag: 'latest' })

const project = prepare({
dependencies: {
'@pnpm.e2e/foo': '100.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/100.0.0']).toBeTruthy()
expect(lockfile.packages['/@pnpm.e2e/bar/100.0.0']).toBeTruthy()

await addDistTag({ package: '@pnpm.e2e/foo', version: '100.1.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
latest: true,
})

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()
})

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: '100.0.0', distTag: 'latest' })
await addDistTag({ package: '@pnpm.e2e/bar', version: '100.0.0', distTag: 'latest' })

const project = prepare({
dependencies: {
'@pnpm.e2e/foo': '100.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/100.0.0']).toBeTruthy()
expect(lockfile.packages['/@pnpm.e2e/bar/100.0.0']).toBeTruthy()

await addDistTag({ package: '@pnpm.e2e/foo', version: '100.1.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.1.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