|
|
|
|
@@ -250,6 +250,11 @@ class PackageManager {
|
|
|
|
|
|
|
|
|
|
console.log(chalk.green(`✓ Installed ${name}@${version || 'latest'} from ${source || (isFromCache ? 'cache' : 'registry')}`));
|
|
|
|
|
|
|
|
|
|
// Create binary links for locally installed packages
|
|
|
|
|
if (!options.global) {
|
|
|
|
|
await this.createBinaryLinksForPackage(name, targetDir);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Install dependencies recursively (with depth control)
|
|
|
|
|
await this.installDependenciesRecursively(name, targetDir, options);
|
|
|
|
|
|
|
|
|
|
@@ -378,6 +383,11 @@ class PackageManager {
|
|
|
|
|
await this.createGlobalBinLinks(pkg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create local binary links if not global
|
|
|
|
|
if (!options.global && pkg.bin) {
|
|
|
|
|
await this.createLocalBinLinks(pkg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.logger.info(`Installed ${pkg.name}@${pkg.version}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -398,6 +408,8 @@ class PackageManager {
|
|
|
|
|
// Remove from package.json
|
|
|
|
|
if (!options.global) {
|
|
|
|
|
await this.removeFromPackageJson(packageName);
|
|
|
|
|
// Remove local bin links
|
|
|
|
|
await this.removeLocalBinLinks(packageName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove global bin links
|
|
|
|
|
@@ -962,6 +974,62 @@ class PackageManager {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async createLocalBinLinks(pkg) {
|
|
|
|
|
// Implementation for creating local binary links in node_modules/.bin
|
|
|
|
|
const binDir = path.join(this.projectRoot, 'node_modules', '.bin');
|
|
|
|
|
await fs.ensureDir(binDir);
|
|
|
|
|
|
|
|
|
|
if (pkg.bin) {
|
|
|
|
|
// Handle both string and object formats for bin field
|
|
|
|
|
const binEntries = typeof pkg.bin === 'string'
|
|
|
|
|
? [[pkg.name, pkg.bin]]
|
|
|
|
|
: Object.entries(pkg.bin);
|
|
|
|
|
|
|
|
|
|
for (const [binName, binPath] of binEntries) {
|
|
|
|
|
const linkPath = path.join(binDir, binName);
|
|
|
|
|
const targetPath = path.join(this.projectRoot, 'node_modules', pkg.name, binPath);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// Remove existing link if it exists
|
|
|
|
|
if (await fs.pathExists(linkPath)) {
|
|
|
|
|
await fs.remove(linkPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create the symlink
|
|
|
|
|
await fs.ensureSymlink(targetPath, linkPath);
|
|
|
|
|
|
|
|
|
|
// Make the target executable (Unix/Linux only)
|
|
|
|
|
if (process.platform !== 'win32') {
|
|
|
|
|
await fs.chmod(targetPath, '755');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log(chalk.gray(` Created bin link: ${binName} -> ${binPath}`));
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.warn(chalk.yellow(`Warning: Failed to create bin link for ${binName}: ${error.message}`));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async createBinaryLinksForPackage(packageName, packageDir) {
|
|
|
|
|
try {
|
|
|
|
|
const packageJsonPath = path.join(packageDir, 'package.json');
|
|
|
|
|
|
|
|
|
|
if (!await fs.pathExists(packageJsonPath)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const packageJson = await fs.readJson(packageJsonPath);
|
|
|
|
|
|
|
|
|
|
if (packageJson.bin) {
|
|
|
|
|
console.log(chalk.blue(`Creating local bin links for ${packageName}`));
|
|
|
|
|
await this.createLocalBinLinks(packageJson);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.warn(chalk.yellow(`Warning: Failed to create binary links for ${packageName}: ${error.message}`));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async removeGlobalBinLinks(packageName) {
|
|
|
|
|
// Implementation for removing global binary links
|
|
|
|
|
const binDir = path.join(this.globalRoot, 'bin');
|
|
|
|
|
@@ -983,6 +1051,37 @@ class PackageManager {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async removeLocalBinLinks(packageName) {
|
|
|
|
|
// Implementation for removing local binary links from node_modules/.bin
|
|
|
|
|
const binDir = path.join(this.projectRoot, 'node_modules', '.bin');
|
|
|
|
|
const packageDir = path.join(this.projectRoot, 'node_modules', packageName);
|
|
|
|
|
|
|
|
|
|
if (await fs.pathExists(packageDir)) {
|
|
|
|
|
const packageJsonPath = path.join(packageDir, 'package.json');
|
|
|
|
|
if (await fs.pathExists(packageJsonPath)) {
|
|
|
|
|
try {
|
|
|
|
|
const packageJson = await fs.readJson(packageJsonPath);
|
|
|
|
|
if (packageJson.bin) {
|
|
|
|
|
// Handle both string and object formats for bin field
|
|
|
|
|
const binEntries = typeof packageJson.bin === 'string'
|
|
|
|
|
? [[packageJson.name, packageJson.bin]]
|
|
|
|
|
: Object.entries(packageJson.bin);
|
|
|
|
|
|
|
|
|
|
for (const [binName] of binEntries) {
|
|
|
|
|
const linkPath = path.join(binDir, binName);
|
|
|
|
|
if (await fs.pathExists(linkPath)) {
|
|
|
|
|
await fs.remove(linkPath);
|
|
|
|
|
console.log(chalk.gray(` Removed bin link: ${binName}`));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.warn(chalk.yellow(`Warning: Failed to read package.json for ${packageName}: ${error.message}`));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fixVulnerabilities(vulnerabilities) {
|
|
|
|
|
const spinner = ora('Fixing vulnerabilities...').start();
|
|
|
|
|
|
|
|
|
|
|