Skip to content

Commit

Permalink
chore(deps): bump up all packages, add strict rules for project struc…
Browse files Browse the repository at this point in the history
…ture, update eslint config
  • Loading branch information
chertik77 committed Feb 19, 2025
1 parent 6fa852e commit 761d4ac
Show file tree
Hide file tree
Showing 9 changed files with 2,798 additions and 2,262 deletions.
102 changes: 0 additions & 102 deletions .eslintrc

This file was deleted.

121 changes: 121 additions & 0 deletions eslint.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import eslintPluginJs from '@eslint/js'
import eslintPluginQuery from '@tanstack/eslint-plugin-query'
import eslintPluginRouter from '@tanstack/eslint-plugin-router'
import eslintTsParser from '@typescript-eslint/parser'
import { projectStructurePlugin } from 'eslint-plugin-project-structure'
import eslintPluginReact from 'eslint-plugin-react'
import eslintPluginReactHooks from 'eslint-plugin-react-hooks'
import eslintTypescript from 'typescript-eslint'

import { independentModulesConfig } from './independentModules.mjs'
import eslintPluginTailwind from 'eslint-plugin-tailwindcss'

export default eslintTypescript.config(
{ ignores: ['**/vite-env.d.ts'] },
{
languageOptions: {
parser: eslintTsParser,
ecmaVersion: 'latest',
sourceType: 'module',
parserOptions: { warnOnUnsupportedTypeScriptVersion: false }
}
},
eslintPluginJs.configs.recommended,
eslintTypescript.configs.recommended,
eslintPluginReact.configs.flat.recommended,
...eslintPluginRouter.configs['flat/recommended'],
...eslintPluginQuery.configs['flat/recommended'],
...eslintPluginTailwind.configs['flat/recommended'],
{
rules: {
'no-restricted-syntax': [
'error',
{
selector:
"ImportDeclaration[source.value='react'] :matches(ImportDefaultSpecifier, ImportNamespaceSpecifier)",
message: 'Default React import is not allowed'
},
{
selector: 'Identifier[name="React"]',
message: 'Prefix React is not allowed'
}
],
'newline-before-return': 'error',
'arrow-body-style': ['warn', 'as-needed']
}
},
{
rules: {
'@typescript-eslint/no-import-type-side-effects': 'error',
'@typescript-eslint/consistent-type-definitions': ['error', 'type'],
'@typescript-eslint/array-type': ['error', { default: 'array' }],
'@typescript-eslint/naming-convention': [
'error',
{ selector: 'typeLike', format: ['PascalCase'] }
],
'@typescript-eslint/no-restricted-types': [
'error',
{
types: {
FC: 'Useless and has some drawbacks, see https://github.com/facebook/create-react-app/pull/8177'
}
}
]
}
},
{
plugins: { react: eslintPluginReact },
settings: { react: { version: 'detect' } },
rules: {
'react/react-in-jsx-scope': 'off',
'react/display-name': 'off',
'react/jsx-no-useless-fragment': 'error',
'react/boolean-prop-naming': [
'error',
{ rule: '^is[A-Z]([A-Za-z0-9]?)+', validateNested: true }
],
'react/destructuring-assignment': [
'warn',
'always',
{ destructureInSignature: 'always' }
],
'react/function-component-definition': [
'error',
{
namedComponents: 'arrow-function',
unnamedComponents: 'arrow-function'
}
],
'react/jsx-curly-brace-presence': [
'error',
{ props: 'never', children: 'never' }
],
'react/self-closing-comp': ['warn', { component: true, html: true }]
}
},
//? Simplify when https://github.com/facebook/react/pull/30774 is released
{
plugins: { 'react-hooks': eslintPluginReactHooks },
rules: { ...eslintPluginReactHooks.configs.recommended.rules }
},
{
plugins: { tailwindcss: eslintPluginTailwind },
settings: { tailwindcss: { callees: ['cn'] } },
rules: {
'tailwindcss/migration-from-tailwind-2': 'off',
'tailwindcss/no-custom-classname': [
'warn',
{ whitelist: ['shadow-none'] }
]
}
},
{
plugins: { 'project-structure': projectStructurePlugin },
rules: {
'project-structure/independent-modules': [
'error',
independentModulesConfig
]
}
}
)
80 changes: 80 additions & 0 deletions independentModules.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// @ts-check

import { createIndependentModules } from 'eslint-plugin-project-structure'

/**
* @param {string} folderName
*/
const createEntityRule = folderName => ({
name: `${folderName} folder`,
pattern: `src/entities/${folderName}/**`,
allowImportsFrom: [
'{family_3}/**',
'src/shared/**',
`src/entities/**/@x/${folderName}.ts`
],
errorMessage: `Entities may only: (1) import within their own folder, (2) use another entity through its @x/${folderName} folder inside the target entity, or (3) import from shared modules. Direct entity-to-entity imports are not allowed.`
})

export const independentModulesConfig = createIndependentModules({
modules: [
{
name: 'App folder',
pattern: 'src/app/**',
allowImportsFrom: [
'{family_1}/**',
'src/features/**',
'src/blocks/**',
'src/entities/**',
'src/shared/**'
]
},
{
name: 'Features',
pattern: 'src/features/**',
allowImportsFrom: ['{family_3}/**', 'src/shared/**', 'src/entities/**'],
errorMessage:
'A feature may only import from its own family (at least 3 common path parts), shared modules, or entities.'
},
{
name: 'Blocks',
pattern: 'src/blocks/**',
allowImportsFrom: [
'{family_3}/**',
'src/shared/**',
'src/entities/**',
'src/features/**'
],
errorMessage:
'A block may only import from its own family (at least 3 common path parts), shared modules, entities, or features.'
},
createEntityRule('auth'),
createEntityRule('user'),
createEntityRule('board'),
createEntityRule('column'),
createEntityRule('card'),
createEntityRule('dnd'),
{
name: 'Unknown entity',
pattern: 'src/entities/**',
allowImportsFrom: [],
allowExternalImports: false,
errorMessage:
'This entity is not specified as an independent module in independentModules.mjs. Use createEntityRule function.'
},
{
name: 'Shared',
pattern: ['src/shared/**'],
allowImportsFrom: ['{family}/**'],
errorMessage: 'Shared modules can only import from other shared modules.'
},
{
name: 'Unknown files',
pattern: [['src/**', '!src/*']],
allowImportsFrom: [],
allowExternalImports: false,
errorMessage:
'This file is not specified as an independent module in independentModules.jsonc.'
}
]
})
Loading

0 comments on commit 761d4ac

Please sign in to comment.