Skip to content

Commit c4c83a5

Browse files
Merge pull request #251 from balena-io-modules/drop-some-lodash-for-native-alternatives
Drop some lodash usage in favor of native JS alternatives
2 parents e5d8b3e + f5c2dd6 commit c4c83a5

File tree

21 files changed

+98
-73
lines changed

21 files changed

+98
-73
lines changed

src/components/DownloadImageDialog/index.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,9 @@ import { DropDownButton } from '../DropDownButton';
2424

2525
import pickBy from 'lodash/pickBy';
2626
import debounce from 'lodash/debounce';
27-
import isEmpty from 'lodash/isEmpty';
2827
import type { DeviceType, Dictionary, OsVersionsByDeviceType } from './models';
2928
import { OsTypesEnum } from './models';
30-
import uniq from 'lodash/uniq';
29+
import { uniq } from '../../utils/arrays';
3130
import { enqueueSnackbar } from 'notistack';
3231
import { DialogWithCloseButton } from '../DialogWithCloseButton';
3332
import type { CalloutProps } from '../Callout';
@@ -36,6 +35,7 @@ import { Spinner } from '../Spinner';
3635
import { faDownload } from '@fortawesome/free-solid-svg-icons';
3736
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
3837
import * as semver from 'balena-semver';
38+
import { isObjectEmpty } from '../../utils/objects';
3939

4040
const etcherLogoBase64 =
4141
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAwIiBoZWlnaHQ9IjYwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KIDxnPgogIDx0aXRsZT5FdGNoZXI8L3RpdGxlPgogIDxnIGlkPSJzdmdfMSIgc3Ryb2tlPSJudWxsIj4KICAgPHBhdGggaWQ9InN2Z18yIiBjbGFzcz0ic3QxIiBkPSJtNDEyLjkwMzgzLDM1OC4wNjcxM2wwLDE3MS40OTU4M2M3LjQ5MjU0LC0xLjY2NTAxIDE0LjE1MjU3LC0zLjMzMDAyIDIwLjgxMjYsLTcuNDkyNTRsMTQyLjM1ODE5LC04MS41ODUzOWMyMC44MTI2LC0xMS42NTUwNiAzMy4zMDAxNiwtMzQuMTMyNjYgMzMuMzAwMTYsLTU4LjI3NTI4bDAsLTE2Mi4zMzgyOGMwLC02LjY2MDAzIC0wLjgzMjUsLTEzLjMyMDA2IC0zLjMzMDAyLC0xOS4xNDc1OWwtMTU0LjAxMzI0LDg5LjA3NzkzYy0zMi40Njc2NiwyMi40Nzc2MSAtMzkuMTI3NjksNDMuMjkwMjEgLTM5LjEyNzY5LDY4LjI2NTMzbDAsLTAuMDAwMDF6IiBmaWxsPSIjQTVERTM3IiBzdHJva2U9Im51bGwiLz4KICAgPHBhdGggaWQ9InN2Z18zIiBjbGFzcz0ic3QyIiBkPSJtNjYyLjY1NTAzLDE2Ny40MjM3MWwtNTYuNjEwMjcsMzIuNDY3NjZjMS42NjUwMSw1LjgyNzUzIDMuMzMwMDIsMTIuNDg3NTYgMy4zMzAwMiwxOS4xNDc1OWwwLDE2My4xNzA3OWMwLDI0LjE0MjYyIC0xMy4zMjAwNiw0Ni42MjAyMiAtMzMuMzAwMTYsNTguMjc1MjhsLTE0Mi4zNTgxOSw4MS41ODUzOWMtNi42NjAwMywzLjMzMDAyIC0xMy4zMjAwNiw1LjgyNzUzIC0yMC44MTI2LDcuNDkyNTRsMCw2NC45MzUzMWM5Ljk5MDA1LC0xLjY2NTAxIDE5Ljk4MDEsLTQuOTk1MDIgMjguMzA1MTQsLTkuOTkwMDVsMTg0LjgxNTg5LC0xMDUuNzI4MDFjMjUuODA3NjIsLTE0Ljk4NTA3IDQxLjYyNTIsLTQyLjQ1NzcgNDEuNjI1MiwtNzIuNDI3ODVsMCwtMjExLjQ1NjAyYzAsLTkuMTU3NTQgLTEuNjY1MDEsLTE4LjMxNTA5IC00Ljk5NTAyLC0yNy40NzI2M2wtMC4wMDAwMSwweiIgZmlsbD0iI0M4RjE3OCIgc3Ryb2tlPSJudWxsIi8+CiAgIDxwYXRoIGlkPSJzdmdfNCIgY2xhc3M9InN0MSIgZD0ibTM5OS41ODM3NiwzMDMuOTU0MzZjOC4zMjUwNCwtMTMuMzIwMDYgMjAuODEyNiwtMjUuODA3NjIgMzkuMTI3NjksLTM2LjYzMDE4bDE1NS42NzgyNSwtODkuOTEwNDNjLTQuOTk1MDIsLTYuNjYwMDMgLTExLjY1NTA2LC0xMi40ODc1NiAtMTguMzE1MDksLTE2LjY1MDA4bC0xNDIuMzU4MTksLTgxLjU4NTM5Yy0yMC44MTI2LC0xMS42NTUwNiAtNDYuNjIwMjIsLTExLjY1NTA2IC02Ny40MzI4MiwwbC0xNDEuNTI1NjgsODEuNTg1MzljLTcuNDkyNTQsNC4xNjI1MiAtMTMuMzIwMDYsOS45OTAwNSAtMTkuMTQ3NTksMTYuNjUwMDhsMTU0Ljg0NTc1LDg5LjkxMDQzYzE4LjMxNTA5LDExLjY1NTA2IDMwLjgwMjY1LDIzLjMxMDExIDM5LjEyNzY5LDM2LjYzMDE4bC0wLjAwMDAxLDB6IiBmaWxsPSIjQTVERTM3IiBzdHJva2U9Im51bGwiLz4KICAgPHBhdGggaWQ9InN2Z181IiBjbGFzcz0ic3QyIiBkPSJtMjI0Ljc1NzkyLDE2MS41OTYxOGwxNDEuNTI1NjgsLTgxLjU4NTM5YzIwLjgxMjYsLTExLjY1NTA2IDQ2LjYyMDIyLC0xMS42NTUwNiA2Ny40MzI4MiwwbDE0Mi4zNTgxOSw4MS41ODUzOWM3LjQ5MjU0LDQuMTYyNTIgMTMuMzIwMDYsOS45OTAwNSAxOC4zMTUwOSwxNi42NTAwOGw1Ni42MTAyNywtMzIuNDY3NjZjLTYuNjYwMDMsLTkuMTU3NTQgLTE0Ljk4NTA3LC0xNi42NTAwOCAtMjQuOTc1MTIsLTIxLjY0NTFsLTE4NC44MTU4OSwtMTA3LjM5MzAyYy0yNS44MDc2MiwtMTQuOTg1MDcgLTU3LjQ0Mjc4LC0xNC45ODUwNyAtODMuMjUwNCwwbC0xODMuMTUwODgsMTA2LjU2MDUxYy05Ljk5MDA1LDUuODI3NTMgLTE4LjMxNTA5LDEzLjMyMDA2IC0yNC45NzUxMiwyMi40Nzc2MWw1Ni42MTAyNywzMi40Njc2NmM0LjE2MjUyLC02LjY2MDAzIDEwLjgyMjU1LC0xMi40ODc1NiAxOC4zMTUwOSwtMTYuNjUwMDh6IiBmaWxsPSIjQzhGMTc4IiBzdHJva2U9Im51bGwiLz4KICAgPHBhdGggaWQ9InN2Z182IiBjbGFzcz0ic3QyIiBkPSJtMzY2LjI4MzYsNTIyLjA3MDQxbC0xNDEuNTI1NjgsLTgxLjU4NTM5Yy0yMC44MTI2LC0xMS42NTUwNiAtMzMuMzAwMTYsLTM0LjEzMjY2IC0zMy4zMDAxNiwtNTguMjc1MjhsMCwtMTYzLjE3MDc5YzAsLTYuNjYwMDMgMC44MzI1LC0xMi40ODc1NiAyLjQ5NzUxLC0xOC4zMTUwOWwtNTYuNjEwMjcsLTMyLjQ2NzY2Yy0zLjMzMDAyLDkuMTU3NTQgLTQuOTk1MDIsMTcuNDgyNTggLTQuOTk1MDIsMjYuNjQwMTNsMCwyMTIuMjg4NTJjMCwyOS45NzAxNCAxNS44MTc1OCw1Ny40NDI3OCA0MS42MjUyLDcxLjU5NTM0bDE4My45ODMzOSwxMDUuNzI4MDFjOC4zMjUwNCw0Ljk5NTAyIDE4LjMxNTA5LDguMzI1MDQgMjguMzA1MTQsOS45OTAwNWwwLC02NC45MzUzMWMtNi42NjAwMywtMC44MzI1IC0xMy4zMjAwNiwtMy4zMzAwMiAtMTkuOTgwMSwtNy40OTI1NGwtMC4wMDAwMSwwLjAwMDAxeiIgZmlsbD0iI0M4RjE3OCIgc3Ryb2tlPSJudWxsIi8+CiAgIDxwYXRoIGlkPSJzdmdfNyIgY2xhc3M9InN0MSIgZD0ibTM0Ny4xMzYwMSwyODguOTY5MjlsLTE1My4xODA3NCwtODguMjQ1NDJjLTEuNjY1MDEsNS44Mjc1MyAtMi40OTc1MSwxMi40ODc1NiAtMi40OTc1MSwxOC4zMTUwOWwwLDE2My4xNzA3OWMwLDI0LjE0MjYyIDEyLjQ4NzU2LDQ2LjYyMDIyIDMzLjMwMDE2LDU4LjI3NTI4bDE0MS41MjU2OCw4MS41ODUzOWM2LjY2MDAzLDMuMzMwMDIgMTMuMzIwMDYsNS44Mjc1MyAyMC44MTI2LDcuNDkyNTRsMCwtMTcxLjQ5NTgzYy0wLjgzMjUsLTI0Ljk3NTEyIC03LjQ5MjU0LC00NS43ODc3MiAtMzkuOTYwMTksLTY5LjA5NzgzbDAsLTAuMDAwMDF6IiBmaWxsPSIjQTVERTM3IiBzdHJva2U9Im51bGwiLz4KICA8L2c+CiA8L2c+Cgo8L3N2Zz4=';
@@ -68,9 +68,9 @@ const getUniqueOsTypes = (
6868
deviceTypeSlug: string | undefined,
6969
) => {
7070
if (
71-
isEmpty(osVersions) ||
71+
isObjectEmpty(osVersions) ||
7272
!deviceTypeSlug ||
73-
isEmpty(osVersions[deviceTypeSlug])
73+
osVersions[deviceTypeSlug]?.length === 0
7474
) {
7575
return [];
7676
}
@@ -237,7 +237,7 @@ export const DownloadImageDialog = ({
237237
}
238238
: {},
239239
);
240-
const [isFetching, setIsFetching] = useState(isEmpty(osVersions));
240+
const [isFetching, setIsFetching] = useState(isObjectEmpty(osVersions));
241241
const [downloadSize, setDownloadSize] = useState<string | null>(null);
242242
const [isValidatingUrl, setIsValidatingUrl] = useState(false);
243243

@@ -488,7 +488,7 @@ export const DownloadImageDialog = ({
488488
<Spinner />
489489
) : (
490490
<>
491-
{isEmpty(osVersions) && (
491+
{isObjectEmpty(osVersions) && (
492492
<Callout severity="warning">
493493
No OS versions available for download
494494
</Callout>

src/components/DownloadImageDialog/version.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import uniq from 'lodash/uniq';
1+
import { uniq } from '../../utils/arrays';
22
import partition from 'lodash/partition';
33
import type { Dictionary, OsVersion } from './models';
44

src/components/DropDownButton/index.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { Button, ButtonGroup, MenuItem, Menu } from '@mui/material';
99
import { ButtonWithTracking } from '../ButtonWithTracking';
1010
import { useAnalyticsContext } from '../../contexts/AnalyticsContext';
1111
import groupBy from 'lodash/groupBy';
12-
import flatMap from 'lodash/flatMap';
1312
import { Tooltip } from '../Tooltip';
1413
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
1514
import {
@@ -59,16 +58,18 @@ export const DropDownButton = <T extends unknown>({
5958
return items;
6059
}
6160
const grouped = groupBy(items, (item) => item[groupByProp]);
62-
const keys = Object.keys(grouped);
63-
const lastKey = keys[keys.length - 1];
61+
const entries = Object.entries(grouped);
62+
const lastKey = entries.at(-1)?.[0];
6463

65-
return flatMap(grouped, (value, key) => [
66-
...value.map((v, index) =>
67-
key !== lastKey && index === value.length - 1
68-
? { ...v, divider: true }
69-
: v,
70-
),
71-
]).filter((item) => item);
64+
return entries
65+
.flatMap(([key, value]) =>
66+
value.map((v, index) =>
67+
key !== lastKey && index === value.length - 1
68+
? { ...v, divider: true }
69+
: v,
70+
),
71+
)
72+
.filter(Boolean);
7273
}, [items, groupByProp]);
7374

7475
const handleClick = (

src/components/Form/Widgets/FileWidget.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import {
2929
faUpload,
3030
} from '@fortawesome/free-solid-svg-icons';
3131
import { useRandomUUID } from '../../../hooks/useRandomUUID';
32-
import uniq from 'lodash/uniq';
32+
import { uniq } from '../../../utils/arrays';
3333
import { token } from '../../../utils/token';
3434

3535
const restingStyle: SxProps = {

src/components/RJST/DataTypes/array.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { CreateFilter } from './utils';
88
import { getDataTypeSchema } from './utils';
99
import { getDataModel } from '.';
1010
import { getRefSchema, isJSONSchema } from '../schemaOps';
11+
import { isObjectEmpty } from '../../../utils/objects';
1112

1213
export const operators = () => ({
1314
contains: 'contains',
@@ -42,7 +43,7 @@ const buildFilterForPropertySchema = (
4243
): JSONSchema => {
4344
const filter = getFilter(field, schema, value, operator);
4445

45-
if (!Object.keys(filter).length) {
46+
if (isObjectEmpty(filter)) {
4647
return {};
4748
}
4849
return {
@@ -93,7 +94,7 @@ const getFilter = (
9394
operator: effectiveOperator,
9495
value,
9596
});
96-
if (!filter || typeof filter !== 'object' || !Object.keys(filter).length) {
97+
if (!filter || typeof filter !== 'object' || isObjectEmpty(filter)) {
9798
return {};
9899
}
99100

@@ -103,7 +104,7 @@ const getFilter = (
103104

104105
return recursiveFilter &&
105106
isJSONSchema(recursiveFilter) &&
106-
Object.keys(recursiveFilter).length
107+
!isObjectEmpty(recursiveFilter)
107108
? wrapFilter(recursiveFilter)
108109
: {};
109110
};

src/components/RJST/DataTypes/object.tsx

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import type { JSONSchema7 as JSONSchema } from 'json-schema';
2-
import find from 'lodash/find';
32
import type { CreateFilter, KeysOfUnion } from './utils';
43
import { getDataTypeSchema, regexEscape } from './utils';
54
import type { FormData } from '../components/Filters/SchemaSieve';
@@ -8,23 +7,43 @@ import {
87
createModelFilter,
98
} from '../components/Filters/SchemaSieve';
109
import { isJSONSchema, getRefSchema } from '../schemaOps';
11-
import findKey from 'lodash/findKey';
1210
import pick from 'lodash/pick';
1311
import mapValues from 'lodash/mapValues';
1412

13+
const findValueByDescription = (
14+
obj: object | undefined,
15+
description: string,
16+
) => {
17+
return Object.values(obj ?? {}).find(
18+
(property) =>
19+
typeof property === 'object' &&
20+
'description' in property &&
21+
property.description === description,
22+
) as JSONSchema | undefined;
23+
};
24+
25+
const findKeyByDescription = (obj: object | undefined, description: string) => {
26+
return Object.entries(obj ?? {}).find(
27+
([, value]) =>
28+
typeof value === 'object' &&
29+
'description' in value &&
30+
value.description === description,
31+
)?.[0];
32+
};
33+
1534
const getKeyLabel = (schema: JSONSchema) => {
16-
const s = find(schema.properties, { description: 'key' }) as JSONSchema;
35+
const s = findValueByDescription(schema.properties, 'key');
1736
return s?.title ?? 'key';
1837
};
1938

2039
const getValueLabel = (schema: JSONSchema) => {
21-
const s = find(schema.properties, { description: 'value' }) as JSONSchema;
40+
const s = findValueByDescription(schema.properties, 'value');
2241
return s?.title ?? 'value';
2342
};
2443

2544
export const isKeyValueObj = (schema: JSONSchema) =>
26-
!!find(schema.properties, { description: 'key' }) ||
27-
!!find(schema.properties, { description: 'value' });
45+
!!findValueByDescription(schema.properties, 'key') ||
46+
!!findValueByDescription(schema.properties, 'value');
2847

2948
export const operators = (s: JSONSchema) => {
3049
return {
@@ -94,7 +113,7 @@ const getValueForOperation = (
94113
? 'value'
95114
: null;
96115
const schemaProperty = schemaField
97-
? findKey(schema.properties, { description: schemaField })
116+
? findKeyByDescription(schema.properties, schemaField)
98117
: null;
99118

100119
// Return the appropriate value format based on the operation type
@@ -191,8 +210,8 @@ export const createFilter: CreateFilter<OperatorSlug> = (
191210

192211
// TODO: this case does not cover complex objects for FULL_TEXT_SLUG
193212
if (operator === FULL_TEXT_SLUG && schema.properties) {
194-
const schemaKey = findKey(schema.properties, { description: 'key' });
195-
const schemaValue = findKey(schema.properties, { description: 'value' });
213+
const schemaKey = findKeyByDescription(schema.properties, 'key');
214+
const schemaValue = findKeyByDescription(schema.properties, 'value');
196215
const properties = [schemaKey, schemaValue]
197216
.map((key) =>
198217
key

src/components/RJST/Filters/utils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import {
1010
parseDescription,
1111
parseDescriptionProperty,
1212
} from '../schemaOps';
13-
import isEmpty from 'lodash/isEmpty';
1413
import get from 'lodash/get';
14+
import { isObjectEmpty } from '../../../utils/objects';
1515

1616
const X_FOREIGN_KEY_SCHEMA_SEPARATOR = '___ref_scheme_separator_';
1717

@@ -69,12 +69,12 @@ export const removeFieldsWithNoFilter = (schema: JSONSchema): JSONSchema => {
6969
}
7070

7171
const hasEmptyProperties =
72-
newValue.properties && isEmpty(newValue.properties);
72+
newValue.properties && isObjectEmpty(newValue.properties);
7373

7474
const hasEmptyItemsProperties =
7575
isJSONSchema(newValue.items) &&
7676
'properties' in newValue.items &&
77-
isEmpty(newValue.items.properties);
77+
isObjectEmpty(newValue.items.properties ?? {});
7878

7979
if (hasEmptyProperties || hasEmptyItemsProperties) {
8080
continue;

src/components/RJST/Lenses/index.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as types from './types';
22
import type { IconProp } from '@fortawesome/fontawesome-svg-core';
3-
import uniq from 'lodash/uniq';
43

54
export interface LensTemplate<T extends { id: number } = any> {
65
slug: string;
@@ -32,7 +31,7 @@ export const getLenses = <T extends { id: number }>(
3231
);
3332

3433
const slugs = concatenatedLenses.map((lens) => lens.slug);
35-
if (slugs.length > uniq(slugs).length) {
34+
if (slugs.length > new Set(slugs).size) {
3635
throw new Error('Lenses must have unique slugs');
3736
}
3837

src/components/RJST/components/Filters/SchemaSieve.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import addFormats from 'ajv-formats';
1212
import pickBy from 'lodash/pickBy';
1313
import Ajv from 'ajv';
1414
import { enqueueSnackbar } from 'notistack';
15+
import { isObjectEmpty } from '../../../../utils/objects';
1516

1617
const ajv = new Ajv();
1718
ajvKeywords(ajv, ['regexp']);
@@ -130,11 +131,7 @@ export const createFilter = (
130131
operator,
131132
value,
132133
});
133-
if (
134-
!filter ||
135-
typeof filter !== 'object' ||
136-
!Object.keys(filter).length
137-
) {
134+
if (!filter || typeof filter !== 'object' || isObjectEmpty(filter)) {
138135
return {};
139136
}
140137
return {

src/components/RJST/components/Table/useTagActions.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useMemo, useState } from 'react';
22
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
33
import { faPlus } from '@fortawesome/free-solid-svg-icons/faPlus';
4-
import uniq from 'lodash/uniq';
4+
import { uniq } from '../../../../utils/arrays';
55
import type { RJSTContext } from '../../schemaOps';
66
import { useQuery } from '@tanstack/react-query';
77
import { Stack } from '@mui/material';

0 commit comments

Comments
 (0)