UNPKG

@acusti/uniquify

Version:

Generate a unique string (e.g. for filenames or url slugs) using an incremental counter

144 lines (143 loc) 4.2 kB
const escapeRegExp = (str) => str.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&"); const getIn = (item, path) => { let value = item; for (let i = 0; i < path.length && value != null; i++) { value = value[path[i]]; } return value; }; const propertyValueExists = ({ caseSensitive, items, propertyPath, propertyPathAlternate, value }) => { value = caseSensitive ? value : value.toLowerCase(); return items.some((item) => { if (item == null) return false; let itemPropertyValue = propertyPath ? getIn(item, propertyPath) : item; if (itemPropertyValue == null && propertyPathAlternate) { itemPropertyValue = getIn(item, propertyPathAlternate); } if (typeof itemPropertyValue !== "string") { return false; } if (caseSensitive) { return itemPropertyValue === value; } return itemPropertyValue.toLowerCase() === value; }); }; const counterRegexString = "(?:[1-9][0-9]|[2-9])"; const counterFromOneRegexString = "(?:[1-9][0-9]|[1-9])"; const otherInstanceExists = ({ caseSensitive, items, propertyPath, propertyPathAlternate, separator, value, valueStem }) => { const existingRegexString = `^${escapeRegExp( valueStem )}(?:${separator}${counterRegexString})?$`; const existingRegex = caseSensitive ? new RegExp(existingRegexString) : new RegExp(existingRegexString, "i"); return items.some((item) => { if (item == null) return false; let itemPropertyValue = propertyPath ? getIn(item, propertyPath) : item; if (itemPropertyValue == null && propertyPathAlternate) { itemPropertyValue = getIn(item, propertyPathAlternate); } if (typeof itemPropertyValue !== "string") { return false; } if (itemPropertyValue.toLowerCase() === value) { return false; } return existingRegex.test(itemPropertyValue); }); }; const uniquify = ({ caseSensitive = false, identify1AsCounter = false, isSuffixOptional = false, items, propertyPath, propertyPathAlternate, separator = " ", suffix = "", value }) => { const valueIdxRegexString = `${separator}${identify1AsCounter ? counterFromOneRegexString : counterRegexString}$`; const valueIdxRegex = caseSensitive ? new RegExp(valueIdxRegexString) : new RegExp(valueIdxRegexString, "i"); let suffixRegexString = ""; let suffixRegex = null; if (suffix) { const optionalValueIdxRegexString = `(?:${valueIdxRegexString.slice(0, -1)})?$`; suffixRegexString = `${separator}${escapeRegExp( suffix )}${optionalValueIdxRegexString}`; suffixRegex = caseSensitive ? new RegExp(suffixRegexString) : new RegExp(suffixRegexString, "i"); if (!suffixRegex.test(value) && !valueIdxRegex.test(value)) { if (isSuffixOptional && !propertyValueExists({ caseSensitive, items, propertyPath, propertyPathAlternate, value })) { return value; } value += `${separator}${suffix}`; } } let valueIdx = 1; while (propertyValueExists({ caseSensitive, items, propertyPath, propertyPathAlternate, value })) { if (valueIdx === 1) { const existingValueMatch = value.match(valueIdxRegex); if (existingValueMatch) { const existingValueIdx = parseInt( existingValueMatch[0].substring(separator.length), 10 ); if (identify1AsCounter) { valueIdx = existingValueIdx; } else { const valueStem = value.replace(valueIdxRegex, ""); if (otherInstanceExists({ caseSensitive, items, propertyPath, propertyPathAlternate, separator, value, valueStem })) { valueIdx = existingValueIdx; } else { value += `${separator}2`; } } } if (suffix && suffixRegex && !suffixRegex.test(value)) { value = value.replace(valueIdxRegex, `${separator}${suffix}$&`); } } valueIdx++; value = `${value.replace(valueIdxRegex, "")}${separator}${valueIdx}`; } return value; }; export { uniquify as default, escapeRegExp }; //# sourceMappingURL=uniquify.js.map