@herb-tools/linter 0.9.0 → 0.9.2

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.
Files changed (62) hide show
  1. package/README.md +2 -2
  2. package/dist/herb-lint.js +1525 -98
  3. package/dist/herb-lint.js.map +1 -1
  4. package/dist/index.cjs +546 -87
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.js +465 -87
  7. package/dist/index.js.map +1 -1
  8. package/dist/lint-worker.js +1523 -96
  9. package/dist/lint-worker.js.map +1 -1
  10. package/dist/loader.cjs +1078 -94
  11. package/dist/loader.cjs.map +1 -1
  12. package/dist/loader.js +1057 -95
  13. package/dist/loader.js.map +1 -1
  14. package/dist/rules/actionview-no-silent-render.js +31 -0
  15. package/dist/rules/actionview-no-silent-render.js.map +1 -0
  16. package/dist/rules/erb-no-case-node-children.js +3 -1
  17. package/dist/rules/erb-no-case-node-children.js.map +1 -1
  18. package/dist/rules/erb-no-duplicate-branch-elements.js +95 -11
  19. package/dist/rules/erb-no-duplicate-branch-elements.js.map +1 -1
  20. package/dist/rules/erb-no-empty-control-flow.js +190 -0
  21. package/dist/rules/erb-no-empty-control-flow.js.map +1 -0
  22. package/dist/rules/erb-no-silent-statement.js +44 -0
  23. package/dist/rules/erb-no-silent-statement.js.map +1 -0
  24. package/dist/rules/erb-no-unsafe-script-interpolation.js +37 -3
  25. package/dist/rules/erb-no-unsafe-script-interpolation.js.map +1 -1
  26. package/dist/rules/html-allowed-script-type.js +1 -1
  27. package/dist/rules/html-allowed-script-type.js.map +1 -1
  28. package/dist/rules/index.js +20 -16
  29. package/dist/rules/index.js.map +1 -1
  30. package/dist/rules/rule-utils.js +14 -23
  31. package/dist/rules/rule-utils.js.map +1 -1
  32. package/dist/rules.js +8 -2
  33. package/dist/rules.js.map +1 -1
  34. package/dist/types/index.d.ts +1 -0
  35. package/dist/types/rules/actionview-no-silent-render.d.ts +9 -0
  36. package/dist/types/rules/erb-no-duplicate-branch-elements.d.ts +1 -0
  37. package/dist/types/rules/erb-no-empty-control-flow.d.ts +8 -0
  38. package/dist/types/rules/erb-no-silent-statement.d.ts +9 -0
  39. package/dist/types/rules/erb-no-unsafe-script-interpolation.d.ts +2 -1
  40. package/dist/types/rules/index.d.ts +20 -16
  41. package/dist/types/rules/rule-utils.d.ts +8 -11
  42. package/dist/types/types.d.ts +4 -3
  43. package/dist/types.js +6 -3
  44. package/dist/types.js.map +1 -1
  45. package/docs/rules/README.md +3 -0
  46. package/docs/rules/actionview-no-silent-render.md +47 -0
  47. package/docs/rules/erb-no-empty-control-flow.md +83 -0
  48. package/docs/rules/erb-no-silent-statement.md +53 -0
  49. package/docs/rules/erb-no-unsafe-script-interpolation.md +70 -3
  50. package/package.json +8 -8
  51. package/src/index.ts +21 -0
  52. package/src/rules/actionview-no-silent-render.ts +44 -0
  53. package/src/rules/erb-no-case-node-children.ts +3 -1
  54. package/src/rules/erb-no-duplicate-branch-elements.ts +130 -14
  55. package/src/rules/erb-no-empty-control-flow.ts +255 -0
  56. package/src/rules/erb-no-silent-statement.ts +58 -0
  57. package/src/rules/erb-no-unsafe-script-interpolation.ts +51 -5
  58. package/src/rules/html-allowed-script-type.ts +1 -1
  59. package/src/rules/index.ts +21 -16
  60. package/src/rules/rule-utils.ts +15 -24
  61. package/src/rules.ts +8 -2
  62. package/src/types.ts +7 -3
@@ -3,40 +3,45 @@ export * from "./file-utils.js"
3
3
  export * from "./string-utils.js"
4
4
  export * from "./herb-disable-comment-base.js"
5
5
 
6
+ export * from "./actionview-no-silent-helper.js"
7
+ export * from "./actionview-no-silent-render.js"
8
+
6
9
  export * from "./erb-comment-syntax.js"
7
10
  export * from "./erb-no-case-node-children.js"
8
- export * from "./erb-no-inline-case-conditions.js"
11
+ export * from "./erb-no-empty-control-flow.js"
9
12
  export * from "./erb-no-conditional-open-tag.js"
10
13
  export * from "./erb-no-duplicate-branch-elements.js"
11
14
  export * from "./erb-no-empty-tags.js"
12
15
  export * from "./erb-no-extra-newline.js"
13
16
  export * from "./erb-no-extra-whitespace-inside-tags.js"
14
- export * from "./erb-no-output-control-flow.js"
15
- export * from "./erb-no-then-in-control-flow.js"
16
- export * from "./erb-no-silent-tag-in-attribute-name.js"
17
- export * from "./erb-no-trailing-whitespace.js"
18
- export * from "./erb-prefer-image-tag-helper.js"
19
- export * from "./erb-require-trailing-newline.js"
20
- export * from "./erb-require-whitespace-inside-tags.js"
17
+ export * from "./erb-no-inline-case-conditions.js"
18
+ export * from "./erb-no-instance-variables-in-partials.js"
21
19
  export * from "./erb-no-javascript-tag-helper.js"
20
+ export * from "./erb-no-output-control-flow.js"
21
+ export * from "./erb-no-output-in-attribute-name.js"
22
+ export * from "./erb-no-output-in-attribute-position.js"
22
23
  export * from "./erb-no-raw-output-in-attribute-value.js"
24
+ export * from "./erb-no-silent-statement.js"
25
+ export * from "./erb-no-silent-tag-in-attribute-name.js"
23
26
  export * from "./erb-no-statement-in-script.js"
27
+ export * from "./erb-no-then-in-control-flow.js"
28
+ export * from "./erb-no-trailing-whitespace.js"
24
29
  export * from "./erb-no-unsafe-js-attribute.js"
25
30
  export * from "./erb-no-unsafe-raw.js"
26
31
  export * from "./erb-no-unsafe-script-interpolation.js"
32
+ export * from "./erb-prefer-image-tag-helper.js"
33
+ export * from "./erb-require-trailing-newline.js"
34
+ export * from "./erb-require-whitespace-inside-tags.js"
27
35
  export * from "./erb-right-trim.js"
28
- export * from "./erb-no-output-in-attribute-position.js"
29
- export * from "./erb-no-output-in-attribute-name.js"
30
- export * from "./erb-no-instance-variables-in-partials.js"
31
36
  export * from "./erb-strict-locals-comment-syntax.js"
32
37
  export * from "./erb-strict-locals-required.js"
33
38
 
34
- export * from "./herb-disable-comment-valid-rule-name.js"
35
- export * from "./herb-disable-comment-no-redundant-all.js"
36
- export * from "./herb-disable-comment-no-duplicate-rules.js"
37
- export * from "./herb-disable-comment-missing-rules.js"
38
39
  export * from "./herb-disable-comment-malformed.js"
40
+ export * from "./herb-disable-comment-missing-rules.js"
41
+ export * from "./herb-disable-comment-no-duplicate-rules.js"
42
+ export * from "./herb-disable-comment-no-redundant-all.js"
39
43
  export * from "./herb-disable-comment-unnecessary.js"
44
+ export * from "./herb-disable-comment-valid-rule-name.js"
40
45
 
41
46
  export * from "./html-allowed-script-type.js"
42
47
  export * from "./html-anchor-require-href.js"
@@ -49,8 +54,8 @@ export * from "./html-attribute-equals-spacing.js"
49
54
  export * from "./html-attribute-values-require-quotes.js"
50
55
  export * from "./html-avoid-both-disabled-and-aria-disabled.js"
51
56
  export * from "./html-body-only-elements.js"
52
- export * from "./html-details-has-summary.js"
53
57
  export * from "./html-boolean-attributes-no-value.js"
58
+ export * from "./html-details-has-summary.js"
54
59
  export * from "./html-head-only-elements.js"
55
60
  export * from "./html-iframe-has-title.js"
56
61
  export * from "./html-img-require-alt.js"
@@ -7,7 +7,7 @@ import {
7
7
  getValidatableStaticContent,
8
8
  getAttributeName,
9
9
  getStaticAttributeValue,
10
- hasDynamicAttributeNameOnAttribute as hasDynamicAttributeName,
10
+ hasDynamicAttributeName,
11
11
  getCombinedAttributeNameString,
12
12
  getAttributeValueNodes,
13
13
  getAttributeValue,
@@ -27,6 +27,7 @@ import type {
27
27
  import { DEFAULT_LINT_CONTEXT } from "../types.js"
28
28
 
29
29
  import type * as Nodes from "@herb-tools/core"
30
+ import type { DiagnosticTag } from "@herb-tools/core"
30
31
  import type { UnboundLintOffense, LintContext, LintSeverity, BaseAutofixContext } from "../types.js"
31
32
 
32
33
  export enum ControlFlowType {
@@ -53,7 +54,7 @@ export abstract class BaseRuleVisitor<TAutofixContext extends BaseAutofixContext
53
54
  * Helper method to create an unbound lint offense (without severity).
54
55
  * The Linter will bind severity based on the rule's config.
55
56
  */
56
- protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity): UnboundLintOffense<TAutofixContext> {
57
+ protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity, tags?: DiagnosticTag[]): UnboundLintOffense<TAutofixContext> {
57
58
  return {
58
59
  rule: this.ruleName,
59
60
  code: this.ruleName,
@@ -62,14 +63,15 @@ export abstract class BaseRuleVisitor<TAutofixContext extends BaseAutofixContext
62
63
  location,
63
64
  autofixContext,
64
65
  severity,
66
+ tags,
65
67
  }
66
68
  }
67
69
 
68
70
  /**
69
71
  * Helper method to add an offense to the offenses array
70
72
  */
71
- protected addOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity): void {
72
- this.offenses.push(this.createOffense(message, location, autofixContext, severity))
73
+ protected addOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity, tags?: DiagnosticTag[]): void {
74
+ this.offenses.push(this.createOffense(message, location, autofixContext, severity, tags))
73
75
  }
74
76
  }
75
77
 
@@ -186,13 +188,7 @@ export const HTML_VOID_ELEMENTS = new Set([
186
188
  "param", "source", "track", "wbr",
187
189
  ])
188
190
 
189
- export const HTML_BOOLEAN_ATTRIBUTES = new Set([
190
- "autofocus", "autoplay", "checked", "controls", "defer", "disabled", "hidden",
191
- "loop", "multiple", "muted", "readonly", "required", "reversed", "selected",
192
- "open", "default", "formnovalidate", "novalidate", "itemscope", "scoped",
193
- "seamless", "allowfullscreen", "async", "compact", "declare", "nohref",
194
- "noresize", "noshade", "nowrap", "sortable", "truespeed", "typemustmatch"
195
- ])
191
+ export { HTML_BOOLEAN_ATTRIBUTES, isBooleanAttribute } from "@herb-tools/core"
196
192
 
197
193
  export const HEADING_TAGS = new Set(["h1", "h2", "h3", "h4", "h5", "h6"])
198
194
 
@@ -399,13 +395,6 @@ export function isVoidElement(tagName: string): boolean {
399
395
  return HTML_VOID_ELEMENTS.has(tagName.toLowerCase())
400
396
  }
401
397
 
402
- /**
403
- * Checks if an attribute is a boolean attribute
404
- */
405
- export function isBooleanAttribute(attributeName: string): boolean {
406
- return HTML_BOOLEAN_ATTRIBUTES.has(attributeName.toLowerCase())
407
- }
408
-
409
398
  /**
410
399
  * Attribute visitor that provides granular processing based on both
411
400
  * attribute name type (static/dynamic) and value type (static/dynamic)
@@ -515,7 +504,7 @@ export abstract class BaseLexerRuleVisitor<TAutofixContext extends BaseAutofixCo
515
504
  * Helper method to create an unbound lint offense (without severity).
516
505
  * The Linter will bind severity based on the rule's config.
517
506
  */
518
- protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity): UnboundLintOffense<TAutofixContext> {
507
+ protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity, tags?: DiagnosticTag[]): UnboundLintOffense<TAutofixContext> {
519
508
  return {
520
509
  rule: this.ruleName,
521
510
  code: this.ruleName,
@@ -524,14 +513,15 @@ export abstract class BaseLexerRuleVisitor<TAutofixContext extends BaseAutofixCo
524
513
  location,
525
514
  autofixContext,
526
515
  severity,
516
+ tags,
527
517
  }
528
518
  }
529
519
 
530
520
  /**
531
521
  * Helper method to add an offense to the offenses array
532
522
  */
533
- protected addOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity): void {
534
- this.offenses.push(this.createOffense(message, location, autofixContext, severity))
523
+ protected addOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity, tags?: DiagnosticTag[]): void {
524
+ this.offenses.push(this.createOffense(message, location, autofixContext, severity, tags))
535
525
  }
536
526
 
537
527
  /**
@@ -578,7 +568,7 @@ export abstract class BaseSourceRuleVisitor<TAutofixContext extends BaseAutofixC
578
568
  * Helper method to create an unbound lint offense (without severity).
579
569
  * The Linter will bind severity based on the rule's config.
580
570
  */
581
- protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity): UnboundLintOffense<TAutofixContext> {
571
+ protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity, tags?: DiagnosticTag[]): UnboundLintOffense<TAutofixContext> {
582
572
  return {
583
573
  rule: this.ruleName,
584
574
  code: this.ruleName,
@@ -587,14 +577,15 @@ export abstract class BaseSourceRuleVisitor<TAutofixContext extends BaseAutofixC
587
577
  location,
588
578
  autofixContext,
589
579
  severity,
580
+ tags,
590
581
  }
591
582
  }
592
583
 
593
584
  /**
594
585
  * Helper method to add an offense to the offenses array
595
586
  */
596
- protected addOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity): void {
597
- this.offenses.push(this.createOffense(message, location, autofixContext, severity))
587
+ protected addOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity, tags?: DiagnosticTag[]): void {
588
+ this.offenses.push(this.createOffense(message, location, autofixContext, severity, tags))
598
589
  }
599
590
 
600
591
  /**
package/src/rules.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  import type { RuleClass } from "./types.js"
2
2
 
3
3
  import { ActionViewNoSilentHelperRule } from "./rules/actionview-no-silent-helper.js"
4
+ import { ActionViewNoSilentRenderRule } from "./rules/actionview-no-silent-render.js"
4
5
 
5
6
  import { ERBCommentSyntax } from "./rules/erb-comment-syntax.js";
6
7
  import { ERBNoCaseNodeChildrenRule } from "./rules/erb-no-case-node-children.js"
8
+ import { ERBNoEmptyControlFlowRule } from "./rules/erb-no-empty-control-flow.js"
7
9
  import { ERBNoConditionalHTMLElementRule } from "./rules/erb-no-conditional-html-element.js"
8
10
  import { ERBNoConditionalOpenTagRule } from "./rules/erb-no-conditional-open-tag.js"
9
11
  import { ERBNoDuplicateBranchElementsRule } from "./rules/erb-no-duplicate-branch-elements.js"
@@ -18,6 +20,7 @@ import { ERBNoOutputControlFlowRule } from "./rules/erb-no-output-control-flow.j
18
20
  import { ERBNoOutputInAttributeNameRule } from "./rules/erb-no-output-in-attribute-name.js"
19
21
  import { ERBNoOutputInAttributePositionRule } from "./rules/erb-no-output-in-attribute-position.js"
20
22
  import { ERBNoRawOutputInAttributeValueRule } from "./rules/erb-no-raw-output-in-attribute-value.js"
23
+ import { ERBNoSilentStatementRule } from "./rules/erb-no-silent-statement.js"
21
24
  import { ERBNoSilentTagInAttributeNameRule } from "./rules/erb-no-silent-tag-in-attribute-name.js"
22
25
  import { ERBNoStatementInScriptRule } from "./rules/erb-no-statement-in-script.js"
23
26
  import { ERBNoThenInControlFlowRule } from "./rules/erb-no-then-in-control-flow.js"
@@ -51,8 +54,8 @@ import { HTMLAttributeEqualsSpacingRule } from "./rules/html-attribute-equals-sp
51
54
  import { HTMLAttributeValuesRequireQuotesRule } from "./rules/html-attribute-values-require-quotes.js"
52
55
  import { HTMLAvoidBothDisabledAndAriaDisabledRule } from "./rules/html-avoid-both-disabled-and-aria-disabled.js"
53
56
  import { HTMLBodyOnlyElementsRule } from "./rules/html-body-only-elements.js"
54
- import { HTMLDetailsHasSummaryRule } from "./rules/html-details-has-summary.js"
55
57
  import { HTMLBooleanAttributesNoValueRule } from "./rules/html-boolean-attributes-no-value.js"
58
+ import { HTMLDetailsHasSummaryRule } from "./rules/html-details-has-summary.js"
56
59
  import { HTMLHeadOnlyElementsRule } from "./rules/html-head-only-elements.js"
57
60
  import { HTMLIframeHasTitleRule } from "./rules/html-iframe-has-title.js"
58
61
  import { HTMLImgRequireAltRule } from "./rules/html-img-require-alt.js"
@@ -84,9 +87,11 @@ import { TurboPermanentRequireIdRule } from "./rules/turbo-permanent-require-id.
84
87
 
85
88
  export const rules: RuleClass[] = [
86
89
  ActionViewNoSilentHelperRule,
90
+ ActionViewNoSilentRenderRule,
87
91
 
88
92
  ERBCommentSyntax,
89
93
  ERBNoCaseNodeChildrenRule,
94
+ ERBNoEmptyControlFlowRule,
90
95
  ERBNoConditionalHTMLElementRule,
91
96
  ERBNoConditionalOpenTagRule,
92
97
  ERBNoDuplicateBranchElementsRule,
@@ -101,6 +106,7 @@ export const rules: RuleClass[] = [
101
106
  ERBNoOutputInAttributeNameRule,
102
107
  ERBNoOutputInAttributePositionRule,
103
108
  ERBNoRawOutputInAttributeValueRule,
109
+ ERBNoSilentStatementRule,
104
110
  ERBNoSilentTagInAttributeNameRule,
105
111
  ERBNoStatementInScriptRule,
106
112
  ERBNoThenInControlFlowRule,
@@ -134,8 +140,8 @@ export const rules: RuleClass[] = [
134
140
  HTMLAttributeValuesRequireQuotesRule,
135
141
  HTMLAvoidBothDisabledAndAriaDisabledRule,
136
142
  HTMLBodyOnlyElementsRule,
137
- HTMLDetailsHasSummaryRule,
138
143
  HTMLBooleanAttributesNoValueRule,
144
+ HTMLDetailsHasSummaryRule,
139
145
  HTMLHeadOnlyElementsRule,
140
146
  HTMLIframeHasTitleRule,
141
147
  HTMLImgRequireAltRule,
package/src/types.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Diagnostic, LexResult, ParseResult, Location } from "@herb-tools/core"
2
2
 
3
+ import type { DiagnosticTag } from "@herb-tools/core"
3
4
  import type { rules } from "./rules.js"
4
5
  import type { Node, ParserOptions } from "@herb-tools/core"
5
6
  import type { RuleConfig } from "@herb-tools/config"
@@ -111,7 +112,7 @@ export abstract class ParserRule<TAutofixContext extends BaseAutofixContext = Ba
111
112
  return DEFAULT_LINTER_PARSER_OPTIONS
112
113
  }
113
114
 
114
- protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity): UnboundLintOffense<TAutofixContext> {
115
+ protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity, tags?: DiagnosticTag[]): UnboundLintOffense<TAutofixContext> {
115
116
  return {
116
117
  rule: this.ruleName,
117
118
  code: this.ruleName,
@@ -120,6 +121,7 @@ export abstract class ParserRule<TAutofixContext extends BaseAutofixContext = Ba
120
121
  location,
121
122
  autofixContext,
122
123
  severity,
124
+ tags,
123
125
  }
124
126
  }
125
127
 
@@ -164,7 +166,7 @@ export abstract class LexerRule<TAutofixContext extends BaseAutofixContext = Bas
164
166
  return DEFAULT_RULE_CONFIG
165
167
  }
166
168
 
167
- protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity): UnboundLintOffense<TAutofixContext> {
169
+ protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity, tags?: DiagnosticTag[]): UnboundLintOffense<TAutofixContext> {
168
170
  return {
169
171
  rule: this.ruleName,
170
172
  code: this.ruleName,
@@ -173,6 +175,7 @@ export abstract class LexerRule<TAutofixContext extends BaseAutofixContext = Bas
173
175
  location,
174
176
  autofixContext,
175
177
  severity,
178
+ tags,
176
179
  }
177
180
  }
178
181
 
@@ -241,7 +244,7 @@ export abstract class SourceRule<TAutofixContext extends BaseAutofixContext = Ba
241
244
  return DEFAULT_RULE_CONFIG
242
245
  }
243
246
 
244
- protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity): UnboundLintOffense<TAutofixContext> {
247
+ protected createOffense(message: string, location: Location, autofixContext?: TAutofixContext, severity?: LintSeverity, tags?: DiagnosticTag[]): UnboundLintOffense<TAutofixContext> {
245
248
  return {
246
249
  rule: this.ruleName,
247
250
  code: this.ruleName,
@@ -250,6 +253,7 @@ export abstract class SourceRule<TAutofixContext extends BaseAutofixContext = Ba
250
253
  location,
251
254
  autofixContext,
252
255
  severity,
256
+ tags,
253
257
  }
254
258
  }
255
259