Package manager integration for installing, removing, and managing npm packages within CLI applications, with support for both npm and Yarn.
Add packages to project dependencies with automatic package manager detection.
/**
* Add packages to project dependencies
* @param packageName - Single package name or array of package names
* @param options - Installation options
* @returns Promise resolving to operation result
*/
add(
packageName: string | string[],
options: GluegunPackageManagerOptions
): Promise<GluegunPackageManagerResult>;
interface GluegunPackageManagerOptions {
/** Install as development dependency */
dev?: boolean;
/** Perform dry run without actual installation */
dryRun?: boolean;
/** Target directory for package installation */
dir?: string;
/** Force specific package manager ('npm' or 'yarn') */
force?: 'npm' | 'yarn';
}
interface GluegunPackageManagerResult {
/** Operation completed successfully */
success: boolean;
/** Command that was executed */
command: string;
/** Command output (stdout) */
stdout: string;
/** Error message if operation failed */
error?: string;
}Package Installation Examples:
import { packageManager } from "gluegun";
// Install single production dependency
const result = await packageManager.add("express", {});
if (result.success) {
console.log("Express installed successfully");
} else {
console.error(`Installation failed: ${result.error}`);
}
// Install multiple development dependencies
const devResult = await packageManager.add(
["jest", "eslint", "@types/node"],
{ dev: true }
);
// Install with specific package manager
const yarnResult = await packageManager.add("lodash", {
force: 'yarn'
});
// Install in specific directory
const dirResult = await packageManager.add("react", {
dir: "./frontend"
});
// Dry run to see what would be installed
const dryResult = await packageManager.add("typescript", {
dev: true,
dryRun: true
});
console.log(`Would execute: ${dryResult.command}`);Remove packages from project dependencies.
/**
* Remove packages from project dependencies
* @param packageName - Single package name or array of package names
* @param options - Removal options
* @returns Promise resolving to operation result
*/
remove(
packageName: string | string[],
options: GluegunPackageManagerOptions
): Promise<GluegunPackageManagerResult>;Package Removal Examples:
import { packageManager } from "gluegun";
// Remove single package
const result = await packageManager.remove("unused-package", {});
// Remove multiple packages
const multiResult = await packageManager.remove([
"old-dependency",
"deprecated-tool",
"unused-lib"
], {});
// Remove from specific directory
const dirResult = await packageManager.remove("react", {
dir: "./old-frontend"
});
// Dry run removal
const dryResult = await packageManager.remove("big-package", {
dryRun: true
});
console.log(`Would execute: ${dryResult.command}`);Utilities for detecting and working with available package managers.
/**
* Check if Yarn package manager is available
* @returns True if yarn is installed and available
*/
hasYarn(): boolean;Package Manager Detection Examples:
import { packageManager, print } from "gluegun";
// Automatic package manager selection
const useYarn = packageManager.hasYarn();
print.info(`Using package manager: ${useYarn ? 'yarn' : 'npm'}`);
// Install with preferred package manager
const preferredManager = packageManager.hasYarn() ? 'yarn' : 'npm';
await packageManager.add("express", { force: preferredManager });
// Conditional logic based on package manager
if (packageManager.hasYarn()) {
// Yarn-specific operations
await packageManager.add("package", { force: 'yarn' });
} else {
// NPM-specific operations
await packageManager.add("package", { force: 'npm' });
}Comprehensive error handling and result information for package operations.
import { packageManager, print } from "gluegun";
async function installDependencies(packages, isDev = false) {
print.info(`Installing ${packages.length} packages...`);
const result = await packageManager.add(packages, { dev: isDev });
if (result.success) {
print.success(`✓ Packages installed successfully`);
print.muted(`Command: ${result.command}`);
// Show installation summary
const lines = result.stdout.split('\n');
const summaryLine = lines.find(line => line.includes('added') || line.includes('installed'));
if (summaryLine) {
print.info(summaryLine.trim());
}
} else {
print.error(`✗ Installation failed`);
print.error(`Command: ${result.command}`);
if (result.error) {
print.error(`Error: ${result.error}`);
}
// Parse error details
if (result.stdout.includes('ENOENT')) {
print.warning('Package not found. Check the package name.');
} else if (result.stdout.includes('EACCES')) {
print.warning('Permission error. Try running with sudo or check npm permissions.');
}
}
return result.success;
}
// Usage
const success = await installDependencies(['express', 'cors'], false);
if (!success) {
process.exit(1);
}Handling multiple package operations with progress feedback.
import { packageManager, print } from "gluegun";
async function setupProject(dependencies, devDependencies) {
const operations = [];
if (dependencies.length > 0) {
operations.push({
type: 'production',
packages: dependencies,
options: { dev: false }
});
}
if (devDependencies.length > 0) {
operations.push({
type: 'development',
packages: devDependencies,
options: { dev: true }
});
}
for (const op of operations) {
print.info(`Installing ${op.type} dependencies...`);
const result = await packageManager.add(op.packages, op.options);
if (result.success) {
print.success(`✓ ${op.type} dependencies installed`);
} else {
print.error(`✗ Failed to install ${op.type} dependencies`);
return false;
}
}
return true;
}
// Usage in CLI command
export = {
name: "init",
description: "Initialize project with dependencies",
run: async (toolbox) => {
const { packageManager, print, prompt } = toolbox;
const config = await prompt.ask([
{
type: 'multiselect',
name: 'framework',
message: 'Select frameworks:',
choices: ['express', 'react', 'vue', 'angular']
},
{
type: 'multiselect',
name: 'tools',
message: 'Select development tools:',
choices: ['typescript', 'eslint', 'prettier', 'jest']
}
]);
await setupProject(config.framework, config.tools);
}
};Working with different package manager configurations and options.
import { packageManager, filesystem } from "gluegun";
// Check for lock files to determine package manager preference
function detectPackageManager() {
if (filesystem.exists('yarn.lock')) {
return 'yarn';
} else if (filesystem.exists('package-lock.json')) {
return 'npm';
}
return packageManager.hasYarn() ? 'yarn' : 'npm';
}
// Install with detected package manager
async function smartInstall(packages, options = {}) {
const manager = detectPackageManager();
return await packageManager.add(packages, {
...options,
force: manager
});
}
// Directory-specific installations
async function setupMonorepo() {
const workspaces = ['frontend', 'backend', 'shared'];
for (const workspace of workspaces) {
print.info(`Setting up ${workspace}...`);
const packages = getPackagesForWorkspace(workspace);
const result = await packageManager.add(packages, {
dir: `./${workspace}`,
force: 'npm' // Consistent across workspaces
});
if (!result.success) {
print.error(`Failed to setup ${workspace}`);
return false;
}
}
return true;
}
function getPackagesForWorkspace(workspace) {
const packageMap = {
frontend: ['react', 'react-dom', '@types/react'],
backend: ['express', 'cors', '@types/node'],
shared: ['lodash', 'moment', 'axios']
};
return packageMap[workspace] || [];
}interface GluegunPackageManager {
/**
* Add packages to dependencies
* @param packageName - Package name(s) to install
* @param options - Installation options
* @returns Operation result
*/
add(
packageName: string | string[],
options: GluegunPackageManagerOptions
): Promise<GluegunPackageManagerResult>;
/**
* Remove packages from dependencies
* @param packageName - Package name(s) to remove
* @param options - Removal options
* @returns Operation result
*/
remove(
packageName: string | string[],
options: GluegunPackageManagerOptions
): Promise<GluegunPackageManagerResult>;
/**
* Check if Yarn is available
* @returns True if Yarn is installed
*/
hasYarn(): boolean;
}
interface GluegunPackageManagerOptions {
/** Install as dev dependency */
dev?: boolean;
/** Dry run mode */
dryRun?: boolean;
/** Target directory */
dir?: string;
/** Force specific manager */
force?: 'npm' | 'yarn';
}
interface GluegunPackageManagerResult {
/** Operation success status */
success: boolean;
/** Executed command */
command: string;
/** Command output */
stdout: string;
/** Error message */
error?: string;
}Comprehensive Usage Example:
// CLI command for project dependency management
export = {
name: "deps",
description: "Manage project dependencies",
run: async (toolbox) => {
const { packageManager, print, prompt, parameters, filesystem } = toolbox;
const action = parameters.first;
switch (action) {
case 'install':
await handleInstall(toolbox);
break;
case 'remove':
await handleRemove(toolbox);
break;
case 'audit':
await handleAudit(toolbox);
break;
case 'init':
await handleInit(toolbox);
break;
default:
print.error('Usage: cli deps <install|remove|audit|init>');
}
}
};
async function handleInstall(toolbox) {
const { packageManager, print, prompt, parameters } = toolbox;
let packages = parameters.array.slice(1); // Skip 'install'
if (packages.length === 0) {
const input = await prompt.ask({
type: 'input',
name: 'packages',
message: 'Enter package names (space-separated):'
});
packages = input.packages.split(/\s+/).filter(Boolean);
}
const options = await prompt.ask([
{
type: 'confirm',
name: 'dev',
message: 'Install as dev dependencies?',
initial: false
},
{
type: 'select',
name: 'manager',
message: 'Package manager:',
choices: [
{ name: 'auto', message: 'Auto-detect' },
{ name: 'npm', message: 'NPM' },
{ name: 'yarn', message: 'Yarn' }
]
}
]);
const installOptions = {
dev: options.dev,
force: options.manager !== 'auto' ? options.manager : undefined
};
print.info(`Installing ${packages.length} packages...`);
const result = await packageManager.add(packages, installOptions);
if (result.success) {
print.success(`✓ Packages installed successfully`);
} else {
print.error(`✗ Installation failed: ${result.error}`);
}
}
async function handleRemove(toolbox) {
const { packageManager, print, filesystem } = toolbox;
// Read current dependencies
const pkg = filesystem.read('package.json', 'json');
if (!pkg) {
print.error('No package.json found');
return;
}
const allDeps = {
...pkg.dependencies,
...pkg.devDependencies
};
if (Object.keys(allDeps).length === 0) {
print.info('No dependencies to remove');
return;
}
const toRemove = await prompt.ask({
type: 'multiselect',
name: 'packages',
message: 'Select packages to remove:',
choices: Object.keys(allDeps)
});
if (toRemove.packages.length === 0) {
print.info('No packages selected');
return;
}
const result = await packageManager.remove(toRemove.packages, {});
if (result.success) {
print.success(`✓ Removed ${toRemove.packages.length} packages`);
} else {
print.error(`✗ Removal failed: ${result.error}`);
}
}
async function handleAudit(toolbox) {
const { packageManager, print, system } = toolbox;
const manager = packageManager.hasYarn() ? 'yarn' : 'npm';
print.info('Running security audit...');
try {
const auditCmd = manager === 'yarn' ? 'yarn audit' : 'npm audit';
const output = await system.run(auditCmd);
print.info(output);
} catch (error) {
print.warning('Security vulnerabilities found');
const fix = await prompt.confirm('Attempt to fix automatically?');
if (fix) {
const fixCmd = manager === 'yarn' ? 'yarn audit fix' : 'npm audit fix';
await system.run(fixCmd);
print.success('Audit fixes applied');
}
}
}
async function handleInit(toolbox) {
const { packageManager, print, prompt } = toolbox;
const template = await prompt.ask({
type: 'select',
name: 'type',
message: 'Project template:',
choices: [
{ name: 'basic', message: 'Basic Node.js project' },
{ name: 'web', message: 'Web application' },
{ name: 'api', message: 'REST API server' },
{ name: 'cli', message: 'CLI application' }
]
});
const packageSets = {
basic: {
deps: [],
devDeps: ['nodemon', 'jest']
},
web: {
deps: ['express', 'cors'],
devDeps: ['nodemon', 'jest', 'supertest']
},
api: {
deps: ['express', 'cors', 'helmet', 'morgan'],
devDeps: ['nodemon', 'jest', 'supertest', '@types/node']
},
cli: {
deps: ['commander', 'inquirer'],
devDeps: ['jest', '@types/node']
}
};
const packages = packageSets[template.type];
print.info('Installing project dependencies...');
if (packages.deps.length > 0) {
await packageManager.add(packages.deps, { dev: false });
}
if (packages.devDeps.length > 0) {
await packageManager.add(packages.devDeps, { dev: true });
}
print.success(`✓ ${template.type} project initialized`);
}