Ensure extracting candidates from JS embedded in a PHP string works as expected (#17031)

This PR fixes an issue where candidates are not properly extractor when
they end in `\`. This can happen if you embed a programming language
like JS inside another language like PHP where you need to escape some
strings.

Here is an example of Livewire flux:
```blade
@php
if ($sidebarIsStashable) {
    $attributes = $attributes->merge([
        'x-init' => '$el.classList.add(\'-translate-x-full\'); $el.classList.add(\'transition-transform\')',
        //                                                ^                                            ^
    ]);
}
@endphp

<div x-data {{ $attributes->class('border-r w-64 p-4 min-h-dvh max-h-dvh top-0 fixed left-0') }}>
    {{ $slot }}
</div>
```
Where the `\'` is causing some issues.

Another solution might be to add a custom pre processor for blade files
where we drop the escaped characters, but that felt overkill for now
because some escapes are still valid.

Fixes: #17023

# Test plan

1. Added a test to cover this case.
2. Existing tests still pass
This commit is contained in:
Robin Malfait 2025-03-07 12:19:01 +01:00 committed by GitHub
parent 225f3233b6
commit f498e4a97c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 46 additions and 0 deletions

View File

@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Ensure strings in Pug and Slim templates are handled correctly ([#17000](https://github.com/tailwindlabs/tailwindcss/pull/17000))
- Ensure `}` and `{` are valid boundary characters when extracting candidates ([#17001](https://github.com/tailwindlabs/tailwindcss/pull/17001))
- Add `razor`/`cshtml` pre processing ([#17027](https://github.com/tailwindlabs/tailwindcss/pull/17027))
- Ensure extracting candidates from JS embedded in a PHP string works as expected ([#17031](https://github.com/tailwindlabs/tailwindcss/pull/17031))
## [4.0.11] - 2025-03-06

View File

@ -97,6 +97,18 @@ enum Class {
// ^
// ```
#[bytes(b'=')]
// Escaped character when embedding one language in another via strings, e.g.:
//
// ```
// $attributes->merge([
// 'x-init' => '$el.classList.add(\'-translate-x-full\'); $el.classList.add(\'transition-transform\')',
// ^ ^
// ]);
// ```
//
// In this case there is some JavaScript embedded in an string in PHP and some of the quotes
// need to be escaped.
#[bytes(b'\\')]
After,
#[fallback]

View File

@ -888,6 +888,39 @@ mod tests {
);
}
#[test]
fn test_js_embedded_in_php_syntax() {
// Escaped single quotes
let input = r#"
@php
if ($sidebarIsStashable) {
$attributes = $attributes->merge([
'x-init' => '$el.classList.add(\'-translate-x-full\'); $el.classList.add(\'transition-transform\')',
]);
}
@endphp
"#;
assert_extract_candidates_contains(
input,
vec!["-translate-x-full", "transition-transform"],
);
// Double quotes
let input = r#"
@php
if ($sidebarIsStashable) {
$attributes = $attributes->merge([
'x-init' => "\$el.classList.add('-translate-x-full'); \$el.classList.add('transition-transform')",
]);
}
@endphp
"#;
assert_extract_candidates_contains(
input,
vec!["-translate-x-full", "transition-transform"],
);
}
// https://github.com/tailwindlabs/tailwindcss/issues/16978
#[test]
fn test_classes_containing_number_followed_by_dash_or_underscore() {