@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,83 @@
|
|
|
1
|
+
import { ParserRule } from "../types.js"
|
|
2
|
+
import { BaseRuleVisitor } from "./rule-utils.js"
|
|
3
|
+
import { isERBOutputNode, getTagLocalName, isHTMLOpenTagNode } from "@herb-tools/core"
|
|
4
|
+
|
|
5
|
+
import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
6
|
+
import type { ParseResult, ERBContentNode, HTMLElementNode } from "@herb-tools/core"
|
|
7
|
+
|
|
8
|
+
const RAW_PATTERN = /\braw[\s(]/
|
|
9
|
+
const HTML_SAFE_PATTERN = /\.html_safe\b/
|
|
10
|
+
|
|
11
|
+
const RAW_TEXT_ELEMENTS = new Set([
|
|
12
|
+
"title",
|
|
13
|
+
"textarea",
|
|
14
|
+
"script",
|
|
15
|
+
"style",
|
|
16
|
+
"xmp",
|
|
17
|
+
"iframe",
|
|
18
|
+
"noembed",
|
|
19
|
+
"noframes",
|
|
20
|
+
"listing",
|
|
21
|
+
"plaintext",
|
|
22
|
+
])
|
|
23
|
+
|
|
24
|
+
class ERBNoUnsafeRawVisitor extends BaseRuleVisitor {
|
|
25
|
+
private insideRawTextElement = false
|
|
26
|
+
|
|
27
|
+
visitHTMLElementNode(node: HTMLElementNode): void {
|
|
28
|
+
if (!isHTMLOpenTagNode(node.open_tag)) {
|
|
29
|
+
super.visitHTMLElementNode(node)
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const tagName = getTagLocalName(node.open_tag)
|
|
34
|
+
|
|
35
|
+
if (tagName && RAW_TEXT_ELEMENTS.has(tagName)) {
|
|
36
|
+
const wasInside = this.insideRawTextElement
|
|
37
|
+
this.insideRawTextElement = true
|
|
38
|
+
super.visitHTMLElementNode(node)
|
|
39
|
+
this.insideRawTextElement = wasInside
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
super.visitHTMLElementNode(node)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
visitERBContentNode(node: ERBContentNode): void {
|
|
47
|
+
if (this.insideRawTextElement) return
|
|
48
|
+
if (!isERBOutputNode(node)) return
|
|
49
|
+
|
|
50
|
+
const content = node.content?.value || ""
|
|
51
|
+
|
|
52
|
+
if (RAW_PATTERN.test(content)) {
|
|
53
|
+
this.addOffense(
|
|
54
|
+
"Avoid `raw()` in ERB output. It bypasses HTML escaping and can cause cross-site scripting (XSS) vulnerabilities.",
|
|
55
|
+
node.location,
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (HTML_SAFE_PATTERN.test(content)) {
|
|
60
|
+
this.addOffense(
|
|
61
|
+
"Avoid `.html_safe` in ERB output. It bypasses HTML escaping and can cause cross-site scripting (XSS) vulnerabilities.",
|
|
62
|
+
node.location,
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export class ERBNoUnsafeRawRule extends ParserRule {
|
|
69
|
+
static ruleName = "erb-no-unsafe-raw"
|
|
70
|
+
|
|
71
|
+
get defaultConfig(): FullRuleConfig {
|
|
72
|
+
return {
|
|
73
|
+
enabled: true,
|
|
74
|
+
severity: "error"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
79
|
+
const visitor = new ERBNoUnsafeRawVisitor(this.ruleName, context)
|
|
80
|
+
visitor.visit(result.value)
|
|
81
|
+
return visitor.offenses
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { ParserRule } from "../types.js"
|
|
2
|
+
import { BaseRuleVisitor } from "./rule-utils.js"
|
|
3
|
+
import {
|
|
4
|
+
getTagLocalName,
|
|
5
|
+
getAttribute,
|
|
6
|
+
getStaticAttributeValue,
|
|
7
|
+
isERBNode,
|
|
8
|
+
isERBOutputNode,
|
|
9
|
+
isHTMLOpenTagNode,
|
|
10
|
+
} from "@herb-tools/core"
|
|
11
|
+
|
|
12
|
+
import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
13
|
+
import type { ParseResult, HTMLElementNode, Node } from "@herb-tools/core"
|
|
14
|
+
|
|
15
|
+
const SAFE_PATTERN = /\.to_json\b/
|
|
16
|
+
|
|
17
|
+
class ERBNoUnsafeScriptInterpolationVisitor extends BaseRuleVisitor {
|
|
18
|
+
visitHTMLElementNode(node: HTMLElementNode): void {
|
|
19
|
+
if (!isHTMLOpenTagNode(node.open_tag)) {
|
|
20
|
+
super.visitHTMLElementNode(node)
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (getTagLocalName(node.open_tag) === "script") {
|
|
25
|
+
this.checkScriptElement(node)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
super.visitHTMLElementNode(node)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private checkScriptElement(node: HTMLElementNode): void {
|
|
32
|
+
if (!isHTMLOpenTagNode(node.open_tag)) return
|
|
33
|
+
|
|
34
|
+
const typeAttribute = getAttribute(node.open_tag, "type")
|
|
35
|
+
const typeValue = typeAttribute ? getStaticAttributeValue(typeAttribute) : null
|
|
36
|
+
|
|
37
|
+
if (typeValue === "text/html") return
|
|
38
|
+
|
|
39
|
+
if (!node.body || node.body.length === 0) return
|
|
40
|
+
|
|
41
|
+
this.checkNodesForUnsafeOutput(node.body)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private checkNodesForUnsafeOutput(nodes: Node[]): void {
|
|
45
|
+
for (const child of nodes) {
|
|
46
|
+
if (!isERBNode(child)) continue
|
|
47
|
+
if (!isERBOutputNode(child)) continue
|
|
48
|
+
|
|
49
|
+
const content = child.content?.value?.trim() || ""
|
|
50
|
+
|
|
51
|
+
if (SAFE_PATTERN.test(content)) continue
|
|
52
|
+
|
|
53
|
+
this.addOffense(
|
|
54
|
+
"Unsafe ERB output in `<script>` tag. Use `.to_json` to safely serialize values into JavaScript.",
|
|
55
|
+
child.location,
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export class ERBNoUnsafeScriptInterpolationRule extends ParserRule {
|
|
62
|
+
static ruleName = "erb-no-unsafe-script-interpolation"
|
|
63
|
+
|
|
64
|
+
get defaultConfig(): FullRuleConfig {
|
|
65
|
+
return {
|
|
66
|
+
enabled: true,
|
|
67
|
+
severity: "error"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
72
|
+
const visitor = new ERBNoUnsafeScriptInterpolationVisitor(this.ruleName, context)
|
|
73
|
+
visitor.visit(result.value)
|
|
74
|
+
return visitor.offenses
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ParserRule } from "../types.js"
|
|
2
|
-
import { BaseRuleVisitor
|
|
2
|
+
import { BaseRuleVisitor } from "./rule-utils.js"
|
|
3
|
+
import { findAttributeByName, getAttributes, getTagLocalName } from "@herb-tools/core"
|
|
3
4
|
|
|
4
5
|
import { ERBToRubyStringPrinter } from "@herb-tools/printer"
|
|
5
6
|
import { filterNodes, ERBContentNode, LiteralNode, isNode } from "@herb-tools/core"
|
|
@@ -14,7 +15,7 @@ class ERBPreferImageTagHelperVisitor extends BaseRuleVisitor {
|
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
private checkImgTag(openTag: HTMLOpenTagNode): void {
|
|
17
|
-
const tagName =
|
|
18
|
+
const tagName = getTagLocalName(openTag)
|
|
18
19
|
|
|
19
20
|
if (tagName !== "img") return
|
|
20
21
|
|
|
@@ -90,7 +91,7 @@ class ERBPreferImageTagHelperVisitor extends BaseRuleVisitor {
|
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
export class ERBPreferImageTagHelperRule extends ParserRule {
|
|
93
|
-
|
|
94
|
+
static ruleName = "erb-prefer-image-tag-helper"
|
|
94
95
|
|
|
95
96
|
get defaultConfig(): FullRuleConfig {
|
|
96
97
|
return {
|
|
@@ -100,7 +101,7 @@ export class ERBPreferImageTagHelperRule extends ParserRule {
|
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
103
|
-
const visitor = new ERBPreferImageTagHelperVisitor(this.
|
|
104
|
+
const visitor = new ERBPreferImageTagHelperVisitor(this.ruleName, context)
|
|
104
105
|
visitor.visit(result.value)
|
|
105
106
|
return visitor.offenses
|
|
106
107
|
}
|
|
@@ -24,7 +24,7 @@ class ERBRequireTrailingNewlineVisitor extends BaseSourceRuleVisitor {
|
|
|
24
24
|
|
|
25
25
|
export class ERBRequireTrailingNewlineRule extends SourceRule {
|
|
26
26
|
static autocorrectable = true
|
|
27
|
-
|
|
27
|
+
static ruleName = "erb-require-trailing-newline"
|
|
28
28
|
|
|
29
29
|
get defaultConfig(): FullRuleConfig {
|
|
30
30
|
return {
|
|
@@ -34,7 +34,7 @@ export class ERBRequireTrailingNewlineRule extends SourceRule {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
check(source: string, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
37
|
-
const visitor = new ERBRequireTrailingNewlineVisitor(this.
|
|
37
|
+
const visitor = new ERBRequireTrailingNewlineVisitor(this.ruleName, context)
|
|
38
38
|
|
|
39
39
|
visitor.visit(source)
|
|
40
40
|
|
|
@@ -34,7 +34,28 @@ class RequireWhitespaceInsideTags extends BaseRuleVisitor<ERBRequireWhitespaceAu
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
private checkCommentTagWhitespace(node: ERBNode, openTag: Token, closeTag: Token, content: string): void {
|
|
37
|
-
|
|
37
|
+
const commentedTagPrefix = this.getCommentedTagPrefix(content)
|
|
38
|
+
|
|
39
|
+
if (commentedTagPrefix) {
|
|
40
|
+
const afterPrefix = content.substring(commentedTagPrefix.length)
|
|
41
|
+
const tag = `<%#${commentedTagPrefix}`
|
|
42
|
+
|
|
43
|
+
if (afterPrefix.length > 0 && !afterPrefix[0].match(/\s/)) {
|
|
44
|
+
this.addOffense(
|
|
45
|
+
`Add whitespace after \`${tag}\`. This looks like a temporarily commented ERB tag.`,
|
|
46
|
+
openTag.location,
|
|
47
|
+
{
|
|
48
|
+
node,
|
|
49
|
+
openTag,
|
|
50
|
+
closeTag,
|
|
51
|
+
content,
|
|
52
|
+
fixType: "after-comment-equals",
|
|
53
|
+
unsafe: true,
|
|
54
|
+
},
|
|
55
|
+
"info"
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
} else if (!content.startsWith(" ") && !content.startsWith("\n")) {
|
|
38
59
|
this.addOffense(
|
|
39
60
|
`Add whitespace after \`${openTag.value}\`.`,
|
|
40
61
|
openTag.location,
|
|
@@ -46,18 +67,6 @@ class RequireWhitespaceInsideTags extends BaseRuleVisitor<ERBRequireWhitespaceAu
|
|
|
46
67
|
fixType: "after-open"
|
|
47
68
|
}
|
|
48
69
|
)
|
|
49
|
-
} else if (content.startsWith("=") && content.length > 1 && !content[1].match(/\s/)) {
|
|
50
|
-
this.addOffense(
|
|
51
|
-
`Add whitespace after \`<%#=\`.`,
|
|
52
|
-
openTag.location,
|
|
53
|
-
{
|
|
54
|
-
node,
|
|
55
|
-
openTag,
|
|
56
|
-
closeTag,
|
|
57
|
-
content,
|
|
58
|
-
fixType: "after-comment-equals"
|
|
59
|
-
}
|
|
60
|
-
)
|
|
61
70
|
}
|
|
62
71
|
|
|
63
72
|
if (!content.endsWith(" ") && !content.endsWith("\n")) {
|
|
@@ -93,6 +102,17 @@ class RequireWhitespaceInsideTags extends BaseRuleVisitor<ERBRequireWhitespaceAu
|
|
|
93
102
|
)
|
|
94
103
|
}
|
|
95
104
|
|
|
105
|
+
private getCommentedTagPrefix(content: string): string | null {
|
|
106
|
+
if (content.startsWith("graphql")) return "graphql"
|
|
107
|
+
if (content.startsWith("%=")) return "%="
|
|
108
|
+
if (content.startsWith("==")) return "=="
|
|
109
|
+
if (content.startsWith("%")) return "%"
|
|
110
|
+
if (content.startsWith("=")) return "="
|
|
111
|
+
if (content.startsWith("-")) return "-"
|
|
112
|
+
|
|
113
|
+
return null
|
|
114
|
+
}
|
|
115
|
+
|
|
96
116
|
private checkCloseTagWhitespace(node: ERBNode, openTag: Token, closeTag: Token, content: string):void {
|
|
97
117
|
if (content.endsWith(" ") || content.endsWith("\n")) {
|
|
98
118
|
return
|
|
@@ -114,7 +134,7 @@ class RequireWhitespaceInsideTags extends BaseRuleVisitor<ERBRequireWhitespaceAu
|
|
|
114
134
|
|
|
115
135
|
export class ERBRequireWhitespaceRule extends ParserRule<ERBRequireWhitespaceAutofixContext> {
|
|
116
136
|
static autocorrectable = true
|
|
117
|
-
|
|
137
|
+
static ruleName = "erb-require-whitespace-inside-tags"
|
|
118
138
|
|
|
119
139
|
get defaultConfig(): FullRuleConfig {
|
|
120
140
|
return {
|
|
@@ -124,7 +144,7 @@ export class ERBRequireWhitespaceRule extends ParserRule<ERBRequireWhitespaceAut
|
|
|
124
144
|
}
|
|
125
145
|
|
|
126
146
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense<ERBRequireWhitespaceAutofixContext>[] {
|
|
127
|
-
const visitor = new RequireWhitespaceInsideTags(this.
|
|
147
|
+
const visitor = new RequireWhitespaceInsideTags(this.ruleName, context)
|
|
128
148
|
|
|
129
149
|
visitor.visit(result.value)
|
|
130
150
|
|
|
@@ -152,10 +172,14 @@ export class ERBRequireWhitespaceRule extends ParserRule<ERBRequireWhitespaceAut
|
|
|
152
172
|
return result
|
|
153
173
|
}
|
|
154
174
|
|
|
155
|
-
if (fixType === "after-comment-equals"
|
|
156
|
-
|
|
175
|
+
if (fixType === "after-comment-equals") {
|
|
176
|
+
const prefix = content.startsWith("graphql") ? "graphql" : content.startsWith("%=") ? "%=" : content.startsWith("==") ? "==" : content.startsWith("%") ? "%" : content.startsWith("=") ? "=" : content.startsWith("-") ? "-" : null
|
|
157
177
|
|
|
158
|
-
|
|
178
|
+
if (prefix) {
|
|
179
|
+
node.content.value = prefix + " " + content.substring(prefix.length)
|
|
180
|
+
|
|
181
|
+
return result
|
|
182
|
+
}
|
|
159
183
|
}
|
|
160
184
|
|
|
161
185
|
return null
|
|
@@ -26,7 +26,7 @@ class ERBRightTrimVisitor extends BaseRuleVisitor<ERBRightTrimAutofixContext> {
|
|
|
26
26
|
|
|
27
27
|
export class ERBRightTrimRule extends ParserRule<ERBRightTrimAutofixContext> {
|
|
28
28
|
static autocorrectable = true
|
|
29
|
-
|
|
29
|
+
static ruleName = "erb-right-trim"
|
|
30
30
|
|
|
31
31
|
get defaultConfig(): FullRuleConfig {
|
|
32
32
|
return {
|
|
@@ -36,7 +36,7 @@ export class ERBRightTrimRule extends ParserRule<ERBRightTrimAutofixContext> {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense<ERBRightTrimAutofixContext>[] {
|
|
39
|
-
const visitor = new ERBRightTrimVisitor(this.
|
|
39
|
+
const visitor = new ERBRightTrimVisitor(this.ruleName, context)
|
|
40
40
|
|
|
41
41
|
visitor.visit(result.value)
|
|
42
42
|
|
|
@@ -7,7 +7,7 @@ import { hasBalancedParentheses, splitByTopLevelComma } from "./string-utils.js"
|
|
|
7
7
|
import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
8
8
|
import type { ParseResult, ERBContentNode } from "@herb-tools/core"
|
|
9
9
|
|
|
10
|
-
export const STRICT_LOCALS_PATTERN = /^locals:\s+\(
|
|
10
|
+
export const STRICT_LOCALS_PATTERN = /^locals:\s+\(.*\)\s*$/s
|
|
11
11
|
|
|
12
12
|
function isValidStrictLocalsFormat(content: string): boolean {
|
|
13
13
|
return STRICT_LOCALS_PATTERN.test(content)
|
|
@@ -42,7 +42,7 @@ function detectLocalsWithoutColon(content: string): boolean {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
function detectSingularLocal(content: string): boolean {
|
|
45
|
-
return
|
|
45
|
+
return content.startsWith('local:')
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
function detectMissingColonBeforeParens(content: string): boolean {
|
|
@@ -50,7 +50,7 @@ function detectMissingColonBeforeParens(content: string): boolean {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
function detectMissingSpaceAfterColon(content: string): boolean {
|
|
53
|
-
return
|
|
53
|
+
return content.startsWith('locals:(')
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function detectMissingParentheses(content: string): boolean {
|
|
@@ -264,7 +264,7 @@ class ERBStrictLocalsCommentSyntaxVisitor extends BaseRuleVisitor {
|
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
export class ERBStrictLocalsCommentSyntaxRule extends ParserRule {
|
|
267
|
-
|
|
267
|
+
static ruleName = "erb-strict-locals-comment-syntax"
|
|
268
268
|
|
|
269
269
|
get defaultConfig(): FullRuleConfig {
|
|
270
270
|
return {
|
|
@@ -274,7 +274,7 @@ export class ERBStrictLocalsCommentSyntaxRule extends ParserRule {
|
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
277
|
-
const visitor = new ERBStrictLocalsCommentSyntaxVisitor(this.
|
|
277
|
+
const visitor = new ERBStrictLocalsCommentSyntaxVisitor(this.ruleName, context)
|
|
278
278
|
|
|
279
279
|
visitor.visit(result.value)
|
|
280
280
|
|
|
@@ -29,7 +29,7 @@ class ERBStrictLocalsRequiredVisitor extends BaseSourceRuleVisitor {
|
|
|
29
29
|
|
|
30
30
|
export class ERBStrictLocalsRequiredRule extends SourceRule {
|
|
31
31
|
static unsafeAutocorrectable = true
|
|
32
|
-
|
|
32
|
+
static ruleName = "erb-strict-locals-required"
|
|
33
33
|
|
|
34
34
|
get defaultConfig(): FullRuleConfig {
|
|
35
35
|
return {
|
|
@@ -39,7 +39,7 @@ export class ERBStrictLocalsRequiredRule extends SourceRule {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
check(source: string, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
42
|
-
const visitor = new ERBStrictLocalsRequiredVisitor(this.
|
|
42
|
+
const visitor = new ERBStrictLocalsRequiredVisitor(this.ruleName, context)
|
|
43
43
|
|
|
44
44
|
visitor.visit(source)
|
|
45
45
|
|
|
@@ -47,7 +47,7 @@ class HerbDisableCommentMalformedVisitor extends HerbDisableCommentBaseVisitor {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
export class HerbDisableCommentMalformedRule extends ParserRule {
|
|
50
|
-
|
|
50
|
+
static ruleName = "herb-disable-comment-malformed"
|
|
51
51
|
|
|
52
52
|
get defaultConfig(): FullRuleConfig {
|
|
53
53
|
return {
|
|
@@ -57,7 +57,7 @@ export class HerbDisableCommentMalformedRule extends ParserRule {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
60
|
-
const visitor = new HerbDisableCommentMalformedVisitor(this.
|
|
60
|
+
const visitor = new HerbDisableCommentMalformedVisitor(this.ruleName, context)
|
|
61
61
|
|
|
62
62
|
visitor.visit(result.value)
|
|
63
63
|
|
|
@@ -22,7 +22,7 @@ class HerbDisableCommentMissingRulesVisitor extends HerbDisableCommentBaseVisito
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export class HerbDisableCommentMissingRulesRule extends ParserRule {
|
|
25
|
-
|
|
25
|
+
static ruleName = "herb-disable-comment-missing-rules"
|
|
26
26
|
|
|
27
27
|
get defaultConfig(): FullRuleConfig {
|
|
28
28
|
return {
|
|
@@ -32,7 +32,7 @@ export class HerbDisableCommentMissingRulesRule extends ParserRule {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
35
|
-
const visitor = new HerbDisableCommentMissingRulesVisitor(this.
|
|
35
|
+
const visitor = new HerbDisableCommentMissingRulesVisitor(this.ruleName, context)
|
|
36
36
|
|
|
37
37
|
visitor.visit(result.value)
|
|
38
38
|
|
|
@@ -27,7 +27,7 @@ class HerbDisableCommentNoDuplicateRulesVisitor extends HerbDisableCommentParsed
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export class HerbDisableCommentNoDuplicateRulesRule extends ParserRule {
|
|
30
|
-
|
|
30
|
+
static ruleName = "herb-disable-comment-no-duplicate-rules"
|
|
31
31
|
|
|
32
32
|
get defaultConfig(): FullRuleConfig {
|
|
33
33
|
return {
|
|
@@ -37,7 +37,7 @@ export class HerbDisableCommentNoDuplicateRulesRule extends ParserRule {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
40
|
-
const visitor = new HerbDisableCommentNoDuplicateRulesVisitor(this.
|
|
40
|
+
const visitor = new HerbDisableCommentNoDuplicateRulesVisitor(this.ruleName, context)
|
|
41
41
|
|
|
42
42
|
visitor.visit(result.value)
|
|
43
43
|
|
|
@@ -21,7 +21,7 @@ class HerbDisableCommentNoRedundantAllVisitor extends HerbDisableCommentParsedVi
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export class HerbDisableCommentNoRedundantAllRule extends ParserRule {
|
|
24
|
-
|
|
24
|
+
static ruleName = "herb-disable-comment-no-redundant-all"
|
|
25
25
|
|
|
26
26
|
get defaultConfig(): FullRuleConfig {
|
|
27
27
|
return {
|
|
@@ -31,7 +31,7 @@ export class HerbDisableCommentNoRedundantAllRule extends ParserRule {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
34
|
-
const visitor = new HerbDisableCommentNoRedundantAllVisitor(this.
|
|
34
|
+
const visitor = new HerbDisableCommentNoRedundantAllVisitor(this.ruleName, context)
|
|
35
35
|
|
|
36
36
|
visitor.visit(result.value)
|
|
37
37
|
|
|
@@ -72,7 +72,7 @@ class HerbDisableCommentUnnecessaryVisitor extends HerbDisableCommentParsedVisit
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
export class HerbDisableCommentUnnecessaryRule extends ParserRule {
|
|
75
|
-
|
|
75
|
+
static ruleName = "herb-disable-comment-unnecessary"
|
|
76
76
|
|
|
77
77
|
get defaultConfig(): FullRuleConfig {
|
|
78
78
|
return {
|
|
@@ -90,7 +90,7 @@ export class HerbDisableCommentUnnecessaryRule extends ParserRule {
|
|
|
90
90
|
if (!ignoredOffensesByLine) return []
|
|
91
91
|
|
|
92
92
|
const visitor = new HerbDisableCommentUnnecessaryVisitor(
|
|
93
|
-
this.
|
|
93
|
+
this.ruleName,
|
|
94
94
|
ignoredOffensesByLine,
|
|
95
95
|
validRuleNames,
|
|
96
96
|
context
|
|
@@ -34,7 +34,7 @@ class HerbDisableCommentValidRuleNameVisitor extends HerbDisableCommentParsedVis
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export class HerbDisableCommentValidRuleNameRule extends ParserRule {
|
|
37
|
-
|
|
37
|
+
static ruleName = "herb-disable-comment-valid-rule-name"
|
|
38
38
|
|
|
39
39
|
get defaultConfig(): FullRuleConfig {
|
|
40
40
|
return {
|
|
@@ -50,7 +50,7 @@ export class HerbDisableCommentValidRuleNameRule extends ParserRule {
|
|
|
50
50
|
if (validRuleNames.length === 0) return []
|
|
51
51
|
|
|
52
52
|
const visitor = new HerbDisableCommentValidRuleNameVisitor(
|
|
53
|
-
this.
|
|
53
|
+
this.ruleName,
|
|
54
54
|
validRuleNames,
|
|
55
55
|
context
|
|
56
56
|
)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ParserRule } from "../types.js"
|
|
2
|
+
import { BaseRuleVisitor } from "./rule-utils.js"
|
|
3
|
+
import { getTagLocalName, getAttribute, getStaticAttributeValue, hasAttributeValue } from "@herb-tools/core"
|
|
4
|
+
|
|
5
|
+
import type { UnboundLintOffense, LintContext, FullRuleConfig } from "../types.js"
|
|
6
|
+
import type { HTMLAttributeNode, HTMLOpenTagNode, ParseResult } from "@herb-tools/core"
|
|
7
|
+
|
|
8
|
+
const ALLOWED_TYPES = ["text/javascript"]
|
|
9
|
+
// NOTE: Rules are not configurable for now, keep some sane defaults
|
|
10
|
+
// See https://github.com/marcoroth/herb/issues/1204
|
|
11
|
+
const ALLOW_BLANK = true
|
|
12
|
+
|
|
13
|
+
class AllowedScriptTypeVisitor extends BaseRuleVisitor {
|
|
14
|
+
visitHTMLOpenTagNode(node: HTMLOpenTagNode): void {
|
|
15
|
+
if (getTagLocalName(node) === "script") {
|
|
16
|
+
this.visitScriptNode(node)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private visitScriptNode(node: HTMLOpenTagNode): void {
|
|
21
|
+
const typeAttribute = getAttribute(node, "type")
|
|
22
|
+
|
|
23
|
+
if (!typeAttribute) {
|
|
24
|
+
if (!ALLOW_BLANK) {
|
|
25
|
+
this.addOffense("`type` attribute required for `<script>` tag.", node.location)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!hasAttributeValue(typeAttribute)) {
|
|
32
|
+
this.addOffense(
|
|
33
|
+
"Avoid using an empty `type` attribute on the `<script>` tag. Either set a valid type or remove the attribute entirely.",
|
|
34
|
+
typeAttribute.location
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
this.validateTypeAttribute(typeAttribute)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private validateTypeAttribute(typeAttribute: HTMLAttributeNode): void {
|
|
44
|
+
const typeValue = getStaticAttributeValue(typeAttribute)
|
|
45
|
+
if (typeValue === null) return
|
|
46
|
+
|
|
47
|
+
if (typeValue === "") {
|
|
48
|
+
this.addOffense(
|
|
49
|
+
"Avoid using an empty `type` attribute on the `<script>` tag. Either set a valid type or remove the attribute entirely.",
|
|
50
|
+
typeAttribute.location
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (ALLOWED_TYPES.includes(typeValue)) return
|
|
57
|
+
|
|
58
|
+
this.addOffense(
|
|
59
|
+
`Avoid using \`${typeValue}\` as the \`type\` attribute for the \`<script>\` tag. ` +
|
|
60
|
+
`Must be one of: ${ALLOWED_TYPES.map(t => `\`${t}\``).join(", ")}` +
|
|
61
|
+
`${ALLOW_BLANK ? " or blank" : ""}.`,
|
|
62
|
+
typeAttribute.location
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export class HTMLAllowedScriptTypeRule extends ParserRule {
|
|
68
|
+
static ruleName = "html-allowed-script-type"
|
|
69
|
+
|
|
70
|
+
get defaultConfig(): FullRuleConfig {
|
|
71
|
+
return {
|
|
72
|
+
enabled: true,
|
|
73
|
+
severity: "error"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
check(result: ParseResult, context?: Partial<LintContext>): UnboundLintOffense[] {
|
|
78
|
+
const visitor = new AllowedScriptTypeVisitor(this.ruleName, context)
|
|
79
|
+
|
|
80
|
+
visitor.visit(result.value)
|
|
81
|
+
|
|
82
|
+
return visitor.offenses
|
|
83
|
+
}
|
|
84
|
+
}
|