1use deno_ast::SourceRangedForSpanned;
4use deno_graph::symbols::EsModuleInfo;
5use serde::Deserialize;
6use serde::Serialize;
7
8use crate::params::ts_fn_param_to_param_def;
9use crate::ts_type::CallSignatureDef;
10use crate::ts_type::ConstructorDef;
11use crate::ts_type::IndexSignatureDef;
12use crate::ts_type::MethodDef;
13use crate::ts_type::PropertyDef;
14use crate::ts_type::TsTypeDef;
15use crate::ts_type_param::maybe_type_param_decl_to_type_param_defs;
16use crate::ts_type_param::TsTypeParamDef;
17use crate::util::swc::get_location;
18use crate::util::swc::js_doc_for_range;
19
20#[derive(Debug, Serialize, Deserialize, Clone)]
21#[serde(rename_all = "camelCase")]
22pub struct InterfaceDef {
23 #[serde(skip_serializing_if = "Option::is_none", default)]
24 pub def_name: Option<String>,
26 pub extends: Vec<TsTypeDef>,
27 #[serde(default)]
28 pub constructors: Vec<ConstructorDef>,
29 pub methods: Vec<MethodDef>,
30 pub properties: Vec<PropertyDef>,
31 pub call_signatures: Vec<CallSignatureDef>,
32 pub index_signatures: Vec<IndexSignatureDef>,
33 pub type_params: Box<[TsTypeParamDef]>,
34}
35
36pub fn expr_to_name(expr: &deno_ast::swc::ast::Expr) -> String {
37 use deno_ast::swc::ast::Expr::*;
38 use deno_ast::swc::ast::MemberProp;
39
40 match expr {
41 Ident(ident) => ident.sym.to_string(),
42 Member(member_expr) => {
43 let left = expr_to_name(&member_expr.obj);
44 let right = match &member_expr.prop {
45 MemberProp::Ident(ident) => format!(".{}", ident.sym),
46 MemberProp::Computed(_) | MemberProp::PrivateName(_) => {
47 "[UNSUPPORTED]".to_string()
48 }
49 };
50 format!("[{}{}]", left, right)
51 }
52 Lit(lit) => {
53 use deno_ast::swc::ast::BigInt;
54 use deno_ast::swc::ast::Bool;
55 use deno_ast::swc::ast::JSXText;
56 use deno_ast::swc::ast::Lit;
57 use deno_ast::swc::ast::Number;
58 use deno_ast::swc::ast::Regex;
59 use deno_ast::swc::ast::Str;
60 match lit {
61 Lit::Str(Str { ref value, .. }) => value.to_string(),
62 Lit::Bool(Bool { ref value, .. }) => {
63 let str_val = if *value { "true" } else { "false" };
64 str_val.to_string()
65 }
66 Lit::Null(_) => "null".to_string(),
67 Lit::Num(Number { ref value, .. }) => value.to_string(),
68 Lit::BigInt(BigInt { ref value, .. }) => value.to_string(),
69 Lit::Regex(Regex { ref exp, .. }) => format!("/{}/", exp),
70 Lit::JSXText(JSXText { ref raw, .. }) => raw.to_string(),
71 }
72 }
73 _ => "[UNSUPPORTED]".to_string(),
74 }
75}
76
77pub fn get_doc_for_ts_interface_decl(
78 module_info: &EsModuleInfo,
79 interface_decl: &deno_ast::swc::ast::TsInterfaceDecl,
80 def_name: Option<String>,
81) -> (String, InterfaceDef) {
82 let interface_name = interface_decl.id.sym.to_string();
83
84 let mut constructors = vec![];
85 let mut methods = vec![];
86 let mut properties = vec![];
87 let mut call_signatures = vec![];
88 let mut index_signatures = vec![];
89
90 for type_element in &interface_decl.body.body {
91 use deno_ast::swc::ast::TsTypeElement::*;
92
93 match &type_element {
94 TsMethodSignature(ts_method_sig) => {
95 if let Some(method_js_doc) =
96 js_doc_for_range(module_info, &ts_method_sig.range())
97 {
98 let mut params = vec![];
99
100 for param in &ts_method_sig.params {
101 let param_def = ts_fn_param_to_param_def(module_info, param);
102 params.push(param_def);
103 }
104
105 let name = expr_to_name(&ts_method_sig.key);
106
107 let maybe_return_type = ts_method_sig
108 .type_ann
109 .as_deref()
110 .map(|type_ann| TsTypeDef::new(module_info, &type_ann.type_ann));
111
112 let type_params = maybe_type_param_decl_to_type_param_defs(
113 module_info,
114 ts_method_sig.type_params.as_deref(),
115 );
116
117 let method_def = MethodDef {
118 name,
119 kind: deno_ast::swc::ast::MethodKind::Method,
120 js_doc: method_js_doc,
121 location: get_location(module_info, ts_method_sig.start()),
122 computed: ts_method_sig.computed,
123 optional: ts_method_sig.optional,
124 params,
125 return_type: maybe_return_type,
126 type_params,
127 };
128 methods.push(method_def);
129 }
130 }
131 TsGetterSignature(ts_getter_sig) => {
132 if let Some(method_js_doc) =
133 js_doc_for_range(module_info, &ts_getter_sig.range())
134 {
135 let name = expr_to_name(&ts_getter_sig.key);
136
137 let maybe_return_type = ts_getter_sig
138 .type_ann
139 .as_deref()
140 .map(|type_ann| TsTypeDef::new(module_info, &type_ann.type_ann));
141
142 let method_def = MethodDef {
143 name,
144 kind: deno_ast::swc::ast::MethodKind::Getter,
145 js_doc: method_js_doc,
146 location: get_location(module_info, ts_getter_sig.start()),
147 computed: ts_getter_sig.computed,
148 optional: false,
149 params: vec![],
150 return_type: maybe_return_type,
151 type_params: Box::new([]),
152 };
153 methods.push(method_def);
154 }
155 }
156 TsSetterSignature(ts_setter_sig) => {
157 if let Some(method_js_doc) =
158 js_doc_for_range(module_info, &ts_setter_sig.range())
159 {
160 let name = expr_to_name(&ts_setter_sig.key);
161
162 let param_def =
163 ts_fn_param_to_param_def(module_info, &ts_setter_sig.param);
164 let params = vec![param_def];
165
166 let method_def = MethodDef {
167 name,
168 kind: deno_ast::swc::ast::MethodKind::Setter,
169 js_doc: method_js_doc,
170 location: get_location(module_info, ts_setter_sig.start()),
171 computed: ts_setter_sig.computed,
172 optional: false,
173 params,
174 return_type: None,
175 type_params: Box::new([]),
176 };
177 methods.push(method_def);
178 }
179 }
180 TsPropertySignature(ts_prop_sig) => {
181 if let Some(prop_js_doc) =
182 js_doc_for_range(module_info, &ts_prop_sig.range())
183 {
184 let name = expr_to_name(&ts_prop_sig.key);
185
186 let ts_type = ts_prop_sig
187 .type_ann
188 .as_deref()
189 .map(|type_ann| TsTypeDef::new(module_info, &type_ann.type_ann));
190
191 let type_params =
192 maybe_type_param_decl_to_type_param_defs(module_info, None);
193
194 let prop_def = PropertyDef {
195 name,
196 js_doc: prop_js_doc,
197 location: get_location(module_info, ts_prop_sig.start()),
198 params: vec![],
199 ts_type,
200 readonly: ts_prop_sig.readonly,
201 computed: ts_prop_sig.computed,
202 optional: ts_prop_sig.optional,
203 type_params,
204 };
205 properties.push(prop_def);
206 }
207 }
208 TsCallSignatureDecl(ts_call_sig) => {
209 if let Some(call_sig_js_doc) =
210 js_doc_for_range(module_info, &ts_call_sig.range())
211 {
212 let mut params = vec![];
213 for param in &ts_call_sig.params {
214 let param_def = ts_fn_param_to_param_def(module_info, param);
215 params.push(param_def);
216 }
217
218 let ts_type = ts_call_sig
219 .type_ann
220 .as_deref()
221 .map(|type_ann| TsTypeDef::new(module_info, &type_ann.type_ann));
222
223 let type_params = maybe_type_param_decl_to_type_param_defs(
224 module_info,
225 ts_call_sig.type_params.as_deref(),
226 );
227
228 let call_sig_def = CallSignatureDef {
229 js_doc: call_sig_js_doc,
230 location: get_location(module_info, ts_call_sig.start()),
231 params,
232 ts_type,
233 type_params,
234 };
235 call_signatures.push(call_sig_def);
236 }
237 }
238 TsIndexSignature(ts_index_sig) => {
239 if let Some(js_doc) =
240 js_doc_for_range(module_info, &ts_index_sig.range())
241 {
242 let mut params = vec![];
243 for param in &ts_index_sig.params {
244 let param_def = ts_fn_param_to_param_def(module_info, param);
245 params.push(param_def);
246 }
247
248 let ts_type = ts_index_sig
249 .type_ann
250 .as_ref()
251 .map(|rt| TsTypeDef::new(module_info, &rt.type_ann));
252
253 let index_sig_def = IndexSignatureDef {
254 location: get_location(module_info, ts_index_sig.start()),
255 js_doc,
256 readonly: ts_index_sig.readonly,
257 params,
258 ts_type,
259 };
260 index_signatures.push(index_sig_def);
261 }
262 }
263 TsConstructSignatureDecl(ts_construct_sig) => {
264 if let Some(js_doc) =
265 js_doc_for_range(module_info, &ts_construct_sig.range())
266 {
267 let mut params = vec![];
268
269 for param in &ts_construct_sig.params {
270 let param_def = ts_fn_param_to_param_def(module_info, param);
271 params.push(param_def);
272 }
273
274 let type_params = maybe_type_param_decl_to_type_param_defs(
275 module_info,
276 ts_construct_sig.type_params.as_deref(),
277 );
278
279 let maybe_return_type = ts_construct_sig
280 .type_ann
281 .as_ref()
282 .map(|rt| TsTypeDef::new(module_info, &rt.type_ann));
283
284 let construct_sig_def = ConstructorDef {
285 js_doc,
286 location: get_location(module_info, ts_construct_sig.start()),
287 params,
288 return_type: maybe_return_type,
289 type_params,
290 };
291
292 constructors.push(construct_sig_def);
293 }
294 }
295 }
296 }
297
298 let type_params = maybe_type_param_decl_to_type_param_defs(
299 module_info,
300 interface_decl.type_params.as_deref(),
301 );
302
303 let extends = interface_decl
304 .extends
305 .iter()
306 .map(|expr| TsTypeDef::ts_expr_with_type_args(module_info, expr))
307 .collect::<Vec<TsTypeDef>>();
308
309 let interface_def = InterfaceDef {
310 def_name,
311 extends,
312 constructors,
313 methods,
314 properties,
315 call_signatures,
316 index_signatures,
317 type_params,
318 };
319
320 (interface_name, interface_def)
321}