utils.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import { readdir, stat } from 'node:fs';
  2. import { dirname, resolve } from 'node:path';
  3. import { fileURLToPath } from 'node:url';
  4. /** 启动`node`进程时所在工作目录的绝对路径 */
  5. const root: string = process.cwd();
  6. /**
  7. * @description 根据可选的路径片段生成一个新的绝对路径
  8. * @param dir 路径片段,默认`build`
  9. * @param metaUrl 模块的完整`url`,如果在`build`目录外调用必传`import.meta.url`
  10. */
  11. const pathResolve = (dir = '.', metaUrl = import.meta.url) => {
  12. // 当前文件目录的绝对路径
  13. const currentFileDir = dirname(fileURLToPath(metaUrl));
  14. // build 目录的绝对路径
  15. const buildDir = resolve(currentFileDir, 'build');
  16. // 解析的绝对路径
  17. const resolvedPath = resolve(currentFileDir, dir);
  18. // 检查解析的绝对路径是否在 build 目录内
  19. if (resolvedPath.startsWith(buildDir)) {
  20. // 在 build 目录内,返回当前文件路径
  21. return fileURLToPath(metaUrl);
  22. }
  23. // 不在 build 目录内,返回解析后的绝对路径
  24. return resolvedPath;
  25. };
  26. /** 设置别名 */
  27. const alias: Record<string, string> = {
  28. '@': pathResolve('../src'),
  29. '@build': pathResolve(),
  30. };
  31. /** 处理环境变量 */
  32. const wrapperEnv = (envConf: Recordable): ImportMetaEnv => {
  33. // 默认值
  34. let ret: ImportMetaEnv = {};
  35. ret = Object.assign(ret, envConf);
  36. const setEnv = (envName: string, envValue: string | number | boolean | object) => {
  37. if (envName.startsWith('VITE_DEV_')) return; // 剔除开发环境变量,不写入 process.env
  38. if (envName.startsWith('VITE_BUILD_')) return; // 剔除构建环境变量,不写入 process.env
  39. if (typeof envValue === 'string') {
  40. process.env[envName] = envValue;
  41. } else if (typeof envValue === 'number' || typeof envValue === 'boolean') {
  42. process.env[envName] = String(envValue);
  43. } else if (typeof envValue === 'object' && envValue !== null) {
  44. process.env[envName] = JSON.stringify(envValue);
  45. }
  46. };
  47. // 先设置默认值到 process.env
  48. for (const envName of Object.keys(ret)) {
  49. setEnv(envName, ret[envName]);
  50. }
  51. // 然后用环境变量覆盖默认值
  52. for (const envName of Object.keys(envConf)) {
  53. const raw = envConf[envName];
  54. let envValue: string | number | boolean =
  55. typeof raw === 'string' ? raw.replace(/\\n/g, '\n') : String(raw);
  56. envValue =
  57. envValue === 'true' ? true : envValue === 'false' ? false : envValue;
  58. if (envName === 'VITE_DEV_PORT') {
  59. envValue = Number(envValue);
  60. }
  61. ret[envName] = envValue;
  62. setEnv(envName, envValue);
  63. }
  64. return ret;
  65. };
  66. const fileListTotal: number[] = [];
  67. /**
  68. * @description 计算数组元素之和
  69. * @param arr 数字数组
  70. * @returns 数组元素之和
  71. */
  72. const sum = (arr: number[]): number => arr.reduce((a, b) => a + b, 0);
  73. /**
  74. * @description 将字节单位智能转化成 Bytes 、 KB 、 MB 、 GB 、 TB 、 PB 、 EB 、 ZB 、 YB 其中的一种
  75. * @param byte — 字节
  76. * @param digits — 四舍五入保留几位小数(默认四舍五入保留两位小数)
  77. * @returns — 智能转化字节单位后的值
  78. */
  79. const formatBytes = (byte: number, digits = 2): string => {
  80. if (byte === 0) return '0 Bytes';
  81. const k = 1024;
  82. const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
  83. const i = Math.floor(Math.log(byte) / Math.log(k));
  84. return `${parseFloat((byte / Math.pow(k, i)).toFixed(digits))} ${sizes[i]}`;
  85. };
  86. interface PackageSizeOptions {
  87. folder?: string;
  88. callback: (size: string | number) => void;
  89. format?: boolean;
  90. }
  91. /** 获取指定文件夹中所有文件的总大小 */
  92. const getPackageSize = (options: PackageSizeOptions) => {
  93. const { folder = 'dist', callback, format = true } = options;
  94. readdir(folder, (err, files: string[]) => {
  95. if (err) throw err;
  96. let count = 0;
  97. const checkEnd = () => {
  98. if (++count === files.length) {
  99. callback(format ? formatBytes(sum(fileListTotal)) : sum(fileListTotal));
  100. }
  101. };
  102. files.forEach((item: string) => {
  103. stat(`${folder}/${item}`, async (err, stats) => {
  104. if (err) throw err;
  105. if (stats.isFile()) {
  106. fileListTotal.push(stats.size);
  107. checkEnd();
  108. } else if (stats.isDirectory()) {
  109. getPackageSize({
  110. folder: `${folder}/${item}/`,
  111. callback: checkEnd,
  112. });
  113. }
  114. });
  115. });
  116. if (files.length === 0) {
  117. callback(0);
  118. }
  119. });
  120. };
  121. export { root, pathResolve, alias, wrapperEnv, getPackageSize, formatBytes };