import { A_CommonHelper, A_Component, A_Context, A_Dependency, A_Feature, A_FormatterHelper, A_Inject, A_Scope, A_TYPES__A_DependencyInjectable, A_TYPES__Ctor } from "@adaas/a-concept"; import { AreSyntax } from "@adaas/are/syntax/AreSyntax.context"; import { A_Logger } from "@adaas/a-utils/a-logger"; import { AreCompiler } from "@adaas/are/compiler/AreCompiler.component"; import { AreTransformer } from "@adaas/are/transformer/AreTransformer.component"; import { AreLoader } from "@adaas/are/loader/AreLoader.component"; import { AreInterpreter } from "@adaas/are/interpreter/AreInterpreter.component"; import { AreEngineError } from "./AreEngine.error"; import { AreLifecycle } from "@adaas/are/lifecycle/AreLifecycle.component"; import { AreEngineFeatures } from "./AreEngine.constants"; import { AreContext } from "@adaas/are/component/Are.context"; import { A_Frame } from "@adaas/a-frame/core"; import { AreTokenizer } from "@adaas/are/tokenizer/AreTokenizer.component"; import { AreEngineDependencies } from "./AreEngine.types"; import { AreSignals } from "@adaas/are/signals/AreSignals.component"; import { AreInit } from "@adaas/are/signals/entities/AreInit.signal"; import { A_SignalBus } from "@adaas/a-utils/a-signal"; import { Are } from "@adaas/are/component/Are.component"; @A_Frame.Define({ namespace: 'A-ARE', description: 'Core rendering engine for A-Concept Rendering Engine (ARE), responsible for orchestrating the loading, building, and execution of the rendering process. It manages the lifecycle of root nodes, coordinates the interactions between syntax, transformer, loader, compiler, and interpreter components, and ensures the proper initialization and mounting of the UI application.' }) export class AreEngine extends A_Component { /** * Feature decorator for the load method, which is responsible for the initial loading phase of the engine. This method is where the engine reads the source template, tokenizes it, and prepares the initial context for building the scene. The decorator allows for extending or overriding the default loading behavior by attaching additional functionality before or after the load process. */ static get Load() { return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => { return A_Feature.Extend({ name: AreEngineFeatures.Load, scope: [target.constructor], override: ['defaultLoad'] })(target, propertyKey, descriptor); } } /** * Feature decorator for the build method, which is responsible for constructing the scene based on the loaded context. This method typically involves initializing root nodes, applying transformations, and compiling the scene into a format that can be executed by the interpreter. The decorator allows for customizing the build process by adding additional steps or modifying the existing behavior. */ static get Build() { return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => { return A_Feature.Extend({ name: AreEngineFeatures.Build, scope: [target.constructor], override: ['defaultBuild'] })(target, propertyKey, descriptor); } } /** * Feature decorator for the execute method, which is responsible for the final execution phase of the engine. This method typically involves mounting the root nodes to the DOM and starting the reactive update cycle based on signals and state changes. The decorator allows for customizing the execution process by adding additional steps or modifying the existing behavior. */ static get Execute() { return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => { return A_Feature.Extend({ name: AreEngineFeatures.Execute, scope: [target.constructor], override: ['defaultExecute'] })(target, propertyKey, descriptor); } } // ========================================================================================== // -----------------------------------ARE Engine Features------------------------------------ // ========================================================================================== /** * Method to start the engine, which involves loading necessary resources, building the scene, and executing the rendering process. It accepts an optional scope parameter that can be used to provide a custom scope for the engine's operations, allowing for greater flexibility in how dependencies are managed and accessed during the rendering lifecycle. * * @param scope * @returns */ @A_Frame.Define({ description: 'Method does engine loading, first read of the source and tokenization.' }) async load(scope?: A_Scope) { const context = scope?.resolve(AreContext) || A_Context.scope(this).resolve(AreContext); context?.startPerformance(); await this.call(AreEngineFeatures.Load, scope || A_Context.scope(this)); } /** * Method responsible for building the scene, which includes initializing root nodes, loading necessary data, applying transformations, and compiling the scene into a format that can be executed by the interpreter. * * @param context * @param logger */ @A_Frame.Define({ description: 'Method responsible for building the scene, which includes initializing root nodes, loading necessary data, applying transformations, and compiling the scene into a format that can be executed by the interpreter.' }) async build(scope?: A_Scope) { const context = scope?.resolve(AreContext) || A_Context.scope(this).resolve(AreContext); context?.startPerformance('Build Total'); await this.call(AreEngineFeatures.Build, scope || A_Context.scope(this)); context?.endPerformance('Build Total'); } /** * Method responsible for executing the rendering process, which involves mounting the root nodes to the DOM and starting the reactive update cycle based on signals and state changes. * * @param context * @param logger */ @A_Frame.Define({ description: 'Method responsible for executing the rendering process, which involves mounting the root nodes to the DOM and starting the reactive update cycle based on signals and state changes.' }) async execute(scope?: A_Scope) { const context = scope?.resolve(AreContext) || A_Context.scope(this).resolve(AreContext); context?.startPerformance('Execute Total'); await this.call(AreEngineFeatures.Execute, scope || A_Context.scope(this)); context?.endPerformance('Execute Total'); context?.endPerformance('Total'); } // ========================================================================================== // ----------------------------ARE Engine Default Methods------------------------------------ // ========================================================================================== @A_Feature.Extend({ name: AreEngineFeatures.Build, before: /.*/ }) protected async defaultBuild( @A_Dependency.Required() @A_Inject(AreContext) context: AreContext, @A_Inject(A_Logger) logger?: A_Logger, ) { logger?.debug('cyan', 'Starting to build the scene...'); for (const root of context.roots) { context.startPerformance(`Init root <${root.aseid.id}>`); root.init(); context.endPerformance(`Init root <${root.aseid.id}>`); context.startPerformance(`Load root <${root.aseid.id}>`); await root.load(); context.endPerformance(`Load root <${root.aseid.id}>`); context.startPerformance(`Transform root <${root.aseid.id}>`); root.transform(); context.endPerformance(`Transform root <${root.aseid.id}>`); context.startPerformance(`Compile root <${root.aseid.id}>`); root.compile(); context.endPerformance(`Compile root <${root.aseid.id}>`); context.endPerformance(`Root <${root.aseid.id}> Total`); } } @A_Feature.Extend({ name: AreEngineFeatures.Execute, before: /.*/ }) protected async defaultExecute( @A_Dependency.Required() @A_Inject(AreContext) context: AreContext, @A_Inject(A_SignalBus) bus?: A_SignalBus, @A_Inject(A_Logger) logger?: A_Logger, ) { logger?.debug('cyan', 'Starting to execute the scene and mount root nodes...'); for (const root of context.roots) { // Skip roots that are not backed by a real Are component. Stray // top-level template nodes (whitespace text, HTML comments, a //