check packageManager
This commit is contained in:
@@ -1,19 +1,18 @@
|
||||
import { existsSync } from "node:fs";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { isKeyOf } from "@ark/util";
|
||||
import { detect } from "package-manager-detector";
|
||||
import { resolveCommand } from "package-manager-detector/commands";
|
||||
import { log } from "../utils/cli.ts";
|
||||
import { spawn } from "../utils/subprocess.ts";
|
||||
import type { NodePackageManager, NodePrepResult, PrepDefinition } from "./types.ts";
|
||||
|
||||
// package managers that need installation (npm is always available)
|
||||
type InstallablePackageManager = Exclude<NodePackageManager, "npm">;
|
||||
|
||||
// install commands for each package manager
|
||||
const PM_INSTALL_COMMANDS: Record<InstallablePackageManager, string[]> = {
|
||||
pnpm: ["npm", "install", "-g", "pnpm"],
|
||||
yarn: ["npm", "install", "-g", "yarn"],
|
||||
bun: ["npm", "install", "-g", "bun"],
|
||||
// install command templates for each package manager (version placeholder: {version})
|
||||
const nodePackageManagers: Record<NodePackageManager, string[]> = {
|
||||
npm: ["echo", "npm is already installed"],
|
||||
pnpm: ["npm", "install", "-g", "{version}"],
|
||||
yarn: ["npm", "install", "-g", "{version}"],
|
||||
bun: ["npm", "install", "-g", "{version}"],
|
||||
deno: ["sh", "-c", "curl -fsSL https://deno.land/install.sh | sh"],
|
||||
};
|
||||
|
||||
@@ -26,9 +25,40 @@ async function isCommandAvailable(command: string): Promise<boolean> {
|
||||
return result.exitCode === 0;
|
||||
}
|
||||
|
||||
async function installPackageManager(name: InstallablePackageManager): Promise<string | null> {
|
||||
log.info(`📦 installing ${name}...`);
|
||||
const [cmd, ...args] = PM_INSTALL_COMMANDS[name];
|
||||
interface PackageManagerSpec {
|
||||
name: NodePackageManager;
|
||||
installSpec: string; // e.g., "pnpm@8.15.0" (without hash suffix)
|
||||
}
|
||||
|
||||
function getPackageManagerFromPackageJson(): PackageManagerSpec | null {
|
||||
const packageJsonPath = join(process.cwd(), "package.json");
|
||||
try {
|
||||
const content = readFileSync(packageJsonPath, "utf-8");
|
||||
const pkg = JSON.parse(content) as { packageManager?: string };
|
||||
if (!pkg.packageManager) return null;
|
||||
|
||||
// format: "pnpm@8.15.0" or "pnpm@8.15.0+sha512.abc123..."
|
||||
// strip the hash suffix (+sha256.xxx) as npm install doesn't understand it
|
||||
const withoutHash = pkg.packageManager.split("+")[0];
|
||||
const name = withoutHash.split("@")[0];
|
||||
if (isKeyOf(name, nodePackageManagers)) {
|
||||
return { name, installSpec: withoutHash };
|
||||
}
|
||||
log.warning(`unknown packageManager in package.json: ${pkg.packageManager}`);
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function installPackageManager(
|
||||
name: NodePackageManager,
|
||||
installSpec: string
|
||||
): Promise<string | null> {
|
||||
if (name === "npm") return null; // npm is always available
|
||||
log.info(`📦 installing ${installSpec}...`);
|
||||
const [cmd, ...templateArgs] = nodePackageManagers[name];
|
||||
const args = templateArgs.map((arg) => (arg === "{version}" ? installSpec : arg));
|
||||
const result = await spawn({
|
||||
cmd,
|
||||
args,
|
||||
@@ -59,24 +89,29 @@ export const installNodeDependencies: PrepDefinition = {
|
||||
},
|
||||
|
||||
run: async (): Promise<NodePrepResult> => {
|
||||
// detect package manager
|
||||
// check packageManager field in package.json first (takes priority)
|
||||
const fromPackageJson = getPackageManagerFromPackageJson();
|
||||
|
||||
// detect from lockfile as fallback
|
||||
const detected = await detect({ cwd: process.cwd() });
|
||||
if (!detected) {
|
||||
return {
|
||||
language: "node",
|
||||
packageManager: "npm",
|
||||
dependenciesInstalled: false,
|
||||
issues: ["no package manager detected from lockfile"],
|
||||
};
|
||||
|
||||
// prefer package.json field, fall back to lockfile detection, default to npm
|
||||
const packageManager = fromPackageJson?.name || (detected?.name as NodePackageManager) || "npm";
|
||||
const installSpec = fromPackageJson?.installSpec || packageManager;
|
||||
const agent = detected?.agent || packageManager;
|
||||
|
||||
if (fromPackageJson) {
|
||||
log.info(`📦 using packageManager from package.json: ${fromPackageJson.installSpec}`);
|
||||
} else if (detected) {
|
||||
log.info(`📦 detected package manager: ${packageManager} (${agent})`);
|
||||
} else {
|
||||
log.info(`📦 no package manager detected, defaulting to npm`);
|
||||
}
|
||||
|
||||
const packageManager = detected.name as NodePackageManager;
|
||||
log.info(`📦 detected package manager: ${packageManager} (${detected.agent})`);
|
||||
|
||||
// check if package manager is available, install if needed (npm is always available)
|
||||
if (packageManager !== "npm" && !(await isCommandAvailable(packageManager))) {
|
||||
// check if package manager is available, install if needed
|
||||
if (!(await isCommandAvailable(packageManager))) {
|
||||
log.info(`${packageManager} not found, attempting to install...`);
|
||||
const installError = await installPackageManager(packageManager);
|
||||
const installError = await installPackageManager(packageManager, installSpec);
|
||||
if (installError) {
|
||||
return {
|
||||
language: "node",
|
||||
@@ -88,14 +123,13 @@ export const installNodeDependencies: PrepDefinition = {
|
||||
}
|
||||
|
||||
// get the frozen install command (or fallback to regular install)
|
||||
const resolved =
|
||||
resolveCommand(detected.agent, "frozen", []) || resolveCommand(detected.agent, "install", []);
|
||||
const resolved = resolveCommand(agent, "frozen", []) || resolveCommand(agent, "install", []);
|
||||
if (!resolved) {
|
||||
return {
|
||||
language: "node",
|
||||
packageManager,
|
||||
dependenciesInstalled: false,
|
||||
issues: [`no install command found for ${detected.agent}`],
|
||||
issues: [`no install command found for ${agent}`],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user