diff --git a/.scripts/docs/operations/toDocumentationItem.ts b/.scripts/docs/operations/toDocumentationItem.ts index 6835f2545..c99fa8627 100644 --- a/.scripts/docs/operations/toDocumentationItem.ts +++ b/.scripts/docs/operations/toDocumentationItem.ts @@ -79,11 +79,11 @@ function toParameters(symbolName: string, tags: JsDocTag[]): DocumentationItem[' } if (param.type == null) { - throw new Error(`parameter type is not provided in ${symbolName}.`); + throw new Error(`parameter type is not provided in ${symbolName}. (for parameter ${param.name})`); } if (param.doc == null) { - throw new Error(`parameter doc is not provided in ${symbolName}.`); + throw new Error(`parameter doc is not provided in ${symbolName}. (for parameter ${param.name})`); } return { diff --git a/.scripts/postbuild.sh b/.scripts/postbuild.sh index 815dd9f85..8ccb5f9db 100755 --- a/.scripts/postbuild.sh +++ b/.scripts/postbuild.sh @@ -4,6 +4,7 @@ # TypeScript projects configured with "moduleResolution": "node10" # (which is the default when using "module": "commonjs"). echo "export * from './dist/array';" > array.d.ts +echo "export * from './dist/error';" > error.d.ts echo "export * from './dist/compat';" > compat.d.ts echo "export * from './dist/function';" > function.d.ts echo "export * from './dist/math';" > math.d.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cd89f0e0..07654d224 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,45 @@ # es-toolkit Changelog +## Version v1.31.0 + +Released on December 27th, 2024. + +- Added support for the [windowed](https://es-toolkit.slash.page/reference/array/windowed.html), [remove](https://es-toolkit.slash.page/reference/array/remove.html) and [asyncNoop](https://es-toolkit.slash.page/reference/function/asyncNoop.html) functions. +- Introduced compatibility functions for [pullAll](https://es-toolkit.slash.page/reference/compat/array/pullAll.html), [subtract](https://es-toolkit.slash.page/reference/compat/math/subtract.html), [isBuffer](https://es-toolkit.slash.page/reference/predicate/isBuffer.html), and [methodOf](https://es-toolkit.slash.page/reference/compat/util/methodOf.html). +- Enhanced the performance of [pull](https://es-toolkit.slash.page/reference/array/pull.html) when working with large arrays. +- Resolved an issue where [reverse](https://es-toolkit.slash.page/reference/compat/array/reverse.html) was not being exported in our compatibility library. +- Updated [groupBy](https://es-toolkit.slash.page/reference/array/groupBy.html) to properly handle keys like `toString` or `valueOf`. +- Fixed [merge](https://es-toolkit.slash.page/reference/object/merge.html) to correctly merge values when `target` or any of its values are `null` or `undefined`. + +We sincerely thank @T3sT3ro, @D-Sketon, @tuhm1, @willmanduffy, @apeltop, @aken-you, @SaeWooKKang, and @ssi02014 for their contributions. We appreciate your great efforts! + +## Version v1.30.1 + +Released on December 14th, 2024. + +- Fixed [uniqueId](https://es-toolkit.slash.page/reference/compat/util/uniqueId.html) not being exported in our compatibility library. + +We sincerely thank @redd97 for their contributions. We appreciate your great efforts! + +## Version v1.30.0 + +Released on December 13th, 2024. + +- Introduced support for [pull](https://es-toolkit.slash.page/reference/array/pull.html). +- Added compatibility functions for [map](https://es-toolkit.slash.page/reference/compat/array/map.html), [range](https://es-toolkit.slash.page/reference/math/range.html), [rangeRight](https://es-toolkit.slash.page/reference/math/rangeRight.html), [differenceWith](https://es-toolkit.slash.page/reference/array/differenceWith.html), [nth](https://es-toolkit.slash.page/reference/compat/array/nth.html), [noop](https://es-toolkit.slash.page/reference/function/noop.html), [identity](https://es-toolkit.slash.page/reference/function/identity.html), [keys](https://es-toolkit.slash.page/reference/compat/object/keys.html), [propertyOf](https://es-toolkit.slash.page/reference/compat/object/propertyOf.html), [nthArg](https://es-toolkit.slash.page/reference/compat/function/nthArg.html), [delay](https://es-toolkit.slash.page/reference/promise/delay.html), [toPlainObject](https://es-toolkit.slash.page/reference/compat/util/toPlainObject.html), [unary](https://es-toolkit.slash.page/reference/function/unary.html), [once](https://es-toolkit.slash.page/reference/function/once.html), [after](https://es-toolkit.slash.page/reference/function/after.html), [takeRightWhile](https://es-toolkit.slash.page/reference/array/takeRightWhile.html), [escapeRegExp](https://es-toolkit.slash.page/reference/string/escapeRegExp.html), [unescape](https://es-toolkit.slash.page/reference/string/unescape.html), [upperFirst](https://es-toolkit.slash.page/reference/string/upperFirst.html), [lowerFirst](https://es-toolkit.slash.page/reference/string/lowerFirst.html), [deburr](https://es-toolkit.slash.page/reference/string/deburr.html), [lt](https://es-toolkit.slash.page/reference/util/lt.html), [lte](https://es-toolkit.slash.page/reference/util/lte.html), [toLower](https://es-toolkit.slash.page/reference/compat/string/toLower.html), [invoke](https://es-toolkit.slash.page/reference/compat/util/invoke.html), [method](https://es-toolkit.slash.page/reference/compat/util/method.html), [reverse](https://es-toolkit.slash.page/reference/compat/array/reverse.html), [now](https://es-toolkit.slash.page/reference/compat/util/now.html), [findKey](https://es-toolkit.slash.page/reference/object/findKey.html), [stubArray](https://es-toolkit.slash.page/reference/compat/util/stubArray.html), [stubFalse](https://es-toolkit.slash.page/reference/compat/util/stubFalse.html), [stubObject](https://es-toolkit.slash.page/reference/compat/util/stubObject.html), [stubString](https://es-toolkit.slash.page/reference/compat/util/stubString.html), and [stubTrue](https://es-toolkit.slash.page/reference/compat/util/stubTrue.html). + +We sincerely thank @healtheloper, @mass2527, @D-Sketon, @eunhyulkim, @scato3, @Na-hyunwoo, and @dasom-jo for their contributions. We appreciate your great efforts! + +## Version v1.29.0 + +Released on December 1st, 2024. + +- Introduced support for [cloneDeepWith](https://es-toolkit.slash.page/reference/object/cloneDeepWith.html). +- Added a compatibility function for [lastIndexOf](https://es-toolkit.slash.page/reference/compat/array/lastIndexOf.html). +- Fixed an issue in [flattenObject](https://es-toolkit.slash.page/reference/object/flattenObject.html) where nested objects in arrays were not flattened correctly. + +We sincerely thank @nnnnoel and @evan-moon for their contributions. We appreciate your great efforts! + ## Version v1.28.0 Released on November 30th, 2024. diff --git a/benchmarks/bundle-size/lastIndexOf.spec.ts b/benchmarks/bundle-size/lastIndexOf.spec.ts new file mode 100644 index 000000000..6d4bd785a --- /dev/null +++ b/benchmarks/bundle-size/lastIndexOf.spec.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from 'vitest'; +import { getBundleSize } from './utils/getBundleSize'; + +describe('lastIndexOf bundle size', () => { + it('lodash-es', async () => { + const bundleSize = await getBundleSize('lodash-es', 'lastIndexOf'); + expect(bundleSize).toMatchInlineSnapshot(`1586`); + }); + + it('es-toolkit', async () => { + const bundleSize = await getBundleSize('es-toolkit', 'lastIndexOf'); + expect(bundleSize).toMatchInlineSnapshot(`235`); + }); + + it('es-toolkit/compat', async () => { + const bundleSize = await getBundleSize('es-toolkit/compat', 'lastIndexOf'); + expect(bundleSize).toMatchInlineSnapshot(`435`); + }); +}); diff --git a/benchmarks/performance/after.bench.ts b/benchmarks/performance/after.bench.ts index ba7906e5e..446818a90 100644 --- a/benchmarks/performance/after.bench.ts +++ b/benchmarks/performance/after.bench.ts @@ -1,8 +1,10 @@ import { bench, describe } from 'vitest'; import { after as afterToolkit_ } from 'es-toolkit'; +import { after as afterCompatToolkit_ } from 'es-toolkit/compat'; import { after as afterLodash_ } from 'lodash'; const afterToolkit = afterToolkit_; +const afterCompatToolkit = afterCompatToolkit_; const afterLodash = afterLodash_; describe('after', () => { @@ -15,6 +17,15 @@ describe('after', () => { } }); + bench('es-toolkit/compat/after', () => { + const add = (a: number, b: number) => a + b; + const n = 10; + const afterFn = afterCompatToolkit(n, add); + for (let i = 0; i < n + 1; i++) { + afterFn(1, 2); + } + }); + bench('lodash/after', () => { const add = (a: number, b: number) => a + b; const n = 10; diff --git a/benchmarks/performance/deburr.bench.ts b/benchmarks/performance/deburr.bench.ts index 29977d6df..f87c8f758 100644 --- a/benchmarks/performance/deburr.bench.ts +++ b/benchmarks/performance/deburr.bench.ts @@ -1,8 +1,10 @@ import { bench, describe } from 'vitest'; import { deburr as deburrToolkit_ } from 'es-toolkit'; +import { deburr as deburrCompatToolkit_ } from 'es-toolkit/compat'; import { deburr as deburrLodash_ } from 'lodash'; const deburrToolkit = deburrToolkit_; +const deburrCompatToolkit = deburrCompatToolkit_; const deburrLodash = deburrLodash_; const longWord = 'déjà vu'.repeat(1000); @@ -11,6 +13,10 @@ describe('deburr', () => { deburrLodash('déjà vu'); }); + bench('es-toolkit/compat/deburr', () => { + deburrCompatToolkit('déjà vu'); + }); + bench('es-toolkit/deburr', () => { deburrToolkit('déjà vu'); }); @@ -19,6 +25,10 @@ describe('deburr', () => { deburrLodash(longWord); }); + bench('es-toolkit/compat/deburr - long words', () => { + deburrCompatToolkit(longWord); + }); + bench('es-toolkit/deburr - long words', () => { deburrToolkit(longWord); }); diff --git a/benchmarks/performance/delay.bench.ts b/benchmarks/performance/delay.bench.ts new file mode 100644 index 000000000..df9539ef1 --- /dev/null +++ b/benchmarks/performance/delay.bench.ts @@ -0,0 +1,20 @@ +import { bench, describe } from 'vitest'; +import { delay as delayToolkitCompat_ } from 'es-toolkit/compat'; +import { delay as delayLodash_ } from 'lodash'; + +const delayToolkitCompat = delayToolkitCompat_; +const delayLodash = delayLodash_; + +describe('delay', () => { + bench('es-toolkit/compat/delay', () => { + delayToolkitCompat(() => { + console.info('hello'); + }, 1000); + }); + + bench('lodash/delay', () => { + delayLodash(() => { + console.info('hello'); + }, 1000); + }); +}); diff --git a/benchmarks/performance/differenceWith.bench.ts b/benchmarks/performance/differenceWith.bench.ts index e08beaeec..a386b9f21 100644 --- a/benchmarks/performance/differenceWith.bench.ts +++ b/benchmarks/performance/differenceWith.bench.ts @@ -1,8 +1,10 @@ import { bench, describe } from 'vitest'; import { differenceWith as differenceWithToolkit_ } from 'es-toolkit'; +import { differenceWith as differenceWithCompatToolkit_ } from 'es-toolkit/compat'; import { differenceWith as differenceWithLodash_ } from 'lodash'; const differenceWithToolkit = differenceWithToolkit_; +const differenceWithCompatToolkit = differenceWithCompatToolkit_; const differenceWithLodash = differenceWithLodash_; describe('differenceWith', () => { @@ -10,6 +12,10 @@ describe('differenceWith', () => { differenceWithToolkit([1.2, 2.3, 3.4], [1.2], (x, y) => Math.floor(x) === Math.floor(y)); }); + bench('es-toolkit/compat/differenceWith', () => { + differenceWithCompatToolkit([1.2, 2.3, 3.4], [1.2], (x, y) => Math.floor(x) === Math.floor(y)); + }); + bench('lodash/differenceWith', () => { differenceWithLodash([1.2, 2.3, 3.4], [1.2], (x, y) => Math.floor(x) === Math.floor(y)); }); @@ -23,6 +29,10 @@ describe('differenceWith/largeArray', () => { differenceWithToolkit(largeArray, largeArray2, (x, y) => Math.floor(x) === Math.floor(y)); }); + bench('es-toolkit/compat/differenceWith', () => { + differenceWithCompatToolkit(largeArray, largeArray2, (x, y) => Math.floor(x) === Math.floor(y)); + }); + bench('lodash/differenceWith', () => { differenceWithLodash(largeArray, largeArray2, (x, y) => Math.floor(x) === Math.floor(y)); }); diff --git a/benchmarks/performance/escapeRegExp.bench.ts b/benchmarks/performance/escapeRegExp.bench.ts index c5b998040..78b92cc08 100644 --- a/benchmarks/performance/escapeRegExp.bench.ts +++ b/benchmarks/performance/escapeRegExp.bench.ts @@ -1,8 +1,10 @@ import { bench, describe } from 'vitest'; import { escapeRegExp as escapeRegExpToolkit_ } from 'es-toolkit'; +import { escapeRegExp as escapeRegExpCompatToolkit_ } from 'es-toolkit/compat'; import { escapeRegExp as escapeRegExpLodash_ } from 'lodash'; const escapeRegExpToolkit = escapeRegExpToolkit_; +const escapeRegExpCompatToolkit = escapeRegExpCompatToolkit_; const escapeRegExpLodash = escapeRegExpLodash_; describe('escape', () => { @@ -11,6 +13,10 @@ describe('escape', () => { escapeRegExpToolkit('^$.*+?()[]{}|\\'); }); + bench('es-toolkit/compat/escapeRegExp', () => { + escapeRegExpCompatToolkit('^$.*+?()[]{}|\\'); + }); + bench('lodash/escapeRegExp', () => { escapeRegExpLodash('^$.*+?()[]{}|\\'); }); @@ -19,6 +25,10 @@ describe('escape', () => { escapeRegExpToolkit(longString); }); + bench('es-toolkit/compat/escapeRegExp - long string', () => { + escapeRegExpCompatToolkit(longString); + }); + bench('lodash/escapeRegExp long string', () => { escapeRegExpLodash(longString); }); diff --git a/benchmarks/performance/findKey.bench.ts b/benchmarks/performance/findKey.bench.ts index 22ed0ef22..4b33118be 100644 --- a/benchmarks/performance/findKey.bench.ts +++ b/benchmarks/performance/findKey.bench.ts @@ -1,5 +1,6 @@ import { bench, describe } from 'vitest'; import { findKey as findKeyToolkit } from 'es-toolkit'; +import { findKey as findKeyCompatToolkit } from 'es-toolkit/compat'; import { findKey as findKeyLodash } from 'lodash'; describe('findKey', () => { @@ -16,6 +17,10 @@ describe('findKey', () => { bench('lodash/findKey', () => { findKeyLodash(users, o => o.age < 40); }); + + bench('es-toolkit/compat/findKey', () => { + findKeyCompatToolkit(users, o => o.age < 40); + }); }); describe('findKey/largeObject', () => { @@ -31,4 +36,8 @@ describe('findKey/largeObject', () => { bench('lodash/findKey', () => { findKeyLodash(largeUsers, o => o.age === 7000); }); + + bench('es-toolkit/compat/findKey', () => { + findKeyCompatToolkit(largeUsers, o => o.age === 7000); + }); }); diff --git a/benchmarks/performance/isBuffer.bench.ts b/benchmarks/performance/isBuffer.bench.ts new file mode 100644 index 000000000..7aa73c7aa --- /dev/null +++ b/benchmarks/performance/isBuffer.bench.ts @@ -0,0 +1,22 @@ +import { bench, describe } from 'vitest'; +import { isBuffer as isBufferToolkit_ } from 'es-toolkit'; +import { isBuffer as isBufferToolkitCompat_ } from 'es-toolkit/compat'; +import { isBuffer as isBufferLodash_ } from 'lodash'; + +const isBufferToolkit = isBufferToolkit_; +const isBufferToolkitCompat = isBufferToolkitCompat_; +const isBufferLodash = isBufferLodash_; + +describe('isBuffer', () => { + bench('es-toolkit/isBuffer', () => { + isBufferToolkit(Buffer.from('test')); + }); + + bench('es-toolkit/compat/isBuffer', () => { + isBufferToolkitCompat(Buffer.from('test')); + }); + + bench('lodash/isBuffer', () => { + isBufferLodash(Buffer.from('test')); + }); +}); diff --git a/benchmarks/performance/kebabCase.bench.ts b/benchmarks/performance/kebabCase.bench.ts index b4c60b0a0..ddfd6b06f 100644 --- a/benchmarks/performance/kebabCase.bench.ts +++ b/benchmarks/performance/kebabCase.bench.ts @@ -16,7 +16,7 @@ describe('kebabCase', () => { kebabCaseToolkit('hello-World_of XML_httpRequest'); }); - bench('es-toolkit/comapt/kebabCase', () => { + bench('es-toolkit/compat/kebabCase', () => { kebabCaseToolkitCompat('hello world'); kebabCaseToolkitCompat('--foo--bar__baz 123'); kebabCaseToolkitCompat('123numericValues'); diff --git a/benchmarks/performance/lastIndexOf.bench.ts b/benchmarks/performance/lastIndexOf.bench.ts new file mode 100644 index 000000000..8c8eea7fc --- /dev/null +++ b/benchmarks/performance/lastIndexOf.bench.ts @@ -0,0 +1,29 @@ +import { bench, describe } from 'vitest'; +import { lastIndexOf as lastIndexOfToolkitCompat_ } from 'es-toolkit/compat'; +import { lastIndexOf as lastIndexOfLodash_ } from 'lodash'; + +const indexOfToolkitCompat = lastIndexOfToolkitCompat_; +const indexOfLodash = lastIndexOfLodash_; + +describe('lastIndexOf', () => { + const largeArray = Array(1_000_000) + .fill(0) + .map((_, i) => i); + const array = [1, 2, 3, 4, NaN, '1', '2', '3', '4', 'NaN', ...largeArray]; + + bench('es-toolkit/compat/lastIndexOf', () => { + indexOfToolkitCompat(array, 1); + indexOfToolkitCompat(array, NaN); + indexOfToolkitCompat(array, '1'); + indexOfToolkitCompat(array, 'NaN', -5); + indexOfToolkitCompat(array, 'NaN', -100); + }); + + bench('lodash/lastIndexOf', () => { + indexOfLodash(array, 1); + indexOfLodash(array, NaN); + indexOfLodash(array, '1'); + indexOfLodash(array, 'NaN', -5); + indexOfLodash(array, 'NaN', -100); + }); +}); diff --git a/benchmarks/performance/lowerFirst.bench.ts b/benchmarks/performance/lowerFirst.bench.ts index 04901b867..38a7273df 100644 --- a/benchmarks/performance/lowerFirst.bench.ts +++ b/benchmarks/performance/lowerFirst.bench.ts @@ -1,8 +1,10 @@ import { bench, describe } from 'vitest'; import { lowerFirst as lowerFirstToolkit_ } from 'es-toolkit'; +import { lowerFirst as lowerFirstCompatToolkit_ } from 'es-toolkit/compat'; import { lowerFirst as lowerFirstLodash_ } from 'lodash'; const lowerFirstToolkit = lowerFirstToolkit_; +const lowerFirstCompatToolkit = lowerFirstCompatToolkit_; const lowerFirstLodash = lowerFirstLodash_; describe('lowerFirst', () => { @@ -12,6 +14,11 @@ describe('lowerFirst', () => { lowerFirstToolkit(str); }); + bench('es-toolkit/compat/lowerFirst', () => { + const str = 'camelCase'; + lowerFirstCompatToolkit(str); + }); + bench('lodash/lowerFirst', () => { const str = 'camelCase'; lowerFirstLodash(str); @@ -24,6 +31,10 @@ describe('lowerFirst', () => { lowerFirstToolkit(LONG_STR); }); + bench('es-toolkit/compat/lowerFirst', () => { + lowerFirstCompatToolkit(LONG_STR); + }); + bench('lodash/lowerFirst', () => { lowerFirstLodash(LONG_STR); }); diff --git a/benchmarks/performance/map.bench.ts b/benchmarks/performance/map.bench.ts new file mode 100644 index 000000000..09b8048a8 --- /dev/null +++ b/benchmarks/performance/map.bench.ts @@ -0,0 +1,63 @@ +import { bench, describe } from 'vitest'; +import { map as mapToolkit_ } from 'es-toolkit/compat'; +import { map as mapLodash_ } from 'lodash'; + +const mapToolkit = mapToolkit_; +const mapLodash = mapLodash_; + +const generateArray = (length: number) => Array.from({ length }, (_, i) => i); +const generateObject = (length: number) => Object.fromEntries(generateArray(length).map(i => [`key${i}`, i])); + +describe('map/array', () => { + const array = [1, 2, 3, 4, 5]; + + bench('es-toolkit/map', () => { + mapToolkit(array, value => value * 2); + }); + + bench('lodash/map', () => { + mapLodash(array, value => value * 2); + }); +}); + +describe('map/object', () => { + const obj = { + key1: 1, + key2: 2, + key3: 3, + key4: 4, + key5: 5, + }; + + bench('es-toolkit/map', () => { + mapToolkit(obj, value => value * 2); + }); + + bench('lodash/map', () => { + mapLodash(obj, value => value * 2); + }); +}); + +describe('map/largeArray', () => { + const largeArray = generateArray(10000); + + bench('es-toolkit/map', () => { + mapToolkit(largeArray, value => value * 2); + }); + + bench('lodash/map', () => { + mapLodash(largeArray, value => value * 2); + }); +}); + +describe('map/largeObject', () => { + const largeObject = generateObject(10000); + + bench('es-toolkit/map', () => { + mapToolkit(largeObject, value => value * 2); + }); + + bench('lodash/map', () => { + mapLodash(largeObject, value => value * 2); + }); +}); diff --git a/benchmarks/performance/method.bench.ts b/benchmarks/performance/method.bench.ts new file mode 100644 index 000000000..4a9c11668 --- /dev/null +++ b/benchmarks/performance/method.bench.ts @@ -0,0 +1,24 @@ +import { bench, describe } from 'vitest'; +import { method as methodToolkit_ } from 'es-toolkit/compat'; +import { method as methodLodash_ } from 'lodash'; + +const methodToolkit = methodToolkit_; +const methodLodash = methodLodash_; + +const object = { + a: { + b: function (x: number, y: number) { + return x + y; + }, + }, +}; + +describe('method', () => { + bench('es-toolkit/compat', () => { + methodToolkit('a.b', 1, 2)(object); + }); + + bench('lodash', () => { + methodLodash('a.b', 1, 2)(object); + }); +}); diff --git a/benchmarks/performance/methodOf.bench.ts b/benchmarks/performance/methodOf.bench.ts new file mode 100644 index 000000000..fd167ccb8 --- /dev/null +++ b/benchmarks/performance/methodOf.bench.ts @@ -0,0 +1,24 @@ +import { bench, describe } from 'vitest'; +import { methodOf as methodOfToolkit_ } from 'es-toolkit/compat'; +import { methodOf as methodOfLodash_ } from 'lodash'; + +const methodOfToolkit = methodOfToolkit_; +const methodOfLodash = methodOfLodash_; + +const object = { + a: { + b: function (x: number, y: number) { + return x + y; + }, + }, +}; + +describe('methodOf', () => { + bench('es-toolkit/compat', () => { + methodOfToolkit(object, 1, 2)('a.b'); + }); + + bench('lodash', () => { + methodOfLodash(object, 1, 2)('a.b'); + }); +}); diff --git a/benchmarks/performance/multiply.bench.ts b/benchmarks/performance/multiply.bench.ts new file mode 100644 index 000000000..6978e1fda --- /dev/null +++ b/benchmarks/performance/multiply.bench.ts @@ -0,0 +1,24 @@ +import { bench, describe } from 'vitest'; +import { multiply as multiplyToolkitCompat_ } from 'es-toolkit/compat'; +import { multiply as multiplyLodash_ } from 'lodash'; + +const multiplyToolkitCompat = multiplyToolkitCompat_; +const multiplyLodash = multiplyLodash_; + +describe('multiply function benchmark', () => { + bench('es-toolkit/compat/multiply', () => { + multiplyToolkitCompat(3, 4); + multiplyToolkitCompat(-3, -4); + multiplyToolkitCompat(NaN, 3); + multiplyToolkitCompat(3, NaN); + multiplyToolkitCompat(NaN, NaN); + }); + + bench('lodash/multiply', () => { + multiplyLodash(3, 4); + multiplyLodash(-3, -4); + multiplyLodash(NaN, 3); + multiplyLodash(3, NaN); + multiplyLodash(NaN, NaN); + }); +}); diff --git a/benchmarks/performance/negate.bench.ts b/benchmarks/performance/negate.bench.ts index 98f5ba7a7..4dad93444 100644 --- a/benchmarks/performance/negate.bench.ts +++ b/benchmarks/performance/negate.bench.ts @@ -1,8 +1,10 @@ import { bench, describe } from 'vitest'; import { negate as negateToolkit_ } from 'es-toolkit'; +import { negate as negateCompatToolkit_ } from 'es-toolkit/compat'; import { negate as negateLodash_ } from 'lodash'; const negateToolkit = negateToolkit_; +const negateCompatToolkit = negateCompatToolkit_; const negateLodash = negateLodash_; describe('compact', () => { @@ -10,6 +12,10 @@ describe('compact', () => { negateToolkit(() => true)(); }); + bench('es-toolkit/compat', () => { + negateCompatToolkit(() => true)(); + }); + bench('lodash', () => { negateLodash(() => true)(); }); diff --git a/benchmarks/performance/nth.bench.ts b/benchmarks/performance/nth.bench.ts new file mode 100644 index 000000000..2b6957174 --- /dev/null +++ b/benchmarks/performance/nth.bench.ts @@ -0,0 +1,34 @@ +import { bench, describe } from 'vitest'; +import { nth as nthToolkitCompat_ } from 'es-toolkit/compat'; +import { nth as nthLodash_ } from 'lodash'; + +const nthToolkitCompat = nthToolkitCompat_; +const nthLodash = nthLodash_; + +describe('nth', () => { + const array = [1, 2, 3]; + + bench('es-toolkit/compat/nth', () => { + nthToolkitCompat(array, 1); + nthToolkitCompat(array, -1); + }); + + bench('lodash/nth', () => { + nthLodash(array, 1); + nthLodash(array, -1); + }); +}); + +describe('nth/largeArray', () => { + const largeArray = Array.from({ length: 10000 }, (_, i) => i); + + bench('es-toolkit/compat/nth', () => { + nthToolkitCompat(largeArray, 1); + nthToolkitCompat(largeArray, -1); + }); + + bench('lodash/nth', () => { + nthLodash(largeArray, 1); + nthLodash(largeArray, -1); + }); +}); diff --git a/benchmarks/performance/nthArg.bench.ts b/benchmarks/performance/nthArg.bench.ts new file mode 100644 index 000000000..64ac5086a --- /dev/null +++ b/benchmarks/performance/nthArg.bench.ts @@ -0,0 +1,34 @@ +import { bench, describe } from 'vitest'; +import { nthArg as nthArgToolkitCompat_ } from 'es-toolkit/compat'; +import { nthArg as nthArgLodash_ } from 'lodash'; + +const nthArgToolkitCompat = nthArgToolkitCompat_; +const nthArgLodash = nthArgLodash_; + +describe('nthArg', () => { + const array = [1, 2, 3]; + + bench('es-toolkit/compat/nthArg', () => { + nthArgToolkitCompat(0)(...array); + nthArgToolkitCompat(-1)(...array); + }); + + bench('lodash/nthArg', () => { + nthArgLodash(0)(...array); + nthArgLodash(-1)(...array); + }); +}); + +describe('nthArg/largeArray', () => { + const largeArray = Array.from({ length: 10000 }, (_, i) => i); + + bench('es-toolkit/compat/nthArg', () => { + nthArgToolkitCompat(0)(...largeArray); + nthArgToolkitCompat(-1)(...largeArray); + }); + + bench('lodash/nthArg', () => { + nthArgLodash(0)(...largeArray); + nthArgLodash(-1)(...largeArray); + }); +}); diff --git a/benchmarks/performance/propertyOf.bench.ts b/benchmarks/performance/propertyOf.bench.ts new file mode 100644 index 000000000..96f44be1b --- /dev/null +++ b/benchmarks/performance/propertyOf.bench.ts @@ -0,0 +1,18 @@ +import { bench, describe } from 'vitest'; +import { propertyOf as propertyOfToolkit_ } from 'es-toolkit/compat'; +import { propertyOf as propertyOfLodash_ } from 'lodash'; + +const propertyOfToolkit = propertyOfToolkit_; +const propertyOfLodash = propertyOfLodash_; + +describe('propertyOf', () => { + bench('es-toolkit/propertyOf', () => { + const getValue = propertyOfToolkit({ 'a.b': 1, a: { b: 1 } }); + getValue('a.b'); + }); + + bench('lodash/propertyOf', () => { + const getValue = propertyOfLodash({ 'a.b': 1, a: { b: 1 } }); + getValue('a.b'); + }); +}); diff --git a/benchmarks/performance/pull.bench.ts b/benchmarks/performance/pull.bench.ts new file mode 100644 index 000000000..c889a08bb --- /dev/null +++ b/benchmarks/performance/pull.bench.ts @@ -0,0 +1,42 @@ +import { bench, describe } from 'vitest'; +import { pull as pullToolkit } from 'es-toolkit/compat'; +import { pull as pullLodash } from 'lodash'; + +describe('pull array size 100', () => { + const array = [...Array(100)].map((_, i) => i); + const even = [...Array(50)].map((_, i) => i * 2); + + bench('es-toolkit/pull', () => { + pullToolkit([...array], even); + }); + + bench('lodash/pull', () => { + pullLodash([...array], ...even); + }); +}); + +describe('pull array size 1000', () => { + const array = [...Array(1000)].map((_, i) => i); + const even = [...Array(500)].map((_, i) => i * 2); + + bench('es-toolkit/pull', () => { + pullToolkit([...array], [...even]); + }); + + bench('lodash/pull', () => { + pullLodash([...array], ...even); + }); +}); + +describe('pull array size 10000', () => { + const array = [...Array(10000)].map((_, i) => i); + const even = [...Array(5000)].map((_, i) => i * 2); + + bench('es-toolkit/pull', () => { + pullToolkit([...array], [...even]); + }); + + bench('lodash/pull', () => { + pullLodash([...array], ...even); + }); +}); diff --git a/benchmarks/performance/range.bench.ts b/benchmarks/performance/range.bench.ts index 84ba64150..7c16d03ea 100644 --- a/benchmarks/performance/range.bench.ts +++ b/benchmarks/performance/range.bench.ts @@ -1,8 +1,10 @@ import { bench, describe } from 'vitest'; import { range as rangeToolkit_ } from 'es-toolkit'; +import { range as rangeCompatToolkit_ } from 'es-toolkit/compat'; import { range as rangeLodash_ } from 'lodash'; const rangeToolkit = rangeToolkit_; +const rangeCompatToolkit = rangeCompatToolkit_; const rangeLodash = rangeLodash_; describe('range', () => { @@ -10,6 +12,10 @@ describe('range', () => { rangeToolkit(0, 100, 1); }); + bench('es-toolkit/compat/range', () => { + rangeCompatToolkit(0, 100, 1); + }); + bench('lodash/range', () => { rangeLodash(0, 100, 1); }); diff --git a/benchmarks/performance/rangeRight.bench.ts b/benchmarks/performance/rangeRight.bench.ts index 1289c2080..1e392d720 100644 --- a/benchmarks/performance/rangeRight.bench.ts +++ b/benchmarks/performance/rangeRight.bench.ts @@ -1,8 +1,10 @@ import { bench, describe } from 'vitest'; import { rangeRight as rangeRightToolkit_ } from 'es-toolkit'; +import { rangeRight as rangeRightCompatToolkiTt_ } from 'es-toolkit/compat'; import { rangeRight as rangeRightLodash_ } from 'lodash'; const rangeRightToolkit = rangeRightToolkit_; +const rangeRightCompatToolkit = rangeRightCompatToolkiTt_; const rangeRightLodash = rangeRightLodash_; describe('rangeRight', () => { @@ -10,6 +12,10 @@ describe('rangeRight', () => { rangeRightToolkit(0, 100, 1); }); + bench('es-toolkit/compat/rangeRight', () => { + rangeRightCompatToolkit(0, 100, 1); + }); + bench('lodash/rangeRight', () => { rangeRightLodash(0, 100, 1); }); diff --git a/benchmarks/performance/reverse.bench.ts b/benchmarks/performance/reverse.bench.ts new file mode 100644 index 000000000..0838d1317 --- /dev/null +++ b/benchmarks/performance/reverse.bench.ts @@ -0,0 +1,20 @@ +import { bench, describe } from 'vitest'; +import { reverse as reverseCompat_ } from 'es-toolkit/compat'; +import { reverse as reverseLodash_ } from 'lodash'; + +const reverseCompat = reverseCompat_; +const reverseLodash = reverseLodash_; + +const testArray = Array.from({ length: 1000 }, (_, i) => i); + +describe('Reverse function performance tests', () => { + bench('es-toolkit compat reverse', () => { + const arrayCopy = [...testArray]; + reverseCompat(arrayCopy); + }); + + bench('lodash reverse', () => { + const arrayCopy = [...testArray]; + reverseLodash(arrayCopy); + }); +}); diff --git a/benchmarks/performance/slice.bench.ts b/benchmarks/performance/slice.bench.ts index c90c23bca..1e1231b08 100644 --- a/benchmarks/performance/slice.bench.ts +++ b/benchmarks/performance/slice.bench.ts @@ -7,7 +7,7 @@ const sliceLodash = sliceLodash_; describe('slice', () => { const array = Array(1000); - bench('es-toolkit/comapt/slice', () => { + bench('es-toolkit/compat/slice', () => { sliceToolkitCompat(array); sliceToolkitCompat(array, 1); sliceToolkitCompat(array, 1, 2); diff --git a/benchmarks/performance/snakeCase.bench.ts b/benchmarks/performance/snakeCase.bench.ts index b473f3586..785116049 100644 --- a/benchmarks/performance/snakeCase.bench.ts +++ b/benchmarks/performance/snakeCase.bench.ts @@ -16,7 +16,7 @@ describe('snakeCase', () => { snakeCaseToolkit('hello-World_of XML_httpRequest'); }); - bench('es-toolkit/comapt/snakeCase', () => { + bench('es-toolkit/compat/snakeCase', () => { snakeCaseToolkitCompat('hello world'); snakeCaseToolkitCompat('--foo--bar__baz 123'); snakeCaseToolkitCompat('123numericValues'); diff --git a/benchmarks/performance/subtract.bench.ts b/benchmarks/performance/subtract.bench.ts new file mode 100644 index 000000000..80a01c694 --- /dev/null +++ b/benchmarks/performance/subtract.bench.ts @@ -0,0 +1,24 @@ +import { bench, describe } from 'vitest'; +import { subtract as subtractToolkitCompat_ } from 'es-toolkit/compat'; +import { subtract as subtractLodash_ } from 'lodash'; + +const subtractToolkitCompat = subtractToolkitCompat_; +const subtractLodash = subtractLodash_; + +describe('subtract function benchmark', () => { + bench('es-toolkit/compat/subtract', () => { + subtractToolkitCompat(2, 3); + subtractToolkitCompat(-1, -5); + subtractToolkitCompat(NaN, 3); + subtractToolkitCompat(3, NaN); + subtractToolkitCompat(NaN, NaN); + }); + + bench('lodash/subtract', () => { + subtractLodash(2, 3); + subtractLodash(-1, -5); + subtractLodash(NaN, 3); + subtractLodash(3, NaN); + subtractLodash(NaN, NaN); + }); +}); diff --git a/benchmarks/performance/takeRightWhile.bench.ts b/benchmarks/performance/takeRightWhile.bench.ts index 093eec2b6..6bbf2c308 100644 --- a/benchmarks/performance/takeRightWhile.bench.ts +++ b/benchmarks/performance/takeRightWhile.bench.ts @@ -1,8 +1,10 @@ import { bench, describe } from 'vitest'; import { takeRightWhile as takeRightWhileToolkit_ } from 'es-toolkit'; +import { takeRightWhile as takeRightWhileCompatToolkit_ } from 'es-toolkit/compat'; import { takeRightWhile as takeRightWhileLodash_ } from 'lodash'; const takeRightWhileToolkit = takeRightWhileToolkit_; +const takeRightWhileCompatToolkit = takeRightWhileCompatToolkit_; const takeRightWhileLodash = takeRightWhileLodash_; describe('takeRightWhile', () => { @@ -10,6 +12,10 @@ describe('takeRightWhile', () => { takeRightWhileToolkit([5, 4, 3, 2, 1], n => n < 4); }); + bench('es-toolkit/compat/takeRightWhile', () => { + takeRightWhileCompatToolkit([5, 4, 3, 2, 1], n => n < 4); + }); + bench('lodash/takeRightWhile', () => { takeRightWhileLodash([5, 4, 3, 2, 1], n => n < 4); }); @@ -22,6 +28,10 @@ describe('takeRightWhile/largeArray', () => { takeRightWhileToolkit(largeArray, n => n < 100); }); + bench('es-toolkit/compat/takeRightWhile', () => { + takeRightWhileCompatToolkit(largeArray, n => n < 100); + }); + bench('lodash/takeRightWhile', () => { takeRightWhileLodash(largeArray, n => n < 100); }); diff --git a/benchmarks/performance/toPlainObject.bench.ts b/benchmarks/performance/toPlainObject.bench.ts new file mode 100644 index 000000000..233a30362 --- /dev/null +++ b/benchmarks/performance/toPlainObject.bench.ts @@ -0,0 +1,23 @@ +import { bench, describe } from 'vitest'; +import { toPlainObject as toPlainObjectCompatToolkit_ } from 'es-toolkit/compat'; +import { toPlainObject as toPlainObjectLodash_ } from 'lodash'; + +const toPlainObjectCompatToolkit = toPlainObjectCompatToolkit_; +const toPlainObjectLodash = toPlainObjectLodash_; + +describe('toPlainObject', () => { + function Foo() { + this.b = 2; + } + + Foo.prototype.c = 3; + const foo = new Foo(); + + bench('es-toolkit/compat/toPlainObject', () => { + toPlainObjectCompatToolkit(foo); + }); + + bench('lodash/toPlainObject', () => { + toPlainObjectLodash(foo); + }); +}); 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/benchmarks/performance/upperFirst.bench.ts b/benchmarks/performance/upperFirst.bench.ts index 035074b0a..b5fcb41be 100644 --- a/benchmarks/performance/upperFirst.bench.ts +++ b/benchmarks/performance/upperFirst.bench.ts @@ -1,8 +1,10 @@ import { bench, describe } from 'vitest'; import { upperFirst as upperFirstToolkit_ } from 'es-toolkit'; +import { upperFirst as upperFirstCompatToolkit_ } from 'es-toolkit/compat'; import { upperFirst as upperFirstLodash_ } from 'lodash'; const upperFirstToolkit = upperFirstToolkit_; +const upperFirstCompatToolkit = upperFirstCompatToolkit_; const upperFirstLodash = upperFirstLodash_; describe('upperFirst', () => { @@ -12,6 +14,11 @@ describe('upperFirst', () => { upperFirstToolkit(str); }); + bench('es-toolkit/compat/upperFirst', () => { + const str = 'camelCase'; + upperFirstCompatToolkit(str); + }); + bench('lodash/upperFirst', () => { const str = 'camelCase'; upperFirstLodash(str); @@ -24,6 +31,10 @@ describe('upperFirst', () => { upperFirstToolkit(LONG_STR); }); + bench('es-toolkit/compat/upperFirst', () => { + upperFirstCompatToolkit(LONG_STR); + }); + bench('lodash/upperFirst', () => { upperFirstLodash(LONG_STR); }); diff --git a/docs/.vitepress/components/CompatibilityStatus.vue b/docs/.vitepress/components/CompatibilityStatus.vue index 8ec16538a..fd275d1c7 100644 --- a/docs/.vitepress/components/CompatibilityStatus.vue +++ b/docs/.vitepress/components/CompatibilityStatus.vue @@ -40,10 +40,17 @@ const ids = titles.map(title => title.toLowerCase().replaceAll('"', '').replace(