@sap/eslint-plugin-cds 2.3.2 → 2.3.3
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 +11 -50
- package/lib/api/index.js +11 -13
- package/lib/api/lint.d.ts +48 -0
- package/lib/constants.js +54 -0
- package/lib/index.js +44 -0
- package/lib/{impl/parser.js → parser.js} +2 -13
- package/lib/processor.js +47 -0
- package/lib/{impl/rules → rules}/assoc2many-ambiguous-key.js +50 -53
- package/lib/rules/latest-cds-version.js +42 -0
- package/lib/rules/min-node-version.js +47 -0
- package/lib/rules/no-db-keywords.js +46 -0
- package/lib/rules/no-dollar-prefixed-names.js +47 -0
- package/lib/{impl/rules → rules}/no-join-on-draft-enabled-entities.js +14 -11
- package/lib/rules/require-2many-oncond.js +27 -0
- package/lib/rules/sql-cast-suggestion.js +52 -0
- package/lib/rules/start-elements-lowercase.js +61 -0
- package/lib/rules/start-entities-uppercase.js +55 -0
- package/lib/{impl/rules → rules}/valid-csv-header.js +17 -9
- package/lib/{impl/utils → utils}/fuzzySearch.js +0 -0
- package/lib/utils/helpers.js +55 -0
- package/lib/{impl/utils → utils}/jsonc.js +0 -0
- package/lib/{impl/utils → utils}/model.js +107 -221
- package/lib/utils/ruleHelpers.js +56 -0
- package/lib/utils/ruleTester.js +79 -0
- package/lib/utils/rules.js +1033 -0
- package/lib/{impl/utils → utils}/validate.js +2 -18
- package/package.json +2 -2
- package/lib/impl/constants.js +0 -30
- package/lib/impl/index.js +0 -63
- package/lib/impl/processor.js +0 -23
- package/lib/impl/ruleFactory.js +0 -360
- package/lib/impl/rules/cds-compile-error.js +0 -34
- package/lib/impl/rules/latest-cds-version.js +0 -51
- package/lib/impl/rules/min-node-version.js +0 -44
- package/lib/impl/rules/no-db-keywords.js +0 -38
- package/lib/impl/rules/require-2many-oncond.js +0 -31
- package/lib/impl/rules/rule.hbs +0 -20
- package/lib/impl/rules/sql-cast-suggestion.js +0 -52
- package/lib/impl/rules/start-elements-lowercase.js +0 -75
- package/lib/impl/rules/start-entities-uppercase.js +0 -65
- package/lib/impl/types.d.ts +0 -48
- package/lib/impl/utils/helpers.js +0 -68
- package/lib/impl/utils/rules.js +0 -697
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,14 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
8
8
|
|
|
9
|
+
## [2.3.3] - 2022-03-24
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Added new rule `no-dollar-prefixed-names`
|
|
14
|
+
- Lint reports with rules marked with '!' notify of rule compile errors
|
|
15
|
+
- Lint reports of any thrown errors can be exposed by `--debug` (includes stack)
|
|
16
|
+
|
|
9
17
|
## [2.3.2] - 2022-01-24
|
|
10
18
|
|
|
11
19
|
### Changed
|
|
@@ -34,7 +42,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
34
42
|
- Added rule properties 'docs.recommended', 'severity'
|
|
35
43
|
|
|
36
44
|
## [2.2.2] - 2021-11-08
|
|
37
|
-
|
|
38
45
|
### Added
|
|
39
46
|
|
|
40
47
|
- Added new rule 'no-join-on-draft-enabled-entities'
|
|
@@ -45,13 +52,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
45
52
|
- Compile 'outsider' files based on CSN flavor 'parsed'
|
|
46
53
|
|
|
47
54
|
## [2.2.1] - 2021-11-01
|
|
48
|
-
|
|
49
55
|
### Changed
|
|
50
56
|
|
|
51
57
|
- Optimized model loading and fixed bug in loading of 'outsider' files
|
|
52
58
|
|
|
53
59
|
## [2.2.0] - 2021-10-29
|
|
54
|
-
|
|
55
60
|
### Added
|
|
56
61
|
|
|
57
62
|
- Added typings to javascript for all exposed apis
|
|
@@ -61,19 +66,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
61
66
|
- Aligned rule creation and tester api with ESLint
|
|
62
67
|
|
|
63
68
|
## [2.1.2] - 2021-10-05
|
|
64
|
-
|
|
65
69
|
### Changed
|
|
66
70
|
|
|
67
71
|
- Allow not only *.js but also other file types (i.e. *.ts, etc) to bypass plugin rules
|
|
68
72
|
|
|
69
73
|
## [2.1.1] - 2021-10-04
|
|
70
|
-
|
|
71
74
|
### Changed
|
|
72
75
|
|
|
73
76
|
- Added preprocessor to avoid (other plugins) parsing errors on cds files
|
|
74
77
|
|
|
75
78
|
## [2.1.0] - 2021-09-23
|
|
76
|
-
|
|
77
79
|
### Changed
|
|
78
80
|
|
|
79
81
|
- Source code is now Javascript only
|
|
@@ -82,8 +84,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
82
84
|
- Filter out lint messages when run from command line with custom formatter
|
|
83
85
|
|
|
84
86
|
## [2.0.5] - 2021-07-18
|
|
85
|
-
|
|
86
|
-
|
|
87
87
|
### Added
|
|
88
88
|
|
|
89
89
|
- When used from within VS Code ESLint exnteion, do not show environment rules
|
|
@@ -92,8 +92,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
92
92
|
- Rule 'min-node-version' reverted to use cds.resolve not cds.home
|
|
93
93
|
|
|
94
94
|
## [2.0.4] - 2021-07-12
|
|
95
|
-
|
|
96
|
-
|
|
97
95
|
### Added
|
|
98
96
|
|
|
99
97
|
- Plugin also considers 'outsider' files which are not part of a model
|
|
@@ -102,21 +100,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
102
100
|
- Custom formatter now prints projectPath for triggered env rule checks
|
|
103
101
|
|
|
104
102
|
## [2.0.3] - 20210-07-09
|
|
105
|
-
|
|
106
103
|
### Changed
|
|
107
104
|
|
|
108
105
|
- Removed `npm-shrinkwrap.json` file from package
|
|
109
106
|
|
|
110
107
|
## [2.0.2] - 2021-07-07
|
|
111
|
-
|
|
112
|
-
|
|
113
108
|
### Changed
|
|
114
109
|
|
|
115
110
|
- Fixed bug that always triggers csn-compile-err on type
|
|
116
111
|
|
|
117
112
|
## [2.0.1] - 2021-07-06
|
|
118
|
-
|
|
119
|
-
|
|
120
113
|
### Added
|
|
121
114
|
|
|
122
115
|
- Re-added model rule 'csn-compile-err' to pass csn compile errors to eslint for readability
|
|
@@ -126,8 +119,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
126
119
|
- Formatter ignores all lint reports for other file extensions except for those in plugin overrides files
|
|
127
120
|
|
|
128
121
|
## [2.0.0] - 2021-07-02
|
|
129
|
-
|
|
130
|
-
|
|
131
122
|
### Added
|
|
132
123
|
|
|
133
124
|
- New API: split exports into 'impl' (for eslint) and 'api' (for user)
|
|
@@ -138,15 +129,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
138
129
|
- API: getRuleTester now erquires relative path from ruleTester location to project root
|
|
139
130
|
|
|
140
131
|
## [1.1.7] - 2021-06-22
|
|
141
|
-
|
|
142
|
-
|
|
143
132
|
### Changed
|
|
144
133
|
|
|
145
134
|
- Load/Update model must be in sync with every 'on-type' event
|
|
146
135
|
|
|
147
136
|
## [1.1.6] - 2021-06-21
|
|
148
|
-
|
|
149
|
-
|
|
150
137
|
### Changed
|
|
151
138
|
|
|
152
139
|
- Formatter no longer has explicit dependencies, only reslies on 'stylish' output
|
|
@@ -155,26 +142,20 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
155
142
|
- On glob file expressions, gets multiple models in series and reports on all warnings/errors
|
|
156
143
|
|
|
157
144
|
## [1.1.5] - 2021-05-26
|
|
158
|
-
|
|
145
|
+
### Changed
|
|
159
146
|
|
|
160
147
|
- Naming convention rules changed to severity 'warning'
|
|
161
148
|
- Rules of type 'suggestion' must return 'fix' for appliable multipass fixes
|
|
162
149
|
- Split model generation into load and update to be able to work on all files
|
|
163
150
|
|
|
164
151
|
## [1.1.4] - 2021-05-20
|
|
165
|
-
|
|
166
|
-
|
|
167
152
|
### Changed
|
|
168
153
|
|
|
169
154
|
- Formatter does not show any (env/other) lint messages on model error
|
|
170
155
|
- Rule lower-camelcase-elements reverted to also check type keys
|
|
171
156
|
- Rule lower-camelcase-elements is not triggered by element 'ID' (see Bookshop in CAP samples)
|
|
172
157
|
|
|
173
|
-
|
|
174
158
|
## [1.1.3] - 2021-05-12
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
159
|
### Changed
|
|
179
160
|
|
|
180
161
|
- Changed rule type for naming convention rules to "suggestion"
|
|
@@ -182,16 +163,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
182
163
|
- Added extra layer to also support ruleTester "environment" checks
|
|
183
164
|
|
|
184
165
|
## [1.1.2] - 2021-05-05
|
|
185
|
-
|
|
186
|
-
|
|
187
166
|
### Changed
|
|
188
167
|
|
|
189
168
|
- Updated all rules to ingest args: (cds, context)
|
|
190
169
|
- Use context's sourcecode to get correct range indices for fixers
|
|
191
170
|
|
|
192
171
|
## [1.1.1] - 2021-05-04
|
|
193
|
-
|
|
194
|
-
|
|
195
172
|
### Changed
|
|
196
173
|
|
|
197
174
|
- Removed bulky headers from custom formatter
|
|
@@ -201,8 +178,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
201
178
|
- Fixed formatter to also print any other ESLint messages received
|
|
202
179
|
|
|
203
180
|
## [1.1.0] - 2021-04-29
|
|
204
|
-
|
|
205
|
-
|
|
206
181
|
### Added
|
|
207
182
|
|
|
208
183
|
- Custom cds formatter for separate reporting of env and model checks
|
|
@@ -211,25 +186,19 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
211
186
|
|
|
212
187
|
- Fixed version of model rule 'sql-cast-suggestion'
|
|
213
188
|
|
|
214
|
-
|
|
215
189
|
## [1.0.8] - 2021-04-12
|
|
216
|
-
|
|
217
|
-
|
|
218
190
|
### Added
|
|
219
191
|
|
|
220
192
|
- Proxy for cds object replaces fragile object clone from before
|
|
221
193
|
- Added proper typings and ignore where options should remain invisible to the (cds) api
|
|
222
194
|
- Added docstrings and header to each file to explain ESLint context
|
|
223
195
|
- Added model rule 'sql-cast-suggestion'
|
|
224
|
-
|
|
225
196
|
## [1.0.7] - 2021-04-01
|
|
226
|
-
|
|
227
|
-
### Fixed
|
|
197
|
+
### Changed
|
|
228
198
|
|
|
229
199
|
- Do not crash if `parserServices.cds` is not available
|
|
230
200
|
|
|
231
201
|
## [1.0.6] - 2021-04-01
|
|
232
|
-
|
|
233
202
|
### Added
|
|
234
203
|
|
|
235
204
|
- peer dependency to `eslint`
|
|
@@ -237,7 +206,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
237
206
|
- simplified api, cds instead of parserServices
|
|
238
207
|
|
|
239
208
|
## [1.0.4] - 2021-03-24
|
|
240
|
-
|
|
241
209
|
### Changed
|
|
242
210
|
|
|
243
211
|
- Added sync model load from cds to generate fully resolved models
|
|
@@ -246,25 +214,19 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
246
214
|
- Removed model rule 'no-entity-moo' and use as sample custom rule in docs
|
|
247
215
|
- Refactored and added more parserServices
|
|
248
216
|
|
|
249
|
-
|
|
250
217
|
## [1.0.3] - 2021-01-22
|
|
251
|
-
|
|
252
218
|
### Changed
|
|
253
219
|
|
|
254
220
|
- Fixed rule min-node-version to check if cds dependency is installed
|
|
255
221
|
- Updated README glob statement to double asterisk for check nested dirs
|
|
256
222
|
|
|
257
|
-
|
|
258
223
|
## [1.0.2] - 2021-01-21
|
|
259
|
-
|
|
260
224
|
### Changed
|
|
261
225
|
|
|
262
226
|
- Fixed rules to work in concert and allow for globs
|
|
263
227
|
- Improved README for better readability
|
|
264
228
|
|
|
265
|
-
|
|
266
229
|
## [1.0.1] - 2021-01-19
|
|
267
|
-
|
|
268
230
|
### Added
|
|
269
231
|
|
|
270
232
|
- Rule `assocs-card-flaw` in category *Model validation*
|
|
@@ -273,7 +235,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
273
235
|
|
|
274
236
|
- Refactoring of ruleFactory and parser code
|
|
275
237
|
|
|
276
|
-
|
|
277
238
|
## [1.0.0] - 2020-12-07
|
|
278
|
-
|
|
239
|
+
### Added
|
|
279
240
|
- Initial release 1.0.0
|
package/lib/api/index.js
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Our custom ESLint plugin API should:
|
|
3
|
-
* - Expose 'createRule'
|
|
4
|
-
* support the addition of *custom* CDS Lint rules
|
|
5
|
-
* - Expose 'getConfigPath', 'getFileExtensions', and 'genDocs' for usage in
|
|
5
|
+
* - Expose 'createRule' and 'runRuleTester' to
|
|
6
|
+
* support the addition of *custom* CDS Lint rules at runtime
|
|
7
|
+
* - Expose 'getConfigPath', 'getFileExtensions', and 'genDocs' for usage in
|
|
8
|
+
* 'cds lint' client (@sap/cds-dk)
|
|
6
9
|
* - Expose 'parserPath' for CDS Lint rule unit tests with ESLint's ruleTester
|
|
7
10
|
*/
|
|
8
11
|
|
|
9
|
-
const {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const { getConfigPath } = require("../impl/utils/model");
|
|
15
|
-
const { getFileExtensions } = require("../impl/utils/helpers");
|
|
16
|
-
const { genDocs } = require("../impl/utils/rules");
|
|
17
|
-
const parserPath = require.resolve("../impl/parser");
|
|
12
|
+
const { runRuleTester } = require("../utils/ruleTester")
|
|
13
|
+
const { getConfigPath } = require("../utils/model");
|
|
14
|
+
const { getFileExtensions } = require("../utils/helpers");
|
|
15
|
+
const { createRule, genDocs } = require("../utils/rules");
|
|
16
|
+
const parserPath = require.resolve("../parser");
|
|
18
17
|
|
|
19
18
|
module.exports = {
|
|
20
19
|
createRule,
|
|
21
|
-
defineRule,
|
|
22
20
|
runRuleTester,
|
|
23
21
|
getConfigPath,
|
|
24
22
|
getFileExtensions,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
|
|
2
|
+
import { Linter, Rule, RuleTester, SourceCode } from "eslint";
|
|
3
|
+
|
|
4
|
+
export interface CDSRuleContext extends Rule.RuleContext {
|
|
5
|
+
cds: any;
|
|
6
|
+
configPath: string;
|
|
7
|
+
code: string;
|
|
8
|
+
filePath: string;
|
|
9
|
+
options: [];
|
|
10
|
+
id: string;
|
|
11
|
+
sourcecode: SourceCode;
|
|
12
|
+
report: (CDSRuleReport) => void;
|
|
13
|
+
err: Error;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface CDSRuleSpec {
|
|
17
|
+
meta: CDSRuleMetaData,
|
|
18
|
+
create: (context: CDSRuleContext) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface CDSRuleMetaData extends Rule.RuleMetaData {
|
|
22
|
+
docs: Rule.RuleMetaData['docs'] & {
|
|
23
|
+
version: string;
|
|
24
|
+
};
|
|
25
|
+
severity?: Linter.RuleLevel;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type CDSRuleReport = Rule.ReportDescriptor & {
|
|
29
|
+
loc?: Rule.ReportDescriptorLocation;
|
|
30
|
+
file?: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export interface CDSTestCaseError extends RuleTester.TestCaseError {
|
|
34
|
+
message: string | RegExp;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface CDSRuleTestOpts {
|
|
38
|
+
/** specifies __dirname */
|
|
39
|
+
root: string;
|
|
40
|
+
/** requires your rule .js here */
|
|
41
|
+
rule?: string;
|
|
42
|
+
/** filename ('schema.cds' for model, 'package.json' for env) */
|
|
43
|
+
filename: string;
|
|
44
|
+
/** resolves cds parser path */
|
|
45
|
+
parser?: string;
|
|
46
|
+
/** list of errors from ESLint's [RuleTester](https://eslint.org/docs/developer-guide/nodejs-api#ruletester) */
|
|
47
|
+
errors: CDSTestCaseError[]
|
|
48
|
+
}
|
package/lib/constants.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is used to store/share any constants for this plugin:
|
|
3
|
+
* - RULE_CATEGORIES: List of plugin's rule categories (used purely as documentation labels)
|
|
4
|
+
* - DEFAULT_RULE_CATEGORY: Default rule category (must be one of plugin's rule categories)
|
|
5
|
+
* - DEFAULT_RULE_SEVERITY: Default rule severity (must be one of those define by ESLint):
|
|
6
|
+
* https://eslint.org/docs/user-guide/configuring/rules#configuring-rules
|
|
7
|
+
* - DEFAULT_RULE_TYPE: Default rule type (must be one of those defined by ESLint):
|
|
8
|
+
* https://eslint.org/docs/developer-guide/working-with-rules#rule-basics
|
|
9
|
+
* - CUSTOM_RULES_DIR: Custom rules directory name in the user's project home
|
|
10
|
+
* (contains rules, docs, tests)
|
|
11
|
+
* - FILES: Files/file extensions which ESLint should lint with this plugin
|
|
12
|
+
* - MODEL_FILES: Files/file extensions which should can be compiled (to CSN)
|
|
13
|
+
* - GLOBALS: Globals which should be exposed to ESLint by this plugin
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const RULE_CATEGORIES = ["Model Validation", "Environment"];
|
|
17
|
+
const DEFAULT_RULE_CATEGORY = RULE_CATEGORIES[0];
|
|
18
|
+
const DEFAULT_RULE_SEVERITY = "error";
|
|
19
|
+
const DEFAULT_RULE_TYPE = "problem";
|
|
20
|
+
|
|
21
|
+
const PLUGIN_NAME = require("../package.json").name;
|
|
22
|
+
const PLUGIN_PREFIX = "@sap/cds";
|
|
23
|
+
const PROCESSOR_NAME = `${PLUGIN_PREFIX}/cds`
|
|
24
|
+
const CUSTOM_RULES_DIR = ".cdslint";
|
|
25
|
+
|
|
26
|
+
const FILES = ["*.cds", "*.csn", "*.csv"];
|
|
27
|
+
const MODEL_FILES = ["*.cds", "*.csn"];
|
|
28
|
+
|
|
29
|
+
const GLOBALS = {
|
|
30
|
+
SELECT: true,
|
|
31
|
+
INSERT: true,
|
|
32
|
+
UPDATE: true,
|
|
33
|
+
DELETE: true,
|
|
34
|
+
CREATE: true,
|
|
35
|
+
DROP: true,
|
|
36
|
+
CDL: true,
|
|
37
|
+
CQL: true,
|
|
38
|
+
CXL: true,
|
|
39
|
+
cds: true,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
RULE_CATEGORIES,
|
|
44
|
+
DEFAULT_RULE_CATEGORY,
|
|
45
|
+
DEFAULT_RULE_SEVERITY,
|
|
46
|
+
DEFAULT_RULE_TYPE,
|
|
47
|
+
PLUGIN_NAME,
|
|
48
|
+
PLUGIN_PREFIX,
|
|
49
|
+
PROCESSOR_NAME,
|
|
50
|
+
CUSTOM_RULES_DIR,
|
|
51
|
+
FILES,
|
|
52
|
+
MODEL_FILES,
|
|
53
|
+
GLOBALS
|
|
54
|
+
};
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom ESLint plugin:
|
|
3
|
+
* https://eslint.org/docs/developer-guide/working-with-plugins
|
|
4
|
+
* This file exposes our plugins ESLint configuration, which must:
|
|
5
|
+
* - Expose any 'configs' for prescribed rule configuration bundles
|
|
6
|
+
* (i.e. "recommended"). See shareable configs:
|
|
7
|
+
* https://eslint.org/docs/developer-guide/shareable-configs
|
|
8
|
+
* - Expose any 'globals' for use in ESLint
|
|
9
|
+
* - Expose any 'processors' for use in ESLint
|
|
10
|
+
* - Expose any 'rules' for use in ESLint
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const path = require("path");
|
|
14
|
+
const processor = require("./processor");
|
|
15
|
+
|
|
16
|
+
const { FILES, GLOBALS, PLUGIN_NAME, PROCESSOR_NAME } = require("./constants");
|
|
17
|
+
const { getRules } = require("./utils/rules");
|
|
18
|
+
|
|
19
|
+
const rules = getRules(path.join(__dirname, "rules"));
|
|
20
|
+
|
|
21
|
+
function _getConfig(configName) {
|
|
22
|
+
return {
|
|
23
|
+
globals: GLOBALS,
|
|
24
|
+
plugins: [PLUGIN_NAME],
|
|
25
|
+
overrides: [
|
|
26
|
+
{
|
|
27
|
+
files: FILES,
|
|
28
|
+
processor: PROCESSOR_NAME,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
rules: rules[configName],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = {
|
|
36
|
+
configs: {
|
|
37
|
+
recommended: _getConfig("recommended"),
|
|
38
|
+
all: _getConfig("all"),
|
|
39
|
+
},
|
|
40
|
+
processors: {
|
|
41
|
+
cds: processor,
|
|
42
|
+
},
|
|
43
|
+
rules: rules.sources,
|
|
44
|
+
};
|
|
@@ -9,14 +9,7 @@
|
|
|
9
9
|
* (parserOptions). Note, that because we use a 'empty' preprocessor, the
|
|
10
10
|
* parser is only used by ESLint's ruleTester.
|
|
11
11
|
*/
|
|
12
|
-
|
|
13
|
-
const {
|
|
14
|
-
getAST,
|
|
15
|
-
getCDSProxy,
|
|
16
|
-
getLocation,
|
|
17
|
-
getRange,
|
|
18
|
-
} = require("../impl/utils/model");
|
|
19
|
-
const cds = require("@sap/cds");
|
|
12
|
+
const { getAST } = require("./utils/model");
|
|
20
13
|
|
|
21
14
|
module.exports = {
|
|
22
15
|
parse: function (code, options) {
|
|
@@ -24,13 +17,9 @@ module.exports = {
|
|
|
24
17
|
},
|
|
25
18
|
|
|
26
19
|
parseForESLint: function (code) {
|
|
27
|
-
cds.getLocation = getLocation;
|
|
28
|
-
cds.getRange = getRange;
|
|
29
20
|
return {
|
|
30
21
|
ast: getAST(code),
|
|
31
|
-
services: {
|
|
32
|
-
cdsProxy: getCDSProxy(cds),
|
|
33
|
-
},
|
|
22
|
+
services: {},
|
|
34
23
|
scopeManager: null,
|
|
35
24
|
tokensAndComments: [],
|
|
36
25
|
visitorKeys: null,
|
package/lib/processor.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom ESLint processor:
|
|
3
|
+
* https://eslint.org/docs/developer-guide/working-with-plugins#processors-in-plugins
|
|
4
|
+
* This processor is used to avoid parsing errors when this plugin is extended
|
|
5
|
+
* in ESLint alongside other plugins, such as prettier which then also try to
|
|
6
|
+
* read the new file types exposed via globs.
|
|
7
|
+
*
|
|
8
|
+
* Note, that because we cache the file contents and return files contents,
|
|
9
|
+
* the plugin's parser is bypassed so we must retrieve the file contents (see createRule()
|
|
10
|
+
* in utils/rules.js).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { Cache } = require("./utils/model");
|
|
14
|
+
const { isValidFile } = require("./utils/helpers");
|
|
15
|
+
|
|
16
|
+
module.exports = {
|
|
17
|
+
preprocess: function (text, filename) {
|
|
18
|
+
if (isValidFile(filename, 'FILES')) {
|
|
19
|
+
Cache.set(`file:${filename}`, text);
|
|
20
|
+
}
|
|
21
|
+
return [{ text: "", filename }];
|
|
22
|
+
},
|
|
23
|
+
/**
|
|
24
|
+
* Returns message objects as defined by ESLint:
|
|
25
|
+
* https://eslint.org/docs/developer-guide/working-with-custom-formatters#the-message-object
|
|
26
|
+
* @param {*} messages
|
|
27
|
+
* @returns
|
|
28
|
+
*/
|
|
29
|
+
postprocess: function (messages) {
|
|
30
|
+
const messagesSanitized = [];
|
|
31
|
+
messages.forEach(fileMessages => {
|
|
32
|
+
const fileMessagesSanitized = [];
|
|
33
|
+
fileMessages.forEach(r => {
|
|
34
|
+
if (r.message.startsWith(`CompilationError:`)) {
|
|
35
|
+
r.message = r.message.replace(`CompilationError: `,
|
|
36
|
+
'CDS model could not be compiled!\n');
|
|
37
|
+
r.ruleId = `❗${r.ruleId}`;
|
|
38
|
+
r.severity = 2;
|
|
39
|
+
}
|
|
40
|
+
fileMessagesSanitized.push(r);
|
|
41
|
+
})
|
|
42
|
+
messagesSanitized.push(fileMessagesSanitized);
|
|
43
|
+
})
|
|
44
|
+
return [].concat(...messagesSanitized);
|
|
45
|
+
},
|
|
46
|
+
supportsAutofix: true,
|
|
47
|
+
};
|
|
@@ -1,30 +1,36 @@
|
|
|
1
|
-
module.exports =
|
|
1
|
+
module.exports = {
|
|
2
2
|
meta: {
|
|
3
3
|
docs: {
|
|
4
|
-
description:
|
|
4
|
+
description:
|
|
5
|
+
"Ambiguous key with a `TO MANY` relationship since entries could appear multiple times with the same key.",
|
|
5
6
|
category: "Model Validation",
|
|
6
7
|
recommended: true,
|
|
7
8
|
version: "1.0.1",
|
|
8
9
|
},
|
|
9
10
|
severity: "warn",
|
|
10
|
-
type: "problem"
|
|
11
|
+
type: "problem",
|
|
11
12
|
},
|
|
12
13
|
create(context) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
return { all: check_assoc2many_ambiguous_key };
|
|
15
|
+
|
|
16
|
+
function check_assoc2many_ambiguous_key() {
|
|
17
|
+
let reports = [];
|
|
18
|
+
let csnOdata;
|
|
19
|
+
const m = context.cds.model;
|
|
20
|
+
if (m && m.definitions) {
|
|
17
21
|
csnOdata = context.cds.compile.for.odata(m);
|
|
18
22
|
const csnOdataLinked = context.cds.linked(csnOdata);
|
|
19
|
-
associationCardinalityFlaw(csnOdataLinked, context);
|
|
20
|
-
}
|
|
21
|
-
|
|
23
|
+
reports = associationCardinalityFlaw(csnOdataLinked, context);
|
|
24
|
+
}
|
|
25
|
+
if (reports.length > 0) {
|
|
26
|
+
return reports;
|
|
22
27
|
}
|
|
23
28
|
}
|
|
24
29
|
},
|
|
25
|
-
}
|
|
30
|
+
};
|
|
26
31
|
|
|
27
32
|
function associationCardinalityFlaw(csn, context) {
|
|
33
|
+
const reports = [];
|
|
28
34
|
processEntity(csn, (definition, sourceEntity, sourceAlias) => {
|
|
29
35
|
let refCardinalityMult = false;
|
|
30
36
|
let refPlainElement = false;
|
|
@@ -38,10 +44,7 @@ function associationCardinalityFlaw(csn, context) {
|
|
|
38
44
|
refPlainElement = false;
|
|
39
45
|
},
|
|
40
46
|
(refEntity, refElement) => {
|
|
41
|
-
if (
|
|
42
|
-
refElement.type === "cds.Association" ||
|
|
43
|
-
refElement.type === "cds.Composition"
|
|
44
|
-
) {
|
|
47
|
+
if (refElement.type === "cds.Association" || refElement.type === "cds.Composition") {
|
|
45
48
|
if (refElement.cardinality && refElement.cardinality.max === "*") {
|
|
46
49
|
refCardinalityMult = true;
|
|
47
50
|
}
|
|
@@ -57,20 +60,35 @@ function associationCardinalityFlaw(csn, context) {
|
|
|
57
60
|
refCardinalityMult &&
|
|
58
61
|
refPlainElement
|
|
59
62
|
) {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
const keyName = Object.keys(definition.keys)[0];
|
|
64
|
+
const key = definition.keys[keyName];
|
|
65
|
+
const keyLoc = context.cds.getLocation(keyName, key, csn);
|
|
66
|
+
const colName = column.as ? column.as : column.name;
|
|
67
|
+
if (context.sourcecode.lines[column.$location.line - 1]) {
|
|
68
|
+
const endCol = context.sourcecode.lines[column.$location.col - 1].length;
|
|
69
|
+
const colLoc = {
|
|
70
|
+
start: { line: column.$location.line, column: column.$location.col - 1 },
|
|
71
|
+
end: { line: column.$location.line, column: endCol },
|
|
72
|
+
};
|
|
73
|
+
const message = `Ambiguous key in '${definition.name}'. Element '${colName}' leads to multiple entries so that key '${keyName}' is not unique.`;
|
|
74
|
+
reports.push(
|
|
75
|
+
{
|
|
76
|
+
message,
|
|
77
|
+
loc: keyLoc,
|
|
78
|
+
file: key.$location.file,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
message,
|
|
82
|
+
loc: colLoc,
|
|
83
|
+
file: column.$location.file,
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
}
|
|
70
87
|
}
|
|
71
88
|
}
|
|
72
89
|
);
|
|
73
90
|
});
|
|
91
|
+
return reports;
|
|
74
92
|
}
|
|
75
93
|
|
|
76
94
|
function processEntity(csn, eachCallback) {
|
|
@@ -89,21 +107,14 @@ function processEntity(csn, eachCallback) {
|
|
|
89
107
|
const sourceAlias = [];
|
|
90
108
|
if (definition.query.SELECT.from.ref) {
|
|
91
109
|
// From
|
|
92
|
-
sourceEntity =
|
|
93
|
-
csn.definitions[definition.query.SELECT.from.ref.join("_")];
|
|
110
|
+
sourceEntity = csn.definitions[definition.query.SELECT.from.ref.join("_")];
|
|
94
111
|
sourceAlias.push({
|
|
95
112
|
from: sourceEntity.name,
|
|
96
|
-
as:
|
|
97
|
-
definition.query.SELECT.from.as ||
|
|
98
|
-
definition.query.SELECT.from.ref.slice(-1)[0].split(".").pop(),
|
|
113
|
+
as: definition.query.SELECT.from.as || definition.query.SELECT.from.ref.slice(-1)[0].split(".").pop(),
|
|
99
114
|
});
|
|
100
|
-
} else if (
|
|
101
|
-
definition.query.SELECT.from.args &&
|
|
102
|
-
definition.query.SELECT.from.args[0].ref
|
|
103
|
-
) {
|
|
115
|
+
} else if (definition.query.SELECT.from.args && definition.query.SELECT.from.args[0].ref) {
|
|
104
116
|
// Join
|
|
105
|
-
sourceEntity =
|
|
106
|
-
csn.definitions[definition.query.SELECT.from.args[0].ref.join("_")];
|
|
117
|
+
sourceEntity = csn.definitions[definition.query.SELECT.from.args[0].ref.join("_")];
|
|
107
118
|
definition.query.SELECT.from.args.forEach((arg) => {
|
|
108
119
|
sourceAlias.push({
|
|
109
120
|
from: arg.ref.join("_"),
|
|
@@ -119,15 +130,7 @@ function processEntity(csn, eachCallback) {
|
|
|
119
130
|
});
|
|
120
131
|
}
|
|
121
132
|
|
|
122
|
-
function processElement(
|
|
123
|
-
csn,
|
|
124
|
-
definition,
|
|
125
|
-
sourceEntity,
|
|
126
|
-
sourceAlias,
|
|
127
|
-
beforeCallback,
|
|
128
|
-
eachCallback,
|
|
129
|
-
afterCallback
|
|
130
|
-
) {
|
|
133
|
+
function processElement(csn, definition, sourceEntity, sourceAlias, beforeCallback, eachCallback, afterCallback) {
|
|
131
134
|
definition.query.SELECT.columns.forEach((column) => {
|
|
132
135
|
if (column.ref && column.ref.length > 1) {
|
|
133
136
|
let refEntity = sourceEntity;
|
|
@@ -150,18 +153,12 @@ function processElement(
|
|
|
150
153
|
if (!refElement && definition.query.SELECT.mixin) {
|
|
151
154
|
refElement = definition.query.SELECT.mixin[ref];
|
|
152
155
|
if (!refElement && definition.query.SELECT.mixin[column.ref[0]]) {
|
|
153
|
-
refElement =
|
|
154
|
-
definition.query.SELECT.mixin[column.ref[0]]._target.elements[
|
|
155
|
-
ref
|
|
156
|
-
];
|
|
156
|
+
refElement = definition.query.SELECT.mixin[column.ref[0]]._target.elements[ref];
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
eachCallback(refEntity, refElement);
|
|
161
|
-
if (
|
|
162
|
-
refElement.type === "cds.Association" ||
|
|
163
|
-
refElement.type === "cds.Composition"
|
|
164
|
-
) {
|
|
161
|
+
if (refElement.type === "cds.Association" || refElement.type === "cds.Composition") {
|
|
165
162
|
refEntity = csn.definitions[refElement.target];
|
|
166
163
|
}
|
|
167
164
|
}
|