/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-function-type */ import path from 'node:path' import fs from 'node:fs' import { createHash } from 'node:crypto' import { createRequire } from 'node:module' import { loadEnv as viteLoadEnv } from 'vite' export function isObject(value: unknown): value is Record { return Object.prototype.toString.call(value) === '[object Object]' } export const wildcardHosts = new Set(['0.0.0.0', '::', '0000:0000:0000:0000:0000:0000:0000:0000']) export function resolveHostname(optionsHost: string | boolean | undefined): string { return typeof optionsHost === 'string' && !wildcardHosts.has(optionsHost) ? optionsHost : 'localhost' } export const queryRE = /\?.*$/s export const hashRE = /#.*$/s export const cleanUrl = (url: string): string => url.replace(hashRE, '').replace(queryRE, '') export function getHash(text: Buffer | string): string { return createHash('sha256') .update(text as unknown as Uint8Array) .digest('hex') .substring(0, 8) } export function toRelativePath(filename: string, importer: string): string { const relPath = path.posix.relative(path.dirname(importer), filename) return relPath.startsWith('.') ? relPath : `./${relPath}` } /** * Load `.env` files within the `envDir` (default: `process.cwd()`) . * By default, only env variables prefixed with `VITE_`, `MAIN_VITE_`, `PRELOAD_VITE_` and * `RENDERER_VITE_` are loaded, unless `prefixes` is changed. */ export function loadEnv( mode: string, envDir: string = process.cwd(), prefixes: string | string[] = ['VITE_', 'MAIN_VITE_', 'PRELOAD_VITE_', 'RENDERER_VITE_'] ): Record { return viteLoadEnv(mode, envDir, prefixes) } interface PackageData { main?: string type?: 'module' | 'commonjs' dependencies?: Record } let packageCached: PackageData | null = null export function loadPackageData(root = process.cwd()): PackageData | null { if (packageCached) return packageCached const pkg = path.join(root, 'package.json') if (fs.existsSync(pkg)) { const _require = createRequire(import.meta.url) const data = _require(pkg) packageCached = { main: data.main, type: data.type, dependencies: data.dependencies } return packageCached } return null } export function isFilePathESM(filePath: string): boolean { if (/\.m[jt]s$/.test(filePath) || filePath.endsWith('.ts')) { return true } else if (/\.c[jt]s$/.test(filePath)) { return false } else { const pkg = loadPackageData() return pkg?.type === 'module' } } type DeepWritable = T extends ReadonlyArray ? { -readonly [P in keyof T]: DeepWritable } : T extends RegExp ? RegExp : T[keyof T] extends Function ? T : { -readonly [P in keyof T]: DeepWritable } export function deepClone(value: T): DeepWritable { if (Array.isArray(value)) { return value.map(v => deepClone(v)) as DeepWritable } if (isObject(value)) { const cloned: Record = {} for (const key in value) { cloned[key] = deepClone(value[key]) } return cloned as DeepWritable } if (typeof value === 'function') { return value as DeepWritable } if (value instanceof RegExp) { return new RegExp(value) as DeepWritable } if (typeof value === 'object' && value != null) { throw new Error('Cannot deep clone non-plain object') } return value as DeepWritable } /** * Isolate configuration data without cloning plugins or runtime instances. * Plugins may capture their own object in hooks, so their identity must survive. */ export function cloneConfig(config: T): T { const seen = new WeakMap() const pluginArrays = new WeakMap() function clone(value: unknown, plugins = false): unknown { if (value === null || typeof value !== 'object') return value const cache = plugins ? pluginArrays : seen if (cache.has(value)) return cache.get(value) if (Array.isArray(value)) { const result: unknown[] = new Array(value.length) cache.set(value, result) value.forEach((item, index) => { result[index] = clone(item, plugins) }) return result } // Preserve plugin objects, promises and other opaque plugin options. if (plugins) return value if (value instanceof RegExp) { const result = new RegExp(value) result.lastIndex = value.lastIndex cache.set(value, result) return result } const prototype = Object.getPrototypeOf(value) if (prototype !== Object.prototype && prototype !== null) return value const result = Object.create(prototype) as Record cache.set(value, result) for (const key of Reflect.ownKeys(value)) { if (!Object.prototype.propertyIsEnumerable.call(value, key)) continue Object.defineProperty(result, key, { value: clone((value as Record)[key], key === 'plugins'), enumerable: true, configurable: true, writable: true }) } return result } return clone(config) as T } type AsyncFlatten = T extends (infer U)[] ? Exclude, U[]>[] : never export async function asyncFlatten(arr: T): Promise> { do { arr = (await Promise.all(arr)).flat(Infinity) as any } while (arr.some((v: any) => v?.then)) return arr as unknown[] as AsyncFlatten }