@sap/cds-compiler 2.15.4 → 2.15.6

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,12 @@
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 2.15.6 - 2022-07-26
11
+
12
+ ### Fixed
13
+
14
+ - Annotations on sub-elements were lost during re-compilation.
15
+
10
16
  ## Version 2.15.4 - 2022-06-09
11
17
 
12
18
  ### Fixed
package/lib/api/main.js CHANGED
@@ -36,7 +36,7 @@ const propertyToCheck = {
36
36
  const { cloneCsnNonDict } = require('../model/csnUtils');
37
37
  const { toHdbcdsSource } = require('../render/toHdbcds');
38
38
  const { ModelError } = require('../base/error');
39
- const { forEach } = require('../utils/objectUtils');
39
+ const { forEach, forEachKey } = require('../utils/objectUtils');
40
40
 
41
41
  const relevantGeneralOptions = [ /* for future generic options */ ];
42
42
  const relevantOdataOptions = [ 'sqlMapping', 'odataFormat' ];
@@ -639,6 +639,7 @@ function publishCsnProcessor( processor, _name ) {
639
639
  */
640
640
  function api( csn, options = {}, ...args ) {
641
641
  try {
642
+ checkOutdatedOptions( options );
642
643
  return processor( csn, options, ...args );
643
644
  }
644
645
  catch (err) {
@@ -662,6 +663,48 @@ function publishCsnProcessor( processor, _name ) {
662
663
  }
663
664
  }
664
665
 
666
+ // Note: No toCsn, because @sap/cds may still use it (2022-06-15)
667
+ const oldBackendOptionNames = [ 'toSql', 'toOdata', 'toHana', 'forHana' ];
668
+ /**
669
+ * Checks if outdated options are used and if so, throw a compiler error.
670
+ * These include:
671
+ * - magicVars (now variableReplacements)
672
+ * - toOdata/toSql/toHana/forHana -> now flat options
673
+ *
674
+ * @param {CSN.Options} options Backend options
675
+ */
676
+ function checkOutdatedOptions(options) {
677
+ if (!options)
678
+ return;
679
+ const { warning } = makeMessageFunction(null, options, 'api');
680
+
681
+ // This warning has been emitted once, we don't need to emit it again.
682
+ if (options.messages && options.messages.some(m => m.messageId === 'api-invalid-option'))
683
+ return;
684
+
685
+ for (const name of oldBackendOptionNames) {
686
+ if (typeof options[name] === 'object') // may be a boolean due to internal options
687
+ warning('api-invalid-option', null, { '#': 'std', name });
688
+ }
689
+
690
+ if (options.magicVars)
691
+ warning('api-invalid-option', null, { '#': 'magicVars' });
692
+
693
+ // Don't check `options.magicVars`. It's likely that the user renamed `magicVars` but
694
+ // forgot about user -> $user and locale -> $user.locale
695
+ if (options.variableReplacements) {
696
+ if (options.variableReplacements.user)
697
+ warning('api-invalid-option', null, { '#': 'user' });
698
+ if (options.variableReplacements.locale)
699
+ warning('api-invalid-option', null, { '#': 'locale' });
700
+ }
701
+
702
+ forEachKey(options.variableReplacements || {}, (name) => {
703
+ if (!name.startsWith('$') && name !== 'user' && name !== 'locale')
704
+ warning('api-invalid-option', null, { '#': 'noDollar', name });
705
+ });
706
+ }
707
+
665
708
 
666
709
  /**
667
710
  * Option format used by the old API, where they are grouped thematically.
@@ -193,6 +193,14 @@ for (const oldName in oldMessageIds) {
193
193
 
194
194
  // For messageIds, where no text has been provided via code (central def)
195
195
  const centralMessageTexts = {
196
+ 'api-invalid-option': {
197
+ std: 'Option $(NAME) is deprecated! Use SNAPI options instead',
198
+ magicVars: 'Option “magicVars” is deprecated! Use “variableReplacements” instead. See <https://cap.cloud.sap/docs/guides/databases#configuring-variables> for details',
199
+ user: 'Option “variableReplacements” expects “$user” instead of “user”. See <https://cap.cloud.sap/docs/guides/databases#configuring-variables> for details',
200
+ locale: 'Option “variableReplacements” expects “$user.locale” instead of “locale”. See <https://cap.cloud.sap/docs/guides/databases#configuring-variables> for details',
201
+ 'noDollar': 'Option “variableReplacements” does not know $(NAME). Did you forget a leading “$”?'
202
+ },
203
+
196
204
  'anno-duplicate': 'Duplicate assignment with $(ANNO)',
197
205
  'anno-mismatched-ellipsis': 'An array with $(CODE) can only be used if there is an assignment below with an array value',
198
206
  'anno-unexpected-ellipsis': 'No base annotation available to apply $(CODE)',
@@ -109,6 +109,8 @@ function populate( model ) {
109
109
  function traverseElementEnvironments( art ) {
110
110
  populateView( art );
111
111
  environment( art );
112
+ if (art.elements$)
113
+ mergeSpecifiedElements(art);
112
114
  forEachMember( art, traverseElementEnvironments );
113
115
  }
114
116
 
@@ -425,8 +427,6 @@ function populate( model ) {
425
427
  setLink( view, '_status', '_query' );
426
428
  // must be run in order “sub query in FROM first”:
427
429
  traverseQueryPost( view.query, null, populateQuery );
428
- if (view.elements$) // specified elements
429
- mergeSpecifiedElements( view );
430
430
  if (!view.$entity) {
431
431
  model._entities.push( view );
432
432
  view.$entity = ++model.$entity;
@@ -435,14 +435,25 @@ function populate( model ) {
435
435
  }
436
436
  }
437
437
 
438
- function mergeSpecifiedElements( view ) {
438
+ /**
439
+ * Merge _specified_ elements with _inferred_ elements in the given view/element,
440
+ * where specified elements can appear through CSN.
441
+ *
442
+ * We only copy annotations, since they are not part of `columns`,
443
+ * but only appear in `elements` in CSN.
444
+ *
445
+ * This is important to ensure re-compilability.
446
+ *
447
+ * @param art
448
+ */
449
+ function mergeSpecifiedElements( art ) {
439
450
  // Later we use specified elements as proxies to inferred of leading query
440
451
  // (No, we probably do not.)
441
- for (const id in view.elements) {
442
- const ielem = view.elements[id]; // inferred element
443
- const selem = view.elements$[id]; // specified element
452
+ for (const id in art.elements) {
453
+ const ielem = art.elements[id]; // inferred element
454
+ const selem = art.elements$[id]; // specified element
444
455
  if (!selem) {
445
- info( 'query-missing-element', [ ielem.name.location, view ], { id },
456
+ info( 'query-missing-element', [ ielem.name.location, art ], { id },
446
457
  'Element $(ID) is missing in specified elements' );
447
458
  }
448
459
  else {
@@ -452,13 +463,20 @@ function populate( model ) {
452
463
  ielem[prop] = selem[prop];
453
464
  }
454
465
  selem.$replacement = true;
466
+ if (selem.elements) {
467
+ setLink(ielem, 'elements$', selem.elements);
468
+ delete selem.elements;
469
+ }
455
470
  }
456
471
  }
457
- for (const id in view.elements$) {
458
- const selem = view.elements$[id]; // specified element
459
- if (!selem.$replacement) {
460
- error( 'query-unspecified-element', [ selem.name.location, selem ], { id },
461
- 'Element $(ID) does not result from the query' );
472
+ // Without element expansion, we can't merge nested elements.
473
+ if (art.kind === 'entity' || enableExpandElements) {
474
+ for (const id in art.elements$) {
475
+ const selem = art.elements$[id]; // specified element
476
+ if (!selem.$replacement) {
477
+ error( 'query-unspecified-element', [ selem.name.location, selem ], { id },
478
+ 'Element $(ID) does not result from the query' );
479
+ }
462
480
  }
463
481
  }
464
482
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "2.15.4",
3
+ "version": "2.15.6",
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)",