@sap/cds-compiler 6.9.2 → 7.0.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 +86 -2
- package/bin/cdsc.js +4 -33
- package/doc/IncompatibleChanges_v7.md +639 -0
- package/lib/api/main.js +4 -56
- package/lib/api/options.js +6 -15
- package/lib/api/validate.js +1 -0
- package/lib/base/builtins.js +1 -2
- package/lib/base/csnRefs.js +2 -6
- package/lib/base/message-registry.js +82 -76
- package/lib/base/messages.js +23 -4
- package/lib/base/optionProcessor.js +2 -72
- package/lib/base/specialOptions.js +20 -17
- package/lib/checks/defaultValues.js +1 -39
- package/lib/checks/hasPersistedElements.js +19 -3
- package/lib/checks/parameters.js +0 -34
- package/lib/checks/selectItems.js +2 -38
- package/lib/checks/typeParameters.js +162 -0
- package/lib/checks/validator.js +5 -8
- package/lib/compiler/assert-consistency.js +19 -5
- package/lib/compiler/checks.js +47 -43
- package/lib/compiler/define.js +6 -6
- package/lib/compiler/extend.js +102 -111
- package/lib/compiler/generate.js +4 -8
- package/lib/compiler/populate.js +4 -7
- package/lib/compiler/propagator.js +9 -9
- package/lib/compiler/resolve.js +205 -7
- package/lib/compiler/shared.js +76 -82
- package/lib/compiler/tweak-assocs.js +102 -22
- package/lib/compiler/utils.js +57 -12
- package/lib/compiler/xpr-rewrite.js +2 -15
- package/lib/edm/annotations/edmJson.js +14 -10
- package/lib/edm/annotations/genericTranslation.js +3 -1
- package/lib/edm/annotations/preprocessAnnotations.js +9 -26
- package/lib/edm/csn2edm.js +27 -20
- package/lib/edm/edmUtils.js +25 -0
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +2237 -2241
- package/lib/gen/Dictionary.json +17 -2
- package/lib/json/from-csn.js +67 -52
- package/lib/json/to-csn.js +28 -25
- package/lib/language/textUtils.js +0 -13
- package/lib/main.d.ts +34 -59
- package/lib/main.js +1 -1
- package/lib/model/csnUtils.js +9 -8
- package/lib/parsers/AstBuildingParser.js +45 -55
- package/lib/parsers/Lexer.js +2 -0
- package/lib/parsers/identifiers.js +0 -9
- package/lib/render/toCdl.js +41 -40
- package/lib/render/toSql.js +8 -1
- package/lib/render/utils/common.js +1 -1
- package/lib/render/utils/sql.js +2 -3
- package/lib/tool-lib/enrichCsn.js +1 -2
- package/lib/transform/db/applyTransformations.js +7 -5
- package/lib/transform/db/assertUnique.js +8 -51
- package/lib/transform/db/associations.js +1 -1
- package/lib/transform/db/cdsPersistence.js +1 -15
- package/lib/transform/db/expansion.js +9 -12
- package/lib/transform/db/flattening.js +1 -1
- package/lib/transform/db/groupByOrderBy.js +0 -16
- package/lib/transform/db/views.js +57 -161
- package/lib/transform/draft/db.js +2 -2
- package/lib/transform/forOdata.js +25 -14
- package/lib/transform/forRelationalDB.js +93 -301
- package/lib/transform/localized.js +33 -102
- package/lib/transform/odata/flattening.js +11 -2
- package/lib/transform/transformUtils.js +25 -3
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -2
- package/package.json +2 -2
- package/lib/render/toHdbcds.js +0 -1810
package/lib/compiler/extend.js
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
const { weakRefLocation } = require('../base/location');
|
|
6
6
|
const { searchName } = require('../base/messages');
|
|
7
|
-
const { isDeprecatedEnabled } = require('../base/specialOptions');
|
|
8
7
|
const { dictAdd, pushToDict, dictForEach } = require('./dictionaries');
|
|
9
8
|
const { kindProperties, dictKinds } = require('./base');
|
|
10
9
|
const {
|
|
@@ -17,7 +16,6 @@ const {
|
|
|
17
16
|
setMemberParent,
|
|
18
17
|
createAndLinkCalcDepElement,
|
|
19
18
|
initExprAnnoBlock,
|
|
20
|
-
initDollarSelf,
|
|
21
19
|
initBoundSelfParam,
|
|
22
20
|
dependsOnSilent,
|
|
23
21
|
pathName,
|
|
@@ -81,9 +79,6 @@ function extend( model ) {
|
|
|
81
79
|
applyIncludes, // TODO: re-check
|
|
82
80
|
} );
|
|
83
81
|
|
|
84
|
-
const includesNonShadowedFirst
|
|
85
|
-
= isDeprecatedEnabled( model.options, '_includesNonShadowedFirst' );
|
|
86
|
-
|
|
87
82
|
const includeCollisions = [];
|
|
88
83
|
|
|
89
84
|
forEachGeneric( model, 'definitions', tagCompositionTargets );
|
|
@@ -186,6 +181,7 @@ function extend( model ) {
|
|
|
186
181
|
for (const prop in art._extensions) {
|
|
187
182
|
if (Object.hasOwn( art._extensions, prop ) &&
|
|
188
183
|
// remark: if we change the array, consider whether to delete the artifact
|
|
184
|
+
// TODO: what is the `$gen`? `$add` is handled...
|
|
189
185
|
![ 'elements', 'actions', 'params', '$gen' ].includes( prop ))
|
|
190
186
|
applyPropertyExtensions( art, prop );
|
|
191
187
|
}
|
|
@@ -292,7 +288,7 @@ function extend( model ) {
|
|
|
292
288
|
|
|
293
289
|
/**
|
|
294
290
|
* Applying extensions is handled in extendArtifactAfter(). And only afterward,
|
|
295
|
-
* an effective sequence number is set. Meaning that if a sub
|
|
291
|
+
* an effective sequence number is set. Meaning that if a sub artifact already
|
|
296
292
|
* has a sequence number, then extensions would be lost.
|
|
297
293
|
*
|
|
298
294
|
* A special case are foreign keys, see extendForeignKeys().
|
|
@@ -425,6 +421,8 @@ function extend( model ) {
|
|
|
425
421
|
}
|
|
426
422
|
else if (prop === 'returns') {
|
|
427
423
|
pushToDict( dict, 'elements', ext );
|
|
424
|
+
if (hasSecurityAnno( ext.returns ))
|
|
425
|
+
checkReturnsExtension( ext, ext );
|
|
428
426
|
// create 'returns' for the super annotate, store in elements anyway
|
|
429
427
|
if (!art.returns && art.kind === 'annotate')
|
|
430
428
|
annotateCreate( art, '', art, 'returns' );
|
|
@@ -498,10 +496,12 @@ function extend( model ) {
|
|
|
498
496
|
}
|
|
499
497
|
// Now apply the relevant extensions
|
|
500
498
|
scheduled.reverse();
|
|
499
|
+
art.$postponeInclude ??= 0; // add elems of includes before direct ones
|
|
501
500
|
if (prop === 'includes' && !art.includes?.$original) {
|
|
502
501
|
const $original = art.includes ?? [];
|
|
503
502
|
art.includes = [ ...$original ];
|
|
504
503
|
art.includes.$original = $original;
|
|
504
|
+
// console.log('APE:',art.name.id,art.includes.map( r => r._artifact?.name.id ))
|
|
505
505
|
}
|
|
506
506
|
if (prop === '$add') {
|
|
507
507
|
extensions[prop] = scheduled;
|
|
@@ -703,7 +703,7 @@ function extend( model ) {
|
|
|
703
703
|
result.push( item );
|
|
704
704
|
}
|
|
705
705
|
else {
|
|
706
|
-
let upToSpec = item
|
|
706
|
+
let upToSpec = !item.$errorReported && item.upTo;
|
|
707
707
|
while (prevPos < previousValue.length) {
|
|
708
708
|
const prevItem = previousValue[prevPos++];
|
|
709
709
|
result.push( prevItem );
|
|
@@ -728,40 +728,18 @@ function extend( model ) {
|
|
|
728
728
|
location: previousAnno.location,
|
|
729
729
|
};
|
|
730
730
|
}
|
|
731
|
-
// function se(a) { return a.upTo ? [a.val,a.upTo.val] : a.val ; }
|
|
732
731
|
|
|
733
|
-
|
|
734
|
-
const { literal } = upToSpec;
|
|
735
|
-
if (!isFullUpTo) { // inside struct of UP TO
|
|
736
|
-
if (literal !== 'struct' && literal !== 'array' )
|
|
737
|
-
return true;
|
|
738
|
-
}
|
|
739
|
-
else if (literal === 'struct') {
|
|
740
|
-
return Object.values( upToSpec.struct ).every( v => checkUpToSpec( v, art, annoName ) );
|
|
741
|
-
}
|
|
742
|
-
else if (literal !== 'array' && literal !== 'boolean' && literal !== 'null') {
|
|
743
|
-
return true;
|
|
744
|
-
}
|
|
745
|
-
error( null, [ upToSpec.location, art ],
|
|
746
|
-
{ anno: annoName, code: '... up to', '#': literal },
|
|
747
|
-
{
|
|
748
|
-
std: 'Unexpected $(CODE) value type in the assignment of $(ANNO)',
|
|
749
|
-
array: 'Unexpected array as $(CODE) value in the assignment of $(ANNO)',
|
|
750
|
-
// eslint-disable-next-line @stylistic/max-len
|
|
751
|
-
struct: 'Unexpected structure as $(CODE) structure property value in the assignment of $(ANNO)',
|
|
752
|
-
boolean: 'Unexpected boolean as $(CODE) value in the assignment of $(ANNO)',
|
|
753
|
-
null: 'Unexpected null as $(CODE) value in the assignment of $(ANNO)',
|
|
754
|
-
} );
|
|
755
|
-
return false;
|
|
756
|
-
}
|
|
732
|
+
// For the `up to` specification, see ../../test3/Annotations/EllipsisUpTo/
|
|
757
733
|
|
|
758
734
|
function equalUpTo( previousItem, upToSpec ) {
|
|
759
735
|
if (!previousItem)
|
|
760
736
|
return false;
|
|
761
|
-
|
|
737
|
+
|
|
738
|
+
if (upToSpec.sym) // enum symbol (literal: 'enum')
|
|
739
|
+
return previousItem.sym?.id === upToSpec.sym.id;
|
|
740
|
+
if (upToSpec.val !== undefined) { // not struct, ref or non-val expression
|
|
762
741
|
if (previousItem.val === upToSpec.val) // enum, struct and ref have no val
|
|
763
742
|
return true;
|
|
764
|
-
// TODO v6: delete the special UP TO comparison?
|
|
765
743
|
const upToVal = upToSpec.val;
|
|
766
744
|
const prevVal = previousItem.val;
|
|
767
745
|
// eslint-disable-next-line eqeqeq
|
|
@@ -769,17 +747,27 @@ function extend( model ) {
|
|
|
769
747
|
( typeof upToVal === 'number' && stringCouldHaveBeenCdlNumber( prevVal ) ||
|
|
770
748
|
typeof prevVal === 'number' && stringCouldHaveBeenCdlNumber( upToVal ) );
|
|
771
749
|
}
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
750
|
+
|
|
751
|
+
if (upToSpec.path) {
|
|
752
|
+
// console.log(upToSpec.location+'',normalizeRef( upToSpec ),previousItem.struct?.['='])
|
|
753
|
+
return (previousItem.path)
|
|
754
|
+
? normalizeRef( upToSpec ) === normalizeRef( previousItem )
|
|
755
|
+
: normalizeRef( upToSpec ) === previousItem.struct?.['=']?.val &&
|
|
756
|
+
(!upToSpec.$tokenTexts || Object.keys( previousItem.struct ).length === 1);
|
|
777
757
|
}
|
|
778
|
-
|
|
779
|
-
return
|
|
780
|
-
|
|
758
|
+
if (!upToSpec.struct)
|
|
759
|
+
return false;
|
|
760
|
+
|
|
761
|
+
const { struct } = previousItem;
|
|
762
|
+
if (struct) {
|
|
763
|
+
const entries = Object.entries( upToSpec.struct );
|
|
764
|
+
// {} does not match { "=": "ref" }, but { prop: … } and { "=": "ref", more: … }
|
|
765
|
+
return (entries.length)
|
|
766
|
+
? entries.every( ([ n, v ]) => equalUpTo( previousItem.struct[n], v ) )
|
|
767
|
+
: !struct['='] || Object.keys( struct ).length > 1;
|
|
781
768
|
}
|
|
782
|
-
return
|
|
769
|
+
return previousItem.path && Object.keys( upToSpec.struct ).length === 1 &&
|
|
770
|
+
normalizeRef( previousItem ) === upToSpec.struct['=']?.val;
|
|
783
771
|
}
|
|
784
772
|
|
|
785
773
|
// We only compare a string by number if the string is not empty, and could have
|
|
@@ -794,7 +782,7 @@ function extend( model ) {
|
|
|
794
782
|
|
|
795
783
|
function normalizeRef( node ) { // see to-csn.js
|
|
796
784
|
const ref = pathName( node.path );
|
|
797
|
-
// TODO: get rid of name.variant (induces a wrong structure anyway)
|
|
785
|
+
// TODO XSN 7.1: get rid of name.variant (induces a wrong structure anyway)
|
|
798
786
|
return node.variant ? `${ ref }#${ pathName( node.variant.path ) }` : ref;
|
|
799
787
|
}
|
|
800
788
|
|
|
@@ -886,11 +874,10 @@ function extend( model ) {
|
|
|
886
874
|
return;
|
|
887
875
|
|
|
888
876
|
for (const ext of extensions) {
|
|
889
|
-
let dictCheck = (art.kind !== 'annotate'); // no check in super annotate statement
|
|
890
877
|
forEachGeneric( ext, extProp || (ext.enum ? 'enum' : 'elements'), ( elemExt, name ) => {
|
|
891
878
|
if (elemExt.kind !== 'annotate' && elemExt.kind !== 'extend') // TODO: specified elems
|
|
892
879
|
return; // definitions inside extend, already handled
|
|
893
|
-
|
|
880
|
+
checkRemainingMemberExtensions( art, elemExt, artProp, name );
|
|
894
881
|
const elem = art[artProp]?.[name] || annotateFor( art, extProp || 'elements', name );
|
|
895
882
|
setLink( elemExt.name, '_artifact', (elem.kind !== 'annotate' ? elem : null ) );
|
|
896
883
|
// TODO: why null for annotate?
|
|
@@ -948,13 +935,7 @@ function extend( model ) {
|
|
|
948
935
|
? 'ext-unexpected-returns-sec'
|
|
949
936
|
: 'ext-unexpected-returns';
|
|
950
937
|
message( msgId, [ ext.returns.location, ext ],
|
|
951
|
-
{ '#': art.kind, keyword: 'returns' }
|
|
952
|
-
{
|
|
953
|
-
std: 'Unexpected $(KEYWORD); only actions and functions have return parameters',
|
|
954
|
-
action: 'Unexpected $(KEYWORD) for action without return parameter',
|
|
955
|
-
// function without `returns` can happen via CSN input! TODO: check in parser
|
|
956
|
-
function: 'Unexpected $(KEYWORD) for function without return parameter',
|
|
957
|
-
} );
|
|
938
|
+
{ '#': art.kind, keyword: 'returns' } );
|
|
958
939
|
// Do not put completely wrong returns into a “super annotate” statement;
|
|
959
940
|
// this could induce consequential errors with [..., …]:
|
|
960
941
|
return art.kind === 'action' || art.kind === 'function';
|
|
@@ -983,7 +964,10 @@ function extend( model ) {
|
|
|
983
964
|
// (i.e. not put into the "super annotate").
|
|
984
965
|
const dict = parent[prop];
|
|
985
966
|
const securityRelevant = hasSecurityAnno( ext ) ? '-sec' : '';
|
|
967
|
+
const inSuperAnnotate = (parent.kind === 'annotate');
|
|
986
968
|
if (!dict && !securityRelevant) {
|
|
969
|
+
if (inSuperAnnotate) // no check in super annotate statement
|
|
970
|
+
return;
|
|
987
971
|
// TODO: check - for each name? - better locations
|
|
988
972
|
const location = ext._parent?.[prop]?.[$location] || ext.name.location;
|
|
989
973
|
// Remark: no `elements` dict location with `annotate Main:elem`
|
|
@@ -1007,7 +991,7 @@ function extend( model ) {
|
|
|
1007
991
|
break;
|
|
1008
992
|
case 'actions':
|
|
1009
993
|
if (canBeDraftMember( name, parent, draftBoundActions ))
|
|
1010
|
-
return
|
|
994
|
+
return;
|
|
1011
995
|
// TODO: use extra text variant and location of dictionary - no
|
|
1012
996
|
notFound( 'ext-undefined-action', ext.name.location, ext,
|
|
1013
997
|
{ '#': 'action', art: parent, name } );
|
|
@@ -1016,9 +1000,10 @@ function extend( model ) {
|
|
|
1016
1000
|
if (model.options.testMode)
|
|
1017
1001
|
throw new CompilerAssertion(`Missing case for prop: ${ prop }`);
|
|
1018
1002
|
}
|
|
1019
|
-
return false;
|
|
1020
1003
|
}
|
|
1021
1004
|
else if (!dict?.[name]) {
|
|
1005
|
+
if (inSuperAnnotate && !securityRelevant)
|
|
1006
|
+
return; // there was already a warning for the parent
|
|
1022
1007
|
// TODO: make variant `returns` an auto-variant for ($ART) ?
|
|
1023
1008
|
const inReturns = parent._parent?.returns;
|
|
1024
1009
|
switch (prop) {
|
|
@@ -1055,7 +1040,6 @@ function extend( model ) {
|
|
|
1055
1040
|
throw new CompilerAssertion(`Missing case for prop: ${ prop }`);
|
|
1056
1041
|
}
|
|
1057
1042
|
}
|
|
1058
|
-
return true;
|
|
1059
1043
|
}
|
|
1060
1044
|
|
|
1061
1045
|
function notFound( msgId, location, address, args, validDict ) {
|
|
@@ -1080,10 +1064,10 @@ function extend( model ) {
|
|
|
1080
1064
|
annotate[prop] = art[prop];
|
|
1081
1065
|
}
|
|
1082
1066
|
}
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1067
|
+
}
|
|
1068
|
+
if (extensions?.length === 1) { // i.e. no proper location if from more than one extension
|
|
1069
|
+
annotate.location = extensions[0].location;
|
|
1070
|
+
annotate.name.location = extensions[0].name.location;
|
|
1087
1071
|
}
|
|
1088
1072
|
extendArtifactBefore( annotate );
|
|
1089
1073
|
extendArtifactAfter( annotate );
|
|
@@ -1158,13 +1142,26 @@ function extend( model ) {
|
|
|
1158
1142
|
|
|
1159
1143
|
// extend, mainly old-style ---------------------------------------------------
|
|
1160
1144
|
|
|
1145
|
+
function addedElements( art ) {
|
|
1146
|
+
return Object.values( art.elements )
|
|
1147
|
+
.reduce( ( acc, elem ) => (elem.kind === 'extend' ? acc : acc + 1), 0 );
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* Extend artifact with additional elements and actions: via includes and/or
|
|
1152
|
+
* extensions. This happens _after_ `populateArtifact`, i.e. after eventually
|
|
1153
|
+
* having inferred the elements/... from an "origin".
|
|
1154
|
+
*/
|
|
1161
1155
|
function extendArtifactAdd( art ) {
|
|
1162
1156
|
const { includes } = art;
|
|
1163
1157
|
if (includes) {
|
|
1164
1158
|
if (includes.$original) // if extensions with includes:
|
|
1165
1159
|
art.includes = includes.$original; // original includes have been stored
|
|
1166
1160
|
if (art.includes?.length)
|
|
1167
|
-
applyIncludes( art, art );
|
|
1161
|
+
applyIncludes( art, art ); // apply includes provided with definition
|
|
1162
|
+
else if (art.elements) // next include only after elements so far
|
|
1163
|
+
art.$postponeInclude += addedElements( art );
|
|
1164
|
+
|
|
1168
1165
|
art.includes = includes;
|
|
1169
1166
|
// early propagation of specific annotation assignments
|
|
1170
1167
|
// TODO: propagate in effectiveType() ?
|
|
@@ -1172,7 +1169,7 @@ function extend( model ) {
|
|
|
1172
1169
|
propagateEarly( art, '@fiori.draft.enabled' );
|
|
1173
1170
|
}
|
|
1174
1171
|
if (art._extensions?.$add)
|
|
1175
|
-
extendArtifact( art._extensions.$add, art );
|
|
1172
|
+
extendArtifact( art._extensions.$add, art ); // apply extensions
|
|
1176
1173
|
checkRedefinitionThroughIncludes( art, 'elements' );
|
|
1177
1174
|
checkRedefinitionThroughIncludes( art, 'actions' );
|
|
1178
1175
|
}
|
|
@@ -1225,6 +1222,7 @@ function extend( model ) {
|
|
|
1225
1222
|
}
|
|
1226
1223
|
|
|
1227
1224
|
function extendMembers( extensions, art ) {
|
|
1225
|
+
// only called in extendArtifact()
|
|
1228
1226
|
// TODO: do the whole extension stuff lazily if the elements are requested
|
|
1229
1227
|
const elemExtensions = [];
|
|
1230
1228
|
// if (art._main) // extensions already sorted for main artifacts
|
|
@@ -1246,7 +1244,9 @@ function extend( model ) {
|
|
|
1246
1244
|
// 'Info', 'EXT').toString())
|
|
1247
1245
|
setArtifactLink( ext.name, art ); // TODO: probably already done
|
|
1248
1246
|
if (ext.includes)
|
|
1249
|
-
applyIncludes( ext, art );
|
|
1247
|
+
applyIncludes( ext, art ); // moves included elem to extension(! TODO: recheck)
|
|
1248
|
+
else if (ext.elements)
|
|
1249
|
+
art.$postponeInclude += addedElements( ext );
|
|
1250
1250
|
|
|
1251
1251
|
// console.log(ext,art)
|
|
1252
1252
|
checkAnnotate( ext, art );
|
|
@@ -1364,19 +1364,21 @@ function extend( model ) {
|
|
|
1364
1364
|
* Set property `_parent` for all elements in `parent` to `parent` and do so
|
|
1365
1365
|
* recursively for all sub elements.
|
|
1366
1366
|
*
|
|
1367
|
-
* If
|
|
1367
|
+
* If `construct !== parent`, also copy elements to `parent`.
|
|
1368
|
+
*
|
|
1369
|
+
* (Only) if called recursively: `construct === parent`
|
|
1368
1370
|
*
|
|
1369
1371
|
* TODO: separate extension!
|
|
1370
1372
|
*/
|
|
1371
1373
|
function initMembers( construct, parent, block ) {
|
|
1374
|
+
// only called by extendMembers() and recursively
|
|
1372
1375
|
// TODO: split extend from init
|
|
1376
|
+
const obj = initItemsLinks( construct, block );
|
|
1377
|
+
initExprAnnoBlock( construct, block, error );
|
|
1378
|
+
if (parent.targetAspect?.elements)
|
|
1379
|
+
parent = parent.targetAspect;
|
|
1373
1380
|
const main = parent._main || parent;
|
|
1374
1381
|
const isQueryExtension = construct.kind === 'extend' && main.query;
|
|
1375
|
-
let obj = initItemsLinks( construct, block );
|
|
1376
|
-
initExprAnnoBlock( construct, block );
|
|
1377
|
-
const { targetAspect } = obj;
|
|
1378
|
-
if (targetAspect?.elements)
|
|
1379
|
-
initAnonymousAspect();
|
|
1380
1382
|
|
|
1381
1383
|
if (obj !== parent && obj.elements && parent.enum) { // applying the extension
|
|
1382
1384
|
initElementsAsEnum();
|
|
@@ -1436,42 +1438,6 @@ function extend( model ) {
|
|
|
1436
1438
|
forEachGeneric( { enum: obj.elements }, 'enum', init );
|
|
1437
1439
|
}
|
|
1438
1440
|
|
|
1439
|
-
function initAnonymousAspect() {
|
|
1440
|
-
// TODO: main?
|
|
1441
|
-
const inEntity = parent._main?.kind === 'entity';
|
|
1442
|
-
// TODO: also allow indirectly (component in component in entity)?
|
|
1443
|
-
setLink( targetAspect, '_outer', obj );
|
|
1444
|
-
setLink( targetAspect, '_parent', parent._parent );
|
|
1445
|
-
setLink( targetAspect, '_main', null ); // for name resolution
|
|
1446
|
-
|
|
1447
|
-
parent = targetAspect;
|
|
1448
|
-
construct = parent; // avoid extension behavior
|
|
1449
|
-
targetAspect.kind = 'aspect'; // TODO: probably '$aspect' to detect
|
|
1450
|
-
setLink( targetAspect, '_block', block );
|
|
1451
|
-
initDollarSelf( targetAspect );
|
|
1452
|
-
// allow ref of up_ in anonymous aspect inside entity
|
|
1453
|
-
// (TODO: complain if used and the managed composition is included into
|
|
1454
|
-
// another entity - might induce auto-redirection):
|
|
1455
|
-
if (inEntity && !targetAspect.elements.up_) {
|
|
1456
|
-
const up = {
|
|
1457
|
-
name: { id: 'up_' },
|
|
1458
|
-
kind: '$navElement',
|
|
1459
|
-
location: obj.location,
|
|
1460
|
-
};
|
|
1461
|
-
setLink( up, '_parent', targetAspect );
|
|
1462
|
-
setLink( up, '_main', targetAspect ); // used on main artifact
|
|
1463
|
-
// recompilation case: both target and targetAspect → allow up_ in that case, too:
|
|
1464
|
-
const name = obj.target && resolveUncheckedPath( obj.target, 'target', obj );
|
|
1465
|
-
const entity = name && model.definitions[name];
|
|
1466
|
-
if (entity && entity.elements)
|
|
1467
|
-
setLink( up, '_origin', entity.elements.up_ );
|
|
1468
|
-
// processAspectComposition/expand() sets _origin to element of
|
|
1469
|
-
// generated target entity
|
|
1470
|
-
targetAspect.$tableAliases.up_ = up;
|
|
1471
|
-
}
|
|
1472
|
-
obj = targetAspect;
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
1441
|
function init( elem, name, prop ) {
|
|
1476
1442
|
if (!elem.name && !elem._outer) {
|
|
1477
1443
|
const ref = elem.targetElement || elem.kind === 'element' && elem.value;
|
|
@@ -1670,6 +1636,7 @@ function extend( model ) {
|
|
|
1670
1636
|
// ...ext.includes.map( r => r._artifact?.name.id ));
|
|
1671
1637
|
if (!art.query && art.elements) // do not set art.elements and art.enums with query entity!
|
|
1672
1638
|
includeMembers( ext, art, 'elements' );
|
|
1639
|
+
|
|
1673
1640
|
if (art.kind !== 'type') {
|
|
1674
1641
|
includeMembers( ext, art, 'actions' );
|
|
1675
1642
|
}
|
|
@@ -1696,7 +1663,7 @@ function extend( model ) {
|
|
|
1696
1663
|
// TODO two kind of messages:
|
|
1697
1664
|
// Error 'More than one include defines element "A"' (at include ref)
|
|
1698
1665
|
// Warning 'Overwrites definition from include "I" (at elem def)
|
|
1699
|
-
const propagateKeys = art.kind !== 'type' ||
|
|
1666
|
+
const propagateKeys = art.kind !== 'type' || model.options.v6KeyPropagation;
|
|
1700
1667
|
const parent = ext === art && art;
|
|
1701
1668
|
const members = ext[prop];
|
|
1702
1669
|
// if (members)console.log( 'EXT:', prop, art.kind, art.name.id, ...Object.keys(members));
|
|
@@ -1704,9 +1671,29 @@ function extend( model ) {
|
|
|
1704
1671
|
ext[prop] = Object.create( null );
|
|
1705
1672
|
ext[prop][$location] = members[$location];
|
|
1706
1673
|
}
|
|
1674
|
+
let elementsAdded = 0;
|
|
1707
1675
|
let hasNewElement = false;
|
|
1708
|
-
|
|
1709
1676
|
for (const ref of ext.includes) {
|
|
1677
|
+
// Elements of include are added after previously provided elements:
|
|
1678
|
+
if (prop === 'elements') {
|
|
1679
|
+
if (ref.$postponeInclude == null) {
|
|
1680
|
+
if (ref._artifact?.elements)
|
|
1681
|
+
ref.$postponeInclude = art.$postponeInclude || 0;
|
|
1682
|
+
}
|
|
1683
|
+
else if (ref.$postponeInclude > 0 && members) {
|
|
1684
|
+
let added = elementsAdded;
|
|
1685
|
+
let postpone = ref.$postponeInclude;
|
|
1686
|
+
elementsAdded += postpone;
|
|
1687
|
+
forEachInOrder( { [prop]: members }, prop, ( elem, name ) => {
|
|
1688
|
+
// The element could have been added in the previous loop (includes) to keep
|
|
1689
|
+
// the element order.
|
|
1690
|
+
if (--added < 0 && --postpone >= 0 && ext[prop][name] !== elem )
|
|
1691
|
+
dictAdd( ext[prop], name, elem );
|
|
1692
|
+
} );
|
|
1693
|
+
}
|
|
1694
|
+
if (ref.$postponeInclude != null) // not reset after bare aspect
|
|
1695
|
+
art.$postponeInclude = 0; // elements of next include directly after this one
|
|
1696
|
+
}
|
|
1710
1697
|
const template = ref._artifact; // already resolved
|
|
1711
1698
|
if (template) { // be robust
|
|
1712
1699
|
if (template[prop] && !ext[prop])
|
|
@@ -1724,7 +1711,7 @@ function extend( model ) {
|
|
|
1724
1711
|
if (checkForLocalized && (name === 'texts' || name === 'localized'))
|
|
1725
1712
|
return;
|
|
1726
1713
|
if (members && members[name]) {
|
|
1727
|
-
if (!
|
|
1714
|
+
if (!ext[prop][name])
|
|
1728
1715
|
dictAdd( ext[prop], name, members[name] ); // to keep order
|
|
1729
1716
|
return;
|
|
1730
1717
|
}
|
|
@@ -1734,8 +1721,6 @@ function extend( model ) {
|
|
|
1734
1721
|
if (!parent) // not yet set for EXTEND foo WITH bar => linkToOrigin() did not add it
|
|
1735
1722
|
dictAdd( ext[prop], name, elem );
|
|
1736
1723
|
elem.$inferred = 'include';
|
|
1737
|
-
if (origin.masked) // TODO(v6): remove 'masked'
|
|
1738
|
-
elem.masked = Object.assign( { $inferred: 'include' }, origin.masked );
|
|
1739
1724
|
if (origin.key && propagateKeys)
|
|
1740
1725
|
elem.key = Object.assign( { $inferred: 'include' }, origin.key );
|
|
1741
1726
|
if (origin.value && origin.$syntax === 'calc') {
|
|
@@ -1754,14 +1739,20 @@ function extend( model ) {
|
|
|
1754
1739
|
}
|
|
1755
1740
|
if (!hasNewElement && members) {
|
|
1756
1741
|
ext[prop] = members;
|
|
1742
|
+
if (prop === 'elements')
|
|
1743
|
+
art.$postponeInclude += addedElements( ext );
|
|
1757
1744
|
}
|
|
1758
1745
|
else if (members) {
|
|
1746
|
+
let added = elementsAdded;
|
|
1759
1747
|
// TODO: expand elements having direct elements (if needed)
|
|
1760
1748
|
forEachInOrder( { [prop]: members }, prop, ( elem, name ) => {
|
|
1761
1749
|
// The element could have been added in the previous loop (includes) to keep
|
|
1762
1750
|
// the element order.
|
|
1763
|
-
if (ext[prop][name] !== elem )
|
|
1751
|
+
if (--added < 0 && ext[prop][name] !== elem ) {
|
|
1764
1752
|
dictAdd( ext[prop], name, elem );
|
|
1753
|
+
if (prop === 'elements' && elem.kind !== 'extend')
|
|
1754
|
+
++art.$postponeInclude;
|
|
1755
|
+
}
|
|
1765
1756
|
} );
|
|
1766
1757
|
}
|
|
1767
1758
|
}
|
package/lib/compiler/generate.js
CHANGED
|
@@ -47,6 +47,7 @@ function generate( model ) {
|
|
|
47
47
|
const textsAspect = model.definitions['sap.common.TextsAspect'];
|
|
48
48
|
let enableLanguageAssoc = null; // → addLanguageAssoc()
|
|
49
49
|
let enableTextsAspect = null; // → useTextsAspect()
|
|
50
|
+
const copyJournal = !isDeprecatedEnabled( options, '_noPersistenceJournalForGeneratedEntities' );
|
|
50
51
|
return;
|
|
51
52
|
|
|
52
53
|
/**
|
|
@@ -725,7 +726,7 @@ function generate( model ) {
|
|
|
725
726
|
// annotations (remark: adding elements is not allowed for generated artifacts):
|
|
726
727
|
extendArtifactBefore( art );
|
|
727
728
|
setGenExtensions( art, { _composition: elem } );
|
|
728
|
-
setLink( art, '_origin', target ); //
|
|
729
|
+
setLink( art, '_origin', target ); // needed especially with anonymous target aspect
|
|
729
730
|
return art;
|
|
730
731
|
}
|
|
731
732
|
|
|
@@ -746,7 +747,7 @@ function generate( model ) {
|
|
|
746
747
|
}
|
|
747
748
|
|
|
748
749
|
if (targetAspect.name) { // named target aspect
|
|
749
|
-
if (!isDeprecatedEnabled( options, '
|
|
750
|
+
if (!isDeprecatedEnabled( options, '_noCompositionIncludes' )) {
|
|
750
751
|
art.includes = [ createInclude( targetAspect.name.id, location ) ];
|
|
751
752
|
art.includes.$origin = []; // included elements after up_
|
|
752
753
|
// TODO: propagate in effectiveType()
|
|
@@ -790,8 +791,6 @@ function generate( model ) {
|
|
|
790
791
|
setLink( proxy, '_block', origin._block );
|
|
791
792
|
if (location)
|
|
792
793
|
proxy.$inferred = inferred;
|
|
793
|
-
if (origin.masked)
|
|
794
|
-
proxy.masked = Object.assign( { $inferred: 'include' }, origin.masked );
|
|
795
794
|
if (origin.key)
|
|
796
795
|
proxy.key = Object.assign( { $inferred: 'include' }, origin.key );
|
|
797
796
|
if (origin.value && origin.$syntax === 'calc') {
|
|
@@ -824,13 +823,10 @@ function generate( model ) {
|
|
|
824
823
|
return;
|
|
825
824
|
|
|
826
825
|
// Copied since v6
|
|
827
|
-
const copyJournal = !isDeprecatedEnabled( options, 'noPersistenceJournalForGeneratedEntities' );
|
|
828
826
|
if (copyJournal)
|
|
829
827
|
copy( '@cds.persistence.journal' );
|
|
830
828
|
|
|
831
|
-
|
|
832
|
-
if (copyExists)
|
|
833
|
-
copy( '@cds.persistence.exists' );
|
|
829
|
+
copy( '@cds.persistence.exists' );
|
|
834
830
|
copy( '@cds.persistence.skip' );
|
|
835
831
|
copy( '@cds.tenant.independent' );
|
|
836
832
|
|
package/lib/compiler/populate.js
CHANGED
|
@@ -459,8 +459,7 @@ function populate( model ) {
|
|
|
459
459
|
const location = weakRefLocation( ref ) || weakLocation( art.location );
|
|
460
460
|
// console.log( message( null, location, art, {target:struct,art}, 'Info','EXPAND-ELEM')
|
|
461
461
|
// .toString(), Object.keys(struct.elements))
|
|
462
|
-
proxyCopyMembers( art, 'elements', struct.elements, location,
|
|
463
|
-
null, isDeprecatedEnabled( options, '_noKeyPropagationWithExpansions' ) );
|
|
462
|
+
proxyCopyMembers( art, 'elements', struct.elements, location, null );
|
|
464
463
|
// Set elements expansion status (the if condition is always true, as no
|
|
465
464
|
// elements expansion will take place on artifact with existing other
|
|
466
465
|
// member property):
|
|
@@ -724,9 +723,8 @@ function populate( model ) {
|
|
|
724
723
|
// (we might have those already)
|
|
725
724
|
if (alias.kind === 'mixin' || alias.kind === '$self')
|
|
726
725
|
return;
|
|
727
|
-
if (!alias
|
|
728
|
-
|
|
729
|
-
|
|
726
|
+
if (!effectiveType( alias )) // not found or cyclic
|
|
727
|
+
return;
|
|
730
728
|
forEachGeneric( { elements: alias.elements }, 'elements', ( elem, name ) => {
|
|
731
729
|
if (elem.$duplicates !== true)
|
|
732
730
|
dictAddArray( query._combined, name, elem, null ); // not dictAdd()
|
|
@@ -887,8 +885,7 @@ function populate( model ) {
|
|
|
887
885
|
|
|
888
886
|
for (const name in env) {
|
|
889
887
|
const navElem = env[name];
|
|
890
|
-
|
|
891
|
-
if (excludingDict[name] || navElem.masked?.val)
|
|
888
|
+
if (excludingDict[name])
|
|
892
889
|
continue;
|
|
893
890
|
const sibling = siblingElements[name];
|
|
894
891
|
if (sibling) { // is explicitly provided (without duplicate)
|
|
@@ -37,7 +37,6 @@ function propagate( model ) {
|
|
|
37
37
|
// TODO(!): think of having an extra XSN property for calculated elements,
|
|
38
38
|
// replacing `value:…`+`$syntax:'calc'` and `$calc:…
|
|
39
39
|
$calc: enumOrCalcValue,
|
|
40
|
-
// masked: special = done in definer
|
|
41
40
|
// key: special = done in resolver
|
|
42
41
|
// actions: struct includes & primary source = in definer/resolver
|
|
43
42
|
type: notWithExpand,
|
|
@@ -65,7 +64,7 @@ function propagate( model ) {
|
|
|
65
64
|
};
|
|
66
65
|
const ruleToFunction = {
|
|
67
66
|
__proto__: null,
|
|
68
|
-
never,
|
|
67
|
+
never: onlyViaParent,
|
|
69
68
|
onlyViaArtifact,
|
|
70
69
|
onlyViaParent,
|
|
71
70
|
notWithPersistenceTable,
|
|
@@ -165,10 +164,11 @@ function propagate( model ) {
|
|
|
165
164
|
const viaType = target.type && // TODO: falsy $inferred value instead of 'cast'?
|
|
166
165
|
(!target.type.$inferred || target.type.$inferred === 'cast');
|
|
167
166
|
const keys = Object.keys( source );
|
|
168
|
-
// console.log('PROPS:',
|
|
167
|
+
// console.log('PROPS:',source.name.id,'->',target.name.id,keys.join('+'))
|
|
169
168
|
for (const prop of keys) {
|
|
170
169
|
// TODO: warning with competing props from multi-includes, but not in propagator.js
|
|
171
|
-
if (target[prop] !== undefined &&
|
|
170
|
+
if ((target[prop] !== undefined && target[prop]?.$inferred !== 'prop-null') &&
|
|
171
|
+
// do not propagate into calculated element from calc expression:
|
|
172
172
|
(prop !== 'value' || !source.$calcDepElement || !target._main?.query) ||
|
|
173
173
|
source[prop] === undefined)
|
|
174
174
|
continue;
|
|
@@ -178,8 +178,6 @@ function propagate( model ) {
|
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
function never() { /* no-op: don't propagate */ }
|
|
182
|
-
|
|
183
181
|
function always( prop, target, source ) {
|
|
184
182
|
const val = source[prop];
|
|
185
183
|
if (Array.isArray( val )) {
|
|
@@ -261,6 +259,7 @@ function propagate( model ) {
|
|
|
261
259
|
}
|
|
262
260
|
|
|
263
261
|
// Only propagate if parent object (which is not necessarily `_parent`) was propagated.
|
|
262
|
+
// (part of a deep-copy)
|
|
264
263
|
function onlyViaParent( prop, target, source ) {
|
|
265
264
|
if (target.$inferred === 'proxy' || target.$inferred === 'expanded')
|
|
266
265
|
// assocs and enums do not have 'include'
|
|
@@ -316,14 +315,15 @@ function propagate( model ) {
|
|
|
316
315
|
|
|
317
316
|
function annotation( prop, target, source ) {
|
|
318
317
|
const anno = source[prop];
|
|
319
|
-
|
|
320
|
-
|
|
318
|
+
withKind( prop, target, source );
|
|
319
|
+
if (anno.val === null && target[prop]?.$inferred === 'prop')
|
|
320
|
+
target[prop].$inferred = 'prop-null';
|
|
321
321
|
}
|
|
322
322
|
|
|
323
323
|
function docComment( prop, target, source ) {
|
|
324
324
|
if (model.options.propagateDocComments)
|
|
325
325
|
annotation( prop, target, source );
|
|
326
|
-
else
|
|
326
|
+
else
|
|
327
327
|
onlyViaParent( prop, target, source );
|
|
328
328
|
}
|
|
329
329
|
|