/** * Maps over array passing `isLast` bool to iterator as the second arguemnt */ export function mapWithLast(array: T[], iteratee: (item: T, isLast: boolean) => P) { const res: P[] = []; for (let i = 0; i < array.length - 1; i++) { res.push(iteratee(array[i], false)); } if (array.length !== 0) { res.push(iteratee(array[array.length - 1], true)); } return res; } /** * Creates an object with the same keys as object and values generated by running each * own enumerable string keyed property of object thru iteratee. * The iteratee is invoked with three arguments: (value, key, object). * * @param object the object to iterate over * @param iteratee the function invoked per iteration. */ export function mapValues( object: Dict, iteratee: (val: T, key: string, obj: Dict) => P, ): Dict

{ const res: { [key: string]: P } = {}; for (let key in object) { if (object.hasOwnProperty(key)) { res[key] = iteratee(object[key], key, object); } } return res; } /** * flattens collection using `prop` field as a children * @param items collection items * @param prop item property with child elements */ export function flattenByProp(items: T[], prop: P): T[] { const res: T[] = []; const iterate = (items: T[]) => { for (let i = 0; i < items.length; i++) { const item = items[i]; res.push(item); if (item[prop]) { iterate(item[prop]); } } }; iterate(items); return res; } export function stripTrailingSlash(path: string): string { if (path.endsWith('/')) { return path.substring(0, path.length - 1); } return path; } export function isAbsolutePath(path: string): boolean { return /^(?:[a-z]+:)?/i.test(path); } export function isNumeric(n: any): n is Number { return !isNaN(parseFloat(n)) && isFinite(n); } export function appendToMdHeading(md: string, heading: string, content: string) { // if heading is already in md and append to the end of it const testRegex = new RegExp(`(^|\\n)#\\s?${heading}\\s*\\n`, 'i'); const replaceRegex = new RegExp(`((\\n|^)#\\s*${heading}\\s*(\\n|$)(?:.|\\n)*?)(\\n#|$)`, 'i'); if (testRegex.test(md)) { return md.replace(replaceRegex, `$1\n\n${content}\n$4`); } else { // else append heading itself const br = md === '' || md.endsWith('\n\n') ? '' : md.endsWith('\n') ? '\n' : '\n\n'; return `${md}${br}# ${heading}\n\n${content}`; } } // credits https://stackoverflow.com/a/46973278/1749888 export const mergeObjects = (target: T, ...sources: T[]): T => { if (!sources.length) { return target; } const source = sources.shift(); if (source === undefined) { return target; } if (isMergebleObject(target) && isMergebleObject(source)) { Object.keys(source).forEach(function(key: string) { if (isMergebleObject(source[key])) { if (!target[key]) { target[key] = {}; } mergeObjects(target[key], source[key]); } else { target[key] = source[key]; } }); } return mergeObjects(target, ...sources); }; const isObject = (item: any): boolean => { return item !== null && typeof item === 'object'; }; const isMergebleObject = (item): boolean => { return isObject(item) && !Array.isArray(item); };