UNPKG

jodit

Version:

Jodit is an awesome and useful wysiwyg editor with filebrowser

167 lines (166 loc) 6.17 kB
/*! * Jodit Editor (https://xdsoft.net/jodit/) * Released under MIT see LICENSE.txt in the project root for license information. * Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net */ import { STATUSES } from "../../component/statuses.js"; import { IS_PROD } from "../../constants.js"; import { Dom } from "../../dom/dom.js"; import { isFunction, isViewObject } from "../../helpers/checker/index.js"; import { error } from "../../helpers/utils/error/error.js"; /** * Retrieves a cached property value from an object if it exists; otherwise, returns `null`. * * This utility is particularly useful when working with properties that are lazily initialized * or dynamically created, such as getters or cached computations. It ensures you can safely * access the value without triggering initialization or creating a new instance. * * ### Usage Example: * ```typescript * import type { IUIElement } from "jodit"; * * const { component, cache, cached } = Jodit.decorators; * const { UIElement } = Jodit.modules; * * @component * class SomeComponent extends UIElement { * @cache * get someElement(): IUIElement { * return new UIElement(this.jodit); * } * * destruct() { * // Use the cached utility to clean up only if the property is initialized * cached(this, 'someElement')?.destruct(); * super.destruct(); * } * } * ``` * * @param object - The object containing the property to check. * @param property - The name of the property to retrieve from the cache. * @returns The cached value of the property if it exists; otherwise, `null`. * * ### Notes: * - If the property is defined as a getter, the function will return `null` * instead of invoking the getter. * - This function is non-destructive and does not alter the object's state. */ export function cached(object, property) { const descriptor = Object.getOwnPropertyDescriptor(object, property); if (!descriptor || isFunction(descriptor.get)) { return null; } return descriptor.value; } /** * A decorator that caches the result of a getter method. Once the getter is accessed for the first time, * its computed value is stored as a property of the object. Subsequent accesses return the cached value * without recalculating it, improving performance and avoiding redundant computations. * * ### Key Features: * - **Lazy Initialization**: The original getter is invoked only once, the first time the property is accessed. * - **Immutability**: After caching, the value is stored as a non-writable, non-configurable property, preventing accidental modifications. * - **Conditional Caching**: If the returned value has a property `noCache` set to `true`, the caching mechanism is bypassed, and the getter is invoked each time. * * ### Usage Example 1: Basic Caching * ```typescript * import { cache } from './decorators'; * * class Example { * private counter = 0; * * @cache * get expensiveComputation(): number { * console.log('Calculating...'); * return ++this.counter; * } * } * * const instance = new Example(); * console.log(instance.expensiveComputation); // Logs "Calculating..." and returns 1 * console.log(instance.expensiveComputation); // Returns 1 (cached value, no calculation) * ``` * * ### Usage Example 2: Integration with Cached Utilities * ```typescript * import { cache, cached } from './decorators'; * import type { IUIElement } from "jodit"; * * const { component } = Jodit.decorators; * const { UIElement } = Jodit.modules; * * @component * class SomeComponent extends UIElement { * @cache * get someElement(): IUIElement { * return new UIElement(this.jodit); * } * * destruct() { * // Use the cached utility to clean up only if the property is initialized * cached(this, 'someElement')?.destruct(); * super.destruct(); * } * } * ``` * * @param _ - The target object (not used directly). * @param name - The name of the property to decorate. * @param descriptor - The property descriptor, which must include a getter method. * @throws Will throw an error if the descriptor does not include a getter. * * ### Notes: * - **Performance**: Ideal for properties that are computationally expensive and do not change after the initial computation. * - **Flexibility**: Supports conditional caching via the `noCache` property in the returned value. * - **Compatibility**: Designed to work seamlessly with objects and classes in TypeScript or JavaScript. */ export function cache(_, name, descriptor) { const getter = descriptor.get; if (!getter) { throw error('Getter property descriptor expected'); } descriptor.get = function () { if (!IS_PROD) { if (this.isInDestruct) { console.error('Trying to access property in destructed component'); } } const value = getter.call(this); if (value && value.noCache === true) { return value; } Object.defineProperty(this, name, { configurable: descriptor.configurable, enumerable: descriptor.enumerable, writable: false, value }); return value; }; } export function cacheHTML(target, _, descriptor) { const fn = descriptor.value; if (!isFunction(fn)) { throw error('Handler must be a Function'); } let useCache = true; const cached = new WeakMap(); descriptor.value = function (...attrs) { var _a; if (useCache && cached.has(this.constructor)) { return (_a = cached.get(this.constructor)) === null || _a === void 0 ? void 0 : _a.cloneNode(true); } const value = fn.apply(this, attrs); if (useCache && Dom.isElement(value)) { cached.set(this.constructor, value); } return useCache ? value.cloneNode(true) : value; }; target.hookStatus(STATUSES.ready, (component) => { const view = isViewObject(component) ? component : component.jodit; useCache = Boolean(view.options.cache); }); }