@servicetitan/eslint-plugin 22.20.0 → 23.0.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/dist/index.d.ts +3 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/rules/__mocks__/fixture/file.d.ts +2 -0
- package/dist/rules/__mocks__/fixture/file.d.ts.map +1 -0
- package/dist/rules/__mocks__/fixture/file.js +7 -0
- package/dist/rules/__mocks__/fixture/file.js.map +1 -0
- package/dist/rules/__mocks__/fixture/react.d.ts +2 -0
- package/dist/rules/__mocks__/fixture/react.d.ts.map +1 -0
- package/dist/rules/__mocks__/fixture/react.js +7 -0
- package/dist/rules/__mocks__/fixture/react.js.map +1 -0
- package/dist/rules/no-async-in-foreach.d.ts +3 -0
- package/dist/rules/no-async-in-foreach.d.ts.map +1 -0
- package/dist/rules/no-async-in-foreach.js +78 -0
- package/dist/rules/no-async-in-foreach.js.map +1 -0
- package/dist/rules/react/destructure-default-import.d.ts.map +1 -1
- package/dist/rules/react/destructure-default-import.js +0 -4
- package/dist/rules/react/destructure-default-import.js.map +1 -1
- package/package.json +12 -5
- package/src/index.ts +6 -2
- package/src/rules/__mocks__/fixture/file.ts +4 -0
- package/src/rules/__mocks__/fixture/react.tsx +4 -0
- package/src/rules/__mocks__/fixture/tsconfig.json +6 -0
- package/src/rules/__tests__/no-async-in-foreach.test.ts +163 -0
- package/src/rules/no-async-in-foreach.ts +92 -0
- package/src/rules/react/destructure-default-import.ts +0 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
'mobx/no-abstract-decorators': import("eslint").Rule.RuleModule;
|
|
5
|
-
'react/destructure-default-import': import("eslint").Rule.RuleModule;
|
|
6
|
-
'react/no-qualified-type': import("eslint").Rule.RuleModule;
|
|
7
|
-
};
|
|
1
|
+
import { AnyRuleModule } from '@typescript-eslint/utils/ts-eslint';
|
|
2
|
+
import { Rule } from 'eslint';
|
|
3
|
+
export declare const rules: Record<string, Rule.RuleModule | AnyRuleModule>;
|
|
8
4
|
export declare const processors: {
|
|
9
5
|
stub: import("eslint").Linter.LintOptions;
|
|
10
6
|
};
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAS9B,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,GAAG,aAAa,CAOjE,CAAC;AAEF,eAAO,MAAM,UAAU;;CAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.processors = exports.rules = void 0;
|
|
4
|
+
const no_async_in_foreach_1 = require("./rules/no-async-in-foreach");
|
|
4
5
|
const decorators_declare_1 = require("./rules/decorators-declare");
|
|
5
6
|
const use_makeObservable_with_decorators_1 = require("./rules/mobx/use-makeObservable-with-decorators");
|
|
6
7
|
const no_abstract_decorators_1 = require("./rules/mobx/no-abstract-decorators");
|
|
@@ -8,11 +9,12 @@ const destructure_default_import_1 = require("./rules/react/destructure-default-
|
|
|
8
9
|
const no_qualified_type_1 = require("./rules/react/no-qualified-type");
|
|
9
10
|
const stub_1 = require("./processors/stub");
|
|
10
11
|
exports.rules = {
|
|
11
|
-
'use-declare-with-decorators': decorators_declare_1.useDeclareWithDecorators,
|
|
12
12
|
'mobx/use-makeObservable-with-decorators': use_makeObservable_with_decorators_1.mobxUseMakeObservableWithDecorators,
|
|
13
13
|
'mobx/no-abstract-decorators': no_abstract_decorators_1.mobxNoAbstractDecorators,
|
|
14
14
|
'react/destructure-default-import': destructure_default_import_1.reactDestructureDefaultImport,
|
|
15
15
|
'react/no-qualified-type': no_qualified_type_1.reactNoQualifiedType,
|
|
16
|
+
'no-async-in-foreach': no_async_in_foreach_1.noAsyncInForEach,
|
|
17
|
+
'use-declare-with-decorators': decorators_declare_1.useDeclareWithDecorators,
|
|
16
18
|
};
|
|
17
19
|
exports.processors = { stub: stub_1.stub };
|
|
18
20
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAEA,qEAA+D;AAC/D,mEAAsE;AACtE,wGAAsG;AACtG,gFAA+E;AAC/E,yFAAyF;AACzF,uEAAuE;AACvE,4CAAyC;AAE5B,QAAA,KAAK,GAAoD;IAClE,yCAAyC,EAAE,wEAAmC;IAC9E,6BAA6B,EAAE,iDAAwB;IACvD,kCAAkC,EAAE,0DAA6B;IACjE,yBAAyB,EAAE,wCAAoB;IAC/C,qBAAqB,EAAE,sCAAgB;IACvC,6BAA6B,EAAE,6CAAwB;CAC1D,CAAC;AAEW,QAAA,UAAU,GAAG,EAAE,IAAI,EAAJ,WAAI,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../../../src/rules/__mocks__/fixture/file.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../../../src/rules/__mocks__/fixture/file.ts"],"names":[],"mappings":";;AAAA;;;GAGG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../../../src/rules/__mocks__/fixture/react.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","sourceRoot":"","sources":["../../../../src/rules/__mocks__/fixture/react.tsx"],"names":[],"mappings":";;AAAA;;;GAGG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-async-in-foreach.d.ts","sourceRoot":"","sources":["../../src/rules/no-async-in-foreach.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAY,MAAM,0BAA0B,CAAC;AASjE,eAAO,MAAM,gBAAgB,+EAkF3B,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.noAsyncInForEach = void 0;
|
|
7
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
8
|
+
const eslint_utils_1 = require("@typescript-eslint/utils/eslint-utils");
|
|
9
|
+
const type_utils_1 = require("@typescript-eslint/type-utils");
|
|
10
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
11
|
+
const createRule = utils_1.ESLintUtils.RuleCreator(_name => 'https://gist.github.com/joeytwiddle/37d2085425c049629b80956d3c618971');
|
|
12
|
+
exports.noAsyncInForEach = createRule({
|
|
13
|
+
create(context) {
|
|
14
|
+
const services = (0, eslint_utils_1.getParserServices)(context);
|
|
15
|
+
const checker = services.program.getTypeChecker();
|
|
16
|
+
function checkForEachExpression(node) {
|
|
17
|
+
const callExpression = node.parent;
|
|
18
|
+
if (!isAsync(callExpression.arguments[0])) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const callee = callExpression.callee;
|
|
22
|
+
if (!isArrayLike(callee.object)) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
context.report({ messageId: 'noAsyncInForEach', node });
|
|
26
|
+
}
|
|
27
|
+
function getSymbolAtLocation(node) {
|
|
28
|
+
const symbol = services.getSymbolAtLocation(node);
|
|
29
|
+
if (symbol && symbol.flags & typescript_1.default.SymbolFlags.Alias) {
|
|
30
|
+
return checker.getAliasedSymbol(symbol);
|
|
31
|
+
}
|
|
32
|
+
return symbol;
|
|
33
|
+
}
|
|
34
|
+
function isArrayLike(node) {
|
|
35
|
+
const nodeType = services.getTypeAtLocation(node);
|
|
36
|
+
const types = isUnionType(nodeType) ? nodeType.types : [nodeType];
|
|
37
|
+
return types.some(type => checker.isArrayType(type) || checker.isTupleType(type));
|
|
38
|
+
}
|
|
39
|
+
function isAsync(node) {
|
|
40
|
+
return ((node.type === utils_1.TSESTree.AST_NODE_TYPES.ArrowFunctionExpression && node.async) ||
|
|
41
|
+
hasAsyncModifier(getSymbolAtLocation(node)) ||
|
|
42
|
+
returnsPromise(node));
|
|
43
|
+
}
|
|
44
|
+
function isUnionType(type) {
|
|
45
|
+
return (type.flags & typescript_1.default.TypeFlags.Union) !== 0;
|
|
46
|
+
}
|
|
47
|
+
function hasAsyncModifier(symbol) {
|
|
48
|
+
var _a;
|
|
49
|
+
return !!((_a = symbol === null || symbol === void 0 ? void 0 : symbol.declarations) === null || _a === void 0 ? void 0 : _a.find(declaration => {
|
|
50
|
+
var _a;
|
|
51
|
+
return (_a = declaration.modifiers) === null || _a === void 0 ? void 0 : _a.find(modifier => modifier.kind === typescript_1.default.SyntaxKind.AsyncKeyword);
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
function returnsPromise(node) {
|
|
55
|
+
const signatures = services.getTypeAtLocation(node).getCallSignatures();
|
|
56
|
+
if (signatures.length) {
|
|
57
|
+
const returnType = checker.getReturnTypeOfSignature(signatures[0]);
|
|
58
|
+
return (0, type_utils_1.containsAllTypesByName)(returnType, true, new Set(['Promise']));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
"CallExpression[arguments.length=1] > MemberExpression[property.name='forEach']": checkForEachExpression,
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
name: 'no-async-in-foreach',
|
|
66
|
+
meta: {
|
|
67
|
+
docs: {
|
|
68
|
+
description: 'Disallow passing asynchronous callback to Array.forEach',
|
|
69
|
+
},
|
|
70
|
+
messages: {
|
|
71
|
+
noAsyncInForEach: 'Async callback passed to Array.forEach',
|
|
72
|
+
},
|
|
73
|
+
type: 'problem',
|
|
74
|
+
schema: [],
|
|
75
|
+
},
|
|
76
|
+
defaultOptions: [],
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=no-async-in-foreach.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-async-in-foreach.js","sourceRoot":"","sources":["../../src/rules/no-async-in-foreach.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"destructure-default-import.d.ts","sourceRoot":"","sources":["../../../src/rules/react/destructure-default-import.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,eAAO,MAAM,6BAA6B,EAAE,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"destructure-default-import.d.ts","sourceRoot":"","sources":["../../../src/rules/react/destructure-default-import.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,eAAO,MAAM,6BAA6B,EAAE,IAAI,CAAC,UAkChD,CAAC"}
|
|
@@ -17,7 +17,6 @@ exports.reactDestructureDefaultImport = {
|
|
|
17
17
|
create(context) {
|
|
18
18
|
return {
|
|
19
19
|
ImportDeclaration(node) {
|
|
20
|
-
const reactImportSpecifiers = new Set();
|
|
21
20
|
if (node.source.value !== 'react') {
|
|
22
21
|
return;
|
|
23
22
|
}
|
|
@@ -29,9 +28,6 @@ exports.reactDestructureDefaultImport = {
|
|
|
29
28
|
messageId: 'importDefault',
|
|
30
29
|
});
|
|
31
30
|
}
|
|
32
|
-
else {
|
|
33
|
-
reactImportSpecifiers.add(specifier.imported);
|
|
34
|
-
}
|
|
35
31
|
});
|
|
36
32
|
},
|
|
37
33
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"destructure-default-import.js","sourceRoot":"","sources":["../../../src/rules/react/destructure-default-import.ts"],"names":[],"mappings":";;;AAEa,QAAA,6BAA6B,GAAoB;IAC1D,IAAI,EAAE;QACF,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE;YACF,WAAW,EAAE,2CAA2C;YACxD,QAAQ,EAAE,gBAAgB;YAC1B,WAAW,EAAE,KAAK;YAClB,GAAG,EAAE,2CAA2C;SACnD;QACD,QAAQ,EAAE;YACN,aAAa,EAAE,sCAAsC;SACxD;KACJ;IACD,MAAM,CAAC,OAAO;QACV,OAAO;YACH,iBAAiB,CAAC,IAAI;gBAClB,
|
|
1
|
+
{"version":3,"file":"destructure-default-import.js","sourceRoot":"","sources":["../../../src/rules/react/destructure-default-import.ts"],"names":[],"mappings":";;;AAEa,QAAA,6BAA6B,GAAoB;IAC1D,IAAI,EAAE;QACF,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE;YACF,WAAW,EAAE,2CAA2C;YACxD,QAAQ,EAAE,gBAAgB;YAC1B,WAAW,EAAE,KAAK;YAClB,GAAG,EAAE,2CAA2C;SACnD;QACD,QAAQ,EAAE;YACN,aAAa,EAAE,sCAAsC;SACxD;KACJ;IACD,MAAM,CAAC,OAAO;QACV,OAAO;YACH,iBAAiB,CAAC,IAAI;gBAClB,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,KAAK,OAAO,EAAE;oBAC/B,OAAO;iBACV;gBAED,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE;oBAChC,IACI,SAAS,CAAC,IAAI,KAAK,wBAAwB;wBAC3C,SAAS,CAAC,IAAI,KAAK,0BAA0B,EAC/C;wBACE,OAAO,CAAC,MAAM,CAAC;4BACX,IAAI;4BACJ,SAAS,EAAE,eAAe;yBAC7B,CAAC,CAAC;qBACN;gBACL,CAAC,CAAC,CAAC;YACP,CAAC;SACJ,CAAC;IACN,CAAC;CACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@servicetitan/eslint-plugin",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "23.0.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -13,12 +13,19 @@
|
|
|
13
13
|
"dist",
|
|
14
14
|
"src"
|
|
15
15
|
],
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@typescript-eslint/parser": "~6.21.0",
|
|
18
|
+
"@typescript-eslint/type-utils": "^6.21.0",
|
|
19
|
+
"@typescript-eslint/utils": "~6.21.0"
|
|
20
|
+
},
|
|
16
21
|
"devDependencies": {
|
|
17
|
-
"@types/eslint": "~8.
|
|
18
|
-
"eslint": "~
|
|
22
|
+
"@types/eslint": "~8.56.2",
|
|
23
|
+
"@typescript-eslint/rule-tester": "~6.21.0",
|
|
24
|
+
"eslint": "~8.56.0"
|
|
19
25
|
},
|
|
20
26
|
"peerDependencies": {
|
|
21
|
-
"eslint": "~8.
|
|
27
|
+
"eslint": "~8.56.0",
|
|
28
|
+
"typescript": ">=4"
|
|
22
29
|
},
|
|
23
30
|
"publishConfig": {
|
|
24
31
|
"access": "public"
|
|
@@ -26,5 +33,5 @@
|
|
|
26
33
|
"cli": {
|
|
27
34
|
"webpack": false
|
|
28
35
|
},
|
|
29
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "45876ed36415ad78d3ec4ef3783d6df1430504d3"
|
|
30
37
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { AnyRuleModule } from '@typescript-eslint/utils/ts-eslint';
|
|
2
|
+
import { Rule } from 'eslint';
|
|
3
|
+
import { noAsyncInForEach } from './rules/no-async-in-foreach';
|
|
1
4
|
import { useDeclareWithDecorators } from './rules/decorators-declare';
|
|
2
5
|
import { mobxUseMakeObservableWithDecorators } from './rules/mobx/use-makeObservable-with-decorators';
|
|
3
6
|
import { mobxNoAbstractDecorators } from './rules/mobx/no-abstract-decorators';
|
|
@@ -5,12 +8,13 @@ import { reactDestructureDefaultImport } from './rules/react/destructure-default
|
|
|
5
8
|
import { reactNoQualifiedType } from './rules/react/no-qualified-type';
|
|
6
9
|
import { stub } from './processors/stub';
|
|
7
10
|
|
|
8
|
-
export const rules = {
|
|
9
|
-
'use-declare-with-decorators': useDeclareWithDecorators,
|
|
11
|
+
export const rules: Record<string, Rule.RuleModule | AnyRuleModule> = {
|
|
10
12
|
'mobx/use-makeObservable-with-decorators': mobxUseMakeObservableWithDecorators,
|
|
11
13
|
'mobx/no-abstract-decorators': mobxNoAbstractDecorators,
|
|
12
14
|
'react/destructure-default-import': reactDestructureDefaultImport,
|
|
13
15
|
'react/no-qualified-type': reactNoQualifiedType,
|
|
16
|
+
'no-async-in-foreach': noAsyncInForEach,
|
|
17
|
+
'use-declare-with-decorators': useDeclareWithDecorators,
|
|
14
18
|
};
|
|
15
19
|
|
|
16
20
|
export const processors = { stub };
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
3
|
+
import { noAsyncInForEach } from '../no-async-in-foreach';
|
|
4
|
+
|
|
5
|
+
const ruleTester = new RuleTester({
|
|
6
|
+
parser: '@typescript-eslint/parser',
|
|
7
|
+
parserOptions: {
|
|
8
|
+
project: './tsconfig.json',
|
|
9
|
+
tsconfigRootDir: path.resolve(__dirname, '../__mocks__/fixture'),
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
ruleTester.run('no-async-in-foreach', noAsyncInForEach, {
|
|
14
|
+
valid: [
|
|
15
|
+
// Synchronous function
|
|
16
|
+
{
|
|
17
|
+
name: 'array literal with synchronous function',
|
|
18
|
+
code: `
|
|
19
|
+
[].forEach(() => {})
|
|
20
|
+
`,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'array variable with synchronous function',
|
|
24
|
+
code: `
|
|
25
|
+
const arr = [];
|
|
26
|
+
arr.forEach(() => {});
|
|
27
|
+
`,
|
|
28
|
+
},
|
|
29
|
+
// Synchronous variable
|
|
30
|
+
{
|
|
31
|
+
name: 'array literal with synchronous variable',
|
|
32
|
+
code: `
|
|
33
|
+
function fn() {}
|
|
34
|
+
[].forEach(fn);
|
|
35
|
+
`,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'array variable with synchronous variable',
|
|
39
|
+
code: `
|
|
40
|
+
const arr = [];
|
|
41
|
+
function fn() {}
|
|
42
|
+
arr.forEach(fn);
|
|
43
|
+
`,
|
|
44
|
+
},
|
|
45
|
+
// Any type
|
|
46
|
+
{
|
|
47
|
+
name: 'array literal with variable that returns any',
|
|
48
|
+
code: `
|
|
49
|
+
function fn(): any { return ''; }
|
|
50
|
+
[].forEach(fn);
|
|
51
|
+
`,
|
|
52
|
+
},
|
|
53
|
+
// Objects
|
|
54
|
+
{
|
|
55
|
+
name: 'object with synchronous function',
|
|
56
|
+
code: `
|
|
57
|
+
const obj = { forEach: (callback: Function) => callback() };
|
|
58
|
+
obj.forEach(() => {});
|
|
59
|
+
`,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'object with synchronous variable',
|
|
63
|
+
code: `
|
|
64
|
+
const obj = { forEach: (callback: Function) => callback() };
|
|
65
|
+
function fn() {}
|
|
66
|
+
obj.forEach(fn);
|
|
67
|
+
`,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'object with async function',
|
|
71
|
+
code: `
|
|
72
|
+
const obj = { forEach: (callback: Function) => callback() };
|
|
73
|
+
obj.forEach(async () => Promise.resolve());
|
|
74
|
+
`,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'object with async variable',
|
|
78
|
+
code: `
|
|
79
|
+
const obj = { forEach: (callback: Function) => callback() };
|
|
80
|
+
async function fn() {}
|
|
81
|
+
obj.forEach(fn);
|
|
82
|
+
`,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
invalid: [
|
|
86
|
+
// Async function
|
|
87
|
+
{
|
|
88
|
+
name: 'array literal with async function',
|
|
89
|
+
code: `
|
|
90
|
+
[].forEach(async () => Promise.resolve());
|
|
91
|
+
`,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'array literal with async variable',
|
|
95
|
+
code: `
|
|
96
|
+
async function fn() {}
|
|
97
|
+
[].forEach(fn);
|
|
98
|
+
`,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'array variable with async function',
|
|
102
|
+
code: `
|
|
103
|
+
const arr = [];
|
|
104
|
+
arr.forEach(async () => Promise.resolve());
|
|
105
|
+
`,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'array variable with async variable',
|
|
109
|
+
code: `
|
|
110
|
+
const arr = [];
|
|
111
|
+
async function fn() {}
|
|
112
|
+
arr.forEach(fn);
|
|
113
|
+
`,
|
|
114
|
+
},
|
|
115
|
+
// Function that returns Promise
|
|
116
|
+
{
|
|
117
|
+
name: 'array literal with function that returns Promise',
|
|
118
|
+
code: `
|
|
119
|
+
[].forEach(() => Promise.resolve())
|
|
120
|
+
`,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: 'array literal with type that returns Promise',
|
|
124
|
+
code: `
|
|
125
|
+
type AsyncFunction = () => Promise<any>;
|
|
126
|
+
let fn: AsyncFunction;
|
|
127
|
+
[].forEach(fn)
|
|
128
|
+
`,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: 'array variable with function that returns Promise',
|
|
132
|
+
code: `
|
|
133
|
+
let arr: Array<any>;
|
|
134
|
+
arr.forEach(() => Promise.resolve());
|
|
135
|
+
`,
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: 'array variable with type that returns Promise',
|
|
139
|
+
code: `
|
|
140
|
+
let arr: Array<any>;
|
|
141
|
+
type AsyncFunction = () => Promise<any>;
|
|
142
|
+
let fn: AsyncFunction;
|
|
143
|
+
arr.forEach(fn)
|
|
144
|
+
`,
|
|
145
|
+
},
|
|
146
|
+
// Tuple
|
|
147
|
+
{
|
|
148
|
+
name: 'tuple with async function',
|
|
149
|
+
code: `
|
|
150
|
+
const tuple: [number, string] = [1, 'a'];
|
|
151
|
+
tuple.forEach(async () => Promise.resolve());
|
|
152
|
+
`,
|
|
153
|
+
},
|
|
154
|
+
// Conditional callee
|
|
155
|
+
{
|
|
156
|
+
name: 'conditional callee with async function',
|
|
157
|
+
code: `
|
|
158
|
+
let arr: Array<any> | undefined;
|
|
159
|
+
arr?.forEach(async () => Promise.resolve());
|
|
160
|
+
`,
|
|
161
|
+
},
|
|
162
|
+
].map(testCase => ({ ...testCase, errors: [{ messageId: 'noAsyncInForEach' }] })),
|
|
163
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { ESLintUtils, TSESTree } from '@typescript-eslint/utils';
|
|
2
|
+
import { getParserServices } from '@typescript-eslint/utils/eslint-utils';
|
|
3
|
+
import { containsAllTypesByName } from '@typescript-eslint/type-utils';
|
|
4
|
+
import ts, { TypeChecker } from 'typescript';
|
|
5
|
+
|
|
6
|
+
const createRule = ESLintUtils.RuleCreator(
|
|
7
|
+
_name => 'https://gist.github.com/joeytwiddle/37d2085425c049629b80956d3c618971'
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
export const noAsyncInForEach = createRule({
|
|
11
|
+
create(context) {
|
|
12
|
+
const services = getParserServices(context);
|
|
13
|
+
const checker = services.program.getTypeChecker() as TypeChecker & {
|
|
14
|
+
// Type declarations aren't in 4.x. See https://github.com/microsoft/TypeScript/pull/52467
|
|
15
|
+
isArrayType: (type: ts.Type) => boolean;
|
|
16
|
+
isTupleType: (type: ts.Type) => boolean;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function checkForEachExpression(node: TSESTree.MemberExpression) {
|
|
20
|
+
const callExpression = node.parent as TSESTree.CallExpression;
|
|
21
|
+
if (!isAsync(callExpression.arguments[0])) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const callee = callExpression.callee as TSESTree.MemberExpression;
|
|
26
|
+
if (!isArrayLike(callee.object)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
context.report({ messageId: 'noAsyncInForEach', node });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getSymbolAtLocation(node: TSESTree.Node) {
|
|
34
|
+
const symbol = services.getSymbolAtLocation(node);
|
|
35
|
+
if (symbol && symbol.flags & ts.SymbolFlags.Alias) {
|
|
36
|
+
return checker.getAliasedSymbol(symbol);
|
|
37
|
+
}
|
|
38
|
+
return symbol;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isArrayLike(node: TSESTree.Node) {
|
|
42
|
+
const nodeType = services.getTypeAtLocation(node);
|
|
43
|
+
const types = isUnionType(nodeType) ? nodeType.types : [nodeType];
|
|
44
|
+
return types.some(type => checker.isArrayType(type) || checker.isTupleType(type));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isAsync(node: TSESTree.Node) {
|
|
48
|
+
return (
|
|
49
|
+
(node.type === TSESTree.AST_NODE_TYPES.ArrowFunctionExpression && node.async) ||
|
|
50
|
+
hasAsyncModifier(getSymbolAtLocation(node)) ||
|
|
51
|
+
returnsPromise(node)
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isUnionType(type: ts.Type): type is ts.UnionType {
|
|
56
|
+
return (type.flags & ts.TypeFlags.Union) !== 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function hasAsyncModifier(symbol?: ts.Symbol) {
|
|
60
|
+
return !!symbol?.declarations?.find(declaration =>
|
|
61
|
+
declaration.modifiers?.find(
|
|
62
|
+
modifier => modifier.kind === ts.SyntaxKind.AsyncKeyword
|
|
63
|
+
)
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function returnsPromise(node: TSESTree.Node) {
|
|
68
|
+
const signatures = services.getTypeAtLocation(node).getCallSignatures();
|
|
69
|
+
if (signatures.length) {
|
|
70
|
+
const returnType = checker.getReturnTypeOfSignature(signatures[0]);
|
|
71
|
+
return containsAllTypesByName(returnType, true, new Set(['Promise']));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
"CallExpression[arguments.length=1] > MemberExpression[property.name='forEach']":
|
|
77
|
+
checkForEachExpression,
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
name: 'no-async-in-foreach',
|
|
81
|
+
meta: {
|
|
82
|
+
docs: {
|
|
83
|
+
description: 'Disallow passing asynchronous callback to Array.forEach',
|
|
84
|
+
},
|
|
85
|
+
messages: {
|
|
86
|
+
noAsyncInForEach: 'Async callback passed to Array.forEach',
|
|
87
|
+
},
|
|
88
|
+
type: 'problem',
|
|
89
|
+
schema: [],
|
|
90
|
+
},
|
|
91
|
+
defaultOptions: [],
|
|
92
|
+
});
|
|
@@ -16,8 +16,6 @@ export const reactDestructureDefaultImport: Rule.RuleModule = {
|
|
|
16
16
|
create(context) {
|
|
17
17
|
return {
|
|
18
18
|
ImportDeclaration(node) {
|
|
19
|
-
const reactImportSpecifiers = new Set();
|
|
20
|
-
|
|
21
19
|
if (node.source.value !== 'react') {
|
|
22
20
|
return;
|
|
23
21
|
}
|
|
@@ -31,8 +29,6 @@ export const reactDestructureDefaultImport: Rule.RuleModule = {
|
|
|
31
29
|
node,
|
|
32
30
|
messageId: 'importDefault',
|
|
33
31
|
});
|
|
34
|
-
} else {
|
|
35
|
-
reactImportSpecifiers.add(specifier.imported);
|
|
36
32
|
}
|
|
37
33
|
});
|
|
38
34
|
},
|