11use super :: * ;
2- use biome_js_syntax:: { AnyJsRoot , JsSyntaxNode , TextRange , TsConditionalType , TsTypeParameterName } ;
2+ use biome_js_syntax:: {
3+ AnyJsDeclaration , AnyJsRoot , JsExport , JsSyntaxNode , TextRange , TsConditionalType ,
4+ TsTypeParameterName ,
5+ } ;
6+ use biome_jsdoc_comment:: JsdocComment ;
37use biome_rowan:: SyntaxNodePtr ;
48use rustc_hash:: { FxHashMap , FxHashSet } ;
59use std:: collections:: hash_map:: Entry ;
@@ -27,6 +31,7 @@ pub struct SemanticModelBuilder {
2731 declared_at_by_start : FxHashMap < TextSize , BindingId > ,
2832 exported : FxHashSet < TextSize > ,
2933 unresolved_references : Vec < SemanticModelUnresolvedReference > ,
34+ pub ( crate ) export_jsdoc_by_range : FxHashMap < TextRange , JsdocComment > ,
3035}
3136
3237impl SemanticModelBuilder {
@@ -45,6 +50,7 @@ impl SemanticModelBuilder {
4550 declared_at_by_start : FxHashMap :: default ( ) ,
4651 exported : FxHashSet :: default ( ) ,
4752 unresolved_references : Vec :: new ( ) ,
53+ export_jsdoc_by_range : FxHashMap :: default ( ) ,
4854 }
4955 }
5056
@@ -113,6 +119,12 @@ impl SemanticModelBuilder {
113119 self . scope_node_by_range
114120 . insert ( node. text_trimmed_range ( ) , node. clone ( ) ) ;
115121 }
122+ JS_EXPORT => {
123+ if let Ok ( jsdoc) = JsdocComment :: try_from ( node) {
124+ self . export_jsdoc_by_range
125+ . insert ( node. text_trimmed_range ( ) , jsdoc) ;
126+ }
127+ }
116128 _ => {
117129 if let Some ( conditional_type) = TsConditionalType :: cast_ref ( node)
118130 && let Ok ( conditional_true_type) = conditional_type. true_type ( )
@@ -181,11 +193,16 @@ impl SemanticModelBuilder {
181193 debug_assert ! ( ( binding_scope_id. index( ) ) < self . scopes. len( ) ) ;
182194
183195 let binding_id = BindingId :: new ( self . bindings . len ( ) ) ;
196+ let jsdoc = self
197+ . binding_node_by_start
198+ . get ( & range. start ( ) )
199+ . and_then ( find_jsdoc) ;
184200 self . bindings . push ( SemanticModelBindingData {
185201 range,
186202 references : Vec :: new ( ) ,
187- export_by_start : smallvec:: SmallVec :: new ( ) ,
203+ export_ranges : smallvec:: SmallVec :: new ( ) ,
188204 declaration_kind,
205+ jsdoc,
189206 } ) ;
190207 self . bindings_by_start . insert ( range. start ( ) , binding_id) ;
191208
@@ -345,7 +362,7 @@ impl SemanticModelBuilder {
345362
346363 let binding_id = self . bindings_by_start [ & declaration_at] ;
347364 let binding = & mut self . bindings [ binding_id. index ( ) ] ;
348- binding. export_by_start . push ( range. start ( ) ) ;
365+ binding. export_ranges . push ( range) ;
349366 }
350367 }
351368 }
@@ -379,7 +396,20 @@ impl SemanticModelBuilder {
379396 exported : self . exported ,
380397 unresolved_references : self . unresolved_references ,
381398 globals : self . globals ,
399+ export_jsdoc_by_range : self . export_jsdoc_by_range ,
382400 } ;
383401 SemanticModel :: new ( data)
384402 }
385403}
404+
405+ fn find_jsdoc ( node : & JsSyntaxNode ) -> Option < JsdocComment > {
406+ node. ancestors ( ) . find_map ( |ancestor| {
407+ if let Some ( export) = JsExport :: cast_ref ( & ancestor) {
408+ JsdocComment :: try_from ( export. syntax ( ) ) . ok ( )
409+ } else if let Some ( decl) = AnyJsDeclaration :: cast ( ancestor) {
410+ JsdocComment :: try_from ( decl. syntax ( ) ) . ok ( )
411+ } else {
412+ None
413+ }
414+ } )
415+ }
0 commit comments