@sap/eslint-plugin-cds 2.5.0 → 2.6.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 +24 -0
- package/README.md +2 -1
- package/lib/api/index.js +9 -9
- package/lib/conf/all.js +20 -19
- package/lib/conf/index.js +10 -10
- package/lib/conf/recommended.js +17 -16
- package/lib/constants.js +16 -14
- package/lib/index.js +17 -11
- package/lib/parser.js +90 -82
- package/lib/rules/assoc2many-ambiguous-key.js +71 -70
- package/lib/rules/auth-no-empty-restrictions.js +16 -15
- package/lib/rules/auth-use-requires.js +19 -18
- package/lib/rules/auth-valid-restrict-grant.js +49 -46
- package/lib/rules/auth-valid-restrict-keys.js +19 -18
- package/lib/rules/auth-valid-restrict-to.js +68 -64
- package/lib/rules/auth-valid-restrict-where.js +44 -43
- package/lib/rules/extension-restrictions.js +69 -0
- package/lib/rules/index.js +23 -22
- package/lib/rules/latest-cds-version.js +21 -20
- package/lib/rules/min-node-version.js +22 -22
- package/lib/rules/no-db-keywords.js +21 -27
- package/lib/rules/no-dollar-prefixed-names.js +12 -11
- package/lib/rules/no-join-on-draft.js +27 -0
- package/lib/rules/require-2many-oncond.js +8 -8
- package/lib/rules/sql-cast-suggestion.js +13 -12
- package/lib/rules/start-elements-lowercase.js +42 -41
- package/lib/rules/start-entities-uppercase.js +26 -25
- package/lib/rules/valid-csv-header.js +58 -57
- package/lib/types.d.ts +1 -0
- package/lib/utils/Cache.js +17 -17
- package/lib/utils/Colors.js +8 -8
- package/lib/utils/createRule.js +172 -153
- package/lib/utils/findFuzzy.js +37 -38
- package/lib/utils/genDocs.js +224 -242
- package/lib/utils/getConfigPath.js +27 -27
- package/lib/utils/getConfiguredFileTypes.js +4 -4
- package/lib/utils/getFileExtensions.js +3 -3
- package/lib/utils/getProjectRootPath.js +25 -0
- package/lib/utils/isConfiguredFileType.js +11 -11
- package/lib/utils/rules.js +59 -59
- package/lib/utils/runRuleTester.js +76 -71
- package/package.json +7 -1
- package/lib/rules/no-join-on-draft-enabled-entities.js +0 -25
- package/lib/utils/createRuleDocs.js +0 -361
- package/lib/utils/jsonc.js +0 -1
- package/lib/utils/jsoncParser.js +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
const { FILES } = require(
|
|
1
|
+
const { FILES } = require('../constants')
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Returns an array of allowed file extensions
|
|
5
5
|
* the plugin can parse of the form "*.ext"
|
|
6
|
-
* @returns {ConfigOverrideFiles} Array of file extensions
|
|
6
|
+
* @returns { ConfigOverrideFiles } Array of file extensions
|
|
7
7
|
*/
|
|
8
8
|
module.exports = () => {
|
|
9
|
-
return FILES
|
|
10
|
-
}
|
|
9
|
+
return FILES
|
|
10
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const constants = require(
|
|
1
|
+
const constants = require('../constants')
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Returns an array of allowed file extensions
|
|
5
5
|
* the plugin can parse of the form "*.ext"
|
|
6
|
-
* @returns {ConfigOverrideFiles} Array of file extensions
|
|
6
|
+
* @returns { ConfigOverrideFiles } Array of file extensions
|
|
7
7
|
*/
|
|
8
|
-
module.exports = () => constants.FILES
|
|
8
|
+
module.exports = () => constants.FILES
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Searches for directory containing cds roots
|
|
3
|
+
* @param {string} currentDir start here and search until root dir
|
|
4
|
+
* @returns {string} dir containing cds roots (empty if not exists)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const path = require('path')
|
|
8
|
+
const cds = require('@sap/cds')
|
|
9
|
+
const Cache = require('./Cache')
|
|
10
|
+
|
|
11
|
+
module.exports = (currentDir = '.') => {
|
|
12
|
+
let dir = path.resolve(currentDir)
|
|
13
|
+
while (dir !== path.resolve(dir, '..')) {
|
|
14
|
+
// @ts-ignore
|
|
15
|
+
cds.resolve.cache = {}
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
const roots = cds.resolve('*', { root: dir })
|
|
18
|
+
if (roots && roots.length > 0) {
|
|
19
|
+
Cache.set(`roots:${dir}`, roots)
|
|
20
|
+
return dir
|
|
21
|
+
}
|
|
22
|
+
dir = path.join(dir, '..')
|
|
23
|
+
}
|
|
24
|
+
return ''
|
|
25
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { FILES, MODEL_FILES } = require(
|
|
1
|
+
const { FILES, MODEL_FILES } = require('../constants')
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Checks whether the given filePath matches a regex `files`
|
|
@@ -6,15 +6,15 @@ const { FILES, MODEL_FILES } = require("../constants");
|
|
|
6
6
|
* @returns boolean
|
|
7
7
|
*/
|
|
8
8
|
module.exports = (filePath, fileType) => {
|
|
9
|
-
const genRegex = (key) => new RegExp(`${key.map((file) => file.replace(
|
|
10
|
-
let isValid = false
|
|
9
|
+
const genRegex = (key) => new RegExp(`${key.map((file) => file.replace('*', '')).join('$|')}$`)
|
|
10
|
+
let isValid = false
|
|
11
11
|
switch (fileType) {
|
|
12
|
-
case
|
|
13
|
-
isValid = genRegex(MODEL_FILES).test(filePath)
|
|
14
|
-
break
|
|
15
|
-
case
|
|
16
|
-
isValid = genRegex(FILES).test(filePath)
|
|
17
|
-
break
|
|
12
|
+
case 'MODEL_FILES':
|
|
13
|
+
isValid = genRegex(MODEL_FILES).test(filePath)
|
|
14
|
+
break
|
|
15
|
+
case 'FILES':
|
|
16
|
+
isValid = genRegex(FILES).test(filePath)
|
|
17
|
+
break
|
|
18
18
|
}
|
|
19
|
-
return isValid
|
|
20
|
-
}
|
|
19
|
+
return isValid
|
|
20
|
+
}
|
package/lib/utils/rules.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const SEP =
|
|
2
|
-
const EOL =
|
|
1
|
+
const SEP = '[,;\t]'
|
|
2
|
+
const EOL = '\\r?\\n'
|
|
3
3
|
|
|
4
|
-
const findFuzzy = require(
|
|
4
|
+
const findFuzzy = require('./findFuzzy')
|
|
5
5
|
|
|
6
6
|
module.exports = {
|
|
7
7
|
findFuzzy,
|
|
@@ -9,105 +9,105 @@ module.exports = {
|
|
|
9
9
|
*
|
|
10
10
|
* @param {*} e
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
splitDefName: function (e) {
|
|
13
13
|
// Entity names from CSN are of the form:
|
|
14
|
-
// <namespace>.<service>.<
|
|
15
|
-
let prefix =
|
|
16
|
-
let suffix =
|
|
17
|
-
let
|
|
18
|
-
const names =
|
|
19
|
-
|
|
14
|
+
// <namespace>.<service>.<def>.<'texts'|'localized'>|<composition value>
|
|
15
|
+
let prefix = ''
|
|
16
|
+
let suffix = ''
|
|
17
|
+
let defName = e.name
|
|
18
|
+
const names = defName.split('.')
|
|
19
|
+
defName = names[names.length - 1]
|
|
20
20
|
|
|
21
|
-
if (
|
|
21
|
+
if (defName) {
|
|
22
22
|
// Managed composition get compiler tag `_up`
|
|
23
|
-
let isManagedComposition = false
|
|
23
|
+
let isManagedComposition = false
|
|
24
24
|
if (e.elements) {
|
|
25
|
-
isManagedComposition = Object.keys(e.elements).some((k) => k ===
|
|
25
|
+
isManagedComposition = Object.keys(e.elements).some((k) => k === 'up_')
|
|
26
26
|
}
|
|
27
27
|
// Check for compiler tags
|
|
28
|
-
|
|
29
|
-
const isCompilerTag = compilerTagsToExclude.includes(
|
|
28
|
+
const compilerTagsToExclude = ['texts', 'localized']
|
|
29
|
+
const isCompilerTag = compilerTagsToExclude.includes(defName)
|
|
30
30
|
|
|
31
31
|
if (isManagedComposition || isCompilerTag) {
|
|
32
|
-
suffix = names[names.length - 1]
|
|
33
|
-
|
|
32
|
+
suffix = names[names.length - 1]
|
|
33
|
+
defName = names[names.length - 2]
|
|
34
34
|
}
|
|
35
|
-
prefix = e.name.split(`.${
|
|
35
|
+
prefix = e.name.split(`.${defName}`)[0]
|
|
36
36
|
}
|
|
37
|
-
return { prefix,
|
|
37
|
+
return { prefix, name: defName, suffix }
|
|
38
38
|
},
|
|
39
39
|
|
|
40
40
|
_findInCode: function (miss, code) {
|
|
41
41
|
// middle
|
|
42
|
-
let match = new RegExp(SEP + miss + SEP).exec(code)
|
|
43
|
-
if (match) return match.index + 1
|
|
42
|
+
let match = new RegExp(SEP + miss + SEP).exec(code)
|
|
43
|
+
if (match) return match.index + 1
|
|
44
44
|
// end of line
|
|
45
|
-
match = new RegExp(SEP + miss + EOL).exec(code)
|
|
46
|
-
if (match) return match.index + 1
|
|
45
|
+
match = new RegExp(SEP + miss + EOL).exec(code)
|
|
46
|
+
if (match) return match.index + 1
|
|
47
47
|
// start of doc
|
|
48
|
-
match = new RegExp(
|
|
49
|
-
if (match) return match.index
|
|
48
|
+
match = new RegExp('^' + miss + SEP).exec(code)
|
|
49
|
+
if (match) return match.index
|
|
50
50
|
// somewhere (fallback)
|
|
51
|
-
return code.indexOf(miss)
|
|
51
|
+
return code.indexOf(miss)
|
|
52
52
|
},
|
|
53
53
|
|
|
54
54
|
isEmptyString: function (value) {
|
|
55
|
-
if (typeof value !==
|
|
56
|
-
return false
|
|
55
|
+
if (typeof value !== 'string' || (typeof value === 'string' && value && value.length > 0)) {
|
|
56
|
+
return false
|
|
57
57
|
}
|
|
58
|
-
return true
|
|
58
|
+
return true
|
|
59
59
|
},
|
|
60
60
|
|
|
61
61
|
isEmptyObject: function (value) {
|
|
62
|
-
function isEmpty(object) {
|
|
63
|
-
|
|
64
|
-
return false
|
|
62
|
+
function isEmpty (object) {
|
|
63
|
+
if (Object.keys(object).length) {
|
|
64
|
+
return false
|
|
65
65
|
}
|
|
66
|
-
return true
|
|
66
|
+
return true
|
|
67
67
|
}
|
|
68
|
-
if (typeof value !==
|
|
69
|
-
(typeof value ===
|
|
70
|
-
return false
|
|
68
|
+
if (typeof value !== 'object' || (typeof value === 'object' && !isEmpty(value)) ||
|
|
69
|
+
(typeof value === 'object' && value && value.length > 0)) {
|
|
70
|
+
return false
|
|
71
71
|
}
|
|
72
|
-
return true
|
|
72
|
+
return true
|
|
73
73
|
},
|
|
74
74
|
|
|
75
|
-
isStringInArray(str, arr, caps=false) {
|
|
76
|
-
const notIncluded = !arr.includes(str)
|
|
75
|
+
isStringInArray (str, arr, caps = false) {
|
|
76
|
+
const notIncluded = !arr.includes(str)
|
|
77
77
|
if (!caps && notIncluded) {
|
|
78
|
-
return false
|
|
78
|
+
return false
|
|
79
79
|
}
|
|
80
|
-
if (caps && notIncluded && !arr.includes(str.toLowerCase()) && !arr.includes(str.toUpperCase()))
|
|
81
|
-
return false
|
|
80
|
+
if (caps && notIncluded && !arr.includes(str.toLowerCase()) && !arr.includes(str.toUpperCase())) {
|
|
81
|
+
return false
|
|
82
82
|
}
|
|
83
|
-
return true
|
|
83
|
+
return true
|
|
84
84
|
},
|
|
85
85
|
|
|
86
86
|
getReplacementsSuggestions: function (context, value, loc) {
|
|
87
|
-
let invalid
|
|
88
|
-
const lineToReplace = context.sourcecode.lines[loc.line]
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
let invalid
|
|
88
|
+
const lineToReplace = context.sourcecode.lines[loc.line]
|
|
89
|
+
const regExp = /\[([^)]+)\]/
|
|
90
|
+
const matches = regExp.exec(lineToReplace)
|
|
91
91
|
if (matches && matches[0]) {
|
|
92
|
-
invalid = matches[0]
|
|
92
|
+
invalid = matches[0]
|
|
93
93
|
}
|
|
94
|
-
const startIndex = lineToReplace.indexOf(invalid)
|
|
95
|
-
const candidates = `['${value}']
|
|
94
|
+
const startIndex = lineToReplace.indexOf(invalid)
|
|
95
|
+
const candidates = `['${value}']`
|
|
96
96
|
const suggest = {
|
|
97
|
-
messageId:
|
|
97
|
+
messageId: 'ReplaceItemWith',
|
|
98
98
|
data: { invalid, candidates },
|
|
99
|
-
fix: (fixer) => fixer.replaceTextRange([startIndex, startIndex + invalid.length] + 1, candidates)
|
|
100
|
-
}
|
|
99
|
+
fix: (fixer) => fixer.replaceTextRange([startIndex, startIndex + invalid.length] + 1, candidates)
|
|
100
|
+
}
|
|
101
101
|
return ({
|
|
102
|
-
messageId:
|
|
102
|
+
messageId: 'InvalidItem',
|
|
103
103
|
data: { invalid, candidates },
|
|
104
104
|
loc: {
|
|
105
105
|
start: { line: loc.line + 1, column: startIndex },
|
|
106
|
-
end: { line: loc.line + 1, column: startIndex + invalid.length }
|
|
106
|
+
end: { line: loc.line + 1, column: startIndex + invalid.length }
|
|
107
107
|
},
|
|
108
108
|
file: loc.file,
|
|
109
109
|
suggest,
|
|
110
|
-
severity:
|
|
111
|
-
})
|
|
112
|
-
}
|
|
113
|
-
}
|
|
110
|
+
severity: 'warn'
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
const fs = require(
|
|
2
|
-
const path = require(
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
3
|
|
|
4
|
-
const { RuleTester } = require(
|
|
5
|
-
const Cache = require(
|
|
6
|
-
const createRule = require(
|
|
7
|
-
const isConfiguredFileType = require(
|
|
8
|
-
const { compileModelFromDict } = require(
|
|
4
|
+
const { RuleTester } = require('eslint')
|
|
5
|
+
const Cache = require('./Cache')
|
|
6
|
+
const createRule = require('./createRule')
|
|
7
|
+
const isConfiguredFileType = require('./isConfiguredFileType')
|
|
8
|
+
const { compileModelFromDict } = require('../parser')
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* ESLint RuleTester (used by custom rule creator api)
|
|
@@ -13,99 +13,104 @@ const { compileModelFromDict } = require("../parser");
|
|
|
13
13
|
* valid/invalid checks:
|
|
14
14
|
* Model checks require input 'code' entries
|
|
15
15
|
* Env checks require input 'options' with selected parameters
|
|
16
|
-
* @param {CDSRuleTestOpts} options RuleTester input options
|
|
16
|
+
* @param { CDSRuleTestOpts } options RuleTester input options
|
|
17
17
|
* @returns RuleTester results
|
|
18
18
|
*/
|
|
19
19
|
module.exports = (options) => {
|
|
20
|
-
let parser
|
|
21
|
-
let rule = {}
|
|
22
|
-
Cache.set(
|
|
23
|
-
const rulename = path.basename(options.root)
|
|
24
|
-
if (options.root.startsWith(path.resolve(__dirname,
|
|
20
|
+
let parser
|
|
21
|
+
let rule = {}
|
|
22
|
+
Cache.set('rules', require(path.join(__dirname, '../rules')))
|
|
23
|
+
const rulename = path.basename(options.root)
|
|
24
|
+
if (options.root.startsWith(path.resolve(__dirname, '../..'))) {
|
|
25
25
|
// For plugin's internal tests, resolve parser from here
|
|
26
|
-
parser = require.resolve(
|
|
27
|
-
const pluginPath = path.join(path.dirname(options.root),
|
|
28
|
-
rule = createRule(require(`../rules/${path.basename(options.root)}`))
|
|
29
|
-
Cache.set(
|
|
26
|
+
parser = require.resolve('../parser')
|
|
27
|
+
const pluginPath = path.join(path.dirname(options.root), '../..')
|
|
28
|
+
rule = createRule(require(`../rules/${path.basename(options.root)}`))
|
|
29
|
+
Cache.set('pluginpath', pluginPath)
|
|
30
30
|
} else {
|
|
31
31
|
// Otherwise from project root
|
|
32
|
-
const resolvedPlugin = require.resolve(
|
|
33
|
-
paths: [options.root]
|
|
34
|
-
})
|
|
35
|
-
parser = path.join(path.dirname(resolvedPlugin),
|
|
36
|
-
rule = require(path.join(options.root,
|
|
37
|
-
const pluginPath = path.join(path.dirname(options.root),
|
|
38
|
-
Cache.set(
|
|
32
|
+
const resolvedPlugin = require.resolve('@sap/eslint-plugin-cds', {
|
|
33
|
+
paths: [options.root]
|
|
34
|
+
})
|
|
35
|
+
parser = path.join(path.dirname(resolvedPlugin), 'parser')
|
|
36
|
+
rule = require(path.join(options.root, `../../rules/${path.basename(options.root)}`))
|
|
37
|
+
const pluginPath = path.join(path.dirname(options.root), '../../..')
|
|
38
|
+
Cache.set('pluginpath', pluginPath)
|
|
39
39
|
}
|
|
40
|
-
let tester = new RuleTester({})
|
|
40
|
+
let tester = new RuleTester({})
|
|
41
41
|
if (parser) {
|
|
42
|
-
tester = new RuleTester({ parser })
|
|
42
|
+
tester = new RuleTester({ parser })
|
|
43
43
|
}
|
|
44
44
|
const testerCases = {};
|
|
45
|
-
[
|
|
46
|
-
const filePath = path.join(options.root, `${type}/${options.filename}`)
|
|
47
|
-
Cache.set(
|
|
48
|
-
_initModelRuleTester(filePath)
|
|
45
|
+
['valid', 'invalid'].forEach((type) => {
|
|
46
|
+
const filePath = path.join(options.root, `${type}/${options.filename}`)
|
|
47
|
+
Cache.set('rootpath', path.dirname(filePath))
|
|
48
|
+
_initModelRuleTester(filePath, rule.meta?.model)
|
|
49
49
|
testerCases[type] = [
|
|
50
50
|
{
|
|
51
|
-
filename: filePath
|
|
52
|
-
}
|
|
53
|
-
]
|
|
54
|
-
if (!isConfiguredFileType(options.filename,
|
|
55
|
-
const fileContents = JSON.parse(fs.readFileSync(filePath,
|
|
56
|
-
testerCases[type][0].code =
|
|
57
|
-
testerCases[type][0].filename =
|
|
58
|
-
testerCases[type][0].options = [{ environment: fileContents }]
|
|
51
|
+
filename: filePath
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
if (!isConfiguredFileType(options.filename, 'FILES')) {
|
|
55
|
+
const fileContents = JSON.parse(fs.readFileSync(filePath, 'utf8'))
|
|
56
|
+
testerCases[type][0].code = ''
|
|
57
|
+
testerCases[type][0].filename = '<text>'
|
|
58
|
+
testerCases[type][0].options = [{ environment: fileContents }]
|
|
59
59
|
} else {
|
|
60
|
-
testerCases[type][0].code = fs.readFileSync(filePath,
|
|
60
|
+
testerCases[type][0].code = fs.readFileSync(filePath, 'utf8')
|
|
61
61
|
if (options.options) {
|
|
62
|
-
testerCases[type][0].options = options.options
|
|
62
|
+
testerCases[type][0].options = options.options
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
-
if (type ===
|
|
66
|
-
testerCases[type][0].errors = options.errors
|
|
67
|
-
const fileFixed = path.join(options.root, `fixed/${options.filename}`)
|
|
68
|
-
if (fs.existsSync(fileFixed) && rule.meta.type !==
|
|
69
|
-
testerCases[type][0].output = fs.readFileSync(fileFixed,
|
|
65
|
+
if (type === 'invalid') {
|
|
66
|
+
testerCases[type][0].errors = options.errors
|
|
67
|
+
const fileFixed = path.join(options.root, `fixed/${options.filename}`)
|
|
68
|
+
if (fs.existsSync(fileFixed) && rule.meta.type !== 'suggestion') {
|
|
69
|
+
testerCases[type][0].output = fs.readFileSync(fileFixed, 'utf8')
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
-
})
|
|
73
|
-
|
|
72
|
+
})
|
|
73
|
+
if (Cache.get('testerCases')) {
|
|
74
|
+
Cache.set(`testerCases:${rulename}`, testerCases)
|
|
75
|
+
}
|
|
76
|
+
return tester.run(rulename, rule, testerCases)
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
/**
|
|
77
80
|
* Creates a model for ESLint unit tests
|
|
78
81
|
*/
|
|
79
|
-
function _initModelRuleTester(filePath) {
|
|
80
|
-
Cache.set(
|
|
81
|
-
const rootPath = path.dirname(filePath)
|
|
82
|
-
Cache.set(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
82
|
+
function _initModelRuleTester (filePath, flavor) {
|
|
83
|
+
Cache.set('test', true)
|
|
84
|
+
const rootPath = path.dirname(filePath)
|
|
85
|
+
Cache.set('rootpath', rootPath)
|
|
86
|
+
if (flavor !== 'none') { // not for env rules
|
|
87
|
+
const files = fs.readdirSync(rootPath)
|
|
88
|
+
const modelfiles = files.map((f) => path.join(rootPath, f)).filter((fp) => isConfiguredFileType(fp, 'MODEL_FILES'))
|
|
89
|
+
Cache.set(`modelfiles:${rootPath}`, modelfiles)
|
|
90
|
+
const dictFiles = _getDictFiles(rootPath, modelfiles)
|
|
91
|
+
Cache.set(`dictfiles:${rootPath}`, dictFiles)
|
|
92
|
+
const reflectedModel = compileModelFromDict(dictFiles, { flavor })
|
|
93
|
+
Cache.set(`model:${rootPath}`, reflectedModel)
|
|
94
|
+
}
|
|
90
95
|
}
|
|
91
96
|
|
|
92
|
-
|
|
97
|
+
/**
|
|
93
98
|
* Creates or updates a dictionary of files/file contents for a given
|
|
94
99
|
* project path.
|
|
95
100
|
* @param input
|
|
96
101
|
* @param files
|
|
97
102
|
* @returns dictFiles
|
|
98
103
|
*/
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
return dictFiles;
|
|
104
|
+
function _getDictFiles (input, files) {
|
|
105
|
+
let dictFiles = {}
|
|
106
|
+
if (Cache.has(`dictfiles:${input}`)) {
|
|
107
|
+
dictFiles = Cache.get(`dictfiles:${input}`)
|
|
108
|
+
} else {
|
|
109
|
+
files.forEach((file) => {
|
|
110
|
+
dictFiles[file] = Cache.has(`file:${file}`)
|
|
111
|
+
? Cache.get(`file:${file}`)
|
|
112
|
+
: fs.readFileSync(file, 'utf8')
|
|
113
|
+
})
|
|
111
114
|
}
|
|
115
|
+
return dictFiles
|
|
116
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/eslint-plugin-cds",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.1",
|
|
4
4
|
"description": "ESLint plugin including recommended SAP Cloud Application Programming model and environment rules",
|
|
5
5
|
"homepage": "https://cap.cloud.sap/",
|
|
6
6
|
"keywords": [
|
|
@@ -23,6 +23,12 @@
|
|
|
23
23
|
"@sap/cds": ">=5.6.0",
|
|
24
24
|
"semver": "^7.3.4"
|
|
25
25
|
},
|
|
26
|
+
"eslintConfig": {
|
|
27
|
+
"extends": [
|
|
28
|
+
"eslint:recommended",
|
|
29
|
+
"standard"
|
|
30
|
+
]
|
|
31
|
+
},
|
|
26
32
|
"peerDependencies": {
|
|
27
33
|
"eslint": ">=7"
|
|
28
34
|
},
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
meta: {
|
|
3
|
-
docs: {
|
|
4
|
-
description: `Draft-enabled entities shall not be used in views that make use of \`JOIN\`.`,
|
|
5
|
-
recommended: true
|
|
6
|
-
},
|
|
7
|
-
type: "suggestion",
|
|
8
|
-
model: "inferred"
|
|
9
|
-
},
|
|
10
|
-
create: function (context) {
|
|
11
|
-
return { entity: check_nojoin_draftenabled };
|
|
12
|
-
|
|
13
|
-
function check_nojoin_draftenabled(e) {
|
|
14
|
-
if (e["@odata.draft.enabled"]) {
|
|
15
|
-
if (e.query.SELECT.from.join) {
|
|
16
|
-
context.report({
|
|
17
|
-
message: `Do not use draft-enabled entities in views that make use of \`JOIN\`.`,
|
|
18
|
-
node: context.getNode(e),
|
|
19
|
-
file: e.$location.file
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
};
|