From 41b9cb4cb5bbdbc4bb34e6a26cef42f56ec93e4d Mon Sep 17 00:00:00 2001 From: Bori-github Date: Fri, 7 Nov 2025 21:28:29 +0900 Subject: [PATCH 01/11] =?UTF-8?q?chore:=20=ED=83=80=EC=9A=B0=EB=A6=AC=20te?= =?UTF-8?q?mpfile=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 1 + apps/executeJS/src-tauri/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 137c2b4..1fc8830 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1455,6 +1455,7 @@ dependencies = [ "tauri-plugin-http", "tauri-plugin-opener", "tauri-plugin-store", + "tempfile", "thiserror 2.0.17", "tokio", "tokio-native-tls", diff --git a/apps/executeJS/src-tauri/Cargo.toml b/apps/executeJS/src-tauri/Cargo.toml index e830059..7b310da 100644 --- a/apps/executeJS/src-tauri/Cargo.toml +++ b/apps/executeJS/src-tauri/Cargo.toml @@ -43,6 +43,7 @@ native-tls = "0.2" tokio-rustls = "0.26" tokio-stream = "0.1" flate2 = "1.1" +tempfile = "3.23.0" # JavaScript 런타임 의존성 (Deno Core) deno-runtime = { path = "../../../crates/deno-runtime" } From dfd33ff8407a4c533070cdc120b1849cc4f8aa42 Mon Sep 17 00:00:00 2001 From: Bori-github Date: Fri, 7 Nov 2025 21:34:07 +0900 Subject: [PATCH 02/11] =?UTF-8?q?feat:=20oxlint=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=20=ED=95=A8=EC=88=98=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/executeJS/src-tauri/src/commands.rs | 140 +++++++++++++++++++++++ apps/executeJS/src-tauri/src/lib.rs | 2 +- 2 files changed, 141 insertions(+), 1 deletion(-) diff --git a/apps/executeJS/src-tauri/src/commands.rs b/apps/executeJS/src-tauri/src/commands.rs index dd8928e..977d9f7 100644 --- a/apps/executeJS/src-tauri/src/commands.rs +++ b/apps/executeJS/src-tauri/src/commands.rs @@ -1,5 +1,6 @@ use crate::js_executor::{execute_javascript_code, JsExecutionResult}; use serde::{Deserialize, Serialize}; +use std::process::Command; #[derive(Debug, Serialize, Deserialize)] pub struct AppInfo { @@ -9,6 +10,49 @@ pub struct AppInfo { pub author: String, } +// 린트 결과를 나타내는 구조체 +#[derive(Debug, Serialize, Deserialize)] +pub struct LintResult { + pub line: usize, + pub column: usize, + pub end_line: usize, + pub end_column: usize, + pub message: String, + // "error" 또는 "warning" + pub severity: String, + pub rule_id: String, +} + +// oxlint JSON 출력 형식에 맞는 구조체 +#[derive(Debug, Deserialize)] +struct OxlintOutput { + diagnostics: Vec, +} + +// oxlint 진단 정보 +#[derive(Debug, Deserialize)] +struct OxlintDiagnostic { + message: String, + code: String, + severity: String, + labels: Vec, +} + +// oxlint 레이블 (위치 정보 포함) +#[derive(Debug, Deserialize)] +struct OxlintLabel { + span: OxlintSpan, +} + +// oxlint 스팬 (라인, 컬럼, 길이 정보) +#[derive(Debug, Deserialize)] +struct OxlintSpan { + line: usize, + column: usize, + #[serde(default)] + length: usize, +} + #[tauri::command] pub async fn execute_js(code: &str) -> Result { let result = execute_javascript_code(code).await; @@ -39,3 +83,99 @@ pub fn get_app_info() -> AppInfo { author: "ExecuteJS Team".to_string(), } } + +// JavaScript 코드를 oxlint로 린트하고 결과를 반환 +#[tauri::command] +pub async fn lint_code(code: String) -> Result, String> { + use std::io::Write; + use tempfile::NamedTempFile; + + // 임시 파일 생성 + let mut temp_file = NamedTempFile::with_suffix(".js") + .map_err(|e| format!("Failed to create temp file: {}", e))?; + + temp_file + .write_all(code.as_bytes()) + .map_err(|e| format!("Failed to write to temp file: {}", e))?; + + let temp_path = temp_file.path().to_str().ok_or("Invalid temp path")?; + + // oxlint 실행 (JSON 형식으로 출력) + let output = Command::new("npx") + .arg("--yes") + .arg("oxlint") + .arg("--format") + .arg("json") + .arg(temp_path) + .output() + .map_err(|e| format!("Failed to execute oxlint: {}", e))?; + + drop(temp_file); + + // oxlint 출력 파싱 + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + + let results = parse_oxlint_output(&stdout, &stderr); + + Ok(results) +} + +// oxlint의 JSON 출력을 파싱하여 LintResult 벡터로 변환 +fn parse_oxlint_output(stdout: &str, stderr: &str) -> Vec { + let output_text = if stdout.trim().is_empty() { + stderr + } else { + stdout + }; + + // JSON 형식으로 직접 deserialize + match serde_json::from_str::(output_text) { + Ok(oxlint_output) => { + let mut results = Vec::new(); + + for diagnostic in oxlint_output.diagnostics { + // labels의 첫 번째 항목에서 위치 정보 가져오기 + if let Some(label) = diagnostic.labels.first() { + let span = &label.span; + let line = span.line; + let column = span.column.max(1); + let end_column = if span.length > 0 { + column + span.length + } else { + // length가 없을 경우 기본값 사용 + column + 10 + }; + + // code에서 rule_id 추출 + // 예: "eslint(no-unused-vars)" -> "no-unused-vars" + let rule_id = if diagnostic.code.starts_with("eslint(") { + diagnostic + .code + .strip_prefix("eslint(") + .and_then(|s| s.strip_suffix(')')) + .unwrap_or("unknown") + .to_string() + } else { + diagnostic.code.clone() + }; + + results.push(LintResult { + line, + column, + end_line: line, + end_column, + message: diagnostic.message, + severity: diagnostic.severity, + rule_id, + }); + } + } + + results + } + Err(_) => { + Vec::new() + } + } +} diff --git a/apps/executeJS/src-tauri/src/lib.rs b/apps/executeJS/src-tauri/src/lib.rs index e06a972..089a3f6 100644 --- a/apps/executeJS/src-tauri/src/lib.rs +++ b/apps/executeJS/src-tauri/src/lib.rs @@ -49,7 +49,7 @@ pub fn run() { tracing::info!("ExecuteJS 애플리케이션이 종료됩니다."); } }) - .invoke_handler(tauri::generate_handler![execute_js, get_app_info]) + .invoke_handler(tauri::generate_handler![execute_js, get_app_info, lint_code]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } From bd6a7e46372a9559b49d1add0740a12b2ee5a8f4 Mon Sep 17 00:00:00 2001 From: Bori-github Date: Fri, 7 Nov 2025 21:35:18 +0900 Subject: [PATCH 03/11] =?UTF-8?q?feat:=20=EB=B0=B1=EC=97=94=EB=93=9C?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=A0=84=EB=8B=AC=ED=95=98=EB=8A=94=20oxl?= =?UTF-8?q?int=20=EA=B2=B0=EA=B3=BC=EB=A5=BC=20=EC=97=90=EB=94=94=ED=84=B0?= =?UTF-8?q?=EC=97=90=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/executeJS/src/shared/types/index.ts | 10 ++ .../src/widgets/code-editor/code-editor.tsx | 91 ++++++++++++++++++- 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/apps/executeJS/src/shared/types/index.ts b/apps/executeJS/src/shared/types/index.ts index 4d9fd28..09e4fa6 100644 --- a/apps/executeJS/src/shared/types/index.ts +++ b/apps/executeJS/src/shared/types/index.ts @@ -20,3 +20,13 @@ export interface OutputPanelProps { result: JsExecutionResult | null; isExecuting: boolean; } + +export interface LintResult { + line: number; + column: number; + end_line: number; + end_column: number; + message: string; + severity: string; + rule_id: string; +} diff --git a/apps/executeJS/src/widgets/code-editor/code-editor.tsx b/apps/executeJS/src/widgets/code-editor/code-editor.tsx index b0388ab..36d7a6a 100644 --- a/apps/executeJS/src/widgets/code-editor/code-editor.tsx +++ b/apps/executeJS/src/widgets/code-editor/code-editor.tsx @@ -1,5 +1,6 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useCallback, useEffect, useRef } from 'react'; +import { invoke } from '@tauri-apps/api/core'; import { Editor, EditorProps } from '@monaco-editor/react'; import type { Options as PrettierOptions } from 'prettier'; import prettier from 'prettier/standalone'; @@ -7,7 +8,7 @@ import babel from 'prettier/plugins/babel'; import estree from 'prettier/plugins/estree'; import typescript from 'prettier/plugins/typescript'; -import type { CodeEditorProps } from '../../shared/types'; +import { CodeEditorProps, LintResult } from '@/shared'; const prettierOptions: PrettierOptions = { semi: true, @@ -26,12 +27,79 @@ export const CodeEditor: React.FC = ({ theme = 'vs-dark', }) => { const editorRef = useRef(null); + const monacoRef = useRef(null); const disposablesRef = useRef>([]); + const debounceTimeoutRef = useRef(null); + + const validateCode = useCallback(async (model: any, version: number) => { + if (debounceTimeoutRef.current) { + clearTimeout(debounceTimeoutRef.current); + } + + debounceTimeoutRef.current = setTimeout(async () => { + if (!model || !monacoRef.current) return; + + try { + const code = model.getValue(); + + // Tauri 백엔드에서 oxlint 실행 + const lintResults = await invoke>('lint_code', { + code, + }); + + // setModelMarkers 사용 + const monaco = monacoRef.current; + if (monaco && model.getVersionId() === version) { + const markers = lintResults.map((result) => { + // Monaco는 1-based 인덱스 사용 + const startColumn = Math.max(1, result.column); + const endColumn = Math.max(startColumn + 1, result.end_column); + + // severity를 소문자로 비교하여 MarkerSeverity enum 사용 + const severity = + result.severity.toLowerCase() === 'error' + ? monaco.MarkerSeverity.Error + : monaco.MarkerSeverity.Warning; + + return { + message: `${result.message} (${result.rule_id})`, + severity: severity, + startLineNumber: result.line, + startColumn: startColumn, + endLineNumber: result.end_line, + endColumn: endColumn, + source: 'oxlint', + code: result.rule_id, + }; + }); + + monaco.editor.setModelMarkers(model, 'oxlint', markers); + } + } catch (error) { + console.error('oxlint validation error:', error); + // 에러 발생 시 마커 초기화 + const monaco = monacoRef.current; + if (monaco) { + const model = editorRef.current?.getModel(); + if (model && model.getVersionId() === version) { + monaco.editor.setModelMarkers(model, 'oxlint', []); + } + } + } + }, 500); + }, []); // Monaco Editor 설정 const handleEditorDidMount: EditorProps['onMount'] = (editor, monaco) => { try { editorRef.current = editor; + monacoRef.current = monaco; + + // 기본 TypeScript validator 비활성화 (oxlint 사용 시) + monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({ + noSemanticValidation: true, + noSyntaxValidation: true, + }); // 이전 등록된 포맷터가 남아있는 경우, 먼저 해제 if (disposablesRef.current.length > 0) { @@ -113,6 +181,22 @@ export const CodeEditor: React.FC = ({ ); } + const model = editor.getModel(); + + if (model) { + // 모델 변경 시 validation + model.onDidChangeContent(() => { + // Reset the markers + monaco.editor.setModelMarkers(model, 'oxlint', []); + + // Send the code to the backend for validation + validateCode(model, model.getVersionId()); + }); + + // 초기 validation + validateCode(model, model.getVersionId()); + } + // 에디터 포커스 editor.focus(); } catch (error) { @@ -165,6 +249,9 @@ export const CodeEditor: React.FC = ({ // Cleanup: unmount 시 포맷터 등록 해제 useEffect(() => { return () => { + if (debounceTimeoutRef.current) { + clearTimeout(debounceTimeoutRef.current); + } // 모든 disposable 해제 disposablesRef.current.forEach((disposable) => { disposable.dispose(); From 58322e02d546963869dd59d4c61cabd0f1c76c6d Mon Sep 17 00:00:00 2001 From: Bori-github Date: Fri, 7 Nov 2025 21:48:35 +0900 Subject: [PATCH 04/11] fix: rust lint fix --- apps/executeJS/src-tauri/src/commands.rs | 4 +--- apps/executeJS/src-tauri/src/lib.rs | 6 +++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/executeJS/src-tauri/src/commands.rs b/apps/executeJS/src-tauri/src/commands.rs index 977d9f7..5ac3e95 100644 --- a/apps/executeJS/src-tauri/src/commands.rs +++ b/apps/executeJS/src-tauri/src/commands.rs @@ -174,8 +174,6 @@ fn parse_oxlint_output(stdout: &str, stderr: &str) -> Vec { results } - Err(_) => { - Vec::new() - } + Err(_) => Vec::new(), } } diff --git a/apps/executeJS/src-tauri/src/lib.rs b/apps/executeJS/src-tauri/src/lib.rs index 089a3f6..9d55f52 100644 --- a/apps/executeJS/src-tauri/src/lib.rs +++ b/apps/executeJS/src-tauri/src/lib.rs @@ -49,7 +49,11 @@ pub fn run() { tracing::info!("ExecuteJS 애플리케이션이 종료됩니다."); } }) - .invoke_handler(tauri::generate_handler![execute_js, get_app_info, lint_code]) + .invoke_handler(tauri::generate_handler![ + execute_js, + get_app_info, + lint_code + ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } From 57926d5b3a6788ac51e4ef560f1e3382d2c3de8a Mon Sep 17 00:00:00 2001 From: Bori-github Date: Mon, 10 Nov 2025 14:42:31 +0900 Subject: [PATCH 05/11] =?UTF-8?q?refactor:=20npx=20--yes=20=EB=AA=85?= =?UTF-8?q?=EB=A0=B9=EC=96=B4=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20=EC=84=B1?= =?UTF-8?q?=EB=8A=A5=20=EB=AC=B8=EC=A0=9C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/executeJS/src-tauri/src/commands.rs | 57 ++++++++++++++++++++---- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/apps/executeJS/src-tauri/src/commands.rs b/apps/executeJS/src-tauri/src/commands.rs index 5ac3e95..b4bedc6 100644 --- a/apps/executeJS/src-tauri/src/commands.rs +++ b/apps/executeJS/src-tauri/src/commands.rs @@ -100,15 +100,54 @@ pub async fn lint_code(code: String) -> Result, String> { let temp_path = temp_file.path().to_str().ok_or("Invalid temp path")?; - // oxlint 실행 (JSON 형식으로 출력) - let output = Command::new("npx") - .arg("--yes") - .arg("oxlint") - .arg("--format") - .arg("json") - .arg(temp_path) - .output() - .map_err(|e| format!("Failed to execute oxlint: {}", e))?; + // 프로젝트 루트 경로 찾기 (현재 실행 파일 위치 기준) + let current_exe = std::env::current_exe() + .map_err(|e| format!("Failed to get current exe path: {}", e))?; + + // Tauri 앱의 경우: target/debug/executeJS 또는 target/release/executeJS + // 프로젝트 루트는 3단계 위 (target/debug 또는 target/release) + let mut project_root = current_exe + .parent() // target/debug 또는 target/release + .and_then(|p| p.parent()) // target + .and_then(|p| p.parent()) // 프로젝트 루트 + .ok_or("Failed to find project root")?; + + // 개발 모드에서는 src-tauri가 있으므로 한 단계 더 올라가야 함 + let src_tauri_path = project_root.join("apps/executeJS/src-tauri"); + if src_tauri_path.exists() { + project_root = src_tauri_path + .parent() + .and_then(|p| p.parent()) + .ok_or("Failed to find project root")?; + } + + // oxlint 경로: 프로젝트 루트의 node_modules/.bin/oxlint + let oxlint_path = project_root + .join("node_modules") + .join(".bin") + .join("oxlint"); + + // oxlint 실행 (로컬 설치된 버전 사용) + let output = if oxlint_path.exists() { + // 로컬 설치된 oxlint 사용 + Command::new(oxlint_path) + .arg("--format") + .arg("json") + .arg(temp_path) + .output() + .map_err(|e| format!("Failed to execute oxlint: {}", e))? + } else { + // fallback: pnpm exec oxlint (pnpm이 설치되어 있다면) + Command::new("pnpm") + .arg("exec") + .arg("oxlint") + .arg("--format") + .arg("json") + .arg(temp_path) + .current_dir(project_root) + .output() + .map_err(|e| format!("Failed to execute oxlint: {}", e))? + }; drop(temp_file); From 1b41febb76284d990df2843448e206aadb557b60 Mon Sep 17 00:00:00 2001 From: Bori-github Date: Mon, 10 Nov 2025 15:29:11 +0900 Subject: [PATCH 06/11] =?UTF-8?q?chore:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=EB=84=88=EB=8F=84=20=EC=96=B8=EB=A7=88?= =?UTF-8?q?=EC=9A=B4=ED=8A=B8=20=EC=8B=9C=20cleanup=20=ED=95=98=EC=97=AC?= =?UTF-8?q?=20=EC=A4=91=EB=B3=B5=20=EB=93=B1=EB=A1=9D=EB=90=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/widgets/code-editor/code-editor.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/executeJS/src/widgets/code-editor/code-editor.tsx b/apps/executeJS/src/widgets/code-editor/code-editor.tsx index 36d7a6a..8ac1988 100644 --- a/apps/executeJS/src/widgets/code-editor/code-editor.tsx +++ b/apps/executeJS/src/widgets/code-editor/code-editor.tsx @@ -185,7 +185,7 @@ export const CodeEditor: React.FC = ({ if (model) { // 모델 변경 시 validation - model.onDidChangeContent(() => { + const contentChangeDisposable = model.onDidChangeContent(() => { // Reset the markers monaco.editor.setModelMarkers(model, 'oxlint', []); @@ -193,8 +193,18 @@ export const CodeEditor: React.FC = ({ validateCode(model, model.getVersionId()); }); + // model이 있는 경우 포맷터 + 이벤트 리스너 저장 + disposablesRef.current = [ + jsDisposable, + tsDisposable, + contentChangeDisposable, + ]; + // 초기 validation validateCode(model, model.getVersionId()); + } else { + // model이 없는 경우 포맷터만 저장 + disposablesRef.current = [jsDisposable, tsDisposable]; } // 에디터 포커스 From bdb33778d682bbca5ffa7f2ed5a2ec4f58e5eecb Mon Sep 17 00:00:00 2001 From: Bori-github Date: Mon, 10 Nov 2025 15:51:21 +0900 Subject: [PATCH 07/11] =?UTF-8?q?chore:=20severity=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EC=A2=81=ED=9E=88=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/executeJS/src-tauri/src/commands.rs | 23 +++++++++++++-- apps/executeJS/src/shared/types/index.ts | 4 ++- .../src/widgets/code-editor/code-editor.tsx | 28 ++++++++++++++----- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/apps/executeJS/src-tauri/src/commands.rs b/apps/executeJS/src-tauri/src/commands.rs index b4bedc6..0aecf37 100644 --- a/apps/executeJS/src-tauri/src/commands.rs +++ b/apps/executeJS/src-tauri/src/commands.rs @@ -11,6 +11,15 @@ pub struct AppInfo { } // 린트 결과를 나타내는 구조체 +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum LintSeverity { + Error, + Warning, + Info, + Hint, +} + #[derive(Debug, Serialize, Deserialize)] pub struct LintResult { pub line: usize, @@ -18,8 +27,7 @@ pub struct LintResult { pub end_line: usize, pub end_column: usize, pub message: String, - // "error" 또는 "warning" - pub severity: String, + pub severity: LintSeverity, pub rule_id: String, } @@ -199,13 +207,22 @@ fn parse_oxlint_output(stdout: &str, stderr: &str) -> Vec { diagnostic.code.clone() }; + // severity 변환 + let severity = match diagnostic.severity.as_str() { + "error" => LintSeverity::Error, + "warning" => LintSeverity::Warning, + "info" => LintSeverity::Info, + "hint" => LintSeverity::Hint, + _ => LintSeverity::Warning, // 기본값 + }; + results.push(LintResult { line, column, end_line: line, end_column, message: diagnostic.message, - severity: diagnostic.severity, + severity, rule_id, }); } diff --git a/apps/executeJS/src/shared/types/index.ts b/apps/executeJS/src/shared/types/index.ts index 09e4fa6..3b37fe5 100644 --- a/apps/executeJS/src/shared/types/index.ts +++ b/apps/executeJS/src/shared/types/index.ts @@ -21,12 +21,14 @@ export interface OutputPanelProps { isExecuting: boolean; } +export type LintSeverity = 'error' | 'warning' | 'info' | 'hint'; + export interface LintResult { line: number; column: number; end_line: number; end_column: number; message: string; - severity: string; + severity: LintSeverity; rule_id: string; } diff --git a/apps/executeJS/src/widgets/code-editor/code-editor.tsx b/apps/executeJS/src/widgets/code-editor/code-editor.tsx index 8ac1988..75da291 100644 --- a/apps/executeJS/src/widgets/code-editor/code-editor.tsx +++ b/apps/executeJS/src/widgets/code-editor/code-editor.tsx @@ -1,14 +1,14 @@ import React, { useCallback, useEffect, useRef } from 'react'; import { invoke } from '@tauri-apps/api/core'; -import { Editor, EditorProps } from '@monaco-editor/react'; +import { Editor, EditorProps, Monaco } from '@monaco-editor/react'; import type { Options as PrettierOptions } from 'prettier'; import prettier from 'prettier/standalone'; import babel from 'prettier/plugins/babel'; import estree from 'prettier/plugins/estree'; import typescript from 'prettier/plugins/typescript'; -import { CodeEditorProps, LintResult } from '@/shared'; +import { CodeEditorProps, LintResult, LintSeverity } from '@/shared'; const prettierOptions: PrettierOptions = { semi: true, @@ -19,6 +19,23 @@ const prettierOptions: PrettierOptions = { useTabs: false, }; +const severityToMarkerSeverity = (severity: LintSeverity, monaco: Monaco) => { + switch (severity) { + case 'error': + return monaco.MarkerSeverity.Error; + case 'warning': + return monaco.MarkerSeverity.Warning; + case 'info': + return monaco.MarkerSeverity.Info; + case 'hint': + return monaco.MarkerSeverity.Hint; + default: + // 타입 체크로 도달 불가능하지만, 런타임 안전을 위해 + console.warn(`Unknown severity: ${severity}, defaulting to Warning`); + return monaco.MarkerSeverity.Warning; + } +}; + export const CodeEditor: React.FC = ({ value, onChange, @@ -27,7 +44,7 @@ export const CodeEditor: React.FC = ({ theme = 'vs-dark', }) => { const editorRef = useRef(null); - const monacoRef = useRef(null); + const monacoRef = useRef(null); const disposablesRef = useRef>([]); const debounceTimeoutRef = useRef(null); @@ -56,10 +73,7 @@ export const CodeEditor: React.FC = ({ const endColumn = Math.max(startColumn + 1, result.end_column); // severity를 소문자로 비교하여 MarkerSeverity enum 사용 - const severity = - result.severity.toLowerCase() === 'error' - ? monaco.MarkerSeverity.Error - : monaco.MarkerSeverity.Warning; + const severity = severityToMarkerSeverity(result.severity, monaco); return { message: `${result.message} (${result.rule_id})`, From cb700c5ca09e8bdfc5d9a66b907bac65a6cfb34b Mon Sep 17 00:00:00 2001 From: Bori-github Date: Mon, 10 Nov 2025 16:10:36 +0900 Subject: [PATCH 08/11] =?UTF-8?q?chore:=20=EB=94=94=EB=B2=84=EA=B9=85?= =?UTF-8?q?=EC=9A=A9=20=EC=97=90=EB=9F=AC=20=EB=A1=9C=EA=B9=85=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/executeJS/src-tauri/src/commands.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/executeJS/src-tauri/src/commands.rs b/apps/executeJS/src-tauri/src/commands.rs index 0aecf37..3bccaa4 100644 --- a/apps/executeJS/src-tauri/src/commands.rs +++ b/apps/executeJS/src-tauri/src/commands.rs @@ -230,6 +230,12 @@ fn parse_oxlint_output(stdout: &str, stderr: &str) -> Vec { results } - Err(_) => Vec::new(), + Err(e) => { + // 에러 로깅 추가 + eprintln!("Failed to parse oxlint output: {}", e); + eprintln!("stdout: {}", stdout); + eprintln!("stderr: {}", stderr); + Vec::new() + } } } From 6fe079bbc9c5deaeade8f8d0227c54be53752c67 Mon Sep 17 00:00:00 2001 From: Bori-github Date: Mon, 10 Nov 2025 16:11:22 +0900 Subject: [PATCH 09/11] fix: format rust --- apps/executeJS/src-tauri/src/commands.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/executeJS/src-tauri/src/commands.rs b/apps/executeJS/src-tauri/src/commands.rs index 3bccaa4..6acb5c1 100644 --- a/apps/executeJS/src-tauri/src/commands.rs +++ b/apps/executeJS/src-tauri/src/commands.rs @@ -109,8 +109,8 @@ pub async fn lint_code(code: String) -> Result, String> { let temp_path = temp_file.path().to_str().ok_or("Invalid temp path")?; // 프로젝트 루트 경로 찾기 (현재 실행 파일 위치 기준) - let current_exe = std::env::current_exe() - .map_err(|e| format!("Failed to get current exe path: {}", e))?; + let current_exe = + std::env::current_exe().map_err(|e| format!("Failed to get current exe path: {}", e))?; // Tauri 앱의 경우: target/debug/executeJS 또는 target/release/executeJS // 프로젝트 루트는 3단계 위 (target/debug 또는 target/release) From 4f3f35466b6563a756b632aab9d61cb2e7599d95 Mon Sep 17 00:00:00 2001 From: Bori-github Date: Mon, 10 Nov 2025 16:45:16 +0900 Subject: [PATCH 10/11] =?UTF-8?q?chore:=20=EB=AA=85=ED=99=95=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EC=9D=80=20=EA=B8=B0=EB=B3=B8=EA=B0=92?= =?UTF-8?q?=EC=9D=84=20=EC=B5=9C=EC=86=8C=EA=B0=92=EC=9D=84=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/executeJS/src-tauri/src/commands.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/executeJS/src-tauri/src/commands.rs b/apps/executeJS/src-tauri/src/commands.rs index 6acb5c1..e632eea 100644 --- a/apps/executeJS/src-tauri/src/commands.rs +++ b/apps/executeJS/src-tauri/src/commands.rs @@ -190,8 +190,8 @@ fn parse_oxlint_output(stdout: &str, stderr: &str) -> Vec { let end_column = if span.length > 0 { column + span.length } else { - // length가 없을 경우 기본값 사용 - column + 10 + // length가 없을 경우 최소값 사용 + column + 1 }; // code에서 rule_id 추출 From fbc0fdb5855cd3cfdca8ecb2b74767f8506022c7 Mon Sep 17 00:00:00 2001 From: Bori-github Date: Fri, 14 Nov 2025 20:51:10 +0900 Subject: [PATCH 11/11] =?UTF-8?q?chore:=20severity=20=EB=B3=80=EC=88=98=20?= =?UTF-8?q?=ED=95=A0=EB=8B=B9=20=EB=B0=A9=EC=8B=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/executeJS/src/widgets/code-editor/code-editor.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/executeJS/src/widgets/code-editor/code-editor.tsx b/apps/executeJS/src/widgets/code-editor/code-editor.tsx index 75da291..046a339 100644 --- a/apps/executeJS/src/widgets/code-editor/code-editor.tsx +++ b/apps/executeJS/src/widgets/code-editor/code-editor.tsx @@ -77,7 +77,7 @@ export const CodeEditor: React.FC = ({ return { message: `${result.message} (${result.rule_id})`, - severity: severity, + severity, startLineNumber: result.line, startColumn: startColumn, endLineNumber: result.end_line,