Visitenbuch/eslint.config.js

819 lines
27 KiB
JavaScript

// @ts-expect-error js module
import js from "@eslint/js";
import stylistic from "@stylistic/eslint-plugin";
// @ts-expect-error js module
import eslintImport from "eslint-plugin-import";
// @ts-expect-error js module
import noRelativeImportPaths from "eslint-plugin-no-relative-import-paths";
import svelte from "eslint-plugin-svelte";
// @ts-expect-error js module
import unusedImports from "eslint-plugin-unused-imports";
import globals from "globals";
import svelteParser from "svelte-eslint-parser";
import ts from "typescript-eslint";
export default [
js.configs.recommended,
...ts.configs.recommended,
...svelte.configs["flat/recommended"],
// TS-Svelte
{
files: ["*.svelte", "**/*.svelte"],
languageOptions: {
parser: svelteParser,
parserOptions: {
parser: ts.parser,
svelteFeatures: {
experimentalGenerics: true,
},
},
},
},
// TS options
{
languageOptions: {
parserOptions: {
sourceType: "module",
ecmaVersion: 2020,
project: "./tsconfig.json",
extraFileExtensions: [".svelte"],
},
globals: {
...globals.browser,
$$Generic: "readonly",
},
},
},
// No relative import paths
{
plugins: {
"no-relative-import-paths": noRelativeImportPaths,
},
rules: {
"no-relative-import-paths/no-relative-import-paths": [
"warn",
{ allowSameFolder: true, rootDir: "src/lib", prefix: "$lib" },
],
},
},
// Import
{
plugins: {
import: eslintImport,
},
rules: {
"import/order": [
"warn",
{
// Order: SvelteKit builtins, External deps, Internal deps, Components, Siblings
groups: [
"builtin",
"external",
"internal",
"unknown",
["sibling", "parent", "index"],
],
"newlines-between": "always",
alphabetize: {
order: "asc",
caseInsensitive: true,
},
pathGroups: [
{ pattern: "$app/**", group: "builtin", position: "after" },
{ pattern: "./$types", group: "builtin", position: "after" },
{ pattern: "$lib/components/**", group: "unknown", position: "before" },
{ pattern: "$lib/shared/**", group: "internal", position: "before" },
],
distinctGroup: false,
},
],
},
},
// No unused imports
{
plugins: {
"unused-imports": unusedImports,
},
rules: {
"@typescript-eslint/no-unused-vars": "off",
"unused-imports/no-unused-imports": "warn",
"unused-imports/no-unused-vars": ["warn", { args: "none" }],
},
},
// JS STYLING
{
plugins: {
"@stylistic": stylistic,
},
rules: {
// enforces line breaks after opening and before closing array brackets
// https://eslint.style/rules/default/array-bracket-newline
"@stylistic/array-bracket-newline": ["warn", "consistent"],
// enforce spacing inside array brackets
// https://eslint.style/rules/default/array-bracket-spacing
"@stylistic/array-bracket-spacing": ["warn", "never"],
// enforces line breaks between array elements
// https://eslint.style/rules/default/array-element-newline
"@stylistic/array-element-newline": ["warn", "consistent"],
// enforces parentheses in arrow functions
// https://eslint.style/rules/default/arrow-parens
"@stylistic/arrow-parens": "warn",
// enforces spacing before/after an arrow function's arrow
// https://eslint.style/rules/default/arrow-spacing
"@stylistic/arrow-spacing": "warn",
// enforce spacing inside single-line blocks
// https://eslint.style/rules/default/block-spacing
"@stylistic/block-spacing": ["warn", "always"],
// enforce one true brace style
// https://eslint.style/rules/default/brace-style
"@stylistic/brace-style": ["warn", "1tbs", { allowSingleLine: true }],
// require trailing commas in multiline object literals
// https://eslint.style/rules/default/comma-dangle
"@stylistic/comma-dangle": [
"warn",
{
arrays: "always-multiline",
objects: "always-multiline",
imports: "always-multiline",
exports: "always-multiline",
functions: "always-multiline",
enums: "always-multiline",
generics: "always-multiline",
tuples: "always-multiline",
},
],
// enforce spacing before and after comma
// https://eslint.style/rules/default/comma-spacing
"@stylistic/comma-spacing": ["warn", { before: false, after: true }],
// enforce one true comma style
// https://eslint.style/rules/default/comma-style
"@stylistic/comma-style": [
"warn",
"last",
{
exceptions: {
ArrayExpression: false,
ArrayPattern: false,
ArrowFunctionExpression: false,
CallExpression: false,
FunctionDeclaration: false,
FunctionExpression: false,
ImportDeclaration: false,
ObjectExpression: false,
ObjectPattern: false,
VariableDeclaration: false,
NewExpression: false,
},
},
],
// disallow padding inside computed properties
// https://eslint.style/rules/default/computed-property-spacing
"@stylistic/computed-property-spacing": ["warn", "never"],
// enforce newlines before a dot in a member expression
// https://eslint.style/rules/default/dot-location
"@stylistic/dot-location": ["warn", "property"],
// enforce newline at the end of file, with no multiple empty lines
// https://eslint.style/rules/default/eol-last
"@stylistic/eol-last": ["warn", "always"],
// line breaks between arguments of a function call
// https://eslint.style/rules/default/function-call-argument-newline
"@stylistic/function-call-argument-newline": ["warn", "consistent"],
// enforce spacing between functions and their invocations
// https://eslint.style/rules/default/function-call-spacing
"@stylistic/function-call-spacing": ["warn", "never"],
// require function expressions to have a name
// https://eslint.org/docs/rules/func-names
"func-names": "warn",
// require line breaks inside function parentheses if there are line breaks between parameters
// https://eslint.style/rules/default/function-paren-newline
"@stylistic/function-paren-newline": ["warn", "multiline-arguments"],
// Enforce the location of arrow function bodies with implicit returns
// https://eslint.style/rules/default/implicit-arrow-linebreak
"@stylistic/implicit-arrow-linebreak": ["warn", "beside"],
// this option sets a specific tab width for your code
// https://eslint.style/rules/default/indent
"@stylistic/indent": [
"warn",
2,
{
SwitchCase: 1,
VariableDeclarator: 1,
outerIIFEBody: 1,
// MemberExpression: null,
FunctionDeclaration: {
parameters: 1,
body: 1,
},
FunctionExpression: {
parameters: 1,
body: 1,
},
CallExpression: {
arguments: 1,
},
ArrayExpression: 1,
ObjectExpression: 1,
ImportDeclaration: 1,
flatTernaryExpressions: false,
// list derived from https://github.com/benjamn/ast-types/blob/HEAD/def/jsx.js
ignoredNodes: [
"JSXElement",
"JSXElement > *",
"JSXAttribute",
"JSXIdentifier",
"JSXNamespacedName",
"JSXMemberExpression",
"JSXSpreadAttribute",
"JSXExpressionContainer",
"JSXOpeningElement",
"JSXClosingElement",
"JSXFragment",
"JSXOpeningFragment",
"JSXClosingFragment",
"JSXText",
"JSXEmptyExpression",
"JSXSpreadChild",
],
ignoreComments: false,
},
],
// enforces spacing between keys and values in object literal properties
// https://eslint.style/rules/default/key-spacing
"@stylistic/key-spacing": ["warn", { beforeColon: false, afterColon: true }],
// require a space before & after certain keywords
// https://eslint.style/rules/default/keyword-spacing
"@stylistic/keyword-spacing": [
"warn",
{
before: true,
after: true,
overrides: {
return: { after: true },
throw: { after: true },
case: { after: true },
},
},
],
// disallow mixed 'LF' and 'CRLF' as linebreaks
// https://eslint.style/rules/default/linebreak-style
"@stylistic/linebreak-style": ["warn", "unix"],
// require or disallow an empty line between class members
// https://eslint.style/rules/default/lines-between-class-members
"@stylistic/lines-between-class-members": [
"warn",
"always",
{ exceptAfterSingleLine: false },
],
// require or disallow newlines around directives
// https://eslint.org/docs/rules/lines-around-directive
"lines-around-directive": [
"warn",
{
before: "always",
after: "always",
},
],
// specify the maximum length of a line in your program
// https://eslint.style/rules/default/max-len
"@stylistic/max-len": [
"warn",
100,
2,
{
ignoreUrls: true,
ignoreComments: false,
ignoreRegExpLiterals: true,
ignoreStrings: true,
ignoreTemplateLiterals: true,
},
],
// require a capital letter for constructors
"new-cap": [
"warn",
{
newIsCap: true,
newIsCapExceptions: [],
capIsNew: false,
capIsNewExceptions: ["Immutable.Map", "Immutable.Set", "Immutable.List"],
},
],
// disallow the omission of parentheses when invoking a constructor with no arguments
// https://eslint.style/rules/default/new-parens
"@stylistic/new-parens": "warn",
// enforces new line after each method call in the chain to make it
// more readable and easy to maintain
// https://eslint.style/rules/default/newline-per-chained-call
"@stylistic/newline-per-chained-call": ["warn", { ignoreChainWithDepth: 4 }],
// disallow use of the Array constructor
"@typescript-eslint/no-array-constructor": "warn",
// disallow un-paren'd mixes of different operators
// https://eslint.org/docs/rules/no-mixed-operators
"@stylistic/no-mixed-operators": [
"warn",
{
// the list of arithmetic groups disallows mixing `%` and `**`
// with other arithmetic operators.
groups: [
["%", "**"],
["%", "+"],
["%", "-"],
["%", "*"],
["%", "/"],
["/", "*"],
["&", "|", "<<", ">>", ">>>"],
["==", "!=", "===", "!=="],
["&&", "||"],
],
allowSamePrecedence: false,
},
],
// disallow mixed spaces and tabs for indentation
// https://eslint.style/rules/default/no-mixed-spaces-and-tabs
"@stylistic/no-mixed-spaces-and-tabs": "warn",
// disallow use of chained assignment expressions
// https://eslint.org/docs/rules/no-multi-assign
"no-multi-assign": ["warn"],
// disallow multiple empty lines, only one newline at the end,
// and no new lines at the beginning
// https://eslint.style/rules/default/no-multiple-empty-lines
"@stylistic/no-multiple-empty-lines": ["warn", { max: 1, maxBOF: 0, maxEOF: 0 }],
// disallow nested ternary expressions
"no-nested-ternary": "warn",
// disallow use of the Object constructor
"no-new-object": "warn",
// disallow certain syntax forms
// https://eslint.org/docs/rules/no-restricted-syntax
"no-restricted-syntax": [
"warn",
{
selector: "ForInStatement",
message:
"for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.",
},
{
selector: "LabeledStatement",
message:
"Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.",
},
{
selector: "WithStatement",
message:
"`with` is disallowed in strict mode because it makes code impossible to predict and optimize.",
},
],
// disallow tab characters entirely
// https://eslint.style/rules/default/no-tabs
"@stylistic/no-tabs": "warn",
// disallow trailing whitespace at the end of lines
// https://eslint.style/rules/default/no-trailing-spaces
"svelte/no-trailing-spaces": [
"warn",
{
skipBlankLines: false,
ignoreComments: false,
},
],
// disallow the use of Boolean literals in conditional expressions
// also, prefer `a || b` over `a ? a : b`
// https://eslint.org/docs/rules/no-unneeded-ternary
"no-unneeded-ternary": ["warn", { defaultAssignment: false }],
// disallow whitespace before properties
// https://eslint.style/rules/default/no-whitespace-before-property
"@stylistic/no-whitespace-before-property": "warn",
// enforce the location of single-line statements
// https://eslint.style/rules/default/nonblock-statement-body-position
"@stylistic/nonblock-statement-body-position": ["warn", "beside", { overrides: {} }],
// require padding inside curly braces
// https://eslint.style/rules/default/object-curly-spacing
"@stylistic/object-curly-spacing": ["warn", "always"],
// enforce line breaks between braces
// https://eslint.style/rules/default/object-curly-newline
"@stylistic/object-curly-newline": [
"warn",
{
ObjectExpression: { minProperties: 4, multiline: true, consistent: true },
ObjectPattern: { minProperties: 4, multiline: true, consistent: true },
ImportDeclaration: { minProperties: 4, multiline: true, consistent: true },
ExportDeclaration: { minProperties: 4, multiline: true, consistent: true },
},
],
// enforce "same line" or "multiple line" on object properties.
// https://eslint.style/rules/default/object-property-newline
"@stylistic/object-property-newline": [
"warn",
{
allowAllPropertiesOnSameLine: true,
},
],
// require assignment operator shorthand where possible or prohibit it entirely
// https://eslint.org/docs/rules/operator-assignment
"operator-assignment": ["warn", "always"],
// Requires operator at the beginning of the line in multiline statements
// https://eslint.style/rules/default/operator-linebreak
"@stylistic/operator-linebreak": ["warn", "before", { overrides: { "=": "none" } }],
// disallow padding within blocks
// https://eslint.style/rules/default/padded-blocks
"@stylistic/padded-blocks": [
"warn",
{
blocks: "never",
classes: "never",
switches: "never",
},
{
allowSingleLineBlocks: true,
},
],
// Disallow the use of Math.pow in favor of the ** operator
// https://eslint.org/docs/rules/prefer-exponentiation-operator
"prefer-exponentiation-operator": "warn",
// Prefer use of an object spread over Object.assign
// https://eslint.org/docs/rules/prefer-object-spread
"prefer-object-spread": "warn",
// require quotes around object literal property names
// https://eslint.style/rules/default/quote-props.html
"@stylistic/quote-props": [
"warn",
"as-needed",
{ keywords: false, unnecessary: true, numbers: false },
],
// specify whether double or single quotes should be used
// https://eslint.style/rules/default/quotes
"@stylistic/quotes": ["warn", "double", { avoidEscape: true, allowTemplateLiterals: true }],
// require or disallow use of semicolons instead of ASI
// https://eslint.style/rules/default/semi
"@stylistic/semi": ["warn", "always"],
// enforce spacing before and after semicolons
// https://eslint.style/rules/default/semi-spacing
"@stylistic/semi-spacing": ["warn", { before: false, after: true }],
// Enforce location of semicolons
// https://eslint.style/rules/default/semi-style
"@stylistic/semi-style": ["warn", "last"],
// require or disallow space before blocks
// https://eslint.style/rules/default/space-before-blocks
"@stylistic/space-before-blocks": "warn",
// require or disallow space before function opening parenthesis
// https://eslint.style/rules/default/space-before-function-paren
"@stylistic/space-before-function-paren": [
"warn",
{
anonymous: "always",
named: "never",
asyncArrow: "always",
},
],
// require or disallow spaces inside parentheses
// https://eslint.style/rules/default/space-in-parens
"@stylistic/space-in-parens": ["warn", "never"],
// require spaces around operators
// https://eslint.style/rules/default/space-infix-ops
"@stylistic/space-infix-ops": "warn",
// Require or disallow spaces before/after unary operators
// https://eslint.style/rules/default/space-unary-ops
"@stylistic/space-unary-ops": [
"warn",
{
words: true,
nonwords: false,
overrides: {},
},
],
// require or disallow a space immediately following the // or /* in a comment
// https://eslint.style/rules/default/spaced-comment
"@stylistic/spaced-comment": [
"warn",
"always",
{
line: {
exceptions: ["-", "+"],
markers: ["=", "!", "/"], // space here to support sprockets directives, slash for TS /// comments
},
block: {
exceptions: ["-", "+"],
markers: ["=", "!", ":", "::"], // space here to support sprockets directives and flow comment types
balanced: true,
},
},
],
// Enforce spacing around colons of switch statements
// https://eslint.style/rules/default/switch-colon-spacing
"@stylistic/switch-colon-spacing": ["warn", { after: true, before: false }],
// Require or disallow spacing between template tags and their literals
// https://eslint.style/rules/default/template-tag-spacing
"@stylistic/template-tag-spacing": ["warn", "never"],
// Disallow multiple spaces
// https://eslint.style/rules/default/no-multi-spaces
"@stylistic/no-multi-spaces": "warn",
},
},
// BEST PRACTICES
{
rules: {
"@typescript-eslint/default-param-last": "warn",
"@typescript-eslint/dot-notation": ["warn", { allowKeywords: true }],
"@typescript-eslint/no-implied-eval": "error",
"@typescript-eslint/no-loop-func": "warn",
"@typescript-eslint/no-redeclare": "error",
"@typescript-eslint/no-unused-expressions": [
"warn",
{
allowShortCircuit: false,
allowTernary: false,
allowTaggedTemplates: false,
},
],
"@typescript-eslint/return-await": "error",
"@typescript-eslint/no-shadow": ["error", { allow: ["i", "j"] }],
"no-shadow-restricted-names": "error",
"@typescript-eslint/no-loss-of-precision": "error",
},
},
// ERRORS
{
rules: {
// Enforce “for” loop update clause moving the counter in the right direction
// https://eslint.org/docs/rules/for-direction
"for-direction": "error",
// Enforces that a return statement is present in property getters
// https://eslint.org/docs/rules/getter-return
"getter-return": ["error", { allowImplicit: true }],
// disallow using an async function as a Promise executor
// https://eslint.org/docs/rules/no-async-promise-executor
"no-async-promise-executor": "error",
// Disallow await inside of loops
// https://eslint.org/docs/rules/no-await-in-loop
"no-await-in-loop": "error",
// Disallow comparisons to negative zero
// https://eslint.org/docs/rules/no-compare-neg-zero
"no-compare-neg-zero": "error",
// disallow assignment in conditional expressions
"no-cond-assign": ["error", "always"],
// disallow use of console
"no-console": "warn",
// Disallows expressions where the operation doesn't affect the value
// https://eslint.org/docs/rules/no-constant-binary-expression
"no-constant-binary-expression": "error",
// disallow use of constant expressions in conditions
"no-constant-condition": "warn",
// disallow control characters in regular expressions
"no-control-regex": "error",
// disallow use of debugger
"no-debugger": "error",
// disallow duplicate arguments in functions
"no-dupe-args": "error",
// Disallow duplicate conditions in if-else-if chains
// https://eslint.org/docs/rules/no-dupe-else-if
"no-dupe-else-if": "error",
// disallow duplicate keys when creating object literals
"no-dupe-keys": "error",
// disallow a duplicate case label.
"no-duplicate-case": "error",
// disallow empty statements
"no-empty": "error",
// disallow the use of empty character classes in regular expressions
"no-empty-character-class": "error",
// disallow assigning to the exception in a catch block
"no-ex-assign": "error",
// disallow double-negation boolean casts in a boolean context
// https://eslint.org/docs/rules/no-extra-boolean-cast
"no-extra-boolean-cast": "error",
// disallow unnecessary parentheses
// https://eslint.org/docs/rules/no-extra-parens
"@typescript-eslint/no-extra-parens": [
"warn",
"all",
{
conditionalAssign: true,
nestedBinaryExpressions: false,
returnAssign: false,
ignoreJSX: "all", // delegate to eslint-plugin-react
enforceForArrowConditionals: false,
},
],
// disallow unnecessary semicolons
"no-extra-semi": "error",
// disallow overwriting functions written as function declarations
"no-func-assign": "error",
// https://eslint.org/docs/rules/no-import-assign
"no-import-assign": "error",
// disallow function or variable declarations in nested blocks
"svelte/no-inner-declarations": "error",
// disallow invalid regular expression strings in the RegExp constructor
"no-invalid-regexp": "error",
// disallow irregular whitespace outside of strings and comments
"no-irregular-whitespace": "error",
// Disallow Number Literals That Lose Precision
// https://eslint.org/docs/rules/no-loss-of-precision
"no-loss-of-precision": "error",
// Disallow characters which are made with multiple code points in character class syntax
// https://eslint.org/docs/rules/no-misleading-character-class
"no-misleading-character-class": "error",
// disallow the use of object properties of the global object (Math and JSON) as functions
"no-obj-calls": "error",
// Disallow new operators with global non-constructor functions
// https://eslint.org/docs/latest/rules/no-new-native-nonconstructor
"no-new-native-nonconstructor": "error",
// Disallow returning values from Promise executor functions
// https://eslint.org/docs/rules/no-promise-executor-return
"no-promise-executor-return": "error",
// disallow use of Object.prototypes builtins directly
// https://eslint.org/docs/rules/no-prototype-builtins
"no-prototype-builtins": "error",
// disallow multiple spaces in a regular expression literal
"no-regex-spaces": "error",
// Disallow returning values from setters
// https://eslint.org/docs/rules/no-setter-return
"no-setter-return": "error",
// disallow sparse arrays
"no-sparse-arrays": "error",
// Disallow template literal placeholder syntax in regular strings
// https://eslint.org/docs/rules/no-template-curly-in-string
"no-template-curly-in-string": "error",
// Avoid code that looks like two expressions but is actually one
// https://eslint.org/docs/rules/no-unexpected-multiline
"no-unexpected-multiline": "error",
// disallow unreachable statements after a return, throw, continue, or break statement
"no-unreachable": "error",
// Disallow loops with a body that allows only one iteration
// https://eslint.org/docs/rules/no-unreachable-loop
"no-unreachable-loop": "error",
// disallow return/throw/break/continue inside finally blocks
// https://eslint.org/docs/rules/no-unsafe-finally
"no-unsafe-finally": "error",
// disallow negating the left operand of relational operators
// https://eslint.org/docs/rules/no-unsafe-negation
"no-unsafe-negation": "error",
// disallow use of optional chaining in contexts where the undefined value is not allowed
// https://eslint.org/docs/rules/no-unsafe-optional-chaining
"no-unsafe-optional-chaining": ["error", { disallowArithmeticOperators: true }],
// Disallow Unused Private Class Members
// https://eslint.org/docs/rules/no-unused-private-class-members
"no-unused-private-class-members": "warn",
// Disallow useless backreferences in regular expressions
// https://eslint.org/docs/rules/no-useless-backreference
"no-useless-backreference": "error",
// disallow comparisons with the value NaN
"use-isnan": "error",
// ensure that the results of typeof are compared against a valid string
// https://eslint.org/docs/rules/valid-typeof
"valid-typeof": ["error", { requireStringLiterals: true }],
},
},
// Svelte errors
{
rules: {
"svelte/block-lang": ["error", { script: "ts", style: "postcss" }],
"svelte/no-target-blank": "error",
"svelte/valid-each-key": "error",
"svelte/require-optimized-style-attribute": "warn",
},
},
// Svelte styling
{
rules: {
"svelte/first-attribute-linebreak": "warn",
"svelte/html-closing-bracket-spacing": "warn",
"svelte/html-quotes": "warn",
"svelte/html-self-closing": "warn",
"svelte/indent": "warn",
"svelte/max-attributes-per-line": [
"warn",
{
multiline: 1,
singleline: 4,
},
],
"svelte/mustache-spacing": "warn",
"svelte/no-extra-reactive-curlies": "warn",
"svelte/no-spaces-around-equal-signs-in-attribute": "warn",
"svelte/prefer-class-directive": "warn",
"svelte/prefer-style-directive": "warn",
"svelte/shorthand-attribute": "warn",
"svelte/shorthand-directive": "warn",
"svelte/sort-attributes": "warn",
},
},
// Ignore
{
ignores: [
"build/",
".svelte-kit/",
"*.config.cjs",
"vite.config.js.timestamp-*",
"vite.config.ts.timestamp-*",
".tmp/",
],
},
];