简介
核心工具模块。提供高层模块(FileLoader、ControllerLoader、ConfigLoader 等)使用的基础能力。包括:
- FileLoader:通用文件加载器,扫描目录并根据目录结构将导出内容组织为嵌套对象,支持同步、异步和注册表加载模式。
- Timing:性能计时器,记录框架阶段耗时。
- 工具函数:
loadFile、loadFileAsync、callFn、getResolvedFilename、isBytecodeClass、filePatterns、extensions。
导入
CJS 和 ESM:
// ESM
import { FileLoader, FULLPATH, EXPORTS } from 'ee-core/core/loader/file_loader';
import { Timing } from 'ee-core/core/utils/timing';
import { loadFile, loadFileAsync, callFn, getResolvedFilename, isBytecodeClass, filePatterns, extensions } from 'ee-core/core/utils';
// CJS
const { FileLoader, FULLPATH, EXPORTS } = require('ee-core/core/loader/file_loader');
const { Timing } = require('ee-core/core/utils/timing');
const { loadFile, loadFileAsync, callFn, getResolvedFilename, isBytecodeClass, filePatterns, extensions } = require('ee-core/core/utils');API
FULLPATH
说明:Symbol 常量(Symbol('LOADER_ITEM_FULLPATH'))。标记非原始导出的完整文件路径,用于调试和 IPC 路由。由 FileLoader._assignToTarget() 设置在导出对象上。 返回值:symbol — FULLPATH symbol
EXPORTS
说明:Symbol 常量(Symbol('LOADER_ITEM_EXPORTS'))。标记非原始导出已被 FileLoader 处理。由 FileLoader._assignToTarget() 设置为 true。 返回值:symbol — EXPORTS symbol
FileLoader 类
通用文件加载器,根据目录结构将文件导出内容组织为嵌套属性对象。三种加载方法:
parse():同步文件系统扫描 +require(),适用于 CJS dev 模式parseFromRegistry():从预注册表加载,适用于 bundle 模式parseAsync():异步文件系统扫描 +import(),适用于 ESM dev 模式
目录到属性的映射示例:
controller/user.js -> target.controller.user
controller/admin/login.js -> target.controller.admin.login属性命名由 caseStyle 控制:
'camel':驼峰命名(默认)—user-info->userInfo'lower':全小写 — 控制器使用'upper':首字母大写- 自定义函数:完全自定义转换逻辑
FileLoader.constructor(options)
说明:创建 FileLoader 实例。options.directory 必填;其他选项与默认值合并(caseStyle: 'camel'、call: true、initializer: null、inject: undefined、target: null、match: undefined)。 参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| options | FileLoaderOptions | 是 | 加载器配置 |
| options.directory | string | 是 | 要扫描的目录路径 |
| options.caseStyle | 'lower' | 'upper' | 'camel' | ((filepath: string) => string[]) | 否 | 属性命名风格,默认 'camel' |
| options.initializer | ((obj: unknown, options: { pathName: string; path: string }) => unknown) | null | 否 | 自定义导出处理器,默认 null |
| options.call | boolean | 否 | 是否自动调用函数导出,默认 true |
| options.inject | unknown | 否 | 自动调用函数的注入参数 |
| options.match | string[] | undefined | 否 | 文件匹配模式(覆盖默认 filePatterns) |
| options.registry | RegistryEntry[] | 否 | bundle 模式的预注册条目 |
返回值:FileLoader 实例 示例:
import { FileLoader } from 'ee-core/core/loader/file_loader';
const loader = new FileLoader({
directory: path.join(getElectronDir(), 'controller'),
caseStyle: 'lower',
initializer: (obj, options) => {
if (isClass(obj)) {
obj.prototype.pathName = options.pathName;
return wrapClass(obj);
}
return obj;
},
});
const result = loader.load();FileLoader.load()
说明:加载文件并构建嵌套目标对象。当 options.registry 存在时使用注册表模式(bundle 模式),否则回退到文件系统扫描(dev 模式,通过 parse())。通过 _assignToTarget() 将所有加载项分配到嵌套对象。 参数:无 返回值:Record<string, unknown> — 按目录结构组织的嵌套属性对象
FileLoader.loadAsync()
说明:load() 的异步版本。使用 parseAsync(),其中用 fs.promises 扫描并用动态 import() 加载模块。适用于 ESM dev 模式。 参数:无 返回值:Promise<Record<string, unknown>> — 按目录结构组织的嵌套属性对象
FileLoader.parse()
说明:从文件系统同步解析文件。使用 scanDirSync() 递归扫描和 require() 加载模块。支持 options.directory 为字符串或目录数组。 参数:无 返回值:LoaderItem[] — 加载项数组,结构为 { fullpath, properties, exports }
FileLoader.parseFromRegistry()
说明:从预注册表解析模块。在 bundle 模式下,esbuild 插件将控制器/配置信息预注册到全局变量。此方法直接读取注册表,无需文件系统扫描。处理 ESM 互操作:带 __esModule 标记的模块提取 default 导出。 参数:无 返回值:LoaderItem[] — 加载项数组,结构为 { fullpath, properties, exports }
FileLoader.parseAsync()
说明:从文件系统异步解析文件。使用 scanDirAsync() 和动态 import()。适用于 ESM dev 模式。 参数:无 返回值:Promise<LoaderItem[]> — 加载项数组,结构为 { fullpath, properties, exports }
Timing 类
性能计时器,记录框架阶段耗时。由 ConfigLoader 和 ControllerLoader 使用,记录如"加载配置"、"加载控制器"等阶段耗时。支持嵌套计时——重复调用 start() 同名项会自动先结束前一个。
import { Timing } from 'ee-core/core/utils/timing';
const timing = new Timing();
timing.start('Load Controller');
// ... 执行加载
timing.end('Load Controller');
const items = timing.toJSON(); // 获取所有计时记录Timing.constructor()
说明:创建新的 Timing 实例并初始化,记录进程启动时间和脚本启动时间(如可用)。 参数:无 返回值:Timing 实例
Timing.start(name, start)
说明:开始计时。如果同名计时项已存在,会先结束前一个。记录 process.pid 和顺序索引。 参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| name | string | 是 | 计时项名称(如 'Load Controller') |
| start | number | 否 | 自定义起始时间戳(毫秒);未提供时使用 Date.now() |
返回值:TimingItem \| undefined — 计时项对象;名称为空或计时禁用时返回 undefined
Timing.end(name)
说明:结束计时。计算 duration 为 end - start 毫秒。如果指定名称的计时项不存在(即未先调用 start()),则抛出错误。 参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| name | string | 是 | 计时项名称(须与之前的 start() 调用匹配) |
返回值:TimingItem \| undefined — 包含 duration 的计时项对象;名称为空或计时禁用时返回 undefined
Timing.enable()
说明:启用计时记录。 参数:无 返回值:void
Timing.disable()
说明:禁用计时记录。后续所有 start() 和 end() 调用返回 undefined。 参数:无 返回值:void
Timing.clear()
说明:清除所有计时记录,重置内部 map 和有序列表。 参数:无 返回值:void
Timing.toJSON()
说明:将所有计时记录导出为可 JSON 序列化的数组,按 start() 调用时的时间顺序排列。 参数:无 返回值:TimingItem[] — 按时间顺序排列的计时项列表,每项包含 { name, start, end, duration, pid, index }
loadFile(filepath)
说明:同步加载文件。对于非 JS 文件(扩展名不在 Module._extensions 中),返回 Buffer 形式的文件内容。对于 JS/CJS/JSC 文件,使用 require() 加载模块。处理 ESM 互操作:带 __esModule 标记的模块提取 default 导出。通过已注册的扩展支持 bytenode .jsc。 参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| filepath | string | 是 | 绝对文件路径 |
返回值:unknown — 文件内容(非 JS 为 Buffer)或模块导出值 示例:
import { loadFile } from 'ee-core/core/utils';
const exports = loadFile('/path/to/controller/user.js');loadFileAsync(filepath)
说明:异步加载文件(ESM 支持)。与 loadFile 逻辑相同,但使用动态 import() 加载模块,使用 fs.promises.readFile() 读取非 JS/ESM 文件。同时支持 ESM 和 CJS 模块格式。 参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| filepath | string | 是 | 绝对文件路径 |
返回值:Promise<unknown> — 文件内容(非 JS 为 Buffer)或模块导出值
callFn(fn, args, ctx)
说明:带可选上下文绑定调用函数。如果提供 ctx,调用 fn.call(ctx, ...args);否则直接调用 fn(...args)。如果 fn 不是函数则返回 undefined。由 methodToMiddleware() 内部使用,在新建实例上调用控制器方法。 参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| fn | (...args: unknown[]) => unknown | 是 | 要调用的函数 |
| args | unknown[] | 否 | 参数列表,默认 [] |
| ctx | unknown | 否 | 执行上下文(this 绑定) |
返回值:Promise<unknown> — 函数返回值;fn 不是函数时返回 undefined
getResolvedFilename(filepath, baseDir)
说明:将绝对文件路径转换为基于 baseDir 的相对路径,使用正斜杠作为分隔符。内部用于计算属性路径。 参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| filepath | string | 是 | 绝对文件路径 |
| baseDir | string | 是 | 基准目录 |
返回值:string — 使用正斜杠分隔符的相对路径
isBytecodeClass(exports)
说明:判断导出是否为字节码类(来自 bytenode 编译的 .jsc 文件)。检查 String(exports) 是否包含 [class。处理 Node.js 中 bytenode 类显示为 [class XXX] 的情况。 参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| exports | unknown | 是 | 要检查的导出值 |
返回值:boolean — 是否为字节码类;null/undefined 时返回 false
filePatterns()
说明:获取目录扫描支持的文件匹配模式。用于早期基于 globby 的扫描;现在框架内部使用 scanDirSync/scanDirAsync,但此函数仍保留以兼容。 参数:无 返回值:string[] — 模式列表:['**/*.js', '**/*.jsc']
extensions
说明:Node.js 原生模块扩展映射(Module._extensions)。包含 .js、.json、.node 及 bytenode 注册的 .jsc。由 loadFile 和 loadFileAsync 用于判断文件应以模块加载还是以原始内容读取。 返回值:NodeJS.RequireExtensions — 扩展处理器映射
