Skip to content
Open
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
43 changes: 9 additions & 34 deletions packages/cli-kit/src/public/node/node-package-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -846,11 +846,7 @@ describe('getPackageManager', () => {
test('finds if npm is being used', async () => {
await inTemporaryDirectory(async (tmpDir) => {
// Given
const packageJSON = {name: 'mock name'}

// When
await writePackageJSON(tmpDir, packageJSON)
mockedCaptureOutput.mockReturnValueOnce(Promise.resolve(tmpDir))
await writePackageJSON(tmpDir, {name: 'mock name'})

// Then
const packageManager = await getPackageManager(tmpDir)
Expand All @@ -861,13 +857,8 @@ describe('getPackageManager', () => {
test('finds if yarn is being used', async () => {
await inTemporaryDirectory(async (tmpDir) => {
// Given
const packageJSON = {name: 'mock name'}
const yarnLock = joinPath(tmpDir, 'yarn.lock')

// When
await writePackageJSON(tmpDir, packageJSON)
await writeFile(yarnLock, '')
mockedCaptureOutput.mockReturnValueOnce(Promise.resolve(tmpDir))
await writePackageJSON(tmpDir, {name: 'mock name'})
await writeFile(joinPath(tmpDir, 'yarn.lock'), '')

// Then
const packageManager = await getPackageManager(tmpDir)
Expand All @@ -878,48 +869,32 @@ describe('getPackageManager', () => {
test('finds if pnpm is being used', async () => {
await inTemporaryDirectory(async (tmpDir) => {
// Given
const packageJSON = {name: 'mock name'}
const pnpmLock = joinPath(tmpDir, 'pnpm-lock.yaml')

// When
await writePackageJSON(tmpDir, packageJSON)
await writeFile(pnpmLock, '')
mockedCaptureOutput.mockReturnValueOnce(Promise.resolve(tmpDir))
await writePackageJSON(tmpDir, {name: 'mock name'})
await writeFile(joinPath(tmpDir, 'pnpm-lock.yaml'), '')

// Then
const packageManager = await getPackageManager(tmpDir)
expect(packageManager).toEqual('pnpm')
})
})

test('falls back to packageManagerFromUserAgent when npm prefix fails', async () => {
test('falls back to packageManagerFromUserAgent when no package.json is found', async () => {
await inTemporaryDirectory(async (tmpDir) => {
// Given
// Given — no package.json in tmpDir, stub user agent to yarn
vi.stubEnv('npm_config_user_agent', 'yarn/1.22.0')

// Mock npm prefix to fail
mockedCaptureOutput.mockRejectedValueOnce(new Error('npm prefix failed'))

// When
const packageManager = await getPackageManager(tmpDir)

// Then
expect(mockedCaptureOutput).toHaveBeenCalledWith('npm', ['prefix'], expect.anything())
expect(packageManager).toEqual('yarn')

// Restore original implementation
vi.unstubAllEnvs()
expect(packageManager).toEqual('yarn')
})
})

test("tries to guess the package manager from the environment if it can't find a package.json", async () => {
await inTemporaryDirectory(async (tmpDir) => {
// Given
const subDirectory = joinPath(tmpDir, 'subdir')
await mkdir(subDirectory)
mockedCaptureOutput.mockReturnValueOnce(Promise.resolve(tmpDir))

// When/Then
// When/Then — no package.json, falls back to user agent
const packageManager = await getPackageManager(tmpDir)
// pnpm is used locally and in CI
expect(packageManager).toEqual('pnpm')
Expand Down
36 changes: 11 additions & 25 deletions packages/cli-kit/src/public/node/node-package-manager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {AbortError, BugError} from './error.js'
import {AbortController, AbortSignal} from './abort.js'
import {captureOutput, exec} from './system.js'
import {exec} from './system.js'
import {fileExists, readFile, writeFile, findPathUp, glob} from './fs.js'
import {dirname, joinPath} from './path.js'
import {runWithTimer} from './metadata.js'
Expand Down Expand Up @@ -115,32 +115,18 @@ export function packageManagerFromUserAgent(env = process.env): PackageManager {
* @returns The dependency manager
*/
export async function getPackageManager(fromDirectory: string): Promise<PackageManager> {
let directory: string | undefined
let packageJson: string | undefined
try {
directory = await captureOutput('npm', ['prefix'], {cwd: fromDirectory})
outputDebug(outputContent`Obtaining the dependency manager in directory ${outputToken.path(directory)}...`)
packageJson = joinPath(directory, 'package.json')
// eslint-disable-next-line no-catch-all/no-catch-all
} catch {
// if problems locating directoy/package file, we use user agent instead
}

if (!directory || !packageJson || !(await fileExists(packageJson))) {
const packageJsonPath = await findPathUp('package.json', {cwd: fromDirectory, type: 'file'})
if (!packageJsonPath) {
return packageManagerFromUserAgent()
}
const yarnLockPath = joinPath(directory, yarnLockfile)
const pnpmLockPath = joinPath(directory, pnpmLockfile)
const bunLockPath = joinPath(directory, bunLockfile)
if (await fileExists(yarnLockPath)) {
return 'yarn'
} else if (await fileExists(pnpmLockPath)) {
return 'pnpm'
} else if (await fileExists(bunLockPath)) {
return 'bun'
} else {
return 'npm'
}

const directory = dirname(packageJsonPath)
outputDebug(outputContent`Obtaining the dependency manager in directory ${outputToken.path(directory)}...`)

if (await fileExists(joinPath(directory, yarnLockfile))) return 'yarn'
if (await fileExists(joinPath(directory, pnpmLockfile))) return 'pnpm'
if (await fileExists(joinPath(directory, bunLockfile))) return 'bun'
return 'npm'
}

interface InstallNPMDependenciesRecursivelyOptions {
Expand Down
Loading