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
@@ -0,0 +1,584 @@
1
+ import { B as runCommandSilently, M as multiselect, P as select, R as isCancel, j as log, k as confirm, l as cancelAndExit, w as getSpinner } from "./tsconfig-D6sTl0dN.js";
2
+ import { a as writeJsonFile, c as parse, i as readJsonFile, n as editJsonFile, o as applyEdits, s as modify, t as detectFormattingOptions } from "./json-DiRs8ceZ.js";
3
+ import { G as detectConfigs, J as warnMigration, U as PRETTIER_CONFIG_FILES, Y as displayRelative, z as rewriteToolLintStagedConfigFiles } from "./agent-D3nZAMGI.js";
4
+ import path from "node:path";
5
+ import { rewritePrettier } from "../binding/index.js";
6
+ import fs from "node:fs";
7
+ import { styleText } from "node:util";
8
+ import fsPromises from "node:fs/promises";
9
+ //#region src/migration/migrator/prettier.ts
10
+ function detectPrettierProject(projectPath, packages) {
11
+ const packageJsonPath = path.join(projectPath, "package.json");
12
+ if (!fs.existsSync(packageJsonPath)) return { hasDependency: false };
13
+ const pkg = readJsonFile(packageJsonPath);
14
+ let hasDependency = !!(pkg.devDependencies?.prettier || pkg.dependencies?.prettier);
15
+ const configFile = detectConfigs(projectPath).prettierConfig;
16
+ if (!hasDependency && packages) for (const wp of packages) {
17
+ const pkgJsonPath = path.join(projectPath, wp.path, "package.json");
18
+ if (!fs.existsSync(pkgJsonPath)) continue;
19
+ const wpPkg = readJsonFile(pkgJsonPath);
20
+ if (wpPkg.devDependencies?.prettier || wpPkg.dependencies?.prettier) {
21
+ hasDependency = true;
22
+ break;
23
+ }
24
+ }
25
+ return {
26
+ hasDependency,
27
+ configFile
28
+ };
29
+ }
30
+ /**
31
+ * Run `vp fmt --migrate=prettier` step with graceful error handling.
32
+ * Returns true on success, false on failure.
33
+ */
34
+ async function runPrettierMigrateStep(vpBin, cwd, spinner, failMessage, manualHint) {
35
+ try {
36
+ const result = await runCommandSilently({
37
+ command: vpBin,
38
+ args: ["fmt", "--migrate=prettier"],
39
+ cwd,
40
+ envs: process.env
41
+ });
42
+ if (result.exitCode !== 0) {
43
+ spinner.stop(failMessage);
44
+ const stderr = result.stderr.toString().trim();
45
+ if (stderr) log.warn(`⚠ ${stderr}`);
46
+ log.info(manualHint);
47
+ return false;
48
+ }
49
+ return true;
50
+ } catch {
51
+ spinner.stop(failMessage);
52
+ log.info(manualHint);
53
+ return false;
54
+ }
55
+ }
56
+ async function migratePrettierToOxfmt(projectPath, interactive, prettierConfigFile, packages, options) {
57
+ const vpBin = process.env.VP_CLI_BIN ?? "vp";
58
+ const spinner = options?.silent ? {
59
+ start: () => {},
60
+ stop: () => {},
61
+ pause: () => {},
62
+ resume: () => {},
63
+ cancel: () => {},
64
+ error: () => {},
65
+ clear: () => {},
66
+ message: () => {},
67
+ isCancelled: false
68
+ } : getSpinner(interactive);
69
+ if (prettierConfigFile) {
70
+ let tempPrettierConfig;
71
+ if (prettierConfigFile === "package.json#prettier") {
72
+ const pkg = readJsonFile(path.join(projectPath, "package.json"));
73
+ if (pkg.prettier) {
74
+ tempPrettierConfig = path.join(projectPath, ".prettierrc.json");
75
+ fs.writeFileSync(tempPrettierConfig, JSON.stringify(pkg.prettier, null, 2));
76
+ } else return true;
77
+ }
78
+ try {
79
+ spinner.start("Migrating Prettier config to Oxfmt...");
80
+ if (!await runPrettierMigrateStep(vpBin, projectPath, spinner, "Prettier migration failed", "You can run `vp fmt --migrate=prettier` manually later")) return false;
81
+ spinner.stop("Prettier config migrated to .oxfmtrc.json");
82
+ } finally {
83
+ if (tempPrettierConfig) try {
84
+ fs.unlinkSync(tempPrettierConfig);
85
+ } catch {}
86
+ }
87
+ }
88
+ if (options?.report) options.report.prettierMigrated = true;
89
+ deletePrettierConfigFiles(projectPath, options?.report, options?.silent);
90
+ rewritePrettierPackageJson(path.join(projectPath, "package.json"));
91
+ if (packages) for (const pkg of packages) rewritePrettierPackageJson(path.join(projectPath, pkg.path, "package.json"));
92
+ rewritePrettierLintStagedConfigFiles(projectPath, options?.report);
93
+ const prettierIgnorePath = path.join(projectPath, ".prettierignore");
94
+ if (fs.existsSync(prettierIgnorePath)) warnMigration(`${displayRelative(prettierIgnorePath)} found — Oxfmt supports .prettierignore, but using the \`ignorePatterns\` option is recommended.`, options?.report);
95
+ return true;
96
+ }
97
+ function deletePrettierConfigFiles(basePath, report, silent = false) {
98
+ const configs = detectConfigs(basePath);
99
+ if (configs.prettierConfig && configs.prettierConfig !== "package.json#prettier") {
100
+ const configPath = path.join(basePath, configs.prettierConfig);
101
+ if (fs.existsSync(configPath)) {
102
+ fs.unlinkSync(configPath);
103
+ if (report) report.removedConfigCount++;
104
+ if (!silent) log.success(`✔ Removed ${displayRelative(configPath)}`);
105
+ }
106
+ }
107
+ for (const file of PRETTIER_CONFIG_FILES) {
108
+ if (file === configs.prettierConfig) continue;
109
+ const configPath = path.join(basePath, file);
110
+ if (fs.existsSync(configPath)) {
111
+ fs.unlinkSync(configPath);
112
+ if (report) report.removedConfigCount++;
113
+ if (!silent) log.success(`✔ Removed ${displayRelative(configPath)}`);
114
+ }
115
+ }
116
+ editJsonFile(path.join(basePath, "package.json"), (pkg) => {
117
+ if (pkg.prettier) {
118
+ delete pkg.prettier;
119
+ return pkg;
120
+ }
121
+ });
122
+ }
123
+ function rewritePrettierPackageJson(packageJsonPath) {
124
+ if (!fs.existsSync(packageJsonPath)) return;
125
+ editJsonFile(packageJsonPath, (pkg) => {
126
+ let changed = false;
127
+ if (pkg.devDependencies) {
128
+ for (const dep of Object.keys(pkg.devDependencies)) if (dep === "prettier" || dep.startsWith("prettier-plugin-")) {
129
+ delete pkg.devDependencies[dep];
130
+ changed = true;
131
+ }
132
+ }
133
+ if (pkg.dependencies) {
134
+ for (const dep of Object.keys(pkg.dependencies)) if (dep === "prettier" || dep.startsWith("prettier-plugin-")) {
135
+ delete pkg.dependencies[dep];
136
+ changed = true;
137
+ }
138
+ }
139
+ if (pkg.scripts) {
140
+ const updated = rewritePrettier(JSON.stringify(pkg.scripts));
141
+ if (updated) {
142
+ pkg.scripts = JSON.parse(updated);
143
+ changed = true;
144
+ }
145
+ }
146
+ if (pkg["lint-staged"]) {
147
+ const updated = rewritePrettier(JSON.stringify(pkg["lint-staged"]));
148
+ if (updated) {
149
+ pkg["lint-staged"] = JSON.parse(updated);
150
+ changed = true;
151
+ }
152
+ }
153
+ return changed ? pkg : void 0;
154
+ });
155
+ }
156
+ function rewritePrettierLintStagedConfigFiles(projectPath, report) {
157
+ rewriteToolLintStagedConfigFiles(projectPath, rewritePrettier, "prettier", report);
158
+ }
159
+ function warnPackageLevelPrettier() {
160
+ log.warn("Prettier detected in workspace packages but no root config found. Package-level Prettier must be migrated manually.");
161
+ }
162
+ async function confirmPrettierMigration(interactive) {
163
+ if (interactive) {
164
+ const confirmed = await confirm({
165
+ message: "Migrate Prettier to Oxfmt?\n " + styleText("gray", "Oxfmt is Vite+'s built-in formatter that replaces Prettier with faster performance. Your configuration will be converted automatically."),
166
+ initialValue: true
167
+ });
168
+ if (isCancel(confirmed)) cancelAndExit();
169
+ return confirmed;
170
+ }
171
+ log.info("Prettier configuration detected. Auto-migrating to Oxfmt...");
172
+ return true;
173
+ }
174
+ async function promptPrettierMigration(projectPath, interactive, packages) {
175
+ const prettierProject = detectPrettierProject(projectPath, packages);
176
+ if (!prettierProject.hasDependency) return false;
177
+ if (!prettierProject.configFile) {
178
+ warnPackageLevelPrettier();
179
+ return false;
180
+ }
181
+ if (!await confirmPrettierMigration(interactive)) return false;
182
+ if (!await migratePrettierToOxfmt(projectPath, interactive, prettierProject.configFile, packages)) cancelAndExit("Prettier migration failed.", 1);
183
+ return true;
184
+ }
185
+ //#endregion
186
+ //#region src/migration/migrator/framework-shim.ts
187
+ const FRAMEWORK_SHIMS = {
188
+ vue: [
189
+ "declare module '*.vue' {",
190
+ " import type { DefineComponent } from 'vue';",
191
+ " const component: DefineComponent<{}, {}, unknown>;",
192
+ " export default component;",
193
+ "}"
194
+ ].join("\n"),
195
+ astro: "/// <reference types=\"astro/client\" />"
196
+ };
197
+ function detectFramework(projectPath) {
198
+ const packageJsonPath = path.join(projectPath, "package.json");
199
+ if (!fs.existsSync(packageJsonPath)) return [];
200
+ const pkg = readJsonFile(packageJsonPath);
201
+ const allDeps = {
202
+ ...pkg.dependencies,
203
+ ...pkg.devDependencies
204
+ };
205
+ return ["vue", "astro"].filter((framework) => !!allDeps[framework]);
206
+ }
207
+ function getEnvDtsPath(projectPath) {
208
+ const srcEnvDts = path.join(projectPath, "src", "env.d.ts");
209
+ const rootEnvDts = path.join(projectPath, "env.d.ts");
210
+ for (const candidate of [srcEnvDts, rootEnvDts]) if (fs.existsSync(candidate)) return candidate;
211
+ return fs.existsSync(path.join(projectPath, "src")) ? srcEnvDts : rootEnvDts;
212
+ }
213
+ function hasFrameworkShim(projectPath, framework) {
214
+ const dirsToScan = [projectPath, path.join(projectPath, "src")];
215
+ for (const dir of dirsToScan) {
216
+ if (!fs.existsSync(dir)) continue;
217
+ let entries;
218
+ try {
219
+ entries = fs.readdirSync(dir);
220
+ } catch {
221
+ continue;
222
+ }
223
+ for (const entry of entries) {
224
+ if (!entry.endsWith(".d.ts")) continue;
225
+ const content = fs.readFileSync(path.join(dir, entry), "utf-8");
226
+ if (framework === "astro") {
227
+ if (content.includes("astro/client")) return true;
228
+ } else if (content.includes(`'*.${framework}'`) || content.includes(`"*.${framework}"`)) return true;
229
+ }
230
+ }
231
+ return false;
232
+ }
233
+ function addFrameworkShim(projectPath, framework, report) {
234
+ const envDtsPath = getEnvDtsPath(projectPath);
235
+ const shim = FRAMEWORK_SHIMS[framework];
236
+ if (fs.existsSync(envDtsPath)) {
237
+ const existing = fs.readFileSync(envDtsPath, "utf-8");
238
+ fs.writeFileSync(envDtsPath, `${existing.trimEnd()}\n\n${shim}\n`, "utf-8");
239
+ } else {
240
+ fs.mkdirSync(path.dirname(envDtsPath), { recursive: true });
241
+ fs.writeFileSync(envDtsPath, `${shim}\n`, "utf-8");
242
+ }
243
+ if (report) report.frameworkShimAdded = true;
244
+ }
245
+ const EDITORS = [{
246
+ id: "vscode",
247
+ label: "VSCode",
248
+ targetDir: ".vscode",
249
+ files: {
250
+ "settings.json": {
251
+ "editor.defaultFormatter": "oxc.oxc-vscode",
252
+ "[javascript]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
253
+ "[javascriptreact]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
254
+ "[typescript]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
255
+ "[typescriptreact]": { "editor.defaultFormatter": "oxc.oxc-vscode" },
256
+ "oxc.fmt.configPath": "./vite.config.ts",
257
+ "editor.formatOnSave": true,
258
+ "editor.formatOnSaveMode": "file",
259
+ "editor.codeActionsOnSave": { "source.fixAll.oxc": "explicit" }
260
+ },
261
+ "extensions.json": { recommendations: ["VoidZero.vite-plus-extension-pack"] }
262
+ }
263
+ }, {
264
+ id: "zed",
265
+ label: "Zed",
266
+ targetDir: ".zed",
267
+ files: { "settings.json": {
268
+ lsp: {
269
+ oxlint: { initialization_options: { settings: {
270
+ run: "onType",
271
+ fixKind: "safe_fix",
272
+ typeAware: true,
273
+ unusedDisableDirectives: "deny"
274
+ } } },
275
+ oxfmt: { initialization_options: { settings: {
276
+ "fmt.configPath": "./vite.config.ts",
277
+ run: "onSave"
278
+ } } }
279
+ },
280
+ languages: {
281
+ CSS: {
282
+ format_on_save: "on",
283
+ prettier: { allowed: false },
284
+ formatter: [{ language_server: { name: "oxfmt" } }]
285
+ },
286
+ GraphQL: {
287
+ format_on_save: "on",
288
+ prettier: { allowed: false },
289
+ formatter: [{ language_server: { name: "oxfmt" } }]
290
+ },
291
+ Handlebars: {
292
+ format_on_save: "on",
293
+ prettier: { allowed: false },
294
+ formatter: [{ language_server: { name: "oxfmt" } }]
295
+ },
296
+ HTML: {
297
+ format_on_save: "on",
298
+ prettier: { allowed: false },
299
+ formatter: [{ language_server: { name: "oxfmt" } }]
300
+ },
301
+ JavaScript: {
302
+ format_on_save: "on",
303
+ prettier: { allowed: false },
304
+ formatter: [{ language_server: { name: "oxfmt" } }],
305
+ code_action: "source.fixAll.oxc"
306
+ },
307
+ JSX: {
308
+ format_on_save: "on",
309
+ prettier: { allowed: false },
310
+ formatter: [{ language_server: { name: "oxfmt" } }]
311
+ },
312
+ JSON: {
313
+ format_on_save: "on",
314
+ prettier: { allowed: false },
315
+ formatter: [{ language_server: { name: "oxfmt" } }]
316
+ },
317
+ JSON5: {
318
+ format_on_save: "on",
319
+ prettier: { allowed: false },
320
+ formatter: [{ language_server: { name: "oxfmt" } }]
321
+ },
322
+ JSONC: {
323
+ format_on_save: "on",
324
+ prettier: { allowed: false },
325
+ formatter: [{ language_server: { name: "oxfmt" } }]
326
+ },
327
+ Less: {
328
+ format_on_save: "on",
329
+ prettier: { allowed: false },
330
+ formatter: [{ language_server: { name: "oxfmt" } }]
331
+ },
332
+ Markdown: {
333
+ format_on_save: "on",
334
+ prettier: { allowed: false },
335
+ formatter: [{ language_server: { name: "oxfmt" } }]
336
+ },
337
+ MDX: {
338
+ format_on_save: "on",
339
+ prettier: { allowed: false },
340
+ formatter: [{ language_server: { name: "oxfmt" } }]
341
+ },
342
+ SCSS: {
343
+ format_on_save: "on",
344
+ prettier: { allowed: false },
345
+ formatter: [{ language_server: { name: "oxfmt" } }]
346
+ },
347
+ TypeScript: {
348
+ format_on_save: "on",
349
+ prettier: { allowed: false },
350
+ formatter: [{ language_server: { name: "oxfmt" } }]
351
+ },
352
+ TSX: {
353
+ format_on_save: "on",
354
+ prettier: { allowed: false },
355
+ formatter: [{ language_server: { name: "oxfmt" } }]
356
+ },
357
+ "Vue.js": {
358
+ format_on_save: "on",
359
+ prettier: { allowed: false },
360
+ formatter: [{ language_server: { name: "oxfmt" } }]
361
+ },
362
+ YAML: {
363
+ format_on_save: "on",
364
+ prettier: { allowed: false },
365
+ formatter: [{ language_server: { name: "oxfmt" } }]
366
+ }
367
+ }
368
+ } }
369
+ }];
370
+ async function selectEditor({ interactive, editor, onCancel }) {
371
+ if (editor === false) return;
372
+ if (interactive && !editor) {
373
+ const editorOptions = EDITORS.map((option) => ({
374
+ label: option.label,
375
+ value: option.id,
376
+ hint: option.targetDir
377
+ }));
378
+ const otherOption = {
379
+ label: "Other",
380
+ value: null,
381
+ hint: "Skip writing editor configs"
382
+ };
383
+ const selectedEditor = await select({
384
+ message: "Which editor are you using?\n " + styleText("gray", "Writes editor config files to enable recommended extensions and Oxlint/Oxfmt integrations."),
385
+ options: [...editorOptions, otherOption],
386
+ initialValue: "vscode"
387
+ });
388
+ if (isCancel(selectedEditor)) {
389
+ onCancel();
390
+ return;
391
+ }
392
+ if (selectedEditor === null) return;
393
+ return resolveEditorId(selectedEditor);
394
+ }
395
+ if (editor) return resolveEditorId(editor);
396
+ }
397
+ async function selectEditors({ interactive, editor, onCancel }) {
398
+ if (editor === false) return;
399
+ if (interactive && !editor) {
400
+ const selectedEditors = await multiselect({
401
+ message: "Which editors are you using?\n " + styleText("gray", "Writes editor config files to enable recommended extensions and Oxlint/Oxfmt integrations."),
402
+ options: EDITORS.map((option) => ({
403
+ label: option.label,
404
+ value: option.id,
405
+ hint: option.targetDir
406
+ })),
407
+ initialValues: ["vscode"],
408
+ required: false
409
+ });
410
+ if (isCancel(selectedEditors)) {
411
+ onCancel();
412
+ return;
413
+ }
414
+ return selectedEditors.length === 0 ? void 0 : resolveEditorIds(selectedEditors);
415
+ }
416
+ if (editor) {
417
+ const editorId = resolveEditorId(editor);
418
+ return editorId ? [editorId] : void 0;
419
+ }
420
+ }
421
+ function detectExistingEditors(projectRoot) {
422
+ const editors = [];
423
+ for (const option of EDITORS) for (const fileName of Object.keys(option.files)) {
424
+ const filePath = path.join(projectRoot, option.targetDir, fileName);
425
+ if (fs.existsSync(filePath)) {
426
+ editors.push(option.id);
427
+ break;
428
+ }
429
+ }
430
+ return editors.length === 0 ? void 0 : editors;
431
+ }
432
+ /**
433
+ * Detect editor config files that would conflict (already exist).
434
+ * Read-only — does not write or modify any files.
435
+ */
436
+ function detectEditorConflicts({ projectRoot, editorId }) {
437
+ if (!editorId) return [];
438
+ const editorConfig = EDITORS.find((e) => e.id === editorId);
439
+ if (!editorConfig) return [];
440
+ const conflicts = [];
441
+ for (const fileName of Object.keys(editorConfig.files)) {
442
+ const filePath = path.join(projectRoot, editorConfig.targetDir, fileName);
443
+ if (fs.existsSync(filePath)) conflicts.push({
444
+ fileName,
445
+ displayPath: `${editorConfig.targetDir}/${fileName}`
446
+ });
447
+ }
448
+ return conflicts;
449
+ }
450
+ async function writeEditorConfigs({ projectRoot, editorId, interactive, conflictDecisions, silent = false, extraVsCodeSettings }) {
451
+ const editorIds = normalizeEditorSelection(editorId);
452
+ if (editorIds.length === 0) return;
453
+ for (const currentEditorId of editorIds) await writeEditorConfig({
454
+ projectRoot,
455
+ editorId: currentEditorId,
456
+ interactive,
457
+ conflictDecisions,
458
+ silent,
459
+ extraVsCodeSettings
460
+ });
461
+ }
462
+ async function writeEditorConfig({ projectRoot, editorId, interactive, conflictDecisions, silent, extraVsCodeSettings }) {
463
+ const editorConfig = EDITORS.find((e) => e.id === editorId);
464
+ if (!editorConfig) return;
465
+ const targetDir = path.join(projectRoot, editorConfig.targetDir);
466
+ await fsPromises.mkdir(targetDir, { recursive: true });
467
+ for (const [fileName, baseIncoming] of Object.entries(editorConfig.files)) {
468
+ const incoming = editorId === "vscode" && fileName === "settings.json" && extraVsCodeSettings ? {
469
+ ...extraVsCodeSettings,
470
+ ...baseIncoming
471
+ } : baseIncoming;
472
+ const filePath = path.join(targetDir, fileName);
473
+ if (fs.existsSync(filePath)) {
474
+ const displayPath = `${editorConfig.targetDir}/${fileName}`;
475
+ let conflictAction;
476
+ const preResolved = conflictDecisions?.get(displayPath) ?? conflictDecisions?.get(fileName);
477
+ if (preResolved) conflictAction = preResolved;
478
+ else if (interactive) {
479
+ const action = await select({
480
+ message: `${displayPath} already exists.\n ` + styleText("gray", `Vite+ adds ${editorConfig.label} settings for the built-in linter and formatter. Merge adds new keys without overwriting existing ones.`),
481
+ options: [{
482
+ label: "Merge",
483
+ value: "merge",
484
+ hint: "Merge new settings into existing file"
485
+ }, {
486
+ label: "Skip",
487
+ value: "skip",
488
+ hint: "Leave existing file unchanged"
489
+ }],
490
+ initialValue: "skip"
491
+ });
492
+ conflictAction = isCancel(action) || action === "skip" ? "skip" : "merge";
493
+ } else conflictAction = "merge";
494
+ if (conflictAction === "merge") mergeAndWriteEditorConfig(filePath, incoming, fileName, displayPath, silent);
495
+ else if (!silent) log.info(`Skipped writing ${displayPath}`);
496
+ continue;
497
+ }
498
+ writeJsonFile(filePath, incoming);
499
+ if (!silent) log.success(`Wrote editor config to ${editorConfig.targetDir}/${fileName}`);
500
+ }
501
+ }
502
+ function normalizeEditorSelection(editorId) {
503
+ if (!editorId) return [];
504
+ return [...new Set(Array.isArray(editorId) ? editorId : [editorId])];
505
+ }
506
+ /**
507
+ * Merge incoming settings into an existing editor JSON/JSONC file by patching the
508
+ * original text with `jsonc-parser` instead of re-serializing a merged object.
509
+ * This preserves comments, key order, trailing commas, and untouched formatting.
510
+ * Existing values always win; only missing keys/branches are inserted.
511
+ */
512
+ function mergeAndWriteEditorConfig(filePath, incoming, fileName, displayPath, silent = false) {
513
+ const originalText = fs.readFileSync(filePath, "utf-8");
514
+ const existing = parse(originalText);
515
+ if (!isPlainObject(existing)) throw new Error(`Cannot merge editor config: ${displayPath} is not a JSON object`);
516
+ const formattingOptions = detectFormattingOptions(originalText);
517
+ const newText = fileName === "extensions.json" ? mergeExtensionsText(originalText, existing, incoming, formattingOptions) : mergeSettingsText(originalText, existing, incoming, formattingOptions);
518
+ if (newText === originalText) {
519
+ if (!silent) log.info(`No changes needed for ${displayPath}`);
520
+ return;
521
+ }
522
+ fs.writeFileSync(filePath, newText, "utf-8");
523
+ if (!silent) log.success(`Merged editor config into ${displayPath}`);
524
+ }
525
+ function isPlainObject(value) {
526
+ return typeof value === "object" && value !== null && !Array.isArray(value);
527
+ }
528
+ /** Apply a single `jsonc-parser` modification to `text` and return the patched text. */
529
+ function applyJsoncEdit(text, path, value, options) {
530
+ return applyEdits(text, modify(text, path, value, options));
531
+ }
532
+ /**
533
+ * Deep-merge missing keys from `incoming` into the existing text. Inserts a whole
534
+ * branch when it is absent, and recurses only when both sides are non-array objects
535
+ * so comments inside existing branches are preserved.
536
+ */
537
+ function mergeSettingsText(text, existing, incoming, formattingOptions) {
538
+ let currentText = text;
539
+ const insertMissing = (existingNode, incomingNode, basePath) => {
540
+ for (const [key, value] of Object.entries(incomingNode)) {
541
+ const fullPath = [...basePath, key];
542
+ if (!(key in existingNode)) currentText = applyJsoncEdit(currentText, fullPath, value, { formattingOptions });
543
+ else if (isPlainObject(existingNode[key]) && isPlainObject(value)) insertMissing(existingNode[key], value, fullPath);
544
+ }
545
+ };
546
+ insertMissing(existing, incoming, []);
547
+ return currentText;
548
+ }
549
+ /**
550
+ * For `extensions.json`, append missing recommendations without rebuilding the array,
551
+ * so comments inside the array survive. Existing entries always win.
552
+ */
553
+ function mergeExtensionsText(text, existing, incoming, formattingOptions) {
554
+ const incomingRecs = Array.isArray(incoming["recommendations"]) ? incoming["recommendations"] : [];
555
+ const existingValue = existing["recommendations"];
556
+ if (!("recommendations" in existing)) return applyJsoncEdit(text, ["recommendations"], incomingRecs, { formattingOptions });
557
+ if (!Array.isArray(existingValue)) return text;
558
+ const existingRecs = new Set(existingValue);
559
+ let currentText = text;
560
+ let nextIndex = existingValue.length;
561
+ for (const rec of incomingRecs) {
562
+ if (existingRecs.has(rec)) continue;
563
+ currentText = applyJsoncEdit(currentText, ["recommendations", nextIndex], rec, {
564
+ formattingOptions,
565
+ isArrayInsertion: true
566
+ });
567
+ nextIndex++;
568
+ }
569
+ return currentText;
570
+ }
571
+ function resolveEditorId(editor) {
572
+ const normalized = editor.trim().toLowerCase();
573
+ return EDITORS.find((option) => option.id === normalized || option.label.toLowerCase() === normalized)?.id;
574
+ }
575
+ function resolveEditorIds(editors) {
576
+ const editorIds = editors.flatMap((editor) => {
577
+ const editorId = resolveEditorId(editor);
578
+ return editorId ? [editorId] : [];
579
+ });
580
+ const uniqueEditorIds = [...new Set(editorIds)];
581
+ return uniqueEditorIds.length === 0 ? void 0 : uniqueEditorIds;
582
+ }
583
+ //#endregion
584
+ export { writeEditorConfigs as a, hasFrameworkShim as c, migratePrettierToOxfmt as d, promptPrettierMigration as f, selectEditors as i, confirmPrettierMigration as l, detectExistingEditors as n, addFrameworkShim as o, warnPackageLevelPrettier as p, selectEditor as r, detectFramework as s, detectEditorConflicts as t, detectPrettierProject as u };
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_define_config = require("./define-config-BGSjF6Xp.cjs");
1
+ const require_define_config = require("./define-config-Dut6XD42.cjs");
2
2
  //#region src/index.cts
3
3
  const vite = require("@voidzero-dev/vite-plus-core");
4
4
  const { configDefaults, coverageConfigDefaults, defaultBrowserPort, defaultExclude, defaultInclude } = require("vitest/config");
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as defineConfig, c as lazyPlugins, o as defineProject } from "./define-config-2tfJoXr1.js";
1
+ import { a as defineConfig, c as lazyPlugins, o as defineProject } from "./define-config-BuMs_LKa.js";
2
2
  import { TestProjectConfiguration, TestProjectInlineConfiguration, TestTagDefinition, TestUserConfig, UserProjectConfigExport, UserProjectConfigFn, UserWorkspaceConfig, ViteUserConfig, ViteUserConfigExport, ViteUserConfigFn, ViteUserConfigFnObject, ViteUserConfigFnPromise, WatcherTriggerPattern, configDefaults, coverageConfigDefaults, defaultBrowserPort, defaultExclude, defaultInclude } from "vitest/config";
3
3
  export * from "@voidzero-dev/vite-plus-core";
4
4
  export { type TestProjectConfiguration, type TestProjectInlineConfiguration, type TestTagDefinition, type TestUserConfig, type UserProjectConfigExport, type UserProjectConfigFn, type UserWorkspaceConfig, type ViteUserConfig, type ViteUserConfigExport, type ViteUserConfigFn, type ViteUserConfigFnObject, type ViteUserConfigFnPromise, type WatcherTriggerPattern, configDefaults, coverageConfigDefaults, defaultBrowserPort, defaultExclude, defaultInclude, defineConfig, defineProject, lazyPlugins };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as defineConfig, c as lazyPlugins, o as defineProject } from "./define-config-DJUehepE.js";
1
+ import { a as defineConfig, c as lazyPlugins, o as defineProject } from "./define-config-D2jo1xVA.js";
2
2
  import { configDefaults, coverageConfigDefaults, defaultBrowserPort, defaultExclude, defaultInclude } from "vitest/config";
3
3
  export * from "@voidzero-dev/vite-plus-core";
4
4
  export { configDefaults, coverageConfigDefaults, defaultBrowserPort, defaultExclude, defaultInclude, defineConfig, defineProject, lazyPlugins };
@@ -1332,6 +1332,18 @@ function detectNewline(string) {
1332
1332
  }
1333
1333
  //#endregion
1334
1334
  //#region src/utils/json.ts
1335
+ /**
1336
+ * Derive `jsonc-parser` formatting options from existing file text so inserted
1337
+ * fragments match the file's indentation and newline style.
1338
+ */
1339
+ function detectFormattingOptions(text) {
1340
+ const detected = detectIndent(text);
1341
+ return {
1342
+ insertSpaces: detected.type !== "tab",
1343
+ tabSize: detected.amount || 2,
1344
+ eol: detectNewline(text) ?? "\n"
1345
+ };
1346
+ }
1335
1347
  function readJsonFile(file, allowComments) {
1336
1348
  const content = fs.readFileSync(file, "utf-8");
1337
1349
  return (allowComments ? parse : JSON.parse)(content);
@@ -1359,4 +1371,4 @@ function isJsonFile(file) {
1359
1371
  }
1360
1372
  }
1361
1373
  //#endregion
1362
- export { applyEdits as a, writeJsonFile as i, isJsonFile as n, modify as o, readJsonFile as r, parse as s, editJsonFile as t };
1374
+ export { writeJsonFile as a, parse as c, readJsonFile as i, editJsonFile as n, applyEdits as o, isJsonFile as r, modify as s, detectFormattingOptions as t };
@@ -1,6 +1,6 @@
1
1
  import { a as ansiStyles, t as stripAnsi } from "./strip-ansi-C3wrWz9t.js";
2
2
  import { t as isFullwidthCodePoint } from "./is-fullwidth-code-point-BUNlIICg.js";
3
- import { t as wrapAnsi } from "./wrap-ansi-k7Dn4VtV.js";
3
+ import { t as wrapAnsi } from "./wrap-ansi-DNjkuBEp.js";
4
4
  import process$1 from "node:process";
5
5
  import os from "node:os";
6
6
  //#region ../../node_modules/.pnpm/environment@1.1.0/node_modules/environment/index.js
@@ -338,7 +338,7 @@ cliCursor.toggle = (force, writableStream) => {
338
338
  };
339
339
  //#endregion
340
340
  //#region ../../node_modules/.pnpm/slice-ansi@7.1.2/node_modules/slice-ansi/index.js
341
- const ESCAPES = new Set([27, 155]);
341
+ const ESCAPES = /* @__PURE__ */ new Set([27, 155]);
342
342
  const CODE_POINT_0 = "0".codePointAt(0);
343
343
  const CODE_POINT_9 = "9".codePointAt(0);
344
344
  const MAX_ANSI_SEQUENCE_LENGTH = 19;
@@ -1,10 +1 @@
1
- //#region src/migration/bin.d.ts
2
- interface MigrationOptions {
3
- interactive: boolean;
4
- help?: boolean;
5
- agent?: string | string[] | false;
6
- editor?: string | false;
7
- hooks?: boolean;
8
- }
9
- //#endregion
10
- export { MigrationOptions };
1
+ export { };