@sap/cds-compiler 2.15.4 → 3.0.0

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 (105) hide show
  1. package/CHANGELOG.md +33 -1590
  2. package/bin/cdsc.js +36 -33
  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 +220 -103
  10. package/lib/api/options.js +15 -85
  11. package/lib/api/validate.js +6 -10
  12. package/lib/base/keywords.js +216 -109
  13. package/lib/base/message-registry.js +60 -20
  14. package/lib/base/messages.js +65 -24
  15. package/lib/base/model.js +44 -2
  16. package/lib/checks/actionsFunctions.js +7 -5
  17. package/lib/checks/annotationsOData.js +1 -1
  18. package/lib/checks/cdsPersistence.js +1 -0
  19. package/lib/checks/elements.js +6 -6
  20. package/lib/checks/invalidTarget.js +1 -1
  21. package/lib/checks/nonexpandableStructured.js +1 -1
  22. package/lib/checks/queryNoDbArtifacts.js +2 -1
  23. package/lib/checks/selectItems.js +5 -1
  24. package/lib/checks/types.js +4 -2
  25. package/lib/checks/utils.js +2 -2
  26. package/lib/checks/validator.js +2 -1
  27. package/lib/compiler/assert-consistency.js +15 -10
  28. package/lib/compiler/builtins.js +87 -9
  29. package/lib/compiler/define.js +2 -2
  30. package/lib/compiler/extend.js +59 -11
  31. package/lib/compiler/finalize-parse-cdl.js +20 -9
  32. package/lib/compiler/index.js +25 -11
  33. package/lib/compiler/moduleLayers.js +7 -0
  34. package/lib/compiler/populate.js +13 -13
  35. package/lib/compiler/propagator.js +3 -3
  36. package/lib/compiler/resolve.js +193 -218
  37. package/lib/compiler/shared.js +47 -76
  38. package/lib/compiler/tweak-assocs.js +9 -10
  39. package/lib/compiler/utils.js +5 -0
  40. package/lib/edm/csn2edm.js +18 -21
  41. package/lib/edm/edmPreprocessor.js +25 -30
  42. package/lib/edm/edmUtils.js +10 -24
  43. package/lib/gen/language.checksum +1 -1
  44. package/lib/gen/language.interp +8 -30
  45. package/lib/gen/language.tokens +105 -114
  46. package/lib/gen/languageLexer.interp +1 -34
  47. package/lib/gen/languageLexer.js +889 -1007
  48. package/lib/gen/languageLexer.tokens +95 -106
  49. package/lib/gen/languageParser.js +20632 -22313
  50. package/lib/json/from-csn.js +56 -49
  51. package/lib/json/to-csn.js +10 -8
  52. package/lib/language/antlrParser.js +2 -2
  53. package/lib/language/docCommentParser.js +61 -38
  54. package/lib/language/errorStrategy.js +52 -40
  55. package/lib/language/genericAntlrParser.js +303 -229
  56. package/lib/language/language.g4 +573 -629
  57. package/lib/language/multiLineStringParser.js +14 -42
  58. package/lib/language/textUtils.js +44 -0
  59. package/lib/main.d.ts +27 -42
  60. package/lib/main.js +104 -81
  61. package/lib/model/csnRefs.js +1 -1
  62. package/lib/model/csnUtils.js +170 -283
  63. package/lib/model/revealInternalProperties.js +28 -8
  64. package/lib/model/sortViews.js +32 -31
  65. package/lib/optionProcessor.js +12 -21
  66. package/lib/render/.eslintrc.json +1 -1
  67. package/lib/render/DuplicateChecker.js +4 -7
  68. package/lib/render/manageConstraints.js +70 -2
  69. package/lib/render/toCdl.js +334 -339
  70. package/lib/render/toHdbcds.js +19 -15
  71. package/lib/render/toRename.js +44 -22
  72. package/lib/render/toSql.js +53 -51
  73. package/lib/render/utils/common.js +15 -1
  74. package/lib/render/utils/sql.js +20 -19
  75. package/lib/sql-identifier.js +6 -0
  76. package/lib/transform/db/.eslintrc.json +3 -2
  77. package/lib/transform/db/cdsPersistence.js +5 -15
  78. package/lib/transform/db/constraints.js +1 -1
  79. package/lib/transform/db/expansion.js +7 -6
  80. package/lib/transform/db/flattening.js +18 -19
  81. package/lib/transform/db/views.js +3 -3
  82. package/lib/transform/draft/.eslintrc.json +2 -2
  83. package/lib/transform/draft/db.js +6 -6
  84. package/lib/transform/draft/odata.js +6 -7
  85. package/lib/transform/forHanaNew.js +19 -22
  86. package/lib/transform/forOdataNew.js +10 -12
  87. package/lib/transform/localized.js +22 -16
  88. package/lib/transform/odata/toFinalBaseType.js +10 -10
  89. package/lib/transform/odata/typesExposure.js +3 -3
  90. package/lib/transform/odata/utils.js +1 -38
  91. package/lib/transform/transformUtilsNew.js +63 -77
  92. package/lib/transform/translateAssocsToJoins.js +2 -2
  93. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  94. package/lib/transform/universalCsn/coreComputed.js +11 -6
  95. package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
  96. package/lib/utils/file.js +3 -3
  97. package/lib/utils/timetrace.js +20 -21
  98. package/package.json +35 -4
  99. package/doc/ApiMigration.md +0 -237
  100. package/doc/CommandLineMigration.md +0 -58
  101. package/doc/ErrorMessages.md +0 -175
  102. package/doc/FioriAnnotations.md +0 -94
  103. package/doc/ODataTransformation.md +0 -273
  104. package/lib/backends.js +0 -529
  105. package/lib/fix_antlr4-8_warning.js +0 -56
@@ -3,8 +3,7 @@
3
3
  const { makeMessageFunction } = require('../base/messages');
4
4
  const { isDeprecatedEnabled, isBetaEnabled } = require('../base/model');
5
5
  const transformUtils = require('./transformUtilsNew');
6
- const { getUtils,
7
- cloneCsnNonDict,
6
+ const { cloneCsnNonDict,
8
7
  forEachDefinition,
9
8
  forEachMemberRecursively,
10
9
  applyTransformationsOnNonDictionary,
@@ -86,10 +85,10 @@ function transform4odataWithCsn(inputModel, options) {
86
85
  extractValidFromToKeyElement,
87
86
  checkAssignment, checkMultipleAssignments,
88
87
  recurseElements, setAnnotation, renameAnnotation,
89
- expandStructsInExpression
88
+ expandStructsInExpression,
89
+ csnUtils,
90
90
  } = transformers;
91
91
 
92
- const csnUtils = getUtils(csn);
93
92
  const {
94
93
  getCsnDef,
95
94
  getServiceName,
@@ -98,11 +97,10 @@ function transform4odataWithCsn(inputModel, options) {
98
97
  inspectRef,
99
98
  artifactRef,
100
99
  effectiveType,
101
- getFinalBaseType,
102
100
  } = csnUtils;
103
101
 
104
102
  // are we working with structured OData or not
105
- const structuredOData = options.toOdata.odataFormat === 'structured' && options.toOdata.version === 'v4';
103
+ const structuredOData = options.odataFormat === 'structured' && options.odataVersion === 'v4';
106
104
 
107
105
  // collect all declared non-abstract services from the model
108
106
  // use the array when there is a need to identify if an artifact is in a service or not
@@ -115,7 +113,7 @@ function transform4odataWithCsn(inputModel, options) {
115
113
  if (options.csnFlavor === 'universal' && isBetaEnabled(options, 'enableUniversalCsn'))
116
114
  enrichUniversalCsn(csn, options);
117
115
 
118
- const keepLocalizedViews = isDeprecatedEnabled(options, 'createLocalizedViews');
116
+ const keepLocalizedViews = isDeprecatedEnabled(options, '_createLocalizedViews');
119
117
 
120
118
  function acceptLocalizedView(_name, parent) {
121
119
  csn.definitions[parent].$localized = true;
@@ -125,7 +123,7 @@ function transform4odataWithCsn(inputModel, options) {
125
123
  addLocalizationViews(csn, options, acceptLocalizedView);
126
124
 
127
125
  const cleanup = validate.forOdata(csn, {
128
- message, error, warning, info, inspectRef, effectiveType, artifactRef, csn, options, csnUtils, services, getFinalBaseType, isAspect, isExternalServiceMember
126
+ message, error, warning, info, inspectRef, effectiveType, artifactRef, csn, options, csnUtils, services, isAspect, isExternalServiceMember
129
127
  });
130
128
 
131
129
 
@@ -214,16 +212,16 @@ function transform4odataWithCsn(inputModel, options) {
214
212
 
215
213
  // Annotate artifacts with their DB names if requested.
216
214
  // Skip artifacts that have no DB equivalent anyway
217
- if (options.toOdata.names && !(def.kind in skipPersNameKinds))
218
- def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.toOdata.names, csn);
215
+ if (options.sqlMapping && !(def.kind in skipPersNameKinds))
216
+ def['@cds.persistence.name'] = getArtifactDatabaseNameOf(defName, options.sqlMapping, csn, 'hana'); // hana to allow naming mode "hdbcds"
219
217
 
220
218
  forEachMemberRecursively(def, (member, memberName, propertyName) => {
221
219
  // Annotate elements, foreign keys, parameters, etc. with their DB names if requested
222
220
  // Only these are actually required and don't annotate virtual elements in entities or types
223
221
  // as they have no DB representation (although in views)
224
- if (options.toOdata.names && typeof member === 'object' && !(member.kind === 'action' || member.kind === 'function') && propertyName !== 'enum' && (!member.virtual || def.query)) {
222
+ if (options.sqlMapping && typeof member === 'object' && !(member.kind === 'action' || member.kind === 'function') && propertyName !== 'enum' && (!member.virtual || def.query)) {
225
223
  // If we have a 'preserved dotted name' (i.e. we are a result of flattening), use that for the @cds.persistence.name annotation
226
- member['@cds.persistence.name'] = getElementDatabaseNameOf(member._flatElementNameWithDots || memberName, options.toOdata.names);
224
+ member['@cds.persistence.name'] = getElementDatabaseNameOf(member._flatElementNameWithDots || memberName, options.sqlMapping, 'hana'); // hana to allow "hdbcds"
227
225
  }
228
226
 
229
227
  // Mark fields with @odata.on.insert/update as @Core.Computed
@@ -1,12 +1,14 @@
1
1
  'use strict';
2
2
 
3
3
  const { makeMessageFunction } = require('../base/messages');
4
- const { setProp } = require('../base/model');
4
+ const { setProp, isDeprecatedEnabled} = require('../base/model');
5
5
  const { hasErrors } = require('../base/messages');
6
- const { cloneCsnDictionary, applyDefinitionAnnotationsFromExtensions} = require('../model/csnUtils');
6
+ const { forEachKey } = require('../utils/objectUtils');
7
7
  const { cleanSymbols } = require('../base/cleanSymbols.js');
8
8
  const {
9
+ cloneCsnDictionary,
9
10
  cloneCsnNonDict,
11
+ applyAnnotationsFromExtensions,
10
12
  forEachDefinition,
11
13
  forEachGeneric,
12
14
  forAllQueries,
@@ -67,7 +69,8 @@ const _targetFor = Symbol('_targetFor');
67
69
  * @param {CSN.Options} options
68
70
  * @param {boolean} useJoins If true, rewrite the "localized" association to a
69
71
  * join in direct convenience views.
70
- * @param {acceptLocalizedView} [acceptLocalizedView] optional callback function returning true if the localized view name and its parent name provided as parameter should be created
72
+ * @param {acceptLocalizedView} [acceptLocalizedView] optional callback function returning true if the localized view
73
+ * name and its parent name provided as parameter should be created
71
74
  */
72
75
  function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = null) {
73
76
  // Don't try to create convenience views with errors.
@@ -87,7 +90,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
87
90
  forEachDefinition(csn, definition => cleanSymbols(definition, _hasLocalizedView, _isViewForEntity, _isViewForView, _targetFor));
88
91
 
89
92
  // In case that the user tried to annotate `localized.*` artifacts, apply them.
90
- applyDefinitionAnnotationsFromExtensions(csn, {
93
+ applyAnnotationsFromExtensions(csn, {
91
94
  overwrite: true,
92
95
  filter: (name) => name.startsWith('localized.')
93
96
  });
@@ -147,7 +150,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
147
150
  else
148
151
  view = createLocalizedViewForEntity(art, artName, textElements);
149
152
 
150
- copyPersistenceAnnotations(view, art);
153
+ copyPersistenceAnnotations(view, art, options);
151
154
  csn.definitions[viewName] = view;
152
155
  }
153
156
 
@@ -667,21 +670,24 @@ function copyLocation(target, source) {
667
670
  }
668
671
 
669
672
  /**
670
- * Copy some @cds.persistence.* annotations from the source to
671
- * the target. Ignores existing annotations on the target.
673
+ * Copy @cds.persistence.exists/skip annotations from the source to
674
+ * the target. Ignores existing annotations on the _target_.
672
675
  *
673
676
  * @param {CSN.Artifact} target
674
677
  * @param {CSN.Artifact} source
678
+ * @param {CSN.Options} options
675
679
  */
676
- function copyPersistenceAnnotations(target, source) {
677
- Object.keys(source)
678
- .forEach(anno => {
679
- // Do NOT copy ".exists" at the moment. ".exists" is not propagated
680
- // and this would lead to some localization views referencing not-existing
681
- // "localized.XYZ" views.
682
- if (anno === '@cds.persistence.skip')
683
- target[anno] = source[anno];
684
- });
680
+ function copyPersistenceAnnotations(target, source, options) {
681
+ const doNotCopyExists = isDeprecatedEnabled( options, 'eagerPersistenceForGeneratedEntities' );
682
+ forEachKey(source, anno => {
683
+ // Note:
684
+ // Because `.exists` is copied to the convenience view, it could
685
+ // lead to some localization views referencing non-existing ones.
686
+ // But that is the contract: User says that it already exists!
687
+ // In v2, `.exists` was never copied.
688
+ if (anno === '@cds.persistence.skip' || (!doNotCopyExists && anno === '@cds.persistence.exists'))
689
+ target[anno] = source[anno];
690
+ });
685
691
  }
686
692
 
687
693
  /**
@@ -7,7 +7,7 @@ const {
7
7
  const { isArtifactInSomeService, isArtifactInService } = require('./utils');
8
8
 
9
9
  function expandToFinalBaseType(csn, transformers, csnUtils, services, options, isExternalServiceMember) {
10
- const isV4 = options.toOdata.version === 'v4';
10
+ const isV4 = options.odataVersion === 'v4';
11
11
  forEachDefinition(csn, (def, defName) => {
12
12
  // Unravel derived type chains to final one for elements, actions, action parameters (propagating annotations)
13
13
  forEachMemberRecursively(def, (member) => {
@@ -87,9 +87,9 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
87
87
  if (node.kind === 'event') return;
88
88
 
89
89
  // elements have precedence over type
90
- if (node.type && (!isBuiltinType(node.type) &&isExpandable(node, defName) || node.kind === 'type')) {
90
+ if (node.type && (!isBuiltinType(node.type) && isExpandable(node, defName) || node.kind === 'type')) {
91
91
  // 1. Get the final type of the node (resolve derived type chain)
92
- const finalType = csnUtils.getFinalBaseType(node.type);
92
+ const finalType = csnUtils.getFinalBaseTypeWithProps(node.type);
93
93
  if (finalType) {
94
94
  // The type replacement depends on whether 'node' is a definition or a member[element].
95
95
  if (node.kind) {
@@ -101,7 +101,7 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
101
101
  // type A: B; -> {...}
102
102
  // type B: C; -> { ... }
103
103
  // type C { .... };
104
- if (isBuiltinType(finalType)) {
104
+ if (isBuiltinType(finalType.type)) {
105
105
  // use transformUrilsNew::toFinalBaseType for the moment,
106
106
  // as it is collects along the chain of types
107
107
  // attributes that need to be propagated
@@ -129,8 +129,8 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
129
129
  // type A: B; -> {...}
130
130
  // type B: C; -> { ... }
131
131
  // type C { .... };
132
- if (isBuiltinType(finalType)) {
133
- // use transformUrilsNew::toFinalBaseType for the moment,
132
+ if (isBuiltinType(finalType.type)) {
133
+ // use transformUtilsNew::toFinalBaseType for the moment,
134
134
  // as it is collects along the chain of types
135
135
  // attributes that need to be propagated
136
136
  // enum, length, scale, etc.
@@ -148,8 +148,8 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
148
148
  // example in actions: 'action act() return Primitive; type Primitive: array of String;'
149
149
  const currService = csnUtils.getServiceName(defName);
150
150
  const finalType = csnUtils.getFinalTypeDef(node.type);
151
- if (finalType.items &&
152
- (isBuiltinType(finalType.items.type) || isBuiltinType(csnUtils.getFinalBaseType(finalType.items.type))))
151
+ if (finalType.items &&
152
+ (isBuiltinType(finalType.items.type) || isBuiltinType(csnUtils.getFinalBaseTypeWithProps(finalType.items.type)?.type)))
153
153
  {
154
154
  if (!isArtifactInService(node.type, currService) || !isV4) {
155
155
  node.items = finalType.items;
@@ -186,8 +186,8 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
186
186
  function isUserDefinedBuiltinFromTheCurrService(node, defName) {
187
187
  // in V4 we should use TypeDefinitions whenever possible, thus in case the final type of a field is
188
188
  // a builtin from the service - do not expand to the final base type
189
- let finalBaseType = csnUtils.getFinalBaseType(node.type);
190
- // if (finalBaseType && finalBaseType.items) finalBaseType = csnUtils.getFinalBaseType(finalBaseType.items);
189
+ let finalBaseType = csnUtils.getFinalBaseTypeWithProps(node.type).type;
190
+ // if (finalBaseType && finalBaseType.items) finalBaseType = csnUtils.getFinalBaseTypeWithProps(finalBaseType.items);
191
191
  const currService = csnUtils.getServiceName(defName);
192
192
  return node.type && !node.type.ref
193
193
  && isBuiltinType(finalBaseType) && !csnUtils.isAssocOrComposition(finalBaseType)
@@ -14,7 +14,7 @@ const { copyAnnotations } = require('../../model/csnUtils');
14
14
  function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackSchemaName, options, csnUtils, message) {
15
15
  const { error } = message;
16
16
  // are we working with OData proxies or cross-service refs
17
- const isMultiSchema = options.toOdata.version === 'v4' && (options.toOdata.odataProxies || options.toOdata.odataXServiceRefs);
17
+ const isMultiSchema = options.odataVersion === 'v4' && (options.odataProxies || options.odataXServiceRefs);
18
18
  // collect in this variable all the newly exposed types
19
19
  const schemas = Object.create(null);
20
20
  const exposedTypes = Object.create(null);
@@ -122,7 +122,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
122
122
  if (node.elements && node.elements[elemName].$location)
123
123
  setProp(newElem, '$location', node.elements[elemName].$location);
124
124
  defName = typeDef.kind === 'type' ? typeName : defName;
125
- exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
125
+ exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
126
126
  getNewTypeName(newElem, elemName, newTypeName, serviceName), path, fullQualifiedNewTypeName);
127
127
  });
128
128
  copyAnnotations(typeDef, newType);
@@ -151,7 +151,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
151
151
  * 1) If it's an anonymous structured type (items.elements || elements)
152
152
  * 2) If it's a named type resolve to the final type definition and
153
153
  * check if this is a structured type
154
- * Returns an object that indicates
154
+ * Returns an object that indicates
155
155
  * - wether or not the type needs exposure
156
156
  * - the elements dictionary that needs to be cloned
157
157
  * - the typeDef, either the resolved type def or the node itself
@@ -1,38 +1,4 @@
1
- const {
2
- forEachDefinition,
3
- forEachMemberRecursively,
4
- } = require('../../model/csnUtils');
5
-
6
-
7
- // Return true if 'artifact' has an association type
8
- function isAssociation(artifact) {
9
- return (artifact.type === 'cds.Association' || artifact.type === 'Association');
10
- }
11
-
12
- // Return true if 'artifact' has a composition type
13
- function isComposition(artifact) {
14
- return (artifact.type === 'cds.Composition' || artifact.type === 'Composition')
15
- }
16
-
17
- function isAssociationOrComposition(artifact) {
18
- return isAssociation(artifact) || isComposition(artifact);
19
- }
20
-
21
- function isManagedAssociation(artifact) {
22
- return artifact.target !== undefined && artifact.on === undefined;
23
- }
24
-
25
- function forEachManagedAssociation(csn, callback, isExternalServiceMember) {
26
-
27
- forEachDefinition(csn, (def) => {
28
- forEachMemberRecursively(def, (element) => {
29
- if (isAssociationOrComposition(element) && !element.on) {
30
- callback(element)
31
- }
32
- })
33
- }, { skipArtifact: isExternalServiceMember });
34
-
35
- }
1
+ 'use strict';
36
2
 
37
3
  /**
38
4
  * Return the definition name, without the prefixed service name
@@ -87,12 +53,9 @@ function isLocalizedArtifactInService(artName, services) {
87
53
  }
88
54
 
89
55
  module.exports = {
90
- forEachManagedAssociation,
91
56
  defNameWithoutServiceOrContextName,
92
57
  getServiceOfArtifact,
93
58
  isArtifactInService,
94
59
  isArtifactInSomeService,
95
- isAssociationOrComposition,
96
60
  isLocalizedArtifactInService,
97
- isManagedAssociation,
98
61
  }
@@ -6,10 +6,10 @@
6
6
 
7
7
  const { hasErrors, makeMessageFunction } = require('../base/messages');
8
8
  const { setProp } = require('../base/model');
9
- const { csnRefs } = require('../model/csnRefs');
10
9
 
11
10
  const { copyAnnotations, applyTransformations } = require('../model/csnUtils');
12
- const { cloneCsnNonDict, cloneCsnDictionary, getUtils, isBuiltinType } = require('../model/csnUtils');
11
+ const { cloneCsnNonDict, cloneCsnDictionary, getUtils } = require('../model/csnUtils');
12
+ const { typeParameters, isBuiltinType } = require('../compiler/builtins');
13
13
  const { ModelError } = require("../base/error");
14
14
  const { forEach } = require('../utils/objectUtils');
15
15
 
@@ -20,20 +20,18 @@ const { forEach } = require('../utils/objectUtils');
20
20
  // TODO: check the situation with assocs with values. In compacted CSN such elements have only "@Core.Computed": true
21
21
  function getTransformers(model, options, pathDelimiter = '_') {
22
22
  const { error, warning, info } = makeMessageFunction(model, options);
23
+ const csnUtils = getUtils(model);
23
24
  const {
24
25
  getCsnDef,
25
- getFinalBaseType,
26
+ getFinalBaseTypeWithProps,
26
27
  hasAnnotationValue,
27
28
  inspectRef,
28
29
  isStructured,
29
- } = getUtils(model);
30
-
31
- const {
32
30
  effectiveType,
33
- } = csnRefs(model);
34
-
31
+ } = csnUtils;
35
32
 
36
33
  return {
34
+ csnUtils,
37
35
  resolvePath,
38
36
  flattenPath,
39
37
  addDefaultTypeFacets,
@@ -45,7 +43,6 @@ function getTransformers(model, options, pathDelimiter = '_') {
45
43
  copyTypeProperties,
46
44
  isAssociationOperand,
47
45
  isDollarSelfOrProjectionOperand,
48
- getFinalBaseType,
49
46
  createExposingProjection,
50
47
  createAndAddDraftAdminDataProjection,
51
48
  createScalarElement,
@@ -243,21 +240,19 @@ function getTransformers(model, options, pathDelimiter = '_') {
243
240
  // for type of 'x' -> elem.type is an object, not a string -> use directly
244
241
  let elemType;
245
242
  if (!elem.elements) // structures do not have final base type
246
- elemType = getFinalBaseType(elem.type);
243
+ elemType = getFinalBaseTypeWithProps(elem.type);
247
244
 
248
245
  const struct = elemType ? elemType.elements : elem.elements;
249
246
 
250
247
  // Collect all child elements (recursively) into 'result'
251
248
  // TODO: Do not report collisions in the generated elements here, but instead
252
- // leave that work to the receiver of this result
249
+ // leave that work to the receiver of this result
253
250
  let result = Object.create(null);
254
251
  const addGeneratedFlattenedElement = (e, eName) => {
255
- if(result[eName]){
256
- error(null, pathInCsn, { name: eName },
257
- 'Generated element $(NAME) conflicts with other generated element')
258
- } else {
252
+ if (result[eName])
253
+ error('name-duplicate-element', pathInCsn, { '#': 'flatten-element-gen', name: eName })
254
+ else
259
255
  result[eName] = e;
260
- }
261
256
  }
262
257
  forEach(struct, (childName, childElem) => {
263
258
  if (isStructured(childElem)) {
@@ -287,7 +282,11 @@ function getTransformers(model, options, pathDelimiter = '_') {
287
282
 
288
283
  // Fix all collected flat elements (names, annotations, properties, origin ..)
289
284
  forEach(result, (name, flatElem) => {
290
- // Copy annotations from struct (not overwriting, because deep annotations should have precedence)
285
+ // Copy annotations from struct (not overwriting, because deep annotations should have precedence).
286
+ // Attention:
287
+ // This has historic reasons. We don't copy doc-comments because copying annotations
288
+ // is questionable to begin with. Only selected annotations should have been copied,
289
+ // if at all.
291
290
  copyAnnotations(elem, flatElem, false);
292
291
  // Copy selected type properties
293
292
  const props = ['key', 'virtual', 'masked', 'viaAll'];
@@ -395,78 +394,65 @@ function getTransformers(model, options, pathDelimiter = '_') {
395
394
  }
396
395
 
397
396
  /**
398
- * Replace the type of 'node' with its final base type (in contrast to the compiler,
399
- * also unravel derived enum types, i.e. take the final base type of the enum's base type.
400
- * Similar with associations and compositions (we probably need a _baseType link)
397
+ * Replace the type of 'nodeWithType' with its final base type, i.e. copy relevant type properties and
398
+ * set the `type` property to the builtin if scalar or delete it if structured/arrayed.
401
399
  *
402
- * @param {CSN.Artifact} node
400
+ * @param {object} nodeWithType
403
401
  * @param {WeakMap} [resolved] WeakMap containing already resolved refs
404
402
  * @param {boolean} [keepLocalized=false] Whether to clone .localized from a type def
405
- * @returns {void}
406
403
  */
407
- function toFinalBaseType(node, resolved, keepLocalized=false) {
408
- // Nothing to do if no type (or if array/struct type)
409
- if (!node || !node.type) return;
410
- // In case of a ref -> Follow the ref
411
- if (node.type && node.type.ref) {
412
- const finalBaseType = getFinalBaseType(node.type, undefined, resolved);
413
- if(finalBaseType === null)
414
- throw Error('Failed to obtain final base type for reference : ' + node.type.ref.join('/'));
415
- if(finalBaseType.elements) {
416
- // This changes the order - to be discussed!
417
- node.elements = cloneCsnNonDict(finalBaseType, options).elements; // copy elements
418
- delete node.type; // delete the type reference as edm processing does not expect it
419
- } else if(finalBaseType.items) {
420
- // This changes the order - to be discussed!
421
- node.items = cloneCsnNonDict(finalBaseType.items, options); // copy items
422
- delete node.type;
423
- } else {
424
- node.type=finalBaseType;
425
- }
404
+ function toFinalBaseType(nodeWithType, resolved = new WeakMap(), keepLocalized = false) {
405
+ const type = nodeWithType?.type;
406
+ if (!type || nodeWithType.elements || nodeWithType.items || resolved.has(nodeWithType)) {
426
407
  return;
427
408
  }
428
- // .. or builtin already
429
- if (node.type && isBuiltinType(node.type)) return;
409
+ // The caller may use `{ art }` syntax for `{ ref }` objects, but we only use
410
+ // it to indicate that an artifact has been processed.
411
+ resolved.set(nodeWithType, nodeWithType);
430
412
 
431
- // The type might already be a full fledged type def (array of)
432
- let typeDef = typeof node.type === 'string' ? getCsnDef(node.type) : node.type;
433
- // Nothing to do if type is an array or a struct type
434
- if (typeDef.items || typeDef.elements) {
435
- // cloneCsn only works correctly if we start "from the top"
436
- const cloneTypeDef = cloneCsnNonDict(typeDef, options);
437
- // With hdbcds-hdbcds, don't resolve structured types - but propagate ".items", to turn into LargeString later on.
438
- if(typeDef.items) {
439
- delete node.type;
440
- if(!node.items)
441
- Object.assign(node, {items: cloneTypeDef.items});
442
- }
443
- if(typeDef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')) {
444
- if(!typeDef.items)
445
- delete node.type;
446
- if(!node.elements)
447
- Object.assign(node, {elements: cloneTypeDef.elements});
448
- }
413
+ // Nothing to copy from builtin.
414
+ if (typeof type === 'string' && isBuiltinType(type))
415
+ return;
449
416
 
417
+ let typeRef = null;
418
+ if (resolved.has(type)) {
419
+ typeRef = resolved.get(type)?.art
420
+ // The cached entry may not be resolved, yet.
421
+ if (typeRef.type && !isBuiltinType(typeRef.type))
422
+ typeRef = getFinalBaseTypeWithProps(typeRef.type);
423
+ } else {
424
+ typeRef = getFinalBaseTypeWithProps(type);
425
+ }
450
426
 
427
+ if (typeRef.elements || typeRef.items) {
428
+ // Copy elements/items and we're finished. No need to look up actual base type,
429
+ // since it must also be structured and must contain at least as many elements,
430
+ // if not more (in client style CSN).
431
+ if (typeRef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')) {
432
+ nodeWithType.elements = cloneCsnDictionary(typeRef.elements, options);
433
+ delete nodeWithType.type;
434
+ }
435
+ if (typeRef.items) {
436
+ nodeWithType.items = cloneCsnNonDict(typeRef.items, options);
437
+ delete nodeWithType.type;
438
+ }
451
439
  return;
452
440
  }
453
- // if the declared element is an enum, these values are with priority
454
- if (!node.enum && typeDef.enum) {
455
- const clone = cloneCsnDictionary(typeDef.enum, options);
456
- Object.assign(node, { enum: clone });
441
+
442
+ if (typeRef.enum && nodeWithType.enum === undefined)
443
+ nodeWithType.enum = cloneCsnDictionary(typeRef.enum, options);
444
+
445
+ // Copy type and type arguments (+ localized)
446
+
447
+ for (const param of typeParameters.list) {
448
+ if (nodeWithType[param] === undefined && typeRef[param] !== undefined &&!typeRef.$default) {
449
+ nodeWithType[param] = typeRef[param];
450
+ }
457
451
  }
458
- if (node.length === undefined && typeDef.length !== undefined)
459
- Object.assign(node, { length: typeDef.length });
460
- if (node.precision === undefined && typeDef.precision !== undefined)
461
- Object.assign(node, { precision: typeDef.precision });
462
- if (node.scale === undefined && typeDef.scale !== undefined)
463
- Object.assign(node, { scale: typeDef.scale });
464
- if (node.srid === undefined && typeDef.srid !== undefined)
465
- Object.assign(node, { srid: typeDef.srid });
466
- if (keepLocalized && node.localized === undefined && typeDef.localized !== undefined)
467
- Object.assign(node, { localized: typeDef.localized });
468
- node.type = typeDef.type;
469
- toFinalBaseType(node);
452
+ if (keepLocalized && nodeWithType.localized === undefined && typeRef.localized !== undefined)
453
+ nodeWithType.localized = typeRef.localized;
454
+ if (typeRef.type)
455
+ nodeWithType.type = typeRef.type;
470
456
  }
471
457
 
472
458
  // Return a full projection 'projectionId' of artifact 'art' for exposure in 'service'.
@@ -57,10 +57,10 @@ function translateAssocsToJoins(model, inputOptions = {})
57
57
  const options = model.options || inputOptions;
58
58
 
59
59
  // create JOINs for foreign key paths
60
- const noJoinForFK = options.forHana ? !options.forHana.joinfk : true;
60
+ const noJoinForFK = options.forHana ? !options.joinfk : true;
61
61
 
62
62
  // Note: This is called from the 'forHana' transformations, so it is controlled by its options)
63
- const pathDelimiter = (options.forHana && options.forHana.names === 'hdbcds') ? '.' : '_';
63
+ const pathDelimiter = (options.forHana && options.sqlMapping === 'hdbcds') ? '.' : '_';
64
64
 
65
65
  forEachDefinition(model, prepareAssociations);
66
66
  forEachDefinition(model, transformQueries);
@@ -21,11 +21,11 @@
21
21
  "sonarjs/no-duplicate-string": "off"
22
22
  },
23
23
  "parserOptions": {
24
- "ecmaVersion": 2018,
24
+ "ecmaVersion": 2020,
25
25
  "sourceType": "script"
26
26
  },
27
27
  "env": {
28
- "es6": true,
28
+ "es2020": true,
29
29
  "node": true
30
30
  },
31
31
  "settings": {
@@ -12,7 +12,7 @@ const { setAnnotationIfNotDefined } = require('./utils');
12
12
  */
13
13
  function setCoreComputedOnViews(csn) {
14
14
  const {
15
- artifactRef, getColumn, getElement,
15
+ artifactRef, getColumn, getElement, getOrigin,
16
16
  } = getUtils(csn, 'init-all');
17
17
 
18
18
  forEachDefinition(csn, (artifact) => {
@@ -57,7 +57,15 @@ function setCoreComputedOnViews(csn) {
57
57
  const column = getColumn(element);
58
58
  if (column)
59
59
  return column;
60
- return getElementFromFrom(name, base.from);
60
+ const from = getElementFromFrom(name, base.from);
61
+ if (from)
62
+ return from;
63
+ // For .expand/.inline, we can find it via origin
64
+ // Although I would have expected to find it via getColumn...
65
+ const origin = getOrigin(element);
66
+ if (origin)
67
+ return origin;
68
+ throw new Error(`Could not find ancestor for ${JSON.stringify(element)} named ${name}`);
61
69
  }
62
70
 
63
71
  /**
@@ -87,10 +95,7 @@ function setCoreComputedOnViews(csn) {
87
95
  return getElementFromFrom(name, base.SET.args[0]);
88
96
  }
89
97
  else if (base.args && base.join) {
90
- const result = checkJoinSources(base.args, name);
91
- if (!result)
92
- throw new Error(`Could not find ${name} in ${JSON.stringify(base.args)}`);
93
- return result;
98
+ return checkJoinSources(base.args, name);
94
99
  }
95
100
 
96
101
  throw new Error(JSON.stringify(base));
@@ -29,10 +29,7 @@ module.exports = (csn, options) => {
29
29
  const definitionPropagationRules = {
30
30
  '@cds.autoexpose': onlyViaArtifact,
31
31
  '@fiori.draft.enabled': onlyViaArtifact,
32
- '@': (prop, target, source) => {
33
- if (source[prop] !== null)
34
- target[prop] = source[prop];
35
- },
32
+ '@': nullStopsPropagation,
36
33
  // Example: `type E : F;` does not have `elements`, but they are required for e.g. OData.
37
34
  elements: onlyTypeDef,
38
35
  '@cds.persistence.exists': skip,
@@ -48,7 +45,7 @@ module.exports = (csn, options) => {
48
45
  '@cds.autoexposed': skip,
49
46
  '@cds.redirection.target': skip,
50
47
  type: always,
51
- doc: always,
48
+ doc: nullStopsPropagation,
52
49
  length: always,
53
50
  precision: always,
54
51
  scale: always,
@@ -94,6 +91,10 @@ module.exports = (csn, options) => {
94
91
  }, // overwrite from defProps
95
92
  kind: skip,
96
93
  val: always,
94
+ type: notWithItemsOrElements,
95
+ target: notWithItemsOrElements,
96
+ keys: notWithItemsOrElements,
97
+ cardinality: notWithItemsOrElements,
97
98
  };
98
99
 
99
100
  generate();
@@ -702,6 +703,19 @@ function notWithTypeOrigin(prop, target, source) {
702
703
  target[prop] = source[prop];
703
704
  }
704
705
 
706
+ /**
707
+ * The value `null` tells us to skip the propagation of the property.
708
+ * This is the case e.g. for `doc` or for annotations.
709
+ *
710
+ * @param {string} prop
711
+ * @param {CSN.Element} target
712
+ * @param {CSN.Element} source
713
+ */
714
+ function nullStopsPropagation(prop, target, source) {
715
+ if (source[prop] !== null)
716
+ target[prop] = source[prop];
717
+ }
718
+
705
719
  /**
706
720
  * Special propagation rules for .items - depending on the exact type of .items and the
707
721
  * way it was referenced (type of, direct type, direct many), we need to propagate (or not).
@@ -722,6 +736,20 @@ function specialItemsRules(prop, target, source) {
722
736
  target[prop] = source[prop];
723
737
  }
724
738
 
739
+ /**
740
+ * Don't propagate certain properties if the target already has a .items or .elements
741
+ *
742
+ * This happens with .expand/.inline
743
+ *
744
+ * @param {string} prop
745
+ * @param {CSN.Element} target
746
+ * @param {CSN.Element} source
747
+ */
748
+ function notWithItemsOrElements(prop, target, source) {
749
+ if (!target.items && !target.elements || !source.target)
750
+ target[prop] = source[prop];
751
+ }
752
+
725
753
  /**
726
754
  * Some properties must not be copied over if the type of this member
727
755
  * is a reference to another element.