(node: SyntaxNode, source: string)
| 92 | * Get the docstring/comment preceding a node |
| 93 | */ |
| 94 | export function getPrecedingDocstring(node: SyntaxNode, source: string): string | undefined { |
| 95 | // Climb out of any wrapper(s) so a comment preceding the WHOLE construct |
| 96 | // (export-, decorator-, or const-arrow-wrapped) is reachable as a sibling. |
| 97 | // The emitted node's own `previousNamedSibling` is empty (export/const) or a |
| 98 | // decorator (Python) in those cases, so without this the docstring was |
| 99 | // dropped. (#780) |
| 100 | let anchor = node; |
| 101 | while (anchor.parent && DOCSTRING_WRAPPER_TYPES.has(anchor.parent.type)) { |
| 102 | anchor = anchor.parent; |
| 103 | } |
| 104 | |
| 105 | let sibling = anchor.previousNamedSibling; |
| 106 | const comments: string[] = []; |
| 107 | |
| 108 | while (sibling) { |
| 109 | if ( |
| 110 | sibling.type === 'comment' || |
| 111 | sibling.type === 'line_comment' || |
| 112 | sibling.type === 'block_comment' || |
| 113 | sibling.type === 'documentation_comment' |
| 114 | ) { |
| 115 | comments.unshift(getNodeText(sibling, source)); |
| 116 | sibling = sibling.previousNamedSibling; |
| 117 | } else { |
| 118 | break; |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | if (comments.length === 0) return undefined; |
| 123 | |
| 124 | // Strip each comment's syntax markers (language-aware), then join. |
| 125 | return comments.map(cleanCommentMarkers).join('\n').trim(); |
| 126 | } |
no test coverage detected