From ffa27ddcbc32275a333a1408b2eb7f6a5f68b3ca Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 15 Feb 2026 04:32:55 +0000 Subject: [PATCH] refactor(update): dedupe package manager detection --- src/infra/detect-package-manager.ts | 29 +++++++++++++++++++++++++++++ src/infra/update-check.ts | 24 ++---------------------- src/infra/update-runner.ts | 24 ++---------------------- 3 files changed, 33 insertions(+), 44 deletions(-) create mode 100644 src/infra/detect-package-manager.ts diff --git a/src/infra/detect-package-manager.ts b/src/infra/detect-package-manager.ts new file mode 100644 index 000000000..f1f96180c --- /dev/null +++ b/src/infra/detect-package-manager.ts @@ -0,0 +1,29 @@ +import fs from "node:fs/promises"; +import path from "node:path"; + +export type DetectedPackageManager = "pnpm" | "bun" | "npm"; + +export async function detectPackageManager(root: string): Promise { + try { + const raw = await fs.readFile(path.join(root, "package.json"), "utf-8"); + const parsed = JSON.parse(raw) as { packageManager?: string }; + const pm = parsed?.packageManager?.split("@")[0]?.trim(); + if (pm === "pnpm" || pm === "bun" || pm === "npm") { + return pm; + } + } catch { + // ignore + } + + const files = await fs.readdir(root).catch((): string[] => []); + if (files.includes("pnpm-lock.yaml")) { + return "pnpm"; + } + if (files.includes("bun.lockb")) { + return "bun"; + } + if (files.includes("package-lock.json")) { + return "npm"; + } + return null; +} diff --git a/src/infra/update-check.ts b/src/infra/update-check.ts index 8525f53bf..d20621039 100644 --- a/src/infra/update-check.ts +++ b/src/infra/update-check.ts @@ -2,6 +2,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { runCommandWithTimeout } from "../process/exec.js"; import { fetchWithTimeout } from "../utils/fetch-timeout.js"; +import { detectPackageManager as detectPackageManagerImpl } from "./detect-package-manager.js"; import { parseSemver } from "./runtime-guard.js"; import { channelToNpmTag, type UpdateChannel } from "./update-channels.js"; @@ -58,28 +59,7 @@ async function exists(p: string): Promise { } async function detectPackageManager(root: string): Promise { - try { - const raw = await fs.readFile(path.join(root, "package.json"), "utf-8"); - const parsed = JSON.parse(raw) as { packageManager?: string }; - const pm = parsed?.packageManager?.split("@")[0]?.trim(); - if (pm === "pnpm" || pm === "bun" || pm === "npm") { - return pm; - } - } catch { - // ignore - } - - const files = await fs.readdir(root).catch((): string[] => []); - if (files.includes("pnpm-lock.yaml")) { - return "pnpm"; - } - if (files.includes("bun.lockb")) { - return "bun"; - } - if (files.includes("package-lock.json")) { - return "npm"; - } - return "unknown"; + return (await detectPackageManagerImpl(root)) ?? "unknown"; } async function detectGitRoot(root: string): Promise { diff --git a/src/infra/update-runner.ts b/src/infra/update-runner.ts index ac774a141..46bbf8a19 100644 --- a/src/infra/update-runner.ts +++ b/src/infra/update-runner.ts @@ -6,6 +6,7 @@ import { resolveControlUiDistIndexHealth, resolveControlUiDistIndexPathForRoot, } from "./control-ui-assets.js"; +import { detectPackageManager as detectPackageManagerImpl } from "./detect-package-manager.js"; import { trimLogTail } from "./restart-sentinel.js"; import { channelToNpmTag, @@ -254,28 +255,7 @@ async function findPackageRoot(candidates: string[]) { } async function detectPackageManager(root: string) { - try { - const raw = await fs.readFile(path.join(root, "package.json"), "utf-8"); - const parsed = JSON.parse(raw) as { packageManager?: string }; - const pm = parsed?.packageManager?.split("@")[0]?.trim(); - if (pm === "pnpm" || pm === "bun" || pm === "npm") { - return pm; - } - } catch { - // ignore - } - - const files = await fs.readdir(root).catch((): string[] => []); - if (files.includes("pnpm-lock.yaml")) { - return "pnpm"; - } - if (files.includes("bun.lockb")) { - return "bun"; - } - if (files.includes("package-lock.json")) { - return "npm"; - } - return "npm"; + return (await detectPackageManagerImpl(root)) ?? "npm"; } type RunStepOptions = {