fix: pnpm init skips devEngines.packageManager in workspace subpackages (#11285)

The devEngines.packageManager field should only live at the workspace root.
When init runs inside a subpackage, the field is now omitted.
This commit is contained in:
Zoltan Kochan
2026-04-17 14:30:28 +02:00
committed by GitHub
parent 1c95cb6392
commit 2dd8712c87
3 changed files with 44 additions and 2 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/workspace.commands": patch
"pnpm": patch
---
`pnpm init` no longer adds the `devEngines.packageManager` field when run inside a workspace subpackage. The field is only added to the workspace root's `package.json`.

View File

@@ -59,6 +59,7 @@ export type InitOptions =
& Partial<Pick<Config,
| 'initPackageManager'
| 'initType'
| 'workspaceDir'
>> & {
bare?: boolean
initAuthorName?: string
@@ -77,10 +78,13 @@ export async function handler (opts: InitOptions, params?: string[]): Promise<st
// Using cwd instead of the dir option because the dir option
// is set to the first parent directory that has a package.json file
// But --dir option from cliOptions should be respected.
const manifestPath = path.join(opts.cliOptions.dir ?? process.cwd(), 'package.json')
const initDir = opts.cliOptions.dir ?? process.cwd()
const manifestPath = path.join(initDir, 'package.json')
if (fs.existsSync(manifestPath)) {
throw new PnpmError('PACKAGE_JSON_EXISTS', 'package.json already exists')
}
const isWorkspaceSubpackage = opts.workspaceDir != null &&
path.resolve(opts.workspaceDir) !== path.resolve(initDir)
const manifest: ProjectManifest = opts.bare
? {}
: {
@@ -102,7 +106,7 @@ export async function handler (opts: InitOptions, params?: string[]): Promise<st
const initConfig = getInitConfig(opts)
const packageJson = { ...manifest, ...initConfig }
if (opts.initPackageManager) {
if (opts.initPackageManager && !isWorkspaceSubpackage) {
packageJson.devEngines = {
...packageJson.devEngines,
packageManager: {

View File

@@ -90,6 +90,38 @@ test('init a new package.json with init-package-manager=false', async () => {
expect(manifest).not.toHaveProperty('devEngines')
})
test('init a new package.json in a workspace subpackage does not add devEngines', async () => {
prepareEmpty()
const workspaceDir = process.cwd()
const subpackageDir = path.join(workspaceDir, 'packages/foo')
fs.mkdirSync(subpackageDir, { recursive: true })
await init.handler({
cliOptions: { dir: subpackageDir },
initPackageManager: true,
workspaceDir,
})
const manifest = loadJsonFileSync<ProjectManifest>(path.join(subpackageDir, 'package.json'))
expect(manifest).toBeTruthy()
expect(manifest).not.toHaveProperty('devEngines')
expect(manifest).not.toHaveProperty('packageManager')
})
test('init a new package.json at the workspace root adds devEngines', async () => {
prepareEmpty()
const workspaceDir = process.cwd()
await init.handler({
cliOptions: {},
initPackageManager: true,
workspaceDir,
})
const manifest = loadJsonFileSync<ProjectManifest>(path.resolve('package.json'))
expect(manifest.devEngines?.packageManager).toEqual({
name: 'pnpm',
version: expect.stringMatching(/^\^\d+\.\d+\.\d+/),
onFail: 'download',
})
})
test('init a new package.json with init-type=module', async () => {
prepareEmpty()
await init.handler({ cliOptions: {}, initType: 'module' })