Правила миграции
Этот справочный раздел подробно описывает, что именно делает команда vp migrate с проектом: как она обновляет зависимости, переписывает импорты в исходном коде и сценарии пакетов, а также изменяет конфигурацию менеджера пакетов. Общее описание команды и процесса миграции см. в руководстве по миграции.
За исключением раздела Перед миграцией, в котором перечислены действия, выполняемые вручную, всё, что описано ниже, происходит автоматически.
Перед миграцией
- Выполните
vp upgrade, чтобы глобальная CLI получила последние правила миграции. Устаревшая локальная версияvite-plusне является препятствием: если локальная копия проекта старее, миграция будет выполняться глобальной CLI. - При необходимости обновите проект до Vite 8+ и Vitest 4.1+.
- Выполните
vp migrateиз корня рабочего пространства. В автоматизированных средах используйте флаг--no-interactive. - Проверьте все изменённые манифесты, конфигурацию менеджера пакетов, изменения исходного кода и сгенерированный lock-файл.
- Выполните проверку с помощью
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: false | unbundle: true |
bundle: true | Удалена; сборка по-прежнему выполняется по умолчанию |
outExtension | outExtensions |
publicDir | copy |
removeNodeProtocol: true | nodeProtocol: 'strip' |
injectStyle | css.inject |
inlineOnly / deps.onlyAllowBundle | deps.onlyBundle |
noExternal | deps.alwaysBundle |
skipNodeModulesBundle: true / deps.skipNodeModulesBundle: true | deps.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-taglatest.- Алиас
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, но нигде не объявляет vite (в dependencies, 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 переписываются с сохранением всех их аргументов:
| До | После |
|---|---|
vite | vp dev или соответствующая подкоманда vp |
vitest | vp test |
oxlint | vp lint |
oxfmt | vp fmt |
tsdown | vp pack |
lint-staged | vp staged |
eslint | vp lint, если выполняется его необязательная миграция |
prettier | vp fmt, если выполняется его необязательная миграция |
tsup | vp pack, когда выполняется его необязательная миграция |
Для команд, запускаемых через bunx, миграция сохраняет сам bunx и его флаг --bun (оставляя выбранную пользователем среду выполнения), переписывая только управляемую команду. Это также работает, если bunx находится после разделителя запуска команд, например run или --:
| До | После |
|---|---|
bunx --bun vite build | bunx --bun vp build |
bunx --bun vitest run | bunx --bun vp test run |
portless --tailscale run bunx --bun vite | portless --tailscale run bunx --bun vp dev |
dotenv -e .env.test -- bunx --bun oxlint --type-aware | dotenv -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. Ошибка форматирования выводится как предупреждение, чтобы результаты миграции сохранились, а пользователь мог вручную выполнить команду форматирования.