Skip to content

Commit 7227492

Browse files
authored
feat: impl ModuleConfigs for standalone (#136)
1 parent c277a97 commit 7227492

File tree

16 files changed

+306
-43
lines changed

16 files changed

+306
-43
lines changed

standalone/standalone/README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,44 @@ await main(cwd, {
4242
},
4343
});
4444
```
45+
46+
### 配置
47+
48+
module 支持通过 module.yml 来定义配置,在代码中可以通过注入 moduleConfigs 获取全局配置,通过注入 moduleConfig 来获取单 module 的配置。
49+
50+
```yaml
51+
# module.yml
52+
# module 根目录中
53+
54+
features:
55+
dynamic:
56+
foo: 'bar'
57+
```
58+
59+
```ts
60+
@ContextProto()
61+
export class Foo {
62+
// 获取全局配置, 通过 get 方法来获取特定 module 的配置
63+
@Inject()
64+
moduleConfigs: ModuleConfigs;
65+
66+
// 注入当前 module 的配置
67+
@Inject()
68+
moduleConfig: ModuleConfig;
69+
70+
// 注入 "bar" module 的配置
71+
@Inject({
72+
name: 'moduleConfig',
73+
})
74+
@ConfigSourceQualifier('bar')
75+
barModuleConfig: ModuleConfig;
76+
77+
async main() {
78+
return {
79+
configs: this.moduleConfigs,
80+
foo: this.moduleConfig,
81+
bar: this.barModuleConfig,
82+
};
83+
}
84+
}
85+
```
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { QualifierUtil, EggProtoImplClass } from '@eggjs/tegg';
2+
3+
export const ConfigSourceQualifierAttribute = Symbol.for('Qualifier.ConfigSource');
4+
5+
export function ConfigSourceQualifier(moduleName: string) {
6+
return function(target: any, propertyKey: PropertyKey) {
7+
QualifierUtil.addProperQualifier(target.constructor as EggProtoImplClass, propertyKey, ConfigSourceQualifierAttribute, moduleName);
8+
};
9+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { LoadUnit, LoadUnitLifecycleContext } from '@eggjs/tegg-metadata';
2+
import {
3+
LifecycleHook,
4+
PrototypeUtil, QualifierUtil,
5+
} from '@eggjs/tegg';
6+
import { ConfigSourceQualifier, ConfigSourceQualifierAttribute } from './ConfigSource';
7+
8+
export class ConfigSourceLoadUnitHook implements LifecycleHook<LoadUnitLifecycleContext, LoadUnit> {
9+
async preCreate(ctx: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise<void> {
10+
const classList = ctx.loader.load();
11+
for (const clazz of classList) {
12+
const injectObjects = PrototypeUtil.getInjectObjects(clazz);
13+
const moduleConfigObject = injectObjects.find(t => t.objName === 'moduleConfig');
14+
const configSourceQualifier = QualifierUtil.getProperQualifier(clazz, 'moduleConfig', ConfigSourceQualifierAttribute);
15+
if (moduleConfigObject && !configSourceQualifier) {
16+
ConfigSourceQualifier(loadUnit.name)(clazz.prototype, moduleConfigObject.refName);
17+
}
18+
}
19+
}
20+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import 'egg';
2+
3+
// for declare merging
4+
declare module 'egg' {
5+
export interface ModuleConfig {
6+
// ...
7+
}
8+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { ModuleConfig } from 'egg';
2+
import { ModuleReference } from '@eggjs/tegg-common-util';
3+
4+
export interface ModuleConfigHolder {
5+
name: string;
6+
config: ModuleConfig;
7+
reference: ModuleReference;
8+
}
9+
10+
export class ModuleConfigs {
11+
constructor(readonly inner: Record<string, ModuleConfigHolder>) {
12+
}
13+
14+
get(moduleName: string): ModuleConfig | undefined {
15+
return this.inner[moduleName]?.config;
16+
}
17+
}

standalone/standalone/src/Runner.ts

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
import { ModuleConfig, ModuleConfigUtil, ModuleReference } from '@eggjs/tegg-common-util';
2-
import { EggPrototype, LoadUnit, LoadUnitFactory } from '@eggjs/tegg-metadata';
1+
import { ModuleConfigUtil, ModuleReference } from '@eggjs/tegg-common-util';
2+
import {
3+
EggPrototype,
4+
LoadUnit,
5+
LoadUnitFactory,
6+
LoadUnitLifecycleUtil,
7+
} from '@eggjs/tegg-metadata';
38
import {
49
ContextHandler,
510
EggContainerFactory, EggContext,
@@ -10,18 +15,21 @@ import {
1015
import { EggProtoImplClass, PrototypeUtil } from '@eggjs/tegg';
1116
import { StandaloneUtil, MainRunner } from '@eggjs/tegg/standalone';
1217
import { EggModuleLoader } from './EggModuleLoader';
13-
import { StandaloneLoadUnit, StandaloneLoadUnitType } from './StandaloneLoadUnit';
18+
import { InnerObject, StandaloneLoadUnit, StandaloneLoadUnitType } from './StandaloneLoadUnit';
1419
import { StandaloneContext } from './StandaloneContext';
1520
import { StandaloneContextHandler } from './StandaloneContextHandler';
16-
17-
export interface ModuleConfigHolder {
18-
name: string;
19-
config: ModuleConfig;
20-
reference: ModuleReference;
21-
}
21+
import { ModuleConfigHolder, ModuleConfigs } from './ModuleConfigs';
22+
import { ConfigSourceQualifierAttribute } from './ConfigSource';
23+
import { ConfigSourceLoadUnitHook } from './ConfigSourceLoadUnitHook';
2224

2325
export interface RunnerOptions {
24-
innerObjects: Record<string, object>;
26+
/**
27+
* @deprecated
28+
* use inner object handlers instead
29+
*/
30+
innerObjects?: Record<string, object>;
31+
32+
innerObjectHandlers?: Record<string, InnerObject[]>;
2533
}
2634

2735
export class Runner {
@@ -30,16 +38,23 @@ export class Runner {
3038
readonly moduleConfigs: Record<string, ModuleConfigHolder>;
3139
private loadUnitLoader: EggModuleLoader;
3240
private runnerProto: EggPrototype;
41+
private configSourceEggPrototypeHook: ConfigSourceLoadUnitHook;
3342

3443
loadUnits: LoadUnit[] = [];
3544
loadUnitInstances: LoadUnitInstance[] = [];
36-
innerObjects: Record<string, object> | undefined;
45+
innerObjects: Record<string, InnerObject[]>;
3746

3847
constructor(cwd: string, options?: RunnerOptions) {
3948
this.cwd = cwd;
40-
this.innerObjects = options?.innerObjects;
4149
this.moduleReferences = ModuleConfigUtil.readModuleReference(this.cwd);
4250
this.moduleConfigs = {};
51+
this.innerObjects = {
52+
moduleConfigs: [{
53+
obj: new ModuleConfigs(this.moduleConfigs),
54+
}],
55+
moduleConfig: [],
56+
};
57+
4358
for (const reference of this.moduleReferences) {
4459
const absoluteRef = {
4560
path: ModuleConfigUtil.resolveModuleDir(reference.path, this.cwd),
@@ -52,26 +67,42 @@ export class Runner {
5267
config: ModuleConfigUtil.loadModuleConfigSync(absoluteRef.path) || {},
5368
};
5469
}
70+
for (const moduleConfig of Object.values(this.moduleConfigs)) {
71+
this.innerObjects.moduleConfig.push({
72+
obj: moduleConfig.config,
73+
qualifiers: [{
74+
attribute: ConfigSourceQualifierAttribute,
75+
value: moduleConfig.name,
76+
}],
77+
});
78+
}
79+
if (options?.innerObjects) {
80+
for (const [ name, obj ] of Object.entries(options.innerObjects)) {
81+
this.innerObjects[name] = [{
82+
obj,
83+
}];
84+
}
85+
} else if (options?.innerObjectHandlers) {
86+
Object.assign(this.innerObjects, options.innerObjectHandlers);
87+
}
5588
this.loadUnitLoader = new EggModuleLoader(this.moduleReferences);
89+
const configSourceEggPrototypeHook = new ConfigSourceLoadUnitHook();
90+
LoadUnitLifecycleUtil.registerLifecycle(configSourceEggPrototypeHook);
5691
}
5792

5893
async init() {
5994
StandaloneContextHandler.register();
60-
this.loadUnits = [];
61-
if (this.innerObjects) {
62-
LoadUnitFactory.registerLoadUnitCreator(StandaloneLoadUnitType, () => {
63-
return new StandaloneLoadUnit(this.innerObjects!);
64-
});
65-
LoadUnitInstanceFactory.registerLoadUnitInstanceClass(StandaloneLoadUnitType, ModuleLoadUnitInstance.createModuleLoadUnitInstance);
66-
const standaloneLoadUnit = await LoadUnitFactory.createLoadUnit('MockStandaloneLoadUnitPath', StandaloneLoadUnitType, {
67-
load(): EggProtoImplClass[] {
68-
return [];
69-
},
70-
});
71-
this.loadUnits.push(standaloneLoadUnit);
72-
}
95+
LoadUnitFactory.registerLoadUnitCreator(StandaloneLoadUnitType, () => {
96+
return new StandaloneLoadUnit(this.innerObjects);
97+
});
98+
LoadUnitInstanceFactory.registerLoadUnitInstanceClass(StandaloneLoadUnitType, ModuleLoadUnitInstance.createModuleLoadUnitInstance);
99+
const standaloneLoadUnit = await LoadUnitFactory.createLoadUnit('MockStandaloneLoadUnitPath', StandaloneLoadUnitType, {
100+
load(): EggProtoImplClass[] {
101+
return [];
102+
},
103+
});
73104
const loadUnits = await this.loadUnitLoader.load();
74-
this.loadUnits = this.loadUnits.concat(loadUnits);
105+
this.loadUnits = [ standaloneLoadUnit, ...loadUnits ];
75106

76107
const instances: LoadUnitInstance[] = [];
77108
for (const loadUnit of this.loadUnits) {
@@ -123,5 +154,8 @@ export class Runner {
123154
await LoadUnitFactory.destroyLoadUnit(loadUnit);
124155
}
125156
}
157+
if (this.configSourceEggPrototypeHook) {
158+
LoadUnitLifecycleUtil.deleteLifecycle(this.configSourceEggPrototypeHook);
159+
}
126160
}
127161
}

standalone/standalone/src/StandaloneLoadUnit.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,37 @@ import { StandaloneInnerObjectProto } from './StandaloneInnerObjectProto';
66

77
export const StandaloneLoadUnitType = 'StandaloneLoadUnitType';
88

9+
export interface InnerObject {
10+
obj: object,
11+
qualifiers?: QualifierInfo[],
12+
}
13+
914
export class StandaloneLoadUnit implements LoadUnit {
1015
readonly id: string = 'StandaloneLoadUnit';
1116
readonly name: string = 'StandaloneLoadUnit';
1217
readonly unitPath: string = 'MockStandaloneLoadUnitPath';
1318
readonly type = StandaloneLoadUnitType;
1419

15-
private innerObject: Record<string, object>;
20+
private innerObject: Record<string, InnerObject[]>;
1621
private protoMap: Map<EggPrototypeName, EggPrototype[]> = new Map();
1722

18-
constructor(innerObject: Record<string, object>) {
23+
constructor(innerObject: Record<string, InnerObject[]>) {
1924
this.innerObject = innerObject;
2025
}
2126

2227
async init() {
23-
for (const [ name, obj ] of Object.entries(this.innerObject)) {
24-
const proto = new StandaloneInnerObjectProto(
25-
IdenticalUtil.createProtoId(this.id, name),
26-
name,
27-
(() => obj) as any,
28-
ObjectInitType.SINGLETON,
29-
this.id,
30-
[],
31-
);
32-
EggPrototypeFactory.instance.registerPrototype(proto, this);
28+
for (const [ name, objs ] of Object.entries(this.innerObject)) {
29+
for (const { obj, qualifiers } of objs) {
30+
const proto = new StandaloneInnerObjectProto(
31+
IdenticalUtil.createProtoId(this.id, name),
32+
name,
33+
(() => obj) as any,
34+
ObjectInitType.SINGLETON,
35+
this.id,
36+
qualifiers || [],
37+
);
38+
EggPrototypeFactory.instance.registerPrototype(proto, this);
39+
}
3340
}
3441
}
3542

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { ContextProto, Inject, SingletonProto } from '@eggjs/tegg';
2+
import { Runner, MainRunner } from '@eggjs/tegg/standalone';
3+
import { ModuleConfigs } from '../../../src/ModuleConfigs';
4+
5+
@SingletonProto()
6+
export class Hello {
7+
hello() {
8+
return 'hello!';
9+
}
10+
}
11+
12+
@ContextProto()
13+
export class HelloContext {
14+
hello() {
15+
return 'hello from ctx';
16+
}
17+
}
18+
19+
@ContextProto()
20+
@Runner()
21+
export class Foo implements MainRunner<object> {
22+
@Inject()
23+
moduleConfigs: ModuleConfigs;
24+
25+
async main(): Promise<object> {
26+
return this.moduleConfigs.get('simple')!;
27+
}
28+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
features:
2+
dynamic:
3+
foo: 'bar'
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "simple",
3+
"eggModule": {
4+
"name": "simple"
5+
}
6+
}

0 commit comments

Comments
 (0)