1import { vol } from 'memfs';
2import path from 'path';
3
4import { BunPackageManager } from '../../node/BunPackageManager';
5import { NpmPackageManager } from '../../node/NpmPackageManager';
6import { PnpmPackageManager } from '../../node/PnpmPackageManager';
7import { YarnPackageManager } from '../../node/YarnPackageManager';
8import { createForProject, resolvePackageManager } from '../nodeManagers';
9import {
10  BUN_LOCK_FILE,
11  NPM_LOCK_FILE,
12  PNPM_LOCK_FILE,
13  PNPM_WORKSPACE_FILE,
14  YARN_LOCK_FILE,
15} from '../nodeWorkspaces';
16
17jest.mock('fs');
18
19describe(createForProject, () => {
20  const projectRoot = '/foo';
21
22  afterEach(() => vol.reset());
23
24  it(`creates npm package manager from options`, () => {
25    expect(createForProject(projectRoot, { npm: true })).toBeInstanceOf(NpmPackageManager);
26  });
27
28  it(`creates yarn package manager from options`, () => {
29    expect(createForProject(projectRoot, { yarn: true })).toBeInstanceOf(YarnPackageManager);
30  });
31
32  it(`creates pnpm package manager from options`, () => {
33    expect(createForProject(projectRoot, { pnpm: true })).toBeInstanceOf(PnpmPackageManager);
34  });
35
36  it(`creates bun package manager from options`, () => {
37    expect(createForProject(projectRoot, { bun: true })).toBeInstanceOf(BunPackageManager);
38  });
39
40  it(`defaults to npm package manager`, () => {
41    expect(createForProject(projectRoot)).toBeInstanceOf(NpmPackageManager);
42  });
43
44  it(`creates npm package manager from project`, () => {
45    vol.fromJSON(
46      {
47        'package.json': JSON.stringify({ name: 'project' }),
48        [NPM_LOCK_FILE]: '',
49      },
50      projectRoot
51    );
52
53    expect(createForProject(projectRoot)).toBeInstanceOf(NpmPackageManager);
54  });
55
56  it(`creates yarn package manager from project`, () => {
57    vol.fromJSON(
58      {
59        'package.json': JSON.stringify({ name: 'project' }),
60        [YARN_LOCK_FILE]: '',
61      },
62      projectRoot
63    );
64
65    expect(createForProject(projectRoot)).toBeInstanceOf(YarnPackageManager);
66  });
67
68  it(`creates pnpm package manager from project`, () => {
69    vol.fromJSON(
70      {
71        'package.json': JSON.stringify({ name: 'project' }),
72        [PNPM_LOCK_FILE]: '',
73      },
74      projectRoot
75    );
76
77    expect(createForProject(projectRoot)).toBeInstanceOf(PnpmPackageManager);
78  });
79
80  it(`creates bun package manager from project`, () => {
81    vol.fromJSON(
82      {
83        'package.json': JSON.stringify({ name: 'project' }),
84        [BUN_LOCK_FILE]: '',
85      },
86      projectRoot
87    );
88
89    expect(createForProject(projectRoot)).toBeInstanceOf(BunPackageManager);
90  });
91
92  it(`creates bun package manager from project using "yarn.lock" and "bun.lockb"`, () => {
93    vol.fromJSON(
94      {
95        'package.json': JSON.stringify({ name: 'project' }),
96        [BUN_LOCK_FILE]: '',
97        [YARN_LOCK_FILE]: '',
98      },
99      projectRoot
100    );
101
102    expect(createForProject(projectRoot)).toBeInstanceOf(BunPackageManager);
103  });
104
105  it(`defaults to npm package manager`, () => {
106    vol.fromJSON(
107      {
108        'package.json': JSON.stringify({ name: 'project' }),
109      },
110      projectRoot
111    );
112
113    expect(createForProject(projectRoot)).toBeInstanceOf(NpmPackageManager);
114  });
115});
116
117describe(resolvePackageManager, () => {
118  const workspaceRoot = path.resolve('/monorepo/');
119  const projectRoot = path.resolve('/monorepo/packages/test/');
120
121  afterEach(() => vol.reset());
122
123  it(`resolves npm from monorepo workspace`, () => {
124    vol.fromJSON(
125      {
126        'packages/test/package.json': JSON.stringify({ name: 'project' }),
127        'package.json': JSON.stringify({
128          private: true,
129          name: 'monorepo',
130          workspaces: ['packages/*'],
131        }),
132        [NPM_LOCK_FILE]: '',
133      },
134      workspaceRoot
135    );
136
137    expect(resolvePackageManager(projectRoot)).toBe('npm');
138    expect(resolvePackageManager(projectRoot, 'npm')).toBe('npm');
139    expect(resolvePackageManager(projectRoot, 'pnpm')).toBeNull();
140    expect(resolvePackageManager(projectRoot, 'yarn')).toBeNull();
141    expect(resolvePackageManager(projectRoot, 'bun')).toBeNull();
142  });
143
144  it(`resolves pnpm from monorepo workspace`, () => {
145    vol.fromJSON(
146      {
147        'packages/test/package.json': JSON.stringify({ name: 'project' }),
148        'package.json': JSON.stringify({
149          private: true,
150          name: 'monorepo',
151        }),
152        [PNPM_LOCK_FILE]: '',
153        [PNPM_WORKSPACE_FILE]: 'packages:\n  - packages/*',
154      },
155      workspaceRoot
156    );
157
158    expect(resolvePackageManager(projectRoot)).toBe('pnpm');
159    expect(resolvePackageManager(projectRoot, 'pnpm')).toBe('pnpm');
160    expect(resolvePackageManager(projectRoot, 'npm')).toBeNull();
161    expect(resolvePackageManager(projectRoot, 'yarn')).toBeNull();
162    expect(resolvePackageManager(projectRoot, 'bun')).toBeNull();
163  });
164
165  it(`resolves yarn from monorepo workspace`, () => {
166    vol.fromJSON(
167      {
168        'packages/test/package.json': JSON.stringify({ name: 'project' }),
169        'package.json': JSON.stringify({
170          private: true,
171          name: 'monorepo',
172          workspaces: ['packages/*'],
173        }),
174        [YARN_LOCK_FILE]: '',
175      },
176      workspaceRoot
177    );
178
179    expect(resolvePackageManager(projectRoot)).toBe('yarn');
180    expect(resolvePackageManager(projectRoot, 'yarn')).toBe('yarn');
181    expect(resolvePackageManager(projectRoot, 'npm')).toBeNull();
182    expect(resolvePackageManager(projectRoot, 'pnpm')).toBeNull();
183    expect(resolvePackageManager(projectRoot, 'bun')).toBeNull();
184  });
185
186  it(`resolves bun from monorepo workspace`, () => {
187    vol.fromJSON(
188      {
189        'packages/test/package.json': JSON.stringify({ name: 'project' }),
190        'package.json': JSON.stringify({
191          private: true,
192          name: 'monorepo',
193          workspaces: ['packages/*'],
194        }),
195        [BUN_LOCK_FILE]: '',
196      },
197      workspaceRoot
198    );
199
200    expect(resolvePackageManager(projectRoot)).toBe('bun');
201    expect(resolvePackageManager(projectRoot, 'bun')).toBe('bun');
202    expect(resolvePackageManager(projectRoot, 'npm')).toBeNull();
203    expect(resolvePackageManager(projectRoot, 'pnpm')).toBeNull();
204    expect(resolvePackageManager(projectRoot, 'yarn')).toBeNull();
205  });
206
207  it(`resolves bun from monorepo workspace using "yarn.lock" and "bun.lockb"`, () => {
208    vol.fromJSON(
209      {
210        'packages/test/package.json': JSON.stringify({ name: 'project' }),
211        'package.json': JSON.stringify({
212          private: true,
213          name: 'monorepo',
214          workspaces: ['packages/*'],
215        }),
216        [BUN_LOCK_FILE]: '',
217        [YARN_LOCK_FILE]: '',
218      },
219      workspaceRoot
220    );
221
222    expect(resolvePackageManager(projectRoot)).toBe('bun');
223    expect(resolvePackageManager(projectRoot, 'bun')).toBe('bun');
224    expect(resolvePackageManager(projectRoot, 'npm')).toBeNull();
225    expect(resolvePackageManager(projectRoot, 'pnpm')).toBeNull();
226
227    // Due to the `yarn.lock` file being present when running `bun install --yarn`,
228    // yarn can be returned as package manager when prefering `yarn`.
229    expect(resolvePackageManager(projectRoot, 'yarn')).toBe('yarn');
230  });
231});
232