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
@@ -0,0 +1,728 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Main API Class
|
3
|
+
* @author Kai Cataldo
|
4
|
+
* @author Toru Nagashima
|
5
|
+
*/
|
6
|
+
|
7
|
+
"use strict";
|
8
|
+
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
// Requirements
|
11
|
+
//------------------------------------------------------------------------------
|
12
|
+
|
13
|
+
const path = require("path");
|
14
|
+
const fs = require("fs");
|
15
|
+
const { promisify } = require("util");
|
16
|
+
const { CLIEngine, getCLIEngineInternalSlots } = require("../cli-engine/cli-engine");
|
17
|
+
const BuiltinRules = require("../rules");
|
18
|
+
const {
|
19
|
+
Legacy: {
|
20
|
+
ConfigOps: {
|
21
|
+
getRuleSeverity
|
22
|
+
}
|
23
|
+
}
|
24
|
+
} = require("@eslint/eslintrc");
|
25
|
+
const { version } = require("../../package.json");
|
26
|
+
|
27
|
+
//------------------------------------------------------------------------------
|
28
|
+
// Typedefs
|
29
|
+
//------------------------------------------------------------------------------
|
30
|
+
|
31
|
+
/** @typedef {import("../cli-engine/cli-engine").LintReport} CLIEngineLintReport */
|
32
|
+
/** @typedef {import("../shared/types").DeprecatedRuleInfo} DeprecatedRuleInfo */
|
33
|
+
/** @typedef {import("../shared/types").ConfigData} ConfigData */
|
34
|
+
/** @typedef {import("../shared/types").LintMessage} LintMessage */
|
35
|
+
/** @typedef {import("../shared/types").SuppressedLintMessage} SuppressedLintMessage */
|
36
|
+
/** @typedef {import("../shared/types").Plugin} Plugin */
|
37
|
+
/** @typedef {import("../shared/types").Rule} Rule */
|
38
|
+
/** @typedef {import("../shared/types").LintResult} LintResult */
|
39
|
+
/** @typedef {import("../shared/types").ResultsMeta} ResultsMeta */
|
40
|
+
|
41
|
+
/**
|
42
|
+
* The main formatter object.
|
43
|
+
* @typedef LoadedFormatter
|
44
|
+
* @property {(results: LintResult[], resultsMeta: ResultsMeta) => string | Promise<string>} format format function.
|
45
|
+
*/
|
46
|
+
|
47
|
+
/**
|
48
|
+
* The options with which to configure the LegacyESLint instance.
|
49
|
+
* @typedef {Object} LegacyESLintOptions
|
50
|
+
* @property {boolean} [allowInlineConfig] Enable or disable inline configuration comments.
|
51
|
+
* @property {ConfigData} [baseConfig] Base config object, extended by all configs used with this instance
|
52
|
+
* @property {boolean} [cache] Enable result caching.
|
53
|
+
* @property {string} [cacheLocation] The cache file to use instead of .eslintcache.
|
54
|
+
* @property {"metadata" | "content"} [cacheStrategy] The strategy used to detect changed files.
|
55
|
+
* @property {string} [cwd] The value to use for the current working directory.
|
56
|
+
* @property {boolean} [errorOnUnmatchedPattern] If `false` then `ESLint#lintFiles()` doesn't throw even if no target files found. Defaults to `true`.
|
57
|
+
* @property {string[]} [extensions] An array of file extensions to check.
|
58
|
+
* @property {boolean|Function} [fix] Execute in autofix mode. If a function, should return a boolean.
|
59
|
+
* @property {string[]} [fixTypes] Array of rule types to apply fixes for.
|
60
|
+
* @property {boolean} [globInputPaths] Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file.
|
61
|
+
* @property {boolean} [ignore] False disables use of .eslintignore.
|
62
|
+
* @property {string} [ignorePath] The ignore file to use instead of .eslintignore.
|
63
|
+
* @property {ConfigData} [overrideConfig] Override config object, overrides all configs used with this instance
|
64
|
+
* @property {string} [overrideConfigFile] The configuration file to use.
|
65
|
+
* @property {Record<string,Plugin>|null} [plugins] Preloaded plugins. This is a map-like object, keys are plugin IDs and each value is implementation.
|
66
|
+
* @property {"error" | "warn" | "off"} [reportUnusedDisableDirectives] the severity to report unused eslint-disable directives.
|
67
|
+
* @property {string} [resolvePluginsRelativeTo] The folder where plugins should be resolved from, defaulting to the CWD.
|
68
|
+
* @property {string[]} [rulePaths] An array of directories to load custom rules from.
|
69
|
+
* @property {boolean} [useEslintrc] False disables looking for .eslintrc.* files.
|
70
|
+
* @property {boolean} [passOnNoPatterns=false] When set to true, missing patterns cause
|
71
|
+
* the linting operation to short circuit and not report any failures.
|
72
|
+
*/
|
73
|
+
|
74
|
+
/**
|
75
|
+
* A rules metadata object.
|
76
|
+
* @typedef {Object} RulesMeta
|
77
|
+
* @property {string} id The plugin ID.
|
78
|
+
* @property {Object} definition The plugin definition.
|
79
|
+
*/
|
80
|
+
|
81
|
+
/**
|
82
|
+
* Private members for the `ESLint` instance.
|
83
|
+
* @typedef {Object} ESLintPrivateMembers
|
84
|
+
* @property {CLIEngine} cliEngine The wrapped CLIEngine instance.
|
85
|
+
* @property {LegacyESLintOptions} options The options used to instantiate the ESLint instance.
|
86
|
+
*/
|
87
|
+
|
88
|
+
//------------------------------------------------------------------------------
|
89
|
+
// Helpers
|
90
|
+
//------------------------------------------------------------------------------
|
91
|
+
|
92
|
+
const writeFile = promisify(fs.writeFile);
|
93
|
+
|
94
|
+
/**
|
95
|
+
* The map with which to store private class members.
|
96
|
+
* @type {WeakMap<ESLint, ESLintPrivateMembers>}
|
97
|
+
*/
|
98
|
+
const privateMembersMap = new WeakMap();
|
99
|
+
|
100
|
+
/**
|
101
|
+
* Check if a given value is a non-empty string or not.
|
102
|
+
* @param {any} value The value to check.
|
103
|
+
* @returns {boolean} `true` if `value` is a non-empty string.
|
104
|
+
*/
|
105
|
+
function isNonEmptyString(value) {
|
106
|
+
return typeof value === "string" && value.trim() !== "";
|
107
|
+
}
|
108
|
+
|
109
|
+
/**
|
110
|
+
* Check if a given value is an array of non-empty strings or not.
|
111
|
+
* @param {any} value The value to check.
|
112
|
+
* @returns {boolean} `true` if `value` is an array of non-empty strings.
|
113
|
+
*/
|
114
|
+
function isArrayOfNonEmptyString(value) {
|
115
|
+
return Array.isArray(value) && value.length && value.every(isNonEmptyString);
|
116
|
+
}
|
117
|
+
|
118
|
+
/**
|
119
|
+
* Check if a given value is an empty array or an array of non-empty strings.
|
120
|
+
* @param {any} value The value to check.
|
121
|
+
* @returns {boolean} `true` if `value` is an empty array or an array of non-empty
|
122
|
+
* strings.
|
123
|
+
*/
|
124
|
+
function isEmptyArrayOrArrayOfNonEmptyString(value) {
|
125
|
+
return Array.isArray(value) && value.every(isNonEmptyString);
|
126
|
+
}
|
127
|
+
|
128
|
+
/**
|
129
|
+
* Check if a given value is a valid fix type or not.
|
130
|
+
* @param {any} value The value to check.
|
131
|
+
* @returns {boolean} `true` if `value` is valid fix type.
|
132
|
+
*/
|
133
|
+
function isFixType(value) {
|
134
|
+
return value === "directive" || value === "problem" || value === "suggestion" || value === "layout";
|
135
|
+
}
|
136
|
+
|
137
|
+
/**
|
138
|
+
* Check if a given value is an array of fix types or not.
|
139
|
+
* @param {any} value The value to check.
|
140
|
+
* @returns {boolean} `true` if `value` is an array of fix types.
|
141
|
+
*/
|
142
|
+
function isFixTypeArray(value) {
|
143
|
+
return Array.isArray(value) && value.every(isFixType);
|
144
|
+
}
|
145
|
+
|
146
|
+
/**
|
147
|
+
* The error for invalid options.
|
148
|
+
*/
|
149
|
+
class ESLintInvalidOptionsError extends Error {
|
150
|
+
constructor(messages) {
|
151
|
+
super(`Invalid Options:\n- ${messages.join("\n- ")}`);
|
152
|
+
this.code = "ESLINT_INVALID_OPTIONS";
|
153
|
+
Error.captureStackTrace(this, ESLintInvalidOptionsError);
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
/**
|
158
|
+
* Validates and normalizes options for the wrapped CLIEngine instance.
|
159
|
+
* @param {LegacyESLintOptions} options The options to process.
|
160
|
+
* @throws {ESLintInvalidOptionsError} If of any of a variety of type errors.
|
161
|
+
* @returns {LegacyESLintOptions} The normalized options.
|
162
|
+
*/
|
163
|
+
function processOptions({
|
164
|
+
allowInlineConfig = true, // ← we cannot use `overrideConfig.noInlineConfig` instead because `allowInlineConfig` has side-effect that suppress warnings that show inline configs are ignored.
|
165
|
+
baseConfig = null,
|
166
|
+
cache = false,
|
167
|
+
cacheLocation = ".eslintcache",
|
168
|
+
cacheStrategy = "metadata",
|
169
|
+
cwd = process.cwd(),
|
170
|
+
errorOnUnmatchedPattern = true,
|
171
|
+
extensions = null, // ← should be null by default because if it's an array then it suppresses RFC20 feature.
|
172
|
+
fix = false,
|
173
|
+
fixTypes = null, // ← should be null by default because if it's an array then it suppresses rules that don't have the `meta.type` property.
|
174
|
+
globInputPaths = true,
|
175
|
+
ignore = true,
|
176
|
+
ignorePath = null, // ← should be null by default because if it's a string then it may throw ENOENT.
|
177
|
+
overrideConfig = null,
|
178
|
+
overrideConfigFile = null,
|
179
|
+
plugins = {},
|
180
|
+
reportUnusedDisableDirectives = null, // ← should be null by default because if it's a string then it overrides the 'reportUnusedDisableDirectives' setting in config files. And we cannot use `overrideConfig.reportUnusedDisableDirectives` instead because we cannot configure the `error` severity with that.
|
181
|
+
resolvePluginsRelativeTo = null, // ← should be null by default because if it's a string then it suppresses RFC47 feature.
|
182
|
+
rulePaths = [],
|
183
|
+
useEslintrc = true,
|
184
|
+
passOnNoPatterns = false,
|
185
|
+
...unknownOptions
|
186
|
+
}) {
|
187
|
+
const errors = [];
|
188
|
+
const unknownOptionKeys = Object.keys(unknownOptions);
|
189
|
+
|
190
|
+
if (unknownOptionKeys.length >= 1) {
|
191
|
+
errors.push(`Unknown options: ${unknownOptionKeys.join(", ")}`);
|
192
|
+
if (unknownOptionKeys.includes("cacheFile")) {
|
193
|
+
errors.push("'cacheFile' has been removed. Please use the 'cacheLocation' option instead.");
|
194
|
+
}
|
195
|
+
if (unknownOptionKeys.includes("configFile")) {
|
196
|
+
errors.push("'configFile' has been removed. Please use the 'overrideConfigFile' option instead.");
|
197
|
+
}
|
198
|
+
if (unknownOptionKeys.includes("envs")) {
|
199
|
+
errors.push("'envs' has been removed. Please use the 'overrideConfig.env' option instead.");
|
200
|
+
}
|
201
|
+
if (unknownOptionKeys.includes("globals")) {
|
202
|
+
errors.push("'globals' has been removed. Please use the 'overrideConfig.globals' option instead.");
|
203
|
+
}
|
204
|
+
if (unknownOptionKeys.includes("ignorePattern")) {
|
205
|
+
errors.push("'ignorePattern' has been removed. Please use the 'overrideConfig.ignorePatterns' option instead.");
|
206
|
+
}
|
207
|
+
if (unknownOptionKeys.includes("parser")) {
|
208
|
+
errors.push("'parser' has been removed. Please use the 'overrideConfig.parser' option instead.");
|
209
|
+
}
|
210
|
+
if (unknownOptionKeys.includes("parserOptions")) {
|
211
|
+
errors.push("'parserOptions' has been removed. Please use the 'overrideConfig.parserOptions' option instead.");
|
212
|
+
}
|
213
|
+
if (unknownOptionKeys.includes("rules")) {
|
214
|
+
errors.push("'rules' has been removed. Please use the 'overrideConfig.rules' option instead.");
|
215
|
+
}
|
216
|
+
}
|
217
|
+
if (typeof allowInlineConfig !== "boolean") {
|
218
|
+
errors.push("'allowInlineConfig' must be a boolean.");
|
219
|
+
}
|
220
|
+
if (typeof baseConfig !== "object") {
|
221
|
+
errors.push("'baseConfig' must be an object or null.");
|
222
|
+
}
|
223
|
+
if (typeof cache !== "boolean") {
|
224
|
+
errors.push("'cache' must be a boolean.");
|
225
|
+
}
|
226
|
+
if (!isNonEmptyString(cacheLocation)) {
|
227
|
+
errors.push("'cacheLocation' must be a non-empty string.");
|
228
|
+
}
|
229
|
+
if (
|
230
|
+
cacheStrategy !== "metadata" &&
|
231
|
+
cacheStrategy !== "content"
|
232
|
+
) {
|
233
|
+
errors.push("'cacheStrategy' must be any of \"metadata\", \"content\".");
|
234
|
+
}
|
235
|
+
if (!isNonEmptyString(cwd) || !path.isAbsolute(cwd)) {
|
236
|
+
errors.push("'cwd' must be an absolute path.");
|
237
|
+
}
|
238
|
+
if (typeof errorOnUnmatchedPattern !== "boolean") {
|
239
|
+
errors.push("'errorOnUnmatchedPattern' must be a boolean.");
|
240
|
+
}
|
241
|
+
if (!isEmptyArrayOrArrayOfNonEmptyString(extensions) && extensions !== null) {
|
242
|
+
errors.push("'extensions' must be an array of non-empty strings or null.");
|
243
|
+
}
|
244
|
+
if (typeof fix !== "boolean" && typeof fix !== "function") {
|
245
|
+
errors.push("'fix' must be a boolean or a function.");
|
246
|
+
}
|
247
|
+
if (fixTypes !== null && !isFixTypeArray(fixTypes)) {
|
248
|
+
errors.push("'fixTypes' must be an array of any of \"directive\", \"problem\", \"suggestion\", and \"layout\".");
|
249
|
+
}
|
250
|
+
if (typeof globInputPaths !== "boolean") {
|
251
|
+
errors.push("'globInputPaths' must be a boolean.");
|
252
|
+
}
|
253
|
+
if (typeof ignore !== "boolean") {
|
254
|
+
errors.push("'ignore' must be a boolean.");
|
255
|
+
}
|
256
|
+
if (!isNonEmptyString(ignorePath) && ignorePath !== null) {
|
257
|
+
errors.push("'ignorePath' must be a non-empty string or null.");
|
258
|
+
}
|
259
|
+
if (typeof overrideConfig !== "object") {
|
260
|
+
errors.push("'overrideConfig' must be an object or null.");
|
261
|
+
}
|
262
|
+
if (!isNonEmptyString(overrideConfigFile) && overrideConfigFile !== null) {
|
263
|
+
errors.push("'overrideConfigFile' must be a non-empty string or null.");
|
264
|
+
}
|
265
|
+
if (typeof plugins !== "object") {
|
266
|
+
errors.push("'plugins' must be an object or null.");
|
267
|
+
} else if (plugins !== null && Object.keys(plugins).includes("")) {
|
268
|
+
errors.push("'plugins' must not include an empty string.");
|
269
|
+
}
|
270
|
+
if (Array.isArray(plugins)) {
|
271
|
+
errors.push("'plugins' doesn't add plugins to configuration to load. Please use the 'overrideConfig.plugins' option instead.");
|
272
|
+
}
|
273
|
+
if (
|
274
|
+
reportUnusedDisableDirectives !== "error" &&
|
275
|
+
reportUnusedDisableDirectives !== "warn" &&
|
276
|
+
reportUnusedDisableDirectives !== "off" &&
|
277
|
+
reportUnusedDisableDirectives !== null
|
278
|
+
) {
|
279
|
+
errors.push("'reportUnusedDisableDirectives' must be any of \"error\", \"warn\", \"off\", and null.");
|
280
|
+
}
|
281
|
+
if (
|
282
|
+
!isNonEmptyString(resolvePluginsRelativeTo) &&
|
283
|
+
resolvePluginsRelativeTo !== null
|
284
|
+
) {
|
285
|
+
errors.push("'resolvePluginsRelativeTo' must be a non-empty string or null.");
|
286
|
+
}
|
287
|
+
if (!isEmptyArrayOrArrayOfNonEmptyString(rulePaths)) {
|
288
|
+
errors.push("'rulePaths' must be an array of non-empty strings.");
|
289
|
+
}
|
290
|
+
if (typeof useEslintrc !== "boolean") {
|
291
|
+
errors.push("'useEslintrc' must be a boolean.");
|
292
|
+
}
|
293
|
+
if (typeof passOnNoPatterns !== "boolean") {
|
294
|
+
errors.push("'passOnNoPatterns' must be a boolean.");
|
295
|
+
}
|
296
|
+
|
297
|
+
if (errors.length > 0) {
|
298
|
+
throw new ESLintInvalidOptionsError(errors);
|
299
|
+
}
|
300
|
+
|
301
|
+
return {
|
302
|
+
allowInlineConfig,
|
303
|
+
baseConfig,
|
304
|
+
cache,
|
305
|
+
cacheLocation,
|
306
|
+
cacheStrategy,
|
307
|
+
configFile: overrideConfigFile,
|
308
|
+
cwd: path.normalize(cwd),
|
309
|
+
errorOnUnmatchedPattern,
|
310
|
+
extensions,
|
311
|
+
fix,
|
312
|
+
fixTypes,
|
313
|
+
globInputPaths,
|
314
|
+
ignore,
|
315
|
+
ignorePath,
|
316
|
+
reportUnusedDisableDirectives,
|
317
|
+
resolvePluginsRelativeTo,
|
318
|
+
rulePaths,
|
319
|
+
useEslintrc,
|
320
|
+
passOnNoPatterns
|
321
|
+
};
|
322
|
+
}
|
323
|
+
|
324
|
+
/**
|
325
|
+
* Check if a value has one or more properties and that value is not undefined.
|
326
|
+
* @param {any} obj The value to check.
|
327
|
+
* @returns {boolean} `true` if `obj` has one or more properties that that value is not undefined.
|
328
|
+
*/
|
329
|
+
function hasDefinedProperty(obj) {
|
330
|
+
if (typeof obj === "object" && obj !== null) {
|
331
|
+
for (const key in obj) {
|
332
|
+
if (typeof obj[key] !== "undefined") {
|
333
|
+
return true;
|
334
|
+
}
|
335
|
+
}
|
336
|
+
}
|
337
|
+
return false;
|
338
|
+
}
|
339
|
+
|
340
|
+
/**
|
341
|
+
* Create rulesMeta object.
|
342
|
+
* @param {Map<string,Rule>} rules a map of rules from which to generate the object.
|
343
|
+
* @returns {Object} metadata for all enabled rules.
|
344
|
+
*/
|
345
|
+
function createRulesMeta(rules) {
|
346
|
+
return Array.from(rules).reduce((retVal, [id, rule]) => {
|
347
|
+
retVal[id] = rule.meta;
|
348
|
+
return retVal;
|
349
|
+
}, {});
|
350
|
+
}
|
351
|
+
|
352
|
+
/** @type {WeakMap<ExtractedConfig, DeprecatedRuleInfo[]>} */
|
353
|
+
const usedDeprecatedRulesCache = new WeakMap();
|
354
|
+
|
355
|
+
/**
|
356
|
+
* Create used deprecated rule list.
|
357
|
+
* @param {CLIEngine} cliEngine The CLIEngine instance.
|
358
|
+
* @param {string} maybeFilePath The absolute path to a lint target file or `"<text>"`.
|
359
|
+
* @returns {DeprecatedRuleInfo[]} The used deprecated rule list.
|
360
|
+
*/
|
361
|
+
function getOrFindUsedDeprecatedRules(cliEngine, maybeFilePath) {
|
362
|
+
const {
|
363
|
+
configArrayFactory,
|
364
|
+
options: { cwd }
|
365
|
+
} = getCLIEngineInternalSlots(cliEngine);
|
366
|
+
const filePath = path.isAbsolute(maybeFilePath)
|
367
|
+
? maybeFilePath
|
368
|
+
: path.join(cwd, "__placeholder__.js");
|
369
|
+
const configArray = configArrayFactory.getConfigArrayForFile(filePath);
|
370
|
+
const config = configArray.extractConfig(filePath);
|
371
|
+
|
372
|
+
// Most files use the same config, so cache it.
|
373
|
+
if (!usedDeprecatedRulesCache.has(config)) {
|
374
|
+
const pluginRules = configArray.pluginRules;
|
375
|
+
const retv = [];
|
376
|
+
|
377
|
+
for (const [ruleId, ruleConf] of Object.entries(config.rules)) {
|
378
|
+
if (getRuleSeverity(ruleConf) === 0) {
|
379
|
+
continue;
|
380
|
+
}
|
381
|
+
const rule = pluginRules.get(ruleId) || BuiltinRules.get(ruleId);
|
382
|
+
const meta = rule && rule.meta;
|
383
|
+
|
384
|
+
if (meta && meta.deprecated) {
|
385
|
+
retv.push({ ruleId, replacedBy: meta.replacedBy || [] });
|
386
|
+
}
|
387
|
+
}
|
388
|
+
|
389
|
+
usedDeprecatedRulesCache.set(config, Object.freeze(retv));
|
390
|
+
}
|
391
|
+
|
392
|
+
return usedDeprecatedRulesCache.get(config);
|
393
|
+
}
|
394
|
+
|
395
|
+
/**
|
396
|
+
* Processes the linting results generated by a CLIEngine linting report to
|
397
|
+
* match the ESLint class's API.
|
398
|
+
* @param {CLIEngine} cliEngine The CLIEngine instance.
|
399
|
+
* @param {CLIEngineLintReport} report The CLIEngine linting report to process.
|
400
|
+
* @returns {LintResult[]} The processed linting results.
|
401
|
+
*/
|
402
|
+
function processCLIEngineLintReport(cliEngine, { results }) {
|
403
|
+
const descriptor = {
|
404
|
+
configurable: true,
|
405
|
+
enumerable: true,
|
406
|
+
get() {
|
407
|
+
return getOrFindUsedDeprecatedRules(cliEngine, this.filePath);
|
408
|
+
}
|
409
|
+
};
|
410
|
+
|
411
|
+
for (const result of results) {
|
412
|
+
Object.defineProperty(result, "usedDeprecatedRules", descriptor);
|
413
|
+
}
|
414
|
+
|
415
|
+
return results;
|
416
|
+
}
|
417
|
+
|
418
|
+
/**
|
419
|
+
* An Array.prototype.sort() compatible compare function to order results by their file path.
|
420
|
+
* @param {LintResult} a The first lint result.
|
421
|
+
* @param {LintResult} b The second lint result.
|
422
|
+
* @returns {number} An integer representing the order in which the two results should occur.
|
423
|
+
*/
|
424
|
+
function compareResultsByFilePath(a, b) {
|
425
|
+
if (a.filePath < b.filePath) {
|
426
|
+
return -1;
|
427
|
+
}
|
428
|
+
|
429
|
+
if (a.filePath > b.filePath) {
|
430
|
+
return 1;
|
431
|
+
}
|
432
|
+
|
433
|
+
return 0;
|
434
|
+
}
|
435
|
+
|
436
|
+
/**
|
437
|
+
* Main API.
|
438
|
+
*/
|
439
|
+
class LegacyESLint {
|
440
|
+
|
441
|
+
/**
|
442
|
+
* The type of configuration used by this class.
|
443
|
+
* @type {string}
|
444
|
+
*/
|
445
|
+
static configType = "eslintrc";
|
446
|
+
|
447
|
+
/**
|
448
|
+
* Creates a new instance of the main ESLint API.
|
449
|
+
* @param {LegacyESLintOptions} options The options for this instance.
|
450
|
+
*/
|
451
|
+
constructor(options = {}) {
|
452
|
+
const processedOptions = processOptions(options);
|
453
|
+
const cliEngine = new CLIEngine(processedOptions, { preloadedPlugins: options.plugins });
|
454
|
+
const {
|
455
|
+
configArrayFactory,
|
456
|
+
lastConfigArrays
|
457
|
+
} = getCLIEngineInternalSlots(cliEngine);
|
458
|
+
let updated = false;
|
459
|
+
|
460
|
+
/*
|
461
|
+
* Address `overrideConfig` to set override config.
|
462
|
+
* Operate the `configArrayFactory` internal slot directly because this
|
463
|
+
* functionality doesn't exist as the public API of CLIEngine.
|
464
|
+
*/
|
465
|
+
if (hasDefinedProperty(options.overrideConfig)) {
|
466
|
+
configArrayFactory.setOverrideConfig(options.overrideConfig);
|
467
|
+
updated = true;
|
468
|
+
}
|
469
|
+
|
470
|
+
// Update caches.
|
471
|
+
if (updated) {
|
472
|
+
configArrayFactory.clearCache();
|
473
|
+
lastConfigArrays[0] = configArrayFactory.getConfigArrayForFile();
|
474
|
+
}
|
475
|
+
|
476
|
+
// Initialize private properties.
|
477
|
+
privateMembersMap.set(this, {
|
478
|
+
cliEngine,
|
479
|
+
options: processedOptions
|
480
|
+
});
|
481
|
+
}
|
482
|
+
|
483
|
+
/**
|
484
|
+
* The version text.
|
485
|
+
* @type {string}
|
486
|
+
*/
|
487
|
+
static get version() {
|
488
|
+
return version;
|
489
|
+
}
|
490
|
+
|
491
|
+
/**
|
492
|
+
* Outputs fixes from the given results to files.
|
493
|
+
* @param {LintResult[]} results The lint results.
|
494
|
+
* @returns {Promise<void>} Returns a promise that is used to track side effects.
|
495
|
+
*/
|
496
|
+
static async outputFixes(results) {
|
497
|
+
if (!Array.isArray(results)) {
|
498
|
+
throw new Error("'results' must be an array");
|
499
|
+
}
|
500
|
+
|
501
|
+
await Promise.all(
|
502
|
+
results
|
503
|
+
.filter(result => {
|
504
|
+
if (typeof result !== "object" || result === null) {
|
505
|
+
throw new Error("'results' must include only objects");
|
506
|
+
}
|
507
|
+
return (
|
508
|
+
typeof result.output === "string" &&
|
509
|
+
path.isAbsolute(result.filePath)
|
510
|
+
);
|
511
|
+
})
|
512
|
+
.map(r => writeFile(r.filePath, r.output))
|
513
|
+
);
|
514
|
+
}
|
515
|
+
|
516
|
+
/**
|
517
|
+
* Returns results that only contains errors.
|
518
|
+
* @param {LintResult[]} results The results to filter.
|
519
|
+
* @returns {LintResult[]} The filtered results.
|
520
|
+
*/
|
521
|
+
static getErrorResults(results) {
|
522
|
+
return CLIEngine.getErrorResults(results);
|
523
|
+
}
|
524
|
+
|
525
|
+
/**
|
526
|
+
* Returns meta objects for each rule represented in the lint results.
|
527
|
+
* @param {LintResult[]} results The results to fetch rules meta for.
|
528
|
+
* @returns {Object} A mapping of ruleIds to rule meta objects.
|
529
|
+
*/
|
530
|
+
getRulesMetaForResults(results) {
|
531
|
+
|
532
|
+
const resultRuleIds = new Set();
|
533
|
+
|
534
|
+
// first gather all ruleIds from all results
|
535
|
+
|
536
|
+
for (const result of results) {
|
537
|
+
for (const { ruleId } of result.messages) {
|
538
|
+
resultRuleIds.add(ruleId);
|
539
|
+
}
|
540
|
+
for (const { ruleId } of result.suppressedMessages) {
|
541
|
+
resultRuleIds.add(ruleId);
|
542
|
+
}
|
543
|
+
}
|
544
|
+
|
545
|
+
// create a map of all rules in the results
|
546
|
+
|
547
|
+
const { cliEngine } = privateMembersMap.get(this);
|
548
|
+
const rules = cliEngine.getRules();
|
549
|
+
const resultRules = new Map();
|
550
|
+
|
551
|
+
for (const [ruleId, rule] of rules) {
|
552
|
+
if (resultRuleIds.has(ruleId)) {
|
553
|
+
resultRules.set(ruleId, rule);
|
554
|
+
}
|
555
|
+
}
|
556
|
+
|
557
|
+
return createRulesMeta(resultRules);
|
558
|
+
|
559
|
+
}
|
560
|
+
|
561
|
+
/**
|
562
|
+
* Executes the current configuration on an array of file and directory names.
|
563
|
+
* @param {string[]} patterns An array of file and directory names.
|
564
|
+
* @returns {Promise<LintResult[]>} The results of linting the file patterns given.
|
565
|
+
*/
|
566
|
+
async lintFiles(patterns) {
|
567
|
+
const { cliEngine, options } = privateMembersMap.get(this);
|
568
|
+
|
569
|
+
if (options.passOnNoPatterns && (patterns === "" || (Array.isArray(patterns) && patterns.length === 0))) {
|
570
|
+
return [];
|
571
|
+
}
|
572
|
+
|
573
|
+
if (!isNonEmptyString(patterns) && !isArrayOfNonEmptyString(patterns)) {
|
574
|
+
throw new Error("'patterns' must be a non-empty string or an array of non-empty strings");
|
575
|
+
}
|
576
|
+
|
577
|
+
return processCLIEngineLintReport(
|
578
|
+
cliEngine,
|
579
|
+
cliEngine.executeOnFiles(patterns)
|
580
|
+
);
|
581
|
+
}
|
582
|
+
|
583
|
+
/**
|
584
|
+
* Executes the current configuration on text.
|
585
|
+
* @param {string} code A string of JavaScript code to lint.
|
586
|
+
* @param {Object} [options] The options.
|
587
|
+
* @param {string} [options.filePath] The path to the file of the source code.
|
588
|
+
* @param {boolean} [options.warnIgnored] When set to true, warn if given filePath is an ignored path.
|
589
|
+
* @returns {Promise<LintResult[]>} The results of linting the string of code given.
|
590
|
+
*/
|
591
|
+
async lintText(code, options = {}) {
|
592
|
+
if (typeof code !== "string") {
|
593
|
+
throw new Error("'code' must be a string");
|
594
|
+
}
|
595
|
+
if (typeof options !== "object") {
|
596
|
+
throw new Error("'options' must be an object, null, or undefined");
|
597
|
+
}
|
598
|
+
const {
|
599
|
+
filePath,
|
600
|
+
warnIgnored = false,
|
601
|
+
...unknownOptions
|
602
|
+
} = options || {};
|
603
|
+
|
604
|
+
const unknownOptionKeys = Object.keys(unknownOptions);
|
605
|
+
|
606
|
+
if (unknownOptionKeys.length > 0) {
|
607
|
+
throw new Error(`'options' must not include the unknown option(s): ${unknownOptionKeys.join(", ")}`);
|
608
|
+
}
|
609
|
+
|
610
|
+
if (filePath !== void 0 && !isNonEmptyString(filePath)) {
|
611
|
+
throw new Error("'options.filePath' must be a non-empty string or undefined");
|
612
|
+
}
|
613
|
+
if (typeof warnIgnored !== "boolean") {
|
614
|
+
throw new Error("'options.warnIgnored' must be a boolean or undefined");
|
615
|
+
}
|
616
|
+
|
617
|
+
const { cliEngine } = privateMembersMap.get(this);
|
618
|
+
|
619
|
+
return processCLIEngineLintReport(
|
620
|
+
cliEngine,
|
621
|
+
cliEngine.executeOnText(code, filePath, warnIgnored)
|
622
|
+
);
|
623
|
+
}
|
624
|
+
|
625
|
+
/**
|
626
|
+
* Returns the formatter representing the given formatter name.
|
627
|
+
* @param {string} [name] The name of the formatter to load.
|
628
|
+
* The following values are allowed:
|
629
|
+
* - `undefined` ... Load `stylish` builtin formatter.
|
630
|
+
* - A builtin formatter name ... Load the builtin formatter.
|
631
|
+
* - A third-party formatter name:
|
632
|
+
* - `foo` → `eslint-formatter-foo`
|
633
|
+
* - `@foo` → `@foo/eslint-formatter`
|
634
|
+
* - `@foo/bar` → `@foo/eslint-formatter-bar`
|
635
|
+
* - A file path ... Load the file.
|
636
|
+
* @returns {Promise<LoadedFormatter>} A promise resolving to the formatter object.
|
637
|
+
* This promise will be rejected if the given formatter was not found or not
|
638
|
+
* a function.
|
639
|
+
*/
|
640
|
+
async loadFormatter(name = "stylish") {
|
641
|
+
if (typeof name !== "string") {
|
642
|
+
throw new Error("'name' must be a string");
|
643
|
+
}
|
644
|
+
|
645
|
+
const { cliEngine, options } = privateMembersMap.get(this);
|
646
|
+
const formatter = cliEngine.getFormatter(name);
|
647
|
+
|
648
|
+
if (typeof formatter !== "function") {
|
649
|
+
throw new Error(`Formatter must be a function, but got a ${typeof formatter}.`);
|
650
|
+
}
|
651
|
+
|
652
|
+
return {
|
653
|
+
|
654
|
+
/**
|
655
|
+
* The main formatter method.
|
656
|
+
* @param {LintResult[]} results The lint results to format.
|
657
|
+
* @param {ResultsMeta} resultsMeta Warning count and max threshold.
|
658
|
+
* @returns {string | Promise<string>} The formatted lint results.
|
659
|
+
*/
|
660
|
+
format(results, resultsMeta) {
|
661
|
+
let rulesMeta = null;
|
662
|
+
|
663
|
+
results.sort(compareResultsByFilePath);
|
664
|
+
|
665
|
+
return formatter(results, {
|
666
|
+
...resultsMeta,
|
667
|
+
get cwd() {
|
668
|
+
return options.cwd;
|
669
|
+
},
|
670
|
+
get rulesMeta() {
|
671
|
+
if (!rulesMeta) {
|
672
|
+
rulesMeta = createRulesMeta(cliEngine.getRules());
|
673
|
+
}
|
674
|
+
|
675
|
+
return rulesMeta;
|
676
|
+
}
|
677
|
+
});
|
678
|
+
}
|
679
|
+
};
|
680
|
+
}
|
681
|
+
|
682
|
+
/**
|
683
|
+
* Returns a configuration object for the given file based on the CLI options.
|
684
|
+
* This is the same logic used by the ESLint CLI executable to determine
|
685
|
+
* configuration for each file it processes.
|
686
|
+
* @param {string} filePath The path of the file to retrieve a config object for.
|
687
|
+
* @returns {Promise<ConfigData>} A configuration object for the file.
|
688
|
+
*/
|
689
|
+
async calculateConfigForFile(filePath) {
|
690
|
+
if (!isNonEmptyString(filePath)) {
|
691
|
+
throw new Error("'filePath' must be a non-empty string");
|
692
|
+
}
|
693
|
+
const { cliEngine } = privateMembersMap.get(this);
|
694
|
+
|
695
|
+
return cliEngine.getConfigForFile(filePath);
|
696
|
+
}
|
697
|
+
|
698
|
+
/**
|
699
|
+
* Checks if a given path is ignored by ESLint.
|
700
|
+
* @param {string} filePath The path of the file to check.
|
701
|
+
* @returns {Promise<boolean>} Whether or not the given path is ignored.
|
702
|
+
*/
|
703
|
+
async isPathIgnored(filePath) {
|
704
|
+
if (!isNonEmptyString(filePath)) {
|
705
|
+
throw new Error("'filePath' must be a non-empty string");
|
706
|
+
}
|
707
|
+
const { cliEngine } = privateMembersMap.get(this);
|
708
|
+
|
709
|
+
return cliEngine.isPathIgnored(filePath);
|
710
|
+
}
|
711
|
+
}
|
712
|
+
|
713
|
+
//------------------------------------------------------------------------------
|
714
|
+
// Public Interface
|
715
|
+
//------------------------------------------------------------------------------
|
716
|
+
|
717
|
+
module.exports = {
|
718
|
+
LegacyESLint,
|
719
|
+
|
720
|
+
/**
|
721
|
+
* Get the private class members of a given ESLint instance for tests.
|
722
|
+
* @param {ESLint} instance The ESLint instance to get.
|
723
|
+
* @returns {ESLintPrivateMembers} The instance's private class members.
|
724
|
+
*/
|
725
|
+
getESLintPrivateMembers(instance) {
|
726
|
+
return privateMembersMap.get(instance);
|
727
|
+
}
|
728
|
+
};
|