mirror of
https://github.com/toss/es-toolkit.git
synced 2024-11-30 18:23:22 +03:00
feat(snakeCase): add snakeCase (#152)
* feat(snakeCase): Add caseSplitPattern RegExp const * feat(snakeCase): Add caseSplitPattern test code * feat(snakeCase): Add snakeCase function * feat(snakeCase): Add snakeCase test code * feat(snakeCase): Add snakeCase docs * feat(snakeCase): Add snakeCase benchmarks * chore: Add string export * fix(snakeCase): constants public api * Update docs/ko/reference/string/snakeCase.md * Update docs/ko/reference/string/snakeCase.md * Update docs/reference/string/snakeCase.md --------- Co-authored-by: Sojin Park <raon0211@gmail.com>
This commit is contained in:
parent
883553b39b
commit
d48900fa55
@ -5,3 +5,4 @@ echo "export * from './dist/math';" > math.d.ts
|
|||||||
echo "export * from './dist/object';" > object.d.ts
|
echo "export * from './dist/object';" > object.d.ts
|
||||||
echo "export * from './dist/predicate';" > predicate.d.ts
|
echo "export * from './dist/predicate';" > predicate.d.ts
|
||||||
echo "export * from './dist/promise';" > promise.d.ts
|
echo "export * from './dist/promise';" > promise.d.ts
|
||||||
|
echo "export * from './dist/string';" > string.d.ts
|
||||||
|
15
benchmarks/snakeCase.bench.ts
Normal file
15
benchmarks/snakeCase.bench.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { bench, describe } from 'vitest';
|
||||||
|
import { snakeCase as snakeCaseToolkit } from 'es-toolkit';
|
||||||
|
import { snakeCase as snakeCaseLodash } from 'lodash';
|
||||||
|
|
||||||
|
describe('snakeCase', () => {
|
||||||
|
bench('es-toolkit/snakeCase', () => {
|
||||||
|
const str = 'camleCase';
|
||||||
|
snakeCaseToolkit(str);
|
||||||
|
});
|
||||||
|
|
||||||
|
bench('lodash/snakeCase', () => {
|
||||||
|
const str = 'camelCase';
|
||||||
|
snakeCaseLodash(str);
|
||||||
|
});
|
||||||
|
});
|
@ -142,6 +142,10 @@ function sidebar(): DefaultTheme.Sidebar {
|
|||||||
text: 'Promise Utilities',
|
text: 'Promise Utilities',
|
||||||
items: [{ text: 'delay', link: '/reference/promise/delay' }],
|
items: [{ text: 'delay', link: '/reference/promise/delay' }],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: 'String Utilities',
|
||||||
|
items: [{ text: 'snakeCase', link: '/reference/string/snakeCase' }],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -141,6 +141,10 @@ function sidebar(): DefaultTheme.Sidebar {
|
|||||||
text: 'Promise',
|
text: 'Promise',
|
||||||
items: [{ text: 'delay', link: '/ko/reference/promise/delay' }],
|
items: [{ text: 'delay', link: '/ko/reference/promise/delay' }],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: '문자열',
|
||||||
|
items: [{ text: 'snakeCase', link: '/ko/reference/string/snakeCase' }],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -16,6 +16,7 @@ Here are some of the features es-toolkit offers:
|
|||||||
- **Object**: Tools for manipulating JavaScript objects, such as [pick](./reference/object/pick.md) and [omit](./reference/object/omit.md).
|
- **Object**: Tools for manipulating JavaScript objects, such as [pick](./reference/object/pick.md) and [omit](./reference/object/omit.md).
|
||||||
- **Predicate**: Type guard functions like [isNotNil](./reference/predicate/isNotNil.md).
|
- **Predicate**: Type guard functions like [isNotNil](./reference/predicate/isNotNil.md).
|
||||||
- **Promise**: Asynchronous utilities like [delay](./reference/promise/delay.md).
|
- **Promise**: Asynchronous utilities like [delay](./reference/promise/delay.md).
|
||||||
|
- **String**: Utilties for string manipulation, such as [snakeCase](./reference/string/snakeCase.md)
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ es-toolkit이 제공하는 기능 목록은 다음과 같습니다.
|
|||||||
- **객체**: [pick](./reference/object/pick.md)이나 [omit](./reference/object/omit.md)처럼 JavaScript 객체를 다루는 함수를 제공해요.
|
- **객체**: [pick](./reference/object/pick.md)이나 [omit](./reference/object/omit.md)처럼 JavaScript 객체를 다루는 함수를 제공해요.
|
||||||
- **타입 가드**: [isNotNil](./reference/predicate/isNotNil.md)처럼 특정한 객체가 어떤 상태인지 검사하는 타입 가드 함수를 제공해요.
|
- **타입 가드**: [isNotNil](./reference/predicate/isNotNil.md)처럼 특정한 객체가 어떤 상태인지 검사하는 타입 가드 함수를 제공해요.
|
||||||
- **Promise**: [delay](./reference/promise/delay.md)와 같은 비동기 유틸리티 함수를 제공해요.
|
- **Promise**: [delay](./reference/promise/delay.md)와 같은 비동기 유틸리티 함수를 제공해요.
|
||||||
|
- **문자열**: [snakeCase](./reference/string/snakeCase.md)와 같이 문자열을 다루기 위한 다양한 함수를 제공해요.
|
||||||
|
|
||||||
## 링크
|
## 링크
|
||||||
|
|
||||||
|
30
docs/ko/reference/string/snakeCase.md
Normal file
30
docs/ko/reference/string/snakeCase.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# snakeCase
|
||||||
|
|
||||||
|
문자열을 스네이크 표기법으로 변환해요.
|
||||||
|
|
||||||
|
스네이크 표기법은 여러 단어로 구성된 식별자에서 각 단어를 소문자로 작성하고, 단어 사이를 밑줄(\_)로 연결하는 명명 규칙이에요. 예를 들어서, `snake_case` 처럼 작성해요.
|
||||||
|
|
||||||
|
## 인터페이스
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function snakeCase(str: string): string;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 파라미터
|
||||||
|
|
||||||
|
- `str` (`string`): 스네이크 케이스로 변환할 문자열이에요.
|
||||||
|
|
||||||
|
### 반환 값
|
||||||
|
|
||||||
|
(`string`) 스네이크 케이스로 변환된 문자열이에요.
|
||||||
|
|
||||||
|
## 예시
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { snakeCase } from 'es-toolkit/string';
|
||||||
|
|
||||||
|
snakeCase('camelCase'); // returns 'camel_case'
|
||||||
|
snakeCase('some whitespace'); // returns 'some_whitespace'
|
||||||
|
snakeCase('hyphen-text'); // returns 'hyphen_text'
|
||||||
|
snakeCase('HTTPRequest'); // returns 'http_request'
|
||||||
|
```
|
30
docs/reference/string/snakeCase.md
Normal file
30
docs/reference/string/snakeCase.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# snakeCase
|
||||||
|
|
||||||
|
Converts a string to snake case.
|
||||||
|
|
||||||
|
Snake case is the naming convention in which each word is written in lowercase and separated by an underscore (\_) character. For example, `snake_case`.
|
||||||
|
|
||||||
|
## Signature
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
function snakeCase(str: string): string;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
- `str` (`string`): The string that is to be changed to snake case.
|
||||||
|
|
||||||
|
### Returns
|
||||||
|
|
||||||
|
(`string`) The converted string to snake case.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { snakeCase } from 'es-toolkit/string';
|
||||||
|
|
||||||
|
snakeCase('camelCase'); // returns 'camel_case'
|
||||||
|
snakeCase('some whitespace'); // returns 'some_whitespace'
|
||||||
|
snakeCase('hyphen-text'); // returns 'hyphen_text'
|
||||||
|
snakeCase('HTTPRequest'); // returns 'http_request'
|
||||||
|
```
|
11
package.json
11
package.json
@ -21,6 +21,7 @@
|
|||||||
"./object": "./src/object/index.ts",
|
"./object": "./src/object/index.ts",
|
||||||
"./predicate": "./src/predicate/index.ts",
|
"./predicate": "./src/predicate/index.ts",
|
||||||
"./promise": "./src/promise/index.ts",
|
"./promise": "./src/promise/index.ts",
|
||||||
|
"./string": "./src/string/index.ts",
|
||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
@ -103,6 +104,16 @@
|
|||||||
"default": "./dist/promise/index.js"
|
"default": "./dist/promise/index.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"./string": {
|
||||||
|
"import": {
|
||||||
|
"types": "./dist/string/index.d.mts",
|
||||||
|
"default": "./dist/string/index.mjs"
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"types": "./dist/string/index.d.ts",
|
||||||
|
"default": "./dist/string/index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"./package.json": "./package.json"
|
"./package.json": "./package.json"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
57
src/constants/caseSplitPattern.spec.ts
Normal file
57
src/constants/caseSplitPattern.spec.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { CASE_SPLIT_PATTERN } from './caseSplitPattern';
|
||||||
|
describe('caseSplitPattern', () => {
|
||||||
|
it('should match camelCase', async () => {
|
||||||
|
const str = 'camelCase';
|
||||||
|
const matches = str.match(CASE_SPLIT_PATTERN);
|
||||||
|
expect(matches).toEqual(['camel', 'Case']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match snake_case', async () => {
|
||||||
|
const str = 'snake_case';
|
||||||
|
const matches = str.match(CASE_SPLIT_PATTERN);
|
||||||
|
expect(matches).toEqual(['snake', 'case']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match kebab-case', async () => {
|
||||||
|
const str = 'kebab-case';
|
||||||
|
const matches = str.match(CASE_SPLIT_PATTERN);
|
||||||
|
expect(matches).toEqual(['kebab', 'case']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle mixed formats', async () => {
|
||||||
|
const str = 'camelCase_snake_case-kebabCase';
|
||||||
|
const matches = str.match(CASE_SPLIT_PATTERN);
|
||||||
|
expect(matches).toEqual(['camel', 'Case', 'snake', 'case', 'kebab', 'Case']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match acronyms', async () => {
|
||||||
|
const str = 'HTTPRequest';
|
||||||
|
const matches = str.match(CASE_SPLIT_PATTERN);
|
||||||
|
expect(matches).toEqual(['HTTP', 'Request']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should match special characters', async () => {
|
||||||
|
const str = 'special_characters@123';
|
||||||
|
const matches = str.match(CASE_SPLIT_PATTERN);
|
||||||
|
expect(matches).toEqual(['special', 'characters', '123']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle leading and trailing whitespace', async () => {
|
||||||
|
const str = ' leading_and_trailing_whitespace ';
|
||||||
|
const matches = str.match(CASE_SPLIT_PATTERN);
|
||||||
|
expect(matches).toEqual(['leading', 'and', 'trailing', 'whitespace']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle underscores', async () => {
|
||||||
|
const str = 'underscore_case_example';
|
||||||
|
const matches = str.match(CASE_SPLIT_PATTERN);
|
||||||
|
expect(matches).toEqual(['underscore', 'case', 'example']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle single character words', async () => {
|
||||||
|
const str = 'aB';
|
||||||
|
const matches = str.match(CASE_SPLIT_PATTERN);
|
||||||
|
expect(matches).toEqual(['a', 'B']);
|
||||||
|
});
|
||||||
|
});
|
18
src/constants/caseSplitPattern.ts
Normal file
18
src/constants/caseSplitPattern.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Regular expression pattern to split strings into words for various case conversions
|
||||||
|
*
|
||||||
|
* This pattern matchs sequences of characters in a string, considering the following case:
|
||||||
|
* - Sequences of two or more uppercase letters followed by an uppercase letter and lowercase letters or digits (for acronyms)
|
||||||
|
* - Sequences of one uppercase letter optionally followed by lowercase letters and digits
|
||||||
|
* - Single uppercase letters
|
||||||
|
* - Sequences of digis
|
||||||
|
*
|
||||||
|
* The resulting match can be used to convert camelCase, snake_case, kebab-case, and other mixed formats into
|
||||||
|
* a consistent format like snake case.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const matches = 'camelCaseHTTPRequest'.match(CASE_SPLIT_PATTERN);
|
||||||
|
* // matchs: ['camel', 'Case', 'HTTP', 'Request']
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const CASE_SPLIT_PATTERN = /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g;
|
1
src/constants/index.ts
Normal file
1
src/constants/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { CASE_SPLIT_PATTERN } from './caseSplitPattern';
|
@ -20,6 +20,7 @@
|
|||||||
* - **Object**: Tools for manipulating JavaScript objects, such as [pick](https://es-toolkit.slash.page/reference/object/pick.html) and [omit](https://es-toolkit.slash.page/reference/object/omit.html).
|
* - **Object**: Tools for manipulating JavaScript objects, such as [pick](https://es-toolkit.slash.page/reference/object/pick.html) and [omit](https://es-toolkit.slash.page/reference/object/omit.html).
|
||||||
* - **Predicate**: Type guard functions like [isNotNil](https://es-toolkit.slash.page/reference/predicate/isNotNil.html).
|
* - **Predicate**: Type guard functions like [isNotNil](https://es-toolkit.slash.page/reference/predicate/isNotNil.html).
|
||||||
* - **Promise**: Asynchronous utilities like [delay](https://es-toolkit.slash.page/reference/promise/delay.html).
|
* - **Promise**: Asynchronous utilities like [delay](https://es-toolkit.slash.page/reference/promise/delay.html).
|
||||||
|
* - **String**: Utilities for string manipulation, such as [snakeCase](https://es-toolkit.slash.page/reference/string/snakeCase.html)
|
||||||
*
|
*
|
||||||
* If you want to know more about the project, please take a look at the
|
* If you want to know more about the project, please take a look at the
|
||||||
* following resources:
|
* following resources:
|
||||||
@ -36,3 +37,4 @@ export * from './math/index.ts';
|
|||||||
export * from './object/index.ts';
|
export * from './object/index.ts';
|
||||||
export * from './predicate/index.ts';
|
export * from './predicate/index.ts';
|
||||||
export * from './promise/index.ts';
|
export * from './promise/index.ts';
|
||||||
|
export * from './string/index.ts'
|
||||||
|
1
src/string/index.ts
Normal file
1
src/string/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { snakeCase } from './snakeCase.ts';
|
36
src/string/snakeCase.spec.ts
Normal file
36
src/string/snakeCase.spec.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { snakeCase } from './snakeCase';
|
||||||
|
|
||||||
|
describe('snakeCase', () => {
|
||||||
|
it('should change camel case to snake case', async () => {
|
||||||
|
expect(snakeCase('camelCase')).toEqual('camel_case');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should change space to underscore', async () => {
|
||||||
|
expect(snakeCase('some whitespace')).toEqual('some_whitespace');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should change hyphen to underscore', async () => {
|
||||||
|
expect(snakeCase('hyphen-text')).toEqual('hyphen_text');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should change Acronyms to small letter', async () => {
|
||||||
|
expect(snakeCase('HTTPRequest')).toEqual('http_request');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle leading and trailing whitepspace', async () => {
|
||||||
|
expect(snakeCase(' leading and trailing whitespace')).toEqual('leading_and_trailing_whitespace');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle special characters correctly', async () => {
|
||||||
|
expect(snakeCase('special@characters!')).toEqual('special_characters');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle strings that are already in snake_case', async () => {
|
||||||
|
expect(snakeCase('snake_case')).toEqual('snake_case');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with an empty string', async () => {
|
||||||
|
expect(snakeCase('')).toEqual('');
|
||||||
|
});
|
||||||
|
});
|
21
src/string/snakeCase.ts
Normal file
21
src/string/snakeCase.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { CASE_SPLIT_PATTERN } from '../constants';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a string to snake case.
|
||||||
|
*
|
||||||
|
* Snake case is the naming convention in which each word is written in lowercase and separated by an underscore (_) character.
|
||||||
|
*
|
||||||
|
* @param {string} str - The string that is to be changed to snake case.
|
||||||
|
* @returns {string} - The converted string to snake case.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const convertedStr1 = snakeCase('camelCase') // returns 'camel_case'
|
||||||
|
* const convertedStr2 = snakeCase('some whitespace') // returns 'some_whitespace'
|
||||||
|
* const convertedStr3 = snakeCase('hyphen-text') // returns 'hyphen_text'
|
||||||
|
* const convertedStr4 = snakeCase('HTTPRequest') // returns 'http_request'
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const snakeCase = (str: string): string => {
|
||||||
|
const splitWords = str.match(CASE_SPLIT_PATTERN) || [];
|
||||||
|
return splitWords.map(word => word.toLowerCase()).join('_');
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user