.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / stylelint / lib / rules / at-rule-empty-line-before / index.js
1 "use strict";
2
3 const _ = require("lodash");
4 const addEmptyLineBefore = require("../../utils/addEmptyLineBefore");
5 const getPreviousNonSharedLineCommentNode = require("../../utils/getPreviousNonSharedLineCommentNode");
6 const hasEmptyLine = require("../../utils/hasEmptyLine");
7 const isAfterComment = require("../../utils/isAfterComment");
8 const isBlocklessAtRuleAfterBlocklessAtRule = require("../../utils/isBlocklessAtRuleAfterBlocklessAtRule");
9 const isBlocklessAtRuleAfterSameNameBlocklessAtRule = require("../../utils/isBlocklessAtRuleAfterSameNameBlocklessAtRule");
10 const isFirstNested = require("../../utils/isFirstNested");
11 const isFirstNodeOfRoot = require("../../utils/isFirstNodeOfRoot");
12 const optionsMatches = require("../../utils/optionsMatches");
13 const removeEmptyLinesBefore = require("../../utils/removeEmptyLinesBefore");
14 const report = require("../../utils/report");
15 const ruleMessages = require("../../utils/ruleMessages");
16 const validateOptions = require("../../utils/validateOptions");
17
18 const ruleName = "at-rule-empty-line-before";
19
20 const messages = ruleMessages(ruleName, {
21   expected: "Expected empty line before at-rule",
22   rejected: "Unexpected empty line before at-rule"
23 });
24
25 const rule = function(expectation, options, context) {
26   return (root, result) => {
27     const validOptions = validateOptions(
28       result,
29       ruleName,
30       {
31         actual: expectation,
32         possible: ["always", "never"]
33       },
34       {
35         actual: options,
36         possible: {
37           except: [
38             "after-same-name",
39             "inside-block",
40             "blockless-after-same-name-blockless",
41             "blockless-after-blockless",
42             "first-nested"
43           ],
44           ignore: [
45             "after-comment",
46             "inside-block",
47             "blockless-after-same-name-blockless",
48             "blockless-after-blockless"
49           ],
50           ignoreAtRules: [_.isString]
51         },
52         optional: true
53       }
54     );
55     if (!validOptions) {
56       return;
57     }
58
59     root.walkAtRules(atRule => {
60       const isNested = atRule.parent.type !== "root";
61       // Ignore the first node
62       if (isFirstNodeOfRoot(atRule)) {
63         return;
64       }
65
66       // Return early if at-rule is to be ignored
67       if (optionsMatches(options, "ignoreAtRules", atRule.name)) {
68         return;
69       }
70
71       // Optionally ignore the expectation if the node is blockless
72       if (
73         optionsMatches(options, "ignore", "blockless-after-blockless") &&
74         isBlocklessAtRuleAfterBlocklessAtRule(atRule)
75       ) {
76         return;
77       }
78
79       // Optionally ignore the expection if the node is blockless
80       // and following another blockless at-rule with the same name
81       if (
82         optionsMatches(
83           options,
84           "ignore",
85           "blockless-after-same-name-blockless"
86         ) &&
87         isBlocklessAtRuleAfterSameNameBlocklessAtRule(atRule)
88       ) {
89         return;
90       }
91
92       // Optionally ignore the expectation if the node is inside a block
93       if (optionsMatches(options, "ignore", "inside-block") && isNested) {
94         return;
95       }
96
97       // Optionally ignore the expectation if a comment precedes this node
98       if (
99         optionsMatches(options, "ignore", "after-comment") &&
100         isAfterComment(atRule)
101       ) {
102         return;
103       }
104
105       const hasEmptyLineBefore = hasEmptyLine(atRule.raws.before);
106       let expectEmptyLineBefore = expectation === "always" ? true : false;
107
108       // Optionally reverse the expectation if any exceptions apply
109       if (
110         (optionsMatches(options, "except", "after-same-name") &&
111           isAtRuleAfterSameNameAtRule(atRule)) ||
112         (optionsMatches(options, "except", "inside-block") && isNested) ||
113         (optionsMatches(options, "except", "first-nested") &&
114           isFirstNested(atRule)) ||
115         (optionsMatches(options, "except", "blockless-after-blockless") &&
116           isBlocklessAtRuleAfterBlocklessAtRule(atRule)) ||
117         (optionsMatches(
118           options,
119           "except",
120           "blockless-after-same-name-blockless"
121         ) &&
122           isBlocklessAtRuleAfterSameNameBlocklessAtRule(atRule))
123       ) {
124         expectEmptyLineBefore = !expectEmptyLineBefore;
125       }
126
127       // Return if the expectation is met
128       if (expectEmptyLineBefore === hasEmptyLineBefore) {
129         return;
130       }
131
132       // Fix
133       if (context.fix) {
134         if (expectEmptyLineBefore) {
135           addEmptyLineBefore(atRule, context.newline);
136         } else {
137           removeEmptyLinesBefore(atRule, context.newline);
138         }
139
140         return;
141       }
142
143       const message = expectEmptyLineBefore
144         ? messages.expected
145         : messages.rejected;
146
147       report({ message, node: atRule, result, ruleName });
148     });
149   };
150 };
151
152 function isAtRuleAfterSameNameAtRule(atRule) {
153   const previousNode = getPreviousNonSharedLineCommentNode(atRule);
154   return (
155     previousNode &&
156     previousNode.type === "atrule" &&
157     previousNode.name === atRule.name
158   );
159 }
160
161 rule.ruleName = ruleName;
162 rule.messages = messages;
163 module.exports = rule;