import { readdir, stat } from 'node:fs'; import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; /** 启动`node`进程时所在工作目录的绝对路径 */ const root: string = process.cwd(); /** * @description 根据可选的路径片段生成一个新的绝对路径 * @param dir 路径片段,默认`build` * @param metaUrl 模块的完整`url`,如果在`build`目录外调用必传`import.meta.url` */ const pathResolve = (dir = '.', metaUrl = import.meta.url) => { // 当前文件目录的绝对路径 const currentFileDir = dirname(fileURLToPath(metaUrl)); // build 目录的绝对路径 const buildDir = resolve(currentFileDir, 'build'); // 解析的绝对路径 const resolvedPath = resolve(currentFileDir, dir); // 检查解析的绝对路径是否在 build 目录内 if (resolvedPath.startsWith(buildDir)) { // 在 build 目录内,返回当前文件路径 return fileURLToPath(metaUrl); } // 不在 build 目录内,返回解析后的绝对路径 return resolvedPath; }; /** 设置别名 */ const alias: Record = { '@': pathResolve('../src'), '@build': pathResolve(), }; /** 处理环境变量 */ const wrapperEnv = (envConf: Recordable): ImportMetaEnv => { // 默认值 let ret: ImportMetaEnv = {}; ret = Object.assign(ret, envConf); const setEnv = (envName: string, envValue: string | number | boolean | object) => { if (envName.startsWith('VITE_DEV_')) return; // 剔除开发环境变量,不写入 process.env if (envName.startsWith('VITE_BUILD_')) return; // 剔除构建环境变量,不写入 process.env if (typeof envValue === 'string') { process.env[envName] = envValue; } else if (typeof envValue === 'number' || typeof envValue === 'boolean') { process.env[envName] = String(envValue); } else if (typeof envValue === 'object' && envValue !== null) { process.env[envName] = JSON.stringify(envValue); } }; // 先设置默认值到 process.env for (const envName of Object.keys(ret)) { setEnv(envName, ret[envName]); } // 然后用环境变量覆盖默认值 for (const envName of Object.keys(envConf)) { const raw = envConf[envName]; let envValue: string | number | boolean = typeof raw === 'string' ? raw.replace(/\\n/g, '\n') : String(raw); envValue = envValue === 'true' ? true : envValue === 'false' ? false : envValue; if (envName === 'VITE_DEV_PORT') { envValue = Number(envValue); } ret[envName] = envValue; setEnv(envName, envValue); } return ret; }; const fileListTotal: number[] = []; /** * @description 计算数组元素之和 * @param arr 数字数组 * @returns 数组元素之和 */ const sum = (arr: number[]): number => arr.reduce((a, b) => a + b, 0); /** * @description 将字节单位智能转化成 Bytes 、 KB 、 MB 、 GB 、 TB 、 PB 、 EB 、 ZB 、 YB 其中的一种 * @param byte — 字节 * @param digits — 四舍五入保留几位小数(默认四舍五入保留两位小数) * @returns — 智能转化字节单位后的值 */ const formatBytes = (byte: number, digits = 2): string => { if (byte === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; const i = Math.floor(Math.log(byte) / Math.log(k)); return `${parseFloat((byte / Math.pow(k, i)).toFixed(digits))} ${sizes[i]}`; }; interface PackageSizeOptions { folder?: string; callback: (size: string | number) => void; format?: boolean; } /** 获取指定文件夹中所有文件的总大小 */ const getPackageSize = (options: PackageSizeOptions) => { const { folder = 'dist', callback, format = true } = options; readdir(folder, (err, files: string[]) => { if (err) throw err; let count = 0; const checkEnd = () => { if (++count === files.length) { callback(format ? formatBytes(sum(fileListTotal)) : sum(fileListTotal)); } }; files.forEach((item: string) => { stat(`${folder}/${item}`, async (err, stats) => { if (err) throw err; if (stats.isFile()) { fileListTotal.push(stats.size); checkEnd(); } else if (stats.isDirectory()) { getPackageSize({ folder: `${folder}/${item}/`, callback: checkEnd, }); } }); }); if (files.length === 0) { callback(0); } }); }; export { root, pathResolve, alias, wrapperEnv, getPackageSize, formatBytes };