@@ -160,32 +160,37 @@ export class Linter {
160160 return this . rules . length
161161 }
162162
163+ protected findRuleClass ( ruleName : string ) : RuleClass | undefined {
164+ return this . rules . find ( ruleClass => ruleClass . ruleName === ruleName )
165+ }
166+
163167 /**
164- * Type guard to check if a rule is a LexerRule
168+ * Type guard to check if a rule class is a LexerRule class
165169 */
166- protected isLexerRule ( rule : Rule ) : rule is LexerRule {
167- return ( rule . constructor as any ) . type === "lexer"
170+ protected isLexerRuleClass ( ruleClass : RuleClass ) : boolean {
171+ return ruleClass . type === "lexer"
168172 }
169173
170174 /**
171- * Type guard to check if a rule is a SourceRule
175+ * Type guard to check if a rule class is a SourceRule class
172176 */
173- protected isSourceRule ( rule : Rule ) : rule is SourceRule {
174- return ( rule . constructor as any ) . type === "source"
177+ protected isSourceRuleClass ( ruleClass : RuleClass ) : boolean {
178+ return ruleClass . type === "source"
175179 }
176180
177181 /**
178- * Type guard to check if a rule is a ParserRule
182+ * Type guard to check if a rule class is a ParserRule class
179183 */
180- protected isParserRule ( rule : Rule ) : rule is ParserRule {
181- return ( rule . constructor as any ) . type === "parser"
184+ protected isParserRuleClass ( ruleClass : RuleClass ) : boolean {
185+ return ruleClass . type === "parser" || ruleClass . type === undefined
182186 }
183187
184188 /**
185189 * Execute a single rule and return its unbound offenses.
186190 * Handles rule type checking (Lexer/Parser/Source) and isEnabled checks.
187191 */
188192 private executeRule (
193+ ruleClass : RuleClass ,
189194 rule : Rule ,
190195 parseResult : ParseResult ,
191196 lexResult : LexResult ,
@@ -215,34 +220,40 @@ export class Linter {
215220 let isEnabled = true
216221 let ruleOffenses : UnboundLintOffense [ ]
217222
218- if ( this . isLexerRule ( rule ) ) {
219- if ( rule . isEnabled ) {
220- isEnabled = rule . isEnabled ( lexResult , context )
223+ if ( this . isLexerRuleClass ( ruleClass ) ) {
224+ const lexerRule = rule as LexerRule
225+
226+ if ( lexerRule . isEnabled ) {
227+ isEnabled = lexerRule . isEnabled ( lexResult , context )
221228 }
222229
223230 if ( isEnabled ) {
224- ruleOffenses = ( rule as LexerRule ) . check ( lexResult , context )
231+ ruleOffenses = lexerRule . check ( lexResult , context )
225232 } else {
226233 ruleOffenses = [ ]
227234 }
228235
229- } else if ( this . isSourceRule ( rule ) ) {
230- if ( rule . isEnabled ) {
231- isEnabled = rule . isEnabled ( source , context )
236+ } else if ( this . isSourceRuleClass ( ruleClass ) ) {
237+ const sourceRule = rule as SourceRule
238+
239+ if ( sourceRule . isEnabled ) {
240+ isEnabled = sourceRule . isEnabled ( source , context )
232241 }
233242
234243 if ( isEnabled ) {
235- ruleOffenses = ( rule as SourceRule ) . check ( source , context )
244+ ruleOffenses = sourceRule . check ( source , context )
236245 } else {
237246 ruleOffenses = [ ]
238247 }
239248 } else {
240- if ( rule . isEnabled ) {
241- isEnabled = rule . isEnabled ( parseResult , context )
249+ const parserRule = rule as ParserRule
250+
251+ if ( parserRule . isEnabled ) {
252+ isEnabled = parserRule . isEnabled ( parseResult , context )
242253 }
243254
244255 if ( isEnabled ) {
245- ruleOffenses = ( rule as ParserRule ) . check ( parseResult , context )
256+ ruleOffenses = parserRule . check ( parseResult , context )
246257 } else {
247258 ruleOffenses = [ ]
248259 }
@@ -336,7 +347,7 @@ export class Linter {
336347 const herbDisableCache = new Map < number , string [ ] > ( )
337348
338349 if ( hasParserErrors ) {
339- const hasParserRule = this . rules . find ( RuleClass => RuleClass . ruleName === "parser-no-errors" )
350+ const hasParserRule = this . findRuleClass ( "parser-no-errors" )
340351
341352 if ( hasParserRule ) {
342353 const rule = new ParserNoErrorsRule ( )
@@ -356,31 +367,31 @@ export class Linter {
356367
357368 context = {
358369 ...context ,
359- validRuleNames : this . getAvailableRules ( ) . map ( RuleClass => RuleClass . ruleName ) ,
370+ validRuleNames : this . getAvailableRules ( ) . map ( ruleClass => ruleClass . ruleName ) ,
360371 ignoredOffensesByLine
361372 }
362373
363- const regularRules = this . rules . filter ( RuleClass => RuleClass . ruleName !== "herb-disable-comment-unnecessary" )
374+ const regularRules = this . rules . filter ( ruleClass => ruleClass . ruleName !== "herb-disable-comment-unnecessary" )
364375
365- for ( const RuleClass of regularRules ) {
366- const rule = new RuleClass ( )
367- const parserOptions = this . isParserRule ( rule ) ? rule . parserOptions : { }
376+ for ( const ruleClass of regularRules ) {
377+ const rule = new ruleClass ( )
378+ const parserOptions = this . isParserRuleClass ( ruleClass ) ? ( rule as ParserRule ) . parserOptions : { }
368379 const parseResult = this . parseCache . get ( source , parserOptions )
369380
370381 // Skip parser rules whose parse result has errors (parser-no-errors handled above)
371382 // Skip lexer/source rules when the default parse has errors
372- if ( this . isParserRule ( rule ) ) {
383+ if ( this . isParserRuleClass ( ruleClass ) ) {
373384 if ( parseResult . recursiveErrors ( ) . length > 0 ) continue
374385 } else if ( hasParserErrors ) {
375386 continue
376387 }
377388
378- const unboundOffenses = this . executeRule ( rule , parseResult , lexResult , source , context )
379- const boundOffenses = this . bindSeverity ( unboundOffenses , RuleClass . ruleName )
389+ const unboundOffenses = this . executeRule ( ruleClass , rule , parseResult , lexResult , source , context )
390+ const boundOffenses = this . bindSeverity ( unboundOffenses , ruleClass . ruleName )
380391
381392 const { kept, ignored, wouldBeIgnored } = this . filterOffenses (
382393 boundOffenses ,
383- RuleClass . ruleName ,
394+ ruleClass . ruleName ,
384395 ignoredOffensesByLine ,
385396 herbDisableCache ,
386397 context ?. ignoreDisableComments
@@ -391,7 +402,7 @@ export class Linter {
391402 this . offenses . push ( ...kept )
392403 }
393404
394- const unnecessaryRuleClass = this . rules . find ( RuleClass => RuleClass . ruleName === "herb-disable-comment-unnecessary" )
405+ const unnecessaryRuleClass = this . findRuleClass ( "herb-disable-comment-unnecessary" )
395406
396407 if ( unnecessaryRuleClass ) {
397408 const unnecessaryRule = new unnecessaryRuleClass ( ) as ParserRule
@@ -437,16 +448,16 @@ export class Linter {
437448 * @returns Array of offenses with severity bound
438449 */
439450 protected bindSeverity ( unboundOffenses : UnboundLintOffense [ ] , ruleName : string ) : LintOffense [ ] {
440- const RuleClass = this . rules . find ( rule => rule . ruleName === ruleName )
451+ const ruleClass = this . findRuleClass ( ruleName )
441452
442- if ( ! RuleClass ) {
453+ if ( ! ruleClass ) {
443454 return unboundOffenses . map ( offense => ( {
444455 ...offense ,
445456 severity : "error" as const
446457 } ) )
447458 }
448459
449- const ruleInstance = new RuleClass ( )
460+ const ruleInstance = new ruleClass ( )
450461 const defaultSeverity = ruleInstance . defaultConfig ?. severity ?? DEFAULT_RULE_CONFIG . severity
451462
452463 const userRuleConfig = this . config ?. linter ?. rules ?. [ ruleName ]
@@ -477,13 +488,13 @@ export class Linter {
477488 const sourceOffenses : LintOffense [ ] = [ ]
478489
479490 for ( const offense of lintResult . offenses ) {
480- const RuleClass = this . rules . find ( rule => rule . ruleName === offense . rule )
491+ const ruleClass = this . findRuleClass ( offense . rule )
481492
482- if ( ! RuleClass ) continue
493+ if ( ! ruleClass ) continue
483494
484- if ( ( RuleClass as any ) . type === "lexer" ) {
495+ if ( this . isLexerRuleClass ( ruleClass ) ) {
485496 lexerOffenses . push ( offense )
486- } else if ( ( RuleClass as any ) . type === "source" ) {
497+ } else if ( this . isSourceRuleClass ( ruleClass ) ) {
487498 sourceOffenses . push ( offense )
488499 } else {
489500 parserOffenses . push ( offense )
@@ -498,16 +509,16 @@ export class Linter {
498509 const parseResult = this . parseCache . get ( currentSource )
499510
500511 for ( const offense of parserOffenses ) {
501- const RuleClass = this . rules . find ( rule => rule . ruleName === offense . rule )
512+ const ruleClass = this . findRuleClass ( offense . rule )
502513
503- if ( ! RuleClass ) {
514+ if ( ! ruleClass ) {
504515 unfixed . push ( offense )
505516
506517 continue
507518 }
508519
509- const rule = new RuleClass ( ) as ParserRule
510- const isUnsafe = ( RuleClass as any ) . unsafeAutocorrectable === true
520+ const rule = new ruleClass ( ) as ParserRule
521+ const isUnsafe = ( ruleClass as any ) . unsafeAutocorrectable === true
511522
512523 if ( ! rule . autofix ) {
513524 unfixed . push ( offense )
@@ -565,15 +576,15 @@ export class Linter {
565576 } )
566577
567578 for ( const offense of sortedSourceOffenses ) {
568- const RuleClass = this . rules . find ( rule => rule . ruleName === offense . rule )
579+ const ruleClass = this . findRuleClass ( offense . rule )
569580
570- if ( ! RuleClass ) {
581+ if ( ! ruleClass ) {
571582 unfixed . push ( offense )
572583 continue
573584 }
574585
575- const rule = new RuleClass ( ) as SourceRule
576- const isUnsafe = ( RuleClass as any ) . unsafeAutocorrectable === true
586+ const rule = new ruleClass ( ) as SourceRule
587+ const isUnsafe = ( ruleClass as any ) . unsafeAutocorrectable === true
577588
578589 if ( ! rule . autofix ) {
579590 unfixed . push ( offense )
0 commit comments