Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Emotion SWC transform escape sequence bugs (#40385, #38301) #40534

Closed
wants to merge 10 commits into from
Closed
20 changes: 20 additions & 0 deletions errors/invalid-escape-sequence-in-emotion-tagged-template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Invalid escape sequence in Emotion tagged template

#### Why This Error Occurred

<!-- Explain why the error occurred. Ensure the description makes it clear why the warning/error exists -->

One or more escape sequence in an Emotion tagged template was incorrect. This prevented correct parsing before transformations were applied.

#### Possible Ways to Fix It

<!-- Explain how to fix the warning/error, potentially by providing alternative approaches. Ensure this section is actionable by users -->

Look for `\\` in the string to find the broken escape sequence(s). For example, `\\u` must be followed by four hex digits.

### Useful Links

<!-- Add links to relevant documentation -->

- [Next.js compiler Emotion configuration](https://nextjs.org/docs/advanced-features/compiler#emotion)
- [ECMAScript 2023 syntax for string literals](https://tc39.es/ecma262/multipage/ecmascript-language-lexical-grammar.html#sec-literals-string-literals)
4 changes: 4 additions & 0 deletions errors/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,10 @@
{
"title": "nonce-contained-invalid-characters",
"path": "/errors/nonce-contained-invalid-characters.md"
},
{
"title": "invalid-escape-sequence-in-emotion-tagged-template",
"path": "/errors/invalid-escape-sequence-in-emotion-tagged-template.md"
}
]
}
Expand Down
18 changes: 16 additions & 2 deletions packages/next-swc/crates/emotion/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use regex::Regex;
use serde::{Deserialize, Serialize};
use sourcemap::{RawToken, SourceMap as RawSourcemap};
use swc_core::{
common::{comments::Comments, util::take::Take, BytePos, SourceMapperDyn, DUMMY_SP},
common::{comments::Comments, util::take::Take, BytePos, SourceMapperDyn, DUMMY_SP, errors::HANDLER},
ecma::utils::ExprFactory,
ecma::visit::{Fold, FoldWith},
ecma::{
Expand Down Expand Up @@ -337,7 +337,21 @@ impl<C: Comments> EmotionTransformer<C> {
if index % 2 == 0 {
if let Some(q) = tagged_tpl.quasis.get_mut(i) {
let q = q.take();
let minified = minify_css_string(&q.raw, index == 0, index == args_len - 1);
let input = match q.cooked {
Some(cooked) => cooked,
Comment on lines +342 to +343
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to lead to more headaches than expected. CSS allows \ to escape unicode chars without a u prefix, eg "\2022" should render a , and "\0025a0" should render a . Both of these are illegal escape sequences (they're legacy octal escapes in JS), but perfectly fine in CSS.

Rather than use JS's cooking semantics, we can manually cook the raw strings using CSS semantics. We can pilfer SWC's escape parser to do this: https://github.com/swc-project/swc/blob/decebadff1686adb8ea30d0ef84d849061205c7c/crates/swc_css_parser/src/lexer/mod.rs#L892-L969

_ => {
HANDLER.with(|handler| {
handler
.struct_span_err(
kdy1 marked this conversation as resolved.
Show resolved Hide resolved
q.span,
"This tagged template contains an invalid escape sequence.\nSee: https://nextjs.org/docs/messages/invalid-escape-sequence-in-emotion-tagged-template",
)
.emit()
});
q.raw
},
};
let minified = minify_css_string(&input, index == 0, index == args_len - 1);
// Compress one more spaces into one space
if minified.replace(' ', "").is_empty() {
if index != 0 && index != args_len - 1 {
Expand Down
Loading