9
9
import ts from 'typescript' ;
10
10
11
11
import { ErrorCode , makeDiagnostic , makeRelatedInformation } from '../../../diagnostics' ;
12
- import { ImportedSymbolsTracker , Reference } from '../../../imports' ;
13
- import {
12
+ import type { ImportedSymbolsTracker , Reference } from '../../../imports' ;
13
+ import type { ClassDeclaration } from '../../../reflection' ;
14
+ import type {
14
15
TemplateTypeChecker ,
15
16
TypeCheckableDirectiveMeta ,
16
17
TypeCheckingConfig ,
17
18
} from '../../../typecheck/api' ;
18
19
19
- import { SourceFileValidatorRule } from './api' ;
20
+ import type { SourceFileValidatorRule } from './api' ;
20
21
21
22
/**
22
23
* Rule that flags unused symbols inside of the `imports` array of a component.
@@ -79,7 +80,7 @@ export class UnusedStandaloneImportsRule implements SourceFileValidatorRule {
79
80
if ( unused . length === metadata . imports . length ) {
80
81
return makeDiagnostic (
81
82
ErrorCode . UNUSED_STANDALONE_IMPORTS ,
82
- metadata . rawImports ,
83
+ this . getDiagnosticNode ( metadata . rawImports ) ,
83
84
'All imports are unused' ,
84
85
undefined ,
85
86
category ,
@@ -88,14 +89,19 @@ export class UnusedStandaloneImportsRule implements SourceFileValidatorRule {
88
89
89
90
return makeDiagnostic (
90
91
ErrorCode . UNUSED_STANDALONE_IMPORTS ,
91
- metadata . rawImports ,
92
+ this . getDiagnosticNode ( metadata . rawImports ) ,
92
93
'Imports array contains unused imports' ,
93
- unused . map ( ( [ ref , type , name ] ) =>
94
- makeRelatedInformation (
95
- ref . getOriginForDiagnostics ( metadata . rawImports ! ) ,
96
- `${ type } "${ name } " is not used within the template` ,
97
- ) ,
98
- ) ,
94
+ unused . map ( ( ref ) => {
95
+ return makeRelatedInformation (
96
+ // Intentionally don't pass a message to `makeRelatedInformation` to make the diagnostic
97
+ // less noisy. The node will already be highlighted so the user can see which node is
98
+ // unused. Note that in the case where an origin can't be resolved, we fall back to
99
+ // the original node's identifier so the user can still see the name. This can happen
100
+ // when the unused is coming from an imports array within the same file.
101
+ ref . getOriginForDiagnostics ( metadata . rawImports ! , ref . node . name ) ,
102
+ '' ,
103
+ ) ;
104
+ } ) ,
99
105
category ,
100
106
) ;
101
107
}
@@ -111,7 +117,7 @@ export class UnusedStandaloneImportsRule implements SourceFileValidatorRule {
111
117
return null ;
112
118
}
113
119
114
- let unused : [ ref : Reference , type : string , name : string ] [ ] | null = null ;
120
+ let unused : Reference < ClassDeclaration > [ ] | null = null ;
115
121
116
122
for ( const current of imports ) {
117
123
const currentNode = current . node as ts . ClassDeclaration ;
@@ -124,7 +130,7 @@ export class UnusedStandaloneImportsRule implements SourceFileValidatorRule {
124
130
! this . isPotentialSharedReference ( current , rawImports )
125
131
) {
126
132
unused ??= [ ] ;
127
- unused . push ( [ current , dirMeta . isComponent ? 'Component' : 'Directive' , dirMeta . name ] ) ;
133
+ unused . push ( current ) ;
128
134
}
129
135
continue ;
130
136
}
@@ -138,7 +144,7 @@ export class UnusedStandaloneImportsRule implements SourceFileValidatorRule {
138
144
! this . isPotentialSharedReference ( current , rawImports )
139
145
) {
140
146
unused ??= [ ] ;
141
- unused . push ( [ current , 'Pipe' , pipeMeta . ref . node . name . text ] ) ;
147
+ unused . push ( current ) ;
142
148
}
143
149
}
144
150
@@ -175,4 +181,21 @@ export class UnusedStandaloneImportsRule implements SourceFileValidatorRule {
175
181
// symbol like an array of shared common components.
176
182
return true ;
177
183
}
184
+
185
+ /** Gets the node on which to report the diagnostic. */
186
+ private getDiagnosticNode ( importsExpression : ts . Expression ) : ts . Node {
187
+ let current = importsExpression . parent ;
188
+
189
+ while ( current ) {
190
+ // Highlight the `imports:` part of the node instead of the entire node, because
191
+ // imports arrays can be long which makes the diagnostic harder to scan visually.
192
+ if ( ts . isPropertyAssignment ( current ) ) {
193
+ return current . name ;
194
+ } else {
195
+ current = current . parent ;
196
+ }
197
+ }
198
+
199
+ return importsExpression ;
200
+ }
178
201
}
0 commit comments