Open valentinpalkovic opened 1 month ago
Disclaimer This information might be inaccurate, due to it being generated automatically
Replace lodash/camelCase
:
change-case
.import { camelCase } from 'change-case';
Replace lodash/cloneDeep
:
structuredClone
or a custom deep clone function.const clone = structuredClone(obj);
Replace lodash/countBy
:
Array.prototype.reduce
to count occurrences.const countBy = (arr, fn) => arr.reduce((acc, val) => { const key = fn(val); acc[key] = (acc[key] || 0) + 1; return acc; }, {});
Replace lodash/debounce
:
const debounce = (func, wait) => { let timeout; return (...args) => { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; };
Replace lodash/isEqual
:
const isEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
Replace lodash/isPlainObject
:
typeof
and Object.prototype.toString
.const isPlainObject = (obj) => Object.prototype.toString.call(obj) === '[object Object]';
Replace lodash/kebabCase
:
change-case
.import { paramCase } from 'change-case';
Replace lodash/mapKeys
:
Object.entries
and Array.prototype.reduce
.const mapKeys = (obj, fn) => Object.entries(obj).reduce((acc, [key, value]) => { acc[fn(key)] = value; return acc; }, {});
Replace lodash/mapValues
:
Object.entries
and Array.prototype.reduce
.const mapValues = (obj, fn) => Object.entries(obj).reduce((acc, [key, value]) => { acc[key] = fn(value); return acc; }, {});
Replace lodash/merge
and lodash/mergeWith
:
Object.assign
or a custom deep merge function.const merge = (target, ...sources) => sources.reduce((acc, src) => { Object.keys(src).forEach(key => { if (typeof src[key] === 'object' && src[key] !== null) { if (!acc[key]) acc[key] = {}; merge(acc[key], src[key]); } else { acc[key] = src[key]; } }); return acc; }, target);
Replace lodash/pick
and lodash/pickBy
:
Object.keys
and Array.prototype.reduce
.const pick = (obj, keys) => keys.reduce((acc, key) => { if (obj.hasOwnProperty(key)) acc[key] = obj[key]; return acc; }, {});
Replace lodash/startCase
:
change-case
.import { startCase } from 'change-case';
Replace lodash/throttle
:
const throttle = (func, limit) => { let inThrottle; return (...args) => { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; };
Replace lodash/uniq
:
Array.prototype.filter
and Set
.const uniq = (arr) => [...new Set(arr)];
Replace lodash/upperFirst
:
const upperFirst = (str) => str.charAt(0).toUpperCase() + str.slice(1);
/code/lib/cli/src/automigrate/helpers
for utility functions that might be useful./code/lib/cli/src/automigrate/fixes
to replace Lodash functions./code/lib/cli/src/automigrate/fixes/remove-jest-testing-library.test.ts /code/lib/cli/src/automigrate/fixes/prompt-remove-react.ts /code/lib/cli/src/automigrate/fixes/prompt-remove-react.test.ts /code/lib/cli/src/automigrate/fixes/webpack5-compiler-setup.test.ts /code/lib/cli/src/automigrate/fixes/eslint-plugin.test.ts /code/lib/cli/src/automigrate/fixes/mdx-to-csf.test.ts /code/lib/cli/src/automigrate/fixes/storyshots-migration.test.ts /code/lib/cli/src/automigrate/fixes/remove-global-client-apis.test.ts /code/lib/cli/src/automigrate/fixes/new-frameworks.test.ts /code/lib/cli/src/automigrate/fixes/sb-scripts.test.ts /code/lib/cli/src/automigrate /code/lib/cli/src/automigrate/helpers /code/core/src/builder-manager/utils/framework.ts /code/core/src/builder-manager/utils/framework.test.ts /code/core/src/telemetry/get-monorepo-type.test.ts /code/lib/cli/src/automigrate/index.test.ts
Hello, I am new here so I might ask some silly questions.
Which bundle exactly is it preferable to analyze? manager-bundle, common-manager-bundle or globals-runtime? Is it possible to contribute gradually, by refactoring one function in one PR?
Thank you.
Can deepmerge-ts
be a potential candidate for replacing merge
?
It possibly can replace mergeWith
with deepmergeCustom
as well.
es-toolkit could be considered as an alternative to lodash - https://github.com/toss/es-toolkit
Hello, I am new here so I might ask some silly questions.
Which bundle exactly is it preferable to analyze? manager-bundle, common-manager-bundle or globals-runtime? Is it possible to contribute gradually, by refactoring one function in one PR?
Thank you.
The biggest impact would be to get rid of lodash in @storybook/core
. Due to how the sub packages of @storybook/core are bundled, lodash occurs several times in the built output.
Also feel free to contribute one PR at a time to remove/replace single occurrences of lodash calls.
As soon as you open a PR, you will see some benchmark stats about bundle size.
Can
deepmerge-ts
be a potential candidate for replacingmerge
?It possibly can replace
mergeWith
withdeepmergeCustom
as well.
Would replacing merge
from lodash with deepmerge-ts
have a positive impact on bundle size? We have some benchmarking in place as soon as you create a PR. The manager's and preview's bundle size will be reported. So just open a PR to figure out its impact.
es-toolkit could be considered as an alternative to lodash - https://github.com/toss/es-toolkit
Seems to be pretty interesting! @xeho91, @tanel-terras, @stolbikova do you have any experience or opinion about es-toolkit?
I analyzed es-toolkit, and it seems it doesn't cover the following lodash functions (yet):
I think to split the work so that multiple contributors could contribute, we could do the following:
es-toolkit/compat
lodash
, as es-toolkit/compat
is a compatibility layer that bridges the gap between the two libraries.compat
layer and use es-toolkit
"purely"lodash/cloneDeep
need alternatives. Ideally, we should analyze the code, understand its context, and figure out how we can best replace its usage.es-toolkit
directly. My feeling says, though, that this might take a bit of time.Can
deepmerge-ts
be a potential candidate for replacingmerge
? It possibly can replacemergeWith
withdeepmergeCustom
as well.Would replacing
merge
from lodash withdeepmerge-ts
have a positive impact on bundle size? We have some benchmarking in place as soon as you create a PR. The manager's and preview's bundle size will be reported. So just open a PR to figure out its impact.
Are there any set boundaries for maximum positive impact on the bundle size?
Is very minimal package, 5,8kB
and tree-shakeable: https://bundlephobia.com/package/deepmerge-ts@7.0.3
And also, not 'polluted', 0 dependencies: https://npmgraph.js.org/?q=deepmerge-ts
PR for preview: https://github.com/storybookjs/storybook/pull/28663
es-toolkit could be considered as an alternative to lodash - https://github.com/toss/es-toolkit
Seems to be pretty interesting! @xeho91, @tanel-terras, @stolbikova do you have any experience or opinion about es-toolkit?
Honestly, no strong opinion.
I definitely would favour this more over lodash
, because how well maintained, and even typed this collection is. Benchmarks included as well, awesome to see how much possible positive impact we could get from replacing.
You already observed that it cannot replace all of the existing lodash
snippets, but is a good start, and definitely worth a try/effort. ๐
Hello! I'm the maintainer of es-toolkit
.
Just a quick update to let you know that we've added several new functions to es-toolkit
, including:
Please note that there are some differences between es-toolkit
and lodash
, designed to optimize bundle size and runtime performance. For example:
es-toolkit
does not clone values like Object(true)
or arguments
in a special way.es-toolkit
does not check for Symbol.toStringTag
.es-toolkit
does not support special property syntax for object mapping, such as mapKeys(arr, 'property')
. Instead, use mapKeys(arr, x => x.property)
.es-toolkit
does not support merging multiple objects at once with merge(obj1, obj2, obj3)
or mergeWith(obj1, obj2, obj3)
. You can use merge
or mergeWith
iteratively instead.In the meantime, if you need full compatibility with lodash
, you might want to use es-toolkit/compat
, which fully supports and tests all behaviors from lodash
.
Hi @raon0211! That's amazing. Thank you for letting us know.
Overview
In our efforts to optimize our monorepo's dependencies and improve bundle sizes, we are looking to replace our usage of Lodash with native JavaScript functionality or alternative libraries where applicable. This initiative is inspired by the recommendations found in You Don't Need Lodash/Underscore, which outlines many cases where modern JavaScript provides solutions that previously required utility libraries like Lodash.
Specific Lodash Functions in Use
From an initial audit, the following Lodash functions are currently in use across various packages in our monorepo:
lodash/camelCase
lodash/cloneDeep
lodash/countBy
lodash/debounce
lodash/isEqual
lodash/isPlainObject
lodash/kebabCase
lodash/mapKeys
lodash/mapValues
lodash/merge
lodash/mergeWith
lodash/pick
lodash/pickBy
lodash/startCase
lodash/throttle
lodash/uniq
lodash/upperFirst
Acceptance Criteria
To consider this work complete, the following criteria must be met:
import merge from 'lodash/merge'
) should be replaced across the monorepo.Potential Challenges
merge
andmergeWith
: These functions are used extensively for deep object merging. Identifying a suitable replacement that handles all edge cases might be challenging.lodash/throttle
function is used in performance-sensitive areas. Ensuring that the replacement maintains the same throttling behavior and performance characteristics is crucial.Request for Contributions
We are seeking contributions from the open source community to tackle this initiative. Contributions can range from identifying and documenting potential replacements, to implementing changes in specific packages. Please comment on this issue if you're able to take on a part of this work, specifying which packages or Lodash functions you're focusing on.
Thank you for your contributions to improving our monorepo's efficiency and maintainability!