]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | /** |
2 | * @fileoverview Rule to enforce declarations in program or function body root. | |
3 | * @author Brandon Mills | |
4 | */ | |
5 | ||
6 | "use strict"; | |
7 | ||
56c4a2cb DC |
8 | //------------------------------------------------------------------------------ |
9 | // Requirements | |
10 | //------------------------------------------------------------------------------ | |
11 | ||
12 | const astUtils = require("./utils/ast-utils"); | |
13 | ||
eb39fafa DC |
14 | //------------------------------------------------------------------------------ |
15 | // Rule Definition | |
16 | //------------------------------------------------------------------------------ | |
17 | ||
609c276f | 18 | const validParent = new Set(["Program", "StaticBlock", "ExportNamedDeclaration", "ExportDefaultDeclaration"]); |
56c4a2cb DC |
19 | const validBlockStatementParent = new Set(["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"]); |
20 | ||
609c276f TL |
21 | /** |
22 | * Finds the nearest enclosing context where this rule allows declarations and returns its description. | |
23 | * @param {ASTNode} node Node to search from. | |
24 | * @returns {string} Description. One of "program", "function body", "class static block body". | |
25 | */ | |
26 | function getAllowedBodyDescription(node) { | |
27 | let { parent } = node; | |
28 | ||
29 | while (parent) { | |
30 | ||
31 | if (parent.type === "StaticBlock") { | |
32 | return "class static block body"; | |
33 | } | |
34 | ||
35 | if (astUtils.isFunction(parent)) { | |
36 | return "function body"; | |
37 | } | |
38 | ||
39 | ({ parent } = parent); | |
40 | } | |
41 | ||
42 | return "program"; | |
43 | } | |
44 | ||
34eeec05 | 45 | /** @type {import('../shared/types').Rule} */ |
eb39fafa DC |
46 | module.exports = { |
47 | meta: { | |
48 | type: "problem", | |
49 | ||
50 | docs: { | |
8f9d1d4d | 51 | description: "Disallow variable or `function` declarations in nested blocks", |
eb39fafa DC |
52 | recommended: true, |
53 | url: "https://eslint.org/docs/rules/no-inner-declarations" | |
54 | }, | |
55 | ||
56 | schema: [ | |
57 | { | |
58 | enum: ["functions", "both"] | |
59 | } | |
60 | ], | |
61 | ||
62 | messages: { | |
63 | moveDeclToRoot: "Move {{type}} declaration to {{body}} root." | |
64 | } | |
65 | }, | |
66 | ||
67 | create(context) { | |
68 | ||
eb39fafa DC |
69 | /** |
70 | * Ensure that a given node is at a program or function body's root. | |
71 | * @param {ASTNode} node Declaration node to check. | |
72 | * @returns {void} | |
73 | */ | |
74 | function check(node) { | |
56c4a2cb DC |
75 | const parent = node.parent; |
76 | ||
77 | if ( | |
78 | parent.type === "BlockStatement" && validBlockStatementParent.has(parent.parent.type) | |
79 | ) { | |
80 | return; | |
81 | } | |
82 | ||
83 | if (validParent.has(parent.type)) { | |
84 | return; | |
eb39fafa | 85 | } |
56c4a2cb | 86 | |
56c4a2cb DC |
87 | context.report({ |
88 | node, | |
89 | messageId: "moveDeclToRoot", | |
90 | data: { | |
91 | type: (node.type === "FunctionDeclaration" ? "function" : "variable"), | |
609c276f | 92 | body: getAllowedBodyDescription(node) |
56c4a2cb DC |
93 | } |
94 | }); | |
eb39fafa DC |
95 | } |
96 | ||
56c4a2cb | 97 | |
eb39fafa DC |
98 | return { |
99 | ||
100 | FunctionDeclaration: check, | |
101 | VariableDeclaration(node) { | |
102 | if (context.options[0] === "both" && node.kind === "var") { | |
103 | check(node); | |
104 | } | |
105 | } | |
106 | ||
107 | }; | |
108 | ||
109 | } | |
110 | }; |