#!/usr/bin/env node /** * Node.js 依赖安装和同步脚本 * 功能:检查、安装 package.json 中的依赖,然后同步所有已安装的包到 dependencies.txt */ const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); // 获取脚本所在目录和项目根目录 const scriptDir = __dirname; const projectRoot = path.dirname(scriptDir); const packageJsonPath = path.join(projectRoot, 'package.json'); const dependenciesFile = path.join(scriptDir, 'dependencies.txt'); const nodeModulesPath = path.join(projectRoot, 'node_modules'); // 颜色输出函数 const colors = { reset: '\x1b[0m', red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m', cyan: '\x1b[36m', white: '\x1b[37m' }; function log(message, color = 'reset') { console.log(`${colors[color]}${message}${colors.reset}`); } // 检查 package.json 是否存在 if (!fs.existsSync(packageJsonPath)) { log('[X] package.json not found', 'red'); process.exit(1); } // 读取 package.json const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); const allDependencies = {}; // 收集所有依赖 if (packageJson.dependencies) { Object.keys(packageJson.dependencies).forEach(depName => { allDependencies[depName] = { version: packageJson.dependencies[depName], type: 'dependency' }; }); } if (packageJson.devDependencies) { Object.keys(packageJson.devDependencies).forEach(depName => { allDependencies[depName] = { version: packageJson.devDependencies[depName], type: 'devDependency' }; }); } // 检查缺失的依赖 const missingDependencies = []; let installedCount = 0; let missingCount = 0; const depNames = Object.keys(allDependencies).sort(); for (const depName of depNames) { const packagePath = path.join(nodeModulesPath, depName); const packageJsonPath = path.join(packagePath, 'package.json'); if (fs.existsSync(packageJsonPath)) { installedCount++; } else { missingDependencies.push(depName); missingCount++; } } // 如果有缺失的依赖,显示必要信息并安装 if (missingCount > 0) { log(`[X] Missing ${missingCount} package(s) out of ${Object.keys(allDependencies).length}`, 'red'); log('Missing dependencies:', 'yellow'); missingDependencies.forEach(missing => { log(` - ${missing}`, 'red'); }); log('\nInstalling missing dependencies...', 'yellow'); try { // 切换到项目根目录 process.chdir(projectRoot); // 执行 npm install,隐藏输出 execSync('npm install', { stdio: 'ignore', cwd: projectRoot }); log('[OK] All dependencies installed successfully', 'green'); } catch (error) { log('[X] Dependency installation failed', 'red'); process.exit(1); } } else { log(`[OK] All dependencies are installed (${Object.keys(allDependencies).length} packages)`, 'green'); } // 同步所有已安装的依赖到 dependencies.txt log('\nSyncing all installed dependencies to configs/dependencies.txt...', 'cyan'); const syncResult = []; if (fs.existsSync(nodeModulesPath)) { // 遍历 node_modules 目录,查找所有 package.json 文件 function findPackageJsonFiles(dir) { const files = []; try { const entries = fs.readdirSync(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { // 跳过 .bin 目录和其他特殊目录 if (entry.name.startsWith('.') || entry.name === 'node_modules') { continue; } // 递归查找 files.push(...findPackageJsonFiles(fullPath)); } else if (entry.name === 'package.json') { files.push(fullPath); } } } catch (error) { // 忽略权限错误等 } return files; } const packageJsonFiles = findPackageJsonFiles(nodeModulesPath); for (const file of packageJsonFiles) { try { const pkg = JSON.parse(fs.readFileSync(file, 'utf-8')); if (pkg.name && pkg.version) { syncResult.push(`${pkg.name}==${pkg.version}`); } } catch (error) { // 忽略解析错误 } } // 如果遍历结果为空,使用 npm list 作为后备方案 if (syncResult.length === 0) { try { const npmListOutput = execSync('npm list --depth=0 --json', { encoding: 'utf-8', cwd: projectRoot, stdio: ['ignore', 'pipe', 'ignore'] }); const npmList = JSON.parse(npmListOutput); if (npmList.dependencies) { Object.keys(npmList.dependencies).forEach(depName => { const dep = npmList.dependencies[depName]; if (dep && dep.version) { syncResult.push(`${depName}==${dep.version}`); } }); } } catch (error) { // 忽略错误 } } } else { // 如果 node_modules 不存在,使用 npm list try { const npmListOutput = execSync('npm list --depth=0 --json', { encoding: 'utf-8', cwd: projectRoot, stdio: ['ignore', 'pipe', 'ignore'] }); const npmList = JSON.parse(npmListOutput); if (npmList.dependencies) { Object.keys(npmList.dependencies).forEach(depName => { const dep = npmList.dependencies[depName]; if (dep && dep.version) { syncResult.push(`${depName}==${dep.version}`); } }); } } catch (error) { // 忽略错误 } } // 去重并排序 const uniqueResult = [...new Set(syncResult)].sort(); // 写入文件(UTF-8 编码) fs.writeFileSync(dependenciesFile, uniqueResult.join('\n') + '\n', 'utf-8'); log(`[OK] Dependencies synced to ${dependenciesFile} (${uniqueResult.length} packages)`, 'green'); process.exit(0);