Порядок разрешения конфигурационного файла

При запуске Vite выполняется автоматический поиск конфигурационного файла. Система проходит по заранее определённому набору имён и расширений, пытаясь определить подходящий файл конфигурации в корне проекта.

Поддерживаются следующие варианты:

vite.config.js
vite.config.mjs
vite.config.cjs
vite.config.ts
vite.config.mts
vite.config.cts

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

Пример структуры:

project/
├── vite.config.ts
├── package.json
├── src/
└── public/

При запуске команды:

vite

или:

vite build

Vite начинает поиск конфигурации из текущей рабочей директории (process.cwd()).


Приоритет разрешения конфигурационного файла

Если в проекте одновременно присутствует несколько конфигурационных файлов, Vite использует строгий порядок приоритета.

Например:

vite.config.ts
vite.config.js
vite.config.mjs

В этом случае будет выбран только один файл.

Основной принцип:

  1. Явно указанный файл через --config
  2. TypeScript-конфигурации
  3. ESM-конфигурации
  4. CommonJS-конфигурации

На практике приоритет выглядит следующим образом:

vite.config.ts
vite.config.mts
vite.config.cts
vite.config.mjs
vite.config.js
vite.config.cjs

Если найден первый подходящий файл, дальнейший поиск прекращается.


Явное указание конфигурации через CLI

Наивысший приоритет имеет параметр --config.

Пример:

vite --config configs/vite.dev.ts

или:

vite build --config ./configs/prod/vite.config.ts

В этом случае автоматический поиск полностью отключается.

Vite загружает только указанный файл.

Это особенно важно:

  • в монорепозиториях;
  • при разделении dev/prod-конфигураций;
  • в CI/CD;
  • при использовании нескольких окружений.

Пример:

configs/
├── vite.dev.ts
├── vite.prod.ts
└── vite.test.ts

Запуск:

vite --config configs/vite.dev.ts

Разрешение относительно текущей директории

Vite ищет конфигурацию относительно текущей директории запуска.

Пример:

cd apps/admin
vite

Поиск будет происходить внутри:

apps/admin/

Даже если выше существует другой vite.config.ts.

Структура:

monorepo/
├── vite.config.ts
├── apps/
│   ├── admin/
│   │   └── vite.config.ts
│   └── client/
│       └── vite.config.ts

Если запуск производится из apps/admin, будет использован:

apps/admin/vite.config.ts

Корневой файл проигнорируется.


Поведение параметра root

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

Пример:

import { defineConfig } from 'vite'

export default defineConfig({
  root: './src'
})

Vite всё равно сначала найдёт:

vite.config.ts

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

Это важная особенность архитектуры.


Загрузка TypeScript-конфигурации

Файлы:

vite.config.ts
vite.config.mts
vite.config.cts

не исполняются Node.js напрямую.

Vite выполняет промежуточную трансформацию через esbuild.

Процесс выглядит так:

vite.config.ts
       ↓
esbuild transpile
       ↓
temporary JS
       ↓
dynamic import

Это позволяет:

  • использовать TypeScript без отдельной компиляции;
  • применять типизацию;
  • использовать современные возможности ECMAScript.

Различия между .js, .mjs и .cjs

Поведение зависит от модуля Node.js.

vite.config.js

Может интерпретироваться как:

  • CommonJS
  • ES Module

в зависимости от:

{
  "type": "module"
}

в package.json.


vite.config.mjs

Всегда трактуется как ES Module.

Пример:

import { defineConfig } from 'vite'

export default defineConfig({
  server: {
    port: 3000
  }
})

vite.config.cjs

Всегда трактуется как CommonJS.

Пример:

const { defineConfig } = require('vite')

module.exports = defineConfig({
  server: {
    port: 3000
  }
})

Влияние package.json

Поле:

{
  "type": "module"
}

изменяет способ обработки .js.

Без type: module

vite.config.js → CommonJS

С type: module

vite.config.js → ESM

Из-за этого одинаковый файл может работать по-разному в разных проектах.


Как Vite загружает конфигурацию

После определения файла выполняется один из механизмов:

Для ESM

Используется:

import()

Для CommonJS

Используется:

require()

Для TypeScript

Используется:

esbuild → import()

Порядок вычисления конфигурации

После загрузки файла Vite выполняет несколько этапов:

1. Поиск файла
2. Импорт модуля
3. Получение default export
4. Выполнение функции конфигурации
5. Слияние с internal defaults
6. Применение CLI-параметров
7. Инициализация плагинов

Функциональная конфигурация

Конфигурация может экспортировать функцию.

Пример:

import { defineConfig } from 'vite'

export default defineConfig(({ command, mode }) => {
  if (command === 'build') {
    return {
      build: {
        sourcemap: false
      }
    }
  }

  return {
    server: {
      port: 3000
    }
  }
})

Порядок работы:

1. Файл найден
2. Модуль импортирован
3. Вызвана функция
4. Получен объект конфигурации

Конфигурация и режимы (mode)

При использовании:

vite --mode staging

Vite:

  1. сначала загружает конфигурацию;
  2. затем определяет mode;
  3. затем подключает .env файлы.

Порядок:

vite.config.ts
.env
.env.local
.env.staging
.env.staging.local

Влияние CLI-параметров

CLI-параметры имеют более высокий приоритет, чем конфигурация.

Пример:

vite --port 5000

Даже если в конфигурации указано:

server: {
  port: 3000
}

будет использован:

5000

Приоритет:

CLI arguments
↓
Vite config
↓
Internal defaults

Разрешение конфигурации в монорепозиториях

В monorepo возможны разные схемы.

Локальная конфигурация для каждого пакета

packages/
├── admin/
│   └── vite.config.ts
├── client/
│   └── vite.config.ts

Каждый пакет запускается независимо.


Общая конфигурация

vite.base.ts
packages/
├── admin/
├── client/

Использование:

import baseConfig from '../. ./vite.base'

export default baseConfig

Явное указание файла

vite --config ../. ./vite.shared.ts

Кэширование конфигурации

Во время dev-сервера Vite отслеживает изменения конфигурации.

Изменение:

vite.config.ts

вызывает:

server restart

Причина:

  • конфигурация влияет на плагины;
  • aliases;
  • resolve;
  • optimizeDeps;
  • build pipeline.

Hot reload для конфигурации невозможен.


Ошибки разрешения конфигурации

Конфигурация не найдена

Ошибка:

failed to load config

Причины:

  • запуск не из той директории;
  • неверный путь;
  • отсутствует файл;
  • ошибка расширения.

Ошибка ESM/CJS

Пример:

Cannot use import statement outside a module

Причины:

  • конфликт type: module;
  • неправильное расширение;
  • смешивание require и import.

Ошибка TypeScript

Пример:

Unexpected token ':'

Обычно означает:

  • файл загружается как JS;
  • Vite не смог обработать TypeScript;
  • используется несовместимая среда Node.js.

Внутренний алгоритм разрешения

Упрощённо процесс выглядит так:

if (--config provided)
    use explicit file
else
    search default filenames in cwd

if (ts)
    transpile with esbuild

if (esm)
    use dynamic import

if (cjs)
    use require

resolve export
execute config function if needed
merge defaults
apply cli overrides

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

Полный порядок влияния:

1. CLI arguments
2. Explicit --config
3. Loaded vite.config.*
4. Environment variables
5. Vite internal defaults

Рекомендации по организации конфигурации

Для небольших проектов

vite.config.ts

Один файл без разделения.


Для средних проектов

config/
├── vite.base.ts
├── vite.dev.ts
└── vite.prod.ts

Для монорепозиториев

packages/*/vite.config.ts
shared/vite.base.ts

Особенности SSR и library mode

При использовании:

build: {
  ssr: true
}

или:

build: {
  lib: {}
}

порядок поиска конфигурации не меняется.

Изменяется только последующая обработка build pipeline.


Взаимодействие с плагинами

Плагины инициализируются только после полного разрешения конфигурации.

Последовательность:

1. load config
2. resolve plugins
3. create plugin container
4. start dev server/build

Поэтому плагины не могут участвовать в поиске самого конфигурационного файла.


Влияние Node.js loader system

Vite опирается на стандартный механизм Node.js:

  • ESM Loader
  • CommonJS Loader
  • dynamic import
  • package scope resolution

Из-за этого поведение может различаться между версиями Node.js.

Особенно заметно при:

  • "type": "module"
  • смешанном ESM/CJS;
  • нестандартных расширениях;
  • experimental loaders.

Почему рекомендуется vite.config.ts

TypeScript-конфигурация стала стандартом по нескольким причинам:

  • полноценная типизация;
  • автодополнение IDE;
  • безопасный рефакторинг;
  • корректная работа defineConfig;
  • лучшая совместимость с экосистемой Vite.

Пример:

import { defineConfig } from 'vite'

export default defineConfig({
  resolve: {
    alias: {
      '@': '/src'
    }
  }
})

defineConfig обеспечивает:

  • типовую проверку;
  • корректный inference;
  • поддержку IntelliSense;
  • защиту от ошибочных полей конфигурации.