env-manage-plugin
Version:
A dev env plugin that integrates an Express server with request proxying capabilities.
214 lines (213 loc) • 7.73 kB
JavaScript
;
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;