diff --git a/helpers/analyzeTypescriptFiles.ts b/helpers/analyzeTypescriptFiles.ts index b546d57..673d0c7 100644 --- a/helpers/analyzeTypescriptFiles.ts +++ b/helpers/analyzeTypescriptFiles.ts @@ -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) diff --git a/lib/decorators.ts b/lib/decorators.ts new file mode 100644 index 0000000..72135d8 --- /dev/null +++ b/lib/decorators.ts @@ -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 (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; + const registedParams = Base.prototype[_registedParams_] as Map; + + // 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(); + // 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(); + // Here we just add some information that class decorator will use + target[_registedParams_].set(propertyKey, options); + }; +} \ No newline at end of file diff --git a/lib/observables/nopeObservable.ts b/lib/observables/nopeObservable.ts new file mode 100644 index 0000000..4c65a0b --- /dev/null +++ b/lib/observables/nopeObservable.ts @@ -0,0 +1,122 @@ +import { BehaviorSubject, CompletionObserver, ErrorObserver, NextObserver, Subscription } from 'rxjs'; + +export interface nopeObserver { + active: boolean; +} + +export declare type NopePartialObserver = NextObserver & nopeObserver | ErrorObserver & nopeObserver | CompletionObserver & 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 extends BehaviorSubject { + + 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, 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).next) { + const orgFunc = observerOrNext.next; + observerOrNext.next = (v: T) => setImmediate(() => { + if ((observerOrNext as NopePartialObserver).active) { + orgFunc.call(observerOrNext, v); + } + }); + } + if ((observerOrNext as NopePartialObserver).error) { + const orgFunc = observerOrNext.next; + observerOrNext.next = (v: T) => setImmediate(() => { + if ((observerOrNext as NopePartialObserver).active) { + orgFunc.call(observerOrNext, v); + } + }); + } if ((observerOrNext as NopePartialObserver).complete) { + const orgFunc = observerOrNext.next; + observerOrNext.next = (v: T) => setImmediate(() => { + if ((observerOrNext as NopePartialObserver).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); + } +} \ No newline at end of file diff --git a/lib/pubSub/nopePubSubSystem.ts b/lib/pubSub/nopePubSubSystem.ts new file mode 100644 index 0000000..e69de29 diff --git a/package-lock.json b/package-lock.json index 5e7a062..c0ef98e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index b13fce1..d16582e 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/test/testDecorators.ts b/test/testDecorators.ts new file mode 100644 index 0000000..2c3c1e3 --- /dev/null +++ b/test/testDecorators.ts @@ -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) \ No newline at end of file diff --git a/test/testNopeObservable.ts b/test/testNopeObservable.ts new file mode 100644 index 0000000..8f7e8da --- /dev/null +++ b/test/testNopeObservable.ts @@ -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); +} \ No newline at end of file diff --git a/tsconfigBackend.json b/tsconfigBackend.json index e5b1ed6..62c4e3e 100644 --- a/tsconfigBackend.json +++ b/tsconfigBackend.json @@ -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" ] -} +} \ No newline at end of file