Skip to content

Правила миграции

Этот справочный раздел подробно описывает, что именно делает команда vp migrate с проектом: как она обновляет зависимости, переписывает импорты в исходном коде и сценарии пакетов, а также изменяет конфигурацию менеджера пакетов. Общее описание команды и процесса миграции см. в руководстве по миграции.

За исключением раздела Перед миграцией, в котором перечислены действия, выполняемые вручную, всё, что описано ниже, происходит автоматически.

Перед миграцией

  1. Выполните vp upgrade, чтобы глобальная CLI получила последние правила миграции. Устаревшая локальная версия vite-plus не является препятствием: если локальная копия проекта старее, миграция будет выполняться глобальной CLI.
  2. При необходимости обновите проект до Vite 8+ и Vitest 4.1+.
  3. Выполните vp migrate из корня рабочего пространства. В автоматизированных средах используйте флаг --no-interactive.
  4. Проверьте все изменённые манифесты, конфигурацию менеджера пакетов, изменения исходного кода и сгенерированный lock-файл.
  5. Выполните проверку с помощью vp install, vp check, vp test и vp build.

Миграция является идемпотентной: повторный запуск после успешной миграции не должен приводить к появлению новых изменений.

Обновление или полная настройка

Если проект уже использует vite-plus, команда vp migrate выполняет только обновление: обновляет зависимости и конфигурацию менеджера пакетов, а также завершает обновление импортов. При этом первоначальная настройка проекта не затрагивается.

  • Флаг --full дополнительно выполняет действия по настройке: устанавливает git-хуки, настраивает редактор, создаёт файлы для ИИ-агентов, переносит конфигурации ESLint и Prettier, добавляет совместимость с фреймворками, исправляет параметр baseUrl в tsconfig и преобразует .nvmrc/Volta в .node-version.
  • Флаги --hooks, --agent и --editor позволяют выполнить только одно из этих действий без использования --full.

Если при обычном обновлении пропускаются действия по настройке, которые можно было бы применить, команда выведет подсказку выполнить vp migrate --full. Для новых проектов (не использующих Vite+) всегда выполняется полная миграция.

Правила обработки зависимостей

Что происходит с каждой зависимостью из набора инструментов:

ЗависимостьЧто происходит
vite-plusДобавляется в пакет, для которого выполняется миграция; обычные диапазоны версий заменяются на конкретную целевую версию — напрямую или через каталог.
viteСуществующие объявления сохраняются и перенаправляются на алиас ядра. При использовании pnpm добавляется как прямая зависимость devDependencies там, где это необходимо (см. Vite и overrides).
vitestВ типичном случае для Node.js удаляется, поскольку vite-plus предоставляет его как транзитивную зависимость. Сохраняется или добавляется только в случаях, когда требуется прямая зависимость.
@vitest/*Напрямую устанавливаемые пакеты экосистемы синхронизируются с версией Vitest, поставляемой вместе с Vite+ (см. Пакеты экосистемы Vitest).
@voidzero-dev/vite-plus-testПолностью удаляется: из зависимостей, overrides, resolutions и алиасов каталогов. Импорты переписываются на актуальный API vite-plus/test*.

Выбор версии

  • vite-plus закрепляется за конкретной версией CLI, выполняющей миграцию, а не за dist-tag latest.
  • Алиас vite указывает на @voidzero-dev/vite-plus-core из того же релиза Vite+.
  • Если в манифесте используются каталоги (catalog: или именованные ссылки на каталог), миграция сохраняет ссылку и обновляет значение соответствующей записи каталога до конкретной целевой версии набора инструментов.
  • Намеренно закреплённые протоколы сохраняются: workspace:, file:, link:, npm:, github:, Git URL и HTTP URL.
  • Миграция синхронизирует все пакеты рабочего пространства, а не только корневой манифест. Общие overrides и каталоги остаются в корне рабочего пространства, а зависимости, предоставляющие peer-зависимости, размещаются в каждом пакете, которому они необходимы.

Vite и overrides

Правила overrides менеджера пакетов сами по себе не создают граф зависимостей. В pnpm пакет, который содержит vite-plus в dependencies или devDependencies, но нигде не объявляет vitedependencies, devDependencies, optionalDependencies или peerDependencies), позволяет pnpm автоматически установить исходный Vite для удовлетворения обязательной peer-зависимости vite, требуемой Vitest. В результате проект оказывается разделён между отдельными экземплярами Vite+, Vite и Vitest. Чтобы этого избежать, vp migrate добавляет отсутствующую запись vite в devDependencies каждого такого пакета, после чего правило override на уровне рабочего пространства перенаправляет её на ядро Vite+.

Связанные правила:

  • Прямое объявление vite никогда не удаляется только потому, что в корне рабочего пространства существует override.
  • Обычные или устаревшие алиасы нормализуются; именованные ссылки на каталог сохраняются.
  • Описанное выше правило добавления прямой зависимости относится только к pnpm. Bun аналогичным образом дублирует алиас ядра как прямую зависимость для своего механизма разрешения peer-зависимостей, а при использовании npm некоторые схемы browser-provider могут требовать зависимость vite верхнего уровня, чтобы вложенные пакеты Vitest могли корректно разрешить vite.

Когда требуется прямая зависимость от Vitest

Во время миграции локальная зависимость vitest сохраняется или добавляется с точной версией, поставляемой вместе с Vite+, если выполняется хотя бы одно из следующих условий:

  • установленная зависимость имеет обязательную peer-зависимость от vitest (как с точной версией, так и с диапазоном версий);
  • пакет использует браузерный режим Vitest или один из дополнительных браузерных провайдеров;
  • исходный код или конфигурация TypeScript сохраняют ссылки на исходный vitest;
  • пакет объявляет зависимость @nuxt/test-utils; или
  • метаданные зависимостей недоступны, и существующая прямая зависимость vitest может удовлетворять неизвестную обязательную peer-зависимость.

Для определения используется информация о peer-зависимостях установленных пакетов, поэтому интеграции вроде vite-plugin-gherkin также обрабатываются корректно, даже если их имя не содержит vitest.

Если пакет подпадает под эти условия, миграция:

  • добавляет vitest только в этот пакет, а не во все пакеты рабочего пространства;
  • использует существующую ссылку на каталог, если она поддерживается, либо точную поставляемую версию в противном случае;
  • сохраняет соответствующее правило override или resolution на уровне рабочего пространства, чтобы всё дерево зависимостей использовало единственную версию Vitest.

Само по себе объявление peer-зависимости не устанавливает Vitest. Если сохранившаяся запись peerDependencies.vitest использует запись каталога, которая будет удалена в ходе миграции, она сначала преобразуется в публичный диапазон версий.

Пакеты экосистемы Vitest

Официальные пакеты @vitest/* обычно выпускаются синхронно с Vitest. Миграция синхронизирует версии тех из них, которые проект устанавливает напрямую, включая @vitest/coverage-v8, @vitest/coverage-istanbul, @vitest/ui и @vitest/web-worker:

  • если менеджер пакетов поддерживает каталоги, зависимости будут ссылаться на каталог набора инструментов: существующие ссылки catalog: или catalog:<имя> сохраняются, для пакетов без такой ссылки создаётся запись каталога, а каждая запись обновляется до версии Vitest, поставляемой вместе с Vite+;
  • если каталоги не поддерживаются (npm, отдельный проект Bun или версии pnpm/Yarn без поддержки каталогов), записывается конкретная поставляемая версия.

Пакеты, которые не синхронизируются:

  • @vitest/eslint-plugin использует собственную схему версионирования;
  • @vitest/coverage-c8 остановился на более раннем релизе и не имеет версии для Vitest 4;
  • сторонние интеграции vitest-* сохраняют собственные совместимые версии, хотя их обязательная peer-зависимость от Vitest всё равно может привести к добавлению прямой зависимости.

Для браузерного режима базовые пакеты @vitest/browser и @vitest/browser-preview уже входят в состав Vite+ и удаляются из прямых зависимостей. Провайдеры Playwright и WebdriverIO остаются дополнительными: сохранённый или автоматически добавленный провайдер подключается через предпочтительный каталог набора инструментов с поставляемой версией Vitest (или записывается как конкретная версия, если каталоги не поддерживаются), а вместе с ним устанавливается соответствующая peer-зависимость playwright или webdriverio.

Провайдеры определяются до переписывания импортов. Это обеспечивает корректную обработку устаревших проектов, которые использовали алиас vitest на @voidzero-dev/vite-plus-test и импортировали модули из vitest/browser-<provider>, vitest/browser/providers/<provider> или vitest/plugins/browser-<provider>: такие импорты по-прежнему приводят к установке соответствующей зависимости @vitest/browser-playwright или @vitest/browser-webdriverio вместе с её peer-зависимостью.

Вложенные правила overrides npm и Bun, заданные в виде объектов, сохраняются, поскольку они представляют собой пользовательские области действия, а не обычные закрепления версий.

Правила переписывания исходного кода

Импорты vite

Импорты vite и vite/* переписываются на vite-plus только в файлах конфигурации: vite.config.*, vitest.config.* и любых других конфигурационных файлах, обнаруженных в процессе миграции. Во всех остальных файлах импорты vite остаются без изменений по двум причинам:

  • vite-plus не является гарантированным надмножеством публичного API Vite. Он предоставляет только defineConfig, defineProject и lazyPlugins, поэтому переписывание таких символов, как createBuilder или loadConfigFromFile (в том числе в типах вида typeof import('vite')), может нарушить работу кода.
  • Импорт vite, оставленный без изменений, в проекте Vite+ всё равно будет разрешён через алиас @voidzero-dev/vite-plus-core.

Для пакетов плагинов (непространственные имена, начинающиеся с vite-plugin- или unplugin-, а также пакеты, содержащие vite в peerDependencies или dependencies) переписывание не выполняется даже в конфигурационных файлах. Это правило распространяется только на спецификатор vite.

Дополнения модулей через declare module 'vite' подчиняются тем же правилам и сохраняются за пределами файлов конфигурации. Благодаря алиасу ядра они продолжают расширять тот же модуль @voidzero-dev/vite-plus-core, тип UserConfig которого используется функцией defineConfig из vite-plus, поэтому после миграции всё продолжает работать. Сам vite-plus не экспортирует символ UserConfig, поэтому переписанное дополнение declare module 'vite-plus' не с чем было бы объединять. Расширения, предназначенные для собственного API vite-plus, необходимо писать вручную для vite-plus.

Импорты vitest и браузерных модулей

  • Обычные импорты vitest и vitest/* переписываются на vite-plus/test*.
  • Импорты устаревших провайдеров Playwright и WebdriverIO обнаруживаются до переписывания, чтобы не потерять соответствующие необязательные зависимости.
  • Пространственные импорты @vitest/browser* переписываются на соответствующие экспорты vite-plus/test/browser*, при необходимости автоматически добавляя дополнительные провайдеры.
  • Уже существующие импорты vite-plus/test* остаются без изменений.

Что никогда не переписывается

  • declare module 'vitest' и declare module '@vitest/browser*': дополнения модулей должны сохранять идентичность исходного модуля.
  • Ссылки, которые остаются в проекте, такие как compilerOptions.types, require.resolve, import.meta.resolve и vitest/package.json, требуют наличия локальной зависимости vitest (см. раздел Когда требуется прямая зависимость от Vitest).
  • Если пакет объявляет зависимость @nuxt/test-utils, все спецификаторы модулей vitest и vitest/* сохраняются без изменений во всём пакете: преобразование Nuxt требует использования исходного имени модуля, иначе может быть добавлен дублирующий импорт vi. Это исключение не распространяется на соседние пакеты и не касается пространственных импортов @vitest/browser*.

Правило линтера prefer-vite-plus-imports использует то же исключение для Nuxt, поэтому автоматическое исправление также сохраняет эти импорты.

Правила переписывания сценариев пакетов

Во время миграции команды набора инструментов Vite+ в сценариях package.json переписываются с сохранением всех их аргументов:

ДоПосле
vitevp dev или соответствующая подкоманда vp
vitestvp test
oxlintvp lint
oxfmtvp fmt
tsdownvp pack
lint-stagedvp staged
eslintvp lint, если выполняется соответствующая дополнительная миграция
prettiervp fmt, если выполняется соответствующая дополнительная миграция

Для команд, запускаемых через bunx, миграция сохраняет сам bunx и его флаг --bun (оставляя выбранную пользователем среду выполнения), переписывая только управляемую команду. Это также работает, если bunx находится после разделителя запуска команд, например run или --:

ДоПосле
bunx --bun vite buildbunx --bun vp build
bunx --bun vitest runbunx --bun vp test run
portless --tailscale run bunx --bun viteportless --tailscale run bunx --bun vp dev
dotenv -e .env.test -- bunx --bun oxlint --type-awaredotenv -e .env.test -- bunx --bun vp lint --type-aware

Другие команды, запускаемые через bunx, а также иные способы запуска пакетов остаются без изменений.

Правила обработки версии Node.js

Во время миграции устаревшие файлы менеджеров версий Node.js преобразуются в .node-version — формат, который использует Vite+. В уже существующем проекте Vite+ это преобразование входит в состав полной настройки, поэтому выполняется при запуске vp migrate --full. Для новых проектов оно выполняется всегда.

  • Файлы .nvmrc и закреплённая версия volta.node преобразуются в .node-version. Если файл .node-version уже существует, он сохраняется.
  • При удалении .nvmrc все ссылки node-version-file: .nvmrc в actions/setup-node внутри .github/workflows/*.{yml,yaml} и составных действий (.github/actions/**/action.{yml,yaml}) автоматически заменяются на .node-version, чтобы предотвратить ошибки CI вида «node version file ... does not exist».

Правила для менеджеров пакетов

pnpm

Расположение корневых настроек. Начиная с pnpm 10.6.2, файл pnpm-workspace.yaml является единственным источником поддерживаемых корневых настроек. Во время миграции распознаваемые поля из package.json#pnpm переносятся туда, включая overrides, правила для peer-зависимостей, настройки патчей, расширения пакетов, параметры архитектуры и политики сборки, конфигурацию аудита и обновлений, а также зависимости конфигурации. Если объект pnpm становится пустым, он удаляется. Неизвестные ключи сохраняются, поскольку они могут использоваться другими инструментами.

  • Если одинаковая переносимая настройка определена в обоих файлах, объекты объединяются рекурсивно, а уникальные элементы массивов сохраняются. При конфликте скалярных значений приоритет отдаётся значениям из package.json#pnpm, а остальные записи, существующие только в pnpm-workspace.yaml, сохраняются.
  • До версии pnpm 10.6.2 эти настройки остаются в package.json#pnpm. (Поддержка настроек рабочего пространства появлялась постепенно: начиная с 10.5.0 — в общем случае, с 10.5.1 — для overrides, с 10.6.2 — для peerDependencyRules. Начиная с pnpm 11 устаревшие настройки в package.json больше не читаются.)

Каталоги. Каталоги — это отдельная возможность, поддерживаемая начиная с pnpm 9.5.0 и не зависящая от описанного выше расположения настроек. Даже в версиях ниже 10.6.2, где overrides остаются в package.json#pnpm, миграция всё равно обновляет каталог рабочего пространства, заменяя устаревшие алиасы-обёртки, и сохраняет catalog: в overrides как ссылки, а не подставляет вместо них конкретные версии.

  • Ссылки на зависимости, стандартный и именованные каталоги, overrides и peerDependencyRules поддерживаются в согласованном состоянии.
  • pnpm допускает определение стандартного каталога либо через корневое поле catalog, либо через catalogs.default, но не одновременно. Миграция сохраняет уже используемый вариант и никогда не создаёт второй параллельно.
  • Если существующий именованный каталог уже содержит записи для vite-plus, vite или vitest, миграция использует этот каталог набора инструментов и для новых зависимостей и overrides. Новый стандартный каталог верхнего уровня создаётся только в том случае, если невозможно использовать существующий стандартный или именованный каталог.

Другие правила.

  • Каждый пакет, объявляющий зависимость vite-plus, также получает прямую зависимость vite в devDependencies (см. Vite и overrides).
  • Не связанные с миграцией overrides в виде селекторов или объектов сохраняются.

npm

  • Прямые алиасы нормализуются до добавления соответствующего override, чтобы npm не завершался с ошибкой EOVERRIDE.
  • Если обычная установка Vite заменяется алиасом ядра, перед повторной установкой удаляются устаревшие данные установки Vite и lock-файла.
  • При использовании дополнительных браузерных провайдеров добавляется зависимость верхнего уровня vite, если без неё вложенные пакеты Vitest не могут её разрешить.

Yarn

  • Vite+ не поддерживает Plug'n'Play. Миграция обнаруживает явное и неявное использование PnP и переводит проект на nodeLinker: node-modules, сохраняя все остальные настройки .yarnrc.yml. При использовании --no-interactive преобразование выполняется автоматически. Если установлен YARN_NODE_LINKER=pnp на уровне процесса, пользователь должен изменить его самостоятельно.
  • Ссылки на каталоги и пользовательские настройки hoisting сохраняются.
  • Миграция предотвращает появление нескольких экземпляров Vitest при изолированном hoisting в рабочих пространствах: если возможно, применяется исправление на уровне пакета, а если безопасно изменить конфигурацию нельзя — выводится предупреждение.

Bun

  • Каталоги Bun работают только внутри рабочего пространства (корневой package.json с непустым полем workspaces). В рабочем пространстве Bun существующие расположения каталогов (верхнего уровня или рабочего пространства), а также ссылки на именованные каталоги сохраняются. Для отдельного проекта Bun (состоящего из одного пакета) используются конкретные версии зависимостей без создания поля каталога, поскольку bun install не умеет разрешать ссылки catalog: вне рабочего пространства.
  • Алиас ядра дублируется как прямая зависимость vite, чтобы Bun мог обнаружить поставщика peer-зависимости до применения overrides.

После миграции

  • Каждый файл конфигурации Vite проверяется на наличие конструкций, несовместимых с Rolldown (например, manualChunks). Все найденные проблемы выводятся в виде предупреждений, но конфигурация автоматически не изменяется.
  • Зависимости переустанавливаются один раз для обновления lock-файла. Если установка завершается ошибкой, миграция сообщает об этом и завершается с ненулевым кодом.
  • После успешной миграции выполняется vp fmt для файлов, изменённых в процессе миграции, за исключением путей, которые уже содержали несохранённые изменения в рабочем дереве Git. Oxfmt самостоятельно определяет поддерживаемые форматы файлов; в проектах без Git форматируется весь проект. Форматирование пропускается, если проект всё ещё использует Prettier. Ошибка форматирования выводится как предупреждение, чтобы результаты миграции сохранились, а пользователь мог вручную выполнить команду форматирования.