Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
imports-linting: order, access
/** Разрешенные импорты (для сортировки) */
const ALLOWED_PATH_GROUPS = ["pages/**", "features/**", "entities/**", "shared/**"].map(
(pattern) => ({
pattern,
group: "internal",
position: "after",
}),
);
/** Для запрета приватных путей */
const DENIED_PATH_GROUPS = [
// Private imports are prohibited, use public imports instead
"app/**",
"pages/*/**",
"features/*/**",
"entities/*/**",
"shared/*/*/**", // Для shared +1 уровень, т.к. там чаще мы обращаемся к конкретной библиотеке/компоненты
// Prefer absolute imports instead of relatives (for root modules)
"../**/app",
"../**/pages",
"../**/features",
"../**/entities",
"../**/shared",
];
module.exports = {
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: 2020,
ecmaFeatures: {
jsx: true,
modules: true,
},
sourceType: "module",
},
env: {
browser: true,
es6: true,
},
plugins: ["react", "@typescript-eslint"],
extends: [
"react-app",
"eslint:recommended",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:import/typescript",
"plugin:prettier/recommended",
"plugin:react/recommended",
"prettier",
],
rules: {
// TODO: eslint-plugin-boundaries
"import/order": [
2,
{
pathGroups: ALLOWED_PATH_GROUPS,
pathGroupsExcludedImportTypes: ["builtin"],
groups: ["builtin", "external", "internal", "parent", "sibling", "index"],
},
],
"no-restricted-imports": [
2,
{
patterns: DENIED_PATH_GROUPS
}
],
},
};
@SQReder
Copy link

SQReder commented Oct 4, 2021

This is a config for the boundaries plugin to get started from. Take a note that ~/ prefix is used for absolute imports, so extra configuration in tsconfig.json is required. If you won't use this prefix for just remove ~/ from rules config

Eslint plugins:

@typescript-eslint/eslint-plugin
@typescript-eslint/parser
eslint-import-resolver-typescript
eslint-plugin-boundaries
eslint-plugin-import

eslintrc.js

{
  "extends": ["plugin:import/errors", "plugin:import/warnings", "plugin:import/typescript", "plugin:boundaries/recommended"],
  "parser": "@typescript-eslint/parser",
  "settings": {
    "import/resolver": { "typescript": {} },
    "boundaries/elements": [
      { "type": "app", "pattern": "app/*" },
      { "type": "processes", "pattern": "processes/*" },
      { "type": "pages", "pattern": "pages/*" },
      { "type": "widgets", "pattern": "widgets/*" },
      { "type": "features", "pattern": "features/*" },
      { "type": "entities", "pattern": "entities/*" },
      { "type": "shared", "pattern": "shared/*" }
    ],
    "boundaries/ignore": ["**/*.test.*"],
  },
  "rules": {
    "import/order": [
      "error",
      {
        "alphabetize": { "order": "asc", "caseInsensitive": true },
        "newlines-between": "always",
        "pathGroups": [
          { "group": "internal", "position": "after", "pattern": "~/processes/**" },
          { "group": "internal", "position": "after", "pattern": "~/pages/**" },
          { "group": "internal", "position": "after", "pattern": "~/widgets/**" },
          { "group": "internal", "position": "after", "pattern": "~/features/**" },
          { "group": "internal", "position": "after", "pattern": "~/entities/**" },
          { "group": "internal", "position": "after", "pattern": "~/shared/**" }
        ],
        "pathGroupsExcludedImportTypes": ["builtin"],
        "groups": ["builtin", "external", "internal", "parent", "sibling", "index"]
      }
    ],
    "no-restricted-imports": [
      "error",
      {
        "patterns": [
          { "message": "Private imports are prohibited, use public imports instead", "group": ["~/app/**"] },
          { "message": "Private imports are prohibited, use public imports instead", "group": ["~/processes/*/**"] },
          { "message": "Private imports are prohibited, use public imports instead", "group": ["~/pages/*/**"] },
          { "message": "Private imports are prohibited, use public imports instead", "group": ["~/widgets/*/**"] },
          { "message": "Private imports are prohibited, use public imports instead", "group": ["~/features/*/**"] },
          { "message": "Private imports are prohibited, use public imports instead", "group": ["~/entities/*/**"] },
          { "message": "Private imports are prohibited, use public imports instead", "group": ["~/shared/*/*/**"] },
          { "message": "Prefer absolute imports instead of relatives (for root modules)", "group": ["../**/app"] },
          { "message": "Prefer absolute imports instead of relatives (for root modules)", "group": ["../**/processes"] },
          { "message": "Prefer absolute imports instead of relatives (for root modules)", "group": ["../**/pages"] },
          { "message": "Prefer absolute imports instead of relatives (for root modules)", "group": ["../**/widgets"] },
          { "message": "Prefer absolute imports instead of relatives (for root modules)", "group": ["../**/features"] },
          { "message": "Prefer absolute imports instead of relatives (for root modules)", "group": ["../**/entities"] },
          { "message": "Prefer absolute imports instead of relatives (for root modules)", "group": ["../**/shared"] }
        ]
      }
    ],
    "boundaries/element-types": [
      "warn",
      {
        "default": "disallow",
        "rules": [
          { "from": "app", "allow": ["processes", "pages", "widgets", "features", "entities", "shared"] },
          { "from": "processes", "allow": ["pages", "widgets", "features", "entities", "shared"] },
          { "from": "pages", "allow": ["widgets", "features", "entities", "shared"] },
          { "from": "widgets", "allow": ["features", "entities", "shared"] },
          { "from": "features", "allow": ["entities", "shared"] },
          { "from": "entities", "allow": ["shared"] },
          { "from": "shared", "allow": ["shared"] }
        ]
      }
    ]
  },
  "overrides": [{ "files": ["**/*.test.*"], "rules": { "boundaries/element-types": "off" } }]
}

tsconfig.json

{
  "compilerOptions": {
    ...
    "paths": {
      "~/*": ["src/*"]
    }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment