diff --git a/angular.json b/angular.json index 286957f..293da78 100644 --- a/angular.json +++ b/angular.json @@ -43,7 +43,7 @@ "namedChunks": false, "aot": true, "extractLicenses": true, - "vendorChunk": false, + "vendorChunk": true, "buildOptimizer": true, "budgets": [ { diff --git a/package-lock.json b/package-lock.json index 4cd00ec..c544a29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3352,6 +3352,11 @@ } } }, + "core-decorators": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/core-decorators/-/core-decorators-0.20.0.tgz", + "integrity": "sha1-YFiWYkBTr4wo775zXCWjAaYcZcU=" + }, "core-js": { "version": "2.6.5", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", diff --git a/projects/logger/package.json b/projects/logger/package.json index de34d54..2505d41 100644 --- a/projects/logger/package.json +++ b/projects/logger/package.json @@ -1,6 +1,6 @@ { "name": "@angular-ru/logger", - "version": "1.5.0", + "version": "1.8.0", "license": "MIT", "homepage": "https://github.com/Angular-RU/angular-logger", "repository": "https://github.com/Angular-RU/angular-logger", diff --git a/projects/logger/src/lib/decorators/autobind.decorator.ts b/projects/logger/src/lib/decorators/autobind.decorator.ts new file mode 100644 index 0000000..836165d --- /dev/null +++ b/projects/logger/src/lib/decorators/autobind.decorator.ts @@ -0,0 +1,144 @@ +import { Any, Fn, ObjectKeyMap } from '../logger.interfaces'; + +const { getPrototypeOf }: ObjectKeyMap = Object; +let mapStore: ObjectKeyMap; + +const { defineProperty, getOwnPropertyDescriptor, getOwnPropertyNames, getOwnPropertySymbols }: ObjectKeyMap = Object; + +function getBoundSuper(obj: ObjectKeyMap, fn: Fn): Fn { + if (typeof WeakMap === 'undefined') { + throw new Error( + `Using @autobind on ${fn.name}() requires WeakMap support due to its use of super.${fn.name}() + See https://github.com/jayphelps/core-decorators.js/issues/20` + ); + } + + if (!mapStore) { + mapStore = new WeakMap(); + } + + if (mapStore.has(obj) === false) { + mapStore.set(obj, new WeakMap()); + } + + const superStore: ObjectKeyMap = mapStore.get(obj); + + if (superStore.has(fn) === false) { + superStore.set(fn, bind(fn, obj)); + } + + return superStore.get(fn); +} + +function bind(fn: Fn, context: Any): Fn { + if (fn.bind) { + return fn.bind(context); + } else { + return function __autobind__(): Fn { + return fn.apply(context, arguments); + }; + } +} + +const getOwnKeys: Fn = getOwnPropertySymbols + ? function(object: ObjectKeyMap): string[] { + return getOwnPropertyNames(object).concat(getOwnPropertySymbols(object)); + } + : getOwnPropertyNames; + +function autobindClass(klass: ObjectKeyMap): Any { + const descs: ObjectKeyMap = getOwnPropertyDescriptors(klass.prototype); + const keys: Any = getOwnKeys(descs); + + for (let i: number = 0, l: number = keys.length; i < l; i++) { + const key: string = keys[i]; + const desc: Any = descs[key]; + + if (typeof desc.value !== 'function' || key === 'constructor') { + continue; + } + + defineProperty(klass.prototype, key, autobindMethod(klass.prototype, key, desc)); + } +} + +function getOwnPropertyDescriptors(obj: ObjectKeyMap): ObjectKeyMap { + const descs: ObjectKeyMap = {}; + + getOwnKeys(obj).forEach((key: Any) => (descs[key] = getOwnPropertyDescriptor(obj, key))); + + return descs; +} + +function autobindMethod(target: Any, key: Any, { value: fn, configurable, enumerable }: Any): PropertyDescriptor { + if (typeof fn !== 'function') { + throw new SyntaxError(`@autobind can only be used on functions, not: ${fn}`); + } + + const { constructor }: Any = target; + + return { + configurable, + enumerable, + + get(): Fn { + if (this === target) { + return fn; + } + + if (this.constructor !== constructor && getPrototypeOf(this).constructor === constructor) { + return fn; + } + + if (this.constructor !== constructor && key in this.constructor.prototype) { + return getBoundSuper(this, fn); + } + + const boundFn: Fn = bind(fn, this); + + defineProperty(this, key, { + configurable: true, + writable: true, + + enumerable: false, + value: boundFn + }); + + return boundFn; + }, + set: createDefaultSetter(key) + }; +} + +function handle(args: Any[]): Fn { + if (args.length === 1) { + return autobindClass(args[0]); + } else { + // @ts-ignore + return autobindMethod(...args); + } +} + +export function autobind(...args: Any[]): Any { + if (args.length === 0) { + return function(...argsClass: Any[]): Fn { + return handle(argsClass); + }; + } else { + return handle(args); + } +} + +function createDefaultSetter(key: Any): Fn { + return function set(newValue: unknown): unknown { + Object.defineProperty(this, key, { + configurable: true, + writable: true, + + enumerable: true, + value: newValue + }); + + return newValue; + }; +} diff --git a/projects/logger/src/lib/logger.service.ts b/projects/logger/src/lib/logger.service.ts index d3f3517..5854fc6 100644 --- a/projects/logger/src/lib/logger.service.ts +++ b/projects/logger/src/lib/logger.service.ts @@ -9,13 +9,16 @@ import { JsonFactory } from './services/json-factory.service'; import { ClipboardFactory } from './services/clipboard-factory.service'; import { TimerFactory } from './services/timer-factory.service'; import { LoggerOptionsImpl } from './logger.options'; +import { autobind } from './decorators/autobind.decorator'; +@autobind @Injectable() export class LoggerService { private readonly DEFAULT_DEPTH: number = 2; + constructor( - public readonly clipboard: ClipboardFactory, - public readonly cssFactory: CssFactory, + private readonly clipboard: ClipboardFactory, + private readonly cssFactory: CssFactory, private readonly console: ConsoleService, private readonly factory: LoggerFactory, private readonly groupFactory: GroupFactory, diff --git a/projects/logger/tests/clipboard.spec.ts b/projects/logger/tests/clipboard.spec.ts index 9232c7d..68c77b3 100644 --- a/projects/logger/tests/clipboard.spec.ts +++ b/projects/logger/tests/clipboard.spec.ts @@ -3,6 +3,7 @@ import { ConsoleFake } from '../../../helpers/console-fake'; import { TestBed } from '@angular/core/testing'; import { LoggerModule } from '../src/lib/logger.module'; import { ObjectKeyMap } from '../src/lib/logger.interfaces'; +import { LoggerInjector } from '../src/lib/logger.injector'; describe('[TEST]: Check clipboard', () => { let logger: LoggerService; @@ -66,7 +67,9 @@ describe('[TEST]: Check clipboard', () => { }); const JsonValue: ObjectKeyMap = { a: 1, b: [1, 2, 3] }; - const isExec: boolean = logger.clipboard.copyOnBuffer(JsonValue); + const isExec: boolean = LoggerInjector.getInjector() + .get(LoggerService) + .copy(JsonValue); expect(isExec).toEqual(true); expect(buffer).toEqual(JSON.stringify(JsonValue, null, 4)); @@ -76,7 +79,9 @@ describe('[TEST]: Check clipboard', () => { createMockQueryCommands(textarea); const JsonValue: ObjectKeyMap = { a: 1, b: [1, 2, 3] }; - const isExec: boolean = logger.clipboard.copyOnBuffer(JsonValue); + const isExec: boolean = LoggerInjector.getInjector() + .get(LoggerService) + .copy(JsonValue); expect(isExec).toEqual(false); expect(buffer).toEqual(null);