crosscut.js v0.9.4
Introduction
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.
Sample usage (Custom Decorators)
To use advice using custom decorators, you can use the makeMethodDecorator()
and makeMemberDecorator()
functions. Here's an example:
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.
Sample usage (@Wove)
To use the aspect.js style of weaving, use the @Wove class decorator.
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}.`);
return this.articles.filter(a => {
return a.id === id;
}).pop();
}
setArticle(article: Article) {
console.log(`Setting article with id: ${article.id}.`);
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:
class LoggerAspect {
@beforeMethod({
classes: [ArticleCollection],
methods: [ArticleCollection.prototype.getArticle, ArticleCollection.prototype.setArticle]
})
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 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.
Demo
git clone https://github.com/lizardruss/crosscut.js --depth 1
npm install -g ts-node
ts-node demo/index.ts
API
The library offers the following combinations of advices and joint points:
Method calls
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
Static method calls
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
Accessors
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
MethodSelector
export type DecoratorKey = string | symbol | MethodDecorator | MethodDecoratorFactory;
export interface MethodSelector {
classNamePattern?: RegExp;
methodNamePattern?: RegExp;
classes?: Function[];
methods?: Function[];
decorators?: DecoratorKey[];
}
MemberSelector
export type DecoratorKey = string | symbol | MethodDecorator | MethodDecoratorFactory;
export interface MemberSelector {
classNamePattern?: RegExp;
fieldNamePattern?: RegExp;
classes?: Function[];
methods?: PropertyDescriptor[];
decorators?: DecoratorKey[];
}
Metadata
export class Metadata {
public method: MethodMetadata;
public className: string;
public woveMetadata: any;
}
MethodMetadata
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;
}
Roadmap
- Tests
- Type annotations and DTS generation
- Aspect factories
- Generic aspects
- Implement the following advices:
- Before
- After
- Throwing
- Returning
- Around
- Implement the following joint points:
- Method execution
- Static method execution
- Field get
- Field set
- Implement selectos
- Class & method selector
- Class & field selector
- Method & field decorator selectors
License
MIT