@cldmv/eslint-plugin-jsonv 1.0.12 → 1.0.14
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 +57 -27
- package/dist/index.d.mts +416 -29
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +444 -63
- package/package.json +24 -5
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* @Project: @cldmv/eslint-plugin-jsonv
|
|
4
|
+
* @Filename: /index.mjs
|
|
5
|
+
* @Date: 2026-01-18T20:55:56-08:00 (1768798556)
|
|
6
|
+
* @Author: Nate Corcoran <CLDMV>
|
|
7
|
+
* @Email: <Shinrai@users.noreply.github.com>
|
|
8
|
+
* -----
|
|
9
|
+
* @Last modified by: Nate Corcoran <CLDMV> (Shinrai@users.noreply.github.com)
|
|
10
|
+
* @Last modified time: 2026-10-02T12:15:39-07:00 (1790968539)
|
|
11
|
+
* -----
|
|
12
|
+
* @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved.
|
|
13
|
+
*
|
|
14
|
+
*/
|
|
15
|
+
|
|
1
16
|
/**
|
|
2
17
|
* @fileoverview ESLint plugin for @cldmv/jsonv files
|
|
3
18
|
* @module eslint-plugin-jsonv
|
|
@@ -12,7 +27,8 @@
|
|
|
12
27
|
* - Reports parse errors with accurate source locations
|
|
13
28
|
* - Supports all ES2011-2025 features (JSON5, binary/octal literals, BigInt, numeric separators, etc.)
|
|
14
29
|
* - Detects internal reference errors (circular refs, undefined refs)
|
|
15
|
-
* - Configurable year
|
|
30
|
+
* - Configurable language options: target ES year, parse mode, strict BigInt and octal checks,
|
|
31
|
+
* and internal reference resolution
|
|
16
32
|
* - Exposes a positioned AST (objects, arrays, properties, literals, references, templates),
|
|
17
33
|
* comments and tokens, so rules can select nodes and inline `eslint-disable` comments work
|
|
18
34
|
*
|
|
@@ -38,6 +54,254 @@ import { createRequire } from "node:module";
|
|
|
38
54
|
import { dirname, join } from "node:path";
|
|
39
55
|
import { fileURLToPath } from "node:url";
|
|
40
56
|
|
|
57
|
+
//-----------------------------------------------------------------------------
|
|
58
|
+
// Types
|
|
59
|
+
//
|
|
60
|
+
// The language, its SourceCode and the plugin are typed against @eslint/core's
|
|
61
|
+
// generics (`Language`, `TextSourceCode`, `OkParseResult`, `Plugin`, ...), the same
|
|
62
|
+
// way @eslint/json types its JSON language. `tsc -p .configs/tsconfig.types.json`
|
|
63
|
+
// type-checks this JSDoc (`checkJs`) and emits it as `dist/index.d.mts`.
|
|
64
|
+
//-----------------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @import { ConfigObject, File, FileError, FileProblem, Language, OkParseResult, ParseResult, Plugin, RulesConfig, RuleVisitor, SourceLocation, SourceRange } from "@eslint/core";
|
|
68
|
+
* @import { CustomRuleDefinitionType, CustomRuleTypeDefinitions, CustomRuleVisitorWithExit, DirectiveType } from "@eslint/plugin-kit";
|
|
69
|
+
* @import { AstResult, SourceLocation as JsonvParserLocation, TokenType } from "@cldmv/jsonv/parser";
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The ES years the `year` language option accepts, matching @cldmv/jsonv's `ParseOptions["year"]`.
|
|
74
|
+
*
|
|
75
|
+
* @public
|
|
76
|
+
* @typedef {2011 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 | 2023 | 2024 | 2025} JsonvYear
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The parse modes the `mode` language option accepts, matching @cldmv/jsonv's `ParseOptions["mode"]`:
|
|
81
|
+
* `"jsonv"` (every jsonv feature of the selected year), `"json5"` (JSON5 only) or `"json"` (strict JSON).
|
|
82
|
+
*
|
|
83
|
+
* @public
|
|
84
|
+
* @typedef {"jsonv" | "json5" | "json"} JsonvMode
|
|
85
|
+
*/
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Language options for `language: "jsonv/jsonv"`. Each one is forwarded to @cldmv/jsonv's
|
|
89
|
+
* `parseWithOptions()` and `parseToAst()`. Any other key is rejected by `validateLanguageOptions`,
|
|
90
|
+
* so the type is closed.
|
|
91
|
+
*
|
|
92
|
+
* @public
|
|
93
|
+
* @typedef {object} JsonvLanguageOptions
|
|
94
|
+
* @property {JsonvYear} [year] The ES year whose jsonv features are allowed. Defaults to `2025`.
|
|
95
|
+
* @property {JsonvMode} [mode] The parse mode. Defaults to `"jsonv"`.
|
|
96
|
+
* @property {boolean} [strictBigInt] When `true`, an integer outside the safe range without an `n` suffix is an error instead of
|
|
97
|
+
* being converted to a BigInt. Defaults to `false`.
|
|
98
|
+
* @property {boolean} [strictOctal] When `true`, a legacy octal literal such as `0755` is an error (`0o755` is still allowed).
|
|
99
|
+
* Defaults to `false`.
|
|
100
|
+
* @property {boolean} [allowInternalReferences] When `false`, internal references are left unresolved, so an undefined or
|
|
101
|
+
* circular reference is no longer reported. Defaults to `true`.
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* A `Literal`: string, number, BigInt, boolean, `null`, `Infinity` or `NaN`, with its source text in `raw`.
|
|
106
|
+
*
|
|
107
|
+
* @public
|
|
108
|
+
* @typedef {object} JsonvLiteral
|
|
109
|
+
* @property {"Literal"} type The node type.
|
|
110
|
+
* @property {string | number | bigint | boolean | null} value The evaluated value.
|
|
111
|
+
* @property {string} raw The source text.
|
|
112
|
+
* @property {string} [bigint] The BigInt digits, for a BigInt literal.
|
|
113
|
+
* @property {SourceLocation} loc The location (1-based lines and columns).
|
|
114
|
+
* @property {SourceRange} range The `[start, end)` source offsets.
|
|
115
|
+
*/
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* An `Identifier`: an unquoted key, or an internal reference such as `backup: port`.
|
|
119
|
+
*
|
|
120
|
+
* @public
|
|
121
|
+
* @typedef {object} JsonvIdentifier
|
|
122
|
+
* @property {"Identifier"} type The node type.
|
|
123
|
+
* @property {string} name The identifier text.
|
|
124
|
+
* @property {SourceLocation} loc The location (1-based lines and columns).
|
|
125
|
+
* @property {SourceRange} range The `[start, end)` source offsets.
|
|
126
|
+
*/
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* An `ObjectExpression`: `{ ... }`.
|
|
130
|
+
*
|
|
131
|
+
* @public
|
|
132
|
+
* @typedef {object} JsonvObjectExpression
|
|
133
|
+
* @property {"ObjectExpression"} type The node type.
|
|
134
|
+
* @property {JsonvProperty[]} properties The properties, in source order.
|
|
135
|
+
* @property {SourceLocation} loc The location (1-based lines and columns).
|
|
136
|
+
* @property {SourceRange} range The `[start, end)` source offsets.
|
|
137
|
+
*/
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* A `Property`: `key: value`. The key is a `Literal` (quoted or numeric key) or an `Identifier` (unquoted key).
|
|
141
|
+
*
|
|
142
|
+
* @public
|
|
143
|
+
* @typedef {object} JsonvProperty
|
|
144
|
+
* @property {"Property"} type The node type.
|
|
145
|
+
* @property {JsonvLiteral | JsonvIdentifier} key The key.
|
|
146
|
+
* @property {JsonvExpression} value The value.
|
|
147
|
+
* @property {boolean} computed Always `false` in jsonv.
|
|
148
|
+
* @property {SourceLocation} loc The location (1-based lines and columns).
|
|
149
|
+
* @property {SourceRange} range The `[start, end)` source offsets.
|
|
150
|
+
*/
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* An `ArrayExpression`: `[ ... ]`.
|
|
154
|
+
*
|
|
155
|
+
* @public
|
|
156
|
+
* @typedef {object} JsonvArrayExpression
|
|
157
|
+
* @property {"ArrayExpression"} type The node type.
|
|
158
|
+
* @property {JsonvExpression[]} elements The elements, in source order.
|
|
159
|
+
* @property {SourceLocation} loc The location (1-based lines and columns).
|
|
160
|
+
* @property {SourceRange} range The `[start, end)` source offsets.
|
|
161
|
+
*/
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* A `MemberExpression`: a dotted internal reference such as `server.port`.
|
|
165
|
+
*
|
|
166
|
+
* @public
|
|
167
|
+
* @typedef {object} JsonvMemberExpression
|
|
168
|
+
* @property {"MemberExpression"} type The node type.
|
|
169
|
+
* @property {JsonvExpression} object The object being accessed.
|
|
170
|
+
* @property {JsonvIdentifier} property The accessed property.
|
|
171
|
+
* @property {false} computed Always `false`.
|
|
172
|
+
* @property {SourceLocation} loc The location (1-based lines and columns).
|
|
173
|
+
* @property {SourceRange} range The `[start, end)` source offsets.
|
|
174
|
+
*/
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* A `TemplateElement`: one string segment of a template literal.
|
|
178
|
+
*
|
|
179
|
+
* @public
|
|
180
|
+
* @typedef {object} JsonvTemplateElement
|
|
181
|
+
* @property {"TemplateElement"} type The node type.
|
|
182
|
+
* @property {{raw: string, cooked: string}} value The raw and cooked segment text.
|
|
183
|
+
* @property {boolean} tail Whether this is the last segment.
|
|
184
|
+
* @property {SourceLocation} loc The location (1-based lines and columns).
|
|
185
|
+
* @property {SourceRange} range The `[start, end)` source offsets.
|
|
186
|
+
*/
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* A `TemplateLiteral`: a backtick string and its `${...}` interpolations.
|
|
190
|
+
*
|
|
191
|
+
* @public
|
|
192
|
+
* @typedef {object} JsonvTemplateLiteral
|
|
193
|
+
* @property {"TemplateLiteral"} type The node type.
|
|
194
|
+
* @property {JsonvTemplateElement[]} quasis The string segments.
|
|
195
|
+
* @property {JsonvExpression[]} expressions The interpolated expressions.
|
|
196
|
+
* @property {SourceLocation} loc The location (1-based lines and columns).
|
|
197
|
+
* @property {SourceRange} range The `[start, end)` source offsets.
|
|
198
|
+
*/
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Any value node.
|
|
202
|
+
*
|
|
203
|
+
* @public
|
|
204
|
+
* @typedef {JsonvLiteral | JsonvObjectExpression | JsonvArrayExpression | JsonvIdentifier | JsonvTemplateLiteral | JsonvMemberExpression} JsonvExpression
|
|
205
|
+
*/
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* A `Line` (`// ...`) or `Block` (`/* ... *\/`) comment. `value` excludes the delimiters; `loc` and `range` include them.
|
|
209
|
+
*
|
|
210
|
+
* @public
|
|
211
|
+
* @typedef {object} JsonvComment
|
|
212
|
+
* @property {"Line" | "Block"} type The comment kind.
|
|
213
|
+
* @property {string} value The comment text without its delimiters.
|
|
214
|
+
* @property {SourceLocation} loc The location (1-based lines and columns).
|
|
215
|
+
* @property {SourceRange} range The `[start, end)` source offsets.
|
|
216
|
+
*/
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* A token (comments and the end-of-file token are not included).
|
|
220
|
+
*
|
|
221
|
+
* @public
|
|
222
|
+
* @typedef {object} JsonvToken
|
|
223
|
+
* @property {`${TokenType}`} type The token type, e.g. `"{"`, `"String"`, `"Identifier"`, `"TemplateHead"`.
|
|
224
|
+
* @property {string | number | bigint | boolean | null} value The token's value.
|
|
225
|
+
* @property {string} raw The source text.
|
|
226
|
+
* @property {SourceLocation} loc The location (1-based lines and columns).
|
|
227
|
+
* @property {SourceRange} range The `[start, end)` source offsets.
|
|
228
|
+
*/
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* The `Program` root: `body` is the root value; `comments` and `tokens` are in source order.
|
|
232
|
+
*
|
|
233
|
+
* @public
|
|
234
|
+
* @typedef {object} JsonvProgram
|
|
235
|
+
* @property {"Program"} type The node type.
|
|
236
|
+
* @property {JsonvExpression} body The root value.
|
|
237
|
+
* @property {JsonvComment[]} comments The comments.
|
|
238
|
+
* @property {JsonvToken[]} tokens The tokens, excluding comments.
|
|
239
|
+
* @property {SourceLocation} loc The location of the whole document (1-based lines and columns).
|
|
240
|
+
* @property {SourceRange} range The `[start, end)` source offsets of the whole document.
|
|
241
|
+
*/
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Any AST node.
|
|
245
|
+
*
|
|
246
|
+
* @public
|
|
247
|
+
* @typedef {JsonvProgram | JsonvExpression | JsonvProperty | JsonvTemplateElement} JsonvNode
|
|
248
|
+
*/
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Anything with a location: a node, a token or a comment.
|
|
252
|
+
*
|
|
253
|
+
* @public
|
|
254
|
+
* @typedef {JsonvNode | JsonvToken | JsonvComment} JsonvSyntaxElement
|
|
255
|
+
*/
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* The jsonv language, typed with @eslint/core's `Language` generics.
|
|
259
|
+
*
|
|
260
|
+
* @public
|
|
261
|
+
* @typedef {Language<{LangOptions: JsonvLanguageOptions, Code: JsonvSourceCode, RootNode: JsonvProgram, Node: JsonvNode}>} JsonvLanguage
|
|
262
|
+
*/
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* A successful `parse()` result. Besides `ast` it carries `jsonvValue`, the evaluated document
|
|
266
|
+
* (references resolved).
|
|
267
|
+
*
|
|
268
|
+
* @public
|
|
269
|
+
* @typedef {OkParseResult<JsonvProgram>} JsonvOkParseResult
|
|
270
|
+
*/
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* A `parse()` result: {@link JsonvOkParseResult}, or a failure carrying `errors`.
|
|
274
|
+
*
|
|
275
|
+
* @public
|
|
276
|
+
* @typedef {ParseResult<JsonvProgram>} JsonvParseResult
|
|
277
|
+
*/
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* The visitor a jsonv rule returns: a handler per node type (plus `:exit` variants), each called
|
|
281
|
+
* with the node and its parent.
|
|
282
|
+
*
|
|
283
|
+
* @public
|
|
284
|
+
* @typedef {RuleVisitor & CustomRuleVisitorWithExit<{
|
|
285
|
+
* Program?: (node: JsonvProgram) => void,
|
|
286
|
+
* ObjectExpression?: (node: JsonvObjectExpression, parent?: JsonvNode) => void,
|
|
287
|
+
* Property?: (node: JsonvProperty, parent?: JsonvObjectExpression) => void,
|
|
288
|
+
* ArrayExpression?: (node: JsonvArrayExpression, parent?: JsonvNode) => void,
|
|
289
|
+
* Literal?: (node: JsonvLiteral, parent?: JsonvNode) => void,
|
|
290
|
+
* Identifier?: (node: JsonvIdentifier, parent?: JsonvNode) => void,
|
|
291
|
+
* MemberExpression?: (node: JsonvMemberExpression, parent?: JsonvNode) => void,
|
|
292
|
+
* TemplateLiteral?: (node: JsonvTemplateLiteral, parent?: JsonvNode) => void,
|
|
293
|
+
* TemplateElement?: (node: JsonvTemplateElement, parent?: JsonvTemplateLiteral) => void
|
|
294
|
+
* }>} JsonvRuleVisitor
|
|
295
|
+
*/
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* A rule for jsonv files. `Options` sets `RuleOptions`, `MessageIds` and `ExtRuleDocs`.
|
|
299
|
+
*
|
|
300
|
+
* @public
|
|
301
|
+
* @template {Partial<CustomRuleTypeDefinitions>} [Options={}]
|
|
302
|
+
* @typedef {CustomRuleDefinitionType<{LangOptions: JsonvLanguageOptions, Code: JsonvSourceCode, Visitor: JsonvRuleVisitor, Node: JsonvNode}, Options>} JsonvRuleDefinition
|
|
303
|
+
*/
|
|
304
|
+
|
|
41
305
|
/**
|
|
42
306
|
* Reads this package's own `name` and `version` from its `package.json`.
|
|
43
307
|
*
|
|
@@ -61,14 +325,43 @@ function readPackageMeta() {
|
|
|
61
325
|
}
|
|
62
326
|
|
|
63
327
|
/**
|
|
64
|
-
*
|
|
65
|
-
* forwards to
|
|
66
|
-
* `
|
|
328
|
+
* The default of every language option `jsonvLanguage#parse` reads from `context.languageOptions`
|
|
329
|
+
* and forwards to @cldmv/jsonv's `parseWithOptions()` and `parseToAst()`. The defaults are
|
|
330
|
+
* @cldmv/jsonv's own (`ParseOptions` in its `src/api-types.mts`), except `year`, which jsonv
|
|
331
|
+
* derives from the current date and the plugin pins to the latest supported year.
|
|
332
|
+
*
|
|
333
|
+
* @public
|
|
334
|
+
* @type {Readonly<Required<JsonvLanguageOptions>>}
|
|
335
|
+
*/
|
|
336
|
+
const DEFAULT_LANGUAGE_OPTIONS = Object.freeze({
|
|
337
|
+
year: 2025,
|
|
338
|
+
mode: "jsonv",
|
|
339
|
+
strictBigInt: false,
|
|
340
|
+
strictOctal: false,
|
|
341
|
+
allowInternalReferences: true
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* The language options the plugin accepts: exactly the keys of {@link DEFAULT_LANGUAGE_OPTIONS}.
|
|
346
|
+
* Any other key is rejected by `validateLanguageOptions`, since it would silently do nothing.
|
|
67
347
|
*
|
|
68
348
|
* @public
|
|
69
349
|
* @type {Set<string>}
|
|
70
350
|
*/
|
|
71
|
-
const SUPPORTED_LANGUAGE_OPTIONS = new Set(
|
|
351
|
+
const SUPPORTED_LANGUAGE_OPTIONS = new Set(Object.keys(DEFAULT_LANGUAGE_OPTIONS));
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* @cldmv/jsonv parse options the plugin deliberately does not accept, each with the reason
|
|
355
|
+
* `validateLanguageOptions` gives when a config sets it.
|
|
356
|
+
*
|
|
357
|
+
* @public
|
|
358
|
+
* @type {Readonly<Record<string, string>>}
|
|
359
|
+
*/
|
|
360
|
+
const UNSUPPORTED_JSONV_OPTIONS = Object.freeze({
|
|
361
|
+
reviver: "a reviver only transforms the evaluated value, which linting does not use",
|
|
362
|
+
preserveComments: "the AST always carries the comments",
|
|
363
|
+
tolerant: "the plugin controls how parse errors are collected"
|
|
364
|
+
});
|
|
72
365
|
|
|
73
366
|
/**
|
|
74
367
|
* Valid `year` language option values, matching @cldmv/jsonv's `ParseOptions["year"]`
|
|
@@ -79,6 +372,22 @@ const SUPPORTED_LANGUAGE_OPTIONS = new Set(["year", "strictBigInt"]);
|
|
|
79
372
|
*/
|
|
80
373
|
const VALID_YEARS = new Set([2011, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025]);
|
|
81
374
|
|
|
375
|
+
/**
|
|
376
|
+
* Valid `mode` language option values, matching @cldmv/jsonv's `ParseOptions["mode"]` union.
|
|
377
|
+
*
|
|
378
|
+
* @public
|
|
379
|
+
* @type {Set<string>}
|
|
380
|
+
*/
|
|
381
|
+
const VALID_MODES = new Set(["jsonv", "json5", "json"]);
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* The language options that take a boolean.
|
|
385
|
+
*
|
|
386
|
+
* @public
|
|
387
|
+
* @type {ReadonlyArray<"strictBigInt" | "strictOctal" | "allowInternalReferences">}
|
|
388
|
+
*/
|
|
389
|
+
const BOOLEAN_LANGUAGE_OPTIONS = Object.freeze(["strictBigInt", "strictOctal", "allowInternalReferences"]);
|
|
390
|
+
|
|
82
391
|
/**
|
|
83
392
|
* Child keys of every node type in the AST `jsonvLanguage#parse` produces, in source order
|
|
84
393
|
* where that is defined. The node types and their shapes come from @cldmv/jsonv's own AST
|
|
@@ -138,19 +447,26 @@ const commentParser = new ConfigCommentParser();
|
|
|
138
447
|
* Tests whether a value is a positioned @cldmv/jsonv AST node.
|
|
139
448
|
* @public
|
|
140
449
|
* @param {unknown} value - Any property value of a node.
|
|
141
|
-
* @returns {
|
|
450
|
+
* @returns {value is {type: string, [key: string]: unknown}} `true` for an object with a string `type` and a `loc`.
|
|
142
451
|
* @example
|
|
143
452
|
* isJsonvNode({ type: "Literal", loc: { start: {}, end: {} } }); // true
|
|
144
453
|
*/
|
|
145
454
|
function isJsonvNode(value) {
|
|
146
|
-
return
|
|
455
|
+
return (
|
|
456
|
+
value !== null &&
|
|
457
|
+
typeof value === "object" &&
|
|
458
|
+
"type" in value &&
|
|
459
|
+
typeof value.type === "string" &&
|
|
460
|
+
"loc" in value &&
|
|
461
|
+
typeof value.loc === "object"
|
|
462
|
+
);
|
|
147
463
|
}
|
|
148
464
|
|
|
149
465
|
/**
|
|
150
466
|
* Returns the child keys to visit for a node: its entry in {@link VISITOR_KEYS}, or — for a
|
|
151
467
|
* node type @cldmv/jsonv may add later — every key holding a node or an array of nodes.
|
|
152
468
|
* @public
|
|
153
|
-
* @param {
|
|
469
|
+
* @param {{type: string, [key: string]: unknown}} node - An AST node.
|
|
154
470
|
* @returns {ReadonlyArray<string>} Child property names.
|
|
155
471
|
* @example
|
|
156
472
|
* getChildKeys({ type: "Property", key, value }); // ["key", "value"]
|
|
@@ -170,8 +486,8 @@ function getChildKeys(node) {
|
|
|
170
486
|
* ESLint `loc`/`range` pair for this language. The language declares `lineStart: 1` and
|
|
171
487
|
* `columnStart: 1`, so columns are shifted to 1-based; `range` comes from the offsets.
|
|
172
488
|
* @public
|
|
173
|
-
* @param {
|
|
174
|
-
* @returns {{loc:
|
|
489
|
+
* @param {JsonvParserLocation} loc - A jsonv location.
|
|
490
|
+
* @returns {{loc: SourceLocation, range: SourceRange}} The ESLint location and range.
|
|
175
491
|
* @example
|
|
176
492
|
* toEslintPosition({ start: { line: 1, column: 0, offset: 0 }, end: { line: 1, column: 3, offset: 3 } });
|
|
177
493
|
* // { loc: { start: { line: 1, column: 1 }, end: { line: 1, column: 4 } }, range: [0, 3] }
|
|
@@ -191,28 +507,36 @@ function toEslintPosition(loc) {
|
|
|
191
507
|
* Tokens are never walked for children: some token types share a name with a node type
|
|
192
508
|
* (`Identifier`, `TemplateLiteral`) but carry no child nodes.
|
|
193
509
|
* @public
|
|
194
|
-
* @
|
|
195
|
-
* @
|
|
510
|
+
* @template {{loc: JsonvParserLocation}} T
|
|
511
|
+
* @param {T} element - A jsonv comment or token.
|
|
512
|
+
* @returns {{[K in keyof T]: K extends "loc" ? SourceLocation : T[K]} & {range: SourceRange}} The converted copy.
|
|
196
513
|
* @example
|
|
197
514
|
* convertLeaf(parseToAst("// note\n1").comments[0]);
|
|
198
515
|
*/
|
|
199
516
|
function convertLeaf(element) {
|
|
200
|
-
|
|
517
|
+
// TypeScript types a spread of a generic as an intersection (`T & {loc, range}`), which keeps
|
|
518
|
+
// the jsonv `loc` type alongside the ESLint one; the cast states the actual replacement.
|
|
519
|
+
return /** @type {{[K in keyof T]: K extends "loc" ? SourceLocation : T[K]} & {range: SourceRange}} */ ({
|
|
520
|
+
...element,
|
|
521
|
+
...toEslintPosition(element.loc)
|
|
522
|
+
});
|
|
201
523
|
}
|
|
202
524
|
|
|
203
525
|
/**
|
|
204
526
|
* Deep-copies a jsonv AST node with ESLint positions: every node gets a 1-based `loc` and a
|
|
205
527
|
* `range`, and child nodes are converted recursively.
|
|
206
528
|
* @public
|
|
207
|
-
* @param {
|
|
208
|
-
* @returns {
|
|
529
|
+
* @param {{type: string, loc: JsonvParserLocation, [key: string]: unknown}} node - A jsonv AST node.
|
|
530
|
+
* @returns {{type: string, [key: string]: unknown}} The converted copy.
|
|
209
531
|
* @example
|
|
210
532
|
* convertNode(parseToAst("{ a: 1 }").program.body);
|
|
211
533
|
*/
|
|
212
534
|
function convertNode(node) {
|
|
535
|
+
/** @type {{type: string, [key: string]: unknown}} */
|
|
213
536
|
const converted = convertLeaf(node);
|
|
214
537
|
for (const key of getChildKeys(node)) {
|
|
215
|
-
|
|
538
|
+
// getChildKeys() only returns keys that hold a node or an array of nodes.
|
|
539
|
+
const child = /** @type {Parameters<typeof convertNode>[0] | Parameters<typeof convertNode>[0][]} */ (node[key]);
|
|
216
540
|
converted[key] = Array.isArray(child) ? child.map(convertNode) : convertNode(child);
|
|
217
541
|
}
|
|
218
542
|
return converted;
|
|
@@ -222,14 +546,19 @@ function convertNode(node) {
|
|
|
222
546
|
* Builds the ESLint AST for a document from @cldmv/jsonv's `parseToAst()` result: the
|
|
223
547
|
* `Program` root (whose `body` is the root value) with positioned `comments` and `tokens`.
|
|
224
548
|
* @public
|
|
225
|
-
* @param {
|
|
226
|
-
* @returns {
|
|
549
|
+
* @param {AstResult} astResult - The `parseToAst()` result.
|
|
550
|
+
* @returns {JsonvProgram} The `Program` node.
|
|
227
551
|
* @example
|
|
228
552
|
* buildProgram(parseToAst("// note\n{ a: 1 }"));
|
|
229
553
|
*/
|
|
230
554
|
function buildProgram({ program, comments, tokens }) {
|
|
555
|
+
// @cldmv/jsonv positions every node it produces (`ASTNode#loc` is only optional in its type), and
|
|
556
|
+
// convertNode() keeps each node's shape while swapping in ESLint positions, so the copy is a
|
|
557
|
+
// JsonvProgram. The generic, runtime-keyed walk cannot express that per node type, hence the casts.
|
|
558
|
+
const rawProgram = /** @type {Parameters<typeof convertNode>[0]} */ (/** @type {unknown} */ (program));
|
|
559
|
+
const root = /** @type {Omit<JsonvProgram, "comments" | "tokens">} */ (convertNode(rawProgram));
|
|
231
560
|
return {
|
|
232
|
-
...
|
|
561
|
+
...root,
|
|
233
562
|
comments: comments.map(convertLeaf),
|
|
234
563
|
tokens: tokens.map(convertLeaf)
|
|
235
564
|
};
|
|
@@ -242,6 +571,7 @@ function buildProgram({ program, comments, tokens }) {
|
|
|
242
571
|
* directives and `/* eslint rule: ... *\/` config comments).
|
|
243
572
|
*
|
|
244
573
|
* @public
|
|
574
|
+
* @extends {TextSourceCodeBase<{LangOptions: JsonvLanguageOptions, RootNode: JsonvProgram, SyntaxElementWithLoc: JsonvSyntaxElement, ConfigNode: JsonvComment}>}
|
|
245
575
|
*/
|
|
246
576
|
class JsonvSourceCode extends TextSourceCodeBase {
|
|
247
577
|
/**
|
|
@@ -252,13 +582,13 @@ class JsonvSourceCode extends TextSourceCodeBase {
|
|
|
252
582
|
|
|
253
583
|
/**
|
|
254
584
|
* Parent of every non-root node.
|
|
255
|
-
* @type {WeakMap<
|
|
585
|
+
* @type {WeakMap<object, JsonvNode>}
|
|
256
586
|
*/
|
|
257
587
|
#parents = new WeakMap();
|
|
258
588
|
|
|
259
589
|
/**
|
|
260
590
|
* Cached inline configuration comments.
|
|
261
|
-
* @type {
|
|
591
|
+
* @type {JsonvComment[]|undefined}
|
|
262
592
|
*/
|
|
263
593
|
#inlineConfigComments;
|
|
264
594
|
|
|
@@ -266,13 +596,13 @@ class JsonvSourceCode extends TextSourceCodeBase {
|
|
|
266
596
|
* Creates a SourceCode object.
|
|
267
597
|
* @param {Object} options - Options.
|
|
268
598
|
* @param {string} options.text - The source text.
|
|
269
|
-
* @param {
|
|
599
|
+
* @param {JsonvProgram} options.ast - The `Program` node built by `jsonvLanguage#parse`.
|
|
270
600
|
*/
|
|
271
601
|
constructor({ text, ast }) {
|
|
272
602
|
super({ text, ast, lineEndingPattern: LINE_ENDING_PATTERN });
|
|
273
|
-
/** @type {
|
|
603
|
+
/** @type {JsonvComment[]} Comments in source order. */
|
|
274
604
|
this.comments = ast.comments;
|
|
275
|
-
/** @type {
|
|
605
|
+
/** @type {JsonvToken[]} Tokens in source order, excluding comments. */
|
|
276
606
|
this.tokens = ast.tokens;
|
|
277
607
|
/** @type {boolean} Whether the text starts with a byte order mark. */
|
|
278
608
|
this.hasBOM = text.charCodeAt(0) === 0xfeff;
|
|
@@ -293,12 +623,18 @@ class JsonvSourceCode extends TextSourceCodeBase {
|
|
|
293
623
|
*/
|
|
294
624
|
#ensureTraversal() {
|
|
295
625
|
if (!this.#steps) {
|
|
626
|
+
/** @type {VisitNodeStep[]} */
|
|
296
627
|
const steps = [];
|
|
628
|
+
/**
|
|
629
|
+
* @param {JsonvNode} node - The node to visit.
|
|
630
|
+
* @param {JsonvNode|undefined} parent - Its parent (`undefined` for the root).
|
|
631
|
+
*/
|
|
297
632
|
const visit = (node, parent) => {
|
|
298
633
|
if (parent) this.#parents.set(node, parent);
|
|
299
634
|
steps.push(new VisitNodeStep({ target: node, phase: 1, args: [node, parent] }));
|
|
300
635
|
for (const key of getChildKeys(node)) {
|
|
301
|
-
|
|
636
|
+
// getChildKeys() only returns keys that hold a node or an array of nodes.
|
|
637
|
+
const child = /** @type {JsonvNode | JsonvNode[]} */ (/** @type {{type: string, [key: string]: unknown}} */ (node)[key]);
|
|
302
638
|
for (const item of Array.isArray(child) ? child : [child]) visit(item, node);
|
|
303
639
|
}
|
|
304
640
|
steps.push(new VisitNodeStep({ target: node, phase: 2, args: [node, parent] }));
|
|
@@ -312,7 +648,7 @@ class JsonvSourceCode extends TextSourceCodeBase {
|
|
|
312
648
|
/**
|
|
313
649
|
* Returns the enter/exit visit steps for every node, depth first in source order.
|
|
314
650
|
* @public
|
|
315
|
-
* @returns {
|
|
651
|
+
* @returns {IterableIterator<VisitNodeStep>} The traversal steps.
|
|
316
652
|
*/
|
|
317
653
|
traverse() {
|
|
318
654
|
return this.#ensureTraversal().values();
|
|
@@ -321,8 +657,8 @@ class JsonvSourceCode extends TextSourceCodeBase {
|
|
|
321
657
|
/**
|
|
322
658
|
* Returns the parent of a node, or `undefined` for the `Program` root.
|
|
323
659
|
* @public
|
|
324
|
-
* @param {
|
|
325
|
-
* @returns {
|
|
660
|
+
* @param {JsonvSyntaxElement} node - An AST node.
|
|
661
|
+
* @returns {JsonvNode|undefined} The parent node.
|
|
326
662
|
*/
|
|
327
663
|
getParent(node) {
|
|
328
664
|
this.#ensureTraversal();
|
|
@@ -333,7 +669,7 @@ class JsonvSourceCode extends TextSourceCodeBase {
|
|
|
333
669
|
* Returns the comments that hold inline configuration (`eslint`, `eslint-enable`,
|
|
334
670
|
* `eslint-disable`, `eslint-disable-line`, `eslint-disable-next-line`).
|
|
335
671
|
* @public
|
|
336
|
-
* @returns {
|
|
672
|
+
* @returns {JsonvComment[]} The inline configuration comments.
|
|
337
673
|
*/
|
|
338
674
|
getInlineConfigNodes() {
|
|
339
675
|
if (!this.#inlineConfigComments) {
|
|
@@ -346,14 +682,20 @@ class JsonvSourceCode extends TextSourceCodeBase {
|
|
|
346
682
|
* Returns the `eslint-disable*` / `eslint-enable` directives, plus problems for malformed
|
|
347
683
|
* ones (an `eslint-disable-line` spanning several lines).
|
|
348
684
|
* @public
|
|
349
|
-
* @returns {{problems:
|
|
685
|
+
* @returns {{problems: FileProblem[], directives: Directive[]}} Directives and problems.
|
|
350
686
|
*/
|
|
351
687
|
getDisableDirectives() {
|
|
688
|
+
/** @type {FileProblem[]} */
|
|
352
689
|
const problems = [];
|
|
690
|
+
/** @type {Directive[]} */
|
|
353
691
|
const directives = [];
|
|
354
692
|
|
|
355
693
|
for (const comment of this.getInlineConfigNodes()) {
|
|
356
|
-
|
|
694
|
+
// parseDirective() returns undefined only for text that doesn't start with a directive label;
|
|
695
|
+
// every comment here matched INLINE_CONFIG, which requires one.
|
|
696
|
+
const { label, value, justification } = /** @type {NonNullable<ReturnType<ConfigCommentParser["parseDirective"]>>} */ (
|
|
697
|
+
commentParser.parseDirective(comment.value)
|
|
698
|
+
);
|
|
357
699
|
|
|
358
700
|
if (label === "eslint-disable-line" && comment.loc.start.line !== comment.loc.end.line) {
|
|
359
701
|
problems.push({ ruleId: null, message: `${label} comment should not span multiple lines.`, loc: comment.loc });
|
|
@@ -361,7 +703,10 @@ class JsonvSourceCode extends TextSourceCodeBase {
|
|
|
361
703
|
}
|
|
362
704
|
|
|
363
705
|
if (label !== "eslint") {
|
|
364
|
-
|
|
706
|
+
// The comment matched INLINE_CONFIG, so any label other than "eslint" is an
|
|
707
|
+
// `eslint-disable*` / `eslint-enable` label, i.e. a DirectiveType once unprefixed.
|
|
708
|
+
const type = /** @type {DirectiveType} */ (label.slice("eslint-".length));
|
|
709
|
+
directives.push(new Directive({ type, node: comment, value, justification }));
|
|
365
710
|
}
|
|
366
711
|
}
|
|
367
712
|
|
|
@@ -372,14 +717,19 @@ class JsonvSourceCode extends TextSourceCodeBase {
|
|
|
372
717
|
* Returns the rule configurations from `/* eslint rule: ... *\/` comments, plus problems for
|
|
373
718
|
* ones that fail to parse.
|
|
374
719
|
* @public
|
|
375
|
-
* @returns {{problems:
|
|
720
|
+
* @returns {{problems: FileProblem[], configs: Array<{config: {rules: RulesConfig}, loc: SourceLocation}>}} Configs and problems.
|
|
376
721
|
*/
|
|
377
722
|
applyInlineConfig() {
|
|
723
|
+
/** @type {FileProblem[]} */
|
|
378
724
|
const problems = [];
|
|
725
|
+
/** @type {Array<{config: {rules: RulesConfig}, loc: SourceLocation}>} */
|
|
379
726
|
const configs = [];
|
|
380
727
|
|
|
381
728
|
for (const comment of this.getInlineConfigNodes()) {
|
|
382
|
-
|
|
729
|
+
// Defined for the same reason as in getDisableDirectives().
|
|
730
|
+
const { label, value } = /** @type {NonNullable<ReturnType<ConfigCommentParser["parseDirective"]>>} */ (
|
|
731
|
+
commentParser.parseDirective(comment.value)
|
|
732
|
+
);
|
|
383
733
|
if (label !== "eslint") continue;
|
|
384
734
|
|
|
385
735
|
const parseResult = commentParser.parseJSONLikeConfig(value);
|
|
@@ -398,7 +748,7 @@ class JsonvSourceCode extends TextSourceCodeBase {
|
|
|
398
748
|
* Language definition for jsonv files
|
|
399
749
|
*
|
|
400
750
|
* @public
|
|
401
|
-
* @type {
|
|
751
|
+
* @type {JsonvLanguage}
|
|
402
752
|
*/
|
|
403
753
|
const jsonvLanguage = {
|
|
404
754
|
/** File type - must be "text" for text-based languages */
|
|
@@ -409,28 +759,38 @@ const jsonvLanguage = {
|
|
|
409
759
|
columnStart: 1,
|
|
410
760
|
/** Property name for node types */
|
|
411
761
|
nodeTypeKey: "type",
|
|
412
|
-
/**
|
|
413
|
-
|
|
762
|
+
/**
|
|
763
|
+
* Child keys of each AST node type, used for selector matching. `Language#visitorKeys` is typed
|
|
764
|
+
* with mutable arrays; ESLint only reads them, and VISITOR_KEYS stays frozen at runtime.
|
|
765
|
+
*/
|
|
766
|
+
visitorKeys: /** @type {Record<string, string[]>} */ (VISITOR_KEYS),
|
|
414
767
|
|
|
415
768
|
/**
|
|
416
769
|
* Parse jsonv source code - ESLint Language API.
|
|
417
770
|
*
|
|
418
771
|
* The file is first parsed and evaluated with `parseWithOptions` — that is what reports
|
|
419
|
-
* syntax errors, year-gated features, `strictBigInt`
|
|
420
|
-
* internal references
|
|
421
|
-
* with
|
|
772
|
+
* syntax errors, year-gated features, `mode`, `strictBigInt` and `strictOctal` violations
|
|
773
|
+
* and unresolved/circular internal references (unless `allowInternalReferences` is `false`),
|
|
774
|
+
* each with its source position. A file that passes is then parsed with `parseToAst` to
|
|
775
|
+
* build the positioned AST, comments and tokens ESLint traverses. Both calls receive every
|
|
776
|
+
* language option, with the defaults filled in.
|
|
422
777
|
*
|
|
423
|
-
* @param {
|
|
424
|
-
* @param {
|
|
425
|
-
* @returns {
|
|
778
|
+
* @param {File} file - File object with body property containing source text
|
|
779
|
+
* @param {{languageOptions: JsonvLanguageOptions}} context - Context with languageOptions
|
|
780
|
+
* @returns {JsonvParseResult} Parse result {ok, ast, jsonvValue} or {ok: false, errors}
|
|
426
781
|
*/
|
|
427
782
|
parse(file, context) {
|
|
428
|
-
|
|
783
|
+
// `fileType: "text"` means ESLint reads the file as text, so `body` is a string.
|
|
784
|
+
const text = /** @type {string} */ (file.body);
|
|
785
|
+
/** @type {JsonvLanguageOptions} */
|
|
429
786
|
const options = context.languageOptions || {};
|
|
430
787
|
const parseOptions = {
|
|
431
|
-
year
|
|
432
|
-
|
|
433
|
-
mode:
|
|
788
|
+
// `||`, not `??`: a falsy year (e.g. `0` from a direct `parse()` call) has always meant the default.
|
|
789
|
+
year: options.year || DEFAULT_LANGUAGE_OPTIONS.year,
|
|
790
|
+
mode: options.mode ?? DEFAULT_LANGUAGE_OPTIONS.mode,
|
|
791
|
+
strictBigInt: options.strictBigInt ?? DEFAULT_LANGUAGE_OPTIONS.strictBigInt,
|
|
792
|
+
strictOctal: options.strictOctal ?? DEFAULT_LANGUAGE_OPTIONS.strictOctal,
|
|
793
|
+
allowInternalReferences: options.allowInternalReferences ?? DEFAULT_LANGUAGE_OPTIONS.allowInternalReferences
|
|
434
794
|
};
|
|
435
795
|
|
|
436
796
|
try {
|
|
@@ -454,9 +814,18 @@ const jsonvLanguage = {
|
|
|
454
814
|
// nesting) has no position — fall back to line 1, column 1.
|
|
455
815
|
const hasPosition = error instanceof JsonvSyntaxError || error instanceof JsonvReferenceError;
|
|
456
816
|
|
|
817
|
+
// Everything the parser and buildProgram() throw is an Error instance (the RangeError
|
|
818
|
+
// above included), hence the cast in the fallback branch.
|
|
819
|
+
/** @type {FileError} */
|
|
457
820
|
const errorInfo = hasPosition
|
|
458
|
-
? {
|
|
459
|
-
|
|
821
|
+
? {
|
|
822
|
+
message: error.message,
|
|
823
|
+
line: error.line,
|
|
824
|
+
column: error.column + 1,
|
|
825
|
+
endLine: error.loc.end.line,
|
|
826
|
+
endColumn: error.loc.end.column + 1
|
|
827
|
+
}
|
|
828
|
+
: { message: /** @type {Error} */ (error).message, line: 1, column: 1 };
|
|
460
829
|
|
|
461
830
|
return {
|
|
462
831
|
ok: false,
|
|
@@ -469,8 +838,8 @@ const jsonvLanguage = {
|
|
|
469
838
|
* Create the SourceCode object ESLint hands to rules
|
|
470
839
|
*
|
|
471
840
|
* @public
|
|
472
|
-
* @param {
|
|
473
|
-
* @param {
|
|
841
|
+
* @param {File} file - The file object
|
|
842
|
+
* @param {JsonvOkParseResult} parseResult - The successful result from parse()
|
|
474
843
|
* @returns {JsonvSourceCode} SourceCode object
|
|
475
844
|
*/
|
|
476
845
|
createSourceCode(file, parseResult) {
|
|
@@ -485,16 +854,21 @@ const jsonvLanguage = {
|
|
|
485
854
|
* reflects the effective options that will reach `parse()`.
|
|
486
855
|
*
|
|
487
856
|
* @public
|
|
488
|
-
* @param {
|
|
857
|
+
* @param {JsonvLanguageOptions} languageOptions - The (already-merged) language options to validate
|
|
489
858
|
* @returns {void}
|
|
490
859
|
* @throws {TypeError} When an unsupported key is present, or a supported key holds an invalid value
|
|
491
860
|
*/
|
|
492
861
|
validateLanguageOptions(languageOptions) {
|
|
493
862
|
const options = languageOptions ?? {};
|
|
494
863
|
|
|
864
|
+
const supported = `Supported options are: ${[...SUPPORTED_LANGUAGE_OPTIONS].join(", ")}.`;
|
|
865
|
+
|
|
495
866
|
for (const key of Object.keys(options)) {
|
|
867
|
+
if (Object.hasOwn(UNSUPPORTED_JSONV_OPTIONS, key)) {
|
|
868
|
+
throw new TypeError(`Language option "${key}" is not supported: ${UNSUPPORTED_JSONV_OPTIONS[key]}. ${supported}`);
|
|
869
|
+
}
|
|
496
870
|
if (!SUPPORTED_LANGUAGE_OPTIONS.has(key)) {
|
|
497
|
-
throw new TypeError(`Unknown language option "${key}".
|
|
871
|
+
throw new TypeError(`Unknown language option "${key}". ${supported}`);
|
|
498
872
|
}
|
|
499
873
|
}
|
|
500
874
|
|
|
@@ -504,8 +878,16 @@ const jsonvLanguage = {
|
|
|
504
878
|
);
|
|
505
879
|
}
|
|
506
880
|
|
|
507
|
-
if (options.
|
|
508
|
-
throw new TypeError(
|
|
881
|
+
if (options.mode !== undefined && !VALID_MODES.has(options.mode)) {
|
|
882
|
+
throw new TypeError(
|
|
883
|
+
`Invalid "mode" language option: ${JSON.stringify(options.mode)}. Supported modes are: ${[...VALID_MODES].join(", ")}.`
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
for (const key of BOOLEAN_LANGUAGE_OPTIONS) {
|
|
888
|
+
if (options[key] !== undefined && typeof options[key] !== "boolean") {
|
|
889
|
+
throw new TypeError(`Invalid "${key}" language option: expected a boolean, got ${typeof options[key]}.`);
|
|
890
|
+
}
|
|
509
891
|
}
|
|
510
892
|
},
|
|
511
893
|
|
|
@@ -516,12 +898,9 @@ const jsonvLanguage = {
|
|
|
516
898
|
* calling `validateLanguageOptions` and `parse()`.
|
|
517
899
|
*
|
|
518
900
|
* @public
|
|
519
|
-
* @type {
|
|
901
|
+
* @type {JsonvLanguageOptions}
|
|
520
902
|
*/
|
|
521
|
-
defaultLanguageOptions: {
|
|
522
|
-
year: 2025,
|
|
523
|
-
strictBigInt: false
|
|
524
|
-
}
|
|
903
|
+
defaultLanguageOptions: { ...DEFAULT_LANGUAGE_OPTIONS }
|
|
525
904
|
};
|
|
526
905
|
|
|
527
906
|
/**
|
|
@@ -532,7 +911,7 @@ const jsonvLanguage = {
|
|
|
532
911
|
* `language` such as `jsonv/jsonv` never consults it, so setting it here was inert.
|
|
533
912
|
*
|
|
534
913
|
* @public
|
|
535
|
-
* @
|
|
914
|
+
* @satisfies {ConfigObject}
|
|
536
915
|
*/
|
|
537
916
|
const recommendedConfig = {
|
|
538
917
|
rules: {
|
|
@@ -542,10 +921,11 @@ const recommendedConfig = {
|
|
|
542
921
|
};
|
|
543
922
|
|
|
544
923
|
/**
|
|
545
|
-
* ESLint plugin export
|
|
924
|
+
* ESLint plugin export. `@satisfies` checks it against @eslint/core's `Plugin` (which ESLint's
|
|
925
|
+
* own `ESLint.Plugin` is) while keeping its literal keys (`languages.jsonv`, `configs.recommended`).
|
|
546
926
|
*
|
|
547
927
|
* @public
|
|
548
|
-
* @
|
|
928
|
+
* @satisfies {Plugin}
|
|
549
929
|
*/
|
|
550
930
|
const plugin = {
|
|
551
931
|
meta: readPackageMeta(),
|
|
@@ -566,3 +946,4 @@ const plugin = {
|
|
|
566
946
|
};
|
|
567
947
|
|
|
568
948
|
export default plugin;
|
|
949
|
+
export { JsonvSourceCode };
|