UNPKG

express-wx

Version:

Express Router for building Wechat Offical Account Message Server easily, supporting loading request (message) handlers dynamically. 快速构建微信公众号消息后端,支持动态加载请求处理逻辑代码。

139 lines (138 loc) 5.61 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateOnChangeCb = exports.loadRouter = exports.watchRecursively = exports.NocacheRequire = void 0; const Fs = require("fs"); const util_1 = require("util"); const Path = require("path"); function NocacheRequire(path) { path = Path.resolve(path); delete require.cache[path]; return require(path); } exports.NocacheRequire = NocacheRequire; let watchers = {}; async function watchRecursively(path, extraCallback) { const callback = async (event, filename) => { var _a; filename = Path.resolve(filename); let stat; try { stat = await (0, util_1.promisify)(Fs.stat)(filename); } catch (e) { } if (event == "rename" && !stat) { let type = watchers[filename] ? "dirRemove" : "fileRemove"; (_a = watchers[filename]) === null || _a === void 0 ? void 0 : _a.close(); delete watchers[filename]; extraCallback(event, filename, type); } else if (event == "rename" && stat) { if (stat.isDirectory()) { watchDir(filename); extraCallback(event, filename, "dirAdd"); } else { extraCallback(event, filename, "fileAdd"); } } else { extraCallback(event, filename, "fileChange"); } }; function watchDir(dirname = path) { (async () => { var _a; (_a = watchers[dirname]) === null || _a === void 0 ? void 0 : _a.close(); watchers[dirname] = Fs.watch(dirname, (event, filename) => callback(event, Path.join(dirname, filename))) .addListener('error', (err) => { console.warn(err.message, dirname); }); for await (let dir of await (0, util_1.promisify)(Fs.opendir)(dirname)) { if (dir.isDirectory()) watchDir(Path.join(dirname, dir.name)); } })().catch(reason => { console.warn(reason.message); }); } let stat = await (0, util_1.promisify)(Fs.stat)(Path.resolve(path)); if (stat.isDirectory()) watchDir(); // 如果是目录,调用watchDir方法watch这个目录,其内部会自动递归watchDir所有子目录 else { // 如果是文件,则只watch单个文件 let dirname = Path.dirname(Path.resolve(path)); Fs.watch(path, (event, filename) => callback(event, Path.join(dirname, filename))); } } exports.watchRecursively = watchRecursively; /** * 加载动态路由。path可以是目录也可以是文件名,目录的话就会递归加载,文件名就是只加载一个、返回只有一个元素的对象。 */ async function loadRouter(path) { path = Path.resolve(path); let stat; try { stat = await (0, util_1.promisify)(Fs.stat)(path); if (stat.isDirectory()) { let result = {}; let files = await (0, util_1.promisify)(Fs.readdir)(path); for (let file of files) { file = Path.join(path, file); Object.assign(result, await loadRouter(file)); } return result; } else { if (Path.extname(path) !== ".js") return {}; else { let module = NocacheRequire(path); module = module.default || module; if (typeof module !== "function") return {}; // 尝试从文件名中获得优先级数值 // 文件名中的优先级数值高于文件内priority属性定义的优先级数值, // 因此用正则判断文件名上是否有优先级数值的定义,如果有必定覆盖priority属性。 let splited = Path.basename(path, ".js").split("_"); let lastPartStr = splited[splited.length - 1]; let lastPartNumber = lastPartStr.match(/^-?\d+(.\d+)?$/) ? Number(lastPartStr) : null; if (lastPartNumber) module.priority = lastPartNumber; // 如果导入的对象上不存在nameForLog属性,则添加上文件名作为默认值,以备可能的日志记录中使用 module.nameForLog = module.nameForLog || Path.basename(path, ".js"); let obj = {}; obj[path] = module; return obj; } } } catch (e) { return {}; } } exports.loadRouter = loadRouter; function generateOnChangeCb(wxRouter) { return async function (event, filename, type) { if (type === "dirAdd" || type === "fileAdd" || type === "fileChange") { let newRouters = await loadRouter(filename); wxRouter._handlerMap = Object.assign(wxRouter._handlerMap, newRouters); } else if (type === "fileRemove") { let old = wxRouter._handlerMap; delete old[filename]; wxRouter._handlerMap = old; } else if (type === "dirRemove") { let old = wxRouter._handlerMap; for (let key in old) { if (Path.relative(filename, key).indexOf("..") !== 0) { //key相对于filename的相对路径的开头不是..,即key是filename目录的子文件。 delete old[key]; } } wxRouter._handlerMap = old; } }; } exports.generateOnChangeCb = generateOnChangeCb;