mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
0139b91b5d
* chore: Replace lodash with es-toolkit Migrate all direct lodash imports to es-toolkit/compat for a smaller, faster, lodash-compatible utility library. Transitive lodash usage from other packages remains unchanged. * fix: Restore isPlainObject semantics in CanCan policy The lodash migration aliased `isObject` to `lodash/isPlainObject` and the codemod incorrectly mapped the local name to es-toolkit's `isObject`, which also returns true for arrays and functions. This caused condition objects in policy definitions to be skipped, breaking authorization checks across the codebase. * fix: Restore unicode-aware length counting in validators es-toolkit/compat's size() returns string.length, while lodash's _.size() counts unicode code points. Switch to [...value].length to preserve the previous behavior so multi-byte characters like emoji count as one.
49 lines
1.2 KiB
TypeScript
49 lines
1.2 KiB
TypeScript
import emojiRegex from "emoji-regex";
|
|
import { deburr } from "es-toolkit/compat";
|
|
import naturalSort from "natural-sort";
|
|
|
|
type NaturalSortOptions = {
|
|
caseSensitive?: boolean;
|
|
direction?: "asc" | "desc";
|
|
};
|
|
|
|
const sorter = naturalSort();
|
|
const regex = emojiRegex();
|
|
|
|
const stripEmojis = (value: string) => value.replace(regex, "");
|
|
|
|
const cleanValue = (value: string) => stripEmojis(deburr(value));
|
|
|
|
function getSortByField<T extends object>(
|
|
item: T,
|
|
keyOrCallback: string | ((item: T) => string)
|
|
) {
|
|
const field =
|
|
typeof keyOrCallback === "string"
|
|
? (item as Record<string, unknown>)[keyOrCallback]
|
|
: keyOrCallback(item);
|
|
return cleanValue(typeof field === "string" ? field : "");
|
|
}
|
|
|
|
function naturalSortBy<T extends object>(
|
|
items: T[],
|
|
key: string | ((item: T) => string),
|
|
sortOptions?: NaturalSortOptions
|
|
): T[] {
|
|
if (!items) {
|
|
return [];
|
|
}
|
|
const sort = sortOptions
|
|
? naturalSort({
|
|
caseSensitive: sortOptions.caseSensitive,
|
|
direction: sortOptions.direction === "desc" ? "desc" : undefined,
|
|
})
|
|
: sorter;
|
|
|
|
return items.sort((a: T, b: T) =>
|
|
sort(getSortByField(a, key), getSortByField(b, key))
|
|
);
|
|
}
|
|
|
|
export default naturalSortBy;
|