@servicetitan/react-ioc 22.20.0 → 23.0.0
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/await.d.ts.map +1 -1
- package/dist/await.js +2 -1
- package/dist/await.js.map +1 -1
- package/dist/fallbacks.d.ts +4 -0
- package/dist/fallbacks.d.ts.map +1 -0
- package/dist/fallbacks.js +4 -0
- package/dist/fallbacks.js.map +1 -0
- package/dist/provide.d.ts +10 -9
- package/dist/provide.d.ts.map +1 -1
- package/dist/provide.js +1 -1
- package/dist/provide.js.map +1 -1
- package/dist/provider.d.ts +17 -6
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +92 -42
- package/dist/provider.js.map +1 -1
- package/dist/store.d.ts +2 -2
- package/dist/store.d.ts.map +1 -1
- package/package.json +4 -2
- package/src/__tests__/await.test.tsx +21 -0
- package/src/__tests__/provide.test.tsx +343 -0
- package/src/await.tsx +6 -1
- package/src/fallbacks.tsx +2 -0
- package/src/provide.tsx +17 -12
- package/src/provider.tsx +171 -68
- package/src/store.ts +2 -2
package/dist/await.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"await.d.ts","sourceRoot":"","sources":["../src/await.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAsB,SAAS,EAAqB,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"await.d.ts","sourceRoot":"","sources":["../src/await.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAsB,SAAS,EAAqB,MAAM,OAAO,CAAC;AAG7E,MAAM,WAAW,UAAU;IACvB,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;CAC5C;AAED,eAAO,MAAM,KAAK,EAAE,EAAE,CAAC,UAAU,CAsBhC,CAAC"}
|
package/dist/await.js
CHANGED
|
@@ -9,7 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
11
11
|
import { lazy, Suspense, useMemo } from 'react';
|
|
12
|
-
|
|
12
|
+
import { defaultLoadingFallback } from './fallbacks';
|
|
13
|
+
export const Await = ({ promises, fallback = defaultLoadingFallback, children, }) => {
|
|
13
14
|
const Lazy = useMemo(() => lazy(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
14
15
|
yield Promise.all(promises);
|
|
15
16
|
return {
|
package/dist/await.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"await.js","sourceRoot":"","sources":["../src/await.tsx"],"names":[],"mappings":";;;;;;;;;;AAAA,OAAO,EAAM,IAAI,EAA2B,QAAQ,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"await.js","sourceRoot":"","sources":["../src/await.tsx"],"names":[],"mappings":";;;;;;;;;;AAAA,OAAO,EAAM,IAAI,EAA2B,QAAQ,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAC7E,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAOrD,MAAM,CAAC,MAAM,KAAK,GAAmB,CAAC,EAClC,QAAQ,EACR,QAAQ,GAAG,sBAAsB,EACjC,QAAQ,GACX,EAAE,EAAE;IACD,MAAM,IAAI,GAAG,OAAO,CAChB,GAAG,EAAE,CACD,IAAI,CAAK,GAAS,EAAE;QAChB,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAE5B,OAAO;YACH,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,QAAwB;SACtD,CAAC;IACN,CAAC,CAAA,CAAC,EACN,CAAC,QAAQ,CAAC,CACb,CAAC;IAEF,OAAO,CACH,KAAC,QAAQ,kBAAC,QAAQ,EAAE,QAAQ,gBACxB,KAAC,IAAI,cAAE,QAAQ,GAAQ,IAChB,CACd,CAAC;AACN,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallbacks.d.ts","sourceRoot":"","sources":["../src/fallbacks.tsx"],"names":[],"mappings":";AAAA,eAAO,MAAM,sBAAsB,aAAsB,CAAC;AAC1D,eAAO,MAAM,oBAAoB,aAAuC,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
export const defaultLoadingFallback = _jsx("h2", { children: "Loading..." });
|
|
3
|
+
export const defaultErrorFallback = _jsx("h2", Object.assign({ className: "c-red-500" }, { children: "Error" }));
|
|
4
|
+
//# sourceMappingURL=fallbacks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallbacks.js","sourceRoot":"","sources":["../src/fallbacks.tsx"],"names":[],"mappings":";AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG,sCAAmB,CAAC;AAC1D,MAAM,CAAC,MAAM,oBAAoB,GAAG,2BAAI,SAAS,EAAC,WAAW,2BAAW,CAAC"}
|
package/dist/provide.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { ReactInstance,
|
|
2
|
-
import {
|
|
1
|
+
import { ReactInstance, ComponentType } from 'react';
|
|
2
|
+
import { ProviderConfigEntrySingletons, ProviderConfigEntryInstances } from './provider';
|
|
3
3
|
export interface WrappedComponent<P> {
|
|
4
|
-
wrappedComponent:
|
|
4
|
+
wrappedComponent: ComponentType<P>;
|
|
5
5
|
wrappedInstance: ReactInstance | undefined;
|
|
6
6
|
}
|
|
7
|
-
declare type
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
export declare type ProvideDecorator = (stores: {
|
|
8
|
+
singletons?: ProviderConfigEntrySingletons<any>[];
|
|
9
|
+
instances?: ProviderConfigEntryInstances<any>[];
|
|
10
|
+
}, options?: {
|
|
11
|
+
loadingFallback?: JSX.Element;
|
|
12
|
+
errorFallback?: JSX.Element;
|
|
13
|
+
}) => <T extends ComponentType>(target: T) => T & WrappedComponent<T>;
|
|
12
14
|
export declare const provide: ProvideDecorator;
|
|
13
|
-
export {};
|
|
14
15
|
//# sourceMappingURL=provide.d.ts.map
|
package/dist/provide.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provide.d.ts","sourceRoot":"","sources":["../src/provide.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,
|
|
1
|
+
{"version":3,"file":"provide.d.ts","sourceRoot":"","sources":["../src/provide.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAErD,OAAO,EAAY,6BAA6B,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAEnG,MAAM,WAAW,gBAAgB,CAAC,CAAC;IAC/B,gBAAgB,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACnC,eAAe,EAAE,aAAa,GAAG,SAAS,CAAC;CAC9C;AAED,oBAAY,gBAAgB,GAAG,CAC3B,MAAM,EAAE;IACJ,UAAU,CAAC,EAAE,6BAA6B,CAAC,GAAG,CAAC,EAAE,CAAC;IAClD,SAAS,CAAC,EAAE,4BAA4B,CAAC,GAAG,CAAC,EAAE,CAAC;CACnD,EACD,OAAO,CAAC,EAAE;IACN,eAAe,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAC9B,aAAa,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;CAC/B,KACA,CAAC,CAAC,SAAS,aAAa,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC;AAErE,eAAO,MAAM,OAAO,kBASM,CAAC"}
|
package/dist/provide.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Provider } from './provider';
|
|
3
|
-
export const provide = ((stores,
|
|
3
|
+
export const provide = ((stores, options) => (Component) => props => (_jsx(Provider, Object.assign({}, stores, { loadingFallback: options === null || options === void 0 ? void 0 : options.loadingFallback, errorFallback: options === null || options === void 0 ? void 0 : options.errorFallback }, { children: _jsx(Component, Object.assign({}, props)) }))));
|
|
4
4
|
//# sourceMappingURL=provide.js.map
|
package/dist/provide.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provide.js","sourceRoot":"","sources":["../src/provide.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"provide.js","sourceRoot":"","sources":["../src/provide.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,QAAQ,EAA+D,MAAM,YAAY,CAAC;AAkBnG,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,SAAwB,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAC/E,CACI,KAAC,QAAQ,oBACD,MAAM,IACV,eAAe,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,eAAe,EACzC,aAAa,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,aAAa,gBAErC,KAAC,SAAS,oBAAK,KAAK,EAAI,IACjB,CACd,CAAqB,CAAC"}
|
package/dist/provider.d.ts
CHANGED
|
@@ -1,19 +1,30 @@
|
|
|
1
|
-
import { FC } from 'react';
|
|
1
|
+
import { FC, ReactElement } from 'react';
|
|
2
2
|
import { interfaces } from 'inversify';
|
|
3
3
|
import { Token, SymbolToken } from './common';
|
|
4
|
-
export declare type
|
|
5
|
-
|
|
4
|
+
export declare type ProviderConfigEntrySingletons<T> = ClassProviderConfigEntry<T> | ValueProviderConfigEntry<T>;
|
|
5
|
+
export declare type ProviderConfigEntryInstances<T> = ClassProviderConfigEntryNoInherit<T>;
|
|
6
|
+
export declare type ProviderConfigEntry<T> = ProviderConfigEntrySingletons<T> | ProviderConfigEntryInstances<T>;
|
|
7
|
+
declare type ClassProviderConfigEntry<T> = interfaces.Newable<T> | ClassProviderConfigEntryUseClass<T> | ClassProviderConfigEntryToken<T>;
|
|
8
|
+
declare type ClassProviderConfigEntryNoInherit<T> = interfaces.Newable<T> | Omit<ClassProviderConfigEntryUseClass<T>, 'inherit'>;
|
|
9
|
+
interface ClassProviderConfigEntryUseClass<T> {
|
|
6
10
|
provide: Token<T>;
|
|
7
11
|
useClass: interfaces.Newable<T>;
|
|
12
|
+
inherit?: true;
|
|
13
|
+
}
|
|
14
|
+
interface ClassProviderConfigEntryToken<T> {
|
|
15
|
+
provide: interfaces.Newable<T>;
|
|
16
|
+
inherit?: true;
|
|
8
17
|
}
|
|
9
18
|
interface ValueProviderConfigEntry<T> {
|
|
10
19
|
provide: SymbolToken<T> | interfaces.Abstract<T>;
|
|
11
20
|
useValue: T;
|
|
21
|
+
inherit?: true;
|
|
12
22
|
}
|
|
13
23
|
export interface ProviderProps {
|
|
14
|
-
singletons?:
|
|
15
|
-
instances?:
|
|
16
|
-
|
|
24
|
+
singletons?: ProviderConfigEntrySingletons<any>[];
|
|
25
|
+
instances?: ProviderConfigEntryInstances<any>[];
|
|
26
|
+
loadingFallback?: ReactElement;
|
|
27
|
+
errorFallback?: ReactElement;
|
|
17
28
|
}
|
|
18
29
|
export declare const Provider: FC<ProviderProps>;
|
|
19
30
|
export {};
|
package/dist/provider.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAyC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,YAAY,EAAyC,MAAM,OAAO,CAAC;AAEhF,OAAO,EAAa,UAAU,EAAE,MAAM,WAAW,CAAC;AAGlD,OAAO,EAAoB,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAIhE,oBAAY,6BAA6B,CAAC,CAAC,IACrC,wBAAwB,CAAC,CAAC,CAAC,GAC3B,wBAAwB,CAAC,CAAC,CAAC,CAAC;AAClC,oBAAY,4BAA4B,CAAC,CAAC,IAAI,iCAAiC,CAAC,CAAC,CAAC,CAAC;AACnF,oBAAY,mBAAmB,CAAC,CAAC,IAC3B,6BAA6B,CAAC,CAAC,CAAC,GAChC,4BAA4B,CAAC,CAAC,CAAC,CAAC;AAEtC,aAAK,wBAAwB,CAAC,CAAC,IACzB,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,GACrB,gCAAgC,CAAC,CAAC,CAAC,GACnC,6BAA6B,CAAC,CAAC,CAAC,CAAC;AAEvC,aAAK,iCAAiC,CAAC,CAAC,IAClC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,GACrB,IAAI,CAAC,gCAAgC,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AAE3D,UAAU,gCAAgC,CAAC,CAAC;IACxC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IAClB,QAAQ,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,IAAI,CAAC;CAClB;AAED,UAAU,6BAA6B,CAAC,CAAC;IACrC,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,IAAI,CAAC;CAClB;AAED,UAAU,wBAAwB,CAAC,CAAC;IAChC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACjD,QAAQ,EAAE,CAAC,CAAC;IACZ,OAAO,CAAC,EAAE,IAAI,CAAC;CAClB;AA8GD,MAAM,WAAW,aAAa;IAC1B,UAAU,CAAC,EAAE,6BAA6B,CAAC,GAAG,CAAC,EAAE,CAAC;IAClD,SAAS,CAAC,EAAE,4BAA4B,CAAC,GAAG,CAAC,EAAE,CAAC;IAChD,eAAe,CAAC,EAAE,YAAY,CAAC;IAC/B,aAAa,CAAC,EAAE,YAAY,CAAC;CAChC;AAED,eAAO,MAAM,QAAQ,EAAE,EAAE,CAAC,aAAa,CAmFtC,CAAC"}
|
package/dist/provider.js
CHANGED
|
@@ -3,65 +3,106 @@ import { useState, useContext, useLayoutEffect } from 'react';
|
|
|
3
3
|
import { Container } from 'inversify';
|
|
4
4
|
import { Await } from './await';
|
|
5
5
|
import { ContainerContext } from './common';
|
|
6
|
+
import { defaultErrorFallback, defaultLoadingFallback } from './fallbacks';
|
|
6
7
|
import { isStore } from './store';
|
|
7
|
-
function
|
|
8
|
-
return
|
|
9
|
-
Object.prototype.hasOwnProperty.call(entry, 'useClass'));
|
|
8
|
+
function isClassProviderConfigEntryUseClass(entry) {
|
|
9
|
+
return Object.hasOwn(entry, 'provide') && Object.hasOwn(entry, 'useClass');
|
|
10
10
|
}
|
|
11
11
|
function isValueProviderConfigEntry(entry) {
|
|
12
|
-
return
|
|
13
|
-
Object.prototype.hasOwnProperty.call(entry, 'useValue'));
|
|
12
|
+
return Object.hasOwn(entry, 'provide') && Object.hasOwn(entry, 'useValue');
|
|
14
13
|
}
|
|
15
|
-
|
|
14
|
+
function isClassProviderConfigEntryToken(entry) {
|
|
15
|
+
return (Object.hasOwn(entry, 'provide') &&
|
|
16
|
+
!Object.hasOwn(entry, 'useClass') &&
|
|
17
|
+
!Object.hasOwn(entry, 'useValue'));
|
|
18
|
+
}
|
|
19
|
+
function isProviderConfigEntryInheritable(entry) {
|
|
20
|
+
return (isClassProviderConfigEntryUseClass(entry) ||
|
|
21
|
+
isClassProviderConfigEntryToken(entry) ||
|
|
22
|
+
isValueProviderConfigEntry(entry));
|
|
23
|
+
}
|
|
24
|
+
function getProvideTokenWithClass(entry) {
|
|
25
|
+
if (isClassProviderConfigEntryUseClass(entry)) {
|
|
26
|
+
return [entry.provide, entry.useClass];
|
|
27
|
+
}
|
|
28
|
+
if (isClassProviderConfigEntryToken(entry)) {
|
|
29
|
+
return [entry.provide, entry.provide];
|
|
30
|
+
}
|
|
31
|
+
if (isValueProviderConfigEntry(entry)) {
|
|
32
|
+
return [undefined, undefined];
|
|
33
|
+
}
|
|
34
|
+
return [entry, entry];
|
|
35
|
+
}
|
|
36
|
+
function getStoreInstances(container, singletons = [], instances = []) {
|
|
37
|
+
return [...singletons, ...instances].reduce((out, entry) => {
|
|
38
|
+
const [provide, useClass] = getProvideTokenWithClass(entry);
|
|
39
|
+
if (provide && useClass && container.isCurrentBound(provide) && isStore(useClass)) {
|
|
40
|
+
out.push(container.get(provide));
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}, []);
|
|
44
|
+
}
|
|
45
|
+
const bindEntry = (container, checkInherit) => (entry) => {
|
|
46
|
+
if (checkInherit &&
|
|
47
|
+
isProviderConfigEntryInheritable(entry) &&
|
|
48
|
+
entry.inherit &&
|
|
49
|
+
container.isBound(entry.provide)) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
if (isClassProviderConfigEntryUseClass(entry)) {
|
|
53
|
+
return container.bind(entry.provide).to(entry.useClass);
|
|
54
|
+
}
|
|
55
|
+
if (isClassProviderConfigEntryToken(entry)) {
|
|
56
|
+
return container.bind(entry.provide).to(entry.provide);
|
|
57
|
+
}
|
|
58
|
+
if (isValueProviderConfigEntry(entry)) {
|
|
59
|
+
// using toDynamicValue to return BindingInSyntax
|
|
60
|
+
return container.bind(entry.provide).toDynamicValue(() => entry.useValue);
|
|
61
|
+
}
|
|
62
|
+
return container.bind(entry).to(entry);
|
|
63
|
+
};
|
|
64
|
+
const unbindEntry = (container) => (entry) => {
|
|
65
|
+
if (isClassProviderConfigEntryUseClass(entry) ||
|
|
66
|
+
isValueProviderConfigEntry(entry) ||
|
|
67
|
+
isClassProviderConfigEntryToken(entry)) {
|
|
68
|
+
return container.unbind(entry.provide);
|
|
69
|
+
}
|
|
70
|
+
return container.unbind(entry);
|
|
71
|
+
};
|
|
72
|
+
export const Provider = ({ singletons, instances, loadingFallback = defaultLoadingFallback, errorFallback = defaultErrorFallback, children, }) => {
|
|
16
73
|
const parentContainer = useContext(ContainerContext);
|
|
17
74
|
const [container, setContainer] = useState();
|
|
18
|
-
const [promises, setPromises] = useState();
|
|
75
|
+
const [promises, setPromises] = useState([]);
|
|
76
|
+
const [error, setError] = useState(false);
|
|
19
77
|
useLayoutEffect(() => {
|
|
20
78
|
const container = new Container();
|
|
21
79
|
container.parent = parentContainer;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return container.bind(entry.provide).to(entry.useClass);
|
|
25
|
-
}
|
|
26
|
-
if (isValueProviderConfigEntry(entry)) {
|
|
27
|
-
// using toDynamicValue to return BindingInSyntax
|
|
28
|
-
return container.bind(entry.provide).toDynamicValue(() => entry.useValue);
|
|
29
|
-
}
|
|
30
|
-
return container.bind(entry).to(entry);
|
|
31
|
-
};
|
|
32
|
-
singletons === null || singletons === void 0 ? void 0 : singletons.map(bind).forEach(binding => {
|
|
33
|
-
binding.inSingletonScope();
|
|
80
|
+
singletons === null || singletons === void 0 ? void 0 : singletons.map(bindEntry(container, true)).forEach(binding => {
|
|
81
|
+
binding === null || binding === void 0 ? void 0 : binding.inSingletonScope();
|
|
34
82
|
});
|
|
35
|
-
instances === null || instances === void 0 ? void 0 : instances.forEach(
|
|
36
|
-
const storeInstances =
|
|
37
|
-
.map(entry => {
|
|
38
|
-
if (isValueProviderConfigEntry(entry)) {
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
const { provide, useClass } = isClassProviderConfigEntry(entry)
|
|
42
|
-
? entry
|
|
43
|
-
: { provide: entry, useClass: entry };
|
|
44
|
-
if (isStore(useClass)) {
|
|
45
|
-
return container.get(provide);
|
|
46
|
-
}
|
|
47
|
-
return undefined;
|
|
48
|
-
})
|
|
49
|
-
.filter(Boolean);
|
|
83
|
+
instances === null || instances === void 0 ? void 0 : instances.forEach(bindEntry(container, false));
|
|
84
|
+
const storeInstances = getStoreInstances(container, singletons, instances);
|
|
50
85
|
const promises = [];
|
|
51
86
|
for (const instance of storeInstances) {
|
|
52
87
|
if (instance.initialize) {
|
|
53
|
-
|
|
88
|
+
let promise;
|
|
89
|
+
try {
|
|
90
|
+
promise = instance.initialize();
|
|
91
|
+
}
|
|
92
|
+
catch (_a) {
|
|
93
|
+
setError(true);
|
|
94
|
+
}
|
|
95
|
+
if (promise && typeof promise.catch === 'function') {
|
|
96
|
+
promises.push(promise.catch(() => {
|
|
97
|
+
setError(true);
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
54
100
|
}
|
|
55
101
|
}
|
|
56
102
|
setContainer(container);
|
|
57
103
|
setPromises(promises);
|
|
58
104
|
return () => {
|
|
59
|
-
const unbind = (
|
|
60
|
-
if (isClassProviderConfigEntry(entry) || isValueProviderConfigEntry(entry)) {
|
|
61
|
-
return container.unbind(entry.provide);
|
|
62
|
-
}
|
|
63
|
-
return container.unbind(entry);
|
|
64
|
-
};
|
|
105
|
+
const unbind = unbindEntry(container);
|
|
65
106
|
singletons === null || singletons === void 0 ? void 0 : singletons.forEach(unbind);
|
|
66
107
|
instances === null || instances === void 0 ? void 0 : instances.forEach(unbind);
|
|
67
108
|
for (const instance of storeInstances) {
|
|
@@ -74,6 +115,15 @@ export const Provider = ({ singletons, instances, fallback, children }) => {
|
|
|
74
115
|
if (!container) {
|
|
75
116
|
return null;
|
|
76
117
|
}
|
|
77
|
-
|
|
118
|
+
const getContent = () => {
|
|
119
|
+
if (error) {
|
|
120
|
+
return errorFallback;
|
|
121
|
+
}
|
|
122
|
+
if (promises.length) {
|
|
123
|
+
return (_jsx(Await, Object.assign({ promises: promises, fallback: loadingFallback }, { children: children })));
|
|
124
|
+
}
|
|
125
|
+
return children;
|
|
126
|
+
};
|
|
127
|
+
return _jsx(ContainerContext.Provider, Object.assign({ value: container }, { children: getContent() }));
|
|
78
128
|
};
|
|
79
129
|
//# sourceMappingURL=provider.js.map
|
package/dist/provider.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":";AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"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"}
|
package/dist/store.d.ts
CHANGED
|
@@ -4,10 +4,10 @@ interface Identifier<T> extends interfaces.Newable<T> {
|
|
|
4
4
|
[symbol]?: boolean;
|
|
5
5
|
}
|
|
6
6
|
export declare function isStore<T>(identifier: Identifier<T>): boolean | undefined;
|
|
7
|
-
export declare abstract class Store {
|
|
7
|
+
export declare abstract class Store implements IStore {
|
|
8
8
|
static [symbol]: boolean;
|
|
9
9
|
}
|
|
10
|
-
export interface
|
|
10
|
+
export interface IStore {
|
|
11
11
|
initialize?(): Promise<void>;
|
|
12
12
|
dispose?(): void;
|
|
13
13
|
}
|
package/dist/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,UAAU,EAAE,MAAM,WAAW,CAAC;AAEnD,QAAA,MAAM,MAAM,eAAmC,CAAC;AAEhD,UAAU,UAAU,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IACjD,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,uBAEnD;AAED,8BACsB,
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,UAAU,EAAE,MAAM,WAAW,CAAC;AAEnD,QAAA,MAAM,MAAM,eAAmC,CAAC;AAEhD,UAAU,UAAU,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IACjD,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,uBAEnD;AAED,8BACsB,KAAM,YAAW,MAAM;IACzC,MAAM,CAAC,CAAC,MAAM,CAAC,UAAQ;CAC1B;AAED,MAAM,WAAW,MAAM;IACnB,UAAU,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,CAAC,IAAI,IAAI,CAAC;CACpB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@servicetitan/react-ioc",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "23.0.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"homepage": "https://docs.st.dev/docs/frontend/react-ioc",
|
|
6
6
|
"repository": {
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
"reflect-metadata": "~0.1.13"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
|
+
"@servicetitan/startup-jest": "^1.1.0",
|
|
23
|
+
"@testing-library/react": "^12.0.0",
|
|
22
24
|
"@types/react": "~17.0.38",
|
|
23
25
|
"react": "~17.0.2"
|
|
24
26
|
},
|
|
@@ -31,5 +33,5 @@
|
|
|
31
33
|
"cli": {
|
|
32
34
|
"webpack": false
|
|
33
35
|
},
|
|
34
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "45876ed36415ad78d3ec4ef3783d6df1430504d3"
|
|
35
37
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import TestRenderer from 'react-test-renderer';
|
|
2
|
+
import { Await } from '../await';
|
|
3
|
+
import { waitFor } from '@testing-library/react';
|
|
4
|
+
|
|
5
|
+
const TestContent = () => <div role="content">hello</div>;
|
|
6
|
+
|
|
7
|
+
describe(`[react-ioc] ${Await.displayName}`, () => {
|
|
8
|
+
test('should render content when no promises are passed', async () => {
|
|
9
|
+
const { root } = TestRenderer.create(
|
|
10
|
+
<Await promises={[]}>
|
|
11
|
+
<TestContent />
|
|
12
|
+
</Await>
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
await TestRenderer.act(async () => {
|
|
16
|
+
await waitFor(() => {
|
|
17
|
+
expect(root.findByType(TestContent)).toBeTruthy();
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { PropsWithChildren, ReactElement, ReactNode, useEffect } from 'react';
|
|
2
|
+
import { provide, inject, injectable, useDependencies, Store, symbolToken, Provider } from '..';
|
|
3
|
+
import TestRenderer from 'react-test-renderer';
|
|
4
|
+
import { waitFor } from '@testing-library/react';
|
|
5
|
+
|
|
6
|
+
@injectable()
|
|
7
|
+
class StatStore {
|
|
8
|
+
readonly id = 'stat';
|
|
9
|
+
counters: Record<string, number> = {};
|
|
10
|
+
queueCreate: string[] = [];
|
|
11
|
+
queueRender: string[] = [];
|
|
12
|
+
|
|
13
|
+
testInitialize: (id: string) => void = jest.fn();
|
|
14
|
+
testRender = (id: string) => {
|
|
15
|
+
this.queueRender.push(id);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
register(name: string) {
|
|
19
|
+
if (!this.counters[name]) {
|
|
20
|
+
this.counters[name] = 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
this.counters[name]++;
|
|
24
|
+
|
|
25
|
+
const id = `${name}-${this.counters[name]}`;
|
|
26
|
+
this.queueCreate.push(id);
|
|
27
|
+
|
|
28
|
+
return id;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@injectable()
|
|
33
|
+
class StoreSimple {
|
|
34
|
+
readonly id;
|
|
35
|
+
|
|
36
|
+
constructor(@inject(StatStore) stat: StatStore) {
|
|
37
|
+
this.id = stat.register('StoreSimple');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@injectable()
|
|
42
|
+
class StoreWithInitialize extends Store {
|
|
43
|
+
readonly id;
|
|
44
|
+
|
|
45
|
+
constructor(@inject(StatStore) private stat: StatStore) {
|
|
46
|
+
super();
|
|
47
|
+
this.id = stat.register('StoreWithInitialize');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
initialize() {
|
|
51
|
+
this.stat.testInitialize(this.id);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
type TestStore = typeof StoreSimple | typeof StoreWithInitialize | ReturnType<typeof symbolToken>;
|
|
56
|
+
|
|
57
|
+
const getTestComponent = (inst: TestStore) => {
|
|
58
|
+
return ({ children }: PropsWithChildren<{}>) => {
|
|
59
|
+
const [{ id }, { testRender }] = useDependencies<[any, StatStore]>(inst, StatStore);
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
testRender(id);
|
|
62
|
+
}, [testRender, id]);
|
|
63
|
+
return <div>{children}</div>;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
type ProviderStoreProps = Parameters<typeof provide>[0];
|
|
68
|
+
|
|
69
|
+
function TestProvider(props: PropsWithChildren<ProviderStoreProps & { store: TestStore }>) {
|
|
70
|
+
const { singletons, instances, children, store = StoreSimple } = props;
|
|
71
|
+
const Component = provide({ singletons, instances })(getTestComponent(store));
|
|
72
|
+
return <Component>{children}</Component>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
describe(`[react-ioc] ${provide.name}`, () => {
|
|
76
|
+
let stat!: StatStore;
|
|
77
|
+
|
|
78
|
+
const Stat = provide({ singletons: [StatStore] })(({ children }) => {
|
|
79
|
+
const [statStore] = useDependencies(StatStore);
|
|
80
|
+
stat = statStore;
|
|
81
|
+
return <div>{children}</div>;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const subject = (children: ReactNode) => TestRenderer.create(<Stat>{children}</Stat>);
|
|
85
|
+
|
|
86
|
+
const expectQueueRenderToBe = (queueRender: string[]) =>
|
|
87
|
+
TestRenderer.act(async () => {
|
|
88
|
+
await waitFor(() => {
|
|
89
|
+
expect(stat.queueRender).toEqual(queueRender);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('check nested stores', () => {
|
|
94
|
+
describe('singletons', () => {
|
|
95
|
+
test('should reuse singletons for each container.get', async () => {
|
|
96
|
+
subject(
|
|
97
|
+
<TestProvider store={StoreSimple} singletons={[StoreSimple]}>
|
|
98
|
+
<TestProvider store={StoreSimple} />
|
|
99
|
+
</TestProvider>
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
expect(stat.queueCreate).toEqual(['StoreSimple-1']);
|
|
103
|
+
|
|
104
|
+
await expectQueueRenderToBe(['StoreSimple-1', 'StoreSimple-1']);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('should create separate singletons for nested tokens', async () => {
|
|
108
|
+
subject(
|
|
109
|
+
<TestProvider store={StoreSimple} singletons={[StoreSimple]}>
|
|
110
|
+
<TestProvider store={StoreSimple} singletons={[StoreSimple]} />
|
|
111
|
+
</TestProvider>
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
expect(stat.queueCreate).toEqual(['StoreSimple-1', 'StoreSimple-2']);
|
|
115
|
+
|
|
116
|
+
await expectQueueRenderToBe(['StoreSimple-1', 'StoreSimple-2']);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('should reuse singleton when using provide inheritance and instance exists', async () => {
|
|
120
|
+
subject(
|
|
121
|
+
<TestProvider store={StoreSimple} singletons={[StoreSimple]}>
|
|
122
|
+
<TestProvider
|
|
123
|
+
store={StoreSimple}
|
|
124
|
+
singletons={[{ provide: StoreSimple, inherit: true }]}
|
|
125
|
+
/>
|
|
126
|
+
</TestProvider>
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
expect(stat.queueCreate).toEqual(['StoreSimple-1']);
|
|
130
|
+
|
|
131
|
+
await expectQueueRenderToBe(['StoreSimple-1', 'StoreSimple-1']);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('should create singletons when using provide inheritance and instance not exists', () => {
|
|
135
|
+
subject(
|
|
136
|
+
<TestProvider
|
|
137
|
+
store={StoreSimple}
|
|
138
|
+
singletons={[{ provide: StoreSimple, inherit: true }]}
|
|
139
|
+
>
|
|
140
|
+
<TestProvider store={StoreSimple} singletons={[StoreSimple]} />
|
|
141
|
+
</TestProvider>
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
expect(stat.queueCreate).toEqual(['StoreSimple-1', 'StoreSimple-2']);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
describe('instances', () => {
|
|
149
|
+
test('should create separate instances for each container.get', async () => {
|
|
150
|
+
subject(
|
|
151
|
+
<TestProvider store={StoreSimple} instances={[StoreSimple]}>
|
|
152
|
+
<TestProvider store={StoreSimple} />
|
|
153
|
+
</TestProvider>
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
expect(stat.queueCreate).toEqual(['StoreSimple-1', 'StoreSimple-2']);
|
|
157
|
+
|
|
158
|
+
await expectQueueRenderToBe(['StoreSimple-1', 'StoreSimple-2']);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('should create separate instances for nested tokens', async () => {
|
|
162
|
+
subject(
|
|
163
|
+
<TestProvider store={StoreSimple} instances={[StoreSimple]}>
|
|
164
|
+
<TestProvider store={StoreSimple} instances={[StoreSimple]} />
|
|
165
|
+
</TestProvider>
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
expect(stat.queueCreate).toEqual(['StoreSimple-1', 'StoreSimple-2']);
|
|
169
|
+
|
|
170
|
+
await expectQueueRenderToBe(['StoreSimple-1', 'StoreSimple-2']);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe('useValue', () => {
|
|
175
|
+
const valueToken = symbolToken('valueToken');
|
|
176
|
+
|
|
177
|
+
test('create new values', async () => {
|
|
178
|
+
subject(
|
|
179
|
+
<TestProvider
|
|
180
|
+
store={valueToken}
|
|
181
|
+
singletons={[{ provide: valueToken, useValue: { id: 'value1' } }]}
|
|
182
|
+
>
|
|
183
|
+
<TestProvider
|
|
184
|
+
store={valueToken}
|
|
185
|
+
singletons={[{ provide: valueToken, useValue: { id: 'value2' } }]}
|
|
186
|
+
/>
|
|
187
|
+
</TestProvider>
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
await expectQueueRenderToBe(['value1', 'value2']);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test('reuse nested values', async () => {
|
|
194
|
+
subject(
|
|
195
|
+
<TestProvider
|
|
196
|
+
store={valueToken}
|
|
197
|
+
singletons={[{ provide: valueToken, useValue: { id: 'value1' } }]}
|
|
198
|
+
>
|
|
199
|
+
<TestProvider
|
|
200
|
+
store={valueToken}
|
|
201
|
+
singletons={[
|
|
202
|
+
{ provide: valueToken, useValue: { id: 'value2' }, inherit: true },
|
|
203
|
+
]}
|
|
204
|
+
/>
|
|
205
|
+
</TestProvider>
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
await expectQueueRenderToBe(['value1', 'value1']);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test('no reuse nested values for instances', async () => {
|
|
212
|
+
subject(
|
|
213
|
+
<TestProvider
|
|
214
|
+
store={valueToken}
|
|
215
|
+
singletons={[{ provide: valueToken, useValue: { id: 'value1' } }]}
|
|
216
|
+
>
|
|
217
|
+
<TestProvider
|
|
218
|
+
store={valueToken}
|
|
219
|
+
instances={[
|
|
220
|
+
{
|
|
221
|
+
provide: valueToken,
|
|
222
|
+
useValue: { id: 'value2' },
|
|
223
|
+
inherit: true,
|
|
224
|
+
} as any,
|
|
225
|
+
]}
|
|
226
|
+
/>
|
|
227
|
+
</TestProvider>
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
await expectQueueRenderToBe(['value1', 'value2']);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
describe('check store initialization', () => {
|
|
236
|
+
test('should wait for store initialization', async () => {
|
|
237
|
+
subject(
|
|
238
|
+
<TestProvider store={StoreWithInitialize} singletons={[StoreWithInitialize]} />
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
expect(stat.queueRender).toEqual([]);
|
|
242
|
+
|
|
243
|
+
await TestRenderer.act(async () => {
|
|
244
|
+
await waitFor(() => {
|
|
245
|
+
expect(stat.testInitialize).toHaveBeenCalledWith('StoreWithInitialize-1');
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
await expectQueueRenderToBe(['StoreWithInitialize-1']);
|
|
250
|
+
});
|
|
251
|
+
test('should wait for for nested initialization', async () => {
|
|
252
|
+
subject(
|
|
253
|
+
<TestProvider store={StoreWithInitialize} singletons={[StoreWithInitialize]}>
|
|
254
|
+
<TestProvider store={StoreWithInitialize} singletons={[StoreWithInitialize]} />
|
|
255
|
+
</TestProvider>
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
await TestRenderer.act(async () => {
|
|
259
|
+
await waitFor(() => {
|
|
260
|
+
expect(stat.testInitialize).toHaveBeenCalledWith('StoreWithInitialize-1');
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
await waitFor(() => {
|
|
264
|
+
expect(stat.testInitialize).toHaveBeenCalledWith('StoreWithInitialize-2');
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
expect(stat.queueCreate).toEqual(['StoreWithInitialize-1', 'StoreWithInitialize-2']);
|
|
269
|
+
|
|
270
|
+
await expectQueueRenderToBe(['StoreWithInitialize-1', 'StoreWithInitialize-2']);
|
|
271
|
+
});
|
|
272
|
+
test('should initialize only once when store is reused', async () => {
|
|
273
|
+
subject(
|
|
274
|
+
<TestProvider store={StoreWithInitialize} singletons={[StoreWithInitialize]}>
|
|
275
|
+
<TestProvider
|
|
276
|
+
store={StoreWithInitialize}
|
|
277
|
+
singletons={[{ provide: StoreWithInitialize, inherit: true }]}
|
|
278
|
+
/>
|
|
279
|
+
</TestProvider>
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
await TestRenderer.act(async () => {
|
|
283
|
+
await waitFor(() => {
|
|
284
|
+
expect(stat.testInitialize).toHaveBeenCalledWith('StoreWithInitialize-1');
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
expect(stat.queueCreate).toEqual(['StoreWithInitialize-1']);
|
|
289
|
+
await expectQueueRenderToBe(['StoreWithInitialize-1', 'StoreWithInitialize-1']);
|
|
290
|
+
expect(stat.testInitialize).toHaveBeenCalledTimes(1);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
describe('error initialization', () => {
|
|
294
|
+
@injectable()
|
|
295
|
+
class StoreWithErrorInitialize extends Store {
|
|
296
|
+
initialize() {
|
|
297
|
+
throw new Error('error');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
@injectable()
|
|
301
|
+
class StoreWithAsyncErrorInitialize extends Store {
|
|
302
|
+
async initialize() {
|
|
303
|
+
await new Promise<void>(resolve => resolve());
|
|
304
|
+
throw new Error('error');
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const subject = async (
|
|
309
|
+
children: ReactElement
|
|
310
|
+
): Promise<ReturnType<typeof TestRenderer.create>> => {
|
|
311
|
+
let result: ReturnType<typeof TestRenderer.create> | undefined = undefined;
|
|
312
|
+
|
|
313
|
+
await TestRenderer.act(async () => {
|
|
314
|
+
// we need this function to be async because of TestRenderer warnings
|
|
315
|
+
await new Promise<void>(resolve => resolve());
|
|
316
|
+
result = TestRenderer.create(children);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
if (!result) {
|
|
320
|
+
throw new Error();
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return result;
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
test('async error initialization should show error', async () => {
|
|
327
|
+
const { root } = await subject(
|
|
328
|
+
<Provider singletons={[StoreWithAsyncErrorInitialize]}>hello</Provider>
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
expect(root.findByType('h2')).toBeTruthy();
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
test('sync error initialization should show error', async () => {
|
|
335
|
+
const { root } = await subject(
|
|
336
|
+
<Provider singletons={[StoreWithErrorInitialize]}>hello</Provider>
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
expect(root.findByType('h2')).toBeTruthy();
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
});
|
package/src/await.tsx
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { FC, lazy, ReactElement, ReactNode, Suspense, useMemo } from 'react';
|
|
2
|
+
import { defaultLoadingFallback } from './fallbacks';
|
|
2
3
|
|
|
3
4
|
export interface AwaitProps {
|
|
4
5
|
promises: Promise<void>[];
|
|
5
6
|
fallback?: NonNullable<ReactNode> | null;
|
|
6
7
|
}
|
|
7
8
|
|
|
8
|
-
export const Await: FC<AwaitProps> = ({
|
|
9
|
+
export const Await: FC<AwaitProps> = ({
|
|
10
|
+
promises,
|
|
11
|
+
fallback = defaultLoadingFallback,
|
|
12
|
+
children,
|
|
13
|
+
}) => {
|
|
9
14
|
const Lazy = useMemo(
|
|
10
15
|
() =>
|
|
11
16
|
lazy<FC>(async () => {
|
package/src/provide.tsx
CHANGED
|
@@ -1,25 +1,30 @@
|
|
|
1
|
-
import { ReactInstance,
|
|
1
|
+
import { ReactInstance, ComponentType } from 'react';
|
|
2
2
|
|
|
3
|
-
import { Provider,
|
|
3
|
+
import { Provider, ProviderConfigEntrySingletons, ProviderConfigEntryInstances } from './provider';
|
|
4
4
|
|
|
5
5
|
export interface WrappedComponent<P> {
|
|
6
|
-
wrappedComponent:
|
|
6
|
+
wrappedComponent: ComponentType<P>;
|
|
7
7
|
wrappedInstance: ReactInstance | undefined;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
type
|
|
11
|
-
|
|
12
|
-
type ProvideDecorator = (
|
|
10
|
+
export type ProvideDecorator = (
|
|
13
11
|
stores: {
|
|
14
|
-
singletons?:
|
|
15
|
-
instances?:
|
|
12
|
+
singletons?: ProviderConfigEntrySingletons<any>[];
|
|
13
|
+
instances?: ProviderConfigEntryInstances<any>[];
|
|
16
14
|
},
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
options?: {
|
|
16
|
+
loadingFallback?: JSX.Element;
|
|
17
|
+
errorFallback?: JSX.Element;
|
|
18
|
+
}
|
|
19
|
+
) => <T extends ComponentType>(target: T) => T & WrappedComponent<T>;
|
|
19
20
|
|
|
20
|
-
export const provide = ((stores,
|
|
21
|
+
export const provide = ((stores, options) => (Component: ComponentType) => props =>
|
|
21
22
|
(
|
|
22
|
-
<Provider
|
|
23
|
+
<Provider
|
|
24
|
+
{...stores}
|
|
25
|
+
loadingFallback={options?.loadingFallback}
|
|
26
|
+
errorFallback={options?.errorFallback}
|
|
27
|
+
>
|
|
23
28
|
<Component {...props} />
|
|
24
29
|
</Provider>
|
|
25
30
|
)) as ProvideDecorator;
|
package/src/provider.tsx
CHANGED
|
@@ -1,100 +1,203 @@
|
|
|
1
|
-
import { FC, useState, useContext, useLayoutEffect } from 'react';
|
|
1
|
+
import { FC, ReactElement, useState, useContext, useLayoutEffect } from 'react';
|
|
2
2
|
|
|
3
3
|
import { Container, interfaces } from 'inversify';
|
|
4
4
|
|
|
5
5
|
import { Await } from './await';
|
|
6
6
|
import { ContainerContext, Token, SymbolToken } from './common';
|
|
7
|
-
import {
|
|
7
|
+
import { defaultErrorFallback, defaultLoadingFallback } from './fallbacks';
|
|
8
|
+
import { isStore, IStore } from './store';
|
|
8
9
|
|
|
9
|
-
export type
|
|
10
|
-
| interfaces.Newable<T>
|
|
10
|
+
export type ProviderConfigEntrySingletons<T> =
|
|
11
11
|
| ClassProviderConfigEntry<T>
|
|
12
12
|
| ValueProviderConfigEntry<T>;
|
|
13
|
+
export type ProviderConfigEntryInstances<T> = ClassProviderConfigEntryNoInherit<T>;
|
|
14
|
+
export type ProviderConfigEntry<T> =
|
|
15
|
+
| ProviderConfigEntrySingletons<T>
|
|
16
|
+
| ProviderConfigEntryInstances<T>;
|
|
17
|
+
|
|
18
|
+
type ClassProviderConfigEntry<T> =
|
|
19
|
+
| interfaces.Newable<T>
|
|
20
|
+
| ClassProviderConfigEntryUseClass<T>
|
|
21
|
+
| ClassProviderConfigEntryToken<T>;
|
|
13
22
|
|
|
14
|
-
|
|
23
|
+
type ClassProviderConfigEntryNoInherit<T> =
|
|
24
|
+
| interfaces.Newable<T>
|
|
25
|
+
| Omit<ClassProviderConfigEntryUseClass<T>, 'inherit'>;
|
|
26
|
+
|
|
27
|
+
interface ClassProviderConfigEntryUseClass<T> {
|
|
15
28
|
provide: Token<T>;
|
|
16
29
|
useClass: interfaces.Newable<T>;
|
|
30
|
+
inherit?: true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface ClassProviderConfigEntryToken<T> {
|
|
34
|
+
provide: interfaces.Newable<T>;
|
|
35
|
+
inherit?: true;
|
|
17
36
|
}
|
|
18
37
|
|
|
19
38
|
interface ValueProviderConfigEntry<T> {
|
|
20
39
|
provide: SymbolToken<T> | interfaces.Abstract<T>;
|
|
21
40
|
useValue: T;
|
|
41
|
+
inherit?: true;
|
|
22
42
|
}
|
|
23
43
|
|
|
24
|
-
function
|
|
44
|
+
function isClassProviderConfigEntryUseClass<T>(
|
|
25
45
|
entry: ProviderConfigEntry<T>
|
|
26
|
-
): entry is
|
|
46
|
+
): entry is Omit<ClassProviderConfigEntryUseClass<T>, 'inherit'> {
|
|
47
|
+
return Object.hasOwn(entry, 'provide') && Object.hasOwn(entry, 'useClass');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isValueProviderConfigEntry<T>(
|
|
51
|
+
entry: ProviderConfigEntry<T>
|
|
52
|
+
): entry is Omit<ValueProviderConfigEntry<T>, 'inherit'> {
|
|
53
|
+
return Object.hasOwn(entry, 'provide') && Object.hasOwn(entry, 'useValue');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isClassProviderConfigEntryToken<T>(
|
|
57
|
+
entry: ProviderConfigEntry<T>
|
|
58
|
+
): entry is Omit<ClassProviderConfigEntryToken<T>, 'inherit'> {
|
|
27
59
|
return (
|
|
28
|
-
Object.
|
|
29
|
-
Object.
|
|
60
|
+
Object.hasOwn(entry, 'provide') &&
|
|
61
|
+
!Object.hasOwn(entry, 'useClass') &&
|
|
62
|
+
!Object.hasOwn(entry, 'useValue')
|
|
30
63
|
);
|
|
31
64
|
}
|
|
32
65
|
|
|
33
|
-
function
|
|
66
|
+
function isProviderConfigEntryInheritable<T>(
|
|
34
67
|
entry: ProviderConfigEntry<T>
|
|
35
|
-
): entry is
|
|
68
|
+
): entry is
|
|
69
|
+
| ClassProviderConfigEntryUseClass<T>
|
|
70
|
+
| ClassProviderConfigEntryToken<T>
|
|
71
|
+
| ValueProviderConfigEntry<T> {
|
|
36
72
|
return (
|
|
37
|
-
|
|
38
|
-
|
|
73
|
+
isClassProviderConfigEntryUseClass(entry) ||
|
|
74
|
+
isClassProviderConfigEntryToken(entry) ||
|
|
75
|
+
isValueProviderConfigEntry(entry)
|
|
39
76
|
);
|
|
40
77
|
}
|
|
41
78
|
|
|
79
|
+
function getProvideTokenWithClass<T>(
|
|
80
|
+
entry: ProviderConfigEntry<T>
|
|
81
|
+
): [Token<T>, interfaces.Newable<T>] | [undefined, undefined] {
|
|
82
|
+
if (isClassProviderConfigEntryUseClass(entry)) {
|
|
83
|
+
return [entry.provide, entry.useClass];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (isClassProviderConfigEntryToken(entry)) {
|
|
87
|
+
return [entry.provide, entry.provide];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (isValueProviderConfigEntry(entry)) {
|
|
91
|
+
return [undefined, undefined];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return [entry, entry];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function getStoreInstances(
|
|
98
|
+
container: Container,
|
|
99
|
+
singletons: ProviderConfigEntrySingletons<any>[] = [],
|
|
100
|
+
instances: ProviderConfigEntryInstances<any>[] = []
|
|
101
|
+
): IStore[] {
|
|
102
|
+
return [...singletons, ...instances].reduce((out, entry) => {
|
|
103
|
+
const [provide, useClass] = getProvideTokenWithClass(entry);
|
|
104
|
+
|
|
105
|
+
if (provide && useClass && container.isCurrentBound(provide) && isStore(useClass)) {
|
|
106
|
+
out.push(container.get(provide));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return out;
|
|
110
|
+
}, [] as IStore[]);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const bindEntry =
|
|
114
|
+
(container: Container, checkInherit: boolean) => (entry: ProviderConfigEntry<any>) => {
|
|
115
|
+
if (
|
|
116
|
+
checkInherit &&
|
|
117
|
+
isProviderConfigEntryInheritable(entry) &&
|
|
118
|
+
entry.inherit &&
|
|
119
|
+
container.isBound(entry.provide)
|
|
120
|
+
) {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (isClassProviderConfigEntryUseClass(entry)) {
|
|
125
|
+
return container.bind(entry.provide).to(entry.useClass);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (isClassProviderConfigEntryToken(entry)) {
|
|
129
|
+
return container.bind(entry.provide).to(entry.provide);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (isValueProviderConfigEntry(entry)) {
|
|
133
|
+
// using toDynamicValue to return BindingInSyntax
|
|
134
|
+
return container.bind(entry.provide).toDynamicValue(() => entry.useValue);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return container.bind(entry).to(entry);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const unbindEntry = (container: Container) => (entry: ProviderConfigEntry<any>) => {
|
|
141
|
+
if (
|
|
142
|
+
isClassProviderConfigEntryUseClass(entry) ||
|
|
143
|
+
isValueProviderConfigEntry(entry) ||
|
|
144
|
+
isClassProviderConfigEntryToken(entry)
|
|
145
|
+
) {
|
|
146
|
+
return container.unbind(entry.provide);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return container.unbind(entry);
|
|
150
|
+
};
|
|
151
|
+
|
|
42
152
|
export interface ProviderProps {
|
|
43
|
-
singletons?:
|
|
44
|
-
instances?:
|
|
45
|
-
|
|
153
|
+
singletons?: ProviderConfigEntrySingletons<any>[];
|
|
154
|
+
instances?: ProviderConfigEntryInstances<any>[];
|
|
155
|
+
loadingFallback?: ReactElement;
|
|
156
|
+
errorFallback?: ReactElement;
|
|
46
157
|
}
|
|
47
158
|
|
|
48
|
-
export const Provider: FC<ProviderProps> = ({
|
|
159
|
+
export const Provider: FC<ProviderProps> = ({
|
|
160
|
+
singletons,
|
|
161
|
+
instances,
|
|
162
|
+
loadingFallback = defaultLoadingFallback,
|
|
163
|
+
errorFallback = defaultErrorFallback,
|
|
164
|
+
children,
|
|
165
|
+
}) => {
|
|
49
166
|
const parentContainer = useContext(ContainerContext);
|
|
50
167
|
|
|
51
168
|
const [container, setContainer] = useState<Container>();
|
|
52
|
-
const [promises, setPromises] = useState<Promise<void>[]>();
|
|
169
|
+
const [promises, setPromises] = useState<Promise<void>[]>([]);
|
|
170
|
+
const [error, setError] = useState(false);
|
|
53
171
|
|
|
54
172
|
useLayoutEffect(() => {
|
|
55
173
|
const container = new Container();
|
|
56
174
|
container.parent = parentContainer;
|
|
57
175
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return container.bind(entry.provide).to(entry.useClass);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (isValueProviderConfigEntry(entry)) {
|
|
64
|
-
// using toDynamicValue to return BindingInSyntax
|
|
65
|
-
return container.bind(entry.provide).toDynamicValue(() => entry.useValue);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return container.bind(entry).to(entry);
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
singletons?.map(bind).forEach(binding => {
|
|
72
|
-
binding.inSingletonScope();
|
|
176
|
+
singletons?.map(bindEntry(container, true)).forEach(binding => {
|
|
177
|
+
binding?.inSingletonScope();
|
|
73
178
|
});
|
|
74
|
-
instances?.forEach(
|
|
75
|
-
|
|
76
|
-
const storeInstances = [...(singletons ?? []), ...(instances ?? [])]
|
|
77
|
-
.map(entry => {
|
|
78
|
-
if (isValueProviderConfigEntry(entry)) {
|
|
79
|
-
return undefined;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const { provide, useClass } = isClassProviderConfigEntry(entry)
|
|
83
|
-
? entry
|
|
84
|
-
: { provide: entry, useClass: entry };
|
|
179
|
+
instances?.forEach(bindEntry(container, false));
|
|
85
180
|
|
|
86
|
-
|
|
87
|
-
return container.get(provide);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return undefined;
|
|
91
|
-
})
|
|
92
|
-
.filter(Boolean);
|
|
181
|
+
const storeInstances = getStoreInstances(container, singletons, instances);
|
|
93
182
|
|
|
94
183
|
const promises: Promise<void>[] = [];
|
|
95
184
|
for (const instance of storeInstances) {
|
|
96
185
|
if (instance.initialize) {
|
|
97
|
-
|
|
186
|
+
let promise;
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
promise = instance.initialize();
|
|
190
|
+
} catch {
|
|
191
|
+
setError(true);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (promise && typeof promise.catch === 'function') {
|
|
195
|
+
promises.push(
|
|
196
|
+
promise.catch(() => {
|
|
197
|
+
setError(true);
|
|
198
|
+
})
|
|
199
|
+
);
|
|
200
|
+
}
|
|
98
201
|
}
|
|
99
202
|
}
|
|
100
203
|
|
|
@@ -102,13 +205,7 @@ export const Provider: FC<ProviderProps> = ({ singletons, instances, fallback, c
|
|
|
102
205
|
setPromises(promises);
|
|
103
206
|
|
|
104
207
|
return () => {
|
|
105
|
-
const unbind = (
|
|
106
|
-
if (isClassProviderConfigEntry(entry) || isValueProviderConfigEntry(entry)) {
|
|
107
|
-
return container.unbind(entry.provide);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return container.unbind(entry);
|
|
111
|
-
};
|
|
208
|
+
const unbind = unbindEntry(container);
|
|
112
209
|
|
|
113
210
|
singletons?.forEach(unbind);
|
|
114
211
|
instances?.forEach(unbind);
|
|
@@ -125,15 +222,21 @@ export const Provider: FC<ProviderProps> = ({ singletons, instances, fallback, c
|
|
|
125
222
|
return null;
|
|
126
223
|
}
|
|
127
224
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
225
|
+
const getContent = () => {
|
|
226
|
+
if (error) {
|
|
227
|
+
return errorFallback;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (promises.length) {
|
|
231
|
+
return (
|
|
232
|
+
<Await promises={promises} fallback={loadingFallback}>
|
|
132
233
|
{children}
|
|
133
234
|
</Await>
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return children;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
return <ContainerContext.Provider value={container}>{getContent()}</ContainerContext.Provider>;
|
|
139
242
|
};
|
package/src/store.ts
CHANGED
|
@@ -11,11 +11,11 @@ export function isStore<T>(identifier: Identifier<T>) {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
@injectable()
|
|
14
|
-
export abstract class Store {
|
|
14
|
+
export abstract class Store implements IStore {
|
|
15
15
|
static [symbol] = true;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export interface
|
|
18
|
+
export interface IStore {
|
|
19
19
|
initialize?(): Promise<void>;
|
|
20
20
|
dispose?(): void;
|
|
21
21
|
}
|