@sap/cds-compiler 4.8.0 → 4.9.2

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 (95) hide show
  1. package/CHANGELOG.md +38 -4
  2. package/bin/cds_remove_invalid_whitespace.js +135 -0
  3. package/bin/cds_update_annotations.js +180 -0
  4. package/bin/cds_update_identifiers.js +3 -4
  5. package/bin/cdsc.js +30 -17
  6. package/doc/CHANGELOG_BETA.md +19 -0
  7. package/lib/api/main.js +59 -24
  8. package/lib/api/options.js +12 -1
  9. package/lib/api/validate.js +1 -5
  10. package/lib/base/builtins.js +27 -0
  11. package/lib/base/message-registry.js +38 -21
  12. package/lib/base/messages.js +51 -20
  13. package/lib/base/model.js +4 -5
  14. package/lib/checks/actionsFunctions.js +2 -2
  15. package/lib/checks/annotationsOData.js +3 -0
  16. package/lib/checks/defaultValues.js +5 -2
  17. package/lib/checks/queryNoDbArtifacts.js +3 -2
  18. package/lib/checks/validator.js +2 -34
  19. package/lib/compiler/assert-consistency.js +10 -3
  20. package/lib/compiler/checks.js +44 -18
  21. package/lib/compiler/define.js +38 -30
  22. package/lib/compiler/extend.js +33 -10
  23. package/lib/compiler/index.js +0 -1
  24. package/lib/compiler/lsp-api.js +5 -0
  25. package/lib/compiler/populate.js +0 -2
  26. package/lib/compiler/propagator.js +23 -19
  27. package/lib/compiler/resolve.js +48 -29
  28. package/lib/compiler/shared.js +60 -20
  29. package/lib/compiler/tweak-assocs.js +72 -116
  30. package/lib/compiler/xpr-rewrite.js +762 -0
  31. package/lib/edm/annotations/edmJson.js +24 -7
  32. package/lib/edm/annotations/genericTranslation.js +81 -61
  33. package/lib/edm/edm.js +4 -4
  34. package/lib/edm/edmInboundChecks.js +33 -0
  35. package/lib/edm/edmPreprocessor.js +9 -6
  36. package/lib/gen/Dictionary.json +129 -14
  37. package/lib/gen/language.checksum +1 -1
  38. package/lib/gen/language.interp +1 -1
  39. package/lib/gen/languageParser.js +1523 -1518
  40. package/lib/json/from-csn.js +13 -4
  41. package/lib/json/to-csn.js +12 -12
  42. package/lib/language/genericAntlrParser.js +14 -6
  43. package/lib/main.d.ts +67 -14
  44. package/lib/main.js +1 -0
  45. package/lib/model/cloneCsn.js +6 -3
  46. package/lib/model/csnRefs.js +23 -11
  47. package/lib/model/csnUtils.js +13 -7
  48. package/lib/model/enrichCsn.js +3 -1
  49. package/lib/model/revealInternalProperties.js +2 -1
  50. package/lib/model/sortViews.js +14 -6
  51. package/lib/modelCompare/compare.js +33 -34
  52. package/lib/optionProcessor.js +27 -2
  53. package/lib/render/DuplicateChecker.js +6 -6
  54. package/lib/render/manageConstraints.js +1 -0
  55. package/lib/render/toCdl.js +3 -1
  56. package/lib/transform/db/applyTransformations.js +33 -0
  57. package/lib/transform/db/constraints.js +75 -28
  58. package/lib/transform/db/expansion.js +8 -3
  59. package/lib/transform/db/flattening.js +2 -2
  60. package/lib/transform/db/groupByOrderBy.js +2 -2
  61. package/lib/transform/db/temporal.js +6 -3
  62. package/lib/transform/db/transformExists.js +2 -2
  63. package/lib/transform/effective/annotations.js +194 -0
  64. package/lib/transform/effective/main.js +6 -8
  65. package/lib/transform/effective/misc.js +31 -10
  66. package/lib/transform/forOdata.js +23 -7
  67. package/lib/transform/forRelationalDB.js +3 -3
  68. package/lib/transform/localized.js +7 -6
  69. package/lib/transform/odata/flattening.js +221 -124
  70. package/lib/transform/odata/toFinalBaseType.js +1 -1
  71. package/lib/transform/odata/typesExposure.js +15 -12
  72. package/lib/transform/parseExpr.js +4 -4
  73. package/lib/transform/transformUtils.js +47 -42
  74. package/lib/transform/translateAssocsToJoins.js +47 -47
  75. package/lib/transform/universalCsn/universalCsnEnricher.js +16 -19
  76. package/package.json +1 -1
  77. package/share/messages/anno-missing-rewrite.md +45 -0
  78. package/share/messages/message-explanations.json +1 -0
  79. package/bin/.eslintrc.json +0 -17
  80. package/lib/api/.eslintrc.json +0 -37
  81. package/lib/checks/.eslintrc.json +0 -31
  82. package/lib/compiler/.eslintrc.json +0 -8
  83. package/lib/edm/.eslintrc.json +0 -46
  84. package/lib/inspect/.eslintrc.json +0 -4
  85. package/lib/json/.eslintrc.json +0 -4
  86. package/lib/language/.eslintrc.json +0 -4
  87. package/lib/model/.eslintrc.json +0 -13
  88. package/lib/modelCompare/utils/.eslintrc.json +0 -22
  89. package/lib/render/.eslintrc.json +0 -22
  90. package/lib/transform/.eslintrc.json +0 -13
  91. package/lib/transform/db/.eslintrc.json +0 -41
  92. package/lib/transform/draft/.eslintrc.json +0 -4
  93. package/lib/transform/effective/.eslintrc.json +0 -4
  94. package/lib/transform/universalCsn/.eslintrc.json +0 -37
  95. package/lib/utils/.eslintrc.json +0 -7
package/CHANGELOG.md CHANGED
@@ -7,6 +7,42 @@
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.2 - 2024-05-13
11
+
12
+ ### Fixed
13
+
14
+ - compiler: Rewriting annotation expression paths in structures of projections has been improved.
15
+ - to.edm(x):
16
+ + Operator `/` represents `DivBy` operator, explicit `DivBy` is replaced with `Div` as integer division.
17
+ - to.sql: consider all associations in tenant dependent entity for referential constraint generation
18
+
19
+ ## Version 4.9.0 - 2024-04-25
20
+
21
+ ### Added
22
+
23
+ - compiler: Annotations with expressions are now rewritten when propagated.
24
+ - for.seal: Added API function that produces a CSN for SEAL.
25
+ - for.odata/to.edm(x): Support annotation path expressions including path flattening.
26
+
27
+ ### Changed
28
+
29
+ - parser: A space between `.` and `*`/`{` (nested projections) is now a warning.
30
+ Use `bin/cds_remove_invalid_whitespace.js` to automatically fix this warning.
31
+ - compiler:
32
+ + Published compositions with filters are changed to associations.
33
+ + Expressions as annotation values are rejected for few known annotations that don't expect them.
34
+ - Update OData vocabularies: 'Aggregation', 'Capabilities', 'Common', 'Hierarchy', 'PersonalData', 'Session', 'UI'.
35
+ - to.edm(x): Exposed anonymous parameter types are now prefixed with `ap`, `bap` and `ep` for actions, bound actions and entities.
36
+
37
+ ### Fixed
38
+
39
+ - compiler:
40
+ + Deprecated `$parameters` is no longer proposed in code completion.
41
+ + Duplicate mixin definitions lead to failing name resolution.
42
+ - to.cdl: Types were always rendered for associations with filters, even if it would lead to a compilation failure.
43
+ - to.edm(x):
44
+ + Fix a recursion bug in entity parameter handling.
45
+ + Fix event exclusion in service preprocessing.
10
46
 
11
47
  ## Version 4.8.0 - 2024-03-21
12
48
 
@@ -39,9 +75,6 @@ The compiler behavior concerning `beta` features can change at any time without
39
75
  + Turn types and aspects into dummies to reduce CSN size.
40
76
  + Correctly detect a removed `.default` and forcefully set the default to `null`.
41
77
 
42
- ### Removed
43
-
44
-
45
78
  ## Version 4.7.6 - 2024-02-29
46
79
 
47
80
  ### Fixed
@@ -83,7 +116,8 @@ The compiler behavior concerning `beta` features can change at any time without
83
116
  - compiler: published associations with filters sometimes had the filter applied twice
84
117
  if used in inline aspect compositions
85
118
  - to.sql|hdi|hdbcds[.migration]:
86
- + With `withHanaAssociations`: `false`, remove the association elements from the final CSN in order to correctly detect them during migration scenarios and
119
+ + With `withHanaAssociations`: `false`, remove the association elements from the final CSN in order
120
+ to correctly detect them during migration scenarios and
87
121
  with generated `hdbcds`.
88
122
  + Skip expensive processing (for calculated elements and nested projections) if the model doesn't use it.
89
123
  + Don't greedily set alias on subqueries if not required.
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Remove whitespace where the compiler does not expect it and will
4
+ // emit an error in the next major version.
5
+ //
6
+ // This script removes whitespace where necessary.
7
+ // It does not remove comments, even though they count as whitespace.
8
+ //
9
+ // Example CDS:
10
+ // entity P as projection on E { s. * };
11
+ // will become:
12
+ // entity P as projection on E { s.* };
13
+ //
14
+ // Usage:
15
+ // ./cds_remove_invalid_whitespace.js my_file.cds
16
+ //
17
+ // If you want to update all files in a directory, you can use
18
+ // this shell script:
19
+ // find . -type f -iname '*.cds' -exec ./cds_remove_invalid_whitespace.js {} \;
20
+ //
21
+ // Note that you need to update the path to this script in the commands above.
22
+ //
23
+
24
+ 'use strict';
25
+
26
+ const parseLanguage = require('../lib/language/antlrParser');
27
+ const { createMessageFunctions } = require('../lib/base/messages');
28
+
29
+ const fs = require('fs');
30
+ const path = require('path');
31
+
32
+ const cliArgs = process.argv.slice(2);
33
+ const filepath = cliArgs[0];
34
+
35
+ if (filepath === '--help' || filepath === '-h')
36
+ exitError();
37
+
38
+ if (cliArgs.length !== 1)
39
+ exitError(`Expected exactly one argument, ${cliArgs.length} given`);
40
+
41
+ if (!filepath)
42
+ exitError('Expected non-empty filepath as argument!');
43
+
44
+ // Do not use allow-list approach.
45
+ // There may be CDS files with other extensions than `.cds`.
46
+ if (filepath.endsWith('.csn') || filepath.endsWith('.json'))
47
+ exitError('Only CDS files can be passed! Found CSN file!');
48
+
49
+ const sourceStr = fs.readFileSync(filepath, 'utf-8');
50
+ const newSourceStr = modernizeWhitespace(sourceStr, filepath);
51
+ if (newSourceStr !== sourceStr)
52
+ fs.writeFileSync(filepath, newSourceStr);
53
+ process.exit(0); // success
54
+
55
+ // --------------------------------------------------------
56
+
57
+ function modernizeWhitespace( source, filename ) {
58
+ const options = { messages: [], attachTokens: true };
59
+ const messageFunctions = createMessageFunctions( options, 'parse', null );
60
+
61
+ // parseLanguage does not throw on CompilationError, so
62
+ // we do not need a try...catch block.
63
+ const ast = parseLanguage(source, filename, options, messageFunctions);
64
+
65
+ // To avoid spam, only report errors.
66
+ // Users should use the compiler to get all messages.
67
+ const errors = options.messages.filter(msg => (msg.severity === 'Error'));
68
+ if (errors.length > 0) {
69
+ errors.forEach((msg) => {
70
+ console.error(msg.toString());
71
+ });
72
+ console.error(`Found ${errors.length} errors! \n`);
73
+ exitError('The CDS parser emitted errors. Fix them first and try again.');
74
+ }
75
+
76
+ if (!ast.artifacts)
77
+ return source;
78
+
79
+ let currentOffset = 0;
80
+
81
+ const { tokens } = ast.tokenStream;
82
+ for (let i = 0; i < tokens.length; ++i) {
83
+ const token = tokens[i];
84
+ const next = tokens[i + 1];
85
+ if (next && token.type === ast.tokenStream.DOTbeforeBRACE &&
86
+ (next.type === ast.tokenStream.ASTERISK || next.type === ast.tokenStream.BRACE))
87
+ updateWhitespace(token, next);
88
+ }
89
+
90
+ return source;
91
+
92
+ // -----------------------------------------------
93
+
94
+ function updateWhitespace( dot, braceOrAsterisk ) {
95
+ if (dot.stop === braceOrAsterisk.start)
96
+ return;
97
+
98
+ const from = dot.stop + currentOffset + 1; // end points at the position *before* the character
99
+ const to = braceOrAsterisk.start + currentOffset;
100
+
101
+ source = replaceSliceInSource(source, from, to, '');
102
+
103
+ currentOffset -= to - from;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Replaces a given span with @p replaceWith
109
+ *
110
+ * @param {string} source
111
+ * @param {number} startIndex
112
+ * @param {number} endIndex
113
+ * @param {string} replaceWith
114
+ * @return {string}
115
+ */
116
+ function replaceSliceInSource( source, startIndex, endIndex, replaceWith ) {
117
+ return source.substring(0, startIndex) +
118
+ replaceWith +
119
+ source.substring(endIndex);
120
+ }
121
+
122
+ /**
123
+ * @param {string} [msg]
124
+ */
125
+ function exitError( msg ) {
126
+ if (msg)
127
+ console.error(msg);
128
+ usage();
129
+ process.exit(1);
130
+ }
131
+
132
+ function usage() {
133
+ console.error('');
134
+ console.error(`usage: ${path.basename(process.argv[1])} <filename>`);
135
+ }
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Update CDS Annotation References to use v4 expression syntax.
4
+ //
5
+ // This script replaces bare, unchecked references in annotation assignments
6
+ // with the new syntax using `(`.
7
+ //
8
+ // Example CDS:
9
+ // @anno: ID entity E { key ID : String; };
10
+ // will become:
11
+ // @anno: (ID) entity E { key ID : String; };
12
+ //
13
+ // Usage:
14
+ // ./cds_update_annotations.js my_file.cds
15
+ //
16
+ // If you want to update all identifiers in a directory, you can use
17
+ // this Shell script:
18
+ // find . -type f -iname '*.cds' -exec ./cds_update_annotations.js {} \;
19
+ //
20
+ // Note that you need to update the path to this script in the commands above.
21
+ //
22
+
23
+ 'use strict';
24
+
25
+ const parseLanguage = require('../lib/language/antlrParser');
26
+ const { createMessageFunctions } = require('../lib/base/messages');
27
+
28
+ const fs = require('fs');
29
+ const path = require('path');
30
+ const { forEachMemberRecursively } = require('../lib/base/model');
31
+
32
+ const cliArgs = process.argv.slice(2);
33
+ const filepath = cliArgs[0];
34
+
35
+ if (filepath === '--help' || filepath === '-h')
36
+ exitError();
37
+
38
+ if (cliArgs.length !== 1)
39
+ exitError(`Expected exactly one argument, ${cliArgs.length} given`);
40
+
41
+ if (!filepath)
42
+ exitError('Expected non-empty filepath as argument!');
43
+
44
+ // Do not use allow-list approach.
45
+ // There may be CDS files with other extensions than `.cds`.
46
+ if (filepath.endsWith('.csn') || filepath.endsWith('.json'))
47
+ exitError('Only CDS files can be passed! Found CSN file!');
48
+
49
+ const sourceStr = fs.readFileSync(filepath, 'utf-8');
50
+ const newSourceStr = modernizeAnnotationExpressions(sourceStr, filepath);
51
+ if (newSourceStr !== sourceStr)
52
+ fs.writeFileSync(filepath, newSourceStr);
53
+ process.exit(0); // success
54
+
55
+ // --------------------------------------------------------
56
+
57
+ function modernizeAnnotationExpressions( source, filename ) {
58
+ const options = { messages: [], attachTokens: true };
59
+ const messageFunctions = createMessageFunctions( options, 'parse', null );
60
+
61
+ // parseLanguage does not throw on CompilationError, so
62
+ // we do not need a try...catch block.
63
+ const ast = parseLanguage(source, filename, options, messageFunctions);
64
+
65
+ // To avoid spam, only report errors.
66
+ // Users should use the compiler to get all messages.
67
+ const errors = options.messages.filter(msg => (msg.severity === 'Error'));
68
+ if (errors.length > 0) {
69
+ errors.forEach((msg) => {
70
+ console.error(msg.toString());
71
+ });
72
+ console.error(`Found ${errors.length} errors! \n`);
73
+ exitError('The parser emitted errors. Please fix them first and try again.');
74
+ }
75
+
76
+ if (!ast.artifacts)
77
+ return source;
78
+
79
+ let currentOffset = 0;
80
+
81
+ for (const artName in ast.artifacts) {
82
+ const art = ast.artifacts[artName];
83
+ updateAnnosOfArtifact(art);
84
+ forEachMemberRecursively(art, updateAnnosOfArtifact);
85
+ }
86
+ for (const vocName in ast.vocabularies) {
87
+ const voc = ast.vocabularies[vocName];
88
+ updateAnnosOfArtifact(voc);
89
+ forEachMemberRecursively(voc, updateAnnosOfArtifact);
90
+ }
91
+ for (const ext of ast.extensions)
92
+ updateAnnosOfArtifact(ext);
93
+
94
+ return source;
95
+
96
+ // -----------------------------------------------
97
+
98
+ function updateAnnosOfArtifact( art ) {
99
+ for (const prop in art) {
100
+ if (prop.charAt(0) === '@')
101
+ updateAnno(art[prop]);
102
+ }
103
+ }
104
+
105
+ function updateAnno( anno ) {
106
+ if (anno.literal === 'array') {
107
+ anno.val.forEach(val => updateAnno( val ));
108
+ }
109
+ else if (anno.literal === 'struct') {
110
+ const struct = Object.values(anno.struct);
111
+ struct.forEach(val => updateAnno( val ));
112
+ }
113
+ else if (!anno.$tokenTexts && anno.path) {
114
+ const first = anno.path[0];
115
+ const last = anno.path[anno.path.length - 1];
116
+ insertParentheses(first, last);
117
+ }
118
+ }
119
+
120
+ function findTokenFor( node ) {
121
+ const loc = node.location;
122
+ // We use slow linear search; we could do binary search, but not performance critical.
123
+ const { tokens } = ast.tokenStream;
124
+ for (const token of tokens) {
125
+ if (token.column + 1 === loc.col && token.line === loc.line)
126
+ return token;
127
+ }
128
+ return null;
129
+ }
130
+
131
+ function insertParentheses( first, last ) {
132
+ const firstToken = findTokenFor(first);
133
+ const lastToken = findTokenFor(last);
134
+
135
+ if (!firstToken?.stop || !lastToken?.stop)
136
+ throw new Error(`INTERNAL ERROR: Identifier at ${firstToken?.start} or ${lastToken?.start} has no end! (${filepath})`);
137
+
138
+ const start = firstToken.start + currentOffset;
139
+ const end = lastToken.stop + currentOffset + 1; // end points at the position *before* the character
140
+
141
+ const original = source.substring(start, end);
142
+ if (original.includes('@'))
143
+ return; // we ignore annotation references for now
144
+
145
+ const newSource = `( ${original} )`;
146
+ source = replaceSliceInSource(source, start, end, newSource);
147
+
148
+ currentOffset += (newSource.length - original.length);
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Replaces a given span with @p replaceWith
154
+ *
155
+ * @param {string} source
156
+ * @param {number} startIndex
157
+ * @param {number} endIndex
158
+ * @param {string} replaceWith
159
+ * @return {string}
160
+ */
161
+ function replaceSliceInSource( source, startIndex, endIndex, replaceWith ) {
162
+ return source.substring(0, startIndex) +
163
+ replaceWith +
164
+ source.substring(endIndex);
165
+ }
166
+
167
+ /**
168
+ * @param {string} [msg]
169
+ */
170
+ function exitError( msg ) {
171
+ if (msg)
172
+ console.error(msg);
173
+ usage();
174
+ process.exit(1);
175
+ }
176
+
177
+ function usage() {
178
+ console.error('');
179
+ console.error(`usage: ${path.basename(process.argv[1])} <filename>`);
180
+ }
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- //
4
3
  // Update CDS Delimited Identifiers
5
4
  //
6
5
  // This script replaces the old delimited identifier style with the new one
@@ -12,11 +11,11 @@
12
11
  // entity ![My Entity] { ... }
13
12
  //
14
13
  // Usage:
15
- // cds_update_identifiers.js my_file.cds
14
+ // ./cds_update_identifiers.js my_file.cds
16
15
  //
17
16
  // If you want to update all identifiers in a directory, you can use
18
17
  // this Shell script:
19
- // find . -type f -iname '*.cds' -exec cds_update_identifiers.js {} \;
18
+ // find . -type f -iname '*.cds' -exec ./cds_update_identifiers.js {} \;
20
19
  //
21
20
  // Note that you need to update the path to this script in the commands above.
22
21
  //
@@ -70,7 +69,7 @@ function modernizeIdentifierStyle( source, filename ) {
70
69
  console.error(msg.toString());
71
70
  });
72
71
  console.error(`Found ${errors.length} errors! \n`);
73
- exitError('The parser emitted errors. Please fix them first and try again.');
72
+ exitError('The CDS parser emitted errors. Fix them first and try again.');
74
73
  }
75
74
 
76
75
  let currentOffset = 0;
package/bin/cdsc.js CHANGED
@@ -278,6 +278,7 @@ function executeCommandLine( command, options, args ) {
278
278
  toSql,
279
279
  inspect,
280
280
  toEffectiveCsn,
281
+ forSeal,
281
282
  };
282
283
  const commandsWithoutCompilation = {
283
284
  explain,
@@ -318,7 +319,7 @@ function executeCommandLine( command, options, args ) {
318
319
  }
319
320
 
320
321
  function toEffectiveCsn( model ) {
321
- const features = [ 'resolveSimpleTypes', 'resolveProjections' ];
322
+ const features = [ 'resolveSimpleTypes', 'resolveProjections', 'remapOdataAnnotations', 'keepLocalized' ];
322
323
  for (const feature of features) {
323
324
  if (options[feature]) // map to boolean equivalent
324
325
  options[feature] = options[feature] === 'true';
@@ -329,6 +330,18 @@ function executeCommandLine( command, options, args ) {
329
330
  return model;
330
331
  }
331
332
 
333
+ function forSeal( model ) {
334
+ const features = [ 'remapOdataAnnotations' ];
335
+ for (const feature of features) {
336
+ if (options[feature]) // map to boolean equivalent
337
+ options[feature] = options[feature] === 'true';
338
+ }
339
+ const csn = options.directBackend ? model : compactModel(model, options);
340
+ displayNamedCsn(main.for.seal(csn, options), 'seal');
341
+
342
+ return model;
343
+ }
344
+
332
345
  // Execute the command line option 'toCsn' and display the results.
333
346
  // Return the original model (for chaining)
334
347
  function toCsn( model ) {
@@ -565,22 +578,12 @@ function executeCommandLine( command, options, args ) {
565
578
  // Depending on 'options.rawOutput', the model is either compacted to 'name.json' or
566
579
  // written in raw form to '<name>_raw.txt'.
567
580
  function displayNamedXsn( xsn, name ) {
568
- if (options.rawOutput) {
581
+ if (options.rawOutput)
569
582
  writeToFileOrDisplay(options.out, `${name}_raw.txt`, util.inspect(reveal(xsn, options.rawOutput), false, null), true);
570
- }
571
- else if (options.internalMsg) {
583
+ else if (options.internalMsg)
572
584
  writeToFileOrDisplay(options.out, `${name}_raw.txt`, util.inspect(reveal(xsn).messages, { depth: null, maxArrayLength: null }), true);
573
- }
574
- else if (!options.parseOnly) { // no output if parseOnly but not rawOutput
575
- const csn = compactModel(xsn, options);
576
- if (command === 'toCsn' && options.tenantDiscriminator)
577
- addTenantFields(csn, options);
578
- if (command === 'toCsn' && options.withLocalized)
579
- addLocalizationViews(csn, options);
580
- if (options.enrichCsn)
581
- enrichCsn( csn, options );
582
- writeToFileOrDisplay(options.out, `${name}.json`, csn, true);
583
- }
585
+ else if (!options.parseOnly) // no output if parseOnly but not rawOutput
586
+ displayNamedCsn(compactModel(xsn, options), name);
584
587
  }
585
588
 
586
589
  /**
@@ -590,12 +593,22 @@ function executeCommandLine( command, options, args ) {
590
593
  function displayNamedCsn( csn, name ) {
591
594
  if (!csn) // only print CSN if it is set.
592
595
  return;
596
+
597
+ if (command === 'toCsn' ) {
598
+ // If requested, run some CSN postprocessing.
599
+ if (options.tenantDiscriminator)
600
+ addTenantFields(csn, options); // always _before_ localized convenience views are added
601
+ if (options.withLocalized)
602
+ addLocalizationViews(csn, options);
603
+ }
604
+
605
+ if (options.enrichCsn)
606
+ enrichCsn( csn, options );
607
+
593
608
  if (options.internalMsg) {
594
609
  writeToFileOrDisplay(options.out, `${name}_raw.txt`, options.messages, true);
595
610
  }
596
611
  else if (!options.internalMsg) {
597
- if (command === 'toCsn' && options.withLocalized)
598
- addLocalizationViews(csn, options);
599
612
  writeToFileOrDisplay(options.out, `${name}.json`, csn, true);
600
613
  }
601
614
  }
@@ -8,6 +8,25 @@ Note: `beta` fixes, changes and features are listed in this ChangeLog just for i
8
8
  The compiler behavior concerning `beta` features can change at any time without notice.
9
9
  **Don't use `beta` fixes, changes and features in productive mode.**
10
10
 
11
+ ## Version 4.9.0 - 2024-04-25
12
+
13
+ ## Removed `odataAnnotationExpressions`
14
+
15
+ It is now enabled by default.
16
+
17
+ ## Removed `odataPathsInAnnotationExpressions`
18
+
19
+ It is now enabled by default.
20
+
21
+ ## Removed `annotationExpressions`
22
+
23
+ It is now enabled by default.
24
+
25
+ ## Added `temporalRawProjection`
26
+
27
+ Enables revocation of temporal where clause in projections of temporal entities if the
28
+ `@cds.valid { from, to }` annotations on the projection elements have falsy values.
29
+
11
30
  ## Version 4.8.0 - 2024-03-21
12
31
 
13
32
  ### Removed `vectorType`