eslint 8.57.0 → 9.2.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 +31 -28
- package/bin/eslint.js +4 -3
- package/conf/ecma-version.js +16 -0
- package/conf/globals.js +1 -0
- package/conf/rule-type-list.json +3 -1
- package/lib/api.js +7 -11
- package/lib/cli-engine/cli-engine.js +14 -3
- package/lib/cli-engine/formatters/formatters-meta.json +1 -29
- package/lib/cli-engine/lint-result-cache.js +2 -2
- package/lib/cli.js +115 -36
- package/lib/config/default-config.js +3 -0
- package/lib/config/flat-config-array.js +110 -24
- package/lib/config/flat-config-helpers.js +41 -20
- package/lib/config/flat-config-schema.js +1 -7
- package/lib/config/rule-validator.js +42 -6
- package/lib/eslint/eslint-helpers.js +116 -58
- package/lib/eslint/eslint.js +892 -377
- package/lib/eslint/index.js +2 -2
- package/lib/eslint/legacy-eslint.js +728 -0
- package/lib/linter/apply-disable-directives.js +59 -31
- package/lib/linter/code-path-analysis/code-path-analyzer.js +0 -1
- package/lib/linter/code-path-analysis/code-path.js +32 -30
- package/lib/linter/code-path-analysis/fork-context.js +1 -1
- package/lib/linter/config-comment-parser.js +8 -11
- package/lib/linter/index.js +1 -3
- package/lib/linter/interpolate.js +24 -2
- package/lib/linter/linter.js +428 -207
- package/lib/linter/report-translator.js +3 -3
- package/lib/linter/rules.js +6 -15
- package/lib/linter/source-code-fixer.js +1 -1
- package/lib/linter/timing.js +16 -8
- package/lib/options.js +35 -3
- package/lib/rule-tester/index.js +3 -1
- package/lib/rule-tester/rule-tester.js +424 -347
- package/lib/rules/array-bracket-newline.js +1 -1
- package/lib/rules/array-bracket-spacing.js +1 -1
- package/lib/rules/block-scoped-var.js +1 -1
- package/lib/rules/callback-return.js +2 -2
- package/lib/rules/camelcase.js +3 -5
- package/lib/rules/capitalized-comments.js +10 -7
- package/lib/rules/comma-dangle.js +1 -1
- package/lib/rules/comma-style.js +2 -2
- package/lib/rules/complexity.js +14 -1
- package/lib/rules/constructor-super.js +99 -100
- package/lib/rules/default-case.js +1 -1
- package/lib/rules/eol-last.js +2 -2
- package/lib/rules/function-paren-newline.js +2 -2
- package/lib/rules/indent-legacy.js +5 -5
- package/lib/rules/indent.js +5 -5
- package/lib/rules/index.js +1 -2
- package/lib/rules/key-spacing.js +2 -2
- package/lib/rules/line-comment-position.js +1 -1
- package/lib/rules/lines-around-directive.js +2 -2
- package/lib/rules/max-depth.js +1 -1
- package/lib/rules/max-len.js +3 -3
- package/lib/rules/max-lines.js +3 -3
- package/lib/rules/max-nested-callbacks.js +1 -1
- package/lib/rules/max-params.js +1 -1
- package/lib/rules/max-statements.js +1 -1
- package/lib/rules/multiline-comment-style.js +7 -7
- package/lib/rules/new-cap.js +1 -1
- package/lib/rules/newline-after-var.js +1 -1
- package/lib/rules/newline-before-return.js +1 -1
- package/lib/rules/no-case-declarations.js +13 -1
- package/lib/rules/no-constant-binary-expression.js +7 -8
- package/lib/rules/no-constant-condition.js +18 -7
- package/lib/rules/no-constructor-return.js +2 -2
- package/lib/rules/no-dupe-class-members.js +2 -2
- package/lib/rules/no-else-return.js +1 -1
- package/lib/rules/no-empty-function.js +2 -2
- package/lib/rules/no-empty-static-block.js +1 -1
- package/lib/rules/no-extend-native.js +1 -2
- package/lib/rules/no-extra-semi.js +1 -1
- package/lib/rules/no-fallthrough.js +41 -16
- package/lib/rules/no-implicit-coercion.js +66 -24
- package/lib/rules/no-inner-declarations.js +23 -2
- package/lib/rules/no-invalid-regexp.js +1 -1
- package/lib/rules/no-invalid-this.js +1 -1
- package/lib/rules/no-lone-blocks.js +3 -3
- package/lib/rules/no-loss-of-precision.js +1 -1
- package/lib/rules/no-misleading-character-class.js +225 -69
- package/lib/rules/no-mixed-spaces-and-tabs.js +1 -1
- package/lib/rules/no-multiple-empty-lines.js +1 -1
- package/lib/rules/no-new-native-nonconstructor.js +1 -1
- package/lib/rules/no-new-symbol.js +8 -1
- package/lib/rules/no-restricted-globals.js +1 -1
- package/lib/rules/no-restricted-imports.js +186 -40
- package/lib/rules/no-restricted-modules.js +2 -2
- package/lib/rules/no-return-await.js +1 -1
- package/lib/rules/no-sequences.js +1 -0
- package/lib/rules/no-this-before-super.js +45 -13
- package/lib/rules/no-trailing-spaces.js +2 -3
- package/lib/rules/no-unneeded-ternary.js +1 -1
- package/lib/rules/no-unsafe-optional-chaining.js +1 -1
- package/lib/rules/no-unused-private-class-members.js +1 -1
- package/lib/rules/no-unused-vars.js +197 -36
- package/lib/rules/no-useless-assignment.js +566 -0
- package/lib/rules/no-useless-backreference.js +1 -1
- package/lib/rules/no-useless-computed-key.js +2 -2
- package/lib/rules/no-useless-return.js +7 -2
- package/lib/rules/object-curly-spacing.js +3 -3
- package/lib/rules/object-property-newline.js +1 -1
- package/lib/rules/one-var.js +5 -5
- package/lib/rules/padded-blocks.js +7 -7
- package/lib/rules/prefer-arrow-callback.js +3 -3
- package/lib/rules/prefer-reflect.js +1 -1
- package/lib/rules/prefer-regex-literals.js +1 -1
- package/lib/rules/prefer-template.js +1 -1
- package/lib/rules/radix.js +2 -2
- package/lib/rules/semi-style.js +1 -1
- package/lib/rules/sort-imports.js +1 -1
- package/lib/rules/sort-keys.js +1 -1
- package/lib/rules/sort-vars.js +1 -1
- package/lib/rules/space-unary-ops.js +1 -1
- package/lib/rules/strict.js +1 -1
- package/lib/rules/use-isnan.js +101 -7
- package/lib/rules/utils/ast-utils.js +16 -7
- package/lib/rules/utils/char-source.js +240 -0
- package/lib/rules/utils/lazy-loading-rule-map.js +1 -1
- package/lib/rules/utils/unicode/index.js +9 -4
- package/lib/rules/yield-star-spacing.js +1 -1
- package/lib/shared/runtime-info.js +1 -0
- package/lib/shared/serialization.js +55 -0
- package/lib/shared/stats.js +30 -0
- package/lib/shared/string-utils.js +9 -11
- package/lib/shared/types.js +35 -1
- package/lib/source-code/index.js +3 -1
- package/lib/source-code/source-code.js +299 -85
- package/lib/source-code/token-store/backward-token-cursor.js +3 -3
- package/lib/source-code/token-store/cursors.js +4 -2
- package/lib/source-code/token-store/forward-token-comment-cursor.js +3 -3
- package/lib/source-code/token-store/forward-token-cursor.js +3 -3
- package/lib/source-code/token-store/index.js +2 -2
- package/lib/unsupported-api.js +3 -5
- package/messages/no-config-found.js +1 -1
- package/messages/plugin-conflict.js +1 -1
- package/messages/plugin-invalid.js +1 -1
- package/messages/plugin-missing.js +1 -1
- package/package.json +32 -29
- package/conf/config-schema.js +0 -93
- package/lib/cli-engine/formatters/checkstyle.js +0 -60
- package/lib/cli-engine/formatters/compact.js +0 -60
- package/lib/cli-engine/formatters/jslint-xml.js +0 -41
- package/lib/cli-engine/formatters/junit.js +0 -82
- package/lib/cli-engine/formatters/tap.js +0 -95
- package/lib/cli-engine/formatters/unix.js +0 -58
- package/lib/cli-engine/formatters/visualstudio.js +0 -63
- package/lib/cli-engine/xml-escape.js +0 -34
- package/lib/eslint/flat-eslint.js +0 -1155
- package/lib/rule-tester/flat-rule-tester.js +0 -1131
- package/lib/rules/require-jsdoc.js +0 -122
- package/lib/rules/utils/patterns/letters.js +0 -36
- package/lib/rules/valid-jsdoc.js +0 -516
- package/lib/shared/config-validator.js +0 -347
- package/lib/shared/deprecation-warnings.js +0 -58
- package/lib/shared/relative-module-resolver.js +0 -50
@@ -13,12 +13,16 @@ const { ConfigArray, ConfigArraySymbol } = require("@humanwhocodes/config-array"
|
|
13
13
|
const { flatConfigSchema } = require("./flat-config-schema");
|
14
14
|
const { RuleValidator } = require("./rule-validator");
|
15
15
|
const { defaultConfig } = require("./default-config");
|
16
|
-
const jsPlugin = require("@eslint/js");
|
17
16
|
|
18
17
|
//-----------------------------------------------------------------------------
|
19
18
|
// Helpers
|
20
19
|
//-----------------------------------------------------------------------------
|
21
20
|
|
21
|
+
/**
|
22
|
+
* Fields that are considered metadata and not part of the config object.
|
23
|
+
*/
|
24
|
+
const META_FIELDS = new Set(["name"]);
|
25
|
+
|
22
26
|
const ruleValidator = new RuleValidator();
|
23
27
|
|
24
28
|
/**
|
@@ -75,7 +79,53 @@ function getObjectId(object) {
|
|
75
79
|
return name;
|
76
80
|
}
|
77
81
|
|
82
|
+
/**
|
83
|
+
* Wraps a config error with details about where the error occurred.
|
84
|
+
* @param {Error} error The original error.
|
85
|
+
* @param {number} originalLength The original length of the config array.
|
86
|
+
* @param {number} baseLength The length of the base config.
|
87
|
+
* @returns {TypeError} The new error with details.
|
88
|
+
*/
|
89
|
+
function wrapConfigErrorWithDetails(error, originalLength, baseLength) {
|
90
|
+
|
91
|
+
let location = "user-defined";
|
92
|
+
let configIndex = error.index;
|
93
|
+
|
94
|
+
/*
|
95
|
+
* A config array is set up in this order:
|
96
|
+
* 1. Base config
|
97
|
+
* 2. Original configs
|
98
|
+
* 3. User-defined configs
|
99
|
+
* 4. CLI-defined configs
|
100
|
+
*
|
101
|
+
* So we need to adjust the index to account for the base config.
|
102
|
+
*
|
103
|
+
* - If the index is less than the base length, it's in the base config
|
104
|
+
* (as specified by `baseConfig` argument to `FlatConfigArray` constructor).
|
105
|
+
* - If the index is greater than the base length but less than the original
|
106
|
+
* length + base length, it's in the original config. The original config
|
107
|
+
* is passed to the `FlatConfigArray` constructor as the first argument.
|
108
|
+
* - Otherwise, it's in the user-defined config, which is loaded from the
|
109
|
+
* config file and merged with any command-line options.
|
110
|
+
*/
|
111
|
+
if (error.index < baseLength) {
|
112
|
+
location = "base";
|
113
|
+
} else if (error.index < originalLength + baseLength) {
|
114
|
+
location = "original";
|
115
|
+
configIndex = error.index - baseLength;
|
116
|
+
} else {
|
117
|
+
configIndex = error.index - originalLength - baseLength;
|
118
|
+
}
|
119
|
+
|
120
|
+
return new TypeError(
|
121
|
+
`${error.message.slice(0, -1)} at ${location} index ${configIndex}.`,
|
122
|
+
{ cause: error }
|
123
|
+
);
|
124
|
+
}
|
125
|
+
|
78
126
|
const originalBaseConfig = Symbol("originalBaseConfig");
|
127
|
+
const originalLength = Symbol("originalLength");
|
128
|
+
const baseLength = Symbol("baseLength");
|
79
129
|
|
80
130
|
//-----------------------------------------------------------------------------
|
81
131
|
// Exports
|
@@ -102,12 +152,24 @@ class FlatConfigArray extends ConfigArray {
|
|
102
152
|
schema: flatConfigSchema
|
103
153
|
});
|
104
154
|
|
155
|
+
/**
|
156
|
+
* The original length of the array before any modifications.
|
157
|
+
* @type {number}
|
158
|
+
*/
|
159
|
+
this[originalLength] = this.length;
|
160
|
+
|
105
161
|
if (baseConfig[Symbol.iterator]) {
|
106
162
|
this.unshift(...baseConfig);
|
107
163
|
} else {
|
108
164
|
this.unshift(baseConfig);
|
109
165
|
}
|
110
166
|
|
167
|
+
/**
|
168
|
+
* The length of the array after applying the base config.
|
169
|
+
* @type {number}
|
170
|
+
*/
|
171
|
+
this[baseLength] = this.length - this[originalLength];
|
172
|
+
|
111
173
|
/**
|
112
174
|
* The base config used to build the config array.
|
113
175
|
* @type {Array<FlatConfig>}
|
@@ -125,45 +187,69 @@ class FlatConfigArray extends ConfigArray {
|
|
125
187
|
Object.defineProperty(this, "shouldIgnore", { writable: false });
|
126
188
|
}
|
127
189
|
|
128
|
-
/* eslint-disable class-methods-use-this -- Desired as instance method */
|
129
190
|
/**
|
130
|
-
*
|
131
|
-
*
|
132
|
-
*
|
133
|
-
* @
|
134
|
-
* @returns {Object} The preprocessed config.
|
191
|
+
* Normalizes the array by calling the superclass method and catching/rethrowing
|
192
|
+
* any ConfigError exceptions with additional details.
|
193
|
+
* @param {any} [context] The context to use to normalize the array.
|
194
|
+
* @returns {Promise<FlatConfigArray>} A promise that resolves when the array is normalized.
|
135
195
|
*/
|
136
|
-
|
137
|
-
|
196
|
+
normalize(context) {
|
197
|
+
return super.normalize(context)
|
198
|
+
.catch(error => {
|
199
|
+
if (error.name === "ConfigError") {
|
200
|
+
throw wrapConfigErrorWithDetails(error, this[originalLength], this[baseLength]);
|
201
|
+
}
|
138
202
|
|
139
|
-
|
140
|
-
if (typeof process !== "undefined" && process.emitWarning) {
|
141
|
-
process.emitWarning("The 'eslint:recommended' string configuration is deprecated and will be replaced by the @eslint/js package's 'recommended' config.");
|
142
|
-
}
|
203
|
+
throw error;
|
143
204
|
|
144
|
-
|
145
|
-
|
205
|
+
});
|
206
|
+
}
|
207
|
+
|
208
|
+
/**
|
209
|
+
* Normalizes the array by calling the superclass method and catching/rethrowing
|
210
|
+
* any ConfigError exceptions with additional details.
|
211
|
+
* @param {any} [context] The context to use to normalize the array.
|
212
|
+
* @returns {FlatConfigArray} The current instance.
|
213
|
+
* @throws {TypeError} If the config is invalid.
|
214
|
+
*/
|
215
|
+
normalizeSync(context) {
|
216
|
+
|
217
|
+
try {
|
146
218
|
|
147
|
-
|
219
|
+
return super.normalizeSync(context);
|
148
220
|
|
149
|
-
|
150
|
-
|
151
|
-
|
221
|
+
} catch (error) {
|
222
|
+
|
223
|
+
if (error.name === "ConfigError") {
|
224
|
+
throw wrapConfigErrorWithDetails(error, this[originalLength], this[baseLength]);
|
152
225
|
}
|
153
226
|
|
154
|
-
|
227
|
+
throw error;
|
228
|
+
|
155
229
|
}
|
156
230
|
|
231
|
+
}
|
232
|
+
|
233
|
+
/* eslint-disable class-methods-use-this -- Desired as instance method */
|
234
|
+
/**
|
235
|
+
* Replaces a config with another config to allow us to put strings
|
236
|
+
* in the config array that will be replaced by objects before
|
237
|
+
* normalization.
|
238
|
+
* @param {Object} config The config to preprocess.
|
239
|
+
* @returns {Object} The preprocessed config.
|
240
|
+
*/
|
241
|
+
[ConfigArraySymbol.preprocessConfig](config) {
|
242
|
+
|
157
243
|
/*
|
158
|
-
* If `
|
159
|
-
*
|
160
|
-
*
|
244
|
+
* If a config object has `ignores` and no other non-meta fields, then it's an object
|
245
|
+
* for global ignores. If `shouldIgnore` is false, that object shouldn't apply,
|
246
|
+
* so we'll remove its `ignores`.
|
161
247
|
*/
|
162
248
|
if (
|
163
249
|
!this.shouldIgnore &&
|
164
250
|
!this[originalBaseConfig].includes(config) &&
|
165
251
|
config.ignores &&
|
166
|
-
|
252
|
+
Object.keys(config).filter(key => !META_FIELDS.has(key)).length === 1
|
167
253
|
) {
|
168
254
|
/* eslint-disable-next-line no-unused-vars -- need to strip off other keys */
|
169
255
|
const { ignores, ...otherKeys } = config;
|
@@ -5,6 +5,23 @@
|
|
5
5
|
|
6
6
|
"use strict";
|
7
7
|
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Typedefs
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
/** @typedef {import("../shared/types").Rule} Rule */
|
13
|
+
|
14
|
+
//------------------------------------------------------------------------------
|
15
|
+
// Private Members
|
16
|
+
//------------------------------------------------------------------------------
|
17
|
+
|
18
|
+
// JSON schema that disallows passing any options
|
19
|
+
const noOptionsSchema = Object.freeze({
|
20
|
+
type: "array",
|
21
|
+
minItems: 0,
|
22
|
+
maxItems: 0
|
23
|
+
});
|
24
|
+
|
8
25
|
//-----------------------------------------------------------------------------
|
9
26
|
// Functions
|
10
27
|
//-----------------------------------------------------------------------------
|
@@ -52,32 +69,39 @@ function getRuleFromConfig(ruleId, config) {
|
|
52
69
|
const { pluginName, ruleName } = parseRuleId(ruleId);
|
53
70
|
|
54
71
|
const plugin = config.plugins && config.plugins[pluginName];
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
// normalize function rules into objects
|
59
|
-
if (rule && typeof rule === "function") {
|
60
|
-
rule = {
|
61
|
-
create: rule
|
62
|
-
};
|
63
|
-
}
|
72
|
+
const rule = plugin && plugin.rules && plugin.rules[ruleName];
|
64
73
|
|
65
74
|
return rule;
|
66
75
|
}
|
67
76
|
|
68
77
|
/**
|
69
78
|
* Gets a complete options schema for a rule.
|
70
|
-
* @param {
|
71
|
-
* @
|
79
|
+
* @param {Rule} rule A rule object
|
80
|
+
* @throws {TypeError} If `meta.schema` is specified but is not an array, object or `false`.
|
81
|
+
* @returns {Object|null} JSON Schema for the rule's options. `null` if `meta.schema` is `false`.
|
72
82
|
*/
|
73
83
|
function getRuleOptionsSchema(rule) {
|
74
84
|
|
75
|
-
if (!rule) {
|
85
|
+
if (!rule.meta) {
|
86
|
+
return { ...noOptionsSchema }; // default if `meta.schema` is not specified
|
87
|
+
}
|
88
|
+
|
89
|
+
const schema = rule.meta.schema;
|
90
|
+
|
91
|
+
if (typeof schema === "undefined") {
|
92
|
+
return { ...noOptionsSchema }; // default if `meta.schema` is not specified
|
93
|
+
}
|
94
|
+
|
95
|
+
// `schema:false` is an allowed explicit opt-out of options validation for the rule
|
96
|
+
if (schema === false) {
|
76
97
|
return null;
|
77
98
|
}
|
78
99
|
|
79
|
-
|
100
|
+
if (typeof schema !== "object" || schema === null) {
|
101
|
+
throw new TypeError("Rule's `meta.schema` must be an array or object");
|
102
|
+
}
|
80
103
|
|
104
|
+
// ESLint-specific array form needs to be converted into a valid JSON Schema definition
|
81
105
|
if (Array.isArray(schema)) {
|
82
106
|
if (schema.length) {
|
83
107
|
return {
|
@@ -87,16 +111,13 @@ function getRuleOptionsSchema(rule) {
|
|
87
111
|
maxItems: schema.length
|
88
112
|
};
|
89
113
|
}
|
90
|
-
return {
|
91
|
-
type: "array",
|
92
|
-
minItems: 0,
|
93
|
-
maxItems: 0
|
94
|
-
};
|
95
114
|
|
115
|
+
// `schema:[]` is an explicit way to specify that the rule does not accept any options
|
116
|
+
return { ...noOptionsSchema };
|
96
117
|
}
|
97
118
|
|
98
|
-
//
|
99
|
-
return schema
|
119
|
+
// `schema:<object>` is assumed to be a valid JSON Schema definition
|
120
|
+
return schema;
|
100
121
|
}
|
101
122
|
|
102
123
|
|
@@ -9,11 +9,6 @@
|
|
9
9
|
// Requirements
|
10
10
|
//-----------------------------------------------------------------------------
|
11
11
|
|
12
|
-
/*
|
13
|
-
* Note: This can be removed in ESLint v9 because structuredClone is available globally
|
14
|
-
* starting in Node.js v17.
|
15
|
-
*/
|
16
|
-
const structuredClone = require("@ungap/structured-clone").default;
|
17
12
|
const { normalizeSeverityToNumber } = require("../shared/severity");
|
18
13
|
|
19
14
|
//-----------------------------------------------------------------------------
|
@@ -593,6 +588,5 @@ const flatConfigSchema = {
|
|
593
588
|
|
594
589
|
module.exports = {
|
595
590
|
flatConfigSchema,
|
596
|
-
assertIsRuleSeverity
|
597
|
-
assertIsRuleOptions
|
591
|
+
assertIsRuleSeverity
|
598
592
|
};
|
@@ -66,6 +66,25 @@ function throwRuleNotFoundError({ pluginName, ruleName }, config) {
|
|
66
66
|
throw new TypeError(errorMessage);
|
67
67
|
}
|
68
68
|
|
69
|
+
/**
|
70
|
+
* The error type when a rule has an invalid `meta.schema`.
|
71
|
+
*/
|
72
|
+
class InvalidRuleOptionsSchemaError extends Error {
|
73
|
+
|
74
|
+
/**
|
75
|
+
* Creates a new instance.
|
76
|
+
* @param {string} ruleId Id of the rule that has an invalid `meta.schema`.
|
77
|
+
* @param {Error} processingError Error caught while processing the `meta.schema`.
|
78
|
+
*/
|
79
|
+
constructor(ruleId, processingError) {
|
80
|
+
super(
|
81
|
+
`Error while processing options validation schema of rule '${ruleId}': ${processingError.message}`,
|
82
|
+
{ cause: processingError }
|
83
|
+
);
|
84
|
+
this.code = "ESLINT_INVALID_RULE_OPTIONS_SCHEMA";
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
69
88
|
//-----------------------------------------------------------------------------
|
70
89
|
// Exports
|
71
90
|
//-----------------------------------------------------------------------------
|
@@ -130,10 +149,14 @@ class RuleValidator {
|
|
130
149
|
|
131
150
|
// Precompile and cache validator the first time
|
132
151
|
if (!this.validators.has(rule)) {
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
152
|
+
try {
|
153
|
+
const schema = getRuleOptionsSchema(rule);
|
154
|
+
|
155
|
+
if (schema) {
|
156
|
+
this.validators.set(rule, ajv.compile(schema));
|
157
|
+
}
|
158
|
+
} catch (err) {
|
159
|
+
throw new InvalidRuleOptionsSchemaError(ruleId, err);
|
137
160
|
}
|
138
161
|
}
|
139
162
|
|
@@ -144,9 +167,22 @@ class RuleValidator {
|
|
144
167
|
validateRule(ruleOptions.slice(1));
|
145
168
|
|
146
169
|
if (validateRule.errors) {
|
147
|
-
throw new Error(`Key "rules": Key "${ruleId}"
|
170
|
+
throw new Error(`Key "rules": Key "${ruleId}":\n${
|
148
171
|
validateRule.errors.map(
|
149
|
-
error =>
|
172
|
+
error => {
|
173
|
+
if (
|
174
|
+
error.keyword === "additionalProperties" &&
|
175
|
+
error.schema === false &&
|
176
|
+
typeof error.parentSchema?.properties === "object" &&
|
177
|
+
typeof error.params?.additionalProperty === "string"
|
178
|
+
) {
|
179
|
+
const expectedProperties = Object.keys(error.parentSchema.properties).map(property => `"${property}"`);
|
180
|
+
|
181
|
+
return `\tValue ${JSON.stringify(error.data)} ${error.message}.\n\t\tUnexpected property "${error.params.additionalProperty}". Expected properties: ${expectedProperties.join(", ")}.\n`;
|
182
|
+
}
|
183
|
+
|
184
|
+
return `\tValue ${JSON.stringify(error.data)} ${error.message}.\n`;
|
185
|
+
}
|
150
186
|
).join("")
|
151
187
|
}`);
|
152
188
|
}
|
@@ -15,7 +15,6 @@ const fsp = fs.promises;
|
|
15
15
|
const isGlob = require("is-glob");
|
16
16
|
const hash = require("../cli-engine/hash");
|
17
17
|
const minimatch = require("minimatch");
|
18
|
-
const util = require("util");
|
19
18
|
const fswalk = require("@nodelib/fs.walk");
|
20
19
|
const globParent = require("glob-parent");
|
21
20
|
const isPathInside = require("is-path-inside");
|
@@ -24,7 +23,6 @@ const isPathInside = require("is-path-inside");
|
|
24
23
|
// Fixup references
|
25
24
|
//-----------------------------------------------------------------------------
|
26
25
|
|
27
|
-
const doFsWalk = util.promisify(fswalk.walk);
|
28
26
|
const Minimatch = minimatch.Minimatch;
|
29
27
|
const MINIMATCH_OPTIONS = { dot: true };
|
30
28
|
|
@@ -105,20 +103,30 @@ class AllFilesIgnoredError extends Error {
|
|
105
103
|
|
106
104
|
/**
|
107
105
|
* Check if a given value is a non-empty string or not.
|
108
|
-
* @param {any}
|
109
|
-
* @returns {boolean} `true` if `
|
106
|
+
* @param {any} value The value to check.
|
107
|
+
* @returns {boolean} `true` if `value` is a non-empty string.
|
110
108
|
*/
|
111
|
-
function isNonEmptyString(
|
112
|
-
return typeof
|
109
|
+
function isNonEmptyString(value) {
|
110
|
+
return typeof value === "string" && value.trim() !== "";
|
113
111
|
}
|
114
112
|
|
115
113
|
/**
|
116
114
|
* Check if a given value is an array of non-empty strings or not.
|
117
|
-
* @param {any}
|
118
|
-
* @returns {boolean} `true` if `
|
115
|
+
* @param {any} value The value to check.
|
116
|
+
* @returns {boolean} `true` if `value` is an array of non-empty strings.
|
117
|
+
*/
|
118
|
+
function isArrayOfNonEmptyString(value) {
|
119
|
+
return Array.isArray(value) && value.length && value.every(isNonEmptyString);
|
120
|
+
}
|
121
|
+
|
122
|
+
/**
|
123
|
+
* Check if a given value is an empty array or an array of non-empty strings.
|
124
|
+
* @param {any} value The value to check.
|
125
|
+
* @returns {boolean} `true` if `value` is an empty array or an array of non-empty
|
126
|
+
* strings.
|
119
127
|
*/
|
120
|
-
function
|
121
|
-
return Array.isArray(
|
128
|
+
function isEmptyArrayOrArrayOfNonEmptyString(value) {
|
129
|
+
return Array.isArray(value) && value.every(isNonEmptyString);
|
122
130
|
}
|
123
131
|
|
124
132
|
//-----------------------------------------------------------------------------
|
@@ -270,56 +278,92 @@ async function globSearch({
|
|
270
278
|
*/
|
271
279
|
const unmatchedPatterns = new Set([...relativeToPatterns.keys()]);
|
272
280
|
|
273
|
-
const filePaths = (await
|
274
|
-
|
275
|
-
deepFilter(entry) {
|
276
|
-
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
|
277
|
-
const matchesPattern = matchers.some(matcher => matcher.match(relativePath, true));
|
281
|
+
const filePaths = (await new Promise((resolve, reject) => {
|
278
282
|
|
279
|
-
|
280
|
-
},
|
281
|
-
entryFilter(entry) {
|
282
|
-
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
|
283
|
+
let promiseRejected = false;
|
283
284
|
|
284
|
-
|
285
|
-
|
285
|
+
/**
|
286
|
+
* Wraps a boolean-returning filter function. The wrapped function will reject the promise if an error occurs.
|
287
|
+
* @param {Function} filter A filter function to wrap.
|
288
|
+
* @returns {Function} A function similar to the wrapped filter that rejects the promise if an error occurs.
|
289
|
+
*/
|
290
|
+
function wrapFilter(filter) {
|
291
|
+
return (...args) => {
|
292
|
+
|
293
|
+
// No need to run the filter if an error has been thrown.
|
294
|
+
if (!promiseRejected) {
|
295
|
+
try {
|
296
|
+
return filter(...args);
|
297
|
+
} catch (error) {
|
298
|
+
promiseRejected = true;
|
299
|
+
reject(error);
|
300
|
+
}
|
301
|
+
}
|
286
302
|
return false;
|
287
|
-
}
|
303
|
+
};
|
304
|
+
}
|
288
305
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
306
|
+
fswalk.walk(
|
307
|
+
basePath,
|
308
|
+
{
|
309
|
+
deepFilter: wrapFilter(entry => {
|
310
|
+
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
|
311
|
+
const matchesPattern = matchers.some(matcher => matcher.match(relativePath, true));
|
312
|
+
|
313
|
+
return matchesPattern && !configs.isDirectoryIgnored(entry.path);
|
314
|
+
}),
|
315
|
+
entryFilter: wrapFilter(entry => {
|
316
|
+
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
|
317
|
+
|
318
|
+
// entries may be directories or files so filter out directories
|
319
|
+
if (entry.dirent.isDirectory()) {
|
320
|
+
return false;
|
321
|
+
}
|
301
322
|
|
302
323
|
/*
|
303
|
-
* We
|
304
|
-
*
|
305
|
-
*
|
306
|
-
* pattern
|
307
|
-
*
|
308
|
-
*
|
309
|
-
*
|
310
|
-
* it twice with the same argument.
|
324
|
+
* Optimization: We need to track when patterns are left unmatched
|
325
|
+
* and so we use `unmatchedPatterns` to do that. There is a bit of
|
326
|
+
* complexity here because the same file can be matched by more than
|
327
|
+
* one pattern. So, when we start, we actually need to test every
|
328
|
+
* pattern against every file. Once we know there are no remaining
|
329
|
+
* unmatched patterns, then we can switch to just looking for the
|
330
|
+
* first matching pattern for improved speed.
|
311
331
|
*/
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
332
|
+
const matchesPattern = unmatchedPatterns.size > 0
|
333
|
+
? matchers.reduce((previousValue, matcher) => {
|
334
|
+
const pathMatches = matcher.match(relativePath);
|
335
|
+
|
336
|
+
/*
|
337
|
+
* We updated the unmatched patterns set only if the path
|
338
|
+
* matches and the file isn't ignored. If the file is
|
339
|
+
* ignored, that means there wasn't a match for the
|
340
|
+
* pattern so it should not be removed.
|
341
|
+
*
|
342
|
+
* Performance note: isFileIgnored() aggressively caches
|
343
|
+
* results so there is no performance penalty for calling
|
344
|
+
* it twice with the same argument.
|
345
|
+
*/
|
346
|
+
if (pathMatches && !configs.isFileIgnored(entry.path)) {
|
347
|
+
unmatchedPatterns.delete(matcher.pattern);
|
348
|
+
}
|
349
|
+
|
350
|
+
return pathMatches || previousValue;
|
351
|
+
}, false)
|
352
|
+
: matchers.some(matcher => matcher.match(relativePath));
|
353
|
+
|
354
|
+
return matchesPattern && !configs.isFileIgnored(entry.path);
|
355
|
+
})
|
356
|
+
},
|
357
|
+
(error, entries) => {
|
358
|
+
|
359
|
+
// If the promise is already rejected, calling `resolve` or `reject` will do nothing.
|
360
|
+
if (error) {
|
361
|
+
reject(error);
|
362
|
+
} else {
|
363
|
+
resolve(entries);
|
364
|
+
}
|
365
|
+
}
|
366
|
+
);
|
323
367
|
})).map(entry => entry.path);
|
324
368
|
|
325
369
|
// now check to see if we have any unmatched patterns
|
@@ -655,9 +699,9 @@ class ESLintInvalidOptionsError extends Error {
|
|
655
699
|
|
656
700
|
/**
|
657
701
|
* Validates and normalizes options for the wrapped CLIEngine instance.
|
658
|
-
* @param {
|
702
|
+
* @param {ESLintOptions} options The options to process.
|
659
703
|
* @throws {ESLintInvalidOptionsError} If of any of a variety of type errors.
|
660
|
-
* @returns {
|
704
|
+
* @returns {ESLintOptions} The normalized options.
|
661
705
|
*/
|
662
706
|
function processOptions({
|
663
707
|
allowInlineConfig = true, // ← we cannot use `overrideConfig.noInlineConfig` instead because `allowInlineConfig` has side-effect that suppress warnings that show inline configs are ignored.
|
@@ -675,7 +719,10 @@ function processOptions({
|
|
675
719
|
overrideConfig = null,
|
676
720
|
overrideConfigFile = null,
|
677
721
|
plugins = {},
|
722
|
+
stats = false,
|
678
723
|
warnIgnored = true,
|
724
|
+
passOnNoPatterns = false,
|
725
|
+
ruleFilter = () => true,
|
679
726
|
...unknownOptions
|
680
727
|
}) {
|
681
728
|
const errors = [];
|
@@ -759,7 +806,7 @@ function processOptions({
|
|
759
806
|
if (typeof ignore !== "boolean") {
|
760
807
|
errors.push("'ignore' must be a boolean.");
|
761
808
|
}
|
762
|
-
if (!
|
809
|
+
if (!isEmptyArrayOrArrayOfNonEmptyString(ignorePatterns) && ignorePatterns !== null) {
|
763
810
|
errors.push("'ignorePatterns' must be an array of non-empty strings or null.");
|
764
811
|
}
|
765
812
|
if (typeof overrideConfig !== "object") {
|
@@ -768,6 +815,9 @@ function processOptions({
|
|
768
815
|
if (!isNonEmptyString(overrideConfigFile) && overrideConfigFile !== null && overrideConfigFile !== true) {
|
769
816
|
errors.push("'overrideConfigFile' must be a non-empty string, null, or true.");
|
770
817
|
}
|
818
|
+
if (typeof passOnNoPatterns !== "boolean") {
|
819
|
+
errors.push("'passOnNoPatterns' must be a boolean.");
|
820
|
+
}
|
771
821
|
if (typeof plugins !== "object") {
|
772
822
|
errors.push("'plugins' must be an object or null.");
|
773
823
|
} else if (plugins !== null && Object.keys(plugins).includes("")) {
|
@@ -776,9 +826,15 @@ function processOptions({
|
|
776
826
|
if (Array.isArray(plugins)) {
|
777
827
|
errors.push("'plugins' doesn't add plugins to configuration to load. Please use the 'overrideConfig.plugins' option instead.");
|
778
828
|
}
|
829
|
+
if (typeof stats !== "boolean") {
|
830
|
+
errors.push("'stats' must be a boolean.");
|
831
|
+
}
|
779
832
|
if (typeof warnIgnored !== "boolean") {
|
780
833
|
errors.push("'warnIgnored' must be a boolean.");
|
781
834
|
}
|
835
|
+
if (typeof ruleFilter !== "function") {
|
836
|
+
errors.push("'ruleFilter' must be a function.");
|
837
|
+
}
|
782
838
|
if (errors.length > 0) {
|
783
839
|
throw new ESLintInvalidOptionsError(errors);
|
784
840
|
}
|
@@ -800,7 +856,10 @@ function processOptions({
|
|
800
856
|
globInputPaths,
|
801
857
|
ignore,
|
802
858
|
ignorePatterns,
|
803
|
-
|
859
|
+
stats,
|
860
|
+
passOnNoPatterns,
|
861
|
+
warnIgnored,
|
862
|
+
ruleFilter
|
804
863
|
};
|
805
864
|
}
|
806
865
|
|
@@ -887,7 +946,6 @@ function getCacheFile(cacheFile, cwd) {
|
|
887
946
|
//-----------------------------------------------------------------------------
|
888
947
|
|
889
948
|
module.exports = {
|
890
|
-
isGlobPattern,
|
891
949
|
findFiles,
|
892
950
|
|
893
951
|
isNonEmptyString,
|