diff --git a/crates/oxide/src/extractor/arbitrary_property_machine.rs b/crates/oxide/src/extractor/arbitrary_property_machine.rs index 8ed3f1763091..d9baab00275e 100644 --- a/crates/oxide/src/extractor/arbitrary_property_machine.rs +++ b/crates/oxide/src/extractor/arbitrary_property_machine.rs @@ -231,6 +231,18 @@ impl Machine for ArbitraryPropertyMachine { return self.restart() } + // An `!` at the top-level must be followed by "important" *and* be at the end + // otherwise its invalid + Class::Exclamation if self.bracket_stack.is_empty() => { + if cursor.input[cursor.pos..].starts_with(b"!important]") { + cursor.advance_by(10); + + return self.done(self.start_pos, cursor); + } + + return self.restart(); + } + // Everything else is valid _ => cursor.advance(), }; @@ -293,6 +305,9 @@ enum Class { #[bytes(b'/')] Slash, + #[bytes(b'!')] + Exclamation, + #[bytes(b' ', b'\t', b'\n', b'\r', b'\x0C')] Whitespace, @@ -369,6 +384,10 @@ mod tests { "[background:url(https://example.com?q={[{[([{[[2]]}])]}]})]", vec!["[background:url(https://example.com?q={[{[([{[[2]]}])]}]})]"], ), + // A property containing `!` at the top-level is invalid + ("[color:red!]", vec![]), + // Unless its part of `!important at the end + ("[color:red!important]", vec!["[color:red!important]"]), ] { for wrapper in [ // No wrapper diff --git a/crates/oxide/src/extractor/pre_processors/ruby.rs b/crates/oxide/src/extractor/pre_processors/ruby.rs index 6bb1d49238a3..9649c8c55287 100644 --- a/crates/oxide/src/extractor/pre_processors/ruby.rs +++ b/crates/oxide/src/extractor/pre_processors/ruby.rs @@ -77,6 +77,72 @@ impl PreProcessor for Ruby { // Ruby extraction while cursor.pos < len { + match cursor.curr { + b'"' => { + cursor.advance(); + + while cursor.pos < len { + match cursor.curr { + // Escaped character, skip ahead to the next character + b'\\' => cursor.advance_twice(), + + // End of the string + b'"' => break, + + // Everything else is valid + _ => cursor.advance(), + }; + } + + cursor.advance(); + continue; + } + + b'\'' => { + cursor.advance(); + + while cursor.pos < len { + match cursor.curr { + // Escaped character, skip ahead to the next character + b'\\' => cursor.advance_twice(), + + // End of the string + b'\'' => break, + + // Everything else is valid + _ => cursor.advance(), + }; + } + + cursor.advance(); + continue; + } + + // Replace comments in Ruby files + b'#' => { + result[cursor.pos] = b' '; + cursor.advance(); + + while cursor.pos < len { + match cursor.curr { + // End of the comment + b'\n' => break, + + // Everything else is part of the comment and replaced + _ => { + result[cursor.pos] = b' '; + cursor.advance(); + } + }; + } + + cursor.advance(); + continue; + } + + _ => {} + } + // Looking for `%w` or `%W` if cursor.curr != b'%' && !matches!(cursor.next, b'w' | b'W') { cursor.advance(); @@ -179,6 +245,18 @@ mod tests { // The nested delimiters evaluated to a flat array of strings // (not nested array). (r#"%w[foo[bar baz]qux]"#, r#"%w foo[bar baz]qux "#), + + ( + "# test\n# test\n# {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!]\n%w[flex px-2.5]", + " \n \n \n%w flex px-2.5 " + ), + + (r#""foo # bar""#, r#""foo # bar""#), + (r#"'foo # bar'"#, r#"'foo # bar'"#), + ( + r#"def call = tag.span "Foo", class: %w[rounded-full h-0.75 w-0.75]"#, + r#"def call = tag.span "Foo", class: %w rounded-full h-0.75 w-0.75 "# + ), ] { Ruby::test(input, expected); } @@ -211,6 +289,14 @@ mod tests { "%w(flex data-[state=pending]:bg-(--my-color) flex-col)", vec!["flex", "data-[state=pending]:bg-(--my-color)", "flex-col"], ), + + ( + "# test\n# test\n# {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!]\n%w[flex px-2.5]", + vec!["flex", "px-2.5"], + ), + + (r#""foo # bar""#, vec!["foo", "bar"]), + (r#"'foo # bar'"#, vec!["foo", "bar"]), ] { Ruby::test_extract_contains(input, expected); } 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 index b233f3b8b2a4..4a3543379420 100644 --- 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 @@ -7,7 +7,6 @@ .relative ^^^^^^^^ - # Blurred background star - ^^^^^^^^^^ ^^^^ .absolute.left-0.z-0{ class: "-top-[400px] -right-[400px]" } ^^^^^^^^ ^^^^^^ ^^^ ^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^^ .flex.justify-end.blur-3xl @@ -196,7 +195,6 @@ ^^^^^^^^ ^^^^ ^^^ ^^^^ ^^ :escaped - # app/components/character_component.html.haml - ^^^^ = part(:component) do ^^ = part(:head)