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 = {
env: {
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: {
react: {
version: "detect"
}
version: "detect",
},
},
overrides: [
{
env: {
node: true
node: true,
},
files: [".eslintrc.{js,cjs}"],
parserOptions: {
sourceType: "script"
}
}
sourceType: "script",
},
},
],
parserOptions: {
project: true,
ecmaVersion: "latest",
sourceType: "module"
sourceType: "module",
},
plugins: ["react"],
rules: {
@@ -31,11 +39,13 @@ module.exports = {
"@typescript-eslint/no-confusing-void-expression": ["error", { ignoreArrowShorthand: true }],
"@typescript-eslint/no-floating-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/strict-boolean-expressions": "off",
"@typescript-eslint/unbound-method": "off",
"react/no-unescaped-entities": "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/throttle-debounce": "^5.0.2",
"@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",
"babel-plugin-macros": "^3.1.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-standard-with-typescript": "^43.0.1",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.33.2",
"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(
response => response,
error => {
const { status, data } = error.response
if (
(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"
if (axios.isAxiosError(error) && error.response) {
const { status, data } = error.response
if (
(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"
}
}
throw error
}
@@ -107,13 +109,11 @@ export const client = {
export const errorToStrings = (err: unknown) => {
let strings: string[] = []
if (axios.isAxiosError(err)) {
if (err.response) {
const { data } = err.response
if (typeof data === "string") strings.push(data)
if (typeof data === "object" && data.message) strings.push(data.message as string)
if (typeof data === "object" && data.errors) strings = [...strings, ...data.errors]
}
if (axios.isAxiosError(err) && err.response) {
const { data } = err.response
if (typeof data === "string") strings.push(data)
if (typeof data === "object" && data.message) strings.push(data.message as string)
if (typeof data === "object" && data.errors) strings = [...strings, ...data.errors]
}
return strings

View File

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

View File

@@ -46,11 +46,11 @@ const buildGetEntriesPaginatedRequest = (state: RootState, source: EntrySource,
tag: source.type === "tag" ? source.id : undefined,
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()
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()
thunkApi.dispatch(setSearch(arg))
thunkApi.dispatch(loadEntries({ source: state.entries.source, clearSearch: false }))
@@ -84,7 +84,7 @@ export const markMultipleEntries = createAppAsyncThunk(
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 { entries } = state.entries

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ export function ImportOpml() {
const form = useForm<{ file: File }>({
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" />
<SimpleGrid cols={2}>
{(Object.keys(Constants.sharing) as Array<keyof SharingSettings>).map(site => (
{(Object.keys(Constants.sharing) as (keyof SharingSettings)[]).map(site => (
<Switch
key={site}
label={Constants.sharing[site].label}

View File

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

View File

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

View File

@@ -108,7 +108,7 @@ export function AdminUsersPage() {
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{users?.map(u => (
{users.map(u => (
<Table.Tr key={u.id}>
<Table.Td>{u.id}</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
Object.defineProperty(HistoryService.prototype, "replace", {
value: () => {},
value: () => {
// do nothing
},
})
function ApiDocumentationPage() {

View File

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