@ape-egg/vibe-compiler 4.3.14 → 4.3.16
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
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ape-egg/vibe-compiler",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Optional Rust compiler for Vibe \u2014 pre-renders, inlines components, and optimizes iterations. The runtime never needs it.",
|
|
6
6
|
"homepage": "https://vibe.korte.kim",
|
package/src/Cargo.lock
CHANGED
package/src/Cargo.toml
CHANGED
package/src/compiler/compile.rs
CHANGED
|
@@ -1601,6 +1601,7 @@ impl Compiler {
|
|
|
1601
1601
|
|
|
1602
1602
|
// Raw pre-pass: read every page and detect the shared-layout shape
|
|
1603
1603
|
// BEFORE component inlining consumes the <component src> root.
|
|
1604
|
+
let component_names: Vec<&str> = parser.elements().keys().map(String::as_str).collect();
|
|
1604
1605
|
let mut raw_pages = Vec::new();
|
|
1605
1606
|
for path in &page_files {
|
|
1606
1607
|
let rel = path
|
|
@@ -1612,7 +1613,7 @@ impl Compiler {
|
|
|
1612
1613
|
path: path.display().to_string(),
|
|
1613
1614
|
source: e,
|
|
1614
1615
|
})?;
|
|
1615
|
-
let candidate = spa::detect_layout_root(&content);
|
|
1616
|
+
let candidate = spa::detect_layout_root(&content, &component_names);
|
|
1616
1617
|
raw_pages.push((rel, content, candidate));
|
|
1617
1618
|
}
|
|
1618
1619
|
let candidates: Vec<_> = raw_pages.iter().map(|(_, _, c)| c.clone()).collect();
|
package/src/compiler/spa.rs
CHANGED
|
@@ -260,10 +260,11 @@ pub fn dissect_page(rel_path: &str, html: &str) -> Result<SpaPage, String> {
|
|
|
260
260
|
|
|
261
261
|
/// Scan a page body for the hoistable-layout shape: ignoring whitespace,
|
|
262
262
|
/// comments and top-level <script> blocks, the body must be EXACTLY ONE
|
|
263
|
-
/// `<component src="…">` element
|
|
263
|
+
/// `<component src="…">` element or one tag named after a component
|
|
264
|
+
/// (`<Layout>`). Returns (open_tag, src or tag name, inner_html).
|
|
264
265
|
/// In-body scripts stay page code (they ride the fragment prelude), so they
|
|
265
266
|
/// don't break the shape.
|
|
266
|
-
pub fn detect_layout_root(raw_source: &str) -> Option<(String, String, String)> {
|
|
267
|
+
pub fn detect_layout_root(raw_source: &str, component_names: &[&str]) -> Option<(String, String, String)> {
|
|
267
268
|
let body_re = Regex::new(r"(?is)<body[^>]*>(.*)</body>").unwrap();
|
|
268
269
|
let body_caps = body_re.captures(raw_source)?;
|
|
269
270
|
let body_match = body_caps.get(1)?;
|
|
@@ -280,12 +281,21 @@ pub fn detect_layout_root(raw_source: &str) -> Option<(String, String, String)>
|
|
|
280
281
|
let cleaned = page_style_re.replace_all(&cleaned, "");
|
|
281
282
|
let trimmed = cleaned.trim();
|
|
282
283
|
|
|
283
|
-
let open_re = Regex::new(r#"(?
|
|
284
|
+
let open_re = Regex::new(r#"(?s)^<([A-Za-z][\w-]*)\b((?:"[^"]*"|'[^']*'|[^>"'])*)>"#).unwrap();
|
|
284
285
|
let caps = open_re.captures(trimmed)?;
|
|
285
286
|
let open_tag = caps.get(0).unwrap().as_str().to_string();
|
|
286
|
-
let
|
|
287
|
+
let name = caps.get(1).unwrap().as_str();
|
|
288
|
+
let attrs = caps.get(2).unwrap().as_str();
|
|
287
289
|
let src_re = Regex::new(r#"(?i)src\s*=\s*"([^"]+)""#).unwrap();
|
|
288
|
-
let src =
|
|
290
|
+
let (src, tag_re) = if name.eq_ignore_ascii_case("component") {
|
|
291
|
+
let src = src_re.captures(attrs)?.get(1).unwrap().as_str().to_string();
|
|
292
|
+
(src, Regex::new(r"(?i)<component\b|</component\s*>").unwrap())
|
|
293
|
+
} else if component_names.contains(&name) {
|
|
294
|
+
let name = regex::escape(name);
|
|
295
|
+
(name.clone(), Regex::new(&format!(r"<{name}\b|</{name}\s*>")).unwrap())
|
|
296
|
+
} else {
|
|
297
|
+
return None;
|
|
298
|
+
};
|
|
289
299
|
|
|
290
300
|
// Find the matching </component> by depth over a MASKED copy of the body:
|
|
291
301
|
// comments, scripts and styles are space-filled at identical byte offsets,
|
|
@@ -306,7 +316,6 @@ pub fn detect_layout_root(raw_source: &str) -> Option<(String, String, String)>
|
|
|
306
316
|
);
|
|
307
317
|
let start = masked_body.find(open_tag.as_str())?;
|
|
308
318
|
let after_open = start + open_tag.len();
|
|
309
|
-
let tag_re = Regex::new(r"(?i)<component\b|</component\s*>").unwrap();
|
|
310
319
|
let mut depth = 1usize;
|
|
311
320
|
let mut inner_end = None;
|
|
312
321
|
for m in tag_re.find_iter(&masked_body[after_open..]) {
|
|
@@ -532,11 +541,12 @@ pub fn compose_shell(
|
|
|
532
541
|
// state and timers persist, matching what a hand-written SPA shell does.
|
|
533
542
|
match hoisted_layout {
|
|
534
543
|
Some((open_tag, _src)) => {
|
|
544
|
+
let name = open_tag[1..].split(|c: char| c.is_whitespace() || c == '>').next().unwrap();
|
|
535
545
|
html.push_str(" ");
|
|
536
546
|
html.push_str(open_tag);
|
|
537
547
|
html.push('\n');
|
|
538
548
|
html.push_str(" <component src=\"@[page.src]\" key=\"@[page.path]\"></component>\n");
|
|
539
|
-
html.push_str(" </
|
|
549
|
+
html.push_str(&format!(" </{}>\n", name));
|
|
540
550
|
}
|
|
541
551
|
None => {
|
|
542
552
|
html.push_str(" <component src=\"@[page.src]\" key=\"@[page.path]\"></component>\n");
|