eslint 9.9.1 → 9.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/lib/config/config.js +278 -0
- package/lib/config/flat-config-array.js +3 -204
- package/lib/config/flat-config-helpers.js +1 -5
- package/lib/languages/js/source-code/source-code.js +29 -145
- package/lib/linter/apply-disable-directives.js +17 -28
- package/lib/linter/file-context.js +134 -0
- package/lib/linter/linter.js +182 -150
- package/lib/linter/vfile.js +8 -1
- package/lib/rules/id-length.js +8 -0
- package/lib/rules/no-invalid-regexp.js +34 -18
- package/lib/rules/no-useless-constructor.js +18 -2
- package/lib/rules/require-unicode-regexp.js +95 -14
- package/lib/rules/utils/regular-expressions.js +11 -3
- package/lib/services/processor-service.js +109 -0
- package/lib/shared/types.js +1 -1
- package/lib/types/index.d.ts +1668 -0
- package/lib/types/rules/best-practices.d.ts +1083 -0
- package/lib/types/rules/deprecated.d.ts +294 -0
- package/lib/types/rules/ecmascript-6.d.ts +607 -0
- package/lib/types/rules/index.d.ts +50 -0
- package/lib/types/rules/node-commonjs.d.ts +160 -0
- package/lib/types/rules/possible-errors.d.ts +631 -0
- package/lib/types/rules/strict-mode.d.ts +38 -0
- package/lib/types/rules/stylistic-issues.d.ts +1942 -0
- package/lib/types/rules/variables.d.ts +221 -0
- package/lib/types/universal.d.ts +6 -0
- package/lib/types/use-at-your-own-risk.d.ts +85 -0
- package/lib/universal.js +10 -0
- package/package.json +29 -10
- package/lib/linter/config-comment-parser.js +0 -169
@@ -10,7 +10,7 @@
|
|
10
10
|
|
11
11
|
const RegExpValidator = require("@eslint-community/regexpp").RegExpValidator;
|
12
12
|
const validator = new RegExpValidator();
|
13
|
-
const validFlags =
|
13
|
+
const validFlags = "dgimsuvy";
|
14
14
|
const undefined1 = void 0;
|
15
15
|
|
16
16
|
//------------------------------------------------------------------------------
|
@@ -49,13 +49,13 @@ module.exports = {
|
|
49
49
|
create(context) {
|
50
50
|
|
51
51
|
const options = context.options[0];
|
52
|
-
let allowedFlags =
|
52
|
+
let allowedFlags = [];
|
53
53
|
|
54
54
|
if (options && options.allowConstructorFlags) {
|
55
|
-
const temp = options.allowConstructorFlags.join("").replace(validFlags, "");
|
55
|
+
const temp = options.allowConstructorFlags.join("").replace(new RegExp(`[${validFlags}]`, "gu"), "");
|
56
56
|
|
57
57
|
if (temp) {
|
58
|
-
allowedFlags = new
|
58
|
+
allowedFlags = [...new Set(temp)];
|
59
59
|
}
|
60
60
|
}
|
61
61
|
|
@@ -125,16 +125,19 @@ module.exports = {
|
|
125
125
|
/**
|
126
126
|
* Check syntax error in a given flags.
|
127
127
|
* @param {string|null} flags The RegExp flags to validate.
|
128
|
+
* @param {string|null} flagsToCheck The RegExp invalid flags.
|
129
|
+
* @param {string} allFlags all valid and allowed flags.
|
128
130
|
* @returns {string|null} The syntax error.
|
129
131
|
*/
|
130
|
-
function validateRegExpFlags(flags) {
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
132
|
+
function validateRegExpFlags(flags, flagsToCheck, allFlags) {
|
133
|
+
const duplicateFlags = [];
|
134
|
+
|
135
|
+
if (typeof flagsToCheck === "string") {
|
136
|
+
for (const flag of flagsToCheck) {
|
137
|
+
if (allFlags.includes(flag)) {
|
138
|
+
duplicateFlags.push(flag);
|
139
|
+
}
|
140
|
+
}
|
138
141
|
}
|
139
142
|
|
140
143
|
/*
|
@@ -142,10 +145,19 @@ module.exports = {
|
|
142
145
|
* but this rule may check only the flag when the pattern is unidentifiable, so check it here.
|
143
146
|
* https://tc39.es/ecma262/multipage/text-processing.html#sec-parsepattern
|
144
147
|
*/
|
145
|
-
if (flags.includes("u") && flags.includes("v")) {
|
148
|
+
if (flags && flags.includes("u") && flags.includes("v")) {
|
146
149
|
return "Regex 'u' and 'v' flags cannot be used together";
|
147
150
|
}
|
148
|
-
|
151
|
+
|
152
|
+
if (duplicateFlags.length > 0) {
|
153
|
+
return `Duplicate flags ('${duplicateFlags.join("")}') supplied to RegExp constructor`;
|
154
|
+
}
|
155
|
+
|
156
|
+
if (!flagsToCheck) {
|
157
|
+
return null;
|
158
|
+
}
|
159
|
+
|
160
|
+
return `Invalid flags supplied to RegExp constructor '${flagsToCheck}'`;
|
149
161
|
}
|
150
162
|
|
151
163
|
return {
|
@@ -154,13 +166,17 @@ module.exports = {
|
|
154
166
|
return;
|
155
167
|
}
|
156
168
|
|
157
|
-
|
169
|
+
const flags = getFlags(node);
|
170
|
+
let flagsToCheck = flags;
|
171
|
+
const allFlags = allowedFlags.length > 0 ? validFlags.split("").concat(allowedFlags) : validFlags.split("");
|
158
172
|
|
159
|
-
if (flags
|
160
|
-
|
173
|
+
if (flags) {
|
174
|
+
allFlags.forEach(flag => {
|
175
|
+
flagsToCheck = flagsToCheck.replace(flag, "");
|
176
|
+
});
|
161
177
|
}
|
162
178
|
|
163
|
-
let message = validateRegExpFlags(flags);
|
179
|
+
let message = validateRegExpFlags(flags, flagsToCheck, allFlags);
|
164
180
|
|
165
181
|
if (message) {
|
166
182
|
report(node, message);
|
@@ -4,6 +4,8 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
+
const astUtils = require("./utils/ast-utils");
|
8
|
+
|
7
9
|
//------------------------------------------------------------------------------
|
8
10
|
// Helpers
|
9
11
|
//------------------------------------------------------------------------------
|
@@ -143,10 +145,13 @@ module.exports = {
|
|
143
145
|
url: "https://eslint.org/docs/latest/rules/no-useless-constructor"
|
144
146
|
},
|
145
147
|
|
148
|
+
hasSuggestions: true,
|
149
|
+
|
146
150
|
schema: [],
|
147
151
|
|
148
152
|
messages: {
|
149
|
-
noUselessConstructor: "Useless constructor."
|
153
|
+
noUselessConstructor: "Useless constructor.",
|
154
|
+
removeConstructor: "Remove the constructor."
|
150
155
|
}
|
151
156
|
},
|
152
157
|
|
@@ -177,7 +182,18 @@ module.exports = {
|
|
177
182
|
if (superClass ? isRedundantSuperCall(body, ctorParams) : (body.length === 0)) {
|
178
183
|
context.report({
|
179
184
|
node,
|
180
|
-
messageId: "noUselessConstructor"
|
185
|
+
messageId: "noUselessConstructor",
|
186
|
+
suggest: [
|
187
|
+
{
|
188
|
+
messageId: "removeConstructor",
|
189
|
+
*fix(fixer) {
|
190
|
+
const nextToken = context.sourceCode.getTokenAfter(node);
|
191
|
+
const addSemiColon = nextToken.type === "Punctuator" && nextToken.value === "[" && astUtils.needsPrecedingSemicolon(context.sourceCode, node);
|
192
|
+
|
193
|
+
yield fixer.replaceText(node, addSemiColon ? ";" : "");
|
194
|
+
}
|
195
|
+
}
|
196
|
+
]
|
181
197
|
});
|
182
198
|
}
|
183
199
|
}
|
@@ -18,6 +18,26 @@ const {
|
|
18
18
|
const astUtils = require("./utils/ast-utils.js");
|
19
19
|
const { isValidWithUnicodeFlag } = require("./utils/regular-expressions");
|
20
20
|
|
21
|
+
/**
|
22
|
+
* Checks whether the flag configuration should be treated as a missing flag.
|
23
|
+
* @param {"u"|"v"|undefined} requireFlag A particular flag to require
|
24
|
+
* @param {string} flags The regex flags
|
25
|
+
* @returns {boolean} Whether the flag configuration results in a missing flag.
|
26
|
+
*/
|
27
|
+
function checkFlags(requireFlag, flags) {
|
28
|
+
let missingFlag;
|
29
|
+
|
30
|
+
if (requireFlag === "v") {
|
31
|
+
missingFlag = !flags.includes("v");
|
32
|
+
} else if (requireFlag === "u") {
|
33
|
+
missingFlag = !flags.includes("u");
|
34
|
+
} else {
|
35
|
+
missingFlag = !flags.includes("u") && !flags.includes("v");
|
36
|
+
}
|
37
|
+
|
38
|
+
return missingFlag;
|
39
|
+
}
|
40
|
+
|
21
41
|
//------------------------------------------------------------------------------
|
22
42
|
// Rule Definition
|
23
43
|
//------------------------------------------------------------------------------
|
@@ -37,31 +57,65 @@ module.exports = {
|
|
37
57
|
|
38
58
|
messages: {
|
39
59
|
addUFlag: "Add the 'u' flag.",
|
40
|
-
|
60
|
+
addVFlag: "Add the 'v' flag.",
|
61
|
+
requireUFlag: "Use the 'u' flag.",
|
62
|
+
requireVFlag: "Use the 'v' flag."
|
41
63
|
},
|
42
64
|
|
43
|
-
schema: [
|
65
|
+
schema: [
|
66
|
+
{
|
67
|
+
type: "object",
|
68
|
+
properties: {
|
69
|
+
requireFlag: {
|
70
|
+
enum: ["u", "v"]
|
71
|
+
}
|
72
|
+
},
|
73
|
+
additionalProperties: false
|
74
|
+
}
|
75
|
+
]
|
44
76
|
},
|
45
77
|
|
46
78
|
create(context) {
|
47
79
|
|
48
80
|
const sourceCode = context.sourceCode;
|
49
81
|
|
82
|
+
const {
|
83
|
+
requireFlag
|
84
|
+
} = context.options[0] ?? {};
|
85
|
+
|
50
86
|
return {
|
51
87
|
"Literal[regex]"(node) {
|
52
88
|
const flags = node.regex.flags || "";
|
53
89
|
|
54
|
-
|
90
|
+
const missingFlag = checkFlags(requireFlag, flags);
|
91
|
+
|
92
|
+
if (missingFlag) {
|
55
93
|
context.report({
|
56
|
-
messageId: "requireUFlag",
|
94
|
+
messageId: requireFlag === "v" ? "requireVFlag" : "requireUFlag",
|
57
95
|
node,
|
58
|
-
suggest: isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, node.regex.pattern)
|
96
|
+
suggest: isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, node.regex.pattern, requireFlag)
|
59
97
|
? [
|
60
98
|
{
|
61
99
|
fix(fixer) {
|
62
|
-
|
100
|
+
const replaceFlag = requireFlag ?? "u";
|
101
|
+
const regex = sourceCode.getText(node);
|
102
|
+
const slashPos = regex.lastIndexOf("/");
|
103
|
+
|
104
|
+
if (requireFlag) {
|
105
|
+
const flag = requireFlag === "u" ? "v" : "u";
|
106
|
+
|
107
|
+
if (regex.includes(flag, slashPos)) {
|
108
|
+
return fixer.replaceText(
|
109
|
+
node,
|
110
|
+
regex.slice(0, slashPos) +
|
111
|
+
regex.slice(slashPos).replace(flag, requireFlag)
|
112
|
+
);
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
return fixer.insertTextAfter(node, replaceFlag);
|
63
117
|
},
|
64
|
-
messageId: "addUFlag"
|
118
|
+
messageId: requireFlag === "v" ? "addVFlag" : "addUFlag"
|
65
119
|
}
|
66
120
|
]
|
67
121
|
: null
|
@@ -85,22 +139,49 @@ module.exports = {
|
|
85
139
|
const pattern = getStringIfConstant(patternNode, scope);
|
86
140
|
const flags = getStringIfConstant(flagsNode, scope);
|
87
141
|
|
88
|
-
|
142
|
+
let missingFlag = !flagsNode;
|
143
|
+
|
144
|
+
if (typeof flags === "string") {
|
145
|
+
missingFlag = checkFlags(requireFlag, flags);
|
146
|
+
}
|
147
|
+
|
148
|
+
if (missingFlag) {
|
89
149
|
context.report({
|
90
|
-
messageId: "requireUFlag",
|
150
|
+
messageId: requireFlag === "v" ? "requireVFlag" : "requireUFlag",
|
91
151
|
node: refNode,
|
92
|
-
suggest: typeof pattern === "string" && isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, pattern)
|
152
|
+
suggest: typeof pattern === "string" && isValidWithUnicodeFlag(context.languageOptions.ecmaVersion, pattern, requireFlag)
|
93
153
|
? [
|
94
154
|
{
|
95
155
|
fix(fixer) {
|
156
|
+
const replaceFlag = requireFlag ?? "u";
|
157
|
+
|
96
158
|
if (flagsNode) {
|
97
159
|
if ((flagsNode.type === "Literal" && typeof flagsNode.value === "string") || flagsNode.type === "TemplateLiteral") {
|
98
160
|
const flagsNodeText = sourceCode.getText(flagsNode);
|
161
|
+
const flag = requireFlag === "u" ? "v" : "u";
|
162
|
+
|
163
|
+
if (flags.includes(flag)) {
|
164
|
+
|
165
|
+
// Avoid replacing "u" in escapes like `\uXXXX`
|
166
|
+
if (flagsNode.type === "Literal" && flagsNode.raw.includes("\\")) {
|
167
|
+
return null;
|
168
|
+
}
|
169
|
+
|
170
|
+
// Avoid replacing "u" in expressions like "`${regularFlags}g`"
|
171
|
+
if (flagsNode.type === "TemplateLiteral" && (
|
172
|
+
flagsNode.expressions.length ||
|
173
|
+
flagsNode.quasis.some(({ value: { raw } }) => raw.includes("\\"))
|
174
|
+
)) {
|
175
|
+
return null;
|
176
|
+
}
|
177
|
+
|
178
|
+
return fixer.replaceText(flagsNode, flagsNodeText.replace(flag, replaceFlag));
|
179
|
+
}
|
99
180
|
|
100
181
|
return fixer.replaceText(flagsNode, [
|
101
182
|
flagsNodeText.slice(0, flagsNodeText.length - 1),
|
102
183
|
flagsNodeText.slice(flagsNodeText.length - 1)
|
103
|
-
].join(
|
184
|
+
].join(replaceFlag));
|
104
185
|
}
|
105
186
|
|
106
187
|
// We intentionally don't suggest concatenating + "u" to non-literals
|
@@ -112,11 +193,11 @@ module.exports = {
|
|
112
193
|
return fixer.insertTextAfter(
|
113
194
|
penultimateToken,
|
114
195
|
astUtils.isCommaToken(penultimateToken)
|
115
|
-
?
|
116
|
-
:
|
196
|
+
? ` "${replaceFlag}",`
|
197
|
+
: `, "${replaceFlag}"`
|
117
198
|
);
|
118
199
|
},
|
119
|
-
messageId: "addUFlag"
|
200
|
+
messageId: requireFlag === "v" ? "addVFlag" : "addUFlag"
|
120
201
|
}
|
121
202
|
]
|
122
203
|
: null
|
@@ -14,12 +14,16 @@ const REGEXPP_LATEST_ECMA_VERSION = 2025;
|
|
14
14
|
* Checks if the given regular expression pattern would be valid with the `u` flag.
|
15
15
|
* @param {number} ecmaVersion ECMAScript version to parse in.
|
16
16
|
* @param {string} pattern The regular expression pattern to verify.
|
17
|
+
* @param {"u"|"v"} flag The type of Unicode flag
|
17
18
|
* @returns {boolean} `true` if the pattern would be valid with the `u` flag.
|
18
19
|
* `false` if the pattern would be invalid with the `u` flag or the configured
|
19
20
|
* ecmaVersion doesn't support the `u` flag.
|
20
21
|
*/
|
21
|
-
function isValidWithUnicodeFlag(ecmaVersion, pattern) {
|
22
|
-
if (ecmaVersion <= 5) { // ecmaVersion <= 5 doesn't support the 'u' flag
|
22
|
+
function isValidWithUnicodeFlag(ecmaVersion, pattern, flag = "u") {
|
23
|
+
if (flag === "u" && ecmaVersion <= 5) { // ecmaVersion <= 5 doesn't support the 'u' flag
|
24
|
+
return false;
|
25
|
+
}
|
26
|
+
if (flag === "v" && ecmaVersion <= 2023) {
|
23
27
|
return false;
|
24
28
|
}
|
25
29
|
|
@@ -28,7 +32,11 @@ function isValidWithUnicodeFlag(ecmaVersion, pattern) {
|
|
28
32
|
});
|
29
33
|
|
30
34
|
try {
|
31
|
-
validator.validatePattern(pattern, void 0, void 0,
|
35
|
+
validator.validatePattern(pattern, void 0, void 0, flag === "u" ? {
|
36
|
+
unicode: /* uFlag = */ true
|
37
|
+
} : {
|
38
|
+
unicodeSets: true
|
39
|
+
});
|
32
40
|
} catch {
|
33
41
|
return false;
|
34
42
|
}
|
@@ -0,0 +1,109 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview ESLint Processor Service
|
3
|
+
* @author Nicholas C. Zakas
|
4
|
+
*/
|
5
|
+
/* eslint class-methods-use-this: off -- Anticipate future constructor arguments. */
|
6
|
+
|
7
|
+
"use strict";
|
8
|
+
|
9
|
+
//-----------------------------------------------------------------------------
|
10
|
+
// Requirements
|
11
|
+
//-----------------------------------------------------------------------------
|
12
|
+
|
13
|
+
const path = require("node:path");
|
14
|
+
const { VFile } = require("../linter/vfile.js");
|
15
|
+
|
16
|
+
//-----------------------------------------------------------------------------
|
17
|
+
// Types
|
18
|
+
//-----------------------------------------------------------------------------
|
19
|
+
|
20
|
+
/** @typedef {import("../shared/types.js").LintMessage} LintMessage */
|
21
|
+
/** @typedef {import("../linter/vfile.js").VFile} VFile */
|
22
|
+
/** @typedef {import("@eslint/core").Language} Language */
|
23
|
+
/** @typedef {import("@eslint/core").LanguageOptions} LanguageOptions */
|
24
|
+
/** @typedef {import("eslint").Linter.Processor} Processor */
|
25
|
+
|
26
|
+
//-----------------------------------------------------------------------------
|
27
|
+
// Exports
|
28
|
+
//-----------------------------------------------------------------------------
|
29
|
+
|
30
|
+
/**
|
31
|
+
* The service that applies processors to files.
|
32
|
+
*/
|
33
|
+
class ProcessorService {
|
34
|
+
|
35
|
+
/**
|
36
|
+
* Preprocesses the given file synchronously.
|
37
|
+
* @param {VFile} file The file to preprocess.
|
38
|
+
* @param {{processor:Processor}} config The configuration to use.
|
39
|
+
* @returns {{ok:boolean, files?: Array<VFile>, errors?: Array<LintMessage>}} An array of preprocessed files or errors.
|
40
|
+
* @throws {Error} If the preprocessor returns a promise.
|
41
|
+
*/
|
42
|
+
preprocessSync(file, config) {
|
43
|
+
|
44
|
+
const { processor } = config;
|
45
|
+
let blocks;
|
46
|
+
|
47
|
+
try {
|
48
|
+
blocks = processor.preprocess(file.rawBody, file.path);
|
49
|
+
} catch (ex) {
|
50
|
+
|
51
|
+
// If the message includes a leading line number, strip it:
|
52
|
+
const message = `Preprocessing error: ${ex.message.replace(/^line \d+:/iu, "").trim()}`;
|
53
|
+
|
54
|
+
return {
|
55
|
+
ok: false,
|
56
|
+
errors: [
|
57
|
+
{
|
58
|
+
ruleId: null,
|
59
|
+
fatal: true,
|
60
|
+
severity: 2,
|
61
|
+
message,
|
62
|
+
line: ex.lineNumber,
|
63
|
+
column: ex.column,
|
64
|
+
nodeType: null
|
65
|
+
}
|
66
|
+
]
|
67
|
+
};
|
68
|
+
}
|
69
|
+
|
70
|
+
if (typeof blocks.then === "function") {
|
71
|
+
throw new Error("Unsupported: Preprocessor returned a promise.");
|
72
|
+
}
|
73
|
+
|
74
|
+
return {
|
75
|
+
ok: true,
|
76
|
+
files: blocks.map((block, i) => {
|
77
|
+
|
78
|
+
// Legacy behavior: return the block as a string
|
79
|
+
if (typeof block === "string") {
|
80
|
+
return block;
|
81
|
+
}
|
82
|
+
|
83
|
+
const filePath = path.join(file.path, `${i}_${block.filename}`);
|
84
|
+
|
85
|
+
return new VFile(filePath, block.text, {
|
86
|
+
physicalPath: file.physicalPath
|
87
|
+
});
|
88
|
+
})
|
89
|
+
};
|
90
|
+
|
91
|
+
}
|
92
|
+
|
93
|
+
/**
|
94
|
+
* Postprocesses the given messages synchronously.
|
95
|
+
* @param {VFile} file The file to postprocess.
|
96
|
+
* @param {LintMessage[][]} messages The messages to postprocess.
|
97
|
+
* @param {{processor:Processor}} config The configuration to use.
|
98
|
+
* @returns {LintMessage[]} The postprocessed messages.
|
99
|
+
*/
|
100
|
+
postprocessSync(file, messages, config) {
|
101
|
+
|
102
|
+
const { processor } = config;
|
103
|
+
|
104
|
+
return processor.postprocess(messages, file.path);
|
105
|
+
}
|
106
|
+
|
107
|
+
}
|
108
|
+
|
109
|
+
module.exports = { ProcessorService };
|
package/lib/shared/types.js
CHANGED
@@ -245,6 +245,6 @@ module.exports = {};
|
|
245
245
|
* A formatter function.
|
246
246
|
* @callback FormatterFunction
|
247
247
|
* @param {LintResult[]} results The list of linting results.
|
248
|
-
* @param {{cwd: string, maxWarningsExceeded?: MaxWarningsExceeded, rulesMeta: Record<string, RuleMeta>}}
|
248
|
+
* @param {{cwd: string, maxWarningsExceeded?: MaxWarningsExceeded, rulesMeta: Record<string, RuleMeta>}} context A context object.
|
249
249
|
* @returns {string | Promise<string>} Formatted text.
|
250
250
|
*/
|