goodchuck-utils 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/date/index.d.ts +64 -0
- package/dist/date/index.d.ts.map +1 -0
- package/dist/date/index.js +92 -0
- package/dist/date/index.test.d.ts +2 -0
- package/dist/date/index.test.d.ts.map +1 -0
- package/dist/date/index.test.js +166 -0
- package/dist/form/__tests__/formatter.test.d.ts +2 -0
- package/dist/form/__tests__/formatter.test.d.ts.map +1 -0
- package/dist/form/__tests__/formatter.test.js +74 -0
- package/dist/form/__tests__/helpers.test.d.ts +2 -0
- package/dist/form/__tests__/helpers.test.d.ts.map +1 -0
- package/dist/form/__tests__/helpers.test.js +42 -0
- package/dist/form/__tests__/validation.test.d.ts +2 -0
- package/dist/form/__tests__/validation.test.d.ts.map +1 -0
- package/dist/form/__tests__/validation.test.js +67 -0
- package/dist/form/formatter.d.ts +34 -0
- package/dist/form/formatter.d.ts.map +1 -0
- package/dist/form/formatter.js +76 -0
- package/dist/form/helpers.d.ts +16 -0
- package/dist/form/helpers.d.ts.map +1 -0
- package/dist/form/helpers.js +34 -0
- package/dist/form/index.d.ts +9 -0
- package/dist/form/index.d.ts.map +1 -0
- package/dist/form/index.js +11 -0
- package/dist/form/validation.d.ts +33 -0
- package/dist/form/validation.d.ts.map +1 -0
- package/dist/form/validation.js +56 -0
- package/dist/hooks/index.d.ts +11 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +12 -0
- package/dist/hooks/useClickOutside.d.ts +49 -0
- package/dist/hooks/useClickOutside.d.ts.map +1 -0
- package/dist/hooks/useClickOutside.js +94 -0
- package/dist/hooks/useLocalStorage.d.ts +19 -0
- package/dist/hooks/useLocalStorage.d.ts.map +1 -0
- package/dist/hooks/useLocalStorage.js +91 -0
- package/dist/hooks/useMediaQuery.d.ts +56 -0
- package/dist/hooks/useMediaQuery.d.ts.map +1 -0
- package/dist/hooks/useMediaQuery.js +104 -0
- package/dist/hooks/useSessionStorage.d.ts +19 -0
- package/dist/hooks/useSessionStorage.d.ts.map +1 -0
- package/dist/hooks/useSessionStorage.js +85 -0
- package/dist/index.d.ts +4 -5
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -18
- package/dist/string/__tests__/case.test.d.ts +2 -0
- package/dist/string/__tests__/case.test.d.ts.map +1 -0
- package/dist/string/__tests__/case.test.js +61 -0
- package/dist/string/__tests__/manipulation.test.d.ts +2 -0
- package/dist/string/__tests__/manipulation.test.d.ts.map +1 -0
- package/dist/string/__tests__/manipulation.test.js +109 -0
- package/dist/string/__tests__/validation.test.d.ts +2 -0
- package/dist/string/__tests__/validation.test.d.ts.map +1 -0
- package/dist/string/__tests__/validation.test.js +101 -0
- package/dist/string/case.d.ts +42 -0
- package/dist/string/case.d.ts.map +1 -0
- package/dist/string/case.js +71 -0
- package/dist/string/index.d.ts +9 -0
- package/dist/string/index.d.ts.map +1 -0
- package/dist/string/index.js +11 -0
- package/dist/string/manipulation.d.ts +61 -0
- package/dist/string/manipulation.d.ts.map +1 -0
- package/dist/string/manipulation.js +106 -0
- package/dist/string/validation.d.ts +79 -0
- package/dist/string/validation.d.ts.map +1 -0
- package/dist/string/validation.js +115 -0
- package/package.json +27 -3
- package/src/date/index.test.ts +206 -0
- package/src/date/index.ts +123 -0
- package/src/form/__tests__/formatter.test.ts +97 -0
- package/src/form/__tests__/helpers.test.ts +53 -0
- package/src/form/__tests__/validation.test.ts +84 -0
- package/src/form/formatter.ts +85 -0
- package/src/form/helpers.ts +44 -0
- package/src/form/index.ts +14 -0
- package/src/form/validation.ts +72 -0
- package/src/hooks/index.ts +14 -0
- package/src/hooks/useClickOutside.ts +114 -0
- package/src/hooks/useLocalStorage.ts +112 -0
- package/src/hooks/useMediaQuery.ts +116 -0
- package/src/hooks/useSessionStorage.ts +106 -0
- package/src/index.ts +14 -13
- package/src/string/__tests__/case.test.ts +78 -0
- package/src/string/__tests__/manipulation.test.ts +142 -0
- package/src/string/__tests__/validation.test.ts +128 -0
- package/src/string/case.ts +76 -0
- package/src/string/index.ts +14 -0
- package/src/string/manipulation.ts +124 -0
- package/src/string/validation.ts +126 -0
- package/tsconfig.json +15 -11
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* sessionStorage와 동기화되는 state hook
|
|
4
|
+
*
|
|
5
|
+
* @param key - sessionStorage 키
|
|
6
|
+
* @param initialValue - 초기값
|
|
7
|
+
* @returns [storedValue, setValue, removeValue]
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const [token, setToken, removeToken] = useSessionStorage('auth-token', '');
|
|
11
|
+
*
|
|
12
|
+
* // 값 설정
|
|
13
|
+
* setToken('abc123');
|
|
14
|
+
*
|
|
15
|
+
* // 값 제거
|
|
16
|
+
* removeToken();
|
|
17
|
+
*/
|
|
18
|
+
export function useSessionStorage(key, initialValue) {
|
|
19
|
+
// SSR 안전성 체크
|
|
20
|
+
const isBrowser = typeof window !== 'undefined';
|
|
21
|
+
// 초기값을 sessionStorage에서 가져오기
|
|
22
|
+
const readValue = useCallback(() => {
|
|
23
|
+
if (!isBrowser) {
|
|
24
|
+
return initialValue;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const item = window.sessionStorage.getItem(key);
|
|
28
|
+
return item ? JSON.parse(item) : initialValue;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.warn(`Error reading sessionStorage key "${key}":`, error);
|
|
32
|
+
return initialValue;
|
|
33
|
+
}
|
|
34
|
+
}, [initialValue, key, isBrowser]);
|
|
35
|
+
const [storedValue, setStoredValue] = useState(readValue);
|
|
36
|
+
// 값 설정 함수
|
|
37
|
+
const setValue = useCallback((value) => {
|
|
38
|
+
if (!isBrowser) {
|
|
39
|
+
console.warn(`Tried setting sessionStorage key "${key}" even though environment is not a client`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
// useState와 동일하게 함수형 업데이트 지원
|
|
44
|
+
const newValue = value instanceof Function ? value(storedValue) : value;
|
|
45
|
+
// sessionStorage에 저장
|
|
46
|
+
window.sessionStorage.setItem(key, JSON.stringify(newValue));
|
|
47
|
+
// state 업데이트
|
|
48
|
+
setStoredValue(newValue);
|
|
49
|
+
// storage event 발생
|
|
50
|
+
window.dispatchEvent(new Event('session-storage'));
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
console.warn(`Error setting sessionStorage key "${key}":`, error);
|
|
54
|
+
}
|
|
55
|
+
}, [key, storedValue, isBrowser]);
|
|
56
|
+
// 값 제거 함수
|
|
57
|
+
const removeValue = useCallback(() => {
|
|
58
|
+
if (!isBrowser) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
window.sessionStorage.removeItem(key);
|
|
63
|
+
setStoredValue(initialValue);
|
|
64
|
+
window.dispatchEvent(new Event('session-storage'));
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
console.warn(`Error removing sessionStorage key "${key}":`, error);
|
|
68
|
+
}
|
|
69
|
+
}, [key, initialValue, isBrowser]);
|
|
70
|
+
// 같은 페이지 내의 변경사항 감지
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (!isBrowser) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const handleStorageChange = () => {
|
|
76
|
+
setStoredValue(readValue());
|
|
77
|
+
};
|
|
78
|
+
// 같은 페이지 내의 변경사항
|
|
79
|
+
window.addEventListener('session-storage', handleStorageChange);
|
|
80
|
+
return () => {
|
|
81
|
+
window.removeEventListener('session-storage', handleStorageChange);
|
|
82
|
+
};
|
|
83
|
+
}, [key, readValue, isBrowser]);
|
|
84
|
+
return [storedValue, setValue, removeValue];
|
|
85
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export { add, subtract, multiply, divide };
|
|
1
|
+
export * from './date';
|
|
2
|
+
export * from './form';
|
|
3
|
+
export * from './string';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,QAAQ,CAAC;AAGvB,cAAc,QAAQ,CAAC;AAGvB,cAAc,UAAU,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return a * b;
|
|
15
|
-
}
|
|
16
|
-
function divide(a, b) {
|
|
17
|
-
return a / b;
|
|
18
|
-
}
|
|
1
|
+
// Date utilities
|
|
2
|
+
export * from './date';
|
|
3
|
+
// Form utilities
|
|
4
|
+
export * from './form';
|
|
5
|
+
// String utilities
|
|
6
|
+
export * from './string';
|
|
7
|
+
// React Hooks (import separately: 'goodchuck-utils/hooks')
|
|
8
|
+
// Note: Hooks are not exported from main entry to avoid React dependency for non-React users
|
|
9
|
+
// export * from './hooks';
|
|
10
|
+
// Array utilities (placeholder for future)
|
|
11
|
+
// export * from './array';
|
|
12
|
+
// Object utilities (placeholder for future)
|
|
13
|
+
// export * from './object';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"case.test.d.ts","sourceRoot":"","sources":["../../../src/string/__tests__/case.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { capitalize, capitalizeWords, camelCase, pascalCase, snakeCase, kebabCase, constantCase, } from '../case';
|
|
3
|
+
describe('String Case Conversion', () => {
|
|
4
|
+
describe('capitalize', () => {
|
|
5
|
+
it('should capitalize first letter', () => {
|
|
6
|
+
expect(capitalize('hello')).toBe('Hello');
|
|
7
|
+
expect(capitalize('hello world')).toBe('Hello world');
|
|
8
|
+
expect(capitalize('HELLO')).toBe('HELLO');
|
|
9
|
+
});
|
|
10
|
+
it('should handle empty string', () => {
|
|
11
|
+
expect(capitalize('')).toBe('');
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
describe('capitalizeWords', () => {
|
|
15
|
+
it('should capitalize each word', () => {
|
|
16
|
+
expect(capitalizeWords('hello world')).toBe('Hello World');
|
|
17
|
+
expect(capitalizeWords('the quick brown fox')).toBe('The Quick Brown Fox');
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
describe('camelCase', () => {
|
|
21
|
+
it('should convert to camelCase', () => {
|
|
22
|
+
expect(camelCase('hello world')).toBe('helloWorld');
|
|
23
|
+
expect(camelCase('hello-world')).toBe('helloWorld');
|
|
24
|
+
expect(camelCase('hello_world')).toBe('helloWorld');
|
|
25
|
+
expect(camelCase('Hello World')).toBe('helloWorld');
|
|
26
|
+
});
|
|
27
|
+
it('should handle already camelCase strings', () => {
|
|
28
|
+
expect(camelCase('helloWorld')).toBe('helloworld');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
describe('pascalCase', () => {
|
|
32
|
+
it('should convert to PascalCase', () => {
|
|
33
|
+
expect(pascalCase('hello world')).toBe('HelloWorld');
|
|
34
|
+
expect(pascalCase('hello-world')).toBe('HelloWorld');
|
|
35
|
+
expect(pascalCase('hello_world')).toBe('HelloWorld');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe('snakeCase', () => {
|
|
39
|
+
it('should convert to snake_case', () => {
|
|
40
|
+
expect(snakeCase('helloWorld')).toBe('hello_world');
|
|
41
|
+
expect(snakeCase('Hello World')).toBe('hello_world');
|
|
42
|
+
expect(snakeCase('hello-world')).toBe('hello_world');
|
|
43
|
+
expect(snakeCase('HelloWorld')).toBe('hello_world');
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
describe('kebabCase', () => {
|
|
47
|
+
it('should convert to kebab-case', () => {
|
|
48
|
+
expect(kebabCase('helloWorld')).toBe('hello-world');
|
|
49
|
+
expect(kebabCase('Hello World')).toBe('hello-world');
|
|
50
|
+
expect(kebabCase('hello_world')).toBe('hello-world');
|
|
51
|
+
expect(kebabCase('HelloWorld')).toBe('hello-world');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe('constantCase', () => {
|
|
55
|
+
it('should convert to CONSTANT_CASE', () => {
|
|
56
|
+
expect(constantCase('helloWorld')).toBe('HELLO_WORLD');
|
|
57
|
+
expect(constantCase('Hello World')).toBe('HELLO_WORLD');
|
|
58
|
+
expect(constantCase('hello-world')).toBe('HELLO_WORLD');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manipulation.test.d.ts","sourceRoot":"","sources":["../../../src/string/__tests__/manipulation.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { truncate, normalizeWhitespace, slugify, randomString, repeat, reverse, stripHtml, padStart, padEnd, template, mask, } from '../manipulation';
|
|
3
|
+
describe('String Manipulation', () => {
|
|
4
|
+
describe('truncate', () => {
|
|
5
|
+
it('should truncate long strings', () => {
|
|
6
|
+
expect(truncate('Hello World', 5)).toBe('Hello...');
|
|
7
|
+
expect(truncate('Hello World', 20)).toBe('Hello World');
|
|
8
|
+
});
|
|
9
|
+
it('should use custom suffix', () => {
|
|
10
|
+
expect(truncate('Hello World', 5, '---')).toBe('Hello---');
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
describe('normalizeWhitespace', () => {
|
|
14
|
+
it('should normalize whitespace', () => {
|
|
15
|
+
expect(normalizeWhitespace(' hello world ')).toBe('hello world');
|
|
16
|
+
expect(normalizeWhitespace('hello\n\nworld')).toBe('hello world');
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
describe('slugify', () => {
|
|
20
|
+
it('should create URL-friendly slugs', () => {
|
|
21
|
+
expect(slugify('Hello World!')).toBe('hello-world');
|
|
22
|
+
expect(slugify('Hello World')).toBe('hello-world');
|
|
23
|
+
expect(slugify('Hello_World')).toBe('hello-world');
|
|
24
|
+
});
|
|
25
|
+
it('should preserve alphanumeric characters', () => {
|
|
26
|
+
expect(slugify('Test123 World')).toBe('test123-world');
|
|
27
|
+
});
|
|
28
|
+
it('should remove special characters', () => {
|
|
29
|
+
expect(slugify('Hello @#$ World!')).toBe('hello-world');
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe('randomString', () => {
|
|
33
|
+
it('should generate random alphanumeric string', () => {
|
|
34
|
+
const result = randomString(10);
|
|
35
|
+
expect(result).toHaveLength(10);
|
|
36
|
+
expect(/^[a-zA-Z0-9]+$/.test(result)).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
it('should generate numeric only string', () => {
|
|
39
|
+
const result = randomString(10, 'numeric');
|
|
40
|
+
expect(result).toHaveLength(10);
|
|
41
|
+
expect(/^[0-9]+$/.test(result)).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
it('should generate alpha only string', () => {
|
|
44
|
+
const result = randomString(10, 'alpha');
|
|
45
|
+
expect(result).toHaveLength(10);
|
|
46
|
+
expect(/^[a-zA-Z]+$/.test(result)).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
it('should generate hex string', () => {
|
|
49
|
+
const result = randomString(10, 'hex');
|
|
50
|
+
expect(result).toHaveLength(10);
|
|
51
|
+
expect(/^[0-9a-f]+$/.test(result)).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe('repeat', () => {
|
|
55
|
+
it('should repeat string', () => {
|
|
56
|
+
expect(repeat('ab', 3)).toBe('ababab');
|
|
57
|
+
expect(repeat('x', 5)).toBe('xxxxx');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe('reverse', () => {
|
|
61
|
+
it('should reverse string', () => {
|
|
62
|
+
expect(reverse('hello')).toBe('olleh');
|
|
63
|
+
expect(reverse('12345')).toBe('54321');
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
describe('stripHtml', () => {
|
|
67
|
+
it('should remove HTML tags', () => {
|
|
68
|
+
expect(stripHtml('<p>Hello</p>')).toBe('Hello');
|
|
69
|
+
expect(stripHtml('<p>Hello <strong>World</strong></p>')).toBe('Hello World');
|
|
70
|
+
expect(stripHtml('No tags here')).toBe('No tags here');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe('padStart', () => {
|
|
74
|
+
it('should pad string at start', () => {
|
|
75
|
+
expect(padStart('5', 3, '0')).toBe('005');
|
|
76
|
+
expect(padStart('abc', 5, 'x')).toBe('xxabc');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
describe('padEnd', () => {
|
|
80
|
+
it('should pad string at end', () => {
|
|
81
|
+
expect(padEnd('5', 3, '0')).toBe('500');
|
|
82
|
+
expect(padEnd('abc', 5, 'x')).toBe('abcxx');
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
describe('template', () => {
|
|
86
|
+
it('should replace placeholders', () => {
|
|
87
|
+
expect(template('Hello {name}!', { name: 'World' })).toBe('Hello World!');
|
|
88
|
+
expect(template('I am {age} years old', { age: 25 })).toBe('I am 25 years old');
|
|
89
|
+
});
|
|
90
|
+
it('should keep unreplaced placeholders', () => {
|
|
91
|
+
expect(template('Hello {name}!', {})).toBe('Hello {name}!');
|
|
92
|
+
});
|
|
93
|
+
it('should handle multiple placeholders', () => {
|
|
94
|
+
expect(template('{greeting} {name}!', { greeting: 'Hi', name: 'John' })).toBe('Hi John!');
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
describe('mask', () => {
|
|
98
|
+
it('should mask characters', () => {
|
|
99
|
+
expect(mask('1234567890', 3, 7)).toBe('123****890');
|
|
100
|
+
expect(mask('password', 2, 6)).toBe('pa****rd');
|
|
101
|
+
});
|
|
102
|
+
it('should use custom mask character', () => {
|
|
103
|
+
expect(mask('1234567890', 3, 7, 'x')).toBe('123xxxx890');
|
|
104
|
+
});
|
|
105
|
+
it('should mask to end if end not specified', () => {
|
|
106
|
+
expect(mask('1234567890', 5)).toBe('12345*****');
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.test.d.ts","sourceRoot":"","sources":["../../../src/string/__tests__/validation.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { isEmpty, startsWith, endsWith, contains, isAlpha, isNumeric, isAlphanumeric, isLowerCase, isUpperCase, isJSON, isHexColor, isUUID, isBase64, } from '../validation';
|
|
3
|
+
describe('String Validation', () => {
|
|
4
|
+
describe('isEmpty', () => {
|
|
5
|
+
it('should detect empty strings', () => {
|
|
6
|
+
expect(isEmpty('')).toBe(true);
|
|
7
|
+
expect(isEmpty(' ')).toBe(true);
|
|
8
|
+
expect(isEmpty('hello')).toBe(false);
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
describe('startsWith', () => {
|
|
12
|
+
it('should check if string starts with substring', () => {
|
|
13
|
+
expect(startsWith('hello', 'he')).toBe(true);
|
|
14
|
+
expect(startsWith('hello', 'ho')).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
describe('endsWith', () => {
|
|
18
|
+
it('should check if string ends with substring', () => {
|
|
19
|
+
expect(endsWith('hello', 'lo')).toBe(true);
|
|
20
|
+
expect(endsWith('hello', 'he')).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe('contains', () => {
|
|
24
|
+
it('should check if string contains substring', () => {
|
|
25
|
+
expect(contains('hello world', 'world')).toBe(true);
|
|
26
|
+
expect(contains('hello world', 'foo')).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe('isAlpha', () => {
|
|
30
|
+
it('should validate alphabetic strings', () => {
|
|
31
|
+
expect(isAlpha('hello')).toBe(true);
|
|
32
|
+
expect(isAlpha('Hello')).toBe(true);
|
|
33
|
+
expect(isAlpha('hello123')).toBe(false);
|
|
34
|
+
expect(isAlpha('hello_world')).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe('isNumeric', () => {
|
|
38
|
+
it('should validate numeric strings', () => {
|
|
39
|
+
expect(isNumeric('123')).toBe(true);
|
|
40
|
+
expect(isNumeric('123.45')).toBe(false);
|
|
41
|
+
expect(isNumeric('abc')).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
describe('isAlphanumeric', () => {
|
|
45
|
+
it('should validate alphanumeric strings', () => {
|
|
46
|
+
expect(isAlphanumeric('hello123')).toBe(true);
|
|
47
|
+
expect(isAlphanumeric('hello')).toBe(true);
|
|
48
|
+
expect(isAlphanumeric('123')).toBe(true);
|
|
49
|
+
expect(isAlphanumeric('hello_123')).toBe(false);
|
|
50
|
+
expect(isAlphanumeric('hello-123')).toBe(false);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe('isLowerCase', () => {
|
|
54
|
+
it('should detect lowercase strings', () => {
|
|
55
|
+
expect(isLowerCase('hello')).toBe(true);
|
|
56
|
+
expect(isLowerCase('Hello')).toBe(false);
|
|
57
|
+
expect(isLowerCase('HELLO')).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe('isUpperCase', () => {
|
|
61
|
+
it('should detect uppercase strings', () => {
|
|
62
|
+
expect(isUpperCase('HELLO')).toBe(true);
|
|
63
|
+
expect(isUpperCase('Hello')).toBe(false);
|
|
64
|
+
expect(isUpperCase('hello')).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe('isJSON', () => {
|
|
68
|
+
it('should validate JSON strings', () => {
|
|
69
|
+
expect(isJSON('{"name":"John"}')).toBe(true);
|
|
70
|
+
expect(isJSON('[1,2,3]')).toBe(true);
|
|
71
|
+
expect(isJSON('"hello"')).toBe(true);
|
|
72
|
+
expect(isJSON('not json')).toBe(false);
|
|
73
|
+
expect(isJSON('{invalid}')).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe('isHexColor', () => {
|
|
77
|
+
it('should validate hex color codes', () => {
|
|
78
|
+
expect(isHexColor('#fff')).toBe(true);
|
|
79
|
+
expect(isHexColor('#ffffff')).toBe(true);
|
|
80
|
+
expect(isHexColor('#FFF')).toBe(true);
|
|
81
|
+
expect(isHexColor('#ABC123')).toBe(true);
|
|
82
|
+
expect(isHexColor('fff')).toBe(false);
|
|
83
|
+
expect(isHexColor('#gggggg')).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
describe('isUUID', () => {
|
|
87
|
+
it('should validate UUID strings', () => {
|
|
88
|
+
expect(isUUID('123e4567-e89b-12d3-a456-426614174000')).toBe(true);
|
|
89
|
+
expect(isUUID('550e8400-e29b-41d4-a716-446655440000')).toBe(true);
|
|
90
|
+
expect(isUUID('not-a-uuid')).toBe(false);
|
|
91
|
+
expect(isUUID('123e4567-e89b-12d3')).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
describe('isBase64', () => {
|
|
95
|
+
it('should validate base64 strings', () => {
|
|
96
|
+
expect(isBase64('SGVsbG8gV29ybGQ=')).toBe(true);
|
|
97
|
+
expect(isBase64('dGVzdA==')).toBe(true);
|
|
98
|
+
expect(isBase64('not base64')).toBe(false);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String Case Conversion Utilities
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* 첫 글자를 대문자로 변환
|
|
6
|
+
* @example capitalize('hello world') // 'Hello world'
|
|
7
|
+
*/
|
|
8
|
+
export declare function capitalize(str: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* 모든 단어의 첫 글자를 대문자로 변환
|
|
11
|
+
* @example capitalizeWords('hello world') // 'Hello World'
|
|
12
|
+
*/
|
|
13
|
+
export declare function capitalizeWords(str: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* camelCase로 변환
|
|
16
|
+
* @example camelCase('hello world') // 'helloWorld'
|
|
17
|
+
* @example camelCase('hello-world') // 'helloWorld'
|
|
18
|
+
*/
|
|
19
|
+
export declare function camelCase(str: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* PascalCase로 변환
|
|
22
|
+
* @example pascalCase('hello world') // 'HelloWorld'
|
|
23
|
+
*/
|
|
24
|
+
export declare function pascalCase(str: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* snake_case로 변환
|
|
27
|
+
* @example snakeCase('helloWorld') // 'hello_world'
|
|
28
|
+
* @example snakeCase('Hello World') // 'hello_world'
|
|
29
|
+
*/
|
|
30
|
+
export declare function snakeCase(str: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* kebab-case로 변환
|
|
33
|
+
* @example kebabCase('helloWorld') // 'hello-world'
|
|
34
|
+
* @example kebabCase('Hello World') // 'hello-world'
|
|
35
|
+
*/
|
|
36
|
+
export declare function kebabCase(str: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* CONSTANT_CASE로 변환
|
|
39
|
+
* @example constantCase('helloWorld') // 'HELLO_WORLD'
|
|
40
|
+
*/
|
|
41
|
+
export declare function constantCase(str: string): string;
|
|
42
|
+
//# sourceMappingURL=case.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"case.d.ts","sourceRoot":"","sources":["../../src/string/case.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG9C;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI7C;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO7C;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO7C;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String Case Conversion Utilities
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* 첫 글자를 대문자로 변환
|
|
6
|
+
* @example capitalize('hello world') // 'Hello world'
|
|
7
|
+
*/
|
|
8
|
+
export function capitalize(str) {
|
|
9
|
+
if (!str)
|
|
10
|
+
return str;
|
|
11
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 모든 단어의 첫 글자를 대문자로 변환
|
|
15
|
+
* @example capitalizeWords('hello world') // 'Hello World'
|
|
16
|
+
*/
|
|
17
|
+
export function capitalizeWords(str) {
|
|
18
|
+
if (!str)
|
|
19
|
+
return str;
|
|
20
|
+
return str.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* camelCase로 변환
|
|
24
|
+
* @example camelCase('hello world') // 'helloWorld'
|
|
25
|
+
* @example camelCase('hello-world') // 'helloWorld'
|
|
26
|
+
*/
|
|
27
|
+
export function camelCase(str) {
|
|
28
|
+
return str
|
|
29
|
+
.toLowerCase()
|
|
30
|
+
.replace(/[^a-zA-Z0-9]+(.)/g, (_, char) => char.toUpperCase());
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* PascalCase로 변환
|
|
34
|
+
* @example pascalCase('hello world') // 'HelloWorld'
|
|
35
|
+
*/
|
|
36
|
+
export function pascalCase(str) {
|
|
37
|
+
return capitalize(camelCase(str));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* snake_case로 변환
|
|
41
|
+
* @example snakeCase('helloWorld') // 'hello_world'
|
|
42
|
+
* @example snakeCase('Hello World') // 'hello_world'
|
|
43
|
+
*/
|
|
44
|
+
export function snakeCase(str) {
|
|
45
|
+
return str
|
|
46
|
+
.replace(/([A-Z])/g, '_$1')
|
|
47
|
+
.replace(/[\s-]+/g, '_')
|
|
48
|
+
.replace(/_+/g, '_')
|
|
49
|
+
.replace(/^_/, '')
|
|
50
|
+
.toLowerCase();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* kebab-case로 변환
|
|
54
|
+
* @example kebabCase('helloWorld') // 'hello-world'
|
|
55
|
+
* @example kebabCase('Hello World') // 'hello-world'
|
|
56
|
+
*/
|
|
57
|
+
export function kebabCase(str) {
|
|
58
|
+
return str
|
|
59
|
+
.replace(/([A-Z])/g, '-$1')
|
|
60
|
+
.replace(/[\s_]+/g, '-')
|
|
61
|
+
.replace(/-+/g, '-')
|
|
62
|
+
.replace(/^-/, '')
|
|
63
|
+
.toLowerCase();
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* CONSTANT_CASE로 변환
|
|
67
|
+
* @example constantCase('helloWorld') // 'HELLO_WORLD'
|
|
68
|
+
*/
|
|
69
|
+
export function constantCase(str) {
|
|
70
|
+
return snakeCase(str).toUpperCase();
|
|
71
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/string/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,cAAc,QAAQ,CAAC;AAGvB,cAAc,gBAAgB,CAAC;AAG/B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String Utilities
|
|
3
|
+
*
|
|
4
|
+
* This module provides utilities for string case conversion, manipulation, and validation.
|
|
5
|
+
*/
|
|
6
|
+
// Case conversion utilities
|
|
7
|
+
export * from './case';
|
|
8
|
+
// Manipulation utilities
|
|
9
|
+
export * from './manipulation';
|
|
10
|
+
// Validation utilities
|
|
11
|
+
export * from './validation';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String Manipulation Utilities
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* 문자열을 지정된 길이로 자르고 말줄임표 추가
|
|
6
|
+
* @example truncate('Hello World', 5) // 'Hello...'
|
|
7
|
+
*/
|
|
8
|
+
export declare function truncate(str: string, length: number, suffix?: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* 문자열 양쪽 공백 제거 및 연속된 공백을 하나로
|
|
11
|
+
* @example normalizeWhitespace(' hello world ') // 'hello world'
|
|
12
|
+
*/
|
|
13
|
+
export declare function normalizeWhitespace(str: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* URL 친화적인 슬러그 생성
|
|
16
|
+
* @example slugify('Hello World!') // 'hello-world'
|
|
17
|
+
* @example slugify('Test123 World') // 'test123-world'
|
|
18
|
+
*/
|
|
19
|
+
export declare function slugify(str: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* 랜덤 문자열 생성
|
|
22
|
+
* @param length - 생성할 문자열 길이
|
|
23
|
+
* @param charset - 사용할 문자 집합 ('alphanumeric', 'alpha', 'numeric', 'hex')
|
|
24
|
+
*/
|
|
25
|
+
export declare function randomString(length: number, charset?: 'alphanumeric' | 'alpha' | 'numeric' | 'hex'): string;
|
|
26
|
+
/**
|
|
27
|
+
* 문자열 반복
|
|
28
|
+
* @example repeat('ab', 3) // 'ababab'
|
|
29
|
+
*/
|
|
30
|
+
export declare function repeat(str: string, count: number): string;
|
|
31
|
+
/**
|
|
32
|
+
* 문자열 뒤집기
|
|
33
|
+
* @example reverse('hello') // 'olleh'
|
|
34
|
+
*/
|
|
35
|
+
export declare function reverse(str: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* 문자열에서 HTML 태그 제거
|
|
38
|
+
* @example stripHtml('<p>Hello <strong>World</strong></p>') // 'Hello World'
|
|
39
|
+
*/
|
|
40
|
+
export declare function stripHtml(str: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* 문자열을 특정 길이로 패딩
|
|
43
|
+
* @example padStart('5', 3, '0') // '005'
|
|
44
|
+
*/
|
|
45
|
+
export declare function padStart(str: string, length: number, fillString?: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* 문자열을 특정 길이로 패딩 (오른쪽)
|
|
48
|
+
* @example padEnd('5', 3, '0') // '500'
|
|
49
|
+
*/
|
|
50
|
+
export declare function padEnd(str: string, length: number, fillString?: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* 템플릿 문자열 치환
|
|
53
|
+
* @example template('Hello {name}!', { name: 'World' }) // 'Hello World!'
|
|
54
|
+
*/
|
|
55
|
+
export declare function template(str: string, values: Record<string, any>): string;
|
|
56
|
+
/**
|
|
57
|
+
* 마스킹 (특정 위치의 문자를 *로 변경)
|
|
58
|
+
* @example mask('1234567890', 3, 7) // '123****890'
|
|
59
|
+
*/
|
|
60
|
+
export declare function mask(str: string, start: number, end?: number, maskChar?: string): string;
|
|
61
|
+
//# sourceMappingURL=manipulation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manipulation.d.ts","sourceRoot":"","sources":["../../src/string/manipulation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAc,GAAG,MAAM,CAGpF;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQ3C;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,cAAc,GAAG,OAAO,GAAG,SAAS,GAAG,KAAsB,GACrE,MAAM,CAgBR;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAE,MAAY,GAAG,MAAM,CAEtF;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAE,MAAY,GAAG,MAAM,CAEpF;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAIzE;AAED;;;GAGG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAY,GAAG,MAAM,CAO7F"}
|