UNPKG

dioma

Version:

Elegant dependency injection container for vanilla JavaScript and TypeScript

1 lines 13.2 kB
{"version":3,"file":"dioma.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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"}