Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: port more string functions from lodash
Added `camelCase`, `kebabCase`, `snakeCase` and `upperFirst`. `camelCase`, `kebabCase` and `snakeCase` work with Unicode string if engine has `Unicode property escapes` support.
- Loading branch information
Showing
10 changed files
with
252 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
language: node_js | ||
node_js: | ||
- "6" | ||
- "8" | ||
- "10" | ||
- "12" | ||
- "14" | ||
script: | ||
- npm run test:ci |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export function isUnicodePropertyEscapesSupported(): boolean { | ||
try { | ||
new RegExp(/\P{L}/u); | ||
return true; | ||
} catch (e) { | ||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import camelCase from '../camelCase'; | ||
import { isUnicodePropertyEscapesSupported } from '../../internal/support'; | ||
|
||
jest.mock('../../internal/support', () => ({ isUnicodePropertyEscapesSupported: jest.fn() })); | ||
|
||
describe('utils/string/camelCase', () => { | ||
describe('modern engines', () => { | ||
beforeAll(() => { | ||
(isUnicodePropertyEscapesSupported as jest.Mock).mockReturnValue(true); | ||
}); | ||
|
||
it.each([ | ||
['', ''], | ||
['abc', 'abc'], | ||
['Foo Bar', 'fooBar'], | ||
['foo-bar', 'fooBar'], | ||
['foo_bar', 'fooBar'], | ||
['FOO_BAR', 'fooBar'], | ||
['Foo Bär', 'fooBär'], | ||
])('should return camel cased string for %s: %s', (str, expected) => { | ||
expect(camelCase(str)).toEqual(expected); | ||
}); | ||
}); | ||
|
||
describe('legacy engines', () => { | ||
beforeAll(() => { | ||
(isUnicodePropertyEscapesSupported as jest.Mock).mockReturnValue(false); | ||
}); | ||
|
||
it.each([ | ||
['', ''], | ||
['abc', 'abc'], | ||
['Foo Bar', 'fooBar'], | ||
['foo-bar', 'fooBar'], | ||
['foo_bar', 'fooBar'], | ||
['FOO_BAR', 'fooBar'], | ||
['Foo Bär', 'fooBR'], | ||
])('should return camel cased string for %s: %s', (str, expected) => { | ||
expect(camelCase(str)).toEqual(expected); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import kebabCase from '../kebabCase'; | ||
import { isUnicodePropertyEscapesSupported } from '../../internal/support'; | ||
|
||
jest.mock('../../internal/support', () => ({ isUnicodePropertyEscapesSupported: jest.fn() })); | ||
|
||
describe('utils/string/kebabCase', () => { | ||
describe('modern engines', () => { | ||
beforeAll(() => { | ||
(isUnicodePropertyEscapesSupported as jest.Mock).mockReturnValue(true); | ||
}); | ||
|
||
it.each([ | ||
['', ''], | ||
['abc', 'abc'], | ||
['Foo Bar', 'foo-bar'], | ||
['fooBar', 'foo-bar'], | ||
['foo_Bar', 'foo-bar'], | ||
['FOO_BAR', 'foo-bar'], | ||
['Foo Bär', 'foo-bär'], | ||
])('should return kebab cased string for %s: %s', (str, expected) => { | ||
expect(kebabCase(str)).toEqual(expected); | ||
}); | ||
}); | ||
|
||
describe('legacy engines', () => { | ||
beforeAll(() => { | ||
(isUnicodePropertyEscapesSupported as jest.Mock).mockReturnValue(false); | ||
}); | ||
|
||
it.each([ | ||
['', ''], | ||
['abc', 'abc'], | ||
['Foo Bar', 'foo-bar'], | ||
['fooBar', 'foo-bar'], | ||
['foo_Bar', 'foo-bar'], | ||
['FOO_BAR', 'foo-bar'], | ||
['Foo Bär', 'foo-b-r'], | ||
])('should return kebab cased string for %s: %s', (str, expected) => { | ||
expect(kebabCase(str)).toEqual(expected); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import snakeCase from '../snakeCase'; | ||
|
||
import { isUnicodePropertyEscapesSupported } from '../../internal/support'; | ||
|
||
jest.mock('../../internal/support', () => ({ isUnicodePropertyEscapesSupported: jest.fn() })); | ||
|
||
describe('utils/string/snakeCase', () => { | ||
describe('modern engines', () => { | ||
beforeAll(() => { | ||
(isUnicodePropertyEscapesSupported as jest.Mock).mockReturnValue(true); | ||
}); | ||
|
||
it.each([ | ||
['', ''], | ||
['abc', 'abc'], | ||
['Foo Bar', 'foo_bar'], | ||
['fooBar', 'foo_bar'], | ||
['foo_Bar', 'foo_bar'], | ||
['foo-Bar', 'foo_bar'], | ||
['FOO_BAR', 'foo_bar'], | ||
['Foo Bär', 'foo_bär'], | ||
])('should return snake cased string for %s: %s', (str, expected) => { | ||
expect(snakeCase(str)).toEqual(expected); | ||
}); | ||
}); | ||
|
||
describe('legacy engines', () => { | ||
beforeAll(() => { | ||
(isUnicodePropertyEscapesSupported as jest.Mock).mockReturnValue(false); | ||
}); | ||
|
||
it.each([ | ||
['', ''], | ||
['abc', 'abc'], | ||
['Foo Bar', 'foo_bar'], | ||
['fooBar', 'foo_bar'], | ||
['foo_Bar', 'foo_bar'], | ||
['foo-Bar', 'foo_bar'], | ||
['FOO_BAR', 'foo_bar'], | ||
['Foo Bär', 'foo_b_r'], | ||
])('should return snake cased string for %s: %s', (str, expected) => { | ||
expect(snakeCase(str)).toEqual(expected); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import upperFirst from '../upperFirst'; | ||
|
||
describe('utils/string/upperFirst', () => { | ||
it.each([ | ||
['', ''], | ||
['foo', 'Foo'], | ||
['FOO', 'FOO'], | ||
['über', 'Über'], | ||
])('should convert the first character of string to upper case for %s: %s', (str, expected) => { | ||
expect(upperFirst(str)).toEqual(expected); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { isUnicodePropertyEscapesSupported } from '../internal/support'; | ||
|
||
/** | ||
* Converts string to camel case. | ||
* | ||
* @param {String} str The string to convert. | ||
* @return {String} The camel cased string. | ||
* @example | ||
* | ||
* camelCase('Foo Bar'); //=> 'fooBar' | ||
* camelCase('foo-bar'); //=> 'fooBar' | ||
* camelCase('foo_bar'); //=> 'fooBar' | ||
* camelCase('FOO_BAR'); //=> 'fooBar' | ||
* camelCase('Foo Bär'); //=> 'fooBär' only with Unicode property escapes support | ||
*/ | ||
function camelCase(str: string): string { | ||
const regex = isUnicodePropertyEscapesSupported() ? /\P{L}+(.)/gu : /[\W_]+(.)/g; | ||
|
||
return str.toLowerCase().replace(regex, (match, chr) => chr.toUpperCase()); | ||
} | ||
|
||
export default camelCase; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { isUnicodePropertyEscapesSupported } from '../internal/support'; | ||
|
||
/** | ||
* Converts string to kebab case. | ||
* | ||
* @param {String} str The string to convert. | ||
* @return {String} The kebab cased string. | ||
* @example | ||
* | ||
* kebabCase('Foo Bar'); //=> 'foo-bar' | ||
* kebabCase('fooBar'); //=> 'foo-bar' | ||
* kebabCase('foo_bar'); //=> 'foo-bar' | ||
* kebabCase('FOO_BAR'); //=> 'foo-bar' | ||
* kebabCase('Foo Bär'); //=> 'foo-bär' only with Unicode property escapes support | ||
*/ | ||
function kebabCase(str: string): string { | ||
const unicodePropertyEscapesSupported = isUnicodePropertyEscapesSupported(); | ||
const camelCaseRegex = unicodePropertyEscapesSupported ? /(\p{Lower})(\p{Upper})/gu : /([a-z])([A-Z])/g; | ||
const nonLettersRegex = unicodePropertyEscapesSupported ? /\P{L}+/gu : /[\W_]+/g; | ||
|
||
return ( | ||
str | ||
// handle camelCase | ||
.replace(camelCaseRegex, (match, first, second) => `${first}-${second}`) | ||
// replace non-letters with hyphens | ||
.replace(nonLettersRegex, '-') | ||
.toLowerCase() | ||
); | ||
} | ||
|
||
export default kebabCase; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { isUnicodePropertyEscapesSupported } from '../internal/support'; | ||
|
||
/** | ||
* Returns the snake case version of a string. | ||
* | ||
* @param {String} str The string to switch to snake case. | ||
* @return {String} The snake case version of `str`. | ||
* @example | ||
* | ||
* snakeCase('Foo Bar'); //=> 'foo_bar' | ||
* snakeCase('fooBar'); //=> 'foo_bar' | ||
* snakeCase('foo-bar'); //=> 'foo_bar' | ||
* snakeCase('FOO-BAR'); //=> 'foo_bar' | ||
* snakeCase('Foo Bär'); //=> 'foo_bär' only with Unicode property escapes support | ||
*/ | ||
function snakeCase(str: string): string { | ||
const unicodePropertyEscapesSupported = isUnicodePropertyEscapesSupported(); | ||
const camelCaseRegex = unicodePropertyEscapesSupported ? /(\p{Lower})(\p{Upper})/gu : /([a-z])([A-Z])/g; | ||
const nonLettersRegex = unicodePropertyEscapesSupported ? /\P{L}+/gu : /[\W_]+/g; | ||
|
||
return ( | ||
str | ||
// handle camelCase | ||
.replace(camelCaseRegex, (match, first, second) => `${first}-${second}`) | ||
// replace non-letters with underscore | ||
.replace(nonLettersRegex, '_') | ||
.toLowerCase() | ||
); | ||
} | ||
|
||
export default snakeCase; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/** | ||
* Converts the first character of string to upper case and returns the new string. | ||
* | ||
* @param {String} str The string to convert. | ||
* @return {String} The converted string. | ||
* @example | ||
* | ||
* upperFirst('foo'); //=> 'Foo' | ||
* upperFirst('über'); //=> 'Über' | ||
* upperFirst('FOO'); //=> 'FOO' | ||
*/ | ||
function upperFirst(str: string): string { | ||
return `${str.charAt(0).toUpperCase()}${str.slice(1)}`; | ||
} | ||
|
||
export default upperFirst; |