Files
pnpm/resolving/npm-resolver/test/index.ts
Zoltan Kochan 2554264fdd perf: use NDJSON format for metadata cache (#11188)
The metadata cache files now use a two-line NDJSON format:
- Line 1: cache headers (etag, modified, cachedAt) ~100 bytes
- Line 2: raw registry metadata JSON (unchanged)

This allows loadMetaHeaders to read only the first 1 KB of the file
to extract conditional-request headers (etag, modified), avoiding
the cost of reading and parsing multi-MB metadata files when the
registry returns 200 and the old metadata would be discarded.

Also moves cache directories to v11/ namespace (v11/metadata,
v11/metadata-full, v11/metadata-full-filtered) since the format
is not backwards compatible.
2026-04-04 01:24:05 +02:00

2183 lines
73 KiB
TypeScript

/// <reference path="../../../__typings__/index.d.ts"/>
import fs from 'node:fs'
import path from 'node:path'
import { ABBREVIATED_META_DIR } from '@pnpm/constants'
import { createHexHash } from '@pnpm/crypto.hash'
import { PnpmError } from '@pnpm/error'
import { createFetchFromRegistry } from '@pnpm/network.fetch'
import {
createNpmResolver,
NoMatchingVersionError,
RegistryResponseError,
} from '@pnpm/resolving.npm-resolver'
import { fixtures } from '@pnpm/test-fixtures'
import type { ProjectRootDir, Registries } from '@pnpm/types'
import { loadJsonFileSync } from 'load-json-file'
import { omit } from 'ramda'
import { temporaryDirectory } from 'tempy'
import { delay, getMockAgent, retryLoadJsonFile, setupMockAgent, teardownMockAgent } from './utils/index.js'
const f = fixtures(import.meta.dirname)
/* eslint-disable @typescript-eslint/no-explicit-any */
const isPositiveMeta = loadJsonFileSync<any>(f.find('is-positive.json'))
const isPositiveMetaWithDeprecated = loadJsonFileSync<any>(f.find('is-positive-with-deprecated.json'))
const isPositiveMetaFull = loadJsonFileSync<any>(f.find('is-positive-full.json'))
const isPositiveBrokenMeta = loadJsonFileSync<any>(f.find('is-positive-broken.json'))
const sindresorhusIsMeta = loadJsonFileSync<any>(f.find('sindresorhus-is.json'))
const jsonMeta = loadJsonFileSync<any>(f.find('JSON.json'))
const brokenIntegrity = loadJsonFileSync<any>(f.find('broken-integrity.json'))
/* eslint-enable @typescript-eslint/no-explicit-any */
const registries = {
default: 'https://registry.npmjs.org/',
'@jsr': 'https://npm.jsr.io/',
} satisfies Registries
const fetch = createFetchFromRegistry({})
const getAuthHeader = () => undefined
const createResolveFromNpm = createNpmResolver.bind(null, fetch, getAuthHeader)
afterEach(async () => {
await teardownMockAgent()
})
beforeEach(async () => {
await setupMockAgent()
})
test('resolveFromNpm()', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.0.0' }, { calcSpecifier: true })
expect(resolveResult!.resolvedVia).toBe('npm-registry')
expect(resolveResult!.id).toBe('is-positive@1.0.0')
expect(resolveResult!.normalizedBareSpecifier).toBe('1.0.0')
expect(resolveResult!.latest!.split('.')).toHaveLength(3)
expect(resolveResult!.resolution).toStrictEqual({
integrity: 'sha512-9cI+DmhNhA8ioT/3EJFnt0s1yehnAECyIOXdT+2uQGzcEEBaj8oNmVWj33+ZjPndMIFRQh8JeJlEu1uv5/J7pQ==',
tarball: 'https://registry.npmjs.org/is-positive/-/is-positive-1.0.0.tgz',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('1.0.0')
// The resolve function does not wait for the package meta cache file to be saved
// so we must delay for a bit in order to read it
const meta = await retryLoadJsonFile<any>(path.join(cacheDir, ABBREVIATED_META_DIR, 'registry.npmjs.org/is-positive.jsonl')) // eslint-disable-line @typescript-eslint/no-explicit-any
expect(meta.name).toBeTruthy()
expect(meta.versions).toBeTruthy()
expect(meta['dist-tags']).toBeTruthy()
})
test('resolveFromNpm() strips port 80 from http tarball URLs', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, {
...isPositiveMeta,
versions: {
'1.0.0': {
...isPositiveMeta.versions['1.0.0'],
dist: {
...isPositiveMeta.versions['1.0.0'].dist,
tarball: 'http://registry.npmjs.org:80/is-positive/-/is-positive-1.0.0.tgz',
},
},
},
})
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.0.0' }, { calcSpecifier: true })
expect(resolveResult!.resolution).toStrictEqual({
integrity: 'sha512-9cI+DmhNhA8ioT/3EJFnt0s1yehnAECyIOXdT+2uQGzcEEBaj8oNmVWj33+ZjPndMIFRQh8JeJlEu1uv5/J7pQ==',
tarball: 'http://registry.npmjs.org/is-positive/-/is-positive-1.0.0.tgz',
})
})
test('resolveFromNpm() does not save mutated meta to the cache', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {})
resolveResult!.manifest!.version = '1000'
// The resolve function does not wait for the package meta cache file to be saved
// so we must delay for a bit in order to read it
const meta = await retryLoadJsonFile<any>(path.join(cacheDir, ABBREVIATED_META_DIR, 'registry.npmjs.org/is-positive.jsonl')) // eslint-disable-line @typescript-eslint/no-explicit-any
expect(meta.versions['1.0.0'].version).toBe('1.0.0')
})
test('resolveFromNpm() should save metadata to a unique file when the package name has upper case letters', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/JSON', method: 'GET' })
.reply(200, jsonMeta)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'JSON', bareSpecifier: '1.0.0' }, {})
expect(resolveResult!.resolvedVia).toBe('npm-registry')
expect(resolveResult!.id).toBe('JSON@1.0.0')
// The resolve function does not wait for the package meta cache file to be saved
// so we must delay for a bit in order to read it
const meta = await retryLoadJsonFile<any>(path.join(cacheDir, ABBREVIATED_META_DIR, `registry.npmjs.org/JSON_${createHexHash('JSON')}.jsonl`)) // eslint-disable-line @typescript-eslint/no-explicit-any
expect(meta.name).toBeTruthy()
expect(meta.versions).toBeTruthy()
expect(meta['dist-tags']).toBeTruthy()
})
test('relative workspace protocol is skipped', async () => {
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ bareSpecifier: 'workspace:../is-positive' }, {
projectDir: '/home/istvan/src',
})
expect(resolveResult).toBeNull()
})
test('dry run', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {
dryRun: true,
})
expect(resolveResult!.id).toBe('is-positive@1.0.0')
expect(resolveResult!.latest!.split('.')).toHaveLength(3)
expect(resolveResult!.resolution).toStrictEqual({
integrity: 'sha512-9cI+DmhNhA8ioT/3EJFnt0s1yehnAECyIOXdT+2uQGzcEEBaj8oNmVWj33+ZjPndMIFRQh8JeJlEu1uv5/J7pQ==',
tarball: 'https://registry.npmjs.org/is-positive/-/is-positive-1.0.0.tgz',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('1.0.0')
// The resolve function does not wait for the package meta cache file to be saved
// so we must delay for a bit in order to read it
await delay(500)
expect(fs.existsSync(path.join(cacheDir, resolveResult!.id, '..', 'index.json'))).toBeFalsy()
})
test('resolve to latest when no bareSpecifier specified', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive' }, {})
expect(resolveResult!.id).toBe('is-positive@3.1.0')
})
test('resolve to defaultTag when no bareSpecifier specified', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive' }, {
defaultTag: 'stable',
})
expect(resolveResult!.id).toBe('is-positive@3.0.0')
})
test('resolve to biggest non-deprecated version that satisfies the range', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMetaWithDeprecated)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '3' }, {
})
expect(resolveResult!.id).toBe('is-positive@3.0.0')
})
test('resolve to a deprecated version if there are no non-deprecated ones that satisfy the range', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMetaWithDeprecated)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '2' }, {})
expect(resolveResult!.id).toBe('is-positive@2.0.0')
})
test('can resolve aliased dependency', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'positive', bareSpecifier: 'npm:is-positive@1.0.0' }, {})
expect(resolveResult!.id).toBe('is-positive@1.0.0')
})
test('can resolve aliased dependency w/o version specifier', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'positive', bareSpecifier: 'npm:is-positive' }, {})
expect(resolveResult!.id).toBe('is-positive@3.1.0')
})
test('can resolve aliased dependency w/o version specifier to default tag', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'positive', bareSpecifier: 'npm:is-positive' }, {
defaultTag: 'stable',
calcSpecifier: true,
})
expect(resolveResult!.id).toBe('is-positive@3.0.0')
expect(resolveResult!.normalizedBareSpecifier).toBe('npm:is-positive@^3.0.0')
})
test('can resolve aliased scoped dependency', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/@sindresorhus%2Fis', method: 'GET' })
.reply(200, sindresorhusIsMeta)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is', bareSpecifier: 'npm:@sindresorhus/is@0.6.0' }, {})
expect(resolveResult!.id).toBe('@sindresorhus/is@0.6.0')
})
test('can resolve aliased scoped dependency w/o version specifier', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/@sindresorhus%2Fis', method: 'GET' })
.reply(200, sindresorhusIsMeta)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is', bareSpecifier: 'npm:@sindresorhus/is' }, {})
expect(resolveResult!.id).toBe('@sindresorhus/is@0.7.0')
})
test('can resolve package with version prefixed with v', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: 'v1.0.0' }, {})
expect(resolveResult!.id).toBe('is-positive@1.0.0')
})
test('can resolve package version loosely', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '= 1.0.0' }, {})
expect(resolveResult!.id).toBe('is-positive@1.0.0')
})
test("resolves to latest if it's inside the wanted range. Even if there are newer versions available inside the range", async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, {
...isPositiveMeta,
'dist-tags': { latest: '3.0.0' },
})
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({
alias: 'is-positive',
bareSpecifier: '^3.0.0',
}, {})
// 3.1.0 is available but latest is 3.0.0, so preferring it
expect(resolveResult!.id).toBe('is-positive@3.0.0')
})
test("resolves to latest if it's inside the preferred range. Even if there are newer versions available inside the preferred range", async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, {
...isPositiveMeta,
'dist-tags': { latest: '3.0.0' },
})
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({
alias: 'is-positive',
bareSpecifier: '^3.0.0',
}, {
preferredVersions: {
'is-positive': { '^3.0.0': 'range' },
},
})
// 3.1.0 is available but latest is 3.0.0, so preferring it
expect(resolveResult!.id).toBe('is-positive@3.0.0')
})
test("resolve using the wanted range, when it doesn't intersect with the preferred range. Even if the preferred range contains the latest version", async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, {
...isPositiveMeta,
'dist-tags': { latest: '2.0.0' },
})
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({
alias: 'is-positive',
bareSpecifier: '^3.0.0',
}, {
preferredVersions: {
'is-positive': { '^2.0.0': 'range' },
},
})
expect(resolveResult!.id).toBe('is-positive@3.1.0')
})
test("use the preferred version if it's inside the wanted range", async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, {
...isPositiveMeta,
'dist-tags': { latest: '3.1.0' },
})
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({
alias: 'is-positive',
bareSpecifier: '^3.0.0',
}, {
preferredVersions: {
'is-positive': { '3.0.0': 'version' },
},
})
// 3.1.0 is the latest but we prefer the 3.0.0
expect(resolveResult!.id).toBe('is-positive@3.0.0')
})
test("ignore the preferred version if it's not inside the wanted range", async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, {
...isPositiveMeta,
'dist-tags': { latest: '3.1.0' },
})
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({
alias: 'is-positive',
bareSpecifier: '^3.0.0',
}, {
preferredVersions: {
'is-positive': { '2.0.0': 'version' },
},
})
expect(resolveResult!.id).toBe('is-positive@3.1.0')
})
test('use the preferred range if it intersects with the wanted range', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, {
...isPositiveMeta,
'dist-tags': { latest: '1.0.0' },
})
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({
alias: 'is-positive',
bareSpecifier: '>=1.0.0',
}, {
preferredVersions: {
'is-positive': { '^3.0.0': 'range' },
},
})
// 1.0.0 is the latest but we prefer a version that is also in the preferred range
expect(resolveResult!.id).toBe('is-positive@3.1.0')
})
test('use the preferred range if it intersects with the wanted range (an array of preferred versions is passed)', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, {
...isPositiveMeta,
'dist-tags': { latest: '1.0.0' },
})
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({
alias: 'is-positive',
bareSpecifier: '>=1.0.0',
}, {
preferredVersions: {
'is-positive': {
'3.0.0': 'version',
'3.1.0': 'version',
},
},
})
// 1.0.0 is the latest but we prefer a version that is also in the preferred range
expect(resolveResult!.id).toBe('is-positive@3.1.0')
})
test("ignore the preferred range if it doesn't intersect with the wanted range", async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, {
...isPositiveMeta,
'dist-tags': { latest: '3.1.0' },
})
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({
alias: 'is-positive',
bareSpecifier: '^3.0.0',
}, {
preferredVersions: {
'is-positive': { '^2.0.0': 'range' },
},
})
expect(resolveResult!.id).toBe('is-positive@3.1.0')
})
test("use the preferred dist-tag if it's inside the wanted range", async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, {
...isPositiveMeta,
'dist-tags': {
latest: '3.1.0',
stable: '3.0.0',
},
})
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({
alias: 'is-positive',
bareSpecifier: '^3.0.0',
}, {
preferredVersions: {
'is-positive': { stable: 'tag' },
},
})
expect(resolveResult!.id).toBe('is-positive@3.0.0')
})
test("ignore the preferred dist-tag if it's not inside the wanted range", async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, {
...isPositiveMeta,
'dist-tags': {
latest: '3.1.0',
stable: '2.0.0',
},
})
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({
alias: 'is-positive',
bareSpecifier: '^3.0.0',
}, {
preferredVersions: {
'is-positive': { stable: 'tag' },
},
})
expect(resolveResult!.id).toBe('is-positive@3.1.0')
})
test("prefer a version that is both inside the wanted and preferred ranges. Even if it's not the latest of any of them", async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, {
...isPositiveMeta,
'dist-tags': {
latest: '3.0.0',
},
})
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({
alias: 'is-positive',
bareSpecifier: '1.0.0 || 2.0.0',
}, {
preferredVersions: {
'is-positive': { '1.0.0 || 3.0.0': 'range' },
},
})
expect(resolveResult!.id).toBe('is-positive@1.0.0')
})
test('prefer the version that is matched by more preferred selectors', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({
alias: 'is-positive',
bareSpecifier: '^3.0.0',
}, {
preferredVersions: {
'is-positive': { '^3.0.0': 'range', '3.0.0': 'version' },
},
})
expect(resolveResult!.id).toBe('is-positive@3.0.0')
})
test('prefer the version that has bigger weight in preferred selectors', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({
alias: 'is-positive',
bareSpecifier: '^3.0.0',
}, {
preferredVersions: {
'is-positive': {
'^3.0.0': 'range',
'3.0.0': { selectorType: 'version', weight: 100 },
'3.1.0': 'version',
},
},
})
expect(resolveResult!.id).toBe('is-positive@3.0.0')
})
test('versions without selector weights should have higher priority than negatively weighted versions', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({
alias: 'is-positive',
bareSpecifier: '^3.0.0',
}, {
preferredVersions: {
'is-positive': {
// Penalize 3.0.0, but don't mention 3.1.0
'3.0.0': { selectorType: 'version', weight: -1 },
},
},
})
// 3.1.0 should be selected because it has default weight 0, higher than 3.0.0's -1
expect(resolveResult!.id).toBe('is-positive@3.1.0')
})
test('offline resolution fails when package meta not found in the store', async () => {
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
offline: true,
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
await expect(resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {})).rejects
.toThrow(
new PnpmError('NO_OFFLINE_META', `Failed to resolve is-positive@1.0.0 in package mirror ${path.join(cacheDir, ABBREVIATED_META_DIR, 'registry.npmjs.org/is-positive.jsonl')}`)
)
})
test('offline resolution succeeds when package meta is found in the store', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const cacheDir = temporaryDirectory()
{
const { resolveFromNpm } = createResolveFromNpm({
offline: false,
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
// This request will save the package's meta in the store
await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {})
}
{
const { resolveFromNpm } = createResolveFromNpm({
offline: true,
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {})
expect(resolveResult!.id).toBe('is-positive@1.0.0')
}
})
test('prefer offline resolution does not fail when package meta not found in the store', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const { resolveFromNpm } = createResolveFromNpm({
preferOffline: true,
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {})
expect(resolveResult!.id).toBe('is-positive@1.0.0')
})
test('when prefer offline is used, meta from store is used, where latest might be out-of-date', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, {
...isPositiveMeta,
'dist-tags': { latest: '3.0.0' },
})
const cacheDir = temporaryDirectory()
{
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
// This request will save the package's meta in the store
await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {})
}
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, {
...isPositiveMeta,
'dist-tags': { latest: '3.1.0' },
})
{
const { resolveFromNpm } = createResolveFromNpm({
preferOffline: true,
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '^3.0.0' }, {})
expect(resolveResult!.id).toBe('is-positive@3.0.0')
}
})
test('error is thrown when package is not found in the registry', async () => {
const notExistingPackage = 'foo'
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: `/${notExistingPackage}`, method: 'GET' })
.reply(404, {})
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
await expect(resolveFromNpm({ alias: notExistingPackage, bareSpecifier: '1.0.0' }, {})).rejects
.toThrow(
new RegistryResponseError(
{
url: `${registries.default}${notExistingPackage}`,
},
{
status: 404,
statusText: 'Not Found',
},
notExistingPackage
)
)
})
test('error is thrown when registry not responding', async () => {
const notExistingPackage = 'foo'
const notExistingRegistry = 'http://not-existing.pnpm.io'
// Mock a network error for the non-existing registry
const dnsError = Object.assign(new Error('getaddrinfo ENOTFOUND not-existing.pnpm.io'), { code: 'ENOTFOUND' })
getMockAgent().get(notExistingRegistry)
.intercept({ path: `/${notExistingPackage}`, method: 'GET' })
.replyWithError(dnsError)
getMockAgent().get(notExistingRegistry)
.intercept({ path: `/${notExistingPackage}`, method: 'GET' })
.replyWithError(dnsError)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
retry: { retries: 1 },
registries: {
default: notExistingRegistry,
},
})
let thrown: any // eslint-disable-line
try {
await resolveFromNpm({ alias: notExistingPackage, bareSpecifier: '1.0.0' }, {})
} catch (err) {
thrown = err
}
expect(thrown).toBeTruthy()
expect(thrown.code).toBe('ERR_PNPM_META_FETCH_FAIL')
expect(thrown.message).toContain(`GET ${notExistingRegistry}/${notExistingPackage}:`)
})
test('extra info is shown if package has valid semver appended', async () => {
const notExistingPackage = 'foo1.0.0'
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: `/${notExistingPackage}`, method: 'GET' })
.reply(404, {})
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
await expect(resolveFromNpm({ alias: notExistingPackage, bareSpecifier: '1.0.0' }, {})).rejects
.toThrow(
new RegistryResponseError(
{
url: `${registries.default}${notExistingPackage}`,
},
{
status: 404,
statusText: 'Not Found',
},
notExistingPackage
)
)
})
test('error is thrown when there is no package found for the requested version', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const wantedDependency = { alias: 'is-positive', bareSpecifier: '1000.0.0' }
await expect(resolveFromNpm(wantedDependency, {})).rejects
.toThrow(
new NoMatchingVersionError({
wantedDependency,
packageMeta: isPositiveMeta,
registry: registries.default,
})
)
})
test('error is thrown when package needs authorization', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/needs-auth', method: 'GET' })
.reply(403, {})
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
await expect(resolveFromNpm({ alias: 'needs-auth', bareSpecifier: '*' }, {})).rejects
.toThrow(
new RegistryResponseError(
{
url: `${registries.default}needs-auth`,
},
{
status: 403,
statusText: 'Forbidden',
},
'needs-auth'
)
)
})
test('error is thrown when registry returns 400 Bad Request', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/bad-pkg', method: 'GET' })
.reply(400)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
await expect(resolveFromNpm({ alias: 'bad-pkg', bareSpecifier: '1.0.0' }, {})).rejects
.toThrow(
new RegistryResponseError(
{
url: `${registries.default}bad-pkg`,
},
{
status: 400,
statusText: 'Bad Request',
},
'bad-pkg'
)
)
})
test('error is thrown when there is no package found for the requested range', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const wantedDependency = { alias: 'is-positive', bareSpecifier: '^1000.0.0' }
await expect(resolveFromNpm(wantedDependency, {})).rejects
.toThrow(
new NoMatchingVersionError({
wantedDependency,
packageMeta: isPositiveMeta,
registry: registries.default,
})
)
})
test('error is thrown when there is no package found for the requested tag', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const wantedDependency = { alias: 'is-positive', bareSpecifier: 'unknown-tag' }
await expect(resolveFromNpm(wantedDependency, {})).rejects
.toThrow(
new NoMatchingVersionError({
wantedDependency,
packageMeta: isPositiveMeta,
registry: registries.default,
})
)
})
test('resolveFromNpm() loads full metadata even if non-full metadata is already cached in store', async () => {
const mockPool = getMockAgent().get(registries.default.replace(/\/$/, ''))
// First request returns abbreviated metadata
mockPool.intercept({ path: '/is-positive', method: 'GET' }).reply(200, isPositiveMeta)
// Second request returns full metadata
mockPool.intercept({ path: '/is-positive', method: 'GET' }).reply(200, isPositiveMetaFull)
const cacheDir = temporaryDirectory()
{
const { resolveFromNpm } = createResolveFromNpm({
fullMetadata: false,
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {})
expect(resolveResult!.manifest!['scripts']).toBeFalsy()
}
{
const { resolveFromNpm } = createResolveFromNpm({
fullMetadata: true,
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {})
expect(resolveResult!.manifest!['scripts']).toBeTruthy()
}
})
test('resolve when tarball URL is requested from the registry', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({
alias: 'is-positive',
bareSpecifier: `${registries.default}is-positive/-/is-positive-1.0.0.tgz`,
}, {
calcSpecifier: true,
})
expect(resolveResult!.resolvedVia).toBe('npm-registry')
expect(resolveResult!.id).toBe('is-positive@1.0.0')
expect(resolveResult!.latest!.split('.')).toHaveLength(3)
expect(resolveResult!.resolution).toStrictEqual({
integrity: 'sha512-9cI+DmhNhA8ioT/3EJFnt0s1yehnAECyIOXdT+2uQGzcEEBaj8oNmVWj33+ZjPndMIFRQh8JeJlEu1uv5/J7pQ==',
tarball: 'https://registry.npmjs.org/is-positive/-/is-positive-1.0.0.tgz',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('1.0.0')
expect(resolveResult!.normalizedBareSpecifier).toBe(`${registries.default}is-positive/-/is-positive-1.0.0.tgz`)
// The resolve function does not wait for the package meta cache file to be saved
// so we must delay for a bit in order to read it
const meta = await retryLoadJsonFile<any>(path.join(cacheDir, ABBREVIATED_META_DIR, 'registry.npmjs.org/is-positive.jsonl')) // eslint-disable-line @typescript-eslint/no-explicit-any
expect(meta.name).toBeTruthy()
expect(meta.versions).toBeTruthy()
expect(meta['dist-tags']).toBeTruthy()
})
test('resolve when tarball URL is requested from the registry and alias is not specified', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ bareSpecifier: `${registries.default}is-positive/-/is-positive-1.0.0.tgz` }, { calcSpecifier: true })
expect(resolveResult!.resolvedVia).toBe('npm-registry')
expect(resolveResult!.id).toBe('is-positive@1.0.0')
expect(resolveResult!.latest!.split('.')).toHaveLength(3)
expect(resolveResult!.resolution).toStrictEqual({
integrity: 'sha512-9cI+DmhNhA8ioT/3EJFnt0s1yehnAECyIOXdT+2uQGzcEEBaj8oNmVWj33+ZjPndMIFRQh8JeJlEu1uv5/J7pQ==',
tarball: 'https://registry.npmjs.org/is-positive/-/is-positive-1.0.0.tgz',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('1.0.0')
expect(resolveResult!.normalizedBareSpecifier).toBe(`${registries.default}is-positive/-/is-positive-1.0.0.tgz`)
// The resolve function does not wait for the package meta cache file to be saved
// so we must delay for a bit in order to read it
const meta = await retryLoadJsonFile<any>(path.join(cacheDir, ABBREVIATED_META_DIR, 'registry.npmjs.org/is-positive.jsonl')) // eslint-disable-line @typescript-eslint/no-explicit-any
expect(meta.name).toBeTruthy()
expect(meta.versions).toBeTruthy()
expect(meta['dist-tags']).toBeTruthy()
})
test('resolve from local directory when it matches the latest version of the package', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {
projectDir: '/home/istvan/src',
workspacePackages: new Map([
['is-positive', new Map([
['1.0.0', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '1.0.0',
},
}],
])],
]),
})
expect(resolveResult!.resolvedVia).toBe('workspace')
expect(resolveResult!.id).toBe('link:is-positive')
expect(resolveResult!.latest!.split('.')).toHaveLength(3)
expect(resolveResult!.resolution).toStrictEqual({
directory: '/home/istvan/src/is-positive',
type: 'directory',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('1.0.0')
})
test('resolve injected dependency from local directory when it matches the latest version of the package', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', injected: true, bareSpecifier: '1.0.0' }, {
projectDir: '/home/istvan/src',
lockfileDir: '/home/istvan/src',
workspacePackages: new Map([
['is-positive', new Map([
['1.0.0', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '1.0.0',
},
}],
])],
]),
})
// Injected workspace dependencies should still signal that they're resolved
// via the 'workspace' rather than 'local-filesystem'.
expect(resolveResult!.resolvedVia).toBe('workspace')
expect(resolveResult!.id).toBe('file:is-positive')
expect(resolveResult!.latest!.split('.')).toHaveLength(3)
expect(resolveResult!.resolution).toStrictEqual({
directory: 'is-positive',
type: 'directory',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('1.0.0')
})
test('do not resolve from local directory when alwaysTryWorkspacePackages is false', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {
alwaysTryWorkspacePackages: false,
projectDir: '/home/istvan/src',
workspacePackages: new Map([
['is-positive', new Map([
['1.0.0', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '1.0.0',
},
}],
])],
]),
})
expect(resolveResult!.resolvedVia).toBe('npm-registry')
expect(resolveResult!.id).toBe('is-positive@1.0.0')
expect(resolveResult!.latest!.split('.')).toHaveLength(3)
expect(resolveResult!.resolution).toStrictEqual({
integrity: 'sha512-9cI+DmhNhA8ioT/3EJFnt0s1yehnAECyIOXdT+2uQGzcEEBaj8oNmVWj33+ZjPndMIFRQh8JeJlEu1uv5/J7pQ==',
tarball: 'https://registry.npmjs.org/is-positive/-/is-positive-1.0.0.tgz',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('1.0.0')
})
test('resolve from local directory when alwaysTryWorkspacePackages is false but workspace: is used', async () => {
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: 'workspace:*' }, {
alwaysTryWorkspacePackages: false,
projectDir: '/home/istvan/src',
workspacePackages: new Map([
['is-positive', new Map([
['1.0.0', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '1.0.0',
},
}],
])],
]),
})
expect(resolveResult!.resolvedVia).toBe('workspace')
expect(resolveResult!.id).toBe('link:is-positive')
expect(resolveResult!.resolution).toStrictEqual({
directory: '/home/istvan/src/is-positive',
type: 'directory',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('1.0.0')
})
test('resolve from local directory when alwaysTryWorkspacePackages is false but workspace: is used with a different package name', async () => {
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'positive', bareSpecifier: 'workspace:is-positive@*' }, {
alwaysTryWorkspacePackages: false,
projectDir: '/home/istvan/src',
workspacePackages: new Map([
['is-positive', new Map([
['1.0.0', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '1.0.0',
},
}],
])],
]),
})
expect(resolveResult!.resolvedVia).toBe('workspace')
expect(resolveResult!.id).toBe('link:is-positive')
expect(resolveResult!.resolution).toStrictEqual({
directory: '/home/istvan/src/is-positive',
type: 'directory',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('1.0.0')
})
test('use version from the registry if it is newer than the local one', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, {
...isPositiveMeta,
'dist-tags': { latest: '3.1.0' },
})
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({
alias: 'is-positive',
bareSpecifier: '^3.0.0',
}, {
projectDir: '/home/istvan/src',
workspacePackages: new Map([
['is-positive', new Map([
['3.0.0', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '3.0.0',
},
}],
])],
]),
})
expect(resolveResult!.resolvedVia).toBe('npm-registry')
expect(resolveResult!.id).toBe('is-positive@3.1.0')
expect(resolveResult!.latest!.split('.')).toHaveLength(3)
expect(resolveResult!.resolution).toStrictEqual({
integrity: 'sha512-9Qa5b+9n69IEuxk4FiNcavXqkixb9lD03BLtdTeu2bbORnLZQrw+pR/exiSg7SoODeu08yxS47mdZa9ddodNwQ==',
tarball: 'https://registry.npmjs.org/is-positive/-/is-positive-3.1.0.tgz',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('3.1.0')
})
test('preferWorkspacePackages: use version from the workspace even if there is newer version in the registry', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, {
...isPositiveMeta,
'dist-tags': { latest: '3.1.0' },
})
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({
alias: 'is-positive',
bareSpecifier: '^3.0.0',
}, {
preferWorkspacePackages: true,
projectDir: '/home/istvan/src',
workspacePackages: new Map([
['is-positive', new Map([
['3.0.0', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '3.0.0',
},
}],
])],
]),
})
expect(resolveResult).toStrictEqual(
expect.objectContaining({
resolvedVia: 'workspace',
id: 'link:is-positive',
latest: '3.1.0',
})
)
})
test('use local version if it is newer than the latest in the registry', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, {
...isPositiveMeta,
'dist-tags': { latest: '3.1.0' },
})
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
const resolveResult = await resolveFromNpm({
alias: 'is-positive',
bareSpecifier: '^3.0.0',
}, {
projectDir: '/home/istvan/src',
workspacePackages: new Map([
['is-positive', new Map([
['3.2.0', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '3.2.0',
},
}],
])],
]),
})
expect(resolveResult!.resolvedVia).toBe('workspace')
expect(resolveResult!.id).toBe('link:is-positive')
expect(resolveResult!.latest!.split('.')).toHaveLength(3)
expect(resolveResult!.resolution).toStrictEqual({
directory: '/home/istvan/src/is-positive',
type: 'directory',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('3.2.0')
})
test('resolve from local directory when package is not found in the registry', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(404, {})
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1' }, {
projectDir: '/home/istvan/src/foo',
workspacePackages: new Map([
['is-positive', new Map([
['1.0.0', {
rootDir: '/home/istvan/src/is-positive-1.0.0' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '1.0.0',
},
}],
['1.1.0', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '1.1.0',
},
}],
['2.0.0', {
rootDir: '/home/istvan/src/is-positive-2.0.0' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '2.0.0',
},
}],
])],
]),
})
expect(resolveResult!.resolvedVia).toBe('workspace')
expect(resolveResult!.id).toBe('link:../is-positive')
expect(resolveResult!.latest).toBeFalsy()
expect(resolveResult!.resolution).toStrictEqual({
directory: '/home/istvan/src/is-positive',
type: 'directory',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('1.1.0')
})
test('resolve from local directory when package is not found in the registry and latest installed', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(404, {})
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: 'latest' }, {
projectDir: '/home/istvan/src',
workspacePackages: new Map([
['is-positive', new Map([
['1.0.0', {
rootDir: '/home/istvan/src/is-positive-1.0.0' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '1.0.0',
},
}],
['1.1.0', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '1.1.0',
},
}],
['2.0.0', {
rootDir: '/home/istvan/src/is-positive-2.0.0' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '2.0.0',
},
}],
])],
]),
})
expect(resolveResult!.resolvedVia).toBe('workspace')
expect(resolveResult!.id).toBe('link:is-positive-2.0.0')
expect(resolveResult!.latest).toBeFalsy()
expect(resolveResult!.resolution).toStrictEqual({
directory: '/home/istvan/src/is-positive-2.0.0',
type: 'directory',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('2.0.0')
})
test('resolve from local directory when package is not found in the registry and local prerelease available', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(404, {})
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: 'latest' }, {
projectDir: '/home/istvan/src',
workspacePackages: new Map([
['is-positive', new Map([
['3.0.0-alpha.1.2.3', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '3.0.0-alpha.1.2.3',
},
}],
])],
]),
})
expect(resolveResult!.resolvedVia).toBe('workspace')
expect(resolveResult!.id).toBe('link:is-positive')
expect(resolveResult!.latest).toBeFalsy()
expect(resolveResult!.resolution).toStrictEqual({
directory: '/home/istvan/src/is-positive',
type: 'directory',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('3.0.0-alpha.1.2.3')
})
test('resolve from local directory when package is not found in the registry and specific version is requested', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(404, {})
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.1.0' }, {
projectDir: '/home/istvan/src/foo',
workspacePackages: new Map([
['is-positive', new Map([
['1.0.0', {
rootDir: '/home/istvan/src/is-positive-1.0.0' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '1.0.0',
},
}],
['1.1.0', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '1.1.0',
},
}],
['2.0.0', {
rootDir: '/home/istvan/src/is-positive-2.0.0' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '2.0.0',
},
}],
])],
]),
})
expect(resolveResult!.resolvedVia).toBe('workspace')
expect(resolveResult!.id).toBe('link:../is-positive')
expect(resolveResult!.latest).toBeFalsy()
expect(resolveResult!.resolution).toStrictEqual({
directory: '/home/istvan/src/is-positive',
type: 'directory',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('1.1.0')
})
test('resolve from local directory when the requested version is not found in the registry but is available locally', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '100.0.0' }, {
projectDir: '/home/istvan/src/foo',
workspacePackages: new Map([
['is-positive', new Map([
['100.0.0', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '100.0.0',
},
}],
])],
]),
})
expect(resolveResult!.resolvedVia).toBe('workspace')
expect(resolveResult!.id).toBe('link:../is-positive')
expect(resolveResult!.latest).toBeFalsy()
expect(resolveResult!.resolution).toStrictEqual({
directory: '/home/istvan/src/is-positive',
type: 'directory',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('100.0.0')
})
test('workspace protocol: resolve from local directory even when it does not match the latest version of the package', async () => {
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: 'workspace:^3.0.0' }, {
projectDir: '/home/istvan/src',
workspacePackages: new Map([
['is-positive', new Map([
['3.0.0', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '3.0.0',
},
}],
])],
]),
})
expect(resolveResult!.resolvedVia).toBe('workspace')
expect(resolveResult!.id).toBe('link:is-positive')
expect(resolveResult!.latest).toBeFalsy()
expect(resolveResult!.resolution).toStrictEqual({
directory: '/home/istvan/src/is-positive',
type: 'directory',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('3.0.0')
})
test('workspace protocol: resolve from local package that has a pre-release version', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: 'workspace:*' }, {
projectDir: '/home/istvan/src',
workspacePackages: new Map([
['is-positive', new Map([
['3.0.0-alpha.1.2.3', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '3.0.0-alpha.1.2.3',
},
}],
])],
]),
})
expect(resolveResult!.resolvedVia).toBe('workspace')
expect(resolveResult!.id).toBe('link:is-positive')
expect(resolveResult!.latest).toBeFalsy()
expect(resolveResult!.resolution).toStrictEqual({
directory: '/home/istvan/src/is-positive',
type: 'directory',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('3.0.0-alpha.1.2.3')
})
test("workspace protocol: don't resolve from local package that has a pre-release version that don't satisfy the range", async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '2' }, {
projectDir: '/home/istvan/src',
workspacePackages: new Map([
['is-positive', new Map([
['3.0.0-alpha.1.2.3', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '3.0.0-alpha.1.2.3',
},
}],
])],
]),
})
expect(resolveResult!.resolvedVia).toBe('npm-registry')
expect(resolveResult!.id).toBe('is-positive@2.0.0')
expect(resolveResult!.latest).toBeTruthy()
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('2.0.0')
})
test('workspace protocol: resolution fails if there is no matching local package', async () => {
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const projectDir = '/home/istvan/src'
let err!: Error & { code: string }
try {
await resolveFromNpm({ alias: 'is-positive', bareSpecifier: 'workspace:^3.0.0' }, {
projectDir,
workspacePackages: new Map(),
})
} catch (_err: any) { // eslint-disable-line
err = _err
}
expect(err).toBeTruthy()
expect(err.code).toBe('ERR_PNPM_WORKSPACE_PKG_NOT_FOUND')
expect(err.message).toBe(`In ${path.relative(process.cwd(), projectDir)}: "is-positive@workspace:^3.0.0" is in the dependencies but no package named "is-positive" is present in the workspace`)
})
test('workspace protocol: resolution fails if there is no matching local package version', async () => {
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const projectDir = '/home/istvan/src'
let err!: Error & { code: string }
try {
await resolveFromNpm({ alias: 'is-positive', bareSpecifier: 'workspace:^3.0.0' }, {
projectDir,
workspacePackages: new Map([
['is-positive', new Map([
['2.0.0', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '2.0.0',
},
}],
])],
]),
})
} catch (_err: any) { // eslint-disable-line
err = _err
}
expect(err).toBeTruthy()
expect(err.code).toBe('ERR_PNPM_NO_MATCHING_VERSION_INSIDE_WORKSPACE')
expect(err.message).toBe(`In ${path.relative(process.cwd(), projectDir)}: No matching version found for is-positive@workspace:^3.0.0 inside the workspace. Available versions: 2.0.0`)
})
test('workspace protocol: resolution fails if there are no local packages', async () => {
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
let err!: Error
try {
await resolveFromNpm({ alias: 'is-positive', bareSpecifier: 'workspace:^3.0.0' }, {
projectDir: '/home/istvan/src',
})
} catch (_err: any) { // eslint-disable-line
err = _err
}
expect(err).toBeTruthy()
expect(err.message).toBe('Cannot resolve package from workspace because opts.workspacePackages is not defined')
})
test('throws error when package name has "/" but not starts with @scope', async () => {
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir: temporaryDirectory(),
registries,
})
await expect(resolveFromNpm({ alias: 'regenerator/runtime' }, {})).rejects
.toThrow(
new PnpmError('INVALID_PACKAGE_NAME', 'Package name regenerator/runtime is invalid, it should have a @scope')
)
})
test('resolveFromNpm() should always return the name of the package that is specified in the root of the meta', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveBrokenMeta)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '3.1.0' }, {})
expect(resolveResult!.resolvedVia).toBe('npm-registry')
expect(resolveResult!.id).toBe('is-positive@3.1.0')
expect(resolveResult!.latest!.split('.')).toHaveLength(3)
expect(resolveResult!.resolution).toStrictEqual({
integrity: 'sha512-9Qa5b+9n69IEuxk4FiNcavXqkixb9lD03BLtdTeu2bbORnLZQrw+pR/exiSg7SoODeu08yxS47mdZa9ddodNwQ==',
tarball: 'https://registry.npmjs.org/is-positive/-/is-positive-3.1.0.tgz',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('3.1.0')
// The resolve function does not wait for the package meta cache file to be saved
// so we must delay for a bit in order to read it
const meta = await retryLoadJsonFile<any>(path.join(cacheDir, ABBREVIATED_META_DIR, 'registry.npmjs.org/is-positive.jsonl')) // eslint-disable-line @typescript-eslint/no-explicit-any
expect(meta.name).toBeTruthy()
expect(meta.versions).toBeTruthy()
expect(meta['dist-tags']).toBeTruthy()
})
test('request to metadata is retried if the received JSON is broken', async () => {
const localRegistries: Registries = {
default: 'https://registry1.com/',
}
const mockPool = getMockAgent().get(localRegistries.default.replace(/\/$/, ''))
// First request returns broken JSON
mockPool.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, '{')
// Second request (retry) returns valid meta
mockPool.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
retry: { retries: 1 },
storeDir: temporaryDirectory(),
registries: localRegistries,
cacheDir,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {})!
expect(resolveResult?.id).toBe('is-positive@1.0.0')
})
test('request to a package with unpublished versions', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/code-snippet', method: 'GET' })
.reply(200, loadJsonFileSync(f.find('unpublished.json')) as object)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
await expect(resolveFromNpm({ alias: 'code-snippet' }, {})).rejects
.toThrow(
new PnpmError('NO_VERSIONS', 'No versions available for code-snippet because it was unpublished')
)
})
test('request to a package with no versions', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/code-snippet', method: 'GET' })
.reply(200, { name: 'code-snippet' })
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
await expect(resolveFromNpm({ alias: 'code-snippet' }, {})).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'], loadJsonFileSync<any>(f.find('is-positive.json'))) // eslint-disable-line
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
let thrown: any // eslint-disable-line
try {
await resolveFromNpm({ alias: 'is-positive' }, {})
} catch (err) {
thrown = err
}
expect(thrown).toBeTruthy()
expect(thrown.code).toBe('ERR_PNPM_MALFORMED_METADATA')
expect(thrown.message).toBe('Received malformed metadata for "is-positive"')
expect(thrown.hint).toBe('This might mean that the package was unpublished from the registry')
expect(thrown.cause).toBeTruthy()
expect(thrown.cause.message).toContain("Cannot read properties of undefined (reading 'latest')")
})
test('resolveFromNpm() does not fail if the meta file contains no integrity information', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, brokenIntegrity)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '2.0.0' }, {})
expect(resolveResult!.resolvedVia).toBe('npm-registry')
expect(resolveResult!.id).toBe('is-positive@2.0.0')
expect(resolveResult!.latest!.split('.')).toHaveLength(3)
expect(resolveResult!.resolution).toStrictEqual({
integrity: undefined,
tarball: 'https://registry.npmjs.org/is-positive/-/is-positive-2.0.0.tgz',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('2.0.0')
})
test('resolveFromNpm() fails if the meta file contains invalid shasum', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, brokenIntegrity)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
await expect(
resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {})
).rejects.toThrow('Tarball "https://registry.npmjs.org/is-positive/-/is-positive-1.0.0.tgz" has invalid shasum specified in its metadata: a')
})
test('resolveFromNpm() should normalize the registry', async () => {
getMockAgent().get('https://reg.com')
.intercept({ path: '/owner/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: path.join(cacheDir, 'store'),
cacheDir,
registries: {
default: 'https://reg.com/owner',
},
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {})
expect(resolveResult!.resolvedVia).toBe('npm-registry')
expect(resolveResult!.id).toBe('is-positive@1.0.0')
expect(resolveResult!.latest!.split('.')).toHaveLength(3)
expect(resolveResult!.resolution).toStrictEqual({
integrity: 'sha512-9cI+DmhNhA8ioT/3EJFnt0s1yehnAECyIOXdT+2uQGzcEEBaj8oNmVWj33+ZjPndMIFRQh8JeJlEu1uv5/J7pQ==',
tarball: 'https://registry.npmjs.org/is-positive/-/is-positive-1.0.0.tgz',
})
expect(resolveResult!.manifest).toBeTruthy()
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('1.0.0')
})
test('pick lowest version by * when there are only prerelease versions', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, {
versions: {
'1.0.0-alpha.1': {
name: 'is-positive',
version: '1.0.0-alpha.1',
dist: {
tarball: 'https://registry.npmjs.org/is-positive/-/is-positive-1.0.0-alpha.1.tgz',
},
},
},
'dist-tags': {
latest: '1.0.0-alpha.1',
},
})
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: path.join(cacheDir, 'store'),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '*' }, {
pickLowestVersion: true,
})
expect(resolveResult!.resolvedVia).toBe('npm-registry')
expect(resolveResult!.id).toBe('is-positive@1.0.0-alpha.1')
expect(resolveResult!.manifest!.name).toBe('is-positive')
expect(resolveResult!.manifest!.version).toBe('1.0.0-alpha.1')
})
test('throws when workspace package version does not match and package is not found in the registry', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(404, {})
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
await expect(
resolveFromNpm({ alias: 'is-positive', bareSpecifier: '2.0.0' }, {
projectDir: '/home/istvan/src',
update: 'compatible',
workspacePackages: new Map([
['is-positive', new Map([
['1.0.0', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '1.0.0',
},
}],
])],
]),
})
).rejects.toThrow()
})
test('throws NoMatchingVersionError when workspace package version does not match and registry has no matching version', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
await expect(
resolveFromNpm({ alias: 'is-positive', bareSpecifier: '99.0.0' }, {
projectDir: '/home/istvan/src',
update: 'compatible',
workspacePackages: new Map([
['is-positive', new Map([
['1.0.0', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '1.0.0',
},
}],
])],
]),
})
).rejects.toThrow(NoMatchingVersionError)
})
test('resolve from registry when workspace package version does not match the requested version', async () => {
getMockAgent().get(registries.default.replace(/\/$/, ''))
.intercept({ path: '/is-positive', method: 'GET' })
.reply(200, isPositiveMeta)
const cacheDir = temporaryDirectory()
const { resolveFromNpm } = createResolveFromNpm({
storeDir: temporaryDirectory(),
cacheDir,
registries,
})
const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '3.1.0' }, {
projectDir: '/home/istvan/src',
update: 'compatible',
workspacePackages: new Map([
['is-positive', new Map([
['1.0.0', {
rootDir: '/home/istvan/src/is-positive' as ProjectRootDir,
manifest: {
name: 'is-positive',
version: '1.0.0',
},
}],
])],
]),
})
expect(resolveResult!.resolvedVia).toBe('npm-registry')
expect(resolveResult!.id).toBe('is-positive@3.1.0')
})