eslint 8.26.0 → 8.27.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/lib/cli-engine/formatters/html.js +76 -51
- package/lib/eslint/eslint-helpers.js +280 -93
- package/lib/eslint/flat-eslint.js +36 -12
- package/lib/rules/comma-dangle.js +2 -2
- package/lib/rules/func-name-matching.js +2 -2
- package/lib/rules/index.js +2 -0
- package/lib/rules/no-empty-static-block.js +47 -0
- package/lib/rules/no-empty.js +19 -2
- package/lib/rules/no-misleading-character-class.js +4 -4
- package/lib/rules/no-new-native-nonconstructor.js +64 -0
- package/lib/rules/prefer-regex-literals.js +4 -4
- package/package.json +1 -1
@@ -43,85 +43,110 @@ function pageTemplate(it) {
|
|
43
43
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PScwIDAgMjk0LjgyNSAyNTguOTgyJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPg0KPHBhdGggZmlsbD0nIzgwODBGMicgZD0nTTk3LjAyMSw5OS4wMTZsNDguNDMyLTI3Ljk2MmMxLjIxMi0wLjcsMi43MDYtMC43LDMuOTE4LDBsNDguNDMzLDI3Ljk2MiBjMS4yMTEsMC43LDEuOTU5LDEuOTkzLDEuOTU5LDMuMzkzdjU1LjkyNGMwLDEuMzk5LTAuNzQ4LDIuNjkzLTEuOTU5LDMuMzk0bC00OC40MzMsMjcuOTYyYy0xLjIxMiwwLjctMi43MDYsMC43LTMuOTE4LDAgbC00OC40MzItMjcuOTYyYy0xLjIxMi0wLjctMS45NTktMS45OTQtMS45NTktMy4zOTR2LTU1LjkyNEM5NS4wNjMsMTAxLjAwOSw5NS44MSw5OS43MTYsOTcuMDIxLDk5LjAxNicvPg0KPHBhdGggZmlsbD0nIzRCMzJDMycgZD0nTTI3My4zMzYsMTI0LjQ4OEwyMTUuNDY5LDIzLjgxNmMtMi4xMDItMy42NC01Ljk4NS02LjMyNS0xMC4xODgtNi4zMjVIODkuNTQ1IGMtNC4yMDQsMC04LjA4OCwyLjY4NS0xMC4xOSw2LjMyNWwtNTcuODY3LDEwMC40NWMtMi4xMDIsMy42NDEtMi4xMDIsOC4yMzYsMCwxMS44NzdsNTcuODY3LDk5Ljg0NyBjMi4xMDIsMy42NCw1Ljk4Niw1LjUwMSwxMC4xOSw1LjUwMWgxMTUuNzM1YzQuMjAzLDAsOC4wODctMS44MDUsMTAuMTg4LTUuNDQ2bDU3Ljg2Ny0xMDAuMDEgQzI3NS40MzksMTMyLjM5NiwyNzUuNDM5LDEyOC4xMjgsMjczLjMzNiwxMjQuNDg4IE0yMjUuNDE5LDE3Mi44OThjMCwxLjQ4LTAuODkxLDIuODQ5LTIuMTc0LDMuNTlsLTczLjcxLDQyLjUyNyBjLTEuMjgyLDAuNzQtMi44ODgsMC43NC00LjE3LDBsLTczLjc2Ny00Mi41MjdjLTEuMjgyLTAuNzQxLTIuMTc5LTIuMTA5LTIuMTc5LTMuNTlWODcuODQzYzAtMS40ODEsMC44ODQtMi44NDksMi4xNjctMy41OSBsNzMuNzA3LTQyLjUyN2MxLjI4Mi0wLjc0MSwyLjg4Ni0wLjc0MSw0LjE2OCwwbDczLjc3Miw0Mi41MjdjMS4yODMsMC43NDEsMi4xODYsMi4xMDksMi4xODYsMy41OVYxNzIuODk4eicvPg0KPC9zdmc+">
|
44
44
|
<style>
|
45
45
|
body {
|
46
|
-
font-family:Arial, "Helvetica Neue", Helvetica, sans-serif;
|
47
|
-
font-size:16px;
|
48
|
-
font-weight:normal;
|
49
|
-
margin:0;
|
50
|
-
padding:0;
|
51
|
-
color
|
46
|
+
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
|
47
|
+
font-size: 16px;
|
48
|
+
font-weight: normal;
|
49
|
+
margin: 0;
|
50
|
+
padding: 0;
|
51
|
+
color: #333;
|
52
52
|
}
|
53
|
+
|
53
54
|
#overview {
|
54
|
-
padding:20px 30px
|
55
|
+
padding: 20px 30px;
|
55
56
|
}
|
56
|
-
|
57
|
-
|
57
|
+
|
58
|
+
td,
|
59
|
+
th {
|
60
|
+
padding: 5px 10px;
|
58
61
|
}
|
62
|
+
|
59
63
|
h1 {
|
60
|
-
margin:0
|
64
|
+
margin: 0;
|
61
65
|
}
|
66
|
+
|
62
67
|
table {
|
63
|
-
margin:30px;
|
64
|
-
width:calc(100% - 60px);
|
65
|
-
max-width:1000px;
|
66
|
-
border-radius:5px;
|
67
|
-
border:1px solid #ddd;
|
68
|
-
border-spacing:
|
68
|
+
margin: 30px;
|
69
|
+
width: calc(100% - 60px);
|
70
|
+
max-width: 1000px;
|
71
|
+
border-radius: 5px;
|
72
|
+
border: 1px solid #ddd;
|
73
|
+
border-spacing: 0;
|
69
74
|
}
|
75
|
+
|
70
76
|
th {
|
71
|
-
font-weight:400;
|
72
|
-
font-size:medium;
|
73
|
-
text-align:left;
|
74
|
-
cursor:pointer
|
77
|
+
font-weight: 400;
|
78
|
+
font-size: medium;
|
79
|
+
text-align: left;
|
80
|
+
cursor: pointer;
|
75
81
|
}
|
76
|
-
|
77
|
-
|
82
|
+
|
83
|
+
td.clr-1,
|
84
|
+
td.clr-2,
|
85
|
+
th span {
|
86
|
+
font-weight: 700;
|
78
87
|
}
|
88
|
+
|
79
89
|
th span {
|
80
|
-
float:right;
|
81
|
-
margin-left:20px
|
90
|
+
float: right;
|
91
|
+
margin-left: 20px;
|
82
92
|
}
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
93
|
+
|
94
|
+
th span::after {
|
95
|
+
content: "";
|
96
|
+
clear: both;
|
97
|
+
display: block;
|
87
98
|
}
|
99
|
+
|
88
100
|
tr:last-child td {
|
89
|
-
border-bottom:none
|
101
|
+
border-bottom: none;
|
90
102
|
}
|
91
|
-
|
92
|
-
|
103
|
+
|
104
|
+
tr td:first-child,
|
105
|
+
tr td:last-child {
|
106
|
+
color: #9da0a4;
|
93
107
|
}
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
108
|
+
|
109
|
+
#overview.bg-0,
|
110
|
+
tr.bg-0 th {
|
111
|
+
color: #468847;
|
112
|
+
background: #dff0d8;
|
113
|
+
border-bottom: 1px solid #d6e9c6;
|
98
114
|
}
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
115
|
+
|
116
|
+
#overview.bg-1,
|
117
|
+
tr.bg-1 th {
|
118
|
+
color: #f0ad4e;
|
119
|
+
background: #fcf8e3;
|
120
|
+
border-bottom: 1px solid #fbeed5;
|
103
121
|
}
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
122
|
+
|
123
|
+
#overview.bg-2,
|
124
|
+
tr.bg-2 th {
|
125
|
+
color: #b94a48;
|
126
|
+
background: #f2dede;
|
127
|
+
border-bottom: 1px solid #eed3d7;
|
108
128
|
}
|
129
|
+
|
109
130
|
td {
|
110
|
-
border-bottom:1px solid #ddd
|
131
|
+
border-bottom: 1px solid #ddd;
|
111
132
|
}
|
133
|
+
|
112
134
|
td.clr-1 {
|
113
|
-
color
|
135
|
+
color: #f0ad4e;
|
114
136
|
}
|
137
|
+
|
115
138
|
td.clr-2 {
|
116
|
-
color
|
139
|
+
color: #b94a48;
|
117
140
|
}
|
141
|
+
|
118
142
|
td a {
|
119
|
-
color
|
120
|
-
text-decoration:none
|
143
|
+
color: #3a33d1;
|
144
|
+
text-decoration: none;
|
121
145
|
}
|
146
|
+
|
122
147
|
td a:hover {
|
123
|
-
color
|
124
|
-
text-decoration:underline
|
148
|
+
color: #272296;
|
149
|
+
text-decoration: underline;
|
125
150
|
}
|
126
151
|
</style>
|
127
152
|
</head>
|
@@ -214,7 +239,7 @@ function messageTemplate(it) {
|
|
214
239
|
} = it;
|
215
240
|
|
216
241
|
return `
|
217
|
-
<tr style="display:none" class="f-${parentIndex}">
|
242
|
+
<tr style="display: none;" class="f-${parentIndex}">
|
218
243
|
<td>${lineNumber}:${columnNumber}</td>
|
219
244
|
<td class="clr-${severityNumber}">${severityName}</td>
|
220
245
|
<td>${encodeHTML(message)}</td>
|
@@ -27,6 +27,17 @@ const isPathInside = require("is-path-inside");
|
|
27
27
|
const doFsWalk = util.promisify(fswalk.walk);
|
28
28
|
const Minimatch = minimatch.Minimatch;
|
29
29
|
|
30
|
+
//-----------------------------------------------------------------------------
|
31
|
+
// Types
|
32
|
+
//-----------------------------------------------------------------------------
|
33
|
+
|
34
|
+
/**
|
35
|
+
* @typedef {Object} GlobSearch
|
36
|
+
* @property {Array<string>} patterns The normalized patterns to use for a search.
|
37
|
+
* @property {Array<string>} rawPatterns The patterns as entered by the user
|
38
|
+
* before doing any normalization.
|
39
|
+
*/
|
40
|
+
|
30
41
|
//-----------------------------------------------------------------------------
|
31
42
|
// Errors
|
32
43
|
//-----------------------------------------------------------------------------
|
@@ -47,6 +58,30 @@ class NoFilesFoundError extends Error {
|
|
47
58
|
}
|
48
59
|
}
|
49
60
|
|
61
|
+
/**
|
62
|
+
* The error type when a search fails to match multiple patterns.
|
63
|
+
*/
|
64
|
+
class UnmatchedSearchPatternsError extends Error {
|
65
|
+
|
66
|
+
/**
|
67
|
+
* @param {Object} options The options for the error.
|
68
|
+
* @param {string} options.basePath The directory that was searched.
|
69
|
+
* @param {Array<string>} options.unmatchedPatterns The glob patterns
|
70
|
+
* which were not found.
|
71
|
+
* @param {Array<string>} options.patterns The glob patterns that were
|
72
|
+
* searched.
|
73
|
+
* @param {Array<string>} options.rawPatterns The raw glob patterns that
|
74
|
+
* were searched.
|
75
|
+
*/
|
76
|
+
constructor({ basePath, unmatchedPatterns, patterns, rawPatterns }) {
|
77
|
+
super(`No files matching '${rawPatterns}' in '${basePath}' were found.`);
|
78
|
+
this.basePath = basePath;
|
79
|
+
this.patternsToCheck = unmatchedPatterns;
|
80
|
+
this.patterns = patterns;
|
81
|
+
this.rawPatterns = rawPatterns;
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
50
85
|
/**
|
51
86
|
* The error type when there are files matched by a glob, but all of them have been ignored.
|
52
87
|
*/
|
@@ -107,6 +142,69 @@ function isGlobPattern(pattern) {
|
|
107
142
|
return isGlob(path.sep === "\\" ? normalizeToPosix(pattern) : pattern);
|
108
143
|
}
|
109
144
|
|
145
|
+
|
146
|
+
/**
|
147
|
+
* Determines if a given glob pattern will return any results.
|
148
|
+
* Used primarily to help with useful error messages.
|
149
|
+
* @param {Object} options The options for the function.
|
150
|
+
* @param {string} options.basePath The directory to search.
|
151
|
+
* @param {string} options.pattern A glob pattern to match.
|
152
|
+
* @returns {Promise<boolean>} True if there is a glob match, false if not.
|
153
|
+
*/
|
154
|
+
function globMatch({ basePath, pattern }) {
|
155
|
+
|
156
|
+
let found = false;
|
157
|
+
const patternToUse = path.isAbsolute(pattern)
|
158
|
+
? normalizeToPosix(path.relative(basePath, pattern))
|
159
|
+
: pattern;
|
160
|
+
|
161
|
+
const matcher = new Minimatch(patternToUse);
|
162
|
+
|
163
|
+
const fsWalkSettings = {
|
164
|
+
|
165
|
+
deepFilter(entry) {
|
166
|
+
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
|
167
|
+
|
168
|
+
return !found && matcher.match(relativePath, true);
|
169
|
+
},
|
170
|
+
|
171
|
+
entryFilter(entry) {
|
172
|
+
if (found || entry.dirent.isDirectory()) {
|
173
|
+
return false;
|
174
|
+
}
|
175
|
+
|
176
|
+
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
|
177
|
+
|
178
|
+
if (matcher.match(relativePath)) {
|
179
|
+
found = true;
|
180
|
+
return true;
|
181
|
+
}
|
182
|
+
|
183
|
+
return false;
|
184
|
+
}
|
185
|
+
};
|
186
|
+
|
187
|
+
return new Promise(resolve => {
|
188
|
+
|
189
|
+
// using a stream so we can exit early because we just need one match
|
190
|
+
const globStream = fswalk.walkStream(basePath, fsWalkSettings);
|
191
|
+
|
192
|
+
globStream.on("data", () => {
|
193
|
+
globStream.destroy();
|
194
|
+
resolve(true);
|
195
|
+
});
|
196
|
+
|
197
|
+
// swallow errors as they're not important here
|
198
|
+
globStream.on("error", () => { });
|
199
|
+
|
200
|
+
globStream.on("end", () => {
|
201
|
+
resolve(false);
|
202
|
+
});
|
203
|
+
globStream.read();
|
204
|
+
});
|
205
|
+
|
206
|
+
}
|
207
|
+
|
110
208
|
/**
|
111
209
|
* Searches a directory looking for matching glob patterns. This uses
|
112
210
|
* the config array's logic to determine if a directory or file should
|
@@ -116,26 +214,62 @@ function isGlobPattern(pattern) {
|
|
116
214
|
* @param {string} options.basePath The directory to search.
|
117
215
|
* @param {Array<string>} options.patterns An array of glob patterns
|
118
216
|
* to match.
|
217
|
+
* @param {Array<string>} options.rawPatterns An array of glob patterns
|
218
|
+
* as the user inputted them. Used for errors.
|
119
219
|
* @param {FlatConfigArray} options.configs The config array to use for
|
120
220
|
* determining what to ignore.
|
221
|
+
* @param {boolean} options.errorOnUnmatchedPattern Determines if an error
|
222
|
+
* should be thrown when a pattern is unmatched.
|
121
223
|
* @returns {Promise<Array<string>>} An array of matching file paths
|
122
224
|
* or an empty array if there are no matches.
|
225
|
+
* @throws {UnmatchedSearchPatternsErrror} If there is a pattern that doesn't
|
226
|
+
* match any files.
|
123
227
|
*/
|
124
|
-
async function globSearch({
|
228
|
+
async function globSearch({
|
229
|
+
basePath,
|
230
|
+
patterns,
|
231
|
+
rawPatterns,
|
232
|
+
configs,
|
233
|
+
errorOnUnmatchedPattern
|
234
|
+
}) {
|
125
235
|
|
126
236
|
if (patterns.length === 0) {
|
127
237
|
return [];
|
128
238
|
}
|
129
239
|
|
130
|
-
|
240
|
+
/*
|
241
|
+
* In this section we are converting the patterns into Minimatch
|
242
|
+
* instances for performance reasons. Because we are doing the same
|
243
|
+
* matches repeatedly, it's best to compile those patterns once and
|
244
|
+
* reuse them multiple times.
|
245
|
+
*
|
246
|
+
* To do that, we convert any patterns with an absolute path into a
|
247
|
+
* relative path and normalize it to Posix-style slashes. We also keep
|
248
|
+
* track of the relative patterns to map them back to the original
|
249
|
+
* patterns, which we need in order to throw an error if there are any
|
250
|
+
* unmatched patterns.
|
251
|
+
*/
|
252
|
+
const relativeToPatterns = new Map();
|
253
|
+
const matchers = patterns.map((pattern, i) => {
|
131
254
|
const patternToUse = path.isAbsolute(pattern)
|
132
255
|
? normalizeToPosix(path.relative(basePath, pattern))
|
133
256
|
: pattern;
|
134
257
|
|
258
|
+
relativeToPatterns.set(patternToUse, patterns[i]);
|
259
|
+
|
135
260
|
return new minimatch.Minimatch(patternToUse);
|
136
261
|
});
|
137
262
|
|
138
|
-
|
263
|
+
/*
|
264
|
+
* We track unmatched patterns because we may want to throw an error when
|
265
|
+
* they occur. To start, this set is initialized with all of the patterns.
|
266
|
+
* Every time a match occurs, the pattern is removed from the set, making
|
267
|
+
* it easy to tell if we have any unmatched patterns left at the end of
|
268
|
+
* search.
|
269
|
+
*/
|
270
|
+
const unmatchedPatterns = new Set([...relativeToPatterns.keys()]);
|
271
|
+
|
272
|
+
const filePaths = (await doFsWalk(basePath, {
|
139
273
|
|
140
274
|
deepFilter(entry) {
|
141
275
|
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
|
@@ -151,94 +285,177 @@ async function globSearch({ basePath, patterns, configs }) {
|
|
151
285
|
return false;
|
152
286
|
}
|
153
287
|
|
154
|
-
|
288
|
+
/*
|
289
|
+
* Optimization: We need to track when patterns are left unmatched
|
290
|
+
* and so we use `unmatchedPatterns` to do that. There is a bit of
|
291
|
+
* complexity here because the same file can be matched by more than
|
292
|
+
* one pattern. So, when we start, we actually need to test every
|
293
|
+
* pattern against every file. Once we know there are no remaining
|
294
|
+
* unmatched patterns, then we can switch to just looking for the
|
295
|
+
* first matching pattern for improved speed.
|
296
|
+
*/
|
297
|
+
const matchesPattern = unmatchedPatterns.size > 0
|
298
|
+
? matchers.reduce((previousValue, matcher) => {
|
299
|
+
const pathMatches = matcher.match(relativePath);
|
300
|
+
|
301
|
+
/*
|
302
|
+
* We updated the unmatched patterns set only if the path
|
303
|
+
* matches and the file isn't ignored. If the file is
|
304
|
+
* ignored, that means there wasn't a match for the
|
305
|
+
* pattern so it should not be removed.
|
306
|
+
*
|
307
|
+
* Performance note: isFileIgnored() aggressively caches
|
308
|
+
* results so there is no performance penalty for calling
|
309
|
+
* it twice with the same argument.
|
310
|
+
*/
|
311
|
+
if (pathMatches && !configs.isFileIgnored(entry.path)) {
|
312
|
+
unmatchedPatterns.delete(matcher.pattern);
|
313
|
+
}
|
314
|
+
|
315
|
+
return pathMatches || previousValue;
|
316
|
+
}, false)
|
317
|
+
: matchers.some(matcher => matcher.match(relativePath));
|
155
318
|
|
156
319
|
return matchesPattern && !configs.isFileIgnored(entry.path);
|
157
320
|
}
|
321
|
+
|
158
322
|
})).map(entry => entry.path);
|
159
323
|
|
324
|
+
// now check to see if we have any unmatched patterns
|
325
|
+
if (errorOnUnmatchedPattern && unmatchedPatterns.size > 0) {
|
326
|
+
throw new UnmatchedSearchPatternsError({
|
327
|
+
basePath,
|
328
|
+
unmatchedPatterns: [...unmatchedPatterns].map(
|
329
|
+
pattern => relativeToPatterns.get(pattern)
|
330
|
+
),
|
331
|
+
patterns,
|
332
|
+
rawPatterns
|
333
|
+
});
|
334
|
+
}
|
335
|
+
|
336
|
+
return filePaths;
|
337
|
+
}
|
338
|
+
|
339
|
+
/**
|
340
|
+
* Checks to see if there are any ignored results for a given search. This
|
341
|
+
* happens either when there are unmatched patterns during a search or if
|
342
|
+
* a search returns no results.
|
343
|
+
* @param {Object} options The options for this function.
|
344
|
+
* @param {string} options.basePath The directory to search.
|
345
|
+
* @param {Array<string>} options.patterns An array of glob patterns
|
346
|
+
* that were used in the original search.
|
347
|
+
* @param {Array<string>} options.rawPatterns An array of glob patterns
|
348
|
+
* as the user inputted them. Used for errors.
|
349
|
+
* @param {Array<string>} options.patternsToCheck An array of glob patterns
|
350
|
+
* to use for this check.
|
351
|
+
* @returns {void}
|
352
|
+
* @throws {NoFilesFoundError} If there is a pattern that doesn't match
|
353
|
+
* any files and `errorOnUnmatchedPattern` is true.
|
354
|
+
* @throws {AllFilesIgnoredError} If there is a pattern that matches files
|
355
|
+
* when there are no ignores.
|
356
|
+
*/
|
357
|
+
async function checkForIgnoredResults({
|
358
|
+
basePath,
|
359
|
+
patterns,
|
360
|
+
rawPatterns,
|
361
|
+
patternsToCheck = patterns
|
362
|
+
}) {
|
363
|
+
|
364
|
+
for (const pattern of patternsToCheck) {
|
365
|
+
|
366
|
+
const patternHasMatch = await globMatch({
|
367
|
+
basePath,
|
368
|
+
pattern
|
369
|
+
});
|
370
|
+
|
371
|
+
if (patternHasMatch) {
|
372
|
+
throw new AllFilesIgnoredError(
|
373
|
+
rawPatterns[patterns.indexOf(pattern)]
|
374
|
+
);
|
375
|
+
}
|
376
|
+
}
|
377
|
+
|
378
|
+
// if we get here there are truly no matches
|
379
|
+
throw new NoFilesFoundError(
|
380
|
+
rawPatterns[patterns.indexOf(patternsToCheck[0])],
|
381
|
+
true
|
382
|
+
);
|
160
383
|
}
|
161
384
|
|
162
385
|
/**
|
163
386
|
* Performs multiple glob searches in parallel.
|
164
387
|
* @param {Object} options The options for this function.
|
165
|
-
* @param {
|
388
|
+
* @param {Map<string,GlobSearch>} options.searches
|
166
389
|
* An array of glob patterns to match.
|
167
390
|
* @param {FlatConfigArray} options.configs The config array to use for
|
168
391
|
* determining what to ignore.
|
392
|
+
* @param {boolean} options.errorOnUnmatchedPattern Determines if an
|
393
|
+
* unmatched glob pattern should throw an error.
|
169
394
|
* @returns {Promise<Array<string>>} An array of matching file paths
|
170
395
|
* or an empty array if there are no matches.
|
171
396
|
*/
|
172
|
-
async function globMultiSearch({ searches, configs }) {
|
397
|
+
async function globMultiSearch({ searches, configs, errorOnUnmatchedPattern }) {
|
173
398
|
|
174
|
-
|
175
|
-
|
176
|
-
|
399
|
+
/*
|
400
|
+
* For convenience, we normalized the search map into an array of objects.
|
401
|
+
* Next, we filter out all searches that have no patterns. This happens
|
402
|
+
* primarily for the cwd, which is prepopulated in the searches map as an
|
403
|
+
* optimization. However, if it has no patterns, it means all patterns
|
404
|
+
* occur outside of the cwd and we can safely filter out that search.
|
405
|
+
*/
|
406
|
+
const normalizedSearches = [...searches].map(
|
407
|
+
([basePath, { patterns, rawPatterns }]) => ({ basePath, patterns, rawPatterns })
|
408
|
+
).filter(({ patterns }) => patterns.length > 0);
|
409
|
+
|
410
|
+
const results = await Promise.allSettled(
|
411
|
+
normalizedSearches.map(
|
412
|
+
({ basePath, patterns, rawPatterns }) => globSearch({
|
413
|
+
basePath,
|
414
|
+
patterns,
|
415
|
+
rawPatterns,
|
416
|
+
configs,
|
417
|
+
errorOnUnmatchedPattern
|
418
|
+
})
|
177
419
|
)
|
178
420
|
);
|
179
421
|
|
180
|
-
|
181
|
-
}
|
182
|
-
|
183
|
-
/**
|
184
|
-
* Determines if a given glob pattern will return any results.
|
185
|
-
* Used primarily to help with useful error messages.
|
186
|
-
* @param {Object} options The options for the function.
|
187
|
-
* @param {string} options.basePath The directory to search.
|
188
|
-
* @param {string} options.pattern A glob pattern to match.
|
189
|
-
* @returns {Promise<boolean>} True if there is a glob match, false if not.
|
190
|
-
*/
|
191
|
-
function globMatch({ basePath, pattern }) {
|
192
|
-
|
193
|
-
let found = false;
|
194
|
-
const patternToUse = path.isAbsolute(pattern)
|
195
|
-
? normalizeToPosix(path.relative(basePath, pattern))
|
196
|
-
: pattern;
|
422
|
+
const filePaths = [];
|
197
423
|
|
198
|
-
|
424
|
+
for (let i = 0; i < results.length; i++) {
|
199
425
|
|
200
|
-
|
426
|
+
const result = results[i];
|
427
|
+
const currentSearch = normalizedSearches[i];
|
201
428
|
|
202
|
-
|
203
|
-
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
|
429
|
+
if (result.status === "fulfilled") {
|
204
430
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
entryFilter(entry) {
|
209
|
-
if (found || entry.dirent.isDirectory()) {
|
210
|
-
return false;
|
431
|
+
// if the search was successful just add the results
|
432
|
+
if (result.value.length > 0) {
|
433
|
+
filePaths.push(...result.value);
|
211
434
|
}
|
212
435
|
|
213
|
-
|
436
|
+
continue;
|
437
|
+
}
|
214
438
|
|
215
|
-
|
216
|
-
|
217
|
-
return true;
|
218
|
-
}
|
439
|
+
// if we make it here then there was an error
|
440
|
+
const error = result.reason;
|
219
441
|
|
220
|
-
|
442
|
+
// unexpected errors should be re-thrown
|
443
|
+
if (!error.basePath) {
|
444
|
+
throw error;
|
221
445
|
}
|
222
|
-
};
|
223
446
|
|
224
|
-
|
447
|
+
if (errorOnUnmatchedPattern) {
|
225
448
|
|
226
|
-
|
227
|
-
|
449
|
+
await checkForIgnoredResults({
|
450
|
+
...currentSearch,
|
451
|
+
patternsToCheck: error.patternsToCheck
|
452
|
+
});
|
228
453
|
|
229
|
-
|
230
|
-
globStream.destroy();
|
231
|
-
resolve(true);
|
232
|
-
});
|
454
|
+
}
|
233
455
|
|
234
|
-
|
235
|
-
globStream.on("error", () => {});
|
456
|
+
}
|
236
457
|
|
237
|
-
|
238
|
-
resolve(false);
|
239
|
-
});
|
240
|
-
globStream.read();
|
241
|
-
});
|
458
|
+
return [...new Set(filePaths)];
|
242
459
|
|
243
460
|
}
|
244
461
|
|
@@ -335,47 +552,17 @@ async function findFiles({
|
|
335
552
|
}
|
336
553
|
});
|
337
554
|
|
338
|
-
const globbyResults = await globMultiSearch({
|
339
|
-
searches,
|
340
|
-
configs
|
341
|
-
});
|
342
|
-
|
343
|
-
// if there are no results, tell the user why
|
344
|
-
if (!results.length && !globbyResults.length) {
|
345
|
-
|
346
|
-
for (const [basePath, { patterns: patternsToCheck, rawPatterns: patternsToReport }] of searches) {
|
347
|
-
|
348
|
-
let index = 0;
|
349
|
-
|
350
|
-
// try globby without ignoring anything
|
351
|
-
for (const patternToCheck of patternsToCheck) {
|
352
|
-
|
353
|
-
// check if there are any matches at all
|
354
|
-
const patternHasMatch = await globMatch({
|
355
|
-
basePath,
|
356
|
-
pattern: patternToCheck
|
357
|
-
});
|
358
|
-
|
359
|
-
if (patternHasMatch) {
|
360
|
-
throw new AllFilesIgnoredError(patternsToReport[index]);
|
361
|
-
}
|
362
|
-
|
363
|
-
// otherwise no files were found
|
364
|
-
if (errorOnUnmatchedPattern) {
|
365
|
-
throw new NoFilesFoundError(patternsToReport[index], globInputPaths);
|
366
|
-
}
|
367
|
-
|
368
|
-
index++;
|
369
|
-
}
|
370
|
-
}
|
371
|
-
|
372
|
-
}
|
373
|
-
|
374
555
|
// there were patterns that didn't match anything, tell the user
|
375
556
|
if (errorOnUnmatchedPattern && missingPatterns.length) {
|
376
557
|
throw new NoFilesFoundError(missingPatterns[0], globInputPaths);
|
377
558
|
}
|
378
559
|
|
560
|
+
// now we are safe to do the search
|
561
|
+
const globbyResults = await globMultiSearch({
|
562
|
+
searches,
|
563
|
+
configs,
|
564
|
+
errorOnUnmatchedPattern
|
565
|
+
});
|
379
566
|
|
380
567
|
return [
|
381
568
|
...results,
|
@@ -161,6 +161,16 @@ function createRulesMeta(rules) {
|
|
161
161
|
}, {});
|
162
162
|
}
|
163
163
|
|
164
|
+
/**
|
165
|
+
* Return the absolute path of a file named `"__placeholder__.js"` in a given directory.
|
166
|
+
* This is used as a replacement for a missing file path.
|
167
|
+
* @param {string} cwd An absolute directory path.
|
168
|
+
* @returns {string} The absolute path of a file named `"__placeholder__.js"` in the given directory.
|
169
|
+
*/
|
170
|
+
function getPlaceholderPath(cwd) {
|
171
|
+
return path.join(cwd, "__placeholder__.js");
|
172
|
+
}
|
173
|
+
|
164
174
|
/** @type {WeakMap<ExtractedConfig, DeprecatedRuleInfo[]>} */
|
165
175
|
const usedDeprecatedRulesCache = new WeakMap();
|
166
176
|
|
@@ -177,7 +187,7 @@ function getOrFindUsedDeprecatedRules(eslint, maybeFilePath) {
|
|
177
187
|
} = privateMembers.get(eslint);
|
178
188
|
const filePath = path.isAbsolute(maybeFilePath)
|
179
189
|
? maybeFilePath
|
180
|
-
:
|
190
|
+
: getPlaceholderPath(cwd);
|
181
191
|
const config = configs.getConfig(filePath);
|
182
192
|
|
183
193
|
// Most files use the same config, so cache it.
|
@@ -422,7 +432,7 @@ function verifyText({
|
|
422
432
|
* `config.extractConfig(filePath)` requires an absolute path, but `linter`
|
423
433
|
* doesn't know CWD, so it gives `linter` an absolute path always.
|
424
434
|
*/
|
425
|
-
const filePathToVerify = filePath === "<text>" ?
|
435
|
+
const filePathToVerify = filePath === "<text>" ? getPlaceholderPath(cwd) : filePath;
|
426
436
|
const { fixed, messages, output } = linter.verifyAndFix(
|
427
437
|
text,
|
428
438
|
configs,
|
@@ -519,6 +529,14 @@ function *iterateRuleDeprecationWarnings(configs) {
|
|
519
529
|
}
|
520
530
|
}
|
521
531
|
|
532
|
+
/**
|
533
|
+
* Creates an error to be thrown when an array of results passed to `getRulesMetaForResults` was not created by the current engine.
|
534
|
+
* @returns {TypeError} An error object.
|
535
|
+
*/
|
536
|
+
function createExtraneousResultsError() {
|
537
|
+
return new TypeError("Results object was not created from this ESLint instance.");
|
538
|
+
}
|
539
|
+
|
522
540
|
//-----------------------------------------------------------------------------
|
523
541
|
// Main API
|
524
542
|
//-----------------------------------------------------------------------------
|
@@ -655,7 +673,10 @@ class FlatESLint {
|
|
655
673
|
}
|
656
674
|
|
657
675
|
const resultRules = new Map();
|
658
|
-
const {
|
676
|
+
const {
|
677
|
+
configs,
|
678
|
+
options: { cwd }
|
679
|
+
} = privateMembers.get(this);
|
659
680
|
|
660
681
|
/*
|
661
682
|
* We can only accurately return rules meta information for linting results if the
|
@@ -664,7 +685,7 @@ class FlatESLint {
|
|
664
685
|
* to let the user know we can't do anything here.
|
665
686
|
*/
|
666
687
|
if (!configs) {
|
667
|
-
throw
|
688
|
+
throw createExtraneousResultsError();
|
668
689
|
}
|
669
690
|
|
670
691
|
for (const result of results) {
|
@@ -673,13 +694,7 @@ class FlatESLint {
|
|
673
694
|
* Normalize filename for <text>.
|
674
695
|
*/
|
675
696
|
const filePath = result.filePath === "<text>"
|
676
|
-
?
|
677
|
-
|
678
|
-
/*
|
679
|
-
* All of the plugin and rule information is contained within the
|
680
|
-
* calculated config for the given file.
|
681
|
-
*/
|
682
|
-
const config = configs.getConfig(filePath);
|
697
|
+
? getPlaceholderPath(cwd) : result.filePath;
|
683
698
|
const allMessages = result.messages.concat(result.suppressedMessages);
|
684
699
|
|
685
700
|
for (const { ruleId } of allMessages) {
|
@@ -687,6 +702,15 @@ class FlatESLint {
|
|
687
702
|
continue;
|
688
703
|
}
|
689
704
|
|
705
|
+
/*
|
706
|
+
* All of the plugin and rule information is contained within the
|
707
|
+
* calculated config for the given file.
|
708
|
+
*/
|
709
|
+
const config = configs.getConfig(filePath);
|
710
|
+
|
711
|
+
if (!config) {
|
712
|
+
throw createExtraneousResultsError();
|
713
|
+
}
|
690
714
|
const rule = getRuleFromConfig(ruleId, config);
|
691
715
|
|
692
716
|
// ensure the rule exists
|
@@ -1024,7 +1048,7 @@ class FlatESLint {
|
|
1024
1048
|
const npmFormat = naming.normalizePackageName(normalizedFormatName, "eslint-formatter");
|
1025
1049
|
|
1026
1050
|
// TODO: This is pretty dirty...would be nice to clean up at some point.
|
1027
|
-
formatterPath = ModuleResolver.resolve(npmFormat,
|
1051
|
+
formatterPath = ModuleResolver.resolve(npmFormat, getPlaceholderPath(cwd));
|
1028
1052
|
} catch {
|
1029
1053
|
formatterPath = path.resolve(__dirname, "../", "cli-engine", "formatters", `${normalizedFormatName}.js`);
|
1030
1054
|
}
|
@@ -50,7 +50,7 @@ function normalizeOptions(optionValue, ecmaVersion) {
|
|
50
50
|
objects: optionValue,
|
51
51
|
imports: optionValue,
|
52
52
|
exports: optionValue,
|
53
|
-
functions:
|
53
|
+
functions: ecmaVersion < 2017 ? "ignore" : optionValue
|
54
54
|
};
|
55
55
|
}
|
56
56
|
if (typeof optionValue === "object" && optionValue !== null) {
|
@@ -134,7 +134,7 @@ module.exports = {
|
|
134
134
|
},
|
135
135
|
|
136
136
|
create(context) {
|
137
|
-
const options = normalizeOptions(context.options[0], context.
|
137
|
+
const options = normalizeOptions(context.options[0], context.languageOptions.ecmaVersion);
|
138
138
|
|
139
139
|
const sourceCode = context.getSourceCode();
|
140
140
|
|
@@ -44,7 +44,7 @@ function isModuleExports(pattern) {
|
|
44
44
|
* @returns {boolean} True if the string is a valid identifier
|
45
45
|
*/
|
46
46
|
function isIdentifier(name, ecmaVersion) {
|
47
|
-
if (ecmaVersion >=
|
47
|
+
if (ecmaVersion >= 2015) {
|
48
48
|
return esutils.keyword.isIdentifierES6(name);
|
49
49
|
}
|
50
50
|
return esutils.keyword.isIdentifierES5(name);
|
@@ -104,7 +104,7 @@ module.exports = {
|
|
104
104
|
const nameMatches = typeof context.options[0] === "string" ? context.options[0] : "always";
|
105
105
|
const considerPropertyDescriptor = options.considerPropertyDescriptor;
|
106
106
|
const includeModuleExports = options.includeCommonJSModuleExports;
|
107
|
-
const ecmaVersion = context.
|
107
|
+
const ecmaVersion = context.languageOptions.ecmaVersion;
|
108
108
|
|
109
109
|
/**
|
110
110
|
* Check whether node is a certain CallExpression.
|
package/lib/rules/index.js
CHANGED
@@ -123,6 +123,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
|
|
123
123
|
"no-empty-character-class": () => require("./no-empty-character-class"),
|
124
124
|
"no-empty-function": () => require("./no-empty-function"),
|
125
125
|
"no-empty-pattern": () => require("./no-empty-pattern"),
|
126
|
+
"no-empty-static-block": () => require("./no-empty-static-block"),
|
126
127
|
"no-eq-null": () => require("./no-eq-null"),
|
127
128
|
"no-eval": () => require("./no-eval"),
|
128
129
|
"no-ex-assign": () => require("./no-ex-assign"),
|
@@ -167,6 +168,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
|
|
167
168
|
"no-nested-ternary": () => require("./no-nested-ternary"),
|
168
169
|
"no-new": () => require("./no-new"),
|
169
170
|
"no-new-func": () => require("./no-new-func"),
|
171
|
+
"no-new-native-nonconstructor": () => require("./no-new-native-nonconstructor"),
|
170
172
|
"no-new-object": () => require("./no-new-object"),
|
171
173
|
"no-new-require": () => require("./no-new-require"),
|
172
174
|
"no-new-symbol": () => require("./no-new-symbol"),
|
@@ -0,0 +1,47 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Rule to disallow empty static blocks.
|
3
|
+
* @author Sosuke Suzuki
|
4
|
+
*/
|
5
|
+
"use strict";
|
6
|
+
|
7
|
+
//------------------------------------------------------------------------------
|
8
|
+
// Rule Definition
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
|
11
|
+
/** @type {import('../shared/types').Rule} */
|
12
|
+
module.exports = {
|
13
|
+
meta: {
|
14
|
+
type: "suggestion",
|
15
|
+
|
16
|
+
docs: {
|
17
|
+
description: "Disallow empty static blocks",
|
18
|
+
recommended: false,
|
19
|
+
url: "https://eslint.org/docs/rules/no-empty-static-block"
|
20
|
+
},
|
21
|
+
|
22
|
+
schema: [],
|
23
|
+
|
24
|
+
messages: {
|
25
|
+
unexpected: "Unexpected empty static block."
|
26
|
+
}
|
27
|
+
},
|
28
|
+
|
29
|
+
create(context) {
|
30
|
+
const sourceCode = context.getSourceCode();
|
31
|
+
|
32
|
+
return {
|
33
|
+
StaticBlock(node) {
|
34
|
+
if (node.body.length === 0) {
|
35
|
+
const closingBrace = sourceCode.getLastToken(node);
|
36
|
+
|
37
|
+
if (sourceCode.getCommentsBefore(closingBrace).length === 0) {
|
38
|
+
context.report({
|
39
|
+
node,
|
40
|
+
messageId: "unexpected"
|
41
|
+
});
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
};
|
46
|
+
}
|
47
|
+
};
|
package/lib/rules/no-empty.js
CHANGED
@@ -17,6 +17,7 @@ const astUtils = require("./utils/ast-utils");
|
|
17
17
|
/** @type {import('../shared/types').Rule} */
|
18
18
|
module.exports = {
|
19
19
|
meta: {
|
20
|
+
hasSuggestions: true,
|
20
21
|
type: "suggestion",
|
21
22
|
|
22
23
|
docs: {
|
@@ -39,7 +40,8 @@ module.exports = {
|
|
39
40
|
],
|
40
41
|
|
41
42
|
messages: {
|
42
|
-
unexpected: "Empty {{type}} statement."
|
43
|
+
unexpected: "Empty {{type}} statement.",
|
44
|
+
suggestComment: "Add comment inside empty {{type}} statement."
|
43
45
|
}
|
44
46
|
},
|
45
47
|
|
@@ -71,7 +73,22 @@ module.exports = {
|
|
71
73
|
return;
|
72
74
|
}
|
73
75
|
|
74
|
-
context.report({
|
76
|
+
context.report({
|
77
|
+
node,
|
78
|
+
messageId: "unexpected",
|
79
|
+
data: { type: "block" },
|
80
|
+
suggest: [
|
81
|
+
{
|
82
|
+
messageId: "suggestComment",
|
83
|
+
data: { type: "block" },
|
84
|
+
fix(fixer) {
|
85
|
+
const range = [node.range[0] + 1, node.range[1] - 1];
|
86
|
+
|
87
|
+
return fixer.replaceTextRange(range, " /* empty */ ");
|
88
|
+
}
|
89
|
+
}
|
90
|
+
]
|
91
|
+
});
|
75
92
|
},
|
76
93
|
|
77
94
|
SwitchStatement(node) {
|
@@ -193,15 +193,15 @@ module.exports = {
|
|
193
193
|
* ecmaVersion doesn't support the `u` flag.
|
194
194
|
*/
|
195
195
|
function isValidWithUnicodeFlag(pattern) {
|
196
|
-
const { ecmaVersion } = context.
|
196
|
+
const { ecmaVersion } = context.languageOptions;
|
197
197
|
|
198
|
-
// ecmaVersion
|
199
|
-
if (
|
198
|
+
// ecmaVersion <= 5 doesn't support the 'u' flag
|
199
|
+
if (ecmaVersion <= 5) {
|
200
200
|
return false;
|
201
201
|
}
|
202
202
|
|
203
203
|
const validator = new RegExpValidator({
|
204
|
-
ecmaVersion: Math.min(ecmaVersion
|
204
|
+
ecmaVersion: Math.min(ecmaVersion, REGEXPP_LATEST_ECMA_VERSION)
|
205
205
|
});
|
206
206
|
|
207
207
|
try {
|
@@ -0,0 +1,64 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Rule to disallow use of the new operator with global non-constructor functions
|
3
|
+
* @author Sosuke Suzuki
|
4
|
+
*/
|
5
|
+
|
6
|
+
"use strict";
|
7
|
+
|
8
|
+
//------------------------------------------------------------------------------
|
9
|
+
// Helpers
|
10
|
+
//------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
const nonConstructorGlobalFunctionNames = ["Symbol", "BigInt"];
|
13
|
+
|
14
|
+
//------------------------------------------------------------------------------
|
15
|
+
// Rule Definition
|
16
|
+
//------------------------------------------------------------------------------
|
17
|
+
|
18
|
+
/** @type {import('../shared/types').Rule} */
|
19
|
+
module.exports = {
|
20
|
+
meta: {
|
21
|
+
type: "problem",
|
22
|
+
|
23
|
+
docs: {
|
24
|
+
description: "Disallow `new` operators with global non-constructor functions",
|
25
|
+
recommended: false,
|
26
|
+
url: "https://eslint.org/docs/rules/no-new-native-nonconstructor"
|
27
|
+
},
|
28
|
+
|
29
|
+
schema: [],
|
30
|
+
|
31
|
+
messages: {
|
32
|
+
noNewNonconstructor: "`{{name}}` cannot be called as a constructor."
|
33
|
+
}
|
34
|
+
},
|
35
|
+
|
36
|
+
create(context) {
|
37
|
+
|
38
|
+
return {
|
39
|
+
"Program:exit"() {
|
40
|
+
const globalScope = context.getScope();
|
41
|
+
|
42
|
+
for (const nonConstructorName of nonConstructorGlobalFunctionNames) {
|
43
|
+
const variable = globalScope.set.get(nonConstructorName);
|
44
|
+
|
45
|
+
if (variable && variable.defs.length === 0) {
|
46
|
+
variable.references.forEach(ref => {
|
47
|
+
const node = ref.identifier;
|
48
|
+
const parent = node.parent;
|
49
|
+
|
50
|
+
if (parent && parent.type === "NewExpression" && parent.callee === node) {
|
51
|
+
context.report({
|
52
|
+
node,
|
53
|
+
messageId: "noNewNonconstructor",
|
54
|
+
data: { name: nonConstructorName }
|
55
|
+
});
|
56
|
+
}
|
57
|
+
});
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
};
|
62
|
+
|
63
|
+
}
|
64
|
+
};
|
@@ -248,14 +248,14 @@ module.exports = {
|
|
248
248
|
|
249
249
|
/**
|
250
250
|
* Returns a ecmaVersion compatible for regexpp.
|
251
|
-
* @param {
|
251
|
+
* @param {number} ecmaVersion The ecmaVersion to convert.
|
252
252
|
* @returns {import("regexpp/ecma-versions").EcmaVersion} The resulting ecmaVersion compatible for regexpp.
|
253
253
|
*/
|
254
254
|
function getRegexppEcmaVersion(ecmaVersion) {
|
255
|
-
if (
|
255
|
+
if (ecmaVersion <= 5) {
|
256
256
|
return 5;
|
257
257
|
}
|
258
|
-
return Math.min(ecmaVersion
|
258
|
+
return Math.min(ecmaVersion, REGEXPP_LATEST_ECMA_VERSION);
|
259
259
|
}
|
260
260
|
|
261
261
|
/**
|
@@ -320,7 +320,7 @@ module.exports = {
|
|
320
320
|
flags = getStringValue(node.arguments[1]);
|
321
321
|
}
|
322
322
|
|
323
|
-
const regexppEcmaVersion = getRegexppEcmaVersion(context.
|
323
|
+
const regexppEcmaVersion = getRegexppEcmaVersion(context.languageOptions.ecmaVersion);
|
324
324
|
const RegExpValidatorInstance = new RegExpValidator({ ecmaVersion: regexppEcmaVersion });
|
325
325
|
|
326
326
|
try {
|