Skip to content

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:

javascript
// 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 mode
  • parseFromRegistry(): Load from pre-registered registry, suitable for bundle mode
  • parseAsync(): 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.login

Property 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:

ParameterTypeRequiredDescription
optionsFileLoaderOptionsYesLoader configuration
options.directorystringYesDirectory path(s) to scan
options.caseStyle'lower' | 'upper' | 'camel' | ((filepath: string) => string[])NoProperty naming style, default 'camel'
options.initializer((obj: unknown, options: { pathName: string; path: string }) => unknown) | nullNoCustom export processor, default null
options.callbooleanNoWhether to auto-invoke function exports, default true
options.injectunknownNoInjection arguments for auto-called functions
options.matchstring[] | undefinedNoFile match patterns (overrides default filePatterns)
options.registryRegistryEntry[]NoPre-registry entries for bundle mode

Returns: FileLoader instance Example:

javascript
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.

javascript
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 records

Timing.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:

ParameterTypeRequiredDescription
namestringYesTiming item name (e.g. 'Load Controller')
startnumberNoCustom 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:

ParameterTypeRequiredDescription
namestringYesTiming 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:

ParameterTypeRequiredDescription
filepathstringYesAbsolute file path

Returns: unknown — File content (Buffer for non-JS) or module export value Example:

javascript
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:

ParameterTypeRequiredDescription
filepathstringYesAbsolute 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:

ParameterTypeRequiredDescription
fn(...args: unknown[]) => unknownYesFunction to call
argsunknown[]NoArgument list, defaults to []
ctxunknownNoExecution 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:

ParameterTypeRequiredDescription
filepathstringYesAbsolute file path
baseDirstringYesBase 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:

ParameterTypeRequiredDescription
exportsunknownYesExport 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