Skip to content

Commit 13e143e

Browse files
Merge pull request #7600 from Shopify/fix-init-install-in-place
[Fix] Move scaffolded project to its final directory before installing deps in `app init`
2 parents b52e0cb + 36deafe commit 13e143e

2 files changed

Lines changed: 34 additions & 8 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/app': patch
3+
---
4+
5+
Fix `shopify app init` leaving dangling `node_modules` symlinks on Windows when using `pnpm` (and similarly affected package managers). The scaffolded project is now moved to its final directory before dependencies are installed, so package-manager-managed symlinks/junctions resolve to the final location instead of the temporary scaffold path.

packages/app/src/cli/services/init/init.ts

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
mkdir,
2828
moveFile,
2929
readFile,
30+
rmdir,
3031
writeFile,
3132
} from '@shopify/cli-kit/node/fs'
3233
import {joinPath, normalizePath} from '@shopify/cli-kit/node/path'
@@ -170,32 +171,52 @@ async function init(options: InitOptions) {
170171
})
171172
}
172173

174+
// Move the scaffolded template into its final directory BEFORE installing
175+
// dependencies. pnpm (and other package managers) create absolute-path
176+
// junctions/symlinks on Windows, so installing in the temp dir and then
177+
// moving the tree orphans every link under node_modules/.pnpm/*.
178+
let outputDirectoryCreated = false
179+
tasks.push({
180+
title: 'Preparing project directory',
181+
task: async () => {
182+
await ensureAppDirectoryIsAvailable(outputDirectory, hyphenizedName)
183+
outputDirectoryCreated = true
184+
await moveFile(templateScaffoldDir, outputDirectory)
185+
},
186+
})
187+
173188
tasks.push(
174189
{
175190
title: `Installing dependencies with ${packageManager}`,
176191
task: async () => {
177-
await getDeepInstallNPMTasks({from: templateScaffoldDir, packageManager})
192+
await getDeepInstallNPMTasks({from: outputDirectory, packageManager})
178193
},
179194
},
180195
{
181196
title: 'Cleaning up',
182197
task: async () => {
183-
await cleanup(templateScaffoldDir, packageManager)
198+
await cleanup(outputDirectory, packageManager)
184199
},
185200
},
186201
{
187202
title: 'Initializing a Git repository...',
188203
task: async () => {
189-
await initializeGitRepository(templateScaffoldDir)
204+
await initializeGitRepository(outputDirectory)
190205
},
191206
},
192207
)
193208

194-
await renderTasks(tasks)
195-
196-
// Ensure the app directory is available before moving the template scaffold
197-
await ensureAppDirectoryIsAvailable(outputDirectory, hyphenizedName)
198-
await moveFile(templateScaffoldDir, outputDirectory)
209+
try {
210+
await renderTasks(tasks)
211+
} catch (error) {
212+
// If a task failed after the project was moved to its final directory,
213+
// remove the partial project so the user isn't left with a half-baked
214+
// scaffold (no node_modules, no cleanup, no git init).
215+
if (outputDirectoryCreated) {
216+
await rmdir(outputDirectory).catch(() => {})
217+
}
218+
throw error
219+
}
199220
})
200221

201222
let app: OrganizationApp

0 commit comments

Comments
 (0)