@herb-tools/linter 0.7.4 → 0.8.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 +253 -13
- package/dist/herb-lint.js +26087 -3414
- package/dist/herb-lint.js.map +1 -1
- package/dist/index.cjs +5783 -1568
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5749 -1569
- package/dist/index.js.map +1 -1
- package/dist/loader.cjs +17010 -0
- package/dist/loader.cjs.map +1 -0
- package/dist/loader.js +16879 -0
- package/dist/loader.js.map +1 -0
- package/dist/package.json +13 -5
- package/dist/src/cli/argument-parser.js +42 -35
- package/dist/src/cli/argument-parser.js.map +1 -1
- package/dist/src/cli/file-processor.js +124 -23
- package/dist/src/cli/file-processor.js.map +1 -1
- package/dist/src/cli/formatters/detailed-formatter.js +18 -3
- package/dist/src/cli/formatters/detailed-formatter.js.map +1 -1
- package/dist/src/cli/formatters/github-actions-formatter.js +15 -1
- package/dist/src/cli/formatters/github-actions-formatter.js.map +1 -1
- package/dist/src/cli/formatters/json-formatter.js +3 -0
- package/dist/src/cli/formatters/json-formatter.js.map +1 -1
- package/dist/src/cli/formatters/simple-formatter.js +20 -7
- package/dist/src/cli/formatters/simple-formatter.js.map +1 -1
- package/dist/src/cli/output-manager.js +22 -3
- package/dist/src/cli/output-manager.js.map +1 -1
- package/dist/src/cli/summary-reporter.js +26 -3
- package/dist/src/cli/summary-reporter.js.map +1 -1
- package/dist/src/cli.js +109 -43
- package/dist/src/cli.js.map +1 -1
- package/dist/src/custom-rule-loader.js +139 -0
- package/dist/src/custom-rule-loader.js.map +1 -0
- package/dist/src/herb-disable-comment-utils.js +129 -0
- package/dist/src/herb-disable-comment-utils.js.map +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/linter.js +369 -34
- package/dist/src/linter.js.map +1 -1
- package/dist/src/loader.js +17 -0
- package/dist/src/loader.js.map +1 -0
- package/dist/src/rules/erb-comment-syntax.js +48 -0
- package/dist/src/rules/erb-comment-syntax.js.map +1 -0
- package/dist/src/rules/erb-no-case-node-children.js +52 -0
- package/dist/src/rules/erb-no-case-node-children.js.map +1 -0
- package/dist/src/rules/erb-no-empty-tags.js +7 -1
- package/dist/src/rules/erb-no-empty-tags.js.map +1 -1
- package/dist/src/rules/erb-no-extra-newline.js +65 -0
- package/dist/src/rules/erb-no-extra-newline.js.map +1 -0
- package/dist/src/rules/erb-no-extra-whitespace-inside-tags.js +95 -0
- package/dist/src/rules/erb-no-extra-whitespace-inside-tags.js.map +1 -0
- package/dist/src/rules/erb-no-output-control-flow.js +7 -1
- package/dist/src/rules/erb-no-output-control-flow.js.map +1 -1
- package/dist/src/rules/erb-no-silent-tag-in-attribute-name.js +7 -1
- package/dist/src/rules/erb-no-silent-tag-in-attribute-name.js.map +1 -1
- package/dist/src/rules/erb-prefer-image-tag-helper.js +7 -1
- package/dist/src/rules/erb-prefer-image-tag-helper.js.map +1 -1
- package/dist/src/rules/erb-require-trailing-newline.js +35 -0
- package/dist/src/rules/erb-require-trailing-newline.js.map +1 -0
- package/dist/src/rules/erb-require-whitespace-inside-tags.js +70 -20
- package/dist/src/rules/erb-require-whitespace-inside-tags.js.map +1 -1
- package/dist/src/rules/erb-right-trim.js +45 -0
- package/dist/src/rules/erb-right-trim.js.map +1 -0
- package/dist/src/rules/herb-disable-comment-base.js +51 -0
- package/dist/src/rules/herb-disable-comment-base.js.map +1 -0
- package/dist/src/rules/herb-disable-comment-malformed.js +51 -0
- package/dist/src/rules/herb-disable-comment-malformed.js.map +1 -0
- package/dist/src/rules/herb-disable-comment-missing-rules.js +29 -0
- package/dist/src/rules/herb-disable-comment-missing-rules.js.map +1 -0
- package/dist/src/rules/herb-disable-comment-no-duplicate-rules.js +32 -0
- package/dist/src/rules/herb-disable-comment-no-duplicate-rules.js.map +1 -0
- package/dist/src/rules/herb-disable-comment-no-redundant-all.js +31 -0
- package/dist/src/rules/herb-disable-comment-no-redundant-all.js.map +1 -0
- package/dist/src/rules/herb-disable-comment-unnecessary.js +65 -0
- package/dist/src/rules/herb-disable-comment-unnecessary.js.map +1 -0
- package/dist/src/rules/herb-disable-comment-valid-rule-name.js +44 -0
- package/dist/src/rules/herb-disable-comment-valid-rule-name.js.map +1 -0
- package/dist/src/rules/html-anchor-require-href.js +7 -1
- package/dist/src/rules/html-anchor-require-href.js.map +1 -1
- package/dist/src/rules/html-aria-attribute-must-be-valid.js +7 -1
- package/dist/src/rules/html-aria-attribute-must-be-valid.js.map +1 -1
- package/dist/src/rules/html-aria-label-is-well-formatted.js +9 -3
- package/dist/src/rules/html-aria-label-is-well-formatted.js.map +1 -1
- package/dist/src/rules/html-aria-level-must-be-valid.js +6 -0
- 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 +7 -1
- 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 +7 -1
- package/dist/src/rules/html-aria-role-must-be-valid.js.map +1 -1
- package/dist/src/rules/html-attribute-double-quotes.js +29 -2
- package/dist/src/rules/html-attribute-double-quotes.js.map +1 -1
- package/dist/src/rules/html-attribute-equals-spacing.js +18 -2
- package/dist/src/rules/html-attribute-equals-spacing.js.map +1 -1
- package/dist/src/rules/html-attribute-values-require-quotes.js +39 -3
- package/dist/src/rules/html-attribute-values-require-quotes.js.map +1 -1
- package/dist/src/rules/html-avoid-both-disabled-and-aria-disabled.js +7 -1
- package/dist/src/rules/html-avoid-both-disabled-and-aria-disabled.js.map +1 -1
- package/dist/src/rules/html-body-only-elements.js +46 -0
- package/dist/src/rules/html-body-only-elements.js.map +1 -0
- package/dist/src/rules/html-boolean-attributes-no-value.js +18 -1
- package/dist/src/rules/html-boolean-attributes-no-value.js.map +1 -1
- package/dist/src/rules/html-head-only-elements.js +51 -0
- package/dist/src/rules/html-head-only-elements.js.map +1 -0
- package/dist/src/rules/html-iframe-has-title.js +8 -2
- package/dist/src/rules/html-iframe-has-title.js.map +1 -1
- package/dist/src/rules/html-img-require-alt.js +7 -1
- package/dist/src/rules/html-img-require-alt.js.map +1 -1
- package/dist/src/rules/html-input-require-autocomplete.js +70 -0
- package/dist/src/rules/html-input-require-autocomplete.js.map +1 -0
- package/dist/src/rules/html-navigation-has-label.js +7 -1
- package/dist/src/rules/html-navigation-has-label.js.map +1 -1
- package/dist/src/rules/html-no-aria-hidden-on-focusable.js +7 -1
- package/dist/src/rules/html-no-aria-hidden-on-focusable.js.map +1 -1
- package/dist/src/rules/html-no-block-inside-inline.js +7 -1
- package/dist/src/rules/html-no-block-inside-inline.js.map +1 -1
- package/dist/src/rules/html-no-duplicate-attributes.js +7 -1
- package/dist/src/rules/html-no-duplicate-attributes.js.map +1 -1
- package/dist/src/rules/html-no-duplicate-ids.js +9 -3
- package/dist/src/rules/html-no-duplicate-ids.js.map +1 -1
- package/dist/src/rules/html-no-duplicate-meta-names.js +136 -0
- package/dist/src/rules/html-no-duplicate-meta-names.js.map +1 -0
- package/dist/src/rules/html-no-empty-attributes.js +45 -7
- package/dist/src/rules/html-no-empty-attributes.js.map +1 -1
- package/dist/src/rules/html-no-empty-headings.js +7 -6
- package/dist/src/rules/html-no-empty-headings.js.map +1 -1
- package/dist/src/rules/html-no-nested-links.js +7 -1
- package/dist/src/rules/html-no-nested-links.js.map +1 -1
- package/dist/src/rules/html-no-positive-tab-index.js +7 -1
- package/dist/src/rules/html-no-positive-tab-index.js.map +1 -1
- package/dist/src/rules/html-no-self-closing.js +48 -3
- package/dist/src/rules/html-no-self-closing.js.map +1 -1
- package/dist/src/rules/html-no-space-in-tag.js +173 -0
- package/dist/src/rules/html-no-space-in-tag.js.map +1 -0
- package/dist/src/rules/html-no-title-attribute.js +7 -1
- package/dist/src/rules/html-no-title-attribute.js.map +1 -1
- package/dist/src/rules/html-no-underscores-in-attribute-names.js +7 -1
- package/dist/src/rules/html-no-underscores-in-attribute-names.js.map +1 -1
- package/dist/src/rules/html-tag-name-lowercase.js +23 -5
- package/dist/src/rules/html-tag-name-lowercase.js.map +1 -1
- package/dist/src/rules/index.js +20 -2
- package/dist/src/rules/index.js.map +1 -1
- package/dist/src/rules/parser-no-errors.js +6 -0
- package/dist/src/rules/parser-no-errors.js.map +1 -1
- package/dist/src/rules/rule-utils.js +211 -31
- package/dist/src/rules/rule-utils.js.map +1 -1
- package/dist/src/rules/svg-tag-name-capitalization.js +22 -2
- package/dist/src/rules/svg-tag-name-capitalization.js.map +1 -1
- package/dist/src/{default-rules.js → rules.js} +46 -14
- package/dist/src/rules.js.map +1 -0
- package/dist/src/types.js +34 -1
- package/dist/src/types.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/cli/argument-parser.d.ts +8 -2
- package/dist/types/cli/file-processor.d.ts +15 -0
- package/dist/types/cli/formatters/json-formatter.d.ts +6 -0
- package/dist/types/cli/formatters/simple-formatter.d.ts +1 -0
- package/dist/types/cli/summary-reporter.d.ts +6 -0
- package/dist/types/cli.d.ts +9 -4
- package/dist/types/custom-rule-loader.d.ts +62 -0
- package/dist/types/herb-disable-comment-utils.d.ts +69 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/linter.d.ts +99 -3
- package/dist/types/loader.d.ts +20 -0
- package/dist/types/rules/erb-comment-syntax.d.ts +14 -0
- package/dist/types/rules/erb-no-case-node-children.d.ts +8 -0
- package/dist/types/rules/erb-no-empty-tags.d.ts +3 -2
- package/dist/types/rules/erb-no-extra-newline.d.ts +14 -0
- package/dist/types/rules/erb-no-extra-whitespace-inside-tags.d.ts +18 -0
- package/dist/types/rules/erb-no-output-control-flow.d.ts +3 -2
- package/dist/types/rules/erb-no-silent-tag-in-attribute-name.d.ts +3 -2
- package/dist/types/rules/erb-prefer-image-tag-helper.d.ts +3 -2
- package/dist/types/rules/erb-require-trailing-newline.d.ts +9 -0
- package/dist/types/rules/erb-require-whitespace-inside-tags.d.ts +16 -5
- package/dist/types/rules/erb-right-trim.d.ts +14 -0
- package/dist/types/rules/herb-disable-comment-base.d.ts +37 -0
- package/dist/types/rules/herb-disable-comment-malformed.d.ts +8 -0
- package/dist/types/rules/herb-disable-comment-missing-rules.d.ts +8 -0
- package/dist/types/rules/herb-disable-comment-no-duplicate-rules.d.ts +8 -0
- package/dist/types/rules/herb-disable-comment-no-redundant-all.d.ts +8 -0
- package/dist/types/rules/herb-disable-comment-unnecessary.d.ts +8 -0
- package/dist/types/rules/herb-disable-comment-valid-rule-name.d.ts +8 -0
- package/dist/types/rules/html-anchor-require-href.d.ts +3 -2
- package/dist/types/rules/html-aria-attribute-must-be-valid.d.ts +3 -2
- package/dist/types/rules/html-aria-label-is-well-formatted.d.ts +3 -2
- package/dist/types/rules/html-aria-level-must-be-valid.d.ts +3 -2
- package/dist/types/rules/html-aria-role-heading-requires-level.d.ts +3 -2
- package/dist/types/rules/html-aria-role-must-be-valid.d.ts +3 -2
- package/dist/types/rules/html-attribute-double-quotes.d.ts +13 -5
- package/dist/types/rules/html-attribute-equals-spacing.d.ts +12 -5
- package/dist/types/rules/html-attribute-values-require-quotes.d.ts +13 -5
- package/dist/types/rules/html-avoid-both-disabled-and-aria-disabled.d.ts +3 -2
- package/dist/types/rules/html-body-only-elements.d.ts +9 -0
- package/dist/types/rules/html-boolean-attributes-no-value.d.ts +12 -5
- package/dist/types/rules/html-head-only-elements.d.ts +9 -0
- package/dist/types/rules/html-iframe-has-title.d.ts +3 -2
- package/dist/types/rules/html-img-require-alt.d.ts +3 -2
- package/dist/types/rules/html-input-require-autocomplete.d.ts +8 -0
- package/dist/types/rules/html-navigation-has-label.d.ts +3 -2
- package/dist/types/rules/html-no-aria-hidden-on-focusable.d.ts +3 -2
- package/dist/types/rules/html-no-block-inside-inline.d.ts +3 -2
- package/dist/types/rules/html-no-duplicate-attributes.d.ts +3 -2
- package/dist/types/rules/html-no-duplicate-ids.d.ts +3 -2
- package/dist/types/rules/html-no-duplicate-meta-names.d.ts +9 -0
- package/dist/types/rules/html-no-empty-attributes.d.ts +3 -2
- package/dist/types/rules/html-no-empty-headings.d.ts +3 -2
- package/dist/types/rules/html-no-nested-links.d.ts +3 -2
- package/dist/types/rules/html-no-positive-tab-index.d.ts +3 -2
- package/dist/types/rules/html-no-self-closing.d.ts +14 -5
- package/dist/types/rules/html-no-space-in-tag.d.ts +16 -0
- package/dist/types/rules/html-no-title-attribute.d.ts +3 -2
- package/dist/types/rules/html-no-underscores-in-attribute-names.d.ts +3 -2
- package/dist/types/rules/html-tag-name-lowercase.d.ts +16 -6
- package/dist/types/rules/index.d.ts +20 -2
- package/dist/types/rules/parser-no-errors.d.ts +2 -1
- package/dist/types/rules/rule-utils.d.ts +72 -25
- package/dist/types/rules/svg-tag-name-capitalization.d.ts +13 -4
- package/dist/types/rules.d.ts +2 -0
- package/dist/types/src/cli/argument-parser.d.ts +8 -2
- package/dist/types/src/cli/file-processor.d.ts +15 -0
- package/dist/types/src/cli/formatters/json-formatter.d.ts +6 -0
- package/dist/types/src/cli/formatters/simple-formatter.d.ts +1 -0
- package/dist/types/src/cli/summary-reporter.d.ts +6 -0
- package/dist/types/src/cli.d.ts +9 -4
- package/dist/types/src/custom-rule-loader.d.ts +62 -0
- package/dist/types/src/herb-disable-comment-utils.d.ts +69 -0
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/linter.d.ts +99 -3
- package/dist/types/src/loader.d.ts +20 -0
- package/dist/types/src/rules/erb-comment-syntax.d.ts +14 -0
- package/dist/types/src/rules/erb-no-case-node-children.d.ts +8 -0
- package/dist/types/src/rules/erb-no-empty-tags.d.ts +3 -2
- package/dist/types/src/rules/erb-no-extra-newline.d.ts +14 -0
- package/dist/types/src/rules/erb-no-extra-whitespace-inside-tags.d.ts +18 -0
- package/dist/types/src/rules/erb-no-output-control-flow.d.ts +3 -2
- package/dist/types/src/rules/erb-no-silent-tag-in-attribute-name.d.ts +3 -2
- package/dist/types/src/rules/erb-prefer-image-tag-helper.d.ts +3 -2
- package/dist/types/src/rules/erb-require-trailing-newline.d.ts +9 -0
- package/dist/types/src/rules/erb-require-whitespace-inside-tags.d.ts +16 -5
- package/dist/types/src/rules/erb-right-trim.d.ts +14 -0
- package/dist/types/src/rules/herb-disable-comment-base.d.ts +37 -0
- package/dist/types/src/rules/herb-disable-comment-malformed.d.ts +8 -0
- package/dist/types/src/rules/herb-disable-comment-missing-rules.d.ts +8 -0
- package/dist/types/src/rules/herb-disable-comment-no-duplicate-rules.d.ts +8 -0
- package/dist/types/src/rules/herb-disable-comment-no-redundant-all.d.ts +8 -0
- package/dist/types/src/rules/herb-disable-comment-unnecessary.d.ts +8 -0
- package/dist/types/src/rules/herb-disable-comment-valid-rule-name.d.ts +8 -0
- package/dist/types/src/rules/html-anchor-require-href.d.ts +3 -2
- package/dist/types/src/rules/html-aria-attribute-must-be-valid.d.ts +3 -2
- package/dist/types/src/rules/html-aria-label-is-well-formatted.d.ts +3 -2
- package/dist/types/src/rules/html-aria-level-must-be-valid.d.ts +3 -2
- package/dist/types/src/rules/html-aria-role-heading-requires-level.d.ts +3 -2
- package/dist/types/src/rules/html-aria-role-must-be-valid.d.ts +3 -2
- package/dist/types/src/rules/html-attribute-double-quotes.d.ts +13 -5
- package/dist/types/src/rules/html-attribute-equals-spacing.d.ts +12 -5
- package/dist/types/src/rules/html-attribute-values-require-quotes.d.ts +13 -5
- package/dist/types/src/rules/html-avoid-both-disabled-and-aria-disabled.d.ts +3 -2
- package/dist/types/src/rules/html-body-only-elements.d.ts +9 -0
- package/dist/types/src/rules/html-boolean-attributes-no-value.d.ts +12 -5
- package/dist/types/src/rules/html-head-only-elements.d.ts +9 -0
- package/dist/types/src/rules/html-iframe-has-title.d.ts +3 -2
- package/dist/types/src/rules/html-img-require-alt.d.ts +3 -2
- package/dist/types/src/rules/html-input-require-autocomplete.d.ts +8 -0
- package/dist/types/src/rules/html-navigation-has-label.d.ts +3 -2
- package/dist/types/src/rules/html-no-aria-hidden-on-focusable.d.ts +3 -2
- package/dist/types/src/rules/html-no-block-inside-inline.d.ts +3 -2
- package/dist/types/src/rules/html-no-duplicate-attributes.d.ts +3 -2
- package/dist/types/src/rules/html-no-duplicate-ids.d.ts +3 -2
- package/dist/types/src/rules/html-no-duplicate-meta-names.d.ts +9 -0
- package/dist/types/src/rules/html-no-empty-attributes.d.ts +3 -2
- package/dist/types/src/rules/html-no-empty-headings.d.ts +3 -2
- package/dist/types/src/rules/html-no-nested-links.d.ts +3 -2
- package/dist/types/src/rules/html-no-positive-tab-index.d.ts +3 -2
- package/dist/types/src/rules/html-no-self-closing.d.ts +14 -5
- package/dist/types/src/rules/html-no-space-in-tag.d.ts +16 -0
- package/dist/types/src/rules/html-no-title-attribute.d.ts +3 -2
- package/dist/types/src/rules/html-no-underscores-in-attribute-names.d.ts +3 -2
- package/dist/types/src/rules/html-tag-name-lowercase.d.ts +16 -6
- package/dist/types/src/rules/index.d.ts +20 -2
- package/dist/types/src/rules/parser-no-errors.d.ts +2 -1
- package/dist/types/src/rules/rule-utils.d.ts +72 -25
- package/dist/types/src/rules/svg-tag-name-capitalization.d.ts +13 -4
- package/dist/types/src/rules.d.ts +2 -0
- package/dist/types/src/types.d.ts +102 -11
- package/dist/types/types.d.ts +102 -11
- package/docs/rules/README.md +19 -3
- package/docs/rules/erb-comment-syntax.md +44 -0
- package/docs/rules/erb-no-case-node-children.md +50 -0
- package/docs/rules/erb-no-extra-newline.md +74 -0
- package/docs/rules/erb-no-extra-whitespace-inside-tags.md +39 -0
- package/docs/rules/{erb-requires-trailing-newline.md → erb-require-trailing-newline.md} +1 -1
- package/docs/rules/erb-right-trim.md +52 -0
- package/docs/rules/herb-disable-comment-malformed.md +45 -0
- package/docs/rules/herb-disable-comment-missing-rules.md +60 -0
- package/docs/rules/herb-disable-comment-no-duplicate-rules.md +49 -0
- package/docs/rules/herb-disable-comment-no-redundant-all.md +53 -0
- package/docs/rules/herb-disable-comment-unnecessary.md +44 -0
- package/docs/rules/herb-disable-comment-valid-rule-name.md +41 -0
- package/docs/rules/html-aria-attribute-must-be-valid.md +2 -5
- package/docs/rules/html-aria-label-is-well-formatted.md +1 -1
- package/docs/rules/html-attribute-double-quotes.md +2 -2
- package/docs/rules/html-attribute-equals-spacing.md +2 -2
- package/docs/rules/html-attribute-values-require-quotes.md +3 -3
- package/docs/rules/html-avoid-both-disabled-and-aria-disabled.md +2 -2
- package/docs/rules/html-body-only-elements.md +99 -0
- package/docs/rules/html-boolean-attributes-no-value.md +2 -2
- package/docs/rules/html-head-only-elements.md +81 -0
- package/docs/rules/html-input-require-autocomplete.md +64 -0
- package/docs/rules/html-no-aria-hidden-on-focusable.md +2 -2
- package/docs/rules/html-no-duplicate-attributes.md +2 -2
- package/docs/rules/html-no-duplicate-meta-names.md +64 -0
- package/docs/rules/html-no-empty-attributes.md +3 -3
- package/docs/rules/html-no-empty-headings.md +4 -26
- package/docs/rules/html-no-positive-tab-index.md +1 -2
- package/docs/rules/html-no-self-closing.md +17 -2
- package/docs/rules/html-no-space-in-tag.md +66 -0
- package/docs/rules/html-no-title-attribute.md +2 -2
- package/docs/rules/html-no-underscores-in-attribute-names.md +2 -2
- package/docs/rules/html-tag-name-lowercase.md +2 -2
- package/package.json +13 -5
- package/src/cli/argument-parser.ts +50 -39
- package/src/cli/file-processor.ts +159 -28
- package/src/cli/formatters/detailed-formatter.ts +21 -3
- package/src/cli/formatters/github-actions-formatter.ts +17 -1
- package/src/cli/formatters/json-formatter.ts +9 -0
- package/src/cli/formatters/simple-formatter.ts +24 -8
- package/src/cli/output-manager.ts +23 -3
- package/src/cli/summary-reporter.ts +40 -3
- package/src/cli.ts +137 -52
- package/src/custom-rule-loader.ts +189 -0
- package/src/herb-disable-comment-utils.ts +175 -0
- package/src/index.ts +2 -0
- package/src/linter.ts +501 -36
- package/src/loader.ts +30 -0
- package/src/rules/erb-comment-syntax.ts +73 -0
- package/src/rules/erb-no-case-node-children.ts +68 -0
- package/src/rules/erb-no-empty-tags.ts +9 -3
- package/src/rules/erb-no-extra-newline.ts +91 -0
- package/src/rules/erb-no-extra-whitespace-inside-tags.ts +147 -0
- package/src/rules/erb-no-output-control-flow.ts +9 -3
- package/src/rules/erb-no-silent-tag-in-attribute-name.ts +9 -3
- package/src/rules/erb-prefer-image-tag-helper.ts +9 -3
- package/src/rules/erb-require-trailing-newline.ts +47 -0
- package/src/rules/erb-require-whitespace-inside-tags.ts +96 -26
- package/src/rules/erb-right-trim.ts +67 -0
- package/src/rules/herb-disable-comment-base.ts +76 -0
- package/src/rules/herb-disable-comment-malformed.ts +66 -0
- package/src/rules/herb-disable-comment-missing-rules.ts +41 -0
- package/src/rules/herb-disable-comment-no-duplicate-rules.ts +46 -0
- package/src/rules/herb-disable-comment-no-redundant-all.ts +40 -0
- package/src/rules/herb-disable-comment-unnecessary.ts +103 -0
- package/src/rules/herb-disable-comment-valid-rule-name.ts +62 -0
- package/src/rules/html-anchor-require-href.ts +9 -3
- package/src/rules/html-aria-attribute-must-be-valid.ts +9 -3
- package/src/rules/html-aria-label-is-well-formatted.ts +9 -5
- package/src/rules/html-aria-level-must-be-valid.ts +9 -2
- package/src/rules/html-aria-role-heading-requires-level.ts +9 -3
- package/src/rules/html-aria-role-must-be-valid.ts +9 -3
- package/src/rules/html-attribute-double-quotes.ts +42 -8
- package/src/rules/html-attribute-equals-spacing.ts +31 -7
- package/src/rules/html-attribute-values-require-quotes.ts +56 -10
- package/src/rules/html-avoid-both-disabled-and-aria-disabled.ts +9 -3
- package/src/rules/html-body-only-elements.ts +60 -0
- package/src/rules/html-boolean-attributes-no-value.ts +31 -6
- package/src/rules/html-head-only-elements.ts +65 -0
- package/src/rules/html-iframe-has-title.ts +9 -4
- package/src/rules/html-img-require-alt.ts +10 -4
- package/src/rules/html-input-require-autocomplete.ts +85 -0
- package/src/rules/html-navigation-has-label.ts +9 -3
- package/src/rules/html-no-aria-hidden-on-focusable.ts +9 -3
- package/src/rules/html-no-block-inside-inline.ts +9 -3
- package/src/rules/html-no-duplicate-attributes.ts +9 -3
- package/src/rules/html-no-duplicate-ids.ts +11 -7
- package/src/rules/html-no-duplicate-meta-names.ts +188 -0
- package/src/rules/html-no-empty-attributes.ts +58 -10
- package/src/rules/html-no-empty-headings.ts +10 -8
- package/src/rules/html-no-nested-links.ts +10 -4
- package/src/rules/html-no-positive-tab-index.ts +9 -3
- package/src/rules/html-no-self-closing.ts +69 -9
- package/src/rules/html-no-space-in-tag.ts +221 -0
- package/src/rules/html-no-title-attribute.ts +9 -3
- package/src/rules/html-no-underscores-in-attribute-names.ts +12 -4
- package/src/rules/html-tag-name-lowercase.ts +41 -10
- package/src/rules/index.ts +24 -2
- package/src/rules/parser-no-errors.ts +8 -1
- package/src/rules/rule-utils.ts +250 -44
- package/src/rules/svg-tag-name-capitalization.ts +39 -6
- package/src/{default-rules.ts → rules.ts} +53 -13
- package/src/types.ts +133 -15
- package/dist/src/default-rules.js.map +0 -1
- package/dist/src/rules/erb-requires-trailing-newline.js +0 -22
- package/dist/src/rules/erb-requires-trailing-newline.js.map +0 -1
- package/dist/types/default-rules.d.ts +0 -2
- package/dist/types/rules/erb-requires-trailing-newline.d.ts +0 -6
- package/dist/types/src/default-rules.d.ts +0 -2
- package/dist/types/src/rules/erb-requires-trailing-newline.d.ts +0 -6
- package/src/rules/erb-requires-trailing-newline.ts +0 -29
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { pathToFileURL } from "url"
|
|
2
|
+
import { glob } from "glob"
|
|
3
|
+
|
|
4
|
+
import type { RuleClass } from "./types.js"
|
|
5
|
+
|
|
6
|
+
export interface CustomRuleLoaderOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Base directory to search for custom rules
|
|
9
|
+
* Defaults to current working directory
|
|
10
|
+
*/
|
|
11
|
+
baseDir?: string
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Glob patterns to search for custom rule files
|
|
15
|
+
* Defaults to looking in common locations
|
|
16
|
+
*/
|
|
17
|
+
patterns?: string[]
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Whether to suppress errors when loading custom rules
|
|
21
|
+
* Defaults to false
|
|
22
|
+
*/
|
|
23
|
+
silent?: boolean
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const DEFAULT_PATTERNS = [
|
|
27
|
+
".herb/rules/**/*.mjs",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Loads custom linter rules from the user's project
|
|
32
|
+
*/
|
|
33
|
+
export class CustomRuleLoader {
|
|
34
|
+
private baseDir: string
|
|
35
|
+
private patterns: string[]
|
|
36
|
+
private silent: boolean
|
|
37
|
+
|
|
38
|
+
constructor(options: CustomRuleLoaderOptions = {}) {
|
|
39
|
+
this.baseDir = options.baseDir || process.cwd()
|
|
40
|
+
this.patterns = options.patterns || DEFAULT_PATTERNS
|
|
41
|
+
this.silent = options.silent || false
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Discovers custom rule files in the project
|
|
46
|
+
*/
|
|
47
|
+
async discoverRuleFiles(): Promise<string[]> {
|
|
48
|
+
const allFiles: string[] = []
|
|
49
|
+
|
|
50
|
+
for (const pattern of this.patterns) {
|
|
51
|
+
try {
|
|
52
|
+
const files = await glob(pattern, {
|
|
53
|
+
cwd: this.baseDir,
|
|
54
|
+
absolute: true,
|
|
55
|
+
nodir: true
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
allFiles.push(...files)
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (!this.silent) {
|
|
61
|
+
console.warn(`Warning: Failed to search pattern "${pattern}": ${error}`)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return [...new Set(allFiles)]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Loads a single rule file
|
|
71
|
+
*/
|
|
72
|
+
async loadRuleFile(filePath: string): Promise<RuleClass[]> {
|
|
73
|
+
try {
|
|
74
|
+
const fileUrl = pathToFileURL(filePath).href
|
|
75
|
+
const cacheBustedUrl = `${fileUrl}?t=${Date.now()}`
|
|
76
|
+
const module = await import(cacheBustedUrl)
|
|
77
|
+
|
|
78
|
+
if (module.default && this.isValidRuleClass(module.default)) {
|
|
79
|
+
return [module.default]
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!this.silent) {
|
|
83
|
+
console.warn(`Warning: No valid default export found in "${filePath}". Custom rules must use default export.`)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return []
|
|
87
|
+
} catch (error) {
|
|
88
|
+
if (!this.silent) {
|
|
89
|
+
console.error(`Error loading rule file "${filePath}": ${error}`)
|
|
90
|
+
}
|
|
91
|
+
return []
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Type guard to check if an export is a valid rule class
|
|
97
|
+
*/
|
|
98
|
+
private isValidRuleClass(value: any): value is RuleClass {
|
|
99
|
+
if (typeof value !== 'function') return false
|
|
100
|
+
if (!value.prototype) return false
|
|
101
|
+
|
|
102
|
+
const instance = new value()
|
|
103
|
+
|
|
104
|
+
return typeof instance.check === 'function' && typeof instance.name === 'string'
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Loads all custom rules from the project
|
|
109
|
+
*/
|
|
110
|
+
async loadRules(): Promise<RuleClass[]> {
|
|
111
|
+
const ruleFiles = await this.discoverRuleFiles()
|
|
112
|
+
|
|
113
|
+
if (ruleFiles.length === 0) {
|
|
114
|
+
return []
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const allRules: RuleClass[] = []
|
|
118
|
+
|
|
119
|
+
for (const filePath of ruleFiles) {
|
|
120
|
+
const rules = await this.loadRuleFile(filePath)
|
|
121
|
+
allRules.push(...rules)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return allRules
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Loads all custom rules and returns detailed information about each rule
|
|
129
|
+
*/
|
|
130
|
+
async loadRulesWithInfo(): Promise<{ rules: RuleClass[], ruleInfo: Array<{ name: string, path: string }>, duplicateWarnings: string[] }> {
|
|
131
|
+
const ruleFiles = await this.discoverRuleFiles()
|
|
132
|
+
|
|
133
|
+
if (ruleFiles.length === 0) {
|
|
134
|
+
return { rules: [], ruleInfo: [], duplicateWarnings: [] }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const allRules: RuleClass[] = []
|
|
138
|
+
const ruleInfo: Array<{ name: string, path: string }> = []
|
|
139
|
+
const duplicateWarnings: string[] = []
|
|
140
|
+
const seenNames = new Map<string, string>()
|
|
141
|
+
|
|
142
|
+
for (const filePath of ruleFiles) {
|
|
143
|
+
const rules = await this.loadRuleFile(filePath)
|
|
144
|
+
for (const RuleClass of rules) {
|
|
145
|
+
const instance = new RuleClass()
|
|
146
|
+
const ruleName = instance.name
|
|
147
|
+
|
|
148
|
+
if (seenNames.has(ruleName)) {
|
|
149
|
+
const firstPath = seenNames.get(ruleName)!
|
|
150
|
+
duplicateWarnings.push(
|
|
151
|
+
`Custom rule "${ruleName}" is defined in multiple files: "${firstPath}" and "${filePath}". The later one will be used.`
|
|
152
|
+
)
|
|
153
|
+
} else {
|
|
154
|
+
seenNames.set(ruleName, filePath)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
allRules.push(RuleClass)
|
|
158
|
+
ruleInfo.push({
|
|
159
|
+
name: ruleName,
|
|
160
|
+
path: filePath
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return { rules: allRules, ruleInfo, duplicateWarnings }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Static helper to check if custom rules exist in a project
|
|
170
|
+
*/
|
|
171
|
+
static async hasCustomRules(baseDir: string = process.cwd()): Promise<boolean> {
|
|
172
|
+
const loader = new CustomRuleLoader({ baseDir, silent: true })
|
|
173
|
+
const files = await loader.discoverRuleFiles()
|
|
174
|
+
return files.length > 0
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Static helper to load custom rules and merge with default rules
|
|
179
|
+
*/
|
|
180
|
+
static async loadAndMergeRules(
|
|
181
|
+
defaultRules: RuleClass[],
|
|
182
|
+
options: CustomRuleLoaderOptions = {}
|
|
183
|
+
): Promise<RuleClass[]> {
|
|
184
|
+
const loader = new CustomRuleLoader(options)
|
|
185
|
+
const customRules = await loader.loadRules()
|
|
186
|
+
|
|
187
|
+
return [...defaultRules, ...customRules]
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for parsing herb:disable comments
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Information about a single rule name in a herb:disable comment
|
|
7
|
+
*/
|
|
8
|
+
export interface HerbDisableRuleName {
|
|
9
|
+
/** The rule name */
|
|
10
|
+
name: string
|
|
11
|
+
/** The starting offset of this rule name within the content/line */
|
|
12
|
+
offset: number
|
|
13
|
+
/** The length of the rule name */
|
|
14
|
+
length: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Result of parsing a herb:disable comment
|
|
19
|
+
*/
|
|
20
|
+
export interface HerbDisableComment {
|
|
21
|
+
/** The full matched string */
|
|
22
|
+
match: string
|
|
23
|
+
/** Array of rule names specified in the comment */
|
|
24
|
+
ruleNames: string[]
|
|
25
|
+
/** Array of rule name information with positions */
|
|
26
|
+
ruleNameDetails: HerbDisableRuleName[]
|
|
27
|
+
/** The original rules string (e.g., "rule1, rule2") */
|
|
28
|
+
rulesString: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Prefix for herb:disable comments
|
|
33
|
+
*/
|
|
34
|
+
const HERB_DISABLE_PREFIX = "herb:disable"
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Parse a herb:disable comment from ERB comment content.
|
|
38
|
+
* Use this when you have the content inside <%# ... %> (e.g., from ERBContentNode.content.value)
|
|
39
|
+
*
|
|
40
|
+
* @param content - The content string (without <%# %> delimiters)
|
|
41
|
+
* @returns Parsed comment data or null if not a valid herb:disable comment
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* const result = parseHerbDisableContent("herb:disable rule1, rule2")
|
|
46
|
+
* // { match: "herb:disable rule1, rule2", ruleNames: ["rule1", "rule2"], rulesString: "rule1, rule2" }
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export function parseHerbDisableContent(content: string): HerbDisableComment | null {
|
|
50
|
+
const trimmed = content.trim()
|
|
51
|
+
|
|
52
|
+
if (!trimmed.startsWith(HERB_DISABLE_PREFIX)) return null
|
|
53
|
+
|
|
54
|
+
const afterPrefix = trimmed.substring(HERB_DISABLE_PREFIX.length).trimStart()
|
|
55
|
+
if (afterPrefix.length === 0) return null
|
|
56
|
+
|
|
57
|
+
const rulesString = afterPrefix.trimEnd()
|
|
58
|
+
const ruleNames = rulesString.split(',').map(name => name.trim())
|
|
59
|
+
|
|
60
|
+
if (ruleNames.some(name => name.length === 0)) return null
|
|
61
|
+
if (ruleNames.length === 0) return null
|
|
62
|
+
|
|
63
|
+
const herbDisablePrefix = content.indexOf(HERB_DISABLE_PREFIX)
|
|
64
|
+
const searchStart = herbDisablePrefix + HERB_DISABLE_PREFIX.length
|
|
65
|
+
const rulesStringOffset = content.indexOf(rulesString, searchStart)
|
|
66
|
+
|
|
67
|
+
const ruleNameDetails: HerbDisableRuleName[] = []
|
|
68
|
+
|
|
69
|
+
let currentOffset = 0
|
|
70
|
+
|
|
71
|
+
for (const ruleName of ruleNames) {
|
|
72
|
+
const ruleOffset = rulesString.indexOf(ruleName, currentOffset)
|
|
73
|
+
|
|
74
|
+
ruleNameDetails.push({
|
|
75
|
+
name: ruleName,
|
|
76
|
+
offset: rulesStringOffset + ruleOffset,
|
|
77
|
+
length: ruleName.length
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
currentOffset = ruleOffset + ruleName.length
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
match: trimmed,
|
|
85
|
+
ruleNames,
|
|
86
|
+
ruleNameDetails,
|
|
87
|
+
rulesString
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Parse a herb:disable comment from a full source line.
|
|
93
|
+
* Use this when you have a complete line that may contain <%# herb:disable ... %>
|
|
94
|
+
*
|
|
95
|
+
* @param line - The source line that may contain a herb:disable comment
|
|
96
|
+
* @returns Parsed comment data or null if not a valid herb:disable comment
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* const result = parseHerbDisableLine("<div>test</div> <%# herb:disable rule1, rule2 %>")
|
|
101
|
+
* // { match: "<%# herb:disable rule1, rule2 %>", ruleNames: ["rule1", "rule2"], rulesString: "rule1, rule2" }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export function parseHerbDisableLine(line: string): HerbDisableComment | null {
|
|
105
|
+
const startTag = "<%#"
|
|
106
|
+
const endTag = "%>"
|
|
107
|
+
|
|
108
|
+
const startIndex = line.indexOf(startTag)
|
|
109
|
+
if (startIndex === -1) return null
|
|
110
|
+
|
|
111
|
+
const endIndex = line.indexOf(endTag, startIndex)
|
|
112
|
+
if (endIndex === -1) return null
|
|
113
|
+
|
|
114
|
+
const content = line.substring(startIndex + startTag.length, endIndex).trim()
|
|
115
|
+
|
|
116
|
+
if (!content.startsWith(HERB_DISABLE_PREFIX)) return null
|
|
117
|
+
|
|
118
|
+
const afterPrefix = content.substring(HERB_DISABLE_PREFIX.length).trimStart()
|
|
119
|
+
if (afterPrefix.length === 0) return null
|
|
120
|
+
|
|
121
|
+
const rulesString = afterPrefix.trimEnd()
|
|
122
|
+
const ruleNames = rulesString.split(',').map(name => name.trim())
|
|
123
|
+
|
|
124
|
+
if (ruleNames.some(name => name.length === 0)) return null
|
|
125
|
+
if (ruleNames.length === 0) return null
|
|
126
|
+
|
|
127
|
+
const herbDisablePrefix = line.indexOf(HERB_DISABLE_PREFIX)
|
|
128
|
+
const searchStart = herbDisablePrefix + HERB_DISABLE_PREFIX.length
|
|
129
|
+
const rulesStringOffset = line.indexOf(rulesString, searchStart)
|
|
130
|
+
|
|
131
|
+
const ruleNameDetails: HerbDisableRuleName[] = []
|
|
132
|
+
|
|
133
|
+
let currentOffset = 0
|
|
134
|
+
|
|
135
|
+
for (const ruleName of ruleNames) {
|
|
136
|
+
const ruleOffset = rulesString.indexOf(ruleName, currentOffset)
|
|
137
|
+
|
|
138
|
+
ruleNameDetails.push({
|
|
139
|
+
name: ruleName,
|
|
140
|
+
offset: rulesStringOffset + ruleOffset,
|
|
141
|
+
length: ruleName.length
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
currentOffset = ruleOffset + ruleName.length
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const fullMatch = line.substring(startIndex, endIndex + endTag.length)
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
match: fullMatch,
|
|
151
|
+
ruleNames,
|
|
152
|
+
ruleNameDetails,
|
|
153
|
+
rulesString
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Check if an ERB comment content contains a herb:disable directive.
|
|
159
|
+
*
|
|
160
|
+
* @param content - The content string (without <%# %> delimiters)
|
|
161
|
+
* @returns true if the content contains a herb:disable directive
|
|
162
|
+
*/
|
|
163
|
+
export function isHerbDisableContent(content: string): boolean {
|
|
164
|
+
return parseHerbDisableContent(content) !== null
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Check if a source line contains a herb:disable comment.
|
|
169
|
+
*
|
|
170
|
+
* @param line - The source line
|
|
171
|
+
* @returns true if the line contains a herb:disable comment
|
|
172
|
+
*/
|
|
173
|
+
export function isHerbDisableLine(line: string): boolean {
|
|
174
|
+
return parseHerbDisableLine(line) !== null
|
|
175
|
+
}
|