import fs from 'node:fs'; // import { AssistantConfig, checkFileExists } from '@/module/assistant/index.ts'; import { checkFileExists } from './file.ts'; import path from 'node:path'; type AssistantConfig = { configPath?: { pagesDir?: string; }; getCacheAssistantConfig?: () => { watch?: { enabled?: boolean; }; }; }; type ProxyType = { user: string; key: string; path: string; type?: 'file'; file: { indexPath: string; absolutePath: string; }; }; export type LocalProxyOpts = { watch?: boolean; // 是否监听文件变化 pagesDir?: string; // 前端应用路径 init?: boolean; }; export class LocalProxy { localProxyProxyList: ProxyType[] = []; pagesDir: string; watch?: boolean; watching?: boolean; initing?: boolean; constructor(opts?: LocalProxyOpts) { this.pagesDir = opts?.pagesDir || ''; this.watch = opts?.watch ?? false; if (opts.init) { this.init(); if (this.watch) { this.onWatch(); } } } initFromAssistantConfig(assistantConfig?: AssistantConfig) { if (!assistantConfig) return; this.pagesDir = assistantConfig.configPath?.pagesDir || ''; this.watch = assistantConfig.getCacheAssistantConfig?.()?.watch?.enabled ?? true; this.init(); if (this.watch) { this.onWatch(); } } init() { const frontAppDir = this.pagesDir; if (frontAppDir) { if (this.initing) { return; } if (!checkFileExists(frontAppDir)) { fs.mkdirSync(frontAppDir, { recursive: true }); } this.initing = true; const userList = fs.readdirSync(frontAppDir); const localProxyProxyList: ProxyType[] = []; userList.forEach((user) => { const userPath = path.join(frontAppDir, user); const stat = fs.statSync(userPath); if (stat.isDirectory()) { const appList = fs.readdirSync(userPath); appList.forEach((app) => { const appPath = path.join(userPath, app); const indexPath = path.join(appPath, 'index.html'); if (!checkFileExists(indexPath, true)) { return; } // const appPath = `${appPath}/index.html`; if (checkFileExists(indexPath, true)) { localProxyProxyList.push({ user: user, key: app, path: `/${user}/${app}/`, file: { indexPath: `${user}/${app}/index.html`, absolutePath: appPath, } }); } }); } }); this.localProxyProxyList = localProxyProxyList; this.initing = false; } } onWatch() { // 监听文件变化 const frontAppDir = this.pagesDir; const that = this; if (!this.watch && !frontAppDir) { return; } if (this.watching) { return; } that.watching = true; let timer: NodeJS.Timeout; const debounce = (fn: () => void, delay: number) => { if (timer) { clearTimeout(timer); } timer = setTimeout(() => { fn(); }, delay); }; fs.watch(frontAppDir, { recursive: true }, (eventType, filename) => { if (eventType === 'rename' || eventType === 'change') { // 过滤 node_modules 目录 if (filename && filename.includes('node_modules')) { return; } // 只监听 js、html、css 文件 const validExtensions = ['.js', '.html', '.css', '.json', '.png']; const hasValidExtension = validExtensions.some(ext => filename && filename.endsWith(ext)); if (!hasValidExtension) { return; } const filePath = path.join(frontAppDir, filename); try { const stat = fs.statSync(filePath); if (stat.isFile() || stat.isDirectory()) { // 重新加载 debounce(that.init.bind(that), 5 * 1000); } } catch (error) { } } }); } getLocalProxyList() { return this.localProxyProxyList; } reload() { this.init(); } }