diff --git a/core/index.ts b/core/index.ts index ef5e471..fa997ad 100644 --- a/core/index.ts +++ b/core/index.ts @@ -1,7 +1,7 @@ // Core types and classes export { Core } from "./core"; export type { Type, ComponentType, DirectiveType } from "./module/type"; -export { Injector, LocalProvider } from "./module/injector"; +export { Injector } from "./module/injector"; // Component system export { IComponent, ViewEncapsulation } from "./module/component"; @@ -29,6 +29,6 @@ export { signal, computed, effect } from "./angular/signals"; export type { Signal, WritableSignal, EffectRef, CreateSignalOptions, CreateEffectOptions } from "./angular/signals"; // types -export type { ApplicationConfig, EnvironmentProviders, PluginConfig, PluginRoutingMode } from "./angular/app-config"; +export type { ApplicationConfig, EnvironmentProviders, PluginConfig, PluginRoutingMode, Provider } from "./angular/app-config"; export { ComponentUtils } from "./utils/component-utils"; export { TemplateFragment } from "./module/template-renderer"; \ No newline at end of file diff --git a/core/module/directive-runner.ts b/core/module/directive-runner.ts index 4d9b54a..a697ca8 100644 --- a/core/module/directive-runner.ts +++ b/core/module/directive-runner.ts @@ -2,12 +2,12 @@ import { DirectiveType, DirectiveRegistry, Injector, - LocalProvider, IDirective, effect, EffectRef, WritableSignal, } from '../index'; +import { Provider } from '../angular/app-config'; import { ActivatedRoute } from '../../router/angular/types'; import { WebComponent } from './web-component'; @@ -60,7 +60,7 @@ export class DirectiveRunner { element: HTMLElement, ): DirectiveInstance | null { const injector = Injector.get(); - const localProviders: LocalProvider[] = [ + const localProviders: Provider[] = [ { provide: HTMLElement, useValue: element }, ]; diff --git a/core/module/injector.ts b/core/module/injector.ts index 50fa44a..8765642 100644 --- a/core/module/injector.ts +++ b/core/module/injector.ts @@ -1,9 +1,6 @@ import { Type } from "../index"; +import { Provider } from "../angular/app-config"; -export interface LocalProvider { - provide: Type | any; - useValue: any; -} export class Injector { private static instance: Injector; @@ -28,76 +25,56 @@ export class Injector { } public createInstance(classType: Type): T { - return this.createInstanceWithProviders(classType, {}); + return this.createInstanceWithProviders(classType, []); } - public createInstanceWithProvidersOld(classType: Type, localProviders: Record): T { - if (!classType) { - throw new Error(`[DI] createInstance called with undefined classType`); - } + private findProvider(token: any, providers: Provider[]): Provider | undefined { + const tokenName = typeof token === 'string' ? token : (token as any).__quarc_original_name__ || token.name; - const key = (classType as any).__quarc_original_name__ || classType.name; - // Prevent instantiation of built-in classes - if (key === "HTMLElement") { - throw new Error(`[DI] Cannot create instance of HTMLElement`); - } - - // First check local cache - if (this.instanceCache[key]) { - return this.instanceCache[key]; - } - - // Then check shared instances (cross-build sharing) - if (this.sharedInstances[key]) { - const sharedInstance = this.sharedInstances[key]; - return sharedInstance; - } - - try { - const dependencies = this.resolveDependencies(classType); - const instance = new classType(...dependencies); - this.instanceCache[key] = instance; - this.sharedInstances[key] = instance; - return instance; - } catch (error) { - const className = this.getReadableClassName(classType); - const dependencyInfo = this.getDependencyInfo(classType); - throw new Error(`[DI] Failed to create instance of "${className}": ${(error as Error).message}\nDependencies: ${dependencyInfo}`); - } + return providers.find(p => { + const providerName = typeof p.provide === 'string' + ? p.provide + : (p.provide as any).__quarc_original_name__ || p.provide.name; + return providerName === tokenName; + }); } - private convertLocalProvidersToRecord(localProviders: LocalProvider[]): Record { - const record: Record = {}; - - for (const provider of localProviders) { - const key = typeof provider.provide === 'string' - ? provider.provide - : (provider.provide as any).__quarc_original_name__ || provider.provide.name; - - record[key] = provider.useValue; + private resolveProviderValue(provider: Provider, providers: Provider[]): any { + if ('useValue' in provider) { + return provider.useValue; + } else if ('useFactory' in provider && provider.useFactory) { + return provider.useFactory(); + } else if ('useExisting' in provider && provider.useExisting) { + const existingToken = provider.useExisting; + const existingProvider = this.findProvider(existingToken, providers); + if (existingProvider) { + return this.resolveProviderValue(existingProvider, providers); + } + const existingKey = typeof existingToken === 'string' + ? existingToken + : (existingToken as any).__quarc_original_name__ || existingToken.name; + return this.sharedInstances[existingKey] || this.instanceCache[existingKey]; + } else if ('useClass' in provider && provider.useClass) { + return this.createInstanceWithProviders(provider.useClass, providers); } - - return record; + return undefined; } - public createInstanceWithProviders(classType: Type, localProviders: Record): T; - public createInstanceWithProviders(classType: Type, localProviders: LocalProvider[]): T; - public createInstanceWithProviders(classType: Type, localProviders: Record | LocalProvider[]): T { + public createInstanceWithProviders(classType: Type, providers: Provider[]): T { if (!classType) { throw new Error(`[DI] createInstanceWithProviders called with undefined classType`); } - // Convert LocalProvider[] to Record if needed - const providersRecord = Array.isArray(localProviders) - ? this.convertLocalProvidersToRecord(localProviders) - : localProviders; - try { - const dependencies = this.resolveDependenciesWithProviders(classType, providersRecord); + console.log({ + className: (classType as any).__quarc_original_name__ || classType.name, + classType, + }); + const dependencies = this.resolveDependenciesWithProviders(classType, providers); /** / console.log({ className: (classType as any).__quarc_original_name__ || classType.name, - localProviders: providersRecord, + providers, dependencies, classType, }); @@ -189,50 +166,31 @@ export class Injector { }); } - private resolveDependenciesWithProviders(classType: Type, localProviders: Record): any[] { + private resolveDependenciesWithProviders(classType: Type, providers: Provider[]): any[] { const tokens = this.getConstructorParameterTypes(classType); - const contextProviders: Record = { - ...this.sharedInstances, - ...this.instanceCache, - ...localProviders, - }; - return tokens.map(token => { - const dep = this.resolveDependency(token, contextProviders, localProviders); - const depName = dep.__quarc_original_name__ || dep.name; - return dep; + return this.resolveDependency(token, providers); }); } - private resolveDependency(token: any, contextProviders: Record, localProviders: Record): any { - const tokenName = typeof token === 'string' ? token : (token as any).__quarc_original_name__ || token.name; + private resolveDependency(token: any, providers: Provider[]): any { + const tokenName = typeof token === 'string' ? token : (token as any).__quarc_original_name__ || token.name; - // First check local providers (they have highest priority) - if (localProviders[tokenName]) { - const providerValue = localProviders[tokenName]; + const provider = this.findProvider(token, providers); + if (provider) { + return this.resolveProviderValue(provider, providers); + } - // If the provider value is a constructor (type), create a new instance - if (typeof providerValue === 'function' && providerValue.prototype && providerValue.prototype.constructor === providerValue) { - return this.createInstanceWithProviders(providerValue, localProviders); - } + if (this.sharedInstances[tokenName]) { + return this.sharedInstances[tokenName]; + } - return providerValue; - } + if (this.instanceCache[tokenName]) { + return this.instanceCache[tokenName]; + } - // Then check other context providers - if (contextProviders[tokenName]) { - const providerValue = contextProviders[tokenName]; - - // If the provider value is a constructor (type), create a new instance - if (typeof providerValue === 'function' && providerValue.prototype && providerValue.prototype.constructor === providerValue) { - return this.createInstanceWithProviders(providerValue, localProviders); - } - - return providerValue; - } - - return this.createInstanceWithProviders(token, localProviders); + return this.createInstanceWithProviders(token, providers); } private getConstructorParameterTypes(classType: Type): any[] { diff --git a/core/module/web-component-factory.ts b/core/module/web-component-factory.ts index 80ece22..4543965 100644 --- a/core/module/web-component-factory.ts +++ b/core/module/web-component-factory.ts @@ -1,4 +1,5 @@ -import { IComponent, WebComponent, Injector, LocalProvider, ComponentType, ComponentUtils, ChangeDetectorRef } from '../index'; +import { IComponent, WebComponent, Injector, ComponentType, ComponentUtils, ChangeDetectorRef } from '../index'; +import { Provider } from '../angular/app-config'; import { ActivatedRoute } from '../../router'; import '../global'; @@ -90,23 +91,24 @@ export class WebComponentFactory { this.getWebComponentInstances().set(webComponentId, webComponent); //const changeDetectorRef = new ChangeDetectorRef(webComponentId); - const localProviders: Record = { - HTMLElement: element, - //ChangeDetectorRef: changeDetectorRef, - ActivatedRoute: this.findActivatedRouteFromElement(element), - }; + const localProviders: Provider[] = [ + { provide: HTMLElement, useValue: element }, + { provide: ActivatedRoute, useValue: this.findActivatedRouteFromElement(element) }, + ]; const componentMeta = componentType._quarcComponent?.[0]; if (componentMeta?.providers) { for (const providerType of componentMeta.providers) { - if (typeof providerType === 'function' && !localProviders[providerType]) { - const providerInstance = injector.createInstanceWithProviders(providerType, localProviders); - const provider = providerType.__quarc_original_name__ || providerType.name || providerType.constructor?.name || providerType; - localProviders[provider] = providerInstance; + if (typeof providerType === 'function') { + const alreadyProvided = localProviders.some(p => p.provide === providerType); + if (!alreadyProvided) { + localProviders.push({ provide: providerType, useClass: providerType }); + } } } } + console.log({localProviders}); return injector.createInstanceWithProviders(componentType, localProviders); } diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..37c84b4 Binary files /dev/null and b/logo.png differ diff --git a/router/directives/router-link.directive.ts b/router/directives/router-link.directive.ts index 779d87f..81044cc 100644 --- a/router/directives/router-link.directive.ts +++ b/router/directives/router-link.directive.ts @@ -1,4 +1,4 @@ -import { Directive, IDirective, input, IComponent, InputSignal } from "../../core"; +import { Directive, IDirective, input } from "../../core"; import { Router } from "../angular/router"; import { ActivatedRoute } from "../angular/types"; @@ -6,7 +6,7 @@ import { ActivatedRoute } from "../angular/types"; selector: '[routerLink]', }) export class RouterLink implements IDirective { - static __quarc_original_name__ = "RouterLink"; + //static __quarc_original_name__ = "RouterLink"; public routerLink = input();