| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- #!/usr/bin/env node
- /**
- * Node.js 依赖列表更新脚本
- * 功能:对比 node_modules 中已安装的包和 dependencies.txt,如果不一致则更新 dependencies.txt
- */
- const fs = require('fs');
- const path = require('path');
- // 获取脚本所在目录和项目根目录
- const scriptDir = __dirname;
- const projectRoot = path.dirname(path.dirname(path.dirname(scriptDir)));
- 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}`);
- }
- /**
- * 获取 node_modules 中已安装的包列表(包含版本号)
- */
- function getInstalledPackages() {
- const installedPackages = {};
-
- if (!fs.existsSync(nodeModulesPath)) {
- log('[ERROR] node_modules not found', 'red');
- return null;
- }
-
- 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) {
- installedPackages[pkg.name.toLowerCase()] = `${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) {
- installedPackages[pkg.name.toLowerCase()] = `${pkg.name}==${pkg.version}`;
- }
- } catch (error) {
- // 忽略解析错误
- }
- }
- }
- }
- } catch (error) {
- // 忽略错误
- }
- }
- }
- }
- } catch (error) {
- log(`[ERROR] Failed to read node_modules: ${error.message}`, 'red');
- return null;
- }
-
- return installedPackages;
- }
- /**
- * 读取 dependencies.txt 中的包列表
- */
- function readDependenciesFile() {
- if (!fs.existsSync(dependenciesFile)) {
- log(`[WARN] ${dependenciesFile} not found, will create new one`, 'yellow');
- return {};
- }
-
- const packages = {};
- try {
- const content = fs.readFileSync(dependenciesFile, 'utf-8');
- const lines = content.split('\n');
-
- for (const line of lines) {
- const trimmed = line.trim();
- if (trimmed && !trimmed.startsWith('#')) {
- // 提取包名(支持 ==, >=, <=, >, <, ~= 等版本操作符)
- if (trimmed.includes('==')) {
- const parts = trimmed.split('==', 2);
- packages[parts[0].trim().toLowerCase()] = trimmed;
- } else {
- // 如果没有版本号,使用整行
- const pkgName = trimmed.split('>=')[0].split('<=')[0].split('>')[0].split('<')[0].split('~=')[0].trim();
- packages[pkgName.toLowerCase()] = trimmed;
- }
- }
- }
- } catch (error) {
- log(`[ERROR] Failed to read dependencies.txt: ${error.message}`, 'red');
- return {};
- }
-
- return packages;
- }
- /**
- * 对比并更新 dependencies.txt
- */
- function compareAndUpdate() {
- log('Comparing installed packages with dependencies.txt...', 'cyan');
- log('='.repeat(60), 'cyan');
-
- // 获取已安装的包
- const installedPackages = getInstalledPackages();
- if (installedPackages === null) {
- process.exit(1);
- }
-
- // 读取 dependencies.txt 中的包
- const filePackages = readDependenciesFile();
-
- // 对比差异
- const installedSet = new Set(Object.keys(installedPackages));
- const fileSet = new Set(Object.keys(filePackages));
-
- const addedPackages = [];
- const removedPackages = [];
- const changedPackages = [];
-
- // 检查新增的包
- for (const pkgName of installedSet) {
- if (!fileSet.has(pkgName)) {
- addedPackages.push(pkgName);
- }
- }
-
- // 检查删除的包
- for (const pkgName of fileSet) {
- if (!installedSet.has(pkgName)) {
- removedPackages.push(pkgName);
- }
- }
-
- // 检查版本变化
- for (const pkgName of installedSet) {
- if (fileSet.has(pkgName)) {
- if (installedPackages[pkgName] !== filePackages[pkgName]) {
- changedPackages.push(pkgName);
- }
- }
- }
-
- // 显示差异
- if (addedPackages.length > 0) {
- log(`\n[+] Added packages (${addedPackages.length}):`, 'green');
- addedPackages.sort().forEach(pkg => {
- log(` + ${installedPackages[pkg]}`, 'green');
- });
- }
-
- if (removedPackages.length > 0) {
- log(`\n[-] Removed packages (${removedPackages.length}):`, 'red');
- removedPackages.sort().forEach(pkg => {
- log(` - ${filePackages[pkg]}`, 'red');
- });
- }
-
- if (changedPackages.length > 0) {
- log(`\n[~] Changed packages (${changedPackages.length}):`, 'yellow');
- changedPackages.sort().forEach(pkg => {
- log(` ~ ${filePackages[pkg]} -> ${installedPackages[pkg]}`, 'yellow');
- });
- }
-
- // 判断是否需要更新
- if (addedPackages.length === 0 && removedPackages.length === 0 && changedPackages.length === 0) {
- log(`\n[OK] dependencies.txt is up to date`, 'green');
- log(` Total packages: ${Object.keys(installedPackages).length}`, 'green');
- return true;
- }
-
- // 更新 dependencies.txt
- log(`\nUpdating ${dependenciesFile}...`, 'cyan');
-
- // 获取所有已安装的包(按名称排序)
- const allPackages = Object.values(installedPackages).sort();
-
- // 写入文件(UTF-8 编码)
- try {
- fs.writeFileSync(dependenciesFile, allPackages.join('\n') + '\n', 'utf-8');
- log(`[OK] ${dependenciesFile} updated successfully`, 'green');
- log(` Total packages: ${allPackages.length}`, 'green');
- return true;
- } catch (error) {
- log(`[ERROR] Failed to write dependencies.txt: ${error.message}`, 'red');
- return false;
- }
- }
- /**
- * 主函数
- */
- function main() {
- if (!compareAndUpdate()) {
- process.exit(1);
- }
- process.exit(0);
- }
- if (require.main === module) {
- main();
- }
|