Skip to content

Commit 3f90fe8

Browse files
committed
v0.3.6: dartlab UI 자동 빌드 — npm install + vite build 자동 실행
- setup: ensure_ui_build() 추가 — ui/build/ 없으면 npm install + npm run build 자동 실행 - setup: find_npm() — PATH/Program Files/nvm 경로에서 npm 자동 탐색 - paths: dartlab_ui_dir() 추가 - main: 웜/콜드 스타트 모두 서버 시작 전 UI 빌드 확인 단계 추가
1 parent abd42d7 commit 3f90fe8

File tree

4 files changed

+111
-1
lines changed

4 files changed

+111
-1
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "dartlab-desktop"
3-
version = "0.3.5"
3+
version = "0.3.6"
44
edition = "2024"
55
description = "DartLab AI Desktop Launcher for Windows"
66
license = "MIT"

src/main.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,12 @@ fn run_setup(
493493
return;
494494
}
495495

496+
progress(40, "UI 빌드 확인 중...");
497+
if let Err(e) = setup::ensure_ui_build(&app_dir) {
498+
fail(&format!("UI 빌드 실패: {e}"));
499+
return;
500+
}
501+
496502
progress(50, "서버 시작 중...");
497503
match runner::start_server(&app_dir) {
498504
Ok(_) => {}
@@ -553,6 +559,12 @@ fn run_setup(
553559
return;
554560
}
555561

562+
progress(80, "UI 빌드 확인 중...");
563+
if let Err(e) = setup::ensure_ui_build(&app_dir) {
564+
fail(&format!("UI 빌드 실패: {e}"));
565+
return;
566+
}
567+
556568
progress(85, "서버 시작 중...");
557569
match runner::start_server(&app_dir) {
558570
Ok(_) => {}

src/paths.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,12 @@ pub fn python_bin(app_dir: &std::path::Path) -> PathBuf {
2121
pub fn dartlab_bin(app_dir: &std::path::Path) -> PathBuf {
2222
venv_dir(app_dir).join("Scripts").join("dartlab.exe")
2323
}
24+
25+
pub fn dartlab_ui_dir(app_dir: &std::path::Path) -> PathBuf {
26+
venv_dir(app_dir)
27+
.join("Lib")
28+
.join("site-packages")
29+
.join("dartlab")
30+
.join("ui")
31+
}
32+

src/setup.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,95 @@ pub fn ensure_dartlab(app_dir: &Path) -> Result<(), String> {
136136
Ok(())
137137
}
138138

139+
pub fn ensure_ui_build(app_dir: &Path) -> Result<(), String> {
140+
let ui_dir = paths::dartlab_ui_dir(app_dir);
141+
let build_dir = ui_dir.join("build");
142+
143+
if build_dir.exists() && build_dir.join("index.html").exists() {
144+
return Ok(());
145+
}
146+
147+
if !ui_dir.join("package.json").exists() {
148+
return Err("dartlab UI 소스를 찾을 수 없습니다 (package.json 없음)".into());
149+
}
150+
151+
let npm = find_npm()?;
152+
153+
logger::log("dartlab UI npm install 실행 중...");
154+
let output = Command::new(&npm)
155+
.args(["install"])
156+
.current_dir(&ui_dir)
157+
.creation_flags(CREATE_NO_WINDOW)
158+
.output()
159+
.map_err(|e| format!("npm install 실행 실패: {e}"))?;
160+
161+
if !output.status.success() {
162+
let stderr = String::from_utf8_lossy(&output.stderr);
163+
logger::log(&format!("npm install stderr: {stderr}"));
164+
return Err(format!("npm install 실패: {stderr}"));
165+
}
166+
167+
logger::log("dartlab UI 빌드 중...");
168+
let output = Command::new(&npm)
169+
.args(["run", "build"])
170+
.current_dir(&ui_dir)
171+
.creation_flags(CREATE_NO_WINDOW)
172+
.output()
173+
.map_err(|e| format!("npm run build 실행 실패: {e}"))?;
174+
175+
if !output.status.success() {
176+
let stderr = String::from_utf8_lossy(&output.stderr);
177+
logger::log(&format!("npm run build stderr: {stderr}"));
178+
return Err(format!("UI 빌드 실패: {stderr}"));
179+
}
180+
181+
if !build_dir.exists() || !build_dir.join("index.html").exists() {
182+
return Err("UI 빌드 완료되었으나 build/index.html이 생성되지 않았습니다".into());
183+
}
184+
185+
logger::log("dartlab UI 빌드 완료");
186+
Ok(())
187+
}
188+
189+
fn find_npm() -> Result<String, String> {
190+
if Command::new("npm")
191+
.arg("--version")
192+
.creation_flags(CREATE_NO_WINDOW)
193+
.output()
194+
.map(|o| o.status.success())
195+
.unwrap_or(false)
196+
{
197+
return Ok("npm".to_string());
198+
}
199+
200+
let candidates = [
201+
r"C:\Program Files\nodejs\npm.cmd",
202+
r"C:\Program Files (x86)\nodejs\npm.cmd",
203+
];
204+
205+
for c in &candidates {
206+
if std::path::Path::new(c).exists() {
207+
return Ok(c.to_string());
208+
}
209+
}
210+
211+
if let Ok(appdata) = std::env::var("APPDATA") {
212+
let nvm_dir = std::path::Path::new(&appdata).join("nvm");
213+
if nvm_dir.exists() {
214+
if let Ok(entries) = std::fs::read_dir(&nvm_dir) {
215+
for entry in entries.flatten() {
216+
let npm_cmd = entry.path().join("npm.cmd");
217+
if npm_cmd.exists() {
218+
return Ok(npm_cmd.to_string_lossy().to_string());
219+
}
220+
}
221+
}
222+
}
223+
}
224+
225+
Err("Node.js가 설치되어 있지 않습니다. https://nodejs.org 에서 설치 후 다시 시도해 주세요.".into())
226+
}
227+
139228
fn cleanup_legacy(app_dir: &Path) {
140229
let pyproject = app_dir.join("pyproject.toml");
141230
if pyproject.exists() {

0 commit comments

Comments
 (0)