1use crate::{
2 derive_ASTNode,
3 errors::parse_lexing_error,
4 functions::{FunctionBased, HeadingAndPosition, MethodHeader, ThisParameter},
5 property_key::AlwaysPublic,
6 throw_unexpected_token_with_token,
7 visiting::Visitable,
8 ASTNode, Block, Expression, FunctionBase, ParseOptions, ParseResult, PropertyKey, Span,
9 TSXToken, Token, TokenReader, WithComment,
10};
11
12use derive_partial_eq_extras::PartialEqExtras;
13use std::fmt::Debug;
14use tokenizer_lib::sized_tokens::{TokenReaderWithTokenEnds, TokenStart};
15use visitable_derive::Visitable;
16
17#[apply(derive_ASTNode)]
18#[derive(Debug, Clone, PartialEq, Visitable, get_field_by_type::GetFieldByType)]
19#[get_field_by_type_target(Span)]
20pub struct ObjectLiteral {
21 pub members: Vec<ObjectLiteralMember>,
22 pub position: Span,
23}
24
25#[apply(derive_ASTNode)]
26#[derive(Debug, Clone, PartialEqExtras, get_field_by_type::GetFieldByType)]
27#[partial_eq_ignore_types(Span, VariableId)]
28#[get_field_by_type_target(Span)]
29pub enum ObjectLiteralMember {
30 Spread(Expression, Span),
31 Shorthand(String, Span),
32 Property {
33 key: WithComment<PropertyKey<AlwaysPublic>>,
34 assignment: bool,
36 value: Expression,
37 position: Span,
38 },
39 Method(ObjectLiteralMethod),
40 Comment(String, bool, Span),
41}
42
43impl crate::Visitable for ObjectLiteralMember {
44 fn visit<TData>(
45 &self,
46 visitors: &mut (impl crate::VisitorReceiver<TData> + ?Sized),
47 data: &mut TData,
48 options: &crate::VisitOptions,
49 chain: &mut temporary_annex::Annex<crate::Chain>,
50 ) {
51 match self {
52 ObjectLiteralMember::Shorthand(..)
53 | ObjectLiteralMember::Property { .. }
54 | ObjectLiteralMember::Spread(..)
55 | ObjectLiteralMember::Comment(..) => {}
56 ObjectLiteralMember::Method(method) => method.visit(visitors, data, options, chain),
57 }
58 }
59
60 fn visit_mut<TData>(
61 &mut self,
62 visitors: &mut (impl crate::VisitorMutReceiver<TData> + ?Sized),
63 data: &mut TData,
64 options: &crate::VisitOptions,
65 chain: &mut temporary_annex::Annex<crate::Chain>,
66 ) {
67 match self {
68 ObjectLiteralMember::Shorthand(..)
69 | ObjectLiteralMember::Property { .. }
70 | ObjectLiteralMember::Spread(..)
71 | ObjectLiteralMember::Comment(..) => {}
72 ObjectLiteralMember::Method(method) => method.visit_mut(visitors, data, options, chain),
73 }
74 }
75}
76
77#[derive(Debug, PartialEq, Eq, Clone, Hash)]
78pub struct ObjectLiteralMethodBase;
79pub type ObjectLiteralMethod = FunctionBase<ObjectLiteralMethodBase>;
80
81#[cfg_attr(target_family = "wasm", wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section))]
82#[allow(dead_code)]
83const OBJECT_LITERAL_METHOD_TYPE: &str = r"
84 export interface ObjectLiteralMethod extends FunctionBase {
85 header: MethodHeader,
86 body: Block,
87 name: WithComment<PropertyKey<AlwaysPublic>>,
88 parameters: FunctionParameters<ThisParameter | null, null>
89 }
90";
91
92impl FunctionBased for ObjectLiteralMethodBase {
93 type Name = WithComment<PropertyKey<AlwaysPublic>>;
94 type Header = MethodHeader;
95 type Body = Block;
96 type LeadingParameter = Option<ThisParameter>;
97 type ParameterVisibility = ();
98
99 fn header_and_name_from_reader(
100 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
101 state: &mut crate::ParsingState,
102 options: &ParseOptions,
103 ) -> ParseResult<(HeadingAndPosition<Self>, Self::Name)> {
104 let start = reader.peek().ok_or_else(parse_lexing_error)?.1;
106 Ok((
107 (Some(start), MethodHeader::from_reader(reader)),
108 WithComment::from_reader(reader, state, options)?,
109 ))
110 }
111
112 fn header_and_name_to_string_from_buffer<T: source_map::ToString>(
113 buf: &mut T,
114 header: &Self::Header,
115 name: &Self::Name,
116 options: &crate::ToStringOptions,
117 local: crate::LocalToStringInformation,
118 ) {
119 header.to_string_from_buffer(buf);
120 name.to_string_from_buffer(buf, options, local);
121 }
122
123 fn visit_name<TData>(
124 name: &Self::Name,
125 visitors: &mut (impl crate::VisitorReceiver<TData> + ?Sized),
126 data: &mut TData,
127 options: &crate::visiting::VisitOptions,
128 chain: &mut temporary_annex::Annex<crate::Chain>,
129 ) {
130 name.visit(visitors, data, options, chain);
131 }
132
133 fn visit_name_mut<TData>(
134 name: &mut Self::Name,
135 visitors: &mut (impl crate::VisitorMutReceiver<TData> + ?Sized),
136 data: &mut TData,
137 options: &crate::visiting::VisitOptions,
138 chain: &mut temporary_annex::Annex<crate::Chain>,
139 ) {
140 name.visit_mut(visitors, data, options, chain);
141 }
142
143 fn get_name(name: &Self::Name) -> Option<&str> {
144 if let PropertyKey::Identifier(name, ..) = name.get_ast_ref() {
145 Some(name.as_str())
146 } else {
147 None
148 }
149 }
150}
151
152impl Eq for ObjectLiteralMember {}
153
154impl ASTNode for ObjectLiteral {
155 fn get_position(&self) -> Span {
156 self.position
157 }
158
159 fn from_reader(
160 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
161 state: &mut crate::ParsingState,
162 options: &ParseOptions,
163 ) -> ParseResult<Self> {
164 let start = reader.expect_next(TSXToken::OpenBrace)?;
165 Self::from_reader_sub_open_curly(reader, state, options, start)
166 }
167
168 fn to_string_from_buffer<T: source_map::ToString>(
169 &self,
170 buf: &mut T,
171 options: &crate::ToStringOptions,
172 local: crate::LocalToStringInformation,
173 ) {
174 crate::to_string_bracketed(&self.members, ('{', '}'), buf, options, local);
175 }
176}
177
178impl ObjectLiteral {
179 pub(crate) fn from_reader_sub_open_curly(
180 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
181 state: &mut crate::ParsingState,
182 options: &ParseOptions,
183 start: TokenStart,
184 ) -> ParseResult<Self> {
185 let mut members: Vec<ObjectLiteralMember> = Vec::new();
186 loop {
187 if matches!(reader.peek(), Some(Token(TSXToken::CloseBrace, _))) {
188 break;
189 }
190 let member = ObjectLiteralMember::from_reader(reader, state, options)?;
191 let is_comment = matches!(member, ObjectLiteralMember::Comment(..));
192 members.push(member);
193 if let Some(Token(TSXToken::Comma, _)) = reader.peek() {
194 reader.next();
195 } else if !is_comment {
196 break;
197 }
198 }
199 let end = reader.expect_next_get_end(TSXToken::CloseBrace)?;
200 Ok(ObjectLiteral { members, position: start.union(end) })
201 }
202}
203
204impl ASTNode for ObjectLiteralMember {
205 #[allow(clippy::similar_names)]
206 fn from_reader(
207 reader: &mut impl TokenReader<TSXToken, crate::TokenStart>,
208 state: &mut crate::ParsingState,
209 options: &ParseOptions,
210 ) -> ParseResult<Self> {
211 if reader.peek().map_or(false, |t| t.0.is_comment()) {
212 let (comment, is_multiline, span) =
213 TSXToken::try_into_comment(reader.next().unwrap()).unwrap();
214 return Ok(Self::Comment(comment, is_multiline, span));
215 }
216
217 if let Some(Token(_, spread_start)) =
218 reader.conditional_next(|tok| matches!(tok, TSXToken::Spread))
219 {
220 let expression = Expression::from_reader(reader, state, options)?;
222 let position = spread_start.union(expression.get_position());
223 return Ok(Self::Spread(expression, position));
224 };
225
226 let start = reader.peek().ok_or_else(parse_lexing_error)?.1;
228
229 let (header, key) = crate::functions::get_method_name(reader, state, options)?;
231
232 let Token(token, _) = &reader.peek().ok_or_else(parse_lexing_error)?;
233 match token {
234 TSXToken::OpenParentheses | TSXToken::OpenChevron => {
236 let method: ObjectLiteralMethod = FunctionBase::from_reader_with_header_and_name(
237 reader,
238 state,
239 options,
240 (Some(start), header),
241 key,
242 )?;
243
244 Ok(Self::Method(method))
245 }
246 _ => {
247 if !header.is_no_modifiers() {
248 return crate::throw_unexpected_token(reader, &[TSXToken::OpenParentheses]);
249 }
250 if let Some(Token(TSXToken::Comma | TSXToken::CloseBrace, _)) = reader.peek() {
251 if let PropertyKey::Identifier(name, position, _) = key.get_ast() {
252 Ok(Self::Shorthand(name, position))
253 } else {
254 let token = reader.next().ok_or_else(parse_lexing_error)?;
255 throw_unexpected_token_with_token(token, &[TSXToken::Colon])
256 }
257 } else {
258 let token = reader.next().ok_or_else(parse_lexing_error)?;
259 let assignment = match token.0 {
260 TSXToken::Colon => false,
261 TSXToken::Assign => true,
262 _ => return throw_unexpected_token_with_token(token, &[TSXToken::Colon]),
263 };
264 let value = Expression::from_reader(reader, state, options)?;
266 let position = key.get_position().union(value.get_position());
267 Ok(Self::Property { assignment, key, value, position })
268 }
269 }
270 }
271 }
272
273 fn to_string_from_buffer<T: source_map::ToString>(
274 &self,
275 buf: &mut T,
276 options: &crate::ToStringOptions,
277 local: crate::LocalToStringInformation,
278 ) {
279 match self {
280 Self::Property { assignment: _, key, value, position: _ } => {
281 key.to_string_from_buffer(buf, options, local);
282 buf.push(':');
283 options.push_gap_optionally(buf);
284 value.to_string_from_buffer(buf, options, local);
285 }
286 Self::Shorthand(name, ..) => {
287 buf.push_str(name.as_str());
288 }
289 Self::Method(func) => {
290 func.to_string_from_buffer(buf, options, local);
291 }
292 Self::Spread(spread_expr, _) => {
293 buf.push_str("...");
294 spread_expr.to_string_from_buffer(buf, options, local);
295 }
296 Self::Comment(content, is_multiline, _) => {
297 if options.should_add_comment(content) {
298 if *is_multiline {
299 buf.push_str("/*");
300 buf.push_str(content);
301 buf.push_str("*/");
302 } else {
303 buf.push_str("//");
304 buf.push_str(content);
305 buf.push_new_line();
306 }
307 }
308 }
309 };
310 }
311
312 fn get_position(&self) -> Span {
313 *get_field_by_type::GetFieldByType::get(self)
314 }
315}