@loopback/metadata
Advanced tools
Comparing version 0.3.1 to 0.4.0
@@ -6,2 +6,21 @@ # Change Log | ||
<a name="0.4.0"></a> | ||
# [0.4.0](https://github.com/strongloop/loopback-next/compare/@loopback/metadata@0.3.1...@loopback/metadata@0.4.0) (2018-03-29) | ||
### Bug Fixes | ||
* **metadata:** refine clone of decoration spec ([544052e](https://github.com/strongloop/loopback-next/commit/544052e)) | ||
### BREAKING CHANGES | ||
* **metadata:** instances of user-defined classes are not cloned any more. | ||
See https://github.com/strongloop/loopback-next/issues/1182. The root | ||
cause is that DataSource instances are cloned incorrectly. | ||
<a name="0.3.1"></a> | ||
@@ -8,0 +27,0 @@ ## [0.3.1](https://github.com/strongloop/loopback-next/compare/@loopback/metadata@0.3.0...@loopback/metadata@0.3.1) (2018-03-23) |
@@ -160,3 +160,4 @@ /** | ||
protected static _createDecorator<T, M extends T | MetadataMap<T> | MetadataMap<T[]>, D extends DecoratorType>(key: string, spec: T, options?: DecoratorOptions): D; | ||
static cloneDeep<T>(val: T): T; | ||
private static _cloneableTypes; | ||
static cloneDeep<V>(val: Readonly<V>): V; | ||
} | ||
@@ -163,0 +164,0 @@ /** |
@@ -140,2 +140,4 @@ "use strict"; | ||
enumerable: false, | ||
// Make sure it won't be redefined on the same object | ||
configurable: false, | ||
}); | ||
@@ -240,10 +242,10 @@ } | ||
return _.cloneDeepWith(val, v => { | ||
// Do not clone functions | ||
if (typeof v === 'function') | ||
if (typeof v !== 'object') | ||
return v; | ||
if (v && | ||
typeof v.constructor === 'function' && | ||
v.constructor.prototype === v) { | ||
// Do not clone class prototype | ||
if (v == null) | ||
return v; | ||
if (v.constructor != null && | ||
!DecoratorFactory._cloneableTypes.includes(v.constructor)) { | ||
// Do not clone instances of classes/constructors, such as Date | ||
return v; | ||
} | ||
@@ -258,2 +260,22 @@ return undefined; | ||
DecoratorFactory.TARGET = '__decoratorTarget'; | ||
// See https://github.com/lodash/lodash/blob/master/.internal/baseClone.js | ||
DecoratorFactory._cloneableTypes = [ | ||
Object, | ||
Array, | ||
Set, | ||
Map, | ||
RegExp, | ||
Date, | ||
Buffer, | ||
ArrayBuffer, | ||
Float32Array, | ||
Float64Array, | ||
Int8Array, | ||
Int16Array, | ||
Int32Array, | ||
Uint8Array, | ||
Uint8ClampedArray, | ||
Uint16Array, | ||
Uint32Array, | ||
]; | ||
exports.DecoratorFactory = DecoratorFactory; | ||
@@ -260,0 +282,0 @@ /** |
{ | ||
"name": "@loopback/metadata", | ||
"version": "0.3.1", | ||
"version": "0.4.0", | ||
"description": "LoopBack's metadata utilities for reflection and decoration", | ||
@@ -28,4 +28,4 @@ "engines": { | ||
"devDependencies": { | ||
"@loopback/build": "^0.3.3", | ||
"@loopback/testlab": "^0.4.1", | ||
"@loopback/build": "^0.4.0", | ||
"@loopback/testlab": "^0.5.0", | ||
"@types/debug": "^0.0.30", | ||
@@ -32,0 +32,0 @@ "@types/lodash": "^4.14.106" |
249
README.md
# @loopback/metadata | ||
This module contains utilities to help developers implement [TypeScript decorators](https://www.typescriptlang.org/docs/handbook/decorators.html), define/merge | ||
metadata, and inspect metadata. | ||
This module contains utilities to help developers implement | ||
[TypeScript decorators](https://www.typescriptlang.org/docs/handbook/decorators.html), | ||
define/merge metadata, and inspect metadata. | ||
* Reflector: Wrapper of | ||
- Reflector: Wrapper of | ||
[reflect-metadata](https://github.com/rbuckton/reflect-metadata) | ||
* Decorator factories: A set of factories for class/method/property/parameter | ||
- Decorator factories: A set of factories for class/method/property/parameter | ||
decorators to apply metadata to a given class and its static or instance | ||
members. | ||
* MetadataInspector: High level APIs to inspect a class and/or its members to | ||
- MetadataInspector: High level APIs to inspect a class and/or its members to | ||
get metadata applied by decorators. | ||
@@ -139,3 +140,4 @@ | ||
Now we can use `@myParameterDecorator` to add metadata to a parameter as follows: | ||
Now we can use `@myParameterDecorator` to add metadata to a parameter as | ||
follows: | ||
@@ -187,4 +189,4 @@ ```ts | ||
Now we can use `@myMethodParameterDecorator` to add metadata to a parameter | ||
as follows: | ||
Now we can use `@myMethodParameterDecorator` to add metadata to a parameter as | ||
follows: | ||
@@ -195,6 +197,3 @@ ```ts | ||
@myMethodParameterDecorator({name: 'y'}) | ||
myMethod( | ||
x: number, | ||
y: number, | ||
) {} | ||
myMethod(x: number, y: number) {} | ||
} | ||
@@ -216,7 +215,8 @@ ``` | ||
- allowInheritance: Controls if inherited metadata will be honored. Default to `true`. | ||
- 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`. | ||
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`. | ||
@@ -227,102 +227,102 @@ ### Customize inheritance of metadata | ||
1. If the metadata is an object, we merge the `spec` argument from the decorator | ||
function into the inherited value from base classes. For metadata of array and | ||
other primitive types, the `spec` argument is used if provided. | ||
1. If the metadata is an object, we merge the `spec` argument from the | ||
decorator function into the inherited value from base classes. For metadata | ||
of array and other primitive types, the `spec` argument is used if provided. | ||
- We can override `inherit` method of the decorator factory to customize | ||
how to resolve `spec` against the inherited metadata. For example: | ||
- We can override `inherit` method of the decorator factory to customize how | ||
to resolve `spec` against the inherited metadata. For example: | ||
```ts | ||
protected inherit(inheritedMetadata: T | undefined | null): T { | ||
// Ignore the inherited metadata | ||
return this.spec; | ||
} | ||
``` | ||
```ts | ||
protected inherit(inheritedMetadata: T | undefined | null): T { | ||
// Ignore the inherited metadata | ||
return this.spec; | ||
} | ||
``` | ||
2. Method/property/parameter level metadata is applied to the class or its | ||
prototype as a map keyed method/property names. We think this approach is better | ||
than keeping metadata at method/property level as it's not easy to inspect a | ||
class to find static/instance methods and properties with decorations. The | ||
metadata for a class is illustrated below: | ||
prototype as a map keyed method/property names. We think this approach is | ||
better than keeping metadata at method/property level as it's not easy to | ||
inspect a class to find static/instance methods and properties with | ||
decorations. The metadata for a class is illustrated below: | ||
- MyClass (the constructor function itself) | ||
- MyClass (the constructor function itself) | ||
```ts | ||
{ | ||
// Class level metadata | ||
'my-class-decorator-key': MyClassMetadata, | ||
// Static method (including the constructor) parameter metadata | ||
'my-static-parameter-decorator-key': { | ||
'': [MyConstructorParameterMetadata], // Constructor parameter metadata | ||
'myStaticMethod1': [MyStaticMethodParameterMetadata], | ||
'myStaticMethod2': [MyStaticMethodParameterMetadata], | ||
}, | ||
// Static method metadata | ||
'my-static-method-decorator-key': { | ||
'myStaticMethod1': MyStaticMethodMetadata, | ||
'myStaticMethod2': MyStaticMethodMetadata, | ||
}, | ||
// Static property metadata | ||
'my-static-property-decorator-key': { | ||
'myStaticMethod1': MyStaticPropertyMetadata, | ||
'myStaticMethod1': MyStaticPropertyMetadata, | ||
} | ||
} | ||
``` | ||
```ts | ||
{ | ||
// Class level metadata | ||
'my-class-decorator-key': MyClassMetadata, | ||
// Static method (including the constructor) parameter metadata | ||
'my-static-parameter-decorator-key': { | ||
'': [MyConstructorParameterMetadata], // Constructor parameter metadata | ||
'myStaticMethod1': [MyStaticMethodParameterMetadata], | ||
'myStaticMethod2': [MyStaticMethodParameterMetadata], | ||
}, | ||
// Static method metadata | ||
'my-static-method-decorator-key': { | ||
'myStaticMethod1': MyStaticMethodMetadata, | ||
'myStaticMethod2': MyStaticMethodMetadata, | ||
}, | ||
// Static property metadata | ||
'my-static-property-decorator-key': { | ||
'myStaticMethod1': MyStaticPropertyMetadata, | ||
'myStaticMethod1': MyStaticPropertyMetadata, | ||
} | ||
} | ||
``` | ||
- MyClass.prototype | ||
- MyClass.prototype | ||
```ts | ||
{ | ||
// Instance method parameter metadata | ||
'my-instance-parameter-decorator-key': { | ||
'myMethod1': [MyMethodParameterMetadata], | ||
'myMethod2': [MyMethodParameterMetadata], | ||
}, | ||
// Instance method metadata | ||
'my-instance-method-decorator-key': { | ||
'myMethod1': MyMethodMetadata, | ||
'myMethod2': MyMethodMetadata, | ||
}, | ||
// Instance property metadata | ||
'my-instance-property-decorator-key': { | ||
'myProperty1': MyPropertyMetadata, | ||
'myProperty2': MyPropertyMetadata, | ||
} | ||
} | ||
``` | ||
```ts | ||
{ | ||
// Instance method parameter metadata | ||
'my-instance-parameter-decorator-key': { | ||
'myMethod1': [MyMethodParameterMetadata], | ||
'myMethod2': [MyMethodParameterMetadata], | ||
}, | ||
// Instance method metadata | ||
'my-instance-method-decorator-key': { | ||
'myMethod1': MyMethodMetadata, | ||
'myMethod2': MyMethodMetadata, | ||
}, | ||
// Instance property metadata | ||
'my-instance-property-decorator-key': { | ||
'myProperty1': MyPropertyMetadata, | ||
'myProperty2': MyPropertyMetadata, | ||
} | ||
} | ||
``` | ||
The following methods in `DecoratorFactory` allow subclasses to customize how | ||
to merge the `spec` with existing metadata for a class, methods, properties, and | ||
method parameters. Please note `M` is a map for methods/properties/parameters. | ||
The following methods in `DecoratorFactory` allow subclasses to customize how to | ||
merge the `spec` with existing metadata for a class, methods, properties, and | ||
method parameters. Please note `M` is a map for methods/properties/parameters. | ||
```ts | ||
protected mergeWithInherited( | ||
inheritedMetadata: M, | ||
target: Object, | ||
member?: string | symbol, | ||
descriptorOrIndex?: TypedPropertyDescriptor<any> | number, | ||
): M { | ||
// ... | ||
} | ||
```ts | ||
protected mergeWithInherited( | ||
inheritedMetadata: M, | ||
target: Object, | ||
member?: string | symbol, | ||
descriptorOrIndex?: TypedPropertyDescriptor<any> | number, | ||
): M { | ||
// ... | ||
} | ||
protected mergeWithOwn( | ||
ownMetadata: M, | ||
target: Object, | ||
member?: string | symbol, | ||
descriptorOrIndex?: TypedPropertyDescriptor<any> | number, | ||
): M { | ||
// ... | ||
} | ||
``` | ||
protected mergeWithOwn( | ||
ownMetadata: M, | ||
target: Object, | ||
member?: string | symbol, | ||
descriptorOrIndex?: TypedPropertyDescriptor<any> | number, | ||
): M { | ||
// ... | ||
} | ||
``` | ||
3. The default implementation throws errors if the same decorator function is applied | ||
to a given target member (class/method/property/parameter) more than once. | ||
For example, the following usage will report an error at runtime. | ||
3. The default implementation throws errors if the same decorator function is | ||
applied to a given target member (class/method/property/parameter) more than | ||
once. For example, the following usage will report an error at runtime. | ||
```ts | ||
@myClassDecorator({name: 'my-controller'}) | ||
@myClassDecorator({name: 'your-controller'}) | ||
class MyController {} | ||
``` | ||
```ts | ||
@myClassDecorator({name: 'my-controller'}) | ||
@myClassDecorator({name: 'your-controller'}) | ||
class MyController {} | ||
``` | ||
@@ -345,3 +345,3 @@ ### Inspect metadata | ||
## Inspect own metadata of a class | ||
### Inspect own metadata of a class | ||
@@ -399,23 +399,22 @@ ```ts | ||
const allParamsForMyMethod = | ||
MetadataInspector.getAllParameterMetaData<MyParameterMetadata>( | ||
'my-parameter-decorator-key', | ||
MyController.prototype, // Use MyController for static methods, | ||
'myMethod', | ||
); | ||
const allParamsForMyMethod = MetadataInspector.getAllParameterMetaData< | ||
MyParameterMetadata | ||
>( | ||
'my-parameter-decorator-key', | ||
MyController.prototype, // Use MyController for static methods, | ||
'myMethod', | ||
); | ||
const firstParamForMyMethod = | ||
MetadataInspector.getMyParameterMetaData<MyParameterMetadata>( | ||
'my-parameter-decorator-key', | ||
MyController.prototype, // Use MyController for static methods | ||
'myMethod', | ||
0, // parameter index | ||
); | ||
const firstParamForMyMethod = MetadataInspector.getMyParameterMetaData< | ||
MyParameterMetadata | ||
>( | ||
'my-parameter-decorator-key', | ||
MyController.prototype, // Use MyController for static methods | ||
'myMethod', | ||
0, // parameter index | ||
); | ||
const allParamsForConstructor = | ||
MetadataInspector.getAllParameterMetaData<MyParameterMetadata>( | ||
'my-parameter-decorator-key', | ||
MyController, | ||
'', | ||
); | ||
const allParamsForConstructor = MetadataInspector.getAllParameterMetaData< | ||
MyParameterMetadata | ||
>('my-parameter-decorator-key', MyController, ''); | ||
``` | ||
@@ -446,5 +445,5 @@ | ||
```sh | ||
npm install --save @loopback/metadata | ||
``` | ||
$ npm install --save @loopback/metadata | ||
``` | ||
@@ -451,0 +450,0 @@ ## Contributions |
@@ -193,2 +193,4 @@ // Copyright IBM Corp. 2017,2018. All Rights Reserved. | ||
enumerable: false, | ||
// Make sure it won't be redefined on the same object | ||
configurable: false, | ||
}); | ||
@@ -318,13 +320,33 @@ } | ||
static cloneDeep<T>(val: T): T { | ||
// See https://github.com/lodash/lodash/blob/master/.internal/baseClone.js | ||
private static _cloneableTypes = [ | ||
Object, | ||
Array, | ||
Set, | ||
Map, | ||
RegExp, | ||
Date, | ||
Buffer, | ||
ArrayBuffer, | ||
Float32Array, | ||
Float64Array, | ||
Int8Array, | ||
Int16Array, | ||
Int32Array, | ||
Uint8Array, | ||
Uint8ClampedArray, | ||
Uint16Array, | ||
Uint32Array, | ||
]; | ||
static cloneDeep<V>(val: Readonly<V>): V { | ||
if (typeof val !== 'object') return val; | ||
return _.cloneDeepWith(val, v => { | ||
// Do not clone functions | ||
if (typeof v === 'function') return v; | ||
if (typeof v !== 'object') return v; | ||
if (v == null) return v; | ||
if ( | ||
v && | ||
typeof v.constructor === 'function' && | ||
v.constructor.prototype === v | ||
v.constructor != null && | ||
!DecoratorFactory._cloneableTypes.includes(v.constructor) | ||
) { | ||
// Do not clone class prototype | ||
// Do not clone instances of classes/constructors, such as Date | ||
return v; | ||
@@ -331,0 +353,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
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
656782
5817
458