chore: update helpers

pull/48/MERGE
vben 2024-06-02 10:29:17 +08:00
parent 8f1b054bb1
commit fc423c3657
19 changed files with 361 additions and 353 deletions

View File

@ -7,7 +7,7 @@
"repository": {
"type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "packages/@vben-core/helpers"
"directory": "packages/@vben-core/forward/helpers"
},
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"scripts": {

View File

@ -0,0 +1,132 @@
import { describe, expect, it } from 'vitest';
import { flattenObject } from './flatten-object';
describe('flattenObject', () => {
it('should flatten a nested object correctly', () => {
const nestedObject = {
language: 'en',
notifications: {
email: true,
push: {
sound: true,
vibration: false,
},
},
theme: 'light',
};
const expected = {
language: 'en',
notificationsEmail: true,
notificationsPushSound: true,
notificationsPushVibration: false,
theme: 'light',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle empty objects', () => {
const nestedObject = {};
const expected = {};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle objects with primitive values', () => {
const nestedObject = {
active: true,
age: 30,
name: 'Alice',
};
const expected = {
active: true,
age: 30,
name: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle objects with null values', () => {
const nestedObject = {
user: {
age: null,
name: null,
},
};
const expected = {
userAge: null,
userName: null,
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle nested empty objects', () => {
const nestedObject = {
a: {},
b: { c: {} },
};
const expected = {};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle arrays within objects', () => {
const nestedObject = {
hobbies: ['reading', 'gaming'],
name: 'Alice',
};
const expected = {
hobbies: ['reading', 'gaming'],
name: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should flatten objects with nested arrays correctly', () => {
const nestedObject = {
person: {
hobbies: ['reading', 'gaming'],
name: 'Alice',
},
};
const expected = {
personHobbies: ['reading', 'gaming'],
personName: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle objects with undefined values', () => {
const nestedObject = {
user: {
age: undefined,
name: 'Alice',
},
};
const expected = {
userAge: undefined,
userName: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
});

View File

@ -1,21 +1,6 @@
import type { Flatten } from '@vben-core/typings';
import {
capitalizeFirstLetter,
toLowerCaseFirstLetter,
} from '@vben-core/toolkit';
/**
*
* @param key
* @param parentKey
*/
function toCamelCase(key: string, parentKey: string): string {
if (!parentKey) {
return key;
}
return parentKey + key.charAt(0).toUpperCase() + key.slice(1);
}
import { capitalizeFirstLetter } from '@vben-core/toolkit';
/**
*
@ -70,74 +55,7 @@ function flattenObject<T extends Record<string, any>>(
return result as Flatten<T>;
}
/**
*
*
* @template T -
* @param {Record<string, T>} obj -
* @param {number} level -
* @returns {T}
*
* @example
* 1
* const flatObject = {
* 'commonAppName': 1,
* 'anotherKeyExample': 2,
* 'someOtherKey': 3
* };
* const nestedObject = toNestedObject(flatObject, 1);
* console.log(nestedObject);
* :
* {
* commonAppName: 1,
* anotherKeyExample: 2,
* someOtherKey: 3
* }
*
* @example
* 2
* const flatObject = {
* 'appCommonName': 1,
* 'appAnotherKeyExample': 2,
* 'appSomeOtherKey': 3
* };
* const nestedObject = toNestedObject(flatObject, 2);
* console.log(nestedObject);
* :
* {
* app: {
* commonName: 1,
* anotherKeyExample: 2,
* someOtherKey: 3
* }
* }
*/
function toNestedObject<T>(obj: Record<string, T>, level: number): T {
const result: any = {};
for (const key in obj) {
const keys = key.split(/(?=[A-Z])/);
// 将驼峰式分割为数组;
let current = result;
for (let i = 0; i < keys.length; i++) {
const lowerKey = keys[i].toLowerCase();
if (i === level - 1) {
const remainingKeys = keys.slice(i).join(''); // 保留后续部分作为键的一部分
current[toLowerCaseFirstLetter(remainingKeys)] = obj[key];
break;
} else {
current[lowerKey] = current[lowerKey] || {};
current = current[lowerKey];
}
}
}
return result as T;
}
export { flattenObject, toCamelCase, toNestedObject };
export { flattenObject };
// 定义递归类型,用于推断扁平化后的对象类型
// 限制递归深度的辅助类型

View File

@ -0,0 +1,2 @@
export * from './flatten-object';
export * from './nested-object';

View File

@ -0,0 +1,97 @@
import { describe, expect, it } from 'vitest';
import { nestedObject } from './nested-object';
describe('nestedObject', () => {
it('should convert flat object to nested object with level 1', () => {
const flatObject = {
anotherKeyExample: 2,
commonAppName: 1,
someOtherKey: 3,
};
const expectedNestedObject = {
anotherKeyExample: 2,
commonAppName: 1,
someOtherKey: 3,
};
expect(nestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
it('should convert flat object to nested object with level 2', () => {
const flatObject = {
appAnotherKeyExample: 2,
appCommonName: 1,
appSomeOtherKey: 3,
};
const expectedNestedObject = {
app: {
anotherKeyExample: 2,
commonName: 1,
someOtherKey: 3,
},
};
expect(nestedObject(flatObject, 2)).toEqual(expectedNestedObject);
});
it('should convert flat object to nested object with level 3', () => {
const flatObject = {
appAnotherKeyExampleValue: 2,
appCommonNameKey: 1,
appSomeOtherKeyItem: 3,
};
const expectedNestedObject = {
app: {
another: {
keyExampleValue: 2,
},
common: {
nameKey: 1,
},
some: {
otherKeyItem: 3,
},
},
};
expect(nestedObject(flatObject, 3)).toEqual(expectedNestedObject);
});
it('should handle empty object', () => {
const flatObject = {};
const expectedNestedObject = {};
expect(nestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
it('should handle single key object', () => {
const flatObject = {
singleKey: 1,
};
const expectedNestedObject = {
singleKey: 1,
};
expect(nestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
it('should handle complex keys', () => {
const flatObject = {
anotherComplexKeyWithParts: 2,
complexKeyWithMultipleParts: 1,
};
const expectedNestedObject = {
anotherComplexKeyWithParts: 2,
complexKeyWithMultipleParts: 1,
};
expect(nestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
});

View File

@ -0,0 +1,70 @@
import { toLowerCaseFirstLetter } from '@vben-core/toolkit';
/**
*
*
* @template T -
* @param {Record<string, T>} obj -
* @param {number} level -
* @returns {T}
*
* @example
* 1
* const flatObject = {
* 'commonAppName': 1,
* 'anotherKeyExample': 2,
* 'someOtherKey': 3
* };
* const nestedObject = nestedObject(flatObject, 1);
* console.log(nestedObject);
* :
* {
* commonAppName: 1,
* anotherKeyExample: 2,
* someOtherKey: 3
* }
*
* @example
* 2
* const flatObject = {
* 'appCommonName': 1,
* 'appAnotherKeyExample': 2,
* 'appSomeOtherKey': 3
* };
* const nestedObject = nestedObject(flatObject, 2);
* console.log(nestedObject);
* :
* {
* app: {
* commonName: 1,
* anotherKeyExample: 2,
* someOtherKey: 3
* }
* }
*/
function nestedObject<T>(obj: Record<string, T>, level: number): T {
const result: any = {};
for (const key in obj) {
const keys = key.split(/(?=[A-Z])/);
// 将驼峰式分割为数组;
let current = result;
for (let i = 0; i < keys.length; i++) {
const lowerKey = keys[i].toLowerCase();
if (i === level - 1) {
const remainingKeys = keys.slice(i).join(''); // 保留后续部分作为键的一部分
current[toLowerCaseFirstLetter(remainingKeys)] = obj[key];
break;
} else {
current[lowerKey] = current[lowerKey] || {};
current = current[lowerKey];
}
}
}
return result as T;
}
export { nestedObject };

View File

@ -7,7 +7,7 @@
"repository": {
"type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "packages/@vben-core/preferences"
"directory": "packages/@vben-core/forward/preferences"
},
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"scripts": {

View File

@ -5,7 +5,7 @@ import type {
} from '@vben-core/typings';
import { StorageManager } from '@vben-core/cache';
import { flattenObject, toNestedObject } from '@vben-core/helpers';
import { flattenObject, nestedObject } from '@vben-core/helpers';
import { convertToHslCssVar, merge } from '@vben-core/toolkit';
import {
@ -190,7 +190,7 @@ class PreferenceManager {
* @param {FlattenObject<Preferences>} newValue -
*/
private updateState(newValue: Flatten<Preferences>) {
const nestObj = toNestedObject(newValue, 2);
const nestObj = nestedObject(newValue, 2);
Object.assign(this.state, merge(nestObj, this.state));
}

View File

@ -7,7 +7,7 @@
"repository": {
"type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "packages/@vben-core/stores"
"directory": "packages/@vben-core/forward/stores"
},
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"scripts": {

View File

@ -1 +0,0 @@
export * from './object';

View File

@ -1,245 +0,0 @@
import { describe, expect, it } from 'vitest';
import { flattenObject, toCamelCase, toNestedObject } from './object';
describe('toCamelCase', () => {
it('should return the key if parentKey is empty', () => {
expect(toCamelCase('child', '')).toBe('child');
});
it('should combine parentKey and key in camel case', () => {
expect(toCamelCase('child', 'parent')).toBe('parentChild');
});
it('should handle empty key and parentKey', () => {
expect(toCamelCase('', '')).toBe('');
});
it('should handle key with capital letters', () => {
expect(toCamelCase('Child', 'parent')).toBe('parentChild');
expect(toCamelCase('Child', 'Parent')).toBe('ParentChild');
});
});
describe('flattenObject', () => {
it('should flatten a nested object correctly', () => {
const nestedObject = {
language: 'en',
notifications: {
email: true,
push: {
sound: true,
vibration: false,
},
},
theme: 'light',
};
const expected = {
language: 'en',
notificationsEmail: true,
notificationsPushSound: true,
notificationsPushVibration: false,
theme: 'light',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle empty objects', () => {
const nestedObject = {};
const expected = {};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle objects with primitive values', () => {
const nestedObject = {
active: true,
age: 30,
name: 'Alice',
};
const expected = {
active: true,
age: 30,
name: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle objects with null values', () => {
const nestedObject = {
user: {
age: null,
name: null,
},
};
const expected = {
userAge: null,
userName: null,
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle nested empty objects', () => {
const nestedObject = {
a: {},
b: { c: {} },
};
const expected = {};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle arrays within objects', () => {
const nestedObject = {
hobbies: ['reading', 'gaming'],
name: 'Alice',
};
const expected = {
hobbies: ['reading', 'gaming'],
name: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should flatten objects with nested arrays correctly', () => {
const nestedObject = {
person: {
hobbies: ['reading', 'gaming'],
name: 'Alice',
},
};
const expected = {
personHobbies: ['reading', 'gaming'],
personName: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
it('should handle objects with undefined values', () => {
const nestedObject = {
user: {
age: undefined,
name: 'Alice',
},
};
const expected = {
userAge: undefined,
userName: 'Alice',
};
const result = flattenObject(nestedObject);
expect(result).toEqual(expected);
});
});
describe('toNestedObject', () => {
it('should convert flat object to nested object with level 1', () => {
const flatObject = {
anotherKeyExample: 2,
commonAppName: 1,
someOtherKey: 3,
};
const expectedNestedObject = {
anotherKeyExample: 2,
commonAppName: 1,
someOtherKey: 3,
};
expect(toNestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
it('should convert flat object to nested object with level 2', () => {
const flatObject = {
appAnotherKeyExample: 2,
appCommonName: 1,
appSomeOtherKey: 3,
};
const expectedNestedObject = {
app: {
anotherKeyExample: 2,
commonName: 1,
someOtherKey: 3,
},
};
expect(toNestedObject(flatObject, 2)).toEqual(expectedNestedObject);
});
it('should convert flat object to nested object with level 3', () => {
const flatObject = {
appAnotherKeyExampleValue: 2,
appCommonNameKey: 1,
appSomeOtherKeyItem: 3,
};
const expectedNestedObject = {
app: {
another: {
keyExampleValue: 2,
},
common: {
nameKey: 1,
},
some: {
otherKeyItem: 3,
},
},
};
expect(toNestedObject(flatObject, 3)).toEqual(expectedNestedObject);
});
it('should handle empty object', () => {
const flatObject = {};
const expectedNestedObject = {};
expect(toNestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
it('should handle single key object', () => {
const flatObject = {
singleKey: 1,
};
const expectedNestedObject = {
singleKey: 1,
};
expect(toNestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
it('should handle complex keys', () => {
const flatObject = {
anotherComplexKeyWithParts: 2,
complexKeyWithMultipleParts: 1,
};
const expectedNestedObject = {
anotherComplexKeyWithParts: 2,
complexKeyWithMultipleParts: 1,
};
expect(toNestedObject(flatObject, 1)).toEqual(expectedNestedObject);
});
});

View File

@ -7,7 +7,7 @@
"repository": {
"type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "packages/@vben-core/shared/toolkit"
"directory": "packages/@vben-core/shared/cache"
},
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"scripts": {

View File

@ -7,7 +7,7 @@
"repository": {
"type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "packages/@vben-core/iconify"
"directory": "packages/@vben-core/shared/iconify"
},
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"files": [

View File

@ -1,6 +1,10 @@
import { describe, expect, it } from 'vitest';
import { capitalizeFirstLetter, toLowerCaseFirstLetter } from './letter';
import {
capitalizeFirstLetter,
toCamelCase,
toLowerCaseFirstLetter,
} from './letter';
// 编写测试用例
describe('capitalizeFirstLetter', () => {
@ -53,3 +57,22 @@ describe('toLowerCaseFirstLetter', () => {
expect(toLowerCaseFirstLetter('123Number')).toBe('123Number');
});
});
describe('toCamelCase', () => {
it('should return the key if parentKey is empty', () => {
expect(toCamelCase('child', '')).toBe('child');
});
it('should combine parentKey and key in camel case', () => {
expect(toCamelCase('child', 'parent')).toBe('parentChild');
});
it('should handle empty key and parentKey', () => {
expect(toCamelCase('', '')).toBe('');
});
it('should handle key with capital letters', () => {
expect(toCamelCase('Child', 'parent')).toBe('parentChild');
expect(toCamelCase('Child', 'Parent')).toBe('ParentChild');
});
});

View File

@ -17,4 +17,16 @@ function toLowerCaseFirstLetter(str: string): string {
return str.charAt(0).toLowerCase() + str.slice(1);
}
export { capitalizeFirstLetter, toLowerCaseFirstLetter };
/**
*
* @param key
* @param parentKey
*/
function toCamelCase(key: string, parentKey: string): string {
if (!parentKey) {
return key;
}
return parentKey + key.charAt(0).toUpperCase() + key.slice(1);
}
export { capitalizeFirstLetter, toCamelCase, toLowerCaseFirstLetter };

View File

@ -457,6 +457,15 @@ importers:
specifier: ^3.0.2
version: 3.0.2(esbuild@0.20.2)(mockjs@1.1.0)(vite@6.0.0-alpha.17(@types/node@20.13.0)(sass@1.77.4)(terser@5.31.0))
packages/@vben-core/forward/helpers:
dependencies:
'@vben-core/toolkit':
specifier: workspace:*
version: link:../../shared/toolkit
'@vben-core/typings':
specifier: workspace:*
version: link:../../shared/typings
packages/@vben-core/forward/preferences:
dependencies:
'@vben-core/cache':
@ -464,7 +473,7 @@ importers:
version: link:../../shared/chche
'@vben-core/helpers':
specifier: workspace:*
version: link:../../helpers
version: link:../helpers
'@vben-core/toolkit':
specifier: workspace:*
version: link:../../shared/toolkit
@ -499,15 +508,6 @@ importers:
specifier: ^4.3.2
version: 4.3.2(vue@3.4.27(typescript@5.4.5))
packages/@vben-core/helpers:
dependencies:
'@vben-core/toolkit':
specifier: workspace:*
version: link:../shared/toolkit
'@vben-core/typings':
specifier: workspace:*
version: link:../shared/typings
packages/@vben-core/shared/chche: {}
packages/@vben-core/shared/design:

View File

@ -40,6 +40,10 @@
"name": "@vben/vite-config",
"path": "internal/vite-config",
},
{
"name": "@vben-core/helpers",
"path": "packages/@vben-core/forward/helpers",
},
{
"name": "@vben-core/preferences",
"path": "packages/@vben-core/forward/preferences",
@ -48,10 +52,6 @@
"name": "@vben-core/stores",
"path": "packages/@vben-core/forward/stores",
},
{
"name": "@vben-core/helpers",
"path": "packages/@vben-core/helpers",
},
{
"name": "@vben-core/cache",
"path": "packages/@vben-core/shared/chche",