@@ -2,7 +2,6 @@ import {join, relative} from 'path';
2
2
import { readFileSync } from 'fs' ;
3
3
import chalk from 'chalk' ;
4
4
import ts from 'typescript' ;
5
- import * as tsutils from 'tsutils' ;
6
5
7
6
const projectRoot = process . cwd ( ) ;
8
7
@@ -25,25 +24,39 @@ parsedConfig.fileNames.forEach((fileName: string) => {
25
24
readFileSync ( fileName , 'utf8' ) ,
26
25
configFile . languageVersion ,
27
26
) ;
28
- const lineRanges = tsutils . getLineRanges ( sourceFile ) ;
27
+ const lineStarts = sourceFile . getLineStarts ( ) ;
28
+ const fileText = sourceFile . getFullText ( ) ;
29
+ const seenRanges = new Set < string > ( ) ;
29
30
30
31
// Go through each of the comments of the file.
31
- tsutils . forEachComment ( sourceFile , ( file , range ) => {
32
- const comment = file . substring ( range . pos , range . end ) ;
33
- const versionMatch = comment . match ( versionRegex ) ;
34
-
35
- // Don't do any extra work if the comment doesn't indicate a breaking change.
36
- if ( ! versionMatch || comment . indexOf ( '@breaking-change' ) === - 1 ) {
37
- return ;
38
- }
39
-
40
- // Use a path relative to the project root, in order to make the summary more tidy.
41
- // Also replace escaped Windows slashes with regular forward slashes.
42
- const pathInProject = relative ( projectRoot , sourceFile . fileName ) . replace ( / \\ / g, '/' ) ;
43
- const [ version ] = versionMatch ;
44
-
45
- summary [ version ] = summary [ version ] || [ ] ;
46
- summary [ version ] . push ( ` ${ pathInProject } : ${ formatMessage ( comment , range , lineRanges ) } ` ) ;
32
+ sourceFile . forEachChild ( function walk ( node ) {
33
+ ts . getLeadingCommentRanges ( fileText , node . getFullStart ( ) ) ?. forEach ( range => {
34
+ const rangeKey = `${ range . pos } -${ range . end } ` ;
35
+
36
+ // Ranges can apply to more than one node.
37
+ if ( seenRanges . has ( rangeKey ) ) {
38
+ return ;
39
+ }
40
+
41
+ seenRanges . add ( rangeKey ) ;
42
+ const comment = fileText . slice ( range . pos , range . end ) ;
43
+ const versionMatch = comment . match ( versionRegex ) ;
44
+
45
+ // Don't do any extra work if the comment doesn't indicate a breaking change.
46
+ if ( ! versionMatch || comment . indexOf ( '@breaking-change' ) === - 1 ) {
47
+ return ;
48
+ }
49
+
50
+ // Use a path relative to the project root, in order to make the summary more tidy.
51
+ // Also replace escaped Windows slashes with regular forward slashes.
52
+ const pathInProject = relative ( projectRoot , sourceFile . fileName ) . replace ( / \\ / g, '/' ) ;
53
+ const [ version ] = versionMatch ;
54
+
55
+ summary [ version ] = summary [ version ] || [ ] ;
56
+ summary [ version ] . push ( ` ${ pathInProject } : ${ formatMessage ( comment , range . pos , lineStarts ) } ` ) ;
57
+ } ) ;
58
+
59
+ node . forEachChild ( walk ) ;
47
60
} ) ;
48
61
} ) ;
49
62
@@ -61,11 +74,13 @@ Object.keys(summary).forEach(version => {
61
74
/**
62
75
* Formats a message to be logged out in the breaking changes summary.
63
76
* @param comment Contents of the comment that contains the breaking change.
64
- * @param commentRange Object containing info on the position of the comment in the file.
65
- * @param lines Ranges of the lines of code in the file .
77
+ * @param position Position of the comment within the file.
78
+ * @param lineStarts Indexes at which the individual lines start .
66
79
*/
67
- function formatMessage ( comment : string , commentRange : ts . CommentRange , lines : tsutils . LineRange [ ] ) {
68
- const lineNumber = lines . findIndex ( line => line . pos > commentRange . pos ) ;
80
+ function formatMessage ( comment : string , position : number , lineStarts : readonly number [ ] ) {
81
+ // `lineStarts` is sorted so we could use binary search, but this isn't
82
+ // particularly performance-sensitive so we search linearly instead.
83
+ const lineNumber = lineStarts . findIndex ( line => line > position ) ;
69
84
const messageMatch = comment . match ( / @ d e p r e c a t e d ( .* ) | @ b r e a k i n g - c h a n g e ( .* ) / ) ;
70
85
const message = messageMatch ? messageMatch [ 0 ] : '' ;
71
86
const cleanMessage = message
0 commit comments