@sap/cds-compiler 3.9.4 → 4.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 (94) hide show
  1. package/CHANGELOG.md +92 -4
  2. package/README.md +0 -1
  3. package/bin/cdsc.js +11 -23
  4. package/bin/cdsse.js +3 -3
  5. package/doc/API.md +5 -0
  6. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  7. package/doc/CHANGELOG_BETA.md +17 -1
  8. package/doc/CHANGELOG_DEPRECATED.md +28 -0
  9. package/lib/api/.eslintrc.json +1 -1
  10. package/lib/api/main.js +26 -8
  11. package/lib/api/options.js +2 -0
  12. package/lib/base/error.js +2 -0
  13. package/lib/base/message-registry.js +143 -64
  14. package/lib/base/messages.js +213 -107
  15. package/lib/base/model.js +11 -11
  16. package/lib/checks/.eslintrc.json +1 -1
  17. package/lib/checks/annotationsOData.js +2 -2
  18. package/lib/checks/elements.js +1 -1
  19. package/lib/checks/enricher.js +26 -3
  20. package/lib/checks/onConditions.js +67 -12
  21. package/lib/checks/queryNoDbArtifacts.js +106 -105
  22. package/lib/checks/sql-snippets.js +2 -0
  23. package/lib/checks/types.js +12 -6
  24. package/lib/checks/validator.js +2 -2
  25. package/lib/compiler/assert-consistency.js +10 -8
  26. package/lib/compiler/builtins.js +8 -2
  27. package/lib/compiler/checks.js +52 -35
  28. package/lib/compiler/define.js +31 -26
  29. package/lib/compiler/extend.js +120 -65
  30. package/lib/compiler/finalize-parse-cdl.js +12 -43
  31. package/lib/compiler/generate.js +16 -5
  32. package/lib/compiler/index.js +8 -5
  33. package/lib/compiler/kick-start.js +4 -3
  34. package/lib/compiler/populate.js +96 -95
  35. package/lib/compiler/propagator.js +7 -8
  36. package/lib/compiler/resolve.js +377 -103
  37. package/lib/compiler/shared.js +794 -517
  38. package/lib/compiler/tweak-assocs.js +8 -6
  39. package/lib/compiler/utils.js +44 -0
  40. package/lib/edm/annotations/genericTranslation.js +12 -4
  41. package/lib/edm/csn2edm.js +34 -32
  42. package/lib/edm/edm.js +34 -31
  43. package/lib/edm/edmAnnoPreprocessor.js +0 -23
  44. package/lib/edm/edmInboundChecks.js +7 -2
  45. package/lib/edm/edmPreprocessor.js +18 -17
  46. package/lib/edm/edmUtils.js +8 -4
  47. package/lib/gen/Dictionary.json +18 -0
  48. package/lib/gen/language.checksum +1 -1
  49. package/lib/gen/language.interp +4 -2
  50. package/lib/gen/languageParser.js +5006 -4582
  51. package/lib/json/from-csn.js +157 -112
  52. package/lib/json/to-csn.js +60 -89
  53. package/lib/language/antlrParser.js +17 -13
  54. package/lib/language/docCommentParser.js +11 -1
  55. package/lib/language/genericAntlrParser.js +13 -10
  56. package/lib/language/language.g4 +168 -97
  57. package/lib/main.d.ts +128 -36
  58. package/lib/main.js +1 -1
  59. package/lib/model/csnRefs.js +24 -5
  60. package/lib/model/csnUtils.js +9 -8
  61. package/lib/model/revealInternalProperties.js +7 -12
  62. package/lib/modelCompare/compare.js +1 -1
  63. package/lib/modelCompare/utils/filter.js +40 -2
  64. package/lib/optionProcessor.js +0 -3
  65. package/lib/render/toCdl.js +247 -214
  66. package/lib/render/toHdbcds.js +197 -181
  67. package/lib/render/toSql.js +325 -289
  68. package/lib/render/utils/common.js +42 -4
  69. package/lib/render/utils/delta.js +1 -1
  70. package/lib/render/utils/sql.js +3 -3
  71. package/lib/transform/braceExpression.js +2 -2
  72. package/lib/transform/db/.eslintrc.json +1 -1
  73. package/lib/transform/db/applyTransformations.js +3 -3
  74. package/lib/transform/db/associations.js +24 -12
  75. package/lib/transform/db/expansion.js +17 -18
  76. package/lib/transform/db/flattening.js +17 -21
  77. package/lib/transform/db/rewriteCalculatedElements.js +171 -64
  78. package/lib/transform/db/views.js +3 -4
  79. package/lib/transform/draft/db.js +21 -12
  80. package/lib/transform/draft/odata.js +4 -0
  81. package/lib/transform/forOdataNew.js +11 -10
  82. package/lib/transform/forRelationalDB.js +12 -7
  83. package/lib/transform/localized.js +4 -2
  84. package/lib/transform/odata/toFinalBaseType.js +5 -5
  85. package/lib/transform/odata/typesExposure.js +3 -3
  86. package/lib/transform/parseExpr.js +3 -0
  87. package/lib/transform/transformUtilsNew.js +43 -23
  88. package/lib/transform/translateAssocsToJoins.js +7 -6
  89. package/lib/transform/universalCsn/.eslintrc.json +1 -1
  90. package/lib/transform/universalCsn/coreComputed.js +7 -5
  91. package/lib/transform/universalCsn/universalCsnEnricher.js +12 -12
  92. package/package.json +2 -2
  93. package/share/messages/{duplicate-autoexposed.md → def-duplicate-autoexposed.md} +5 -1
  94. package/share/messages/message-explanations.json +1 -1
@@ -610,8 +610,10 @@ function _addLocalizationViews(csn, options, useJoins, config) {
610
610
  override: true,
611
611
  filter: (name) => name.startsWith('localized.'),
612
612
  notFound(name, index) {
613
- if (!ignoreUnknownExtensions)
614
- messageFunctions.message('anno-undefined-art', [ 'extensions', index ], { name })
613
+ if (!ignoreUnknownExtensions) {
614
+ messageFunctions.message('anno-undefined-art', [ 'extensions', index ],
615
+ { art: name });
616
+ }
615
617
  },
616
618
  });
617
619
  }
@@ -103,14 +103,14 @@ function expandToFinalBaseType(csn, transformers, csnUtils, services, options, i
103
103
 
104
104
  if(node.type && !isBuiltinType(node.type)) {
105
105
  const finalBaseType = csnUtils.getFinalTypeInfo(node.type);
106
- if(!finalBaseType) {
106
+ if(finalBaseType == null) {
107
107
  /*
108
- type could not be resolved, set it to null
109
- Today, all type refs must be resolvable,
110
- input validations checkTypeDefinitionHasType, checkElementTypeDefinitionHasType
108
+ type could not be resolved, delete type property to be equal to a typeless element
109
+ definition. Today, all type refs must be resolvable, input validations
110
+ checkTypeDefinitionHasType, checkElementTypeDefinitionHasType
111
111
  guarantee this. In the future this may change.
112
112
  */
113
- node.type = null;
113
+ delete node.type;
114
114
  }
115
115
  else {
116
116
  if (isExpandable(finalBaseType) || node.kind === 'type') {
@@ -26,7 +26,7 @@ const { CompilerAssertion } = require('../../base/error');
26
26
  * and edges e(v_r, v_d) such that all v_r, v_d are elements of S (v_rs, v_ds) and with that
27
27
  * all edges are { e(v_rs, v_ds) }.
28
28
  *
29
- * The input CSN may contain edges e(v_rs, v_dns) with v_r element of S_i (v_rs) and v_d not element
29
+ * The input CSN may contain edges e(v_rs, v_dns) with v_r element of S_i (v_rs) and v_d not element
30
30
  * of S_i (v_dns).
31
31
  *
32
32
  * The aim of this algorithm is to produce Tc's for all requested S_i by 'filling' up the missing
@@ -50,7 +50,7 @@ const { CompilerAssertion } = require('../../base/error');
50
50
  *
51
51
  * If name(v_dns) has no prefix segments, the fallback schema name is prepended instead:
52
52
  * name(v_ds) = name(v_s) + '.' + fallbackschema + name(v_dns);
53
- *
53
+ *
54
54
  * @param {CSN.Model} csn
55
55
  * @param {function} whatsMyServiceName
56
56
  * @param {string[]} requestedServiceNames
@@ -160,7 +160,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
160
160
  // we're no longer in a key def => don't set notNull:true on named types
161
161
  if(isKey)
162
162
  isKey = false;
163
- // in case this was a named type and if the openess does not match the type definition
163
+ // in case this was a named type and if the openness does not match the type definition
164
164
  // expose the type as a new one not changing the original definition.
165
165
  if(elements && !!node['@open'] !== !!typeDef['@open'])
166
166
  fullQualifiedNewTypeName += node['@open'] ? '_open' : '_closed';
@@ -282,6 +282,9 @@ function parseExpr(xpr, state = { anno: 0, array: true, nary: false }) {
282
282
  // if(xpr?.func && funkyfuncs.includes(xpr?.func))
283
283
  // return xpr;
284
284
  for(let n in xpr) {
285
+ // xpr could be an array with polluted prototype
286
+ if (!Object.hasOwnProperty.call(xpr, n))
287
+ continue;
285
288
  const x = xpr[n];
286
289
  const isAnno = n[0] === '@' && isSimpleAnnoValue(x);
287
290
  if(isAnno)
@@ -66,28 +66,35 @@ function getTransformers(model, options, pathDelimiter = '_') {
66
66
  expandStructsInExpression,
67
67
  };
68
68
 
69
- // Try to apply length, precision, scale from options if no type facet is set on the primitive types 'cds.String' or 'cds.Decimal'.
70
- // If 'obj' has primitive type 'cds.String' and no length try to apply length from options if available or set to default 5000.
71
- // if 'obj' has primitive type 'cds.Decimal' try to apply precision, scale from options if available.
72
- function addDefaultTypeFacets(element, defStrLen5k=true) {
69
+ /**
70
+ * Try to apply length, precision, scale from options if no type facet is set on the primitive types 'cds.String' or 'cds.Decimal'.
71
+ * If 'obj' has primitive type 'cds.String' and no length try to apply length from options if available or set to default internalDefaultLengths[type].
72
+ * if 'obj' has primitive type 'cds.Decimal' try to apply precision, scale from options if available.
73
+ *
74
+ * @param {CSN.Element} element
75
+ * @param {null|object} [internalDefaultLengths] Either null (no implicit default) or an object `{ 'cds.String': N, 'cds.Binary': N }`.
76
+ * */
77
+ function addDefaultTypeFacets(element, internalDefaultLengths = null) {
73
78
  if (!element || !element.type)
74
79
  return;
75
80
 
76
81
  if (element.type === 'cds.String' && element.length === undefined) {
77
- if(options.defaultStringLength) {
82
+ if (options.defaultStringLength) {
78
83
  element.length = options.defaultStringLength;
79
84
  setProp(element, '$default', true);
80
85
  }
81
- else if(defStrLen5k)
82
- element.length = 5000;
86
+ else if (internalDefaultLengths !== null) {
87
+ element.length = internalDefaultLengths[element.type];
88
+ }
83
89
  }
84
90
  if (element.type === 'cds.Binary' && element.length === undefined) {
85
- if(options.defaultBinaryLength) {
91
+ if (options.defaultBinaryLength) {
86
92
  element.length = options.defaultBinaryLength;
87
93
  setProp(element, '$default', true);
88
94
  }
89
- else if(defStrLen5k)
90
- element.length = 5000;
95
+ else if(internalDefaultLengths !== null) {
96
+ element.length = internalDefaultLengths[element.type];
97
+ }
91
98
  }
92
99
  /*
93
100
  if (element.type === 'cds.Decimal' && element.precision === undefined && options.precision) {
@@ -273,7 +280,22 @@ function getTransformers(model, options, pathDelimiter = '_') {
273
280
  // This has historic reasons. We don't copy doc-comments because copying annotations
274
281
  // is questionable to begin with. Only selected annotations should have been copied,
275
282
  // if at all.
276
- copyAnnotations(elem, flatElem, false);
283
+ // When flattening structured elements for OData don't propagate the odata.Type annotations
284
+ // as these would falsify the flattened elements. Type facets must be aligned with
285
+ // EdmTypeFacetMap defined in edm.js
286
+ const excludes = options.toOdata ?
287
+ {
288
+ '@odata.Type': 1,
289
+ '@odata.Scale': 1,
290
+ '@odata.Precision': 1,
291
+ '@odata.MaxLength': 1,
292
+ '@odata.SRID': 1,
293
+ '@odata.FixedLength': 1,
294
+ '@odata.Collation': 1,
295
+ '@odata.Unicode': 1,
296
+ } : {};
297
+ copyAnnotations(elem, flatElem, false, excludes);
298
+
277
299
  // Copy selected type properties
278
300
  const props = ['key', 'virtual', 'masked', 'viaAll'];
279
301
  // 'localized' is needed for OData
@@ -413,14 +435,11 @@ function getTransformers(model, options, pathDelimiter = '_') {
413
435
  // Copy elements/items and we're finished. No need to look up actual base type,
414
436
  // since it must also be structured and must contain at least as many elements,
415
437
  // if not more (in client style CSN).
416
- if (typeRef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')) {
438
+ if (typeRef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds'))
417
439
  nodeWithType.elements = cloneCsnDictionary(typeRef.elements, options);
418
- delete nodeWithType.type;
419
- }
420
- if (typeRef.items) {
440
+ else if (typeRef.items)
421
441
  nodeWithType.items = cloneCsnNonDict(typeRef.items, options);
422
- delete nodeWithType.type;
423
- }
442
+
424
443
  return;
425
444
  }
426
445
 
@@ -450,7 +469,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
450
469
  art.elements && Object.entries(art.elements).forEach(([elemName, artElem]) => {
451
470
  let elem = Object.assign({}, artElem);
452
471
  // Transfer xrefs, that are redirected to the projection
453
- // TODO: shall we remove the transfered elements from the original?
472
+ // TODO: shall we remove the transferred elements from the original?
454
473
  // if (artElem._xref) {
455
474
  // setProp(elem, '_xref', artElem._xref.filter(xref => xref.user && xref.user._main && xref.user._main._service == service));
456
475
  // }
@@ -470,7 +489,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
470
489
  // Assemble the projection itself and add it into the model
471
490
  let projection = {
472
491
  'kind': 'entity',
473
- projection: query.SELECT, // it is important that projetion and query refer to the same object!
492
+ projection: query.SELECT, // it is important that projection and query refer to the same object!
474
493
  elements
475
494
  };
476
495
  // copy annotations from art to projection
@@ -749,6 +768,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
749
768
  if (!isBuiltinType(returnTypeName) && !model.definitions[returnTypeName])
750
769
  throw new ModelError('Expecting valid return type name: ' + returnTypeName);
751
770
  action.returns = { type: returnTypeName };
771
+ // TODO: What about annotation propagation from return type to `returns`?
752
772
  }
753
773
 
754
774
  // Add parameter if provided
@@ -918,7 +938,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
918
938
 
919
939
  /**
920
940
  * Assigns unconditionally annotation to a node, which means it overwrites already existing annotation assignment.
921
- * Overwritting is when the assignment differs from undefined and null, also when differs from the already set value.
941
+ * Overwriting is when the assignment differs from undefined and null, also when differs from the already set value.
922
942
  * Setting new assignment results false as return value and overwriting - true.
923
943
  *
924
944
  * @param {object} node Assignee
@@ -1114,7 +1134,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
1114
1134
  const lhsArt = lhs._art || lhs.ref && !lhs.$scope && inspectRef(location.concat(i)).art;
1115
1135
  const rhsArt = rhs._art || rhs.ref && !rhs.$scope && inspectRef(location.concat(i+2)).art;
1116
1136
  const lhsIsVal = (lhs.val !== undefined);
1117
- // if ever rhs should be alowed to be a value uncomment this
1137
+ // if ever rhs should be allowed to be a value uncomment this
1118
1138
  const rhsIsVal = (rhs === 'null' /*|| rhs.val !== undefined*/);
1119
1139
 
1120
1140
  // lhs & rhs must be expandable types (structures or managed associations)
@@ -1300,8 +1320,8 @@ function getTransformers(model, options, pathDelimiter = '_') {
1300
1320
  }
1301
1321
 
1302
1322
  function getType(art) {
1303
- const effart = effectiveType(art);
1304
- return Object.keys(effart).length ? effart : art.type;
1323
+ const effArt = effectiveType(art);
1324
+ return Object.keys(effArt).length ? effArt : art.type;
1305
1325
  }
1306
1326
 
1307
1327
  function isExpandable(art) {
@@ -230,7 +230,7 @@ function translateAssocsToJoins(model, inputOptions = {})
230
230
 
231
231
  /*
232
232
  Add an artificial QA for each mixin definition. This QA completes the QAT
233
- datastructure that requires a QA at the rootQat before starting the join generation.
233
+ data-structure that requires a QA at the rootQat before starting the join generation.
234
234
  This QA is marked as 'mixin' which indicates that the paths of the ON condition must
235
235
  not receive the usual source and target table alias (which is used for generic associations)
236
236
  but instead just use the rootQA of the individual ON condition paths. These paths are
@@ -684,7 +684,7 @@ function translateAssocsToJoins(model, inputOptions = {})
684
684
  //env.assocStack.includes(fwdAssoc) => recursion
685
685
  if(env.assocStack.length === 2) {
686
686
  // reuse (ugly) error message from forHana
687
- error(null, env.assocStack[0].location,
687
+ error(null, [env.assocStack[0].location,env.assocStack[0]],
688
688
  { name: '$self', id: '$self' },
689
689
  'An association that uses $(NAME) in its ON-condition can\'t be compared to $(ID)');
690
690
  // don't check these paths again
@@ -706,6 +706,7 @@ function translateAssocsToJoins(model, inputOptions = {})
706
706
  }
707
707
 
708
708
  function cloneOnCondExprTree(expr) {
709
+ // TODO: This function is not covered by an tests, only cloneOnCondExprStream is.
709
710
  // keep parentheses intact
710
711
  if(Array.isArray(expr))
711
712
  return expr.map(cloneOnCondition);
@@ -1126,7 +1127,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1126
1127
  for(let fkn in element.foreignKeys)
1127
1128
  {
1128
1129
  let fk = element.foreignKeys[fkn];
1129
- // once a fk is to be followed, treat all sub patsh as srcSide, this will add fk.name.id only
1130
+ // once a fk is to be followed, treat all sub-paths as srcSide, this will add fk.name.id only
1130
1131
  if(srcSide)
1131
1132
  paths = paths.concat(flattenElement(fk.targetElement._artifact, true, fk.name.id, fk.targetElement.path.map(ps => ps.id).join(pathDelimiter)));
1132
1133
  else
@@ -1199,7 +1200,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1199
1200
  /*
1200
1201
  Munch path steps and append them to a path string until an
1201
1202
  assoc step is found. The assoc path step is also appended
1202
- to the path string. If no assoc path step has occured, all
1203
+ to the path string. If no assoc path step has occurred, all
1203
1204
  path steps are added to the path string and tail is empty.
1204
1205
 
1205
1206
  Return assocPathStep, the remaining tail path and the path string
@@ -1219,7 +1220,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1219
1220
 
1220
1221
  /*
1221
1222
  Substitute the n first path steps of a given path against a FK alias name.
1222
- Resolve a foreign key of a managaged association by following the n first
1223
+ Resolve a foreign key of a managed association by following the n first
1223
1224
  path steps. Longest path matches:
1224
1225
  Example: fk tuple { a.b, a.b.c, a.b.e },
1225
1226
  path: a.b.c.d.e.f: FK a.b.c is found, even if FK a.b is one level higher in the prefix tree.
@@ -1866,7 +1867,7 @@ function walkPath(node, env)
1866
1867
  /*
1867
1868
  NOTE: As long as association path steps are not allowed in filters,
1868
1869
  it is not required to walk over filter expressions.
1869
- Simple filter paths are rewritten inin createJoinTree (first filter)
1870
+ Simple filter paths are rewritten in createJoinTree (first filter)
1870
1871
  and createJoinQA (subsequent one that belong to the ON condition).
1871
1872
 
1872
1873
  If the filter becomes JOIN relevant, default FILTERS (part of the
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "root": true,
3
3
  "plugins": ["sonarjs", "jsdoc"],
4
- "extends": ["../../../.eslintrc-ydkjsi.json", "plugin:sonarjs/recommended", "plugin:jsdoc/recommended"],
4
+ "extends": ["plugin:jsdoc/recommended", "../../../.eslintrc-ydkjsi.json", "plugin:sonarjs/recommended"],
5
5
  "rules": {
6
6
  "prefer-const": "error",
7
7
  "quotes": ["error", "single", "avoid-escape"],
@@ -27,7 +27,9 @@ function setCoreComputedOnViewsAndCalculatedElements( csn, csnUtils ) {
27
27
  }
28
28
  else if (artifact.kind === 'entity' || artifact.kind === 'aspect') {
29
29
  forEachMemberRecursively(artifact, (element) => {
30
- if (element.value && !element.value?.ref) // calculated elements, but simple references are ignored
30
+ // Calculated elements, but simple references are ignored for on-read.
31
+ // casts() are also computed. In CSN, they appear next to a `.ref`.
32
+ if (element.value && (!element.value.ref || element.value.cast || element.value.stored))
31
33
  setAnnotationIfNotDefined(element, '@Core.Computed', true);
32
34
  }, path);
33
35
  }
@@ -95,7 +97,7 @@ function setCoreComputedOnViewsAndCalculatedElements( csn, csnUtils ) {
95
97
  return getAncestor(base.SELECT.elements[name], name, base.SELECT);
96
98
  }
97
99
  else if (base.ref) {
98
- let artifact = artifactRef(base);
100
+ let artifact = artifactRef.from(base);
99
101
  if (artifact.target)
100
102
  artifact = artifactRef(artifact.target);
101
103
  return artifact.elements[name];
@@ -145,7 +147,7 @@ function setCoreComputedOnViewsAndCalculatedElements( csn, csnUtils ) {
145
147
  }
146
148
 
147
149
  /**
148
- * Return whether the given columns element needs to be marked with @Core.Computed.
150
+ * Returns true, if the given columns element needs to be annotated with @Core.Computed.
149
151
  *
150
152
  * @param {CSN.Column} column
151
153
  * @returns {boolean}
@@ -153,9 +155,9 @@ function setCoreComputedOnViewsAndCalculatedElements( csn, csnUtils ) {
153
155
  function needsCoreComputed( column ) {
154
156
  return column &&
155
157
  (
156
- column.xpr || column.list || column.func || column.val !== undefined || column.param ||
158
+ column.xpr || column.list || column.func || column.val !== undefined || column['#'] !== undefined || column.param ||
157
159
  column.SELECT || column.SET ||
158
- column.ref && [ '$at', '$valid', '$now', '$user', '$session' ].includes(column.ref[0])
160
+ column.ref && [ '$at', '$valid', '$now', '$user', '$session', '$parameters' ].includes(column.ref[0])
159
161
  );
160
162
  }
161
163
 
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { setProp, isBetaEnabled } = require('../../base/model');
3
+ const { setProp } = require('../../base/model');
4
4
  const shuffleGen = require('../../base/shuffle');
5
5
  const { setAnnotationIfNotDefined, makeClientCompatible } = require('./utils');
6
6
  const {
@@ -23,7 +23,6 @@ const { setCoreComputedOnViewsAndCalculatedElements } = require('./coreComputed'
23
23
  * @param {CSN.Options} options
24
24
  */
25
25
  module.exports = (csn, options) => {
26
- const propagateToReturns = isBetaEnabled( options, 'v4preview' );
27
26
  const csnUtils = getUtils(csn, 'init-all');
28
27
  const {
29
28
  initDefinition, getOrigin, getQueryPrimarySource, artifactRef, getColumn,
@@ -116,7 +115,7 @@ module.exports = (csn, options) => {
116
115
  // The $origin properties need to be removed separately
117
116
  // as the values are used in csnRef::getOrigin that is used during
118
117
  // the propagation above.
119
- // Currently testMode-only for comparison against client CSN.
118
+ // Currently, testMode-only for comparison against client CSN.
120
119
  if (options.testMode)
121
120
  makeClientCompatible(csn);
122
121
 
@@ -174,10 +173,7 @@ module.exports = (csn, options) => {
174
173
 
175
174
  setTargetAspectIfRequired(parent);
176
175
  },
177
- type: ( parent, prop, type, path, grandParent, parentProp ) => {
178
- // annos are not propagated to `returns` (<=v3) and `items`
179
- if (parentProp === 'returns' && !propagateToReturns)
180
- return;
176
+ type: ( parent, prop, type ) => {
181
177
  const annotationsForBuiltinType = extensions[type];
182
178
  Object.assign( parent, annotationsForBuiltinType );
183
179
  },
@@ -288,13 +284,17 @@ module.exports = (csn, options) => {
288
284
  },
289
285
  params: (parent, prop, params) => {
290
286
  forEachValue(params, (param) => {
287
+ const propagateToParams = typeof param.type === 'string' ? csn.definitions[param.type]?.kind !== 'entity' : true;
291
288
  propagateMemberPropsFromOrigin(param, {
292
- items: true, elements: true, enum: true, virtual: true,
289
+ '@': !propagateToParams, items: true, elements: true, enum: true, virtual: true,
293
290
  });
294
291
  });
295
292
  },
296
293
  returns: (parent, prop, returns) => {
297
- propagateMemberPropsFromOrigin(returns, { items: true, '@': !propagateToReturns, elements: true });
294
+ // Only propagate to `returns` (return parameter) if return type is not an entity.
295
+ // If returns.type is an array, it is an element ref. If it's not found, it's likely builtin.
296
+ const propagateToParams = typeof returns.type === 'string' ? csn.definitions[returns.type]?.kind !== 'entity' : true;
297
+ propagateMemberPropsFromOrigin(returns, { '@': !propagateToParams, items: true, elements: true });
298
298
  if (returns.target)
299
299
  calculateForeignKeys(returns);
300
300
  },
@@ -368,7 +368,7 @@ module.exports = (csn, options) => {
368
368
  * @todo check if still necessary
369
369
  */
370
370
  function skipMemberPropagation( origin ) {
371
- // For empty members (`{}`), the origin was set in a previous call to `getOrigin(definition)`.
371
+ // For empty members (`{}`), the origin was set in a previous call to `getOrigin(definition)`.
372
372
  return !origin;
373
373
  }
374
374
  }
@@ -519,7 +519,7 @@ module.exports = (csn, options) => {
519
519
  forEachValue(elements, (element) => {
520
520
  if (element.target) {
521
521
  const column = getColumn(element);
522
- if (column) {
522
+ if (column?.ref) {
523
523
  const mixin = query.SELECT.mixin[implicitAs(column.ref)] || {};
524
524
  copyProperties(mixin, element, getMemberPropagationRuleFor);
525
525
  }
@@ -552,7 +552,7 @@ module.exports = (csn, options) => {
552
552
  if (!target.kind)
553
553
  return;
554
554
  const primarySourceRef = getQueryPrimarySource(target.query || target.projection);
555
- const artRef = primarySourceRef ? artifactRef(primarySourceRef) : source;
555
+ const artRef = primarySourceRef ? artifactRef.from(primarySourceRef) : source;
556
556
  if (!artRef.target)
557
557
  target[prop] = source[prop];
558
558
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "3.9.4",
3
+ "version": "4.0.0",
4
4
  "description": "CDS (Core Data Services) compiler and backends",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "author": "SAP SE (https://www.sap.com)",
@@ -59,6 +59,6 @@
59
59
  "LICENSE"
60
60
  ],
61
61
  "engines": {
62
- "node": ">=14"
62
+ "node": ">=16"
63
63
  }
64
64
  }
@@ -1,4 +1,4 @@
1
- # duplicate-autoexposed
1
+ # def-duplicate-autoexposed
2
2
 
3
3
  Two or more entities with the same name can’t be auto-exposed in the same
4
4
  service.
@@ -76,3 +76,7 @@ auto-exposing entities. The reason is that the resulting auto-exposed names
76
76
  could become _long_ names that don’t seem natural nor intuitive. We chose to
77
77
  expose the entity name because that’s what most developers want to do when
78
78
  they manually expose entities.
79
+
80
+ ## Other Notes
81
+
82
+ This message was called `duplicate-autoexposed` in cds-compiler v3 and earlier.
@@ -4,7 +4,7 @@
4
4
  "anno-duplicate-unrelated-layer",
5
5
  "check-proper-type",
6
6
  "check-proper-type-of",
7
- "duplicate-autoexposed",
7
+ "def-duplicate-autoexposed",
8
8
  "extend-repeated-intralayer",
9
9
  "extend-unrelated-layer",
10
10
  "redirected-to-ambiguous",