diff --git a/.changeset/open-laws-pull.md b/.changeset/open-laws-pull.md new file mode 100644 index 0000000000..ce96e0a5c6 --- /dev/null +++ b/.changeset/open-laws-pull.md @@ -0,0 +1,6 @@ +--- +"@pnpm/npm-resolver": patch +pnpm: patch +--- + +Try to avoid making network calls with preferOffline [#10334](https://github.com/pnpm/pnpm/pull/10334). diff --git a/resolving/npm-resolver/src/pickPackage.ts b/resolving/npm-resolver/src/pickPackage.ts index e76f004956..df392d83c3 100644 --- a/resolving/npm-resolver/src/pickPackage.ts +++ b/resolving/npm-resolver/src/pickPackage.ts @@ -141,13 +141,14 @@ export async function pickPackage ( if (ctx.offline === true || ctx.preferOffline === true || opts.pickLowestVersion) { metaCachedInStore = await limit(async () => loadMeta(pkgMirror)) - if (ctx.offline) { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if (ctx.offline || ctx.preferOffline) { if (metaCachedInStore != null) return { meta: metaCachedInStore, pickedPackage: _pickPackageFromMeta(metaCachedInStore), } - throw new PnpmError('NO_OFFLINE_META', `Failed to resolve ${toRaw(spec)} in package mirror ${pkgMirror}`) + if (ctx.offline) throw new PnpmError('NO_OFFLINE_META', `Failed to resolve ${toRaw(spec)} in package mirror ${pkgMirror}`) } if (metaCachedInStore != null) { diff --git a/resolving/npm-resolver/test/index.ts b/resolving/npm-resolver/test/index.ts index b0ae1d308b..7b0ddaff8e 100644 --- a/resolving/npm-resolver/test/index.ts +++ b/resolving/npm-resolver/test/index.ts @@ -761,6 +761,45 @@ test('when prefer offline is used, meta from store is used, where latest might b nock.cleanAll() }) +test('prefer offline does not make network requests when cached metadata exists', async () => { + nock(registries.default) + .get('/is-positive') + .reply(200, isPositiveMeta) + + const cacheDir = temporaryDirectory() + + { + const { resolveFromNpm } = createResolveFromNpm({ + cacheDir, + registries, + }) + + await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {}) + } + + // Wait for the cache file to be written + await retryLoadJsonFile(path.join(cacheDir, ABBREVIATED_META_DIR, 'registry.npmjs.org/is-positive.v8')) // eslint-disable-line @typescript-eslint/no-explicit-any + + // Clear all mocks - if a network request is made, nock will throw an error + nock.cleanAll() + nock.disableNetConnect() + + { + const { resolveFromNpm } = createResolveFromNpm({ + preferOffline: true, + cacheDir, + registries, + }) + + const resolveResult = await resolveFromNpm({ alias: 'is-positive', bareSpecifier: '1.0.0' }, {}) + expect(resolveResult!.id).toBe('is-positive@1.0.0') + 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', + }) + } +}) + test('error is thrown when package is not found in the registry', async () => { const notExistingPackage = 'foo'