@@ -1,3 +1,6 @@
1+mod common;
2+
3+use common::codegen;
14use std::fs;
25use std::path::Path;
36
@@ -11,7 +14,7 @@ fn compile(input: &str) -> String {
1114 "unexpected parse errors: {:?}",
1215 result.diagnostics
1316 );
14− htmlang::codegen::generate(&result.document)
17+ codegen::generate(&result.document)
1518}
1619
1720fn compile_with_base(input: &str, base: &Path) -> String {
@@ -24,7 +27,7 @@ fn compile_with_base(input: &str, base: &Path) -> String {
2427 "unexpected parse errors: {:?}",
2528 result.diagnostics
2629 );
27− htmlang::codegen::generate(&result.document)
30+ codegen::generate(&result.document)
2831}
2932
3033fn parse_diagnostics(input: &str) -> Vec<htmlang::parser::Diagnostic> {
@@ -256,7 +259,7 @@ fn error_unknown_attribute() {
256259 diags
257260 );
258261 assert!(
259− htmlang::codegen::generate(&result.document).contains("bakground:red"),
262+ codegen::generate(&result.document).contains("bakground:red"),
260263 "an unknown property is passed through"
261264 );
262265}
@@ -313,7 +316,7 @@ fn values_pass_through_unchecked() {
313316 color rebeccapurple, width fit-content, font-weight 450, display blok]\n x";
314317 let result = htmlang::parser::parse(src);
315318 assert!(result.diagnostics.is_empty(), "{:?}", result.diagnostics);
316− let html = htmlang::codegen::generate(&result.document);
319+ let html = codegen::generate(&result.document);
317320 for css in [
318321 "padding:abc",
319322 "opacity:50%",
@@ -2748,7 +2751,7 @@ fn test_variable_filters() {
27482751 .iter()
27492752 .all(|d| d.severity != htmlang::parser::Severity::Error)
27502753 );
2751− let html = htmlang::codegen::generate(&result.document);
2754+ let html = codegen::generate(&result.document);
27522755 assert!(
27532756 html.contains("HELLO"),
27542757 "uppercase filter should work, got: {}",
@@ -2756,7 +2759,7 @@ fn test_variable_filters() {
27562759 );
27572760
27582761 let result = htmlang::parser::parse("@let name HELLO\n@text ${lowercase($name)}");
2759− let html = htmlang::codegen::generate(&result.document);
2762+ let html = codegen::generate(&result.document);
27602763 assert!(
27612764 html.contains("hello"),
27622765 "lowercase filter should work, got: {}",
@@ -2764,7 +2767,7 @@ fn test_variable_filters() {
27642767 );
27652768
27662769 let result = htmlang::parser::parse("@let name hello\n@text ${capitalize($name)}");
2767− let html = htmlang::codegen::generate(&result.document);
2770+ let html = codegen::generate(&result.document);
27682771 assert!(
27692772 html.contains("Hello"),
27702773 "capitalize filter should work, got: {}",
@@ -2772,7 +2775,7 @@ fn test_variable_filters() {
27722775 );
27732776
27742777 let result = htmlang::parser::parse("@let name hello\n@text ${length($name)}");
2775− let html = htmlang::codegen::generate(&result.document);
2778+ let html = codegen::generate(&result.document);
27762779 assert!(
27772780 html.contains("5"),
27782781 "length filter should work, got: {}",
@@ -2780,7 +2783,7 @@ fn test_variable_filters() {
27802783 );
27812784
27822785 let result = htmlang::parser::parse("@let name hello\n@text ${reverse($name)}");
2783− let html = htmlang::codegen::generate(&result.document);
2786+ let html = codegen::generate(&result.document);
27842787 assert!(
27852788 html.contains("olleh"),
27862789 "reverse filter should work, got: {}",
@@ -2788,7 +2791,7 @@ fn test_variable_filters() {
27882791 );
27892792
27902793 let result = htmlang::parser::parse("@let name hello world\n@text ${truncate($name, 5)}");
2791− let html = htmlang::codegen::generate(&result.document);
2794+ let html = codegen::generate(&result.document);
27922795 assert!(
27932796 html.contains("hello..."),
27942797 "truncate filter should work, got: {}",
@@ -2799,7 +2802,7 @@ fn test_variable_filters() {
27992802#[test]
28002803fn test_css_shorthands_output() {
28012804 let result = htmlang::parser::parse("@text [$truncate] Hello");
2802− let html = htmlang::codegen::generate(&result.document);
2805+ let html = codegen::generate(&result.document);
28032806 assert!(
28042807 html.contains("text-overflow:ellipsis"),
28052808 "truncate should add ellipsis, got: {}",
@@ -2811,7 +2814,7 @@ fn test_css_shorthands_output() {
28112814 );
28122815
28132816 let result = htmlang::parser::parse("@paragraph [line-clamp 3] Text");
2814− let html = htmlang::codegen::generate(&result.document);
2817+ let html = codegen::generate(&result.document);
28152818 assert!(
28162819 html.contains("-webkit-line-clamp:3"),
28172820 "line-clamp should work, got: {}",
@@ -2819,7 +2822,7 @@ fn test_css_shorthands_output() {
28192822 );
28202823
28212824 let result = htmlang::parser::parse("@el [filter blur(4px)] Content");
2822− let html = htmlang::codegen::generate(&result.document);
2825+ let html = codegen::generate(&result.document);
28232826 assert!(
28242827 html.contains("filter:blur(4px)"),
28252828 "blur should work, got: {}",
@@ -2827,7 +2830,7 @@ fn test_css_shorthands_output() {
28272830 );
28282831
28292832 let result = htmlang::parser::parse("@el [backdrop-filter blur(10px)] Content");
2830− let html = htmlang::codegen::generate(&result.document);
2833+ let html = codegen::generate(&result.document);
28312834 assert!(
28322835 html.contains("backdrop-filter:blur(10px)"),
28332836 "backdrop-blur should work, got: {}",
@@ -2865,7 +2868,7 @@ fn test_theme_directive() {
28652868 "theme should not cause errors: {:?}",
28662869 errors
28672870 );
2868− let html = htmlang::codegen::generate(&result.document);
2871+ let html = codegen::generate(&result.document);
28692872 assert!(
28702873 html.contains("--primary:#3b82f6"),
28712874 "theme should emit CSS vars, got: {}",
@@ -2991,7 +2994,7 @@ fn a_token_is_named_like_a_custom_property_on_an_element() {
29912994#[test]
29922995fn test_autofocus_attribute() {
29932996 let result = htmlang::parser::parse("@input [type=text, autofocus]");
2994− let html = htmlang::codegen::generate(&result.document);
2997+ let html = codegen::generate(&result.document);
29952998 assert!(
29962999 html.contains("autofocus"),
29973000 "autofocus should be in output, got: {}",
@@ -3681,8 +3684,8 @@ fn switch_with_attrs() {
36813684fn minified_output_is_smaller() {
36823685 let input = "@page Test\n@el [padding 20]\n @text [font-weight bold] Hello World\n @paragraph\n Some text here\n";
36833686 let result = htmlang::parser::parse(input);
3684− let normal = htmlang::codegen::generate(&result.document);
3685− let minified = htmlang::codegen::generate_minified(&result.document);
3687+ let normal = codegen::generate(&result.document);
3688+ let minified = codegen::generate_minified(&result.document);
36863689 assert!(
36873690 minified.len() <= normal.len(),
36883691 "minified ({}) should be <= normal ({})",
@@ -3696,8 +3699,8 @@ fn minified_output_is_smaller() {
36963699fn minified_strips_comments() {
36973700 let input = "@page Test\n@el\n @text Hello\n";
36983701 let result = htmlang::parser::parse(input);
3699− let dev = htmlang::codegen::generate_dev(&result.document);
3700− let minified = htmlang::codegen::generate_minified(&result.document);
3702+ let dev = codegen::generate_dev(&result.document);
3703+ let minified = codegen::generate_minified(&result.document);
37013704 // Dev mode has comments, minified should not
37023705 assert!(dev.contains("<!--"));
37033706 assert!(!minified.contains("<!--"));
@@ -4201,7 +4204,7 @@ fn snapshot_responsive_images() {
42014204fn source_map_generation() {
42024205 let input = "@page Test\n@text [font-weight bold] Hello";
42034206 let result = htmlang::parser::parse(input);
4204− let map = htmlang::codegen::generate_source_map(&result.document, "test.hl");
4207+ let map = codegen::generate_source_map(&result.document, "test.hl");
42054208 assert!(
42064209 map.contains("\"version\":3"),
42074210 "source map should have version 3"
@@ -4293,7 +4296,7 @@ fn svg_directive_inline() {
42934296 "should parse without errors: {:?}",
42944297 result.diagnostics
42954298 );
4296− let html = htmlang::codegen::generate(&result.document);
4299+ let html = codegen::generate(&result.document);
42974300 assert!(
42984301 html.contains("<svg"),
42994302 "should inline SVG content, got: {}",
@@ -4324,7 +4327,7 @@ fn svg_directive_with_attrs() {
43244327 svg_path.display()
43254328 );
43264329 let result = htmlang::parser::parse(&input);
4327− let html = htmlang::codegen::generate(&result.document);
4330+ let html = codegen::generate(&result.document);
43284331 assert!(
43294332 html.contains("style=\"width:24px;fill:red\""),
43304333 "should override width, got: {}",
@@ -4363,7 +4366,7 @@ fn css_property_directive() {
43634366 "should parse without errors: {:?}",
43644367 result.diagnostics
43654368 );
4366− let html = htmlang::codegen::generate(&result.document);
4369+ let html = codegen::generate(&result.document);
43674370 assert!(
43684371 html.contains("@property --my-color"),
43694372 "@property rule should be emitted, got: {}",
@@ -4389,7 +4392,7 @@ fn css_property_directive() {
43894392#[test]
43904393fn partial_output() {
43914394 let result = htmlang::parser::parse("@page Test\n@el [padding 20]\n Hello");
4392− let html = htmlang::codegen::generate_partial(&result.document);
4395+ let html = codegen::generate_partial(&result.document);
43934396 assert!(
43944397 !html.contains("<!DOCTYPE"),
43954398 "partial should not have doctype, got: {}",
@@ -4425,7 +4428,7 @@ fn partial_output() {
44254428#[test]
44264429fn partial_output_dev() {
44274430 let result = htmlang::parser::parse("@page Test\n@el [padding 20]\n Hello");
4428− let html = htmlang::codegen::generate_partial_dev(&result.document);
4431+ let html = codegen::generate_partial_dev(&result.document);
44294432 assert!(
44304433 !html.contains("<!DOCTYPE"),
44314434 "partial dev should not have doctype"
@@ -4466,7 +4469,7 @@ fn auto_image_dimensions_respects_explicit() {
44664469 png_path.display()
44674470 );
44684471 let result = htmlang::parser::parse(&input);
4469− let html = htmlang::codegen::generate(&result.document);
4472+ let html = codegen::generate(&result.document);
44704473 // When width/height are set as CSS attrs, auto-dimensions should not add HTML width/height
44714474 assert!(
44724475 html.contains("width:100px"),
@@ -4503,7 +4506,7 @@ fn perf_large_document() {
45034506 let input = std::fs::read_to_string(dir.join("stress_large.hl")).unwrap();
45044507 let start = std::time::Instant::now();
45054508 let result = htmlang::parser::parse(&input);
4506− let _ = htmlang::codegen::generate(&result.document);
4509+ let _ = codegen::generate(&result.document);
45074510 let elapsed = start.elapsed();
45084511 assert!(
45094512 elapsed.as_millis() < 5000,
@@ -4576,7 +4579,7 @@ fn markdown_file_renders_content() {
45764579
45774580 let input = "@markdown article.md\n";
45784581 let result = htmlang::parser::parse_with_base(input, Some(&dir));
4579− let html = htmlang::codegen::generate(&result.document);
4582+ let html = codegen::generate(&result.document);
45804583 let _ = std::fs::remove_dir_all(&dir);
45814584
45824585 assert!(
@@ -4609,7 +4612,7 @@ fn markdown_file_with_variable_path() {
46094612
46104613 let input = "@let file post.md\n@markdown $file\n";
46114614 let result = htmlang::parser::parse_with_base(input, Some(&dir));
4612− let html = htmlang::codegen::generate(&result.document);
4615+ let html = codegen::generate(&result.document);
46134616 let _ = std::fs::remove_dir_all(&dir);
46144617
46154618 assert!(
@@ -4746,7 +4749,7 @@ fn only_a_function_takes_a_body() {
47464749 assert_eq!(found[0].0, 3);
47474750 assert!(found[0].1.contains("`@let @card"), "{}", found[0].1);
47484751 let result = htmlang::parser::parse(&input);
4749− let html = htmlang::codegen::generate(&result.document);
4752+ let html = codegen::generate(&result.document);
47504753 assert!(!html.contains("Body"), "{}: {}", head, html);
47514754 }
47524755}
@@ -4780,7 +4783,7 @@ fn a_let_needs_a_bare_name_and_a_value() {
47804783 }
47814784 // The body of a `@let` without a value isn't rendered either
47824785 let result = htmlang::parser::parse("@let card\n @el Body\n");
4783− assert!(!htmlang::codegen::generate(&result.document).contains("Body"));
4786+ assert!(!codegen::generate(&result.document).contains("Body"));
47844787
47854788 for name in ["1x", "a!b", "true"] {
47864789 let input = format!("@let {} = 1\n", name);
@@ -5054,7 +5057,7 @@ fn a_parameter_passed_with_equals_is_an_error() {
50545057 // Not also reported as missing, and not forwarded as an HTML attribute
50555058 assert!(coded(&diags, "missing-parameter").is_empty(), "{:?}", diags);
50565059 let result = htmlang::parser::parse("@let @card [title]\n @el $title\n@card [title=Hi]\n");
5057− let out = htmlang::codegen::generate(&result.document);
5060+ let out = codegen::generate(&result.document);
50585061 assert!(out.contains("Hi"), "{}", out);
50595062 assert!(!out.contains("title=\"Hi\""), "{}", out);
50605063}
@@ -5311,7 +5314,7 @@ fn a_function_in_its_own_callers_content_is_not_recursion() {
53115314 let src = "@let @box\n @el [padding 4]\n @children\n@box\n @box\n Inner\n {@box x}\n@box {@box y}\n";
53125315 let result = htmlang::parser::parse(src);
53135316 assert!(result.diagnostics.is_empty(), "{:?}", result.diagnostics);
5314− let out = htmlang::codegen::generate(&result.document);
5317+ let out = codegen::generate(&result.document);
53155318 // The two calls inside text are inline-flex spans
53165319 assert_eq!(out.matches("<div").count(), 3, "{}", out);
53175320 assert_eq!(out.matches("<span class=").count(), 2, "{}", out);
@@ -5705,7 +5708,7 @@ fn an_included_file_s_slots_are_where_its_include_is() {
57055708 Some(&dir),
57065709 );
57075710 assert!(result.diagnostics.is_empty(), "{:?}", result.diagnostics);
5708− let html = htmlang::codegen::generate(&result.document);
5711+ let html = codegen::generate(&result.document);
57095712 for text in ["Hello", "Foot", "From fillers", "Plain"] {
57105713 assert!(html.contains(text), "{}: {}", text, html);
57115714 }
@@ -5866,7 +5869,7 @@ fn css_if_in_a_value_is_css() {
58665869 "@el [width if(media(width > 40em): 50%; else: 100%), color if(style(--dark: 1): white; else: black)]\n x\n",
58675870 );
58685871 assert!(result.diagnostics.is_empty(), "{:?}", result.diagnostics);
5869− let html = htmlang::codegen::generate(&result.document);
5872+ let html = codegen::generate(&result.document);
58705873 assert!(
58715874 html.contains("width:if(media(width > 40em): 50%; else: 100%);"),
58725875 "{}",
@@ -5978,7 +5981,7 @@ fn the_after_snippet_of_names_not_values() {
59785981 let unknown = coded(&result.diagnostics, "unknown-attribute");
59795982 assert_eq!(unknown.len(), 1, "{:?}", result.diagnostics);
59805983 assert_eq!(unknown[0].severity, htmlang::parser::Severity::Warning);
5981− assert!(htmlang::codegen::generate(&result.document).contains("text-grow:per-line"));
5984+ assert!(codegen::generate(&result.document).contains("text-grow:per-line"));
59825985
59835986 let d = parse_diagnostics("@let name Ada\n@text Hello $nmae\n");
59845987 let undefined = coded(&d, "undefined-variable");
@@ -6011,7 +6014,7 @@ fn unknown_css_properties_pass_through_with_a_warning() {
60116014 assert_eq!(unknown[0].subject.as_deref(), Some("colr"));
60126015 assert_eq!(unknown[0].suggestion.as_deref(), Some("color"));
60136016 assert_eq!(unknown[0].column, Some(5));
6014− let html = htmlang::codegen::generate(&result.document);
6017+ let html = codegen::generate(&result.document);
60156018 for css in [
60166019 "colr:red",
60176020 "--gap:12px",
@@ -6045,7 +6048,7 @@ fn what_cannot_be_css_is_an_error_and_left_out() {
60456048 src,
60466049 result.diagnostics
60476050 );
6048− let html = htmlang::codegen::generate(&result.document);
6051+ let html = codegen::generate(&result.document);
60496052 assert!(
60506053 !html.contains("center-z") && !html.contains("email"),
60516054 "{}",
@@ -6103,7 +6106,7 @@ fn selector_prefixes_are_css_pseudo_classes_and_elements() {
61036106 @el [not([title=\") x\"]):color blue] x\n";
61046107 let result = htmlang::parser::parse(src);
61056108 assert!(result.diagnostics.is_empty(), "{:?}", result.diagnostics);
6106− let html = htmlang::codegen::generate(&result.document);
6109+ let html = codegen::generate(&result.document);
61076110 for css in [
61086111 ":first-child{font-weight:700;}",
61096112 ":nth-child(odd){background:var(--subtle);}",
@@ -6170,7 +6173,7 @@ fn values_that_would_break_out_of_the_css_rule_are_errors() {
61706173 result.diagnostics
61716174 );
61726175 // The value is left out of the page
6173− let html = htmlang::codegen::generate(&result.document);
6176+ let html = codegen::generate(&result.document);
61746177 assert!(
61756178 !html.contains("background blue") && !html.contains("{3}"),
61766179 "{}",
@@ -6220,7 +6223,7 @@ fn a_style_whose_value_comes_out_empty_is_left_out() {
62206223 "@data $p {\"title\": \"Hi\"}\n@let on false\n@el [padding $p.gap, margin ${if($on, 4)}, color red]\n $p.title\n",
62216224 );
62226225 assert!(result.diagnostics.is_empty(), "{:?}", result.diagnostics);
6223− let html = htmlang::codegen::generate(&result.document);
6226+ let html = codegen::generate(&result.document);
62246227 assert!(
62256228 !html.contains("padding:") && !html.contains("margin:"),
62266229 "{}",
@@ -6330,7 +6333,7 @@ fn a_warning_in_a_loop_or_a_function_is_reported_once() {
63306333fn hidden_is_a_boolean_attribute() {
63316334 let result = htmlang::parser::parse("@el [hidden] x\n");
63326335 assert!(result.diagnostics.is_empty(), "{:?}", result.diagnostics);
6333− assert!(htmlang::codegen::generate(&result.document).contains("<div class=\"hl-a\" hidden>"));
6336+ assert!(codegen::generate(&result.document).contains("<div class=\"hl-a\" hidden>"));
63346337}
63356338
63366339#[test]
@@ -6527,7 +6530,7 @@ fn spacing_goes_only_on_a_row_column_or_grid() {
65276530 );
65286531 assert!(errors[0].message.contains(name), "{}: {:?}", src, errors);
65296532 // Left out of the page
6530− let out = htmlang::codegen::generate(&result.document);
6533+ let out = codegen::generate(&result.document);
65316534 assert!(!out.contains("gap:"), "{}: {}", src, out);
65326535 assert!(!out.contains("flex-wrap"), "{}: {}", src, out);
65336536 assert!(!out.contains("grid-template"), "{}: {}", src, out);
@@ -6575,7 +6578,7 @@ fn fill_and_center_compile_against_the_parent_s_layout() {
65756578 "{:?}",
65766579 result.diagnostics
65776580 );
6578− assert!(!htmlang::codegen::generate(&result.document).contains("flex:1"));
6581+ assert!(!codegen::generate(&result.document).contains("flex:1"));
65796582}
65806583
65816584#[test]
@@ -7385,7 +7388,7 @@ fn an_element_inside_text_is_not_a_child_of_the_row_around_it() {
73857388/// Output with the errors allowed, for tests of what an error leaves.
73867389fn compile_anyway(input: &str) -> (String, Vec<htmlang::parser::Diagnostic>) {
73877390 let result = htmlang::parser::parse(input);
7388− let html = htmlang::codegen::generate_partial(&result.document);
7391+ let html = codegen::generate_partial(&result.document);
73897392 (html, result.diagnostics)
73907393}
73917394
@@ -7740,7 +7743,7 @@ fn readable_output_adds_no_whitespace_inside_pre_and_textarea() {
77407743 "@el\n @pre > @code\n a\n b\n @textarea [aria-label=x]\n c\n",
77417744 )
77427745 .document;
7743− let out = htmlang::codegen::generate_dev(&doc);
7746+ let out = codegen::generate_dev(&doc);
77447747 assert!(
77457748 out.contains("><code class=\"hl-c\">a\n b</code></pre>\n"),
77467749 "{}",
@@ -8198,7 +8201,7 @@ fn a_custom_property_is_a_style_on_any_element_under_any_prefix() {
81988201 "@el [--a 1, hover:--b 2, md:--c 3, dark:--d 4, children:--e 5, nth-child(2n):--f 6, has(.x):--g 7, print:--h 8, cq-md:--i 9]\n x\n@image [alt=x, --w 20px] a.png\n@paragraph {@em [--j 1] y}\n",
81998202 );
82008203 assert!(result.diagnostics.is_empty(), "{:?}", result.diagnostics);
8201− let html = htmlang::codegen::generate(&result.document);
8204+ let html = codegen::generate(&result.document);
82028205 for declaration in [
82038206 "--a:1;",
82048207 "--b:2;",
@@ -8228,7 +8231,7 @@ fn a_vendor_prefixed_property_is_a_style_under_any_prefix() {
82288231 "@el [-webkit-text-stroke 1px red, hover:-webkit-text-stroke 2px red, dark:-webkit-tap-highlight-color transparent, -moz-osx-font-smoothing grayscale, -ms-overflow-style none]\n x\n",
82298232 );
82308233 assert!(result.diagnostics.is_empty(), "{:?}", result.diagnostics);
8231− let html = htmlang::codegen::generate(&result.document);
8234+ let html = codegen::generate(&result.document);
82328235 for declaration in [
82338236 "-webkit-text-stroke:1px red;",
82348237 ":hover{-webkit-text-stroke:2px red;}",
@@ -8264,7 +8267,7 @@ fn a_custom_property_on_a_line_of_a_list_is_a_declaration() {
82648267 "@el [\n padding 4,\n --surface white,\n -- a comment\n --\n color red\n]\n x\n",
82658268 );
82668269 assert!(result.diagnostics.is_empty(), "{:?}", result.diagnostics);
8267− let html = htmlang::codegen::generate(&result.document);
8270+ let html = codegen::generate(&result.document);
82688271 assert!(
82698272 html.contains("padding:4px;--surface:white;color:red;"),
82708273 "{}",
@@ -8296,7 +8299,7 @@ fn a_style_an_inline_svg_can_t_take_is_an_error() {
82968299 "@image [inline, width 24, color red, class=icon, aria-label=Logo, role=img] i.svg\n@image [inline, --w 2px, hover:color blue, alt=x] i.svg\n",
82978300 Some(&dir),
82988301 );
8299− let html = htmlang::codegen::generate(&result.document);
8302+ let html = codegen::generate(&result.document);
83008303 assert!(
83018304 html.contains(
83028305 "<svg viewBox=\"0 0 1 1\" class=\"icon\" aria-label=\"Logo\" role=\"img\" style=\"width:24px;fill:red\">"
@@ -8331,7 +8334,7 @@ fn a_style_an_inline_svg_can_t_take_is_an_error() {
83318334 "{}",
83328335 errors[0].message
83338336 );
8334− let html = htmlang::codegen::generate(&result.document);
8337+ let html = codegen::generate(&result.document);
83358338 assert!(!html.contains("width=\"fill\""), "{}", html);
83368339 assert!(html.contains("style=\"height:2em\""), "{}", html);
83378340 let _ = fs::remove_dir_all(&dir);