@eslint/json 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -28,12 +28,13 @@ deno add @eslint/json
28
28
 
29
29
  ## Usage
30
30
 
31
- This package exports two different languages:
31
+ This package exports these languages:
32
32
 
33
33
  - `"json/json"` is for regular JSON files
34
- - `"json/jsonc"` is for JSON files that support comments (JSON-C)
34
+ - `"json/jsonc"` is for JSON files that support comments ([JSONC](https://github.com/microsoft/node-jsonc-parser)) such as those used for Visual Studio Code configuration files
35
+ - `"json/json5"` is for [JSON5](https://json5.org) files
35
36
 
36
- Depending on which types of JSON files you'd like to lint, you can set up your `eslint.config.js` file to include just the files you'd like. Here's an example that lints both JSON and JSON-C files:
37
+ Depending on which types of JSON files you'd like to lint, you can set up your `eslint.config.js` file to include just the files you'd like. Here's an example that lints JSON, JSONC, and JSON5 files:
37
38
 
38
39
  ```js
39
40
  import json from "@eslint/json";
@@ -54,14 +55,23 @@ export default [
54
55
  },
55
56
  },
56
57
 
57
- // lint JSON-C files
58
+ // lint JSONC files
58
59
  {
59
- files: ["**/*.jsonc"],
60
+ files: ["**/*.jsonc", ".vscode/*.json"],
60
61
  language: "json/jsonc",
61
62
  rules: {
62
63
  "json/no-duplicate-keys": "error",
63
64
  },
64
65
  },
66
+
67
+ // lint JSON5 files
68
+ {
69
+ files: ["**/*.json5"],
70
+ language: "json/json5",
71
+ rules: {
72
+ "json/no-duplicate-keys": "error",
73
+ },
74
+ },
65
75
  ];
66
76
  ```
67
77
 
@@ -76,23 +86,33 @@ export default [
76
86
  // lint JSON files
77
87
  {
78
88
  files: ["**/*.json"],
89
+ ignores: ["package-lock.json"],
79
90
  language: "json/json",
80
91
  ...json.configs.recommended,
81
92
  },
82
93
 
83
- // lint JSON-C files
94
+ // lint JSONC files
84
95
  {
85
96
  files: ["**/*.jsonc"],
86
97
  language: "json/jsonc",
87
98
  ...json.configs.recommended,
88
99
  },
100
+
101
+ // lint JSON5 files
102
+ {
103
+ files: ["**/*.json5"],
104
+ language: "json/json5",
105
+ ...json.configs.recommended,
106
+ },
89
107
  ];
90
108
  ```
91
109
 
110
+ **Note:** You generally want to ignore `package-lock.json` because it is auto-generated and you typically will not want to manually make changes to it.
111
+
92
112
  ## Rules
93
113
 
94
114
  - `no-duplicate-keys` - warns when there are two keys in an object with the same text.
95
- - `no-empty-keys` - warns when there is a key in an object that is an empty string or contains only whitespace
115
+ - `no-empty-keys` - warns when there is a key in an object that is an empty string or contains only whitespace (note: `package-lock.json` uses empty keys intentionally)
96
116
 
97
117
  ## License
98
118
 
@@ -15,13 +15,12 @@ var momoa = require('@humanwhocodes/momoa');
15
15
  /** @typedef {import("@humanwhocodes/momoa").DocumentNode} DocumentNode */
16
16
  /** @typedef {import("@humanwhocodes/momoa").Node} JSONNode */
17
17
  /** @typedef {import("@humanwhocodes/momoa").Token} JSONToken */
18
- /** @typedef {import("@eslint/core").SyntaxElement} SyntaxElement */
19
- /** @typedef {import("@eslint/core").Language} Language */
18
+ /** @typedef {import("@eslint/core").SourceRange} SourceRange */
19
+ /** @typedef {import("@eslint/core").SourceLocation} SourceLocation */
20
20
  /** @typedef {import("@eslint/core").File} File */
21
21
  /** @typedef {import("@eslint/core").TraversalStep} TraversalStep */
22
- /** @typedef {import("@eslint/core").VisitTraversalStep} VisitTraversalStep */
23
22
  /** @typedef {import("@eslint/core").TextSourceCode} TextSourceCode */
24
- /** @typedef {import("@eslint/core").ParseResult} ParseResult */
23
+ /** @typedef {import("@eslint/core").VisitTraversalStep} VisitTraversalStep */
25
24
 
26
25
  //-----------------------------------------------------------------------------
27
26
  // Helpers
@@ -49,7 +48,7 @@ class JSONTraversalStep {
49
48
 
50
49
  /**
51
50
  * The target of the step.
52
- * @type {JSONNode & SyntaxElement}
51
+ * @type {JSONNode}
53
52
  */
54
53
  target;
55
54
 
@@ -68,7 +67,7 @@ class JSONTraversalStep {
68
67
  /**
69
68
  * Creates a new instance.
70
69
  * @param {Object} options The options for the step.
71
- * @param {JSONNode & SyntaxElement} options.target The target of the step.
70
+ * @param {JSONNode} options.target The target of the step.
72
71
  * @param {1|2} options.phase The phase of the step.
73
72
  * @param {Array<any>} options.args The arguments of the step.
74
73
  */
@@ -108,7 +107,7 @@ class JSONSourceCode {
108
107
 
109
108
  /**
110
109
  * The AST of the source code.
111
- * @type {DocumentNode & SyntaxElement}
110
+ * @type {DocumentNode}
112
111
  */
113
112
  ast;
114
113
 
@@ -128,7 +127,7 @@ class JSONSourceCode {
128
127
  * Creates a new instance.
129
128
  * @param {Object} options The options for the instance.
130
129
  * @param {string} options.text The source code text.
131
- * @param {DocumentNode & SyntaxElement} options.ast The root AST node.
130
+ * @param {DocumentNode} options.ast The root AST node.
132
131
  */
133
132
  constructor({ text, ast }) {
134
133
  this.ast = ast;
@@ -138,6 +137,28 @@ class JSONSourceCode {
138
137
  );
139
138
  }
140
139
 
140
+ /* eslint-disable class-methods-use-this -- Required to complete interface. */
141
+
142
+ /**
143
+ * Returns the loc information for the given node or token.
144
+ * @param {JSONNode|JSONToken} nodeOrToken The node or token to get the loc information for.
145
+ * @returns {SourceLocation} The loc information for the node or token.
146
+ */
147
+ getLoc(nodeOrToken) {
148
+ return nodeOrToken.loc;
149
+ }
150
+
151
+ /**
152
+ * Returns the range information for the given node or token.
153
+ * @param {JSONNode|JSONToken} nodeOrToken The node or token to get the range information for.
154
+ * @returns {SourceRange} The range information for the node or token.
155
+ */
156
+ getRange(nodeOrToken) {
157
+ return nodeOrToken.range;
158
+ }
159
+
160
+ /* eslint-enable class-methods-use-this -- Required to complete interface. */
161
+
141
162
  /**
142
163
  * Returns the parent of the given node.
143
164
  * @param {JSONNode} node The node to get the parent of.
@@ -204,7 +225,7 @@ class JSONSourceCode {
204
225
 
205
226
  /**
206
227
  * Traverse the source code and return the steps that were taken.
207
- * @returns {Iterable<TraversalStep>} The steps that were taken while traversing the source code.
228
+ * @returns {Iterable<JSONTraversalStep>} The steps that were taken while traversing the source code.
208
229
  */
209
230
  traverse() {
210
231
  // Because the AST doesn't mutate, we can cache the steps
@@ -212,15 +233,14 @@ class JSONSourceCode {
212
233
  return this.#steps.values();
213
234
  }
214
235
 
236
+ /** @type {Array<JSONTraversalStep>} */
215
237
  const steps = (this.#steps = []);
216
238
 
217
- for (const { node, parent, phase } of momoa.iterator(
218
- /** @type {DocumentNode} */ (this.ast),
219
- )) {
239
+ for (const { node, parent, phase } of momoa.iterator(this.ast)) {
220
240
  this.#parents.set(node, parent);
221
241
  steps.push(
222
242
  new JSONTraversalStep({
223
- target: /** @type {JSONNode & SyntaxElement} */ (node),
243
+ target: node,
224
244
  phase: phase === "enter" ? 1 : 2,
225
245
  args: [node, parent],
226
246
  }),
@@ -241,6 +261,9 @@ class JSONSourceCode {
241
261
  // Types
242
262
  //-----------------------------------------------------------------------------
243
263
 
264
+ /** @typedef {import("@eslint/core").Language} Language */
265
+ /** @typedef {import("@eslint/core").OkParseResult<DocumentNode>} OkParseResult */
266
+ /** @typedef {import("@eslint/core").ParseResult<DocumentNode>} ParseResult */
244
267
 
245
268
  //-----------------------------------------------------------------------------
246
269
  // Exports
@@ -277,7 +300,7 @@ class JSONLanguage {
277
300
 
278
301
  /**
279
302
  * The parser mode.
280
- * @type {"json"|"jsonc"}
303
+ * @type {"json"|"jsonc"|"json5"}
281
304
  */
282
305
  #mode = "json";
283
306
 
@@ -290,7 +313,7 @@ class JSONLanguage {
290
313
  /**
291
314
  * Creates a new instance.
292
315
  * @param {Object} options The options to use for this instance.
293
- * @param {"json"|"jsonc"} options.mode The parser mode to use.
316
+ * @param {"json"|"jsonc"|"json5"} options.mode The parser mode to use.
294
317
  */
295
318
  constructor({ mode }) {
296
319
  this.#mode = mode;
@@ -332,7 +355,7 @@ class JSONLanguage {
332
355
 
333
356
  return {
334
357
  ok: true,
335
- ast: /** @type {DocumentNode & SyntaxElement} */ (root),
358
+ ast: root,
336
359
  };
337
360
  } catch (ex) {
338
361
  // error messages end with (line:column) so we strip that off for ESLint
@@ -356,7 +379,7 @@ class JSONLanguage {
356
379
  /**
357
380
  * Creates a new `JSONSourceCode` object from the given information.
358
381
  * @param {File} file The virtual file to create a `JSONSourceCode` object from.
359
- * @param {ParseResult} parseResult The result returned from `parse()`.
382
+ * @param {OkParseResult} parseResult The result returned from `parse()`.
360
383
  * @returns {JSONSourceCode} The new `JSONSourceCode` object.
361
384
  */
362
385
  createSourceCode(file, parseResult) {
@@ -374,7 +397,7 @@ class JSONLanguage {
374
397
  */
375
398
 
376
399
  //-----------------------------------------------------------------------------
377
- // Type Definitions
400
+ // Rule Definition
378
401
  //-----------------------------------------------------------------------------
379
402
 
380
403
  var noDuplicateKeys = {
@@ -401,7 +424,10 @@ var noDuplicateKeys = {
401
424
  },
402
425
 
403
426
  Member(node) {
404
- const key = node.name.value;
427
+ const key =
428
+ node.name.type === "String"
429
+ ? node.name.value
430
+ : node.name.name;
405
431
 
406
432
  if (keys.has(key)) {
407
433
  context.report({
@@ -443,7 +469,10 @@ var noEmptyKeys = {
443
469
  create(context) {
444
470
  return {
445
471
  Member(node) {
446
- const key = node.name.value;
472
+ const key =
473
+ node.name.type === "String"
474
+ ? node.name.value
475
+ : node.name.name;
447
476
 
448
477
  if (key.trim() === "") {
449
478
  context.report({
@@ -467,9 +496,14 @@ var noEmptyKeys = {
467
496
  //-----------------------------------------------------------------------------
468
497
 
469
498
  const plugin = {
499
+ meta: {
500
+ name: "@eslint/json",
501
+ version: "0.3.0", // x-release-please-version
502
+ },
470
503
  languages: {
471
504
  json: new JSONLanguage({ mode: "json" }),
472
505
  jsonc: new JSONLanguage({ mode: "jsonc" }),
506
+ json5: new JSONLanguage({ mode: "json5" }),
473
507
  },
474
508
  rules: {
475
509
  "no-duplicate-keys": noDuplicateKeys,
@@ -2,17 +2,24 @@ export { plugin as default };
2
2
  export type DocumentNode = import("@humanwhocodes/momoa").DocumentNode;
3
3
  export type JSONNode = import("@humanwhocodes/momoa").Node;
4
4
  export type JSONToken = import("@humanwhocodes/momoa").Token;
5
- export type SyntaxElement = import("@eslint/core").SyntaxElement;
6
- export type Language = import("@eslint/core").Language;
5
+ export type SourceRange = import("@eslint/core").SourceRange;
6
+ export type SourceLocation = import("@eslint/core").SourceLocation;
7
7
  export type File = import("@eslint/core").File;
8
8
  export type TraversalStep = import("@eslint/core").TraversalStep;
9
- export type VisitTraversalStep = import("@eslint/core").VisitTraversalStep;
10
9
  export type TextSourceCode = import("@eslint/core").TextSourceCode;
11
- export type ParseResult = import("@eslint/core").ParseResult;
10
+ export type VisitTraversalStep = import("@eslint/core").VisitTraversalStep;
11
+ export type Language = import("@eslint/core").Language;
12
+ export type OkParseResult = import("@eslint/core").OkParseResult<DocumentNode>;
13
+ export type ParseResult = import("@eslint/core").ParseResult<DocumentNode>;
12
14
  declare namespace plugin {
15
+ namespace meta {
16
+ let name: string;
17
+ let version: string;
18
+ }
13
19
  namespace languages {
14
20
  let json: JSONLanguage;
15
21
  let jsonc: JSONLanguage;
22
+ let json5: JSONLanguage;
16
23
  }
17
24
  let rules: {
18
25
  "no-duplicate-keys": {
@@ -52,6 +59,9 @@ declare namespace plugin {
52
59
  * @filedescription Functions to fix up rules to provide missing methods on the `context` object.
53
60
  * @author Nicholas C. Zakas
54
61
  */
62
+ /** @typedef {import("@eslint/core").Language} Language */
63
+ /** @typedef {import("@eslint/core").OkParseResult<DocumentNode>} OkParseResult */
64
+ /** @typedef {import("@eslint/core").ParseResult<DocumentNode>} ParseResult */
55
65
  /**
56
66
  * JSON Language Object
57
67
  * @implements {Language}
@@ -60,10 +70,10 @@ declare class JSONLanguage implements Language {
60
70
  /**
61
71
  * Creates a new instance.
62
72
  * @param {Object} options The options to use for this instance.
63
- * @param {"json"|"jsonc"} options.mode The parser mode to use.
73
+ * @param {"json"|"jsonc"|"json5"} options.mode The parser mode to use.
64
74
  */
65
75
  constructor({ mode }: {
66
- mode: "json" | "jsonc";
76
+ mode: "json" | "jsonc" | "json5";
67
77
  });
68
78
  /**
69
79
  * The type of file to read.
@@ -106,10 +116,10 @@ declare class JSONLanguage implements Language {
106
116
  /**
107
117
  * Creates a new `JSONSourceCode` object from the given information.
108
118
  * @param {File} file The virtual file to create a `JSONSourceCode` object from.
109
- * @param {ParseResult} parseResult The result returned from `parse()`.
119
+ * @param {OkParseResult} parseResult The result returned from `parse()`.
110
120
  * @returns {JSONSourceCode} The new `JSONSourceCode` object.
111
121
  */
112
- createSourceCode(file: File, parseResult: ParseResult): JSONSourceCode;
122
+ createSourceCode(file: File, parseResult: OkParseResult): JSONSourceCode;
113
123
  #private;
114
124
  }
115
125
  /**
@@ -121,17 +131,17 @@ declare class JSONSourceCode implements TextSourceCode {
121
131
  * Creates a new instance.
122
132
  * @param {Object} options The options for the instance.
123
133
  * @param {string} options.text The source code text.
124
- * @param {DocumentNode & SyntaxElement} options.ast The root AST node.
134
+ * @param {DocumentNode} options.ast The root AST node.
125
135
  */
126
136
  constructor({ text, ast }: {
127
137
  text: string;
128
- ast: DocumentNode & SyntaxElement;
138
+ ast: DocumentNode;
129
139
  });
130
140
  /**
131
141
  * The AST of the source code.
132
- * @type {DocumentNode & SyntaxElement}
142
+ * @type {DocumentNode}
133
143
  */
134
- ast: DocumentNode & SyntaxElement;
144
+ ast: DocumentNode;
135
145
  /**
136
146
  * The text of the source code.
137
147
  * @type {string}
@@ -142,6 +152,18 @@ declare class JSONSourceCode implements TextSourceCode {
142
152
  * @type {Array<JSONToken>|undefined}
143
153
  */
144
154
  comments: Array<JSONToken> | undefined;
155
+ /**
156
+ * Returns the loc information for the given node or token.
157
+ * @param {JSONNode|JSONToken} nodeOrToken The node or token to get the loc information for.
158
+ * @returns {SourceLocation} The loc information for the node or token.
159
+ */
160
+ getLoc(nodeOrToken: JSONNode | JSONToken): SourceLocation;
161
+ /**
162
+ * Returns the range information for the given node or token.
163
+ * @param {JSONNode|JSONToken} nodeOrToken The node or token to get the range information for.
164
+ * @returns {SourceRange} The range information for the node or token.
165
+ */
166
+ getRange(nodeOrToken: JSONNode | JSONToken): SourceRange;
145
167
  /**
146
168
  * Returns the parent of the given node.
147
169
  * @param {JSONNode} node The node to get the parent of.
@@ -173,8 +195,67 @@ declare class JSONSourceCode implements TextSourceCode {
173
195
  public get lines(): any[];
174
196
  /**
175
197
  * Traverse the source code and return the steps that were taken.
176
- * @returns {Iterable<TraversalStep>} The steps that were taken while traversing the source code.
198
+ * @returns {Iterable<JSONTraversalStep>} The steps that were taken while traversing the source code.
177
199
  */
178
- traverse(): Iterable<TraversalStep>;
200
+ traverse(): Iterable<JSONTraversalStep>;
179
201
  #private;
180
202
  }
203
+ /**
204
+ * @fileoverview The JSONSourceCode class.
205
+ * @author Nicholas C. Zakas
206
+ */
207
+ /** @typedef {import("@humanwhocodes/momoa").DocumentNode} DocumentNode */
208
+ /** @typedef {import("@humanwhocodes/momoa").Node} JSONNode */
209
+ /** @typedef {import("@humanwhocodes/momoa").Token} JSONToken */
210
+ /** @typedef {import("@eslint/core").SourceRange} SourceRange */
211
+ /** @typedef {import("@eslint/core").SourceLocation} SourceLocation */
212
+ /** @typedef {import("@eslint/core").File} File */
213
+ /** @typedef {import("@eslint/core").TraversalStep} TraversalStep */
214
+ /** @typedef {import("@eslint/core").TextSourceCode} TextSourceCode */
215
+ /** @typedef {import("@eslint/core").VisitTraversalStep} VisitTraversalStep */
216
+ /**
217
+ * A class to represent a step in the traversal process.
218
+ * @implements {VisitTraversalStep}
219
+ */
220
+ declare class JSONTraversalStep implements VisitTraversalStep {
221
+ /**
222
+ * Creates a new instance.
223
+ * @param {Object} options The options for the step.
224
+ * @param {JSONNode} options.target The target of the step.
225
+ * @param {1|2} options.phase The phase of the step.
226
+ * @param {Array<any>} options.args The arguments of the step.
227
+ */
228
+ constructor({ target, phase, args }: {
229
+ target: JSONNode;
230
+ phase: 1 | 2;
231
+ args: Array<any>;
232
+ });
233
+ /**
234
+ * The type of the step.
235
+ * @type {"visit"}
236
+ * @readonly
237
+ */
238
+ readonly type: "visit";
239
+ /**
240
+ * The kind of the step. Represents the same data as the `type` property
241
+ * but it's a number for performance.
242
+ * @type {1}
243
+ * @readonly
244
+ */
245
+ readonly kind: 1;
246
+ /**
247
+ * The target of the step.
248
+ * @type {JSONNode}
249
+ */
250
+ target: JSONNode;
251
+ /**
252
+ * The phase of the step.
253
+ * @type {1|2}
254
+ */
255
+ phase: 1 | 2;
256
+ /**
257
+ * The arguments of the step.
258
+ * @type {Array<any>}
259
+ */
260
+ args: Array<any>;
261
+ }
@@ -2,17 +2,24 @@ export { plugin as default };
2
2
  export type DocumentNode = import("@humanwhocodes/momoa").DocumentNode;
3
3
  export type JSONNode = import("@humanwhocodes/momoa").Node;
4
4
  export type JSONToken = import("@humanwhocodes/momoa").Token;
5
- export type SyntaxElement = import("@eslint/core").SyntaxElement;
6
- export type Language = import("@eslint/core").Language;
5
+ export type SourceRange = import("@eslint/core").SourceRange;
6
+ export type SourceLocation = import("@eslint/core").SourceLocation;
7
7
  export type File = import("@eslint/core").File;
8
8
  export type TraversalStep = import("@eslint/core").TraversalStep;
9
- export type VisitTraversalStep = import("@eslint/core").VisitTraversalStep;
10
9
  export type TextSourceCode = import("@eslint/core").TextSourceCode;
11
- export type ParseResult = import("@eslint/core").ParseResult;
10
+ export type VisitTraversalStep = import("@eslint/core").VisitTraversalStep;
11
+ export type Language = import("@eslint/core").Language;
12
+ export type OkParseResult = import("@eslint/core").OkParseResult<DocumentNode>;
13
+ export type ParseResult = import("@eslint/core").ParseResult<DocumentNode>;
12
14
  declare namespace plugin {
15
+ namespace meta {
16
+ let name: string;
17
+ let version: string;
18
+ }
13
19
  namespace languages {
14
20
  let json: JSONLanguage;
15
21
  let jsonc: JSONLanguage;
22
+ let json5: JSONLanguage;
16
23
  }
17
24
  let rules: {
18
25
  "no-duplicate-keys": {
@@ -52,6 +59,9 @@ declare namespace plugin {
52
59
  * @filedescription Functions to fix up rules to provide missing methods on the `context` object.
53
60
  * @author Nicholas C. Zakas
54
61
  */
62
+ /** @typedef {import("@eslint/core").Language} Language */
63
+ /** @typedef {import("@eslint/core").OkParseResult<DocumentNode>} OkParseResult */
64
+ /** @typedef {import("@eslint/core").ParseResult<DocumentNode>} ParseResult */
55
65
  /**
56
66
  * JSON Language Object
57
67
  * @implements {Language}
@@ -60,10 +70,10 @@ declare class JSONLanguage implements Language {
60
70
  /**
61
71
  * Creates a new instance.
62
72
  * @param {Object} options The options to use for this instance.
63
- * @param {"json"|"jsonc"} options.mode The parser mode to use.
73
+ * @param {"json"|"jsonc"|"json5"} options.mode The parser mode to use.
64
74
  */
65
75
  constructor({ mode }: {
66
- mode: "json" | "jsonc";
76
+ mode: "json" | "jsonc" | "json5";
67
77
  });
68
78
  /**
69
79
  * The type of file to read.
@@ -106,10 +116,10 @@ declare class JSONLanguage implements Language {
106
116
  /**
107
117
  * Creates a new `JSONSourceCode` object from the given information.
108
118
  * @param {File} file The virtual file to create a `JSONSourceCode` object from.
109
- * @param {ParseResult} parseResult The result returned from `parse()`.
119
+ * @param {OkParseResult} parseResult The result returned from `parse()`.
110
120
  * @returns {JSONSourceCode} The new `JSONSourceCode` object.
111
121
  */
112
- createSourceCode(file: File, parseResult: ParseResult): JSONSourceCode;
122
+ createSourceCode(file: File, parseResult: OkParseResult): JSONSourceCode;
113
123
  #private;
114
124
  }
115
125
  /**
@@ -121,17 +131,17 @@ declare class JSONSourceCode implements TextSourceCode {
121
131
  * Creates a new instance.
122
132
  * @param {Object} options The options for the instance.
123
133
  * @param {string} options.text The source code text.
124
- * @param {DocumentNode & SyntaxElement} options.ast The root AST node.
134
+ * @param {DocumentNode} options.ast The root AST node.
125
135
  */
126
136
  constructor({ text, ast }: {
127
137
  text: string;
128
- ast: DocumentNode & SyntaxElement;
138
+ ast: DocumentNode;
129
139
  });
130
140
  /**
131
141
  * The AST of the source code.
132
- * @type {DocumentNode & SyntaxElement}
142
+ * @type {DocumentNode}
133
143
  */
134
- ast: DocumentNode & SyntaxElement;
144
+ ast: DocumentNode;
135
145
  /**
136
146
  * The text of the source code.
137
147
  * @type {string}
@@ -142,6 +152,18 @@ declare class JSONSourceCode implements TextSourceCode {
142
152
  * @type {Array<JSONToken>|undefined}
143
153
  */
144
154
  comments: Array<JSONToken> | undefined;
155
+ /**
156
+ * Returns the loc information for the given node or token.
157
+ * @param {JSONNode|JSONToken} nodeOrToken The node or token to get the loc information for.
158
+ * @returns {SourceLocation} The loc information for the node or token.
159
+ */
160
+ getLoc(nodeOrToken: JSONNode | JSONToken): SourceLocation;
161
+ /**
162
+ * Returns the range information for the given node or token.
163
+ * @param {JSONNode|JSONToken} nodeOrToken The node or token to get the range information for.
164
+ * @returns {SourceRange} The range information for the node or token.
165
+ */
166
+ getRange(nodeOrToken: JSONNode | JSONToken): SourceRange;
145
167
  /**
146
168
  * Returns the parent of the given node.
147
169
  * @param {JSONNode} node The node to get the parent of.
@@ -173,8 +195,67 @@ declare class JSONSourceCode implements TextSourceCode {
173
195
  public get lines(): any[];
174
196
  /**
175
197
  * Traverse the source code and return the steps that were taken.
176
- * @returns {Iterable<TraversalStep>} The steps that were taken while traversing the source code.
198
+ * @returns {Iterable<JSONTraversalStep>} The steps that were taken while traversing the source code.
177
199
  */
178
- traverse(): Iterable<TraversalStep>;
200
+ traverse(): Iterable<JSONTraversalStep>;
179
201
  #private;
180
202
  }
203
+ /**
204
+ * @fileoverview The JSONSourceCode class.
205
+ * @author Nicholas C. Zakas
206
+ */
207
+ /** @typedef {import("@humanwhocodes/momoa").DocumentNode} DocumentNode */
208
+ /** @typedef {import("@humanwhocodes/momoa").Node} JSONNode */
209
+ /** @typedef {import("@humanwhocodes/momoa").Token} JSONToken */
210
+ /** @typedef {import("@eslint/core").SourceRange} SourceRange */
211
+ /** @typedef {import("@eslint/core").SourceLocation} SourceLocation */
212
+ /** @typedef {import("@eslint/core").File} File */
213
+ /** @typedef {import("@eslint/core").TraversalStep} TraversalStep */
214
+ /** @typedef {import("@eslint/core").TextSourceCode} TextSourceCode */
215
+ /** @typedef {import("@eslint/core").VisitTraversalStep} VisitTraversalStep */
216
+ /**
217
+ * A class to represent a step in the traversal process.
218
+ * @implements {VisitTraversalStep}
219
+ */
220
+ declare class JSONTraversalStep implements VisitTraversalStep {
221
+ /**
222
+ * Creates a new instance.
223
+ * @param {Object} options The options for the step.
224
+ * @param {JSONNode} options.target The target of the step.
225
+ * @param {1|2} options.phase The phase of the step.
226
+ * @param {Array<any>} options.args The arguments of the step.
227
+ */
228
+ constructor({ target, phase, args }: {
229
+ target: JSONNode;
230
+ phase: 1 | 2;
231
+ args: Array<any>;
232
+ });
233
+ /**
234
+ * The type of the step.
235
+ * @type {"visit"}
236
+ * @readonly
237
+ */
238
+ readonly type: "visit";
239
+ /**
240
+ * The kind of the step. Represents the same data as the `type` property
241
+ * but it's a number for performance.
242
+ * @type {1}
243
+ * @readonly
244
+ */
245
+ readonly kind: 1;
246
+ /**
247
+ * The target of the step.
248
+ * @type {JSONNode}
249
+ */
250
+ target: JSONNode;
251
+ /**
252
+ * The phase of the step.
253
+ * @type {1|2}
254
+ */
255
+ phase: 1 | 2;
256
+ /**
257
+ * The arguments of the step.
258
+ * @type {Array<any>}
259
+ */
260
+ args: Array<any>;
261
+ }
package/dist/esm/index.js CHANGED
@@ -14,13 +14,12 @@ import { iterator, visitorKeys, parse } from '@humanwhocodes/momoa';
14
14
  /** @typedef {import("@humanwhocodes/momoa").DocumentNode} DocumentNode */
15
15
  /** @typedef {import("@humanwhocodes/momoa").Node} JSONNode */
16
16
  /** @typedef {import("@humanwhocodes/momoa").Token} JSONToken */
17
- /** @typedef {import("@eslint/core").SyntaxElement} SyntaxElement */
18
- /** @typedef {import("@eslint/core").Language} Language */
17
+ /** @typedef {import("@eslint/core").SourceRange} SourceRange */
18
+ /** @typedef {import("@eslint/core").SourceLocation} SourceLocation */
19
19
  /** @typedef {import("@eslint/core").File} File */
20
20
  /** @typedef {import("@eslint/core").TraversalStep} TraversalStep */
21
- /** @typedef {import("@eslint/core").VisitTraversalStep} VisitTraversalStep */
22
21
  /** @typedef {import("@eslint/core").TextSourceCode} TextSourceCode */
23
- /** @typedef {import("@eslint/core").ParseResult} ParseResult */
22
+ /** @typedef {import("@eslint/core").VisitTraversalStep} VisitTraversalStep */
24
23
 
25
24
  //-----------------------------------------------------------------------------
26
25
  // Helpers
@@ -48,7 +47,7 @@ class JSONTraversalStep {
48
47
 
49
48
  /**
50
49
  * The target of the step.
51
- * @type {JSONNode & SyntaxElement}
50
+ * @type {JSONNode}
52
51
  */
53
52
  target;
54
53
 
@@ -67,7 +66,7 @@ class JSONTraversalStep {
67
66
  /**
68
67
  * Creates a new instance.
69
68
  * @param {Object} options The options for the step.
70
- * @param {JSONNode & SyntaxElement} options.target The target of the step.
69
+ * @param {JSONNode} options.target The target of the step.
71
70
  * @param {1|2} options.phase The phase of the step.
72
71
  * @param {Array<any>} options.args The arguments of the step.
73
72
  */
@@ -107,7 +106,7 @@ class JSONSourceCode {
107
106
 
108
107
  /**
109
108
  * The AST of the source code.
110
- * @type {DocumentNode & SyntaxElement}
109
+ * @type {DocumentNode}
111
110
  */
112
111
  ast;
113
112
 
@@ -127,7 +126,7 @@ class JSONSourceCode {
127
126
  * Creates a new instance.
128
127
  * @param {Object} options The options for the instance.
129
128
  * @param {string} options.text The source code text.
130
- * @param {DocumentNode & SyntaxElement} options.ast The root AST node.
129
+ * @param {DocumentNode} options.ast The root AST node.
131
130
  */
132
131
  constructor({ text, ast }) {
133
132
  this.ast = ast;
@@ -137,6 +136,28 @@ class JSONSourceCode {
137
136
  );
138
137
  }
139
138
 
139
+ /* eslint-disable class-methods-use-this -- Required to complete interface. */
140
+
141
+ /**
142
+ * Returns the loc information for the given node or token.
143
+ * @param {JSONNode|JSONToken} nodeOrToken The node or token to get the loc information for.
144
+ * @returns {SourceLocation} The loc information for the node or token.
145
+ */
146
+ getLoc(nodeOrToken) {
147
+ return nodeOrToken.loc;
148
+ }
149
+
150
+ /**
151
+ * Returns the range information for the given node or token.
152
+ * @param {JSONNode|JSONToken} nodeOrToken The node or token to get the range information for.
153
+ * @returns {SourceRange} The range information for the node or token.
154
+ */
155
+ getRange(nodeOrToken) {
156
+ return nodeOrToken.range;
157
+ }
158
+
159
+ /* eslint-enable class-methods-use-this -- Required to complete interface. */
160
+
140
161
  /**
141
162
  * Returns the parent of the given node.
142
163
  * @param {JSONNode} node The node to get the parent of.
@@ -203,7 +224,7 @@ class JSONSourceCode {
203
224
 
204
225
  /**
205
226
  * Traverse the source code and return the steps that were taken.
206
- * @returns {Iterable<TraversalStep>} The steps that were taken while traversing the source code.
227
+ * @returns {Iterable<JSONTraversalStep>} The steps that were taken while traversing the source code.
207
228
  */
208
229
  traverse() {
209
230
  // Because the AST doesn't mutate, we can cache the steps
@@ -211,15 +232,14 @@ class JSONSourceCode {
211
232
  return this.#steps.values();
212
233
  }
213
234
 
235
+ /** @type {Array<JSONTraversalStep>} */
214
236
  const steps = (this.#steps = []);
215
237
 
216
- for (const { node, parent, phase } of iterator(
217
- /** @type {DocumentNode} */ (this.ast),
218
- )) {
238
+ for (const { node, parent, phase } of iterator(this.ast)) {
219
239
  this.#parents.set(node, parent);
220
240
  steps.push(
221
241
  new JSONTraversalStep({
222
- target: /** @type {JSONNode & SyntaxElement} */ (node),
242
+ target: node,
223
243
  phase: phase === "enter" ? 1 : 2,
224
244
  args: [node, parent],
225
245
  }),
@@ -240,6 +260,9 @@ class JSONSourceCode {
240
260
  // Types
241
261
  //-----------------------------------------------------------------------------
242
262
 
263
+ /** @typedef {import("@eslint/core").Language} Language */
264
+ /** @typedef {import("@eslint/core").OkParseResult<DocumentNode>} OkParseResult */
265
+ /** @typedef {import("@eslint/core").ParseResult<DocumentNode>} ParseResult */
243
266
 
244
267
  //-----------------------------------------------------------------------------
245
268
  // Exports
@@ -276,7 +299,7 @@ class JSONLanguage {
276
299
 
277
300
  /**
278
301
  * The parser mode.
279
- * @type {"json"|"jsonc"}
302
+ * @type {"json"|"jsonc"|"json5"}
280
303
  */
281
304
  #mode = "json";
282
305
 
@@ -289,7 +312,7 @@ class JSONLanguage {
289
312
  /**
290
313
  * Creates a new instance.
291
314
  * @param {Object} options The options to use for this instance.
292
- * @param {"json"|"jsonc"} options.mode The parser mode to use.
315
+ * @param {"json"|"jsonc"|"json5"} options.mode The parser mode to use.
293
316
  */
294
317
  constructor({ mode }) {
295
318
  this.#mode = mode;
@@ -331,7 +354,7 @@ class JSONLanguage {
331
354
 
332
355
  return {
333
356
  ok: true,
334
- ast: /** @type {DocumentNode & SyntaxElement} */ (root),
357
+ ast: root,
335
358
  };
336
359
  } catch (ex) {
337
360
  // error messages end with (line:column) so we strip that off for ESLint
@@ -355,7 +378,7 @@ class JSONLanguage {
355
378
  /**
356
379
  * Creates a new `JSONSourceCode` object from the given information.
357
380
  * @param {File} file The virtual file to create a `JSONSourceCode` object from.
358
- * @param {ParseResult} parseResult The result returned from `parse()`.
381
+ * @param {OkParseResult} parseResult The result returned from `parse()`.
359
382
  * @returns {JSONSourceCode} The new `JSONSourceCode` object.
360
383
  */
361
384
  createSourceCode(file, parseResult) {
@@ -373,7 +396,7 @@ class JSONLanguage {
373
396
  */
374
397
 
375
398
  //-----------------------------------------------------------------------------
376
- // Type Definitions
399
+ // Rule Definition
377
400
  //-----------------------------------------------------------------------------
378
401
 
379
402
  var noDuplicateKeys = {
@@ -400,7 +423,10 @@ var noDuplicateKeys = {
400
423
  },
401
424
 
402
425
  Member(node) {
403
- const key = node.name.value;
426
+ const key =
427
+ node.name.type === "String"
428
+ ? node.name.value
429
+ : node.name.name;
404
430
 
405
431
  if (keys.has(key)) {
406
432
  context.report({
@@ -442,7 +468,10 @@ var noEmptyKeys = {
442
468
  create(context) {
443
469
  return {
444
470
  Member(node) {
445
- const key = node.name.value;
471
+ const key =
472
+ node.name.type === "String"
473
+ ? node.name.value
474
+ : node.name.name;
446
475
 
447
476
  if (key.trim() === "") {
448
477
  context.report({
@@ -466,9 +495,14 @@ var noEmptyKeys = {
466
495
  //-----------------------------------------------------------------------------
467
496
 
468
497
  const plugin = {
498
+ meta: {
499
+ name: "@eslint/json",
500
+ version: "0.3.0", // x-release-please-version
501
+ },
469
502
  languages: {
470
503
  json: new JSONLanguage({ mode: "json" }),
471
504
  jsonc: new JSONLanguage({ mode: "jsonc" }),
505
+ json5: new JSONLanguage({ mode: "json5" }),
472
506
  },
473
507
  rules: {
474
508
  "no-duplicate-keys": noDuplicateKeys,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eslint/json",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "JSON linting plugin for ESLint",
5
5
  "author": "Nicholas C. Zakas",
6
6
  "type": "module",
@@ -61,10 +61,10 @@
61
61
  ],
62
62
  "license": "Apache-2.0",
63
63
  "dependencies": {
64
- "@humanwhocodes/momoa": "^3.1.1"
64
+ "@humanwhocodes/momoa": "^3.2.0"
65
65
  },
66
66
  "devDependencies": {
67
- "@eslint/core": "^0.1.0",
67
+ "@eslint/core": "^0.3.0",
68
68
  "@types/eslint": "^8.56.10",
69
69
  "c8": "^9.1.0",
70
70
  "eslint": "^9.6.0",