Skip to content

Commit 99a2666

Browse files
Fix getPackageManager logic with workspaces
1 parent ed0a5b4 commit 99a2666

File tree

2 files changed

+28
-11
lines changed

2 files changed

+28
-11
lines changed

packages/cli-kit/src/public/node/node-package-manager.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,19 @@ describe('getPackageManager', () => {
878878
})
879879
})
880880

881+
test('finds pnpm from a nested workspace package when the lockfile is only at the repo root', async () => {
882+
await inTemporaryDirectory(async (tmpDir) => {
883+
await writePackageJSON(tmpDir, {name: 'root'})
884+
await writeFile(joinPath(tmpDir, 'pnpm-lock.yaml'), '')
885+
const nested = joinPath(tmpDir, 'extensions', 'cart-transformer')
886+
await mkdir(nested)
887+
await writePackageJSON(nested, {name: 'cart-transformer'})
888+
889+
const packageManager = await getPackageManager(nested)
890+
expect(packageManager).toEqual('pnpm')
891+
})
892+
})
893+
881894
test('falls back to packageManagerFromUserAgent when no package.json is found', async () => {
882895
await inTemporaryDirectory(async (tmpDir) => {
883896
// Given — no package.json in tmpDir, stub user agent to yarn

packages/cli-kit/src/public/node/node-package-manager.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {AbortError, BugError} from './error.js'
22
import {AbortController, AbortSignal} from './abort.js'
33
import {exec} from './system.js'
4-
import {fileExists, readFile, writeFile, findPathUp, glob} from './fs.js'
4+
import {fileExists, readFile, writeFile, findPathUp, glob, fileExistsSync} from './fs.js'
55
import {dirname, joinPath} from './path.js'
66
import {runWithTimer} from './metadata.js'
77
import {inferPackageManagerForGlobalCLI} from './is-global.js'
@@ -111,22 +111,26 @@ export function packageManagerFromUserAgent(env = process.env): PackageManager {
111111

112112
/**
113113
* Returns the dependency manager used in a directory.
114+
* Walks upward from `fromDirectory` so workspace packages (e.g. `extensions/my-fn/package.json`)
115+
* still resolve to the repo root lockfile (`pnpm-lock.yaml`), not `npm` when only the root has the lock.
114116
* @param fromDirectory - The starting directory
115117
* @returns The dependency manager
116118
*/
117119
export async function getPackageManager(fromDirectory: string): Promise<PackageManager> {
118-
const packageJsonPath = await findPathUp('package.json', {cwd: fromDirectory, type: 'file'})
119-
if (!packageJsonPath) {
120-
return packageManagerFromUserAgent()
120+
let current = fromDirectory
121+
outputDebug(outputContent`Looking for a lockfile in ${outputToken.path(current)}...`)
122+
while (true) {
123+
outputDebug(outputContent`Looking for a lockfile in ${outputToken.path(current)}...`)
124+
if (fileExistsSync(joinPath(current, yarnLockfile))) return 'yarn'
125+
if (fileExistsSync(joinPath(current, pnpmLockfile))) return 'pnpm'
126+
if (fileExistsSync(joinPath(current, bunLockfile))) return 'bun'
127+
if (fileExistsSync(joinPath(current, npmLockfile))) return 'npm'
128+
const parent = dirname(current)
129+
if (parent === current) break
130+
current = parent
121131
}
122132

123-
const directory = dirname(packageJsonPath)
124-
outputDebug(outputContent`Obtaining the dependency manager in directory ${outputToken.path(directory)}...`)
125-
126-
if (await fileExists(joinPath(directory, yarnLockfile))) return 'yarn'
127-
if (await fileExists(joinPath(directory, pnpmLockfile))) return 'pnpm'
128-
if (await fileExists(joinPath(directory, bunLockfile))) return 'bun'
129-
return 'npm'
133+
return packageManagerFromUserAgent()
130134
}
131135

132136
interface InstallNPMDependenciesRecursivelyOptions {

0 commit comments

Comments
 (0)