From 7c211d8b0d790141f47d8ea1b4507d0af372d17c Mon Sep 17 00:00:00 2001 From: phonedu <135960865+phonedu@users.noreply.github.com> Date: Thu, 22 Oct 2026 14:45:38 +0800 Subject: [PATCH] Review: escape Vue binding attributes and use the template's indent - usage: a non-plain Vue argument escapes " as " in the attribute value (same rule as the connected-code path) - usage: Vue pads with two spaces like the markup, not four - test: an unconnected instance with a quoted title asserts inspect.componentCode and inspect.code --- crates/engine/src/component_codegen.rs | 4 ++-- crates/engine/src/component_codegen_tests.rs | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/engine/src/component_codegen.rs b/crates/engine/src/component_codegen.rs index 8e568313..2e74f3d3 100644 --- a/crates/engine/src/component_codegen.rs +++ b/crates/engine/src/component_codegen.rs @@ -526,7 +526,7 @@ pub(crate) fn instance_values(doc: &Document, id: NodeId, c: &Component) -> Opti pub fn usage(doc: &Document, id: NodeId, lang: CodeLang, depth: usize) -> Option { let c = main_of(doc, id).and_then(|m| describe(doc, m))?; let values = instance_values(doc, id, &c)?; - let pad = if lang == CodeLang::React { " ".repeat(depth) } else { indent(depth) }; + let pad = if matches!(lang, CodeLang::React | CodeLang::Vue) { " ".repeat(depth) } else { indent(depth) }; // A connected component: its real snippet. shortcut: inside a layer tree it brings no import // line; collect them at the top of the tree's code when people ask for it. if let Some(link) = crate::code_connect::link(doc, id, lang) { @@ -574,7 +574,7 @@ pub fn usage(doc: &Document, id: NodeId, lang: CodeLang, depth: usize) -> Option let attrs: String = args .iter() .filter(|(name, _, _)| !is_slot(name)) - .map(|(name, expr, plain)| if *plain { format!(" {name}={expr}") } else { format!(" :{name}=\"{expr}\"") }) + .map(|(name, expr, plain)| if *plain { format!(" {name}={expr}") } else { format!(" :{name}=\"{}\"", expr.replace('"', """)) }) .collect(); let slots: String = args .iter() diff --git a/crates/engine/src/component_codegen_tests.rs b/crates/engine/src/component_codegen_tests.rs index 4cd3d776..9e3e65f3 100644 --- a/crates/engine/src/component_codegen_tests.rs +++ b/crates/engine/src/component_codegen_tests.rs @@ -124,6 +124,11 @@ fn instances_are_calls_passing_the_values_they_set() { assert_eq!(component_code(&mut s, i, "swiftui")["usage"], "Card(showDot: false, title: \"Say \\\"hi\\\" $5\\nnow\")\n"); assert_eq!(component_code(&mut s, i, "compose")["usage"], "Card(showDot = false, title = \"Say \\\"hi\\\" \\$5\\nnow\")\n"); assert_eq!(component_code(&mut s, i, "leptos")["usage"], "\n"); + // A quoted title becomes a binding whose attribute value is escaped: the expression itself + // keeps its JS quotes, and the attribute's own quotes are entities. + assert_eq!(component_code(&mut s, i, "vue")["usage"], "\n"); + let vue = code(&mut s, i, "vue"); + assert!(vue.starts_with("\n"), "{vue}"); } #[test]