feat: Refactor layouts in www
This commit is contained in:
66 files changed
+1805
-352
No files matched your search
@@ -1,4 +1,5 @@
|
||||
export * from './types/index.js';
|
||||
export * from './functions/index.js';
|
||||
export * from './object/index.js';
|
||||
export * from './window/localStorage.js';
|
||||
export * from './react/index.js';
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Object utilities for common operations on objects.
|
||||
*/
|
||||
|
||||
export { merge } from './merge/merge';
|
||||
export type { MergeOptions } from './merge/merge';
|
||||
@@ -0,0 +1,108 @@
|
||||
import { merge } from './merge';
|
||||
|
||||
/**
|
||||
* Test suite for the merge utility function.
|
||||
*/
|
||||
describe('shared/helpers/object/merge', () => {
|
||||
it('deeply merges two objects, preserving source values by default', () => {
|
||||
const source = { a: 1, b: { c: 2 } };
|
||||
const target = { a: 3, b: { c: 4, d: 5 }, e: 6 };
|
||||
const result = merge(source, target);
|
||||
|
||||
expect(result).toEqual({
|
||||
a: 1, // force is false, so 1 is preserved
|
||||
b: {
|
||||
c: 2, // force is false, so 2 is preserved
|
||||
d: 5, // added because it was not in source
|
||||
},
|
||||
e: 6, // added because it was not in source
|
||||
});
|
||||
});
|
||||
|
||||
it('deeply merges two objects and overrides values when force is true', () => {
|
||||
const source = { a: 1, b: { c: 2 } };
|
||||
const target = { a: 3, b: { c: 4, d: 5 }, e: 6 };
|
||||
const result = merge(source, target, { force: true });
|
||||
|
||||
expect(result).toEqual({
|
||||
a: 3,
|
||||
b: {
|
||||
c: 4,
|
||||
d: 5,
|
||||
},
|
||||
e: 6,
|
||||
});
|
||||
});
|
||||
|
||||
it('concatenates arrays when both are present at the same key', () => {
|
||||
const source = { a: [1, 2] };
|
||||
const target = { a: [3, 4] };
|
||||
const result = merge(source, target);
|
||||
|
||||
expect(result).toEqual({ a: [1, 2, 3, 4] });
|
||||
});
|
||||
|
||||
it('handles primitive values at the root', () => {
|
||||
expect(merge(1, 2)).toBe(1);
|
||||
expect(merge(1, 2, { force: true })).toBe(2);
|
||||
});
|
||||
|
||||
it('handles mixed types between source and target', () => {
|
||||
const source = { a: { b: 1 } };
|
||||
const target = { a: [1, 2] };
|
||||
|
||||
// Source wins because force is false
|
||||
expect(merge(source, target)).toEqual({ a: { b: 1 } });
|
||||
|
||||
// Target wins because force is true
|
||||
expect(merge(source, target, { force: true })).toEqual({ a: [1, 2] });
|
||||
});
|
||||
|
||||
it('handles deep recursion with arrays and objects', () => {
|
||||
const source = {
|
||||
users: [
|
||||
{ id: 1, name: 'John' },
|
||||
{ id: 2, name: 'Jane' },
|
||||
],
|
||||
config: {
|
||||
retries: 3,
|
||||
enabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
const target = {
|
||||
users: [{ id: 3, name: 'Doe' }],
|
||||
config: {
|
||||
retries: 5,
|
||||
timeout: 1000,
|
||||
},
|
||||
};
|
||||
|
||||
const result = merge(source, target);
|
||||
|
||||
expect(result).toEqual({
|
||||
users: [
|
||||
{ id: 1, name: 'John' },
|
||||
{ id: 2, name: 'Jane' },
|
||||
{ id: 3, name: 'Doe' },
|
||||
],
|
||||
config: {
|
||||
retries: 3,
|
||||
enabled: true,
|
||||
timeout: 1000,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('preserves the source when target has undefined values', () => {
|
||||
const source = { a: 1 };
|
||||
const target = { a: undefined };
|
||||
expect(merge(source, target)).toEqual({ a: 1 });
|
||||
});
|
||||
|
||||
it('replaces undefined source with target', () => {
|
||||
const source = { a: undefined };
|
||||
const target = { a: 1 };
|
||||
expect(merge(source, target)).toEqual({ a: 1 });
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,76 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unnecessary-type-assertion */
|
||||
/**
|
||||
* Options for the merge utility.
|
||||
*/
|
||||
export interface MergeOptions {
|
||||
/**
|
||||
* Whether to override existing primitive values in the source with values from the target.
|
||||
* If false, existing primitive values are preserved.
|
||||
* @default false
|
||||
*/
|
||||
force?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deeply merges two values (objects, arrays, or primitives).
|
||||
*
|
||||
* Behavior:
|
||||
* - If both values are arrays, they are concatenated.
|
||||
* - If both values are objects, they are merged recursively.
|
||||
* - If encounter a primitive value, it is overridden only if `force` is set to true.
|
||||
* - If a key exists in target but not in source, it is added to the result.
|
||||
*
|
||||
* @param source - The source value to merge from
|
||||
* @param target - The target value to merge with
|
||||
* @param options - Merge configuration options
|
||||
* @returns The merged result
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* merge({ a: 1 }, { a: 2 }) // { a: 1 }
|
||||
* merge({ a: 1 }, { a: 2 }, { force: true }) // { a: 2 }
|
||||
* merge({ a: [1] }, { a: [2] }) // { a: [1, 2] }
|
||||
* merge({ a: { b: 1 } }, { a: { c: 2 } }) // { a: { b: 1, c: 2 } }
|
||||
* ```
|
||||
*/
|
||||
export function merge<T = any>(source: any, target: any, options: MergeOptions = {}): T {
|
||||
const { force = false } = options;
|
||||
|
||||
// If target is undefined, source wins
|
||||
if (target === undefined) {
|
||||
return source;
|
||||
}
|
||||
// If source is undefined, target wins
|
||||
if (source === undefined) {
|
||||
return target;
|
||||
}
|
||||
|
||||
const sourceIsArray = Array.isArray(source);
|
||||
const targetIsArray = Array.isArray(target);
|
||||
|
||||
// If both are arrays, concatenate
|
||||
if (sourceIsArray && targetIsArray) {
|
||||
return [...source, ...target] as any;
|
||||
}
|
||||
|
||||
const sourceIsObject = source !== null && typeof source === 'object' && !sourceIsArray;
|
||||
const targetIsObject = target !== null && typeof target === 'object' && !targetIsArray;
|
||||
|
||||
// If both are objects, deep merge
|
||||
if (sourceIsObject && targetIsObject) {
|
||||
const result = { ...source };
|
||||
for (const key in target) {
|
||||
if (Object.prototype.hasOwnProperty.call(target, key)) {
|
||||
if (key in source) {
|
||||
result[key] = merge(source[key], target[key], options);
|
||||
} else {
|
||||
result[key] = target[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return result as any;
|
||||
}
|
||||
|
||||
// If we encounter primitive values or mixed types, override only if force is true
|
||||
return force ? target : source;
|
||||
}
|
||||
Reference in new issue
Block a user