@sap/cds-compiler 4.4.4 → 4.6.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 (82) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/bin/cdsc.js +18 -11
  3. package/bin/cdsv2m.js +7 -5
  4. package/doc/CHANGELOG_BETA.md +22 -0
  5. package/lib/api/main.js +306 -144
  6. package/lib/api/options.js +18 -6
  7. package/lib/api/validate.js +1 -1
  8. package/lib/base/message-registry.js +45 -10
  9. package/lib/base/messages.js +33 -16
  10. package/lib/base/model.js +4 -0
  11. package/lib/base/optionProcessorHelper.js +45 -176
  12. package/lib/checks/annotationsOData.js +49 -0
  13. package/lib/checks/elements.js +32 -34
  14. package/lib/checks/enricher.js +39 -3
  15. package/lib/checks/validator.js +8 -7
  16. package/lib/compiler/assert-consistency.js +40 -17
  17. package/lib/compiler/builtins.js +30 -53
  18. package/lib/compiler/checks.js +46 -14
  19. package/lib/compiler/cycle-detector.js +1 -4
  20. package/lib/compiler/define.js +35 -10
  21. package/lib/compiler/extend.js +21 -7
  22. package/lib/compiler/generate.js +3 -0
  23. package/lib/compiler/populate.js +5 -1
  24. package/lib/compiler/propagator.js +46 -9
  25. package/lib/compiler/resolve.js +94 -35
  26. package/lib/compiler/shared.js +60 -33
  27. package/lib/compiler/tweak-assocs.js +188 -92
  28. package/lib/compiler/utils.js +11 -1
  29. package/lib/edm/annotations/edmJson.js +41 -66
  30. package/lib/edm/annotations/genericTranslation.js +27 -9
  31. package/lib/edm/annotations/preprocessAnnotations.js +2 -3
  32. package/lib/edm/csn2edm.js +28 -11
  33. package/lib/edm/edmInboundChecks.js +58 -15
  34. package/lib/edm/edmPreprocessor.js +12 -16
  35. package/lib/edm/edmUtils.js +5 -2
  36. package/lib/gen/Dictionary.json +10 -0
  37. package/lib/gen/language.checksum +1 -1
  38. package/lib/gen/language.interp +15 -2
  39. package/lib/gen/language.tokens +1 -0
  40. package/lib/gen/languageParser.js +6557 -5618
  41. package/lib/json/from-csn.js +4 -5
  42. package/lib/json/to-csn.js +29 -4
  43. package/lib/language/antlrParser.js +19 -1
  44. package/lib/language/errorStrategy.js +28 -7
  45. package/lib/language/genericAntlrParser.js +118 -24
  46. package/lib/language/textUtils.js +16 -0
  47. package/lib/main.d.ts +28 -3
  48. package/lib/main.js +3 -0
  49. package/lib/model/csnRefs.js +4 -1
  50. package/lib/model/csnUtils.js +20 -14
  51. package/lib/model/revealInternalProperties.js +5 -2
  52. package/lib/optionProcessor.js +23 -22
  53. package/lib/render/manageConstraints.js +13 -29
  54. package/lib/render/toCdl.js +47 -26
  55. package/lib/render/toHdbcds.js +63 -42
  56. package/lib/render/toRename.js +6 -10
  57. package/lib/render/toSql.js +71 -117
  58. package/lib/render/utils/common.js +41 -6
  59. package/lib/transform/.eslintrc.json +9 -1
  60. package/lib/transform/addTenantFields.js +228 -0
  61. package/lib/transform/db/applyTransformations.js +57 -4
  62. package/lib/transform/db/assertUnique.js +4 -4
  63. package/lib/transform/db/backlinks.js +13 -1
  64. package/lib/transform/db/cdsPersistence.js +1 -1
  65. package/lib/transform/db/expansion.js +24 -3
  66. package/lib/transform/db/flattening.js +70 -71
  67. package/lib/transform/db/killAnnotations.js +37 -0
  68. package/lib/transform/db/rewriteCalculatedElements.js +46 -6
  69. package/lib/transform/db/temporal.js +1 -1
  70. package/lib/transform/draft/db.js +2 -16
  71. package/lib/transform/draft/odata.js +3 -3
  72. package/lib/transform/effective/associations.js +3 -5
  73. package/lib/transform/effective/main.js +6 -9
  74. package/lib/transform/forOdata.js +26 -55
  75. package/lib/transform/forRelationalDB.js +38 -18
  76. package/lib/transform/odata/toFinalBaseType.js +3 -3
  77. package/lib/transform/odata/typesExposure.js +14 -5
  78. package/lib/transform/transformUtils.js +47 -34
  79. package/lib/transform/translateAssocsToJoins.js +45 -11
  80. package/lib/transform/universalCsn/coreComputed.js +1 -1
  81. package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
  82. package/package.json +7 -6
package/lib/api/main.js CHANGED
@@ -19,6 +19,14 @@ const timetrace = lazyload('../utils/timetrace');
19
19
  const forRelationalDB = lazyload('../transform/forRelationalDB');
20
20
  const sqlUtils = lazyload('../render/utils/sql');
21
21
  const effective = lazyload('../transform/effective/main');
22
+ const toHdbcds = lazyload('../render/toHdbcds');
23
+ const baseError = lazyload('../base/error');
24
+ const csnToEdm = lazyload('../edm/csn2edm');
25
+ const trace = lazyload('./trace');
26
+
27
+ const { forEach, forEachKey } = require('../utils/objectUtils');
28
+ const { checkRemovedDeprecatedFlags } = require('../base/model');
29
+ const { makeMessageFunction } = require('../base/messages');
22
30
 
23
31
  /**
24
32
  * Return the artifact name for use for the hdbresult object
@@ -32,13 +40,6 @@ function getFileName( artifactName, csn ) {
32
40
  return csnUtils.getResultingName(csn, 'quoted', artifactName);
33
41
  }
34
42
 
35
- const { toHdbcdsSource } = require('../render/toHdbcds');
36
- const { ModelError } = require('../base/error');
37
- const { forEach, forEachKey } = require('../utils/objectUtils');
38
- const { checkRemovedDeprecatedFlags } = require('../base/model');
39
- const { csn2edm, csn2edmAll } = require('../edm/csn2edm');
40
- const { traceApi } = require('./trace');
41
-
42
43
  const relevantGeneralOptions = [ /* for future generic options */ ];
43
44
  const relevantOdataOptions = [ 'sqlMapping', 'odataFormat' ];
44
45
  const warnAboutMismatchOdata = [ 'odataVersion' ];
@@ -50,7 +51,7 @@ const warnAboutMismatchOdata = [ 'odataVersion' ];
50
51
  * @param {string} transformation Name of the transformation - odata or hana
51
52
  * @param {NestedOptions} options Options used for the transformation
52
53
  * @param {string[]} relevantOptionNames Option names that are defining characteristics
53
- * @param {string[]} [optionalOptionNames=[]] Option names that should be attached as a fyi
54
+ * @param {string[]} [optionalOptionNames] Option names that should be attached as a fyi
54
55
  */
55
56
  function attachTransformerCharacteristics( csn, transformation, options,
56
57
  relevantOptionNames, optionalOptionNames = [] ) {
@@ -86,17 +87,19 @@ function attachTransformerCharacteristics( csn, transformation, options,
86
87
  * @param {NestedOptions} options Options used for the transformation - scanned top-level
87
88
  * @param {string[]} relevantOptionNames Option names that are defining characteristics
88
89
  * @param {string[]} warnAboutMismatch Option names to warn about, but not error on
89
- * @param {string} module Name of the module that calls this function, e.g. `for.odata`
90
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`,
90
91
  */
91
- function checkPreTransformedCsn( csn, options, relevantOptionNames, warnAboutMismatch, module ) {
92
- if (!csn.meta) {
92
+ function checkPreTransformedCsn( csn, options,
93
+ relevantOptionNames, warnAboutMismatch,
94
+ messageFunctions ) {
95
+ if (!csn.meta?.options) {
93
96
  // Not able to check
94
97
  return;
95
98
  }
96
- const { error, warning, throwWithAnyError } = messages.makeMessageFunction(csn, options, module);
99
+ const { error, warning, throwWithAnyError } = messageFunctions;
97
100
 
98
101
  for (const name of relevantOptionNames ) {
99
- if (options[name] !== csn.meta.options?.[name]) {
102
+ if (options[name] !== csn.meta.options[name]) {
100
103
  error('api-invalid-option-preprocessed', null, { prop: name, value: options[name], othervalue: csn.meta.options[name] },
101
104
  'Expected pre-processed CSN to have option $(PROP) set to $(VALUE). Found: $(OTHERVALUE)');
102
105
  }
@@ -130,11 +133,13 @@ function isPreTransformed( csn, transformation ) {
130
133
  *
131
134
  * @param {CSN.Model} csn Clean input CSN
132
135
  * @param {object} internalOptions processed options
136
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
133
137
  * @returns {object} Return an oData-pre-processed CSN
134
138
  */
135
- function odataInternal( csn, internalOptions ) {
139
+ function odataInternal( csn, internalOptions, messageFunctions ) {
136
140
  internalOptions.transformation = 'odata';
137
- const oDataCsn = forOdataNew.transform4odataWithCsn(csn, internalOptions);
141
+ const oDataCsn = forOdataNew.transform4odataWithCsn(csn, internalOptions, messageFunctions);
142
+ messageFunctions.setModel(oDataCsn);
138
143
  attachTransformerCharacteristics(oDataCsn, 'odata', internalOptions, relevantOdataOptions, warnAboutMismatchOdata);
139
144
  return oDataCsn;
140
145
  }
@@ -143,26 +148,28 @@ function odataInternal( csn, internalOptions ) {
143
148
  * Return a odata-transformed CSN
144
149
  *
145
150
  * @param {CSN.Model} csn Clean input CSN
146
- * @param {ODataOptions} [options={}] Options
151
+ * @param {ODataOptions} options Options
152
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
147
153
  * @returns {oDataCSN} Return an oData-pre-processed CSN
148
154
  */
149
- function odata( csn, options = {} ) {
150
- traceApi('for.odata', options);
155
+ function odata( csn, options, messageFunctions ) {
156
+ trace.traceApi('for.odata', options);
151
157
  const internalOptions = prepareOptions.for.odata(options);
152
- return odataInternal(csn, internalOptions);
158
+ return odataInternal(csn, internalOptions, messageFunctions);
153
159
  }
154
160
 
155
161
  /**
156
162
  * Process the given csn back to cdl.
157
163
  *
158
164
  * @param {object} csn CSN to process
159
- * @param {object} [options={}] Options
165
+ * @param {object} options Options
166
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
160
167
  * @returns {object} { model: string, namespace: string }
161
168
  */
162
- function cdl( csn, options = {} ) {
163
- traceApi('to.cdl', options);
169
+ function cdl( csn, options, messageFunctions ) {
170
+ trace.traceApi('to.cdl', options);
164
171
  const internalOptions = prepareOptions.to.cdl(options);
165
- return toCdl.csnToCdl(csn, internalOptions);
172
+ return toCdl.csnToCdl(csn, internalOptions, messageFunctions);
166
173
  }
167
174
 
168
175
  /**
@@ -171,13 +178,15 @@ function cdl( csn, options = {} ) {
171
178
  *
172
179
  * @param {CSN.Model} csn Plain input CSN
173
180
  * @param {SqlOptions} internalOptions Options
181
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
174
182
  * @returns {CSN.Model} CSN transformed like to.sql
175
183
  * @private
176
184
  */
177
- function csnForSql( csn, internalOptions ) {
185
+ function csnForSql( csn, internalOptions, messageFunctions ) {
178
186
  internalOptions.transformation = 'sql';
179
- const transformedCsn = forRelationalDB.transformForRelationalDBWithCsn(csn, internalOptions, 'to.sql');
180
-
187
+ const transformedCsn = forRelationalDB.transformForRelationalDBWithCsn(
188
+ csn, internalOptions, messageFunctions
189
+ );
181
190
  return internalOptions.testMode ? toCsn.sortCsn(transformedCsn, internalOptions) : transformedCsn;
182
191
  }
183
192
 
@@ -186,44 +195,48 @@ function csnForSql( csn, internalOptions ) {
186
195
  * Pseudo-public version of csnForSql().
187
196
  *
188
197
  * @param {CSN.Model} csn Plain input CSN
189
- * @param {SqlOptions} [options={}] Options
198
+ * @param {SqlOptions} options Options
199
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
190
200
  * @returns {CSN.Model} CSN transformed like to.sql
191
201
  * @private
192
202
  */
193
- function forSql( csn, options = {} ) {
203
+ function forSql( csn, options, messageFunctions ) {
194
204
  const internalOptions = prepareOptions.to.sql(options);
195
- return csnForSql(csn, internalOptions);
205
+ return csnForSql(csn, internalOptions, messageFunctions);
196
206
  }
197
207
 
198
208
  /**
199
209
  * Transform a CSN like to.hdi
200
210
  *
201
211
  * @param {CSN.Model} csn Plain input CSN
202
- * @param {HdiOptions} [options={}] Options
212
+ * @param {HdiOptions} options Options
213
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
203
214
  * @returns {CSN.Model} CSN transformed like to.hdi
204
215
  * @private
205
216
  */
206
- function forHdi( csn, options = {} ) {
217
+ function forHdi( csn, options, messageFunctions ) {
207
218
  const internalOptions = prepareOptions.to.hdi(options);
208
219
  internalOptions.transformation = 'sql';
209
- const transformedCsn = forRelationalDB.transformForRelationalDBWithCsn(csn, internalOptions, 'to.hdi');
210
-
220
+ const transformedCsn = forRelationalDB.transformForRelationalDBWithCsn(
221
+ csn, internalOptions, messageFunctions
222
+ );
211
223
  return internalOptions.testMode ? toCsn.sortCsn(transformedCsn, internalOptions) : transformedCsn;
212
224
  }
213
225
  /**
214
226
  * Transform a CSN like to.hdbcds
215
227
  *
216
228
  * @param {CSN.Model} csn Plain input CSN
217
- * @param {HdbcdsOptions} [options={}] Options
229
+ * @param {HdbcdsOptions} options Options
230
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
218
231
  * @returns {CSN.Model} CSN transformed like to.hdbcds
219
232
  * @private
220
233
  */
221
- function forHdbcds( csn, options = {} ) {
234
+ function forHdbcds( csn, options, messageFunctions ) {
222
235
  const internalOptions = prepareOptions.to.hdbcds(options);
223
236
  internalOptions.transformation = 'hdbcds';
224
-
225
- const hanaCsn = forRelationalDB.transformForRelationalDBWithCsn(csn, internalOptions, 'to.hdbcds');
226
-
237
+ const hanaCsn = forRelationalDB.transformForRelationalDBWithCsn(
238
+ csn, internalOptions, messageFunctions
239
+ );
227
240
  return internalOptions.testMode ? toCsn.sortCsn(hanaCsn, internalOptions) : hanaCsn;
228
241
  }
229
242
 
@@ -231,16 +244,16 @@ function forHdbcds( csn, options = {} ) {
231
244
  * Effective CSN transformation
232
245
  *
233
246
  * @param {CSN.Model} csn Plain input CSN
234
- * @param {EffectiveCsnOptions} [options={}] Options
247
+ * @param {EffectiveCsnOptions} options Options
248
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
235
249
  * @returns {CSN.Model} CSN transformed
236
250
  * @private
237
251
  */
238
- function forEffective( csn, options = {} ) {
252
+ function forEffective( csn, options, messageFunctions ) {
239
253
  const internalOptions = prepareOptions.to.sql(options);
240
254
  internalOptions.transformation = 'effective';
241
255
 
242
- const eCsn = effective.effectiveCsn(csn, internalOptions);
243
-
256
+ const eCsn = effective.effectiveCsn(csn, internalOptions, messageFunctions);
244
257
  return internalOptions.testMode ? toCsn.sortCsn(eCsn, internalOptions) : eCsn;
245
258
  }
246
259
 
@@ -248,17 +261,19 @@ function forEffective( csn, options = {} ) {
248
261
  * Process the given CSN into SQL.
249
262
  *
250
263
  * @param {CSN.Model} csn A clean input CSN
251
- * @param {SqlOptions} [options={}] Options
264
+ * @param {SqlOptions} options Options
265
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
252
266
  * @returns {SQL[]} Array of SQL statements, tables first, views second
253
267
  */
254
- function sql( csn, options = {} ) {
255
- traceApi('to.sql', options);
268
+ function sql( csn, options, messageFunctions ) {
269
+ trace.traceApi('to.sql', options);
256
270
  const internalOptions = prepareOptions.to.sql(options);
257
271
  internalOptions.transformation = 'sql';
258
272
 
259
273
  // we need the CSN for view sorting
260
- const transformedCsn = csnForSql(csn, internalOptions);
261
- const sqls = toSql.toSqlDdl(transformedCsn, internalOptions);
274
+ const transformedCsn = csnForSql(csn, internalOptions, messageFunctions);
275
+ messageFunctions.setModel(transformedCsn);
276
+ const sqls = toSql.toSqlDdl(transformedCsn, internalOptions, messageFunctions);
262
277
 
263
278
  const result = sortViews({ csn: transformedCsn, sql: sqls.sql });
264
279
  return [
@@ -271,16 +286,18 @@ function sql( csn, options = {} ) {
271
286
  * Process the given CSN into HDI artifacts.
272
287
  *
273
288
  * @param {CSN.Model} csn A clean input CSN
274
- * @param {HdiOptions} [options={}] Options
289
+ * @param {HdiOptions} options Options
290
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
275
291
  * @returns {HDIArtifacts} { <filename>:<content>, ...}
276
292
  */
277
- function hdi( csn, options = {} ) {
278
- traceApi('to.hdi', options);
293
+ function hdi( csn, options, messageFunctions ) {
294
+ trace.traceApi('to.hdi', options);
279
295
  const internalOptions = prepareOptions.to.hdi(options);
280
296
 
281
297
  // we need the CSN for view sorting
282
- const sqlCSN = forHdi(csn, options);
283
- const sqls = toSql.toSqlDdl(sqlCSN, internalOptions);
298
+ const sqlCSN = forHdi(csn, options, messageFunctions);
299
+ messageFunctions.setModel(sqlCSN);
300
+ const sqls = toSql.toSqlDdl(sqlCSN, internalOptions, messageFunctions);
284
301
 
285
302
  if (internalOptions.testMode) {
286
303
  // All this mapping is needed because sortViews crossmatches
@@ -370,6 +387,7 @@ function remapName( key, csn, filter = () => true ) {
370
387
  *
371
388
  * @param {CSN.Model} csn A clean input CSN representing the desired "after-image"
372
389
  * @param {HdiOptions} options Options
390
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
373
391
  * @param {CSN.Model} beforeImage A db-transformed CSN representing the "before-image", or null in case no such image
374
392
  * is known, i.e. for the very first migration step.
375
393
  * @returns {object} An object with three properties:
@@ -377,27 +395,23 @@ function remapName( key, csn, filter = () => true ) {
377
395
  * - drops: An array of SQL statements to drop views/tables
378
396
  * - createsAndAlters: An array of SQL statements to ALTER/CREATE tables/views
379
397
  */
380
- function sqlMigration( csn, options, beforeImage ) {
381
- traceApi('to.sql.migration', options);
398
+ function sqlMigration( csn, options, messageFunctions, beforeImage ) {
399
+ trace.traceApi('to.sql.migration', options);
382
400
  const internalOptions = prepareOptions.to.sql(options);
383
- const {
384
- error, warning, info, throwWithError, message,
385
- } = messages.makeMessageFunction(csn, internalOptions, 'to.sql.migration');
401
+ const { error, throwWithError } = messageFunctions;
386
402
 
387
403
  // Prepare after-image.
388
- const afterImage = internalOptions.filterCsn
389
- ? diffFilter.csn(csnForSql(csn, internalOptions)) : csnForSql(csn, internalOptions);
404
+ let afterImage = csnForSql(csn, internalOptions, messageFunctions);
405
+ if (internalOptions.filterCsn)
406
+ afterImage = diffFilter.csn(afterImage);
390
407
  // Compare both images.
391
408
  const diff = modelCompare.compareModels(beforeImage || afterImage, afterImage, internalOptions);
409
+ messageFunctions.setModel(diff);
392
410
  const diffFilterObj = diffFilter[internalOptions.sqlDialect];
393
411
 
394
412
  if (diffFilterObj) {
395
- diff.extensions = diff.extensions.filter(ex => diffFilterObj.extension(ex, {
396
- error, warning, info, throwWithError, message,
397
- }));
398
- diff.migrations.forEach(migration => diffFilterObj.migration(migration, {
399
- error, warning, info, throwWithError, message,
400
- }));
413
+ diff.extensions = diff.extensions.filter(ex => diffFilterObj.extension(ex, messageFunctions));
414
+ diff.migrations.forEach(migration => diffFilterObj.migration(migration, messageFunctions));
401
415
  Object.entries(diff.deletions).forEach(entry => diffFilterObj.deletion(entry, error));
402
416
  diff.changedPrimaryKeys = diff.changedPrimaryKeys
403
417
  .filter(an => diffFilterObj.changedPrimaryKeys(an));
@@ -475,7 +489,7 @@ function sqlMigration( csn, options, beforeImage ) {
475
489
  const {
476
490
  // eslint-disable-next-line no-unused-vars
477
491
  deletions, constraintDeletions, migrations, constraints, ...hdbkinds
478
- } = toSql.toSqlDdl(diff, internalOptions);
492
+ } = toSql.toSqlDdl(diff, internalOptions, messageFunctions);
479
493
 
480
494
  cleanup.forEach(fn => fn());
481
495
  // TODO: Handle `ADD CONSTRAINT` etc!
@@ -528,18 +542,20 @@ function sqlMigration( csn, options, beforeImage ) {
528
542
  *
529
543
  * @param {CSN.Model} csn A clean input CSN representing the desired "after-image"
530
544
  * @param {HdiOptions} options Options
545
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
531
546
  * @param {CSN.Model} beforeImage A HANA-transformed CSN representing the "before-image", or null in case no such image
532
547
  * is known, i.e. for the very first migration step
533
548
  * @returns {migration} The migration result
534
549
  */
535
- function hdiMigration( csn, options, beforeImage ) {
536
- traceApi('to.hdi.migration', options);
550
+ function hdiMigration( csn, options, messageFunctions, beforeImage ) {
551
+ trace.traceApi('to.hdi.migration', options);
537
552
  const internalOptions = prepareOptions.to.hdi(options);
538
553
 
539
554
  // Prepare after-image.
540
- const afterImage = forHdi(csn, options);
555
+ const afterImage = forHdi(csn, options, messageFunctions);
541
556
 
542
557
  const diff = modelCompare.compareModels(beforeImage || afterImage, afterImage, internalOptions);
558
+ messageFunctions.setModel(diff);
543
559
 
544
560
  // Convert the diff to SQL.
545
561
  if (!internalOptions.beta)
@@ -551,7 +567,7 @@ function hdiMigration( csn, options, beforeImage ) {
551
567
  const {
552
568
  // eslint-disable-next-line no-unused-vars
553
569
  deletions, migrations, constraintDeletions, ...hdbkinds
554
- } = toSql.toSqlDdl(diff, internalOptions);
570
+ } = toSql.toSqlDdl(diff, internalOptions, messageFunctions);
555
571
 
556
572
  return {
557
573
  afterImage,
@@ -614,29 +630,31 @@ sql.migration = sqlMigration;
614
630
  * Process the given CSN into HDBCDS artifacts.
615
631
  *
616
632
  * @param {any} csn A clean input CSN
617
- * @param {HdbcdsOptions} [options={}] Options
633
+ * @param {HdbcdsOptions} options Options
634
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
618
635
  * @returns {HDBCDS} { <filename>:<content>, ...}
619
636
  */
620
- function hdbcds( csn, options = {} ) {
621
- traceApi('to.hdbcds', options);
637
+ function hdbcds( csn, options, messageFunctions ) {
638
+ trace.traceApi('to.hdbcds', options);
622
639
  const internalOptions = prepareOptions.to.hdbcds(options);
623
640
  internalOptions.transformation = 'hdbcds';
624
641
 
625
- const hanaCsn = forHdbcds(csn, internalOptions);
626
-
627
- const result = flattenResultStructure(toHdbcdsSource(hanaCsn, internalOptions));
628
- return result;
642
+ const hanaCsn = forHdbcds(csn, internalOptions, messageFunctions);
643
+ messageFunctions.setModel(hanaCsn);
644
+ const result = toHdbcds.toHdbcdsSource(hanaCsn, internalOptions, messageFunctions);
645
+ return flattenResultStructure(result);
629
646
  }
630
647
  /**
631
- * Generate a edm document for the given service
648
+ * Generate an edm document for the given service
632
649
  *
633
650
  * @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
634
- * @param {ODataOptions} [options={}] Options
651
+ * @param {ODataOptions} options Options
652
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
635
653
  * @returns {edm} The JSON representation of the service
636
654
  */
637
- function edm( csn, options = {} ) {
638
- traceApi('to.edm', options);
639
- // If not provided at all, set service to undefined to trigger validation
655
+ function edm( csn, options, messageFunctions ) {
656
+ trace.traceApi('to.edm', options);
657
+ // If not provided at all, set service to 'undefined' to trigger validation
640
658
  const internalOptions = prepareOptions.to.edm(
641
659
  // eslint-disable-next-line comma-dangle
642
660
  options.service ? options : Object.assign({ service: undefined }, options)
@@ -646,12 +664,14 @@ function edm( csn, options = {} ) {
646
664
 
647
665
  let servicesEdmj;
648
666
  if (isPreTransformed(csn, 'odata')) {
649
- checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
650
- servicesEdmj = preparedCsnToEdm(csn, service, internalOptions);
667
+ checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions,
668
+ warnAboutMismatchOdata, messageFunctions);
669
+ servicesEdmj = preparedCsnToEdm(csn, service, internalOptions, messageFunctions);
651
670
  }
652
671
  else {
653
- const oDataCsn = odataInternal(csn, internalOptions);
654
- servicesEdmj = preparedCsnToEdm(oDataCsn, service, internalOptions);
672
+ const oDataCsn = odataInternal(csn, internalOptions, messageFunctions);
673
+ messageFunctions.setModel(oDataCsn);
674
+ servicesEdmj = preparedCsnToEdm(oDataCsn, service, internalOptions, messageFunctions);
655
675
  }
656
676
  return servicesEdmj.edmj;
657
677
  }
@@ -662,13 +682,14 @@ edm.all = edmall;
662
682
  * Generate edm documents for all services
663
683
  *
664
684
  * @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
665
- * @param {ODataOptions} [options={}] Options
685
+ * @param {ODataOptions} options Options
686
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
666
687
  * @returns {edms} { <service>:<JSON representation>, ...}
667
688
  */
668
- function edmall( csn, options = {} ) {
669
- traceApi('to.edm.all', options);
689
+ function edmall( csn, options, messageFunctions ) {
690
+ trace.traceApi('to.edm.all', options);
670
691
  const internalOptions = prepareOptions.to.edm(options);
671
- const { error } = messages.makeMessageFunction(csn, internalOptions, 'for.odata');
692
+ const { error } = messageFunctions;
672
693
 
673
694
  if (internalOptions.odataVersion === 'v2')
674
695
  error(null, null, {}, 'OData JSON output is not available for OData V2');
@@ -676,13 +697,16 @@ function edmall( csn, options = {} ) {
676
697
  const result = {};
677
698
  let oDataCsn = csn;
678
699
 
679
- if (isPreTransformed(csn, 'odata'))
680
- checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
681
-
682
- else
683
- oDataCsn = odataInternal(csn, internalOptions);
700
+ if (isPreTransformed(csn, 'odata')) {
701
+ checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions,
702
+ warnAboutMismatchOdata, messageFunctions);
703
+ }
704
+ else {
705
+ oDataCsn = odataInternal(csn, internalOptions, messageFunctions);
706
+ }
684
707
 
685
- const servicesJson = preparedCsnToEdmAll(oDataCsn, internalOptions);
708
+ messageFunctions.setModel(oDataCsn);
709
+ const servicesJson = preparedCsnToEdmAll(oDataCsn, internalOptions, messageFunctions);
686
710
  const services = servicesJson.edmj;
687
711
  for (const serviceName in services)
688
712
  result[serviceName] = services[serviceName];
@@ -690,15 +714,16 @@ function edmall( csn, options = {} ) {
690
714
  return result;
691
715
  }
692
716
  /**
693
- * Generate a edmx document for the given service
717
+ * Generate an edmx document for the given service
694
718
  *
695
719
  * @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
696
- * @param {ODataOptions} [options={}] Options
720
+ * @param {ODataOptions} options Options
721
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
697
722
  * @returns {edmx} The XML representation of the service
698
723
  */
699
- function edmx( csn, options = {} ) {
700
- traceApi('to.edmx', options);
701
- // If not provided at all, set service to undefined to trigger validation
724
+ function edmx( csn, options, messageFunctions ) {
725
+ trace.traceApi('to.edmx', options);
726
+ // If not provided at all, set service to 'undefined' to trigger validation
702
727
  const internalOptions = prepareOptions.to.edmx(
703
728
  // eslint-disable-next-line comma-dangle
704
729
  options.service ? options : Object.assign({ service: undefined }, options)
@@ -708,12 +733,14 @@ function edmx( csn, options = {} ) {
708
733
 
709
734
  let services;
710
735
  if (isPreTransformed(csn, 'odata')) {
711
- checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
712
- services = preparedCsnToEdmx(csn, service, internalOptions);
736
+ checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions,
737
+ warnAboutMismatchOdata, messageFunctions);
738
+ services = preparedCsnToEdmx(csn, service, internalOptions, messageFunctions);
713
739
  }
714
740
  else {
715
- const oDataCsn = odataInternal(csn, internalOptions);
716
- services = preparedCsnToEdmx(oDataCsn, service, internalOptions);
741
+ const oDataCsn = odataInternal(csn, internalOptions, messageFunctions);
742
+ messageFunctions.setModel(oDataCsn);
743
+ services = preparedCsnToEdmx(oDataCsn, service, internalOptions, messageFunctions);
717
744
  }
718
745
 
719
746
  return services.edmx;
@@ -725,23 +752,27 @@ edmx.all = edmxall;
725
752
  * Generate edmx documents for all services
726
753
  *
727
754
  * @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
728
- * @param {ODataOptions} [options={}] Options
755
+ * @param {ODataOptions} options Options
756
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
729
757
  * @returns {edmxs} { <service>:<XML representation>, ...}
730
758
  */
731
- function edmxall( csn, options = {} ) {
732
- traceApi('to.edmx.all', options);
759
+ function edmxall( csn, options, messageFunctions ) {
760
+ trace.traceApi('to.edmx.all', options);
733
761
  const internalOptions = prepareOptions.to.edmx(options);
734
762
 
735
763
  const result = {};
736
764
  let oDataCsn = csn;
737
765
 
738
- if (isPreTransformed(csn, 'odata'))
739
- checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
740
-
741
- else
742
- oDataCsn = odataInternal(csn, internalOptions);
766
+ if (isPreTransformed(csn, 'odata')) {
767
+ checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions,
768
+ warnAboutMismatchOdata, messageFunctions);
769
+ }
770
+ else {
771
+ oDataCsn = odataInternal(csn, internalOptions, messageFunctions);
772
+ }
743
773
 
744
- const servicesEdmx = preparedCsnToEdmxAll(oDataCsn, internalOptions);
774
+ messageFunctions.setModel(oDataCsn);
775
+ const servicesEdmx = preparedCsnToEdmxAll(oDataCsn, internalOptions, messageFunctions);
745
776
  const services = servicesEdmx.edmx;
746
777
  // Create annotations and metadata once per service
747
778
  for (const serviceName in services) {
@@ -752,6 +783,97 @@ function edmxall( csn, options = {} ) {
752
783
  return result;
753
784
  }
754
785
 
786
+ /**
787
+ * Generate an EDM document for the given service in XML and JSON representation
788
+ * If odataVersion is not 'v4', then no JSON is rendered
789
+ *
790
+ * @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
791
+ * @param {ODataOptions} [options={}] Options
792
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
793
+ * @returns {object} { <protocol> : { <ServiceName>: { edmx: <XML representation>, edm: <JSON representation> } } }
794
+ */
795
+ // @ts-ignore
796
+ function odata2( csn, options = {}, messageFunctions ) {
797
+ trace.traceApi('to.odata', options);
798
+ // If not provided at all, set service to undefined to trigger validation
799
+ const internalOptions = prepareOptions.to.odata(
800
+ // eslint-disable-next-line comma-dangle
801
+ options.service ? options : Object.assign({ service: undefined }, options)
802
+ );
803
+
804
+ const { service } = options;
805
+
806
+ let oDataCsn = csn;
807
+ if (isPreTransformed(csn, 'odata')) {
808
+ checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions,
809
+ warnAboutMismatchOdata, messageFunctions);
810
+ }
811
+ else {
812
+ oDataCsn = odataInternal(csn, internalOptions, messageFunctions);
813
+ messageFunctions.setModel(oDataCsn);
814
+ }
815
+
816
+ const edmIR = csnToEdm.csn2edm(oDataCsn, service, internalOptions, messageFunctions);
817
+
818
+
819
+ const version = internalOptions.odataVersion;
820
+ const result = { [version]: { [service]: {} } };
821
+
822
+ if (edmIR) {
823
+ result[version][service].edmx = edmIR.toXML();
824
+ if (version === 'v4')
825
+ result[version][service].edm = edmIR.toJSON();
826
+ }
827
+ return result;
828
+ }
829
+
830
+ odata2.all = odataall;
831
+
832
+ /**
833
+ * Generate EDM documents for all services in XML and JSON representation
834
+ * If odataVersion is not 'v4', then no JSON is rendered
835
+ *
836
+ * @param {CSN|oDataCSN} csn Clean input CSN or a pre-transformed CSN
837
+ * @param {ODataOptions} [options={}] Options
838
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
839
+ * @returns {object} { <protocol>: { <serviceName>: { edmx: <XML representation>, edm: <JSON representation> } } }
840
+ */
841
+ // @ts-ignore
842
+ function odataall( csn, options = {}, messageFunctions ) {
843
+ trace.traceApi('to.odata.all', options);
844
+ const internalOptions = prepareOptions.to.odata(options);
845
+ const { error } = messageFunctions;
846
+
847
+ if (internalOptions.odataVersion === 'v2')
848
+ error(null, null, {}, 'OData JSON output is not available for OData V2');
849
+
850
+ let oDataCsn = csn;
851
+ if (isPreTransformed(csn, 'odata')) {
852
+ checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions,
853
+ warnAboutMismatchOdata, messageFunctions);
854
+ }
855
+ else {
856
+ oDataCsn = odataInternal(csn, internalOptions, messageFunctions);
857
+ messageFunctions.setModel(oDataCsn);
858
+ }
859
+
860
+ const edmIR = csnToEdm.csn2edmAll(oDataCsn, internalOptions, undefined, messageFunctions);
861
+
862
+ const version = internalOptions.odataVersion;
863
+
864
+ const result = {};
865
+ result[version] = {};
866
+
867
+ if (edmIR) {
868
+ for (const serviceName in edmIR) {
869
+ result[version][serviceName] = { edmx: edmIR[serviceName].toXML() };
870
+ if (internalOptions.odataVersion === 'v4')
871
+ result[version][serviceName].edm = edmIR[serviceName].toJSON();
872
+ }
873
+ }
874
+ return result;
875
+ }
876
+
755
877
  /**
756
878
  * Generate edmx for given 'service' based on 'csn' (new-style compact, already prepared for OData)
757
879
  * using 'options'
@@ -759,11 +881,12 @@ function edmxall( csn, options = {} ) {
759
881
  * @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
760
882
  * @param {string} service Service name to use. If you want all services, use preparedCsnToEdmxAll()
761
883
  * @param {ODataOptions} options OData / EDMX specific options.
884
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
762
885
  * @returns {object} Rendered EDMX string for the given service.
763
886
  */
764
- function preparedCsnToEdmx( csn, service, options ) {
887
+ function preparedCsnToEdmx( csn, service, options, messageFunctions ) {
765
888
  timetrace.timetrace.start('EDMX rendering');
766
- const e = csn2edm(csn, service, options)?.toXML('all');
889
+ const e = csnToEdm.csn2edm(csn, service, options, messageFunctions)?.toXML('all');
767
890
  timetrace.timetrace.stop('EDMX rendering');
768
891
  return { edmx: e };
769
892
  }
@@ -774,11 +897,12 @@ function preparedCsnToEdmx( csn, service, options ) {
774
897
  *
775
898
  * @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
776
899
  * @param {ODataOptions} options OData / EDMX specific options.
900
+ * @param {object} messageFunctions Message functions such as `error()`, `info()`, …
777
901
  * @returns {object} Dictionary of rendered EDMX strings for each service.
778
902
  */
779
- function preparedCsnToEdmxAll( csn, options ) {
903
+ function preparedCsnToEdmxAll( csn, options, messageFunctions ) {
780
904
  timetrace.timetrace.start('EDMX all rendering');
781
- const edmxResult = csn2edmAll(csn, options);
905
+ const edmxResult = csnToEdm.csn2edmAll(csn, options, undefined, messageFunctions);
782
906
  for (const service in edmxResult)
783
907
  edmxResult[service] = edmxResult[service].toXML('all');
784
908
  timetrace.timetrace.stop('EDMX all rendering');
@@ -792,13 +916,14 @@ function preparedCsnToEdmxAll( csn, options ) {
792
916
  * @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
793
917
  * @param {string} service Service name for which EDMX should be rendered.
794
918
  * @param {ODataOptions} options OData / EDMX specific options.
919
+ * @param {object} [messageFunctions] Message functions such as `error()`, `info()`, …
795
920
  * @returns {object} Rendered EDM JSON object for of the given service.
796
921
  */
797
- function preparedCsnToEdm( csn, service, options ) {
922
+ function preparedCsnToEdm( csn, service, options, messageFunctions ) {
798
923
  timetrace.timetrace.start('EDM rendering');
799
924
  // Override OData version as edm json is always v4
800
925
  options.odataVersion = 'v4';
801
- const e = csn2edm(csn, service, options)?.toJSON();
926
+ const e = csnToEdm.csn2edm(csn, service, options, messageFunctions)?.toJSON();
802
927
  timetrace.timetrace.stop('EDM rendering');
803
928
  return { edmj: e };
804
929
  }
@@ -809,13 +934,14 @@ function preparedCsnToEdm( csn, service, options ) {
809
934
  *
810
935
  * @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
811
936
  * @param {ODataOptions} options OData / EDMX specific options.
937
+ * @param {object} [messageFunctions] Message functions such as `error()`, `info()`, …
812
938
  * @returns {object} Dictionary of rendered EDM JSON objects for each service.
813
939
  */
814
- function preparedCsnToEdmAll( csn, options ) {
940
+ function preparedCsnToEdmAll( csn, options, messageFunctions ) {
815
941
  timetrace.timetrace.start('EDM all rendering');
816
942
  // Override OData version as edm json is always v4
817
943
  options.odataVersion = 'v4';
818
- const edmj = csn2edmAll(csn, options);
944
+ const edmj = csnToEdm.csn2edmAll(csn, options, undefined, messageFunctions);
819
945
  for (const service in edmj)
820
946
  edmj[service] = edmj[service].toJSON();
821
947
  timetrace.timetrace.stop('EDM all rendering');
@@ -855,14 +981,21 @@ module.exports = {
855
981
  hdbcds: publishCsnProcessor(hdbcds, 'to.hdbcds'),
856
982
  edm: publishCsnProcessor(edm, 'to.edm'),
857
983
  edmx: publishCsnProcessor(edmx, 'to.edmx'),
984
+ odata2: publishCsnProcessor(odata2, 'to.odata'),
858
985
  /** Internal only */
859
986
  for_sql: publishCsnProcessor(forSql, 'for.sql'),
860
987
  for_hdi: publishCsnProcessor(forHdi, 'for.hdi'),
861
988
  for_hdbcds: publishCsnProcessor(forHdbcds, 'for.hdbcds'),
862
989
  for_effective: publishCsnProcessor(forEffective, 'for.effective'),
863
- /** Deprecated, will be removed in cds-compiler@v4 */
864
- preparedCsnToEdmx,
865
- preparedCsnToEdm,
990
+
991
+ /* Deprecated, will be removed in cds-compiler@v5 */ // TODO(v5): Remove
992
+ preparedCsnToEdmx(csn, service, options) {
993
+ preparedCsnToEdmx(csn, service, options, makeMessageFunction( csn, options, 'to.edmx' ));
994
+ },
995
+ /* Deprecated, will be removed in cds-compiler@v5 */ // TODO(v5): Remove
996
+ preparedCsnToEdm(csn, service, options) {
997
+ preparedCsnToEdm(csn, service, options, makeMessageFunction( csn, options, 'to.edm' ));
998
+ },
866
999
  };
867
1000
 
868
1001
 
@@ -891,18 +1024,19 @@ function publishCsnProcessor( processor, _name ) {
891
1024
  * @returns {any} What ever the processor returns
892
1025
  */
893
1026
  function api( csn, options = {}, ...args ) {
1027
+ const originalMessageLength = options.messages?.length;
894
1028
  try {
895
- const messageFunctions = messages.makeMessageFunction(csn, options, 'api');
1029
+ const messageFunctions = messages.makeMessageFunction(csn, options, _name);
896
1030
  if (options.deprecated)
897
1031
  checkRemovedDeprecatedFlags( options, messageFunctions );
898
1032
 
899
1033
  checkOutdatedOptions( options, messageFunctions );
900
- checkCsnFlavor( csn, options, messageFunctions, _name );
901
-
1034
+ csn = ensureClientCsn( csn, options, messageFunctions, _name );
902
1035
  messageFunctions.throwWithError();
1036
+ messageFunctions.setModel(csn);
903
1037
 
904
1038
  timetrace.timetrace.start(_name);
905
- const result = processor( csn, options, ...args );
1039
+ const result = processor( csn, options, messageFunctions, ...args );
906
1040
  timetrace.timetrace.stop(_name);
907
1041
  return result;
908
1042
  }
@@ -912,20 +1046,27 @@ function publishCsnProcessor( processor, _name ) {
912
1046
  throw err;
913
1047
 
914
1048
  if (options.testMode && !(err instanceof TypeError) &&
915
- !(err instanceof ModelError))
1049
+ !(err instanceof baseError.ModelError))
916
1050
  throw err;
917
1051
 
918
- const { info } = messages.makeMessageFunction( csn, options, 'compile' );
919
- const msg = info( 'api-recompiled-csn', location.emptyLocation('csn.json'), {}, 'CSN input had to be recompiled' );
1052
+ // Reset messages to what we had before the backend crashed.
1053
+ // Backends may report the same issues again after compilation.
1054
+ if (originalMessageLength !== undefined)
1055
+ options.messages.length = originalMessageLength;
1056
+
1057
+ const messageFunctions = messages.makeMessageFunction( csn, options, _name );
1058
+ const recompileMsg = messageFunctions.info( 'api-recompiled-csn', location.emptyLocation('csn.json'), {},
1059
+ 'CSN input had to be recompiled' );
920
1060
  if (options.internalMsg || options.testMode)
921
- msg.error = err; // Attach original error;
1061
+ recompileMsg.error = err; // Attach original error;
922
1062
  if (options.testMode) // Attach recompilation reason in testMode
923
- msg.message += `\n ↳ cause: ${ err.message }`;
1063
+ recompileMsg.message += `\n ↳ cause: ${ err.message }`;
924
1064
 
925
1065
  // next line to be replaced by CSN parser call which reads the CSN object
926
1066
  const xsn = compiler.recompileX(csn, options);
927
1067
  const recompiledCsn = toCsn.compactModel(xsn);
928
- return processor( recompiledCsn, options, ...args );
1068
+ messageFunctions.setModel(recompiledCsn);
1069
+ return processor( recompiledCsn, options, messageFunctions, ...args );
929
1070
  }
930
1071
  }
931
1072
  }
@@ -946,7 +1087,6 @@ function checkOutdatedOptions( options, messageFunctions ) {
946
1087
  if (options.messages?.some(m => m.messageId === 'api-invalid-option' || m.messageId === 'api-invalid-variable-replacement'))
947
1088
  return;
948
1089
 
949
-
950
1090
  for (const name of oldBackendOptionNames) {
951
1091
  if (typeof options[name] === 'object') // may be a boolean due to internal options
952
1092
  messageFunctions.error('api-invalid-option', null, { '#': 'deprecated', name });
@@ -981,6 +1121,12 @@ function checkOutdatedOptions( options, messageFunctions ) {
981
1121
  * Checks that the given CSN is usable by our backends, e.g. that
982
1122
  * the CSN is not a gensrc (a.k.a. xtended) for most backends.
983
1123
  *
1124
+ * Returns the input CSN if it is acceptable or compiles the input CSN if it does not
1125
+ * have the expected CSN flavor.
1126
+ *
1127
+ * The compiler does not set any marker in `meta`; we use the umbrella one
1128
+ * for easier debugging.
1129
+ *
984
1130
  * For reference, cds-compiler/cds-dk CSN flavor map:
985
1131
  * - client -> inferred
986
1132
  * - gensrc -> xtended
@@ -989,23 +1135,39 @@ function checkOutdatedOptions( options, messageFunctions ) {
989
1135
  * If this function becomes more complex (e.g. more module conditions),
990
1136
  * move it from then generic api wrapper to the individual module.
991
1137
  *
992
- * TODO: The compiler does not set any marker in `meta`; we use the umbrella one
993
- * for easier debugging.
994
- *
995
1138
  * @param {CSN.Model} csn User CSN
996
1139
  * @param {CSN.Options} options User options
997
1140
  * @param {object} messageFunctions Functions returned by makeMessageFunction()
998
1141
  * @param {string} module Backend module, e.g. to.cdl or to.sql
1142
+ * @returns {CSN.Model} CSN that works for backends.
999
1143
  */
1000
- function checkCsnFlavor( csn, options, messageFunctions, module ) {
1144
+ function ensureClientCsn( csn, options, messageFunctions, module ) {
1001
1145
  if (module === 'to.cdl' || !csn)
1002
- return; // to.cdl allows every CSN flavor
1146
+ return csn; // to.cdl allows every CSN flavor
1003
1147
 
1004
1148
  if (csn.meta?.flavor === 'xtended') {
1005
- // TODO: csn.meta?.flavor === 'parsed'; currently used by `@sap/cds` tests.
1006
- messageFunctions.error('api-unsupported-csn-flavor', null, { name: module, option: csn.meta?.flavor },
1007
- 'Module $(NAME) expects a client/inferred CSN, not $(OPTION)');
1149
+ messageFunctions.error('api-unsupported-csn-flavor', null, { name: module, option: csn.meta?.flavor });
1150
+ return csn;
1008
1151
  }
1152
+
1153
+ // `parsed` CSN is allowed if it can be compiled (i.e. no `requires`).
1154
+ // Still return false, because it's not client CSN. The caller must handle it.
1155
+ if (csn.meta?.flavor === 'parsed') {
1156
+ if (csn.requires?.length > 0) {
1157
+ messageFunctions.error('api-unsupported-csn-flavor', null, {
1158
+ '#': 'parsed-requires',
1159
+ name: module,
1160
+ prop: 'requires',
1161
+ });
1162
+ return csn;
1163
+ }
1164
+
1165
+ // TODO: next line to be replaced by CSN parser call which reads the CSN object once the two API files are merged.
1166
+ const xsn = compiler.recompileX(csn, options);
1167
+ return toCsn.compactModel(xsn);
1168
+ }
1169
+
1170
+ return csn;
1009
1171
  }
1010
1172
 
1011
1173
  /**