@herb-tools/linter 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -2
- package/dist/herb-lint.js +426 -116
- package/dist/herb-lint.js.map +1 -1
- package/dist/index.cjs +322 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +317 -63
- package/dist/index.js.map +1 -1
- package/dist/package.json +4 -4
- package/dist/src/cli/file-processor.js +2 -4
- package/dist/src/cli/file-processor.js.map +1 -1
- package/dist/src/default-rules.js +6 -0
- package/dist/src/default-rules.js.map +1 -1
- package/dist/src/linter.js +37 -6
- package/dist/src/linter.js.map +1 -1
- package/dist/src/rules/erb-no-empty-tags.js +4 -3
- package/dist/src/rules/erb-no-empty-tags.js.map +1 -1
- package/dist/src/rules/erb-no-output-control-flow.js +4 -3
- package/dist/src/rules/erb-no-output-control-flow.js.map +1 -1
- package/dist/src/rules/erb-prefer-image-tag-helper.js +93 -0
- package/dist/src/rules/erb-prefer-image-tag-helper.js.map +1 -0
- package/dist/src/rules/erb-require-whitespace-inside-tags.js +4 -3
- package/dist/src/rules/erb-require-whitespace-inside-tags.js.map +1 -1
- package/dist/src/rules/erb-requires-trailing-newline.js +22 -0
- package/dist/src/rules/erb-requires-trailing-newline.js.map +1 -0
- package/dist/src/rules/html-anchor-require-href.js +4 -3
- package/dist/src/rules/html-anchor-require-href.js.map +1 -1
- package/dist/src/rules/html-aria-attribute-must-be-valid.js +4 -3
- package/dist/src/rules/html-aria-attribute-must-be-valid.js.map +1 -1
- package/dist/src/rules/html-aria-level-must-be-valid.js +27 -0
- package/dist/src/rules/html-aria-level-must-be-valid.js.map +1 -0
- package/dist/src/rules/html-aria-role-heading-requires-level.js +4 -3
- package/dist/src/rules/html-aria-role-heading-requires-level.js.map +1 -1
- package/dist/src/rules/html-aria-role-must-be-valid.js +4 -3
- package/dist/src/rules/html-aria-role-must-be-valid.js.map +1 -1
- package/dist/src/rules/html-attribute-double-quotes.js +4 -3
- package/dist/src/rules/html-attribute-double-quotes.js.map +1 -1
- package/dist/src/rules/html-attribute-values-require-quotes.js +4 -3
- package/dist/src/rules/html-attribute-values-require-quotes.js.map +1 -1
- package/dist/src/rules/html-boolean-attributes-no-value.js +4 -3
- package/dist/src/rules/html-boolean-attributes-no-value.js.map +1 -1
- package/dist/src/rules/html-img-require-alt.js +4 -3
- package/dist/src/rules/html-img-require-alt.js.map +1 -1
- package/dist/src/rules/html-no-block-inside-inline.js +4 -3
- package/dist/src/rules/html-no-block-inside-inline.js.map +1 -1
- package/dist/src/rules/html-no-duplicate-attributes.js +4 -3
- package/dist/src/rules/html-no-duplicate-attributes.js.map +1 -1
- package/dist/src/rules/html-no-duplicate-ids.js +4 -3
- package/dist/src/rules/html-no-duplicate-ids.js.map +1 -1
- package/dist/src/rules/html-no-empty-headings.js +4 -3
- package/dist/src/rules/html-no-empty-headings.js.map +1 -1
- package/dist/src/rules/html-no-nested-links.js +4 -3
- package/dist/src/rules/html-no-nested-links.js.map +1 -1
- package/dist/src/rules/html-tag-name-lowercase.js +4 -3
- package/dist/src/rules/html-tag-name-lowercase.js.map +1 -1
- package/dist/src/rules/index.js +3 -0
- package/dist/src/rules/index.js.map +1 -1
- package/dist/src/rules/rule-utils.js +125 -2
- package/dist/src/rules/rule-utils.js.map +1 -1
- package/dist/src/rules/svg-tag-name-capitalization.js +4 -3
- package/dist/src/rules/svg-tag-name-capitalization.js.map +1 -1
- package/dist/src/types.js +15 -1
- package/dist/src/types.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/linter.d.ts +20 -5
- package/dist/types/rules/erb-no-empty-tags.d.ts +4 -3
- package/dist/types/rules/erb-no-output-control-flow.d.ts +4 -3
- package/dist/types/rules/erb-prefer-image-tag-helper.d.ts +7 -0
- package/dist/types/rules/erb-require-whitespace-inside-tags.d.ts +4 -3
- package/dist/types/rules/erb-requires-trailing-newline.d.ts +6 -0
- package/dist/types/rules/html-anchor-require-href.d.ts +4 -3
- package/dist/types/rules/html-aria-attribute-must-be-valid.d.ts +4 -3
- package/dist/types/rules/html-aria-level-must-be-valid.d.ts +7 -0
- package/dist/types/rules/html-aria-role-heading-requires-level.d.ts +4 -3
- package/dist/types/rules/html-aria-role-must-be-valid.d.ts +4 -3
- package/dist/types/rules/html-attribute-double-quotes.d.ts +4 -3
- package/dist/types/rules/html-attribute-values-require-quotes.d.ts +4 -3
- package/dist/types/rules/html-boolean-attributes-no-value.d.ts +4 -3
- package/dist/types/rules/html-img-require-alt.d.ts +4 -3
- package/dist/types/rules/html-no-block-inside-inline.d.ts +4 -3
- package/dist/types/rules/html-no-duplicate-attributes.d.ts +4 -3
- package/dist/types/rules/html-no-duplicate-ids.d.ts +4 -3
- package/dist/types/rules/html-no-empty-headings.d.ts +4 -3
- package/dist/types/rules/html-no-nested-links.d.ts +4 -3
- package/dist/types/rules/html-tag-name-lowercase.d.ts +4 -3
- package/dist/types/rules/index.d.ts +3 -0
- package/dist/types/rules/rule-utils.d.ts +73 -4
- package/dist/types/rules/svg-tag-name-capitalization.d.ts +4 -3
- package/dist/types/src/linter.d.ts +20 -5
- package/dist/types/src/rules/erb-no-empty-tags.d.ts +4 -3
- package/dist/types/src/rules/erb-no-output-control-flow.d.ts +4 -3
- package/dist/types/src/rules/erb-prefer-image-tag-helper.d.ts +7 -0
- package/dist/types/src/rules/erb-require-whitespace-inside-tags.d.ts +4 -3
- package/dist/types/src/rules/erb-requires-trailing-newline.d.ts +6 -0
- package/dist/types/src/rules/html-anchor-require-href.d.ts +4 -3
- package/dist/types/src/rules/html-aria-attribute-must-be-valid.d.ts +4 -3
- package/dist/types/src/rules/html-aria-level-must-be-valid.d.ts +7 -0
- package/dist/types/src/rules/html-aria-role-heading-requires-level.d.ts +4 -3
- package/dist/types/src/rules/html-aria-role-must-be-valid.d.ts +4 -3
- package/dist/types/src/rules/html-attribute-double-quotes.d.ts +4 -3
- package/dist/types/src/rules/html-attribute-values-require-quotes.d.ts +4 -3
- package/dist/types/src/rules/html-boolean-attributes-no-value.d.ts +4 -3
- package/dist/types/src/rules/html-img-require-alt.d.ts +4 -3
- package/dist/types/src/rules/html-no-block-inside-inline.d.ts +4 -3
- package/dist/types/src/rules/html-no-duplicate-attributes.d.ts +4 -3
- package/dist/types/src/rules/html-no-duplicate-ids.d.ts +4 -3
- package/dist/types/src/rules/html-no-empty-headings.d.ts +4 -3
- package/dist/types/src/rules/html-no-nested-links.d.ts +4 -3
- package/dist/types/src/rules/html-tag-name-lowercase.d.ts +4 -3
- package/dist/types/src/rules/index.d.ts +3 -0
- package/dist/types/src/rules/rule-utils.d.ts +73 -4
- package/dist/types/src/rules/svg-tag-name-capitalization.d.ts +4 -3
- package/dist/types/src/types.d.ts +49 -6
- package/dist/types/types.d.ts +49 -6
- package/docs/rules/README.md +4 -1
- package/docs/rules/erb-prefer-image-tag-helper.md +65 -0
- package/docs/rules/erb-requires-trailing-newline.md +37 -0
- package/docs/rules/html-anchor-require-href.md +1 -1
- package/docs/rules/html-aria-level-must-be-valid.md +37 -0
- package/package.json +4 -4
- package/src/cli/file-processor.ts +2 -4
- package/src/default-rules.ts +6 -0
- package/src/linter.ts +42 -8
- package/src/rules/erb-no-empty-tags.ts +5 -4
- package/src/rules/erb-no-output-control-flow.ts +6 -4
- package/src/rules/erb-prefer-image-tag-helper.ts +124 -0
- package/src/rules/erb-require-whitespace-inside-tags.ts +5 -4
- package/src/rules/erb-requires-trailing-newline.ts +27 -0
- package/src/rules/html-anchor-require-href.ts +5 -4
- package/src/rules/html-aria-attribute-must-be-valid.ts +5 -5
- package/src/rules/html-aria-level-must-be-valid.ts +42 -0
- package/src/rules/html-aria-role-heading-requires-level.ts +5 -4
- package/src/rules/html-aria-role-must-be-valid.ts +5 -4
- package/src/rules/html-attribute-double-quotes.ts +5 -4
- package/src/rules/html-attribute-values-require-quotes.ts +5 -4
- package/src/rules/html-boolean-attributes-no-value.ts +5 -4
- package/src/rules/html-img-require-alt.ts +5 -4
- package/src/rules/html-no-block-inside-inline.ts +5 -4
- package/src/rules/html-no-duplicate-attributes.ts +5 -4
- package/src/rules/html-no-duplicate-ids.ts +5 -5
- package/src/rules/html-no-empty-headings.ts +5 -4
- package/src/rules/html-no-nested-links.ts +5 -4
- package/src/rules/html-tag-name-lowercase.ts +5 -4
- package/src/rules/index.ts +3 -0
- package/src/rules/rule-utils.ts +156 -4
- package/src/rules/svg-tag-name-capitalization.ts +5 -4
- package/src/types.ts +60 -6
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseRuleVisitor, getTagName } from "./rule-utils.js"
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { ParserRule } from "../types.js"
|
|
4
|
+
import type { LintOffense, LintContext } from "../types.js"
|
|
4
5
|
import type { HTMLOpenTagNode, HTMLElementNode, Node } from "@herb-tools/core"
|
|
5
6
|
|
|
6
7
|
class NestedLinkVisitor extends BaseRuleVisitor {
|
|
@@ -54,11 +55,11 @@ class NestedLinkVisitor extends BaseRuleVisitor {
|
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
export class HTMLNoNestedLinksRule
|
|
58
|
+
export class HTMLNoNestedLinksRule extends ParserRule {
|
|
58
59
|
name = "html-no-nested-links"
|
|
59
60
|
|
|
60
|
-
check(node: Node): LintOffense[] {
|
|
61
|
-
const visitor = new NestedLinkVisitor(this.name)
|
|
61
|
+
check(node: Node, context?: Partial<LintContext>): LintOffense[] {
|
|
62
|
+
const visitor = new NestedLinkVisitor(this.name, context)
|
|
62
63
|
visitor.visit(node)
|
|
63
64
|
return visitor.offenses
|
|
64
65
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseRuleVisitor } from "./rule-utils.js"
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { ParserRule } from "../types.js"
|
|
4
|
+
import type { LintOffense, LintContext } from "../types.js"
|
|
4
5
|
import type { HTMLElementNode, HTMLOpenTagNode, HTMLCloseTagNode, HTMLSelfCloseTagNode, Node } from "@herb-tools/core"
|
|
5
6
|
|
|
6
7
|
class TagNameLowercaseVisitor extends BaseRuleVisitor {
|
|
@@ -54,11 +55,11 @@ class TagNameLowercaseVisitor extends BaseRuleVisitor {
|
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
export class HTMLTagNameLowercaseRule
|
|
58
|
+
export class HTMLTagNameLowercaseRule extends ParserRule {
|
|
58
59
|
name = "html-tag-name-lowercase"
|
|
59
60
|
|
|
60
|
-
check(node: Node): LintOffense[] {
|
|
61
|
-
const visitor = new TagNameLowercaseVisitor(this.name)
|
|
61
|
+
check(node: Node, context?: Partial<LintContext>): LintOffense[] {
|
|
62
|
+
const visitor = new TagNameLowercaseVisitor(this.name, context)
|
|
62
63
|
visitor.visit(node)
|
|
63
64
|
return visitor.offenses
|
|
64
65
|
}
|
package/src/rules/index.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export * from "./erb-no-empty-tags.js"
|
|
2
2
|
export * from "./erb-no-output-control-flow.js"
|
|
3
|
+
export * from "./erb-prefer-image-tag-helper.js"
|
|
4
|
+
export * from "./erb-requires-trailing-newline.js"
|
|
3
5
|
export * from "./html-anchor-require-href.js"
|
|
6
|
+
export * from "./html-aria-level-must-be-valid.js"
|
|
4
7
|
export * from "./html-aria-role-heading-requires-level.js"
|
|
5
8
|
export * from "./html-aria-role-must-be-valid.js"
|
|
6
9
|
export * from "./html-attribute-double-quotes.js"
|
package/src/rules/rule-utils.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
Visitor
|
|
2
|
+
Visitor,
|
|
3
|
+
Position,
|
|
4
|
+
Location
|
|
3
5
|
} from "@herb-tools/core"
|
|
4
6
|
|
|
5
7
|
import type {
|
|
@@ -10,9 +12,11 @@ import type {
|
|
|
10
12
|
HTMLOpenTagNode,
|
|
11
13
|
HTMLSelfCloseTagNode,
|
|
12
14
|
LiteralNode,
|
|
13
|
-
|
|
15
|
+
LexResult,
|
|
16
|
+
Token
|
|
14
17
|
} from "@herb-tools/core"
|
|
15
|
-
import type { LintOffense, LintSeverity, } from "../types.js"
|
|
18
|
+
import type { LintOffense, LintSeverity, LintContext } from "../types.js"
|
|
19
|
+
import { DEFAULT_LINT_CONTEXT } from "../types.js"
|
|
16
20
|
|
|
17
21
|
/**
|
|
18
22
|
* Base visitor class that provides common functionality for rule visitors
|
|
@@ -20,11 +24,13 @@ import type { LintOffense, LintSeverity, } from "../types.js"
|
|
|
20
24
|
export abstract class BaseRuleVisitor extends Visitor {
|
|
21
25
|
public readonly offenses: LintOffense[] = []
|
|
22
26
|
protected ruleName: string
|
|
27
|
+
protected context: LintContext
|
|
23
28
|
|
|
24
|
-
constructor(ruleName: string) {
|
|
29
|
+
constructor(ruleName: string, context?: Partial<LintContext>) {
|
|
25
30
|
super()
|
|
26
31
|
|
|
27
32
|
this.ruleName = ruleName
|
|
33
|
+
this.context = { ...DEFAULT_LINT_CONTEXT, ...context }
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
/**
|
|
@@ -299,6 +305,22 @@ export const ARIA_ATTRIBUTES = new Set([
|
|
|
299
305
|
'aria-valuetext',
|
|
300
306
|
])
|
|
301
307
|
|
|
308
|
+
/**
|
|
309
|
+
* Helper function to create a location at the end of the source with a 1-character range
|
|
310
|
+
*/
|
|
311
|
+
export function createEndOfFileLocation(source: string): Location {
|
|
312
|
+
const lines = source.split('\n')
|
|
313
|
+
const lastLineNumber = lines.length
|
|
314
|
+
const lastLine = lines[lines.length - 1]
|
|
315
|
+
const lastColumnNumber = lastLine.length
|
|
316
|
+
|
|
317
|
+
const startColumn = lastColumnNumber > 0 ? lastColumnNumber - 1 : 0
|
|
318
|
+
const start = new Position(lastLineNumber, startColumn)
|
|
319
|
+
const end = new Position(lastLineNumber, lastColumnNumber)
|
|
320
|
+
|
|
321
|
+
return new Location(start, end)
|
|
322
|
+
}
|
|
323
|
+
|
|
302
324
|
/**
|
|
303
325
|
* Checks if an element is inline
|
|
304
326
|
*/
|
|
@@ -326,6 +348,10 @@ export function isBooleanAttribute(attributeName: string): boolean {
|
|
|
326
348
|
* and attribute iteration logic. Provides simplified interface with extracted attribute info.
|
|
327
349
|
*/
|
|
328
350
|
export abstract class AttributeVisitorMixin extends BaseRuleVisitor {
|
|
351
|
+
constructor(ruleName: string, context?: Partial<LintContext>) {
|
|
352
|
+
super(ruleName, context)
|
|
353
|
+
}
|
|
354
|
+
|
|
329
355
|
visitHTMLOpenTagNode(node: HTMLOpenTagNode): void {
|
|
330
356
|
this.checkAttributesOnNode(node)
|
|
331
357
|
super.visitHTMLOpenTagNode(node)
|
|
@@ -383,3 +409,129 @@ export function forEachAttribute(
|
|
|
383
409
|
}
|
|
384
410
|
}
|
|
385
411
|
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Base lexer visitor class that provides common functionality for lexer-based rule visitors
|
|
415
|
+
*/
|
|
416
|
+
export abstract class BaseLexerRuleVisitor {
|
|
417
|
+
public readonly offenses: LintOffense[] = []
|
|
418
|
+
protected ruleName: string
|
|
419
|
+
protected context: LintContext
|
|
420
|
+
|
|
421
|
+
constructor(ruleName: string, context?: Partial<LintContext>) {
|
|
422
|
+
this.ruleName = ruleName
|
|
423
|
+
this.context = { ...DEFAULT_LINT_CONTEXT, ...context }
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Helper method to create a lint offense for lexer rules
|
|
428
|
+
*/
|
|
429
|
+
protected createOffense(message: string, location: Location, severity: LintSeverity = "error"): LintOffense {
|
|
430
|
+
return {
|
|
431
|
+
rule: this.ruleName,
|
|
432
|
+
code: this.ruleName,
|
|
433
|
+
source: "Herb Linter",
|
|
434
|
+
message,
|
|
435
|
+
location,
|
|
436
|
+
severity,
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Helper method to add an offense to the offenses array
|
|
442
|
+
*/
|
|
443
|
+
protected addOffense(message: string, location: Location, severity: LintSeverity = "error"): void {
|
|
444
|
+
this.offenses.push(this.createOffense(message, location, severity))
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Main entry point for lexer rule visitors
|
|
449
|
+
* @param lexResult - The lexer result containing tokens and source
|
|
450
|
+
*/
|
|
451
|
+
visit(lexResult: LexResult): void {
|
|
452
|
+
this.visitTokens(lexResult.value.tokens)
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Visit all tokens
|
|
457
|
+
* Override this method to implement token-level checks
|
|
458
|
+
*/
|
|
459
|
+
protected visitTokens(tokens: Token[]): void {
|
|
460
|
+
for (const token of tokens) {
|
|
461
|
+
this.visitToken(token)
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Visit individual tokens
|
|
467
|
+
* Override this method to implement per-token checks
|
|
468
|
+
*/
|
|
469
|
+
protected visitToken(_token: Token): void {
|
|
470
|
+
// Default implementation does nothing
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Base source visitor class that provides common functionality for source-based rule visitors
|
|
477
|
+
*/
|
|
478
|
+
export abstract class BaseSourceRuleVisitor {
|
|
479
|
+
public readonly offenses: LintOffense[] = []
|
|
480
|
+
protected ruleName: string
|
|
481
|
+
protected context: LintContext
|
|
482
|
+
|
|
483
|
+
constructor(ruleName: string, context?: Partial<LintContext>) {
|
|
484
|
+
this.ruleName = ruleName
|
|
485
|
+
this.context = { ...DEFAULT_LINT_CONTEXT, ...context }
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Helper method to create a lint offense for source rules
|
|
490
|
+
*/
|
|
491
|
+
protected createOffense(message: string, location: Location, severity: LintSeverity = "error"): LintOffense {
|
|
492
|
+
return {
|
|
493
|
+
rule: this.ruleName as any, // Type assertion for compatibility
|
|
494
|
+
code: this.ruleName,
|
|
495
|
+
source: "Herb Linter",
|
|
496
|
+
message,
|
|
497
|
+
location,
|
|
498
|
+
severity,
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Helper method to add an offense to the offenses array
|
|
504
|
+
*/
|
|
505
|
+
protected addOffense(message: string, location: Location, severity: LintSeverity = "error"): void {
|
|
506
|
+
this.offenses.push(this.createOffense(message, location, severity))
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Main entry point for source rule visitors
|
|
511
|
+
* @param source - The raw source code
|
|
512
|
+
*/
|
|
513
|
+
visit(source: string): void {
|
|
514
|
+
this.visitSource(source)
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Visit the source code directly
|
|
519
|
+
* Override this method to implement source-level checks
|
|
520
|
+
*/
|
|
521
|
+
protected abstract visitSource(source: string): void
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Helper method to create a location for a specific position in the source
|
|
525
|
+
*/
|
|
526
|
+
protected createLocationAt(source: string, position: number): Location {
|
|
527
|
+
const beforePosition = source.substring(0, position)
|
|
528
|
+
const lines = beforePosition.split('\n')
|
|
529
|
+
const line = lines.length
|
|
530
|
+
const column = lines[lines.length - 1].length + 1
|
|
531
|
+
|
|
532
|
+
const start = new Position(line, column)
|
|
533
|
+
const end = new Position(line, column)
|
|
534
|
+
|
|
535
|
+
return new Location(start, end)
|
|
536
|
+
}
|
|
537
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseRuleVisitor, SVG_CAMEL_CASE_ELEMENTS, SVG_LOWERCASE_TO_CAMELCASE } from "./rule-utils.js"
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { ParserRule } from "../types.js"
|
|
4
|
+
import type { LintOffense, LintContext } from "../types.js"
|
|
4
5
|
import type { HTMLElementNode, HTMLOpenTagNode, HTMLCloseTagNode, HTMLSelfCloseTagNode, Node } from "@herb-tools/core"
|
|
5
6
|
|
|
6
7
|
class SVGTagNameCapitalizationVisitor extends BaseRuleVisitor {
|
|
@@ -62,11 +63,11 @@ class SVGTagNameCapitalizationVisitor extends BaseRuleVisitor {
|
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
export class SVGTagNameCapitalizationRule
|
|
66
|
+
export class SVGTagNameCapitalizationRule extends ParserRule {
|
|
66
67
|
name = "svg-tag-name-capitalization"
|
|
67
68
|
|
|
68
|
-
check(node: Node): LintOffense[] {
|
|
69
|
-
const visitor = new SVGTagNameCapitalizationVisitor(this.name)
|
|
69
|
+
check(node: Node, context?: Partial<LintContext>): LintOffense[] {
|
|
70
|
+
const visitor = new SVGTagNameCapitalizationVisitor(this.name, context)
|
|
70
71
|
visitor.visit(node)
|
|
71
72
|
return visitor.offenses
|
|
72
73
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Node, Diagnostic } from "@herb-tools/core"
|
|
1
|
+
import { Node, Diagnostic, LexResult } from "@herb-tools/core"
|
|
2
2
|
import type { defaultRules } from "./default-rules.js"
|
|
3
3
|
|
|
4
4
|
export type LintSeverity = "error" | "warning"
|
|
@@ -20,13 +20,67 @@ export interface LintResult {
|
|
|
20
20
|
warnings: number
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
export abstract class ParserRule {
|
|
24
|
+
static type = "parser" as const
|
|
25
|
+
abstract name: string
|
|
26
|
+
abstract check(node: Node, context?: Partial<LintContext>): LintOffense[]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export abstract class LexerRule {
|
|
30
|
+
static type = "lexer" as const
|
|
31
|
+
abstract name: string
|
|
32
|
+
abstract check(lexResult: LexResult, context?: Partial<LintContext>): LintOffense[]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface LexerRuleConstructor {
|
|
36
|
+
type: "lexer"
|
|
37
|
+
new (): LexerRule
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Complete lint context with all properties defined.
|
|
42
|
+
* Use Partial<LintContext> when passing context to rules.
|
|
43
|
+
*/
|
|
44
|
+
export interface LintContext {
|
|
45
|
+
fileName: string | undefined
|
|
26
46
|
}
|
|
27
47
|
|
|
28
48
|
/**
|
|
29
|
-
*
|
|
49
|
+
* Default context object with all keys defined but set to undefined
|
|
50
|
+
*/
|
|
51
|
+
export const DEFAULT_LINT_CONTEXT: LintContext = {
|
|
52
|
+
fileName: undefined
|
|
53
|
+
} as const
|
|
54
|
+
|
|
55
|
+
export abstract class SourceRule {
|
|
56
|
+
static type = "source" as const
|
|
57
|
+
abstract name: string
|
|
58
|
+
abstract check(source: string, context?: Partial<LintContext>): LintOffense[]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface SourceRuleConstructor {
|
|
62
|
+
type: "source"
|
|
63
|
+
new (): SourceRule
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Type representing a parser/AST rule class constructor.
|
|
30
68
|
* The Linter accepts rule classes rather than instances for better performance and memory usage.
|
|
69
|
+
* Parser rules are the default and don't require static properties.
|
|
70
|
+
*/
|
|
71
|
+
export type ParserRuleClass = (new () => ParserRule) & {
|
|
72
|
+
type?: "parser"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type LexerRuleClass = LexerRuleConstructor
|
|
76
|
+
export type SourceRuleClass = SourceRuleConstructor
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Union type for any rule instance (Parser/AST, Lexer, or Source)
|
|
80
|
+
*/
|
|
81
|
+
export type Rule = ParserRule | LexerRule | SourceRule
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Union type for any rule class (Parser/AST, Lexer, or Source)
|
|
31
85
|
*/
|
|
32
|
-
export type RuleClass =
|
|
86
|
+
export type RuleClass = ParserRuleClass | LexerRuleClass | SourceRuleClass
|