From 8699eaca32b63d9159e77de208cb8d7ba94fa32e Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Wed, 1 Nov 2023 10:03:09 +0100 Subject: [PATCH 1/3] First batch of ramda removals --- src/common/Home.tsx | 5 ++-- src/common/ShlinkVersions.tsx | 3 +-- src/container/index.ts | 9 +++++-- src/servers/ServersDropdown.tsx | 5 ++-- src/servers/data/index.ts | 4 +-- src/servers/reducers/selectedServer.ts | 8 +++--- src/servers/reducers/servers.ts | 34 ++++++++++++++------------ src/utils/helpers/version.ts | 5 ++-- 8 files changed, 39 insertions(+), 34 deletions(-) diff --git a/src/common/Home.tsx b/src/common/Home.tsx index bf7b79bd..3388574e 100644 --- a/src/common/Home.tsx +++ b/src/common/Home.tsx @@ -1,6 +1,5 @@ import { faExternalLinkAlt, faPlus } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { isEmpty, values } from 'ramda'; import { useEffect } from 'react'; import { ExternalLink } from 'react-external-link'; import { Link, useNavigate } from 'react-router-dom'; @@ -16,8 +15,8 @@ interface HomeProps { export const Home = ({ servers }: HomeProps) => { const navigate = useNavigate(); - const serversList = values(servers); - const hasServers = !isEmpty(serversList); + const serversList = Object.values(servers); + const hasServers = serversList.length > 0; useEffect(() => { // Try to redirect to the first server marked as auto-connect diff --git a/src/common/ShlinkVersions.tsx b/src/common/ShlinkVersions.tsx index dad141d5..38a3a4aa 100644 --- a/src/common/ShlinkVersions.tsx +++ b/src/common/ShlinkVersions.tsx @@ -1,11 +1,10 @@ -import { pipe } from 'ramda'; import { ExternalLink } from 'react-external-link'; import type { SelectedServer } from '../servers/data'; import { isReachableServer } from '../servers/data'; import { versionToPrintable, versionToSemVer } from '../utils/helpers/version'; const SHLINK_WEB_CLIENT_VERSION = '%_VERSION_%'; -const normalizeVersion = pipe(versionToSemVer(), versionToPrintable); +const normalizeVersion = (version: string) => versionToPrintable(versionToSemVer(version)); export interface ShlinkVersionsProps { selectedServer: SelectedServer; diff --git a/src/container/index.ts b/src/container/index.ts index 994b95f4..75648877 100644 --- a/src/container/index.ts +++ b/src/container/index.ts @@ -1,6 +1,5 @@ import type { IContainer } from 'bottlejs'; import Bottle from 'bottlejs'; -import { pick } from 'ramda'; import { connect as reduxConnect } from 'react-redux'; import { provideServices as provideApiServices } from '../api/services/provideServices'; import { provideServices as provideAppServices } from '../app/services/provideServices'; @@ -18,14 +17,20 @@ export const { container } = bottle; const lazyService = (cont: IContainer, serviceName: string) => (...args: any[]) => (cont[serviceName] as T)(...args) as K; + const mapActionService = (map: LazyActionMap, actionName: string): LazyActionMap => ({ ...map, // Wrap actual action service in a function so that it is lazily created the first time it is called [actionName]: lazyService(container, actionName), }); + +const pickProps = (propsToPick: string[]) => (obj: any) => Object.fromEntries( + propsToPick.map((key) => [key, obj[key]]), +); + const connect: ConnectDecorator = (propsFromState: string[] | null, actionServiceNames: string[] = []) => reduxConnect( - propsFromState ? pick(propsFromState) : null, + propsFromState ? pickProps(propsFromState) : null, actionServiceNames.reduce(mapActionService, {}), ); diff --git a/src/servers/ServersDropdown.tsx b/src/servers/ServersDropdown.tsx index 0265c610..f15a93e6 100644 --- a/src/servers/ServersDropdown.tsx +++ b/src/servers/ServersDropdown.tsx @@ -1,6 +1,5 @@ import { faPlus as plusIcon, faServer as serverIcon } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { isEmpty, values } from 'ramda'; import { Link } from 'react-router-dom'; import { DropdownItem, DropdownMenu, DropdownToggle, UncontrolledDropdown } from 'reactstrap'; import type { SelectedServer, ServersMap } from './data'; @@ -12,10 +11,10 @@ export interface ServersDropdownProps { } export const ServersDropdown = ({ servers, selectedServer }: ServersDropdownProps) => { - const serversList = values(servers); + const serversList = Object.values(servers); const renderServers = () => { - if (isEmpty(serversList)) { + if (serversList.length === 0) { return ( Add a server diff --git a/src/servers/data/index.ts b/src/servers/data/index.ts index 52a98a6f..35b0e5e6 100644 --- a/src/servers/data/index.ts +++ b/src/servers/data/index.ts @@ -1,4 +1,3 @@ -import { omit } from 'ramda'; import type { SemVer } from '../../utils/helpers/version'; export interface ServerData { @@ -45,5 +44,4 @@ export const isNotFoundServer = (server: SelectedServer): server is NotFoundServ export const getServerId = (server: SelectedServer) => (isServerWithId(server) ? server.id : ''); -export const serverWithIdToServerData = (server: ServerWithId): ServerData => - omit(['id', 'autoConnect'], server); +export const serverWithIdToServerData = ({ id, autoConnect, ...server }: ServerWithId): ServerData => server; diff --git a/src/servers/reducers/selectedServer.ts b/src/servers/reducers/selectedServer.ts index e67a8c0d..5e271a14 100644 --- a/src/servers/reducers/selectedServer.ts +++ b/src/servers/reducers/selectedServer.ts @@ -1,6 +1,6 @@ import { createAction, createSlice } from '@reduxjs/toolkit'; import type { ShlinkHealth } from '@shlinkio/shlink-web-component/api-contract'; -import { memoizeWith, pipe } from 'ramda'; +import { memoizeWith } from 'ramda'; import type { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder'; import { createAsyncThunk } from '../../utils/helpers/redux'; import { versionToPrintable, versionToSemVer as toSemVer } from '../../utils/helpers/version'; @@ -12,9 +12,9 @@ export const MIN_FALLBACK_VERSION = '1.0.0'; export const MAX_FALLBACK_VERSION = '999.999.999'; export const LATEST_VERSION_CONSTRAINT = 'latest'; -const versionToSemVer = pipe( - (version: string) => (version === LATEST_VERSION_CONSTRAINT ? MAX_FALLBACK_VERSION : version), - toSemVer(MIN_FALLBACK_VERSION), +const versionToSemVer = (version: string) => toSemVer( + version === LATEST_VERSION_CONSTRAINT ? MAX_FALLBACK_VERSION : version, + MIN_FALLBACK_VERSION, ); const getServerVersion = memoizeWith( diff --git a/src/servers/reducers/servers.ts b/src/servers/reducers/servers.ts index 2402dc46..c3db31fb 100644 --- a/src/servers/reducers/servers.ts +++ b/src/servers/reducers/servers.ts @@ -1,6 +1,5 @@ import type { PayloadAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; -import { assoc, dissoc, fromPairs, map, pipe, reduce, toPairs } from 'ramda'; import { v4 as uuid } from 'uuid'; import type { ServerData, ServersMap, ServerWithId } from '../data'; @@ -17,14 +16,17 @@ interface SetAutoConnect { const initialState: ServersMap = {}; const serverWithId = (server: ServerWithId | ServerData): ServerWithId => { - if ((server as ServerWithId).id) { - return server as ServerWithId; + if ('id' in server) { + return server; } - return assoc('id', uuid(), server); + return { ...server, id: uuid() }; }; -const serversListToMap = reduce((acc, server) => assoc(server.id, server, acc), {}); +const serversListToMap = (servers: ServerWithId[]): ServersMap => servers.reduce( + (acc, server) => ({ ...acc, [server.id]: server }), + {}, +); export const { actions, reducer } = createSlice({ name: 'shlink/servers', @@ -37,11 +39,14 @@ export const { actions, reducer } = createSlice({ reducer: (state, { payload }: PayloadAction) => { const { serverId, serverData } = payload; return ( - !state[serverId] ? state : assoc(serverId, { ...state[serverId], ...serverData }, state) + !state[serverId] ? state : { ...state, [serverId]: { ...state[serverId], ...serverData } } ); }, }, - deleteServer: (state, { payload }) => dissoc(payload.id, state), + deleteServer: (state, { payload }) => { + const { [payload.id]: deletedServer, ...rest } = state; + return rest; + }, setAutoConnect: { prepare: ({ id: serverId }: ServerWithId, autoConnect: boolean) => ({ payload: { serverId, autoConnect }, @@ -53,11 +58,11 @@ export const { actions, reducer } = createSlice({ } if (!autoConnect) { - return assoc(serverId, { ...state[serverId], autoConnect }, state); + return { ...state, [serverId]: { ...state[serverId], autoConnect } }; } - return fromPairs( - toPairs(state).map(([evaluatedServerId, server]) => [ + return Object.fromEntries( + Object.entries(state).map(([evaluatedServerId, server]) => [ evaluatedServerId, { ...server, autoConnect: evaluatedServerId === serverId }, ]), @@ -65,11 +70,10 @@ export const { actions, reducer } = createSlice({ }, }, createServers: { - prepare: pipe( - map(serverWithId), - serversListToMap, - (payload: ServersMap) => ({ payload }), - ), + prepare: (servers: ServerData[]) => { + const payload = serversListToMap(servers.map(serverWithId)); + return { payload }; + }, reducer: (state, { payload: newServers }: PayloadAction) => ({ ...state, ...newServers }), }, }, diff --git a/src/utils/helpers/version.ts b/src/utils/helpers/version.ts index 1d5d708d..cda5bbe0 100644 --- a/src/utils/helpers/version.ts +++ b/src/utils/helpers/version.ts @@ -39,5 +39,6 @@ const versionIsValidSemVer = memoizeWith(identity, (version: string): version is export const versionToPrintable = (version: string) => (!versionIsValidSemVer(version) ? version : `v${version}`); -export const versionToSemVer = (defaultValue: SemVer = 'latest') => - (version: string): SemVer => (versionIsValidSemVer(version) ? version : defaultValue); +export const versionToSemVer = (version: string, fallback: SemVer = 'latest'): SemVer => ( + versionIsValidSemVer(version) ? version : fallback +); From 7ba78fd91930149a2e35f11853ae04712fa73b7c Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Wed, 1 Nov 2023 10:07:50 +0100 Subject: [PATCH 2/3] Remove more ovbious ramda helper usages --- src/servers/helpers/ImportServersBtn.tsx | 9 ++++----- src/servers/services/ServersExporter.ts | 3 +-- src/servers/services/provideServices.ts | 3 +-- src/utils/helpers/redux.ts | 3 +-- src/utils/helpers/version.ts | 12 +++++++++--- src/utils/utils.ts | 14 +++++--------- test/servers/ServersDropdown.test.tsx | 3 +-- test/servers/reducers/servers.test.ts | 7 +++---- 8 files changed, 25 insertions(+), 29 deletions(-) diff --git a/src/servers/helpers/ImportServersBtn.tsx b/src/servers/helpers/ImportServersBtn.tsx index d93688df..44697be9 100644 --- a/src/servers/helpers/ImportServersBtn.tsx +++ b/src/servers/helpers/ImportServersBtn.tsx @@ -1,7 +1,6 @@ import { faFileUpload as importIcon } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { useElementRef, useToggle } from '@shlinkio/shlink-frontend-kit'; -import { complement } from 'ramda'; import type { ChangeEvent, PropsWithChildren } from 'react'; import { useCallback, useRef, useState } from 'react'; import { Button, UncontrolledTooltip } from 'reactstrap'; @@ -27,8 +26,8 @@ type ImportServersBtnDeps = { ServersImporter: ServersImporter }; -const serversFiltering = (servers: ServerData[]) => - ({ url, apiKey }: ServerData) => servers.some((server) => server.url === url && server.apiKey === apiKey); +const serversInclude = (servers: ServerData[], { url, apiKey }: ServerData) => + servers.some((server) => server.url === url && server.apiKey === apiKey); const ImportServersBtn: FCWithDeps = ({ createServers, @@ -56,7 +55,7 @@ const ImportServersBtn: FCWithDeps serversInclude(existingServers, server)); const hasDuplicatedServers = !!dupServers.length; !hasDuplicatedServers ? create(newServers) : setDuplicatedServers(dupServers); @@ -75,7 +74,7 @@ const ImportServersBtn: FCWithDeps { - create(serversToCreate.current.filter(complement(serversFiltering(duplicatedServers)))); + create(serversToCreate.current.filter((server) => !serversInclude(duplicatedServers, server))); hideModal(); }, [create, duplicatedServers, hideModal]); diff --git a/src/servers/services/ServersExporter.ts b/src/servers/services/ServersExporter.ts index c003869b..9d2fabae 100644 --- a/src/servers/services/ServersExporter.ts +++ b/src/servers/services/ServersExporter.ts @@ -1,4 +1,3 @@ -import { values } from 'ramda'; import type { JsonToCsv } from '../../utils/helpers/csvjson'; import { saveCsv } from '../../utils/helpers/files'; import type { LocalStorage } from '../../utils/services/LocalStorage'; @@ -15,7 +14,7 @@ export class ServersExporter { ) {} public readonly exportServers = async () => { - const servers = values(this.storage.get('servers') ?? {}).map(serverWithIdToServerData); + const servers = Object.values(this.storage.get('servers') ?? {}).map(serverWithIdToServerData); try { const csv = this.jsonToCsv(servers); diff --git a/src/servers/services/provideServices.ts b/src/servers/services/provideServices.ts index 8c332d55..4a2d1ab3 100644 --- a/src/servers/services/provideServices.ts +++ b/src/servers/services/provideServices.ts @@ -1,5 +1,4 @@ import type Bottle from 'bottlejs'; -import { prop } from 'ramda'; import type { ConnectDecorator } from '../../container/types'; import { CreateServerFactory } from '../CreateServer'; import { DeleteServerButtonFactory } from '../DeleteServerButton'; @@ -70,5 +69,5 @@ export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => { // Reducers bottle.serviceFactory('selectedServerReducerCreator', selectedServerReducerCreator, 'selectServer'); - bottle.serviceFactory('selectedServerReducer', prop('reducer'), 'selectedServerReducerCreator'); + bottle.serviceFactory('selectedServerReducer', (obj) => obj.reducer, 'selectedServerReducerCreator'); }; diff --git a/src/utils/helpers/redux.ts b/src/utils/helpers/redux.ts index e113c937..b6dbcbe1 100644 --- a/src/utils/helpers/redux.ts +++ b/src/utils/helpers/redux.ts @@ -1,6 +1,5 @@ import type { AsyncThunkPayloadCreator } from '@reduxjs/toolkit'; import { createAsyncThunk as baseCreateAsyncThunk } from '@reduxjs/toolkit'; -import { identity } from 'ramda'; import type { ShlinkState } from '../../container/types'; export const createAsyncThunk = ( @@ -9,5 +8,5 @@ export const createAsyncThunk = ( ) => baseCreateAsyncThunk( typePrefix, payloadCreator, - { serializeError: identity }, + { serializeError: (e) => e }, ); diff --git a/src/utils/helpers/version.ts b/src/utils/helpers/version.ts index cda5bbe0..5b03abfc 100644 --- a/src/utils/helpers/version.ts +++ b/src/utils/helpers/version.ts @@ -1,9 +1,15 @@ import { compare } from 'compare-versions'; -import { identity, isEmpty, isNil, memoizeWith } from 'ramda'; +import { memoizeWith } from 'ramda'; export type Empty = null | undefined | '' | never[]; -const hasValue = (value: T | Empty): value is T => !isNil(value) && !isEmpty(value); +const isEmpty = (value: Exclude): boolean => ( + (Array.isArray(value) && value.length === 0) + || (typeof value === 'string' && value === '') + || (typeof value === 'object' && Object.keys(value).length === 0) +); + +export const hasValue = (value: T | Empty): value is T => value !== undefined && value !== null && !isEmpty(value); type SemVerPatternFragment = `${bigint | '*'}`; @@ -29,7 +35,7 @@ export const versionMatch = (versionToMatch: SemVer | Empty, { maxVersion, minVe return matchesMaxVersion && matchesMinVersion; }; -const versionIsValidSemVer = memoizeWith(identity, (version: string): version is SemVer => { +const versionIsValidSemVer = memoizeWith((v) => v, (version: string): version is SemVer => { try { return compare(version, version, '='); } catch (e) { diff --git a/src/utils/utils.ts b/src/utils/utils.ts index badf0ba0..75618c5b 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,14 +1,10 @@ -import { pipe, range } from 'ramda'; +import { range } from 'ramda'; import type { SyntheticEvent } from 'react'; -type Optional = T | null | undefined; - -export type OptionalString = Optional; - -export const handleEventPreventingDefault = (handler: () => T) => pipe( - (e: SyntheticEvent) => e.preventDefault(), - handler, -); +export const handleEventPreventingDefault = (handler: () => T) => (e: SyntheticEvent) => { + e.preventDefault(); + handler(); +}; export const rangeOf = (size: number, mappingFn: (value: number) => T, startAt = 1): T[] => range(startAt, size + 1).map(mappingFn); diff --git a/test/servers/ServersDropdown.test.tsx b/test/servers/ServersDropdown.test.tsx index c1a2e548..c8e051c6 100644 --- a/test/servers/ServersDropdown.test.tsx +++ b/test/servers/ServersDropdown.test.tsx @@ -1,6 +1,5 @@ import { screen } from '@testing-library/react'; import { fromPartial } from '@total-typescript/shoehorn'; -import { values } from 'ramda'; import { MemoryRouter } from 'react-router-dom'; import type { ServersMap } from '../../src/servers/data'; import { ServersDropdown } from '../../src/servers/ServersDropdown'; @@ -28,7 +27,7 @@ describe('', () => { await user.click(screen.getByText('Servers')); const items = screen.getAllByRole('menuitem'); - expect(items).toHaveLength(values(fallbackServers).length + 1); + expect(items).toHaveLength(Object.values(fallbackServers).length + 1); expect(items[0]).toHaveTextContent('foo'); expect(items[1]).toHaveTextContent('bar'); expect(items[2]).toHaveTextContent('baz'); diff --git a/test/servers/reducers/servers.test.ts b/test/servers/reducers/servers.test.ts index e88c24bc..5e6652bc 100644 --- a/test/servers/reducers/servers.test.ts +++ b/test/servers/reducers/servers.test.ts @@ -1,5 +1,4 @@ import { fromPartial } from '@total-typescript/shoehorn'; -import { dissoc, values } from 'ramda'; import type { RegularServer, ServersMap, ServerWithId } from '../../../src/servers/data'; import { createServers, @@ -101,17 +100,17 @@ describe('serversReducer', () => { describe('createServers', () => { it('returns expected action', () => { - const newServers = values(list); + const newServers = Object.values(list); const { payload } = createServers(newServers); expect(payload).toEqual(list); }); it('generates an id for every provided server if they do not have it', () => { - const servers = values(list).map(dissoc('id')); + const servers = Object.values(list).map(({ id, ...rest }) => rest); const { payload } = createServers(servers); - expect(values(payload).every(({ id }) => !!id)).toEqual(true); + expect(Object.values(payload).every(({ id }) => !!id)).toEqual(true); }); }); From 8ad1e7e0d9482902aad631c69c121f29bf5f72ab Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Wed, 1 Nov 2023 16:19:51 +0100 Subject: [PATCH 3/3] Replace remaining ramda references with shlinkio/data-manipulation --- .github/dependabot.yml | 3 ++ package-lock.json | 40 +++++++------------------- package.json | 3 +- src/servers/reducers/selectedServer.ts | 2 +- src/settings/reducers/settings.ts | 2 +- src/utils/helpers/version.ts | 2 +- src/utils/utils.ts | 4 --- 7 files changed, 18 insertions(+), 38 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index fbd03a63..38625a8a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,6 +12,9 @@ updates: fontawesome: patterns: - '@fortawesome*' + shlink: + patterns: + - '@shlinkio*' ignore: # Bootstrap can introduce visual breaking changes on styles # Ignore it, since the plan is to remove it anyway diff --git a/package-lock.json b/package-lock.json index 4bb3de60..394dc6bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@fortawesome/react-fontawesome": "^0.2.0", "@json2csv/plainjs": "^7.0.3", "@reduxjs/toolkit": "^1.9.7", + "@shlinkio/data-manipulation": "^1.0.1", "@shlinkio/shlink-frontend-kit": "^0.4.0", "@shlinkio/shlink-js-sdk": "^0.1.0", "@shlinkio/shlink-web-component": "^0.3.5", @@ -24,7 +25,6 @@ "compare-versions": "^6.1.0", "csvtojson": "^2.0.10", "date-fns": "^2.30.0", - "ramda": "^0.27.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-external-link": "^2.2.0", @@ -46,7 +46,6 @@ "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.5.1", "@total-typescript/shoehorn": "^0.1.1", - "@types/ramda": "^0.27.66", "@types/react": "^18.2.33", "@types/react-dom": "^18.2.14", "@types/uuid": "^9.0.6", @@ -2790,6 +2789,11 @@ "node": ">=14.0.0" } }, + "node_modules/@shlinkio/data-manipulation": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@shlinkio/data-manipulation/-/data-manipulation-1.0.1.tgz", + "integrity": "sha512-LYmRXcI2CPi8RZfI5wE7lf0Bg74NiPK+y2wuePvRlQtQud2jDZTbCmCIe6fyQV4NGrpKTn+D4NjcmVT/Iq2InQ==" + }, "node_modules/@shlinkio/eslint-config-js-coding-standard": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@shlinkio/eslint-config-js-coding-standard/-/eslint-config-js-coding-standard-2.3.0.tgz", @@ -3272,15 +3276,6 @@ "version": "15.7.3", "license": "MIT" }, - "node_modules/@types/ramda": { - "version": "0.27.66", - "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.27.66.tgz", - "integrity": "sha512-i2YW+E2U6NfMt3dp0RxNcejox+bxJUNDjB7BpYuRuoHIzv5juPHkJkNgcUOu+YSQEmaWu8cnAo/8r63C0NnuVA==", - "dev": true, - "dependencies": { - "ts-toolbelt": "^6.15.1" - } - }, "node_modules/@types/react": { "version": "18.2.33", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.33.tgz", @@ -9622,11 +9617,6 @@ "typescript": ">=4.2.0" } }, - "node_modules/ts-toolbelt": { - "version": "6.15.5", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/tsconfig-paths": { "version": "3.14.1", "dev": true, @@ -12507,6 +12497,11 @@ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.10.0.tgz", "integrity": "sha512-Lm+fYpMfZoEucJ7cMxgt4dYt8jLfbpwRCzAjm9UgSLOkmlqo9gupxt6YX3DY0Fk155NT9l17d/ydi+964uS9Lw==" }, + "@shlinkio/data-manipulation": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@shlinkio/data-manipulation/-/data-manipulation-1.0.1.tgz", + "integrity": "sha512-LYmRXcI2CPi8RZfI5wE7lf0Bg74NiPK+y2wuePvRlQtQud2jDZTbCmCIe6fyQV4NGrpKTn+D4NjcmVT/Iq2InQ==" + }, "@shlinkio/eslint-config-js-coding-standard": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@shlinkio/eslint-config-js-coding-standard/-/eslint-config-js-coding-standard-2.3.0.tgz", @@ -12853,15 +12848,6 @@ "@types/prop-types": { "version": "15.7.3" }, - "@types/ramda": { - "version": "0.27.66", - "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.27.66.tgz", - "integrity": "sha512-i2YW+E2U6NfMt3dp0RxNcejox+bxJUNDjB7BpYuRuoHIzv5juPHkJkNgcUOu+YSQEmaWu8cnAo/8r63C0NnuVA==", - "dev": true, - "requires": { - "ts-toolbelt": "^6.15.1" - } - }, "@types/react": { "version": "18.2.33", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.33.tgz", @@ -17115,10 +17101,6 @@ "dev": true, "requires": {} }, - "ts-toolbelt": { - "version": "6.15.5", - "dev": true - }, "tsconfig-paths": { "version": "3.14.1", "dev": true, diff --git a/package.json b/package.json index d5257562..7c2a21d0 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@fortawesome/react-fontawesome": "^0.2.0", "@json2csv/plainjs": "^7.0.3", "@reduxjs/toolkit": "^1.9.7", + "@shlinkio/data-manipulation": "^1.0.1", "@shlinkio/shlink-frontend-kit": "^0.4.0", "@shlinkio/shlink-js-sdk": "^0.1.0", "@shlinkio/shlink-web-component": "^0.3.5", @@ -40,7 +41,6 @@ "compare-versions": "^6.1.0", "csvtojson": "^2.0.10", "date-fns": "^2.30.0", - "ramda": "^0.27.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-external-link": "^2.2.0", @@ -62,7 +62,6 @@ "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.5.1", "@total-typescript/shoehorn": "^0.1.1", - "@types/ramda": "^0.27.66", "@types/react": "^18.2.33", "@types/react-dom": "^18.2.14", "@types/uuid": "^9.0.6", diff --git a/src/servers/reducers/selectedServer.ts b/src/servers/reducers/selectedServer.ts index 5e271a14..17d50429 100644 --- a/src/servers/reducers/selectedServer.ts +++ b/src/servers/reducers/selectedServer.ts @@ -1,6 +1,6 @@ import { createAction, createSlice } from '@reduxjs/toolkit'; +import { memoizeWith } from '@shlinkio/data-manipulation'; import type { ShlinkHealth } from '@shlinkio/shlink-web-component/api-contract'; -import { memoizeWith } from 'ramda'; import type { ShlinkApiClientBuilder } from '../../api/services/ShlinkApiClientBuilder'; import { createAsyncThunk } from '../../utils/helpers/redux'; import { versionToPrintable, versionToSemVer as toSemVer } from '../../utils/helpers/version'; diff --git a/src/settings/reducers/settings.ts b/src/settings/reducers/settings.ts index 64da272c..8d3143f5 100644 --- a/src/settings/reducers/settings.ts +++ b/src/settings/reducers/settings.ts @@ -1,5 +1,6 @@ import type { PayloadAction, PrepareAction } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit'; +import { mergeDeepRight } from '@shlinkio/data-manipulation'; import type { Theme } from '@shlinkio/shlink-frontend-kit'; import type { Settings, @@ -8,7 +9,6 @@ import type { TagsSettings, VisitsSettings, } from '@shlinkio/shlink-web-component'; -import { mergeDeepRight } from 'ramda'; import type { Defined } from '../../utils/types'; type ShortUrlsOrder = Defined; diff --git a/src/utils/helpers/version.ts b/src/utils/helpers/version.ts index 5b03abfc..3c27cc9f 100644 --- a/src/utils/helpers/version.ts +++ b/src/utils/helpers/version.ts @@ -1,5 +1,5 @@ +import { memoizeWith } from '@shlinkio/data-manipulation'; import { compare } from 'compare-versions'; -import { memoizeWith } from 'ramda'; export type Empty = null | undefined | '' | never[]; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 75618c5b..b1b11ca0 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,10 +1,6 @@ -import { range } from 'ramda'; import type { SyntheticEvent } from 'react'; export const handleEventPreventingDefault = (handler: () => T) => (e: SyntheticEvent) => { e.preventDefault(); handler(); }; - -export const rangeOf = (size: number, mappingFn: (value: number) => T, startAt = 1): T[] => - range(startAt, size + 1).map(mappingFn);