recreate providers
This commit is contained in:
parent
91f8dd5d82
commit
be5961dde9
|
|
@ -1,7 +1,7 @@
|
||||||
// Core types and classes
|
// Core types and classes
|
||||||
export { Core } from "./core";
|
export { Core } from "./core";
|
||||||
export type { Type, ComponentType, DirectiveType } from "./module/type";
|
export type { Type, ComponentType, DirectiveType } from "./module/type";
|
||||||
export { Injector, LocalProvider } from "./module/injector";
|
export { Injector } from "./module/injector";
|
||||||
|
|
||||||
// Component system
|
// Component system
|
||||||
export { IComponent, ViewEncapsulation } from "./module/component";
|
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";
|
export type { Signal, WritableSignal, EffectRef, CreateSignalOptions, CreateEffectOptions } from "./angular/signals";
|
||||||
|
|
||||||
// types
|
// 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 { ComponentUtils } from "./utils/component-utils";
|
||||||
export { TemplateFragment } from "./module/template-renderer";
|
export { TemplateFragment } from "./module/template-renderer";
|
||||||
|
|
@ -2,12 +2,12 @@ import {
|
||||||
DirectiveType,
|
DirectiveType,
|
||||||
DirectiveRegistry,
|
DirectiveRegistry,
|
||||||
Injector,
|
Injector,
|
||||||
LocalProvider,
|
|
||||||
IDirective,
|
IDirective,
|
||||||
effect,
|
effect,
|
||||||
EffectRef,
|
EffectRef,
|
||||||
WritableSignal,
|
WritableSignal,
|
||||||
} from '../index';
|
} from '../index';
|
||||||
|
import { Provider } from '../angular/app-config';
|
||||||
import { ActivatedRoute } from '../../router/angular/types';
|
import { ActivatedRoute } from '../../router/angular/types';
|
||||||
import { WebComponent } from './web-component';
|
import { WebComponent } from './web-component';
|
||||||
|
|
||||||
|
|
@ -60,7 +60,7 @@ export class DirectiveRunner {
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
): DirectiveInstance | null {
|
): DirectiveInstance | null {
|
||||||
const injector = Injector.get();
|
const injector = Injector.get();
|
||||||
const localProviders: LocalProvider[] = [
|
const localProviders: Provider[] = [
|
||||||
{ provide: HTMLElement, useValue: element },
|
{ provide: HTMLElement, useValue: element },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
import { Type } from "../index";
|
import { Type } from "../index";
|
||||||
|
import { Provider } from "../angular/app-config";
|
||||||
|
|
||||||
export interface LocalProvider {
|
|
||||||
provide: Type<any> | any;
|
|
||||||
useValue: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Injector {
|
export class Injector {
|
||||||
private static instance: Injector;
|
private static instance: Injector;
|
||||||
|
|
@ -28,76 +25,56 @@ export class Injector {
|
||||||
}
|
}
|
||||||
|
|
||||||
public createInstance<T>(classType: Type<T>): T {
|
public createInstance<T>(classType: Type<T>): T {
|
||||||
return this.createInstanceWithProviders(classType, {});
|
return this.createInstanceWithProviders(classType, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
public createInstanceWithProvidersOld<T>(classType: Type<T>, localProviders: Record<string, any>): T {
|
private findProvider(token: any, providers: Provider[]): Provider | undefined {
|
||||||
if (!classType) {
|
const tokenName = typeof token === 'string' ? token : (token as any).__quarc_original_name__ || token.name;
|
||||||
throw new Error(`[DI] createInstance called with undefined classType`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const key = (classType as any).__quarc_original_name__ || classType.name;
|
return providers.find(p => {
|
||||||
// Prevent instantiation of built-in classes
|
const providerName = typeof p.provide === 'string'
|
||||||
if (key === "HTMLElement") {
|
? p.provide
|
||||||
throw new Error(`[DI] Cannot create instance of HTMLElement`);
|
: (p.provide as any).__quarc_original_name__ || p.provide.name;
|
||||||
}
|
return providerName === tokenName;
|
||||||
|
});
|
||||||
// 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}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private convertLocalProvidersToRecord(localProviders: LocalProvider[]): Record<string, any> {
|
private resolveProviderValue(provider: Provider, providers: Provider[]): any {
|
||||||
const record: Record<string, any> = {};
|
if ('useValue' in provider) {
|
||||||
|
return provider.useValue;
|
||||||
for (const provider of localProviders) {
|
} else if ('useFactory' in provider && provider.useFactory) {
|
||||||
const key = typeof provider.provide === 'string'
|
return provider.useFactory();
|
||||||
? provider.provide
|
} else if ('useExisting' in provider && provider.useExisting) {
|
||||||
: (provider.provide as any).__quarc_original_name__ || provider.provide.name;
|
const existingToken = provider.useExisting;
|
||||||
|
const existingProvider = this.findProvider(existingToken, providers);
|
||||||
record[key] = provider.useValue;
|
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 undefined;
|
||||||
return record;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public createInstanceWithProviders<T>(classType: Type<T>, localProviders: Record<string, any>): T;
|
public createInstanceWithProviders<T>(classType: Type<T>, providers: Provider[]): T {
|
||||||
public createInstanceWithProviders<T>(classType: Type<T>, localProviders: LocalProvider[]): T;
|
|
||||||
public createInstanceWithProviders<T>(classType: Type<T>, localProviders: Record<string, any> | LocalProvider[]): T {
|
|
||||||
if (!classType) {
|
if (!classType) {
|
||||||
throw new Error(`[DI] createInstanceWithProviders called with undefined classType`);
|
throw new Error(`[DI] createInstanceWithProviders called with undefined classType`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert LocalProvider[] to Record<string, any> if needed
|
|
||||||
const providersRecord = Array.isArray(localProviders)
|
|
||||||
? this.convertLocalProvidersToRecord(localProviders)
|
|
||||||
: localProviders;
|
|
||||||
|
|
||||||
try {
|
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({
|
console.log({
|
||||||
className: (classType as any).__quarc_original_name__ || classType.name,
|
className: (classType as any).__quarc_original_name__ || classType.name,
|
||||||
localProviders: providersRecord,
|
providers,
|
||||||
dependencies,
|
dependencies,
|
||||||
classType,
|
classType,
|
||||||
});
|
});
|
||||||
|
|
@ -189,50 +166,31 @@ export class Injector {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolveDependenciesWithProviders(classType: Type<any>, localProviders: Record<string, any>): any[] {
|
private resolveDependenciesWithProviders(classType: Type<any>, providers: Provider[]): any[] {
|
||||||
const tokens = this.getConstructorParameterTypes(classType);
|
const tokens = this.getConstructorParameterTypes(classType);
|
||||||
|
|
||||||
const contextProviders: Record<string, any> = {
|
|
||||||
...this.sharedInstances,
|
|
||||||
...this.instanceCache,
|
|
||||||
...localProviders,
|
|
||||||
};
|
|
||||||
|
|
||||||
return tokens.map(token => {
|
return tokens.map(token => {
|
||||||
const dep = this.resolveDependency(token, contextProviders, localProviders);
|
return this.resolveDependency(token, providers);
|
||||||
const depName = dep.__quarc_original_name__ || dep.name;
|
|
||||||
return dep;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolveDependency(token: any, contextProviders: Record<string, any>, localProviders: Record<string, any>): any {
|
private resolveDependency(token: any, providers: Provider[]): any {
|
||||||
const tokenName = typeof token === 'string' ? token : (token as any).__quarc_original_name__ || token.name;
|
const tokenName = typeof token === 'string' ? token : (token as any).__quarc_original_name__ || token.name;
|
||||||
|
|
||||||
// First check local providers (they have highest priority)
|
const provider = this.findProvider(token, providers);
|
||||||
if (localProviders[tokenName]) {
|
if (provider) {
|
||||||
const providerValue = localProviders[tokenName];
|
return this.resolveProviderValue(provider, providers);
|
||||||
|
}
|
||||||
|
|
||||||
// If the provider value is a constructor (type), create a new instance
|
if (this.sharedInstances[tokenName]) {
|
||||||
if (typeof providerValue === 'function' && providerValue.prototype && providerValue.prototype.constructor === providerValue) {
|
return this.sharedInstances[tokenName];
|
||||||
return this.createInstanceWithProviders(providerValue, localProviders);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return providerValue;
|
if (this.instanceCache[tokenName]) {
|
||||||
}
|
return this.instanceCache[tokenName];
|
||||||
|
}
|
||||||
|
|
||||||
// Then check other context providers
|
return this.createInstanceWithProviders(token, 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getConstructorParameterTypes(classType: Type<any>): any[] {
|
private getConstructorParameterTypes(classType: Type<any>): any[] {
|
||||||
|
|
|
||||||
|
|
@ -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 { ActivatedRoute } from '../../router';
|
||||||
import '../global';
|
import '../global';
|
||||||
|
|
||||||
|
|
@ -90,23 +91,24 @@ export class WebComponentFactory {
|
||||||
this.getWebComponentInstances().set(webComponentId, webComponent);
|
this.getWebComponentInstances().set(webComponentId, webComponent);
|
||||||
//const changeDetectorRef = new ChangeDetectorRef(webComponentId);
|
//const changeDetectorRef = new ChangeDetectorRef(webComponentId);
|
||||||
|
|
||||||
const localProviders: Record<string, any> = {
|
const localProviders: Provider[] = [
|
||||||
HTMLElement: element,
|
{ provide: HTMLElement, useValue: element },
|
||||||
//ChangeDetectorRef: changeDetectorRef,
|
{ provide: ActivatedRoute, useValue: this.findActivatedRouteFromElement(element) },
|
||||||
ActivatedRoute: this.findActivatedRouteFromElement(element),
|
];
|
||||||
};
|
|
||||||
|
|
||||||
const componentMeta = componentType._quarcComponent?.[0];
|
const componentMeta = componentType._quarcComponent?.[0];
|
||||||
if (componentMeta?.providers) {
|
if (componentMeta?.providers) {
|
||||||
for (const providerType of componentMeta.providers) {
|
for (const providerType of componentMeta.providers) {
|
||||||
if (typeof providerType === 'function' && !localProviders[providerType]) {
|
if (typeof providerType === 'function') {
|
||||||
const providerInstance = injector.createInstanceWithProviders(providerType, localProviders);
|
const alreadyProvided = localProviders.some(p => p.provide === providerType);
|
||||||
const provider = providerType.__quarc_original_name__ || providerType.name || providerType.constructor?.name || providerType;
|
if (!alreadyProvided) {
|
||||||
localProviders[provider] = providerInstance;
|
localProviders.push({ provide: providerType, useClass: providerType });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log({localProviders});
|
||||||
return injector.createInstanceWithProviders<IComponent>(componentType, localProviders);
|
return injector.createInstanceWithProviders<IComponent>(componentType, localProviders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Directive, IDirective, input, IComponent, InputSignal } from "../../core";
|
import { Directive, IDirective, input } from "../../core";
|
||||||
import { Router } from "../angular/router";
|
import { Router } from "../angular/router";
|
||||||
import { ActivatedRoute } from "../angular/types";
|
import { ActivatedRoute } from "../angular/types";
|
||||||
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { ActivatedRoute } from "../angular/types";
|
||||||
selector: '[routerLink]',
|
selector: '[routerLink]',
|
||||||
})
|
})
|
||||||
export class RouterLink implements IDirective {
|
export class RouterLink implements IDirective {
|
||||||
static __quarc_original_name__ = "RouterLink";
|
//static __quarc_original_name__ = "RouterLink";
|
||||||
|
|
||||||
public routerLink = input<string | string[]>();
|
public routerLink = input<string | string[]>();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue