@@ -931,79 +931,7 @@ impl Parser {
931931 }
932932
933933 if let Some(rest) = content.strip_prefix("@include ") {
934− let filename = substitute_vars(rest.trim(), &ctx.variables);
935− let resolved = match &ctx.base_path {
936− Some(base) => base.join(&filename),
937− None => PathBuf::from(&filename),
938− };
939−
940− // Circular include check
941− if ctx.include_stack.contains(&resolved) {
942− let cycle_chain = format_include_chain(&ctx.include_stack);
943− ctx.diagnostics.push(Diagnostic {
944− line: line_num,
945− column: None,
946− message: format!(
947− "circular include '{}' (cycle: {} → {})",
948− filename, cycle_chain, filename
949− ),
950− severity: Severity::Error,
951− source_line: Some(content.clone()),
952− });
953− return Ok(None);
954− }
955−
956− // Use file cache to avoid redundant reads
957− let included_text = if let Some(cached) = ctx.file_cache.get(&resolved) {
958− cached.clone()
959− } else {
960− match std::fs::read_to_string(&resolved) {
961− Ok(text) => {
962− ctx.file_cache.insert(resolved.clone(), text.clone());
963− text
964− }
965− Err(e) => {
966− ctx.diagnostics.push(Diagnostic {
967− line: line_num,
968− column: None,
969− message: format!("cannot include '{}': {}", filename, e),
970− severity: Severity::Error,
971− source_line: Some(content.clone()),
972− });
973− return Ok(None);
974− }
975− }
976− };
977−
978− ctx.included_files.push(resolved.clone());
979− ctx.include_stack.push(resolved.clone());
980− let saved_base = ctx.base_path.clone();
981− ctx.base_path = resolved.parent().map(|p| p.to_path_buf());
982−
983− let diag_count_before = ctx.diagnostics.len();
984− let included_lines = preprocess(&included_text);
985− let mut included_parser = Parser {
986− lines: included_lines,
987− pos: 0,
988− };
989− let nodes = included_parser.parse_children(0, ctx);
990−
991− // Annotate new diagnostics with include chain
992− let include_chain = format_include_chain(&ctx.include_stack);
993− for d in &mut ctx.diagnostics[diag_count_before..] {
994− d.message = format!("{}\n in {}", d.message, include_chain);
995− }
996−
997− ctx.base_path = saved_base;
998− ctx.include_stack.pop();
999− return Ok(Some(nodes));
1000− }
1001−
1002− // --- @import (definitions only, no DOM nodes) ---
1003−
1004− if let Some(rest) = content.strip_prefix("@import ") {
1005934 let rest = rest.trim();
1006− // Support @import "file.hl" as prefix — namespace imported definitions
1007935 let (filename, alias) = if let Some((file_part, alias_part)) = rest.rsplit_once(" as ")
1008936 {
1009937 (
@@ -1077,13 +1005,14 @@ impl Parser {
10771005 });
10781006 return Ok(None);
10791007 }
1008+ let mut matched_nodes = Vec::new();
10801009 for file_path in matched_files {
10811010 let rel_name = file_path
10821011 .strip_prefix(&base_dir)
10831012 .unwrap_or(&file_path)
10841013 .to_string_lossy()
10851014 .to_string();
1086− // Synthesize an @import line for each matched file
1015+ // Synthesize an @include line for each matched file
10871016 let import_line = match &alias {
10881017 Some(pfx) => {
10891018 let stem = file_path
@@ -1091,18 +1020,18 @@ impl Parser {
10911020 .unwrap_or_default()
10921021 .to_string_lossy()
10931022 .to_string();
1094− format!("@import \"{}\" as {}.{}", rel_name, pfx, stem)
1023+ format!("@include \"{}\" as {}.{}", rel_name, pfx, stem)
10951024 }
1096− None => format!("@import \"{}\"", rel_name),
1025+ None => format!("@include \"{}\"", rel_name),
10971026 };
10981027 let synth_lines = preprocess(&import_line);
10991028 let mut synth_parser = Parser {
11001029 lines: synth_lines,
11011030 pos: 0,
11021031 };
1103− let _ = synth_parser.parse_children(0, ctx);
1032+ matched_nodes.extend(synth_parser.parse_children(0, ctx));
11041033 }
1105− return Ok(None);
1034+ return Ok(Some(matched_nodes));
11061035 }
11071036
11081037 let resolved = match &ctx.base_path {
@@ -1116,7 +1045,7 @@ impl Parser {
11161045 line: line_num,
11171046 column: None,
11181047 message: format!(
1119− "circular import '{}' (cycle: {} → {})",
1048+ "circular include '{}' (cycle: {} → {})",
11201049 filename, cycle_chain, filename
11211050 ),
11221051 severity: Severity::Error,
@@ -1137,7 +1066,7 @@ impl Parser {
11371066 ctx.diagnostics.push(Diagnostic {
11381067 line: line_num,
11391068 column: None,
1140− message: format!("cannot import '{}': {}", filename, e),
1069+ message: format!("cannot include '{}': {}", filename, e),
11411070 severity: Severity::Error,
11421071 source_line: Some(content.clone()),
11431072 });
@@ -1153,6 +1082,7 @@ impl Parser {
11531082 ctx.base_path = resolved.parent().map(|p| p.to_path_buf());
11541083
11551084 let diag_count_before = ctx.diagnostics.len();
1085+ let mut included_nodes = Vec::new();
11561086
11571087 if let Some(ref prefix) = alias {
11581088 // Snapshot just the *key sets* before parsing — much cheaper
@@ -1209,13 +1139,12 @@ impl Parser {
12091139 }
12101140 }
12111141 } else {
1212− // Parse the file but discard DOM nodes — only keep definitions
1213− let imported_lines = preprocess(&imported_text);
1214− let mut imported_parser = Parser {
1215− lines: imported_lines,
1142+ let included_lines = preprocess(&imported_text);
1143+ let mut included_parser = Parser {
1144+ lines: included_lines,
12161145 pos: 0,
12171146 };
1218− let _discarded_nodes = imported_parser.parse_children(0, ctx);
1147+ included_nodes = included_parser.parse_children(0, ctx);
12191148 }
12201149
12211150 // Annotate new diagnostics with import chain
@@ -1227,7 +1156,7 @@ impl Parser {
12271156 ctx.base_path = saved_base;
12281157 ctx.include_stack.pop();
12291158 ctx.import_stack.pop();
1230− return Ok(None); // No nodes emitted
1159+ return Ok(Some(included_nodes));
12311160 }
12321161
12331162 // --- @env (compile-time environment variables) ---
@@ -2714,7 +2643,6 @@ const KNOWN_DIRECTIVES: &[&str] = &[
27142643 "page",
27152644 "let",
27162645 "include",
2717− "import",
27182646 "raw",
27192647 "keyframes",
27202648 "if",
@@ -2776,7 +2704,11 @@ const REMOVED_SYNTAX: &[(&str, &str)] = &[
27762704 "@switch",
27772705 "use `@match`, with `@let __switch [...]` inside a case for its attributes",
27782706 ),
2779− ("@use", "use `@import`"),
2707+ ("@use", "use `@include`"),
2708+ (
2709+ "@import",
2710+ "use `@include` (a file with only definitions emits no content)",
2711+ ),
27802712 ("@with", "use `@let alias $source`"),
27812713 (
27822714 "@layout",