@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,23 +1,24 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
meta: {
|
|
3
|
+
schema: [{/* to avoid deprecation warning for ESLint 9 */}],
|
|
3
4
|
docs: {
|
|
4
|
-
description:
|
|
5
|
+
description: 'Names must not start with $ to avoid possible shadowing of reserved variables.',
|
|
5
6
|
recommended: true
|
|
6
7
|
},
|
|
7
|
-
type:
|
|
8
|
+
type: 'problem'
|
|
8
9
|
},
|
|
9
|
-
create(context) {
|
|
10
|
-
return { element: _check }
|
|
10
|
+
create (context) {
|
|
11
|
+
return { element: _check }
|
|
11
12
|
|
|
12
|
-
function _check(d) {
|
|
13
|
-
|
|
14
|
-
if (srv && srv[
|
|
15
|
-
if (d.name.startsWith(
|
|
13
|
+
function _check (d) {
|
|
14
|
+
const srv = d._service || (d.parent && d.parent._service)
|
|
15
|
+
if (srv && srv['@cds.external']) return
|
|
16
|
+
if (d.name.startsWith('$')) {
|
|
16
17
|
context.report({
|
|
17
|
-
message:
|
|
18
|
+
message: `'${d.name}' is prefixed with a dollar sign ($)`,
|
|
18
19
|
node: context.getNode(d)
|
|
19
|
-
})
|
|
20
|
+
})
|
|
20
21
|
}
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
|
-
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
meta: {
|
|
3
|
+
schema: [{/* to avoid deprecation warning for ESLint 9 */}],
|
|
4
|
+
docs: {
|
|
5
|
+
// eslint-disable-next-line quotes
|
|
6
|
+
description: "Draft-enabled entities shall not be used in views that make use of `JOIN`.",
|
|
7
|
+
recommended: true
|
|
8
|
+
},
|
|
9
|
+
type: 'suggestion',
|
|
10
|
+
model: 'inferred'
|
|
11
|
+
},
|
|
12
|
+
create: function (context) {
|
|
13
|
+
return { entity: checkNojoinDraftenabled }
|
|
14
|
+
|
|
15
|
+
function checkNojoinDraftenabled (e) {
|
|
16
|
+
if (e['@odata.draft.enabled']) {
|
|
17
|
+
if (e.query.SELECT.from.join) {
|
|
18
|
+
context.report({
|
|
19
|
+
message: 'Do not use draft-enabled entities in views that make use of `JOIN`.',
|
|
20
|
+
node: context.getNode(e),
|
|
21
|
+
file: e.$location.file
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
meta: {
|
|
3
|
+
schema: [{/* to avoid deprecation warning for ESLint 9 */}],
|
|
3
4
|
docs: {
|
|
4
|
-
description:
|
|
5
|
+
description: 'Foreign key information of a `TO MANY` relationship must be defined within the target and specified in an `ON` condition.',
|
|
5
6
|
recommended: true
|
|
6
7
|
},
|
|
7
|
-
type:
|
|
8
|
+
type: 'problem'
|
|
8
9
|
},
|
|
9
10
|
create: function (context) {
|
|
10
|
-
return { element:
|
|
11
|
+
return { element: check2manyOncond }
|
|
11
12
|
|
|
12
|
-
function
|
|
13
|
-
if (e.is2many && !e.on && typeof e.target ===
|
|
13
|
+
function check2manyOncond (e) {
|
|
14
|
+
if (e.is2many && !e.on && typeof e.target === 'string') {
|
|
14
15
|
context.report({
|
|
15
16
|
message: `You must provide an \`ON\` condition for \`TO MANY\` relationship '${e.name}'.`,
|
|
16
17
|
node: context.getNode(e)
|
|
17
|
-
})
|
|
18
|
+
})
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
|
-
|
|
21
21
|
}
|
|
22
|
-
}
|
|
22
|
+
}
|
|
@@ -1,38 +1,39 @@
|
|
|
1
1
|
/* eslint-disable no-undef */
|
|
2
2
|
module.exports = {
|
|
3
3
|
meta: {
|
|
4
|
+
schema: [{/* to avoid deprecation warning for ESLint 9 */}],
|
|
4
5
|
docs: {
|
|
5
|
-
description:
|
|
6
|
+
description: 'Should make suggestions for possible missing SQL casts.',
|
|
6
7
|
recommended: true
|
|
7
8
|
},
|
|
8
|
-
type:
|
|
9
|
+
type: 'suggestion',
|
|
9
10
|
hasSuggestions: true,
|
|
10
11
|
messages: {
|
|
11
|
-
missingSQLCast:
|
|
12
|
-
}
|
|
12
|
+
missingSQLCast: 'Potential issue - Missing SQL cast for column expression?'
|
|
13
|
+
}
|
|
13
14
|
},
|
|
14
15
|
create: function (context) {
|
|
15
|
-
return { view:
|
|
16
|
+
return { view: checkSqlCast }
|
|
16
17
|
|
|
17
|
-
function
|
|
18
|
+
function checkSqlCast (v) {
|
|
18
19
|
if (v.query && v.query.SET) {
|
|
19
20
|
for (const { SELECT } of v.query.SET.args) {
|
|
20
21
|
// Only in UNION cases?
|
|
21
22
|
for (const each of SELECT.columns || []) {
|
|
22
|
-
const { xpr, cast } = each
|
|
23
|
+
const { xpr, cast } = each
|
|
23
24
|
if (cast && xpr) {
|
|
24
25
|
if (xpr[0].xpr && xpr[0].cast) {
|
|
25
|
-
continue
|
|
26
|
+
continue
|
|
26
27
|
} else {
|
|
27
28
|
context.report({
|
|
28
|
-
messageId:
|
|
29
|
+
messageId: 'missingSQLCast',
|
|
29
30
|
node: context.getNode(v)
|
|
30
|
-
})
|
|
31
|
+
})
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -1,58 +1,59 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
meta: {
|
|
3
|
+
schema: [{/* to avoid deprecation warning for ESLint 9 */}],
|
|
3
4
|
docs: {
|
|
4
|
-
description:
|
|
5
|
+
description: 'Regular element names should start with lowercase letters.'
|
|
5
6
|
},
|
|
6
|
-
type:
|
|
7
|
+
type: 'suggestion',
|
|
7
8
|
hasSuggestions: true,
|
|
8
9
|
messages: {
|
|
9
10
|
startLowercase: "Element name '{{entityName}}.{{elementName}}' should start with a lowercase letter.",
|
|
10
|
-
fixLowercase:
|
|
11
|
+
fixLowercase: 'Start element name with a lowercase letter.'
|
|
11
12
|
},
|
|
12
|
-
fixable:
|
|
13
|
+
fixable: 'code'
|
|
13
14
|
},
|
|
14
15
|
create: function (context) {
|
|
15
|
-
const sourcecode = context.getSourceCode()
|
|
16
|
+
const sourcecode = context.getSourceCode()
|
|
16
17
|
|
|
17
18
|
return {
|
|
18
|
-
element:
|
|
19
|
-
}
|
|
19
|
+
element: checkStartLowercase
|
|
20
|
+
}
|
|
20
21
|
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
};
|
|
38
|
-
context.report({
|
|
39
|
-
messageId: "startLowercase",
|
|
40
|
-
loc,
|
|
41
|
-
file,
|
|
42
|
-
data: {
|
|
43
|
-
entityName,
|
|
44
|
-
elementName,
|
|
45
|
-
},
|
|
46
|
-
suggest: [
|
|
47
|
-
{
|
|
48
|
-
messageId: "fixLowercase",
|
|
49
|
-
fix,
|
|
50
|
-
},
|
|
51
|
-
],
|
|
52
|
-
});
|
|
22
|
+
function checkStartLowercase (e) {
|
|
23
|
+
const elementName = e.name
|
|
24
|
+
const entityName = e.parent.name
|
|
25
|
+
if (elementName && !(entityName.startsWith('localized') || entityName.endsWith('texts'))) {
|
|
26
|
+
if (elementName.charAt(0) !== elementName.charAt(0).toLowerCase() && !['ID'].includes(elementName)) {
|
|
27
|
+
if (e.$location && e.$location.file) {
|
|
28
|
+
const file = e.$location.file
|
|
29
|
+
const loc = context.getLocation(elementName, e)
|
|
30
|
+
const fix = (fixer, source = sourcecode) => {
|
|
31
|
+
const elementNameSanitized = elementName.charAt(0).toLowerCase() + elementName.slice(1)
|
|
32
|
+
const rangeEnd = source.getIndexFromLoc({
|
|
33
|
+
line: loc.end.line,
|
|
34
|
+
column: loc.end.column
|
|
35
|
+
})
|
|
36
|
+
const rangeBeg = rangeEnd ? rangeEnd - elementNameSanitized.length : 0
|
|
37
|
+
return fixer.replaceTextRange([rangeBeg, rangeEnd], elementNameSanitized)
|
|
53
38
|
}
|
|
39
|
+
context.report({
|
|
40
|
+
messageId: 'startLowercase',
|
|
41
|
+
loc,
|
|
42
|
+
file,
|
|
43
|
+
data: {
|
|
44
|
+
entityName,
|
|
45
|
+
elementName
|
|
46
|
+
},
|
|
47
|
+
suggest: [
|
|
48
|
+
{
|
|
49
|
+
messageId: 'fixLowercase',
|
|
50
|
+
fix
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
})
|
|
54
|
+
}
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -1,52 +1,53 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { splitDefName } = require('../utils/rules')
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
4
|
meta: {
|
|
5
|
+
schema: [{/* to avoid deprecation warning for ESLint 9 */}],
|
|
5
6
|
docs: {
|
|
6
|
-
description:
|
|
7
|
+
description: 'Regular entity names should start with uppercase letters.'
|
|
7
8
|
},
|
|
8
|
-
type:
|
|
9
|
+
type: 'suggestion',
|
|
9
10
|
hasSuggestions: true,
|
|
10
11
|
messages: {
|
|
11
12
|
startUppercase: "Entity name '{{entityName}}' should start with an uppercase letter.",
|
|
12
|
-
fixUppercase:
|
|
13
|
+
fixUppercase: 'Start entity name with an uppercase letter.'
|
|
13
14
|
},
|
|
14
|
-
fixable:
|
|
15
|
+
fixable: 'code'
|
|
15
16
|
},
|
|
16
17
|
create: function (context) {
|
|
17
|
-
const sourcecode = context.getSourceCode()
|
|
18
|
+
const sourcecode = context.getSourceCode()
|
|
18
19
|
|
|
19
|
-
return { entity:
|
|
20
|
+
return { entity: checkStartsUppercase }
|
|
20
21
|
|
|
21
|
-
function
|
|
22
|
-
const entityName =
|
|
22
|
+
function checkStartsUppercase (e) {
|
|
23
|
+
const entityName = splitDefName(e).name
|
|
23
24
|
if (entityName.charAt(0) !== entityName.charAt(0).toUpperCase()) {
|
|
24
25
|
if (e.$location && e.$location.file) {
|
|
25
|
-
const file = e.$location.file
|
|
26
|
-
const loc = context.getLocation(entityName, e)
|
|
26
|
+
const file = e.$location.file
|
|
27
|
+
const loc = context.getLocation(entityName, e)
|
|
27
28
|
const fix = (fixer) => {
|
|
28
|
-
const entityNameSanitized = entityName.charAt(0).toUpperCase() + entityName.slice(1)
|
|
29
|
+
const entityNameSanitized = entityName.charAt(0).toUpperCase() + entityName.slice(1)
|
|
29
30
|
const rangeEnd = sourcecode.getIndexFromLoc({
|
|
30
31
|
line: loc.end.line,
|
|
31
|
-
column: loc.end.column
|
|
32
|
-
})
|
|
33
|
-
const rangeBeg = rangeEnd ? rangeEnd - entityNameSanitized.length : 0
|
|
34
|
-
return fixer.replaceTextRange([rangeBeg, rangeEnd], entityNameSanitized)
|
|
35
|
-
}
|
|
32
|
+
column: loc.end.column
|
|
33
|
+
})
|
|
34
|
+
const rangeBeg = rangeEnd ? rangeEnd - entityNameSanitized.length : 0
|
|
35
|
+
return fixer.replaceTextRange([rangeBeg, rangeEnd], entityNameSanitized)
|
|
36
|
+
}
|
|
36
37
|
context.report({
|
|
37
|
-
messageId:
|
|
38
|
+
messageId: 'startUppercase',
|
|
38
39
|
loc,
|
|
39
40
|
file,
|
|
40
41
|
data: { entityName },
|
|
41
42
|
suggest: [
|
|
42
43
|
{
|
|
43
|
-
messageId:
|
|
44
|
-
fix
|
|
45
|
-
}
|
|
46
|
-
]
|
|
47
|
-
})
|
|
44
|
+
messageId: 'fixUppercase',
|
|
45
|
+
fix
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
})
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -1,100 +1,101 @@
|
|
|
1
|
-
const cds = require(
|
|
2
|
-
const { basename, extname } = require(
|
|
3
|
-
const findFuzzy = require(
|
|
4
|
-
const SEP =
|
|
5
|
-
const EOL =
|
|
1
|
+
const cds = require('@sap/cds')
|
|
2
|
+
const { basename, extname } = require('path')
|
|
3
|
+
const findFuzzy = require('../utils/findFuzzy')
|
|
4
|
+
const SEP = '[,;\t]'
|
|
5
|
+
const EOL = '\\r?\\n'
|
|
6
6
|
|
|
7
7
|
module.exports = {
|
|
8
8
|
meta: {
|
|
9
|
+
schema: [{/* to avoid deprecation warning for ESLint 9 */}],
|
|
9
10
|
docs: {
|
|
10
|
-
description:
|
|
11
|
-
category:
|
|
11
|
+
description: 'CSV files for entities must refer to valid element names.',
|
|
12
|
+
category: 'Model Validation',
|
|
12
13
|
recommended: true
|
|
13
14
|
},
|
|
14
|
-
severity:
|
|
15
|
-
type:
|
|
15
|
+
severity: 'warn',
|
|
16
|
+
type: 'problem',
|
|
16
17
|
hasSuggestions: true,
|
|
17
18
|
messages: {
|
|
18
|
-
InvalidColumn:
|
|
19
|
-
ReplaceColumnWith:
|
|
19
|
+
InvalidColumn: "Invalid column '{{column}}'. Did you mean '{{candidates}}'?",
|
|
20
|
+
ReplaceColumnWith: "Replace '{{column}}' with '{{candidates}}'"
|
|
20
21
|
},
|
|
21
|
-
model:
|
|
22
|
+
model: 'inferred'
|
|
22
23
|
},
|
|
23
24
|
create: function (context) {
|
|
24
|
-
return
|
|
25
|
+
return checkValidHeaders
|
|
25
26
|
|
|
26
|
-
function
|
|
27
|
-
const filePath = context.getFilename()
|
|
28
|
-
const sourcecode = context.getSourceCode()
|
|
29
|
-
const code = sourcecode.getText()
|
|
27
|
+
function checkValidHeaders () {
|
|
28
|
+
const filePath = context.getFilename()
|
|
29
|
+
const sourcecode = context.getSourceCode()
|
|
30
|
+
const code = sourcecode.getText()
|
|
30
31
|
|
|
31
|
-
let model = context.getModel()
|
|
32
|
-
if (!filePath.endsWith(
|
|
33
|
-
if (!model) return
|
|
32
|
+
let model = context.getModel()
|
|
33
|
+
if (!filePath.endsWith('.csv')) return
|
|
34
|
+
if (!model) return
|
|
34
35
|
|
|
35
|
-
model = cds.compile.for.sql(model, { names: cds.env.sql.names, messages: [] })
|
|
36
|
+
model = cds.compile.for.sql(model, { names: cds.env.sql.names, messages: [] })
|
|
36
37
|
|
|
37
|
-
const filename = basename(filePath)
|
|
38
|
-
const entityName = filename.replace(/-/g,
|
|
39
|
-
const entity = _entity4(entityName, model)
|
|
40
|
-
if (!entity) return
|
|
38
|
+
const filename = basename(filePath)
|
|
39
|
+
const entityName = filename.replace(/-/g, '.').slice(0, -extname(filename).length)
|
|
40
|
+
const entity = _entity4(entityName, model)
|
|
41
|
+
if (!entity) return
|
|
41
42
|
|
|
42
43
|
const elements = Object.values(entity.elements)
|
|
43
|
-
.filter((e) => !!e[
|
|
44
|
-
.map((e) => e[
|
|
44
|
+
.filter((e) => !!e['@cds.persistence.name'])
|
|
45
|
+
.map((e) => e['@cds.persistence.name'].toUpperCase())
|
|
45
46
|
|
|
46
|
-
const [cols] = cds.parse.csv(code)
|
|
47
|
-
const missing = cols.filter((col) => !elements.includes(col.toUpperCase()))
|
|
47
|
+
const [cols] = cds.parse.csv(code)
|
|
48
|
+
const missing = cols.filter((col) => !elements.includes(col.toUpperCase()))
|
|
48
49
|
for (const miss of missing) {
|
|
49
|
-
const index = _findInCode(miss, code)
|
|
50
|
-
const loc = sourcecode.getLocFromIndex(index)
|
|
51
|
-
const candidates = findFuzzy(miss, Object.keys(entity.elements).sort())
|
|
50
|
+
const index = _findInCode(miss, code)
|
|
51
|
+
const loc = sourcecode.getLocFromIndex(index)
|
|
52
|
+
const candidates = findFuzzy(miss, Object.keys(entity.elements).sort())
|
|
52
53
|
const suggest = candidates.map((cand) => {
|
|
53
54
|
return {
|
|
54
|
-
messageId:
|
|
55
|
+
messageId: 'ReplaceColumnWith',
|
|
55
56
|
data: { column: miss, candidates: cand },
|
|
56
|
-
fix: (fixer) => fixer.replaceTextRange([index, index + miss.length], cand)
|
|
57
|
-
}
|
|
58
|
-
})
|
|
57
|
+
fix: (fixer) => fixer.replaceTextRange([index, index + miss.length], cand)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
59
60
|
context.report({
|
|
60
|
-
messageId:
|
|
61
|
+
messageId: 'InvalidColumn',
|
|
61
62
|
data: { column: miss, candidates },
|
|
62
63
|
loc: { start: loc, end: { line: loc.line, column: loc.column + miss.length } },
|
|
63
64
|
file: filePath,
|
|
64
|
-
suggest
|
|
65
|
-
})
|
|
65
|
+
suggest
|
|
66
|
+
})
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
70
71
|
|
|
71
|
-
function _findInCode(miss, code) {
|
|
72
|
+
function _findInCode (miss, code) {
|
|
72
73
|
// middle
|
|
73
|
-
let match = new RegExp(SEP + miss + SEP).exec(code)
|
|
74
|
-
if (match) return match.index + 1
|
|
74
|
+
let match = new RegExp(SEP + miss + SEP).exec(code)
|
|
75
|
+
if (match) return match.index + 1
|
|
75
76
|
// end of line
|
|
76
|
-
match = new RegExp(SEP + miss + EOL).exec(code)
|
|
77
|
-
if (match) return match.index + 1
|
|
77
|
+
match = new RegExp(SEP + miss + EOL).exec(code)
|
|
78
|
+
if (match) return match.index + 1
|
|
78
79
|
// start of doc
|
|
79
|
-
match = new RegExp(
|
|
80
|
-
if (match) return match.index
|
|
80
|
+
match = new RegExp('^' + miss + SEP).exec(code)
|
|
81
|
+
if (match) return match.index
|
|
81
82
|
// somewhere (fallback)
|
|
82
|
-
return code.indexOf(miss)
|
|
83
|
+
return code.indexOf(miss)
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
function _entity4(name, csn) {
|
|
86
|
-
|
|
86
|
+
function _entity4 (name, csn) {
|
|
87
|
+
const entity = csn.definitions[name]
|
|
87
88
|
if (!entity) {
|
|
88
89
|
if (/(.+)[._]texts_?/.test(name)) {
|
|
89
90
|
// 'Books.texts', 'Books.texts_de'
|
|
90
|
-
const base = csn.definitions[RegExp.$1]
|
|
91
|
-
return base && _entity4(base.elements.texts.target, csn)
|
|
92
|
-
} else return
|
|
91
|
+
const base = csn.definitions[RegExp.$1]
|
|
92
|
+
return base && _entity4(base.elements.texts.target, csn)
|
|
93
|
+
} else return
|
|
93
94
|
}
|
|
94
95
|
// we also support simple views if they have no projection
|
|
95
|
-
const p = (entity.query && entity.query.SELECT) || entity.projection
|
|
96
|
+
const p = (entity.query && entity.query.SELECT) || entity.projection
|
|
96
97
|
if (p && !p.columns && p.from.ref && p.from.ref.length === 1) {
|
|
97
|
-
if (csn.definitions[p.from.ref[0]]) return entity
|
|
98
|
+
if (csn.definitions[p.from.ref[0]]) return entity
|
|
98
99
|
}
|
|
99
|
-
return entity.name ? entity : { name, __proto__: entity }
|
|
100
|
+
return entity.name ? entity : { name, __proto__: entity }
|
|
100
101
|
}
|
package/lib/types.d.ts
CHANGED
package/lib/utils/Cache.js
CHANGED
|
@@ -2,32 +2,32 @@
|
|
|
2
2
|
* Simple cache to store model and any cds calls made in the rule creation
|
|
3
3
|
* api to modify the model
|
|
4
4
|
*/
|
|
5
|
-
const cache = new Map()
|
|
5
|
+
const cache = new Map()
|
|
6
6
|
|
|
7
7
|
module.exports = {
|
|
8
|
-
has(key) {
|
|
9
|
-
return cache.has(key)
|
|
8
|
+
has (key) {
|
|
9
|
+
return cache.has(key)
|
|
10
10
|
},
|
|
11
|
-
set(key, value) {
|
|
12
|
-
return cache.set(key, [value, Date.now()])
|
|
11
|
+
set (key, value) {
|
|
12
|
+
return cache.set(key, [value, Date.now()])
|
|
13
13
|
},
|
|
14
|
-
get(key) {
|
|
15
|
-
return cache.get(key) ? cache.get(key)[0] : undefined
|
|
14
|
+
get (key) {
|
|
15
|
+
return cache.get(key) ? cache.get(key)[0] : undefined
|
|
16
16
|
},
|
|
17
|
-
dump() {
|
|
18
|
-
const dump = {}
|
|
17
|
+
dump () {
|
|
18
|
+
const dump = {}
|
|
19
19
|
for (const [key, value] of cache.entries()) {
|
|
20
|
-
const timestamp = new Date(value[1])
|
|
21
|
-
dump[key] = { key, value: JSON.stringify(value[0]), timestamp }
|
|
20
|
+
const timestamp = new Date(value[1])
|
|
21
|
+
dump[key] = { key, value: JSON.stringify(value[0]), timestamp }
|
|
22
22
|
}
|
|
23
|
-
return dump
|
|
23
|
+
return dump
|
|
24
24
|
},
|
|
25
|
-
remove(key) {
|
|
25
|
+
remove (key) {
|
|
26
26
|
if (cache.has(key)) {
|
|
27
|
-
cache.delete(key)
|
|
27
|
+
cache.delete(key)
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
|
-
clear() {
|
|
31
|
-
cache.clear()
|
|
32
|
-
}
|
|
30
|
+
clear () {
|
|
31
|
+
cache.clear()
|
|
32
|
+
}
|
|
33
33
|
}
|
package/lib/utils/Colors.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
module.exports = {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
reset: '\x1b[0m', // Default
|
|
3
|
+
bold: '\x1b[1m', // Bold/Bright
|
|
4
|
+
link: '\x1b[4m', // underline
|
|
5
|
+
red: '\x1b[91m', // Bright Foreground Red
|
|
6
|
+
green: '\x1b[32m', // Foreground Green
|
|
7
|
+
blue: '\x1b[34m', // Foreground Blue
|
|
8
|
+
orange: '\x1b[38;2;255;140;0m' // darker orange, works with bright and dark background
|
|
9
|
+
}
|