{
  "$schema": "./node_modules/oxlint/configuration_schema.json",
  "plugins": ["react", "typescript", "import", "jsx-a11y"],
  "jsPlugins": [
    "./packages/eslint-plugin-actual/lib/index.js",
    "eslint-plugin-typescript-paths",
    "eslint-plugin-perfectionist"
  ],
  "env": {
    "browser": true,
    "node": true,
    "vitest": true
  },
  "globals": {
    "vi": "readonly",
    "backend": "readonly",
    "importScripts": "readonly",
    "FS": "readonly"
  },
  "rules": {
    // TODO fix all these and re-enable
    "jsx-a11y/click-events-have-key-events": "off",
    "jsx-a11y/prefer-tag-over-role": "off",
    "jsx-a11y/tabindex-no-positive": "off",

    // Import sorting
    // TODO replace once oxfmt supports this: https://github.com/oxc-project/oxc/issues/17076
    "perfectionist/sort-imports": [
      "warn",
      {
        "groups": [
          "react",
          "builtin",
          "external",
          "loot-core",
          "parent",
          "sibling",
          "index",
          "desktop-client"
        ],
        "customGroups": [
          {
            "groupName": "react",
            "elementNamePattern": "^react(-.*)?$"
          },
          {
            "groupName": "loot-core",
            "elementNamePattern": "^loot-core"
          },
          {
            "groupName": "desktop-client",
            "elementNamePattern": "^@desktop-client"
          }
        ],
        "newlinesBetween": "always"
      }
    ],

    // Actual rules
    "actual/typography": "warn",
    "actual/no-untranslated-strings": "error",
    "actual/prefer-trans-over-t": "error",
    "actual/prefer-if-statement": "warn",
    "actual/prefer-logger-over-console": "error",
    "actual/object-shorthand-properties": "warn",
    "actual/prefer-const": "warn",
    "actual/no-anchor-tag": "warn",
    "actual/no-react-default-import": "warn",

    // JSX A11y rules
    "jsx-a11y/no-autofocus": [
      "warn",
      {
        "ignoreNonDOM": true
      }
    ],
    "jsx-a11y/alt-text": "warn",
    "jsx-a11y/anchor-has-content": "warn",
    "jsx-a11y/anchor-is-valid": [
      "warn",
      {
        "aspects": ["noHref", "invalidHref"]
      }
    ],
    "jsx-a11y/aria-activedescendant-has-tabindex": "warn",
    "jsx-a11y/aria-props": "warn",
    "jsx-a11y/aria-proptypes": "warn",
    "jsx-a11y/aria-role": [
      "warn",
      {
        "ignoreNonDOM": true
      }
    ],
    "jsx-a11y/aria-unsupported-elements": "warn",
    "jsx-a11y/heading-has-content": "warn",
    "jsx-a11y/iframe-has-title": "warn",
    "jsx-a11y/img-redundant-alt": "warn",
    "jsx-a11y/no-access-key": "warn",
    "jsx-a11y/no-distracting-elements": "warn",
    "jsx-a11y/no-redundant-roles": "warn",
    "jsx-a11y/role-has-required-aria-props": "warn",
    "jsx-a11y/role-supports-aria-props": "warn",
    "jsx-a11y/scope": "warn",

    // Typescript rules
    "typescript/ban-ts-comment": ["warn"],
    "typescript/consistent-type-definitions": ["warn", "type"],
    "typescript/consistent-type-imports": [
      "warn",
      {
        "prefer": "type-imports",
        "fixStyle": "inline-type-imports"
      }
    ],
    "typescript/no-implied-eval": "warn",
    "typescript/no-explicit-any": "warn",
    "typescript/no-restricted-types": [
      "warn",
      {
        "types": {
          // forbid FC as superfluous
          "FunctionComponent": {
            "message": "Type the props argument and let TS infer or use ComponentType for a component prop"
          },
          "FC": {
            "message": "Type the props argument and let TS infer or use ComponentType for a component prop"
          }
        }
      }
    ],
    "typescript/no-var-requires": "warn",

    // Import rules
    "import/first": "error",
    "import/no-amd": "error",
    "import/no-default-export": "warn",
    "import/no-webpack-loader-syntax": "error",
    "import/no-useless-path-segments": "warn",
    "import/no-unresolved": "warn",
    "import/no-unused-modules": "warn",
    "import/no-duplicates": [
      "warn",
      {
        "prefer-inline": true
      }
    ],

    // React rules
    "react/exhaustive-deps": [
      "warn",
      {
        "additionalHooks": "(useQuery|useEffectAfterMount)"
      }
    ],
    "react/jsx-curly-brace-presence": "warn",
    "react/jsx-filename-extension": [
      "warn",
      {
        "extensions": [".jsx", ".tsx"],
        "allow": "as-needed"
      }
    ],
    "react/jsx-no-comment-textnodes": "warn",
    "react/jsx-no-duplicate-props": "warn",
    "react/jsx-no-target-blank": "warn",
    "react/jsx-no-undef": "error",
    "react/jsx-no-useless-fragment": "warn",
    "react/jsx-pascal-case": [
      "warn",
      {
        "allowAllCaps": true,
        "ignore": []
      }
    ],
    "react/no-danger-with-children": "warn",
    "react/no-direct-mutation-state": "warn",
    "react/no-is-mounted": "warn",
    "react/no-unstable-nested-components": "warn",
    "react/require-render-return": "error",
    "react/rules-of-hooks": "error",
    "react/self-closing-comp": "warn",
    "react/style-prop-object": "warn",
    "react/jsx-boolean-value": "warn",

    // ESLint rules
    "eslint/array-callback-return": "warn",
    "eslint/curly": ["warn", "multi-line", "consistent"],
    "eslint/default-case": [
      "warn",
      {
        "commentPattern": "^no default$"
      }
    ],
    "eslint/eqeqeq": ["warn", "smart"],
    "eslint/no-array-constructor": "warn",
    "eslint/no-caller": "warn",
    "eslint/no-cond-assign": ["warn", "except-parens"],
    "eslint/no-const-assign": "warn",
    "eslint/no-control-regex": "warn",
    "eslint/no-delete-var": "warn",
    "eslint/no-dupe-class-members": "warn",
    "eslint/no-dupe-keys": "warn",
    "eslint/no-duplicate-case": "warn",
    "eslint/no-empty-character-class": "warn",
    "eslint/no-empty-function": "warn",
    "eslint/no-empty-pattern": "warn",
    "eslint/no-eval": "warn",
    "eslint/no-ex-assign": "warn",
    "eslint/no-extend-native": "warn",
    "eslint/no-extra-bind": "warn",
    "eslint/no-extra-label": "warn",
    "eslint/no-fallthrough": "warn",
    "eslint/no-func-assign": "warn",
    "eslint/no-invalid-regexp": "warn",
    "eslint/no-iterator": "warn",
    "eslint/no-label-var": "warn",
    "eslint/no-var": "warn",
    "eslint/no-labels": [
      "warn",
      {
        "allowLoop": true,
        "allowSwitch": false
      }
    ],
    "eslint/no-new-func": "warn",
    "eslint/no-script-url": "warn",
    "eslint/no-self-assign": "warn",
    "eslint/no-self-compare": "warn",
    "eslint/no-sequences": "warn",
    "eslint/no-shadow-restricted-names": "warn",
    "eslint/no-sparse-arrays": "warn",
    "eslint/no-template-curly-in-string": "warn",
    "eslint/no-this-before-super": "warn",
    "eslint/no-throw-literal": "warn",
    "eslint/no-unreachable": "warn",
    "eslint/no-obj-calls": "warn",
    "eslint/no-new-wrappers": "warn",
    "eslint/no-unsafe-negation": "warn",
    "eslint/no-multi-str": "warn",
    "eslint/no-global-assign": "warn",
    "eslint/no-lone-blocks": "warn",
    "eslint/no-unused-labels": "warn",
    "eslint/no-object-constructor": "warn",
    "eslint/no-new-native-nonconstructor": "warn",
    "eslint/no-redeclare": "warn",
    "eslint/no-useless-computed-key": "warn",
    "eslint/no-useless-concat": "warn",
    "eslint/no-useless-escape": "warn",
    "eslint/require-yield": "warn",
    "eslint/getter-return": "warn",
    "eslint/unicode-bom": ["warn", "never"],
    "eslint/no-use-isnan": "warn",
    "eslint/valid-typeof": "warn",
    "eslint/no-useless-rename": [
      "warn",
      {
        "ignoreDestructuring": false,
        "ignoreImport": false,
        "ignoreExport": false
      }
    ],
    "eslint/no-with": "warn",
    "eslint/no-regex-spaces": "warn",
    "eslint/no-restricted-globals": [
      "warn",

      // https://github.com/facebook/create-react-app/tree/main/packages/confusing-browser-globals
      "addEventListener",
      "blur",
      "close",
      "closed",
      "confirm",
      "defaultStatus",
      "defaultstatus",
      "event",
      "external",
      "find",
      "focus",
      "frameElement",
      "frames",
      "history",
      "innerHeight",
      "innerWidth",
      "length",
      "location",
      "locationbar",
      "menubar",
      "moveBy",
      "moveTo",
      "name",
      "onblur",
      "onerror",
      "onfocus",
      "onload",
      "onresize",
      "onunload",
      "open",
      "opener",
      "opera",
      "outerHeight",
      "outerWidth",
      "pageXOffset",
      "pageYOffset",
      "parent",
      "print",
      "removeEventListener",
      "resizeBy",
      "resizeTo",
      "screen",
      "screenLeft",
      "screenTop",
      "screenX",
      "screenY",
      "scroll",
      "scrollbars",
      "scrollBy",
      "scrollTo",
      "scrollX",
      "scrollY",
      "status",
      "statusbar",
      "stop",
      "toolbar",
      "top"
    ],
    "eslint/no-restricted-imports": [
      "warn",
      {
        "paths": [
          {
            "name": "react-router",
            "importNames": ["useNavigate"],
            "message": "Please import Actual's useNavigate() hook from `src/hooks` instead."
          },
          {
            "name": "react-redux",
            "importNames": ["useDispatch"],
            "message": "Please import Actual's useDispatch() hook from `src/redux` instead."
          },
          {
            "name": "react-redux",
            "importNames": ["useSelector"],
            "message": "Please import Actual's useSelector() hook from `src/redux` instead."
          },
          {
            "name": "react-redux",
            "importNames": ["useStore"],
            "message": "Please import Actual's useStore() hook from `src/redux` instead."
          }
        ],
        "patterns": [
          {
            "group": ["**/*.api", "**/*.web", "**/*.electron"],
            "message": "Don't directly reference imports from other platforms"
          },
          {
            "group": ["uuid"],
            "importNames": ["*"],
            "message": "Use `import { v4 as uuidv4 } from 'uuid'` instead"
          },
          {
            "group": ["**/style", "**/colors"],
            "importNames": ["colors"],
            "message": "Please use themes instead of colors"
          },
          {
            "group": ["@actual-app/web/**/*"],
            "message": "Please do not import `@actual-app/web` in `loot-core`"
          }
        ]
      }
    ],
    "eslint/no-useless-constructor": "warn",
    "eslint/no-undef": "warn",
    "eslint/no-unused-expressions": "warn"
  },
  "overrides": [
    {
      // TODO: fix the issues in these files
      "files": [
        "packages/component-library/src/Menu.tsx",
        "packages/desktop-client/src/components/accounts/Account.jsx",
        "packages/desktop-client/src/components/accounts/MobileAccount.jsx",
        "packages/desktop-client/src/components/accounts/MobileAccounts.jsx",
        "packages/desktop-client/src/components/budget/BudgetCategories.jsx",
        "packages/desktop-client/src/components/budget/BudgetSummaries.tsx",
        "packages/desktop-client/src/components/budget/DynamicBudgetTable.tsx",
        "packages/desktop-client/src/components/budget/envelope/HoldMenu.tsx",
        "packages/desktop-client/src/components/budget/envelope/TransferMenu.tsx",
        "packages/desktop-client/src/components/budget/index.tsx",
        "packages/desktop-client/src/components/budget/MobileBudget.tsx",
        "packages/desktop-client/src/components/FinancesApp.tsx",
        "packages/desktop-client/src/components/GlobalKeys.ts",
        "packages/desktop-client/src/components/LoggedInUser.tsx",
        "packages/desktop-client/src/components/manager/ManagementApp.jsx",
        "packages/desktop-client/src/components/manager/subscribe/common.tsx",
        "packages/desktop-client/src/components/ManageRules.tsx",
        "packages/desktop-client/src/components/mobile/MobileAmountInput.jsx",
        "packages/desktop-client/src/components/mobile/MobileNavTabs.tsx",
        "packages/desktop-client/src/components/Modals.tsx",
        "packages/desktop-client/src/components/modals/EditRule.jsx",
        "packages/desktop-client/src/components/modals/ImportTransactions.jsx",
        "packages/desktop-client/src/components/modals/ImportTransactionsModal/ImportTransactionsModal.tsx",
        "packages/desktop-client/src/components/modals/MergeUnusedPayees.jsx",
        "packages/desktop-client/src/components/Notifications.tsx",
        "packages/desktop-client/src/components/payees/ManagePayees.jsx",
        "packages/desktop-client/src/components/payees/ManagePayeesWithData.jsx",
        "packages/desktop-client/src/components/payees/PayeeTable.tsx",
        "packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTable.tsx",
        "packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableTotals.tsx",
        "packages/desktop-client/src/components/reports/reports/CashFlowCard.jsx",
        "packages/desktop-client/src/components/reports/reports/CustomReport.jsx",
        "packages/desktop-client/src/components/reports/reports/CustomReport.tsx",
        "packages/desktop-client/src/components/reports/reports/NetWorthCard.jsx",
        "packages/desktop-client/src/components/reports/SaveReportName.tsx",
        "packages/desktop-client/src/components/reports/useReport.ts",
        "packages/desktop-client/src/components/schedules/ScheduleDetails.jsx",
        "packages/desktop-client/src/components/schedules/ScheduleEditModal.tsx",
        "packages/desktop-client/src/components/schedules/SchedulesTable.tsx",
        "packages/desktop-client/src/components/select/DateSelect.tsx",
        "packages/desktop-client/src/components/sidebar/Tools.tsx",
        "packages/desktop-client/src/components/sort.tsx",
        "packages/desktop-client/src/hooks/useEffectAfterMount.ts",
        "packages/desktop-client/src/hooks/useQuery.ts"
      ],
      "rules": {
        "react/exhaustive-deps": "off"
      }
    },
    {
      "files": ["**/*.test.{js,ts,jsx,tsx}", "packages/docs/**/*"],
      "rules": {
        "actual/no-untranslated-strings": "off",
        "actual/prefer-logger-over-console": "off"
      }
    },
    {
      "files": [
        "packages/api/migrations/*",
        "packages/loot-core/migrations/*",
        "packages/sync-server/src/app-gocardless/banks/*.js",
        "*.config.{ts,mts,mjs}"
      ],
      "rules": {
        "import/no-default-export": "off"
      }
    },
    {
      "files": ["packages/docs/**/*"],
      "rules": {
        "actual/no-anchor-tag": "off"
      }
    },
    {
      "files": ["packages/desktop-client/**/*.{js,ts,jsx,tsx}"],
      "rules": {
        "typescript-paths/absolute-parent-import": [
          "error",
          { "preferPathOverBaseUrl": true }
        ],
        "typescript-paths/absolute-import": ["error", { "enableAlias": false }]
      }
    },
    // TODO: enable these
    {
      "files": [
        "packages/desktop-client/src/components/ManageRules.tsx",
        "packages/desktop-client/src/components/mobile/budget/ExpenseGroupList.tsx",
        "packages/desktop-client/src/components/reports/reports/Calendar.tsx",
        "packages/desktop-client/src/components/table.tsx"
      ],
      "rules": {
        "eslint/no-empty-function": "off"
      }
    }
  ]
}
