Глобальные переменные в flat config

ESLint в flat config-архитектуре рассматривает глобальные переменные как часть контекста выполнения кода, задаваемого явно через languageOptions.globals. Это принципиально отличается от старого .eslintrc, где глобальные переменные часто задавались через env, а сами окружения скрыто добавляли наборы глобалов.

Глобальные переменные в JavaScript появляются из разных источников:

  • среда выполнения (браузер, Node.js, Worker)
  • встроенные Web API (window, document, fetch)
  • Node.js API (process, Buffer, __dirname)
  • тестовые фреймворки (describe, it, jest)
  • пользовательские глобалы, объявленные через bundler или runtime

Проблема возникает в статическом анализе: линтер должен понимать, какие идентификаторы допустимы без объявления. Без этого правило no-undef начинает ошибочно помечать валидный код как ошибочный.

Flat config и явное объявление globals

В flat config глобальные переменные задаются через languageOptions.globals:

export default [
  {
    files: ["**/*.{js,mjs,cjs}"],
    languageOptions: {
      globals: {
        window: "readonly",
        document: "readonly",
        fetch: "readonly"
      }
    }
  }
];

Каждое имя глобальной переменной задаётся как ключ объекта, а значение определяет уровень доступа:

  • "readonly" — переменная доступна только для чтения
  • "writable" — переменная может быть переопределена
  • undefined (не рекомендуется явно использовать) — отсутствие глобала

Эта модель делает конфигурацию более прозрачной: вместо абстрактных env: browser теперь явно перечисляется поверхность глобального API.

Взаимодействие globals и правила no-undef

Правило no-undef в ESLint опирается на список глобальных переменных, доступных в текущем конфиге. При использовании flat config логика становится строго детерминированной:

  • если идентификатор не объявлен в globals
  • и не импортирован через import/require
  • и не является параметром функции

то он считается ошибкой.

Пример:

export default [
  {
    languageOptions: {
      globals: {
        console: "readonly"
      }
    }
  }
];
console.log("ok"); // допустимо
myGlobalVar();     // ошибка no-undef

Переход от env к globals

В старой конфигурации:

env: {
  browser: true
}

это автоматически добавляло десятки глобальных переменных.

В flat config этот механизм заменяется явным перечислением или использованием пакета globals:

import globals from "globals";

export default [
  {
    languageOptions: {
      globals: {
        ...globals.browser
      }
    }
  }
];

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

Типы глобальных окружений

Браузерное окружение

Типичный набор:

  • window
  • document
  • navigator
  • location
  • fetch

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

import globals from "globals";

export default [
  {
    languageOptions: {
      globals: {
        ...globals.browser
      }
    }
  }
];

Node.js окружение

Включает:

  • process
  • Buffer
  • __dirname
  • module
  • require
import globals from "globals";

export default [
  {
    languageOptions: {
      globals: {
        ...globals.node
      }
    }
  }
];

Worker окружение

Используется в Web Workers и Service Workers:

  • self
  • postMessage
  • importScripts
languageOptions: {
  globals: {
    ...globals.worker
  }
}

Управление мутабельностью глобалов

Flat config позволяет тонко управлять тем, можно ли перезаписывать глобальные значения:

globals: {
  Math: "readonly",
  myDebug: "writable"
}

Такой подход важен для предотвращения случайного переопределения встроенных объектов:

Math = {}; // потенциальная ошибка, если readonly

Переопределение globals для отдельных файлов

Flat config поддерживает условные блоки через files:

export default [
  {
    files: ["src/**/*.js"],
    languageOptions: {
      globals: {
        ...globals.browser
      }
    }
  },
  {
    files: ["scripts/**/*.js"],
    languageOptions: {
      globals: {
        ...globals.node
      }
    }
  }
];

Это позволяет разделять контексты внутри одного проекта без конфликтов.

Глобальные переменные в тестовых средах

Тестовые фреймворки добавляют собственные глобалы:

  • Jest: describe, test, expect
  • Mocha: describe, it

Пример:

languageOptions: {
  globals: {
    describe: "readonly",
    it: "readonly",
    expect: "readonly"
  }
}

Или через globals пакет:

import globals from "globals";

globals.jest;

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

В некоторых проектах глобальные переменные вводятся явно через bundler (например, Webpack DefinePlugin) или runtime injection.

languageOptions: {
  globals: {
    __APP_VERSION__: "readonly",
    API_BASE_URL: "readonly"
  }
}

Такие переменные часто используются как compile-time константы, но линтеру необходимо явно сообщить о них, иначе они будут трактоваться как неопределённые.

Взаимодействие с TypeScript-проектами

В проектах с TypeScript глобальные переменные могут дублироваться декларациями *.d.ts, но ESLint не читает типы напрямую для определения глобалов.

Поэтому даже при наличии:

declare const API_URL: string;

в ESLint flat config всё равно требуется:

globals: {
  API_URL: "readonly"
}

Частые проблемы и конфликты

Дублирование глобалов

Если одновременно подключены:

  • globals.browser
  • ручные определения

может возникнуть конфликт прав доступа.

Избыточное разрешение writable

Разрешение writable для стандартных API приводит к скрытым багам:

document = null; // ломает окружение

Отсутствие глобалов в монорепозиториях

В монорепозиториях разные пакеты могут требовать разные глобальные контексты. Flat config требует явного разделения через files, иначе возникает загрязнение окружения.

Стратегии организации globals

Базовый слой

Общий набор безопасных глобалов:

globals: {
  console: "readonly",
  setTimeout: "readonly",
  clearTimeout: "readonly"
}

Окружения поверх базового слоя

{
  files: ["**/*.test.js"],
  languageOptions: {
    globals: {
      ...globals.jest
    }
  }
}

Изоляция runtime-контекстов

Разделение браузера и Node.js в одном проекте:

{
  files: ["server/**/*.js"],
  languageOptions: {
    globals: {
      ...globals.node
    }
  }
}
{
  files: ["client/**/*.js"],
  languageOptions: {
    globals: {
      ...globals.browser
    }
  }
}

Роль явной декларации в flat config

Flat config в ESLint делает акцент на прозрачности: глобальные переменные больше не «подмешиваются» окружением, а становятся частью конфигурационного графа анализа.

Это приводит к следующим свойствам модели:

  • предсказуемость анализа
  • отсутствие скрытых env-эффектов
  • явное управление контекстами выполнения
  • возможность точечной настройки для каждого набора файлов