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+) всегда выполняется полная миграция.

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

vp migrate обновляет статические объекты pack в vite.config.* и экспортируемые объекты в tsdown.config.* для tsdown 0.23.

Это также выполняется в существующих проектах Vite+ без --full, включая пакеты workspace. Поддерживаются массивы и объекты, напрямую возвращаемые колбэками defineConfig. Конфигурации tsdown в формате JSON получают те же обновления после их объединения с vite.config.ts.

Предыдущая опцияОбновлённая опция
bundle: falseunbundle: true
bundle: trueУдалена; сборка по-прежнему выполняется по умолчанию
outExtensionoutExtensions
publicDircopy
removeNodeProtocol: truenodeProtocol: 'strip'
injectStylecss.inject
inlineOnly / deps.onlyAllowBundledeps.onlyBundle
noExternaldeps.alwaysBundle
skipNodeModulesBundle: true / deps.skipNodeModulesBundle: truedeps.neverBundle: true
dts.tsgo / dts.oxcВыбор выполняется через dts.generator; объекты параметров генератора сохраняются, а логические флаги удаляются
dts.cjsReexportУдалена; tsdown генерирует объявления CJS отдельно
--public-dir в скриптах tsdown или vp pack--copy

Для сохранения прежних значений по умолчанию при отсутствии deps.resolveDepSubpath устанавливается значение true. Для включённых проверок ATTW устанавливается profile: 'strict', если профиль не задан. Явные значения, включая false, остаются без изменений.

noExternal переносится в deps.alwaysBundle с сохранением выражений сопоставления, ссылок и методов колбэков. Существующие значения deps.alwaysBundle остаются без изменений.

Если external используется вместе с любой из форм skipNodeModulesBundle, статические сопоставители и ссылки на локальные константы переносятся в inputOptions.external перед установкой deps.neverBundle. Объявления констант и ссылки на них остаются без изменений.

Это сохраняет исходные правила сопоставления, включая пути к внешним файлам. Неподдерживаемые сопоставители, конфликтующие inputOptions и правила зависимостей, специфичные для объявлений, оставляют объект pack без изменений и приводят к предупреждению о необходимости ручной миграции.

Преобразование не выполняет код конфигурации. Объекты с spread-операторами, вычисляемыми ключами или дублирующимися ключами, а также конфликтующие старые и новые опции требуют ручной проверки. Динамические селекторы логических значений остаются без изменений. Не связанные с этим опции Vite и плагинов также остаются без изменений. После миграции выполните vp pack, чтобы проверить результат. Требования к Node.js, разрешение модулей TypeScript и возвращаемые значения программного build() требуют отдельной проверки.

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

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

ЗависимостьЧто происходит
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 управляемые ключи override используют явный диапазон @* (vite@*, vitest@*). pnpm применяет override, заменяя указанную спецификацию во всех манифестах, включая манифесты импортёров. Ключ без диапазона соответствует любой спецификации, включая catalog:, после чего vp up переписывает эту ссылку на конкретную версию. Диапазон @* ограничивает действие override семантическими диапазонами версий, которые используются в транзитивных зависимостях и peer-зависимостях. Ссылки catalog: при этом остаются ссылками на каталог, который и так разрешает их в ядро Vite+. При миграции проект, в котором всё ещё используется ключ без диапазона, получает новый ключ, при этом выбранный именованный каталог сохраняется.
  • Описанное выше правило добавления прямой зависимости относится только к 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* остаются без изменений.

Импорты JS-плагинов Oxlint

Vite+ включает Oxlint в свой состав, поэтому при миграции отдельная зависимость oxlint удаляется. Собственные JS-плагины Oxlint импортируют API для создания плагинов по имени. После удаления зависимости такой импорт перестаёт разрешаться. В результате vp lint не может загрузить плагин.

При миграции такие импорты перенаправляются на Vite+:

  • @oxlint/plugins заменяется на vite-plus/lint/plugins.
  • oxlint/plugins-dev заменяется на vite-plus/lint/plugins-dev.
  • oxlint заменяется на vite-plus/lint/plugins, если импорт использует привязку из API для создания плагинов, например defineRule, definePlugin или Context. В старых версиях Oxlint этот API предоставлялся из основной точки входа. Теперь он находится в @oxlint/plugins.

Импорт через Vite+ всегда соответствует версии Oxlint, которую включает Vite+. Второй пакет закреплять не требуется. Такой импорт также разрешается из любого пакета, который уже зависит от vite-plus.

Миграция оставляет без изменений три формы:

  • Импорты oxlint, использующие только поверхность конфигурации, например defineConfig, OxlintConfig или OxlintOverride. Они по-прежнему разрешаются через отдельный пакет.
  • Импорты oxlint по умолчанию и импорты пространства имён. Они не указывают конкретную привязку, поэтому миграция не может определить, какую из двух поверхностей следует использовать.
  • Импорты oxlint без привязок, предназначенные только для побочных эффектов, по той же причине.

Миграция также пропускает пакет, который объявляет oxlint или @oxlint/plugins в dependencies или peerDependencies, либо @oxlint/plugins в optionalDependencies. Эти зависимости могут предоставлять опубликованный плагин Oxlint. Его потребители могут не использовать Vite+.

При очистке сохраняется зависимость @oxlint/plugins для разработки, если исходный код, псевдонимы импортов пакета или собранные плагины по-прежнему ссылаются на неё. Это включает игнорируемые выходные файлы в таких каталогах, как dist, build и out.

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

  • 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, если выполняется его необязательная миграция
tsupvp pack, когда выполняется его необязательная миграция

Для команд, запускаемых через 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, а также иные способы запуска пакетов остаются без изменений.

Правила непрерывной интеграции

Миграция заменяет точные ссылки на voidzero-dev/setup-vp@v1 в рабочих процессах GitHub Actions и составных действиях в .github на последнюю точную версию setup-vp, известную этой версии Vite+. Замороженный тег v1 больше не получает новых версий. Существующие точные версии и SHA коммитов остаются без изменений.

Правила обработки версии 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. Ошибка форматирования выводится как предупреждение, чтобы результаты миграции сохранились, а пользователь мог вручную выполнить команду форматирования.