Skip to content

Commit a612cef

Browse files
committed
fix(install): auto-create package.json when missing in vp install / vp add
Running `vp install` or `vp add` in a directory without package.json previously failed with "Package not found in workspace". Now a minimal `{ "type": "module" }` package.json is created automatically, allowing PM detection and install to proceed.
1 parent 18d4112 commit a612cef

5 files changed

Lines changed: 87 additions & 0 deletions

File tree

crates/vite_global_cli/src/commands/add.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ impl AddCommand {
3636
pass_through_args: Option<&[String]>,
3737
) -> Result<ExitStatus, Error> {
3838
prepend_js_runtime_to_path_env(&self.cwd).await?;
39+
super::ensure_package_json(&self.cwd).await?;
3940

4041
let add_command_options = AddCommandOptions {
4142
packages,

crates/vite_global_cli/src/commands/install.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ impl InstallCommand {
1818

1919
pub async fn execute(self, options: &InstallCommandOptions<'_>) -> Result<ExitStatus, Error> {
2020
prepend_js_runtime_to_path_env(&self.cwd).await?;
21+
super::ensure_package_json(&self.cwd).await?;
2122

2223
let package_manager = PackageManager::builder(&self.cwd).build_with_default().await?;
2324

@@ -83,6 +84,42 @@ mod tests {
8384
assert!(result.is_ok());
8485
}
8586

87+
#[tokio::test]
88+
async fn test_ensure_package_json_creates_when_missing() {
89+
let temp_dir = TempDir::new().unwrap();
90+
let dir_path = AbsolutePathBuf::new(temp_dir.path().to_path_buf()).unwrap();
91+
let package_json_path = dir_path.join("package.json");
92+
93+
// Verify no package.json exists
94+
assert!(!package_json_path.as_path().exists());
95+
96+
// Call ensure_package_json
97+
crate::commands::ensure_package_json(&dir_path).await.unwrap();
98+
99+
// Verify package.json was created with correct content
100+
let content = fs::read_to_string(&package_json_path).unwrap();
101+
let parsed: serde_json::Value = serde_json::from_str(&content).unwrap();
102+
assert_eq!(parsed["type"], "module");
103+
}
104+
105+
#[tokio::test]
106+
async fn test_ensure_package_json_does_not_overwrite_existing() {
107+
let temp_dir = TempDir::new().unwrap();
108+
let dir_path = AbsolutePathBuf::new(temp_dir.path().to_path_buf()).unwrap();
109+
let package_json_path = dir_path.join("package.json");
110+
111+
// Create an existing package.json
112+
let existing_content = r#"{"name": "existing-package"}"#;
113+
fs::write(&package_json_path, existing_content).unwrap();
114+
115+
// Call ensure_package_json
116+
crate::commands::ensure_package_json(&dir_path).await.unwrap();
117+
118+
// Verify existing package.json was NOT overwritten
119+
let content = fs::read_to_string(&package_json_path).unwrap();
120+
assert_eq!(content, existing_content);
121+
}
122+
86123
#[tokio::test]
87124
async fn test_install_command_execute_with_invalid_workspace() {
88125
let temp_dir = TempDir::new().unwrap();

crates/vite_global_cli/src/commands/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,20 @@ use vite_shared::{PrependOptions, prepend_to_path_env};
2828

2929
use crate::{error::Error, js_executor::JsExecutor};
3030

31+
/// Ensure a package.json exists in the given directory.
32+
/// If it doesn't exist, create a minimal one with `{ "type": "module" }`.
33+
pub async fn ensure_package_json(project_path: &AbsolutePath) -> Result<(), Error> {
34+
let package_json_path = project_path.join("package.json");
35+
if !package_json_path.as_path().exists() {
36+
let content = serde_json::to_string_pretty(&serde_json::json!({
37+
"type": "module"
38+
}))?;
39+
tokio::fs::write(&package_json_path, format!("{content}\n")).await?;
40+
tracing::info!("Created package.json in {:?}", project_path);
41+
}
42+
Ok(())
43+
}
44+
3145
/// Ensure the JS runtime is downloaded and prepend its bin directory to PATH.
3246
/// This should be called before executing any package manager command.
3347
///
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
> test ! -f package.json && echo 'no package.json' # verify no package.json exists
2+
no package.json
3+
4+
> vp install --silent && cat package.json # should auto-create package.json and install
5+
{
6+
"type": "module",
7+
"packageManager": "pnpm@<semver>"
8+
}
9+
> vp add testnpm2 -D && cat package.json # should add package to auto-created package.json
10+
Packages: +<variable>
11+
+<repeat>
12+
Progress: resolved <variable>, reused <variable>, downloaded <variable>, added <variable>, done
13+
14+
devDependencies:
15+
+ testnpm2 <semver>
16+
17+
Done in <variable>ms using pnpm v<semver>
18+
{
19+
"type": "module",
20+
"packageManager": "pnpm@<semver>",
21+
"devDependencies": {
22+
"testnpm2": "^1.0.1"
23+
}
24+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"ignoredPlatforms": ["win32"],
3+
"env": {
4+
"VITE_DISABLE_AUTO_INSTALL": "1"
5+
},
6+
"commands": [
7+
"test ! -f package.json && echo 'no package.json' # verify no package.json exists",
8+
"vp install --silent && cat package.json # should auto-create package.json and install",
9+
"vp add testnpm2 -D && cat package.json # should add package to auto-created package.json"
10+
]
11+
}

0 commit comments

Comments
 (0)