@sap/eslint-plugin-cds 3.1.2 → 4.0.2

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.
Files changed (42) hide show
  1. package/CHANGELOG.md +30 -2
  2. package/LICENSE +15 -21
  3. package/lib/conf/all.js +2 -0
  4. package/lib/conf/experimental.js +1 -2
  5. package/lib/conf/index.js +17 -22
  6. package/lib/conf/js/all.js +8 -0
  7. package/lib/conf/js/recommended.js +8 -0
  8. package/lib/constants.js +7 -0
  9. package/lib/parser.js +53 -35
  10. package/lib/rules/assoc2many-ambiguous-key.js +1 -1
  11. package/lib/rules/auth-no-empty-restrictions.js +1 -1
  12. package/lib/rules/auth-restrict-grant-service.js +1 -1
  13. package/lib/rules/auth-use-requires.js +1 -1
  14. package/lib/rules/auth-valid-restrict-grant.js +1 -1
  15. package/lib/rules/auth-valid-restrict-keys.js +1 -1
  16. package/lib/rules/auth-valid-restrict-to.js +1 -1
  17. package/lib/rules/auth-valid-restrict-where.js +1 -1
  18. package/lib/rules/extension-restrictions.js +1 -1
  19. package/lib/rules/index.js +19 -23
  20. package/lib/rules/js/CdsHandlerRule.js +265 -0
  21. package/lib/rules/js/no-cross-service-import.js +69 -0
  22. package/lib/rules/js/no-deep-sap-cds-import.js +56 -0
  23. package/lib/rules/js/no-shared-handler-variable.js +73 -0
  24. package/lib/rules/js/types.d.ts +15 -0
  25. package/lib/rules/js/use-cql-select-template-strings.js +35 -0
  26. package/lib/rules/latest-cds-version.js +2 -0
  27. package/lib/rules/no-db-keywords.js +3 -2
  28. package/lib/rules/no-dollar-prefixed-names.js +67 -9
  29. package/lib/rules/no-java-keywords.js +3 -1
  30. package/lib/rules/no-join-on-draft.js +4 -1
  31. package/lib/rules/sql-cast-suggestion.js +46 -22
  32. package/lib/rules/sql-null-comparison.js +39 -35
  33. package/lib/rules/start-elements-lowercase.js +43 -39
  34. package/lib/rules/start-entities-uppercase.js +24 -34
  35. package/lib/rules/valid-csv-header.js +3 -2
  36. package/lib/utils/Cache.js +21 -18
  37. package/lib/utils/createRule.js +47 -44
  38. package/lib/utils/getConfigPath.js +1 -12
  39. package/lib/utils/{getProjectRootPath.js → projectRootPath.js} +18 -6
  40. package/lib/utils/rules.js +10 -9
  41. package/lib/utils/runRuleTester.js +16 -24
  42. package/package.json +5 -5
@@ -1,40 +1,64 @@
1
1
  'use strict'
2
2
 
3
+ const { RULE_CATEGORIES } = require('../constants')
4
+
3
5
  module.exports = {
4
6
  meta: {
5
7
  schema: [{/* to avoid deprecation warning for ESLint 9 */}],
6
8
  docs: {
9
+ category: RULE_CATEGORIES.model,
7
10
  description: 'Should make suggestions for possible missing SQL casts.',
8
11
  recommended: true,
9
- url: 'https://cap.cloud.sap/docs/tools/cds-lint/meta/sql-cast-suggestion',
12
+ url: 'https://cap.cloud.sap/docs/tools/cds-lint/rules/sql-cast-suggestion',
10
13
  },
11
14
  type: 'suggestion',
12
15
  messages: {
13
- missingSQLCast: 'Potential issue - Missing SQL cast for column expression?'
14
- }
16
+ missingSqlCast: 'Potential issue - Missing SQL cast for column expression?'
17
+ },
18
+ model: 'parsed',
15
19
  },
16
20
  create: function (context) {
17
- return { view: checkSqlCast }
21
+ const model = context.getModel()
22
+ if (!model?.definitions)
23
+ return
24
+
25
+ return function checkAllElementsStartWithLowercase() {
26
+ for (const defName in model.definitions) {
27
+ const def = model.definitions[defName]
28
+ checkSqlCastsInView(defName, def)
29
+ }
30
+ }
18
31
 
19
- function checkSqlCast (v) {
32
+ function checkSqlCastsInView(defName, def) {
20
33
  // TODO: restructure and make more robust (#507)
21
- if (v?.query?.SET?.args) {
22
- for (const arg of v.query.SET.args) {
23
- if (arg?.SELECT) {
24
- // Only in UNION cases?
25
- for (const each of arg.SELECT.columns || []) {
26
- if (each) {
27
- const { xpr, cast } = each
28
- if (cast && xpr) {
29
- if (!(xpr[0]?.xpr && xpr[0]?.cast)) {
30
- context.report({
31
- messageId: 'missingSQLCast',
32
- node: context.getNode(v)
33
- })
34
- }
35
- }
36
- }
37
- }
34
+ if (!def?.query?.SET?.args?.length)
35
+ return
36
+
37
+ for (const arg of def.query.SET.args) {
38
+ if (arg?.SELECT?.columns?.length) {
39
+ // Only in UNION cases?
40
+ for (const col of arg.SELECT.columns) {
41
+ if (col)
42
+ checkColumn(col)
43
+ }
44
+ }
45
+ }
46
+
47
+ function checkColumn(col) {
48
+ const { xpr, cast } = col
49
+ if (cast && xpr) {
50
+ if (!(xpr[0]?.xpr && xpr[0]?.cast)) {
51
+ // we don't pass a name for the column's location, as it would be used to calculate
52
+ // endColumn, which is not correct for this expression
53
+ const loc = col.$location ?
54
+ context.getLocation('', col, model) :
55
+ context.getLocation(defName, def, model)
56
+
57
+ context.report({
58
+ messageId: 'missingSqlCast',
59
+ loc,
60
+ file: def.$location.file,
61
+ })
38
62
  }
39
63
  }
40
64
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { forEachXprInDefinition } = require('../utils/csnTraversal')
4
4
 
5
- const invalidComparisonOperators = [ '=', '!=', '<>' ]
5
+ const invalidComparisonOperators = [ '=', '<>' ]
6
6
 
7
7
  module.exports = {
8
8
  meta: {
@@ -11,50 +11,54 @@ module.exports = {
11
11
  description: 'Ensure SQL comparisons with \'null\' are valid',
12
12
  category: 'Model Validation',
13
13
  recommended: false,
14
- // TODO: Add documentation
15
- // url: 'https://cap.cloud.sap/docs/tools/cds-lint/meta/sql-null-comparison',
14
+ url: 'https://cap.cloud.sap/docs/tools/cds-lint/rules/sql-null-comparison',
16
15
  },
17
16
  type: 'problem',
18
17
  model: 'parsed',
19
18
  messages: {
20
- nullComparison: `Comparisons against 'null' are always null. Did you mean 'is not null'?`,
19
+ nullComparison: `Comparisons against 'null' using '=' and '<>' are always null. Did you mean 'is null'/'is not null'?`,
21
20
  }
22
21
  },
23
22
  create(context) {
24
- return {
25
- view(v) {
26
- forEachXprInDefinition(v, checkExpression)
27
-
28
- function checkExpression(xpr, ctx) {
29
- if (!xpr || !Array.isArray(xpr))
30
- return
31
-
32
- for (let i = 0; i < xpr.length; i++) {
33
- if (typeof xpr[i] !== 'object')
34
- continue // scalar value, etc.
35
-
36
- if (xpr[i]?.val === null) {
37
- const prev = i > 0 && typeof xpr[i-1] === 'string' ? xpr[i-1] : null
38
- if (prev && invalidComparisonOperators.includes(prev)) {
39
- reportComparison(xpr, ctx)
40
- continue
41
- }
42
- const next = i+1 < xpr.length && typeof xpr[i+1] === 'string' ? xpr[i+1] : null
43
- if (next && invalidComparisonOperators.includes(next))
44
- reportComparison(xpr, ctx)
45
- }
46
- }
47
- }
23
+ const model = context.getModel()
24
+ if (!model?.definitions)
25
+ return
48
26
 
49
- function reportComparison(xpr, ctx) {
50
- context.report({
51
- messageId: 'nullComparison',
52
- loc: context.getLocation(null, ctx),
53
- file: ctx.$location?.file,
54
- })
55
- }
27
+ return function checkSqlNullComparisonsInModel() {
28
+ for (const defName in model.definitions) {
29
+ const def = model.definitions[defName]
30
+ if (def.query || def.projection)
31
+ forEachXprInDefinition(def, checkExpression)
32
+ }
33
+ }
34
+
35
+ function checkExpression(xpr, ctx) {
36
+ if (!xpr || !Array.isArray(xpr))
37
+ return
56
38
 
39
+ for (let i = 0; i < xpr.length; i++) {
40
+ if (typeof xpr[i] !== 'object')
41
+ continue // scalar value, etc.
42
+
43
+ if (xpr[i]?.val === null) {
44
+ const prev = i > 0 && typeof xpr[i-1] === 'string' ? xpr[i-1] : null
45
+ if (prev && invalidComparisonOperators.includes(prev)) {
46
+ reportComparison(xpr, ctx)
47
+ continue
48
+ }
49
+ const next = i+1 < xpr.length && typeof xpr[i+1] === 'string' ? xpr[i+1] : null
50
+ if (next && invalidComparisonOperators.includes(next))
51
+ reportComparison(xpr, ctx)
52
+ }
57
53
  }
58
54
  }
55
+
56
+ function reportComparison(xpr, ctx) {
57
+ context.report({
58
+ messageId: 'nullComparison',
59
+ loc: context.getLocation('', ctx),
60
+ file: ctx.$location?.file,
61
+ })
62
+ }
59
63
  }
60
64
  }
@@ -1,63 +1,67 @@
1
1
  'use strict'
2
2
 
3
+ const { RULE_CATEGORIES } = require('../constants')
4
+
5
+ const allowedUpperCaseElements = ['ID']
6
+
3
7
  module.exports = {
4
8
  meta: {
5
9
  schema: [{/* to avoid deprecation warning for ESLint 9 */}],
6
10
  docs: {
11
+ category: RULE_CATEGORIES.model,
7
12
  description: 'Regular element names should start with lowercase letters.',
8
- url: 'https://cap.cloud.sap/docs/tools/cds-lint/meta/start-elements-lowercase',
13
+ url: 'https://cap.cloud.sap/docs/tools/cds-lint/rules/start-elements-lowercase',
9
14
  },
10
15
  type: 'suggestion',
11
- hasSuggestions: true,
12
16
  messages: {
13
- startLowercase: "Element name '{{entityName}}.{{elementName}}' should start with a lowercase letter.",
14
- fixLowercase: 'Start element name with a lowercase letter.'
17
+ startLowercase: "Element name '{{defName}}:{{elementName}}' should start with a lowercase letter.",
15
18
  },
16
19
  fixable: 'code',
17
20
  model: 'parsed',
18
21
  },
19
22
  create: function (context) {
20
- const sourcecode = context.getSourceCode()
23
+ const model = context.getModel()
24
+ if (!model?.definitions)
25
+ return
21
26
 
22
- return {
23
- element: checkStartLowercase
27
+ return function checkAllElementsStartWithLowercase() {
28
+ for (const defName in model.definitions)
29
+ checkDefinition(defName, model.definitions[defName])
24
30
  }
25
31
 
26
- function checkStartLowercase (e) {
27
- const elementName = e.name
28
- const entityName = e.parent.name
29
- if (elementName && !(entityName.startsWith('localized') || entityName.endsWith('texts'))) {
30
- if (elementName.charAt(0) !== elementName.charAt(0).toLowerCase() && !['ID'].includes(elementName)) {
31
- if (e.$location && e.$location.file) {
32
- const file = e.$location.file
33
- const loc = context.getLocation(elementName, e)
34
- const fix = (fixer, source = sourcecode) => {
35
- const elementNameSanitized = elementName.charAt(0).toLowerCase() + elementName.slice(1)
36
- const rangeEnd = source.getIndexFromLoc({
37
- line: loc.end.line,
38
- column: loc.end.column
39
- })
40
- const rangeBeg = rangeEnd ? rangeEnd - elementNameSanitized.length : 0
41
- return fixer.replaceTextRange([rangeBeg, rangeEnd], elementNameSanitized)
42
- }
43
- context.report({
44
- messageId: 'startLowercase',
45
- loc,
46
- file,
47
- data: {
48
- entityName,
49
- elementName
50
- },
51
- suggest: [
52
- {
53
- messageId: 'fixLowercase',
54
- fix
55
- }
56
- ]
57
- })
32
+ function checkDefinition(defName, def) {
33
+ if (defName.startsWith('localized') || defName.endsWith('texts'))
34
+ return
35
+
36
+ checkElements(def)
37
+
38
+ function checkElements(art) {
39
+ if (art.elements) {
40
+ for (const elementName in art.elements) {
41
+ const element = art.elements[elementName]
42
+ checkStartLowercase(element, elementName)
43
+ checkElements(element)
58
44
  }
59
45
  }
60
46
  }
47
+
48
+ function checkStartLowercase (element, elementName) {
49
+ if (!element.$location?.file)
50
+ return // without location, we can't report anything properly
51
+
52
+ if (elementName.charAt(0) !== elementName.charAt(0).toLowerCase()
53
+ && !allowedUpperCaseElements.includes(elementName)) {
54
+ context.report({
55
+ messageId: 'startLowercase',
56
+ loc: context.getLocation(elementName, element),
57
+ file: element.$location.file,
58
+ data: {
59
+ defName,
60
+ elementName
61
+ }
62
+ })
63
+ }
64
+ }
61
65
  }
62
66
  }
63
67
  }
@@ -1,59 +1,49 @@
1
1
  'use strict'
2
2
 
3
+ const { RULE_CATEGORIES } = require('../constants')
3
4
  const { splitDefName } = require('../utils/rules')
4
5
 
5
6
  module.exports = {
6
7
  meta: {
7
8
  schema: [{/* to avoid deprecation warning for ESLint 9 */}],
8
9
  docs: {
10
+ category: RULE_CATEGORIES.model,
9
11
  description: 'Regular entity names should start with uppercase letters.',
10
- url: 'https://cap.cloud.sap/docs/tools/cds-lint/meta/start-entities-uppercase',
12
+ url: 'https://cap.cloud.sap/docs/tools/cds-lint/rules/start-entities-uppercase',
11
13
  },
12
14
  type: 'suggestion',
13
- hasSuggestions: true,
14
15
  messages: {
15
16
  startUppercase: "Entity name '{{entityName}}' should start with an uppercase letter.",
16
- fixUppercase: 'Start entity name with an uppercase letter.'
17
17
  },
18
18
  fixable: 'code',
19
19
  model: 'parsed',
20
20
  },
21
21
  create(context) {
22
- const sourcecode = context.getSourceCode()
22
+ const model = context.getModel()
23
+ if (!model?.definitions)
24
+ return
23
25
 
24
- return { entity: checkStartsUppercase }
26
+ return function checkAllEntitiesStartWithUppercase() {
27
+ for (const defName in model.definitions) {
28
+ const def = model.definitions[defName]
29
+ if (def.kind === 'entity') {
30
+ checkEntityStartsUppercase(defName, def)
31
+ }
32
+ }
33
+ }
25
34
 
26
- function checkStartsUppercase (e) {
27
- if (e.kind !== 'entity')
28
- return // workaround for #424
35
+ function checkEntityStartsUppercase(name, entity) {
36
+ if (!entity.$location?.file)
37
+ return // without location, we can't report anything properly
29
38
 
30
- const entityName = splitDefName(e).name
39
+ const entityName = splitDefName(entity, name).name
31
40
  if (entityName.charAt(0) !== entityName.charAt(0).toUpperCase()) {
32
- if (e.$location?.file) {
33
- const file = e.$location.file
34
- const loc = context.getLocation(entityName, e)
35
- const fix = fixer => {
36
- const entityNameSanitized = entityName.charAt(0).toUpperCase() + entityName.slice(1)
37
- const rangeEnd = sourcecode.getIndexFromLoc({
38
- line: loc.end.line,
39
- column: loc.end.column
40
- })
41
- const rangeBeg = rangeEnd ? rangeEnd - entityNameSanitized.length : 0
42
- return fixer.replaceTextRange([rangeBeg, rangeEnd], entityNameSanitized)
43
- }
44
- context.report({
45
- messageId: 'startUppercase',
46
- loc,
47
- file,
48
- data: { entityName },
49
- suggest: [
50
- {
51
- messageId: 'fixUppercase',
52
- fix
53
- }
54
- ]
55
- })
56
- }
41
+ context.report({
42
+ messageId: 'startUppercase',
43
+ loc: context.getLocation(entityName, entity),
44
+ file: entity.$location.file,
45
+ data: { entityName },
46
+ })
57
47
  }
58
48
  }
59
49
  }
@@ -3,6 +3,7 @@
3
3
  const cds = require('@sap/cds')
4
4
  const { basename, extname } = require('node:path')
5
5
  const findFuzzy = require('../utils/findFuzzy')
6
+ const { RULE_CATEGORIES } = require('../constants')
6
7
  const SEP = '[,;\t]'
7
8
  const EOL = '\\r?\\n'
8
9
 
@@ -11,9 +12,9 @@ module.exports = {
11
12
  schema: [{/* to avoid deprecation warning for ESLint 9 */}],
12
13
  docs: {
13
14
  description: 'CSV files for entities must refer to valid element names.',
14
- category: 'Model Validation',
15
+ category: RULE_CATEGORIES.csv,
15
16
  recommended: true,
16
- url: 'https://cap.cloud.sap/docs/tools/cds-lint/meta/valid-csv-header',
17
+ url: 'https://cap.cloud.sap/docs/tools/cds-lint/rules/valid-csv-header',
17
18
  },
18
19
  severity: 'warn',
19
20
  type: 'problem',
@@ -1,35 +1,38 @@
1
1
  'use strict'
2
2
 
3
- /**
4
- * Simple cache to store model and any cds calls made in the rule creation
5
- * api to modify the model
6
- */
7
- const cache = new Map()
8
3
 
9
- module.exports = {
4
+ class Cache {
5
+ #entries = new Map()
6
+
10
7
  has (key) {
11
- return cache.has(key)
12
- },
8
+ return this.#entries.has(key)
9
+ }
13
10
  set (key, value) {
14
- return cache.set(key, [value, Date.now()])
15
- },
11
+ return this.#entries.set(key, [value, Date.now()])
12
+ }
16
13
  get (key) {
17
- return cache.get(key) ? cache.get(key)[0] : undefined
18
- },
14
+ return this.#entries.has(key) ? this.#entries.get(key)[0] : undefined
15
+ }
19
16
  dump () {
20
17
  const dump = {}
21
- for (const [key, value] of cache.entries()) {
18
+ for (const [key, value] of this.#entries.entries()) {
22
19
  const timestamp = new Date(value[1])
23
20
  dump[key] = { key, value: value[0], timestamp }
24
21
  }
25
22
  return JSON.stringify(dump, null, 2)
26
- },
23
+ }
27
24
  remove (key) {
28
- if (cache.has(key)) {
29
- cache.delete(key)
25
+ if (this.#entries.has(key)) {
26
+ this.#entries.delete(key)
30
27
  }
31
- },
28
+ }
32
29
  clear () {
33
- cache.clear()
30
+ this.#entries.clear()
34
31
  }
35
32
  }
33
+
34
+
35
+ module.exports = {
36
+ Cache,
37
+ globalCache: new Cache(),
38
+ }
@@ -17,10 +17,10 @@ const fs = require('fs')
17
17
  const path = require('path')
18
18
  const cds = require('@sap/cds')
19
19
 
20
- const Cache = require('./Cache')
20
+ const { globalCache } = require('./Cache')
21
21
  const constants = require('../constants')
22
22
  const isConfiguredFileType = require('./isConfiguredFileType')
23
- const getProjectRootPath = require('./getProjectRootPath')
23
+ const { getProjectRootPath, hasProjectRoots } = require('./projectRootPath')
24
24
  const { CdsLintAssertionError } = require('./LintError')
25
25
 
26
26
  const LOG = cds.debug('lint:plugin')
@@ -48,18 +48,18 @@ module.exports = function createRule(spec) {
48
48
  Program: node => {
49
49
  const file = context.getFilename()
50
50
  if (file !== filePrev) {
51
- LOG && LOG(`File: ${context.getFilename()}`)
51
+ LOG?.(`File: ${context.getFilename()}`)
52
52
  }
53
- const cdscontext = extendContext(node, context, meta)
54
- Cache.set('context', cdscontext)
55
- const { isTest, isValidFile, doEnvironmentChecks, doRootModelChecks, showInEditor } = checkEntryCriteria(meta, cdscontext)
53
+ const cdsContext = extendContext(node, context, meta)
54
+ globalCache.set('context', cdsContext)
55
+ const { isTest, isValidFile, doEnvironmentChecks, doRootModelChecks, showInEditor } = checkEntryCriteria(meta, cdsContext)
56
56
  switch (meta.model) {
57
57
  case 'none':
58
58
  if (doEnvironmentChecks) {
59
- if (isTest || !Cache.has(`rule:${cdscontext.id}`)) {
60
- LOG && LOG(` Model: "${meta.model}" Rule: ${context.id}`)
61
- Cache.set(`rule:${cdscontext.id}:${Cache.get('rootpath')}`, 'done')
62
- createReport(node, cdscontext, meta, create)
59
+ if (isTest || !globalCache.has(`rule:${cdsContext.id}`)) {
60
+ LOG?.(` Model: "${meta.model}" Rule: ${context.id}`)
61
+ globalCache.set(`rule:${cdsContext.id}:${globalCache.get('rootpath')}`, 'done')
62
+ createReport(node, cdsContext, meta, create)
63
63
  }
64
64
  }
65
65
  break
@@ -67,24 +67,24 @@ module.exports = function createRule(spec) {
67
67
  case 'inferred':
68
68
  if (isValidFile && doRootModelChecks) {
69
69
  if (showInEditor) {
70
- Cache.remove(`model:${Cache.get('rootpath')}`)
71
- Cache.remove(`rule:${cdscontext.id}:${Cache.get('rootpath')}`)
72
- Cache.remove(`report:${context.getFilename()}:${context.id}`)
70
+ globalCache.remove(`model:${globalCache.get('rootpath')}`)
71
+ globalCache.remove(`rule:${cdsContext.id}:${globalCache.get('rootpath')}`)
72
+ globalCache.remove(`report:${context.getFilename()}:${context.id}`)
73
73
  }
74
- if (isTest || showInEditor || !Cache.has(`rule:${cdscontext.id}:${Cache.get('rootpath')}`)) {
75
- LOG && LOG(` Model: "${meta.model}" Rule: ${context.id}`)
74
+ if (isTest || showInEditor || !globalCache.has(`rule:${cdsContext.id}:${globalCache.get('rootpath')}`)) {
75
+ LOG?.(` Model: "${meta.model}" Rule: ${context.id}`)
76
76
  if (!showInEditor) {
77
- Cache.set(`rule:${cdscontext.id}:${Cache.get('rootpath')}`, 'done')
77
+ globalCache.set(`rule:${cdsContext.id}:${globalCache.get('rootpath')}`, 'done')
78
78
  }
79
- createReport(node, cdscontext, meta, create)
79
+ createReport(node, cdsContext, meta, create)
80
80
  } else {
81
- if (Cache.has(`report:${context.getFilename()}:${context.id}`)) {
82
- const reports = Cache.get(`report:${context.getFilename()}:${context.id}`)
81
+ if (globalCache.has(`report:${context.getFilename()}:${context.id}`)) {
82
+ const reports = globalCache.get(`report:${context.getFilename()}:${context.id}`)
83
83
  for (const r of Array.from(reports)) {
84
84
  context.report(JSON.parse(r))
85
85
  }
86
- Cache.remove(`report:${context.getFilename()}:${context.id}`)
87
- Cache.set(`rule:${cdscontext.id}:${Cache.get('rootpath')}`, 'done')
86
+ globalCache.remove(`report:${context.getFilename()}:${context.id}`)
87
+ globalCache.set(`rule:${cdsContext.id}:${globalCache.get('rootpath')}`, 'done')
88
88
  }
89
89
  }
90
90
  }
@@ -92,8 +92,8 @@ module.exports = function createRule(spec) {
92
92
 
93
93
  default:
94
94
  if (isValidFile) {
95
- LOG && LOG(` Model: "${meta.model}" Rule: ${context.id}`)
96
- createReport(node, cdscontext, meta, create)
95
+ LOG?.(` Model: "${meta.model}" Rule: ${context.id}`)
96
+ createReport(node, cdsContext, meta, create)
97
97
  }
98
98
  break
99
99
  }
@@ -112,15 +112,14 @@ function isRunningWithESLint () {
112
112
  return process.argv[1].match(/eslint(\.js)?$/)
113
113
  }
114
114
 
115
- function checkEntryCriteria (meta, cdscontext) {
116
- const isTest = Cache.has('test')
117
- const showInEditor = cdscontext.options.includes('show')
118
- const hasProjectRoots = Cache.has(`roots:${Cache.get('rootpath')}`)
119
- const isValidFile = isConfiguredFileType(cdscontext.getFilename(), 'FILES')
120
- const doRootModelChecks = isTest || (hasProjectRoots && (isRunningWithCDSLint() || isRunningWithESLint()) || showInEditor)
115
+ function checkEntryCriteria (meta, cdsContext) {
116
+ const isTest = globalCache.has('test')
117
+ const showInEditor = cdsContext.options.includes('show')
118
+ const isValidFile = isConfiguredFileType(cdsContext.getFilename(), 'FILES')
119
+ const doRootModelChecks = isTest || (hasProjectRoots() && (isRunningWithCDSLint() || isRunningWithESLint()) || showInEditor)
121
120
  // Lint all env rules independent of any parsed file (i.e. 'cds lint' uses the lintText "" API)
122
121
  const doEnvironmentChecks =
123
- isTest || (isRunningWithCDSLint() && cdscontext.getFilename() === '<text>')
122
+ isTest || (isRunningWithCDSLint() && cdsContext.getFilename() === '<text>')
124
123
  return { isTest, isValidFile, doRootModelChecks, doEnvironmentChecks, showInEditor }
125
124
  }
126
125
 
@@ -128,7 +127,6 @@ function setMetaDefaults (meta) {
128
127
  meta ??= {}
129
128
  meta.severity ??= constants.DEFAULT_RULE_SEVERITY
130
129
  meta.docs ??= {}
131
- meta.docs.category ??= constants.DEFAULT_RULE_CATEGORY
132
130
  meta.model ??= 'parsed'
133
131
  return meta
134
132
  }
@@ -185,8 +183,10 @@ function createReport (node, cdsContext, meta, create) {
185
183
 
186
184
  function sanitizeFileLocation (d) {
187
185
  let parent = d
188
- while (!parent.$location && parent.parent && !parent.parent.definitions) parent = d.parent
189
- if (parent.$location) d.$location = parent.$location
186
+ while (!parent.$location && parent.parent && !parent.parent.definitions)
187
+ parent = d.parent
188
+ if (parent.$location)
189
+ d.$location = parent.$location
190
190
  return d
191
191
  }
192
192
 
@@ -196,11 +196,11 @@ function sanitizeFileLocation (d) {
196
196
  * @param meta
197
197
  */
198
198
  function extendContext (node, context, meta) {
199
- if (!Cache.has('test')) {
199
+ if (!globalCache.has('test')) {
200
200
  const filePath = context.getFilename()
201
201
  const rootPath = filePath && fs.existsSync(filePath) ? getProjectRootPath(filePath) : ''
202
202
  if (rootPath) {
203
- Cache.set('rootpath', rootPath)
203
+ globalCache.set('rootpath', rootPath)
204
204
  }
205
205
  }
206
206
 
@@ -214,15 +214,18 @@ function extendContext (node, context, meta) {
214
214
 
215
215
  const cdscontext = Object.create(Object.getPrototypeOf(context), descriptors)
216
216
  const { parserServices } = context.sourceCode || context
217
- cdscontext.getModel =
218
- meta.model === 'inferred' ? parserServices.getInferredCsn : parserServices.getParsedCsn
217
+
218
+ cdscontext.getModel = meta.model === 'inferred'
219
+ ? parserServices.getInferredCsn
220
+ : parserServices.getParsedCsn
221
+
219
222
  cdscontext.getEnvironment = () => {
220
223
  const options = context.options
221
224
  return options && options[0] && options[0].environment ? options[0].environment : undefined
222
225
  }
223
226
  cdscontext.getLocation = parserServices.getLocation
224
227
  cdscontext.getNode = Object.keys(parserServices).length > 0 ? parserServices.getNode : () => node
225
- cdscontext.getRootPath = () => Cache.get('rootpath')
228
+ cdscontext.getRootPath = () => globalCache.get('rootpath')
226
229
  return cdscontext
227
230
 
228
231
  function reportWrapper(r) {
@@ -232,7 +235,7 @@ function extendContext (node, context, meta) {
232
235
  if (!r.file) {
233
236
  throw new CdsLintAssertionError(`Rule ${context.id} must return a "file" property in the rule report!`)
234
237
  }
235
- const file = Cache.get('rootpath') ? resolveFilePath(r.file) : r.file
238
+ const file = globalCache.get('rootpath') ? resolveFilePath(r.file) : r.file
236
239
  if (cdscontext.getFilename() === file) {
237
240
  delete r.file
238
241
  context.report(r)
@@ -280,11 +283,11 @@ function cacheReport (r, filepath, context, meta) {
280
283
  }
281
284
  if (r) {
282
285
  let reports = new Set()
283
- if (Cache.has(`report:${filepath}:${context.id}`)) {
284
- reports = Cache.get(`report:${filepath}:${context.id}`)
286
+ if (globalCache.has(`report:${filepath}:${context.id}`)) {
287
+ reports = globalCache.get(`report:${filepath}:${context.id}`)
285
288
  }
286
289
  reports.add(JSON.stringify(r))
287
- Cache.set(`report:${filepath}:${context.id}`, reports)
290
+ globalCache.set(`report:${filepath}:${context.id}`, reports)
288
291
  }
289
292
  }
290
293
 
@@ -295,7 +298,7 @@ function cacheReport (r, filepath, context, meta) {
295
298
  */
296
299
  function getDisabled (code, sourcecode, line) {
297
300
  const listDisabled = []
298
- const rules = Cache.get('rules')
301
+ const rules = globalCache.get('rules')
299
302
  const rulesDisabled = Object.keys(rules).reduce((o, key) => ({ ...o, [key]: 'on' }), {})
300
303
  if (code) {
301
304
  const matches = [...code.matchAll(REGEX_COMMENTS)]
@@ -375,5 +378,5 @@ function getLastLine (code) {
375
378
  * @param {string} file
376
379
  */
377
380
  function resolveFilePath (file) {
378
- return path.isAbsolute(file) ? file : path.join(Cache.get('rootpath'), file)
381
+ return path.isAbsolute(file) ? file : path.join(globalCache.get('rootpath'), file)
379
382
  }