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 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
- ```js
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
@@ -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 reactRules = {
80
- 'react/prefer-es6-class': [ERROR, 'always'],
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
- settings: {
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: ['**/*.ts', '**/*.tsx'],
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: ['**/*.js', '**/*.jsx'],
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: ['**/__tests__/**/*.{js,ts,tsx}', '**/*.@(spec|test).{js,ts,tsx}'],
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: ['cypress/**/*.{js,ts,tsx}'],
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
@@ -0,0 +1,2 @@
1
+ module.exports.js = ['js', 'cjs', 'mjs', 'jsx'];
2
+ module.exports.ts = ['ts', 'cts', 'mts', 'tsx'];
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-typescript-config-20221028024455",
3
+ "version": "0.0.0-unsafe-to-chain-autofix-20230606043410",
4
4
  "description": "ESLint configuration used by SEEK",
5
- "main": ".eslintrc.js",
5
+ "main": "index.js",
6
6
  "files": [
7
- ".eslintrc.js"
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.19.6",
23
+ "@babel/core": "^7.21.0",
21
24
  "@babel/eslint-parser": "^7.19.1",
22
25
  "@babel/preset-react": "^7.18.6",
23
- "@typescript-eslint/eslint-plugin": "^5.41.0",
24
- "@typescript-eslint/parser": "^5.41.0",
25
- "eslint-config-prettier": "^8.5.0",
26
- "eslint-import-resolver-typescript": "3.5.2",
27
- "eslint-plugin-cypress": "^2.12.1",
28
- "eslint-plugin-import": "^2.26.0",
29
- "eslint-plugin-jest": "^27.1.3",
30
- "eslint-plugin-react": "^7.31.10",
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.25.0",
36
- "@changesets/get-github-info": "0.5.1",
37
- "eslint": "8.26.0",
38
- "prettier": "2.7.1",
39
- "typescript": "^4.8.4"
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": ">=3.3"
47
+ "typescript": ">=4.5"
44
48
  },
45
- "packageManager": "pnpm@7.14.0",
49
+ "packageManager": "pnpm@8.5.1",
46
50
  "volta": {
47
- "node": "16.18.0"
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
+ }