@sap/eslint-plugin-cds 2.2.1 → 2.2.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.
@@ -11,9 +11,13 @@ module.exports = require("../../api").createRule({
11
11
  let csnOdata;
12
12
  const m = context.cds.model;
13
13
  if (m && m.definitions) {
14
- csnOdata = context.cds.compile.for.odata(m);
15
- const csnOdataLinked = context.cds.linked(csnOdata);
16
- associationCardinalityFlaw(csnOdataLinked, context);
14
+ try {
15
+ csnOdata = context.cds.compile.for.odata(m);
16
+ const csnOdataLinked = context.cds.linked(csnOdata);
17
+ associationCardinalityFlaw(csnOdataLinked, context);
18
+ } catch (err) {
19
+ // Don't continue with rule if model fails to compile
20
+ }
17
21
  }
18
22
  },
19
23
  });
@@ -32,7 +36,10 @@ function associationCardinalityFlaw(csn, context) {
32
36
  refPlainElement = false;
33
37
  },
34
38
  (refEntity, refElement) => {
35
- if (refElement.type === "cds.Association" || refElement.type === "cds.Composition") {
39
+ if (
40
+ refElement.type === "cds.Association" ||
41
+ refElement.type === "cds.Composition"
42
+ ) {
36
43
  if (refElement.cardinality && refElement.cardinality.max === "*") {
37
44
  refCardinalityMult = true;
38
45
  }
@@ -80,16 +87,21 @@ function processEntity(csn, eachCallback) {
80
87
  const sourceAlias = [];
81
88
  if (definition.query.SELECT.from.ref) {
82
89
  // From
83
- sourceEntity = csn.definitions[definition.query.SELECT.from.ref.join("_")];
90
+ sourceEntity =
91
+ csn.definitions[definition.query.SELECT.from.ref.join("_")];
84
92
  sourceAlias.push({
85
93
  from: sourceEntity.name,
86
94
  as:
87
95
  definition.query.SELECT.from.as ||
88
96
  definition.query.SELECT.from.ref.slice(-1)[0].split(".").pop(),
89
97
  });
90
- } else if (definition.query.SELECT.from.args && definition.query.SELECT.from.args[0].ref) {
98
+ } else if (
99
+ definition.query.SELECT.from.args &&
100
+ definition.query.SELECT.from.args[0].ref
101
+ ) {
91
102
  // Join
92
- sourceEntity = csn.definitions[definition.query.SELECT.from.args[0].ref.join("_")];
103
+ sourceEntity =
104
+ csn.definitions[definition.query.SELECT.from.args[0].ref.join("_")];
93
105
  definition.query.SELECT.from.args.forEach((arg) => {
94
106
  sourceAlias.push({
95
107
  from: arg.ref.join("_"),
@@ -136,12 +148,18 @@ function processElement(
136
148
  if (!refElement && definition.query.SELECT.mixin) {
137
149
  refElement = definition.query.SELECT.mixin[ref];
138
150
  if (!refElement && definition.query.SELECT.mixin[column.ref[0]]) {
139
- refElement = definition.query.SELECT.mixin[column.ref[0]]._target.elements[ref];
151
+ refElement =
152
+ definition.query.SELECT.mixin[column.ref[0]]._target.elements[
153
+ ref
154
+ ];
140
155
  }
141
156
  }
142
157
  }
143
158
  eachCallback(refEntity, refElement);
144
- if (refElement.type === "cds.Association" || refElement.type === "cds.Composition") {
159
+ if (
160
+ refElement.type === "cds.Association" ||
161
+ refElement.type === "cds.Composition"
162
+ ) {
145
163
  refEntity = csn.definitions[refElement.target];
146
164
  }
147
165
  }
@@ -14,7 +14,7 @@ module.exports = require("../../api").createRule({
14
14
  latestCDSVersion: `A newer CDS version is available!`,
15
15
  },
16
16
  },
17
- create: function(context) {
17
+ create: function (context) {
18
18
  let result;
19
19
  let cdsVersions;
20
20
  const e = context.cds.environment;
@@ -39,9 +39,8 @@ module.exports = require("../../api").createRule({
39
39
  ) {
40
40
  // Add to ESLint report
41
41
  context.report({
42
- messageId: 'latestCDSVersion',
43
- loc: { line: 0, column: 0 },
42
+ messageId: "latestCDSVersion",
44
43
  });
45
44
  }
46
- }
47
- });
45
+ },
46
+ });
@@ -36,7 +36,6 @@ module.exports = require("../../api").createRule({
36
36
  ) {
37
37
  context.report({
38
38
  message: `CDS minimum node version of ${nodeVersionCDS} required, found ${nodeVersion}!`,
39
- loc: { line: 0, column: 1 },
40
39
  });
41
40
  }
42
41
  },
@@ -1,25 +1,35 @@
1
1
  module.exports = require("../../api").defineRule({
2
- meta: { docs: { description: `Avoid using reserved SQL keywords.` }},
3
- create (context) {
4
- const { db = { kind: 'sql' } } = context.cds.env.requires
5
- function _check (d) {
2
+ meta: {
3
+ docs: {
4
+ description: `Avoid using reserved SQL keywords.`,
5
+ category: "Model Validation",
6
+ },
7
+ },
8
+ create(context) {
9
+ const { db = { kind: "sql" } } = context.cds.env.requires;
10
+ function _check(d) {
6
11
  if (d.name in RESERVED) {
7
-
8
12
  // Do not blame in case of external services
9
- let srv = d._service || d.parent && d.parent._service
10
- if (srv && srv['@cds.external']) return
13
+ let srv = d._service || (d.parent && d.parent._service);
14
+ if (srv && srv["@cds.external"]) return;
11
15
 
12
16
  // Do blame
13
- return `'${d.name}' is a reserved keyword in ${db.kind.toUpperCase()}`
17
+ return `'${d.name}' is a reserved keyword in ${db.kind.toUpperCase()}`;
14
18
  }
15
19
  }
16
- return { entity: _check, element: _check }
17
- }
18
- })
20
+ return { entity: _check, element: _check };
21
+ },
22
+ });
19
23
 
20
24
  // REVISIT: Replace by compiler-provided check
21
25
  const RESERVED = {
22
- ORDER: 1, Order: 1, order: 1,
23
- GROUP: 1, Group: 1, group: 1,
24
- LIMIT: 1, Limit: 1, limit: 1,
25
- }
26
+ ORDER: 1,
27
+ Order: 1,
28
+ order: 1,
29
+ GROUP: 1,
30
+ Group: 1,
31
+ group: 1,
32
+ LIMIT: 1,
33
+ Limit: 1,
34
+ limit: 1,
35
+ };
@@ -0,0 +1,35 @@
1
+ module.exports = require("../../api").createRule({
2
+ meta: {
3
+ docs: {
4
+ description: `Draft-enabled entities shall not be used in views that make use of \`JOIN\`.`,
5
+ category: "Model Validation",
6
+ version: "2.2.1",
7
+ },
8
+ type: "suggestion",
9
+ messages: {
10
+ noJoinOnDraftEnabledEntities: `Do not use draft-enabled entities in views that make use of \`JOIN\`.`,
11
+ },
12
+ },
13
+ create: function (context) {
14
+ const m = context.cds.model;
15
+ m.foreach("entity", (entity) => {
16
+ if (entity["@odata.draft.enabled"]) {
17
+ if (entity.query.SELECT.from.join) {
18
+ const location = entity.query.$location;
19
+ if (context.sourcecode.lines[location.line - 1]) {
20
+ const endCol = context.sourcecode.lines[location.line - 1].length;
21
+ const loc = {
22
+ start: { line: location.line, column: location.col - 1 },
23
+ end: { line: location.line, column: endCol },
24
+ };
25
+ context.report({
26
+ messageId: "noJoinOnDraftEnabledEntities",
27
+ loc,
28
+ file: entity.$location.file,
29
+ });
30
+ }
31
+ }
32
+ }
33
+ });
34
+ },
35
+ });
@@ -7,7 +7,7 @@ module.exports = require("../../api").createRule({
7
7
  },
8
8
  type: "problem",
9
9
  },
10
- create: function(context) {
10
+ create: function (context) {
11
11
  const m = context.cds.model;
12
12
  m.forall((d) => {
13
13
  if (d.name) {
@@ -1,13 +1,20 @@
1
1
  // @ts-check
2
2
  module.exports = require("../../api").createRule({
3
- meta: {docs: {
4
- description: "{{description}}",
5
- version: "{{version}}"
6
- }},
3
+ meta: {
4
+ docs: {
5
+ description: "{{description}}",
6
+ version: "{{version}}"
7
+ },
8
+ type:"{{type}}",
9
+ },
7
10
  create: function(context) {
8
- return [{
9
- message: "{{messages}}",
10
- loc: {{loc}}
11
- }];
11
+ const m = cotext.cds.model;
12
+ m.forall((d)) => {
13
+ // Add cds logic here, for example
14
+ return [{
15
+ message: "{{messages}}",
16
+ loc: {{loc}}
17
+ }];
18
+ }
12
19
  }
13
20
  })
@@ -8,7 +8,8 @@ module.exports = require("../../api").createRule({
8
8
  type: "suggestion",
9
9
  hasSuggestions: true,
10
10
  messages: {
11
- missingSQLCast: "Potential issue - Missing SQL cast for column expression?",
11
+ missingSQLCast:
12
+ "Potential issue - Missing SQL cast for column expression?",
12
13
  },
13
14
  },
14
15
  create: function (context) {
@@ -26,7 +27,8 @@ module.exports = require("../../api").createRule({
26
27
  continue;
27
28
  } else {
28
29
  if (context.sourcecode.lines[location.line - 1]) {
29
- const endCol = context.sourcecode.lines[location.line - 1].length;
30
+ const endCol =
31
+ context.sourcecode.lines[location.line - 1].length;
30
32
  const loc = {
31
33
  start: { line: location.line, column: location.col - 1 },
32
34
  end: { line: location.line, column: endCol },
@@ -8,7 +8,8 @@ module.exports = require("../../api").createRule({
8
8
  type: "suggestion",
9
9
  hasSuggestions: true,
10
10
  messages: {
11
- startLowercase: "Element name '{{entityName}}.{{elementName}}' should start with a lowercase letter.",
11
+ startLowercase:
12
+ "Element name '{{entityName}}.{{elementName}}' should start with a lowercase letter.",
12
13
  },
13
14
  fixable: "code",
14
15
  },
@@ -21,7 +22,9 @@ module.exports = require("../../api").createRule({
21
22
  const element = d.elements[elementName];
22
23
  if (
23
24
  elementName &&
24
- !(entityName.startsWith("localized") || entityName.endsWith("texts"))
25
+ !(
26
+ entityName.startsWith("localized") || entityName.endsWith("texts")
27
+ )
25
28
  ) {
26
29
  if (
27
30
  elementName.charAt(0) !== elementName.charAt(0).toLowerCase() &&
@@ -37,8 +40,13 @@ module.exports = require("../../api").createRule({
37
40
  line: loc.end.line,
38
41
  column: loc.end.column,
39
42
  });
40
- const rangeBeg = rangeEnd ? rangeEnd - elementNameSanitized.length : 0;
41
- return fixer.replaceTextRange([rangeBeg, rangeEnd], elementNameSanitized);
43
+ const rangeBeg = rangeEnd
44
+ ? rangeEnd - elementNameSanitized.length
45
+ : 0;
46
+ return fixer.replaceTextRange(
47
+ [rangeBeg, rangeEnd],
48
+ elementNameSanitized
49
+ );
42
50
  };
43
51
  context.report({
44
52
  messageId: "startLowercase",
@@ -47,7 +55,7 @@ module.exports = require("../../api").createRule({
47
55
  fix,
48
56
  data: {
49
57
  entityName,
50
- elementName
58
+ elementName,
51
59
  },
52
60
  suggest: [
53
61
  {
@@ -8,7 +8,8 @@ module.exports = require("../../api").createRule({
8
8
  type: "suggestion",
9
9
  hasSuggestions: true,
10
10
  messages: {
11
- startUppercase: "Entity name '{{entityName}}' should start with an uppercase letter.",
11
+ startUppercase:
12
+ "Entity name '{{entityName}}' should start with an uppercase letter.",
12
13
  },
13
14
  fixable: "code",
14
15
  },
@@ -19,7 +20,10 @@ module.exports = require("../../api").createRule({
19
20
  let entityName = e.name;
20
21
  const names = entityName.split(".");
21
22
  entityName = names[names.length - 1];
22
- if (entityName && !(entityName.startsWith("localized") || entityName.endsWith("texts"))) {
23
+ if (
24
+ entityName &&
25
+ !(entityName.startsWith("localized") || entityName.endsWith("texts"))
26
+ ) {
23
27
  if (entityName.charAt(0) !== entityName.charAt(0).toUpperCase()) {
24
28
  if (e.$location && e.$location.file) {
25
29
  const file = e.$location.file;
@@ -31,8 +35,13 @@ module.exports = require("../../api").createRule({
31
35
  line: loc.end.line,
32
36
  column: loc.end.column,
33
37
  });
34
- const rangeBeg = rangeEnd ? rangeEnd - entityNameSanitized.length : 0;
35
- return fixer.replaceTextRange([rangeBeg, rangeEnd], entityNameSanitized);
38
+ const rangeBeg = rangeEnd
39
+ ? rangeEnd - entityNameSanitized.length
40
+ : 0;
41
+ return fixer.replaceTextRange(
42
+ [rangeBeg, rangeEnd],
43
+ entityNameSanitized
44
+ );
36
45
  };
37
46
  context.report({
38
47
  messageId: "startUppercase",
@@ -1,17 +1,6 @@
1
- const { files } = require("../constants");
1
+ const { files, envFiles, modelFiles } = require("../constants");
2
2
 
3
3
  module.exports = {
4
- /**
5
- * Checks whether plugin is used in 'test' mode
6
- * @returns boolean
7
- */
8
- isTest: function () {
9
- let isTest = false;
10
- if (process.argv[1].includes("jest") || process.argv[1].includes("mocha")) {
11
- isTest = true;
12
- }
13
- return isTest;
14
- },
15
4
 
16
5
  /**
17
6
  * Checks whether the given file path contains a file extension allowed by
@@ -19,15 +8,23 @@ module.exports = {
19
8
  * @param {string} filePath
20
9
  * @returns boolean
21
10
  */
22
- isValidFile: function (filePath) {
23
- const regex = new RegExp(
24
- `${files
25
- .map((file) => {
26
- return file.replace("*", "");
27
- })
28
- .join("$|")}$`
29
- );
30
- return regex.test(filePath);
11
+ isValidFile: function (filePath, key = "") {
12
+ function genRegex(key) {
13
+ return new RegExp(
14
+ `${key
15
+ .map((file) => {
16
+ return file.replace("*", "");
17
+ })
18
+ .join("$|")}$`
19
+ );
20
+ }
21
+ if (key === "model") {
22
+ return genRegex(modelFiles).test(filePath);
23
+ } else if (key === "env") {
24
+ return genRegex(envFiles).test(filePath);
25
+ } else {
26
+ return genRegex(files).test(filePath);
27
+ }
31
28
  },
32
29
 
33
30
  /**
@@ -38,6 +35,15 @@ module.exports = {
38
35
  return process.argv.join(" ").includes("dbaeumer.vscode-eslint");
39
36
  },
40
37
 
38
+ /**
39
+ * Returns an array of allowed file extensions
40
+ * the plugin can parse of the form "*.ext"
41
+ * @returns {ConfigOverrideFiles} Array of file extensions
42
+ */
43
+ getFileExtensions: function () {
44
+ return files;
45
+ },
46
+
41
47
  /**
42
48
  * Prints a formatted message string according to the styles provided
43
49
  * @param msg message to print
@@ -59,31 +65,4 @@ module.exports = {
59
65
  });
60
66
  return `${msgStyle}${msg}${types.reset}`;
61
67
  },
62
-
63
- /**
64
- * Checks whether the compiled cds model contains compilation errors which
65
- * should only be reported via the 'cds-compile-error' rule
66
- * @param cds cds object
67
- * @param ruleID rule name
68
- * @returns
69
- */
70
- hasCompilationError: function (context) {
71
- const cds = context.cds;
72
- const ruleID = context.ruleID;
73
- if (
74
- cds &&
75
- cds.model &&
76
- cds.model.err &&
77
- cds.model.err.message.startsWith("CDS compilation failed")
78
- ) {
79
- if (
80
- ruleID === "@sap/cds/cds-compile-error" ||
81
- ruleID === "cds-compile-error"
82
- ) {
83
- cds.model.err;
84
- return true;
85
- }
86
- }
87
- return false;
88
- },
89
68
  };