UNPKG

insta-toc

Version:

Simultaneously generate, update, and maintain a table of contents for your notes in real time.

83 lines (69 loc) 3.2 kB
export function assert<T>(value: T | undefined | null, message?: string): asserts value is NonNullable<T> { if (value == null || value === undefined) { throw new Error(message ?? "Expected value to be defined"); } } export function isNothing<T>(value: T): value is Extract<T, null | undefined> { return value === null || value === undefined; } export function sortRecord<T extends Record<string, any>>( obj: T, sortFn?: (a: [keyof T, T[keyof T]?], b: [keyof T, T[keyof T]?]) => number ): Record<string, T[keyof T]> { const entries = Object.entries(obj); const sortedEntries = entries.sort(sortFn ? sortFn : ([ a ]: string[], [ b ]: string[]) => a.localeCompare(b)); return Object.fromEntries(sortedEntries); } export function sortMap<K, V>(map: Map<K, V>, sortFn?: (a: [K, V], b: [K, V]) => number): Map<K, V> { const entries = [ ...map.entries() ]; const sortedEntries = entries.sort(sortFn ? sortFn : ([ a ], [ b ]) => String(a).localeCompare(String(b))); return new Map(sortedEntries); } /** * Check if a value is an object that can be merged * @param value The input value to check for being a mergeable object * @returns A boolean value indicating whether the input value is a mergeable object (true) or not (false), based on whether it is a non-null object that is not an array */ export function isRecord<T extends Record<string, unknown>>(value: unknown): value is T { if (isNothing(value)) return false; let maybeRecord: string; try { maybeRecord = JSON.stringify(value, undefined, 2) as string; } catch { return false; } const hasBrackets = maybeRecord.startsWith("{") && maybeRecord.endsWith("}"); const isObjectNotArray = value instanceof Object && !Array.isArray(value); return hasBrackets && isObjectNotArray; } export function isMap<K = unknown, V = unknown>(value: unknown): value is Map<K, V> { return value instanceof Map; } RegExp.isRegexPattern = function isRegexPattern(str: string): str is `/${string}/` { // Checks if the string starts and ends with '/' if (!/^\/.*\/$/.test(str)) return false; // `/<str>/<flags>` => <str> const endPattern = str.lastIndexOf("/"); const pattern = str.slice(1, endPattern); try { // Attempting to create a new RegExp instance will throw a SyntaxError // at runtime if the pattern is invalid. new RegExp(pattern); } catch { return false; } return true; }; RegExp.escape = function (value: string): string { return value.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); }; export function injectGlobals(): void { (globalThis as typeof globalThis & { assert: typeof assert; }).assert = assert; (globalThis as typeof globalThis & { isNothing: typeof isNothing; }).isNothing = isNothing; (globalThis as typeof globalThis & { sortRecord: typeof sortRecord; }).sortRecord = sortRecord; (globalThis as typeof globalThis & { sortMap: typeof sortMap; }).sortMap = sortMap; (globalThis as typeof globalThis & { isRecord: typeof isRecord; }).isRecord = isRecord; (globalThis as typeof globalThis & { isMap: typeof isMap; }).isMap = isMap; }