@ape-egg/vibe-compiler 4.3.27 → 4.3.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ape-egg/vibe-compiler",
3
- "version": "4.3.27",
3
+ "version": "4.3.28",
4
4
  "type": "module",
5
5
  "description": "Optional Rust compiler for Vibe — pre-renders, inlines components, and optimizes iterations. The runtime never needs it.",
6
6
  "homepage": "https://vibe.korte.kim",
@@ -7,6 +7,7 @@ use serde_json::Value;
7
7
  use std::cell::RefCell;
8
8
  use std::collections::{HashMap, BTreeMap};
9
9
  use crate::compiler::iteration_optimizer::build_iteration_optimizations;
10
+ use crate::constants::DEFER_ATTR_PREFIX;
10
11
 
11
12
  pub struct ManifestBuilder {
12
13
  binding_regex: Regex,
@@ -127,7 +128,7 @@ impl ManifestBuilder {
127
128
  }
128
129
 
129
130
  NodeData::Element { name, attrs, .. } => {
130
- let _tag_name = name.local.to_string();
131
+ let tag_name = name.local.to_string();
131
132
 
132
133
  // Check attribute bindings (both value and name bindings)
133
134
  let mut attr_bindings = BTreeMap::new();
@@ -170,9 +171,25 @@ impl ManifestBuilder {
170
171
  // Store if we have any bindings
171
172
  let has_name_bindings = name_bindings.iter().any(|b| b.is_some());
172
173
 
174
+ // A fetched component binds only src and key, as the runtime's
175
+ // parse does: every other attribute is a prop substituted into the
176
+ // fetched template. Bound here, a prop hydrates as a plain
177
+ // attribute and a falsy one strips itself before the fetch reads
178
+ // it. Restoration still carries every binding, so a stamped prop
179
+ // is back in its raw form before the fetch.
180
+ let attr_value = |n: &str| borrowed_attrs.iter().find(|a| &*a.name.local == n).map(|a| a.value.to_string());
181
+ let fetched = (tag_name == "component"
182
+ || (tag_name == "div" && attr_value("class").is_some_and(|c| c.split_whitespace().any(|c| c == "component"))))
183
+ && (attr_value("src").is_some() || attr_value(&format!("{}src", DEFER_ATTR_PREFIX)).is_some());
184
+ let bound: BTreeMap<String, String> = attr_bindings
185
+ .iter()
186
+ .filter(|(k, _)| !fetched || ["src", "key"].contains(&k.as_str()))
187
+ .map(|(k, v)| (k.clone(), v.clone()))
188
+ .collect();
189
+
173
190
  // Set runtime-compatible fields at node level
174
- if !attr_bindings.is_empty() {
175
- manifest_node.attributes = Some(attr_bindings.clone());
191
+ if !bound.is_empty() {
192
+ manifest_node.attributes = Some(bound);
176
193
  }
177
194
  if has_name_bindings {
178
195
  manifest_node.name_bindings = Some(name_bindings.clone());
@@ -667,6 +684,24 @@ mod tests {
667
684
  assert_eq!(result, vec!["Hello World"]);
668
685
  }
669
686
 
687
+ // A prop on a fetched component is no attribute binding: hydrated as one, a
688
+ // falsy `open="@[flag]"` strips itself before the fetch reads it. It is
689
+ // still restored, so the fetch sees it raw.
690
+ #[test]
691
+ fn fetched_component_binds_only_src_and_key() {
692
+ let html = r#"<component src="@[src]" key="@[id]" open="@[flag]"></component><component open="@[flag]"></component>"#;
693
+ let manifest = ManifestBuilder::new().build_from_html(html, &json!({}), false).unwrap();
694
+ let body = manifest.children.get("html_0").unwrap().children.get("body_1").unwrap();
695
+
696
+ let fetched = body.children.get("component_0").unwrap();
697
+ assert_eq!(fetched.attributes.as_ref().unwrap().keys().collect::<Vec<_>>(), ["key", "src"]);
698
+ let restored = fetched.compiled.as_ref().unwrap().restoration.as_ref().unwrap().attributes.as_ref().unwrap();
699
+ assert_eq!(restored.get("open").unwrap(), "@[flag]");
700
+
701
+ let inline = body.children.get("component_1").unwrap();
702
+ assert_eq!(inline.attributes.as_ref().unwrap().get("open").unwrap(), "@[flag]");
703
+ }
704
+
670
705
  #[test]
671
706
  fn build_multiple_name_bindings() {
672
707
  let html = r#"<div test @[theme] @[size]></div>"#;