@eslint/json 0.9.1 → 0.11.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 +4 -3
- package/dist/cjs/index.cjs +334 -30
- package/dist/cjs/index.d.cts +39 -99
- package/dist/cjs/types.cts +138 -0
- package/dist/esm/index.d.ts +39 -99
- package/dist/esm/index.js +334 -30
- package/dist/esm/types.d.ts +91 -0
- package/dist/esm/types.ts +138 -0
- package/package.json +22 -12
package/README.md
CHANGED
|
@@ -161,6 +161,7 @@ export default [
|
|
|
161
161
|
like integers but are too large, and
|
|
162
162
|
[subnormal numbers](https://en.wikipedia.org/wiki/Subnormal_number).
|
|
163
163
|
- `no-unnormalized-keys` - warns on keys containing [unnormalized characters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize#description). You can optionally specify the normalization form via `{ form: "form_name" }`, where `form_name` can be any of `"NFC"`, `"NFD"`, `"NFKC"`, or `"NFKD"`.
|
|
164
|
+
- `sort-keys` - warns when keys are not in the specified order. Based on the ESLint [`sort-keys`](https://eslint.org/docs/latest/rules/sort-keys) rule.
|
|
164
165
|
- `top-level-interop` - warns when the top-level item in the document is neither an array nor an object. This can be enabled to ensure maximal interoperability with the oldest JSON parsers.
|
|
165
166
|
|
|
166
167
|
## Configuration Comments
|
|
@@ -255,9 +256,9 @@ to get your logo on our READMEs and [website](https://eslint.org/sponsors).
|
|
|
255
256
|
|
|
256
257
|
<h3>Platinum Sponsors</h3>
|
|
257
258
|
<p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="128"></a></p><h3>Gold Sponsors</h3>
|
|
258
|
-
<p><a href="https://trunk.io/"><img src="https://images.opencollective.com/trunkio/fb92d60/avatar.png" alt="trunk.io" height="96"></a></p><h3>Silver Sponsors</h3>
|
|
259
|
-
<p><a href="https://
|
|
260
|
-
<p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104" alt="Nx" height="32"></a> <a href="https://opensource.mercedes-benz.com/"><img src="https://avatars.githubusercontent.com/u/34240465" alt="Mercedes-Benz Group" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a></p>
|
|
259
|
+
<p><a href="https://qlty.sh/"><img src="https://images.opencollective.com/qltysh/33d157d/logo.png" alt="Qlty Software" height="96"></a> <a href="https://trunk.io/"><img src="https://images.opencollective.com/trunkio/fb92d60/avatar.png" alt="trunk.io" height="96"></a> <a href="https://shopify.engineering/"><img src="https://avatars.githubusercontent.com/u/8085" alt="Shopify" height="96"></a></p><h3>Silver Sponsors</h3>
|
|
260
|
+
<p><a href="https://vite.dev/"><img src="https://images.opencollective.com/vite/e6d15e1/logo.png" alt="Vite" height="64"></a> <a href="https://www.jetbrains.com/"><img src="https://images.opencollective.com/jetbrains/fe76f99/logo.png" alt="JetBrains" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://stackblitz.com"><img src="https://avatars.githubusercontent.com/u/28635252" alt="StackBlitz" height="64"></a></p><h3>Bronze Sponsors</h3>
|
|
261
|
+
<p><a href="https://cybozu.co.jp/"><img src="https://images.opencollective.com/cybozu/933e46d/logo.png" alt="Cybozu" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.gitbook.com"><img src="https://avatars.githubusercontent.com/u/7111340" alt="GitBook" height="32"></a> <a href="https://nolebase.ayaka.io"><img src="https://avatars.githubusercontent.com/u/11081491" alt="Neko" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104" alt="Nx" height="32"></a> <a href="https://opensource.mercedes-benz.com/"><img src="https://avatars.githubusercontent.com/u/34240465" alt="Mercedes-Benz Group" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774" alt="HeroCoders" height="32"></a> <a href="https://www.lambdatest.com"><img src="https://avatars.githubusercontent.com/u/171592363" alt="LambdaTest" height="32"></a></p>
|
|
261
262
|
<h3>Technology Sponsors</h3>
|
|
262
263
|
Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.
|
|
263
264
|
<p><a href="https://netlify.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/netlify-icon.svg" alt="Netlify" height="32"></a> <a href="https://algolia.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/algolia-icon.svg" alt="Algolia" height="32"></a> <a href="https://1password.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/1password-icon.svg" alt="1Password" height="32"></a></p>
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var momoa = require('@humanwhocodes/momoa');
|
|
6
6
|
var pluginKit = require('@eslint/plugin-kit');
|
|
7
|
+
var naturalCompare = require('natural-compare');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* @fileoverview The JSONSourceCode class.
|
|
@@ -22,11 +23,12 @@ var pluginKit = require('@eslint/plugin-kit');
|
|
|
22
23
|
/** @typedef {import("@eslint/core").SourceLocation} SourceLocation */
|
|
23
24
|
/** @typedef {import("@eslint/core").File} File */
|
|
24
25
|
/** @typedef {import("@eslint/core").TraversalStep} TraversalStep */
|
|
25
|
-
/** @typedef {import("@eslint/core").TextSourceCode} TextSourceCode */
|
|
26
26
|
/** @typedef {import("@eslint/core").VisitTraversalStep} VisitTraversalStep */
|
|
27
27
|
/** @typedef {import("@eslint/core").FileProblem} FileProblem */
|
|
28
28
|
/** @typedef {import("@eslint/core").DirectiveType} DirectiveType */
|
|
29
29
|
/** @typedef {import("@eslint/core").RulesConfig} RulesConfig */
|
|
30
|
+
/** @typedef {import("./types.ts").IJSONSourceCode} IJSONSourceCode */
|
|
31
|
+
/** @typedef {import("./types.ts").JSONSyntaxElement} JSONSyntaxElement */
|
|
30
32
|
|
|
31
33
|
//-----------------------------------------------------------------------------
|
|
32
34
|
// Helpers
|
|
@@ -67,6 +69,7 @@ class JSONTraversalStep extends pluginKit.VisitNodeStep {
|
|
|
67
69
|
|
|
68
70
|
/**
|
|
69
71
|
* JSON Source Code Object
|
|
72
|
+
* @implements {IJSONSourceCode}
|
|
70
73
|
*/
|
|
71
74
|
class JSONSourceCode extends pluginKit.TextSourceCodeBase {
|
|
72
75
|
/**
|
|
@@ -143,7 +146,7 @@ class JSONSourceCode extends pluginKit.TextSourceCodeBase {
|
|
|
143
146
|
);
|
|
144
147
|
}
|
|
145
148
|
|
|
146
|
-
return this.#inlineConfigComments;
|
|
149
|
+
return this.#inlineConfigComments ?? [];
|
|
147
150
|
}
|
|
148
151
|
|
|
149
152
|
/**
|
|
@@ -296,10 +299,8 @@ class JSONSourceCode extends pluginKit.TextSourceCodeBase {
|
|
|
296
299
|
/** @typedef {import("@eslint/core").Language} Language */
|
|
297
300
|
/** @typedef {import("@eslint/core").OkParseResult<DocumentNode>} OkParseResult */
|
|
298
301
|
/** @typedef {import("@eslint/core").ParseResult<DocumentNode>} ParseResult */
|
|
299
|
-
/**
|
|
300
|
-
|
|
301
|
-
* @property {boolean} [allowTrailingCommas] Whether to allow trailing commas.
|
|
302
|
-
*/
|
|
302
|
+
/** @typedef {import("./types.ts").IJSONLanguage} IJSONLanguage */
|
|
303
|
+
/** @typedef {import("./types.ts").JSONLanguageOptions} JSONLanguageOptions */
|
|
303
304
|
|
|
304
305
|
//-----------------------------------------------------------------------------
|
|
305
306
|
// Exports
|
|
@@ -307,7 +308,7 @@ class JSONSourceCode extends pluginKit.TextSourceCodeBase {
|
|
|
307
308
|
|
|
308
309
|
/**
|
|
309
310
|
* JSON Language Object
|
|
310
|
-
* @implements {
|
|
311
|
+
* @implements {IJSONLanguage}
|
|
311
312
|
*/
|
|
312
313
|
class JSONLanguage {
|
|
313
314
|
/**
|
|
@@ -449,13 +450,22 @@ class JSONLanguage {
|
|
|
449
450
|
* @author Nicholas C. Zakas
|
|
450
451
|
*/
|
|
451
452
|
|
|
453
|
+
//-----------------------------------------------------------------------------
|
|
454
|
+
// Type Definitions
|
|
455
|
+
//-----------------------------------------------------------------------------
|
|
456
|
+
|
|
457
|
+
/** @typedef {"duplicateKey"} NoDuplicateKeysMessageIds */
|
|
458
|
+
/** @typedef {import("./types.ts").JSONRuleDefinition<[], NoDuplicateKeysMessageIds>} NoDuplicateKeysRuleDefinition */
|
|
459
|
+
/** @typedef {import("@humanwhocodes/momoa").MemberNode} MemberNode */
|
|
460
|
+
|
|
452
461
|
//-----------------------------------------------------------------------------
|
|
453
462
|
// Rule Definition
|
|
454
463
|
//-----------------------------------------------------------------------------
|
|
455
464
|
|
|
456
|
-
|
|
465
|
+
/** @type {NoDuplicateKeysRuleDefinition} */
|
|
466
|
+
const rule$5 = {
|
|
457
467
|
meta: {
|
|
458
|
-
type:
|
|
468
|
+
type: "problem",
|
|
459
469
|
|
|
460
470
|
docs: {
|
|
461
471
|
description: "Disallow duplicate keys in JSON objects",
|
|
@@ -467,7 +477,10 @@ var noDuplicateKeys = {
|
|
|
467
477
|
},
|
|
468
478
|
|
|
469
479
|
create(context) {
|
|
480
|
+
/** @type {Array<Map<string, MemberNode>|undefined>} */
|
|
470
481
|
const objectKeys = [];
|
|
482
|
+
|
|
483
|
+
/** @type {Map<string, MemberNode>|undefined} */
|
|
471
484
|
let keys;
|
|
472
485
|
|
|
473
486
|
return {
|
|
@@ -506,9 +519,21 @@ var noDuplicateKeys = {
|
|
|
506
519
|
* @author Nicholas C. Zakas
|
|
507
520
|
*/
|
|
508
521
|
|
|
509
|
-
|
|
522
|
+
//-----------------------------------------------------------------------------
|
|
523
|
+
// Type Definitions
|
|
524
|
+
//-----------------------------------------------------------------------------
|
|
525
|
+
|
|
526
|
+
/** @typedef {"emptyKey"} NoEmptyKeysMessageIds */
|
|
527
|
+
/** @typedef {import("./types.ts").JSONRuleDefinition<[], NoEmptyKeysMessageIds>} NoEmptyKeysRuleDefinition */
|
|
528
|
+
|
|
529
|
+
//-----------------------------------------------------------------------------
|
|
530
|
+
// Rule Definition
|
|
531
|
+
//-----------------------------------------------------------------------------
|
|
532
|
+
|
|
533
|
+
/** @type {NoEmptyKeysRuleDefinition} */
|
|
534
|
+
const rule$4 = {
|
|
510
535
|
meta: {
|
|
511
|
-
type:
|
|
536
|
+
type: "problem",
|
|
512
537
|
|
|
513
538
|
docs: {
|
|
514
539
|
description: "Disallow empty keys in JSON objects",
|
|
@@ -543,15 +568,36 @@ var noEmptyKeys = {
|
|
|
543
568
|
* @author Bradley Meck Farias
|
|
544
569
|
*/
|
|
545
570
|
|
|
546
|
-
|
|
547
|
-
//
|
|
571
|
+
//-----------------------------------------------------------------------------
|
|
572
|
+
// Type Definitions
|
|
573
|
+
//-----------------------------------------------------------------------------
|
|
574
|
+
|
|
575
|
+
/** @typedef {"unsafeNumber"|"unsafeInteger"|"unsafeZero"|"subnormal"|"loneSurrogate"} NoUnsafeValuesMessageIds */
|
|
576
|
+
/** @typedef {import("./types.ts").JSONRuleDefinition<[], NoUnsafeValuesMessageIds>} NoUnsafeValuesRuleDefinition */
|
|
577
|
+
|
|
578
|
+
//-----------------------------------------------------------------------------
|
|
579
|
+
// Helpers
|
|
580
|
+
//-----------------------------------------------------------------------------
|
|
581
|
+
|
|
582
|
+
/*
|
|
583
|
+
* This rule is based on the JSON grammar from RFC 8259, section 6.
|
|
584
|
+
* https://tools.ietf.org/html/rfc8259#section-6
|
|
585
|
+
*
|
|
586
|
+
* We separately capture the integer and fractional parts of a number, so that
|
|
587
|
+
* we can check for unsafe numbers that will evaluate to Infinity.
|
|
588
|
+
*/
|
|
548
589
|
const NUMBER =
|
|
549
590
|
/^-?(?<int>0|([1-9][0-9]*))(?:\.(?<frac>[0-9]+))?(?:[eE][+-]?[0-9]+)?$/u;
|
|
550
591
|
const NON_ZERO = /[1-9]/u;
|
|
551
592
|
|
|
552
|
-
|
|
593
|
+
//-----------------------------------------------------------------------------
|
|
594
|
+
// Rule Definition
|
|
595
|
+
//-----------------------------------------------------------------------------
|
|
596
|
+
|
|
597
|
+
/** @type {NoUnsafeValuesRuleDefinition} */
|
|
598
|
+
const rule$3 = {
|
|
553
599
|
meta: {
|
|
554
|
-
type:
|
|
600
|
+
type: "problem",
|
|
555
601
|
|
|
556
602
|
docs: {
|
|
557
603
|
description: "Disallow JSON values that are unsafe for interchange",
|
|
@@ -625,7 +671,9 @@ var noUnsafeValues = {
|
|
|
625
671
|
loc: node.loc,
|
|
626
672
|
messageId: "subnormal",
|
|
627
673
|
// Value included so that it's seen in scientific notation
|
|
628
|
-
data:
|
|
674
|
+
data: {
|
|
675
|
+
value,
|
|
676
|
+
},
|
|
629
677
|
});
|
|
630
678
|
}
|
|
631
679
|
}
|
|
@@ -668,9 +716,21 @@ var noUnsafeValues = {
|
|
|
668
716
|
* @author Bradley Meck Farias
|
|
669
717
|
*/
|
|
670
718
|
|
|
671
|
-
|
|
719
|
+
//-----------------------------------------------------------------------------
|
|
720
|
+
// Type Definitions
|
|
721
|
+
//-----------------------------------------------------------------------------
|
|
722
|
+
|
|
723
|
+
/** @typedef {"unnormalizedKey"} NoUnnormalizedKeysMessageIds */
|
|
724
|
+
/** @typedef {import("./types.ts").JSONRuleDefinition<[{form:string}], NoUnnormalizedKeysMessageIds>} NoUnnormalizedKeysRuleDefinition */
|
|
725
|
+
|
|
726
|
+
//-----------------------------------------------------------------------------
|
|
727
|
+
// Rule Definition
|
|
728
|
+
//-----------------------------------------------------------------------------
|
|
729
|
+
|
|
730
|
+
/** @type {NoUnnormalizedKeysRuleDefinition} */
|
|
731
|
+
const rule$2 = {
|
|
672
732
|
meta: {
|
|
673
|
-
type:
|
|
733
|
+
type: "problem",
|
|
674
734
|
|
|
675
735
|
docs: {
|
|
676
736
|
description: "Disallow JSON keys that are not normalized",
|
|
@@ -694,9 +754,10 @@ var noUnnormalizedKeys = {
|
|
|
694
754
|
},
|
|
695
755
|
|
|
696
756
|
create(context) {
|
|
697
|
-
const
|
|
698
|
-
?
|
|
699
|
-
:
|
|
757
|
+
const form = context.options.length
|
|
758
|
+
? context.options[0].form
|
|
759
|
+
: undefined;
|
|
760
|
+
|
|
700
761
|
return {
|
|
701
762
|
Member(node) {
|
|
702
763
|
const key =
|
|
@@ -704,7 +765,7 @@ var noUnnormalizedKeys = {
|
|
|
704
765
|
? node.name.value
|
|
705
766
|
: node.name.name;
|
|
706
767
|
|
|
707
|
-
if (
|
|
768
|
+
if (key.normalize(form) !== key) {
|
|
708
769
|
context.report({
|
|
709
770
|
loc: node.name.loc,
|
|
710
771
|
messageId: "unnormalizedKey",
|
|
@@ -718,14 +779,256 @@ var noUnnormalizedKeys = {
|
|
|
718
779
|
},
|
|
719
780
|
};
|
|
720
781
|
|
|
782
|
+
/**
|
|
783
|
+
* @fileoverview Rule to require JSON object keys to be sorted.
|
|
784
|
+
* Copied largely from https://github.com/eslint/eslint/blob/main/lib/rules/sort-keys.js
|
|
785
|
+
* @author Robin Thomas
|
|
786
|
+
*/
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
//-----------------------------------------------------------------------------
|
|
790
|
+
// Type Definitions
|
|
791
|
+
//-----------------------------------------------------------------------------
|
|
792
|
+
|
|
793
|
+
/** @typedef {"sortKeys"} SortKeysMessageIds */
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* @typedef {Object} SortOptions
|
|
797
|
+
* @property {boolean} caseSensitive
|
|
798
|
+
* @property {boolean} natural
|
|
799
|
+
* @property {number} minKeys
|
|
800
|
+
* @property {boolean} allowLineSeparatedGroups
|
|
801
|
+
*/
|
|
802
|
+
|
|
803
|
+
/** @typedef {"asc"|"desc"} SortDirection */
|
|
804
|
+
/** @typedef {[SortDirection, SortOptions]} SortKeysRuleOptions */
|
|
805
|
+
/** @typedef {import("./types.ts").JSONRuleDefinition<SortKeysRuleOptions, SortKeysMessageIds>} SortKeysRuleDefinition */
|
|
806
|
+
/** @typedef {(a:string,b:string) => boolean} Comparator */
|
|
807
|
+
|
|
808
|
+
//-----------------------------------------------------------------------------
|
|
809
|
+
// Helpers
|
|
810
|
+
//-----------------------------------------------------------------------------
|
|
811
|
+
|
|
812
|
+
const hasNonWhitespace = /\S/u;
|
|
813
|
+
|
|
814
|
+
const comparators = {
|
|
815
|
+
ascending: {
|
|
816
|
+
alphanumeric: {
|
|
817
|
+
/** @type {Comparator} */
|
|
818
|
+
sensitive: (a, b) => a <= b,
|
|
819
|
+
|
|
820
|
+
/** @type {Comparator} */
|
|
821
|
+
insensitive: (a, b) => a.toLowerCase() <= b.toLowerCase(),
|
|
822
|
+
},
|
|
823
|
+
natural: {
|
|
824
|
+
/** @type {Comparator} */
|
|
825
|
+
sensitive: (a, b) => naturalCompare(a, b) <= 0,
|
|
826
|
+
|
|
827
|
+
/** @type {Comparator} */
|
|
828
|
+
insensitive: (a, b) =>
|
|
829
|
+
naturalCompare(a.toLowerCase(), b.toLowerCase()) <= 0,
|
|
830
|
+
},
|
|
831
|
+
},
|
|
832
|
+
descending: {
|
|
833
|
+
alphanumeric: {
|
|
834
|
+
/** @type {Comparator} */
|
|
835
|
+
sensitive: (a, b) =>
|
|
836
|
+
comparators.ascending.alphanumeric.sensitive(b, a),
|
|
837
|
+
|
|
838
|
+
/** @type {Comparator} */
|
|
839
|
+
insensitive: (a, b) =>
|
|
840
|
+
comparators.ascending.alphanumeric.insensitive(b, a),
|
|
841
|
+
},
|
|
842
|
+
natural: {
|
|
843
|
+
/** @type {Comparator} */
|
|
844
|
+
sensitive: (a, b) => comparators.ascending.natural.sensitive(b, a),
|
|
845
|
+
|
|
846
|
+
/** @type {Comparator} */
|
|
847
|
+
insensitive: (a, b) =>
|
|
848
|
+
comparators.ascending.natural.insensitive(b, a),
|
|
849
|
+
},
|
|
850
|
+
},
|
|
851
|
+
};
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* Gets the MemberNode's string key value.
|
|
855
|
+
* @param {MemberNode} member
|
|
856
|
+
* @return {string}
|
|
857
|
+
*/
|
|
858
|
+
function getKey(member) {
|
|
859
|
+
return member.name.type === "Identifier"
|
|
860
|
+
? member.name.name
|
|
861
|
+
: member.name.value;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
//-----------------------------------------------------------------------------
|
|
865
|
+
// Rule Definition
|
|
866
|
+
//-----------------------------------------------------------------------------
|
|
867
|
+
|
|
868
|
+
/** @type {SortKeysRuleDefinition} */
|
|
869
|
+
const rule$1 = {
|
|
870
|
+
meta: {
|
|
871
|
+
type: "suggestion",
|
|
872
|
+
|
|
873
|
+
defaultOptions: [
|
|
874
|
+
"asc",
|
|
875
|
+
{
|
|
876
|
+
allowLineSeparatedGroups: false,
|
|
877
|
+
caseSensitive: true,
|
|
878
|
+
minKeys: 2,
|
|
879
|
+
natural: false,
|
|
880
|
+
},
|
|
881
|
+
],
|
|
882
|
+
|
|
883
|
+
docs: {
|
|
884
|
+
description: `Require JSON object keys to be sorted`,
|
|
885
|
+
},
|
|
886
|
+
|
|
887
|
+
messages: {
|
|
888
|
+
sortKeys:
|
|
889
|
+
"Expected object keys to be in {{sortName}} case-{{sensitivity}} {{direction}} order. '{{thisName}}' should be before '{{prevName}}'.",
|
|
890
|
+
},
|
|
891
|
+
|
|
892
|
+
schema: [
|
|
893
|
+
{
|
|
894
|
+
enum: ["asc", "desc"],
|
|
895
|
+
},
|
|
896
|
+
{
|
|
897
|
+
type: "object",
|
|
898
|
+
properties: {
|
|
899
|
+
caseSensitive: {
|
|
900
|
+
type: "boolean",
|
|
901
|
+
},
|
|
902
|
+
natural: {
|
|
903
|
+
type: "boolean",
|
|
904
|
+
},
|
|
905
|
+
minKeys: {
|
|
906
|
+
type: "integer",
|
|
907
|
+
minimum: 2,
|
|
908
|
+
},
|
|
909
|
+
allowLineSeparatedGroups: {
|
|
910
|
+
type: "boolean",
|
|
911
|
+
},
|
|
912
|
+
},
|
|
913
|
+
additionalProperties: false,
|
|
914
|
+
},
|
|
915
|
+
],
|
|
916
|
+
},
|
|
917
|
+
|
|
918
|
+
create(context) {
|
|
919
|
+
const [
|
|
920
|
+
directionShort,
|
|
921
|
+
{ allowLineSeparatedGroups, caseSensitive, natural, minKeys },
|
|
922
|
+
] = context.options;
|
|
923
|
+
|
|
924
|
+
const direction = directionShort === "asc" ? "ascending" : "descending";
|
|
925
|
+
const sortName = natural ? "natural" : "alphanumeric";
|
|
926
|
+
const sensitivity = caseSensitive ? "sensitive" : "insensitive";
|
|
927
|
+
const isValidOrder = comparators[direction][sortName][sensitivity];
|
|
928
|
+
|
|
929
|
+
// Note that @humanwhocodes/momoa doesn't include comments in the object.members tree, so we can't just see if a member is preceded by a comment
|
|
930
|
+
const commentLineNums = new Set();
|
|
931
|
+
for (const comment of context.sourceCode.comments) {
|
|
932
|
+
for (
|
|
933
|
+
let lineNum = comment.loc.start.line;
|
|
934
|
+
lineNum <= comment.loc.end.line;
|
|
935
|
+
lineNum += 1
|
|
936
|
+
) {
|
|
937
|
+
commentLineNums.add(lineNum);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* Checks if two members are line-separated.
|
|
943
|
+
* @param {MemberNode} prevMember The previous member.
|
|
944
|
+
* @param {MemberNode} member The current member.
|
|
945
|
+
* @return {boolean}
|
|
946
|
+
*/
|
|
947
|
+
function isLineSeparated(prevMember, member) {
|
|
948
|
+
// Note that there can be comments *inside* members, e.g. `{"foo: /* comment *\/ "bar"}`, but these are ignored when calculating line-separated groups
|
|
949
|
+
const prevMemberEndLine = prevMember.loc.end.line;
|
|
950
|
+
const thisStartLine = member.loc.start.line;
|
|
951
|
+
if (thisStartLine - prevMemberEndLine < 2) {
|
|
952
|
+
return false;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
for (
|
|
956
|
+
let lineNum = prevMemberEndLine + 1;
|
|
957
|
+
lineNum < thisStartLine;
|
|
958
|
+
lineNum += 1
|
|
959
|
+
) {
|
|
960
|
+
if (
|
|
961
|
+
!commentLineNums.has(lineNum) &&
|
|
962
|
+
!hasNonWhitespace.test(
|
|
963
|
+
context.sourceCode.lines[lineNum - 1],
|
|
964
|
+
)
|
|
965
|
+
) {
|
|
966
|
+
return true;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
return false;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
return {
|
|
974
|
+
Object(node) {
|
|
975
|
+
let prevMember;
|
|
976
|
+
let prevName;
|
|
977
|
+
|
|
978
|
+
if (node.members.length < minKeys) {
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
for (const member of node.members) {
|
|
983
|
+
const thisName = getKey(member);
|
|
984
|
+
|
|
985
|
+
if (
|
|
986
|
+
prevMember &&
|
|
987
|
+
!isValidOrder(prevName, thisName) &&
|
|
988
|
+
(!allowLineSeparatedGroups ||
|
|
989
|
+
!isLineSeparated(prevMember, member))
|
|
990
|
+
) {
|
|
991
|
+
context.report({
|
|
992
|
+
loc: member.name.loc,
|
|
993
|
+
messageId: "sortKeys",
|
|
994
|
+
data: {
|
|
995
|
+
thisName,
|
|
996
|
+
prevName,
|
|
997
|
+
direction,
|
|
998
|
+
sensitivity,
|
|
999
|
+
sortName,
|
|
1000
|
+
},
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
prevMember = member;
|
|
1005
|
+
prevName = thisName;
|
|
1006
|
+
}
|
|
1007
|
+
},
|
|
1008
|
+
};
|
|
1009
|
+
},
|
|
1010
|
+
};
|
|
1011
|
+
|
|
721
1012
|
/**
|
|
722
1013
|
* @fileoverview Rule to ensure top-level items are either an array or ojbect.
|
|
723
1014
|
* @author Joe Hildebrand
|
|
724
1015
|
*/
|
|
725
1016
|
|
|
726
|
-
|
|
1017
|
+
//-----------------------------------------------------------------------------
|
|
1018
|
+
// Type Definitions
|
|
1019
|
+
//-----------------------------------------------------------------------------
|
|
1020
|
+
|
|
1021
|
+
/** @typedef {"topLevel"} TopLevelInteropMessageIds */
|
|
1022
|
+
/** @typedef {import("./types.ts").JSONRuleDefinition<[], TopLevelInteropMessageIds>} TopLevelInteropRuleDefinition */
|
|
1023
|
+
|
|
1024
|
+
//-----------------------------------------------------------------------------
|
|
1025
|
+
// Rule Definition
|
|
1026
|
+
//-----------------------------------------------------------------------------
|
|
1027
|
+
|
|
1028
|
+
/** @type {TopLevelInteropRuleDefinition} */
|
|
1029
|
+
const rule = {
|
|
727
1030
|
meta: {
|
|
728
|
-
type:
|
|
1031
|
+
type: "problem",
|
|
729
1032
|
|
|
730
1033
|
docs: {
|
|
731
1034
|
description:
|
|
@@ -767,7 +1070,7 @@ var topLevelInterop = {
|
|
|
767
1070
|
const plugin = {
|
|
768
1071
|
meta: {
|
|
769
1072
|
name: "@eslint/json",
|
|
770
|
-
version: "0.
|
|
1073
|
+
version: "0.11.0", // x-release-please-version
|
|
771
1074
|
},
|
|
772
1075
|
languages: {
|
|
773
1076
|
json: new JSONLanguage({ mode: "json" }),
|
|
@@ -775,11 +1078,12 @@ const plugin = {
|
|
|
775
1078
|
json5: new JSONLanguage({ mode: "json5" }),
|
|
776
1079
|
},
|
|
777
1080
|
rules: {
|
|
778
|
-
"no-duplicate-keys":
|
|
779
|
-
"no-empty-keys":
|
|
780
|
-
"no-unsafe-values":
|
|
781
|
-
"no-unnormalized-keys":
|
|
782
|
-
"
|
|
1081
|
+
"no-duplicate-keys": rule$5,
|
|
1082
|
+
"no-empty-keys": rule$4,
|
|
1083
|
+
"no-unsafe-values": rule$3,
|
|
1084
|
+
"no-unnormalized-keys": rule$2,
|
|
1085
|
+
"sort-keys": rule$1,
|
|
1086
|
+
"top-level-interop": rule,
|
|
783
1087
|
},
|
|
784
1088
|
configs: {
|
|
785
1089
|
recommended: {
|