diff --git a/benchmarks/performance/unescape.bench.ts b/benchmarks/performance/unescape.bench.ts index 55d3b55a0..17a4ddcea 100644 --- a/benchmarks/performance/unescape.bench.ts +++ b/benchmarks/performance/unescape.bench.ts @@ -1,8 +1,10 @@ import { bench, describe } from 'vitest'; import { unescape as unescapeToolkit_ } from 'es-toolkit'; +import { unescape as unescapeCompatToolkit_ } from 'es-toolkit/compat'; import { unescape as unescapeLodash_ } from 'lodash'; const unescapeToolkit = unescapeToolkit_; +const unescapeCompatToolkit = unescapeCompatToolkit_; const unescapeLodash = unescapeLodash_; const longString = 'fred, barney, & pebbles'.repeat(50); @@ -12,6 +14,10 @@ describe('unescape', () => { unescapeToolkit('fred, barney, & pebbles'); }); + bench('es-toolkit/compat/unescape', () => { + unescapeCompatToolkit('fred, barney, & pebbles'); + }); + bench('lodash/unescape', () => { unescapeLodash('fred, barney, & pebbles'); }); @@ -22,6 +28,10 @@ describe('unescape/long', () => { unescapeToolkit(longString); }); + bench('es-toolkit/compat/unescape - long string', () => { + unescapeCompatToolkit(longString); + }); + bench('lodash/unescape long string', () => { unescapeLodash(longString); }); diff --git a/src/compat/index.ts b/src/compat/index.ts index 7537cd837..62ad8c21e 100644 --- a/src/compat/index.ts +++ b/src/compat/index.ts @@ -183,6 +183,7 @@ export { toUpper } from './string/toUpper.ts'; export { trim } from './string/trim.ts'; export { trimEnd } from './string/trimEnd.ts'; export { trimStart } from './string/trimStart.ts'; +export { unescape } from './string/unescape.ts'; export { upperCase } from './string/upperCase.ts'; export { upperFirst } from './string/upperFirst.ts'; export { words } from './string/words.ts'; diff --git a/src/compat/string/unescape.spec.ts b/src/compat/string/unescape.spec.ts new file mode 100644 index 000000000..95c95defd --- /dev/null +++ b/src/compat/string/unescape.spec.ts @@ -0,0 +1,51 @@ +import { describe, expect, it } from 'vitest'; +import { escape } from './escape'; +import { unescape } from './unescape'; +import { map } from '../array/map'; +import { stubString } from '../util/stubString'; + +describe('unescape', () => { + let escaped = '&<>"'/'; + let unescaped = '&<>"\'/'; + + escaped += escaped; + unescaped += unescaped; + + it('should unescape entities in order', () => { + expect(unescape('&lt;')).toBe('<'); + }); + + it('should unescape the proper entities', () => { + expect(unescape(escaped)).toBe(unescaped); + }); + + it('should handle strings with nothing to unescape', () => { + expect(unescape('abc')).toBe('abc'); + }); + + it('should unescape the same characters escaped by `_.escape`', () => { + expect(unescape(escape(unescaped))).toBe(unescaped); + }); + + it('should handle leading zeros in html entities', () => { + expect(unescape(''')).toBe("'"); + expect(unescape(''')).toBe("'"); + expect(unescape(''')).toBe("'"); + }); + + ['`', '/'].forEach(entity => { + it(`should not unescape the "${entity}" entity`, () => { + expect(unescape(entity)).toBe(entity); + }); + }); + + it('should return an empty string for empty values', () => { + // eslint-disable-next-line no-sparse-arrays + const values = [, null, undefined, '']; + const expected = map(values, stubString); + + const actual = map(values, (value, index) => (index ? unescape(value as any) : unescape())); + + expect(actual).toEqual(expected); + }); +}); diff --git a/src/compat/string/unescape.ts b/src/compat/string/unescape.ts new file mode 100644 index 000000000..be7c68430 --- /dev/null +++ b/src/compat/string/unescape.ts @@ -0,0 +1,19 @@ +import { unescape as unescapeToolkit } from '../../string/unescape.ts'; +import { toString } from '../util/toString.ts'; + +/** + * Converts the HTML entities `&`, `<`, `>`, `"`, and `'` in `str` to their corresponding characters. + * It is the inverse of `escape`. + * + * @param {string} str The string to unescape. + * @returns {string} Returns the unescaped string. + * + * @example + * unescape('This is a <div> element.'); // returns 'This is a
element.' + * unescape('This is a "quote"'); // returns 'This is a "quote"' + * unescape('This is a 'quote''); // returns 'This is a 'quote'' + * unescape('This is a & symbol'); // returns 'This is a & symbol' + */ +export function unescape(str?: string): string { + return unescapeToolkit(toString(str)); +}