chore: disable sorting of non-core folder object fields (#4547)

* chore: disable sorting of non-core folder object fields

* chore: ci error
pull/48/MERGE
Vben 2024-09-29 22:03:17 +08:00 committed by GitHub
parent b7776c5148
commit d1e1256202
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 126 additions and 31 deletions

View File

@ -15,10 +15,17 @@ const customConfig: Linter.Config[] = [
},
},
{
files: ['packages/effects/**/**', 'packages/types/**/**'],
files: [
'apps/**/**',
'packages/effects/**/**',
'packages/utils/**/**',
'packages/types/**/**',
'packages/locales/**/**',
],
ignores: restrictedImportIgnores,
rules: {
'perfectionist/sort-interfaces': 'off',
'perfectionist/sort-objects': 'off',
},
},
{

View File

@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest';
import { bindMethods } from '../util';
import { bindMethods, getNestedValue } from '../util';
class TestClass {
public value: string;
@ -78,3 +78,79 @@ describe('bindMethods', () => {
expect(value).toBe('test');
});
});
describe('getNestedValue', () => {
interface UserProfile {
age: number;
name: string;
}
interface UserSettings {
theme: string;
}
interface Data {
user: {
profile: UserProfile;
settings: UserSettings;
};
}
const data: Data = {
user: {
profile: {
age: 25,
name: 'Alice',
},
settings: {
theme: 'dark',
},
},
};
it('should get a nested value when the path is valid', () => {
const result = getNestedValue(data, 'user.profile.name');
expect(result).toBe('Alice');
});
it('should return undefined for non-existent property', () => {
const result = getNestedValue(data, 'user.profile.gender');
expect(result).toBeUndefined();
});
it('should return undefined when accessing a non-existent deep path', () => {
const result = getNestedValue(data, 'user.nonexistent.field');
expect(result).toBeUndefined();
});
it('should return undefined if a middle level is undefined', () => {
const result = getNestedValue({ user: undefined }, 'user.profile.name');
expect(result).toBeUndefined();
});
it('should return the correct value for a nested setting', () => {
const result = getNestedValue(data, 'user.settings.theme');
expect(result).toBe('dark');
});
it('should work for a single-level path', () => {
const result = getNestedValue({ a: 1, b: 2 }, 'b');
expect(result).toBe(2);
});
it('should return the entire object if path is empty', () => {
expect(() => getNestedValue(data, '')()).toThrow();
});
it('should handle paths with array indexes', () => {
const complexData = { list: [{ name: 'Item1' }, { name: 'Item2' }] };
const result = getNestedValue(complexData, 'list.1.name');
expect(result).toBe('Item2');
});
it('should return undefined when accessing an out-of-bounds array index', () => {
const complexData = { list: [{ name: 'Item1' }] };
const result = getNestedValue(complexData, 'list.2.name');
expect(result).toBeUndefined();
});
});

View File

@ -1 +1,10 @@
import { createDefu } from 'defu';
export { createDefu as createMerge, defu as merge } from 'defu';
export const mergeWithArrayOverride = createDefu((originObj, key, updates) => {
if (Array.isArray(originObj[key]) && Array.isArray(updates)) {
originObj[key] = updates;
return true;
}
});

View File

@ -17,3 +17,28 @@ export function bindMethods<T extends object>(instance: T): void {
}
});
}
/**
*
* @param obj -
* @param path - 使
* @returns undefined
*/
export function getNestedValue<T>(obj: T, path: string): any {
if (typeof path !== 'string' || path.length === 0) {
throw new Error('Path must be a non-empty string');
}
// 把路径字符串按 "." 分割成数组
const keys = path.split('.') as (number | string)[];
let current: any = obj;
for (const key of keys) {
if (current === null || current === undefined) {
return undefined;
}
current = current[key as keyof typeof current];
}
return current;
}

View File

@ -1,24 +1,7 @@
// 假设这个文件为 FormApi.ts
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { FormApi } from '../src/form-api';
vi.mock('@vben-core/shared/utils', () => ({
bindMethods: vi.fn(),
createMerge: vi.fn((mergeFn) => {
return (stateOrFn: any, prev: any) => {
mergeFn(prev, 'key', stateOrFn);
return { ...prev, ...stateOrFn };
};
}),
isFunction: (fn: any) => typeof fn === 'function',
StateHandler: vi.fn().mockImplementation(() => ({
reset: vi.fn(),
setConditionTrue: vi.fn(),
waitForCondition: vi.fn().mockResolvedValue(true),
})),
}));
describe('formApi', () => {
let formApi: FormApi;
@ -128,7 +111,6 @@ describe('formApi', () => {
it('should unmount form and reset state', () => {
formApi.unmounted();
expect(formApi.isMounted).toBe(false);
expect(formApi.stateHandler.reset).toHaveBeenCalled();
});
it('should validate form', async () => {

View File

@ -12,20 +12,13 @@ import { toRaw } from 'vue';
import { Store } from '@vben-core/shared/store';
import {
bindMethods,
createMerge,
isFunction,
mergeWithArrayOverride,
StateHandler,
} from '@vben-core/shared/utils';
import { objectPick } from '@vueuse/core';
const merge = createMerge((originObj, key, updates) => {
if (Array.isArray(originObj[key]) && Array.isArray(updates)) {
originObj[key] = updates;
return true;
}
});
function getDefaultState(): VbenFormProps {
return {
actionWrapperClass: '',
@ -218,10 +211,10 @@ export class FormApi {
) {
if (isFunction(stateOrFn)) {
this.store.setState((prev) => {
return merge(stateOrFn(prev), prev);
return mergeWithArrayOverride(stateOrFn(prev), prev);
});
} else {
this.store.setState((prev) => merge(stateOrFn, prev));
this.store.setState((prev) => mergeWithArrayOverride(stateOrFn, prev));
}
}
@ -287,7 +280,10 @@ export class FormApi {
currentSchema.forEach((schema, index) => {
const updatedData = updatedMap[schema.fieldName];
if (updatedData) {
currentSchema[index] = merge(updatedData, schema) as FormSchema;
currentSchema[index] = mergeWithArrayOverride(
updatedData,
schema,
) as FormSchema;
}
});
this.setState({ schema: currentSchema });