UNPKG

2.97 kBJavaScriptView Raw
1// @ts-nocheck
2
3'use strict';
4
5const _ = require('lodash');
6const beforeBlockString = require('../../utils/beforeBlockString');
7const blockString = require('../../utils/blockString');
8const hasBlock = require('../../utils/hasBlock');
9const hasEmptyBlock = require('../../utils/hasEmptyBlock');
10const optionsMatches = require('../../utils/optionsMatches');
11const report = require('../../utils/report');
12const ruleMessages = require('../../utils/ruleMessages');
13const validateOptions = require('../../utils/validateOptions');
14const whitespaceChecker = require('../../utils/whitespaceChecker');
15
16const ruleName = 'block-opening-brace-space-before';
17
18const messages = ruleMessages(ruleName, {
19 expectedBefore: () => 'Expected single space before "{"',
20 rejectedBefore: () => 'Unexpected whitespace before "{"',
21 expectedBeforeSingleLine: () => 'Expected single space before "{" of a single-line block',
22 rejectedBeforeSingleLine: () => 'Unexpected whitespace before "{" of a single-line block',
23 expectedBeforeMultiLine: () => 'Expected single space before "{" of a multi-line block',
24 rejectedBeforeMultiLine: () => 'Unexpected whitespace before "{" of a multi-line block',
25});
26
27function rule(expectation, options, context) {
28 const checker = whitespaceChecker('space', expectation, messages);
29
30 return (root, result) => {
31 const validOptions = validateOptions(
32 result,
33 ruleName,
34 {
35 actual: expectation,
36 possible: [
37 'always',
38 'never',
39 'always-single-line',
40 'never-single-line',
41 'always-multi-line',
42 'never-multi-line',
43 ],
44 },
45 {
46 actual: options,
47 possible: {
48 ignoreAtRules: [_.isString, _.isRegExp],
49 },
50 optional: true,
51 },
52 );
53
54 if (!validOptions) {
55 return;
56 }
57
58 // Check both kinds of statements: rules and at-rules
59 root.walkRules(check);
60 root.walkAtRules(check);
61
62 function check(statement) {
63 // Return early if blockless or has an empty block
64 if (!hasBlock(statement) || hasEmptyBlock(statement)) {
65 return;
66 }
67
68 // Return early if at-rule is to be ignored
69 if (optionsMatches(options, 'ignoreAtRules', statement.name)) {
70 return;
71 }
72
73 const source = beforeBlockString(statement);
74 const beforeBraceNoRaw = beforeBlockString(statement, {
75 noRawBefore: true,
76 });
77
78 let index = beforeBraceNoRaw.length - 1;
79
80 if (beforeBraceNoRaw[index - 1] === '\r') {
81 index -= 1;
82 }
83
84 checker.before({
85 source,
86 index: source.length,
87 lineCheckStr: blockString(statement),
88 err: (m) => {
89 if (context.fix) {
90 if (expectation.startsWith('always')) {
91 statement.raws.between = ' ';
92
93 return;
94 }
95
96 if (expectation.startsWith('never')) {
97 statement.raws.between = '';
98
99 return;
100 }
101 }
102
103 report({
104 message: m,
105 node: statement,
106 index,
107 result,
108 ruleName,
109 });
110 },
111 });
112 }
113 };
114}
115
116rule.ruleName = ruleName;
117rule.messages = messages;
118module.exports = rule;