From 28cdb2fa3065c5cef308cc63b3faff78b26ded4a Mon Sep 17 00:00:00 2001 From: Marko Vujanic Date: Sun, 19 May 2024 08:29:14 +0200 Subject: [PATCH] add documentation for utill functions --- reports/api-extractor.md | 4 ++-- src/logic/generateId.ts | 4 ++++ src/logic/getFocusFieldName.ts | 20 +++++++++++++++++++- src/logic/getValidationModes.ts | 13 +++++++++++++ src/logic/iterateFieldsByAction.ts | 28 ++++++++++++++++++++++++++++ src/utils/append.ts | 4 ++++ src/utils/cloneObject.ts | 6 +++++- src/utils/compact.ts | 8 ++++++++ src/utils/convertToArrayPayload.ts | 3 +++ src/utils/createSubject.ts | 27 ++++++++++++++++++++++++--- src/utils/deepEqual.ts | 21 ++++++++++++++++++++- src/utils/deepMerge.ts | 14 ++++++++++++++ src/utils/fillEmptyArray.ts | 5 +++++ src/utils/get.ts | 12 ++++++++++++ src/utils/insert.ts | 3 +++ src/utils/isEmptyObject.ts | 4 ++++ src/utils/isKey.ts | 8 ++++++++ src/utils/isMessage.ts | 10 +++++++++- src/utils/noop.ts | 3 +++ src/utils/objectHasFunction.ts | 4 +++- src/utils/prepend.ts | 4 +++- src/utils/remove.ts | 10 +++++++++- src/utils/set.ts | 10 ++++++++++ src/utils/sleep.ts | 4 ++++ src/utils/stringToPath.ts | 20 ++++++++++++++++++-- src/utils/swap.ts | 8 ++++++++ src/utils/unset.ts | 8 ++++++++ src/utils/update.ts | 9 +++++++++ 28 files changed, 260 insertions(+), 14 deletions(-) diff --git a/reports/api-extractor.md b/reports/api-extractor.md index 5371dbc1df9..814a32a2bc5 100644 --- a/reports/api-extractor.md +++ b/reports/api-extractor.md @@ -339,7 +339,7 @@ export type FormSubmitHandler = (payload: { method?: 'post' | 'put' | 'delete'; }) => unknown | Promise; -// @public (undocumented) +// @public export const get: (object: T, path?: string, defaultValue?: unknown) => any; // @public (undocumented) @@ -531,7 +531,7 @@ export type ResolverSuccess = { errors: {}; }; -// @public (undocumented) +// @public export const set: (object: FieldValues, path: string, value?: unknown) => FieldValues; // @public (undocumented) diff --git a/src/logic/generateId.ts b/src/logic/generateId.ts index a19c2f4ad99..e6f7a854c4d 100644 --- a/src/logic/generateId.ts +++ b/src/logic/generateId.ts @@ -1,3 +1,7 @@ +/** + Generates a unique identifier string in the UUID v4 format. + NOT for serious cryptography. + */ export default () => { const d = typeof performance === 'undefined' ? Date.now() : performance.now() * 1000; diff --git a/src/logic/getFocusFieldName.ts b/src/logic/getFocusFieldName.ts index a468079576c..19afad6c64c 100644 --- a/src/logic/getFocusFieldName.ts +++ b/src/logic/getFocusFieldName.ts @@ -1,6 +1,24 @@ import { FieldArrayMethodProps, InternalFieldName } from '../types'; import isUndefined from '../utils/isUndefined'; - +/** + * Constructs the focus name for a field array + * based on the given parameters. If the `shouldFocus` + * option is true or undefined, it returns a focus name. + * The focus name is constructed based on the + * `focusName` option, the `name` parameter, + * and either the `focusIndex` option or the provided `index`. + * If `shouldFocus` is false, it returns an empty string. + * + * @example + * getFocusName('field', 2, { shouldFocus: true }); + * Output: "field.2." + * + * getFocusName('field', 2, { shouldFocus: true, focusName: 'apple' }); + * Output: "apple" + * + * getFocusName('field', 2, { shouldFocus: false }); + // Output: "" + */ export default ( name: InternalFieldName, index: number, diff --git a/src/logic/getValidationModes.ts b/src/logic/getValidationModes.ts index bf3fd40ab3f..554b0920a8b 100644 --- a/src/logic/getValidationModes.ts +++ b/src/logic/getValidationModes.ts @@ -1,6 +1,19 @@ import { VALIDATION_MODE } from '../constants'; import { Mode, ValidationModeFlags } from '../types'; +/** + * Constructs validation mode flags based on the provided mode. + * This function returns an object with flags indicating which + * validation mode is active. + * @remarks If no mode is provided, it defaults to `onSubmit`. + * + * @example + * const flags = getValidationModeFlags(VALIDATION_MODE.onBlur); + * Output: { isOnBlur: true, ...false} + * + * getValidationModeFlags(); + * Output: { isOnSubmit: true, false} + */ export default (mode?: Mode): ValidationModeFlags => ({ isOnSubmit: !mode || mode === VALIDATION_MODE.onSubmit, isOnBlur: mode === VALIDATION_MODE.onBlur, diff --git a/src/logic/iterateFieldsByAction.ts b/src/logic/iterateFieldsByAction.ts index 964f87a74e5..f3eaadd44e7 100644 --- a/src/logic/iterateFieldsByAction.ts +++ b/src/logic/iterateFieldsByAction.ts @@ -2,6 +2,34 @@ import { FieldRefs, InternalFieldName, Ref } from '../types'; import { get } from '../utils'; import isObject from '../utils/isObject'; +/** + * Iterates over field references and performs + * an action on each field. This function traverses + * through the field references, applying the given + * action to each field. + * @remarks The traversal can be limited to specific field names, + * and it can be aborted early based on the action's return value. + * + * @example + * const fields = { + * field1: { + * _f: { + * ref: document.createElement('input'), + * name: 'field1', + * }}, + * field2: { + * _f: { + * refs: [document.createElement('input')], + * name: 'field2', + * }}}; + * + * iterateFieldsByAction(fields, (ref, name) => { + * console.log(name, ref); + * }); + * Output: + * field1 + * field2 + */ const iterateFieldsByAction = ( fields: FieldRefs, action: (ref: Ref, name: string) => 1 | undefined | void, diff --git a/src/utils/append.ts b/src/utils/append.ts index 82b413129de..23b69453288 100644 --- a/src/utils/append.ts +++ b/src/utils/append.ts @@ -1,5 +1,9 @@ import convertToArrayPayload from './convertToArrayPayload'; +/** + * Add given value or array of values to the end of given array. + * @returns {T[]} A new array with the appended value(s). + */ export default (data: T[], value: T | T[]): T[] => [ ...data, ...convertToArrayPayload(value), diff --git a/src/utils/cloneObject.ts b/src/utils/cloneObject.ts index 196305fa142..fd2d5609736 100644 --- a/src/utils/cloneObject.ts +++ b/src/utils/cloneObject.ts @@ -1,7 +1,11 @@ import isObject from './isObject'; import isPlainObject from './isPlainObject'; import isWeb from './isWeb'; - +/** + * Deeply clones a given object, + * including handling special types like Date and Set. + * It also handles arrays and plain objects. + */ export default function cloneObject(data: T): T { let copy: any; const isArray = Array.isArray(data); diff --git a/src/utils/compact.ts b/src/utils/compact.ts index 35779202e06..3bfbd4a482f 100644 --- a/src/utils/compact.ts +++ b/src/utils/compact.ts @@ -1,2 +1,10 @@ +/** + * @description Removes falsy values from an array. + * @returns {TValue[]} A new array with all falsy values removed. + * @example + * const array = [0, 1, false, 2, '', 3, null, undefined, NaN, 4]; + * const compactedArray = compact(array); + * compactedArray // Output: [1, 2, 3, 4] + */ export default (value: TValue[]) => Array.isArray(value) ? value.filter(Boolean) : []; diff --git a/src/utils/convertToArrayPayload.ts b/src/utils/convertToArrayPayload.ts index f68ec705ed4..be684e1e11d 100644 --- a/src/utils/convertToArrayPayload.ts +++ b/src/utils/convertToArrayPayload.ts @@ -1 +1,4 @@ +/** + * Put's given value into an array if it's not already an array. + */ export default (value: T) => (Array.isArray(value) ? value : [value]); diff --git a/src/utils/createSubject.ts b/src/utils/createSubject.ts index 222c29c28bd..4c61a8aeb84 100644 --- a/src/utils/createSubject.ts +++ b/src/utils/createSubject.ts @@ -1,20 +1,41 @@ +/** + * Implementation of an observable pattern. + * This pattern is used for implementing + * a publisher-subscriber system, where subscribers (observers) + * can listen to events or data changes + * emitted by a subject (observable). + */ import { Noop } from '../types'; +/** + * Observer that has a next method. + * This method is called when new data is emitted. + */ export type Observer = { next: (value: T) => void; }; - +/** + * A type representing a subscription that + * has an unsubscribe method. This method is used + * to stop receiving updates from the subject. + */ export type Subscription = { unsubscribe: Noop; }; +/** + * A type representing a subject that can be observed. + * It has an array of observers, methods to subscribe + * and unsubscribe observers, and a method to + * emit data to all observers. + */ export type Subject = { readonly observers: Observer[]; subscribe: (value: Observer) => Subscription; unsubscribe: Noop; } & Observer; -export default (): Subject => { +export default function createSubject(): Subject { let _observers: Observer[] = []; const next = (value: T) => { @@ -44,4 +65,4 @@ export default (): Subject => { subscribe, unsubscribe, }; -}; +} diff --git a/src/utils/deepEqual.ts b/src/utils/deepEqual.ts index 5156278e5aa..85669f25ee1 100644 --- a/src/utils/deepEqual.ts +++ b/src/utils/deepEqual.ts @@ -2,7 +2,26 @@ import isObject from '../utils/isObject'; import isDateObject from './isDateObject'; import isPrimitive from './isPrimitive'; - +/** + * Compares two values deeply to determine + * if they are equal. Handles primitives, + * dates, objects, and arrays. + * + * @example + * const obj1 = { a: 1, b: { c: 2 } }; + * const obj2 = { a: 1, b: { c: 2 } }; + * deepEqual(obj1, obj2) // Output: true + * + * const obj3 = { a: 1, b: { c: 3 } }; + * deepEqual(obj1, obj3) // Output: false + * + * const date1 = new Date(2020, 1, 1); + * const date2 = new Date(2020, 1, 1); + * deepEqual(date1, date2) // Output: true + * + * @remarks This function compares complex + * structures including nested objects and arrays. + */ export default function deepEqual(object1: any, object2: any) { if (isPrimitive(object1) || isPrimitive(object2)) { return object1 === object2; diff --git a/src/utils/deepMerge.ts b/src/utils/deepMerge.ts index fb6c4c9aab2..6e3d477652e 100644 --- a/src/utils/deepMerge.ts +++ b/src/utils/deepMerge.ts @@ -1,6 +1,20 @@ import isObject from './isObject'; import isPrimitive from './isPrimitive'; +/** + * Deeply merges two objects. If either target or + * source is a primitive, the source is returned. + * Otherwise, recursively merges properties of the + * source object into the target object. + * @remarks This function mutates the target object. + * @remarks uses source keys for iteration + * + * @example + * const target = { a: 1, b: { c: 3 } }; + * const source = { b: { d: 4 }, e: 5 }; + * const result = deepMerge(target, source); + * result // Output: { a: 1, b: { c: 3, d: 4 }, e: 5 } + */ export default function deepMerge< T extends Record, U extends Record, diff --git a/src/utils/fillEmptyArray.ts b/src/utils/fillEmptyArray.ts index 094400392c7..9a28834b6a8 100644 --- a/src/utils/fillEmptyArray.ts +++ b/src/utils/fillEmptyArray.ts @@ -1,2 +1,7 @@ +/** + * Converts an array to an array of + * `undefined` * values, or returns `undefined` if + * the input is not an array. + */ export default (value: T | T[]): undefined[] | undefined => Array.isArray(value) ? value.map(() => undefined) : undefined; diff --git a/src/utils/get.ts b/src/utils/get.ts index a52d61006c8..42f01df404c 100644 --- a/src/utils/get.ts +++ b/src/utils/get.ts @@ -3,6 +3,18 @@ import isNullOrUndefined from './isNullOrUndefined'; import isObject from './isObject'; import isUndefined from './isUndefined'; +/** + * Safely retrieves the value at a given + * path within an object. If the value is not found or + * is undefined, it returns the provided default value. + * @example + * ```typescript + * const obj = { a: { b: { c: 42 }}}; + * getNestedValue(obj, 'a.b.c') // Output: 42 + * getNestedValue(obj, 'a.b.x', 'default') // Output: 'default' + * getNestedValue(obj, 'a.b.c.d', 'default') // Output: 'default' + * ``` + */ export default (object: T, path?: string, defaultValue?: unknown): any => { if (!path || !isObject(object)) { return defaultValue; diff --git a/src/utils/insert.ts b/src/utils/insert.ts index 8ed53248b2f..94563c04b9e 100644 --- a/src/utils/insert.ts +++ b/src/utils/insert.ts @@ -1,5 +1,8 @@ import convertToArrayPayload from './convertToArrayPayload'; +/** + * Insert value or values into specific index, + */ export default function insert(data: T[], index: number): (T | undefined)[]; export default function insert( data: T[], diff --git a/src/utils/isEmptyObject.ts b/src/utils/isEmptyObject.ts index c31e42e1f7b..9e8fe5e00da 100644 --- a/src/utils/isEmptyObject.ts +++ b/src/utils/isEmptyObject.ts @@ -2,5 +2,9 @@ import { EmptyObject } from '../types'; import isObject from './isObject'; +/** + * Checks if the given value is an object + * with no own properties + */ export default (value: unknown): value is EmptyObject => isObject(value) && !Object.keys(value).length; diff --git a/src/utils/isKey.ts b/src/utils/isKey.ts index 248fadebfd8..6e47948ed91 100644 --- a/src/utils/isKey.ts +++ b/src/utils/isKey.ts @@ -1 +1,9 @@ +/** + * Checks if a string is a valid key consisting of alphanumeric + * characters and underscores. + * @remarks (\w qualify _ as a valid character) + * @example + * isKey("valid_key") // Output: true + * isKey("invalid key!") // Output: false + */ export default (value: string) => /^\w*$/.test(value); diff --git a/src/utils/isMessage.ts b/src/utils/isMessage.ts index 1512c353465..8da1f6d25fb 100644 --- a/src/utils/isMessage.ts +++ b/src/utils/isMessage.ts @@ -1,4 +1,12 @@ import { Message } from '../types'; import isString from '../utils/isString'; - +/** + * Checks if the given value is a string + * and therefore qualifies as a `Message`. + * @example + * const val1 = "Hello, world!"; + * const val2 = 123; + * isMessage(val1) // Output: true + * isMessage(val2) // Output: false + */ export default (value: unknown): value is Message => isString(value); diff --git a/src/utils/noop.ts b/src/utils/noop.ts index ca6a744710d..54bf00eb1bd 100644 --- a/src/utils/noop.ts +++ b/src/utils/noop.ts @@ -1 +1,4 @@ +/** + * No operation function. + */ export default function noop() {} diff --git a/src/utils/objectHasFunction.ts b/src/utils/objectHasFunction.ts index 232e50ece94..52b64a75369 100644 --- a/src/utils/objectHasFunction.ts +++ b/src/utils/objectHasFunction.ts @@ -1,5 +1,7 @@ import isFunction from './isFunction'; - +/** + * Checks if an object contains at least one function as a property. + */ export default (data: T): boolean => { for (const key in data) { if (isFunction(data[key])) { diff --git a/src/utils/prepend.ts b/src/utils/prepend.ts index 078aeabe8fa..f1a61f000e2 100644 --- a/src/utils/prepend.ts +++ b/src/utils/prepend.ts @@ -1,5 +1,7 @@ import convertToArrayPayload from './convertToArrayPayload'; - +/** + * Add's value or an array of values to the beginning of an array. + */ export default (data: T[], value: T | T[]): T[] => [ ...convertToArrayPayload(value), ...convertToArrayPayload(data), diff --git a/src/utils/remove.ts b/src/utils/remove.ts index efe2af518d4..aa292a0e9f1 100644 --- a/src/utils/remove.ts +++ b/src/utils/remove.ts @@ -1,7 +1,15 @@ import compact from './compact'; import convertToArrayPayload from './convertToArrayPayload'; import isUndefined from './isUndefined'; - +/** + * Removes elements from an array at the + * specified index or indexes. If no index is provided, + * an empty array is returned. + * @example + * const arr = ['a', 'b', 'c', 'd']; + * removeElements(arr, [1, 3]); // Output: ['a', 'c'] + * removeElements(arr, 2); // Output: ['a', 'b', 'd'] + */ function removeAtIndexes(data: T[], indexes: number[]): T[] { let i = 0; const temp = [...data]; diff --git a/src/utils/set.ts b/src/utils/set.ts index f26e3c196d2..0e5655364db 100644 --- a/src/utils/set.ts +++ b/src/utils/set.ts @@ -4,6 +4,16 @@ import isKey from './isKey'; import isObject from './isObject'; import stringToPath from './stringToPath'; +/** + * Sets the value at the specified path of the object. + * If a portion of the path does not exist, it is created. + * @example + * ```typescript + * const object = {}; + * setValueAtPath(object, 'a.b.c', 42); + * Output: { a: { b: { c: 42 } } } + * ``` + */ export default (object: FieldValues, path: string, value?: unknown) => { let index = -1; const tempPath = isKey(path) ? [path] : stringToPath(path); diff --git a/src/utils/sleep.ts b/src/utils/sleep.ts index e1495dcd315..6fc1717c8bb 100644 --- a/src/utils/sleep.ts +++ b/src/utils/sleep.ts @@ -1,2 +1,6 @@ +/** + * Promises that resolves after given amount of time. + * in milliseconds. + */ export default (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); diff --git a/src/utils/stringToPath.ts b/src/utils/stringToPath.ts index 57ba898920b..e9607cf1b18 100644 --- a/src/utils/stringToPath.ts +++ b/src/utils/stringToPath.ts @@ -1,4 +1,20 @@ import compact from './compact'; -export default (input: string): string[] => - compact(input.replace(/["|']|\]/g, '').split(/\.|\[/)); +/** + * Converts a input string into an array of 1ch long keys (Path). + * @param {string} input - The input string to process. + * @returns {Path} The processed array of string segments 1ch long. + * @example + * stringToPath('a.b.c'); // Output: ['a', 'b', 'c'] + * stringToPath('["a.b[c]"]'); // Output: ['a', 'b', 'c'] + * stringToPath('a[0].b["c"]') // Output: ['a', '0', 'b', 'c'] + */ +export default function stringToPath(input: string): string[] { + // Input: a["b"]['c'].d[0] -> Output: 'a[b[c.d[0' + const removeQuotesAndBrackets = /["|']|\]/g; + // Input: 'a[b[c.d[0' -> ['a', 'b', 'c', 'd', '0'] + const splitOnDotOrOpenBracket = /\.|\[/; + return compact( + input.replace(removeQuotesAndBrackets, '').split(splitOnDotOrOpenBracket), + ); +} diff --git a/src/utils/swap.ts b/src/utils/swap.ts index 8b685a6fdc5..0c02cfaf5a7 100644 --- a/src/utils/swap.ts +++ b/src/utils/swap.ts @@ -1,3 +1,11 @@ +/** + * Swaps the elements at the specified indices in + * the array. + * @remarks Mutates the original array. + * @example + * const arr = [1, 2, 3, 4]; + * swapElements(arr, 1, 3); // Output: [1, 4, 3, 2] + */ export default (data: T[], indexA: number, indexB: number): void => { [data[indexA], data[indexB]] = [data[indexB], data[indexA]]; }; diff --git a/src/utils/unset.ts b/src/utils/unset.ts index 58c9f25499d..363f70ffeea 100644 --- a/src/utils/unset.ts +++ b/src/utils/unset.ts @@ -24,6 +24,14 @@ function isEmptyArray(obj: unknown[]) { return true; } +/** + * Removes the property at `path` of `object`. + * @remarks `Path` could be string or 'a.b.c' or 'a[0].b.c' + * @example + * const obj = { a: { b: { c: 42 } } }; + * unset(obj, 'a.b.c'); // Output: { a: { b: {} } } + * unset(obj, 'a.b'); // Output: { a: {} } + */ export default function unset(object: any, path: string | (string | number)[]) { const paths = Array.isArray(path) ? path diff --git a/src/utils/update.ts b/src/utils/update.ts index b3ac5f35c93..f35eceab279 100644 --- a/src/utils/update.ts +++ b/src/utils/update.ts @@ -1,3 +1,12 @@ +/** + * Replaces the element at the specified index + * in the array with a new value and returns the modified array. + * @remarks This function mutates the original array. + * @example + * const fields = ['a', 'b', 'c']; + * const updatedFields = updateFieldValue(fields, 1, 'x'); + * updatedFields // Output: ['a', 'x', 'c'] + */ export default (fieldValues: T[], index: number, value: T) => { fieldValues[index] = value; return fieldValues;