redoc/src/utils/helpers.ts

132 lines
3.9 KiB
TypeScript
Raw Normal View History

import slugify from 'slugify';
2017-10-12 00:01:37 +03:00
/**
* Maps over array passing `isLast` bool to iterator as the second arguemnt
*/
export function mapWithLast<T, P>(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<T, P>(
object: Dict<T>,
iteratee: (val: T, key: string, obj: Dict<T>) => P,
): Dict<P> {
const res: { [key: string]: P } = {};
2018-01-22 21:30:53 +03:00
for (const key in object) {
2017-10-12 00:01:37 +03:00
if (object.hasOwnProperty(key)) {
res[key] = iteratee(object[key], key, object);
}
}
return res;
}
/**
* flattens collection using `prop` field as a children
2018-01-22 21:30:53 +03:00
* @param collectionItems collection items
2017-10-12 00:01:37 +03:00
* @param prop item property with child elements
*/
2018-01-22 21:30:53 +03:00
export function flattenByProp<T extends object, P extends keyof T>(
collectionItems: T[],
prop: P,
): T[] {
2017-10-12 00:01:37 +03:00
const res: T[] = [];
const iterate = (items: T[]) => {
2018-01-22 21:30:53 +03:00
for (const item of items) {
2017-10-12 00:01:37 +03:00
res.push(item);
if (item[prop]) {
2018-02-08 00:21:18 +03:00
iterate((item[prop] as any) as T[]);
2017-10-12 00:01:37 +03:00
}
}
};
2018-01-22 21:30:53 +03:00
iterate(collectionItems);
2017-10-12 00:01:37 +03:00
return res;
}
export function stripTrailingSlash(path: string): string {
if (path.endsWith('/')) {
return path.substring(0, path.length - 1);
}
return path;
}
2018-01-22 21:30:53 +03:00
export function isNumeric(n: any): n is number {
return !isNaN(parseFloat(n)) && isFinite(n);
}
2017-11-21 17:33:22 +03:00
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}`;
}
}
2017-11-22 15:00:42 +03:00
// credits https://stackoverflow.com/a/46973278/1749888
export const mergeObjects = <T extends object = object>(target: T, ...sources: T[]): T => {
if (!sources.length) {
return target;
}
const source = sources.shift();
if (source === undefined) {
return target;
}
if (isMergebleObject(target) && isMergebleObject(source)) {
2018-01-22 21:30:53 +03:00
Object.keys(source).forEach((key: string) => {
2017-11-22 15:00:42 +03:00
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);
};
/**
* slugify() returns empty string when failed to slugify.
* so try to return minimun slugified-string with failed one which keeps original value
* the regex codes are referenced with https://gist.github.com/mathewbyrne/1280286
*/
export function safeSlugify(value: string): string {
return slugify(value) ||
value.toString().toLowerCase()
.replace(/\s+/g, '-') // Replace spaces with -
.replace(/&/g, '-and-') // Replace & with 'and'
.replace(/\--+/g, '-') // Replace multiple - with single -
.replace(/^-+/, '') // Trim - from start of text
.replace(/-+$/, ''); // Trim - from end of text
}