Схема опций правила

Роль схемы опций в архитектуре правила

Каждое правило ESLint опирается на строго определённую структуру входных данных, передаваемых через конфигурацию. Эти данные называются опциями правила и формируют контракт между пользователем и реализацией линтера. Схема опций определяет допустимые типы, структуру и ограничения значений, которые могут быть переданы в context.options.

Основная задача схемы — обеспечить детерминированную валидацию конфигурации до выполнения логики правила. Это исключает необходимость ручных проверок типов внутри create() и снижает вероятность некорректного поведения при различных конфигурациях проекта.


Расположение схемы в определении правила

В классическом определении правила ESLint схема указывается в объекте meta:

export default {
  meta: {
    type: "suggestion",
    schema: []
  },
  create(context) {
    return {};
  }
};

Поле schema является массивом JSON Schema-описаний. Каждый элемент массива соответствует одному позиционному аргументу в options.


Позиционная модель опций

ESLint использует позиционную модель передачи опций:

{
  "rules": {
    "example-rule": ["error", "always", { "ignoreComments": true }]
  }
}

Разбор:

  • options[0] = "always"
  • options[1] = { ignoreComments: true }

Схема должна соответствовать этой структуре:

schema: [
  { type: "string" },
  {
    type: "object",
    properties: {
      ignoreComments: { type: "boolean" }
    },
    additionalProperties: false
  }
]

Базовые типы в JSON Schema

ESLint использует подмножество JSON Schema Draft 4/7, ориентированное на проверку конфигураций.

Основные типы:

  • string
  • number
  • boolean
  • object
  • array
  • null

Пример простого ограничения:

schema: [
  {
    type: "string",
    enum: ["always", "never"]
  }
]

Использование enum ограничивает допустимые значения, обеспечивая строгий контроль конфигурации.


Объектные схемы и структура вложенных опций

Наиболее распространённый формат — объектная схема:

schema: [
  {
    type: "object",
    properties: {
      allowEmpty: { type: "boolean" },
      maxLength: { type: "number" }
    },
    additionalProperties: false
  }
]

Ключевые элементы:

  • properties — описание допустимых ключей
  • additionalProperties — контроль наличия лишних ключей
  • required — обязательные поля

Пример с обязательными параметрами:

schema: [
  {
    type: "object",
    properties: {
      min: { type: "number" },
      max: { type: "number" }
    },
    required: ["min"],
    additionalProperties: false
  }
]

Массивы как структура опций

Массивы используются для описания наборов значений:

schema: [
  {
    type: "array",
    items: { type: "string" },
    minItems: 1,
    uniqueItems: true
  }
]

Здесь:

  • items определяет тип элементов
  • minItems ограничивает минимальную длину
  • uniqueItems запрещает дублирование

Вложенные схемы и сложные структуры

Сложные правила часто требуют комбинированных структур:

schema: [
  {
    type: "object",
    properties: {
      rules: {
        type: "array",
        items: {
          type: "object",
          properties: {
            selector: { type: "string" },
            message: { type: "string" }
          },
          required: ["selector"],
          additionalProperties: false
        }
      }
    },
    additionalProperties: false
  }
]

Такая структура используется для правил, работающих с наборами шаблонов, селекторов или конфигурационных блоков.


Альтернативные схемы: oneOf, anyOf, allOf

Для описания гибких конфигураций применяются композиционные операторы:

oneOf
schema: [
  {
    oneOf: [
      { type: "string" },
      { type: "object", properties: { mode: { type: "string" } } }
    ]
  }
]

Опция должна соответствовать ровно одной из схем.

anyOf
schema: [
  {
    anyOf: [
      { type: "number" },
      { type: "null" }
    ]
  }
]

Допускается соответствие любой из схем.

allOf
schema: [
  {
    allOf: [
      { type: "object" },
      { required: ["enabled"] }
    ]
  }
]

Объединяет ограничения нескольких схем.


Значение default и его роль в схемах

JSON Schema не всегда гарантирует подстановку значений по умолчанию, однако в ESLint часто используется ручная нормализация:

create(context) {
  const options = context.options[0] || {};
  const mode = options.mode || "strict";
}

Схема при этом остаётся декларативной, а значения по умолчанию реализуются в логике правила.


Валидация схемы и механизм ESLint

При запуске ESLint выполняется этап валидации конфигурации:

  1. Загружается правило
  2. Извлекается meta.schema
  3. Применяется JSON Schema validator
  4. Несоответствия приводят к ошибке конфигурации

Ошибка не допускает выполнения правила с некорректными опциями, что предотвращает скрытые дефекты в анализе кода.


Типизация через schema и связь с TypeScript

Хотя ESLint не использует TypeScript для схем, структура может быть синхронизирована с типами:

interface Options {
  mode: "strict" | "loose";
  max?: number;
}

И соответствующая схема:

schema: [
  {
    type: "object",
    properties: {
      mode: { type: "string", enum: ["strict", "loose"] },
      max: { type: "number" }
    },
    additionalProperties: false
  }
]

Расхождение между типами и схемой часто приводит к ошибкам конфигурации, поэтому их поддержание в согласованном состоянии критично для крупных наборов правил.


Множественные аргументы опций

Правила могут принимать несколько независимых параметров:

schema: [
  { type: "string" },
  { type: "boolean" },
  {
    type: "object",
    properties: {
      allow: { type: "array", items: { type: "string" } }
    }
  }
]

Конфигурация:

["error", "always", true, { "allow": ["log", "warn"] }]

Каждый элемент массива конфигурации сопоставляется с соответствующим индексом схемы.


Ограничения и особенности реализации

Схема опций в ESLint имеет ряд практических ограничений:

  • не поддерживаются произвольные вычисляемые типы
  • отсутствует runtime трансформация значений
  • не выполняется автоматическое приведение типов
  • сложные зависимости между полями ограничены

Это делает схемы предсказуемыми, но требует дополнительной логики внутри create() для зависимых параметров.


Практика проектирования схем

При разработке схемы опций учитываются следующие принципы:

  • минимизация количества опций
  • явное ограничение допустимых значений
  • исключение неоднозначных комбинаций
  • предпочтение простых структур над вложенными
  • использование additionalProperties: false для строгих конфигураций

Сложные схемы увеличивают когнитивную нагрузку и усложняют поддержку правил, поэтому их обычно декомпозируют на более простые логические блоки внутри одного объекта конфигурации.