@sap/eslint-plugin-cds 4.2.3 → 4.2.4

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/CHANGELOG.md CHANGED
@@ -6,6 +6,10 @@ This project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  The format is based on [Keep a Changelog](https://keepachangelog.com/).
8
8
 
9
+ ## [4.2.4] - 2026-06-22
10
+ ### Fixed
11
+ - `auth-valid-restrict-grant` no longer crashes on JOINs with three or more tables.
12
+
9
13
  ## [4.2.3] - 2026-05-12
10
14
  ### Fixed
11
15
  - Don't fail if the `tree-sitter` dependency cannot be installed. This might happen in WebContainer environments where native modules cannot be loaded.
package/lib/conf/all.js CHANGED
@@ -12,6 +12,7 @@ module.exports = {
12
12
  '@sap/cds/latest-cds-version': 'error',
13
13
  '@sap/cds/no-db-keywords': 'error',
14
14
  '@sap/cds/no-dollar-prefixed-names': 'error',
15
+ '@sap/cds/no-escaped-anno-brackets': 'warn',
15
16
  '@sap/cds/no-join-on-draft': 'error',
16
17
  '@sap/cds/sql-cast-suggestion': 'error',
17
18
  '@sap/cds/start-elements-lowercase': 'error',
@@ -10,6 +10,7 @@ module.exports = {
10
10
  '@sap/cds/auth-valid-restrict-to': 'warn',
11
11
  '@sap/cds/auth-valid-restrict-where': 'warn',
12
12
  '@sap/cds/no-dollar-prefixed-names': 'warn',
13
+ '@sap/cds/no-escaped-anno-brackets': 'warn',
13
14
  '@sap/cds/no-join-on-draft': 'warn',
14
15
  '@sap/cds/sql-cast-suggestion': 'warn',
15
16
  '@sap/cds/valid-csv-header': 'warn',
@@ -21,12 +21,19 @@ function extractActions (e, csn) {
21
21
  const entity = queue.pop()
22
22
  actions.push(...getActions(entity))
23
23
  if (isJoin(entity)) {
24
- for (const { ref } of entity.query.SELECT.from.args[0].args) {
25
- const ancestor = csn.definitions[ref[0]]
26
- if (ancestor) {
27
- queue.push(ancestor)
24
+ const collectRefs = args => {
25
+ for (const arg of args) {
26
+ if (arg.args) {
27
+ collectRefs(arg.args)
28
+ } else if (arg.ref) {
29
+ const ancestor = csn.definitions[arg.ref[0]]
30
+ if (ancestor) {
31
+ queue.push(ancestor)
32
+ }
33
+ }
28
34
  }
29
35
  }
36
+ collectRefs(entity.query.SELECT.from.args)
30
37
  } else if (isProjection(entity)) {
31
38
  const ancestor = csn.definitions[projectionTarget(entity)]
32
39
  if (ancestor) {
@@ -0,0 +1,85 @@
1
+ 'use strict'
2
+
3
+ const { RULE_CATEGORIES } = require('../constants')
4
+
5
+ module.exports = {
6
+ meta: {
7
+ schema: [{/* to avoid deprecation warning for ESLint 9 */}],
8
+ docs: {
9
+ category: RULE_CATEGORIES.model,
10
+ description: 'Escaped annotation syntax ![@...] is unnecessary in modern CAP CDS.',
11
+ recommended: true,
12
+ url: 'https://cap.cloud.sap/docs/tools/cds-lint/rules/no-escaped-anno-brackets',
13
+ },
14
+ type: 'problem',
15
+ fixable: 'code',
16
+ messages: {
17
+ noEscapedAnno: "Escaped annotation '![@{{name}}]' is unnecessary. Use '@{{name}}' instead.",
18
+ },
19
+ model: 'parsed',
20
+ },
21
+
22
+ create(context) {
23
+ return function checkNoEscapedAnnotations() {
24
+ const sourceCode = context.sourceCode
25
+ const code = sourceCode.getText()
26
+
27
+ const regex = /!\[@([^\]]+)\]/g
28
+ let match
29
+ while ((match = regex.exec(code)) !== null) {
30
+ const index = match.index
31
+ if (_isInCommentOrString(code, index)) continue
32
+
33
+ const name = match[1]
34
+ const loc = sourceCode.getLocFromIndex(index)
35
+ const endLoc = sourceCode.getLocFromIndex(index + match[0].length)
36
+
37
+ context.report({
38
+ messageId: 'noEscapedAnno',
39
+ data: { name },
40
+ loc: { start: loc, end: endLoc },
41
+ fix: fixer => fixer.replaceTextRange(
42
+ [index, index + match[0].length],
43
+ '@' + name
44
+ ),
45
+ })
46
+ }
47
+ }
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Checks whether the given index in code falls inside a line comment,
53
+ * block comment, or string literal.
54
+ * @param {string} code
55
+ * @param {number} index
56
+ * @returns {boolean}
57
+ */
58
+ function _isInCommentOrString(code, index) {
59
+ let i = 0
60
+ let inLineComment = false
61
+ let inBlockComment = false
62
+ let inString = false
63
+ let stringChar = ''
64
+
65
+ while (i < index) {
66
+ const ch = code[i]
67
+ if (inLineComment) {
68
+ if (ch === '\n') inLineComment = false
69
+ } else if (inBlockComment) {
70
+ if (ch === '*' && code[i + 1] === '/') {
71
+ inBlockComment = false
72
+ i++
73
+ }
74
+ } else if (inString) {
75
+ if (ch === '\\') { i++ } // skip escaped char
76
+ else if (ch === stringChar) inString = false
77
+ } else {
78
+ if (ch === '/' && code[i + 1] === '/') { inLineComment = true; i++ }
79
+ else if (ch === '/' && code[i + 1] === '*') { inBlockComment = true; i++ }
80
+ else if (ch === "'" || ch === '"') { inString = true; stringChar = ch }
81
+ }
82
+ i++
83
+ }
84
+ return inLineComment || inBlockComment || inString
85
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/eslint-plugin-cds",
3
- "version": "4.2.3",
3
+ "version": "4.2.4",
4
4
  "description": "ESLint plugin including recommended SAP Cloud Application Programming model and environment rules",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [
@@ -20,8 +20,8 @@
20
20
  "README.md"
21
21
  ],
22
22
  "dependencies": {
23
- "@eslint/plugin-kit": "^0.7.0",
24
- "semver": "^7.7.1"
23
+ "@eslint/plugin-kit": "^0.7.2",
24
+ "semver": "^7.8.2"
25
25
  },
26
26
  "optionalDependencies": {
27
27
  "tree-sitter": "^0.21.1",
@@ -32,6 +32,6 @@
32
32
  "eslint": "^9 || ^10"
33
33
  },
34
34
  "engines": {
35
- "node": ">=20"
35
+ "node": ">=22"
36
36
  }
37
37
  }