|
3 | 3 |
|
4 | 4 | import * as ts from 'typescript'; |
5 | 5 |
|
| 6 | +import type { Collector } from '../collector/Collector'; |
| 7 | +import { ExtractorMessageId } from '../api/ExtractorMessageId'; |
| 8 | + |
6 | 9 | export class ModuleDocComment { |
7 | 10 | /** |
8 | 11 | * For the given source file, see if it starts with a TSDoc comment containing the `@module` tag. |
9 | 12 | */ |
10 | | - public static tryFindInSourceFile(sourceFile: ts.SourceFile): ts.TextRange | undefined { |
| 13 | + public static tryFindInSourceFile( |
| 14 | + sourceFile: ts.SourceFile, |
| 15 | + collector: Collector |
| 16 | + ): ts.TextRange | undefined { |
11 | 17 | // The @module comment is special because it is not attached to an AST |
12 | 18 | // definition. Instead, it is part of the "trivia" tokens that the compiler treats |
13 | 19 | // as irrelevant white space. |
14 | 20 | // |
15 | 21 | // This implementation assumes that the "@module" will be in the first TSDoc comment |
16 | 22 | // that appears in the source file. |
17 | 23 | let moduleCommentRange: ts.TextRange | undefined = undefined; |
| 24 | + let foundFirstJSDocComment: boolean = false; |
18 | 25 |
|
19 | 26 | for (const commentRange of ts.getLeadingCommentRanges(sourceFile.text, sourceFile.getFullStart()) || []) { |
20 | 27 | if (commentRange.kind === ts.SyntaxKind.MultiLineCommentTrivia) { |
21 | 28 | const commentBody: string = sourceFile.text.substring(commentRange.pos, commentRange.end); |
22 | 29 |
|
23 | 30 | // Choose the first JSDoc-style comment |
24 | 31 | if (/^\s*\/\*\*/.test(commentBody)) { |
25 | | - // But only if it looks like it's trying to be @module |
26 | | - // (The TSDoc parser will validate this more rigorously) |
27 | | - if (/\@module/i.test(commentBody)) { |
28 | | - moduleCommentRange = commentRange; |
| 32 | + if (!foundFirstJSDocComment) { |
| 33 | + foundFirstJSDocComment = true; |
| 34 | + // But only if it looks like it's trying to be @module |
| 35 | + // (The TSDoc parser will validate this more rigorously) |
| 36 | + if (/\@module/i.test(commentBody)) { |
| 37 | + moduleCommentRange = commentRange; |
| 38 | + } |
| 39 | + } else { |
| 40 | + // If we find another JSDoc comment with @module, report an error |
| 41 | + if (/\@module/i.test(commentBody)) { |
| 42 | + collector.messageRouter.addAnalyzerIssueForPosition( |
| 43 | + ExtractorMessageId.MisplacedModuleTag, |
| 44 | + 'The @module comment should only appear once at the top of the source file', |
| 45 | + sourceFile, |
| 46 | + commentRange.pos |
| 47 | + ); |
| 48 | + } |
29 | 49 | } |
30 | | - break; |
31 | 50 | } |
32 | 51 | } |
33 | 52 | } |
|
0 commit comments