@@ -13,9 +13,11 @@ module.exports = createRule({
1313 internalCommentNotLastError : `@internal should only appear in final JSDoc comment for declaration.` ,
1414 multipleJSDocError : `Declaration has multiple JSDoc comments.` ,
1515 internalCommentOnParameterProperty : `@internal cannot appear on a JSDoc comment; use a declared property and an assignment in the constructor instead.` ,
16+ misalignedJSDocComment : `This JSDoc comment is misaligned.` ,
1617 } ,
1718 schema : [ ] ,
1819 type : "problem" ,
20+ fixable : "whitespace" ,
1921 } ,
2022 defaultOptions : [ ] ,
2123
@@ -24,6 +26,11 @@ module.exports = createRule({
2426 const atInternal = "@internal" ;
2527 const jsdocStart = "/**" ;
2628
29+ /** @type {(text: string) => boolean } */
30+ function isJSDocText ( text ) {
31+ return text . startsWith ( jsdocStart ) ;
32+ }
33+
2734 /** @type {(c: TSESTree.Comment, indexInComment: number) => TSESTree.SourceLocation } */
2835 const getAtInternalLoc = ( c , indexInComment ) => {
2936 const line = c . loc . start . line ;
@@ -51,7 +58,7 @@ module.exports = createRule({
5158 } ;
5259
5360 /** @type {(node: TSESTree.Node) => void } */
54- const checkJSDocFormat = ( node ) => {
61+ const checkDeclaration = ( node ) => {
5562 const blockComments = sourceCode . getCommentsBefore ( node ) . filter ( c => c . type === "Block" ) ;
5663 if ( blockComments . length === 0 ) {
5764 return ;
@@ -63,7 +70,7 @@ module.exports = createRule({
6370 const c = blockComments [ i ] ;
6471 const rawComment = sourceCode . getText ( c ) ;
6572
66- const isJSDoc = rawComment . startsWith ( jsdocStart ) ;
73+ const isJSDoc = isJSDocText ( rawComment ) ;
6774 if ( isJSDoc && seenJSDoc ) {
6875 context . report ( { messageId : "multipleJSDocError" , node : c , loc : getJSDocStartLoc ( c ) } ) ;
6976 }
@@ -86,25 +93,85 @@ module.exports = createRule({
8693 }
8794 } ;
8895
96+ /** @type {(node: TSESTree.Node) => void } */
97+ const checkProgram = ( ) => {
98+ const comments = sourceCode . getAllComments ( ) ;
99+
100+ for ( const c of comments ) {
101+ if ( c . type !== "Block" ) {
102+ continue ;
103+ }
104+
105+ const rawComment = sourceCode . getText ( c ) ;
106+ if ( ! isJSDocText ( rawComment ) ) {
107+ continue ;
108+ }
109+
110+ const expected = c . loc . start . column + 2 ;
111+ const split = rawComment . split ( / \r ? \n / g) ;
112+ for ( let i = 1 ; i < split . length ; i ++ ) {
113+ const line = split [ i ] ;
114+ const match = / ^ * \* / . exec ( line ) ;
115+ if ( ! match ) {
116+ continue ;
117+ }
118+
119+ const actual = match [ 0 ] . length ;
120+ const diff = actual - expected ;
121+ if ( diff !== 0 ) {
122+ const line = c . loc . start . line + i ;
123+ context . report ( {
124+ messageId : "misalignedJSDocComment" ,
125+ node : c ,
126+ loc : {
127+ start : {
128+ line,
129+ column : 0 ,
130+ } ,
131+ end : {
132+ line,
133+ column : actual - 1 ,
134+ }
135+ } ,
136+ fix : ( fixer ) => {
137+ if ( diff > 0 ) {
138+ // Too many
139+ const start = sourceCode . getIndexFromLoc ( { line, column : expected - 1 } ) ;
140+ return fixer . removeRange ( [ start , start + diff ] ) ;
141+ }
142+ else {
143+ // Too few
144+ const start = sourceCode . getIndexFromLoc ( { line, column : 0 } ) ;
145+ return fixer . insertTextAfterRange ( [ start , start ] , " " . repeat ( - diff ) ) ;
146+ }
147+ } ,
148+ } ) ;
149+ break ;
150+ }
151+ }
152+ }
153+ } ;
154+
89155 return {
90- ClassDeclaration : checkJSDocFormat ,
91- FunctionDeclaration : checkJSDocFormat ,
92- TSEnumDeclaration : checkJSDocFormat ,
93- TSModuleDeclaration : checkJSDocFormat ,
94- VariableDeclaration : checkJSDocFormat ,
95- TSInterfaceDeclaration : checkJSDocFormat ,
96- TSTypeAliasDeclaration : checkJSDocFormat ,
97- TSCallSignatureDeclaration : checkJSDocFormat ,
98- ExportAllDeclaration : checkJSDocFormat ,
99- ExportNamedDeclaration : checkJSDocFormat ,
100- TSImportEqualsDeclaration : checkJSDocFormat ,
101- TSNamespaceExportDeclaration : checkJSDocFormat ,
102- TSConstructSignatureDeclaration : checkJSDocFormat ,
103- ExportDefaultDeclaration : checkJSDocFormat ,
104- TSPropertySignature : checkJSDocFormat ,
105- TSIndexSignature : checkJSDocFormat ,
106- TSMethodSignature : checkJSDocFormat ,
107- TSParameterProperty : checkJSDocFormat ,
156+ Program : checkProgram ,
157+ ClassDeclaration : checkDeclaration ,
158+ FunctionDeclaration : checkDeclaration ,
159+ TSEnumDeclaration : checkDeclaration ,
160+ TSModuleDeclaration : checkDeclaration ,
161+ VariableDeclaration : checkDeclaration ,
162+ TSInterfaceDeclaration : checkDeclaration ,
163+ TSTypeAliasDeclaration : checkDeclaration ,
164+ TSCallSignatureDeclaration : checkDeclaration ,
165+ ExportAllDeclaration : checkDeclaration ,
166+ ExportNamedDeclaration : checkDeclaration ,
167+ TSImportEqualsDeclaration : checkDeclaration ,
168+ TSNamespaceExportDeclaration : checkDeclaration ,
169+ TSConstructSignatureDeclaration : checkDeclaration ,
170+ ExportDefaultDeclaration : checkDeclaration ,
171+ TSPropertySignature : checkDeclaration ,
172+ TSIndexSignature : checkDeclaration ,
173+ TSMethodSignature : checkDeclaration ,
174+ TSParameterProperty : checkDeclaration ,
108175 } ;
109176 } ,
110177} ) ;
0 commit comments