@sap/cds-compiler 3.8.2 → 3.9.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 +63 -0
- package/bin/cdsc.js +2 -2
- package/doc/CHANGELOG_BETA.md +26 -5
- package/lib/api/.eslintrc.json +3 -2
- package/lib/api/options.js +3 -1
- package/lib/api/validate.js +1 -1
- package/lib/base/message-registry.js +28 -19
- package/lib/base/messages.js +6 -1
- package/lib/base/model.js +2 -2
- package/lib/checks/.eslintrc.json +1 -0
- package/lib/checks/actionsFunctions.js +6 -6
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/elements.js +28 -17
- package/lib/checks/foreignKeys.js +1 -1
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/onConditions.js +11 -6
- package/lib/checks/queryNoDbArtifacts.js +1 -1
- package/lib/checks/types.js +1 -1
- package/lib/checks/utils.js +1 -1
- package/lib/checks/validator.js +3 -2
- package/lib/compiler/assert-consistency.js +7 -2
- package/lib/compiler/base.js +8 -4
- package/lib/compiler/builtins.js +7 -0
- package/lib/compiler/checks.js +73 -6
- package/lib/compiler/define.js +10 -5
- package/lib/compiler/extend.js +910 -1711
- package/lib/compiler/finalize-parse-cdl.js +1 -1
- package/lib/compiler/generate.js +838 -0
- package/lib/compiler/index.js +2 -0
- package/lib/compiler/populate.js +2 -2
- package/lib/compiler/propagator.js +20 -8
- package/lib/compiler/resolve.js +3 -3
- package/lib/compiler/shared.js +3 -1
- package/lib/edm/annotations/genericTranslation.js +18 -8
- package/lib/edm/csn2edm.js +14 -14
- package/lib/edm/edm.js +25 -11
- package/lib/edm/edmPreprocessor.js +47 -23
- package/lib/edm/edmUtils.js +37 -9
- package/lib/gen/Dictionary.json +5 -7
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/language.tokens +24 -23
- package/lib/gen/languageLexer.interp +4 -1
- package/lib/gen/languageLexer.js +792 -784
- package/lib/gen/languageLexer.tokens +12 -11
- package/lib/gen/languageParser.js +3564 -3493
- package/lib/json/from-csn.js +28 -6
- package/lib/json/to-csn.js +10 -6
- package/lib/language/antlrParser.js +11 -3
- package/lib/language/genericAntlrParser.js +2 -1
- package/lib/language/language.g4 +14 -3
- package/lib/model/csnRefs.js +10 -5
- package/lib/model/csnUtils.js +41 -76
- package/lib/modelCompare/utils/.eslintrc.json +1 -1
- package/lib/optionProcessor.js +7 -4
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/toCdl.js +244 -168
- package/lib/render/toHdbcds.js +18 -10
- package/lib/render/toSql.js +24 -2
- package/lib/transform/db/.eslintrc.json +4 -3
- package/lib/transform/db/cdsPersistence.js +1 -1
- package/lib/transform/db/expansion.js +11 -6
- package/lib/transform/db/flattening.js +22 -15
- package/lib/transform/db/rewriteCalculatedElements.js +50 -29
- package/lib/transform/db/temporal.js +1 -1
- package/lib/transform/db/views.js +1 -1
- package/lib/transform/draft/db.js +1 -1
- package/lib/transform/draft/odata.js +3 -4
- package/lib/transform/forOdataNew.js +5 -6
- package/lib/transform/forRelationalDB.js +7 -7
- package/lib/transform/localized.js +1 -1
- package/lib/transform/odata/toFinalBaseType.js +6 -6
- package/lib/transform/odata/typesExposure.js +12 -3
- package/lib/transform/odata/utils.js +3 -0
- package/lib/transform/transformUtilsNew.js +11 -26
- package/lib/transform/translateAssocsToJoins.js +9 -9
- package/lib/transform/universalCsn/.eslintrc.json +3 -2
- package/lib/transform/universalCsn/coreComputed.js +1 -1
- package/lib/transform/universalCsn/universalCsnEnricher.js +6 -4
- package/lib/utils/file.js +3 -3
- package/lib/utils/moduleResolve.js +1 -1
- package/package.json +1 -1
package/lib/compiler/base.js
CHANGED
|
@@ -66,9 +66,13 @@ function propExists( prop, parent ) {
|
|
|
66
66
|
return (obj.items || obj.targetAspect || obj)[prop];
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Return the "old style" name structure with `absolute`, `action`, `param`,
|
|
71
|
+
* `element`. The following code makes use of the fact that only member extensions
|
|
72
|
+
* have a "sparse" name structure.
|
|
73
|
+
*
|
|
74
|
+
* @param {XSN.Artifact} art
|
|
75
|
+
*/
|
|
72
76
|
function getArtifactName( art ) {
|
|
73
77
|
if (!art.name || art.name.absolute) // no name or “old style”
|
|
74
78
|
return art.name;
|
|
@@ -105,7 +109,7 @@ function getMemberNameProp( elem, kind ) {
|
|
|
105
109
|
obj = obj.items;
|
|
106
110
|
if (obj.elements || obj.enum)
|
|
107
111
|
return 'element';
|
|
108
|
-
throw CompilerAssertion( `Member not found in parent properties ${ Object.keys( obj ).join('+') }` );
|
|
112
|
+
throw new CompilerAssertion( `Member not found in parent properties ${ Object.keys( obj ).join('+') }` );
|
|
109
113
|
}
|
|
110
114
|
|
|
111
115
|
module.exports = {
|
package/lib/compiler/builtins.js
CHANGED
|
@@ -191,6 +191,13 @@ const magicVariables = {
|
|
|
191
191
|
// Require that elements are accessed, i.e. no $at, only $at.<element>.
|
|
192
192
|
$requireElementAccess: true,
|
|
193
193
|
},
|
|
194
|
+
$valid: { // dito
|
|
195
|
+
elements: {
|
|
196
|
+
from: {}, to: {},
|
|
197
|
+
},
|
|
198
|
+
// Require that elements are accessed, i.e. no $valid, only $valid.<element>.
|
|
199
|
+
$requireElementAccess: true,
|
|
200
|
+
},
|
|
194
201
|
$now: {}, // Dito
|
|
195
202
|
$session: {
|
|
196
203
|
// In ABAP CDS session variables are accessed in a generic way via
|
package/lib/compiler/checks.js
CHANGED
|
@@ -16,11 +16,12 @@
|
|
|
16
16
|
// const { hasArtifactTypeInformation } = require('../model/csnUtils')
|
|
17
17
|
const builtins = require('../compiler/builtins');
|
|
18
18
|
const {
|
|
19
|
-
forEachGeneric, forEachDefinition, forEachMember,
|
|
19
|
+
forEachGeneric, forEachDefinition, forEachMember, isBetaEnabled,
|
|
20
20
|
} = require('../base/model');
|
|
21
21
|
const { CompilerAssertion } = require('../base/error');
|
|
22
22
|
const { pathName } = require('./utils');
|
|
23
23
|
const { forEachMemberRecursively } = require('../model/csnUtils');
|
|
24
|
+
const $location = Symbol.for('cds.$location');
|
|
24
25
|
|
|
25
26
|
function check( model ) { // = XSN
|
|
26
27
|
const {
|
|
@@ -34,11 +35,13 @@ function check( model ) { // = XSN
|
|
|
34
35
|
forEachGeneric( model, 'vocabularies', checkAnnotationDefinition );
|
|
35
36
|
return;
|
|
36
37
|
|
|
37
|
-
function checkDefinition(
|
|
38
|
-
checkGenericConstruct(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
function checkDefinition( def ) {
|
|
39
|
+
checkGenericConstruct( def );
|
|
40
|
+
if (def.includes && def.elements)
|
|
41
|
+
checkElementIncludeOverride( def );
|
|
42
|
+
forEachMember( def, member => checkMember(member) );
|
|
43
|
+
if (def.$queries)
|
|
44
|
+
def.$queries.forEach( checkQuery );
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
function checkAnnotationDefinition( art ) {
|
|
@@ -498,6 +501,70 @@ function check( model ) { // = XSN
|
|
|
498
501
|
checkTypeStructure(artifact.items);
|
|
499
502
|
}
|
|
500
503
|
|
|
504
|
+
/**
|
|
505
|
+
* Report issues when an entity overrides structured elements of an included entity
|
|
506
|
+
* with a scalar one or vice versa.
|
|
507
|
+
*
|
|
508
|
+
* NOTE: Relies on element expansion.
|
|
509
|
+
*/
|
|
510
|
+
function checkElementIncludeOverride( def ) {
|
|
511
|
+
if (!isBetaEnabled(model.options, 'v4preview'))
|
|
512
|
+
return; // this is a v4 check only
|
|
513
|
+
|
|
514
|
+
for (const name in def.elements) {
|
|
515
|
+
const element = def.elements[name];
|
|
516
|
+
// Element is new in `art`, not expanded; we can't check for !element._origin, due
|
|
517
|
+
// to calculated elements such as `a = b`.
|
|
518
|
+
if (element.$inferred !== 'include') {
|
|
519
|
+
for (const include of def.includes) {
|
|
520
|
+
if (include._artifact?.elements?.[name] !== undefined)
|
|
521
|
+
checkElementOverride( element, include._artifact.elements[name]);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
return;
|
|
527
|
+
|
|
528
|
+
function checkElementOverride( elem, original ) {
|
|
529
|
+
const xorElements = !elem.elements !== !original.elements;
|
|
530
|
+
if (xorElements) {
|
|
531
|
+
// one of the two elements is not structured
|
|
532
|
+
const prop = !elem.elements ? 'new-not-structured' : 'old-not-structured';
|
|
533
|
+
const name = elem.name.id;
|
|
534
|
+
// Position at type/struct, not name
|
|
535
|
+
const loc = elem.type?.location || elem.elements?.[$location] || elem.location;
|
|
536
|
+
error('ref-invalid-override', [ loc, elem ],
|
|
537
|
+
{ '#': prop, art: original._main, name });
|
|
538
|
+
return false;
|
|
539
|
+
}
|
|
540
|
+
else if (original.elements &&
|
|
541
|
+
!checkSubStructureOverride(elem, elem.elements, original.elements)) {
|
|
542
|
+
return false;
|
|
543
|
+
}
|
|
544
|
+
return true;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Ensure the new one has at least as many elements as the original.
|
|
549
|
+
*/
|
|
550
|
+
function checkSubStructureOverride( user, elements, originals ) {
|
|
551
|
+
for (const element in originals) {
|
|
552
|
+
const elem = elements[element];
|
|
553
|
+
const orig = originals[element];
|
|
554
|
+
if (elem === undefined) {
|
|
555
|
+
const loc = [ elements[$location], user ];
|
|
556
|
+
error('ref-invalid-override', loc, { '#': 'missing', id: user.name.id, name: element });
|
|
557
|
+
return false; // only report one
|
|
558
|
+
}
|
|
559
|
+
else if (!checkElementOverride(elem, orig)) {
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
return true;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
|
|
501
568
|
// Former checkExpressions.js ----------------------------------------------
|
|
502
569
|
|
|
503
570
|
/**
|
package/lib/compiler/define.js
CHANGED
|
@@ -123,6 +123,7 @@ const {
|
|
|
123
123
|
forEachGeneric,
|
|
124
124
|
forEachInOrder,
|
|
125
125
|
forEachMember,
|
|
126
|
+
isBetaEnabled,
|
|
126
127
|
} = require('../base/model');
|
|
127
128
|
const shuffleGen = require('../base/shuffle');
|
|
128
129
|
const {
|
|
@@ -159,7 +160,7 @@ function define( model ) {
|
|
|
159
160
|
const { options } = model;
|
|
160
161
|
// Get simplified "resolve" functionality and the message function:
|
|
161
162
|
const {
|
|
162
|
-
error, warning, info, messages,
|
|
163
|
+
error, warning, info, messages, message,
|
|
163
164
|
} = model.$messageFunctions;
|
|
164
165
|
const {
|
|
165
166
|
resolveUncheckedPath,
|
|
@@ -201,7 +202,7 @@ function define( model ) {
|
|
|
201
202
|
setLink( model, '_entities', [] ); // for entities with includes
|
|
202
203
|
model.$entity = 0;
|
|
203
204
|
model.$compositionTargets = Object.create(null);
|
|
204
|
-
model.$
|
|
205
|
+
model.$collectedExtensions = Object.create(null);
|
|
205
206
|
|
|
206
207
|
initBuiltins( model );
|
|
207
208
|
const sourceNames = shuffleArray( Object.keys( model.sources ) );
|
|
@@ -211,7 +212,7 @@ function define( model ) {
|
|
|
211
212
|
initNamespaceAndUsing( model.sources[name] );
|
|
212
213
|
dictForEach( model.definitions, initArtifact );
|
|
213
214
|
dictForEach( model.vocabularies, initVocabulary );
|
|
214
|
-
dictForEach( model.$
|
|
215
|
+
dictForEach( model.$collectedExtensions, e => e._extensions.forEach( initExtension ) );
|
|
215
216
|
|
|
216
217
|
addI18nBlocks();
|
|
217
218
|
}
|
|
@@ -398,8 +399,8 @@ function define( model ) {
|
|
|
398
399
|
delete ext.name.path[0]._artifact; // might point to wrong JS object in phase 1
|
|
399
400
|
ext.name.absolute = absolute; // definition might not be there yet, no _artifact link
|
|
400
401
|
const location = { file: '' }; // stupid required location
|
|
401
|
-
const late = model.$
|
|
402
|
-
(model.$
|
|
402
|
+
const late = model.$collectedExtensions[absolute] ||
|
|
403
|
+
(model.$collectedExtensions[absolute] = {
|
|
403
404
|
kind: 'annotate',
|
|
404
405
|
name: { absolute, location },
|
|
405
406
|
$inferred: '',
|
|
@@ -1165,6 +1166,10 @@ function define( model ) {
|
|
|
1165
1166
|
if (!elem.target)
|
|
1166
1167
|
elem.type = { ...elem.value.type, $inferred: 'cast' };
|
|
1167
1168
|
}
|
|
1169
|
+
if (elem.value.stored?.val === true && !isBetaEnabled(options, 'calculatedElementsOnWrite')) {
|
|
1170
|
+
const loc = [ elem.value.stored.location, elem ];
|
|
1171
|
+
message( 'def-unsupported-calc-elem', loc, { '#': 'on-write' } );
|
|
1172
|
+
}
|
|
1168
1173
|
elem.$syntax = 'calc';
|
|
1169
1174
|
}
|
|
1170
1175
|
}
|