@sap/cds-compiler 2.13.6 → 2.15.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.
Files changed (78) hide show
  1. package/CHANGELOG.md +128 -4
  2. package/bin/cdsc.js +112 -37
  3. package/lib/api/main.js +20 -22
  4. package/lib/api/options.js +2 -3
  5. package/lib/api/validate.js +6 -6
  6. package/lib/base/message-registry.js +92 -17
  7. package/lib/base/messages.js +85 -64
  8. package/lib/base/optionProcessorHelper.js +19 -0
  9. package/lib/checks/annotationsOData.js +11 -32
  10. package/lib/checks/arrayOfs.js +1 -34
  11. package/lib/checks/validator.js +2 -4
  12. package/lib/compiler/assert-consistency.js +1 -0
  13. package/lib/compiler/base.js +1 -0
  14. package/lib/compiler/builtins.js +11 -0
  15. package/lib/compiler/checks.js +22 -70
  16. package/lib/compiler/define.js +59 -11
  17. package/lib/compiler/extend.js +20 -3
  18. package/lib/compiler/finalize-parse-cdl.js +26 -20
  19. package/lib/compiler/index.js +75 -26
  20. package/lib/compiler/populate.js +6 -5
  21. package/lib/compiler/propagator.js +4 -1
  22. package/lib/compiler/resolve.js +104 -16
  23. package/lib/compiler/shared.js +61 -27
  24. package/lib/compiler/tweak-assocs.js +7 -1
  25. package/lib/edm/annotations/genericTranslation.js +93 -21
  26. package/lib/edm/csn2edm.js +216 -98
  27. package/lib/edm/edm.js +305 -226
  28. package/lib/edm/edmPreprocessor.js +499 -423
  29. package/lib/edm/edmUtils.js +22 -22
  30. package/lib/gen/Dictionary.json +98 -22
  31. package/lib/gen/language.checksum +1 -1
  32. package/lib/gen/language.interp +3 -1
  33. package/lib/gen/languageParser.js +4636 -4368
  34. package/lib/json/csnVersion.js +10 -11
  35. package/lib/json/from-csn.js +3 -2
  36. package/lib/json/to-csn.js +0 -2
  37. package/lib/language/docCommentParser.js +2 -2
  38. package/lib/language/genericAntlrParser.js +47 -2
  39. package/lib/language/language.g4 +59 -27
  40. package/lib/main.d.ts +19 -1
  41. package/lib/main.js +6 -0
  42. package/lib/model/csnRefs.js +33 -6
  43. package/lib/model/csnUtils.js +193 -75
  44. package/lib/model/enrichCsn.js +1 -0
  45. package/lib/model/revealInternalProperties.js +2 -2
  46. package/lib/modelCompare/compare.js +6 -6
  47. package/lib/optionProcessor.js +62 -26
  48. package/lib/render/toCdl.js +844 -679
  49. package/lib/render/toHdbcds.js +189 -243
  50. package/lib/render/toSql.js +180 -198
  51. package/lib/render/utils/common.js +131 -15
  52. package/lib/transform/db/.eslintrc.json +1 -1
  53. package/lib/transform/db/associations.js +2 -2
  54. package/lib/transform/db/constraints.js +3 -1
  55. package/lib/transform/db/expansion.js +15 -10
  56. package/lib/transform/db/flattening.js +95 -68
  57. package/lib/transform/db/transformExists.js +7 -7
  58. package/lib/transform/db/views.js +6 -3
  59. package/lib/transform/forHanaNew.js +43 -26
  60. package/lib/transform/forOdataNew.js +43 -42
  61. package/lib/transform/localized.js +12 -7
  62. package/lib/transform/odata/toFinalBaseType.js +8 -6
  63. package/lib/transform/odata/typesExposure.js +145 -197
  64. package/lib/transform/transformUtilsNew.js +9 -12
  65. package/lib/transform/translateAssocsToJoins.js +5 -1
  66. package/lib/transform/universalCsn/coreComputed.js +5 -3
  67. package/lib/transform/universalCsn/universalCsnEnricher.js +27 -5
  68. package/lib/utils/moduleResolve.js +13 -6
  69. package/package.json +1 -1
  70. package/share/messages/message-explanations.json +2 -1
  71. package/share/messages/syntax-expected-integer.md +37 -0
  72. package/lib/transform/odata/attachPath.js +0 -96
  73. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  74. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  75. package/lib/transform/odata/referenceFlattener.js +0 -296
  76. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  77. package/lib/transform/odata/structuralPath.js +0 -72
  78. package/lib/transform/odata/structureFlattener.js +0 -171
@@ -447,13 +447,13 @@ function getUtils(model, universalReady) {
447
447
  * The transformer functions are called with the following signature:
448
448
  * transformer(value, node, resultNode, key)
449
449
  *
450
- * @param {any} node Node to transform
450
+ * @param {any} rootNode Node to transform
451
451
  * @param {any} transformers Object defining transformer functions
452
452
  * @returns {object}
453
453
  */
454
- function cloneWithTransformations(node, transformers) {
454
+ function cloneWithTransformations(rootNode, transformers) {
455
455
 
456
- return transformNode(node);
456
+ return transformNode(rootNode);
457
457
 
458
458
  // This general transformation function will be applied to each node recursively
459
459
  function transformNode(node) {
@@ -588,7 +588,7 @@ function getUtils(model, universalReady) {
588
588
  * @param {object} csn Top-level CSN. You can pass non-dictionary values.
589
589
  * @param {CSN.Options} options CSN Options, only used for `dictionaryPrototype`, `testMode`, and `testSortCsn`
590
590
  */
591
- function cloneCsn(csn, options) {
591
+ function cloneCsnNonDict(csn, options) {
592
592
  return sortCsn(csn, options);
593
593
  }
594
594
 
@@ -596,7 +596,7 @@ function cloneCsn(csn, options) {
596
596
  * Deeply clone the given CSN dictionary and return it.
597
597
  * Note that annotations are only copied shallowly.
598
598
  * This function does _not_ sort the given dictionary.
599
- * See cloneCsn() if you want sorted definitions.
599
+ * See cloneCsnNonDict() if you want sorted definitions.
600
600
  *
601
601
  * @param {object} csn
602
602
  * @param {CSN.Options} options Only cloneOptions.dictionaryPrototype is
@@ -632,8 +632,10 @@ function forEachDefinition( csn, callback, iterateOptions = {} ) {
632
632
  * @param {CSN.Path} [path]
633
633
  * @param {boolean} [ignoreIgnore]
634
634
  * @param {object} iterateOptions can be used to skip certain kinds from being iterated
635
+ * @param constructCallback
635
636
  */
636
- function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {}) {
637
+ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
638
+ constructCallback = (_construct, _prop, _path) => {}) {
637
639
  // Allow processing _ignored elements if requested
638
640
  if (ignoreIgnore && construct._ignore) {
639
641
  return;
@@ -641,8 +643,7 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
641
643
 
642
644
  // `items` itself is a structure that can contain "elements", and more.
643
645
  if (construct.items) {
644
- // TODO: Should we go to the deepest items.items?
645
- forEachMember( construct.items, callback, [...path, 'items'], ignoreIgnore, iterateOptions );
646
+ forEachMember( construct.items, callback, [...path, 'items'], ignoreIgnore, iterateOptions, constructCallback );
646
647
  }
647
648
 
648
649
  // Unlike XSN, we don't make "returns" a "params" in the callback.
@@ -650,12 +651,43 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
650
651
  // `elements` of the return type (if structured).
651
652
  // TODO: `returns` should be handled like a parameter just like XSN (maybe with different prop name)
652
653
  if (construct.returns && !iterateOptions.elementsOnly) {
653
- forEachMember( construct.returns, callback, [...path, 'returns'], ignoreIgnore, iterateOptions );
654
+ forEachMember( construct.returns, callback, [...path, 'returns'], ignoreIgnore, iterateOptions, constructCallback );
654
655
  }
655
656
 
656
657
  path = [...path]; // Copy
657
658
  const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', 'actions', 'params']);
658
- propsWithMembers.forEach((prop) => forEachGeneric( construct, prop, callback, path, iterateOptions ));
659
+ propsWithMembers.forEach((prop) => {
660
+ forEachGeneric( construct, prop, callback, path, iterateOptions );
661
+ if (construct[prop]) {
662
+ if (Array.isArray(constructCallback))
663
+ constructCallback.forEach(cb => cb(construct, prop, path));
664
+ else
665
+ constructCallback(construct, prop, path);
666
+ }
667
+ });
668
+ }
669
+
670
+ /**
671
+ * Call `forEachMember` and then apply `forEachMember` on queries.
672
+ *
673
+ * @param {CSN.Artifact} construct
674
+ * @param {genericCallback|genericCallback[]} callback
675
+ * @param {CSN.Path} [path]
676
+ * @param {boolean} [ignoreIgnore]
677
+ * @param {object} iterateOptions can be used to skip certain kinds from being iterated
678
+ * @param {constructCallback|constructCallback[]} callback
679
+ */
680
+ function forEachMemberWithQuery( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
681
+ constructCallback = (_construct, _prop, _path) => {}) {
682
+ forEachMember(construct, callback, path, ignoreIgnore, iterateOptions, constructCallback);
683
+ if (construct.query) {
684
+ forAllQueries(construct.query, (q, p) => {
685
+ const s = q.SELECT;
686
+ if(s) {
687
+ forEachMember(s, callback, p, ignoreIgnore, iterateOptions);
688
+ }
689
+ }, [ ...path, 'query' ]);
690
+ }
659
691
  }
660
692
 
661
693
  /**
@@ -667,16 +699,43 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
667
699
  * @param {CSN.Path} [path]
668
700
  * @param {boolean} [ignoreIgnore]
669
701
  * @param {object} iterateOptions can be used to skip certain kinds from being iterated
702
+ * @param {constructCallback|constructCallback[]} callback
670
703
  */
671
- function forEachMemberRecursively( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {}) {
672
- forEachMember( construct, ( member, memberName, prop, subpath ) => {
704
+ function forEachMemberRecursively( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
705
+ constructCallback = (_construct, _prop, _path) => {}) {
706
+ forEachMember( construct, ( member, memberName, prop, subpath, parent ) => {
673
707
  if(Array.isArray(callback))
674
- callback.forEach(cb => cb( member, memberName, prop, subpath, construct ));
708
+ callback.forEach(cb => cb( member, memberName, prop, subpath, parent ));
675
709
  else
676
- callback( member, memberName, prop, subpath, construct );
710
+ callback( member, memberName, prop, subpath, parent );
677
711
  // Descend into nested members, too
678
- forEachMemberRecursively( member, callback, subpath, ignoreIgnore, iterateOptions);
679
- }, path, ignoreIgnore, iterateOptions);
712
+ forEachMemberRecursively( member, callback, subpath, ignoreIgnore, iterateOptions, constructCallback);
713
+ }, path, ignoreIgnore, iterateOptions, constructCallback);
714
+ }
715
+
716
+ /**
717
+ * Apply function `callback(member, memberName)` to each member in `construct`,
718
+ * recursively (i.e. also for sub-elements of elements).
719
+ * Recursively iterate over elements of `construct` query.
720
+ *
721
+ * @param {CSN.Artifact} construct
722
+ * @param {genericCallback|genericCallback[]} callback
723
+ * @param {CSN.Path} [path]
724
+ * @param {boolean} [ignoreIgnore]
725
+ * @param {object} iterateOptions can be used to skip certain kinds from being iterated
726
+ * @param {constructCallback|constructCallback[]} callback
727
+ */
728
+ function forEachMemberRecursivelyWithQuery( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
729
+ constructCallback = (_construct, _prop, _path) => {}) {
730
+ forEachMemberRecursively(construct, callback, path, ignoreIgnore, iterateOptions, constructCallback);
731
+ if(construct.query) {
732
+ forAllQueries(construct.query, (q, p) => {
733
+ const s = q.SELECT;
734
+ if(s) {
735
+ forEachMemberRecursively(s, callback, p, ignoreIgnore, iterateOptions);
736
+ }
737
+ }, [ ...path, 'query' ]);
738
+ }
680
739
  }
681
740
 
682
741
  /**
@@ -685,14 +744,14 @@ function forEachMemberRecursively( construct, callback, path=[], ignoreIgnore=tr
685
744
  * the following arguments: the object, the name, and -if it is a duplicate-
686
745
  * the array index and the array containing all duplicates.
687
746
  *
688
- * @param {object} obj
747
+ * @param {object} construct
689
748
  * @param {string} prop
690
749
  * @param {genericCallback|genericCallback[]} callback
691
750
  * @param {CSN.Path} path
692
751
  * @param {object} iterateOptions can be used to skip certain kinds from being iterated
693
752
  */
694
- function forEachGeneric( obj, prop, callback, path = [], iterateOptions = {}) {
695
- const dict = obj[prop];
753
+ function forEachGeneric( construct, prop, callback, path = [], iterateOptions = {}) {
754
+ const dict = construct[prop];
696
755
  for (const name in dict) {
697
756
  if (!Object.prototype.hasOwnProperty.call(dict, name))
698
757
  continue;
@@ -701,26 +760,26 @@ function forEachGeneric( obj, prop, callback, path = [], iterateOptions = {}) {
701
760
  || (iterateOptions.skipArtifact && typeof iterateOptions.skipArtifact === 'function'
702
761
  && iterateOptions.skipArtifact(dictObj, name)))
703
762
  continue;
704
- cb( dictObj, name );
763
+ executeCallbacks( dictObj, name );
705
764
  }
706
- function cb(o, name ) {
765
+ function executeCallbacks(o, name ) {
707
766
  if (Array.isArray(callback))
708
- callback.forEach(cb => cb( o, name, prop, path.concat([prop, name])));
767
+ callback.forEach(cb => cb( o, name, prop, path.concat([prop, name]), construct ));
709
768
  else
710
- callback( o, name, prop, path.concat([prop, name]))
769
+ callback( o, name, prop, path.concat([prop, name]), construct )
711
770
  }
712
771
  }
713
772
 
714
773
  // Like Object.assign() but copies also non enumerable properties
715
774
  function assignAll(target, ...sources) {
716
775
  sources.forEach(source => {
717
- let descriptors = Object.getOwnPropertyNames(source).reduce((descriptors, key) => {
718
- descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
719
- return descriptors;
776
+ const descriptors = Object.getOwnPropertyNames(source).reduce((propertyDescriptors, current) => {
777
+ propertyDescriptors[current] = Object.getOwnPropertyDescriptor(source, current);
778
+ return propertyDescriptors;
720
779
  }, {});
721
780
  // by default, Object.assign copies enumerable Symbols too
722
781
  Object.getOwnPropertySymbols(source).forEach(sym => {
723
- let descriptor = Object.getOwnPropertyDescriptor(source, sym);
782
+ const descriptor = Object.getOwnPropertyDescriptor(source, sym);
724
783
  if (descriptor.enumerable) {
725
784
  descriptors[sym] = descriptor;
726
785
  }
@@ -731,68 +790,68 @@ function assignAll(target, ...sources) {
731
790
  }
732
791
 
733
792
  /**
734
- * @param {CSN.Query} query
735
- * @param {queryCallback|queryCallback[]} callback
793
+ * @param {CSN.Query} mainQuery
794
+ * @param {queryCallback|queryCallback[]} queryCallback
736
795
  * @param {CSN.Path} path
737
796
  */
738
- function forAllQueries(query, callback, path = []){
739
- return traverseQuery(query, callback, path);
740
- function traverseQuery( q, callback, p ) {
741
- if (q.SELECT) {
797
+ function forAllQueries(mainQuery, queryCallback, path = []){
798
+ return traverseQuery(mainQuery, queryCallback, path);
799
+ function traverseQuery( query, callback, queryPath ) {
800
+ if (query.SELECT) {
742
801
  // The projection is turned into a normalized query - there
743
802
  // is no real SELECT, it is fake
744
803
  if(!(path.length === 3 && path[2] === 'projection'))
745
- p.push('SELECT');
746
- cb( q, p );
747
- q = q.SELECT;
804
+ queryPath.push('SELECT');
805
+ executeCallbacks();
806
+ query = query.SELECT;
748
807
  }
749
- else if (q.SET) {
750
- p.push('SET');
751
- cb( q, p );
752
- q = q.SET;
808
+ else if (query.SET) {
809
+ queryPath.push('SET');
810
+ executeCallbacks();
811
+ query = query.SET;
753
812
  }
754
813
 
755
- if (q.from)
756
- traverseFrom( q.from, callback, p.concat(['from']) );
814
+ if (query.from)
815
+ traverseFrom( query.from, callback, queryPath.concat(['from']) );
757
816
 
758
817
  for (const prop of ['args', 'xpr', 'columns', 'where', 'having']) {
759
818
  // all properties which could have sub queries (directly or indirectly)
760
- const expr = q[prop];
819
+ const expr = query[prop];
761
820
  if (expr && typeof expr === 'object') {
762
821
  if(Array.isArray(expr)){
763
822
  for(let i = 0; i < expr.length; i++){
764
- traverseQuery(expr[i], callback, p.concat([prop, i]));
823
+ traverseQuery(expr[i], callback, queryPath.concat([prop, i]));
765
824
  }
766
825
  } else {
767
826
  for(const argName of Object.keys( expr )){
768
- traverseQuery(expr[argName], callback, p.concat([prop, argName]))
827
+ traverseQuery(expr[argName], callback, queryPath.concat([prop, argName]))
769
828
  }
770
829
  }
771
830
  }
772
831
  }
773
- function cb(q, p) {
832
+ function executeCallbacks() {
774
833
  if(Array.isArray(callback))
775
- callback.forEach(cb => cb( q, p ));
834
+ callback.forEach(cb => cb( query, queryPath ));
776
835
  else
777
- callback( q, p );
836
+ callback( query, queryPath );
778
837
  }
779
838
  }
780
839
 
781
840
  /**
782
841
  * @param {CSN.QueryFrom} from
783
842
  * @param {Function} callback
784
- * @param {CSN.Path} path
843
+ * @param {CSN.Path} csnPath
785
844
  */
786
- function traverseFrom( from, callback, path = [] ) {
845
+ function traverseFrom( from, callback, csnPath = [] ) {
787
846
  if (from.ref) // ignore
788
847
  return;
789
848
  else if (from.args){ // join
790
849
  for(let i = 0; i < from.args.length; i++){
791
- traverseFrom(from.args[i], callback, path.concat(['args', i]));
850
+ traverseFrom(from.args[i], callback, csnPath.concat(['args', i]));
792
851
  }
793
852
  }
794
853
  else
795
- traverseQuery( from, callback, path ); // sub query in FROM
854
+ traverseQuery( from, callback, csnPath ); // sub query in FROM
796
855
  }
797
856
  }
798
857
 
@@ -967,7 +1026,7 @@ function getUnderscoredName(startIndex, parts, csn) {
967
1026
 
968
1027
  return result;
969
1028
  } else if(art && art.kind === 'service') {
970
- // inside services, we immediatly turn . into _
1029
+ // inside services, we immediately turn . into _
971
1030
  const prefix = parts.slice(0, i).join('.');
972
1031
  const suffix = parts.slice(i).join('_');
973
1032
  const result = [];
@@ -1139,17 +1198,17 @@ function mergeOptions(...optionsObjects) {
1139
1198
 
1140
1199
  // Recursively used for scalars, too
1141
1200
  function mergeTwo(left, right, name) {
1142
- let result;
1201
+ let intermediateResult;
1143
1202
  // Copy left as far as required
1144
1203
  if (Array.isArray(left)) {
1145
1204
  // Shallow-copy left array
1146
- result = left.slice();
1205
+ intermediateResult = left.slice();
1147
1206
  } else if (isObject(left)) {
1148
1207
  // Deep-copy left object (unless empty)
1149
- result = Object.keys(left).length ? mergeTwo({}, left, name) : {};
1208
+ intermediateResult = Object.keys(left).length ? mergeTwo({}, left, name) : {};
1150
1209
  } else {
1151
1210
  // Just use left scalar
1152
- result = left;
1211
+ intermediateResult = left;
1153
1212
  }
1154
1213
  // Check against improper overwriting
1155
1214
  if (isObject(left) && !Array.isArray(left) && (Array.isArray(right) || isScalar(right))) {
@@ -1162,21 +1221,21 @@ function mergeOptions(...optionsObjects) {
1162
1221
  // Copy or overwrite properties from right to left
1163
1222
  if (Array.isArray(right)) {
1164
1223
  // Shallow-copy right array
1165
- result = right.slice();
1224
+ intermediateResult = right.slice();
1166
1225
  } else if (isObject(right)) {
1167
1226
  // Object overwrites undefined, scalars and arrays
1168
- if (result === undefined || isScalar(result) || Array.isArray(result)) {
1169
- result = {};
1227
+ if (intermediateResult === undefined || isScalar(intermediateResult) || Array.isArray(intermediateResult)) {
1228
+ intermediateResult = {};
1170
1229
  }
1171
1230
  // Deep-copy right object into result
1172
1231
  for (let key of Object.keys(right)) {
1173
- result[key] = mergeTwo(result[key], right[key], `${name}.${key}`);
1232
+ intermediateResult[key] = mergeTwo(intermediateResult[key], right[key], `${name}.${key}`);
1174
1233
  }
1175
1234
  } else {
1176
1235
  // Right scalar wins (unless undefined)
1177
- result = (right !== undefined) ? right : result;
1236
+ intermediateResult = (right !== undefined) ? right : intermediateResult;
1178
1237
  }
1179
- return result;
1238
+ return intermediateResult;
1180
1239
  }
1181
1240
 
1182
1241
  // Return true if 'o' is a non-null object or array
@@ -1264,19 +1323,73 @@ function getParentNamesOf(name) {
1264
1323
  }
1265
1324
 
1266
1325
 
1267
- // Copy all annotations from 'fromNode' to 'toNode'. Overwrite existing ones only if 'overwrite' is true
1268
- function copyAnnotations(fromNode, toNode, overwrite=false) {
1326
+ /**
1327
+ * Copy all annotations from 'fromNode' to 'toNode'.
1328
+ *
1329
+ * Overwrite existing ones only if 'overwrite' is true.
1330
+ *
1331
+ * @param {object} fromNode
1332
+ * @param {object} toNode
1333
+ * @param {boolean} [overwrite]
1334
+ */
1335
+ function copyAnnotations(fromNode, toNode, overwrite = false) {
1269
1336
  // Ignore if no toNode (in case of errors)
1270
- if (!toNode) {
1337
+ if (!toNode)
1271
1338
  return;
1339
+
1340
+ const annotations = Object.keys(fromNode).filter(key => key.startsWith('@'));
1341
+
1342
+ for (const anno of annotations) {
1343
+ if (toNode[anno] === undefined || overwrite) {
1344
+ toNode[anno] = fromNode[anno];
1345
+ }
1272
1346
  }
1273
- for (let prop in fromNode) {
1274
- if (!Object.hasOwnProperty.call( fromNode, prop ))
1275
- continue;
1276
- if (prop.startsWith('@')) {
1277
- if (toNode[prop] === undefined || overwrite) {
1278
- toNode[prop] = fromNode[prop];
1279
- }
1347
+ }
1348
+
1349
+
1350
+ /**
1351
+ * Same as `copyAnnotations()` but also copies the
1352
+ * annotation-like property `doc`.
1353
+ *
1354
+ * Overwrite existing ones only if 'overwrite' is true.
1355
+ *
1356
+ * @param {object} fromNode
1357
+ * @param {object} toNode
1358
+ * @param {boolean} [overwrite]
1359
+ */
1360
+ function copyAnnotationsAndDoc(fromNode, toNode, overwrite = false) {
1361
+ // Ignore if no toNode (in case of errors)
1362
+ if (!toNode)
1363
+ return;
1364
+
1365
+ const annotations = Object.keys(fromNode)
1366
+ .filter(key => key.startsWith('@') || key === 'doc');
1367
+
1368
+ for (const anno of annotations) {
1369
+ if (toNode[anno] === undefined || overwrite) {
1370
+ toNode[anno] = fromNode[anno];
1371
+ }
1372
+ }
1373
+ }
1374
+
1375
+ /**
1376
+ * Applies annotations from `csn.extensions` to definitions, i.e. top-level artifacts.
1377
+ * Does _not_ apply element/param/action/... annotations.
1378
+ * `config.filter` can be used to only copy annotations for those definitions,
1379
+ * for which the filter returns true.
1380
+ *
1381
+ * @param {CSN.Model} csn
1382
+ * @param {{overwrite?: boolean, filter?: (name: string) => boolean}} config
1383
+ */
1384
+ function applyDefinitionAnnotationsFromExtensions(csn, config) {
1385
+ if (!csn.extensions)
1386
+ return;
1387
+
1388
+ const filter = config.filter || ((_name) => true);
1389
+ for (const ext of csn.extensions) {
1390
+ const name = ext.annotate || ext.extend;
1391
+ if (name && csn.definitions[name] && filter(name)) {
1392
+ copyAnnotationsAndDoc(ext, csn.definitions[name], config.overwrite);
1280
1393
  }
1281
1394
  }
1282
1395
  }
@@ -1442,7 +1555,7 @@ function getVariableReplacement(ref, options) {
1442
1555
  * @returns {boolean} returns equality
1443
1556
  *
1444
1557
  * noExtendedProps remove '$', '_' and '@' properties from
1445
- * the comparision. This eliminates false negatives such as
1558
+ * the comparison. This eliminates false negatives such as
1446
1559
  * mismatching $locations or @odata.foreignKey4.
1447
1560
  */
1448
1561
  function isDeepEqual(obj, other, noExtendedProps) {
@@ -1472,14 +1585,18 @@ function isDeepEqual(obj, other, noExtendedProps) {
1472
1585
 
1473
1586
  module.exports = {
1474
1587
  getUtils,
1475
- cloneCsn,
1588
+ cloneCsn: cloneCsnNonDict, // Umbrella relies on this name
1589
+ cloneCsnNonDict,
1476
1590
  cloneCsnDictionary,
1477
1591
  isBuiltinType,
1478
1592
  assignAll,
1593
+ applyDefinitionAnnotationsFromExtensions,
1479
1594
  forEachGeneric,
1480
1595
  forEachDefinition,
1481
1596
  forEachMember,
1597
+ forEachMemberWithQuery,
1482
1598
  forEachMemberRecursively,
1599
+ forEachMemberRecursivelyWithQuery,
1483
1600
  forAllQueries,
1484
1601
  hasAnnotationValue,
1485
1602
  isEdmPropertyRendered,
@@ -1500,6 +1617,7 @@ module.exports = {
1500
1617
  getParentNameOf,
1501
1618
  getLastPartOf,
1502
1619
  copyAnnotations,
1620
+ copyAnnotationsAndDoc,
1503
1621
  isAspect,
1504
1622
  forEachPath,
1505
1623
  hasValidSkipOrExists,
@@ -152,6 +152,7 @@ function enrichCsn( csn, options = {} ) {
152
152
  else { // targetAspect, target
153
153
  csnPath.push( prop );
154
154
  dictionary( ref, 'elements', ref.elements );
155
+ _cache_debug( ref );
155
156
  csnPath.pop();
156
157
  }
157
158
  // } catch (e) {
@@ -36,7 +36,7 @@ const kindsRepresentedAsLinks = {
36
36
  // represent table alias in from / join-args property as link:
37
37
  $tableAlias: tableAliasAsLink,
38
38
  // represent "navigation elemens" in _combined as links:
39
- $navElement: (art, parent) => art._parent && parent !== art._parent.elements,
39
+ $navElement: (art, parent) => art._parent && parent !== art._parent.elements && art._parent.kind !== 'aspect',
40
40
  // represent mixin in $tableAliases as link:
41
41
  mixin: tableAliasAsLink,
42
42
  // represent $projection as link, as it is just another search name for $self:
@@ -129,7 +129,7 @@ function revealInternalProperties( model, nameOrPath ) {
129
129
 
130
130
  path = path.split('/');
131
131
  if (path.length === 1) {
132
- return reveal( xsn.definitions[path] );
132
+ return reveal( xsn.definitions[path] || xsn.vocabularies && xsn.vocabularies[path] );
133
133
  }
134
134
 
135
135
  // with the code below, we might miss the right transformer function
@@ -45,19 +45,19 @@ function validateCsnVersions(beforeModel, afterModel, options) {
45
45
  let afterVersionParts = afterVersion && afterVersion.split('.');
46
46
 
47
47
  if (!beforeVersionParts || beforeVersionParts.length < 2) {
48
- const { error, throwWithError } = makeMessageFunction(beforeModel, options, 'modelCompare');
48
+ const { error, throwWithAnyError } = makeMessageFunction(beforeModel, options, 'modelCompare');
49
49
  error(null, null, `Invalid CSN version: ${beforeVersion}`);
50
- throwWithError();
50
+ throwWithAnyError();
51
51
  }
52
52
  if (!afterVersionParts || afterVersionParts.length < 2) {
53
- const { error, throwWithError } = makeMessageFunction(afterModel, options, 'modelCompare');
53
+ const { error, throwWithAnyError } = makeMessageFunction(afterModel, options, 'modelCompare');
54
54
  error(null, null, `Invalid CSN version: ${afterVersion}`);
55
- throwWithError();
55
+ throwWithAnyError();
56
56
  }
57
57
  if (beforeVersionParts[0] > afterVersionParts[0] && !(options && options.allowCsnDowngrade)) {
58
- const { error, throwWithError } = makeMessageFunction(afterModel, options, 'modelCompare');
58
+ const { error, throwWithAnyError } = makeMessageFunction(afterModel, options, 'modelCompare');
59
59
  error(null, null, `Incompatible CSN versions: ${afterVersion} is a major downgrade from ${beforeVersion}. Is @sap/cds-compiler version ${require('../../package.json').version} outdated?`);
60
- throwWithError();
60
+ throwWithAnyError();
61
61
  }
62
62
  }
63
63