]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | /** |
2 | * @fileoverview enforce the location of single-line statements | |
3 | * @author Teddy Katz | |
4 | */ | |
5 | "use strict"; | |
6 | ||
7 | //------------------------------------------------------------------------------ | |
8 | // Rule Definition | |
9 | //------------------------------------------------------------------------------ | |
10 | ||
11 | const POSITION_SCHEMA = { enum: ["beside", "below", "any"] }; | |
12 | ||
13 | module.exports = { | |
14 | meta: { | |
15 | type: "layout", | |
16 | ||
17 | docs: { | |
18 | description: "enforce the location of single-line statements", | |
eb39fafa DC |
19 | recommended: false, |
20 | url: "https://eslint.org/docs/rules/nonblock-statement-body-position" | |
21 | }, | |
22 | ||
23 | fixable: "whitespace", | |
24 | ||
25 | schema: [ | |
26 | POSITION_SCHEMA, | |
27 | { | |
28 | properties: { | |
29 | overrides: { | |
30 | properties: { | |
31 | if: POSITION_SCHEMA, | |
32 | else: POSITION_SCHEMA, | |
33 | while: POSITION_SCHEMA, | |
34 | do: POSITION_SCHEMA, | |
35 | for: POSITION_SCHEMA | |
36 | }, | |
37 | additionalProperties: false | |
38 | } | |
39 | }, | |
40 | additionalProperties: false | |
41 | } | |
42 | ], | |
43 | ||
44 | messages: { | |
45 | expectNoLinebreak: "Expected no linebreak before this statement.", | |
46 | expectLinebreak: "Expected a linebreak before this statement." | |
47 | } | |
48 | }, | |
49 | ||
50 | create(context) { | |
51 | const sourceCode = context.getSourceCode(); | |
52 | ||
53 | //---------------------------------------------------------------------- | |
54 | // Helpers | |
55 | //---------------------------------------------------------------------- | |
56 | ||
57 | /** | |
58 | * Gets the applicable preference for a particular keyword | |
59 | * @param {string} keywordName The name of a keyword, e.g. 'if' | |
60 | * @returns {string} The applicable option for the keyword, e.g. 'beside' | |
61 | */ | |
62 | function getOption(keywordName) { | |
63 | return context.options[1] && context.options[1].overrides && context.options[1].overrides[keywordName] || | |
64 | context.options[0] || | |
65 | "beside"; | |
66 | } | |
67 | ||
68 | /** | |
69 | * Validates the location of a single-line statement | |
70 | * @param {ASTNode} node The single-line statement | |
71 | * @param {string} keywordName The applicable keyword name for the single-line statement | |
72 | * @returns {void} | |
73 | */ | |
74 | function validateStatement(node, keywordName) { | |
75 | const option = getOption(keywordName); | |
76 | ||
77 | if (node.type === "BlockStatement" || option === "any") { | |
78 | return; | |
79 | } | |
80 | ||
81 | const tokenBefore = sourceCode.getTokenBefore(node); | |
82 | ||
83 | if (tokenBefore.loc.end.line === node.loc.start.line && option === "below") { | |
84 | context.report({ | |
85 | node, | |
86 | messageId: "expectLinebreak", | |
87 | fix: fixer => fixer.insertTextBefore(node, "\n") | |
88 | }); | |
89 | } else if (tokenBefore.loc.end.line !== node.loc.start.line && option === "beside") { | |
90 | context.report({ | |
91 | node, | |
92 | messageId: "expectNoLinebreak", | |
93 | fix(fixer) { | |
94 | if (sourceCode.getText().slice(tokenBefore.range[1], node.range[0]).trim()) { | |
95 | return null; | |
96 | } | |
97 | return fixer.replaceTextRange([tokenBefore.range[1], node.range[0]], " "); | |
98 | } | |
99 | }); | |
100 | } | |
101 | } | |
102 | ||
103 | //---------------------------------------------------------------------- | |
104 | // Public | |
105 | //---------------------------------------------------------------------- | |
106 | ||
107 | return { | |
108 | IfStatement(node) { | |
109 | validateStatement(node.consequent, "if"); | |
110 | ||
111 | // Check the `else` node, but don't check 'else if' statements. | |
112 | if (node.alternate && node.alternate.type !== "IfStatement") { | |
113 | validateStatement(node.alternate, "else"); | |
114 | } | |
115 | }, | |
116 | WhileStatement: node => validateStatement(node.body, "while"), | |
117 | DoWhileStatement: node => validateStatement(node.body, "do"), | |
118 | ForStatement: node => validateStatement(node.body, "for"), | |
119 | ForInStatement: node => validateStatement(node.body, "for"), | |
120 | ForOfStatement: node => validateStatement(node.body, "for") | |
121 | }; | |
122 | } | |
123 | }; |