mirror of
https://github.com/pnpm/pnpm.git
synced 2026-04-28 11:01:30 -04:00
feat: add native view/info/show/v command (#11064)
* feat: add native view/info command * test: add unit tests for native view command * fix(view): support ranges, aliases, and tags * chore: update lockfile and tsconfig * refactor(view): reuse pickPackageFromMeta from npm-resolver - Share version resolution logic with the npm-resolver instead of reimplementing tag/range/version matching in the view command. - Export pickPackageFromMeta and pickVersionByVersionRange from @pnpm/resolving.npm-resolver. - Remove redundant double HTTP fetch (metadata already contains all version data). - Remove duplicate author/repository fields from PackageInRegistry (already inherited from BaseManifest). - Consolidate four changesets into one. - Revert unrelated .gitignore change. - Drop direct semver dependency from deps.inspection.commands. * refactor(view): reuse fetchMetadataFromFromRegistry from npm-resolver Use the npm-resolver's fetchMetadataFromFromRegistry instead of hand-rolled fetch logic. This fixes: - Broken URL encoding for scoped packages (@scope/pkg) - Missing auth header, proxy, SSL, and retry config - Duplicated fetch + error handling code Also pass proper Config options (rawConfig, userAgent, SSL, proxy, retry, timeout) through to createFetchFromRegistry and createGetAuthHeaderByURI so the view command works with private registries and corporate proxies. * test(view): improve test coverage for view command Add tests for: - non-registry spec rejection (git URLs) - no matching version error - version range resolution (^1.0.0) - dist-tag resolution (latest) - nested field selection (dist.shasum) - field selection with --json - text output format (header, dist section, dist-tags) - scoped package lookup (@pnpm.e2e/pkg-with-1-dep) - deps count / deps: none in header - object field rendering as JSON * revert: undo rename of @pnpm/resolving.registry.types The rename from @pnpm/resolving.registry.types to @pnpm/registry.types (and the move from resolving/registry/types/ to registry/types/) is a separate refactoring concern unrelated to the view command. Revert all rename-related changes. Keep the legitimate type additions to PackageInRegistry: maintainers, contributors, and dist.unpackedSize. * revert: restore pnpm-workspace.yaml (remove registry/* glob) * fix(view): handle edge cases in formatBytes and unpackedSize - Use explicit null check for unpackedSize so 0 B is still rendered - Add TB/PB units and clamp index to prevent undefined output --------- Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
7
.changeset/native-registry-check.md
Normal file
7
.changeset/native-registry-check.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@pnpm/resolving.registry.types": patch
|
||||
"@pnpm/deps.inspection.commands": minor
|
||||
"pnpm": minor
|
||||
---
|
||||
|
||||
Added native `pnpm view` command with `info`, `show`, and `v` aliases for viewing package information from the registry. Supports version ranges, dist-tags, aliases, field selection, and JSON output.
|
||||
5
deps/inspection/commands/package.json
vendored
5
deps/inspection/commands/package.json
vendored
@@ -36,6 +36,7 @@
|
||||
"@pnpm/cli.utils": "workspace:*",
|
||||
"@pnpm/colorize-semver-diff": "catalog:",
|
||||
"@pnpm/config.matcher": "workspace:*",
|
||||
"@pnpm/config.pick-registry-for-package": "workspace:*",
|
||||
"@pnpm/config.reader": "workspace:*",
|
||||
"@pnpm/deps.inspection.list": "workspace:*",
|
||||
"@pnpm/deps.inspection.outdated": "workspace:*",
|
||||
@@ -45,7 +46,11 @@
|
||||
"@pnpm/global.packages": "workspace:*",
|
||||
"@pnpm/installing.modules-yaml": "workspace:*",
|
||||
"@pnpm/lockfile.fs": "workspace:*",
|
||||
"@pnpm/network.auth-header": "workspace:*",
|
||||
"@pnpm/network.fetch": "workspace:*",
|
||||
"@pnpm/npm-package-arg": "catalog:",
|
||||
"@pnpm/resolving.default-resolver": "workspace:*",
|
||||
"@pnpm/resolving.npm-resolver": "workspace:*",
|
||||
"@pnpm/semver-diff": "catalog:",
|
||||
"@pnpm/store.path": "workspace:*",
|
||||
"@pnpm/types": "workspace:*",
|
||||
|
||||
1
deps/inspection/commands/src/index.ts
vendored
1
deps/inspection/commands/src/index.ts
vendored
@@ -1,3 +1,4 @@
|
||||
export { list, ll, why } from './listing/index.js'
|
||||
export { outdated } from './outdated/index.js'
|
||||
export * as peers from './peers.js'
|
||||
export * as view from './view/index.js'
|
||||
|
||||
269
deps/inspection/commands/src/view/index.ts
vendored
Normal file
269
deps/inspection/commands/src/view/index.ts
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
import { pickRegistryForPackage } from '@pnpm/config.pick-registry-for-package'
|
||||
import { type Config, types as allTypes } from '@pnpm/config.reader'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { createGetAuthHeaderByURI } from '@pnpm/network.auth-header'
|
||||
import { createFetchFromRegistry } from '@pnpm/network.fetch'
|
||||
import npa from '@pnpm/npm-package-arg'
|
||||
import {
|
||||
fetchMetadataFromFromRegistry,
|
||||
pickPackageFromMeta,
|
||||
pickVersionByVersionRange,
|
||||
type RegistryPackageSpec,
|
||||
} from '@pnpm/resolving.npm-resolver'
|
||||
import { pick } from 'ramda'
|
||||
import { renderHelp } from 'render-help'
|
||||
|
||||
export function rcOptionsTypes (): Record<string, unknown> {
|
||||
return pick([], allTypes)
|
||||
}
|
||||
|
||||
export function cliOptionsTypes (): Record<string, unknown> {
|
||||
return {
|
||||
...rcOptionsTypes(),
|
||||
json: Boolean,
|
||||
}
|
||||
}
|
||||
|
||||
export const commandNames = ['view', 'info', 'show', 'v']
|
||||
|
||||
export function help (): string {
|
||||
return renderHelp({
|
||||
description: 'View package information from the registry without using npm CLI.',
|
||||
usages: [
|
||||
'pnpm view <package-name>',
|
||||
'pnpm view <package-name>@<version>',
|
||||
'pnpm view <package-name> [<field>[.subfield]...]',
|
||||
],
|
||||
descriptionLists: [
|
||||
{
|
||||
title: 'Options',
|
||||
list: [
|
||||
{
|
||||
description: 'Show information in JSON format',
|
||||
name: '--json',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
export async function handler (
|
||||
opts: Config & {
|
||||
json?: boolean
|
||||
},
|
||||
params: string[]
|
||||
): Promise<string | void> {
|
||||
const packageSpec = params[0]
|
||||
|
||||
if (!packageSpec) {
|
||||
throw new PnpmError('MISSING_PACKAGE_NAME', 'Package name is required. Usage: pnpm view <package-name>')
|
||||
}
|
||||
|
||||
const fields = params.slice(1)
|
||||
|
||||
let parsed: ReturnType<typeof npa>
|
||||
try {
|
||||
parsed = npa(packageSpec)
|
||||
} catch {
|
||||
throw new PnpmError('INVALID_PACKAGE_NAME', `Invalid package name: "${packageSpec}"`)
|
||||
}
|
||||
|
||||
if (!parsed.registry) {
|
||||
throw new PnpmError('INVALID_PACKAGE_NAME', `Invalid package name: "${packageSpec}". pnpm view only supports registry packages.`)
|
||||
}
|
||||
|
||||
const subSpec = parsed.type === 'alias' ? parsed.subSpec : parsed
|
||||
const packageName = subSpec?.name
|
||||
if (!packageName) {
|
||||
throw new PnpmError('INVALID_PACKAGE_NAME', `Invalid package name: "${packageSpec}"`)
|
||||
}
|
||||
|
||||
const specType = (subSpec?.type ?? 'tag') as 'tag' | 'version' | 'range'
|
||||
const spec: RegistryPackageSpec = {
|
||||
name: packageName,
|
||||
fetchSpec: subSpec?.fetchSpec ?? 'latest',
|
||||
type: specType,
|
||||
}
|
||||
const registry = pickRegistryForPackage(opts.registries, packageName)
|
||||
const fetchFromRegistry = createFetchFromRegistry(opts)
|
||||
const getAuthHeader = createGetAuthHeaderByURI({ allSettings: opts.rawConfig ?? {}, userSettings: opts.userConfig ?? {} })
|
||||
const { meta: metadata } = await fetchMetadataFromFromRegistry(
|
||||
{
|
||||
fetch: fetchFromRegistry,
|
||||
retry: {
|
||||
factor: opts.fetchRetryFactor,
|
||||
maxTimeout: opts.fetchRetryMaxtimeout,
|
||||
minTimeout: opts.fetchRetryMintimeout,
|
||||
retries: opts.fetchRetries,
|
||||
},
|
||||
timeout: opts.fetchTimeout ?? 60000,
|
||||
fetchWarnTimeoutMs: 10000,
|
||||
},
|
||||
packageName,
|
||||
{
|
||||
registry,
|
||||
authHeaderValue: getAuthHeader(registry),
|
||||
fullMetadata: true,
|
||||
}
|
||||
)
|
||||
const data = pickPackageFromMeta(
|
||||
pickVersionByVersionRange,
|
||||
{ preferredVersionSelectors: undefined },
|
||||
spec,
|
||||
metadata
|
||||
)
|
||||
if (!data) {
|
||||
throw new PnpmError('PACKAGE_NOT_FOUND', `No matching version found for ${packageName}@${spec.fetchSpec}`)
|
||||
}
|
||||
|
||||
const versionsCount = metadata.versions ? Object.keys(metadata.versions).length : 0
|
||||
const depsCount = data.dependencies ? Object.keys(data.dependencies).length : 0
|
||||
const distTags = metadata['dist-tags']
|
||||
|
||||
const info = {
|
||||
...data,
|
||||
author: typeof data.author === 'object' ? (data.author as { name: string }).name : data.author,
|
||||
repository: typeof data.repository === 'object' ? data.repository.url : data.repository,
|
||||
versionsCount: versionsCount > 0 ? versionsCount : undefined,
|
||||
depsCount: depsCount > 0 ? depsCount : undefined,
|
||||
distTags,
|
||||
}
|
||||
|
||||
// If fields are specified, filter and return only those
|
||||
if (fields.length > 0) {
|
||||
const selectedFields: Record<string, unknown> = {}
|
||||
for (const field of fields) {
|
||||
selectedFields[field] = getNestedProperty(info as unknown as Record<string, unknown>, field)
|
||||
}
|
||||
|
||||
if (opts.json) {
|
||||
return JSON.stringify(selectedFields, null, 2)
|
||||
}
|
||||
|
||||
if (fields.length === 1) {
|
||||
const value = selectedFields[fields[0]]
|
||||
return formatFieldValue(value)
|
||||
}
|
||||
|
||||
const lines = fields.map(field => {
|
||||
const value = selectedFields[field]
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
return `${field} = ${JSON.stringify(value)}`
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return `${field} = '${value}'`
|
||||
}
|
||||
return `${field} = ${formatFieldValue(value)}`
|
||||
})
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
if (opts.json) {
|
||||
return JSON.stringify(info, null, 2)
|
||||
}
|
||||
|
||||
const headerParts: string[] = []
|
||||
|
||||
if (info.name && info.version) {
|
||||
headerParts.push(`${info.name}@${info.version}`)
|
||||
}
|
||||
|
||||
if (info.license) {
|
||||
headerParts.push(info.license)
|
||||
}
|
||||
|
||||
if (info.depsCount !== undefined) {
|
||||
headerParts.push(`deps: ${info.depsCount}`)
|
||||
} else {
|
||||
headerParts.push('deps: none')
|
||||
}
|
||||
|
||||
if (info.versionsCount !== undefined) {
|
||||
headerParts.push(`versions: ${info.versionsCount}`)
|
||||
}
|
||||
|
||||
const lines = [headerParts.join(' | ')]
|
||||
|
||||
if (info.description) {
|
||||
lines.push(info.description)
|
||||
}
|
||||
|
||||
if (info.homepage) {
|
||||
lines.push(info.homepage)
|
||||
}
|
||||
|
||||
if (info.keywords && info.keywords.length > 0) {
|
||||
lines.push('')
|
||||
lines.push(`keywords: ${info.keywords.join(', ')}`)
|
||||
}
|
||||
|
||||
if (info.dependencies && Object.keys(info.dependencies).length > 0) {
|
||||
lines.push('')
|
||||
lines.push('dependencies:')
|
||||
const depEntries = Object.entries(info.dependencies).map(([name, version]) => `${name}: ${version}`)
|
||||
lines.push(depEntries.join(', '))
|
||||
}
|
||||
|
||||
if (info.dist) {
|
||||
lines.push('')
|
||||
lines.push('dist')
|
||||
if (info.dist.tarball) {
|
||||
lines.push(`.tarball: ${info.dist.tarball}`)
|
||||
}
|
||||
if (info.dist.shasum) {
|
||||
lines.push(`.shasum: ${info.dist.shasum}`)
|
||||
}
|
||||
if (info.dist.integrity) {
|
||||
lines.push(`.integrity: ${info.dist.integrity}`)
|
||||
}
|
||||
if (info.dist.unpackedSize != null) {
|
||||
lines.push(`.unpackedSize: ${formatBytes(info.dist.unpackedSize)}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (info.maintainers && info.maintainers.length > 0) {
|
||||
lines.push('')
|
||||
lines.push('maintainers:')
|
||||
for (const maintainer of info.maintainers) {
|
||||
lines.push(`- ${maintainer.name}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (info.distTags && Object.keys(info.distTags).length > 0) {
|
||||
lines.push('')
|
||||
lines.push('dist-tags:')
|
||||
for (const [tag, tagVersion] of Object.entries(info.distTags)) {
|
||||
lines.push(`${tag}: ${tagVersion}`)
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
function formatBytes (bytes: number): string {
|
||||
if (bytes === 0) return '0 B'
|
||||
const k = 1024
|
||||
const sizes = ['B', 'kB', 'MB', 'GB', 'TB', 'PB']
|
||||
const i = Math.min(Math.floor(Math.log(bytes) / Math.log(k)), sizes.length - 1)
|
||||
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i]
|
||||
}
|
||||
|
||||
function getNestedProperty (obj: Record<string, unknown>, path: string): unknown {
|
||||
return path.split('.').reduce((acc: unknown, part) => {
|
||||
if (typeof acc === 'object' && acc !== null) {
|
||||
return (acc as Record<string, unknown>)[part]
|
||||
}
|
||||
return undefined
|
||||
}, obj)
|
||||
}
|
||||
|
||||
function formatFieldValue (value: unknown): string {
|
||||
if (value === null || value === undefined) {
|
||||
return ''
|
||||
}
|
||||
if (typeof value === 'object') {
|
||||
return JSON.stringify(value, null, 2)
|
||||
}
|
||||
return String(value)
|
||||
}
|
||||
162
deps/inspection/commands/test/view.ts
vendored
Normal file
162
deps/inspection/commands/test/view.ts
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import { view } from '@pnpm/deps.inspection.commands'
|
||||
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
|
||||
|
||||
const REGISTRY_URL = `http://localhost:${REGISTRY_MOCK_PORT}`
|
||||
|
||||
const VIEW_OPTIONS = {
|
||||
registries: { default: REGISTRY_URL },
|
||||
}
|
||||
|
||||
test('view: command should be available', () => {
|
||||
expect(view.handler).toBeDefined()
|
||||
expect(view.help).toBeDefined()
|
||||
expect(view.commandNames).toBeDefined()
|
||||
expect(view.cliOptionsTypes).toBeDefined()
|
||||
})
|
||||
|
||||
test('view: command should have correct names', () => {
|
||||
expect(view.commandNames).toEqual(['view', 'info', 'show', 'v'])
|
||||
})
|
||||
|
||||
test('view: help should return a string', () => {
|
||||
const help = view.help()
|
||||
expect(typeof help).toBe('string')
|
||||
expect(help.length).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
test('view: cliOptionsTypes should return object', () => {
|
||||
const types = view.cliOptionsTypes()
|
||||
expect(typeof types).toBe('object')
|
||||
})
|
||||
|
||||
test('view: rcOptionsTypes should return object', () => {
|
||||
const types = view.rcOptionsTypes()
|
||||
expect(typeof types).toBe('object')
|
||||
})
|
||||
|
||||
test('view: missing package name throws error', async () => {
|
||||
await expect(
|
||||
view.handler(VIEW_OPTIONS as unknown as Config, [])
|
||||
).rejects.toMatchObject({ code: 'ERR_PNPM_MISSING_PACKAGE_NAME' })
|
||||
})
|
||||
|
||||
test('view: non-registry spec throws error', async () => {
|
||||
await expect(
|
||||
view.handler(VIEW_OPTIONS as unknown as Config, ['github:user/repo'])
|
||||
).rejects.toMatchObject({ code: 'ERR_PNPM_INVALID_PACKAGE_NAME' })
|
||||
})
|
||||
|
||||
test('view: successful lookup of package', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative'])
|
||||
expect(typeof result).toBe('string')
|
||||
expect(result).toContain('is-negative')
|
||||
})
|
||||
|
||||
test('view: package not found throws an error', async () => {
|
||||
await expect(
|
||||
view.handler(VIEW_OPTIONS as unknown as Config, ['not-a-real-package-123456789'])
|
||||
).rejects.toMatchObject({ code: 'ERR_PNPM_FETCH_404' })
|
||||
})
|
||||
|
||||
test('view: no matching version throws an error', async () => {
|
||||
await expect(
|
||||
view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@99999.0.0'])
|
||||
).rejects.toMatchObject({ code: 'ERR_PNPM_PACKAGE_NOT_FOUND' })
|
||||
})
|
||||
|
||||
test('view: with --json option', async () => {
|
||||
const result = await view.handler({ ...VIEW_OPTIONS, json: true } as unknown as Config, ['is-negative'])
|
||||
expect(typeof result).toBe('string')
|
||||
const parsed = JSON.parse(result as string)
|
||||
expect(parsed.name).toBe('is-negative')
|
||||
})
|
||||
|
||||
test('view: accessing a specific field', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative', 'name'])
|
||||
expect(result).toBe('is-negative')
|
||||
})
|
||||
|
||||
test('view: accessing a specific version', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@1.0.0', 'version'])
|
||||
expect(result).toBe('1.0.0')
|
||||
})
|
||||
|
||||
test('view: accessing multiple fields adds quotes for strings', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@1.0.0', 'name', 'version'])
|
||||
expect(typeof result).toBe('string')
|
||||
expect(result).toContain("name = 'is-negative'")
|
||||
expect(result).toContain("version = '1.0.0'")
|
||||
})
|
||||
|
||||
test('view: version range resolves to matching version', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@^1.0.0', 'version'])
|
||||
expect(typeof result).toBe('string')
|
||||
expect(result).toMatch(/^1\./)
|
||||
})
|
||||
|
||||
test('view: dist-tag resolves correctly', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@latest', 'version'])
|
||||
expect(typeof result).toBe('string')
|
||||
expect(result).toMatch(/^\d+\.\d+\.\d+/)
|
||||
})
|
||||
|
||||
test('view: nested field selection', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@1.0.0', 'dist.shasum'])
|
||||
expect(typeof result).toBe('string')
|
||||
expect(result!.length).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
test('view: field selection with --json', async () => {
|
||||
const result = await view.handler(
|
||||
{ ...VIEW_OPTIONS, json: true } as unknown as Config,
|
||||
['is-negative@1.0.0', 'name', 'version']
|
||||
)
|
||||
const parsed = JSON.parse(result as string)
|
||||
expect(parsed.name).toBe('is-negative')
|
||||
expect(parsed.version).toBe('1.0.0')
|
||||
})
|
||||
|
||||
test('view: text output includes header with name@version', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@1.0.0']) as string
|
||||
const firstLine = result.split('\n')[0]
|
||||
expect(firstLine).toContain('is-negative@1.0.0')
|
||||
})
|
||||
|
||||
test('view: text output includes dist section', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@1.0.0']) as string
|
||||
expect(result).toContain('.tarball:')
|
||||
expect(result).toContain('.shasum:')
|
||||
})
|
||||
|
||||
test('view: text output includes dist-tags', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative']) as string
|
||||
expect(result).toContain('dist-tags:')
|
||||
expect(result).toContain('latest:')
|
||||
})
|
||||
|
||||
test('view: text output for package with dependencies shows deps count', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['@pnpm.e2e/pkg-with-1-dep@100.0.0']) as string
|
||||
const firstLine = result.split('\n')[0]
|
||||
expect(firstLine).toContain('deps: ')
|
||||
expect(firstLine).not.toContain('deps: none')
|
||||
})
|
||||
|
||||
test('view: text output for package without dependencies shows deps: none', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@1.0.0']) as string
|
||||
const firstLine = result.split('\n')[0]
|
||||
expect(firstLine).toContain('deps: none')
|
||||
})
|
||||
|
||||
test('view: scoped package lookup', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['@pnpm.e2e/pkg-with-1-dep@100.0.0', 'name'])
|
||||
expect(result).toBe('@pnpm.e2e/pkg-with-1-dep')
|
||||
})
|
||||
|
||||
test('view: object field renders as JSON', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@1.0.0', 'dist'])
|
||||
expect(typeof result).toBe('string')
|
||||
const parsed = JSON.parse(result as string)
|
||||
expect(parsed.tarball).toBeDefined()
|
||||
expect(parsed.shasum).toBeDefined()
|
||||
})
|
||||
12
deps/inspection/commands/tsconfig.json
vendored
12
deps/inspection/commands/tsconfig.json
vendored
@@ -27,6 +27,9 @@
|
||||
{
|
||||
"path": "../../../config/matcher"
|
||||
},
|
||||
{
|
||||
"path": "../../../config/pick-registry-for-package"
|
||||
},
|
||||
{
|
||||
"path": "../../../config/reader"
|
||||
},
|
||||
@@ -54,9 +57,18 @@
|
||||
{
|
||||
"path": "../../../lockfile/fs"
|
||||
},
|
||||
{
|
||||
"path": "../../../network/auth-header"
|
||||
},
|
||||
{
|
||||
"path": "../../../network/fetch"
|
||||
},
|
||||
{
|
||||
"path": "../../../resolving/default-resolver"
|
||||
},
|
||||
{
|
||||
"path": "../../../resolving/npm-resolver"
|
||||
},
|
||||
{
|
||||
"path": "../../../store/path"
|
||||
},
|
||||
|
||||
15
pnpm-lock.yaml
generated
15
pnpm-lock.yaml
generated
@@ -3122,6 +3122,9 @@ importers:
|
||||
'@pnpm/config.matcher':
|
||||
specifier: workspace:*
|
||||
version: link:../../../config/matcher
|
||||
'@pnpm/config.pick-registry-for-package':
|
||||
specifier: workspace:*
|
||||
version: link:../../../config/pick-registry-for-package
|
||||
'@pnpm/config.reader':
|
||||
specifier: workspace:*
|
||||
version: link:../../../config/reader
|
||||
@@ -3152,9 +3155,21 @@ importers:
|
||||
'@pnpm/logger':
|
||||
specifier: 'catalog:'
|
||||
version: 1001.0.1
|
||||
'@pnpm/network.auth-header':
|
||||
specifier: workspace:*
|
||||
version: link:../../../network/auth-header
|
||||
'@pnpm/network.fetch':
|
||||
specifier: workspace:*
|
||||
version: link:../../../network/fetch
|
||||
'@pnpm/npm-package-arg':
|
||||
specifier: 'catalog:'
|
||||
version: 2.0.0
|
||||
'@pnpm/resolving.default-resolver':
|
||||
specifier: workspace:*
|
||||
version: link:../../../resolving/default-resolver
|
||||
'@pnpm/resolving.npm-resolver':
|
||||
specifier: workspace:*
|
||||
version: link:../../../resolving/npm-resolver
|
||||
'@pnpm/semver-diff':
|
||||
specifier: 'catalog:'
|
||||
version: 1.1.0
|
||||
|
||||
@@ -6,7 +6,7 @@ import { createCompletionServer, doctor, generateCompletion } from '@pnpm/cli.co
|
||||
import { config, getCommand, setCommand } from '@pnpm/config.commands'
|
||||
import { types as allTypes } from '@pnpm/config.reader'
|
||||
import { audit, licenses, sbom } from '@pnpm/deps.compliance.commands'
|
||||
import { list, ll, outdated, peers, why } from '@pnpm/deps.inspection.commands'
|
||||
import { list, ll, outdated, peers, view, why } from '@pnpm/deps.inspection.commands'
|
||||
import { selfUpdate, setup } from '@pnpm/engine.pm.commands'
|
||||
import { env, runtime } from '@pnpm/engine.runtime.commands'
|
||||
import {
|
||||
@@ -167,6 +167,7 @@ const commands: CommandDefinition[] = [
|
||||
unlink,
|
||||
update,
|
||||
version,
|
||||
view,
|
||||
why,
|
||||
createHelp(helpByCommandName),
|
||||
...notImplementedCommandDefinitions,
|
||||
|
||||
@@ -11,7 +11,6 @@ const NOT_IMPLEMENTED_COMMANDS = [
|
||||
'edit',
|
||||
'find',
|
||||
'home',
|
||||
'info',
|
||||
'issues',
|
||||
'logout',
|
||||
'owner',
|
||||
@@ -24,15 +23,12 @@ const NOT_IMPLEMENTED_COMMANDS = [
|
||||
'se',
|
||||
'search',
|
||||
'set-script',
|
||||
'show',
|
||||
'star',
|
||||
'stars',
|
||||
'team',
|
||||
'token',
|
||||
'unpublish',
|
||||
'unstar',
|
||||
'v',
|
||||
'view',
|
||||
'whoami',
|
||||
'xmas',
|
||||
]
|
||||
|
||||
@@ -52,7 +52,7 @@ import {
|
||||
pickPackage,
|
||||
type PickPackageOptions,
|
||||
} from './pickPackage.js'
|
||||
import { pickVersionByVersionRange } from './pickPackageFromMeta.js'
|
||||
import { pickPackageFromMeta, pickVersionByVersionRange } from './pickPackageFromMeta.js'
|
||||
import { failIfTrustDowngraded } from './trustChecks.js'
|
||||
import { whichVersionIsPinned } from './whichVersionIsPinned.js'
|
||||
import { workspacePrefToNpm } from './workspacePrefToNpm.js'
|
||||
@@ -110,9 +110,13 @@ function formatTimeAgo (date: Date): string {
|
||||
}
|
||||
|
||||
export {
|
||||
fetchMetadataFromFromRegistry,
|
||||
type FetchMetadataFromFromRegistryOptions,
|
||||
type PackageMeta,
|
||||
type PackageMetaCache,
|
||||
parseBareSpecifier,
|
||||
pickPackageFromMeta,
|
||||
pickVersionByVersionRange,
|
||||
type RegistryPackageSpec,
|
||||
RegistryResponseError,
|
||||
workspacePrefToNpm,
|
||||
|
||||
@@ -33,10 +33,21 @@ export interface PackageInRegistry extends PackageManifest {
|
||||
oidcConfigId: string
|
||||
}
|
||||
}
|
||||
maintainers?: Array<{
|
||||
name: string
|
||||
email?: string
|
||||
url?: string
|
||||
}>
|
||||
contributors?: Array<{
|
||||
name: string
|
||||
email?: string
|
||||
url?: string
|
||||
}>
|
||||
dist: {
|
||||
integrity?: string
|
||||
shasum: string
|
||||
tarball: string
|
||||
unpackedSize?: number
|
||||
attestations?: {
|
||||
provenance?: {
|
||||
predicateType: string
|
||||
|
||||
Reference in New Issue
Block a user