@sap/cds-compiler 4.9.6 → 4.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,21 @@
7
7
  Note: `beta` fixes, changes and features are usually not listed in this ChangeLog but [here](doc/CHANGELOG_BETA.md).
8
8
  The compiler behavior concerning `beta` features can change at any time without notice.
9
9
 
10
+ ## Version 4.9.10 - 2025-04-29
11
+
12
+ ### Fixed
13
+
14
+ - Added option `allowMixinInProjectionExtension` which allows referring to mixins in `extend projection`.
15
+ This was forbidden in cds-compiler v4, but re-introduced in v5.5. Users wanting to migrate from cds-compiler
16
+ v3 to v4 can use this option for easier migration.
17
+
18
+ ## Version 4.9.8 - 2024-07-29
19
+
20
+ ### Fixed
21
+
22
+ - compiler: Fix extensions with bound actions using an explicit binding parameter in `parseCdl` CSN.
23
+ - to.edm(x): No `Nullable` attribute for `$ReturnType` of `Collection(<entity type>)` [OData V4 CSDL, section 12.8 Return Type](https://docs.oasis-open.org/odata/odata-csdl-xml/v4.01/odata-csdl-xml-v4.01.html#sec_ReturnType)
24
+
10
25
  ## Version 4.9.6 - 2024-07-15
11
26
 
12
27
  ### Fixed
@@ -975,6 +975,12 @@ const centralMessageTexts = {
975
975
  none: 'Ambiguous $(ID) requires an explicit table alias, but there are none: add table aliases to all sub-queries to disambiguate $(ID)',
976
976
  },
977
977
 
978
+ 'ref-special-in-extend': {
979
+ std: 'In an added column, $(ID) refers to the element of the projection source $(ART), not the table alias or mixin',
980
+ alias: 'In an added column, $(ID) refers to the element of the projection source $(ART), not the table alias',
981
+ mixin: 'In an added column, $(ID) refers to the element of the projection source $(ART), not the mixin',
982
+ },
983
+
978
984
  'type-managed-composition': {
979
985
  std: 'Managed compositions can\'t be used in types', // yet
980
986
  sub: 'Managed compositions can\'t be used in sub elements',
@@ -1203,7 +1203,9 @@ function define( model ) {
1203
1203
  const type = first?.type || first?.items?.type; // this sequence = no derived type
1204
1204
  const path = type?.path;
1205
1205
  if (path?.length === 1 && path[0]?.id === '$self') { // TODO: no where: ?
1206
- const { $self } = main.$tableAliases;
1206
+ const $self = main.$tableAliases?.$self ||
1207
+ main.kind === 'extend' && { name: { id: '$self' } };
1208
+ // remark: an extend has no "table alias" `$self` (relevant for parse-cdl)
1207
1209
  setLink( type, '_artifact', $self );
1208
1210
  setLink( path[0], '_artifact', $self );
1209
1211
  }
@@ -175,7 +175,9 @@ function fns( model ) {
175
175
  having: 'where',
176
176
  groupBy: 'where',
177
177
  column: {
178
- lexical: tableAliasesIfNotExtendAndSelf,
178
+ lexical: (!options.allowMixinInProjectionExtension
179
+ ? tableAliasesIfNotExtendAndSelf // default for v4
180
+ : tableAliasesAndSelf), // default for v5 or via option
179
181
  dollar: true,
180
182
  dynamic: combinedSourcesOrParentElements,
181
183
  notFound: undefinedSourceElement,
@@ -629,7 +631,6 @@ function fns( model ) {
629
631
  // simple 'ref-undefined-art'/'ref-undefined-def' - TODO: which we
630
632
  // could "change" to this message at the end of compile():
631
633
  error( 'ref-unexpected-autoexposed', [ item.location, user ], { art },
632
- // eslint-disable-next-line max-len
633
634
  'An auto-exposed entity can\'t be referred to - expose entity $(ART) explicitly' );
634
635
  return null; // continuation semantics: like “not found”
635
636
  }
@@ -693,16 +694,49 @@ function fns( model ) {
693
694
  return setArtifactLink( head, def ); // we do not want to see the using
694
695
  }
695
696
  case 'mixin': {
697
+ if (options.allowMixinInProjectionExtension) { // for v5 compatibility
698
+ // use a source element having that name if in `extend … with columns`:
699
+ const elem = (user._user || user).$extended &&
700
+ art._parent._combined[head.id];
701
+ if (elem) {
702
+ path.$prefix = elem._parent.name.id; // prepend alias name
703
+ info( 'ref-special-in-extend', [ head.location, user ],
704
+ { '#': 'mixin', id: head.id, art: elem._origin._main } );
705
+ setLink( head, '_navigation', elem );
706
+ return setArtifactLink( head, elem._origin );
707
+ }
708
+ }
696
709
  return setLink( head, '_navigation', art );
697
710
  }
698
711
  case '$navElement': {
699
- if (head.id === (user._user || user).$extended)
700
- path.$prefix = head.id;
712
+ if (!options.allowMixinInProjectionExtension) { // for v5 compatibility
713
+ if (head.id === (user._user || user).$extended)
714
+ path.$prefix = head.id;
715
+ }
701
716
  setLink( head, '_navigation', art );
702
717
  return setArtifactLink( head, art._origin );
703
718
  }
704
- case '$self': // TODO: remove $projection from CC
705
719
  case '$tableAlias': {
720
+ if (options.allowMixinInProjectionExtension) { // for v5 compatibility
721
+ // use a source element having that name if in `extend … with columns`:
722
+ const { $extended } = user._user || user;
723
+ const elem = $extended && art.elements[head.id];
724
+ if (elem) {
725
+ path.$prefix = art.name.id; // prepend alias name
726
+ info('ref-special-in-extend', [ head.location, user ],
727
+ { '#': 'alias', id: head.id, art: elem._origin._main });
728
+ setLink( head, '_navigation', elem );
729
+ return setArtifactLink( head, elem._origin );
730
+ }
731
+ else if ($extended) {
732
+ warning( 'ref-deprecated-in-extend', [ head.location, user ], { id: head.id },
733
+ // eslint-disable-next-line max-len
734
+ 'In an added column, do not use the table alias $(ID) to refer to source elements' );
735
+ }
736
+ }
737
+ }
738
+ /* FALLTHROUGH */
739
+ case '$self': { // TODO: remove $projection from CC
706
740
  setLink( head, '_navigation', art );
707
741
  setArtifactLink( head, art._origin ); // query source or leading query in FROM
708
742
  if (!art._origin)
@@ -1113,7 +1147,7 @@ function fns( model ) {
1113
1147
  // TODO: if it becomes non-configurable, we can omit this warning
1114
1148
  let id = pathName( path );
1115
1149
  let head = path[0]._artifact || { _parent: art };
1116
- // eslint-disable-next-line no-cond-assign
1150
+
1117
1151
  while ((head = head?._parent) && head.kind === 'builtin')
1118
1152
  id = `${ head.name.id }.${ id }`;
1119
1153
  const msgId = (art.$uncheckedElements) ? 'ref-unknown-var' : 'ref-undefined-var';
@@ -1635,7 +1669,7 @@ function fns( model ) {
1635
1669
  const txt = index >= path.length
1636
1670
  ? 'complete'
1637
1671
  : (isAssocToPrimaryKeys( assoc ) ? 'keys' : 'std');
1638
- // eslint-disable-next-line max-len
1672
+
1639
1673
  error( 'ref-invalid-navigation', [ last.location, user ], {
1640
1674
  '#': msgPrefix + txt, art: assoc, name: last.id, alias: '$self',
1641
1675
  }, {
package/lib/edm/edm.js CHANGED
@@ -882,12 +882,20 @@ function getEdm( options, messageFunctions ) {
882
882
  class ReturnType extends PropertyBase {
883
883
  constructor(version, csn) {
884
884
  super(version, {}, csn);
885
+ // CSDL 12.8: If the return type is a collection of entity types,
886
+ // the Nullable attribute has no meaning and MUST NOT be specified.
887
+ if (csn.$NoNullableProperty)
888
+ delete this._edmAttributes.Nullable;
885
889
  }
886
890
 
887
891
  // we need Name but NO $kind, can't use standard to JSON()
888
892
  toJSON() {
889
893
  const json = Object.create(null);
890
894
  this.toJSONattributes(json);
895
+ // CSDL 12.8: If the return type is a collection of entity types,
896
+ // the Nullable attribute has no meaning and MUST NOT be specified.
897
+ if (this._csn.$NoNullableProperty)
898
+ delete json.$Nullable;
891
899
  return json;
892
900
  }
893
901
  }
@@ -1892,7 +1892,7 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
1892
1892
  mapCdsToEdmProp(def);
1893
1893
  annotateAllowedValues(def, defLocation);
1894
1894
  if (def.returns) {
1895
- markCollection(def.returns);
1895
+ markCollection(def.returns, true);
1896
1896
  mapCdsToEdmProp(def.returns);
1897
1897
  annotateAllowedValues(def.returns, [ ...defLocation, 'returns' ]);
1898
1898
  }
@@ -1905,16 +1905,20 @@ function initializeModel( csn, _options, messageFunctions, requestedServiceNames
1905
1905
  rewriteAnnotationExpressions(member);
1906
1906
  if (member.returns) {
1907
1907
  edmUtils.assignAnnotation(member.returns, '@Core.Description', member.returns.doc);
1908
- markCollection(member.returns);
1908
+ markCollection(member.returns, true);
1909
1909
  mapCdsToEdmProp(member.returns);
1910
1910
  annotateAllowedValues(member.returns, [ ...location, 'returns' ]);
1911
1911
  rewriteAnnotationExpressions(member.returns);
1912
1912
  }
1913
1913
  }, defLocation);
1914
1914
  // mark members that need to be rendered as collections
1915
- function markCollection( obj ) {
1915
+ function markCollection( obj, isReturns ) {
1916
1916
  const items = obj.items || csn.definitions[obj.type] && csn.definitions[obj.type].items;
1917
1917
  if (items) {
1918
+ edmUtils.assignProp(obj, '$NoNullableProperty',
1919
+ isReturns && items.type &&
1920
+ !isBuiltinType(items.type) &&
1921
+ csn.definitions[items.type]?.kind === 'entity');
1918
1922
  edmUtils.assignProp(obj, '_NotNullCollection', items.notNull !== undefined ? items.notNull : true);
1919
1923
  edmUtils.assignProp(obj, '$isCollection', true);
1920
1924
  }
@@ -1192,7 +1192,9 @@ function exprInternal( node, xprParens ) {
1192
1192
  }
1193
1193
  if (node.path) {
1194
1194
  const ref = node.path.map( pathItem );
1195
- if (node.path.$prefix) // auto-corrected ORDER BY refs without table alias
1195
+ // auto-corrected ORDER BY refs without table alias, or EXTEND … WITH COLUMN
1196
+ // refs to source element shadowed by alias name:
1197
+ if (node.path.$prefix)
1196
1198
  ref.unshift( node.path.$prefix );
1197
1199
  // we would need to consider node.global here if we introduce that
1198
1200
  return extra( { ref }, node );
package/lib/main.d.ts CHANGED
@@ -131,6 +131,15 @@ declare namespace compiler {
131
131
  * @since v2.8.0
132
132
  */
133
133
  addTextsLanguageAssoc?: boolean
134
+ /**
135
+ * Allow referring to mixins in `extend projection` clauses.
136
+ * This was allowed in cds-compiler v3, forbidden in v4, and allowed in v5.5 by default again.
137
+ *
138
+ * @default false
139
+ * @since v4.9.10
140
+ * @until v4.9.10
141
+ */
142
+ allowMixinInProjectionExtension?: boolean
134
143
  /**
135
144
  * An array of directory names that are used for CDS module lookups.
136
145
  * Lookup directory `node_modules/` is appended if not set explicitly.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "4.9.6",
3
+ "version": "4.9.10",
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)",
@@ -28,7 +28,7 @@
28
28
  "gentest3": "cross-env MAKEREFS=${MAKEREFS:-'true'} mocha --reporter-option maxDiffSize=0 test3/testRefFiles.js",
29
29
  "coverage": "cross-env nyc mocha --reporter-option maxDiffSize=0 test/ test3/testRefFiles.js && nyc report --reporter=lcov",
30
30
  "coverage:piper": "cross-env nyc mocha --reporter test/TestMochaReporter.js --reporter-options mochaFile=./coverage/TEST-results.xml --reporter-option maxDiffSize=0 --timeout 10000 test/ test3/ && nyc report --reporter=cobertura && nyc report --reporter=lcov",
31
- "lint": "eslint bin/ benchmark/ lib/ test/ test3/ scripts/ types/ && node scripts/linter/lintGrammar.js && node scripts/linter/lintTests.js test3/ && node scripts/linter/lintMessages.js && node scripts/linter/lintMessageIdCoverage.js lib/ && markdownlint README.md CHANGELOG.md doc/ internalDoc/ && cd share/messages && markdownlint . && cd ../../ && node scripts/check-changelog.js",
31
+ "lint": "eslint bin/ benchmark/ lib/ test/ test3/ scripts/ && node scripts/linter/lintGrammar.js && node scripts/linter/lintTests.js test3/ && node scripts/linter/lintMessages.js && node scripts/linter/lintMessageIdCoverage.js lib/ && markdownlint README.md CHANGELOG.md doc/ internalDoc/ && cd share/messages && markdownlint . && cd ../../ && node scripts/check-changelog.js",
32
32
  "tslint": "tsc --pretty -p .",
33
33
  "updateVocs": "node scripts/odataAnnotations/generateDictMain.js && npm run generateAllRefs",
34
34
  "updateTocs": "node scripts/update-toc.js",