UNPKG

react-obsidian

Version:

Dependency injection framework for React and React Native applications

250 lines 11.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.GraphRegistry = void 0; const GraphMiddlewareChain_1 = __importDefault(require("./GraphMiddlewareChain")); const ObtainLifecycleBoundGraphException_1 = require("./ObtainLifecycleBoundGraphException"); const getGlobal_1 = require("../../utils/getGlobal"); const isString_1 = require("../../utils/isString"); const ReferenceCounter_1 = __importDefault(require("../../ReferenceCounter")); const reflect_1 = require("../../utils/reflect"); class GraphRegistry { constructor() { this.constructorToInstance = new Map(); this.instanceToConstructor = new Map(); this.injectionTokenToInstance = new Map(); this.instanceToInjectionToken = new Map(); this.nameToInstance = new Map(); this.graphToSubgraphs = new Map(); this.graphMiddlewares = new GraphMiddlewareChain_1.default(); this.keyToGenerator = new Map(); this.keyToGraph = new Map(); this.onClearListeners = new Map(); } register(constructor, subgraphs = []) { this.graphToSubgraphs.set(constructor, new Set(subgraphs)); } registerGraphGenerator(key, generator) { if (this.keyToGenerator.has(key)) throw new Error(`Attempted to register a graph generator for key "${key}" that is already registered.`); this.keyToGenerator.set(key, generator); } ensureRegistered(keyOrGraph) { if ((0, isString_1.isString)(keyOrGraph)) return; const graph = keyOrGraph; if (this.instanceToConstructor.get(graph)) return; this.set(graph.constructor, graph); } isInstantiated(G) { var _a, _b; return ((_b = (_a = this.constructorToInstance.get(G)) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : 0) > 0; } getSubgraphs(graph) { var _a; const Graph = this.instanceToConstructor.get(graph); const subgraphs = (_a = this.graphToSubgraphs.get(Graph)) !== null && _a !== void 0 ? _a : new Set(); return Array.from(subgraphs).map((G) => this.resolve(G)); } getGraphInstance(name) { return this.nameToInstance.get(name); } resolve(keyOrGraph, source = 'lifecycleOwner', props = undefined, injectionToken) { const Graph = (0, isString_1.isString)(keyOrGraph) ? this.getGraphConstructorByKey(keyOrGraph) : keyOrGraph; if ((this.isSingleton(Graph) || this.isBoundToReactLifecycle(Graph)) && this.has(Graph, injectionToken)) { return this.isComponentScopedLifecycleBound(Graph) ? this.getByInjectionToken(Graph, injectionToken) : this.getFirst(Graph); } if (this.isBoundToReactLifecycle(Graph) && source !== 'lifecycleOwner') { throw new ObtainLifecycleBoundGraphException_1.ObtainLifecycleBoundGraphException(Graph); } const graph = this.graphMiddlewares.resolve(Graph, props); this.set(Graph, graph, injectionToken); this.instantiateCustomScopedSubgraphs(graph, props); return graph; } instantiateCustomScopedSubgraphs(graph, props) { this.assertInstantiatingCustomScopedSubgraphFromSameScope(graph); if (!this.isCustomScopedLifecycleBound(this.instanceToConstructor.get(graph))) return; const customScope = reflect_1.Reflect.getMetadata('lifecycleScope', this.instanceToConstructor.get(graph)); const subgraphs = this.getSubgraphsConstructors(graph); const sameScopeSubgraphs = subgraphs.filter(subgraph => reflect_1.Reflect.getMetadata('lifecycleScope', subgraph) === customScope); const instantiatedSubgraphs = sameScopeSubgraphs.map(subgraph => { return this.resolve(subgraph, 'lifecycleOwner', props); }); instantiatedSubgraphs.forEach((subgraph) => ReferenceCounter_1.default.retain(subgraph)); this.registerOnClearListener(graph, () => { instantiatedSubgraphs.forEach((subgraph) => ReferenceCounter_1.default.release(subgraph, () => this.clear(subgraph))); }); } assertInstantiatingCustomScopedSubgraphFromSameScope(graph) { const graphScope = reflect_1.Reflect.getMetadata('lifecycleScope', this.instanceToConstructor.get(graph)); const subgraphs = this.getSubgraphsConstructors(graph); subgraphs.forEach(subgraph => { const subgraphScope = reflect_1.Reflect.getMetadata('lifecycleScope', subgraph); if (!this.isInstantiated(subgraph) && this.isCustomScopedLifecycleBound(subgraph) && graphScope !== subgraphScope) { throw new Error(`Cannot instantiate the scoped graph '${subgraph.name}' as a subgraph of '${graph.constructor.name}' because the scopes do not match. ${graphScope} !== ${subgraphScope}`); } }); } getSubgraphsConstructors(graph) { var _a; const Graph = typeof graph === 'function' ? graph : this.instanceToConstructor.get(graph); const directSubgraphs = Array.from((_a = this.graphToSubgraphs.get(Graph)) !== null && _a !== void 0 ? _a : new Set()); if (directSubgraphs.length === 0) return []; return [ ...directSubgraphs, ...new Set(directSubgraphs .map(subgraph => this.getSubgraphsConstructors(subgraph)) .flat()), ]; } getGraphConstructorByKey(key) { if (this.keyToGraph.has(key)) return this.keyToGraph.get(key); const generator = this.keyToGenerator.get(key); if (!generator) throw new Error(`Attempted to resolve a graph by key "${key}" that is not registered. Did you forget to call Obsidian.registerGraph?`); const constructor = generator(); this.keyToGraph.set(key, constructor); return constructor; } has(Graph, injectionToken) { var _a, _b; const instances = this.constructorToInstance.get(Graph); if (!instances) return false; if (this.isComponentScopedLifecycleBound(Graph)) { return Array .from(instances) .some((graph) => this.instanceToInjectionToken.get(graph) === injectionToken); } return ((_b = (_a = this.constructorToInstance.get(Graph)) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : 0) > 0; } getFirst(Graph) { return this.constructorToInstance.get(Graph).values().next().value; } getByInjectionToken(Graph, injectionToken) { return Array .from(this.constructorToInstance.get(Graph)) .find((graph) => { return this.instanceToInjectionToken.get(graph) === injectionToken; }); } set(Graph, graph, injectionToken) { var _a; const graphs = (_a = this.constructorToInstance.get(Graph)) !== null && _a !== void 0 ? _a : new Set(); if (injectionToken && this.isComponentScopedLifecycleBound(Graph)) { this.injectionTokenToInstance.set(injectionToken, graph); this.instanceToInjectionToken.set(graph, injectionToken); } graphs.add(graph); this.constructorToInstance.set(Graph, graphs); this.instanceToConstructor.set(graph, Graph); this.nameToInstance.set(graph.name, graph); } isSingleton(Graph) { var _a; return (_a = reflect_1.Reflect.getMetadata('isSingleton', Graph)) !== null && _a !== void 0 ? _a : false; } isBoundToReactLifecycle(Graph) { var _a; return (_a = reflect_1.Reflect.getMetadata('isLifecycleBound', Graph)) !== null && _a !== void 0 ? _a : false; } isComponentScopedLifecycleBound(Graph) { return reflect_1.Reflect.getMetadata('lifecycleScope', Graph) === 'component'; } isCustomScopedLifecycleBound(Graph) { const scope = reflect_1.Reflect.getMetadata('lifecycleScope', Graph); return typeof scope === 'string' && scope !== 'component' && scope !== 'feature'; } clearGraphAfterItWasMockedInTests(graphName) { const graphNames = this.nameToInstance.keys(); for (const name of graphNames) { if (name.match(graphName)) { const graph = this.nameToInstance.get(name); if (!graph) return; const Graph = this.instanceToConstructor.get(graph); if (!Graph) return; this.instanceToConstructor.delete(graph); this.constructorToInstance.get(Graph).delete(graph); this.nameToInstance.delete(graph.name); const token = this.instanceToInjectionToken.get(graph); if (token) { this.injectionTokenToInstance.delete(token); this.instanceToInjectionToken.delete(graph); } this.invokeOnClearListeners(graph); } } } clear(graph) { const Graph = this.instanceToConstructor.get(graph); if (!Graph || this.isSingleton(Graph)) return; this.instanceToConstructor.delete(graph); this.constructorToInstance.get(Graph).delete(graph); this.nameToInstance.delete(graph.name); const token = this.instanceToInjectionToken.get(graph); if (token) { this.injectionTokenToInstance.delete(token); this.instanceToInjectionToken.delete(graph); } this.clearGraphsRegisteredByKey(Graph); this.invokeOnClearListeners(graph); } registerOnClearListener(graph, callback) { var _a; const listeners = (_a = this.onClearListeners.get(graph)) !== null && _a !== void 0 ? _a : new Set(); listeners.add(callback); this.onClearListeners.set(graph, listeners); } invokeOnClearListeners(graph) { const listeners = this.onClearListeners.get(graph); if (!listeners) return; listeners.forEach((listener) => listener()); this.onClearListeners.delete(graph); } clearGraphsRegisteredByKey(Graph) { [...this.keyToGraph.keys()] .map((key) => [key, this.keyToGraph.get(key)]) .filter(([_, $Graph]) => $Graph === Graph) .forEach(([key, _]) => { this.keyToGraph.delete(key); this.keyToGenerator.delete(key); }); } addGraphMiddleware(middleware) { this.graphMiddlewares.add(middleware); } clearGraphMiddlewares() { this.graphMiddlewares.clear(); } clearAll() { this.instanceToConstructor.clear(); this.constructorToInstance.clear(); this.nameToInstance.clear(); this.injectionTokenToInstance.clear(); this.instanceToInjectionToken.clear(); this.keyToGenerator.clear(); this.keyToGraph.clear(); } } exports.GraphRegistry = GraphRegistry; const globalObject = (0, getGlobal_1.getGlobal)(); globalObject.graphRegistry = globalObject.graphRegistry || new GraphRegistry(); exports.default = globalObject.graphRegistry; //# sourceMappingURL=GraphRegistry.js.map