@smbcloud / cli / commits / 052e2e8

Let pnpm recreate node_modules during Next.js deploy

The install step captures pnpm's output, so pnpm has no TTY. When it needs to recreate node_modules (for example after a different pnpm major version installed it), it can't ask for confirmation and aborts with ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY. Pass --config.confirm-modules-purge=false to pnpm so it purges without asking. npm and yarn are left alone. Co-Authored-By: siGit Code <noreply@sigit.si>

Seto Elkahfi committed Sep 30, 2026 at 07:27 UTC 052e2e8683b6fa64f166e8e22d91bd17dd7f7a47
1 file changed +14 -10
crates/cli/src/cloud-deploy/process_deploy_nextjs_ssr.rs
+14 -10
@@ -215,16 +215,20 @@ pub async fn process_deploy_nextjs_ssr(env: Environment, config: Config) -> Resu
215
216 // Capture stdout/stderr so pnpm's output does not interleave with the
217 // spinner animation. On failure the captured output is printed for the user.
218 - let install_output = Command::new(package_manager)
219 - .args(["install", "--ignore-scripts"])
220 - .current_dir(source)
221 - .output()
222 - .map_err(|e| {
223 - anyhow!(fail_message(&format!(
224 - "Failed to spawn '{} install': {}",
225 - package_manager, e
226 - )))
227 - })?;
218 + let mut install_command = Command::new(package_manager);
219 + install_command.args(["install", "--ignore-scripts"]);
220 + // With stdio captured pnpm has no TTY, so when it needs to recreate
221 + // node_modules (store or linker change) it aborts with
222 + // ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY instead of prompting.
223 + if package_manager == "pnpm" {
224 + install_command.arg("--config.confirm-modules-purge=false");
225 + }
226 + let install_output = install_command.current_dir(source).output().map_err(|e| {
227 + anyhow!(fail_message(&format!(
228 + "Failed to spawn '{} install': {}",
229 + package_manager, e
230 + )))
231 + })?;
232
233 if !install_output.status.success() {
234 install_spinner.stop_and_persist(&fail_symbol(), fail_message("Install failed."));