nodejs-dependencies-install.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. #!/usr/bin/env node
  2. /**
  3. * Node.js 依赖:按需执行 npm install,并同步 dependencies.txt
  4. * 用法:node nodejs-dependencies-install.js [--update]
  5. * 无参数:若无 node_modules 或缺少依赖则 npm install,否则跳过
  6. * --update:仅对比 node_modules 与 dependencies.txt,不一致时更新 dependencies.txt
  7. */
  8. const fs = require('fs');
  9. const path = require('path');
  10. const { execSync } = require('child_process');
  11. const scriptDir = __dirname;
  12. const projectRoot = path.resolve(scriptDir, '..', '..', '..');
  13. const config = require(path.join(projectRoot, 'configs', 'config.js'));
  14. const nodeDir = config.nodeDir || path.dirname(config.nodejsPath);
  15. const nodeModulesPath = path.join(nodeDir, 'node_modules');
  16. const dependenciesFile = path.join(scriptDir, 'dependencies.txt');
  17. const nodeExe = path.join(nodeDir, process.platform === 'win32' ? 'node.exe' : 'node');
  18. const npmCmd = config.npmCmdPath || path.join(nodeDir, process.platform === 'win32' ? 'npm.cmd' : 'npm');
  19. if (!fs.existsSync(nodeDir)) {
  20. console.error('[X] nodejs/node not found.');
  21. process.exit(1);
  22. }
  23. if (!fs.existsSync(nodeModulesPath)) {
  24. fs.mkdirSync(nodeModulesPath, { recursive: true });
  25. }
  26. process.env.PATH = nodeDir + path.delimiter + (process.env.PATH || '');
  27. const colors = { reset: '\x1b[0m', red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m', cyan: '\x1b[36m' };
  28. function log(message, color = 'reset') {
  29. console.log(`${colors[color]}${message}${colors.reset}`);
  30. }
  31. function getRequiredPackages() {
  32. const pkgPath = path.join(projectRoot, 'package.json');
  33. if (!fs.existsSync(pkgPath)) return [];
  34. try {
  35. const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
  36. const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
  37. return Object.keys(deps);
  38. } catch (_) { return []; }
  39. }
  40. function isPackageInstalled(name) {
  41. if (name.startsWith('@')) {
  42. const parts = name.slice(1).split('/');
  43. const pkgPath = path.join(nodeModulesPath, '@' + parts[0], parts[1] || '');
  44. return fs.existsSync(path.join(pkgPath, 'package.json'));
  45. }
  46. return fs.existsSync(path.join(nodeModulesPath, name, 'package.json'));
  47. }
  48. function needsInstall() {
  49. const installed = getInstalledPackages();
  50. const installedCount = Object.keys(installed).length;
  51. const required = getRequiredPackages();
  52. if (required.length === 0) return true;
  53. if (installedCount === 0) return true;
  54. for (const name of required) {
  55. if (!isPackageInstalled(name)) return true;
  56. }
  57. try {
  58. execSync(`"${npmCmd}" ls --prefix ${path.join(nodeDir).replace(/\\/g, '/')}`, {
  59. stdio: 'pipe',
  60. cwd: projectRoot,
  61. env: process.env
  62. });
  63. } catch (_) {
  64. return true;
  65. }
  66. return false;
  67. }
  68. function getInstalledPackages() {
  69. const out = {};
  70. if (!fs.existsSync(nodeModulesPath)) return out;
  71. try {
  72. const entries = fs.readdirSync(nodeModulesPath, { withFileTypes: true });
  73. for (const entry of entries) {
  74. if (!entry.isDirectory()) continue;
  75. const packageName = entry.name;
  76. if (packageName.startsWith('.') || packageName === 'node_modules' || packageName === 'npm' || packageName === 'corepack') continue;
  77. const pkgPath = path.join(nodeModulesPath, packageName, 'package.json');
  78. if (fs.existsSync(pkgPath)) {
  79. try {
  80. const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
  81. if (pkg.name && pkg.version) out[pkg.name.toLowerCase()] = `${pkg.name}==${pkg.version}`;
  82. } catch (_) {}
  83. }
  84. if (packageName.startsWith('@')) {
  85. try {
  86. const scopedPath = path.join(nodeModulesPath, packageName);
  87. const sub = fs.readdirSync(scopedPath, { withFileTypes: true });
  88. for (const e of sub) {
  89. if (!e.isDirectory()) continue;
  90. const subPath = path.join(scopedPath, e.name, 'package.json');
  91. if (fs.existsSync(subPath)) {
  92. try {
  93. const pkg = JSON.parse(fs.readFileSync(subPath, 'utf-8'));
  94. if (pkg.name && pkg.version) out[pkg.name.toLowerCase()] = `${pkg.name}==${pkg.version}`;
  95. } catch (_) {}
  96. }
  97. }
  98. } catch (_) {}
  99. }
  100. }
  101. } catch (_) {}
  102. return out;
  103. }
  104. function writeDependenciesFile(installed) {
  105. const lines = [...new Set(Object.values(installed))].sort();
  106. fs.writeFileSync(dependenciesFile, lines.join('\n') + '\n', 'utf-8');
  107. }
  108. function runUpdateOnly() {
  109. log('Comparing node_modules with dependencies.txt...', 'cyan');
  110. const installed = getInstalledPackages();
  111. if (Object.keys(installed).length === 0) {
  112. log('[ERROR] nodejs/node/node_modules not found or empty.', 'red');
  113. process.exit(1);
  114. }
  115. writeDependenciesFile(installed);
  116. log(`[OK] dependencies.txt updated (${Object.keys(installed).length} packages)`, 'green');
  117. }
  118. function main() {
  119. if (process.argv.includes('--update')) {
  120. runUpdateOnly();
  121. process.exit(0);
  122. }
  123. if (!fs.existsSync(path.join(projectRoot, 'package.json'))) {
  124. log('[X] package.json not found', 'red');
  125. process.exit(1);
  126. }
  127. if (!needsInstall()) {
  128. log('[OK] Dependencies complete, skipping npm install', 'green');
  129. const installed = getInstalledPackages();
  130. if (Object.keys(installed).length > 0) {
  131. writeDependenciesFile(installed);
  132. }
  133. return;
  134. }
  135. log('Running npm install...', 'cyan');
  136. process.chdir(projectRoot);
  137. const npmInstallPrefix = `--prefix ${path.join(nodeDir).replace(/\\/g, '/')}`;
  138. try {
  139. execSync(`"${npmCmd}" install ${npmInstallPrefix} --ignore-engines --no-audit`, { stdio: 'inherit', cwd: projectRoot, env: process.env });
  140. } catch (e) {
  141. log('[X] npm install failed', 'red');
  142. process.exit(1);
  143. }
  144. const installed = getInstalledPackages();
  145. writeDependenciesFile(installed);
  146. log(`[OK] Installed and synced to dependencies.txt (${Object.keys(installed).length} packages)`, 'green');
  147. }
  148. main();