18d3f3824SCedric van Puttenimport fs from 'fs';
28d3f3824SCedric van Puttenimport path from 'path';
38d3f3824SCedric van Putten
48d3f3824SCedric van Puttenimport {
58d3f3824SCedric van Putten  findPnpmWorkspaceRoot,
68d3f3824SCedric van Putten  findYarnOrNpmWorkspaceRoot,
78d3f3824SCedric van Putten  NPM_LOCK_FILE,
88d3f3824SCedric van Putten  PNPM_LOCK_FILE,
98d3f3824SCedric van Putten  YARN_LOCK_FILE,
106a725f6fSColin McDonnell  BUN_LOCK_FILE,
118d3f3824SCedric van Putten} from './nodeWorkspaces';
128a424bebSJames Ideimport { PackageManagerOptions } from '../PackageManager';
136a725f6fSColin McDonnellimport { BunPackageManager } from '../node/BunPackageManager';
148a424bebSJames Ideimport { NpmPackageManager } from '../node/NpmPackageManager';
158a424bebSJames Ideimport { PnpmPackageManager } from '../node/PnpmPackageManager';
168a424bebSJames Ideimport { YarnPackageManager } from '../node/YarnPackageManager';
178d3f3824SCedric van Putten
186a725f6fSColin McDonnellexport type NodePackageManager =
196a725f6fSColin McDonnell  | NpmPackageManager
206a725f6fSColin McDonnell  | PnpmPackageManager
216a725f6fSColin McDonnell  | YarnPackageManager
226a725f6fSColin McDonnell  | BunPackageManager;
238d3f3824SCedric van Putten
248d3f3824SCedric van Puttenexport type NodePackageManagerForProject = PackageManagerOptions &
258d3f3824SCedric van Putten  Partial<Record<NodePackageManager['name'], boolean>>;
268d3f3824SCedric van Putten
278d3f3824SCedric van Putten/** The order of the package managers to use when resolving automatically */
28*82c7d456SCedric van Puttenexport const RESOLUTION_ORDER: NodePackageManager['name'][] = ['bun', 'yarn', 'npm', 'pnpm'];
298d3f3824SCedric van Putten
308d3f3824SCedric van Putten/**
318d3f3824SCedric van Putten * Resolve the workspace root for a project, if its part of a monorepo.
328d3f3824SCedric van Putten * Optionally, provide a specific packager to only resolve that one specifically.
338d3f3824SCedric van Putten */
348d3f3824SCedric van Puttenexport function findWorkspaceRoot(
358d3f3824SCedric van Putten  projectRoot: string,
368d3f3824SCedric van Putten  preferredManager?: NodePackageManager['name']
378d3f3824SCedric van Putten): string | null {
388d3f3824SCedric van Putten  const strategies: Record<NodePackageManager['name'], (projectRoot: string) => string | null> = {
398d3f3824SCedric van Putten    npm: findYarnOrNpmWorkspaceRoot,
408d3f3824SCedric van Putten    yarn: findYarnOrNpmWorkspaceRoot,
418d3f3824SCedric van Putten    pnpm: findPnpmWorkspaceRoot,
426a725f6fSColin McDonnell    bun: findYarnOrNpmWorkspaceRoot,
438d3f3824SCedric van Putten  };
448d3f3824SCedric van Putten
458d3f3824SCedric van Putten  if (preferredManager) {
468d3f3824SCedric van Putten    return strategies[preferredManager](projectRoot);
478d3f3824SCedric van Putten  }
488d3f3824SCedric van Putten
498d3f3824SCedric van Putten  for (const strategy of RESOLUTION_ORDER) {
508d3f3824SCedric van Putten    const root = strategies[strategy](projectRoot);
518d3f3824SCedric van Putten    if (root) {
528d3f3824SCedric van Putten      return root;
538d3f3824SCedric van Putten    }
548d3f3824SCedric van Putten  }
558d3f3824SCedric van Putten
568d3f3824SCedric van Putten  return null;
578d3f3824SCedric van Putten}
588d3f3824SCedric van Putten
598d3f3824SCedric van Putten/**
608d3f3824SCedric van Putten * Resolve the used node package manager for a project by checking the lockfile.
618d3f3824SCedric van Putten * This also tries to resolve the workspace root, if its part of a monorepo.
628d3f3824SCedric van Putten * Optionally, provide a preferred packager to only resolve that one specifically.
638d3f3824SCedric van Putten */
648d3f3824SCedric van Puttenexport function resolvePackageManager(
658d3f3824SCedric van Putten  projectRoot: string,
668d3f3824SCedric van Putten  preferredManager?: NodePackageManager['name']
678d3f3824SCedric van Putten): NodePackageManager['name'] | null {
688d3f3824SCedric van Putten  const root = findWorkspaceRoot(projectRoot, preferredManager) ?? projectRoot;
698d3f3824SCedric van Putten  const lockFiles: Record<NodePackageManager['name'], string> = {
708d3f3824SCedric van Putten    npm: NPM_LOCK_FILE,
718d3f3824SCedric van Putten    pnpm: PNPM_LOCK_FILE,
728d3f3824SCedric van Putten    yarn: YARN_LOCK_FILE,
736a725f6fSColin McDonnell    bun: BUN_LOCK_FILE,
748d3f3824SCedric van Putten  };
758d3f3824SCedric van Putten
768d3f3824SCedric van Putten  if (preferredManager) {
778d3f3824SCedric van Putten    if (fs.existsSync(path.join(root, lockFiles[preferredManager]))) {
788d3f3824SCedric van Putten      return preferredManager;
798d3f3824SCedric van Putten    }
808d3f3824SCedric van Putten
818d3f3824SCedric van Putten    return null;
828d3f3824SCedric van Putten  }
838d3f3824SCedric van Putten
848d3f3824SCedric van Putten  for (const managerName of RESOLUTION_ORDER) {
858d3f3824SCedric van Putten    if (fs.existsSync(path.join(root, lockFiles[managerName]))) {
868d3f3824SCedric van Putten      return managerName;
878d3f3824SCedric van Putten    }
888d3f3824SCedric van Putten  }
898d3f3824SCedric van Putten
908d3f3824SCedric van Putten  return null;
918d3f3824SCedric van Putten}
928d3f3824SCedric van Putten
938d3f3824SCedric van Putten/**
948d3f3824SCedric van Putten * This creates a Node package manager from the provided options.
958d3f3824SCedric van Putten * If these options are not provided, it will infer the package manager from lockfiles.
968d3f3824SCedric van Putten * When no package manager is found, it falls back to npm.
978d3f3824SCedric van Putten */
988d3f3824SCedric van Puttenexport function createForProject(
998d3f3824SCedric van Putten  projectRoot: string,
1008d3f3824SCedric van Putten  options: NodePackageManagerForProject = {}
1018d3f3824SCedric van Putten): NodePackageManager {
1028d3f3824SCedric van Putten  if (options.npm) {
1038d3f3824SCedric van Putten    return new NpmPackageManager({ cwd: projectRoot, ...options });
1048d3f3824SCedric van Putten  } else if (options.yarn) {
1058d3f3824SCedric van Putten    return new YarnPackageManager({ cwd: projectRoot, ...options });
1068d3f3824SCedric van Putten  } else if (options.pnpm) {
1078d3f3824SCedric van Putten    return new PnpmPackageManager({ cwd: projectRoot, ...options });
1086a725f6fSColin McDonnell  } else if (options.bun) {
1096a725f6fSColin McDonnell    return new BunPackageManager({ cwd: projectRoot, ...options });
1108d3f3824SCedric van Putten  }
1118d3f3824SCedric van Putten
1128d3f3824SCedric van Putten  switch (resolvePackageManager(projectRoot)) {
1138d3f3824SCedric van Putten    case 'npm':
1148d3f3824SCedric van Putten      return new NpmPackageManager({ cwd: projectRoot, ...options });
1158d3f3824SCedric van Putten    case 'pnpm':
1168d3f3824SCedric van Putten      return new PnpmPackageManager({ cwd: projectRoot, ...options });
1178d3f3824SCedric van Putten    case 'yarn':
1188d3f3824SCedric van Putten      return new YarnPackageManager({ cwd: projectRoot, ...options });
1196a725f6fSColin McDonnell    case 'bun':
1206a725f6fSColin McDonnell      return new BunPackageManager({ cwd: projectRoot, ...options });
1218d3f3824SCedric van Putten    default:
1228d3f3824SCedric van Putten      return new NpmPackageManager({ cwd: projectRoot, ...options });
1238d3f3824SCedric van Putten  }
1248d3f3824SCedric van Putten}
125