UNPKG

@spaced-out/ui-design-system

Version:
263 lines (245 loc) 8.79 kB
// ESLint 9 flat config for @spaced-out/ui-design-system // TypeScript-first config (Flow removed) import tseslint from 'typescript-eslint'; import ftFlowPlugin from 'eslint-plugin-ft-flow'; import reactPlugin from 'eslint-plugin-react'; import reactHooksPlugin from 'eslint-plugin-react-hooks'; import importPlugin from 'eslint-plugin-import'; import simpleImportSortPlugin from 'eslint-plugin-simple-import-sort'; import unusedImportsPlugin from 'eslint-plugin-unused-imports'; import jestPlugin from 'eslint-plugin-jest'; import storybookPlugin from 'eslint-plugin-storybook'; import globals from 'globals'; export default [ // Global ignores (replaces .eslintignore) { ignores: [ 'node_modules/**', 'lib/**', 'dist/**', 'storybook-static/**', 'src/assets/fontawesome/**', ], }, // TypeScript rules for project source (non type-aware for now) //NOTE:(diwaker) make it type-aware later // Bring in TS-ESLint recommended (no type checking) scoped to TS files ...tseslint.configs.recommended.map((cfg) => ({ ...cfg, files: ['src/**/*.{ts,tsx}'], languageOptions: { ...(cfg.languageOptions ?? {}), parserOptions: { ...(cfg.languageOptions?.parserOptions ?? {}), ecmaVersion: 'latest', sourceType: 'module', ecmaFeatures: {jsx: true}, }, globals: { ...globals.browser, ...globals.node, Iterator: 'readonly', }, }, settings: { react: {version: 'detect'}, }, plugins: { '@typescript-eslint': tseslint.plugin, // Keep Flow plugin loaded so legacy inline directives don't error 'ft-flow': ftFlowPlugin, react: reactPlugin, 'react-hooks': reactHooksPlugin, import: importPlugin, 'simple-import-sort': simpleImportSortPlugin, 'unused-imports': unusedImportsPlugin, jest: jestPlugin, storybook: storybookPlugin, }, })), // Local project rule tweaks { files: ['src/**/*.{ts,tsx}'], rules: { // Core 'arrow-body-style': ['warn', 'as-needed'], curly: ['warn', 'all'], 'comma-spacing': ['warn', {before: false, after: true}], 'eol-last': 'warn', eqeqeq: ['error', 'always', {null: 'ignore'}], indent: 'off', 'max-params': ['warn', {max: 4}], 'no-alert': 'warn', 'no-console': ['error', {allow: ['warn', 'error']}], 'no-dupe-args': 'error', 'no-dupe-keys': 'error', 'no-duplicate-case': 'error', // Allow TS overloads 'no-dupe-class-members': 'off', 'no-empty-function': 'warn', 'no-empty-pattern': 'error', 'no-eval': 'error', 'no-extend-native': 'error', 'no-extra-boolean-cast': 'error', 'no-fallthrough': 'error', 'no-floating-decimal': 'error', 'no-inner-declarations': 'error', 'no-loop-func': 'error', 'no-mixed-requires': 'off', 'no-nested-ternary': 'off', 'no-new-symbol': 'error', 'no-redeclare': 'off', // use TS rule 'no-restricted-globals': ['error', 'event', 'location'], 'no-sparse-arrays': 'error', 'no-tabs': 'warn', 'no-this-before-super': 'error', 'no-trailing-spaces': 'warn', 'no-underscore-dangle': 'off', 'no-unmodified-loop-condition': 'warn', 'no-undef': 'off', // TS handles this 'no-unreachable': 'warn', 'no-useless-constructor': 'off', // use TS rule 'no-var': 'error', 'no-with': 'error', 'object-shorthand': 'error', 'prefer-arrow-callback': 'error', 'prefer-const': 'warn', 'prefer-spread': 'warn', quotes: ['warn', 'single', {allowTemplateLiterals: true}], 'require-yield': 'error', 'rest-spread-spacing': 'error', strict: ['warn', 'never'], // import 'import/newline-after-import': ['warn', {count: 2}], 'import/no-duplicates': 'error', // react 'react/jsx-no-undef': 'error', 'react/jsx-pascal-case': 'warn', 'react/jsx-uses-react': 'off', 'react/jsx-uses-vars': 'error', 'react/no-array-index-key': 'warn', 'react/no-deprecated': 'error', 'react/no-direct-mutation-state': 'error', 'react/no-string-refs': 'warn', 'react/prop-types': 'off', 'react/react-in-jsx-scope': 'off', // hooks //NOTE:(diwaker) temp keep as warn during migration; tighten later 'react-hooks/rules-of-hooks': 'warn', // unused 'unused-imports/no-unused-imports': 'error', 'unused-imports/no-unused-vars': [ 'error', { vars: 'all', varsIgnorePattern: '^_', args: 'after-used', argsIgnorePattern: '^_', }, ], // TS-specific replacements (non type-aware) '@typescript-eslint/no-redeclare': 'error', '@typescript-eslint/no-useless-constructor': 'error', '@typescript-eslint/no-explicit-any': ['error', {ignoreRestArgs: true}], '@typescript-eslint/no-unused-vars': [ 'error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_', }, ], '@typescript-eslint/no-unused-expressions': [ 'error', { allowShortCircuit: false, allowTaggedTemplates: false, allowTernary: false, }, ], '@typescript-eslint/no-this-alias': 'error', '@typescript-eslint/ban-ts-comment': 'error', '@typescript-eslint/no-empty-object-type': 'error', '@typescript-eslint/no-unnecessary-type-constraint': 'error', '@typescript-eslint/no-non-null-assertion': 'warn', '@typescript-eslint/prefer-as-const': 'error', // ADDITIONAL NON TYPE-AWARE RULES (Valid for v8.39.1) // '@typescript-eslint/consistent-type-definitions': ['error', 'interface'], '@typescript-eslint/explicit-function-return-type': 'off', // Too strict for most projects '@typescript-eslint/explicit-member-accessibility': 'off', // Too strict for most projects '@typescript-eslint/explicit-module-boundary-types': 'off', // Too strict for most projects '@typescript-eslint/method-signature-style': ['error', 'property'], '@typescript-eslint/naming-convention': [ 'error', {selector: 'interface', format: ['PascalCase']}, {selector: 'typeAlias', format: ['PascalCase']}, {selector: 'enum', format: ['PascalCase']}, {selector: 'enumMember', format: ['UPPER_CASE']}, ], '@typescript-eslint/no-confusing-non-null-assertion': 'error', '@typescript-eslint/no-duplicate-enum-values': 'error', '@typescript-eslint/no-empty-interface': 'error', '@typescript-eslint/no-extra-non-null-assertion': 'error', '@typescript-eslint/no-extraneous-class': 'error', '@typescript-eslint/no-inferrable-types': 'error', '@typescript-eslint/no-namespace': 'error', '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error', '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', '@typescript-eslint/no-require-imports': 'error', '@typescript-eslint/no-type-alias': 'off', // Too restrictive }, }, // Tests (JS/TS) { files: ['**/__tests__/*.{js,jsx,ts,tsx}'], languageOptions: { globals: globals.jest, }, plugins: { jest: jestPlugin, }, }, // Storybook specifics { files: ['**/*.stories.{jsx,tsx}'], rules: { semi: ['warn', 'always'], }, }, // Project import sort groups (apply to JS and TS) // put this as the LAST item in your export default array { files: ['**/*.{ts,tsx,js,jsx}'], plugins: { 'simple-import-sort': (await import('eslint-plugin-simple-import-sort')) .default, }, rules: { 'simple-import-sort/imports': [ 'error', { groups: [ // ---------- 1) ALL TYPE IMPORTS (one block, grouped together) ---------- ['^type:'], // ---------- 2) VALUE IMPORTS ---------- // React first ['^react$'], // then external packages ['^@?\\w'], // then src/* (non-components first) ['^src/(?!components/)'], // then components ['^src/components/'], // side-effects ['^\\u0000'], // relatives ['^\\.\\.(?!/?$)', '^\\.\\./?$'], ['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'], // ---------- 3) CSS LAST ---------- ['^.+\\.css$'], ], }, ], // optional: keep two blank lines after imports (helps readability) 'import/newline-after-import': ['warn', {count: 2}], }, }, ];