@html-eslint/eslint-plugin 0.32.0 → 0.33.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.
@@ -246,14 +246,14 @@ module.exports = {
246
246
  * @param {NewlineNode} node
247
247
  */
248
248
  function isNotNewlineEnd(node) {
249
- return node.type !== `Text` || /\n\s*$/.test(node.value) === false;
249
+ return node.type !== `Text` || /(\n|\r\n)\s*$/.test(node.value) === false;
250
250
  }
251
251
 
252
252
  /**
253
253
  * @param {NewlineNode} node
254
254
  */
255
255
  function isNotNewlineStart(node) {
256
- return node.type !== `Text` || /^\n/.test(node.value) === false;
256
+ return node.type !== `Text` || /^(\n|\r\n)/.test(node.value) === false;
257
257
  }
258
258
 
259
259
  /**
@@ -271,6 +271,7 @@ module.exports = {
271
271
  },
272
272
  OpenScriptTagStart: checkIndent,
273
273
  OpenScriptTagEnd: checkIndent,
274
+ CloseScriptTag: checkIndent,
274
275
  StyleTag(node) {
275
276
  indentLevel.indent(node);
276
277
  },
@@ -279,6 +280,7 @@ module.exports = {
279
280
  },
280
281
  OpenStyleTagStart: checkIndent,
281
282
  OpenStyleTagEnd: checkIndent,
283
+ CloseStyleTag: checkIndent,
282
284
  OpenTagStart: checkIndent,
283
285
  OpenTagEnd(node) {
284
286
  checkIndent(node);
@@ -43,6 +43,10 @@ const requireFormMethod = require("./require-form-method");
43
43
  const noHeadingInsideButton = require("./no-heading-inside-button");
44
44
  const noInvalidRole = require("./no-invalid-role");
45
45
  const noNestedInteractive = require("./no-nested-interactive");
46
+ const maxElementDepth = require("./max-element-depth");
47
+ const requireExplicitSize = require("./require-explicit-size");
48
+ // import new rule here ↑
49
+ // DO NOT REMOVE THIS COMMENT
46
50
 
47
51
  module.exports = {
48
52
  "require-lang": requireLang,
@@ -90,4 +94,8 @@ module.exports = {
90
94
  "sort-attrs": sortAttrs,
91
95
  "prefer-https": preferHttps,
92
96
  "require-input-label": requireInputLabel,
97
+ "max-element-depth": maxElementDepth,
98
+ "require-explicit-size": requireExplicitSize,
99
+ // export new rule here ↑
100
+ // DO NOT REMOVE THIS COMMENT
93
101
  };
@@ -0,0 +1,96 @@
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
6
+ */
7
+
8
+ const { RULE_CATEGORY } = require("../constants");
9
+ const { createVisitors } = require("./utils/visitors");
10
+
11
+ const MESSAGE_IDS = {
12
+ MAX_DEPTH_EXCEEDED: "maxDepthExceeded",
13
+ };
14
+
15
+ /**
16
+ * @type {RuleModule}
17
+ */
18
+ module.exports = {
19
+ meta: {
20
+ type: "code",
21
+
22
+ docs: {
23
+ description: "Enforce element maximum depth",
24
+ category: RULE_CATEGORY.STYLE,
25
+ recommended: false,
26
+ },
27
+
28
+ fixable: null,
29
+ schema: [
30
+ {
31
+ type: "object",
32
+ properties: {
33
+ max: {
34
+ type: "integer",
35
+ minimum: 1,
36
+ default: 32,
37
+ },
38
+ },
39
+ required: ["max"],
40
+ additionalProperties: false,
41
+ },
42
+ ],
43
+ messages: {
44
+ [MESSAGE_IDS.MAX_DEPTH_EXCEEDED]:
45
+ "Expected the depth of nested elements to be <= {{needed}}, but found {{found}}",
46
+ },
47
+ },
48
+
49
+ create(context) {
50
+ const maxDepth =
51
+ context.options &&
52
+ context.options[0] &&
53
+ typeof context.options[0].max === "number"
54
+ ? context.options[0].max
55
+ : 32;
56
+
57
+ let depth = 0;
58
+
59
+ function resetDepth() {
60
+ depth = 0;
61
+ }
62
+
63
+ /**
64
+ *
65
+ * @param {Tag | ScriptTag | StyleTag} node
66
+ */
67
+ function increaseDepth(node) {
68
+ depth++;
69
+ if (depth > maxDepth) {
70
+ context.report({
71
+ node,
72
+ messageId: MESSAGE_IDS.MAX_DEPTH_EXCEEDED,
73
+ data: {
74
+ needed: maxDepth,
75
+ found: String(depth),
76
+ },
77
+ });
78
+ }
79
+ }
80
+
81
+ function decreaseDepth() {
82
+ depth--;
83
+ }
84
+
85
+ return createVisitors(context, {
86
+ Document: resetDepth,
87
+ "Document:exit": resetDepth,
88
+ Tag: increaseDepth,
89
+ "Tag:exit": decreaseDepth,
90
+ ScriptTag: increaseDepth,
91
+ "ScriptTag:exit": decreaseDepth,
92
+ StyleTag: increaseDepth,
93
+ "StyleTag:exit": decreaseDepth,
94
+ });
95
+ },
96
+ };
@@ -0,0 +1,122 @@
1
+ /**
2
+ * @typedef { import("../types").RuleModule } RuleModule
3
+ * @typedef { import("../types").Tag } Tag
4
+ * @typedef { import("../types").AnyNode } AnyNode
5
+ */
6
+
7
+ const { RULE_CATEGORY } = require("../constants");
8
+ const { findAttr } = require("./utils/node");
9
+ const { createVisitors } = require("./utils/visitors");
10
+
11
+ const MESSAGE_IDS = {
12
+ MISSING_HEIGHT: "missingHeight",
13
+ MISSING_WIDTH: "missingWidth",
14
+ };
15
+
16
+ const TARGET_ELEMENTS = ["img", "iframe"];
17
+
18
+ /**
19
+ * @type {RuleModule}
20
+ */
21
+ module.exports = {
22
+ meta: {
23
+ type: "code",
24
+
25
+ docs: {
26
+ description:
27
+ "Enforces that some elements (img, iframe) have explicitly defined width and height attributes.",
28
+ category: RULE_CATEGORY.BEST_PRACTICE,
29
+ recommended: false,
30
+ },
31
+
32
+ fixable: null,
33
+ schema: [
34
+ {
35
+ type: "object",
36
+ properties: {
37
+ allowClass: {
38
+ type: "array",
39
+ items: {
40
+ type: "string",
41
+ },
42
+ },
43
+ allowId: {
44
+ type: "array",
45
+ items: {
46
+ type: "string",
47
+ },
48
+ },
49
+ },
50
+ additionalProperties: false,
51
+ },
52
+ ],
53
+ messages: {
54
+ [MESSAGE_IDS.MISSING_HEIGHT]: "Missing `width` attribute for <{{name}}>",
55
+ [MESSAGE_IDS.MISSING_WIDTH]: "Missing `height` attribute for <{{name}}>",
56
+ },
57
+ },
58
+
59
+ create(context) {
60
+ const allowClass =
61
+ (context.options &&
62
+ context.options[0] &&
63
+ context.options[0].allowClass) ||
64
+ [];
65
+ const allowId =
66
+ (context.options && context.options[0] && context.options[0].allowId) ||
67
+ [];
68
+
69
+ const allowClassSet = new Set(allowClass);
70
+ const allowIdSet = new Set(allowId);
71
+ return createVisitors(context, {
72
+ Tag(node) {
73
+ if (
74
+ !TARGET_ELEMENTS.some(
75
+ (element) => element === node.name.toLowerCase()
76
+ )
77
+ ) {
78
+ return;
79
+ }
80
+ const classAttr = findAttr(node, "class");
81
+ if (
82
+ classAttr &&
83
+ classAttr.value &&
84
+ classAttr.value.value.split(" ").some((cls) => allowClassSet.has(cls))
85
+ ) {
86
+ return;
87
+ }
88
+ const idAttr = findAttr(node, "id");
89
+ if (
90
+ idAttr &&
91
+ idAttr.value &&
92
+ idAttr.value.value.split(" ").some((id) => allowIdSet.has(id))
93
+ ) {
94
+ return;
95
+ }
96
+
97
+ const width = findAttr(node, "width");
98
+ const height = findAttr(node, "height");
99
+
100
+ if (!height || !height.value) {
101
+ context.report({
102
+ node: node.openStart,
103
+ messageId: MESSAGE_IDS.MISSING_HEIGHT,
104
+ data: {
105
+ name: node.name,
106
+ },
107
+ });
108
+ }
109
+
110
+ if (!width || !width.value) {
111
+ context.report({
112
+ node: node.openStart,
113
+ messageId: MESSAGE_IDS.MISSING_WIDTH,
114
+ data: {
115
+ name: node.name,
116
+ },
117
+ });
118
+ }
119
+ },
120
+ });
121
+ },
122
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@html-eslint/eslint-plugin",
3
- "version": "0.32.0",
3
+ "version": "0.33.1",
4
4
  "description": "ESLint plugin for html",
5
5
  "author": "yeonjuan",
6
6
  "homepage": "https://github.com/yeonjuan/html-eslint#readme",
@@ -45,15 +45,15 @@
45
45
  "accessibility"
46
46
  ],
47
47
  "dependencies": {
48
- "@html-eslint/template-parser": "^0.32.0"
48
+ "@html-eslint/template-parser": "^0.33.0"
49
49
  },
50
50
  "devDependencies": {
51
- "@html-eslint/parser": "^0.32.0",
51
+ "@html-eslint/parser": "^0.33.0",
52
52
  "@types/eslint": "^9.6.1",
53
53
  "@types/estree": "^0.0.47",
54
54
  "es-html-parser": "^1.0.0-alpha.4",
55
55
  "espree": "^10.3.0",
56
56
  "typescript": "^5.7.2"
57
57
  },
58
- "gitHead": "ae0860389c4463a3e259d0ccae38992db46a20e5"
58
+ "gitHead": "808869fd592b2e773afef1cad2f2fc998221f0c4"
59
59
  }
@@ -44,6 +44,8 @@ declare const _exports: {
44
44
  "sort-attrs": import("../types").RuleModule;
45
45
  "prefer-https": import("../types").RuleModule;
46
46
  "require-input-label": import("../types").RuleModule;
47
+ "max-element-depth": import("../types").RuleModule;
48
+ "require-explicit-size": import("../types").RuleModule;
47
49
  };
48
50
  export = _exports;
49
51
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,10 @@
1
+ declare namespace _exports {
2
+ export { RuleModule, Tag, StyleTag, ScriptTag };
3
+ }
4
+ declare const _exports: RuleModule;
5
+ export = _exports;
6
+ type RuleModule = import("../types").RuleModule;
7
+ type Tag = import("../types").Tag;
8
+ type StyleTag = import("../types").StyleTag;
9
+ type ScriptTag = import("../types").ScriptTag;
10
+ //# sourceMappingURL=max-element-depth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"max-element-depth.d.ts","sourceRoot":"","sources":["../../lib/rules/max-element-depth.js"],"names":[],"mappings":";;;wBAeU,UAAU;;kBAdN,OAAO,UAAU,EAAE,UAAU;WAC7B,OAAO,UAAU,EAAE,GAAG;gBACtB,OAAO,UAAU,EAAE,QAAQ;iBAC3B,OAAO,UAAU,EAAE,SAAS"}
@@ -0,0 +1,9 @@
1
+ declare namespace _exports {
2
+ export { RuleModule, Tag, AnyNode };
3
+ }
4
+ declare const _exports: RuleModule;
5
+ export = _exports;
6
+ type RuleModule = import("../types").RuleModule;
7
+ type Tag = import("../types").Tag;
8
+ type AnyNode = import("../types").AnyNode;
9
+ //# sourceMappingURL=require-explicit-size.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-explicit-size.d.ts","sourceRoot":"","sources":["../../lib/rules/require-explicit-size.js"],"names":[],"mappings":";;;wBAkBU,UAAU;;kBAjBN,OAAO,UAAU,EAAE,UAAU;WAC7B,OAAO,UAAU,EAAE,GAAG;eACtB,OAAO,UAAU,EAAE,OAAO"}