Unified interface and implementations for npm, yarn, pnpm, and bun package managers with package manager-specific optimizations and consistent API.
All Node.js package managers implement the core PackageManager interface.
interface PackageManager {
/** The options for this package manager */
readonly options: PackageManagerOptions;
/** Run any command using the package manager */
runAsync(command: string[], options?: SpawnOptions): SpawnPromise<SpawnResult>;
/** Invoke a binary from within a package, like "eslint" or "jest" */
runBinAsync(command: string[], options?: SpawnOptions): SpawnPromise<SpawnResult>;
/** Get the version of the used package manager */
versionAsync(): Promise<string>;
/** Get a single configuration property from the package manager */
getConfigAsync(key: string): Promise<string>;
/** Remove the lock file within the project, if any */
removeLockfileAsync(): Promise<void>;
/** Get the workspace root package manager, if this project is within a workspace/monorepo */
workspaceRoot(): PackageManager | null;
/** Install all current dependencies using the package manager */
installAsync(): Promise<SpawnResult> | SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
/** Uninstall all current dependencies by removing the folder containing the packages */
uninstallAsync(): Promise<void>;
/** Add a normal dependency to the project */
addAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
/** Add a development dependency to the project */
addDevAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
/** Add a global dependency to the environment */
addGlobalAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
/** Remove a normal dependency from the project */
removeAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
/** Remove a development dependency from the project */
removeDevAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
/** Remove a global dependency from the environments */
removeGlobalAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
}
interface PackageManagerOptions extends SpawnOptions {
/** If the package manager should run in silent mode */
silent?: boolean;
/** The logging method used to communicate the command which is executed */
log?: (...args: any[]) => void;
}Abstract base class providing common functionality for all Node.js package managers.
abstract class BasePackageManager implements PackageManager {
readonly silent: boolean;
readonly log?: (...args: any) => void;
readonly options: PackageManagerOptions;
/** Get the name of the package manager */
abstract readonly name: string;
/** Get the executable binary of the package manager */
abstract readonly bin: string;
/** Get the lockfile for this package manager */
abstract readonly lockFile: string;
constructor(options?: PackageManagerOptions);
runAsync(command: string[], options?: SpawnOptions): SpawnPromise<SpawnResult>;
runBinAsync(command: string[], options?: SpawnOptions): SpawnPromise<SpawnResult>;
versionAsync(): Promise<string>;
getConfigAsync(key: string): Promise<string>;
removeLockfileAsync(): Promise<void>;
installAsync(flags?: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
uninstallAsync(): Promise<void>;
}Implementation for npm package manager with npm-specific optimizations.
class NpmPackageManager extends BasePackageManager {
readonly name = 'npm';
readonly bin = 'npm';
readonly lockFile = 'package-lock.json';
workspaceRoot(): NpmPackageManager | null;
addAsync(namesOrFlags?: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
addDevAsync(namesOrFlags?: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
addGlobalAsync(namesOrFlags?: string[]): SpawnPromise<SpawnResult>;
removeAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult>;
removeDevAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult>;
removeGlobalAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult>;
runBinAsync(command: string[], options?: SpawnOptions): SpawnPromise<SpawnResult>;
}Usage Examples:
import { NpmPackageManager } from '@expo/package-manager';
const npm = new NpmPackageManager({ cwd: '/path/to/project' });
// Install dependencies
await npm.installAsync();
// Add packages with version specifications
await npm.addAsync(['express@^4.18.0', 'lodash']);
// Use npx to run binaries
await npm.runBinAsync(['typescript', '--version']);Implementation for Yarn package manager with offline support and workspace detection.
class YarnPackageManager extends BasePackageManager {
readonly name = 'yarn';
readonly bin = 'yarnpkg';
readonly lockFile = 'yarn.lock';
workspaceRoot(): YarnPackageManager | null;
installAsync(flags?: string[]): PendingSpawnPromise<SpawnResult>;
addAsync(namesOrFlags?: string[]): PendingSpawnPromise<SpawnResult>;
addDevAsync(namesOrFlags?: string[]): PendingSpawnPromise<SpawnResult>;
addGlobalAsync(namesOrFlags?: string[]): PendingSpawnPromise<SpawnResult>;
removeAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult>;
removeDevAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult>;
removeGlobalAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult>;
}Usage Examples:
import { YarnPackageManager } from '@expo/package-manager';
const yarn = new YarnPackageManager({ cwd: '/path/to/project' });
// Yarn automatically handles offline mode
await yarn.installAsync();
// Add development dependencies
await yarn.addDevAsync(['@types/node', 'typescript']);
// Global package management
await yarn.addGlobalAsync(['nodemon']);
await yarn.removeGlobalAsync(['old-global-package']);Implementation for pnpm package manager with CI-specific optimizations.
class PnpmPackageManager extends BasePackageManager {
readonly name = 'pnpm';
readonly bin = 'pnpm';
readonly lockFile = 'pnpm-lock.yaml';
workspaceRoot(): PnpmPackageManager | null;
installAsync(namesOrFlags?: string[]): SpawnPromise<SpawnResult>;
addAsync(namesOrFlags?: string[]): SpawnPromise<SpawnResult>;
addDevAsync(namesOrFlags?: string[]): SpawnPromise<SpawnResult>;
addGlobalAsync(namesOrFlags?: string[]): SpawnPromise<SpawnResult>;
removeAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult>;
removeDevAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult>;
removeGlobalAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult>;
}Usage Examples:
import { PnpmPackageManager } from '@expo/package-manager';
const pnpm = new PnpmPackageManager({ cwd: '/path/to/project' });
// In CI, automatically adds --no-frozen-lockfile if not specified
await pnpm.installAsync();
// Standard dependency management
await pnpm.addAsync(['fastify', 'zod']);
await pnpm.removeAsync(['unused-package']);Implementation for Bun package manager.
class BunPackageManager extends BasePackageManager {
readonly name = 'bun';
readonly bin = 'bun';
readonly lockFile = 'bun.lockb';
workspaceRoot(): BunPackageManager | null;
installAsync(namesOrFlags?: string[]): SpawnPromise<SpawnResult>;
addAsync(namesOrFlags?: string[]): SpawnPromise<SpawnResult>;
addDevAsync(namesOrFlags?: string[]): SpawnPromise<SpawnResult>;
addGlobalAsync(namesOrFlags?: string[]): SpawnPromise<SpawnResult>;
removeAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult>;
removeDevAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult>;
removeGlobalAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult>;
}Usage Examples:
import { BunPackageManager } from '@expo/package-manager';
const bun = new BunPackageManager({ cwd: '/path/to/project' });
// Fast installation with Bun
await bun.installAsync();
// Add packages
await bun.addAsync(['hono', '@types/bun']);
// Development dependencies
await bun.addDevAsync(['bun-types']);Workspace Management:
// Get workspace root manager
const rootManager = manager.workspaceRoot();
if (rootManager) {
// Install at workspace root
await rootManager.installAsync();
}Configuration Access:
// Get package manager configuration
const registry = await manager.getConfigAsync('registry');
const version = await manager.versionAsync();Cleanup Operations:
// Remove dependencies and clean up
await manager.removeAsync(['old-package']);
await manager.removeLockfileAsync();
await manager.uninstallAsync(); // Removes node_modulesRunning Commands:
// Run package manager commands
await manager.runAsync(['audit', 'fix']);
// Run package binaries
await manager.runBinAsync(['eslint', 'src/']);type NodePackageManager =
| NpmPackageManager
| PnpmPackageManager
| YarnPackageManager
| BunPackageManager;