diff --git a/CHANGELOG.md b/CHANGELOG.md index a8982c4b23ce..16cf63f53d83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Ensure negative arbitrary `scale` values generate negative values ([#17831](https://github.com/tailwindlabs/tailwindcss/pull/17831)) +- Fix HAML extraction with embedded Ruby ([#17846](https://github.com/tailwindlabs/tailwindcss/pull/17846)) ## [4.1.5] - 2025-04-30 diff --git a/Cargo.lock b/Cargo.lock index 1f2af8b554b9..694700fcbf73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -582,6 +582,7 @@ dependencies = [ "tempfile", "tracing", "tracing-subscriber", + "unicode-width", "walkdir", ] @@ -681,6 +682,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "valuable" version = "0.1.0" diff --git a/crates/oxide/Cargo.toml b/crates/oxide/Cargo.toml index 3c5624b45d41..9b800b04994c 100644 --- a/crates/oxide/Cargo.toml +++ b/crates/oxide/Cargo.toml @@ -23,3 +23,4 @@ regex = "1.11.1" [dev-dependencies] tempfile = "3.13.0" pretty_assertions = "1.4.1" +unicode-width = "0.2.0" diff --git a/crates/oxide/src/extractor/pre_processors/haml.rs b/crates/oxide/src/extractor/pre_processors/haml.rs index 2e535bf41193..a32c12950977 100644 --- a/crates/oxide/src/extractor/pre_processors/haml.rs +++ b/crates/oxide/src/extractor/pre_processors/haml.rs @@ -3,6 +3,8 @@ use crate::extractor::bracket_stack::BracketStack; use crate::extractor::machine::{Machine, MachineState}; use crate::extractor::pre_processors::pre_processor::PreProcessor; use crate::extractor::variant_machine::VariantMachine; +use crate::scanner::pre_process_input; +use bstr::ByteVec; #[derive(Debug, Default)] pub struct Haml; @@ -14,8 +16,153 @@ impl PreProcessor for Haml { let mut cursor = cursor::Cursor::new(content); let mut bracket_stack = BracketStack::default(); + // Haml Comments: -# + // https://haml.info/docs/yardoc/file.REFERENCE.html#ruby-evaluation + // + // > The hyphen followed immediately by the pound sign signifies a silent comment. Any text + // > following this isnβt rendered in the resulting document at all. + // + // ```haml + // %p foo + // -# This is a comment + // %p bar + // ``` + // + // > You can also nest text beneath a silent comment. None of this text will be rendered. + // + // ```haml + // %p foo + // -# + // This won't be displayed + // Nor will this + // Nor will this. + // %p bar + // ``` + // + // Ruby Evaluation + // https://haml.info/docs/yardoc/file.REFERENCE.html#ruby-evaluation + // + // When any of the following characters are the first non-whitespace character on the line, + // then the line is treated as Ruby code: + // + // - Inserting Ruby: = + // https://haml.info/docs/yardoc/file.REFERENCE.html#inserting_ruby + // + // ```haml + // %p + // = ['hi', 'there', 'reader!'].join " " + // = "yo" + // ``` + // + // - Running Ruby: - + // https://haml.info/docs/yardoc/file.REFERENCE.html#running-ruby-- + // + // ```haml + // - foo = "hello" + // - foo << " there" + // - foo << " you!" + // %p= foo + // ``` + // + // - Whitespace Preservation: ~ + // https://haml.info/docs/yardoc/file.REFERENCE.html#tilde + // + // > ~ works just like =, except that it runs Haml::Helpers.preserve on its input. + // + // ```haml + // ~ "Foo\n
Bar\nBaz" + // ``` + // + // Important note: + // + // > A line of Ruby code can be stretched over multiple lines as long as each line but the + // > last ends with a comma. + // + // ```haml + // - links = {:home => "/", + // :docs => "/docs", + // :about => "/about"} + // ``` + // + // Ruby Blocks: + // https://haml.info/docs/yardoc/file.REFERENCE.html#ruby-blocks + // + // > Ruby blocks, like XHTML tags, donβt need to be explicitly closed in Haml. Rather, + // > theyβre automatically closed, based on indentation. A block begins whenever the + // > indentation is increased after a Ruby evaluation command. It ends when the indentation + // > decreases (as long as itβs not an else clause or something similar). + // + // ```haml + // - (42...47).each do |i| + // %p= i + // %p See, I can count! + // ``` + // + let mut last_newline_position = 0; + while cursor.pos < len { match cursor.curr { + // Escape the next character + b'\\' => { + cursor.advance_twice(); + continue; + } + + // Track the last newline position + b'\n' => { + last_newline_position = cursor.pos; + cursor.advance(); + continue; + } + + // Skip HAML comments. `-#` + b'-' if cursor.input[last_newline_position..cursor.pos] + .iter() + .all(u8::is_ascii_whitespace) + && matches!(cursor.next, b'#') => + { + // Just consume the comment + let updated_last_newline_position = + self.skip_indented_block(&mut cursor, last_newline_position); + + // Override the last known newline position + last_newline_position = updated_last_newline_position; + } + + // Skip HTML comments. `/` + b'/' if cursor.input[last_newline_position..cursor.pos] + .iter() + .all(u8::is_ascii_whitespace) => + { + // Just consume the comment + let updated_last_newline_position = + self.skip_indented_block(&mut cursor, last_newline_position); + + // Override the last known newline position + last_newline_position = updated_last_newline_position; + } + + // Ruby evaluation + b'-' | b'=' | b'~' + if cursor.input[last_newline_position..cursor.pos] + .iter() + .all(u8::is_ascii_whitespace) => + { + let mut start = cursor.pos; + let end = self.skip_indented_block(&mut cursor, last_newline_position); + + // Increment start with 1 character to skip the `=` or `-` character + start += 1; + + let ruby_code = &cursor.input[start..end]; + + // Override the last known newline position + last_newline_position = end; + + let replaced = pre_process_input(ruby_code, "rb"); + result.replace_range(start..end, replaced); + } + // Only replace `.` with a space if it's not surrounded by numbers. E.g.: // // ```diff @@ -89,6 +236,107 @@ impl PreProcessor for Haml { } } +impl Haml { + fn skip_indented_block( + &self, + cursor: &mut cursor::Cursor, + last_known_newline_position: usize, + ) -> usize { + let len = cursor.input.len(); + + // Special case: if the first character of the block is `=`, then newlines are only allowed + // _if_ the last character of the previous line is a comma `,`. + // + // https://haml.info/docs/yardoc/file.REFERENCE.html#inserting_ruby + // + // > A line of Ruby code can be stretched over multiple lines as long as each line but the + // > last ends with a comma. For example: + // + // ```haml + // = link_to_remote "Add to cart", + // :url => { :action => "add", :id => product.id }, + // :update => { :success => "cart", :failure => "error" } + // ``` + let evaluation_type = cursor.curr; + + let block_indentation_level = cursor + .pos + .saturating_sub(last_known_newline_position) + .saturating_sub(1); /* The newline itself */ + + let mut last_newline_position = last_known_newline_position; + + // Consume until the end of the line first + while cursor.pos < len && cursor.curr != b'\n' { + cursor.advance(); + } + + // Block is already done, aka just a line + if evaluation_type == b'=' && cursor.prev != b',' { + return cursor.pos; + } + + 'outer: while cursor.pos < len { + match cursor.curr { + // Escape the next character + b'\\' => { + cursor.advance_twice(); + continue; + } + + // Track the last newline position + b'\n' => { + last_newline_position = cursor.pos; + + // We are done with this block + if evaluation_type == b'=' && cursor.prev != b',' { + break; + } + + cursor.advance(); + continue; + } + + // Skip whitespace and compute the indentation level + x if x.is_ascii_whitespace() => { + // Find first non-whitespace character + while cursor.pos < len && cursor.curr.is_ascii_whitespace() { + if cursor.curr == b'\n' { + last_newline_position = cursor.pos; + + if evaluation_type == b'=' && cursor.prev != b',' { + // We are done with this block + break 'outer; + } + } + + cursor.advance(); + } + + let indentation = cursor + .pos + .saturating_sub(last_newline_position) + .saturating_sub(1); /* The newline itself */ + if indentation < block_indentation_level { + // We are done with this block + break; + } + } + + // Not whitespace, end of block + _ => break, + }; + + cursor.advance(); + } + + // Move the cursor to the last newline position + cursor.move_to(last_newline_position); + + last_newline_position + } +} + #[cfg(test)] mod tests { use super::Haml; @@ -173,10 +421,18 @@ mod tests { // https://github.com/tailwindlabs/tailwindcss/pull/17051#issuecomment-2711181352 #[test] - fn test_haml_full_file() { - let processed = Haml.process(include_bytes!("./test-fixtures/haml/src-1.haml")); - let actual = std::str::from_utf8(&processed).unwrap(); - let expected = include_str!("./test-fixtures/haml/dst-1.haml"); + fn test_haml_full_file_17051() { + let actual = Haml::extract_annotated(include_bytes!("./test-fixtures/haml/src-17051.haml")); + let expected = include_str!("./test-fixtures/haml/dst-17051.haml"); + + assert_eq!(actual, expected); + } + + // https://github.com/tailwindlabs/tailwindcss/issues/17813 + #[test] + fn test_haml_full_file_17813() { + let actual = Haml::extract_annotated(include_bytes!("./test-fixtures/haml/src-17813.haml")); + let expected = include_str!("./test-fixtures/haml/dst-17813.haml"); assert_eq!(actual, expected); } diff --git a/crates/oxide/src/extractor/pre_processors/pre_processor.rs b/crates/oxide/src/extractor/pre_processors/pre_processor.rs index 7b094129c736..8d42e744a73a 100644 --- a/crates/oxide/src/extractor/pre_processors/pre_processor.rs +++ b/crates/oxide/src/extractor/pre_processors/pre_processor.rs @@ -57,4 +57,104 @@ pub trait PreProcessor: Sized + Default { panic!("Missing some items"); } } + + #[cfg(test)] + fn extract_annotated(input: &[u8]) -> String { + use crate::extractor::{Extracted, Extractor}; + use std::collections::BTreeMap; + use unicode_width::UnicodeWidthStr; + + let processor = Self::default(); + let transformed = processor.process(input); + + let extracted = Extractor::new(&transformed).extract(); + + // Extract only candidate positions + let byte_ranges = extracted + .iter() + .filter_map(|x| match x { + Extracted::Candidate(bytes) => { + let start = bytes.as_ptr() as usize - transformed.as_ptr() as usize; + let end = start + bytes.len(); + Some((start, end)) + } + _ => None, + }) + .collect::
Number <% i %>
- <% else %> -Numero <% i %>
- <% end %> - <% end %> - - doc_code css: "grow mt-8 xl:mt-0", pre_css: "xl:h-full", code_css: "xl:pl-0 xl:pr-2 xl:h-full xl:rounded-l-none" do - :escaped - - HAML - - - 5 times do |i| - - if i even? - %p odd Number \ i - - else - %p even Numero \ i - - - %h3 mt-32 text-4xl font-bold - Build Your OWN Components - - %p mt-2 text-xl - Can't find exactly what you need? No problem! Build your own components - with ease using our simple, flexible, and powerful DSL - - mt-8 xl:flex xl:space-x-8 - %div - doc_code language: "ruby" do - :escaped - app/components/application_component rb - class ApplicationComponent < LocoMotion::BaseComponent - Add your custom / shared component logic here! - end - - doc_code language: "haml", css: "mt-8" do - :escaped - - app/components/character_component html haml - part :component do - part :head - part :body do - content - part :legs - - %div mt-8 xl:mt-0 - doc_code language: "ruby" do - :escaped - app/components/character_component rb - class CharacterComponent < ApplicationComponent - define_parts :head, :body, :legs - - def before_render - set_tag_name :head, :h1 - add_css :head, "text-3xl font-bold" - - set_tag_name :body, :p - add_stimulus_controller :body, "character-body" - add_css :body, "text-lg" - - set_tag_name :legs, :footer - add_css :legs, "text-sm" - end - end - - %h3 mt-24 text-center text-4xl font-bold italic - More Coming Soon! - %p mt-2 text-xl text-center - Keen an eye out as we'll be adding more components, guides, and - %br max-sm:hidden - suggested gems for you to build amazing Rails apps! - mt-4 text-center - daisy_button "π Get Started", css: "btn-primary text-xl", - right_icon: "arrow-right", target: "_blank", - class: "px-2.5" - href: "https://github com/profoundry-us/loco_motion locomotion-components" diff --git a/crates/oxide/src/extractor/pre_processors/test-fixtures/haml/dst-17051.haml b/crates/oxide/src/extractor/pre_processors/test-fixtures/haml/dst-17051.haml new file mode 100644 index 000000000000..b233f3b8b2a4 --- /dev/null +++ b/crates/oxide/src/extractor/pre_processors/test-fixtures/haml/dst-17051.haml @@ -0,0 +1,264 @@ +/ https://github.com/tailwindlabs/tailwindcss/pull/17051#issuecomment-2711181352 +- star_styles = "size-[800px] mask mask-star bg-gradient-to-r from-secondary via-cyan-400 to-lime-400" + ^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^ +- crazy_text_styles = "italic font-black bg-gradient-to-r via-60% to-90% from-orange-500 via-secondary to-primary text-transparent bg-clip-text inline-block py-2" + ^^^^^^^^^^^^^^^^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^ ^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^ + +.relative + ^^^^^^^^ + - # Blurred background star + ^^^^^^^^^^ ^^^^ + .absolute.left-0.z-0{ class: "-top-[400px] -right-[400px]" } + ^^^^^^^^ ^^^^^^ ^^^ ^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^^ + .flex.justify-end.blur-3xl + ^^^^ ^^^^^^^^^^^ ^^^^^^^^ + %div{ class: star_styles } + ^^^^^ ^^^^^^^^^^^ + + .relative.z-10 + ^^^^^^^^ ^^^^ + %h1.mt-8.text-center.text-5xl.font-black.tracking-wide.drop-shadow-lg + ^^^^ ^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ + %div + Components, Guides, and Paradigms + ^^^ + .md:inline-flex.md:items-center.justify-center + ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ + %span.-mr-2 + ^^^^^ + for + ^^^ + %span.relative.-rotate-3.hover:-rotate-6.hover:scale-125.transition-transform + ^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ + %span{ class: crazy_text_styles + " absolute blur-xl" } + ^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^ + CRAZY + %span{ class: crazy_text_styles + " drop-shadow-[0_0_1px_#fff]" } + ^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + CRAZY + %span.-ml-3.md:-ml-1 + ^^^^^ ^^^^^^^^ + \-fast Development + %div + in Ruby on Rails! + ^^ ^^ + + .mt-16.text-center + ^^^^^ ^^^^^^^^^^^ + = daisy_button "π Get Started", css: "btn-primary text-xl", + ^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^ ^^^^^^^ + right_icon: "arrow-right", target: "_blank", + ^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^ + href: "https://github.com/profoundry-us/loco_motion#locomotion-components" + ^^^^ ^^^^^^^^^^^^^^^^^^^^^ + + .mt-32.lg:flex.lg:space-x-8 + ^^^^^ ^^^^^^^ ^^^^^^^^^^^^ + %div{ class: "md:basis-2/5" } + ^^^^^ ^^^^^^^^^^^^ + %h3.text-4xl.font-bold + ^^^^^^^^ ^^^^^^^^^ + Easy, Flexible Components + %p.mt-2.text-xl + ^^^^ ^^^^^^^ + Powered by the fabulous + ^^ ^^^ ^^^^^^^^ + = succeed ',' do + ^^^^^^^ ^^ + = daisy_link("ViewComponent", "https://viewcomponent.org", target: "_blank") + ^^^ ^^^^^^ + = succeed ', and' do + ^^^^^^^ ^^^ ^^ + = daisy_link "DaisyUI", "https://daisyui.com/", target: "_blank" + ^^^^^^^^^^ ^^^^^^ + = daisy_link "TailwindCSS", "https://tailwindcss.com/", target: "_blank" + ^^^^^^^^^^ ^^^^^^ + libraries, our components are designed to be fast, flexible, and easy to + ^^^ ^^^^^^^^^^ ^^^ ^^^^^^^^ ^^ ^^ ^^^ ^^^^ ^^ + use directy in Ruby on Rails! + ^^^ ^^^^^^^ ^^ ^^ + + = doc_example(css: "mt-8 lg:mt-0 lg:basis-3/5 h-44") do + ^^^ ^^^^ ^^^^^^^ ^^^^^^^^^^^^ ^^^^ ^^ + .flex.flex-col.sm:flex-row.items-center.gap-4 + ^^^^ ^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^ + = daisy_button "Accent Button", css: "btn-accent" + ^^^^^^^^^^^^ ^^^ ^^^^^^^^^^ + = daisy_tip("Click to Swap") do + ^^ ^^ + = daisy_swap off: "π", on: "π", css: "swap-rotate text-4xl" + ^^^^^^^^^^ ^^^ ^^ ^^^ ^^^^^^^^^^^ ^^^^^^^^ + = daisy_badge "Large Badge", css: "badge-secondary badge-lg" + ^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^ + + + .mt-32.xl:flex.xl:flex-row-reverse.xl:items-center + ^^^^^ ^^^^^^^ ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ + .text-xl.xl:ml-8 + ^^^^^^^ ^^^^^^^ + %h3.text-4xl.font-bold + ^^^^^^^^ ^^^^^^^^^ + Simple, Concise Views + + %p.mt-2 + ^^^^ + Utlize + = daisy_link("HAML", "https://haml.info/", target: "_blank") + ^^^^^^ + so your views are simple, concise, and easy to understand. + ^^ ^^^^ ^^^^^ ^^^ ^^^ ^^^^ ^^ ^^^^^^^^^^ + + %p.mt-2 + ^^^^ + No more messy ERB files with all of their closing tags and Ruby wrappers. + ^^^^ ^^^^^ ^^^^^ ^^^^ ^^^ ^^ ^^^^^ ^^^^^^^ ^^^^ ^^^ ^^^^^^^^ + HAML feels more natural to write and reduces file sizes, making your + ^^^^^ ^^^^ ^^^^^^^ ^^ ^^^^^ ^^^ ^^^^^^^ ^^^^ ^^^^^^ ^^^^ + views easier to read and maintain. + ^^^^^ ^^^^^^ ^^ ^^^^ ^^^ ^^^^^^^^ + + %p.mt-2 + ^^^^ + :markdown + **PLUS!** You can utlize filters like _Markdown_, CoffeeScript, + ^^^ ^^^^^^ ^^^^^^^ ^^^^ + Textile, and many more! + ^^^ ^^^^ ^^^^^ + + .mt-8.xl:mt-0 + ^^^^ ^^^^^^^ + .flex.flex-col.xl:flex.xl:flex-row.w-full + ^^^^ ^^^^^^^^ ^^^^^^^ ^^^^^^^^^^^ ^^^^^^ + = doc_code(css: "grow", code_css: "xl:pl-2 xl:pr-0 xl:rounded-r-none", language: "erb") do + ^^^ ^^^^ ^^^^^^^^ ^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^^ ^^ + :escaped + <% # Ruby %> + + <% 5.times do |i| %> + ^^^^^ ^^ + <% if i.even? %> + ^^ ^ +Number <%= i %>
+ ^^^^^ ^^^ ^ + <% else %> + ^^^^ +Numero <%= i %>
+ ^^^^^ ^^^^ ^ + <% end %> + ^^^ + <% end %> + ^^^ + + = doc_code(css: "grow mt-8 xl:mt-0", pre_css: "xl:h-full", code_css: "xl:pl-0 xl:pr-2 xl:h-full xl:rounded-l-none") do + ^^^ ^^^^ ^^^^ ^^^^^^^ ^^^^^^^ ^^^^^^^^^ ^^^^^^^^ ^^^^^^^ ^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^ ^^ + :escaped + - # HAML + + - 5.times do |i| + ^^^^^ ^^ + - if i.even? + ^^ + %p.odd Number \#{i} + ^^^ ^ + - else + ^^^^ + %p.even Numero \#{i} + ^^^^ ^ + + + %h3.mt-32.text-4xl.font-bold + ^^^^^ ^^^^^^^^ ^^^^^^^^^ + Build Your OWN Components + + %p.mt-2.text-xl + ^^^^ ^^^^^^^ + Can't find exactly what you need? No problem! Build your own components + ^ ^^^^ ^^^^^^^ ^^^^ ^^^ ^^^^^^^^ ^^^^ ^^^ ^^^^^^^^^^ + with ease using our simple, flexible, and powerful DSL. + ^^^^ ^^^^ ^^^^^ ^^^ ^^^ ^^^^^^^^ + + .mt-8.xl:flex.xl:space-x-8 + ^^^^ ^^^^^^^ ^^^^^^^^^^^^ + %div + = doc_code(language: "ruby") do + ^^^^^^^^ ^^^^ ^^ + :escaped + # app/components/application_component.rb + ^^ + class ApplicationComponent < LocoMotion::BaseComponent + ^^^^^ + # Add your custom / shared component logic here! + ^^^^ ^^^^^^ ^^^^^^ ^^^^^^^^^ ^^^^^ ^^^^^ + end + ^^^ + + = doc_code(language: "haml", css: "mt-8") do + ^^^^^^^^ ^^^^ ^^^ ^^^^ ^^ + :escaped + - # app/components/character_component.html.haml + ^^^^ + = part(:component) do + ^^ + = part(:head) + = part(:body) do + ^^ + = content + ^^^^^^^ + = part(:legs) + + %div.mt-8.xl:mt-0 + ^^^^ ^^^^^^^ + = doc_code(language: "ruby") do + ^^^^^^^^ ^^^^ ^^ + :escaped + # app/components/character_component.rb + ^^ + class CharacterComponent < ApplicationComponent + ^^^^^ + define_parts :head, :body, :legs + ^^^^^^^^^^^^ + + def before_render + ^^^ ^^^^^^^^^^^^^ + set_tag_name(:head, :h1) + ^^^^^^^^^^^^ + add_css(:head, "text-3xl font-bold") + ^^^^^^^ ^^^^^^^^ ^^^^^^^^^ + + set_tag_name(:body, :p) + ^^^^^^^^^^^^ + add_stimulus_controller(:body, "character-body") + ^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ + add_css(:body, "text-lg") + ^^^^^^^ ^^^^^^^ + + set_tag_name(:legs, :footer) + ^^^^^^^^^^^^ + add_css(:legs, "text-sm") + ^^^^^^^ ^^^^^^^ + end + ^^^ + end + ^^^ + + %h3.mt-24.text-center.text-4xl.font-bold.italic + ^^^^^ ^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^ ^^^^^^ + More Coming Soon! + %p.mt-2.text-xl.text-center + ^^^^ ^^^^^^^ ^^^^^^^^^^^ + Keen an eye out as we'll be adding more components, guides, and + ^^ ^^^ ^^^ ^^ ^^ ^^ ^^ ^^^^^^ ^^^^ ^^^ + %br.max-sm:hidden + ^^^^^^^^^^^^^ + suggested gems for you to build amazing Rails apps! + ^^^^^^^^^ ^^^^ ^^^ ^^^ ^^ ^^^^^ ^^^^^^^ ^^^^^ + .mt-4.text-center + ^^^^ ^^^^^^^^^^^ + = daisy_button "π Get Started", css: "btn-primary text-xl", + ^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^ ^^^^^^^ + right_icon: "arrow-right", target: "_blank", + ^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^ + class: "px-2.5" + ^^^^^ ^^^^^^ + href: "https://github.com/profoundry-us/loco_motion#locomotion-components" + ^^^^ ^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/oxide/src/extractor/pre_processors/test-fixtures/haml/dst-17813.haml b/crates/oxide/src/extractor/pre_processors/test-fixtures/haml/dst-17813.haml new file mode 100644 index 000000000000..18f0fdc4b20a --- /dev/null +++ b/crates/oxide/src/extractor/pre_processors/test-fixtures/haml/dst-17813.haml @@ -0,0 +1,30 @@ +:ruby + - devices_classes = 'size-5 mr-2px' + ^^^^^^^^^^^^^^^ ^^^^^^ ^^^^^^ + - icon_classes = 'w-[12px] h-[12px]' + ^^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^ +!!! +%html{ lang: 'en' } + ^^^^ ^^ + %head + %title Tailwind v4.1.4 + HAML bug + ^^^^^^ ^^^ + + -# This is a comment + ^^ ^ ^^^^^^^ + + A multi-line comment + ^^^^^^^^^^ ^^^^^^^ + With more indentation + ^^^^ ^^^^^^^^^^^ + + Which can dedent again just fine + ^^^ ^^^^^^ ^^^^^ ^^^^ ^^^^ + + %body + - icon_classes = 'self-center w-[16px] h-[16px]' + ^^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^ + .flex{ class: icon_classes } + ^^^^ ^^^^^ ^^^^^^^^^^^^ + .flex{ class: devices_classes } + ^^^^ ^^^^^ ^^^^^^^^^^^^^^^ diff --git a/crates/oxide/src/extractor/pre_processors/test-fixtures/haml/src-1.haml b/crates/oxide/src/extractor/pre_processors/test-fixtures/haml/src-17051.haml similarity index 100% rename from crates/oxide/src/extractor/pre_processors/test-fixtures/haml/src-1.haml rename to crates/oxide/src/extractor/pre_processors/test-fixtures/haml/src-17051.haml diff --git a/crates/oxide/src/extractor/pre_processors/test-fixtures/haml/src-17813.haml b/crates/oxide/src/extractor/pre_processors/test-fixtures/haml/src-17813.haml new file mode 100644 index 000000000000..5fdaefdb03c3 --- /dev/null +++ b/crates/oxide/src/extractor/pre_processors/test-fixtures/haml/src-17813.haml @@ -0,0 +1,19 @@ +:ruby + - devices_classes = 'size-5 mr-2px' + - icon_classes = 'w-[12px] h-[12px]' +!!! +%html{ lang: 'en' } + %head + %title Tailwind v4.1.4 + HAML bug + + -# This is a comment + + A multi-line comment + With more indentation + + Which can dedent again just fine + + %body + - icon_classes = 'self-center w-[16px] h-[16px]' + .flex{ class: icon_classes } + .flex{ class: devices_classes }