Library for aspect-oriented programming with TypeScript
npm install crosscut.js
Library for aspect-oriented programming with JavaScript, which takes advantage of ECMAScript 2016 decorators syntax.
For further reading on decorators, take a look at the spec.
Blog post, introduction to the AOP and the library could be found here.
makeMethodDecorator() and makeMemberDecorator() functions. Here's an example:``ts
import { makeMethodDecorator } from 'crosscut.js';
export const Log = (message?: string) => {
return makeMethodDecorator(((target: object, propertyKey: string | symbol) => {
Reflect.defineMetadata(Log, { message }, target, propertyKey);
});
};
class LoggerAspect {
@beforeMethod({
decorators: [ Log ],
})
invokeBeforeMethod(meta: Metadata) {
let { message } = Reflect.getMetadata(Log, meta.method.context, meta.method.name);
if (message != null) {
console.log(message);
} else {
console.log(Inside of the logger. Called ${meta.className}.${meta.method.name} with args: ${meta.method.args.join(', ')}.);
}
}
}
class Article {
id: number;
title: string;
content: string;
}
class ArticleCollection {
articles: Article[] = [];
@Log()
getArticle(id: number) {
console.log(Getting article with id: ${id}.);
return this.articles.filter(a => {
return a.id === id;
}).pop();
}
@Log('Log a custom message')
setArticle(article: Article) {
console.log(Setting article with id: ${article.id}.);
this.articles.push(article);
}
}
new ArticleCollection().getArticle(1);
new ArticleCollection().setArticle({
id: 2,
title: 'Current event',
content: '...',
});
// Result:
// Inside of the logger. Called ArticleCollection.getArticle with args: 1.
// Getting article with id: 1.
// Log a custom message
// Setting article with id: 2.
`
To use the aspect.js style of weaving, use the @Wove class decorator.
`ts
import {beforeMethod, Wove, Metadata} from 'crosscut.js';
class LoggerAspect {
@beforeMethod({
classNamePattern: /^Article/,
methodNamePattern: /^(get|set)/
})
invokeBeforeMethod(meta: Metadata) {
// meta.woveMetadata == { bar: 42 }
console.log(Inside of the logger. Called ${meta.className}.${meta.method.name} with args: ${meta.method.args.join(', ')}.);
}
}
class Article {
id: number;
title: string;
content: string;
}
@Wove({ bar: 42 })
class ArticleCollection {
articles: Article[] = [];
getArticle(id: number) {
console.log(Getting article with id: ${id}.);Setting article with id: ${article.id}.
return this.articles.filter(a => {
return a.id === id;
}).pop();
}
setArticle(article: Article) {
console.log();
this.articles.push(article);
}
}
new ArticleCollection().getArticle(1);
// Result:
// Inside of the logger. Called ArticleCollection.getArticle with args: 1.
// Getting article with id: 1.
`
In case you're using crosscut.js in a browser environment the minifier may break the annotated code because of the performed mangling. In order to handle this problem you can use:
`tsInside of the logger. Called ${meta.className}.${meta.method.name} with args: ${meta.method.args.join(', ')}.
class LoggerAspect {
@beforeMethod({
classes: [ArticleCollection],
methods: [ArticleCollection.prototype.getArticle, ArticleCollection.prototype.setArticle]
})
invokeBeforeMethod(meta: Metadata) {
// meta.woveMetadata == { bar: 42 }
console.log();
}
}
class ArticleCollection {
getArticle(id: number) {...}
setArticle(article: Article) {...}
}
`
In this case you can omit the @Wove decorator.
This way, by explicitly listing the classes and methods which should be woven, you can prevent the unwanted effect of mangling.
``
git clone https://github.com/lizardruss/crosscut.js --depth 1
npm install -g ts-node
ts-node demo/index.ts
The library offers the following combinations of advices and joint points:
- beforeMethod(MethodSelector) - invoked before method callafterMethod(MethodSelector)
- - invoked after method callaroundMethod(MethodSelector)
- - invoked around method callonThrowOfMethod(MethodSelector)
- - invoked on throw of method callasyncOnThrowOfMethod(MethodSelector)
- - invoked on throw of async method call
- beforeStaticMethod(MethodSelector) - invoked before static method callafterStaticMethod(MethodSelector)
- - invoked after static method callaroundStaticMethod(MethodSelector)
- - invoked around static method callonThrowOfStaticMethod(MethodSelector)
- - invoked on throw of static method callasyncOnThrowOfStaticMethod(MethodSelector)
- - invoked on throw of async static method call
- beforeSetter(MemberSelector) - invoked before setter callafterSetter(MemberSelector)
- - invoked after setter callaroundSetter(MemberSelector)
- - invoked around setter callonThrowOfSetter(MemberSelector)
- - invoked on throw of setter callasyncOnThrowOfSetter(MemberSelector)
- - invoked on throw of async setter callbeforeGetter(MemberSelector)
- - invoked before getter callafterGetter(MemberSelector)
- - invoked after getter callaroundGetter(MemberSelector)
- - invoked around getter callonThrowOfGetter(MemberSelector)
- - invoked on throw of getter callasyncOnThrowOfGetter(MemberSelector)
- - invoked on throw of async getter call
`ts
export type DecoratorKey = string | symbol | MethodDecorator | MethodDecoratorFactory;
export interface MethodSelector {
classNamePattern?: RegExp;
methodNamePattern?: RegExp;
classes?: Function[];
methods?: Function[];
decorators?: DecoratorKey[];
}
`
`ts
export type DecoratorKey = string | symbol | MethodDecorator | MethodDecoratorFactory;
export interface MemberSelector {
classNamePattern?: RegExp;
fieldNamePattern?: RegExp;
classes?: Function[];
methods?: PropertyDescriptor[];
decorators?: DecoratorKey[];
}
`
`ts`
export class Metadata {
public method: MethodMetadata;
public className: string;
public woveMetadata: any;
}
`ts``
export class MethodMetadata {
public proceed: boolean;
public name: string;
public args: any[];
public context: any;
public result: any;
public exception: any;
public invoke: (...args: any[]) => any;
}
- [x] Tests
- [x] Type annotations and DTS generation
- [x] Aspect factories
- [x] Generic aspects
- [x] Implement the following advices:
- [x] Before
- [x] After
- [x] Throwing
- [x] Returning
- [x] Around
- [x] Implement the following joint points:
- [x] Method execution
- [x] Static method execution
- [x] Field get
- [x] Field set
- [x] Implement selectos
- [x] Class & method selector
- [x] Class & field selector
- [x] Method & field decorator selectors
MIT