Inspired by the AspectJ java framework, AspectJS leverages ES Decorators to bring Aspect Oriented Programming to Javascript and Typescript.
See the demo on stackblitz.
ECMAScript Decorators are fantastic: they allow developers to hide boilerplate code behind a simple @
sign, keeping the code clean, easy to read, and easy to write. Widely used in popular projects such as Angular, Nest.js or TypeORM, decorators have one major drawback: they come with their own built-in behavior, making interoperability between tools difficult, and preventing them from being repurposed for other uses.
AspectJS takes a different approach, by introducing two important concepts: Annotations and Aspects.
- An Annotation is essentially an empty decorator that marks a target (class, property, method or parameter) as a candidate for further enhancements.
- Aspects can be selectively enabled to introduce new behaviors into the annotated elements.
-
without aspects
const A = function( target: Function) { // behaviorA } const B = function( target: Function) { // behaviorB } @A() @B() class MyClass {}
-
with aspects
const af = new AnnotationFactory('my.org') const A = af.create('A'); const B = af.create('B'); @A() @B() class MyClass {} @Aspect() class AB_Aspect { // behavior A // behavior B } getWeaver().enable(new AB_Aspect())
-
Install the packages
npm i @aspectjs/core @aspectjs/common
-
Create an annotation:
// toasted.annotation.ts import { AnnotationFactory, AnnotationKind } from '@aspectjs/common'; const ANNOTATION_FACTORY = new AnnotationFactory('demo'); const Toasted = ANNOTATION_FACTORY.create( AnnotationKind.METHOD, 'Toasted', function Toasted() {}, );
-
Use that annotation on a class, a property, a method or a parameter:
// main.ts class Main { @Toasted() run() { console.log('run'); } }
-
Declare an aspect triggered by the annotation:
// toasted.aspect.ts import { Around, AroundContext, Aspect, JoinPoint, on } from '@aspectjs/core'; @Aspect() class ToastedAspect { @Around(on.methods.withAnnotations(Toasted)) toast(ctxt: AroundContext, jp: JoinPoint, jpArgs: unknown[]) { const result = jp(...jpArgs); const text = `${ctxt.target.label} completed successfully`; showToast(text); return result; } }
-
Enable the aspect
// aop.ts import { getWeaver } from '@aspectjs/core'; getWeaver().enable(new ToastedAspect());
For more advanced usage, please read the documentation: https://aspectjs.gitlab.io/.
MIT Licensed