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!["`{'>'}`", "`>`"],
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!["`{'}'}`", "`}`"],
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}