#!/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' }; }); } // 快速获取已安装的包列表(直接从 node_modules 文件夹读取) function getInstalledPackagesFromFilesystem() { const installedPackages = new Set(); if (!fs.existsSync(nodeModulesPath)) { return installedPackages; } try { const entries = fs.readdirSync(nodeModulesPath, { withFileTypes: true }); for (const entry of entries) { if (entry.isDirectory()) { const packageName = entry.name; // 跳过特殊目录 if (packageName.startsWith('.') || packageName === 'node_modules') { continue; } // 检查是否是有效的包(有 package.json) const packageJsonPath = path.join(nodeModulesPath, packageName, 'package.json'); if (fs.existsSync(packageJsonPath)) { installedPackages.add(packageName.toLowerCase()); } // 处理 scoped 包(如 @babel/core) if (packageName.startsWith('@')) { try { const scopedPath = path.join(nodeModulesPath, packageName); const scopedEntries = fs.readdirSync(scopedPath, { withFileTypes: true }); for (const scopedEntry of scopedEntries) { if (scopedEntry.isDirectory()) { const scopedPackageName = `${packageName}/${scopedEntry.name}`; const scopedPackageJsonPath = path.join(scopedPath, scopedEntry.name, 'package.json'); if (fs.existsSync(scopedPackageJsonPath)) { installedPackages.add(scopedPackageName.toLowerCase()); } } } } catch (error) { // 忽略错误 } } } } } catch (error) { // 忽略错误 } return installedPackages; } // 快速检查缺失的依赖(使用文件系统) const missingDependencies = []; let installedCount = 0; let missingCount = 0; // 一次性获取所有已安装的包(只检查一次文件系统) const installedPackagesSet = getInstalledPackagesFromFilesystem(); const depNames = Object.keys(allDependencies).sort(); for (const depName of depNames) { const depNameLower = depName.toLowerCase(); // 快速检查(使用已获取的集合) if (installedPackagesSet.has(depNameLower)) { 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(只读取根级包,静默执行) function syncInstalledPackagesToFile() { const syncResult = []; if (!fs.existsSync(nodeModulesPath)) { return syncResult; } try { const entries = fs.readdirSync(nodeModulesPath, { withFileTypes: true }); for (const entry of entries) { if (entry.isDirectory()) { const packageName = entry.name; // 跳过特殊目录 if (packageName.startsWith('.') || packageName === 'node_modules') { continue; } // 处理普通包 const packageJsonPath = path.join(nodeModulesPath, packageName, 'package.json'); if (fs.existsSync(packageJsonPath)) { try { const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); if (pkg.name && pkg.version) { syncResult.push(`${pkg.name}==${pkg.version}`); } } catch (error) { // 忽略解析错误 } } // 处理 scoped 包(如 @babel/core) if (packageName.startsWith('@')) { try { const scopedPath = path.join(nodeModulesPath, packageName); const scopedEntries = fs.readdirSync(scopedPath, { withFileTypes: true }); for (const scopedEntry of scopedEntries) { if (scopedEntry.isDirectory()) { const scopedPackageJsonPath = path.join(scopedPath, scopedEntry.name, 'package.json'); if (fs.existsSync(scopedPackageJsonPath)) { try { const pkg = JSON.parse(fs.readFileSync(scopedPackageJsonPath, 'utf-8')); if (pkg.name && pkg.version) { syncResult.push(`${pkg.name}==${pkg.version}`); } } catch (error) { // 忽略解析错误 } } } } } catch (error) { // 忽略错误 } } } } } catch (error) { // 忽略错误 } return syncResult; } // 同步所有已安装的依赖到 dependencies.txt(快速方法,静默执行) const syncResult = syncInstalledPackagesToFile(); // 去重并排序 const uniqueResult = [...new Set(syncResult)].sort(); // 写入文件(UTF-8 编码) fs.writeFileSync(dependenciesFile, uniqueResult.join('\n') + '\n', 'utf-8'); process.exit(0);