eslint 9.39.1 → 10.0.0-alpha.1

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.
Files changed (64) hide show
  1. package/README.md +3 -3
  2. package/bin/eslint.js +1 -2
  3. package/lib/api.js +4 -15
  4. package/lib/cli.js +14 -56
  5. package/lib/config/config-loader.js +6 -154
  6. package/lib/eslint/eslint-helpers.js +5 -8
  7. package/lib/eslint/eslint.js +1 -1
  8. package/lib/eslint/index.js +0 -2
  9. package/lib/languages/js/source-code/source-code.js +66 -252
  10. package/lib/languages/js/source-code/token-store/index.js +0 -26
  11. package/lib/languages/js/source-code/token-store/utils.js +29 -8
  12. package/lib/linter/apply-disable-directives.js +0 -1
  13. package/lib/linter/file-context.js +0 -56
  14. package/lib/linter/file-report.js +0 -4
  15. package/lib/linter/linter.js +45 -1086
  16. package/lib/linter/rule-fixer.js +30 -0
  17. package/lib/options.js +62 -182
  18. package/lib/rule-tester/rule-tester.js +265 -194
  19. package/lib/rules/array-bracket-spacing.js +4 -4
  20. package/lib/rules/block-spacing.js +1 -1
  21. package/lib/rules/comma-spacing.js +2 -5
  22. package/lib/rules/computed-property-spacing.js +4 -4
  23. package/lib/rules/dot-notation.js +2 -2
  24. package/lib/rules/func-names.js +2 -0
  25. package/lib/rules/keyword-spacing.js +4 -4
  26. package/lib/rules/no-eval.js +1 -1
  27. package/lib/rules/no-extra-parens.js +1 -1
  28. package/lib/rules/no-invalid-regexp.js +1 -0
  29. package/lib/rules/no-shadow-restricted-names.js +1 -1
  30. package/lib/rules/no-spaced-func.js +1 -1
  31. package/lib/rules/no-unassigned-vars.js +1 -1
  32. package/lib/rules/no-useless-assignment.js +1 -1
  33. package/lib/rules/no-useless-constructor.js +13 -3
  34. package/lib/rules/no-whitespace-before-property.js +1 -1
  35. package/lib/rules/object-curly-spacing.js +2 -8
  36. package/lib/rules/preserve-caught-error.js +1 -1
  37. package/lib/rules/radix.js +25 -48
  38. package/lib/rules/require-yield.js +11 -1
  39. package/lib/rules/rest-spread-spacing.js +1 -4
  40. package/lib/rules/semi-spacing.js +2 -2
  41. package/lib/rules/space-before-blocks.js +1 -1
  42. package/lib/rules/space-before-function-paren.js +1 -4
  43. package/lib/rules/space-in-parens.js +4 -4
  44. package/lib/rules/space-infix-ops.js +4 -7
  45. package/lib/rules/switch-colon-spacing.js +1 -1
  46. package/lib/rules/template-tag-spacing.js +1 -1
  47. package/lib/rules/utils/ast-utils.js +114 -22
  48. package/lib/rules/yield-star-spacing.js +1 -2
  49. package/lib/services/parser-service.js +0 -1
  50. package/lib/services/processor-service.js +0 -1
  51. package/lib/services/warning-service.js +0 -11
  52. package/lib/shared/flags.js +0 -19
  53. package/lib/shared/translate-cli-options.js +106 -164
  54. package/lib/types/index.d.ts +7 -81
  55. package/lib/types/rules.d.ts +11 -2
  56. package/lib/types/use-at-your-own-risk.d.ts +1 -54
  57. package/lib/unsupported-api.js +3 -6
  58. package/package.json +15 -20
  59. package/conf/default-cli-options.js +0 -32
  60. package/lib/cli-engine/cli-engine.js +0 -1109
  61. package/lib/cli-engine/file-enumerator.js +0 -541
  62. package/lib/cli-engine/index.js +0 -7
  63. package/lib/cli-engine/load-rules.js +0 -46
  64. package/lib/eslint/legacy-eslint.js +0 -786
@@ -18,7 +18,8 @@ const assert = require("node:assert"),
18
18
  { Config } = require("../config/config"),
19
19
  { Linter, SourceCodeFixer } = require("../linter"),
20
20
  { interpolate, getPlaceholderMatcher } = require("../linter/interpolate"),
21
- stringify = require("json-stable-stringify-without-jsonify");
21
+ stringify = require("json-stable-stringify-without-jsonify"),
22
+ { isSerializable } = require("../shared/serialization");
22
23
 
23
24
  const { FlatConfigArray } = require("../config/flat-config-array");
24
25
  const {
@@ -30,7 +31,6 @@ const ajv = require("../shared/ajv")({ strictDefaults: true });
30
31
 
31
32
  const parserSymbol = Symbol.for("eslint.RuleTester.parser");
32
33
  const { ConfigArraySymbol } = require("@eslint/config-array");
33
- const { isSerializable } = require("../shared/serialization");
34
34
 
35
35
  const jslang = require("../languages/js");
36
36
  const { SourceCode } = require("../languages/js/source-code");
@@ -78,7 +78,6 @@ const { SourceCode } = require("../languages/js/source-code");
78
78
  * @typedef {Object} TestCaseError
79
79
  * @property {string | RegExp} [message] Message.
80
80
  * @property {string} [messageId] Message ID.
81
- * @property {string} [type] The type of the reported AST node.
82
81
  * @property {{ [name: string]: string }} [data] The data used to fill the message template.
83
82
  * @property {number} [line] The 1-based line number of the reported start location.
84
83
  * @property {number} [column] The 1-based column number of the reported start location.
@@ -125,7 +124,6 @@ const errorObjectParameters = new Set([
125
124
  "message",
126
125
  "messageId",
127
126
  "data",
128
- "type",
129
127
  "line",
130
128
  "column",
131
129
  "endLine",
@@ -145,11 +143,6 @@ const suggestionObjectParameters = new Set([
145
143
  ]);
146
144
  const friendlySuggestionObjectParameterList = `[${[...suggestionObjectParameters].map(key => `'${key}'`).join(", ")}]`;
147
145
 
148
- /*
149
- * Ignored test case properties when checking for test case duplicates.
150
- */
151
- const duplicationIgnoredParameters = new Set(["name", "errors", "output"]);
152
-
153
146
  const forbiddenMethods = [
154
147
  "applyInlineConfig",
155
148
  "applyLanguageOptions",
@@ -375,6 +368,221 @@ const metaSchemaDescription = `
375
368
  \thttps://eslint.org/docs/latest/extend/custom-rules#options-schemas
376
369
  `;
377
370
 
371
+ /*
372
+ * Ignored test case properties when checking for test case duplicates.
373
+ */
374
+ const duplicationIgnoredParameters = new Set(["name", "errors", "output"]);
375
+
376
+ /**
377
+ * Normalizes a test case item, ensuring it is an object with a 'code' property.
378
+ * If the item is not an object, it returns an object with the 'code' property set to the item.
379
+ * @param {any} item The test case item to normalize.
380
+ * @returns {Object} The normalized test case object.
381
+ */
382
+ function normalizeTestCase(item) {
383
+ return item && typeof item === "object" ? item : { code: item };
384
+ }
385
+
386
+ /**
387
+ * Asserts that the `errors` property of an invalid test case is valid.
388
+ * @param {number | string[]} errors The `errors` property of the invalid test case.
389
+ * @param {string} ruleName The name of the rule being tested.
390
+ * @returns {void}
391
+ */
392
+ function assertErrorsProperty(errors, ruleName) {
393
+ const isNumber = typeof errors === "number";
394
+ const isArray = Array.isArray(errors);
395
+
396
+ if (!isNumber && !isArray) {
397
+ if (errors === void 0) {
398
+ assert.fail(
399
+ `Did not specify errors for an invalid test of ${ruleName}`,
400
+ );
401
+ } else {
402
+ assert.fail(
403
+ `Invalid 'errors' property for invalid test of ${ruleName}: expected a number or an array but got ${
404
+ errors === null ? "null" : typeof errors
405
+ }`,
406
+ );
407
+ }
408
+ }
409
+
410
+ if (isArray) {
411
+ assert.ok(
412
+ errors.length !== 0,
413
+ "Invalid cases must have at least one error",
414
+ );
415
+ } else {
416
+ assert.ok(
417
+ errors > 0,
418
+ "Invalid cases must have 'error' value greater than 0",
419
+ );
420
+ }
421
+ }
422
+
423
+ /**
424
+ * Check if this test case is a duplicate of one we have seen before.
425
+ * @param {Object} item test case object
426
+ * @param {Set<string>} seenTestCases set of serialized test cases we have seen so far (managed by this function)
427
+ * @returns {void}
428
+ */
429
+ function checkDuplicateTestCase(item, seenTestCases) {
430
+ if (!isSerializable(item)) {
431
+ /*
432
+ * If we can't serialize a test case (because it contains a function, RegExp, etc), skip the check.
433
+ * This might happen with properties like: options, plugins, settings, languageOptions.parser, languageOptions.parserOptions.
434
+ */
435
+ return;
436
+ }
437
+
438
+ const serializedTestCase = stringify(item, {
439
+ replacer(key, value) {
440
+ // "this" is the currently stringified object --> only ignore top-level properties
441
+ return item !== this || !duplicationIgnoredParameters.has(key)
442
+ ? value
443
+ : void 0;
444
+ },
445
+ });
446
+
447
+ assert(
448
+ !seenTestCases.has(serializedTestCase),
449
+ "detected duplicate test case",
450
+ );
451
+ seenTestCases.add(serializedTestCase);
452
+ }
453
+
454
+ /**
455
+ * Asserts that a rule is valid.
456
+ * A valid rule must be an object with a `create` method.
457
+ * @param {Object} rule The rule to check.
458
+ * @param {string} ruleName The name of the rule.
459
+ * @returns {void}
460
+ * @throws {AssertionError} If the rule is not valid.
461
+ */
462
+ function assertRule(rule, ruleName) {
463
+ assert.ok(
464
+ rule && typeof rule === "object" && typeof rule.create === "function",
465
+ `Rule ${ruleName} must be an object with a \`create\` method`,
466
+ );
467
+ }
468
+
469
+ /**
470
+ * Asserts that a test scenario object is valid.
471
+ * A valid test scenario object must have `valid` and `invalid` properties, both of
472
+ * which must be arrays.
473
+ * @param {Object} test The test scenario object to check.
474
+ * @param {string} ruleName The name of the rule being tested.
475
+ * @returns {void}
476
+ * @throws {AssertionError} If the test scenario object is not valid.
477
+ */
478
+ function assertTest(test, ruleName) {
479
+ assert.ok(
480
+ test && typeof test === "object",
481
+ `Test Scenarios for rule ${ruleName} : Could not find test scenario object`,
482
+ );
483
+
484
+ const hasValid = Array.isArray(test.valid);
485
+ const hasInvalid = Array.isArray(test.invalid);
486
+
487
+ assert.ok(
488
+ hasValid,
489
+ `Test Scenarios for rule ${ruleName} is invalid: Could not find any valid test scenarios`,
490
+ );
491
+
492
+ assert.ok(
493
+ hasInvalid,
494
+ `Test Scenarios for rule ${ruleName} is invalid: Could not find any invalid test scenarios`,
495
+ );
496
+ }
497
+
498
+ /**
499
+ * Asserts that the common properties of a valid/invalid test case have the correct types.
500
+ * @param {Object} item The test case object to check.
501
+ * @returns {void}
502
+ */
503
+ function assertTestCommonProperties(item) {
504
+ assert.ok(
505
+ typeof item.code === "string",
506
+ "Test case must specify a string value for 'code'",
507
+ );
508
+
509
+ // optional properties
510
+ if (item.name) {
511
+ assert.ok(
512
+ typeof item.name === "string",
513
+ "Optional test case property 'name' must be a string",
514
+ );
515
+ }
516
+ if (hasOwnProperty(item, "only")) {
517
+ assert.ok(
518
+ typeof item.only === "boolean",
519
+ "Optional test case property 'only' must be a boolean",
520
+ );
521
+ }
522
+ if (hasOwnProperty(item, "filename")) {
523
+ assert.ok(
524
+ typeof item.filename === "string",
525
+ "Optional test case property 'filename' must be a string",
526
+ );
527
+ }
528
+ if (hasOwnProperty(item, "options")) {
529
+ assert.ok(
530
+ Array.isArray(item.options),
531
+ "Optional test case property 'options' must be an array",
532
+ );
533
+ }
534
+ }
535
+
536
+ /**
537
+ * Asserts that a valid test case object is valid.
538
+ * A valid test case must specify a string value for 'code'.
539
+ * Optional properties are checked for correct types.
540
+ * @param {Object} item The valid test case object to check.
541
+ * @param {Set<string>} seenTestCases Set of serialized test cases to check for duplicates.
542
+ * @returns {void}
543
+ * @throws {AssertionError} If the test case is not valid.
544
+ */
545
+ function assertValidTestCase(item, seenTestCases) {
546
+ // must not have properties of invalid test cases
547
+ assert.ok(
548
+ item.errors === void 0,
549
+ "Valid test case must not have 'errors' property",
550
+ );
551
+ assert.ok(
552
+ item.output === void 0,
553
+ "Valid test case must not have 'output' property",
554
+ );
555
+
556
+ assertTestCommonProperties(item);
557
+ checkDuplicateTestCase(item, seenTestCases);
558
+ }
559
+
560
+ /**
561
+ * Asserts that the invalid test case object is valid.
562
+ * An invalid test case must specify a string value for 'code' and must have 'errors' property.
563
+ * Optional properties are checked for correct types.
564
+ * @param {Object} item The invalid test case object to check.
565
+ * @param {Set<string>} seenTestCases Set of serialized test cases to check for duplicates.
566
+ * @param {string} ruleName The name of the rule being tested.
567
+ * @returns {void}
568
+ * @throws {AssertionError} If the test case is not valid.
569
+ */
570
+ function assertInvalidTestCase(item, seenTestCases, ruleName) {
571
+ assertTestCommonProperties(item);
572
+
573
+ assertErrorsProperty(item.errors, ruleName);
574
+
575
+ // 'output' is optional, but if it exists it must be a string or null
576
+ if (hasOwnProperty(item, "output")) {
577
+ assert.ok(
578
+ item.output === null || typeof item.output === "string",
579
+ "Test property 'output', if specified, must be a string or null. If no autofix is expected, then omit the 'output' property or set it to null.",
580
+ );
581
+ }
582
+
583
+ checkDuplicateTestCase(item, seenTestCases);
584
+ }
585
+
378
586
  //------------------------------------------------------------------------------
379
587
  // Public Interface
380
588
  //------------------------------------------------------------------------------
@@ -564,45 +772,11 @@ class RuleTester {
564
772
  */
565
773
  run(ruleName, rule, test) {
566
774
  const testerConfig = this.testerConfig,
567
- requiredScenarios = ["valid", "invalid"],
568
- scenarioErrors = [],
569
775
  linter = this.linter,
570
776
  ruleId = `rule-to-test/${ruleName}`;
571
777
 
572
- const seenValidTestCases = new Set();
573
- const seenInvalidTestCases = new Set();
574
-
575
- if (
576
- !rule ||
577
- typeof rule !== "object" ||
578
- typeof rule.create !== "function"
579
- ) {
580
- throw new TypeError(
581
- "Rule must be an object with a `create` method",
582
- );
583
- }
584
-
585
- if (!test || typeof test !== "object") {
586
- throw new TypeError(
587
- `Test Scenarios for rule ${ruleName} : Could not find test scenario object`,
588
- );
589
- }
590
-
591
- requiredScenarios.forEach(scenarioType => {
592
- if (!test[scenarioType]) {
593
- scenarioErrors.push(
594
- `Could not find any ${scenarioType} test scenarios`,
595
- );
596
- }
597
- });
598
-
599
- if (scenarioErrors.length > 0) {
600
- throw new Error(
601
- [`Test Scenarios for rule ${ruleName} is invalid:`]
602
- .concat(scenarioErrors)
603
- .join("\n"),
604
- );
605
- }
778
+ assertRule(rule, ruleName);
779
+ assertTest(test, ruleName);
606
780
 
607
781
  const baseConfig = [
608
782
  {
@@ -651,14 +825,14 @@ class RuleTester {
651
825
 
652
826
  /**
653
827
  * Runs a hook on the given item when it's assigned to the given property
654
- * @param {string|Object} item Item to run the hook on
828
+ * @param {Object} item Item to run the hook on
655
829
  * @param {string} prop The property having the hook assigned to
656
830
  * @throws {Error} If the property is not a function or that function throws an error
657
831
  * @returns {void}
658
832
  * @private
659
833
  */
660
834
  function runHook(item, prop) {
661
- if (typeof item === "object" && hasOwnProperty(item, prop)) {
835
+ if (hasOwnProperty(item, prop)) {
662
836
  assert.strictEqual(
663
837
  typeof item[prop],
664
838
  "function",
@@ -667,22 +841,26 @@ class RuleTester {
667
841
  item[prop]();
668
842
  }
669
843
  }
670
-
671
844
  /**
672
845
  * Run the rule for the given item
673
- * @param {string|Object} item Item to run the rule against
846
+ * @param {Object} item Item to run the rule against
674
847
  * @throws {Error} If an invalid schema.
675
848
  * @returns {Object} Eslint run result
676
849
  * @private
677
850
  */
678
851
  function runRuleForItem(item) {
852
+ const code = item.code;
853
+ const filename = hasOwnProperty(item, "filename")
854
+ ? item.filename
855
+ : void 0;
856
+ const options = hasOwnProperty(item, "options") ? item.options : [];
679
857
  const flatConfigArrayOptions = {
680
858
  baseConfig,
681
859
  };
682
860
 
683
- if (item.filename) {
861
+ if (filename) {
684
862
  flatConfigArrayOptions.basePath =
685
- path.parse(item.filename).root || void 0;
863
+ path.parse(filename).root || void 0;
686
864
  }
687
865
 
688
866
  const configs = new FlatConfigArray(
@@ -713,54 +891,27 @@ class RuleTester {
713
891
  return calculatedConfig;
714
892
  };
715
893
 
716
- let code, filename, output, beforeAST, afterAST;
894
+ let output, beforeAST, afterAST;
717
895
 
718
- if (typeof item === "string") {
719
- code = item;
720
- } else {
721
- code = item.code;
722
-
723
- /*
724
- * Assumes everything on the item is a config except for the
725
- * parameters used by this tester
726
- */
727
- const itemConfig = { ...item };
728
-
729
- for (const parameter of RuleTesterParameters) {
730
- delete itemConfig[parameter];
731
- }
732
-
733
- /*
734
- * Create the config object from the tester config and this item
735
- * specific configurations.
736
- */
737
- configs.push(itemConfig);
738
- }
896
+ /*
897
+ * Assumes everything on the item is a config except for the
898
+ * parameters used by this tester
899
+ */
900
+ const itemConfig = { ...item };
739
901
 
740
- if (hasOwnProperty(item, "only")) {
741
- assert.ok(
742
- typeof item.only === "boolean",
743
- "Optional test case property 'only' must be a boolean",
744
- );
902
+ for (const parameter of RuleTesterParameters) {
903
+ delete itemConfig[parameter];
745
904
  }
746
- if (hasOwnProperty(item, "filename")) {
747
- assert.ok(
748
- typeof item.filename === "string",
749
- "Optional test case property 'filename' must be a string",
750
- );
751
- filename = item.filename;
752
- }
753
-
754
- let ruleConfig = 1;
755
905
 
756
- if (hasOwnProperty(item, "options")) {
757
- assert(Array.isArray(item.options), "options must be an array");
758
- ruleConfig = [1, ...item.options];
759
- }
906
+ /*
907
+ * Create the config object from the tester config and this item
908
+ * specific configurations.
909
+ */
910
+ configs.push(itemConfig);
760
911
 
761
912
  configs.push({
762
913
  rules: {
763
- [ruleId]: ruleConfig,
914
+ [ruleId]: [1, ...options],
764
915
  },
765
916
  });
766
917
 
@@ -939,64 +1090,14 @@ class RuleTester {
939
1090
  }
940
1091
  }
941
1092
 
942
- /**
943
- * Check if this test case is a duplicate of one we have seen before.
944
- * @param {string|Object} item test case object
945
- * @param {Set<string>} seenTestCases set of serialized test cases we have seen so far (managed by this function)
946
- * @returns {void}
947
- * @private
948
- */
949
- function checkDuplicateTestCase(item, seenTestCases) {
950
- if (!isSerializable(item)) {
951
- /*
952
- * If we can't serialize a test case (because it contains a function, RegExp, etc), skip the check.
953
- * This might happen with properties like: options, plugins, settings, languageOptions.parser, languageOptions.parserOptions.
954
- */
955
- return;
956
- }
957
-
958
- const normalizedItem =
959
- typeof item === "string" ? { code: item } : item;
960
- const serializedTestCase = stringify(normalizedItem, {
961
- replacer(key, value) {
962
- // "this" is the currently stringified object --> only ignore top-level properties
963
- return normalizedItem !== this ||
964
- !duplicationIgnoredParameters.has(key)
965
- ? value
966
- : void 0;
967
- },
968
- });
969
-
970
- assert(
971
- !seenTestCases.has(serializedTestCase),
972
- "detected duplicate test case",
973
- );
974
- seenTestCases.add(serializedTestCase);
975
- }
976
-
977
1093
  /**
978
1094
  * Check if the template is valid or not
979
1095
  * all valid cases go through this
980
- * @param {string|Object} item Item to run the rule against
1096
+ * @param {Object} item Item to run the rule against
981
1097
  * @returns {void}
982
1098
  * @private
983
1099
  */
984
1100
  function testValidTemplate(item) {
985
- const code = typeof item === "object" ? item.code : item;
986
-
987
- assert.ok(
988
- typeof code === "string",
989
- "Test case must specify a string value for 'code'",
990
- );
991
- if (item.name) {
992
- assert.ok(
993
- typeof item.name === "string",
994
- "Optional test case property 'name' must be a string",
995
- );
996
- }
997
-
998
- checkDuplicateTestCase(item, seenValidTestCases);
999
-
1000
1101
  const result = runRuleForItem(item);
1001
1102
  const messages = result.messages;
1002
1103
 
@@ -1037,32 +1138,11 @@ class RuleTester {
1037
1138
  /**
1038
1139
  * Check if the template is invalid or not
1039
1140
  * all invalid cases go through this.
1040
- * @param {string|Object} item Item to run the rule against
1141
+ * @param {Object} item Item to run the rule against
1041
1142
  * @returns {void}
1042
1143
  * @private
1043
1144
  */
1044
1145
  function testInvalidTemplate(item) {
1045
- assert.ok(
1046
- typeof item.code === "string",
1047
- "Test case must specify a string value for 'code'",
1048
- );
1049
- if (item.name) {
1050
- assert.ok(
1051
- typeof item.name === "string",
1052
- "Optional test case property 'name' must be a string",
1053
- );
1054
- }
1055
- assert.ok(
1056
- item.errors || item.errors === 0,
1057
- `Did not specify errors for an invalid test of ${ruleName}`,
1058
- );
1059
-
1060
- if (Array.isArray(item.errors) && item.errors.length === 0) {
1061
- assert.fail("Invalid cases must have at least one error");
1062
- }
1063
-
1064
- checkDuplicateTestCase(item, seenInvalidTestCases);
1065
-
1066
1146
  const ruleHasMetaMessages =
1067
1147
  hasOwnProperty(rule, "meta") &&
1068
1148
  hasOwnProperty(rule.meta, "messages");
@@ -1095,12 +1175,6 @@ class RuleTester {
1095
1175
  }
1096
1176
 
1097
1177
  if (typeof item.errors === "number") {
1098
- if (item.errors === 0) {
1099
- assert.fail(
1100
- "Invalid cases must have 'error' value greater than 0",
1101
- );
1102
- }
1103
-
1104
1178
  assert.strictEqual(
1105
1179
  messages.length,
1106
1180
  item.errors,
@@ -1148,8 +1222,7 @@ class RuleTester {
1148
1222
  } else if (typeof error === "object" && error !== null) {
1149
1223
  /*
1150
1224
  * Error object.
1151
- * This may have a message, messageId, data, node type, line, and/or
1152
- * column.
1225
+ * This may have a message, messageId, data, line, and/or column.
1153
1226
  */
1154
1227
 
1155
1228
  Object.keys(error).forEach(propertyName => {
@@ -1231,14 +1304,6 @@ class RuleTester {
1231
1304
  );
1232
1305
  }
1233
1306
 
1234
- if (error.type) {
1235
- assert.strictEqual(
1236
- message.nodeType,
1237
- error.type,
1238
- `Error type should be ${error.type}, found ${message.nodeType}`,
1239
- );
1240
- }
1241
-
1242
1307
  const actualLocation = {};
1243
1308
  const expectedLocation = {};
1244
1309
 
@@ -1533,19 +1598,18 @@ class RuleTester {
1533
1598
  this.constructor.describe(ruleName, () => {
1534
1599
  if (test.valid.length > 0) {
1535
1600
  this.constructor.describe("valid", () => {
1601
+ const seenTestCases = new Set();
1536
1602
  test.valid.forEach(valid => {
1603
+ const item = normalizeTestCase(valid);
1537
1604
  this.constructor[valid.only ? "itOnly" : "it"](
1538
- sanitize(
1539
- typeof valid === "object"
1540
- ? valid.name || valid.code
1541
- : valid,
1542
- ),
1605
+ sanitize(item.name || item.code),
1543
1606
  () => {
1544
1607
  try {
1545
- runHook(valid, "before");
1546
- testValidTemplate(valid);
1608
+ runHook(item, "before");
1609
+ assertValidTestCase(item, seenTestCases);
1610
+ testValidTemplate(item);
1547
1611
  } finally {
1548
- runHook(valid, "after");
1612
+ runHook(item, "after");
1549
1613
  }
1550
1614
  },
1551
1615
  );
@@ -1555,15 +1619,22 @@ class RuleTester {
1555
1619
 
1556
1620
  if (test.invalid.length > 0) {
1557
1621
  this.constructor.describe("invalid", () => {
1622
+ const seenTestCases = new Set();
1558
1623
  test.invalid.forEach(invalid => {
1559
- this.constructor[invalid.only ? "itOnly" : "it"](
1560
- sanitize(invalid.name || invalid.code),
1624
+ const item = normalizeTestCase(invalid);
1625
+ this.constructor[item.only ? "itOnly" : "it"](
1626
+ sanitize(item.name || item.code),
1561
1627
  () => {
1562
1628
  try {
1563
- runHook(invalid, "before");
1564
- testInvalidTemplate(invalid);
1629
+ runHook(item, "before");
1630
+ assertInvalidTestCase(
1631
+ item,
1632
+ seenTestCases,
1633
+ ruleName,
1634
+ );
1635
+ testInvalidTemplate(item);
1565
1636
  } finally {
1566
- runHook(invalid, "after");
1637
+ runHook(item, "after");
1567
1638
  }
1568
1639
  },
1569
1640
  );
@@ -258,13 +258,13 @@ module.exports = {
258
258
  if (astUtils.isTokenOnSameLine(first, second)) {
259
259
  if (
260
260
  openingBracketMustBeSpaced &&
261
- !sourceCode.isSpaceBetweenTokens(first, second)
261
+ !sourceCode.isSpaceBetween(first, second)
262
262
  ) {
263
263
  reportRequiredBeginningSpace(node, first);
264
264
  }
265
265
  if (
266
266
  !openingBracketMustBeSpaced &&
267
- sourceCode.isSpaceBetweenTokens(first, second)
267
+ sourceCode.isSpaceBetween(first, second)
268
268
  ) {
269
269
  reportNoBeginningSpace(node, first);
270
270
  }
@@ -276,13 +276,13 @@ module.exports = {
276
276
  ) {
277
277
  if (
278
278
  closingBracketMustBeSpaced &&
279
- !sourceCode.isSpaceBetweenTokens(penultimate, last)
279
+ !sourceCode.isSpaceBetween(penultimate, last)
280
280
  ) {
281
281
  reportRequiredEndingSpace(node, last);
282
282
  }
283
283
  if (
284
284
  !closingBracketMustBeSpaced &&
285
- sourceCode.isSpaceBetweenTokens(penultimate, last)
285
+ sourceCode.isSpaceBetween(penultimate, last)
286
286
  ) {
287
287
  reportNoEndingSpace(node, last);
288
288
  }
@@ -95,7 +95,7 @@ module.exports = {
95
95
  function isValid(left, right) {
96
96
  return (
97
97
  !util.isTokenOnSameLine(left, right) ||
98
- sourceCode.isSpaceBetweenTokens(left, right) === always
98
+ sourceCode.isSpaceBetween(left, right) === always
99
99
  );
100
100
  }
101
101
 
@@ -178,10 +178,7 @@ module.exports = {
178
178
  !commaTokensToIgnore.includes(token) &&
179
179
  astUtils.isTokenOnSameLine(previousToken, token) &&
180
180
  options.before !==
181
- sourceCode.isSpaceBetweenTokens(
182
- previousToken,
183
- token,
184
- )
181
+ sourceCode.isSpaceBetween(previousToken, token)
185
182
  ) {
186
183
  report(token, "before", previousToken);
187
184
  }
@@ -195,7 +192,7 @@ module.exports = {
195
192
  !(!options.after && nextToken.type === "Line") && // special case, allow space before line comment
196
193
  astUtils.isTokenOnSameLine(token, nextToken) &&
197
194
  options.after !==
198
- sourceCode.isSpaceBetweenTokens(token, nextToken)
195
+ sourceCode.isSpaceBetween(token, nextToken)
199
196
  ) {
200
197
  report(token, "after", nextToken);
201
198
  }