vite-plus 0.1.23 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/LICENSE +30 -0
  2. package/README.md +8 -6
  3. package/bin/oxfmt +5 -2
  4. package/bin/oxlint +12 -2
  5. package/binding/index.cjs +84 -67
  6. package/binding/index.d.cts +155 -4
  7. package/dist/{agent-aSGY0osq.js → agent-BD31CsvU.js} +969 -89
  8. package/dist/bin.js +29 -33
  9. package/dist/{compat-DXZgnEyq.js → compat-Cql3K40m.js} +1 -1
  10. package/dist/config/bin.js +30 -14
  11. package/dist/constants-CrfJQIUX.js +66 -0
  12. package/dist/create/bin.d.ts +7 -1
  13. package/dist/create/bin.js +578 -266
  14. package/dist/define-config-2tfJoXr1.d.ts +305 -0
  15. package/dist/define-config-BGSjF6Xp.cjs +488 -0
  16. package/dist/define-config-DJUehepE.js +445 -0
  17. package/dist/define-config.cjs +8 -1
  18. package/dist/define-config.d.ts +2 -2
  19. package/dist/define-config.js +2 -2
  20. package/dist/dist-DRJUd9bL.js +3 -0
  21. package/dist/{dist-BgQuvbtq.js → dist-Oxo16Y0q.js} +4 -4
  22. package/dist/index.cjs +9 -4
  23. package/dist/index.d.ts +3 -3
  24. package/dist/index.js +3 -3
  25. package/dist/{main-DpJl3LoU.js → json-Dn87fvjk.js} +137 -1
  26. package/dist/migration/bin.js +301 -84
  27. package/dist/{oxlint-plugin-config-B89iKTKN.js → oxlint-plugin-config-q8a5PFch.js} +1 -1
  28. package/dist/oxlint-plugin.js +11 -3
  29. package/dist/pack-bin.js +44 -15
  30. package/dist/{package-PmBUZ-ve.js → package-BHirM1_v.js} +3 -138
  31. package/dist/{report-DgSBQUdz.js → report-BHSkWqRR.js} +2 -0
  32. package/dist/{resolve-vite-config-TTvhycU1.js → resolve-vite-config-CmdsfQzS.js} +13 -4
  33. package/dist/staged/bin.js +150 -417
  34. package/dist/test/_at-vitest-browser/context.d.ts +2 -0
  35. package/dist/test/_at-vitest-browser.d.ts +2 -0
  36. package/dist/test/browser/context.d.ts +2 -2
  37. package/dist/test/browser/context.js +1 -1
  38. package/dist/test/browser/providers/playwright/context.d.ts +1 -0
  39. package/dist/test/browser/providers/playwright/context.js +1 -0
  40. package/dist/test/browser/providers/playwright.d.ts +124 -2
  41. package/dist/test/browser/providers/playwright.js +1 -1
  42. package/dist/test/browser/providers/preview/context.d.ts +1 -0
  43. package/dist/test/browser/providers/preview/context.js +1 -0
  44. package/dist/test/browser/providers/preview.d.ts +32 -2
  45. package/dist/test/browser/providers/preview.js +1 -1
  46. package/dist/test/browser/providers/webdriverio/context.d.ts +1 -0
  47. package/dist/test/browser/providers/webdriverio/context.js +1 -0
  48. package/dist/test/browser/providers/webdriverio.d.ts +77 -2
  49. package/dist/test/browser/providers/webdriverio.js +1 -1
  50. package/dist/test/browser-compat.d.ts +2 -0
  51. package/dist/test/browser-compat.js +1 -1
  52. package/dist/test/browser-playwright/context.d.ts +1 -0
  53. package/dist/test/browser-playwright/context.js +1 -0
  54. package/dist/test/browser-playwright.d.ts +124 -2
  55. package/dist/test/browser-playwright.js +1 -1
  56. package/dist/test/browser-preview/context.d.ts +1 -0
  57. package/dist/test/browser-preview/context.js +1 -0
  58. package/dist/test/browser-preview.d.ts +32 -2
  59. package/dist/test/browser-preview.js +1 -1
  60. package/dist/test/browser-webdriverio/context.d.ts +1 -0
  61. package/dist/test/browser-webdriverio/context.js +1 -0
  62. package/dist/test/browser-webdriverio.d.ts +77 -2
  63. package/dist/test/browser-webdriverio.js +1 -1
  64. package/dist/test/browser.d.ts +2 -2
  65. package/dist/test/browser.js +1 -1
  66. package/dist/test/client.js +1 -1
  67. package/dist/test/config.cjs +1 -1
  68. package/dist/test/config.d.ts +2 -2
  69. package/dist/test/config.js +1 -1
  70. package/dist/test/context.d.ts +942 -2
  71. package/dist/test/context.js +1 -1
  72. package/dist/test/coverage.d.ts +2 -2
  73. package/dist/test/coverage.js +1 -1
  74. package/dist/test/environments.d.ts +2 -2
  75. package/dist/test/environments.js +1 -1
  76. package/dist/test/globals.d.ts +2 -2
  77. package/dist/test/import-meta.d.ts +2 -2
  78. package/dist/test/importMeta.d.ts +2 -2
  79. package/dist/test/index.cjs +1 -1
  80. package/dist/test/index.d.cts +2 -2
  81. package/dist/test/index.d.ts +2 -2
  82. package/dist/test/index.js +1 -1
  83. package/dist/test/internal/browser.d.ts +2 -2
  84. package/dist/test/internal/browser.js +1 -1
  85. package/dist/test/jsdom.d.ts +2 -2
  86. package/dist/test/locators.d.ts +294 -0
  87. package/dist/test/locators.js +1 -1
  88. package/dist/test/matchers.d.ts +29 -0
  89. package/dist/test/matchers.js +1 -1
  90. package/dist/test/node.d.ts +2 -2
  91. package/dist/test/node.js +1 -1
  92. package/dist/test/optional-runtime-types.js.d.ts +2 -2
  93. package/dist/test/optional-types.js.d.ts +2 -2
  94. package/dist/test/plugins/browser-client.js +1 -1
  95. package/dist/test/plugins/browser-context.js +1 -1
  96. package/dist/test/plugins/browser-locators.js +1 -1
  97. package/dist/test/plugins/browser-playwright.js +1 -1
  98. package/dist/test/plugins/browser-preview.js +1 -1
  99. package/dist/test/plugins/browser-webdriverio.js +1 -1
  100. package/dist/test/plugins/browser.js +1 -1
  101. package/dist/test/plugins/expect.js +1 -1
  102. package/dist/test/plugins/mocker-automock.js +1 -1
  103. package/dist/test/plugins/mocker-browser.js +1 -1
  104. package/dist/test/plugins/mocker-node.js +1 -1
  105. package/dist/test/plugins/mocker-redirect.js +1 -1
  106. package/dist/test/plugins/mocker-register.js +1 -1
  107. package/dist/test/plugins/mocker-transforms.js +1 -1
  108. package/dist/test/plugins/mocker.js +1 -1
  109. package/dist/test/plugins/pretty-format.js +1 -1
  110. package/dist/test/plugins/runner-types.js +1 -1
  111. package/dist/test/plugins/runner-utils.js +1 -1
  112. package/dist/test/plugins/runner.js +1 -1
  113. package/dist/test/plugins/snapshot-environment.js +1 -1
  114. package/dist/test/plugins/snapshot-manager.js +1 -1
  115. package/dist/test/plugins/snapshot.js +1 -1
  116. package/dist/test/plugins/spy.js +1 -1
  117. package/dist/test/plugins/utils-constants.js +1 -1
  118. package/dist/test/plugins/utils-diff.js +1 -1
  119. package/dist/test/plugins/utils-display.js +1 -1
  120. package/dist/test/plugins/utils-error.js +1 -1
  121. package/dist/test/plugins/utils-helpers.js +1 -1
  122. package/dist/test/plugins/utils-offset.js +1 -1
  123. package/dist/test/plugins/utils-resolver.js +1 -1
  124. package/dist/test/plugins/utils-serialize.js +1 -1
  125. package/dist/test/plugins/utils-source-map-node.js +1 -1
  126. package/dist/test/plugins/utils-source-map.js +1 -1
  127. package/dist/test/plugins/utils-timers.js +1 -1
  128. package/dist/test/plugins/utils.js +1 -1
  129. package/dist/test/reporters.d.ts +2 -2
  130. package/dist/test/reporters.js +1 -1
  131. package/dist/test/runners.d.ts +2 -2
  132. package/dist/test/runners.js +1 -1
  133. package/dist/test/runtime.d.ts +2 -2
  134. package/dist/test/runtime.js +1 -1
  135. package/dist/test/snapshot.d.ts +2 -2
  136. package/dist/test/snapshot.js +1 -1
  137. package/dist/test/suite.d.ts +2 -2
  138. package/dist/test/suite.js +1 -1
  139. package/dist/test/utils.js +1 -1
  140. package/dist/test/worker.d.ts +2 -2
  141. package/dist/test/worker.js +1 -1
  142. package/dist/{tsconfig-DlUVXT3J.js → tsconfig-CJ_StdFc.js} +605 -263
  143. package/dist/tsgolint-path-B-yOos8p.js +32 -0
  144. package/dist/tsgolint-path.d.ts +8 -0
  145. package/dist/tsgolint-path.js +2 -0
  146. package/dist/version.js +3 -3
  147. package/dist/versions.d.ts +1 -1
  148. package/dist/versions.js +7 -7
  149. package/dist/{workspace-DElv730L.js → workspace-Cjoc1c_A.js} +20 -18
  150. package/docs/_data/team.ts +5 -4
  151. package/docs/config/create.md +36 -1
  152. package/docs/config/index.md +7 -5
  153. package/docs/guide/commit-hooks.md +9 -0
  154. package/docs/guide/create.md +106 -2
  155. package/docs/guide/env.md +33 -5
  156. package/docs/guide/index.md +9 -3
  157. package/docs/guide/install.md +46 -10
  158. package/docs/guide/migrate.md +13 -3
  159. package/docs/guide/troubleshooting.md +3 -29
  160. package/docs/guide/upgrade.md +36 -6
  161. package/docs/package.json +3 -3
  162. package/docs/pnpm-lock.yaml +298 -395
  163. package/package.json +104 -56
  164. package/templates/generator/bin/index.ts +6 -3
  165. package/templates/generator/package.json +2 -3
  166. package/templates/generator/src/template.ts +0 -2
  167. package/templates/monorepo/package.json +1 -1
  168. package/dist/constants-DCBWlNrn.js +0 -33
  169. package/dist/define-config-BR1Y88zz.cjs +0 -84
  170. package/dist/define-config-BRC7qPNE.js +0 -21
  171. package/dist/define-config-COdn-tsn.d.ts +0 -177
  172. package/dist/dist-Bapm49IR.js +0 -3
  173. package/dist/test/plugins/utils-highlight.js +0 -1
  174. /package/dist/{chunk-DnnnRqeS.js → rolldown-runtime-DnnnRqeS.js} +0 -0
@@ -1,14 +1,16 @@
1
- import { r as __toESM } from "./chunk-DnnnRqeS.js";
2
- import { a as VITE_PLUS_OVERRIDE_PACKAGES, c as isForceOverrideMode, i as VITE_PLUS_NAME, n as BASEURL_TSCONFIG_WARNING, o as VITE_PLUS_VERSION } from "./constants-DCBWlNrn.js";
3
- import { i as ensureVitePlusImportRuleDefaults, r as createDefaultVitePlusLintConfig } from "./oxlint-plugin-config-B89iKTKN.js";
4
- import { A as q, C as log, E as select, M as runCommandSilently, N as require_cross_spawn, a as removeDeprecatedTsconfigFalseOption, i as hasBaseUrlInTsconfig, n as findTsconfigFiles, o as rewriteTypesInTsconfig, s as cancelAndExit, u as getSpinner, v as PackageManager, w as multiselect, x as confirm, y as require_semver } from "./tsconfig-DlUVXT3J.js";
5
- import { t as require_dist } from "./dist-BgQuvbtq.js";
6
- import { c as editJsonFile, l as isJsonFile, n as detectPackageMetadata, u as readJsonFile } from "./package-PmBUZ-ve.js";
7
- import { n as addMigrationWarning, t as addManualStep } from "./report-DgSBQUdz.js";
1
+ import { r as __toESM } from "./rolldown-runtime-DnnnRqeS.js";
2
+ import { a as VITEST_VERSION, c as VITE_PLUS_VERSION, i as VITEST_AGE_GATE_EXEMPT_PACKAGES, n as BASEURL_TSCONFIG_WARNING, o as VITE_PLUS_NAME, s as VITE_PLUS_OVERRIDE_PACKAGES, u as isForceOverrideMode } from "./constants-CrfJQIUX.js";
3
+ import { i as ensureVitePlusImportRuleDefaults, r as createDefaultVitePlusLintConfig } from "./oxlint-plugin-config-q8a5PFch.js";
4
+ import { A as select, C as require_semver, D as log, I as runCommandSilently, L as require_cross_spawn, O as multiselect, P as R, S as PackageManager, T as confirm, a as hasTypesToRewriteInTsconfig, b as getSpinner, c as cancelAndExit, i as hasBaseUrlInTsconfig, n as findTsconfigFiles, o as removeDeprecatedTsconfigFalseOption, s as rewriteTypesInTsconfig } from "./tsconfig-CJ_StdFc.js";
5
+ import { n as isJsonFile, r as readJsonFile, t as editJsonFile } from "./json-Dn87fvjk.js";
6
+ import { t as require_dist } from "./dist-Oxo16Y0q.js";
7
+ import { n as detectPackageMetadata } from "./package-BHirM1_v.js";
8
+ import { n as addMigrationWarning, t as addManualStep } from "./report-BHSkWqRR.js";
8
9
  import path from "node:path";
9
- import { hasConfigKey, mergeJsonConfig, mergeTsdownConfig, rewriteEslint, rewriteImportsInDirectory, rewritePrettier, rewriteScripts } from "../binding/index.js";
10
+ import { hasConfigKey, mergeJsonConfig, mergeTsdownConfig, rewriteEslint, rewriteImportsInDirectory, rewritePrettier, rewriteScripts, wrapLazyPlugins } from "../binding/index.js";
10
11
  import fs from "node:fs";
11
12
  import { styleText } from "node:util";
13
+ import os from "node:os";
12
14
  import fsPromises from "node:fs/promises";
13
15
  //#region src/utils/path.ts
14
16
  var import_cross_spawn = /* @__PURE__ */ __toESM(require_cross_spawn(), 1);
@@ -173,14 +175,134 @@ const REMOVE_PACKAGES = [
173
175
  "oxfmt",
174
176
  "tsdown",
175
177
  "@vitest/browser",
176
- "@vitest/browser-preview",
177
- "@vitest/browser-playwright",
178
- "@vitest/browser-webdriverio"
178
+ "@vitest/browser-preview"
179
179
  ];
180
+ const WEBDRIVERIO_PROVIDER = "@vitest/browser-webdriverio";
181
+ const PLAYWRIGHT_PROVIDER = "@vitest/browser-playwright";
182
+ const OPT_IN_BROWSER_PROVIDERS = [WEBDRIVERIO_PROVIDER, PLAYWRIGHT_PROVIDER];
183
+ const PROVIDER_OVERRIDE_DROP_NAMES = [...REMOVE_PACKAGES, ...OPT_IN_BROWSER_PROVIDERS];
184
+ function extractOverrideTargetName(key) {
185
+ let target = key.trim();
186
+ for (let delim = target.search(/[^ |@]>/); delim !== -1; delim = target.search(/[^ |@]>/)) target = target.slice(delim + 2).trim();
187
+ if (!target) return target;
188
+ if (target.includes("/")) {
189
+ const segments = target.split("/");
190
+ const last = segments[segments.length - 1];
191
+ const scope = segments[segments.length - 2];
192
+ target = scope?.startsWith("@") ? `${scope}/${last}` : last;
193
+ }
194
+ const nameStart = target.startsWith("@") ? target.indexOf("/") + 1 : 0;
195
+ const versionAt = target.indexOf("@", nameStart);
196
+ if (versionAt > 0) target = target.slice(0, versionAt);
197
+ return target;
198
+ }
199
+ function isRemovePackageOverrideKey(key) {
200
+ return PROVIDER_OVERRIDE_DROP_NAMES.includes(extractOverrideTargetName(key));
201
+ }
202
+ function stripSegmentVersion(segment) {
203
+ const nameStart = segment.startsWith("@") ? segment.indexOf("/") + 1 : 0;
204
+ const versionAt = segment.indexOf("@", nameStart);
205
+ return versionAt > 0 ? segment.slice(0, versionAt) : segment;
206
+ }
207
+ function parentGlobMatchesName(glob, name) {
208
+ const pattern = glob.split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*");
209
+ return new RegExp(`^${pattern}$`).test(name);
210
+ }
211
+ function ancestorSegmentMatches(segment, name) {
212
+ return segment.includes("*") ? parentGlobMatchesName(segment, name) : segment === name;
213
+ }
214
+ const OWNED_PROVIDER_ANCESTOR_NAMES = REMOVE_PACKAGES.filter((name) => name.startsWith("@vitest/"));
215
+ function parentChainReachesVitePlus(segments) {
216
+ const concrete = segments.filter((segment) => segment !== "**");
217
+ let index = 0;
218
+ if (concrete.length > 0 && ancestorSegmentMatches(concrete[0], "vite-plus")) index = 1;
219
+ for (; index < concrete.length; index += 1) {
220
+ const segment = concrete[index];
221
+ if (!OWNED_PROVIDER_ANCESTOR_NAMES.some((name) => ancestorSegmentMatches(segment, name))) return false;
222
+ }
223
+ return true;
224
+ }
225
+ function extractOverrideParentSegments(key) {
226
+ let rest = key.trim();
227
+ const pnpmParents = [];
228
+ for (let delim = rest.search(/[^ |@]>/); delim !== -1; delim = rest.search(/[^ |@]>/)) {
229
+ pnpmParents.push(stripSegmentVersion(rest.slice(0, delim + 1).trim()));
230
+ rest = rest.slice(delim + 2).trim();
231
+ }
232
+ if (pnpmParents.length > 0) return pnpmParents;
233
+ if (!rest.includes("/")) return null;
234
+ const segments = rest.split("/");
235
+ const descriptorSegmentCount = segments[segments.length - 2]?.startsWith("@") ?? false ? 2 : 1;
236
+ const rawParents = segments.slice(0, segments.length - descriptorSegmentCount);
237
+ if (rawParents.length === 0) return null;
238
+ const parents = [];
239
+ for (let i = 0; i < rawParents.length; i += 1) {
240
+ const segment = rawParents[i];
241
+ if (segment.startsWith("@") && i + 1 < rawParents.length) {
242
+ parents.push(stripSegmentVersion(`${segment}/${rawParents[i + 1]}`));
243
+ i += 1;
244
+ } else parents.push(stripSegmentVersion(segment));
245
+ }
246
+ return parents;
247
+ }
248
+ function providerKeyReachesVitePlus(key, ancestorChain) {
249
+ if (!isRemovePackageOverrideKey(key)) return false;
250
+ const keyParents = extractOverrideParentSegments(key) ?? [];
251
+ return parentChainReachesVitePlus([...ancestorChain, ...keyParents]);
252
+ }
253
+ function shouldDropProviderOverrideKey(key) {
254
+ return providerKeyReachesVitePlus(key, []);
255
+ }
256
+ function childChainContribution(key) {
257
+ return [...extractOverrideParentSegments(key) ?? [], extractOverrideTargetName(key)];
258
+ }
259
+ // @vitest/provider`. Covers bare, versioned, global-glob and `vite-plus`-parent
260
+ function dropRemovePackageOverrideKeys(overrides, ancestorChain = []) {
261
+ if (!overrides) return false;
262
+ let removed = false;
263
+ for (const key of Object.keys(overrides)) {
264
+ const value = overrides[key];
265
+ const child = value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
266
+ if (providerKeyReachesVitePlus(key, ancestorChain)) {
267
+ if (child) {
268
+ let changed = false;
269
+ if ("." in child) {
270
+ delete child["."];
271
+ changed = true;
272
+ }
273
+ if (dropRemovePackageOverrideKeys(child, [...ancestorChain, ...childChainContribution(key)])) changed = true;
274
+ if (Object.keys(child).length === 0) {
275
+ delete overrides[key];
276
+ changed = true;
277
+ }
278
+ if (changed) removed = true;
279
+ } else {
280
+ delete overrides[key];
281
+ removed = true;
282
+ }
283
+ continue;
284
+ }
285
+ if (child) {
286
+ if (dropRemovePackageOverrideKeys(child, [...ancestorChain, ...childChainContribution(key)])) {
287
+ removed = true;
288
+ if (Object.keys(child).length === 0) delete overrides[key];
289
+ }
290
+ }
291
+ }
292
+ return removed;
293
+ }
180
294
  const BROWSER_PROVIDER_PEER_DEPS = {
181
295
  "@vitest/browser-playwright": "playwright",
182
296
  "@vitest/browser-webdriverio": "webdriverio"
183
297
  };
298
+ const BROWSER_PROVIDER_POSTINSTALL_PACKAGES = ["edgedriver", "geckodriver"];
299
+ const WEBDRIVERIO_ALLOW_SIGNAL_DEPS = ["webdriverio", WEBDRIVERIO_PROVIDER];
300
+ const VITEST_BROWSER_DEP_NAMES = [
301
+ "@vitest/browser",
302
+ "@vitest/browser-preview",
303
+ "@vitest/browser-playwright",
304
+ "@vitest/browser-webdriverio"
305
+ ];
184
306
  const PUBLIC_PEER_DEPENDENCY_FALLBACKS = {
185
307
  vite: "*",
186
308
  vitest: "*"
@@ -202,6 +324,46 @@ const OXLINT_NATIVE_PLUGINS = new Set([
202
324
  "node",
203
325
  "vue"
204
326
  ]);
327
+ const LEGACY_WRAPPER_PACKAGE_NAMES = ["@voidzero-dev/vite-plus-test"];
328
+ const LEGACY_WRAPPER_FALLBACK_VERSIONS = { vitest: VITEST_VERSION };
329
+ function isLegacyWrapperSpec(value) {
330
+ if (typeof value !== "string" || !value) return false;
331
+ for (const name of LEGACY_WRAPPER_PACKAGE_NAMES) if (value === `npm:${name}` || value.startsWith(`npm:${name}@`)) return true;
332
+ return false;
333
+ }
334
+ /**
335
+ * Rewrite or remove keys whose value points at a deleted vite-plus wrapper.
336
+ * When a fallback exists for the key (e.g. `vitest`), the value is replaced
337
+ * so existing `catalog:` references continue to resolve. Otherwise the key
338
+ * is dropped entirely. Returns true iff any entry was changed.
339
+ *
340
+ * npm/bun `overrides` may nest an object of scoped overrides under a parent
341
+ * key (e.g. `{ "some-parent": { "vitest": "npm:@voidzero-dev/vite-plus-test@latest" } }`),
342
+ * so object values are recursed into; a parent emptied by pruning is dropped so
343
+ * no `{}` is left behind. Flat maps (pnpm `overrides`, yarn `resolutions`,
344
+ * catalogs) hold only string values, where the recursion is inert.
345
+ */
346
+ function pruneLegacyWrapperAliases(record) {
347
+ if (!record) return false;
348
+ let mutated = false;
349
+ for (const key of Object.keys(record)) {
350
+ const value = record[key];
351
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
352
+ if (pruneLegacyWrapperAliases(value)) {
353
+ mutated = true;
354
+ if (Object.keys(value).length === 0) delete record[key];
355
+ }
356
+ continue;
357
+ }
358
+ if (isLegacyWrapperSpec(value)) {
359
+ const fallback = LEGACY_WRAPPER_FALLBACK_VERSIONS[key];
360
+ if (fallback !== void 0) record[key] = fallback;
361
+ else delete record[key];
362
+ mutated = true;
363
+ }
364
+ }
365
+ return mutated;
366
+ }
205
367
  function warnMigration(message, report) {
206
368
  addMigrationWarning(report, message);
207
369
  if (!report) log.warn(message);
@@ -643,11 +805,17 @@ function cleanupDeprecatedTsconfigOptions(projectPath, silent = false, report) {
643
805
  }
644
806
  }
645
807
  function rewriteTsconfigTypes(projectPath, silent = false, report) {
808
+ let changed = false;
646
809
  const files = findTsconfigFiles(projectPath);
647
810
  for (const filePath of files) if (rewriteTypesInTsconfig(filePath)) {
811
+ changed = true;
648
812
  if (report) report.removedConfigCount++;
649
813
  if (!silent) log.success(`✔ Rewrote types in ${displayRelative(filePath)}`);
650
814
  }
815
+ return changed;
816
+ }
817
+ function hasTsconfigTypesToRewrite(projectPath) {
818
+ return findTsconfigFiles(projectPath).some((filePath) => hasTypesToRewriteInTsconfig(filePath));
651
819
  }
652
820
  const FRAMEWORK_SHIMS = {
653
821
  vue: [
@@ -716,51 +884,67 @@ function rewriteStandaloneProject(projectPath, workspaceInfo, skipStagedMigratio
716
884
  if (!fs.existsSync(packageJsonPath)) return;
717
885
  const packageManager = workspaceInfo.packageManager;
718
886
  const catalogDependencyResolver = createCatalogDependencyResolver(projectPath, packageManager);
887
+ const pnpmMajorVersion = pnpmMajor(workspaceInfo.downloadPackageManager.version);
719
888
  let extractedStagedConfig = null;
720
889
  let remainingPnpmOverrides;
721
890
  let shouldRewritePnpmWorkspaceYaml = false;
722
891
  let shouldAddPnpmWorkspaceVitePlusOverride = false;
892
+ let shouldAllowBrowserProviderBuilds = false;
723
893
  let usePnpmWorkspaceYaml = false;
724
894
  editJsonFile(packageJsonPath, (pkg) => {
895
+ shouldAllowBrowserProviderBuilds = hasOwnWebdriverioDependency(pkg) || usesWebdriverioProvider(projectPath);
896
+ pruneLegacyWrapperAliases(pkg.resolutions);
897
+ pruneLegacyWrapperAliases(pkg.overrides);
898
+ pruneLegacyWrapperAliases(pkg.pnpm?.overrides);
899
+ dropRemovePackageOverrideKeys(pkg.resolutions);
900
+ dropRemovePackageOverrideKeys(pkg.overrides);
725
901
  if (packageManager === PackageManager.yarn) pkg.resolutions = {
726
902
  ...pkg.resolutions,
727
903
  ...VITE_PLUS_OVERRIDE_PACKAGES
728
904
  };
729
- else if (packageManager === PackageManager.npm || packageManager === PackageManager.bun) pkg.overrides = {
730
- ...pkg.overrides,
731
- ...VITE_PLUS_OVERRIDE_PACKAGES
732
- };
733
- else if (packageManager === PackageManager.pnpm) {
905
+ else if (packageManager === PackageManager.npm || packageManager === PackageManager.bun) {
906
+ pkg.overrides = {
907
+ ...pkg.overrides,
908
+ ...VITE_PLUS_OVERRIDE_PACKAGES
909
+ };
910
+ if (packageManager === PackageManager.bun) pkg.devDependencies = {
911
+ ...pkg.devDependencies,
912
+ vite: VITE_PLUS_OVERRIDE_PACKAGES.vite
913
+ };
914
+ } else if (packageManager === PackageManager.pnpm) {
734
915
  usePnpmWorkspaceYaml = !pkg.pnpm;
735
916
  if (usePnpmWorkspaceYaml) {
736
917
  shouldRewritePnpmWorkspaceYaml = true;
737
918
  shouldAddPnpmWorkspaceVitePlusOverride = isForceOverrideMode();
738
919
  }
739
920
  const overrideKeys = Object.keys(VITE_PLUS_OVERRIDE_PACKAGES);
740
- if (!usePnpmWorkspaceYaml) pkg.pnpm = {
741
- ...pkg.pnpm,
742
- overrides: {
743
- ...pkg.pnpm?.overrides,
744
- ...VITE_PLUS_OVERRIDE_PACKAGES,
745
- ...isForceOverrideMode() ? { [VITE_PLUS_NAME]: VITE_PLUS_VERSION } : {}
746
- },
747
- peerDependencyRules: {
748
- ...pkg.pnpm?.peerDependencyRules,
749
- allowAny: [...new Set([...pkg.pnpm?.peerDependencyRules?.allowAny ?? [], ...overrideKeys])],
750
- allowedVersions: {
751
- ...pkg.pnpm?.peerDependencyRules?.allowedVersions,
752
- ...Object.fromEntries(overrideKeys.map((key) => [key, "*"]))
921
+ if (!usePnpmWorkspaceYaml) {
922
+ dropRemovePackageOverrideKeys(pkg.pnpm?.overrides);
923
+ pkg.pnpm = {
924
+ ...pkg.pnpm,
925
+ overrides: {
926
+ ...pkg.pnpm?.overrides,
927
+ ...VITE_PLUS_OVERRIDE_PACKAGES,
928
+ ...isForceOverrideMode() ? { [VITE_PLUS_NAME]: VITE_PLUS_VERSION } : {}
929
+ },
930
+ peerDependencyRules: {
931
+ ...pkg.pnpm?.peerDependencyRules,
932
+ allowAny: [...new Set([...pkg.pnpm?.peerDependencyRules?.allowAny ?? [], ...overrideKeys])],
933
+ allowedVersions: {
934
+ ...pkg.pnpm?.peerDependencyRules?.allowedVersions,
935
+ ...Object.fromEntries(overrideKeys.map((key) => [key, "*"]))
936
+ }
753
937
  }
754
- }
755
- };
756
- else remainingPnpmOverrides = cleanupPnpmOverridesForWorkspaceYaml(pkg, overrideKeys);
938
+ };
939
+ } else remainingPnpmOverrides = cleanupPnpmOverridesForWorkspaceYaml(pkg, overrideKeys);
757
940
  for (const key in pkg.pnpm?.overrides) if (key.includes(">")) {
758
941
  const splits = key.split(">");
759
942
  if (splits[splits.length - 1].trim() === "vite") delete pkg.pnpm.overrides[key];
760
943
  }
761
- for (const key of [...overrideKeys, ...REMOVE_PACKAGES]) if (pkg.resolutions?.[key]) delete pkg.resolutions[key];
944
+ for (const key of [...overrideKeys, ...PROVIDER_OVERRIDE_DROP_NAMES]) if (pkg.resolutions?.[key]) delete pkg.resolutions[key];
945
+ if (!usePnpmWorkspaceYaml && pnpmMajorVersion !== void 0 && pkg.pnpm) applyBuildAllowanceToPackageJsonPnpm(pkg.pnpm, pnpmMajorVersion, shouldAllowBrowserProviderBuilds);
762
946
  }
763
- extractedStagedConfig = rewritePackageJson(pkg, packageManager, usePnpmWorkspaceYaml, skipStagedMigration, catalogDependencyResolver);
947
+ extractedStagedConfig = rewritePackageJson(pkg, packageManager, usePnpmWorkspaceYaml, skipStagedMigration, catalogDependencyResolver, usesVitestBrowserMode(projectPath), collectProviderSourceModes(projectPath));
764
948
  if (!pkg.devDependencies?.["vite-plus"] || isForceOverrideMode()) {
765
949
  const version = usePnpmWorkspaceYaml && !VITE_PLUS_VERSION.startsWith("file:") ? "catalog:" : VITE_PLUS_VERSION;
766
950
  pkg.devDependencies = {
@@ -770,10 +954,11 @@ function rewriteStandaloneProject(projectPath, workspaceInfo, skipStagedMigratio
770
954
  }
771
955
  return pkg;
772
956
  });
773
- if (shouldRewritePnpmWorkspaceYaml) rewritePnpmWorkspaceYaml(projectPath);
957
+ if (shouldRewritePnpmWorkspaceYaml) rewritePnpmWorkspaceYaml(projectPath, pnpmMajorVersion, shouldAllowBrowserProviderBuilds);
774
958
  if (remainingPnpmOverrides) migratePnpmOverridesToWorkspaceYaml(projectPath, remainingPnpmOverrides);
775
959
  if (shouldAddPnpmWorkspaceVitePlusOverride) migratePnpmOverridesToWorkspaceYaml(projectPath, { [VITE_PLUS_NAME]: VITE_PLUS_VERSION });
776
960
  if (packageManager === PackageManager.yarn) rewriteYarnrcYml(projectPath);
961
+ else if (packageManager === PackageManager.bun) ensureBunfigPeerSuppression(projectPath);
777
962
  if (extractedStagedConfig) {
778
963
  if (mergeStagedConfigToViteConfig(projectPath, extractedStagedConfig, silent, report)) removeLintStagedFromPackageJson(packageJsonPath);
779
964
  }
@@ -785,6 +970,7 @@ function rewriteStandaloneProject(projectPath, workspaceInfo, skipStagedMigratio
785
970
  injectFmtDefaults(projectPath, silent, report);
786
971
  mergeTsdownConfigFile(projectPath, silent, report);
787
972
  rewriteAllImports(projectPath, silent, report);
973
+ wrapLazyPluginsInViteConfig(projectPath, silent, report);
788
974
  setPackageManager(projectPath, workspaceInfo.downloadPackageManager);
789
975
  }
790
976
  /**
@@ -793,15 +979,17 @@ function rewriteStandaloneProject(projectPath, workspaceInfo, skipStagedMigratio
793
979
  */
794
980
  function rewriteMonorepo(workspaceInfo, skipStagedMigration, silent = false, report) {
795
981
  const catalogDependencyResolver = createCatalogDependencyResolver(workspaceInfo.rootDir, workspaceInfo.packageManager);
796
- if (workspaceInfo.packageManager === PackageManager.pnpm) rewritePnpmWorkspaceYaml(workspaceInfo.rootDir);
982
+ const pnpmMajorVersion = pnpmMajor(workspaceInfo.downloadPackageManager.version);
983
+ const workspaceShouldAllowBrowserBuilds = workspaceUsesWebdriverio(workspaceInfo.rootDir, workspaceInfo.packages);
984
+ if (workspaceInfo.packageManager === PackageManager.pnpm) rewritePnpmWorkspaceYaml(workspaceInfo.rootDir, pnpmMajorVersion, workspaceShouldAllowBrowserBuilds);
797
985
  else if (workspaceInfo.packageManager === PackageManager.yarn) rewriteYarnrcYml(workspaceInfo.rootDir);
798
986
  else if (workspaceInfo.packageManager === PackageManager.bun) rewriteBunCatalog(workspaceInfo.rootDir);
799
- rewriteRootWorkspacePackageJson(workspaceInfo.rootDir, workspaceInfo.packageManager, skipStagedMigration, catalogDependencyResolver, workspaceInfo.packages);
987
+ rewriteRootWorkspacePackageJson(workspaceInfo.rootDir, workspaceInfo.packageManager, skipStagedMigration, catalogDependencyResolver, workspaceInfo.packages, pnpmMajorVersion, workspaceShouldAllowBrowserBuilds);
800
988
  const workspaceContext = {
801
989
  rootDir: workspaceInfo.rootDir,
802
990
  packages: workspaceInfo.packages
803
991
  };
804
- for (const pkg of workspaceInfo.packages) rewriteMonorepoProject(path.join(workspaceInfo.rootDir, pkg.path), workspaceInfo.packageManager, skipStagedMigration, silent, report, catalogDependencyResolver, workspaceContext);
992
+ for (const pkg of workspaceInfo.packages) rewriteMonorepoProject(path.join(workspaceInfo.rootDir, pkg.path), workspaceInfo.packageManager, skipStagedMigration, silent, report, catalogDependencyResolver, workspaceContext, true);
805
993
  if (!skipStagedMigration) rewriteLintStagedConfigFile(workspaceInfo.rootDir, report);
806
994
  cleanupDeprecatedTsconfigOptions(workspaceInfo.rootDir, silent, report);
807
995
  rewriteTsconfigTypes(workspaceInfo.rootDir, silent, report);
@@ -810,6 +998,8 @@ function rewriteMonorepo(workspaceInfo, skipStagedMigration, silent = false, rep
810
998
  injectFmtDefaults(workspaceInfo.rootDir, silent, report);
811
999
  mergeTsdownConfigFile(workspaceInfo.rootDir, silent, report);
812
1000
  rewriteAllImports(workspaceInfo.rootDir, silent, report);
1001
+ wrapLazyPluginsInViteConfig(workspaceInfo.rootDir, silent, report);
1002
+ for (const pkg of workspaceInfo.packages) wrapLazyPluginsInViteConfig(path.join(workspaceInfo.rootDir, pkg.path), silent, report);
813
1003
  setPackageManager(workspaceInfo.rootDir, workspaceInfo.downloadPackageManager);
814
1004
  }
815
1005
  /**
@@ -821,32 +1011,41 @@ function rewriteMonorepo(workspaceInfo, skipStagedMigration, silent = false, rep
821
1011
  * workspace root (paths in `packages` are relative to it); `packages`
822
1012
  * is the workspace package list.
823
1013
  */
824
- function rewriteMonorepoProject(projectPath, packageManager, skipStagedMigration, silent = false, report, catalogDependencyResolver, workspaceContext) {
1014
+ function rewriteMonorepoProject(projectPath, packageManager, skipStagedMigration, silent = false, report, catalogDependencyResolver, workspaceContext, deferLazyPluginWrapping = false) {
825
1015
  cleanupDeprecatedTsconfigOptions(projectPath, silent, report);
826
1016
  rewriteTsconfigTypes(projectPath, silent, report);
827
1017
  mergeViteConfigFiles(projectPath, silent, report, workspaceContext?.packages, workspaceContext?.rootDir);
828
1018
  mergeTsdownConfigFile(projectPath, silent, report);
829
1019
  const packageJsonPath = path.join(projectPath, "package.json");
830
1020
  if (!fs.existsSync(packageJsonPath)) return;
1021
+ const yarnHoisting = packageManager === PackageManager.yarn ? findYarnWorkspaceHoisting(workspaceContext?.rootDir ?? projectPath) : void 0;
831
1022
  let extractedStagedConfig = null;
832
1023
  editJsonFile(packageJsonPath, (pkg) => {
833
- extractedStagedConfig = rewritePackageJson(pkg, packageManager, true, skipStagedMigration, catalogDependencyResolver);
1024
+ extractedStagedConfig = rewritePackageJson(pkg, packageManager, true, skipStagedMigration, catalogDependencyResolver, usesVitestBrowserMode(projectPath), collectProviderSourceModes(projectPath));
1025
+ if (yarnHoisting && path.resolve(projectPath) !== yarnHoisting.rootDir && pkg.devDependencies?.["vite-plus"]) applyYarnWorkspaceHoistingFix(pkg, yarnHoisting.limit, yarnHoisting.nodeLinker, path.relative(yarnHoisting.rootDir, projectPath) || projectPath, report);
834
1026
  return pkg;
835
1027
  });
836
1028
  if (extractedStagedConfig) {
837
1029
  if (mergeStagedConfigToViteConfig(projectPath, extractedStagedConfig, silent, report)) removeLintStagedFromPackageJson(packageJsonPath);
838
1030
  }
1031
+ if (!deferLazyPluginWrapping) wrapLazyPluginsInViteConfig(projectPath, silent, report);
839
1032
  }
840
1033
  /**
841
1034
  * Rewrite pnpm-workspace.yaml to add vite-plus dependencies
842
1035
  * @param projectPath - The path to the project
843
1036
  */
844
- function rewritePnpmWorkspaceYaml(projectPath) {
1037
+ function rewritePnpmWorkspaceYaml(projectPath, pnpmMajorVersion, shouldAllowBrowserBuilds) {
845
1038
  const pnpmWorkspaceYamlPath = path.join(projectPath, "pnpm-workspace.yaml");
846
1039
  if (!fs.existsSync(pnpmWorkspaceYamlPath)) fs.writeFileSync(pnpmWorkspaceYamlPath, "");
847
1040
  editYamlFile(pnpmWorkspaceYamlPath, (doc) => {
848
1041
  rewriteCatalog(doc);
1042
+ if (pnpmMajorVersion !== void 0) applyBuildAllowanceToWorkspaceYaml(doc, pnpmMajorVersion, shouldAllowBrowserBuilds);
849
1043
  const overrides = doc.getIn(["overrides"]);
1044
+ pruneYamlMapLegacyWrapperAliases(overrides);
1045
+ if (overrides instanceof import_dist.YAMLMap) {
1046
+ const keysSnapshot = overrides.items.map((item) => item.key);
1047
+ for (const keyNode of keysSnapshot) if (shouldDropProviderOverrideKey(keyNode instanceof import_dist.Scalar ? String(keyNode.value ?? "") : String(keyNode ?? ""))) overrides.delete(keyNode);
1048
+ }
850
1049
  for (const key of Object.keys(VITE_PLUS_OVERRIDE_PACKAGES)) {
851
1050
  const version = getCatalogDependencySpec(getYamlMapScalarStringValue(overrides, key), VITE_PLUS_OVERRIDE_PACKAGES[key], true);
852
1051
  doc.setIn(["overrides", scalarString(key)], scalarString(version));
@@ -874,7 +1073,8 @@ function rewritePnpmWorkspaceYaml(projectPath) {
874
1073
  "oxlint-tsgolint",
875
1074
  "@oxlint-tsgolint/*",
876
1075
  "oxfmt",
877
- "@oxfmt/*"
1076
+ "@oxfmt/*",
1077
+ ...VITEST_AGE_GATE_EXEMPT_PACKAGES
878
1078
  ];
879
1079
  let minimumReleaseAgeExclude = doc.getIn(["minimumReleaseAgeExclude"]);
880
1080
  if (!minimumReleaseAgeExclude) minimumReleaseAgeExclude = new import_dist.YAMLSeq();
@@ -890,9 +1090,10 @@ function rewritePnpmWorkspaceYaml(projectPath) {
890
1090
  * moved to pnpm-workspace.yaml.
891
1091
  */
892
1092
  function cleanupPnpmOverridesForWorkspaceYaml(pkg, overrideKeys) {
1093
+ dropRemovePackageOverrideKeys(pkg.pnpm?.overrides);
893
1094
  const catalogOverrides = {};
894
1095
  const overrides = pkg.pnpm?.overrides;
895
- for (const key of [...overrideKeys, ...REMOVE_PACKAGES]) {
1096
+ for (const key of [...overrideKeys, ...PROVIDER_OVERRIDE_DROP_NAMES]) {
896
1097
  const value = overrides?.[key];
897
1098
  if (value) {
898
1099
  if (overrideKeys.includes(key) && value.startsWith("catalog:")) catalogOverrides[key] = value;
@@ -925,6 +1126,65 @@ function migratePnpmOverridesToWorkspaceYaml(projectPath, overrides) {
925
1126
  for (const [key, value] of Object.entries(overrides)) doc.setIn(["overrides", scalarString(key)], scalarString(value));
926
1127
  });
927
1128
  }
1129
+ function hasOwnWebdriverioDependency(pkg) {
1130
+ for (const name of WEBDRIVERIO_ALLOW_SIGNAL_DEPS) if (pkg.dependencies?.[name] ?? pkg.devDependencies?.[name] ?? pkg.optionalDependencies?.[name] ?? pkg.peerDependencies?.[name]) return true;
1131
+ return false;
1132
+ }
1133
+ function workspaceUsesWebdriverio(rootDir, packages) {
1134
+ const rootPkg = readPackageJsonIfExists(path.join(rootDir, "package.json"));
1135
+ if (rootPkg && hasOwnWebdriverioDependency(rootPkg)) return true;
1136
+ if (usesWebdriverioProvider(rootDir)) return true;
1137
+ if (!packages) return false;
1138
+ for (const pkg of packages) {
1139
+ const packageDir = path.join(rootDir, pkg.path);
1140
+ const subPkg = readPackageJsonIfExists(path.join(packageDir, "package.json"));
1141
+ if (subPkg && hasOwnWebdriverioDependency(subPkg)) return true;
1142
+ if (usesWebdriverioProvider(packageDir)) return true;
1143
+ }
1144
+ return false;
1145
+ }
1146
+ function readPackageJsonIfExists(packageJsonPath) {
1147
+ if (!fs.existsSync(packageJsonPath)) return;
1148
+ try {
1149
+ return readJsonFile(packageJsonPath);
1150
+ } catch {
1151
+ return;
1152
+ }
1153
+ }
1154
+ function pnpmMajor(version) {
1155
+ const coerced = version ? import_semver.default.coerce(version)?.version : void 0;
1156
+ return coerced ? import_semver.default.major(coerced) : void 0;
1157
+ }
1158
+ function applyBuildAllowanceToPackageJsonPnpm(pnpm, major, shouldAllow) {
1159
+ if (major >= 10) {
1160
+ if (shouldAllow) for (const name of BROWSER_PROVIDER_POSTINSTALL_PACKAGES) (pnpm.allowBuilds ??= {})[name] = true;
1161
+ } else if (shouldAllow) {
1162
+ const list = pnpm.onlyBuiltDependencies ?? [];
1163
+ const existing = new Set(list);
1164
+ for (const name of BROWSER_PROVIDER_POSTINSTALL_PACKAGES) if (!existing.has(name)) {
1165
+ list.push(name);
1166
+ existing.add(name);
1167
+ }
1168
+ pnpm.onlyBuiltDependencies = list;
1169
+ }
1170
+ }
1171
+ function applyBuildAllowanceToWorkspaceYaml(doc, major, shouldAllow) {
1172
+ if (major >= 10) {
1173
+ if (shouldAllow) {
1174
+ const existing = doc.getIn(["allowBuilds"]);
1175
+ const isNew = !(existing instanceof import_dist.YAMLMap);
1176
+ const allowBuilds = isNew ? new import_dist.YAMLMap() : existing;
1177
+ for (const name of BROWSER_PROVIDER_POSTINSTALL_PACKAGES) allowBuilds.set(scalarString(name), new import_dist.Scalar(true));
1178
+ if (isNew) doc.setIn(["allowBuilds"], allowBuilds);
1179
+ }
1180
+ } else if (shouldAllow) {
1181
+ let onlyBuiltDependencies = doc.getIn(["onlyBuiltDependencies"]);
1182
+ if (!(onlyBuiltDependencies instanceof import_dist.YAMLSeq)) onlyBuiltDependencies = new import_dist.YAMLSeq();
1183
+ const existing = new Set(onlyBuiltDependencies.items.map((n) => n.value));
1184
+ for (const name of BROWSER_PROVIDER_POSTINSTALL_PACKAGES) if (!existing.has(name)) onlyBuiltDependencies.add(scalarString(name));
1185
+ doc.setIn(["onlyBuiltDependencies"], onlyBuiltDependencies);
1186
+ }
1187
+ }
928
1188
  /**
929
1189
  * Remove only Vite-managed entries from peerDependencyRules, preserving custom ones.
930
1190
  */
@@ -943,11 +1203,78 @@ function cleanupPeerDependencyRules(peerDependencyRules, overrideKeys) {
943
1203
  * Rewrite .yarnrc.yml to add vite-plus dependencies
944
1204
  * @param projectPath - The path to the project
945
1205
  */
1206
+ function readYarnrcValue(dir, key) {
1207
+ const yarnrcYmlPath = path.join(dir, ".yarnrc.yml");
1208
+ if (!fs.existsSync(yarnrcYmlPath)) return;
1209
+ try {
1210
+ const value = readYamlFile(yarnrcYmlPath)?.[key];
1211
+ return typeof value === "string" ? value : void 0;
1212
+ } catch {
1213
+ return;
1214
+ }
1215
+ }
1216
+ function resolveEffectiveYarnConfigValue(workspaceRootDir, key, envVar) {
1217
+ const fromEnv = process.env[envVar]?.trim();
1218
+ if (fromEnv) return fromEnv;
1219
+ let dir = path.resolve(workspaceRootDir);
1220
+ for (;;) {
1221
+ const value = readYarnrcValue(dir, key);
1222
+ if (value !== void 0) return value;
1223
+ const parent = path.dirname(dir);
1224
+ if (parent === dir) break;
1225
+ dir = parent;
1226
+ }
1227
+ const home = os.homedir();
1228
+ return home ? readYarnrcValue(home, key) : void 0;
1229
+ }
1230
+ function dirIsWorkspaceRoot(dir) {
1231
+ const pkgJsonPath = path.join(dir, "package.json");
1232
+ if (!fs.existsSync(pkgJsonPath)) return false;
1233
+ try {
1234
+ return readJsonFile(pkgJsonPath).workspaces != null;
1235
+ } catch {
1236
+ return false;
1237
+ }
1238
+ }
1239
+ function findYarnWorkspaceHoisting(startDir) {
1240
+ let dir = path.resolve(startDir);
1241
+ for (;;) {
1242
+ if (dirIsWorkspaceRoot(dir)) return {
1243
+ rootDir: dir,
1244
+ limit: resolveEffectiveYarnConfigValue(dir, "nmHoistingLimits", "YARN_NM_HOISTING_LIMITS"),
1245
+ nodeLinker: resolveEffectiveYarnConfigValue(dir, "nodeLinker", "YARN_NODE_LINKER")
1246
+ };
1247
+ const parent = path.dirname(dir);
1248
+ if (parent === dir) return;
1249
+ dir = parent;
1250
+ }
1251
+ }
1252
+ function setYarnWorkspaceHoistingOptOut(pkg) {
1253
+ if (pkg.installConfig?.hoistingLimits !== void 0) return;
1254
+ pkg.installConfig = {
1255
+ ...pkg.installConfig,
1256
+ hoistingLimits: "none"
1257
+ };
1258
+ }
1259
+ function applyYarnWorkspaceHoistingFix(pkg, rootLimit, nodeLinker, workspaceLabel, report) {
1260
+ if (nodeLinker !== "node-modules") return;
1261
+ if (rootLimit === "workspaces" && pkg.installConfig?.hoistingLimits === void 0) {
1262
+ setYarnWorkspaceHoistingOptOut(pkg);
1263
+ return;
1264
+ }
1265
+ const explicit = pkg.installConfig?.hoistingLimits;
1266
+ if (rootLimit === "dependencies" || explicit === "workspaces" || explicit === "dependencies") warnMigration(`Yarn workspace "${workspaceLabel}" isolates dependency hoisting (hoistingLimits: ${explicit ?? rootLimit}), so it keeps its own \`vitest\`/\`vite-plus\` copy and \`vp test\` may crash with a split \`@vitest/runner\`. Dedupe them to a single copy — relax this workspace's hoisting isolation or pin one \`vitest\` for the workspace.`, report);
1267
+ }
946
1268
  function rewriteYarnrcYml(projectPath) {
947
1269
  const yarnrcYmlPath = path.join(projectPath, ".yarnrc.yml");
948
1270
  if (!fs.existsSync(yarnrcYmlPath)) fs.writeFileSync(yarnrcYmlPath, "");
949
1271
  editYamlFile(yarnrcYmlPath, (doc) => {
950
1272
  if (!doc.has("nodeLinker")) doc.set("nodeLinker", "node-modules");
1273
+ let npmPreapprovedPackages = doc.getIn(["npmPreapprovedPackages"]);
1274
+ if (!npmPreapprovedPackages) npmPreapprovedPackages = new import_dist.YAMLSeq();
1275
+ const existingPreapproved = new Set(npmPreapprovedPackages.items.map((n) => n.value));
1276
+ for (const pkg of VITEST_AGE_GATE_EXEMPT_PACKAGES) if (!existingPreapproved.has(pkg)) npmPreapprovedPackages.add(scalarString(pkg));
1277
+ doc.setIn(["npmPreapprovedPackages"], npmPreapprovedPackages);
951
1278
  rewriteCatalog(doc);
952
1279
  });
953
1280
  }
@@ -989,17 +1316,15 @@ function createCatalogDependencyResolver(projectPath, packageManager) {
989
1316
  if (!fs.existsSync(packageJsonPath)) return;
990
1317
  const pkg = readJsonFile(packageJsonPath);
991
1318
  const workspacesObj = pkg.workspaces && !Array.isArray(pkg.workspaces) ? pkg.workspaces : void 0;
992
- return (catalogSpec, dependencyName) => {
993
- const catalogName = catalogSpec.slice(8);
994
- if (catalogName) return workspacesObj?.catalogs?.[catalogName]?.[dependencyName] ?? pkg.catalogs?.[catalogName]?.[dependencyName];
995
- return workspacesObj?.catalog?.[dependencyName] ?? pkg.catalog?.[dependencyName];
996
- };
1319
+ const fromWorkspaces = createCatalogDependencyResolverFromCatalogs(workspacesObj?.catalog, workspacesObj?.catalogs);
1320
+ const fromPkg = createCatalogDependencyResolverFromCatalogs(pkg.catalog, pkg.catalogs);
1321
+ return (catalogSpec, dependencyName) => fromWorkspaces(catalogSpec, dependencyName) ?? fromPkg(catalogSpec, dependencyName);
997
1322
  }
998
1323
  }
999
1324
  function createCatalogDependencyResolverFromCatalogs(catalog, catalogs) {
1000
1325
  return (catalogSpec, dependencyName) => {
1001
1326
  const catalogName = catalogSpec.slice(8);
1002
- if (catalogName) return catalogs?.[catalogName]?.[dependencyName];
1327
+ if (catalogName && catalogName !== "default") return catalogs?.[catalogName]?.[dependencyName];
1003
1328
  return catalog?.[dependencyName];
1004
1329
  };
1005
1330
  }
@@ -1007,6 +1332,19 @@ function getYamlMapScalarStringValue(map, key) {
1007
1332
  if (!(map instanceof import_dist.YAMLMap)) return;
1008
1333
  for (const item of map.items) if (item.key instanceof import_dist.Scalar && item.key.value === key && item.value instanceof import_dist.Scalar && typeof item.value.value === "string") return item.value.value;
1009
1334
  }
1335
+ function pruneYamlMapLegacyWrapperAliases(map) {
1336
+ if (!(map instanceof import_dist.YAMLMap)) return;
1337
+ const stale = [];
1338
+ for (const item of map.items) {
1339
+ const value = item.value instanceof import_dist.Scalar ? item.value.value : void 0;
1340
+ if (typeof value === "string" && isLegacyWrapperSpec(value) && item.key instanceof import_dist.Scalar) stale.push({
1341
+ key: item.key,
1342
+ fallback: LEGACY_WRAPPER_FALLBACK_VERSIONS[item.key.value]
1343
+ });
1344
+ }
1345
+ for (const { key, fallback } of stale) if (fallback !== void 0) map.set(key, scalarString(fallback));
1346
+ else map.delete(key);
1347
+ }
1010
1348
  function rewriteCatalog(doc) {
1011
1349
  for (const [key, value] of Object.entries(VITE_PLUS_OVERRIDE_PACKAGES)) {
1012
1350
  if (value.startsWith("file:")) continue;
@@ -1017,6 +1355,7 @@ function rewriteCatalog(doc) {
1017
1355
  const path = ["catalog", name];
1018
1356
  if (doc.hasIn(path)) doc.deleteIn(path);
1019
1357
  }
1358
+ pruneYamlMapLegacyWrapperAliases(doc.getIn(["catalog"]));
1020
1359
  const catalogs = doc.getIn(["catalogs"]);
1021
1360
  if (!(catalogs instanceof import_dist.YAMLMap)) return;
1022
1361
  for (const item of catalogs.items) {
@@ -1044,6 +1383,7 @@ function rewriteCatalog(doc) {
1044
1383
  ];
1045
1384
  if (doc.hasIn(catalogPath)) doc.deleteIn(catalogPath);
1046
1385
  }
1386
+ pruneYamlMapLegacyWrapperAliases(item.value);
1047
1387
  }
1048
1388
  }
1049
1389
  function rewriteCatalogObject(catalog, addMissing) {
@@ -1058,6 +1398,34 @@ function rewriteCatalogsObject(catalogs) {
1058
1398
  for (const catalog of Object.values(catalogs)) rewriteCatalogObject(catalog, false);
1059
1399
  }
1060
1400
  /**
1401
+ * Bun rejects vitest@4.1.9's `vite^6/^7/^8` peer-dep when the user's project
1402
+ * overrides `vite` to `@voidzero-dev/vite-plus-core` (whose package.json version
1403
+ * does not match those ranges). pnpm/yarn/npm all tolerate this redirect; bun
1404
+ * does not, and there is no `peerDependencyRules`-style escape hatch — only the
1405
+ * `[install] peer = false` setting in `bunfig.toml`.
1406
+ *
1407
+ * `vite-plus`/`@voidzero-dev/vite-plus-core` already provide the vite surface
1408
+ * the user needs, so disabling bun's auto-install of *missing* peers is safe in
1409
+ * this configuration: any vitest peer that's not already pulled in transitively
1410
+ * (jsdom, happy-dom, etc.) is marked optional upstream anyway.
1411
+ *
1412
+ * Writes/merges `bunfig.toml` at `projectPath` so the suppression applies on
1413
+ * the migration's reinstall AND every subsequent `bun install` the user runs.
1414
+ */
1415
+ function ensureBunfigPeerSuppression(projectPath) {
1416
+ const bunfigPath = path.join(projectPath, "bunfig.toml");
1417
+ const block = "[install]\npeer = false\n";
1418
+ if (!fs.existsSync(bunfigPath)) {
1419
+ fs.writeFileSync(bunfigPath, block);
1420
+ return;
1421
+ }
1422
+ const existing = fs.readFileSync(bunfigPath, "utf8");
1423
+ if (/^\s*peer\s*=\s*(true|false)\s*$/m.test(existing)) return;
1424
+ const installSectionRe = /^\[install\][^[]*/m;
1425
+ const next = installSectionRe.test(existing) ? existing.replace(installSectionRe, (section) => `${section.trimEnd()}\npeer = false\n`) : `${existing.trimEnd()}\n\n${block}`;
1426
+ fs.writeFileSync(bunfigPath, next);
1427
+ }
1428
+ /**
1061
1429
  * Write catalog entries to root package.json for bun.
1062
1430
  * Bun stores catalogs in package.json under the `catalog` key,
1063
1431
  * unlike pnpm which uses pnpm-workspace.yaml.
@@ -1071,30 +1439,54 @@ function rewriteBunCatalog(projectPath) {
1071
1439
  const useWorkspacesCatalog = workspacesObj?.catalog != null || pkg.catalog == null && workspacesObj?.catalogs != null;
1072
1440
  const catalog = { ...useWorkspacesCatalog ? workspacesObj?.catalog : pkg.catalog };
1073
1441
  rewriteCatalogObject(catalog, true);
1442
+ pruneLegacyWrapperAliases(catalog);
1074
1443
  if (useWorkspacesCatalog) {
1075
1444
  workspacesObj.catalog = catalog;
1076
- if (pkg.catalog) rewriteCatalogObject(pkg.catalog, false);
1445
+ if (pkg.catalog) {
1446
+ rewriteCatalogObject(pkg.catalog, false);
1447
+ pruneLegacyWrapperAliases(pkg.catalog);
1448
+ }
1077
1449
  } else {
1078
1450
  pkg.catalog = catalog;
1079
- if (workspacesObj?.catalog) rewriteCatalogObject(workspacesObj.catalog, false);
1451
+ if (workspacesObj?.catalog) {
1452
+ rewriteCatalogObject(workspacesObj.catalog, false);
1453
+ pruneLegacyWrapperAliases(workspacesObj.catalog);
1454
+ }
1455
+ }
1456
+ if (workspacesObj?.catalogs) {
1457
+ rewriteCatalogsObject(workspacesObj.catalogs);
1458
+ for (const named of Object.values(workspacesObj.catalogs)) pruneLegacyWrapperAliases(named);
1459
+ }
1460
+ if (pkg.catalogs) {
1461
+ rewriteCatalogsObject(pkg.catalogs);
1462
+ for (const named of Object.values(pkg.catalogs)) pruneLegacyWrapperAliases(named);
1080
1463
  }
1081
- if (workspacesObj?.catalogs) rewriteCatalogsObject(workspacesObj.catalogs);
1082
- if (pkg.catalogs) rewriteCatalogsObject(pkg.catalogs);
1083
1464
  const overrides = { ...pkg.overrides };
1084
- for (const [key, value] of Object.entries(VITE_PLUS_OVERRIDE_PACKAGES)) overrides[key] = getCatalogDependencySpec(overrides[key], value, true);
1465
+ pruneLegacyWrapperAliases(overrides);
1466
+ for (const [key, value] of Object.entries(VITE_PLUS_OVERRIDE_PACKAGES)) {
1467
+ const current = overrides[key];
1468
+ if (current !== void 0 && typeof current !== "string") continue;
1469
+ overrides[key] = getCatalogDependencySpec(current, value, true);
1470
+ }
1085
1471
  pkg.overrides = overrides;
1086
1472
  return pkg;
1087
1473
  });
1474
+ ensureBunfigPeerSuppression(projectPath);
1088
1475
  }
1089
1476
  /**
1090
1477
  * Rewrite root workspace package.json to add vite-plus dependencies
1091
1478
  * @param projectPath - The path to the project
1092
1479
  */
1093
- function rewriteRootWorkspacePackageJson(projectPath, packageManager, skipStagedMigration, catalogDependencyResolver, packages) {
1480
+ function rewriteRootWorkspacePackageJson(projectPath, packageManager, skipStagedMigration, catalogDependencyResolver, packages, pnpmMajorVersion, shouldAllowBrowserBuilds = false) {
1094
1481
  const packageJsonPath = path.join(projectPath, "package.json");
1095
1482
  if (!fs.existsSync(packageJsonPath)) return;
1096
1483
  let remainingPnpmOverrides;
1097
1484
  editJsonFile(packageJsonPath, (pkg) => {
1485
+ pruneLegacyWrapperAliases(pkg.resolutions);
1486
+ pruneLegacyWrapperAliases(pkg.overrides);
1487
+ pruneLegacyWrapperAliases(pkg.pnpm?.overrides);
1488
+ dropRemovePackageOverrideKeys(pkg.resolutions);
1489
+ dropRemovePackageOverrideKeys(pkg.overrides);
1098
1490
  if (packageManager === PackageManager.yarn) pkg.resolutions = {
1099
1491
  ...pkg.resolutions,
1100
1492
  ...VITE_PLUS_OVERRIDE_PACKAGES
@@ -1103,24 +1495,31 @@ function rewriteRootWorkspacePackageJson(projectPath, packageManager, skipStaged
1103
1495
  ...pkg.overrides,
1104
1496
  ...VITE_PLUS_OVERRIDE_PACKAGES
1105
1497
  };
1106
- else if (packageManager === PackageManager.bun) {} else if (packageManager === PackageManager.pnpm) {
1498
+ else if (packageManager === PackageManager.bun) pkg.devDependencies = {
1499
+ ...pkg.devDependencies,
1500
+ vite: getCatalogDependencySpec(pkg.devDependencies?.vite, VITE_PLUS_OVERRIDE_PACKAGES.vite, true)
1501
+ };
1502
+ else if (packageManager === PackageManager.pnpm) {
1107
1503
  const overrideKeys = Object.keys(VITE_PLUS_OVERRIDE_PACKAGES);
1108
- if (isForceOverrideMode()) pkg.pnpm = {
1109
- ...pkg.pnpm,
1110
- overrides: {
1111
- ...pkg.pnpm?.overrides,
1112
- ...VITE_PLUS_OVERRIDE_PACKAGES,
1113
- [VITE_PLUS_NAME]: VITE_PLUS_VERSION
1114
- }
1115
- };
1116
- else {
1117
- for (const key of [...overrideKeys, ...REMOVE_PACKAGES]) if (pkg.resolutions?.[key]) delete pkg.resolutions[key];
1504
+ if (isForceOverrideMode()) {
1505
+ dropRemovePackageOverrideKeys(pkg.pnpm?.overrides);
1506
+ pkg.pnpm = {
1507
+ ...pkg.pnpm,
1508
+ overrides: {
1509
+ ...pkg.pnpm?.overrides,
1510
+ ...VITE_PLUS_OVERRIDE_PACKAGES,
1511
+ [VITE_PLUS_NAME]: VITE_PLUS_VERSION
1512
+ }
1513
+ };
1514
+ } else {
1515
+ for (const key of [...overrideKeys, ...PROVIDER_OVERRIDE_DROP_NAMES]) if (pkg.resolutions?.[key]) delete pkg.resolutions[key];
1118
1516
  remainingPnpmOverrides = cleanupPnpmOverridesForWorkspaceYaml(pkg, overrideKeys);
1119
1517
  }
1120
1518
  for (const key in pkg.pnpm?.overrides) if (key.includes(">")) {
1121
1519
  const splits = key.split(">");
1122
1520
  if (splits[splits.length - 1].trim() === "vite") delete pkg.pnpm.overrides[key];
1123
1521
  }
1522
+ if (pnpmMajorVersion !== void 0 && pkg.pnpm) applyBuildAllowanceToPackageJsonPnpm(pkg.pnpm, pnpmMajorVersion, shouldAllowBrowserBuilds);
1124
1523
  }
1125
1524
  if (!pkg.devDependencies?.["vite-plus"]) pkg.devDependencies = {
1126
1525
  ...pkg.devDependencies,
@@ -1132,7 +1531,7 @@ function rewriteRootWorkspacePackageJson(projectPath, packageManager, skipStaged
1132
1531
  rewriteMonorepoProject(projectPath, packageManager, skipStagedMigration, void 0, void 0, catalogDependencyResolver, packages ? {
1133
1532
  rootDir: projectPath,
1134
1533
  packages
1135
- } : void 0);
1534
+ } : void 0, true);
1136
1535
  }
1137
1536
  const RULES_YAML_PATH = path.join(rulesDir, "vite-tools.yml");
1138
1537
  const PREPARE_RULES_YAML_PATH = path.join(rulesDir, "vite-prepare.yml");
@@ -1153,7 +1552,401 @@ function readPrepareRulesYaml() {
1153
1552
  cachedPrepareRulesYaml ??= fs.readFileSync(PREPARE_RULES_YAML_PATH, "utf8");
1154
1553
  return cachedPrepareRulesYaml;
1155
1554
  }
1156
- function rewritePackageJson(pkg, packageManager, isMonorepo, skipStagedMigration, catalogDependencyResolver) {
1555
+ function getCoreMigrationProjectPaths(workspaceInfo) {
1556
+ return [workspaceInfo.rootDir, ...(workspaceInfo.packages ?? []).map((pkg) => path.join(workspaceInfo.rootDir, pkg.path))];
1557
+ }
1558
+ function hasCorePackageScriptRewrites(projectPath) {
1559
+ const packageJsonPath = path.join(projectPath, "package.json");
1560
+ if (!fs.existsSync(packageJsonPath)) return false;
1561
+ const pkg = readJsonFile(packageJsonPath);
1562
+ if (!pkg.scripts) return false;
1563
+ return !!rewriteScripts(JSON.stringify(pkg.scripts), getScriptRulesYaml(true));
1564
+ }
1565
+ function rewriteCorePackageScripts(projectPath) {
1566
+ const packageJsonPath = path.join(projectPath, "package.json");
1567
+ if (!fs.existsSync(packageJsonPath)) return false;
1568
+ let changed = false;
1569
+ editJsonFile(packageJsonPath, (pkg) => {
1570
+ if (!pkg.scripts) return;
1571
+ const updated = rewriteScripts(JSON.stringify(pkg.scripts), getScriptRulesYaml(true));
1572
+ if (!updated) return;
1573
+ pkg.scripts = JSON.parse(updated);
1574
+ changed = true;
1575
+ return pkg;
1576
+ });
1577
+ return changed;
1578
+ }
1579
+ function detectPendingCoreMigration(workspaceInfo) {
1580
+ const projectPaths = getCoreMigrationProjectPaths(workspaceInfo);
1581
+ return {
1582
+ scripts: projectPaths.some((projectPath) => hasCorePackageScriptRewrites(projectPath)),
1583
+ tsconfigTypes: projectPaths.some((projectPath) => hasTsconfigTypesToRewrite(projectPath))
1584
+ };
1585
+ }
1586
+ function finalizeCoreMigrationForExistingVitePlus(workspaceInfo, silent = false, report, pending = detectPendingCoreMigration(workspaceInfo)) {
1587
+ const projectPaths = getCoreMigrationProjectPaths(workspaceInfo);
1588
+ const result = {
1589
+ scripts: false,
1590
+ tsconfigTypes: false,
1591
+ imports: false
1592
+ };
1593
+ if (pending.scripts) for (const projectPath of projectPaths) result.scripts = rewriteCorePackageScripts(projectPath) || result.scripts;
1594
+ if (pending.tsconfigTypes) for (const projectPath of projectPaths) result.tsconfigTypes = rewriteTsconfigTypes(projectPath, silent, report) || result.tsconfigTypes;
1595
+ result.imports = rewriteAllImports(workspaceInfo.rootDir, silent, report);
1596
+ return result;
1597
+ }
1598
+ function getVitePlusOverridePackageName(dependencyName) {
1599
+ if (dependencyName === "vite") return "@voidzero-dev/vite-plus-core";
1600
+ if (dependencyName === "vitest") return "@voidzero-dev/vite-plus-test";
1601
+ }
1602
+ function isSemanticVitePlusOverrideSpec(dependencyName, spec) {
1603
+ if (!spec) return false;
1604
+ if (isLegacyWrapperSpec(spec)) return false;
1605
+ if (spec === VITE_PLUS_OVERRIDE_PACKAGES[dependencyName]) return true;
1606
+ const packageName = getVitePlusOverridePackageName(dependencyName);
1607
+ return packageName !== void 0 && spec.includes(packageName);
1608
+ }
1609
+ function overrideSpecSatisfiesVitePlus(dependencyName, spec, catalogDependencyResolver) {
1610
+ if (!spec) return false;
1611
+ if (isSemanticVitePlusOverrideSpec(dependencyName, spec)) return true;
1612
+ if (!spec.startsWith("catalog:")) return false;
1613
+ return isSemanticVitePlusOverrideSpec(dependencyName, catalogDependencyResolver?.(spec, dependencyName));
1614
+ }
1615
+ function overridesSatisfyVitePlus(overrides, catalogDependencyResolver) {
1616
+ return Object.keys(VITE_PLUS_OVERRIDE_PACKAGES).every((dependencyName) => overrideSpecSatisfiesVitePlus(dependencyName, overrides?.[dependencyName], catalogDependencyResolver));
1617
+ }
1618
+ function hasPackageManagerPin(pkg) {
1619
+ return Boolean(pkg.packageManager || pkg.devEngines?.packageManager);
1620
+ }
1621
+ function vitePlusDependencyNeedsConcreteVersion(pkg) {
1622
+ return [
1623
+ pkg.devDependencies,
1624
+ pkg.dependencies,
1625
+ pkg.optionalDependencies
1626
+ ].some((dependencies) => dependencies?.["vite-plus"]?.startsWith("catalog:") ?? false);
1627
+ }
1628
+ function defaultCatalogVitePlusDependencyPending(pkg, catalogDependencyResolver) {
1629
+ return [
1630
+ pkg.devDependencies,
1631
+ pkg.dependencies,
1632
+ pkg.optionalDependencies
1633
+ ].some((dependencies) => {
1634
+ const spec = dependencies?.[VITE_PLUS_NAME];
1635
+ if (spec !== "catalog:" && spec !== "catalog:default") return false;
1636
+ return catalogDependencyResolver?.(spec, VITE_PLUS_NAME) !== VITE_PLUS_VERSION;
1637
+ });
1638
+ }
1639
+ function pnpmPeerDependencyRulesSatisfyVitePlus(peerDependencyRules) {
1640
+ const overrideKeys = Object.keys(VITE_PLUS_OVERRIDE_PACKAGES);
1641
+ const allowAny = new Set(peerDependencyRules?.allowAny ?? []);
1642
+ const allowedVersions = peerDependencyRules?.allowedVersions ?? {};
1643
+ return overrideKeys.every((key) => allowAny.has(key) && allowedVersions[key] === "*");
1644
+ }
1645
+ function npmVitePlusManagedDependenciesPending(pkg) {
1646
+ const dependencyGroups = [
1647
+ pkg.devDependencies,
1648
+ pkg.dependencies,
1649
+ pkg.optionalDependencies
1650
+ ];
1651
+ return Object.keys(VITE_PLUS_OVERRIDE_PACKAGES).some((dependencyName) => dependencyGroups.some((dependencies) => dependencies?.[dependencyName] !== void 0 && !overrideSpecSatisfiesVitePlus(dependencyName, dependencies[dependencyName])));
1652
+ }
1653
+ function readPnpmWorkspaceCatalogDependencyResolver(projectPath) {
1654
+ const pnpmWorkspaceYamlPath = path.join(projectPath, "pnpm-workspace.yaml");
1655
+ if (!fs.existsSync(pnpmWorkspaceYamlPath)) return;
1656
+ const doc = readYamlFile(pnpmWorkspaceYamlPath);
1657
+ return createCatalogDependencyResolverFromCatalogs(doc?.catalog, doc?.catalogs);
1658
+ }
1659
+ function readPnpmWorkspaceOverrides(projectPath) {
1660
+ const pnpmWorkspaceYamlPath = path.join(projectPath, "pnpm-workspace.yaml");
1661
+ if (!fs.existsSync(pnpmWorkspaceYamlPath)) return;
1662
+ return readYamlFile(pnpmWorkspaceYamlPath)?.overrides;
1663
+ }
1664
+ function readPnpmWorkspacePeerDependencyRules(projectPath) {
1665
+ const pnpmWorkspaceYamlPath = path.join(projectPath, "pnpm-workspace.yaml");
1666
+ if (!fs.existsSync(pnpmWorkspaceYamlPath)) return;
1667
+ return readYamlFile(pnpmWorkspaceYamlPath)?.peerDependencyRules;
1668
+ }
1669
+ function yarnrcSatisfiesVitePlus(projectPath) {
1670
+ const yarnrcYmlPath = path.join(projectPath, ".yarnrc.yml");
1671
+ if (!fs.existsSync(yarnrcYmlPath)) return false;
1672
+ const doc = readYamlFile(yarnrcYmlPath);
1673
+ return !!doc && Object.hasOwn(doc, "nodeLinker") && overridesSatisfyVitePlus(doc.catalog) && (VITE_PLUS_VERSION.startsWith("file:") || doc.catalog?.["vite-plus"] === VITE_PLUS_VERSION);
1674
+ }
1675
+ function ensurePnpmWorkspacePackages(projectPath, workspacePatterns) {
1676
+ if (workspacePatterns.length === 0) return false;
1677
+ const pnpmWorkspaceYamlPath = path.join(projectPath, "pnpm-workspace.yaml");
1678
+ let changed = false;
1679
+ editYamlFile(pnpmWorkspaceYamlPath, (doc) => {
1680
+ if (doc.has("packages")) return;
1681
+ const packages = new import_dist.YAMLSeq();
1682
+ for (const pattern of workspacePatterns) packages.add(scalarString(pattern));
1683
+ doc.set("packages", packages);
1684
+ changed = true;
1685
+ });
1686
+ return changed;
1687
+ }
1688
+ function readBunCatalogDependencyResolver(pkg) {
1689
+ const workspacesObj = pkg.workspaces && !Array.isArray(pkg.workspaces) ? pkg.workspaces : {};
1690
+ const fromWorkspaces = createCatalogDependencyResolverFromCatalogs(workspacesObj.catalog, workspacesObj.catalogs);
1691
+ const fromPkg = createCatalogDependencyResolverFromCatalogs(pkg.catalog, pkg.catalogs);
1692
+ return (catalogSpec, dependencyName) => fromWorkspaces(catalogSpec, dependencyName) ?? fromPkg(catalogSpec, dependencyName);
1693
+ }
1694
+ function detectVitePlusBootstrapPending(projectPath, packageManager) {
1695
+ const packageJsonPath = path.join(projectPath, "package.json");
1696
+ if (!fs.existsSync(packageJsonPath)) return false;
1697
+ const pkg = readJsonFile(packageJsonPath);
1698
+ if (!pkg.devDependencies?.["vite-plus"] || !hasPackageManagerPin(pkg)) return true;
1699
+ if (packageManager === void 0) return true;
1700
+ if (packageManager === PackageManager.yarn) return !overridesSatisfyVitePlus(pkg.resolutions) || !yarnrcSatisfiesVitePlus(projectPath);
1701
+ if (packageManager === PackageManager.npm) return vitePlusDependencyNeedsConcreteVersion(pkg) || !overridesSatisfyVitePlus(pkg.overrides) || npmVitePlusManagedDependenciesPending(pkg);
1702
+ if (packageManager === PackageManager.bun) return !overridesSatisfyVitePlus(pkg.overrides, readBunCatalogDependencyResolver(pkg));
1703
+ if (packageManager === PackageManager.pnpm) {
1704
+ if (pkg.pnpm) return vitePlusDependencyNeedsConcreteVersion(pkg) || !overridesSatisfyVitePlus(pkg.pnpm.overrides) || !pnpmPeerDependencyRulesSatisfyVitePlus(pkg.pnpm.peerDependencyRules);
1705
+ const resolver = readPnpmWorkspaceCatalogDependencyResolver(projectPath);
1706
+ return defaultCatalogVitePlusDependencyPending(pkg, resolver) || !overridesSatisfyVitePlus(readPnpmWorkspaceOverrides(projectPath), resolver) || !pnpmPeerDependencyRulesSatisfyVitePlus(readPnpmWorkspacePeerDependencyRules(projectPath));
1707
+ }
1708
+ return false;
1709
+ }
1710
+ function ensureVitePlusDependencySpecs(pkg, version) {
1711
+ let changed = false;
1712
+ if (version !== "catalog:") {
1713
+ const dependencyGroups = [
1714
+ pkg.devDependencies,
1715
+ pkg.dependencies,
1716
+ pkg.optionalDependencies
1717
+ ];
1718
+ for (const dependencies of dependencyGroups) if (dependencies?.["vite-plus"]?.startsWith("catalog:")) {
1719
+ dependencies[VITE_PLUS_NAME] = version;
1720
+ changed = true;
1721
+ }
1722
+ }
1723
+ if (pkg.devDependencies?.["vite-plus"]) return changed;
1724
+ pkg.devDependencies = {
1725
+ ...pkg.devDependencies,
1726
+ [VITE_PLUS_NAME]: version
1727
+ };
1728
+ return true;
1729
+ }
1730
+ function ensureOverrideEntries(overrides, catalogDependencyResolver) {
1731
+ const next = { ...overrides };
1732
+ let changed = false;
1733
+ for (const [dependencyName, overrideSpec] of Object.entries(VITE_PLUS_OVERRIDE_PACKAGES)) if (!overrideSpecSatisfiesVitePlus(dependencyName, next[dependencyName], catalogDependencyResolver)) {
1734
+ next[dependencyName] = overrideSpec;
1735
+ changed = true;
1736
+ }
1737
+ return {
1738
+ overrides: next,
1739
+ changed
1740
+ };
1741
+ }
1742
+ function ensureNpmVitePlusManagedDependencies(pkg) {
1743
+ let changed = false;
1744
+ const dependencyGroups = [
1745
+ pkg.devDependencies,
1746
+ pkg.dependencies,
1747
+ pkg.optionalDependencies
1748
+ ];
1749
+ for (const [dependencyName, version] of Object.entries(VITE_PLUS_OVERRIDE_PACKAGES)) for (const dependencies of dependencyGroups) if (dependencies?.[dependencyName] !== void 0 && !overrideSpecSatisfiesVitePlus(dependencyName, dependencies[dependencyName])) {
1750
+ dependencies[dependencyName] = version;
1751
+ changed = true;
1752
+ }
1753
+ return changed;
1754
+ }
1755
+ function ensurePnpmPeerDependencyRules(pkg) {
1756
+ const overrideKeys = Object.keys(VITE_PLUS_OVERRIDE_PACKAGES);
1757
+ pkg.pnpm ??= {};
1758
+ const peerDependencyRules = {
1759
+ ...pkg.pnpm.peerDependencyRules,
1760
+ allowAny: [...new Set([...pkg.pnpm.peerDependencyRules?.allowAny ?? [], ...overrideKeys])],
1761
+ allowedVersions: {
1762
+ ...pkg.pnpm.peerDependencyRules?.allowedVersions,
1763
+ ...Object.fromEntries(overrideKeys.map((key) => [key, "*"]))
1764
+ }
1765
+ };
1766
+ const changed = JSON.stringify(pkg.pnpm.peerDependencyRules ?? {}) !== JSON.stringify(peerDependencyRules);
1767
+ pkg.pnpm.peerDependencyRules = peerDependencyRules;
1768
+ return changed;
1769
+ }
1770
+ function ensureVitePlusBootstrap(workspaceInfo, report) {
1771
+ const projectPath = workspaceInfo.rootDir;
1772
+ const packageJsonPath = path.join(projectPath, "package.json");
1773
+ const result = {
1774
+ changed: false,
1775
+ packageJson: false,
1776
+ packageManagerConfig: false,
1777
+ packageManagerField: false
1778
+ };
1779
+ if (!fs.existsSync(packageJsonPath)) return result;
1780
+ editJsonFile(packageJsonPath, (pkg) => {
1781
+ const usePnpmWorkspaceYaml = workspaceInfo.packageManager === PackageManager.pnpm && !pkg.pnpm;
1782
+ let packageJsonChanged = ensureVitePlusDependencySpecs(pkg, !VITE_PLUS_VERSION.startsWith("file:") && (usePnpmWorkspaceYaml || workspaceInfo.packageManager === PackageManager.bun) ? "catalog:" : VITE_PLUS_VERSION);
1783
+ if (workspaceInfo.packageManager === PackageManager.npm) packageJsonChanged = ensureNpmVitePlusManagedDependencies(pkg) || packageJsonChanged;
1784
+ if (workspaceInfo.packageManager === PackageManager.yarn) {
1785
+ const ensured = ensureOverrideEntries(pkg.resolutions);
1786
+ if (ensured.changed) {
1787
+ pkg.resolutions = ensured.overrides;
1788
+ packageJsonChanged = true;
1789
+ }
1790
+ } else if (workspaceInfo.packageManager === PackageManager.npm) {
1791
+ const ensured = ensureOverrideEntries(pkg.overrides);
1792
+ if (ensured.changed) {
1793
+ pkg.overrides = ensured.overrides;
1794
+ packageJsonChanged = true;
1795
+ }
1796
+ } else if (workspaceInfo.packageManager === PackageManager.bun) {
1797
+ const ensured = ensureOverrideEntries(pkg.overrides, readBunCatalogDependencyResolver(pkg));
1798
+ if (ensured.changed) {
1799
+ pkg.overrides = ensured.overrides;
1800
+ packageJsonChanged = true;
1801
+ }
1802
+ } else if (workspaceInfo.packageManager === PackageManager.pnpm && pkg.pnpm) {
1803
+ const ensured = ensureOverrideEntries(pkg.pnpm.overrides);
1804
+ if (ensured.changed) {
1805
+ pkg.pnpm.overrides = ensured.overrides;
1806
+ packageJsonChanged = true;
1807
+ }
1808
+ packageJsonChanged = ensurePnpmPeerDependencyRules(pkg) || packageJsonChanged;
1809
+ }
1810
+ result.packageJson = packageJsonChanged;
1811
+ return pkg;
1812
+ });
1813
+ if (workspaceInfo.packageManager === PackageManager.pnpm) {
1814
+ const pkg = readJsonFile(packageJsonPath);
1815
+ if (!pkg.pnpm) {
1816
+ const pnpmWorkspaceYamlPath = path.join(projectPath, "pnpm-workspace.yaml");
1817
+ const before = fs.existsSync(pnpmWorkspaceYamlPath) ? fs.readFileSync(pnpmWorkspaceYamlPath, "utf-8") : void 0;
1818
+ const catalogDependencyResolver = readPnpmWorkspaceCatalogDependencyResolver(projectPath);
1819
+ if (defaultCatalogVitePlusDependencyPending(pkg, catalogDependencyResolver) || !overridesSatisfyVitePlus(readPnpmWorkspaceOverrides(projectPath), catalogDependencyResolver) || !pnpmPeerDependencyRulesSatisfyVitePlus(readPnpmWorkspacePeerDependencyRules(projectPath))) rewritePnpmWorkspaceYaml(projectPath, void 0, false);
1820
+ if (fs.existsSync(pnpmWorkspaceYamlPath)) ensurePnpmWorkspacePackages(projectPath, workspaceInfo.workspacePatterns);
1821
+ result.packageManagerConfig = before !== (fs.existsSync(pnpmWorkspaceYamlPath) ? fs.readFileSync(pnpmWorkspaceYamlPath, "utf-8") : void 0);
1822
+ }
1823
+ } else if (workspaceInfo.packageManager === PackageManager.yarn) {
1824
+ const yarnrcYmlPath = path.join(projectPath, ".yarnrc.yml");
1825
+ const before = fs.existsSync(yarnrcYmlPath) ? fs.readFileSync(yarnrcYmlPath, "utf-8") : void 0;
1826
+ rewriteYarnrcYml(projectPath);
1827
+ result.packageManagerConfig = before !== fs.readFileSync(yarnrcYmlPath, "utf-8");
1828
+ } else if (workspaceInfo.packageManager === PackageManager.bun) {
1829
+ const before = fs.readFileSync(packageJsonPath, "utf-8");
1830
+ rewriteBunCatalog(projectPath);
1831
+ const after = fs.readFileSync(packageJsonPath, "utf-8");
1832
+ result.packageJson = result.packageJson || before !== after;
1833
+ }
1834
+ const beforePackageManager = fs.readFileSync(packageJsonPath, "utf-8");
1835
+ setPackageManager(projectPath, workspaceInfo.downloadPackageManager);
1836
+ result.packageManagerField = beforePackageManager !== fs.readFileSync(packageJsonPath, "utf-8");
1837
+ result.changed = result.packageJson || result.packageManagerConfig || result.packageManagerField;
1838
+ if (result.changed && report) report.packageManagerBootstrapConfigured = true;
1839
+ return result;
1840
+ }
1841
+ const VITEST_BROWSER_SPECIFIER_HINTS = [
1842
+ "@vitest/browser",
1843
+ "vite-plus/test/browser",
1844
+ "vite-plus/test/plugins/browser",
1845
+ "vite-plus/test/internal/browser",
1846
+ "vite-plus/test/client",
1847
+ "vite-plus/test/context",
1848
+ "vite-plus/test/locators",
1849
+ "vite-plus/test/matchers",
1850
+ "vite-plus/test/utils"
1851
+ ];
1852
+ const WEBDRIVERIO_PROVIDER_SPECIFIER_HINTS = [
1853
+ "@vitest/browser-webdriverio",
1854
+ "vite-plus/test/browser-webdriverio",
1855
+ "vite-plus/test/browser/providers/webdriverio",
1856
+ "vite-plus/test/plugins/browser-webdriverio"
1857
+ ];
1858
+ const PLAYWRIGHT_PROVIDER_SPECIFIER_HINTS = [
1859
+ "@vitest/browser-playwright",
1860
+ "vite-plus/test/browser-playwright",
1861
+ "vite-plus/test/browser/providers/playwright",
1862
+ "vite-plus/test/plugins/browser-playwright"
1863
+ ];
1864
+ const BROWSER_PROVIDER_SPECIFIER_HINTS = {
1865
+ [WEBDRIVERIO_PROVIDER]: WEBDRIVERIO_PROVIDER_SPECIFIER_HINTS,
1866
+ [PLAYWRIGHT_PROVIDER]: PLAYWRIGHT_PROVIDER_SPECIFIER_HINTS
1867
+ };
1868
+ const VITEST_SCAN_EXTENSIONS = new Set([
1869
+ ".ts",
1870
+ ".mts",
1871
+ ".cts",
1872
+ ".tsx",
1873
+ ".js",
1874
+ ".mjs",
1875
+ ".cjs",
1876
+ ".jsx"
1877
+ ]);
1878
+ const VITEST_SCAN_SKIP_DIRS = new Set([
1879
+ "node_modules",
1880
+ "dist",
1881
+ "build",
1882
+ "out",
1883
+ "coverage",
1884
+ ".git",
1885
+ ".next",
1886
+ ".nuxt",
1887
+ ".svelte-kit",
1888
+ ".vite",
1889
+ ".cache"
1890
+ ]);
1891
+ /**
1892
+ * Detect whether a package uses vitest's browser mode.
1893
+ *
1894
+ * Upstream `@vitest/browser` injects `optimizeDeps.include` entries of the form
1895
+ * `vitest > expect-type` (and `vitest > @vitest/snapshot > magic-string`,
1896
+ * `vitest > @vitest/expect > chai`). Vite resolves the leading `vitest` segment
1897
+ * from the Vite config root, so `vitest` MUST be resolvable as a package from
1898
+ * the consuming package's directory. In a pnpm strict (non-hoisted) layout,
1899
+ * `vitest` pulled in only transitively via `vite-plus` is NOT reachable from the
1900
+ * package root — the optimizer then fails with `Failed to resolve dependency`
1901
+ * and the browser test page hangs forever.
1902
+ *
1903
+ * When this returns true the migration adds `vitest` as a direct
1904
+ * devDependency so it is hoisted next to the package and the optimizer chain
1905
+ * resolves. The signal is any of the package's TS/JS files (config, workspace
1906
+ * config under any name, or test file) referencing `@vitest/browser*` or
1907
+ * `vite-plus/test/browser*`. The scan recurses through the package directory
1908
+ * (skipping `node_modules`, build output, VCS metadata) so browser config in a
1909
+ * non-standard filename or browser imports in test files are all caught.
1910
+ *
1911
+ * Recursion stops at nested `package.json` boundaries: a workspace sub-package
1912
+ * is a separate package that the migration scans on its own pass, so the root
1913
+ * package must not inherit a browser-mode signal from a sub-package.
1914
+ */
1915
+ function sourceTreeReferencesAny(projectPath, hints) {
1916
+ const matchesHint = (content) => hints.some((hint) => content.includes(hint));
1917
+ const scanDir = (dir, isRoot) => {
1918
+ let entries;
1919
+ try {
1920
+ entries = fs.readdirSync(dir, { withFileTypes: true });
1921
+ } catch {
1922
+ return false;
1923
+ }
1924
+ if (!isRoot && entries.some((e) => e.isFile() && e.name === "package.json")) return false;
1925
+ for (const entry of entries) {
1926
+ const entryPath = path.join(dir, entry.name);
1927
+ if (entry.isDirectory()) {
1928
+ if (VITEST_SCAN_SKIP_DIRS.has(entry.name)) continue;
1929
+ if (scanDir(entryPath, false)) return true;
1930
+ } else if (entry.isFile() && VITEST_SCAN_EXTENSIONS.has(path.extname(entry.name))) try {
1931
+ if (matchesHint(fs.readFileSync(entryPath, "utf8"))) return true;
1932
+ } catch {}
1933
+ }
1934
+ return false;
1935
+ };
1936
+ return scanDir(projectPath, true);
1937
+ }
1938
+ function usesVitestBrowserMode(projectPath) {
1939
+ return sourceTreeReferencesAny(projectPath, VITEST_BROWSER_SPECIFIER_HINTS);
1940
+ }
1941
+ function usesWebdriverioProvider(projectPath) {
1942
+ return sourceTreeReferencesAny(projectPath, WEBDRIVERIO_PROVIDER_SPECIFIER_HINTS);
1943
+ }
1944
+ function collectProviderSourceModes(projectPath) {
1945
+ const modes = {};
1946
+ for (const provider of OPT_IN_BROWSER_PROVIDERS) modes[provider] = sourceTreeReferencesAny(projectPath, BROWSER_PROVIDER_SPECIFIER_HINTS[provider]);
1947
+ return modes;
1948
+ }
1949
+ function rewritePackageJson(pkg, packageManager, isMonorepo, skipStagedMigration, catalogDependencyResolver, vitestBrowserMode, providerSourceModes) {
1157
1950
  if (pkg.scripts) {
1158
1951
  const updated = rewriteScripts(JSON.stringify(pkg.scripts), getScriptRulesYaml(skipStagedMigration));
1159
1952
  if (updated) pkg.scripts = JSON.parse(updated);
@@ -1184,6 +1977,7 @@ function rewritePackageJson(pkg, packageManager, isMonorepo, skipStagedMigration
1184
1977
  dependencies: pkg.optionalDependencies
1185
1978
  }
1186
1979
  ];
1980
+ for (const { dependencies } of dependencyGroups) if (pruneLegacyWrapperAliases(dependencies)) needVitePlus = true;
1187
1981
  for (const [key, version] of Object.entries(VITE_PLUS_OVERRIDE_PACKAGES)) for (const { dependencyField, dependencies } of dependencyGroups) if (dependencies?.[key]) {
1188
1982
  dependencies[key] = getCatalogDependencySpec(dependencies[key], version, supportCatalog, {
1189
1983
  dependencyField,
@@ -1193,6 +1987,13 @@ function rewritePackageJson(pkg, packageManager, isMonorepo, skipStagedMigration
1193
1987
  });
1194
1988
  needVitePlus = true;
1195
1989
  }
1990
+ if (isForceOverrideMode()) {
1991
+ for (const { dependencies } of dependencyGroups) if (dependencies?.["vite-plus"]) {
1992
+ dependencies[VITE_PLUS_NAME] = VITE_PLUS_VERSION;
1993
+ needVitePlus = true;
1994
+ }
1995
+ }
1996
+ const hasBrowserDepSignal = VITEST_BROWSER_DEP_NAMES.some((name) => dependencyGroups.some(({ dependencies }) => dependencies?.[name] !== void 0));
1196
1997
  for (const name of REMOVE_PACKAGES) {
1197
1998
  let wasRemoved = false;
1198
1999
  for (const { dependencies } of dependencyGroups) if (dependencies?.[name]) {
@@ -1206,23 +2007,63 @@ function rewritePackageJson(pkg, packageManager, isMonorepo, skipStagedMigration
1206
2007
  pkg.devDependencies[peerDep] = "*";
1207
2008
  }
1208
2009
  }
2010
+ let usesAnyOptInProvider = false;
2011
+ for (const provider of OPT_IN_BROWSER_PROVIDERS) {
2012
+ if (!(providerSourceModes?.[provider] || dependencyGroups.some(({ dependencies }) => dependencies?.[provider] !== void 0))) continue;
2013
+ usesAnyOptInProvider = true;
2014
+ const installGroup = [
2015
+ pkg.dependencies,
2016
+ pkg.devDependencies,
2017
+ pkg.optionalDependencies
2018
+ ].find((deps) => deps?.[provider] !== void 0);
2019
+ if (installGroup) installGroup[provider] = VITEST_VERSION;
2020
+ else {
2021
+ pkg.devDependencies ??= {};
2022
+ pkg.devDependencies[provider] = VITEST_VERSION;
2023
+ }
2024
+ const peer = BROWSER_PROVIDER_PEER_DEPS[provider];
2025
+ const peerPresent = pkg.dependencies?.[peer] ?? pkg.devDependencies?.[peer] ?? pkg.peerDependencies?.[peer] ?? pkg.optionalDependencies?.[peer];
2026
+ if (peer && !peerPresent) {
2027
+ pkg.devDependencies ??= {};
2028
+ pkg.devDependencies[peer] = "*";
2029
+ }
2030
+ needVitePlus = true;
2031
+ }
2032
+ if (usesAnyOptInProvider && packageManager === PackageManager.npm) {
2033
+ const viteOverride = VITE_PLUS_OVERRIDE_PACKAGES.vite;
2034
+ const viteAlreadyDirect = pkg.dependencies?.vite ?? pkg.devDependencies?.vite ?? pkg.optionalDependencies?.vite;
2035
+ if (viteOverride && !viteAlreadyDirect) {
2036
+ pkg.devDependencies ??= {};
2037
+ pkg.devDependencies.vite = viteOverride;
2038
+ needVitePlus = true;
2039
+ }
2040
+ }
2041
+ const effectiveBrowserMode = vitestBrowserMode || hasBrowserDepSignal;
2042
+ const installableNames = [
2043
+ ...Object.keys(pkg.dependencies ?? {}),
2044
+ ...Object.keys(pkg.devDependencies ?? {}),
2045
+ ...Object.keys(pkg.optionalDependencies ?? {})
2046
+ ];
2047
+ const adjacentSignals = [...installableNames, ...Object.keys(pkg.peerDependencies ?? {})];
2048
+ const isVitestAdjacent = !installableNames.includes("vitest") && adjacentSignals.some((name) => name !== "vitest" && name.includes("vitest"));
1209
2049
  const canonicalVitePlusSpec = supportCatalog && !VITE_PLUS_VERSION.startsWith("file:") ? "catalog:" : VITE_PLUS_VERSION;
1210
2050
  const existingVitePlus = pkg.devDependencies?.[VITE_PLUS_NAME];
1211
2051
  const shouldNormalizeExistingVitePlus = !!existingVitePlus && supportCatalog && existingVitePlus !== canonicalVitePlusSpec && !isProtocolPinnedSpec(existingVitePlus);
2052
+ if (!existingVitePlus && (isVitestAdjacent || effectiveBrowserMode)) needVitePlus = true;
2053
+ const needDirectVitest = needVitePlus || effectiveBrowserMode || isVitestAdjacent;
1212
2054
  if (needVitePlus || shouldNormalizeExistingVitePlus) pkg.devDependencies = {
1213
2055
  ...pkg.devDependencies,
1214
2056
  [VITE_PLUS_NAME]: canonicalVitePlusSpec
1215
2057
  };
1216
- if (needVitePlus) {
2058
+ if (needDirectVitest) {
1217
2059
  const installableDeps = {
1218
2060
  ...pkg.dependencies,
1219
2061
  ...pkg.devDependencies,
1220
2062
  ...pkg.optionalDependencies
1221
2063
  };
1222
- if (!installableDeps.vitest && Object.keys(installableDeps).some((name) => name.includes("vitest"))) {
1223
- const ver = VITE_PLUS_OVERRIDE_PACKAGES.vitest;
2064
+ if (!installableDeps.vitest && (effectiveBrowserMode || Object.keys(installableDeps).some((name) => name.includes("vitest")))) {
1224
2065
  pkg.devDependencies ??= {};
1225
- pkg.devDependencies.vitest = getCatalogDependencySpec(void 0, ver, supportCatalog);
2066
+ pkg.devDependencies.vitest = getCatalogDependencySpec(void 0, VITEST_VERSION, supportCatalog);
1226
2067
  }
1227
2068
  }
1228
2069
  return extractedStagedConfig;
@@ -1590,6 +2431,20 @@ function hasStagedConfigInViteConfig(projectPath) {
1590
2431
  return /\bstaged\s*:/.test(content);
1591
2432
  }
1592
2433
  /**
2434
+ * Wrap safe inline Vite plugin arrays with lazyPlugins so check/lint/fmt do not
2435
+ * eagerly execute plugin factories while loading vite.config.ts.
2436
+ */
2437
+ function wrapLazyPluginsInViteConfig(projectPath, silent = false, report) {
2438
+ const configs = detectConfigs(projectPath);
2439
+ if (!configs.viteConfig) return;
2440
+ const viteConfigPath = path.join(projectPath, configs.viteConfig);
2441
+ const result = wrapLazyPlugins(viteConfigPath);
2442
+ if (!result.updated) return;
2443
+ fs.writeFileSync(viteConfigPath, result.content);
2444
+ if (report) report.wrappedPluginConfigCount++;
2445
+ if (!silent) log.success(`✔ Wrapped inline Vite plugins with lazyPlugins in ${displayRelative(viteConfigPath)}`);
2446
+ }
2447
+ /**
1593
2448
  * Rewrite imports in all TypeScript/JavaScript files under a directory
1594
2449
  * This rewrites vite/vitest imports to @voidzero-dev/vite-plus
1595
2450
  * @param projectPath - The root directory to search for files
@@ -1614,19 +2469,28 @@ function rewriteAllImports(projectPath, silent = false, report) {
1614
2469
  log.warn(`⚠ ${errors === 1 ? "one file had an error" : `${errors} files had errors`}:`);
1615
2470
  for (const error of result.errors) log.error(` ${displayRelative(error.path)}: ${error.message}`);
1616
2471
  }
2472
+ return modified > 0;
1617
2473
  }
1618
2474
  /**
1619
2475
  * Check if the project has an unsupported husky version (<9.0.0).
1620
2476
  * Uses `semver.coerce` to handle ranges like `^8.0.0` → `8.0.0`.
1621
- * When the specifier is not coercible (e.g. `"latest"`), falls back to
1622
- * the installed version in node_modules via `detectPackageMetadata`.
2477
+ * When the specifier is a catalog reference (e.g. `"catalog:"`), resolves
2478
+ * it from the active package manager's catalog first — a `catalog:` spec is
2479
+ * only meaningful to the manager that owns the workspace, so we never read a
2480
+ * leftover/foreign catalog file. When it is still not coercible (e.g.
2481
+ * `"latest"`), falls back to the installed version in node_modules via
2482
+ * `detectPackageMetadata`.
1623
2483
  * Returns a reason string if hooks migration should be skipped, or null
1624
2484
  * if husky is absent or compatible.
1625
2485
  */
1626
- function checkUnsupportedHuskyVersion(projectPath, deps, prodDeps) {
2486
+ function checkUnsupportedHuskyVersion(projectPath, deps, prodDeps, packageManager) {
1627
2487
  const huskyVersion = deps?.husky ?? prodDeps?.husky;
1628
2488
  if (!huskyVersion) return null;
1629
2489
  let coerced = import_semver.default.coerce(huskyVersion);
2490
+ if (coerced == null && packageManager != null && huskyVersion.startsWith("catalog:")) {
2491
+ const resolved = createCatalogDependencyResolver(projectPath, packageManager)?.(huskyVersion, "husky");
2492
+ if (resolved) coerced = import_semver.default.coerce(resolved);
2493
+ }
1630
2494
  if (coerced == null) {
1631
2495
  const installed = detectPackageMetadata(projectPath, "husky");
1632
2496
  if (installed) coerced = import_semver.default.coerce(installed.version);
@@ -1650,6 +2514,12 @@ function removeReplacedHookPackages(packageJsonPath) {
1650
2514
  return pkg;
1651
2515
  });
1652
2516
  }
2517
+ function detectLegacyGitHooksMigrationCandidate(projectPath) {
2518
+ const packageJsonPath = path.join(projectPath, "package.json");
2519
+ if (!fs.existsSync(packageJsonPath)) return false;
2520
+ const pkg = readJsonFile(packageJsonPath);
2521
+ return getOldHooksDir(projectPath) !== void 0 || pkg["lint-staged"] !== void 0;
2522
+ }
1653
2523
  /**
1654
2524
  * Walk up from `startPath` looking for `.git` (directory or file — submodules
1655
2525
  * use a `.git` file). Returns the directory that contains `.git`, or `null`.
@@ -1674,8 +2544,8 @@ function collapseHuskyInstall(script) {
1674
2544
  * High-level helper: detect old hooks dir, set up git hooks, and rewrite
1675
2545
  * the prepare script. Returns true if hooks were successfully installed.
1676
2546
  */
1677
- function installGitHooks(projectPath, silent = false, report) {
1678
- if (setupGitHooks(projectPath, getOldHooksDir(projectPath), silent, report)) {
2547
+ function installGitHooks(projectPath, silent = false, report, packageManager) {
2548
+ if (setupGitHooks(projectPath, getOldHooksDir(projectPath), silent, report, packageManager)) {
1679
2549
  rewritePrepareScript(projectPath);
1680
2550
  return true;
1681
2551
  }
@@ -1701,8 +2571,11 @@ function getOldHooksDir(rootDir) {
1701
2571
  *
1702
2572
  * These checks are deterministic and read-only — they do not modify
1703
2573
  * the project in any way, making them safe to call before migration.
2574
+ *
2575
+ * `packageManager` is the project's detected manager; it scopes `catalog:`
2576
+ * resolution to that manager's catalog so a foreign catalog file is ignored.
1704
2577
  */
1705
- function preflightGitHooksSetup(projectPath) {
2578
+ function preflightGitHooksSetup(projectPath, packageManager) {
1706
2579
  const gitRoot = findGitRoot(projectPath);
1707
2580
  if (gitRoot && path.resolve(projectPath) !== path.resolve(gitRoot)) return "Subdirectory project detected — skipping git hooks setup. Configure hooks at the repository root.";
1708
2581
  const packageJsonPath = path.join(projectPath, "package.json");
@@ -1711,7 +2584,7 @@ function preflightGitHooksSetup(projectPath) {
1711
2584
  const deps = pkgContent.devDependencies;
1712
2585
  const prodDeps = pkgContent.dependencies;
1713
2586
  for (const tool of OTHER_HOOK_TOOLS) if (deps?.[tool] || prodDeps?.[tool] || pkgContent[tool]) return `Detected ${tool} — skipping git hooks setup. Please configure git hooks manually.`;
1714
- const huskyReason = checkUnsupportedHuskyVersion(projectPath, deps, prodDeps);
2587
+ const huskyReason = checkUnsupportedHuskyVersion(projectPath, deps, prodDeps, packageManager);
1715
2588
  if (huskyReason) return huskyReason;
1716
2589
  if (hasUnsupportedLintStagedConfig(projectPath)) return "Unsupported lint-staged config format — skipping git hooks setup. Please configure git hooks manually.";
1717
2590
  return null;
@@ -1721,8 +2594,8 @@ function preflightGitHooksSetup(projectPath) {
1721
2594
  * Skips if another hook tool is detected (warns user).
1722
2595
  * Returns true if hooks were successfully set up, false if skipped.
1723
2596
  */
1724
- function setupGitHooks(projectPath, oldHooksDir, silent = false, report) {
1725
- const reason = preflightGitHooksSetup(projectPath);
2597
+ function setupGitHooks(projectPath, oldHooksDir, silent = false, report, packageManager) {
2598
+ const reason = preflightGitHooksSetup(projectPath, packageManager);
1726
2599
  if (reason) {
1727
2600
  warnMigration(reason, report);
1728
2601
  return false;
@@ -1792,10 +2665,10 @@ function setupGitHooks(projectPath, oldHooksDir, silent = false, report) {
1792
2665
  const vpBin = process.env.VP_CLI_BIN ?? "vp";
1793
2666
  const configArgs = isCustomDir ? [
1794
2667
  "config",
1795
- "--hooks-only",
2668
+ "--no-agent",
1796
2669
  "--hooks-dir",
1797
2670
  hooksDir
1798
- ] : ["config", "--hooks-only"];
2671
+ ] : ["config", "--no-agent"];
1799
2672
  const configResult = import_cross_spawn.default.sync(vpBin, configArgs, {
1800
2673
  cwd: projectPath,
1801
2674
  stdio: "pipe"
@@ -1913,7 +2786,14 @@ function rewritePrepareScript(rootDir) {
1913
2786
  }
1914
2787
  function setPackageManager(projectDir, downloadPackageManager) {
1915
2788
  editJsonFile(path.join(projectDir, "package.json"), (pkg) => {
1916
- if (!pkg.packageManager) pkg.packageManager = `${downloadPackageManager.name}@${downloadPackageManager.version}`;
2789
+ if (!pkg.packageManager && !pkg.devEngines?.packageManager) pkg.devEngines = {
2790
+ ...typeof pkg.devEngines === "object" && pkg.devEngines !== null && !Array.isArray(pkg.devEngines) ? pkg.devEngines : void 0,
2791
+ packageManager: {
2792
+ name: downloadPackageManager.name,
2793
+ version: downloadPackageManager.version,
2794
+ onFail: "download"
2795
+ }
2796
+ };
1917
2797
  return pkg;
1918
2798
  });
1919
2799
  }
@@ -2023,7 +2903,7 @@ async function confirmEslintMigration(interactive) {
2023
2903
  message: "Migrate ESLint rules to Oxlint using @oxlint/migrate?\n " + styleText("gray", "Oxlint is Vite+'s built-in linter — significantly faster than ESLint with compatible rule support. @oxlint/migrate converts your existing rules automatically."),
2024
2904
  initialValue: true
2025
2905
  });
2026
- if (q(confirmed)) cancelAndExit();
2906
+ if (R(confirmed)) cancelAndExit();
2027
2907
  return confirmed;
2028
2908
  }
2029
2909
  return true;
@@ -2057,7 +2937,7 @@ async function confirmPrettierMigration(interactive) {
2057
2937
  message: "Migrate Prettier to Oxfmt?\n " + styleText("gray", "Oxfmt is Vite+'s built-in formatter that replaces Prettier with faster performance. Your configuration will be converted automatically."),
2058
2938
  initialValue: true
2059
2939
  });
2060
- if (q(confirmed)) cancelAndExit();
2940
+ if (R(confirmed)) cancelAndExit();
2061
2941
  return confirmed;
2062
2942
  }
2063
2943
  log.info("Prettier configuration detected. Auto-migrating to Oxfmt...");
@@ -2189,7 +3069,7 @@ async function selectAgentTargets({ interactive, agent, onCancel }) {
2189
3069
  initialValues: [AGENT_DEFAULT_ID],
2190
3070
  required: false
2191
3071
  });
2192
- if (q(selectedAgentIds)) {
3072
+ if (R(selectedAgentIds)) {
2193
3073
  onCancel();
2194
3074
  return {
2195
3075
  targetPaths: void 0,
@@ -2376,7 +3256,7 @@ async function writeAgentInstructions({ projectRoot, targetPath, targetPaths, in
2376
3256
  }],
2377
3257
  initialValue: "skip"
2378
3258
  });
2379
- conflictAction = q(action) || action === "skip" ? "skip" : "append";
3259
+ conflictAction = R(action) || action === "skip" ? "skip" : "append";
2380
3260
  } else conflictAction = "skip";
2381
3261
  if (conflictAction === "append") await appendAgentContent(destinationPath, targetPathToWrite, existingContent, incomingContent, silent);
2382
3262
  else {
@@ -2458,4 +3338,4 @@ function getMarkedRange(content, startMarker, endMarker) {
2458
3338
  };
2459
3339
  }
2460
3340
  //#endregion
2461
- export { promptEslintMigration as A, editYamlFile as B, injectLintTypeCheckDefaults as C, migrateNodeVersionManagerFile as D, migrateEslintToOxlint as E, setPackageManager as F, displayRelative as H, warnIncompatibleEslintIntegration as I, warnLegacyEslintConfig as L, rewriteMonorepo as M, rewriteMonorepoProject as N, migratePrettierToOxfmt as O, rewriteStandaloneProject as P, warnPackageLevelEslint as R, injectCreateDefaultTemplate as S, mergeViteConfigFiles as T, templatesDir as U, readYamlFile as V, detectNodeVersionManagerFile as _, selectAgentTargets as a, hasFrameworkShim as b, writeCopilotSetupWorkflow as c, checkVitestVersion as d, confirmEslintMigration as f, detectIncompatibleEslintIntegration as g, detectFramework as h, selectAgentTargetPaths as i, promptPrettierMigration as j, preflightGitHooksSetup as k, addFrameworkShim as l, detectEslintProject as m, detectAgentConflicts as n, updateExistingAgentInstructions as o, confirmPrettierMigration as p, detectExistingAgentTargetPaths as r, writeAgentInstructions as s, COPILOT_AGENT_ID as t, checkViteVersion as u, detectPrettierProject as v, installGitHooks as w, hasStagedConfigInViteConfig as x, ensurePreCommitHook as y, warnPackageLevelPrettier as z };
3341
+ export { mergeViteConfigFiles as A, setPackageManager as B, ensureVitePlusBootstrap as C, injectCreateDefaultTemplate as D, hasStagedConfigInViteConfig as E, promptEslintMigration as F, editYamlFile as G, warnLegacyEslintConfig as H, promptPrettierMigration as I, templatesDir as J, readYamlFile as K, rewriteMonorepo as L, migrateNodeVersionManagerFile as M, migratePrettierToOxfmt as N, injectLintTypeCheckDefaults as O, preflightGitHooksSetup as P, rewriteMonorepoProject as R, ensurePreCommitHook as S, hasFrameworkShim as T, warnPackageLevelEslint as U, warnIncompatibleEslintIntegration as V, warnPackageLevelPrettier as W, detectLegacyGitHooksMigrationCandidate as _, selectAgentTargets as a, detectPrettierProject as b, writeCopilotSetupWorkflow as c, checkVitestVersion as d, confirmEslintMigration as f, detectIncompatibleEslintIntegration as g, detectFramework as h, selectAgentTargetPaths as i, migrateEslintToOxlint as j, installGitHooks as k, addFrameworkShim as l, detectEslintProject as m, detectAgentConflicts as n, updateExistingAgentInstructions as o, confirmPrettierMigration as p, displayRelative as q, detectExistingAgentTargetPaths as r, writeAgentInstructions as s, COPILOT_AGENT_ID as t, checkViteVersion as u, detectNodeVersionManagerFile as v, finalizeCoreMigrationForExistingVitePlus as w, detectVitePlusBootstrapPending as x, detectPendingCoreMigration as y, rewriteStandaloneProject as z };