feat(eslint): new config

This commit is contained in:
he3als
2025-07-11 12:22:40 +01:00
parent 7514208919
commit 5f551155e9
15 changed files with 146 additions and 164 deletions

73
eslint.config.js Normal file
View File

@@ -0,0 +1,73 @@
import { includeIgnoreFile } from '@eslint/compat';
import js from '@eslint/js';
import eslintConfigPrettier from 'eslint-config-prettier';
import prettier from 'eslint-plugin-prettier';
import pluginReact from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import turbo from 'eslint-plugin-turbo';
import { defineConfig } from 'eslint/config';
import globals from 'globals';
import { fileURLToPath } from 'node:url';
import tsEslint from 'typescript-eslint';
const gitignorePath = fileURLToPath(new URL('.gitignore', import.meta.url));
export default defineConfig([
includeIgnoreFile(gitignorePath),
{
ignores: ['public/**', 'postcss.config.*'],
},
{
files: ['**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
plugins: { js },
extends: ['js/recommended'],
languageOptions: { globals: { ...globals.browser } },
},
...tsEslint.configs.recommended,
pluginReact.configs.flat.recommended,
eslintConfigPrettier,
{
plugins: {
'react-hooks': reactHooks,
turbo,
prettier,
},
settings: { react: { version: 'detect' } },
rules: {
// React
'react/react-in-jsx-scope': 'off',
'react/prop-types': 'off',
'react/display-name': 'warn',
// React Hooks
...reactHooks.configs.recommended.rules,
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
// TypeScript
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
// Prettier
'prettier/prettier': 'error',
// Turbo
'turbo/no-undeclared-env-vars': 'error',
},
},
{
files: ['**/*.{ts,tsx}'],
languageOptions: {
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: './',
},
},
},
]);

View File

@@ -1,130 +0,0 @@
import { fixupPluginRules } from '@eslint/compat';
import { FlatCompat } from '@eslint/eslintrc';
import js from '@eslint/js';
import typescriptEslint from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import prettier from 'eslint-plugin-prettier';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import globals from 'globals';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all,
});
export default [
{
ignores: [
'**/public',
'**/node_modules',
'resources/views',
'**/babel.config.js',
'**/tailwind.config.js',
'**/webpack.config.js',
'**/tsconfig.json',
'**/eslint.config.mjs',
],
},
...compat.extends(
'eslint:recommended',
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:@typescript-eslint/recommended',
),
{
plugins: {
react,
'react-hooks': fixupPluginRules(reactHooks),
prettier,
'@typescript-eslint': typescriptEslint,
},
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
parser: tsParser,
},
settings: {
react: {
pragma: 'React',
version: 'detect',
},
linkComponents: [
{
name: 'Link',
linkAttribute: 'to',
},
{
name: 'NavLink',
linkAttribute: 'to',
},
],
},
rules: {
'@typescript-eslint/no-var-requires': 0,
'@typescript-eslint/ban-ts-comment': 0,
'prettier/prettier': [
'warn',
{
endOfLine: 'auto',
},
{
usePrettierrc: true,
},
],
'react/prop-types': 0,
'react/display-name': 0,
'react/no-unknown-property': [
'error',
{
ignore: ['css'],
},
],
'@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/no-non-null-assertion': 0,
'no-use-before-define': 0,
'@typescript-eslint/no-use-before-define': 'warn',
'@typescript-eslint/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
},
],
},
},
{
files: ['**/*.ts', '**/*.tsx'],
languageOptions: {
ecmaVersion: 6,
sourceType: 'script',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
project: './tsconfig.json',
tsconfigRootDir: './',
},
},
},
];

View File

@@ -55,6 +55,7 @@
"debounce": "^2.2.0",
"deepmerge-ts": "^7.1.5",
"easy-peasy": "^6.1.0",
"eslint-plugin-turbo": "^2.5.4",
"events": "^3.3.0",
"formik": "^2.4.6",
"globals": "^16.3.0",
@@ -80,6 +81,7 @@
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.11",
"turbo": "^2.5.4",
"typescript-eslint": "^8.36.0",
"uuid": "^11.1.0",
"vite": "^7.0.2",
"yup": "^1.6.1"
@@ -119,7 +121,7 @@
"vite-plugin-manifest-sri": "^0.2.0"
},
"scripts": {
"lint": "eslint ./resources/scripts --ext .ts,.tsx --fix",
"lint": "eslint . --fix",
"lint:turbo": "turbo lint",
"dev": "vite",
"dev:docker": "cross-env APP_URL=\"http://localhost:3000\" vite",

41
pnpm-lock.yaml generated
View File

@@ -152,6 +152,9 @@ importers:
easy-peasy:
specifier: ^6.1.0
version: 6.1.0(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
eslint-plugin-turbo:
specifier: ^2.5.4
version: 2.5.4(eslint@9.30.1(jiti@2.4.2))(turbo@2.5.4)
events:
specifier: ^3.3.0
version: 3.3.0
@@ -227,6 +230,9 @@ importers:
turbo:
specifier: ^2.5.4
version: 2.5.4
typescript-eslint:
specifier: ^8.36.0
version: 8.36.0(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)
uuid:
specifier: ^11.1.0
version: 11.1.0
@@ -2426,6 +2432,10 @@ packages:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'}
dotenv@16.0.3:
resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
engines: {node: '>=12'}
dotenv@16.5.0:
resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==}
engines: {node: '>=12'}
@@ -2548,6 +2558,12 @@ packages:
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
eslint-plugin-turbo@2.5.4:
resolution: {integrity: sha512-IZsW61DFj5mLMMaCJxhh1VE4HvNhfdnHnAaXajgne+LUzdyHk2NvYT0ECSa/1SssArcqgTvV74MrLL68hWLLFw==}
peerDependencies:
eslint: '>6.6.0'
turbo: '>2.0.0'
eslint-scope@8.4.0:
resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -3814,6 +3830,13 @@ packages:
resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
engines: {node: '>= 0.4'}
typescript-eslint@8.36.0:
resolution: {integrity: sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
typescript@5.8.3:
resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
engines: {node: '>=14.17'}
@@ -6277,6 +6300,8 @@ snapshots:
dependencies:
esutils: 2.0.3
dotenv@16.0.3: {}
dotenv@16.5.0: {}
dunder-proto@1.0.1:
@@ -6496,6 +6521,12 @@ snapshots:
string.prototype.matchall: 4.0.12
string.prototype.repeat: 1.0.0
eslint-plugin-turbo@2.5.4(eslint@9.30.1(jiti@2.4.2))(turbo@2.5.4):
dependencies:
dotenv: 16.0.3
eslint: 9.30.1(jiti@2.4.2)
turbo: 2.5.4
eslint-scope@8.4.0:
dependencies:
esrecurse: 4.3.0
@@ -7783,6 +7814,16 @@ snapshots:
possible-typed-array-names: 1.1.0
reflect.getprototypeof: 1.0.10
typescript-eslint@8.36.0(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3):
dependencies:
'@typescript-eslint/eslint-plugin': 8.36.0(@typescript-eslint/parser@8.36.0(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3))(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)
'@typescript-eslint/parser': 8.36.0(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)
'@typescript-eslint/utils': 8.36.0(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)
eslint: 9.30.1(jiti@2.4.2)
typescript: 5.8.3
transitivePeerDependencies:
- supports-color
typescript@5.8.3: {}
unbox-primitive@1.1.0:

View File

@@ -58,7 +58,7 @@ function LoginContainer() {
const resetCaptcha = () => {
setToken('');
if (isTurnstileEnabled && turnstileRef.current) {
// @ts-ignore - The type doesn't expose the reset method directly
// @ts-expect-error - The type doesn't expose the reset method directly
turnstileRef.current.reset();
}
if (isFriendlyEnabled && friendlyCaptchaRef.current) {

View File

@@ -41,7 +41,7 @@ const CopyOnClick = ({ text, children, showInNotification }: CopyOnClickProps) =
const child = !text
? React.Children.only(children)
: React.cloneElement(React.Children.only(children), {
// @ts-ignore
// @ts-expect-error - Props type inference issue with React.cloneElement
className: clsx(children.props.className || '', 'cursor-pointer'),
onClick: (e: React.MouseEvent<HTMLElement>) => {
copy(String(text));

View File

@@ -35,7 +35,7 @@ function Pagination<T>({ data: { items, pagination }, onPageSelect, children }:
const end = Math.min(pagination.totalPages, pagination.currentPage + 5);
for (let i = start; i <= end; i++) {
// @ts-ignore
// @ts-expect-error - Type issue with array push
pages.push(i);
}

View File

@@ -1,4 +1,4 @@
import type { ReactNode } from 'react';
import type { JSX, ReactNode } from 'react';
import { ServerError } from '@/components/elements/ScreenBlock';
@@ -6,17 +6,16 @@ import { usePermissions } from '@/plugins/usePermissions';
interface Props {
children?: ReactNode;
permission?: string | string[];
}
function PermissionRoute({ children, permission }: Props): JSX.Element {
const can = usePermissions(permission || []);
if (permission === undefined || permission === null) {
return <>{children}</>;
}
const can = usePermissions(permission);
if (can.filter((p) => p).length > 0) {
return <>{children}</>;
}

View File

@@ -25,7 +25,7 @@ type Stats = Record<'memory' | 'cpu' | 'disk' | 'uptime' | 'rx' | 'tx', number>;
// return undefined;
// };
// @ts-ignore
// @ts-expect-error - Unused parameter in component definition
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const Limit = ({ limit, children }: { limit: string | null; children: React.ReactNode }) => <>{children}</>;

View File

@@ -66,7 +66,7 @@ const options: ChartOptions<'line'> = {
};
function getOptions(opts?: DeepPartial<ChartOptions<'line'>> | undefined): ChartOptions<'line'> {
// @ts-ignore I'm not even going to try to tell you what this error means
// @ts-expect-error - deepmerge type compatibility issue with ChartOptions
return deepmerge(options, opts || {});
}

View File

@@ -192,7 +192,7 @@ export default () => {
}}
>
<FileObjectRow
// @ts-ignore
// @ts-expect-error - Legacy type suppression
file={filesArray[virtualItem.index]}
key={filesArray[virtualItem.index]?.name}
/>

View File

@@ -41,13 +41,13 @@ export default () => {
const showSideBar = (shown: boolean) => {
setSidebarVisible(shown);
// @ts-ignore
// @ts-expect-error - Legacy type suppression
if (!shown) setSidebarPosition(-500);
else setSidebarPosition(0);
};
const checkIfMinimal = () => {
// @ts-ignore
// @ts-expect-error - Legacy type suppression
if (!(window.getComputedStyle(sidebarRef.current, null).display === 'block')) {
showSideBar(true);
return true;
@@ -98,31 +98,30 @@ export default () => {
// Handle touch events for swipe to close
const handleTouchStart = (e: React.TouchEvent) => {
if (checkIfMinimal()) return;
// @ts-ignore it is not "possibly undefined." Pretty much guarunteed to work.
// @ts-expect-error - Legacy type suppression it is not "possibly undefined." Pretty much guarunteed to work.
if (isSidebarVisible) setTouchStartX(e.touches[0].clientX - sidebarRef.current?.clientWidth);
// @ts-ignore
// @ts-expect-error - Legacy type suppression
else setTouchStartX(e.touches[0].clientX);
};
const handleTouchMove = (e: React.TouchEvent) => {
if (checkIfMinimal()) return;
// @ts-ignore go to sleep TSC
// @ts-expect-error - Legacy type suppression go to sleep TSC
const sidebarWidth = sidebarRef.current.clientWidth;
// @ts-ignore
// @ts-expect-error - Legacy type suppression
if (e.touches[0].clientX - touchStartX < 30) {
setSidebarPosition(-sidebarWidth);
return;
}
// @ts-ignore
// @ts-expect-error - Legacy type suppression
const clampedValue = Math.max(Math.min(e.touches[0].clientX - touchStartX, sidebarWidth), 0) - sidebarWidth;
setSidebarBetween(false);
console.group('updateDragLocation');
//@ts-ignore Ok, TSC please go back to bed.
console.info(`start ${clampedValue}`);
console.groupEnd();
@@ -135,7 +134,7 @@ export default () => {
setTouchStartX(null);
setSidebarBetween(true);
// @ts-ignore
// @ts-expect-error - Legacy type suppression
if ((sidebarPosition - sidebarRef.current?.clientWidth) / sidebarRef.current?.clientWidth > -1.35) {
showSideBar(true);
} else {

View File

@@ -108,13 +108,13 @@ export default () => {
const showSideBar = (shown: boolean) => {
setSidebarVisible(shown);
// @ts-ignore
// @ts-expect-error - Legacy type suppression
if (!shown) setSidebarPosition(-500);
else setSidebarPosition(0);
};
const checkIfMinimal = () => {
// @ts-ignore
// @ts-expect-error - Legacy type suppression
if (!(window.getComputedStyle(sidebarRef.current, null).display === 'block')) {
showSideBar(true);
return true;
@@ -167,31 +167,30 @@ export default () => {
// Handle touch events for swipe to close
const handleTouchStart = (e: React.TouchEvent) => {
if (checkIfMinimal()) return;
// @ts-ignore it is not "possibly undefined." Pretty much guarunteed to work.
// @ts-expect-error - Legacy type suppression it is not "possibly undefined." Pretty much guarunteed to work.
if (isSidebarVisible) setTouchStartX(e.touches[0].clientX - sidebarRef.current?.clientWidth);
// @ts-ignore
// @ts-expect-error - Legacy type suppression
else setTouchStartX(e.touches[0].clientX);
};
const handleTouchMove = (e: React.TouchEvent) => {
if (checkIfMinimal()) return;
// @ts-ignore go to sleep TSC
// @ts-expect-error - Legacy type suppression go to sleep TSC
const sidebarWidth = sidebarRef.current.clientWidth;
// @ts-ignore
// @ts-expect-error - Legacy type suppression
if (e.touches[0].clientX - touchStartX < 30) {
setSidebarPosition(-sidebarWidth);
return;
}
// @ts-ignore
// @ts-expect-error - Legacy type suppression
const clampedValue = Math.max(Math.min(e.touches[0].clientX - touchStartX, sidebarWidth), 0) - sidebarWidth;
setSidebarBetween(false);
console.group('updateDragLocation');
//@ts-ignore Ok, TSC please go back to bed.
console.info(`start ${clampedValue}`);
console.groupEnd();
@@ -204,8 +203,8 @@ export default () => {
setTouchStartX(null);
setSidebarBetween(true);
// @ts-ignore
// @ts-ignore
// @ts-expect-error - Legacy type suppression
// @ts-expect-error - Legacy type suppression
if ((sidebarPosition - sidebarRef.current?.clientWidth) / sidebarRef.current?.clientWidth > -1.35) {
showSideBar(true);
} else {
@@ -411,7 +410,6 @@ export default () => {
viewBox='0 0 16 15'
className='flex shrink-0 h-full w-full'
>
{/* @ts-ignore */}
<path d='M8.9375 7.3775C8.9375 7.56341 8.88252 7.74515 8.7795 7.89974C8.67649 8.05432 8.53007 8.1748 8.35877 8.24595C8.18746 8.31709 7.99896 8.33571 7.8171 8.29944C7.63525 8.26317 7.4682 8.17364 7.33709 8.04218C7.20598 7.91072 7.11669 7.74323 7.08051 7.56088C7.04434 7.37854 7.06291 7.18954 7.13386 7.01778C7.20482 6.84601 7.32498 6.69921 7.47915 6.59592C7.63332 6.49263 7.81458 6.4375 8 6.4375C8.24864 6.4375 8.4871 6.53654 8.66291 6.71282C8.83873 6.8891 8.9375 7.1282 8.9375 7.3775ZM1.625 6.4375C1.43958 6.4375 1.25832 6.49263 1.10415 6.59592C0.949982 6.69921 0.829821 6.84601 0.758863 7.01778C0.687906 7.18954 0.669341 7.37854 0.705514 7.56088C0.741688 7.74323 0.830976 7.91072 0.962088 8.04218C1.0932 8.17364 1.26025 8.26317 1.4421 8.29944C1.62396 8.33571 1.81246 8.31709 1.98377 8.24595C2.15507 8.1748 2.30149 8.05432 2.4045 7.89974C2.50752 7.74515 2.5625 7.56341 2.5625 7.3775C2.5625 7.1282 2.46373 6.8891 2.28791 6.71282C2.1121 6.53654 1.87364 6.4375 1.625 6.4375ZM14.375 6.4375C14.1896 6.4375 14.0083 6.49263 13.8542 6.59592C13.7 6.69921 13.5798 6.84601 13.5089 7.01778C13.4379 7.18954 13.4193 7.37854 13.4555 7.56088C13.4917 7.74323 13.581 7.91072 13.7121 8.04218C13.8432 8.17364 14.0102 8.26317 14.1921 8.29944C14.374 8.33571 14.5625 8.31709 14.7338 8.24595C14.9051 8.1748 15.0515 8.05432 15.1545 7.89974C15.2575 7.74515 15.3125 7.56341 15.3125 7.3775C15.3125 7.1282 15.2137 6.8891 15.0379 6.71282C14.8621 6.53654 14.6236 6.4375 14.375 6.4375Z' />
</svg>
</button>

View File

@@ -1,5 +1,6 @@
{
"$schema": "https://turbo.build/schema.json",
"globalEnv": ["NODE_ENV"],
"tasks": {
"build": {
"outputs": ["public/build/**"]

View File

@@ -58,11 +58,10 @@ export default defineConfig({
rollupOptions: {
input: path.resolve('resources/scripts/index.tsx'),
output: {
// @ts-ignore
// @ts-expect-error It won't fail lol
manualChunks(id) {
if (id.includes('node_modules')) {
// @ts-expect-error
// It won't fail lol
// @ts-expect-error It won't fail lol
return id.toString().split('node_modules/')[1].split('/')[0].toString();
}
},