@html-eslint/eslint-plugin 0.33.1 → 0.34.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.
@@ -12,11 +12,7 @@ const {
12
12
  isPascalCase,
13
13
  isKebabCase,
14
14
  } = require("./utils/naming");
15
- const {
16
- findAttr,
17
- isAttributesEmpty,
18
- isExpressionInTemplate,
19
- } = require("./utils/node");
15
+ const { findAttr, isAttributesEmpty, hasTemplate } = require("./utils/node");
20
16
  const { createVisitors } = require("./utils/visitors");
21
17
 
22
18
  const MESSAGE_IDS = {
@@ -94,7 +90,12 @@ module.exports = {
94
90
  return;
95
91
  }
96
92
  const idAttr = findAttr(node, "id");
97
- if (idAttr && idAttr.value && !checkNaming(idAttr.value.value)) {
93
+ if (
94
+ idAttr &&
95
+ idAttr.value &&
96
+ !hasTemplate(idAttr.value) &&
97
+ !checkNaming(idAttr.value.value)
98
+ ) {
98
99
  context.report({
99
100
  node: idAttr,
100
101
  data: {
@@ -117,7 +118,7 @@ module.exports = {
117
118
  if (
118
119
  idAttr &&
119
120
  idAttr.value &&
120
- !isExpressionInTemplate(idAttr.value) &&
121
+ !hasTemplate(idAttr.value) &&
121
122
  !checkNaming(idAttr.value.value)
122
123
  ) {
123
124
  context.report({
@@ -9,6 +9,7 @@ const { NODE_TYPES } = require("@html-eslint/parser");
9
9
  const { RULE_CATEGORY } = require("../constants");
10
10
  const SVG_CAMEL_CASE_ATTRIBUTES = require("../constants/svg-camel-case-attributes");
11
11
  const { createVisitors } = require("./utils/visitors");
12
+ const { hasTemplate } = require("./utils/node");
12
13
 
13
14
  const MESSAGE_IDS = {
14
15
  UNEXPECTED: "unexpected",
@@ -102,6 +103,9 @@ module.exports = {
102
103
  if (isAllowedAttributeKey(attribute.key.value)) {
103
104
  return;
104
105
  }
106
+ if (hasTemplate(attribute.key)) {
107
+ return;
108
+ }
105
109
  if (attribute.key.value !== attribute.key.value.toLowerCase()) {
106
110
  context.report({
107
111
  node: attribute.key,
@@ -41,7 +41,7 @@ module.exports = {
41
41
  if (Array.isArray(node.attributes)) {
42
42
  const attrsSet = new Set();
43
43
  node.attributes.forEach((attr) => {
44
- if (attr.key && attrsSet.has(attr.key.value)) {
44
+ if (attr.key && attrsSet.has(attr.key.value.toLowerCase())) {
45
45
  context.report({
46
46
  node: attr,
47
47
  data: {
@@ -50,7 +50,7 @@ module.exports = {
50
50
  messageId: MESSAGE_IDS.DUPLICATE_ATTRS,
51
51
  });
52
52
  } else {
53
- attrsSet.add(attr.key.value);
53
+ attrsSet.add(attr.key.value.toLowerCase());
54
54
  }
55
55
  });
56
56
  }
@@ -5,6 +5,7 @@
5
5
  * @typedef { import("../types").Text } Text
6
6
  */
7
7
 
8
+ const { hasTemplate } = require("./utils/node");
8
9
  const { RULE_CATEGORY } = require("../constants");
9
10
  const { getSourceCode } = require("./utils/source-code");
10
11
  const { createVisitors } = require("./utils/visitors");
@@ -62,6 +63,7 @@ module.exports = {
62
63
  function compare(attrA, attrB) {
63
64
  const keyA = attrA.key.value;
64
65
  const keyB = attrB.key.value;
66
+
65
67
  const keyAReservedValue = priority.indexOf(keyA);
66
68
  const keyBReservedValue = priority.indexOf(keyB);
67
69
  if (keyAReservedValue >= 0 && keyBReservedValue >= 0) {
@@ -122,6 +124,30 @@ module.exports = {
122
124
  return false;
123
125
  }
124
126
 
127
+ /**
128
+ * @param {Attribute[]} attributes
129
+ * @return {Attribute[][]}
130
+ */
131
+ function groupAttributes(attributes) {
132
+ /**
133
+ * @type {Attribute[][]}
134
+ */
135
+ const attributesList = [];
136
+ let index = 0;
137
+
138
+ for (const attribute of attributes) {
139
+ if (hasTemplate(attribute.key)) {
140
+ index = attributesList.length;
141
+ continue;
142
+ }
143
+ if (!attributesList[index]) {
144
+ attributesList[index] = [];
145
+ }
146
+ attributesList[index].push(attribute);
147
+ }
148
+ return attributesList;
149
+ }
150
+
125
151
  /**
126
152
  * @param {Attribute[]} unsorted
127
153
  */
@@ -129,26 +155,28 @@ module.exports = {
129
155
  if (unsorted.length <= 1) {
130
156
  return;
131
157
  }
158
+ const grouped = groupAttributes(unsorted);
159
+ grouped.forEach((unsorted) => {
160
+ const sorted = [...unsorted].sort(compare);
132
161
 
133
- const sorted = [...unsorted].sort(compare);
134
-
135
- if (!isChanged(unsorted, sorted)) {
136
- return;
137
- }
138
- const first = unsorted[0];
139
- const last = unsorted[unsorted.length - 1];
140
- context.report({
141
- node: {
142
- range: [first.range[0], last.range[1]],
143
- loc: {
144
- start: first.loc.start,
145
- end: last.loc.end,
162
+ if (!isChanged(unsorted, sorted)) {
163
+ return;
164
+ }
165
+ const first = unsorted[0];
166
+ const last = unsorted[unsorted.length - 1];
167
+ context.report({
168
+ node: {
169
+ range: [first.range[0], last.range[1]],
170
+ loc: {
171
+ start: first.loc.start,
172
+ end: last.loc.end,
173
+ },
146
174
  },
147
- },
148
- messageId: MESSAGE_IDS.UNSORTED,
149
- fix(fixer) {
150
- return fix(fixer, unsorted, sorted);
151
- },
175
+ messageId: MESSAGE_IDS.UNSORTED,
176
+ fix(fixer) {
177
+ return fix(fixer, unsorted, sorted);
178
+ },
179
+ });
152
180
  });
153
181
  }
154
182
 
@@ -9,6 +9,7 @@
9
9
  * @typedef { import("../../types").Comment } Comment
10
10
  * @typedef { import("../../types").AnyNode } AnyNode
11
11
  * @typedef { import("../../types").AttributeValue } AttributeValue
12
+ * @typedef { import("../../types").AttributeKey } AttributeKey
12
13
  * @typedef { import("eslint").AST.Range } Range
13
14
  * @typedef { import("eslint").AST.SourceLocation } SourceLocation
14
15
  * @typedef { import("es-html-parser").AnyToken } AnyToken
@@ -67,6 +68,14 @@ function isOverlapWithTemplates(templates, range) {
67
68
  .some((template) => isRangesOverlap(template.range, range));
68
69
  }
69
70
 
71
+ /**
72
+ * @param {AttributeKey | AttributeValue} node
73
+ * @returns {boolean}
74
+ */
75
+ function hasTemplate(node) {
76
+ return node.templates.some((template) => template.isTemplate);
77
+ }
78
+
70
79
  /**
71
80
  *
72
81
  * @param {Text | CommentContent} node
@@ -141,17 +150,6 @@ function getLocBetween(before, after) {
141
150
  };
142
151
  }
143
152
 
144
- /**
145
- * @param {AttributeValue} node
146
- * @return {boolean}
147
- */
148
- function isExpressionInTemplate(node) {
149
- if (node.type === NODE_TYPES.AttributeValue) {
150
- return node.value.indexOf("${") === 0;
151
- }
152
- return false;
153
- }
154
-
155
153
  /**
156
154
  * @param {AnyNode} node
157
155
  * @returns {node is Tag}
@@ -248,7 +246,6 @@ module.exports = {
248
246
  splitToLineNodes,
249
247
  getLocBetween,
250
248
  findParent,
251
- isExpressionInTemplate,
252
249
  isTag,
253
250
  isComment,
254
251
  isText,
@@ -258,4 +255,5 @@ module.exports = {
258
255
  codeToLines,
259
256
  isRangesOverlap,
260
257
  getTemplateTokens,
258
+ hasTemplate,
261
259
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@html-eslint/eslint-plugin",
3
- "version": "0.33.1",
3
+ "version": "0.34.0",
4
4
  "description": "ESLint plugin for html",
5
5
  "author": "yeonjuan",
6
6
  "homepage": "https://github.com/yeonjuan/html-eslint#readme",
@@ -45,15 +45,17 @@
45
45
  "accessibility"
46
46
  ],
47
47
  "dependencies": {
48
- "@html-eslint/template-parser": "^0.33.0"
48
+ "@html-eslint/template-parser": "^0.34.0",
49
+ "@html-eslint/template-syntax-parser": "^0.34.0"
49
50
  },
50
51
  "devDependencies": {
51
- "@html-eslint/parser": "^0.33.0",
52
+ "@html-eslint/parser": "^0.34.0",
52
53
  "@types/eslint": "^9.6.1",
53
54
  "@types/estree": "^0.0.47",
54
55
  "es-html-parser": "^1.0.0-alpha.4",
56
+ "eslint": "^8",
55
57
  "espree": "^10.3.0",
56
58
  "typescript": "^5.7.2"
57
59
  },
58
- "gitHead": "808869fd592b2e773afef1cad2f2fc998221f0c4"
60
+ "gitHead": "f0401cc30d1510d6f5c7511280c0a6b9779fea0c"
59
61
  }
@@ -1 +1 @@
1
- {"version":3,"file":"id-naming-convention.d.ts","sourceRoot":"","sources":["../../lib/rules/id-naming-convention.js"],"names":[],"mappings":";;;wBAyCU,UAAU;;kBAxCN,OAAO,UAAU,EAAE,UAAU;WAC7B,OAAO,UAAU,EAAE,GAAG;iBACtB,OAAO,UAAU,EAAE,SAAS;gBAC5B,OAAO,UAAU,EAAE,QAAQ"}
1
+ {"version":3,"file":"id-naming-convention.d.ts","sourceRoot":"","sources":["../../lib/rules/id-naming-convention.js"],"names":[],"mappings":";;;wBAqCU,UAAU;;kBApCN,OAAO,UAAU,EAAE,UAAU;WAC7B,OAAO,UAAU,EAAE,GAAG;iBACtB,OAAO,UAAU,EAAE,SAAS;gBAC5B,OAAO,UAAU,EAAE,QAAQ"}
@@ -1 +1 @@
1
- {"version":3,"file":"lowercase.d.ts","sourceRoot":"","sources":["../../lib/rules/lowercase.js"],"names":[],"mappings":";;;wBAiBU,UAAU;;kBAhBN,OAAO,UAAU,EAAE,UAAU;WAC7B,OAAO,UAAU,EAAE,GAAG;gBACtB,OAAO,UAAU,EAAE,QAAQ;iBAC3B,OAAO,UAAU,EAAE,SAAS"}
1
+ {"version":3,"file":"lowercase.d.ts","sourceRoot":"","sources":["../../lib/rules/lowercase.js"],"names":[],"mappings":";;;wBAkBU,UAAU;;kBAjBN,OAAO,UAAU,EAAE,UAAU;WAC7B,OAAO,UAAU,EAAE,GAAG;gBACtB,OAAO,UAAU,EAAE,QAAQ;iBAC3B,OAAO,UAAU,EAAE,SAAS"}
@@ -1 +1 @@
1
- {"version":3,"file":"sort-attrs.d.ts","sourceRoot":"","sources":["../../lib/rules/sort-attrs.js"],"names":[],"mappings":";;;wBAgBU,UAAU;;iBAfN,OAAO,QAAQ,EAAE,IAAI,CAAC,SAAS;kBAC/B,OAAO,UAAU,EAAE,UAAU;iBAC7B,OAAO,UAAU,EAAE,SAAS;YAC5B,OAAO,UAAU,EAAE,IAAI"}
1
+ {"version":3,"file":"sort-attrs.d.ts","sourceRoot":"","sources":["../../lib/rules/sort-attrs.js"],"names":[],"mappings":";;;wBAiBU,UAAU;;iBAhBN,OAAO,QAAQ,EAAE,IAAI,CAAC,SAAS;kBAC/B,OAAO,UAAU,EAAE,UAAU;iBAC7B,OAAO,UAAU,EAAE,SAAS;YAC5B,OAAO,UAAU,EAAE,IAAI"}
@@ -8,6 +8,7 @@ export type CommentContent = import("../../types").CommentContent;
8
8
  export type Comment = import("../../types").Comment;
9
9
  export type AnyNode = import("../../types").AnyNode;
10
10
  export type AttributeValue = import("../../types").AttributeValue;
11
+ export type AttributeKey = import("../../types").AttributeKey;
11
12
  export type Range = import("eslint").AST.Range;
12
13
  export type SourceLocation = import("eslint").AST.SourceLocation;
13
14
  export type AnyToken = import("es-html-parser").AnyToken;
@@ -52,11 +53,6 @@ export function getLocBetween(before: {
52
53
  * @returns {null | AnyNode}
53
54
  */
54
55
  export function findParent(node: Exclude<AnyNode, Line>, predicate: (node: AnyNode) => boolean): null | AnyNode;
55
- /**
56
- * @param {AttributeValue} node
57
- * @return {boolean}
58
- */
59
- export function isExpressionInTemplate(node: AttributeValue): boolean;
60
56
  /**
61
57
  * @param {AnyNode} node
62
58
  * @returns {node is Tag}
@@ -106,4 +102,9 @@ export function isRangesOverlap(rangeA: Range, rangeB: Range): boolean;
106
102
  * @returns {((CommentContent | Text)['templates'][number])[]}
107
103
  */
108
104
  export function getTemplateTokens(tokens: AnyToken[]): ((CommentContent | Text)["templates"][number])[];
105
+ /**
106
+ * @param {AttributeKey | AttributeValue} node
107
+ * @returns {boolean}
108
+ */
109
+ export function hasTemplate(node: AttributeKey | AttributeValue): boolean;
109
110
  //# sourceMappingURL=node.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../lib/rules/utils/node.js"],"names":[],"mappings":"wBACc,OAAO,aAAa,EAAE,SAAS;kBAC/B,OAAO,aAAa,EAAE,GAAG;wBACzB,OAAO,aAAa,EAAE,SAAS;uBAC/B,OAAO,aAAa,EAAE,QAAQ;mBAC9B,OAAO,aAAa,EAAE,IAAI;mBAC1B,OAAO,aAAa,EAAE,IAAI;6BAC1B,OAAO,aAAa,EAAE,cAAc;sBACpC,OAAO,aAAa,EAAE,OAAO;sBAC7B,OAAO,aAAa,EAAE,OAAO;6BAC7B,OAAO,aAAa,EAAE,cAAc;oBACpC,OAAO,QAAQ,EAAE,GAAG,CAAC,KAAK;6BAC1B,OAAO,QAAQ,EAAE,GAAG,CAAC,cAAc;uBACnC,OAAO,gBAAgB,EAAE,QAAQ;AAM/C;;;;GAIG;AACH,+BAJW,GAAG,GAAG,SAAS,GAAG,QAAQ,OAC1B,MAAM,GACJ,SAAS,GAAG,SAAS,CAMjC;AAED;;;;GAIG;AACH,wCAHW,GAAG,GAAG,SAAS,GAAG,QAAQ,GACxB,OAAO,CAInB;AAED;;;;GAIG;AACH,6CAHW,OAAO,GACL,OAAO,CAInB;AAuBD;;;;GAIG;AACH,uCAHW,IAAI,GAAG,cAAc,GACnB,IAAI,EAAE,CAwDlB;AAED;;;;;GAKG;AACH,sCAJW;IAAC,GAAG,EAAE,cAAc,CAAA;CAAC,SACrB;IAAC,GAAG,EAAE,cAAc,CAAA;CAAC,GACnB,cAAc,CAO1B;AA+DD;;;;GAIG;AACH,iCAJW,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,aACtB,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,GACxB,IAAI,GAAG,OAAO,CAgB1B;AAhFD;;;GAGG;AACH,6CAHW,cAAc,GACb,OAAO,CAOlB;AAED;;;GAGG;AACH,4BAHW,OAAO,GACL,IAAI,IAAI,GAAG,CAIvB;AAUD;;;GAGG;AACH,gCAHW,OAAO,GACL,IAAI,IAAI,OAAO,CAI3B;AAED;;;GAGG;AACH,6BAHW,OAAO,GACL,IAAI,IAAI,IAAI,CAIxB;AAED;;;GAGG;AACH,6BAHW,OAAO,GAAG,IAAI,GACZ,IAAI,IAAI,IAAI,CAIxB;AA9BD;;;GAGG;AACH,+BAHW,OAAO,GACL,IAAI,IAAI,SAAS,CAI7B;AA9GD;;;;GAIG;AACH,kDAJW,CAAC,IAAI,GAAG,cAAc,CAAC,CAAC,WAAW,CAAC,SACpC,KAAK,GACH,OAAO,CAMnB;AAiID;;;GAGG;AACH,oCAHW,MAAM,GACJ,MAAM,EAAE,CAIpB;AA1JD;;;;;GAKG;AACH,wCAJW,KAAK,UACL,KAAK,GACH,OAAO,CAInB;AAyKD;;;;GAIG;AACH,0CAHW,QAAQ,EAAE,GACR,CAAC,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAa5D"}
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../lib/rules/utils/node.js"],"names":[],"mappings":"wBACc,OAAO,aAAa,EAAE,SAAS;kBAC/B,OAAO,aAAa,EAAE,GAAG;wBACzB,OAAO,aAAa,EAAE,SAAS;uBAC/B,OAAO,aAAa,EAAE,QAAQ;mBAC9B,OAAO,aAAa,EAAE,IAAI;mBAC1B,OAAO,aAAa,EAAE,IAAI;6BAC1B,OAAO,aAAa,EAAE,cAAc;sBACpC,OAAO,aAAa,EAAE,OAAO;sBAC7B,OAAO,aAAa,EAAE,OAAO;6BAC7B,OAAO,aAAa,EAAE,cAAc;2BACpC,OAAO,aAAa,EAAE,YAAY;oBAClC,OAAO,QAAQ,EAAE,GAAG,CAAC,KAAK;6BAC1B,OAAO,QAAQ,EAAE,GAAG,CAAC,cAAc;uBACnC,OAAO,gBAAgB,EAAE,QAAQ;AAM/C;;;;GAIG;AACH,+BAJW,GAAG,GAAG,SAAS,GAAG,QAAQ,OAC1B,MAAM,GACJ,SAAS,GAAG,SAAS,CAMjC;AAED;;;;GAIG;AACH,wCAHW,GAAG,GAAG,SAAS,GAAG,QAAQ,GACxB,OAAO,CAInB;AAED;;;;GAIG;AACH,6CAHW,OAAO,GACL,OAAO,CAInB;AA+BD;;;;GAIG;AACH,uCAHW,IAAI,GAAG,cAAc,GACnB,IAAI,EAAE,CAwDlB;AAED;;;;;GAKG;AACH,sCAJW;IAAC,GAAG,EAAE,cAAc,CAAA;CAAC,SACrB;IAAC,GAAG,EAAE,cAAc,CAAA;CAAC,GACnB,cAAc,CAO1B;AAoDD;;;;GAIG;AACH,iCAJW,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,aACtB,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,GACxB,IAAI,GAAG,OAAO,CAgB1B;AArED;;;GAGG;AACH,4BAHW,OAAO,GACL,IAAI,IAAI,GAAG,CAIvB;AAUD;;;GAGG;AACH,gCAHW,OAAO,GACL,IAAI,IAAI,OAAO,CAI3B;AAED;;;GAGG;AACH,6BAHW,OAAO,GACL,IAAI,IAAI,IAAI,CAIxB;AAED;;;GAGG;AACH,6BAHW,OAAO,GAAG,IAAI,GACZ,IAAI,IAAI,IAAI,CAIxB;AA9BD;;;GAGG;AACH,+BAHW,OAAO,GACL,IAAI,IAAI,SAAS,CAI7B;AA3GD;;;;GAIG;AACH,kDAJW,CAAC,IAAI,GAAG,cAAc,CAAC,CAAC,WAAW,CAAC,SACpC,KAAK,GACH,OAAO,CAMnB;AA8HD;;;GAGG;AACH,oCAHW,MAAM,GACJ,MAAM,EAAE,CAIpB;AAvJD;;;;;GAKG;AACH,wCAJW,KAAK,UACL,KAAK,GACH,OAAO,CAInB;AAsKD;;;;GAIG;AACH,0CAHW,QAAQ,EAAE,GACR,CAAC,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,CAa5D;AAzKD;;;GAGG;AACH,kCAHW,YAAY,GAAG,cAAc,GAC3B,OAAO,CAInB"}
File without changes