@sap/eslint-plugin-cds 2.4.1 → 2.5.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +16 -2
  2. package/lib/api/index.js +9 -8
  3. package/lib/conf/all.js +21 -0
  4. package/lib/conf/index.js +22 -0
  5. package/lib/conf/recommended.js +18 -0
  6. package/lib/constants.js +10 -8
  7. package/lib/index.js +11 -32
  8. package/lib/parser.js +158 -7
  9. package/lib/rules/assoc2many-ambiguous-key.js +21 -38
  10. package/lib/rules/auth-no-empty-restrictions.js +13 -21
  11. package/lib/rules/auth-use-requires.js +15 -15
  12. package/lib/rules/auth-valid-restrict-grant.js +71 -34
  13. package/lib/rules/auth-valid-restrict-keys.js +22 -16
  14. package/lib/rules/auth-valid-restrict-to.js +71 -27
  15. package/lib/rules/auth-valid-restrict-where.js +24 -15
  16. package/lib/rules/index.js +26 -0
  17. package/lib/rules/latest-cds-version.js +8 -7
  18. package/lib/rules/min-node-version.js +10 -9
  19. package/lib/rules/no-db-keywords.js +16 -15
  20. package/lib/rules/no-dollar-prefixed-names.js +9 -7
  21. package/lib/rules/no-join-on-draft-enabled-entities.js +9 -24
  22. package/lib/rules/require-2many-oncond.js +9 -14
  23. package/lib/rules/sql-cast-suggestion.js +6 -20
  24. package/lib/rules/start-elements-lowercase.js +5 -8
  25. package/lib/rules/start-entities-uppercase.js +8 -11
  26. package/lib/rules/valid-csv-header.js +66 -66
  27. package/lib/{api/lint.d.ts → types.d.ts} +4 -7
  28. package/lib/utils/Cache.js +33 -0
  29. package/lib/utils/Colors.js +9 -0
  30. package/lib/utils/createRule.js +304 -0
  31. package/lib/utils/createRuleDocs.js +361 -0
  32. package/lib/utils/{fuzzySearch.js → findFuzzy.js} +0 -2
  33. package/lib/utils/genDocs.js +363 -0
  34. package/lib/utils/getConfigPath.js +33 -0
  35. package/lib/utils/getConfiguredFileTypes.js +10 -0
  36. package/lib/utils/getFileExtensions.js +8 -0
  37. package/lib/utils/isConfiguredFileType.js +20 -0
  38. package/lib/utils/jsonc.js +1 -1
  39. package/lib/utils/jsoncParser.js +1 -0
  40. package/lib/utils/rules.js +112 -1041
  41. package/lib/utils/runRuleTester.js +111 -0
  42. package/package.json +4 -4
  43. package/lib/processor.js +0 -50
  44. package/lib/utils/helpers.js +0 -94
  45. package/lib/utils/model.js +0 -393
  46. package/lib/utils/ruleHelpers.js +0 -199
  47. package/lib/utils/ruleTester.js +0 -78
  48. package/lib/utils/validate.js +0 -36
@@ -1,68 +1,105 @@
1
- const { validateObject, validateString } = require("../utils/ruleHelpers");
1
+ const { findFuzzy, isEmptyString, isEmptyObject, isStringInArray } = require("../utils/rules");
2
2
 
3
- const DEFAULT_SEVERITY = "error";
4
3
  const REPLACE_AS_WRITE_EVENTS = ["READ", "CREATE", "UPDATE", "DELETE"];
5
4
  const VALID_EVENTS = REPLACE_AS_WRITE_EVENTS.concat(["INSERT", "UPSERT", "WRITE", "*"]);
6
5
 
7
6
  module.exports = {
8
7
  meta: {
9
8
  docs: {
10
- description: '`@restrict.grant` must have valid values',
9
+ description: "`@restrict.grant` must have valid values.",
11
10
  category: "Model Validation",
12
- recommended: true,
13
- version: "2.4.1",
11
+ recommended: true
14
12
  },
15
- severity: DEFAULT_SEVERITY,
16
- hasSuggestions: true,
17
13
  messages: {
18
14
  InvalidItem: `Invalid item '{{invalid}}'. Did you mean '{{candidates}}'?`,
19
- ReplaceItemWith: `Replace '{{invalid}}' with '{{candidates}}'`,
15
+ ReplaceItemWith: `Replace '{{invalid}}' with '{{candidates}}'`
20
16
  },
17
+ type: "problem",
18
+ model: "inferred"
21
19
  },
22
20
  create(context) {
23
21
  return {
24
- entity: check_restrict_grant,
25
- //service: check_hierarchy_action
22
+ entity: check_restrict_grant
26
23
  };
27
24
 
28
25
  function check_restrict_grant(e) {
29
- const reports = [];
30
-
26
+ const node = context.getNode(e);
27
+ const file = e.$location.file;
31
28
  if (e["@restrict"]) {
32
29
  const actions = e.actions;
33
30
  const actionNames = actions ? Object.keys(actions).map((s) => actions[s].name) : [];
34
31
  const validEventsAndActions = VALID_EVENTS.concat(actionNames);
32
+
35
33
  for (const entry of e["@restrict"]) {
36
- const grantValues = entry.grant;
37
- if (Object.keys(entry).includes('grant')) {
38
- switch (typeof grantValues) {
34
+ if (Object.keys(entry).includes("grant")) {
35
+ const grantValue = entry.grant;
36
+ switch (typeof grantValue) {
39
37
  case "string": {
40
- validateString(
41
- reports,
42
- context,
43
- e,
44
- { key: "grant", name: "event/action" },
45
- grantValues,
46
- validEventsAndActions
47
- );
38
+ if (isEmptyString(grantValue)) {
39
+ context.report({
40
+ message: `Missing event/action on ${e.name} for \`@restrict.grant\`.`,
41
+ node,
42
+ file
43
+ });
44
+ } else {
45
+ if (!isStringInArray(grantValue, validEventsAndActions, true)) {
46
+ const candidates = findFuzzy(grantValue, validEventsAndActions.sort());
47
+ context.report({
48
+ messageId: "InvalidItem",
49
+ data: { invalid: grantValue, candidates },
50
+ node,
51
+ file
52
+ });
53
+ }
54
+ }
48
55
  break;
49
56
  }
57
+
50
58
  case "object":
51
- validateObject(
52
- reports,
53
- context,
54
- e,
55
- { key: "grant", name: "events/actions" },
56
- grantValues,
57
- validEventsAndActions
58
- );
59
+ if (isEmptyObject(grantValue)) {
60
+ context.report({
61
+ message: `Missing event/action on ${e.name} for \`@restrict.grant\`.`,
62
+ node,
63
+ file
64
+ });
65
+ } else {
66
+ let valuesForWrite = grantValue.filter(function (item) {
67
+ return item !== "READ" && item !== "WRITE" && item !== "*";
68
+ });
69
+ for (const value of grantValue) {
70
+ if (!validEventsAndActions.includes(value)) {
71
+ const candidates = findFuzzy(value, validEventsAndActions.sort());
72
+ context.report({
73
+ messageId: "InvalidItem",
74
+ data: { invalid: value, candidates },
75
+ node,
76
+ file
77
+ });
78
+ }
79
+ }
80
+ let allValuesIncluded = grantValue.every((v) => valuesForWrite.includes(v));
81
+ if (allValuesIncluded) {
82
+ context.report({
83
+ messageId: "InvalidItem",
84
+ data: { invalid: [`[${grantValue}]`], candidates: [`["WRITE"]`] },
85
+ node,
86
+ file
87
+ });
88
+ }
89
+ if (grantValue.includes("*")) {
90
+ context.report({
91
+ messageId: "InvalidItem",
92
+ data: { invalid: `[${grantValue}]`, candidates: [`["*"]`] },
93
+ node,
94
+ file
95
+ });
96
+ }
97
+ }
59
98
  break;
60
99
  }
61
100
  }
62
101
  }
63
102
  }
64
-
65
- return reports.length > 0 ? reports : undefined;
66
103
  }
67
- },
104
+ }
68
105
  };
@@ -1,37 +1,43 @@
1
- const { suggestItems } = require("../utils/ruleHelpers");
1
+ const { isEmptyObject, findFuzzy } = require("../utils/rules");
2
2
 
3
- const DEFAULT_SEVERITY = "error";
3
+ const VALID_RESTRICT_KEYS = ["grant", "to", "where"];
4
4
 
5
5
  module.exports = {
6
6
  meta: {
7
7
  docs: {
8
- description: '`@restrict` must have properly spelled `to`, `grant`, and `where` keys',
8
+ description: "`@restrict` must have properly spelled `to`, `grant`, and `where` keys.",
9
9
  category: "Model Validation",
10
- recommended: true,
11
- version: "2.4.1",
10
+ recommended: true
12
11
  },
13
- severity: DEFAULT_SEVERITY,
14
- hasSuggestions: true,
15
12
  messages: {
16
- InvalidItem: `Misspelled key '{{invalid}}'. Did you mean '{{candidates}}'?`,
17
- ReplaceItemWith: `Replace '{{invalid}}' with '{{candidates}}'`,
13
+ InvalidItem: `Misspelled key '{{invalid}}'. Did you mean '{{candidates}}'?`
18
14
  },
15
+ type: "problem",
16
+ model: "inferred"
19
17
  },
20
18
  create(context) {
21
19
  return {
22
- entity: check_restrict_keys,
20
+ entity: check_restrict_keys
23
21
  };
24
22
 
25
23
  function check_restrict_keys(e) {
26
- const reports = [];
27
-
28
- const validRestrictKeys = ["grant", "to", "where"];
29
24
  if (e["@restrict"]) {
30
25
  for (const entry of e["@restrict"]) {
31
- suggestItems(reports, context, Object.keys(entry), validRestrictKeys, DEFAULT_SEVERITY);
26
+ if (typeof entry === "object" && !isEmptyObject(entry)) {
27
+ for (const key of Object.keys(entry)) {
28
+ if (!VALID_RESTRICT_KEYS.includes(key)) {
29
+ const candidates = findFuzzy(key, VALID_RESTRICT_KEYS.sort());
30
+ context.report({
31
+ messageId: "InvalidItem",
32
+ data: { invalid: key, candidates },
33
+ node: context.getNode(e),
34
+ file: e.$location.file
35
+ });
36
+ }
37
+ }
38
+ }
32
39
  }
33
40
  }
34
- return reports.length > 0 ? reports : undefined;
35
41
  }
36
- },
42
+ }
37
43
  };
@@ -1,36 +1,34 @@
1
- const { splitEntityName, validateObject, validateString } = require("../utils/ruleHelpers");
1
+ const { isEmptyString, isStringInArray, findFuzzy, splitEntityName, isEmptyObject } = require("../utils/rules");
2
2
 
3
3
  const VALID_PSEUDO_ROLES = ["authenticated-user", "system-user", "any"];
4
4
 
5
5
  module.exports = {
6
6
  meta: {
7
7
  docs: {
8
- description: '`@restrict.to` must have valid values',
8
+ description: "`@restrict.to` must have valid values.",
9
9
  category: "Model Validation",
10
- recommended: true,
11
- version: "2.4.1",
10
+ recommended: true
12
11
  },
13
- severity: "warn",
14
12
  hasSuggestions: true,
15
13
  messages: {
16
14
  InvalidItem: `Invalid item '{{invalid}}'. Did you mean '{{candidates}}'?`,
17
- ReplaceItemWith: `Replace '{{invalid}}' with '{{candidates}}'`,
15
+ ReplaceItemWith: `Replace '{{invalid}}' with '{{candidates}}'`
18
16
  },
17
+ type: "problem",
18
+ model: "inferred"
19
19
  },
20
20
  create(context) {
21
- const { model } = context.cds;
22
-
23
21
  return {
24
- entity: check_restrict_to,
22
+ entity: check_restrict_to
25
23
  };
26
24
 
27
25
  function check_restrict_to(e) {
28
- const reports = [];
29
-
30
26
  const USER_ROLES = [];
31
- model.foreach('entity', e => {
27
+ const model = context.getModel();
28
+
29
+ model.foreach("entity", (e) => {
32
30
  if (e["@restrict"]) {
33
- e["@restrict"].forEach(p => {
31
+ e["@restrict"].forEach((p) => {
34
32
  if (p.to) {
35
33
  switch (typeof p.to) {
36
34
  case "string":
@@ -42,45 +40,91 @@ module.exports = {
42
40
  for (const r in p.to) {
43
41
  if (r !== r.toLowerCase() && !USER_ROLES.includes(r)) {
44
42
  USER_ROLES.push(r);
45
- }
43
+ }
46
44
  }
47
45
  }
48
46
  }
49
- })
47
+ });
50
48
  }
51
- });
49
+ });
52
50
  const ROLES = USER_ROLES.concat(VALID_PSEUDO_ROLES);
53
51
 
54
52
  if (e["@restrict"]) {
53
+ let node = context.getNode(e);
54
+ let file = e.$location.file;
55
55
  const { prefix } = splitEntityName(e);
56
56
  const prefixSplit = prefix.split(".");
57
57
  const serviceName = prefixSplit[prefixSplit.length - 1];
58
58
  let services = model.services;
59
59
 
60
- // For hierachies, check whether service restriction exists
61
- let grantAllTo;
60
+ // TODO: For hierachies, check whether service restriction exists
61
+ //let grantAllTo;
62
62
  Object.values(services).map((s) => {
63
- if (s.name === serviceName && s['@requires']) {
64
- grantAllTo = s['@requires'];
63
+ if (s.name === serviceName && s["@requires"]) {
64
+ //grantAllTo = s['@requires'];
65
65
  }
66
66
  });
67
67
 
68
68
  for (const entry of e["@restrict"]) {
69
- if (Object.keys(entry).includes('to')) {
70
- switch (typeof entry.to) {
69
+ if (Object.keys(entry).includes("to")) {
70
+ const toValue = entry.to;
71
+
72
+ switch (typeof toValue) {
71
73
  case "string": {
72
- const isPseudoRole = entry.to && (entry.to === entry.to.toLowerCase());
73
- validateString(reports, context, e, {key: 'to', name: 'role'}, entry.to, ROLES, isPseudoRole);
74
+ if (isEmptyString(toValue)) {
75
+ context.report({
76
+ message: `Missing role on ${e.name} for \`@restrict.to\`.`,
77
+ node,
78
+ file
79
+ });
80
+ } else {
81
+ const isPseudoRole = entry.to && entry.to === entry.to.toLowerCase();
82
+ if (!isStringInArray(toValue, ROLES, isPseudoRole)) {
83
+ const candidates = findFuzzy(toValue, ROLES.sort());
84
+ context.report({
85
+ messageId: "InvalidItem",
86
+ data: { invalid: toValue, candidates },
87
+ node,
88
+ file
89
+ });
90
+ }
91
+ }
74
92
  break;
75
93
  }
94
+
76
95
  case "object":
77
- validateObject(reports, context, e, {key: 'to', name: 'roles'}, entry.to, ROLES);
96
+ if (isEmptyObject(toValue)) {
97
+ context.report({
98
+ message: `Missing roles on ${e.name} for \`@restrict.to\`.`,
99
+ node,
100
+ file
101
+ });
102
+ } else {
103
+ if (toValue.includes("any")) {
104
+ context.report({
105
+ messageId: "InvalidItem",
106
+ data: { invalid: `[${toValue}]`, candidates: [`["any"]`] },
107
+ node,
108
+ file
109
+ });
110
+ }
111
+ toValue.forEach((value) => {
112
+ if (!ROLES.includes(value)) {
113
+ const candidates = findFuzzy(value, ROLES.sort());
114
+ context.report({
115
+ messageId: "InvalidItem",
116
+ data: { invalid: value, candidates },
117
+ node,
118
+ file
119
+ });
120
+ }
121
+ });
122
+ }
78
123
  break;
79
124
  }
80
125
  }
81
126
  }
82
127
  }
83
- return reports.length > 0 ? reports : undefined;
84
128
  }
85
- },
129
+ }
86
130
  };
@@ -1,33 +1,33 @@
1
- const { splitEntityName, validateObject, validateString } = require("../utils/ruleHelpers");
1
+ const cds = require("@sap/cds");
2
2
 
3
3
  const VALID_PSEUDO_ROLES = ["authenticated-user", "system-user", "any"];
4
4
 
5
5
  module.exports = {
6
6
  meta: {
7
7
  docs: {
8
- description: '`@restrict.where` must have valid values',
8
+ description: "`@restrict.where` must have valid values.",
9
9
  category: "Model Validation",
10
- recommended: true,
11
- version: "2.4.1",
10
+ recommended: true
12
11
  },
13
12
  severity: "error",
14
13
  hasSuggestions: true,
15
14
  messages: {
16
15
  InvalidItem: `Invalid item '{{invalid}}'. Did you mean '{{candidates}}'?`,
17
- ReplaceItemWith: `Replace '{{invalid}}' with '{{candidates}}'`,
16
+ ReplaceItemWith: `Replace '{{invalid}}' with '{{candidates}}'`
18
17
  },
18
+ type: "problem",
19
+ model: "inferred"
19
20
  },
20
21
  create(context) {
21
- const { model } = context.cds;
22
+ const model = context.getModel();
22
23
 
23
24
  return {
24
- entity: check_restrict_grant,
25
+ entity: check_restrict_grant
25
26
  };
26
27
 
27
28
  function check_restrict_grant(e) {
28
- const reports = [];
29
-
30
29
  const USER_ROLES = [];
30
+
31
31
  model.foreach("entity", (e) => {
32
32
  if (e["@restrict"]) {
33
33
  e["@restrict"].forEach((p) => {
@@ -52,29 +52,38 @@ module.exports = {
52
52
  const ROLES = USER_ROLES.concat(VALID_PSEUDO_ROLES);
53
53
 
54
54
  if (e["@restrict"]) {
55
+ const node = context.getNode(e);
56
+ const file = e.$location.file;
55
57
  for (const entry of e["@restrict"]) {
56
58
  const whereValues = entry.where;
57
59
  if (whereValues && typeof whereValues === "string") {
58
60
  let cxn;
59
61
  try {
60
- cxn = context.cds.parse.expr(entry.where);
62
+ cxn = cds.parse.expr(entry.where);
61
63
  } catch (err) {
62
- reports.push(`Invalid \`where\` expression, CDS compilation failed.`);
64
+ context.report({
65
+ message: `Invalid \`where\` expression, CDS compilation failed.`,
66
+ node,
67
+ file
68
+ });
63
69
  }
64
70
  if (cxn && cxn.xpr) {
65
71
  const operator = cxn.xpr[1];
66
72
  const role = cxn.xpr[2].ref;
67
73
  if (operator === "=") {
68
- const isValidRole = (role == '$user') || ROLES.includes(role);
74
+ const isValidRole = role == "$user" || ROLES.includes(role);
69
75
  if (!isValidRole) {
70
- reports.push(`Invalid \`where\` expression, role \`${role}\` not found.`);
76
+ context.report({
77
+ message: `Invalid \`where\` expression, role \`${role}\` not found.`,
78
+ node,
79
+ file
80
+ });
71
81
  }
72
82
  }
73
83
  }
74
84
  }
75
85
  }
76
86
  }
77
- return reports.length > 0 ? reports : undefined;
78
87
  }
79
- },
88
+ }
80
89
  };
@@ -0,0 +1,26 @@
1
+ const Cache = require("../utils/Cache");
2
+ const createRule = require("../utils/createRule");
3
+
4
+ const rules = {
5
+ "assoc2many-ambiguous-key": () => createRule(require("./assoc2many-ambiguous-key")),
6
+ "auth-no-empty-restrictions": () => createRule(require("./auth-no-empty-restrictions")),
7
+ "auth-use-requires": () => createRule(require("./auth-use-requires")),
8
+ "auth-valid-restrict-grant": () => createRule(require("./auth-valid-restrict-grant")),
9
+ "auth-valid-restrict-keys": () => createRule(require("./auth-valid-restrict-keys")),
10
+ "auth-valid-restrict-to": () => createRule(require("./auth-valid-restrict-to")),
11
+ "auth-valid-restrict-where": () => createRule(require("./auth-valid-restrict-where")),
12
+ "latest-cds-version": () => createRule(require("./latest-cds-version")),
13
+ "min-node-version": () => createRule(require("./min-node-version")),
14
+ "no-db-keywords": () => createRule(require("./no-db-keywords")),
15
+ "no-dollar-prefixed-names": () => createRule(require("./no-dollar-prefixed-names")),
16
+ "no-join-on-draft-enabled-entities": () => createRule(require("./no-join-on-draft-enabled-entities")),
17
+ "require-2many-oncond": () => createRule(require("./require-2many-oncond")),
18
+ "sql-cast-suggestion": () => createRule(require("./sql-cast-suggestion")),
19
+ "start-elements-lowercase": () => createRule(require("./start-elements-lowercase")),
20
+ "start-entities-uppercase": () => createRule(require("./start-entities-uppercase")),
21
+ "valid-csv-header": () => createRule(require("./valid-csv-header"))
22
+ };
23
+
24
+ Cache.set("rules", rules);
25
+
26
+ module.exports = rules;
@@ -4,22 +4,22 @@ const semver = require("semver");
4
4
  module.exports = {
5
5
  meta: {
6
6
  docs: {
7
- description: "Checks whether the latest `@sap/cds` version is being used.",
8
- category: "Environment",
9
- version: "1.0.4",
7
+ description: "Checks whether the latest `@sap/cds` version is being used."
10
8
  },
11
9
  type: "suggestion",
12
10
  hasSuggestions: true,
13
11
  messages: {
14
12
  latestCDSVersion: `A newer CDS version is available!`,
15
13
  },
14
+ severity: "off",
15
+ model: "none"
16
16
  },
17
17
  create: function (context) {
18
- return { all: check_latest_cds_version };
18
+ return check_latest_cds_version;
19
19
 
20
20
  function check_latest_cds_version() {
21
21
  let cdsVersions;
22
- let e = context.cds.environment;
22
+ let e = context.getEnvironment();
23
23
  if (!e) {
24
24
  e = cp
25
25
  .execSync(`npm outdated @sap/cds --json`, {
@@ -32,9 +32,10 @@ module.exports = {
32
32
  cdsVersions = e["@sap/cds"];
33
33
  // If current cds version is not the latest
34
34
  if (Object.keys(cdsVersions).length !== 0 && !semver.satisfies(cdsVersions.latest, cdsVersions.current)) {
35
- return {
35
+ context.report({
36
36
  messageId: "latestCDSVersion",
37
- };
37
+ node: context.getNode()
38
+ });
38
39
  }
39
40
  }
40
41
  }
@@ -4,25 +4,23 @@ const semver = require("semver");
4
4
  module.exports = {
5
5
  meta: {
6
6
  docs: {
7
- description: `Checks whether the minimum Node.js version required by \`@sap/cds\` is achieved.`,
8
- category: "Environment",
9
- recommended: true,
10
- version: "1.0.0",
7
+ description: `Checks whether the minimum Node.js version required by \`@sap/cds\` is achieved.`
11
8
  },
12
- severity: "error",
9
+ severity: "off",
13
10
  type: "problem",
11
+ model: "none"
14
12
  },
15
13
  create: function (context) {
16
- return { all: check_min_node_version }
14
+ return check_min_node_version;
17
15
 
18
16
  function check_min_node_version() {
19
- const e = context.cds.environment;
17
+ const e = context.getEnvironment();
20
18
  let nodeVersion, nodeVersionCDS;
21
19
  if (!e) {
22
20
  // Get current and required node versions
23
21
  try {
24
22
  const CDSPath = require.resolve("@sap/cds/package.json", {
25
- paths: [path.dirname(context.filePath)],
23
+ paths: [path.dirname(".")],
26
24
  });
27
25
  const jsonCDS = require(CDSPath);
28
26
  nodeVersion = process.version;
@@ -39,7 +37,10 @@ module.exports = {
39
37
  nodeVersionCDS &&
40
38
  !semver.satisfies(nodeVersion, nodeVersionCDS, { loose: true })
41
39
  ) {
42
- return `CDS minimum node version of ${nodeVersionCDS} required, found ${nodeVersion}!`;
40
+ context.report({
41
+ message: `CDS minimum node version of ${nodeVersionCDS} required, found ${nodeVersion}!`,
42
+ node: context.getNode()
43
+ });
43
44
  }
44
45
  }
45
46
 
@@ -1,33 +1,34 @@
1
+ const cds = require("@sap/cds");
2
+
1
3
  module.exports = {
2
4
  meta: {
3
5
  docs: {
4
6
  description: `Avoid using reserved SQL keywords.`,
5
- category: "Model Validation", //> values are specific for cds lint
6
- recommended: true,
7
- version: "2.1.0",
7
+ recommended: true
8
8
  },
9
- severity: "error" //> convenience option by cds.lint
9
+ type: "problem"
10
10
  },
11
11
  create(context) {
12
- const { cds } = context //> specific for cds lint
13
- const { db = { kind: "sql" } } = cds.env.requires
12
+ const { db = { kind: "sql" } } = cds.env.requires;
14
13
 
15
- return { //> return standard eslint visitor callbacks registered to CSN kinds, types, ...
14
+ return {
15
+ //> return standard eslint visitor callbacks registered to CSN kinds, types, ...
16
16
  entity: check_name_is_not_reserved,
17
17
  element: check_name_is_not_reserved
18
- }
18
+ };
19
19
 
20
20
  function check_name_is_not_reserved(d) {
21
21
  if (d.name in RESERVED) {
22
22
  // Do not blame in case of external services
23
- let srv = d._service || (d.parent && d.parent._service)
24
- if (srv && srv["@cds.external"]) return
25
-
26
- // Do blame
27
- return `'${d.name}' is a reserved keyword in ${db.kind.toUpperCase()}` //> convenience options by cds lint
23
+ let srv = d._service || (d.parent && d.parent._service);
24
+ if (srv && srv["@cds.external"]) return;
25
+ context.report({
26
+ message: `'${d.name}' is a reserved keyword in ${db.kind.toUpperCase()}`,
27
+ node: context.getNode(d)
28
+ });
28
29
  }
29
30
  }
30
- },
31
+ }
31
32
  };
32
33
 
33
34
  // REVISIT: Replace by compiler-provided check
@@ -40,5 +41,5 @@ const RESERVED = {
40
41
  group: 1,
41
42
  LIMIT: 1,
42
43
  Limit: 1,
43
- limit: 1,
44
+ limit: 1
44
45
  };
@@ -2,20 +2,22 @@ module.exports = {
2
2
  meta: {
3
3
  docs: {
4
4
  description: `Names must not start with $ to avoid possible shadowing of reserved variables.`,
5
- category: "Model Validation",
6
- recommended: true,
7
- version: "2.3.3",
5
+ recommended: true
8
6
  },
9
- severity: "warn",
7
+ type: "problem"
10
8
  },
11
9
  create(context) {
12
10
  return { element: _check };
13
11
 
14
12
  function _check(d) {
15
- // Do not blame in case of external services
16
13
  let srv = d._service || (d.parent && d.parent._service);
17
14
  if (srv && srv["@cds.external"]) return;
18
- return d.name.startsWith("$") ? [`“${d.name}” is prefixed with a dollar sign ($)`] : undefined;
15
+ if (d.name.startsWith("$")) {
16
+ context.report({
17
+ message: `“${d.name}” is prefixed with a dollar sign ($)`,
18
+ node: context.getNode(d)
19
+ });
20
+ }
19
21
  }
20
- },
22
+ }
21
23
  };