141 lines
3.0 KiB
JavaScript
Vendored
141 lines
3.0 KiB
JavaScript
Vendored
const symbolAdvices = Symbol('Aspect-Advices')
|
|
const mapTargets = new WeakMap()
|
|
const symbolDummy = Symbol('Aspect-dummy')
|
|
|
|
/**
|
|
* Aspect
|
|
* @property {Proxy} proxy
|
|
*/
|
|
class Aspect {
|
|
constructor (pointcut, target) {
|
|
if (this.proxy) return this
|
|
|
|
if (mapTargets.has(target.prototype)) {
|
|
return mapTargets.get(target.prototype)
|
|
}
|
|
|
|
const that = this
|
|
this.pointcut = pointcut
|
|
this.target = target
|
|
if (!this[symbolAdvices]) {
|
|
this[symbolAdvices] = new Map()
|
|
}
|
|
|
|
this.proxy = new Proxy(target, {
|
|
apply (target, thisArg, params) {
|
|
let result = symbolDummy
|
|
const adviceArguments = { params, result }
|
|
that.applyTarget('before', thisArg, adviceArguments)
|
|
|
|
try {
|
|
result = Reflect.apply(target, thisArg, adviceArguments.params)
|
|
|
|
if (result) {
|
|
adviceArguments.result = result
|
|
that.applyTarget('afterReturn', thisArg, adviceArguments)
|
|
|
|
if (result instanceof Promise) {
|
|
result.catch((error) => {
|
|
throw (error)
|
|
})
|
|
}
|
|
}
|
|
} catch (error) {
|
|
that.applyTarget('thrown', thisArg, adviceArguments)
|
|
}
|
|
|
|
that.applyTarget('after', thisArg, adviceArguments)
|
|
|
|
if (adviceArguments.result !== symbolDummy) {
|
|
return adviceArguments.result
|
|
}
|
|
}
|
|
})
|
|
|
|
mapTargets.set(target.prototype, this)
|
|
|
|
return this
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} pointcut
|
|
* @param {function} target
|
|
*/
|
|
static aspect (pointcut, target) {
|
|
if (mapTargets.has(target.prototype)) {
|
|
return mapTargets.get(target.prototype)
|
|
}
|
|
|
|
return new Aspect(pointcut, target)
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} name
|
|
* @param {function} advice
|
|
* @return {Aspect}
|
|
*/
|
|
before (name, advice) {
|
|
this.registerAdvice('before', name, advice)
|
|
|
|
return this
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} name
|
|
* @param {function} advice
|
|
* @return {Aspect}
|
|
*/
|
|
after (name, advice) {
|
|
this.registerAdvice('after', name, advice)
|
|
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* @param {string} position
|
|
* @param {object} thisArg
|
|
* @param {object} adviceArguments
|
|
*/
|
|
applyTarget (position, thisArg, adviceArguments) {
|
|
if (this[symbolAdvices].has(position)) {
|
|
let advices = this[symbolAdvices].get(position)
|
|
|
|
advices.forEach((advice) => {
|
|
advice(adviceArguments)
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} position
|
|
* @param {*} name
|
|
* @return {function} advice
|
|
*/
|
|
getAdvice (position, name) {
|
|
return this[symbolAdvices].get(position).get(name)
|
|
}
|
|
|
|
/**
|
|
* @param {string} position
|
|
* @param {*} name
|
|
* @param {*} advice
|
|
*/
|
|
registerAdvice (position, name, advice) {
|
|
if (!this[symbolAdvices].has(position)) {
|
|
this[symbolAdvices].set(position, new Map())
|
|
}
|
|
|
|
const mapPosition = this[symbolAdvices].get(position)
|
|
|
|
if (!mapPosition.has(name)) {
|
|
mapPosition.set(name, advice)
|
|
}
|
|
}
|
|
}
|
|
|
|
export default Aspect
|