stack/configs/eslint/defaults.js
Konstantin Wohlwend 5394614dd3 Upgrade ESLint
2026-02-27 10:58:28 -08:00

141 lines
4.6 KiB
JavaScript

module.exports = {
extends: [],
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
parserOptions: {
// use the package folder as the root for the TS ESLint plugin
project: "./tsconfig.json",
tsconfigRootDir: process.cwd(),
},
rules: {
"no-unused-expressions": ["error", { enforceForJSX: true }],
"no-trailing-spaces": "warn",
"eol-last": "error",
"key-spacing": "error",
"object-curly-spacing": ["error", "always"],
indent: [
"error",
2,
{
SwitchCase: 1,
ignoredNodes: [
"TSIntersectionType",
"TSTypeParameter",
"TSTypeParameterDeclaration",
"TSTypeParameterInstantiation",
"TSUnionType",
"ConditionalType",
"TSConditionalType",
"FunctionDeclaration",
"CallExpression",
"TemplateLiteral *",
],
},
],
"keyword-spacing": "warn",
"block-spacing": "warn",
"max-statements-per-line": "warn",
semi: ["error", "always"],
"no-fallthrough": "error",
"@typescript-eslint/switch-exhaustiveness-check": ["error", {
"considerDefaultExhaustiveForUnions": true,
}],
"@typescript-eslint/no-floating-promises": [
"error",
{
ignoreVoid: false,
},
],
"no-return-await": "off",
"@typescript-eslint/return-await": ["error", "always"],
"no-multiple-empty-lines": "warn",
"@typescript-eslint/await-thenable": "error",
// TODO: Re-introduce this rule when we migrate to the Stylistic plugin/ESLint 9+
/*"@typescript-eslint/member-delimiter-style": [
"error",
{
multiline: {
delimiter: "comma",
},
singleline: {
delimiter: "comma",
requireLast: false,
},
multilineDetection: "brackets",
},
],*/
"@typescript-eslint/no-unnecessary-condition": [
"error",
{ allowConstantLoopConditions: true },
],
"no-restricted-syntax": [
"error",
{
selector: 'SwitchCase > *.consequent[type!="BlockStatement"]',
message: "Switch cases without blocks are disallowed.",
},
{
selector: "CallExpression[callee.property.name='catch']:has(MemberExpression[object.name='console'])",
message: "Don't do .catch(console.error). Please handle errors explicitly, eg. with runAsynchronously or runAsynchronouslyWithAlert.",
},
{
selector:
"MemberExpression:has(Identifier[name='yupString']) > Identifier[name='url']",
message:
"Use urlSchema from schema-fields.tsx instead of yupString().url().",
},
{
selector:
"CallExpression > MemberExpression > Identifier[name='required']",
message:
`Use .defined(), .nonNullable(), or .nonEmpty() instead of .required(), as the latter has inconsistent/unexpected behavior on strings.`,
},
{
selector:
"CallExpression > MemberExpression:has(Identifier[name='yupString']) > Identifier[name='email']",
message:
`Use emailSchema instead of yupString().email().`,
},
{
selector:
"MemberExpression:has(Identifier[name='yup']):has(Identifier[name='string'], Identifier[name='number'], Identifier[name='boolean'], Identifier[name='array'], Identifier[name='object'], Identifier[name='tuple'], Identifier[name='date'], Identifier[name='mixed'])",
message: "Use yupXyz() from schema-fields.tsx instead of yup.xyz().",
},
{
selector:
"Identifier[name='localeCompare']",
message: "Use stringCompare() from utils/strings.tsx instead of String.prototype.localeCompare.",
},
{
selector: "CallExpression > MemberExpression[property.name='$transaction']",
message: "Calling .$transaction is disallowed. Use retryTransaction() instead.",
},
{
selector: "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='use']",
message: "Use `use` from @stack-shared/dist/utils/react instead (as it also supports React 18).",
},
],
"@typescript-eslint/no-misused-promises": [
"error",
{
checksConditionals: true,
},
],
"@typescript-eslint/require-array-sort-compare": "error",
"@typescript-eslint/consistent-type-definitions": ["error", "type"],
"no-restricted-imports": [
"error",
{
patterns: [
{
group: ["@vercel/functions"],
importNames: ["waitUntil"],
message:
'Use runAsynchronouslyAndWaitUntil instead.',
},
],
},
],
},
};