chore: trim shared lodash helpers

This commit is contained in:
2026-06-12 17:35:47 +08:00
parent 0cbbbfacc7
commit e6cf7efc59
2 changed files with 122 additions and 4 deletions
@@ -11,11 +11,8 @@ export * from './resources';
export * from './state-handler'; export * from './state-handler';
export * from './to'; export * from './to';
export * from './tree'; export * from './tree';
export * from './object';
export * from './unique'; export * from './unique';
export * from './update-css-variables'; export * from './update-css-variables';
export * from './util'; export * from './util';
export * from './window'; export * from './window';
export { default as cloneDeep } from 'lodash.clonedeep';
export { default as get } from 'lodash.get';
export { default as isEqual } from 'lodash.isequal';
export { default as set } from 'lodash.set';
@@ -0,0 +1,121 @@
type KeyPath = Array<number | string> | number | string;
const keyPathPattern = /[^.[\]]+|\[(\d+)\]/g;
function toKeyPath(path: KeyPath): Array<number | string> {
if (Array.isArray(path)) {
return path;
}
if (typeof path === 'number') {
return [path];
}
const keys: Array<number | string> = [];
path.replace(keyPathPattern, (match, index) => {
keys.push(index === undefined ? match : Number(index));
return match;
});
return keys;
}
function cloneDeep<T>(value: T): T {
if (value === null || typeof value !== 'object') {
return value;
}
if (value instanceof Date) {
return new Date(value) as T;
}
if (Array.isArray(value)) {
return value.map((item) => cloneDeep(item)) as T;
}
if (value instanceof Map) {
return new Map(
[...value.entries()].map(([key, item]) => [cloneDeep(key), cloneDeep(item)]),
) as T;
}
if (value instanceof Set) {
return new Set([...value.values()].map((item) => cloneDeep(item))) as T;
}
const result = {} as Record<string, unknown>;
for (const [key, item] of Object.entries(value)) {
result[key] = cloneDeep(item);
}
return result as T;
}
function get<T = any>(
object: any,
path: KeyPath,
defaultValue?: T,
): T | undefined {
let result = object;
for (const key of toKeyPath(path)) {
if (result == null) {
return defaultValue;
}
result = result[key as keyof typeof result];
}
return result === undefined ? defaultValue : result;
}
function set<T extends Record<string, any>>(
object: T,
path: KeyPath,
value: unknown,
): T {
const keys = toKeyPath(path);
let cursor: Record<string, any> = object;
keys.forEach((key, index) => {
if (index === keys.length - 1) {
cursor[key] = value;
return;
}
const nextKey = keys[index + 1];
cursor[key] ??= typeof nextKey === 'number' ? [] : {};
cursor = cursor[key];
});
return object;
}
function isEqual(left: unknown, right: unknown): boolean {
if (Object.is(left, right)) {
return true;
}
if (left instanceof Date && right instanceof Date) {
return left.getTime() === right.getTime();
}
if (
left === null ||
right === null ||
typeof left !== 'object' ||
typeof right !== 'object'
) {
return false;
}
if (Array.isArray(left) || Array.isArray(right)) {
if (!Array.isArray(left) || !Array.isArray(right)) {
return false;
}
return (
left.length === right.length &&
left.every((item, index) => isEqual(item, right[index]))
);
}
const leftKeys = Object.keys(left);
const rightKeys = Object.keys(right);
return (
leftKeys.length === rightKeys.length &&
leftKeys.every((key) =>
isEqual(
(left as Record<string, unknown>)[key],
(right as Record<string, unknown>)[key],
),
)
);
}
export { cloneDeep, get, isEqual, set };