use stricter eslint rules

This commit is contained in:
Athou
2024-02-19 20:32:20 +01:00
parent cb1a00c5cd
commit 0d7300c192
17 changed files with 125 additions and 837 deletions

View File

@@ -1,28 +1,36 @@
module.exports = { module.exports = {
env: { env: {
browser: true, browser: true,
es2021: true es2021: true,
}, },
extends: ["standard-with-typescript", "plugin:react/recommended", "plugin:react-hooks/recommended", "plugin:prettier/recommended"], extends: [
"eslint:recommended",
"plugin:@typescript-eslint/strict-type-checked",
"plugin:@typescript-eslint/stylistic-type-checked",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:prettier/recommended",
],
settings: { settings: {
react: { react: {
version: "detect" version: "detect",
} },
}, },
overrides: [ overrides: [
{ {
env: { env: {
node: true node: true,
}, },
files: [".eslintrc.{js,cjs}"], files: [".eslintrc.{js,cjs}"],
parserOptions: { parserOptions: {
sourceType: "script" sourceType: "script",
} },
} },
], ],
parserOptions: { parserOptions: {
project: true,
ecmaVersion: "latest", ecmaVersion: "latest",
sourceType: "module" sourceType: "module",
}, },
plugins: ["react"], plugins: ["react"],
rules: { rules: {
@@ -31,11 +39,13 @@ module.exports = {
"@typescript-eslint/no-confusing-void-expression": ["error", { ignoreArrowShorthand: true }], "@typescript-eslint/no-confusing-void-expression": ["error", { ignoreArrowShorthand: true }],
"@typescript-eslint/no-floating-promises": "off", "@typescript-eslint/no-floating-promises": "off",
"@typescript-eslint/no-misused-promises": "off", "@typescript-eslint/no-misused-promises": "off",
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/prefer-nullish-coalescing": ["error", { ignoreConditionalTests: true }], "@typescript-eslint/prefer-nullish-coalescing": ["error", { ignoreConditionalTests: true }],
"@typescript-eslint/strict-boolean-expressions": "off", "@typescript-eslint/strict-boolean-expressions": "off",
"@typescript-eslint/unbound-method": "off", "@typescript-eslint/unbound-method": "off",
"react/no-unescaped-entities": "off", "react/no-unescaped-entities": "off",
"react/react-in-jsx-scope": "off", "react/react-in-jsx-scope": "off",
"react-hooks/exhaustive-deps": "error" "react-hooks/exhaustive-deps": "error",
} },
} }

File diff suppressed because it is too large Load Diff

View File

@@ -61,12 +61,11 @@
"@types/swagger-ui-react": "^4.18.3", "@types/swagger-ui-react": "^4.18.3",
"@types/throttle-debounce": "^5.0.2", "@types/throttle-debounce": "^5.0.2",
"@types/tinycon": "^0.6.5", "@types/tinycon": "^0.6.5",
"@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/eslint-plugin": "^7.0.2",
"@vitejs/plugin-react": "^4.2.1", "@vitejs/plugin-react": "^4.2.1",
"babel-plugin-macros": "^3.1.0", "babel-plugin-macros": "^3.1.0",
"eslint": "^8.56.0", "eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-config-standard-with-typescript": "^43.0.1",
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.33.2", "eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",

View File

@@ -31,12 +31,14 @@ const axiosInstance = axios.create({ baseURL: "./rest", withCredentials: true })
axiosInstance.interceptors.response.use( axiosInstance.interceptors.response.use(
response => response, response => response,
error => { error => {
const { status, data } = error.response if (axios.isAxiosError(error) && error.response) {
if ( const { status, data } = error.response
(status === 401 && data?.message === "Credentials are required to access this resource.") || if (
(status === 403 && data?.message === "You don't have the required role to access this resource.") (status === 401 && data?.message === "Credentials are required to access this resource.") ||
) { (status === 403 && data?.message === "You don't have the required role to access this resource.")
window.location.hash = data?.allowRegistrations ? "/welcome" : "/login" ) {
window.location.hash = data?.allowRegistrations ? "/welcome" : "/login"
}
} }
throw error throw error
} }
@@ -107,13 +109,11 @@ export const client = {
export const errorToStrings = (err: unknown) => { export const errorToStrings = (err: unknown) => {
let strings: string[] = [] let strings: string[] = []
if (axios.isAxiosError(err)) { if (axios.isAxiosError(err) && err.response) {
if (err.response) { const { data } = err.response
const { data } = err.response if (typeof data === "string") strings.push(data)
if (typeof data === "string") strings.push(data) if (typeof data === "object" && data.message) strings.push(data.message as string)
if (typeof data === "object" && data.message) strings.push(data.message as string) if (typeof data === "object" && data.errors) strings = [...strings, ...data.errors]
if (typeof data === "object" && data.errors) strings = [...strings, ...data.errors]
}
} }
return strings return strings

View File

@@ -1,4 +1,3 @@
/* eslint-disable import/first */
import { configureStore } from "@reduxjs/toolkit" import { configureStore } from "@reduxjs/toolkit"
import { type client } from "app/client" import { type client } from "app/client"
import { loadEntries, loadMoreEntries, markAllEntries, markEntry } from "app/entries/thunks" import { loadEntries, loadMoreEntries, markAllEntries, markEntry } from "app/entries/thunks"
@@ -90,7 +89,7 @@ describe("entries", () => {
expect(store.getState().entries.hasMore).toBe(false) expect(store.getState().entries.hasMore).toBe(false)
}) })
it("marks an entry as read", async () => { it("marks an entry as read", () => {
const store = configureStore({ const store = configureStore({
reducer: reducers, reducer: reducers,
preloadedState: { preloadedState: {
@@ -117,7 +116,7 @@ describe("entries", () => {
expect(mockClient.entry.mark).toHaveBeenCalledWith({ id: "3", read: true }) expect(mockClient.entry.mark).toHaveBeenCalledWith({ id: "3", read: true })
}) })
it("marks all entries as read", async () => { it("marks all entries as read", () => {
const store = configureStore({ const store = configureStore({
reducer: reducers, reducer: reducers,
preloadedState: { preloadedState: {

View File

@@ -46,11 +46,11 @@ const buildGetEntriesPaginatedRequest = (state: RootState, source: EntrySource,
tag: source.type === "tag" ? source.id : undefined, tag: source.type === "tag" ? source.id : undefined,
keywords: state.entries.search, keywords: state.entries.search,
}) })
export const reloadEntries = createAppAsyncThunk("entries/reload", async (arg, thunkApi) => { export const reloadEntries = createAppAsyncThunk("entries/reload", (arg, thunkApi) => {
const state = thunkApi.getState() const state = thunkApi.getState()
thunkApi.dispatch(loadEntries({ source: state.entries.source, clearSearch: false })) thunkApi.dispatch(loadEntries({ source: state.entries.source, clearSearch: false }))
}) })
export const search = createAppAsyncThunk("entries/search", async (arg: string, thunkApi) => { export const search = createAppAsyncThunk("entries/search", (arg: string, thunkApi) => {
const state = thunkApi.getState() const state = thunkApi.getState()
thunkApi.dispatch(setSearch(arg)) thunkApi.dispatch(setSearch(arg))
thunkApi.dispatch(loadEntries({ source: state.entries.source, clearSearch: false })) thunkApi.dispatch(loadEntries({ source: state.entries.source, clearSearch: false }))
@@ -84,7 +84,7 @@ export const markMultipleEntries = createAppAsyncThunk(
thunkApi.dispatch(reloadTree()) thunkApi.dispatch(reloadTree())
} }
) )
export const markEntriesUpToEntry = createAppAsyncThunk("entries/entry/upToEntry", async (arg: Entry, thunkApi) => { export const markEntriesUpToEntry = createAppAsyncThunk("entries/entry/upToEntry", (arg: Entry, thunkApi) => {
const state = thunkApi.getState() const state = thunkApi.getState()
const { entries } = state.entries const { entries } = state.entries

View File

@@ -76,7 +76,7 @@ class HighlightMatcher extends Matcher {
return this.doMatch(string, new RegExp(pattern, "i"), () => ({})) return this.doMatch(string, new RegExp(pattern, "i"), () => ({}))
} }
replaceWith(children: ChildrenNode, props: unknown): Node { replaceWith(children: ChildrenNode): Node {
return <Mark>{children}</Mark> return <Mark>{children}</Mark>
} }

View File

@@ -2,9 +2,9 @@ import { BasicHtmlStyles } from "components/content/BasicHtmlStyles"
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading" import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading"
export function Enclosure(props: { enclosureType: string; enclosureUrl: string }) { export function Enclosure(props: { enclosureType: string; enclosureUrl: string }) {
const hasVideo = props.enclosureType?.startsWith("video") const hasVideo = props.enclosureType.startsWith("video")
const hasAudio = props.enclosureType?.startsWith("audio") const hasAudio = props.enclosureType.startsWith("audio")
const hasImage = props.enclosureType?.startsWith("image") const hasImage = props.enclosureType.startsWith("image")
return ( return (
<BasicHtmlStyles> <BasicHtmlStyles>

View File

@@ -295,7 +295,6 @@ export function FeedEntries() {
}) })
) )
if (!entries) return <Loader />
return ( return (
<InfiniteScroll <InfiniteScroll
id="entries" id="entries"

View File

@@ -47,7 +47,7 @@ export function ShareButtons(props: { url: string; description: string }) {
return ( return (
<SimpleGrid cols={4}> <SimpleGrid cols={4}>
{(Object.keys(Constants.sharing) as Array<keyof SharingSettings>) {(Object.keys(Constants.sharing) as (keyof SharingSettings)[])
.filter(site => sharingSettings?.[site]) .filter(site => sharingSettings?.[site])
.map(site => ( .map(site => (
<ShareButton <ShareButton

View File

@@ -14,7 +14,7 @@ export function ImportOpml() {
const form = useForm<{ file: File }>({ const form = useForm<{ file: File }>({
validate: { validate: {
file: v => (v ? null : t`file is required`), file: () => t`file is required`,
}, },
}) })

View File

@@ -100,7 +100,7 @@ export function DisplaySettings() {
<Divider label={<Trans>Sharing sites</Trans>} labelPosition="center" /> <Divider label={<Trans>Sharing sites</Trans>} labelPosition="center" />
<SimpleGrid cols={2}> <SimpleGrid cols={2}>
{(Object.keys(Constants.sharing) as Array<keyof SharingSettings>).map(site => ( {(Object.keys(Constants.sharing) as (keyof SharingSettings)[]).map(site => (
<Switch <Switch
key={site} key={site}
label={Constants.sharing[site].label} label={Constants.sharing[site].label}

View File

@@ -38,9 +38,8 @@ export const useWebSocket = () => {
ws.onopen = () => dispatch(setWebSocketConnected(true)) ws.onopen = () => dispatch(setWebSocketConnected(true))
ws.onclose = () => dispatch(setWebSocketConnected(false)) ws.onclose = () => dispatch(setWebSocketConnected(false))
ws.onmessage = event => { ws.onmessage = event => {
const { data } = event if (typeof event.data === "string") {
if (typeof data === "string") { handleMessage(dispatch, event.data)
handleMessage(dispatch, data)
} }
} }
} }

View File

@@ -44,8 +44,8 @@ export const locales: Locale[] = [
function activateLocale(locale: string) { function activateLocale(locale: string) {
// lingui // lingui
import(`./locales/${locale}/messages.po`).then(data => { import(`./locales/${locale}/messages.po`).then((data: { messages: Messages }) => {
i18n.load(locale, data.messages as Messages) i18n.load(locale, data.messages)
i18n.activate(locale) i18n.activate(locale)
}) })

View File

@@ -108,7 +108,7 @@ export function AdminUsersPage() {
</Table.Tr> </Table.Tr>
</Table.Thead> </Table.Thead>
<Table.Tbody> <Table.Tbody>
{users?.map(u => ( {users.map(u => (
<Table.Tr key={u.id}> <Table.Tr key={u.id}>
<Table.Td>{u.id}</Table.Td> <Table.Td>{u.id}</Table.Td>
<Table.Td>{u.name}</Table.Td> <Table.Td>{u.name}</Table.Td>

View File

@@ -3,7 +3,9 @@ import { HistoryService, RedocStandalone } from "redoc"
// disable redoc url sync because it causes issues with hashrouter // disable redoc url sync because it causes issues with hashrouter
Object.defineProperty(HistoryService.prototype, "replace", { Object.defineProperty(HistoryService.prototype, "replace", {
value: () => {}, value: () => {
// do nothing
},
}) })
function ApiDocumentationPage() { function ApiDocumentationPage() {

View File

@@ -49,11 +49,17 @@ export function FeedEntriesPage(props: FeedEntriesPageProps) {
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const titleClicked = () => { const titleClicked = () => {
if (props.sourceType === "category") { switch (props.sourceType) {
dispatch(redirectToCategoryDetails(id)) case "category":
} else if (props.sourceType === "feed") { dispatch(redirectToCategoryDetails(id))
dispatch(redirectToFeedDetails(id)) break
} else if (props.sourceType === "tag") dispatch(redirectToTagDetails(id)) case "feed":
dispatch(redirectToFeedDetails(id))
break
case "tag":
dispatch(redirectToTagDetails(id))
break
}
} }
useEffect(() => { useEffect(() => {