@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 +6 -0
- package/lib/api/main.js +44 -1
- package/lib/base/message-registry.js +8 -0
- package/lib/compiler/populate.js +30 -12
- package/package.json +1 -1
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)',
|
package/lib/compiler/populate.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
442
|
-
const ielem =
|
|
443
|
-
const selem =
|
|
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,
|
|
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
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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
|
}
|