From 353203880d5ac26d28980180f7f51b4ec802f33e Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Fri, 17 Jul 2026 08:41:28 +0000
Subject: [PATCH 01/19] feat(create): implement support for JetBrains editors
---
.../cli/src/utils/__tests__/editor.spec.ts | 40 ++++++++++
packages/cli/src/utils/editor.ts | 80 +++++++++++++++++--
2 files changed, 112 insertions(+), 8 deletions(-)
diff --git a/packages/cli/src/utils/__tests__/editor.spec.ts b/packages/cli/src/utils/__tests__/editor.spec.ts
index 12eeabd64c..9a329ed433 100644
--- a/packages/cli/src/utils/__tests__/editor.spec.ts
+++ b/packages/cli/src/utils/__tests__/editor.spec.ts
@@ -91,6 +91,14 @@ describe('detectExistingEditors', () => {
it('returns undefined when no editor config files exist', () => {
expect(detectExistingEditors(createTempDir())).toBeUndefined();
});
+
+ it('detects existing jetbrains editor config files', () => {
+ const projectRoot = createTempDir();
+ fs.mkdirSync(path.join(projectRoot, '.idea'), { recursive: true });
+ fs.writeFileSync(path.join(projectRoot, '.idea', 'externalDependencies.xml'), '');
+
+ expect(detectExistingEditors(projectRoot)).toEqual(['jetbrains']);
+ });
});
describe('writeEditorConfigs', () => {
@@ -539,4 +547,36 @@ describe('writeEditorConfigs', () => {
expect(zedSettings['npm.scriptRunner']).toBeUndefined();
expect(zedSettings.lsp).toBeDefined();
});
+
+ it('writes jetbrains config as XML based on file extension', async () => {
+ const projectRoot = createTempDir();
+
+ await writeEditorConfigs({
+ projectRoot,
+ editorId: 'jetbrains',
+ interactive: false,
+ silent: true,
+ });
+
+ const xml = fs.readFileSync(path.join(projectRoot, '.idea', 'externalDependencies.xml'), 'utf8');
+ expect(xml).toContain('');
+ expect(xml).toContain('');
+ });
+
+ it('does not overwrite existing non-JSON editor config in non-interactive mode', async () => {
+ const projectRoot = createTempDir();
+ const xmlPath = path.join(projectRoot, '.idea', 'externalDependencies.xml');
+ fs.mkdirSync(path.dirname(xmlPath), { recursive: true });
+ fs.writeFileSync(xmlPath, '', 'utf8');
+
+ await writeEditorConfigs({
+ projectRoot,
+ editorId: 'jetbrains',
+ interactive: false,
+ silent: true,
+ });
+
+ const xml = fs.readFileSync(xmlPath, 'utf8');
+ expect(xml).toBe('');
+ });
});
diff --git a/packages/cli/src/utils/editor.ts b/packages/cli/src/utils/editor.ts
index 9688b36397..e210841ed0 100644
--- a/packages/cli/src/utils/editor.ts
+++ b/packages/cli/src/utils/editor.ts
@@ -150,6 +150,17 @@ const ZED_SETTINGS = {
},
} as const;
+const JETBRAINS_EXTERNAL_DEPENDENCIES = `
+
+
+
+
+
+
+`;
+
+type EditorConfigValue = Record | string;
+
export const EDITORS = [
{
id: 'vscode',
@@ -168,6 +179,14 @@ export const EDITORS = [
'settings.json': ZED_SETTINGS as Record,
},
},
+ {
+ id: 'jetbrains',
+ label: 'JetBrains (IntelliJ, WebStorm, etc)',
+ targetDir: '.idea',
+ files: {
+ 'externalDependencies.xml': JETBRAINS_EXTERNAL_DEPENDENCIES,
+ },
+ },
] as const;
export type EditorId = (typeof EDITORS)[number]['id'];
@@ -386,11 +405,12 @@ async function writeEditorConfig({
await fsPromises.mkdir(targetDir, { recursive: true });
for (const [fileName, baseIncoming] of Object.entries(editorConfig.files)) {
- const incoming =
+ const incoming: EditorConfigValue =
editorId === 'vscode' && fileName === 'settings.json' && extraVsCodeSettings
? { ...extraVsCodeSettings, ...baseIncoming }
: baseIncoming;
const filePath = path.join(targetDir, fileName);
+ const jsonFormat = isJsonLikeFile(fileName);
if (fs.existsSync(filePath)) {
const displayPath = `${editorConfig.targetDir}/${fileName}`;
@@ -406,13 +426,17 @@ async function writeEditorConfig({
`${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.`,
+ jsonFormat
+ ? `Vite+ adds ${editorConfig.label} settings for the built-in linter and formatter. Merge adds new keys without overwriting existing ones.`
+ : `Vite+ adds ${editorConfig.label} settings for the built-in linter and formatter. Overwrite replaces the existing file with the generated config.`,
),
options: [
{
- label: 'Merge',
+ label: jsonFormat ? 'Merge' : 'Overwrite',
value: 'merge',
- hint: 'Merge new settings into existing file',
+ hint: jsonFormat
+ ? 'Merge new settings into existing file'
+ : 'Replace existing file with generated config',
},
{
label: 'Skip',
@@ -424,12 +448,19 @@ async function writeEditorConfig({
});
conflictAction = prompts.isCancel(action) || action === 'skip' ? 'skip' : 'merge';
} else {
- // Non-interactive: always merge (safe because existing keys are never overwritten)
- conflictAction = 'merge';
+ // Non-interactive: merge JSON safely, skip non-JSON to avoid destructive overwrite.
+ conflictAction = jsonFormat ? 'merge' : 'skip';
}
if (conflictAction === 'merge') {
- mergeAndWriteEditorConfig(filePath, incoming, fileName, displayPath, silent);
+ if (jsonFormat) {
+ if (!isPlainObject(incoming)) {
+ throw new Error(`Cannot merge editor config: ${displayPath} incoming value is not JSON`);
+ }
+ mergeAndWriteEditorConfig(filePath, incoming, fileName, displayPath, silent);
+ } else {
+ writeTextEditorConfig(filePath, incoming, displayPath, silent);
+ }
} else {
if (!silent) {
prompts.log.info(`Skipped writing ${displayPath}`);
@@ -438,13 +469,46 @@ async function writeEditorConfig({
continue;
}
- writeJsonFile(filePath, incoming);
+ if (jsonFormat) {
+ if (!isPlainObject(incoming)) {
+ throw new Error(`Cannot write editor config: ${editorConfig.targetDir}/${fileName} must be JSON`);
+ }
+ writeJsonFile(filePath, incoming);
+ } else {
+ writeTextEditorConfig(filePath, incoming, `${editorConfig.targetDir}/${fileName}`, silent);
+ }
if (!silent) {
prompts.log.success(`Wrote editor config to ${editorConfig.targetDir}/${fileName}`);
}
}
}
+function isJsonLikeFile(fileName: string): boolean {
+ const ext = path.extname(fileName).toLowerCase();
+ return ext === '.json' || ext === '.jsonc';
+}
+
+function writeTextEditorConfig(
+ filePath: string,
+ incoming: EditorConfigValue,
+ displayPath: string,
+ silent = false,
+) {
+ if (typeof incoming !== 'string') {
+ throw new Error(`Cannot write editor config: ${displayPath} must be text content`);
+ }
+
+ const existingText = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf-8') : undefined;
+ if (existingText === incoming) {
+ if (!silent) {
+ prompts.log.info(`No changes needed for ${displayPath}`);
+ }
+ return;
+ }
+
+ fs.writeFileSync(filePath, incoming, 'utf-8');
+}
+
function normalizeEditorSelection(editorId: EditorSelection): EditorId[] {
if (!editorId) {
return [];
From c56386fa7c7c0125078790bc487ba2b672bab334 Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Sat, 25 Jul 2026 09:50:27 +0000
Subject: [PATCH 02/19] WIP docs on JetBrains
---
docs/guide/ide-integration.md | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/docs/guide/ide-integration.md b/docs/guide/ide-integration.md
index 554da52e4f..0d8b23d8fd 100644
--- a/docs/guide/ide-integration.md
+++ b/docs/guide/ide-integration.md
@@ -99,3 +99,15 @@ You can also manually set up the Zed config:
```
Setting `oxfmt.fmt.configPath` to `./vite.config.ts` keeps editor format-on-save aligned with the `fmt` block in your Vite+ config. The full generated config covers additional languages (CSS, HTML, JSON, Markdown, etc.) — run `vp create` or `vp migrate` to get the complete file written automatically.
+
+## JetBrains (IntelliJ, WebStorm, etc...)
+
+For the best Vite+ experience with JetBrains IDEs such as IntelliJ & WebStorm, install the [Oxc](https://plugins.jetbrains.com/plugin/27061-oxc) plugin from the JetBrains marketplace.
+
+When you create or migrate a project, Vite+ prompts you to choose whether you want the editor config written for Zed.
+
+You can also manually set up the IDE configuration to utilise Oxc:
+
+```json
+
+```
From 07bdba133bae3a8a1a2c2efaaeef6bc25b176e7b Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Sun, 26 Jul 2026 05:55:30 +0000
Subject: [PATCH 03/19] some todos
---
packages/cli/src/utils/editor.ts | 26 ++++++++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)
diff --git a/packages/cli/src/utils/editor.ts b/packages/cli/src/utils/editor.ts
index 9ab3598e1b..c23b222518 100644
--- a/packages/cli/src/utils/editor.ts
+++ b/packages/cli/src/utils/editor.ts
@@ -150,6 +150,8 @@ const ZED_SETTINGS = {
},
} as const;
+// TODO Replace this raw XML template with a JSON-object definition
+// once the XML parser/merge support below is in place.
const JETBRAINS_EXTERNAL_DEPENDENCIES = `
@@ -159,6 +161,8 @@ const JETBRAINS_EXTERNAL_DEPENDENCIES = `
`;
+// TODO: Extend this to allow XML files to be authored as JSON objects too
+// e.g. `Record | string` -> add a dedicated XML-object type once an XML parser/differ is written
type EditorConfigValue = Record | string;
export const EDITORS = [
@@ -189,6 +193,15 @@ export const EDITORS = [
},
] as const;
+// Since some file types may not be obvious (e.g. `.editorconfig`), it may be beneficial to add a property for declaring file type overrides for both read and write
+// The only problem is that now we have to maintain a list of file types and their extensions, which is not trivial. For now, we can just use the file extension to determine the file type and just deal with corner-case scenarios as we go.
+// TODO: Replace values with custom parser classes/objects implemented specifically for each file type
+export const FILE_TYPE_MAPPINGS = {
+ '.json': 'jsonc',
+ '.jsonc': 'jsonc',
+ '.xml': 'xml',
+}
+
export type EditorId = (typeof EDITORS)[number]['id'];
type EditorSelection = EditorId | readonly EditorId[] | undefined;
@@ -451,7 +464,9 @@ async function writeEditorConfig({
if (conflictAction === 'merge') {
if (jsonFormat) {
if (!isPlainObject(incoming)) {
- throw new Error(`Cannot merge editor config: ${displayPath} incoming value is not JSON`);
+ throw new Error(
+ `Cannot merge editor config: ${displayPath} incoming value is not JSON`,
+ );
}
mergeAndWriteEditorConfig(filePath, incoming, fileName, displayPath, silent);
} else {
@@ -467,7 +482,9 @@ async function writeEditorConfig({
if (jsonFormat) {
if (!isPlainObject(incoming)) {
- throw new Error(`Cannot write editor config: ${editorConfig.targetDir}/${fileName} must be JSON`);
+ throw new Error(
+ `Cannot write editor config: ${editorConfig.targetDir}/${fileName} must be JSON`,
+ );
}
writeJsonFile(filePath, incoming);
} else {
@@ -484,6 +501,9 @@ function isJsonLikeFile(fileName: string): boolean {
return ext === '.json' || ext === '.jsonc';
}
+// TODO: Add an `isXmlFile()` extension check (`.xml`) alongside `isJsonLikeFile()`,
+// and dispatch `.xml` files to write/merge instead of write/skip-on-conflict
+// See `writeEditorConfig()`'s `jsonFormat` branching for the call sites that need a parallel `xmlFormat` branch (conflict prompt copy, merge dispatch, initial write).
function writeTextEditorConfig(
filePath: string,
incoming: EditorConfigValue,
@@ -502,6 +522,8 @@ function writeTextEditorConfig(
return;
}
+ // TODO: Once XML merge support exists, this plain overwrite-or-skip behavior should be replaced with a merge that preserves comments and formatting, similar to `mergeAndWriteEditorConfig()`.
+ // Likely will need to be extended for other file types too, so consider a generic `mergeAndWriteFile()` that dispatches to JSON/XML/text merge strategies based on file extension.
fs.writeFileSync(filePath, incoming, 'utf-8');
}
From 8b9cc42c2ca6b38df3f6a8559db95fb9e2444702 Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Sun, 26 Jul 2026 06:34:38 +0000
Subject: [PATCH 04/19] Finish IDE integration docs page
---
docs/guide/ide-integration.md | 49 ++++++++++++++++++++++++++++++++---
1 file changed, 46 insertions(+), 3 deletions(-)
diff --git a/docs/guide/ide-integration.md b/docs/guide/ide-integration.md
index 0d8b23d8fd..0704db4266 100644
--- a/docs/guide/ide-integration.md
+++ b/docs/guide/ide-integration.md
@@ -104,10 +104,53 @@ Setting `oxfmt.fmt.configPath` to `./vite.config.ts` keeps editor format-on-save
For the best Vite+ experience with JetBrains IDEs such as IntelliJ & WebStorm, install the [Oxc](https://plugins.jetbrains.com/plugin/27061-oxc) plugin from the JetBrains marketplace.
-When you create or migrate a project, Vite+ prompts you to choose whether you want the editor config written for Zed.
+When you create or migrate a project, Vite+ prompts you to choose whether you want the editor config written for JetBrains IDEs.
+
+::: tip Vite+ does not merge with existing config files
+Due to some complexities with merging XML files, Vite+ currently does not merge your current files if the files already exist.
+You'll be given the opportunity to replace your files, instead of merging.
+:::
+
+You can also manually set up the IDE configuration to match your Vite+ setup:
+
+```xml [.idea/externalDependencies.xml]
+
+
+
+
+
+
+
+```
-You can also manually set up the IDE configuration to utilise Oxc:
+```xml [.idea/workspace.xml]
+
+
+
+
+
+
+
+```
-```json
+```xml [.idea/OxfmtSettings.xml]
+
+
+
+
+
+
+```
+```gitignore [.idea/.gitignore]
+!externalDependencies.xml
```
From 81a098fcb57027f88de183c45301ceb04e04a3dc Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Sun, 26 Jul 2026 06:36:34 +0000
Subject: [PATCH 05/19] oops
---
docs/guide/ide-integration.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/guide/ide-integration.md b/docs/guide/ide-integration.md
index 0704db4266..71c92cab27 100644
--- a/docs/guide/ide-integration.md
+++ b/docs/guide/ide-integration.md
@@ -134,7 +134,7 @@ You can also manually set up the IDE configuration to match your Vite+ setup:
"javascript.nodejs.core.library.configured.version": "24.18.0", // Replace with your selected Node.js version
"javascript.nodejs.core.library.typings.version": "24.13.3", // Replace with the version of @types/node that corresponds to your runtime (or omit if you don't want it)
"javascript.preferred.runtime.type.id": "node",
- "nodejs_interpreter_path": "~/.vite-plus/bin/node",
+ "nodejs_interpreter_path": "~/.vite-plus/bin/node", // Replace ~ with the path to your home directory, IntelliJ/WebStorm don't understand ~
"nodejs_package_manager_path": "pnpm", // Replace with your package manager of choice
}
}]]>
From 08155c8c5b32f17729486958cc941dc4c1a7dac6 Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Wed, 5 Aug 2026 06:16:56 +0000
Subject: [PATCH 06/19] Revert "some todos"
This reverts commit 07bdba133bae3a8a1a2c2efaaeef6bc25b176e7b.
---
packages/cli/src/utils/editor.ts | 26 ++------------------------
1 file changed, 2 insertions(+), 24 deletions(-)
diff --git a/packages/cli/src/utils/editor.ts b/packages/cli/src/utils/editor.ts
index c23b222518..9ab3598e1b 100644
--- a/packages/cli/src/utils/editor.ts
+++ b/packages/cli/src/utils/editor.ts
@@ -150,8 +150,6 @@ const ZED_SETTINGS = {
},
} as const;
-// TODO Replace this raw XML template with a JSON-object definition
-// once the XML parser/merge support below is in place.
const JETBRAINS_EXTERNAL_DEPENDENCIES = `
@@ -161,8 +159,6 @@ const JETBRAINS_EXTERNAL_DEPENDENCIES = `
`;
-// TODO: Extend this to allow XML files to be authored as JSON objects too
-// e.g. `Record | string` -> add a dedicated XML-object type once an XML parser/differ is written
type EditorConfigValue = Record | string;
export const EDITORS = [
@@ -193,15 +189,6 @@ export const EDITORS = [
},
] as const;
-// Since some file types may not be obvious (e.g. `.editorconfig`), it may be beneficial to add a property for declaring file type overrides for both read and write
-// The only problem is that now we have to maintain a list of file types and their extensions, which is not trivial. For now, we can just use the file extension to determine the file type and just deal with corner-case scenarios as we go.
-// TODO: Replace values with custom parser classes/objects implemented specifically for each file type
-export const FILE_TYPE_MAPPINGS = {
- '.json': 'jsonc',
- '.jsonc': 'jsonc',
- '.xml': 'xml',
-}
-
export type EditorId = (typeof EDITORS)[number]['id'];
type EditorSelection = EditorId | readonly EditorId[] | undefined;
@@ -464,9 +451,7 @@ async function writeEditorConfig({
if (conflictAction === 'merge') {
if (jsonFormat) {
if (!isPlainObject(incoming)) {
- throw new Error(
- `Cannot merge editor config: ${displayPath} incoming value is not JSON`,
- );
+ throw new Error(`Cannot merge editor config: ${displayPath} incoming value is not JSON`);
}
mergeAndWriteEditorConfig(filePath, incoming, fileName, displayPath, silent);
} else {
@@ -482,9 +467,7 @@ async function writeEditorConfig({
if (jsonFormat) {
if (!isPlainObject(incoming)) {
- throw new Error(
- `Cannot write editor config: ${editorConfig.targetDir}/${fileName} must be JSON`,
- );
+ throw new Error(`Cannot write editor config: ${editorConfig.targetDir}/${fileName} must be JSON`);
}
writeJsonFile(filePath, incoming);
} else {
@@ -501,9 +484,6 @@ function isJsonLikeFile(fileName: string): boolean {
return ext === '.json' || ext === '.jsonc';
}
-// TODO: Add an `isXmlFile()` extension check (`.xml`) alongside `isJsonLikeFile()`,
-// and dispatch `.xml` files to write/merge instead of write/skip-on-conflict
-// See `writeEditorConfig()`'s `jsonFormat` branching for the call sites that need a parallel `xmlFormat` branch (conflict prompt copy, merge dispatch, initial write).
function writeTextEditorConfig(
filePath: string,
incoming: EditorConfigValue,
@@ -522,8 +502,6 @@ function writeTextEditorConfig(
return;
}
- // TODO: Once XML merge support exists, this plain overwrite-or-skip behavior should be replaced with a merge that preserves comments and formatting, similar to `mergeAndWriteEditorConfig()`.
- // Likely will need to be extended for other file types too, so consider a generic `mergeAndWriteFile()` that dispatches to JSON/XML/text merge strategies based on file extension.
fs.writeFileSync(filePath, incoming, 'utf-8');
}
From add16266fd49bf296c5a5d14e5dc104738ca38fe Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Wed, 5 Aug 2026 06:47:19 +0000
Subject: [PATCH 07/19] feat: auto-add other relevant files when selecting
JetBrains
---
docs/guide/ide-integration.md | 4 +-
.../cli/src/utils/__tests__/editor.spec.ts | 51 ++++++++++++++++---
packages/cli/src/utils/editor.ts | 36 ++++++++++++-
3 files changed, 81 insertions(+), 10 deletions(-)
diff --git a/docs/guide/ide-integration.md b/docs/guide/ide-integration.md
index 71c92cab27..7786b7b886 100644
--- a/docs/guide/ide-integration.md
+++ b/docs/guide/ide-integration.md
@@ -108,7 +108,7 @@ When you create or migrate a project, Vite+ prompts you to choose whether you wa
::: tip Vite+ does not merge with existing config files
Due to some complexities with merging XML files, Vite+ currently does not merge your current files if the files already exist.
-You'll be given the opportunity to replace your files, instead of merging.
+You'll be given the opportunity to replace any existing files, instead of merging.
:::
You can also manually set up the IDE configuration to match your Vite+ setup:
@@ -134,7 +134,7 @@ You can also manually set up the IDE configuration to match your Vite+ setup:
"javascript.nodejs.core.library.configured.version": "24.18.0", // Replace with your selected Node.js version
"javascript.nodejs.core.library.typings.version": "24.13.3", // Replace with the version of @types/node that corresponds to your runtime (or omit if you don't want it)
"javascript.preferred.runtime.type.id": "node",
- "nodejs_interpreter_path": "~/.vite-plus/bin/node", // Replace ~ with the path to your home directory, IntelliJ/WebStorm don't understand ~
+ "nodejs_interpreter_path": "~/.vite-plus/bin/node", // Replace ~ with the path to your home directory, IntelliJ/WebStorm doesn't understand ~
"nodejs_package_manager_path": "pnpm", // Replace with your package manager of choice
}
}]]>
diff --git a/packages/cli/src/utils/__tests__/editor.spec.ts b/packages/cli/src/utils/__tests__/editor.spec.ts
index 9a329ed433..9fb46fa9a0 100644
--- a/packages/cli/src/utils/__tests__/editor.spec.ts
+++ b/packages/cli/src/utils/__tests__/editor.spec.ts
@@ -95,7 +95,7 @@ describe('detectExistingEditors', () => {
it('detects existing jetbrains editor config files', () => {
const projectRoot = createTempDir();
fs.mkdirSync(path.join(projectRoot, '.idea'), { recursive: true });
- fs.writeFileSync(path.join(projectRoot, '.idea', 'externalDependencies.xml'), '');
+ fs.writeFileSync(path.join(projectRoot, '.idea', 'workspace.xml'), '');
expect(detectExistingEditors(projectRoot)).toEqual(['jetbrains']);
});
@@ -548,7 +548,7 @@ describe('writeEditorConfigs', () => {
expect(zedSettings.lsp).toBeDefined();
});
- it('writes jetbrains config as XML based on file extension', async () => {
+ it('writes all jetbrains editor config files', async () => {
const projectRoot = createTempDir();
await writeEditorConfigs({
@@ -558,12 +558,39 @@ describe('writeEditorConfigs', () => {
silent: true,
});
- const xml = fs.readFileSync(path.join(projectRoot, '.idea', 'externalDependencies.xml'), 'utf8');
- expect(xml).toContain('');
- expect(xml).toContain('');
+ const externalDependenciesXml = fs.readFileSync(
+ path.join(projectRoot, '.idea', 'externalDependencies.xml'),
+ 'utf8',
+ );
+ expect(externalDependenciesXml).toContain('');
+ expect(externalDependenciesXml).toContain(
+ '',
+ );
+ expect(externalDependenciesXml).toContain('');
+
+ const workspaceXml = fs.readFileSync(path.join(projectRoot, '.idea', 'workspace.xml'), 'utf8');
+ expect(workspaceXml).toContain('');
+ expect(workspaceXml).toContain('"javascript.preferred.runtime.type.id": "node"');
+ expect(workspaceXml).toContain(
+ `"nodejs_interpreter_path": "${os.homedir()}/.vite-plus/bin/node"`,
+ );
+ expect(workspaceXml).toContain('"nodejs_package_manager_path": "pnpm"');
+
+ const oxfmtSettingsXml = fs.readFileSync(
+ path.join(projectRoot, '.idea', 'OxfmtSettings.xml'),
+ 'utf8',
+ );
+ expect(oxfmtSettingsXml).toContain('');
+ expect(oxfmtSettingsXml).toContain('');
+ expect(oxfmtSettingsXml).toContain(
+ '',
+ );
+
+ const gitignore = fs.readFileSync(path.join(projectRoot, '.idea', '.gitignore'), 'utf8');
+ expect(gitignore).toBe('!externalDependencies.xml');
});
- it('does not overwrite existing non-JSON editor config in non-interactive mode', async () => {
+ it('does not overwrite existing non-JSON jetbrains file in non-interactive mode', async () => {
const projectRoot = createTempDir();
const xmlPath = path.join(projectRoot, '.idea', 'externalDependencies.xml');
fs.mkdirSync(path.dirname(xmlPath), { recursive: true });
@@ -578,5 +605,17 @@ describe('writeEditorConfigs', () => {
const xml = fs.readFileSync(xmlPath, 'utf8');
expect(xml).toBe('');
+
+ const workspaceXml = fs.readFileSync(path.join(projectRoot, '.idea', 'workspace.xml'), 'utf8');
+ expect(workspaceXml).toContain('');
+
+ const oxfmtSettingsXml = fs.readFileSync(
+ path.join(projectRoot, '.idea', 'OxfmtSettings.xml'),
+ 'utf8',
+ );
+ expect(oxfmtSettingsXml).toContain('');
+
+ const gitignore = fs.readFileSync(path.join(projectRoot, '.idea', '.gitignore'), 'utf8');
+ expect(gitignore).toBe('!externalDependencies.xml');
});
});
diff --git a/packages/cli/src/utils/editor.ts b/packages/cli/src/utils/editor.ts
index 9ab3598e1b..ff87ecdd91 100644
--- a/packages/cli/src/utils/editor.ts
+++ b/packages/cli/src/utils/editor.ts
@@ -1,5 +1,6 @@
import fs from 'node:fs';
import fsPromises from 'node:fs/promises';
+import os from 'node:os';
import path from 'node:path';
import { styleText } from 'node:util';
@@ -159,6 +160,30 @@ const JETBRAINS_EXTERNAL_DEPENDENCIES = `
`;
+const JETBRAINS_WORKSPACE_CONFIG = `
+
+
+
+
+
+`;
+
+const JETBRAINS_OXFMT_SETTINGS = `
+
+
+
+
+
+`;
+
+const JETBRAINS_GITIGNORE_ADDITION = `!externalDependencies.xml`
+
type EditorConfigValue = Record | string;
export const EDITORS = [
@@ -185,6 +210,9 @@ export const EDITORS = [
targetDir: '.idea',
files: {
'externalDependencies.xml': JETBRAINS_EXTERNAL_DEPENDENCIES,
+ 'workspace.xml': JETBRAINS_WORKSPACE_CONFIG,
+ 'OxfmtSettings.xml': JETBRAINS_OXFMT_SETTINGS,
+ '.gitignore': JETBRAINS_GITIGNORE_ADDITION,
},
},
] as const;
@@ -451,7 +479,9 @@ async function writeEditorConfig({
if (conflictAction === 'merge') {
if (jsonFormat) {
if (!isPlainObject(incoming)) {
- throw new Error(`Cannot merge editor config: ${displayPath} incoming value is not JSON`);
+ throw new Error(
+ `Cannot merge editor config: ${displayPath} incoming value is not JSON`,
+ );
}
mergeAndWriteEditorConfig(filePath, incoming, fileName, displayPath, silent);
} else {
@@ -467,7 +497,9 @@ async function writeEditorConfig({
if (jsonFormat) {
if (!isPlainObject(incoming)) {
- throw new Error(`Cannot write editor config: ${editorConfig.targetDir}/${fileName} must be JSON`);
+ throw new Error(
+ `Cannot write editor config: ${editorConfig.targetDir}/${fileName} must be JSON`,
+ );
}
writeJsonFile(filePath, incoming);
} else {
From 823a04c4b6562d6204c1244800631a49ea3df8a1 Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Wed, 5 Aug 2026 06:51:51 +0000
Subject: [PATCH 08/19] feat: Remove intellij.vitejs from
externalDependencies.xml file preset for now
---
docs/guide/ide-integration.md | 1 -
packages/cli/src/utils/__tests__/editor.spec.ts | 1 -
packages/cli/src/utils/editor.ts | 1 -
3 files changed, 3 deletions(-)
diff --git a/docs/guide/ide-integration.md b/docs/guide/ide-integration.md
index 7786b7b886..e281633015 100644
--- a/docs/guide/ide-integration.md
+++ b/docs/guide/ide-integration.md
@@ -118,7 +118,6 @@ You can also manually set up the IDE configuration to match your Vite+ setup:
-
```
diff --git a/packages/cli/src/utils/__tests__/editor.spec.ts b/packages/cli/src/utils/__tests__/editor.spec.ts
index 9fb46fa9a0..b31330a087 100644
--- a/packages/cli/src/utils/__tests__/editor.spec.ts
+++ b/packages/cli/src/utils/__tests__/editor.spec.ts
@@ -566,7 +566,6 @@ describe('writeEditorConfigs', () => {
expect(externalDependenciesXml).toContain(
'',
);
- expect(externalDependenciesXml).toContain('');
const workspaceXml = fs.readFileSync(path.join(projectRoot, '.idea', 'workspace.xml'), 'utf8');
expect(workspaceXml).toContain('');
diff --git a/packages/cli/src/utils/editor.ts b/packages/cli/src/utils/editor.ts
index ff87ecdd91..69eb9447a6 100644
--- a/packages/cli/src/utils/editor.ts
+++ b/packages/cli/src/utils/editor.ts
@@ -155,7 +155,6 @@ const JETBRAINS_EXTERNAL_DEPENDENCIES = `
-
`;
From 6fdfb830ba371c36a00089e83298c32ee152f119 Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Wed, 5 Aug 2026 06:52:17 +0000
Subject: [PATCH 09/19] docs: add note about .idea folders in most projects
---
docs/guide/ide-integration.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docs/guide/ide-integration.md b/docs/guide/ide-integration.md
index e281633015..305f22c029 100644
--- a/docs/guide/ide-integration.md
+++ b/docs/guide/ide-integration.md
@@ -150,6 +150,8 @@ You can also manually set up the IDE configuration to match your Vite+ setup:
```
+Often, `.idea` folders are gitignored in a project, even the `externalDependencies.xml` file. Adding the `.idea/.gitignore` file with the following content will help ensure that it is present:
+
```gitignore [.idea/.gitignore]
!externalDependencies.xml
```
From 9c5664622eba8fcc97d2ebdec34cb2df6dfd81f3 Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Wed, 5 Aug 2026 10:54:18 +0000
Subject: [PATCH 10/19] do not override jetbrains configs in non-interactive vp
migrate
---
.../src/migration/__tests__/migrator.spec.ts | 38 +++++++++++++++++++
packages/cli/src/migration/setup-plan.ts | 13 ++++++-
packages/cli/src/utils/editor.ts | 2 +-
3 files changed, 50 insertions(+), 3 deletions(-)
diff --git a/packages/cli/src/migration/__tests__/migrator.spec.ts b/packages/cli/src/migration/__tests__/migrator.spec.ts
index bcefe8a9f5..005cef7171 100644
--- a/packages/cli/src/migration/__tests__/migrator.spec.ts
+++ b/packages/cli/src/migration/__tests__/migrator.spec.ts
@@ -8593,3 +8593,41 @@ describe('collectMigrationSetupPlan ESLint gating', () => {
},
);
});
+
+describe('collectMigrationSetupPlan non-interactive editor conflicts', () => {
+ let tmpDir: string;
+
+ beforeEach(() => {
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vp-test-setup-plan-editor-'));
+ writePkgAt(tmpDir, { name: 'x' });
+ });
+
+ afterEach(() => {
+ fs.rmSync(tmpDir, { recursive: true, force: true });
+ });
+
+ it('skips (never overwrites) existing non-JSON jetbrains files, only merges JSON-like ones', async () => {
+ fs.mkdirSync(path.join(tmpDir, '.idea'), { recursive: true });
+ fs.writeFileSync(
+ path.join(tmpDir, '.idea', 'externalDependencies.xml'),
+ '',
+ );
+ fs.writeFileSync(path.join(tmpDir, '.idea', 'workspace.xml'), '');
+
+ const plan = await collectMigrationSetupPlan(
+ tmpDir,
+ PackageManager.pnpm,
+ {
+ interactive: false,
+ hooks: false,
+ agent: false as const,
+ editor: 'jetbrains',
+ },
+ undefined,
+ false,
+ );
+
+ expect(plan.editorConflictDecisions.get('externalDependencies.xml')).toBe('skip');
+ expect(plan.editorConflictDecisions.get('workspace.xml')).toBe('skip');
+ });
+});
diff --git a/packages/cli/src/migration/setup-plan.ts b/packages/cli/src/migration/setup-plan.ts
index 660e46fdb0..d4728eac83 100644
--- a/packages/cli/src/migration/setup-plan.ts
+++ b/packages/cli/src/migration/setup-plan.ts
@@ -8,7 +8,12 @@ import {
detectExistingAgentTargetPaths,
selectAgentTargetPaths,
} from '../utils/agent.ts';
-import { detectEditorConflicts, type EditorId, selectEditor } from '../utils/editor.ts';
+import {
+ detectEditorConflicts,
+ type EditorId,
+ isJsonLikeFile,
+ selectEditor,
+} from '../utils/editor.ts';
import { cancelAndExit, promptGitHooks } from '../utils/prompts.ts';
import {
confirmEslintMigration,
@@ -141,7 +146,11 @@ async function collectEditorConfigPlan(
}
editorConflictDecisions.set(conflict.fileName, action);
} else {
- editorConflictDecisions.set(conflict.fileName, 'merge');
+ // Non-JSON files (e.g. JetBrains XML) can't be merged, so only skip them here.
+ editorConflictDecisions.set(
+ conflict.fileName,
+ isJsonLikeFile(conflict.fileName) ? 'merge' : 'skip',
+ );
}
}
diff --git a/packages/cli/src/utils/editor.ts b/packages/cli/src/utils/editor.ts
index 69eb9447a6..df3d769bea 100644
--- a/packages/cli/src/utils/editor.ts
+++ b/packages/cli/src/utils/editor.ts
@@ -510,7 +510,7 @@ async function writeEditorConfig({
}
}
-function isJsonLikeFile(fileName: string): boolean {
+export function isJsonLikeFile(fileName: string): boolean {
const ext = path.extname(fileName).toLowerCase();
return ext === '.json' || ext === '.jsonc';
}
From 91eb277e3dcc90af55f925a740f7e4830f2909d4 Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Wed, 5 Aug 2026 11:11:52 +0000
Subject: [PATCH 11/19] adjust gitignore to ignore other files in .idea as well
---
packages/cli/src/utils/editor.ts | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/packages/cli/src/utils/editor.ts b/packages/cli/src/utils/editor.ts
index df3d769bea..bd4f36b135 100644
--- a/packages/cli/src/utils/editor.ts
+++ b/packages/cli/src/utils/editor.ts
@@ -181,7 +181,9 @@ const JETBRAINS_OXFMT_SETTINGS = `
`;
-const JETBRAINS_GITIGNORE_ADDITION = `!externalDependencies.xml`
+const JETBRAINS_GITIGNORE_ADDITION = `**
+!externalDependencies.xml
+`;
type EditorConfigValue = Record | string;
From 9c03b3078b5dfa5c4d0f8da45512b58938c99b69 Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Wed, 5 Aug 2026 11:33:56 +0000
Subject: [PATCH 12/19] resolve package manager correctly in the JetBrains
config
---
packages/cli/src/create/bin.ts | 2 ++
packages/cli/src/migration/bin.ts | 2 ++
.../cli/src/utils/__tests__/editor.spec.ts | 19 +++++++++++++++++--
packages/cli/src/utils/editor.ts | 19 +++++++++++++++----
4 files changed, 36 insertions(+), 6 deletions(-)
diff --git a/packages/cli/src/create/bin.ts b/packages/cli/src/create/bin.ts
index 0ef9c028e1..6e7cbe8e46 100644
--- a/packages/cli/src/create/bin.ts
+++ b/packages/cli/src/create/bin.ts
@@ -1101,6 +1101,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
interactive: options.interactive,
silent: compactOutput,
extraVsCodeSettings: { 'npm.scriptRunner': 'vp' },
+ packageManager,
});
if (selectedEditors?.includes('vscode')) {
ensureGitignoreVsCodeEditorConfigs(fullPath);
@@ -1269,6 +1270,7 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
interactive: options.interactive,
silent: compactOutput,
extraVsCodeSettings: { 'npm.scriptRunner': 'vp' },
+ packageManager,
});
if (selectedEditors?.includes('vscode')) {
ensureGitignoreVsCodeEditorConfigs(fullPath);
diff --git a/packages/cli/src/migration/bin.ts b/packages/cli/src/migration/bin.ts
index 4065458314..1eb928f769 100644
--- a/packages/cli/src/migration/bin.ts
+++ b/packages/cli/src/migration/bin.ts
@@ -958,6 +958,7 @@ async function executeMigrationPlan(
interactive,
conflictDecisions: plan.editorConflictDecisions,
silent: true,
+ packageManager: plan.packageManager,
});
// 11. Add framework shims if requested
@@ -1472,6 +1473,7 @@ async function main() {
interactive: options.interactive,
conflictDecisions: plan.editorConflictDecisions,
silent: true,
+ packageManager,
});
didMigrate = true;
}
diff --git a/packages/cli/src/utils/__tests__/editor.spec.ts b/packages/cli/src/utils/__tests__/editor.spec.ts
index b31330a087..360f3c0ca2 100644
--- a/packages/cli/src/utils/__tests__/editor.spec.ts
+++ b/packages/cli/src/utils/__tests__/editor.spec.ts
@@ -586,7 +586,22 @@ describe('writeEditorConfigs', () => {
);
const gitignore = fs.readFileSync(path.join(projectRoot, '.idea', '.gitignore'), 'utf8');
- expect(gitignore).toBe('!externalDependencies.xml');
+ expect(gitignore).toBe('**\n!externalDependencies.xml\n');
+ });
+
+ it('writes workspace.xml with the resolved package manager', async () => {
+ const projectRoot = createTempDir();
+
+ await writeEditorConfigs({
+ projectRoot,
+ editorId: 'jetbrains',
+ interactive: false,
+ silent: true,
+ packageManager: 'yarn',
+ });
+
+ const workspaceXml = fs.readFileSync(path.join(projectRoot, '.idea', 'workspace.xml'), 'utf8');
+ expect(workspaceXml).toContain('"nodejs_package_manager_path": "yarn"');
});
it('does not overwrite existing non-JSON jetbrains file in non-interactive mode', async () => {
@@ -615,6 +630,6 @@ describe('writeEditorConfigs', () => {
expect(oxfmtSettingsXml).toContain('');
const gitignore = fs.readFileSync(path.join(projectRoot, '.idea', '.gitignore'), 'utf8');
- expect(gitignore).toBe('!externalDependencies.xml');
+ expect(gitignore).toBe('**\n!externalDependencies.xml\n');
});
});
diff --git a/packages/cli/src/utils/editor.ts b/packages/cli/src/utils/editor.ts
index bd4f36b135..2019bde44e 100644
--- a/packages/cli/src/utils/editor.ts
+++ b/packages/cli/src/utils/editor.ts
@@ -14,6 +14,7 @@ import {
parse as parseJsonc,
} from 'jsonc-parser';
+import { PackageManager } from '../types/package.ts';
import { detectFormattingOptions, writeJsonFile } from './json.ts';
// Language-specific overrides because user-level [lang] settings beat the workspace default
@@ -159,19 +160,21 @@ const JETBRAINS_EXTERNAL_DEPENDENCIES = `
`;
-const JETBRAINS_WORKSPACE_CONFIG = `
+function jetbrainsWorkspaceConfig(packageManager: PackageManager): string {
+ return `
`;
+}
const JETBRAINS_OXFMT_SETTINGS = `
@@ -211,7 +214,8 @@ export const EDITORS = [
targetDir: '.idea',
files: {
'externalDependencies.xml': JETBRAINS_EXTERNAL_DEPENDENCIES,
- 'workspace.xml': JETBRAINS_WORKSPACE_CONFIG,
+ // Placeholder; writeEditorConfig() regenerates this with the resolved package manager.
+ 'workspace.xml': jetbrainsWorkspaceConfig(PackageManager.pnpm),
'OxfmtSettings.xml': JETBRAINS_OXFMT_SETTINGS,
'.gitignore': JETBRAINS_GITIGNORE_ADDITION,
},
@@ -381,6 +385,7 @@ export async function writeEditorConfigs({
conflictDecisions,
silent = false,
extraVsCodeSettings,
+ packageManager = PackageManager.pnpm,
}: {
projectRoot: string;
editorId: EditorSelection;
@@ -388,6 +393,7 @@ export async function writeEditorConfigs({
conflictDecisions?: Map;
silent?: boolean;
extraVsCodeSettings?: Record;
+ packageManager?: PackageManager;
}) {
const editorIds = normalizeEditorSelection(editorId);
if (editorIds.length === 0) {
@@ -402,6 +408,7 @@ export async function writeEditorConfigs({
conflictDecisions,
silent,
extraVsCodeSettings,
+ packageManager,
});
}
}
@@ -413,6 +420,7 @@ async function writeEditorConfig({
conflictDecisions,
silent,
extraVsCodeSettings,
+ packageManager,
}: {
projectRoot: string;
editorId: EditorId;
@@ -420,6 +428,7 @@ async function writeEditorConfig({
conflictDecisions?: Map;
silent: boolean;
extraVsCodeSettings?: Record;
+ packageManager: PackageManager;
}) {
const editorConfig = EDITORS.find((e) => e.id === editorId);
if (!editorConfig) {
@@ -433,7 +442,9 @@ async function writeEditorConfig({
const incoming: EditorConfigValue =
editorId === 'vscode' && fileName === 'settings.json' && extraVsCodeSettings
? { ...extraVsCodeSettings, ...baseIncoming }
- : baseIncoming;
+ : editorId === 'jetbrains' && fileName === 'workspace.xml'
+ ? jetbrainsWorkspaceConfig(packageManager)
+ : baseIncoming;
const filePath = path.join(targetDir, fileName);
const jsonFormat = isJsonLikeFile(fileName);
From 4e06aac54949f2b8e10582e3a2d6bf4dc4ca01eb Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Wed, 5 Aug 2026 11:39:04 +0000
Subject: [PATCH 13/19] use JSON.stringify for making the PropertiesComponent
in JB config + normalize nodejs_interpreter_path
---
packages/cli/src/utils/editor.ts | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/packages/cli/src/utils/editor.ts b/packages/cli/src/utils/editor.ts
index 2019bde44e..23540bc098 100644
--- a/packages/cli/src/utils/editor.ts
+++ b/packages/cli/src/utils/editor.ts
@@ -164,13 +164,13 @@ function jetbrainsWorkspaceConfig(packageManager: PackageManager): string {
return `
-
+ })}]]>
`;
From 0fc5db774c4da1d736386f5914f52284f92cea0b Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Fri, 7 Aug 2026 08:26:40 +0000
Subject: [PATCH 14/19] chore(cli): alias --editor intellij and --editor
webstorm to --editor jetbrains
also warns if it is passed
---
.../cli/src/utils/__tests__/editor.spec.ts | 75 ++++++++++++++++++-
packages/cli/src/utils/editor.ts | 30 ++++++--
2 files changed, 98 insertions(+), 7 deletions(-)
diff --git a/packages/cli/src/utils/__tests__/editor.spec.ts b/packages/cli/src/utils/__tests__/editor.spec.ts
index 360f3c0ca2..28eb1bcc70 100644
--- a/packages/cli/src/utils/__tests__/editor.spec.ts
+++ b/packages/cli/src/utils/__tests__/editor.spec.ts
@@ -6,7 +6,12 @@ import * as prompts from '@voidzero-dev/vite-plus-prompts';
import { parse as parseJsonc } from 'jsonc-parser';
import { afterEach, describe, expect, it, vi } from 'vitest';
-import { detectExistingEditors, selectEditors, writeEditorConfigs } from '../editor.js';
+import {
+ detectExistingEditors,
+ selectEditor,
+ selectEditors,
+ writeEditorConfigs,
+} from '../editor.js';
const tempDirs: string[] = [];
@@ -75,6 +80,74 @@ describe('selectEditors', () => {
}),
).resolves.toEqual(['zed']);
});
+
+ it('resolves --editor intellij to jetbrains and warns about the non-canonical ID used', async () => {
+ const warnSpy = vi.spyOn(prompts.log, 'warn').mockImplementation(() => {});
+
+ await expect(
+ selectEditors({
+ interactive: false,
+ editor: 'intellij',
+ onCancel: vi.fn(),
+ }),
+ ).resolves.toEqual(['jetbrains']);
+
+ expect(warnSpy).toHaveBeenCalledWith(
+ expect.stringContaining('--editor intellij was passed; use --editor jetbrains instead, as it\'s the canonical ID for that editor.'),
+ );
+ });
+
+ it('resolves --editor webstorm to jetbrains and warns about the non-canonical ID used', async () => {
+ const warnSpy = vi.spyOn(prompts.log, 'warn').mockImplementation(() => {});
+
+ await expect(
+ selectEditors({
+ interactive: false,
+ editor: 'webstorm',
+ onCancel: vi.fn(),
+ }),
+ ).resolves.toEqual(['jetbrains']);
+
+ expect(warnSpy).toHaveBeenCalledWith(
+ expect.stringContaining('--editor webstorm was passed; use --editor jetbrains instead, as it\'s the canonical ID for that editor.'),
+ );
+ });
+
+ it('does not show intellij/webstorm aliases as interactive TUI options', async () => {
+ const multiselectSpy = vi.spyOn(prompts, 'multiselect').mockResolvedValue(['vscode']);
+
+ await selectEditors({
+ interactive: true,
+ onCancel: vi.fn(),
+ });
+
+ expect(multiselectSpy).toHaveBeenCalledWith(
+ expect.objectContaining({
+ options: expect.not.arrayContaining([
+ expect.objectContaining({ value: 'intellij' }),
+ expect.objectContaining({ value: 'webstorm' }),
+ ]),
+ }),
+ );
+ });
+});
+
+describe('selectEditor', () => {
+ it('resolves --editor intellij to jetbrains and warns about the non-canonical ID used', async () => {
+ const warnSpy = vi.spyOn(prompts.log, 'warn').mockImplementation(() => {});
+
+ await expect(
+ selectEditor({
+ interactive: false,
+ editor: 'intellij',
+ onCancel: vi.fn(),
+ }),
+ ).resolves.toBe('jetbrains');
+
+ expect(warnSpy).toHaveBeenCalledWith(
+ expect.stringContaining('--editor intellij was passed; use --editor jetbrains instead, as it\'s the canonical ID for that editor.'),
+ );
+ });
});
describe('detectExistingEditors', () => {
diff --git a/packages/cli/src/utils/editor.ts b/packages/cli/src/utils/editor.ts
index 23540bc098..85986539d3 100644
--- a/packages/cli/src/utils/editor.ts
+++ b/packages/cli/src/utils/editor.ts
@@ -165,11 +165,11 @@ function jetbrainsWorkspaceConfig(packageManager: PackageManager): string {
@@ -222,6 +222,12 @@ export const EDITORS = [
},
] as const;
+// Deprecated aliases kept for backwards-compatible `--editor` values; not shown as TUI options.
+const EDITOR_ALIASES = [
+ { id: 'intellij', alias: 'jetbrains' },
+ { id: 'webstorm', alias: 'jetbrains' },
+] as const;
+
export type EditorId = (typeof EDITORS)[number]['id'];
type EditorSelection = EditorId | readonly EditorId[] | undefined;
@@ -686,7 +692,19 @@ function resolveEditorId(editor: string): EditorId | undefined {
const match = EDITORS.find(
(option) => option.id === normalized || option.label.toLowerCase() === normalized,
);
- return match?.id;
+ if (match) {
+ return match.id;
+ }
+
+ const aliasMatch = EDITOR_ALIASES.find((option) => option.id === normalized);
+ if (aliasMatch) {
+ prompts.log.warn(
+ `--editor ${aliasMatch.id} was passed; use --editor ${aliasMatch.alias} instead, as it's the canonical ID for the editor.`,
+ );
+ return aliasMatch.alias;
+ }
+
+ return undefined;
}
function resolveEditorIds(editors: readonly string[]): EditorId[] | undefined {
From a71e16f1a159140613cd27281a68ce54b2cca049 Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Fri, 7 Aug 2026 11:53:03 +0000
Subject: [PATCH 15/19] fix(cli): remove the need for importing os module
---
packages/cli/src/utils/editor.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/packages/cli/src/utils/editor.ts b/packages/cli/src/utils/editor.ts
index 78b65afa53..e91bc2e2cf 100644
--- a/packages/cli/src/utils/editor.ts
+++ b/packages/cli/src/utils/editor.ts
@@ -1,6 +1,5 @@
import fs from 'node:fs';
import fsPromises from 'node:fs/promises';
-import os from 'node:os';
import path from 'node:path';
import { styleText } from 'node:util';
@@ -110,7 +109,7 @@ function jetbrainsWorkspaceConfig(packageManager: PackageManager): string {
From 9b5445709169551551947d2ebebb5b7fa328e45a Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Fri, 7 Aug 2026 11:54:30 +0000
Subject: [PATCH 16/19] docs: use $USER_HOME$ on .idea/workspace.xml config
---
docs/guide/ide-integration.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/guide/ide-integration.md b/docs/guide/ide-integration.md
index f477d95620..4aef237b1d 100644
--- a/docs/guide/ide-integration.md
+++ b/docs/guide/ide-integration.md
@@ -134,8 +134,8 @@ You can also manually set up the IDE configuration to match your Vite+ setup:
"javascript.nodejs.core.library.configured.version": "24.18.0", // Replace with your selected Node.js version
"javascript.nodejs.core.library.typings.version": "24.13.3", // Replace with the version of @types/node that corresponds to your runtime (or omit if you don't want it)
"javascript.preferred.runtime.type.id": "node",
- "nodejs_interpreter_path": "~/.vite-plus/bin/node", // Replace ~ with the path to your home directory, IntelliJ/WebStorm doesn't understand ~
- "nodejs_package_manager_path": "pnpm", // Replace with your package manager of choice
+ "nodejs_interpreter_path": "$USER_HOME$/.vite-plus/bin/node",
+ "nodejs_package_manager_path": "pnpm" // Replace with your package manager of choice
}
}]]>
From c6a4aec02af0f61006008dc97f8947111355a3e2 Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Fri, 7 Aug 2026 12:39:51 +0000
Subject: [PATCH 17/19] fix: pretty-print JSON.striingify output for JetBrains
config
---
packages/cli/src/utils/editor.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/cli/src/utils/editor.ts b/packages/cli/src/utils/editor.ts
index e91bc2e2cf..cc0eee73e8 100644
--- a/packages/cli/src/utils/editor.ts
+++ b/packages/cli/src/utils/editor.ts
@@ -112,7 +112,7 @@ function jetbrainsWorkspaceConfig(packageManager: PackageManager): string {
nodejs_interpreter_path: '$USER_HOME$/.vite-plus/bin/node.exe',
nodejs_package_manager_path: packageManager,
},
- })}]]>
+ }, null, 2)}]]>
`;
From 90ba55ff36d70bf50def25817f9469fcf5b70da5 Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Fri, 7 Aug 2026 12:41:48 +0000
Subject: [PATCH 18/19] chore: fmt
---
packages/cli/src/utils/__tests__/editor.spec.ts | 12 +++++++++---
packages/cli/src/utils/editor.ts | 16 ++++++++++------
2 files changed, 19 insertions(+), 9 deletions(-)
diff --git a/packages/cli/src/utils/__tests__/editor.spec.ts b/packages/cli/src/utils/__tests__/editor.spec.ts
index e0e095af33..a6ad7fbac2 100644
--- a/packages/cli/src/utils/__tests__/editor.spec.ts
+++ b/packages/cli/src/utils/__tests__/editor.spec.ts
@@ -93,7 +93,9 @@ describe('selectEditors', () => {
).resolves.toEqual(['jetbrains']);
expect(warnSpy).toHaveBeenCalledWith(
- expect.stringContaining('--editor intellij was passed; use --editor jetbrains instead, as it\'s the canonical ID for that editor.'),
+ expect.stringContaining(
+ "--editor intellij was passed; use --editor jetbrains instead, as it's the canonical ID for that editor.",
+ ),
);
});
@@ -109,7 +111,9 @@ describe('selectEditors', () => {
).resolves.toEqual(['jetbrains']);
expect(warnSpy).toHaveBeenCalledWith(
- expect.stringContaining('--editor webstorm was passed; use --editor jetbrains instead, as it\'s the canonical ID for that editor.'),
+ expect.stringContaining(
+ "--editor webstorm was passed; use --editor jetbrains instead, as it's the canonical ID for that editor.",
+ ),
);
});
@@ -145,7 +149,9 @@ describe('selectEditor', () => {
).resolves.toBe('jetbrains');
expect(warnSpy).toHaveBeenCalledWith(
- expect.stringContaining('--editor intellij was passed; use --editor jetbrains instead, as it\'s the canonical ID for that editor.'),
+ expect.stringContaining(
+ "--editor intellij was passed; use --editor jetbrains instead, as it's the canonical ID for that editor.",
+ ),
);
});
});
diff --git a/packages/cli/src/utils/editor.ts b/packages/cli/src/utils/editor.ts
index cc0eee73e8..267ed40cc7 100644
--- a/packages/cli/src/utils/editor.ts
+++ b/packages/cli/src/utils/editor.ts
@@ -106,13 +106,17 @@ function jetbrainsWorkspaceConfig(packageManager: PackageManager): string {
return `
-
+ null,
+ 2,
+ )}]]>
`;
From 6b088768b6b5bd4940b09afb3a02bc70f52108fc Mon Sep 17 00:00:00 2001
From: KTrain <69028025+KTrain5169@users.noreply.github.com>
Date: Fri, 7 Aug 2026 12:49:01 +0000
Subject: [PATCH 19/19] chore: fix tests
---
packages/cli/src/utils/__tests__/editor.spec.ts | 2 +-
packages/cli/src/utils/editor.ts | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/cli/src/utils/__tests__/editor.spec.ts b/packages/cli/src/utils/__tests__/editor.spec.ts
index a6ad7fbac2..bf8746f06a 100644
--- a/packages/cli/src/utils/__tests__/editor.spec.ts
+++ b/packages/cli/src/utils/__tests__/editor.spec.ts
@@ -652,7 +652,7 @@ describe('writeEditorConfigs', () => {
expect(workspaceXml).toContain('');
expect(workspaceXml).toContain('"javascript.preferred.runtime.type.id": "node"');
expect(workspaceXml).toContain(
- `"nodejs_interpreter_path": "${os.homedir()}/.vite-plus/bin/node"`,
+ `"nodejs_interpreter_path": "$USER_HOME$/.vite-plus/bin/node.exe"`,
);
expect(workspaceXml).toContain('"nodejs_package_manager_path": "pnpm"');
diff --git a/packages/cli/src/utils/editor.ts b/packages/cli/src/utils/editor.ts
index 267ed40cc7..5de892ba43 100644
--- a/packages/cli/src/utils/editor.ts
+++ b/packages/cli/src/utils/editor.ts
@@ -645,7 +645,7 @@ function resolveEditorId(editor: string): EditorId | undefined {
const aliasMatch = EDITOR_ALIASES.find((option) => option.id === normalized);
if (aliasMatch) {
prompts.log.warn(
- `--editor ${aliasMatch.id} was passed; use --editor ${aliasMatch.alias} instead, as it's the canonical ID for the editor.`,
+ `--editor ${aliasMatch.id} was passed; use --editor ${aliasMatch.alias} instead, as it's the canonical ID for that editor.`,
);
return aliasMatch.alias;
}