Skip to content

Commit

Permalink
refactor: Improve modularity by splitting AutoAspectExecutor
Browse files Browse the repository at this point in the history
  • Loading branch information
WhiteKiwi committed Nov 6, 2023
1 parent b117d82 commit 0340d93
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 58 deletions.
123 changes: 69 additions & 54 deletions src/auto-aspect-executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Injectable, OnModuleInit } from '@nestjs/common';
import { DiscoveryService, MetadataScanner, Reflector } from '@nestjs/core';
import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
import { ASPECT } from './aspect';
import { AnyFunction } from './core/types';
import { AopMetadata } from './core/types';
import { LazyDecorator } from './lazy-decorator';

/**
Expand All @@ -18,6 +18,10 @@ export class AutoAspectExecutor implements OnModuleInit {
) {}

onModuleInit() {
this.bootstrapLazyDecorators();
}

private bootstrapLazyDecorators() {
const controllers = this.discoveryService.getControllers();
const providers = this.discoveryService.getProviders();

Expand All @@ -30,66 +34,77 @@ export class AutoAspectExecutor implements OnModuleInit {
.concat(controllers)
.filter(({ instance }) => instance && Object.getPrototypeOf(instance));

for (const wrapper of instanceWrappers) {
const target = wrapper.isDependencyTreeStatic()
? wrapper.instance
: wrapper.metatype.prototype;
for (const lazyDecorator of lazyDecorators) {
for (const wrapper of instanceWrappers) {
this.applyLazyDecorator(lazyDecorator, wrapper);
}
}
}

// Use scanFromPrototype for support nestjs 8
const methodNames = this.metadataScanner.scanFromPrototype(
target,
wrapper.isDependencyTreeStatic() ? Object.getPrototypeOf(target) : target,
(name) => name,
private applyLazyDecorator(lazyDecorator: LazyDecorator, instanceWrapper: InstanceWrapper<any>) {
const target = instanceWrapper.isDependencyTreeStatic()
? instanceWrapper.instance
: instanceWrapper.metatype.prototype;

// Use scanFromPrototype for support nestjs 8
const methodNames = this.metadataScanner.scanFromPrototype(
target,
instanceWrapper.isDependencyTreeStatic() ? Object.getPrototypeOf(target) : target,
(name) => name,
);

const metadataKey = this.reflector.get(ASPECT, lazyDecorator.constructor);
// instance에 method names 를 순회하면서 lazyDecorator.wrap을 적용함
for (const methodName of methodNames) {
const metadataList: AopMetadata[] = this.reflector.get<AopMetadata[]>(
metadataKey,
target[methodName],
);
if (!metadataList) {
return;
}

for (const methodName of methodNames) {
lazyDecorators.forEach((lazyDecorator) => {
const metadataKey = this.reflector.get(ASPECT, lazyDecorator.constructor);

const metadataList: {
originalFn: AnyFunction;
metadata?: unknown;
aopSymbol: symbol;
}[] = this.reflector.get<
{
originalFn: AnyFunction;
metadata?: unknown;
aopSymbol: symbol;
}[]
>(metadataKey, target[methodName]);
if (!metadataList) {
return;
}

for (const item of metadataList) {
const { originalFn, metadata, aopSymbol } = item;

// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
function wrap(this: object, ...args: unknown[]) {
const cached = self.wrappedMethodCache.get(item);
if (cached) {
return cached.apply(this, args);
}

const wrappedMethod = lazyDecorator.wrap({
instance: this,
methodName,
method: originalFn.bind(this),
metadata,
});
self.wrappedMethodCache.set(this, wrappedMethod);
return wrappedMethod.apply(this, args);
}

target[aopSymbol] ??= {};
target[aopSymbol][methodName] = wrap;
}
});
for (const aopMetadata of metadataList) {
this.wrapMethod({ lazyDecorator, aopMetadata, methodName, target });
}
}
}

private wrapMethod({
lazyDecorator,
aopMetadata,
methodName,
target,
}: {
lazyDecorator: LazyDecorator;
aopMetadata: AopMetadata;
methodName: string;
target: any;
}) {
const { originalFn, metadata, aopSymbol } = aopMetadata;

// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
function wrappedFunction(this: object, ...args: unknown[]) {
const cached = self.wrappedMethodCache.get(aopMetadata);
if (cached) {
return cached.apply(this, args);
}

const wrappedMethod = lazyDecorator.wrap({
instance: this,
methodName,
method: originalFn.bind(this),
metadata,
});
self.wrappedMethodCache.set(this, wrappedMethod);
return wrappedMethod.apply(this, args);
}

target[aopSymbol] ??= {};
target[aopSymbol][methodName] = wrappedFunction;
}

private lookupLazyDecorators(providers: InstanceWrapper[]): LazyDecorator[] {
const { reflector } = this;

Expand Down
6 changes: 6 additions & 0 deletions src/core/types.ts
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
export type AnyFunction = (...args: unknown[]) => unknown;

export type AopMetadata = {
originalFn: AnyFunction;
metadata?: unknown;
aopSymbol: symbol;
};
6 changes: 2 additions & 4 deletions src/create-decorator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { applyDecorators } from '@nestjs/common';
import { AopMetadata } from './core/types';
import { AddMetadata } from './utils';

/**
Expand All @@ -13,10 +14,7 @@ export const createDecorator = (
return applyDecorators(
// 1. Add metadata to the method
(target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
return AddMetadata<
symbol | string,
{ metadata?: unknown; aopSymbol: symbol; originalFn: unknown }
>(metadataKey, {
return AddMetadata<symbol | string, AopMetadata>(metadataKey, {
originalFn: descriptor.value,
metadata,
aopSymbol,
Expand Down

0 comments on commit 0340d93

Please sign in to comment.