deno_doc/
interface.rs

1// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
2
3use 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  /// set when the interface is a default export
25  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}