@spaced-out/ui-design-system
Version:
Sense UI components library
263 lines (245 loc) • 8.79 kB
JavaScript
// 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}],
},
},
];