@html-eslint/eslint-plugin 0.9.0-alpha.0 → 0.11.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.
Files changed (38) hide show
  1. package/lib/configs/recommended.js +1 -0
  2. package/lib/constants/node-types.js +0 -5
  3. package/lib/constants/obsolete-tags.js +1 -1
  4. package/lib/constants/rule-category.js +0 -5
  5. package/lib/constants/void-elements.js +1 -1
  6. package/lib/rules/element-newline.js +29 -9
  7. package/lib/rules/id-naming-convention.js +16 -9
  8. package/lib/rules/indent.js +19 -7
  9. package/lib/rules/index.js +14 -2
  10. package/lib/rules/no-abstract-roles.js +62 -0
  11. package/lib/rules/no-accesskey-attrs.js +45 -0
  12. package/lib/rules/no-aria-hidden-body.js +46 -0
  13. package/lib/rules/no-duplicate-attrs.js +54 -0
  14. package/lib/rules/no-duplicate-id.js +7 -0
  15. package/lib/rules/no-extra-spacing-attrs.js +27 -5
  16. package/lib/rules/no-inline-styles.js +7 -0
  17. package/lib/rules/no-multiple-empty-lines.js +90 -0
  18. package/lib/rules/no-multiple-h1.js +7 -0
  19. package/lib/rules/no-non-scalable-viewport.js +7 -0
  20. package/lib/rules/no-obsolete-tags.js +7 -0
  21. package/lib/rules/no-positive-tabindex.js +7 -0
  22. package/lib/rules/no-skip-heading-levels.js +7 -0
  23. package/lib/rules/no-target-blank.js +7 -0
  24. package/lib/rules/quotes.js +30 -2
  25. package/lib/rules/require-button-type.js +58 -0
  26. package/lib/rules/require-closing-tags.js +11 -4
  27. package/lib/rules/require-doctype.js +7 -0
  28. package/lib/rules/require-frame-title.js +7 -0
  29. package/lib/rules/require-img-alt.js +13 -0
  30. package/lib/rules/require-lang.js +10 -4
  31. package/lib/rules/require-li-container.js +13 -1
  32. package/lib/rules/require-meta-charset.js +12 -1
  33. package/lib/rules/require-meta-description.js +15 -4
  34. package/lib/rules/require-meta-viewport.js +12 -8
  35. package/lib/rules/require-title.js +9 -3
  36. package/lib/rules/utils/node-utils.js +27 -5
  37. package/lib/types.d.ts +104 -33
  38. package/package.json +8 -4
@@ -0,0 +1,90 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
5
+ const { RULE_CATEGORY } = require("../constants");
6
+
7
+ const MESSAGE_IDS = {
8
+ UNEXPECTED: "unexpected",
9
+ };
10
+
11
+ /**
12
+ * @type {Rule}
13
+ */
14
+ module.exports = {
15
+ meta: {
16
+ type: "code",
17
+
18
+ docs: {
19
+ description: "Disallow multiple empty lines",
20
+ category: RULE_CATEGORY.STYLE,
21
+ recommended: false,
22
+ },
23
+
24
+ fixable: "whitespace",
25
+ schema: [
26
+ {
27
+ type: "object",
28
+ properties: {
29
+ max: {
30
+ type: "integer",
31
+ minimum: 0,
32
+ },
33
+ },
34
+ required: ["max"],
35
+ additionalProperties: false,
36
+ },
37
+ ],
38
+ messages: {
39
+ [MESSAGE_IDS.UNEXPECTED]: "More than {{max}} blank lines not allowed.",
40
+ },
41
+ },
42
+
43
+ create(context) {
44
+ const sourceCode = context.getSourceCode();
45
+ const lines = sourceCode.lines;
46
+ const max = context.options.length ? context.options[0].max : 2;
47
+ return {
48
+ "Program:exit"(node) {
49
+ /** @type {number[]} */
50
+ const nonEmptyLineNumbers = [];
51
+
52
+ lines.forEach((line, index) => {
53
+ if (line.trim().length > 0) {
54
+ nonEmptyLineNumbers.push(index + 1);
55
+ }
56
+ });
57
+
58
+ nonEmptyLineNumbers.forEach((current, index, arr) => {
59
+ const before = arr[index - 1];
60
+ if (typeof before === "number") {
61
+ if (current - before - 1 > max) {
62
+ context.report({
63
+ node,
64
+ loc: {
65
+ start: { line: before, column: 0 },
66
+ end: { line: current, column: 0 },
67
+ },
68
+ messageId: MESSAGE_IDS.UNEXPECTED,
69
+ data: {
70
+ max,
71
+ },
72
+ fix(fixer) {
73
+ const start = sourceCode.getIndexFromLoc({
74
+ line: before + 1,
75
+ column: 0,
76
+ });
77
+ const end = sourceCode.getIndexFromLoc({
78
+ line: current - max,
79
+ column: 0,
80
+ });
81
+ return fixer.removeRange([start, end]);
82
+ },
83
+ });
84
+ }
85
+ }
86
+ });
87
+ },
88
+ };
89
+ },
90
+ };
@@ -1,9 +1,16 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY } = require("../constants");
2
6
 
3
7
  const MESSAGE_IDS = {
4
8
  MULTIPLE_H1: "unexpectedMultiH1",
5
9
  };
6
10
 
11
+ /**
12
+ * @type {Rule}
13
+ */
7
14
  module.exports = {
8
15
  meta: {
9
16
  type: "code",
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY } = require("../constants");
2
6
  const { NodeUtils } = require("./utils");
3
7
 
@@ -5,6 +9,9 @@ const MESSAGE_IDS = {
5
9
  UNEXPECTED: "unexpected",
6
10
  };
7
11
 
12
+ /**
13
+ * @type {Rule}
14
+ */
8
15
  module.exports = {
9
16
  meta: {
10
17
  type: "code",
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY, NODE_TYPES } = require("../constants");
2
6
  const { OBSOLETE_TAGS } = require("../constants");
3
7
 
@@ -7,6 +11,9 @@ const MESSAGE_IDS = {
7
11
  UNEXPECTED: "unexpected",
8
12
  };
9
13
 
14
+ /**
15
+ * @type {Rule}
16
+ */
10
17
  module.exports = {
11
18
  meta: {
12
19
  type: "code",
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY } = require("../constants");
2
6
  const { NodeUtils } = require("./utils");
3
7
 
@@ -5,6 +9,9 @@ const MESSAGE_IDS = {
5
9
  UNEXPECTED: "unexpected",
6
10
  };
7
11
 
12
+ /**
13
+ * @type {Rule}
14
+ */
8
15
  module.exports = {
9
16
  meta: {
10
17
  type: "code",
@@ -1,9 +1,16 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY } = require("../constants");
2
6
 
3
7
  const MESSAGE_IDS = {
4
8
  UNEXPECTED: "unexpected",
5
9
  };
6
10
 
11
+ /**
12
+ * @type {Rule}
13
+ */
7
14
  module.exports = {
8
15
  meta: {
9
16
  type: "code",
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY } = require("../constants");
2
6
  const { NodeUtils } = require("./utils");
3
7
 
@@ -5,6 +9,9 @@ const MESSAGE_IDS = {
5
9
  MISSING: "missing",
6
10
  };
7
11
 
12
+ /**
13
+ * @type {Rule}
14
+ */
8
15
  module.exports = {
9
16
  meta: {
10
17
  type: "code",
@@ -1,3 +1,9 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ * @typedef {import("../types").Range} Range
4
+ * @typedef {import("../types").AttrNode} AttrNode
5
+ */
6
+
1
7
  const { RULE_CATEGORY } = require("../constants");
2
8
 
3
9
  const MESSAGE_IDS = {
@@ -12,6 +18,9 @@ const QUOTES_STYLES = {
12
18
 
13
19
  const QUOTES_CODES = [`"`, `'`];
14
20
 
21
+ /**
22
+ * @type {Rule}
23
+ */
15
24
  module.exports = {
16
25
  meta: {
17
26
  type: "code",
@@ -45,28 +54,47 @@ module.exports = {
45
54
 
46
55
  const sourceCode = context.getSourceCode();
47
56
 
57
+ /**
58
+ * @param {Range} range
59
+ * @returns {string}
60
+ */
48
61
  function getCodeIn(range) {
49
62
  return sourceCode.text.slice(range[0], range[1]);
50
63
  }
51
64
 
65
+ /**
66
+ * @param {AttrNode} attr
67
+ * @returns {Range}
68
+ */
52
69
  function getValueRange(attr) {
53
70
  const attrCode = getCodeIn(attr.range);
54
71
  const [matched = ""] = attrCode.match(/\S*?\s*=\s*/) || [];
55
72
  return [attr.range[0] + matched.length, attr.range[1]];
56
73
  }
57
74
 
75
+ /**
76
+ * @param {AttrNode} attr
77
+ * @returns {[string, string]}
78
+ */
58
79
  function getQuotes(attr) {
59
80
  const [valueStart, valueEnd] = getValueRange(attr);
60
- const openinig = getCodeIn([valueStart, valueStart + 1]);
81
+ const opening = getCodeIn([valueStart, valueStart + 1]);
61
82
  const closing = getCodeIn([valueEnd - 1, valueEnd]);
62
- return [openinig, closing];
83
+ return [opening, closing];
63
84
  }
64
85
 
86
+ /**
87
+ * @param {AttrNode} attr
88
+ * @returns {boolean}
89
+ */
65
90
  function hasEqualSign(attr) {
66
91
  const keyEnd = attr.range[0] + attr.name.length;
67
92
  return getCodeIn([keyEnd, attr.range[1]]).trimStart().startsWith("=");
68
93
  }
69
94
 
95
+ /**
96
+ * @param {AttrNode} attr
97
+ */
70
98
  function checkQuotes(attr) {
71
99
  const [opening, closing] = getQuotes(attr);
72
100
  if (QUOTES_CODES.includes(opening)) {
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
5
+ const { RULE_CATEGORY } = require("../constants");
6
+ const { NodeUtils } = require("./utils");
7
+
8
+ const MESSAGE_IDS = {
9
+ MISSING: "missing",
10
+ INVALID: "invalid",
11
+ };
12
+
13
+ const VALID_BUTTON_TYPES_SET = new Set(["submit", "button", "reset"]);
14
+
15
+ /**
16
+ * @type {Rule}
17
+ */
18
+ module.exports = {
19
+ meta: {
20
+ type: "code",
21
+
22
+ docs: {
23
+ description: "Require use of button element with a valid type attribute.",
24
+ category: RULE_CATEGORY.BEST_PRACTICE,
25
+ recommended: false,
26
+ },
27
+
28
+ fixable: null,
29
+ schema: [],
30
+ messages: {
31
+ [MESSAGE_IDS.MISSING]: "Missing a type attribute for button",
32
+ [MESSAGE_IDS.INVALID]:
33
+ '"{{type}}" is an invalid value for button type attribute.',
34
+ },
35
+ },
36
+
37
+ create(context) {
38
+ return {
39
+ Button(node) {
40
+ const typeAttr = NodeUtils.findAttr(node, "type");
41
+ if (!typeAttr) {
42
+ context.report({
43
+ node: node.startTag || node,
44
+ messageId: MESSAGE_IDS.MISSING,
45
+ });
46
+ } else if (!VALID_BUTTON_TYPES_SET.has(typeAttr.value)) {
47
+ context.report({
48
+ node: typeAttr,
49
+ messageId: MESSAGE_IDS.INVALID,
50
+ data: {
51
+ type: typeAttr.value,
52
+ },
53
+ });
54
+ }
55
+ },
56
+ };
57
+ },
58
+ };
@@ -1,19 +1,26 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY, VOID_ELEMENTS } = require("../constants");
2
6
 
3
7
  const VOID_ELEMENTS_SET = new Set(VOID_ELEMENTS);
4
8
 
5
9
  const MESSAGE_IDS = {
6
- MISSNG: "missing",
10
+ MISSING: "missing",
7
11
  MISSING_SELF: "missingSelf",
8
12
  UNEXPECTED: "unexpected",
9
13
  };
10
14
 
15
+ /**
16
+ * @type {Rule}
17
+ */
11
18
  module.exports = {
12
19
  meta: {
13
20
  type: "code",
14
21
 
15
22
  docs: {
16
- description: "Reqiure closing tags.",
23
+ description: "Require closing tags.",
17
24
  category: RULE_CATEGORY.BEST_PRACTICE,
18
25
  recommended: true,
19
26
  },
@@ -31,7 +38,7 @@ module.exports = {
31
38
  },
32
39
  ],
33
40
  messages: {
34
- [MESSAGE_IDS.MISSNG]: "Missing closing tag for {{tag}}.",
41
+ [MESSAGE_IDS.MISSING]: "Missing closing tag for {{tag}}.",
35
42
  [MESSAGE_IDS.MISSING_SELF]: "Missing self closing tag for {{tag}}",
36
43
  [MESSAGE_IDS.UNEXPECTED]: "Unexpected self closing tag for {{tag}}.",
37
44
  },
@@ -55,7 +62,7 @@ module.exports = {
55
62
  data: {
56
63
  tag: node.tagName,
57
64
  },
58
- messageId: MESSAGE_IDS.MISSNG,
65
+ messageId: MESSAGE_IDS.MISSING,
59
66
  });
60
67
  }
61
68
  }
@@ -1,9 +1,16 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY } = require("../constants");
2
6
 
3
7
  const MESSAGE_IDS = {
4
8
  MISSING: "missing",
5
9
  };
6
10
 
11
+ /**
12
+ * @type {Rule}
13
+ */
7
14
  module.exports = {
8
15
  meta: {
9
16
  type: "code",
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY } = require("../constants");
2
6
  const { NodeUtils } = require("./utils");
3
7
 
@@ -6,6 +10,9 @@ const MESSAGE_IDS = {
6
10
  UNEXPECTED: "unexpected",
7
11
  };
8
12
 
13
+ /**
14
+ * @type {Rule}
15
+ */
9
16
  module.exports = {
10
17
  meta: {
11
18
  type: "code",
@@ -1,9 +1,17 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ * @typedef {import("../types").ElementNode} ElementNode
4
+ */
5
+
1
6
  const { RULE_CATEGORY } = require("../constants");
2
7
 
3
8
  const MESSAGE_IDS = {
4
9
  MISSING_ALT: "missingAlt",
5
10
  };
6
11
 
12
+ /**
13
+ * @type {Rule}
14
+ */
7
15
  module.exports = {
8
16
  meta: {
9
17
  type: "code",
@@ -35,6 +43,11 @@ module.exports = {
35
43
  },
36
44
  };
37
45
 
46
+ /**
47
+ * Checks whether a node has `alt` attribute value or not.
48
+ * @param {ElementNode} node a node to check.
49
+ * @returns {boolean} `true` if a node has `alt` attribute value.
50
+ */
38
51
  function hasAltAttrAndValue(node) {
39
52
  return (node.attrs || []).some((attr) => {
40
53
  return attr.name === "alt" && attr.value.trim().length > 0;
@@ -1,12 +1,18 @@
1
- // @ts-check
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
2
5
  const { RULE_CATEGORY } = require("../constants");
3
6
  const { NodeUtils } = require("./utils");
4
7
 
5
8
  const MESSAGE_IDS = {
6
- MISSNG: "missing",
9
+ MISSING: "missing",
7
10
  EMPTY: "empty",
8
11
  };
9
12
 
13
+ /**
14
+ * @type {Rule}
15
+ */
10
16
  module.exports = {
11
17
  meta: {
12
18
  type: "code",
@@ -20,7 +26,7 @@ module.exports = {
20
26
  fixable: null,
21
27
  schema: [],
22
28
  messages: {
23
- [MESSAGE_IDS.MISSNG]: "Missing `lang` attribute in `<html>` tag.",
29
+ [MESSAGE_IDS.MISSING]: "Missing `lang` attribute in `<html>` tag.",
24
30
  [MESSAGE_IDS.EMPTY]: "Unexpected empty `lang` in in `<html>` tag.",
25
31
  },
26
32
  },
@@ -32,7 +38,7 @@ module.exports = {
32
38
  if (!langAttr) {
33
39
  context.report({
34
40
  node: node.startTag,
35
- messageId: MESSAGE_IDS.MISSNG,
41
+ messageId: MESSAGE_IDS.MISSING,
36
42
  });
37
43
  } else if (langAttr.value.trim().length === 0) {
38
44
  context.report({
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY, NODE_TYPES } = require("../constants");
2
6
 
3
7
  const MESSAGE_IDS = {
@@ -6,6 +10,9 @@ const MESSAGE_IDS = {
6
10
 
7
11
  const VALID_CONTAINERS = [NODE_TYPES.UL, NODE_TYPES.OL, NODE_TYPES.MENU];
8
12
 
13
+ /**
14
+ * @type {Rule}
15
+ */
9
16
  module.exports = {
10
17
  meta: {
11
18
  type: "code",
@@ -27,7 +34,12 @@ module.exports = {
27
34
  create(context) {
28
35
  return {
29
36
  Li(node) {
30
- if (!node.parent || !VALID_CONTAINERS.includes(node.parent.type)) {
37
+ if (!node.parent) {
38
+ context.report({
39
+ node,
40
+ messageId: MESSAGE_IDS.INVALID,
41
+ });
42
+ } else if (!VALID_CONTAINERS.includes(node.parent.type || "")) {
31
43
  context.report({
32
44
  node,
33
45
  messageId: MESSAGE_IDS.INVALID,
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @typedef {import("../types").ElementNode} ElementNode
3
+ * @typedef {import("../types").Context} Context
4
+ */
5
+
1
6
  const { RULE_CATEGORY, NODE_TYPES } = require("../constants");
2
7
  const { NodeUtils } = require("./utils");
3
8
 
@@ -24,8 +29,14 @@ module.exports = {
24
29
  },
25
30
  },
26
31
 
32
+ /**
33
+ * @param {Context} context
34
+ */
27
35
  create(context) {
28
36
  return {
37
+ /**
38
+ * @param {ElementNode} node
39
+ */
29
40
  Head(node) {
30
41
  const metaCharset = (node.childNodes || []).find((child) => {
31
42
  return (
@@ -41,7 +52,7 @@ module.exports = {
41
52
  return;
42
53
  }
43
54
  const charsetAttr = NodeUtils.findAttr(metaCharset, "charset");
44
- if (!charsetAttr || !charsetAttr.value.length) {
55
+ if (charsetAttr && !charsetAttr.value.length) {
45
56
  context.report({
46
57
  node: charsetAttr,
47
58
  messageId: MESSAGE_IDS.EMPTY,
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @typedef {import("../types").ElementNode} ElementNode
3
+ * @typedef {import("../types").Context} Context
4
+ */
5
+
1
6
  const { RULE_CATEGORY, NODE_TYPES } = require("../constants");
2
7
  const { NodeUtils } = require("./utils");
3
8
 
@@ -21,28 +26,34 @@ module.exports = {
21
26
  messages: {
22
27
  [MESSAGE_IDS.MISSING]: 'Missing `<meta name="description">`.',
23
28
  [MESSAGE_IDS.EMPTY]:
24
- 'Unexpected emtpy `conetnt` in `<meta name="description">`',
29
+ 'Unexpected empty `content` in `<meta name="description">`',
25
30
  },
26
31
  },
27
32
 
33
+ /**
34
+ * @param {Context} context
35
+ */
28
36
  create(context) {
29
37
  return {
38
+ /**
39
+ * @param {ElementNode} node
40
+ */
30
41
  Head(node) {
31
42
  const metaTags = (node.childNodes || []).filter(
32
43
  (child) => child.type === NODE_TYPES.META
33
44
  );
34
- const descripMetaTags = metaTags.filter((meta) => {
45
+ const descriptionMetaTags = metaTags.filter((meta) => {
35
46
  const nameAttr = NodeUtils.findAttr(meta, "name");
36
47
  return !!nameAttr && nameAttr.value.toLowerCase() === "description";
37
48
  });
38
49
 
39
- if (descripMetaTags.length === 0) {
50
+ if (descriptionMetaTags.length === 0) {
40
51
  context.report({
41
52
  node,
42
53
  messageId: MESSAGE_IDS.MISSING,
43
54
  });
44
55
  } else {
45
- descripMetaTags.forEach((meta) => {
56
+ descriptionMetaTags.forEach((meta) => {
46
57
  const content = NodeUtils.findAttr(meta, "content");
47
58
  if (!content || !content.value.trim().length) {
48
59
  context.report({
@@ -1,7 +1,6 @@
1
- // @ts-check
2
1
  /**
3
- * @typedef {import("../types").HTMLNode} HTMLNode
4
- * @typedef {import("../types").AttrNode} AttrNode
2
+ * @typedef {import("../types").ElementNode} ElementNode
3
+ * @typedef {import("../types").Rule} Rule
5
4
  */
6
5
 
7
6
  const { RULE_CATEGORY, NODE_TYPES } = require("../constants");
@@ -12,6 +11,9 @@ const MESSAGE_IDS = {
12
11
  EMPTY: "empty",
13
12
  };
14
13
 
14
+ /**
15
+ * @type {Rule}
16
+ */
15
17
  module.exports = {
16
18
  meta: {
17
19
  type: "code",
@@ -33,7 +35,7 @@ module.exports = {
33
35
 
34
36
  create(context) {
35
37
  /**
36
- * @param {HTMLNode} node
38
+ * @param {ElementNode} node
37
39
  * @returns {boolean}
38
40
  */
39
41
  function isMetaViewport(node) {
@@ -44,9 +46,6 @@ module.exports = {
44
46
  return false;
45
47
  }
46
48
  return {
47
- /**
48
- * @param {HTMLNode} node
49
- */
50
49
  Head(node) {
51
50
  const metaViewport = (node.childNodes || []).find(isMetaViewport);
52
51
  if (!metaViewport) {
@@ -57,7 +56,12 @@ module.exports = {
57
56
  return;
58
57
  }
59
58
  const contentAttr = NodeUtils.findAttr(metaViewport, "content");
60
- if (!contentAttr || !contentAttr.value.length) {
59
+ if (!contentAttr) {
60
+ context.report({
61
+ node: metaViewport,
62
+ messageId: MESSAGE_IDS.EMPTY,
63
+ });
64
+ } else if (!contentAttr.value.length) {
61
65
  context.report({
62
66
  node: contentAttr,
63
67
  messageId: MESSAGE_IDS.EMPTY,
@@ -1,10 +1,18 @@
1
+ /**
2
+ * @typedef {import("../types").Rule} Rule
3
+ */
4
+
1
5
  const { RULE_CATEGORY, NODE_TYPES } = require("../constants");
6
+ const { NodeUtils } = require("./utils");
2
7
 
3
8
  const MESSAGE_IDS = {
4
9
  MISSING_TITLE: "missing",
5
10
  EMPTY_TITLE: "empty",
6
11
  };
7
12
 
13
+ /**
14
+ * @type {Rule}
15
+ */
8
16
  module.exports = {
9
17
  meta: {
10
18
  type: "code",
@@ -23,7 +31,6 @@ module.exports = {
23
31
  [MESSAGE_IDS.EMPTY_TITLE]: "Unexpected empty text in `<title><title/>`",
24
32
  },
25
33
  },
26
-
27
34
  create(context) {
28
35
  return {
29
36
  Head(node) {
@@ -38,8 +45,7 @@ module.exports = {
38
45
  });
39
46
  } else if (
40
47
  !(titleTag.childNodes || []).some(
41
- (node) =>
42
- node.type === NODE_TYPES.TEXT && node.value.trim().length > 0
48
+ (node) => NodeUtils.isTextNode(node) && node.value.trim().length > 0
43
49
  )
44
50
  ) {
45
51
  context.report({