@reliverse/rse
Version:
@reliverse/rse is your all-in-one companion for bootstrapping and improving any kind of projects (especially web apps built with frameworks like Next.js) — whether you're kicking off something new or upgrading an existing app. It is also a little AI-power
188 lines (187 loc) • 5.91 kB
JavaScript
import path from "@reliverse/pathkit";
import fs from "@reliverse/relifso";
import { execa } from "execa";
const cache = /* @__PURE__ */ new Map();
async function pathExists(p) {
try {
await fs.access(p);
return true;
} catch {
return false;
}
}
async function checkPMVersion(pm, includeGlobalBun = true) {
if (pm === "bun" && !includeGlobalBun) return null;
const cacheKey = `has_global_${pm}`;
if (cache.has(cacheKey)) {
return cache.get(cacheKey);
}
try {
const { stdout } = await execa(pm, ["--version"]);
if (/^\d+.\d+.\d+$/.test(stdout)) {
cache.set(cacheKey, stdout);
return stdout;
}
} catch (_error) {
cache.set(cacheKey, null);
}
return null;
}
async function detectLockFile(projectPath) {
const cacheKey = `lockfile_${projectPath}`;
if (cache.has(cacheKey)) {
return cache.get(cacheKey);
}
const lockFiles = await Promise.all([
pathExists(path.join(projectPath, "yarn.lock")),
pathExists(path.join(projectPath, "package-lock.json")),
pathExists(path.join(projectPath, "pnpm-lock.yaml")),
pathExists(path.join(projectPath, "bun.lock"))
]);
let result = null;
const [isYarn, isNpm, isPnpm, isBun] = lockFiles;
if (isBun) {
const version = await checkPMVersion("bun");
result = {
packageManager: "bun",
source: "lockfile",
...version && { version }
};
} else if (isPnpm) {
const version = await checkPMVersion("pnpm");
result = {
packageManager: "pnpm",
source: "lockfile",
...version && { version }
};
} else if (isYarn) {
const version = await checkPMVersion("yarn");
result = {
packageManager: "yarn",
source: "lockfile",
...version && { version }
};
} else if (isNpm) {
const version = await checkPMVersion("npm");
result = {
packageManager: "npm",
source: "lockfile",
...version && { version }
};
}
cache.set(cacheKey, result);
return result;
}
async function detectPackageManagers(projectPath, options = {}) {
const cacheKey = `detect_${projectPath}_${options.includeGlobalBun}`;
if (cache.has(cacheKey)) {
return cache.get(cacheKey);
}
const detected = [];
const lockFileResult = await detectLockFile(projectPath);
if (lockFileResult) {
detected.push(lockFileResult);
}
try {
const { stdout } = await execa("bun", ["--version"]);
if (stdout) {
detected.push({
packageManager: "bun",
source: "runtime",
version: stdout.trim()
});
}
} catch {
}
const pkgManagerUserAgent = process.env.npm_config_user_agent ?? "";
if (pkgManagerUserAgent.startsWith("yarn")) {
detected.push({ packageManager: "yarn", source: "env" });
} else if (pkgManagerUserAgent.startsWith("pnpm")) {
detected.push({ packageManager: "pnpm", source: "env" });
} else if (pkgManagerUserAgent.startsWith("bun")) {
detected.push({ packageManager: "bun", source: "env" });
}
const configFiles = await Promise.all([
pathExists(path.join(projectPath, ".npmrc")),
pathExists(path.join(projectPath, ".yarnrc")),
pathExists(path.join(projectPath, ".yarnrc.yml")),
pathExists(path.join(projectPath, ".pnpmfile.cjs")),
pathExists(path.join(projectPath, ".pnpmfile.js")),
pathExists(path.join(projectPath, "bunfig.toml"))
]);
const [
hasNpmrc,
hasYarnrc,
hasYarnrcYml,
hasPnpmfileCjs,
hasPnpmfileJs,
hasBunConfig
] = configFiles;
if (hasNpmrc) detected.push({ packageManager: "npm", source: "config" });
if (hasYarnrc || hasYarnrcYml)
detected.push({ packageManager: "yarn", source: "config" });
if (hasPnpmfileCjs || hasPnpmfileJs)
detected.push({ packageManager: "pnpm", source: "config" });
if (hasBunConfig) detected.push({ packageManager: "bun", source: "config" });
const versionChecks = await Promise.all([
checkPMVersion("npm"),
checkPMVersion("pnpm"),
checkPMVersion("yarn"),
checkPMVersion("bun", options.includeGlobalBun)
]);
const pmList = ["npm", "pnpm", "yarn", "bun"];
pmList.forEach((pm, index) => {
const version = versionChecks[index];
if (version) {
detected.push({
packageManager: pm,
source: "global",
version
});
}
});
cache.set(cacheKey, detected);
return detected;
}
export async function getUserPkgManager(projectPath, options = {}) {
const defaultPM = {
packageManager: "npm",
source: "default"
};
const priorityOrder = ["bun", "pnpm", "yarn", "npm"];
if (projectPath) {
try {
const detected = await detectPackageManagers(projectPath, options);
if (detected.length === 0) return defaultPM;
return priorityOrder.reduce((selected, pm) => {
if (selected) return selected;
return detected.find((d) => d.packageManager === pm) ?? null;
}, null) ?? detected[0] ?? defaultPM;
} catch (error) {
console.error("Error detecting package manager:", error);
return defaultPM;
}
}
let currentDir = process.cwd();
const { root } = path.parse(currentDir);
while (currentDir !== root) {
const detected = await detectPackageManagers(currentDir, options);
if (detected.length > 0) {
return priorityOrder.reduce((selected, pm) => {
if (selected) return selected;
return detected.find((d) => d.packageManager === pm) ?? null;
}, null) ?? detected[0] ?? defaultPM;
}
currentDir = path.dirname(currentDir);
}
return defaultPM;
}
export async function getAllPkgManagers(projectPath, options = {}) {
try {
const detected = await detectPackageManagers(projectPath, options);
return detected.length > 0 ? detected : [{ packageManager: "npm", source: "default" }];
} catch (error) {
console.error("Error detecting package managers:", error);
return [{ packageManager: "npm", source: "default" }];
}
}