@pnpm/cli.default-reporter 1100.1.1 → 1100.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -69,6 +69,7 @@ export function toOutput$(opts) {
69
69
  const deprecationPushStream = new Rx.Subject();
70
70
  const summaryPushStream = new Rx.Subject();
71
71
  const lifecyclePushStream = new Rx.Subject();
72
+ const lockfileVerificationPushStream = new Rx.Subject();
72
73
  const statsPushStream = new Rx.Subject();
73
74
  const packageImportMethodPushStream = new Rx.Subject();
74
75
  const installCheckPushStream = new Rx.Subject();
@@ -112,6 +113,9 @@ export function toOutput$(opts) {
112
113
  case 'pnpm:lifecycle':
113
114
  lifecyclePushStream.next(log);
114
115
  break;
116
+ case 'pnpm:lockfile-verification':
117
+ lockfileVerificationPushStream.next(log);
118
+ break;
115
119
  case 'pnpm:stats':
116
120
  statsPushStream.next(log);
117
121
  break;
@@ -185,6 +189,7 @@ export function toOutput$(opts) {
185
189
  ignoredScripts: Rx.from(ignoredScriptsPushStream),
186
190
  lifecycle: Rx.from(lifecyclePushStream),
187
191
  link: Rx.from(linkPushStream),
192
+ lockfileVerification: Rx.from(lockfileVerificationPushStream),
188
193
  other,
189
194
  packageImportMethod: Rx.from(packageImportMethodPushStream),
190
195
  packageManifest: Rx.from(packageManifestPushStream),
@@ -51,6 +51,11 @@ function getErrorInfo(logObj, config) {
51
51
  return { title: err.message, body: 'If you cannot fix this registry issue, then set "resolution-mode" to "highest".' };
52
52
  case 'ERR_PNPM_NO_MATCHING_VERSION':
53
53
  case 'ERR_PNPM_NO_MATURE_MATCHING_VERSION':
54
+ // ERR_PNPM_NO_MATURE_MATCHING_VERSION used to come from the resolver
55
+ // with `packageMeta` attached; it now comes from the install / dlx /
56
+ // self-update callers as a plain PnpmError once the resolver has
57
+ // surfaced the violations. `packageMeta` may be undefined, in which
58
+ // case the formatter falls back to the bare title+message.
54
59
  return formatNoMatchingVersion(err, logObj);
55
60
  case 'ERR_PNPM_RECURSIVE_FAIL':
56
61
  return formatRecursiveCommandSummary(logObj); // eslint-disable-line @typescript-eslint/no-explicit-any
@@ -94,10 +99,17 @@ ${formatPkgNameVer(pkgsStack[0])}\
94
99
  ${pkgsStack.slice(1).map((pkgInfo) => `${EOL} at ${formatPkgNameVer(pkgInfo)}`).join('')}`;
95
100
  }
96
101
  function formatNoMatchingVersion(err, msg) {
102
+ // Errors raised by the install/dlx/self-update layer after the resolver
103
+ // surfaces violations may not carry the original packageMeta. In that
104
+ // case the error message alone already names every offending entry,
105
+ // so we just echo it through without the registry-metadata appendix.
97
106
  const meta = msg.packageMeta;
107
+ if (!meta) {
108
+ return { title: err.message };
109
+ }
98
110
  const latestVersion = meta['dist-tags'].latest;
99
111
  let output = `The latest release of ${meta.name} is "${latestVersion}".`;
100
- const latestTime = msg.packageMeta.time?.[latestVersion];
112
+ const latestTime = meta.time?.[latestVersion];
101
113
  if (latestTime) {
102
114
  output += ` Published at ${stringifyDate(latestTime)}`;
103
115
  }
@@ -108,7 +120,7 @@ function formatNoMatchingVersion(err, msg) {
108
120
  if (tag !== 'latest') {
109
121
  const version = meta['dist-tags'][tag];
110
122
  output += ` * ${tag}: ${version}`;
111
- const time = msg.packageMeta.time?.[version];
123
+ const time = meta.time?.[version];
112
124
  if (time) {
113
125
  output += ` published at ${stringifyDate(time)}`;
114
126
  }
@@ -117,9 +129,6 @@ function formatNoMatchingVersion(err, msg) {
117
129
  }
118
130
  }
119
131
  output += `${EOL}If you need the full list of all ${Object.keys(meta.versions).length} published versions run "pnpm view ${meta.name} versions".`;
120
- if (msg.immatureVersion) {
121
- output += `${EOL}${EOL}If you want to install the matched version ignoring the time it was published, you can add the package name to the minimumReleaseAgeExclude setting. Read more about it: https://pnpm.io/settings#minimumreleaseageexclude`;
122
- }
123
132
  return {
124
133
  title: err.message,
125
134
  body: output,
@@ -13,6 +13,7 @@ export declare function reporterForClient(log$: {
13
13
  deprecation: Rx.Observable<logs.DeprecationLog>;
14
14
  summary: Rx.Observable<logs.SummaryLog>;
15
15
  lifecycle: Rx.Observable<logs.LifecycleLog>;
16
+ lockfileVerification: Rx.Observable<logs.LockfileVerificationLog>;
16
17
  stats: Rx.Observable<logs.StatsLog>;
17
18
  installCheck: Rx.Observable<logs.InstallCheckLog>;
18
19
  installingConfigDeps: Rx.Observable<logs.InstallingConfigDepsLog>;
@@ -8,6 +8,7 @@ import { reportIgnoredBuilds } from './reportIgnoredBuilds.js';
8
8
  import { reportInstallChecks } from './reportInstallChecks.js';
9
9
  import { reportInstallingConfigDeps } from './reportInstallingConfigDeps.js';
10
10
  import { reportLifecycleScripts } from './reportLifecycleScripts.js';
11
+ import { reportLockfileVerification } from './reportLockfileVerification.js';
11
12
  import { LOG_LEVEL_NUMBER, reportMisc } from './reportMisc.js';
12
13
  import { reportPeerDependencyIssues } from './reportPeerDependencyIssues.js';
13
14
  import { reportProgress } from './reportProgress.js';
@@ -60,7 +61,10 @@ export function reporterForClient(log$, opts) {
60
61
  hideLifecyclePrefix: opts.hideLifecyclePrefix,
61
62
  cwd,
62
63
  width,
63
- }), reportInstallChecks(log$.installCheck, { cwd }), reportInstallingConfigDeps(log$.installingConfigDeps), reportScope(log$.scope, { isRecursive: opts.isRecursive, cmd: opts.cmd }), reportSkippedOptionalDependencies(log$.skippedOptionalDependency, { cwd }), reportHooks(log$.hook, { cwd, isRecursive: opts.isRecursive }), reportUpdateCheck(log$.updateCheck, opts), reportProgress(log$, {
64
+ }), reportInstallChecks(log$.installCheck, { cwd }), reportInstallingConfigDeps(log$.installingConfigDeps), reportLockfileVerification(log$.lockfileVerification, {
65
+ cwd,
66
+ workspaceDir: opts.pnpmConfig?.workspaceDir,
67
+ }), reportScope(log$.scope, { isRecursive: opts.isRecursive, cmd: opts.cmd }), reportSkippedOptionalDependencies(log$.skippedOptionalDependency, { cwd }), reportHooks(log$.hook, { cwd, isRecursive: opts.isRecursive }), reportUpdateCheck(log$.updateCheck, opts), reportProgress(log$, {
64
68
  cwd,
65
69
  throttle,
66
70
  hideAddedPkgsProgress: opts.hideAddedPkgsProgress,
@@ -0,0 +1,15 @@
1
+ import type { LockfileVerificationLog } from '@pnpm/core-loggers';
2
+ import * as Rx from 'rxjs';
3
+ export interface ReportLockfileVerificationOptions {
4
+ cwd: string;
5
+ /**
6
+ * The workspace root, when one exists. Used as the "expected"
7
+ * location for the lockfile — when the lockfile lives there, the
8
+ * path is implied and we don't repeat it in the rendered message.
9
+ * Falls back to `cwd` for single-project installs.
10
+ */
11
+ workspaceDir?: string;
12
+ }
13
+ export declare function reportLockfileVerification(lockfileVerification$: Rx.Observable<LockfileVerificationLog>, opts: ReportLockfileVerificationOptions): Rx.Observable<Rx.Observable<{
14
+ msg: string;
15
+ }>>;
@@ -0,0 +1,56 @@
1
+ import path from 'node:path';
2
+ import chalk from 'chalk';
3
+ import normalize from 'normalize-path';
4
+ import prettyMs from 'pretty-ms';
5
+ import * as Rx from 'rxjs';
6
+ import { map } from 'rxjs/operators';
7
+ export function reportLockfileVerification(lockfileVerification$, opts) {
8
+ const expectedDir = opts.workspaceDir ?? opts.cwd;
9
+ // A single inner observable so the `done` message overwrites the
10
+ // transient `started` message in ansi-diff mode. In appendOnly mode
11
+ // both lines are printed.
12
+ return Rx.of(lockfileVerification$.pipe(map((log) => {
13
+ const path_ = formatLockfilePath(log.lockfilePath, opts.cwd, expectedDir);
14
+ const entries = `${log.entries} ${log.entries === 1 ? 'entry' : 'entries'}`;
15
+ switch (log.status) {
16
+ case 'started':
17
+ return {
18
+ msg: `${chalk.cyan('?')} Verifying lockfile${path_} against supply-chain policies (${entries})...`,
19
+ };
20
+ case 'done':
21
+ return {
22
+ msg: `${chalk.green('✓')} Lockfile${path_} passes supply-chain policies (${entries} in ${prettyMs(log.elapsedMs)})`,
23
+ };
24
+ case 'failed':
25
+ // Brief one-liner so the transient `started` frame doesn't
26
+ // stay on screen above the detailed PnpmError block that the
27
+ // error reporter prints next.
28
+ return {
29
+ msg: `${chalk.red('✗')} Lockfile${path_} failed supply-chain policy check (${entries} in ${prettyMs(log.elapsedMs)})`,
30
+ };
31
+ }
32
+ })));
33
+ }
34
+ // Returns a leading-space-prefixed `at <path>` suffix only when the
35
+ // lockfile sits outside the obvious project/workspace root — otherwise
36
+ // the path is implied and printing it would just add noise to every
37
+ // install. Empty string when the path is omitted or matches the
38
+ // expected location.
39
+ //
40
+ // Uses `path.relative` rather than a strict `===` between
41
+ // `path.dirname(lockfilePath)` and `expectedDir`: relative path
42
+ // computation normalizes slash direction and trailing separators, so
43
+ // a workspaceDir like `C:/repo/` correctly matches a lockfilePath at
44
+ // `C:\repo\pnpm-lock.yaml` on Windows. The lockfile is considered
45
+ // "inside the expected dir" when the relative path is a bare file
46
+ // name (no separator) that doesn't escape upward.
47
+ function formatLockfilePath(lockfilePath, cwd, expectedDir) {
48
+ if (lockfilePath == null)
49
+ return '';
50
+ const fromExpected = path.relative(expectedDir, lockfilePath);
51
+ const isDirectChild = !fromExpected.includes(path.sep) && !fromExpected.startsWith('..');
52
+ if (isDirectChild)
53
+ return '';
54
+ return ` at ${normalize(path.relative(cwd, lockfilePath))}`;
55
+ }
56
+ //# sourceMappingURL=reportLockfileVerification.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/cli.default-reporter",
3
- "version": "1100.1.1",
3
+ "version": "1100.2.0",
4
4
  "description": "The default reporter of pnpm",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -42,14 +42,14 @@
42
42
  "semver": "^7.7.2",
43
43
  "stacktracey": "^2.2.0",
44
44
  "string-length": "^7.0.1",
45
- "@pnpm/cli.meta": "1100.0.3",
46
- "@pnpm/core-loggers": "1100.0.2",
47
- "@pnpm/error": "1100.0.0",
45
+ "@pnpm/config.reader": "1101.3.2",
46
+ "@pnpm/core-loggers": "1100.1.0",
48
47
  "@pnpm/deps.inspection.peers-issues-renderer": "1100.0.1",
49
- "@pnpm/types": "1101.1.0",
50
- "@pnpm/config.reader": "1101.3.0",
48
+ "@pnpm/error": "1100.0.0",
49
+ "@pnpm/installing.dedupe.issues-renderer": "1100.0.1",
51
50
  "@pnpm/installing.dedupe.types": "1100.0.1",
52
- "@pnpm/installing.dedupe.issues-renderer": "1100.0.1"
51
+ "@pnpm/types": "1101.1.0",
52
+ "@pnpm/cli.meta": "1100.0.3"
53
53
  },
54
54
  "peerDependencies": {
55
55
  "@pnpm/logger": ">=1001.0.0 <1002.0.0"
@@ -62,8 +62,8 @@
62
62
  "ghooks": "2.0.4",
63
63
  "load-json-file": "^7.0.1",
64
64
  "normalize-newline": "5.0.0",
65
- "@pnpm/cli.default-reporter": "1100.1.1",
66
- "@pnpm/logger": "1100.0.0"
65
+ "@pnpm/logger": "1100.0.0",
66
+ "@pnpm/cli.default-reporter": "1100.2.0"
67
67
  },
68
68
  "engines": {
69
69
  "node": ">=22.13"