From 76e9c939e208ef4b9b51cdfd707751e7d9e97144 Mon Sep 17 00:00:00 2001 From: ale Date: Tue, 19 Aug 2025 23:11:19 +0200 Subject: [PATCH] v1.1.0 Signed-off-by: ale --- src/core/package-manager.js | 111 ++++++++++++++++++++++++++---------- 1 file changed, 80 insertions(+), 31 deletions(-) diff --git a/src/core/package-manager.js b/src/core/package-manager.js index 011beb3..04304a0 100644 --- a/src/core/package-manager.js +++ b/src/core/package-manager.js @@ -115,7 +115,7 @@ class PackageManager { const packageJson = await fs.readJson(packageJsonPath); const dependencies = { ...packageJson.dependencies, - ...(options.includeDev ? packageJson.devDependencies : {}) + ...(options.includeDev || options.saveDev ? packageJson.devDependencies : {}) }; if (Object.keys(dependencies).length === 0) { @@ -348,10 +348,14 @@ class PackageManager { ...packageJson.dependencies, ...(options.includeDev ? packageJson.devDependencies : {}) }; + + const optionalDependencies = packageJson.optionalDependencies || {}; if (!dependencies || Object.keys(dependencies).length === 0) { - // No dependencies to install - return; + // If no regular dependencies, check if there are optional dependencies to install + if (Object.keys(optionalDependencies).length === 0) { + return; + } } // Initialize installed packages tracking if not exists @@ -368,6 +372,14 @@ class PackageManager { console.log(chalk.blue(`Installing dependencies for ${packageName}...`)); + // Install dependencies recursively by calling installPackages + const depOptions = { + ...options, + fromPackageJson: true, // Prevent updating package.json + _depth: currentDepth + 1, // Increment depth + _installedPackages: options._installedPackages // Pass along installed packages set + }; + // Filter out already installed packages to avoid duplicates const dependenciesToInstall = Object.entries(dependencies).filter(([name, version]) => { const packageKey = `${name}@${version}`; @@ -384,37 +396,74 @@ class PackageManager { }); if (dependenciesToInstall.length === 0) { - return; + // No regular dependencies to install, but continue to check optional dependencies + } else { + // Prepare dependency specs for installation + const dependencySpecs = dependenciesToInstall.map(([name, version]) => { + // Mark as installed to prevent duplicates + options._installedPackages.add(`${name}@${version}`); + + // Handle various version formats + if (version.startsWith('^') || version.startsWith('~') || version.startsWith('>=') || version.startsWith('<=')) { + return `${name}@${version}`; + } else if (version === '*' || version === 'latest') { + return `${name}@latest`; + } else if (semver.validRange(version)) { + return `${name}@${version}`; + } else { + // For non-semver versions (git urls, file paths, etc.), use as-is + return `${name}@${version}`; + } + }); + + // Install dependencies + await this.installPackages(dependencySpecs, depOptions); } - // Prepare dependency specs for installation - const dependencySpecs = dependenciesToInstall.map(([name, version]) => { - // Mark as installed to prevent duplicates - options._installedPackages.add(`${name}@${version}`); - - // Handle various version formats - if (version.startsWith('^') || version.startsWith('~') || version.startsWith('>=') || version.startsWith('<=')) { - return `${name}@${version}`; - } else if (version === '*' || version === 'latest') { - return `${name}@latest`; - } else if (semver.validRange(version)) { - return `${name}@${version}`; - } else { - // For non-semver versions (git urls, file paths, etc.), use as-is - return `${name}@${version}`; + // Install optional dependencies (ignore failures) + if (Object.keys(optionalDependencies).length > 0) { + const optionalDependenciesToInstall = Object.entries(optionalDependencies).filter(([name, version]) => { + const packageKey = `${name}@${version}`; + if (options._installedPackages && options._installedPackages.has(packageKey)) { + return false; // Skip already installed package + } + + // Check if package already exists in node_modules + const targetDir = options.global + ? path.join(this.globalRoot, 'node_modules', name) + : path.join(this.projectRoot, 'node_modules', name); + + const exists = fs.existsSync(targetDir); + return !exists; + }); + + if (optionalDependenciesToInstall.length > 0) { + const optionalSpecs = optionalDependenciesToInstall.map(([name, version]) => { + // Mark as installed to prevent duplicates + if (options._installedPackages) { + options._installedPackages.add(`${name}@${version}`); + } + + // Handle various version formats + if (version.startsWith('^') || version.startsWith('~') || version.startsWith('>=') || version.startsWith('<=')) { + return `${name}@${version}`; + } else if (version === '*' || version === 'latest') { + return `${name}@latest`; + } else if (semver.validRange(version)) { + return `${name}@${version}`; + } else { + return `${name}@${version}`; + } + }); + + // Install optional dependencies (ignore failures) + try { + await this.installPackages(optionalSpecs, depOptions); + } catch (error) { + console.warn(chalk.yellow(`Some optional dependencies for ${packageName} could not be installed (this is usually safe to ignore)`)); + } } - }); - - // Install dependencies recursively by calling installPackages - const depOptions = { - ...options, - fromPackageJson: true, // Prevent updating package.json - _depth: currentDepth + 1, // Increment depth - _installedPackages: options._installedPackages // Pass along installed packages set - }; - - // Install dependencies - await this.installPackages(dependencySpecs, depOptions); + } } catch (error) { console.warn(chalk.yellow(`Failed to install dependencies for ${packageName}: ${error.message}`));