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

ota-meshi / jsonc-eslint-parser / 17888386344

21 Sep 2025 03:28AM UTC coverage: 88.63%. Remained the same
17888386344

push

github

web-flow
chore: release jsonc-eslint-parser (#242)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

308 of 366 branches covered (84.15%)

Branch coverage included in aggregate %.

2 of 2 new or added lines in 1 file covered. (100.0%)

604 of 663 relevant lines covered (91.1%)

63.98 hits per line

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

90.22
/src/parser/parser.ts
1
import type { Comment, Node } from "estree";
2
import type { AST, SourceCode } from "eslint";
3
import type { ESPree } from "./modules/espree";
4
import { getEspree } from "./modules/espree";
1✔
5
import { getVisitorKeys } from "./visitor-keys";
1✔
6
import { convertProgramNode } from "./convert";
1✔
7
import { TokenStore } from "./token-store";
1✔
8
import type { JSONProgram } from "./ast";
9
import { lte } from "semver";
1✔
10
import { getAnyTokenErrorParser, getParser } from "./extend-parser";
1✔
11
import type { JSONSyntaxContext } from "./syntax-context";
12

13
const DEFAULT_ECMA_VERSION = "latest";
1✔
14

15
/**
16
 * Parse source code
17
 */
18
export function parseForESLint(
1✔
19
  code: string,
20
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- any
21
  options?: any,
22
): {
23
  ast: JSONProgram;
24
  visitorKeys: SourceCode.VisitorKeys;
25
  services: {
26
    isJSON: boolean;
27
  };
28
} {
29
  const parserOptions = Object.assign(
176✔
30
    { filePath: "<input>", ecmaVersion: DEFAULT_ECMA_VERSION },
31
    options || {},
176!
32
    {
33
      loc: true,
34
      range: true,
35
      raw: true,
36
      tokens: true,
37
      comment: true,
38
      eslintVisitorKeys: true,
39
      eslintScopeManager: true,
40
    },
41
  );
42
  parserOptions.ecmaVersion = normalizeEcmaVersion(parserOptions.ecmaVersion);
176✔
43
  const ctx: JSONSyntaxContext = getJSONSyntaxContext(parserOptions.jsonSyntax);
176✔
44
  const tokens: AST.Token[] = [];
176✔
45
  const comments: Comment[] = [];
176✔
46
  const tokenStore = new TokenStore(tokens);
176✔
47
  const nodes: Node[] = [];
176✔
48
  parserOptions.ctx = ctx;
176✔
49
  parserOptions.tokenStore = tokenStore;
176✔
50
  parserOptions.comments = comments;
176✔
51
  parserOptions.nodes = nodes;
176✔
52
  const baseAst = getParser().parseExpressionAt(code, 0, parserOptions);
176✔
53
  // transform json nodes
54
  for (const node of nodes) {
100✔
55
    // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
56
    (node as any).type = `JSON${node.type}`;
437✔
57
  }
58
  const ast = convertProgramNode(baseAst as never, tokenStore, ctx, code);
100✔
59
  let lastIndex = Math.max(
96✔
60
    baseAst.range![1],
61
    tokens[tokens.length - 1]?.range[1] ?? 0,
576!
62
    comments[comments.length - 1]?.range![1] ?? 0,
576✔
63
  );
64
  let lastChar = code[lastIndex];
96✔
65
  while (
96✔
66
    lastChar === "\n" ||
388✔
67
    lastChar === "\r" ||
68
    lastChar === " " ||
69
    lastChar === "\t"
70
  ) {
71
    lastIndex++;
4✔
72
    lastChar = code[lastIndex];
4✔
73
  }
74
  if (lastIndex < code.length) {
96✔
75
    getAnyTokenErrorParser().parseExpressionAt(code, lastIndex, parserOptions);
2✔
76
  }
77
  ast.tokens = tokens;
94✔
78
  ast.comments = comments;
94✔
79
  return {
94✔
80
    ast,
81
    visitorKeys: getVisitorKeys(),
82
    services: {
83
      isJSON: true,
84
    },
85
  };
86
}
87

88
/**
89
 * Normalize json syntax option
90
 */
91
function getJSONSyntaxContext(str?: string | null): JSONSyntaxContext {
92
  const upperCase = str?.toUpperCase();
176✔
93
  if (upperCase === "JSON") {
176✔
94
    return {
17✔
95
      trailingCommas: false,
96
      comments: false,
97
      plusSigns: false,
98
      spacedSigns: false,
99
      leadingOrTrailingDecimalPoints: false,
100
      infinities: false,
101
      nans: false,
102
      numericSeparators: false,
103
      binaryNumericLiterals: false,
104
      octalNumericLiterals: false,
105
      legacyOctalNumericLiterals: false,
106
      invalidJsonNumbers: false,
107
      multilineStrings: false,
108
      unquoteProperties: false,
109
      singleQuotes: false,
110
      numberProperties: false,
111
      undefinedKeywords: false,
112
      sparseArrays: false,
113
      regExpLiterals: false,
114
      templateLiterals: false,
115
      bigintLiterals: false,
116
      unicodeCodepointEscapes: false,
117
      escapeSequenceInIdentifier: false,
118
      parentheses: false,
119
      staticExpressions: false,
120
    };
121
  }
122
  if (upperCase === "JSONC") {
159✔
123
    return {
18✔
124
      trailingCommas: true,
125
      comments: true,
126
      plusSigns: false,
127
      spacedSigns: false,
128
      leadingOrTrailingDecimalPoints: false,
129
      infinities: false,
130
      nans: false,
131
      numericSeparators: false,
132
      binaryNumericLiterals: false,
133
      octalNumericLiterals: false,
134
      legacyOctalNumericLiterals: false,
135
      invalidJsonNumbers: false,
136
      multilineStrings: false,
137
      unquoteProperties: false,
138
      singleQuotes: false,
139
      numberProperties: false,
140
      undefinedKeywords: false,
141
      sparseArrays: false,
142
      regExpLiterals: false,
143
      templateLiterals: false,
144
      bigintLiterals: false,
145
      unicodeCodepointEscapes: false,
146
      escapeSequenceInIdentifier: false,
147
      parentheses: false,
148
      staticExpressions: false,
149
    };
150
  }
151
  if (upperCase === "JSON5") {
141✔
152
    return {
55✔
153
      trailingCommas: true,
154
      comments: true,
155
      plusSigns: true,
156
      spacedSigns: true,
157
      leadingOrTrailingDecimalPoints: true,
158
      infinities: true,
159
      nans: true,
160
      numericSeparators: false,
161
      binaryNumericLiterals: false,
162
      octalNumericLiterals: false,
163
      legacyOctalNumericLiterals: false,
164
      invalidJsonNumbers: true,
165
      multilineStrings: true,
166
      unquoteProperties: true,
167
      singleQuotes: true,
168
      numberProperties: false,
169
      undefinedKeywords: false,
170
      sparseArrays: false,
171
      regExpLiterals: false,
172
      templateLiterals: false,
173
      bigintLiterals: false,
174
      unicodeCodepointEscapes: false,
175
      escapeSequenceInIdentifier: false,
176
      parentheses: false,
177
      staticExpressions: false,
178
    };
179
  }
180
  return {
86✔
181
    trailingCommas: true,
182
    comments: true,
183
    plusSigns: true,
184
    spacedSigns: true,
185
    leadingOrTrailingDecimalPoints: true,
186
    infinities: true,
187
    nans: true,
188
    numericSeparators: true,
189
    binaryNumericLiterals: true,
190
    octalNumericLiterals: true,
191
    legacyOctalNumericLiterals: true,
192
    invalidJsonNumbers: true,
193
    multilineStrings: true,
194
    unquoteProperties: true,
195
    singleQuotes: true,
196
    numberProperties: true,
197
    undefinedKeywords: true,
198
    sparseArrays: true,
199
    regExpLiterals: true,
200
    templateLiterals: true,
201
    bigintLiterals: true,
202
    unicodeCodepointEscapes: true,
203
    escapeSequenceInIdentifier: true,
204
    parentheses: true,
205
    staticExpressions: true,
206
  };
207
}
208

209
/**
210
 * Normalize ECMAScript version
211
 */
212
function normalizeEcmaVersion(version: number | "latest" | undefined) {
213
  const espree = getEspree();
176✔
214
  const latestEcmaVersion = getLatestEcmaVersion(espree);
176✔
215
  if (version == null || version === "latest") {
176✔
216
    return latestEcmaVersion;
1✔
217
  }
218
  return Math.min(getEcmaVersionYear(version), latestEcmaVersion);
175✔
219
}
220

221
/**
222
 * Get the latest ecma version from espree
223
 */
224
function getLatestEcmaVersion(espree: ESPree): number {
225
  if (espree.latestEcmaVersion == null) {
176!
226
    for (const { v, latest } of [
×
227
      { v: "6.1.0", latest: 2020 },
228
      { v: "4.0.0", latest: 2019 },
229
    ]) {
230
      if (lte(v, espree.version)) {
×
231
        return latest;
×
232
      }
233
    }
234
    return 2018;
×
235
  }
236
  return getEcmaVersionYear(espree.latestEcmaVersion);
176✔
237
}
238

239
/**
240
 * Get ECMAScript version year
241
 */
242
function getEcmaVersionYear(version: number) {
243
  return version > 5 && version < 2015 ? version + 2009 : version;
351✔
244
}
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