diff --git a/.eslintrc.js b/.eslintrc.js index 0f33f43f4..1444f4aad 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,20 +5,27 @@ module.exports = { extends: [ 'expo', 'plugin:tailwindcss/recommended', - 'prettier' + 'prettier', + 'eslint:recommended' ], + env: { + 'jest/globals': true, + 'node': true + }, plugins: [ 'unicorn', '@typescript-eslint', 'unused-imports', 'tailwindcss', 'simple-import-sort', - 'sonarjs' + 'sonarjs', + 'jest' ], parserOptions: { project: './tsconfig.json', }, rules: { + 'import/no-duplicates': 'error', '@typescript-eslint/no-explicit-any': 'error', 'unicorn/filename-case': [ 'error', @@ -73,6 +80,7 @@ module.exports = { ], 'object-shorthand': 'error', 'arrow-body-style': ["error", "as-needed"], + 'no-console': ['error', {allow: ['error']}], 'guard-for-in': 'error' }, overrides: [ diff --git a/env.js b/env.js index a75c45438..172935095 100644 --- a/env.js +++ b/env.js @@ -220,7 +220,6 @@ if (shouldValidateEnv) { } console.error(...messages); - throw new Error( 'Invalid environment variables, Check terminal for more details ' ); diff --git a/package.json b/package.json index c6641c429..7da4b34a8 100644 --- a/package.json +++ b/package.json @@ -73,8 +73,8 @@ "expo-splash-screen": "0.27.5", "expo-status-bar": "~1.12.1", "expo-system-ui": "~3.0.7", - "i18next": "^23.14.0", "expo-updates": "~0.25.24", + "i18next": "^23.14.0", "lodash.memoize": "^4.1.2", "moti": "^0.29.0", "nativewind": "4.0.36", @@ -120,6 +120,7 @@ "eslint-config-expo": "^7.1.2", "eslint-config-prettier": "^9.1.0", "eslint-plugin-i18n-json": "^4.0.0", + "eslint-plugin-jest": "^28.8.3", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-simple-import-sort": "^10.0.0", "eslint-plugin-sonarjs": "^1.0.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 87e1b81fe..3ec5c6cbc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -198,6 +198,9 @@ importers: eslint-plugin-i18n-json: specifier: ^4.0.0 version: 4.0.0(eslint@8.57.0) + eslint-plugin-jest: + specifier: ^28.8.3 + version: 28.8.3(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(jest@29.7.0(@types/node@22.5.0)(ts-node@10.9.2(@types/node@22.5.0)(typescript@5.3.3)))(typescript@5.3.3) eslint-plugin-prettier: specifier: ^5.2.1 version: 5.2.1(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.3.3) @@ -3417,6 +3420,19 @@ packages: '@typescript-eslint/parser': optional: true + eslint-plugin-jest@28.8.3: + resolution: {integrity: sha512-HIQ3t9hASLKm2IhIOqnu+ifw7uLZkIlR7RYNv7fMcEi/p0CIiJmfriStQS2LDkgtY4nyLbIZAD+JL347Yc2ETQ==} + engines: {node: ^16.10.0 || ^18.12.0 || >=20.0.0} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 + jest: '*' + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + jest: + optional: true + eslint-plugin-prettier@5.2.1: resolution: {integrity: sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==} engines: {node: ^14.18.0 || >=16.0.0} @@ -11867,6 +11883,17 @@ snapshots: - eslint-import-resolver-webpack - supports-color + eslint-plugin-jest@28.8.3(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(jest@29.7.0(@types/node@22.5.0)(ts-node@10.9.2(@types/node@22.5.0)(typescript@5.3.3)))(typescript@5.3.3): + dependencies: + '@typescript-eslint/utils': 7.18.0(eslint@8.57.0)(typescript@5.3.3) + eslint: 8.57.0 + optionalDependencies: + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.3.3))(eslint@8.57.0)(typescript@5.3.3) + jest: 29.7.0(@types/node@22.5.0)(ts-node@10.9.2(@types/node@22.5.0)(typescript@5.3.3)) + transitivePeerDependencies: + - supports-color + - typescript + eslint-plugin-prettier@5.2.1(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.3.3): dependencies: eslint: 8.57.0 diff --git a/src/api/common/api-provider.tsx b/src/api/common/api-provider.tsx index 1f86b8c6c..b02d39492 100644 --- a/src/api/common/api-provider.tsx +++ b/src/api/common/api-provider.tsx @@ -1,8 +1,9 @@ import { useReactQueryDevTools } from '@dev-plugins/react-query'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { type ReactNode } from 'react'; export const queryClient = new QueryClient(); -export function APIProvider({ children }: { children: React.ReactNode }) { +export function APIProvider({ children }: Readonly<{ children: ReactNode }>) { useReactQueryDevTools(queryClient); return ( // Provide the client to your App diff --git a/src/api/common/utils.tsx b/src/api/common/utils.tsx index efc5bd3fd..7fa8652c8 100644 --- a/src/api/common/utils.tsx +++ b/src/api/common/utils.tsx @@ -51,7 +51,7 @@ type GenericObject = { [key: string]: unknown }; export const toCamelCase = (obj: GenericObject): GenericObject => { const newObj: GenericObject = {}; for (const key in obj) { - if (obj.hasOwnProperty(key)) { + if (Object.hasOwn(obj,key)) { if (key.includes('_')) { const newKey = key.replace(/_([a-z])/g, (g) => g[1].toUpperCase()); newObj[newKey] = obj[key]; @@ -66,7 +66,7 @@ export const toCamelCase = (obj: GenericObject): GenericObject => { export const toSnakeCase = (obj: GenericObject): GenericObject => { const newObj: GenericObject = {}; for (const key in obj) { - if (obj.hasOwnProperty(key)) { + if (Object.hasOwn(obj,key)) { let newKey = key.match(/([A-Z])/g) ? key .match(/([A-Z])/g)! diff --git a/src/app/_layout.tsx b/src/app/_layout.tsx index 25177696f..577edcc5a 100644 --- a/src/app/_layout.tsx +++ b/src/app/_layout.tsx @@ -4,6 +4,7 @@ import '../../global.css'; import { BottomSheetModalProvider } from '@gorhom/bottom-sheet'; import { ThemeProvider } from '@react-navigation/native'; import { SplashScreen, Stack } from 'expo-router'; +import React from 'react'; import { StyleSheet } from 'react-native'; import FlashMessage from 'react-native-flash-message'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; diff --git a/src/app/feed/add-post.tsx b/src/app/feed/add-post.tsx index 4ff5fbda6..aa2021d7b 100644 --- a/src/app/feed/add-post.tsx +++ b/src/app/feed/add-post.tsx @@ -21,7 +21,6 @@ export default function AddPost() { const { mutate: addPost, isPending } = useAddPost(); const onSubmit = (data: FormType) => { - console.log(data); addPost( { ...data, userId: 1 }, { diff --git a/src/app/login.tsx b/src/app/login.tsx index 4557a03fb..28a51d0eb 100644 --- a/src/app/login.tsx +++ b/src/app/login.tsx @@ -10,6 +10,7 @@ export default function Login() { const signIn = useAuth.use.signIn(); const onSubmit: LoginFormProps['onSubmit'] = (data) => { + // eslint-disable-next-line no-console console.log(data); signIn({ access: 'access-token', refresh: 'refresh-token' }); router.push('/'); diff --git a/src/app/onboarding.tsx b/src/app/onboarding.tsx index f38ba74b8..1de43a148 100644 --- a/src/app/onboarding.tsx +++ b/src/app/onboarding.tsx @@ -4,7 +4,7 @@ import { Cover } from '@/components/cover'; import { useIsFirstTime } from '@/core/hooks'; import { Button, FocusAwareStatusBar, SafeAreaView, Text, View } from '@/ui'; export default function Onboarding() { - const [_, setIsFirstTime] = useIsFirstTime(); + const [, setIsFirstTime] = useIsFirstTime(); const router = useRouter(); return ( diff --git a/src/components/inputs.tsx b/src/components/inputs.tsx index f49116460..53ebb9acb 100644 --- a/src/components/inputs.tsx +++ b/src/components/inputs.tsx @@ -1,8 +1,7 @@ import { useState } from 'react'; import type { OptionType } from '@/ui'; -import { Input, Select, View } from '@/ui'; -import { Checkbox, Radio, Switch } from '@/ui'; +import { Checkbox, Input, Radio, Select, Switch, View } from '@/ui'; import { Title } from './title'; diff --git a/src/components/settings/item.tsx b/src/components/settings/item.tsx index f8cf35b70..71407741a 100644 --- a/src/components/settings/item.tsx +++ b/src/components/settings/item.tsx @@ -1,3 +1,5 @@ +import React from 'react'; + import type { TxKeyPath } from '@/core'; import { Pressable, Text, View } from '@/ui'; import { ArrowRight } from '@/ui/icons'; diff --git a/src/components/settings/items-container.tsx b/src/components/settings/items-container.tsx index 8f0fcf1ef..4ece303a2 100644 --- a/src/components/settings/items-container.tsx +++ b/src/components/settings/items-container.tsx @@ -1,3 +1,5 @@ +import React from 'react'; + import type { TxKeyPath } from '@/core'; import { Text, View } from '@/ui'; diff --git a/src/core/auth/index.tsx b/src/core/auth/index.tsx index 0e0d426da..63347c4ae 100644 --- a/src/core/auth/index.tsx +++ b/src/core/auth/index.tsx @@ -1,8 +1,7 @@ import { create } from 'zustand'; import { createSelectors } from '../utils'; -import type { TokenType } from './utils'; -import { getToken, removeToken, setToken } from './utils'; +import { getToken, removeToken, setToken, type TokenType } from './utils'; interface AuthState { token: TokenType | null; @@ -31,7 +30,7 @@ const _useAuth = create((set, get) => ({ } else { get().signOut(); } - } catch (e) { + } catch (_) { // catch error here // Maybe sign_out user! } diff --git a/src/core/hooks/use-selected-theme.tsx b/src/core/hooks/use-selected-theme.tsx index a5bc1f6ff..b370d4b2a 100644 --- a/src/core/hooks/use-selected-theme.tsx +++ b/src/core/hooks/use-selected-theme.tsx @@ -14,7 +14,7 @@ export type ColorSchemeType = 'light' | 'dark' | 'system'; * */ export const useSelectedTheme = () => { - const { colorScheme: _color, setColorScheme } = useColorScheme(); + const { setColorScheme } = useColorScheme(); const [theme, _setTheme] = useMMKVString(SELECTED_THEME, storage); const setSelectedTheme = useCallback( @@ -32,7 +32,6 @@ export const useSelectedTheme = () => { export const loadSelectedTheme = () => { const theme = storage.getString(SELECTED_THEME); if (theme !== undefined) { - console.log('theme', theme); colorScheme.set(theme as ColorSchemeType); } }; diff --git a/src/core/test-utils.tsx b/src/core/test-utils.tsx index 65d9b006a..0f1330348 100644 --- a/src/core/test-utils.tsx +++ b/src/core/test-utils.tsx @@ -5,6 +5,7 @@ import { NavigationContainer } from '@react-navigation/native'; import type { RenderOptions } from '@testing-library/react-native'; import { render } from '@testing-library/react-native'; import type { ReactElement } from 'react'; +import React from 'react'; const createAppWrapper = () => ({ children }: { children: React.ReactNode }) => ( {children} diff --git a/src/ui/focus-aware-status-bar.tsx b/src/ui/focus-aware-status-bar.tsx index f7ad844a4..315d6ee99 100644 --- a/src/ui/focus-aware-status-bar.tsx +++ b/src/ui/focus-aware-status-bar.tsx @@ -1,5 +1,6 @@ import { useIsFocused } from '@react-navigation/native'; import { useColorScheme } from 'nativewind'; +import React from 'react'; import { StatusBar } from 'react-native'; type Props = React.ComponentProps; diff --git a/src/ui/select.tsx b/src/ui/select.tsx index f6b8c3b81..123443acd 100644 --- a/src/ui/select.tsx +++ b/src/ui/select.tsx @@ -18,8 +18,7 @@ import colors from '@/ui/colors'; import { CaretDown } from '@/ui/icons'; import type { InputControllerType } from './input'; -import { useModal } from './modal'; -import { Modal } from './modal'; +import { Modal, useModal } from './modal'; import { Text } from './text'; const selectTv = tv({ diff --git a/src/ui/utils.tsx b/src/ui/utils.tsx index 9a189234f..452e15487 100644 --- a/src/ui/utils.tsx +++ b/src/ui/utils.tsx @@ -10,7 +10,6 @@ export const HEIGHT = height; // for onError react queries and mutations export const showError = (error: AxiosError) => { - console.log(JSON.stringify(error?.response?.data)); const description = extractError(error?.response?.data).trimEnd(); showMessage({