vite-plus 0.2.1 → 0.2.3

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 (69) hide show
  1. package/bin/oxfmt +6 -3
  2. package/bin/oxlint +6 -3
  3. package/bin/vpr +9 -0
  4. package/binding/index.cjs +53 -53
  5. package/binding/index.d.cts +86 -144
  6. package/dist/agent-D3nZAMGI.js +9456 -0
  7. package/dist/bin.js +10 -7
  8. package/dist/{cli-truncate-CWsmbK3p.js → cli-truncate-BVOlIIPy.js} +1 -1
  9. package/dist/config/bin.js +2 -2
  10. package/dist/{constants-CrfJQIUX.js → constants-CZ7Mnq-C.js} +7 -3
  11. package/dist/create/bin.js +800 -806
  12. package/dist/{define-config-2tfJoXr1.d.ts → define-config-BuMs_LKa.d.ts} +64 -9
  13. package/dist/{define-config-DJUehepE.js → define-config-D2jo1xVA.js} +43 -18
  14. package/dist/{define-config-BGSjF6Xp.cjs → define-config-Dut6XD42.cjs} +61 -22
  15. package/dist/define-config.cjs +2 -1
  16. package/dist/define-config.d.ts +2 -2
  17. package/dist/define-config.js +2 -2
  18. package/dist/{dist-Oxo16Y0q.js → dist-CKz3vcoG.js} +4 -4
  19. package/dist/dist-CtM2JRbM.js +3 -0
  20. package/dist/editor-D1BZiU1h.js +584 -0
  21. package/dist/index.cjs +1 -1
  22. package/dist/index.d.ts +1 -1
  23. package/dist/index.js +1 -1
  24. package/dist/{json-Dn87fvjk.js → json-DiRs8ceZ.js} +13 -1
  25. package/dist/{log-update-lyIiuflf.js → log-update-DHZRyJ2m.js} +2 -2
  26. package/dist/migration/bin.d.ts +1 -10
  27. package/dist/migration/bin.js +531 -170
  28. package/dist/migration/compat/worker.d.ts +1 -0
  29. package/dist/migration/compat/worker.js +35 -0
  30. package/dist/{oxlint-plugin-config-q8a5PFch.js → oxlint-plugin-config-BSbv66Qn.js} +1 -1
  31. package/dist/oxlint-plugin.js +78 -9
  32. package/dist/pack-bin.js +1 -1
  33. package/dist/{package-BHirM1_v.js → package-Cpo2ei_V.js} +52 -3
  34. package/dist/protocol-D99W10Qi.js +4 -0
  35. package/dist/{report-BHSkWqRR.js → report-BS7ULx7A.js} +2 -0
  36. package/dist/{resolve-vite-config-CmdsfQzS.js → resolve-vite-config-DX2o0SWM.js} +14 -10
  37. package/dist/{rolldown-runtime-DnnnRqeS.js → rolldown-runtime-C7HZzL1F.js} +1 -1
  38. package/dist/staged/bin.js +23 -19
  39. package/dist/{tsconfig-BWQPmGKz.js → tsconfig-D6sTl0dN.js} +439 -208
  40. package/dist/version.js +2 -5
  41. package/dist/versions.js +5 -5
  42. package/dist/{wrap-ansi-k7Dn4VtV.js → wrap-ansi-DNjkuBEp.js} +1 -1
  43. package/dist/wrap-ansi-DUi52C6W.js +2 -0
  44. package/docs/_data/team.ts +9 -0
  45. package/docs/config/check.md +35 -0
  46. package/docs/config/index.md +2 -0
  47. package/docs/config/run.md +61 -12
  48. package/docs/guide/automatic-data-tracking.md +145 -0
  49. package/docs/guide/cache.md +14 -35
  50. package/docs/guide/check.md +16 -0
  51. package/docs/guide/ci.md +4 -0
  52. package/docs/guide/docker.md +175 -0
  53. package/docs/guide/env.md +13 -1
  54. package/docs/guide/github-actions-cache.md +165 -0
  55. package/docs/guide/install.md +12 -2
  56. package/docs/guide/migrate-rules.md +347 -0
  57. package/docs/guide/migrate.md +14 -0
  58. package/docs/guide/pack.md +8 -0
  59. package/docs/guide/run.md +33 -3
  60. package/docs/guide/troubleshooting.md +3 -3
  61. package/docs/guide/upgrade.md +43 -21
  62. package/docs/package.json +3 -2
  63. package/docs/vite.config.ts +17 -0
  64. package/package.json +19 -18
  65. package/dist/agent--cKmgD_n.js +0 -3341
  66. package/dist/compat-Cql3K40m.js +0 -14
  67. package/dist/dist-DRJUd9bL.js +0 -3
  68. package/dist/workspace-D0AVy4fu.js +0 -5830
  69. package/dist/wrap-ansi-CeQuiQ31.js +0 -2
@@ -1,17 +1,400 @@
1
- import { r as __toESM } from "../rolldown-runtime-DnnnRqeS.js";
2
- import { u as isForceOverrideMode } from "../constants-CrfJQIUX.js";
3
- import { A as select, C as require_semver, D as log, P as R, S as PackageManager, T as confirm, c as cancelAndExit, d as promptGitHooks, g as upgradeYarn, h as selectPackageManager, i as hasBaseUrlInTsconfig, j as spinner, k as outro, l as defaultInteractive, m as runViteInstall, r as fixBaseUrlInTsconfig, t as confirmBaseUrlFix, u as downloadPackageManager } from "../tsconfig-BWQPmGKz.js";
1
+ import { r as __toESM } from "../rolldown-runtime-C7HZzL1F.js";
2
+ import { d as isForceOverrideMode, l as VITE_PLUS_VERSION } from "../constants-CZ7Mnq-C.js";
3
+ import { B as runCommandSilently, D as require_semver, E as PackageManager, F as spinner, N as outro, P as select, R as isCancel, _ as upgradeYarn, d as downloadPackageManager, f as promptGitHooks, g as selectPackageManager, h as runViteInstall, i as hasBaseUrlInTsconfig, j as log, k as confirm, l as cancelAndExit, m as runViteFmt, r as fixBaseUrlInTsconfig, t as confirmBaseUrlFix, u as defaultInteractive } from "../tsconfig-D6sTl0dN.js";
4
4
  import { a as printHeader, i as muted, r as log$1, s as warnMsg, t as accent } from "../terminal-uTv0ZaMr.js";
5
+ import { a as writeJsonFile, i as readJsonFile } from "../json-DiRs8ceZ.js";
5
6
  import { t as lib_default } from "../lib-L3DWSRQp.js";
6
- import { a as readNearestPackageJson, i as hasVitePlusDependency } from "../package-BHirM1_v.js";
7
- import { A as mergeViteConfigFiles, C as ensureVitePlusBootstrap, H as warnLegacyEslintConfig, L as rewriteMonorepo, M as migrateNodeVersionManagerFile, N as migratePrettierToOxfmt, O as injectLintTypeCheckDefaults, P as preflightGitHooksSetup, T as hasFrameworkShim, U as warnPackageLevelEslint, V as warnIncompatibleEslintIntegration, W as warnPackageLevelPrettier, _ as detectLegacyGitHooksMigrationCandidate, b as detectPrettierProject, d as checkVitestVersion, f as confirmEslintMigration, g as detectIncompatibleEslintIntegration, h as detectFramework, i as selectAgentTargetPaths, j as migrateEslintToOxlint, k as installGitHooks, l as addFrameworkShim, m as detectEslintProject, n as detectAgentConflicts, p as confirmPrettierMigration, q as displayRelative, r as detectExistingAgentTargetPaths, s as writeAgentInstructions, u as checkViteVersion, v as detectNodeVersionManagerFile, w as finalizeCoreMigrationForExistingVitePlus, x as detectVitePlusBootstrapPending, y as detectPendingCoreMigration, z as rewriteStandaloneProject } from "../agent--cKmgD_n.js";
8
- import { n as addMigrationWarning, r as createMigrationReport } from "../report-BHSkWqRR.js";
9
- import { a as detectEditorConflicts, l as writeEditorConfigs, s as selectEditor, t as detectWorkspace } from "../workspace-D0AVy4fu.js";
7
+ import { a as readNearestPackageJson, i as hasVitePlusDependency } from "../package-Cpo2ei_V.js";
8
+ import { A as detectWorkspace, B as warnIncompatibleEslintIntegration, C as injectLintTypeCheckDefaults, D as ensureVitePlusBootstrap, E as detectVitePlusBootstrapPending, F as detectEslintProject, H as warnPackageLevelEslint, I as detectIncompatibleEslintIntegration, K as checkViteVersion, L as migrateEslintToOxlint, O as configureYarnNodeModulesMode, P as confirmEslintMigration, T as collectToolchainVersionChanges, V as warnLegacyEslintConfig, Y as displayRelative, _ as detectLegacyGitHooksMigrationCandidate, b as preflightGitHooksSetup, d as rewriteStandaloneProject, f as detectPendingCoreMigration, h as migrateNodeVersionManagerFile, i as selectAgentTargetPaths, k as detectYarnPnpMode, l as rewriteMonorepo, m as detectNodeVersionManagerFile, n as detectAgentConflicts, p as finalizeCoreMigrationForExistingVitePlus, q as checkVitestVersion, r as detectExistingAgentTargetPaths, s as writeAgentInstructions, w as mergeViteConfigFiles, y as installGitHooks } from "../agent-D3nZAMGI.js";
9
+ import { n as addMigrationWarning, r as createMigrationReport } from "../report-BS7ULx7A.js";
10
+ import { a as writeEditorConfigs, c as hasFrameworkShim, d as migratePrettierToOxfmt, l as confirmPrettierMigration, o as addFrameworkShim, p as warnPackageLevelPrettier, r as selectEditor, s as detectFramework, t as detectEditorConflicts, u as detectPrettierProject } from "../editor-D1BZiU1h.js";
10
11
  import { t as renderCliDoc } from "../help-YP84FSEz.js";
12
+ import { t as ROLLDOWN_COMPAT_RESULT_PREFIX } from "../protocol-D99W10Qi.js";
11
13
  import path from "node:path";
14
+ import fs, { existsSync } from "node:fs";
12
15
  import { styleText } from "node:util";
13
- //#region src/migration/bin.ts
16
+ import { fileURLToPath } from "node:url";
17
+ //#region src/migration/compat/runner.ts
14
18
  var import_semver = /* @__PURE__ */ __toESM(require_semver(), 1);
19
+ /**
20
+ * Resolve the isolated compat worker emitted at `migration/compat/worker.js`.
21
+ *
22
+ * The worker is a sibling of this module in source (`src/migration/compat/`),
23
+ * so `./worker.js` is the correct relative path. The bundler, however, inlines
24
+ * this runner into the parent `migration/bin.js` entry, which sits one level up
25
+ * from the emitted `migration/compat/worker.js`; there the worker lives in the
26
+ * nested `./compat/` directory. A fixed literal can only satisfy one layout:
27
+ * `./compat/worker.js` doubles to `compat/compat/worker.js` in source, while
28
+ * `./worker.js` misses the bundled worker. Probe for the nested
29
+ * `./compat/worker.js` next to this module to pick the right prefix for
30
+ * whichever layout this code is running in.
31
+ */
32
+ function resolveWorkerPath() {
33
+ const nestedWorker = new URL("./compat/worker.js", import.meta.url);
34
+ if (existsSync(nestedWorker)) return fileURLToPath(nestedWorker);
35
+ return fileURLToPath(new URL("./worker.js", import.meta.url));
36
+ }
37
+ const WORKER_TIMEOUT_MS = 3e4;
38
+ function parseRolldownCompatibilityResult(stdout) {
39
+ const output = stdout.toString();
40
+ const markerIndex = output.lastIndexOf(ROLLDOWN_COMPAT_RESULT_PREFIX);
41
+ if (markerIndex === -1) return;
42
+ const resultStart = markerIndex + ROLLDOWN_COMPAT_RESULT_PREFIX.length;
43
+ const resultEnd = output.indexOf("\n", resultStart);
44
+ const serialized = output.slice(resultStart, resultEnd === -1 ? void 0 : resultEnd).trim();
45
+ try {
46
+ const result = JSON.parse(serialized);
47
+ if (!Array.isArray(result.warnings) || !result.warnings.every((item) => typeof item === "string")) return;
48
+ return { warnings: result.warnings };
49
+ } catch {
50
+ return;
51
+ }
52
+ }
53
+ /**
54
+ * Resolve a project's Vite config in a child process before checking it for
55
+ * Rolldown-incompatible options. Config files execute arbitrary project code;
56
+ * isolating them prevents process-level handlers, explicit exits, and
57
+ * asynchronous crashes from terminating the migration itself.
58
+ */
59
+ async function checkRolldownCompatibility(rootDir, report) {
60
+ try {
61
+ const workerPath = resolveWorkerPath();
62
+ const result = await runCommandSilently({
63
+ command: process.execPath,
64
+ args: [workerPath, rootDir],
65
+ cwd: rootDir,
66
+ envs: process.env,
67
+ timeoutMs: WORKER_TIMEOUT_MS
68
+ });
69
+ if (result.exitCode !== 0) return;
70
+ const compatibilityResult = parseRolldownCompatibilityResult(result.stdout);
71
+ for (const warning of compatibilityResult?.warnings ?? []) addMigrationWarning(report, warning);
72
+ } catch {}
73
+ }
74
+ //#endregion
75
+ //#region src/migration/format.ts
76
+ const FORMAT_FAILURE_MESSAGE = "Automatic formatting failed. Run `vp fmt` manually after migration.";
77
+ const MAX_FORMAT_ARG_BYTES = process.platform === "win32" ? 24e3 : 1e5;
78
+ function chunkPathsByArgLength(paths) {
79
+ const chunks = [];
80
+ let current = [];
81
+ let currentBytes = 0;
82
+ for (const filePath of paths) {
83
+ const bytes = Buffer.byteLength(filePath) + 1;
84
+ if (current.length > 0 && currentBytes + bytes > MAX_FORMAT_ARG_BYTES) {
85
+ chunks.push(current);
86
+ current = [];
87
+ currentBytes = 0;
88
+ }
89
+ current.push(filePath);
90
+ currentBytes += bytes;
91
+ }
92
+ if (current.length > 0) chunks.push(current);
93
+ return chunks;
94
+ }
95
+ function parseNullDelimitedPaths(output) {
96
+ return output.toString().split("\0").filter(Boolean);
97
+ }
98
+ function isExistingFile(projectRoot, relativePath) {
99
+ const absolutePath = path.join(projectRoot, relativePath);
100
+ try {
101
+ return fs.statSync(absolutePath).isFile();
102
+ } catch {
103
+ return false;
104
+ }
105
+ }
106
+ /**
107
+ * Limit automatic formatting to files changed in the current Git worktree.
108
+ * This prevents migration from reformatting unrelated source trees while still
109
+ * covering manifests, generated config, and rewritten imports.
110
+ *
111
+ * Return `undefined` outside a Git worktree so non-Git projects retain the
112
+ * existing full-project formatting behavior.
113
+ */
114
+ async function collectChangedFormatPaths(projectRoot, excludedPaths) {
115
+ try {
116
+ const git = (args) => runCommandSilently({
117
+ command: "git",
118
+ args,
119
+ cwd: projectRoot,
120
+ envs: process.env
121
+ });
122
+ const worktree = await git(["rev-parse", "--is-inside-work-tree"]);
123
+ if (worktree.exitCode !== 0) return worktree.stderr.toString().includes("not a git repository") ? void 0 : [];
124
+ if (worktree.stdout.toString().trim() !== "true") return;
125
+ const [unstaged, staged, untracked] = await Promise.all([
126
+ git([
127
+ "diff",
128
+ "--name-only",
129
+ "--relative",
130
+ "-z",
131
+ "--diff-filter=ACMRTUXB",
132
+ "--",
133
+ "."
134
+ ]),
135
+ git([
136
+ "diff",
137
+ "--cached",
138
+ "--name-only",
139
+ "--relative",
140
+ "-z",
141
+ "--diff-filter=ACMRTUXB",
142
+ "--",
143
+ "."
144
+ ]),
145
+ git([
146
+ "ls-files",
147
+ "--others",
148
+ "--exclude-standard",
149
+ "-z",
150
+ "--",
151
+ "."
152
+ ])
153
+ ]);
154
+ if (unstaged.exitCode !== 0 || staged.exitCode !== 0 || untracked.exitCode !== 0) return [];
155
+ return [.../* @__PURE__ */ new Set([
156
+ ...parseNullDelimitedPaths(unstaged.stdout),
157
+ ...parseNullDelimitedPaths(staged.stdout),
158
+ ...parseNullDelimitedPaths(untracked.stdout)
159
+ ])].filter((file) => !excludedPaths?.has(file) && isExistingFile(projectRoot, file)).toSorted();
160
+ } catch {
161
+ return [];
162
+ }
163
+ }
164
+ /**
165
+ * Do not apply Oxfmt to a project that still uses Prettier. Their formatting
166
+ * rules can conflict, especially when Prettier is enforced through ESLint.
167
+ */
168
+ function canFormatWithOxfmt(hasPrettierDependency, prettierMigrated) {
169
+ return !hasPrettierDependency || prettierMigrated;
170
+ }
171
+ /**
172
+ * Format a successfully migrated project without turning a formatter problem
173
+ * into an unhandled migration failure. The formatter already prints its
174
+ * stdout/stderr when it exits nonzero; the report keeps the manual follow-up
175
+ * visible in the final migration summary.
176
+ */
177
+ async function formatMigratedProject(projectRoot, interactive, report, options = {}) {
178
+ const { format = runViteFmt, collectPaths = collectChangedFormatPaths, excludedPaths } = options;
179
+ try {
180
+ const paths = await collectPaths(projectRoot, excludedPaths);
181
+ if (paths?.length === 0) return true;
182
+ const cliEntry = process.argv[1] ? path.resolve(process.cwd(), process.argv[1]) : void 0;
183
+ const formatOptions = {
184
+ silent: false,
185
+ ...cliEntry ? {
186
+ command: process.execPath,
187
+ commandArgs: [...process.execArgv, cliEntry]
188
+ } : {}
189
+ };
190
+ const batches = paths === void 0 ? [void 0] : chunkPathsByArgLength(paths);
191
+ let allFormatted = true;
192
+ for (const batch of batches) if ((await format(projectRoot, interactive, batch, formatOptions)).status !== "formatted") {
193
+ allFormatted = false;
194
+ break;
195
+ }
196
+ if (allFormatted) return true;
197
+ } catch {}
198
+ addMigrationWarning(report, FORMAT_FAILURE_MESSAGE);
199
+ return false;
200
+ }
201
+ //#endregion
202
+ //#region src/migration/npm-reinstall.ts
203
+ const VITE_PLUS_CORE_PACKAGE = "@voidzero-dev/vite-plus-core";
204
+ function isViteInstallPath(packagePath) {
205
+ return packagePath === "node_modules/vite" || packagePath.endsWith("/node_modules/vite");
206
+ }
207
+ function isVitePlusCorePackage(pkg) {
208
+ return pkg?.name === VITE_PLUS_CORE_PACKAGE || pkg?.resolved?.includes("/@voidzero-dev/vite-plus-core/") === true;
209
+ }
210
+ const STALE_VITE_BACKUP_NAME = ".vite-plus-migrate-stale-vite";
211
+ function removeStaleInstalledVite(packagePath, moved) {
212
+ const packageJsonPath = path.join(packagePath, "package.json");
213
+ if (!fs.existsSync(packageJsonPath)) return false;
214
+ try {
215
+ if (readJsonFile(packageJsonPath).name === VITE_PLUS_CORE_PACKAGE) return false;
216
+ } catch {}
217
+ const backupPath = path.join(path.dirname(packagePath), STALE_VITE_BACKUP_NAME);
218
+ try {
219
+ fs.rmSync(backupPath, {
220
+ recursive: true,
221
+ force: true
222
+ });
223
+ fs.renameSync(packagePath, backupPath);
224
+ moved.push({
225
+ originalPath: packagePath,
226
+ backupPath
227
+ });
228
+ } catch {
229
+ fs.rmSync(packagePath, {
230
+ recursive: true,
231
+ force: true
232
+ });
233
+ }
234
+ return true;
235
+ }
236
+ /**
237
+ * npm does not replace an already-installed package when its dependency changes
238
+ * from `vite` to the `@voidzero-dev/vite-plus-core` npm alias. Even `npm
239
+ * install --force` can exit successfully while retaining the real Vite package
240
+ * and its stale package-lock entry. Move those stale Vite installs aside (and
241
+ * drop their lock entries) before the migration's final install so npm resolves
242
+ * the managed alias afresh; the caller commits or restores them depending on
243
+ * whether the install succeeded, so a failed install does not strand the
244
+ * project without any vite package.
245
+ */
246
+ function prepareNpmViteAliasReinstall(rootDir, projectPaths = [rootDir]) {
247
+ const packageLockPath = path.join(rootDir, "package-lock.json");
248
+ const moved = [];
249
+ let changed = false;
250
+ if (fs.existsSync(packageLockPath)) try {
251
+ const packageLock = readJsonFile(packageLockPath);
252
+ let lockChanged = false;
253
+ for (const [packagePath, pkg] of Object.entries(packageLock.packages ?? {})) {
254
+ if (!isViteInstallPath(packagePath)) continue;
255
+ const installPath = path.resolve(rootDir, packagePath);
256
+ const relativeInstallPath = path.relative(rootDir, installPath);
257
+ if (relativeInstallPath.startsWith("..") || path.isAbsolute(relativeInstallPath)) continue;
258
+ if (!isVitePlusCorePackage(pkg)) {
259
+ delete packageLock.packages?.[packagePath];
260
+ lockChanged = true;
261
+ removeStaleInstalledVite(installPath, moved);
262
+ } else changed = removeStaleInstalledVite(installPath, moved) || changed;
263
+ }
264
+ if (lockChanged) {
265
+ writeJsonFile(packageLockPath, packageLock);
266
+ changed = true;
267
+ }
268
+ } catch {}
269
+ for (const projectPath of projectPaths) changed = removeStaleInstalledVite(path.join(projectPath, "node_modules", "vite"), moved) || changed;
270
+ return {
271
+ changed,
272
+ commit: () => {
273
+ for (const { backupPath } of moved) try {
274
+ fs.rmSync(backupPath, {
275
+ recursive: true,
276
+ force: true
277
+ });
278
+ } catch {}
279
+ },
280
+ restore: () => {
281
+ for (const { originalPath, backupPath } of moved) try {
282
+ fs.rmSync(originalPath, {
283
+ recursive: true,
284
+ force: true
285
+ });
286
+ fs.renameSync(backupPath, originalPath);
287
+ } catch {}
288
+ }
289
+ };
290
+ }
291
+ //#endregion
292
+ //#region src/migration/setup-plan.ts
293
+ async function collectGitHooksDecision(rootDir, packageManager, options) {
294
+ let shouldSetupHooks = await promptGitHooks(options);
295
+ if (shouldSetupHooks) {
296
+ const reason = preflightGitHooksSetup(rootDir, packageManager);
297
+ if (reason) {
298
+ log.warn(`⚠ ${reason}`);
299
+ shouldSetupHooks = false;
300
+ }
301
+ }
302
+ return shouldSetupHooks;
303
+ }
304
+ async function collectAgentInstructionPlan(rootDir, options) {
305
+ const existingAgentTargetPaths = options.agent !== void 0 || !options.interactive ? void 0 : detectExistingAgentTargetPaths(rootDir);
306
+ const selectedAgentTargetPaths = existingAgentTargetPaths !== void 0 ? existingAgentTargetPaths : await selectAgentTargetPaths({
307
+ interactive: options.interactive,
308
+ agent: options.agent,
309
+ onCancel: () => cancelAndExit()
310
+ });
311
+ const agentConflicts = await detectAgentConflicts({
312
+ projectRoot: rootDir,
313
+ targetPaths: selectedAgentTargetPaths
314
+ });
315
+ const agentConflictDecisions = /* @__PURE__ */ new Map();
316
+ for (const conflict of agentConflicts) if (options.interactive) {
317
+ const action = await select({
318
+ message: `Agent instructions already exist at ${conflict.targetPath}.\n ` + styleText("gray", "The Vite+ template includes guidance on `vp` commands, the build pipeline, and project conventions."),
319
+ options: [{
320
+ label: "Append",
321
+ value: "append",
322
+ hint: "Add template content to the end"
323
+ }, {
324
+ label: "Skip",
325
+ value: "skip",
326
+ hint: "Leave existing file unchanged"
327
+ }],
328
+ initialValue: "skip"
329
+ });
330
+ if (isCancel(action)) cancelAndExit();
331
+ agentConflictDecisions.set(conflict.targetPath, action);
332
+ } else agentConflictDecisions.set(conflict.targetPath, "skip");
333
+ return {
334
+ selectedAgentTargetPaths,
335
+ agentConflictDecisions
336
+ };
337
+ }
338
+ async function collectEditorConfigPlan(rootDir, options) {
339
+ const selectedEditor = await selectEditor({
340
+ interactive: options.interactive,
341
+ editor: options.editor,
342
+ onCancel: () => cancelAndExit()
343
+ });
344
+ const editorConflicts = detectEditorConflicts({
345
+ projectRoot: rootDir,
346
+ editorId: selectedEditor
347
+ });
348
+ const editorConflictDecisions = /* @__PURE__ */ new Map();
349
+ for (const conflict of editorConflicts) if (options.interactive) {
350
+ const action = await select({
351
+ message: `${conflict.displayPath} already exists.\n ` + styleText("gray", "Vite+ adds editor settings for the built-in linter and formatter. Merge adds new keys without overwriting existing ones."),
352
+ options: [{
353
+ label: "Merge",
354
+ value: "merge",
355
+ hint: "Merge new settings into existing file"
356
+ }, {
357
+ label: "Skip",
358
+ value: "skip",
359
+ hint: "Leave existing file unchanged"
360
+ }],
361
+ initialValue: "skip"
362
+ });
363
+ if (isCancel(action)) cancelAndExit();
364
+ editorConflictDecisions.set(conflict.fileName, action);
365
+ } else editorConflictDecisions.set(conflict.fileName, "merge");
366
+ return {
367
+ selectedEditor,
368
+ editorConflictDecisions
369
+ };
370
+ }
371
+ async function collectEslintMigrationDecision(rootDir, options, packages) {
372
+ const eslintProject = detectEslintProject(rootDir, packages);
373
+ const incompatibleEslintIntegration = detectIncompatibleEslintIntegration(rootDir, packages);
374
+ let migrateEslint = false;
375
+ if (incompatibleEslintIntegration) warnIncompatibleEslintIntegration(incompatibleEslintIntegration);
376
+ else if (eslintProject.hasDependency && !eslintProject.configFile && eslintProject.legacyConfigFile) warnLegacyEslintConfig(eslintProject.legacyConfigFile);
377
+ else if (eslintProject.hasDependency && eslintProject.configFile) migrateEslint = await confirmEslintMigration(options.interactive);
378
+ else if (eslintProject.hasDependency) warnPackageLevelEslint();
379
+ return {
380
+ migrateEslint,
381
+ eslintConfigFile: eslintProject.configFile
382
+ };
383
+ }
384
+ async function collectMigrationSetupPlan(rootDir, packageManager, options, packages, includeEslint = true) {
385
+ const shouldSetupHooks = await collectGitHooksDecision(rootDir, packageManager, options);
386
+ const agentPlan = await collectAgentInstructionPlan(rootDir, options);
387
+ const editorPlan = await collectEditorConfigPlan(rootDir, options);
388
+ const eslintPlan = includeEslint ? await collectEslintMigrationDecision(rootDir, options, packages) : { migrateEslint: false };
389
+ return {
390
+ shouldSetupHooks,
391
+ ...agentPlan,
392
+ ...editorPlan,
393
+ ...eslintPlan
394
+ };
395
+ }
396
+ //#endregion
397
+ //#region src/migration/bin.ts
15
398
  async function confirmNodeVersionFileMigration(interactive, detection) {
16
399
  const message = {
17
400
  "package.json": "Migrate Volta node version (package.json) to .node-version?",
@@ -22,7 +405,7 @@ async function confirmNodeVersionFileMigration(interactive, detection) {
22
405
  message,
23
406
  initialValue: true
24
407
  });
25
- if (R(confirmed)) cancelAndExit();
408
+ if (isCancel(confirmed)) cancelAndExit();
26
409
  return confirmed;
27
410
  }
28
411
  return true;
@@ -37,11 +420,27 @@ async function confirmFrameworkShim(framework, interactive) {
37
420
  message: `Add TypeScript shim for ${name} component files (*.${framework})?\n ` + styleText("gray", `Lets TypeScript recognize .${framework} files until vp check fully supports them.`),
38
421
  initialValue: true
39
422
  });
40
- if (R(confirmed)) cancelAndExit();
423
+ if (isCancel(confirmed)) cancelAndExit();
41
424
  return confirmed;
42
425
  }
43
426
  return true;
44
427
  }
428
+ async function confirmYarnNodeModulesMode(rootDir, packageManager, packageManagerVersion, interactive) {
429
+ if (packageManager !== PackageManager.yarn) return false;
430
+ const pnp = detectYarnPnpMode(rootDir, packageManagerVersion);
431
+ if (!pnp) return false;
432
+ log.warn(`⚠ Vite+ does not currently support Yarn Plug'n'Play (PnP).`);
433
+ if (pnp.source === "environment") cancelAndExit("YARN_NODE_LINKER=pnp overrides project configuration. Set it to node-modules or unset it, then re-run `vp migrate`.", 1);
434
+ if (interactive) {
435
+ const confirmed = await confirm({
436
+ message: "Switch this project to Yarn node-modules mode and continue?",
437
+ initialValue: true
438
+ });
439
+ if (isCancel(confirmed)) cancelAndExit();
440
+ if (!confirmed) cancelAndExit("Migration cancelled. Vite+ requires Yarn node-modules mode.");
441
+ }
442
+ return true;
443
+ }
45
444
  async function fixBaseUrlForWorkspace(workspaceInfo, fixBaseUrl, updateProgress, report) {
46
445
  if (!fixBaseUrl) return [];
47
446
  const fixedProjectPaths = [];
@@ -61,11 +460,16 @@ function getWorkspaceProjectPaths(workspaceInfo) {
61
460
  return [workspaceInfo.rootDir, ...(workspaceInfo.packages ?? []).map((pkg) => path.join(workspaceInfo.rootDir, pkg.path))];
62
461
  }
63
462
  function hasBaseUrlInWorkspace(workspaceInfo) {
64
- for (const projectPath of getWorkspaceProjectPaths(workspaceInfo)) {
65
- if (!hasBaseUrlInTsconfig(projectPath)) continue;
66
- return true;
463
+ return getWorkspaceProjectPaths(workspaceInfo).some(hasBaseUrlInTsconfig);
464
+ }
465
+ async function checkWorkspaceRolldownCompatibility(workspaceInfo, report, updateProgress) {
466
+ const projectPaths = getWorkspaceProjectPaths(workspaceInfo);
467
+ for (const [index, projectPath] of projectPaths.entries()) {
468
+ const counter = projectPaths.length > 1 ? ` (${index + 1}/${projectPaths.length})` : "";
469
+ const label = displayRelative(projectPath, workspaceInfo.rootDir);
470
+ updateProgress?.(`Checking config compatibility${counter}${label ? `: ${label}` : ""}`);
471
+ await checkRolldownCompatibility(projectPath, report);
67
472
  }
68
- return false;
69
473
  }
70
474
  const helpMessage = renderCliDoc({
71
475
  usage: "vp migrate [PATH] [OPTIONS]",
@@ -106,6 +510,10 @@ const helpMessage = renderCliDoc({
106
510
  label: "--no-hooks",
107
511
  description: "Skip pre-commit hooks setup"
108
512
  },
513
+ {
514
+ label: "--full",
515
+ description: "Existing Vite+ projects: also run the full setup (hooks, editor, agent files, ESLint/Prettier migration, framework shims, tsconfig baseUrl, .node-version). Without it, `vp migrate` only upgrades the toolchain version."
516
+ },
109
517
  {
110
518
  label: "--no-interactive",
111
519
  description: "Run in non-interactive mode (skip prompts and use defaults)"
@@ -178,7 +586,8 @@ function parseArgs() {
178
586
  boolean: [
179
587
  "help",
180
588
  "interactive",
181
- "hooks"
589
+ "hooks",
590
+ "full"
182
591
  ],
183
592
  default: { interactive: defaultInteractive() }
184
593
  });
@@ -193,7 +602,8 @@ function parseArgs() {
193
602
  help: parsed.help,
194
603
  agent: parsed.agent,
195
604
  editor: parsed.editor,
196
- hooks: parsed.hooks
605
+ hooks: parsed.hooks,
606
+ full: parsed.full
197
607
  }
198
608
  };
199
609
  }
@@ -244,120 +654,18 @@ function hasExistingVitePlusMigrationCandidates(workspaceInfo, options) {
244
654
  const prettierProject = detectPrettierProject(workspaceInfo.rootDir, workspaceInfo.packages);
245
655
  return hasExplicitExistingVitePlusSetupRequest(options) || detectLegacyGitHooksMigrationCandidate(workspaceInfo.rootDir) || hasBaseUrlInWorkspace(workspaceInfo) || eslintProject.hasDependency || prettierProject.hasDependency || detectNodeVersionManagerFile(workspaceInfo.rootDir) !== void 0 || getFrameworkShimCandidates(workspaceInfo.rootDir, workspaceInfo.packages).length > 0;
246
656
  }
247
- async function collectGitHooksDecision(rootDir, packageManager, options) {
248
- let shouldSetupHooks = await promptGitHooks(options);
249
- if (shouldSetupHooks) {
250
- const reason = preflightGitHooksSetup(rootDir, packageManager);
251
- if (reason) {
252
- log.warn(`⚠ ${reason}`);
253
- shouldSetupHooks = false;
254
- }
255
- }
256
- return shouldSetupHooks;
257
- }
258
- async function collectAgentInstructionPlan(rootDir, options) {
259
- const existingAgentTargetPaths = options.agent !== void 0 || !options.interactive ? void 0 : detectExistingAgentTargetPaths(rootDir);
260
- const selectedAgentTargetPaths = existingAgentTargetPaths !== void 0 ? existingAgentTargetPaths : await selectAgentTargetPaths({
261
- interactive: options.interactive,
262
- agent: options.agent,
263
- onCancel: () => cancelAndExit()
264
- });
265
- const agentConflicts = await detectAgentConflicts({
266
- projectRoot: rootDir,
267
- targetPaths: selectedAgentTargetPaths
268
- });
269
- const agentConflictDecisions = /* @__PURE__ */ new Map();
270
- for (const conflict of agentConflicts) if (options.interactive) {
271
- const action = await select({
272
- message: `Agent instructions already exist at ${conflict.targetPath}.\n ` + styleText("gray", "The Vite+ template includes guidance on `vp` commands, the build pipeline, and project conventions."),
273
- options: [{
274
- label: "Append",
275
- value: "append",
276
- hint: "Add template content to the end"
277
- }, {
278
- label: "Skip",
279
- value: "skip",
280
- hint: "Leave existing file unchanged"
281
- }],
282
- initialValue: "skip"
283
- });
284
- if (R(action)) cancelAndExit();
285
- agentConflictDecisions.set(conflict.targetPath, action);
286
- } else agentConflictDecisions.set(conflict.targetPath, "skip");
287
- return {
288
- selectedAgentTargetPaths,
289
- agentConflictDecisions
290
- };
291
- }
292
- async function collectEditorConfigPlan(rootDir, options) {
293
- const selectedEditor = await selectEditor({
294
- interactive: options.interactive,
295
- editor: options.editor,
296
- onCancel: () => cancelAndExit()
297
- });
298
- const editorConflicts = detectEditorConflicts({
299
- projectRoot: rootDir,
300
- editorId: selectedEditor
301
- });
302
- const editorConflictDecisions = /* @__PURE__ */ new Map();
303
- for (const conflict of editorConflicts) if (options.interactive) {
304
- const action = await select({
305
- message: `${conflict.displayPath} already exists.\n ` + styleText("gray", "Vite+ adds editor settings for the built-in linter and formatter. Merge adds new keys without overwriting existing ones."),
306
- options: [{
307
- label: "Merge",
308
- value: "merge",
309
- hint: "Merge new settings into existing file"
310
- }, {
311
- label: "Skip",
312
- value: "skip",
313
- hint: "Leave existing file unchanged"
314
- }],
315
- initialValue: "skip"
316
- });
317
- if (R(action)) cancelAndExit();
318
- editorConflictDecisions.set(conflict.fileName, action);
319
- } else editorConflictDecisions.set(conflict.fileName, "merge");
320
- return {
321
- selectedEditor,
322
- editorConflictDecisions
323
- };
324
- }
325
- async function collectEslintMigrationDecision(rootDir, options, packages) {
326
- const eslintProject = detectEslintProject(rootDir, packages);
327
- const incompatibleEslintIntegration = detectIncompatibleEslintIntegration(rootDir, packages);
328
- let migrateEslint = false;
329
- if (incompatibleEslintIntegration) warnIncompatibleEslintIntegration(incompatibleEslintIntegration);
330
- else if (eslintProject.hasDependency && !eslintProject.configFile && eslintProject.legacyConfigFile) warnLegacyEslintConfig(eslintProject.legacyConfigFile);
331
- else if (eslintProject.hasDependency && eslintProject.configFile) migrateEslint = await confirmEslintMigration(options.interactive);
332
- else if (eslintProject.hasDependency) warnPackageLevelEslint();
333
- return {
334
- migrateEslint,
335
- eslintConfigFile: eslintProject.configFile
336
- };
337
- }
338
- async function collectMigrationSetupPlan(rootDir, packageManager, options, packages, includeEslint = true) {
339
- const shouldSetupHooks = await collectGitHooksDecision(rootDir, packageManager, options);
340
- const agentPlan = await collectAgentInstructionPlan(rootDir, options);
341
- const editorPlan = await collectEditorConfigPlan(rootDir, options);
342
- const eslintPlan = includeEslint ? await collectEslintMigrationDecision(rootDir, options, packages) : { migrateEslint: false };
343
- return {
344
- shouldSetupHooks,
345
- ...agentPlan,
346
- ...editorPlan,
347
- ...eslintPlan
348
- };
349
- }
350
- function getExistingVitePlusSetupOptions(options, legacyGitHooksMigrationCandidate, useFullMigrationDefaults = false) {
657
+ function getExistingVitePlusSetupOptions(options, useFullMigrationDefaults = false) {
351
658
  if (useFullMigrationDefaults) return options;
352
659
  return {
353
660
  ...options,
354
- hooks: options.hooks ?? (legacyGitHooksMigrationCandidate ? options.interactive ? void 0 : true : false),
661
+ hooks: options.hooks ?? false,
355
662
  agent: options.agent ?? false,
356
663
  editor: options.editor ?? false
357
664
  };
358
665
  }
359
- async function collectMigrationPlan(rootDir, detectedPackageManager, options, packages) {
666
+ async function collectMigrationPlan(rootDir, detectedPackageManager, detectedPackageManagerVersion, options, packages) {
360
667
  const packageManager = detectedPackageManager ?? await selectPackageManager(options.interactive, true);
668
+ const convertYarnPnp = await confirmYarnNodeModulesMode(rootDir, packageManager, detectedPackageManager ? detectedPackageManagerVersion : "latest", options.interactive);
361
669
  const setupPlan = await collectMigrationSetupPlan(rootDir, packageManager, options, packages);
362
670
  const prettierProject = detectPrettierProject(rootDir, packages);
363
671
  let migratePrettier = false;
@@ -373,8 +681,10 @@ async function collectMigrationPlan(rootDir, detectedPackageManager, options, pa
373
681
  const frameworkShimFrameworks = await collectFrameworkShimFrameworks(rootDir, options, packages);
374
682
  return {
375
683
  packageManager,
684
+ convertYarnPnp,
376
685
  ...setupPlan,
377
686
  migratePrettier,
687
+ hasPrettierDependency: prettierProject.hasDependency,
378
688
  prettierConfigFile: prettierProject.configFile,
379
689
  fixBaseUrl,
380
690
  migrateNodeVersionFile,
@@ -409,12 +719,19 @@ function handleInstallResult(installSummary, rootDir, report, options) {
409
719
  }
410
720
  return 0;
411
721
  }
722
+ const FULL_MIGRATION_HINT = `${styleText("gray", "•")} Skipped editor, hooks, and lint setup. Run \`vp migrate --full\` to apply them.`;
412
723
  function showMigrationSummary(options) {
413
- const { projectRoot, packageManager, packageManagerVersion, installDurationMs, finalInstallOk, report, updatedExistingVitePlus } = options;
724
+ const { projectRoot, packageManager, packageManagerVersion, installDurationMs, finalInstallOk, report, updatedExistingVitePlus, suggestFullMigration } = options;
414
725
  const projectLabel = displayRelative(projectRoot) || ".";
415
726
  const configUpdates = report.createdViteConfigCount + report.mergedConfigCount + report.mergedStagedConfigCount + report.inlinedLintStagedConfigCount + report.removedConfigCount + report.tsdownImportCount + report.wrappedPluginConfigCount;
416
- log$1(`${styleText("magenta", "◇")} ${updatedExistingVitePlus ? "Updated" : "Migrated"} ${accent(projectLabel)}${updatedExistingVitePlus ? "" : " to Vite+"}`);
727
+ log$1(`${styleText("magenta", "◇")} ${updatedExistingVitePlus ? "Updated" : "Migrated"} ${accent(projectLabel)} to Vite+ ${VITE_PLUS_VERSION}`);
417
728
  log$1(`${styleText("gray", "•")} Node ${process.versions.node} ${packageManager} ${packageManagerVersion}`);
729
+ if (report.dependencyUpgrades.length > 0) {
730
+ const nameWidth = Math.max(...report.dependencyUpgrades.map((change) => change.name.length));
731
+ const fromWidth = Math.max(...report.dependencyUpgrades.map((change) => (change.from ?? "").length));
732
+ log$1(`${styleText("gray", "•")} Dependencies:`);
733
+ for (const change of report.dependencyUpgrades) log$1(` ${change.name.padEnd(nameWidth)} ${muted((change.from ?? "").padEnd(fromWidth))} → ${styleText("green", change.to)}`);
734
+ }
418
735
  if (finalInstallOk && installDurationMs > 0) log$1(`${styleText("green", "✓")} Dependencies installed in ${formatDuration(installDurationMs)}`);
419
736
  if (configUpdates > 0 || report.rewrittenImportFileCount > 0) {
420
737
  const parts = [];
@@ -422,6 +739,7 @@ function showMigrationSummary(options) {
422
739
  if (report.rewrittenImportFileCount > 0) parts.push(`${report.rewrittenImportFileCount} ${report.rewrittenImportFileCount === 1 ? "file had" : "files had"} imports rewritten`);
423
740
  log$1(`${styleText("gray", "•")} ${parts.join(", ")}`);
424
741
  }
742
+ if (report.preservedUpstreamVitestImportFileCount > 0) log$1(`${styleText("gray", "•")} Kept upstream \`vitest\` imports in ${report.preservedUpstreamVitestImportFileCount} ${report.preservedUpstreamVitestImportFileCount === 1 ? "file" : "files"} for @nuxt/test-utils compatibility`);
425
743
  if (report.eslintMigrated) log$1(`${styleText("gray", "•")} ESLint rules migrated to Oxlint`);
426
744
  if (report.prettierMigrated) log$1(`${styleText("gray", "•")} Prettier migrated to Oxfmt`);
427
745
  if (report.nodeVersionFileMigrated) log$1(`${styleText("gray", "•")} Node version manager file migrated to .node-version`);
@@ -429,6 +747,7 @@ function showMigrationSummary(options) {
429
747
  if (report.gitHooksConfigured) log$1(`${styleText("gray", "•")} Git hooks configured`);
430
748
  if (report.frameworkShimAdded) log$1(`${styleText("gray", "•")} TypeScript shim added for framework component files`);
431
749
  if (report.packageManagerBootstrapConfigured) log$1(`${styleText("gray", "•")} Package manager settings configured`);
750
+ if (suggestFullMigration) log$1(FULL_MIGRATION_HINT);
432
751
  if (report.warnings.length > 0) {
433
752
  log$1(`${styleText("yellow", "!")} Warnings:`);
434
753
  for (const warning of report.warnings) log$1(` - ${warning}`);
@@ -438,17 +757,6 @@ function showMigrationSummary(options) {
438
757
  for (const step of report.manualSteps) log$1(` - ${step}`);
439
758
  }
440
759
  }
441
- async function checkRolldownCompatibility(rootDir, report) {
442
- try {
443
- const { resolveConfig } = await import("../index.js");
444
- const { checkManualChunksCompat } = await import("../compat-Cql3K40m.js");
445
- checkManualChunksCompat((await resolveConfig({
446
- root: rootDir,
447
- logLevel: "silent",
448
- configLoader: "runner"
449
- }, "build")).build?.rollupOptions?.output, report);
450
- } catch {}
451
- }
452
760
  async function downloadSupportedPackageManager(options) {
453
761
  const { rootDir, packageManager, packageManagerVersion, interactive, updateMigrationProgress, failMigrationProgress } = options;
454
762
  updateMigrationProgress("Preparing migration");
@@ -467,7 +775,7 @@ async function downloadSupportedPackageManager(options) {
467
775
  }
468
776
  return downloadResult;
469
777
  }
470
- async function executeMigrationPlan(workspaceInfoOptional, plan, interactive) {
778
+ async function executeMigrationPlan(workspaceInfoOptional, plan, interactive, preExistingChangedPaths) {
471
779
  const report = createMigrationReport();
472
780
  const migrationProgress = interactive ? spinner({ indicator: "timer" }) : void 0;
473
781
  let migrationProgressStarted = false;
@@ -505,6 +813,11 @@ async function executeMigrationPlan(workspaceInfoOptional, plan, interactive) {
505
813
  packageManager: plan.packageManager,
506
814
  downloadPackageManager: downloadResult
507
815
  };
816
+ if (plan.convertYarnPnp) {
817
+ updateMigrationProgress("Configuring Yarn node-modules mode");
818
+ report.packageManagerBootstrapConfigured = configureYarnNodeModulesMode(workspaceInfo.rootDir);
819
+ if (report.packageManagerBootstrapConfigured) log.success("✔ Switched Yarn to node-modules mode");
820
+ }
508
821
  if (plan.migrateNodeVersionFile && plan.nodeVersionDetection) {
509
822
  updateMigrationProgress("Migrating node version file");
510
823
  migrateNodeVersionManagerFile(workspaceInfo.rootDir, plan.nodeVersionDetection, report);
@@ -522,9 +835,7 @@ async function executeMigrationPlan(workspaceInfoOptional, plan, interactive) {
522
835
  failMigrationProgress("Migration failed");
523
836
  cancelAndExit("Vite+ cannot automatically migrate this project yet.", 1);
524
837
  }
525
- updateMigrationProgress("Checking config compatibility");
526
- await checkRolldownCompatibility(workspaceInfo.rootDir, report);
527
- if (workspaceInfo.packages) for (const pkg of workspaceInfo.packages) await checkRolldownCompatibility(path.join(workspaceInfo.rootDir, pkg.path), report);
838
+ await checkWorkspaceRolldownCompatibility(workspaceInfo, report, updateMigrationProgress);
528
839
  await fixBaseUrlForWorkspace(workspaceInfo, plan.fixBaseUrl, updateMigrationProgress, report);
529
840
  if (plan.migrateEslint) {
530
841
  updateMigrationProgress("Migrating ESLint");
@@ -572,15 +883,21 @@ async function executeMigrationPlan(workspaceInfoOptional, plan, interactive) {
572
883
  });
573
884
  addFrameworkShimsForWorkspace(workspaceInfo.rootDir, plan.frameworkShimFrameworks, workspaceInfo.packages, report, updateMigrationProgress);
574
885
  const installArgs = plan.packageManager === PackageManager.npm || plan.packageManager === PackageManager.bun ? ["--force"] : ["--no-frozen-lockfile"];
886
+ const npmReinstallPreparation = plan.packageManager === PackageManager.npm ? prepareNpmViteAliasReinstall(workspaceInfo.rootDir, getWorkspaceProjectPaths(workspaceInfo)) : void 0;
575
887
  updateMigrationProgress("Installing dependencies");
576
888
  const finalInstallSummary = await runViteInstall(workspaceInfo.rootDir, interactive, installArgs, {
577
889
  silent: true,
578
890
  packageManager: workspaceInfo.packageManager,
579
891
  packageManagerVersion: workspaceInfo.downloadPackageManager.version
580
892
  });
893
+ if (finalInstallSummary.status === "failed") npmReinstallPreparation?.restore();
894
+ else npmReinstallPreparation?.commit();
581
895
  clearMigrationProgress();
896
+ const initialInstallDurationMs = handleInstallResult(initialInstallSummary, workspaceInfo.rootDir, report, { propagateExitCode: false });
897
+ const finalInstallDurationMs = handleInstallResult(finalInstallSummary, workspaceInfo.rootDir, report);
898
+ if (finalInstallSummary.status === "installed" && canFormatWithOxfmt(plan.hasPrettierDependency, plan.migratePrettier)) await formatMigratedProject(workspaceInfo.rootDir, interactive, report, { excludedPaths: preExistingChangedPaths });
582
899
  return {
583
- installDurationMs: handleInstallResult(initialInstallSummary, workspaceInfo.rootDir, report, { propagateExitCode: false }) + handleInstallResult(finalInstallSummary, workspaceInfo.rootDir, report),
900
+ installDurationMs: initialInstallDurationMs + finalInstallDurationMs,
584
901
  finalInstallOk: finalInstallSummary.status === "installed",
585
902
  packageManagerVersion: downloadResult.version,
586
903
  report
@@ -595,11 +912,15 @@ async function main() {
595
912
  }
596
913
  printHeader();
597
914
  const workspaceInfoOptional = await detectWorkspace(projectPath);
915
+ const initialChangedPaths = await collectChangedFormatPaths(workspaceInfoOptional.rootDir);
916
+ const preExistingChangedPaths = initialChangedPaths ? new Set(initialChangedPaths) : void 0;
598
917
  const resolvedPackageManager = workspaceInfoOptional.packageManager ?? "unknown";
599
918
  if (hasVitePlusDependency(readNearestPackageJson(workspaceInfoOptional.rootDir)) && !isForceOverrideMode()) {
919
+ let convertYarnPnp = await confirmYarnNodeModulesMode(workspaceInfoOptional.rootDir, workspaceInfoOptional.packageManager, workspaceInfoOptional.packageManagerVersion, options.interactive);
600
920
  let didMigrate = false;
601
921
  let installDurationMs = 0;
602
922
  let finalInstallOk = true;
923
+ let canFormatMigratedProject = !process.env.VP_SKIP_INSTALL;
603
924
  const report = createMigrationReport();
604
925
  const migrationProgress = options.interactive ? spinner({ indicator: "timer" }) : void 0;
605
926
  let migrationProgressStarted = false;
@@ -625,8 +946,7 @@ async function main() {
625
946
  }
626
947
  };
627
948
  const pendingCoreMigration = detectPendingCoreMigration(workspaceInfoOptional);
628
- const legacyGitHooksMigrationCandidate = detectLegacyGitHooksMigrationCandidate(workspaceInfoOptional.rootDir);
629
- const vitePlusBootstrapPending = detectVitePlusBootstrapPending(workspaceInfoOptional.rootDir, workspaceInfoOptional.packageManager);
949
+ const vitePlusBootstrapPending = detectVitePlusBootstrapPending(workspaceInfoOptional.rootDir, workspaceInfoOptional.packageManager, workspaceInfoOptional.packages, workspaceInfoOptional.packageManagerVersion);
630
950
  let packageManager = vitePlusBootstrapPending ? workspaceInfoOptional.packageManager ?? await selectPackageManager(options.interactive, true) : workspaceInfoOptional.packageManager;
631
951
  let downloadedPackageManager;
632
952
  let packageManagerVersion = workspaceInfoOptional.packageManagerVersion;
@@ -648,16 +968,23 @@ async function main() {
648
968
  return downloadExistingPackageManager();
649
969
  };
650
970
  if (vitePlusBootstrapPending) await ensureExistingPackageManager();
971
+ clearMigrationProgress();
972
+ if (!convertYarnPnp && workspaceInfoOptional.packageManager === void 0 && packageManager === PackageManager.yarn) convertYarnPnp = await confirmYarnNodeModulesMode(workspaceInfoOptional.rootDir, packageManager, packageManagerVersion, options.interactive);
973
+ report.dependencyUpgrades = await collectToolchainVersionChanges(workspaceInfoOptional.rootDir);
974
+ updateMigrationProgress("Rewriting toolchain imports across the workspace");
651
975
  const coreMigrationResult = finalizeCoreMigrationForExistingVitePlus(workspaceInfoOptional, true, report, pendingCoreMigration);
652
976
  if (coreMigrationResult.scripts || coreMigrationResult.tsconfigTypes || coreMigrationResult.imports) didMigrate = true;
653
- if (!didMigrate && report.warnings.length === 0 && !vitePlusBootstrapPending && !hasExistingVitePlusMigrationCandidates(workspaceInfoOptional, options)) {
977
+ clearMigrationProgress();
978
+ const fullSetup = options.full === true;
979
+ const skippedSetupCandidates = !fullSetup && !hasExplicitExistingVitePlusSetupRequest(options) && hasExistingVitePlusMigrationCandidates(workspaceInfoOptional, options);
980
+ if (!didMigrate && !convertYarnPnp && report.warnings.length === 0 && !vitePlusBootstrapPending && !(fullSetup ? hasExistingVitePlusMigrationCandidates(workspaceInfoOptional, options) : hasExplicitExistingVitePlusSetupRequest(options))) {
981
+ if (skippedSetupCandidates) log$1(FULL_MIGRATION_HINT);
654
982
  outro(`This project is already using Vite+! ${accent("Happy coding!")}`);
655
983
  return;
656
984
  }
657
- const fullMigrationSummary = vitePlusBootstrapPending || coreMigrationResult.scripts || coreMigrationResult.tsconfigTypes || coreMigrationResult.imports;
658
- const setupOptions = getExistingVitePlusSetupOptions(options, legacyGitHooksMigrationCandidate, options.interactive && fullMigrationSummary);
659
- const plan = await collectMigrationSetupPlan(workspaceInfoOptional.rootDir, packageManager, setupOptions, workspaceInfoOptional.packages);
660
- const frameworkShimFrameworks = await collectFrameworkShimFrameworks(workspaceInfoOptional.rootDir, options, workspaceInfoOptional.packages);
985
+ const setupOptions = getExistingVitePlusSetupOptions(options, fullSetup);
986
+ const plan = await collectMigrationSetupPlan(workspaceInfoOptional.rootDir, packageManager, setupOptions, workspaceInfoOptional.packages, fullSetup);
987
+ const frameworkShimFrameworks = fullSetup ? await collectFrameworkShimFrameworks(workspaceInfoOptional.rootDir, options, workspaceInfoOptional.packages) : void 0;
661
988
  let needsInstall = false;
662
989
  if (vitePlusBootstrapPending) {
663
990
  const downloadResult = await ensureExistingPackageManager();
@@ -672,7 +999,12 @@ async function main() {
672
999
  needsInstall = bootstrapResult.changed || needsInstall;
673
1000
  }
674
1001
  }
675
- const fixedBaseUrlProjectPaths = await fixBaseUrlForWorkspace(workspaceInfoOptional, hasBaseUrlInWorkspace(workspaceInfoOptional) ? await confirmBaseUrlFix(options.interactive) : false, updateMigrationProgress, report);
1002
+ let fixBaseUrl = false;
1003
+ if (fullSetup && hasBaseUrlInWorkspace(workspaceInfoOptional)) {
1004
+ if (options.interactive) clearMigrationProgress();
1005
+ fixBaseUrl = await confirmBaseUrlFix(options.interactive);
1006
+ }
1007
+ const fixedBaseUrlProjectPaths = await fixBaseUrlForWorkspace(workspaceInfoOptional, fixBaseUrl, updateMigrationProgress, report);
676
1008
  if (fixedBaseUrlProjectPaths.length > 0) {
677
1009
  updateMigrationProgress("Updating lint defaults");
678
1010
  for (const projectPath of fixedBaseUrlProjectPaths) injectLintTypeCheckDefaults(projectPath, true, report);
@@ -694,23 +1026,38 @@ async function main() {
694
1026
  }
695
1027
  const prettierProject = detectPrettierProject(workspaceInfoOptional.rootDir, workspaceInfoOptional.packages);
696
1028
  let prettierMigrated = false;
697
- if (prettierProject.hasDependency && prettierProject.configFile) {
698
- if (await confirmPrettierMigration(options.interactive)) {
699
- await ensureExistingPackageManager();
700
- updateMigrationProgress("Migrating Prettier");
701
- if (!await migratePrettierToOxfmt(workspaceInfoOptional.rootDir, options.interactive, prettierProject.configFile, workspaceInfoOptional.packages, {
702
- silent: true,
703
- report
704
- })) {
705
- clearMigrationProgress();
706
- cancelAndExit("Prettier migration failed. Fix the issue and re-run `vp migrate`.", 1);
1029
+ if (fullSetup) {
1030
+ if (prettierProject.hasDependency && prettierProject.configFile) {
1031
+ if (options.interactive) clearMigrationProgress();
1032
+ if (await confirmPrettierMigration(options.interactive)) {
1033
+ await ensureExistingPackageManager();
1034
+ updateMigrationProgress("Migrating Prettier");
1035
+ if (!await migratePrettierToOxfmt(workspaceInfoOptional.rootDir, options.interactive, prettierProject.configFile, workspaceInfoOptional.packages, {
1036
+ silent: true,
1037
+ report
1038
+ })) {
1039
+ clearMigrationProgress();
1040
+ cancelAndExit("Prettier migration failed. Fix the issue and re-run `vp migrate`.", 1);
1041
+ }
1042
+ prettierMigrated = true;
707
1043
  }
708
- prettierMigrated = true;
1044
+ } else if (prettierProject.hasDependency) warnPackageLevelPrettier();
1045
+ }
1046
+ if (fullSetup) {
1047
+ const nodeVersionDetection = detectNodeVersionManagerFile(workspaceInfoOptional.rootDir);
1048
+ if (nodeVersionDetection) {
1049
+ if (options.interactive) clearMigrationProgress();
1050
+ if (await confirmNodeVersionFileMigration(options.interactive, nodeVersionDetection) && migrateNodeVersionManagerFile(workspaceInfoOptional.rootDir, nodeVersionDetection, report)) didMigrate = true;
1051
+ }
1052
+ }
1053
+ if (convertYarnPnp) {
1054
+ updateMigrationProgress("Configuring Yarn node-modules mode");
1055
+ if (configureYarnNodeModulesMode(workspaceInfoOptional.rootDir)) {
1056
+ log.success("✔ Switched Yarn to node-modules mode");
1057
+ report.packageManagerBootstrapConfigured = true;
1058
+ didMigrate = true;
1059
+ needsInstall = true;
709
1060
  }
710
- } else if (prettierProject.hasDependency) warnPackageLevelPrettier();
711
- const nodeVersionDetection = detectNodeVersionManagerFile(workspaceInfoOptional.rootDir);
712
- if (nodeVersionDetection) {
713
- if (await confirmNodeVersionFileMigration(options.interactive, nodeVersionDetection) && migrateNodeVersionManagerFile(workspaceInfoOptional.rootDir, nodeVersionDetection, report)) didMigrate = true;
714
1061
  }
715
1062
  if (addFrameworkShimsForWorkspace(workspaceInfoOptional.rootDir, frameworkShimFrameworks, workspaceInfoOptional.packages, report, updateMigrationProgress)) didMigrate = true;
716
1063
  if (eslintMigrated || prettierMigrated) {
@@ -733,12 +1080,18 @@ async function main() {
733
1080
  const resolved = await ensureExistingPackageManager();
734
1081
  updateMigrationProgress("Installing dependencies");
735
1082
  const resolvedVersion = resolved?.version ?? packageManagerVersion;
1083
+ const npmReinstallPreparation = packageManager === PackageManager.npm ? prepareNpmViteAliasReinstall(workspaceInfoOptional.rootDir, getWorkspaceProjectPaths(workspaceInfoOptional)) : void 0;
736
1084
  const installSummary = await runViteInstall(workspaceInfoOptional.rootDir, options.interactive, packageManager === PackageManager.npm || packageManager === PackageManager.bun ? ["--force"] : ["--no-frozen-lockfile"], {
737
1085
  silent: true,
738
1086
  packageManager,
739
1087
  packageManagerVersion: resolvedVersion
740
1088
  });
741
- if (installSummary.status === "failed") clearMigrationProgress();
1089
+ if (installSummary.status === "failed") {
1090
+ clearMigrationProgress();
1091
+ npmReinstallPreparation?.restore();
1092
+ } else npmReinstallPreparation?.commit();
1093
+ finalInstallOk = installSummary.status !== "failed";
1094
+ canFormatMigratedProject = finalInstallOk && canFormatMigratedProject;
742
1095
  installDurationMs += handleInstallResult(installSummary, workspaceInfoOptional.rootDir, report);
743
1096
  }
744
1097
  if (plan.selectedAgentTargetPaths && plan.selectedAgentTargetPaths.length > 0) {
@@ -763,8 +1116,11 @@ async function main() {
763
1116
  });
764
1117
  didMigrate = true;
765
1118
  }
766
- await checkRolldownCompatibility(workspaceInfoOptional.rootDir, report);
767
- if (workspaceInfoOptional.packages) for (const pkg of workspaceInfoOptional.packages) await checkRolldownCompatibility(path.join(workspaceInfoOptional.rootDir, pkg.path), report);
1119
+ await checkWorkspaceRolldownCompatibility(workspaceInfoOptional, report, updateMigrationProgress);
1120
+ if (didMigrate && finalInstallOk && canFormatMigratedProject && canFormatWithOxfmt(prettierProject.hasDependency, prettierMigrated)) {
1121
+ clearMigrationProgress();
1122
+ await formatMigratedProject(workspaceInfoOptional.rootDir, options.interactive, report, { excludedPaths: preExistingChangedPaths });
1123
+ }
768
1124
  if (didMigrate || report.warnings.length > 0) {
769
1125
  clearMigrationProgress();
770
1126
  showMigrationSummary({
@@ -774,13 +1130,18 @@ async function main() {
774
1130
  installDurationMs,
775
1131
  finalInstallOk,
776
1132
  report,
777
- updatedExistingVitePlus: !fullMigrationSummary
1133
+ updatedExistingVitePlus: true,
1134
+ suggestFullMigration: skippedSetupCandidates
778
1135
  });
779
- } else outro(`This project is already using Vite+! ${accent("Happy coding!")}`);
1136
+ } else {
1137
+ clearMigrationProgress();
1138
+ if (skippedSetupCandidates) log$1(FULL_MIGRATION_HINT);
1139
+ outro(`This project is already using Vite+! ${accent("Happy coding!")}`);
1140
+ }
780
1141
  return;
781
1142
  }
782
- const plan = await collectMigrationPlan(workspaceInfoOptional.rootDir, workspaceInfoOptional.packageManager, options, workspaceInfoOptional.packages);
783
- const result = await executeMigrationPlan(workspaceInfoOptional, plan, options.interactive);
1143
+ const plan = await collectMigrationPlan(workspaceInfoOptional.rootDir, workspaceInfoOptional.packageManager, workspaceInfoOptional.packageManagerVersion, options, workspaceInfoOptional.packages);
1144
+ const result = await executeMigrationPlan(workspaceInfoOptional, plan, options.interactive, preExistingChangedPaths);
784
1145
  showMigrationSummary({
785
1146
  projectRoot: workspaceInfoOptional.rootDir,
786
1147
  packageManager: plan.packageManager,