UNPKG

@joist/di

Version:

Dependency Injection for Vanilla JS classes

78 lines 2.88 kB
import { callLifecycle } from "./lifecycle.js"; import { readInjector, readMetadata } from "./metadata.js"; import { StaticToken, } from "./provider.js"; export const INJECTOR = Symbol("JOIST_INJECTOR"); export class ProviderMap extends Map { } export class Injector { #instances = new WeakMap(); name; parent; providers; constructor(opts) { this.parent = opts?.parent; this.providers = new ProviderMap(opts?.providers); } injectAll(token, opts, collection = []) { collection.push(this.inject(token, { ignoreParent: true, singleton: opts?.singleton })); if (this.parent) { return this.parent.injectAll(token, opts, collection); } return collection; } inject(token, opts) { if (opts?.singleton !== false && this.#instances.has(token)) { const instance = this.#instances.get(token); const metadata = readMetadata(token); const injector = readInjector(instance); if (metadata) { callLifecycle(instance, injector ?? this, metadata.onInjected); } return instance; } const provider = this.providers.get(token); const createOpts = { singleton: opts?.singleton !== false }; if (provider) { if ("use" in provider) { return this.#createAndCache(token, () => new provider.use(), createOpts); } if ("factory" in provider) { return this.#createAndCache(token, provider.factory, createOpts); } throw new Error(`Provider for ${token.name} found but is missing either 'use' or 'factory'`); } if (this.parent && opts?.ignoreParent !== true) { return this.parent.inject(token); } if (token instanceof StaticToken) { if (!token.factory) { throw new Error(`Provider not found for "${token.name}"`); } return this.#createAndCache(token, token.factory, createOpts); } return this.#createAndCache(token, () => new token(), createOpts); } clear() { this.#instances = new WeakMap(); } #createAndCache(token, factory, opts) { const instance = factory(this); if (opts.singleton !== false) { this.#instances.set(token, instance); } const injector = readInjector(instance); if (!injector) { return instance; } if (injector !== this) { injector.parent = this; } const metadata = readMetadata(token); if (metadata) { callLifecycle(instance ?? this, injector, metadata.onCreated); callLifecycle(instance ?? this, injector, metadata.onInjected); } return instance; } } //# sourceMappingURL=injector.js.map