@loopback/metadata
Advanced tools
Comparing version 4.0.0-alpha.2 to 4.0.0-alpha.3
@@ -6,2 +6,19 @@ # Change Log | ||
<a name="4.0.0-alpha.3"></a> | ||
# [4.0.0-alpha.3](https://github.com/strongloop/loopback-next/compare/@loopback/metadata@4.0.0-alpha.2...@loopback/metadata@4.0.0-alpha.3) (2017-12-21) | ||
### Bug Fixes | ||
* **metadata:** fix method-level parameter decorators ([c5127d4](https://github.com/strongloop/loopback-next/commit/c5127d4)) | ||
* **metadata:** keep class prototypes untouched during clone ([195e421](https://github.com/strongloop/loopback-next/commit/195e421)) | ||
### Features | ||
* **metadata:** Add a flag to keep args of decorators safe ([3782192](https://github.com/strongloop/loopback-next/commit/3782192)) | ||
<a name="4.0.0-alpha.2"></a> | ||
@@ -8,0 +25,0 @@ # 4.0.0-alpha.2 (2017-12-15) |
@@ -12,5 +12,14 @@ /** | ||
/** | ||
* Inheritance will be honored | ||
* Controls if inherited metadata will be honored. Default to `true`. | ||
*/ | ||
allowsInheritance?: boolean; | ||
allowInheritance?: boolean; | ||
/** | ||
* Controls if the value of `spec` argument will be cloned. Sometimes we | ||
* use shared spec for the decoration, but the decorator function might need | ||
* to mutate the object. Cloning the input spec makes it safe to use the same | ||
* spec (`template`) to decorate different members. | ||
* | ||
* Default to `true`. | ||
*/ | ||
cloneInputSpec?: boolean; | ||
[name: string]: any; | ||
@@ -217,5 +226,10 @@ } | ||
export declare class MethodParameterDecoratorFactory<T> extends DecoratorFactory<T, MetadataMap<T[]>, MethodDecorator> { | ||
protected mergeWithInherited(inheritedMetadata: MetadataMap<T[]>, target: Object, methodName?: string | symbol, methodDescriptor?: TypedPropertyDescriptor<any> | number): { | ||
[x: string]: T[]; | ||
}; | ||
/** | ||
* Find the corresponding parameter index for the decoration | ||
* @param target | ||
* @param methodName | ||
* @param methodDescriptor | ||
*/ | ||
private getParameterIndex(target, methodName?, methodDescriptor?); | ||
protected mergeWithInherited(inheritedMetadata: MetadataMap<T[]>, target: Object, methodName?: string | symbol, methodDescriptor?: TypedPropertyDescriptor<any> | number): MetadataMap<T[]>; | ||
protected mergeWithOwn(ownMetadata: MetadataMap<T[]>, target: Object, methodName?: string | symbol, methodDescriptor?: TypedPropertyDescriptor<any> | number): MetadataMap<T[]>; | ||
@@ -222,0 +236,0 @@ create(): MethodDecorator; |
@@ -45,6 +45,9 @@ "use strict"; | ||
this.options = options; | ||
this.options = Object.assign({ allowInheritance: true }, options); | ||
this.options = Object.assign({ allowInheritance: true, cloneInputSpec: true }, options); | ||
if (this.options.cloneInputSpec) { | ||
this.spec = DecoratorFactory.cloneDeep(spec); | ||
} | ||
} | ||
allowInheritance() { | ||
return this.options && this.options.allowInheritance; | ||
return !!(this.options && this.options.allowInheritance); | ||
} | ||
@@ -229,5 +232,4 @@ /** | ||
static cloneDeep(val) { | ||
if (val === undefined) { | ||
return {}; | ||
} | ||
if (typeof val !== 'object') | ||
return val; | ||
return _.cloneDeepWith(val, v => { | ||
@@ -237,2 +239,8 @@ // Do not clone functions | ||
return v; | ||
if (v && | ||
typeof v.constructor === 'function' && | ||
v.constructor.prototype === v) { | ||
// Do not clone class prototype | ||
return v; | ||
} | ||
return undefined; | ||
@@ -280,2 +288,3 @@ }); | ||
mergeWithInherited(inheritedMetadata, target, propertyName, descriptorOrIndex) { | ||
inheritedMetadata = inheritedMetadata || {}; | ||
const propertyMeta = this.withTarget(this.inherit(inheritedMetadata[propertyName]), target); | ||
@@ -286,4 +295,3 @@ inheritedMetadata[propertyName] = propertyMeta; | ||
mergeWithOwn(ownMetadata, target, propertyName, descriptorOrParameterIndex) { | ||
if (ownMetadata == null) | ||
ownMetadata = {}; | ||
ownMetadata = ownMetadata || {}; | ||
if (ownMetadata[propertyName] != null) { | ||
@@ -315,2 +323,3 @@ const targetName = this.getTargetName(target, propertyName); | ||
mergeWithInherited(inheritedMetadata, target, methodName, methodDescriptor) { | ||
inheritedMetadata = inheritedMetadata || {}; | ||
const methodMeta = this.withTarget(this.inherit(inheritedMetadata[methodName]), target); | ||
@@ -321,4 +330,3 @@ inheritedMetadata[methodName] = methodMeta; | ||
mergeWithOwn(ownMetadata, target, methodName, methodDescriptor) { | ||
if (ownMetadata == null) | ||
ownMetadata = {}; | ||
ownMetadata = ownMetadata || {}; | ||
const methodMeta = ownMetadata[methodName]; | ||
@@ -362,2 +370,3 @@ if (this.getTarget(methodMeta) === target) { | ||
mergeWithInherited(inheritedMetadata, target, methodName, parameterIndex) { | ||
inheritedMetadata = inheritedMetadata || {}; | ||
const methodMeta = this.getOrInitMetadata(inheritedMetadata, target, methodName); | ||
@@ -369,4 +378,3 @@ const index = parameterIndex; | ||
mergeWithOwn(ownMetadata, target, methodName, parameterIndex) { | ||
if (ownMetadata == null) | ||
ownMetadata = {}; | ||
ownMetadata = ownMetadata || {}; | ||
// Find the method metadata | ||
@@ -409,11 +417,43 @@ const methodMeta = this.getOrInitMetadata(ownMetadata, target, methodName); | ||
class MethodParameterDecoratorFactory extends DecoratorFactory { | ||
/** | ||
* Find the corresponding parameter index for the decoration | ||
* @param target | ||
* @param methodName | ||
* @param methodDescriptor | ||
*/ | ||
getParameterIndex(target, methodName, methodDescriptor) { | ||
const numOfParams = this.getNumberOfParameters(target, methodName); | ||
// Fetch the cached parameter index | ||
let index = reflect_1.Reflector.getOwnMetadata(this.key + ':index', target, methodName); | ||
// Default to the last parameter | ||
if (index == null) | ||
index = numOfParams - 1; | ||
if (index < 0) { | ||
// Excessive decorations than the number of parameters detected | ||
const method = this.getTargetName(target, methodName, methodDescriptor); | ||
throw new Error(`The decorator is used more than ${numOfParams} time(s) on ${method}`); | ||
} | ||
return index; | ||
} | ||
mergeWithInherited(inheritedMetadata, target, methodName, methodDescriptor) { | ||
return { [methodName]: [this.spec] }; | ||
inheritedMetadata = inheritedMetadata || {}; | ||
const index = this.getParameterIndex(target, methodName, methodDescriptor); | ||
const inheritedParams = inheritedMetadata[methodName] || new Array(index + 1).fill(undefined); | ||
if (inheritedParams.length) { | ||
// First time applied to a method. This is the last parameter of the method | ||
inheritedParams[index] = this.withTarget(this.inherit(inheritedParams[index]), target); | ||
} | ||
// Cache the index to help us position the next parameter | ||
reflect_1.Reflector.defineMetadata(this.key + ':index', index - 1, target, methodName); | ||
inheritedMetadata[methodName] = inheritedParams; | ||
return inheritedMetadata; | ||
} | ||
mergeWithOwn(ownMetadata, target, methodName, methodDescriptor) { | ||
if (ownMetadata == null) | ||
ownMetadata = {}; | ||
let params = ownMetadata[methodName]; | ||
params = [this.spec].concat(params); | ||
ownMetadata = ownMetadata || {}; | ||
const index = this.getParameterIndex(target, methodName, methodDescriptor); | ||
let params = ownMetadata[methodName] || new Array(index + 1).fill(undefined); | ||
params[index] = this.withTarget(this.inherit(params[index]), target); | ||
ownMetadata[methodName] = params; | ||
// Cache the index to help us position the next parameter | ||
reflect_1.Reflector.defineMetadata(this.key + ':index', index - 1, target, methodName); | ||
return ownMetadata; | ||
@@ -420,0 +460,0 @@ } |
@@ -12,5 +12,14 @@ /** | ||
/** | ||
* Inheritance will be honored | ||
* Controls if inherited metadata will be honored. Default to `true`. | ||
*/ | ||
allowsInheritance?: boolean; | ||
allowInheritance?: boolean; | ||
/** | ||
* Controls if the value of `spec` argument will be cloned. Sometimes we | ||
* use shared spec for the decoration, but the decorator function might need | ||
* to mutate the object. Cloning the input spec makes it safe to use the same | ||
* spec (`template`) to decorate different members. | ||
* | ||
* Default to `true`. | ||
*/ | ||
cloneInputSpec?: boolean; | ||
[name: string]: any; | ||
@@ -217,5 +226,10 @@ } | ||
export declare class MethodParameterDecoratorFactory<T> extends DecoratorFactory<T, MetadataMap<T[]>, MethodDecorator> { | ||
protected mergeWithInherited(inheritedMetadata: MetadataMap<T[]>, target: Object, methodName?: string | symbol, methodDescriptor?: TypedPropertyDescriptor<any> | number): { | ||
[x: string]: T[]; | ||
}; | ||
/** | ||
* Find the corresponding parameter index for the decoration | ||
* @param target | ||
* @param methodName | ||
* @param methodDescriptor | ||
*/ | ||
private getParameterIndex(target, methodName?, methodDescriptor?); | ||
protected mergeWithInherited(inheritedMetadata: MetadataMap<T[]>, target: Object, methodName?: string | symbol, methodDescriptor?: TypedPropertyDescriptor<any> | number): MetadataMap<T[]>; | ||
protected mergeWithOwn(ownMetadata: MetadataMap<T[]>, target: Object, methodName?: string | symbol, methodDescriptor?: TypedPropertyDescriptor<any> | number): MetadataMap<T[]>; | ||
@@ -222,0 +236,0 @@ create(): MethodDecorator; |
@@ -45,6 +45,9 @@ "use strict"; | ||
this.options = options; | ||
this.options = Object.assign({ allowInheritance: true }, options); | ||
this.options = Object.assign({ allowInheritance: true, cloneInputSpec: true }, options); | ||
if (this.options.cloneInputSpec) { | ||
this.spec = DecoratorFactory.cloneDeep(spec); | ||
} | ||
} | ||
allowInheritance() { | ||
return this.options && this.options.allowInheritance; | ||
return !!(this.options && this.options.allowInheritance); | ||
} | ||
@@ -229,5 +232,4 @@ /** | ||
static cloneDeep(val) { | ||
if (val === undefined) { | ||
return {}; | ||
} | ||
if (typeof val !== 'object') | ||
return val; | ||
return _.cloneDeepWith(val, v => { | ||
@@ -237,2 +239,8 @@ // Do not clone functions | ||
return v; | ||
if (v && | ||
typeof v.constructor === 'function' && | ||
v.constructor.prototype === v) { | ||
// Do not clone class prototype | ||
return v; | ||
} | ||
return undefined; | ||
@@ -280,2 +288,3 @@ }); | ||
mergeWithInherited(inheritedMetadata, target, propertyName, descriptorOrIndex) { | ||
inheritedMetadata = inheritedMetadata || {}; | ||
const propertyMeta = this.withTarget(this.inherit(inheritedMetadata[propertyName]), target); | ||
@@ -286,4 +295,3 @@ inheritedMetadata[propertyName] = propertyMeta; | ||
mergeWithOwn(ownMetadata, target, propertyName, descriptorOrParameterIndex) { | ||
if (ownMetadata == null) | ||
ownMetadata = {}; | ||
ownMetadata = ownMetadata || {}; | ||
if (ownMetadata[propertyName] != null) { | ||
@@ -315,2 +323,3 @@ const targetName = this.getTargetName(target, propertyName); | ||
mergeWithInherited(inheritedMetadata, target, methodName, methodDescriptor) { | ||
inheritedMetadata = inheritedMetadata || {}; | ||
const methodMeta = this.withTarget(this.inherit(inheritedMetadata[methodName]), target); | ||
@@ -321,4 +330,3 @@ inheritedMetadata[methodName] = methodMeta; | ||
mergeWithOwn(ownMetadata, target, methodName, methodDescriptor) { | ||
if (ownMetadata == null) | ||
ownMetadata = {}; | ||
ownMetadata = ownMetadata || {}; | ||
const methodMeta = ownMetadata[methodName]; | ||
@@ -362,2 +370,3 @@ if (this.getTarget(methodMeta) === target) { | ||
mergeWithInherited(inheritedMetadata, target, methodName, parameterIndex) { | ||
inheritedMetadata = inheritedMetadata || {}; | ||
const methodMeta = this.getOrInitMetadata(inheritedMetadata, target, methodName); | ||
@@ -369,4 +378,3 @@ const index = parameterIndex; | ||
mergeWithOwn(ownMetadata, target, methodName, parameterIndex) { | ||
if (ownMetadata == null) | ||
ownMetadata = {}; | ||
ownMetadata = ownMetadata || {}; | ||
// Find the method metadata | ||
@@ -409,11 +417,43 @@ const methodMeta = this.getOrInitMetadata(ownMetadata, target, methodName); | ||
class MethodParameterDecoratorFactory extends DecoratorFactory { | ||
/** | ||
* Find the corresponding parameter index for the decoration | ||
* @param target | ||
* @param methodName | ||
* @param methodDescriptor | ||
*/ | ||
getParameterIndex(target, methodName, methodDescriptor) { | ||
const numOfParams = this.getNumberOfParameters(target, methodName); | ||
// Fetch the cached parameter index | ||
let index = reflect_1.Reflector.getOwnMetadata(this.key + ':index', target, methodName); | ||
// Default to the last parameter | ||
if (index == null) | ||
index = numOfParams - 1; | ||
if (index < 0) { | ||
// Excessive decorations than the number of parameters detected | ||
const method = this.getTargetName(target, methodName, methodDescriptor); | ||
throw new Error(`The decorator is used more than ${numOfParams} time(s) on ${method}`); | ||
} | ||
return index; | ||
} | ||
mergeWithInherited(inheritedMetadata, target, methodName, methodDescriptor) { | ||
return { [methodName]: [this.spec] }; | ||
inheritedMetadata = inheritedMetadata || {}; | ||
const index = this.getParameterIndex(target, methodName, methodDescriptor); | ||
const inheritedParams = inheritedMetadata[methodName] || new Array(index + 1).fill(undefined); | ||
if (inheritedParams.length) { | ||
// First time applied to a method. This is the last parameter of the method | ||
inheritedParams[index] = this.withTarget(this.inherit(inheritedParams[index]), target); | ||
} | ||
// Cache the index to help us position the next parameter | ||
reflect_1.Reflector.defineMetadata(this.key + ':index', index - 1, target, methodName); | ||
inheritedMetadata[methodName] = inheritedParams; | ||
return inheritedMetadata; | ||
} | ||
mergeWithOwn(ownMetadata, target, methodName, methodDescriptor) { | ||
if (ownMetadata == null) | ||
ownMetadata = {}; | ||
let params = ownMetadata[methodName]; | ||
params = [this.spec].concat(params); | ||
ownMetadata = ownMetadata || {}; | ||
const index = this.getParameterIndex(target, methodName, methodDescriptor); | ||
let params = ownMetadata[methodName] || new Array(index + 1).fill(undefined); | ||
params[index] = this.withTarget(this.inherit(params[index]), target); | ||
ownMetadata[methodName] = params; | ||
// Cache the index to help us position the next parameter | ||
reflect_1.Reflector.defineMetadata(this.key + ':index', index - 1, target, methodName); | ||
return ownMetadata; | ||
@@ -420,0 +460,0 @@ } |
{ | ||
"name": "@loopback/metadata", | ||
"version": "4.0.0-alpha.2", | ||
"version": "4.0.0-alpha.3", | ||
"description": "LoopBack's metadata utilities for reflection and decoration", | ||
@@ -15,3 +15,3 @@ "engines": { | ||
"build:apidocs": "lb-apidocs", | ||
"clean": "rm -rf loopback-metadata*.tgz dist* package", | ||
"clean": "lb-clean loopback-metadata*.tgz dist dist6 package api-docs", | ||
"prepare": "npm run build && npm run build:apidocs", | ||
@@ -32,3 +32,3 @@ "pretest": "npm run build:current", | ||
"@loopback/build": "^4.0.0-alpha.6", | ||
"@loopback/testlab": "^4.0.0-alpha.15", | ||
"@loopback/testlab": "^4.0.0-alpha.17", | ||
"@types/debug": "0.0.30", | ||
@@ -35,0 +35,0 @@ "@types/lodash": "^4.14.87" |
@@ -207,2 +207,13 @@ # @loopback/metadata | ||
## Decorator options | ||
An object of type `DecoratorOptions` can be passed in to create decorator | ||
functions. There are two flags for the options: | ||
- allowInheritance: Controls if inherited metadata will be honored. Default to `true`. | ||
- cloneInputSpec: Controls if the value of `spec` argument will be cloned. | ||
Sometimes we use shared spec for the decoration, but the decorator function | ||
might need to mutate the object. Cloning the input spec makes it safe to use | ||
the same spec (`template`) to decorate different members. Default to `true`. | ||
## Customize inheritance of metadata | ||
@@ -209,0 +220,0 @@ |
@@ -25,5 +25,14 @@ // Copyright IBM Corp. 2013,2017. All Rights Reserved. | ||
/** | ||
* Inheritance will be honored | ||
* Controls if inherited metadata will be honored. Default to `true`. | ||
*/ | ||
allowsInheritance?: boolean; | ||
allowInheritance?: boolean; | ||
/** | ||
* Controls if the value of `spec` argument will be cloned. Sometimes we | ||
* use shared spec for the decoration, but the decorator function might need | ||
* to mutate the object. Cloning the input spec makes it safe to use the same | ||
* spec (`template`) to decorate different members. | ||
* | ||
* Default to `true`. | ||
*/ | ||
cloneInputSpec?: boolean; | ||
[name: string]: any; | ||
@@ -85,7 +94,13 @@ } | ||
) { | ||
this.options = Object.assign({allowInheritance: true}, options); | ||
this.options = Object.assign( | ||
{allowInheritance: true, cloneInputSpec: true}, | ||
options, | ||
); | ||
if (this.options.cloneInputSpec) { | ||
this.spec = DecoratorFactory.cloneDeep(spec); | ||
} | ||
} | ||
protected allowInheritance(): boolean { | ||
return this.options && this.options.allowInheritance; | ||
return !!(this.options && this.options.allowInheritance); | ||
} | ||
@@ -296,8 +311,14 @@ | ||
static cloneDeep<T>(val: T): T { | ||
if (val === undefined) { | ||
return {} as T; | ||
} | ||
if (typeof val !== 'object') return val; | ||
return _.cloneDeepWith(val, v => { | ||
// Do not clone functions | ||
if (typeof v === 'function') return v; | ||
if ( | ||
v && | ||
typeof v.constructor === 'function' && | ||
v.constructor.prototype === v | ||
) { | ||
// Do not clone class prototype | ||
return v; | ||
} | ||
return undefined; | ||
@@ -369,2 +390,3 @@ }); | ||
) { | ||
inheritedMetadata = inheritedMetadata || {}; | ||
const propertyMeta: T = this.withTarget( | ||
@@ -384,3 +406,3 @@ <T>this.inherit(inheritedMetadata[propertyName!]), | ||
) { | ||
if (ownMetadata == null) ownMetadata = {}; | ||
ownMetadata = ownMetadata || {}; | ||
if (ownMetadata[propertyName!] != null) { | ||
@@ -430,2 +452,3 @@ const targetName = this.getTargetName(target, propertyName); | ||
) { | ||
inheritedMetadata = inheritedMetadata || {}; | ||
const methodMeta = this.withTarget( | ||
@@ -445,3 +468,3 @@ <T>this.inherit(inheritedMetadata[methodName!]), | ||
) { | ||
if (ownMetadata == null) ownMetadata = {}; | ||
ownMetadata = ownMetadata || {}; | ||
const methodMeta = ownMetadata[methodName!]; | ||
@@ -513,2 +536,3 @@ if (this.getTarget(methodMeta) === target) { | ||
) { | ||
inheritedMetadata = inheritedMetadata || {}; | ||
const methodMeta = this.getOrInitMetadata( | ||
@@ -533,3 +557,3 @@ inheritedMetadata, | ||
) { | ||
if (ownMetadata == null) ownMetadata = {}; | ||
ownMetadata = ownMetadata || {}; | ||
// Find the method metadata | ||
@@ -591,2 +615,32 @@ const methodMeta = this.getOrInitMetadata(ownMetadata, target, methodName); | ||
> { | ||
/** | ||
* Find the corresponding parameter index for the decoration | ||
* @param target | ||
* @param methodName | ||
* @param methodDescriptor | ||
*/ | ||
private getParameterIndex( | ||
target: Object, | ||
methodName?: string | symbol, | ||
methodDescriptor?: TypedPropertyDescriptor<any> | number, | ||
) { | ||
const numOfParams = this.getNumberOfParameters(target, methodName); | ||
// Fetch the cached parameter index | ||
let index = Reflector.getOwnMetadata( | ||
this.key + ':index', | ||
target, | ||
methodName, | ||
); | ||
// Default to the last parameter | ||
if (index == null) index = numOfParams - 1; | ||
if (index < 0) { | ||
// Excessive decorations than the number of parameters detected | ||
const method = this.getTargetName(target, methodName, methodDescriptor); | ||
throw new Error( | ||
`The decorator is used more than ${numOfParams} time(s) on ${method}`, | ||
); | ||
} | ||
return index; | ||
} | ||
protected mergeWithInherited( | ||
@@ -598,3 +652,23 @@ inheritedMetadata: MetadataMap<T[]>, | ||
) { | ||
return {[methodName!]: [this.spec]}; | ||
inheritedMetadata = inheritedMetadata || {}; | ||
const index = this.getParameterIndex(target, methodName, methodDescriptor); | ||
const inheritedParams = | ||
inheritedMetadata[methodName!] || new Array(index + 1).fill(undefined); | ||
if (inheritedParams.length) { | ||
// First time applied to a method. This is the last parameter of the method | ||
inheritedParams[index] = this.withTarget( | ||
<T>this.inherit(inheritedParams[index]), | ||
target, | ||
); | ||
} | ||
// Cache the index to help us position the next parameter | ||
Reflector.defineMetadata( | ||
this.key + ':index', | ||
index - 1, | ||
target, | ||
methodName, | ||
); | ||
inheritedMetadata[methodName!] = inheritedParams; | ||
return inheritedMetadata; | ||
} | ||
@@ -608,6 +682,16 @@ | ||
) { | ||
if (ownMetadata == null) ownMetadata = {}; | ||
let params = ownMetadata[methodName!]; | ||
params = [this.spec].concat(params); | ||
ownMetadata = ownMetadata || {}; | ||
const index = this.getParameterIndex(target, methodName, methodDescriptor); | ||
let params = | ||
ownMetadata[methodName!] || new Array(index + 1).fill(undefined); | ||
params[index] = this.withTarget(<T>this.inherit(params[index]), target); | ||
ownMetadata[methodName!] = params; | ||
// Cache the index to help us position the next parameter | ||
Reflector.defineMetadata( | ||
this.key + ':index', | ||
index - 1, | ||
target, | ||
methodName, | ||
); | ||
return ownMetadata; | ||
@@ -614,0 +698,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
679134
6969
461