Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions tegg/core/aop-decorator/src/CrosscutAdviceFactory.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import assert from 'node:assert';

import { InnerObjectProto } from '@eggjs/core-decorator';
import type { EggProtoImplClass, IAdvice, AdviceInfo } from '@eggjs/tegg-types';

import { CrosscutInfoUtil } from './util/index.ts';

@InnerObjectProto()
export class CrosscutAdviceFactory {
private readonly crosscutAdviceClazzList: Array<EggProtoImplClass<IAdvice>> = [];

Expand Down
4 changes: 1 addition & 3 deletions tegg/core/aop-runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"dependencies": {
"@eggjs/aop-decorator": "workspace:*",
"@eggjs/core-decorator": "workspace:*",
"@eggjs/lifecycle": "workspace:*",
"@eggjs/metadata": "workspace:*",
"@eggjs/tegg-common-util": "workspace:*",
"@eggjs/tegg-runtime": "workspace:*",
Expand All @@ -59,8 +60,5 @@
},
"engines": {
"node": ">=22.18.0"
},
"eggModule": {
"name": "teggAopRuntime"
}
}
27 changes: 27 additions & 0 deletions tegg/core/aop-runtime/src/AopGraphHookRegistrar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { InnerObjectProto } from '@eggjs/core-decorator';
import { LifecyclePostInject } from '@eggjs/lifecycle';
import { GlobalGraph } from '@eggjs/metadata';

import { crossCutGraphHook } from './CrossCutGraphHook.js';
import { pointCutGraphHook } from './PointCutGraphHook.js';
Comment on lines +5 to +6

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

According to the repository's general rules, TypeScript source file imports should use the .ts extension instead of .js to maintain consistency across the codebase.

Suggested change
import { crossCutGraphHook } from './CrossCutGraphHook.js';
import { pointCutGraphHook } from './PointCutGraphHook.js';
import { crossCutGraphHook } from './CrossCutGraphHook.ts';
import { pointCutGraphHook } from './PointCutGraphHook.ts';
References
  1. In this repository, use '.ts' extensions in import/export paths for TypeScript source files to maintain consistency with the existing convention across source and test files.


/**
* Registers the AOP graph build hooks declaratively. Instantiated with the
* InnerObjectLoadUnit, which both hosts run AFTER the business GlobalGraph is
* created and BEFORE build() consumes the hooks — the only valid window.
*/
@InnerObjectProto()
export class AopGraphHookRegistrar {
@LifecyclePostInject()
protected registerGraphHooks(): void {
const globalGraph = GlobalGraph.instance;
if (!globalGraph) {
throw new Error(
'[aop-runtime] GlobalGraph must be created before AopGraphHookRegistrar is instantiated, ' +
'cross-loadUnit crosscut/pointcut weaving would silently never happen',
);
}
globalGraph.registerBuildHook(crossCutGraphHook);
globalGraph.registerBuildHook(pointCutGraphHook);
}
}
3 changes: 2 additions & 1 deletion tegg/core/aop-runtime/src/EggObjectAopHook.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import assert from 'node:assert';

import { Aspect } from '@eggjs/aop-decorator';
import { PrototypeUtil } from '@eggjs/core-decorator';
import { EggObjectLifecycleProto, PrototypeUtil } from '@eggjs/core-decorator';
import { EggContainerFactory } from '@eggjs/tegg-runtime';
import { ASPECT_LIST, InjectType } from '@eggjs/tegg-types';
import type { EggObject, EggObjectLifeCycleContext, LifecycleHook } from '@eggjs/tegg-types';

import { AspectExecutor } from './AspectExecutor.js';

@EggObjectLifecycleProto()
export class EggObjectAopHook implements LifecycleHook<EggObjectLifeCycleContext, EggObject> {
private hijackMethods(obj: any, aspectList: Array<Aspect>) {
for (const aspect of aspectList) {
Expand Down
7 changes: 3 additions & 4 deletions tegg/core/aop-runtime/src/EggPrototypeCrossCutHook.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { CrosscutAdviceFactory, CrosscutInfoUtil } from '@eggjs/aop-decorator';
import { EggPrototypeLifecycleProto, Inject } from '@eggjs/core-decorator';
import type { EggPrototype, EggPrototypeLifecycleContext, LifecycleHook } from '@eggjs/tegg-types';

@EggPrototypeLifecycleProto()
export class EggPrototypeCrossCutHook implements LifecycleHook<EggPrototypeLifecycleContext, EggPrototype> {
@Inject()
private readonly crosscutAdviceFactory: CrosscutAdviceFactory;

constructor(crosscutAdviceFactory: CrosscutAdviceFactory) {
this.crosscutAdviceFactory = crosscutAdviceFactory;
}

async preCreate(ctx: EggPrototypeLifecycleContext): Promise<void> {
if (CrosscutInfoUtil.isCrosscutAdvice(ctx.clazz)) {
this.crosscutAdviceFactory.registerCrossAdviceClazz(ctx.clazz);
Expand Down
7 changes: 3 additions & 4 deletions tegg/core/aop-runtime/src/LoadUnitAopHook.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AspectInfoUtil, AspectMetaBuilder, CrosscutAdviceFactory } from '@eggjs/aop-decorator';
import { Inject, LoadUnitLifecycleProto } from '@eggjs/core-decorator';
import { EggPrototypeFactory, TeggError } from '@eggjs/metadata';
import type {
EggPrototype,
Expand All @@ -8,13 +9,11 @@ import type {
LoadUnitLifecycleContext,
} from '@eggjs/tegg-types';

@LoadUnitLifecycleProto()
export class LoadUnitAopHook implements LifecycleHook<LoadUnitLifecycleContext, LoadUnit> {
@Inject()
private readonly crosscutAdviceFactory: CrosscutAdviceFactory;

constructor(crosscutAdviceFactory: CrosscutAdviceFactory) {
this.crosscutAdviceFactory = crosscutAdviceFactory;
}

async postCreate(_: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise<void> {
for (const proto of loadUnit.iterateEggPrototype()) {
const protoWithClazz = proto as EggPrototypeWithClazz;
Expand Down
1 change: 1 addition & 0 deletions tegg/core/aop-runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './EggObjectAopHook.js';
export * from './EggPrototypeCrossCutHook.js';
export * from './LoadUnitAopHook.js';
export * from './PointCutGraphHook.js';
export * from './AopGraphHookRegistrar.js';
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

exports[`should export stable 1`] = `
{
"AopGraphHookRegistrar": [Function],
"AspectExecutor": [Function],
"EggObjectAopHook": [Function],
"EggPrototypeCrossCutHook": [Function],
Expand Down
21 changes: 12 additions & 9 deletions tegg/core/aop-runtime/test/aop-runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,16 @@ describe('test/aop-runtime.test.ts', () => {
beforeEach(async () => {
crosscutAdviceFactory = new CrosscutAdviceFactory();
eggObjectAopHook = new EggObjectAopHook();
loadUnitAopHook = new LoadUnitAopHook(crosscutAdviceFactory);
eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook(crosscutAdviceFactory);
loadUnitAopHook = new LoadUnitAopHook();
Reflect.set(loadUnitAopHook, 'crosscutAdviceFactory', crosscutAdviceFactory);
eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook();
Reflect.set(eggPrototypeCrossCutHook, 'crosscutAdviceFactory', crosscutAdviceFactory);
EggPrototypeLifecycleUtil.registerLifecycle(eggPrototypeCrossCutHook);
LoadUnitLifecycleUtil.registerLifecycle(loadUnitAopHook);
EggObjectLifecycleUtil.registerLifecycle(eggObjectAopHook);

modules = await CoreTestHelper.prepareModules(
[
path.join(__dirname, '..'),
path.join(__dirname, 'fixtures/modules/hello_succeed'),
path.join(__dirname, 'fixtures/modules/hello_point_cut'),
path.join(__dirname, 'fixtures/modules/state_point_cut'),
Expand Down Expand Up @@ -166,8 +167,10 @@ describe('test/aop-runtime.test.ts', () => {
beforeEach(async () => {
crosscutAdviceFactory = new CrosscutAdviceFactory();
eggObjectAopHook = new EggObjectAopHook();
loadUnitAopHook = new LoadUnitAopHook(crosscutAdviceFactory);
eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook(crosscutAdviceFactory);
loadUnitAopHook = new LoadUnitAopHook();
Reflect.set(loadUnitAopHook, 'crosscutAdviceFactory', crosscutAdviceFactory);
eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook();
Reflect.set(eggPrototypeCrossCutHook, 'crosscutAdviceFactory', crosscutAdviceFactory);
EggPrototypeLifecycleUtil.registerLifecycle(eggPrototypeCrossCutHook);
LoadUnitLifecycleUtil.registerLifecycle(loadUnitAopHook);
EggObjectLifecycleUtil.registerLifecycle(eggObjectAopHook);
Expand All @@ -176,7 +179,6 @@ describe('test/aop-runtime.test.ts', () => {
it('should throw', async () => {
await assert.rejects(async () => {
await CoreTestHelper.prepareModules([
path.join(__dirname, '..'),
path.join(__dirname, 'fixtures/modules/should_throw'),
]);
}, /Aop Advice\(PointcutAdvice\) not found in loadUnits/);
Expand All @@ -193,15 +195,16 @@ describe('test/aop-runtime.test.ts', () => {
beforeEach(async () => {
crosscutAdviceFactory = new CrosscutAdviceFactory();
eggObjectAopHook = new EggObjectAopHook();
loadUnitAopHook = new LoadUnitAopHook(crosscutAdviceFactory);
eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook(crosscutAdviceFactory);
loadUnitAopHook = new LoadUnitAopHook();
Reflect.set(loadUnitAopHook, 'crosscutAdviceFactory', crosscutAdviceFactory);
eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook();
Reflect.set(eggPrototypeCrossCutHook, 'crosscutAdviceFactory', crosscutAdviceFactory);
EggPrototypeLifecycleUtil.registerLifecycle(eggPrototypeCrossCutHook);
LoadUnitLifecycleUtil.registerLifecycle(loadUnitAopHook);
EggObjectLifecycleUtil.registerLifecycle(eggObjectAopHook);

modules = await CoreTestHelper.prepareModules(
[
path.join(__dirname, '..'),
path.join(__dirname, 'fixtures/modules/constructor_inject_aop'),
path.join(__dirname, 'fixtures/modules/hello_point_cut'),
path.join(__dirname, 'fixtures/modules/hello_cross_cut'),
Expand Down
28 changes: 28 additions & 0 deletions tegg/core/core-decorator/src/decorator/EggLifecycleProto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import assert from 'node:assert';

import type { CommonEggLifecycleProtoParams, EggLifecycleProtoParams, EggProtoImplClass } from '@eggjs/tegg-types';

import { PrototypeUtil } from '../util/PrototypeUtil.ts';
import { InnerObjectProto } from './InnerObjectProto.ts';

export function EggLifecycleProto(params: CommonEggLifecycleProtoParams) {
return function (clazz: EggProtoImplClass) {
const { type, ...protoParams } = params || {};
assert(type, 'EggLifecycle decorator should have type property');

InnerObjectProto(protoParams)(clazz);

PrototypeUtil.setIsEggLifecyclePrototype(clazz);
PrototypeUtil.setEggLifecyclePrototypeMetadata(clazz, { type });
};
}

const createLifecycleProto = (type: CommonEggLifecycleProtoParams['type']) => {
return (params?: EggLifecycleProtoParams) => EggLifecycleProto({ type, ...params });
};

export const LoadUnitLifecycleProto = createLifecycleProto('LoadUnit');
export const LoadUnitInstanceLifecycleProto = createLifecycleProto('LoadUnitInstance');
export const EggObjectLifecycleProto = createLifecycleProto('EggObject');
export const EggPrototypeLifecycleProto = createLifecycleProto('EggPrototype');
export const EggContextLifecycleProto = createLifecycleProto('EggContext');
16 changes: 16 additions & 0 deletions tegg/core/core-decorator/src/decorator/InnerObjectProto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { EGG_INNER_OBJECT_PROTO_IMPL_TYPE } from '@eggjs/tegg-types';
import type { EggProtoImplClass, InnerObjectProtoParams } from '@eggjs/tegg-types';

import { PrototypeUtil } from '../util/PrototypeUtil.ts';
import { SingletonProto } from './SingletonProto.ts';

export function InnerObjectProto(params?: InnerObjectProtoParams) {
return function (clazz: EggProtoImplClass) {
const protoParams = {
protoImplType: EGG_INNER_OBJECT_PROTO_IMPL_TYPE,
...params,
};
SingletonProto(protoParams)(clazz);
PrototypeUtil.setIsEggInnerObject(clazz);
};
}
2 changes: 2 additions & 0 deletions tegg/core/core-decorator/src/decorator/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
export * from './ConfigSource.ts';
export * from './ContextProto.ts';
export * from './EggLifecycleProto.ts';
export * from './EggQualifier.ts';
export * from './InitTypeQualifier.ts';
export * from './Inject.ts';
export * from './InnerObjectProto.ts';
export * from './ModuleQualifier.ts';
export * from './MultiInstanceInfo.ts';
export * from './MultiInstanceProto.ts';
Expand Down
56 changes: 56 additions & 0 deletions tegg/core/core-decorator/src/util/PrototypeUtil.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
type EggLifecycleInfo,
type EggMultiInstanceCallbackPrototypeInfo,
type EggMultiInstancePrototypeInfo,
type EggProtoImplClass,
Expand Down Expand Up @@ -37,6 +38,9 @@ export class PrototypeUtil {
static readonly MULTI_INSTANCE_CONSTRUCTOR_ATTRIBUTES: symbol = Symbol.for(
'EggPrototype#multiInstanceConstructorAttributes',
);
static readonly IS_EGG_INNER_OBJECT: symbol = Symbol.for('EggPrototype#isEggInnerObject');
static readonly IS_EGG_LIFECYCLE_PROTOTYPE: symbol = Symbol.for('EggPrototype#isEggLifecyclePrototype');
static readonly EGG_LIFECYCLE_PROTOTYPE_METADATA: symbol = Symbol.for('EggPrototype#eggLifecyclePrototype#metadata');

/**
* Mark class is egg object prototype
Expand Down Expand Up @@ -70,6 +74,58 @@ export class PrototypeUtil {
return MetadataUtil.getOwnBooleanMetaData(PrototypeUtil.IS_EGG_OBJECT_MULTI_INSTANCE_PROTOTYPE, clazz);
}

/**
* Mark class is egg inner object prototype
* @param {Function} clazz -
*/
static setIsEggInnerObject(clazz: EggProtoImplClass): void {
MetadataUtil.defineMetaData(PrototypeUtil.IS_EGG_INNER_OBJECT, true, clazz);
}

/**
* If class is egg inner object prototype, return true
* @param {Function} clazz -
*/
static isEggInnerObject(clazz: EggProtoImplClass): boolean {
return MetadataUtil.getOwnBooleanMetaData(PrototypeUtil.IS_EGG_INNER_OBJECT, clazz);
}

/**
* Mark class is egg lifecycle prototype
* @param {Function} clazz -
*/
static setIsEggLifecyclePrototype(clazz: EggProtoImplClass): void {
MetadataUtil.defineMetaData(PrototypeUtil.IS_EGG_LIFECYCLE_PROTOTYPE, true, clazz);
}

/**
* If class is egg lifecycle prototype, return true
* @param {Function} clazz -
*/
static isEggLifecyclePrototype(clazz: EggProtoImplClass): boolean {
return MetadataUtil.getOwnBooleanMetaData(PrototypeUtil.IS_EGG_LIFECYCLE_PROTOTYPE, clazz);
}

/**
* Set egg lifecycle prototype metadata, like the lifecycle type
* @param {Function} clazz -
* @param {EggLifecycleInfo} metadata -
*/
static setEggLifecyclePrototypeMetadata(clazz: EggProtoImplClass, metadata: EggLifecycleInfo): void {
MetadataUtil.defineMetaData(PrototypeUtil.EGG_LIFECYCLE_PROTOTYPE_METADATA, metadata, clazz);
}

/**
* Get egg lifecycle prototype metadata
* @param {Function} clazz -
*/
static getEggLifecyclePrototypeMetadata(clazz: EggProtoImplClass): EggLifecycleInfo | undefined {
if (!PrototypeUtil.isEggLifecyclePrototype(clazz)) {
return undefined;
}
return MetadataUtil.getOwnMetaData<EggLifecycleInfo>(PrototypeUtil.EGG_LIFECYCLE_PROTOTYPE_METADATA, clazz);
}

/**
* Get the type of the egg multi-instance prototype.
* @param {Function} clazz -
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ exports[`should export stable 1`] = `
"ConfigSourceQualifierAttribute": Symbol(Qualifier.ConfigSource),
"ContextProto": [Function],
"DEFAULT_PROTO_IMPL_TYPE": "DEFAULT",
"EGG_INNER_OBJECT_PROTO_IMPL_TYPE": "EGG_INNER_OBJECT_PROTOTYPE",
"EggContextLifecycleProto": [Function],
"EggLifecycleProto": [Function],
"EggObjectLifecycleProto": [Function],
"EggPrototypeLifecycleProto": [Function],
"EggQualifier": [Function],
"EggQualifierAttribute": Symbol(Qualifier.Egg),
"EggType": {
Expand All @@ -30,6 +35,9 @@ exports[`should export stable 1`] = `
"CONSTRUCTOR": "CONSTRUCTOR",
"PROPERTY": "PROPERTY",
},
"InnerObjectProto": [Function],
"LoadUnitInstanceLifecycleProto": [Function],
"LoadUnitLifecycleProto": [Function],
"LoadUnitNameQualifierAttribute": Symbol(Qualifier.LoadUnitName),
"MetadataUtil": [Function],
"ModuleQualifier": [Function],
Expand Down
Loading
Loading