swc_ecma_lexer/lexer/
jsx.rs

1use either::Either;
2
3use super::*;
4
5impl Lexer<'_> {
6    pub(super) fn read_jsx_token(&mut self) -> LexResult<Token> {
7        debug_assert!(self.syntax.jsx());
8
9        let start = self.input.cur_pos();
10        let mut chunk_start = self.input.cur_pos();
11        let mut value = String::new();
12
13        loop {
14            let cur = match self.input.cur() {
15                Some(c) => c,
16                None => {
17                    let start = self.state.start;
18                    self.error(start, SyntaxError::UnterminatedJSXContents)?
19                }
20            };
21            let cur_pos = self.input.cur_pos();
22
23            match cur {
24                '<' if self.had_line_break_before_last() && self.is_str("<<<<<< ") => {
25                    let span = Span::new_with_checked(cur_pos, cur_pos + BytePos(7));
26
27                    self.emit_error_span(span, SyntaxError::TS1185);
28                    self.skip_line_comment(6);
29                    self.skip_space::<true>();
30                    return self.read_token();
31                }
32                '<' | '{' => {
33                    //
34                    if cur_pos == self.state.start {
35                        if cur == '<' && self.state.is_expr_allowed {
36                            unsafe {
37                                // Safety: cur() was Some('<')
38                                self.input.bump();
39                            }
40                            return Ok(Token::JSXTagStart);
41                        }
42                        return self.read_token();
43                    }
44
45                    let s = unsafe {
46                        // Safety: We already checked for the range
47                        self.input.slice(chunk_start, cur_pos)
48                    };
49                    let value = if value.is_empty() {
50                        // Fast path: We don't need to allocate extra buffer for value
51                        self.atoms.atom(s)
52                    } else {
53                        value.push_str(s);
54                        self.atoms.atom(value)
55                    };
56
57                    let raw = {
58                        let s = unsafe {
59                            // Safety: We already checked for the range
60                            self.input.slice(start, cur_pos)
61                        };
62                        self.atoms.atom(s)
63                    };
64
65                    return Ok(Token::JSXText { raw, value });
66                }
67                '>' => {
68                    self.emit_error(
69                        cur_pos,
70                        SyntaxError::UnexpectedTokenWithSuggestions {
71                            candidate_list: vec!["`{'>'}`", "`&gt;`"],
72                        },
73                    );
74                    unsafe {
75                        // Safety: cur() was Some('>')
76                        self.input.bump()
77                    }
78                }
79                '}' => {
80                    self.emit_error(
81                        cur_pos,
82                        SyntaxError::UnexpectedTokenWithSuggestions {
83                            candidate_list: vec!["`{'}'}`", "`&rbrace;`"],
84                        },
85                    );
86                    unsafe {
87                        // Safety: cur() was Some('}')
88                        self.input.bump()
89                    }
90                }
91                '&' => {
92                    value.push_str(unsafe {
93                        // Safety: We already checked for the range
94                        self.input.slice(chunk_start, cur_pos)
95                    });
96
97                    let jsx_entity = self.read_jsx_entity()?;
98
99                    value.push(jsx_entity.0);
100                    chunk_start = self.input.cur_pos();
101                }
102
103                _ => {
104                    if cur.is_line_terminator() {
105                        value.push_str(unsafe {
106                            // Safety: We already checked for the range
107                            self.input.slice(chunk_start, cur_pos)
108                        });
109                        match self.read_jsx_new_line(true)? {
110                            Either::Left(s) => value.push_str(s),
111                            Either::Right(c) => value.push(c),
112                        }
113                        chunk_start = self.input.cur_pos();
114                    } else {
115                        unsafe {
116                            // Safety: cur() was Some(c)
117                            self.input.bump()
118                        }
119                    }
120                }
121            }
122        }
123    }
124}