@arcgis/eslint-config 6.0.0-next.11 → 6.0.0-next.13
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/dist/config/core.js +7 -0
- package/dist/plugins/core/index.js +97 -0
- package/package.json +2 -2
package/dist/config/core.js
CHANGED
|
@@ -817,6 +817,13 @@ const eslintConfigCore = [
|
|
|
817
817
|
// Require awaiting assert.screenshot() in tests
|
|
818
818
|
"webgis/require-await-for-async-screenshot-assert": "error"
|
|
819
819
|
}
|
|
820
|
+
},
|
|
821
|
+
{
|
|
822
|
+
files: ["packages/core-packages/core/tests/**/*.{ts,tsx}"],
|
|
823
|
+
ignores: ["packages/core-packages/core/tests/support/schedulingUtils.ts"],
|
|
824
|
+
rules: {
|
|
825
|
+
"core/no-unsafe-fake-timers": "error"
|
|
826
|
+
}
|
|
820
827
|
}
|
|
821
828
|
];
|
|
822
829
|
export {
|
|
@@ -483,6 +483,103 @@ function getDeclaredClass(filename) {
|
|
|
483
483
|
const relativePath = relative(root, filename).replaceAll(win32.sep, ".").replaceAll(posix.sep, ".").replace(/^src\./u, "esri.").replace(/\.[^/.]+$/u, "");
|
|
484
484
|
return relativePath;
|
|
485
485
|
}
|
|
486
|
+
const timerHelpers = /* @__PURE__ */ new Set(["useFakeTimers", "useFastForwardTimers"]);
|
|
487
|
+
const timerGlobals = /* @__PURE__ */ new Set([
|
|
488
|
+
"setTimeout",
|
|
489
|
+
"clearTimeout",
|
|
490
|
+
"setInterval",
|
|
491
|
+
"clearInterval",
|
|
492
|
+
"requestAnimationFrame",
|
|
493
|
+
"cancelAnimationFrame",
|
|
494
|
+
"requestIdleCallback",
|
|
495
|
+
"cancelIdleCallback",
|
|
496
|
+
"queueMicrotask",
|
|
497
|
+
"Date"
|
|
498
|
+
]);
|
|
499
|
+
plugin.createRule({
|
|
500
|
+
name: "no-unsafe-fake-timers",
|
|
501
|
+
meta: {
|
|
502
|
+
docs: {
|
|
503
|
+
description: "Use core's managed timer helpers and do not mix fake timers with global timer spies.",
|
|
504
|
+
defaultLevel: "error",
|
|
505
|
+
isTypeChecked: false
|
|
506
|
+
},
|
|
507
|
+
messages: {
|
|
508
|
+
managedTimers: "Use useFakeTimers or useFastForwardTimers from test-support/schedulingUtils. They restore timers on disposal and test completion.",
|
|
509
|
+
timerSpy: "Do not mix fake timers with global timer spies in the same file. Vitest can reinstall fake timers when restoring disposed spies. Use vi.getTimerCount() or observe timer callbacks instead."
|
|
510
|
+
},
|
|
511
|
+
schema: [],
|
|
512
|
+
type: "problem"
|
|
513
|
+
},
|
|
514
|
+
defaultOptions: [],
|
|
515
|
+
create(context) {
|
|
516
|
+
const vitestNames = /* @__PURE__ */ new Set();
|
|
517
|
+
const vitestNamespaces = /* @__PURE__ */ new Set();
|
|
518
|
+
const helperNames = /* @__PURE__ */ new Set();
|
|
519
|
+
const helperNamespaces = /* @__PURE__ */ new Set();
|
|
520
|
+
const timerSpies = [];
|
|
521
|
+
let usesFakeTimers = false;
|
|
522
|
+
function isVitest(node) {
|
|
523
|
+
return node.type === AST_NODE_TYPES.Identifier && vitestNames.has(node.name) || node.type === AST_NODE_TYPES.MemberExpression && node.object.type === AST_NODE_TYPES.Identifier && vitestNamespaces.has(node.object.name) && ["vi", "vitest"].includes(propertyName(node) ?? "");
|
|
524
|
+
}
|
|
525
|
+
return {
|
|
526
|
+
"ImportDeclaration"(node) {
|
|
527
|
+
const isVitestImport = node.source.value === "vitest";
|
|
528
|
+
if (!isVitestImport && node.source.value !== "test-support/schedulingUtils") {
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
for (const specifier of node.specifiers) {
|
|
532
|
+
if (specifier.type === AST_NODE_TYPES.ImportNamespaceSpecifier) {
|
|
533
|
+
(isVitestImport ? vitestNamespaces : helperNamespaces).add(specifier.local.name);
|
|
534
|
+
} else if (specifier.type === AST_NODE_TYPES.ImportSpecifier) {
|
|
535
|
+
const imported = specifier.imported.type === AST_NODE_TYPES.Identifier ? specifier.imported.name : specifier.imported.value;
|
|
536
|
+
if (isVitestImport ? imported === "vi" || imported === "vitest" : timerHelpers.has(imported)) {
|
|
537
|
+
(isVitestImport ? vitestNames : helperNames).add(specifier.local.name);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
},
|
|
542
|
+
"CallExpression"(node) {
|
|
543
|
+
const callee = node.callee;
|
|
544
|
+
if (callee.type === AST_NODE_TYPES.Identifier && helperNames.has(callee.name)) {
|
|
545
|
+
usesFakeTimers = true;
|
|
546
|
+
}
|
|
547
|
+
if (callee.type !== AST_NODE_TYPES.MemberExpression) {
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
const method = propertyName(callee);
|
|
551
|
+
if (callee.object.type === AST_NODE_TYPES.Identifier && helperNamespaces.has(callee.object.name) && method != null && timerHelpers.has(method)) {
|
|
552
|
+
usesFakeTimers = true;
|
|
553
|
+
}
|
|
554
|
+
if (!isVitest(callee.object)) {
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
if (method === "useFakeTimers" || method === "useRealTimers") {
|
|
558
|
+
usesFakeTimers ||= method === "useFakeTimers";
|
|
559
|
+
context.report({ node, messageId: "managedTimers" });
|
|
560
|
+
} else if (method === "spyOn") {
|
|
561
|
+
const [target, property] = node.arguments;
|
|
562
|
+
if (target?.type === AST_NODE_TYPES.Identifier && ["globalThis", "window", "global", "self"].includes(target.name) && property?.type === AST_NODE_TYPES.Literal && typeof property.value === "string" && timerGlobals.has(property.value)) {
|
|
563
|
+
timerSpies.push(node);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
},
|
|
567
|
+
"Program:exit"() {
|
|
568
|
+
if (usesFakeTimers) {
|
|
569
|
+
for (const node of timerSpies) {
|
|
570
|
+
context.report({ node, messageId: "timerSpy" });
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
function propertyName(node) {
|
|
578
|
+
if (!node.computed && node.property.type === AST_NODE_TYPES.Identifier) {
|
|
579
|
+
return node.property.name;
|
|
580
|
+
}
|
|
581
|
+
return node.property.type === AST_NODE_TYPES.Literal && typeof node.property.value === "string" ? node.property.value : void 0;
|
|
582
|
+
}
|
|
486
583
|
const tsRe = /\.tsx?$/u;
|
|
487
584
|
let gitIndexFiles;
|
|
488
585
|
function getGitIndexFiles(cwd) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcgis/eslint-config",
|
|
3
|
-
"version": "6.0.0-next.
|
|
3
|
+
"version": "6.0.0-next.13",
|
|
4
4
|
"description": "ESLint configuration for WebGIS SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
],
|
|
22
22
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@arcgis/toolkit": "6.0.0-next.
|
|
24
|
+
"@arcgis/toolkit": "6.0.0-next.13",
|
|
25
25
|
"@eslint/markdown": "^8.0.3",
|
|
26
26
|
"@types/confusing-browser-globals": "^1.0.3",
|
|
27
27
|
"@typescript-eslint/utils": "^8.63.0",
|