@herb-tools/linter 0.4.3 → 0.5.0
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 +216 -19
- package/dist/herb-lint.js +420 -205
- package/dist/herb-lint.js.map +1 -1
- package/dist/index.cjs +61 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +61 -43
- package/dist/index.js.map +1 -1
- package/dist/package.json +4 -4
- package/dist/src/cli/argument-parser.js +28 -18
- package/dist/src/cli/argument-parser.js.map +1 -1
- package/dist/src/cli/file-processor.js +19 -13
- package/dist/src/cli/file-processor.js.map +1 -1
- package/dist/src/cli/formatters/detailed-formatter.js +9 -9
- package/dist/src/cli/formatters/detailed-formatter.js.map +1 -1
- package/dist/src/cli/formatters/github-actions-formatter.js +50 -0
- package/dist/src/cli/formatters/github-actions-formatter.js.map +1 -0
- package/dist/src/cli/formatters/index.js +2 -0
- package/dist/src/cli/formatters/index.js.map +1 -1
- package/dist/src/cli/formatters/json-formatter.js +58 -0
- package/dist/src/cli/formatters/json-formatter.js.map +1 -0
- package/dist/src/cli/formatters/simple-formatter.js +15 -15
- package/dist/src/cli/formatters/simple-formatter.js.map +1 -1
- package/dist/src/cli/output-manager.js +120 -0
- package/dist/src/cli/output-manager.js.map +1 -0
- package/dist/src/cli/summary-reporter.js +22 -22
- package/dist/src/cli/summary-reporter.js.map +1 -1
- package/dist/src/cli.js +41 -26
- package/dist/src/cli.js.map +1 -1
- package/dist/src/default-rules.js +2 -0
- package/dist/src/default-rules.js.map +1 -1
- package/dist/src/linter.js +1 -1
- package/dist/src/linter.js.map +1 -1
- package/dist/src/rules/erb-no-empty-tags.js +2 -2
- package/dist/src/rules/erb-no-empty-tags.js.map +1 -1
- package/dist/src/rules/erb-no-output-control-flow.js +2 -2
- package/dist/src/rules/erb-no-output-control-flow.js.map +1 -1
- package/dist/src/rules/erb-prefer-image-tag-helper.js +2 -2
- package/dist/src/rules/erb-prefer-image-tag-helper.js.map +1 -1
- package/dist/src/rules/erb-require-whitespace-inside-tags.js +2 -2
- package/dist/src/rules/erb-require-whitespace-inside-tags.js.map +1 -1
- package/dist/src/rules/erb-requires-trailing-newline.js.map +1 -1
- package/dist/src/rules/html-anchor-require-href.js +2 -2
- package/dist/src/rules/html-anchor-require-href.js.map +1 -1
- package/dist/src/rules/html-aria-attribute-must-be-valid.js +2 -2
- 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 +2 -2
- package/dist/src/rules/html-aria-level-must-be-valid.js.map +1 -1
- package/dist/src/rules/html-aria-role-heading-requires-level.js +2 -2
- 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 +2 -2
- package/dist/src/rules/html-aria-role-must-be-valid.js.map +1 -1
- package/dist/src/rules/html-attribute-double-quotes.js +2 -2
- package/dist/src/rules/html-attribute-double-quotes.js.map +1 -1
- package/dist/src/rules/html-attribute-values-require-quotes.js +2 -2
- package/dist/src/rules/html-attribute-values-require-quotes.js.map +1 -1
- package/dist/src/rules/html-boolean-attributes-no-value.js +2 -2
- package/dist/src/rules/html-boolean-attributes-no-value.js.map +1 -1
- package/dist/src/rules/html-img-require-alt.js +2 -2
- package/dist/src/rules/html-img-require-alt.js.map +1 -1
- package/dist/src/rules/html-no-block-inside-inline.js +4 -4
- package/dist/src/rules/html-no-block-inside-inline.js.map +1 -1
- package/dist/src/rules/html-no-duplicate-attributes.js +2 -2
- package/dist/src/rules/html-no-duplicate-attributes.js.map +1 -1
- package/dist/src/rules/html-no-duplicate-ids.js +2 -2
- package/dist/src/rules/html-no-duplicate-ids.js.map +1 -1
- package/dist/src/rules/html-no-empty-headings.js +2 -2
- package/dist/src/rules/html-no-empty-headings.js.map +1 -1
- package/dist/src/rules/html-no-nested-links.js +2 -2
- package/dist/src/rules/html-no-nested-links.js.map +1 -1
- package/dist/src/rules/html-tag-name-lowercase.js +2 -2
- package/dist/src/rules/html-tag-name-lowercase.js.map +1 -1
- package/dist/src/rules/parser-no-errors.js +18 -0
- package/dist/src/rules/parser-no-errors.js.map +1 -0
- package/dist/src/rules/svg-tag-name-capitalization.js +2 -2
- package/dist/src/rules/svg-tag-name-capitalization.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/cli/argument-parser.d.ts +2 -1
- package/dist/types/cli/file-processor.d.ts +6 -5
- package/dist/types/cli/formatters/base-formatter.d.ts +2 -2
- package/dist/types/cli/formatters/detailed-formatter.d.ts +2 -2
- package/dist/types/cli/formatters/github-actions-formatter.d.ts +12 -0
- package/dist/types/cli/formatters/index.d.ts +2 -0
- package/dist/types/cli/formatters/json-formatter.d.ts +42 -0
- package/dist/types/cli/formatters/simple-formatter.d.ts +2 -2
- package/dist/types/cli/output-manager.d.ts +31 -0
- package/dist/types/cli/summary-reporter.d.ts +3 -3
- package/dist/types/cli.d.ts +3 -1
- package/dist/types/rules/erb-no-empty-tags.d.ts +2 -2
- package/dist/types/rules/erb-no-output-control-flow.d.ts +2 -2
- package/dist/types/rules/erb-prefer-image-tag-helper.d.ts +2 -2
- package/dist/types/rules/erb-require-whitespace-inside-tags.d.ts +2 -2
- package/dist/types/rules/html-anchor-require-href.d.ts +2 -2
- package/dist/types/rules/html-aria-attribute-must-be-valid.d.ts +2 -2
- package/dist/types/rules/html-aria-level-must-be-valid.d.ts +2 -2
- package/dist/types/rules/html-aria-role-heading-requires-level.d.ts +2 -2
- package/dist/types/rules/html-aria-role-must-be-valid.d.ts +2 -2
- package/dist/types/rules/html-attribute-double-quotes.d.ts +2 -2
- package/dist/types/rules/html-attribute-values-require-quotes.d.ts +2 -2
- package/dist/types/rules/html-boolean-attributes-no-value.d.ts +2 -2
- package/dist/types/rules/html-img-require-alt.d.ts +2 -2
- package/dist/types/rules/html-no-block-inside-inline.d.ts +2 -2
- package/dist/types/rules/html-no-duplicate-attributes.d.ts +2 -2
- package/dist/types/rules/html-no-duplicate-ids.d.ts +2 -2
- package/dist/types/rules/html-no-empty-headings.d.ts +2 -2
- package/dist/types/rules/html-no-nested-links.d.ts +2 -2
- package/dist/types/rules/html-tag-name-lowercase.d.ts +2 -2
- package/dist/types/rules/parser-no-errors.d.ts +8 -0
- package/dist/types/rules/svg-tag-name-capitalization.d.ts +2 -2
- package/dist/types/src/cli/argument-parser.d.ts +2 -1
- package/dist/types/src/cli/file-processor.d.ts +6 -5
- package/dist/types/src/cli/formatters/base-formatter.d.ts +2 -2
- package/dist/types/src/cli/formatters/detailed-formatter.d.ts +2 -2
- package/dist/types/src/cli/formatters/github-actions-formatter.d.ts +12 -0
- package/dist/types/src/cli/formatters/index.d.ts +2 -0
- package/dist/types/src/cli/formatters/json-formatter.d.ts +42 -0
- package/dist/types/src/cli/formatters/simple-formatter.d.ts +2 -2
- package/dist/types/src/cli/output-manager.d.ts +31 -0
- package/dist/types/src/cli/summary-reporter.d.ts +3 -3
- package/dist/types/src/cli.d.ts +3 -1
- package/dist/types/src/rules/erb-no-empty-tags.d.ts +2 -2
- package/dist/types/src/rules/erb-no-output-control-flow.d.ts +2 -2
- package/dist/types/src/rules/erb-prefer-image-tag-helper.d.ts +2 -2
- package/dist/types/src/rules/erb-require-whitespace-inside-tags.d.ts +2 -2
- package/dist/types/src/rules/html-anchor-require-href.d.ts +2 -2
- package/dist/types/src/rules/html-aria-attribute-must-be-valid.d.ts +2 -2
- package/dist/types/src/rules/html-aria-level-must-be-valid.d.ts +2 -2
- package/dist/types/src/rules/html-aria-role-heading-requires-level.d.ts +2 -2
- package/dist/types/src/rules/html-aria-role-must-be-valid.d.ts +2 -2
- package/dist/types/src/rules/html-attribute-double-quotes.d.ts +2 -2
- package/dist/types/src/rules/html-attribute-values-require-quotes.d.ts +2 -2
- package/dist/types/src/rules/html-boolean-attributes-no-value.d.ts +2 -2
- package/dist/types/src/rules/html-img-require-alt.d.ts +2 -2
- package/dist/types/src/rules/html-no-block-inside-inline.d.ts +2 -2
- package/dist/types/src/rules/html-no-duplicate-attributes.d.ts +2 -2
- package/dist/types/src/rules/html-no-duplicate-ids.d.ts +2 -2
- package/dist/types/src/rules/html-no-empty-headings.d.ts +2 -2
- package/dist/types/src/rules/html-no-nested-links.d.ts +2 -2
- package/dist/types/src/rules/html-tag-name-lowercase.d.ts +2 -2
- package/dist/types/src/rules/parser-no-errors.d.ts +8 -0
- package/dist/types/src/rules/svg-tag-name-capitalization.d.ts +2 -2
- package/dist/types/src/types.d.ts +3 -3
- package/dist/types/types.d.ts +3 -3
- package/docs/rules/README.md +1 -0
- package/docs/rules/parser-no-errors.md +84 -0
- package/package.json +4 -4
- package/src/cli/argument-parser.ts +33 -19
- package/src/cli/file-processor.ts +25 -17
- package/src/cli/formatters/base-formatter.ts +2 -2
- package/src/cli/formatters/detailed-formatter.ts +9 -9
- package/src/cli/formatters/github-actions-formatter.ts +70 -0
- package/src/cli/formatters/index.ts +2 -0
- package/src/cli/formatters/json-formatter.ts +107 -0
- package/src/cli/formatters/simple-formatter.ts +15 -15
- package/src/cli/output-manager.ts +143 -0
- package/src/cli/summary-reporter.ts +24 -24
- package/src/cli.ts +48 -31
- package/src/default-rules.ts +2 -0
- package/src/linter.ts +1 -1
- package/src/rules/erb-no-empty-tags.ts +3 -3
- package/src/rules/erb-no-output-control-flow.ts +3 -3
- package/src/rules/erb-prefer-image-tag-helper.ts +3 -3
- package/src/rules/erb-require-whitespace-inside-tags.ts +3 -3
- package/src/rules/erb-requires-trailing-newline.ts +2 -0
- package/src/rules/html-anchor-require-href.ts +3 -3
- package/src/rules/html-aria-attribute-must-be-valid.ts +3 -3
- package/src/rules/html-aria-level-must-be-valid.ts +3 -3
- package/src/rules/html-aria-role-heading-requires-level.ts +3 -3
- package/src/rules/html-aria-role-must-be-valid.ts +3 -3
- package/src/rules/html-attribute-double-quotes.ts +3 -3
- package/src/rules/html-attribute-values-require-quotes.ts +3 -3
- package/src/rules/html-boolean-attributes-no-value.ts +3 -3
- package/src/rules/html-img-require-alt.ts +3 -3
- package/src/rules/html-no-block-inside-inline.ts +5 -5
- package/src/rules/html-no-duplicate-attributes.ts +3 -3
- package/src/rules/html-no-duplicate-ids.ts +3 -3
- package/src/rules/html-no-empty-headings.ts +3 -3
- package/src/rules/html-no-nested-links.ts +3 -3
- package/src/rules/html-tag-name-lowercase.ts +3 -3
- package/src/rules/parser-no-errors.ts +25 -0
- package/src/rules/svg-tag-name-capitalization.ts +3 -3
- package/src/types.ts +3 -3
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { BaseFormatter } from "./base-formatter.js";
|
|
2
2
|
export { SimpleFormatter } from "./simple-formatter.js";
|
|
3
3
|
export { DetailedFormatter } from "./detailed-formatter.js";
|
|
4
|
+
export { JSONFormatter, type JSONOutput } from "./json-formatter.js";
|
|
5
|
+
export { GitHubActionsFormatter } from "./github-actions-formatter.js";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { BaseFormatter } from "./base-formatter.js";
|
|
2
|
+
import type { Diagnostic, SerializedDiagnostic } from "@herb-tools/core";
|
|
3
|
+
import type { ProcessedFile } from "../file-processor.js";
|
|
4
|
+
interface JSONOffense extends SerializedDiagnostic {
|
|
5
|
+
filename: string;
|
|
6
|
+
}
|
|
7
|
+
interface JSONSummary {
|
|
8
|
+
filesChecked: number;
|
|
9
|
+
filesWithOffenses: number;
|
|
10
|
+
totalErrors: number;
|
|
11
|
+
totalWarnings: number;
|
|
12
|
+
totalOffenses: number;
|
|
13
|
+
ruleCount: number;
|
|
14
|
+
}
|
|
15
|
+
interface JSONTiming {
|
|
16
|
+
startTime: string;
|
|
17
|
+
duration: number;
|
|
18
|
+
}
|
|
19
|
+
export interface JSONOutput {
|
|
20
|
+
offenses: JSONOffense[];
|
|
21
|
+
summary: JSONSummary | null;
|
|
22
|
+
timing: JSONTiming | null;
|
|
23
|
+
completed: boolean;
|
|
24
|
+
clean: boolean | null;
|
|
25
|
+
message: string | null;
|
|
26
|
+
}
|
|
27
|
+
interface JSONFormatOptions {
|
|
28
|
+
files: string[];
|
|
29
|
+
totalErrors: number;
|
|
30
|
+
totalWarnings: number;
|
|
31
|
+
filesWithOffenses: number;
|
|
32
|
+
ruleCount: number;
|
|
33
|
+
startTime: number;
|
|
34
|
+
startDate: Date;
|
|
35
|
+
showTiming: boolean;
|
|
36
|
+
}
|
|
37
|
+
export declare class JSONFormatter extends BaseFormatter {
|
|
38
|
+
format(allOffenses: ProcessedFile[]): Promise<void>;
|
|
39
|
+
formatWithSummary(allOffenses: ProcessedFile[], options: JSONFormatOptions): Promise<void>;
|
|
40
|
+
formatFile(_filename: string, _offenses: Diagnostic[]): void;
|
|
41
|
+
}
|
|
42
|
+
export {};
|
|
@@ -2,6 +2,6 @@ import { BaseFormatter } from "./base-formatter.js";
|
|
|
2
2
|
import type { Diagnostic } from "@herb-tools/core";
|
|
3
3
|
import type { ProcessedFile } from "../file-processor.js";
|
|
4
4
|
export declare class SimpleFormatter extends BaseFormatter {
|
|
5
|
-
format(
|
|
6
|
-
formatFile(filename: string,
|
|
5
|
+
format(allOffenses: ProcessedFile[]): Promise<void>;
|
|
6
|
+
formatFile(filename: string, offenses: Diagnostic[]): void;
|
|
7
7
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ThemeInput } from "@herb-tools/highlighter";
|
|
2
|
+
import type { FormatOption } from "./argument-parser.js";
|
|
3
|
+
import type { ProcessingResult } from "./file-processor.js";
|
|
4
|
+
interface OutputOptions {
|
|
5
|
+
formatOption: FormatOption;
|
|
6
|
+
theme: ThemeInput;
|
|
7
|
+
wrapLines: boolean;
|
|
8
|
+
truncateLines: boolean;
|
|
9
|
+
showTiming: boolean;
|
|
10
|
+
startTime: number;
|
|
11
|
+
startDate: Date;
|
|
12
|
+
}
|
|
13
|
+
interface LintResults extends ProcessingResult {
|
|
14
|
+
files: string[];
|
|
15
|
+
}
|
|
16
|
+
export declare class OutputManager {
|
|
17
|
+
private summaryReporter;
|
|
18
|
+
/**
|
|
19
|
+
* Output successful lint results
|
|
20
|
+
*/
|
|
21
|
+
outputResults(results: LintResults, options: OutputOptions): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Output informational message (like "no files found")
|
|
24
|
+
*/
|
|
25
|
+
outputInfo(message: string, options: OutputOptions): void;
|
|
26
|
+
/**
|
|
27
|
+
* Output error message
|
|
28
|
+
*/
|
|
29
|
+
outputError(message: string, options: OutputOptions): void;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
@@ -2,12 +2,12 @@ export interface SummaryData {
|
|
|
2
2
|
files: string[];
|
|
3
3
|
totalErrors: number;
|
|
4
4
|
totalWarnings: number;
|
|
5
|
-
|
|
5
|
+
filesWithOffenses: number;
|
|
6
6
|
ruleCount: number;
|
|
7
7
|
startTime: number;
|
|
8
8
|
startDate: Date;
|
|
9
9
|
showTiming: boolean;
|
|
10
|
-
|
|
10
|
+
ruleOffenses: Map<string, {
|
|
11
11
|
count: number;
|
|
12
12
|
files: Set<string>;
|
|
13
13
|
}>;
|
|
@@ -15,7 +15,7 @@ export interface SummaryData {
|
|
|
15
15
|
export declare class SummaryReporter {
|
|
16
16
|
private pluralize;
|
|
17
17
|
displaySummary(data: SummaryData): void;
|
|
18
|
-
displayMostViolatedRules(
|
|
18
|
+
displayMostViolatedRules(ruleOffenses: Map<string, {
|
|
19
19
|
count: number;
|
|
20
20
|
files: Set<string>;
|
|
21
21
|
}>, limit?: number): void;
|
package/dist/types/src/cli.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js";
|
|
2
2
|
import type { LintOffense, LintContext } from "../types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
4
4
|
export declare class ERBNoEmptyTagsRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
2
2
|
import { ParserRule } from "../types.js";
|
|
3
3
|
import type { LintOffense, LintContext } from "../types.js";
|
|
4
4
|
export declare class ERBNoOutputControlFlowRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js";
|
|
2
2
|
import type { LintOffense, LintContext } from "../types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
4
4
|
export declare class ERBPreferImageTagHelperRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
2
2
|
import { ParserRule } from "../types.js";
|
|
3
3
|
import type { LintOffense, LintContext } from "../types.js";
|
|
4
4
|
export declare class ERBRequireWhitespaceRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js";
|
|
2
2
|
import type { LintOffense, LintContext } from "../types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
4
4
|
export declare class HTMLAnchorRequireHrefRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js";
|
|
2
2
|
import type { LintOffense, LintContext } from "../types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
4
4
|
export declare class HTMLAriaAttributeMustBeValid extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js";
|
|
2
2
|
import type { LintOffense, LintContext } from "../types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
4
4
|
export declare class HTMLAriaLevelMustBeValidRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js";
|
|
2
2
|
import type { LintOffense, LintContext } from "../types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
4
4
|
export declare class HTMLAriaRoleHeadingRequiresLevelRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js";
|
|
2
2
|
import type { LintOffense, LintContext } from "../types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
4
4
|
export declare class HTMLAriaRoleMustBeValidRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js";
|
|
2
2
|
import type { LintOffense, LintContext } from "../types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
4
4
|
export declare class HTMLAttributeDoubleQuotesRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js";
|
|
2
2
|
import type { LintOffense, LintContext } from "../types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
4
4
|
export declare class HTMLAttributeValuesRequireQuotesRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js";
|
|
2
2
|
import type { LintOffense, LintContext } from "../types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
4
4
|
export declare class HTMLBooleanAttributesNoValueRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js";
|
|
2
2
|
import type { LintOffense, LintContext } from "../types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
4
4
|
export declare class HTMLImgRequireAltRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js";
|
|
2
2
|
import type { LintOffense, LintContext } from "../types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
4
4
|
export declare class HTMLNoBlockInsideInlineRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js";
|
|
2
2
|
import type { LintOffense, LintContext } from "../types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
4
4
|
export declare class HTMLNoDuplicateAttributesRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserRule } from "../types";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
3
3
|
import type { LintOffense, LintContext } from "../types";
|
|
4
4
|
export declare class HTMLNoDuplicateIdsRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js";
|
|
2
2
|
import type { LintOffense, LintContext } from "../types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
4
4
|
export declare class HTMLNoEmptyHeadingsRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js";
|
|
2
2
|
import type { LintOffense, LintContext } from "../types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
4
4
|
export declare class HTMLNoNestedLinksRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js";
|
|
2
2
|
import type { LintOffense, LintContext } from "../types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
4
4
|
export declare class HTMLTagNameLowercaseRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ParserRule } from "../types.js";
|
|
2
|
+
import type { LintOffense } from "../types.js";
|
|
3
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
4
|
+
export declare class ParserNoErrorsRule extends ParserRule {
|
|
5
|
+
name: string;
|
|
6
|
+
check(result: ParseResult): LintOffense[];
|
|
7
|
+
private herbErrorToLintOffense;
|
|
8
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js";
|
|
2
2
|
import type { LintOffense, LintContext } from "../types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ParseResult } from "@herb-tools/core";
|
|
4
4
|
export declare class SVGTagNameCapitalizationRule extends ParserRule {
|
|
5
5
|
name: string;
|
|
6
|
-
check(
|
|
6
|
+
check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
7
7
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Diagnostic, LexResult, ParseResult } from "@herb-tools/core";
|
|
2
2
|
import type { defaultRules } from "./default-rules.js";
|
|
3
|
-
export type LintSeverity = "error" | "warning";
|
|
3
|
+
export type LintSeverity = "error" | "warning" | "info" | "hint";
|
|
4
4
|
/**
|
|
5
5
|
* Automatically inferred union type of all available linter rule names.
|
|
6
6
|
* This type extracts the 'name' property from each rule class instance.
|
|
@@ -18,7 +18,7 @@ export interface LintResult {
|
|
|
18
18
|
export declare abstract class ParserRule {
|
|
19
19
|
static type: "parser";
|
|
20
20
|
abstract name: string;
|
|
21
|
-
abstract check(
|
|
21
|
+
abstract check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
22
22
|
}
|
|
23
23
|
export declare abstract class LexerRule {
|
|
24
24
|
static type: "lexer";
|
package/dist/types/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Diagnostic, LexResult, ParseResult } from "@herb-tools/core";
|
|
2
2
|
import type { defaultRules } from "./default-rules.js";
|
|
3
|
-
export type LintSeverity = "error" | "warning";
|
|
3
|
+
export type LintSeverity = "error" | "warning" | "info" | "hint";
|
|
4
4
|
/**
|
|
5
5
|
* Automatically inferred union type of all available linter rule names.
|
|
6
6
|
* This type extracts the 'name' property from each rule class instance.
|
|
@@ -18,7 +18,7 @@ export interface LintResult {
|
|
|
18
18
|
export declare abstract class ParserRule {
|
|
19
19
|
static type: "parser";
|
|
20
20
|
abstract name: string;
|
|
21
|
-
abstract check(
|
|
21
|
+
abstract check(result: ParseResult, context?: Partial<LintContext>): LintOffense[];
|
|
22
22
|
}
|
|
23
23
|
export declare abstract class LexerRule {
|
|
24
24
|
static type: "lexer";
|
package/docs/rules/README.md
CHANGED
|
@@ -23,6 +23,7 @@ This page contains documentation for all Herb Linter rules.
|
|
|
23
23
|
- [`html-no-duplicate-ids`](./html-no-duplicate-ids.md) - Prevents duplicate IDs within a document
|
|
24
24
|
- [`html-no-nested-links`](./html-no-nested-links.md) - Prevents nested anchor tags
|
|
25
25
|
- [`html-tag-name-lowercase`](./html-tag-name-lowercase.md) - Enforces lowercase tag names in HTML
|
|
26
|
+
- [`parser-no-errors`](./parser-no-errors.md) - Disallow parser errors in HTML+ERB documents
|
|
26
27
|
- [`svg-tag-name-capitalization`](./svg-tag-name-capitalization.md) - Enforces proper camelCase capitalization for SVG elements
|
|
27
28
|
|
|
28
29
|
## Contributing
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Linter Rule: Disallow parser errors in HTML+ERB documents
|
|
2
|
+
|
|
3
|
+
**Rule:** `parser-no-errors`
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
Report parser errors as linting offenses. This rule surfaces syntax errors, malformed HTML, and other parsing issues that prevent the document from being correctly parsed.
|
|
8
|
+
|
|
9
|
+
## Rationale
|
|
10
|
+
|
|
11
|
+
Parser errors indicate fundamental structural problems in HTML+ERB documents that can lead to unexpected rendering behavior, accessibility issues, and maintenance difficulties. These errors should be fixed before addressing other linting concerns as they represent invalid markup that browsers may interpret inconsistently.
|
|
12
|
+
|
|
13
|
+
By surfacing parser errors through the linter, developers can catch these critical issues when running lint checks directly, without needing to switch to the language server or other tools.
|
|
14
|
+
|
|
15
|
+
## Examples
|
|
16
|
+
|
|
17
|
+
### ✅ Good
|
|
18
|
+
|
|
19
|
+
```html
|
|
20
|
+
<h2>Welcome to our site</h2>
|
|
21
|
+
<p>This is a paragraph with proper structure.</p>
|
|
22
|
+
|
|
23
|
+
<div class="container">
|
|
24
|
+
<img src="image.jpg" alt="Description">
|
|
25
|
+
</div>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```erb
|
|
29
|
+
<h2><%= @page.title %></h2>
|
|
30
|
+
<p><%= @page.description %></p>
|
|
31
|
+
|
|
32
|
+
<% if user_signed_in? %>
|
|
33
|
+
<div class="user-section">
|
|
34
|
+
<%= current_user.name %>
|
|
35
|
+
</div>
|
|
36
|
+
<% end %>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 🚫 Bad
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<!-- Mismatched closing tag -->
|
|
43
|
+
<h2>Welcome to our site</h3>
|
|
44
|
+
|
|
45
|
+
<!-- Unclosed element -->
|
|
46
|
+
<div>
|
|
47
|
+
<p>This paragraph is never closed
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<!-- Missing opening tag -->
|
|
51
|
+
Some content
|
|
52
|
+
</div>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```erb
|
|
56
|
+
<!-- Invalid Ruby syntax in ERB -->
|
|
57
|
+
<%= 1 + %>
|
|
58
|
+
|
|
59
|
+
<!-- Mismatched quotes -->
|
|
60
|
+
<div class="container'>Content</div>
|
|
61
|
+
|
|
62
|
+
<!-- Void element with closing tag -->
|
|
63
|
+
<img src="image.jpg" alt="Description"></img>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Error Types
|
|
67
|
+
|
|
68
|
+
This rule reports various parser error types:
|
|
69
|
+
|
|
70
|
+
- **`UNCLOSED_ELEMENT_ERROR`**: Elements that are opened but never closed
|
|
71
|
+
- **`MISSING_CLOSING_TAG_ERROR`**: Opening tags without matching closing tags
|
|
72
|
+
- **`MISSING_OPENING_TAG_ERROR`**: Closing tags without matching opening tags
|
|
73
|
+
- **`TAG_NAMES_MISMATCH_ERROR`**: Opening and closing tags with different names
|
|
74
|
+
- **`QUOTES_MISMATCH_ERROR`**: Mismatched quotation marks in attributes
|
|
75
|
+
- **`VOID_ELEMENT_CLOSING_TAG_ERROR`**: Void elements (like `<img>`) with closing tags
|
|
76
|
+
- **`RUBY_PARSE_ERROR`**: Invalid Ruby syntax within ERB tags
|
|
77
|
+
- **`UNEXPECTED_TOKEN_ERROR`**: Unexpected tokens during parsing
|
|
78
|
+
- **`UNEXPECTED_ERROR`**: Other unexpected parsing issues
|
|
79
|
+
|
|
80
|
+
## References
|
|
81
|
+
|
|
82
|
+
* [HTML Living Standard - Parsing](https://html.spec.whatwg.org/multipage/parsing.html)
|
|
83
|
+
* [W3C HTML Validator](https://validator.w3.org/)
|
|
84
|
+
* [ERB Template Guide](https://guides.rubyonrails.org/layouts_and_rendering.html)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@herb-tools/linter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "HTML+ERB linter for validating HTML structure and enforcing best practices",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://herb-tools.dev",
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@herb-tools/core": "0.
|
|
37
|
-
"@herb-tools/highlighter": "0.
|
|
38
|
-
"@herb-tools/node-wasm": "0.
|
|
36
|
+
"@herb-tools/core": "0.5.0",
|
|
37
|
+
"@herb-tools/highlighter": "0.5.0",
|
|
38
|
+
"@herb-tools/node-wasm": "0.5.0",
|
|
39
39
|
"glob": "^11.0.3"
|
|
40
40
|
},
|
|
41
41
|
"files": [
|
|
@@ -11,9 +11,11 @@ import type { ThemeInput } from "@herb-tools/highlighter"
|
|
|
11
11
|
|
|
12
12
|
import { name, version } from "../../package.json"
|
|
13
13
|
|
|
14
|
+
export type FormatOption = "simple" | "detailed" | "json" | "github"
|
|
15
|
+
|
|
14
16
|
export interface ParsedArguments {
|
|
15
17
|
pattern: string
|
|
16
|
-
formatOption:
|
|
18
|
+
formatOption: FormatOption
|
|
17
19
|
showTiming: boolean
|
|
18
20
|
theme: ThemeInput
|
|
19
21
|
wrapLines: boolean
|
|
@@ -32,9 +34,11 @@ export class ArgumentParser {
|
|
|
32
34
|
Options:
|
|
33
35
|
-h, --help show help
|
|
34
36
|
-v, --version show version
|
|
35
|
-
--format output format (simple|detailed) [default: detailed]
|
|
37
|
+
--format output format (simple|detailed|json|github) [default: detailed]
|
|
36
38
|
--simple use simple output format (shortcut for --format simple)
|
|
37
|
-
--
|
|
39
|
+
--json use JSON output format (shortcut for --format json)
|
|
40
|
+
--github use GitHub Actions output format (shortcut for --format github)
|
|
41
|
+
--theme syntax highlighting theme (${THEME_NAMES.join("|")}) or path to custom theme file [default: ${DEFAULT_THEME}]
|
|
38
42
|
--no-color disable colored output
|
|
39
43
|
--no-timing hide timing information
|
|
40
44
|
--no-wrap-lines disable line wrapping
|
|
@@ -45,15 +49,17 @@ export class ArgumentParser {
|
|
|
45
49
|
const { values, positionals } = parseArgs({
|
|
46
50
|
args: argv.slice(2),
|
|
47
51
|
options: {
|
|
48
|
-
help: { type:
|
|
49
|
-
version: { type:
|
|
50
|
-
format: { type:
|
|
51
|
-
simple: { type:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
help: { type: "boolean", short: "h" },
|
|
53
|
+
version: { type: "boolean", short: "v" },
|
|
54
|
+
format: { type: "string" },
|
|
55
|
+
simple: { type: "boolean" },
|
|
56
|
+
json: { type: "boolean" },
|
|
57
|
+
github: { type: "boolean" },
|
|
58
|
+
theme: { type: "string" },
|
|
59
|
+
"no-color": { type: "boolean" },
|
|
60
|
+
"no-timing": { type: "boolean" },
|
|
61
|
+
"no-wrap-lines": { type: "boolean" },
|
|
62
|
+
"truncate-lines": { type: "boolean" }
|
|
57
63
|
},
|
|
58
64
|
allowPositionals: true
|
|
59
65
|
})
|
|
@@ -69,8 +75,8 @@ export class ArgumentParser {
|
|
|
69
75
|
process.exit(0)
|
|
70
76
|
}
|
|
71
77
|
|
|
72
|
-
let formatOption:
|
|
73
|
-
if (values.format && (values.format === "detailed" || values.format === "simple")) {
|
|
78
|
+
let formatOption: FormatOption = "detailed"
|
|
79
|
+
if (values.format && (values.format === "detailed" || values.format === "simple" || values.format === "json" || values.format === "github")) {
|
|
74
80
|
formatOption = values.format
|
|
75
81
|
}
|
|
76
82
|
|
|
@@ -78,21 +84,29 @@ export class ArgumentParser {
|
|
|
78
84
|
formatOption = "simple"
|
|
79
85
|
}
|
|
80
86
|
|
|
81
|
-
if (values
|
|
87
|
+
if (values.json) {
|
|
88
|
+
formatOption = "json"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (values.github) {
|
|
92
|
+
formatOption = "github"
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (values["no-color"]) {
|
|
82
96
|
process.env.NO_COLOR = "1"
|
|
83
97
|
}
|
|
84
98
|
|
|
85
|
-
const showTiming = !values[
|
|
99
|
+
const showTiming = !values["no-timing"]
|
|
86
100
|
|
|
87
|
-
let wrapLines = !values[
|
|
101
|
+
let wrapLines = !values["no-wrap-lines"]
|
|
88
102
|
let truncateLines = false
|
|
89
103
|
|
|
90
|
-
if (values[
|
|
104
|
+
if (values["truncate-lines"]) {
|
|
91
105
|
truncateLines = true
|
|
92
106
|
wrapLines = false
|
|
93
107
|
}
|
|
94
108
|
|
|
95
|
-
if (!values[
|
|
109
|
+
if (!values["no-wrap-lines"] && values["truncate-lines"]) {
|
|
96
110
|
console.error("Error: Line wrapping and --truncate-lines cannot be used together. Use --no-wrap-lines with --truncate-lines.")
|
|
97
111
|
process.exit(1)
|
|
98
112
|
}
|