eslint-plugin-cypress 6.3.0 → 6.4.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/README.md CHANGED
@@ -139,6 +139,46 @@ export default defineConfig([
139
139
  ])
140
140
  ```
141
141
 
142
+ ### Typed Linting
143
+
144
+ You can activate typed linting for increased accuracy, if you want. This is purely optional and all rules continue to work without it. It uses a TypeScript parser to parse your entire project, instead of only the file being linted, which gives the plugin more information to work with. Be aware of the [performance penalty](https://typescript-eslint.io/getting-started/typed-linting/#performance) that this brings with it.
145
+
146
+ First, install the `@typescript-eslint/parser` npm package:
147
+
148
+ ```sh
149
+ npm install @typescript-eslint/parser --save-dev
150
+ ```
151
+
152
+ If you already have a TypeScript codebase, you can skip the next step. If not, you need a `tsconfig.json`. The following minimal `tsconfig.json` does not compile or check your code and includes all .js files.
153
+
154
+ ```json
155
+ {
156
+ "include": ["**/*.js"],
157
+ "compilerOptions": {
158
+ "allowJs": true,
159
+ "checkJs": false,
160
+ "noEmit": true
161
+ },
162
+ }
163
+ ```
164
+
165
+ The last step is to extend your eslint config to use the `typescript-eslint` parser.
166
+
167
+ ```js
168
+ import { defineConfig } from 'eslint/config'
169
+ import typescriptParser from '@typescript-eslint/parser'
170
+ export default defineConfig([
171
+ {
172
+ languageOptions: {
173
+ parser: typescriptParser,
174
+ parserOptions: {
175
+ projectService: true,
176
+ },
177
+ },
178
+ }
179
+ ])
180
+ ```
181
+
142
182
  ## Disable rules
143
183
 
144
184
  You can disable specific rules per file, for a portion of a file, or for a single line. See the [ESLint rules](https://eslint.org/docs/latest/use/configure/rules#disable-rules) documentation. For example ...
@@ -16,6 +16,18 @@
16
16
 
17
17
  <!-- end auto-generated rule options list -->
18
18
 
19
+ ## Typed Linting
20
+
21
+ If [Typed Linting](../../README.md#typed-linting) is enabled, this rule also catches unsafe chaining even when the Cypress chain was started from a helper function.
22
+
23
+ ```js
24
+ function getTodo() {
25
+ return cy.get('.todo')
26
+ }
27
+
28
+ getTodo().type('todo A{enter}').type('todo B{enter}')
29
+ ```
30
+
19
31
  ## Further Reading
20
32
 
21
33
  See [retry-ability guide](https://docs.cypress.io/app/core-concepts/retry-ability#Actions-should-be-at-the-end-of-chains-not-the-middle).
@@ -90,7 +90,7 @@ module.exports = {
90
90
  return {
91
91
  CallExpression(node) {
92
92
  if (
93
- isRootCypress(node)
93
+ isRootCypress(node, context)
94
94
  && isActionUnsafeToChain(node, methods)
95
95
  && node.parent.type === 'MemberExpression'
96
96
  ) {
@@ -3,26 +3,44 @@
3
3
  /**
4
4
  * Whether `node` is a CallExpression that belongs to a Cypress command chain
5
5
  * rooted at `cy` (walks `callee.object` through nested calls).
6
+ * If type information are available, additionally checks the return type of helper functions.
6
7
  *
7
8
  * @param {import('estree').Node} node
8
9
  * @returns {boolean}
9
10
  */
10
- function isRootCypress(node) {
11
- if (
12
- node.type !== 'CallExpression'
13
- || node.callee.type !== 'MemberExpression'
14
- ) {
11
+ const isRootCypress = (node, context) => {
12
+ if (node.type !== 'CallExpression') {
15
13
  return false
16
14
  }
17
15
 
18
- if (
19
- node.callee.object.type === 'Identifier'
20
- && node.callee.object.name === 'cy'
21
- ) {
22
- return true
16
+ const calleeType = node.callee.type
17
+ if (calleeType === 'MemberExpression') {
18
+ if (node.callee.object.type === 'Identifier' && node.callee.object.name === 'cy') {
19
+ return true
20
+ }
21
+
22
+ return isRootCypress(node.callee.object, context)
23
+ }
24
+
25
+ if (calleeType === 'Identifier') {
26
+ const services = context?.sourceCode?.parserServices
27
+ if (services?.program && services.esTreeNodeToTSNodeMap) {
28
+ const checker = services.program.getTypeChecker()
29
+ const tsNode = services.esTreeNodeToTSNodeMap.get(node)
30
+ if (tsNode) {
31
+ const signature = checker.getResolvedSignature(tsNode)
32
+ if (signature) {
33
+ const returnType = checker.getReturnTypeOfSignature(signature)
34
+ const symbol = returnType.getSymbol()
35
+ if (symbol) {
36
+ return checker.getFullyQualifiedName(symbol) === 'Cypress.Chainable'
37
+ }
38
+ }
39
+ }
40
+ }
23
41
  }
24
42
 
25
- return isRootCypress(node.callee.object)
43
+ return false
26
44
  }
27
45
 
28
46
  module.exports = { isRootCypress }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-cypress",
3
- "version": "6.3.0",
3
+ "version": "6.4.0",
4
4
  "description": "An ESLint plugin for projects using Cypress",
5
5
  "main": "./lib/index.js",
6
6
  "exports": {
@@ -28,22 +28,30 @@
28
28
  },
29
29
  "homepage": "https://github.com/cypress-io/eslint-plugin-cypress#readme",
30
30
  "peerDependencies": {
31
+ "@typescript-eslint/parser": ">=8",
31
32
  "eslint": ">=9"
32
33
  },
34
+ "peerDependenciesMeta": {
35
+ "@typescript-eslint/parser": {
36
+ "optional": true
37
+ }
38
+ },
33
39
  "dependencies": {
34
- "globals": "^17.4.0"
40
+ "globals": "^17.5.0"
35
41
  },
36
42
  "devDependencies": {
37
43
  "@eslint/js": "^10.0.1",
38
- "@stylistic/eslint-plugin": "^5.9.0",
39
- "eslint": "^10.0.2",
40
- "eslint-plugin-eslint-plugin": "^7.3.1",
44
+ "@stylistic/eslint-plugin": "^5.10.0",
45
+ "@typescript-eslint/rule-tester": "^8.59.0",
46
+ "cypress": "^15.14.0",
47
+ "eslint": "^10.2.1",
48
+ "eslint-plugin-eslint-plugin": "^7.3.2",
41
49
  "eslint-plugin-mocha": "^11.2.0",
42
50
  "husky": "^9.1.7",
43
- "prettier": "^3.8.1",
51
+ "prettier": "^3.8.3",
44
52
  "typescript": "^5.9.3",
45
- "typescript-eslint": "^8.56.1",
46
- "vitest": "^4.0.18"
53
+ "typescript-eslint": "^8.59.1",
54
+ "vitest": "^4.1.5"
47
55
  },
48
56
  "scripts": {
49
57
  "lint": "eslint",