Skip to content

Commit 4d9d45a

Browse files
committed
Handle parser corpus edge cases
Accept comments between JSX attributes so valid TSX files do not fall back to unchanged output. Keep dynamic quoted Svelte attributes in source-preservation checks without treating them as static scrambling candidates.
1 parent 26b14e5 commit 4d9d45a

3 files changed

Lines changed: 56 additions & 4 deletions

File tree

rustywind-core/src/jsx_parser.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,15 @@ impl<'a> JsxParser<'a> {
203203
return None;
204204
}
205205

206+
if remaining(input).starts_with("//") {
207+
consume_line_comment(input, "//")?;
208+
continue;
209+
}
210+
if remaining(input).starts_with("/*") {
211+
consume_block_comment(input)?;
212+
continue;
213+
}
214+
206215
if remaining(input).starts_with('{') {
207216
consume_character(input, '{')?;
208217
self.parse_javascript(input, Some('}'))?;
@@ -436,6 +445,22 @@ mod tests {
436445
assert_eq!(values(source), Some(vec!["p-4 flex"]));
437446
}
438447

448+
#[test]
449+
fn comments_can_separate_attributes() {
450+
let source = r#"
451+
const view = (
452+
<Button
453+
onClick={() => submit()}
454+
// keep this prop documented
455+
className="p-4 flex"
456+
/* and this one */ disabled
457+
/>
458+
);
459+
"#;
460+
461+
assert_eq!(values(source), Some(vec!["p-4 flex"]));
462+
}
463+
439464
#[test]
440465
fn generic_and_relational_syntax_is_not_jsx() {
441466
let source = r#"

tests/tailwind-compare/lib.mjs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,31 @@ function findReactAttributes(source, kind) {
6565
return attributes;
6666
}
6767

68-
function findSvelteAttributes(source) {
68+
function findStaticSvelteAttributes(source) {
6969
const ast = parseSvelte(source, { modern: true });
7070
const attributes = [];
7171
walk(ast.fragment, (node) => {
7272
if (
7373
node.type !== "Attribute" ||
74-
!isClassAttributeName(node.name)
74+
!isClassAttributeName(node.name) ||
75+
node.value?.length !== 1 ||
76+
node.value[0].type !== "Text"
7577
) {
7678
return;
7779
}
80+
const { start, end } = node.value[0];
81+
const quote = source[start - 1];
82+
if ((quote !== '"' && quote !== "'") || source[end] !== quote) return;
83+
attributes.push({ end, start });
84+
});
85+
return attributes;
86+
}
87+
88+
function findQuotedSvelteAttributes(source) {
89+
const ast = parseSvelte(source, { modern: true });
90+
const attributes = [];
91+
walk(ast.fragment, (node) => {
92+
if (node.type !== "Attribute" || !isClassAttributeName(node.name)) return;
7893
const valueStart = source.indexOf("=", node.name_loc.end.character) + 1;
7994
if (valueStart === 0 || valueStart >= node.end) return;
8095
const quoteStart = source
@@ -166,7 +181,7 @@ function findStaticAttributes(source, kind) {
166181
if (kind === "jsx" || kind === "tsx") {
167182
attributes = findReactAttributes(source, kind);
168183
} else if (kind === "svelte") {
169-
attributes = findSvelteAttributes(source);
184+
attributes = findStaticSvelteAttributes(source);
170185
} else if (kind === "astro") {
171186
attributes = findAstroAttributes(source);
172187
} else {
@@ -194,7 +209,7 @@ function findQuotedAttributes(source, kind) {
194209
if (kind === "jsx" || kind === "tsx") {
195210
attributes = findReactAttributes(source, kind);
196211
} else if (kind === "svelte") {
197-
attributes = findSvelteAttributes(source);
212+
attributes = findQuotedSvelteAttributes(source);
198213
} else if (kind === "astro") {
199214
attributes = findAstroAttributes(source);
200215
} else {

tests/tailwind-compare/test/lib.test.mjs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,18 @@ test("permits changes only inside real quoted class attributes", () => {
8888
);
8989
});
9090

91+
test("preserves dynamic quoted Svelte attributes without scrambling them", () => {
92+
const source = '<div class="p-4 {active ? \'flex\' : \'grid\'}"></div>';
93+
const changed = source.replace("p-4", "m-4");
94+
95+
assert.deepEqual(extractAttributes(source, "svelte"), []);
96+
assert.equal(scrambleAttributes(source, "svelte"), source);
97+
assert.equal(
98+
preservesSourceOutsideAttributes(source, changed, "svelte"),
99+
true,
100+
);
101+
});
102+
91103
test("ignores markup text in source comments and string literals", () => {
92104
const fixtures = [
93105
{

0 commit comments

Comments
 (0)