react-obsidian 3.0.0-alpha.6 → 3.0.0-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/Obsidian.d.ts +4 -3
- package/dist/src/Obsidian.d.ts.map +1 -1
- package/dist/src/Obsidian.js +7 -4
- package/dist/src/Obsidian.js.map +1 -1
- package/dist/src/decorators/LifecycleBound.d.ts +1 -1
- package/dist/src/decorators/LifecycleBound.d.ts.map +1 -1
- package/dist/src/decorators/inject/Inject.d.ts.map +1 -1
- package/dist/src/decorators/inject/Inject.js.map +1 -1
- package/dist/src/decorators/inject/Injectable.d.ts +1 -1
- package/dist/src/decorators/inject/Injectable.d.ts.map +1 -1
- package/dist/src/decorators/inject/Injectable.js +2 -2
- package/dist/src/decorators/inject/Injectable.js.map +1 -1
- package/dist/src/graph/ObjectGraph.d.ts +1 -1
- package/dist/src/graph/ObjectGraph.d.ts.map +1 -1
- package/dist/src/graph/ObjectGraph.js +9 -2
- package/dist/src/graph/ObjectGraph.js.map +1 -1
- package/dist/src/graph/ServiceLocatorFactory.d.ts +1 -1
- package/dist/src/graph/ServiceLocatorFactory.d.ts.map +1 -1
- package/dist/src/graph/ServiceLocatorFactory.js +2 -2
- package/dist/src/graph/ServiceLocatorFactory.js.map +1 -1
- package/dist/src/graph/registry/GraphRegistry.d.ts +15 -2
- package/dist/src/graph/registry/GraphRegistry.d.ts.map +1 -1
- package/dist/src/graph/registry/GraphRegistry.js +107 -10
- package/dist/src/graph/registry/GraphRegistry.js.map +1 -1
- package/dist/src/injectors/class/ClassInjector.d.ts +1 -1
- package/dist/src/injectors/class/ClassInjector.d.ts.map +1 -1
- package/dist/src/injectors/class/ClassInjector.js +5 -14
- package/dist/src/injectors/class/ClassInjector.js.map +1 -1
- package/dist/src/injectors/class/LateInjector.d.ts +2 -1
- package/dist/src/injectors/class/LateInjector.d.ts.map +1 -1
- package/dist/src/injectors/class/LateInjector.js +13 -4
- package/dist/src/injectors/class/LateInjector.js.map +1 -1
- package/dist/src/injectors/components/ComponentInjector.d.ts +1 -1
- package/dist/src/injectors/components/ComponentInjector.d.ts.map +1 -1
- package/dist/src/injectors/components/ComponentInjector.js +5 -5
- package/dist/src/injectors/components/ComponentInjector.js.map +1 -1
- package/dist/src/injectors/components/InjectComponent.d.ts +1 -1
- package/dist/src/injectors/components/InjectComponent.d.ts.map +1 -1
- package/dist/src/injectors/components/InjectComponent.js +6 -5
- package/dist/src/injectors/components/InjectComponent.js.map +1 -1
- package/dist/src/injectors/components/useGraph.d.ts +1 -1
- package/dist/src/injectors/components/useGraph.d.ts.map +1 -1
- package/dist/src/injectors/components/useGraph.js +2 -2
- package/dist/src/injectors/components/useGraph.js.map +1 -1
- package/dist/src/injectors/components/useInjectionToken.d.ts +1 -1
- package/dist/src/injectors/components/useInjectionToken.d.ts.map +1 -1
- package/dist/src/injectors/components/useInjectionToken.js +5 -2
- package/dist/src/injectors/components/useInjectionToken.js.map +1 -1
- package/dist/src/injectors/hooks/HookInjector.d.ts +1 -1
- package/dist/src/injectors/hooks/HookInjector.d.ts.map +1 -1
- package/dist/src/injectors/hooks/HookInjector.js +2 -2
- package/dist/src/injectors/hooks/HookInjector.js.map +1 -1
- package/dist/src/injectors/hooks/InjectHook.d.ts +2 -2
- package/dist/src/injectors/hooks/InjectHook.d.ts.map +1 -1
- package/dist/src/injectors/hooks/InjectHook.js +4 -4
- package/dist/src/injectors/hooks/InjectHook.js.map +1 -1
- package/dist/src/utils/isString.d.ts +2 -0
- package/dist/src/utils/isString.d.ts.map +1 -0
- package/dist/src/utils/isString.js +7 -0
- package/dist/src/utils/isString.js.map +1 -0
- package/dist/src/utils/reflect.d.ts +1 -0
- package/dist/src/utils/reflect.d.ts.map +1 -1
- package/dist/src/utils/reflect.js +12 -1
- package/dist/src/utils/reflect.js.map +1 -1
- package/dist/test/fixtures/LifecycleBoundGraph.js +3 -3
- package/dist/test/fixtures/LifecycleBoundGraph.js.map +1 -1
- package/eslint.config.mjs +2 -1
- package/package.json +13 -10
- package/src/Obsidian.ts +10 -6
- package/src/decorators/LifecycleBound.ts +1 -1
- package/src/decorators/inject/Inject.ts +1 -4
- package/src/decorators/inject/Injectable.ts +2 -2
- package/src/graph/ObjectGraph.ts +10 -2
- package/src/graph/ServiceLocatorFactory.ts +2 -2
- package/src/graph/registry/GraphRegistry.ts +119 -10
- package/src/injectors/class/ClassInjector.ts +6 -14
- package/src/injectors/class/LateInjector.ts +10 -3
- package/src/injectors/components/ComponentInjector.tsx +5 -5
- package/src/injectors/components/InjectComponent.ts +6 -5
- package/src/injectors/components/useGraph.ts +2 -2
- package/src/injectors/components/useInjectionToken.ts +5 -2
- package/src/injectors/hooks/HookInjector.ts +2 -2
- package/src/injectors/hooks/InjectHook.ts +4 -4
- package/src/utils/isString.ts +3 -0
- package/src/utils/reflect.ts +14 -1
|
@@ -5,6 +5,8 @@ import GraphMiddlewareChain from './GraphMiddlewareChain';
|
|
|
5
5
|
import { ObtainLifecycleBoundGraphException } from './ObtainLifecycleBoundGraphException';
|
|
6
6
|
import { getMetadata } from '../../utils/reflect';
|
|
7
7
|
import { getGlobal } from '../../utils/getGlobal';
|
|
8
|
+
import { isString } from '../../utils/isString';
|
|
9
|
+
import referenceCounter from '../../ReferenceCounter';
|
|
8
10
|
|
|
9
11
|
export class GraphRegistry {
|
|
10
12
|
private readonly constructorToInstance = new Map<Constructable<Graph>, Set<Graph>>();
|
|
@@ -14,16 +16,30 @@ export class GraphRegistry {
|
|
|
14
16
|
private readonly nameToInstance = new Map<string, Graph>();
|
|
15
17
|
private readonly graphToSubgraphs = new Map<Constructable<Graph>, Set<Constructable<Graph>>>();
|
|
16
18
|
private readonly graphMiddlewares = new GraphMiddlewareChain();
|
|
19
|
+
private readonly keyToGenerator = new Map<string, () => Constructable<Graph>>();
|
|
20
|
+
private readonly keyToGraph = new Map<string, Constructable<Graph>>();
|
|
21
|
+
private readonly onClearListeners = new Map<Graph, Set<() => void>>();
|
|
17
22
|
|
|
18
23
|
register(constructor: Constructable<Graph>, subgraphs: Constructable<Graph>[] = []) {
|
|
19
24
|
this.graphToSubgraphs.set(constructor, new Set(subgraphs));
|
|
20
25
|
}
|
|
21
26
|
|
|
22
|
-
|
|
27
|
+
registerGraphGenerator(key: string, generator: () => Constructable<Graph>) {
|
|
28
|
+
if (this.keyToGenerator.has(key)) throw new Error(`Attempted to register a graph generator for key "${key}" that is already registered.`);
|
|
29
|
+
this.keyToGenerator.set(key, generator);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
ensureRegistered(keyOrGraph: string | Graph) {
|
|
33
|
+
if (isString(keyOrGraph)) return;
|
|
34
|
+
const graph = keyOrGraph;
|
|
23
35
|
if (this.instanceToConstructor.get(graph)) return;
|
|
24
36
|
this.set(graph.constructor as any, graph);
|
|
25
37
|
}
|
|
26
38
|
|
|
39
|
+
public isInstantiated(G: Constructable<Graph>): boolean {
|
|
40
|
+
return (this.constructorToInstance.get(G)?.size ?? 0) > 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
27
43
|
getSubgraphs(graph: Graph): Graph[] {
|
|
28
44
|
const Graph = this.instanceToConstructor.get(graph)!;
|
|
29
45
|
const subgraphs = this.graphToSubgraphs.get(Graph) ?? new Set();
|
|
@@ -35,24 +51,85 @@ export class GraphRegistry {
|
|
|
35
51
|
}
|
|
36
52
|
|
|
37
53
|
resolve<T extends Graph>(
|
|
38
|
-
|
|
54
|
+
keyOrGraph: string | Constructable<T>,
|
|
39
55
|
source: 'lifecycleOwner' | 'classInjection' | 'serviceLocator' = 'lifecycleOwner',
|
|
40
56
|
props: any = undefined,
|
|
41
57
|
injectionToken?: string,
|
|
42
58
|
): T {
|
|
59
|
+
const Graph = isString(keyOrGraph) ?
|
|
60
|
+
this.getGraphConstructorByKey<T>(keyOrGraph) :
|
|
61
|
+
keyOrGraph;
|
|
43
62
|
if ((this.isSingleton(Graph) || this.isBoundToReactLifecycle(Graph)) && this.has(Graph, injectionToken)) {
|
|
44
|
-
return this.isComponentScopedLifecycleBound(Graph)
|
|
45
|
-
|
|
46
|
-
|
|
63
|
+
return this.isComponentScopedLifecycleBound(Graph) ?
|
|
64
|
+
this.getByInjectionToken(Graph, injectionToken) :
|
|
65
|
+
this.getFirst(Graph);
|
|
47
66
|
}
|
|
48
67
|
if (this.isBoundToReactLifecycle(Graph) && source !== 'lifecycleOwner') {
|
|
49
68
|
throw new ObtainLifecycleBoundGraphException(Graph);
|
|
50
69
|
}
|
|
51
70
|
const graph = this.graphMiddlewares.resolve(Graph, props);
|
|
52
71
|
this.set(Graph, graph, injectionToken);
|
|
72
|
+
this.instantiateCustomScopedSubgraphs(graph, props);
|
|
53
73
|
return graph as T;
|
|
54
74
|
}
|
|
55
75
|
|
|
76
|
+
private instantiateCustomScopedSubgraphs(graph: Graph, props: any) {
|
|
77
|
+
this.assertInstantiatingCustomScopedSubgraphFromSameScope(graph);
|
|
78
|
+
if (!this.isCustomScopedLifecycleBound(this.instanceToConstructor.get(graph)!)) return;
|
|
79
|
+
const customScope = getMetadata(this.instanceToConstructor.get(graph)!, 'lifecycleScope');
|
|
80
|
+
const subgraphs = this.getSubgraphsConstructors(graph);
|
|
81
|
+
const sameScopeSubgraphs = subgraphs.filter(
|
|
82
|
+
subgraph => getMetadata(subgraph, 'lifecycleScope') === customScope,
|
|
83
|
+
);
|
|
84
|
+
const instantiatedSubgraphs = sameScopeSubgraphs.map(
|
|
85
|
+
(subgraph) => {
|
|
86
|
+
return this.resolve(subgraph, 'lifecycleOwner', props);
|
|
87
|
+
},
|
|
88
|
+
);
|
|
89
|
+
instantiatedSubgraphs.forEach(subgraph => referenceCounter.retain(subgraph));
|
|
90
|
+
this.registerOnClearListener(graph, () => {
|
|
91
|
+
instantiatedSubgraphs.forEach(subgraph => referenceCounter.release(subgraph, () => this.clear(subgraph)));
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private assertInstantiatingCustomScopedSubgraphFromSameScope(graph: Graph) {
|
|
96
|
+
const graphScope = getMetadata(this.instanceToConstructor.get(graph)!, 'lifecycleScope');
|
|
97
|
+
const subgraphs = this.getSubgraphsConstructors(graph);
|
|
98
|
+
subgraphs.forEach((subgraph) => {
|
|
99
|
+
const subgraphScope = getMetadata(subgraph, 'lifecycleScope');
|
|
100
|
+
if (
|
|
101
|
+
!this.isInstantiated(subgraph)
|
|
102
|
+
&& this.isCustomScopedLifecycleBound(subgraph)
|
|
103
|
+
&& graphScope !== subgraphScope
|
|
104
|
+
) {
|
|
105
|
+
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}`);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private getSubgraphsConstructors(graph: Graph | Constructable<Graph>): Constructable<Graph>[] {
|
|
111
|
+
const Graph = typeof graph === 'function' ? graph : this.instanceToConstructor.get(graph)!;
|
|
112
|
+
const directSubgraphs = Array.from(this.graphToSubgraphs.get(Graph) ?? new Set<Constructable<Graph>>());
|
|
113
|
+
if (directSubgraphs.length === 0) return [];
|
|
114
|
+
return [
|
|
115
|
+
...directSubgraphs,
|
|
116
|
+
...new Set(
|
|
117
|
+
directSubgraphs
|
|
118
|
+
.map(subgraph => this.getSubgraphsConstructors(subgraph))
|
|
119
|
+
.flat(),
|
|
120
|
+
),
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private getGraphConstructorByKey<T extends Graph>(key: string): Constructable<T> {
|
|
125
|
+
if (this.keyToGraph.has(key)) return this.keyToGraph.get(key) as Constructable<T>;
|
|
126
|
+
const generator = this.keyToGenerator.get(key);
|
|
127
|
+
if (!generator) throw new Error(`Attempted to resolve a graph by key "${key}" that is not registered. Did you forget to call Obsidian.registerGraph?`);
|
|
128
|
+
const constructor = generator();
|
|
129
|
+
this.keyToGraph.set(key, constructor);
|
|
130
|
+
return constructor as Constructable<T>;
|
|
131
|
+
}
|
|
132
|
+
|
|
56
133
|
private has(Graph: Constructable<Graph>, injectionToken?: string): boolean {
|
|
57
134
|
const instances = this.constructorToInstance.get(Graph);
|
|
58
135
|
if (!instances) return false;
|
|
@@ -102,6 +179,11 @@ export class GraphRegistry {
|
|
|
102
179
|
return getMetadata(Graph, 'lifecycleScope') === 'component';
|
|
103
180
|
}
|
|
104
181
|
|
|
182
|
+
private isCustomScopedLifecycleBound(Graph: Constructable<Graph>): boolean {
|
|
183
|
+
const scope = getMetadata(Graph, 'lifecycleScope');
|
|
184
|
+
return typeof scope === 'string' && scope !== 'component' && scope !== 'feature';
|
|
185
|
+
}
|
|
186
|
+
|
|
105
187
|
clearGraphAfterItWasMockedInTests(graphName: string) {
|
|
106
188
|
const graphNames = this.nameToInstance.keys();
|
|
107
189
|
for (const name of graphNames) {
|
|
@@ -119,6 +201,7 @@ export class GraphRegistry {
|
|
|
119
201
|
this.injectionTokenToInstance.delete(token);
|
|
120
202
|
this.instanceToInjectionToken.delete(graph);
|
|
121
203
|
}
|
|
204
|
+
this.invokeOnClearListeners(graph);
|
|
122
205
|
}
|
|
123
206
|
}
|
|
124
207
|
}
|
|
@@ -134,6 +217,32 @@ export class GraphRegistry {
|
|
|
134
217
|
this.injectionTokenToInstance.delete(token);
|
|
135
218
|
this.instanceToInjectionToken.delete(graph);
|
|
136
219
|
}
|
|
220
|
+
|
|
221
|
+
this.clearGraphsRegisteredByKey(Graph);
|
|
222
|
+
this.invokeOnClearListeners(graph);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private registerOnClearListener(graph: Graph, callback: () => void) {
|
|
226
|
+
const listeners = this.onClearListeners.get(graph) ?? new Set();
|
|
227
|
+
listeners.add(callback);
|
|
228
|
+
this.onClearListeners.set(graph, listeners);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private invokeOnClearListeners(graph: Graph) {
|
|
232
|
+
const listeners = this.onClearListeners.get(graph);
|
|
233
|
+
if (!listeners) return;
|
|
234
|
+
listeners.forEach(listener => listener());
|
|
235
|
+
this.onClearListeners.delete(graph);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private clearGraphsRegisteredByKey(Graph: Constructable<Graph>) {
|
|
239
|
+
[...this.keyToGraph.keys()]
|
|
240
|
+
.map(key => [key, this.keyToGraph.get(key)!] as [string, Constructable<Graph>])
|
|
241
|
+
.filter(([_, $Graph]) => $Graph === Graph)
|
|
242
|
+
.forEach(([key, _]) => {
|
|
243
|
+
this.keyToGraph.delete(key);
|
|
244
|
+
this.keyToGenerator.delete(key);
|
|
245
|
+
});
|
|
137
246
|
}
|
|
138
247
|
|
|
139
248
|
addGraphMiddleware(middleware: Middleware<Graph>) {
|
|
@@ -150,11 +259,11 @@ export class GraphRegistry {
|
|
|
150
259
|
this.nameToInstance.clear();
|
|
151
260
|
this.injectionTokenToInstance.clear();
|
|
152
261
|
this.instanceToInjectionToken.clear();
|
|
262
|
+
this.keyToGenerator.clear();
|
|
263
|
+
this.keyToGraph.clear();
|
|
153
264
|
}
|
|
154
265
|
}
|
|
155
266
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
// // @ts-expect-error - see above
|
|
160
|
-
export default _global.graphRegistry as GraphRegistry;
|
|
267
|
+
const globalObject = getGlobal();
|
|
268
|
+
globalObject.graphRegistry = globalObject.graphRegistry || new GraphRegistry();
|
|
269
|
+
export default globalObject.graphRegistry as GraphRegistry;
|
|
@@ -12,14 +12,14 @@ export default class ClassInjector {
|
|
|
12
12
|
private injectionMetadata: InjectionMetadata = new InjectionMetadata(),
|
|
13
13
|
) {}
|
|
14
14
|
|
|
15
|
-
inject(
|
|
15
|
+
inject(keyOrGraph: string | Constructable<Graph>) {
|
|
16
16
|
return (Target: Constructable<any>) => {
|
|
17
|
-
return new Proxy(Target, this.createProxyHandler(
|
|
17
|
+
return new Proxy(Target, this.createProxyHandler(keyOrGraph, this.graphRegistry, this.injectionMetadata));
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
private createProxyHandler(
|
|
22
|
-
|
|
22
|
+
keyOrGraph: string | Constructable<Graph>,
|
|
23
23
|
graphRegistry: GraphRegistry,
|
|
24
24
|
injectionMetadata: InjectionMetadata,
|
|
25
25
|
): ProxyHandler<any> {
|
|
@@ -27,14 +27,14 @@ export default class ClassInjector {
|
|
|
27
27
|
construct(target: any, args: any[], newTarget: Function): any {
|
|
28
28
|
const isReactClassComponent = target.prototype?.isReactComponent;
|
|
29
29
|
const source = isReactClassComponent ? 'lifecycleOwner' : 'classInjection';
|
|
30
|
-
const graph = graphRegistry.resolve(
|
|
30
|
+
const graph = graphRegistry.resolve(keyOrGraph, source, args.length > 0 ? args[0] : undefined);
|
|
31
31
|
if (isReactClassComponent) {
|
|
32
32
|
referenceCounter.retain(graph);
|
|
33
33
|
}
|
|
34
34
|
defineMetadata(target, GRAPH_INSTANCE_NAME_KEY, graph.name);
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
graph.onBind(target);
|
|
37
|
-
const createdObject = Reflect.construct(target,
|
|
37
|
+
const createdObject = Reflect.construct(target, args, newTarget);
|
|
38
38
|
this.injectProperties(target, createdObject, graph);
|
|
39
39
|
const originalComponentWillUnmount: () => void | undefined = createdObject.componentWillUnmount;
|
|
40
40
|
createdObject.componentWillUnmount = () => {
|
|
@@ -44,14 +44,6 @@ export default class ClassInjector {
|
|
|
44
44
|
return createdObject;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
private injectConstructorArgs(args: any[], graph: Graph, target: any): any[] {
|
|
48
|
-
const argsToInject = injectionMetadata.getConstructorArgsToInject(target);
|
|
49
|
-
if (!argsToInject.hasArgs()) return args;
|
|
50
|
-
return [...args, ...new Array(Math.abs(args.length - argsToInject.size()))].map((value, idx): any => {
|
|
51
|
-
return value ?? graph.retrieve(argsToInject.getProperty(idx));
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
47
|
private injectProperties(target: any, createdObject: any, graph: Graph) {
|
|
56
48
|
injectionMetadata.getPropertiesToInject(target).forEach((key) => {
|
|
57
49
|
Reflect.set(createdObject, key, graph.retrieve(key));
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isString } from 'lodash';
|
|
1
2
|
import { ObjectGraph } from '../../graph/ObjectGraph';
|
|
2
3
|
import graphRegistry from '../../graph/registry/GraphRegistry';
|
|
3
4
|
import { getMetadata } from '../../utils/reflect';
|
|
@@ -6,16 +7,22 @@ import InjectionMetadata from './InjectionMetadata';
|
|
|
6
7
|
export const GRAPH_INSTANCE_NAME_KEY = 'GRAPH_INSTANCE_NAME';
|
|
7
8
|
|
|
8
9
|
class LateInjector<T extends object> {
|
|
9
|
-
inject(target: T,
|
|
10
|
-
if (
|
|
10
|
+
inject(target: T, keyOrGraph?: string | ObjectGraph): T {
|
|
11
|
+
if (keyOrGraph) graphRegistry.ensureRegistered(keyOrGraph);
|
|
11
12
|
const injectionMetadata = new InjectionMetadata();
|
|
12
|
-
const graph =
|
|
13
|
+
const graph = this.getGraph(target, keyOrGraph);
|
|
13
14
|
injectionMetadata.getLatePropertiesToInject(target.constructor).forEach((key) => {
|
|
14
15
|
Reflect.set(target, key, graph.retrieve(key));
|
|
15
16
|
});
|
|
16
17
|
return target;
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
private getGraph(target: T, keyOrGraph?: string | ObjectGraph) {
|
|
21
|
+
if (keyOrGraph instanceof ObjectGraph) return keyOrGraph;
|
|
22
|
+
if (isString(keyOrGraph)) return graphRegistry.resolve(keyOrGraph, 'classInjection');
|
|
23
|
+
return this.getGraphInstance(target);
|
|
24
|
+
}
|
|
25
|
+
|
|
19
26
|
private getGraphInstance(target: T) {
|
|
20
27
|
const graphInstanceName = getMetadata(target.constructor, GRAPH_INSTANCE_NAME_KEY);
|
|
21
28
|
return graphRegistry.getGraphInstance(graphInstanceName);
|
|
@@ -11,24 +11,24 @@ import { useInjectionToken } from './useInjectionToken';
|
|
|
11
11
|
export default class ComponentInjector {
|
|
12
12
|
inject<P>(
|
|
13
13
|
Target: React.FunctionComponent<P>,
|
|
14
|
-
|
|
14
|
+
keyOrGraph: string | Constructable<ObjectGraph>,
|
|
15
15
|
): React.FunctionComponent<Partial<P>> {
|
|
16
|
-
const Wrapped = this.wrapComponent(Target,
|
|
16
|
+
const Wrapped = this.wrapComponent(Target, keyOrGraph);
|
|
17
17
|
hoistNonReactStatics(Wrapped, Target);
|
|
18
18
|
return Wrapped;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
private wrapComponent<P>(
|
|
22
22
|
InjectionCandidate: React.FunctionComponent<P>,
|
|
23
|
-
|
|
23
|
+
keyOrGraph: string | Constructable<ObjectGraph>,
|
|
24
24
|
): React.FunctionComponent<Partial<P>> {
|
|
25
25
|
const isMemoized = isMemoizedComponent(InjectionCandidate);
|
|
26
26
|
const Target = isMemoized ? InjectionCandidate.type : InjectionCandidate;
|
|
27
27
|
const compare = isMemoized ? InjectionCandidate.compare : undefined;
|
|
28
28
|
|
|
29
29
|
return genericMemo((passedProps: P) => {
|
|
30
|
-
const injectionToken = useInjectionToken(
|
|
31
|
-
const graph = useGraph<P>(
|
|
30
|
+
const injectionToken = useInjectionToken(keyOrGraph);
|
|
31
|
+
const graph = useGraph<P>(keyOrGraph, Target, passedProps, injectionToken);
|
|
32
32
|
const proxiedProps = new PropsInjector(graph).inject(passedProps);
|
|
33
33
|
|
|
34
34
|
return (
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ObjectGraph } from '../../graph/ObjectGraph';
|
|
3
3
|
import { Constructable } from '../../types';
|
|
4
|
+
import { isString } from '../../utils/isString';
|
|
4
5
|
import ComponentInjector from './ComponentInjector';
|
|
5
6
|
|
|
6
7
|
interface Discriminator {
|
|
@@ -14,18 +15,18 @@ export const injectComponent = <OwnProps = Discriminator, InjectedProps = Discri
|
|
|
14
15
|
(OwnProps extends infer P ? OwnProps extends Discriminator ? P : OwnProps : never) &
|
|
15
16
|
(InjectedProps extends Discriminator ? any : InjectedProps)
|
|
16
17
|
>,
|
|
17
|
-
|
|
18
|
+
keyOrGraph: string | Constructable<ObjectGraph>,
|
|
18
19
|
) => {
|
|
19
|
-
assertGraph(
|
|
20
|
+
assertGraph(keyOrGraph, Target);
|
|
20
21
|
|
|
21
|
-
return componentInjector.inject(Target,
|
|
22
|
+
return componentInjector.inject(Target, keyOrGraph) as React.FunctionComponent<
|
|
22
23
|
InjectedProps extends Discriminator ?
|
|
23
24
|
OwnProps extends Discriminator ? Partial<OwnProps> : OwnProps :
|
|
24
25
|
OwnProps extends InjectedProps ? Partial<OwnProps> : OwnProps & Partial<InjectedProps>
|
|
25
26
|
>;
|
|
26
27
|
};
|
|
27
|
-
function assertGraph(
|
|
28
|
-
if (!
|
|
28
|
+
function assertGraph(keyOrGraph: string | Constructable<ObjectGraph>, Target: any) {
|
|
29
|
+
if (!isString(keyOrGraph) && !keyOrGraph) {
|
|
29
30
|
throw new Error(
|
|
30
31
|
`injectComponent was called with an undefined Graph.`
|
|
31
32
|
+ `This is probably not an issue with Obsidian.`
|
|
@@ -5,13 +5,13 @@ import graphRegistry from '../../graph/registry/GraphRegistry';
|
|
|
5
5
|
import referenceCounter from '../../ReferenceCounter';
|
|
6
6
|
|
|
7
7
|
export default <P>(
|
|
8
|
-
|
|
8
|
+
keyOrGraph: string | Constructable<ObjectGraph>,
|
|
9
9
|
target: any,
|
|
10
10
|
props?: Partial<P>,
|
|
11
11
|
injectionToken?: string,
|
|
12
12
|
) => {
|
|
13
13
|
const [graph] = useState(() => {
|
|
14
|
-
const resolvedGraph = graphRegistry.resolve(
|
|
14
|
+
const resolvedGraph = graphRegistry.resolve(keyOrGraph, 'lifecycleOwner', props, injectionToken);
|
|
15
15
|
resolvedGraph.onBind(target);
|
|
16
16
|
return resolvedGraph;
|
|
17
17
|
});
|
|
@@ -2,9 +2,12 @@ import { useContext, useState } from 'react';
|
|
|
2
2
|
import { GraphContext } from './graphContext';
|
|
3
3
|
import type { Constructable, ObjectGraph } from '../..';
|
|
4
4
|
import { uniqueId } from '../../utils/uniqueId';
|
|
5
|
+
import { isString } from '../../utils/isString';
|
|
5
6
|
|
|
6
|
-
export const useInjectionToken = (
|
|
7
|
+
export const useInjectionToken = (keyOrGraph: string | Constructable<ObjectGraph>) => {
|
|
7
8
|
const ctx = useContext(GraphContext);
|
|
8
|
-
const [injectionToken] = useState(() =>
|
|
9
|
+
const [injectionToken] = useState(() => {
|
|
10
|
+
return ctx?.injectionToken ?? uniqueId(isString(keyOrGraph) ? keyOrGraph : keyOrGraph.name);
|
|
11
|
+
});
|
|
9
12
|
return injectionToken;
|
|
10
13
|
};
|
|
@@ -6,10 +6,10 @@ import { Constructable } from '../../types';
|
|
|
6
6
|
export default class HookInjector {
|
|
7
7
|
inject<Args, Result>(
|
|
8
8
|
hook: (args: Args) => Result,
|
|
9
|
-
|
|
9
|
+
keyOrGraph: string | Constructable<ObjectGraph>,
|
|
10
10
|
): (args?: Partial<Args>) => Result {
|
|
11
11
|
return (args?: Partial<Args>): Result => {
|
|
12
|
-
const graph = useGraph(
|
|
12
|
+
const graph = useGraph(keyOrGraph, hook, args);
|
|
13
13
|
return hook(new Proxy(args ?? {}, new Injector(graph)));
|
|
14
14
|
};
|
|
15
15
|
}
|
|
@@ -11,14 +11,14 @@ const hookInjector = new HookInjector();
|
|
|
11
11
|
|
|
12
12
|
export function injectHookWithArguments<Injected, Own, Result = {}>(
|
|
13
13
|
hook: (args: Injected & Own) => Result,
|
|
14
|
-
|
|
14
|
+
keyOrGraph: string | Constructable<ObjectGraph>,
|
|
15
15
|
): (props: Own & Partial<Injected>) => Result {
|
|
16
|
-
return hookInjector.inject(hook,
|
|
16
|
+
return hookInjector.inject(hook, keyOrGraph) as (props: Own & Partial<Injected>) => Result;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export function injectHook<Injected, Result = {}>(
|
|
20
20
|
hook: (args: Injected) => Result,
|
|
21
|
-
|
|
21
|
+
keyOrGraph: string | Constructable<ObjectGraph>,
|
|
22
22
|
): (props?: Partial<Injected>) => Result {
|
|
23
|
-
return hookInjector.inject(hook,
|
|
23
|
+
return hookInjector.inject(hook, keyOrGraph);
|
|
24
24
|
}
|
package/src/utils/reflect.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import { getGlobal } from './getGlobal';
|
|
2
|
+
|
|
3
|
+
function getStore(): WeakMap<any, any> {
|
|
4
|
+
const global = getGlobal();
|
|
5
|
+
global.__metadataStore = global.__metadataStore || new WeakMap();
|
|
6
|
+
return global.__metadataStore;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const metadataStore = getStore();
|
|
2
10
|
|
|
3
11
|
export function defineMetadata(target: any, key: string, value: any) {
|
|
4
12
|
let metadata = metadataStore.get(target);
|
|
@@ -15,3 +23,8 @@ export function getMetadata(target: any, key: string) {
|
|
|
15
23
|
const metadata = metadataStore.get(target);
|
|
16
24
|
return metadata ? metadata[key] : undefined;
|
|
17
25
|
}
|
|
26
|
+
|
|
27
|
+
export function hasMetadata(target: any, key: string) {
|
|
28
|
+
const metadata = metadataStore.get(target);
|
|
29
|
+
return metadata ? metadata[key] !== undefined : false;
|
|
30
|
+
}
|