@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
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
**Rule:** `erb-no-then-in-control-flow`
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Disallow the use of the Ruby `then` keyword in control flow expressions inside ERB templates. This applies to:
|
|
6
|
+
|
|
7
|
+
- `if … then`
|
|
8
|
+
- `elsif … then`
|
|
9
|
+
- `unless … then`
|
|
10
|
+
- `case … when … then`
|
|
11
|
+
- `case … in … then`
|
|
12
|
+
|
|
13
|
+
## Rationale
|
|
14
|
+
|
|
15
|
+
While Ruby allows the `then` keyword, its use inside ERB templates significantly reduces readability and adds confusion/ambiguity.
|
|
16
|
+
|
|
17
|
+
In templates, clarity matters more than terseness. The multiline form:
|
|
18
|
+
|
|
19
|
+
- Is easier to read and review
|
|
20
|
+
- Works better with indentation and formatting rules
|
|
21
|
+
- Avoids subtle parsing confusion in mixed HTML/Ruby contexts
|
|
22
|
+
- Matches idiomatic Rails view style
|
|
23
|
+
|
|
24
|
+
This rule enforces a consistent, block-oriented style for control flow in ERB.
|
|
25
|
+
|
|
26
|
+
## Examples
|
|
27
|
+
|
|
28
|
+
### Good
|
|
29
|
+
|
|
30
|
+
```erb
|
|
31
|
+
<% if condition %>
|
|
32
|
+
yes
|
|
33
|
+
<% end %>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```erb
|
|
37
|
+
<% unless logged_in? %>
|
|
38
|
+
please log in
|
|
39
|
+
<% end %>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```erb
|
|
43
|
+
<% case status %>
|
|
44
|
+
<% when :ok %>
|
|
45
|
+
success
|
|
46
|
+
<% when :error %>
|
|
47
|
+
failure
|
|
48
|
+
<% else %>
|
|
49
|
+
unknown
|
|
50
|
+
<% end %>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```erb
|
|
54
|
+
<% case value %>
|
|
55
|
+
<% in Integer %>
|
|
56
|
+
number
|
|
57
|
+
<% in String %>
|
|
58
|
+
string
|
|
59
|
+
<% end %>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Bad
|
|
63
|
+
|
|
64
|
+
```erb
|
|
65
|
+
<% if condition then %>
|
|
66
|
+
yes
|
|
67
|
+
<% end %>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```erb
|
|
71
|
+
<% case status %>
|
|
72
|
+
<% when :ok then %>
|
|
73
|
+
success
|
|
74
|
+
<% end %>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
```erb
|
|
78
|
+
<% case value %>
|
|
79
|
+
<% in Integer then "number" %>
|
|
80
|
+
<% in String then "text" %>
|
|
81
|
+
<% end %>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## References
|
|
85
|
+
|
|
86
|
+
- [GitHub Issue #1077](https://github.com/marcoroth/herb/issues/1077)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Linter Rule: Disallow trailing whitespace at end of lines
|
|
2
|
+
|
|
3
|
+
**Rule:** `erb-no-trailing-whitespace`
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
Disallow trailing whitespace (spaces, tabs, carriage returns, and other whitespace characters) at the end of lines in ERB templates.
|
|
8
|
+
|
|
9
|
+
## Skipped Content
|
|
10
|
+
|
|
11
|
+
This rule does **not** flag trailing whitespace inside:
|
|
12
|
+
|
|
13
|
+
* `<pre>` - Preformatted text where whitespace is significant
|
|
14
|
+
* `<textarea>` - User input where whitespace may be intentional
|
|
15
|
+
* `<script>` - Treated as foreign content
|
|
16
|
+
* `<style>` - Treated as foreign content
|
|
17
|
+
* ERB tags (`<% %>`, `<%= %>`, `<%# %>`) - Ruby code where trailing whitespace could be significant (heredocs, string literals)
|
|
18
|
+
|
|
19
|
+
## Rationale
|
|
20
|
+
|
|
21
|
+
Trailing whitespace is invisible and serves no purpose, but it can cause several issues:
|
|
22
|
+
|
|
23
|
+
* Creates unnecessary noise in diffs and pull requests
|
|
24
|
+
* Can trigger merge conflicts when different editors handle trailing whitespace differently
|
|
25
|
+
* Increases file size with characters that have no visual or functional effect
|
|
26
|
+
* Many editor configurations and linters in other languages flag trailing whitespace, so keeping templates clean maintains consistency across the project
|
|
27
|
+
|
|
28
|
+
## Examples
|
|
29
|
+
|
|
30
|
+
### ✅ Good
|
|
31
|
+
|
|
32
|
+
```erb
|
|
33
|
+
<div>
|
|
34
|
+
<p>Hello</p>
|
|
35
|
+
</div>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```erb
|
|
39
|
+
<%= content %>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```erb
|
|
43
|
+
<div>
|
|
44
|
+
<h1>Title</h1>
|
|
45
|
+
</div>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 🚫 Bad
|
|
49
|
+
|
|
50
|
+
```erb
|
|
51
|
+
<div>···
|
|
52
|
+
<p>Hello</p>
|
|
53
|
+
</div>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```erb
|
|
57
|
+
<%= content %>·
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```erb
|
|
61
|
+
Hello→→
|
|
62
|
+
World···
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
> `·` represents a trailing space, `→` represents a trailing tab.
|
|
66
|
+
|
|
67
|
+
## References
|
|
68
|
+
|
|
69
|
+
- [Inspiration: ERB Lint `TrailingWhitespace` rule](https://github.com/Shopify/erb_lint/blob/main/README.md)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Linter Rule: Disallow unsafe ERB output in JavaScript attributes
|
|
2
|
+
|
|
3
|
+
**Rule:** `erb-no-unsafe-js-attribute`
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
ERB interpolation in JavaScript event handler attributes (`onclick`, `onmouseover`, etc.) must be wrapped in a safe helper such as `.to_json`, `j()`, or `escape_javascript()`. Without proper encoding, user-controlled values can break out of string literals and execute arbitrary JavaScript.
|
|
8
|
+
|
|
9
|
+
## Rationale
|
|
10
|
+
|
|
11
|
+
HTML attributes that start with `on` (like `onclick`, `onmouseover`, `onfocus`) are evaluated as JavaScript by the browser. When ERB output is interpolated into these attributes without proper encoding, it creates a JavaScript injection vector. The `.to_json` method properly serializes Ruby values into safe JavaScript literals, while `j()` and `escape_javascript()` escape values for safe embedding in JavaScript string contexts.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### ✅ Good
|
|
16
|
+
|
|
17
|
+
```erb
|
|
18
|
+
<a onclick="method(<%= unsafe.to_json %>)"></a>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```erb
|
|
22
|
+
<a onclick="method('<%= j(unsafe) %>')"></a>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```erb
|
|
26
|
+
<a onclick="method(<%= escape_javascript(unsafe) %>)"></a>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 🚫 Bad
|
|
30
|
+
|
|
31
|
+
```erb
|
|
32
|
+
<a onclick="method(<%= unsafe %>)"></a>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```erb
|
|
36
|
+
<div onmouseover="highlight('<%= element_id %>')"></div>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## References
|
|
40
|
+
|
|
41
|
+
- [Shopify/better-html — TagInterpolation](https://github.com/Shopify/better-html/blob/main/lib/better_html/test_helper/safe_erb/tag_interpolation.rb)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Linter Rule: Disallow `raw()` and `.html_safe` in ERB output
|
|
2
|
+
|
|
3
|
+
**Rule:** `erb-no-unsafe-raw`
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
Disallow the use of `raw()` and `.html_safe` in ERB output tags. These methods bypass Rails' automatic HTML escaping, which is the primary defense against cross-site scripting (XSS) vulnerabilities.
|
|
8
|
+
|
|
9
|
+
## Rationale
|
|
10
|
+
|
|
11
|
+
Rails automatically escapes ERB output to prevent XSS. Using `raw()` or `.html_safe` disables this protection, allowing arbitrary HTML and JavaScript injection. Even when combined with other safe methods like `.to_json`, using `raw()` or `.html_safe` is still unsafe because the escaping bypass applies to the final output.
|
|
12
|
+
|
|
13
|
+
For example, `<%= raw unsafe.to_json %>` is flagged because `raw()` disables escaping on the entire expression, even though `.to_json` serializes the value safely. The `raw()` wrapper means any future changes to the expression could silently introduce a vulnerability.
|
|
14
|
+
|
|
15
|
+
## Examples
|
|
16
|
+
|
|
17
|
+
### ✅ Good
|
|
18
|
+
|
|
19
|
+
```erb
|
|
20
|
+
<div class="<%= user_input %>"></div>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```erb
|
|
24
|
+
<p><%= user_input %></p>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 🚫 Bad
|
|
28
|
+
|
|
29
|
+
```erb
|
|
30
|
+
<div class="<%= raw(user_input) %>"></div>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```erb
|
|
34
|
+
<div class="<%= user_input.html_safe %>"></div>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```erb
|
|
38
|
+
<p><%= raw(user_input) %></p>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```erb
|
|
42
|
+
<p><%= user_input.html_safe %></p>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## References
|
|
46
|
+
|
|
47
|
+
- [Shopify/better-html — TagInterpolation](https://github.com/Shopify/better-html/blob/main/lib/better_html/test_helper/safe_erb/tag_interpolation.rb)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Linter Rule: Disallow unsafe ERB output inside `<script>` tags
|
|
2
|
+
|
|
3
|
+
**Rule:** `erb-no-unsafe-script-interpolation`
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
ERB interpolation in `<script>` tags must call `.to_json` to safely serialize Ruby data into JavaScript. Without `.to_json`, user-controlled values can break out of string literals and execute arbitrary JavaScript.
|
|
8
|
+
|
|
9
|
+
## Rationale
|
|
10
|
+
|
|
11
|
+
The main goal of this rule is to assert that Ruby data translates into JavaScript data, but never becomes JavaScript code. ERB output inside `<script>` tags is interpolated directly into the JavaScript context. Without proper serialization via `.to_json`, an attacker can inject arbitrary JavaScript by manipulating the interpolated value.
|
|
12
|
+
|
|
13
|
+
For example, consider:
|
|
14
|
+
|
|
15
|
+
```erb
|
|
16
|
+
<script>
|
|
17
|
+
var name = "<%= user.name %>";
|
|
18
|
+
</script>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
If `user.name` contains `"; alert(1); "`, the resulting JavaScript would execute arbitrary code. Using `.to_json` properly escapes the value and wraps it in quotes:
|
|
22
|
+
|
|
23
|
+
```erb
|
|
24
|
+
<script>
|
|
25
|
+
var name = <%= user.name.to_json %>;
|
|
26
|
+
</script>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Examples
|
|
30
|
+
|
|
31
|
+
### ✅ Good
|
|
32
|
+
|
|
33
|
+
```erb
|
|
34
|
+
<script>
|
|
35
|
+
var name = <%= user.name.to_json %>;
|
|
36
|
+
</script>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```erb
|
|
40
|
+
<script>
|
|
41
|
+
var data = <%== config.to_json %>;
|
|
42
|
+
</script>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```erb
|
|
46
|
+
<script>
|
|
47
|
+
<%= raw unsafe.to_json %>
|
|
48
|
+
</script>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 🚫 Bad
|
|
52
|
+
|
|
53
|
+
```erb
|
|
54
|
+
<script>
|
|
55
|
+
var name = "<%= user.name %>";
|
|
56
|
+
</script>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```erb
|
|
60
|
+
<script>
|
|
61
|
+
if (a < 1) { <%= unsafe %> }
|
|
62
|
+
</script>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```erb
|
|
66
|
+
<script>
|
|
67
|
+
<%= @feature.html_safe %>
|
|
68
|
+
</script>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## References
|
|
72
|
+
|
|
73
|
+
- [Shopify/better-html — ScriptInterpolation](https://github.com/Shopify/better-html/blob/main/lib/better_html/test_helper/safe_erb/script_interpolation.rb)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Linter Rule: Restrict allowed `type` attributes for `<script>` tags
|
|
2
|
+
|
|
3
|
+
**Rule:** `html-allowed-script-type`
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
Restricts which `type` attribute values are permitted on `<script>` tags. Only approved types are allowed: `text/javascript`. An empty or valueless `type` attribute is reported.
|
|
8
|
+
|
|
9
|
+
## Rationale
|
|
10
|
+
|
|
11
|
+
Developers frequently use `<script>` tags with non-executable type attributes (like `application/json` or `text/html`) to embed data in pages. However, these tags share parsing quirks with executable scripts and can create security risks. For example, unescaped `</script><script>` sequences inside a `text/html` script tag could enable XSS attacks.
|
|
12
|
+
|
|
13
|
+
By restricting the allowed `type` values and requiring the `type` attribute to be present, this rule helps catch typos and discourages unsafe or unintended script usage patterns.
|
|
14
|
+
|
|
15
|
+
## Examples
|
|
16
|
+
|
|
17
|
+
### ✅ Good
|
|
18
|
+
|
|
19
|
+
```erb
|
|
20
|
+
<script type="text/javascript">
|
|
21
|
+
console.log("Hello")
|
|
22
|
+
</script>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```erb
|
|
26
|
+
<script>
|
|
27
|
+
console.log("Hello")
|
|
28
|
+
</script>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 🚫 Bad
|
|
32
|
+
|
|
33
|
+
```erb
|
|
34
|
+
<script type="text/coffeescript">
|
|
35
|
+
console.log "Hello"
|
|
36
|
+
</script>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```erb
|
|
40
|
+
<script type="application/ecmascript">
|
|
41
|
+
console.log("Hello")
|
|
42
|
+
</script>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```erb
|
|
46
|
+
<script type="">
|
|
47
|
+
console.log("Hello")
|
|
48
|
+
</script>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```erb
|
|
52
|
+
<script type>
|
|
53
|
+
console.log("Hello")
|
|
54
|
+
</script>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## References
|
|
58
|
+
|
|
59
|
+
- [Inspiration: ERB Lint `AllowedScriptType` rule](https://github.com/Shopify/erb_lint/tree/main?tab=readme-ov-file#allowedscripttype)
|
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
## Description
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
An `<a>` element that has an `href` attribute represents a hyperlink (a hypertext anchor) labeled by its contents. Links should go somewhere. If you want to perform an action without navigating the user to a new URL, use a `<button>` instead.
|
|
8
8
|
|
|
9
9
|
## Rationale
|
|
10
10
|
|
|
11
|
-
Anchor tags without href are
|
|
11
|
+
Anchor tags without an `href` attribute are not focusable via keyboard navigation and are not visible to screen readers. This makes them inaccessible to users who rely on assistive technologies.
|
|
12
12
|
|
|
13
13
|
## Examples
|
|
14
14
|
|
|
@@ -20,13 +20,26 @@ Anchor tags without href are unfocusable if user is using keyboard navigation, o
|
|
|
20
20
|
|
|
21
21
|
### 🚫 Bad
|
|
22
22
|
|
|
23
|
+
```erb
|
|
24
|
+
<a>Go to Page</a>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```erb
|
|
28
|
+
<a href="#">Go to Page</a>
|
|
29
|
+
```
|
|
30
|
+
|
|
23
31
|
```erb
|
|
24
32
|
<a data-action="click->doSomething">I'm a fake link</a>
|
|
25
33
|
```
|
|
26
34
|
|
|
27
35
|
## References
|
|
28
36
|
|
|
29
|
-
* https://
|
|
30
|
-
* https://
|
|
31
|
-
* https://
|
|
32
|
-
* https://
|
|
37
|
+
* [MDN: The Anchor element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)
|
|
38
|
+
* [HTML Spec: The `a` element](https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-a-element)
|
|
39
|
+
* [WAI-ARIA 1.2: `link` role](https://www.w3.org/TR/wai-aria-1.2/#link)
|
|
40
|
+
* [Primer: Links](https://primer.style/design/accessibility/links)
|
|
41
|
+
* [Links vs. Buttons in Modern Web Applications](https://marcysutton.com/links-vs-buttons-in-modern-web-applications)
|
|
42
|
+
* [Button vs. Link](https://a11y-101.com/design/button-vs-link)
|
|
43
|
+
* [MDN: ARIA button role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/button_role)
|
|
44
|
+
* [Disabled Links](https://www.scottohara.me/blog/2021/05/28/disabled-links.html)
|
|
45
|
+
* [`erblint-github`: `LinkHasHref`](https://github.com/github/erblint-github/blob/main/docs/rules/accessibility/link-has-href.md)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Linter Rule: `<details>` elements must have a `<summary>` child
|
|
2
|
+
|
|
3
|
+
**Rule:** `html-details-has-summary`
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
Ensure that all `<details>` elements have a direct `<summary>` child element that describes what the disclosure widget will expand.
|
|
8
|
+
|
|
9
|
+
## Rationale
|
|
10
|
+
|
|
11
|
+
The `<summary>` element provides a visible label for the `<details>` disclosure widget, hinting to the user what they'll be expanding. Screen reader users rely on `<summary>` elements to understand the purpose of the expandable content. If a developer omits the `<summary>`, the user agent adds a default one with no meaningful context. The `<summary>` must be a direct child of `<details>` to function correctly — nesting it inside another element will not work as intended.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### ✅ Good
|
|
16
|
+
|
|
17
|
+
```erb
|
|
18
|
+
<details>
|
|
19
|
+
<summary>Expand me!</summary>
|
|
20
|
+
I do have a summary tag!
|
|
21
|
+
</details>
|
|
22
|
+
|
|
23
|
+
<details>
|
|
24
|
+
I do have a summary tag!
|
|
25
|
+
<summary>Expand me!</summary>
|
|
26
|
+
</details>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 🚫 Bad
|
|
30
|
+
|
|
31
|
+
```erb
|
|
32
|
+
<details>
|
|
33
|
+
I don't have a summary tag!
|
|
34
|
+
</details>
|
|
35
|
+
|
|
36
|
+
<details>
|
|
37
|
+
<div><summary>Expand me!</summary></div>
|
|
38
|
+
The summary tag needs to be a direct child of the details tag.
|
|
39
|
+
</details>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## References
|
|
43
|
+
|
|
44
|
+
- [HTML: `details` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)
|
|
45
|
+
- [HTML: `summary` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary)
|
|
46
|
+
- [erblint-github: GitHub::Accessibility::DetailsHasSummary](https://github.com/github/erblint-github/pull/23)
|
|
@@ -31,12 +31,14 @@ Omitting the `alt` attribute entirely leads to poor accessibility and can negati
|
|
|
31
31
|
```erb
|
|
32
32
|
<img src="/logo.png">
|
|
33
33
|
|
|
34
|
-
<img src="/avatar.jpg" alt>
|
|
34
|
+
<img src="/avatar.jpg" alt>
|
|
35
35
|
|
|
36
36
|
<%= image_tag image_path("logo.png") %> <!-- TODO -->
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
## References
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
- [W3C WAI Images Tutorial](https://www.w3.org/WAI/tutorials/images/)
|
|
42
|
+
- [WCAG 2.1: Non-text Content](https://www.w3.org/WAI/WCAG22/quickref/?versions=2.1#non-text-content)
|
|
43
|
+
- [Primer: Alternative text for images](https://primer.style/accessibility/design-guidance/alternative-text-for-images/)
|
|
44
|
+
- [erblint-github: `GitHub::Accessibility::ImageHasAlt`](https://github.com/github/erblint-github/blob/main/docs/rules/accessibility/image-has-alt.md)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Linter Rule: No abstract ARIA roles
|
|
2
|
+
|
|
3
|
+
**Rule:** `html-no-abstract-roles`
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
Prevent usage of WAI-ARIA abstract roles in the `role` attribute.
|
|
8
|
+
|
|
9
|
+
## Rationale
|
|
10
|
+
|
|
11
|
+
The WAI-ARIA specification defines a set of abstract roles that are used to support the ARIA Roles Model for the purpose of defining general role concepts.
|
|
12
|
+
|
|
13
|
+
Abstract roles are used for the ontology only. They exist to help organize the hierarchy of roles and define shared characteristics, but they are not meant to be used by authors directly. Using abstract roles in content provides no semantic meaning to assistive technologies and can lead to accessibility issues.
|
|
14
|
+
|
|
15
|
+
Authors **MUST NOT** use abstract roles in content. Instead, use one of the concrete roles that inherit from these abstract roles. For example, use `button` instead of `command`, or `navigation` instead of `landmark`.
|
|
16
|
+
|
|
17
|
+
The following abstract roles must not be used:
|
|
18
|
+
|
|
19
|
+
- `command`
|
|
20
|
+
- `composite`
|
|
21
|
+
- `input`
|
|
22
|
+
- `landmark`
|
|
23
|
+
- `range`
|
|
24
|
+
- `roletype`
|
|
25
|
+
- `section`
|
|
26
|
+
- `sectionhead`
|
|
27
|
+
- `select`
|
|
28
|
+
- `structure`
|
|
29
|
+
- `widget`
|
|
30
|
+
- `window`
|
|
31
|
+
|
|
32
|
+
## Examples
|
|
33
|
+
|
|
34
|
+
### ✅ Good
|
|
35
|
+
|
|
36
|
+
```erb
|
|
37
|
+
<div role="button">Push it</div>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```erb
|
|
41
|
+
<nav role="navigation">Menu</nav>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```erb
|
|
45
|
+
<div role="alert">Warning!</div>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```erb
|
|
49
|
+
<div role="slider" aria-valuenow="50">Volume</div>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 🚫 Bad
|
|
53
|
+
|
|
54
|
+
```erb
|
|
55
|
+
<div role="window">Hello, world!</div>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```erb
|
|
59
|
+
<div role="widget">Content</div>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```erb
|
|
63
|
+
<div role="command">Action</div>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```erb
|
|
67
|
+
<div role="landmark">Navigation</div>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## References
|
|
71
|
+
|
|
72
|
+
- [WAI-ARIA 1.0: Abstract Roles](https://www.w3.org/TR/wai-aria-1.0/roles#abstract_roles)
|
|
73
|
+
- [WAI-ARIA 1.2: Abstract Roles](https://www.w3.org/TR/wai-aria-1.2/#abstract_roles)
|
|
74
|
+
- [ember-template-lint: no-abstract-roles](https://github.com/ember-template-lint/ember-template-lint/blob/main/docs/rule/no-abstract-roles.md)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Linter Rule: No `aria-hidden` on `<body>`
|
|
2
|
+
|
|
3
|
+
**Rule:** `html-no-aria-hidden-on-body`
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
Prevent usage of `aria-hidden` on `<body>` tags.
|
|
8
|
+
|
|
9
|
+
## Rationale
|
|
10
|
+
|
|
11
|
+
The `aria-hidden` attribute should never be present on the `<body>` element, as it hides the entire document from assistive technology users. This makes the entire page completely inaccessible to screen reader users, which is a critical accessibility violation.
|
|
12
|
+
|
|
13
|
+
## Examples
|
|
14
|
+
|
|
15
|
+
### ✅ Good
|
|
16
|
+
|
|
17
|
+
```erb
|
|
18
|
+
<body>
|
|
19
|
+
<main>Content</main>
|
|
20
|
+
</body>
|
|
21
|
+
|
|
22
|
+
<body class="app" id="main">
|
|
23
|
+
<main>Content</main>
|
|
24
|
+
</body>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 🚫 Bad
|
|
28
|
+
|
|
29
|
+
```erb
|
|
30
|
+
<body aria-hidden>
|
|
31
|
+
<main>Content</main>
|
|
32
|
+
</body>
|
|
33
|
+
|
|
34
|
+
<body aria-hidden="true">
|
|
35
|
+
<main>Content</main>
|
|
36
|
+
</body>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## References
|
|
40
|
+
|
|
41
|
+
- [Using the aria-hidden attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-hidden_attribute)
|
|
42
|
+
- [How Lighthouse identifies hidden body elements](https://web.dev/aria-hidden-body/)
|
|
43
|
+
- [WCAG 4.1.2 - Name, Role, Value (Level A)](https://www.w3.org/TR/WCAG21/#name-role-value)
|
|
44
|
+
- [ember-template-lint: no-aria-hidden-body](https://github.com/ember-template-lint/ember-template-lint/blob/main/docs/rule/no-aria-hidden-body.md)
|