@sap/eslint-plugin-cds 2.1.1 → 2.2.1
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 +56 -0
- package/README.md +1 -1
- package/lib/api/formatter.js +23 -45
- package/lib/api/index.js +17 -7
- package/lib/impl/constants.js +3 -5
- package/lib/impl/index.js +30 -18
- package/lib/impl/parser.js +12 -4
- package/lib/impl/processor.js +17 -6
- package/lib/impl/ruleFactory.js +309 -359
- package/lib/impl/rules/assoc2many-ambiguous-key.js +137 -135
- package/lib/impl/rules/cds-compile-error.js +26 -37
- package/lib/impl/rules/latest-cds-version.js +33 -41
- package/lib/impl/rules/min-node-version.js +32 -44
- package/lib/impl/rules/no-db-keywords.js +1 -1
- package/lib/impl/rules/require-2many-oncond.js +21 -31
- package/lib/impl/rules/rule.hbs +12 -25
- package/lib/impl/rules/sql-cast-suggestion.js +35 -42
- package/lib/impl/rules/start-elements-lowercase.js +51 -54
- package/lib/impl/rules/start-entities-uppercase.js +38 -57
- package/lib/impl/types.d.ts +48 -0
- package/lib/impl/utils/helpers.js +46 -2
- package/lib/impl/utils/model.js +483 -404
- package/lib/impl/utils/rules.js +218 -134
- package/lib/impl/utils/validate.js +56 -0
- package/package.json +1 -1
- package/lib/impl/rules/index.js +0 -5
- package/lib/impl/rules/test.hbs +0 -10
package/lib/impl/utils/rules.js
CHANGED
|
@@ -1,25 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef { import('eslint').Linter.ConfigOverride.files } ConfigOverrideFiles
|
|
3
|
+
*/
|
|
4
|
+
|
|
1
5
|
const fs = require("fs");
|
|
2
6
|
const path = require("path");
|
|
3
7
|
const { mkdirp } = require("@sap/cds/lib/utils");
|
|
4
8
|
const { getLastLine } = require("./model");
|
|
5
9
|
|
|
6
10
|
const JSONC = require("./jsonc");
|
|
7
|
-
const
|
|
8
|
-
const REGEX_COMMENT_START = "(/\\*|(.+)?//)\\s+eslint-";
|
|
11
|
+
const { categories, files, recommended } = require("../constants");
|
|
12
|
+
const REGEX_COMMENT_START = "(/\\*|(.+)?//)(\\s?)+eslint-";
|
|
9
13
|
const REGEX_COMMENTS = `${REGEX_COMMENT_START}(enable|disable)(-next)?(-line)?(.+)?`;
|
|
10
14
|
|
|
11
15
|
module.exports = {
|
|
12
16
|
/**
|
|
13
17
|
* Returns an array of allowed file extensions
|
|
14
18
|
* the plugin can parse of the form "*.ext"
|
|
15
|
-
* @returns Array of file extensions
|
|
19
|
+
* @returns {ConfigOverrideFiles} Array of file extensions
|
|
16
20
|
*/
|
|
17
21
|
getFileExtensions: function () {
|
|
18
|
-
return
|
|
22
|
+
return files;
|
|
19
23
|
},
|
|
20
24
|
|
|
21
25
|
/**
|
|
22
|
-
* Turns rules on
|
|
26
|
+
* Turns rules "on" or "off" for given line according to eslint-disable
|
|
27
|
+
* comments:
|
|
23
28
|
* 1. Reads code string and extracts a list of comments (in order)
|
|
24
29
|
* 2. Initiates rulesDisabled array with all rules "on" by default
|
|
25
30
|
* 3. Switches rules "off" (or "on" again) based on disable comment
|
|
@@ -28,92 +33,152 @@ module.exports = {
|
|
|
28
33
|
* @param line current code line to analyze
|
|
29
34
|
* @returns rules dictionary with rules being either 'on' and 'off'
|
|
30
35
|
*/
|
|
31
|
-
|
|
36
|
+
getDisabled: function (
|
|
37
|
+
rules,
|
|
38
|
+
listEnvRules,
|
|
39
|
+
listModelRules,
|
|
40
|
+
code,
|
|
41
|
+
sourcecode,
|
|
42
|
+
line
|
|
43
|
+
) {
|
|
32
44
|
const listDisabled = [];
|
|
33
45
|
const rulesDisabled = rules.reduce((o, key) => ({ ...o, [key]: "on" }), {});
|
|
34
|
-
|
|
35
|
-
if (
|
|
36
|
-
matches.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
match
|
|
40
|
-
|
|
41
|
-
match = match
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
["disable", "enable"].forEach((keyword) => {
|
|
47
|
-
const loc = sourcecode.getLocFromIndex(index);
|
|
48
|
-
const disableType = match.split(" ")[0];
|
|
49
|
-
let disableRules = match.split(`${disableType} `)[1];
|
|
50
|
-
if (disableRules) {
|
|
51
|
-
disableRules = disableRules.split(",").map((rule) => rule.trim());
|
|
52
|
-
} else {
|
|
53
|
-
const envRules = module.exports.getEnvRules(path.join(__dirname, "../rules"));
|
|
54
|
-
const modelRules = module.exports.getModelRules(path.join(__dirname, "../rules"));
|
|
55
|
-
disableRules = envRules.concat(modelRules).map((rule) => `@sap/cds/${rule}`);
|
|
46
|
+
let matches = [];
|
|
47
|
+
if (code) {
|
|
48
|
+
matches = [...code.matchAll(REGEX_COMMENTS)];
|
|
49
|
+
if (matches.length > 0) {
|
|
50
|
+
matches.forEach((match) => {
|
|
51
|
+
if (match) {
|
|
52
|
+
const index = match.index;
|
|
53
|
+
match = match[0];
|
|
54
|
+
if (match.includes("*/")) {
|
|
55
|
+
match = match.split("*/")[0].replace("/*", "");
|
|
56
|
+
} else if (match.includes("//")) {
|
|
57
|
+
match = match.split("//")[1];
|
|
56
58
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
lineDisabled: loc.line + 1,
|
|
69
|
-
rules: disableRules,
|
|
70
|
-
type: keyword,
|
|
71
|
-
};
|
|
59
|
+
if (match) {
|
|
60
|
+
match = match.trim();
|
|
61
|
+
}
|
|
62
|
+
["disable", "enable"].forEach((keyword) => {
|
|
63
|
+
const loc = sourcecode.getLocFromIndex(index);
|
|
64
|
+
const disableType = match.split(" ")[0];
|
|
65
|
+
let disableRules = match.split(`${disableType} `)[1];
|
|
66
|
+
if (disableRules) {
|
|
67
|
+
disableRules = disableRules
|
|
68
|
+
.split(",")
|
|
69
|
+
.map((rule) => rule.trim());
|
|
72
70
|
} else {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
rules: disableRules,
|
|
77
|
-
type: keyword,
|
|
78
|
-
};
|
|
71
|
+
disableRules = listEnvRules
|
|
72
|
+
.concat(listModelRules)
|
|
73
|
+
.map((rule) => `@sap/cds/${rule}`);
|
|
79
74
|
}
|
|
80
|
-
|
|
81
|
-
|
|
75
|
+
let comment = {};
|
|
76
|
+
if (
|
|
77
|
+
[
|
|
78
|
+
`eslint-${keyword}`,
|
|
79
|
+
`eslint-${keyword}-line`,
|
|
80
|
+
`eslint-${keyword}-next-line`,
|
|
81
|
+
].includes(disableType)
|
|
82
|
+
) {
|
|
83
|
+
if (disableType.includes("-next-line")) {
|
|
84
|
+
comment = {
|
|
85
|
+
lineComment: loc.line,
|
|
86
|
+
lineDisabled: loc.line + 1,
|
|
87
|
+
rules: disableRules,
|
|
88
|
+
type: keyword,
|
|
89
|
+
};
|
|
90
|
+
} else {
|
|
91
|
+
comment = {
|
|
92
|
+
lineComment: loc.line,
|
|
93
|
+
lineDisabled: loc.line,
|
|
94
|
+
rules: disableRules,
|
|
95
|
+
type: keyword,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (!disableType.includes("-line")) {
|
|
99
|
+
comment.lineDisabled = "EOF";
|
|
100
|
+
}
|
|
82
101
|
}
|
|
102
|
+
listDisabled.push(comment);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
for (let i = 0; i <= listDisabled.length - 1; i++) {
|
|
107
|
+
if (listDisabled[i].lineComment > line) {
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
if (
|
|
111
|
+
listDisabled[i].lineDisabled === "EOF" ||
|
|
112
|
+
listDisabled[i].lineDisabled === line
|
|
113
|
+
) {
|
|
114
|
+
if (listDisabled[i].lineDisabled === "EOF") {
|
|
115
|
+
listDisabled[i].lineDisabled = getLastLine(code);
|
|
83
116
|
}
|
|
84
|
-
listDisabled.
|
|
85
|
-
|
|
117
|
+
if (listDisabled[i].rules) {
|
|
118
|
+
listDisabled[i].rules.forEach((rule) => {
|
|
119
|
+
if (listDisabled[i].type === "disable") {
|
|
120
|
+
rulesDisabled[rule] = "off";
|
|
121
|
+
} else if (listDisabled[i].type === "enable") {
|
|
122
|
+
rulesDisabled[rule] = "on";
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
86
127
|
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return rulesDisabled;
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Checks whether a given lint error/warning has been disabled
|
|
135
|
+
* by comments (as it should then be suppressed)
|
|
136
|
+
* @param entry lint report
|
|
137
|
+
* @param cdscontext cds context object
|
|
138
|
+
* @param rules all availabe rules
|
|
139
|
+
* @returns boolean
|
|
140
|
+
*/
|
|
141
|
+
|
|
142
|
+
checkDisabled: function (
|
|
143
|
+
entry,
|
|
144
|
+
cdscontext,
|
|
145
|
+
rules,
|
|
146
|
+
listEnvRules,
|
|
147
|
+
listModelRules
|
|
148
|
+
) {
|
|
149
|
+
let isDisabled = false;
|
|
150
|
+
if (entry.loc && entry.loc.start) {
|
|
151
|
+
const line = entry.loc.start.line;
|
|
152
|
+
if (cdscontext) {
|
|
153
|
+
const rulesDisabled = module.exports.getDisabled(
|
|
154
|
+
rules,
|
|
155
|
+
listEnvRules,
|
|
156
|
+
listModelRules,
|
|
157
|
+
cdscontext.code,
|
|
158
|
+
cdscontext.sourcecode,
|
|
159
|
+
line
|
|
160
|
+
);
|
|
161
|
+
let ruleID = cdscontext.ruleID;
|
|
162
|
+
if (!ruleID) {
|
|
163
|
+
ruleID = cdscontext.id;
|
|
91
164
|
}
|
|
92
165
|
if (
|
|
93
|
-
|
|
94
|
-
|
|
166
|
+
line &&
|
|
167
|
+
ruleID in rulesDisabled &&
|
|
168
|
+
rulesDisabled[ruleID] === "off"
|
|
95
169
|
) {
|
|
96
|
-
|
|
97
|
-
listDisabled[i].lineDisabled = getLastLine(code);
|
|
98
|
-
}
|
|
99
|
-
if (listDisabled[i].rules) {
|
|
100
|
-
listDisabled[i].rules.forEach((rule) => {
|
|
101
|
-
if (listDisabled[i].type === "disable") {
|
|
102
|
-
rulesDisabled[rule] = "off";
|
|
103
|
-
} else if (listDisabled[i].type === "enable") {
|
|
104
|
-
rulesDisabled[rule] = "on";
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
}
|
|
170
|
+
isDisabled = true;
|
|
108
171
|
}
|
|
109
172
|
}
|
|
110
173
|
}
|
|
111
|
-
return
|
|
174
|
+
return isDisabled;
|
|
112
175
|
},
|
|
176
|
+
|
|
113
177
|
/**
|
|
114
|
-
* Gets value for a given key in allowed keys of ESLint's meta data in
|
|
115
|
-
*
|
|
116
|
-
* @param {
|
|
178
|
+
* Gets value for a given key in allowed keys of ESLint's meta data in
|
|
179
|
+
* defineRule api
|
|
180
|
+
* @param {string} text meta object from rule
|
|
181
|
+
* @param {string} key key to get value for
|
|
117
182
|
* @returns Value for given key
|
|
118
183
|
*/
|
|
119
184
|
getKeyFromMeta: function (text, key) {
|
|
@@ -122,7 +187,7 @@ module.exports = {
|
|
|
122
187
|
if (matchQuote) {
|
|
123
188
|
const quote = matchQuote[0].slice(-1);
|
|
124
189
|
const exprStart = `${key}:[\\s]+\\${quote}`;
|
|
125
|
-
const exprEnd = `(\\${quote}
|
|
190
|
+
const exprEnd = `(\\${quote},?)`;
|
|
126
191
|
const regexKey = new RegExp(`${exprStart}[\\s\\S]*?${exprEnd}`, "gm");
|
|
127
192
|
const matchKey = regexKey.exec(text);
|
|
128
193
|
if (matchKey) {
|
|
@@ -138,14 +203,23 @@ module.exports = {
|
|
|
138
203
|
return "";
|
|
139
204
|
}
|
|
140
205
|
} else {
|
|
206
|
+
const regexBoolean = new RegExp(`${key}:[\\s]+true[\\s]?,`, "gm");
|
|
207
|
+
const matchBoolean = regexBoolean.exec(text);
|
|
208
|
+
if (matchBoolean) {
|
|
209
|
+
if (matchBoolean[0].includes("true,")) {
|
|
210
|
+
return true;
|
|
211
|
+
} else {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
141
215
|
return "";
|
|
142
216
|
}
|
|
143
217
|
},
|
|
144
218
|
|
|
145
219
|
/**
|
|
146
220
|
* Gets value for a given key in allowed keys for input of runRuleTester api
|
|
147
|
-
* @param {
|
|
148
|
-
* @param {
|
|
221
|
+
* @param {string} text test input for ruleTester
|
|
222
|
+
* @param {string} key key to get value for
|
|
149
223
|
* @returns Value for given key
|
|
150
224
|
*/
|
|
151
225
|
getKeyFromTest: function (text, key) {
|
|
@@ -180,9 +254,9 @@ module.exports = {
|
|
|
180
254
|
|
|
181
255
|
/**
|
|
182
256
|
* Generates overview table of all rules based on rule dictionary.
|
|
183
|
-
* @param
|
|
184
|
-
* @param
|
|
185
|
-
* @param
|
|
257
|
+
* @param ruleDict
|
|
258
|
+
* @param release
|
|
259
|
+
* @param table
|
|
186
260
|
* @returns Markdown table
|
|
187
261
|
*/
|
|
188
262
|
genMdRules: function (ruleDict, release, table = true) {
|
|
@@ -192,14 +266,19 @@ module.exports = {
|
|
|
192
266
|
}
|
|
193
267
|
let mdRules = `# @sap/eslint-plugin-cds [${version}]\n\n`;
|
|
194
268
|
if (table) {
|
|
195
|
-
mdRules += "
|
|
196
|
-
mdRules += "
|
|
197
|
-
|
|
269
|
+
mdRules += "| | | | | |\n";
|
|
270
|
+
mdRules += "|:-:|:-:|:-:|:-:|:-|\n";
|
|
271
|
+
/* eslint-disable-next-line no-unused-vars */
|
|
272
|
+
Object.entries(ruleDict).forEach(([, rules]) => {
|
|
198
273
|
rules.forEach(function (rule) {
|
|
199
|
-
|
|
274
|
+
if (release) {
|
|
275
|
+
mdRules += `| ${rule.recommended} | ${rule.fixable} | ${rule.hasSuggestions} | [${rule.name}](Rules-released.md#rule-${rule.name}) | ${rule.details}|\n`;
|
|
276
|
+
} else {
|
|
277
|
+
mdRules += `| ${rule.recommended} | ${rule.fixable} | ${rule.hasSuggestions} | [${rule.name}](Rules.md#rule-${rule.name}) | ${rule.details}|\n`;
|
|
278
|
+
}
|
|
200
279
|
});
|
|
201
280
|
});
|
|
202
|
-
mdRules += "
|
|
281
|
+
mdRules += "\n";
|
|
203
282
|
}
|
|
204
283
|
return mdRules;
|
|
205
284
|
},
|
|
@@ -211,9 +290,9 @@ module.exports = {
|
|
|
211
290
|
* If used internally within the @sap/eslint-plugin-cds, this
|
|
212
291
|
* also generates 'released' files, which only contain information
|
|
213
292
|
* on rules published until the currently released version.
|
|
214
|
-
* @param
|
|
215
|
-
* @param
|
|
216
|
-
* @param
|
|
293
|
+
* @param ruleDict
|
|
294
|
+
* @param docsPath
|
|
295
|
+
* @param release
|
|
217
296
|
*/
|
|
218
297
|
genDocFiles: function (ruleDict, docsPath, release) {
|
|
219
298
|
let suffix = "";
|
|
@@ -252,7 +331,8 @@ module.exports = {
|
|
|
252
331
|
* for user according to contents of:
|
|
253
332
|
* - Rule files
|
|
254
333
|
* - Test files (with valid/invalid/fixed examples)
|
|
255
|
-
* @param {
|
|
334
|
+
* @param {string} projectPath
|
|
335
|
+
* @param {string} customRulesDir
|
|
256
336
|
*/
|
|
257
337
|
async genDocs(projectPath, customRulesDir) {
|
|
258
338
|
let mdRule, mdRuleSources, mdRuleContents;
|
|
@@ -296,6 +376,10 @@ module.exports = {
|
|
|
296
376
|
);
|
|
297
377
|
const category = module.exports.getKeyFromMeta(ruleMeta, "category");
|
|
298
378
|
const fixable = module.exports.getKeyFromMeta(ruleMeta, "fixable");
|
|
379
|
+
const suggestions = module.exports.getKeyFromMeta(
|
|
380
|
+
ruleMeta,
|
|
381
|
+
"hasSuggestions"
|
|
382
|
+
);
|
|
299
383
|
|
|
300
384
|
let isFixable = "";
|
|
301
385
|
if (["code", "whitespace"].includes(fixable)) {
|
|
@@ -303,10 +387,15 @@ module.exports = {
|
|
|
303
387
|
}
|
|
304
388
|
|
|
305
389
|
let isRecommended = "";
|
|
306
|
-
if (Object.keys(
|
|
390
|
+
if (Object.keys(recommended).includes(`@sap/cds/${rule}`)) {
|
|
307
391
|
isRecommended = "✔️";
|
|
308
392
|
}
|
|
309
393
|
|
|
394
|
+
let hasSuggestions = "";
|
|
395
|
+
if (suggestions === true) {
|
|
396
|
+
hasSuggestions = "💡";
|
|
397
|
+
}
|
|
398
|
+
|
|
310
399
|
const version = module.exports.getKeyFromMeta(ruleMeta, "version");
|
|
311
400
|
|
|
312
401
|
// Get rule valid/invalid tests
|
|
@@ -338,6 +427,9 @@ module.exports = {
|
|
|
338
427
|
const insertAt = (str, sub, pos) =>
|
|
339
428
|
`${str.slice(0, pos)}${sub}${str.slice(pos)}`;
|
|
340
429
|
errors.forEach((err) => {
|
|
430
|
+
if (err.messageId) {
|
|
431
|
+
err.message = err.messageId;
|
|
432
|
+
}
|
|
341
433
|
const msg = err.message.replace(/"/gm, "`");
|
|
342
434
|
if (err.line) {
|
|
343
435
|
const code = invalid.split("\n");
|
|
@@ -365,24 +457,24 @@ module.exports = {
|
|
|
365
457
|
`code for this rule:</span>\n\n<pre><code>\n${invalid}\n</code></pre>`;
|
|
366
458
|
}
|
|
367
459
|
|
|
368
|
-
mdRuleContents = `##
|
|
460
|
+
mdRuleContents = `## ${rule}\n`;
|
|
369
461
|
mdRuleContents += `<span class='label shifted'>${category}</span>\n\n`;
|
|
370
462
|
mdRuleContents += `### Rule Details\n${details}\n\n`;
|
|
371
463
|
if (mdRule) {
|
|
372
464
|
mdRuleContents += `### Examples\n${mdRule}\n\n`;
|
|
373
465
|
}
|
|
374
466
|
mdRuleContents += `### Version\nThis rule was introduced in \`@sap/eslint-plugin-cds ${version}\`.\n\n`;
|
|
375
|
-
mdRuleSources = `### Resources\n[Rule & Documentation source](${path
|
|
376
|
-
docsPath,
|
|
377
|
-
|
|
378
|
-
).replace(/\\/g,'/')})\n\n`;
|
|
467
|
+
mdRuleSources = `### Resources\n[Rule & Documentation source](${path
|
|
468
|
+
.relative(docsPath, path.join(rulePath, `${rule}.js`))
|
|
469
|
+
.replace(/\\/g, "/")})\n\n`;
|
|
379
470
|
|
|
380
471
|
if (Object.keys(ruleDict).includes(category)) {
|
|
381
472
|
ruleDict[category].push({
|
|
382
473
|
name: rule,
|
|
383
474
|
details,
|
|
384
475
|
recommended: isRecommended,
|
|
385
|
-
|
|
476
|
+
fixable: isFixable,
|
|
477
|
+
hasSuggestions,
|
|
386
478
|
version: version,
|
|
387
479
|
contents: mdRuleContents,
|
|
388
480
|
sources: mdRuleSources,
|
|
@@ -393,7 +485,8 @@ module.exports = {
|
|
|
393
485
|
name: rule,
|
|
394
486
|
details,
|
|
395
487
|
recommended: isRecommended,
|
|
396
|
-
|
|
488
|
+
fixable: isFixable,
|
|
489
|
+
hasSuggestions,
|
|
397
490
|
version: version,
|
|
398
491
|
contents: mdRuleContents,
|
|
399
492
|
sources: mdRuleSources,
|
|
@@ -410,48 +503,39 @@ module.exports = {
|
|
|
410
503
|
}
|
|
411
504
|
},
|
|
412
505
|
|
|
413
|
-
|
|
506
|
+
/**
|
|
507
|
+
* Gets all relevant rules information (dictionary of rules contents, lists
|
|
508
|
+
* of 'env' and 'model' rules) for the rules directory provided
|
|
509
|
+
* - Default categories is 'model'
|
|
510
|
+
* @param {string} dirname
|
|
511
|
+
* @returns rules information
|
|
512
|
+
*/
|
|
513
|
+
getRules(dirname) {
|
|
414
514
|
const rules = {};
|
|
515
|
+
const listEnvRules = [];
|
|
516
|
+
const listModelRules = [];
|
|
415
517
|
fs.readdirSync(dirname).forEach((file) => {
|
|
416
518
|
if (path.extname(file) === ".js" && file !== "index.js") {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
getEnvRules: function (dirname) {
|
|
429
|
-
const envRules = [];
|
|
430
|
-
fs.readdirSync(dirname).forEach((file) => {
|
|
431
|
-
if (path.extname(file) === ".js" && file !== "index.js") {
|
|
432
|
-
const rulePath = path.join(dirname, file);
|
|
433
|
-
const ruleMeta = fs.readFileSync(rulePath, "utf8");
|
|
434
|
-
const category = module.exports.getKeyFromMeta(ruleMeta, "category");
|
|
435
|
-
if (category === "Environment") {
|
|
436
|
-
envRules.push(file.replace(".js", ""));
|
|
519
|
+
const rulename = file.replace(".js", "");
|
|
520
|
+
const ruleID = `${rulename}`;
|
|
521
|
+
rules[ruleID] = require(path.join(dirname, file));
|
|
522
|
+
const category =
|
|
523
|
+
rules[ruleID].meta.docs.category || categories["model"];
|
|
524
|
+
if (
|
|
525
|
+
!listEnvRules.includes(rulename) &&
|
|
526
|
+
category === categories["env"]
|
|
527
|
+
) {
|
|
528
|
+
listEnvRules.push(rulename);
|
|
437
529
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
getModelRules: function (dirname) {
|
|
444
|
-
const modelRules = [];
|
|
445
|
-
fs.readdirSync(dirname).forEach((file) => {
|
|
446
|
-
if (path.extname(file) === ".js" && file !== "index.js") {
|
|
447
|
-
const rulePath = path.join(dirname, file);
|
|
448
|
-
const ruleMeta = fs.readFileSync(rulePath, "utf8");
|
|
449
|
-
const category = module.exports.getKeyFromMeta(ruleMeta, "category");
|
|
450
|
-
if (category === CONSTANTS.categories.model) {
|
|
451
|
-
modelRules.push(file.replace(".js", ""));
|
|
530
|
+
if (
|
|
531
|
+
!listModelRules.includes(rulename) &&
|
|
532
|
+
category === categories["model"]
|
|
533
|
+
) {
|
|
534
|
+
listModelRules.push(rulename);
|
|
452
535
|
}
|
|
453
536
|
}
|
|
454
537
|
});
|
|
455
|
-
|
|
538
|
+
const listRules = listEnvRules.concat(listModelRules);
|
|
539
|
+
return { rules, listRules, listEnvRules, listModelRules };
|
|
456
540
|
},
|
|
457
541
|
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef { import("eslint").Rule.RuleContext.options } ContextOptions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
/**
|
|
7
|
+
* Checks whether the rule name is valid, as it must be
|
|
8
|
+
* contained in either the plugins rule set or the user's
|
|
9
|
+
* custom rules provided at runtime
|
|
10
|
+
* @param context
|
|
11
|
+
* @param pluginRules
|
|
12
|
+
* @param customRules
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
15
|
+
isValidRule: function (context, rules) {
|
|
16
|
+
if (rules.includes(context.ruleID.replace("@sap/cds/", ""))) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Checks whether the compiled cds model is eligible
|
|
24
|
+
* for performing the plugin's rule checks
|
|
25
|
+
* @param cds cds object
|
|
26
|
+
* @param ruleID rule name
|
|
27
|
+
* @returns
|
|
28
|
+
*/
|
|
29
|
+
isValidModel: function (context) {
|
|
30
|
+
const { cds, ruleID } = context;
|
|
31
|
+
if (
|
|
32
|
+
cds &&
|
|
33
|
+
cds.model &&
|
|
34
|
+
!cds.model.err &&
|
|
35
|
+
!(
|
|
36
|
+
ruleID === "@sap/cds/cds-compile-error" ||
|
|
37
|
+
ruleID === "cds-compile-error"
|
|
38
|
+
)
|
|
39
|
+
) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Checks whether the cds environment is passed
|
|
47
|
+
* @param {ContextOptions} options
|
|
48
|
+
* @returns
|
|
49
|
+
*/
|
|
50
|
+
isValidEnv: function (options) {
|
|
51
|
+
if (options[0] && options[0].environment) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
},
|
|
56
|
+
};
|
package/package.json
CHANGED
package/lib/impl/rules/index.js
DELETED
package/lib/impl/rules/test.hbs
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
const path = require("path");
|
|
2
|
-
const { runRuleTester } = require("../../../lib/api");
|
|
3
|
-
|
|
4
|
-
runRuleTester({
|
|
5
|
-
root: path.resolve(__dirname),
|
|
6
|
-
rule: require(`../../../lib/rules/${path.basename(__dirname)}`),
|
|
7
|
-
filename: "{{filename}}",
|
|
8
|
-
parser: path.resolve(path.join(__dirname, '../../../lib/impl/parser')),
|
|
9
|
-
errors: "{{errors}}",
|
|
10
|
-
});
|