1use fervid_core::{SfcScriptBlock, SfcScriptLang};
2use swc_core::{
3 common::Span,
4 ecma::ast::{Expr, Module, Pat},
5};
6use swc_ecma_parser::{lexer::Lexer, EsConfig, Parser, StringInput, Syntax, TsConfig};
7use swc_html_ast::{Child, Element};
8
9use crate::{error::ParseErrorKind, ParseError, SfcParser};
10
11impl SfcParser<'_, '_, '_> {
12 pub fn parse_sfc_script_element(
14 &mut self,
15 element: Element,
16 ) -> Result<Option<SfcScriptBlock>, ParseError> {
17 let mut is_setup = false;
19 let mut is_setup_seen = false;
20 let mut is_lang_seen = false;
21 let mut lang = SfcScriptLang::Es;
22 for attr in element.attributes.iter() {
23 match attr.name.as_str() {
24 "setup" => {
25 if is_setup_seen {
26 self.report_error(ParseError {
27 kind: ParseErrorKind::DuplicateAttribute,
28 span: attr.span,
29 });
30 }
31
32 is_setup = true;
33 is_setup_seen = true;
34 }
35 "lang" if is_lang_seen => self.errors.push(ParseError {
36 kind: ParseErrorKind::DuplicateAttribute,
37 span: attr.span,
38 }),
39 "lang" => {
40 is_lang_seen = true;
41
42 lang = match attr.value.as_ref().map(|v| v.as_str()) {
43 Some("ts" | "typescript") => SfcScriptLang::Typescript,
44 None | Some("js" | "javascript") => SfcScriptLang::Es,
45 Some(_) => {
46 return Err(ParseError {
47 kind: ParseErrorKind::UnsupportedLang,
48 span: attr.span,
49 });
50 }
51 }
52 }
53 _ => {}
54 }
55 }
56
57 let script_content = match element.children.get(0) {
59 Some(Child::Text(t)) => t,
60 Some(_) => {
61 return Err(ParseError {
62 kind: ParseErrorKind::UnexpectedNonRawTextContent,
63 span: element.span,
64 });
65 }
66 None if self.ignore_empty => {
67 return Ok(None);
68 }
69 None => {
70 return Ok(Some(SfcScriptBlock {
72 content: Box::new(Module {
73 span: element.span,
74 body: Vec::new(),
75 shebang: None,
76 }),
77 lang,
78 is_setup,
79 span: element.span,
80 }));
81 }
82 };
83
84 if self.ignore_empty && script_content.data.trim().is_empty() {
86 return Ok(None);
87 }
88
89 let module_content = self.parse_module(
90 &script_content.data,
91 if matches!(lang, SfcScriptLang::Typescript) {
92 Syntax::Typescript(TsConfig::default())
93 } else {
94 Syntax::Es(EsConfig::default())
95 },
96 script_content.span,
97 )?;
98
99 Ok(Some(SfcScriptBlock {
100 content: Box::new(module_content),
101 lang,
102 is_setup,
103 span: element.span,
104 }))
105 }
106
107 #[inline]
108 pub fn parse_module(
109 &mut self,
110 raw: &str,
111 syntax: Syntax,
112 span: Span,
113 ) -> Result<Module, ParseError> {
114 let lexer = Lexer::new(
115 syntax,
116 Default::default(),
118 StringInput::new(raw, span.lo, span.hi),
119 Some(&self.comments),
120 );
121
122 let mut parser = Parser::new_from(lexer);
123 let parse_result = parser.parse_module();
124
125 self.errors
127 .extend(parser.take_errors().into_iter().map(From::from));
128
129 parse_result.map_err(From::from)
130 }
131
132 pub fn parse_expr(
133 &mut self,
134 raw: &str,
135 syntax: Syntax,
136 span: Span,
137 ) -> Result<Box<Expr>, ParseError> {
138 let lexer = Lexer::new(
139 syntax,
140 Default::default(),
142 StringInput::new(raw, span.lo, span.hi),
143 Some(&self.comments),
144 );
145
146 let mut parser = Parser::new_from(lexer);
147 let parse_result = parser.parse_expr();
148
149 self.errors
151 .extend(parser.take_errors().into_iter().map(From::from));
152
153 parse_result.map_err(From::from)
154 }
155
156 pub fn parse_pat(&mut self, raw: &str, syntax: Syntax, span: Span) -> Result<Pat, ParseError> {
157 let lexer = Lexer::new(
158 syntax,
159 Default::default(),
161 StringInput::new(raw, span.lo, span.hi),
162 Some(&self.comments),
163 );
164
165 let mut parser = Parser::new_from(lexer);
166 let parse_result = parser.parse_pat();
167
168 self.errors
170 .extend(parser.take_errors().into_iter().map(From::from));
171
172 parse_result.map_err(From::from)
173 }
174}