@herb-tools/linter 0.4.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 +34 -0
- package/bin/herb-lint +3 -0
- package/dist/herb-lint.js +16505 -0
- package/dist/herb-lint.js.map +1 -0
- package/dist/index.cjs +834 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +820 -0
- package/dist/index.js.map +1 -0
- package/dist/package.json +49 -0
- package/dist/src/cli/argument-parser.js +96 -0
- package/dist/src/cli/argument-parser.js.map +1 -0
- package/dist/src/cli/file-processor.js +58 -0
- package/dist/src/cli/file-processor.js.map +1 -0
- package/dist/src/cli/formatters/base-formatter.js +3 -0
- package/dist/src/cli/formatters/base-formatter.js.map +1 -0
- package/dist/src/cli/formatters/detailed-formatter.js +62 -0
- package/dist/src/cli/formatters/detailed-formatter.js.map +1 -0
- package/dist/src/cli/formatters/index.js +4 -0
- package/dist/src/cli/formatters/index.js.map +1 -0
- package/dist/src/cli/formatters/simple-formatter.js +31 -0
- package/dist/src/cli/formatters/simple-formatter.js.map +1 -0
- package/dist/src/cli/index.js +5 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/summary-reporter.js +96 -0
- package/dist/src/cli/summary-reporter.js.map +1 -0
- package/dist/src/cli.js +50 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/default-rules.js +31 -0
- package/dist/src/default-rules.js.map +1 -0
- package/dist/src/herb-lint.js +5 -0
- package/dist/src/herb-lint.js.map +1 -0
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/linter.js +39 -0
- package/dist/src/linter.js.map +1 -0
- package/dist/src/rules/erb-no-empty-tags.js +23 -0
- package/dist/src/rules/erb-no-empty-tags.js.map +1 -0
- package/dist/src/rules/erb-no-output-control-flow.js +47 -0
- package/dist/src/rules/erb-no-output-control-flow.js.map +1 -0
- package/dist/src/rules/erb-require-whitespace-inside-tags.js +43 -0
- package/dist/src/rules/erb-require-whitespace-inside-tags.js.map +1 -0
- package/dist/src/rules/html-anchor-require-href.js +25 -0
- package/dist/src/rules/html-anchor-require-href.js.map +1 -0
- package/dist/src/rules/html-aria-role-heading-requires-level.js +26 -0
- package/dist/src/rules/html-aria-role-heading-requires-level.js.map +1 -0
- package/dist/src/rules/html-attribute-double-quotes.js +21 -0
- package/dist/src/rules/html-attribute-double-quotes.js.map +1 -0
- package/dist/src/rules/html-attribute-values-require-quotes.js +22 -0
- package/dist/src/rules/html-attribute-values-require-quotes.js.map +1 -0
- package/dist/src/rules/html-boolean-attributes-no-value.js +19 -0
- package/dist/src/rules/html-boolean-attributes-no-value.js.map +1 -0
- package/dist/src/rules/html-img-require-alt.js +29 -0
- package/dist/src/rules/html-img-require-alt.js.map +1 -0
- package/dist/src/rules/html-no-block-inside-inline.js +59 -0
- package/dist/src/rules/html-no-block-inside-inline.js.map +1 -0
- package/dist/src/rules/html-no-duplicate-attributes.js +43 -0
- package/dist/src/rules/html-no-duplicate-attributes.js.map +1 -0
- package/dist/src/rules/html-no-empty-headings.js +148 -0
- package/dist/src/rules/html-no-empty-headings.js.map +1 -0
- package/dist/src/rules/html-no-nested-links.js +45 -0
- package/dist/src/rules/html-no-nested-links.js.map +1 -0
- package/dist/src/rules/html-tag-name-lowercase.js +39 -0
- package/dist/src/rules/html-tag-name-lowercase.js.map +1 -0
- package/dist/src/rules/index.js +13 -0
- package/dist/src/rules/index.js.map +1 -0
- package/dist/src/rules/rule-utils.js +198 -0
- package/dist/src/rules/rule-utils.js.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/cli/argument-parser.d.ts +14 -0
- package/dist/types/cli/file-processor.d.ts +21 -0
- package/dist/types/cli/formatters/base-formatter.d.ts +6 -0
- package/dist/types/cli/formatters/detailed-formatter.d.ts +13 -0
- package/dist/types/cli/formatters/index.d.ts +3 -0
- package/dist/types/cli/formatters/simple-formatter.d.ts +7 -0
- package/dist/types/cli/summary-reporter.d.ts +22 -0
- package/dist/types/cli.d.ts +6 -0
- package/dist/types/default-rules.d.ts +2 -0
- package/dist/types/herb-lint.d.ts +2 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/linter.d.ts +18 -0
- package/dist/types/rules/erb-no-empty-tags.d.ts +6 -0
- package/dist/types/rules/erb-no-output-control-flow.d.ts +6 -0
- package/dist/types/rules/erb-require-whitespace-inside-tags.d.ts +6 -0
- package/dist/types/rules/html-anchor-require-href.d.ts +6 -0
- package/dist/types/rules/html-aria-role-heading-requires-level.d.ts +6 -0
- package/dist/types/rules/html-attribute-double-quotes.d.ts +6 -0
- package/dist/types/rules/html-attribute-values-require-quotes.d.ts +6 -0
- package/dist/types/rules/html-boolean-attributes-no-value.d.ts +6 -0
- package/dist/types/rules/html-img-require-alt.d.ts +6 -0
- package/dist/types/rules/html-no-block-inside-inline.d.ts +6 -0
- package/dist/types/rules/html-no-duplicate-attributes.d.ts +6 -0
- package/dist/types/rules/html-no-empty-headings.d.ts +6 -0
- package/dist/types/rules/html-no-nested-links.d.ts +6 -0
- package/dist/types/rules/html-tag-name-lowercase.d.ts +6 -0
- package/dist/types/rules/index.d.ts +12 -0
- package/dist/types/rules/rule-utils.d.ts +89 -0
- package/dist/types/src/cli/argument-parser.d.ts +14 -0
- package/dist/types/src/cli/file-processor.d.ts +21 -0
- package/dist/types/src/cli/formatters/base-formatter.d.ts +6 -0
- package/dist/types/src/cli/formatters/detailed-formatter.d.ts +13 -0
- package/dist/types/src/cli/formatters/index.d.ts +3 -0
- package/dist/types/src/cli/formatters/simple-formatter.d.ts +7 -0
- package/dist/types/src/cli/index.d.ts +4 -0
- package/dist/types/src/cli/summary-reporter.d.ts +22 -0
- package/dist/types/src/cli.d.ts +6 -0
- package/dist/types/src/default-rules.d.ts +2 -0
- package/dist/types/src/herb-lint.d.ts +2 -0
- package/dist/types/src/index.d.ts +3 -0
- package/dist/types/src/linter.d.ts +18 -0
- package/dist/types/src/rules/erb-no-empty-tags.d.ts +6 -0
- package/dist/types/src/rules/erb-no-output-control-flow.d.ts +6 -0
- package/dist/types/src/rules/erb-require-whitespace-inside-tags.d.ts +6 -0
- package/dist/types/src/rules/html-anchor-require-href.d.ts +6 -0
- package/dist/types/src/rules/html-aria-role-heading-requires-level.d.ts +6 -0
- package/dist/types/src/rules/html-attribute-double-quotes.d.ts +6 -0
- package/dist/types/src/rules/html-attribute-values-require-quotes.d.ts +6 -0
- package/dist/types/src/rules/html-boolean-attributes-no-value.d.ts +6 -0
- package/dist/types/src/rules/html-img-require-alt.d.ts +6 -0
- package/dist/types/src/rules/html-no-block-inside-inline.d.ts +6 -0
- package/dist/types/src/rules/html-no-duplicate-attributes.d.ts +6 -0
- package/dist/types/src/rules/html-no-empty-headings.d.ts +6 -0
- package/dist/types/src/rules/html-no-nested-links.d.ts +6 -0
- package/dist/types/src/rules/html-tag-name-lowercase.d.ts +6 -0
- package/dist/types/src/rules/index.d.ts +12 -0
- package/dist/types/src/rules/rule-utils.d.ts +89 -0
- package/dist/types/src/types.d.ts +26 -0
- package/dist/types/types.d.ts +26 -0
- package/docs/rules/README.md +39 -0
- package/docs/rules/erb-no-empty-tags.md +38 -0
- package/docs/rules/erb-no-output-control-flow.md +45 -0
- package/docs/rules/erb-require-whitespace-inside-tags.md +43 -0
- package/docs/rules/html-anchor-require-href.md +32 -0
- package/docs/rules/html-aria-role-heading-requires-level.md +34 -0
- package/docs/rules/html-attribute-double-quotes.md +43 -0
- package/docs/rules/html-attribute-values-require-quotes.md +43 -0
- package/docs/rules/html-boolean-attributes-no-value.md +39 -0
- package/docs/rules/html-img-require-alt.md +44 -0
- package/docs/rules/html-no-block-inside-inline.md +66 -0
- package/docs/rules/html-no-duplicate-attributes.md +35 -0
- package/docs/rules/html-no-empty-headings.md +78 -0
- package/docs/rules/html-no-nested-links.md +44 -0
- package/docs/rules/html-tag-name-lowercase.md +44 -0
- package/package.json +49 -0
- package/src/cli/argument-parser.ts +125 -0
- package/src/cli/file-processor.ts +86 -0
- package/src/cli/formatters/base-formatter.ts +11 -0
- package/src/cli/formatters/detailed-formatter.ts +74 -0
- package/src/cli/formatters/index.ts +3 -0
- package/src/cli/formatters/simple-formatter.ts +40 -0
- package/src/cli/index.ts +4 -0
- package/src/cli/summary-reporter.ts +127 -0
- package/src/cli.ts +60 -0
- package/src/default-rules.ts +33 -0
- package/src/herb-lint.ts +6 -0
- package/src/index.ts +3 -0
- package/src/linter.ts +50 -0
- package/src/rules/erb-no-empty-tags.ts +34 -0
- package/src/rules/erb-no-output-control-flow.ts +61 -0
- package/src/rules/erb-require-whitespace-inside-tags.ts +61 -0
- package/src/rules/html-anchor-require-href.ts +39 -0
- package/src/rules/html-aria-role-heading-requires-level.ts +44 -0
- package/src/rules/html-attribute-double-quotes.ts +28 -0
- package/src/rules/html-attribute-values-require-quotes.ts +30 -0
- package/src/rules/html-boolean-attributes-no-value.ts +27 -0
- package/src/rules/html-img-require-alt.ts +42 -0
- package/src/rules/html-no-block-inside-inline.ts +84 -0
- package/src/rules/html-no-duplicate-attributes.ts +59 -0
- package/src/rules/html-no-empty-headings.ts +185 -0
- package/src/rules/html-no-nested-links.ts +65 -0
- package/src/rules/html-tag-name-lowercase.ts +50 -0
- package/src/rules/index.ts +12 -0
- package/src/rules/rule-utils.ts +257 -0
- package/src/types.ts +32 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { BaseRuleVisitor, getTagName } from "./rule-utils.js";
|
|
2
|
+
class NestedLinkVisitor extends BaseRuleVisitor {
|
|
3
|
+
linkStack = [];
|
|
4
|
+
checkNestedLink(openTag) {
|
|
5
|
+
if (this.linkStack.length > 0) {
|
|
6
|
+
this.addOffense("Nested `<a>` elements are not allowed. Links cannot contain other links.", openTag.tag_name.location, "error");
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
visitHTMLElementNode(node) {
|
|
12
|
+
if (!node.open_tag || node.open_tag.type !== "AST_HTML_OPEN_TAG_NODE") {
|
|
13
|
+
super.visitHTMLElementNode(node);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const openTag = node.open_tag;
|
|
17
|
+
const tagName = getTagName(openTag);
|
|
18
|
+
if (tagName !== "a") {
|
|
19
|
+
super.visitHTMLElementNode(node);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
// If we're already inside a link, this is a nested link
|
|
23
|
+
this.checkNestedLink(openTag);
|
|
24
|
+
this.linkStack.push(openTag);
|
|
25
|
+
super.visitHTMLElementNode(node);
|
|
26
|
+
this.linkStack.pop();
|
|
27
|
+
}
|
|
28
|
+
// Handle self-closing <a> tags (though they're not valid HTML, they might exist)
|
|
29
|
+
visitHTMLOpenTagNode(node) {
|
|
30
|
+
const tagName = getTagName(node);
|
|
31
|
+
if (tagName === "a" && node.is_void) {
|
|
32
|
+
this.checkNestedLink(node);
|
|
33
|
+
}
|
|
34
|
+
super.visitHTMLOpenTagNode(node);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export class HTMLNoNestedLinksRule {
|
|
38
|
+
name = "html-no-nested-links";
|
|
39
|
+
check(node) {
|
|
40
|
+
const visitor = new NestedLinkVisitor(this.name);
|
|
41
|
+
visitor.visit(node);
|
|
42
|
+
return visitor.offenses;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=html-no-nested-links.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-no-nested-links.js","sourceRoot":"","sources":["../../../src/rules/html-no-nested-links.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAK7D,MAAM,iBAAkB,SAAQ,eAAe;IACrC,SAAS,GAAsB,EAAE,CAAA;IAEjC,eAAe,CAAC,OAAwB;QAC9C,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,IAAI,CAAC,UAAU,CACb,0EAA0E,EAC1E,OAAO,CAAC,QAAS,CAAC,QAAQ,EAC1B,OAAO,CACR,CAAA;YAED,OAAO,IAAI,CAAA;QACb,CAAC;QAED,OAAO,KAAK,CAAA;IACd,CAAC;IAED,oBAAoB,CAAC,IAAqB;QACxC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,wBAAwB,EAAE,CAAC;YACtE,KAAK,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAA;YAChC,OAAM;QACR,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,QAA2B,CAAA;QAChD,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAA;QAEnC,IAAI,OAAO,KAAK,GAAG,EAAE,CAAC;YACpB,KAAK,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAA;YAChC,OAAM;QACR,CAAC;QAED,wDAAwD;QACxD,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAA;QAE7B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC5B,KAAK,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAA;QAChC,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,CAAA;IACtB,CAAC;IAED,iFAAiF;IACjF,oBAAoB,CAAC,IAAqB;QACxC,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;QAEhC,IAAI,OAAO,KAAK,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACpC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;QAC5B,CAAC;QAED,KAAK,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAA;IAClC,CAAC;CACF;AAED,MAAM,OAAO,qBAAqB;IAChC,IAAI,GAAG,sBAAsB,CAAA;IAE7B,KAAK,CAAC,IAAU;QACd,MAAM,OAAO,GAAG,IAAI,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAChD,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACnB,OAAO,OAAO,CAAC,QAAQ,CAAA;IACzB,CAAC;CACF"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { BaseRuleVisitor } from "./rule-utils.js";
|
|
2
|
+
class TagNameLowercaseVisitor extends BaseRuleVisitor {
|
|
3
|
+
visitHTMLOpenTagNode(node) {
|
|
4
|
+
this.checkTagName(node);
|
|
5
|
+
this.visitChildNodes(node);
|
|
6
|
+
}
|
|
7
|
+
visitHTMLCloseTagNode(node) {
|
|
8
|
+
this.checkTagName(node);
|
|
9
|
+
this.visitChildNodes(node);
|
|
10
|
+
}
|
|
11
|
+
visitHTMLSelfCloseTagNode(node) {
|
|
12
|
+
this.checkTagName(node);
|
|
13
|
+
this.visitChildNodes(node);
|
|
14
|
+
}
|
|
15
|
+
checkTagName(node) {
|
|
16
|
+
const tagName = node.tag_name?.value;
|
|
17
|
+
if (!tagName)
|
|
18
|
+
return;
|
|
19
|
+
if (tagName !== tagName.toLowerCase()) {
|
|
20
|
+
let type = node.type;
|
|
21
|
+
if (node.type == "AST_HTML_OPEN_TAG_NODE")
|
|
22
|
+
type = "Opening";
|
|
23
|
+
if (node.type == "AST_HTML_CLOSE_TAG_NODE")
|
|
24
|
+
type = "Closing";
|
|
25
|
+
if (node.type == "AST_HTML_SELF_CLOSE_TAG_NODE")
|
|
26
|
+
type = "Self-closing";
|
|
27
|
+
this.addOffense(`${type} tag name \`${tagName}\` should be lowercase. Use \`${tagName.toLowerCase()}\` instead.`, node.tag_name.location, "error");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export class HTMLTagNameLowercaseRule {
|
|
32
|
+
name = "html-tag-name-lowercase";
|
|
33
|
+
check(node) {
|
|
34
|
+
const visitor = new TagNameLowercaseVisitor(this.name);
|
|
35
|
+
visitor.visit(node);
|
|
36
|
+
return visitor.offenses;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=html-tag-name-lowercase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-tag-name-lowercase.js","sourceRoot":"","sources":["../../../src/rules/html-tag-name-lowercase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAKjD,MAAM,uBAAwB,SAAQ,eAAe;IACnD,oBAAoB,CAAC,IAAqB;QACxC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;QACvB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;IAC5B,CAAC;IAED,qBAAqB,CAAC,IAAsB;QAC1C,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;QACvB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;IAC5B,CAAC;IAED,yBAAyB,CAAC,IAA0B;QAClD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAA;QACvB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAA;IAC5B,CAAC;IAEO,YAAY,CAAC,IAA+D;QAClF,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAA;QACpC,IAAI,CAAC,OAAO;YAAE,OAAM;QAEpB,IAAI,OAAO,KAAK,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;YACtC,IAAI,IAAI,GAAW,IAAI,CAAC,IAAI,CAAA;YAE5B,IAAI,IAAI,CAAC,IAAI,IAAI,wBAAwB;gBAAE,IAAI,GAAG,SAAS,CAAA;YAC3D,IAAI,IAAI,CAAC,IAAI,IAAI,yBAAyB;gBAAE,IAAI,GAAG,SAAS,CAAA;YAC5D,IAAI,IAAI,CAAC,IAAI,IAAI,8BAA8B;gBAAE,IAAI,GAAG,cAAc,CAAA;YAEtE,IAAI,CAAC,UAAU,CACb,GAAG,IAAI,eAAe,OAAO,iCAAiC,OAAO,CAAC,WAAW,EAAE,aAAa,EAChG,IAAI,CAAC,QAAS,CAAC,QAAQ,EACvB,OAAO,CACR,CAAA;QACH,CAAC;IACH,CAAC;CACF;AAED,MAAM,OAAO,wBAAwB;IACnC,IAAI,GAAG,yBAAyB,CAAA;IAEhC,KAAK,CAAC,IAAU;QACd,MAAM,OAAO,GAAG,IAAI,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACtD,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACnB,OAAO,OAAO,CAAC,QAAQ,CAAA;IACzB,CAAC;CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from "./erb-no-empty-tags.js";
|
|
2
|
+
export * from "./erb-no-output-control-flow.js";
|
|
3
|
+
export * from "./html-anchor-require-href.js";
|
|
4
|
+
export * from "./html-attribute-double-quotes.js";
|
|
5
|
+
export * from "./html-attribute-values-require-quotes.js";
|
|
6
|
+
export * from "./html-boolean-attributes-no-value.js";
|
|
7
|
+
export * from "./html-img-require-alt.js";
|
|
8
|
+
export * from "./html-no-block-inside-inline.js";
|
|
9
|
+
export * from "./html-no-duplicate-attributes.js";
|
|
10
|
+
export * from "./html-no-empty-headings.js";
|
|
11
|
+
export * from "./html-no-nested-links.js";
|
|
12
|
+
export * from "./html-tag-name-lowercase.js";
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rules/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAA;AACtC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,+BAA+B,CAAA;AAC7C,cAAc,mCAAmC,CAAA;AACjD,cAAc,2CAA2C,CAAA;AACzD,cAAc,uCAAuC,CAAA;AACrD,cAAc,2BAA2B,CAAA;AACzC,cAAc,kCAAkC,CAAA;AAChD,cAAc,mCAAmC,CAAA;AACjD,cAAc,6BAA6B,CAAA;AAC3C,cAAc,2BAA2B,CAAA;AACzC,cAAc,8BAA8B,CAAA"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { Visitor } from "@herb-tools/core";
|
|
2
|
+
/**
|
|
3
|
+
* Base visitor class that provides common functionality for rule visitors
|
|
4
|
+
*/
|
|
5
|
+
export class BaseRuleVisitor extends Visitor {
|
|
6
|
+
offenses = [];
|
|
7
|
+
ruleName;
|
|
8
|
+
constructor(ruleName) {
|
|
9
|
+
super();
|
|
10
|
+
this.ruleName = ruleName;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Helper method to create a lint offense
|
|
14
|
+
*/
|
|
15
|
+
createOffense(message, location, severity = "error") {
|
|
16
|
+
return {
|
|
17
|
+
rule: this.ruleName,
|
|
18
|
+
code: this.ruleName,
|
|
19
|
+
source: "Herb Linter",
|
|
20
|
+
message,
|
|
21
|
+
location,
|
|
22
|
+
severity,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Helper method to add an offense to the offenses array
|
|
27
|
+
*/
|
|
28
|
+
addOffense(message, location, severity = "error") {
|
|
29
|
+
this.offenses.push(this.createOffense(message, location, severity));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Gets attributes from either an HTMLOpenTagNode or HTMLSelfCloseTagNode
|
|
34
|
+
*/
|
|
35
|
+
export function getAttributes(node) {
|
|
36
|
+
return node.type === "AST_HTML_SELF_CLOSE_TAG_NODE"
|
|
37
|
+
? node.attributes
|
|
38
|
+
: node.children;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Gets the tag name from an HTML tag node (lowercased)
|
|
42
|
+
*/
|
|
43
|
+
export function getTagName(node) {
|
|
44
|
+
return node.tag_name?.value.toLowerCase() || null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Gets the attribute name from an HTMLAttributeNode (lowercased)
|
|
48
|
+
*/
|
|
49
|
+
export function getAttributeName(attributeNode) {
|
|
50
|
+
if (attributeNode.name?.type === "AST_HTML_ATTRIBUTE_NAME_NODE") {
|
|
51
|
+
const nameNode = attributeNode.name;
|
|
52
|
+
return nameNode.name?.value.toLowerCase() || null;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Gets the attribute value content from an HTMLAttributeValueNode
|
|
58
|
+
*/
|
|
59
|
+
export function getAttributeValue(attributeNode) {
|
|
60
|
+
if (attributeNode.value?.type === "AST_HTML_ATTRIBUTE_VALUE_NODE") {
|
|
61
|
+
const valueNode = attributeNode.value;
|
|
62
|
+
if (valueNode.children && valueNode.children.length > 0) {
|
|
63
|
+
return valueNode.children
|
|
64
|
+
.filter(child => child.type === "AST_LITERAL_NODE")
|
|
65
|
+
.map(child => child.content)
|
|
66
|
+
.join("");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Checks if an attribute has a value
|
|
73
|
+
*/
|
|
74
|
+
export function hasAttributeValue(attributeNode) {
|
|
75
|
+
return attributeNode.value?.type === "AST_HTML_ATTRIBUTE_VALUE_NODE";
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Gets the quote type used for an attribute value
|
|
79
|
+
*/
|
|
80
|
+
export function getAttributeValueQuoteType(attributeNode) {
|
|
81
|
+
if (attributeNode.value?.type === "AST_HTML_ATTRIBUTE_VALUE_NODE") {
|
|
82
|
+
const valueNode = attributeNode.value;
|
|
83
|
+
if (valueNode.quoted && valueNode.open_quote) {
|
|
84
|
+
return valueNode.open_quote.value === '"' ? "double" : "single";
|
|
85
|
+
}
|
|
86
|
+
return "none";
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Finds an attribute by name in a list of attributes
|
|
92
|
+
*/
|
|
93
|
+
export function findAttributeByName(attributes, attributeName) {
|
|
94
|
+
for (const child of attributes) {
|
|
95
|
+
if (child.type === "AST_HTML_ATTRIBUTE_NODE") {
|
|
96
|
+
const attributeNode = child;
|
|
97
|
+
const name = getAttributeName(attributeNode);
|
|
98
|
+
if (name === attributeName.toLowerCase()) {
|
|
99
|
+
return attributeNode;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Checks if a tag has a specific attribute
|
|
107
|
+
*/
|
|
108
|
+
export function hasAttribute(node, attributeName) {
|
|
109
|
+
const attributes = getAttributes(node);
|
|
110
|
+
return findAttributeByName(attributes, attributeName) !== null;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Common HTML element categorization
|
|
114
|
+
*/
|
|
115
|
+
export const HTML_INLINE_ELEMENTS = new Set([
|
|
116
|
+
"a", "abbr", "acronym", "b", "bdo", "big", "br", "button", "cite", "code",
|
|
117
|
+
"dfn", "em", "i", "img", "input", "kbd", "label", "map", "object", "output",
|
|
118
|
+
"q", "samp", "script", "select", "small", "span", "strong", "sub", "sup",
|
|
119
|
+
"textarea", "time", "tt", "var"
|
|
120
|
+
]);
|
|
121
|
+
export const HTML_BLOCK_ELEMENTS = new Set([
|
|
122
|
+
"address", "article", "aside", "blockquote", "canvas", "dd", "div", "dl",
|
|
123
|
+
"dt", "fieldset", "figcaption", "figure", "footer", "form", "h1", "h2",
|
|
124
|
+
"h3", "h4", "h5", "h6", "header", "hr", "li", "main", "nav", "noscript",
|
|
125
|
+
"ol", "p", "pre", "section", "table", "tfoot", "ul", "video"
|
|
126
|
+
]);
|
|
127
|
+
export const HTML_BOOLEAN_ATTRIBUTES = new Set([
|
|
128
|
+
"autofocus", "autoplay", "checked", "controls", "defer", "disabled", "hidden",
|
|
129
|
+
"loop", "multiple", "muted", "readonly", "required", "reversed", "selected",
|
|
130
|
+
"open", "default", "formnovalidate", "novalidate", "itemscope", "scoped",
|
|
131
|
+
"seamless", "allowfullscreen", "async", "compact", "declare", "nohref",
|
|
132
|
+
"noresize", "noshade", "nowrap", "sortable", "truespeed", "typemustmatch"
|
|
133
|
+
]);
|
|
134
|
+
export const HEADING_TAGS = new Set(["h1", "h2", "h3", "h4", "h5", "h6"]);
|
|
135
|
+
/**
|
|
136
|
+
* Checks if an element is inline
|
|
137
|
+
*/
|
|
138
|
+
export function isInlineElement(tagName) {
|
|
139
|
+
return HTML_INLINE_ELEMENTS.has(tagName.toLowerCase());
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Checks if an element is block-level
|
|
143
|
+
*/
|
|
144
|
+
export function isBlockElement(tagName) {
|
|
145
|
+
return HTML_BLOCK_ELEMENTS.has(tagName.toLowerCase());
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Checks if an attribute is a boolean attribute
|
|
149
|
+
*/
|
|
150
|
+
export function isBooleanAttribute(attributeName) {
|
|
151
|
+
return HTML_BOOLEAN_ATTRIBUTES.has(attributeName.toLowerCase());
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Abstract base class for rules that need to check individual attributes on HTML tags
|
|
155
|
+
* Eliminates duplication of visitHTMLOpenTagNode/visitHTMLSelfCloseTagNode patterns
|
|
156
|
+
* and attribute iteration logic. Provides simplified interface with extracted attribute info.
|
|
157
|
+
*/
|
|
158
|
+
export class AttributeVisitorMixin extends BaseRuleVisitor {
|
|
159
|
+
visitHTMLOpenTagNode(node) {
|
|
160
|
+
this.checkAttributesOnNode(node);
|
|
161
|
+
super.visitHTMLOpenTagNode(node);
|
|
162
|
+
}
|
|
163
|
+
visitHTMLSelfCloseTagNode(node) {
|
|
164
|
+
this.checkAttributesOnNode(node);
|
|
165
|
+
super.visitHTMLSelfCloseTagNode(node);
|
|
166
|
+
}
|
|
167
|
+
checkAttributesOnNode(node) {
|
|
168
|
+
forEachAttribute(node, (attributeNode) => {
|
|
169
|
+
const attributeName = getAttributeName(attributeNode);
|
|
170
|
+
const attributeValue = getAttributeValue(attributeNode);
|
|
171
|
+
if (attributeName) {
|
|
172
|
+
this.checkAttribute(attributeName, attributeValue, attributeNode, node);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Checks if an attribute value is quoted
|
|
179
|
+
*/
|
|
180
|
+
export function isAttributeValueQuoted(attributeNode) {
|
|
181
|
+
if (attributeNode.value?.type === "AST_HTML_ATTRIBUTE_VALUE_NODE") {
|
|
182
|
+
const valueNode = attributeNode.value;
|
|
183
|
+
return !!valueNode.quoted;
|
|
184
|
+
}
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Iterates over all attributes of a tag node, calling the callback for each attribute
|
|
189
|
+
*/
|
|
190
|
+
export function forEachAttribute(node, callback) {
|
|
191
|
+
const attributes = getAttributes(node);
|
|
192
|
+
for (const child of attributes) {
|
|
193
|
+
if (child.type === "AST_HTML_ATTRIBUTE_NODE") {
|
|
194
|
+
callback(child);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=rule-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-utils.js","sourceRoot":"","sources":["../../../src/rules/rule-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../src/cli.ts","../src/default-rules.ts","../src/herb-lint.ts","../src/index.ts","../src/linter.ts","../src/types.ts","../src/cli/argument-parser.ts","../src/cli/file-processor.ts","../src/cli/index.ts","../src/cli/summary-reporter.ts","../src/cli/formatters/base-formatter.ts","../src/cli/formatters/detailed-formatter.ts","../src/cli/formatters/index.ts","../src/cli/formatters/simple-formatter.ts","../src/rules/erb-no-empty-tags.ts","../src/rules/erb-no-output-control-flow.ts","../src/rules/erb-require-whitespace-inside-tags.ts","../src/rules/html-anchor-require-href.ts","../src/rules/html-aria-role-heading-requires-level.ts","../src/rules/html-attribute-double-quotes.ts","../src/rules/html-attribute-values-require-quotes.ts","../src/rules/html-boolean-attributes-no-value.ts","../src/rules/html-img-require-alt.ts","../src/rules/html-no-block-inside-inline.ts","../src/rules/html-no-duplicate-attributes.ts","../src/rules/html-no-empty-headings.ts","../src/rules/html-no-nested-links.ts","../src/rules/html-tag-name-lowercase.ts","../src/rules/index.ts","../src/rules/rule-utils.ts"],"version":"5.8.3"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ThemeInput } from "@herb-tools/highlighter";
|
|
2
|
+
export interface ParsedArguments {
|
|
3
|
+
pattern: string;
|
|
4
|
+
formatOption: 'simple' | 'detailed';
|
|
5
|
+
showTiming: boolean;
|
|
6
|
+
theme: ThemeInput;
|
|
7
|
+
wrapLines: boolean;
|
|
8
|
+
truncateLines: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare class ArgumentParser {
|
|
11
|
+
private readonly usage;
|
|
12
|
+
parse(argv: string[]): ParsedArguments;
|
|
13
|
+
private getFilePattern;
|
|
14
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Diagnostic } from "@herb-tools/core";
|
|
2
|
+
export interface ProcessedFile {
|
|
3
|
+
filename: string;
|
|
4
|
+
diagnostic: Diagnostic;
|
|
5
|
+
content: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ProcessingResult {
|
|
8
|
+
totalErrors: number;
|
|
9
|
+
totalWarnings: number;
|
|
10
|
+
filesWithIssues: number;
|
|
11
|
+
ruleCount: number;
|
|
12
|
+
allDiagnostics: ProcessedFile[];
|
|
13
|
+
ruleViolations: Map<string, {
|
|
14
|
+
count: number;
|
|
15
|
+
files: Set<string>;
|
|
16
|
+
}>;
|
|
17
|
+
}
|
|
18
|
+
export declare class FileProcessor {
|
|
19
|
+
private linter;
|
|
20
|
+
processFiles(files: string[]): Promise<ProcessingResult>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Diagnostic } from "@herb-tools/core";
|
|
2
|
+
import type { ProcessedFile } from "../file-processor.js";
|
|
3
|
+
export declare abstract class BaseFormatter {
|
|
4
|
+
abstract format(allDiagnostics: ProcessedFile[], isSingleFile?: boolean): Promise<void>;
|
|
5
|
+
abstract formatFile(filename: string, diagnostics: Diagnostic[]): void;
|
|
6
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ThemeInput } from "@herb-tools/highlighter";
|
|
2
|
+
import { BaseFormatter } from "./base-formatter.js";
|
|
3
|
+
import type { Diagnostic } from "@herb-tools/core";
|
|
4
|
+
import type { ProcessedFile } from "../file-processor.js";
|
|
5
|
+
export declare class DetailedFormatter extends BaseFormatter {
|
|
6
|
+
private highlighter;
|
|
7
|
+
private theme;
|
|
8
|
+
private wrapLines;
|
|
9
|
+
private truncateLines;
|
|
10
|
+
constructor(theme?: ThemeInput, wrapLines?: boolean, truncateLines?: boolean);
|
|
11
|
+
format(allDiagnostics: ProcessedFile[], isSingleFile?: boolean): Promise<void>;
|
|
12
|
+
formatFile(_filename: string, _diagnostics: Diagnostic[]): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { BaseFormatter } from "./base-formatter.js";
|
|
2
|
+
import type { Diagnostic } from "@herb-tools/core";
|
|
3
|
+
import type { ProcessedFile } from "../file-processor.js";
|
|
4
|
+
export declare class SimpleFormatter extends BaseFormatter {
|
|
5
|
+
format(allDiagnostics: ProcessedFile[]): Promise<void>;
|
|
6
|
+
formatFile(filename: string, diagnostics: Diagnostic[]): void;
|
|
7
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface SummaryData {
|
|
2
|
+
files: string[];
|
|
3
|
+
totalErrors: number;
|
|
4
|
+
totalWarnings: number;
|
|
5
|
+
filesWithViolations: number;
|
|
6
|
+
ruleCount: number;
|
|
7
|
+
startTime: number;
|
|
8
|
+
startDate: Date;
|
|
9
|
+
showTiming: boolean;
|
|
10
|
+
ruleViolations: Map<string, {
|
|
11
|
+
count: number;
|
|
12
|
+
files: Set<string>;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
export declare class SummaryReporter {
|
|
16
|
+
private pluralize;
|
|
17
|
+
displaySummary(data: SummaryData): void;
|
|
18
|
+
displayMostViolatedRules(ruleViolations: Map<string, {
|
|
19
|
+
count: number;
|
|
20
|
+
files: Set<string>;
|
|
21
|
+
}>, limit?: number): void;
|
|
22
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { RuleClass, LintResult } from "./types.js";
|
|
2
|
+
import type { DocumentNode } from "@herb-tools/core";
|
|
3
|
+
export declare class Linter {
|
|
4
|
+
private rules;
|
|
5
|
+
private offenses;
|
|
6
|
+
/**
|
|
7
|
+
* Creates a new Linter instance.
|
|
8
|
+
* @param rules - Array of rule classes (not instances) to use. If not provided, uses default rules.
|
|
9
|
+
*/
|
|
10
|
+
constructor(rules?: RuleClass[]);
|
|
11
|
+
/**
|
|
12
|
+
* Returns the default set of rule classes used by the linter.
|
|
13
|
+
* @returns Array of rule classes
|
|
14
|
+
*/
|
|
15
|
+
private getDefaultRules;
|
|
16
|
+
getRuleCount(): number;
|
|
17
|
+
lint(document: DocumentNode): LintResult;
|
|
18
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from "./erb-no-empty-tags.js";
|
|
2
|
+
export * from "./erb-no-output-control-flow.js";
|
|
3
|
+
export * from "./html-anchor-require-href.js";
|
|
4
|
+
export * from "./html-attribute-double-quotes.js";
|
|
5
|
+
export * from "./html-attribute-values-require-quotes.js";
|
|
6
|
+
export * from "./html-boolean-attributes-no-value.js";
|
|
7
|
+
export * from "./html-img-require-alt.js";
|
|
8
|
+
export * from "./html-no-block-inside-inline.js";
|
|
9
|
+
export * from "./html-no-duplicate-attributes.js";
|
|
10
|
+
export * from "./html-no-empty-headings.js";
|
|
11
|
+
export * from "./html-no-nested-links.js";
|
|
12
|
+
export * from "./html-tag-name-lowercase.js";
|