UNPKG

3.42 kBJavaScriptView Raw
1// @ts-nocheck
2
3'use strict';
4
5const _ = require('lodash');
6const blockString = require('../../utils/blockString');
7const hasBlock = require('../../utils/hasBlock');
8const optionsMatches = require('../../utils/optionsMatches');
9const rawNodeString = require('../../utils/rawNodeString');
10const report = require('../../utils/report');
11const ruleMessages = require('../../utils/ruleMessages');
12const validateOptions = require('../../utils/validateOptions');
13const whitespaceChecker = require('../../utils/whitespaceChecker');
14
15const ruleName = 'block-closing-brace-newline-after';
16
17const messages = ruleMessages(ruleName, {
18 expectedAfter: () => 'Expected newline after "}"',
19 expectedAfterSingleLine: () => 'Expected newline after "}" of a single-line block',
20 rejectedAfterSingleLine: () => 'Unexpected whitespace after "}" of a single-line block',
21 expectedAfterMultiLine: () => 'Expected newline after "}" of a multi-line block',
22 rejectedAfterMultiLine: () => 'Unexpected whitespace after "}" of a multi-line block',
23});
24
25function rule(expectation, options, context) {
26 const checker = whitespaceChecker('newline', expectation, messages);
27
28 return (root, result) => {
29 const validOptions = validateOptions(
30 result,
31 ruleName,
32 {
33 actual: expectation,
34 possible: [
35 'always',
36 'always-single-line',
37 'never-single-line',
38 'always-multi-line',
39 'never-multi-line',
40 ],
41 },
42 {
43 actual: options,
44 possible: {
45 ignoreAtRules: [_.isString],
46 },
47 optional: true,
48 },
49 );
50
51 if (!validOptions) {
52 return;
53 }
54
55 // Check both kinds of statements: rules and at-rules
56 root.walkRules(check);
57 root.walkAtRules(check);
58
59 function check(statement) {
60 if (!hasBlock(statement)) {
61 return;
62 }
63
64 if (optionsMatches(options, 'ignoreAtRules', statement.name)) {
65 return;
66 }
67
68 const nextNode = statement.next();
69
70 if (!nextNode) {
71 return;
72 }
73
74 // Allow an end-of-line comment x spaces after the brace
75 const nextNodeIsSingleLineComment =
76 nextNode.type === 'comment' &&
77 !/[^ ]/.test(nextNode.raws.before || '') &&
78 !nextNode.toString().includes('\n');
79
80 const nodeToCheck = nextNodeIsSingleLineComment ? nextNode.next() : nextNode;
81
82 if (!nodeToCheck) {
83 return;
84 }
85
86 let reportIndex = statement.toString().length;
87 let source = rawNodeString(nodeToCheck);
88
89 // Skip a semicolon at the beginning, if any
90 if (source && source.startsWith(';')) {
91 source = source.slice(1);
92 reportIndex++;
93 }
94
95 // Only check one after, because there might be other
96 // spaces handled by the indentation rule
97 checker.afterOneOnly({
98 source,
99 index: -1,
100 lineCheckStr: blockString(statement),
101 err: (msg) => {
102 if (context.fix) {
103 if (expectation.startsWith('always')) {
104 const index = nodeToCheck.raws.before.search(/\r?\n/);
105
106 if (index >= 0) {
107 nodeToCheck.raws.before = nodeToCheck.raws.before.slice(index);
108 } else {
109 nodeToCheck.raws.before = context.newline + nodeToCheck.raws.before;
110 }
111
112 return;
113 }
114
115 if (expectation.startsWith('never')) {
116 nodeToCheck.raws.before = '';
117
118 return;
119 }
120 }
121
122 report({
123 message: msg,
124 node: statement,
125 index: reportIndex,
126 result,
127 ruleName,
128 });
129 },
130 });
131 }
132 };
133}
134
135rule.ruleName = ruleName;
136rule.messages = messages;
137module.exports = rule;