UNPKG

2.89 kBJavaScriptView Raw
1'use strict';
2
3const addEmptyLineBefore = require('../../utils/addEmptyLineBefore');
4const hasEmptyLine = require('../../utils/hasEmptyLine');
5const isAfterComment = require('../../utils/isAfterComment');
6const isFirstNested = require('../../utils/isFirstNested');
7const isFirstNodeOfRoot = require('../../utils/isFirstNodeOfRoot');
8const isSharedLineComment = require('../../utils/isSharedLineComment');
9const optionsMatches = require('../../utils/optionsMatches');
10const removeEmptyLinesBefore = require('../../utils/removeEmptyLinesBefore');
11const report = require('../../utils/report');
12const ruleMessages = require('../../utils/ruleMessages');
13const validateOptions = require('../../utils/validateOptions');
14
15const ruleName = 'comment-empty-line-before';
16
17const messages = ruleMessages(ruleName, {
18 expected: 'Expected empty line before comment',
19 rejected: 'Unexpected empty line before comment',
20});
21
22const stylelintCommandPrefix = 'stylelint-';
23
24function rule(expectation, options, context) {
25 return (root, result) => {
26 const validOptions = validateOptions(
27 result,
28 ruleName,
29 {
30 actual: expectation,
31 possible: ['always', 'never'],
32 },
33 {
34 actual: options,
35 possible: {
36 except: ['first-nested'],
37 ignore: ['stylelint-commands', 'after-comment'],
38 },
39 optional: true,
40 },
41 );
42
43 if (!validOptions) {
44 return;
45 }
46
47 root.walkComments((comment) => {
48 // Ignore the first node
49 if (isFirstNodeOfRoot(comment)) {
50 return;
51 }
52
53 // Optionally ignore stylelint commands
54 if (
55 comment.text.startsWith(stylelintCommandPrefix) &&
56 optionsMatches(options, 'ignore', 'stylelint-commands')
57 ) {
58 return;
59 }
60
61 // Optionally ignore newlines between comments
62 if (optionsMatches(options, 'ignore', 'after-comment') && isAfterComment(comment)) {
63 return;
64 }
65
66 // Ignore shared-line comments
67 if (isSharedLineComment(comment)) {
68 return;
69 }
70
71 // Ignore SCSS comments
72 if (comment.raws.inline || comment.inline) {
73 return;
74 }
75
76 const expectEmptyLineBefore = (() => {
77 if (optionsMatches(options, 'except', 'first-nested') && isFirstNested(comment)) {
78 return false;
79 }
80
81 return expectation === 'always';
82 })();
83
84 const before = comment.raws.before || '';
85 const hasEmptyLineBefore = hasEmptyLine(before);
86
87 // Return if the expectation is met
88 if (expectEmptyLineBefore === hasEmptyLineBefore) {
89 return;
90 }
91
92 // Fix
93 if (context.fix) {
94 if (expectEmptyLineBefore) {
95 addEmptyLineBefore(comment, context.newline);
96 } else {
97 removeEmptyLinesBefore(comment, context.newline);
98 }
99
100 return;
101 }
102
103 const message = expectEmptyLineBefore ? messages.expected : messages.rejected;
104
105 report({
106 message,
107 node: comment,
108 result,
109 ruleName,
110 });
111 });
112 };
113}
114
115rule.ruleName = ruleName;
116rule.messages = messages;
117module.exports = rule;