Skip to content

Commit

Permalink
feat: impl ModuleConfigs for standalone (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
killagu committed Jul 28, 2023
1 parent c277a97 commit 7227492
Show file tree
Hide file tree
Showing 16 changed files with 306 additions and 43 deletions.
41 changes: 41 additions & 0 deletions standalone/standalone/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,44 @@ await main(cwd, {
},
});
```

### 配置

module 支持通过 module.yml 来定义配置,在代码中可以通过注入 moduleConfigs 获取全局配置,通过注入 moduleConfig 来获取单 module 的配置。

```yaml
# module.yml
# module 根目录中

features:
dynamic:
foo: 'bar'
```
```ts
@ContextProto()
export class Foo {
// 获取全局配置, 通过 get 方法来获取特定 module 的配置
@Inject()
moduleConfigs: ModuleConfigs;

// 注入当前 module 的配置
@Inject()
moduleConfig: ModuleConfig;

// 注入 "bar" module 的配置
@Inject({
name: 'moduleConfig',
})
@ConfigSourceQualifier('bar')
barModuleConfig: ModuleConfig;

async main() {
return {
configs: this.moduleConfigs,
foo: this.moduleConfig,
bar: this.barModuleConfig,
};
}
}
```
9 changes: 9 additions & 0 deletions standalone/standalone/src/ConfigSource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { QualifierUtil, EggProtoImplClass } from '@eggjs/tegg';

export const ConfigSourceQualifierAttribute = Symbol.for('Qualifier.ConfigSource');

export function ConfigSourceQualifier(moduleName: string) {
return function(target: any, propertyKey: PropertyKey) {
QualifierUtil.addProperQualifier(target.constructor as EggProtoImplClass, propertyKey, ConfigSourceQualifierAttribute, moduleName);
};
}
20 changes: 20 additions & 0 deletions standalone/standalone/src/ConfigSourceLoadUnitHook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { LoadUnit, LoadUnitLifecycleContext } from '@eggjs/tegg-metadata';
import {
LifecycleHook,
PrototypeUtil, QualifierUtil,
} from '@eggjs/tegg';
import { ConfigSourceQualifier, ConfigSourceQualifierAttribute } from './ConfigSource';

export class ConfigSourceLoadUnitHook implements LifecycleHook<LoadUnitLifecycleContext, LoadUnit> {
async preCreate(ctx: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise<void> {
const classList = ctx.loader.load();
for (const clazz of classList) {
const injectObjects = PrototypeUtil.getInjectObjects(clazz);
const moduleConfigObject = injectObjects.find(t => t.objName === 'moduleConfig');
const configSourceQualifier = QualifierUtil.getProperQualifier(clazz, 'moduleConfig', ConfigSourceQualifierAttribute);
if (moduleConfigObject && !configSourceQualifier) {
ConfigSourceQualifier(loadUnit.name)(clazz.prototype, moduleConfigObject.refName);
}
}
}
}
8 changes: 8 additions & 0 deletions standalone/standalone/src/ModuleConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import 'egg';

// for declare merging
declare module 'egg' {
export interface ModuleConfig {
// ...
}
}
17 changes: 17 additions & 0 deletions standalone/standalone/src/ModuleConfigs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ModuleConfig } from 'egg';
import { ModuleReference } from '@eggjs/tegg-common-util';

export interface ModuleConfigHolder {
name: string;
config: ModuleConfig;
reference: ModuleReference;
}

export class ModuleConfigs {
constructor(readonly inner: Record<string, ModuleConfigHolder>) {
}

get(moduleName: string): ModuleConfig | undefined {
return this.inner[moduleName]?.config;
}
}
86 changes: 60 additions & 26 deletions standalone/standalone/src/Runner.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { ModuleConfig, ModuleConfigUtil, ModuleReference } from '@eggjs/tegg-common-util';
import { EggPrototype, LoadUnit, LoadUnitFactory } from '@eggjs/tegg-metadata';
import { ModuleConfigUtil, ModuleReference } from '@eggjs/tegg-common-util';
import {
EggPrototype,
LoadUnit,
LoadUnitFactory,
LoadUnitLifecycleUtil,
} from '@eggjs/tegg-metadata';
import {
ContextHandler,
EggContainerFactory, EggContext,
Expand All @@ -10,18 +15,21 @@ import {
import { EggProtoImplClass, PrototypeUtil } from '@eggjs/tegg';
import { StandaloneUtil, MainRunner } from '@eggjs/tegg/standalone';
import { EggModuleLoader } from './EggModuleLoader';
import { StandaloneLoadUnit, StandaloneLoadUnitType } from './StandaloneLoadUnit';
import { InnerObject, StandaloneLoadUnit, StandaloneLoadUnitType } from './StandaloneLoadUnit';
import { StandaloneContext } from './StandaloneContext';
import { StandaloneContextHandler } from './StandaloneContextHandler';

export interface ModuleConfigHolder {
name: string;
config: ModuleConfig;
reference: ModuleReference;
}
import { ModuleConfigHolder, ModuleConfigs } from './ModuleConfigs';
import { ConfigSourceQualifierAttribute } from './ConfigSource';
import { ConfigSourceLoadUnitHook } from './ConfigSourceLoadUnitHook';

export interface RunnerOptions {
innerObjects: Record<string, object>;
/**
* @deprecated
* use inner object handlers instead
*/
innerObjects?: Record<string, object>;

innerObjectHandlers?: Record<string, InnerObject[]>;
}

export class Runner {
Expand All @@ -30,16 +38,23 @@ export class Runner {
readonly moduleConfigs: Record<string, ModuleConfigHolder>;
private loadUnitLoader: EggModuleLoader;
private runnerProto: EggPrototype;
private configSourceEggPrototypeHook: ConfigSourceLoadUnitHook;

loadUnits: LoadUnit[] = [];
loadUnitInstances: LoadUnitInstance[] = [];
innerObjects: Record<string, object> | undefined;
innerObjects: Record<string, InnerObject[]>;

constructor(cwd: string, options?: RunnerOptions) {
this.cwd = cwd;
this.innerObjects = options?.innerObjects;
this.moduleReferences = ModuleConfigUtil.readModuleReference(this.cwd);
this.moduleConfigs = {};
this.innerObjects = {
moduleConfigs: [{
obj: new ModuleConfigs(this.moduleConfigs),
}],
moduleConfig: [],
};

for (const reference of this.moduleReferences) {
const absoluteRef = {
path: ModuleConfigUtil.resolveModuleDir(reference.path, this.cwd),
Expand All @@ -52,26 +67,42 @@ export class Runner {
config: ModuleConfigUtil.loadModuleConfigSync(absoluteRef.path) || {},
};
}
for (const moduleConfig of Object.values(this.moduleConfigs)) {
this.innerObjects.moduleConfig.push({
obj: moduleConfig.config,
qualifiers: [{
attribute: ConfigSourceQualifierAttribute,
value: moduleConfig.name,
}],
});
}
if (options?.innerObjects) {
for (const [ name, obj ] of Object.entries(options.innerObjects)) {
this.innerObjects[name] = [{
obj,
}];
}
} else if (options?.innerObjectHandlers) {
Object.assign(this.innerObjects, options.innerObjectHandlers);
}
this.loadUnitLoader = new EggModuleLoader(this.moduleReferences);
const configSourceEggPrototypeHook = new ConfigSourceLoadUnitHook();
LoadUnitLifecycleUtil.registerLifecycle(configSourceEggPrototypeHook);
}

async init() {
StandaloneContextHandler.register();
this.loadUnits = [];
if (this.innerObjects) {
LoadUnitFactory.registerLoadUnitCreator(StandaloneLoadUnitType, () => {
return new StandaloneLoadUnit(this.innerObjects!);
});
LoadUnitInstanceFactory.registerLoadUnitInstanceClass(StandaloneLoadUnitType, ModuleLoadUnitInstance.createModuleLoadUnitInstance);
const standaloneLoadUnit = await LoadUnitFactory.createLoadUnit('MockStandaloneLoadUnitPath', StandaloneLoadUnitType, {
load(): EggProtoImplClass[] {
return [];
},
});
this.loadUnits.push(standaloneLoadUnit);
}
LoadUnitFactory.registerLoadUnitCreator(StandaloneLoadUnitType, () => {
return new StandaloneLoadUnit(this.innerObjects);
});
LoadUnitInstanceFactory.registerLoadUnitInstanceClass(StandaloneLoadUnitType, ModuleLoadUnitInstance.createModuleLoadUnitInstance);
const standaloneLoadUnit = await LoadUnitFactory.createLoadUnit('MockStandaloneLoadUnitPath', StandaloneLoadUnitType, {
load(): EggProtoImplClass[] {
return [];
},
});
const loadUnits = await this.loadUnitLoader.load();
this.loadUnits = this.loadUnits.concat(loadUnits);
this.loadUnits = [ standaloneLoadUnit, ...loadUnits ];

const instances: LoadUnitInstance[] = [];
for (const loadUnit of this.loadUnits) {
Expand Down Expand Up @@ -123,5 +154,8 @@ export class Runner {
await LoadUnitFactory.destroyLoadUnit(loadUnit);
}
}
if (this.configSourceEggPrototypeHook) {
LoadUnitLifecycleUtil.deleteLifecycle(this.configSourceEggPrototypeHook);
}
}
}
31 changes: 19 additions & 12 deletions standalone/standalone/src/StandaloneLoadUnit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,37 @@ import { StandaloneInnerObjectProto } from './StandaloneInnerObjectProto';

export const StandaloneLoadUnitType = 'StandaloneLoadUnitType';

export interface InnerObject {
obj: object,
qualifiers?: QualifierInfo[],
}

export class StandaloneLoadUnit implements LoadUnit {
readonly id: string = 'StandaloneLoadUnit';
readonly name: string = 'StandaloneLoadUnit';
readonly unitPath: string = 'MockStandaloneLoadUnitPath';
readonly type = StandaloneLoadUnitType;

private innerObject: Record<string, object>;
private innerObject: Record<string, InnerObject[]>;
private protoMap: Map<EggPrototypeName, EggPrototype[]> = new Map();

constructor(innerObject: Record<string, object>) {
constructor(innerObject: Record<string, InnerObject[]>) {
this.innerObject = innerObject;
}

async init() {
for (const [ name, obj ] of Object.entries(this.innerObject)) {
const proto = new StandaloneInnerObjectProto(
IdenticalUtil.createProtoId(this.id, name),
name,
(() => obj) as any,
ObjectInitType.SINGLETON,
this.id,
[],
);
EggPrototypeFactory.instance.registerPrototype(proto, this);
for (const [ name, objs ] of Object.entries(this.innerObject)) {
for (const { obj, qualifiers } of objs) {
const proto = new StandaloneInnerObjectProto(
IdenticalUtil.createProtoId(this.id, name),
name,
(() => obj) as any,
ObjectInitType.SINGLETON,
this.id,
qualifiers || [],
);
EggPrototypeFactory.instance.registerPrototype(proto, this);
}
}
}

Expand Down
28 changes: 28 additions & 0 deletions standalone/standalone/test/fixtures/module-with-config/foo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { ContextProto, Inject, SingletonProto } from '@eggjs/tegg';
import { Runner, MainRunner } from '@eggjs/tegg/standalone';
import { ModuleConfigs } from '../../../src/ModuleConfigs';

@SingletonProto()
export class Hello {
hello() {
return 'hello!';
}
}

@ContextProto()
export class HelloContext {
hello() {
return 'hello from ctx';
}
}

@ContextProto()
@Runner()
export class Foo implements MainRunner<object> {
@Inject()
moduleConfigs: ModuleConfigs;

async main(): Promise<object> {
return this.moduleConfigs.get('simple')!;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
features:
dynamic:
foo: 'bar'
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "simple",
"eggModule": {
"name": "simple"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
features:
dynamic:
bar: 'bar'
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "bar",
"eggModule": {
"name": "bar"
}
}
43 changes: 43 additions & 0 deletions standalone/standalone/test/fixtures/multi-modules/foo/foo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { ContextProto, Inject, SingletonProto } from '@eggjs/tegg';
import { Runner, MainRunner } from '@eggjs/tegg/standalone';
import { ModuleConfig } from 'egg';
import { ConfigSourceQualifier } from '../../../../src/ConfigSource';
import { ModuleConfigs } from '../../../../src/ModuleConfigs';

@SingletonProto()
export class Hello {
hello() {
return 'hello!';
}
}

@ContextProto()
export class HelloContext {
hello() {
return 'hello from ctx';
}
}

@ContextProto()
@Runner()
export class Foo implements MainRunner<object> {
@Inject()
moduleConfigs: ModuleConfigs;

@Inject()
moduleConfig: ModuleConfig;

@Inject({
name: 'moduleConfig',
})
@ConfigSourceQualifier('bar')
barModuleConfig: ModuleConfig;

async main(): Promise<object> {
return {
configs: this.moduleConfigs,
foo: this.moduleConfig,
bar: this.barModuleConfig,
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
features:
dynamic:
foo: 'bar'
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "foo",
"eggModule": {
"name": "foo"
}
}
Loading

0 comments on commit 7227492

Please sign in to comment.