@sap/eslint-plugin-cds 2.3.3 → 2.3.4
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 +7 -0
- package/lib/rules/no-dollar-prefixed-names.js +3 -1
- package/lib/utils/helpers.js +1 -9
- package/lib/utils/model.js +20 -67
- package/lib/utils/rules.js +189 -249
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,13 @@ 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.4] - 2022-03-31
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Only deduplicate model error messages when working within VS Code Editor
|
|
14
|
+
- Hide `no-dollar-prefixed-names` compiler warning message in VS Code Editor (already passed by lsp)
|
|
15
|
+
|
|
9
16
|
## [2.3.3] - 2022-03-24
|
|
10
17
|
|
|
11
18
|
### Added
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const { isVSCodeEditor } = require("../utils/helpers");
|
|
2
|
+
|
|
1
3
|
module.exports = {
|
|
2
4
|
meta: {
|
|
3
5
|
docs: {
|
|
@@ -20,7 +22,7 @@ module.exports = {
|
|
|
20
22
|
const results = [];
|
|
21
23
|
for (const m in context.cds.model.messages) {
|
|
22
24
|
const msg = context.cds.model.messages[m];
|
|
23
|
-
if (msg.messageId === "syntax-dollar-ident") {
|
|
25
|
+
if (msg.messageId === "syntax-dollar-ident" && !isVSCodeEditor()) {
|
|
24
26
|
results.push({
|
|
25
27
|
message: msg.message,
|
|
26
28
|
loc: {
|
package/lib/utils/helpers.js
CHANGED
|
@@ -7,15 +7,7 @@ module.exports = {
|
|
|
7
7
|
* @returns boolean
|
|
8
8
|
*/
|
|
9
9
|
isValidFile: function (filePath, fileType) {
|
|
10
|
-
|
|
11
|
-
return new RegExp(
|
|
12
|
-
`${key
|
|
13
|
-
.map((file) => {
|
|
14
|
-
return file.replace("*", "");
|
|
15
|
-
})
|
|
16
|
-
.join("$|")}$`
|
|
17
|
-
);
|
|
18
|
-
}
|
|
10
|
+
const genRegex = (key) => new RegExp(`${key.map((file) => file.replace("*", "")).join("$|")}$`);
|
|
19
11
|
let isValid = false;
|
|
20
12
|
switch(fileType) {
|
|
21
13
|
case 'MODEL_FILES':
|
package/lib/utils/model.js
CHANGED
|
@@ -23,11 +23,7 @@ module.exports = {
|
|
|
23
23
|
return cache.set(key, [value, Date.now()]);
|
|
24
24
|
},
|
|
25
25
|
get(key) {
|
|
26
|
-
|
|
27
|
-
return cache.get(key)[0];
|
|
28
|
-
} else {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
26
|
+
return cache.get(key) ? cache.get(key)[0] : undefined
|
|
31
27
|
},
|
|
32
28
|
dump() {
|
|
33
29
|
const dump = {};
|
|
@@ -41,11 +37,9 @@ module.exports = {
|
|
|
41
37
|
if (cache.has(key)) {
|
|
42
38
|
cache.delete(key);
|
|
43
39
|
}
|
|
44
|
-
return;
|
|
45
40
|
},
|
|
46
41
|
clear() {
|
|
47
42
|
cache.clear();
|
|
48
|
-
return;
|
|
49
43
|
},
|
|
50
44
|
},
|
|
51
45
|
|
|
@@ -56,11 +50,8 @@ module.exports = {
|
|
|
56
50
|
* @returns boolean
|
|
57
51
|
*/
|
|
58
52
|
isNewConfigPath: function (configPath) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
return false;
|
|
53
|
+
return !module.exports.Cache.has("configpath")
|
|
54
|
+
&& (configPath !== module.exports.Cache.get("configpath"))
|
|
64
55
|
},
|
|
65
56
|
|
|
66
57
|
/**
|
|
@@ -115,25 +106,12 @@ module.exports = {
|
|
|
115
106
|
* @returns ESLint range
|
|
116
107
|
*/
|
|
117
108
|
getRange: function (code, line, column) {
|
|
118
|
-
|
|
119
|
-
if (typeof code === "string") {
|
|
120
|
-
lines = SourceCode.splitLines(code);
|
|
121
|
-
} else {
|
|
122
|
-
lines = code;
|
|
123
|
-
}
|
|
109
|
+
const lines = typeof code === "string" ? SourceCode.splitLines(code) : code
|
|
124
110
|
const ranges = [0];
|
|
125
111
|
lines.forEach((line, i) => {
|
|
126
|
-
|
|
127
|
-
ranges[i + 1] = line.length + 1;
|
|
128
|
-
} else {
|
|
129
|
-
ranges[i + 1] = ranges[i] + line.length + 1;
|
|
130
|
-
}
|
|
112
|
+
ranges[i + 1] = i === 0 ? line.length + 1 : ranges[i] + line.length + 1
|
|
131
113
|
});
|
|
132
|
-
|
|
133
|
-
return ranges[line - 1] + column;
|
|
134
|
-
} else {
|
|
135
|
-
return column;
|
|
136
|
-
}
|
|
114
|
+
return line > 1 ? ranges[line - 1] + column : column
|
|
137
115
|
},
|
|
138
116
|
|
|
139
117
|
/**
|
|
@@ -145,12 +123,7 @@ module.exports = {
|
|
|
145
123
|
* @returns Last line index
|
|
146
124
|
*/
|
|
147
125
|
getLastLine: function (code) {
|
|
148
|
-
|
|
149
|
-
if (typeof code === "string") {
|
|
150
|
-
lines = SourceCode.splitLines(code);
|
|
151
|
-
} else {
|
|
152
|
-
lines = code;
|
|
153
|
-
}
|
|
126
|
+
const lines = typeof code === "string" ? SourceCode.splitLines(code) : code
|
|
154
127
|
return lines.length - 1;
|
|
155
128
|
},
|
|
156
129
|
|
|
@@ -181,10 +154,8 @@ module.exports = {
|
|
|
181
154
|
loc.start.line = nameloc.line;
|
|
182
155
|
loc.end.column = nameloc.col - 1 + name.length;
|
|
183
156
|
loc.end.line = nameloc.line;
|
|
184
|
-
} else {
|
|
185
|
-
if (obj.parent) {
|
|
157
|
+
} else if (obj.parent) {
|
|
186
158
|
this.getLocation(name, obj.parent, model);
|
|
187
|
-
}
|
|
188
159
|
}
|
|
189
160
|
}
|
|
190
161
|
// Empty locations default to line 0, column 0
|
|
@@ -332,13 +303,8 @@ module.exports = {
|
|
|
332
303
|
const configPath = path.dirname(filePath);
|
|
333
304
|
module.exports.Cache.set('configpath', configPath);
|
|
334
305
|
let files = fs.readdirSync(configPath);
|
|
335
|
-
const modelfiles =
|
|
336
|
-
|
|
337
|
-
const filePath = path.join(configPath, file);
|
|
338
|
-
if (isValidFile(filePath, 'MODEL_FILES')) {
|
|
339
|
-
modelfiles.push(filePath);
|
|
340
|
-
}
|
|
341
|
-
});
|
|
306
|
+
const modelfiles = files.map(f => path.join(configPath, f))
|
|
307
|
+
.filter(fp => isValidFile(fp, 'MODEL_FILES'))
|
|
342
308
|
module.exports.Cache.set(`modelfiles:${configPath}`, modelfiles);
|
|
343
309
|
const dictFiles = module.exports.getDictFiles(configPath, modelfiles);
|
|
344
310
|
module.exports.Cache.set(`dictfiles:${configPath}`, dictFiles);
|
|
@@ -359,11 +325,9 @@ module.exports = {
|
|
|
359
325
|
dictFiles = module.exports.Cache.get(`dictfiles:${input}`);
|
|
360
326
|
} else {
|
|
361
327
|
files.forEach((file) => {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
dictFiles[file] = fs.readFileSync(file, "utf8");
|
|
366
|
-
}
|
|
328
|
+
dictFiles[file] = module.exports.Cache.has(`file:${file}`)
|
|
329
|
+
? module.exports.Cache.get(`file:${file}`)
|
|
330
|
+
: fs.readFileSync(file, "utf8")
|
|
367
331
|
});
|
|
368
332
|
}
|
|
369
333
|
return dictFiles;
|
|
@@ -375,6 +339,7 @@ module.exports = {
|
|
|
375
339
|
* @returns boolean
|
|
376
340
|
*/
|
|
377
341
|
hasFileChanged: function (code, filePath, configPath) {
|
|
342
|
+
let result = false
|
|
378
343
|
const files = module.exports.Cache.get(`modelfiles:${configPath}`);
|
|
379
344
|
const dictFiles = module.exports.getDictFiles(configPath, files);
|
|
380
345
|
const isFileInModel = module.exports.isFileInModel(filePath, configPath);
|
|
@@ -384,14 +349,12 @@ module.exports = {
|
|
|
384
349
|
if (dictFiles[filePath] !== code) {
|
|
385
350
|
dictFiles[filePath] = code;
|
|
386
351
|
module.exports.Cache.set(`dictfiles:${configPath}`, dictFiles);
|
|
387
|
-
|
|
388
|
-
}
|
|
389
|
-
} else {
|
|
390
|
-
if (dictFiles[filePath] !== code) {
|
|
391
|
-
return true;
|
|
352
|
+
result = true
|
|
392
353
|
}
|
|
354
|
+
} else if (dictFiles[filePath] !== code) {
|
|
355
|
+
result = true
|
|
393
356
|
}
|
|
394
|
-
return
|
|
357
|
+
return result;
|
|
395
358
|
},
|
|
396
359
|
|
|
397
360
|
/**
|
|
@@ -401,14 +364,8 @@ module.exports = {
|
|
|
401
364
|
* @returns boolean
|
|
402
365
|
*/
|
|
403
366
|
isFileInModel(filePath, configPath) {
|
|
404
|
-
let files = module.exports.Cache.get(`modelfiles:${configPath}`);
|
|
405
|
-
|
|
406
|
-
files = [];
|
|
407
|
-
}
|
|
408
|
-
if (files && files.length > 0 && files.includes(filePath)) {
|
|
409
|
-
return true;
|
|
410
|
-
}
|
|
411
|
-
return false;
|
|
367
|
+
let files = module.exports.Cache.get(`modelfiles:${configPath}`) || [];
|
|
368
|
+
return files && files.length > 0 && files.includes(filePath)
|
|
412
369
|
},
|
|
413
370
|
|
|
414
371
|
/**
|
|
@@ -419,10 +376,6 @@ module.exports = {
|
|
|
419
376
|
*/
|
|
420
377
|
updateModel: function (code, filePath, configPath) {
|
|
421
378
|
let reflectedModel;
|
|
422
|
-
let files = module.exports.Cache.get(`modelfiles:${configPath}`);
|
|
423
|
-
if (!files) {
|
|
424
|
-
files = [];
|
|
425
|
-
}
|
|
426
379
|
const dictFiles = module.exports.Cache.get(`dictfiles:${configPath}`);
|
|
427
380
|
dictFiles[filePath] = code;
|
|
428
381
|
module.exports.Cache.set(`dictfiles:${configPath}`, dictFiles);
|
package/lib/utils/rules.js
CHANGED
|
@@ -39,31 +39,29 @@ const REGEX_COMMENT_START = "(/\\*|(.+)?//)(\\s?)+eslint-";
|
|
|
39
39
|
const REGEX_COMMENTS = `${REGEX_COMMENT_START}(enable|disable)(-next)?(-line)?(.+)?`;
|
|
40
40
|
|
|
41
41
|
function doReport(cdscontext, reportDescriptor, d) {
|
|
42
|
+
const retouchLocations = (descriptor) => {
|
|
43
|
+
if (d) {
|
|
44
|
+
descriptor.loc = descriptor.loc || cdscontext.cds.getLocation(d.name, d);
|
|
45
|
+
descriptor.file = descriptor.file || (d.$location ? d.$location.file : "unknown.cds");
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
42
49
|
switch (typeof reportDescriptor) {
|
|
43
50
|
case "string":
|
|
44
51
|
reportDescriptor = { message: reportDescriptor };
|
|
45
|
-
|
|
46
|
-
if (!reportDescriptor.loc) reportDescriptor.loc = cdscontext.cds.getLocation(d.name, d);
|
|
47
|
-
if (!reportDescriptor.file) reportDescriptor.file = (d.$location && d.$location.file) || "unknown.cds";
|
|
48
|
-
}
|
|
52
|
+
retouchLocations(reportDescriptor);
|
|
49
53
|
cdscontext.report(reportDescriptor);
|
|
50
54
|
break;
|
|
51
55
|
case "object":
|
|
52
56
|
if (!Array.isArray(reportDescriptor)) {
|
|
53
|
-
|
|
54
|
-
if (!reportDescriptor.loc) reportDescriptor.loc = getLocation(d.name, d);
|
|
55
|
-
if (!reportDescriptor.file) reportDescriptor.file = (d.$location && d.$location.file) || "unknown.cds";
|
|
56
|
-
}
|
|
57
|
+
retouchLocations(reportDescriptor);
|
|
57
58
|
cdscontext.report(reportDescriptor);
|
|
58
59
|
} else {
|
|
59
60
|
reportDescriptor.forEach((x) => {
|
|
60
61
|
if (typeof x === "string") {
|
|
61
62
|
x = { message: x };
|
|
62
63
|
}
|
|
63
|
-
|
|
64
|
-
if (!x.loc) x.loc = getLocation(d.name, d);
|
|
65
|
-
if (!x.file) x.file = (d.$location && d.$location.file) || "unknown.cds";
|
|
66
|
-
}
|
|
64
|
+
retouchLocations(x);
|
|
67
65
|
cdscontext.report(x);
|
|
68
66
|
});
|
|
69
67
|
}
|
|
@@ -116,10 +114,7 @@ function addCDSContext(context, node) {
|
|
|
116
114
|
const filePath = (context.filePath = context.getPhysicalFilename());
|
|
117
115
|
const configPath = !Cache.has("test") ? loadConfigPath(filePath) : path.dirname(filePath);
|
|
118
116
|
let sourcecode = context.getSourceCode();
|
|
119
|
-
|
|
120
|
-
if (!code) {
|
|
121
|
-
code = Cache.get(`file:${filePath}`);
|
|
122
|
-
}
|
|
117
|
+
const code = sourcecode.getText(node) || Cache.get(`file:${filePath}`);
|
|
123
118
|
if (code) {
|
|
124
119
|
sourcecode = new SourceCode(code, getAST(code));
|
|
125
120
|
}
|
|
@@ -162,167 +157,151 @@ function createRule(spec) {
|
|
|
162
157
|
|
|
163
158
|
return {
|
|
164
159
|
meta,
|
|
165
|
-
create:
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
} catch (err) {
|
|
195
|
-
// Report errors in ESLint style
|
|
196
|
-
if (err.messages) {
|
|
197
|
-
// Always show model compile errors
|
|
198
|
-
reportCompilationErr(meta, node, cdscontext, err);
|
|
199
|
-
} else {
|
|
200
|
-
// Thrown errors are only shown on console with --debug
|
|
201
|
-
reportErr(meta, node, cdscontext, err);
|
|
160
|
+
create: (context) => ({
|
|
161
|
+
Program: function (node) {
|
|
162
|
+
const cdscontext = addCDSContext(context, node, meta);
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
const { report, ...ruleDescriptors } = cdscontext;
|
|
166
|
+
const handlers = create({ ...ruleDescriptors, report: (r) => report(r) });
|
|
167
|
+
|
|
168
|
+
// Report descriptors with fake visitor 'all'
|
|
169
|
+
// Used for environment rules and rules which require another compiled model
|
|
170
|
+
// (i.e. sql or odata)
|
|
171
|
+
if (handlers.all) {
|
|
172
|
+
let reportDescriptor = handlers.all();
|
|
173
|
+
doReport(cdscontext, reportDescriptor);
|
|
174
|
+
} else {
|
|
175
|
+
// TODO: Use external address
|
|
176
|
+
// Report descriptors with visitors using using `any.is()`
|
|
177
|
+
// https://pages.github.tools.sap/cap/docs/node.js/cds-reflect
|
|
178
|
+
if (cdscontext.cds.model) {
|
|
179
|
+
cdscontext.cds.model.forall((d) =>
|
|
180
|
+
Object.entries(handlers)
|
|
181
|
+
.filter(([type, lazy]) => d.is(type))
|
|
182
|
+
.forEach(([lazy, handler]) => doReport(cdscontext, handler(d), d))
|
|
183
|
+
);
|
|
202
184
|
}
|
|
203
185
|
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
|
|
186
|
+
} catch (err) {
|
|
187
|
+
// Report errors in ESLint style
|
|
188
|
+
if (err.messages) {
|
|
189
|
+
// Always show model compile errors
|
|
190
|
+
reportCompilationErr(meta, node, cdscontext, err);
|
|
191
|
+
} else {
|
|
192
|
+
// Thrown errors are only shown on console with --debug
|
|
193
|
+
reportErr(meta, node, cdscontext, err);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
}),
|
|
207
198
|
};
|
|
208
199
|
}
|
|
209
200
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
201
|
+
/**
|
|
202
|
+
* Checks whether a lint rule has been disabled by eslint-disable
|
|
203
|
+
* comments at a given location
|
|
204
|
+
* @param entry lint report
|
|
205
|
+
* @param cdscontext cds context object
|
|
206
|
+
* @param rules all availabe rules
|
|
207
|
+
* @returns boolean
|
|
208
|
+
*/
|
|
218
209
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
isDisabled = true;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
210
|
+
function isRuleDisabled(entry, cdscontext) {
|
|
211
|
+
let isDisabled = false;
|
|
212
|
+
if (entry.loc && entry.loc.start) {
|
|
213
|
+
const line = entry.loc.start.line;
|
|
214
|
+
if (cdscontext) {
|
|
215
|
+
const rulesDisabled = _getDisabled(cdscontext.code, cdscontext.sourcecode, line);
|
|
216
|
+
const id = cdscontext.id;
|
|
217
|
+
isDisabled = line && id in rulesDisabled && rulesDisabled[id] === "off";
|
|
230
218
|
}
|
|
231
|
-
return isDisabled;
|
|
232
219
|
}
|
|
220
|
+
return isDisabled;
|
|
221
|
+
}
|
|
233
222
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
comment = {
|
|
281
|
-
lineComment: loc.line,
|
|
282
|
-
lineDisabled: loc.line + 1,
|
|
283
|
-
rules: disableRules,
|
|
284
|
-
type: keyword,
|
|
285
|
-
};
|
|
286
|
-
} else {
|
|
287
|
-
comment = {
|
|
288
|
-
lineComment: loc.line,
|
|
289
|
-
lineDisabled: loc.line,
|
|
290
|
-
rules: disableRules,
|
|
291
|
-
type: keyword,
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
|
-
if (!disableType.includes("-line")) {
|
|
295
|
-
comment.lineDisabled = "EOF";
|
|
223
|
+
/**
|
|
224
|
+
* Turns rules "on" or "off" for given line according to eslint-disable
|
|
225
|
+
* comments:
|
|
226
|
+
* 1. Reads code string and extracts a list of comments (in order)
|
|
227
|
+
* 2. Initiates rulesDisabled array with all rules "on" by default
|
|
228
|
+
* 3. Switches rules "off" (or "on" again) based on disable comment
|
|
229
|
+
* @param code current code
|
|
230
|
+
* @param sourcecode source code object to get index from
|
|
231
|
+
* @param line current code line to analyze
|
|
232
|
+
* @returns rules dictionary with rules being either 'on' and 'off'
|
|
233
|
+
*/
|
|
234
|
+
function _getDisabled(code, sourcecode, line) {
|
|
235
|
+
const listDisabled = [];
|
|
236
|
+
let { listEnvRules, listModelRules, listRules } = Cache.get("rulesInfo");
|
|
237
|
+
const rulesDisabled = listRules.reduce((o, key) => ({ ...o, [key]: "on" }), {});
|
|
238
|
+
let matches = [];
|
|
239
|
+
if (code) {
|
|
240
|
+
matches = [...code.matchAll(REGEX_COMMENTS)];
|
|
241
|
+
if (matches.length > 0) {
|
|
242
|
+
matches.forEach((match) => {
|
|
243
|
+
if (match) {
|
|
244
|
+
const index = match.index;
|
|
245
|
+
match = match[0];
|
|
246
|
+
if (match.includes("*/")) {
|
|
247
|
+
match = match.split("*/")[0].replace("/*", "");
|
|
248
|
+
} else if (match.includes("//")) {
|
|
249
|
+
match = match.split("//")[1];
|
|
250
|
+
}
|
|
251
|
+
if (match) {
|
|
252
|
+
match = match.trim();
|
|
253
|
+
}
|
|
254
|
+
["disable", "enable"].forEach((keyword) => {
|
|
255
|
+
const loc = sourcecode.getLocFromIndex(index);
|
|
256
|
+
const disableType = match.split(" ")[0];
|
|
257
|
+
let disableRules = match.split(`${disableType} `)[1];
|
|
258
|
+
disableRules = disableRules
|
|
259
|
+
? disableRules.split(",").map((rule) => rule.trim())
|
|
260
|
+
: listEnvRules.concat(listModelRules).map((rule) => `@sap/cds/${rule}`);
|
|
261
|
+
let comment = {};
|
|
262
|
+
if ([`eslint-${keyword}`, `eslint-${keyword}-line`, `eslint-${keyword}-next-line`].includes(disableType)) {
|
|
263
|
+
comment = disableType.includes("-next-line")
|
|
264
|
+
? {
|
|
265
|
+
lineComment: loc.line,
|
|
266
|
+
lineDisabled: loc.line + 1,
|
|
267
|
+
rules: disableRules,
|
|
268
|
+
type: keyword,
|
|
296
269
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
270
|
+
: {
|
|
271
|
+
lineComment: loc.line,
|
|
272
|
+
lineDisabled: loc.line,
|
|
273
|
+
rules: disableRules,
|
|
274
|
+
type: keyword,
|
|
275
|
+
};
|
|
276
|
+
if (!disableType.includes("-line")) {
|
|
277
|
+
comment.lineDisabled = "EOF";
|
|
278
|
+
}
|
|
300
279
|
}
|
|
280
|
+
listDisabled.push(comment);
|
|
301
281
|
});
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
}
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
for (const el of listDisabled.filter(
|
|
285
|
+
(d) => d.lineComment > line && (d.lineDisabled === "EOF" || d.lineDisabled === line)
|
|
286
|
+
)) {
|
|
287
|
+
if (el.lineDisabled === "EOF") {
|
|
288
|
+
el.lineDisabled = getLastLine(code);
|
|
289
|
+
}
|
|
290
|
+
if (el.rules) {
|
|
291
|
+
el.rules.forEach((rule) => {
|
|
292
|
+
if (el.type === "disable") {
|
|
293
|
+
rulesDisabled[rule] = "off";
|
|
294
|
+
} else if (el.type === "enable") {
|
|
295
|
+
rulesDisabled[rule] = "on";
|
|
319
296
|
}
|
|
320
|
-
}
|
|
297
|
+
});
|
|
321
298
|
}
|
|
322
299
|
}
|
|
323
|
-
return rulesDisabled;
|
|
324
300
|
}
|
|
325
|
-
|
|
301
|
+
}
|
|
302
|
+
return rulesDisabled;
|
|
303
|
+
}
|
|
304
|
+
|
|
326
305
|
module.exports = {
|
|
327
306
|
/**
|
|
328
307
|
* Gets value for a given key in allowed keys of ESLint's meta data
|
|
@@ -452,11 +431,9 @@ module.exports = {
|
|
|
452
431
|
/* eslint-disable-next-line no-unused-vars */
|
|
453
432
|
Object.entries(ruleDict).forEach(([, rules]) => {
|
|
454
433
|
rules.forEach(function (rule) {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
mdRules += `| ${rule.recommended} | ${rule.fixable} | ${rule.hasSuggestions} | ${rule.construction} | | [${rule.name}](Rules.md#${rule.name}) | ${rule.details}|\n`;
|
|
459
|
-
}
|
|
434
|
+
mdRules += release
|
|
435
|
+
? `| ${rule.recommended} | ${rule.fixable} | ${rule.hasSuggestions} | | | [${rule.name}](Rules-released.md#${rule.name}) | ${rule.details}|\n`
|
|
436
|
+
: `| ${rule.recommended} | ${rule.fixable} | ${rule.hasSuggestions} | ${rule.construction} | | [${rule.name}](Rules.md#${rule.name}) | ${rule.details}|\n`;
|
|
460
437
|
});
|
|
461
438
|
});
|
|
462
439
|
mdRules += "\n";
|
|
@@ -493,7 +470,7 @@ module.exports = {
|
|
|
493
470
|
const mdRuleListCur = fs.readFileSync(ruleListDocsPath, "utf8");
|
|
494
471
|
|
|
495
472
|
// Get rules table
|
|
496
|
-
|
|
473
|
+
const mdRuleList = module.exports.genMdRules(ruleDict, release, true);
|
|
497
474
|
|
|
498
475
|
// Get rule details
|
|
499
476
|
let mdRules = module.exports.genMdRules(ruleDict, release, false);
|
|
@@ -525,30 +502,21 @@ module.exports = {
|
|
|
525
502
|
docsPath = path.join(__dirname, "../../docs");
|
|
526
503
|
rulePath = path.join(__dirname, "../rules");
|
|
527
504
|
testPath = path.join(__dirname, "../../test/rules");
|
|
528
|
-
release = JSON.parse(fs.readFileSync(path.join(__dirname, "
|
|
505
|
+
release = JSON.parse(fs.readFileSync(path.join(__dirname, "../../package.json"))).version;
|
|
529
506
|
} else {
|
|
530
507
|
docsPath = path.join(projectPath, `${customRulesDir}/docs`);
|
|
531
508
|
rulePath = path.join(projectPath, `${customRulesDir}/rules`);
|
|
532
509
|
testPath = path.join(projectPath, `${customRulesDir}/tests`);
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
if (!fs.existsSync(rulePath)) {
|
|
537
|
-
await mkdirp(rulePath);
|
|
538
|
-
}
|
|
539
|
-
if (!fs.existsSync(testPath)) {
|
|
540
|
-
await mkdirp(testPath);
|
|
541
|
-
}
|
|
510
|
+
await Promise.all(
|
|
511
|
+
[docsPath, rulePath, testPath].filter((path) => !fs.existsSync(path)).map((path) => mkdirp(path))
|
|
512
|
+
);
|
|
542
513
|
}
|
|
543
514
|
|
|
544
515
|
if (registry) {
|
|
545
516
|
// Get rules (internal on artifactory)
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
} else {
|
|
550
|
-
versionInternal = JSON.parse(fs.readFileSync(path.join(__dirname, "../../../package.json"))).version;
|
|
551
|
-
}
|
|
517
|
+
const versionInternal = prepareRelease
|
|
518
|
+
? JSON.parse(fs.readFileSync(path.join(__dirname, "../../package.json"))).version
|
|
519
|
+
: module.exports.getPackageVersion(registry);
|
|
552
520
|
if (versionInternal) {
|
|
553
521
|
console.log(`Updating internal rules from v>=${versionInternal}:\n${registry}\n`);
|
|
554
522
|
const ruleDictInternal = module.exports.getRuleDict(docsPath, rulePath, testPath, versionInternal);
|
|
@@ -556,12 +524,9 @@ module.exports = {
|
|
|
556
524
|
}
|
|
557
525
|
// Get rules released (external on npm)
|
|
558
526
|
const npmRegistry = "https://registry.npmjs.org";
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
} else {
|
|
563
|
-
versionExternal = JSON.parse(fs.readFileSync(path.join(__dirname, "../../../package.json"))).version;
|
|
564
|
-
}
|
|
527
|
+
const versionExternal = prepareRelease
|
|
528
|
+
? JSON.parse(fs.readFileSync(path.join(__dirname, "../../package.json"))).version
|
|
529
|
+
: module.exports.getPackageVersion(npmRegistry);
|
|
565
530
|
if (versionExternal) {
|
|
566
531
|
console.log(`Updating external rules from v>=${versionExternal}:\n${npmRegistry}\n`);
|
|
567
532
|
const ruleDictExternal = module.exports.getRuleDict(docsPath, rulePath, testPath, versionExternal, release);
|
|
@@ -577,8 +542,8 @@ module.exports = {
|
|
|
577
542
|
|
|
578
543
|
getRuleDict: function (docsPath, rulePath, testPath, versionRequired = "0.0.0", release = false) {
|
|
579
544
|
let mdRule, mdRuleSources, mdRuleContents;
|
|
580
|
-
|
|
581
|
-
fs.readdirSync(rulePath).filter(
|
|
545
|
+
const ruleDict = {};
|
|
546
|
+
fs.readdirSync(rulePath).filter((file) => {
|
|
582
547
|
if (path.extname(file).toLowerCase() === ".js" && file !== "index.js") {
|
|
583
548
|
const rule = path.basename(file).replace(path.extname(file), "");
|
|
584
549
|
const ruleTestPath = path.join(testPath, rule, "rule.test.js");
|
|
@@ -596,25 +561,14 @@ module.exports = {
|
|
|
596
561
|
const suggestions = ruleMeta.hasSuggestions;
|
|
597
562
|
|
|
598
563
|
let underConstruction = "";
|
|
599
|
-
if (!release && semver.satisfies(version, `>${versionRequired}`)) {
|
|
564
|
+
if (!release && (version === "TBD" || semver.satisfies(version, `>${versionRequired}`))) {
|
|
600
565
|
underConstruction = "🚧";
|
|
601
566
|
console.log(` > 🚧 Rule '${rule}' still under construction.\n`);
|
|
602
567
|
}
|
|
603
568
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
let isRecommended = "";
|
|
610
|
-
if (recommended === true) {
|
|
611
|
-
isRecommended = "✔️";
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
let hasSuggestions = "";
|
|
615
|
-
if (suggestions === true) {
|
|
616
|
-
hasSuggestions = "💡";
|
|
617
|
-
}
|
|
569
|
+
const isFixable = ["code", "whitespace"].includes(fixable) ? "🔧" : "";
|
|
570
|
+
const isRecommended = recommended === true ? "✔️" : "";
|
|
571
|
+
const hasSuggestions = suggestions === true ? "💡" : "";
|
|
618
572
|
|
|
619
573
|
const ruleDictEntry = {
|
|
620
574
|
name: rule,
|
|
@@ -628,11 +582,12 @@ module.exports = {
|
|
|
628
582
|
};
|
|
629
583
|
mdRule = module.exports.getRuleExamples(ruleTestPath, testPath, ruleDictEntry);
|
|
630
584
|
mdRuleContents = "";
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
585
|
+
|
|
586
|
+
mdRuleContents +=
|
|
587
|
+
!release && underConstruction
|
|
588
|
+
? `## ${rule}\n<span class='shifted'>${underConstruction} <span class='label'>${category}</span></span>\n\n`
|
|
589
|
+
: `## ${rule}\n<span class='shifted label'>${category}</span>\n\n`;
|
|
590
|
+
|
|
636
591
|
mdRuleContents += `### Rule Details\n${details}\n\n`;
|
|
637
592
|
if (mdRule) {
|
|
638
593
|
mdRuleContents += `### Examples\n${mdRule}\n\n`;
|
|
@@ -890,27 +845,27 @@ module.exports = {
|
|
|
890
845
|
// Do not consider disabled content
|
|
891
846
|
if (!isRuleDisabled(lint, thisArg)) {
|
|
892
847
|
const isModelLint = lint.file && isValidFile(lint.file, "MODEL_FILES") && !lint.err;
|
|
893
|
-
if (isModelLint) {
|
|
894
|
-
if (
|
|
848
|
+
if (isModelLint && module.exports.isDedicatedFile(lint, thisArg)) {
|
|
849
|
+
if (isVSCodeEditor()) {
|
|
895
850
|
report = true;
|
|
851
|
+
} else {
|
|
852
|
+
if (module.exports.isReportUnique(lint, "modelReports")) {
|
|
853
|
+
report = true;
|
|
854
|
+
}
|
|
896
855
|
}
|
|
897
856
|
}
|
|
898
857
|
if (!lint.loc) {
|
|
899
858
|
lint.loc = module.exports.addDefaultLoc();
|
|
900
859
|
}
|
|
901
|
-
if (
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
860
|
+
if (
|
|
861
|
+
!isModelLint &&
|
|
862
|
+
!lint.err &&
|
|
863
|
+
module.exports.isReportUnique(lint, "envReports", thisArg.configPath)
|
|
864
|
+
) {
|
|
865
|
+
report = true;
|
|
905
866
|
}
|
|
906
|
-
if (lint.err) {
|
|
907
|
-
|
|
908
|
-
report = true;
|
|
909
|
-
} else {
|
|
910
|
-
if (module.exports.isReportUnique(lint, "errReports", thisArg.configPath)) {
|
|
911
|
-
report = true;
|
|
912
|
-
}
|
|
913
|
-
}
|
|
867
|
+
if (lint.err && (lint.file || module.exports.isReportUnique(lint, "errReports", thisArg.configPath))) {
|
|
868
|
+
report = true;
|
|
914
869
|
}
|
|
915
870
|
if (report) {
|
|
916
871
|
return thisArg._context.report(lint);
|
|
@@ -924,20 +879,10 @@ module.exports = {
|
|
|
924
879
|
return new Proxy(ruleReport, handler);
|
|
925
880
|
},
|
|
926
881
|
|
|
927
|
-
resolveFilePath:
|
|
928
|
-
let fileAbs;
|
|
929
|
-
if (!path.isAbsolute(file)) {
|
|
930
|
-
fileAbs = path.join(Cache.get("configpath"), file);
|
|
931
|
-
} else {
|
|
932
|
-
fileAbs = file;
|
|
933
|
-
}
|
|
934
|
-
return fileAbs;
|
|
935
|
-
},
|
|
882
|
+
resolveFilePath: (file) => (path.isAbsolute(file) ? file : path.join(Cache.get("configpath"), file)),
|
|
936
883
|
|
|
937
|
-
isDedicatedFile:
|
|
938
|
-
|
|
939
|
-
return fileAbs === thisArg.filePath || lint.file === "<stdin>.cds";
|
|
940
|
-
},
|
|
884
|
+
isDedicatedFile: (lint, thisArg) =>
|
|
885
|
+
module.exports.resolveFilePath(lint.file) === thisArg.filePath || lint.file === "<stdin>.cds",
|
|
941
886
|
|
|
942
887
|
isReportUnique: function (lint, name, uniqueness) {
|
|
943
888
|
let report = false;
|
|
@@ -962,12 +907,10 @@ module.exports = {
|
|
|
962
907
|
return report;
|
|
963
908
|
},
|
|
964
909
|
|
|
965
|
-
addDefaultLoc:
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
};
|
|
970
|
-
},
|
|
910
|
+
addDefaultLoc: () => ({
|
|
911
|
+
start: { line: 0, column: -1 },
|
|
912
|
+
end: { line: 0, column: -1 },
|
|
913
|
+
}),
|
|
971
914
|
|
|
972
915
|
getModel: function (code, filePath, configPath) {
|
|
973
916
|
let model;
|
|
@@ -1013,11 +956,8 @@ module.exports = {
|
|
|
1013
956
|
}
|
|
1014
957
|
},
|
|
1015
958
|
|
|
1016
|
-
isParkedModelFile:
|
|
1017
|
-
|
|
1018
|
-
const isInModel = isFileInModel(filePath, configPath);
|
|
1019
|
-
return isValidModelFile && !isInModel;
|
|
1020
|
-
},
|
|
959
|
+
isParkedModelFile: (filePath, configPath) =>
|
|
960
|
+
isValidFile(filePath, "MODEL_FILES") && !isFileInModel(filePath, configPath),
|
|
1021
961
|
|
|
1022
962
|
getEnvironment: function (options) {
|
|
1023
963
|
let environment;
|
package/package.json
CHANGED