feat(backend): configure eslint to use eslint-config-it4c (#634)

* configure eslint to use eslint-config-it4c

* update node version (note: this requires an update on the hosting alpine)

* use node 24.13.0

* another transitive package

* update eslint-config-it4c

* fix lint

* fix config

* fix packages

* update eslint-config-it4c package

* another package update

* update package

* update package

* lint fixes

* use prettier config from eslint-config-it4c

* package update

* package update

* update package

* reduce config

* fix lint

* remove node_modules from ignore as it is the default for eslint9

* update package

* fix lint
This commit is contained in:
Ulf Gebhardt 2026-02-06 06:53:45 +01:00 committed by GitHub
parent 341764925e
commit f00e9f8558
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 2902 additions and 1008 deletions

View File

@ -1,3 +0,0 @@
node_modules/
build/
coverage/

View File

@ -1,200 +0,0 @@
// eslint-disable-next-line import/no-commonjs
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'standard',
'eslint:recommended',
'plugin:@eslint-community/eslint-comments/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
// 'plugin:promise/recommended',
'plugin:security/recommended-legacy',
],
parserOptions: {
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
plugins: ['@typescript-eslint', 'import', 'promise', 'security', 'no-catch-all'],
settings: {
'import/resolver': {
typescript: true,
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
},
},
rules: {
'no-catch-all/no-catch-all': 'error',
'no-console': 'error',
'no-debugger': 'error',
camelcase: 'error',
indent: ['error', 2],
'linebreak-style': ['error', 'unix'],
semi: ['error', 'never'],
// Optional eslint-comments rule
'@eslint-community/eslint-comments/no-unused-disable': 'error',
'@eslint-community/eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }],
// import
'import/export': 'error',
'import/no-deprecated': 'error',
'import/no-empty-named-blocks': 'error',
'import/no-extraneous-dependencies': 'error',
'import/no-mutable-exports': 'error',
'import/no-unused-modules': 'error',
'import/no-named-as-default': 'error',
'import/no-named-as-default-member': 'error',
'import/no-amd': 'error',
'import/no-commonjs': 'error',
'import/no-import-module-exports': 'error',
'import/no-nodejs-modules': 'off',
'import/unambiguous': 'off', // not compatible with scriptless vue files
'import/default': 'error',
'import/named': 'error',
'import/namespace': 'error',
'import/no-absolute-path': 'error',
'import/no-cycle': 'error',
'import/no-dynamic-require': 'error',
'import/no-internal-modules': 'off',
'import/no-relative-packages': 'error',
'import/no-relative-parent-imports': [
'error',
{
ignore: ['#[src,types,root,components,utils,assets]/*'],
},
],
'import/no-self-import': 'error',
'import/no-unresolved': 'error',
'import/no-useless-path-segments': 'error',
'import/no-webpack-loader-syntax': 'error',
'import/consistent-type-specifier-style': 'error',
'import/exports-last': 'off',
'import/extensions': [
'error',
'never',
{
json: 'always',
},
],
'import/first': 'error',
'import/group-exports': 'off',
'import/newline-after-import': 'error',
'import/no-anonymous-default-export': 'off', // todo - consider to enable again
'import/no-default-export': 'off', // incompatible with vite & vike
'import/no-duplicates': 'error',
'import/no-named-default': 'error',
'import/no-namespace': 'error',
'import/no-unassigned-import': [
'error',
{
allow: ['**/*.css'],
},
],
'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
'newlines-between': 'always',
alphabetize: {
order: 'asc', // sort in ascending order. Options: ["ignore", "asc", "desc"]
caseInsensitive: true, // ignore case. Options: [true, false]
},
distinctGroup: true,
},
],
'import/prefer-default-export': 'off',
// promise
'promise/catch-or-return': 'error',
'promise/no-return-wrap': 'error',
'promise/param-names': 'error',
'promise/always-return': 'error',
'promise/no-native': 'off',
'promise/no-nesting': 'warn',
'promise/no-promise-in-callback': 'warn',
'promise/no-callback-in-promise': 'warn',
'promise/avoid-new': 'warn',
'promise/no-new-statics': 'error',
'promise/no-return-in-finally': 'warn',
'promise/valid-params': 'warn',
'promise/prefer-await-to-callbacks': 'error',
'promise/no-multiple-resolved': 'error',
},
overrides: [
{
files: ['*.ts', '*.tsx'],
parser: '@typescript-eslint/parser',
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./tsconfig.json', '**/tsconfig.json'],
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
plugins: ['@typescript-eslint'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:@typescript-eslint/strict',
],
rules: {
'@typescript-eslint/consistent-type-imports': 'error',
// allow explicitly defined dangling promises
'@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: true }],
'no-void': ['error', { allowAsStatement: true }],
},
},
{
files: ['!*.json'],
plugins: ['prettier'],
extends: ['plugin:prettier/recommended'],
rules: {
'prettier/prettier': 'error',
},
},
{
files: ['*.json'],
plugins: ['json'],
extends: ['plugin:json/recommended-with-comments'],
},
// {
// files: ['*.{test,spec}.[tj]s'],
// plugins: ['vitest'],
// extends: ['plugin:vitest/all'],
// rules: {
// 'vitest/prefer-lowercase-title': 'off',
// 'vitest/no-hooks': 'off',
// 'vitest/consistent-test-filename': 'off',
// 'vitest/prefer-expect-assertions': [
// 'off',
// {
// onlyFunctionsWithExpectInLoop: true,
// onlyFunctionsWithExpectInCallback: true,
// },
// ],
// 'vitest/prefer-strict-equal': 'off',
// 'vitest/prefer-to-be-falsy': 'off',
// 'vitest/prefer-to-be-truthy': 'off',
// 'vitest/require-hook': [
// 'error',
// {
// allowedFunctionCalls: [
// 'mockClient.setRequestHandler',
// 'setActivePinia',
// 'provideApolloClient',
// ],
// },
// ],
// },
// },
{
files: ['*.yaml', '*.yml'],
parser: 'yaml-eslint-parser',
plugins: ['yml'],
extends: ['plugin:yml/prettier'],
},
],
}

View File

@ -1,14 +0,0 @@
{
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"semi": false,
"singleQuote": true,
"quoteProps": "as-needed",
"jsxSingleQuote": true,
"trailingComma": "all",
"bracketSpacing": true,
"bracketSameLine": false,
"arrowParens": "always",
"endOfLine": "auto"
}

View File

@ -0,0 +1,3 @@
import config, { jest } from 'eslint-config-it4c'
export default [{ ignores: ['build/', 'coverage/'] }, ...config, ...jest]

3627
backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -15,25 +15,12 @@
"build": "tsc",
"dev": "tsx watch ./src/index.ts",
"test:unit": "jest",
"test:lint:eslint": "eslint --ext .ts,.tsx,.js,.jsx,.cjs,.mjs,.json,.yml,.yaml --max-warnings 0 .",
"test:lint:eslint": "eslint --max-warnings 0 .",
"update": "npx npm-check-updates"
},
"devDependencies": {
"@eslint-community/eslint-plugin-eslint-comments": "^4.6.0",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"eslint": "^8.24.0",
"eslint-config-prettier": "^10.1.8",
"eslint-config-standard": "^17.1.0",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-json": "^3.1.0",
"eslint-plugin-n": "^16.6.2",
"eslint-plugin-no-catch-all": "^1.1.0",
"eslint-plugin-prettier": "^5.5.5",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-security": "^3.0.1",
"eslint-plugin-yml": "^1.19.1",
"eslint": "^9.39.2",
"eslint-config-it4c": "^0.7.2",
"jest": "^30.2.0",
"ts-jest": "^29.4.6",
"tsx": "^4.21.0",
@ -62,7 +49,9 @@
"statements": 100
}
},
"modulePathIgnorePatterns": ["<rootDir>/build/"]
"modulePathIgnorePatterns": [
"<rootDir>/build/"
]
},
"imports": {
"#src/*": "./src/*",

View File

@ -0,0 +1,3 @@
import config from 'eslint-config-it4c/prettier'
export default config

View File

@ -27,7 +27,7 @@ export const schema = {
export type Env = EnvType<typeof schema>
// eslint-disable-next-line import/no-mutable-exports
// eslint-disable-next-line import-x/no-mutable-exports
export let env: Env
export function loadEnv(): void {

View File

@ -58,7 +58,7 @@ export function IsDateTime(value: string, strictTimeZone?: boolean): boolean {
return dateTime.length === 2 && IsDate(dateTime[0]) && IsTime(dateTime[1], strictTimeZone)
}
*/
export function IsEmail(value: string) {
export function IsEmail(value: string): boolean {
return EMAIL.test(value)
}
/*

View File

@ -1,5 +1,4 @@
// eslint-disable-next-line import/no-namespace
import * as nodemailer from 'nodemailer'
import { createTransport } from 'nodemailer'
import { env } from './env'
import { createServer } from './server'
@ -7,7 +6,7 @@ import { createServer } from './server'
// Mock nodemailer
jest.mock('nodemailer')
const mockCreateTransport = nodemailer.createTransport as unknown as jest.Mock
const mockCreateTransport = createTransport as unknown as jest.Mock
const mockSendMail = jest.fn()

View File

@ -8,8 +8,9 @@ import { IsEmail } from './formats'
import type { Env } from './env'
import type { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
import type { FastifyInstance } from 'fastify'
function createServer(env: Env) {
function createServer(env: Env): FastifyInstance {
// Register EMail format
FormatRegistry.Set('email', (value) => IsEmail(value))
@ -55,7 +56,7 @@ function createServer(env: Env) {
subject: util.format(env.EMAIL_SUBJECT, request.body.name),
text: `${request.body.text}${request.body.telephone ? `\n\nTelephone: ${request.body.telephone}` : ''}`,
})
return reply.status(200).send({ success: true })
return await reply.status(200).send({ success: true })
// eslint-disable-next-line no-catch-all/no-catch-all
} catch (error) {
return reply.status(400).send({ success: false, error: error as string })

View File

@ -11,7 +11,7 @@
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"target": "ESNext" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
@ -25,13 +25,14 @@
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "NodeNext", /* Specify what module code is generated. */
"module": "NodeNext" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */
"moduleResolution": "nodenext", /* Specify how TypeScript looks up a file from a given module specifier. */
"moduleResolution": "nodenext" /* Specify how TypeScript looks up a file from a given module specifier. */,
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
"paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */
"#src/*": ["./src/*"],
"#root/*": ["./*"]
"paths": {
/* Specify a set of entries that re-map imports to additional lookup locations. */
"#src/*": ["./src/*"],
"#root/*": ["./*"]
},
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
@ -61,7 +62,7 @@
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./build", /* Specify an output folder for all emitted files. */
"outDir": "./build" /* Specify an output folder for all emitted files. */,
// "removeComments": true, /* Disable emitting comments. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
@ -81,12 +82,12 @@
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
"strict": true /* Enable all strict type-checking options. */,
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
@ -109,6 +110,6 @@
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}