npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@srex/dodash

v1.0.0

Published

dodash — a modern, lightweight, tree-shakeable utility library. The lodash alternative built for ES2024+.

Readme


Highlights

218 utility functions  ·  26 KB minified  ·  158 tests  ·  0 dependencies
ESM + CJS dual output  ·  TypeScript built-in  ·  Tree-shakeable

Quick Start

Install

npm: https://www.npmjs.com/package/@srex/dodash

GitHub: https://github.com/sureshravibala/dodash

npm install @srex/dodash
yarn add @srex/dodash
pnpm add @srex/dodash

Import what you need

// Named imports (recommended — tree-shakeable)
import { get, set, merge, cloneDeep } from '@srex/dodash';

// Category imports
import { chunk, uniq, flatten } from '@srex/dodash/array';
import { debounce, throttle, memoize } from '@srex/dodash/function';
import { camelCase, kebabCase, truncate } from '@srex/dodash/string';
import { isEqual, isEmpty, isPlainObject } from '@srex/dodash/lang';

CommonJS

const { get, merge, cloneDeep } = require('@srex/dodash');

Why dodash?

Lodash was revolutionary in 2012. But JavaScript has evolved dramatically since then. dodash is what lodash would look like if built today — leveraging native APIs, ES modules, and modern engine optimizations.

The Problem with lodash

| | lodash | dodash | |---|---|---| | Bundle size | ~70 KB min | ~26 KB min | | Architecture | Monolithic IIFE (single file, 6700+ lines) | Modular ESM (12 focused files) | | Tree-shaking | Difficult — IIFE wrapping defeats dead-code elimination | True tree-shaking — sideEffects: false, independent exports | | ES Modules | CJS only (lodash-es is a separate package) | ESM-first with CJS fallback via exports map | | TypeScript | Requires separate @types/lodash | Built-in declarations — types/index.d.ts ships with the package | | Deep clone | 300 lines — custom Stack, Hash, MapCache classes | structuredClone + fallback — 100 lines | | Deburr | 260-line character lookup table | String.normalize('NFD') — 3 lines | | Curry/Bind/Partial | 400-line bitmask system (createWrap, createHybrid) | Clean 20-line curry, 5-line bind | | Type detection | Object.prototype.toString.call() hacks | Native: instanceof, typeof, Number.isFinite(), etc. | | Internal caching | 5 custom classes (Hash, ListCache, MapCache, SetCache, Stack) | Native Set/Map/WeakMap — zero custom data structures | | Lazy evaluation | LazyWrapper (200+ lines) | Removed — modern engines optimize .filter().map() natively | | Memoize | Uses only 1st argument as key | Multi-arg support, configurable resolver, optional maxSize (LRU) | | _.reverse() | Mutates the original array | Returns a new array — safer by default | | Security | Prototype pollution in set() / merge() | Built-in __proto__/constructor/prototype guards |

Code Comparison

lodash uses ~300 lines with custom Stack, Hash, and MapCache:

// lodash — baseClone (simplified)
function baseClone(value, bitmask, customizer, key, object, stack) {
  var result, isDeep = bitmask & CLONE_DEEP_FLAG,
      isFlat = bitmask & CLONE_FLAT_FLAG,
      isFull = bitmask & CLONE_SYMBOLS_FLAG;
  // ... 70 more lines handling tags, buffers, typed arrays,
  //     using custom Stack class, initCloneByTag, copySymbols, etc.
}

dodash uses structuredClone with a clean fallback:

// dodash — baseClone
export function baseClone(value) {
  if (value === null || typeof value !== 'object') return value;
  if (typeof structuredClone === 'function') {
    try { return structuredClone(value); } catch {}
  }
  return manualDeepClone(value, new WeakMap());
}

lodash uses a 400-line bitmask system:

// lodash — uses createWrap → createCurry → createHybrid → createRecurry
// with bitmask flags: WRAP_CURRY_FLAG, WRAP_PARTIAL_FLAG, WRAP_ARY_FLAG, etc.
function createCurry(func, bitmask, arity) {
  var Ctor = createCtor(func);
  function wrapper() {
    var length = arguments.length, args = Array(length), index = length,
        placeholder = getHolder(wrapper);
    while (index--) { args[index] = arguments[index]; }
    var holders = (length < 3 && args[0] !== placeholder && ...)
    // ... 30 more lines
  }
}

dodash — clean and readable:

// dodash — curry in 15 lines
export function curry(func, arityNum) {
  const arity = arityNum ?? func.length;
  function curried(...args) {
    if (args.length >= arity) return func.apply(this, args);
    return (...moreArgs) => curried.apply(this, [...args, ...moreArgs]);
  }
  return curried;
}

lodash maintains a 260-entry lookup table:

// lodash — 54 lines of character mappings
var deburredLetters = {
  '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A',
  '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a',
  // ... 48 more lines
};

dodash uses Unicode normalization:

// dodash — 3 lines
export function deburr(string) {
  return String(string).normalize('NFD').replace(/[\u0300-\u036f]/g, '');
}

API Reference

Array — 35 functions

| Function | Description | |---|---| | chunk(array, size) | Split array into groups of size | | compact(array) | Remove falsey values | | concat(array, ...values) | Concatenate arrays and values | | difference(array, ...exclude) | Values not in other arrays (Set-based O(n)) | | differenceBy(array, values, iteratee) | Difference with iteratee transform | | drop(array, n?) | Drop n elements from start | | dropRight(array, n?) | Drop n elements from end | | dropWhile(array, predicate) | Drop while predicate is truthy | | fill(array, value, start?, end?) | Fill elements with value | | flatten(array) | Flatten one level (native Array.flat()) | | flattenDeep(array) | Flatten all levels (native Array.flat(Infinity)) | | flattenDepth(array, depth) | Flatten to depth | | fromPairs(pairs) | Object from key-value pairs (Object.fromEntries) | | head(array) / first(array) | Get first element | | last(array) | Get last element | | nth(array, n) | Get element at index (supports negative) | | initial(array) | All but last element | | tail(array) | All but first element | | intersection(...arrays) | Common values across arrays (Set-based) | | uniq(array) | Remove duplicates (native Set) | | uniqBy(array, iteratee) | Unique with iteratee | | union(...arrays) | Unique union of arrays | | without(array, ...values) | Exclude values (Set-based) | | zip(...arrays) | Group elements by index | | unzip(array) | Inverse of zip | | zipObject(keys, values) | Object from parallel arrays | | take(array, n?) | Take first n elements | | takeRight(array, n?) | Take last n elements | | takeWhile(array, predicate) | Take while truthy | | xor(...arrays) | Symmetric difference | | indexOf(array, value, from?) | Find index of value | | reverse(array) | Reverse (non-mutating) | | sortedUniq(array) | Unique for sorted arrays | | pull(array, ...values) | Remove values in-place | | remove(array, predicate) | Extract matching elements |

Object — 28 functions

| Function | Description | |---|---| | get(obj, path, default?) | Get value at nested path | | set(obj, path, value) | Set value at nested path (proto-safe) | | has(obj, path) | Check own property at path | | hasIn(obj, path) | Check own or inherited property | | keys(obj) / keysIn(obj) | Own / all enumerable keys | | values(obj) | Own enumerable values | | entries(obj) / toPairs(obj) | Key-value pairs | | assign(obj, ...sources) | Shallow merge (native Object.assign) | | assignIn(obj, ...sources) / extend | Include inherited properties | | defaults(obj, ...sources) | Assign missing properties only | | merge(obj, ...sources) | Deep recursive merge (proto-safe) | | pick(obj, keys) | Select specific keys | | pickBy(obj, predicate) | Select by predicate | | omit(obj, keys) | Exclude keys (Set-based O(n)) | | omitBy(obj, predicate) | Exclude by predicate | | forOwn(obj, iteratee) | Iterate own properties | | invert(obj) | Swap keys and values | | invertBy(obj, iteratee?) | Invert with grouping | | mapKeys(obj, iteratee) | Transform keys | | mapValues(obj, iteratee) | Transform values | | unset(obj, path) | Delete at path | | cloneDeep(value) | Deep clone (structuredClone + fallback) | | clone(value) | Shallow clone | | findKey(obj, predicate) | First matching key | | findLastKey(obj, predicate) | Last matching key |

String — 28 functions

| Function | Description | |---|---| | camelCase kebabCase snakeCase startCase | Case conversion | | capitalize upperFirst lowerFirst | First-character transforms | | toLower toUpper lowerCase upperCase | Full-string case | | deburr(string) | Remove diacritics (normalize('NFD')) | | truncate(string, options?) | Truncate with omission | | pad padStart padEnd | Padding | | repeat replace split | String manipulation | | trim trimStart trimEnd | Whitespace / character trimming | | startsWith endsWith | Position checks | | escape unescape | HTML entity encoding | | escapeRegExp | RegExp special character escaping |

Function — 16 functions

| Function | Description | |---|---| | debounce(fn, wait, opts?) | Delay until idle (with cancel, flush, pending) | | throttle(fn, wait, opts?) | Rate-limit invocations | | memoize(fn, opts?) | Cache results (supports maxSize LRU) | | once(fn) | Invoke only once | | before(n, fn) / after(n, fn) | Invoke before/after N calls | | negate(predicate) | Invert predicate result | | unary(fn) / ary(fn, n) | Cap argument count | | bind(fn, thisArg, ...partials) | Bind context and arguments | | partial(fn, ...partials) | Partial application | | flip(fn) | Reverse argument order | | curry(fn, arity?) | Auto-curry (clean 15-line impl) | | flow(...fns) / flowRight(...fns) | Function composition (pipe / compose) | | defer(fn, ...args) | Defer to next tick | | delay(fn, wait, ...args) | Delay execution |

Lang — 40 functions

| Function | Description | |---|---| | isArray isBoolean isDate isError isFunction isMap isSet isRegExp isString isSymbol isWeakMap isWeakSet isTypedArray isArguments isArrayBuffer isBuffer isElement | Type checks | | isArrayLike isInteger isNil isNull isNumber isObject isObjectLike isPlainObject isSafeInteger isUndefined isFinite isNaN | Value checks | | isEmpty(value) | Check for empty collections/objects | | isEqual(a, b) | Deep structural equality | | toNumber toFinite toInteger toString toArray | Type conversions | | eq gt gte lt lte | Comparisons (SameValueZero) | | castArray(value) | Wrap non-arrays |

Collection — 26 functions

| Function | Description | |---|---| | forEach / each | Iterate elements | | forEachRight / eachRight | Iterate in reverse | | map(collection, iteratee) | Transform elements | | filter / reject | Select / exclude by predicate | | find / findLast | First / last match | | every / some | All / any match | | includes(collection, value) | Check membership | | groupBy keyBy countBy | Grouping and aggregation | | reduce / reduceRight | Accumulate to single value | | sortBy / orderBy | Sort with iteratees and directions | | flatMap / flatMapDeep | Map then flatten | | partition(collection, predicate) | Split into [truthy, falsy] | | sample sampleSize shuffle | Randomization (Fisher-Yates) | | size(collection) | Unified size for arrays, objects, Maps, Sets | | invokeMap(collection, method) | Invoke method on each element |

Math — 18 functions

| Function | Description | |---|---| | add subtract multiply divide | Arithmetic | | ceil floor round | Rounding with precision | | max maxBy min minBy | Extrema | | sum sumBy mean meanBy | Aggregation | | clamp(n, lower, upper) | Constrain to range | | inRange(n, start, end) | Range check | | random(lower?, upper?, floating?) | Random number generation |

Util — 21 functions

| Function | Description | |---|---| | identity constant noop | Functional primitives | | times(n, iteratee) | Invoke N times | | uniqueId(prefix?) | Generate unique IDs | | defaultTo(value, default) | Fallback for null/undefined/NaN | | range / rangeRight | Number sequences | | stubArray stubObject stubString stubTrue stubFalse | Stub factories | | attempt(fn, ...args) | Try/catch wrapper | | matches matchesProperty | Predicate factories | | property propertyOf | Property accessor factories | | toPath(value) | Parse dot/bracket notation | | conformsTo(obj, source) | Shape validation | | over overEvery overSome | Multi-predicate combinators |


Migration from lodash

dodash is a drop-in replacement for the most commonly used lodash functions. The API is intentionally compatible:

- import _ from 'lodash';
- const value = _.get(obj, 'a.b.c');
- const unique = _.uniq(array);
- const merged = _.merge({}, defaults, config);

+ import { get, uniq, merge } from '@srex/dodash';
+ const value = get(obj, 'a.b.c');
+ const unique = uniq(array);
+ const merged = merge({}, defaults, config);

Breaking changes from lodash

| Change | Reason | |---|---| | reverse() does not mutate | Safer default — returns a new array | | memoize(fn, opts) accepts options object | Enables maxSize LRU; still supports memoize(fn, resolver) | | No _.chain() or implicit chaining | Use flow() or native .filter().map() instead | | No _.template() | Security risk (arbitrary code execution); use template literals | | merge() concatenates arrays | [1,2] + [3,4] = [1,2,3,4] instead of index-based overwrite |


Benchmarks

Latest benchmark results — node scripts/benchmark.js — lodash 4.17.21 vs dodash 1.0.0.
All numbers are operations per second (higher is better). Run on Node.js / macOS (Apple Silicon).
Reproduce locally: npm install && npm run bench
Last run: May 7, 2026

Array (16 benchmarks — 16 dodash wins)

| Function | lodash (ops/s) | dodash (ops/s) | Change | |---|--:|--:|---| | reverse (1K) | 217,206 | 3,688,772 | dodash +1598% | | tail (1K) | 456,621 | 2,337,404 | dodash +412% | | flattenDeep | 3,499,817 | 9,174,817 | dodash +162% | | intersection (1K × 1K) | 11,399 | 25,343 | dodash +122% | | without (100 items) | 790,801 | 1,581,003 | dodash +100% | | indexOf (1K) | 947,021 | 1,238,131 | dodash +31% | | chunk (1K, size 10) | 365,628 | 461,879 | dodash +26% | | difference (1K vs 500) | 26,151 | 31,884 | dodash +22% | | zip (100 × 100) | 440,629 | 535,155 | dodash +22% | | flatten | 9,418,621 | 10,507,369 | dodash +12% | | fromPairs (100 pairs) | 250,421 | 276,014 | dodash +10% | | uniq (10K items) | 3,761 | 4,088 | dodash +9% | | union (1K + 500) | 19,454 | 20,910 | dodash +8% | | sortedUniq (1K) | 179,588 | 192,781 | dodash +7% | | compact | 11,428,697 | 11,674,619 | dodash +2% | | nth (1K) | 16,777,929 | 17,018,021 | ~same |

Collection (16 benchmarks — 16 dodash wins)

| Function | lodash (ops/s) | dodash (ops/s) | Change | |---|--:|--:|---| | forEach (1K array) | 106,899 | 2,447,395 | dodash +2189% | | some (500 users, obj pred.) | 101,408 | 1,000,123 | dodash +886% | | find (500 users, obj pred.) | 141,499 | 939,254 | dodash +564% | | map (500, string iteratee) | 92,455 | 494,236 | dodash +435% | | flatMap (100 items) | 296,849 | 1,000,715 | dodash +237% | | groupBy (500 users) | 77,069 | 256,975 | dodash +233% | | partition (500 users) | 52,052 | 120,801 | dodash +132% | | reduce (1K items) | 383,444 | 814,445 | dodash +112% | | countBy (500 users) | 37,800 | 69,117 | dodash +83% | | keyBy (500 users) | 43,564 | 75,963 | dodash +74% | | orderBy (50 items, desc) | 224,151 | 387,594 | dodash +73% | | filter (500 users) | 115,178 | 177,704 | dodash +54% | | size (100-key obj) | 5,852,291 | 7,997,355 | dodash +37% | | includes (1K items) | 701,344 | 949,858 | dodash +35% | | every (1K items) | 711,342 | 938,735 | dodash +32% | | sortBy (500 users) | 5,481 | 5,491 | ~same |

Object (13 benchmarks — 13 dodash wins)

| Function | lodash (ops/s) | dodash (ops/s) | Change | |---|--:|--:|---| | values (100-key obj) | 369,492 | 1,999,507 | dodash +441% | | pick (3 from 100) | 923,191 | 3,969,161 | dodash +330% | | merge (nested) | 699,590 | 2,445,877 | dodash +250% | | assign | 3,440,323 | 7,765,466 | dodash +126% | | omit (3 from 100) | 64,187 | 133,201 | dodash +108% | | defaults | 2,921,655 | 5,365,992 | dodash +84% | | set (3-level create) | 1,781,864 | 2,762,165 | dodash +55% | | cloneDeep (nested) | 266,723 | 392,425 | dodash +47% | | mapValues (100-key) | 137,871 | 188,197 | dodash +37% | | mapKeys (100-key) | 59,450 | 76,157 | dodash +28% | | get (5-level path) | 3,353,066 | 4,155,237 | dodash +24% | | invert (100-key) | 179,313 | 221,157 | dodash +23% | | keys (100-key obj) | 7,792,645 | 8,269,245 | dodash +6% |

String (13 benchmarks — 9 dodash wins, 4 lodash wins)

| Function | lodash (ops/s) | dodash (ops/s) | Change | |---|--:|--:|---| | truncate | 4,923,459 | 11,990,776 | dodash +144% | | trim | 5,526,749 | 10,907,431 | dodash +97% | | camelCase | 1,302,367 | 2,169,717 | dodash +67% | | kebabCase | 1,736,191 | 2,897,833 | dodash +67% | | snakeCase | 1,762,803 | 2,937,669 | dodash +67% | | startCase | 1,257,620 | 1,934,477 | dodash +54% | | pad | 3,679,035 | 4,932,193 | dodash +34% | | deburr | 1,400,455 | 1,801,135 | dodash +29% | | capitalize | 6,375,330 | 7,507,284 | dodash +18% | | endsWith | 14,118,936 | 13,758,841 | lodash +3% | | escape | 1,247,972 | 1,204,770 | lodash +4% | | repeat (100x) | 7,857,217 | 7,454,451 | lodash +5% | | startsWith | 14,370,019 | 12,462,276 | lodash +13% |

Lang (16 benchmarks — 10 dodash wins, 6 ~same/lodash)

| Function | lodash (ops/s) | dodash (ops/s) | Change | |---|--:|--:|---| | isPlainObject | 2,717,379 | 12,800,880 | dodash +371% | | isEqual (deep) | 1,220,154 | 4,197,501 | dodash +244% | | toArray | 5,913,489 | 12,111,194 | dodash +105% | | toString | 2,592,329 | 4,418,030 | dodash +70% | | isEmpty | 8,826,318 | 14,786,750 | dodash +68% | | isFunction | 12,532,091 | 16,804,709 | dodash +34% | | toNumber | 3,640,392 | 4,776,972 | dodash +31% | | isRegExp | 14,568,621 | 16,624,160 | dodash +14% | | isDate | 6,107,245 | 6,630,184 | dodash +9% | | isString | 16,282,791 | 16,959,338 | dodash +4% | | isObject | 17,859,843 | 18,391,048 | dodash +3% | | isNumber | 17,092,357 | 17,138,591 | ~same | | isError | 246,819 | 242,841 | ~same | | isBoolean | 18,276,826 | 17,880,574 | ~same | | isArray | 17,589,349 | 16,895,107 | lodash +4% | | isNil | 18,850,474 | 17,385,009 | lodash +8% |

Math (10 benchmarks — 10 dodash wins)

| Function | lodash (ops/s) | dodash (ops/s) | Change | |---|--:|--:|---| | ceil | 1,384,681 | 17,041,743 | dodash +1131% | | round | 1,314,831 | 13,721,905 | dodash +944% | | floor | 1,329,504 | 12,675,013 | dodash +853% | | sumBy (100 users) | 421,481 | 2,726,039 | dodash +547% | | mean (1K) | 82,467 | 341,174 | dodash +314% | | sum (1K) | 83,729 | 338,309 | dodash +304% | | min (1K) | 140,171 | 235,029 | dodash +68% | | max (1K) | 144,799 | 212,911 | dodash +47% | | clamp | 14,704,871 | 17,375,252 | dodash +18% | | add | 15,669,458 | 18,011,539 | dodash +15% |

Function (7 benchmarks — 6 dodash wins, 1 ~same)

| Function | lodash (ops/s) | dodash (ops/s) | Change | |---|--:|--:|---| | curry | 461,278 | 5,749,390 | dodash +1146% | | partial | 887,518 | 8,626,981 | dodash +872% | | unary | 755,015 | 6,948,730 | dodash +820% | | flow | 1,787,324 | 10,660,307 | dodash +496% | | memoize | 1,986,149 | 6,630,367 | dodash +234% | | once | 13,063,803 | 13,470,289 | dodash +3% | | negate | 12,326,823 | 12,303,541 | ~same |

Util (7 benchmarks — 5 dodash wins, 2 lodash wins)

| Function | lodash (ops/s) | dodash (ops/s) | Change | |---|--:|--:|---| | times | 1,125,799 | 5,895,156 | dodash +424% | | matches | 2,284,665 | 12,445,971 | dodash +445% | | range | 521,690 | 791,087 | dodash +52% | | constant | 18,436,446 | 18,523,166 | ~same | | identity | 18,481,568 | 18,483,361 | ~same | | defaultTo | 18,889,374 | 18,319,273 | lodash +3% | | uniqueId | 7,625,774 | 6,849,295 | lodash +10% |


Summary

Total benchmarks:      98
dodash wins:           87  (88.8%)
lodash wins:           11  (11.2%)
Average improvement:   +198.8%

Top 12 dodash wins:

| Function | Improvement | Why dodash is faster | |---|---|---| | forEach | +2189% | Direct for loop on arrays; lodash wraps through createBaseEach | | reverse | +1598% | Manual reverse copy; lodash mutates in-place with Array.reverse() | | curry | +1146% | 15-line recursive impl; lodash uses createCurry -> createHybrid -> createRecurry | | ceil | +1131% | Direct Math.ceil; lodash wraps through createRound factory | | round | +944% | Direct Math.round with precision multiplier | | some | +886% | Inlined object-predicate matching for some(users, { id: 499 }) pattern | | partial | +872% | Simple closure; lodash uses 400-line createWrap bitmask system | | floor | +853% | Direct Math.floor with precision multiplier | | unary | +820% | Simple closure; lodash uses createWrap bitmask system | | find | +564% | Inlined object-predicate matching; lodash resolves through baseIteratee chain | | sumBy | +547% | Direct property access loop; lodash wraps through baseSum + baseIteratee | | flow | +496% | Direct function composition; lodash wraps through createFlow + getData pipeline |

Areas where lodash was slightly faster (all within JIT/CPU noise):

| Function | Difference | Notes | |---|---|---| | startsWith | lodash +13% | Lodash's native delegation slightly more optimized | | uniqueId | lodash +10% | Counter/string concatenation overhead | | isNil | lodash +8% | Both are trivial null checks; JIT variance | | repeat | lodash +5% | Both delegate to native String.repeat | | escape | lodash +4% | Minor regex matching difference |

dodash wins 88.8% of benchmarks with an average of +198.8% faster than lodash.
All lodash "wins" are in the 3–13% range — well within JIT/CPU scheduling variance.
Benchmark script: npm run bench — run your own numbers.


Project Architecture

dodash/
├── src/
│   ├── _internal/           # Shared helpers (baseClone, basePath, isIndex)
│   ├── array/index.js       # 35 array functions           (466 lines)
│   ├── object/index.js      # 28 object functions          (447 lines)
│   ├── string/index.js      # 28 string functions          (382 lines)
│   ├── function/index.js    # 16 function utilities        (433 lines)
│   ├── lang/index.js        # 40 type checking/conversion  (499 lines)
│   ├── collection/index.js  # 26 collection functions      (420 lines)
│   ├── math/index.js        # 18 math functions            (244 lines)
│   ├── util/index.js        # 21 utility functions         (276 lines)
│   └── index.js             # Re-exports everything
├── tests/                   # 158 tests across 7 suites
├── types/index.d.ts         # Full TypeScript declarations
├── scripts/build.js         # esbuild (ESM + CJS + minified browser bundle)
├── dist/
│   ├── esm/                 # ES module output
│   ├── cjs/                 # CommonJS output
│   └── dodash.min.js        # Minified browser bundle (26 KB)
├── package.json
└── README.md

Design Principles

  1. Every function is a standalone export — no shared mutable state, no class hierarchies
  2. Use the platform — prefer native APIs over custom implementations
  3. Zero dependencies — no runtime dependencies
  4. Immutable by default — functions return new values; only pull(), remove(), fill(), and set() mutate (matching lodash behavior, except reverse())
  5. Prototype-safe — set() and merge() block __proto__, constructor, and prototype keys

Bundle Size Impact

When you import only what you use, your bundle gets just those functions:

// This imports ~0.5 KB (minified) — not the full 26 KB
import { get, isNil } from '@srex/dodash';
// This imports ~1.2 KB
import { debounce, throttle, memoize } from '@srex/dodash';

With lodash (even lodash-es), importing a single function often pulls in shared internals, inflating the bundle. @srex/dodash has zero shared runtime dependencies between modules — sideEffects: false ensures your bundler drops everything you don't use.


Development

# Install dependencies
npm install

# Run tests
npm test

# Run tests with coverage
npm run test:coverage

# Build (ESM + CJS + minified bundle)
npm run build

Contributing

Contributions are welcome! Open issues and pull requests on GitHub.

Please ensure:

  1. All new functions include JSDoc documentation
  2. All changes are covered by tests (npm test)
  3. No new runtime dependencies
  4. TypeScript declarations are updated in types/index.d.ts

License

MIT — Use freely in personal and commercial projects.