Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI: Use closest lockfile to determine package manager #20464

Merged
merged 2 commits into from
Jan 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 79 additions & 16 deletions code/lib/cli/src/js-package-manager/JsPackageManagerFactory.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { sync as spawnSync } from 'cross-spawn';
import { sync as findUpSync } from 'find-up';
import path from 'path';
import { JsPackageManagerFactory } from './JsPackageManagerFactory';
import { NPMProxy } from './NPMProxy';
import { PNPMProxy } from './PNPMProxy';
Expand All @@ -11,9 +12,12 @@ const spawnSyncMock = spawnSync as jest.Mock;

jest.mock('find-up');
const findUpSyncMock = findUpSync as unknown as jest.Mock;
findUpSyncMock.mockReturnValue(undefined);

describe('JsPackageManagerFactory', () => {
beforeEach(() => {
findUpSyncMock.mockReturnValue(undefined);
});

describe('getPackageManager', () => {
describe('return an NPM proxy', () => {
it('when `useNpm` option is used', () => {
Expand Down Expand Up @@ -58,9 +62,7 @@ describe('JsPackageManagerFactory', () => {
});

// There is only a package-lock.json
findUpSyncMock.mockImplementation((file) =>
file === 'package-lock.json' ? 'found' : undefined
);
findUpSyncMock.mockImplementation(() => '/Users/johndoe/Documents/package-lock.json');

expect(JsPackageManagerFactory.getPackageManager()).toBeInstanceOf(NPMProxy);
});
Expand Down Expand Up @@ -103,15 +105,45 @@ describe('JsPackageManagerFactory', () => {
});

// There is only a pnpm-lock.yaml
findUpSyncMock.mockImplementation((file) => {
if (file === 'pnpm-lock.yaml') {
return 'found';
}
return undefined;
});
findUpSyncMock.mockImplementation(() => '/Users/johndoe/Documents/pnpm-lock.yaml');

expect(JsPackageManagerFactory.getPackageManager()).toBeInstanceOf(PNPMProxy);
});

it('when a pnpm-lock.yaml file is closer than a yarn.lock', () => {
// Allow find-up to work as normal, we'll set the cwd to our fixture package
findUpSyncMock.mockImplementation(jest.requireActual('find-up').sync);

spawnSyncMock.mockImplementation((command) => {
// Yarn is ok
if (command === 'yarn') {
return {
status: 0,
output: '1.22.4',
};
}
// NPM is ok
if (command === 'npm') {
return {
status: 0,
output: '6.5.12',
};
}
// PNPM is ok
if (command === 'pnpm') {
return {
status: 0,
output: '7.9.5',
};
}
// Unknown package manager is ko
return {
status: 1,
};
});
const fixture = path.join(__dirname, 'fixtures', 'pnpm-workspace', 'package');
expect(JsPackageManagerFactory.getPackageManager({}, fixture)).toBeInstanceOf(PNPMProxy);
});
});

describe('return a Yarn 1 proxy', () => {
Expand Down Expand Up @@ -184,12 +216,45 @@ describe('JsPackageManagerFactory', () => {
});

// There is a yarn.lock
findUpSyncMock.mockImplementation((file) =>
file === 'yarn.lock' ? '/Users/johndoe/Documents/yarn.lock' : undefined
);
findUpSyncMock.mockImplementation(() => '/Users/johndoe/Documents/yarn.lock');

expect(JsPackageManagerFactory.getPackageManager()).toBeInstanceOf(Yarn1Proxy);
});

it('when multiple lockfiles are in a project, prefers yarn', () => {
// Allow find-up to work as normal, we'll set the cwd to our fixture package
findUpSyncMock.mockImplementation(jest.requireActual('find-up').sync);

spawnSyncMock.mockImplementation((command) => {
// Yarn is ok
if (command === 'yarn') {
return {
status: 0,
output: '1.22.4',
};
}
// NPM is ok
if (command === 'npm') {
return {
status: 0,
output: '6.5.12',
};
}
// PNPM is ok
if (command === 'pnpm') {
return {
status: 0,
output: '7.9.5',
};
}
// Unknown package manager is ko
return {
status: 1,
};
});
const fixture = path.join(__dirname, 'fixtures', 'multiple-lockfiles');
expect(JsPackageManagerFactory.getPackageManager({}, fixture)).toBeInstanceOf(Yarn1Proxy);
});
});

describe('return a Yarn 2 proxy', () => {
Expand Down Expand Up @@ -259,9 +324,7 @@ describe('JsPackageManagerFactory', () => {
});

// There is a yarn.lock
findUpSyncMock.mockImplementation((file) =>
file === 'yarn.lock' ? '/Users/johndoe/Documents/yarn.lock' : undefined
);
findUpSyncMock.mockImplementation(() => '/Users/johndoe/Documents/yarn.lock');

expect(JsPackageManagerFactory.getPackageManager()).toBeInstanceOf(Yarn2Proxy);
});
Expand Down
18 changes: 12 additions & 6 deletions code/lib/cli/src/js-package-manager/JsPackageManagerFactory.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import path from 'node:path';
import { sync as spawnSync } from 'cross-spawn';
import { sync as findUpSync } from 'find-up';

import { NPMProxy } from './NPMProxy';

import { PNPMProxy } from './PNPMProxy';

import type { JsPackageManager } from './JsPackageManager';
import { type PackageManagerName } from './JsPackageManager';

import { Yarn2Proxy } from './Yarn2Proxy';

import { Yarn1Proxy } from './Yarn1Proxy';

const NPM_LOCKFILE = 'package-lock.json';
const PNPM_LOCKFILE = 'pnpm-lock.yaml';
const YARN_LOCKFILE = 'yarn.lock';

export class JsPackageManagerFactory {
public static getPackageManager(
{ force, useNpm }: { force?: PackageManagerName; useNpm?: boolean } = {},
Expand All @@ -31,17 +34,20 @@ export class JsPackageManagerFactory {
}

const yarnVersion = getYarnVersion(cwd);
const hasYarnLockFile = Boolean(findUpSync('yarn.lock', { cwd }));
const hasPNPMLockFile = Boolean(findUpSync('pnpm-lock.yaml', { cwd }));

const closestLockfilePath = findUpSync([YARN_LOCKFILE, PNPM_LOCKFILE, NPM_LOCKFILE], {
cwd,
});
const closestLockfile = closestLockfilePath && path.basename(closestLockfilePath);

const hasNPMCommand = hasNPM(cwd);
const hasPNPMCommand = hasPNPM(cwd);

if (yarnVersion && (hasYarnLockFile || (!hasNPMCommand && !hasPNPMCommand))) {
if (yarnVersion && (closestLockfile === YARN_LOCKFILE || (!hasNPMCommand && !hasPNPMCommand))) {
return yarnVersion === 1 ? new Yarn1Proxy({ cwd }) : new Yarn2Proxy({ cwd });
}

if (hasPNPMCommand && hasPNPMLockFile) {
if (hasPNPMCommand && closestLockfile === PNPM_LOCKFILE) {
return new PNPMProxy({ cwd });
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "multiple-lockfiles"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "pnpm-workspace"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "test-fixture"
}
Empty file.