eslint 9.0.0-alpha.1 → 9.0.0-alpha.2
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 +1 -1
- package/lib/eslint/eslint.js +7 -0
- package/lib/linter/code-path-analysis/code-path.js +28 -12
- package/lib/rule-tester/rule-tester.js +46 -2
- package/lib/rules/no-implicit-coercion.js +51 -25
- package/lib/rules/no-restricted-imports.js +53 -43
- package/lib/rules/no-this-before-super.js +17 -4
- package/lib/shared/serialization.js +55 -0
- package/package.json +4 -4
package/README.md
CHANGED
@@ -117,7 +117,7 @@ Yes, ESLint natively supports parsing JSX syntax (this must be enabled in [confi
|
|
117
117
|
|
118
118
|
### What ECMAScript versions does ESLint support?
|
119
119
|
|
120
|
-
ESLint has full support for ECMAScript 3, 5
|
120
|
+
ESLint has full support for ECMAScript 3, 5, and every year from 2015 up until the most recent stage 4 specification (the default). You can set your desired ECMAScript syntax and other settings (like global variables) through [configuration](https://eslint.org/docs/latest/use/configure).
|
121
121
|
|
122
122
|
### What about experimental features?
|
123
123
|
|
package/lib/eslint/eslint.js
CHANGED
@@ -613,6 +613,13 @@ class ESLint {
|
|
613
613
|
});
|
614
614
|
}
|
615
615
|
|
616
|
+
// Check for the .eslintignore file, and warn if it's present.
|
617
|
+
if (existsSync(path.resolve(processedOptions.cwd, ".eslintignore"))) {
|
618
|
+
process.emitWarning(
|
619
|
+
"The \".eslintignore\" file is no longer supported. Switch to using the \"ignores\" property in \"eslint.config.js\": https://eslint.org/docs/latest/use/configure/migration-guide#ignoring-files",
|
620
|
+
"ESLintIgnoreWarning"
|
621
|
+
);
|
622
|
+
}
|
616
623
|
}
|
617
624
|
|
618
625
|
/**
|
@@ -177,8 +177,8 @@ class CodePath {
|
|
177
177
|
// tracks the traversal steps
|
178
178
|
const stack = [[startSegment, 0]];
|
179
179
|
|
180
|
-
//
|
181
|
-
|
180
|
+
// segments that have been skipped during traversal
|
181
|
+
const skipped = new Set();
|
182
182
|
|
183
183
|
// indicates if we exited early from the traversal
|
184
184
|
let broken = false;
|
@@ -193,11 +193,7 @@ class CodePath {
|
|
193
193
|
* @returns {void}
|
194
194
|
*/
|
195
195
|
skip() {
|
196
|
-
|
197
|
-
broken = true;
|
198
|
-
} else {
|
199
|
-
skippedSegment = stack.at(-2)[0];
|
200
|
-
}
|
196
|
+
skipped.add(segment);
|
201
197
|
},
|
202
198
|
|
203
199
|
/**
|
@@ -222,6 +218,18 @@ class CodePath {
|
|
222
218
|
);
|
223
219
|
}
|
224
220
|
|
221
|
+
/**
|
222
|
+
* Checks if a given previous segment has been skipped.
|
223
|
+
* @param {CodePathSegment} prevSegment A previous segment to check.
|
224
|
+
* @returns {boolean} `true` if the segment has been skipped.
|
225
|
+
*/
|
226
|
+
function isSkipped(prevSegment) {
|
227
|
+
return (
|
228
|
+
skipped.has(prevSegment) ||
|
229
|
+
segment.isLoopedPrevSegment(prevSegment)
|
230
|
+
);
|
231
|
+
}
|
232
|
+
|
225
233
|
// the traversal
|
226
234
|
while (stack.length > 0) {
|
227
235
|
|
@@ -258,17 +266,21 @@ class CodePath {
|
|
258
266
|
continue;
|
259
267
|
}
|
260
268
|
|
261
|
-
// Reset the skipping flag if all branches have been skipped.
|
262
|
-
if (skippedSegment && segment.prevSegments.includes(skippedSegment)) {
|
263
|
-
skippedSegment = null;
|
264
|
-
}
|
265
269
|
visited.add(segment);
|
266
270
|
|
271
|
+
|
272
|
+
// Skips the segment if all previous segments have been skipped.
|
273
|
+
const shouldSkip = (
|
274
|
+
skipped.size > 0 &&
|
275
|
+
segment.prevSegments.length > 0 &&
|
276
|
+
segment.prevSegments.every(isSkipped)
|
277
|
+
);
|
278
|
+
|
267
279
|
/*
|
268
280
|
* If the most recent segment hasn't been skipped, then we call
|
269
281
|
* the callback, passing in the segment and the controller.
|
270
282
|
*/
|
271
|
-
if (!
|
283
|
+
if (!shouldSkip) {
|
272
284
|
resolvedCallback.call(this, segment, controller);
|
273
285
|
|
274
286
|
// exit if we're at the last segment
|
@@ -284,6 +296,10 @@ class CodePath {
|
|
284
296
|
if (broken) {
|
285
297
|
break;
|
286
298
|
}
|
299
|
+
} else {
|
300
|
+
|
301
|
+
// If the most recent segment has been skipped, then mark it as skipped.
|
302
|
+
skipped.add(segment);
|
287
303
|
}
|
288
304
|
}
|
289
305
|
|
@@ -13,10 +13,12 @@
|
|
13
13
|
const
|
14
14
|
assert = require("assert"),
|
15
15
|
util = require("util"),
|
16
|
+
path = require("path"),
|
16
17
|
equal = require("fast-deep-equal"),
|
17
18
|
Traverser = require("../shared/traverser"),
|
18
19
|
{ getRuleOptionsSchema } = require("../config/flat-config-helpers"),
|
19
|
-
{ Linter, SourceCodeFixer, interpolate } = require("../linter")
|
20
|
+
{ Linter, SourceCodeFixer, interpolate } = require("../linter"),
|
21
|
+
stringify = require("json-stable-stringify-without-jsonify");
|
20
22
|
|
21
23
|
const { FlatConfigArray } = require("../config/flat-config-array");
|
22
24
|
const { defaultConfig } = require("../config/default-config");
|
@@ -26,6 +28,7 @@ const ajv = require("../shared/ajv")({ strictDefaults: true });
|
|
26
28
|
const parserSymbol = Symbol.for("eslint.RuleTester.parser");
|
27
29
|
const { SourceCode } = require("../source-code");
|
28
30
|
const { ConfigArraySymbol } = require("@humanwhocodes/config-array");
|
31
|
+
const { isSerializable } = require("../shared/serialization");
|
29
32
|
|
30
33
|
//------------------------------------------------------------------------------
|
31
34
|
// Typedefs
|
@@ -499,6 +502,9 @@ class RuleTester {
|
|
499
502
|
linter = this.linter,
|
500
503
|
ruleId = `rule-to-test/${ruleName}`;
|
501
504
|
|
505
|
+
const seenValidTestCases = new Set();
|
506
|
+
const seenInvalidTestCases = new Set();
|
507
|
+
|
502
508
|
if (!rule || typeof rule !== "object" || typeof rule.create !== "function") {
|
503
509
|
throw new TypeError("Rule must be an object with a `create` method");
|
504
510
|
}
|
@@ -577,7 +583,15 @@ class RuleTester {
|
|
577
583
|
* @private
|
578
584
|
*/
|
579
585
|
function runRuleForItem(item) {
|
580
|
-
const
|
586
|
+
const flatConfigArrayOptions = {
|
587
|
+
baseConfig
|
588
|
+
};
|
589
|
+
|
590
|
+
if (item.filename) {
|
591
|
+
flatConfigArrayOptions.basePath = path.parse(item.filename).root;
|
592
|
+
}
|
593
|
+
|
594
|
+
const configs = new FlatConfigArray(testerConfig, flatConfigArrayOptions);
|
581
595
|
|
582
596
|
/*
|
583
597
|
* Modify the returned config so that the parser is wrapped to catch
|
@@ -794,6 +808,32 @@ class RuleTester {
|
|
794
808
|
}
|
795
809
|
}
|
796
810
|
|
811
|
+
/**
|
812
|
+
* Check if this test case is a duplicate of one we have seen before.
|
813
|
+
* @param {Object} item test case object
|
814
|
+
* @param {Set<string>} seenTestCases set of serialized test cases we have seen so far (managed by this function)
|
815
|
+
* @returns {void}
|
816
|
+
* @private
|
817
|
+
*/
|
818
|
+
function checkDuplicateTestCase(item, seenTestCases) {
|
819
|
+
if (!isSerializable(item)) {
|
820
|
+
|
821
|
+
/*
|
822
|
+
* If we can't serialize a test case (because it contains a function, RegExp, etc), skip the check.
|
823
|
+
* This might happen with properties like: options, plugins, settings, languageOptions.parser, languageOptions.parserOptions.
|
824
|
+
*/
|
825
|
+
return;
|
826
|
+
}
|
827
|
+
|
828
|
+
const serializedTestCase = stringify(item);
|
829
|
+
|
830
|
+
assert(
|
831
|
+
!seenTestCases.has(serializedTestCase),
|
832
|
+
"detected duplicate test case"
|
833
|
+
);
|
834
|
+
seenTestCases.add(serializedTestCase);
|
835
|
+
}
|
836
|
+
|
797
837
|
/**
|
798
838
|
* Check if the template is valid or not
|
799
839
|
* all valid cases go through this
|
@@ -809,6 +849,8 @@ class RuleTester {
|
|
809
849
|
assert.ok(typeof item.name === "string", "Optional test case property 'name' must be a string");
|
810
850
|
}
|
811
851
|
|
852
|
+
checkDuplicateTestCase(item, seenValidTestCases);
|
853
|
+
|
812
854
|
const result = runRuleForItem(item);
|
813
855
|
const messages = result.messages;
|
814
856
|
|
@@ -860,6 +902,8 @@ class RuleTester {
|
|
860
902
|
assert.fail("Invalid cases must have at least one error");
|
861
903
|
}
|
862
904
|
|
905
|
+
checkDuplicateTestCase(item, seenInvalidTestCases);
|
906
|
+
|
863
907
|
const ruleHasMetaMessages = hasOwnProperty(rule, "meta") && hasOwnProperty(rule.meta, "messages");
|
864
908
|
const friendlyIDList = ruleHasMetaMessages ? `[${Object.keys(rule.meta.messages).map(key => `'${key}'`).join(", ")}]` : null;
|
865
909
|
|
@@ -188,6 +188,7 @@ function getNonEmptyOperand(node) {
|
|
188
188
|
/** @type {import('../shared/types').Rule} */
|
189
189
|
module.exports = {
|
190
190
|
meta: {
|
191
|
+
hasSuggestions: true,
|
191
192
|
type: "suggestion",
|
192
193
|
|
193
194
|
docs: {
|
@@ -229,7 +230,8 @@ module.exports = {
|
|
229
230
|
}],
|
230
231
|
|
231
232
|
messages: {
|
232
|
-
|
233
|
+
implicitCoercion: "Unexpected implicit coercion encountered. Use `{{recommendation}}` instead.",
|
234
|
+
useRecommendation: "Use `{{recommendation}}` instead."
|
233
235
|
}
|
234
236
|
},
|
235
237
|
|
@@ -241,32 +243,54 @@ module.exports = {
|
|
241
243
|
* Reports an error and autofixes the node
|
242
244
|
* @param {ASTNode} node An ast node to report the error on.
|
243
245
|
* @param {string} recommendation The recommended code for the issue
|
246
|
+
* @param {bool} shouldSuggest Whether this report should offer a suggestion
|
244
247
|
* @param {bool} shouldFix Whether this report should fix the node
|
245
248
|
* @returns {void}
|
246
249
|
*/
|
247
|
-
function report(node, recommendation, shouldFix) {
|
250
|
+
function report(node, recommendation, shouldSuggest, shouldFix) {
|
251
|
+
|
252
|
+
/**
|
253
|
+
* Fix function
|
254
|
+
* @param {RuleFixer} fixer The fixer to fix.
|
255
|
+
* @returns {Fix} The fix object.
|
256
|
+
*/
|
257
|
+
function fix(fixer) {
|
258
|
+
const tokenBefore = sourceCode.getTokenBefore(node);
|
259
|
+
|
260
|
+
if (
|
261
|
+
tokenBefore?.range[1] === node.range[0] &&
|
262
|
+
!astUtils.canTokensBeAdjacent(tokenBefore, recommendation)
|
263
|
+
) {
|
264
|
+
return fixer.replaceText(node, ` ${recommendation}`);
|
265
|
+
}
|
266
|
+
|
267
|
+
return fixer.replaceText(node, recommendation);
|
268
|
+
}
|
269
|
+
|
248
270
|
context.report({
|
249
271
|
node,
|
250
|
-
messageId: "
|
251
|
-
data: {
|
252
|
-
recommendation
|
253
|
-
},
|
272
|
+
messageId: "implicitCoercion",
|
273
|
+
data: { recommendation },
|
254
274
|
fix(fixer) {
|
255
275
|
if (!shouldFix) {
|
256
276
|
return null;
|
257
277
|
}
|
258
278
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
279
|
+
return fix(fixer);
|
280
|
+
},
|
281
|
+
suggest: [
|
282
|
+
{
|
283
|
+
messageId: "useRecommendation",
|
284
|
+
data: { recommendation },
|
285
|
+
fix(fixer) {
|
286
|
+
if (shouldFix || !shouldSuggest) {
|
287
|
+
return null;
|
288
|
+
}
|
289
|
+
|
290
|
+
return fix(fixer);
|
291
|
+
}
|
267
292
|
}
|
268
|
-
|
269
|
-
}
|
293
|
+
]
|
270
294
|
});
|
271
295
|
}
|
272
296
|
|
@@ -278,8 +302,10 @@ module.exports = {
|
|
278
302
|
operatorAllowed = options.allow.includes("!!");
|
279
303
|
if (!operatorAllowed && options.boolean && isDoubleLogicalNegating(node)) {
|
280
304
|
const recommendation = `Boolean(${sourceCode.getText(node.argument.argument)})`;
|
305
|
+
const variable = astUtils.getVariableByName(sourceCode.getScope(node), "Boolean");
|
306
|
+
const booleanExists = variable?.identifiers.length === 0;
|
281
307
|
|
282
|
-
report(node, recommendation, true);
|
308
|
+
report(node, recommendation, true, booleanExists);
|
283
309
|
}
|
284
310
|
|
285
311
|
// ~foo.indexOf(bar)
|
@@ -290,7 +316,7 @@ module.exports = {
|
|
290
316
|
const comparison = node.argument.type === "ChainExpression" ? ">= 0" : "!== -1";
|
291
317
|
const recommendation = `${sourceCode.getText(node.argument)} ${comparison}`;
|
292
318
|
|
293
|
-
report(node, recommendation, false);
|
319
|
+
report(node, recommendation, false, false);
|
294
320
|
}
|
295
321
|
|
296
322
|
// +foo
|
@@ -298,7 +324,7 @@ module.exports = {
|
|
298
324
|
if (!operatorAllowed && options.number && node.operator === "+" && !isNumeric(node.argument)) {
|
299
325
|
const recommendation = `Number(${sourceCode.getText(node.argument)})`;
|
300
326
|
|
301
|
-
report(node, recommendation, true);
|
327
|
+
report(node, recommendation, true, false);
|
302
328
|
}
|
303
329
|
|
304
330
|
// -(-foo)
|
@@ -306,7 +332,7 @@ module.exports = {
|
|
306
332
|
if (!operatorAllowed && options.number && node.operator === "-" && node.argument.type === "UnaryExpression" && node.argument.operator === "-" && !isNumeric(node.argument.argument)) {
|
307
333
|
const recommendation = `Number(${sourceCode.getText(node.argument.argument)})`;
|
308
334
|
|
309
|
-
report(node, recommendation, false);
|
335
|
+
report(node, recommendation, true, false);
|
310
336
|
}
|
311
337
|
},
|
312
338
|
|
@@ -322,7 +348,7 @@ module.exports = {
|
|
322
348
|
if (nonNumericOperand) {
|
323
349
|
const recommendation = `Number(${sourceCode.getText(nonNumericOperand)})`;
|
324
350
|
|
325
|
-
report(node, recommendation, true);
|
351
|
+
report(node, recommendation, true, false);
|
326
352
|
}
|
327
353
|
|
328
354
|
// foo - 0
|
@@ -330,7 +356,7 @@ module.exports = {
|
|
330
356
|
if (!operatorAllowed && options.number && node.operator === "-" && node.right.type === "Literal" && node.right.value === 0 && !isNumeric(node.left)) {
|
331
357
|
const recommendation = `Number(${sourceCode.getText(node.left)})`;
|
332
358
|
|
333
|
-
report(node, recommendation, true);
|
359
|
+
report(node, recommendation, true, false);
|
334
360
|
}
|
335
361
|
|
336
362
|
// "" + foo
|
@@ -338,7 +364,7 @@ module.exports = {
|
|
338
364
|
if (!operatorAllowed && options.string && isConcatWithEmptyString(node)) {
|
339
365
|
const recommendation = `String(${sourceCode.getText(getNonEmptyOperand(node))})`;
|
340
366
|
|
341
|
-
report(node, recommendation, true);
|
367
|
+
report(node, recommendation, true, false);
|
342
368
|
}
|
343
369
|
},
|
344
370
|
|
@@ -351,7 +377,7 @@ module.exports = {
|
|
351
377
|
const code = sourceCode.getText(getNonEmptyOperand(node));
|
352
378
|
const recommendation = `${code} = String(${code})`;
|
353
379
|
|
354
|
-
report(node, recommendation, true);
|
380
|
+
report(node, recommendation, true, false);
|
355
381
|
}
|
356
382
|
},
|
357
383
|
|
@@ -389,7 +415,7 @@ module.exports = {
|
|
389
415
|
const code = sourceCode.getText(node.expressions[0]);
|
390
416
|
const recommendation = `String(${code})`;
|
391
417
|
|
392
|
-
report(node, recommendation, true);
|
418
|
+
report(node, recommendation, true, false);
|
393
419
|
}
|
394
420
|
};
|
395
421
|
}
|
@@ -161,17 +161,25 @@ module.exports = {
|
|
161
161
|
(Object.hasOwn(options[0], "paths") || Object.hasOwn(options[0], "patterns"));
|
162
162
|
|
163
163
|
const restrictedPaths = (isPathAndPatternsObject ? options[0].paths : context.options) || [];
|
164
|
-
const
|
164
|
+
const groupedRestrictedPaths = restrictedPaths.reduce((memo, importSource) => {
|
165
|
+
const path = typeof importSource === "string"
|
166
|
+
? importSource
|
167
|
+
: importSource.name;
|
168
|
+
|
169
|
+
if (!memo[path]) {
|
170
|
+
memo[path] = [];
|
171
|
+
}
|
172
|
+
|
165
173
|
if (typeof importSource === "string") {
|
166
|
-
memo[
|
174
|
+
memo[path].push({});
|
167
175
|
} else {
|
168
|
-
memo[
|
176
|
+
memo[path].push({
|
169
177
|
message: importSource.message,
|
170
178
|
importNames: importSource.importNames
|
171
|
-
};
|
179
|
+
});
|
172
180
|
}
|
173
181
|
return memo;
|
174
|
-
},
|
182
|
+
}, Object.create(null));
|
175
183
|
|
176
184
|
// Handle patterns too, either as strings or groups
|
177
185
|
let restrictedPatterns = (isPathAndPatternsObject ? options[0].patterns : []) || [];
|
@@ -203,57 +211,59 @@ module.exports = {
|
|
203
211
|
* @private
|
204
212
|
*/
|
205
213
|
function checkRestrictedPathAndReport(importSource, importNames, node) {
|
206
|
-
if (!Object.hasOwn(
|
214
|
+
if (!Object.hasOwn(groupedRestrictedPaths, importSource)) {
|
207
215
|
return;
|
208
216
|
}
|
209
217
|
|
210
|
-
|
211
|
-
|
218
|
+
groupedRestrictedPaths[importSource].forEach(restrictedPathEntry => {
|
219
|
+
const customMessage = restrictedPathEntry.message;
|
220
|
+
const restrictedImportNames = restrictedPathEntry.importNames;
|
212
221
|
|
213
|
-
|
214
|
-
|
215
|
-
|
222
|
+
if (restrictedImportNames) {
|
223
|
+
if (importNames.has("*")) {
|
224
|
+
const specifierData = importNames.get("*")[0];
|
225
|
+
|
226
|
+
context.report({
|
227
|
+
node,
|
228
|
+
messageId: customMessage ? "everythingWithCustomMessage" : "everything",
|
229
|
+
loc: specifierData.loc,
|
230
|
+
data: {
|
231
|
+
importSource,
|
232
|
+
importNames: restrictedImportNames,
|
233
|
+
customMessage
|
234
|
+
}
|
235
|
+
});
|
236
|
+
}
|
216
237
|
|
238
|
+
restrictedImportNames.forEach(importName => {
|
239
|
+
if (importNames.has(importName)) {
|
240
|
+
const specifiers = importNames.get(importName);
|
241
|
+
|
242
|
+
specifiers.forEach(specifier => {
|
243
|
+
context.report({
|
244
|
+
node,
|
245
|
+
messageId: customMessage ? "importNameWithCustomMessage" : "importName",
|
246
|
+
loc: specifier.loc,
|
247
|
+
data: {
|
248
|
+
importSource,
|
249
|
+
customMessage,
|
250
|
+
importName
|
251
|
+
}
|
252
|
+
});
|
253
|
+
});
|
254
|
+
}
|
255
|
+
});
|
256
|
+
} else {
|
217
257
|
context.report({
|
218
258
|
node,
|
219
|
-
messageId: customMessage ? "
|
220
|
-
loc: specifierData.loc,
|
259
|
+
messageId: customMessage ? "pathWithCustomMessage" : "path",
|
221
260
|
data: {
|
222
261
|
importSource,
|
223
|
-
importNames: restrictedImportNames,
|
224
262
|
customMessage
|
225
263
|
}
|
226
264
|
});
|
227
265
|
}
|
228
|
-
|
229
|
-
restrictedImportNames.forEach(importName => {
|
230
|
-
if (importNames.has(importName)) {
|
231
|
-
const specifiers = importNames.get(importName);
|
232
|
-
|
233
|
-
specifiers.forEach(specifier => {
|
234
|
-
context.report({
|
235
|
-
node,
|
236
|
-
messageId: customMessage ? "importNameWithCustomMessage" : "importName",
|
237
|
-
loc: specifier.loc,
|
238
|
-
data: {
|
239
|
-
importSource,
|
240
|
-
customMessage,
|
241
|
-
importName
|
242
|
-
}
|
243
|
-
});
|
244
|
-
});
|
245
|
-
}
|
246
|
-
});
|
247
|
-
} else {
|
248
|
-
context.report({
|
249
|
-
node,
|
250
|
-
messageId: customMessage ? "pathWithCustomMessage" : "path",
|
251
|
-
data: {
|
252
|
-
importSource,
|
253
|
-
customMessage
|
254
|
-
}
|
255
|
-
});
|
256
|
-
}
|
266
|
+
});
|
257
267
|
}
|
258
268
|
|
259
269
|
/**
|
@@ -197,11 +197,26 @@ module.exports = {
|
|
197
197
|
return;
|
198
198
|
}
|
199
199
|
|
200
|
+
/**
|
201
|
+
* A collection of nodes to avoid duplicate reports.
|
202
|
+
* @type {Set<ASTNode>}
|
203
|
+
*/
|
204
|
+
const reported = new Set();
|
205
|
+
|
200
206
|
codePath.traverseSegments((segment, controller) => {
|
201
207
|
const info = segInfoMap[segment.id];
|
208
|
+
const invalidNodes = info.invalidNodes
|
209
|
+
.filter(
|
210
|
+
|
211
|
+
/*
|
212
|
+
* Avoid duplicate reports.
|
213
|
+
* When there is a `finally`, invalidNodes may contain already reported node.
|
214
|
+
*/
|
215
|
+
node => !reported.has(node)
|
216
|
+
);
|
202
217
|
|
203
|
-
for (
|
204
|
-
|
218
|
+
for (const invalidNode of invalidNodes) {
|
219
|
+
reported.add(invalidNode);
|
205
220
|
|
206
221
|
context.report({
|
207
222
|
messageId: "noBeforeSuper",
|
@@ -273,14 +288,12 @@ module.exports = {
|
|
273
288
|
const info = segInfoMap[segment.id];
|
274
289
|
|
275
290
|
if (info.superCalled) {
|
276
|
-
info.invalidNodes = [];
|
277
291
|
controller.skip();
|
278
292
|
} else if (
|
279
293
|
segment.prevSegments.length > 0 &&
|
280
294
|
segment.prevSegments.every(isCalled)
|
281
295
|
) {
|
282
296
|
info.superCalled = true;
|
283
|
-
info.invalidNodes = [];
|
284
297
|
}
|
285
298
|
}
|
286
299
|
);
|
@@ -0,0 +1,55 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Serialization utils.
|
3
|
+
* @author Bryan Mishkin
|
4
|
+
*/
|
5
|
+
|
6
|
+
"use strict";
|
7
|
+
|
8
|
+
/**
|
9
|
+
* Check if a value is a primitive or plain object created by the Object constructor.
|
10
|
+
* @param {any} val the value to check
|
11
|
+
* @returns {boolean} true if so
|
12
|
+
* @private
|
13
|
+
*/
|
14
|
+
function isSerializablePrimitiveOrPlainObject(val) {
|
15
|
+
return (
|
16
|
+
val === null ||
|
17
|
+
typeof val === "string" ||
|
18
|
+
typeof val === "boolean" ||
|
19
|
+
typeof val === "number" ||
|
20
|
+
(typeof val === "object" && val.constructor === Object) ||
|
21
|
+
Array.isArray(val)
|
22
|
+
);
|
23
|
+
}
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Check if a value is serializable.
|
27
|
+
* Functions or objects like RegExp cannot be serialized by JSON.stringify().
|
28
|
+
* Inspired by: https://stackoverflow.com/questions/30579940/reliable-way-to-check-if-objects-is-serializable-in-javascript
|
29
|
+
* @param {any} val the value
|
30
|
+
* @returns {boolean} true if the value is serializable
|
31
|
+
*/
|
32
|
+
function isSerializable(val) {
|
33
|
+
if (!isSerializablePrimitiveOrPlainObject(val)) {
|
34
|
+
return false;
|
35
|
+
}
|
36
|
+
if (typeof val === "object") {
|
37
|
+
for (const property in val) {
|
38
|
+
if (Object.hasOwn(val, property)) {
|
39
|
+
if (!isSerializablePrimitiveOrPlainObject(val[property])) {
|
40
|
+
return false;
|
41
|
+
}
|
42
|
+
if (typeof val[property] === "object") {
|
43
|
+
if (!isSerializable(val[property])) {
|
44
|
+
return false;
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
return true;
|
51
|
+
}
|
52
|
+
|
53
|
+
module.exports = {
|
54
|
+
isSerializable
|
55
|
+
};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "eslint",
|
3
|
-
"version": "9.0.0-alpha.
|
3
|
+
"version": "9.0.0-alpha.2",
|
4
4
|
"author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
|
5
5
|
"description": "An AST-based pattern checker for JavaScript.",
|
6
6
|
"bin": {
|
@@ -66,7 +66,7 @@
|
|
66
66
|
"@eslint-community/eslint-utils": "^4.2.0",
|
67
67
|
"@eslint-community/regexpp": "^4.6.1",
|
68
68
|
"@eslint/eslintrc": "^3.0.0",
|
69
|
-
"@eslint/js": "9.0.0-alpha.
|
69
|
+
"@eslint/js": "9.0.0-alpha.2",
|
70
70
|
"@humanwhocodes/config-array": "^0.11.14",
|
71
71
|
"@humanwhocodes/module-importer": "^1.0.1",
|
72
72
|
"@nodelib/fs.walk": "^1.2.8",
|
@@ -77,7 +77,7 @@
|
|
77
77
|
"escape-string-regexp": "^4.0.0",
|
78
78
|
"eslint-scope": "^8.0.0",
|
79
79
|
"eslint-visitor-keys": "^3.4.3",
|
80
|
-
"espree": "^
|
80
|
+
"espree": "^10.0.0",
|
81
81
|
"esquery": "^1.4.2",
|
82
82
|
"esutils": "^2.0.2",
|
83
83
|
"fast-deep-equal": "^3.1.3",
|
@@ -135,7 +135,7 @@
|
|
135
135
|
"load-perf": "^0.2.0",
|
136
136
|
"markdown-it": "^12.2.0",
|
137
137
|
"markdown-it-container": "^3.0.0",
|
138
|
-
"markdownlint": "^0.
|
138
|
+
"markdownlint": "^0.33.0",
|
139
139
|
"markdownlint-cli": "^0.38.0",
|
140
140
|
"marked": "^4.0.8",
|
141
141
|
"memfs": "^3.0.1",
|