@sap/cds-compiler 2.15.2 → 3.0.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.
Files changed (111) hide show
  1. package/CHANGELOG.md +66 -1590
  2. package/bin/cdsc.js +42 -46
  3. package/doc/CHANGELOG_ARCHIVE.md +1592 -0
  4. package/doc/CHANGELOG_BETA.md +3 -4
  5. package/doc/CHANGELOG_DEPRECATED.md +35 -1
  6. package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
  7. package/doc/Versioning.md +20 -1
  8. package/lib/api/.eslintrc.json +2 -2
  9. package/lib/api/main.js +312 -143
  10. package/lib/api/options.js +15 -85
  11. package/lib/api/validate.js +6 -10
  12. package/lib/base/keywords.js +280 -110
  13. package/lib/base/message-registry.js +80 -24
  14. package/lib/base/messages.js +103 -52
  15. package/lib/base/model.js +44 -2
  16. package/lib/base/optionProcessorHelper.js +53 -21
  17. package/lib/checks/actionsFunctions.js +7 -5
  18. package/lib/checks/annotationsOData.js +1 -1
  19. package/lib/checks/cdsPersistence.js +1 -0
  20. package/lib/checks/elements.js +6 -6
  21. package/lib/checks/invalidTarget.js +1 -1
  22. package/lib/checks/nonexpandableStructured.js +1 -1
  23. package/lib/checks/queryNoDbArtifacts.js +2 -1
  24. package/lib/checks/selectItems.js +5 -1
  25. package/lib/checks/types.js +4 -2
  26. package/lib/checks/utils.js +2 -2
  27. package/lib/checks/validator.js +2 -1
  28. package/lib/compiler/assert-consistency.js +15 -10
  29. package/lib/compiler/builtins.js +127 -10
  30. package/lib/compiler/define.js +6 -4
  31. package/lib/compiler/extend.js +63 -12
  32. package/lib/compiler/finalize-parse-cdl.js +20 -9
  33. package/lib/compiler/index.js +25 -11
  34. package/lib/compiler/moduleLayers.js +7 -0
  35. package/lib/compiler/populate.js +16 -14
  36. package/lib/compiler/propagator.js +3 -3
  37. package/lib/compiler/resolve.js +194 -222
  38. package/lib/compiler/shared.js +56 -76
  39. package/lib/compiler/tweak-assocs.js +9 -10
  40. package/lib/compiler/utils.js +7 -2
  41. package/lib/edm/annotations/genericTranslation.js +60 -6
  42. package/lib/edm/annotations/preprocessAnnotations.js +10 -11
  43. package/lib/edm/csn2edm.js +39 -41
  44. package/lib/edm/edm.js +22 -15
  45. package/lib/edm/edmPreprocessor.js +66 -69
  46. package/lib/edm/edmUtils.js +12 -62
  47. package/lib/gen/Dictionary.json +8 -6
  48. package/lib/gen/language.checksum +1 -1
  49. package/lib/gen/language.interp +8 -30
  50. package/lib/gen/language.tokens +105 -114
  51. package/lib/gen/languageLexer.interp +1 -34
  52. package/lib/gen/languageLexer.js +889 -1007
  53. package/lib/gen/languageLexer.tokens +95 -106
  54. package/lib/gen/languageParser.js +20717 -22376
  55. package/lib/json/from-csn.js +73 -68
  56. package/lib/json/to-csn.js +13 -10
  57. package/lib/language/antlrParser.js +2 -2
  58. package/lib/language/docCommentParser.js +61 -38
  59. package/lib/language/errorStrategy.js +52 -40
  60. package/lib/language/genericAntlrParser.js +333 -259
  61. package/lib/language/language.g4 +600 -645
  62. package/lib/language/multiLineStringParser.js +14 -42
  63. package/lib/language/textUtils.js +44 -0
  64. package/lib/main.d.ts +27 -42
  65. package/lib/main.js +104 -81
  66. package/lib/model/csnRefs.js +2 -1
  67. package/lib/model/csnUtils.js +183 -285
  68. package/lib/model/revealInternalProperties.js +32 -9
  69. package/lib/model/sortViews.js +32 -31
  70. package/lib/optionProcessor.js +64 -57
  71. package/lib/render/.eslintrc.json +1 -1
  72. package/lib/render/DuplicateChecker.js +4 -7
  73. package/lib/render/manageConstraints.js +70 -2
  74. package/lib/render/toCdl.js +334 -339
  75. package/lib/render/toHdbcds.js +20 -16
  76. package/lib/render/toRename.js +44 -22
  77. package/lib/render/toSql.js +60 -54
  78. package/lib/render/utils/common.js +15 -1
  79. package/lib/render/utils/sql.js +20 -19
  80. package/lib/sql-identifier.js +6 -0
  81. package/lib/transform/db/.eslintrc.json +3 -2
  82. package/lib/transform/db/cdsPersistence.js +5 -15
  83. package/lib/transform/db/constraints.js +1 -1
  84. package/lib/transform/db/expansion.js +7 -6
  85. package/lib/transform/db/flattening.js +18 -19
  86. package/lib/transform/db/views.js +3 -3
  87. package/lib/transform/draft/.eslintrc.json +2 -2
  88. package/lib/transform/draft/db.js +6 -6
  89. package/lib/transform/draft/odata.js +6 -7
  90. package/lib/transform/forHanaNew.js +19 -22
  91. package/lib/transform/forOdataNew.js +13 -15
  92. package/lib/transform/localized.js +35 -25
  93. package/lib/transform/odata/toFinalBaseType.js +11 -9
  94. package/lib/transform/odata/typesExposure.js +3 -3
  95. package/lib/transform/odata/utils.js +1 -38
  96. package/lib/transform/transformUtilsNew.js +63 -77
  97. package/lib/transform/translateAssocsToJoins.js +6 -2
  98. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  99. package/lib/transform/universalCsn/coreComputed.js +11 -6
  100. package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
  101. package/lib/utils/file.js +31 -21
  102. package/lib/utils/timetrace.js +20 -21
  103. package/package.json +34 -4
  104. package/share/messages/syntax-expected-integer.md +9 -8
  105. package/doc/ApiMigration.md +0 -237
  106. package/doc/CommandLineMigration.md +0 -58
  107. package/doc/ErrorMessages.md +0 -175
  108. package/doc/FioriAnnotations.md +0 -94
  109. package/doc/ODataTransformation.md +0 -273
  110. package/lib/backends.js +0 -529
  111. package/lib/fix_antlr4-8_warning.js +0 -56
@@ -5,6 +5,8 @@ const { applyTransformations, applyTransformationsOnNonDictionary } = require('.
5
5
  const { isBuiltinType } = require('../compiler/builtins.js')
6
6
  const { sortCsn, cloneCsnDictionary: _cloneCsnDictionary } = require('../json/to-csn');
7
7
  const { ModelError } = require("../base/error");
8
+ const { typeParameters } = require("../compiler/builtins");
9
+ const { forEach } = require('../utils/objectUtils');
8
10
  const version = require('../../package.json').version;
9
11
 
10
12
  // Low-level utility functions to work with compact CSN.
@@ -34,11 +36,14 @@ const version = require('../../package.json').version;
34
36
  */
35
37
 
36
38
  /**
37
- * Get utility functions for a given CSN.
39
+ * Get utility functions for a given CSN. Re-exports functions of `csnRefs()`.
38
40
  * @param {CSN.Model} model (Compact) CSN model
39
41
  */
40
42
  function getUtils(model, universalReady) {
41
- const { artifactRef, inspectRef, effectiveType, getOrigin, targetAspect, getColumn, getElement, initDefinition } = csnRefs(model, universalReady);
43
+ const _csnRefs = csnRefs(model, universalReady);
44
+ const { artifactRef } = _csnRefs;
45
+ /** Cache for getFinalBaseTypeWithProps(). Specific to the current model. */
46
+ const finalBaseTypeCache = Object.create(null);
42
47
 
43
48
  return {
44
49
  getCsnDef,
@@ -56,17 +61,10 @@ function getUtils(model, universalReady) {
56
61
  getServiceName,
57
62
  hasAnnotationValue,
58
63
  cloneWithTransformations,
59
- getFinalBaseType,
60
- inspectRef,
61
- artifactRef,
62
- effectiveType,
64
+ getFinalBaseTypeWithProps,
63
65
  get$combined,
64
- getOrigin,
65
66
  getQueryPrimarySource,
66
- targetAspect,
67
- getColumn,
68
- getElement,
69
- initDefinition
67
+ ..._csnRefs,
70
68
  };
71
69
 
72
70
  /**
@@ -261,7 +259,7 @@ function getUtils(model, universalReady) {
261
259
  */
262
260
  function isStructured(obj) {
263
261
  return obj.elements ||
264
- (obj.type && ((getFinalTypeDef(obj.type).elements) || (obj.type.ref && getFinalBaseType(obj.type).elements)));
262
+ (obj.type && ((getFinalTypeDef(obj.type).elements) || (obj.type.ref && getFinalBaseTypeWithProps(obj.type)?.elements)));
265
263
  }
266
264
 
267
265
  /**
@@ -486,97 +484,122 @@ function getUtils(model, universalReady) {
486
484
  }
487
485
  }
488
486
 
487
+ function _normalizeTypeRef(type) {
488
+ if (type && typeof type === 'object' && type.ref?.length === 1)
489
+ type = type.ref[0]; // simplify type: no element -> simple string can be used
490
+ return type;
491
+ }
489
492
 
490
493
  /**
491
- * Resolve to the final type of a type, that means follow type chains, references to other types or
492
- * elements a.s.o
493
- * Works for all kinds of types, strings as well as type objects. Strings need to be absolute type names.
494
- * Returns the final type as string (if it has a name, which is not always the case, think of embedded structures),
495
- * else the type object itself is returned. If a type is structured, you can navigate into it by providing a path,
496
- * e.g. given the following model
497
- * type bar: S.foo;
498
- * type s1 {
499
- * s: s2;
500
- * };
501
- * type s2 {
502
- * u: type of S.e:t;
503
- * }
504
- * service S {
505
- * type foo: type of S.e:i.j1;
506
- * entity e {
507
- * key i: { j1: Integer };
508
- * t: bar;
509
- * v: s1;
510
- * x: blutz.s.u;
511
- * };
512
- * type blutz: S.e.v;
513
- * view V as select from e {
514
- * 1+1 as i: bar,
515
- * };
516
- * type tt: type of V:i;
517
- * }
518
- * the following calls will all return 'cds.Integer'
519
- * getFinalBaseType('S.tt')
520
- * getFinalBaseType('S.e',['i','j1'])
521
- * getFinalBaseType('S.e',['t'])
522
- * getFinalBaseType('S.e',['x'])
523
- * getFinalBaseType('S.blutz',['s', 'u'])
524
- * Types are always resolved as far as possible. A type name which has no further definition is simply returned.
525
- * Composed types (structures, entities, views, ...) are returned as type objects, if not drilled down into
526
- * the elements. Path steps that have no corresponding element lead to 'undefined'. Refs to something that has
527
- * no type (e.g. expr in a view without explicit type) returns 'null'
494
+ * Resolve to the final type of a type, that means follow type chains, references, etc.
495
+ * Input is a type name, i.e. string, or type ref, i.e. `{ ref: [...] }`.
496
+ *
497
+ * Returns `null` if the type can't be resolved or if the referenced element has no type,
498
+ * e.g. `typeof V:calculated`.
499
+ * Otherwise, if scalar, returns an object that has a `type` property and all collected type
500
+ * properties, or the type object with `elements` or `items` property if structured/arrayed.
501
+ *
502
+ * Caches type lookups. If the CSN changes drastically, you will need to re-call `csnUtils()`
503
+ * and use the newly returned `getFinalBaseTypeWithProps()`.
528
504
  *
529
- * @param {string|object} type Type - either string or ref
530
- * @param {CSN.Path} path
531
- * @param {WeakMap} [resolved=new WeakMap()] WeakMap containing already resolved refs - if a ref is not cached, it will be resolved JIT
532
- * @param {object} [cycleCheck] Dictionary to remember already resolved types - to be cycle-safe
533
- * @returns
505
+ * @param {string|object} type Type as string or type ref, i.e. `{ ref: [...] }`
506
+ * @returns {object|null}
534
507
  */
535
- function getFinalBaseType(type, path = [], resolved = new WeakMap(), cycleCheck = undefined) {
508
+ function getFinalBaseTypeWithProps(type) {
509
+ type = _normalizeTypeRef(type);
536
510
  if (!type)
537
- return type;
538
- if (typeof(type) === 'string') {
539
- if (isBuiltinType(type)) // built-in type
540
- return type;
541
- if (cycleCheck) {
542
- let visited = path.length? type + ':' + path.join('.') : type;
543
- if (cycleCheck[visited])
544
- throw new ModelError('Circular type chain on type ' + type);
545
- else
546
- cycleCheck[visited] = true;
511
+ return null;
512
+
513
+ // Nothing to copy from builtin type name.
514
+ if (typeof type === 'string' && isBuiltinType( type )) {
515
+ return { type };
516
+ }
517
+
518
+ // We differentiate between ref and type to avoid collisions due to dict key.
519
+ // Delimiter chosen arbitrarily; just one that is rarely used.
520
+ const resolvedKey = (typeof type === 'object') ? `ref:${type.ref.join('\\')}` : `type:${type}`;
521
+
522
+ if (finalBaseTypeCache[resolvedKey]) {
523
+ if (finalBaseTypeCache[resolvedKey] === true)
524
+ throw new ModelError(`Detected circular type reference; can't resolve: ${resolvedKey}`);
525
+ return finalBaseTypeCache[resolvedKey];
526
+ }
527
+
528
+ let typeRef = artifactRef(type); // throws if not found
529
+ const isNonScalar = _cacheNonScalar(typeRef);
530
+ if (isNonScalar)
531
+ return finalBaseTypeCache[resolvedKey];
532
+
533
+ const props = {};
534
+ _copyTypeProps(props, typeRef);
535
+
536
+ // If the resolved type is a builtin, stop and use its type arguments.
537
+ type = _normalizeTypeRef(typeRef.type);
538
+ if (typeof type === 'string' && isBuiltinType(type))
539
+ return _cacheResolved(props);
540
+
541
+ // Set to true (before the recursive call) to avoid cyclic issues.
542
+ finalBaseTypeCache[resolvedKey] = true;
543
+
544
+ // Continue the search
545
+ const finalBase = getFinalBaseTypeWithProps(type);
546
+ if (!finalBase) // Reference has no proper type, e.g. due to `type of View:calculated`.
547
+ return _cacheResolved(null);
548
+ const nonScalar = _cacheNonScalar(finalBase);
549
+ if (nonScalar)
550
+ return finalBaseTypeCache[resolvedKey];
551
+
552
+ // If not a non-scalar, must be resolved type.
553
+ _copyTypeProps(props, finalBase);
554
+ _cacheResolved(props);
555
+ return props;
556
+
557
+ /**
558
+ * Cache/Store the type props under the current `resolvedKey` in the `resolved` cache.
559
+ *
560
+ * @param {object} typeProps
561
+ */
562
+ function _cacheResolved(typeProps) {
563
+ finalBaseTypeCache[resolvedKey] = typeProps;
564
+ return typeProps;
565
+ }
566
+
567
+ /**
568
+ * Structured or arrayed types are not followed further, so cache them.
569
+ *
570
+ * @param obj
571
+ * @returns {boolean} True, if structured/arrayed/invalid, false if scalar.
572
+ */
573
+ function _cacheNonScalar(obj) {
574
+ if (!obj) { // Reference has no proper type, e.g. due to `type of View:calculated`.
575
+ _cacheResolved(null);
576
+ return true;
547
577
  }
548
- else {
549
- cycleCheck = Object.create(null);
578
+ if (obj.elements || obj.items) {
579
+ _cacheResolved(obj);
580
+ return true;
550
581
  }
551
- let definedType = model.definitions[type];
552
- if (definedType && definedType.type)
553
- return getFinalBaseType(definedType.type, path, resolved, cycleCheck);
554
- else
555
- return getFinalBaseType(definedType, path, resolved, cycleCheck);
582
+ return false;
556
583
  }
557
- else if (typeof(type) === 'object') {
558
- if (type.ref) {
559
- // assert type.ref instanceof Array && type.ref.length >= 1
560
- const ref = resolved.has(type) ? resolved.get(type).art : artifactRef(type);
561
- return getFinalBaseType(ref, path, resolved, cycleCheck);
562
- }
563
- else if (type.elements) {
564
- if (path.length) {
565
- let [e, ...p] = path;
566
- return getFinalBaseType(type.elements[e], p, resolved, cycleCheck);
584
+
585
+ /**
586
+ * Copy type properties from source to target. Also copies `type`, `enum`,
587
+ * and `localized` (if keepLocalized is true). Only copies from source,
588
+ * if target does not have them.
589
+ *
590
+ * @param {object} target
591
+ * @param {object} source
592
+ */
593
+ function _copyTypeProps(target, source) {
594
+ target.type = source.type;
595
+ const typeProps = [ ...typeParameters.list, 'enum', 'default', 'localized' ];
596
+ for (const param of typeProps) {
597
+ if (target[param] === undefined && source[param] !== undefined) {
598
+ target[param] = source[param];
567
599
  }
568
600
  }
569
- else if (type.type)
570
- return (getFinalBaseType(type.type, path, resolved, cycleCheck));
571
- else if (type.items)
572
- return type;
573
- else
574
- // TODO: this happens if we don't have a type, e.g. an expression in a select list
575
- // in a view without explicit type. Instead of returning null we might want to return
576
- // the object instead?
577
- return null;
601
+ return target;
578
602
  }
579
- return type;
580
603
  }
581
604
  }
582
605
 
@@ -770,25 +793,6 @@ function forEachGeneric( construct, prop, callback, path = [], iterateOptions =
770
793
  }
771
794
  }
772
795
 
773
- // Like Object.assign() but copies also non enumerable properties
774
- function assignAll(target, ...sources) {
775
- sources.forEach(source => {
776
- const descriptors = Object.getOwnPropertyNames(source).reduce((propertyDescriptors, current) => {
777
- propertyDescriptors[current] = Object.getOwnPropertyDescriptor(source, current);
778
- return propertyDescriptors;
779
- }, {});
780
- // by default, Object.assign copies enumerable Symbols too
781
- Object.getOwnPropertySymbols(source).forEach(sym => {
782
- const descriptor = Object.getOwnPropertyDescriptor(source, sym);
783
- if (descriptor.enumerable) {
784
- descriptors[sym] = descriptor;
785
- }
786
- });
787
- Object.defineProperties(target, descriptors);
788
- });
789
- return target;
790
- }
791
-
792
796
  /**
793
797
  * @param {CSN.Query} mainQuery
794
798
  * @param {queryCallback|queryCallback[]} queryCallback
@@ -896,12 +900,10 @@ function hasAnnotationValue(artifact, annotationName, expected = true, caseInsen
896
900
  * @param {ODataOptions} options EDM specific options
897
901
  */
898
902
  function isEdmPropertyRendered(elementCsn, options) {
899
- if(options.toOdata)
900
- options = options.toOdata;
901
903
  // FKs are rendered in
902
904
  // V2/V4 flat: always on
903
905
  // V4 struct: on/off
904
- const renderForeignKey = (options.version === 'v4' && options.odataFormat === 'structured') ? !!options.odataForeignKeys : true;
906
+ const renderForeignKey = (options.odataVersion === 'v4' && options.odataFormat === 'structured') ? !!options.odataForeignKeys : true;
905
907
  const isNotIgnored = !elementCsn.target ? !elementCsn['@cds.api.ignore'] : true;
906
908
  const isNavigable = elementCsn.target ?
907
909
  (elementCsn['@odata.navigable'] === undefined ||
@@ -924,16 +926,19 @@ function isEdmPropertyRendered(elementCsn, options) {
924
926
  * - For the 'plain' naming mode, it means converting all '.' to '_' and upper-casing.
925
927
  * - For the 'quoted' naming mode, this means correctly replacing some '.' with '_'.
926
928
  *
927
- * If the old function signature is used - with a namespace as the third argument - the result might be wrong,
928
- * since the '.' -> '_' conversion for quoted/hdbcds is missing.
929
+ * The above rules might differ for different SQL dialects.
930
+ * Exceptions will be listed below.
929
931
  *
930
932
  * @param {string} artifactName The fully qualified name of the artifact
931
- * @param {('plain'|'quoted'|'hdbcds')} sqlMapping The naming mode to use
932
- * @param {CSN.Model|string|undefined} csn
933
+ * @param {'plain'|'quoted'|'hdbcds'|string} sqlMapping The naming mode to use
934
+ * @param {CSN.Model} csn
935
+ * @param {('sqlite'|'hana'|'plain'|string)} [sqlDialect='plain'] The SQL dialect to use
933
936
  * @returns {string} The resulting database name for (absolute) 'artifactName', depending on the current naming mode.
934
937
  */
935
- function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn) {
936
- if(csn && typeof csn === 'object' && csn.definitions)
938
+ // eslint-disable-next-line no-unused-vars
939
+ function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn, sqlDialect='plain') {
940
+ if(csn && typeof csn === 'object' && csn.definitions) {
941
+ isValidMappingDialectCombi(sqlDialect, sqlMapping)
937
942
  if (sqlMapping === 'quoted' || sqlMapping === 'hdbcds') {
938
943
  return getResultingName(csn, sqlMapping, artifactName);
939
944
  }
@@ -942,24 +947,8 @@ function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn) {
942
947
  } else {
943
948
  throw new Error('Unknown naming mode: ' + sqlMapping);
944
949
  }
945
- else {
946
- console.error(`This invocation of "getArtifactCdsPersistenceName" is deprecated, as it doesn't produce correct output with definition names containing dots - please provide a CSN as the third parameter.`);
947
- if (sqlMapping === 'hdbcds') {
948
- if (csn) {
949
- const namespace = String(csn);
950
- return `${namespace}::${artifactName.substring(namespace.length + 1)}`;
951
- }
952
- return artifactName;
953
- }
954
- else if (sqlMapping === 'plain') {
955
- return artifactName.replace(/\./g, '_').toUpperCase();
956
- }
957
- else if (sqlMapping === 'quoted') {
958
- return artifactName;
959
- }
960
- else {
961
- throw new Error('Unknown naming mode: ' + sqlMapping);
962
- }
950
+ } else {
951
+ throw new Error('A valid CSN model is required to correctly calculate the database name of an artifact.');
963
952
  }
964
953
  }
965
954
 
@@ -1042,6 +1031,12 @@ function getUnderscoredName(startIndex, parts, csn) {
1042
1031
  return null;
1043
1032
  }
1044
1033
 
1034
+ function isValidMappingDialectCombi(sqlDialect, sqlMapping) {
1035
+ if(sqlMapping === 'hdbcds' && sqlDialect !== 'hana')
1036
+ throw new Error(`sqlMapping "hdbcds" must only be used with sqlDialect "hana" - found: ${sqlDialect}`);
1037
+ return true;
1038
+ }
1039
+
1045
1040
 
1046
1041
  /**
1047
1042
  * Return the resulting database element name for 'elemName', depending on the current
@@ -1051,11 +1046,17 @@ function getUnderscoredName(startIndex, parts, csn) {
1051
1046
  * - For the 'quoted' naming mode, it means converting all '.' to '_'.
1052
1047
  * No other naming modes are accepted!
1053
1048
  *
1049
+ * The above rules might differ for different SQL dialects.
1050
+ * Exceptions will be listed below.
1051
+ *
1054
1052
  * @param {string} elemName The name of the element
1055
- * @param {('plain'|'quoted'|'hdbcds')} sqlMapping The naming mode to use
1053
+ * @param {'plain'|'quoted'|'hdbcds'|string} sqlMapping The naming mode to use
1054
+ * @param {('sqlite'|'hana'|'plain'|string)} [sqlDialect='plain'] The SQL dialect to use
1056
1055
  * @returns {string} The resulting database element name for 'elemName', depending on the current naming mode.
1057
1056
  */
1058
- function getElementDatabaseNameOf(elemName, sqlMapping) {
1057
+ // eslint-disable-next-line no-unused-vars
1058
+ function getElementDatabaseNameOf(elemName, sqlMapping, sqlDialect='plain') {
1059
+ isValidMappingDialectCombi(sqlDialect, sqlMapping)
1059
1060
  if (sqlMapping === 'hdbcds') {
1060
1061
  return elemName;
1061
1062
  }
@@ -1170,85 +1171,6 @@ function getNormalizedQuery(art) {
1170
1171
  return art;
1171
1172
  }
1172
1173
 
1173
- /**
1174
- * Merge multiple 'options' objects (from right to left, i.e. rightmost wins). Structured option values are
1175
- * merged deeply. Structured option value from the right may override corresponding bool options on the left,
1176
- * but no other combination of struct/scalar values is allowed. Array options are not merged, i.e. their
1177
- * content is treated like scalars.
1178
- * Returns a new options object.
1179
- *
1180
- * @param {...CSN.Options} optionsObjects
1181
- * @return {CSN.Options}
1182
- */
1183
- function mergeOptions(...optionsObjects) {
1184
- let result = {};
1185
- for (const options of optionsObjects) {
1186
- if (options)
1187
- result = mergeTwo(result, options, 'options');
1188
- }
1189
-
1190
- // Reverse the array to ensure that the rightmost option has priority
1191
- const reversedOptions = [...optionsObjects].reverse(); // de-structure and create a new array, so reverse doesn't impact optionsObject
1192
- const msgOptions = reversedOptions.find(opt => opt && Array.isArray(opt.messages));
1193
- if (msgOptions) {
1194
- result.messages = msgOptions.messages;
1195
- }
1196
-
1197
- return result;
1198
-
1199
- // Recursively used for scalars, too
1200
- function mergeTwo(left, right, name) {
1201
- let intermediateResult;
1202
- // Copy left as far as required
1203
- if (Array.isArray(left)) {
1204
- // Shallow-copy left array
1205
- intermediateResult = left.slice();
1206
- } else if (isObject(left)) {
1207
- // Deep-copy left object (unless empty)
1208
- intermediateResult = Object.keys(left).length ? mergeTwo({}, left, name) : {};
1209
- } else {
1210
- // Just use left scalar
1211
- intermediateResult = left;
1212
- }
1213
- // Check against improper overwriting
1214
- if (isObject(left) && !Array.isArray(left) && (Array.isArray(right) || isScalar(right))) {
1215
- throw new ModelError(`Cannot overwrite structured option "${name}" with array or scalar value`);
1216
- }
1217
- if ((isScalar(left) && typeof left !== 'boolean' || Array.isArray(left)) && isObject(right) && !Array.isArray(right)) {
1218
- throw new ModelError(`Cannot overwrite non-boolean scalar or array option "${name}" with structured value`);
1219
- }
1220
-
1221
- // Copy or overwrite properties from right to left
1222
- if (Array.isArray(right)) {
1223
- // Shallow-copy right array
1224
- intermediateResult = right.slice();
1225
- } else if (isObject(right)) {
1226
- // Object overwrites undefined, scalars and arrays
1227
- if (intermediateResult === undefined || isScalar(intermediateResult) || Array.isArray(intermediateResult)) {
1228
- intermediateResult = {};
1229
- }
1230
- // Deep-copy right object into result
1231
- for (let key of Object.keys(right)) {
1232
- intermediateResult[key] = mergeTwo(intermediateResult[key], right[key], `${name}.${key}`);
1233
- }
1234
- } else {
1235
- // Right scalar wins (unless undefined)
1236
- intermediateResult = (right !== undefined) ? right : intermediateResult;
1237
- }
1238
- return intermediateResult;
1239
- }
1240
-
1241
- // Return true if 'o' is a non-null object or array
1242
- function isObject(o) {
1243
- return typeof o === 'object' && o !== null
1244
- }
1245
-
1246
- // Return true if 'o' is a non-undefined scalar
1247
- function isScalar(o) {
1248
- return o !== undefined && !isObject(o);
1249
- }
1250
- }
1251
-
1252
1174
  /**
1253
1175
  * If the artifact with the name given is part of a context (or multiple), return the top-most context.
1254
1176
  * Else, return the artifact itself. Namespaces are not of concern here.
@@ -1300,29 +1222,6 @@ function getLastPartOfRef(ref) {
1300
1222
  return getLastPartOf(lastPathStep.id || lastPathStep);
1301
1223
  }
1302
1224
 
1303
- // Return the name of the parent artifact of the artifact 'name' or
1304
- // '' if there is no parent.
1305
- function getParentNameOf(name) {
1306
- return name.substring(0, name.lastIndexOf('.'));
1307
- }
1308
-
1309
- // Return an array of parent names of 'name' (recursing into grand-parents)
1310
- // Examples:
1311
- // 'foo.bar.wiz' => [ 'foo.bar', 'foo' ]
1312
- // 'foo' => []
1313
- // 'foo::bar.wiz' => 'foo::bar'
1314
- // 'foo::bar' => []
1315
- function getParentNamesOf(name) {
1316
- let remainder = name.slice(0, -getLastPartOf(name).length);
1317
- if (remainder.endsWith('.')) {
1318
- let parentName = remainder.slice(0, -1);
1319
- return [parentName, ...getParentNamesOf(parentName)];
1320
- } else {
1321
- return [];
1322
- }
1323
- }
1324
-
1325
-
1326
1225
  /**
1327
1226
  * Copy all annotations from 'fromNode' to 'toNode'.
1328
1227
  *
@@ -1373,49 +1272,64 @@ function copyAnnotationsAndDoc(fromNode, toNode, overwrite = false) {
1373
1272
  }
1374
1273
 
1375
1274
  /**
1376
- * Applies annotations from `csn.extensions` to definitions, i.e. top-level artifacts.
1377
- * Does _not_ apply element/param/action/... annotations.
1275
+ * Applies annotations from `csn.extensions` to definitions and their elements.
1276
+ *
1378
1277
  * `config.filter` can be used to only copy annotations for those definitions,
1379
1278
  * for which the filter returns true.
1380
1279
  *
1280
+ * @todo Does _not_ apply param/action/... annotations.
1281
+ *
1381
1282
  * @param {CSN.Model} csn
1382
- * @param {{overwrite?: boolean, filter?: (name: string) => boolean}} config
1283
+ * @param {{notFound?: (name: string, index: number) => void, override?: boolean, filter?: (name: string) => boolean}} config
1284
+ * notFound: Function that is called if the referenced definition can't be found.
1285
+ * Second argument is index in `csn.extensions` array.
1286
+ * override: Whether to ignore existing annotations.
1287
+ * filter: Positive filter. If it returns true, annotations for the referenced artifact
1288
+ * will be applied.
1289
+ * @todo Improve to also apply element annotations
1383
1290
  */
1384
- function applyDefinitionAnnotationsFromExtensions(csn, config) {
1291
+ function applyAnnotationsFromExtensions(csn, config) {
1385
1292
  if (!csn.extensions)
1386
1293
  return;
1387
1294
 
1388
1295
  const filter = config.filter || ((_name) => true);
1389
- for (const ext of csn.extensions) {
1296
+ for (let i = 0; i < csn.extensions.length; ++i) {
1297
+ const ext = csn.extensions[i];
1390
1298
  const name = ext.annotate || ext.extend;
1391
- if (name && csn.definitions[name] && filter(name)) {
1392
- copyAnnotationsAndDoc(ext, csn.definitions[name], config.overwrite);
1299
+ if (name && filter(name)) {
1300
+ const def = csn.definitions[name];
1301
+ if (def) {
1302
+ copyAnnotationsAndDoc(ext, def, config.override);
1303
+ applyAnnotationsToElements(ext, def);
1304
+ } else if (config.notFound) {
1305
+ config.notFound(name, i);
1306
+ }
1393
1307
  }
1394
1308
  }
1395
- }
1396
1309
 
1397
- function isAspect(node) {
1398
- return node && node.kind === 'aspect';
1399
- }
1310
+ function applyAnnotationsToElements(ext, def) {
1311
+ // Only the definition is arrayed but the extension is not since
1312
+ // `items` is not expected in `extensions` by the CSN frontend and not
1313
+ // generated by the CDL parser for `annotate E:arrayed.elem`.
1314
+ if (def.items)
1315
+ def = def.items;
1400
1316
 
1401
- // For each property named 'path' in 'node' (recursively), call callback(path, node)
1402
- function forEachPath(node, callback) {
1403
- if (node === null || typeof node !== 'object') {
1404
- // Primitive node
1405
- return;
1406
- }
1407
- for (let name in node) {
1408
- if (!Object.hasOwnProperty.call( node, name ))
1409
- continue;
1410
- // If path found within a non-dictionary, call callback
1411
- if (name === 'path' && Object.getPrototypeOf(node)) {
1412
- callback(node.path, node);
1413
- }
1414
- // Descend recursively
1415
- forEachPath(node[name], callback);
1317
+ if (!ext.elements || !def.elements)
1318
+ return;
1319
+
1320
+ forEach(ext.elements, (key, sourceElem) => {
1321
+ const targetElem = def.elements[key];
1322
+ if (targetElem) {
1323
+ copyAnnotationsAndDoc(sourceElem, targetElem, config.override);
1324
+ applyAnnotationsToElements(sourceElem, targetElem);
1325
+ }
1326
+ });
1416
1327
  }
1417
1328
  }
1418
1329
 
1330
+ function isAspect(node) {
1331
+ return node && node.kind === 'aspect';
1332
+ }
1419
1333
 
1420
1334
  /**
1421
1335
  * Return true if the artifact has a valid, truthy persistence.exists/skip annotation
@@ -1492,16 +1406,6 @@ function getServiceNames(csn) {
1492
1406
  return result;
1493
1407
  }
1494
1408
 
1495
- /**
1496
- * Check whether the artifact is @cds.persistence.skip
1497
- *
1498
- * @param {CSN.Artifact} artifact
1499
- * @returns {Boolean}
1500
- */
1501
- function isSkipped(artifact) {
1502
- return hasAnnotationValue(artifact, '@cds.persistence.skip', true)
1503
- }
1504
-
1505
1409
  /**
1506
1410
  * Walk path in the CSN and return the result.
1507
1411
  *
@@ -1589,8 +1493,7 @@ module.exports = {
1589
1493
  cloneCsnNonDict,
1590
1494
  cloneCsnDictionary,
1591
1495
  isBuiltinType,
1592
- assignAll,
1593
- applyDefinitionAnnotationsFromExtensions,
1496
+ applyAnnotationsFromExtensions,
1594
1497
  forEachGeneric,
1595
1498
  forEachDefinition,
1596
1499
  forEachMember,
@@ -1610,21 +1513,16 @@ module.exports = {
1610
1513
  isPersistedOnDatabase,
1611
1514
  generatedByCompilerVersion,
1612
1515
  getNormalizedQuery,
1613
- mergeOptions,
1614
1516
  getRootArtifactName,
1615
1517
  getLastPartOfRef,
1616
- getParentNamesOf,
1617
- getParentNameOf,
1618
1518
  getLastPartOf,
1619
1519
  copyAnnotations,
1620
1520
  copyAnnotationsAndDoc,
1621
1521
  isAspect,
1622
- forEachPath,
1623
1522
  hasValidSkipOrExists,
1624
1523
  getNamespace,
1625
1524
  sortCsnDefinitionsForTests,
1626
1525
  getServiceNames,
1627
- isSkipped,
1628
1526
  walkCsnPath,
1629
1527
  getVariableReplacement,
1630
1528
  implicitAs,