@pnpm/deps.inspection.commands 1100.1.12 → 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.
@@ -0,0 +1,8 @@
1
+ import { type Config, type ConfigContext } from '@pnpm/config.reader';
2
+ export declare function rcOptionsTypes(): Record<string, unknown>;
3
+ export declare function cliOptionsTypes(): Record<string, unknown>;
4
+ export declare const commandNames: string[];
5
+ export declare function help(): string;
6
+ export declare function handler(opts: Config & ConfigContext & {
7
+ dir: string;
8
+ }, params: string[]): Promise<void>;
@@ -0,0 +1,99 @@
1
+ import { docsUrl, readProjectManifestOnly } from '@pnpm/cli.utils';
2
+ import { types as allTypes } from '@pnpm/config.reader';
3
+ import { PnpmError } from '@pnpm/error';
4
+ import HostedGit from 'hosted-git-info';
5
+ import open from 'open';
6
+ import { pick } from 'ramda';
7
+ import { renderHelp } from 'render-help';
8
+ import { fetchPackageInfo } from '../fetchPackageInfo.js';
9
+ export function rcOptionsTypes() {
10
+ return pick(['registry'], allTypes);
11
+ }
12
+ export function cliOptionsTypes() {
13
+ return rcOptionsTypes();
14
+ }
15
+ export const commandNames = ['bugs'];
16
+ export function help() {
17
+ return renderHelp({
18
+ description: "Opens the URL of the package's bug tracker in a browser.",
19
+ url: docsUrl('bugs'),
20
+ usages: ['pnpm bugs [<pkgname> [<pkgname> ...]]'],
21
+ });
22
+ }
23
+ export async function handler(opts, params) {
24
+ const urls = params.length === 0
25
+ ? [await getBugsUrlFromCurrentProject(opts)]
26
+ : await Promise.all(params.map((spec) => getBugsUrlFromRegistry(opts, spec)));
27
+ for (const url of urls) {
28
+ // eslint-disable-next-line no-await-in-loop
29
+ await open(url);
30
+ }
31
+ }
32
+ async function getBugsUrlFromCurrentProject(opts) {
33
+ const manifest = await readProjectManifestOnly(opts.dir, {
34
+ engineStrict: opts.engineStrict,
35
+ nodeVersion: opts.nodeVersion,
36
+ supportedArchitectures: opts.supportedArchitectures,
37
+ });
38
+ const url = pickBugsUrl({ bugs: manifest.bugs, repository: manifest.repository });
39
+ if (!url) {
40
+ throw new PnpmError('NO_BUGS_URL', 'The current project does not have a bug tracker URL. Add a "bugs" or "repository" field to its manifest.');
41
+ }
42
+ return url;
43
+ }
44
+ async function getBugsUrlFromRegistry(opts, packageSpec) {
45
+ const info = await fetchPackageInfo(opts, packageSpec);
46
+ const url = pickBugsUrl({ bugs: info.bugs, repository: info.repository });
47
+ if (!url) {
48
+ throw new PnpmError('NO_BUGS_URL', `The package "${info.name}" does not have a bug tracker URL.`);
49
+ }
50
+ return url;
51
+ }
52
+ function pickBugsUrl(manifest) {
53
+ if (manifest.bugs) {
54
+ const bugsUrl = typeof manifest.bugs === 'string' ? manifest.bugs : manifest.bugs.url;
55
+ if (bugsUrl && isHttpUrl(bugsUrl))
56
+ return bugsUrl;
57
+ }
58
+ if (manifest.repository) {
59
+ const repoUrl = typeof manifest.repository === 'string' ? manifest.repository : manifest.repository.url;
60
+ if (repoUrl)
61
+ return repositoryToIssuesUrl(repoUrl);
62
+ }
63
+ return undefined;
64
+ }
65
+ function repositoryToIssuesUrl(rawUrl) {
66
+ // hosted-git-info handles GitHub/GitLab/Bitbucket/etc. shorthand and SSH forms
67
+ // (`owner/repo`, `github:owner/repo`, `git+ssh://git@github.com/owner/repo.git`,
68
+ // `git@github.com:owner/repo.git`, …) and yields the canonical bugs URL directly.
69
+ const hosted = HostedGit.fromUrl(rawUrl);
70
+ if (hosted != null) {
71
+ const url = hosted.bugs();
72
+ if (url && isHttpUrl(url))
73
+ return url;
74
+ }
75
+ // Fallback for self-hosted git servers that hosted-git-info doesn't recognize.
76
+ let parsed;
77
+ try {
78
+ parsed = new URL(rawUrl.replace(/^git\+/, ''));
79
+ }
80
+ catch {
81
+ return undefined;
82
+ }
83
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')
84
+ return undefined;
85
+ parsed.search = '';
86
+ parsed.hash = '';
87
+ parsed.pathname = parsed.pathname.replace(/\/+$/, '').replace(/\.git$/, '') + '/issues';
88
+ return parsed.toString();
89
+ }
90
+ function isHttpUrl(value) {
91
+ try {
92
+ const { protocol } = new URL(value);
93
+ return protocol === 'http:' || protocol === 'https:';
94
+ }
95
+ catch {
96
+ return false;
97
+ }
98
+ }
99
+ //# sourceMappingURL=index.js.map
package/lib/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * as bugs from './bugs/index.js';
1
2
  export * as docs from './docs/index.js';
2
3
  export { list, ll, why } from './listing/index.js';
3
4
  export { outdated } from './outdated/index.js';
package/lib/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export * as bugs from './bugs/index.js';
1
2
  export * as docs from './docs/index.js';
2
3
  export { list, ll, why } from './listing/index.js';
3
4
  export { outdated } from './outdated/index.js';
@@ -1,12 +1,12 @@
1
1
  import { stripVTControlCharacters as stripAnsi } from 'node:util';
2
2
  import { FILTERING, OPTIONS, UNIVERSAL_OPTIONS } from '@pnpm/cli.common-cli-options-help';
3
3
  import { docsUrl, readDepNameCompletions, readProjectManifestOnly, TABLE_OPTIONS, } from '@pnpm/cli.utils';
4
- import colorizeSemverDiff from '@pnpm/colorize-semver-diff';
4
+ import { colorizeSemverDiff } from '@pnpm/colorize-semver-diff';
5
5
  import { types as allTypes } from '@pnpm/config.reader';
6
6
  import { outdatedDepsOfProjects, } from '@pnpm/deps.inspection.outdated';
7
7
  import { PnpmError } from '@pnpm/error';
8
8
  import { scanGlobalPackages } from '@pnpm/global.packages';
9
- import semverDiff from '@pnpm/semver-diff';
9
+ import { semverDiff } from '@pnpm/semver-diff';
10
10
  import { table } from '@zkochan/table';
11
11
  import chalk from 'chalk';
12
12
  import { pick, sortWith } from 'ramda';
@@ -270,7 +270,7 @@ export function toOutdatedWithVersionDiff(outdated) {
270
270
  if (outdated.latestManifest != null) {
271
271
  return {
272
272
  ...outdated,
273
- ...semverDiff.default(outdated.wanted, outdated.latestManifest.version),
273
+ ...semverDiff(outdated.wanted, outdated.latestManifest.version),
274
274
  };
275
275
  }
276
276
  return {
@@ -300,7 +300,7 @@ export function renderLatest(outdatedPkg) {
300
300
  ? chalk.redBright.bold('Deprecated')
301
301
  : latestManifest.version;
302
302
  }
303
- const versionText = colorizeSemverDiff.default({ change, diff });
303
+ const versionText = colorizeSemverDiff({ change, diff });
304
304
  if (latestManifest.deprecated) {
305
305
  return `${versionText} ${chalk.redBright('(deprecated)')}`;
306
306
  }
package/lib/view/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { types as allTypes } from '@pnpm/config.reader';
2
2
  import { PnpmError } from '@pnpm/error';
3
+ import { formatTimeAgo } from '@pnpm/resolving.npm-resolver';
4
+ import chalk from 'chalk';
3
5
  import { pick } from 'ramda';
4
6
  import { renderHelp } from 'render-help';
5
7
  import { fetchPackageInfo } from '../fetchPackageInfo.js';
@@ -74,67 +76,74 @@ export async function handler(opts, params) {
74
76
  }
75
77
  const headerParts = [];
76
78
  if (info.name && info.version) {
77
- headerParts.push(`${info.name}@${info.version}`);
79
+ headerParts.push(chalk.cyan(`${info.name}@${info.version}`));
78
80
  }
79
81
  if (info.license) {
80
- headerParts.push(info.license);
82
+ headerParts.push(chalk.green(info.license));
81
83
  }
82
84
  if (info.depsCount !== undefined) {
83
- headerParts.push(`deps: ${info.depsCount}`);
85
+ headerParts.push(`deps: ${chalk.cyan(info.depsCount)}`);
84
86
  }
85
87
  else {
86
88
  headerParts.push('deps: none');
87
89
  }
88
90
  if (info.versionsCount !== undefined) {
89
- headerParts.push(`versions: ${info.versionsCount}`);
91
+ headerParts.push(`versions: ${chalk.cyan(info.versionsCount)}`);
90
92
  }
91
93
  const lines = [headerParts.join(' | ')];
92
94
  if (info.description) {
93
95
  lines.push(info.description);
94
96
  }
95
97
  if (info.homepage) {
96
- lines.push(info.homepage);
98
+ lines.push(chalk.underline.blue(info.homepage));
97
99
  }
98
100
  if (info.keywords && info.keywords.length > 0) {
99
101
  lines.push('');
100
- lines.push(`keywords: ${info.keywords.join(', ')}`);
101
- }
102
- if (info.dependencies && Object.keys(info.dependencies).length > 0) {
103
- lines.push('');
104
- lines.push('dependencies:');
105
- const depEntries = Object.entries(info.dependencies).map(([name, version]) => `${name}: ${version}`);
106
- lines.push(depEntries.join(', '));
102
+ lines.push(`keywords: ${chalk.cyan(info.keywords.join(', '))}`);
107
103
  }
108
104
  if (info.dist) {
109
105
  lines.push('');
110
- lines.push('dist');
106
+ lines.push(chalk.bold('dist'));
111
107
  if (info.dist.tarball) {
112
- lines.push(`.tarball: ${info.dist.tarball}`);
108
+ lines.push(`.tarball: ${chalk.underline.blue(info.dist.tarball)}`);
113
109
  }
114
110
  if (info.dist.shasum) {
115
- lines.push(`.shasum: ${info.dist.shasum}`);
111
+ lines.push(`.shasum: ${chalk.green(info.dist.shasum)}`);
116
112
  }
117
113
  if (info.dist.integrity) {
118
- lines.push(`.integrity: ${info.dist.integrity}`);
114
+ lines.push(`.integrity: ${chalk.green(info.dist.integrity)}`);
119
115
  }
120
116
  if (info.dist.unpackedSize != null) {
121
- lines.push(`.unpackedSize: ${formatBytes(info.dist.unpackedSize)}`);
117
+ lines.push(`.unpackedSize: ${chalk.blue(formatBytes(info.dist.unpackedSize))}`);
122
118
  }
123
119
  }
120
+ if (info.dependencies && Object.keys(info.dependencies).length > 0) {
121
+ lines.push('');
122
+ lines.push('dependencies:');
123
+ const depEntries = Object.entries(info.dependencies).map(([name, version]) => `${chalk.blue(name)}: ${version}`);
124
+ lines.push(depEntries.join(', '));
125
+ }
124
126
  if (info.maintainers && info.maintainers.length > 0) {
125
127
  lines.push('');
126
128
  lines.push('maintainers:');
127
129
  for (const maintainer of info.maintainers) {
128
- lines.push(`- ${maintainer.name}`);
130
+ const email = maintainer.email;
131
+ const name = email ? `${chalk.blue(maintainer.name)} <${chalk.dim(email)}>` : chalk.blue(maintainer.name);
132
+ lines.push(`- ${name}`);
129
133
  }
130
134
  }
131
135
  if (info.distTags && Object.keys(info.distTags).length > 0) {
132
136
  lines.push('');
133
- lines.push('dist-tags:');
137
+ lines.push(chalk.bold('dist-tags:'));
134
138
  for (const [tag, tagVersion] of Object.entries(info.distTags)) {
135
- lines.push(`${tag}: ${tagVersion}`);
139
+ lines.push(`${chalk.blue(tag)}: ${tagVersion}`);
136
140
  }
137
141
  }
142
+ const publishedInfo = getPublishedInfo(info);
143
+ if (publishedInfo) {
144
+ lines.push('');
145
+ lines.push(publishedInfo);
146
+ }
138
147
  return lines.join('\n');
139
148
  }
140
149
  function formatBytes(bytes) {
@@ -162,4 +171,47 @@ function formatFieldValue(value) {
162
171
  }
163
172
  return String(value);
164
173
  }
174
+ function getPublishedInfo(info) {
175
+ if (!info.version || !info.time) {
176
+ return null;
177
+ }
178
+ const publishedTime = info.time[info.version];
179
+ if (!publishedTime) {
180
+ return null;
181
+ }
182
+ const publishedDate = new Date(publishedTime);
183
+ if (isNaN(publishedDate.getTime())) {
184
+ return null;
185
+ }
186
+ const timeAgo = formatTimeAgo(publishedDate) ?? 'just now';
187
+ const publisher = getPublisher(info);
188
+ if (publisher) {
189
+ return `published ${chalk.cyan(timeAgo)} by ${publisher}`;
190
+ }
191
+ return `published ${chalk.cyan(timeAgo)}`;
192
+ }
193
+ /**
194
+ * Retrieves the publisher name from package metadata.
195
+ * Checks fields in order: _npmUser, maintainers, author.
196
+ * Returns null if no publisher information is available.
197
+ */
198
+ function getPublisher(info) {
199
+ if (info._npmUser?.name) {
200
+ const email = info._npmUser.email;
201
+ return email ? `${chalk.blue(info._npmUser.name)} <${chalk.dim(email)}>` : chalk.blue(info._npmUser.name);
202
+ }
203
+ if (info.maintainers && info.maintainers.length > 0) {
204
+ const first = info.maintainers[0];
205
+ const email = first.email;
206
+ const name = email ? `${chalk.blue(first.name)} <${chalk.dim(email)}>` : chalk.blue(first.name);
207
+ if (info.maintainers.length === 1) {
208
+ return name;
209
+ }
210
+ return `${name} et al.`;
211
+ }
212
+ if (info.author) {
213
+ return String(info.author);
214
+ }
215
+ return null;
216
+ }
165
217
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pnpm/deps.inspection.commands",
3
- "version": "1100.1.12",
3
+ "version": "1100.2.0",
4
4
  "description": "The list, ll, why, and outdated commands of pnpm",
5
5
  "keywords": [
6
6
  "pnpm",
@@ -24,55 +24,57 @@
24
24
  "!*.map"
25
25
  ],
26
26
  "dependencies": {
27
- "@pnpm/colorize-semver-diff": "^1.0.1",
27
+ "@pnpm/colorize-semver-diff": "^2.0.0",
28
28
  "@pnpm/npm-package-arg": "^2.0.0",
29
- "@pnpm/semver-diff": "^1.1.0",
29
+ "@pnpm/semver-diff": "^2.0.0",
30
30
  "@zkochan/table": "^2.0.1",
31
- "chalk": "^5.6.2",
31
+ "chalk": "^5.6.0",
32
+ "hosted-git-info": "npm:@pnpm/hosted-git-info@1.0.0",
32
33
  "open": "^7.4.2",
33
34
  "ramda": "npm:@pnpm/ramda@0.28.1",
34
35
  "render-help": "^2.0.0",
35
- "@pnpm/cli.common-cli-options-help": "1100.0.1",
36
36
  "@pnpm/cli.command": "1100.0.1",
37
- "@pnpm/cli.utils": "1101.0.2",
37
+ "@pnpm/cli.utils": "1101.0.3",
38
+ "@pnpm/cli.common-cli-options-help": "1100.0.1",
39
+ "@pnpm/config.pick-registry-for-package": "1100.0.3",
40
+ "@pnpm/config.reader": "1101.3.0",
38
41
  "@pnpm/config.matcher": "1100.0.1",
39
- "@pnpm/config.reader": "1101.2.1",
40
- "@pnpm/config.pick-registry-for-package": "1100.0.2",
41
- "@pnpm/deps.inspection.list": "1100.0.7",
42
- "@pnpm/deps.inspection.outdated": "1100.0.11",
43
- "@pnpm/deps.inspection.peers-issues-renderer": "1100.0.0",
44
- "@pnpm/deps.inspection.peers-checker": "1100.0.6",
42
+ "@pnpm/deps.inspection.outdated": "1100.0.13",
43
+ "@pnpm/deps.inspection.list": "1100.0.9",
44
+ "@pnpm/deps.inspection.peers-checker": "1100.0.7",
45
+ "@pnpm/deps.inspection.peers-issues-renderer": "1100.0.1",
45
46
  "@pnpm/error": "1100.0.0",
46
- "@pnpm/global.commands": "1100.0.13",
47
- "@pnpm/global.packages": "1100.0.2",
48
- "@pnpm/installing.modules-yaml": "1100.0.3",
49
- "@pnpm/lockfile.fs": "1100.0.6",
50
- "@pnpm/resolving.default-resolver": "1100.0.10",
51
- "@pnpm/resolving.npm-resolver": "1101.0.2",
52
- "@pnpm/resolving.registry.types": "1100.0.2",
47
+ "@pnpm/global.commands": "1100.0.15",
48
+ "@pnpm/global.packages": "1100.0.3",
49
+ "@pnpm/installing.modules-yaml": "1100.0.4",
50
+ "@pnpm/lockfile.fs": "1100.0.7",
51
+ "@pnpm/network.auth-header": "1100.0.2",
52
+ "@pnpm/network.fetch": "1100.0.3",
53
+ "@pnpm/resolving.default-resolver": "1100.1.0",
53
54
  "@pnpm/store.path": "1100.0.1",
54
- "@pnpm/types": "1101.0.0",
55
- "@pnpm/network.fetch": "1100.0.2",
56
- "@pnpm/network.auth-header": "1100.0.1"
55
+ "@pnpm/resolving.npm-resolver": "1101.1.0",
56
+ "@pnpm/resolving.registry.types": "1100.0.3",
57
+ "@pnpm/types": "1101.1.0"
57
58
  },
58
59
  "peerDependencies": {
59
- "@pnpm/logger": "^1001.0.1"
60
+ "@pnpm/logger": ">=1001.0.0 <1002.0.0"
60
61
  },
61
62
  "devDependencies": {
62
63
  "@jest/globals": "30.3.0",
63
64
  "@pnpm/registry-mock": "6.0.0",
65
+ "@types/hosted-git-info": "^3.0.5",
64
66
  "@types/ramda": "0.31.1",
65
67
  "@types/zkochan__table": "npm:@types/table@6.0.0",
66
68
  "execa": "npm:safe-execa@0.3.0",
67
69
  "symlink-dir": "^10.0.1",
68
70
  "write-yaml-file": "^6.0.0",
71
+ "@pnpm/deps.inspection.commands": "1100.2.0",
69
72
  "@pnpm/constants": "1100.0.0",
70
- "@pnpm/deps.inspection.commands": "1100.1.12",
71
- "@pnpm/installing.commands": "1100.1.11",
72
- "@pnpm/prepare": "1100.0.5",
73
- "@pnpm/test-fixtures": "1100.0.0",
73
+ "@pnpm/prepare": "1100.0.7",
74
+ "@pnpm/installing.commands": "1100.2.0",
74
75
  "@pnpm/testing.command-defaults": "1100.0.1",
75
- "@pnpm/workspace.projects-filter": "1100.0.8"
76
+ "@pnpm/workspace.projects-filter": "1100.0.10",
77
+ "@pnpm/test-fixtures": "1100.0.0"
76
78
  },
77
79
  "engines": {
78
80
  "node": ">=22.13"