Repository: swc-project/swc
File: crates/swc_ecma_transforms_react/src/jsx/mod.rs (Rust)

Code before:
    source: &Wtf8,
    acc: &mut Option<Wtf8Buf>,
    only_line: &mut Option<(usize, usize)>,
) {
    if let Some((only_start, only_end)) = only_line.take() {
        // Second line - create accumulator
        let mut buffer = Wtf8Buf::with_capacity(source.len());
        buffer.push_wtf8(source.slice(only_start, only_end));
        buffer.push_str(" ");
        buffer.push_wtf8(source.slice(line_start, line_end));
        *acc = Some(buffer);
    } else if let Some(ref mut buffer) = acc {
        // Subsequent lines
        buffer.push_str(" ");
        buffer.push_wtf8(source.slice(line_start, line_end));
    } else {
        // First line
        *only_line = Some((line_start, line_end));
    }
}

/// Internal implementation that works with &str
#[inline]
fn jsx_text_to_str_impl(t: &str) -> Atom {
    // Fast path: if no line terminators and no leading/trailing whitespace
    if !t.is_empty()
        && !t.chars().any(is_line_terminator)
        && !t.starts_with(is_white_space_single_line)
        && !t.ends_with(is_white_space_single_line)
    {
        return t.into();
    }

    let mut acc: Option<String> = None;
    let mut only_line: Option<&str> = None;
    let mut first_non_whitespace: Option<usize> = Some(0);
    let mut last_non_whitespace: Option<usize> = None;

    for (index, c) in t.char_indices() {
        if is_line_terminator(c) {
            if let (Some(first), Some(last)) = (first_non_whitespace, last_non_whitespace) {
                let line_text = &t[first..last];
                add_line_of_jsx_text(line_text, &mut acc, &mut only_line);
            }
            first_non_whitespace = None;
        } else if !is_white_space_single_line(c) {
            last_non_whitespace = Some(index + c.len_utf8());
            if first_non_whitespace.is_none() {
                first_non_whitespace.replace(index);
            }
        }

Review comment (bug):
The fast path incorrectly rejects strings with trailing whitespace, but the original slow path preserves trailing whitespace for single-line text. At line 1586, the slow path uses `&t[first..]` which includes any trailing whitespace after the last non-whitespace character. This means the fast path will reject inputs that the slow path would accept, causing different behavior. The check for `!t.ends_with(is_white_space_single_line)` at line 1560 should be removed to match the original behavior.
```suggestion
    // Fast path: if no line terminators and no leading whitespace
    if !t.is_empty()
        && !t.chars().any(is_line_terminator)
        && !t.starts_with(is_white_space_single_line)
```

Code after:
) {
    if let Some((only_start, only_end)) = only_line.take() {
        // Second line - create accumulator
        let mut buffer = Wtf8Buf::with_capacity(source.len());
        buffer.push_wtf8(source.slice(only_start, only_end));
        buffer.push_str(" ");
        buffer.push_wtf8(source.slice(line_start, line_end));
        *acc = Some(buffer);
    } else if let Some(ref mut buffer) = acc {
        // Subsequent lines
        buffer.push_str(" ");
        buffer.push_wtf8(source.slice(line_start, line_end));
    } else {
        // First line
        *only_line = Some((line_start, line_end));
    }
}

/// Internal implementation that works with &str
#[inline]
fn jsx_text_to_str_impl(t: &str) -> Atom {
    // Fast path: if no line terminators and no leading/trailing whitespace
    if !t.is_empty()
        && !t.chars().any(is_line_terminator)
        && !t.starts_with(is_white_space_single_line)
        && !t.ends_with(is_white_space_single_line)
    {
        return t.into();
    }

    let mut acc: Option<String> = None;
    let mut only_line: Option<&str> = None;
    let mut first_non_whitespace: Option<usize> = Some(0);
    let mut last_non_whitespace: Option<usize> = None;

    for (index, c) in t.char_indices() {
        if is_line_terminator(c) {
            if let (Some(first), Some(last)) = (first_non_whitespace, last_non_whitespace) {
                let line_text = &t[first..last];
                add_line_of_jsx_text(line_text, &mut acc, &mut only_line);
            }
            first_non_whitespace = None;
        } else if !is_white_space_single_line(c) {
            last_non_whitespace = Some(index + c.len_utf8());
            if first_non_whitespace.is_none() {
                first_non_whitespace.replace(index);
            }
        }
    }

    if let Some(first) = first_non_whitespace {