/* eslint-disable rulesdir/typography */ const path = require('path'); const rulesDirPlugin = require('eslint-plugin-rulesdir'); rulesDirPlugin.RULES_DIR = path.join( __dirname, 'packages', 'eslint-plugin-actual', 'lib', 'rules', ); const ruleFCMsg = 'Type the props argument and let TS infer or use ComponentType for a component prop'; const restrictedImportPatterns = [ { 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", }, ]; const restrictedImportColors = [ { group: ['**/style', '**/colors'], importNames: ['colors'], message: 'Please use themes instead of colors', }, ]; module.exports = { plugins: ['prettier', 'import', 'rulesdir', '@typescript-eslint'], extends: ['react-app', 'plugin:@typescript-eslint/recommended'], parser: '@typescript-eslint/parser', parserOptions: { project: [path.join(__dirname, './tsconfig.json')] }, reportUnusedDisableDirectives: true, rules: { 'prettier/prettier': 'error', 'no-unused-vars': 'off', '@typescript-eslint/no-unused-vars': [ 'error', { args: 'none', varsIgnorePattern: '^_', ignoreRestSiblings: true, }, ], curly: ['error', 'multi-line', 'consistent'], 'no-restricted-globals': ['error'].concat( require('confusing-browser-globals').filter(g => g !== 'self'), ), 'react/jsx-no-useless-fragment': 'error', 'react/self-closing-comp': 'error', 'rulesdir/typography': 'error', 'rulesdir/prefer-if-statement': 'error', // https://github.com/eslint/eslint/issues/16954 // https://github.com/eslint/eslint/issues/16953 'no-loop-func': 'off', // TODO: re-enable these rules 'react-hooks/exhaustive-deps': 'off', // 'react-hooks/exhaustive-deps': [ // 'error', // { // additionalHooks: 'useLiveQuery', // }, // ], 'import/extensions': [ 'error', 'never', { json: 'always', }, ], 'import/no-useless-path-segments': 'error', 'import/no-duplicates': ['error', { 'prefer-inline': true }], 'import/no-unused-modules': ['error', { unusedExports: true }], 'import/order': [ 'error', { alphabetize: { caseInsensitive: true, order: 'asc', }, groups: [ 'builtin', // Built-in types are first 'external', 'parent', 'sibling', 'index', // Then the index file ], 'newlines-between': 'always', pathGroups: [ // Enforce that React (and react-related packages) is the first import { group: 'builtin', pattern: 'react?(-*)', position: 'before' }, // Separate imports from Actual from "real" external imports { group: 'external', pattern: 'loot-{core,design}/**/*', position: 'after', }, ], pathGroupsExcludedImportTypes: ['react'], }, ], 'no-restricted-syntax': [ 'error', { // forbid React.* as they are legacy https://twitter.com/dan_abramov/status/1308739731551858689 selector: ":matches(MemberExpression[object.name='React'], TSQualifiedName[left.name='React'])", message: 'Using default React import is discouraged, please use named exports directly instead.', }, { // forbid <a> in favor of <LinkButton> or <ExternalLink> selector: 'JSXOpeningElement[name.name="a"]', message: 'Using <a> is discouraged, please use <LinkButton> or <ExternalLink> instead.', }, ], 'no-restricted-imports': [ 'error', { patterns: [...restrictedImportPatterns, ...restrictedImportColors] }, ], // Rules disable during TS migration '@typescript-eslint/no-var-requires': 'off', 'prefer-const': 'off', 'prefer-spread': 'off', '@typescript-eslint/no-empty-function': 'off', }, overrides: [ { files: ['.eslintrc.js', './**/.eslintrc.js'], parserOptions: { project: null }, rules: { '@typescript-eslint/consistent-type-exports': 'off', }, }, { files: [ './packages/desktop-client/**/*.{ts,tsx}', './packages/loot-core/src/client/**/*.{ts,tsx}', ], rules: { // enforce type over interface '@typescript-eslint/consistent-type-definitions': ['error', 'type'], // enforce import type '@typescript-eslint/consistent-type-imports': [ 'error', { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, ], '@typescript-eslint/ban-types': [ 'error', { types: { // forbid FC as superflous FunctionComponent: { message: ruleFCMsg }, FC: { message: ruleFCMsg }, }, extendDefaults: true, }, ], }, }, { files: ['./packages/loot-core/src/**/*'], rules: { 'no-restricted-imports': [ 'error', { patterns: [ ...restrictedImportPatterns, { group: ['loot-core/**'], message: 'Please use relative imports in loot-core instead of importing from `loot-core/*`', }, ], }, ], }, }, { files: [ 'packages/loot-core/src/types/**/*', 'packages/loot-core/src/client/state-types/**/*', '**/icons/**/*', '**/{mocks,__mocks__}/**/*', // can't correctly resolve usages '**/*.{testing,electron,browser,web,api}.ts', ], rules: { 'import/no-unused-modules': 'off' }, }, { //This is temporary. We will remove these as dark theme gets ported files: [ './packages/desktop-client/public/index.html', './packages/desktop-client/src/components/BankSyncStatus.*', './packages/desktop-client/src/components/LoggedInUser.*', './packages/desktop-client/src/components/MobileWebMessage.*', './packages/desktop-client/src/components/NotesButton.*', './packages/desktop-client/src/components/Notifications.*', './packages/desktop-client/src/components/Page.*', './packages/desktop-client/src/components/SidebarWithData.*', './packages/desktop-client/src/components/Titlebar.*', './packages/desktop-client/src/components/UpdateNotification.*', './packages/desktop-client/src/components/alerts.*', './packages/desktop-client/src/components/budget/MobileBudget.*', './packages/desktop-client/src/components/budget/MobileBudgetTable.*', './packages/desktop-client/src/components/budget/MobileTable.*', './packages/desktop-client/src/components/budget/MonthCountSelector.*', './packages/desktop-client/src/components/budget/MonthPicker.*', './packages/desktop-client/src/components/budget/constants.*', './packages/desktop-client/src/components/budget/misc.*', './packages/desktop-client/src/components/budget/report/BudgetSummary.*', './packages/desktop-client/src/components/budget/report/components.*', './packages/desktop-client/src/components/budget/rollover/BudgetSummary.*', './packages/desktop-client/src/components/budget/rollover/rollover-components.*', './packages/desktop-client/src/components/budget/util.*', './packages/desktop-client/src/components/common.*', './packages/desktop-client/src/components/common/Card.*', './packages/desktop-client/src/components/common/Label.*', './packages/desktop-client/src/components/common/View.*', './packages/desktop-client/src/components/common/ExternalLink.*', './packages/desktop-client/src/components/manager/BudgetList.*', './packages/desktop-client/src/components/manager/ConfigServer.*', './packages/desktop-client/src/components/manager/DeleteFile.*', './packages/desktop-client/src/components/manager/Import.*', './packages/desktop-client/src/components/manager/ImportActual.*', './packages/desktop-client/src/components/manager/ImportYNAB4.*', './packages/desktop-client/src/components/manager/ImportYNAB5.*', './packages/desktop-client/src/components/manager/WelcomeScreen.*', './packages/desktop-client/src/components/manager/subscribe/Bootstrap.*', './packages/desktop-client/src/components/manager/subscribe/ChangePassword.*', './packages/desktop-client/src/components/manager/subscribe/Error.*', './packages/desktop-client/src/components/manager/subscribe/Login.*', './packages/desktop-client/src/components/manager/subscribe/common.*', './packages/desktop-client/src/components/modals/BudgetSummary.*', './packages/desktop-client/src/components/modals/ConfirmCategoryDelete.*', './packages/desktop-client/src/components/modals/CreateEncryptionKey.*', './packages/desktop-client/src/components/modals/EditField.*', './packages/desktop-client/src/components/modals/FixEncryptionKey.*', './packages/desktop-client/src/components/modals/GoCardlessExternalMsg.*', './packages/desktop-client/src/components/modals/ImportTransactions.*', './packages/desktop-client/src/components/modals/LoadBackup.*', './packages/desktop-client/src/components/modals/MergeUnusedPayees.*', './packages/desktop-client/src/components/modals/PlaidExternalMsg.*', './packages/desktop-client/src/components/payees/index.*', './packages/desktop-client/src/components/reports/CashFlow.*', './packages/desktop-client/src/components/reports/Change.*', './packages/desktop-client/src/components/reports/DateRange.*', './packages/desktop-client/src/components/reports/Header.*', './packages/desktop-client/src/components/reports/NetWorth.*', './packages/desktop-client/src/components/reports/Overview.*', './packages/desktop-client/src/components/reports/Tooltip.*', './packages/desktop-client/src/components/reports/chart-theme.*', './packages/desktop-client/src/components/reports/graphs/CashFlowGraph.*', './packages/desktop-client/src/components/reports/graphs/NetWorthGraph.*', './packages/desktop-client/src/components/schedules/DiscoverSchedules.*', './packages/desktop-client/src/components/schedules/EditSchedule.*', './packages/desktop-client/src/components/schedules/LinkSchedule.*', './packages/desktop-client/src/components/schedules/PostsOfflineNotification.*', './packages/desktop-client/src/components/schedules/SchedulesTable.*', './packages/desktop-client/src/components/schedules/StatusBadge.*', './packages/desktop-client/src/components/schedules/index.*', './packages/desktop-client/src/components/select/DateSelect.*', './packages/desktop-client/src/components/select/RecurringSchedulePicker.*', './packages/desktop-client/src/components/settings/Encryption.*', './packages/desktop-client/src/components/settings/Experimental.*', './packages/desktop-client/src/components/settings/FixSplits.*', './packages/desktop-client/src/components/settings/Format.*', './packages/desktop-client/src/components/settings/Global.*', './packages/desktop-client/src/components/settings/UI.*', './packages/desktop-client/src/components/settings/index.*', './packages/desktop-client/src/components/sidebar.*', './packages/desktop-client/src/components/transactions/MobileTransaction.*', './packages/desktop-client/src/components/util/AmountInput.*', './packages/desktop-client/src/components/util/DisplayId.*', './packages/desktop-client/src/components/util/LoadComponent.*', './packages/desktop-client/src/style/*', ], rules: { 'no-restricted-imports': ['off', { patterns: restrictedImportColors }], }, }, ], settings: { 'import/parsers': { '@typescript-eslint/parser': ['.ts', '.tsx'], }, 'import/resolver': { typescript: { alwaysTryTypes: true, }, }, }, };