eslint 8.26.0 → 8.28.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 +276 -94
- package/lib/eslint/flat-eslint.js +36 -12
- package/lib/rules/comma-dangle.js +2 -2
- package/lib/rules/for-direction.js +1 -1
- package/lib/rules/func-name-matching.js +2 -2
- package/lib/rules/index.js +2 -0
- package/lib/rules/key-spacing.js +16 -16
- package/lib/rules/no-empty-static-block.js +47 -0
- package/lib/rules/no-empty.js +19 -2
- package/lib/rules/no-implicit-coercion.js +20 -1
- package/lib/rules/no-magic-numbers.js +18 -1
- package/lib/rules/no-misleading-character-class.js +4 -4
- package/lib/rules/no-new-native-nonconstructor.js +64 -0
- package/lib/rules/no-obj-calls.js +1 -1
- package/lib/rules/prefer-object-spread.js +1 -1
- package/lib/rules/prefer-regex-literals.js +4 -4
- package/lib/shared/runtime-info.js +1 -1
- 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>
|
@@ -26,6 +26,18 @@ const isPathInside = require("is-path-inside");
|
|
26
26
|
|
27
27
|
const doFsWalk = util.promisify(fswalk.walk);
|
28
28
|
const Minimatch = minimatch.Minimatch;
|
29
|
+
const MINIMATCH_OPTIONS = { dot: true };
|
30
|
+
|
31
|
+
//-----------------------------------------------------------------------------
|
32
|
+
// Types
|
33
|
+
//-----------------------------------------------------------------------------
|
34
|
+
|
35
|
+
/**
|
36
|
+
* @typedef {Object} GlobSearch
|
37
|
+
* @property {Array<string>} patterns The normalized patterns to use for a search.
|
38
|
+
* @property {Array<string>} rawPatterns The patterns as entered by the user
|
39
|
+
* before doing any normalization.
|
40
|
+
*/
|
29
41
|
|
30
42
|
//-----------------------------------------------------------------------------
|
31
43
|
// Errors
|
@@ -47,6 +59,30 @@ class NoFilesFoundError extends Error {
|
|
47
59
|
}
|
48
60
|
}
|
49
61
|
|
62
|
+
/**
|
63
|
+
* The error type when a search fails to match multiple patterns.
|
64
|
+
*/
|
65
|
+
class UnmatchedSearchPatternsError extends Error {
|
66
|
+
|
67
|
+
/**
|
68
|
+
* @param {Object} options The options for the error.
|
69
|
+
* @param {string} options.basePath The directory that was searched.
|
70
|
+
* @param {Array<string>} options.unmatchedPatterns The glob patterns
|
71
|
+
* which were not found.
|
72
|
+
* @param {Array<string>} options.patterns The glob patterns that were
|
73
|
+
* searched.
|
74
|
+
* @param {Array<string>} options.rawPatterns The raw glob patterns that
|
75
|
+
* were searched.
|
76
|
+
*/
|
77
|
+
constructor({ basePath, unmatchedPatterns, patterns, rawPatterns }) {
|
78
|
+
super(`No files matching '${rawPatterns}' in '${basePath}' were found.`);
|
79
|
+
this.basePath = basePath;
|
80
|
+
this.unmatchedPatterns = unmatchedPatterns;
|
81
|
+
this.patterns = patterns;
|
82
|
+
this.rawPatterns = rawPatterns;
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
50
86
|
/**
|
51
87
|
* The error type when there are files matched by a glob, but all of them have been ignored.
|
52
88
|
*/
|
@@ -107,6 +143,69 @@ function isGlobPattern(pattern) {
|
|
107
143
|
return isGlob(path.sep === "\\" ? normalizeToPosix(pattern) : pattern);
|
108
144
|
}
|
109
145
|
|
146
|
+
|
147
|
+
/**
|
148
|
+
* Determines if a given glob pattern will return any results.
|
149
|
+
* Used primarily to help with useful error messages.
|
150
|
+
* @param {Object} options The options for the function.
|
151
|
+
* @param {string} options.basePath The directory to search.
|
152
|
+
* @param {string} options.pattern A glob pattern to match.
|
153
|
+
* @returns {Promise<boolean>} True if there is a glob match, false if not.
|
154
|
+
*/
|
155
|
+
function globMatch({ basePath, pattern }) {
|
156
|
+
|
157
|
+
let found = false;
|
158
|
+
const patternToUse = path.isAbsolute(pattern)
|
159
|
+
? normalizeToPosix(path.relative(basePath, pattern))
|
160
|
+
: pattern;
|
161
|
+
|
162
|
+
const matcher = new Minimatch(patternToUse, MINIMATCH_OPTIONS);
|
163
|
+
|
164
|
+
const fsWalkSettings = {
|
165
|
+
|
166
|
+
deepFilter(entry) {
|
167
|
+
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
|
168
|
+
|
169
|
+
return !found && matcher.match(relativePath, true);
|
170
|
+
},
|
171
|
+
|
172
|
+
entryFilter(entry) {
|
173
|
+
if (found || entry.dirent.isDirectory()) {
|
174
|
+
return false;
|
175
|
+
}
|
176
|
+
|
177
|
+
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
|
178
|
+
|
179
|
+
if (matcher.match(relativePath)) {
|
180
|
+
found = true;
|
181
|
+
return true;
|
182
|
+
}
|
183
|
+
|
184
|
+
return false;
|
185
|
+
}
|
186
|
+
};
|
187
|
+
|
188
|
+
return new Promise(resolve => {
|
189
|
+
|
190
|
+
// using a stream so we can exit early because we just need one match
|
191
|
+
const globStream = fswalk.walkStream(basePath, fsWalkSettings);
|
192
|
+
|
193
|
+
globStream.on("data", () => {
|
194
|
+
globStream.destroy();
|
195
|
+
resolve(true);
|
196
|
+
});
|
197
|
+
|
198
|
+
// swallow errors as they're not important here
|
199
|
+
globStream.on("error", () => { });
|
200
|
+
|
201
|
+
globStream.on("end", () => {
|
202
|
+
resolve(false);
|
203
|
+
});
|
204
|
+
globStream.read();
|
205
|
+
});
|
206
|
+
|
207
|
+
}
|
208
|
+
|
110
209
|
/**
|
111
210
|
* Searches a directory looking for matching glob patterns. This uses
|
112
211
|
* the config array's logic to determine if a directory or file should
|
@@ -116,26 +215,62 @@ function isGlobPattern(pattern) {
|
|
116
215
|
* @param {string} options.basePath The directory to search.
|
117
216
|
* @param {Array<string>} options.patterns An array of glob patterns
|
118
217
|
* to match.
|
218
|
+
* @param {Array<string>} options.rawPatterns An array of glob patterns
|
219
|
+
* as the user inputted them. Used for errors.
|
119
220
|
* @param {FlatConfigArray} options.configs The config array to use for
|
120
221
|
* determining what to ignore.
|
222
|
+
* @param {boolean} options.errorOnUnmatchedPattern Determines if an error
|
223
|
+
* should be thrown when a pattern is unmatched.
|
121
224
|
* @returns {Promise<Array<string>>} An array of matching file paths
|
122
225
|
* or an empty array if there are no matches.
|
226
|
+
* @throws {UnmatchedSearchPatternsErrror} If there is a pattern that doesn't
|
227
|
+
* match any files.
|
123
228
|
*/
|
124
|
-
async function globSearch({
|
229
|
+
async function globSearch({
|
230
|
+
basePath,
|
231
|
+
patterns,
|
232
|
+
rawPatterns,
|
233
|
+
configs,
|
234
|
+
errorOnUnmatchedPattern
|
235
|
+
}) {
|
125
236
|
|
126
237
|
if (patterns.length === 0) {
|
127
238
|
return [];
|
128
239
|
}
|
129
240
|
|
130
|
-
|
241
|
+
/*
|
242
|
+
* In this section we are converting the patterns into Minimatch
|
243
|
+
* instances for performance reasons. Because we are doing the same
|
244
|
+
* matches repeatedly, it's best to compile those patterns once and
|
245
|
+
* reuse them multiple times.
|
246
|
+
*
|
247
|
+
* To do that, we convert any patterns with an absolute path into a
|
248
|
+
* relative path and normalize it to Posix-style slashes. We also keep
|
249
|
+
* track of the relative patterns to map them back to the original
|
250
|
+
* patterns, which we need in order to throw an error if there are any
|
251
|
+
* unmatched patterns.
|
252
|
+
*/
|
253
|
+
const relativeToPatterns = new Map();
|
254
|
+
const matchers = patterns.map((pattern, i) => {
|
131
255
|
const patternToUse = path.isAbsolute(pattern)
|
132
256
|
? normalizeToPosix(path.relative(basePath, pattern))
|
133
257
|
: pattern;
|
134
258
|
|
135
|
-
|
259
|
+
relativeToPatterns.set(patternToUse, patterns[i]);
|
260
|
+
|
261
|
+
return new Minimatch(patternToUse, MINIMATCH_OPTIONS);
|
136
262
|
});
|
137
263
|
|
138
|
-
|
264
|
+
/*
|
265
|
+
* We track unmatched patterns because we may want to throw an error when
|
266
|
+
* they occur. To start, this set is initialized with all of the patterns.
|
267
|
+
* Every time a match occurs, the pattern is removed from the set, making
|
268
|
+
* it easy to tell if we have any unmatched patterns left at the end of
|
269
|
+
* search.
|
270
|
+
*/
|
271
|
+
const unmatchedPatterns = new Set([...relativeToPatterns.keys()]);
|
272
|
+
|
273
|
+
const filePaths = (await doFsWalk(basePath, {
|
139
274
|
|
140
275
|
deepFilter(entry) {
|
141
276
|
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
|
@@ -151,94 +286,171 @@ async function globSearch({ basePath, patterns, configs }) {
|
|
151
286
|
return false;
|
152
287
|
}
|
153
288
|
|
154
|
-
|
289
|
+
/*
|
290
|
+
* Optimization: We need to track when patterns are left unmatched
|
291
|
+
* and so we use `unmatchedPatterns` to do that. There is a bit of
|
292
|
+
* complexity here because the same file can be matched by more than
|
293
|
+
* one pattern. So, when we start, we actually need to test every
|
294
|
+
* pattern against every file. Once we know there are no remaining
|
295
|
+
* unmatched patterns, then we can switch to just looking for the
|
296
|
+
* first matching pattern for improved speed.
|
297
|
+
*/
|
298
|
+
const matchesPattern = unmatchedPatterns.size > 0
|
299
|
+
? matchers.reduce((previousValue, matcher) => {
|
300
|
+
const pathMatches = matcher.match(relativePath);
|
301
|
+
|
302
|
+
/*
|
303
|
+
* We updated the unmatched patterns set only if the path
|
304
|
+
* matches and the file isn't ignored. If the file is
|
305
|
+
* ignored, that means there wasn't a match for the
|
306
|
+
* pattern so it should not be removed.
|
307
|
+
*
|
308
|
+
* Performance note: isFileIgnored() aggressively caches
|
309
|
+
* results so there is no performance penalty for calling
|
310
|
+
* it twice with the same argument.
|
311
|
+
*/
|
312
|
+
if (pathMatches && !configs.isFileIgnored(entry.path)) {
|
313
|
+
unmatchedPatterns.delete(matcher.pattern);
|
314
|
+
}
|
315
|
+
|
316
|
+
return pathMatches || previousValue;
|
317
|
+
}, false)
|
318
|
+
: matchers.some(matcher => matcher.match(relativePath));
|
155
319
|
|
156
320
|
return matchesPattern && !configs.isFileIgnored(entry.path);
|
157
321
|
}
|
322
|
+
|
158
323
|
})).map(entry => entry.path);
|
159
324
|
|
325
|
+
// now check to see if we have any unmatched patterns
|
326
|
+
if (errorOnUnmatchedPattern && unmatchedPatterns.size > 0) {
|
327
|
+
throw new UnmatchedSearchPatternsError({
|
328
|
+
basePath,
|
329
|
+
unmatchedPatterns: [...unmatchedPatterns].map(
|
330
|
+
pattern => relativeToPatterns.get(pattern)
|
331
|
+
),
|
332
|
+
patterns,
|
333
|
+
rawPatterns
|
334
|
+
});
|
335
|
+
}
|
336
|
+
|
337
|
+
return filePaths;
|
338
|
+
}
|
339
|
+
|
340
|
+
/**
|
341
|
+
* Throws an error for unmatched patterns. The error will only contain information about the first one.
|
342
|
+
* Checks to see if there are any ignored results for a given search.
|
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.unmatchedPatterns A non-empty array of glob patterns
|
350
|
+
* that were unmatched in the original search.
|
351
|
+
* @returns {void} Always throws an error.
|
352
|
+
* @throws {NoFilesFoundError} If the first unmatched pattern
|
353
|
+
* doesn't match any files even when there are no ignores.
|
354
|
+
* @throws {AllFilesIgnoredError} If the first unmatched pattern
|
355
|
+
* matches some files when there are no ignores.
|
356
|
+
*/
|
357
|
+
async function throwErrorForUnmatchedPatterns({
|
358
|
+
basePath,
|
359
|
+
patterns,
|
360
|
+
rawPatterns,
|
361
|
+
unmatchedPatterns
|
362
|
+
}) {
|
363
|
+
|
364
|
+
const pattern = unmatchedPatterns[0];
|
365
|
+
const rawPattern = rawPatterns[patterns.indexOf(pattern)];
|
366
|
+
|
367
|
+
const patternHasMatch = await globMatch({
|
368
|
+
basePath,
|
369
|
+
pattern
|
370
|
+
});
|
371
|
+
|
372
|
+
if (patternHasMatch) {
|
373
|
+
throw new AllFilesIgnoredError(rawPattern);
|
374
|
+
}
|
375
|
+
|
376
|
+
// if we get here there are truly no matches
|
377
|
+
throw new NoFilesFoundError(rawPattern, true);
|
160
378
|
}
|
161
379
|
|
162
380
|
/**
|
163
381
|
* Performs multiple glob searches in parallel.
|
164
382
|
* @param {Object} options The options for this function.
|
165
|
-
* @param {
|
383
|
+
* @param {Map<string,GlobSearch>} options.searches
|
166
384
|
* An array of glob patterns to match.
|
167
385
|
* @param {FlatConfigArray} options.configs The config array to use for
|
168
386
|
* determining what to ignore.
|
387
|
+
* @param {boolean} options.errorOnUnmatchedPattern Determines if an
|
388
|
+
* unmatched glob pattern should throw an error.
|
169
389
|
* @returns {Promise<Array<string>>} An array of matching file paths
|
170
390
|
* or an empty array if there are no matches.
|
171
391
|
*/
|
172
|
-
async function globMultiSearch({ searches, configs }) {
|
392
|
+
async function globMultiSearch({ searches, configs, errorOnUnmatchedPattern }) {
|
173
393
|
|
174
|
-
|
175
|
-
|
176
|
-
|
394
|
+
/*
|
395
|
+
* For convenience, we normalized the search map into an array of objects.
|
396
|
+
* Next, we filter out all searches that have no patterns. This happens
|
397
|
+
* primarily for the cwd, which is prepopulated in the searches map as an
|
398
|
+
* optimization. However, if it has no patterns, it means all patterns
|
399
|
+
* occur outside of the cwd and we can safely filter out that search.
|
400
|
+
*/
|
401
|
+
const normalizedSearches = [...searches].map(
|
402
|
+
([basePath, { patterns, rawPatterns }]) => ({ basePath, patterns, rawPatterns })
|
403
|
+
).filter(({ patterns }) => patterns.length > 0);
|
404
|
+
|
405
|
+
const results = await Promise.allSettled(
|
406
|
+
normalizedSearches.map(
|
407
|
+
({ basePath, patterns, rawPatterns }) => globSearch({
|
408
|
+
basePath,
|
409
|
+
patterns,
|
410
|
+
rawPatterns,
|
411
|
+
configs,
|
412
|
+
errorOnUnmatchedPattern
|
413
|
+
})
|
177
414
|
)
|
178
415
|
);
|
179
416
|
|
180
|
-
|
181
|
-
}
|
417
|
+
const filePaths = [];
|
182
418
|
|
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;
|
419
|
+
for (let i = 0; i < results.length; i++) {
|
197
420
|
|
198
|
-
|
421
|
+
const result = results[i];
|
422
|
+
const currentSearch = normalizedSearches[i];
|
199
423
|
|
200
|
-
|
201
|
-
|
202
|
-
deepFilter(entry) {
|
203
|
-
const relativePath = normalizeToPosix(path.relative(basePath, entry.path));
|
424
|
+
if (result.status === "fulfilled") {
|
204
425
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
entryFilter(entry) {
|
209
|
-
if (found || entry.dirent.isDirectory()) {
|
210
|
-
return false;
|
426
|
+
// if the search was successful just add the results
|
427
|
+
if (result.value.length > 0) {
|
428
|
+
filePaths.push(...result.value);
|
211
429
|
}
|
212
430
|
|
213
|
-
|
431
|
+
continue;
|
432
|
+
}
|
214
433
|
|
215
|
-
|
216
|
-
|
217
|
-
return true;
|
218
|
-
}
|
434
|
+
// if we make it here then there was an error
|
435
|
+
const error = result.reason;
|
219
436
|
|
220
|
-
|
437
|
+
// unexpected errors should be re-thrown
|
438
|
+
if (!error.basePath) {
|
439
|
+
throw error;
|
221
440
|
}
|
222
|
-
};
|
223
441
|
|
224
|
-
|
442
|
+
if (errorOnUnmatchedPattern) {
|
225
443
|
|
226
|
-
|
227
|
-
|
444
|
+
await throwErrorForUnmatchedPatterns({
|
445
|
+
...currentSearch,
|
446
|
+
unmatchedPatterns: error.unmatchedPatterns
|
447
|
+
});
|
228
448
|
|
229
|
-
|
230
|
-
globStream.destroy();
|
231
|
-
resolve(true);
|
232
|
-
});
|
449
|
+
}
|
233
450
|
|
234
|
-
|
235
|
-
globStream.on("error", () => {});
|
451
|
+
}
|
236
452
|
|
237
|
-
|
238
|
-
resolve(false);
|
239
|
-
});
|
240
|
-
globStream.read();
|
241
|
-
});
|
453
|
+
return [...new Set(filePaths)];
|
242
454
|
|
243
455
|
}
|
244
456
|
|
@@ -335,47 +547,17 @@ async function findFiles({
|
|
335
547
|
}
|
336
548
|
});
|
337
549
|
|
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
550
|
// there were patterns that didn't match anything, tell the user
|
375
551
|
if (errorOnUnmatchedPattern && missingPatterns.length) {
|
376
552
|
throw new NoFilesFoundError(missingPatterns[0], globInputPaths);
|
377
553
|
}
|
378
554
|
|
555
|
+
// now we are safe to do the search
|
556
|
+
const globbyResults = await globMultiSearch({
|
557
|
+
searches,
|
558
|
+
configs,
|
559
|
+
errorOnUnmatchedPattern
|
560
|
+
});
|
379
561
|
|
380
562
|
return [
|
381
563
|
...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
|
|
@@ -15,7 +15,7 @@ module.exports = {
|
|
15
15
|
type: "problem",
|
16
16
|
|
17
17
|
docs: {
|
18
|
-
description: "Enforce \"for\" loop update clause moving the counter in the right direction
|
18
|
+
description: "Enforce \"for\" loop update clause moving the counter in the right direction",
|
19
19
|
recommended: true,
|
20
20
|
url: "https://eslint.org/docs/rules/for-direction"
|
21
21
|
},
|
@@ -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"),
|
package/lib/rules/key-spacing.js
CHANGED
@@ -334,6 +334,19 @@ module.exports = {
|
|
334
334
|
|
335
335
|
const sourceCode = context.getSourceCode();
|
336
336
|
|
337
|
+
/**
|
338
|
+
* Determines if the given property is key-value property.
|
339
|
+
* @param {ASTNode} property Property node to check.
|
340
|
+
* @returns {boolean} Whether the property is a key-value property.
|
341
|
+
*/
|
342
|
+
function isKeyValueProperty(property) {
|
343
|
+
return !(
|
344
|
+
(property.method ||
|
345
|
+
property.shorthand ||
|
346
|
+
property.kind !== "init" || property.type !== "Property") // Could be "ExperimentalSpreadProperty" or "SpreadElement"
|
347
|
+
);
|
348
|
+
}
|
349
|
+
|
337
350
|
/**
|
338
351
|
* Checks whether a property is a member of the property group it follows.
|
339
352
|
* @param {ASTNode} lastMember The last Property known to be in the group.
|
@@ -342,9 +355,9 @@ module.exports = {
|
|
342
355
|
*/
|
343
356
|
function continuesPropertyGroup(lastMember, candidate) {
|
344
357
|
const groupEndLine = lastMember.loc.start.line,
|
345
|
-
|
358
|
+
candidateValueStartLine = (isKeyValueProperty(candidate) ? candidate.value : candidate).loc.start.line;
|
346
359
|
|
347
|
-
if (
|
360
|
+
if (candidateValueStartLine - groupEndLine <= 1) {
|
348
361
|
return true;
|
349
362
|
}
|
350
363
|
|
@@ -358,7 +371,7 @@ module.exports = {
|
|
358
371
|
if (
|
359
372
|
leadingComments.length &&
|
360
373
|
leadingComments[0].loc.start.line - groupEndLine <= 1 &&
|
361
|
-
|
374
|
+
candidateValueStartLine - last(leadingComments).loc.end.line <= 1
|
362
375
|
) {
|
363
376
|
for (let i = 1; i < leadingComments.length; i++) {
|
364
377
|
if (leadingComments[i].loc.start.line - leadingComments[i - 1].loc.end.line > 1) {
|
@@ -371,19 +384,6 @@ module.exports = {
|
|
371
384
|
return false;
|
372
385
|
}
|
373
386
|
|
374
|
-
/**
|
375
|
-
* Determines if the given property is key-value property.
|
376
|
-
* @param {ASTNode} property Property node to check.
|
377
|
-
* @returns {boolean} Whether the property is a key-value property.
|
378
|
-
*/
|
379
|
-
function isKeyValueProperty(property) {
|
380
|
-
return !(
|
381
|
-
(property.method ||
|
382
|
-
property.shorthand ||
|
383
|
-
property.kind !== "init" || property.type !== "Property") // Could be "ExperimentalSpreadProperty" or "SpreadElement"
|
384
|
-
);
|
385
|
-
}
|
386
|
-
|
387
387
|
/**
|
388
388
|
* Starting from the given a node (a property.key node here) looks forward
|
389
389
|
* until it finds the last token before a colon punctuator and returns it.
|
@@ -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) {
|
@@ -71,6 +71,24 @@ function isMultiplyByOne(node) {
|
|
71
71
|
);
|
72
72
|
}
|
73
73
|
|
74
|
+
/**
|
75
|
+
* Checks whether the given node logically represents multiplication by a fraction of `1`.
|
76
|
+
* For example, `a * 1` in `a * 1 / b` is technically multiplication by `1`, but the
|
77
|
+
* whole expression can be logically interpreted as `a * (1 / b)` rather than `(a * 1) / b`.
|
78
|
+
* @param {BinaryExpression} node A BinaryExpression node to check.
|
79
|
+
* @param {SourceCode} sourceCode The source code object.
|
80
|
+
* @returns {boolean} Whether or not the node is a multiplying by a fraction of `1`.
|
81
|
+
*/
|
82
|
+
function isMultiplyByFractionOfOne(node, sourceCode) {
|
83
|
+
return node.type === "BinaryExpression" &&
|
84
|
+
node.operator === "*" &&
|
85
|
+
(node.right.type === "Literal" && node.right.value === 1) &&
|
86
|
+
node.parent.type === "BinaryExpression" &&
|
87
|
+
node.parent.operator === "/" &&
|
88
|
+
node.parent.left === node &&
|
89
|
+
!astUtils.isParenthesised(sourceCode, node);
|
90
|
+
}
|
91
|
+
|
74
92
|
/**
|
75
93
|
* Checks whether the result of a node is numeric or not
|
76
94
|
* @param {ASTNode} node The node to test
|
@@ -290,7 +308,8 @@ module.exports = {
|
|
290
308
|
|
291
309
|
// 1 * foo
|
292
310
|
operatorAllowed = options.allow.includes("*");
|
293
|
-
const nonNumericOperand = !operatorAllowed && options.number && isMultiplyByOne(node) &&
|
311
|
+
const nonNumericOperand = !operatorAllowed && options.number && isMultiplyByOne(node) && !isMultiplyByFractionOfOne(node, sourceCode) &&
|
312
|
+
getNonNumericOperand(node);
|
294
313
|
|
295
314
|
if (nonNumericOperand) {
|
296
315
|
const recommendation = `Number(${sourceCode.getText(nonNumericOperand)})`;
|
@@ -65,6 +65,10 @@ module.exports = {
|
|
65
65
|
ignoreDefaultValues: {
|
66
66
|
type: "boolean",
|
67
67
|
default: false
|
68
|
+
},
|
69
|
+
ignoreClassFieldInitialValues: {
|
70
|
+
type: "boolean",
|
71
|
+
default: false
|
68
72
|
}
|
69
73
|
},
|
70
74
|
additionalProperties: false
|
@@ -82,7 +86,8 @@ module.exports = {
|
|
82
86
|
enforceConst = !!config.enforceConst,
|
83
87
|
ignore = new Set((config.ignore || []).map(normalizeIgnoreValue)),
|
84
88
|
ignoreArrayIndexes = !!config.ignoreArrayIndexes,
|
85
|
-
ignoreDefaultValues = !!config.ignoreDefaultValues
|
89
|
+
ignoreDefaultValues = !!config.ignoreDefaultValues,
|
90
|
+
ignoreClassFieldInitialValues = !!config.ignoreClassFieldInitialValues;
|
86
91
|
|
87
92
|
const okTypes = detectObjects ? [] : ["ObjectExpression", "Property", "AssignmentExpression"];
|
88
93
|
|
@@ -106,6 +111,17 @@ module.exports = {
|
|
106
111
|
return parent.type === "AssignmentPattern" && parent.right === fullNumberNode;
|
107
112
|
}
|
108
113
|
|
114
|
+
/**
|
115
|
+
* Returns whether the number is the initial value of a class field.
|
116
|
+
* @param {ASTNode} fullNumberNode `Literal` or `UnaryExpression` full number node
|
117
|
+
* @returns {boolean} true if the number is the initial value of a class field.
|
118
|
+
*/
|
119
|
+
function isClassFieldInitialValue(fullNumberNode) {
|
120
|
+
const parent = fullNumberNode.parent;
|
121
|
+
|
122
|
+
return parent.type === "PropertyDefinition" && parent.value === fullNumberNode;
|
123
|
+
}
|
124
|
+
|
109
125
|
/**
|
110
126
|
* Returns whether the given node is used as a radix within parseInt() or Number.parseInt()
|
111
127
|
* @param {ASTNode} fullNumberNode `Literal` or `UnaryExpression` full number node
|
@@ -194,6 +210,7 @@ module.exports = {
|
|
194
210
|
if (
|
195
211
|
isIgnoredValue(value) ||
|
196
212
|
(ignoreDefaultValues && isDefaultValue(fullNumberNode)) ||
|
213
|
+
(ignoreClassFieldInitialValues && isClassFieldInitialValue(fullNumberNode)) ||
|
197
214
|
isParseIntRadix(fullNumberNode) ||
|
198
215
|
isJSXNumber(fullNumberNode) ||
|
199
216
|
(ignoreArrayIndexes && isArrayIndex(fullNumberNode, value))
|
@@ -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
|
+
};
|
@@ -16,7 +16,7 @@ const getPropertyName = require("./utils/ast-utils").getStaticPropertyName;
|
|
16
16
|
// Helpers
|
17
17
|
//------------------------------------------------------------------------------
|
18
18
|
|
19
|
-
const nonCallableGlobals = ["Atomics", "JSON", "Math", "Reflect"];
|
19
|
+
const nonCallableGlobals = ["Atomics", "JSON", "Math", "Reflect", "Intl"];
|
20
20
|
|
21
21
|
/**
|
22
22
|
* Returns the name of the node to report
|
@@ -247,7 +247,7 @@ module.exports = {
|
|
247
247
|
|
248
248
|
docs: {
|
249
249
|
description:
|
250
|
-
"Disallow using Object.assign with an object literal as the first argument and prefer the use of object spread instead
|
250
|
+
"Disallow using Object.assign with an object literal as the first argument and prefer the use of object spread instead",
|
251
251
|
recommended: false,
|
252
252
|
url: "https://eslint.org/docs/rules/prefer-object-spread"
|
253
253
|
},
|
@@ -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 {
|
@@ -97,7 +97,7 @@ function environment() {
|
|
97
97
|
*/
|
98
98
|
function getNpmPackageVersion(pkg, { global = false } = {}) {
|
99
99
|
const npmBinArgs = ["bin", "-g"];
|
100
|
-
const npmLsArgs = ["ls", "--depth=0", "--json",
|
100
|
+
const npmLsArgs = ["ls", "--depth=0", "--json", pkg];
|
101
101
|
|
102
102
|
if (global) {
|
103
103
|
npmLsArgs.push("-g");
|