mirror of
https://github.com/pnpm/pnpm.git
synced 2026-05-12 10:11:42 -04:00
fix: render peer dependency issues on strict error (#11450)
Fixes #11439. When `strictPeerDependencies: true` causes `ERR_PNPM_PEER_DEP_ISSUES`, the peer dependency issues are again rendered inline — using the **same format as `pnpm peers check`** — so users (and CI tools like Renovate) can see what failed without running another command. The non-strict warning path is unchanged: it still emits the short "Run `pnpm peers check`" hint. ### Behavior `strictPeerDependencies: true`: ``` ERR_PNPM_PEER_DEP_ISSUES Unmet peer dependencies ✕ unmet peer react Installed: 17.0.2 Wanted: ^18.2.0: react-dom@18.2.0 hint: To disable failing on peer dependency issues, add the following to pnpm-workspace.yaml in your project root: strictPeerDependencies: false ``` `strictPeerDependencies: false` (unchanged): ``` WARN Issues with peer dependencies found. Run "pnpm peers check" to list them. ``` ### Implementation - Added a new `@pnpm/deps.inspection.peers-issues-renderer` package at `deps/inspection/peers-issues-renderer/`, alongside its data producer `@pnpm/deps.inspection.peers-checker`. It exposes a single `renderPeerIssues()` that emits the flat issue list previously inlined in `pnpm peers check`. - Removed the duplicated formatter from `deps/inspection/commands/src/peers.ts` and made the `pnpm peers check` command consume the new renderer. - `cli/default-reporter/src/reportError.ts`: `reportPeerDependencyIssuesError` now calls the shared `renderPeerIssues()` and prefixes the hint block with the rendered output. Tests strip ANSI escapes before substring assertions so they stay correct under `FORCE_COLOR=1`. Result: a single renderer is shared between the install error and the `pnpm peers check` command — output is identical between the two paths.
This commit is contained in:
8
.changeset/render-peer-issues-on-strict-error.md
Normal file
8
.changeset/render-peer-issues-on-strict-error.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
"@pnpm/deps.inspection.peers-issues-renderer": minor
|
||||
"@pnpm/deps.inspection.commands": patch
|
||||
"@pnpm/cli.default-reporter": patch
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
When `strictPeerDependencies` is `true`, the `ERR_PNPM_PEER_DEP_ISSUES` error once again renders the peer dependency issues inline using the same format as `pnpm peers check`, so users (and CI tools like Renovate) can see what failed without running `pnpm peers check` separately [#11439](https://github.com/pnpm/pnpm/issues/11439).
|
||||
@@ -36,6 +36,7 @@
|
||||
"@pnpm/cli.meta": "workspace:*",
|
||||
"@pnpm/config.reader": "workspace:*",
|
||||
"@pnpm/core-loggers": "workspace:*",
|
||||
"@pnpm/deps.inspection.peers-issues-renderer": "workspace:*",
|
||||
"@pnpm/error": "workspace:*",
|
||||
"@pnpm/installing.dedupe.issues-renderer": "workspace:*",
|
||||
"@pnpm/installing.dedupe.types": "workspace:*",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Log } from '@pnpm/core-loggers'
|
||||
import { renderPeerIssues } from '@pnpm/deps.inspection.peers-issues-renderer'
|
||||
import type { PnpmError } from '@pnpm/error'
|
||||
import { renderDedupeCheckIssues } from '@pnpm/installing.dedupe.issues-renderer'
|
||||
import type { DedupeCheckIssues } from '@pnpm/installing.dedupe.types'
|
||||
@@ -461,9 +462,7 @@ function reportPeerDependencyIssuesError (
|
||||
msg: { issuesByProjects: PeerDependencyIssuesByProjects }
|
||||
): ErrorInfo {
|
||||
const hasMissingPeers = getHasMissingPeers(msg.issuesByProjects)
|
||||
const hints: string[] = [
|
||||
'Run "pnpm peers check" to list the peer dependency issues.',
|
||||
]
|
||||
const hints: string[] = []
|
||||
if (hasMissingPeers) {
|
||||
hints.push(`To auto-install peer dependencies, add the following to "pnpm-workspace.yaml" in your project root:
|
||||
|
||||
@@ -471,11 +470,12 @@ function reportPeerDependencyIssuesError (
|
||||
}
|
||||
hints.push(`To disable failing on peer dependency issues, add the following to pnpm-workspace.yaml in your project root:
|
||||
|
||||
strictPeerDependencies: false
|
||||
`)
|
||||
strictPeerDependencies: false`)
|
||||
const formattedHints = hints.map((hint) => `hint: ${hint}`).join('\n')
|
||||
const rendered = renderPeerIssues(msg.issuesByProjects)
|
||||
return {
|
||||
title: err.message,
|
||||
body: hints.map((hint) => `hint: ${hint}`).join('\n'),
|
||||
body: rendered ? `${rendered}\n${formattedHints}` : formattedHints,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { stripVTControlCharacters as stripAnsi } from 'node:util'
|
||||
|
||||
import { expect, test } from '@jest/globals'
|
||||
import { toOutput$ } from '@pnpm/cli.default-reporter'
|
||||
import { peerDependencyIssuesLogger } from '@pnpm/core-loggers'
|
||||
@@ -44,7 +46,7 @@ test('print peer dependency issues warning', async () => {
|
||||
expect.assertions(1)
|
||||
|
||||
const output = await firstValueFrom(output$)
|
||||
expect(output).toContain('pnpm peers check')
|
||||
expect(stripAnsi(output)).toContain('pnpm peers check')
|
||||
})
|
||||
|
||||
test('print peer dependency issues error', async () => {
|
||||
@@ -81,8 +83,10 @@ test('print peer dependency issues error', async () => {
|
||||
})
|
||||
logger.error(err, err)
|
||||
|
||||
expect.assertions(1)
|
||||
expect.assertions(3)
|
||||
|
||||
const output = await firstValueFrom(output$)
|
||||
expect(output).toContain('pnpm peers check')
|
||||
const output = stripAnsi(await firstValueFrom(output$))
|
||||
expect(output).toContain('unmet peer a')
|
||||
expect(output).toContain('Installed: 2')
|
||||
expect(output).toContain('b@1.0.0')
|
||||
})
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
{
|
||||
"path": "../../core/types"
|
||||
},
|
||||
{
|
||||
"path": "../../deps/inspection/peers-issues-renderer"
|
||||
},
|
||||
{
|
||||
"path": "../../installing/dedupe/issues-renderer"
|
||||
},
|
||||
|
||||
1
deps/inspection/commands/package.json
vendored
1
deps/inspection/commands/package.json
vendored
@@ -41,6 +41,7 @@
|
||||
"@pnpm/deps.inspection.list": "workspace:*",
|
||||
"@pnpm/deps.inspection.outdated": "workspace:*",
|
||||
"@pnpm/deps.inspection.peers-checker": "workspace:*",
|
||||
"@pnpm/deps.inspection.peers-issues-renderer": "workspace:*",
|
||||
"@pnpm/error": "workspace:*",
|
||||
"@pnpm/global.commands": "workspace:*",
|
||||
"@pnpm/global.packages": "workspace:*",
|
||||
|
||||
56
deps/inspection/commands/src/peers.ts
vendored
56
deps/inspection/commands/src/peers.ts
vendored
@@ -2,8 +2,8 @@ import { FILTERING, UNIVERSAL_OPTIONS } from '@pnpm/cli.common-cli-options-help'
|
||||
import { docsUrl } from '@pnpm/cli.utils'
|
||||
import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader'
|
||||
import { checkPeerDependencies } from '@pnpm/deps.inspection.peers-checker'
|
||||
import { renderPeerIssues } from '@pnpm/deps.inspection.peers-issues-renderer'
|
||||
import type { PeerDependencyIssuesByProjects } from '@pnpm/types'
|
||||
import chalk from 'chalk'
|
||||
import { isEmpty, pick } from 'ramda'
|
||||
import { renderHelp } from 'render-help'
|
||||
|
||||
@@ -117,7 +117,7 @@ async function checkCmd (
|
||||
}
|
||||
|
||||
return {
|
||||
output: renderPeerIssuesFlat(issues),
|
||||
output: `Issues with peer dependencies found\n\n${renderPeerIssues(issues)}`,
|
||||
exitCode: 1,
|
||||
}
|
||||
}
|
||||
@@ -129,55 +129,3 @@ function hasNoIssues (issues: PeerDependencyIssuesByProjects): boolean {
|
||||
isEmpty(projectIssues.missing)
|
||||
)
|
||||
}
|
||||
|
||||
function renderPeerIssuesFlat (issuesByProjects: PeerDependencyIssuesByProjects): string {
|
||||
const sections: string[] = []
|
||||
|
||||
for (const [, { bad, missing, conflicts, intersections }] of Object.entries(issuesByProjects)) {
|
||||
for (const [peerName, issues] of Object.entries(bad)) {
|
||||
const foundVersion = issues[0].foundVersion
|
||||
const header = `${chalk.yellowBright('✕ unmet peer')} ${chalk.bold(peerName)}`
|
||||
const installed = ` ${chalk.cyan('Installed:')} ${chalk.dim(foundVersion)}`
|
||||
sections.push(`${header}\n${installed}\n${formatRequiredBy(issues)}`)
|
||||
}
|
||||
|
||||
for (const [peerName, issues] of Object.entries(missing)) {
|
||||
if (!intersections[peerName] && !conflicts.includes(peerName)) continue
|
||||
const conflict = conflicts.includes(peerName)
|
||||
const header = conflict
|
||||
? `${chalk.red('✕ conflicting peer')} ${chalk.bold(peerName)}`
|
||||
: `${chalk.red('✕ missing peer')} ${chalk.bold(peerName)}`
|
||||
sections.push(`${header}\n${formatRequiredBy(issues)}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (sections.length === 0) return ''
|
||||
return `Issues with peer dependencies found\n\n${sections.join('\n\n')}`
|
||||
}
|
||||
|
||||
function formatRequiredBy (issues: Array<{ parents: Array<{ name: string, version: string }>, wantedRange: string }>): string {
|
||||
const byRange = new Map<string, Set<string>>()
|
||||
for (const issue of issues) {
|
||||
const declaring = issue.parents[issue.parents.length - 1]
|
||||
const pkg = `${declaring.name}@${declaring.version}`
|
||||
if (!byRange.has(issue.wantedRange)) {
|
||||
byRange.set(issue.wantedRange, new Set())
|
||||
}
|
||||
byRange.get(issue.wantedRange)!.add(pkg)
|
||||
}
|
||||
const lines: string[] = [` ${chalk.cyan('Wanted:')}`]
|
||||
for (const [range, pkgs] of byRange) {
|
||||
lines.push(` ${chalk.cyanBright(formatRange(range))}${chalk.cyan(':')}`)
|
||||
for (const pkg of pkgs) {
|
||||
lines.push(` ${chalk.dim(pkg)}`)
|
||||
}
|
||||
}
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
function formatRange (range: string): string {
|
||||
if (range.includes(' ') || range === '*') {
|
||||
return `"${range}"`
|
||||
}
|
||||
return range
|
||||
}
|
||||
|
||||
3
deps/inspection/commands/tsconfig.json
vendored
3
deps/inspection/commands/tsconfig.json
vendored
@@ -89,6 +89,9 @@
|
||||
},
|
||||
{
|
||||
"path": "../peers-checker"
|
||||
},
|
||||
{
|
||||
"path": "../peers-issues-renderer"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
13
deps/inspection/peers-issues-renderer/README.md
vendored
Normal file
13
deps/inspection/peers-issues-renderer/README.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# @pnpm/deps.inspection.peers-issues-renderer
|
||||
|
||||
> Visualizes peer dependency issues
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
pnpm add @pnpm/deps.inspection.peers-issues-renderer
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
47
deps/inspection/peers-issues-renderer/package.json
vendored
Normal file
47
deps/inspection/peers-issues-renderer/package.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "@pnpm/deps.inspection.peers-issues-renderer",
|
||||
"version": "1100.0.0-0",
|
||||
"description": "Visualizes peer dependency issues",
|
||||
"keywords": [
|
||||
"pnpm",
|
||||
"pnpm11"
|
||||
],
|
||||
"license": "MIT",
|
||||
"funding": "https://opencollective.com/pnpm",
|
||||
"repository": "https://github.com/pnpm/pnpm/tree/main/deps/inspection/peers-issues-renderer",
|
||||
"homepage": "https://github.com/pnpm/pnpm/tree/main/deps/inspection/peers-issues-renderer#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/pnpm/pnpm/issues"
|
||||
},
|
||||
"type": "module",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"exports": {
|
||||
".": "./lib/index.js"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"!*.map"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "pn compile && pn .test",
|
||||
"lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
"prepublishOnly": "tsgo --build",
|
||||
"compile": "tsgo --build && pn lint --fix",
|
||||
".test": "cross-env NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169\" jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pnpm/types": "workspace:*",
|
||||
"chalk": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "catalog:",
|
||||
"@pnpm/deps.inspection.peers-issues-renderer": "workspace:*"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.13"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "@pnpm/jest-config"
|
||||
}
|
||||
}
|
||||
68
deps/inspection/peers-issues-renderer/src/index.ts
vendored
Normal file
68
deps/inspection/peers-issues-renderer/src/index.ts
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
import type { BadPeerDependencyIssue, PeerDependencyIssuesByProjects } from '@pnpm/types'
|
||||
import chalk from 'chalk'
|
||||
|
||||
export function renderPeerIssues (issuesByProjects: PeerDependencyIssuesByProjects): string {
|
||||
const sections: string[] = []
|
||||
|
||||
for (const [, { bad, missing, conflicts, intersections }] of Object.entries(issuesByProjects)) {
|
||||
for (const [peerName, issues] of Object.entries(bad)) {
|
||||
const header = `${chalk.yellowBright('✕ unmet peer')} ${chalk.bold(peerName)}`
|
||||
for (const [foundVersion, group] of groupByFoundVersion(issues)) {
|
||||
const installed = ` ${chalk.cyan('Installed:')} ${chalk.dim(foundVersion)}`
|
||||
sections.push(`${header}\n${installed}\n${formatRequiredBy(group)}`)
|
||||
}
|
||||
}
|
||||
|
||||
for (const [peerName, issues] of Object.entries(missing)) {
|
||||
if (!intersections[peerName] && !conflicts.includes(peerName)) continue
|
||||
const conflict = conflicts.includes(peerName)
|
||||
const header = conflict
|
||||
? `${chalk.red('✕ conflicting peer')} ${chalk.bold(peerName)}`
|
||||
: `${chalk.red('✕ missing peer')} ${chalk.bold(peerName)}`
|
||||
sections.push(`${header}\n${formatRequiredBy(issues)}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (sections.length === 0) return ''
|
||||
return sections.join('\n\n')
|
||||
}
|
||||
|
||||
function formatRequiredBy (issues: Array<{ parents: Array<{ name: string, version: string }>, wantedRange: string }>): string {
|
||||
const byRange = new Map<string, Set<string>>()
|
||||
for (const issue of issues) {
|
||||
const declaring = issue.parents[issue.parents.length - 1]
|
||||
const pkg = declaring ? `${declaring.name}@${declaring.version}` : '<unknown>'
|
||||
if (!byRange.has(issue.wantedRange)) {
|
||||
byRange.set(issue.wantedRange, new Set())
|
||||
}
|
||||
byRange.get(issue.wantedRange)!.add(pkg)
|
||||
}
|
||||
const lines: string[] = [` ${chalk.cyan('Wanted:')}`]
|
||||
for (const [range, pkgs] of byRange) {
|
||||
lines.push(` ${chalk.cyanBright(formatRange(range))}${chalk.cyan(':')}`)
|
||||
for (const pkg of pkgs) {
|
||||
lines.push(` ${chalk.dim(pkg)}`)
|
||||
}
|
||||
}
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
function formatRange (range: string): string {
|
||||
if (range.includes(' ') || range === '*') {
|
||||
return `"${range}"`
|
||||
}
|
||||
return range
|
||||
}
|
||||
|
||||
function groupByFoundVersion (issues: BadPeerDependencyIssue[]): Map<string, BadPeerDependencyIssue[]> {
|
||||
const groups = new Map<string, BadPeerDependencyIssue[]>()
|
||||
for (const issue of issues) {
|
||||
const list = groups.get(issue.foundVersion)
|
||||
if (list) {
|
||||
list.push(issue)
|
||||
} else {
|
||||
groups.set(issue.foundVersion, [issue])
|
||||
}
|
||||
}
|
||||
return groups
|
||||
}
|
||||
58
deps/inspection/peers-issues-renderer/test/__snapshots__/index.ts.snap
vendored
Normal file
58
deps/inspection/peers-issues-renderer/test/__snapshots__/index.ts.snap
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`renderPeerIssues() formats version ranges with spaces or "*" with quotes 1`] = `
|
||||
"✕ missing peer a
|
||||
Wanted:
|
||||
"*":
|
||||
z@1.0.0
|
||||
|
||||
✕ missing peer b
|
||||
Wanted:
|
||||
"1 || 2":
|
||||
z@1.0.0"
|
||||
`;
|
||||
|
||||
exports[`renderPeerIssues() handles missing parents gracefully 1`] = `
|
||||
"✕ missing peer foo
|
||||
Wanted:
|
||||
">=1.0.0 <3.0.0":
|
||||
<unknown>"
|
||||
`;
|
||||
|
||||
exports[`renderPeerIssues() renders bad peer dependencies 1`] = `
|
||||
"✕ unmet peer a
|
||||
Installed: 2
|
||||
Wanted:
|
||||
3:
|
||||
b@1.0.0"
|
||||
`;
|
||||
|
||||
exports[`renderPeerIssues() renders conflicting peer dependencies 1`] = `
|
||||
"✕ conflicting peer a
|
||||
Wanted:
|
||||
^1.0.0:
|
||||
b@1.0.0
|
||||
^2.0.0:
|
||||
c@1.0.0"
|
||||
`;
|
||||
|
||||
exports[`renderPeerIssues() renders missing peer dependencies that are required 1`] = `
|
||||
"✕ missing peer a
|
||||
Wanted:
|
||||
^1.0.0:
|
||||
b@1.0.0"
|
||||
`;
|
||||
|
||||
exports[`renderPeerIssues() splits bad peer dependencies by foundVersion 1`] = `
|
||||
"✕ unmet peer a
|
||||
Installed: 1.0.0
|
||||
Wanted:
|
||||
^2.0.0:
|
||||
b@1.0.0
|
||||
|
||||
✕ unmet peer a
|
||||
Installed: 2.0.0
|
||||
Wanted:
|
||||
^3.0.0:
|
||||
c@1.0.0"
|
||||
`;
|
||||
156
deps/inspection/peers-issues-renderer/test/index.ts
vendored
Normal file
156
deps/inspection/peers-issues-renderer/test/index.ts
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
import { stripVTControlCharacters as stripAnsi } from 'node:util'
|
||||
|
||||
import { expect, test } from '@jest/globals'
|
||||
import { renderPeerIssues } from '@pnpm/deps.inspection.peers-issues-renderer'
|
||||
|
||||
test('renderPeerIssues() returns an empty string when there are no issues', () => {
|
||||
expect(renderPeerIssues({
|
||||
'.': {
|
||||
missing: {},
|
||||
bad: {},
|
||||
conflicts: [],
|
||||
intersections: {},
|
||||
},
|
||||
})).toBe('')
|
||||
})
|
||||
|
||||
test('renderPeerIssues() renders bad peer dependencies', () => {
|
||||
expect(stripAnsi(renderPeerIssues({
|
||||
'.': {
|
||||
missing: {},
|
||||
bad: {
|
||||
a: [
|
||||
{
|
||||
parents: [
|
||||
{ name: 'b', version: '1.0.0' },
|
||||
],
|
||||
foundVersion: '2',
|
||||
resolvedFrom: [],
|
||||
optional: false,
|
||||
wantedRange: '3',
|
||||
},
|
||||
],
|
||||
},
|
||||
conflicts: [],
|
||||
intersections: {},
|
||||
},
|
||||
}))).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('renderPeerIssues() splits bad peer dependencies by foundVersion', () => {
|
||||
expect(stripAnsi(renderPeerIssues({
|
||||
'.': {
|
||||
missing: {},
|
||||
bad: {
|
||||
a: [
|
||||
{
|
||||
parents: [{ name: 'b', version: '1.0.0' }],
|
||||
foundVersion: '1.0.0',
|
||||
resolvedFrom: [],
|
||||
optional: false,
|
||||
wantedRange: '^2.0.0',
|
||||
},
|
||||
{
|
||||
parents: [{ name: 'c', version: '1.0.0' }],
|
||||
foundVersion: '2.0.0',
|
||||
resolvedFrom: [],
|
||||
optional: false,
|
||||
wantedRange: '^3.0.0',
|
||||
},
|
||||
],
|
||||
},
|
||||
conflicts: [],
|
||||
intersections: {},
|
||||
},
|
||||
}))).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('renderPeerIssues() renders missing peer dependencies that are required', () => {
|
||||
expect(stripAnsi(renderPeerIssues({
|
||||
'.': {
|
||||
missing: {
|
||||
a: [
|
||||
{
|
||||
parents: [
|
||||
{ name: 'b', version: '1.0.0' },
|
||||
],
|
||||
optional: false,
|
||||
wantedRange: '^1.0.0',
|
||||
},
|
||||
],
|
||||
},
|
||||
bad: {},
|
||||
conflicts: [],
|
||||
intersections: { a: '^1.0.0' },
|
||||
},
|
||||
}))).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('renderPeerIssues() renders conflicting peer dependencies', () => {
|
||||
expect(stripAnsi(renderPeerIssues({
|
||||
'.': {
|
||||
missing: {
|
||||
a: [
|
||||
{
|
||||
parents: [{ name: 'b', version: '1.0.0' }],
|
||||
optional: false,
|
||||
wantedRange: '^1.0.0',
|
||||
},
|
||||
{
|
||||
parents: [{ name: 'c', version: '1.0.0' }],
|
||||
optional: false,
|
||||
wantedRange: '^2.0.0',
|
||||
},
|
||||
],
|
||||
},
|
||||
bad: {},
|
||||
conflicts: ['a'],
|
||||
intersections: {},
|
||||
},
|
||||
}))).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('renderPeerIssues() formats version ranges with spaces or "*" with quotes', () => {
|
||||
expect(stripAnsi(renderPeerIssues({
|
||||
'.': {
|
||||
missing: {
|
||||
a: [
|
||||
{
|
||||
parents: [{ name: 'z', version: '1.0.0' }],
|
||||
optional: false,
|
||||
wantedRange: '*',
|
||||
},
|
||||
],
|
||||
b: [
|
||||
{
|
||||
parents: [{ name: 'z', version: '1.0.0' }],
|
||||
optional: false,
|
||||
wantedRange: '1 || 2',
|
||||
},
|
||||
],
|
||||
},
|
||||
bad: {},
|
||||
conflicts: [],
|
||||
intersections: { a: '*', b: '1 || 2' },
|
||||
},
|
||||
}))).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('renderPeerIssues() handles missing parents gracefully', () => {
|
||||
expect(stripAnsi(renderPeerIssues({
|
||||
'.': {
|
||||
missing: {
|
||||
foo: [
|
||||
{
|
||||
parents: [],
|
||||
optional: false,
|
||||
wantedRange: '>=1.0.0 <3.0.0',
|
||||
},
|
||||
],
|
||||
},
|
||||
bad: {},
|
||||
conflicts: [],
|
||||
intersections: { foo: '^1.0.0' },
|
||||
},
|
||||
}))).toMatchSnapshot()
|
||||
})
|
||||
18
deps/inspection/peers-issues-renderer/test/tsconfig.json
vendored
Normal file
18
deps/inspection/peers-issues-renderer/test/tsconfig.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": false,
|
||||
"outDir": "../node_modules/.test.lib",
|
||||
"rootDir": "..",
|
||||
"isolatedModules": true
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"../../../../__typings__/**/*.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": ".."
|
||||
}
|
||||
]
|
||||
}
|
||||
16
deps/inspection/peers-issues-renderer/tsconfig.json
vendored
Normal file
16
deps/inspection/peers-issues-renderer/tsconfig.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "@pnpm/tsconfig",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"../../../__typings__/**/*.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../../core/types"
|
||||
}
|
||||
]
|
||||
}
|
||||
8
deps/inspection/peers-issues-renderer/tsconfig.lint.json
vendored
Normal file
8
deps/inspection/peers-issues-renderer/tsconfig.lint.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"test/**/*.ts",
|
||||
"../../../__typings__/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
22
pnpm-lock.yaml
generated
22
pnpm-lock.yaml
generated
@@ -2245,6 +2245,9 @@ importers:
|
||||
'@pnpm/core-loggers':
|
||||
specifier: workspace:*
|
||||
version: link:../../core/core-loggers
|
||||
'@pnpm/deps.inspection.peers-issues-renderer':
|
||||
specifier: workspace:*
|
||||
version: link:../../deps/inspection/peers-issues-renderer
|
||||
'@pnpm/error':
|
||||
specifier: workspace:*
|
||||
version: link:../../core/error
|
||||
@@ -3357,6 +3360,9 @@ importers:
|
||||
'@pnpm/deps.inspection.peers-checker':
|
||||
specifier: workspace:*
|
||||
version: link:../peers-checker
|
||||
'@pnpm/deps.inspection.peers-issues-renderer':
|
||||
specifier: workspace:*
|
||||
version: link:../peers-issues-renderer
|
||||
'@pnpm/error':
|
||||
specifier: workspace:*
|
||||
version: link:../../../core/error
|
||||
@@ -3626,6 +3632,22 @@ importers:
|
||||
specifier: 'catalog:'
|
||||
version: 7.7.1
|
||||
|
||||
deps/inspection/peers-issues-renderer:
|
||||
dependencies:
|
||||
'@pnpm/types':
|
||||
specifier: workspace:*
|
||||
version: link:../../../core/types
|
||||
chalk:
|
||||
specifier: 'catalog:'
|
||||
version: 5.6.2
|
||||
devDependencies:
|
||||
'@jest/globals':
|
||||
specifier: 'catalog:'
|
||||
version: 30.3.0
|
||||
'@pnpm/deps.inspection.peers-issues-renderer':
|
||||
specifier: workspace:*
|
||||
version: 'link:'
|
||||
|
||||
deps/inspection/tree-builder:
|
||||
dependencies:
|
||||
'@pnpm/config.matcher':
|
||||
|
||||
Reference in New Issue
Block a user