Run tests for @artus/core
master@artus/core uses jest for unit testing. You can run the test suite using npm, yarn, or pnpm.
npm run test
# Or
yarn test
# Or
pnpm run testrepository·master·Indexed 42 days ago
https://github.com/artusjs/coreThe central TypeScript package of the Artus framework. It provides core functionality including the ConfigurationHandler for multi-environment settings, LifecycleManager for application hooks (such as configWillLoad and didReady), a loader system via LoaderFactory and BaseLoader, and a dependency injection system using the artus# prefix.
@artus/core uses jest for unit testing. You can run the test suite using npm, yarn, or pnpm.
npm run test
# Or
yarn test
# Or
pnpm run testSince @artus/core is written in TypeScript, you must build the source code into Node.js modules before use. You can use npm, yarn, or pnpm to execute the build script.
npm run build
# Or
yarn build
# Or
pnpm run buildPlugin configurations can be mapped by environment. A PluginConfigEnvMap is a record where the keys are environment names (e.g., 'development', 'production') and the values are the corresponding PluginConfig objects.
// Key: Env => PluginName => Value: PluginConfigItem
type PluginConfigEnvMap = Record<string, PluginConfig>;Artus uses an ExceptionFilterMapType to associate specific error types with their corresponding handlers. An ExceptionIdentifier can be a string, a symbol, or a Constructable<Error> (the error class itself). This allows you to target specific error classes or use unique identifiers for custom error logic.
import { ExceptionFilterMapType, ExceptionFilterType } from '@artus/core/exception';
// Example of a map associating an Error class with a filter implementation
const filterMap: ExceptionFilterMapType = new Map([
[TypeError, MyTypeErrorFilterClass],
['MY_CUSTOM_ERROR_ID', MyCustomFilterClass]
]);Artus uses a hierarchical merging strategy to resolve the final configuration:
ARTUS_DEFAULT_CONFIG_ENV.DEFAULT key.EnvList provided via the dependency injection container. If not found, it checks the ARTUS_SERVER_ENV environment variable. If that is also missing, it defaults to ARTUS_DEFAULT_CONFIG_ENV.DEV.mergeConfig.This allows you to define base settings in a default file and override them specifically for dev, test, or prod environments.
When initializing a scanner, use the ScannerOptions object to define how the file system is traversed and how files are processed. Key configuration properties include:
extensions: An array of file extensions to include in the scan.needWriteFile: Boolean indicating if the scanner should write files to the disk.manifestFilePath: Optional path to a specific manifest file.useRelativePath: Boolean to determine if paths should be resolved relatively.exclude: An array of patterns or paths to exclude from the scan.configDir: The directory containing configuration files.policy: A ScanPolicy determining the scanning behavior.envs: Optional array of environment variable names to consider.plugin: An optional PluginConfig to extend scanner behavior.app: An optional Application instance.const options: ScannerOptions = {
extensions: ['.ts', '.js'],
needWriteFile: true,
configDir: './config',
policy: ScanPolicy.SOME_POLICY, // Import ScanPolicy from '../constant'
exclude: ['node_modules', 'dist'],
useRelativePath: true
};When initializing or configuring a logger in Artus, you can provide a LoggerOptions object to specify the minimum logging threshold. The level property determines which log messages are processed based on their severity.
const options: LoggerOptions = {
level: 'info' // or any valid LoggerLevel
};A Manifest is the central configuration object used by the loader. It must specify a version of '2' and contains a refMap which maps reference names to their respective items. You can optionally provide extraPluginConfig to supply additional configuration to plugins.
interface Manifest {
version: '2';
refMap: RefMap;
extraPluginConfig?: PluginConfig;
}The Application interface is the central entry point for managing an Artus application. It provides access to core services like the dependency injection container, configurationHandler, lifecycleManager, loaderFactory, and logger.
To initialize and start an application, you typically call .load(manifest) followed by .run(). You can also intercept various stages of the application lifecycle by registering hooks using .registerHook(hookName, hookFn).
// Conceptual usage of the Application interface
const app: Application = // ... initialized application instance
await app.load(myManifest);
await app.run();
app.registerHook('didReady', () => {
console.log('Application is ready!');
});The LifecycleManager class is a singleton service used to manage and trigger application lifecycle events. It allows you to register custom hook functions or register classes that use metadata to automatically bind lifecycle methods.
The manager maintains a predefined sequence of hooks:
configWillLoad: Triggered just before configuration files are loaded. Use this to dynamically modify configuration.configDidLoad: Triggered after configuration files are loaded.didLoad: Triggered after files are loaded.willReady: Triggered when plugins have finished starting.didReady: Triggered when the application has fully started.beforeClose: Triggered just before the application shuts down.You can register hooks in two ways:
registerHook(hookName, hookFn) to attach a function to a specific hook name.registerHookUnit(extClazz) on a class. If the class methods are decorated with metadata starting with the HOOK_NAME_META_PREFIX (internal constant), the LifecycleManager will automatically extract and bind those methods to the corresponding lifecycle hooks.Use emitHook(hookName, payload, reverse) to trigger the registered functions.
emitHook is called for a specific hookName, that hook is removed from the internal map.payload of any type to the hook functions.reverse: true will execute the registered functions in reverse order.// Example: Manual hook registration
lifecycleManager.registerHook('didReady', async ({ app, payload }) => {
console.log('Application is ready with payload:', payload);
});
// Triggering the hook
await lifecycleManager.emitHook('didReady', { status: 'ok' });The ScannerType interface defines the primary method for performing a scan. A scanner instance is typically created via a ScannerConstructor and provides a scan method that returns a Manifest representing the scanned project structure.
// Assuming Scanner is the implementation of ScannerConstructor
const scanner = new Scanner({
extensions: ['.ts'],
configDir: './config',
policy: ScanPolicy.DEFAULT
});
// Perform the scan on a specific root directory
const manifest = await scanner.scan('./my-project-root');The LifecycleManager provides methods to modify the order of the lifecycle execution sequence:
insertHook(existHookName: string, newHookName: string): Inserts a newHookName immediately after the existHookName in the execution sequence.appendHook(newHookName: string): Adds a newHookName to the end of the execution sequence.