fix(exec): preserve user execution cwd (#10445)

close #5759
close #10403
This commit is contained in:
Zoltan Kochan
2026-01-12 15:41:17 +01:00
parent 80a608008a
commit 9cbba288fc
5 changed files with 38 additions and 2 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/plugin-commands-script-runners": patch
"pnpm": patch
---
When running "pnpm exec" from a subdirectory of a project, don't change the current working directory to the root of the project [#5759](https://github.com/pnpm/pnpm/issues/5759).

View File

@@ -148,6 +148,7 @@ export type ExecOpts = Required<Pick<Config, 'selectedProjectsGraph'>> & {
implicitlyFellbackFromRun?: boolean
} & Pick<Config,
| 'bin'
| 'cliOptions'
| 'dir'
| 'extraBinPaths'
| 'extraEnv'
@@ -189,7 +190,7 @@ export async function handler (
chunks = chunks.reverse()
}
} else {
chunks = [[opts.dir as ProjectRootDir]]
chunks = [[(opts.cliOptions.dir ?? process.cwd()) as ProjectRootDir]]
const project = await tryReadProjectManifest(opts.dir)
if (project.manifest != null) {
opts.selectedProjectsGraph = {

View File

@@ -161,6 +161,7 @@ export type RunOpts =
& { recursive?: boolean }
& Pick<Config,
| 'bin'
| 'cliOptions'
| 'verifyDepsBeforeRun'
| 'dir'
| 'enablePrePostScripts'

View File

@@ -157,6 +157,7 @@ test('exec inside a workspace package', async () => {
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
process.chdir('project-1')
await exec.handler({
...DEFAULT_OPTS,
dir: path.resolve('project-1'),

View File

@@ -1,5 +1,7 @@
import fs from 'fs'
import path from 'path'
import { prepare, preparePackages } from '@pnpm/prepare'
import { execPnpmSync } from './utils/index.js'
import { execPnpm, execPnpmSync } from './utils/index.js'
test('exec with executionEnv', async () => {
prepare({
@@ -71,3 +73,28 @@ test('recursive exec when some packages define different executionEnv', async ()
'>>> node-version-unset: v19.0.0',
])
})
test("exec should respect the caller's current working directory", async () => {
prepare({
name: 'root',
version: '1.0.0',
})
const projectRoot = process.cwd()
fs.mkdirSync('some-directory', { recursive: true })
const subdirPath = path.join(projectRoot, 'some-directory')
await execPnpm(['install'])
const cmdFilePath = path.join(subdirPath, 'cwd.txt')
execPnpmSync(
['exec', 'node', '-e', `require('fs').writeFileSync(${JSON.stringify(cmdFilePath)}, process.cwd(), 'utf8')`],
{
cwd: subdirPath,
expectSuccess: true,
}
)
expect(fs.readFileSync(cmdFilePath, 'utf8')).toBe(subdirPath)
})