From 64bb5bf8bd858eb8016a546d31e26f2691989612 Mon Sep 17 00:00:00 2001 From: ajianaz Date: Fri, 24 Jul 2026 14:47:29 +0700 Subject: [PATCH] feat: add Svelte symbol indexing (#375) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Component name derived from filename (kebab-case → PascalCase) - Extract ", + ) + .unwrap() +}); +static RE_SVELTE_EXPORT_PROP: LazyLock = + LazyLock::new(|| Regex::new(r"^\s*export\s+let\s+(\w+)").unwrap()); +static RE_SVELTE_STATE: LazyLock = + LazyLock::new(|| Regex::new(r"^\s*let\s+(\w+)\s*=\s*\$state(?:<[^>]*>)?\s*\(").unwrap()); +static RE_SVELTE_DERIVED: LazyLock = + LazyLock::new(|| Regex::new(r"^\s*let\s+(\w+)\s*=\s*\$derived(?:<[^>]*>)?\s*\(").unwrap()); +static RE_SVELTE_EFFECT: LazyLock = + LazyLock::new(|| Regex::new(r"^\s*\$effect\s*\(\s*(?:async\s+)?\(\s*\)\s*=>").unwrap()); /// Extract symbols from source code. /// @@ -206,6 +224,7 @@ pub fn extract_symbols(content: &str, language: &str, file_path: &str) -> Vec extract_lua(line, line_no, file_path, line, &mut symbols), "zig" => extract_zig(line, line_no, file_path, line, &mut symbols), "dart" => extract_dart(line, line_no, file_path, line, &mut symbols), + "svelte" => extract_svelte(content, file_path, &mut symbols), _ => {} } } @@ -409,6 +428,79 @@ fn extract_dart(line: &str, line_no: u32, file: &str, raw: &str, out: &mut Vec` blocks and delegate to TS/JS extractor +/// 3. Extract Svelte-specific: export props, $state, $derived +fn extract_svelte(content: &str, file: &str, out: &mut Vec) { + // 1. Component name from filename + let component_name = std::path::Path::new(file) + .file_stem() + .and_then(|s| s.to_str()) + .map(|s| { + let mut result = String::new(); + let mut capitalize_next = true; + for ch in s.chars() { + if ch == '-' || ch == '_' { + capitalize_next = true; + } else if capitalize_next { + result.push(ch.to_ascii_uppercase()); + capitalize_next = false; + } else { + result.push(ch); + } + } + result + }) + .unwrap_or_default(); + + if !component_name.is_empty() { + out.push(ExtractedDef { + name: component_name.clone(), + kind: SymbolKind::Struct, + file: file.to_string(), + line: 1, + signature: format!("", component_name), + }); + } + + // 2. Extract + + + +{#if count > 0} +

Counting!

+{/if} + + +"#; + + let symbols = extract_symbols(content, "svelte", "src/lib/Counter.svelte"); + let names: Vec<&str> = symbols.iter().map(|s| s.name.as_str()).collect(); + println!("Svelte symbols: {:?}", names); + + assert!(names.contains(&"Counter"), "missing component name"); + assert!(names.contains(&"name"), "missing prop: name"); + assert!(names.contains(&"count"), "missing prop: count"); + assert!( + names.iter().any(|n| n.starts_with("doubled")), + "missing $derived: doubled" + ); + assert!( + names.iter().any(|n| n.starts_with("items")), + "missing $state: items" + ); + assert!( + names.contains(&"handleClick"), + "missing function: handleClick" + ); + assert!(names.contains(&"fetchData"), "missing function: fetchData"); + } }