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.
- package/README.md +3 -3
- package/bin/eslint.js +1 -2
- package/lib/api.js +4 -15
- package/lib/cli.js +14 -56
- package/lib/config/config-loader.js +6 -154
- package/lib/eslint/eslint-helpers.js +5 -8
- package/lib/eslint/eslint.js +1 -1
- package/lib/eslint/index.js +0 -2
- package/lib/languages/js/source-code/source-code.js +66 -252
- package/lib/languages/js/source-code/token-store/index.js +0 -26
- package/lib/languages/js/source-code/token-store/utils.js +29 -8
- package/lib/linter/apply-disable-directives.js +0 -1
- package/lib/linter/file-context.js +0 -56
- package/lib/linter/file-report.js +0 -4
- package/lib/linter/linter.js +45 -1086
- package/lib/linter/rule-fixer.js +30 -0
- package/lib/options.js +62 -182
- package/lib/rule-tester/rule-tester.js +265 -194
- package/lib/rules/array-bracket-spacing.js +4 -4
- package/lib/rules/block-spacing.js +1 -1
- package/lib/rules/comma-spacing.js +2 -5
- package/lib/rules/computed-property-spacing.js +4 -4
- package/lib/rules/dot-notation.js +2 -2
- package/lib/rules/func-names.js +2 -0
- package/lib/rules/keyword-spacing.js +4 -4
- package/lib/rules/no-eval.js +1 -1
- package/lib/rules/no-extra-parens.js +1 -1
- package/lib/rules/no-invalid-regexp.js +1 -0
- package/lib/rules/no-shadow-restricted-names.js +1 -1
- package/lib/rules/no-spaced-func.js +1 -1
- package/lib/rules/no-unassigned-vars.js +1 -1
- package/lib/rules/no-useless-assignment.js +1 -1
- package/lib/rules/no-useless-constructor.js +13 -3
- package/lib/rules/no-whitespace-before-property.js +1 -1
- package/lib/rules/object-curly-spacing.js +2 -8
- package/lib/rules/preserve-caught-error.js +1 -1
- package/lib/rules/radix.js +25 -48
- package/lib/rules/require-yield.js +11 -1
- package/lib/rules/rest-spread-spacing.js +1 -4
- package/lib/rules/semi-spacing.js +2 -2
- package/lib/rules/space-before-blocks.js +1 -1
- package/lib/rules/space-before-function-paren.js +1 -4
- package/lib/rules/space-in-parens.js +4 -4
- package/lib/rules/space-infix-ops.js +4 -7
- package/lib/rules/switch-colon-spacing.js +1 -1
- package/lib/rules/template-tag-spacing.js +1 -1
- package/lib/rules/utils/ast-utils.js +114 -22
- package/lib/rules/yield-star-spacing.js +1 -2
- package/lib/services/parser-service.js +0 -1
- package/lib/services/processor-service.js +0 -1
- package/lib/services/warning-service.js +0 -11
- package/lib/shared/flags.js +0 -19
- package/lib/shared/translate-cli-options.js +106 -164
- package/lib/types/index.d.ts +7 -81
- package/lib/types/rules.d.ts +11 -2
- package/lib/types/use-at-your-own-risk.d.ts +1 -54
- package/lib/unsupported-api.js +3 -6
- package/package.json +15 -20
- package/conf/default-cli-options.js +0 -32
- package/lib/cli-engine/cli-engine.js +0 -1109
- package/lib/cli-engine/file-enumerator.js +0 -541
- package/lib/cli-engine/index.js +0 -7
- package/lib/cli-engine/load-rules.js +0 -46
- 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
|
-
|
|
573
|
-
|
|
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 {
|
|
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 (
|
|
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 {
|
|
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 (
|
|
861
|
+
if (filename) {
|
|
684
862
|
flatConfigArrayOptions.basePath =
|
|
685
|
-
path.parse(
|
|
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
|
|
894
|
+
let output, beforeAST, afterAST;
|
|
717
895
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
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
|
-
|
|
741
|
-
|
|
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
|
-
|
|
757
|
-
|
|
758
|
-
|
|
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]:
|
|
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 {
|
|
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 {
|
|
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,
|
|
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(
|
|
1546
|
-
|
|
1608
|
+
runHook(item, "before");
|
|
1609
|
+
assertValidTestCase(item, seenTestCases);
|
|
1610
|
+
testValidTemplate(item);
|
|
1547
1611
|
} finally {
|
|
1548
|
-
runHook(
|
|
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
|
-
|
|
1560
|
-
|
|
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(
|
|
1564
|
-
|
|
1629
|
+
runHook(item, "before");
|
|
1630
|
+
assertInvalidTestCase(
|
|
1631
|
+
item,
|
|
1632
|
+
seenTestCases,
|
|
1633
|
+
ruleName,
|
|
1634
|
+
);
|
|
1635
|
+
testInvalidTemplate(item);
|
|
1565
1636
|
} finally {
|
|
1566
|
-
runHook(
|
|
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.
|
|
261
|
+
!sourceCode.isSpaceBetween(first, second)
|
|
262
262
|
) {
|
|
263
263
|
reportRequiredBeginningSpace(node, first);
|
|
264
264
|
}
|
|
265
265
|
if (
|
|
266
266
|
!openingBracketMustBeSpaced &&
|
|
267
|
-
sourceCode.
|
|
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.
|
|
279
|
+
!sourceCode.isSpaceBetween(penultimate, last)
|
|
280
280
|
) {
|
|
281
281
|
reportRequiredEndingSpace(node, last);
|
|
282
282
|
}
|
|
283
283
|
if (
|
|
284
284
|
!closingBracketMustBeSpaced &&
|
|
285
|
-
sourceCode.
|
|
285
|
+
sourceCode.isSpaceBetween(penultimate, last)
|
|
286
286
|
) {
|
|
287
287
|
reportNoEndingSpace(node, last);
|
|
288
288
|
}
|
|
@@ -178,10 +178,7 @@ module.exports = {
|
|
|
178
178
|
!commaTokensToIgnore.includes(token) &&
|
|
179
179
|
astUtils.isTokenOnSameLine(previousToken, token) &&
|
|
180
180
|
options.before !==
|
|
181
|
-
sourceCode.
|
|
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.
|
|
195
|
+
sourceCode.isSpaceBetween(token, nextToken)
|
|
199
196
|
) {
|
|
200
197
|
report(token, "after", nextToken);
|
|
201
198
|
}
|