Adding Rsx and decorator

This commit is contained in:
Martin Karkowski 2020-08-21 16:49:48 +02:00
parent d18601132e
commit 9b6788b47b
9 changed files with 293 additions and 24 deletions

View File

@ -114,7 +114,7 @@ export function getType(node: Node, inputType: Type, text: string) {
let simplifiedType = '';
let simplifiedSubType = '';
let typeImports: { path: string, type: string }[] = [];
// Test if the Type is a Base-Type.
if ((inputType.compilerType as any).intrinsicName) {
// Basic Type.
@ -207,7 +207,7 @@ export function isPropOfType(prop: PropertyDeclaration, reqType: string, caseSen
* @param prop The Property Declaration.
*/
export function getPropertyDescription(prop: PropertyDeclaration) {
return Object.assign(
// Use the Modifiers
getModifiers(prop),
@ -232,7 +232,7 @@ export function getMatchingProperties(cl: ClassDeclaration, reqType: string, cas
return cl.getProperties()
// Firstly Filter the Properties, that they match the requested Type.
.filter(prop => isPropOfType(
prop,
prop,
reqType,
caseSensitive))
// Instead of returning the Property Declaration, return the
@ -257,7 +257,7 @@ export function getDecorators(declaration: MethodDeclaration | PropertyDeclarati
}
let decoratorSettings: { [index: string]: any }[] = [];
let decorators: Decorator[] = declaration.getDecorators()
.filter(usedDecorator => {
let name = usedDecorator.getName();
@ -285,9 +285,9 @@ export function getDecorators(declaration: MethodDeclaration | PropertyDeclarati
if (typeof aliasToOriginal[name] === 'string') {
return decorator === aliasToOriginal[name]
}
return decorator === name;
});
});
return {
declaration,
@ -340,7 +340,7 @@ function getMethodInfo(declaration: MethodDeclaration) {
originalCode: parameter.getText(),
// The Index of the Parameter
index,
},
},
getType(
parameter.getTypeNode(),
parameter.getType(),
@ -389,11 +389,11 @@ export function getModifiers(declaration: MethodDeclaration | PropertyDeclaratio
if ((declaration as MethodDeclaration).getOverloads) {
modifiers = (declaration as MethodDeclaration).getOverloads().map(overload => dict[overload.getName()]);
// Handle Properties
// Handle Properties
} else if ((declaration as PropertyDeclaration).getCombinedModifierFlags) {
modifiers = dict[(declaration as PropertyDeclaration).getCombinedModifierFlags()];
}
// If nothing is provided => Defaults to public
if (modifiers.length === 0) {
modifiers.push('public')
@ -432,7 +432,7 @@ const sharedMethods = injectedClasses[0].getMethods()
const parsedMethods = sharedMethods.map(methodObject => getMethodInfo(methodObject.declaration as MethodDeclaration));
// Get the Properties
const sharedObservables = getMatchingProperties(injectedClasses[0], 'IObservable', false)
const sharedObservables = getMatchingProperties(injectedClasses[0], 'NopeObersvable', false)
.filter(property =>
property.isPublic &&
getDecorators(property.declaration, 'inject', importMapping.aliasToOriginal, false, false)

88
lib/decorators.ts Normal file
View File

@ -0,0 +1,88 @@
// Symbols for the Property Registery:
const _registedMethods_ = Symbol('_registedMethods_');
const _registedParams_ = Symbol('_registedParams_');
// Interfaces for the Class
export interface IExportApiParameters {
url: string,
}
export interface IExportMethodParameters {
url?: string,
}
export interface IExportPropertyParameters {
url?: string,
readonly?: boolean
}
export const unicorn = {
test: []
}
/**
* Decorator used to export a Class API over openAPI
* @param options
*/
export function eportApi(options: IExportApiParameters) {
return function <T extends { new(...args: any[]): {} }>(Base: T) {
return class extends Base {
constructor(...args: any[]) {
super(...args);
// Adding the Path Option.
(this as any).__path = options.url;
// extract the Registered Methods of the Class.
const registedMethods = Base.prototype[_registedMethods_] as Map<string, IExportMethodParameters>;
const registedParams = Base.prototype[_registedParams_] as Map<string, IExportPropertyParameters>;
// Online if they are present, iterate over them
if (registedMethods) {
registedMethods.forEach((options, methodName) => {
// Provide the code that should be exectuted for every
// Registered Function
unicorn.test.push([methodName, options]);
console.log(unicorn.test)
});
}
// Online if they are present, iterate over them
if (registedParams) {
registedParams.forEach((options, parameterName) => {
// Provide the code that should be exectuted for every
// Registered Function
unicorn.test.push([parameterName, options]);
console.log(unicorn.test)
});
}
}
};
}
}
/**
* Decorator, used to export the Method.
* @param options
*/
export function exportMethod(options: IExportMethodParameters = {}) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
target[_registedMethods_] = target[_registedMethods_] || new Map<string, IExportMethodParameters>();
// Here we just add some information that class decorator will use
target[_registedMethods_].set(propertyKey, options);
};
}
/**
* Decorator, will create a POST and GET api for the Parameter.
* @param options
*/
export function exportProperty(options: IExportPropertyParameters = {}) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
target[_registedParams_] = target[_registedParams_] || new Map<string, IExportPropertyParameters>();
// Here we just add some information that class decorator will use
target[_registedParams_].set(propertyKey, options);
};
}

View File

@ -0,0 +1,122 @@
import { BehaviorSubject, CompletionObserver, ErrorObserver, NextObserver, Subscription } from 'rxjs';
export interface nopeObserver {
active: boolean;
}
export declare type NopePartialObserver<T> = NextObserver<T> & nopeObserver | ErrorObserver<T> & nopeObserver | CompletionObserver<T> & nopeObserver;
/**
* RsJX based Observable.
*
* Contains additional Functionalities like:
* - property with the current value
* - function to publish values. (wrapper for next)
* - enables performing a subscription with synced call or a immediate call.
*/
export class nopeObservable<T> extends BehaviorSubject<T> {
protected _currentValue: T;
/**
* Getter for the Current Value.
*/
public get currentValue(): T {
return this._currentValue;
}
/**
* Setter for the Current value.
*/
public set currentValue(value: T) {
this.next(value);
}
/**
* Creates a custom Observable.
* @param initialValue
*/
constructor(initialValue: T) {
super(initialValue);
const _this = this;
this.subscribe((data) => {
_this._currentValue = data;
});
}
/**
* Function to Publish a Value to the stream.
* @param value The Value.
*/
publish(value: T) {
this.next(value);
}
subscribe(observer?: NopePartialObserver<T>, cbMode?: 'sync' | 'immediate'): Subscription;
/** @deprecated Use an observer instead of a complete callback */
subscribe(next: null | undefined, error: null | undefined, complete: () => void, cbMode?: 'sync' | 'immediate'): Subscription;
/** @deprecated Use an observer instead of an error callback */
subscribe(next: null | undefined, error: (error: any) => void, complete?: () => void, cbMode?: 'sync' | 'immediate'): Subscription;
/** @deprecated Use an observer instead of a complete callback */
subscribe(next: (value: T) => void, error: null | undefined, complete: () => void, cbMode?: 'sync' | 'immediate'): Subscription;
subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void, cbMode?: 'sync' | 'immediate'): Subscription;
// Underlying implementation
subscribe(observerOrNext?, errorOrcbMode?, complete?: () => void, cbMode?: 'sync' | 'immediate'): Subscription {
// The first argument could be an Observer or Callback (Depricated) and the callbacks for complete and error
let _cbMode = cbMode;
let _error = errorOrcbMode;
let _complete = complete;
let _observerOrNext = observerOrNext;
// If the first argument is an Observer => the second argument must be the cbMode
if (typeof observerOrNext === 'object') {
// Adapt the Callback
_cbMode = errorOrcbMode || 'sync';
// The error function must be undefined
_error = undefined;
}
// Test the cbMode
if (_cbMode === 'immediate') {
switch (typeof observerOrNext) {
case 'object':
if ((observerOrNext as NopePartialObserver<T>).next) {
const orgFunc = observerOrNext.next;
observerOrNext.next = (v: T) => setImmediate(() => {
if ((observerOrNext as NopePartialObserver<T>).active) {
orgFunc.call(observerOrNext, v);
}
});
}
if ((observerOrNext as NopePartialObserver<T>).error) {
const orgFunc = observerOrNext.next;
observerOrNext.next = (v: T) => setImmediate(() => {
if ((observerOrNext as NopePartialObserver<T>).active) {
orgFunc.call(observerOrNext, v);
}
});
} if ((observerOrNext as NopePartialObserver<T>).complete) {
const orgFunc = observerOrNext.next;
observerOrNext.next = (v: T) => setImmediate(() => {
if ((observerOrNext as NopePartialObserver<T>).active) {
orgFunc.call(observerOrNext, v);
}
});
}
break;
case 'function':
_observerOrNext = typeof observerOrNext === 'function' ? (_value: T) => setImmediate(observerOrNext, _value) : observerOrNext;
break;
default:
break;
}
_error = typeof errorOrcbMode === 'function' ? (_errorValue: any) => setImmediate(errorOrcbMode, _errorValue) : undefined;
_complete = typeof complete === 'function' ? () => setImmediate(complete) : undefined;
}
return super.subscribe(_observerOrNext, _error, _complete);
}
}

View File

8
package-lock.json generated
View File

@ -5781,6 +5781,14 @@
"aproba": "^1.1.1"
}
},
"rxjs": {
"version": "6.6.2",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz",
"integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==",
"requires": {
"tslib": "^1.9.0"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",

View File

@ -36,6 +36,7 @@
"react": "^16.13.1",
"react-bootstrap": "^1.3.0",
"react-dom": "^16.13.1",
"rxjs": "^6.6.2",
"ts-morph": "^7.3.0"
},
"devDependencies": {

28
test/testDecorators.ts Normal file
View File

@ -0,0 +1,28 @@
import { eportApi, exportMethod, exportProperty } from "../lib/decorators";
@eportApi({
url: 'icemaker'
})
export class Icemaker {
toppings = [];
sugar = 0;
@exportMethod()
addTopping(topping) {
this.toppings.push(topping);
}
@exportMethod()
addSugar() {
this.sugar++;
}
@exportProperty()
name: string;
}
const instance = new Icemaker();
instance.addTopping('caramel')
console.log(instance)

View File

@ -0,0 +1,21 @@
import { filter } from 'rxjs/operators';
import { nopeObservable } from "../lib/observables/nopeObservable";
const observable = new nopeObservable(0);
const subscriptionSmaller = observable.pipe(
filter((v, idx) => v < 10)
).subscribe((v) => {
console.log('smaller 10:', v)
});
const subscriptionGreater = observable.pipe(
filter((v, idx) => v > 10)
).subscribe((v) => {
console.log('greater 10: ', v)
});
let i = 1;
while (i < 20) {
observable.currentValue = i++;
console.log('current', observable.currentValue);
}

View File

@ -7,29 +7,30 @@
"esnext"
],
"allowJs": true,
"allowUnusedLabels": true,
"charset": "utf-8",
"experimentalDecorators": true,
"module": "commonjs",
"moduleResolution": "node",
"noFallthroughCasesInSwitch": false,
"noImplicitReturns": true,
"outDir": "./dist",
"pretty": false,
"removeComments": true,
"rootDir": "./",
"stripInternal": true
"allowUnusedLabels": true,
"charset": "utf-8",
"experimentalDecorators": true,
"module": "commonjs",
"moduleResolution": "node",
"noFallthroughCasesInSwitch": false,
"noImplicitReturns": true,
"outDir": "./dist",
"pretty": false,
"removeComments": true,
"rootDir": "./",
"stripInternal": true
},
"include": [
"next-env.d.ts",
"**/*.ts",
"helpers",
"api",
"src"
"src",
"test"
],
"exclude": [
"node_modules",
"pages",
".next"
]
}
}