build.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. const fs = require('fs');
  2. const path = require('path');
  3. const archiver = require('archiver');
  4. const cliProgress = require('cli-progress');
  5. const { minimatch } = require('minimatch');
  6. // 定义忽略模式
  7. const ignorePatterns = [
  8. 'node_modules/**', // 排除 node_modules 目录
  9. 'dist.zip', // 排除 dist.zip 文件
  10. '.idea/**', // 排除 .idea 目录
  11. '.vscode/**', // 排除 .vscode 目录
  12. '.git/**', // 排除 .git 目录
  13. 'build.js', // 排除 build.js 文件
  14. 'server.js', // 排除 server.js 文件
  15. 'package-lock.json', // 排除 package-lock.json 文件
  16. 'package.json', // 排除 package.json 文件
  17. ];
  18. // 定义源目录和目标压缩文件路径
  19. const sourceDir = './';
  20. const outputZipPath = path.join(sourceDir, 'dist.zip');
  21. // 创建进度条
  22. const progressBar = new cliProgress.SingleBar({
  23. format: '进度: {bar} {percentage}% | {value}/{total} MB',
  24. barCompleteChar: '\u2588',
  25. barIncompleteChar: '\u2591',
  26. hideCursor: true,
  27. barComplete: '\u001b[32m\u2588\u001b[0m', // 绿色完成部分
  28. barIncomplete: '\u001b[31m\u2591\u001b[0m', // 红色未完成部分
  29. complete: '\u001b[32m#\u001b[0m', // 绿色完成字符
  30. incomplete: '\u001b[31m#\u001b[0m' // 红色未完成字符
  31. }, cliProgress.Presets.shades_classic);
  32. // 计算总文件大小(以字节为单位)
  33. function getTotalFileSize(dir, ignorePatterns) {
  34. let totalSize = 0;
  35. function walk(dir, isTopLevel = true) {
  36. const files = fs.readdirSync(dir);
  37. files.forEach((file) => {
  38. const filePath = path.join(dir, file);
  39. const stat = fs.statSync(filePath);
  40. if (stat.isDirectory()) {
  41. // 只在第一级时检查忽略目录
  42. if (isTopLevel && ignorePatterns.some(pattern => minimatch(filePath, pattern, { matchBase: true }))) {
  43. return;
  44. }
  45. walk(filePath, false);
  46. } else {
  47. // 检查是否需要忽略文件(非第一级时也检查)
  48. if (!ignorePatterns.some(pattern => minimatch(filePath, pattern, { matchBase: true }))) {
  49. totalSize += stat.size;
  50. }
  51. }
  52. });
  53. }
  54. walk(dir);
  55. return totalSize;
  56. }
  57. // 计算总文件大小
  58. const totalFileSize = getTotalFileSize(sourceDir, ignorePatterns);
  59. // 创建一个输出流
  60. const output = fs.createWriteStream(outputZipPath);
  61. const archive = archiver('zip', {
  62. zlib: { level: 9 } // 设置压缩级别
  63. });
  64. // 标记进度条是否已经启动
  65. let progressBarStarted = false;
  66. let processedSize = 0;
  67. // 监听输出流的事件
  68. output.on('close', () => {
  69. progressBar.stop();
  70. console.log(`压缩完成: ${(archive.pointer() / (1024 * 1024)).toFixed(1)} MB`);
  71. });
  72. output.on('end', () => {
  73. console.log('数据传输完成');
  74. });
  75. // 监听归档过程中的错误
  76. archive.on('warning', (err) => {
  77. if (err.code === 'ENOENT') {
  78. // 文件不存在的警告
  79. console.warn(err);
  80. } else {
  81. // 其他警告
  82. throw err;
  83. }
  84. });
  85. archive.on('error', (err) => {
  86. throw err;
  87. });
  88. // 监听归档开始事件
  89. archive.on('entry', (entry) => {
  90. const filePath = path.join(sourceDir, entry.name);
  91. const stat = fs.statSync(filePath);
  92. processedSize += stat.size;
  93. if (!progressBarStarted) {
  94. const totalMB = parseFloat((totalFileSize / (1024 * 1024)).toFixed(1));
  95. const processedMB = parseFloat((processedSize / (1024 * 1024)).toFixed(1));
  96. progressBar.start(totalMB, processedMB);
  97. progressBarStarted = true;
  98. } else {
  99. const processedMB = parseFloat((processedSize / (1024 * 1024)).toFixed(1));
  100. progressBar.update(processedMB);
  101. }
  102. });
  103. // 将输出流管道到归档器
  104. archive.pipe(output);
  105. // 添加源目录中的所有文件到归档,排除特定文件或目录
  106. archive.glob('**/*', {
  107. cwd: sourceDir,
  108. ignore: ignorePatterns
  109. });
  110. // 完成归档
  111. archive.finalize();