Skip to content

Commit 4455051

Browse files
Skip comments in Ruby files when checking for class names (#19243)
Fixes #19239
1 parent 5bc90dd commit 4455051

File tree

4 files changed

+137
-5
lines changed

4 files changed

+137
-5
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- Ensure validation of `source(…)` happens relative to the file it is in ([#19274](https://github.com/tailwindlabs/tailwindcss/pull/19274))
1313
- Include filename and line numbers in CSS parse errors ([#19282](https://github.com/tailwindlabs/tailwindcss/pull/19282))
14+
- Skip comments in Ruby files when checking for class names ([#19243](https://github.com/tailwindlabs/tailwindcss/pull/19243))
15+
- Skip over arbitrary property utilities with a top-level `!` in the value ([#19243](https://github.com/tailwindlabs/tailwindcss/pull/19243))
1416

1517
### Added
1618

crates/oxide/src/extractor/arbitrary_property_machine.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,12 @@ impl Machine for ArbitraryPropertyMachine<ParsingValueState> {
231231
return self.restart()
232232
}
233233

234+
// An `!` at the top-level is invalid. We don't allow things to end with
235+
// `!important` either as we have dedicated syntax for this.
236+
Class::Exclamation if self.bracket_stack.is_empty() => {
237+
return self.restart();
238+
}
239+
234240
// Everything else is valid
235241
_ => cursor.advance(),
236242
};
@@ -293,6 +299,9 @@ enum Class {
293299
#[bytes(b'/')]
294300
Slash,
295301

302+
#[bytes(b'!')]
303+
Exclamation,
304+
296305
#[bytes(b' ', b'\t', b'\n', b'\r', b'\x0C')]
297306
Whitespace,
298307

@@ -369,6 +378,9 @@ mod tests {
369378
"[background:url(https://example.com?q={[{[([{[[2]]}])]}]})]",
370379
vec!["[background:url(https://example.com?q={[{[([{[[2]]}])]}]})]"],
371380
),
381+
// A property containing `!` at the top-level is invalid
382+
("[color:red!]", vec![]),
383+
("[color:red!important]", vec![]),
372384
] {
373385
for wrapper in [
374386
// No wrapper

crates/oxide/src/extractor/pre_processors/ruby.rs

Lines changed: 123 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,74 @@ impl PreProcessor for Ruby {
7777

7878
// Ruby extraction
7979
while cursor.pos < len {
80-
// Looking for `%w` or `%W`
81-
if cursor.curr != b'%' && !matches!(cursor.next, b'w' | b'W') {
80+
match cursor.curr {
81+
b'"' => {
82+
cursor.advance();
83+
84+
while cursor.pos < len {
85+
match cursor.curr {
86+
// Escaped character, skip ahead to the next character
87+
b'\\' => cursor.advance_twice(),
88+
89+
// End of the string
90+
b'"' => break,
91+
92+
// Everything else is valid
93+
_ => cursor.advance(),
94+
};
95+
}
96+
97+
cursor.advance();
98+
continue;
99+
}
100+
101+
b'\'' => {
102+
cursor.advance();
103+
104+
while cursor.pos < len {
105+
match cursor.curr {
106+
// Escaped character, skip ahead to the next character
107+
b'\\' => cursor.advance_twice(),
108+
109+
// End of the string
110+
b'\'' => break,
111+
112+
// Everything else is valid
113+
_ => cursor.advance(),
114+
};
115+
}
116+
117+
cursor.advance();
118+
continue;
119+
}
120+
121+
// Replace comments in Ruby files
122+
b'#' => {
123+
result[cursor.pos] = b' ';
124+
cursor.advance();
125+
126+
while cursor.pos < len {
127+
match cursor.curr {
128+
// End of the comment
129+
b'\n' => break,
130+
131+
// Everything else is part of the comment and replaced
132+
_ => {
133+
result[cursor.pos] = b' ';
134+
cursor.advance();
135+
}
136+
};
137+
}
138+
139+
cursor.advance();
140+
continue;
141+
}
142+
143+
_ => {}
144+
}
145+
146+
// Looking for `%w`, `%W`, or `%p`
147+
if cursor.curr != b'%' || !matches!(cursor.next, b'w' | b'W' | b'p') {
82148
cursor.advance();
83149
continue;
84150
}
@@ -90,6 +156,8 @@ impl PreProcessor for Ruby {
90156
b'[' => b']',
91157
b'(' => b')',
92158
b'{' => b'}',
159+
b'#' => b'#',
160+
b' ' => b'\n',
93161
_ => {
94162
cursor.advance();
95163
continue;
@@ -131,7 +199,10 @@ impl PreProcessor for Ruby {
131199

132200
// End of the pattern, replace the boundary character with a space
133201
_ if cursor.curr == boundary => {
134-
result[cursor.pos] = b' ';
202+
if boundary != b'\n' {
203+
result[cursor.pos] = b' ';
204+
}
205+
135206
break;
136207
}
137208

@@ -173,12 +244,51 @@ mod tests {
173244
"%w(flex data-[state=pending]:bg-(--my-color) flex-col)",
174245
"%w flex data-[state=pending]:bg-(--my-color) flex-col ",
175246
),
247+
248+
// %w …\n
249+
("%w flex px-2.5\n", "%w flex px-2.5\n"),
250+
176251
// Use backslash to embed spaces in the strings.
177252
(r#"%w[foo\ bar baz\ bat]"#, r#"%w foo bar baz bat "#),
178253
(r#"%W[foo\ bar baz\ bat]"#, r#"%W foo bar baz bat "#),
254+
179255
// The nested delimiters evaluated to a flat array of strings
180256
// (not nested array).
181257
(r#"%w[foo[bar baz]qux]"#, r#"%w foo[bar baz]qux "#),
258+
259+
(
260+
"# test\n# test\n# {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!]\n%w[flex px-2.5]",
261+
" \n \n \n%w flex px-2.5 "
262+
),
263+
264+
(r#""foo # bar""#, r#""foo # bar""#),
265+
(r#"'foo # bar'"#, r#"'foo # bar'"#),
266+
(
267+
r#"def call = tag.span "Foo", class: %w[rounded-full h-0.75 w-0.75]"#,
268+
r#"def call = tag.span "Foo", class: %w rounded-full h-0.75 w-0.75 "#
269+
),
270+
271+
(r#"%w[foo ' bar]"#, r#"%w foo ' bar "#),
272+
(r#"%w[foo " bar]"#, r#"%w foo " bar "#),
273+
(r#"%W[foo ' bar]"#, r#"%W foo ' bar "#),
274+
(r#"%W[foo " bar]"#, r#"%W foo " bar "#),
275+
276+
(r#"%p foo ' bar "#, r#"%p foo ' bar "#),
277+
(r#"%p foo " bar "#, r#"%p foo " bar "#),
278+
279+
(
280+
"%p has a ' quote\n# this should be removed\n%p has a ' quote",
281+
"%p has a ' quote\n \n%p has a ' quote"
282+
),
283+
(
284+
"%p has a \" quote\n# this should be removed\n%p has a \" quote",
285+
"%p has a \" quote\n \n%p has a \" quote"
286+
),
287+
288+
(
289+
"%w#this text is kept# # this text is not",
290+
"%w this text is kept ",
291+
),
182292
] {
183293
Ruby::test(input, expected);
184294
}
@@ -211,6 +321,16 @@ mod tests {
211321
"%w(flex data-[state=pending]:bg-(--my-color) flex-col)",
212322
vec!["flex", "data-[state=pending]:bg-(--my-color)", "flex-col"],
213323
),
324+
325+
(
326+
"# test\n# test\n# {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!]\n%w[flex px-2.5]",
327+
vec!["flex", "px-2.5"],
328+
),
329+
330+
(r#""foo # bar""#, vec!["foo", "bar"]),
331+
(r#"'foo # bar'"#, vec!["foo", "bar"]),
332+
333+
(r#"%w[foo ' bar]"#, vec!["foo", "bar"]),
214334
] {
215335
Ruby::test_extract_contains(input, expected);
216336
}

crates/oxide/src/extractor/pre_processors/test-fixtures/haml/dst-17051.haml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
.relative
88
^^^^^^^^
99
- # Blurred background star
10-
^^^^^^^^^^ ^^^^
1110
.absolute.left-0.z-0{ class: "-top-[400px] -right-[400px]" }
1211
^^^^^^^^ ^^^^^^ ^^^ ^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^^
1312
.flex.justify-end.blur-3xl
@@ -196,7 +195,6 @@
196195
^^^^^^^^ ^^^^ ^^^ ^^^^ ^^
197196
:escaped
198197
- # app/components/character_component.html.haml
199-
^^^^
200198
= part(:component) do
201199
^^
202200
= part(:head)

0 commit comments

Comments
 (0)