1 | /**
|
---|
2 | * @fileoverview A rule to control the style of variable initializations.
|
---|
3 | * @author Colin Ihrig
|
---|
4 | */
|
---|
5 |
|
---|
6 | "use strict";
|
---|
7 |
|
---|
8 | //------------------------------------------------------------------------------
|
---|
9 | // Helpers
|
---|
10 | //------------------------------------------------------------------------------
|
---|
11 |
|
---|
12 | /**
|
---|
13 | * Checks whether or not a given node is a for loop.
|
---|
14 | * @param {ASTNode} block A node to check.
|
---|
15 | * @returns {boolean} `true` when the node is a for loop.
|
---|
16 | */
|
---|
17 | function isForLoop(block) {
|
---|
18 | return block.type === "ForInStatement" ||
|
---|
19 | block.type === "ForOfStatement" ||
|
---|
20 | block.type === "ForStatement";
|
---|
21 | }
|
---|
22 |
|
---|
23 | /**
|
---|
24 | * Checks whether or not a given declarator node has its initializer.
|
---|
25 | * @param {ASTNode} node A declarator node to check.
|
---|
26 | * @returns {boolean} `true` when the node has its initializer.
|
---|
27 | */
|
---|
28 | function isInitialized(node) {
|
---|
29 | const declaration = node.parent;
|
---|
30 | const block = declaration.parent;
|
---|
31 |
|
---|
32 | if (isForLoop(block)) {
|
---|
33 | if (block.type === "ForStatement") {
|
---|
34 | return block.init === declaration;
|
---|
35 | }
|
---|
36 | return block.left === declaration;
|
---|
37 | }
|
---|
38 | return Boolean(node.init);
|
---|
39 | }
|
---|
40 |
|
---|
41 | //------------------------------------------------------------------------------
|
---|
42 | // Rule Definition
|
---|
43 | //------------------------------------------------------------------------------
|
---|
44 |
|
---|
45 | /** @type {import('../shared/types').Rule} */
|
---|
46 | module.exports = {
|
---|
47 | meta: {
|
---|
48 | type: "suggestion",
|
---|
49 |
|
---|
50 | docs: {
|
---|
51 | description: "Require or disallow initialization in variable declarations",
|
---|
52 | recommended: false,
|
---|
53 | url: "https://eslint.org/docs/latest/rules/init-declarations"
|
---|
54 | },
|
---|
55 |
|
---|
56 | schema: {
|
---|
57 | anyOf: [
|
---|
58 | {
|
---|
59 | type: "array",
|
---|
60 | items: [
|
---|
61 | {
|
---|
62 | enum: ["always"]
|
---|
63 | }
|
---|
64 | ],
|
---|
65 | minItems: 0,
|
---|
66 | maxItems: 1
|
---|
67 | },
|
---|
68 | {
|
---|
69 | type: "array",
|
---|
70 | items: [
|
---|
71 | {
|
---|
72 | enum: ["never"]
|
---|
73 | },
|
---|
74 | {
|
---|
75 | type: "object",
|
---|
76 | properties: {
|
---|
77 | ignoreForLoopInit: {
|
---|
78 | type: "boolean"
|
---|
79 | }
|
---|
80 | },
|
---|
81 | additionalProperties: false
|
---|
82 | }
|
---|
83 | ],
|
---|
84 | minItems: 0,
|
---|
85 | maxItems: 2
|
---|
86 | }
|
---|
87 | ]
|
---|
88 | },
|
---|
89 | messages: {
|
---|
90 | initialized: "Variable '{{idName}}' should be initialized on declaration.",
|
---|
91 | notInitialized: "Variable '{{idName}}' should not be initialized on declaration."
|
---|
92 | }
|
---|
93 | },
|
---|
94 |
|
---|
95 | create(context) {
|
---|
96 |
|
---|
97 | const MODE_ALWAYS = "always",
|
---|
98 | MODE_NEVER = "never";
|
---|
99 |
|
---|
100 | const mode = context.options[0] || MODE_ALWAYS;
|
---|
101 | const params = context.options[1] || {};
|
---|
102 |
|
---|
103 | //--------------------------------------------------------------------------
|
---|
104 | // Public API
|
---|
105 | //--------------------------------------------------------------------------
|
---|
106 |
|
---|
107 | return {
|
---|
108 | "VariableDeclaration:exit"(node) {
|
---|
109 |
|
---|
110 | const kind = node.kind,
|
---|
111 | declarations = node.declarations;
|
---|
112 |
|
---|
113 | for (let i = 0; i < declarations.length; ++i) {
|
---|
114 | const declaration = declarations[i],
|
---|
115 | id = declaration.id,
|
---|
116 | initialized = isInitialized(declaration),
|
---|
117 | isIgnoredForLoop = params.ignoreForLoopInit && isForLoop(node.parent);
|
---|
118 | let messageId = "";
|
---|
119 |
|
---|
120 | if (mode === MODE_ALWAYS && !initialized) {
|
---|
121 | messageId = "initialized";
|
---|
122 | } else if (mode === MODE_NEVER && kind !== "const" && initialized && !isIgnoredForLoop) {
|
---|
123 | messageId = "notInitialized";
|
---|
124 | }
|
---|
125 |
|
---|
126 | if (id.type === "Identifier" && messageId) {
|
---|
127 | context.report({
|
---|
128 | node: declaration,
|
---|
129 | messageId,
|
---|
130 | data: {
|
---|
131 | idName: id.name
|
---|
132 | }
|
---|
133 | });
|
---|
134 | }
|
---|
135 | }
|
---|
136 | }
|
---|
137 | };
|
---|
138 | }
|
---|
139 | };
|
---|