Skip to content

Commit

Permalink
Replace lodash with es-toolkit (#2503)
Browse files Browse the repository at this point in the history
* first part of migration

* second part of migration

* bump up es-toolkit version

* bump up `es-toolkit` version and remove `lodash` completely

* update screenshot
  • Loading branch information
tom2drum authored Jan 14, 2025
1 parent 2b47bbe commit 1088126
Show file tree
Hide file tree
Showing 81 changed files with 222 additions and 239 deletions.
5 changes: 0 additions & 5 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,9 @@ const RESTRICTED_MODULES = {
importNames: [ 'Popover', 'Menu', 'PinInput', 'useToast', 'Skeleton' ],
message: 'Please use corresponding component or hook from ui/shared/chakra component instead',
},
{
name: 'lodash',
message: 'Please use `import [package] from \'lodash/[package]\'` instead.',
},
],
patterns: [
'icons/*',
'!lodash/*',
],
};

Expand Down
2 changes: 1 addition & 1 deletion icons/clock-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 3 additions & 4 deletions lib/api/useApiFetch.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useQueryClient } from '@tanstack/react-query';
import _omit from 'lodash/omit';
import _pickBy from 'lodash/pickBy';
import { omit, pickBy } from 'es-toolkit';
import React from 'react';

import type { CsrfData } from 'types/client/account';
Expand Down Expand Up @@ -38,7 +37,7 @@ export default function useApiFetch() {
const resource: ApiResource = RESOURCES[resourceName];
const url = buildUrl(resourceName, pathParams, queryParams);
const withBody = isBodyAllowed(fetchParams?.method);
const headers = _pickBy({
const headers = pickBy({
'x-endpoint': resource.endpoint && isNeedProxy() ? resource.endpoint : undefined,
Authorization: resource.endpoint && resource.needAuth ? apiToken : undefined,
'x-csrf-token': withBody && csrfToken ? csrfToken : undefined,
Expand All @@ -55,7 +54,7 @@ export default function useApiFetch() {
// change condition here if something is changed
credentials: config.features.account.isEnabled ? 'include' : 'same-origin',
headers,
..._omit(fetchParams, 'headers'),
...(fetchParams ? omit(fetchParams, [ 'headers' ]) : {}),
},
{
resource: resource.path,
Expand Down
3 changes: 1 addition & 2 deletions lib/contexts/scrollDirection.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import clamp from 'lodash/clamp';
import throttle from 'lodash/throttle';
import { throttle, clamp } from 'es-toolkit';
import React from 'react';

const ScrollDirectionContext = React.createContext<'up' | 'down' | null>(null);
Expand Down
4 changes: 2 additions & 2 deletions lib/hooks/useClientRect.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import _debounce from 'lodash/debounce';
import { debounce } from 'es-toolkit';
import type { LegacyRef } from 'react';
import React from 'react';

Expand All @@ -19,7 +19,7 @@ export default function useClientRect<E extends Element>(): [ DOMRect | null, Le
return;
}

const resizeHandler = _debounce(() => {
const resizeHandler = debounce(() => {
setRect(nodeRef.current?.getBoundingClientRect() ?? null);
}, 100);

Expand Down
2 changes: 1 addition & 1 deletion lib/hooks/useIsSticky.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import throttle from 'lodash/throttle';
import { throttle } from 'es-toolkit';
import React from 'react';

export default function useIsSticky(ref: React.RefObject<HTMLDivElement>, offset = 0, isEnabled = true) {
Expand Down
4 changes: 2 additions & 2 deletions lib/hooks/useLazyRenderedList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import _clamp from 'lodash/clamp';
import { clamp } from 'es-toolkit';
import React from 'react';
import { useInView } from 'react-intersection-observer';

Expand All @@ -15,7 +15,7 @@ export default function useLazyRenderedList(list: Array<unknown>, isEnabled: boo

React.useEffect(() => {
if (inView) {
setRenderedItemsNum((prev) => _clamp(prev + STEP, 0, list.length));
setRenderedItemsNum((prev) => clamp(prev + STEP, 0, list.length));
}
}, [ inView, list.length ]);

Expand Down
4 changes: 2 additions & 2 deletions lib/mixpanel/getTabName.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import _capitalize from 'lodash/capitalize';
import { capitalize } from 'es-toolkit';

export default function getTabName(tab: string) {
return tab !== '' ? _capitalize(tab.replaceAll('_', ' ')) : 'Default';
return tab !== '' ? capitalize(tab.replaceAll('_', ' ')) : 'Default';
}
6 changes: 3 additions & 3 deletions lib/mixpanel/useInit.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import _capitalize from 'lodash/capitalize';
import { capitalize } from 'es-toolkit';
import type { Config } from 'mixpanel-browser';
import mixpanel from 'mixpanel-browser';
import { useRouter } from 'next/router';
Expand Down Expand Up @@ -40,12 +40,12 @@ export default function useMixpanelInit() {
'Viewport width': window.innerWidth,
'Viewport height': window.innerHeight,
Language: window.navigator.language,
'Device type': _capitalize(deviceType),
'Device type': capitalize(deviceType),
'User id': userId,
});
mixpanel.identify(userId);
userProfile.set({
'Device Type': _capitalize(deviceType),
'Device Type': capitalize(deviceType),
...(isAuth ? { 'With Account': true } : {}),
});
userProfile.setOnce({
Expand Down
5 changes: 2 additions & 3 deletions lib/networks/networkExplorers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import _compose from 'lodash/fp/compose';
import _mapValues from 'lodash/mapValues';
import { mapValues } from 'es-toolkit';

import type { NetworkExplorer } from 'types/networks';

Expand Down Expand Up @@ -32,7 +31,7 @@ const networkExplorers: Array<NetworkExplorer> = (() => {
return config.UI.explorers.items.map((explorer) => ({
...explorer,
baseUrl: stripTrailingSlash(explorer.baseUrl),
paths: _mapValues(explorer.paths, _compose(stripTrailingSlash, addLeadingSlash)),
paths: mapValues(explorer.paths, (value) => value ? stripTrailingSlash(addLeadingSlash(value)) : value),
}));
})();

Expand Down
4 changes: 2 additions & 2 deletions lib/recentSearchKeywords.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import _uniq from 'lodash/uniq';
import { uniq } from 'es-toolkit';

import isBrowser from './isBrowser';

Expand Down Expand Up @@ -27,7 +27,7 @@ export function saveToRecentKeywords(value: string) {
}

const keywordsArr = getRecentSearchKeywords();
const result = _uniq([ value, ...keywordsArr ]).slice(0, MAX_KEYWORDS_NUMBER - 1);
const result = uniq([ value, ...keywordsArr ]).slice(0, MAX_KEYWORDS_NUMBER - 1);
window.localStorage.setItem(RECENT_KEYWORDS_LS_KEY, JSON.stringify(result));
}

Expand Down
4 changes: 2 additions & 2 deletions lib/token/metadata/attributesParser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import _upperFirst from 'lodash/upperFirst';
import { upperFirst } from 'es-toolkit';

import type { Metadata, MetadataAttributes } from 'types/client/token';

Expand Down Expand Up @@ -72,7 +72,7 @@ export default function attributesParser(attributes: Array<unknown>): Metadata['

return {
...formatValue(value, display, trait),
trait_type: _upperFirst(trait || 'property'),
trait_type: upperFirst(trait || 'property'),
};
})
.filter((item) => item?.value)
Expand Down
4 changes: 2 additions & 2 deletions lib/web3/useAddOrSwitchChain.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import _get from 'lodash/get';
import { get } from 'es-toolkit/compat';
import React from 'react';

import config from 'configs/app';
Expand All @@ -25,7 +25,7 @@ export default function useAddOrSwitchChain() {

const errorObj = getErrorObj(error);
const code = errorObj && 'code' in errorObj ? errorObj.code : undefined;
const originalErrorCode = _get(errorObj, 'data.originalError.code');
const originalErrorCode = get(errorObj, 'data.originalError.code');

// This error code indicates that the chain has not been added to Wallet.
if (code === 4902 || originalErrorCode === 4902) {
Expand Down
6 changes: 3 additions & 3 deletions mocks/blocks/epoch.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import _padStart from 'lodash/padStart';
import { padStart } from 'es-toolkit/compat';

import type { BlockEpoch, BlockEpochElectionRewardDetails, BlockEpochElectionRewardDetailsResponse } from 'types/api/block';

Expand Down Expand Up @@ -42,11 +42,11 @@ function getRewardDetailsItem(index: number): BlockEpochElectionRewardDetails {
amount: `${ 100 - index }210001063118670575`,
account: {
...addressMock.withoutName,
hash: `0x30D060F129817c4DE5fBc1366d53e19f43c8c6${ _padStart(String(index), 2, '0') }`,
hash: `0x30D060F129817c4DE5fBc1366d53e19f43c8c6${ padStart(String(index), 2, '0') }`,
},
associated_account: {
...addressMock.withoutName,
hash: `0x456f41406B32c45D59E539e4BBA3D7898c3584${ _padStart(String(index), 2, '0') }`,
hash: `0x456f41406B32c45D59E539e4BBA3D7898c3584${ padStart(String(index), 2, '0') }`,
},
};
}
Expand Down
8 changes: 4 additions & 4 deletions mocks/stats/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import _mapValues from 'lodash/mapValues';
import { mapValues } from 'es-toolkit';

import type { HomeStats } from 'types/api/stats';

Expand Down Expand Up @@ -51,17 +51,17 @@ export const withBtcLocked: HomeStats = {

export const withoutFiatPrices: HomeStats = {
...base,
gas_prices: _mapValues(base.gas_prices, (price) => price ? ({ ...price, fiat_price: null }) : null),
gas_prices: base.gas_prices ? mapValues(base.gas_prices, (price) => price ? ({ ...price, fiat_price: null }) : null) : null,
};

export const withoutGweiPrices: HomeStats = {
...base,
gas_prices: _mapValues(base.gas_prices, (price) => price ? ({ ...price, price: null }) : null),
gas_prices: base.gas_prices ? mapValues(base.gas_prices, (price) => price ? ({ ...price, price: null }) : null) : null,
};

export const withoutBothPrices: HomeStats = {
...base,
gas_prices: _mapValues(base.gas_prices, (price) => price ? ({ ...price, price: null, fiat_price: null }) : null),
gas_prices: base.gas_prices ? mapValues(base.gas_prices, (price) => price ? ({ ...price, price: null, fiat_price: null }) : null) : null,
};

export const withoutGasInfo: HomeStats = {
Expand Down
14 changes: 2 additions & 12 deletions nextjs/csp/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type CspDev from 'csp-dev';
import { uniq } from 'es-toolkit';

export const KEY_WORDS = {
BLOB: 'blob:',
Expand All @@ -11,17 +12,6 @@ export const KEY_WORDS = {
UNSAFE_EVAL: '\'unsafe-eval\'',
};

// we cannot use lodash/uniq and lodash/mergeWith in middleware code since it calls new Set() and it'is causing an error in Next.js
// "Dynamic Code Evaluation (e. g. 'eval', 'new Function', 'WebAssembly.compile') not allowed in Edge Runtime"
export function unique(array: Array<string | undefined>) {
const set: Record<string, boolean> = {};
for (const item of array) {
item && (set[item] = true);
}

return Object.keys(set);
}

export function mergeDescriptors(...descriptors: Array<CspDev.DirectiveDescriptor>) {
return descriptors.reduce((result, item) => {
for (const _key in item) {
Expand Down Expand Up @@ -50,7 +40,7 @@ export function makePolicyString(policyDescriptor: CspDev.DirectiveDescriptor) {
return;
}

const uniqueValues = unique(value);
const uniqueValues = uniq(value);
return [ key, uniqueValues.join(' ') ].join(' ');
})
.filter(Boolean)
Expand Down
4 changes: 2 additions & 2 deletions nextjs/utils/fetchProxy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { pick } from 'es-toolkit';
import type { IncomingMessage } from 'http';
import _pick from 'lodash/pick';
import type { NextApiRequest } from 'next';
import type { NextApiRequestCookies } from 'next/dist/server/api-utils';
import type { RequestInit, Response } from 'node-fetch';
Expand All @@ -21,7 +21,7 @@ export default function fetchFactory(
accept: _req.headers['accept'] || 'application/json',
'content-type': _req.headers['content-type'] || 'application/json',
cookie: apiToken ? `${ cookies.NAMES.API_TOKEN }=${ apiToken }` : '',
..._pick(_req.headers, [
...pick(_req.headers, [
'x-csrf-token',
'Authorization', // the old value, just in case
'authorization', // Node.js automatically lowercases headers
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"dappscout-iframe": "0.2.5",
"dayjs": "^1.11.5",
"dom-to-image": "^2.6.0",
"es-toolkit": "1.31.0",
"focus-visible": "^5.2.0",
"framer-motion": "^6.5.1",
"getit-sdk": "^1.0.4",
Expand All @@ -88,7 +89,6 @@
"graphql": "^16.8.1",
"graphql-ws": "^5.11.3",
"js-cookie": "^3.0.1",
"lodash": "^4.0.0",
"magic-bytes.js": "1.8.0",
"mixpanel-browser": "^2.47.0",
"monaco-editor": "^0.34.1",
Expand Down
5 changes: 2 additions & 3 deletions pages/api/proxy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import _pick from 'lodash/pick';
import _pickBy from 'lodash/pickBy';
import { pick, pickBy } from 'es-toolkit';
import type { NextApiRequest, NextApiResponse } from 'next';

import fetchFactory from 'nextjs/utils/fetchProxy';
Expand All @@ -18,7 +17,7 @@ const handler = async(nextReq: NextApiRequest, nextRes: NextApiResponse) => {
);
const apiRes = await fetchFactory(nextReq)(
url.toString(),
_pickBy(_pick(nextReq, [ 'body', 'method' ]), Boolean),
pickBy(pick(nextReq, [ 'body', 'method' ]), Boolean),
);

// proxy some headers from API
Expand Down
4 changes: 2 additions & 2 deletions playwright/fixtures/mockContractReadResponse.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { TestFixture, Page } from '@playwright/test';
import _isEqual from 'lodash/isEqual';
import { isEqual } from 'es-toolkit';
import { encodeFunctionData, encodeFunctionResult, type AbiFunction } from 'viem';

import { getEnvValue } from 'configs/app/utils';
Expand Down Expand Up @@ -43,7 +43,7 @@ const fixture: TestFixture<MockContractReadResponseFixture, { page: Page }> = as
value: params?.value,
};

if (_isEqual(params, callParams) && id) {
if (isEqual(params, callParams) && id) {
return route.fulfill({
status: 200,
json: {
Expand Down
4 changes: 2 additions & 2 deletions playwright/fixtures/mockRpcResponse.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { TestFixture, Page } from '@playwright/test';
import _isEqual from 'lodash/isEqual';
import { isEqual } from 'es-toolkit';
import type { PublicRpcSchema } from 'viem';

import { getEnvValue } from 'configs/app/utils';
Expand Down Expand Up @@ -34,7 +34,7 @@ const fixture: TestFixture<MockRpcResponseFixture, { page: Page }> = async({ pag
...(rpcMock.Parameters ? { params: rpcMock.Parameters } : {}),
};

if (_isEqual(json, payload) && id !== undefined) {
if (isEqual(json, payload) && id !== undefined) {
return route.fulfill({
status: 200,
json: {
Expand Down
8 changes: 5 additions & 3 deletions playwright/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import './fonts.css';
import './index.css';
import { beforeMount } from '@playwright/experimental-ct-react/hooks';
import _defaultsDeep from 'lodash/defaultsDeep';
import MockDate from 'mockdate';
import * as router from 'next/router';

Expand All @@ -12,12 +11,15 @@ const NEXT_ROUTER_MOCK = {
replace: () => Promise.resolve(),
};

beforeMount(async({ hooksConfig }) => {
beforeMount(async({ hooksConfig }: { hooksConfig?: { router: typeof router } }) => {
// Before mount, redefine useRouter to return mock value from test.

// @ts-ignore: I really want to redefine this property :)
// eslint-disable-next-line no-import-assign
router.useRouter = () => _defaultsDeep(hooksConfig?.router, NEXT_ROUTER_MOCK);
router.useRouter = () => ({
...NEXT_ROUTER_MOCK,
...hooksConfig?.router,
});

// set current date
MockDate.set('2022-11-11T12:00:00Z');
Expand Down
Loading

0 comments on commit 1088126

Please sign in to comment.