eslint-config-seek 0.0.0-typescript-config-20221028024455 → 0.0.0-unsafe-to-chain-autofix-20230606043410
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 +9 -1
- package/{.eslintrc.js → base.js} +44 -29
- package/extensions.js +2 -0
- package/index.js +37 -0
- package/package.json +25 -21
- package/rules/unsafe-to-chain-command.js +107 -0
package/README.md
CHANGED
|
@@ -19,12 +19,20 @@ First, install this package, ESLint and the necessary plugins listed in this pro
|
|
|
19
19
|
|
|
20
20
|
Then create a file named `.eslintrc` with following contents in the root folder of your project:
|
|
21
21
|
|
|
22
|
-
```
|
|
22
|
+
```json
|
|
23
23
|
{
|
|
24
24
|
"extends": "seek"
|
|
25
25
|
}
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
The default configuration includes support for React projects. For projects that are not based on React, the "base" configuration should be used instead:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"extends": "seek/base"
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
28
36
|
You can override the settings from `eslint-config-seek` by editing the `.eslintrc` file. Learn more about [configuring ESLint](http://eslint.org/docs/user-guide/configuring) on the ESLint website.
|
|
29
37
|
|
|
30
38
|
## License
|
package/{.eslintrc.js → base.js}
RENAMED
|
@@ -4,6 +4,9 @@ const root = require('find-root')(process.cwd());
|
|
|
4
4
|
const OFF = 0;
|
|
5
5
|
const ERROR = 2;
|
|
6
6
|
|
|
7
|
+
const rulesDirPlugin = require('eslint-plugin-rulesdir');
|
|
8
|
+
rulesDirPlugin.RULES_DIR = path.join(__dirname, 'rules');
|
|
9
|
+
|
|
7
10
|
const baseRules = {
|
|
8
11
|
// Possible Errors
|
|
9
12
|
'no-console': ERROR,
|
|
@@ -76,40 +79,22 @@ const baseRules = {
|
|
|
76
79
|
'no-return-await': OFF,
|
|
77
80
|
};
|
|
78
81
|
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
'react/self-closing-comp': ERROR,
|
|
82
|
-
'react/jsx-pascal-case': ERROR,
|
|
83
|
-
'react-hooks/rules-of-hooks': ERROR,
|
|
84
|
-
'react-hooks/exhaustive-deps': ERROR,
|
|
85
|
-
'react/no-children-prop': ERROR,
|
|
86
|
-
'react/display-name': OFF,
|
|
87
|
-
'react/prop-types': OFF,
|
|
88
|
-
};
|
|
82
|
+
const { js: jsExtensions, ts: tsExtensions } = require('./extensions');
|
|
83
|
+
const allExtensions = [...jsExtensions, ...tsExtensions];
|
|
89
84
|
|
|
90
85
|
/** @type {import('eslint').Linter.Config} */
|
|
91
86
|
const baseConfig = {
|
|
92
87
|
parser: '@babel/eslint-parser',
|
|
93
88
|
parserOptions: {
|
|
94
|
-
babelOptions: {
|
|
95
|
-
presets: ['@babel/preset-react'],
|
|
96
|
-
},
|
|
97
89
|
requireConfigFile: false,
|
|
98
90
|
sourceType: 'module',
|
|
99
91
|
},
|
|
100
92
|
root: true,
|
|
101
93
|
env: {
|
|
102
|
-
browser: true,
|
|
103
94
|
node: true,
|
|
104
95
|
},
|
|
105
|
-
|
|
106
|
-
react: {
|
|
107
|
-
version: 'detect',
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
plugins: ['react', 'react-hooks', 'import'],
|
|
96
|
+
plugins: ['import'],
|
|
111
97
|
extends: [
|
|
112
|
-
'plugin:react/recommended',
|
|
113
98
|
// this config enables eslint-plugin-import to resolve JavaScript and TypeScript files
|
|
114
99
|
// https://github.com/import-js/eslint-plugin-import/blob/v2.26.0/config/typescript.js
|
|
115
100
|
// Some rules provided by eslint-plugin-import e.g. `import/no-duplicates` don't work without it
|
|
@@ -118,16 +103,16 @@ const baseConfig = {
|
|
|
118
103
|
],
|
|
119
104
|
rules: {
|
|
120
105
|
...baseRules,
|
|
121
|
-
...reactRules,
|
|
122
106
|
},
|
|
123
107
|
overrides: [
|
|
124
108
|
{
|
|
125
109
|
// TypeScript config
|
|
126
|
-
files: [
|
|
110
|
+
files: [`**/*.{${tsExtensions}}`],
|
|
127
111
|
parser: '@typescript-eslint/parser',
|
|
128
112
|
parserOptions: {
|
|
129
113
|
ecmaVersion: 2018,
|
|
130
114
|
sourceType: 'module',
|
|
115
|
+
project: true,
|
|
131
116
|
},
|
|
132
117
|
extends: [
|
|
133
118
|
'plugin:@typescript-eslint/eslint-recommended',
|
|
@@ -166,11 +151,31 @@ const baseConfig = {
|
|
|
166
151
|
// https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-shadow.md
|
|
167
152
|
'no-shadow': OFF,
|
|
168
153
|
'@typescript-eslint/no-shadow': ERROR,
|
|
154
|
+
|
|
155
|
+
// These two rules deal with autofixing type imports/exports
|
|
156
|
+
// https://typescript-eslint.io/rules/consistent-type-imports
|
|
157
|
+
'@typescript-eslint/consistent-type-imports': [
|
|
158
|
+
ERROR,
|
|
159
|
+
{ fixStyle: 'inline-type-imports' },
|
|
160
|
+
],
|
|
161
|
+
// https://typescript-eslint.io/rules/consistent-type-exports
|
|
162
|
+
'@typescript-eslint/consistent-type-exports': [
|
|
163
|
+
ERROR,
|
|
164
|
+
{ fixMixedExportsWithInlineTypeSpecifier: true },
|
|
165
|
+
],
|
|
166
|
+
// https://typescript-eslint.io/rules/no-import-type-side-effects
|
|
167
|
+
'@typescript-eslint/no-import-type-side-effects': ERROR,
|
|
168
|
+
|
|
169
|
+
// This rule deals with merging multiple imports from the same module.
|
|
170
|
+
// In this case, we want type imports to be inlined when merging with the other imports.
|
|
171
|
+
// However, there is a pending PR which improves the behaviour of this rule https://github.com/import-js/eslint-plugin-import/pull/2716
|
|
172
|
+
// https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-duplicates.md#inline-type-imports
|
|
173
|
+
'import/no-duplicates': [ERROR, { 'prefer-inline': true }],
|
|
169
174
|
},
|
|
170
175
|
},
|
|
171
176
|
{
|
|
172
177
|
// JavaScript config
|
|
173
|
-
files: [
|
|
178
|
+
files: [`**/*.{${jsExtensions}}`],
|
|
174
179
|
env: {
|
|
175
180
|
es6: true,
|
|
176
181
|
},
|
|
@@ -183,6 +188,7 @@ const baseConfig = {
|
|
|
183
188
|
},
|
|
184
189
|
},
|
|
185
190
|
rules: {
|
|
191
|
+
'no-undef': ERROR,
|
|
186
192
|
'no-use-before-define': [ERROR, { functions: false }],
|
|
187
193
|
'no-unused-expressions': ERROR,
|
|
188
194
|
'import/no-unresolved': [
|
|
@@ -194,7 +200,10 @@ const baseConfig = {
|
|
|
194
200
|
},
|
|
195
201
|
{
|
|
196
202
|
// Jest config
|
|
197
|
-
files: [
|
|
203
|
+
files: [
|
|
204
|
+
`**/__tests__/**/*.{${allExtensions}}`,
|
|
205
|
+
`**/*.@(spec|test).{${allExtensions}}`,
|
|
206
|
+
],
|
|
198
207
|
env: {
|
|
199
208
|
jest: true,
|
|
200
209
|
},
|
|
@@ -203,12 +212,18 @@ const baseConfig = {
|
|
|
203
212
|
},
|
|
204
213
|
{
|
|
205
214
|
// Cypress config
|
|
206
|
-
files: [
|
|
215
|
+
files: [`**/cypress/**/*.{${allExtensions}}`],
|
|
216
|
+
// eslint-plugin-cypress doesn't support ESLint v8.
|
|
217
|
+
// Use fork by `@finsit` until this is solved.
|
|
218
|
+
// https://github.com/cypress-io/eslint-plugin-cypress/issues/89
|
|
219
|
+
extends: ['plugin:@finsit/cypress/recommended'],
|
|
207
220
|
env: {
|
|
208
|
-
'cypress/globals': true,
|
|
221
|
+
'@finsit/cypress/globals': true,
|
|
222
|
+
},
|
|
223
|
+
plugins: ['@finsit/cypress', 'rulesdir'],
|
|
224
|
+
rules: {
|
|
225
|
+
'rulesdir/unsafe-to-chain-command': ERROR,
|
|
209
226
|
},
|
|
210
|
-
extends: ['plugin:cypress/recommended'],
|
|
211
|
-
plugins: ['cypress'],
|
|
212
227
|
},
|
|
213
228
|
],
|
|
214
229
|
};
|
package/extensions.js
ADDED
package/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const OFF = 0;
|
|
2
|
+
const ERROR = 2;
|
|
3
|
+
|
|
4
|
+
const reactRules = {
|
|
5
|
+
'react/prefer-es6-class': [ERROR, 'always'],
|
|
6
|
+
'react/self-closing-comp': ERROR,
|
|
7
|
+
'react/jsx-pascal-case': ERROR,
|
|
8
|
+
'react-hooks/rules-of-hooks': ERROR,
|
|
9
|
+
'react-hooks/exhaustive-deps': ERROR,
|
|
10
|
+
'react/no-children-prop': ERROR,
|
|
11
|
+
'react/display-name': OFF,
|
|
12
|
+
'react/prop-types': OFF,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/** @type {import('eslint').Linter.Config} */
|
|
16
|
+
const eslintConfig = {
|
|
17
|
+
env: {
|
|
18
|
+
browser: true,
|
|
19
|
+
},
|
|
20
|
+
settings: {
|
|
21
|
+
react: {
|
|
22
|
+
version: 'detect',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
plugins: ['react', 'react-hooks'],
|
|
26
|
+
extends: ['plugin:react/recommended', './base.js'],
|
|
27
|
+
parserOptions: {
|
|
28
|
+
babelOptions: {
|
|
29
|
+
presets: [require.resolve('@babel/preset-react')],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
rules: {
|
|
33
|
+
...reactRules,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
module.exports = eslintConfig;
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-config-seek",
|
|
3
|
-
"version": "0.0.0-
|
|
3
|
+
"version": "0.0.0-unsafe-to-chain-autofix-20230606043410",
|
|
4
4
|
"description": "ESLint configuration used by SEEK",
|
|
5
|
-
"main": ".
|
|
5
|
+
"main": "index.js",
|
|
6
6
|
"files": [
|
|
7
|
-
".
|
|
7
|
+
"index.js",
|
|
8
|
+
"base.js",
|
|
9
|
+
"extensions.js",
|
|
10
|
+
"rules/*"
|
|
8
11
|
],
|
|
9
12
|
"repository": {
|
|
10
13
|
"type": "git",
|
|
@@ -17,38 +20,39 @@
|
|
|
17
20
|
},
|
|
18
21
|
"homepage": "https://github.com/seek-oss/eslint-config-seek#readme",
|
|
19
22
|
"dependencies": {
|
|
20
|
-
"@babel/core": "^7.
|
|
23
|
+
"@babel/core": "^7.21.0",
|
|
21
24
|
"@babel/eslint-parser": "^7.19.1",
|
|
22
25
|
"@babel/preset-react": "^7.18.6",
|
|
23
|
-
"@
|
|
24
|
-
"@typescript-eslint/
|
|
25
|
-
"eslint
|
|
26
|
-
"eslint-
|
|
27
|
-
"eslint-
|
|
28
|
-
"eslint-plugin-import": "^2.
|
|
29
|
-
"eslint-plugin-jest": "^27.1
|
|
30
|
-
"eslint-plugin-react": "^7.
|
|
26
|
+
"@finsit/eslint-plugin-cypress": "^3.1.1",
|
|
27
|
+
"@typescript-eslint/eslint-plugin": "^5.53.0",
|
|
28
|
+
"@typescript-eslint/parser": "^5.53.0",
|
|
29
|
+
"eslint-config-prettier": "^8.6.0",
|
|
30
|
+
"eslint-import-resolver-typescript": "3.5.3",
|
|
31
|
+
"eslint-plugin-import": "^2.27.5",
|
|
32
|
+
"eslint-plugin-jest": "^27.2.1",
|
|
33
|
+
"eslint-plugin-react": "^7.32.2",
|
|
31
34
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
35
|
+
"eslint-plugin-rulesdir": "^0.2.2",
|
|
32
36
|
"find-root": "^1.1.0"
|
|
33
37
|
},
|
|
34
38
|
"devDependencies": {
|
|
35
|
-
"@changesets/cli": "2.
|
|
36
|
-
"@changesets/get-github-info": "0.5.
|
|
37
|
-
"eslint": "8.
|
|
38
|
-
"prettier": "2.
|
|
39
|
-
"typescript": "
|
|
39
|
+
"@changesets/cli": "^2.26.0",
|
|
40
|
+
"@changesets/get-github-info": "^0.5.2",
|
|
41
|
+
"eslint": "^8.34.0",
|
|
42
|
+
"prettier": "^2.8.4",
|
|
43
|
+
"typescript": "~4.9.5"
|
|
40
44
|
},
|
|
41
45
|
"peerDependencies": {
|
|
42
46
|
"eslint": ">=6",
|
|
43
|
-
"typescript": ">=
|
|
47
|
+
"typescript": ">=4.5"
|
|
44
48
|
},
|
|
45
|
-
"packageManager": "pnpm@
|
|
49
|
+
"packageManager": "pnpm@8.5.1",
|
|
46
50
|
"volta": {
|
|
47
|
-
"node": "16.
|
|
51
|
+
"node": "16.19.1"
|
|
48
52
|
},
|
|
49
53
|
"scripts": {
|
|
50
54
|
"release": "changeset publish",
|
|
51
|
-
"test": "eslint .",
|
|
55
|
+
"test": "eslint --config index.js . && eslint --config base.js .",
|
|
52
56
|
"changeset-version": "changeset version && prettier --write ."
|
|
53
57
|
}
|
|
54
58
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/** This rule was copied from the original `eslint-plugin-cypress` so we can use the fork (which
|
|
2
|
+
* supports eslint 8) while having the same recommended rules as the upstream
|
|
3
|
+
* https://github.com/foretagsplatsen/eslint-plugin-cypress
|
|
4
|
+
* https://github.com/cypress-io/eslint-plugin-cypress/blob/c626ad543f65babf1def5caabd1bc9bb9900d2c7/lib/rules/unsafe-to-chain-command.js
|
|
5
|
+
*/
|
|
6
|
+
// eslint-disable-next-line strict
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/** @type {import("eslint").Rule.RuleModule} */
|
|
10
|
+
module.exports = {
|
|
11
|
+
meta: {
|
|
12
|
+
type: 'problem',
|
|
13
|
+
docs: {
|
|
14
|
+
description: 'Actions should be at the end of chains, not in the middle',
|
|
15
|
+
category: 'Possible Errors',
|
|
16
|
+
recommended: true,
|
|
17
|
+
url: 'https://docs.cypress.io/guides/core-concepts/retry-ability#Actions-should-be-at-the-end-of-chains-not-the-middle',
|
|
18
|
+
},
|
|
19
|
+
schema: [],
|
|
20
|
+
fixable: 'code',
|
|
21
|
+
messages: {
|
|
22
|
+
unexpected:
|
|
23
|
+
'It is unsafe to chain further commands that rely on the subject after this command. It is best to split the chain, chaining again from `cy.` in the next command.',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
create(context) {
|
|
27
|
+
return {
|
|
28
|
+
CallExpression(node) {
|
|
29
|
+
if (
|
|
30
|
+
isRootCypress(node) &&
|
|
31
|
+
isActionUnsafeToChain(node) &&
|
|
32
|
+
node.parent.type === 'MemberExpression'
|
|
33
|
+
) {
|
|
34
|
+
context.report({
|
|
35
|
+
node,
|
|
36
|
+
messageId: 'unexpected',
|
|
37
|
+
fix: (fixer) => {
|
|
38
|
+
const { range: originalRange } = node.parent.property;
|
|
39
|
+
|
|
40
|
+
// Include the `.` before the identifier in the range
|
|
41
|
+
const adjustedRange = [originalRange[0] - 1, originalRange[1]];
|
|
42
|
+
|
|
43
|
+
return [
|
|
44
|
+
fixer.insertTextAfter(node, ';'),
|
|
45
|
+
fixer.insertTextBeforeRange(adjustedRange, 'cy'),
|
|
46
|
+
];
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/** @param {import("eslint").Rule.Node} node */
|
|
56
|
+
function isRootCypress(node) {
|
|
57
|
+
while (node.type === 'CallExpression') {
|
|
58
|
+
if (node.callee.type !== 'MemberExpression') {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (
|
|
63
|
+
node.callee.object.type === 'Identifier' &&
|
|
64
|
+
node.callee.object.name === 'cy'
|
|
65
|
+
) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// eslint-disable-next-line no-param-reassign
|
|
70
|
+
node = node.callee.object;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** @param {import("eslint").Rule.Node} node */
|
|
77
|
+
function isActionUnsafeToChain(node) {
|
|
78
|
+
// commands listed in the documentation with text: 'It is unsafe to chain further commands that rely on the subject after xxx'
|
|
79
|
+
const unsafeToChainActions = [
|
|
80
|
+
'blur',
|
|
81
|
+
'clear',
|
|
82
|
+
'click',
|
|
83
|
+
'check',
|
|
84
|
+
'dblclick',
|
|
85
|
+
'each',
|
|
86
|
+
'focus',
|
|
87
|
+
'rightclick',
|
|
88
|
+
'screenshot',
|
|
89
|
+
'scrollIntoView',
|
|
90
|
+
'scrollTo',
|
|
91
|
+
'select',
|
|
92
|
+
'selectFile',
|
|
93
|
+
'spread',
|
|
94
|
+
'submit',
|
|
95
|
+
'type',
|
|
96
|
+
'trigger',
|
|
97
|
+
'uncheck',
|
|
98
|
+
'within',
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
node.callee &&
|
|
103
|
+
node.callee.property &&
|
|
104
|
+
node.callee.property.type === 'Identifier' &&
|
|
105
|
+
unsafeToChainActions.includes(node.callee.property.name)
|
|
106
|
+
);
|
|
107
|
+
}
|