From 604d978db4254b54ccc49f241be50c0894238ddc Mon Sep 17 00:00:00 2001 From: Alexander Harding Date: Thu, 12 Mar 2026 18:13:28 -0500 Subject: [PATCH] chore: remove lodash, add es-toolkit --- package.json | 3 +- pnpm-lock.yaml | 32 ++++++++---- src/features/alerts/JumpActions.tsx | 2 +- src/features/alerts/gairmet/Description.tsx | 5 +- src/features/outlook/OutlookTable.tsx | 2 +- src/features/rap/Hours.tsx | 2 +- .../reportMetadata/RefreshInformation.tsx | 2 +- src/features/weather/header/NWSWeather.tsx | 7 +-- src/helpers/aviationAlerts.ts | 9 ++-- src/helpers/string.ts | 2 +- src/helpers/weather.ts | 52 ++++++++++--------- src/services/aviationWeather.ts | 2 +- src/services/faa.ts | 3 +- src/services/openMeteo.ts | 2 +- 14 files changed, 67 insertions(+), 58 deletions(-) diff --git a/package.json b/package.json index bc93bc2..d6edecc 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "date-fns": "^4.1.0", "detect-browser": "^5.3.0", "emotion": "^11.0.0", + "es-toolkit": "^1.45.1", "geolib": "^3.3.4", "i18next": "^25.8.13", "i18next-browser-languagedetector": "^8.2.1", @@ -37,7 +38,6 @@ "linkify-html": "^4.3.2", "linkify-react": "^4.3.2", "linkifyjs": "^4.3.2", - "lodash": "^4.17.23", "metar-taf-parser": "^9.1.2", "react": "^19.2.4", "react-dom": "^19.2.4", @@ -109,7 +109,6 @@ "@types/chroma-js": "^3.1.2", "@types/geojson": "^7946.0.16", "@types/leaflet": "^1.9.21", - "@types/lodash": "^4.17.24", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@types/react-page-visibility": "^6.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4c8d1d9..6b28c84 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -77,6 +77,9 @@ importers: emotion: specifier: ^11.0.0 version: 11.0.0 + es-toolkit: + specifier: ^1.45.1 + version: 1.45.1 geolib: specifier: ^3.3.4 version: 3.3.4 @@ -101,9 +104,6 @@ importers: linkifyjs: specifier: ^4.3.2 version: 4.3.2 - lodash: - specifier: ^4.17.23 - version: 4.17.23 metar-taf-parser: specifier: ^9.1.2 version: 9.1.2 @@ -177,9 +177,6 @@ importers: '@types/leaflet': specifier: ^1.9.21 version: 1.9.21 - '@types/lodash': - specifier: ^4.17.24 - version: 4.17.24 '@types/react': specifier: ^19.2.14 version: 19.2.14 @@ -1239,66 +1236,79 @@ packages: resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.59.0': resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.59.0': resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.59.0': resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.59.0': resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.59.0': resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} cpu: [loong64] os: [linux] + libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.59.0': resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.59.0': resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} cpu: [ppc64] os: [linux] + libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.59.0': resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.59.0': resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.59.0': resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.59.0': resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.59.0': resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openbsd-x64@4.59.0': resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} @@ -1481,9 +1491,6 @@ packages: '@types/leaflet@1.9.21': resolution: {integrity: sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==} - '@types/lodash@4.17.24': - resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} - '@types/node@25.3.3': resolution: {integrity: sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==} @@ -2171,6 +2178,9 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} + es-toolkit@1.45.1: + resolution: {integrity: sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==} + esbuild@0.27.3: resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} engines: {node: '>=18'} @@ -5287,8 +5297,6 @@ snapshots: dependencies: '@types/geojson': 7946.0.16 - '@types/lodash@4.17.24': {} - '@types/node@25.3.3': dependencies: undici-types: 7.18.2 @@ -6073,6 +6081,8 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 + es-toolkit@1.45.1: {} + esbuild@0.27.3: optionalDependencies: '@esbuild/aix-ppc64': 0.27.3 diff --git a/src/features/alerts/JumpActions.tsx b/src/features/alerts/JumpActions.tsx index dd94c2f..0e0ffb4 100644 --- a/src/features/alerts/JumpActions.tsx +++ b/src/features/alerts/JumpActions.tsx @@ -2,7 +2,7 @@ import { css } from "@emotion/react"; import styled from "@emotion/styled"; import { faAngleDown, faAngleUp } from "@fortawesome/pro-light-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import throttle from "lodash/throttle"; +import { throttle } from "es-toolkit"; import { useLayoutEffect, useState } from "react"; const Button = styled(FontAwesomeIcon)<{ disabled: boolean }>` diff --git a/src/features/alerts/gairmet/Description.tsx b/src/features/alerts/gairmet/Description.tsx index 093790d..75c7dfb 100644 --- a/src/features/alerts/gairmet/Description.tsx +++ b/src/features/alerts/gairmet/Description.tsx @@ -2,12 +2,11 @@ import { GAirmetFeature } from "../../../services/aviationWeather"; import { capitalizeFirstLetter } from "../../../helpers/string"; import { formatSeverity } from "../../../helpers/aviationAlerts"; import React from "react"; -import omit from "lodash/omit"; +import { isEqual, omit } from "es-toolkit"; import styled from "@emotion/styled"; import { getAlertEnd, getAlertStart } from "../alertsSlice"; import { useAppSelector } from "../../../hooks"; import { timeZoneSelector } from "../../weather/weatherSlice"; -import isEqual from "lodash/isEqual"; import { format } from "date-fns"; import { TZDate } from "@date-fns/tz"; @@ -114,7 +113,7 @@ export default function Description({ alerts }: DescriptionProps) { function hasSimilarData(a: GAirmetFeature, b: GAirmetFeature): boolean { const simplify = (alert: GAirmetFeature) => - omit(alert.properties, "issueTime", "forecast", "validTime"); + omit(alert.properties, ["issueTime", "forecast", "validTime"]); return isEqual(simplify(a), simplify(b)); } diff --git a/src/features/outlook/OutlookTable.tsx b/src/features/outlook/OutlookTable.tsx index 1d9a67e..3d66c1e 100644 --- a/src/features/outlook/OutlookTable.tsx +++ b/src/features/outlook/OutlookTable.tsx @@ -4,7 +4,7 @@ import { timeZoneSelector, Weather } from "../weather/weatherSlice"; import { OpenMeteoWeather } from "../../services/openMeteo"; import { useMemo } from "react"; import OutlookRow from "./OutlookRow"; -import compact from "lodash/fp/compact"; +import { compact } from "es-toolkit"; import styled from "@emotion/styled"; import Day from "./Day"; import { TZDate } from "@date-fns/tz"; diff --git a/src/features/rap/Hours.tsx b/src/features/rap/Hours.tsx index 21cb28d..1b54e8c 100644 --- a/src/features/rap/Hours.tsx +++ b/src/features/rap/Hours.tsx @@ -6,7 +6,7 @@ import ReportWatchdog from "./ReportWatchdog"; import Nav from "./Nav"; import roundedScrollbar from "./roundedScrollbar"; import { css } from "@emotion/react"; -import throttle from "lodash/throttle"; +import { throttle } from "es-toolkit"; import ReportElevationDiscrepancy, { ELEVATION_DISCREPANCY_THRESHOLD, } from "./warnings/ReportElevationDiscrepancy"; diff --git a/src/features/rap/extra/reportMetadata/RefreshInformation.tsx b/src/features/rap/extra/reportMetadata/RefreshInformation.tsx index 4772b46..826529a 100644 --- a/src/features/rap/extra/reportMetadata/RefreshInformation.tsx +++ b/src/features/rap/extra/reportMetadata/RefreshInformation.tsx @@ -2,7 +2,7 @@ import { differenceInMinutes, formatDistanceToNow } from "date-fns"; import { useAppSelector } from "../../../../hooks"; import styled from "@emotion/styled"; import { DataListItem } from "../../../../DataList"; -import capitalize from "lodash/capitalize"; +import { capitalize } from "es-toolkit"; const Label = styled.div``; diff --git a/src/features/weather/header/NWSWeather.tsx b/src/features/weather/header/NWSWeather.tsx index d3afc32..f34d165 100644 --- a/src/features/weather/header/NWSWeather.tsx +++ b/src/features/weather/header/NWSWeather.tsx @@ -1,5 +1,4 @@ -import lowerCase from "lodash/lowerCase"; -import capitalize from "lodash/capitalize"; +import { capitalize, compact, lowerCase } from "es-toolkit"; import { IconProp } from "@fortawesome/fontawesome-svg-core"; import { faCloudHail, @@ -45,7 +44,9 @@ export default function NWSWeather({ return capitalize( observations .map((observation) => - [observation.coverage, observation.weather].map(lowerCase).join(" "), + compact([observation.coverage, observation.weather]) + .map(lowerCase) + .join(" "), ) .join(", "), ); diff --git a/src/helpers/aviationAlerts.ts b/src/helpers/aviationAlerts.ts index cc3087d..6bdd050 100644 --- a/src/helpers/aviationAlerts.ts +++ b/src/helpers/aviationAlerts.ts @@ -1,4 +1,4 @@ -import sortBy from "lodash/sortBy"; +import { sortBy } from "es-toolkit"; import { isGAirmetAlert, isISigmetAlert, @@ -186,10 +186,9 @@ export function extractIssuedTimestamp( } if (isGAirmetAlert(alert)) { - const initialRelatedAlert = sortBy( - relatedAlerts, - "properties.validTime", - )[0]; + const initialRelatedAlert = sortBy(relatedAlerts, [ + (a) => a.properties.validTime, + ])[0]; return initialRelatedAlert.properties.validTime; } diff --git a/src/helpers/string.ts b/src/helpers/string.ts index abf33ca..8776ed5 100644 --- a/src/helpers/string.ts +++ b/src/helpers/string.ts @@ -1,4 +1,4 @@ -import startCase from "lodash/startCase"; +import { startCase } from "es-toolkit"; export function capitalizeFirstLetter(string: string) { return string.charAt(0).toUpperCase() + string.slice(1); diff --git a/src/helpers/weather.ts b/src/helpers/weather.ts index b7763d5..7e91b4c 100644 --- a/src/helpers/weather.ts +++ b/src/helpers/weather.ts @@ -1,5 +1,5 @@ import { addHours } from "date-fns"; -import sortBy from "lodash/sortBy"; +import { sortBy } from "es-toolkit"; import { Alert, isGAirmetAlert, @@ -43,36 +43,38 @@ export function sortAlerts( const NON_DANGEROUS_OFFSET = 1e15; const GAIRMET_READ_OFFSET = 2e15; - return sortBy(alerts, (alert) => { - const dangerousPrefix = isAlertDangerous(alert) - ? DANGEROUS_OFFSET - : NON_DANGEROUS_OFFSET; + return sortBy(alerts, [ + (alert) => { + const dangerousPrefix = isAlertDangerous(alert) + ? DANGEROUS_OFFSET + : NON_DANGEROUS_OFFSET; + + // If the setting for always marking G-Airmets as read is on, + // push all G-Airmets to the bottom of the list of alerts + if (gAirmetRead === OnOff.On && isGAirmetAlert(alert)) + return ( + -new Date( + extractIssuedTimestamp(alert, findRelatedAlerts(alert, allAlerts)), + ).getTime() + GAIRMET_READ_OFFSET + ); + + if (isWeatherAlert(alert)) + return -new Date(alert.properties.onset).getTime() + dangerousPrefix; + + if (isTFRAlert(alert)) + return ( + -new Date( + alert.properties.coreNOTAMData.notam.effectiveStart, + ).getTime() + dangerousPrefix + ); - // If the setting for always marking G-Airmets as read is on, - // push all G-Airmets to the bottom of the list of alerts - if (gAirmetRead === OnOff.On && isGAirmetAlert(alert)) return ( -new Date( extractIssuedTimestamp(alert, findRelatedAlerts(alert, allAlerts)), - ).getTime() + GAIRMET_READ_OFFSET - ); - - if (isWeatherAlert(alert)) - return -new Date(alert.properties.onset).getTime() + dangerousPrefix; - - if (isTFRAlert(alert)) - return ( - -new Date( - alert.properties.coreNOTAMData.notam.effectiveStart, ).getTime() + dangerousPrefix ); - - return ( - -new Date( - extractIssuedTimestamp(alert, findRelatedAlerts(alert, allAlerts)), - ).getTime() + dangerousPrefix - ); - }); + }, + ]); } /** diff --git a/src/services/aviationWeather.ts b/src/services/aviationWeather.ts index 5a8f12d..4468ceb 100644 --- a/src/services/aviationWeather.ts +++ b/src/services/aviationWeather.ts @@ -1,6 +1,6 @@ import axios from "axios"; import type { GeometryObject } from "geojson"; -import uniqBy from "lodash/uniqBy"; +import { uniqBy } from "es-toolkit"; export interface TAFReport { raw: string; diff --git a/src/services/faa.ts b/src/services/faa.ts index a2a0812..b727ded 100644 --- a/src/services/faa.ts +++ b/src/services/faa.ts @@ -1,7 +1,6 @@ import axios from "axios"; import type { GeoJsonObject } from "geojson"; -import uniqWith from "lodash/uniqWith"; -import isEqual from "lodash/isEqual"; +import { isEqual, uniqWith } from "es-toolkit"; export interface TFRFeature { properties: { diff --git a/src/services/openMeteo.ts b/src/services/openMeteo.ts index 513f9ae..a3fc6e2 100644 --- a/src/services/openMeteo.ts +++ b/src/services/openMeteo.ts @@ -1,7 +1,7 @@ import axios from "axios"; import { WindsAloftReport } from "../models/WindsAloft"; import { notEmpty } from "../helpers/array"; -import zipObject from "lodash/zipObject"; +import { zipObject } from "es-toolkit"; import * as velitherm from "velitherm"; const FORECAST_DAYS = 7;