]> git.proxmox.com Git - pve-eslint.git/blame - eslint/lib/rules/vars-on-top.js
import 8.23.1 source
[pve-eslint.git] / eslint / lib / rules / vars-on-top.js
CommitLineData
eb39fafa
DC
1/**
2 * @fileoverview Rule to enforce var declarations are only at the top of a function.
3 * @author Danny Fritz
4 * @author Gyandeep Singh
5 */
6"use strict";
7
8//------------------------------------------------------------------------------
9// Rule Definition
10//------------------------------------------------------------------------------
11
34eeec05 12/** @type {import('../shared/types').Rule} */
eb39fafa
DC
13module.exports = {
14 meta: {
15 type: "suggestion",
16
17 docs: {
8f9d1d4d 18 description: "Require `var` declarations be placed at the top of their containing scope",
eb39fafa
DC
19 recommended: false,
20 url: "https://eslint.org/docs/rules/vars-on-top"
21 },
22
23 schema: [],
24 messages: {
25 top: "All 'var' declarations must be at the top of the function scope."
26 }
27 },
28
29 create(context) {
30
31 //--------------------------------------------------------------------------
32 // Helpers
33 //--------------------------------------------------------------------------
34
eb39fafa 35 /**
609c276f 36 * Has AST suggesting a directive.
eb39fafa
DC
37 * @param {ASTNode} node any node
38 * @returns {boolean} whether the given node structurally represents a directive
39 */
40 function looksLikeDirective(node) {
41 return node.type === "ExpressionStatement" &&
42 node.expression.type === "Literal" && typeof node.expression.value === "string";
43 }
44
45 /**
46 * Check to see if its a ES6 import declaration
47 * @param {ASTNode} node any node
48 * @returns {boolean} whether the given node represents a import declaration
49 */
50 function looksLikeImport(node) {
51 return node.type === "ImportDeclaration" || node.type === "ImportSpecifier" ||
52 node.type === "ImportDefaultSpecifier" || node.type === "ImportNamespaceSpecifier";
53 }
54
55 /**
56 * Checks whether a given node is a variable declaration or not.
57 * @param {ASTNode} node any node
58 * @returns {boolean} `true` if the node is a variable declaration.
59 */
60 function isVariableDeclaration(node) {
61 return (
62 node.type === "VariableDeclaration" ||
63 (
64 node.type === "ExportNamedDeclaration" &&
65 node.declaration &&
66 node.declaration.type === "VariableDeclaration"
67 )
68 );
69 }
70
71 /**
72 * Checks whether this variable is on top of the block body
73 * @param {ASTNode} node The node to check
74 * @param {ASTNode[]} statements collection of ASTNodes for the parent node block
75 * @returns {boolean} True if var is on top otherwise false
76 */
77 function isVarOnTop(node, statements) {
78 const l = statements.length;
79 let i = 0;
80
609c276f
TL
81 // Skip over directives and imports. Static blocks don't have either.
82 if (node.parent.type !== "StaticBlock") {
83 for (; i < l; ++i) {
84 if (!looksLikeDirective(statements[i]) && !looksLikeImport(statements[i])) {
85 break;
86 }
eb39fafa
DC
87 }
88 }
89
90 for (; i < l; ++i) {
91 if (!isVariableDeclaration(statements[i])) {
92 return false;
93 }
94 if (statements[i] === node) {
95 return true;
96 }
97 }
98
99 return false;
100 }
101
102 /**
103 * Checks whether variable is on top at the global level
104 * @param {ASTNode} node The node to check
105 * @param {ASTNode} parent Parent of the node
106 * @returns {void}
107 */
108 function globalVarCheck(node, parent) {
109 if (!isVarOnTop(node, parent.body)) {
110 context.report({ node, messageId: "top" });
111 }
112 }
113
114 /**
115 * Checks whether variable is on top at functional block scope level
116 * @param {ASTNode} node The node to check
eb39fafa
DC
117 * @returns {void}
118 */
609c276f
TL
119 function blockScopeVarCheck(node) {
120 const { parent } = node;
121
122 if (
123 parent.type === "BlockStatement" &&
124 /Function/u.test(parent.parent.type) &&
125 isVarOnTop(node, parent.body)
126 ) {
127 return;
eb39fafa 128 }
609c276f
TL
129
130 if (
131 parent.type === "StaticBlock" &&
132 isVarOnTop(node, parent.body)
133 ) {
134 return;
135 }
136
137 context.report({ node, messageId: "top" });
eb39fafa
DC
138 }
139
140 //--------------------------------------------------------------------------
141 // Public API
142 //--------------------------------------------------------------------------
143
144 return {
145 "VariableDeclaration[kind='var']"(node) {
146 if (node.parent.type === "ExportNamedDeclaration") {
147 globalVarCheck(node.parent, node.parent.parent);
148 } else if (node.parent.type === "Program") {
149 globalVarCheck(node, node.parent);
150 } else {
609c276f 151 blockScopeVarCheck(node);
eb39fafa
DC
152 }
153 }
154 };
155
156 }
157};