Skip to content

Commit a4c2437

Browse files
committed
feat(reactant-module): implement @dynamic for dynamic load modules
1 parent 4b3261e commit a4c2437

File tree

22 files changed

+271
-5
lines changed

22 files changed

+271
-5
lines changed

packages/reactant-di/src/decorators/multiInject.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { setMetadata } from '../util';
66
export function multiInject(serviceIdentifier: ServiceIdentifier<any>) {
77
return (target: object, key?: string, index?: number) => {
88
const paramtypes = Reflect.getMetadata(METADATA_KEY.paramtypes, target);
9-
setMetadata(METADATA_KEY.multiple, paramtypes[index!], serviceIdentifier);
9+
setMetadata(METADATA_KEY.multiple, paramtypes?.[index!], serviceIdentifier);
1010
decorate(
1111
multiInjectWithInversify(serviceIdentifier) as ClassDecorator,
1212
target,

packages/reactant-di/src/decorators/multiOptional.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import { setMetadata } from '../util';
77
export function multiOptional(serviceIdentifier: ServiceIdentifier<any>) {
88
return (target: object, key?: string, index?: number) => {
99
const paramtypes = Reflect.getMetadata(METADATA_KEY.paramtypes, target);
10-
setMetadata(METADATA_KEY.optional, paramtypes[index!], serviceIdentifier);
11-
setMetadata(METADATA_KEY.multiple, paramtypes[index!], serviceIdentifier);
10+
setMetadata(METADATA_KEY.optional, paramtypes?.[index!], serviceIdentifier);
11+
setMetadata(METADATA_KEY.multiple, paramtypes?.[index!], serviceIdentifier);
1212
decorate(multiInject(serviceIdentifier) as ClassDecorator, target, index);
1313
decorate(optionalWithInversify() as ClassDecorator, target, index);
1414
};

packages/reactant-model/test/index.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ test('base model with `useValue`', () => {
5858
load: (...args: any[]) => {
5959
//
6060
},
61+
dynamicModules: new Map(),
6162
pluginHooks: {
6263
middleware: [],
6364
beforeCombineRootReducers: [],
@@ -128,6 +129,7 @@ test('base model with `useFactory`', () => {
128129
ServiceIdentifiers,
129130
loadedModules: new Set(),
130131
load: (...args: any[]) => {},
132+
dynamicModules: new Map(),
131133
pluginHooks: {
132134
middleware: [],
133135
beforeCombineRootReducers: [],
@@ -202,6 +204,7 @@ test('base model with `useValue` and `enablePatches`', () => {
202204
load: (...args: any[]) => {
203205
//
204206
},
207+
dynamicModules: new Map(),
205208
pluginHooks: {
206209
middleware: [],
207210
beforeCombineRootReducers: [],

packages/reactant-module/src/constants/moduleKeys.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export const identifierKey: unique symbol = Symbol('identifier');
33
export const modulesKey: unique symbol = Symbol('modules');
44
export const nameKey: unique symbol = Symbol('name');
55
export const initStateKey: unique symbol = Symbol('initState');
6+
export const dynamicModulesKey: unique symbol = Symbol('dynamicModules');

packages/reactant-module/src/core/createStore.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-param-reassign */
12
/* eslint-disable default-param-last */
23
/* eslint-disable guard-for-in */
34
/* eslint-disable @typescript-eslint/no-empty-function */
@@ -38,10 +39,12 @@ import {
3839
initStateKey,
3940
subscriptionsKey,
4041
enableInspectorKey,
42+
dynamicModulesKey,
4143
} from '../constants';
4244
import { getStagedState } from '../decorators';
43-
import {
45+
import type {
4446
DevOptions,
47+
DynamicModules,
4548
Loader,
4649
ModulesMap,
4750
PluginHooks,
@@ -60,6 +63,7 @@ interface CreateStoreOptions<T> {
6063
ServiceIdentifiers: ServiceIdentifiersMap;
6164
loadedModules: Set<any>;
6265
load: (...args: Parameters<Loader>) => void;
66+
dynamicModules: DynamicModules;
6367
pluginHooks: PluginHooks;
6468
preloadedState?: PreloadedState<T>;
6569
devOptions?: DevOptions;
@@ -74,6 +78,7 @@ export function createStore<T = any>({
7478
ServiceIdentifiers,
7579
loadedModules,
7680
load,
81+
dynamicModules,
7782
pluginHooks,
7883
preloadedState,
7984
devOptions = {},
@@ -97,6 +102,15 @@ export function createStore<T = any>({
97102
}
98103
}
99104

105+
dynamicModules.forEach((module, key) => {
106+
try {
107+
const services = container!.getAll(key);
108+
module.value = !module.multiple ? services[0] : services;
109+
} catch (e) {
110+
//
111+
}
112+
});
113+
100114
// add Non-dependent `modules` to ServiceIdentifiers config.
101115
for (const module of modules) {
102116
const moduleIdentifier =
@@ -296,7 +310,13 @@ export function createStore<T = any>({
296310
return container;
297311
},
298312
},
299-
// loader for dynamic modules.
313+
// dynamic modules cache
314+
[dynamicModulesKey]: {
315+
enumerable: false,
316+
configurable: false,
317+
value: dynamicModules,
318+
},
319+
// loader for dynamic modules
300320
[loaderKey]: {
301321
enumerable: false,
302322
configurable: false,
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/* eslint-disable @typescript-eslint/no-unused-vars */
2+
/* eslint-disable @typescript-eslint/no-explicit-any */
3+
import { multiOptional, ServiceIdentifier } from 'reactant-di';
4+
5+
import { containerKey, dynamicModulesKey, identifierKey } from '../constants';
6+
import type { DynamicModules, PropertyDescriptor } from '../interfaces';
7+
8+
type Dynamic = <M extends boolean = false>(
9+
serviceIdentifier: ServiceIdentifier<unknown>,
10+
options?: {
11+
/**
12+
* Whether to inject multiple instances.
13+
*/
14+
multiple?: boolean;
15+
}
16+
) => (
17+
target: object,
18+
key: string | symbol,
19+
descriptor?: PropertyDescriptor<unknown>
20+
) => void;
21+
22+
export const dynamic: Dynamic = (serviceIdentifier, options) => (
23+
target,
24+
key
25+
) => {
26+
const multipleInject = options?.multiple ?? false;
27+
if (multipleInject) {
28+
multiOptional(serviceIdentifier)(class {});
29+
}
30+
function getter(this: any) {
31+
const dynamicModules: DynamicModules = this[dynamicModulesKey];
32+
if (__DEV__ && !dynamicModules) {
33+
throw new Error(
34+
`The property '${key.toString()}' is not readable when class ${
35+
this[identifierKey] ?? this.constructor.name
36+
} is constructing.`
37+
);
38+
}
39+
if (dynamicModules.has(serviceIdentifier)) {
40+
return dynamicModules.get(serviceIdentifier)!.value;
41+
}
42+
let value: unknown = null;
43+
try {
44+
const services = this[containerKey]!.getAll(serviceIdentifier);
45+
value = !multipleInject ? services[0] : services;
46+
} catch (e) {
47+
//
48+
}
49+
dynamicModules.set(serviceIdentifier, { multiple: multipleInject, value });
50+
return value;
51+
}
52+
53+
function setter(this: any, _: unknown) {
54+
throw new Error(
55+
`Cannot assign to read only 'dynamic' injection property '${key.toString()}' of class '${
56+
this[identifierKey] ?? this.constructor.name
57+
}'`
58+
);
59+
}
60+
61+
// It should be compatible with the TS decorator and the babel decorator
62+
return {
63+
configurable: true,
64+
enumerable: true,
65+
get: getter,
66+
set: setter,
67+
} as any;
68+
};

packages/reactant-module/src/decorators/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ export * from './autobind';
33
export * from './state';
44
export * from './computed';
55
export * from './lazy';
6+
export * from './dynamic';
67
export * from './injectable';

packages/reactant-module/src/interfaces.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,3 +270,5 @@ export type ImportClass<T, K extends keyof T> = T extends Record<K, infer S>
270270
: never
271271
: never;
272272
export type Store = ReduxStore;
273+
274+
export type DynamicModules = Map<any, { multiple: boolean; value: any }>;

packages/reactant-module/test/core/createState.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ test('`createState` with type', () => {
3535
ServiceIdentifiers,
3636
loadedModules: new Set(),
3737
load: (...args: any[]) => {},
38+
dynamicModules: new Map(),
3839
pluginHooks: {
3940
middleware: [],
4041
beforeCombineRootReducers: [],

packages/reactant-module/test/core/createStore.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ describe('createStore', () => {
3434
load: (...args: any[]) => {
3535
//
3636
},
37+
dynamicModules: new Map(),
3738
pluginHooks: {
3839
middleware: [],
3940
beforeCombineRootReducers: [],
@@ -73,6 +74,7 @@ describe('createStore', () => {
7374
load: (...args: any[]) => {
7475
//
7576
},
77+
dynamicModules: new Map(),
7678
pluginHooks: {
7779
middleware: [],
7880
beforeCombineRootReducers: [],
@@ -130,6 +132,7 @@ describe('createStore', () => {
130132
load: (...args: any[]) => {
131133
//
132134
},
135+
dynamicModules: new Map(),
133136
pluginHooks: {
134137
middleware: [],
135138
beforeCombineRootReducers: [],
@@ -191,6 +194,7 @@ describe('createStore', () => {
191194
load: (...args: any[]) => {
192195
//
193196
},
197+
dynamicModules: new Map(),
194198
pluginHooks: {
195199
middleware: [],
196200
beforeCombineRootReducers: [],
@@ -240,6 +244,7 @@ describe('createStore', () => {
240244
load: (...args: any[]) => {
241245
//
242246
},
247+
dynamicModules: new Map(),
243248
pluginHooks: {
244249
middleware: [],
245250
beforeCombineRootReducers: [],
@@ -297,6 +302,7 @@ describe('createStore', () => {
297302
load: (...args: any[]) => {
298303
//
299304
},
305+
dynamicModules: new Map(),
300306
pluginHooks: {
301307
middleware: [],
302308
beforeCombineRootReducers: [],

0 commit comments

Comments
 (0)