UNPKG

3.9 kBJavaScriptView Raw
1// @ts-nocheck
2
3'use strict';
4
5const addEmptyLineBefore = require('../../utils/addEmptyLineBefore');
6const blockString = require('../../utils/blockString');
7const hasEmptyLine = require('../../utils/hasEmptyLine');
8const isAfterComment = require('../../utils/isAfterComment');
9const isAfterStandardPropertyDeclaration = require('../../utils/isAfterStandardPropertyDeclaration');
10const isCustomProperty = require('../../utils/isCustomProperty');
11const isFirstNested = require('../../utils/isFirstNested');
12const isFirstNodeOfRoot = require('../../utils/isFirstNodeOfRoot');
13const isSingleLineString = require('../../utils/isSingleLineString');
14const isStandardSyntaxDeclaration = require('../../utils/isStandardSyntaxDeclaration');
15const optionsMatches = require('../../utils/optionsMatches');
16const removeEmptyLinesBefore = require('../../utils/removeEmptyLinesBefore');
17const report = require('../../utils/report');
18const ruleMessages = require('../../utils/ruleMessages');
19const validateOptions = require('../../utils/validateOptions');
20
21const ruleName = 'declaration-empty-line-before';
22
23const messages = ruleMessages(ruleName, {
24 expected: 'Expected empty line before declaration',
25 rejected: 'Unexpected empty line before declaration',
26});
27
28function rule(expectation, options, context) {
29 return (root, result) => {
30 const validOptions = validateOptions(
31 result,
32 ruleName,
33 {
34 actual: expectation,
35 possible: ['always', 'never'],
36 },
37 {
38 actual: options,
39 possible: {
40 except: ['first-nested', 'after-comment', 'after-declaration'],
41 ignore: [
42 'after-comment',
43 'after-declaration',
44 'first-nested',
45 'inside-single-line-block',
46 ],
47 },
48 optional: true,
49 },
50 );
51
52 if (!validOptions) {
53 return;
54 }
55
56 root.walkDecls((decl) => {
57 const prop = decl.prop;
58 const parent = decl.parent;
59
60 // Ignore the first node
61 if (isFirstNodeOfRoot(decl)) {
62 return;
63 }
64
65 if (!isStandardSyntaxDeclaration(decl)) {
66 return;
67 }
68
69 if (isCustomProperty(prop)) {
70 return;
71 }
72
73 // Optionally ignore the node if a comment precedes it
74 if (optionsMatches(options, 'ignore', 'after-comment') && isAfterComment(decl)) {
75 return;
76 }
77
78 // Optionally ignore the node if a declaration precedes it
79 if (
80 optionsMatches(options, 'ignore', 'after-declaration') &&
81 isAfterStandardPropertyDeclaration(decl)
82 ) {
83 return;
84 }
85
86 // Optionally ignore the node if it is the first nested
87 if (optionsMatches(options, 'ignore', 'first-nested') && isFirstNested(decl)) {
88 return;
89 }
90
91 // Optionally ignore nodes inside single-line blocks
92 if (
93 optionsMatches(options, 'ignore', 'inside-single-line-block') &&
94 isSingleLineString(blockString(parent))
95 ) {
96 return;
97 }
98
99 let expectEmptyLineBefore = expectation === 'always';
100
101 // Optionally reverse the expectation if any exceptions apply
102 if (
103 (optionsMatches(options, 'except', 'first-nested') && isFirstNested(decl)) ||
104 (optionsMatches(options, 'except', 'after-comment') && isAfterComment(decl)) ||
105 (optionsMatches(options, 'except', 'after-declaration') &&
106 isAfterStandardPropertyDeclaration(decl))
107 ) {
108 expectEmptyLineBefore = !expectEmptyLineBefore;
109 }
110
111 // Check for at least one empty line
112 const hasEmptyLineBefore = hasEmptyLine(decl.raws.before);
113
114 // Return if the expectation is met
115 if (expectEmptyLineBefore === hasEmptyLineBefore) {
116 return;
117 }
118
119 // Fix
120 if (context.fix) {
121 if (expectEmptyLineBefore) {
122 addEmptyLineBefore(decl, context.newline);
123 } else {
124 removeEmptyLinesBefore(decl, context.newline);
125 }
126
127 return;
128 }
129
130 const message = expectEmptyLineBefore ? messages.expected : messages.rejected;
131
132 report({ message, node: decl, result, ruleName });
133 });
134 };
135}
136
137rule.ruleName = ruleName;
138rule.messages = messages;
139module.exports = rule;