Skip to content

Commit

Permalink
fix: better error message when the installed package was unpublished (#…
Browse files Browse the repository at this point in the history
…5854)

close #5849
  • Loading branch information
zkochan committed Dec 31, 2022
1 parent 7eb7056 commit 83ba90f
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 4 deletions.
6 changes: 6 additions & 0 deletions .changeset/gorgeous-islands-shake.md
@@ -0,0 +1,6 @@
---
"@pnpm/npm-resolver": patch
"pnpm": patch
---

Throw an accurate error message when trying to install a package that has no versions, or all of its versions are unpublished [#5849](https://github.com/pnpm/pnpm/issues/5849).
9 changes: 8 additions & 1 deletion resolving/npm-resolver/src/pickPackage.ts
Expand Up @@ -20,10 +20,17 @@ export interface PackageMeta {
name: string
'dist-tags': Record<string, string>
versions: Record<string, PackageInRegistry>
time?: Record<string, string>
time?: PackageMetaTime
cachedAt?: number
}

export type PackageMetaTime = Record<string, string> & {
unpublished?: {
time: string
versions: string[]
}
}

export interface PackageMetaCache {
get: (key: string) => PackageMeta | undefined
set: (key: string, meta: PackageMeta) => void
Expand Down
8 changes: 8 additions & 0 deletions resolving/npm-resolver/src/pickPackageFromMeta.ts
Expand Up @@ -18,6 +18,14 @@ export function pickPackageFromMeta (
meta: PackageMeta,
publishedBy?: Date
): PackageInRegistry | null {
if ((!meta.versions || Object.keys(meta.versions).length === 0) && !publishedBy) {
// Unfortunately, the npm registry doesn't return the time field in the abbreviated metadata.
// So we won't always know if the package was unpublished.
if (meta.time?.unpublished?.versions?.length) {
throw new PnpmError('UNPUBLISHED_PKG', `No versions available for ${spec.name} because it was unpublished`)
}
throw new PnpmError('NO_VERSIONS', `No versions available for ${spec.name}. The package may be unpublished.`)
}
try {
let version!: string | null
switch (spec.type) {
Expand Down
45 changes: 42 additions & 3 deletions resolving/npm-resolver/test/index.ts
Expand Up @@ -11,6 +11,7 @@ import { fixtures } from '@pnpm/test-fixtures'
import loadJsonFile from 'load-json-file'
import nock from 'nock'
import exists from 'path-exists'
import omit from 'ramda/src/omit'
import tempy from 'tempy'

const f = fixtures(__dirname)
Expand Down Expand Up @@ -45,6 +46,15 @@ async function retryLoadJsonFile<T> (filePath: string) {
}
}

afterEach(() => {
nock.cleanAll()
nock.disableNetConnect()
})

beforeEach(() => {
nock.enableNetConnect()
})

test('resolveFromNpm()', async () => {
nock(registry)
.get('/is-positive')
Expand Down Expand Up @@ -1653,17 +1663,46 @@ test('request to metadata is retried if the received JSON is broken', async () =
expect(resolveResult?.id).toBe('registry.npmjs.org/is-positive/1.0.0')
})

test('request to a package with malformed metadata', async () => {
test('request to a package with unpublished versions', async () => {
nock(registry)
.get('/code-snippet')
.reply(200, loadJsonFile.sync(f.find('malformed.json')))
.reply(200, loadJsonFile.sync(f.find('unpublished.json')))

const cacheDir = tempy.directory()
const resolve = createResolveFromNpm({ cacheDir })

await expect(resolve({ alias: 'code-snippet' }, { registry })).rejects
.toThrow(
new PnpmError('MALFORMED_METADATA', 'Received malformed metadata for "code-snippet"')
new PnpmError('NO_VERSIONS', 'No versions available for code-snippet because it was unpublished')
)
})

test('request to a package with no versions', async () => {
nock(registry)
.get('/code-snippet')
.reply(200, { name: 'code-snippet' })

const cacheDir = tempy.directory()
const resolve = createResolveFromNpm({ cacheDir })

await expect(resolve({ alias: 'code-snippet' }, { registry })).rejects
.toThrow(
new PnpmError('NO_VERSIONS', 'No versions available for code-snippet. The package may be unpublished.')
)
})

test('request to a package with no dist-tags', async () => {
const isPositiveMeta = omit(['dist-tags'], loadJsonFile.sync(f.find('is-positive.json')))
nock(registry)
.get('/is-positive')
.reply(200, isPositiveMeta)

const cacheDir = tempy.directory()
const resolve = createResolveFromNpm({ cacheDir })

await expect(resolve({ alias: 'is-positive' }, { registry })).rejects
.toThrow(
new PnpmError('MALFORMED_METADATA', 'Received malformed metadata for "is-positive"')
)
})

Expand Down
9 changes: 9 additions & 0 deletions resolving/npm-resolver/test/publishedBy.test.ts
Expand Up @@ -18,6 +18,15 @@ const fetch = createFetchFromRegistry({})
const getAuthHeader = () => undefined
const createResolveFromNpm = createNpmResolver.bind(null, fetch, getAuthHeader)

afterEach(() => {
nock.cleanAll()
nock.disableNetConnect()
})

beforeEach(() => {
nock.enableNetConnect()
})

test('fall back to a newer version if there is no version published by the given date', async () => {
nock(registry)
.get('/bad-dates')
Expand Down

0 comments on commit 83ba90f

Please sign in to comment.