dioma
Version:
Elegant dependency injection container for vanilla JavaScript and TypeScript
1 lines • 13.2 kB
Source Map (JSON)
{"version":3,"file":"dioma.umd.cjs","sources":["../src/errors.ts","../src/scopes.ts","../src/token.ts","../src/container.ts"],"sourcesContent":["export class DependencyCycleError extends Error {\n constructor() {\n super(\"Circular dependency detected\");\n }\n}\n\nexport class AsyncDependencyCycleError extends Error {\n constructor() {\n super(\"Circular dependency detected in async resolution\");\n }\n}\n\nexport class ArgumentsError extends Error {\n constructor(scope: string, className: string) {\n super(`Arguments are not supported for ${scope} of ${className}`);\n }\n}\n\nexport class TokenNotRegisteredError extends Error {\n constructor() {\n super(\"Token is not registered in the container\");\n }\n}\n","import { globalContainer } from \"./container\";\nimport { ArgumentsError } from \"./errors\";\nimport type { ScopeHandler } from \"./types\";\n\nexport class Scopes {\n public static Singleton(): ScopeHandler {\n return function SingletonScope(descriptor, args) {\n if (args.length > 0) {\n throw new ArgumentsError(SingletonScope.name, descriptor.class.name);\n }\n\n return globalContainer.$getInstance(descriptor);\n };\n }\n\n public static Transient(): ScopeHandler {\n return function TransientScope(descriptor, args, container) {\n return container.$getInstance(descriptor, args, false);\n };\n }\n\n public static Container(): ScopeHandler {\n return function ContainerScope(descriptor, args, container) {\n if (args.length > 0) {\n throw new ArgumentsError(ContainerScope.name, descriptor.class.name);\n }\n\n return container.$getInstance(descriptor);\n };\n }\n\n public static Resolution(): ScopeHandler {\n return function ResolutionScope(descriptor, args, _, resolutionContainer) {\n return resolutionContainer.$getInstance(descriptor, args);\n };\n }\n\n public static Scoped = Scopes.Container;\n}\n","export class Token<T> {\n constructor(public readonly name?: string) {}\n}\n","import {\n AsyncDependencyCycleError,\n DependencyCycleError,\n TokenNotRegisteredError,\n} from \"./errors\";\nimport { Scopes } from \"./scopes\";\nimport { Token } from \"./token\";\nimport type {\n AnyDescriptor,\n ArgsOf,\n InstanceOf,\n ScopeHandler,\n ScopedClass,\n TokenClassDescriptor,\n TokenFactoryDescriptor,\n TokenOrClass,\n TokenValueDescriptor,\n} from \"./types\";\n\ntype DescriptorWithContainer = AnyDescriptor & {\n container: Container;\n};\n\nconst MAX_LOOP_COUNT = 100;\n\nexport class Container {\n private instances = new WeakMap();\n\n private resolutionContainer: Container | null = null;\n\n private resolutionSet = new Set<TokenOrClass>();\n\n private pendingPromiseMap = new Map<TokenOrClass, Promise<InstanceType<any>>>();\n\n private tokenDescriptorMap = new Map<TokenOrClass, DescriptorWithContainer>();\n\n private resolutionsLoopCounter = 0;\n\n constructor(private parentContainer: Container | null = null, public name?: string) {\n this.register = this.register.bind(this);\n }\n\n childContainer = (name?: string) => {\n return new Container(this, name);\n };\n\n public $getInstance(\n descriptor: TokenClassDescriptor<any>,\n args: any[] = [],\n cache = true\n ) {\n let instance = null;\n let container: Container | null = this;\n\n const cls = descriptor.class;\n\n while (!instance && container) {\n instance = container.instances.get(cls);\n container = container.parentContainer;\n }\n\n if (!instance) {\n if (descriptor.beforeCreate) {\n descriptor.beforeCreate(this, descriptor, args);\n }\n\n instance = new cls(...args);\n\n if (cache) {\n this.instances.set(cls, instance);\n }\n }\n\n return instance;\n }\n\n private getTokenDescriptor(\n clsOrToken: TokenOrClass\n ): DescriptorWithContainer | undefined {\n let tokenDescriptor;\n let container: Container | null = this;\n\n while (!tokenDescriptor && container) {\n tokenDescriptor = container.tokenDescriptorMap.get(clsOrToken);\n container = container.parentContainer;\n }\n\n return tokenDescriptor;\n }\n\n private injectImpl<T extends TokenOrClass, Args extends ArgsOf<T>>(\n clsOrToken: T,\n args: Args,\n resolutionContainer = this.resolutionContainer\n ): InstanceOf<T> {\n this.resolutionContainer = resolutionContainer || new Container();\n\n try {\n if (this.resolutionSet.has(clsOrToken)) {\n throw new DependencyCycleError();\n }\n\n let cls = clsOrToken as TokenOrClass;\n\n let scope: ScopeHandler;\n\n let container: Container = this;\n\n let descriptor = this.getTokenDescriptor(clsOrToken);\n\n this.resolutionSet.add(clsOrToken);\n\n if (!descriptor) {\n if (clsOrToken instanceof Token) {\n throw new TokenNotRegisteredError();\n }\n\n cls = clsOrToken;\n scope = cls.scope || Scopes.Transient();\n container = this;\n descriptor = { class: cls, container: this };\n } else {\n if (descriptor.beforeInject) {\n descriptor.beforeInject(container, descriptor, args);\n }\n\n if (\"class\" in descriptor) {\n cls = descriptor.class as ScopedClass;\n scope = descriptor.scope || cls.scope || Scopes.Transient();\n container = descriptor.container;\n } else if (\"value\" in descriptor) {\n return descriptor.value;\n } else if (\"factory\" in descriptor) {\n // @ts-ignore\n return descriptor.factory(container, ...args);\n } else {\n throw new Error(\"Invalid descriptor\");\n }\n }\n\n return scope(descriptor, args, container, this.resolutionContainer);\n } finally {\n this.resolutionSet.delete(clsOrToken);\n this.resolutionContainer = resolutionContainer;\n\n if (!resolutionContainer) {\n this.resolutionsLoopCounter = 0;\n }\n }\n }\n\n inject = <T extends TokenOrClass, Args extends ArgsOf<T>>(\n cls: T,\n ...args: Args\n ): InstanceOf<T> => {\n return this.injectImpl(cls, args, undefined);\n };\n\n injectAsync = <T extends TokenOrClass, Args extends ArgsOf<T>>(\n cls: T,\n ...args: Args\n ): Promise<InstanceOf<T>> => {\n const resolutionContainer = this.resolutionContainer;\n\n this.resolutionsLoopCounter += 1;\n\n if (this.resolutionsLoopCounter > MAX_LOOP_COUNT) {\n throw new AsyncDependencyCycleError();\n }\n\n if (this.pendingPromiseMap.has(cls)) {\n return this.pendingPromiseMap.get(cls) as Promise<InstanceOf<T>>;\n }\n\n if (this.instances.has(cls)) {\n return Promise.resolve(this.instances.get(cls));\n }\n\n const promise = Promise.resolve().then(() => {\n try {\n return this.injectImpl(cls, args, resolutionContainer);\n } finally {\n this.pendingPromiseMap.delete(cls);\n }\n });\n\n this.pendingPromiseMap.set(cls, promise);\n\n return promise;\n };\n\n waitAsync = async () => {\n // // The solution doesn't work correctly in all cases\n // // because at the moment of the call not all promises are in the map\n // await Promise.all(this.pendingPromiseMap.values());\n return new Promise<void>((resolve) => setTimeout(resolve, 0));\n };\n\n register<T extends Token<any>>(descriptor: TokenValueDescriptor<T>): void;\n\n register<T extends Token<any>>(descriptor: TokenFactoryDescriptor<T>): void;\n\n register<T extends Token<any>>(descriptor: TokenClassDescriptor<T>): void;\n\n register(tokenDescriptor: any): void {\n const token = tokenDescriptor.token || tokenDescriptor.class;\n\n const descriptorWithContainer = { ...tokenDescriptor, container: this };\n\n this.tokenDescriptorMap.set(token, descriptorWithContainer);\n\n if (tokenDescriptor.class) {\n this.tokenDescriptorMap.set(token.class, descriptorWithContainer);\n }\n }\n\n unregister = (token: TokenOrClass): void => {\n const descriptor = this.getTokenDescriptor(token);\n\n this.tokenDescriptorMap.delete(token);\n this.instances.delete(token);\n\n if (descriptor && \"class\" in descriptor) {\n this.tokenDescriptorMap.delete(descriptor.class as ScopedClass);\n this.instances.delete(descriptor.class);\n }\n };\n\n reset = (): void => {\n this.instances = new WeakMap();\n this.resolutionSet.clear();\n this.pendingPromiseMap.clear();\n this.tokenDescriptorMap.clear();\n this.resolutionContainer = null;\n this.resolutionsLoopCounter = 0;\n };\n}\n\nexport const globalContainer = new Container(null, \"Global container\");\n\nexport const inject = globalContainer.inject;\n\nexport const injectAsync = globalContainer.injectAsync;\n\nexport const childContainer = globalContainer.childContainer;\n"],"names":["name"],"mappings":"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"}