vite-plus 0.1.24 → 0.2.1

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-Nuk-9l77.js → agent--cKmgD_n.js} +941 -70
  8. package/dist/bin.js +22 -30
  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 +6 -0
  13. package/dist/create/bin.js +508 -232
  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 +292 -76
  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 +5 -5
  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-DFb5BKyT.js → tsconfig-BWQPmGKz.js} +565 -231
  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 +6 -6
  149. package/dist/{workspace-NL-m9wgM.js → workspace-D0AVy4fu.js} +11 -9
  150. package/docs/_data/team.ts +2 -1
  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 +5 -3
  157. package/docs/guide/install.md +31 -12
  158. package/docs/guide/migrate.md +13 -3
  159. package/docs/guide/troubleshooting.md +2 -2
  160. package/docs/guide/upgrade.md +26 -7
  161. package/docs/package.json +3 -3
  162. package/docs/pnpm-lock.yaml +298 -395
  163. package/package.json +103 -55
  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 R, 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-DFb5BKyT.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-BWQPmGKz.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
  }
@@ -1005,6 +1332,19 @@ function getYamlMapScalarStringValue(map, key) {
1005
1332
  if (!(map instanceof import_dist.YAMLMap)) return;
1006
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;
1007
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
+ }
1008
1348
  function rewriteCatalog(doc) {
1009
1349
  for (const [key, value] of Object.entries(VITE_PLUS_OVERRIDE_PACKAGES)) {
1010
1350
  if (value.startsWith("file:")) continue;
@@ -1015,6 +1355,7 @@ function rewriteCatalog(doc) {
1015
1355
  const path = ["catalog", name];
1016
1356
  if (doc.hasIn(path)) doc.deleteIn(path);
1017
1357
  }
1358
+ pruneYamlMapLegacyWrapperAliases(doc.getIn(["catalog"]));
1018
1359
  const catalogs = doc.getIn(["catalogs"]);
1019
1360
  if (!(catalogs instanceof import_dist.YAMLMap)) return;
1020
1361
  for (const item of catalogs.items) {
@@ -1042,6 +1383,7 @@ function rewriteCatalog(doc) {
1042
1383
  ];
1043
1384
  if (doc.hasIn(catalogPath)) doc.deleteIn(catalogPath);
1044
1385
  }
1386
+ pruneYamlMapLegacyWrapperAliases(item.value);
1045
1387
  }
1046
1388
  }
1047
1389
  function rewriteCatalogObject(catalog, addMissing) {
@@ -1056,6 +1398,34 @@ function rewriteCatalogsObject(catalogs) {
1056
1398
  for (const catalog of Object.values(catalogs)) rewriteCatalogObject(catalog, false);
1057
1399
  }
1058
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
+ /**
1059
1429
  * Write catalog entries to root package.json for bun.
1060
1430
  * Bun stores catalogs in package.json under the `catalog` key,
1061
1431
  * unlike pnpm which uses pnpm-workspace.yaml.
@@ -1069,30 +1439,54 @@ function rewriteBunCatalog(projectPath) {
1069
1439
  const useWorkspacesCatalog = workspacesObj?.catalog != null || pkg.catalog == null && workspacesObj?.catalogs != null;
1070
1440
  const catalog = { ...useWorkspacesCatalog ? workspacesObj?.catalog : pkg.catalog };
1071
1441
  rewriteCatalogObject(catalog, true);
1442
+ pruneLegacyWrapperAliases(catalog);
1072
1443
  if (useWorkspacesCatalog) {
1073
1444
  workspacesObj.catalog = catalog;
1074
- if (pkg.catalog) rewriteCatalogObject(pkg.catalog, false);
1445
+ if (pkg.catalog) {
1446
+ rewriteCatalogObject(pkg.catalog, false);
1447
+ pruneLegacyWrapperAliases(pkg.catalog);
1448
+ }
1075
1449
  } else {
1076
1450
  pkg.catalog = catalog;
1077
- 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);
1078
1463
  }
1079
- if (workspacesObj?.catalogs) rewriteCatalogsObject(workspacesObj.catalogs);
1080
- if (pkg.catalogs) rewriteCatalogsObject(pkg.catalogs);
1081
1464
  const overrides = { ...pkg.overrides };
1082
- 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
+ }
1083
1471
  pkg.overrides = overrides;
1084
1472
  return pkg;
1085
1473
  });
1474
+ ensureBunfigPeerSuppression(projectPath);
1086
1475
  }
1087
1476
  /**
1088
1477
  * Rewrite root workspace package.json to add vite-plus dependencies
1089
1478
  * @param projectPath - The path to the project
1090
1479
  */
1091
- function rewriteRootWorkspacePackageJson(projectPath, packageManager, skipStagedMigration, catalogDependencyResolver, packages) {
1480
+ function rewriteRootWorkspacePackageJson(projectPath, packageManager, skipStagedMigration, catalogDependencyResolver, packages, pnpmMajorVersion, shouldAllowBrowserBuilds = false) {
1092
1481
  const packageJsonPath = path.join(projectPath, "package.json");
1093
1482
  if (!fs.existsSync(packageJsonPath)) return;
1094
1483
  let remainingPnpmOverrides;
1095
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);
1096
1490
  if (packageManager === PackageManager.yarn) pkg.resolutions = {
1097
1491
  ...pkg.resolutions,
1098
1492
  ...VITE_PLUS_OVERRIDE_PACKAGES
@@ -1101,24 +1495,31 @@ function rewriteRootWorkspacePackageJson(projectPath, packageManager, skipStaged
1101
1495
  ...pkg.overrides,
1102
1496
  ...VITE_PLUS_OVERRIDE_PACKAGES
1103
1497
  };
1104
- 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) {
1105
1503
  const overrideKeys = Object.keys(VITE_PLUS_OVERRIDE_PACKAGES);
1106
- if (isForceOverrideMode()) pkg.pnpm = {
1107
- ...pkg.pnpm,
1108
- overrides: {
1109
- ...pkg.pnpm?.overrides,
1110
- ...VITE_PLUS_OVERRIDE_PACKAGES,
1111
- [VITE_PLUS_NAME]: VITE_PLUS_VERSION
1112
- }
1113
- };
1114
- else {
1115
- 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];
1116
1516
  remainingPnpmOverrides = cleanupPnpmOverridesForWorkspaceYaml(pkg, overrideKeys);
1117
1517
  }
1118
1518
  for (const key in pkg.pnpm?.overrides) if (key.includes(">")) {
1119
1519
  const splits = key.split(">");
1120
1520
  if (splits[splits.length - 1].trim() === "vite") delete pkg.pnpm.overrides[key];
1121
1521
  }
1522
+ if (pnpmMajorVersion !== void 0 && pkg.pnpm) applyBuildAllowanceToPackageJsonPnpm(pkg.pnpm, pnpmMajorVersion, shouldAllowBrowserBuilds);
1122
1523
  }
1123
1524
  if (!pkg.devDependencies?.["vite-plus"]) pkg.devDependencies = {
1124
1525
  ...pkg.devDependencies,
@@ -1130,7 +1531,7 @@ function rewriteRootWorkspacePackageJson(projectPath, packageManager, skipStaged
1130
1531
  rewriteMonorepoProject(projectPath, packageManager, skipStagedMigration, void 0, void 0, catalogDependencyResolver, packages ? {
1131
1532
  rootDir: projectPath,
1132
1533
  packages
1133
- } : void 0);
1534
+ } : void 0, true);
1134
1535
  }
1135
1536
  const RULES_YAML_PATH = path.join(rulesDir, "vite-tools.yml");
1136
1537
  const PREPARE_RULES_YAML_PATH = path.join(rulesDir, "vite-prepare.yml");
@@ -1151,7 +1552,401 @@ function readPrepareRulesYaml() {
1151
1552
  cachedPrepareRulesYaml ??= fs.readFileSync(PREPARE_RULES_YAML_PATH, "utf8");
1152
1553
  return cachedPrepareRulesYaml;
1153
1554
  }
1154
- 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) {
1155
1950
  if (pkg.scripts) {
1156
1951
  const updated = rewriteScripts(JSON.stringify(pkg.scripts), getScriptRulesYaml(skipStagedMigration));
1157
1952
  if (updated) pkg.scripts = JSON.parse(updated);
@@ -1182,6 +1977,7 @@ function rewritePackageJson(pkg, packageManager, isMonorepo, skipStagedMigration
1182
1977
  dependencies: pkg.optionalDependencies
1183
1978
  }
1184
1979
  ];
1980
+ for (const { dependencies } of dependencyGroups) if (pruneLegacyWrapperAliases(dependencies)) needVitePlus = true;
1185
1981
  for (const [key, version] of Object.entries(VITE_PLUS_OVERRIDE_PACKAGES)) for (const { dependencyField, dependencies } of dependencyGroups) if (dependencies?.[key]) {
1186
1982
  dependencies[key] = getCatalogDependencySpec(dependencies[key], version, supportCatalog, {
1187
1983
  dependencyField,
@@ -1191,6 +1987,13 @@ function rewritePackageJson(pkg, packageManager, isMonorepo, skipStagedMigration
1191
1987
  });
1192
1988
  needVitePlus = true;
1193
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));
1194
1997
  for (const name of REMOVE_PACKAGES) {
1195
1998
  let wasRemoved = false;
1196
1999
  for (const { dependencies } of dependencyGroups) if (dependencies?.[name]) {
@@ -1204,23 +2007,63 @@ function rewritePackageJson(pkg, packageManager, isMonorepo, skipStagedMigration
1204
2007
  pkg.devDependencies[peerDep] = "*";
1205
2008
  }
1206
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"));
1207
2049
  const canonicalVitePlusSpec = supportCatalog && !VITE_PLUS_VERSION.startsWith("file:") ? "catalog:" : VITE_PLUS_VERSION;
1208
2050
  const existingVitePlus = pkg.devDependencies?.[VITE_PLUS_NAME];
1209
2051
  const shouldNormalizeExistingVitePlus = !!existingVitePlus && supportCatalog && existingVitePlus !== canonicalVitePlusSpec && !isProtocolPinnedSpec(existingVitePlus);
2052
+ if (!existingVitePlus && (isVitestAdjacent || effectiveBrowserMode)) needVitePlus = true;
2053
+ const needDirectVitest = needVitePlus || effectiveBrowserMode || isVitestAdjacent;
1210
2054
  if (needVitePlus || shouldNormalizeExistingVitePlus) pkg.devDependencies = {
1211
2055
  ...pkg.devDependencies,
1212
2056
  [VITE_PLUS_NAME]: canonicalVitePlusSpec
1213
2057
  };
1214
- if (needVitePlus) {
2058
+ if (needDirectVitest) {
1215
2059
  const installableDeps = {
1216
2060
  ...pkg.dependencies,
1217
2061
  ...pkg.devDependencies,
1218
2062
  ...pkg.optionalDependencies
1219
2063
  };
1220
- if (!installableDeps.vitest && Object.keys(installableDeps).some((name) => name.includes("vitest"))) {
1221
- const ver = VITE_PLUS_OVERRIDE_PACKAGES.vitest;
2064
+ if (!installableDeps.vitest && (effectiveBrowserMode || Object.keys(installableDeps).some((name) => name.includes("vitest")))) {
1222
2065
  pkg.devDependencies ??= {};
1223
- pkg.devDependencies.vitest = getCatalogDependencySpec(void 0, ver, supportCatalog);
2066
+ pkg.devDependencies.vitest = getCatalogDependencySpec(void 0, VITEST_VERSION, supportCatalog);
1224
2067
  }
1225
2068
  }
1226
2069
  return extractedStagedConfig;
@@ -1588,6 +2431,20 @@ function hasStagedConfigInViteConfig(projectPath) {
1588
2431
  return /\bstaged\s*:/.test(content);
1589
2432
  }
1590
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
+ /**
1591
2448
  * Rewrite imports in all TypeScript/JavaScript files under a directory
1592
2449
  * This rewrites vite/vitest imports to @voidzero-dev/vite-plus
1593
2450
  * @param projectPath - The root directory to search for files
@@ -1612,6 +2469,7 @@ function rewriteAllImports(projectPath, silent = false, report) {
1612
2469
  log.warn(`⚠ ${errors === 1 ? "one file had an error" : `${errors} files had errors`}:`);
1613
2470
  for (const error of result.errors) log.error(` ${displayRelative(error.path)}: ${error.message}`);
1614
2471
  }
2472
+ return modified > 0;
1615
2473
  }
1616
2474
  /**
1617
2475
  * Check if the project has an unsupported husky version (<9.0.0).
@@ -1656,6 +2514,12 @@ function removeReplacedHookPackages(packageJsonPath) {
1656
2514
  return pkg;
1657
2515
  });
1658
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
+ }
1659
2523
  /**
1660
2524
  * Walk up from `startPath` looking for `.git` (directory or file — submodules
1661
2525
  * use a `.git` file). Returns the directory that contains `.git`, or `null`.
@@ -1801,10 +2665,10 @@ function setupGitHooks(projectPath, oldHooksDir, silent = false, report, package
1801
2665
  const vpBin = process.env.VP_CLI_BIN ?? "vp";
1802
2666
  const configArgs = isCustomDir ? [
1803
2667
  "config",
1804
- "--hooks-only",
2668
+ "--no-agent",
1805
2669
  "--hooks-dir",
1806
2670
  hooksDir
1807
- ] : ["config", "--hooks-only"];
2671
+ ] : ["config", "--no-agent"];
1808
2672
  const configResult = import_cross_spawn.default.sync(vpBin, configArgs, {
1809
2673
  cwd: projectPath,
1810
2674
  stdio: "pipe"
@@ -1922,7 +2786,14 @@ function rewritePrepareScript(rootDir) {
1922
2786
  }
1923
2787
  function setPackageManager(projectDir, downloadPackageManager) {
1924
2788
  editJsonFile(path.join(projectDir, "package.json"), (pkg) => {
1925
- 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
+ };
1926
2797
  return pkg;
1927
2798
  });
1928
2799
  }
@@ -2467,4 +3338,4 @@ function getMarkedRange(content, startMarker, endMarker) {
2467
3338
  };
2468
3339
  }
2469
3340
  //#endregion
2470
- 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 };