structural-police
eslint plugin для контроля структуры файлов и импортов
Установка
npm i eslint-plugin-structural-police
Быстрый старт
// .eslintrc.[js|yml|json]
{
"plugins": [
// ... какие-то твои плагины
"structural-police"
],
"extends": [
// ... какие-то твои конфиги плагинов
// один из конфигов
"plugin:structural-police/recommended"
// или
"plugin:structural-police/featureSliced"
],
}
Правила плагина
import-order
Настройка
Правило import-order
принимает 8 аргументов:
-
groups
:array of object - Массив групп импортов (подробнее в пункте "Группы") -
behaviorRelatedComment
:string - Вариант поведения комментариев, находящихся непосредственно перед импортов при использовании функции автофикса (флаг --fix). При значенииlink
(default value) все комментарии перемещаются вместе с импортом. При значенииremove
все комментарии удаляются -
blankLineAfterEveryGroup
:boolean - Нужно ли после каждой группы импортов вставлять пустую строку (default value: false) -
groupNamePrefix
:string - Префикс необходимый, чтобы отличать обычные комментарии от названий групп импортов (default value: ' # '). Например:// # GroupName
-
oldGroupNamePrefix
:string - Префикс, необходимый при изменении настройкиgroupNamePrefix
, чтобы удалить названия групп со старым префиксом -
withinGroupSort
:array of string - Массив строк формирующих регулярные выражения, в порядке которых будут сортироваться пути импортов внутри групп. То есть передав['^([A-Z]|[a-z])', '^[.]']
внутри группы будут идти сначала импорты начинающиеся с букв, а потом - с точки. При использовании этой настройки все импорты внутри группы будут отсортированы по алфавиту. Если нужно включить только сортировку по алфавиту, то нужно передать массив с одной регуляркой -['^']
-
customErrorMessages
:object - Объект с колбэками, формирующими тексты ошибок (подробнее в пункте "Кастомные сообщения ошибок") -
optimizedFix
:boolean - Если true (default value: false), то в начале файла добавляется ошибка аккумулятор, которая содержит фикс функцию, устраняющую все ошибки данного правила за один проход линта. Рекомендуется использовать при запуске с флагом--fix
, чтобы не запускать скрипт несколько раз для устранения всех ошибок
Группы
У каждого объекта группы есть 5 параметров:
-
name
. Если естьname
, то перед этой группой необходим комментарий с названием группы -
priority
отвечает за приоритет разбора импортов по группам. То есть если импорт удовлетворяет условиям двух групп, то он будет размещен в групу с наивысшим приоритетом (default value: 1) -
blankLineAfter
отвечает за наличие пустой строки после группы (default value: false) -
importPathMatch
содержит регулярное выражение или строку для формирования регулярного выражения (без флагов), по которому проверяется импортируемый путь -
importAbsPathMatch
содержит регулярное выражение или строку для формирования регулярного выражения (без флагов), по которому проверяется абсолютный путь до импортируемого файла. То есть находясь в файлеsrc/feature/index.ts
и импортируя из../ui/form.ts
, по регулярному выражениюimportAbsPathMatch
будет проверяться путьsrc/ui/form.ts
Пример
groups: [
{ priority: 0, importPathMatch: /^/, blankLineAfter: true },
{ name: 'actions', priority: 2, importAbsPathMatch: /Action/, blankLineAfter: false },
{ name: 'stores', priority: 2, importAbsPathMatch: /Store/ },
{ name: 'styles', priority: 1, importPathMatch: /.css$/, blankLineAfter: true },
]
Кастомные сообщения ошибок
Если есть желание изменить тексты ошибок выбрасываемых линтом, то можно сделать это, передав
в аргумент customErrorMessages
объект со своими колбэками.
В правиле есть три типа ошибок mustBeAfter
, blankLineDetected
, blankLineExpected
,
needAddComment
и needRemoveComment
, каждый из которых вызывается при разных проблемах:
-
mustBeAfter
вызывается, если нарушен порядок импортов -
blankLineDetected
вызывается, если обнаружена лишняя пустая строка между импортами -
blankLineExpected
вызывается, если после группы сblankLineAfter
нет пустой строки -
needAddComment
вызывается, если перед группой сname
нет комментария с названием -
needRemoveComment
вызывается для неправильных названий групп
Ниже приведен объект с дефолтными колбэками, тут же можно посмотреть какие аргументы должны принимать колбэки
const defaultErrorMessages = {
mustBeAfter: ({ importPath, mustBeAfterImportPath }) =>
`This import must be after import from "${mustBeAfterImportPath}"`,
blankLineDetected: ({ importPath, groupName }) =>
`Must not be blank line after import from "${importPath}"`,
blankLineExpected: ({ importPath, groupName }) =>
`Need blank line after imports group "${groupName}"`,
needAddComment: ({ groupName, newComment }) =>
`Before imports group "${groupName}" must be comment with groups name "${newComment}"`,
needRemoveComment: () =>
`Need remove wrong groups name`,
}
import-permission-schema
Настройка
Правило import-permission-schema
принимает 5 аргументов:
-
schema
:object - Схема валидации импортов (подробнее в пункте "Схема") -
inheritance
:boolean - Наследование правил. Если у дочернего узла нет собственных правил, то будут применены правила ближайшего родителя -
entryPoints
:array of string - Массив путей до валидируемых файлов -
everywhereAllowed
:array of string - Массив путей до файлов, импорт которых всегда доступен -
customErrorMessages
:object - Объект с колбэками, формирующими тексты ошибок (подробнее в пункте "Кастомные сообщения ошибок")
Схема
Схема описывает структуру проекта, по которой линтер будет проверять корректность расположения файлов и возможность осуществления импортов.
В схеме могут использоваться 3 ключех слова:
-
__rules__
- В данном объекте линтер будет искать правила для конкретного узла схемы -
__any__
- Разрешает любую структуру дочерниих файлов. Если__any__
установленtrue
, то линтер прекращает поиск правил глубже по схеме и применяет ко всем вложенным файлам уже найденые правила, если такие были -
__var__<variable_name>
- При помощи такой конструкции можно обобщить несколько разноименных директорий с одинаковой внутренней структурой, чтобы исключить дублирование в схеме. Например в директории apps у нас лежит три приложения app-a, app-b и app-c. Если у первых двух одинаковая структура, то схему можно составить следующим образом:
apps: {
'app-c': {
// Какая-то схема
},
__var__similar_apps: {
// Какая-то другая схема
},
},
В данном случае проверка будет происходить следующим образом:
Поиск правил для файла apps/app-c/index.ts
: Зашли в apps
, зашли в app-c
,
нашли правила для index.ts
Поиск правил для файла apps/app-a/index.ts
: Зашли в apps
, не нашли объект
app-a
, поэтому зашли в первый попавшийся __var__<variable_name>
, если такой есть
Кастомные сообщения ошибок
Если есть желание изменить тексты ошибок выбрасываемых линтом, то можно сделать это, передав
в аргумент customErrorMessages
объект со своими колбэками.
В правиле есть три типа ошибок importDisallow
, missingFile
и missingRules
, каждый из
которых вызывается при разных проблемах:
-
importDisallow
вызывается, если в файле найден импорт из запрещенного правилами (схемой) источника -
missingFile
вызывается, если проверяемый файл не описан в схеме -
missingRules
вызывается для проверяемого файла в схеме не найдены правила
Ниже приведен объект с дефолтными колбэками, тут же можно посмотреть какие аргументы должны принимать колбэки
const defaultErrorMessages = {
importDisallow: ({ filePath, importPath, absoluteImportPath }) =>
`Not allowed to import from "${absoluteImportPath}"`,
missingFile: ({ schemaPath, missingNode }) =>
`The file is not described in the schema. In "${schemaPath}" expected a node "${missingNode}"`,
missingRules: ({ schemaPath }) =>
`There is no set of rules in the "${schemaPath}"`,
}
Правила
Правила можно передать в любой узел схемы под ключем __rules__
. Правила содержат 3 необязательных ключа:
-
defaultAllowed
:boolean - Импорт со всех адресов разрешен, если true. Запрещен, если false (default value: true) -
allowed
:array of string - Массив разрешенных адресов для исключения изdefaultAllowed = true
-
disallowed
:array of string - Массив запрещенных адресов для исключения изdefaultAllowed = false
!!! Важно !!!
- На одном уровне вложенности может быть только одно ключевое слово
__var__<variable_name>
-
__var__
переменные не должны повторяться - Если в узле отсутствуют правила и аргумент inheritance выставлен на false (отключено наследование правил), то это приведет к ошибке
!!! Интересно !!!
Переменные, указанные при составлении дерева файлов можно использовать в написании правил.
apps: {
__var__similar_apps: {
featuers: {},
shared: {},
},
},
В нашей схеме есть переменная <similar_apps>, полученная при указании ключевого слова __var__
вместо названий приложений app-a, app-b. И мы хотим разрешить импорт из shared в
featuers, но только в рамках одного приложения (то есть в app-b/featuers нельзя сделать
импорт модуля из app-a/shared).
Настроить такие ограничения можно разместив следующие правила в объекте features
__rules__: {
defaultAllowed: false,
allowed: [
'apps/__var__similar_apps/shared',
],
},
Логика проверки путей работает таким образом, что она вместо __var__
переменных подставит узлы,
из пути проверяемого файла. То есть находясь в файле apps/app-a/featuers/index.ts
переменная __var__similar_apps, используемая в правилах будет заменена на app-a
, а
находясь в apps/app-b/featuers/index.ts
- на app-b
.
Проще говоря, переменная __var__<variable_name>
- это текущая директория для каждого файла
no-default-export
Правило no-default-export
запрещает использование дефолтных экспортов