@arcgis/eslint-config 6.0.0-next.2 → 6.0.0-next.20
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/chunks/imports.js +31 -0
- package/dist/config/core.js +15 -0
- package/dist/plugins/core/index.js +27 -0
- package/dist/plugins/webgis/index.js +82 -1
- package/package.json +2 -2
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { AST_NODE_TYPES, ASTUtils, TSESLint } from "@typescript-eslint/utils";
|
|
2
|
+
function isImportedSymbol(node, sourceCode, moduleName, names) {
|
|
3
|
+
const identifier = node.type === AST_NODE_TYPES.MemberExpression ? node.object : node;
|
|
4
|
+
if (identifier.type !== AST_NODE_TYPES.Identifier) {
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
const variable = ASTUtils.findVariable(sourceCode.getScope(identifier), identifier.name);
|
|
8
|
+
const definition = variable?.defs[0];
|
|
9
|
+
if (definition?.type !== TSESLint.Scope.DefinitionType.ImportBinding || definition.parent.type !== AST_NODE_TYPES.ImportDeclaration || definition.parent.source.value !== moduleName || definition.parent.importKind === "type") {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
const specifier = definition.node;
|
|
13
|
+
if (node.type === AST_NODE_TYPES.MemberExpression) {
|
|
14
|
+
return specifier.type === AST_NODE_TYPES.ImportNamespaceSpecifier && names.includes(propertyName(node) ?? "");
|
|
15
|
+
}
|
|
16
|
+
if (specifier.type !== AST_NODE_TYPES.ImportSpecifier || specifier.importKind === "type") {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
const imported = specifier.imported;
|
|
20
|
+
return names.includes(imported.type === AST_NODE_TYPES.Identifier ? imported.name : imported.value);
|
|
21
|
+
}
|
|
22
|
+
function propertyName(node) {
|
|
23
|
+
if (!node.computed && node.property.type === AST_NODE_TYPES.Identifier) {
|
|
24
|
+
return node.property.name;
|
|
25
|
+
}
|
|
26
|
+
return node.property.type === AST_NODE_TYPES.Literal && typeof node.property.value === "string" ? node.property.value : void 0;
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
isImportedSymbol as i,
|
|
30
|
+
propertyName as p
|
|
31
|
+
};
|
package/dist/config/core.js
CHANGED
|
@@ -814,9 +814,24 @@ const eslintConfigCore = [
|
|
|
814
814
|
message: "Avoid adding has flags manually. Instead use the suite/test-level options to set has flags."
|
|
815
815
|
}
|
|
816
816
|
],
|
|
817
|
+
"webgis/no-unsafe-fake-timers": [
|
|
818
|
+
"error",
|
|
819
|
+
{
|
|
820
|
+
fakeTimerHelpers: [
|
|
821
|
+
{ module: "test-support/schedulingUtils", names: ["useFakeTimers", "useFastForwardTimers"] }
|
|
822
|
+
]
|
|
823
|
+
}
|
|
824
|
+
],
|
|
817
825
|
// Require awaiting assert.screenshot() in tests
|
|
818
826
|
"webgis/require-await-for-async-screenshot-assert": "error"
|
|
819
827
|
}
|
|
828
|
+
},
|
|
829
|
+
{
|
|
830
|
+
files: ["packages/core-packages/core/tests/**/*.{ts,tsx}"],
|
|
831
|
+
ignores: ["packages/core-packages/core/tests/support/schedulingUtils.ts"],
|
|
832
|
+
rules: {
|
|
833
|
+
"core/require-managed-timers": "error"
|
|
834
|
+
}
|
|
820
835
|
}
|
|
821
836
|
];
|
|
822
837
|
export {
|
|
@@ -5,6 +5,7 @@ import path, { relative, win32, posix, normalize } from "path";
|
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
6
|
import { existsSync } from "node:fs";
|
|
7
7
|
import { execSync } from "child_process";
|
|
8
|
+
import { p as propertyName, i as isImportedSymbol } from "../../chunks/imports.js";
|
|
8
9
|
const plugin = makeEslintPlugin(
|
|
9
10
|
"core",
|
|
10
11
|
(rule) => `https://devtopia.esri.com/WebGIS/webgis-sdk/tree/main/packages/support-packages/eslint-config/src/plugins/core/rules/${rule}.ts`
|
|
@@ -676,6 +677,32 @@ plugin.createRule({
|
|
|
676
677
|
};
|
|
677
678
|
}
|
|
678
679
|
});
|
|
680
|
+
plugin.createRule({
|
|
681
|
+
name: "require-managed-timers",
|
|
682
|
+
meta: {
|
|
683
|
+
docs: {
|
|
684
|
+
description: "Use core's managed timer helpers instead of direct Vitest timer setup and restoration.",
|
|
685
|
+
defaultLevel: "error",
|
|
686
|
+
isTypeChecked: false
|
|
687
|
+
},
|
|
688
|
+
messages: {
|
|
689
|
+
managedTimers: "Use useFakeTimers or useFastForwardTimers from test-support/schedulingUtils. They restore timers on disposal and test completion."
|
|
690
|
+
},
|
|
691
|
+
schema: [],
|
|
692
|
+
type: "problem"
|
|
693
|
+
},
|
|
694
|
+
defaultOptions: [],
|
|
695
|
+
create(context) {
|
|
696
|
+
return {
|
|
697
|
+
CallExpression(node) {
|
|
698
|
+
const callee = node.callee;
|
|
699
|
+
if (callee.type === AST_NODE_TYPES.MemberExpression && ["useFakeTimers", "useRealTimers"].includes(propertyName(callee) ?? "") && isImportedSymbol(callee.object, context.sourceCode, "vitest", ["vi", "vitest"])) {
|
|
700
|
+
context.report({ node, messageId: "managedTimers" });
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
});
|
|
679
706
|
plugin.createRule({
|
|
680
707
|
name: "spec-suite-filename",
|
|
681
708
|
meta: {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { m as makeEslintPlugin } from "../../chunks/makePlugin.js";
|
|
2
|
-
import { TSESTree, AST_TOKEN_TYPES, AST_NODE_TYPES, TSESLint } from "@typescript-eslint/utils";
|
|
2
|
+
import { TSESTree, AST_TOKEN_TYPES, AST_NODE_TYPES, TSESLint, ASTUtils } from "@typescript-eslint/utils";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { resolve } from "path/posix";
|
|
5
5
|
import globals from "globals";
|
|
6
6
|
import { uncapitalize, kebabToPascal } from "@arcgis/toolkit/string";
|
|
7
|
+
import { i as isImportedSymbol, p as propertyName } from "../../chunks/imports.js";
|
|
7
8
|
import { e as extractDeclareElementsInterface, k as getComponentDeclaration } from "../../chunks/estree.js";
|
|
8
9
|
const plugin = makeEslintPlugin(
|
|
9
10
|
"webgis",
|
|
@@ -907,6 +908,86 @@ plugin.createRule({
|
|
|
907
908
|
}
|
|
908
909
|
});
|
|
909
910
|
const reTouchingJsDoc = /\*\/\s+\/\*\*/gu;
|
|
911
|
+
const timerGlobals = /* @__PURE__ */ new Set([
|
|
912
|
+
"setTimeout",
|
|
913
|
+
"clearTimeout",
|
|
914
|
+
"setInterval",
|
|
915
|
+
"clearInterval",
|
|
916
|
+
"setImmediate",
|
|
917
|
+
"clearImmediate",
|
|
918
|
+
"requestAnimationFrame",
|
|
919
|
+
"cancelAnimationFrame",
|
|
920
|
+
"requestIdleCallback",
|
|
921
|
+
"cancelIdleCallback",
|
|
922
|
+
"queueMicrotask",
|
|
923
|
+
"Date"
|
|
924
|
+
]);
|
|
925
|
+
plugin.createRule({
|
|
926
|
+
name: "no-unsafe-fake-timers",
|
|
927
|
+
meta: {
|
|
928
|
+
docs: {
|
|
929
|
+
description: "Do not mix fake timers with global timer spies, even across tests in the same file.",
|
|
930
|
+
defaultLevel: "error",
|
|
931
|
+
isTypeChecked: false
|
|
932
|
+
},
|
|
933
|
+
messages: {
|
|
934
|
+
timerSpy: "Do not mix fake timers with global timer spies in the same file. Even disposed spies can corrupt timers during later mock cleanup. Use vi.getTimerCount() or observe timer callbacks instead."
|
|
935
|
+
},
|
|
936
|
+
schema: [
|
|
937
|
+
{
|
|
938
|
+
type: "object",
|
|
939
|
+
properties: {
|
|
940
|
+
fakeTimerHelpers: {
|
|
941
|
+
type: "array",
|
|
942
|
+
items: {
|
|
943
|
+
type: "object",
|
|
944
|
+
properties: {
|
|
945
|
+
module: { type: "string" },
|
|
946
|
+
names: { type: "array", items: { type: "string" }, minItems: 1, uniqueItems: true }
|
|
947
|
+
},
|
|
948
|
+
required: ["module", "names"],
|
|
949
|
+
additionalProperties: false
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
},
|
|
953
|
+
additionalProperties: false
|
|
954
|
+
}
|
|
955
|
+
],
|
|
956
|
+
type: "problem"
|
|
957
|
+
},
|
|
958
|
+
defaultOptions: [{}],
|
|
959
|
+
create(context, [{ fakeTimerHelpers = [] }]) {
|
|
960
|
+
const timerSpies = [];
|
|
961
|
+
let usesFakeTimers = false;
|
|
962
|
+
return {
|
|
963
|
+
"CallExpression"(node) {
|
|
964
|
+
const callee = node.callee;
|
|
965
|
+
usesFakeTimers ||= fakeTimerHelpers.some(
|
|
966
|
+
({ module, names }) => isImportedSymbol(callee, context.sourceCode, module, names)
|
|
967
|
+
);
|
|
968
|
+
if (callee.type !== AST_NODE_TYPES.MemberExpression || !isImportedSymbol(callee.object, context.sourceCode, "vitest", ["vi", "vitest"])) {
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
const method = propertyName(callee);
|
|
972
|
+
if (method === "useFakeTimers") {
|
|
973
|
+
usesFakeTimers = true;
|
|
974
|
+
} else if (method === "spyOn") {
|
|
975
|
+
const [target, property] = node.arguments;
|
|
976
|
+
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) && !ASTUtils.findVariable(context.sourceCode.getScope(target), target.name)?.defs.length) {
|
|
977
|
+
timerSpies.push(node);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
},
|
|
981
|
+
"Program:exit"() {
|
|
982
|
+
if (usesFakeTimers) {
|
|
983
|
+
for (const node of timerSpies) {
|
|
984
|
+
context.report({ node, messageId: "timerSpy" });
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
});
|
|
910
991
|
const description$1 = `Do not use links like [](#something) as they are not portable across .d.ts, and are not validated at build time. Use {@link } syntax, or absolute links. See https://webgis.esri.com/references/api-extractor/tags-reference#link)`;
|
|
911
992
|
plugin.createRule({
|
|
912
993
|
name: "no-unsafe-hash-links",
|
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.20",
|
|
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.20",
|
|
25
25
|
"@eslint/markdown": "^8.0.3",
|
|
26
26
|
"@types/confusing-browser-globals": "^1.0.3",
|
|
27
27
|
"@typescript-eslint/utils": "^8.63.0",
|