From 68912cd441b09eb856585b9d9160075d8f94f79b Mon Sep 17 00:00:00 2001 From: Joris Buchou Date: Tue, 25 Aug 2026 12:08:56 -0700 Subject: [PATCH] fix(deps): remove lodash runtime dependency lodash sat in `dependencies` for a single expression of shipped code. Four usages, one of them shipped: - `src/use-extra-deps/index.ts` (shipped): `mapValues(extraDeps, ({value}) => value)` becomes `Object.fromEntries(Object.entries(extraDeps).map(...))`. The existing `as T` cast is unchanged. - `src/use-extra-deps/index.test.tsx`: `last(allDeps)` becomes `allDeps[allDeps.length - 1]`. `Array.prototype.at` is ES2022 and this package's `lib` only reaches ES2019, so `.at(-1)` does not compile. - `src/use-safe-effect/index.test.tsx` and `src/use-safe-imperative-handle/index.test.tsx`: the `isEqual` deep comparator is only ever applied to `Array`, for which an element-wise comparison is deep equality. Each file gets a four-line local `stringArraysEqual` rather than a hand-rolled generic deep equal. Co-Authored-By: Claude Opus 5 (1M context) --- dist/use-extra-deps/index.js | 6 +----- package.json | 2 -- src/use-extra-deps/index.test.tsx | 3 +-- src/use-extra-deps/index.ts | 5 +++-- src/use-safe-effect/index.test.tsx | 8 ++++++-- src/use-safe-imperative-handle/index.test.tsx | 8 ++++++-- yarn.lock | 10 ---------- 7 files changed, 17 insertions(+), 25 deletions(-) diff --git a/dist/use-extra-deps/index.js b/dist/use-extra-deps/index.js index 99a09035..a0dcb5c5 100644 --- a/dist/use-extra-deps/index.js +++ b/dist/use-extra-deps/index.js @@ -32,14 +32,10 @@ var __importStar = (this && this.__importStar) || (function () { return result; }; })(); -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; Object.defineProperty(exports, "__esModule", { value: true }); exports.unsafeMkCallbackFn = unsafeMkCallbackFn; exports.useExtraDeps = useExtraDeps; /* eslint @typescript-eslint/no-explicit-any: 0 */ -const mapValues_1 = __importDefault(require("lodash/mapValues")); const React = __importStar(require("react")); function unsafeMkCallbackFn(callback) { return callback; @@ -78,6 +74,6 @@ function useExtraDeps(deps, extraDeps) { } return { allDeps: [...deps, run], - extraDepValues: (0, mapValues_1.default)(extraDeps, ({ value }) => value) + extraDepValues: Object.fromEntries(Object.entries(extraDeps).map(([key, { value }]) => [key, value])) }; } diff --git a/package.json b/package.json index 047e7506..22f468d1 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,6 @@ "@testing-library/react": "^16.3.2", "@types/invariant": "^2.2.37", "@types/jest": "^30.0.0", - "@types/lodash": "^4.17.25", "@types/react": "^19.2.18", "@types/react-dom": "^19.2.4", "@typescript-eslint/eslint-plugin": "^8.67.0", @@ -58,7 +57,6 @@ "@testing-library/dom": "^10.4.1", "invariant": "^2.2.4", "jest-environment-jsdom": "^30.4.1", - "lodash": "^4.18.1", "react": "^19.2.8", "react-dom": "^19.2.8" }, diff --git a/src/use-extra-deps/index.test.tsx b/src/use-extra-deps/index.test.tsx index 8edc7330..32f681fa 100644 --- a/src/use-extra-deps/index.test.tsx +++ b/src/use-extra-deps/index.test.tsx @@ -1,4 +1,3 @@ -import last from 'lodash/last' import * as React from 'react' import {render} from '@testing-library/react' import {useExtraDeps} from '.' @@ -11,7 +10,7 @@ describe('useExtraDeps', () => { p1: {value: p1, comparator: (a, b) => a === b} }) //The symbol is always the last thing in the allDeps array - symbol = last(allDeps) + symbol = allDeps[allDeps.length - 1] return <>{p1} } const {rerender} = render() diff --git a/src/use-extra-deps/index.ts b/src/use-extra-deps/index.ts index 279d8afd..9e93ddf5 100644 --- a/src/use-extra-deps/index.ts +++ b/src/use-extra-deps/index.ts @@ -1,5 +1,4 @@ /* eslint @typescript-eslint/no-explicit-any: 0 */ -import mapValues from 'lodash/mapValues' import * as React from 'react' // Dependencies that are safe to use in the normal `useEffect` deps array @@ -67,6 +66,8 @@ export function useExtraDeps>( return { allDeps: [...deps, run], - extraDepValues: mapValues(extraDeps, ({value}) => value) as T + extraDepValues: Object.fromEntries( + Object.entries(extraDeps).map(([key, {value}]) => [key, value]) + ) as T } } diff --git a/src/use-safe-effect/index.test.tsx b/src/use-safe-effect/index.test.tsx index eebc624c..e516599a 100644 --- a/src/use-safe-effect/index.test.tsx +++ b/src/use-safe-effect/index.test.tsx @@ -1,10 +1,14 @@ -import isEqual from 'lodash/isEqual' import * as React from 'react' import {render} from '@testing-library/react' import {useSafeEffect, useSafeEffectExtraDeps} from '.' import {useSafeCallback} from './../use-safe-callback' import {CallbackFn} from '../use-extra-deps' +// Element-wise comparison of the `Array` values compared below. For an +// array of strings this is exactly the deep comparison `lodash/isEqual` did. +const stringArraysEqual = (a: Array, b: Array): boolean => + a.length === b.length && a.every((x, i) => x === b[i]) + describe('useSafeEffect', () => { it('works with no deps', async () => { const sideEffect = jest.fn() @@ -222,7 +226,7 @@ describe('useSafeEffect', () => { { p1: {value: p1, comparator: (a, b) => a.text === b.text}, // Deep comparison of arrays - p2: {value: p2, comparator: (a, b) => isEqual(a, b)} + p2: {value: p2, comparator: stringArraysEqual} } ) return <>{p1.text} diff --git a/src/use-safe-imperative-handle/index.test.tsx b/src/use-safe-imperative-handle/index.test.tsx index 59c1a424..8b87cdc6 100644 --- a/src/use-safe-imperative-handle/index.test.tsx +++ b/src/use-safe-imperative-handle/index.test.tsx @@ -1,10 +1,14 @@ -import isEqual from 'lodash/isEqual' import * as React from 'react' import {render} from '@testing-library/react' import {useSafeImperativeHandle, useSafeImperativeHandleExtraDeps} from '.' import {useSafeCallback} from './../use-safe-callback' import {CallbackFn} from '../use-extra-deps' +// Element-wise comparison of the `Array` values compared below. For an +// array of strings this is exactly the deep comparison `lodash/isEqual` did. +const stringArraysEqual = (a: Array, b: Array): boolean => + a.length === b.length && a.every((x, i) => x === b[i]) + describe('useSafeImperativeHandle', () => { it('works with no deps', async () => { const cb = jest.fn().mockImplementation(() => ({a: 'b'})) @@ -231,7 +235,7 @@ describe('useSafeImperativeHandle', () => { { p1: {value: p1, comparator: (a, b) => a.text === b.text}, // Deep comparison of arrays - p2: {value: p2, comparator: (a, b) => isEqual(a, b)} + p2: {value: p2, comparator: stringArraysEqual} } ) return <>{p1.text} diff --git a/yarn.lock b/yarn.lock index e87d551c..7b34e0e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1857,11 +1857,6 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== -"@types/lodash@^4.17.25": - version "4.17.25" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.25.tgz#69765ac7bcddb0eb072961cf292524a8f5b3c2c0" - integrity sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ== - "@types/node@*": version "24.0.10" resolved "https://registry.yarnpkg.com/@types/node/-/node-24.0.10.tgz#f65a169779bf0d70203183a1890be7bee8ca2ddb" @@ -4375,11 +4370,6 @@ lodash.memoize@^4.1.2: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== -lodash@^4.18.1: - version "4.18.1" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c" - integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q== - loose-envify@^1.0.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"