@herb-tools/linter 0.7.5 → 0.8.1
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 +26041 -3435
- package/dist/herb-lint.js.map +1 -1
- package/dist/index.cjs +5437 -1254
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5405 -1255
- package/dist/index.js.map +1 -1
- package/dist/loader.cjs +17017 -0
- package/dist/loader.cjs.map +1 -0
- package/dist/loader.js +16886 -0
- package/dist/loader.js.map +1 -0
- package/dist/package.json +13 -5
- package/dist/src/cli/argument-parser.js +38 -33
- 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 +107 -42
- 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 +31 -2
- package/dist/src/rules/erb-comment-syntax.js.map +1 -1
- 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 +69 -11
- package/dist/src/rules/erb-require-whitespace-inside-tags.js.map +1 -1
- package/dist/src/rules/erb-right-trim.js +26 -9
- package/dist/src/rules/erb-right-trim.js.map +1 -1
- 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 +56 -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 +19 -3
- 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 +213 -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} +44 -16
- 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 +12 -5
- 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 +12 -5
- 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 +19 -3
- package/dist/types/rules/parser-no-errors.d.ts +2 -1
- package/dist/types/rules/rule-utils.d.ts +73 -26
- 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 +12 -5
- 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 +12 -5
- 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 +19 -3
- package/dist/types/src/rules/parser-no-errors.d.ts +2 -1
- package/dist/types/src/rules/rule-utils.d.ts +73 -26
- 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 +16 -2
- 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 +5 -10
- 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 +103 -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 +46 -37
- 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 +134 -51
- 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 +53 -10
- 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 +94 -16
- package/src/rules/erb-right-trim.ts +45 -22
- 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 +70 -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 +23 -3
- package/src/rules/parser-no-errors.ts +8 -1
- package/src/rules/rule-utils.ts +251 -43
- package/src/rules/svg-tag-name-capitalization.ts +39 -6
- package/src/{default-rules.ts → rules.ts} +51 -15
- 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
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import { ParserRule } from "../types.js"
|
|
1
|
+
import { ParserRule, BaseAutofixContext, Mutable } from "../types.js"
|
|
2
2
|
import { BaseRuleVisitor } from "./rule-utils.js"
|
|
3
3
|
|
|
4
|
-
import type { LintOffense, LintContext } from "../types.js"
|
|
5
4
|
import type { ParseResult, Token, ERBNode } from "@herb-tools/core"
|
|
5
|
+
import type { UnboundLintOffense, LintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
interface ERBRequireWhitespaceAutofixContext extends BaseAutofixContext {
|
|
8
|
+
node: Mutable<ERBNode>
|
|
9
|
+
openTag: Token
|
|
10
|
+
closeTag: Token
|
|
11
|
+
content: string
|
|
12
|
+
fixType: "after-open" | "before-close" | "after-comment-equals"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class RequireWhitespaceInsideTags extends BaseRuleVisitor<ERBRequireWhitespaceAutofixContext> {
|
|
8
16
|
|
|
9
17
|
visitERBNode(node: ERBNode): void {
|
|
10
18
|
const openTag = node.tag_opening
|
|
@@ -18,25 +26,37 @@ class RequireWhitespaceInsideTags extends BaseRuleVisitor {
|
|
|
18
26
|
const value = content.value
|
|
19
27
|
|
|
20
28
|
if (openTag.value === "<%#") {
|
|
21
|
-
this.checkCommentTagWhitespace(openTag, closeTag, value)
|
|
29
|
+
this.checkCommentTagWhitespace(node, openTag, closeTag, value)
|
|
22
30
|
} else {
|
|
23
|
-
this.checkOpenTagWhitespace(openTag, value)
|
|
24
|
-
this.checkCloseTagWhitespace(closeTag, value)
|
|
31
|
+
this.checkOpenTagWhitespace(node, openTag, closeTag, value)
|
|
32
|
+
this.checkCloseTagWhitespace(node, openTag, closeTag, value)
|
|
25
33
|
}
|
|
26
34
|
}
|
|
27
35
|
|
|
28
|
-
private checkCommentTagWhitespace(openTag: Token, closeTag: Token, content: string): void {
|
|
36
|
+
private checkCommentTagWhitespace(node: ERBNode, openTag: Token, closeTag: Token, content: string): void {
|
|
29
37
|
if (!content.startsWith(" ") && !content.startsWith("\n") && !content.startsWith("=")) {
|
|
30
38
|
this.addOffense(
|
|
31
39
|
`Add whitespace after \`${openTag.value}\`.`,
|
|
32
40
|
openTag.location,
|
|
33
|
-
|
|
41
|
+
{
|
|
42
|
+
node,
|
|
43
|
+
openTag,
|
|
44
|
+
closeTag,
|
|
45
|
+
content,
|
|
46
|
+
fixType: "after-open"
|
|
47
|
+
}
|
|
34
48
|
)
|
|
35
49
|
} else if (content.startsWith("=") && content.length > 1 && !content[1].match(/\s/)) {
|
|
36
50
|
this.addOffense(
|
|
37
51
|
`Add whitespace after \`<%#=\`.`,
|
|
38
52
|
openTag.location,
|
|
39
|
-
|
|
53
|
+
{
|
|
54
|
+
node,
|
|
55
|
+
openTag,
|
|
56
|
+
closeTag,
|
|
57
|
+
content,
|
|
58
|
+
fixType: "after-comment-equals"
|
|
59
|
+
}
|
|
40
60
|
)
|
|
41
61
|
}
|
|
42
62
|
|
|
@@ -44,12 +64,18 @@ class RequireWhitespaceInsideTags extends BaseRuleVisitor {
|
|
|
44
64
|
this.addOffense(
|
|
45
65
|
`Add whitespace before \`${closeTag.value}\`.`,
|
|
46
66
|
closeTag.location,
|
|
47
|
-
|
|
67
|
+
{
|
|
68
|
+
node,
|
|
69
|
+
openTag,
|
|
70
|
+
closeTag,
|
|
71
|
+
content,
|
|
72
|
+
fixType: "before-close"
|
|
73
|
+
}
|
|
48
74
|
)
|
|
49
75
|
}
|
|
50
76
|
}
|
|
51
77
|
|
|
52
|
-
private checkOpenTagWhitespace(openTag: Token, content:string):void {
|
|
78
|
+
private checkOpenTagWhitespace(node: ERBNode, openTag: Token, closeTag: Token, content: string):void {
|
|
53
79
|
if (content.startsWith(" ") || content.startsWith("\n")) {
|
|
54
80
|
return
|
|
55
81
|
}
|
|
@@ -57,11 +83,17 @@ class RequireWhitespaceInsideTags extends BaseRuleVisitor {
|
|
|
57
83
|
this.addOffense(
|
|
58
84
|
`Add whitespace after \`${openTag.value}\`.`,
|
|
59
85
|
openTag.location,
|
|
60
|
-
|
|
86
|
+
{
|
|
87
|
+
node,
|
|
88
|
+
openTag,
|
|
89
|
+
closeTag,
|
|
90
|
+
content,
|
|
91
|
+
fixType: "after-open"
|
|
92
|
+
}
|
|
61
93
|
)
|
|
62
94
|
}
|
|
63
95
|
|
|
64
|
-
private checkCloseTagWhitespace(closeTag: Token, content:string):void {
|
|
96
|
+
private checkCloseTagWhitespace(node: ERBNode, openTag: Token, closeTag: Token, content: string):void {
|
|
65
97
|
if (content.endsWith(" ") || content.endsWith("\n")) {
|
|
66
98
|
return
|
|
67
99
|
}
|
|
@@ -69,17 +101,63 @@ class RequireWhitespaceInsideTags extends BaseRuleVisitor {
|
|
|
69
101
|
this.addOffense(
|
|
70
102
|
`Add whitespace before \`${closeTag.value}\`.`,
|
|
71
103
|
closeTag.location,
|
|
72
|
-
|
|
104
|
+
{
|
|
105
|
+
node,
|
|
106
|
+
openTag,
|
|
107
|
+
closeTag,
|
|
108
|
+
content,
|
|
109
|
+
fixType: "before-close"
|
|
110
|
+
}
|
|
73
111
|
)
|
|
74
112
|
}
|
|
75
113
|
}
|
|
76
114
|
|
|
77
|
-
export class ERBRequireWhitespaceRule extends ParserRule {
|
|
115
|
+
export class ERBRequireWhitespaceRule extends ParserRule<ERBRequireWhitespaceAutofixContext> {
|
|
116
|
+
static autocorrectable = true
|
|
78
117
|
name = "erb-require-whitespace-inside-tags"
|
|
79
118
|
|
|
80
|
-
|
|
119
|
+
get defaultConfig(): FullRuleConfig {
|
|
120
|
+
return {
|
|
121
|
+
enabled: true,
|
|
122
|
+
severity: "error"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense<ERBRequireWhitespaceAutofixContext>[] {
|
|
81
127
|
const visitor = new RequireWhitespaceInsideTags(this.name, context)
|
|
128
|
+
|
|
82
129
|
visitor.visit(result.value)
|
|
130
|
+
|
|
83
131
|
return visitor.offenses
|
|
84
132
|
}
|
|
133
|
+
|
|
134
|
+
autofix(offense: LintOffense<ERBRequireWhitespaceAutofixContext>, result: ParseResult, _context?: Partial<LintContext>): ParseResult | null {
|
|
135
|
+
if (!offense.autofixContext) return null
|
|
136
|
+
|
|
137
|
+
const { node, fixType } = offense.autofixContext
|
|
138
|
+
|
|
139
|
+
if (!node.content) return null
|
|
140
|
+
|
|
141
|
+
const content = node.content.value
|
|
142
|
+
|
|
143
|
+
if (fixType === "before-close") {
|
|
144
|
+
node.content.value = content + " "
|
|
145
|
+
|
|
146
|
+
return result
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (fixType === "after-open") {
|
|
150
|
+
node.content.value = " " + content
|
|
151
|
+
|
|
152
|
+
return result
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (fixType === "after-comment-equals" && content.startsWith("=")) {
|
|
156
|
+
node.content.value = "= " + content.substring(1)
|
|
157
|
+
|
|
158
|
+
return result
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return null
|
|
162
|
+
}
|
|
85
163
|
}
|
|
@@ -1,44 +1,67 @@
|
|
|
1
1
|
import { BaseRuleVisitor } from "./rule-utils.js"
|
|
2
|
-
import { ParserRule } from "../types.js"
|
|
3
|
-
import { isERBOutputNode } from "@herb-tools/core"
|
|
2
|
+
import { ParserRule, BaseAutofixContext, Mutable } from "../types.js"
|
|
4
3
|
|
|
5
|
-
import type { LintOffense, LintContext } from "../types.js"
|
|
4
|
+
import type { UnboundLintOffense, LintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
6
5
|
import type { ERBNode, ParseResult } from "@herb-tools/core"
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
interface ERBRightTrimAutofixContext extends BaseAutofixContext {
|
|
8
|
+
node: Mutable<ERBNode>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class ERBRightTrimVisitor extends BaseRuleVisitor<ERBRightTrimAutofixContext> {
|
|
9
12
|
visitERBNode(node: ERBNode): void {
|
|
10
13
|
if (!node.tag_closing) return
|
|
11
14
|
|
|
12
15
|
const trimClosing = node.tag_closing.value
|
|
13
16
|
|
|
14
|
-
if (trimClosing !== "=%>"
|
|
15
|
-
|
|
16
|
-
if (!isERBOutputNode(node)) {
|
|
17
|
-
this.addOffense(
|
|
18
|
-
`Right-trimming with \`${trimClosing}\` has no effect on non-output ERB tags. Use \`%>\` instead`,
|
|
19
|
-
node.tag_closing.location
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
return
|
|
23
|
-
}
|
|
17
|
+
if (trimClosing !== "=%>") return
|
|
24
18
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
19
|
+
this.addOffense(
|
|
20
|
+
"Use `-%>` instead of `=%>` for right-trimming. The `=%>` syntax is obscure and not well-supported in most ERB engines.",
|
|
21
|
+
node.tag_closing.location,
|
|
22
|
+
{ node }
|
|
23
|
+
)
|
|
31
24
|
}
|
|
32
25
|
}
|
|
33
26
|
|
|
34
|
-
export class ERBRightTrimRule extends ParserRule {
|
|
27
|
+
export class ERBRightTrimRule extends ParserRule<ERBRightTrimAutofixContext> {
|
|
28
|
+
static autocorrectable = true
|
|
35
29
|
name = "erb-right-trim"
|
|
36
30
|
|
|
37
|
-
|
|
31
|
+
get defaultConfig(): FullRuleConfig {
|
|
32
|
+
return {
|
|
33
|
+
enabled: true,
|
|
34
|
+
severity: "error"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense<ERBRightTrimAutofixContext>[] {
|
|
38
39
|
const visitor = new ERBRightTrimVisitor(this.name, context)
|
|
39
40
|
|
|
40
41
|
visitor.visit(result.value)
|
|
41
42
|
|
|
42
43
|
return visitor.offenses
|
|
43
44
|
}
|
|
45
|
+
|
|
46
|
+
autofix(offense: LintOffense<ERBRightTrimAutofixContext>, result: ParseResult, _context?: Partial<LintContext>): ParseResult | null {
|
|
47
|
+
if (!offense.autofixContext) return null
|
|
48
|
+
|
|
49
|
+
const { node } = offense.autofixContext
|
|
50
|
+
|
|
51
|
+
if (!node.tag_closing) return null
|
|
52
|
+
|
|
53
|
+
const closing = node.tag_closing
|
|
54
|
+
|
|
55
|
+
if (closing.value === "=%>") {
|
|
56
|
+
closing.value = "-%>"
|
|
57
|
+
return result
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (closing.value === "-%>") {
|
|
61
|
+
closing.value = "%>"
|
|
62
|
+
return result
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return null
|
|
66
|
+
}
|
|
44
67
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { BaseRuleVisitor } from "./rule-utils.js"
|
|
2
|
+
import { ERBContentNode, Location } from "@herb-tools/core"
|
|
3
|
+
|
|
4
|
+
import { parseHerbDisableContent } from "../herb-disable-comment-utils.js"
|
|
5
|
+
|
|
6
|
+
import type { LintContext } from "../types.js"
|
|
7
|
+
import type { HerbDisableComment, HerbDisableRuleName } from "../herb-disable-comment-utils.js"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Base visitor class for herb:disable comment validation rules.
|
|
11
|
+
* Handles common patterns like checking ERB comments and parsing herb:disable content.
|
|
12
|
+
*/
|
|
13
|
+
export abstract class HerbDisableCommentBaseVisitor extends BaseRuleVisitor {
|
|
14
|
+
constructor(ruleName: string, context?: Partial<LintContext>) {
|
|
15
|
+
super(ruleName, context)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
visitERBContentNode(node: ERBContentNode): void {
|
|
19
|
+
if (node.tag_opening?.value !== "<%#") return
|
|
20
|
+
|
|
21
|
+
const content = node.content?.value
|
|
22
|
+
if (!content) return
|
|
23
|
+
|
|
24
|
+
this.checkHerbDisableComment(node, content)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Override this method to implement rule-specific logic.
|
|
29
|
+
* This is called for every ERB comment node.
|
|
30
|
+
*/
|
|
31
|
+
protected abstract checkHerbDisableComment(node: ERBContentNode, content: string): void
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Helper to create a precise location for a specific rule name within the comment.
|
|
35
|
+
* Returns null if content location is not available.
|
|
36
|
+
*/
|
|
37
|
+
protected createRuleNameLocation(node: ERBContentNode, ruleDetail: HerbDisableRuleName): Location | null {
|
|
38
|
+
const contentLocation = node.content?.location
|
|
39
|
+
if (!contentLocation) return null
|
|
40
|
+
|
|
41
|
+
const startLine = contentLocation.start.line
|
|
42
|
+
const startColumn = contentLocation.start.column + ruleDetail.offset
|
|
43
|
+
|
|
44
|
+
return Location.from(
|
|
45
|
+
startLine,
|
|
46
|
+
startColumn,
|
|
47
|
+
startLine,
|
|
48
|
+
startColumn + ruleDetail.length
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Helper to add an offense with a fallback to node location if precise location unavailable.
|
|
54
|
+
*/
|
|
55
|
+
protected addOffenseWithFallback(message: string, preciseLocation: Location | null, node: ERBContentNode): void {
|
|
56
|
+
this.addOffense(message, preciseLocation || node.location)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Base visitor for rules that need to process parsed herb:disable comments.
|
|
62
|
+
* Only calls the abstract method if the content successfully parses as a herb:disable comment.
|
|
63
|
+
*/
|
|
64
|
+
export abstract class HerbDisableCommentParsedVisitor extends HerbDisableCommentBaseVisitor {
|
|
65
|
+
protected checkHerbDisableComment(node: ERBContentNode, content: string): void {
|
|
66
|
+
const herbDisable = parseHerbDisableContent(content)
|
|
67
|
+
if (!herbDisable) return
|
|
68
|
+
|
|
69
|
+
this.checkParsedHerbDisable(node, content, herbDisable)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Override this method to implement rule-specific logic for parsed herb:disable comments.
|
|
74
|
+
*/
|
|
75
|
+
protected abstract checkParsedHerbDisable(node: ERBContentNode, content: string, herbDisable: HerbDisableComment): void
|
|
76
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { ParserRule } from "../types.js"
|
|
2
|
+
import { HerbDisableCommentBaseVisitor } from "./herb-disable-comment-base.js"
|
|
3
|
+
import { parseHerbDisableContent } from "../herb-disable-comment-utils.js"
|
|
4
|
+
|
|
5
|
+
import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
6
|
+
import type { ERBContentNode, ParseResult } from "@herb-tools/core"
|
|
7
|
+
|
|
8
|
+
class HerbDisableCommentMalformedVisitor extends HerbDisableCommentBaseVisitor {
|
|
9
|
+
protected checkHerbDisableComment(node: ERBContentNode, content: string): void {
|
|
10
|
+
const trimmed = content.trim()
|
|
11
|
+
const looksLikeHerbDisable = trimmed.startsWith("herb:disable")
|
|
12
|
+
if (!looksLikeHerbDisable) return
|
|
13
|
+
|
|
14
|
+
if (trimmed.length > "herb:disable".length) {
|
|
15
|
+
const charAfterPrefix = trimmed["herb:disable".length]
|
|
16
|
+
|
|
17
|
+
if (charAfterPrefix !== ' ' && charAfterPrefix !== '\t' && charAfterPrefix !== '\n') {
|
|
18
|
+
this.addOffense(
|
|
19
|
+
"`herb:disable` comment is missing a space after `herb:disable`. Add a space before the rule names.",
|
|
20
|
+
node.location,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const afterPrefix = trimmed.substring("herb:disable".length).trim()
|
|
28
|
+
if (afterPrefix.length === 0) return
|
|
29
|
+
|
|
30
|
+
const parsed = parseHerbDisableContent(content)
|
|
31
|
+
if (parsed !== null) return
|
|
32
|
+
|
|
33
|
+
let message = "`herb:disable` comment is malformed."
|
|
34
|
+
|
|
35
|
+
const rulesString = afterPrefix.trim()
|
|
36
|
+
|
|
37
|
+
if (rulesString.endsWith(',')) {
|
|
38
|
+
message = "`herb:disable` comment has a trailing comma. Remove the trailing comma."
|
|
39
|
+
} else if (rulesString.includes(',,') || rulesString.match(/,\s*,/)) {
|
|
40
|
+
message = "`herb:disable` comment has consecutive commas. Remove extra commas."
|
|
41
|
+
} else if (rulesString.startsWith(',')) {
|
|
42
|
+
message = "`herb:disable` comment starts with a comma. Remove the leading comma."
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this.addOffense(message, node.location)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class HerbDisableCommentMalformedRule extends ParserRule {
|
|
50
|
+
name = "herb-disable-comment-malformed"
|
|
51
|
+
|
|
52
|
+
get defaultConfig(): FullRuleConfig {
|
|
53
|
+
return {
|
|
54
|
+
enabled: true,
|
|
55
|
+
severity: "error"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
60
|
+
const visitor = new HerbDisableCommentMalformedVisitor(this.name, context)
|
|
61
|
+
|
|
62
|
+
visitor.visit(result.value)
|
|
63
|
+
|
|
64
|
+
return visitor.offenses
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ParserRule } from "../types.js"
|
|
2
|
+
import { HerbDisableCommentBaseVisitor } from "./herb-disable-comment-base.js"
|
|
3
|
+
|
|
4
|
+
import { parseHerbDisableContent } from "../herb-disable-comment-utils.js"
|
|
5
|
+
|
|
6
|
+
import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
7
|
+
import type { ERBContentNode, ParseResult } from "@herb-tools/core"
|
|
8
|
+
|
|
9
|
+
class HerbDisableCommentMissingRulesVisitor extends HerbDisableCommentBaseVisitor {
|
|
10
|
+
protected checkHerbDisableComment(node: ERBContentNode, content: string): void {
|
|
11
|
+
const herbDisable = parseHerbDisableContent(content)
|
|
12
|
+
if (herbDisable) return
|
|
13
|
+
|
|
14
|
+
const emptyFormat = /^\s*herb:disable\s*$/
|
|
15
|
+
if (!emptyFormat.test(content)) return
|
|
16
|
+
|
|
17
|
+
this.addOffense(
|
|
18
|
+
`\`herb:disable\` comment is missing rule names. Specify \`all\` or list specific rules to disable.`,
|
|
19
|
+
node.location,
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class HerbDisableCommentMissingRulesRule extends ParserRule {
|
|
25
|
+
name = "herb-disable-comment-missing-rules"
|
|
26
|
+
|
|
27
|
+
get defaultConfig(): FullRuleConfig {
|
|
28
|
+
return {
|
|
29
|
+
enabled: true,
|
|
30
|
+
severity: "error"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
35
|
+
const visitor = new HerbDisableCommentMissingRulesVisitor(this.name, context)
|
|
36
|
+
|
|
37
|
+
visitor.visit(result.value)
|
|
38
|
+
|
|
39
|
+
return visitor.offenses
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ParserRule } from "../types.js"
|
|
2
|
+
import { HerbDisableCommentParsedVisitor } from "./herb-disable-comment-base.js"
|
|
3
|
+
|
|
4
|
+
import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
5
|
+
import type { ERBContentNode, ParseResult } from "@herb-tools/core"
|
|
6
|
+
import type { HerbDisableComment } from "../herb-disable-comment-utils.js"
|
|
7
|
+
|
|
8
|
+
class HerbDisableCommentNoDuplicateRulesVisitor extends HerbDisableCommentParsedVisitor {
|
|
9
|
+
protected checkParsedHerbDisable(node: ERBContentNode, _content: string, herbDisable: HerbDisableComment): void {
|
|
10
|
+
const seenRules = new Map<string, number>()
|
|
11
|
+
|
|
12
|
+
herbDisable.ruleNameDetails.forEach((ruleDetail, index) => {
|
|
13
|
+
const firstIndex = seenRules.get(ruleDetail.name)
|
|
14
|
+
|
|
15
|
+
if (firstIndex !== undefined) {
|
|
16
|
+
const location = this.createRuleNameLocation(node, ruleDetail)
|
|
17
|
+
const message = `Duplicate rule \`${ruleDetail.name}\` in \`herb:disable\` comment. Remove the duplicate.`
|
|
18
|
+
|
|
19
|
+
this.addOffenseWithFallback(message, location, node)
|
|
20
|
+
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
seenRules.set(ruleDetail.name, index)
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class HerbDisableCommentNoDuplicateRulesRule extends ParserRule {
|
|
30
|
+
name = "herb-disable-comment-no-duplicate-rules"
|
|
31
|
+
|
|
32
|
+
get defaultConfig(): FullRuleConfig {
|
|
33
|
+
return {
|
|
34
|
+
enabled: true,
|
|
35
|
+
severity: "warning"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
40
|
+
const visitor = new HerbDisableCommentNoDuplicateRulesVisitor(this.name, context)
|
|
41
|
+
|
|
42
|
+
visitor.visit(result.value)
|
|
43
|
+
|
|
44
|
+
return visitor.offenses
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ParserRule } from "../types.js"
|
|
2
|
+
import { HerbDisableCommentParsedVisitor } from "./herb-disable-comment-base.js"
|
|
3
|
+
|
|
4
|
+
import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
5
|
+
import type { ERBContentNode, ParseResult } from "@herb-tools/core"
|
|
6
|
+
import type { HerbDisableComment } from "../herb-disable-comment-utils.js"
|
|
7
|
+
|
|
8
|
+
class HerbDisableCommentNoRedundantAllVisitor extends HerbDisableCommentParsedVisitor {
|
|
9
|
+
protected checkParsedHerbDisable(node: ERBContentNode, _content: string, herbDisable: HerbDisableComment): void {
|
|
10
|
+
if (!herbDisable.ruleNames.includes("all")) return
|
|
11
|
+
if (herbDisable.ruleNames.length <= 1) return
|
|
12
|
+
|
|
13
|
+
const allDetail = herbDisable.ruleNameDetails.find(detail => detail.name === "all")
|
|
14
|
+
if (!allDetail) return
|
|
15
|
+
|
|
16
|
+
const location = this.createRuleNameLocation(node, allDetail)
|
|
17
|
+
const message = `Using \`all\` with specific rules is redundant. Use \`herb:disable all\` by itself or list only specific rules.`
|
|
18
|
+
|
|
19
|
+
this.addOffenseWithFallback(message, location, node)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class HerbDisableCommentNoRedundantAllRule extends ParserRule {
|
|
24
|
+
name = "herb-disable-comment-no-redundant-all"
|
|
25
|
+
|
|
26
|
+
get defaultConfig(): FullRuleConfig {
|
|
27
|
+
return {
|
|
28
|
+
enabled: true,
|
|
29
|
+
severity: "warning"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
34
|
+
const visitor = new HerbDisableCommentNoRedundantAllVisitor(this.name, context)
|
|
35
|
+
|
|
36
|
+
visitor.visit(result.value)
|
|
37
|
+
|
|
38
|
+
return visitor.offenses
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { ParserRule } from "../types.js"
|
|
2
|
+
import { HerbDisableCommentParsedVisitor } from "./herb-disable-comment-base.js"
|
|
3
|
+
|
|
4
|
+
import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
5
|
+
import type { ERBContentNode, ParseResult } from "@herb-tools/core"
|
|
6
|
+
import type { HerbDisableComment } from "../herb-disable-comment-utils.js"
|
|
7
|
+
|
|
8
|
+
class HerbDisableCommentUnnecessaryVisitor extends HerbDisableCommentParsedVisitor {
|
|
9
|
+
private ignoredOffensesByLine: Map<number, Set<string>>
|
|
10
|
+
private validRuleNames: Set<string>
|
|
11
|
+
|
|
12
|
+
constructor(ruleName: string, ignoredOffensesByLine: Map<number, Set<string>>, validRuleNames: string[], context?: Partial<LintContext>) {
|
|
13
|
+
super(ruleName, context)
|
|
14
|
+
|
|
15
|
+
this.ignoredOffensesByLine = ignoredOffensesByLine
|
|
16
|
+
this.validRuleNames = new Set([...validRuleNames, "all"])
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
protected checkParsedHerbDisable(node: ERBContentNode, _content: string, herbDisable: HerbDisableComment): void {
|
|
20
|
+
const line = node.location.start.line
|
|
21
|
+
const usedRuleNames = this.ignoredOffensesByLine.get(line) || new Set<string>()
|
|
22
|
+
|
|
23
|
+
if (herbDisable.ruleNames.includes("all")) {
|
|
24
|
+
if (herbDisable.ruleNames.length > 1) return
|
|
25
|
+
if (usedRuleNames.size > 0) return
|
|
26
|
+
|
|
27
|
+
this.addOffense(
|
|
28
|
+
`No offenses to disable on this line. Remove the \`herb:disable all\` comment.`,
|
|
29
|
+
node.location,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const unnecessaryRules = herbDisable.ruleNameDetails.filter(
|
|
36
|
+
detail => this.validRuleNames.has(detail.name) && !usedRuleNames.has(detail.name)
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
if (unnecessaryRules.length === 0) return
|
|
40
|
+
|
|
41
|
+
const validRuleCount = herbDisable.ruleNames.filter(name => this.validRuleNames.has(name)).length
|
|
42
|
+
|
|
43
|
+
if (unnecessaryRules.length === validRuleCount) {
|
|
44
|
+
if (unnecessaryRules.length === 1) {
|
|
45
|
+
const ruleName = unnecessaryRules[0].name
|
|
46
|
+
|
|
47
|
+
this.addOffense(
|
|
48
|
+
`No offenses from \`${ruleName}\` on this line. Remove the \`herb:disable\` comment.`,
|
|
49
|
+
node.location,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const unnecessaryRuleNames = unnecessaryRules.map(rule => `\`${rule.name}\``).join(", ")
|
|
56
|
+
|
|
57
|
+
this.addOffense(
|
|
58
|
+
`No offenses from rules ${unnecessaryRuleNames} on this line. Remove them from the \`herb:disable\` comment.`,
|
|
59
|
+
node.location,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (const unnecessaryRule of unnecessaryRules) {
|
|
66
|
+
const location = this.createRuleNameLocation(node, unnecessaryRule)
|
|
67
|
+
const message = `No offenses from \`${unnecessaryRule.name}\` on this line. Remove it from the \`herb:disable\` comment.`
|
|
68
|
+
|
|
69
|
+
this.addOffenseWithFallback(message, location, node)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export class HerbDisableCommentUnnecessaryRule extends ParserRule {
|
|
75
|
+
name = "herb-disable-comment-unnecessary"
|
|
76
|
+
|
|
77
|
+
get defaultConfig(): FullRuleConfig {
|
|
78
|
+
return {
|
|
79
|
+
enabled: true,
|
|
80
|
+
severity: "warning"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
85
|
+
const validRuleNames = context?.validRuleNames
|
|
86
|
+
const ignoredOffensesByLine = context?.ignoredOffensesByLine
|
|
87
|
+
|
|
88
|
+
if (!validRuleNames) return []
|
|
89
|
+
if (validRuleNames.length === 0) return []
|
|
90
|
+
if (!ignoredOffensesByLine) return []
|
|
91
|
+
|
|
92
|
+
const visitor = new HerbDisableCommentUnnecessaryVisitor(
|
|
93
|
+
this.name,
|
|
94
|
+
ignoredOffensesByLine,
|
|
95
|
+
validRuleNames,
|
|
96
|
+
context
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
visitor.visit(result.value)
|
|
100
|
+
|
|
101
|
+
return visitor.offenses
|
|
102
|
+
}
|
|
103
|
+
}
|