Files
pnpm/releasing/plugin-commands-deploy/test/shared-lockfile.test.ts
2024-12-26 14:41:18 +07:00

963 lines
31 KiB
TypeScript

import fs from 'fs'
import path from 'path'
import url from 'url'
import { deploy } from '@pnpm/plugin-commands-deploy'
import { install } from '@pnpm/plugin-commands-installation'
import { assertProject } from '@pnpm/assert-project'
import { preparePackages } from '@pnpm/prepare'
import { type PatchFile, type LockfileFile, type LockfilePackageSnapshot } from '@pnpm/lockfile.types'
import { globalWarn } from '@pnpm/logger'
import { filterPackagesFromDir } from '@pnpm/workspace.filter-packages-from-dir'
import { fixtures } from '@pnpm/test-fixtures'
import { type ProjectManifest } from '@pnpm/types'
import { DEFAULT_OPTS } from './utils'
const f = fixtures(__dirname)
const resolvePathAsUrl = (...paths: string[]): string => url.pathToFileURL(path.resolve(...paths)).toString()
beforeEach(async () => {
const logger = await import('@pnpm/logger')
jest.spyOn(logger, 'globalWarn')
})
afterEach(() => {
jest.restoreAllMocks()
})
function readPackageJson (manifestDir: string): unknown {
const manifestPath = path.resolve(manifestDir, 'package.json')
const manifestText = fs.readFileSync(manifestPath, 'utf-8')
return JSON.parse(manifestText)
}
test('deploy with a shared lockfile after full install', async () => {
const projectNames = ['project-1', 'project-2', 'project-3', 'project-4', 'project-5'] as const
const preparedManifests: Record<typeof projectNames[number], ProjectManifest> = {
'project-1': {
name: 'project-1',
version: '1.0.0',
files: ['index.js'],
dependencies: {
'project-2': 'workspace:*',
'is-positive': '1.0.0',
},
devDependencies: {
'project-3': 'workspace:*',
'is-negative': '1.0.0',
},
},
'project-2': {
name: 'project-2',
version: '2.0.0',
files: ['index.js'],
dependencies: {
'project-3': 'workspace:*',
'project-4': 'workspace:*',
'renamed-project-2': 'workspace:project-2@*',
'is-odd': '1.0.0',
},
},
'project-3': {
name: 'project-3',
version: '2.0.0',
files: ['index.js'],
dependencies: {
'project-3': 'workspace:*',
'project-5': 'workspace:*',
'is-odd': '1.0.0',
},
},
'project-4': {
name: 'project-4',
version: '0.0.0',
},
'project-5': {
name: 'project-5',
version: '0.0.0',
},
}
preparePackages(projectNames.map(name => preparedManifests[name]))
for (const name of projectNames) {
fs.writeFileSync(`${name}/test.js`, '', 'utf8')
fs.writeFileSync(`${name}/index.js`, '', 'utf8')
}
const {
allProjects,
allProjectsGraph,
selectedProjectsGraph,
} = await filterPackagesFromDir(process.cwd(), [{ namePattern: 'project-1' }])
await install.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
selectedProjectsGraph: allProjectsGraph,
dir: process.cwd(),
recursive: true,
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
})
expect(fs.existsSync('pnpm-lock.yaml')).toBeTruthy()
const expectedDeployManifest: ProjectManifest = {
name: 'project-1',
version: '1.0.0',
dependencies: {
'project-2': expect.stringMatching(/^project-2@file:/),
'is-positive': '1.0.0',
},
devDependencies: {
'project-3': expect.stringMatching(/^project-3@file:/),
'is-negative': '1.0.0',
},
optionalDependencies: {},
pnpm: {},
}
// deploy prod only
{
fs.rmSync('deploy', { recursive: true, force: true })
await deploy.handler({
...DEFAULT_OPTS,
allProjects,
dir: process.cwd(),
dev: false,
production: true,
recursive: true,
selectedProjectsGraph,
sharedWorkspaceLockfile: true,
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
}, ['deploy'])
const project = assertProject(path.resolve('deploy'))
project.has('project-2')
project.has('is-positive')
project.hasNot('project-3')
project.hasNot('is-negative')
project.hasNot('project-4')
project.hasNot('project-5')
expect(readPackageJson('deploy')).toStrictEqual(expectedDeployManifest)
expect(fs.existsSync('deploy/pnpm-lock.yaml'))
expect(fs.existsSync('deploy/index.js')).toBeTruthy()
expect(fs.existsSync('deploy/test.js')).toBeFalsy()
expect(fs.existsSync('deploy/node_modules/.modules.yaml')).toBeTruthy()
const project2Name = fs.readdirSync('deploy/node_modules/.pnpm').find(name => name.startsWith('project-2@'))
expect(project2Name).toBeDefined()
expect(fs.realpathSync('deploy/node_modules/project-2')).toBe(path.resolve(`deploy/node_modules/.pnpm/${project2Name}/node_modules/project-2`))
expect(fs.existsSync(`deploy/node_modules/.pnpm/${project2Name}/node_modules/project-2/index.js`)).toBeTruthy()
expect(fs.existsSync(`deploy/node_modules/.pnpm/${project2Name}/node_modules/project-2/test.js`)).toBeFalsy()
expect(fs.readdirSync(`deploy/node_modules/.pnpm/${project2Name}/node_modules`).sort()).toStrictEqual([
'is-odd',
'project-2',
'project-3',
'project-4',
'renamed-project-2',
])
expect(readPackageJson(`deploy/node_modules/.pnpm/${project2Name}/node_modules/project-2`)).toStrictEqual(preparedManifests['project-2'])
expect(fs.realpathSync(`deploy/node_modules/.pnpm/${project2Name}/node_modules/renamed-project-2`)).toBe(
path.resolve(`deploy/node_modules/.pnpm/${project2Name}/node_modules/project-2`)
)
const project3Name = fs.readdirSync('deploy/node_modules/.pnpm').find(name => name.startsWith('project-3@'))
expect(project3Name).toBeDefined()
expect(fs.realpathSync(`deploy/node_modules/.pnpm/${project2Name}/node_modules/project-3`)).toBe(
path.resolve(`deploy/node_modules/.pnpm/${project3Name}/node_modules/project-3`)
)
expect(fs.readdirSync(`deploy/node_modules/.pnpm/${project3Name}/node_modules`).sort()).toStrictEqual([
'is-odd',
'project-3',
'project-5',
])
expect(readPackageJson(`deploy/node_modules/.pnpm/${project3Name}/node_modules/project-3`)).toStrictEqual(preparedManifests['project-3'])
const project4Name = fs.readdirSync('deploy/node_modules/.pnpm').find(name => name.startsWith('project-4@'))
expect(project4Name).toBeDefined()
expect(fs.realpathSync(`deploy/node_modules/.pnpm/${project2Name}/node_modules/project-4`)).toBe(
path.resolve(`deploy/node_modules/.pnpm/${project4Name}/node_modules/project-4`)
)
expect(readPackageJson(`deploy/node_modules/.pnpm/${project4Name}/node_modules/project-4`)).toStrictEqual(preparedManifests['project-4'])
const project5Name = fs.readdirSync('deploy/node_modules/.pnpm').find(name => name.startsWith('project-5@'))
expect(project5Name).toBeDefined()
expect(fs.realpathSync(`deploy/node_modules/.pnpm/${project3Name}/node_modules/project-5`)).toBe(
path.resolve(`deploy/node_modules/.pnpm/${project5Name}/node_modules/project-5`)
)
expect(readPackageJson(`deploy/node_modules/.pnpm/${project5Name}/node_modules/project-5`)).toStrictEqual(preparedManifests['project-5'])
expect(globalWarn).not.toHaveBeenCalledWith(expect.stringContaining('Falling back to installing without a lockfile'))
}
// deploy all
{
fs.rmSync('deploy', { recursive: true, force: true })
await deploy.handler({
...DEFAULT_OPTS,
allProjects,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,
sharedWorkspaceLockfile: true,
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
}, ['deploy'])
const project = assertProject(path.resolve('deploy'))
project.has('project-2')
project.has('is-positive')
project.has('project-3')
project.has('is-negative')
project.hasNot('project-4')
project.hasNot('project-5')
expect(readPackageJson('deploy')).toStrictEqual(expectedDeployManifest)
expect(fs.existsSync('deploy/pnpm-lock.yaml'))
expect(fs.existsSync('deploy/index.js')).toBeTruthy()
expect(fs.existsSync('deploy/test.js')).toBeFalsy()
expect(fs.existsSync('deploy/node_modules/.modules.yaml')).toBeTruthy()
const project2Name = fs.readdirSync('deploy/node_modules/.pnpm').find(name => name.startsWith('project-2@'))
expect(project2Name).toBeDefined()
expect(fs.realpathSync('deploy/node_modules/project-2')).toBe(path.resolve(`deploy/node_modules/.pnpm/${project2Name}/node_modules/project-2`))
expect(fs.existsSync(`deploy/node_modules/.pnpm/${project2Name}/node_modules/project-2/index.js`)).toBeTruthy()
expect(fs.existsSync(`deploy/node_modules/.pnpm/${project2Name}/node_modules/project-2/test.js`)).toBeFalsy()
expect(fs.readdirSync(`deploy/node_modules/.pnpm/${project2Name}/node_modules`).sort()).toStrictEqual([
'is-odd',
'project-2',
'project-3',
'project-4',
'renamed-project-2',
])
const project3Name = fs.readdirSync('deploy/node_modules/.pnpm').find(name => name.startsWith('project-3@'))
expect(project3Name).toBeDefined()
expect(fs.realpathSync(`deploy/node_modules/.pnpm/${project2Name}/node_modules/project-3`)).toBe(
path.resolve(`deploy/node_modules/.pnpm/${project3Name}/node_modules/project-3`)
)
expect(project3Name).toBeDefined()
expect(fs.existsSync(`deploy/node_modules/.pnpm/${project3Name}/node_modules/project-3/index.js`)).toBeTruthy()
expect(fs.existsSync(`deploy/node_modules/.pnpm/${project3Name}/node_modules/project-3/test.js`)).toBeFalsy()
expect(fs.readdirSync(`deploy/node_modules/.pnpm/${project3Name}/node_modules`).sort()).toStrictEqual([
'is-odd',
'project-3',
'project-5',
])
expect(fs.realpathSync(`deploy/node_modules/.pnpm/${project3Name}/node_modules/project-3`)).toContain(project3Name)
const project4Name = fs.readdirSync('deploy/node_modules/.pnpm').find(name => name.startsWith('project-4@'))
expect(project4Name).toBeDefined()
expect(fs.realpathSync(`deploy/node_modules/.pnpm/${project2Name}/node_modules/project-4`)).toBe(
path.resolve(`deploy/node_modules/.pnpm/${project4Name}/node_modules/project-4`)
)
const project5Name = fs.readdirSync('deploy/node_modules/.pnpm').find(name => name.startsWith('project-5@'))
expect(project5Name).toBeDefined()
expect(fs.realpathSync(`deploy/node_modules/.pnpm/${project3Name}/node_modules/project-5`)).toBe(
path.resolve(`deploy/node_modules/.pnpm/${project5Name}/node_modules/project-5`)
)
expect(globalWarn).not.toHaveBeenCalledWith(expect.stringContaining('Falling back to installing without a lockfile'))
}
})
test('deploy with a shared lockfile and --prod filter should not fail even if dev workspace package does not exist (#8778)', async () => {
preparePackages([
{
name: 'prod-0',
version: '0.0.0',
private: true,
dependencies: {
'prod-1': 'workspace:*',
},
devDependencies: {
'dev-0': 'workspace:*',
'is-negative': '1.0.0',
},
},
{
name: 'prod-1',
version: '0.0.0',
private: true,
dependencies: {
'is-positive': '1.0.0',
},
devDependencies: {
'dev-1': 'workspace:*',
'is-negative': '1.0.0',
},
},
{
name: 'dev-0',
version: '0.0.0',
private: true,
dependencies: {
'is-negative': '1.0.0',
},
},
{
name: 'dev-1',
version: '0.0.0',
private: true,
},
])
const {
allProjects,
allProjectsGraph,
selectedProjectsGraph,
} = await filterPackagesFromDir(process.cwd(), [{ namePattern: 'prod-0' }])
await install.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
selectedProjectsGraph: allProjectsGraph,
dir: process.cwd(),
recursive: true,
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
})
expect(fs.existsSync('pnpm-lock.yaml')).toBeTruthy()
fs.rmSync('dev-0', { recursive: true })
fs.rmSync('dev-1', { recursive: true })
await deploy.handler({
...DEFAULT_OPTS,
allProjects,
dir: process.cwd(),
recursive: true,
production: true,
dev: false,
selectedProjectsGraph,
sharedWorkspaceLockfile: true,
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
}, ['deploy'])
const project = assertProject(path.resolve('deploy'))
project.has('prod-1')
project.hasNot('dev-0')
project.hasNot('dev-1')
const lockfile = project.readLockfile()
expect(lockfile.importers).toStrictEqual({
'.': {
dependencies: {
'prod-1': {
version: expect.stringMatching(/^prod-1@file:/),
specifier: expect.stringMatching(/^prod-1@file:/),
},
},
devDependencies: {
'dev-0': {
version: expect.stringMatching(/^dev-0@file:/),
specifier: expect.stringMatching(/^dev-0@file:/),
},
'is-negative': {
version: '1.0.0',
specifier: '1.0.0',
},
},
},
} as LockfileFile['importers'])
const manifest = readPackageJson('deploy') as ProjectManifest
expect(manifest).toStrictEqual({
name: 'prod-0',
version: '0.0.0',
private: true,
dependencies: {
'prod-1': expect.stringMatching(/^prod-1@file:/),
},
devDependencies: {
'dev-0': expect.stringMatching(/^dev-0@file:/),
'is-negative': '1.0.0',
},
optionalDependencies: {},
pnpm: {},
} as ProjectManifest)
const prod1Name = fs.readdirSync('deploy/node_modules/.pnpm').find(name => name.includes('prod-1@'))
expect(prod1Name).toBeDefined()
expect(fs.readdirSync(`deploy/node_modules/.pnpm/${prod1Name}/node_modules`).sort()).toStrictEqual(['is-positive', 'prod-1'])
expect(fs.realpathSync('deploy/node_modules/prod-1')).toBe(path.resolve(`deploy/node_modules/.pnpm/${prod1Name}/node_modules/prod-1`))
})
test('deploy with a shared lockfile should correctly handle workspace dependencies that depend on the deployed project', async () => {
preparePackages([
{
name: 'project-0',
version: '0.0.0',
private: true,
dependencies: {
'project-1': 'workspace:*',
},
},
{
name: 'project-1',
version: '0.0.0',
private: true,
dependencies: {
'project-0': 'workspace:*',
},
},
])
const {
allProjects,
allProjectsGraph,
selectedProjectsGraph,
} = await filterPackagesFromDir(process.cwd(), [{ namePattern: 'project-0' }])
await install.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
selectedProjectsGraph: allProjectsGraph,
dir: process.cwd(),
recursive: true,
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
})
expect(fs.existsSync('pnpm-lock.yaml')).toBeTruthy()
await deploy.handler({
...DEFAULT_OPTS,
allProjects,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,
sharedWorkspaceLockfile: true,
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
}, ['deploy'])
const project = assertProject(path.resolve('deploy'))
project.has('project-1')
const lockfile = project.readLockfile()
expect(lockfile.importers).toStrictEqual({
'.': {
dependencies: {
'project-1': {
version: expect.stringMatching(/^project-1@file:/),
specifier: expect.stringMatching(/^project-1@file:/),
},
},
},
} as LockfileFile['importers'])
const manifest = readPackageJson('deploy') as ProjectManifest
expect(manifest).toStrictEqual({
name: 'project-0',
version: '0.0.0',
private: true,
dependencies: {
'project-1': expect.stringMatching(/^project-1@file:/),
},
devDependencies: {},
optionalDependencies: {},
pnpm: {},
} as ProjectManifest)
const project1Name = fs.readdirSync('deploy/node_modules/.pnpm').find(name => name.includes('project-1@'))
expect(project1Name).toBeDefined()
expect(fs.readdirSync(`deploy/node_modules/.pnpm/${project1Name}/node_modules`).sort()).toStrictEqual(['project-0', 'project-1'])
expect(fs.realpathSync(`deploy/node_modules/.pnpm/${project1Name}/node_modules/project-0`)).toBe(path.resolve('deploy'))
expect(fs.realpathSync('deploy/node_modules/project-1')).toBe(path.resolve(`deploy/node_modules/.pnpm/${project1Name}/node_modules/project-1`))
})
test('deploy with a shared lockfile should correctly handle package that depends on itself', async () => {
preparePackages([
{
name: 'project-0',
version: '0.0.0',
private: true,
dependencies: {
'project-0': 'workspace:*',
'renamed-workspace': 'workspace:project-0@*',
'renamed-linked': 'link:.',
},
},
])
const {
allProjects,
allProjectsGraph,
selectedProjectsGraph,
} = await filterPackagesFromDir(process.cwd(), [{ namePattern: 'project-0' }])
await install.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
selectedProjectsGraph: allProjectsGraph,
dir: process.cwd(),
recursive: true,
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
})
expect(fs.existsSync('pnpm-lock.yaml')).toBeTruthy()
await deploy.handler({
...DEFAULT_OPTS,
allProjects,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,
sharedWorkspaceLockfile: true,
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
}, ['deploy'])
const project = assertProject(path.resolve('deploy'))
project.has('project-0')
project.has('renamed-workspace')
project.has('renamed-linked')
const lockfile = project.readLockfile()
expect(lockfile.importers).toStrictEqual({
'.': {
dependencies: {
'project-0': {
version: 'link:.',
specifier: 'link:.',
},
'renamed-workspace': {
version: 'link:.',
specifier: 'link:.',
},
'renamed-linked': {
version: 'link:.',
specifier: 'link:.',
},
},
},
} as LockfileFile['importers'])
const manifest = readPackageJson('deploy') as ProjectManifest
expect(manifest).toStrictEqual({
name: 'project-0',
version: '0.0.0',
private: true,
dependencies: {
'project-0': 'link:.',
'renamed-workspace': 'link:.',
'renamed-linked': 'link:.',
},
devDependencies: {},
optionalDependencies: {},
pnpm: {},
} as ProjectManifest)
expect(fs.realpathSync('deploy/node_modules/project-0')).toBe(path.resolve('deploy'))
expect(fs.realpathSync('deploy/node_modules/renamed-workspace')).toBe(path.resolve('deploy'))
expect(fs.realpathSync('deploy/node_modules/renamed-linked')).toBe(path.resolve('deploy'))
})
test('deploy with a shared lockfile should correctly handle packageExtensions', async () => {
const preparedManifests: Record<string, ProjectManifest> = {
root: {
name: 'root',
version: '0.0.0',
private: true,
pnpm: {
packageExtensions: {
'is-positive': {
dependencies: {
'is-odd': '1.0.0',
'link-to-project-0': 'link:project-0',
'link-to-project-1': 'link:project-1',
'project-0': 'workspace:*',
'project-1': 'workspace:*',
},
},
},
},
},
'project-0': {
name: 'project-0',
version: '0.0.0',
dependencies: {
'project-1': 'workspace:*',
},
},
'project-1': {
name: 'project-1',
version: '0.0.0',
dependencies: {
'is-positive': '1.0.0',
},
},
}
preparePackages([
{
location: '.',
package: preparedManifests.root,
},
preparedManifests['project-0'],
preparedManifests['project-1'],
])
const {
allProjects,
allProjectsGraph,
selectedProjectsGraph,
} = await filterPackagesFromDir(process.cwd(), [{ namePattern: 'project-0' }])
await install.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
selectedProjectsGraph: allProjectsGraph,
dir: process.cwd(),
recursive: true,
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
})
expect(fs.existsSync('pnpm-lock.yaml')).toBeTruthy()
await deploy.handler({
...DEFAULT_OPTS,
allProjects,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,
sharedWorkspaceLockfile: true,
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
}, ['deploy'])
const project = assertProject(path.resolve('deploy'))
project.has('project-1')
const lockfile = project.readLockfile()
expect(lockfile).toHaveProperty(['snapshots', 'is-positive@1.0.0'], {
dependencies: {
'is-odd': '1.0.0',
'link-to-project-0': 'link:.',
'link-to-project-1': expect.stringMatching(/^project-1@file:/),
'project-0': 'link:.',
'project-1': expect.stringMatching(/^project-1@file:/),
},
} as LockfilePackageSnapshot)
const manifest = readPackageJson('deploy') as ProjectManifest
expect(manifest).toStrictEqual({
name: 'project-0',
version: '0.0.0',
dependencies: {
'project-1': expect.stringMatching(/^project-1@file:/),
},
devDependencies: {},
optionalDependencies: {},
pnpm: {},
} as ProjectManifest)
const project1Name = fs.readdirSync('deploy/node_modules/.pnpm').find(name => name.includes('project-1@'))
expect(project1Name).toBeDefined()
expect(fs.realpathSync('deploy/node_modules/.pnpm/is-positive@1.0.0/node_modules/is-odd'))
.toBe(path.resolve('deploy/node_modules/.pnpm/is-odd@1.0.0/node_modules/is-odd'))
expect(fs.realpathSync('deploy/node_modules/.pnpm/is-positive@1.0.0/node_modules/link-to-project-0')).toBe(path.resolve('deploy'))
expect(fs.realpathSync('deploy/node_modules/.pnpm/is-positive@1.0.0/node_modules/link-to-project-1'))
.toBe(path.resolve(`deploy/node_modules/.pnpm/${project1Name}/node_modules/project-1`))
expect(fs.realpathSync('deploy/node_modules/.pnpm/is-positive@1.0.0/node_modules/project-0')).toBe(path.resolve('deploy'))
expect(fs.realpathSync('deploy/node_modules/.pnpm/is-positive@1.0.0/node_modules/project-1'))
.toBe(path.resolve(`deploy/node_modules/.pnpm/${project1Name}/node_modules/project-1`))
expect(readPackageJson('deploy/node_modules/.pnpm/is-positive@1.0.0/node_modules/link-to-project-0')).toStrictEqual(manifest)
expect(readPackageJson('deploy/node_modules/.pnpm/is-positive@1.0.0/node_modules/link-to-project-1')).toStrictEqual(preparedManifests['project-1'])
expect(readPackageJson('deploy/node_modules/.pnpm/is-positive@1.0.0/node_modules/project-0')).toStrictEqual(manifest)
expect(readPackageJson('deploy/node_modules/.pnpm/is-positive@1.0.0/node_modules/project-1')).toStrictEqual(preparedManifests['project-1'])
})
test('deploy with a shared lockfile should correctly handle patchedDependencies', async () => {
const preparedManifests: Record<string, ProjectManifest> = {
root: {
name: 'root',
version: '0.0.0',
private: true,
pnpm: {
patchedDependencies: {
'is-positive': '__patches__/is-positive.patch',
},
},
},
'project-0': {
name: 'project-0',
version: '0.0.0',
dependencies: {
'project-1': 'workspace:*',
},
},
'project-1': {
name: 'project-1',
version: '0.0.0',
dependencies: {
'is-positive': '1.0.0',
},
},
}
preparePackages([
{
location: '.',
package: preparedManifests.root,
},
preparedManifests['project-0'],
preparedManifests['project-1'],
])
f.copy('is-positive.patch', '__patches__/is-positive.patch')
const {
allProjects,
allProjectsGraph,
selectedProjectsGraph,
} = await filterPackagesFromDir(process.cwd(), [{ namePattern: 'project-0' }])
await install.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
selectedProjectsGraph: allProjectsGraph,
dir: process.cwd(),
recursive: true,
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
})
expect(fs.existsSync('pnpm-lock.yaml')).toBeTruthy()
await deploy.handler({
...DEFAULT_OPTS,
allProjects,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,
sharedWorkspaceLockfile: true,
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
}, ['deploy'])
const project = assertProject(path.resolve('deploy'))
project.has('project-1')
const lockfile = project.readLockfile()
expect(lockfile.patchedDependencies).toStrictEqual({
'is-positive': {
hash: expect.any(String),
path: '../__patches__/is-positive.patch',
},
} as Record<string, PatchFile>)
const patchFile = lockfile.patchedDependencies['is-positive']
const manifest = readPackageJson('deploy') as ProjectManifest
expect(manifest).toStrictEqual({
name: 'project-0',
version: '0.0.0',
dependencies: {
'project-1': expect.stringMatching(/^project-1@file:/),
},
devDependencies: {},
optionalDependencies: {},
pnpm: {
patchedDependencies: {
'is-positive': '../__patches__/is-positive.patch',
},
},
} as ProjectManifest)
const project1Name = fs.readdirSync('deploy/node_modules/.pnpm').find(name => name.includes('project-1@'))
expect(project1Name).toBeDefined()
if (process.platform !== 'win32') {
expect(fs.realpathSync(`deploy/node_modules/.pnpm/${project1Name}/node_modules/is-positive`)).toBe(
path.resolve(`deploy/node_modules/.pnpm/is-positive@1.0.0_patch_hash=${patchFile.hash}/node_modules/is-positive`)
)
}
expect(
fs.readFileSync(`deploy/node_modules/.pnpm/${project1Name}/node_modules/is-positive/PATCH.txt`, 'utf-8')
.trim()
).toBe('added by pnpm patch-commit')
})
test('deploy with a shared lockfile that has peer dependencies suffix in workspace package dependency paths', async () => {
const preparedManifests: Record<string, ProjectManifest> = {
'project-0': {
name: 'project-0',
version: '0.0.0',
dependencies: {
'project-1': 'workspace:*',
},
peerDependencies: {
'project-1': '*',
'project-2': '*',
},
},
'project-1': {
name: 'project-1',
version: '0.0.0',
dependencies: {
'is-positive': '1.0.0',
'project-2': 'workspace:*',
},
peerDependencies: {
'is-negative': '>=1.0.0',
'project-2': '*',
},
},
'project-2': {
name: 'project-2',
version: '0.0.0',
peerDependencies: {
'is-positive': '>=1.0.0',
},
},
}
preparePackages(['project-0', 'project-1', 'project-2'].map(name => ({
location: `packages/${name}`,
package: preparedManifests[name],
})))
const {
allProjects,
allProjectsGraph,
selectedProjectsGraph,
} = await filterPackagesFromDir(process.cwd(), [{ namePattern: 'project-0' }])
await install.handler({
...DEFAULT_OPTS,
allProjects,
allProjectsGraph,
selectedProjectsGraph: allProjectsGraph,
dedupeInjectedDeps: false,
dir: process.cwd(),
recursive: true,
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
})
expect(assertProject('.').readLockfile()).toMatchObject({
importers: {
'packages/project-0': {
dependencies: {
'project-1': {
version: 'file:packages/project-1(is-negative@1.0.0)(project-2@file:packages/project-2(is-positive@1.0.0))',
},
'project-2': {
version: 'file:packages/project-2(is-positive@1.0.0)',
},
},
},
'packages/project-1': {
dependencies: {
'project-2': {
version: 'file:packages/project-2(is-positive@1.0.0)',
},
},
},
},
packages: {
'project-1@file:packages/project-1': {
resolution: {
type: 'directory',
directory: 'packages/project-1',
},
},
'project-2@file:packages/project-2': {
resolution: {
type: 'directory',
directory: 'packages/project-2',
},
},
},
snapshots: {
'project-1@file:packages/project-1(is-negative@1.0.0)(project-2@file:packages/project-2(is-positive@1.0.0))': {
dependencies: {
'project-2': 'file:packages/project-2(is-positive@1.0.0)',
},
},
'project-2@file:packages/project-2(is-positive@1.0.0)': {},
},
})
await deploy.handler({
...DEFAULT_OPTS,
allProjects,
dir: process.cwd(),
recursive: true,
selectedProjectsGraph,
sharedWorkspaceLockfile: true,
lockfileDir: process.cwd(),
workspaceDir: process.cwd(),
}, ['deploy'])
const project = assertProject(path.resolve('deploy'))
project.has('project-1')
project.has('project-2')
expect(project.readLockfile()).toMatchObject({
importers: {
'.': {
dependencies: {
'project-1': {
specifier: `project-1@${resolvePathAsUrl('packages/project-1')}(is-negative@1.0.0)(project-2@file:packages/project-2(is-positive@1.0.0))`,
version: `project-1@${resolvePathAsUrl('packages/project-1')}(is-negative@1.0.0)(project-2@file:packages/project-2(is-positive@1.0.0))`,
},
'project-2': {
specifier: `project-2@${resolvePathAsUrl('packages/project-2')}(is-positive@1.0.0)`,
version: `project-2@${resolvePathAsUrl('packages/project-2')}(is-positive@1.0.0)`,
},
},
},
},
packages: {
[`project-1@${resolvePathAsUrl('packages/project-1')}`]: {
resolution: {
type: 'directory',
directory: '../packages/project-1',
},
},
[`project-2@${resolvePathAsUrl('packages/project-2')}`]: {
resolution: {
type: 'directory',
directory: '../packages/project-2',
},
},
},
snapshots: {
[`project-1@${resolvePathAsUrl('packages/project-1')}(is-negative@1.0.0)(project-2@file:packages/project-2(is-positive@1.0.0))`]: {
dependencies: {
'project-2': `project-2@${resolvePathAsUrl('packages/project-2')}(is-positive@1.0.0)`,
},
},
[`project-2@${resolvePathAsUrl('packages/project-2')}(is-positive@1.0.0)`]: {},
},
})
expect(readPackageJson('deploy')).toStrictEqual({
name: 'project-0',
version: '0.0.0',
dependencies: {
'project-1': `project-1@${resolvePathAsUrl('packages/project-1')}(is-negative@1.0.0)(project-2@file:packages/project-2(is-positive@1.0.0))`,
'project-2': `project-2@${resolvePathAsUrl('packages/project-2')}(is-positive@1.0.0)`,
},
devDependencies: {},
optionalDependencies: {},
pnpm: {},
} as ProjectManifest)
expect(readPackageJson('deploy/node_modules/project-1')).toStrictEqual(preparedManifests['project-1'])
expect(readPackageJson('deploy/node_modules/project-2')).toStrictEqual(preparedManifests['project-2'])
const project1Names = fs.readdirSync('deploy/node_modules/.pnpm').filter(name => name.includes('project-1@'))
expect(project1Names).not.toStrictEqual([])
for (const name of project1Names) {
expect(readPackageJson(`deploy/node_modules/.pnpm/${name}/node_modules/project-1`)).toStrictEqual(preparedManifests['project-1'])
}
const project2Names = fs.readdirSync('deploy/node_modules/.pnpm').filter(name => name.includes('project-2@'))
expect(project2Names).not.toStrictEqual([])
for (const name of project2Names) {
expect(readPackageJson(`deploy/node_modules/.pnpm/${name}/node_modules/project-2`)).toStrictEqual(preparedManifests['project-2'])
}
})