@sap/cds-compiler 3.5.4 → 3.6.2
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 +65 -2
- package/bin/cdsc.js +14 -6
- package/doc/CHANGELOG_ARCHIVE.md +10 -10
- package/doc/CHANGELOG_DEPRECATED.md +2 -2
- package/lib/api/main.js +32 -55
- package/lib/api/options.js +3 -2
- package/lib/api/validate.js +5 -0
- package/lib/base/message-registry.js +104 -32
- package/lib/base/messages.js +277 -212
- package/lib/base/optionProcessorHelper.js +9 -2
- package/lib/base/shuffle.js +50 -0
- package/lib/checks/actionsFunctions.js +37 -20
- package/lib/checks/foreignKeys.js +13 -6
- package/lib/checks/nonexpandableStructured.js +1 -2
- package/lib/checks/onConditions.js +21 -19
- package/lib/checks/parameters.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -0
- package/lib/checks/types.js +16 -22
- package/lib/compiler/assert-consistency.js +31 -28
- package/lib/compiler/builtins.js +20 -4
- package/lib/compiler/checks.js +72 -63
- package/lib/compiler/define.js +396 -314
- package/lib/compiler/extend.js +55 -49
- package/lib/compiler/index.js +5 -0
- package/lib/compiler/populate.js +28 -11
- package/lib/compiler/propagator.js +2 -1
- package/lib/compiler/resolve.js +28 -13
- package/lib/compiler/shared.js +15 -10
- package/lib/compiler/utils.js +7 -7
- package/lib/edm/annotations/genericTranslation.js +51 -46
- package/lib/edm/annotations/preprocessAnnotations.js +37 -40
- package/lib/edm/csn2edm.js +69 -21
- package/lib/edm/edm.js +2 -2
- package/lib/edm/edmInboundChecks.js +6 -8
- package/lib/edm/edmPreprocessor.js +88 -80
- package/lib/edm/edmUtils.js +6 -15
- package/lib/gen/Dictionary.json +81 -13
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +2 -1
- package/lib/gen/languageParser.js +4680 -4484
- package/lib/inspect/inspectModelStatistics.js +2 -1
- package/lib/inspect/inspectPropagation.js +2 -1
- package/lib/json/from-csn.js +131 -78
- package/lib/json/to-csn.js +39 -23
- package/lib/language/antlrParser.js +0 -3
- package/lib/language/docCommentParser.js +7 -3
- package/lib/language/errorStrategy.js +3 -2
- package/lib/language/genericAntlrParser.js +96 -41
- package/lib/language/language.g4 +112 -128
- package/lib/language/multiLineStringParser.js +2 -1
- package/lib/main.d.ts +115 -2
- package/lib/main.js +16 -3
- package/lib/model/csnRefs.js +32 -4
- package/lib/model/csnUtils.js +109 -179
- package/lib/model/enrichCsn.js +13 -8
- package/lib/model/revealInternalProperties.js +4 -3
- package/lib/optionProcessor.js +22 -3
- package/lib/render/manageConstraints.js +11 -15
- package/lib/render/toCdl.js +144 -47
- package/lib/render/toHdbcds.js +22 -22
- package/lib/render/toRename.js +3 -4
- package/lib/render/toSql.js +31 -22
- package/lib/render/utils/delta.js +3 -1
- package/lib/render/utils/sql.js +2 -14
- package/lib/transform/db/associations.js +6 -6
- package/lib/transform/db/cdsPersistence.js +3 -3
- package/lib/transform/db/constraints.js +4 -6
- package/lib/transform/db/expansion.js +4 -4
- package/lib/transform/db/flattening.js +12 -15
- package/lib/transform/db/temporal.js +4 -3
- package/lib/transform/db/transformExists.js +13 -7
- package/lib/transform/draft/db.js +7 -7
- package/lib/transform/forOdataNew.js +15 -4
- package/lib/transform/forRelationalDB.js +59 -41
- package/lib/transform/odata/toFinalBaseType.js +106 -82
- package/lib/transform/odata/typesExposure.js +26 -17
- package/lib/transform/odata/utils.js +1 -1
- package/lib/transform/parseExpr.js +1 -1
- package/lib/transform/transformUtilsNew.js +33 -10
- package/lib/transform/translateAssocsToJoins.js +8 -7
- package/lib/transform/universalCsn/coreComputed.js +7 -5
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -4
- package/lib/utils/timetrace.js +2 -2
- package/package.json +1 -2
|
@@ -27,8 +27,9 @@ function checkActionOrFunction( art, artName, prop, path ) {
|
|
|
27
27
|
if (art.kind === 'entity') {
|
|
28
28
|
for (const [ actName, act ] of Object.entries(art.actions)) {
|
|
29
29
|
if (act.params) {
|
|
30
|
+
checkExplicitBindingParameter.call(this, act.params, path.concat([ 'actions', actName, 'params' ]));
|
|
30
31
|
for (const [ paramName, param ] of Object.entries(act.params))
|
|
31
|
-
checkActionOrFunctionParameter.
|
|
32
|
+
checkActionOrFunctionParameter.call(this, param, path.concat([ 'actions', actName, 'params', paramName ]), act.kind);
|
|
32
33
|
}
|
|
33
34
|
if (act.returns)
|
|
34
35
|
checkReturns.bind(this)(act.returns, path.concat([ 'actions', actName, 'returns' ]), act.kind);
|
|
@@ -37,12 +38,27 @@ function checkActionOrFunction( art, artName, prop, path ) {
|
|
|
37
38
|
else {
|
|
38
39
|
if (art.params) {
|
|
39
40
|
for (const [ paramName, param ] of Object.entries(art.params))
|
|
40
|
-
checkActionOrFunctionParameter.
|
|
41
|
+
checkActionOrFunctionParameter.call(this, param, path.concat([ 'params', paramName ]), art.kind);
|
|
41
42
|
}
|
|
42
43
|
if (art.returns)
|
|
43
44
|
checkReturns.bind(this)(art.returns, path.concat('returns'), art.kind);
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
/**
|
|
48
|
+
*
|
|
49
|
+
* @param {object} params parameter dictionary
|
|
50
|
+
* @param {CSN.Path} currPath to the action parameter
|
|
51
|
+
*/
|
|
52
|
+
function checkExplicitBindingParameter( params, currPath ) {
|
|
53
|
+
Object.entries(params).forEach(([ pn, p ], i) => {
|
|
54
|
+
const type = p.items?.type || p.type;
|
|
55
|
+
if (type === '$self' && !this.csn.definitions.$self && i > 0) {
|
|
56
|
+
this.error(null, currPath.concat(pn),
|
|
57
|
+
'Binding parameter is expected to appear on first position only');
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
46
62
|
/**
|
|
47
63
|
* Check the parameters of an action
|
|
48
64
|
*
|
|
@@ -51,28 +67,29 @@ function checkActionOrFunction( art, artName, prop, path ) {
|
|
|
51
67
|
* @param {string} actKind 'action' or 'function'
|
|
52
68
|
*/
|
|
53
69
|
function checkActionOrFunctionParameter( param, currPath, actKind ) {
|
|
54
|
-
const paramType = param.type ? this.csnUtils.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
70
|
+
const paramType = param.type ? this.csnUtils.getFinalBaseTypeWithProps(param.type) : param;
|
|
71
|
+
if (!paramType)
|
|
72
|
+
return; // no type could be resolved
|
|
73
|
+
|
|
74
|
+
// "default" is always propagated to params
|
|
75
|
+
if (param.default && !isBetaEnabled(this.options, 'optionalActionFunctionParameters')) {
|
|
76
|
+
this.message('param-default', currPath, { '#': actKind }, {
|
|
77
|
+
std: 'Artifact parameters can\'t have a default value', // Not used
|
|
78
|
+
action: 'Action parameters can\'t have a default value',
|
|
79
|
+
function: 'Function parameters can\'t have a default value',
|
|
80
|
+
});
|
|
63
81
|
}
|
|
64
82
|
|
|
65
|
-
if (
|
|
66
|
-
this.error(null, currPath, { '#': actKind },
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
});
|
|
83
|
+
if (param.type && this.csnUtils.isAssocOrComposition(param.type)) {
|
|
84
|
+
this.error(null, currPath, { '#': actKind }, {
|
|
85
|
+
std: 'An association is not allowed as this artifact\'s parameter type', // Not used
|
|
86
|
+
action: 'An association is not allowed as action\'s parameter type',
|
|
87
|
+
function: 'An association is not allowed as function\'s parameter type',
|
|
88
|
+
});
|
|
72
89
|
}
|
|
73
90
|
|
|
74
|
-
if (paramType.items
|
|
75
|
-
checkActionOrFunctionParameter.
|
|
91
|
+
if (paramType.items?.type)
|
|
92
|
+
checkActionOrFunctionParameter.call(this, paramType.items, currPath.concat('items'), actKind);
|
|
76
93
|
|
|
77
94
|
// check if the structured & user-defined is from the current service
|
|
78
95
|
checkUserDefinedType.bind(this)(paramType, param.type, currPath);
|
|
@@ -11,8 +11,9 @@ const { setProp } = require('../base/model');
|
|
|
11
11
|
* - no usage of unmanaged association as foreign keys (also not transitively)
|
|
12
12
|
*
|
|
13
13
|
* @param {object} member Member
|
|
14
|
+
* @param {string} memberName Member name
|
|
14
15
|
*/
|
|
15
|
-
function validateForeignKeys( member ) {
|
|
16
|
+
function validateForeignKeys( member, memberName ) {
|
|
16
17
|
// We have a managed association
|
|
17
18
|
const isManagedAssoc = mem => mem && mem.target && !mem.on;
|
|
18
19
|
// We have an unmanaged association
|
|
@@ -25,7 +26,7 @@ function validateForeignKeys( member ) {
|
|
|
25
26
|
if (!key._art)
|
|
26
27
|
continue;
|
|
27
28
|
// eslint-disable-next-line no-use-before-define
|
|
28
|
-
|
|
29
|
+
checkForItemsOrMissingType(key._art, key.ref.join('.'));
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
};
|
|
@@ -35,13 +36,13 @@ function validateForeignKeys( member ) {
|
|
|
35
36
|
for (const elementName of Object.keys(mem.elements)) {
|
|
36
37
|
const element = mem.elements[elementName];
|
|
37
38
|
// eslint-disable-next-line no-use-before-define
|
|
38
|
-
|
|
39
|
+
checkForItemsOrMissingType(element, elementName);
|
|
39
40
|
}
|
|
40
41
|
};
|
|
41
42
|
|
|
42
43
|
// Recursively perform the checks on an element
|
|
43
44
|
// Declared as arrow-function to keep scope the same (this value)
|
|
44
|
-
const
|
|
45
|
+
const checkForItemsOrMissingType = (mem, memName) => {
|
|
45
46
|
if (mem.items) {
|
|
46
47
|
this.error(null, member.$path, {}, 'Array-like properties must not be foreign keys');
|
|
47
48
|
}
|
|
@@ -60,14 +61,20 @@ function validateForeignKeys( member ) {
|
|
|
60
61
|
: this.csn.definitions[mem.type];
|
|
61
62
|
if (type && !type.$visited) {
|
|
62
63
|
setProp(type, '$visited', true);
|
|
63
|
-
|
|
64
|
+
checkForItemsOrMissingType(type, memName);
|
|
64
65
|
delete type.$visited;
|
|
65
66
|
}
|
|
66
67
|
}
|
|
68
|
+
else if (mem && !mem.type) {
|
|
69
|
+
const variant = member.type === 'cds.Composition' ? 'managedCompForeignKey' : 'managedAssocForeignKey';
|
|
70
|
+
this.error('check-proper-type-of', member.$path, {
|
|
71
|
+
'#': variant, name: memName, art: memberName,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
67
74
|
};
|
|
68
75
|
|
|
69
76
|
if (isManagedAssoc(member))
|
|
70
|
-
|
|
77
|
+
checkForItemsOrMissingType(member, memberName);
|
|
71
78
|
}
|
|
72
79
|
|
|
73
80
|
module.exports = validateForeignKeys;
|
|
@@ -24,8 +24,7 @@ function nonexpandableStructuredInExpression( parent, name, expression ) {
|
|
|
24
24
|
if ((_art?.elements || _art?.keys && (i === 0 || expression[i - 1] !== 'exists')) && !validStructuredElement && $scope !== '$self') { // TODO: Use $self to navigate to struct
|
|
25
25
|
this.error('ref-unexpected-structured',
|
|
26
26
|
name === 'on' ? [ ...parent.$path, name, i ] : expression[i].$path,
|
|
27
|
-
{ elemref: { ref } }
|
|
28
|
-
'Unexpected usage of structured type $(ELEMREF)');
|
|
27
|
+
{ '#': 'std', elemref: { ref } } );
|
|
29
28
|
}
|
|
30
29
|
}
|
|
31
30
|
}
|
|
@@ -103,28 +103,30 @@ function validateOnCondition( member, memberName, property, path ) {
|
|
|
103
103
|
this.error(null, csnPath, { id, elemref }, 'ON-conditions must not contain parameters, step $(ID) of path $(ELEMREF)');
|
|
104
104
|
}
|
|
105
105
|
if (_art && $scope !== '$self') {
|
|
106
|
-
|
|
106
|
+
const type = resolveArtifactType.call(this, _art);
|
|
107
|
+
if (type) {
|
|
107
108
|
// For error messages
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
109
|
+
const onPath = path.concat([ 'on', i, 'ref', ref.length - 1 ]);
|
|
110
|
+
// Paths of an ON condition may end on a structured element or an association only if:
|
|
111
|
+
// 1) Both operands in the expression end on a structured element or on
|
|
112
|
+
// a managed association (that are both expandable)
|
|
113
|
+
// 2) Path ends on an association (managed or unmanaged) and the other operand is a '$self'
|
|
113
114
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
!( /* 1) */ (
|
|
117
|
-
/* 2) */ (
|
|
118
|
-
!
|
|
115
|
+
// If this path ends structured or on an association, perform the check:
|
|
116
|
+
if ((type.target) &&
|
|
117
|
+
!( /* 1) */ (type.target && type.keys) && validStructuredElement ||
|
|
118
|
+
/* 2) */ (type.target && validDollarSelf)) &&
|
|
119
|
+
!type.virtual) {
|
|
119
120
|
// Do nothing - handled by lib/checks/nonexpandableStructured.js
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
121
|
+
}
|
|
122
|
+
else if (type.items && !type.virtual) {
|
|
123
|
+
this.error(null, onPath, { elemref: { ref } },
|
|
124
|
+
'ON-conditions can\'t use array-like elements, path $(ELEMREF)');
|
|
125
|
+
}
|
|
126
|
+
else if (type.virtual) {
|
|
127
|
+
this.error(null, onPath, { elemref: { ref } },
|
|
128
|
+
'Virtual elements can\'t be used in ON-conditions, path $(ELEMREF)');
|
|
129
|
+
}
|
|
128
130
|
}
|
|
129
131
|
}
|
|
130
132
|
}
|
package/lib/checks/parameters.js
CHANGED
|
@@ -12,7 +12,7 @@ const { isPersistedOnDatabase } = require('../model/csnUtils.js');
|
|
|
12
12
|
*/
|
|
13
13
|
function checkForParams( parent, name, params, path ) {
|
|
14
14
|
const artifact = this.csn.definitions[path[1]];
|
|
15
|
-
if (artifact.kind === 'entity' && isPersistedOnDatabase(artifact) &&
|
|
15
|
+
if (artifact.kind === 'entity' && isPersistedOnDatabase(artifact) && parent.kind === 'entity') {
|
|
16
16
|
this.error('ref-unexpected-params', [ ...path, 'params' ], { value: this.options.sqlDialect },
|
|
17
17
|
'Parameterized views can\'t be used with sqlDialect $(VALUE)');
|
|
18
18
|
}
|
package/lib/checks/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { hasAnnotationValue } = require('../model/csnUtils');
|
|
4
4
|
|
|
5
5
|
// Only to be used with validator.js - a correct this value needs to be provided!
|
|
6
6
|
|
|
@@ -46,15 +46,12 @@ function checkTypeIsScalar( member, memberName, prop, path ) {
|
|
|
46
46
|
* @param {CSN.Path} path the path to the member
|
|
47
47
|
*/
|
|
48
48
|
function checkElementTypeDefinitionHasType( member, memberName, prop, path ) {
|
|
49
|
+
if (prop !== 'elements' && prop !== 'params')
|
|
50
|
+
return; // no enum, etc.
|
|
49
51
|
// Computed elements, e.g. "1+1 as foo" in a view don't have a valid type and
|
|
50
|
-
// are skipped here.
|
|
51
|
-
// Elements in projections are not tested as well
|
|
52
|
+
// are skipped here. References to such columns are checked further below.
|
|
52
53
|
const parent = this.csn.definitions[path[1]];
|
|
53
|
-
if (parent.projection
|
|
54
|
-
return;
|
|
55
|
-
|
|
56
|
-
// should only happen with csn input, not in cdl
|
|
57
|
-
if (!hasArtifactTypeInformation(member)) {
|
|
54
|
+
if (!parent.projection && !parent.query && !hasArtifactTypeInformation(member)) {
|
|
58
55
|
errorAboutMissingType(this.error, path, memberName, true);
|
|
59
56
|
return;
|
|
60
57
|
}
|
|
@@ -63,8 +60,8 @@ function checkElementTypeDefinitionHasType( member, memberName, prop, path ) {
|
|
|
63
60
|
if (member.type) {
|
|
64
61
|
if (member.type.ref) {
|
|
65
62
|
const isSelfReference = path[1] === member.type.ref[0];
|
|
66
|
-
checkTypeOfHasProperType(
|
|
67
|
-
member, memberName, this.csn, this.error, path, isSelfReference ? member.type.ref[1] : null
|
|
63
|
+
checkTypeOfHasProperType.call(
|
|
64
|
+
this, member, memberName, this.csn, this.error, path, isSelfReference ? member.type.ref[1] : null
|
|
68
65
|
);
|
|
69
66
|
}
|
|
70
67
|
else if (member._type) {
|
|
@@ -77,7 +74,7 @@ function checkElementTypeDefinitionHasType( member, memberName, prop, path ) {
|
|
|
77
74
|
// many
|
|
78
75
|
const { items } = member;
|
|
79
76
|
if (items)
|
|
80
|
-
checkTypeOfHasProperType(items, memberName, this.csn, this.error, path );
|
|
77
|
+
checkTypeOfHasProperType.call(this, items, memberName, this.csn, this.error, path );
|
|
81
78
|
}
|
|
82
79
|
|
|
83
80
|
/**
|
|
@@ -102,14 +99,14 @@ function checkTypeDefinitionHasType( artifact, artifactName, prop, path ) {
|
|
|
102
99
|
|
|
103
100
|
// Check for `type of`
|
|
104
101
|
if (artifact.type) {
|
|
105
|
-
checkTypeOfHasProperType(artifact, artifactName, this.csn, this.error, path);
|
|
102
|
+
checkTypeOfHasProperType.call(this, artifact, artifactName, this.csn, this.error, path);
|
|
106
103
|
return;
|
|
107
104
|
}
|
|
108
105
|
|
|
109
106
|
// many
|
|
110
107
|
const { items } = artifact;
|
|
111
108
|
if (items)
|
|
112
|
-
checkTypeOfHasProperType(items, artifactName, this.csn, this.error, path );
|
|
109
|
+
checkTypeOfHasProperType.call(this, items, artifactName, this.csn, this.error, path );
|
|
113
110
|
}
|
|
114
111
|
|
|
115
112
|
|
|
@@ -129,23 +126,20 @@ function checkTypeOfHasProperType( artOrElement, name, model, error, path, deriv
|
|
|
129
126
|
if (!artOrElement.type)
|
|
130
127
|
return;
|
|
131
128
|
|
|
132
|
-
const
|
|
133
|
-
const typeOfType = getFinalBaseTypeWithProps(artOrElement.type);
|
|
129
|
+
const typeOfType = this.csnUtils.getFinalBaseTypeWithProps(artOrElement.type);
|
|
134
130
|
|
|
135
131
|
if (typeOfType === null) {
|
|
136
|
-
if (artOrElement.type.ref) {
|
|
132
|
+
if (artOrElement.type.ref && this?.options?.transformation !== 'odata') {
|
|
137
133
|
const typeOfArt = artOrElement.type.ref[0];
|
|
138
|
-
const typeOfElt = artOrElement.type.ref
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
std: 'Referred element $(NAME) of $(ART) does not contain proper type information',
|
|
142
|
-
derived: 'Referred type of $(ART) does not contain proper type information',
|
|
134
|
+
const typeOfElt = artOrElement.type.ref.slice(1).join('.');
|
|
135
|
+
error('check-proper-type-of', path, {
|
|
136
|
+
art: derivedTypeName || typeOfArt, name: typeOfElt, '#': derivedTypeName ? 'derived' : 'std',
|
|
143
137
|
});
|
|
144
138
|
}
|
|
145
139
|
}
|
|
146
140
|
else if (typeOfType && typeOfType.items) {
|
|
147
141
|
derivedTypeName = typeof artOrElement.type === 'string' ? artOrElement.type : artOrElement.type.ref[artOrElement.type.ref.length - 1];
|
|
148
|
-
checkTypeOfHasProperType(typeOfType.items, name, model, error, path, derivedTypeName);
|
|
142
|
+
checkTypeOfHasProperType.call(this, typeOfType.items, name, model, error, path, derivedTypeName);
|
|
149
143
|
}
|
|
150
144
|
}
|
|
151
145
|
|
|
@@ -75,6 +75,12 @@ const typeProperties = [
|
|
|
75
75
|
'_effectiveType',
|
|
76
76
|
];
|
|
77
77
|
|
|
78
|
+
class InternalConsistencyError extends Error {
|
|
79
|
+
constructor(msg) {
|
|
80
|
+
super(`cds-compiler XSN consistency: ${ msg }`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
78
84
|
function assertConsistency( model, stage ) {
|
|
79
85
|
const stageParser = typeof stage === 'object';
|
|
80
86
|
const options = stageParser && stage || model.options || { testMode: true };
|
|
@@ -462,7 +468,7 @@ function assertConsistency( model, stage ) {
|
|
|
462
468
|
// preliminary -----------------------------------------------------------
|
|
463
469
|
doc: {
|
|
464
470
|
kind: true,
|
|
465
|
-
test:
|
|
471
|
+
test: TODO,
|
|
466
472
|
}, // doc comment
|
|
467
473
|
'@': {
|
|
468
474
|
kind: true,
|
|
@@ -671,25 +677,27 @@ function assertConsistency( model, stage ) {
|
|
|
671
677
|
function assertProp( node, parent, prop, extraSpec, noPropertyTest ) {
|
|
672
678
|
let spec = extraSpec || schema[prop] || schema[prop.charAt(0)];
|
|
673
679
|
if (!spec)
|
|
674
|
-
throw new
|
|
680
|
+
throw new InternalConsistencyError( `Property '${ prop }' has not been specified`);
|
|
675
681
|
spec = inheritSpec( spec );
|
|
676
682
|
|
|
677
683
|
if (!noPropertyTest) {
|
|
678
684
|
const char = prop.charAt(0);
|
|
679
685
|
const parser = ('parser' in spec) ? spec.parser : char !== '_' && char !== '$';
|
|
680
686
|
if (stageParser && !parser)
|
|
681
|
-
throw new
|
|
687
|
+
throw new InternalConsistencyError( `Non-parser property '${ prop }' set by ${ model.$frontend || '' } parser${ at( [ node, parent ] ) }` );
|
|
682
688
|
const enumerable = ('enumerable' in spec) ? spec.enumerable : char !== '_';
|
|
683
689
|
if (enumerable instanceof Function
|
|
684
690
|
? !enumerable( parent, prop )
|
|
685
691
|
: {}.propertyIsEnumerable.call( parent, prop ) !== enumerable)
|
|
686
|
-
throw new
|
|
692
|
+
throw new InternalConsistencyError( `Unexpected enumerability ${ !enumerable }${ at( [ node, parent ], prop ) }` );
|
|
687
693
|
}
|
|
688
694
|
(spec.test || standard)( node, parent, prop, spec,
|
|
689
695
|
typeof noPropertyTest === 'string' && noPropertyTest );
|
|
690
696
|
}
|
|
691
697
|
|
|
692
698
|
function definition( node, parent, prop, spec, name ) {
|
|
699
|
+
if (node.builtin)
|
|
700
|
+
return;
|
|
693
701
|
if (!Array.isArray( node ))
|
|
694
702
|
node = [ node ];
|
|
695
703
|
// TODO: else check that there is a redefinition error
|
|
@@ -704,14 +712,14 @@ function assertConsistency( model, stage ) {
|
|
|
704
712
|
function builtin( node, parent, prop, spec, name ) {
|
|
705
713
|
const type = typeof node;
|
|
706
714
|
if (type !== 'string' && type !== 'boolean')
|
|
707
|
-
throw new
|
|
715
|
+
throw new InternalConsistencyError(`Property '${ prop }' must be a boolean or string but was '${ typeof node }'${ at( [ node, parent ], prop, name ) }` );
|
|
708
716
|
|
|
709
717
|
if (parent.kind !== 'namespace')
|
|
710
|
-
throw new
|
|
718
|
+
throw new InternalConsistencyError(`Property '${ prop }' must be inside artifact that is a namespace but was '${ parent.kind }'${ at( [ node, parent ], prop, name ) }` );
|
|
711
719
|
|
|
712
720
|
const parentName = parent.name && parent.name.absolute;
|
|
713
721
|
if (parentName !== 'cds' && parentName !== 'localized')
|
|
714
|
-
throw new
|
|
722
|
+
throw new InternalConsistencyError(`Property '${ prop }' must be inside namespace 'cds' or 'localized' but was '${ parentName }'${ at( [ node, parent ], prop, name ) }` );
|
|
715
723
|
}
|
|
716
724
|
|
|
717
725
|
function column( node, ...rest ) {
|
|
@@ -740,7 +748,7 @@ function assertConsistency( model, stage ) {
|
|
|
740
748
|
if (!names.includes(p)) {
|
|
741
749
|
const req = spec.schema && spec.schema[p] && spec.schema[p].isRequired;
|
|
742
750
|
if ((req || schema[p] && schema[p].isRequired || noSyntaxErrors)( node ))
|
|
743
|
-
throw new
|
|
751
|
+
throw new InternalConsistencyError( `Required property '${ p }' missing in object${ at( [ node, parent ], prop, name ) }` );
|
|
744
752
|
}
|
|
745
753
|
}
|
|
746
754
|
const optional = spec.optional || [];
|
|
@@ -749,7 +757,7 @@ function assertConsistency( model, stage ) {
|
|
|
749
757
|
? optional.includes( n ) || optional.includes( n.charAt(0) )
|
|
750
758
|
: optional( n, spec );
|
|
751
759
|
if (!(opt || requires.includes( n ) || n === '$extra'))
|
|
752
|
-
throw new
|
|
760
|
+
throw new InternalConsistencyError( `Property '${ n }' is not expected${ at( [ node[n], node, parent ], prop, name ) }` );
|
|
753
761
|
|
|
754
762
|
assertProp( node[n], node, n, spec.schema && spec.schema[n] );
|
|
755
763
|
}
|
|
@@ -772,7 +780,7 @@ function assertConsistency( model, stage ) {
|
|
|
772
780
|
if (spec[choice])
|
|
773
781
|
assertProp( node, parent, prop, spec[choice], choice );
|
|
774
782
|
else
|
|
775
|
-
throw new
|
|
783
|
+
throw new InternalConsistencyError( `No specification for computed variant '${ choice }'${ at( [ node, parent ], prop, idx ) }` );
|
|
776
784
|
}
|
|
777
785
|
}
|
|
778
786
|
|
|
@@ -787,7 +795,7 @@ function assertConsistency( model, stage ) {
|
|
|
787
795
|
if (spec[choice])
|
|
788
796
|
assertProp( node, parent, prop, spec[choice], choice );
|
|
789
797
|
else
|
|
790
|
-
throw new
|
|
798
|
+
throw new InternalConsistencyError( `No specification for computed variant '${ choice }'${ at( [ node, parent ], prop, idx ) }` );
|
|
791
799
|
}
|
|
792
800
|
}
|
|
793
801
|
|
|
@@ -852,7 +860,7 @@ function assertConsistency( model, stage ) {
|
|
|
852
860
|
expression( node[n], parent, prop, spec, n );
|
|
853
861
|
}
|
|
854
862
|
else {
|
|
855
|
-
throw new
|
|
863
|
+
throw new InternalConsistencyError( `Expected array or dictionary${ at( [ null, parent ], prop ) }` );
|
|
856
864
|
}
|
|
857
865
|
}
|
|
858
866
|
|
|
@@ -871,7 +879,7 @@ function assertConsistency( model, stage ) {
|
|
|
871
879
|
// if (!node || typeof node !== 'object' || Object.getPrototypeOf( node ))
|
|
872
880
|
// console.log(node,prop,model.$frontend)
|
|
873
881
|
if (!node || typeof node !== 'object' || Object.getPrototypeOf( node ))
|
|
874
|
-
throw new
|
|
882
|
+
throw new InternalConsistencyError( `Expected dictionary${ at( [ null, parent ], prop ) }` );
|
|
875
883
|
for (const n in node)
|
|
876
884
|
func( node[n], parent, prop, spec, n );
|
|
877
885
|
};
|
|
@@ -880,7 +888,7 @@ function assertConsistency( model, stage ) {
|
|
|
880
888
|
function isArray( func = standard ) {
|
|
881
889
|
return function vector( node, parent, prop, spec ) {
|
|
882
890
|
if (!Array.isArray(node))
|
|
883
|
-
throw new
|
|
891
|
+
throw new InternalConsistencyError( `Expected array${ at( [ null, parent ], prop ) }` );
|
|
884
892
|
node.forEach( (item, n) => func( item, parent, prop, spec, n ) );
|
|
885
893
|
};
|
|
886
894
|
}
|
|
@@ -898,46 +906,41 @@ function assertConsistency( model, stage ) {
|
|
|
898
906
|
if ((spec.also) ? spec.also.includes( node ) : (node === null))
|
|
899
907
|
return;
|
|
900
908
|
if (typeof node !== 'boolean')
|
|
901
|
-
throw new
|
|
909
|
+
throw new InternalConsistencyError( `Expected boolean or null${ at( [ node, parent ], prop ) }` );
|
|
902
910
|
}
|
|
903
911
|
|
|
904
912
|
function isNumber( node, parent, prop, spec ) {
|
|
905
913
|
if (spec.also && spec.also.includes( node ))
|
|
906
914
|
return;
|
|
907
915
|
if (typeof node !== 'number')
|
|
908
|
-
throw new
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
function isStringOrNull( node, parent, prop, spec ) {
|
|
912
|
-
if (node !== null)
|
|
913
|
-
isString(node, parent, prop, spec);
|
|
916
|
+
throw new InternalConsistencyError( `Expected number${ at( [ node, parent ], prop ) }` );
|
|
914
917
|
}
|
|
915
918
|
|
|
916
919
|
function isOneOf( values ) {
|
|
917
920
|
return function isOneOfInner( node, parent, prop ) {
|
|
918
921
|
if (!values.includes(node))
|
|
919
|
-
throw new
|
|
922
|
+
throw new InternalConsistencyError( `Unexpected value '${ node }', expected ${ JSON.stringify(values) }${ at( [ node, parent ], prop ) }` );
|
|
920
923
|
};
|
|
921
924
|
}
|
|
922
925
|
|
|
923
926
|
function isString( node, parent, prop, spec ) {
|
|
924
927
|
if (typeof node !== 'string')
|
|
925
|
-
throw new
|
|
928
|
+
throw new InternalConsistencyError( `Expected string${ at( [ node, parent ], prop ) }` );
|
|
926
929
|
// TODO: also check getOwnPropertyNames(node)
|
|
927
930
|
if (spec.enum && !spec.enum.includes( node ))
|
|
928
|
-
throw new
|
|
931
|
+
throw new InternalConsistencyError( `Unexpected value '${ node }'${ at( [ node, parent ], prop ) }` );
|
|
929
932
|
}
|
|
930
933
|
|
|
931
934
|
function isVal( node, parent, prop, spec ) {
|
|
932
935
|
if (Array.isArray(node))
|
|
933
936
|
node.forEach( (item, n) => standard( item, parent, prop, spec, n ) );
|
|
934
937
|
else if (node !== null && ![ 'string', 'number', 'boolean' ].includes( typeof node ))
|
|
935
|
-
throw new
|
|
938
|
+
throw new InternalConsistencyError( `Expected array or simple value${ at( [ null, parent ], prop ) }` );
|
|
936
939
|
}
|
|
937
940
|
|
|
938
941
|
function isObject( node, parent, prop, spec, name ) {
|
|
939
942
|
if (!node || typeof node !== 'object' || Object.getPrototypeOf( node ) !== Object.prototype)
|
|
940
|
-
throw new
|
|
943
|
+
throw new InternalConsistencyError( `Expected standard object${ at( [ null, parent ], prop, name ) }` );
|
|
941
944
|
}
|
|
942
945
|
|
|
943
946
|
function inDefinitions( art, parent, prop, spec, name ) {
|
|
@@ -956,7 +959,7 @@ function assertConsistency( model, stage ) {
|
|
|
956
959
|
art.name.absolute && art.name.absolute.startsWith('localized.'))
|
|
957
960
|
standard( art, parent, prop, spec, name );
|
|
958
961
|
else
|
|
959
|
-
throw new
|
|
962
|
+
throw new InternalConsistencyError( `Expected definition${ at( [ art, parent ], prop, name ) }` );
|
|
960
963
|
}
|
|
961
964
|
}
|
|
962
965
|
|
|
@@ -966,7 +969,7 @@ function assertConsistency( model, stage ) {
|
|
|
966
969
|
return;
|
|
967
970
|
const validValues = [ 'typeOf', 'global', 'param' ];
|
|
968
971
|
if (!validValues.includes(node))
|
|
969
|
-
throw new
|
|
972
|
+
throw new InternalConsistencyError( `Property '${ prop }' must be either "${ validValues.join('", "') }" or a number but was "${ node }"` );
|
|
970
973
|
}
|
|
971
974
|
|
|
972
975
|
function TODO() { /* no-op */ }
|
package/lib/compiler/builtins.js
CHANGED
|
@@ -250,6 +250,22 @@ const quotedLiteralPatterns = {
|
|
|
250
250
|
},
|
|
251
251
|
json_type: 'string',
|
|
252
252
|
},
|
|
253
|
+
// and only for CSN parser:
|
|
254
|
+
null: {
|
|
255
|
+
json_type: 'null', // modulo JSON typeof weirdness
|
|
256
|
+
},
|
|
257
|
+
boolean: {
|
|
258
|
+
json_type: 'boolean',
|
|
259
|
+
},
|
|
260
|
+
number: {
|
|
261
|
+
test_variant: 'number',
|
|
262
|
+
test_fn: (x => /^[ \t]*[-+]?(\d+(\.\d*)?|\.\d+)(e[-+]\d+)?[ \t]*$/i.test( x )),
|
|
263
|
+
json_type: 'number',
|
|
264
|
+
secondary_json_type: 'string',
|
|
265
|
+
},
|
|
266
|
+
string: {
|
|
267
|
+
json_type: 'string',
|
|
268
|
+
},
|
|
253
269
|
};
|
|
254
270
|
|
|
255
271
|
/**
|
|
@@ -390,14 +406,13 @@ function initBuiltins( model ) {
|
|
|
390
406
|
setMagicVariables( magicVariables );
|
|
391
407
|
// namespace:"cds" stores the builtins ---
|
|
392
408
|
const cds = createNamespace( 'cds', 'reserved' );
|
|
393
|
-
|
|
394
|
-
model.definitions.cds = cds; // not setProp - oData - TODO: still needed?
|
|
409
|
+
model.definitions.cds = cds;
|
|
395
410
|
// Also add the core artifacts to model.definitions`
|
|
396
411
|
model.$builtins = env( core, 'cds.', cds );
|
|
397
412
|
model.$builtins.cds = cds;
|
|
398
413
|
// namespace:"cds.hana" stores HANA-specific builtins ---
|
|
399
414
|
const hana = createNamespace( 'cds.hana', 'reserved' );
|
|
400
|
-
|
|
415
|
+
model.definitions['cds.hana'] = hana;
|
|
401
416
|
model.$builtins.hana = hana;
|
|
402
417
|
cds._subArtifacts.hana = hana;
|
|
403
418
|
env( coreHana, 'cds.hana.', hana );
|
|
@@ -430,6 +445,7 @@ function initBuiltins( model ) {
|
|
|
430
445
|
const artifacts = Object.create(null);
|
|
431
446
|
for (const name of Object.keys( builtins )) {
|
|
432
447
|
const absolute = prefix + name;
|
|
448
|
+
// TODO: reconsider whether to set a type to itself - looks wrong
|
|
433
449
|
const art = {
|
|
434
450
|
kind: 'type', builtin: true, name: { absolute }, type: { path: [ { id: absolute } ] },
|
|
435
451
|
};
|
|
@@ -441,7 +457,7 @@ function initBuiltins( model ) {
|
|
|
441
457
|
Object.assign( art, builtins[name] );
|
|
442
458
|
if (!art.internal)
|
|
443
459
|
artifacts[name] = art;
|
|
444
|
-
|
|
460
|
+
model.definitions[absolute] = art;
|
|
445
461
|
}
|
|
446
462
|
return artifacts;
|
|
447
463
|
}
|