Очистка директории вывода: build.emptyOutDir

Параметр build.emptyOutDir управляет очисткой директории сборки перед созданием новых файлов. Во время выполнения команды production-сборки Vite удаляет содержимое выходной папки, чтобы в ней не оставались устаревшие ресурсы от предыдущих билдов.

По умолчанию Vite использует директорию dist, однако она может быть изменена через параметр build.outDir. Перед записью новых файлов Vite анализирует содержимое выходной директории и, если очистка разрешена, удаляет старые файлы.

Базовый пример:

import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    emptyOutDir: true
  }
})

Поведение по умолчанию

В большинстве проектов параметр автоматически равен true, если директория сборки находится внутри корня проекта.

Например:

build: {
  outDir: 'dist'
}

При выполнении:

vite build

Vite:

  1. удалит содержимое dist
  2. создаст новую структуру файлов
  3. запишет актуальные JS, CSS и ассеты

Это предотвращает накопление:

  • старых хешированных файлов
  • неиспользуемых чанков
  • устаревших CSS-ресурсов
  • файлов после переименования модулей

Почему очистка директории важна

Во время production-сборки Vite генерирует файлы с хешами:

assets/index-a1b2c3.js
assets/index-f7d8e9.js

После нескольких сборок без очистки каталог начинает содержать множество устаревших файлов:

dist/
├── assets/
│   ├── index-a1b2c3.js
│   ├── index-f7d8e9.js
│   ├── vendor-111.js
│   ├── vendor-222.js

Это приводит к нескольким проблемам.

Лишние файлы на сервере

Старые ресурсы продолжают занимать место.

Загрязнение CDN-кэша

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

Конфликты деплоя

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

Ошибки при динамическом импорте

Если приложение загружает чанки динамически:

const module = await import('./admin.js')

старые версии чанков могут приводить к ошибкам:

Failed to fetch dynamically imported module

Значение true

При установке:

build: {
  emptyOutDir: true
}

Vite полностью очищает директорию сборки перед генерацией новых файлов.

Пример:

import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    outDir: 'dist',
    emptyOutDir: true
  }
})

Поведение:

dist/           ← очищается
dist/assets/    ← удаляется
dist/index.html ← удаляется

После этого создаётся новая структура.


Значение false

Если установить:

build: {
  emptyOutDir: false
}

Vite перестанет удалять старые файлы.

Пример:

import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    emptyOutDir: false
  }
})

После повторной сборки в директории могут одновременно существовать:

dist/assets/main-old.js
dist/assets/main-new.js

Когда отключение очистки оправдано

Многоэтапная сборка

Иногда несколько процессов пишут файлы в одну директорию.

Например:

dist/
├── frontend/
├── backend/
├── docs/

Если frontend-сборка очищает всю директорию, остальные данные будут удалены.

В подобных случаях используют:

build: {
  emptyOutDir: false
}

Генерация файлов сторонними инструментами

Некоторые инструменты создают дополнительные файлы:

  • sitemap.xml
  • robots.txt
  • API-документацию
  • SSR-манифесты
  • серверные шаблоны

Если они находятся внутри outDir, автоматическая очистка может удалить их.


Монорепозитории

В monorepo несколько приложений иногда используют общую директорию:

build/
├── admin/
├── client/
├── landing/

Полная очистка способна повредить сборки соседних пакетов.


Инкрементальный деплой

Иногда deployment-процесс специально сохраняет старые версии файлов.

Например:

dist/releases/v1
dist/releases/v2

В такой архитектуре очистка может быть нежелательной.


Предупреждение при выходе за пределы корня проекта

Если outDir указывает на директорию вне корня проекта, Vite начинает вести себя осторожнее.

Пример:

build: {
  outDir: '../public/build'
}

В этом случае Vite может вывести предупреждение:

(!) outDir is outside of project root

Автоматическая очистка может быть отключена из соображений безопасности.

Чтобы явно разрешить удаление:

build: {
  outDir: '../public/build',
  emptyOutDir: true
}

Причины защитного механизма

Удаление директорий за пределами проекта потенциально опасно.

Ошибочная конфигурация:

outDir: '../'

теоретически может привести к удалению критически важных файлов.

Поэтому Vite требует явного подтверждения через emptyOutDir.


Связь с build.outDir

Параметр напрямую зависит от build.outDir.

Пример:

build: {
  outDir: 'public/assets',
  emptyOutDir: true
}

Очистке подвергнется именно:

public/assets

а не весь каталог public.


Очистка при кастомной структуре проекта

Пример архитектуры:

project/
├── frontend/
├── backend/
├── public/
└── build/

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

build: {
  outDir: '../build/frontend',
  emptyOutDir: true
}

Во время сборки очищается:

build/frontend

но остальные каталоги сохраняются.


Влияние на CI/CD

В CI/CD очистка директории особенно важна.

Типичный pipeline:

npm install
npm run build
rsync dist/ server:/var/www/app

Если старые файлы остаются в dist, они могут попасть на сервер вместе с новыми ресурсами.

Это особенно критично для:

  • chunk splitting
  • dynamic import
  • manifest.json
  • SSR hydration
  • module federation

Взаимодействие с manifest

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

build: {
  manifest: true
}

Vite создаёт:

dist/manifest.json

Если очистка отключена, старые манифесты могут конфликтовать с новыми файлами.

Например:

{
  "main.js": {
    "file": "assets/main-old.js"
  }
}

При удалении старого чанка приложение перестанет корректно разрешать пути.


Взаимодействие с assetsDir

Пример:

build: {
  assetsDir: 'static'
}

Структура:

dist/
└── static/

При включённой очистке директория static также полностью удаляется перед сборкой.


Использование вместе с SSR

В SSR-проектах часто существуют две сборки:

dist/client
dist/server

Пример конфигурации:

build: {
  outDir: 'dist/client'
}

и отдельно:

build: {
  outDir: 'dist/server'
}

Если обе сборки используют общий корневой каталог dist, важно правильно организовать очистку.

Иногда применяют:

emptyOutDir: false

для серверной сборки, чтобы клиентские ресурсы не удалялись.


Отличие от ручного удаления

Вместо:

rm -rf dist
vite build

можно использовать встроенный механизм:

build: {
  emptyOutDir: true
}

Преимущества встроенной очистки:

  • кроссплатформенность
  • отсутствие зависимости от shell-команд
  • безопасная интеграция в Vite pipeline
  • корректная работа в Windows
  • единая конфигурация проекта

Практический пример production-конфигурации

import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    outDir: 'dist',
    assetsDir: 'assets',
    manifest: true,
    sourcemap: true,
    emptyOutDir: true
  }
})

Во время сборки:

  1. удаляется dist
  2. создаётся новая структура
  3. генерируются ассеты
  4. создаётся manifest
  5. записываются sourcemap-файлы

Практический пример для monorepo

import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    outDir: '../. ./shared-build/admin',
    emptyOutDir: false
  }
})

Такая конфигурация предотвращает удаление сборок других приложений.


Практический пример Laravel + Vite

В интеграции с Laravel часто используется:

build: {
  outDir: 'public/build'
}

Полная конфигурация:

import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    outDir: 'public/build',
    emptyOutDir: true,
    manifest: true
  }
})

Перед каждой сборкой Laravel-ассеты полностью обновляются.


Типичные ошибки

Отключение очистки без необходимости

Часто приводит к накоплению мусора:

dist/assets/app-111.js
dist/assets/app-222.js
dist/assets/app-333.js

Использование общей директории для нескольких приложений

Например:

dist/
├── app1
├── app2

При неправильной настройке одна сборка может удалить результаты другой.


Очистка внешней директории без понимания структуры

Опасный пример:

build: {
  outDir: '../public',
  emptyOutDir: true
}

Если public содержит серверные файлы, они будут удалены.


Рекомендации по использованию

Для обычных SPA

Рекомендуется:

emptyOutDir: true

Для SSR

Следует внимательно разделять директории:

dist/client
dist/server

Для monorepo

Желательно избегать общих выходных директорий.


Для CI/CD

Очистка почти всегда должна быть включена.


Внутренний механизм работы

Во время build-процесса Vite:

  1. определяет outDir
  2. проверяет допустимость удаления
  3. анализирует положение директории относительно project root
  4. удаляет содержимое
  5. запускает Rollup build pipeline
  6. записывает новые файлы

Очистка выполняется до генерации ассетов, поэтому старые файлы не участвуют в процессе сборки и не влияют на manifest, preload-ссылки и dependency graph.