@herb-tools/linter 0.8.9 → 0.9.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 +5 -5
- package/dist/{src/cli → cli}/argument-parser.js +15 -2
- package/dist/cli/argument-parser.js.map +1 -0
- package/dist/{src/cli → cli}/file-processor.js +155 -9
- package/dist/cli/file-processor.js.map +1 -0
- package/dist/cli/file-url.js +6 -0
- package/dist/cli/file-url.js.map +1 -0
- package/dist/cli/formatters/base-formatter.js.map +1 -0
- package/dist/{src/cli → cli}/formatters/detailed-formatter.js +16 -19
- package/dist/cli/formatters/detailed-formatter.js.map +1 -0
- package/dist/cli/formatters/github-actions-formatter.js.map +1 -0
- package/dist/cli/formatters/index.js.map +1 -0
- package/dist/cli/formatters/json-formatter.js.map +1 -0
- package/dist/cli/formatters/simple-formatter.js +54 -0
- package/dist/cli/formatters/simple-formatter.js.map +1 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/lint-worker.js +143 -0
- package/dist/cli/lint-worker.js.map +1 -0
- package/dist/cli/output-manager.js.map +1 -0
- package/dist/{src/cli → cli}/summary-reporter.js +13 -16
- package/dist/cli/summary-reporter.js.map +1 -0
- package/dist/{src/cli.js → cli.js} +5 -3
- package/dist/cli.js.map +1 -0
- package/dist/{src/custom-rule-loader.js → custom-rule-loader.js} +20 -4
- package/dist/custom-rule-loader.js.map +1 -0
- package/dist/herb-disable-comment-utils.js.map +1 -0
- package/dist/herb-lint.js +60648 -17513
- package/dist/herb-lint.js.map +1 -1
- package/dist/index.cjs +2621 -934
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2554 -873
- package/dist/index.js.map +1 -1
- package/dist/lint-worker.js +71462 -0
- package/dist/lint-worker.js.map +1 -0
- package/dist/linter-ignore.js.map +1 -0
- package/dist/{src/linter.js → linter.js} +89 -74
- package/dist/linter.js.map +1 -0
- package/dist/loader.cjs +31206 -7834
- package/dist/loader.cjs.map +1 -1
- package/dist/loader.js +31168 -7802
- package/dist/loader.js.map +1 -1
- package/dist/parse-cache.js +30 -0
- package/dist/parse-cache.js.map +1 -0
- package/dist/rules/actionview-no-silent-helper.js +45 -0
- package/dist/rules/actionview-no-silent-helper.js.map +1 -0
- package/dist/{src/rules → rules}/erb-comment-syntax.js +2 -2
- package/dist/rules/erb-comment-syntax.js.map +1 -0
- package/dist/{src/rules → rules}/erb-no-case-node-children.js +2 -2
- package/dist/rules/erb-no-case-node-children.js.map +1 -0
- package/dist/rules/erb-no-conditional-html-element.js +38 -0
- package/dist/rules/erb-no-conditional-html-element.js.map +1 -0
- package/dist/rules/erb-no-conditional-open-tag.js +24 -0
- package/dist/rules/erb-no-conditional-open-tag.js.map +1 -0
- package/dist/rules/erb-no-duplicate-branch-elements.js +245 -0
- package/dist/rules/erb-no-duplicate-branch-elements.js.map +1 -0
- package/dist/{src/rules → rules}/erb-no-empty-tags.js +2 -2
- package/dist/rules/erb-no-empty-tags.js.map +1 -0
- package/dist/{src/rules → rules}/erb-no-extra-newline.js +4 -21
- package/dist/rules/erb-no-extra-newline.js.map +1 -0
- package/dist/{src/rules → rules}/erb-no-extra-whitespace-inside-tags.js +39 -13
- package/dist/rules/erb-no-extra-whitespace-inside-tags.js.map +1 -0
- package/dist/rules/erb-no-inline-case-conditions.js +40 -0
- package/dist/rules/erb-no-inline-case-conditions.js.map +1 -0
- package/dist/rules/erb-no-instance-variables-in-partials.js +67 -0
- package/dist/rules/erb-no-instance-variables-in-partials.js.map +1 -0
- package/dist/rules/erb-no-interpolated-class-names.js +47 -0
- package/dist/rules/erb-no-interpolated-class-names.js.map +1 -0
- package/dist/rules/erb-no-javascript-tag-helper.js +34 -0
- package/dist/rules/erb-no-javascript-tag-helper.js.map +1 -0
- package/dist/{src/rules → rules}/erb-no-output-control-flow.js +9 -12
- package/dist/rules/erb-no-output-control-flow.js.map +1 -0
- package/dist/rules/erb-no-output-in-attribute-name.js +30 -0
- package/dist/rules/erb-no-output-in-attribute-name.js.map +1 -0
- package/dist/rules/erb-no-output-in-attribute-position.js +30 -0
- package/dist/rules/erb-no-output-in-attribute-position.js.map +1 -0
- package/dist/rules/erb-no-raw-output-in-attribute-value.js +35 -0
- package/dist/rules/erb-no-raw-output-in-attribute-value.js.map +1 -0
- package/dist/{src/rules → rules}/erb-no-silent-tag-in-attribute-name.js +2 -2
- package/dist/rules/erb-no-silent-tag-in-attribute-name.js.map +1 -0
- package/dist/rules/erb-no-statement-in-script.js +58 -0
- package/dist/rules/erb-no-statement-in-script.js.map +1 -0
- package/dist/rules/erb-no-then-in-control-flow.js +45 -0
- package/dist/rules/erb-no-then-in-control-flow.js.map +1 -0
- package/dist/rules/erb-no-trailing-whitespace.js +138 -0
- package/dist/rules/erb-no-trailing-whitespace.js.map +1 -0
- package/dist/rules/erb-no-unsafe-js-attribute.js +36 -0
- package/dist/rules/erb-no-unsafe-js-attribute.js.map +1 -0
- package/dist/rules/erb-no-unsafe-raw.js +63 -0
- package/dist/rules/erb-no-unsafe-raw.js.map +1 -0
- package/dist/rules/erb-no-unsafe-script-interpolation.js +54 -0
- package/dist/rules/erb-no-unsafe-script-interpolation.js.map +1 -0
- package/dist/{src/rules → rules}/erb-prefer-image-tag-helper.js +5 -4
- package/dist/rules/erb-prefer-image-tag-helper.js.map +1 -0
- package/dist/{src/rules → rules}/erb-require-trailing-newline.js +2 -2
- package/dist/rules/erb-require-trailing-newline.js.map +1 -0
- package/dist/{src/rules → rules}/erb-require-whitespace-inside-tags.js +39 -15
- package/dist/rules/erb-require-whitespace-inside-tags.js.map +1 -0
- package/dist/{src/rules → rules}/erb-right-trim.js +2 -2
- package/dist/rules/erb-right-trim.js.map +1 -0
- package/dist/{src/rules → rules}/erb-strict-locals-comment-syntax.js +5 -5
- package/dist/rules/erb-strict-locals-comment-syntax.js.map +1 -0
- package/dist/{src/rules → rules}/erb-strict-locals-required.js +2 -2
- package/dist/rules/erb-strict-locals-required.js.map +1 -0
- package/dist/rules/file-utils.js.map +1 -0
- package/dist/rules/herb-disable-comment-base.js.map +1 -0
- package/dist/{src/rules → rules}/herb-disable-comment-malformed.js +2 -2
- package/dist/rules/herb-disable-comment-malformed.js.map +1 -0
- package/dist/{src/rules → rules}/herb-disable-comment-missing-rules.js +2 -2
- package/dist/rules/herb-disable-comment-missing-rules.js.map +1 -0
- package/dist/{src/rules → rules}/herb-disable-comment-no-duplicate-rules.js +2 -2
- package/dist/rules/herb-disable-comment-no-duplicate-rules.js.map +1 -0
- package/dist/{src/rules → rules}/herb-disable-comment-no-redundant-all.js +2 -2
- package/dist/rules/herb-disable-comment-no-redundant-all.js.map +1 -0
- package/dist/{src/rules → rules}/herb-disable-comment-unnecessary.js +2 -2
- package/dist/rules/herb-disable-comment-unnecessary.js.map +1 -0
- package/dist/{src/rules → rules}/herb-disable-comment-valid-rule-name.js +2 -2
- package/dist/rules/herb-disable-comment-valid-rule-name.js.map +1 -0
- package/dist/rules/html-allowed-script-type.js +57 -0
- package/dist/rules/html-allowed-script-type.js.map +1 -0
- package/dist/rules/html-anchor-require-href.js +68 -0
- package/dist/rules/html-anchor-require-href.js.map +1 -0
- package/dist/{src/rules → rules}/html-aria-attribute-must-be-valid.js +3 -3
- package/dist/rules/html-aria-attribute-must-be-valid.js.map +1 -0
- package/dist/{src/rules → rules}/html-aria-label-is-well-formatted.js +3 -3
- package/dist/rules/html-aria-label-is-well-formatted.js.map +1 -0
- package/dist/{src/rules → rules}/html-aria-level-must-be-valid.js +3 -3
- package/dist/rules/html-aria-level-must-be-valid.js.map +1 -0
- package/dist/{src/rules → rules}/html-aria-role-heading-requires-level.js +5 -4
- package/dist/rules/html-aria-role-heading-requires-level.js.map +1 -0
- package/dist/{src/rules → rules}/html-aria-role-must-be-valid.js +3 -3
- package/dist/rules/html-aria-role-must-be-valid.js.map +1 -0
- package/dist/{src/rules → rules}/html-attribute-double-quotes.js +4 -4
- package/dist/rules/html-attribute-double-quotes.js.map +1 -0
- package/dist/{src/rules → rules}/html-attribute-equals-spacing.js +2 -2
- package/dist/rules/html-attribute-equals-spacing.js.map +1 -0
- package/dist/{src/rules → rules}/html-attribute-values-require-quotes.js +2 -2
- package/dist/rules/html-attribute-values-require-quotes.js.map +1 -0
- package/dist/{src/rules → rules}/html-avoid-both-disabled-and-aria-disabled.js +9 -9
- package/dist/rules/html-avoid-both-disabled-and-aria-disabled.js.map +1 -0
- package/dist/{src/rules → rules}/html-body-only-elements.js +5 -4
- package/dist/rules/html-body-only-elements.js.map +1 -0
- package/dist/{src/rules → rules}/html-boolean-attributes-no-value.js +4 -3
- package/dist/rules/html-boolean-attributes-no-value.js.map +1 -0
- package/dist/rules/html-details-has-summary.js +52 -0
- package/dist/rules/html-details-has-summary.js.map +1 -0
- package/dist/{src/rules → rules}/html-head-only-elements.js +6 -5
- package/dist/rules/html-head-only-elements.js.map +1 -0
- package/dist/{src/rules → rules}/html-iframe-has-title.js +8 -11
- package/dist/rules/html-iframe-has-title.js.map +1 -0
- package/dist/{src/rules → rules}/html-img-require-alt.js +11 -5
- package/dist/rules/html-img-require-alt.js.map +1 -0
- package/dist/{src/rules → rules}/html-input-require-autocomplete.js +7 -10
- package/dist/rules/html-input-require-autocomplete.js.map +1 -0
- package/dist/{src/rules → rules}/html-navigation-has-label.js +6 -5
- package/dist/rules/html-navigation-has-label.js.map +1 -0
- package/dist/rules/html-no-abstract-roles.js +29 -0
- package/dist/rules/html-no-abstract-roles.js.map +1 -0
- package/dist/rules/html-no-aria-hidden-on-body.js +42 -0
- package/dist/rules/html-no-aria-hidden-on-body.js.map +1 -0
- package/dist/{src/rules → rules}/html-no-aria-hidden-on-focusable.js +6 -5
- package/dist/rules/html-no-aria-hidden-on-focusable.js.map +1 -0
- package/dist/{src/rules → rules}/html-no-block-inside-inline.js +6 -9
- package/dist/rules/html-no-block-inside-inline.js.map +1 -0
- package/dist/{src/rules → rules}/html-no-duplicate-attributes.js +4 -3
- package/dist/rules/html-no-duplicate-attributes.js.map +1 -0
- package/dist/{src/rules → rules}/html-no-duplicate-ids.js +14 -11
- package/dist/rules/html-no-duplicate-ids.js.map +1 -0
- package/dist/{src/rules → rules}/html-no-duplicate-meta-names.js +22 -20
- package/dist/rules/html-no-duplicate-meta-names.js.map +1 -0
- package/dist/{src/rules → rules}/html-no-empty-attributes.js +2 -2
- package/dist/rules/html-no-empty-attributes.js.map +1 -0
- package/dist/rules/html-no-empty-headings.js +98 -0
- package/dist/rules/html-no-empty-headings.js.map +1 -0
- package/dist/{src/rules → rules}/html-no-nested-links.js +23 -15
- package/dist/rules/html-no-nested-links.js.map +1 -0
- package/dist/{src/rules → rules}/html-no-positive-tab-index.js +3 -3
- package/dist/rules/html-no-positive-tab-index.js.map +1 -0
- package/dist/{src/rules → rules}/html-no-self-closing.js +4 -4
- package/dist/rules/html-no-self-closing.js.map +1 -0
- package/dist/{src/rules → rules}/html-no-space-in-tag.js +4 -6
- package/dist/rules/html-no-space-in-tag.js.map +1 -0
- package/dist/{src/rules → rules}/html-no-title-attribute.js +6 -5
- package/dist/rules/html-no-title-attribute.js.map +1 -0
- package/dist/{src/rules → rules}/html-no-underscores-in-attribute-names.js +2 -2
- package/dist/rules/html-no-underscores-in-attribute-names.js.map +1 -0
- package/dist/rules/html-require-closing-tags.js +29 -0
- package/dist/rules/html-require-closing-tags.js.map +1 -0
- package/dist/{src/rules → rules}/html-tag-name-lowercase.js +13 -9
- package/dist/rules/html-tag-name-lowercase.js.map +1 -0
- package/dist/{src/rules → rules}/index.js +19 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/{src/rules → rules}/parser-no-errors.js +3 -3
- package/dist/rules/parser-no-errors.js.map +1 -0
- package/dist/{src/rules → rules}/rule-utils.js +141 -219
- package/dist/rules/rule-utils.js.map +1 -0
- package/dist/rules/string-utils.js.map +1 -0
- package/dist/{src/rules → rules}/svg-tag-name-capitalization.js +7 -6
- package/dist/rules/svg-tag-name-capitalization.js.map +1 -0
- package/dist/rules/turbo-permanent-require-id.js +34 -0
- package/dist/rules/turbo-permanent-require-id.js.map +1 -0
- package/dist/{src/rules.js → rules.js} +56 -10
- package/dist/rules.js.map +1 -0
- package/dist/types/cli/argument-parser.d.ts +1 -0
- package/dist/types/cli/file-processor.d.ts +13 -0
- package/dist/types/cli/file-url.d.ts +1 -0
- package/dist/types/cli/index.d.ts +1 -0
- package/dist/types/cli/lint-worker.d.ts +34 -0
- package/dist/types/custom-rule-loader.d.ts +4 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/linter.d.ts +13 -6
- package/dist/types/parse-cache.d.ts +9 -0
- package/dist/types/{src/rules/html-aria-level-must-be-valid.d.ts → rules/actionview-no-silent-helper.d.ts} +4 -3
- package/dist/types/rules/erb-comment-syntax.d.ts +1 -1
- package/dist/types/rules/erb-no-case-node-children.d.ts +1 -1
- package/dist/types/{src/rules/herb-disable-comment-malformed.d.ts → rules/erb-no-conditional-html-element.d.ts} +3 -3
- package/dist/types/{src/rules/erb-prefer-image-tag-helper.d.ts → rules/erb-no-conditional-open-tag.d.ts} +3 -3
- package/dist/types/rules/erb-no-duplicate-branch-elements.d.ts +17 -0
- package/dist/types/rules/erb-no-empty-tags.d.ts +1 -1
- package/dist/types/rules/erb-no-extra-newline.d.ts +1 -1
- package/dist/types/rules/erb-no-extra-whitespace-inside-tags.d.ts +1 -1
- package/dist/types/{src/rules/html-no-duplicate-attributes.d.ts → rules/erb-no-inline-case-conditions.d.ts} +4 -3
- package/dist/types/rules/erb-no-instance-variables-in-partials.d.ts +10 -0
- package/dist/types/{src/rules/html-no-aria-hidden-on-focusable.d.ts → rules/erb-no-interpolated-class-names.d.ts} +2 -2
- package/dist/types/{src/rules/html-aria-attribute-must-be-valid.d.ts → rules/erb-no-javascript-tag-helper.d.ts} +2 -2
- package/dist/types/rules/erb-no-output-control-flow.d.ts +1 -1
- package/dist/types/{src/rules/erb-no-silent-tag-in-attribute-name.d.ts → rules/erb-no-output-in-attribute-name.d.ts} +2 -2
- package/dist/types/{src/rules/herb-disable-comment-missing-rules.d.ts → rules/erb-no-output-in-attribute-position.d.ts} +2 -2
- package/dist/types/{src/rules/erb-no-empty-tags.d.ts → rules/erb-no-raw-output-in-attribute-value.d.ts} +2 -2
- package/dist/types/rules/erb-no-silent-tag-in-attribute-name.d.ts +1 -1
- package/dist/types/{src/rules/html-navigation-has-label.d.ts → rules/erb-no-statement-in-script.d.ts} +2 -2
- package/dist/types/rules/erb-no-then-in-control-flow.d.ts +9 -0
- package/dist/types/rules/erb-no-trailing-whitespace.d.ts +19 -0
- package/dist/types/{src/rules/html-no-positive-tab-index.d.ts → rules/erb-no-unsafe-js-attribute.d.ts} +2 -2
- package/dist/types/{src/rules/erb-no-case-node-children.d.ts → rules/erb-no-unsafe-raw.d.ts} +2 -2
- package/dist/types/rules/erb-no-unsafe-script-interpolation.d.ts +8 -0
- package/dist/types/rules/erb-prefer-image-tag-helper.d.ts +1 -1
- package/dist/types/rules/erb-require-trailing-newline.d.ts +1 -1
- package/dist/types/rules/erb-require-whitespace-inside-tags.d.ts +1 -1
- package/dist/types/rules/erb-right-trim.d.ts +1 -1
- package/dist/types/rules/erb-strict-locals-comment-syntax.d.ts +1 -1
- package/dist/types/rules/erb-strict-locals-required.d.ts +1 -1
- package/dist/types/rules/herb-disable-comment-malformed.d.ts +1 -1
- package/dist/types/rules/herb-disable-comment-missing-rules.d.ts +1 -1
- package/dist/types/rules/herb-disable-comment-no-duplicate-rules.d.ts +1 -1
- package/dist/types/rules/herb-disable-comment-no-redundant-all.d.ts +1 -1
- package/dist/types/rules/herb-disable-comment-unnecessary.d.ts +1 -1
- package/dist/types/rules/herb-disable-comment-valid-rule-name.d.ts +1 -1
- package/dist/types/{src/rules/html-anchor-require-href.d.ts → rules/html-allowed-script-type.d.ts} +2 -2
- package/dist/types/rules/html-anchor-require-href.d.ts +3 -2
- package/dist/types/rules/html-aria-attribute-must-be-valid.d.ts +1 -1
- package/dist/types/rules/html-aria-label-is-well-formatted.d.ts +1 -1
- package/dist/types/rules/html-aria-level-must-be-valid.d.ts +1 -1
- package/dist/types/rules/html-aria-role-heading-requires-level.d.ts +1 -1
- package/dist/types/rules/html-aria-role-must-be-valid.d.ts +1 -1
- package/dist/types/rules/html-attribute-double-quotes.d.ts +1 -1
- package/dist/types/rules/html-attribute-equals-spacing.d.ts +1 -1
- package/dist/types/rules/html-attribute-values-require-quotes.d.ts +1 -1
- package/dist/types/rules/html-avoid-both-disabled-and-aria-disabled.d.ts +1 -1
- package/dist/types/rules/html-body-only-elements.d.ts +1 -1
- package/dist/types/rules/html-boolean-attributes-no-value.d.ts +1 -1
- package/dist/types/{src/rules/html-no-empty-attributes.d.ts → rules/html-details-has-summary.d.ts} +4 -3
- package/dist/types/rules/html-head-only-elements.d.ts +1 -1
- package/dist/types/rules/html-iframe-has-title.d.ts +1 -1
- package/dist/types/rules/html-img-require-alt.d.ts +1 -1
- package/dist/types/rules/html-input-require-autocomplete.d.ts +1 -1
- package/dist/types/rules/html-navigation-has-label.d.ts +1 -1
- package/dist/types/{src/rules/html-no-empty-headings.d.ts → rules/html-no-abstract-roles.d.ts} +2 -2
- package/dist/types/{src/rules/erb-no-output-control-flow.d.ts → rules/html-no-aria-hidden-on-body.d.ts} +3 -3
- package/dist/types/rules/html-no-aria-hidden-on-focusable.d.ts +1 -1
- package/dist/types/rules/html-no-block-inside-inline.d.ts +1 -1
- package/dist/types/rules/html-no-duplicate-attributes.d.ts +1 -1
- package/dist/types/rules/html-no-duplicate-ids.d.ts +1 -1
- package/dist/types/rules/html-no-duplicate-meta-names.d.ts +1 -1
- package/dist/types/rules/html-no-empty-attributes.d.ts +1 -1
- package/dist/types/rules/html-no-empty-headings.d.ts +1 -1
- package/dist/types/rules/html-no-nested-links.d.ts +1 -1
- package/dist/types/rules/html-no-positive-tab-index.d.ts +1 -1
- package/dist/types/rules/html-no-self-closing.d.ts +1 -1
- package/dist/types/rules/html-no-space-in-tag.d.ts +1 -1
- package/dist/types/rules/html-no-title-attribute.d.ts +1 -1
- package/dist/types/rules/html-no-underscores-in-attribute-names.d.ts +1 -1
- package/dist/types/{src/rules/html-body-only-elements.d.ts → rules/html-require-closing-tags.d.ts} +4 -3
- package/dist/types/rules/html-tag-name-lowercase.d.ts +1 -1
- package/dist/types/rules/index.d.ts +19 -0
- package/dist/types/rules/parser-no-errors.d.ts +1 -1
- package/dist/types/rules/rule-utils.d.ts +35 -88
- package/dist/types/rules/svg-tag-name-capitalization.d.ts +1 -1
- package/dist/types/{src/rules/html-aria-role-must-be-valid.d.ts → rules/turbo-permanent-require-id.d.ts} +2 -2
- package/dist/types/types.d.ts +25 -7
- package/dist/types/urls.d.ts +1 -0
- package/dist/{src/types.js → types.js} +53 -0
- package/dist/types.js.map +1 -0
- package/dist/urls.js +5 -0
- package/dist/urls.js.map +1 -0
- package/docs/rules/README.md +23 -2
- package/docs/rules/actionview-no-silent-helper.md +57 -0
- package/docs/rules/erb-no-conditional-html-element.md +90 -0
- package/docs/rules/erb-no-conditional-open-tag.md +130 -0
- package/docs/rules/erb-no-duplicate-branch-elements.md +98 -0
- package/docs/rules/erb-no-inline-case-conditions.md +85 -0
- package/docs/rules/erb-no-instance-variables-in-partials.md +43 -0
- package/docs/rules/erb-no-interpolated-class-names.md +57 -0
- package/docs/rules/erb-no-javascript-tag-helper.md +33 -0
- package/docs/rules/erb-no-output-in-attribute-name.md +38 -0
- package/docs/rules/erb-no-output-in-attribute-position.md +60 -0
- package/docs/rules/erb-no-raw-output-in-attribute-value.md +37 -0
- package/docs/rules/erb-no-statement-in-script.md +68 -0
- package/docs/rules/erb-no-then-in-control-flow.md +86 -0
- package/docs/rules/erb-no-trailing-whitespace.md +69 -0
- package/docs/rules/erb-no-unsafe-js-attribute.md +41 -0
- package/docs/rules/erb-no-unsafe-raw.md +47 -0
- package/docs/rules/erb-no-unsafe-script-interpolation.md +73 -0
- package/docs/rules/html-allowed-script-type.md +59 -0
- package/docs/rules/html-anchor-require-href.md +19 -6
- package/docs/rules/html-details-has-summary.md +46 -0
- package/docs/rules/html-img-require-alt.md +5 -3
- package/docs/rules/html-no-abstract-roles.md +74 -0
- package/docs/rules/html-no-aria-hidden-on-body.md +44 -0
- package/docs/rules/html-require-closing-tags.md +142 -0
- package/docs/rules/parser-no-errors.md +4 -17
- package/docs/rules/turbo-permanent-require-id.md +41 -0
- package/package.json +12 -11
- package/src/cli/argument-parser.ts +20 -2
- package/src/cli/file-processor.ts +189 -10
- package/src/cli/file-url.ts +6 -0
- package/src/cli/formatters/detailed-formatter.ts +19 -21
- package/src/cli/formatters/simple-formatter.ts +23 -13
- package/src/cli/index.ts +2 -0
- package/src/cli/lint-worker.ts +208 -0
- package/src/cli/summary-reporter.ts +14 -15
- package/src/cli.ts +5 -3
- package/src/custom-rule-loader.ts +20 -5
- package/src/herb-disable-comment-utils.ts +0 -3
- package/src/index.ts +1 -0
- package/src/linter.ts +98 -79
- package/src/parse-cache.ts +39 -0
- package/src/rules/actionview-no-silent-helper.ts +58 -0
- package/src/rules/erb-comment-syntax.ts +2 -2
- package/src/rules/erb-no-case-node-children.ts +2 -2
- package/src/rules/erb-no-conditional-html-element.ts +53 -0
- package/src/rules/erb-no-conditional-open-tag.ts +37 -0
- package/src/rules/erb-no-duplicate-branch-elements.ts +320 -0
- package/src/rules/erb-no-empty-tags.ts +2 -2
- package/src/rules/erb-no-extra-newline.ts +5 -25
- package/src/rules/erb-no-extra-whitespace-inside-tags.ts +45 -15
- package/src/rules/erb-no-inline-case-conditions.ts +54 -0
- package/src/rules/erb-no-instance-variables-in-partials.ts +101 -0
- package/src/rules/erb-no-interpolated-class-names.ts +65 -0
- package/src/rules/erb-no-javascript-tag-helper.ts +47 -0
- package/src/rules/erb-no-output-control-flow.ts +10 -10
- package/src/rules/erb-no-output-in-attribute-name.ts +39 -0
- package/src/rules/erb-no-output-in-attribute-position.ts +39 -0
- package/src/rules/erb-no-raw-output-in-attribute-value.ts +47 -0
- package/src/rules/erb-no-silent-tag-in-attribute-name.ts +2 -2
- package/src/rules/erb-no-statement-in-script.ts +82 -0
- package/src/rules/erb-no-then-in-control-flow.ts +62 -0
- package/src/rules/erb-no-trailing-whitespace.ts +187 -0
- package/src/rules/erb-no-unsafe-js-attribute.ts +47 -0
- package/src/rules/erb-no-unsafe-raw.ts +83 -0
- package/src/rules/erb-no-unsafe-script-interpolation.ts +76 -0
- package/src/rules/erb-prefer-image-tag-helper.ts +5 -4
- package/src/rules/erb-require-trailing-newline.ts +2 -2
- package/src/rules/erb-require-whitespace-inside-tags.ts +42 -18
- package/src/rules/erb-right-trim.ts +2 -2
- package/src/rules/erb-strict-locals-comment-syntax.ts +5 -5
- package/src/rules/erb-strict-locals-required.ts +2 -2
- package/src/rules/herb-disable-comment-malformed.ts +2 -2
- package/src/rules/herb-disable-comment-missing-rules.ts +2 -2
- package/src/rules/herb-disable-comment-no-duplicate-rules.ts +2 -2
- package/src/rules/herb-disable-comment-no-redundant-all.ts +2 -2
- package/src/rules/herb-disable-comment-unnecessary.ts +2 -2
- package/src/rules/herb-disable-comment-valid-rule-name.ts +2 -2
- package/src/rules/html-allowed-script-type.ts +84 -0
- package/src/rules/html-anchor-require-href.ts +73 -11
- package/src/rules/html-aria-attribute-must-be-valid.ts +3 -3
- package/src/rules/html-aria-label-is-well-formatted.ts +3 -3
- package/src/rules/html-aria-level-must-be-valid.ts +3 -3
- package/src/rules/html-aria-role-heading-requires-level.ts +5 -4
- package/src/rules/html-aria-role-must-be-valid.ts +3 -3
- package/src/rules/html-attribute-double-quotes.ts +4 -4
- package/src/rules/html-attribute-equals-spacing.ts +2 -2
- package/src/rules/html-attribute-values-require-quotes.ts +2 -2
- package/src/rules/html-avoid-both-disabled-and-aria-disabled.ts +10 -11
- package/src/rules/html-body-only-elements.ts +5 -4
- package/src/rules/html-boolean-attributes-no-value.ts +4 -3
- package/src/rules/html-details-has-summary.ts +69 -0
- package/src/rules/html-head-only-elements.ts +6 -5
- package/src/rules/html-iframe-has-title.ts +8 -11
- package/src/rules/html-img-require-alt.ts +16 -5
- package/src/rules/html-input-require-autocomplete.ts +7 -10
- package/src/rules/html-navigation-has-label.ts +6 -5
- package/src/rules/html-no-abstract-roles.ts +40 -0
- package/src/rules/html-no-aria-hidden-on-body.ts +58 -0
- package/src/rules/html-no-aria-hidden-on-focusable.ts +6 -5
- package/src/rules/html-no-block-inside-inline.ts +7 -13
- package/src/rules/html-no-duplicate-attributes.ts +4 -3
- package/src/rules/html-no-duplicate-ids.ts +16 -13
- package/src/rules/html-no-duplicate-meta-names.ts +20 -19
- package/src/rules/html-no-empty-attributes.ts +2 -2
- package/src/rules/html-no-empty-headings.ts +44 -58
- package/src/rules/html-no-nested-links.ts +25 -16
- package/src/rules/html-no-positive-tab-index.ts +3 -3
- package/src/rules/html-no-self-closing.ts +5 -5
- package/src/rules/html-no-space-in-tag.ts +5 -8
- package/src/rules/html-no-title-attribute.ts +6 -5
- package/src/rules/html-no-underscores-in-attribute-names.ts +2 -2
- package/src/rules/html-require-closing-tags.ts +41 -0
- package/src/rules/html-tag-name-lowercase.ts +14 -9
- package/src/rules/index.ts +19 -0
- package/src/rules/parser-no-errors.ts +3 -3
- package/src/rules/rule-utils.ts +162 -279
- package/src/rules/svg-tag-name-capitalization.ts +10 -10
- package/src/rules/turbo-permanent-require-id.ts +49 -0
- package/src/rules.ts +60 -10
- package/src/types.ts +76 -7
- package/src/urls.ts +5 -0
- package/dist/package.json +0 -65
- package/dist/src/cli/argument-parser.js.map +0 -1
- package/dist/src/cli/file-processor.js.map +0 -1
- package/dist/src/cli/formatters/base-formatter.js.map +0 -1
- package/dist/src/cli/formatters/detailed-formatter.js.map +0 -1
- package/dist/src/cli/formatters/github-actions-formatter.js.map +0 -1
- package/dist/src/cli/formatters/index.js.map +0 -1
- package/dist/src/cli/formatters/json-formatter.js.map +0 -1
- package/dist/src/cli/formatters/simple-formatter.js +0 -44
- package/dist/src/cli/formatters/simple-formatter.js.map +0 -1
- package/dist/src/cli/index.js.map +0 -1
- package/dist/src/cli/output-manager.js.map +0 -1
- package/dist/src/cli/summary-reporter.js.map +0 -1
- package/dist/src/cli.js.map +0 -1
- package/dist/src/custom-rule-loader.js.map +0 -1
- package/dist/src/herb-disable-comment-utils.js.map +0 -1
- package/dist/src/herb-lint.js +0 -5
- package/dist/src/herb-lint.js.map +0 -1
- package/dist/src/index.js +0 -5
- package/dist/src/index.js.map +0 -1
- package/dist/src/linter-ignore.js.map +0 -1
- package/dist/src/linter.js.map +0 -1
- package/dist/src/loader.js +0 -17
- package/dist/src/loader.js.map +0 -1
- package/dist/src/rules/erb-comment-syntax.js.map +0 -1
- package/dist/src/rules/erb-no-case-node-children.js.map +0 -1
- package/dist/src/rules/erb-no-empty-tags.js.map +0 -1
- package/dist/src/rules/erb-no-extra-newline.js.map +0 -1
- package/dist/src/rules/erb-no-extra-whitespace-inside-tags.js.map +0 -1
- package/dist/src/rules/erb-no-output-control-flow.js.map +0 -1
- package/dist/src/rules/erb-no-silent-tag-in-attribute-name.js.map +0 -1
- package/dist/src/rules/erb-prefer-image-tag-helper.js.map +0 -1
- package/dist/src/rules/erb-require-trailing-newline.js.map +0 -1
- package/dist/src/rules/erb-require-whitespace-inside-tags.js.map +0 -1
- package/dist/src/rules/erb-right-trim.js.map +0 -1
- package/dist/src/rules/erb-strict-locals-comment-syntax.js.map +0 -1
- package/dist/src/rules/erb-strict-locals-required.js.map +0 -1
- package/dist/src/rules/file-utils.js.map +0 -1
- package/dist/src/rules/herb-disable-comment-base.js.map +0 -1
- package/dist/src/rules/herb-disable-comment-malformed.js.map +0 -1
- package/dist/src/rules/herb-disable-comment-missing-rules.js.map +0 -1
- package/dist/src/rules/herb-disable-comment-no-duplicate-rules.js.map +0 -1
- package/dist/src/rules/herb-disable-comment-no-redundant-all.js.map +0 -1
- package/dist/src/rules/herb-disable-comment-unnecessary.js.map +0 -1
- package/dist/src/rules/herb-disable-comment-valid-rule-name.js.map +0 -1
- package/dist/src/rules/html-anchor-require-href.js +0 -32
- package/dist/src/rules/html-anchor-require-href.js.map +0 -1
- package/dist/src/rules/html-aria-attribute-must-be-valid.js.map +0 -1
- package/dist/src/rules/html-aria-label-is-well-formatted.js.map +0 -1
- package/dist/src/rules/html-aria-level-must-be-valid.js.map +0 -1
- package/dist/src/rules/html-aria-role-heading-requires-level.js.map +0 -1
- package/dist/src/rules/html-aria-role-must-be-valid.js.map +0 -1
- package/dist/src/rules/html-attribute-double-quotes.js.map +0 -1
- package/dist/src/rules/html-attribute-equals-spacing.js.map +0 -1
- package/dist/src/rules/html-attribute-values-require-quotes.js.map +0 -1
- package/dist/src/rules/html-avoid-both-disabled-and-aria-disabled.js.map +0 -1
- package/dist/src/rules/html-body-only-elements.js.map +0 -1
- package/dist/src/rules/html-boolean-attributes-no-value.js.map +0 -1
- package/dist/src/rules/html-head-only-elements.js.map +0 -1
- package/dist/src/rules/html-iframe-has-title.js.map +0 -1
- package/dist/src/rules/html-img-require-alt.js.map +0 -1
- package/dist/src/rules/html-input-require-autocomplete.js.map +0 -1
- package/dist/src/rules/html-navigation-has-label.js.map +0 -1
- package/dist/src/rules/html-no-aria-hidden-on-focusable.js.map +0 -1
- package/dist/src/rules/html-no-block-inside-inline.js.map +0 -1
- package/dist/src/rules/html-no-duplicate-attributes.js.map +0 -1
- package/dist/src/rules/html-no-duplicate-ids.js.map +0 -1
- package/dist/src/rules/html-no-duplicate-meta-names.js.map +0 -1
- package/dist/src/rules/html-no-empty-attributes.js.map +0 -1
- package/dist/src/rules/html-no-empty-headings.js +0 -115
- package/dist/src/rules/html-no-empty-headings.js.map +0 -1
- package/dist/src/rules/html-no-nested-links.js.map +0 -1
- package/dist/src/rules/html-no-positive-tab-index.js.map +0 -1
- package/dist/src/rules/html-no-self-closing.js.map +0 -1
- package/dist/src/rules/html-no-space-in-tag.js.map +0 -1
- package/dist/src/rules/html-no-title-attribute.js.map +0 -1
- package/dist/src/rules/html-no-underscores-in-attribute-names.js.map +0 -1
- package/dist/src/rules/html-tag-name-lowercase.js.map +0 -1
- package/dist/src/rules/index.js.map +0 -1
- package/dist/src/rules/parser-no-errors.js.map +0 -1
- package/dist/src/rules/rule-utils.js.map +0 -1
- package/dist/src/rules/string-utils.js.map +0 -1
- package/dist/src/rules/svg-tag-name-capitalization.js.map +0 -1
- package/dist/src/rules.js.map +0 -1
- package/dist/src/types.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/types/src/cli/argument-parser.d.ts +0 -25
- package/dist/types/src/cli/file-processor.d.ts +0 -43
- package/dist/types/src/cli/formatters/base-formatter.d.ts +0 -6
- package/dist/types/src/cli/formatters/detailed-formatter.d.ts +0 -13
- package/dist/types/src/cli/formatters/github-actions-formatter.d.ts +0 -17
- package/dist/types/src/cli/formatters/index.d.ts +0 -5
- package/dist/types/src/cli/formatters/json-formatter.d.ts +0 -48
- package/dist/types/src/cli/formatters/simple-formatter.d.ts +0 -8
- package/dist/types/src/cli/index.d.ts +0 -5
- package/dist/types/src/cli/output-manager.d.ts +0 -32
- package/dist/types/src/cli/summary-reporter.d.ts +0 -28
- package/dist/types/src/cli.d.ts +0 -28
- package/dist/types/src/custom-rule-loader.d.ts +0 -62
- package/dist/types/src/herb-disable-comment-utils.d.ts +0 -69
- package/dist/types/src/herb-lint.d.ts +0 -2
- package/dist/types/src/index.d.ts +0 -4
- package/dist/types/src/linter-ignore.d.ts +0 -12
- package/dist/types/src/linter.d.ts +0 -133
- package/dist/types/src/loader.d.ts +0 -20
- package/dist/types/src/rules/erb-comment-syntax.d.ts +0 -14
- package/dist/types/src/rules/erb-no-extra-newline.d.ts +0 -14
- package/dist/types/src/rules/erb-no-extra-whitespace-inside-tags.d.ts +0 -18
- package/dist/types/src/rules/erb-require-trailing-newline.d.ts +0 -9
- package/dist/types/src/rules/erb-require-whitespace-inside-tags.d.ts +0 -18
- package/dist/types/src/rules/erb-right-trim.d.ts +0 -14
- package/dist/types/src/rules/erb-strict-locals-comment-syntax.d.ts +0 -9
- package/dist/types/src/rules/erb-strict-locals-required.d.ts +0 -9
- package/dist/types/src/rules/file-utils.d.ts +0 -13
- package/dist/types/src/rules/herb-disable-comment-base.d.ts +0 -37
- package/dist/types/src/rules/herb-disable-comment-no-duplicate-rules.d.ts +0 -8
- package/dist/types/src/rules/herb-disable-comment-no-redundant-all.d.ts +0 -8
- package/dist/types/src/rules/herb-disable-comment-unnecessary.d.ts +0 -8
- package/dist/types/src/rules/herb-disable-comment-valid-rule-name.d.ts +0 -8
- package/dist/types/src/rules/html-aria-label-is-well-formatted.d.ts +0 -8
- package/dist/types/src/rules/html-aria-role-heading-requires-level.d.ts +0 -8
- package/dist/types/src/rules/html-attribute-double-quotes.d.ts +0 -15
- package/dist/types/src/rules/html-attribute-equals-spacing.d.ts +0 -14
- package/dist/types/src/rules/html-attribute-values-require-quotes.d.ts +0 -15
- package/dist/types/src/rules/html-avoid-both-disabled-and-aria-disabled.d.ts +0 -8
- package/dist/types/src/rules/html-boolean-attributes-no-value.d.ts +0 -14
- package/dist/types/src/rules/html-head-only-elements.d.ts +0 -9
- package/dist/types/src/rules/html-iframe-has-title.d.ts +0 -8
- package/dist/types/src/rules/html-img-require-alt.d.ts +0 -8
- package/dist/types/src/rules/html-input-require-autocomplete.d.ts +0 -8
- package/dist/types/src/rules/html-no-block-inside-inline.d.ts +0 -8
- package/dist/types/src/rules/html-no-duplicate-ids.d.ts +0 -8
- package/dist/types/src/rules/html-no-duplicate-meta-names.d.ts +0 -9
- package/dist/types/src/rules/html-no-nested-links.d.ts +0 -8
- package/dist/types/src/rules/html-no-self-closing.d.ts +0 -16
- package/dist/types/src/rules/html-no-space-in-tag.d.ts +0 -16
- package/dist/types/src/rules/html-no-title-attribute.d.ts +0 -8
- package/dist/types/src/rules/html-no-underscores-in-attribute-names.d.ts +0 -8
- package/dist/types/src/rules/html-tag-name-lowercase.d.ts +0 -18
- package/dist/types/src/rules/index.d.ts +0 -54
- package/dist/types/src/rules/parser-no-errors.d.ts +0 -9
- package/dist/types/src/rules/rule-utils.d.ts +0 -351
- package/dist/types/src/rules/string-utils.d.ts +0 -15
- package/dist/types/src/rules/svg-tag-name-capitalization.d.ts +0 -16
- package/dist/types/src/rules.d.ts +0 -2
- package/dist/types/src/types.d.ts +0 -190
- /package/dist/{src/cli → cli}/formatters/base-formatter.js +0 -0
- /package/dist/{src/cli → cli}/formatters/github-actions-formatter.js +0 -0
- /package/dist/{src/cli → cli}/formatters/index.js +0 -0
- /package/dist/{src/cli → cli}/formatters/json-formatter.js +0 -0
- /package/dist/{src/cli → cli}/index.js +0 -0
- /package/dist/{src/cli → cli}/output-manager.js +0 -0
- /package/dist/{src/herb-disable-comment-utils.js → herb-disable-comment-utils.js} +0 -0
- /package/dist/{src/linter-ignore.js → linter-ignore.js} +0 -0
- /package/dist/{src/rules → rules}/file-utils.js +0 -0
- /package/dist/{src/rules → rules}/herb-disable-comment-base.js +0 -0
- /package/dist/{src/rules → rules}/string-utils.js +0 -0
|
@@ -1,33 +1,89 @@
|
|
|
1
|
-
import { BaseRuleVisitor
|
|
1
|
+
import { BaseRuleVisitor } from "./rule-utils.js"
|
|
2
|
+
import { getAttribute, getStaticAttributeValue, getTagLocalName, isERBOpenTagNode, isHTMLOpenTagNode, isRubyLiteralNode, filterHTMLAttributeNodes, findAttributeByName } from "@herb-tools/core"
|
|
2
3
|
|
|
3
4
|
import { ParserRule } from "../types.js"
|
|
4
5
|
import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
5
|
-
import type {
|
|
6
|
+
import type { HTMLElementNode, HTMLAttributeNode, ParseResult, ParserOptions } from "@herb-tools/core"
|
|
6
7
|
|
|
7
|
-
class
|
|
8
|
-
|
|
8
|
+
class AnchorRequireHrefVisitor extends BaseRuleVisitor {
|
|
9
|
+
visitHTMLElementNode(node: HTMLElementNode): void {
|
|
9
10
|
this.checkATag(node)
|
|
10
|
-
super.
|
|
11
|
+
super.visitHTMLElementNode(node)
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
private checkATag(node:
|
|
14
|
-
const tagName =
|
|
14
|
+
private checkATag(node: HTMLElementNode): void {
|
|
15
|
+
const tagName = getTagLocalName(node)
|
|
15
16
|
|
|
16
17
|
if (tagName !== "a") {
|
|
17
18
|
return
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
const hrefAttribute = this.getHrefAttribute(node)
|
|
22
|
+
|
|
23
|
+
if (!hrefAttribute) {
|
|
21
24
|
this.addOffense(
|
|
22
|
-
"Add an `href` attribute to `<a>` to ensure it is focusable and accessible.",
|
|
25
|
+
"Add an `href` attribute to `<a>` to ensure it is focusable and accessible. Links should navigate somewhere. If you need a clickable element without navigation, use a `<button>` instead.",
|
|
23
26
|
node.tag_name!.location,
|
|
24
27
|
)
|
|
28
|
+
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const hrefValue = getStaticAttributeValue(hrefAttribute)
|
|
33
|
+
|
|
34
|
+
if (hrefValue === "#") {
|
|
35
|
+
this.addOffense(
|
|
36
|
+
'Avoid `href="#"` on `<a>`. `href="#"` does not navigate anywhere, scrolls the page to the top, and adds `#` to the URL. If you need a clickable element without navigation, use a `<button>` instead.',
|
|
37
|
+
hrefAttribute.location,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (hrefValue !== null && hrefValue.startsWith("javascript:void")) {
|
|
44
|
+
this.addOffense(
|
|
45
|
+
'Avoid `javascript:void(0)` in `href` on `<a>`. Links should navigate somewhere. If you need a clickable element without navigation, use a `<button>` instead.',
|
|
46
|
+
hrefAttribute.location,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (this.hasNilHrefValue(hrefAttribute)) {
|
|
53
|
+
this.addOffense(
|
|
54
|
+
"Avoid passing `nil` as the URL for `link_to`. Links should navigate somewhere. If you need a clickable element without navigation, use a `<button>` instead.",
|
|
55
|
+
hrefAttribute.location,
|
|
56
|
+
)
|
|
25
57
|
}
|
|
26
58
|
}
|
|
59
|
+
|
|
60
|
+
private hasNilHrefValue(hrefAttribute: HTMLAttributeNode): boolean {
|
|
61
|
+
const valueNode = hrefAttribute.value
|
|
62
|
+
|
|
63
|
+
if (!valueNode) return false
|
|
64
|
+
|
|
65
|
+
return valueNode.children.some(child =>
|
|
66
|
+
isRubyLiteralNode(child) && child.content === "url_for(nil)"
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private getHrefAttribute(node: HTMLElementNode): HTMLAttributeNode | null {
|
|
71
|
+
const openTag = node.open_tag
|
|
72
|
+
|
|
73
|
+
if (isHTMLOpenTagNode(openTag)) {
|
|
74
|
+
return getAttribute(openTag, "href")
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (isERBOpenTagNode(openTag)) {
|
|
78
|
+
return findAttributeByName(filterHTMLAttributeNodes(openTag.children), "href")
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return null
|
|
82
|
+
}
|
|
27
83
|
}
|
|
28
84
|
|
|
29
85
|
export class HTMLAnchorRequireHrefRule extends ParserRule {
|
|
30
|
-
|
|
86
|
+
static ruleName = "html-anchor-require-href"
|
|
31
87
|
|
|
32
88
|
get defaultConfig(): FullRuleConfig {
|
|
33
89
|
return {
|
|
@@ -36,8 +92,14 @@ export class HTMLAnchorRequireHrefRule extends ParserRule {
|
|
|
36
92
|
}
|
|
37
93
|
}
|
|
38
94
|
|
|
95
|
+
get parserOptions(): Partial<ParserOptions> {
|
|
96
|
+
return {
|
|
97
|
+
action_view_helpers: true,
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
39
101
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
40
|
-
const visitor = new
|
|
102
|
+
const visitor = new AnchorRequireHrefVisitor(this.ruleName, context)
|
|
41
103
|
|
|
42
104
|
visitor.visit(result.value)
|
|
43
105
|
|
|
@@ -25,17 +25,17 @@ class AriaAttributeMustBeValid extends AttributeVisitorMixin {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export class HTMLAriaAttributeMustBeValid extends ParserRule {
|
|
28
|
-
|
|
28
|
+
static ruleName = "html-aria-attribute-must-be-valid"
|
|
29
29
|
|
|
30
30
|
get defaultConfig(): FullRuleConfig {
|
|
31
31
|
return {
|
|
32
32
|
enabled: true,
|
|
33
|
-
severity: "
|
|
33
|
+
severity: "warning"
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
38
|
-
const visitor = new AriaAttributeMustBeValid(this.
|
|
38
|
+
const visitor = new AriaAttributeMustBeValid(this.ruleName, context)
|
|
39
39
|
|
|
40
40
|
visitor.visit(result.value)
|
|
41
41
|
|
|
@@ -44,17 +44,17 @@ class AriaLabelIsWellFormattedVisitor extends AttributeVisitorMixin {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
export class HTMLAriaLabelIsWellFormattedRule extends ParserRule {
|
|
47
|
-
|
|
47
|
+
static ruleName = "html-aria-label-is-well-formatted"
|
|
48
48
|
|
|
49
49
|
get defaultConfig(): FullRuleConfig {
|
|
50
50
|
return {
|
|
51
51
|
enabled: true,
|
|
52
|
-
severity: "
|
|
52
|
+
severity: "warning"
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
57
|
-
const visitor = new AriaLabelIsWellFormattedVisitor(this.
|
|
57
|
+
const visitor = new AriaLabelIsWellFormattedVisitor(this.ruleName, context)
|
|
58
58
|
|
|
59
59
|
visitor.visit(result.value)
|
|
60
60
|
|
|
@@ -63,17 +63,17 @@ class HTMLAriaLevelMustBeValidVisitor extends AttributeVisitorMixin {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
export class HTMLAriaLevelMustBeValidRule extends ParserRule {
|
|
66
|
-
|
|
66
|
+
static ruleName = "html-aria-level-must-be-valid"
|
|
67
67
|
|
|
68
68
|
get defaultConfig(): FullRuleConfig {
|
|
69
69
|
return {
|
|
70
70
|
enabled: true,
|
|
71
|
-
severity: "
|
|
71
|
+
severity: "warning"
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
76
|
-
const visitor = new HTMLAriaLevelMustBeValidVisitor(this.
|
|
76
|
+
const visitor = new HTMLAriaLevelMustBeValidVisitor(this.ruleName, context)
|
|
77
77
|
|
|
78
78
|
visitor.visit(result.value)
|
|
79
79
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js"
|
|
2
|
-
import { AttributeVisitorMixin,
|
|
2
|
+
import { AttributeVisitorMixin, StaticAttributeStaticValueParams } from "./rule-utils.js"
|
|
3
|
+
import { getAttributeName, getAttributes } from "@herb-tools/core"
|
|
3
4
|
|
|
4
5
|
import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
5
6
|
import type { ParseResult } from "@herb-tools/core"
|
|
@@ -20,17 +21,17 @@ class AriaRoleHeadingRequiresLevel extends AttributeVisitorMixin {
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export class HTMLAriaRoleHeadingRequiresLevelRule extends ParserRule {
|
|
23
|
-
|
|
24
|
+
static ruleName = "html-aria-role-heading-requires-level"
|
|
24
25
|
|
|
25
26
|
get defaultConfig(): FullRuleConfig {
|
|
26
27
|
return {
|
|
27
28
|
enabled: true,
|
|
28
|
-
severity: "
|
|
29
|
+
severity: "warning"
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
33
|
-
const visitor = new AriaRoleHeadingRequiresLevel(this.
|
|
34
|
+
const visitor = new AriaRoleHeadingRequiresLevel(this.ruleName, context)
|
|
34
35
|
|
|
35
36
|
visitor.visit(result.value)
|
|
36
37
|
|
|
@@ -18,17 +18,17 @@ class AriaRoleMustBeValid extends AttributeVisitorMixin {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export class HTMLAriaRoleMustBeValidRule extends ParserRule {
|
|
21
|
-
|
|
21
|
+
static ruleName = "html-aria-role-must-be-valid"
|
|
22
22
|
|
|
23
23
|
get defaultConfig(): FullRuleConfig {
|
|
24
24
|
return {
|
|
25
25
|
enabled: true,
|
|
26
|
-
severity: "
|
|
26
|
+
severity: "warning"
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
31
|
-
const visitor = new AriaRoleMustBeValid(this.
|
|
31
|
+
const visitor = new AriaRoleMustBeValid(this.ruleName, context)
|
|
32
32
|
|
|
33
33
|
visitor.visit(result.value)
|
|
34
34
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ParserRule, BaseAutofixContext, Mutable } from "../types.js"
|
|
2
|
-
import { AttributeVisitorMixin, StaticAttributeStaticValueParams, StaticAttributeDynamicValueParams
|
|
3
|
-
import { filterLiteralNodes } from "@herb-tools/core"
|
|
2
|
+
import { AttributeVisitorMixin, StaticAttributeStaticValueParams, StaticAttributeDynamicValueParams } from "./rule-utils.js"
|
|
3
|
+
import { filterLiteralNodes, getAttributeValueQuoteType, hasAttributeValue } from "@herb-tools/core"
|
|
4
4
|
|
|
5
5
|
import type { UnboundLintOffense, LintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
6
6
|
import type { ParseResult, HTMLAttributeNode } from "@herb-tools/core"
|
|
@@ -44,7 +44,7 @@ class AttributeDoubleQuotesVisitor extends AttributeVisitorMixin<AttributeDouble
|
|
|
44
44
|
|
|
45
45
|
export class HTMLAttributeDoubleQuotesRule extends ParserRule<AttributeDoubleQuotesAutofixContext> {
|
|
46
46
|
static autocorrectable = true
|
|
47
|
-
|
|
47
|
+
static ruleName = "html-attribute-double-quotes"
|
|
48
48
|
|
|
49
49
|
get defaultConfig(): FullRuleConfig {
|
|
50
50
|
return {
|
|
@@ -54,7 +54,7 @@ export class HTMLAttributeDoubleQuotesRule extends ParserRule<AttributeDoubleQuo
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense<AttributeDoubleQuotesAutofixContext>[] {
|
|
57
|
-
const visitor = new AttributeDoubleQuotesVisitor(this.
|
|
57
|
+
const visitor = new AttributeDoubleQuotesVisitor(this.ruleName, context)
|
|
58
58
|
|
|
59
59
|
visitor.visit(result.value)
|
|
60
60
|
|
|
@@ -34,7 +34,7 @@ class HTMLAttributeEqualsSpacingVisitor extends BaseRuleVisitor<AttributeEqualsS
|
|
|
34
34
|
|
|
35
35
|
export class HTMLAttributeEqualsSpacingRule extends ParserRule<AttributeEqualsSpacingAutofixContext> {
|
|
36
36
|
static autocorrectable = true
|
|
37
|
-
|
|
37
|
+
static ruleName = "html-attribute-equals-spacing"
|
|
38
38
|
|
|
39
39
|
get defaultConfig(): FullRuleConfig {
|
|
40
40
|
return {
|
|
@@ -44,7 +44,7 @@ export class HTMLAttributeEqualsSpacingRule extends ParserRule<AttributeEqualsSp
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense<AttributeEqualsSpacingAutofixContext>[] {
|
|
47
|
-
const visitor = new HTMLAttributeEqualsSpacingVisitor(this.
|
|
47
|
+
const visitor = new HTMLAttributeEqualsSpacingVisitor(this.ruleName, context)
|
|
48
48
|
|
|
49
49
|
visitor.visit(result.value)
|
|
50
50
|
|
|
@@ -52,7 +52,7 @@ class AttributeValuesRequireQuotesVisitor extends AttributeVisitorMixin<Attribut
|
|
|
52
52
|
|
|
53
53
|
export class HTMLAttributeValuesRequireQuotesRule extends ParserRule<AttributeValuesRequireQuotesAutofixContext> {
|
|
54
54
|
static autocorrectable = true
|
|
55
|
-
|
|
55
|
+
static ruleName = "html-attribute-values-require-quotes"
|
|
56
56
|
|
|
57
57
|
get defaultConfig(): FullRuleConfig {
|
|
58
58
|
return {
|
|
@@ -62,7 +62,7 @@ export class HTMLAttributeValuesRequireQuotesRule extends ParserRule<AttributeVa
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense<AttributeValuesRequireQuotesAutofixContext>[] {
|
|
65
|
-
const visitor = new AttributeValuesRequireQuotesVisitor(this.
|
|
65
|
+
const visitor = new AttributeValuesRequireQuotesVisitor(this.ruleName, context)
|
|
66
66
|
|
|
67
67
|
visitor.visit(result.value)
|
|
68
68
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js"
|
|
2
|
-
import { BaseRuleVisitor
|
|
2
|
+
import { BaseRuleVisitor } from "./rule-utils.js"
|
|
3
|
+
import { isHTMLAttributeValueNode, isERBContentNode, getAttributes, findAttributeByName, hasAttribute, getTagLocalName } from "@herb-tools/core"
|
|
3
4
|
|
|
4
5
|
import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
5
|
-
import type { HTMLOpenTagNode,
|
|
6
|
+
import type { HTMLOpenTagNode, ParseResult } from "@herb-tools/core"
|
|
6
7
|
|
|
7
8
|
const ELEMENTS_WITH_NATIVE_DISABLED_ATTRIBUTE_SUPPORT = new Set([
|
|
8
9
|
"button", "fieldset", "input", "optgroup", "option", "select", "textarea"
|
|
@@ -15,7 +16,7 @@ class AvoidBothDisabledAndAriaDisabledVisitor extends BaseRuleVisitor {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
private checkElement(node: HTMLOpenTagNode): void {
|
|
18
|
-
const tagName =
|
|
19
|
+
const tagName = getTagLocalName(node)
|
|
19
20
|
|
|
20
21
|
if (!tagName || !ELEMENTS_WITH_NATIVE_DISABLED_ATTRIBUTE_SUPPORT.has(tagName)) {
|
|
21
22
|
return
|
|
@@ -43,27 +44,25 @@ class AvoidBothDisabledAndAriaDisabledVisitor extends BaseRuleVisitor {
|
|
|
43
44
|
if (!attribute) return false
|
|
44
45
|
|
|
45
46
|
const valueNode = attribute.value
|
|
46
|
-
if (!valueNode
|
|
47
|
+
if (!isHTMLAttributeValueNode(valueNode)) return false
|
|
48
|
+
if (!valueNode.children) return false
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
if (!htmlValueNode.children) return false
|
|
50
|
-
|
|
51
|
-
return htmlValueNode.children.some((child: Node) => child.type === "AST_ERB_CONTENT_NODE")
|
|
50
|
+
return valueNode.children.some(isERBContentNode)
|
|
52
51
|
}
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
export class HTMLAvoidBothDisabledAndAriaDisabledRule extends ParserRule {
|
|
56
|
-
|
|
55
|
+
static ruleName = "html-avoid-both-disabled-and-aria-disabled"
|
|
57
56
|
|
|
58
57
|
get defaultConfig(): FullRuleConfig {
|
|
59
58
|
return {
|
|
60
59
|
enabled: true,
|
|
61
|
-
severity: "
|
|
60
|
+
severity: "warning"
|
|
62
61
|
}
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
66
|
-
const visitor = new AvoidBothDisabledAndAriaDisabledVisitor(this.
|
|
65
|
+
const visitor = new AvoidBothDisabledAndAriaDisabledVisitor(this.ruleName, context)
|
|
67
66
|
|
|
68
67
|
visitor.visit(result.value)
|
|
69
68
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js"
|
|
2
|
-
import { BaseRuleVisitor,
|
|
2
|
+
import { BaseRuleVisitor, isBodyOnlyTag } from "./rule-utils.js"
|
|
3
|
+
import { getTagLocalName } from "@herb-tools/core"
|
|
3
4
|
|
|
4
5
|
import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
5
6
|
import type { HTMLElementNode, ParseResult } from "@herb-tools/core"
|
|
@@ -8,7 +9,7 @@ class HTMLBodyOnlyElementsVisitor extends BaseRuleVisitor {
|
|
|
8
9
|
private elementStack: string[] = []
|
|
9
10
|
|
|
10
11
|
visitHTMLElementNode(node: HTMLElementNode): void {
|
|
11
|
-
const tagName =
|
|
12
|
+
const tagName = getTagLocalName(node)
|
|
12
13
|
if (!tagName) return
|
|
13
14
|
|
|
14
15
|
this.checkBodyOnlyElement(node, tagName)
|
|
@@ -40,7 +41,7 @@ class HTMLBodyOnlyElementsVisitor extends BaseRuleVisitor {
|
|
|
40
41
|
|
|
41
42
|
export class HTMLBodyOnlyElementsRule extends ParserRule {
|
|
42
43
|
static autocorrectable = false
|
|
43
|
-
|
|
44
|
+
static ruleName = "html-body-only-elements"
|
|
44
45
|
|
|
45
46
|
get defaultConfig(): FullRuleConfig {
|
|
46
47
|
return {
|
|
@@ -51,7 +52,7 @@ export class HTMLBodyOnlyElementsRule extends ParserRule {
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
54
|
-
const visitor = new HTMLBodyOnlyElementsVisitor(this.
|
|
55
|
+
const visitor = new HTMLBodyOnlyElementsVisitor(this.ruleName, context)
|
|
55
56
|
|
|
56
57
|
visitor.visit(result.value)
|
|
57
58
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ParserRule, BaseAutofixContext, Mutable } from "../types.js"
|
|
2
|
-
import { AttributeVisitorMixin, StaticAttributeStaticValueParams, StaticAttributeDynamicValueParams, isBooleanAttribute
|
|
2
|
+
import { AttributeVisitorMixin, StaticAttributeStaticValueParams, StaticAttributeDynamicValueParams, isBooleanAttribute } from "./rule-utils.js"
|
|
3
|
+
import { hasAttributeValue } from "@herb-tools/core"
|
|
3
4
|
import { IdentityPrinter } from "@herb-tools/printer"
|
|
4
5
|
|
|
5
6
|
import type { UnboundLintOffense, LintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
@@ -34,7 +35,7 @@ class BooleanAttributesNoValueVisitor extends AttributeVisitorMixin<BooleanAttri
|
|
|
34
35
|
|
|
35
36
|
export class HTMLBooleanAttributesNoValueRule extends ParserRule<BooleanAttributeAutofixContext> {
|
|
36
37
|
static autocorrectable = true
|
|
37
|
-
|
|
38
|
+
static ruleName = "html-boolean-attributes-no-value"
|
|
38
39
|
|
|
39
40
|
get defaultConfig(): FullRuleConfig {
|
|
40
41
|
return {
|
|
@@ -44,7 +45,7 @@ export class HTMLBooleanAttributesNoValueRule extends ParserRule<BooleanAttribut
|
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense<BooleanAttributeAutofixContext>[] {
|
|
47
|
-
const visitor = new BooleanAttributesNoValueVisitor(this.
|
|
48
|
+
const visitor = new BooleanAttributesNoValueVisitor(this.ruleName, context)
|
|
48
49
|
|
|
49
50
|
visitor.visit(result.value)
|
|
50
51
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { BaseRuleVisitor } from "./rule-utils.js"
|
|
2
|
+
import { getTagLocalName, isHTMLElementNode } from "@herb-tools/core"
|
|
3
|
+
|
|
4
|
+
import { ParserRule } from "../types.js"
|
|
5
|
+
import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
6
|
+
import type { HTMLElementNode, ParseResult, ParserOptions } from "@herb-tools/core"
|
|
7
|
+
|
|
8
|
+
class DetailsHasSummaryVisitor extends BaseRuleVisitor {
|
|
9
|
+
visitHTMLElementNode(node: HTMLElementNode): void {
|
|
10
|
+
this.checkDetailsElement(node)
|
|
11
|
+
super.visitHTMLElementNode(node)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
private checkDetailsElement(node: HTMLElementNode): void {
|
|
15
|
+
const tagName = getTagLocalName(node)
|
|
16
|
+
|
|
17
|
+
if (tagName !== "details") {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!this.hasDirectSummaryChild(node)) {
|
|
22
|
+
this.addOffense(
|
|
23
|
+
"`<details>` element must have a direct `<summary>` child element.",
|
|
24
|
+
node.location,
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private hasDirectSummaryChild(node: HTMLElementNode): boolean {
|
|
30
|
+
if (!node.body || node.body.length === 0) {
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
for (const child of node.body) {
|
|
35
|
+
if (isHTMLElementNode(child)) {
|
|
36
|
+
const childTagName = getTagLocalName(child)
|
|
37
|
+
|
|
38
|
+
if (childTagName === "summary") {
|
|
39
|
+
return true
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class HTMLDetailsHasSummaryRule extends ParserRule {
|
|
49
|
+
static ruleName = "html-details-has-summary"
|
|
50
|
+
|
|
51
|
+
get defaultConfig(): FullRuleConfig {
|
|
52
|
+
return {
|
|
53
|
+
enabled: true,
|
|
54
|
+
severity: "warning"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get parserOptions(): Partial<ParserOptions> {
|
|
59
|
+
return {
|
|
60
|
+
action_view_helpers: true,
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
65
|
+
const visitor = new DetailsHasSummaryVisitor(this.ruleName, context)
|
|
66
|
+
visitor.visit(result.value)
|
|
67
|
+
return visitor.offenses
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ParserRule } from "../types"
|
|
2
|
-
import { BaseRuleVisitor,
|
|
2
|
+
import { BaseRuleVisitor, isHeadOnlyTag } from "./rule-utils"
|
|
3
|
+
import { hasAttribute, getTagLocalName } from "@herb-tools/core"
|
|
3
4
|
|
|
4
5
|
import type { ParseResult, HTMLElementNode } from "@herb-tools/core"
|
|
5
6
|
import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types"
|
|
@@ -8,7 +9,7 @@ class HeadOnlyElementsVisitor extends BaseRuleVisitor {
|
|
|
8
9
|
private elementStack: string[] = []
|
|
9
10
|
|
|
10
11
|
visitHTMLElementNode(node: HTMLElementNode): void {
|
|
11
|
-
const tagName =
|
|
12
|
+
const tagName = getTagLocalName(node)
|
|
12
13
|
if (!tagName) return
|
|
13
14
|
|
|
14
15
|
this.checkHeadOnlyElement(node, tagName)
|
|
@@ -33,7 +34,7 @@ class HeadOnlyElementsVisitor extends BaseRuleVisitor {
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
private hasItempropAttribute(node: HTMLElementNode): boolean {
|
|
36
|
-
return hasAttribute(node
|
|
37
|
+
return hasAttribute(node, "itemprop")
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
private get insideHead(): boolean {
|
|
@@ -51,7 +52,7 @@ class HeadOnlyElementsVisitor extends BaseRuleVisitor {
|
|
|
51
52
|
|
|
52
53
|
export class HTMLHeadOnlyElementsRule extends ParserRule {
|
|
53
54
|
static autocorrectable = false
|
|
54
|
-
|
|
55
|
+
static ruleName = "html-head-only-elements"
|
|
55
56
|
|
|
56
57
|
get defaultConfig(): FullRuleConfig {
|
|
57
58
|
return {
|
|
@@ -62,7 +63,7 @@ export class HTMLHeadOnlyElementsRule extends ParserRule {
|
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
65
|
-
const visitor = new HeadOnlyElementsVisitor(this.
|
|
66
|
+
const visitor = new HeadOnlyElementsVisitor(this.ruleName, context)
|
|
66
67
|
|
|
67
68
|
visitor.visit(result.value)
|
|
68
69
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js"
|
|
2
|
-
import { BaseRuleVisitor
|
|
2
|
+
import { BaseRuleVisitor } from "./rule-utils.js"
|
|
3
|
+
import { getStaticAttributeValue, getAttribute, getAttributeValue, getTagLocalName } from "@herb-tools/core"
|
|
3
4
|
|
|
4
5
|
import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
5
6
|
import type { HTMLOpenTagNode, ParseResult } from "@herb-tools/core"
|
|
@@ -11,18 +12,14 @@ class IframeHasTitleVisitor extends BaseRuleVisitor {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
private checkIframeElement(node: HTMLOpenTagNode): void {
|
|
14
|
-
const tagName =
|
|
15
|
+
const tagName = getTagLocalName(node)
|
|
15
16
|
|
|
16
17
|
if (tagName !== "iframe") {
|
|
17
18
|
return
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const ariaHiddenValue = getAttributeValue(ariaHiddenAttribute)
|
|
23
|
-
if (ariaHiddenValue === "true") {
|
|
24
|
-
return
|
|
25
|
-
}
|
|
21
|
+
if (getStaticAttributeValue(node, "aria-hidden") === "true") {
|
|
22
|
+
return
|
|
26
23
|
}
|
|
27
24
|
|
|
28
25
|
const attribute = getAttribute(node, "title")
|
|
@@ -48,17 +45,17 @@ class IframeHasTitleVisitor extends BaseRuleVisitor {
|
|
|
48
45
|
}
|
|
49
46
|
|
|
50
47
|
export class HTMLIframeHasTitleRule extends ParserRule {
|
|
51
|
-
|
|
48
|
+
static ruleName = "html-iframe-has-title"
|
|
52
49
|
|
|
53
50
|
get defaultConfig(): FullRuleConfig {
|
|
54
51
|
return {
|
|
55
52
|
enabled: true,
|
|
56
|
-
severity: "
|
|
53
|
+
severity: "warning"
|
|
57
54
|
}
|
|
58
55
|
}
|
|
59
56
|
|
|
60
57
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
61
|
-
const visitor = new IframeHasTitleVisitor(this.
|
|
58
|
+
const visitor = new IframeHasTitleVisitor(this.ruleName, context)
|
|
62
59
|
|
|
63
60
|
visitor.visit(result.value)
|
|
64
61
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { BaseRuleVisitor
|
|
1
|
+
import { BaseRuleVisitor } from "./rule-utils.js"
|
|
2
|
+
import { hasAttribute, getAttribute, hasAttributeValue, getTagLocalName } from "@herb-tools/core"
|
|
2
3
|
|
|
3
4
|
import { ParserRule } from "../types.js"
|
|
4
5
|
import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
@@ -11,7 +12,7 @@ class ImgRequireAltVisitor extends BaseRuleVisitor {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
private checkImgTag(node: HTMLOpenTagNode): void {
|
|
14
|
-
const tagName =
|
|
15
|
+
const tagName = getTagLocalName(node)
|
|
15
16
|
|
|
16
17
|
if (tagName !== "img") {
|
|
17
18
|
return
|
|
@@ -22,22 +23,32 @@ class ImgRequireAltVisitor extends BaseRuleVisitor {
|
|
|
22
23
|
'Missing required `alt` attribute on `<img>` tag. Add `alt=""` for decorative images or `alt="description"` for informative images.',
|
|
23
24
|
node.tag_name!.location
|
|
24
25
|
)
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const altAttribute = getAttribute(node, "alt")
|
|
30
|
+
|
|
31
|
+
if (altAttribute && !hasAttributeValue(altAttribute)) {
|
|
32
|
+
this.addOffense(
|
|
33
|
+
'The `alt` attribute has no value. Add `alt=""` for decorative images or `alt="description"` for informative images.',
|
|
34
|
+
altAttribute.location
|
|
35
|
+
)
|
|
25
36
|
}
|
|
26
37
|
}
|
|
27
38
|
}
|
|
28
39
|
|
|
29
40
|
export class HTMLImgRequireAltRule extends ParserRule {
|
|
30
|
-
|
|
41
|
+
static ruleName = "html-img-require-alt"
|
|
31
42
|
|
|
32
43
|
get defaultConfig(): FullRuleConfig {
|
|
33
44
|
return {
|
|
34
45
|
enabled: true,
|
|
35
|
-
severity: "
|
|
46
|
+
severity: "warning"
|
|
36
47
|
}
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
40
|
-
const visitor = new ImgRequireAltVisitor(this.
|
|
51
|
+
const visitor = new ImgRequireAltVisitor(this.ruleName, context)
|
|
41
52
|
visitor.visit(result.value)
|
|
42
53
|
return visitor.offenses
|
|
43
54
|
}
|