import fs from 'node:fs' import path from 'node:path' import { afterEach, expect, test } from '@jest/globals' import { add } from '@pnpm/installing.commands' import { prepare, preparePackages } from '@pnpm/prepare' import { addDistTag } from '@pnpm/registry-mock' import { getMockAgent, setupMockAgent, teardownMockAgent } from '@pnpm/testing.mock-agent' import { loadJsonFileSync } from 'load-json-file' import { readYamlFileSync } from 'read-yaml-file' import { DEFAULT_OPTS } from './utils/index.js' // This must be a function because some of its values depend on CWD const createOptions = (saveCatalogName = 'default'): add.AddCommandOptions => ({ ...DEFAULT_OPTS, saveCatalogName, dir: process.cwd(), cacheDir: path.resolve('cache'), storeDir: path.resolve('store'), }) afterEach(async () => { await teardownMockAgent() }) test('saveCatalogName creates new workspace manifest with the new catalogs', async () => { const project = prepare({ name: 'test-save-catalog', version: '0.0.0', private: true, }) await addDistTag({ package: '@pnpm.e2e/foo', version: '100.1.0', distTag: 'latest' }) await add.handler(createOptions(), ['@pnpm.e2e/foo']) expect(loadJsonFileSync('package.json')).toHaveProperty(['dependencies'], { '@pnpm.e2e/foo': 'catalog:', }) expect(readYamlFileSync('pnpm-workspace.yaml')).toHaveProperty(['catalog'], { '@pnpm.e2e/foo': '^100.1.0', }) expect(project.readLockfile()).toStrictEqual(expect.objectContaining({ catalogs: { default: { '@pnpm.e2e/foo': { specifier: '^100.1.0', version: '100.1.0', }, }, }, importers: { '.': { dependencies: { '@pnpm.e2e/foo': { specifier: 'catalog:', version: '100.1.0', }, }, }, }, packages: { '@pnpm.e2e/foo@100.1.0': { resolution: expect.anything(), }, }, })) }) test('saveCatalogName works with different protocols', async () => { const project = prepare({ name: 'test-save-catalog', version: '0.0.0', private: true, }) // Mock the HEAD request that isRepoPublic() in @pnpm/resolving.git-resolver makes. // Without this, transient network failures cause fallback to git+https:// resolution. await setupMockAgent() getMockAgent().enableNetConnect() getMockAgent().get('https://github.com') .intercept({ path: '/kevva/is-positive', method: 'HEAD' }) .reply(200) const options = createOptions() options.registries['@jsr'] = 'https://npm.jsr.io/' await add.handler(options, [ '@pnpm.e2e/foo@100.1.0', 'jsr:@rus/greet@0.0.3', 'github:kevva/is-positive#97edff6', ]) expect(loadJsonFileSync('package.json')).toHaveProperty(['dependencies'], { '@pnpm.e2e/foo': 'catalog:', '@rus/greet': 'catalog:', 'is-positive': 'catalog:', }) expect(readYamlFileSync('pnpm-workspace.yaml')).toHaveProperty(['catalog'], { '@pnpm.e2e/foo': '100.1.0', '@rus/greet': 'jsr:0.0.3', 'is-positive': 'github:kevva/is-positive#97edff6', }) expect(project.readLockfile()).toStrictEqual(expect.objectContaining({ catalogs: { default: { '@pnpm.e2e/foo': { specifier: '100.1.0', version: '100.1.0', }, '@rus/greet': { specifier: 'jsr:0.0.3', version: '0.0.3', }, 'is-positive': { specifier: 'github:kevva/is-positive#97edff6', version: '3.1.0', }, }, }, importers: { '.': { dependencies: { '@pnpm.e2e/foo': { specifier: 'catalog:', version: '100.1.0', }, '@rus/greet': { specifier: 'catalog:', version: '@jsr/rus__greet@0.0.3', }, 'is-positive': { specifier: 'catalog:', version: 'https://codeload.github.com/kevva/is-positive/tar.gz/97edff6f525f192a3f83cea1944765f769ae2678', }, }, }, }, })) }) test('saveCatalogName does not work with local dependencies', async () => { preparePackages([ { name: 'local-dep', version: '0.1.2-local', private: true, }, { name: 'main', version: '0.0.0', private: true, }, ]) process.chdir('main') await add.handler(createOptions(), ['../local-dep']) expect(loadJsonFileSync('package.json')).toStrictEqual({ name: 'main', version: '0.0.0', private: true, dependencies: { 'local-dep': process.platform === 'win32' ? 'link:..\\local-dep' : 'link:../local-dep', }, }) expect(fs.existsSync('pnpm-workspace.yaml')).toBe(false) expect(readYamlFileSync('pnpm-lock.yaml')).not.toHaveProperty(['catalog']) expect(readYamlFileSync('pnpm-lock.yaml')).not.toHaveProperty(['catalogs']) }) test('saveCatalogName with non-default name', async () => { const project = prepare({ name: 'test-save-catalog', version: '0.0.0', private: true, }) await addDistTag({ package: '@pnpm.e2e/foo', version: '100.1.0', distTag: 'latest' }) await add.handler(createOptions('my-catalog'), ['@pnpm.e2e/foo']) expect(loadJsonFileSync('package.json')).toHaveProperty(['dependencies'], { '@pnpm.e2e/foo': 'catalog:my-catalog', }) expect(readYamlFileSync('pnpm-workspace.yaml')).toHaveProperty(['catalogs', 'my-catalog'], { '@pnpm.e2e/foo': '^100.1.0', }) expect(project.readLockfile()).toStrictEqual(expect.objectContaining({ catalogs: { 'my-catalog': { '@pnpm.e2e/foo': { specifier: '^100.1.0', version: '100.1.0', }, }, }, importers: { '.': { dependencies: { '@pnpm.e2e/foo': { specifier: 'catalog:my-catalog', version: '100.1.0', }, }, }, }, packages: { '@pnpm.e2e/foo@100.1.0': { resolution: expect.anything(), }, }, })) })