UNPKG

env-manage-plugin

Version:

A dev env plugin that integrates an Express server with request proxying capabilities.

214 lines (213 loc) 7.73 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Config = exports.FILE_CHANGE_EVENT = void 0; const tslib_1 = require("tslib"); const fs_1 = tslib_1.__importDefault(require("fs")); const http_1 = tslib_1.__importDefault(require("http")); const path_1 = tslib_1.__importDefault(require("path")); const chokidar_1 = tslib_1.__importDefault(require("chokidar")); const events_1 = require("events"); const Utils_js_1 = tslib_1.__importDefault(require("./Utils.js")); const types_js_1 = require("./types.js"); exports.FILE_CHANGE_EVENT = "filechange"; class Config { constructor(isPlugin = false) { /** * 环境信息列表 */ this.envMap = new Map(); /** * 开发服务器列表 */ this.devServerMap = new Map(); /** * 事件总线 */ this.bus = new events_1.EventEmitter(); if (Config.instance) { return Config.instance; } Config.instance = this; this.isPlugin = isPlugin; } /** * 初始化配置文件加载器 * @param configPath * @returns */ async initConfig(configPath) { const localConfigPath = this.resolveAndValidateConfigPath(configPath); this.filePath = path_1.default.resolve(process.cwd(), localConfigPath); await this.loadConfig(); await this.checkPortAsync(); this.watchConfig(); } /** * 解析并验证配置文件路径 * @param configPath 配置文件路径(可选) * @returns 配置文件路径 */ resolveAndValidateConfigPath(configPath) { const finalConfigPath = configPath || this.resolveDefaultConfigFilePath(); if (!fs_1.default.existsSync(finalConfigPath)) { const message = this.isPlugin ? "无法找到配置文件,请在插件配置中指定配置文件!" : "无法找到配置文件,请使用 npx envm init 初始化配置文件!或者通过 --config 指定配置文件!"; console.error(message); throw new Error("配置文件不存在"); } return finalConfigPath; } /** * 解析默认的配置文件路径 * @returns 配置文件路径或 null */ resolveDefaultConfigFilePath() { const exts = [".js", ".cjs", ".mjs"]; for (const ext of exts) { const configFilePath = path_1.default.resolve(process.cwd(), `envm.config${ext}`); if (fs_1.default.existsSync(configFilePath)) { return configFilePath; } } return null; } /** * 重新加载配置文件 */ loadConfig() { const modulePath = this.filePath; delete require.cache[require.resolve(modulePath)]; return Promise.resolve(`${modulePath}`).then(s => tslib_1.__importStar(require(s))).then((module) => { return module.default || module; }) .then((res) => { this.resolveConfig(res); }) .catch((err) => { console.error(`配置文件加载失败 ${modulePath}:`, err); }); } /** * 转换读取到的配置,设置默认值,去除重复数据 * @param resolveDConfig * @returns */ resolveConfig(resolveDConfig) { let { port = 3099, envList = [], indexPage = "", devServerList = [], cookieSuffix = "envm", isEnableCookieProxy = true, basePath = "/dev-manage-api", } = resolveDConfig; devServerList = Utils_js_1.default.removeEnvDuplicates(devServerList); this.devServerMap = Utils_js_1.default.generateMap(devServerList); const defaultDevServerName = devServerList[0]?.name; envList = Utils_js_1.default.removeEnvDuplicates(envList); envList = envList.map((item) => { const rowKey = Utils_js_1.default.getRowKey(item); const oldEnvItem = this.envMap.get(rowKey); let devServerName = `${oldEnvItem?.devServerName || item?.devServerName}`; const devServerKey = Utils_js_1.default.getRowKey({ name: devServerName }); if (!this.devServerMap.has(devServerKey)) { devServerName = defaultDevServerName; } return { ...item, indexPage: `${item?.indexPage ?? indexPage}`, isEnableCookieProxy: item?.isEnableCookieProxy ?? isEnableCookieProxy, status: oldEnvItem?.status ?? types_js_1.APP_STATUS.STOP, devServerName, }; }); this.envMap = Utils_js_1.default.generateMap(envList); this.envmConfig = { ...resolveDConfig, port, envList, basePath, indexPage, cookieSuffix, devServerList, isEnableCookieProxy, }; } watchConfig() { const watcher = chokidar_1.default.watch(this.filePath, { persistent: true, }); let debounceTimer; watcher.on("change", () => { console.log("Config file changed"); clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { this.loadConfig() .then(() => { this.bus.emit(exports.FILE_CHANGE_EVENT, { action: "filechange", }); console.log("Config file reloaded"); }) .catch((error) => { console.error("Error updating config:", error); }); }, 500); }); } /** * 判断端口是否被占用,如果被占用的,查询是否已经启动 * @returns */ checkPortAsync() { return Utils_js_1.default.isPortOccupied(this.envmConfig.port).then((result) => { if (result) { return this.checkIsRunning().then((res) => { console.log(`服务已启动在端口 ${this.envmConfig.port} 配置文件地址为 ${res.filePath} 请检查`); throw new Error("服务已经启动"); }); } }); } /** * 对应代理启动之后,更新状态 * @param envKey */ startServer(envKey) { this.envMap.get(envKey).status = types_js_1.APP_STATUS.RUNNING; } /** * 对应的代理关闭之后,更新状态 * @param envKey */ stopServer(envKey) { this.envMap.get(envKey).status = types_js_1.APP_STATUS.STOP; } /** * 查询端口,中启动的服务是否为 envm * @returns */ checkIsRunning() { const options = { hostname: "127.0.0.1", port: this.envmConfig.port, path: `${this.envmConfig.basePath}/are-you-ok`, method: "GET", }; return new Promise((resolve, reject) => { const req = http_1.default.request(options, (res) => { if (res.statusCode < 200 || res.statusCode >= 300) { reject(new Error(`请求失败,状态码: ${res.statusCode}`)); return; } let responseData = ""; res.on("data", (chunk) => { responseData += chunk; }); res.on("end", () => { const result = JSON.parse(responseData); resolve(result); }); }); req.on("error", (error) => { reject(error); }); req.end(); }); } } exports.Config = Config;