Introduction
Core utility module. Provides foundational capabilities used by higher-level modules (FileLoader, ControllerLoader, ConfigLoader, etc.). Includes:
- FileLoader: Generic file loader that scans directories and organizes exported content into nested objects based on directory structure, with support for synchronous, asynchronous, and registry-based loading modes.
- Timing: Performance timer for recording framework stage durations.
- Utility functions:
loadFile,loadFileAsync,callFn,getResolvedFilename,isBytecodeClass,filePatterns,extensions.
Import
Both CJS and 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
Description: Symbol constant (Symbol('LOADER_ITEM_FULLPATH')). Marks a non-primitive export with its full file path, used for debugging and IPC routing. Set on exported objects by FileLoader._assignToTarget(). Returns: symbol — The FULLPATH symbol
EXPORTS
Description: Symbol constant (Symbol('LOADER_ITEM_EXPORTS')). Marks a non-primitive export as having been processed by the FileLoader. Set to true on exported objects by FileLoader._assignToTarget(). Returns: symbol — The EXPORTS symbol
FileLoader class
Generic file loader that organizes exported content from files in a directory into nested property objects based on directory structure. Three loading methods:
parse(): Synchronous filesystem scanning +require(), suitable for CJS dev modeparseFromRegistry(): Load from pre-registered registry, suitable for bundle modeparseAsync(): Asynchronous filesystem scanning +import(), suitable for ESM dev mode
Directory-to-property mapping example:
controller/user.js -> target.controller.user
controller/admin/login.js -> target.controller.admin.loginProperty naming is controlled by caseStyle:
'camel': camelCase (default) —user-info->userInfo'lower': all lowercase — used by controllers'upper': first letter uppercase- Custom function: fully custom conversion logic
FileLoader.constructor(options)
Description: Creates a FileLoader instance. options.directory is required; other options merge with defaults (caseStyle: 'camel', call: true, initializer: null, inject: undefined, target: null, match: undefined). Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| options | FileLoaderOptions | Yes | Loader configuration |
| options.directory | string | Yes | Directory path(s) to scan |
| options.caseStyle | 'lower' | 'upper' | 'camel' | ((filepath: string) => string[]) | No | Property naming style, default 'camel' |
| options.initializer | ((obj: unknown, options: { pathName: string; path: string }) => unknown) | null | No | Custom export processor, default null |
| options.call | boolean | No | Whether to auto-invoke function exports, default true |
| options.inject | unknown | No | Injection arguments for auto-called functions |
| options.match | string[] | undefined | No | File match patterns (overrides default filePatterns) |
| options.registry | RegistryEntry[] | No | Pre-registry entries for bundle mode |
Returns: FileLoader instance Example:
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()
Description: Load files and build the nested target object. Uses registry mode when options.registry is present (bundle mode), otherwise falls back to filesystem scanning (dev mode via parse()). Assigns all loaded items to a nested object via _assignToTarget(). Parameters: None Returns: Record<string, unknown> — Nested property object organized by directory structure
FileLoader.loadAsync()
Description: Asynchronous version of load(). Uses parseAsync() which scans with fs.promises and loads modules with dynamic import(). Suitable for ESM dev mode. Parameters: None Returns: Promise<Record<string, unknown>> — Nested property object organized by directory structure
FileLoader.parse()
Description: Parse files synchronously from filesystem. Uses scanDirSync() recursive scanning and require() to load modules. Supports options.directory as either a string or array of directories. Parameters: None Returns: LoaderItem[] — Array of load items with { fullpath, properties, exports } structure
FileLoader.parseFromRegistry()
Description: Parse modules from a pre-registered registry. In bundle mode, the esbuild plugin pre-registers controller/config info into global variables. This method reads the registry directly without filesystem scanning. Handles ESM interop: modules with __esModule marker extract the default export. Parameters: None Returns: LoaderItem[] — Array of load items with { fullpath, properties, exports } structure
FileLoader.parseAsync()
Description: Parse files asynchronously from filesystem. Uses scanDirAsync() and dynamic import(). Suitable for ESM dev mode. Parameters: None Returns: Promise<LoaderItem[]> — Array of load items with { fullpath, properties, exports } structure
Timing class
Performance timer for recording framework stage durations. Used by ConfigLoader and ControllerLoader to record timing for stages like "Load Config", "Load Controller". Supports nested timing — repeatedly calling start() with the same name automatically ends the previous one first.
import { Timing } from 'ee-core/core/utils/timing';
const timing = new Timing();
timing.start('Load Controller');
// ... perform loading
timing.end('Load Controller');
const items = timing.toJSON(); // Get all timing recordsTiming.constructor()
Description: Creates a new Timing instance and initializes it, recording process start time and script start time (if available). Parameters: None Returns: Timing instance
Timing.start(name, start)
Description: Start timing. If a timing item with the same name already exists, the previous one is ended first. Records process.pid and a sequential index. Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Timing item name (e.g. 'Load Controller') |
| start | number | No | Custom start timestamp in milliseconds; uses Date.now() if not provided |
Returns: TimingItem \| undefined — Timing item object; returns undefined if name is empty or timing is disabled
Timing.end(name)
Description: End timing. Computes duration as end - start milliseconds. Throws an error if the named timing item does not exist (i.e., start() was not called first). Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Timing item name (must match a previous start() call) |
Returns: TimingItem \| undefined — Timing item object including duration; returns undefined if name is empty or timing is disabled
Timing.enable()
Description: Enable timing recording. Parameters: None Returns: void
Timing.disable()
Description: Disable timing recording. All subsequent start() and end() calls return undefined. Parameters: None Returns: void
Timing.clear()
Description: Clear all timing records, resetting both the internal map and ordered list. Parameters: None Returns: void
Timing.toJSON()
Description: Export all timing records as a JSON-serializable array, ordered chronologically by when start() was called. Parameters: None Returns: TimingItem[] — Chronologically ordered list of timing items, each with { name, start, end, duration, pid, index }
loadFile(filepath)
Description: Load a file synchronously. For non-JS files (extensions not in Module._extensions), returns file content as a Buffer. For JS/CJS/JSC files, loads the module using require(). Handles ESM interop: modules with __esModule marker extract the default export. Includes bytenode .jsc support via registered extensions. Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| filepath | string | Yes | Absolute file path |
Returns: unknown — File content (Buffer for non-JS) or module export value Example:
import { loadFile } from 'ee-core/core/utils';
const exports = loadFile('/path/to/controller/user.js');loadFileAsync(filepath)
Description: Load a file asynchronously (ESM support). Same logic as loadFile, but uses dynamic import() for module loading and fs.promises.readFile() for non-JS/ESM files. Supports both ESM and CJS module formats. Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| filepath | string | Yes | Absolute file path |
Returns: Promise<unknown> — File content (Buffer for non-JS) or module export value
callFn(fn, args, ctx)
Description: Call a function with optional context binding. If ctx is provided, calls fn.call(ctx, ...args); otherwise calls fn(...args) directly. Returns undefined if fn is not a function. Used internally by methodToMiddleware() to invoke controller methods on new instances. Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| fn | (...args: unknown[]) => unknown | Yes | Function to call |
| args | unknown[] | No | Argument list, defaults to [] |
| ctx | unknown | No | Execution context (this binding) |
Returns: Promise<unknown> — Function return value; undefined if fn is not a function
getResolvedFilename(filepath, baseDir)
Description: Convert an absolute file path to a relative path based on baseDir, using forward slashes as separators. Used internally for computing property paths. Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| filepath | string | Yes | Absolute file path |
| baseDir | string | Yes | Base directory to resolve against |
Returns: string — Relative path with forward slash separators
isBytecodeClass(exports)
Description: Determine if an export is a bytecode class (from bytenode-compiled .jsc files). Checks whether String(exports) contains [class. This handles the case where bytenode classes appear as [class XXX] in Node.js. Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| exports | unknown | Yes | Export value to check |
Returns: boolean — Whether the export is a bytecode class; false for null/undefined
filePatterns()
Description: Get supported file match patterns for directory scanning. Used by globby-based scanning in earlier versions; now the framework uses scanDirSync/scanDirAsync internally but this remains available for compatibility. Parameters: None Returns: string[] — Pattern list: ['**/*.js', '**/*.jsc']
extensions
Description: Node.js native module extension map (Module._extensions). Includes .js, .json, .node, and bytenode-registered .jsc. Used by loadFile and loadFileAsync to determine whether a file should be loaded as a module or read as raw content. Returns: NodeJS.RequireExtensions — Extension handler map
