@html-eslint/eslint-plugin 0.25.0 → 0.27.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/lib/configs/recommended.js +6 -1
- package/lib/rules/element-newline.js +240 -77
- package/lib/rules/index.js +2 -0
- package/lib/rules/no-extra-spacing-attrs.js +152 -88
- package/lib/rules/no-extra-spacing-text.js +117 -0
- package/lib/types.d.ts +8 -0
- package/package.json +3 -3
- package/types/configs/recommended.d.ts +3 -1
- package/types/constants/index.d.ts +5 -0
- package/types/constants/index.d.ts.map +1 -0
- package/types/constants/obsolete-tags.d.ts +3 -0
- package/types/constants/obsolete-tags.d.ts.map +1 -0
- package/types/constants/rule-category.d.ts +5 -0
- package/types/constants/rule-category.d.ts.map +1 -0
- package/types/constants/void-elements.d.ts +3 -0
- package/types/constants/void-elements.d.ts.map +1 -0
- package/types/rules/element-newline.d.ts +18 -0
- package/types/rules/element-newline.d.ts.map +1 -0
- package/types/rules/id-naming-convention.d.ts +7 -0
- package/types/rules/indent.d.ts +15 -0
- package/types/rules/indent.d.ts.map +1 -0
- package/types/rules/index.d.ts +1 -0
- package/types/rules/lowercase.d.ts +7 -0
- package/types/rules/no-abstract-roles.d.ts +7 -0
- package/types/rules/no-abstract-roles.d.ts.map +1 -0
- package/types/rules/no-accesskey-attrs.d.ts +7 -0
- package/types/rules/no-accesskey-attrs.d.ts.map +1 -0
- package/types/rules/no-aria-hidden-body.d.ts +4 -0
- package/types/rules/no-aria-hidden-body.d.ts.map +1 -0
- package/types/rules/no-duplicate-attrs.d.ts +7 -0
- package/types/rules/no-duplicate-attrs.d.ts.map +1 -0
- package/types/rules/no-duplicate-id.d.ts +7 -0
- package/types/rules/no-duplicate-id.d.ts.map +1 -0
- package/types/rules/no-extra-spacing-attrs.d.ts +15 -0
- package/types/rules/no-extra-spacing-attrs.d.ts.map +1 -0
- package/types/rules/no-extra-spacing-text.d.ts +8 -0
- package/types/rules/no-extra-spacing-text.d.ts.map +1 -0
- package/types/rules/no-inline-styles.d.ts +4 -0
- package/types/rules/no-inline-styles.d.ts.map +1 -0
- package/types/rules/no-multiple-empty-lines.d.ts +5 -0
- package/types/rules/no-multiple-empty-lines.d.ts.map +1 -0
- package/types/rules/no-multiple-h1.d.ts +5 -0
- package/types/rules/no-multiple-h1.d.ts.map +1 -0
- package/types/rules/no-non-scalable-viewport.d.ts +4 -0
- package/types/rules/no-non-scalable-viewport.d.ts.map +1 -0
- package/types/rules/no-obsolete-tags.d.ts +4 -0
- package/types/rules/no-obsolete-tags.d.ts.map +1 -0
- package/types/rules/no-positive-tabindex.d.ts +7 -0
- package/types/rules/no-positive-tabindex.d.ts.map +1 -0
- package/types/rules/no-restricted-attr-values.d.ts +13 -0
- package/types/rules/no-restricted-attr-values.d.ts.map +1 -0
- package/types/rules/no-restricted-attrs.d.ts +13 -0
- package/types/rules/no-script-style-type.d.ts +7 -0
- package/types/rules/no-script-style-type.d.ts.map +1 -0
- package/types/rules/no-skip-heading-levels.d.ts +5 -0
- package/types/rules/no-skip-heading-levels.d.ts.map +1 -0
- package/types/rules/no-target-blank.d.ts +4 -0
- package/types/rules/no-target-blank.d.ts.map +1 -0
- package/types/rules/no-trailing-spaces.d.ts +4 -0
- package/types/rules/no-trailing-spaces.d.ts.map +1 -0
- package/types/rules/quotes.d.ts +9 -0
- package/types/rules/quotes.d.ts.map +1 -0
- package/types/rules/require-attrs.d.ts +7 -0
- package/types/rules/require-button-type.d.ts +4 -0
- package/types/rules/require-button-type.d.ts.map +1 -0
- package/types/rules/require-closing-tags.d.ts +5 -0
- package/types/rules/require-doctype.d.ts +4 -0
- package/types/rules/require-doctype.d.ts.map +1 -0
- package/types/rules/require-frame-title.d.ts +4 -0
- package/types/rules/require-frame-title.d.ts.map +1 -0
- package/types/rules/require-img-alt.d.ts +5 -0
- package/types/rules/require-img-alt.d.ts.map +1 -0
- package/types/rules/require-lang.d.ts +4 -0
- package/types/rules/require-lang.d.ts.map +1 -0
- package/types/rules/require-li-container.d.ts +4 -0
- package/types/rules/require-meta-charset.d.ts +5 -0
- package/types/rules/require-meta-description.d.ts +5 -0
- package/types/rules/require-meta-viewport.d.ts +5 -0
- package/types/rules/require-open-graph-protocol.d.ts +5 -0
- package/types/rules/require-title.d.ts +6 -0
- package/types/rules/sort-attrs.d.ts +7 -0
- package/types/rules/sort-attrs.d.ts.map +1 -0
- package/types/rules/utils/array.d.ts +17 -0
- package/types/rules/utils/array.d.ts.map +1 -0
- package/types/rules/utils/naming.d.ts +25 -0
- package/types/rules/utils/naming.d.ts.map +1 -0
- package/types/rules/utils/node.d.ts +37 -0
- package/types/rules/utils/node.d.ts.map +1 -0
- package/types/constants/svg-camelcase-attributes.d.ts +0 -3
- package/types/constants/svg-camelcase-attributes.d.ts.map +0 -1
|
@@ -20,9 +20,14 @@ const MESSAGE_IDS = {
|
|
|
20
20
|
EXTRA_BETWEEN: "unexpectedBetween",
|
|
21
21
|
EXTRA_AFTER: "unexpectedAfter",
|
|
22
22
|
EXTRA_BEFORE: "unexpectedBefore",
|
|
23
|
+
EXTRA_BEFORE_CLOSE: "unexpectedBeforeClose",
|
|
24
|
+
EXTRA_IN_ASSIGNMENT: "unexpectedInAssignment",
|
|
23
25
|
MISSING_BEFORE: "missingBefore",
|
|
24
26
|
MISSING_BEFORE_SELF_CLOSE: "missingBeforeSelfClose",
|
|
25
27
|
EXTRA_BEFORE_SELF_CLOSE: "unexpectedBeforeSelfClose",
|
|
28
|
+
EXTRA_TAB_BEFORE: "unexpectedTabBefore",
|
|
29
|
+
EXTRA_TAB_BEFORE_SELF_CLOSE: "unexpectedTabBeforeSelfClose",
|
|
30
|
+
EXTRA_TAB_BETWEEN: "unexpectedTabBetween",
|
|
26
31
|
};
|
|
27
32
|
|
|
28
33
|
/**
|
|
@@ -43,9 +48,15 @@ module.exports = {
|
|
|
43
48
|
{
|
|
44
49
|
type: "object",
|
|
45
50
|
properties: {
|
|
51
|
+
disallowInAssignment: {
|
|
52
|
+
type: "boolean",
|
|
53
|
+
},
|
|
46
54
|
disallowMissing: {
|
|
47
55
|
type: "boolean",
|
|
48
56
|
},
|
|
57
|
+
disallowTabs: {
|
|
58
|
+
type: "boolean",
|
|
59
|
+
},
|
|
49
60
|
enforceBeforeSelfClose: {
|
|
50
61
|
type: "boolean",
|
|
51
62
|
},
|
|
@@ -56,17 +67,31 @@ module.exports = {
|
|
|
56
67
|
[MESSAGE_IDS.EXTRA_BETWEEN]: "Unexpected space between attributes",
|
|
57
68
|
[MESSAGE_IDS.EXTRA_AFTER]: "Unexpected space after attribute",
|
|
58
69
|
[MESSAGE_IDS.EXTRA_BEFORE]: "Unexpected space before attribute",
|
|
70
|
+
[MESSAGE_IDS.EXTRA_BEFORE_CLOSE]: "Unexpected space before closing",
|
|
71
|
+
[MESSAGE_IDS.EXTRA_IN_ASSIGNMENT]:
|
|
72
|
+
"Unexpected space in attribute assignment",
|
|
59
73
|
[MESSAGE_IDS.MISSING_BEFORE_SELF_CLOSE]:
|
|
60
74
|
"Missing space before self closing",
|
|
61
75
|
[MESSAGE_IDS.EXTRA_BEFORE_SELF_CLOSE]:
|
|
62
76
|
"Unexpected extra spaces before self closing",
|
|
63
77
|
[MESSAGE_IDS.MISSING_BEFORE]: "Missing space before attribute",
|
|
78
|
+
[MESSAGE_IDS.EXTRA_TAB_BEFORE]:
|
|
79
|
+
"Unexpected tab before attribute; use space instead",
|
|
80
|
+
[MESSAGE_IDS.EXTRA_TAB_BEFORE_SELF_CLOSE]:
|
|
81
|
+
"Unexpected tab before self closing; use space instead",
|
|
82
|
+
[MESSAGE_IDS.EXTRA_TAB_BETWEEN]:
|
|
83
|
+
"Unexpected tab between attributes; use space instead",
|
|
64
84
|
},
|
|
65
85
|
},
|
|
66
86
|
create(context) {
|
|
67
87
|
const enforceBeforeSelfClose = !!(context.options[0] || {})
|
|
68
88
|
.enforceBeforeSelfClose;
|
|
69
89
|
const disallowMissing = !!(context.options[0] || {}).disallowMissing;
|
|
90
|
+
const disallowTabs = !!(context.options[0] || {}).disallowTabs;
|
|
91
|
+
const disallowInAssignment = !!(context.options[0] || [])
|
|
92
|
+
.disallowInAssignment;
|
|
93
|
+
|
|
94
|
+
const sourceCode = context.getSourceCode().text;
|
|
70
95
|
|
|
71
96
|
/**
|
|
72
97
|
* @param {AttributeNode[]} attrs
|
|
@@ -87,7 +112,10 @@ module.exports = {
|
|
|
87
112
|
loc: getLocBetween(current, after),
|
|
88
113
|
messageId: MESSAGE_IDS.EXTRA_BETWEEN,
|
|
89
114
|
fix(fixer) {
|
|
90
|
-
return fixer.
|
|
115
|
+
return fixer.replaceTextRange(
|
|
116
|
+
[current.range[1], after.range[0]],
|
|
117
|
+
` `
|
|
118
|
+
);
|
|
91
119
|
},
|
|
92
120
|
});
|
|
93
121
|
} else if (disallowMissing && spacesBetween < 1) {
|
|
@@ -98,48 +126,23 @@ module.exports = {
|
|
|
98
126
|
return fixer.insertTextAfter(current, " ");
|
|
99
127
|
},
|
|
100
128
|
});
|
|
129
|
+
} else if (disallowTabs) {
|
|
130
|
+
if (sourceCode[current.range[1]] === `\t`) {
|
|
131
|
+
context.report({
|
|
132
|
+
loc: getLocBetween(current, after),
|
|
133
|
+
messageId: MESSAGE_IDS.EXTRA_TAB_BETWEEN,
|
|
134
|
+
fix(fixer) {
|
|
135
|
+
return fixer.replaceTextRange(
|
|
136
|
+
[current.range[1], after.range[0]],
|
|
137
|
+
` `
|
|
138
|
+
);
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
}
|
|
101
142
|
}
|
|
102
143
|
});
|
|
103
144
|
}
|
|
104
145
|
|
|
105
|
-
/**
|
|
106
|
-
* @param {OpenTagEndNode | OpenScriptTagEndNode | OpenStyleTagEndNode} openEnd
|
|
107
|
-
* @param {AttributeNode} lastAttr
|
|
108
|
-
* @param {boolean} isSelfClosed
|
|
109
|
-
* @returns {void}
|
|
110
|
-
*/
|
|
111
|
-
function checkExtraSpaceAfter(openEnd, lastAttr, isSelfClosed) {
|
|
112
|
-
if (openEnd.loc.end.line !== lastAttr.loc.end.line) {
|
|
113
|
-
// skip the attribute on the different line with the start tag
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
const limit = isSelfClosed && enforceBeforeSelfClose ? 1 : 0;
|
|
117
|
-
const spacesBetween = openEnd.loc.start.column - lastAttr.loc.end.column;
|
|
118
|
-
|
|
119
|
-
if (spacesBetween > limit) {
|
|
120
|
-
context.report({
|
|
121
|
-
loc: getLocBetween(lastAttr, openEnd),
|
|
122
|
-
messageId: MESSAGE_IDS.EXTRA_AFTER,
|
|
123
|
-
fix(fixer) {
|
|
124
|
-
return fixer.removeRange([
|
|
125
|
-
lastAttr.range[1],
|
|
126
|
-
lastAttr.range[1] + spacesBetween - limit,
|
|
127
|
-
]);
|
|
128
|
-
},
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (isSelfClosed && enforceBeforeSelfClose && spacesBetween < 1) {
|
|
133
|
-
context.report({
|
|
134
|
-
loc: getLocBetween(lastAttr, openEnd),
|
|
135
|
-
messageId: MESSAGE_IDS.MISSING_BEFORE_SELF_CLOSE,
|
|
136
|
-
fix(fixer) {
|
|
137
|
-
return fixer.insertTextAfter(lastAttr, " ");
|
|
138
|
-
},
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
146
|
/**
|
|
144
147
|
* @param {OpenScriptTagStartNode | OpenTagStartNode | OpenStyleTagStartNode} node
|
|
145
148
|
* @param {AttributeNode} firstAttr
|
|
@@ -164,42 +167,19 @@ module.exports = {
|
|
|
164
167
|
]);
|
|
165
168
|
},
|
|
166
169
|
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const spacesBetween =
|
|
181
|
-
openEnd.loc.start.column - beforeSelfClosing.loc.end.column;
|
|
182
|
-
const locBetween = getLocBetween(beforeSelfClosing, openEnd);
|
|
183
|
-
|
|
184
|
-
if (spacesBetween > 1) {
|
|
185
|
-
context.report({
|
|
186
|
-
loc: locBetween,
|
|
187
|
-
messageId: MESSAGE_IDS.EXTRA_BEFORE_SELF_CLOSE,
|
|
188
|
-
fix(fixer) {
|
|
189
|
-
return fixer.removeRange([
|
|
190
|
-
beforeSelfClosing.range[1] + 1,
|
|
191
|
-
openEnd.range[0],
|
|
192
|
-
]);
|
|
193
|
-
},
|
|
194
|
-
});
|
|
195
|
-
} else if (spacesBetween < 1) {
|
|
196
|
-
context.report({
|
|
197
|
-
loc: locBetween,
|
|
198
|
-
messageId: MESSAGE_IDS.MISSING_BEFORE_SELF_CLOSE,
|
|
199
|
-
fix(fixer) {
|
|
200
|
-
return fixer.insertTextAfter(beforeSelfClosing, " ");
|
|
201
|
-
},
|
|
202
|
-
});
|
|
170
|
+
} else if (disallowTabs) {
|
|
171
|
+
if (sourceCode[firstAttr.range[0] - 1] === `\t`) {
|
|
172
|
+
context.report({
|
|
173
|
+
loc: firstAttr.loc,
|
|
174
|
+
messageId: MESSAGE_IDS.EXTRA_TAB_BEFORE,
|
|
175
|
+
fix(fixer) {
|
|
176
|
+
return fixer.replaceTextRange(
|
|
177
|
+
[firstAttr.range[0] - 1, firstAttr.range[0]],
|
|
178
|
+
` `
|
|
179
|
+
);
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
}
|
|
203
183
|
}
|
|
204
184
|
}
|
|
205
185
|
|
|
@@ -215,25 +195,109 @@ module.exports = {
|
|
|
215
195
|
|
|
216
196
|
if (node.attributes.length) {
|
|
217
197
|
checkExtraSpaceBefore(node.openStart, node.attributes[0]);
|
|
198
|
+
|
|
199
|
+
for (const attr of node.attributes) {
|
|
200
|
+
if (attr.startWrapper && attr.value) {
|
|
201
|
+
if (
|
|
202
|
+
disallowInAssignment &&
|
|
203
|
+
attr.startWrapper.loc.start.column - attr.key.loc.end.column > 1
|
|
204
|
+
) {
|
|
205
|
+
const start = attr.key.range[1];
|
|
206
|
+
const end = attr.startWrapper.range[0];
|
|
207
|
+
context.report({
|
|
208
|
+
node: attr,
|
|
209
|
+
messageId: MESSAGE_IDS.EXTRA_IN_ASSIGNMENT,
|
|
210
|
+
fix(fixer) {
|
|
211
|
+
return fixer.replaceTextRange([start, end], `=`);
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
218
217
|
}
|
|
218
|
+
|
|
219
219
|
if (node.openEnd) {
|
|
220
|
+
checkExtraSpacesBetweenAttrs(node.attributes);
|
|
221
|
+
|
|
222
|
+
const lastAttr = node.attributes[node.attributes.length - 1];
|
|
223
|
+
const nodeBeforeEnd =
|
|
224
|
+
node.attributes.length === 0 ? node.openStart : lastAttr;
|
|
225
|
+
|
|
226
|
+
if (nodeBeforeEnd.loc.end.line !== node.openEnd.loc.start.line) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
220
230
|
const isSelfClosing = node.openEnd.value === "/>";
|
|
221
231
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
)
|
|
232
|
+
const spacesBetween =
|
|
233
|
+
node.openEnd.loc.start.column - nodeBeforeEnd.loc.end.column;
|
|
234
|
+
const locBetween = getLocBetween(nodeBeforeEnd, node.openEnd);
|
|
235
|
+
|
|
236
|
+
if (isSelfClosing && enforceBeforeSelfClose) {
|
|
237
|
+
if (spacesBetween < 1) {
|
|
238
|
+
context.report({
|
|
239
|
+
loc: locBetween,
|
|
240
|
+
messageId: MESSAGE_IDS.MISSING_BEFORE_SELF_CLOSE,
|
|
241
|
+
fix(fixer) {
|
|
242
|
+
return fixer.insertTextAfter(nodeBeforeEnd, " ");
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
} else if (spacesBetween === 1) {
|
|
246
|
+
if (
|
|
247
|
+
disallowTabs &&
|
|
248
|
+
sourceCode[node.openEnd.range[0] - 1] === `\t`
|
|
249
|
+
) {
|
|
250
|
+
context.report({
|
|
251
|
+
loc: node.openEnd.loc,
|
|
252
|
+
messageId: MESSAGE_IDS.EXTRA_TAB_BEFORE_SELF_CLOSE,
|
|
253
|
+
fix(fixer) {
|
|
254
|
+
return fixer.replaceTextRange(
|
|
255
|
+
[node.openEnd.range[0] - 1, node.openEnd.range[0]],
|
|
256
|
+
` `
|
|
257
|
+
);
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
context.report({
|
|
263
|
+
loc: locBetween,
|
|
264
|
+
messageId: MESSAGE_IDS.EXTRA_BEFORE_SELF_CLOSE,
|
|
265
|
+
fix(fixer) {
|
|
266
|
+
return fixer.removeRange([
|
|
267
|
+
nodeBeforeEnd.range[1] + 1,
|
|
268
|
+
node.openEnd.range[0],
|
|
269
|
+
]);
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return;
|
|
228
275
|
}
|
|
229
276
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
277
|
+
if (spacesBetween > 0) {
|
|
278
|
+
if (node.attributes.length > 0) {
|
|
279
|
+
context.report({
|
|
280
|
+
loc: locBetween,
|
|
281
|
+
messageId: MESSAGE_IDS.EXTRA_AFTER,
|
|
282
|
+
fix(fixer) {
|
|
283
|
+
return fixer.removeRange([
|
|
284
|
+
lastAttr.range[1],
|
|
285
|
+
node.openEnd.range[0],
|
|
286
|
+
]);
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
} else {
|
|
290
|
+
context.report({
|
|
291
|
+
loc: locBetween,
|
|
292
|
+
messageId: MESSAGE_IDS.EXTRA_BEFORE_CLOSE,
|
|
293
|
+
fix(fixer) {
|
|
294
|
+
return fixer.removeRange([
|
|
295
|
+
node.openStart.range[1],
|
|
296
|
+
node.openEnd.range[0],
|
|
297
|
+
]);
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
}
|
|
237
301
|
}
|
|
238
302
|
}
|
|
239
303
|
},
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef { import("../types").RuleModule } RuleModule
|
|
3
|
+
* @typedef { import("../types").ProgramNode } ProgramNode
|
|
4
|
+
* @typedef { import("es-html-parser").CommentContentNode } CommentContentNode
|
|
5
|
+
* @typedef { import("../types").ContentNode } ContentNode
|
|
6
|
+
* @typedef { import("../types").TextNode } TextNode
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { RULE_CATEGORY } = require("../constants");
|
|
10
|
+
|
|
11
|
+
const MESSAGE_IDS = {
|
|
12
|
+
UNEXPECTED: "unexpected",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @type {RuleModule}
|
|
17
|
+
*/
|
|
18
|
+
module.exports = {
|
|
19
|
+
meta: {
|
|
20
|
+
type: "code",
|
|
21
|
+
|
|
22
|
+
docs: {
|
|
23
|
+
description: "Disallow unnecessary consecutive spaces",
|
|
24
|
+
category: RULE_CATEGORY.BEST_PRACTICE,
|
|
25
|
+
recommended: false,
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
fixable: true,
|
|
29
|
+
schema: [
|
|
30
|
+
{
|
|
31
|
+
type: "object",
|
|
32
|
+
properties: {
|
|
33
|
+
skip: {
|
|
34
|
+
type: "array",
|
|
35
|
+
items: {
|
|
36
|
+
type: "string",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
additionalProperties: false,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
messages: {
|
|
44
|
+
[MESSAGE_IDS.UNEXPECTED]:
|
|
45
|
+
"Tabs and/or multiple consecutive spaces not allowed here",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
create(context) {
|
|
50
|
+
const options = context.options[0] || {};
|
|
51
|
+
const skipTags = options.skip || [];
|
|
52
|
+
const sourceCode = context.getSourceCode();
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {Array<ContentNode>} siblings
|
|
56
|
+
*/
|
|
57
|
+
function checkSiblings(siblings) {
|
|
58
|
+
for (
|
|
59
|
+
let length = siblings.length, index = 0;
|
|
60
|
+
index < length;
|
|
61
|
+
index += 1
|
|
62
|
+
) {
|
|
63
|
+
const node = siblings[index];
|
|
64
|
+
|
|
65
|
+
if (node.type === `Tag` && skipTags.includes(node.name) === false) {
|
|
66
|
+
checkSiblings(node.children);
|
|
67
|
+
} else if (node.type === `Text`) {
|
|
68
|
+
stripConsecutiveSpaces(node);
|
|
69
|
+
} else if (node.type === `Comment`) {
|
|
70
|
+
stripConsecutiveSpaces(node.value);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
Program(node) {
|
|
77
|
+
// @ts-ignore
|
|
78
|
+
checkSiblings(node.body);
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {TextNode | CommentContentNode} node
|
|
84
|
+
*/
|
|
85
|
+
function stripConsecutiveSpaces(node) {
|
|
86
|
+
const text = node.value;
|
|
87
|
+
const matcher = /(^|[^\n \t])([ \t]+\n|\t[\t ]*|[ \t]{2,})/g;
|
|
88
|
+
|
|
89
|
+
// eslint-disable-next-line no-constant-condition
|
|
90
|
+
while (true) {
|
|
91
|
+
const offender = matcher.exec(text);
|
|
92
|
+
if (offender === null) {
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const space = offender[2];
|
|
97
|
+
const indexStart = node.range[0] + matcher.lastIndex - space.length;
|
|
98
|
+
const indexEnd = indexStart + space.length;
|
|
99
|
+
|
|
100
|
+
context.report({
|
|
101
|
+
node: node,
|
|
102
|
+
loc: {
|
|
103
|
+
start: sourceCode.getLocFromIndex(indexStart),
|
|
104
|
+
end: sourceCode.getLocFromIndex(indexEnd),
|
|
105
|
+
},
|
|
106
|
+
messageId: MESSAGE_IDS.UNEXPECTED,
|
|
107
|
+
fix(fixer) {
|
|
108
|
+
return fixer.replaceTextRange(
|
|
109
|
+
[indexStart, indexEnd],
|
|
110
|
+
space.endsWith(`\n`) ? `\n` : ` `
|
|
111
|
+
);
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
};
|
package/lib/types.d.ts
CHANGED
|
@@ -267,3 +267,11 @@ export type ChildType<T extends BaseNode> = T extends ProgramNode
|
|
|
267
267
|
: T extends TagNode
|
|
268
268
|
? T["children"][number]
|
|
269
269
|
: never;
|
|
270
|
+
|
|
271
|
+
export type ContentNode =
|
|
272
|
+
| CommentNode
|
|
273
|
+
| DoctypeNode
|
|
274
|
+
| ScriptTagNode
|
|
275
|
+
| StyleTagNode
|
|
276
|
+
| TagNode
|
|
277
|
+
| TextNode;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@html-eslint/eslint-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "ESLint plugin for html",
|
|
5
5
|
"author": "yeonjuan",
|
|
6
6
|
"homepage": "https://github.com/yeonjuan/html-eslint#readme",
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
"accessibility"
|
|
46
46
|
],
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@html-eslint/parser": "^0.
|
|
48
|
+
"@html-eslint/parser": "^0.27.0",
|
|
49
49
|
"@types/eslint": "^8.56.2",
|
|
50
50
|
"@types/estree": "^0.0.47",
|
|
51
51
|
"es-html-parser": "^0.0.8",
|
|
52
52
|
"typescript": "^4.4.4"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "a7c09dfb3090bb779d6fe62fda814d4d7ca07d4a"
|
|
55
55
|
}
|
|
@@ -6,7 +6,9 @@ export const rules: {
|
|
|
6
6
|
"@html-eslint/no-multiple-h1": string;
|
|
7
7
|
"@html-eslint/no-extra-spacing-attrs": string;
|
|
8
8
|
"@html-eslint/attrs-newline": string;
|
|
9
|
-
"@html-eslint/element-newline": string
|
|
9
|
+
"@html-eslint/element-newline": (string | {
|
|
10
|
+
inline: string[];
|
|
11
|
+
})[];
|
|
10
12
|
"@html-eslint/no-duplicate-id": string;
|
|
11
13
|
"@html-eslint/indent": string;
|
|
12
14
|
"@html-eslint/require-li-container": string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/constants/index.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"obsolete-tags.d.ts","sourceRoot":"","sources":["../../lib/constants/obsolete-tags.js"],"names":[],"mappings":"wBACW,MAAM,EAAE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-category.d.ts","sourceRoot":"","sources":["../../lib/constants/rule-category.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"void-elements.d.ts","sourceRoot":"","sources":["../../lib/constants/void-elements.js"],"names":[],"mappings":"wBAAW,MAAM,EAAE"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
declare const _exports: RuleModule;
|
|
2
|
+
export = _exports;
|
|
3
|
+
export type RuleModule = import("../types").RuleModule;
|
|
4
|
+
export type ProgramNode = import("../types").ProgramNode;
|
|
5
|
+
export type TagNode = import("../types").TagNode;
|
|
6
|
+
export type BaseNode = import("../types").BaseNode;
|
|
7
|
+
export type CommentNode = import("../types").CommentNode;
|
|
8
|
+
export type DoctypeNode = import("../types").DoctypeNode;
|
|
9
|
+
export type ScriptTagNode = import("../types").ScriptTagNode;
|
|
10
|
+
export type StyleTagNode = import("../types").StyleTagNode;
|
|
11
|
+
export type TextNode = import("../types").TextNode;
|
|
12
|
+
export type NewlineNode = CommentNode | DoctypeNode | ScriptTagNode | StyleTagNode | TagNode | TextNode;
|
|
13
|
+
export type NodeMeta = {
|
|
14
|
+
childFirst: NewlineNode | null;
|
|
15
|
+
childLast: NewlineNode | null;
|
|
16
|
+
shouldBeNewline: boolean;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=element-newline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element-newline.d.ts","sourceRoot":"","sources":["../../lib/rules/element-newline.js"],"names":[],"mappings":"wBAoEU,UAAU;;yBAnEN,OAAO,UAAU,EAAE,UAAU;0BAC7B,OAAO,UAAU,EAAE,WAAW;sBAC9B,OAAO,UAAU,EAAE,OAAO;uBAC1B,OAAO,UAAU,EAAE,QAAQ;0BAC3B,OAAO,UAAU,EAAE,WAAW;0BAC9B,OAAO,UAAU,EAAE,WAAW;4BAC9B,OAAO,UAAU,EAAE,aAAa;2BAChC,OAAO,UAAU,EAAE,YAAY;uBAC/B,OAAO,UAAU,EAAE,QAAQ;0BAC3B,WAAW,GAAG,WAAW,GAAG,aAAa,GAAG,YAAY,GAAG,OAAO,GAAG,QAAQ;uBAC9E;IACZ,UAAc,EAAE,WAAW,GAAG,IAAI,CAAC;IACnC,SAAa,EAAE,WAAW,GAAG,IAAI,CAAC;IAClC,eAAmB,EAAE,OAAO,CAAC;CAC1B"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
declare const _exports: RuleModule;
|
|
2
|
+
export = _exports;
|
|
3
|
+
export type RuleModule = import("../types").RuleModule;
|
|
4
|
+
export type TagNode = import("../types").TagNode;
|
|
5
|
+
export type ScriptTagNode = import("../types").ScriptTagNode;
|
|
6
|
+
export type StyleTagNode = import("../types").StyleTagNode;
|
|
7
|
+
//# sourceMappingURL=id-naming-convention.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
declare const _exports: RuleModule;
|
|
2
|
+
export = _exports;
|
|
3
|
+
export type RuleModule = import("../types").RuleModule;
|
|
4
|
+
export type AnyNode = import("../types").AnyNode;
|
|
5
|
+
export type LineNode = import("../types").LineNode;
|
|
6
|
+
export type BaseNode = import("../types").BaseNode;
|
|
7
|
+
export type TagNode = import("../types").TagNode;
|
|
8
|
+
export type IndentType = {
|
|
9
|
+
TAB: "tab";
|
|
10
|
+
SPACE: "space";
|
|
11
|
+
};
|
|
12
|
+
export type MessageId = {
|
|
13
|
+
WRONG_INDENT: "wrongIndent";
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=indent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indent.d.ts","sourceRoot":"","sources":["../../lib/rules/indent.js"],"names":[],"mappings":"wBA8BU,UAAU;;yBA7BN,OAAO,UAAU,EAAE,UAAU;sBAC7B,OAAO,UAAU,EAAE,OAAO;uBAC1B,OAAO,UAAU,EAAE,QAAQ;uBAC3B,OAAO,UAAU,EAAE,QAAQ;sBAC3B,OAAO,UAAU,EAAE,OAAO;;SAE1B,KAAK;WACL,OAAO;;;kBAEP,aAAa"}
|
package/types/rules/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ declare const _exports: {
|
|
|
7
7
|
"no-inline-styles": import("../types").RuleModule;
|
|
8
8
|
"no-multiple-h1": import("../types").RuleModule;
|
|
9
9
|
"no-extra-spacing-attrs": import("../types").RuleModule;
|
|
10
|
+
"no-extra-spacing-text": import("../types").RuleModule;
|
|
10
11
|
"attrs-newline": import("../types").RuleModule;
|
|
11
12
|
"element-newline": import("../types").RuleModule;
|
|
12
13
|
"no-skip-heading-levels": import("../types").RuleModule;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
declare const _exports: RuleModule;
|
|
2
|
+
export = _exports;
|
|
3
|
+
export type RuleModule = import("../types").RuleModule;
|
|
4
|
+
export type TagNode = import("../types").TagNode;
|
|
5
|
+
export type StyleTagNode = import("../types").StyleTagNode;
|
|
6
|
+
export type ScriptTagNode = import("../types").ScriptTagNode;
|
|
7
|
+
//# sourceMappingURL=lowercase.d.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
declare const _exports: RuleModule;
|
|
2
|
+
export = _exports;
|
|
3
|
+
export type RuleModule = import("../types").RuleModule;
|
|
4
|
+
export type TagNode = import("../types").TagNode;
|
|
5
|
+
export type StyleTagNode = import("../types").StyleTagNode;
|
|
6
|
+
export type ScriptTagNode = import("../types").ScriptTagNode;
|
|
7
|
+
//# sourceMappingURL=no-abstract-roles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-abstract-roles.d.ts","sourceRoot":"","sources":["../../lib/rules/no-abstract-roles.js"],"names":[],"mappings":"wBA8BU,UAAU;;yBA7BN,OAAO,UAAU,EAAE,UAAU;sBAC7B,OAAO,UAAU,EAAE,OAAO;2BAC1B,OAAO,UAAU,EAAE,YAAY;4BAC/B,OAAO,UAAU,EAAE,aAAa"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
declare const _exports: RuleModule;
|
|
2
|
+
export = _exports;
|
|
3
|
+
export type RuleModule = import("../types").RuleModule;
|
|
4
|
+
export type TagNode = import("../types").TagNode;
|
|
5
|
+
export type StyleTagNode = import("../types").StyleTagNode;
|
|
6
|
+
export type ScriptTagNode = import("../types").ScriptTagNode;
|
|
7
|
+
//# sourceMappingURL=no-accesskey-attrs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-accesskey-attrs.d.ts","sourceRoot":"","sources":["../../lib/rules/no-accesskey-attrs.js"],"names":[],"mappings":"wBAeU,UAAU;;yBAdN,OAAO,UAAU,EAAE,UAAU;sBAC7B,OAAO,UAAU,EAAE,OAAO;2BAC1B,OAAO,UAAU,EAAE,YAAY;4BAC/B,OAAO,UAAU,EAAE,aAAa"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-aria-hidden-body.d.ts","sourceRoot":"","sources":["../../lib/rules/no-aria-hidden-body.js"],"names":[],"mappings":"wBAYU,UAAU;;yBAXN,OAAO,UAAU,EAAE,UAAU"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
declare const _exports: RuleModule;
|
|
2
|
+
export = _exports;
|
|
3
|
+
export type RuleModule = import("../types").RuleModule;
|
|
4
|
+
export type TagNode = import("../types").TagNode;
|
|
5
|
+
export type StyleTagNode = import("../types").StyleTagNode;
|
|
6
|
+
export type ScriptTagNode = import("../types").ScriptTagNode;
|
|
7
|
+
//# sourceMappingURL=no-duplicate-attrs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-duplicate-attrs.d.ts","sourceRoot":"","sources":["../../lib/rules/no-duplicate-attrs.js"],"names":[],"mappings":"wBAcU,UAAU;;yBAbN,OAAO,UAAU,EAAE,UAAU;sBAC7B,OAAO,UAAU,EAAE,OAAO;2BAC1B,OAAO,UAAU,EAAE,YAAY;4BAC/B,OAAO,UAAU,EAAE,aAAa"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
declare const _exports: RuleModule;
|
|
2
|
+
export = _exports;
|
|
3
|
+
export type RuleModule = import("../types").RuleModule;
|
|
4
|
+
export type TagNode = import("../types").TagNode;
|
|
5
|
+
export type StyleTagNode = import("../types").StyleTagNode;
|
|
6
|
+
export type ScriptTagNode = import("../types").ScriptTagNode;
|
|
7
|
+
//# sourceMappingURL=no-duplicate-id.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-duplicate-id.d.ts","sourceRoot":"","sources":["../../lib/rules/no-duplicate-id.js"],"names":[],"mappings":"wBAeU,UAAU;;yBAdN,OAAO,UAAU,EAAE,UAAU;sBAC7B,OAAO,UAAU,EAAE,OAAO;2BAC1B,OAAO,UAAU,EAAE,YAAY;4BAC/B,OAAO,UAAU,EAAE,aAAa"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
declare const _exports: RuleModule;
|
|
2
|
+
export = _exports;
|
|
3
|
+
export type RuleModule = import("../types").RuleModule;
|
|
4
|
+
export type AttributeNode = import("../types").AttributeNode;
|
|
5
|
+
export type OpenTagEndNode = import("../types").OpenTagEndNode;
|
|
6
|
+
export type OpenScriptTagEndNode = import("../types").OpenScriptTagEndNode;
|
|
7
|
+
export type OpenStyleTagEndNode = import("../types").OpenStyleTagEndNode;
|
|
8
|
+
export type OpenScriptTagStartNode = import("../types").OpenScriptTagStartNode;
|
|
9
|
+
export type OpenTagStartNode = import("../types").OpenTagStartNode;
|
|
10
|
+
export type OpenStyleTagStartNode = import("../types").OpenStyleTagStartNode;
|
|
11
|
+
export type TagNode = import("../types").TagNode;
|
|
12
|
+
export type StyleTagNode = import("../types").StyleTagNode;
|
|
13
|
+
export type ScriptTagNode = import("../types").ScriptTagNode;
|
|
14
|
+
export type AnyNode = import("../types").AnyNode;
|
|
15
|
+
//# sourceMappingURL=no-extra-spacing-attrs.d.ts.map
|