diff --git a/.vscode/settings.json b/.vscode/settings.json index 4bf115e25..94ed7a3fc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,24 +4,15 @@ "mode": "auto" } ], + "eslint.experimental.useFlatConfig": true, "typescript.tsdk": "node_modules\\typescript\\lib", "js/ts.implicitProjectConfig.experimentalDecorators": true, "prettier.configPath": "./tooling/prettier/index.mjs", - "cSpell.words": [ - "cqmin", - "homarr", - "superjson", - "trpc", - "Umami" - ], + "cSpell.words": ["cqmin", "homarr", "superjson", "trpc", "Umami"], "i18n-ally.dirStructure": "auto", - "i18n-ally.enabledFrameworks": [ - "next-international" - ], - "i18n-ally.localesPaths": [ - "./packages/translation/src/lang/", - ], + "i18n-ally.enabledFrameworks": ["next-international"], + "i18n-ally.localesPaths": ["./packages/translation/src/lang/"], "i18n-ally.enabledParsers": ["ts"], "i18n-ally.extract.keyMaxLength": 0, - "i18n-ally.keystyle": "flat", -} \ No newline at end of file + "i18n-ally.keystyle": "flat" +} diff --git a/apps/nextjs/eslint.config.js b/apps/nextjs/eslint.config.js new file mode 100644 index 000000000..c131bab90 --- /dev/null +++ b/apps/nextjs/eslint.config.js @@ -0,0 +1,13 @@ +import baseConfig from "@homarr/eslint-config/base"; +import nextjsConfig from "@homarr/eslint-config/nextjs"; +import reactConfig from "@homarr/eslint-config/react"; + +/** @type {import('typescript-eslint').Config} */ +export default [ + { + ignores: [".next/**"], + }, + ...baseConfig, + ...reactConfig, + ...nextjsConfig, +]; diff --git a/apps/nextjs/next.config.mjs b/apps/nextjs/next.config.mjs index 970d393c4..0cc05f786 100644 --- a/apps/nextjs/next.config.mjs +++ b/apps/nextjs/next.config.mjs @@ -10,12 +10,10 @@ const config = { eslint: { ignoreDuringBuilds: true }, typescript: { ignoreBuildErrors: true }, webpack: (config) => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access config.module.rules.push({ test: /\.node$/, loader: "node-loader", }); - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return config; }, experimental: { diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json index a19b9f728..0fdf8888c 100644 --- a/apps/nextjs/package.json +++ b/apps/nextjs/package.json @@ -7,7 +7,7 @@ "build": "pnpm with-env next build", "clean": "git clean -xdf .next .turbo node_modules", "dev": "pnpm with-env next dev", - "lint": "dotenv -v SKIP_ENV_VALIDATION=1 next lint", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "start": "pnpm with-env next start", "typecheck": "tsc --noEmit", @@ -73,19 +73,11 @@ "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "concurrently": "^8.2.2", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "node-loader": "^2.0.0", "prettier": "^3.3.1", "tsx": "4.14.0", "typescript": "^5.4.5" }, - "eslintConfig": { - "root": true, - "extends": [ - "@homarr/eslint-config/base", - "@homarr/eslint-config/nextjs", - "@homarr/eslint-config/react" - ] - }, "prettier": "@homarr/prettier-config" } diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/_context.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/_context.tsx index 808759af1..346baff75 100644 --- a/apps/nextjs/src/app/[locale]/boards/(content)/_context.tsx +++ b/apps/nextjs/src/app/[locale]/boards/(content)/_context.tsx @@ -49,7 +49,6 @@ export const BoardProvider = ({ useEffect(() => { setReadySections((previous) => previous.filter((id) => data.sections.some((section) => section.id === id))); - // eslint-disable-next-line react-hooks/exhaustive-deps }, [data.sections.length, setReadySections]); const markAsReady = useCallback((id: string) => { diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/_theme.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/_theme.tsx index bf68bb902..e3c87aa05 100644 --- a/apps/nextjs/src/app/[locale]/boards/(content)/_theme.tsx +++ b/apps/nextjs/src/app/[locale]/boards/(content)/_theme.tsx @@ -33,6 +33,7 @@ export const generateColors = (hex: string) => { return rgbaColors.map((color) => { return ( "#" + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion color .split("(")[1]! .replaceAll(" ", "") diff --git a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_access/group-access.tsx b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_access/group-access.tsx index f465b6c08..7a6c4dce3 100644 --- a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_access/group-access.tsx +++ b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_access/group-access.tsx @@ -81,6 +81,7 @@ export const GroupsForm = ({ board, initialPermissions, onCountChange }: FormPro {form.values.items.map((row, index) => ( } permission={row.permission} index={index} diff --git a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_access/user-access.tsx b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_access/user-access.tsx index 8ed7e98ee..775925252 100644 --- a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_access/user-access.tsx +++ b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_access/user-access.tsx @@ -93,6 +93,7 @@ export const UsersForm = ({ board, initialPermissions, onCountChange }: FormProp {form.values.items.map((row, index) => ( } permission={row.permission} index={index} diff --git a/apps/nextjs/src/app/[locale]/compose.tsx b/apps/nextjs/src/app/[locale]/compose.tsx index 6997ca986..e7899c938 100644 --- a/apps/nextjs/src/app/[locale]/compose.tsx +++ b/apps/nextjs/src/app/[locale]/compose.tsx @@ -6,7 +6,6 @@ export const composeWrappers = ( wrappers: React.FunctionComponent[], ): React.FunctionComponent => { return wrappers.reverse().reduce((Acc, Current): React.FunctionComponent => { - // eslint-disable-next-line react/display-name return (props) => ( diff --git a/apps/nextjs/src/app/[locale]/manage/about/page.tsx b/apps/nextjs/src/app/[locale]/manage/about/page.tsx index a1dadf037..a29e44e0f 100644 --- a/apps/nextjs/src/app/[locale]/manage/about/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/about/page.tsx @@ -21,11 +21,11 @@ import { setStaticParamsLocale } from "next-international/server"; import { getScopedI18n, getStaticParams } from "@homarr/translation/server"; +import { homarrLogoPath } from "~/components/layout/logo/homarr-logo"; import { createMetaTitle } from "~/metadata"; import { getPackageAttributesAsync } from "~/versions/package-reader"; import contributorsData from "../../../../../../../static-data/contributors.json"; import translatorsData from "../../../../../../../static-data/translators.json"; -import logo from "../../../../../public/logo/logo.png"; import classes from "./about.module.css"; export async function generateMetadata() { @@ -50,7 +50,7 @@ export default async function AboutPage({ params: { locale } }: PageProps) {
- + Homarr diff --git a/apps/nextjs/src/app/[locale]/manage/boards/page.tsx b/apps/nextjs/src/app/[locale]/manage/boards/page.tsx index 5dc4c79d7..906262eb4 100644 --- a/apps/nextjs/src/app/[locale]/manage/boards/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/boards/page.tsx @@ -83,7 +83,7 @@ const BoardCard = async ({ board }: BoardCardProps) => { {board.creator && ( <Group gap="xs"> <UserAvatar user={board.creator} size="sm" /> - <Text>{board.creator?.name}</Text> + <Text>{board.creator.name}</Text> </Group> )} </Group> diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/edit/[id]/_integration-edit-form.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/edit/[id]/_integration-edit-form.tsx index e1521abd2..c93826e92 100644 --- a/apps/nextjs/src/app/[locale]/manage/integrations/edit/[id]/_integration-edit-form.tsx +++ b/apps/nextjs/src/app/[locale]/manage/integrations/edit/[id]/_integration-edit-form.tsx @@ -95,6 +95,7 @@ export const EditIntegrationForm = ({ integration }: EditIntegrationForm) => { {secretsKinds.map((kind, index) => ( <SecretCard key={kind} + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion secret={secretsMap.get(kind)!} onCancel={() => new Promise((res) => { @@ -107,7 +108,7 @@ export const EditIntegrationForm = ({ integration }: EditIntegrationForm) => { children: t("integration.secrets.reset.message"), onCancel: () => res(false), onConfirm: () => { - form.setFieldValue(`secrets.${index}.value`, secretsMap.get(kind)!.value ?? ""); + form.setFieldValue(`secrets.${index}.value`, secretsMap.get(kind)?.value ?? ""); res(true); }, }); diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/new/page.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/new/page.tsx index 23eb474f5..0db877a11 100644 --- a/apps/nextjs/src/app/[locale]/manage/integrations/new/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/integrations/new/page.tsx @@ -17,6 +17,7 @@ interface NewIntegrationPageProps { } export default async function IntegrationsNewPage({ searchParams }: NewIntegrationPageProps) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const result = z.enum([integrationKinds[0]!, ...integrationKinds.slice(1)]).safeParse(searchParams.kind); if (!result.success) { notFound(); diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx index 07b4c894d..15ff2ef83 100644 --- a/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx @@ -82,6 +82,7 @@ const IntegrationList = async ({ integrations, activeTab }: IntegrationListProps const grouppedIntegrations = integrations.reduce( (acc, integration) => { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!acc[integration.kind]) { acc[integration.kind] = []; } diff --git a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_profile-avatar-form.tsx b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_profile-avatar-form.tsx index 0e819d163..4ad991bd6 100644 --- a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_profile-avatar-form.tsx +++ b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_profile-avatar-form.tsx @@ -136,6 +136,6 @@ const fileToBase64Async = async (file: File): Promise<string> => new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); - reader.onload = () => resolve(reader.result?.toString() || ""); + reader.onload = () => resolve(reader.result?.toString() ?? ""); reader.onerror = reject; }); diff --git a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/page.tsx b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/page.tsx index 82d3a6f89..3dfcfca59 100644 --- a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/page.tsx @@ -35,7 +35,7 @@ export async function generateMetadata({ params }: Props) { const t = await getScopedI18n("management.page.user.edit"); return { - title: createMetaTitle(t("metaTitle", { username: user?.name })), + title: createMetaTitle(t("metaTitle", { username: user.name })), }; } diff --git a/apps/nextjs/src/app/[locale]/manage/users/create/_components/create-user-stepper.tsx b/apps/nextjs/src/app/[locale]/manage/users/create/_components/create-user-stepper.tsx index eadf51ddd..7526e821a 100644 --- a/apps/nextjs/src/app/[locale]/manage/users/create/_components/create-user-stepper.tsx +++ b/apps/nextjs/src/app/[locale]/manage/users/create/_components/create-user-stepper.tsx @@ -71,7 +71,8 @@ export const UserCreateStepperComponent = () => { const allForms = useMemo(() => [generalForm, securityForm], [generalForm, securityForm]); - const isCurrentFormValid = allForms[active] ? (allForms[active]!.isValid satisfies () => boolean) : () => true; + const activeForm = allForms[active]; + const isCurrentFormValid = activeForm ? activeForm.isValid : () => true; const canNavigateToNextStep = isCurrentFormValid(); const controlledGoToNextStep = useCallback(async () => { diff --git a/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/page.tsx b/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/page.tsx index 7f694ba6b..e5f0fc604 100644 --- a/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/page.tsx +++ b/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/page.tsx @@ -24,7 +24,8 @@ export default async function GroupsDetailPage({ params, searchParams }: GroupsD const group = await api.group.getById({ id: params.id }); const filteredMembers = searchParams.search - ? group.members.filter((member) => member.name?.toLowerCase().includes(searchParams.search!.trim().toLowerCase())) + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + group.members.filter((member) => member.name?.toLowerCase().includes(searchParams.search!.trim().toLowerCase())) : group.members; return ( diff --git a/apps/nextjs/src/components/board/items/item-actions.tsx b/apps/nextjs/src/components/board/items/item-actions.tsx index 2246853ea..5fd6c0841 100644 --- a/apps/nextjs/src/components/board/items/item-actions.tsx +++ b/apps/nextjs/src/components/board/items/item-actions.tsx @@ -90,7 +90,6 @@ export const useItemActions = () => { const updateItemOptions = useCallback( ({ itemId, newOptions }: UpdateItemOptions) => { updateBoard((previous) => { - if (!previous) return previous; return { ...previous, sections: previous.sections.map((section) => { @@ -117,7 +116,6 @@ export const useItemActions = () => { const updateItemAdvancedOptions = useCallback( ({ itemId, newAdvancedOptions }: UpdateItemAdvancedOptions) => { updateBoard((previous) => { - if (!previous) return previous; return { ...previous, sections: previous.sections.map((section) => { @@ -144,7 +142,6 @@ export const useItemActions = () => { const updateItemIntegrations = useCallback( ({ itemId, newIntegrations }: UpdateItemIntegrations) => { updateBoard((previous) => { - if (!previous) return previous; return { ...previous, sections: previous.sections.map((section) => { diff --git a/apps/nextjs/src/components/board/sections/category/category-actions.ts b/apps/nextjs/src/components/board/sections/category/category-actions.ts index f78144536..c29751f38 100644 --- a/apps/nextjs/src/components/board/sections/category/category-actions.ts +++ b/apps/nextjs/src/components/board/sections/category/category-actions.ts @@ -37,6 +37,7 @@ export const useCategoryActions = () => { sections: [ // Place sections before the new category ...previous.sections.filter( + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition (section) => (section.kind === "category" || section.kind === "empty") && section.position < position, ), { @@ -56,6 +57,7 @@ export const useCategoryActions = () => { ...previous.sections .filter( (section): section is CategorySection | EmptySection => + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition (section.kind === "category" || section.kind === "empty") && section.position >= position, ) .map((section) => ({ @@ -74,6 +76,7 @@ export const useCategoryActions = () => { const lastSection = previous.sections .filter( (section): section is CategorySection | EmptySection => + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition section.kind === "empty" || section.kind === "category", ) .sort((sectionA, sectionB) => sectionB.position - sectionA.position) @@ -130,12 +133,13 @@ export const useCategoryActions = () => { (section): section is CategorySection => section.kind === "category" && section.id === id, ); if (!currentCategory) return previous; - if (currentCategory?.position === 1 && direction === "up") return previous; - if (currentCategory?.position === previous.sections.length - 2 && direction === "down") return previous; + if (currentCategory.position === 1 && direction === "up") return previous; + if (currentCategory.position === previous.sections.length - 2 && direction === "down") return previous; return { ...previous, sections: previous.sections.map((section) => { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (section.kind !== "category" && section.kind !== "empty") return section; const offset = direction === "up" ? -2 : 2; // Move category and empty section diff --git a/apps/nextjs/src/components/board/sections/content.tsx b/apps/nextjs/src/components/board/sections/content.tsx index 10d26a236..96ceaed99 100644 --- a/apps/nextjs/src/components/board/sections/content.tsx +++ b/apps/nextjs/src/components/board/sections/content.tsx @@ -1,6 +1,3 @@ -/* eslint-disable react/no-unknown-property */ -// Ignored because of gridstack attributes - import type { RefObject } from "react"; import { useEffect, useMemo, useRef } from "react"; import { ActionIcon, Card, Menu } from "@mantine/core"; @@ -122,6 +119,7 @@ const BoardItemContent = ({ item, ...dimensions }: ItemContentProps) => { <Comp options={options as never} integrationIds={item.integrationIds} + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition serverData={serverData?.data as never} isEditMode={isEditMode} boardId={board.id} diff --git a/apps/nextjs/src/components/board/sections/gridstack/init-gridstack.ts b/apps/nextjs/src/components/board/sections/gridstack/init-gridstack.ts index 84378fc7f..4fb9c2dd7 100644 --- a/apps/nextjs/src/components/board/sections/gridstack/init-gridstack.ts +++ b/apps/nextjs/src/components/board/sections/gridstack/init-gridstack.ts @@ -37,7 +37,7 @@ export const initializeGridstack = ({ section, refs, sectionColumnCount }: Initi `.grid-stack-${section.kind}[data-section-id='${section.id}']`, ); const grid = newGrid.current; - if (!grid) return false; + // Must be used to update the column count after the initialization grid.column(sectionColumnCount, "none"); diff --git a/apps/nextjs/src/components/board/sections/gridstack/use-gridstack.ts b/apps/nextjs/src/components/board/sections/gridstack/use-gridstack.ts index aad587972..abef91617 100644 --- a/apps/nextjs/src/components/board/sections/gridstack/use-gridstack.ts +++ b/apps/nextjs/src/components/board/sections/gridstack/use-gridstack.ts @@ -54,15 +54,15 @@ export const useGridstack = ({ section, mainRef }: UseGridstackProps): UseGrista const onChange = useCallback( (changedNode: GridStackNode) => { const itemId = changedNode.el?.getAttribute("data-id"); - if (!itemId) return; + if (!itemId || !changedNode.x || !changedNode.y || !changedNode.w || !changedNode.h) return; // Updates the react-query state moveAndResizeItem({ itemId, - xOffset: changedNode.x!, - yOffset: changedNode.y!, - width: changedNode.w!, - height: changedNode.h!, + xOffset: changedNode.x, + yOffset: changedNode.y, + width: changedNode.w, + height: changedNode.h, }); }, [moveAndResizeItem], @@ -70,16 +70,16 @@ export const useGridstack = ({ section, mainRef }: UseGridstackProps): UseGrista const onAdd = useCallback( (addedNode: GridStackNode) => { const itemId = addedNode.el?.getAttribute("data-id"); - if (!itemId) return; + if (!itemId || !addedNode.x || !addedNode.y || !addedNode.w || !addedNode.h) return; // Updates the react-query state moveItemToSection({ itemId, sectionId: section.id, - xOffset: addedNode.x!, - yOffset: addedNode.y!, - width: addedNode.w!, - height: addedNode.h!, + xOffset: addedNode.x, + yOffset: addedNode.y, + width: addedNode.w, + height: addedNode.h, }); }, [moveItemToSection, section.id], @@ -121,7 +121,6 @@ export const useGridstack = ({ section, mainRef }: UseGridstackProps): UseGrista } // Only run this effect when the section items change - // eslint-disable-next-line react-hooks/exhaustive-deps }, [items.length, section.items.length, board.columnCount]); return { diff --git a/apps/nextjs/src/components/layout/background.tsx b/apps/nextjs/src/components/layout/background.tsx index 1c5b7c8b4..5c991e1a6 100644 --- a/apps/nextjs/src/components/layout/background.tsx +++ b/apps/nextjs/src/components/layout/background.tsx @@ -20,11 +20,11 @@ export const useOptionalBackgroundProps = (): Partial<AppShellProps> => { } return { - bg: `url(${board?.backgroundImageUrl})`, + bg: `url(${board.backgroundImageUrl})`, bgp: "center center", - bgsz: board?.backgroundImageSize ?? "cover", - bgr: board?.backgroundImageRepeat ?? "no-repeat", - bga: board?.backgroundImageAttachment ?? "fixed", + bgsz: board.backgroundImageSize, + bgr: board.backgroundImageRepeat, + bga: board.backgroundImageAttachment, }; }; @@ -49,7 +49,7 @@ export const BoardBackgroundVideo = () => { height: "100vh", top: 0, left: 0, - objectFit: board.backgroundImageSize ?? "cover", + objectFit: board.backgroundImageSize, }} > <source src={board.backgroundImageUrl} type={`video/${videoFormat}`} /> diff --git a/apps/nextjs/src/components/layout/header/button.tsx b/apps/nextjs/src/components/layout/header/button.tsx index 0604c137b..98bdbe0b7 100644 --- a/apps/nextjs/src/components/layout/header/button.tsx +++ b/apps/nextjs/src/components/layout/header/button.tsx @@ -22,7 +22,6 @@ const headerButtonActionIconProps: ActionIconProps = { size: "lg", }; -// eslint-disable-next-line react/display-name export const HeaderButton = forwardRef<HTMLButtonElement, HeaderButtonProps>((props, ref) => { if ("href" in props) { return ( diff --git a/apps/nextjs/src/components/user-avatar-menu.tsx b/apps/nextjs/src/components/user-avatar-menu.tsx index a9f47d9ab..0cd5e0726 100644 --- a/apps/nextjs/src/components/user-avatar-menu.tsx +++ b/apps/nextjs/src/components/user-avatar-menu.tsx @@ -72,7 +72,7 @@ export const UserAvatarMenu = ({ children }: UserAvatarMenuProps) => { <> <Menu.Item component={Link} - href={`/manage/users/${session.data?.user?.id}`} + href={`/manage/users/${session.data?.user.id}`} leftSection={<IconSettings size="1rem" />} > {t("preferences")} diff --git a/apps/nextjs/src/components/user-avatar.tsx b/apps/nextjs/src/components/user-avatar.tsx index d75606caf..9ddfba554 100644 --- a/apps/nextjs/src/components/user-avatar.tsx +++ b/apps/nextjs/src/components/user-avatar.tsx @@ -17,7 +17,8 @@ export const UserAvatar = async ({ size }: UserAvatarProps) => { if (!currentSession?.user) return <Avatar {...commonProps} />; if (currentSession.user.image) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return <Avatar {...commonProps} src={currentSession.user.image} alt={currentSession.user.name!} />; - return <Avatar {...commonProps}>{currentSession.user.name!.substring(0, 2).toUpperCase()}</Avatar>; + return <Avatar {...commonProps}>{currentSession.user.name?.substring(0, 2).toUpperCase()}</Avatar>; }; diff --git a/apps/tasks/eslint.config.js b/apps/tasks/eslint.config.js new file mode 100644 index 000000000..eddd608e5 --- /dev/null +++ b/apps/tasks/eslint.config.js @@ -0,0 +1,9 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [ + { + ignores: ["build/**"], + }, + ...baseConfig, +]; diff --git a/apps/tasks/package.json b/apps/tasks/package.json index 2140f78cf..dbcff5525 100644 --- a/apps/tasks/package.json +++ b/apps/tasks/package.json @@ -13,7 +13,7 @@ "dev": "pnpm with-env tsx ./src/main.ts", "build": "esbuild src/main.ts --bundle --platform=node --outfile=tasks.cjs", "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit", "with-env": "dotenv -e ../../.env --" @@ -40,16 +40,10 @@ "@types/node-cron": "^3.0.11", "@types/node": "^20.14.2", "dotenv-cli": "^7.4.2", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "prettier": "^3.3.1", "tsx": "4.14.0", "typescript": "^5.4.5" }, - "eslintConfig": { - "root": true, - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config" } diff --git a/apps/websocket/eslint.config.js b/apps/websocket/eslint.config.js new file mode 100644 index 000000000..f7a5a7d36 --- /dev/null +++ b/apps/websocket/eslint.config.js @@ -0,0 +1,4 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig]; diff --git a/apps/websocket/package.json b/apps/websocket/package.json index 69cd4af39..8ae96995f 100644 --- a/apps/websocket/package.json +++ b/apps/websocket/package.json @@ -10,7 +10,7 @@ "dev": "pnpm with-env tsx ./src/main.ts", "build": "esbuild src/main.ts --bundle --platform=node --outfile=wssServer.cjs --external:bcrypt --loader:.html=text --loader:.node=text", "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit", "with-env": "dotenv -e ../../.env --" @@ -32,15 +32,9 @@ "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", "@types/ws": "^8.5.10", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "prettier": "^3.3.1", "typescript": "^5.4.5" }, - "eslintConfig": { - "root": true, - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config" } diff --git a/packages/analytics/eslint.config.js b/packages/analytics/eslint.config.js new file mode 100644 index 000000000..f7a5a7d36 --- /dev/null +++ b/packages/analytics/eslint.config.js @@ -0,0 +1,4 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig]; diff --git a/packages/analytics/package.json b/packages/analytics/package.json index 7d2c49d17..a0f900c48 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -16,7 +16,7 @@ "license": "MIT", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -27,14 +27,9 @@ "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/server-settings": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, - "eslintConfig": { - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config", "dependencies": { "@umami/node": "^0.3.0", diff --git a/packages/api/eslint.config.js b/packages/api/eslint.config.js new file mode 100644 index 000000000..f7a5a7d36 --- /dev/null +++ b/packages/api/eslint.config.js @@ -0,0 +1,4 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig]; diff --git a/packages/api/package.json b/packages/api/package.json index 5024055bd..e17d26c4f 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -14,7 +14,7 @@ "type": "module", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -40,15 +40,9 @@ "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", "@types/dockerode": "^3.3.29", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "prettier": "^3.3.1", "typescript": "^5.4.5" }, - "eslintConfig": { - "root": true, - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config" } diff --git a/packages/api/src/router/docker/docker-router.ts b/packages/api/src/router/docker/docker-router.ts index 0d936bec1..fd19f14ec 100644 --- a/packages/api/src/router/docker/docker-router.ts +++ b/packages/api/src/router/docker/docker-router.ts @@ -72,7 +72,7 @@ function sanitizeContainers( ): DockerContainer[] { return containers.map((container) => { return { - name: container.Names[0]?.split("/")[1] || "Unknown", + name: container.Names[0]?.split("/")[1] ?? "Unknown", id: container.Id, instance: container.instance, state: container.State as DockerContainerState, diff --git a/packages/api/src/router/docker/docker-singleton.ts b/packages/api/src/router/docker/docker-singleton.ts index 072c40925..f95a58418 100644 --- a/packages/api/src/router/docker/docker-singleton.ts +++ b/packages/api/src/router/docker/docker-singleton.ts @@ -28,7 +28,7 @@ export class DockerSingleton { host: `${host}:${ports[i]}`, instance: new Docker({ host, - port: parseInt(ports[i] || "", 10), + port: parseInt(ports[i] ?? "", 10), }), }); return instances; @@ -41,6 +41,7 @@ export class DockerSingleton { } public static getInstance(): DockerInstance[] { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!DockerSingleton.instances) { DockerSingleton.instances = new DockerSingleton().createInstances(); } diff --git a/packages/api/src/router/group.ts b/packages/api/src/router/group.ts index 17a7749d1..3d0cfe168 100644 --- a/packages/api/src/router/group.ts +++ b/packages/api/src/router/group.ts @@ -42,7 +42,7 @@ export const groupRouter = createTRPCRouter({ ...group, members: group.members.map((member) => member.user), })), - totalCount: groupCount[0]!.count, + totalCount: groupCount[0]?.count ?? 0, }; }), getById: protectedProcedure.input(validation.group.byId).query(async ({ input, ctx }) => { diff --git a/packages/api/src/router/icons.ts b/packages/api/src/router/icons.ts index e0c3c9a7c..e99918da3 100644 --- a/packages/api/src/router/icons.ts +++ b/packages/api/src/router/icons.ts @@ -15,7 +15,7 @@ export const iconsRouter = createTRPCRouter({ name: true, url: true, }, - where: input.searchText?.length ?? 0 > 0 ? like(icons.name, `%${input.searchText}%`) : undefined, + where: (input.searchText?.length ?? 0) > 0 ? like(icons.name, `%${input.searchText}%`) : undefined, limit: 5, }, }, diff --git a/packages/api/src/router/test/app.spec.ts b/packages/api/src/router/test/app.spec.ts index db9550d0c..1ab6ee559 100644 --- a/packages/api/src/router/test/app.spec.ts +++ b/packages/api/src/router/test/app.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { describe, expect, test, vi } from "vitest"; import type { Session } from "@homarr/auth"; diff --git a/packages/api/src/router/test/group.spec.ts b/packages/api/src/router/test/group.spec.ts index 2901ec32b..c91483e72 100644 --- a/packages/api/src/router/test/group.spec.ts +++ b/packages/api/src/router/test/group.spec.ts @@ -169,7 +169,7 @@ describe("byId should return group by id including members and permissions", () expect(result.id).toBe(groupId); expect(result.members.length).toBe(1); - const userKeys = Object.keys(result?.members[0] ?? {}); + const userKeys = Object.keys(result.members[0] ?? {}); expect(userKeys.length).toBe(4); expect(["id", "name", "email", "image"].some((key) => userKeys.includes(key))); expect(result.permissions.length).toBe(1); diff --git a/packages/api/src/router/test/integration.spec.ts b/packages/api/src/router/test/integration.spec.ts index 2a68a8c39..9b5b7f6d7 100644 --- a/packages/api/src/router/test/integration.spec.ts +++ b/packages/api/src/router/test/integration.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { describe, expect, it, vi } from "vitest"; import type { Session } from "@homarr/auth"; diff --git a/packages/api/src/router/test/invite.spec.ts b/packages/api/src/router/test/invite.spec.ts index 59f2fad68..bcd8c3675 100644 --- a/packages/api/src/router/test/invite.spec.ts +++ b/packages/api/src/router/test/invite.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { describe, expect, test, vi } from "vitest"; import type { Session } from "@homarr/auth"; diff --git a/packages/api/src/router/user.ts b/packages/api/src/router/user.ts index 7d3fbd100..a9047f8a2 100644 --- a/packages/api/src/router/user.ts +++ b/packages/api/src/router/user.ts @@ -44,13 +44,6 @@ export const userRouter = createTRPCRouter({ }); } - if (!dbInvite || dbInvite.expirationDate < new Date()) { - throw new TRPCError({ - code: "FORBIDDEN", - message: "Invalid invite", - }); - } - await checkUsernameAlreadyTakenAndThrowAsync(ctx.db, input.username); await createUserAsync(ctx.db, input); diff --git a/packages/api/src/router/widgets/weather.ts b/packages/api/src/router/widgets/weather.ts index ab59ef50f..665fdedd4 100644 --- a/packages/api/src/router/widgets/weather.ts +++ b/packages/api/src/router/widgets/weather.ts @@ -18,7 +18,7 @@ export const weatherRouter = createTRPCRouter({ maxTemp: weather.daily.temperature_2m_max[index], minTemp: weather.daily.temperature_2m_min[index], }; - }) ?? [{ time: 0, weatherCode: 404 }], + }), }; }), }); diff --git a/packages/api/src/trpc.ts b/packages/api/src/trpc.ts index 5d231aae4..0248720d3 100644 --- a/packages/api/src/trpc.ts +++ b/packages/api/src/trpc.ts @@ -122,7 +122,7 @@ export const protectedProcedure = t.procedure.use(enforceUserIsAuthed); export const permissionRequiredProcedure = { requiresPermission: (permission: GroupPermissionKey) => { return protectedProcedure.use(({ ctx, input, next }) => { - if (!ctx.session?.user.permissions.includes(permission)) { + if (!ctx.session.user.permissions.includes(permission)) { throw new TRPCError({ code: "FORBIDDEN", message: "Permission denied", diff --git a/packages/auth/callbacks.ts b/packages/auth/callbacks.ts index 3fdeb0c54..be34dcba7 100644 --- a/packages/auth/callbacks.ts +++ b/packages/auth/callbacks.ts @@ -44,10 +44,8 @@ export const createSignInCallback = async ({ user }) => { if (!isCredentialsRequest) return true; - if (!user) return true; - // https://github.com/nextauthjs/next-auth/issues/6106 - if (!adapter?.createSession) { + if (!adapter.createSession) { return false; } @@ -56,6 +54,7 @@ export const createSignInCallback = await adapter.createSession({ sessionToken, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion userId: user.id!, expires: sessionExpiry, }); diff --git a/packages/auth/eslint.config.js b/packages/auth/eslint.config.js new file mode 100644 index 000000000..f7a5a7d36 --- /dev/null +++ b/packages/auth/eslint.config.js @@ -0,0 +1,4 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig]; diff --git a/packages/auth/package.json b/packages/auth/package.json index b22f98fc2..2793bce08 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -16,7 +16,7 @@ "license": "MIT", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -40,15 +40,9 @@ "@homarr/definitions": "workspace:^0.1.0", "@types/bcrypt": "5.0.2", "@types/cookies": "0.9.0", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "prettier": "^3.3.1", "typescript": "^5.4.5" }, - "eslintConfig": { - "root": true, - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config" } diff --git a/packages/auth/permissions/board-permissions.ts b/packages/auth/permissions/board-permissions.ts index 35bac53ab..af3b57816 100644 --- a/packages/auth/permissions/board-permissions.ts +++ b/packages/auth/permissions/board-permissions.ts @@ -23,14 +23,14 @@ export const constructBoardPermissions = (board: BoardPermissionsProps, session: const creatorId = "creator" in board ? board.creator?.id : board.creatorId; return { - hasFullAccess: session?.user?.id === creatorId || session?.user.permissions.includes("board-full-access"), + hasFullAccess: session?.user.id === creatorId || session?.user.permissions.includes("board-full-access"), hasChangeAccess: - session?.user?.id === creatorId || + session?.user.id === creatorId || board.userPermissions.some(({ permission }) => permission === "board-change") || board.groupPermissions.some(({ permission }) => permission === "board-change") || session?.user.permissions.includes("board-modify-all"), hasViewAccess: - session?.user?.id === creatorId || + session?.user.id === creatorId || board.userPermissions.length >= 1 || board.groupPermissions.length >= 1 || board.isPublic || diff --git a/packages/auth/test/callbacks.spec.ts b/packages/auth/test/callbacks.spec.ts index 1dced2115..ed66f9af4 100644 --- a/packages/auth/test/callbacks.spec.ts +++ b/packages/auth/test/callbacks.spec.ts @@ -1,8 +1,9 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { ResponseCookie } from "next/dist/compiled/@edge-runtime/cookies"; import type { ReadonlyRequestCookies } from "next/dist/server/web/spec-extension/adapters/request-cookies"; import { cookies } from "next/headers"; import type { Adapter, AdapterUser } from "@auth/core/adapters"; -import type { Account, User } from "next-auth"; +import type { Account } from "next-auth"; import type { JWT } from "next-auth/jwt"; import { describe, expect, it, test, vi } from "vitest"; @@ -141,21 +142,11 @@ describe("createSignInCallback", () => { expect(result).toBe(true); }); - it("should return true if no user", async () => { - const isCredentialsRequest = true; - const signInCallback = createSignInCallback(createAdapter(), isCredentialsRequest); - const result = await signInCallback({ - user: undefined as unknown as User, - account: {} as Account, - }); - expect(result).toBe(true); - }); - it("should return false if no adapter.createSession", async () => { const isCredentialsRequest = true; const signInCallback = createSignInCallback( // https://github.com/nextauthjs/next-auth/issues/6106 - undefined as unknown as Adapter, + { createSession: undefined } as unknown as Adapter, isCredentialsRequest, ); const result = await signInCallback({ diff --git a/packages/common/eslint.config.js b/packages/common/eslint.config.js new file mode 100644 index 000000000..f7a5a7d36 --- /dev/null +++ b/packages/common/eslint.config.js @@ -0,0 +1,4 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig]; diff --git a/packages/common/package.json b/packages/common/package.json index 31cd7eabb..dd70190d5 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -17,7 +17,7 @@ "license": "MIT", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -25,13 +25,8 @@ "@homarr/eslint-config": "workspace:^0.2.0", "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, - "eslintConfig": { - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config" } diff --git a/packages/db/configs/mysql.config.ts b/packages/db/configs/mysql.config.ts index b3669df13..407fae864 100644 --- a/packages/db/configs/mysql.config.ts +++ b/packages/db/configs/mysql.config.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import * as dotenv from "dotenv"; import type { Config } from "drizzle-kit"; diff --git a/packages/db/configs/sqlite.config.ts b/packages/db/configs/sqlite.config.ts index 919cbfc0b..aba34fe26 100644 --- a/packages/db/configs/sqlite.config.ts +++ b/packages/db/configs/sqlite.config.ts @@ -6,6 +6,7 @@ dotenv.config({ path: "../../.env" }); export default { dialect: "sqlite", schema: "./schema", + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion dbCredentials: { url: process.env.DB_URL! }, out: "./migrations/sqlite", } satisfies Config; diff --git a/packages/db/driver.ts b/packages/db/driver.ts index 2cea933ea..36a86ece5 100644 --- a/packages/db/driver.ts +++ b/packages/db/driver.ts @@ -13,6 +13,7 @@ import * as sqliteSchema from "./schema/sqlite"; type HomarrDatabase = BetterSQLite3Database<typeof sqliteSchema>; const init = () => { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!connection) { switch (process.env.DB_DRIVER) { case "mysql2": @@ -48,6 +49,7 @@ const initMySQL2 = () => { } else { connection = mysql.createConnection({ host: process.env.DB_HOST, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion database: process.env.DB_NAME!, port: Number(process.env.DB_PORT), user: process.env.DB_USER, diff --git a/packages/db/eslint.config.js b/packages/db/eslint.config.js new file mode 100644 index 000000000..f7a5a7d36 --- /dev/null +++ b/packages/db/eslint.config.js @@ -0,0 +1,4 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig]; diff --git a/packages/db/migrations/mysql/migrate.ts b/packages/db/migrations/mysql/migrate.ts index fd67b7f70..50d495d00 100644 --- a/packages/db/migrations/mysql/migrate.ts +++ b/packages/db/migrations/mysql/migrate.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { drizzle } from "drizzle-orm/mysql2"; import { migrate } from "drizzle-orm/mysql2/migrator"; import mysql from "mysql2"; diff --git a/packages/db/package.json b/packages/db/package.json index 79597b7d1..c6bf13ca9 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -17,7 +17,7 @@ "build:sqlite": "esbuild migrations/sqlite/migrate.ts --bundle --platform=node --outfile=migrations/sqlite/migrate.cjs", "build:mysql": "esbuild migrations/mysql/migrate.ts --bundle --platform=node --outfile=migrations/mysql/migrate.cjs", "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "migration:sqlite:generate": "drizzle-kit generate --config ./configs/sqlite.config.ts", "migration:sqlite:run": "drizzle-kit migrate --config ./configs/sqlite.config.ts", @@ -44,15 +44,9 @@ "@homarr/tsconfig": "workspace:^0.1.0", "@types/better-sqlite3": "7.6.10", "dotenv-cli": "^7.4.2", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "prettier": "^3.3.1", "typescript": "^5.4.5" }, - "eslintConfig": { - "root": true, - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config" } diff --git a/packages/db/test/schema.spec.ts b/packages/db/test/schema.spec.ts index 78b8363e2..1d379763a 100644 --- a/packages/db/test/schema.spec.ts +++ b/packages/db/test/schema.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import type { Column, InferSelectModel } from "drizzle-orm"; import type { ForeignKey as MysqlForeignKey, MySqlTableWithColumns } from "drizzle-orm/mysql-core"; import type { ForeignKey as SqliteForeignKey, SQLiteTableWithColumns } from "drizzle-orm/sqlite-core"; diff --git a/packages/definitions/eslint.config.js b/packages/definitions/eslint.config.js new file mode 100644 index 000000000..f7a5a7d36 --- /dev/null +++ b/packages/definitions/eslint.config.js @@ -0,0 +1,4 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig]; diff --git a/packages/definitions/package.json b/packages/definitions/package.json index c93c62866..c0c1f04a1 100644 --- a/packages/definitions/package.json +++ b/packages/definitions/package.json @@ -16,7 +16,7 @@ "license": "MIT", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -24,14 +24,9 @@ "@homarr/eslint-config": "workspace:^0.2.0", "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, - "eslintConfig": { - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config", "dependencies": { "@homarr/common": "workspace:^0.1.0" diff --git a/packages/definitions/src/integration.ts b/packages/definitions/src/integration.ts index 3f5650af4..e8e153e01 100644 --- a/packages/definitions/src/integration.ts +++ b/packages/definitions/src/integration.ts @@ -115,16 +115,16 @@ export const integrationDefs = { } >; -export const getIconUrl = (integration: IntegrationKind) => integrationDefs[integration]?.iconUrl ?? null; +export const getIconUrl = (integration: IntegrationKind) => integrationDefs[integration].iconUrl; export const getIntegrationName = (integration: IntegrationKind) => integrationDefs[integration].name; export const getDefaultSecretKinds = (integration: IntegrationKind): IntegrationSecretKind[] => - integrationDefs[integration]?.secretKinds[0]; + integrationDefs[integration].secretKinds[0]; export const getAllSecretKindOptions = ( integration: IntegrationKind, -): [IntegrationSecretKind[], ...IntegrationSecretKind[][]] => integrationDefs[integration]?.secretKinds; +): [IntegrationSecretKind[], ...IntegrationSecretKind[][]] => integrationDefs[integration].secretKinds; export const integrationKinds = objectKeys(integrationDefs); diff --git a/packages/definitions/src/permissions.ts b/packages/definitions/src/permissions.ts index 827c78df9..8c3eae83c 100644 --- a/packages/definitions/src/permissions.ts +++ b/packages/definitions/src/permissions.ts @@ -34,6 +34,7 @@ export const getPermissionsWithParents = (permissions: GroupPermissionKey[]): Gr const getPermissionsInner = (permissionSet: Set<GroupPermissionKey>, permissions: GroupPermissionKey[]) => { permissions.forEach((permission) => { const children = groupPermissionParents[permission as keyof typeof groupPermissionParents]; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (children) { getPermissionsInner(permissionSet, children); } diff --git a/packages/form/eslint.config.js b/packages/form/eslint.config.js new file mode 100644 index 000000000..f7a5a7d36 --- /dev/null +++ b/packages/form/eslint.config.js @@ -0,0 +1,4 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig]; diff --git a/packages/form/package.json b/packages/form/package.json index 208e579ab..2dcae3a56 100644 --- a/packages/form/package.json +++ b/packages/form/package.json @@ -2,6 +2,7 @@ "name": "@homarr/form", "private": true, "version": "0.1.0", + "type": "module", "exports": { ".": "./index.ts" }, @@ -15,7 +16,7 @@ "license": "MIT", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -23,14 +24,9 @@ "@homarr/eslint-config": "workspace:^0.2.0", "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, - "eslintConfig": { - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config", "dependencies": { "@mantine/form": "^7.10.1", diff --git a/packages/icons/eslint.config.js b/packages/icons/eslint.config.js new file mode 100644 index 000000000..f7a5a7d36 --- /dev/null +++ b/packages/icons/eslint.config.js @@ -0,0 +1,4 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig]; diff --git a/packages/icons/package.json b/packages/icons/package.json index f3883cad2..eca72229e 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -16,7 +16,7 @@ "license": "MIT", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -27,13 +27,8 @@ "@homarr/eslint-config": "workspace:^0.2.0", "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, - "eslintConfig": { - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config" } diff --git a/packages/icons/src/repositories/github.icon-repository.ts b/packages/icons/src/repositories/github.icon-repository.ts index 3523bf930..780f280da 100644 --- a/packages/icons/src/repositories/github.icon-repository.ts +++ b/packages/icons/src/repositories/github.icon-repository.ts @@ -32,6 +32,7 @@ export class GitHubIconRepository extends IconRepository { const fileNameWithExtension = this.getFileNameWithoutExtensionFromPath(treeItem.path); return { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion imageUrl: new URL(this.repositoryBlobUrlTemplate!.replace("{0}", treeItem.path)), fileNameWithExtension: fileNameWithExtension, local: false, diff --git a/packages/integrations/eslint.config.js b/packages/integrations/eslint.config.js new file mode 100644 index 000000000..f7a5a7d36 --- /dev/null +++ b/packages/integrations/eslint.config.js @@ -0,0 +1,4 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig]; diff --git a/packages/integrations/package.json b/packages/integrations/package.json index 5df87673e..057f79ed5 100644 --- a/packages/integrations/package.json +++ b/packages/integrations/package.json @@ -17,7 +17,7 @@ "type": "module", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -25,17 +25,12 @@ "@homarr/eslint-config": "workspace:^0.2.0", "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, "dependencies": { "@homarr/definitions": "workspace:^0.1.0", "@homarr/validation": "workspace:^0.1.0" }, - "eslintConfig": { - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config" } diff --git a/packages/log/eslint.config.js b/packages/log/eslint.config.js new file mode 100644 index 000000000..f7a5a7d36 --- /dev/null +++ b/packages/log/eslint.config.js @@ -0,0 +1,4 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig]; diff --git a/packages/log/package.json b/packages/log/package.json index 18c403c27..6db63ae59 100644 --- a/packages/log/package.json +++ b/packages/log/package.json @@ -2,6 +2,7 @@ "name": "@homarr/log", "private": true, "version": "0.1.0", + "type": "module", "exports": { ".": { "types": "./src/index.d.ts", @@ -19,7 +20,7 @@ "license": "MIT", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -32,13 +33,8 @@ "@homarr/eslint-config": "workspace:^0.2.0", "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, - "eslintConfig": { - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config" } diff --git a/packages/log/src/override.cjs b/packages/log/src/override.cjs index c8538f602..8d4c5ca94 100644 --- a/packages/log/src/override.cjs +++ b/packages/log/src/override.cjs @@ -1,7 +1,6 @@ void (async () => { const { logger } = await import("./index.mjs"); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires const nextLogger = require("next/dist/build/output/log"); const getWinstonMethodForConsole = (consoleMethod) => { @@ -37,9 +36,7 @@ void (async () => { } }; - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access Object.keys(nextLogger.prefixes).forEach((method) => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access nextLogger[method] = getWinstonMethodForNext(method); }); })(); diff --git a/packages/modals/eslint.config.js b/packages/modals/eslint.config.js new file mode 100644 index 000000000..1df6129af --- /dev/null +++ b/packages/modals/eslint.config.js @@ -0,0 +1,5 @@ +import baseConfig from "@homarr/eslint-config/base"; +import reactConfig from "@homarr/eslint-config/react"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig, ...reactConfig]; diff --git a/packages/modals/package.json b/packages/modals/package.json index 9736781c6..0f5498be5 100644 --- a/packages/modals/package.json +++ b/packages/modals/package.json @@ -2,6 +2,7 @@ "name": "@homarr/modals", "private": true, "version": "0.1.0", + "type": "module", "exports": { ".": "./index.ts" }, @@ -15,7 +16,7 @@ "license": "MIT", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -27,13 +28,8 @@ "@homarr/eslint-config": "workspace:^0.2.0", "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, - "eslintConfig": { - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config" } diff --git a/packages/modals/src/confirm-modal.tsx b/packages/modals/src/confirm-modal.tsx index d1434b874..9b26d6508 100644 --- a/packages/modals/src/confirm-modal.tsx +++ b/packages/modals/src/confirm-modal.tsx @@ -41,7 +41,7 @@ export const ConfirmModal = createModal<Omit<ConfirmModalProps, "title">>(({ act const handleCancel = useCallback( async (event: React.MouseEvent<HTMLButtonElement>) => { - typeof cancelProps?.onClick === "function" && cancelProps?.onClick(event); + typeof cancelProps?.onClick === "function" && cancelProps.onClick(event); typeof onCancel === "function" && (await onCancel()); closeOnCancel && actions.closeModal(); }, @@ -51,7 +51,7 @@ export const ConfirmModal = createModal<Omit<ConfirmModalProps, "title">>(({ act const handleConfirm = useCallback( async (event: React.MouseEvent<HTMLButtonElement>) => { setLoading(true); - typeof confirmProps?.onClick === "function" && confirmProps?.onClick(event); + typeof confirmProps?.onClick === "function" && confirmProps.onClick(event); typeof onConfirm === "function" && (await onConfirm()); closeOnConfirm && actions.closeModal(); setLoading(false); @@ -65,11 +65,11 @@ export const ConfirmModal = createModal<Omit<ConfirmModalProps, "title">>(({ act <Group justify="flex-end" {...groupProps}> <Button variant="default" {...cancelProps} onClick={handleCancel}> - {cancelProps?.children || translateIfNecessary(t, cancelLabel)} + {cancelProps?.children ?? translateIfNecessary(t, cancelLabel)} </Button> <Button {...confirmProps} onClick={handleConfirm} color="red.9" loading={loading}> - {confirmProps?.children || translateIfNecessary(t, confirmLabel)} + {confirmProps?.children ?? translateIfNecessary(t, confirmLabel)} </Button> </Group> </> diff --git a/packages/modals/src/index.tsx b/packages/modals/src/index.tsx index 68876a6b6..f3c9e1386 100644 --- a/packages/modals/src/index.tsx +++ b/packages/modals/src/index.tsx @@ -138,6 +138,8 @@ export const useModalAction = <TModal extends ModalDefinition>(modal: TModal) => return { openModal: (innerProps: inferInnerProps<TModal>, options: OpenModalOptions | void) => { + // void actually is undefined + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition context.openModalInner({ modal, innerProps, options: options ?? {} }); }, }; diff --git a/packages/modals/src/reducer.tsx b/packages/modals/src/reducer.tsx index ab45cb386..ba22f7297 100644 --- a/packages/modals/src/reducer.tsx +++ b/packages/modals/src/reducer.tsx @@ -62,7 +62,7 @@ export const modalReducer = (state: ModalsState, action: OpenAction | CloseActio const remainingModals = state.modals.filter((modal) => modal.id !== action.modalId); return { - current: remainingModals[remainingModals.length - 1] || state.current, + current: remainingModals[remainingModals.length - 1] ?? state.current, modals: remainingModals, }; } diff --git a/packages/notifications/eslint.config.js b/packages/notifications/eslint.config.js new file mode 100644 index 000000000..f7a5a7d36 --- /dev/null +++ b/packages/notifications/eslint.config.js @@ -0,0 +1,4 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig]; diff --git a/packages/notifications/package.json b/packages/notifications/package.json index 484472865..d321cec7c 100644 --- a/packages/notifications/package.json +++ b/packages/notifications/package.json @@ -2,6 +2,7 @@ "name": "@homarr/notifications", "private": true, "version": "0.1.0", + "type": "module", "exports": { ".": "./index.ts", "./styles.css": "./src/styles.css" @@ -16,7 +17,7 @@ "license": "MIT", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -24,17 +25,12 @@ "@homarr/eslint-config": "workspace:^0.2.0", "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, "dependencies": { "@mantine/notifications": "^7.10.1", "@homarr/ui": "workspace:^0.1.0" }, - "eslintConfig": { - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config" } diff --git a/packages/ping/eslint.config.js b/packages/ping/eslint.config.js new file mode 100644 index 000000000..f7a5a7d36 --- /dev/null +++ b/packages/ping/eslint.config.js @@ -0,0 +1,4 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig]; diff --git a/packages/ping/package.json b/packages/ping/package.json index 7279a3ef7..e4246da6d 100644 --- a/packages/ping/package.json +++ b/packages/ping/package.json @@ -16,7 +16,7 @@ "type": "module", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -28,13 +28,8 @@ "@homarr/eslint-config": "workspace:^0.2.0", "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, - "eslintConfig": { - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config" } diff --git a/packages/redis/eslint.config.js b/packages/redis/eslint.config.js new file mode 100644 index 000000000..f7a5a7d36 --- /dev/null +++ b/packages/redis/eslint.config.js @@ -0,0 +1,4 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig]; diff --git a/packages/redis/package.json b/packages/redis/package.json index 90be8eef6..a73cc63ae 100644 --- a/packages/redis/package.json +++ b/packages/redis/package.json @@ -16,7 +16,7 @@ "type": "module", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -30,13 +30,8 @@ "@homarr/eslint-config": "workspace:^0.2.0", "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, - "eslintConfig": { - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config" } diff --git a/packages/server-settings/eslint.config.js b/packages/server-settings/eslint.config.js new file mode 100644 index 000000000..f7a5a7d36 --- /dev/null +++ b/packages/server-settings/eslint.config.js @@ -0,0 +1,4 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig]; diff --git a/packages/server-settings/package.json b/packages/server-settings/package.json index 8e0ffc6c8..9e35567f9 100644 --- a/packages/server-settings/package.json +++ b/packages/server-settings/package.json @@ -16,7 +16,7 @@ "license": "MIT", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -24,13 +24,8 @@ "@homarr/eslint-config": "workspace:^0.2.0", "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, - "eslintConfig": { - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config" } diff --git a/packages/spotlight/eslint.config.js b/packages/spotlight/eslint.config.js new file mode 100644 index 000000000..1df6129af --- /dev/null +++ b/packages/spotlight/eslint.config.js @@ -0,0 +1,5 @@ +import baseConfig from "@homarr/eslint-config/base"; +import reactConfig from "@homarr/eslint-config/react"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig, ...reactConfig]; diff --git a/packages/spotlight/package.json b/packages/spotlight/package.json index 218418b3f..770da472a 100644 --- a/packages/spotlight/package.json +++ b/packages/spotlight/package.json @@ -2,6 +2,7 @@ "name": "@homarr/spotlight", "private": true, "version": "0.1.0", + "type": "module", "exports": { ".": "./index.ts", "./styles.css": "./src/styles.css" @@ -16,7 +17,7 @@ "license": "MIT", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -24,14 +25,9 @@ "@homarr/eslint-config": "workspace:^0.2.0", "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, - "eslintConfig": { - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config", "dependencies": { "@mantine/spotlight": "^7.10.1", diff --git a/packages/translation/eslint.config.js b/packages/translation/eslint.config.js new file mode 100644 index 000000000..f7a5a7d36 --- /dev/null +++ b/packages/translation/eslint.config.js @@ -0,0 +1,4 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig]; diff --git a/packages/translation/package.json b/packages/translation/package.json index 506e5e048..65caf72c3 100644 --- a/packages/translation/package.json +++ b/packages/translation/package.json @@ -2,6 +2,7 @@ "name": "@homarr/translation", "private": true, "version": "0.1.0", + "type": "module", "exports": { ".": "./index.ts", "./client": "./src/client.ts", @@ -18,7 +19,7 @@ "license": "MIT", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -26,14 +27,9 @@ "@homarr/eslint-config": "workspace:^0.2.0", "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, - "eslintConfig": { - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config", "dependencies": { "next-international": "^1.2.4" diff --git a/packages/ui/eslint.config.js b/packages/ui/eslint.config.js new file mode 100644 index 000000000..1df6129af --- /dev/null +++ b/packages/ui/eslint.config.js @@ -0,0 +1,5 @@ +import baseConfig from "@homarr/eslint-config/base"; +import reactConfig from "@homarr/eslint-config/react"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig, ...reactConfig]; diff --git a/packages/ui/package.json b/packages/ui/package.json index 783154b52..ba13d2751 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -2,6 +2,7 @@ "name": "@homarr/ui", "private": true, "version": "0.1.0", + "type": "module", "exports": { ".": "./index.ts", "./styles.css": "./src/styles.css" @@ -16,7 +17,7 @@ "license": "MIT", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -25,17 +26,12 @@ "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", "@types/css-modules": "^1.0.5", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, "dependencies": { "@homarr/log": "workspace:^0.1.0", "@homarr/translation": "workspace:^0.1.0" }, - "eslintConfig": { - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config" } diff --git a/packages/ui/src/components/text-multi-select.tsx b/packages/ui/src/components/text-multi-select.tsx index e63d570dc..5561d7baf 100644 --- a/packages/ui/src/components/text-multi-select.tsx +++ b/packages/ui/src/components/text-multi-select.tsx @@ -72,6 +72,7 @@ export const TextMultiSelect = ({ label, value = [], onChange, onBlur, onFocus, onKeyDown={(event) => { if (event.key === "Backspace" && search.length === 0) { event.preventDefault(); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion handleValueRemove(value.at(-1)!); } }} diff --git a/packages/validation/eslint.config.js b/packages/validation/eslint.config.js new file mode 100644 index 000000000..f7a5a7d36 --- /dev/null +++ b/packages/validation/eslint.config.js @@ -0,0 +1,4 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig]; diff --git a/packages/validation/package.json b/packages/validation/package.json index fa45fba35..0f9f72d07 100644 --- a/packages/validation/package.json +++ b/packages/validation/package.json @@ -17,7 +17,7 @@ "license": "MIT", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -25,14 +25,9 @@ "@homarr/eslint-config": "workspace:^0.2.0", "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, - "eslintConfig": { - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config", "dependencies": { "zod": "^3.23.8", diff --git a/packages/validation/src/enums.ts b/packages/validation/src/enums.ts index f9e59ef89..988198adc 100644 --- a/packages/validation/src/enums.ts +++ b/packages/validation/src/enums.ts @@ -3,7 +3,9 @@ import { z } from "zod"; type CouldBeReadonlyArray<T> = T[] | readonly T[]; export const zodEnumFromArray = <T extends string>(array: CouldBeReadonlyArray<T>) => + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion z.enum([array[0]!, ...array.slice(1)]); export const zodUnionFromArray = <T extends z.ZodTypeAny>(array: CouldBeReadonlyArray<T>) => + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion z.union([array[0]!, array[1]!, ...array.slice(2)]); diff --git a/packages/validation/src/form/i18n.ts b/packages/validation/src/form/i18n.ts index 08f97e988..5a8e4f65d 100644 --- a/packages/validation/src/form/i18n.ts +++ b/packages/validation/src/form/i18n.ts @@ -13,7 +13,7 @@ export const zodErrorMap = < const error = handleZodError(issue, ctx); if ("message" in error && error.message) return { - message: error.message ?? ctx.defaultError, + message: error.message, }; return { message: t(error.key ? `common.zod.${error.key}` : "common.zod.errors.default", error.params ?? {}), diff --git a/packages/widgets/eslint.config.js b/packages/widgets/eslint.config.js new file mode 100644 index 000000000..1df6129af --- /dev/null +++ b/packages/widgets/eslint.config.js @@ -0,0 +1,5 @@ +import baseConfig from "@homarr/eslint-config/base"; +import reactConfig from "@homarr/eslint-config/react"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig, ...reactConfig]; diff --git a/packages/widgets/package.json b/packages/widgets/package.json index 42ffa2132..e97317dfd 100644 --- a/packages/widgets/package.json +++ b/packages/widgets/package.json @@ -2,6 +2,7 @@ "name": "@homarr/widgets", "private": true, "version": "0.1.0", + "type": "module", "exports": { ".": "./index.ts", "./errors": "./src/errors/component.tsx" @@ -16,7 +17,7 @@ "license": "MIT", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -26,14 +27,9 @@ "@homarr/tsconfig": "workspace:^0.1.0", "@types/prismjs": "^1.26.4", "@types/video.js": "^7.3.58", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, - "eslintConfig": { - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config", "dependencies": { "@homarr/api": "workspace:^0.1.0", diff --git a/packages/widgets/src/_inputs/widget-multiselect-input.tsx b/packages/widgets/src/_inputs/widget-multiselect-input.tsx index 9f3db9e92..6793dade3 100644 --- a/packages/widgets/src/_inputs/widget-multiselect-input.tsx +++ b/packages/widgets/src/_inputs/widget-multiselect-input.tsx @@ -20,7 +20,7 @@ export const WidgetMultiSelectInput = ({ property, kind, options }: CommonWidget ? option : { value: option.value, - label: translateIfNecessary(t, option.label)!, + label: translateIfNecessary(t, option.label) ?? option.value, }, )} description={options.withDescription ? t("description") : undefined} diff --git a/packages/widgets/src/_inputs/widget-select-input.tsx b/packages/widgets/src/_inputs/widget-select-input.tsx index fd1df1d8f..ec07b2932 100644 --- a/packages/widgets/src/_inputs/widget-select-input.tsx +++ b/packages/widgets/src/_inputs/widget-select-input.tsx @@ -36,7 +36,7 @@ export const WidgetSelectInput = ({ property, kind, options }: CommonWidgetInput ? option : { value: option.value, - label: translateIfNecessary(t, option.label)!, + label: translateIfNecessary(t, option.label) ?? option.value, }, )} description={options.withDescription ? tWidget("description") : undefined} diff --git a/packages/widgets/src/app/component.tsx b/packages/widgets/src/app/component.tsx index 0c47588dc..8838e6436 100644 --- a/packages/widgets/src/app/component.tsx +++ b/packages/widgets/src/app/component.tsx @@ -27,7 +27,7 @@ export default function AppWidget({ options, serverData, isEditMode, width, heig { initialData: // We need to check if the id's match because otherwise the same initialData for a changed id will be used - serverData?.app?.id === options.appId ? serverData?.app : undefined, + serverData?.app?.id === options.appId ? serverData.app : undefined, refetchOnMount: false, refetchOnWindowFocus: false, refetchOnReconnect: false, @@ -54,12 +54,12 @@ export default function AppWidget({ options, serverData, isEditMode, width, heig ? [ { id: `app-${options.appId}`, - title: app?.name, - description: app?.description ?? "", - icon: app?.iconUrl, + title: app.name, + description: app.description ?? "", + icon: app.iconUrl, group: "app", type: "link", - href: app?.href, + href: app.href, openInNewTab: options.openInNewTab, }, ] diff --git a/packages/widgets/src/modals/widget-edit-modal.tsx b/packages/widgets/src/modals/widget-edit-modal.tsx index 10dc57a02..4330e2752 100644 --- a/packages/widgets/src/modals/widget-edit-modal.tsx +++ b/packages/widgets/src/modals/widget-edit-modal.tsx @@ -62,6 +62,7 @@ export const WidgetEditModal = createModal<ModalProps<WidgetKind>>(({ actions, i {Object.entries(definition.options).map(([key, value]: [string, OptionsBuilderResult[string]]) => { const Input = getInputForType(value.type); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!Input || value.shouldHide?.(form.values.options as never)) { return null; } diff --git a/packages/widgets/src/notebook/notebook.tsx b/packages/widgets/src/notebook/notebook.tsx index e7967d81f..7ca935bc6 100644 --- a/packages/widgets/src/notebook/notebook.tsx +++ b/packages/widgets/src/notebook/notebook.tsx @@ -303,7 +303,9 @@ export function Notebook({ options, isEditMode, boardId, itemId }: WidgetCompone <RichTextEditor.BulletList title={tControls("bulletList")} /> <RichTextEditor.OrderedList title={tControls("orderedList")} /> <TaskListToggle /> - {(editor?.isActive("taskList") || editor?.isActive("bulletList") || editor?.isActive("orderedList")) && ( + {(Boolean(editor?.isActive("taskList")) || + Boolean(editor?.isActive("bulletList")) || + Boolean(editor?.isActive("orderedList"))) && ( <> <ListIndentIncrease /> <ListIndentDecrease /> @@ -680,7 +682,7 @@ function ListIndentIncrease() { }, [editor, itemType]); editor?.on("selectionUpdate", ({ editor }) => { - setItemType(editor?.isActive("taskItem") ? "taskItem" : "listItem"); + setItemType(editor.isActive("taskItem") ? "taskItem" : "listItem"); }); return ( @@ -704,7 +706,7 @@ function ListIndentDecrease() { }, [editor, itemType]); editor?.on("selectionUpdate", ({ editor }) => { - setItemType(editor?.isActive("taskItem") ? "taskItem" : "listItem"); + setItemType(editor.isActive("taskItem") ? "taskItem" : "listItem"); }); return ( diff --git a/packages/widgets/src/widget-integration-select.tsx b/packages/widgets/src/widget-integration-select.tsx index 5014034e0..39bef4d2d 100644 --- a/packages/widgets/src/widget-integration-select.tsx +++ b/packages/widgets/src/widget-integration-select.tsx @@ -98,6 +98,7 @@ export const WidgetIntegrationSelect = ({ if (event.key !== "Backspace") return; event.preventDefault(); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion handleValueRemove(multiSelectValues[multiSelectValues.length - 1]!); }} /> diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bfaa0f5f0..e975568bf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -233,8 +233,8 @@ importers: specifier: ^8.2.2 version: 8.2.2 eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 node-loader: specifier: ^2.0.0 version: 2.0.0(webpack@5.91.0) @@ -309,8 +309,8 @@ importers: specifier: ^7.4.2 version: 7.4.2 eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 prettier: specifier: ^3.3.1 version: 3.3.1 @@ -367,8 +367,8 @@ importers: specifier: ^8.5.10 version: 8.5.10 eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 prettier: specifier: ^3.3.1 version: 3.3.1 @@ -404,8 +404,8 @@ importers: specifier: workspace:^0.1.0 version: link:../../tooling/typescript eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -471,8 +471,8 @@ importers: specifier: ^3.3.29 version: 3.3.29 eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 prettier: specifier: ^3.3.1 version: 3.3.1 @@ -535,8 +535,8 @@ importers: specifier: 0.9.0 version: 0.9.0 eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 prettier: specifier: ^3.3.1 version: 3.3.1 @@ -556,8 +556,8 @@ importers: specifier: workspace:^0.1.0 version: link:../../tooling/typescript eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -605,8 +605,8 @@ importers: specifier: ^7.4.2 version: 7.4.2 eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 prettier: specifier: ^3.3.1 version: 3.3.1 @@ -630,8 +630,8 @@ importers: specifier: workspace:^0.1.0 version: link:../../tooling/typescript eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -658,8 +658,8 @@ importers: specifier: workspace:^0.1.0 version: link:../../tooling/typescript eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -680,8 +680,8 @@ importers: specifier: workspace:^0.1.0 version: link:../../tooling/typescript eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -705,8 +705,8 @@ importers: specifier: workspace:^0.1.0 version: link:../../tooling/typescript eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -733,8 +733,8 @@ importers: specifier: workspace:^0.1.0 version: link:../../tooling/typescript eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -758,8 +758,8 @@ importers: specifier: workspace:^0.1.0 version: link:../../tooling/typescript eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -783,8 +783,8 @@ importers: specifier: workspace:^0.1.0 version: link:../../tooling/typescript eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -808,8 +808,8 @@ importers: specifier: workspace:^0.1.0 version: link:../../tooling/typescript eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -839,8 +839,8 @@ importers: specifier: workspace:^0.1.0 version: link:../../tooling/typescript eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -857,8 +857,8 @@ importers: specifier: workspace:^0.1.0 version: link:../../tooling/typescript eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -885,8 +885,8 @@ importers: specifier: workspace:^0.1.0 version: link:../../tooling/typescript eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -907,8 +907,8 @@ importers: specifier: workspace:^0.1.0 version: link:../../tooling/typescript eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -935,8 +935,8 @@ importers: specifier: ^1.0.5 version: 1.0.5 eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -963,8 +963,8 @@ importers: specifier: workspace:^0.1.0 version: link:../../tooling/typescript eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -1078,8 +1078,8 @@ importers: specifier: ^7.3.58 version: 7.3.58 eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -1089,30 +1089,27 @@ importers: '@next/eslint-plugin-next': specifier: ^14.2.3 version: 14.2.3 - '@typescript-eslint/eslint-plugin': - specifier: ^7.12.0 - version: 7.12.0(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/parser': - specifier: ^7.12.0 - version: 7.12.0(eslint@8.57.0)(typescript@5.4.5) eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.0(eslint@8.57.0) + version: 9.1.0(eslint@9.4.0) eslint-config-turbo: specifier: ^2.0.3 - version: 2.0.3(eslint@8.57.0) + version: 2.0.3(eslint@9.4.0) eslint-plugin-import: specifier: ^2.29.1 - version: 2.29.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0) + version: 2.29.1(@typescript-eslint/parser@7.12.0(eslint@9.4.0)(typescript@5.4.5))(eslint@9.4.0) eslint-plugin-jsx-a11y: specifier: ^6.8.0 - version: 6.8.0(eslint@8.57.0) + version: 6.8.0(eslint@9.4.0) eslint-plugin-react: specifier: ^7.34.2 - version: 7.34.2(eslint@8.57.0) + version: 7.34.2(eslint@9.4.0) eslint-plugin-react-hooks: specifier: ^4.6.2 - version: 4.6.2(eslint@8.57.0) + version: 4.6.2(eslint@9.4.0) + typescript-eslint: + specifier: ^7.12.0 + version: 7.12.0(eslint@9.4.0)(typescript@5.4.5) devDependencies: '@homarr/prettier-config': specifier: workspace:^0.1.0 @@ -1120,12 +1117,9 @@ importers: '@homarr/tsconfig': specifier: workspace:^0.1.0 version: link:../typescript - '@types/eslint': - specifier: ^8.56.10 - version: 8.56.10 eslint: - specifier: ^8.57.0 - version: 8.57.0 + specifier: ^9.4.0 + version: 9.4.0 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -1826,13 +1820,21 @@ packages: resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/eslintrc@2.1.4': - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/config-array@0.15.1': + resolution: {integrity: sha512-K4gzNq+yymn/EVsXYmf+SBcBro8MTf+aXJZUphM96CdzUEr+ClGDvAbpmaEK+cGVigVXIgs9gNmvHAlrzzY5JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@8.57.0': - resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/eslintrc@3.1.0': + resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.4.0': + resolution: {integrity: sha512-fdI7VJjP3Rvc70lC4xkFXHB0fiPeojiL1PxVG6t1ZvXQrarj893PweuBTujxDUFk0Fxj4R7PIIAZ/aiiyZPZcg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.3': + resolution: {integrity: sha512-HAbhAYKfsAC2EkTqve00ibWIZlaU74Z1EHwAjYr4PXF0YU2VEA1zSIKSSpKszRLRWwHzzRZXvK632u+uXzvsvw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@floating-ui/core@1.6.0': resolution: {integrity: sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==} @@ -1858,16 +1860,13 @@ packages: '@homarr/gridstack@1.0.0': resolution: {integrity: sha512-KM9024BipLD9BmtM6jHI8OKLZ1Iy4vZdTfU53ww4qEda/330XQYhIC2SBcQgkNnDB2MTkn/laNSO5gTy+lJg9Q==} - '@humanwhocodes/config-array@0.11.14': - resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} - engines: {node: '>=10.10.0'} - '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/object-schema@2.0.2': - resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} + '@humanwhocodes/retry@0.3.0': + resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} + engines: {node: '>=18.18'} '@ianvs/prettier-plugin-sort-imports@4.2.1': resolution: {integrity: sha512-NKN1LVFWUDGDGr3vt+6Ey3qPeN/163uR1pOPAlkWpgvAqgxQ6kSdUf1F0it8aHUtKRUzEGcK38Wxd07O61d7+Q==} @@ -2704,9 +2703,6 @@ packages: '@umami/node@0.3.0': resolution: {integrity: sha512-+1cZ7o7jVN8oXDYZRqigfLHrWbEv5vtGWjB7blfVH1QUa+DRmWB6GfhMZtE2aSW+P9ACal8ZW7xD2PCAejlNCQ==} - '@ungap/structured-clone@1.2.0': - resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - '@videojs/http-streaming@3.12.1': resolution: {integrity: sha512-rpB5AMt0QZ9bMXzwiWhynF2NLNnm5g2DZjPOFX6OoFqqXhbe2ngY2nqm9lLRhRVe22YeysQCmAlvBNwGuWFI8Q==} engines: {node: '>=8', npm: '>=5'} @@ -3410,10 +3406,6 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - dom-helpers@5.2.1: resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} @@ -3714,22 +3706,26 @@ packages: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@8.0.1: + resolution: {integrity: sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint@8.57.0: - resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-visitor-keys@4.0.0: + resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.4.0: + resolution: {integrity: sha512-sjc7Y8cUD1IlwYcTS9qPSvGjAC8Ne9LctpxKKu3x/1IC9bnOg98Zy6GxEJUfr1NojMgVPlyANXYns8oE2c1TAA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + espree@10.0.1: + resolution: {integrity: sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} @@ -3805,9 +3801,9 @@ packages: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} @@ -3823,9 +3819,9 @@ packages: flag-icons@7.2.3: resolution: {integrity: sha512-X2gUdteNuqdNqob2KKTJTS+ZCvyWeLCtDz9Ty8uJP17Y4o82Y+U/Vd4JNrdwTAjagYsRznOn9DZ+E/Q52qbmqg==} - flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} flatted@3.2.9: resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} @@ -3958,9 +3954,9 @@ packages: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} - globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} globalthis@1.0.3: resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} @@ -5841,10 +5837,6 @@ packages: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} @@ -5888,6 +5880,16 @@ packages: resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} engines: {node: '>= 0.4'} + typescript-eslint@7.12.0: + resolution: {integrity: sha512-D6HKNbQcnNu3BaN4HkQCR16tgG8Q2AMUWPgvhrJksOXu+d6ys07yC06ONiV2kcsEfWC22voB6C3PvK2MqlBZ7w==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + typescript@5.4.5: resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} engines: {node: '>=14.17'} @@ -6783,19 +6785,27 @@ snapshots: '@esbuild/win32-x64@0.20.2': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': + '@eslint-community/eslint-utils@4.4.0(eslint@9.4.0)': dependencies: - eslint: 8.57.0 + eslint: 9.4.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.10.0': {} - '@eslint/eslintrc@2.1.4': + '@eslint/config-array@0.15.1': + dependencies: + '@eslint/object-schema': 2.1.3 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/eslintrc@3.1.0': dependencies: ajv: 6.12.6 debug: 4.3.4 - espree: 9.6.1 - globals: 13.24.0 + espree: 10.0.1 + globals: 14.0.0 ignore: 5.3.1 import-fresh: 3.3.0 js-yaml: 4.1.0 @@ -6804,7 +6814,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@8.57.0': {} + '@eslint/js@9.4.0': {} + + '@eslint/object-schema@2.1.3': {} '@floating-ui/core@1.6.0': dependencies: @@ -6833,17 +6845,9 @@ snapshots: '@homarr/gridstack@1.0.0': {} - '@humanwhocodes/config-array@0.11.14': - dependencies: - '@humanwhocodes/object-schema': 2.0.2 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/object-schema@2.0.2': {} + '@humanwhocodes/retry@0.3.0': {} '@ianvs/prettier-plugin-sort-imports@4.2.1(prettier@3.3.1)': dependencies: @@ -7637,15 +7641,15 @@ snapshots: dependencies: '@types/node': 20.14.2 - '@typescript-eslint/eslint-plugin@7.12.0(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)': + '@typescript-eslint/eslint-plugin@7.12.0(@typescript-eslint/parser@7.12.0(eslint@9.4.0)(typescript@5.4.5))(eslint@9.4.0)(typescript@5.4.5)': dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.12.0(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/parser': 7.12.0(eslint@9.4.0)(typescript@5.4.5) '@typescript-eslint/scope-manager': 7.12.0 - '@typescript-eslint/type-utils': 7.12.0(eslint@8.57.0)(typescript@5.4.5) - '@typescript-eslint/utils': 7.12.0(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/type-utils': 7.12.0(eslint@9.4.0)(typescript@5.4.5) + '@typescript-eslint/utils': 7.12.0(eslint@9.4.0)(typescript@5.4.5) '@typescript-eslint/visitor-keys': 7.12.0 - eslint: 8.57.0 + eslint: 9.4.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 @@ -7655,14 +7659,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5)': + '@typescript-eslint/parser@7.12.0(eslint@9.4.0)(typescript@5.4.5)': dependencies: '@typescript-eslint/scope-manager': 7.12.0 '@typescript-eslint/types': 7.12.0 '@typescript-eslint/typescript-estree': 7.12.0(typescript@5.4.5) '@typescript-eslint/visitor-keys': 7.12.0 debug: 4.3.4 - eslint: 8.57.0 + eslint: 9.4.0 optionalDependencies: typescript: 5.4.5 transitivePeerDependencies: @@ -7673,12 +7677,12 @@ snapshots: '@typescript-eslint/types': 7.12.0 '@typescript-eslint/visitor-keys': 7.12.0 - '@typescript-eslint/type-utils@7.12.0(eslint@8.57.0)(typescript@5.4.5)': + '@typescript-eslint/type-utils@7.12.0(eslint@9.4.0)(typescript@5.4.5)': dependencies: '@typescript-eslint/typescript-estree': 7.12.0(typescript@5.4.5) - '@typescript-eslint/utils': 7.12.0(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/utils': 7.12.0(eslint@9.4.0)(typescript@5.4.5) debug: 4.3.4 - eslint: 8.57.0 + eslint: 9.4.0 ts-api-utils: 1.3.0(typescript@5.4.5) optionalDependencies: typescript: 5.4.5 @@ -7702,13 +7706,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.12.0(eslint@8.57.0)(typescript@5.4.5)': + '@typescript-eslint/utils@7.12.0(eslint@9.4.0)(typescript@5.4.5)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.4.0) '@typescript-eslint/scope-manager': 7.12.0 '@typescript-eslint/types': 7.12.0 '@typescript-eslint/typescript-estree': 7.12.0(typescript@5.4.5) - eslint: 8.57.0 + eslint: 9.4.0 transitivePeerDependencies: - supports-color - typescript @@ -7720,8 +7724,6 @@ snapshots: '@umami/node@0.3.0': {} - '@ungap/structured-clone@1.2.0': {} - '@videojs/http-streaming@3.12.1(video.js@8.12.0)': dependencies: '@babel/runtime': 7.23.9 @@ -8554,10 +8556,6 @@ snapshots: dependencies: esutils: 2.0.3 - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 - dom-helpers@5.2.1: dependencies: '@babel/runtime': 7.23.9 @@ -8883,14 +8881,14 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-prettier@9.1.0(eslint@8.57.0): + eslint-config-prettier@9.1.0(eslint@9.4.0): dependencies: - eslint: 8.57.0 + eslint: 9.4.0 - eslint-config-turbo@2.0.3(eslint@8.57.0): + eslint-config-turbo@2.0.3(eslint@9.4.0): dependencies: - eslint: 8.57.0 - eslint-plugin-turbo: 2.0.3(eslint@8.57.0) + eslint: 9.4.0 + eslint-plugin-turbo: 2.0.3(eslint@9.4.0) eslint-import-resolver-node@0.3.9: dependencies: @@ -8900,17 +8898,17 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.0(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): + eslint-module-utils@2.8.0(@typescript-eslint/parser@7.12.0(eslint@9.4.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@9.4.0): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 7.12.0(eslint@8.57.0)(typescript@5.4.5) - eslint: 8.57.0 + '@typescript-eslint/parser': 7.12.0(eslint@9.4.0)(typescript@5.4.5) + eslint: 9.4.0 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.12.0(eslint@9.4.0)(typescript@5.4.5))(eslint@9.4.0): dependencies: array-includes: 3.1.7 array.prototype.findlastindex: 1.2.4 @@ -8918,9 +8916,9 @@ snapshots: array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.57.0 + eslint: 9.4.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.12.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.12.0(eslint@9.4.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@9.4.0) hasown: 2.0.1 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -8931,13 +8929,13 @@ snapshots: semver: 6.3.1 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 7.12.0(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/parser': 7.12.0(eslint@9.4.0)(typescript@5.4.5) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.8.0(eslint@8.57.0): + eslint-plugin-jsx-a11y@6.8.0(eslint@9.4.0): dependencies: '@babel/runtime': 7.23.9 aria-query: 5.3.0 @@ -8949,7 +8947,7 @@ snapshots: damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 es-iterator-helpers: 1.0.17 - eslint: 8.57.0 + eslint: 9.4.0 hasown: 2.0.1 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -8957,11 +8955,11 @@ snapshots: object.entries: 1.1.7 object.fromentries: 2.0.7 - eslint-plugin-react-hooks@4.6.2(eslint@8.57.0): + eslint-plugin-react-hooks@4.6.2(eslint@9.4.0): dependencies: - eslint: 8.57.0 + eslint: 9.4.0 - eslint-plugin-react@7.34.2(eslint@8.57.0): + eslint-plugin-react@7.34.2(eslint@9.4.0): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 @@ -8970,7 +8968,7 @@ snapshots: array.prototype.tosorted: 1.1.3 doctrine: 2.1.0 es-iterator-helpers: 1.0.19 - eslint: 8.57.0 + eslint: 9.4.0 estraverse: 5.3.0 jsx-ast-utils: 3.3.5 minimatch: 3.1.2 @@ -8983,55 +8981,53 @@ snapshots: semver: 6.3.1 string.prototype.matchall: 4.0.11 - eslint-plugin-turbo@2.0.3(eslint@8.57.0): + eslint-plugin-turbo@2.0.3(eslint@9.4.0): dependencies: dotenv: 16.0.3 - eslint: 8.57.0 + eslint: 9.4.0 eslint-scope@5.1.1: dependencies: esrecurse: 4.3.0 estraverse: 4.3.0 - eslint-scope@7.2.2: + eslint-scope@8.0.1: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 eslint-visitor-keys@3.4.3: {} - eslint@8.57.0: + eslint-visitor-keys@4.0.0: {} + + eslint@9.4.0: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.4.0) '@eslint-community/regexpp': 4.10.0 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.0 - '@humanwhocodes/config-array': 0.11.14 + '@eslint/config-array': 0.15.1 + '@eslint/eslintrc': 3.1.0 + '@eslint/js': 9.4.0 '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.3.0 '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.2.0 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 debug: 4.3.4 - doctrine: 3.0.0 escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 + eslint-scope: 8.0.1 + eslint-visitor-keys: 4.0.0 + espree: 10.0.1 esquery: 1.5.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 + file-entry-cache: 8.0.0 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 ignore: 5.3.1 imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 - js-yaml: 4.1.0 json-stable-stringify-without-jsonify: 1.0.1 levn: 0.4.1 lodash.merge: 4.6.2 @@ -9043,11 +9039,11 @@ snapshots: transitivePeerDependencies: - supports-color - espree@9.6.1: + espree@10.0.1: dependencies: acorn: 8.11.3 acorn-jsx: 5.3.2(acorn@8.11.3) - eslint-visitor-keys: 3.4.3 + eslint-visitor-keys: 4.0.0 esprima@4.0.1: {} @@ -9129,9 +9125,9 @@ snapshots: dependencies: escape-string-regexp: 1.0.5 - file-entry-cache@6.0.1: + file-entry-cache@8.0.0: dependencies: - flat-cache: 3.2.0 + flat-cache: 4.0.1 file-uri-to-path@1.0.0: {} @@ -9146,11 +9142,10 @@ snapshots: flag-icons@7.2.3: {} - flat-cache@3.2.0: + flat-cache@4.0.1: dependencies: flatted: 3.2.9 keyv: 4.5.4 - rimraf: 3.0.2 flatted@3.2.9: {} @@ -9304,9 +9299,7 @@ snapshots: globals@11.12.0: {} - globals@13.24.0: - dependencies: - type-fest: 0.20.2 + globals@14.0.0: {} globalthis@1.0.3: dependencies: @@ -11338,8 +11331,6 @@ snapshots: type-detect@4.0.8: {} - type-fest@0.20.2: {} - type-fest@0.21.3: {} type-fest@2.19.0: {} @@ -11405,6 +11396,17 @@ snapshots: is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 + typescript-eslint@7.12.0(eslint@9.4.0)(typescript@5.4.5): + dependencies: + '@typescript-eslint/eslint-plugin': 7.12.0(@typescript-eslint/parser@7.12.0(eslint@9.4.0)(typescript@5.4.5))(eslint@9.4.0)(typescript@5.4.5) + '@typescript-eslint/parser': 7.12.0(eslint@9.4.0)(typescript@5.4.5) + '@typescript-eslint/utils': 7.12.0(eslint@9.4.0)(typescript@5.4.5) + eslint: 9.4.0 + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + typescript@5.4.5: {} uc.micro@2.1.0: {} diff --git a/tooling/eslint/base.js b/tooling/eslint/base.js index bb154044f..118762731 100644 --- a/tooling/eslint/base.js +++ b/tooling/eslint/base.js @@ -1,80 +1,90 @@ -/** @type {import("eslint").Linter.Config} */ -const config = { - extends: [ - "turbo", - "eslint:recommended", - "plugin:@typescript-eslint/recommended-type-checked", - "plugin:@typescript-eslint/stylistic-type-checked", - "prettier", - ], - env: { - es2022: true, - node: true, - }, - parser: "@typescript-eslint/parser", - parserOptions: { - project: true, - }, - plugins: ["@typescript-eslint", "import"], - rules: { - "id-length": [ - "warn", - { - min: 3, - exceptions: ["_", "i", "z", "t", "id", "db"], // _ for unused variables, i for index, z for zod, t for translation - properties: "never", // This allows for example the use of <Grid.Col span={{ sm: 12, md: 6 }}> as sm and md would be too short - }, - ], - "@typescript-eslint/prefer-nullish-coalescing": "off", - "turbo/no-undeclared-env-vars": "off", - "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }], - "@typescript-eslint/consistent-type-imports": [ - "warn", - { prefer: "type-imports", fixStyle: "separate-type-imports" }, - ], - "@typescript-eslint/no-misused-promises": [2, { checksVoidReturn: { attributes: false } }], - "import/consistent-type-specifier-style": ["error", "prefer-top-level"], - "no-restricted-syntax": [ - "error", - { - selector: "FunctionDeclaration[async=false][id.name=/Async$/]", - message: "Function ending in 'Async' must be declared async", - }, - { - selector: - "FunctionDeclaration[async=true][id.name=/^[a-z].*$/][id.name=/ ^(?!generateMetadata$)[a-z].*$/][id.name!=/Async$/]", - message: "Async function name must end in 'Async' (function declaration)", - }, - { - selector: "MethodDefinition[value.async=false][key.name=/Async$/]", - message: "Method ending in 'Async' must be declared async", - }, - { - selector: "MethodDefinition[value.async=true][key.name!=/Async$/]", - message: "Async method name must end in 'Async'", - }, - { - selector: "Property[value.type=/FunctionExpression$/][value.async=false][key.name=/Async$/]", - message: "Function ending in 'Async' must be declared async", - }, - { - selector: - "Property[value.type=/FunctionExpression$/][value.async=true][key.name!=/^on(Success|Settled)$/][key.name!=/Async$/]", - message: "Async function name must end in 'Async' (property)", - }, - { - selector: "VariableDeclarator[init.type=/FunctionExpression$/][init.async=false][id.name=/Async$/]", - message: "Function ending in 'Async' must be declared async", - }, - { - selector: - "VariableDeclarator[init.type=/FunctionExpression$/][init.async=true][id.name=/^[a-z].*$/][id.name!=/Async$/]", - message: "Async function name must end in 'Async' (variable declarator)", - }, - ], - }, - ignorePatterns: ["**/.eslintrc.cjs", "**/*.config.js", "**/*.config.cjs", ".next", "dist", "pnpm-lock.yaml"], - reportUnusedDisableDirectives: true, -}; +/// <reference types="./types.d.ts" /> -module.exports = config; +import eslint from "@eslint/js"; +import importPlugin from "eslint-plugin-import"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + { + // Globally ignored files + ignores: ["**/*.config.js"], + }, + { + files: ["**/*.js", "**/*.ts", "**/*.tsx"], + plugins: { + import: importPlugin, + }, + extends: [ + eslint.configs.recommended, + ...tseslint.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + ...tseslint.configs.stylisticTypeChecked, + ], + rules: { + "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }], + "@typescript-eslint/consistent-type-imports": [ + "warn", + { prefer: "type-imports", fixStyle: "separate-type-imports" }, + ], + "@typescript-eslint/no-misused-promises": [2, { checksVoidReturn: { attributes: false } }], + "@typescript-eslint/no-unnecessary-condition": [ + "error", + { + allowConstantLoopConditions: true, + }, + ], + "@typescript-eslint/no-non-null-assertion": "error", + "import/consistent-type-specifier-style": ["error", "prefer-top-level"], + "id-length": [ + "warn", + { + min: 3, + exceptions: ["_", "i", "z", "t", "id", "db"], // _ for unused variables, i for index, z for zod, t for translation + properties: "never", // This allows for example the use of <Grid.Col span={{ sm: 12, md: 6 }}> as sm and md would be too short + }, + ], + "no-restricted-syntax": [ + "error", + { + selector: "FunctionDeclaration[async=false][id.name=/Async$/]", + message: "Function ending in 'Async' must be declared async", + }, + { + selector: + "FunctionDeclaration[async=true][id.name=/^[a-z].*$/][id.name=/ ^(?!generateMetadata$)[a-z].*$/][id.name!=/Async$/]", + message: "Async function name must end in 'Async' (function declaration)", + }, + { + selector: "MethodDefinition[value.async=false][key.name=/Async$/]", + message: "Method ending in 'Async' must be declared async", + }, + { + selector: "MethodDefinition[value.async=true][key.name!=/Async$/]", + message: "Async method name must end in 'Async'", + }, + { + selector: "Property[value.type=/FunctionExpression$/][value.async=false][key.name=/Async$/]", + message: "Function ending in 'Async' must be declared async", + }, + { + selector: + "Property[value.type=/FunctionExpression$/][value.async=true][key.name!=/^on(Success|Settled)$/][key.name!=/Async$/]", + message: "Async function name must end in 'Async' (property)", + }, + { + selector: "VariableDeclarator[init.type=/FunctionExpression$/][init.async=false][id.name=/Async$/]", + message: "Function ending in 'Async' must be declared async", + }, + { + selector: + "VariableDeclarator[init.type=/FunctionExpression$/][init.async=true][id.name=/^[a-z].*$/][id.name!=/Async$/]", + message: "Async function name must end in 'Async' (variable declarator)", + }, + ], + }, + }, + { + linterOptions: { reportUnusedDisableDirectives: true }, + languageOptions: { parserOptions: { project: true } }, + }, +); diff --git a/tooling/eslint/eslint.config.js b/tooling/eslint/eslint.config.js new file mode 100644 index 000000000..f7a5a7d36 --- /dev/null +++ b/tooling/eslint/eslint.config.js @@ -0,0 +1,4 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [...baseConfig]; diff --git a/tooling/eslint/nextjs.js b/tooling/eslint/nextjs.js index 278d86600..b876fc84e 100644 --- a/tooling/eslint/nextjs.js +++ b/tooling/eslint/nextjs.js @@ -1,9 +1,17 @@ -/** @type {import('eslint').Linter.Config} */ -const config = { - extends: ["plugin:@next/next/recommended"], - rules: { - "@next/next/no-html-link-for-pages": "off", - }, -}; +import nextPlugin from "@next/eslint-plugin-next"; -module.exports = config; +/** @type {Awaited<import('typescript-eslint').Config>} */ +export default [ + { + files: ["**/*.ts", "**/*.tsx"], + plugins: { + "@next/next": nextPlugin, + }, + rules: { + ...nextPlugin.configs.recommended.rules, + ...nextPlugin.configs["core-web-vitals"].rules, + // TypeError: context.getAncestors is not a function + "@next/next/no-duplicate-head": "off", + }, + }, +]; diff --git a/tooling/eslint/package.json b/tooling/eslint/package.json index 842227482..18f52c7ec 100644 --- a/tooling/eslint/package.json +++ b/tooling/eslint/package.json @@ -3,40 +3,33 @@ "version": "0.2.0", "private": true, "license": "MIT", - "files": [ - "./base.js", - "./nextjs.js", - "./react.js" - ], + "type": "module", + "exports": { + "./base": "./base.js", + "./nextjs": "./nextjs.js", + "./react": "./react.js" + }, "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, "dependencies": { "@next/eslint-plugin-next": "^14.2.3", - "@typescript-eslint/eslint-plugin": "^7.12.0", - "@typescript-eslint/parser": "^7.12.0", "eslint-config-prettier": "^9.1.0", "eslint-config-turbo": "^2.0.3", "eslint-plugin-import": "^2.29.1", "eslint-plugin-jsx-a11y": "^6.8.0", "eslint-plugin-react": "^7.34.2", - "eslint-plugin-react-hooks": "^4.6.2" + "eslint-plugin-react-hooks": "^4.6.2", + "typescript-eslint": "^7.12.0" }, "devDependencies": { - "@types/eslint": "^8.56.10", "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, - "eslintConfig": { - "root": true, - "extends": [ - "./base.js" - ] - }, "prettier": "@homarr/prettier-config" } diff --git a/tooling/eslint/react.js b/tooling/eslint/react.js index ee21a219f..a87b5c165 100644 --- a/tooling/eslint/react.js +++ b/tooling/eslint/react.js @@ -1,20 +1,24 @@ -/** @type {import('eslint').Linter.Config} */ -const config = { - extends: ["plugin:react/recommended", "plugin:react-hooks/recommended", "plugin:jsx-a11y/recommended"], - rules: { - "react/prop-types": "off", - }, - globals: { - React: "writable", - }, - settings: { - react: { - version: "detect", +import reactPlugin from "eslint-plugin-react"; +import hooksPlugin from "eslint-plugin-react-hooks"; + +/** @type {Awaited<import('typescript-eslint').Config>} */ +export default [ + { + files: ["**/*.ts", "**/*.tsx"], + plugins: { + react: reactPlugin, + "react-hooks": hooksPlugin, + }, + rules: { + ...reactPlugin.configs["jsx-runtime"].rules, + ...hooksPlugin.configs.recommended.rules, + // context.getSource is not a function + "react-hooks/exhaustive-deps": "off", + }, + languageOptions: { + globals: { + React: "writable", + }, }, }, - env: { - browser: true, - }, -}; - -module.exports = config; +]; diff --git a/tooling/eslint/types.d.ts b/tooling/eslint/types.d.ts new file mode 100644 index 000000000..37cfded93 --- /dev/null +++ b/tooling/eslint/types.d.ts @@ -0,0 +1,58 @@ +/** + * Since the ecosystem hasn't fully migrated to ESLint's new FlatConfig system yet, + * we "need" to type some of the plugins manually :( + */ + +declare module "@eslint/js" { + // Why the hell doesn't eslint themselves export their types? + import type { Linter } from "eslint"; + + export const configs: { + readonly recommended: { readonly rules: Readonly<Linter.RulesRecord> }; + readonly all: { readonly rules: Readonly<Linter.RulesRecord> }; + }; +} + +declare module "eslint-plugin-import" { + import type { Linter, Rule } from "eslint"; + + export const configs: { + recommended: { rules: Linter.RulesRecord }; + }; + export const rules: Record<string, Rule.RuleModule>; +} + +declare module "eslint-plugin-react" { + import type { Linter, Rule } from "eslint"; + + export const configs: { + recommended: { rules: Linter.RulesRecord }; + all: { rules: Linter.RulesRecord }; + "jsx-runtime": { rules: Linter.RulesRecord }; + }; + export const rules: Record<string, Rule.RuleModule>; +} + +declare module "eslint-plugin-react-hooks" { + import type { Linter, Rule } from "eslint"; + + export const configs: { + recommended: { + rules: { + "rules-of-hooks": Linter.RuleEntry; + "exhaustive-deps": Linter.RuleEntry; + }; + }; + }; + export const rules: Record<string, Rule.RuleModule>; +} + +declare module "@next/eslint-plugin-next" { + import type { Linter, Rule } from "eslint"; + + export const configs: { + recommended: { rules: Linter.RulesRecord }; + "core-web-vitals": { rules: Linter.RulesRecord }; + }; + export const rules: Record<string, Rule.RuleModule>; +} diff --git a/tooling/typescript/base.json b/tooling/typescript/base.json index 944bb0a3c..37f6290fb 100644 --- a/tooling/typescript/base.json +++ b/tooling/typescript/base.json @@ -2,7 +2,11 @@ "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { "target": "ES2022", - "lib": ["dom", "dom.iterable", "ES2022"], + "lib": [ + "dom", + "dom.iterable", + "ES2022" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -16,6 +20,7 @@ "jsx": "preserve", "incremental": true, "noUncheckedIndexedAccess": true, + "strictNullChecks": true, "baseUrl": ".", "paths": { "*": [ @@ -23,5 +28,10 @@ ] } }, - "exclude": ["node_modules", "build", "dist", ".next"] -} + "exclude": [ + "node_modules", + "build", + "dist", + ".next" + ] +} \ No newline at end of file diff --git a/turbo/generators/config.ts b/turbo/generators/config.ts index 1bf6d4896..4ee93a8f8 100644 --- a/turbo/generators/config.ts +++ b/turbo/generators/config.ts @@ -15,14 +15,12 @@ export default function generator(plop: PlopTypes.NodePlopAPI): void { { type: "input", name: "name", - message: - "What is the name of the package? (You can skip the `@homarr/` prefix)", + message: "What is the name of the package? (You can skip the `@homarr/` prefix)", }, { type: "input", name: "deps", - message: - "Enter a space separated list of dependencies you would like to install", + message: "Enter a space separated list of dependencies you would like to install", }, ], actions: [ @@ -34,6 +32,11 @@ export default function generator(plop: PlopTypes.NodePlopAPI): void { } return "Config sanitized"; }, + { + type: "add", + path: "packages/{{ name }}/eslint.config.js", + templateFile: "templates/eslint.config.js.hbs", + }, { type: "add", path: "packages/{{ name }}/package.json", @@ -61,9 +64,7 @@ export default function generator(plop: PlopTypes.NodePlopAPI): void { if ("deps" in answers && typeof answers.deps === "string") { const pkg = JSON.parse(content) as PackageJson; for (const dep of answers.deps.split(" ").filter(Boolean)) { - const version = await fetch( - `https://registry.npmjs.org/-/package/${dep}/dist-tags`, - ) + const version = await fetch(`https://registry.npmjs.org/-/package/${dep}/dist-tags`) .then((res) => res.json()) .then((json) => json.latest); if (!pkg.dependencies) pkg.dependencies = {}; @@ -83,9 +84,7 @@ export default function generator(plop: PlopTypes.NodePlopAPI): void { // stdio: "inherit", // }); execSync("pnpm i", { stdio: "inherit" }); - execSync( - `pnpm prettier --write packages/${answers.name}/** --list-different`, - ); + execSync(`pnpm prettier --write packages/${answers.name}/** --list-different`); return "Package scaffolded"; } return "Package not scaffolded"; diff --git a/turbo/generators/templates/eslint.config.js.hbs b/turbo/generators/templates/eslint.config.js.hbs new file mode 100644 index 000000000..26c6c4ba1 --- /dev/null +++ b/turbo/generators/templates/eslint.config.js.hbs @@ -0,0 +1,9 @@ +import baseConfig from "@homarr/eslint-config/base"; + +/** @type {import('typescript-eslint').Config} */ +export default [ + { + ignores: [], + }, + ...baseConfig, +]; \ No newline at end of file diff --git a/turbo/generators/templates/package.json.hbs b/turbo/generators/templates/package.json.hbs index fd1754a76..90ca0774f 100644 --- a/turbo/generators/templates/package.json.hbs +++ b/turbo/generators/templates/package.json.hbs @@ -2,6 +2,7 @@ "name": "@homarr/{{name}}", "private": true, "version": "0.1.0", + "type": "module", "exports": { ".": "./index.ts" }, @@ -15,7 +16,7 @@ "license": "MIT", "scripts": { "clean": "rm -rf .turbo node_modules", - "lint": "eslint .", + "lint": "eslint", "format": "prettier --check . --ignore-path ../../.gitignore", "typecheck": "tsc --noEmit" }, @@ -23,13 +24,8 @@ "@homarr/eslint-config": "workspace:^0.2.0", "@homarr/prettier-config": "workspace:^0.1.0", "@homarr/tsconfig": "workspace:^0.1.0", - "eslint": "^8.57.0", + "eslint": "^9.4.0", "typescript": "^5.4.5" }, - "eslintConfig": { - "extends": [ - "@homarr/eslint-config/base" - ] - }, "prettier": "@homarr/prettier-config" } \ No newline at end of file