diff --git a/.changeset/hip-clocks-share.md b/.changeset/hip-clocks-share.md new file mode 100644 index 0000000000..79a7d45bcf --- /dev/null +++ b/.changeset/hip-clocks-share.md @@ -0,0 +1,5 @@ +--- +"@pnpm/npm-resolver": patch +--- + +Throw a meaningful error on malformed registry metadata. diff --git a/packages/npm-resolver/src/pickPackageFromMeta.ts b/packages/npm-resolver/src/pickPackageFromMeta.ts index 975654abea..9836365075 100644 --- a/packages/npm-resolver/src/pickPackageFromMeta.ts +++ b/packages/npm-resolver/src/pickPackageFromMeta.ts @@ -1,3 +1,4 @@ +import PnpmError from '@pnpm/error' import { VersionSelectors } from '@pnpm/resolver-base' import { RegistryPackageSpec } from './parsePref' import { PackageInRegistry, PackageMeta } from './pickPackage' @@ -8,28 +9,35 @@ export default function ( preferredVersionSelectors: VersionSelectors | undefined, meta: PackageMeta ): PackageInRegistry { - let version!: string - switch (spec.type) { - case 'version': - version = spec.fetchSpec - break - case 'tag': - version = meta['dist-tags'][spec.fetchSpec] - break - case 'range': - version = pickVersionByVersionRange(meta, spec.fetchSpec, preferredVersionSelectors) - break + try { + let version!: string + switch (spec.type) { + case 'version': + version = spec.fetchSpec + break + case 'tag': + version = meta['dist-tags'][spec.fetchSpec] + break + case 'range': + version = pickVersionByVersionRange(meta, spec.fetchSpec, preferredVersionSelectors) + break + } + const manifest = meta.versions[version] + if (manifest && meta['name']) { + // Packages that are published to the GitHub registry are always published with a scope. + // However, the name in the package.json for some reason may omit the scope. + // So the package published to the GitHub registry will be published under @foo/bar + // but the name in package.json will be just bar. + // In order to avoid issues, we consider that the real name of the package is the one with the scope. + manifest.name = meta['name'] + } + return manifest + } catch (err) { + throw new PnpmError('MALFORMED_METADATA', + `Received malformed metadata for "${spec.name}"`, + { hint: 'This might mean that the package was unpublished from the registry' } + ) } - const manifest = meta.versions[version] - if (manifest && meta['name']) { - // Packages that are published to the GitHub registry are always published with a scope. - // However, the name in the package.json for some reason may omit the scope. - // So the package published to the GitHub registry will be published under @foo/bar - // but the name in package.json will be just bar. - // In order to avoid issues, we consider that the real name of the package is the one with the scope. - manifest.name = meta['name'] - } - return manifest } function pickVersionByVersionRange ( diff --git a/packages/npm-resolver/test/index.ts b/packages/npm-resolver/test/index.ts index 1dd87f15bd..cfcd3bb215 100644 --- a/packages/npm-resolver/test/index.ts +++ b/packages/npm-resolver/test/index.ts @@ -1535,3 +1535,17 @@ 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 () => { + nock(registry) + .get('/code-snippet') + .reply(200, loadJsonFile.sync(path.join(__dirname, 'meta/malformed.json'))) + + const storeDir = tempy.directory() + const resolve = createResolveFromNpm({ storeDir }) + + await expect(resolve({ alias: 'code-snippet' }, { registry })).rejects + .toThrow( + new PnpmError('MALFORMED_METADATA', 'Received malformed metadata for "code-snippet"') + ) +}) diff --git a/packages/npm-resolver/test/meta/malformed.json b/packages/npm-resolver/test/meta/malformed.json new file mode 100644 index 0000000000..a9de3c572a --- /dev/null +++ b/packages/npm-resolver/test/meta/malformed.json @@ -0,0 +1,27 @@ +{ + "_id": "code-snippet", + "_rev": "2-b2defd889997bf20ca640c1a9b38d82c", + "name": "code-snippet", + "time": { + "modified": "2015-07-31T03:43:17.076Z", + "created": "2015-07-31T03:43:17.076Z", + "1.0.4": "2015-07-31T03:43:17.076Z", + "unpublished": { + "name": "nhnent", + "time": "2015-07-31T03:52:16.267Z", + "tags": { + "latest": "1.0.4" + }, + "maintainers": [ + { + "name": "nhnent", + "email": "dl_javascript@nhnent.com" + } + ], + "versions": [ + "1.0.4" + ] + } + }, + "_attachments": {} +} \ No newline at end of file