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

ota-meshi / eslint-plugin-jsonc / 22181448961

19 Feb 2026 12:17PM UTC coverage: 73.221% (+28.0%) from 45.195%
22181448961

push

github

web-flow
Convert to ESM-only package with tsdown bundling (#469)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: ota-meshi <16508807+ota-meshi@users.noreply.github.com>
Co-authored-by: yosuke ota <otameshiyo23@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

949 of 1185 branches covered (80.08%)

Branch coverage included in aggregate %.

133 of 141 new or added lines in 11 files covered. (94.33%)

2429 existing lines in 30 files now uncovered.

6841 of 9454 relevant lines covered (72.36%)

48.76 hits per line

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

99.0
/lib/rules/array-bracket-spacing.ts
1
// Most source code was copied from ESLint v8.
1✔
2
// MIT License. Copyright OpenJS Foundation and other contributors, <www.openjsf.org>
1✔
3
import type { AST } from "jsonc-eslint-parser";
1✔
4
import { createRule } from "../utils";
1✔
5
import { isTokenOnSameLine } from "../utils/eslint-ast-utils";
1✔
6
import type { Token } from "../types";
1✔
7

1✔
8
export interface RuleOptions {
1✔
9
  singleValue?: boolean;
1✔
10
  objectsInArrays?: boolean;
1✔
11
  arraysInArrays?: boolean;
1✔
12
}
1✔
13

1✔
14
export default createRule<["always" | "never", RuleOptions]>(
1✔
15
  "array-bracket-spacing",
1✔
16
  {
1✔
17
    meta: {
1✔
18
      docs: {
1✔
19
        description: "disallow or enforce spaces inside of brackets",
1✔
20
        recommended: null,
1✔
21
        extensionRule: true,
1✔
22
        layout: true,
1✔
23
      },
1✔
24
      type: "layout",
1✔
25

1✔
26
      fixable: "whitespace",
1✔
27

1✔
28
      schema: [
1✔
29
        {
1✔
30
          type: "string",
1✔
31
          enum: ["always", "never"],
1✔
32
        },
1✔
33
        {
1✔
34
          type: "object",
1✔
35
          properties: {
1✔
36
            singleValue: {
1✔
37
              type: "boolean",
1✔
38
            },
1✔
39
            objectsInArrays: {
1✔
40
              type: "boolean",
1✔
41
            },
1✔
42
            arraysInArrays: {
1✔
43
              type: "boolean",
1✔
44
            },
1✔
45
          },
1✔
46
          additionalProperties: false,
1✔
47
        },
1✔
48
      ],
1✔
49

1✔
50
      messages: {
1✔
51
        unexpectedSpaceAfter:
1✔
52
          "There should be no space after '{{tokenValue}}'.",
1✔
53
        unexpectedSpaceBefore:
1✔
54
          "There should be no space before '{{tokenValue}}'.",
1✔
55
        missingSpaceAfter: "A space is required after '{{tokenValue}}'.",
1✔
56
        missingSpaceBefore: "A space is required before '{{tokenValue}}'.",
1✔
57
      },
1✔
58
    },
1✔
59
    create(context) {
1✔
60
      const sourceCode = context.sourceCode;
282✔
61
      if (!sourceCode.parserServices.isJSON) {
282!
UNCOV
62
        return {};
×
UNCOV
63
      }
×
64
      const spaced = context.options[0] === "always";
282✔
65
      interface Schema1 {
282✔
66
        singleValue?: boolean;
282✔
67
        objectsInArrays?: boolean;
282✔
68
        arraysInArrays?: boolean;
282✔
69
      }
282✔
70

282✔
71
      /**
282✔
72
       * Determines whether an option is set, relative to the spacing option.
282✔
73
       * If spaced is "always", then check whether option is set to false.
282✔
74
       * If spaced is "never", then check whether option is set to true.
282✔
75
       * @param option The option to exclude.
282✔
76
       * @returns Whether or not the property is excluded.
282✔
77
       */
282✔
78
      function isOptionSet(option: keyof NonNullable<Schema1>) {
282✔
79
        return context.options[1]
846✔
80
          ? context.options[1][option] === !spaced
396✔
81
          : false;
450✔
82
      }
846✔
83

282✔
84
      const options = {
282✔
85
        spaced,
282✔
86
        singleElementException: isOptionSet("singleValue"),
282✔
87
        objectsInArraysException: isOptionSet("objectsInArrays"),
282✔
88
        arraysInArraysException: isOptionSet("arraysInArrays"),
282✔
89
        isOpeningBracketMustBeSpaced(node: AST.JSONArrayExpression) {
282✔
90
          if (options.singleElementException && node.elements.length === 1) {
350✔
91
            return !options.spaced;
40✔
92
          }
40✔
93
          const firstElement = node.elements[0];
350✔
94
          return firstElement &&
310✔
95
            ((options.objectsInArraysException && isObjectType(firstElement)) ||
292✔
96
              (options.arraysInArraysException && isArrayType(firstElement)))
256✔
97
            ? !options.spaced
78✔
98
            : options.spaced;
232✔
99
        },
350✔
100
        isClosingBracketMustBeSpaced(node: AST.JSONArrayExpression) {
282✔
101
          if (options.singleElementException && node.elements.length === 1) {
335✔
102
            return !options.spaced;
40✔
103
          }
40✔
104
          const lastElement = node.elements[node.elements.length - 1];
335✔
105
          return lastElement &&
295✔
106
            ((options.objectsInArraysException && isObjectType(lastElement)) ||
295✔
107
              (options.arraysInArraysException && isArrayType(lastElement)))
253✔
108
            ? !options.spaced
70✔
109
            : options.spaced;
225✔
110
        },
335✔
111
      };
282✔
112

282✔
113
      /**
282✔
114
       * Reports that there shouldn't be a space after the first token
282✔
115
       * @param node The node to report in the event of an error.
282✔
116
       * @param token The token to use for the report.
282✔
117
       */
282✔
118
      function reportNoBeginningSpace(node: AST.JSONNode, token: Token) {
282✔
119
        const nextToken = sourceCode.getTokenAfter(token)!;
40✔
120

40✔
121
        context.report({
40✔
122
          node: node as any,
40✔
123
          loc: { start: token.loc.end, end: nextToken.loc.start },
40✔
124
          messageId: "unexpectedSpaceAfter",
40✔
125
          data: {
40✔
126
            tokenValue: token.value,
40✔
127
          },
40✔
128
          fix(fixer) {
40✔
129
            return fixer.removeRange([token.range[1], nextToken.range[0]]);
40✔
130
          },
40✔
131
        });
40✔
132
      }
40✔
133

282✔
134
      /**
282✔
135
       * Reports that there shouldn't be a space before the last token
282✔
136
       * @param node The node to report in the event of an error.
282✔
137
       * @param token The token to use for the report.
282✔
138
       */
282✔
139
      function reportNoEndingSpace(node: AST.JSONNode, token: Token) {
282✔
140
        const previousToken = sourceCode.getTokenBefore(token)!;
38✔
141

38✔
142
        context.report({
38✔
143
          node: node as any,
38✔
144
          loc: { start: previousToken.loc.end, end: token.loc.start },
38✔
145
          messageId: "unexpectedSpaceBefore",
38✔
146
          data: {
38✔
147
            tokenValue: token.value,
38✔
148
          },
38✔
149
          fix(fixer) {
38✔
150
            return fixer.removeRange([previousToken.range[1], token.range[0]]);
38✔
151
          },
38✔
152
        });
38✔
153
      }
38✔
154

282✔
155
      /**
282✔
156
       * Reports that there should be a space after the first token
282✔
157
       * @param node The node to report in the event of an error.
282✔
158
       * @param token The token to use for the report.
282✔
159
       */
282✔
160
      function reportRequiredBeginningSpace(node: AST.JSONNode, token: Token) {
282✔
161
        context.report({
10✔
162
          node: node as any,
10✔
163
          loc: token.loc,
10✔
164
          messageId: "missingSpaceAfter",
10✔
165
          data: {
10✔
166
            tokenValue: token.value,
10✔
167
          },
10✔
168
          fix(fixer) {
10✔
169
            return fixer.insertTextAfter(token, " ");
10✔
170
          },
10✔
171
        });
10✔
172
      }
10✔
173

282✔
174
      /**
282✔
175
       * Reports that there should be a space before the last token
282✔
176
       * @param node The node to report in the event of an error.
282✔
177
       * @param token The token to use for the report.
282✔
178
       */
282✔
179
      function reportRequiredEndingSpace(node: AST.JSONNode, token: Token) {
282✔
180
        context.report({
10✔
181
          node: node as any,
10✔
182
          loc: token.loc,
10✔
183
          messageId: "missingSpaceBefore",
10✔
184
          data: {
10✔
185
            tokenValue: token.value,
10✔
186
          },
10✔
187
          fix(fixer) {
10✔
188
            return fixer.insertTextBefore(token, " ");
10✔
189
          },
10✔
190
        });
10✔
191
      }
10✔
192

282✔
193
      /**
282✔
194
       * Determines if a node is an object type
282✔
195
       * @param node The node to check.
282✔
196
       * @returns Whether or not the node is an object type.
282✔
197
       */
282✔
198
      function isObjectType(node: AST.JSONNode) {
282✔
199
        return node && node.type === "JSONObjectExpression";
128✔
200
      }
128✔
201

282✔
202
      /**
282✔
203
       * Determines if a node is an array type
282✔
204
       * @param node The node to check.
282✔
205
       * @returns Whether or not the node is an array type.
282✔
206
       */
282✔
207
      function isArrayType(node: AST.JSONNode) {
282✔
208
        return node && node.type === "JSONArrayExpression";
214✔
209
      }
214✔
210

282✔
211
      /**
282✔
212
       * Validates the spacing around array brackets
282✔
213
       * @param node The node we're checking for spacing
282✔
214
       */
282✔
215
      function validateArraySpacing(node: AST.JSONArrayExpression) {
282✔
216
        if (options.spaced && node.elements.length === 0) return;
378✔
217

378✔
218
        const first = sourceCode.getFirstToken(node as any)!;
378✔
219
        const second = sourceCode.getFirstToken(node as any, 1)!;
376✔
220
        const last = sourceCode.getLastToken(node as any)!;
376✔
221
        const penultimate = sourceCode.getTokenBefore(last)!;
376✔
222

376✔
223
        if (isTokenOnSameLine(first, second)) {
378✔
224
          if (options.isOpeningBracketMustBeSpaced(node)) {
350✔
225
            if (!sourceCode.isSpaceBetween(first, second))
140✔
226
              reportRequiredBeginningSpace(node, first);
140✔
227
          } else {
350✔
228
            if (sourceCode.isSpaceBetween(first, second))
210✔
229
              reportNoBeginningSpace(node, first);
210✔
230
          }
210✔
231
        }
350✔
232

378✔
233
        if (first !== penultimate && isTokenOnSameLine(penultimate, last)) {
378✔
234
          if (options.isClosingBracketMustBeSpaced(node)) {
335✔
235
            if (!sourceCode.isSpaceBetween(penultimate, last))
140✔
236
              reportRequiredEndingSpace(node, last);
140✔
237
          } else {
335✔
238
            if (sourceCode.isSpaceBetween(penultimate, last))
195✔
239
              reportNoEndingSpace(node, last);
195✔
240
          }
195✔
241
        }
335✔
242
      }
378✔
243

282✔
244
      return {
282✔
245
        JSONArrayExpression: validateArraySpacing,
282✔
246
      };
282✔
247
    },
282✔
248
  },
1✔
249
);
1✔
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

© 2026 Coveralls, Inc