@loopback/metadata
Advanced tools
Comparing version 4.0.0-alpha.10 to 4.0.0
@@ -1,1 +0,22 @@ | ||
export * from './src'; | ||
/** | ||
* A package with utilities to help developers implement | ||
* {@link https://www.typescriptlang.org/docs/handbook/decorators.html |TypeScript decorators}, | ||
* define/merge metadata, and inspect metadata. | ||
* | ||
* @remarks | ||
* Utilities: | ||
* | ||
* - Reflector: Wrapper of | ||
* {@link https://github.com/rbuckton/reflect-metadata | reflect-metadata}. | ||
* - 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 | ||
* get metadata applied by decorators. | ||
* | ||
* @packageDocumentation | ||
*/ | ||
export * from './decorator-factory'; | ||
export * from './inspector'; | ||
export * from './reflect'; | ||
export * from './types'; |
"use strict"; | ||
// Copyright IBM Corp. 2017,2018. All Rights Reserved. | ||
// Copyright IBM Corp. 2017,2020. All Rights Reserved. | ||
// Node module: @loopback/metadata | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
function __export(m) { | ||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; | ||
} | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
// DO NOT EDIT THIS FILE | ||
// Add any aditional (re)exports to src/index.ts instead. | ||
__export(require("./src")); | ||
const tslib_1 = require("tslib"); | ||
/** | ||
* A package with utilities to help developers implement | ||
* {@link https://www.typescriptlang.org/docs/handbook/decorators.html |TypeScript decorators}, | ||
* define/merge metadata, and inspect metadata. | ||
* | ||
* @remarks | ||
* Utilities: | ||
* | ||
* - Reflector: Wrapper of | ||
* {@link https://github.com/rbuckton/reflect-metadata | reflect-metadata}. | ||
* - 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 | ||
* get metadata applied by decorators. | ||
* | ||
* @packageDocumentation | ||
*/ | ||
(0, tslib_1.__exportStar)(require("./decorator-factory"), exports); | ||
(0, tslib_1.__exportStar)(require("./inspector"), exports); | ||
(0, tslib_1.__exportStar)(require("./reflect"), exports); | ||
(0, tslib_1.__exportStar)(require("./types"), exports); | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@loopback/metadata", | ||
"version": "4.0.0-alpha.10", | ||
"description": "LoopBack's metadata utilities for reflection and decoration", | ||
"description": "Utilities to help developers implement TypeScript decorators, define/merge metadata, and inspect metadata", | ||
"version": "4.0.0", | ||
"keywords": [ | ||
"LoopBack", | ||
"Decorators", | ||
"Reflect" | ||
], | ||
"license": "MIT", | ||
"main": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"author": "IBM Corp.", | ||
"copyright.owner": "IBM Corp.", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/loopbackio/loopback-next.git", | ||
"directory": "packages/metadata" | ||
}, | ||
"engines": { | ||
"node": ">=8" | ||
"node": "12 || 14 || 16 || 17" | ||
}, | ||
"scripts": { | ||
"acceptance": "lb-mocha \"DIST/test/acceptance/**/*.js\"", | ||
"build": "lb-tsc es2017", | ||
"build:apidocs": "lb-apidocs", | ||
"clean": "lb-clean loopback-metadata*.tgz dist package api-docs", | ||
"prepublishOnly": "npm run build && npm run build:apidocs", | ||
"acceptance": "lb-mocha \"dist/__tests__/acceptance/**/*.js\"", | ||
"build": "lb-tsc", | ||
"clean": "lb-clean loopback-metadata*.tgz dist *.tsbuildinfo package", | ||
"pretest": "npm run build", | ||
"test": "lb-mocha \"DIST/test/unit/**/*.js\" \"DIST/test/acceptance/**/*.js\"", | ||
"unit": "lb-mocha \"DIST/test/unit/**/*.js\"", | ||
"test": "lb-mocha \"dist/__tests__/**/*.js\"", | ||
"unit": "lb-mocha \"dist/__tests__/unit/**/*.js\"", | ||
"verify": "npm pack && tar xf loopback-metadata*.tgz && tree package && npm run clean" | ||
}, | ||
"author": "IBM", | ||
"copyright.owner": "IBM Corp.", | ||
"license": "MIT", | ||
"dependencies": { | ||
"debug": "^3.1.0", | ||
"lodash": "^4.17.4", | ||
"reflect-metadata": "^0.1.10" | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"devDependencies": { | ||
"@loopback/build": "^4.0.0-alpha.13", | ||
"@loopback/testlab": "^4.0.0-alpha.24", | ||
"@types/debug": "^0.0.30", | ||
"@types/lodash": "^4.14.96" | ||
}, | ||
"keywords": [ | ||
"LoopBack", | ||
"Decorators", | ||
"Reflect" | ||
], | ||
"files": [ | ||
"README.md", | ||
"index.js", | ||
"index.d.ts", | ||
"dist/src", | ||
"api-docs", | ||
"src" | ||
"dist", | ||
"src", | ||
"!*/__tests__" | ||
], | ||
"publishConfig": { | ||
"access": "public" | ||
"dependencies": { | ||
"debug": "^4.3.2", | ||
"lodash": "^4.17.21", | ||
"reflect-metadata": "^0.1.13", | ||
"tslib": "^2.3.1" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/strongloop/loopback-next.git" | ||
} | ||
"devDependencies": { | ||
"@loopback/build": "^8.0.0", | ||
"@loopback/eslint-config": "^12.0.0", | ||
"@loopback/testlab": "^4.0.0", | ||
"@types/debug": "^4.1.7", | ||
"@types/lodash": "^4.14.176", | ||
"@types/node": "^10.17.60" | ||
}, | ||
"gitHead": "00a62f630c19341ce399cec3a45563b1ead6e3b8" | ||
} |
407
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. | ||
# Usage | ||
## Basic Use | ||
## To create a class decorator | ||
### To create a class decorator | ||
@@ -30,2 +31,3 @@ ```ts | ||
spec, | ||
{decoratorName: '@myClassDecorator'}, | ||
); | ||
@@ -35,3 +37,3 @@ } | ||
Alternativley, we can instantiate the factory and create a decorator: | ||
Alternatively, we can instantiate the factory and create a decorator: | ||
@@ -55,3 +57,3 @@ ```ts | ||
## To create a method decorator | ||
### To create a method decorator | ||
@@ -91,5 +93,113 @@ ```ts | ||
## To create a property decorator | ||
### To create a decorator that can be used multiple times on a single method | ||
Instead of a single immutable object to be merged, the | ||
`MethodMultiDecoratorFactory` reduced parameters into a flat array of items. | ||
When fetching the metadata later, you will receive it as an array. | ||
```ts | ||
import {MethodMultiDecoratorFactory} from '@loopback/metadata'; | ||
function myMultiMethodDecorator(spec: object): MethodDecorator { | ||
return MethodMultiDecoratorFactory.createDecorator<object>( | ||
'metadata-key-for-my-method-multi-decorator', | ||
spec, | ||
); | ||
} | ||
``` | ||
Now, you can use it multiple times on a method: | ||
```ts | ||
class MyController { | ||
@myMultiMethodDecorator({x: 1}) | ||
@myMultiMethodDecorator({y: 2}) | ||
@myMultiMethodDecorator({z: 3}) | ||
public point() {} | ||
} | ||
class MyOtherController { | ||
@myMultiMethodDecorator([{x: 1}, {y: 2}, {z: 3}]) | ||
public point() {} | ||
} | ||
``` | ||
And when you access this data: | ||
```ts | ||
const arrayOfSpecs = MetadataInspector.getMethodMetadata<object>( | ||
'metadata-key-for-my-method-multi-decorator', | ||
constructor.prototype, | ||
op, | ||
); | ||
// [{z: 3}, {y: 2}, {x: 1}] | ||
``` | ||
Typescript | ||
[applies decorators in reverse order](https://www.typescriptlang.org/docs/handbook/decorators.html) | ||
per class, from the parent down. The metadata array resurned by `getOwnMetadata` | ||
will be in this order: | ||
```ts | ||
class Parent { | ||
@myMultiMethodDecorator('A') // second | ||
@myMultiMethodDecorator('B') // first | ||
public greet() {} | ||
} | ||
class Child extends Parent { | ||
@myMultiMethodDecorator(['C', 'D']) // [third, fourth] | ||
public greet() {} | ||
} | ||
class Grandchild extends Child { | ||
@myMultiMethodDecorator('E') // sixth | ||
@myMultiMethodDecorator('F') // fifth | ||
public greet() {} | ||
} | ||
// getMethodMetadata = ['B', 'A', 'C', 'D', 'F', 'E'] | ||
``` | ||
You can also create a decorator that takes an object that can contain an array: | ||
```ts | ||
interface Point { | ||
x?: number; | ||
y?: number; | ||
z?: number; | ||
} | ||
interface GeometryMetadata { | ||
points: Point[]; | ||
} | ||
function geometry(...points: Point[]): MethodDecorator { | ||
return MethodMultiDecoratorFactory.createDecorator<GeometryMetadata>( | ||
'metadata-key-for-my-method-multi-decorator', | ||
points, | ||
); | ||
} | ||
class MyGeoController { | ||
@geometry({x: 1}) | ||
@geometry({x: 2}, {y: 3}) | ||
@geometry({z: 5}) | ||
public abstract() {} | ||
} | ||
const arrayOfSpecs = MetadataInspector.getMethodMetadata<GeometryMetadata>( | ||
'metadata-key-for-my-method-multi-decorator', | ||
constructor.prototype, | ||
op, | ||
); | ||
// [ | ||
// { points: [{x: 1}]}, | ||
// { points: [{x:2}, {y:3}]}, | ||
// { points: [{z: 5}]}, | ||
// ] | ||
``` | ||
### To create a property decorator | ||
```ts | ||
import {PropertyDecoratorFactory} from '@loopback/metadata'; | ||
@@ -102,3 +212,3 @@ | ||
function myPropertydDecorator(spec: MyPropertyMetadata): PropertyDecorator { | ||
function myPropertyDecorator(spec: MyPropertyMetadata): PropertyDecorator { | ||
return PropertyDecoratorFactory.createDecorator<MyPropertyMetadata>( | ||
@@ -126,3 +236,3 @@ 'metadata-key-for-my-property-decorator', | ||
## To create a parameter decorator | ||
### To create a parameter decorator | ||
@@ -137,3 +247,3 @@ ```ts | ||
function myParameterdDecorator(spec: MyParameterMetadata): ParameterDecorator { | ||
function myParameterDecorator(spec: MyParameterMetadata): ParameterDecorator { | ||
return ParameterDecoratorFactory.createDecorator<MyParameterMetadata>( | ||
@@ -146,3 +256,4 @@ 'metadata-key-for-my-parameter-decorator', | ||
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: | ||
@@ -152,20 +263,14 @@ ```ts | ||
constructor( | ||
@myParameterDecorator({name: 'logging-prefix'}) | ||
public prefix: string, | ||
@myParameterDecorator({name: 'logging-level'}) | ||
public level: number, | ||
@myParameterDecorator({name: 'logging-prefix'}) public prefix: string, | ||
@myParameterDecorator({name: 'logging-level'}) public level: number, | ||
) {} | ||
myMethod( | ||
@myParameterDecorator({name: 'x'}) | ||
x: number, | ||
@myParameterDecorator({name: 'y'}) | ||
y: number, | ||
@myParameterDecorator({name: 'x'}) x: number, | ||
@myParameterDecorator({name: 'y'}) y: number, | ||
) {} | ||
static myStaticMethod( | ||
@myParameterDecorator({name: 'a'}) | ||
a: string, | ||
@myParameterDecorator({name: 'b'}) | ||
b: string, | ||
@myParameterDecorator({name: 'a'}) a: string, | ||
@myParameterDecorator({name: 'b'}) b: string, | ||
) {} | ||
@@ -175,3 +280,3 @@ } | ||
## To create method decorator for parameters | ||
### To create method decorator for parameters | ||
@@ -196,4 +301,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: | ||
@@ -204,6 +309,4 @@ ```ts | ||
@myMethodParameterDecorator({name: 'y'}) | ||
myMethod( | ||
x: number, | ||
y: number, | ||
) {} | ||
myMethod(x: number, y: number) {} | ||
} | ||
``` | ||
@@ -214,8 +317,8 @@ | ||
1. Method decorators cannot be applied to a constructor | ||
2. Method decorators depends on the positions to match parameters | ||
1. Method decorators cannot be applied to a constructor | ||
2. Method decorators depends on the positions to match parameters | ||
We recommend that `ParameterDecorator` be used instead. | ||
## Decorator options | ||
### Decorator options | ||
@@ -225,114 +328,117 @@ An object of type `DecoratorOptions` can be passed in to create decorator | ||
- 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`. | ||
- decoratorName: Name for the decorator such as `@inject` for error and | ||
debugging messages. | ||
## Customize inheritance of metadata | ||
### Customize inheritance of metadata | ||
By default, the decorator factories allow inheritance with the following rules: | ||
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: | ||
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: | ||
- MyClass (the constructor function itself) | ||
```ts | ||
{ | ||
// Class level metadata | ||
'my-class-decorator-key': MyClassMetadata, | ||
// Static method (including the construtor) 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, | ||
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, | ||
descriptorOrIndex?: TypedPropertyDescriptor<any> | number, | ||
): M { | ||
// ... | ||
} | ||
``` | ||
3. The default implemention 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 {} | ||
``` | ||
## Inspect metadata | ||
### Inspect metadata | ||
@@ -342,3 +448,3 @@ `MetadataInspector` provides API to inspect metadata from a class and its | ||
## Inspect metadata of a class | ||
### Inspect metadata of a class | ||
@@ -354,3 +460,3 @@ ```ts | ||
## Inspect own metadata of a class | ||
### Inspect own metadata of a class | ||
@@ -369,3 +475,3 @@ ```ts | ||
## Inspect metadata of a method | ||
### Inspect metadata of a method | ||
@@ -387,3 +493,3 @@ ```ts | ||
## Inspect metadata of a property | ||
### Inspect metadata of a property | ||
@@ -405,3 +511,3 @@ ```ts | ||
## Inspect metadata of method parameters | ||
### Inspect metadata of method parameters | ||
@@ -434,5 +540,24 @@ ```ts | ||
## Inspect design-time metadata of properties/methods | ||
### Use strong-typed metadata access key | ||
You can use MetadataAccessor to provide type checks for metadata access via | ||
keys. For example, | ||
```ts | ||
const CLASS_KEY = MetadataAccessor.create<MyClassMetadata, ClassDecorator>( | ||
'my-class-decorator-key', | ||
); | ||
// Create a class decorator with the key | ||
const myClassDecorator = ClassDecoratorFactory.createDecorator(CLASS_KEY); | ||
// Inspect a class with the key | ||
const myClassMeta = MetadataInspector.getClassMetaData(CLASS_KEY, MyController); | ||
``` | ||
Please note MetadataKey can be an instance of MetadataAccessor or a string. | ||
### Inspect design-time metadata of properties/methods | ||
```ts | ||
import {MetadataInspector} from '@loopback/metadata'; | ||
@@ -458,12 +583,10 @@ | ||
```sh | ||
npm install --save @loopback/metadata | ||
``` | ||
$ npm install --save @loopback/metadata | ||
``` | ||
## Contributions | ||
IBM/StrongLoop is an active supporter of open source and welcomes contributions | ||
to our projects as well as those of the Node.js community in general. For more | ||
information on how to contribute please refer to the | ||
[Contribution Guide](https://loopback.io/doc/en/contrib/index.html). | ||
- [Guidelines](https://github.com/loopbackio/loopback-next/blob/master/docs/CONTRIBUTING.md) | ||
- [Join the team](https://github.com/loopbackio/loopback-next/issues/110) | ||
@@ -477,3 +600,3 @@ ## Tests | ||
See | ||
[all contributors](https://github.com/strongloop/loopback-next/graphs/contributors). | ||
[all contributors](https://github.com/loopbackio/loopback-next/graphs/contributors). | ||
@@ -480,0 +603,0 @@ ## License |
@@ -1,2 +0,2 @@ | ||
// Copyright IBM Corp. 2017,2018. All Rights Reserved. | ||
// Copyright IBM Corp. 2017,2020. All Rights Reserved. | ||
// Node module: @loopback/metadata | ||
@@ -6,17 +6,11 @@ // This file is licensed under the MIT License. | ||
import debugModule from 'debug'; | ||
import _ from 'lodash'; | ||
import {Reflector} from './reflect'; | ||
import * as _ from 'lodash'; | ||
import * as debugModule from 'debug'; | ||
import {DecoratorType, MetadataKey, MetadataMap} from './types'; | ||
const debug = debugModule('loopback:metadata:decorator'); | ||
// tslint:disable:no-any | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
/** | ||
* An object mapping keys to corresponding metadata | ||
*/ | ||
export interface MetadataMap<T> { | ||
[propertyOrMethodName: string]: T; | ||
} | ||
/** | ||
* Options for a decorator | ||
@@ -38,2 +32,8 @@ */ | ||
cloneInputSpec?: boolean; | ||
/** | ||
* Name of the decorator for debugging purpose, such as `@inject` | ||
*/ | ||
decoratorName?: string; | ||
[name: string]: any; | ||
@@ -43,11 +43,2 @@ } | ||
/** | ||
* Decorator function types | ||
*/ | ||
export type DecoratorType = | ||
| ClassDecorator | ||
| PropertyDecorator | ||
| MethodDecorator | ||
| ParameterDecorator; | ||
/** | ||
* Base factory class for decorator functions | ||
@@ -77,4 +68,6 @@ * | ||
M extends T | MetadataMap<T> | MetadataMap<T[]>, // Type of the metadata | ||
D extends DecoratorType // Type of the decorator | ||
D extends DecoratorType, // Type of the decorator | ||
> { | ||
protected decoratorName: string; | ||
/** | ||
@@ -87,5 +80,5 @@ * A constant to reference the target of a decoration | ||
* Construct a new class decorator factory | ||
* @param key Metadata key | ||
* @param spec Metadata object from the decorator function | ||
* @param options Options for the decorator. Default to | ||
* @param key - Metadata key | ||
* @param spec - Metadata object from the decorator function | ||
* @param options - Options for the decorator. Default to | ||
* `{allowInheritance: true}` if not provided | ||
@@ -96,8 +89,13 @@ */ | ||
protected spec: T, | ||
protected options?: DecoratorOptions, | ||
protected options: DecoratorOptions = {}, | ||
) { | ||
this.options = Object.assign( | ||
{allowInheritance: true, cloneInputSpec: true}, | ||
{ | ||
allowInheritance: true, | ||
cloneInputSpec: true, | ||
}, | ||
options, | ||
); | ||
const defaultDecoratorName = this.constructor.name.replace(/Factory$/, ''); | ||
this.decoratorName = this.options.decoratorName ?? defaultDecoratorName; | ||
if (this.options.cloneInputSpec) { | ||
@@ -109,3 +107,3 @@ this.spec = DecoratorFactory.cloneDeep(spec); | ||
protected allowInheritance(): boolean { | ||
return !!(this.options && this.options.allowInheritance); | ||
return !!this.options?.allowInheritance; | ||
} | ||
@@ -118,3 +116,3 @@ | ||
* | ||
* @param inheritedMetadata Metadata from base classes for the member | ||
* @param inheritedMetadata - Metadata from base classes for the member | ||
*/ | ||
@@ -124,3 +122,3 @@ protected inherit(inheritedMetadata: T | undefined | null): T { | ||
if (inheritedMetadata == null) return this.spec; | ||
if (this.spec == undefined) return inheritedMetadata; | ||
if (this.spec == null) return inheritedMetadata; | ||
if (typeof inheritedMetadata !== 'object') return this.spec; | ||
@@ -135,16 +133,20 @@ if (Array.isArray(inheritedMetadata) || Array.isArray(this.spec)) { | ||
/** | ||
* Get the qualified name of a decoration target. For example: | ||
* ``` | ||
* class MyClass | ||
* MyClass.constructor[0] // First parameter of the constructor | ||
* MyClass.myStaticProperty | ||
* MyClass.myStaticMethod() | ||
* MyClass.myStaticMethod[0] // First parameter of the myStaticMethod | ||
* MyClass.prototype.myProperty | ||
* MyClass.prototype.myMethod() | ||
* MyClass.prototype.myMethod[1] // Second parameter of myMethod | ||
* ``` | ||
* @param target Class or prototype of a class | ||
* @param member Optional property/method name | ||
* @param descriptorOrIndex Optional method descriptor or parameter index | ||
* Get the qualified name of a decoration target. | ||
* | ||
* @remarks | ||
* | ||
* Example of target names: | ||
* | ||
* - class MyClass | ||
* - MyClass.constructor[0] // First parameter of the constructor | ||
* - MyClass.myStaticProperty | ||
* - MyClass.myStaticMethod() | ||
* - MyClass.myStaticMethod[0] // First parameter of the myStaticMethod | ||
* - MyClass.prototype.myProperty | ||
* - MyClass.prototype.myMethod() | ||
* - MyClass.prototype.myMethod[1] // Second parameter of myMethod | ||
* | ||
* @param target - Class or prototype of a class | ||
* @param member - Optional property/method name | ||
* @param descriptorOrIndex - Optional method descriptor or parameter index | ||
*/ | ||
@@ -163,10 +165,14 @@ static getTargetName( | ||
} | ||
if (member == null) member = 'constructor'; | ||
if (member == null || member === '') member = 'constructor'; | ||
const memberAccessor = | ||
typeof member === 'symbol' ? '[' + member.toString() + ']' : '.' + member; | ||
if (typeof descriptorOrIndex === 'number') { | ||
// Parameter | ||
name = `${name}.${member}[${descriptorOrIndex}]`; | ||
name = `${name}${memberAccessor}[${descriptorOrIndex}]`; | ||
} else if (descriptorOrIndex != null) { | ||
name = `${name}.${member}()`; | ||
name = `${name}${memberAccessor}()`; | ||
} else { | ||
name = `${name}.${member}`; | ||
name = `${name}${memberAccessor}`; | ||
} | ||
@@ -178,7 +184,7 @@ return name; | ||
* Get the number of parameters for a given constructor or method | ||
* @param target Class or the prototype | ||
* @param member Method name | ||
* @param target - Class or the prototype | ||
* @param member - Method name | ||
*/ | ||
static getNumberOfParameters(target: Object, member?: string | symbol) { | ||
if (target instanceof Function && !member) { | ||
static getNumberOfParameters(target: Object, member?: string) { | ||
if (typeof target === 'function' && !member) { | ||
// constructor | ||
@@ -188,3 +194,4 @@ return target.length; | ||
// target[member] is a function | ||
return (<{[methodName: string]: Function}>target)[member!].length; | ||
const method = (<{[methodName: string]: Function}>target)[member!]; | ||
return method.length; | ||
} | ||
@@ -196,4 +203,4 @@ } | ||
* it's an object | ||
* @param spec Metadata spec | ||
* @param target Target of the decoration. It is a class or the prototype of | ||
* @param spec - Metadata spec | ||
* @param target - Target of the decoration. It is a class or the prototype of | ||
* a class. | ||
@@ -207,2 +214,4 @@ */ | ||
enumerable: false, | ||
// Make sure it won't be redefined on the same object | ||
configurable: false, | ||
}); | ||
@@ -215,3 +224,3 @@ } | ||
* Get the optional decoration target of a given spec | ||
* @param spec Metadata spec | ||
* @param spec - Metadata spec | ||
*/ | ||
@@ -235,6 +244,6 @@ getTarget(spec: T) { | ||
* | ||
* @param inheritedMetadata Metadata inherited from the base classes | ||
* @param target Decoration target | ||
* @param member Optional property or method | ||
* @param descriptorOrIndex Optional parameter index or method descriptor | ||
* @param inheritedMetadata - Metadata inherited from the base classes | ||
* @param target - Decoration target | ||
* @param member - Optional property or method | ||
* @param descriptorOrIndex - Optional parameter index or method descriptor | ||
*/ | ||
@@ -247,3 +256,5 @@ protected mergeWithInherited( | ||
): M { | ||
throw new Error('mergeWithInherited() is not implemented'); | ||
throw new Error( | ||
`mergeWithInherited() is not implemented for ${this.decoratorName}`, | ||
); | ||
} | ||
@@ -259,6 +270,6 @@ | ||
* | ||
* @param ownMetadata Own Metadata exists locally on the target | ||
* @param target Decoration target | ||
* @param member Optional property or method | ||
* @param descriptorOrIndex Optional parameter index or method descriptor | ||
* @param ownMetadata - Own Metadata exists locally on the target | ||
* @param target - Decoration target | ||
* @param member - Optional property or method | ||
* @param descriptorOrIndex - Optional parameter index or method descriptor | ||
*/ | ||
@@ -271,6 +282,30 @@ protected mergeWithOwn( | ||
): M { | ||
throw new Error('mergeWithOwn() is not implemented'); | ||
throw new Error( | ||
`mergeWithOwn() is not implemented for ${this.decoratorName}`, | ||
); | ||
} | ||
/** | ||
* Create an error to report if the decorator is applied to the target more | ||
* than once | ||
* @param target - Decoration target | ||
* @param member - Optional property or method | ||
* @param descriptorOrIndex - Optional parameter index or method descriptor | ||
*/ | ||
protected duplicateDecorationError( | ||
target: Object, | ||
member?: string | symbol, | ||
descriptorOrIndex?: TypedPropertyDescriptor<any> | number, | ||
) { | ||
const targetName = DecoratorFactory.getTargetName( | ||
target, | ||
member, | ||
descriptorOrIndex, | ||
); | ||
return new Error( | ||
`${this.decoratorName} cannot be applied more than once on ${targetName}`, | ||
); | ||
} | ||
/** | ||
* Create a decorator function of the given type. Each sub class MUST | ||
@@ -280,3 +315,3 @@ * implement this method. | ||
create(): D { | ||
throw new Error('create() is not implemented'); | ||
throw new Error(`create() is not implemented for ${this.decoratorName}`); | ||
} | ||
@@ -286,5 +321,5 @@ | ||
* Base implementation of the decorator function | ||
* @param target Decorator target | ||
* @param member Optional property or method | ||
* @param descriptorOrIndex Optional method descriptor or parameter index | ||
* @param target - Decorator target | ||
* @param member - Optional property or method | ||
* @param descriptorOrIndex - Optional method descriptor or parameter index | ||
*/ | ||
@@ -326,26 +361,46 @@ protected decorate( | ||
* Create a decorator function | ||
* @param key Metadata key | ||
* @param spec Metadata object from the decorator function | ||
* @param options Options for the decorator | ||
* @param key - Metadata key | ||
* @param spec - Metadata object from the decorator function | ||
* @param options - Options for the decorator | ||
*/ | ||
protected static _createDecorator< | ||
T, | ||
M extends T | MetadataMap<T> | MetadataMap<T[]>, | ||
D extends DecoratorType | ||
>(key: string, spec: T, options?: DecoratorOptions): D { | ||
const inst = new this<T, M, D>(key, spec, options); | ||
S, | ||
MT extends S | MetadataMap<S> | MetadataMap<S[]>, | ||
DT extends DecoratorType, | ||
>(key: MetadataKey<S, DT>, spec: S, options?: DecoratorOptions): DT { | ||
const inst = new this<S, MT, DT>(key.toString(), spec, options); | ||
return inst.create(); | ||
} | ||
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; | ||
@@ -369,3 +424,3 @@ } | ||
target: Object, | ||
member?: string | symbol, | ||
member?: string, | ||
descriptorOrIndex?: TypedPropertyDescriptor<any> | number, | ||
@@ -379,10 +434,7 @@ ) { | ||
target: Object, | ||
member?: string | symbol, | ||
member?: string, | ||
descriptorOrIndex?: TypedPropertyDescriptor<any> | number, | ||
) { | ||
if (ownMetadata != null) { | ||
throw new Error( | ||
'Decorator cannot be applied more than once on ' + | ||
DecoratorFactory.getTargetName(target), | ||
); | ||
throw this.duplicateDecorationError(target, member, descriptorOrIndex); | ||
} | ||
@@ -398,8 +450,12 @@ return this.withTarget(this.spec, target); | ||
* Create a class decorator function | ||
* @param key Metadata key | ||
* @param spec Metadata object from the decorator function | ||
* @param options Options for the decorator | ||
* @param key - Metadata key | ||
* @param spec - Metadata object from the decorator function | ||
* @param options - Options for the decorator | ||
*/ | ||
static createDecorator<T>(key: string, spec: T, options?: DecoratorOptions) { | ||
return super._createDecorator<T, T, ClassDecorator>(key, spec, options); | ||
static createDecorator<S>( | ||
key: MetadataKey<S, ClassDecorator>, | ||
spec: S, | ||
options?: DecoratorOptions, | ||
) { | ||
return super._createDecorator<S, S, ClassDecorator>(key, spec, options); | ||
} | ||
@@ -419,3 +475,3 @@ } | ||
target: Object, | ||
propertyName?: string | symbol, | ||
propertyName?: string, | ||
descriptorOrIndex?: TypedPropertyDescriptor<any> | number, | ||
@@ -435,3 +491,3 @@ ) { | ||
target: Object, | ||
propertyName?: string | symbol, | ||
propertyName?: string, | ||
descriptorOrParameterIndex?: TypedPropertyDescriptor<any> | number, | ||
@@ -441,5 +497,6 @@ ) { | ||
if (ownMetadata[propertyName!] != null) { | ||
const targetName = DecoratorFactory.getTargetName(target, propertyName); | ||
throw new Error( | ||
'Decorator cannot be applied more than once on ' + targetName, | ||
throw this.duplicateDecorationError( | ||
target, | ||
propertyName, | ||
descriptorOrParameterIndex, | ||
); | ||
@@ -458,8 +515,12 @@ } | ||
* Create a property decorator function | ||
* @param key Metadata key | ||
* @param spec Metadata object from the decorator function | ||
* @param options Options for the decorator | ||
* @param key - Metadata key | ||
* @param spec - Metadata object from the decorator function | ||
* @param options - Options for the decorator | ||
*/ | ||
static createDecorator<T>(key: string, spec: T, options?: DecoratorOptions) { | ||
return super._createDecorator<T, MetadataMap<T>, PropertyDecorator>( | ||
static createDecorator<S>( | ||
key: MetadataKey<S, PropertyDecorator>, | ||
spec: S, | ||
options?: DecoratorOptions, | ||
) { | ||
return super._createDecorator<S, MetadataMap<S>, PropertyDecorator>( | ||
key, | ||
@@ -483,3 +544,3 @@ spec, | ||
target: Object, | ||
methodName?: string | symbol, | ||
methodName?: string, | ||
methodDescriptor?: TypedPropertyDescriptor<any> | number, | ||
@@ -499,3 +560,3 @@ ) { | ||
target: Object, | ||
methodName?: string | symbol, | ||
methodName?: string, | ||
methodDescriptor?: TypedPropertyDescriptor<any> | number, | ||
@@ -506,6 +567,3 @@ ) { | ||
if (this.getTarget(methodMeta) === target) { | ||
throw new Error( | ||
'Decorator cannot be applied more than once on ' + | ||
DecoratorFactory.getTargetName(target, methodName, methodDescriptor), | ||
); | ||
throw this.duplicateDecorationError(target, methodName, methodDescriptor); | ||
} | ||
@@ -527,8 +585,12 @@ // Set the method metadata | ||
* Create a method decorator function | ||
* @param key Metadata key | ||
* @param spec Metadata object from the decorator function | ||
* @param options Options for the decorator | ||
* @param key - Metadata key | ||
* @param spec - Metadata object from the decorator function | ||
* @param options - Options for the decorator | ||
*/ | ||
static createDecorator<T>(key: string, spec: T, options?: DecoratorOptions) { | ||
return super._createDecorator<T, MetadataMap<T>, MethodDecorator>( | ||
static createDecorator<S>( | ||
key: MetadataKey<S, MethodDecorator>, | ||
spec: S, | ||
options?: DecoratorOptions, | ||
) { | ||
return super._createDecorator<S, MetadataMap<S>, MethodDecorator>( | ||
key, | ||
@@ -552,3 +614,3 @@ spec, | ||
target: Object, | ||
methodName?: string | symbol, | ||
methodName?: string, | ||
) { | ||
@@ -570,3 +632,3 @@ const method = methodName ? methodName : ''; | ||
target: Object, | ||
methodName?: string | symbol, | ||
methodName?: string, | ||
parameterIndex?: TypedPropertyDescriptor<any> | number, | ||
@@ -591,3 +653,3 @@ ) { | ||
target: Object, | ||
methodName?: string | symbol, | ||
methodName?: string, | ||
parameterIndex?: TypedPropertyDescriptor<any> | number, | ||
@@ -600,6 +662,3 @@ ) { | ||
if (this.getTarget(methodMeta[index]) === target) { | ||
throw new Error( | ||
'Decorator cannot be applied more than once on ' + | ||
DecoratorFactory.getTargetName(target, methodName, parameterIndex), | ||
); | ||
throw this.duplicateDecorationError(target, methodName, parameterIndex); | ||
} | ||
@@ -624,8 +683,12 @@ // Set the parameter metadata | ||
* Create a parameter decorator function | ||
* @param key Metadata key | ||
* @param spec Metadata object from the decorator function | ||
* @param options Options for the decorator | ||
* @param key - Metadata key | ||
* @param spec - Metadata object from the decorator function | ||
* @param options - Options for the decorator | ||
*/ | ||
static createDecorator<T>(key: string, spec: T, options?: DecoratorOptions) { | ||
return super._createDecorator<T, MetadataMap<T[]>, ParameterDecorator>( | ||
static createDecorator<S>( | ||
key: MetadataKey<S, ParameterDecorator>, | ||
spec: S, | ||
options?: DecoratorOptions, | ||
) { | ||
return super._createDecorator<S, MetadataMap<S[]>, ParameterDecorator>( | ||
key, | ||
@@ -639,4 +702,7 @@ spec, | ||
/** | ||
* Factory for method level parameter decorator. For example, the following | ||
* code uses `@param` to declare two parameters for `greet()`. | ||
* Factory for method level parameter decorator. | ||
* | ||
* @example | ||
* For example, the following code uses `@param` to declare two parameters for | ||
* `greet()`. | ||
* ```ts | ||
@@ -663,3 +729,3 @@ * class MyController { | ||
target: Object, | ||
methodName?: string | symbol, | ||
methodName?: string, | ||
methodDescriptor?: TypedPropertyDescriptor<any> | number, | ||
@@ -687,3 +753,3 @@ ) { | ||
throw new Error( | ||
`The decorator is used more than ${numOfParams} time(s) on ${method}`, | ||
`${this.decoratorName} is used more than ${numOfParams} time(s) on ${method}`, | ||
); | ||
@@ -697,3 +763,3 @@ } | ||
target: Object, | ||
methodName?: string | symbol, | ||
methodName?: string, | ||
methodDescriptor?: TypedPropertyDescriptor<any> | number, | ||
@@ -727,3 +793,3 @@ ) { | ||
target: Object, | ||
methodName?: string | symbol, | ||
methodName?: string, | ||
methodDescriptor?: TypedPropertyDescriptor<any> | number, | ||
@@ -734,3 +800,3 @@ ) { | ||
let params = | ||
const params = | ||
ownMetadata[methodName!] || new Array(index + 1).fill(undefined); | ||
@@ -759,8 +825,12 @@ params[index] = this.withTarget(<T>this.inherit(params[index]), target); | ||
* Create a method decorator function | ||
* @param key Metadata key | ||
* @param spec Metadata object from the decorator function | ||
* @param options Options for the decorator | ||
* @param key - Metadata key | ||
* @param spec - Metadata object from the decorator function | ||
* @param options - Options for the decorator | ||
*/ | ||
static createDecorator<T>(key: string, spec: T, options?: DecoratorOptions) { | ||
return super._createDecorator<T, MetadataMap<T[]>, MethodDecorator>( | ||
static createDecorator<S>( | ||
key: MetadataKey<S, MethodDecorator>, | ||
spec: S, | ||
options?: DecoratorOptions, | ||
) { | ||
return super._createDecorator<S, MetadataMap<S[]>, MethodDecorator>( | ||
key, | ||
@@ -772,1 +842,78 @@ spec, | ||
} | ||
/** | ||
* Factory for an append-array of method-level decorators | ||
* The `@response` metadata for a method is an array. | ||
* Each item in the array should be a single value, containing | ||
* a response code and a single spec or Model. This should allow: | ||
* | ||
* @example | ||
* ```ts | ||
* @response(200, MyFirstModel) | ||
* @response(403, [NotAuthorizedReasonOne, NotAuthorizedReasonTwo]) | ||
* @response(404, NotFoundOne) | ||
* @response(404, NotFoundTwo) | ||
* @response(409, {schema: {}}) | ||
* public async myMethod() {} | ||
* ``` | ||
* | ||
* In the case that a ResponseObject is passed, it becomes the | ||
* default for description/content, and if possible, further Models are | ||
* incorporated as a `oneOf: []` array. | ||
* | ||
* In the case that a ReferenceObject is passed, it and it alone is used, since | ||
* references can be external and we cannot `oneOf` their content. | ||
* | ||
* The factory creates and updates an array of items T[], and the getter | ||
* provides the values as that array. | ||
*/ | ||
export class MethodMultiDecoratorFactory<T> extends MethodDecoratorFactory< | ||
T[] | ||
> { | ||
protected mergeWithInherited( | ||
inheritedMetadata: MetadataMap<T[]>, | ||
target: Object, | ||
methodName?: string, | ||
) { | ||
inheritedMetadata = inheritedMetadata || {}; | ||
inheritedMetadata[methodName!] = this._mergeArray( | ||
inheritedMetadata[methodName!], | ||
this.withTarget(this.spec, target), | ||
); | ||
return inheritedMetadata; | ||
} | ||
protected mergeWithOwn( | ||
ownMetadata: MetadataMap<T[]>, | ||
target: Object, | ||
methodName?: string, | ||
methodDescriptor?: TypedPropertyDescriptor<any> | number, | ||
) { | ||
ownMetadata = ownMetadata || {}; | ||
ownMetadata[methodName!] = this._mergeArray( | ||
ownMetadata[methodName!], | ||
this.withTarget(this.spec, target), | ||
); | ||
return ownMetadata; | ||
} | ||
private _mergeArray(result: T[], methodMeta: T | T[]) { | ||
if (!result) { | ||
if (Array.isArray(methodMeta)) { | ||
result = methodMeta; | ||
} else { | ||
result = [methodMeta]; | ||
} | ||
} else { | ||
if (Array.isArray(methodMeta)) { | ||
result.push(...methodMeta); | ||
} else { | ||
result.push(methodMeta); | ||
} | ||
} | ||
return result; | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
// Copyright IBM Corp. 2017. All Rights Reserved. | ||
// Copyright IBM Corp. 2017,2020. All Rights Reserved. | ||
// Node module: @loopback/metadata | ||
@@ -6,4 +6,24 @@ // This file is licensed under the MIT License. | ||
export * from './reflect'; | ||
/** | ||
* A package with utilities to help developers implement | ||
* {@link https://www.typescriptlang.org/docs/handbook/decorators.html |TypeScript decorators}, | ||
* define/merge metadata, and inspect metadata. | ||
* | ||
* @remarks | ||
* Utilities: | ||
* | ||
* - Reflector: Wrapper of | ||
* {@link https://github.com/rbuckton/reflect-metadata | reflect-metadata}. | ||
* - 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 | ||
* get metadata applied by decorators. | ||
* | ||
* @packageDocumentation | ||
*/ | ||
export * from './decorator-factory'; | ||
export * from './inspector'; | ||
export * from './reflect'; | ||
export * from './types'; |
@@ -1,8 +0,16 @@ | ||
// Copyright IBM Corp. 2017,2018. All Rights Reserved. | ||
// Copyright IBM Corp. 2017,2019. All Rights Reserved. | ||
// Node module: @loopback/metadata | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
import debugModule from 'debug'; | ||
import {DecoratorFactory} from './decorator-factory'; | ||
import {NamespacedReflect, Reflector} from './reflect'; | ||
import { | ||
DecoratorType, | ||
DesignTimeMethodMetadata, | ||
MetadataKey, | ||
MetadataMap, | ||
} from './types'; | ||
import {Reflector, NamespacedReflect} from './reflect'; | ||
import {MetadataMap} from './decorator-factory'; | ||
const debug = debugModule('loopback:metadata:inspector'); | ||
@@ -18,40 +26,2 @@ /** | ||
/** | ||
* Design time metadata for a method. | ||
* | ||
* @example | ||
* ```ts | ||
* class MyController | ||
* { | ||
* myMethod(x: string, y: number, z: MyClass): boolean { | ||
* // ... | ||
* return true; | ||
* } | ||
* } | ||
* ``` | ||
* | ||
* The `myMethod` above has design-time metadata as follows: | ||
* ```ts | ||
* { | ||
* type: Function, | ||
* parameterTypes: [String, Number, MyClass], | ||
* returnType: Boolean | ||
* } | ||
* ``` | ||
*/ | ||
export interface DesignTimeMethodMetadata { | ||
/** | ||
* Type of the method itself. It is `Function` | ||
*/ | ||
type: Function; | ||
/** | ||
* An array of parameter types | ||
*/ | ||
parameterTypes: Function[]; | ||
/** | ||
* Return type | ||
*/ | ||
returnType: Function; | ||
} | ||
/** | ||
* Options for inspection | ||
@@ -86,14 +56,14 @@ */ | ||
* Get the metadata associated with the given key for a given class | ||
* @param key Metadata key | ||
* @param target Class that contains the metadata | ||
* @param options Options for inspection | ||
* @param key - Metadata key | ||
* @param target - Class that contains the metadata | ||
* @param options - Options for inspection | ||
*/ | ||
static getClassMetadata<T>( | ||
key: string, | ||
key: MetadataKey<T, ClassDecorator>, | ||
target: Function, | ||
options?: InspectionOptions, | ||
): T | undefined { | ||
return options && options.ownMetadataOnly | ||
? Reflector.getOwnMetadata(key, target) | ||
: Reflector.getMetadata(key, target); | ||
return options?.ownMetadataOnly | ||
? Reflector.getOwnMetadata(key.toString(), target) | ||
: Reflector.getMetadata(key.toString(), target); | ||
} | ||
@@ -103,14 +73,14 @@ | ||
* Define metadata for the given target | ||
* @param key Metadata key | ||
* @param value Metadata value | ||
* @param target Target for the metadata | ||
* @param member Optional property or method name | ||
* @param key - Metadata key | ||
* @param value - Metadata value | ||
* @param target - Target for the metadata | ||
* @param member - Optional property or method name | ||
*/ | ||
static defineMetadata<T>( | ||
key: string, | ||
key: MetadataKey<T, DecoratorType>, | ||
value: T, | ||
target: Object, | ||
member?: string | symbol, | ||
member?: string, | ||
) { | ||
Reflector.defineMetadata(key, value, target, member); | ||
Reflector.defineMetadata(key.toString(), value, target, member); | ||
} | ||
@@ -121,14 +91,14 @@ | ||
* target class or prototype | ||
* @param key Metadata key | ||
* @param target Class for static methods or prototype for instance methods | ||
* @param options Options for inspection | ||
* @param key - Metadata key | ||
* @param target - Class for static methods or prototype for instance methods | ||
* @param options - Options for inspection | ||
*/ | ||
static getAllMethodMetadata<T>( | ||
key: string, | ||
key: MetadataKey<T, MethodDecorator>, | ||
target: Object, | ||
options?: InspectionOptions, | ||
): MetadataMap<T> | undefined { | ||
return options && options.ownMetadataOnly | ||
? Reflector.getOwnMetadata(key, target) | ||
: Reflector.getMetadata(key, target); | ||
return options?.ownMetadataOnly | ||
? Reflector.getOwnMetadata(key.toString(), target) | ||
: Reflector.getMetadata(key.toString(), target); | ||
} | ||
@@ -139,20 +109,19 @@ | ||
* target class or prototype | ||
* @param key Metadata key | ||
* @param target Class for static methods or prototype for instance methods | ||
* @param methodName Method name. If not present, default to '' to use | ||
* @param key - Metadata key | ||
* @param target - Class for static methods or prototype for instance methods | ||
* @param methodName - Method name. If not present, default to '' to use | ||
* the constructor | ||
* @param options Options for inspection | ||
* @param options - Options for inspection | ||
*/ | ||
static getMethodMetadata<T>( | ||
key: string, | ||
key: MetadataKey<T, MethodDecorator>, | ||
target: Object, | ||
methodName?: string | symbol, | ||
methodName?: string, | ||
options?: InspectionOptions, | ||
): T | undefined { | ||
methodName = methodName || ''; | ||
const meta: MetadataMap<T> = | ||
options && options.ownMetadataOnly | ||
? Reflector.getOwnMetadata(key, target) | ||
: Reflector.getMetadata(key, target); | ||
return meta && meta[methodName]; | ||
methodName = methodName ?? ''; | ||
const meta: MetadataMap<T> = options?.ownMetadataOnly | ||
? Reflector.getOwnMetadata(key.toString(), target) | ||
: Reflector.getMetadata(key.toString(), target); | ||
return meta?.[methodName]; | ||
} | ||
@@ -163,14 +132,14 @@ | ||
* target class or prototype | ||
* @param key Metadata key | ||
* @param target Class for static methods or prototype for instance methods | ||
* @param options Options for inspection | ||
* @param key - Metadata key | ||
* @param target - Class for static methods or prototype for instance methods | ||
* @param options - Options for inspection | ||
*/ | ||
static getAllPropertyMetadata<T>( | ||
key: string, | ||
key: MetadataKey<T, PropertyDecorator>, | ||
target: Object, | ||
options?: InspectionOptions, | ||
): MetadataMap<T> | undefined { | ||
return options && options.ownMetadataOnly | ||
? Reflector.getOwnMetadata(key, target) | ||
: Reflector.getMetadata(key, target); | ||
return options?.ownMetadataOnly | ||
? Reflector.getOwnMetadata(key.toString(), target) | ||
: Reflector.getMetadata(key.toString(), target); | ||
} | ||
@@ -181,19 +150,18 @@ | ||
* target class or prototype | ||
* @param key Metadata key | ||
* @param target Class for static properties or prototype for instance | ||
* @param key - Metadata key | ||
* @param target - Class for static properties or prototype for instance | ||
* properties | ||
* @param propertyName Property name | ||
* @param options Options for inspection | ||
* @param propertyName - Property name | ||
* @param options - Options for inspection | ||
*/ | ||
static getPropertyMetadata<T>( | ||
key: string, | ||
key: MetadataKey<T, PropertyDecorator>, | ||
target: Object, | ||
propertyName: string | symbol, | ||
propertyName: string, | ||
options?: InspectionOptions, | ||
): T | undefined { | ||
const meta: MetadataMap<T> = | ||
options && options.ownMetadataOnly | ||
? Reflector.getOwnMetadata(key, target) | ||
: Reflector.getMetadata(key, target); | ||
return meta && meta[propertyName]; | ||
const meta: MetadataMap<T> = options?.ownMetadataOnly | ||
? Reflector.getOwnMetadata(key.toString(), target) | ||
: Reflector.getMetadata(key.toString(), target); | ||
return meta?.[propertyName]; | ||
} | ||
@@ -204,20 +172,19 @@ | ||
* given method | ||
* @param key Metadata key | ||
* @param target Class for static methods or prototype for instance methods | ||
* @param methodName Method name. If not present, default to '' to use | ||
* @param key - Metadata key | ||
* @param target - Class for static methods or prototype for instance methods | ||
* @param methodName - Method name. If not present, default to '' to use | ||
* the constructor | ||
* @param options Options for inspection | ||
* @param options - Options for inspection | ||
*/ | ||
static getAllParameterMetadata<T>( | ||
key: string, | ||
key: MetadataKey<T, ParameterDecorator>, | ||
target: Object, | ||
methodName?: string | symbol, | ||
methodName?: string, | ||
options?: InspectionOptions, | ||
): T[] | undefined { | ||
methodName = methodName || ''; | ||
const meta: MetadataMap<T[]> = | ||
options && options.ownMetadataOnly | ||
? Reflector.getOwnMetadata(key, target) | ||
: Reflector.getMetadata(key, target); | ||
return meta && meta[methodName]; | ||
methodName = methodName ?? ''; | ||
const meta: MetadataMap<T[]> = options?.ownMetadataOnly | ||
? Reflector.getOwnMetadata(key.toString(), target) | ||
: Reflector.getMetadata(key.toString(), target); | ||
return meta?.[methodName]; | ||
} | ||
@@ -228,13 +195,13 @@ | ||
* method by index | ||
* @param key Metadata key | ||
* @param target Class for static methods or prototype for instance methods | ||
* @param methodName Method name. If not present, default to '' to use | ||
* @param key - Metadata key | ||
* @param target - Class for static methods or prototype for instance methods | ||
* @param methodName - Method name. If not present, default to '' to use | ||
* the constructor | ||
* @param index Index of the parameter, starting with 0 | ||
* @param options Options for inspection | ||
* @param index - Index of the parameter, starting with 0 | ||
* @param options - Options for inspection | ||
*/ | ||
static getParameterMetadata<T>( | ||
key: string, | ||
key: MetadataKey<T, ParameterDecorator>, | ||
target: Object, | ||
methodName: string | symbol, | ||
methodName: string, | ||
index: number, | ||
@@ -244,8 +211,7 @@ options?: InspectionOptions, | ||
methodName = methodName || ''; | ||
const meta: MetadataMap<T[]> = | ||
options && options.ownMetadataOnly | ||
? Reflector.getOwnMetadata(key, target) | ||
: Reflector.getMetadata(key, target); | ||
const params = meta && meta[methodName]; | ||
return params && params[index]; | ||
const meta: MetadataMap<T[]> = options?.ownMetadataOnly | ||
? Reflector.getOwnMetadata(key.toString(), target) | ||
: Reflector.getMetadata(key.toString(), target); | ||
const params = meta?.[methodName]; | ||
return params?.[index]; | ||
} | ||
@@ -255,9 +221,13 @@ | ||
* Get TypeScript design time type for a property | ||
* @param target Class or prototype | ||
* @param propertyName Property name | ||
* @param target - Class or prototype | ||
* @param propertyName - Property name | ||
* @returns Design time metadata. The return value is `undefined` when: | ||
* - The property has type `undefined`, `null` | ||
* - The TypeScript project has not enabled the compiler option `emitDecoratorMetadata`. | ||
* - The code is written in vanilla JavaScript. | ||
*/ | ||
static getDesignTypeForProperty( | ||
target: Object, | ||
propertyName: string | symbol, | ||
): Function { | ||
propertyName: string, | ||
): Function | undefined { | ||
return TSReflector.getMetadata('design:type', target, propertyName); | ||
@@ -267,10 +237,13 @@ } | ||
/** | ||
* Get TypeScript design time type for a method | ||
* @param target Class or prototype | ||
* @param methodName Method name | ||
* Get TypeScript design time type for a method. | ||
* @param target - Class or prototype | ||
* @param methodName - Method name | ||
* @returns Design time metadata. The return value is `undefined` | ||
* in projects that do not enable `emitDecoratorMetadata` | ||
* in TypeScript compiler options or are written in vanilla JavaScript. | ||
*/ | ||
static getDesignTypeForMethod( | ||
target: Object, | ||
methodName: string | symbol, | ||
): DesignTimeMethodMetadata { | ||
methodName: string, | ||
): DesignTimeMethodMetadata | undefined { | ||
const type = TSReflector.getMetadata('design:type', target, methodName); | ||
@@ -287,2 +260,21 @@ const parameterTypes = TSReflector.getMetadata( | ||
); | ||
if ( | ||
type === undefined && | ||
parameterTypes === undefined && | ||
returnType === undefined | ||
) { | ||
/* istanbul ignore next */ | ||
if (debug.enabled) { | ||
const targetName = DecoratorFactory.getTargetName(target, methodName); | ||
debug( | ||
'No design-time type metadata found while inspecting %s. ' + | ||
'Did you forget to enable TypeScript compiler option `emitDecoratorMetadata`?', | ||
targetName, | ||
); | ||
} | ||
return undefined; | ||
} | ||
return { | ||
@@ -289,0 +281,0 @@ type, |
@@ -1,2 +0,2 @@ | ||
// Copyright IBM Corp. 2017. All Rights Reserved. | ||
// Copyright IBM Corp. 2017,2019. All Rights Reserved. | ||
// Node module: @loopback/metadata | ||
@@ -8,3 +8,3 @@ // This file is licensed under the MIT License. | ||
/* tslint:disable:no-any */ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
@@ -16,3 +16,3 @@ /* | ||
/** | ||
* @param namespace : namespace to bind this reflect context | ||
* @param namespace - Namespace to bind this reflect context | ||
*/ | ||
@@ -33,3 +33,3 @@ constructor(private namespace?: string) {} | ||
target: Object, | ||
propertyKey?: string | symbol, | ||
propertyKey?: string, | ||
) { | ||
@@ -47,7 +47,3 @@ metadataKey = this.getMetadataKey(metadataKey); | ||
*/ | ||
getMetadata( | ||
metadataKey: string, | ||
target: Object, | ||
propertyKey?: string | symbol, | ||
): any { | ||
getMetadata(metadataKey: string, target: Object, propertyKey?: string): any { | ||
metadataKey = this.getMetadataKey(metadataKey); | ||
@@ -66,3 +62,3 @@ if (propertyKey) { | ||
target: Object, | ||
propertyKey?: string | symbol, | ||
propertyKey?: string, | ||
): any { | ||
@@ -78,5 +74,5 @@ metadataKey = this.getMetadataKey(metadataKey); | ||
* Check if the target has corresponding metadata | ||
* @param metadataKey Key | ||
* @param target Target | ||
* @param propertyKey Optional property key | ||
* @param metadataKey - Key | ||
* @param target - Target | ||
* @param propertyKey - Optional property key | ||
*/ | ||
@@ -86,3 +82,3 @@ hasMetadata( | ||
target: Object, | ||
propertyKey?: string | symbol, | ||
propertyKey?: string, | ||
): boolean { | ||
@@ -99,3 +95,3 @@ metadataKey = this.getMetadataKey(metadataKey); | ||
target: Object, | ||
propertyKey?: string | symbol, | ||
propertyKey?: string, | ||
): boolean { | ||
@@ -112,3 +108,3 @@ metadataKey = this.getMetadataKey(metadataKey); | ||
target: Object, | ||
propertyKey?: string | symbol, | ||
propertyKey?: string, | ||
): boolean { | ||
@@ -122,3 +118,3 @@ metadataKey = this.getMetadataKey(metadataKey); | ||
getMetadataKeys(target: Object, propertyKey?: string | symbol): string[] { | ||
getMetadataKeys(target: Object, propertyKey?: string): string[] { | ||
let keys: string[]; | ||
@@ -144,3 +140,3 @@ if (propertyKey) { | ||
getOwnMetadataKeys(target: Object, propertyKey?: string | symbol): string[] { | ||
getOwnMetadataKeys(target: Object, propertyKey?: string): string[] { | ||
let keys: string[]; | ||
@@ -167,2 +163,14 @@ if (propertyKey) { | ||
decorate( | ||
decorators: (PropertyDecorator | MethodDecorator)[], | ||
target: Object, | ||
targetKey?: string | symbol, | ||
descriptor?: PropertyDescriptor, | ||
): PropertyDescriptor | Function; | ||
decorate( | ||
decorators: ClassDecorator[], | ||
target: Object, | ||
): PropertyDescriptor | Function; | ||
decorate( | ||
decorators: (PropertyDecorator | MethodDecorator)[] | ClassDecorator[], | ||
@@ -174,3 +182,8 @@ target: Object, | ||
if (targetKey) { | ||
return Reflect.decorate(decorators, target, targetKey, descriptor); | ||
return Reflect.decorate( | ||
<(PropertyDecorator | MethodDecorator)[]>decorators, | ||
target, | ||
targetKey, | ||
descriptor, | ||
); | ||
} else { | ||
@@ -181,3 +194,2 @@ return Reflect.decorate(<ClassDecorator[]>decorators, <Function>target); | ||
/* tslint:disable-next-line:no-any */ | ||
metadata( | ||
@@ -195,3 +207,2 @@ metadataKey: string, | ||
/* tslint:disable-next-line:variable-name */ | ||
export const Reflector = new NamespacedReflect('loopback'); |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
1
584
142945
4
6
23
2868
1
+ Addedtslib@^2.3.1
+ Addeddebug@4.3.6(transitive)
+ Addedms@2.1.2(transitive)
+ Addedtslib@2.6.3(transitive)
- Removeddebug@3.2.7(transitive)
- Removedms@2.1.3(transitive)
Updateddebug@^4.3.2
Updatedlodash@^4.17.21
Updatedreflect-metadata@^0.1.13