From 833d866be46f12e5e675b08642916d0c9317eb53 Mon Sep 17 00:00:00 2001 From: Julie Antunovic Date: Wed, 20 May 2026 11:29:18 -0400 Subject: [PATCH] Skip generating pnpm-workspace.yaml when template provides one When scaffolding with pnpm, the CLI generates a pnpm-workspace.yaml with workspace package globs. Previously this always overwrote any existing file from the template. Now the CLI skips generation if the template already provides one, allowing templates to ship their own pnpm-workspace.yaml with additional settings (e.g. allowBuilds). --- .../app/src/cli/services/init/init.test.ts | 27 +++++++++++++++++++ packages/app/src/cli/services/init/init.ts | 12 +++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 packages/app/src/cli/services/init/init.test.ts diff --git a/packages/app/src/cli/services/init/init.test.ts b/packages/app/src/cli/services/init/init.test.ts new file mode 100644 index 00000000000..70dcde26cfe --- /dev/null +++ b/packages/app/src/cli/services/init/init.test.ts @@ -0,0 +1,27 @@ +import {ensurePnpmWorkspaceFile} from './init.js' +import {describe, expect, test} from 'vitest' +import {inTemporaryDirectory, writeFile, readFile} from '@shopify/cli-kit/node/fs' +import {joinPath} from '@shopify/cli-kit/node/path' + +describe('ensurePnpmWorkspaceFile', () => { + test('generates file when template has none', async () => { + await inTemporaryDirectory(async (tmpDir) => { + await ensurePnpmWorkspaceFile(tmpDir, ['extensions/*']) + + const content = await readFile(joinPath(tmpDir, 'pnpm-workspace.yaml')) + expect(content).toBe("packages:\n - 'extensions/*'") + }) + }) + + test('does not overwrite existing file from template', async () => { + await inTemporaryDirectory(async (tmpDir) => { + const templateContent = `packages:\n - 'extensions/*'\nallowBuilds:\n esbuild: true\n` + await writeFile(joinPath(tmpDir, 'pnpm-workspace.yaml'), templateContent) + + await ensurePnpmWorkspaceFile(tmpDir, ['extensions/*']) + + const content = await readFile(joinPath(tmpDir, 'pnpm-workspace.yaml')) + expect(content).toBe(templateContent) + }) + }) +}) diff --git a/packages/app/src/cli/services/init/init.ts b/packages/app/src/cli/services/init/init.ts index 74be02232c7..e4d0cc9e34b 100644 --- a/packages/app/src/cli/services/init/init.ts +++ b/packages/app/src/cli/services/init/init.ts @@ -117,8 +117,7 @@ async function init(options: InitOptions) { packageJSON.workspaces = workspacesFolders break case 'pnpm': { - const workspacesContent = workspacesFolders.map((folder) => ` - '${folder}'`).join(`\n`) - await writeFile(joinPath(templateScaffoldDir, 'pnpm-workspace.yaml'), `packages:\n${workspacesContent}`) + await ensurePnpmWorkspaceFile(templateScaffoldDir, workspacesFolders) // Ensure that the installation of dependencies doesn't fail when using // pnpm due to missing peerDependencies. await appendFile(joinPath(templateScaffoldDir, '.npmrc'), `auto-install-peers=true\n`) @@ -273,4 +272,13 @@ function detectAdditionalWorkspacesFolders(directory: string) { return ['web', 'web/frontend'].filter((folder) => fileExistsSync(joinPath(directory, folder))) } +export async function ensurePnpmWorkspaceFile(directory: string, workspacesFolders: string[]) { + const pnpmWorkspacePath = joinPath(directory, 'pnpm-workspace.yaml') + if (await fileExists(pnpmWorkspacePath)) { + return + } + const workspacesContent = workspacesFolders.map((folder) => ` - '${folder}'`).join(`\n`) + await writeFile(pnpmWorkspacePath, `packages:\n${workspacesContent}`) +} + export default init