@html-eslint/eslint-plugin 0.35.0-alpha.0 → 0.35.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/lib/rules/attrs-newline.js +11 -7
  2. package/lib/rules/element-newline.js +172 -160
  3. package/lib/rules/id-naming-convention.js +10 -4
  4. package/lib/rules/indent/indent-level.js +1 -1
  5. package/lib/rules/indent/indent.js +28 -15
  6. package/lib/rules/lowercase.js +4 -4
  7. package/lib/rules/max-element-depth.js +8 -5
  8. package/lib/rules/no-abstract-roles.js +4 -4
  9. package/lib/rules/no-accesskey-attrs.js +4 -4
  10. package/lib/rules/no-aria-hidden-body.js +1 -1
  11. package/lib/rules/no-duplicate-attrs.js +4 -4
  12. package/lib/rules/no-duplicate-id.js +5 -5
  13. package/lib/rules/no-extra-spacing-attrs.js +18 -12
  14. package/lib/rules/no-extra-spacing-text.js +8 -5
  15. package/lib/rules/no-heading-inside-button.js +1 -1
  16. package/lib/rules/no-inline-styles.js +1 -1
  17. package/lib/rules/no-invalid-role.js +1 -1
  18. package/lib/rules/no-multiple-empty-lines.js +8 -5
  19. package/lib/rules/no-multiple-h1.js +2 -2
  20. package/lib/rules/no-nested-interactive.js +2 -2
  21. package/lib/rules/no-non-scalable-viewport.js +1 -1
  22. package/lib/rules/no-obsolete-tags.js +1 -1
  23. package/lib/rules/no-positive-tabindex.js +4 -4
  24. package/lib/rules/no-restricted-attr-values.js +5 -5
  25. package/lib/rules/no-restricted-attrs.js +8 -7
  26. package/lib/rules/no-script-style-type.js +4 -4
  27. package/lib/rules/no-skip-heading-levels.js +2 -2
  28. package/lib/rules/no-target-blank.js +1 -1
  29. package/lib/rules/no-trailing-spaces.js +3 -3
  30. package/lib/rules/prefer-https.js +5 -5
  31. package/lib/rules/quotes.js +7 -5
  32. package/lib/rules/require-attrs.js +7 -5
  33. package/lib/rules/require-button-type.js +1 -1
  34. package/lib/rules/require-closing-tags.js +6 -2
  35. package/lib/rules/require-doctype.js +1 -1
  36. package/lib/rules/require-explicit-size.js +8 -3
  37. package/lib/rules/require-form-method.js +1 -1
  38. package/lib/rules/require-frame-title.js +1 -1
  39. package/lib/rules/require-img-alt.js +9 -8
  40. package/lib/rules/require-input-label.js +2 -2
  41. package/lib/rules/require-lang.js +4 -7
  42. package/lib/rules/require-li-container.js +1 -1
  43. package/lib/rules/require-meta-charset.js +3 -3
  44. package/lib/rules/require-meta-description.js +3 -3
  45. package/lib/rules/require-meta-viewport.js +3 -3
  46. package/lib/rules/require-open-graph-protocol.js +6 -3
  47. package/lib/rules/require-title.js +4 -4
  48. package/lib/rules/sort-attrs.js +11 -11
  49. package/lib/rules/utils/node.js +31 -19
  50. package/lib/rules/utils/settings.js +3 -3
  51. package/lib/rules/utils/source-code.js +1 -1
  52. package/lib/rules/utils/visitors.js +1 -1
  53. package/lib/types/ast.d.ts +4 -194
  54. package/lib/types/index.d.ts +1 -1
  55. package/lib/types/rule.d.ts +41 -10
  56. package/package.json +11 -20
  57. package/types/configs/recommended.d.ts +0 -20
  58. package/types/configs/recommended.d.ts.map +0 -1
  59. package/types/constants/index.d.ts +0 -5
  60. package/types/constants/index.d.ts.map +0 -1
  61. package/types/constants/obsolete-tags.d.ts +0 -3
  62. package/types/constants/obsolete-tags.d.ts.map +0 -1
  63. package/types/constants/rule-category.d.ts +0 -5
  64. package/types/constants/rule-category.d.ts.map +0 -1
  65. package/types/constants/svg-camel-case-attributes.d.ts +0 -3
  66. package/types/constants/svg-camel-case-attributes.d.ts.map +0 -1
  67. package/types/constants/void-elements.d.ts +0 -3
  68. package/types/constants/void-elements.d.ts.map +0 -1
  69. package/types/index.d.ts +0 -14
  70. package/types/index.d.ts.map +0 -1
  71. package/types/rules/attrs-newline.d.ts +0 -13
  72. package/types/rules/attrs-newline.d.ts.map +0 -1
  73. package/types/rules/element-newline.d.ts +0 -19
  74. package/types/rules/element-newline.d.ts.map +0 -1
  75. package/types/rules/id-naming-convention.d.ts +0 -10
  76. package/types/rules/id-naming-convention.d.ts.map +0 -1
  77. package/types/rules/indent/indent-level.d.ts +0 -55
  78. package/types/rules/indent/indent-level.d.ts.map +0 -1
  79. package/types/rules/indent/indent.d.ts +0 -30
  80. package/types/rules/indent/indent.d.ts.map +0 -1
  81. package/types/rules/indent/index.d.ts +0 -3
  82. package/types/rules/indent/index.d.ts.map +0 -1
  83. package/types/rules/index.d.ts +0 -51
  84. package/types/rules/index.d.ts.map +0 -1
  85. package/types/rules/lowercase.d.ts +0 -10
  86. package/types/rules/lowercase.d.ts.map +0 -1
  87. package/types/rules/max-element-depth.d.ts +0 -10
  88. package/types/rules/max-element-depth.d.ts.map +0 -1
  89. package/types/rules/no-abstract-roles.d.ts +0 -10
  90. package/types/rules/no-abstract-roles.d.ts.map +0 -1
  91. package/types/rules/no-accesskey-attrs.d.ts +0 -10
  92. package/types/rules/no-accesskey-attrs.d.ts.map +0 -1
  93. package/types/rules/no-aria-hidden-body.d.ts +0 -7
  94. package/types/rules/no-aria-hidden-body.d.ts.map +0 -1
  95. package/types/rules/no-duplicate-attrs.d.ts +0 -10
  96. package/types/rules/no-duplicate-attrs.d.ts.map +0 -1
  97. package/types/rules/no-duplicate-id.d.ts +0 -11
  98. package/types/rules/no-duplicate-id.d.ts.map +0 -1
  99. package/types/rules/no-extra-spacing-attrs.d.ts +0 -18
  100. package/types/rules/no-extra-spacing-attrs.d.ts.map +0 -1
  101. package/types/rules/no-extra-spacing-text.d.ts +0 -13
  102. package/types/rules/no-extra-spacing-text.d.ts.map +0 -1
  103. package/types/rules/no-heading-inside-button.d.ts +0 -7
  104. package/types/rules/no-heading-inside-button.d.ts.map +0 -1
  105. package/types/rules/no-inline-styles.d.ts +0 -7
  106. package/types/rules/no-inline-styles.d.ts.map +0 -1
  107. package/types/rules/no-invalid-role.d.ts +0 -7
  108. package/types/rules/no-invalid-role.d.ts.map +0 -1
  109. package/types/rules/no-multiple-empty-lines.d.ts +0 -11
  110. package/types/rules/no-multiple-empty-lines.d.ts.map +0 -1
  111. package/types/rules/no-multiple-h1.d.ts +0 -8
  112. package/types/rules/no-multiple-h1.d.ts.map +0 -1
  113. package/types/rules/no-nested-interactive.d.ts +0 -8
  114. package/types/rules/no-nested-interactive.d.ts.map +0 -1
  115. package/types/rules/no-non-scalable-viewport.d.ts +0 -7
  116. package/types/rules/no-non-scalable-viewport.d.ts.map +0 -1
  117. package/types/rules/no-obsolete-tags.d.ts +0 -7
  118. package/types/rules/no-obsolete-tags.d.ts.map +0 -1
  119. package/types/rules/no-positive-tabindex.d.ts +0 -10
  120. package/types/rules/no-positive-tabindex.d.ts.map +0 -1
  121. package/types/rules/no-restricted-attr-values.d.ts +0 -16
  122. package/types/rules/no-restricted-attr-values.d.ts.map +0 -1
  123. package/types/rules/no-restricted-attrs.d.ts +0 -16
  124. package/types/rules/no-restricted-attrs.d.ts.map +0 -1
  125. package/types/rules/no-script-style-type.d.ts +0 -10
  126. package/types/rules/no-script-style-type.d.ts.map +0 -1
  127. package/types/rules/no-skip-heading-levels.d.ts +0 -8
  128. package/types/rules/no-skip-heading-levels.d.ts.map +0 -1
  129. package/types/rules/no-target-blank.d.ts +0 -7
  130. package/types/rules/no-target-blank.d.ts.map +0 -1
  131. package/types/rules/no-trailing-spaces.d.ts +0 -9
  132. package/types/rules/no-trailing-spaces.d.ts.map +0 -1
  133. package/types/rules/prefer-https.d.ts +0 -11
  134. package/types/rules/prefer-https.d.ts.map +0 -1
  135. package/types/rules/quotes.d.ts +0 -12
  136. package/types/rules/quotes.d.ts.map +0 -1
  137. package/types/rules/require-attrs.d.ts +0 -15
  138. package/types/rules/require-attrs.d.ts.map +0 -1
  139. package/types/rules/require-button-type.d.ts +0 -7
  140. package/types/rules/require-button-type.d.ts.map +0 -1
  141. package/types/rules/require-closing-tags.d.ts +0 -8
  142. package/types/rules/require-closing-tags.d.ts.map +0 -1
  143. package/types/rules/require-doctype.d.ts +0 -7
  144. package/types/rules/require-doctype.d.ts.map +0 -1
  145. package/types/rules/require-explicit-size.d.ts +0 -9
  146. package/types/rules/require-explicit-size.d.ts.map +0 -1
  147. package/types/rules/require-form-method.d.ts +0 -7
  148. package/types/rules/require-form-method.d.ts.map +0 -1
  149. package/types/rules/require-frame-title.d.ts +0 -7
  150. package/types/rules/require-frame-title.d.ts.map +0 -1
  151. package/types/rules/require-img-alt.d.ts +0 -8
  152. package/types/rules/require-img-alt.d.ts.map +0 -1
  153. package/types/rules/require-input-label.d.ts +0 -8
  154. package/types/rules/require-input-label.d.ts.map +0 -1
  155. package/types/rules/require-lang.d.ts +0 -7
  156. package/types/rules/require-lang.d.ts.map +0 -1
  157. package/types/rules/require-li-container.d.ts +0 -7
  158. package/types/rules/require-li-container.d.ts.map +0 -1
  159. package/types/rules/require-meta-charset.d.ts +0 -9
  160. package/types/rules/require-meta-charset.d.ts.map +0 -1
  161. package/types/rules/require-meta-description.d.ts +0 -9
  162. package/types/rules/require-meta-description.d.ts.map +0 -1
  163. package/types/rules/require-meta-viewport.d.ts +0 -9
  164. package/types/rules/require-meta-viewport.d.ts.map +0 -1
  165. package/types/rules/require-open-graph-protocol.d.ts +0 -9
  166. package/types/rules/require-open-graph-protocol.d.ts.map +0 -1
  167. package/types/rules/require-title.d.ts +0 -10
  168. package/types/rules/require-title.d.ts.map +0 -1
  169. package/types/rules/sort-attrs.d.ts +0 -10
  170. package/types/rules/sort-attrs.d.ts.map +0 -1
  171. package/types/rules/utils/array.d.ts +0 -17
  172. package/types/rules/utils/array.d.ts.map +0 -1
  173. package/types/rules/utils/naming.d.ts +0 -25
  174. package/types/rules/utils/naming.d.ts.map +0 -1
  175. package/types/rules/utils/node.d.ts +0 -111
  176. package/types/rules/utils/node.d.ts.map +0 -1
  177. package/types/rules/utils/settings.d.ts +0 -19
  178. package/types/rules/utils/settings.d.ts.map +0 -1
  179. package/types/rules/utils/source-code.d.ts +0 -9
  180. package/types/rules/utils/source-code.d.ts.map +0 -1
  181. package/types/rules/utils/visitors.d.ts +0 -10
  182. package/types/rules/utils/visitors.d.ts.map +0 -1
@@ -1,11 +1,16 @@
1
1
  /**
2
- * @typedef { import("eslint").Rule.RuleFixer } RuleFixer
3
- * @typedef { import("../types").RuleModule } RuleModule
2
+ * @typedef { import("../types").RuleFixer } RuleFixer
4
3
  *
5
- * @typedef {Object } MessageId
4
+ * @typedef {Object} MessageId
6
5
  * @property {"closeStyleWrong"} CLOSE_STYLE_WRONG
7
6
  * @property {"newlineMissing"} NEWLINE_MISSING
8
7
  * @property {"newlineUnexpected"} NEWLINE_UNEXPECTED
8
+ *
9
+ * @typedef {Object} Option
10
+ * @property {"sameline" | "newline"} [option.closeStyle]
11
+ * @property {number} [options.ifAttrsMoreThan]
12
+ *
13
+ * @typedef { import("../types").RuleModule<[Option]> } RuleModule
9
14
  */
10
15
 
11
16
  const { RULE_CATEGORY } = require("../constants");
@@ -58,9 +63,8 @@ module.exports = {
58
63
 
59
64
  create(context) {
60
65
  const options = context.options[0] || {};
61
- const attrMin = isNaN(options.ifAttrsMoreThan)
62
- ? 2
63
- : options.ifAttrsMoreThan;
66
+ const attrMin =
67
+ typeof options.ifAttrsMoreThan !== "number" ? 2 : options.ifAttrsMoreThan;
64
68
  const closeStyle = options.closeStyle || "newline";
65
69
 
66
70
  return createVisitors(context, {
@@ -147,7 +151,7 @@ module.exports = {
147
151
  return context.report({
148
152
  node,
149
153
  data: {
150
- attrMin,
154
+ attrMin: `${attrMin}`,
151
155
  },
152
156
  fix,
153
157
  messageId: MESSAGE_ID.NEWLINE_UNEXPECTED,
@@ -1,27 +1,36 @@
1
1
  /**
2
- * @typedef { import("../types").RuleModule } RuleModule
3
- * @typedef { import("../types").Tag } Tag
4
- * @typedef { import("../types").Comment } Comment
5
- * @typedef { import("../types").Doctype } Doctype
6
- * @typedef { import("../types").ScriptTag } ScriptTag
7
- * @typedef { import("../types").StyleTag } StyleTag
8
- * @typedef { import("../types").Text } Text
9
- * @typedef { Tag | Doctype | ScriptTag | StyleTag | Text } NewlineNode
10
- * @typedef {{
11
- * childFirst: NewlineNode | null;
12
- * childLast: NewlineNode | null;
13
- * shouldBeNewline: boolean;
14
- * }} NodeMeta
2
+ * @typedef { import("@html-eslint/types").Tag } Tag
3
+ * @typedef { import("@html-eslint/types").Comment } Comment
4
+ * @typedef { import("@html-eslint/types").Doctype } Doctype
5
+ * @typedef { import("@html-eslint/types").ScriptTag } ScriptTag
6
+ * @typedef { import("@html-eslint/types").StyleTag } StyleTag
7
+ * @typedef { import("@html-eslint/types").Text } Text
8
+ * @typedef { import("@html-eslint/types").AnyNode } AnyNode
9
+ * @typedef { import("@html-eslint/types").OpenTagEnd } OpenTagEnd
10
+ * @typedef { import("@html-eslint/types").CloseTag } CloseTag
11
+ * @typedef { import("../types").Line } Line
12
+ * @typedef { AnyNode | Line } AnyNodeOrLine
13
+ *
14
+ * @typedef {Object} Option
15
+ * @property {string[]} [Option.skip]
16
+ * @property {string[]} [Option.inline]
17
+ *
18
+ * @typedef { import("../types").RuleModule<[Option]> } RuleModule
15
19
  */
16
20
 
17
21
  const { RULE_CATEGORY } = require("../constants");
18
- const { isTag, isComment, isText } = require("./utils/node");
22
+ const {
23
+ isTag,
24
+ isComment,
25
+ isText,
26
+ splitToLineNodes,
27
+ isLine,
28
+ isScript,
29
+ isStyle,
30
+ } = require("./utils/node");
19
31
  const { createVisitors } = require("./utils/visitors");
20
32
  const MESSAGE_IDS = {
21
33
  EXPECT_NEW_LINE_AFTER: "expectAfter",
22
- EXPECT_NEW_LINE_AFTER_OPEN: "expectAfterOpen",
23
- EXPECT_NEW_LINE_BEFORE: "expectBefore",
24
- EXPECT_NEW_LINE_BEFORE_CLOSE: "expectBeforeClose",
25
34
  };
26
35
 
27
36
  /**
@@ -100,167 +109,169 @@ module.exports = {
100
109
  ],
101
110
  messages: {
102
111
  [MESSAGE_IDS.EXPECT_NEW_LINE_AFTER]:
103
- "There should be a linebreak after {{tag}} element.",
104
- [MESSAGE_IDS.EXPECT_NEW_LINE_AFTER_OPEN]:
105
- "There should be a linebreak after {{tag}} open.",
106
- [MESSAGE_IDS.EXPECT_NEW_LINE_BEFORE]:
107
- "There should be a linebreak before {{tag}} element.",
108
- [MESSAGE_IDS.EXPECT_NEW_LINE_BEFORE_CLOSE]:
109
- "There should be a linebreak before {{tag}} close.",
112
+ "There should be a linebreak after {{name}}.",
110
113
  },
111
114
  },
112
115
 
113
116
  create(context) {
114
117
  const option = context.options[0] || {};
115
- const skipTags = option.skip || [];
118
+ /**
119
+ * @type {string[]}
120
+ */
121
+ const skipTags = option.skip || ["pre", "code"];
116
122
  const inlineTags = optionsOrPresets(option.inline || []);
117
123
 
118
124
  /**
119
- * @param {Array<NewlineNode>} siblings
120
- * @returns {NodeMeta} meta
125
+ * @param {AnyNodeOrLine[]} children
126
+ * @returns {Exclude<AnyNodeOrLine, Text>[]}
121
127
  */
122
- function checkSiblings(siblings) {
128
+ function getChildrenToCheck(children) {
123
129
  /**
124
- * @type {NodeMeta}
130
+ * @type {Exclude<AnyNodeOrLine, Text>[]}
125
131
  */
126
- const meta = {
127
- childFirst: null,
128
- childLast: null,
129
- shouldBeNewline: false,
130
- };
131
-
132
- const nodesWithContent = [];
133
- for (
134
- let length = siblings.length, index = 0;
135
- index < length;
136
- index += 1
137
- ) {
138
- const node = siblings[index];
132
+ const childrenToCheck = [];
139
133
 
140
- if (isEmptyText(node) === false) {
141
- nodesWithContent.push(node);
134
+ for (const child of children) {
135
+ if (isText(child)) {
136
+ const lines = splitToLineNodes(child);
137
+ childrenToCheck.push(...lines);
138
+ continue;
142
139
  }
140
+ childrenToCheck.push(child);
143
141
  }
142
+ return childrenToCheck.filter((child) => !isEmptyText(child));
143
+ }
144
144
 
145
- for (
146
- let length = nodesWithContent.length, index = 0;
147
- index < length;
148
- index += 1
149
- ) {
150
- const node = nodesWithContent[index];
151
- const nodeNext = nodesWithContent[index + 1];
145
+ /**
146
+ * @param {AnyNodeOrLine} before
147
+ * @param {AnyNodeOrLine} after
148
+ * @returns {boolean}
149
+ */
150
+ function isOnTheSameLine(before, after) {
151
+ return before.loc.end.line === after.loc.start.line;
152
+ }
152
153
 
153
- if (meta.childFirst === null) {
154
- meta.childFirst = node;
155
- }
154
+ /**
155
+ * @param {AnyNode} node
156
+ * @returns {boolean}
157
+ */
158
+ function shouldSkipChildren(node) {
159
+ if (isTag(node) && skipTags.includes(node.name.toLowerCase())) {
160
+ return true;
161
+ }
162
+ return false;
163
+ }
156
164
 
157
- meta.childLast = node;
165
+ /**
166
+ * @param {AnyNodeOrLine} node
167
+ * @returns {boolean}
168
+ */
169
+ function isInline(node) {
170
+ return (
171
+ isLine(node) ||
172
+ (isTag(node) && inlineTags.includes(node.name.toLowerCase()))
173
+ );
174
+ }
175
+
176
+ /**
177
+ * @param {AnyNode[]} children
178
+ * @param {AnyNode} parent
179
+ * @param {[OpenTagEnd, CloseTag]} [wrapper]
180
+ */
181
+ function checkChildren(children, parent, wrapper) {
182
+ if (shouldSkipChildren(parent)) {
183
+ return;
184
+ }
158
185
 
159
- const nodeShouldBeNewline = shouldBeNewline(node);
186
+ const childrenToCheck = getChildrenToCheck(children);
187
+ const firstChild = childrenToCheck[0];
188
+ if (
189
+ wrapper &&
190
+ firstChild &&
191
+ childrenToCheck.some((child) => !isInline(child))
192
+ ) {
193
+ const open = wrapper[0];
194
+ if (isOnTheSameLine(open, firstChild)) {
195
+ context.report({
196
+ node: open,
197
+ messageId: MESSAGE_IDS.EXPECT_NEW_LINE_AFTER,
198
+ data: { name: getName(parent) },
199
+ fix(fixer) {
200
+ return fixer.insertTextAfter(open, `\n`);
201
+ },
202
+ });
203
+ }
204
+ }
160
205
 
161
- if (isTag(node) && skipTags.includes(node.name) === false) {
162
- const nodeMeta = checkSiblings(node.children);
163
- const nodeChildShouldBeNewline = nodeMeta.shouldBeNewline;
206
+ childrenToCheck.forEach((current, index) => {
207
+ const next = childrenToCheck[index + 1];
164
208
 
165
- if (nodeShouldBeNewline || nodeChildShouldBeNewline) {
166
- meta.shouldBeNewline = true;
167
- }
209
+ if (
210
+ !next ||
211
+ !isOnTheSameLine(current, next) ||
212
+ (isInline(current) && isInline(next))
213
+ ) {
214
+ return;
215
+ }
168
216
 
169
- if (
170
- nodeShouldBeNewline &&
171
- nodeChildShouldBeNewline &&
172
- nodeMeta.childFirst &&
173
- nodeMeta.childLast
174
- ) {
175
- if (
176
- node.openEnd.loc.end.line === nodeMeta.childFirst.loc.start.line
177
- ) {
178
- if (isNotNewlineStart(nodeMeta.childFirst)) {
179
- context.report({
180
- node: node,
181
- messageId: MESSAGE_IDS.EXPECT_NEW_LINE_AFTER_OPEN,
182
- data: { tag: label(node) },
183
- fix(fixer) {
184
- return fixer.insertTextAfter(node.openEnd, `\n`);
185
- },
186
- });
187
- }
188
- }
217
+ context.report({
218
+ node: current,
219
+ messageId: MESSAGE_IDS.EXPECT_NEW_LINE_AFTER,
220
+ data: { name: getName(current, { isClose: true }) },
221
+ fix(fixer) {
222
+ return fixer.insertTextAfter(current, `\n`);
223
+ },
224
+ });
225
+ });
189
226
 
190
- if (
191
- node.close &&
192
- nodeMeta.childLast.loc.end.line === node.close.loc.start.line
193
- ) {
194
- if (isNotNewlineEnd(nodeMeta.childLast)) {
195
- context.report({
196
- node: node,
197
- messageId: MESSAGE_IDS.EXPECT_NEW_LINE_BEFORE_CLOSE,
198
- data: { tag: label(node, { isClose: true }) },
199
- fix(fixer) {
200
- return fixer.insertTextBefore(node.close, `\n`);
201
- },
202
- });
203
- }
204
- }
205
- }
227
+ childrenToCheck.forEach((child) => {
228
+ if (isTag(child)) {
229
+ /**
230
+ * @type {[OpenTagEnd, CloseTag] | undefined}
231
+ */
232
+ const wrapper = child.close
233
+ ? [child.openEnd, child.close]
234
+ : undefined;
235
+ checkChildren(child.children, child, wrapper);
206
236
  }
237
+ });
207
238
 
208
- if (nodeNext && node.loc.end.line === nodeNext.loc.start.line) {
209
- if (nodeShouldBeNewline) {
210
- if (isNotNewlineStart(nodeNext)) {
211
- context.report({
212
- node: nodeNext,
213
- messageId: MESSAGE_IDS.EXPECT_NEW_LINE_AFTER,
214
- data: { tag: label(node) },
215
- fix(fixer) {
216
- return fixer.insertTextAfter(node, `\n`);
217
- },
218
- });
219
- }
220
- } else if (shouldBeNewline(nodeNext)) {
221
- if (isNotNewlineEnd(node)) {
222
- context.report({
223
- node: nodeNext,
224
- messageId: MESSAGE_IDS.EXPECT_NEW_LINE_BEFORE,
225
- data: { tag: label(nodeNext) },
226
- fix(fixer) {
227
- return fixer.insertTextBefore(nodeNext, `\n`);
228
- },
229
- });
230
- }
231
- }
239
+ const lastChild = childrenToCheck[childrenToCheck.length - 1];
240
+ if (
241
+ wrapper &&
242
+ lastChild &&
243
+ childrenToCheck.some((child) => !isInline(child))
244
+ ) {
245
+ const close = wrapper[1];
246
+ if (isOnTheSameLine(close, lastChild)) {
247
+ context.report({
248
+ node: lastChild,
249
+ messageId: MESSAGE_IDS.EXPECT_NEW_LINE_AFTER,
250
+ data: { name: getName(lastChild, { isClose: true }) },
251
+ fix(fixer) {
252
+ return fixer.insertTextAfter(lastChild, `\n`);
253
+ },
254
+ });
232
255
  }
233
256
  }
234
-
235
- return meta;
236
257
  }
237
258
 
238
259
  /**
239
- * @param {NewlineNode} node
260
+ * @param {AnyNodeOrLine} node
261
+ * @returns {boolean}
240
262
  */
241
263
  function isEmptyText(node) {
242
- return node.type === `Text` && node.value.trim().length === 0;
243
- }
244
-
245
- /**
246
- * @param {NewlineNode} node
247
- */
248
- function isNotNewlineEnd(node) {
249
- return node.type !== `Text` || /(\n|\r\n)\s*$/.test(node.value) === false;
250
- }
251
-
252
- /**
253
- * @param {NewlineNode} node
254
- */
255
- function isNotNewlineStart(node) {
256
- return node.type !== `Text` || /^(\n|\r\n)/.test(node.value) === false;
264
+ return (
265
+ (isText(node) && node.value.trim().length === 0) ||
266
+ (isLine(node) && node.value.trim().length === 0)
267
+ );
257
268
  }
258
269
 
259
270
  /**
260
- * @param {NewlineNode} node
271
+ * @param {AnyNodeOrLine} node
261
272
  * @param {{ isClose?: boolean }} options
262
273
  */
263
- function label(node, options = {}) {
274
+ function getName(node, options = {}) {
264
275
  const isClose = options.isClose || false;
265
276
  if (isTag(node)) {
266
277
  if (isClose) {
@@ -268,6 +279,24 @@ module.exports = {
268
279
  }
269
280
  return `<${node.name}>`;
270
281
  }
282
+ if (isLine(node)) {
283
+ return "text";
284
+ }
285
+ if (isComment(node)) {
286
+ return "comment";
287
+ }
288
+ if (isScript(node)) {
289
+ if (isClose) {
290
+ return `</script>`;
291
+ }
292
+ return "<script>";
293
+ }
294
+ if (isStyle(node)) {
295
+ if (isClose) {
296
+ return `</style>`;
297
+ }
298
+ return "<style>";
299
+ }
271
300
  return `<${node.type}>`;
272
301
  }
273
302
 
@@ -287,26 +316,9 @@ module.exports = {
287
316
  return result;
288
317
  }
289
318
 
290
- /**
291
- * @param {NewlineNode} node
292
- */
293
- function shouldBeNewline(node) {
294
- if (isComment(node)) {
295
- return /[\n\r]+/.test(node.value.value.trim());
296
- }
297
- if (isTag(node)) {
298
- return inlineTags.includes(node.name.toLowerCase()) === false;
299
- }
300
- if (isText(node)) {
301
- return /[\n\r]+/.test(node.value.trim());
302
- }
303
- return true;
304
- }
305
-
306
319
  return createVisitors(context, {
307
320
  Document(node) {
308
- // @ts-ignore
309
- checkSiblings(node.children);
321
+ checkChildren(node.children, node);
310
322
  },
311
323
  });
312
324
  },
@@ -1,8 +1,14 @@
1
1
  /**
2
- * @typedef { import("../types").RuleModule } RuleModule
3
- * @typedef { import("../types").Tag } Tag
4
- * @typedef { import("../types").ScriptTag } ScriptTag
5
- * @typedef { import("../types").StyleTag } StyleTag
2
+ * @typedef { import("@html-eslint/types").Tag } Tag
3
+ * @typedef { import("@html-eslint/types").ScriptTag } ScriptTag
4
+ * @typedef { import("@html-eslint/types").StyleTag } StyleTag
5
+ *
6
+ * @typedef {"camelCase" | "snake_case" | "PascalCase" | "kebab-case" | "regex"} Option1
7
+ * @typedef {Object} Option2
8
+ * @property {string} pattern
9
+ * @property {string} [flags]
10
+ *
11
+ * @typedef { import("../types").RuleModule<[Option1, Option2]> } RuleModule
6
12
  */
7
13
 
8
14
  const { RULE_CATEGORY } = require("../constants");
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @typedef {import("../../types").AnyNode} AnyNode
2
+ * @typedef {import("@html-eslint/types").AnyNode} AnyNode
3
3
  * @typedef {{ [key in AnyNode['type']]?: number}} IncLevelOptions
4
4
  * @typedef {(node: AnyNode) => number} GetIncreasingLevel
5
5
  */
@@ -1,18 +1,19 @@
1
1
  /**
2
- * @typedef { import("../../types").RuleModule } RuleModule
3
- * @typedef { import("../../types").AnyNode } AnyNode
2
+ * @typedef { import("@html-eslint/types").AnyNode } AnyNode
4
3
  * @typedef { import("../../types").Line } Line
5
- * @typedef { import("../../types").Tag } Tag
4
+ * @typedef { import("@html-eslint/types").Tag } Tag
6
5
  * @typedef { import("../../types").RuleListener } RuleListener
7
- * @typedef { import("../../types").Context } Context
8
- * @typedef { import("../../types").TemplateText } TemplateText
6
+ * @typedef { import("../../types").Context<any[]> } Context
7
+ * @typedef { import("@html-eslint/types").TemplateText } TemplateText
9
8
  * @typedef { import("eslint").AST.Token } Token
10
9
  * @typedef { import("eslint").SourceCode } SourceCode
11
10
  * @typedef { import("eslint").AST.Range } Range
12
11
  * @typedef { import("eslint").AST.SourceLocation } SourceLocation
13
- * @typedef { import("../../types").TemplateLiteral } TemplateLiteral
14
- *
12
+ * @typedef { import("@html-eslint/types").TemplateLiteral } TemplateLiteral
13
+ * @typedef { import("@html-eslint/types").OpenTemplate } OpenTemplate
14
+ * @typedef { import("@html-eslint/types").CloseTemplate } CloseTemplate
15
15
  *
16
+ * @typedef {AnyNode | Line} AnyNodeOrLine
16
17
  * @typedef {Object} IndentType
17
18
  * @property {"tab"} TAB
18
19
  * @property {"space"} SPACE
@@ -22,6 +23,13 @@
22
23
  * @property {IndentType["TAB"] | IndentType["SPACE"]} indentType
23
24
  * @property {number} indentSize
24
25
  * @property {string} indentChar
26
+ *
27
+ * @typedef {"tab" | number} Option1
28
+ * @typedef {Object} Option2
29
+ * @property {number} [Option2.Attribute]
30
+ * @property {Record<string, number>} [Option2.tagChildrenIndent]
31
+ *
32
+ * @typedef { import("../../types").RuleModule<[Option1, Option2]> } RuleModule
25
33
  */
26
34
 
27
35
  const { parse } = require("@html-eslint/template-parser");
@@ -145,9 +153,14 @@ module.exports = {
145
153
  if (isTag(node)) {
146
154
  return getTagIncreasingLevel(node);
147
155
  }
148
- return typeof indentLevelOptions[node.type] === "number"
149
- ? indentLevelOptions[node.type]
150
- : 1;
156
+ const type = node.type;
157
+ if (type === NodeTypes.Attribute) {
158
+ const optionIndent = indentLevelOptions[type];
159
+ if (typeof optionIndent === "number") {
160
+ return optionIndent;
161
+ }
162
+ }
163
+ return 1;
151
164
  }
152
165
 
153
166
  /**
@@ -180,7 +193,7 @@ module.exports = {
180
193
  let parentIgnoringChildCount = 0;
181
194
 
182
195
  /**
183
- * @param {AnyNode | Line | TemplateText} node
196
+ * @param {AnyNode | Line | TemplateText | OpenTemplate | CloseTemplate} node
184
197
  * @returns {string}
185
198
  */
186
199
  function getActualIndent(node) {
@@ -235,7 +248,7 @@ module.exports = {
235
248
  }
236
249
 
237
250
  /**
238
- * @param {AnyNode | Line | TemplateText} node
251
+ * @param {AnyNode | Line | TemplateText | OpenTemplate | CloseTemplate} node
239
252
  */
240
253
  function checkIndent(node) {
241
254
  if (parentIgnoringChildCount > 0) {
@@ -250,11 +263,11 @@ module.exports = {
250
263
  if (actualIndent !== expectedIndent) {
251
264
  const targetNode = getIndentNodeToReport(node, actualIndent);
252
265
  context.report({
253
- node: targetNode,
266
+ loc: targetNode.loc,
254
267
  messageId: MESSAGE_ID.WRONG_INDENT,
255
268
  data: getMessageData(actualIndent, indentLevel.value()),
256
269
  fix(fixer) {
257
- return fixer.replaceText(targetNode, expectedIndent);
270
+ return fixer.replaceTextRange(targetNode.range, expectedIndent);
258
271
  },
259
272
  });
260
273
  }
@@ -396,7 +409,7 @@ module.exports = {
396
409
  };
397
410
 
398
411
  /**
399
- * @param {AnyNode | Line | TemplateText} node
412
+ * @param {AnyNodeOrLine | TemplateText | OpenTemplate | CloseTemplate} node
400
413
  * @param {string} actualIndent
401
414
  * @return {{range: Range; loc: SourceLocation}}
402
415
  */
@@ -1,8 +1,8 @@
1
1
  /**
2
- * @typedef { import("../types").RuleModule } RuleModule
3
- * @typedef { import("../types").Tag } Tag
4
- * @typedef { import("../types").StyleTag } StyleTag
5
- * @typedef { import("../types").ScriptTag } ScriptTag
2
+ * @typedef { import("@html-eslint/types").Tag } Tag
3
+ * @typedef { import("@html-eslint/types").StyleTag } StyleTag
4
+ * @typedef { import("@html-eslint/types").ScriptTag } ScriptTag
5
+ * @typedef { import("../types").RuleModule<[]> } RuleModule
6
6
  */
7
7
 
8
8
  const { NODE_TYPES } = require("@html-eslint/parser");
@@ -1,8 +1,11 @@
1
1
  /**
2
- * @typedef { import("../types").RuleModule } RuleModule
3
- * @typedef { import("../types").Tag } Tag
4
- * @typedef { import("../types").StyleTag } StyleTag
5
- * @typedef { import("../types").ScriptTag } ScriptTag
2
+ * @typedef { import("@html-eslint/types").Tag } Tag
3
+ * @typedef { import("@html-eslint/types").StyleTag } StyleTag
4
+ * @typedef { import("@html-eslint/types").ScriptTag } ScriptTag
5
+ *
6
+ * @typedef {Object} Option
7
+ * @property {number} [Option.max]
8
+ * @typedef { import("../types").RuleModule<[Option]> } RuleModule
6
9
  */
7
10
 
8
11
  const { RULE_CATEGORY } = require("../constants");
@@ -71,7 +74,7 @@ module.exports = {
71
74
  node,
72
75
  messageId: MESSAGE_IDS.MAX_DEPTH_EXCEEDED,
73
76
  data: {
74
- needed: maxDepth,
77
+ needed: `${maxDepth}`,
75
78
  found: String(depth),
76
79
  },
77
80
  });
@@ -1,8 +1,8 @@
1
1
  /**
2
- * @typedef { import("../types").RuleModule } RuleModule
3
- * @typedef { import("../types").Tag } Tag
4
- * @typedef { import("../types").StyleTag } StyleTag
5
- * @typedef { import("../types").ScriptTag } ScriptTag
2
+ * @typedef { import("@html-eslint/types").Tag } Tag
3
+ * @typedef { import("@html-eslint/types").StyleTag } StyleTag
4
+ * @typedef { import("@html-eslint/types").ScriptTag } ScriptTag
5
+ * @typedef { import("../types").RuleModule<[]> } RuleModule
6
6
  */
7
7
 
8
8
  const { RULE_CATEGORY } = require("../constants");
@@ -1,8 +1,8 @@
1
1
  /**
2
- * @typedef { import("../types").RuleModule } RuleModule
3
- * @typedef { import("../types").Tag } Tag
4
- * @typedef { import("../types").StyleTag } StyleTag
5
- * @typedef { import("../types").ScriptTag } ScriptTag
2
+ * @typedef { import("@html-eslint/types").Tag } Tag
3
+ * @typedef { import("@html-eslint/types").StyleTag } StyleTag
4
+ * @typedef { import("@html-eslint/types").ScriptTag } ScriptTag
5
+ * @typedef { import("../types").RuleModule<[]> } RuleModule
6
6
  */
7
7
 
8
8
  const { RULE_CATEGORY } = require("../constants");
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @typedef { import("../types").RuleModule } RuleModule
2
+ * @typedef { import("../types").RuleModule<[]> } RuleModule
3
3
  */
4
4
 
5
5
  const { RULE_CATEGORY } = require("../constants");
@@ -1,8 +1,8 @@
1
1
  /**
2
- * @typedef { import("../types").RuleModule } RuleModule
3
- * @typedef { import("../types").Tag } Tag
4
- * @typedef { import("../types").StyleTag } StyleTag
5
- * @typedef { import("../types").ScriptTag } ScriptTag
2
+ * @typedef { import("@html-eslint/types").Tag } Tag
3
+ * @typedef { import("@html-eslint/types").StyleTag } StyleTag
4
+ * @typedef { import("@html-eslint/types").ScriptTag } ScriptTag
5
+ * @typedef { import("../types").RuleModule<[]> } RuleModule
6
6
  */
7
7
 
8
8
  const { RULE_CATEGORY } = require("../constants");