• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ota-meshi / eslint-plugin-yml / 6447659798

08 Oct 2023 12:38PM UTC coverage: 89.978% (+0.1%) from 89.876%
6447659798

push

github

web-flow
feat: use eslint-compat-utils (#270)

* feat: use eslint-compat-utils

* Create fluffy-phones-battle.md

1514 of 1763 branches covered (0.0%)

Branch coverage included in aggregate %.

107 of 107 new or added lines in 31 files covered. (100.0%)

2230 of 2398 relevant lines covered (92.99%)

635.33 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.3
/src/rules/no-multiple-empty-lines.ts
1
import { createRule } from "../utils";
6✔
2
import { getSourceCode } from "../utils/compat";
1✔
3

4
export default createRule("no-multiple-empty-lines", {
1✔
5
  meta: {
6
    docs: {
7
      description: "disallow multiple empty lines",
8
      categories: null,
9
      extensionRule: "no-multiple-empty-lines",
10
      layout: true,
11
    },
12
    fixable: "whitespace",
13

14
    schema: [
15
      {
16
        type: "object",
17
        properties: {
18
          max: {
19
            type: "integer",
20
            minimum: 0,
21
          },
22
          maxEOF: {
23
            type: "integer",
24
            minimum: 0,
25
          },
26
          maxBOF: {
27
            type: "integer",
28
            minimum: 0,
29
          },
30
        },
31
        required: ["max"],
32
        additionalProperties: false,
33
      },
34
    ],
35

36
    messages: {
37
      blankBeginningOfFile:
38
        "Too many blank lines at the beginning of file. Max of {{max}} allowed.",
39
      blankEndOfFile:
40
        "Too many blank lines at the end of file. Max of {{max}} allowed.",
41
      consecutiveBlank:
42
        "More than {{max}} blank {{pluralizedLines}} not allowed.",
43
    },
44
    type: "layout",
45
  },
46
  create(context) {
47
    const sourceCode = getSourceCode(context);
89✔
48
    if (!sourceCode.parserServices.isYAML) {
89!
49
      return {};
×
50
    }
51

52
    const maxOption = context.options[0]?.max ?? 2;
89✔
53

54
    const options = {
89✔
55
      max: maxOption,
56
      maxEOF: context.options[0]?.maxEOF ?? maxOption,
169✔
57
      maxBOF: context.options[0]?.maxBOF ?? maxOption,
178✔
58
    };
59

60
    const allLines = [...sourceCode.lines];
89✔
61
    if (allLines[allLines.length - 1] === "") {
89!
62
      allLines.pop();
89✔
63
    }
64

65
    const ignoreLineIndexes = new Set();
89✔
66

67
    /**
68
     * Verify
69
     */
70
    function verifyEmptyLines(startLineIndex: number, endLineIndex: number) {
71
      const emptyLineCount = endLineIndex - startLineIndex;
73✔
72

73
      let messageId: string, max: number;
74
      if (startLineIndex === 0) {
73!
75
        messageId = "blankBeginningOfFile";
×
76
        max = options.maxBOF;
×
77
      } else if (endLineIndex === allLines.length) {
73✔
78
        messageId = "blankEndOfFile";
31✔
79
        max = options.maxEOF;
31✔
80
      } else {
81
        messageId = "consecutiveBlank";
42✔
82
        max = options.max;
42✔
83
      }
84

85
      if (emptyLineCount > max) {
73✔
86
        context.report({
51✔
87
          loc: {
88
            start: {
89
              line: startLineIndex + max + 1,
90
              column: 0,
91
            },
92
            end: { line: endLineIndex + 1, column: 0 },
93
          },
94
          messageId,
95
          data: {
96
            max: String(max),
97
            pluralizedLines: max === 1 ? "line" : "lines",
51✔
98
          },
99
          fix(fixer) {
100
            const rangeStart = sourceCode.getIndexFromLoc({
51✔
101
              line: startLineIndex + max + 1,
102
              column: 0,
103
            });
104
            const rangeEnd =
105
              endLineIndex < allLines.length
51✔
106
                ? sourceCode.getIndexFromLoc({
107
                    line: endLineIndex + 1,
108
                    column: 0,
109
                  })
110
                : sourceCode.text.length;
111

112
            return fixer.removeRange([rangeStart, rangeEnd]);
51✔
113
          },
114
        });
115
      }
116
    }
117

118
    return {
89✔
119
      YAMLScalar(node) {
120
        for (
292✔
121
          let lineIndex = node.loc.start.line - 1;
292✔
122
          lineIndex < node.loc.end.line;
123
          lineIndex++
124
        ) {
125
          ignoreLineIndexes.add(lineIndex);
468✔
126
        }
127
      },
128
      "Program:exit"() {
129
        let startEmptyLineIndex: number | null = null;
89✔
130

131
        for (let lineIndex = 0; lineIndex < allLines.length; lineIndex++) {
89✔
132
          const line = allLines[lineIndex];
631✔
133

134
          const isEmptyLine = !line.trim() && !ignoreLineIndexes.has(lineIndex);
631✔
135
          if (isEmptyLine) {
631✔
136
            startEmptyLineIndex ??= lineIndex;
113✔
137
          } else {
138
            if (startEmptyLineIndex != null) {
518✔
139
              verifyEmptyLines(startEmptyLineIndex, lineIndex);
42✔
140
            }
141
            startEmptyLineIndex = null;
518✔
142
          }
143
        }
144
        if (startEmptyLineIndex != null) {
89✔
145
          verifyEmptyLines(startEmptyLineIndex, allLines.length);
31✔
146
        }
147
      },
148
    };
149
  },
150
});
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc