@sap/cds-compiler 2.13.8 → 3.0.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 (127) hide show
  1. package/CHANGELOG.md +155 -1594
  2. package/bin/cdsc.js +144 -66
  3. package/doc/CHANGELOG_ARCHIVE.md +1592 -0
  4. package/doc/CHANGELOG_BETA.md +3 -4
  5. package/doc/CHANGELOG_DEPRECATED.md +35 -1
  6. package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
  7. package/doc/Versioning.md +20 -1
  8. package/lib/api/.eslintrc.json +2 -2
  9. package/lib/api/main.js +237 -122
  10. package/lib/api/options.js +17 -88
  11. package/lib/api/validate.js +12 -16
  12. package/lib/base/keywords.js +216 -109
  13. package/lib/base/message-registry.js +152 -37
  14. package/lib/base/messages.js +145 -83
  15. package/lib/base/model.js +44 -2
  16. package/lib/base/optionProcessorHelper.js +19 -0
  17. package/lib/checks/actionsFunctions.js +7 -5
  18. package/lib/checks/annotationsOData.js +11 -32
  19. package/lib/checks/arrayOfs.js +1 -34
  20. package/lib/checks/cdsPersistence.js +1 -0
  21. package/lib/checks/elements.js +6 -6
  22. package/lib/checks/invalidTarget.js +1 -1
  23. package/lib/checks/nonexpandableStructured.js +1 -1
  24. package/lib/checks/queryNoDbArtifacts.js +2 -1
  25. package/lib/checks/selectItems.js +5 -1
  26. package/lib/checks/types.js +4 -2
  27. package/lib/checks/utils.js +2 -2
  28. package/lib/checks/validator.js +4 -5
  29. package/lib/compiler/assert-consistency.js +16 -10
  30. package/lib/compiler/base.js +1 -0
  31. package/lib/compiler/builtins.js +98 -9
  32. package/lib/compiler/checks.js +22 -70
  33. package/lib/compiler/define.js +61 -13
  34. package/lib/compiler/extend.js +79 -14
  35. package/lib/compiler/finalize-parse-cdl.js +46 -29
  36. package/lib/compiler/index.js +100 -37
  37. package/lib/compiler/moduleLayers.js +7 -0
  38. package/lib/compiler/populate.js +19 -18
  39. package/lib/compiler/propagator.js +7 -4
  40. package/lib/compiler/resolve.js +297 -234
  41. package/lib/compiler/shared.js +107 -102
  42. package/lib/compiler/tweak-assocs.js +16 -11
  43. package/lib/compiler/utils.js +5 -0
  44. package/lib/edm/annotations/genericTranslation.js +93 -21
  45. package/lib/edm/csn2edm.js +230 -115
  46. package/lib/edm/edm.js +305 -226
  47. package/lib/edm/edmPreprocessor.js +509 -438
  48. package/lib/edm/edmUtils.js +31 -45
  49. package/lib/gen/Dictionary.json +98 -22
  50. package/lib/gen/language.checksum +1 -1
  51. package/lib/gen/language.interp +10 -30
  52. package/lib/gen/language.tokens +105 -114
  53. package/lib/gen/languageLexer.interp +1 -34
  54. package/lib/gen/languageLexer.js +889 -1007
  55. package/lib/gen/languageLexer.tokens +95 -106
  56. package/lib/gen/languageParser.js +20786 -22199
  57. package/lib/json/csnVersion.js +10 -11
  58. package/lib/json/from-csn.js +59 -51
  59. package/lib/json/to-csn.js +10 -10
  60. package/lib/language/antlrParser.js +2 -2
  61. package/lib/language/docCommentParser.js +62 -39
  62. package/lib/language/errorStrategy.js +52 -40
  63. package/lib/language/genericAntlrParser.js +348 -229
  64. package/lib/language/language.g4 +629 -653
  65. package/lib/language/multiLineStringParser.js +14 -42
  66. package/lib/language/textUtils.js +44 -0
  67. package/lib/main.d.ts +46 -43
  68. package/lib/main.js +108 -79
  69. package/lib/model/csnRefs.js +34 -7
  70. package/lib/model/csnUtils.js +337 -332
  71. package/lib/model/enrichCsn.js +1 -0
  72. package/lib/model/revealInternalProperties.js +30 -10
  73. package/lib/model/sortViews.js +32 -31
  74. package/lib/modelCompare/compare.js +6 -6
  75. package/lib/optionProcessor.js +73 -46
  76. package/lib/render/.eslintrc.json +1 -1
  77. package/lib/render/DuplicateChecker.js +4 -7
  78. package/lib/render/manageConstraints.js +70 -2
  79. package/lib/render/toCdl.js +1042 -882
  80. package/lib/render/toHdbcds.js +195 -245
  81. package/lib/render/toRename.js +44 -22
  82. package/lib/render/toSql.js +225 -241
  83. package/lib/render/utils/common.js +145 -15
  84. package/lib/render/utils/sql.js +20 -19
  85. package/lib/sql-identifier.js +6 -0
  86. package/lib/transform/db/.eslintrc.json +4 -3
  87. package/lib/transform/db/associations.js +2 -2
  88. package/lib/transform/db/cdsPersistence.js +5 -15
  89. package/lib/transform/db/constraints.js +4 -2
  90. package/lib/transform/db/expansion.js +22 -16
  91. package/lib/transform/db/flattening.js +109 -80
  92. package/lib/transform/db/transformExists.js +7 -7
  93. package/lib/transform/db/views.js +9 -6
  94. package/lib/transform/draft/.eslintrc.json +2 -2
  95. package/lib/transform/draft/db.js +6 -6
  96. package/lib/transform/draft/odata.js +6 -7
  97. package/lib/transform/forHanaNew.js +62 -48
  98. package/lib/transform/forOdataNew.js +49 -50
  99. package/lib/transform/localized.js +31 -20
  100. package/lib/transform/odata/toFinalBaseType.js +16 -14
  101. package/lib/transform/odata/typesExposure.js +146 -198
  102. package/lib/transform/odata/utils.js +1 -38
  103. package/lib/transform/transformUtilsNew.js +67 -84
  104. package/lib/transform/translateAssocsToJoins.js +7 -3
  105. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  106. package/lib/transform/universalCsn/coreComputed.js +16 -9
  107. package/lib/transform/universalCsn/universalCsnEnricher.js +60 -10
  108. package/lib/utils/file.js +3 -3
  109. package/lib/utils/moduleResolve.js +13 -6
  110. package/lib/utils/timetrace.js +20 -21
  111. package/package.json +35 -4
  112. package/share/messages/message-explanations.json +2 -1
  113. package/share/messages/syntax-expected-integer.md +37 -0
  114. package/doc/ApiMigration.md +0 -237
  115. package/doc/CommandLineMigration.md +0 -58
  116. package/doc/ErrorMessages.md +0 -175
  117. package/doc/FioriAnnotations.md +0 -94
  118. package/doc/ODataTransformation.md +0 -273
  119. package/lib/backends.js +0 -529
  120. package/lib/fix_antlr4-8_warning.js +0 -56
  121. package/lib/transform/odata/attachPath.js +0 -96
  122. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  123. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  124. package/lib/transform/odata/referenceFlattener.js +0 -296
  125. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  126. package/lib/transform/odata/structuralPath.js +0 -72
  127. package/lib/transform/odata/structureFlattener.js +0 -171
package/lib/api/main.js CHANGED
@@ -2,20 +2,20 @@
2
2
 
3
3
  'use strict';
4
4
 
5
- const prepareOptions = require('./options');
6
- const backends = require('../backends');
7
- const { setProp } = require('../base/model');
8
- const { emptyLocation } = require('../base/location');
9
- const { CompilationError, makeMessageFunction } = require('../base/messages');
10
- const { recompileX } = require('../compiler/index');
11
- const { compactModel, sortCsn } = require('../json/to-csn');
12
- const { transform4odataWithCsn } = require('../transform/forOdataNew.js');
13
- const { toSqlDdl } = require('../render/toSql');
14
- const { compareModels } = require('../modelCompare/compare');
15
- const sortViews = require('../model/sortViews');
16
- const { getResultingName } = require('../model/csnUtils');
17
- const { timetrace } = require('../utils/timetrace');
18
- const { transformForHanaWithCsn } = require('../transform/forHanaNew');
5
+ const prepareOptions = lazyload('./options');
6
+ const baseModel = lazyload('../base/model');
7
+ const location = lazyload('../base/location');
8
+ const messages = lazyload('../base/messages');
9
+ const compiler = lazyload('../compiler/index');
10
+ const toCsn = lazyload('../json/to-csn');
11
+ const forOdataNew = lazyload('../transform/forOdataNew.js');
12
+ const toSql = lazyload('../render/toSql');
13
+ const toCdl = require('../render/toCdl');
14
+ const modelCompare = lazyload('../modelCompare/compare');
15
+ const sortViews = lazyload('../model/sortViews');
16
+ const csnUtils = lazyload('../model/csnUtils');
17
+ const timetrace = lazyload('../utils/timetrace');
18
+ const forHanaNew = lazyload('../transform/forHanaNew');
19
19
 
20
20
  /**
21
21
  * Return the artifact name for use for the hdbresult object
@@ -26,16 +26,15 @@ const { transformForHanaWithCsn } = require('../transform/forHanaNew');
26
26
  * @returns {string} Name with . replaced as _ in some places
27
27
  */
28
28
  function getFileName(artifactName, csn) {
29
- return getResultingName(csn, 'quoted', artifactName);
29
+ return csnUtils.getResultingName(csn, 'quoted', artifactName);
30
30
  }
31
31
 
32
- const propertyToCheck = {
33
- odata: 'toOdata',
34
- };
35
-
36
- const { cloneCsn } = require('../model/csnUtils');
32
+ const { cloneCsnNonDict } = require('../model/csnUtils');
37
33
  const { toHdbcdsSource } = require('../render/toHdbcds');
38
34
  const { ModelError } = require('../base/error');
35
+ const { forEach, forEachKey } = require('../utils/objectUtils');
36
+ const { checkRemovedDeprecatedFlags } = require('../base/model');
37
+ const { csn2edm, csn2edmAll } = require('../edm/csn2edm');
39
38
 
40
39
  const relevantGeneralOptions = [ /* for future generic options */ ];
41
40
  const relevantOdataOptions = [ 'sqlMapping', 'odataFormat' ];
@@ -53,15 +52,14 @@ const warnAboutMismatchOdata = [ 'odataVersion' ];
53
52
  function attachTransformerCharacteristics(csn, transformation, options,
54
53
  relevantOptionNames, optionalOptionNames = []) {
55
54
  const relevant = {};
56
- const propName = propertyToCheck[transformation];
57
55
  for (const name of relevantOptionNames ) {
58
- if (options[propName][name] !== undefined)
59
- relevant[name] = options[propName][name];
56
+ if (options[name] !== undefined)
57
+ relevant[name] = options[name];
60
58
  }
61
59
 
62
60
  for (const name of optionalOptionNames ) {
63
- if (options[propName][name] !== undefined)
64
- relevant[name] = options[propName][name];
61
+ if (options[name] !== undefined)
62
+ relevant[name] = options[name];
65
63
  }
66
64
 
67
65
  for (const name of relevantGeneralOptions ) {
@@ -69,10 +67,10 @@ function attachTransformerCharacteristics(csn, transformation, options,
69
67
  relevant[name] = options[name];
70
68
  }
71
69
  if (!csn.meta)
72
- setProp(csn, 'meta', {});
70
+ baseModel.setProp(csn, 'meta', {});
73
71
 
74
- setProp(csn.meta, 'options', relevant);
75
- setProp(csn.meta, 'transformation', transformation);
72
+ baseModel.setProp(csn.meta, 'options', relevant);
73
+ baseModel.setProp(csn.meta, 'transformation', transformation);
76
74
  }
77
75
 
78
76
  /**
@@ -91,7 +89,7 @@ function checkPreTransformedCsn(csn, options, relevantOptionNames, warnAboutMism
91
89
  // Not able to check
92
90
  return;
93
91
  }
94
- const { error, warning, throwWithError } = makeMessageFunction(csn, options, module);
92
+ const { error, warning, throwWithAnyError } = messages.makeMessageFunction(csn, options, module);
95
93
 
96
94
  for (const name of relevantOptionNames ) {
97
95
  if (options[name] !== csn.meta.options[name])
@@ -103,7 +101,7 @@ function checkPreTransformedCsn(csn, options, relevantOptionNames, warnAboutMism
103
101
  warning('options-mismatch-pretransformed-csn', null, `Expected pre-processed CSN to have option "${ name }" set to "${ options[name] }". Found: "${ csn.meta.options[name] }"`);
104
102
  }
105
103
 
106
- throwWithError();
104
+ throwWithAnyError();
107
105
  }
108
106
 
109
107
  /**
@@ -127,7 +125,7 @@ function isPreTransformed(csn, transformation) {
127
125
  * @returns {object} Return an oData-pre-processed CSN
128
126
  */
129
127
  function odataInternal(csn, internalOptions) {
130
- const oDataCsn = transform4odataWithCsn(csn, internalOptions);
128
+ const oDataCsn = forOdataNew.transform4odataWithCsn(csn, internalOptions);
131
129
  attachTransformerCharacteristics(oDataCsn, 'odata', internalOptions, relevantOdataOptions, warnAboutMismatchOdata);
132
130
  return oDataCsn;
133
131
  }
@@ -149,13 +147,13 @@ function odata(csn, options = {}) {
149
147
  *
150
148
  * @param {object} csn CSN to process
151
149
  * @param {object} [externalOptions={}] Options
152
- * @returns {CDL} { <artifactName>: <CDL representation>, ...}
150
+ * @returns {object} { model: string, namespace: string, unappliedExtensions: string }
153
151
  */
154
152
  function cdl(csn, externalOptions = {}) {
155
153
  const internalOptions = prepareOptions.to.cdl(externalOptions);
156
- const { result } = backends.toCdlWithCsn(cloneCsn(csn, internalOptions), internalOptions);
157
- return result;
154
+ return toCdl.csnToCdl(cloneCsnNonDict(csn, internalOptions), internalOptions);
158
155
  }
156
+
159
157
  /**
160
158
  * Transform a CSN like to.sql
161
159
  *
@@ -167,8 +165,9 @@ function cdl(csn, externalOptions = {}) {
167
165
  function forSql(csn, options = {}) {
168
166
  const internalOptions = prepareOptions.to.sql(options);
169
167
  internalOptions.transformation = 'sql';
170
- internalOptions.toSql.csn = true;
171
- return backends.toSqlWithCsn(csn, internalOptions).csn;
168
+ const transformedCsn = forHanaNew.transformForHanaWithCsn(csn, internalOptions, 'to.sql');
169
+
170
+ return internalOptions.testMode ? toCsn.sortCsn(transformedCsn, internalOptions) : transformedCsn;
172
171
  }
173
172
  /**
174
173
  * Transform a CSN like to.hdi
@@ -180,8 +179,10 @@ function forSql(csn, options = {}) {
180
179
  */
181
180
  function forHdi(csn, options = {}) {
182
181
  const internalOptions = prepareOptions.to.hdi(options);
183
- internalOptions.toSql.csn = true;
184
- return backends.toSqlWithCsn(csn, internalOptions).csn;
182
+ internalOptions.transformation = 'sql';
183
+ const transformedCsn = forHanaNew.transformForHanaWithCsn(csn, internalOptions, 'to.hdi');
184
+
185
+ return internalOptions.testMode ? toCsn.sortCsn(transformedCsn, internalOptions) : transformedCsn;
185
186
  }
186
187
  /**
187
188
  * Transform a CSN like to.hdbcds
@@ -195,9 +196,9 @@ function forHdbcds(csn, options = {}) {
195
196
  const internalOptions = prepareOptions.to.hdbcds(options);
196
197
  internalOptions.transformation = 'hdbcds';
197
198
 
198
- const hanaCsn = transformForHanaWithCsn(csn, internalOptions, 'to.hdbcds');
199
+ const hanaCsn = forHanaNew.transformForHanaWithCsn(csn, internalOptions, 'to.hdbcds');
199
200
 
200
- return internalOptions.testMode ? sortCsn(hanaCsn, internalOptions) : hanaCsn;
201
+ return internalOptions.testMode ? toCsn.sortCsn(hanaCsn, internalOptions) : hanaCsn;
201
202
  }
202
203
 
203
204
  /**
@@ -211,12 +212,16 @@ function sql(csn, options = {}) {
211
212
  const internalOptions = prepareOptions.to.sql(options);
212
213
  internalOptions.transformation = 'sql';
213
214
 
214
- // we need the CSN for view sorting
215
- internalOptions.toSql.csn = true;
215
+ const { error } = messages.makeMessageFunction(csn, internalOptions, 'for.odata');
216
+
217
+ if (internalOptions.sqlDialect === 'postgres' && !baseModel.isBetaEnabled(internalOptions, 'postgres'))
218
+ error(null, null, 'sqlDialect: \'postgres\' is only supported with beta-flag \'postgres\'');
216
219
 
217
- const intermediateResult = backends.toSqlWithCsn(csn, internalOptions);
220
+ // we need the CSN for view sorting
221
+ const transformedCsn = forSql(csn, options);
222
+ const sqls = toSql.toSqlDdl(transformedCsn, internalOptions);
218
223
 
219
- const result = sortViews(intermediateResult);
224
+ const result = sortViews({ csn: transformedCsn, sql: sqls.sql });
220
225
 
221
226
  return result.map(obj => obj.sql).filter(create => create);
222
227
  }
@@ -232,12 +237,8 @@ function hdi(csn, options = {}) {
232
237
  const internalOptions = prepareOptions.to.hdi(options);
233
238
 
234
239
  // we need the CSN for view sorting
235
- internalOptions.toSql.csn = true;
236
-
237
- const intermediateResult = backends.toSqlWithCsn(csn, internalOptions);
238
-
239
- const sqlCSN = intermediateResult.csn;
240
- delete intermediateResult.csn;
240
+ const sqlCSN = forHdi(csn, options);
241
+ const sqls = toSql.toSqlDdl(sqlCSN, internalOptions);
241
242
 
242
243
  if (internalOptions.testMode) {
243
244
  // All this mapping is needed because sortViews crossmatches
@@ -245,13 +246,13 @@ function hdi(csn, options = {}) {
245
246
  // But we also need to return it with the correct file ending in the end
246
247
  // so remember and do lot's of mapping here.
247
248
 
248
- const flat = flattenResultStructure(intermediateResult);
249
+ const flat = flattenResultStructure(sqls);
249
250
 
250
251
  const nameMapping = Object.create(null);
251
252
  const sqlArtifactsWithCSNNamesToSort = Object.create(null);
252
253
  const sqlArtifactsNotToSort = Object.create(null);
253
254
 
254
- Object.keys(flat).forEach((key) => {
255
+ forEach(flat, (key) => {
255
256
  const artifactNameLikeInCsn = key.replace(/\.[^/.]+$/, '');
256
257
  nameMapping[artifactNameLikeInCsn] = key;
257
258
  if (key.endsWith('.hdbtable') || key.endsWith('.hdbview'))
@@ -269,14 +270,14 @@ function hdi(csn, options = {}) {
269
270
  }, Object.create(null));
270
271
 
271
272
  // now add the not-sorted stuff, like indices
272
- Object.keys(sqlArtifactsNotToSort).forEach((key) => {
273
+ forEach(sqlArtifactsNotToSort, (key) => {
273
274
  sorted[remapName(key, sqlCSN, k => !k.endsWith('.hdbindex'))] = sqlArtifactsNotToSort[key];
274
275
  });
275
276
 
276
277
  return sorted;
277
278
  }
278
279
 
279
- return remapNames(flattenResultStructure(intermediateResult), sqlCSN, k => !k.endsWith('.hdbindex'));
280
+ return remapNames(flattenResultStructure(sqls), sqlCSN, k => !k.endsWith('.hdbindex'));
280
281
  }
281
282
  /**
282
283
  * Remap names so that they stay consistent between v1 and v2
@@ -291,10 +292,10 @@ function hdi(csn, options = {}) {
291
292
  function remapNames(dict, csn, filter) {
292
293
  const result = Object.create(null);
293
294
 
294
- for (const [ key, value ] of Object.entries(dict)) {
295
+ forEach(dict, (key, value) => {
295
296
  const name = remapName(key, csn, filter);
296
297
  result[name] = value;
297
- }
298
+ });
298
299
 
299
300
  return result;
300
301
  }
@@ -342,45 +343,17 @@ function remapName(key, csn, filter = () => true) {
342
343
  * consists of a column drop and add).
343
344
  */
344
345
  function hdiMigration(csn, options, beforeImage) {
345
- /**
346
- * Swap arguments in case of inverted argument order.
347
- * This is for backward compatibility with @sap/cds@4.5.(2…3).
348
- *
349
- * @todo Remove in cds-compiler@2.x
350
- * @param {HdiOptions|CSN.Model} inputOptions Options or CSN image
351
- * @param {HdiOptions|CSN.Model} inputBeforeImage CSN image or options
352
- * @returns {Array} Array where the real options come first
353
- */
354
- function backwardCompatible(inputOptions, inputBeforeImage) {
355
- /**
356
- * Check whether the given argument is a CSN
357
- *
358
- * @param {object} arg Argument to verify
359
- * @returns {boolean} True if it is a CSN
360
- */
361
- function isBeforeImage(arg) {
362
- return arg === null || [ 'definitions', 'meta', '$version' ].some(key => key in arg);
363
- }
364
- return isBeforeImage(inputBeforeImage)
365
- ? [ inputOptions, inputBeforeImage ]
366
- : [ inputBeforeImage, inputOptions ];
367
- }
368
- [ options, beforeImage ] = backwardCompatible(options, beforeImage);
369
-
370
346
  const internalOptions = prepareOptions.to.hdi(options);
371
- internalOptions.toSql.csn = true;
372
347
 
373
348
  // Prepare after-image.
374
- // FIXME: Is this needed?
375
- // cloneCsnMessages(csn, options, internalOptions);
376
- const afterImage = backends.toSqlWithCsn(csn, internalOptions).csn;
349
+ const afterImage = forHdi(csn, options);
377
350
 
378
351
  // Compare both images.
379
- const diff = compareModels(beforeImage || afterImage, afterImage, internalOptions);
352
+ const diff = modelCompare.compareModels(beforeImage || afterImage, afterImage, internalOptions);
380
353
 
381
354
  // Convert the diff to SQL.
382
355
  internalOptions.forHana = true; // Make it pass the SQL rendering
383
- const { deletions, migrations, ...hdbkinds } = toSqlDdl(diff, internalOptions);
356
+ const { deletions, migrations, ...hdbkinds } = toSql.toSqlDdl(diff, internalOptions);
384
357
 
385
358
  return {
386
359
  afterImage,
@@ -396,15 +369,15 @@ function hdiMigration(csn, options, beforeImage) {
396
369
  */
397
370
  function createDefinitions() {
398
371
  const result = [];
399
- for (const [ kind, artifacts ] of Object.entries(hdbkinds)) {
372
+ forEach(hdbkinds, (kind, artifacts) => {
400
373
  const suffix = `.${ kind }`;
401
- for (const [ name, sqlStatement ] of Object.entries(artifacts)) {
374
+ forEach(artifacts, (name, sqlStatement) => {
402
375
  if ( kind !== 'hdbindex' )
403
376
  result.push({ name: getFileName(name, afterImage), suffix, sql: sqlStatement });
404
377
  else
405
378
  result.push({ name, suffix, sql: sqlStatement });
406
- }
407
- }
379
+ });
380
+ });
408
381
  return result;
409
382
  }
410
383
  /**
@@ -414,9 +387,7 @@ function hdiMigration(csn, options, beforeImage) {
414
387
  */
415
388
  function createDeletions() {
416
389
  const result = [];
417
- for (const [ name ] of Object.entries(deletions))
418
- result.push({ name: getFileName(name, beforeImage), suffix: '.hdbtable' });
419
-
390
+ forEach(deletions, name => result.push({ name: getFileName(name, beforeImage), suffix: '.hdbtable' }));
420
391
  return result;
421
392
  }
422
393
  /**
@@ -426,9 +397,7 @@ function hdiMigration(csn, options, beforeImage) {
426
397
  */
427
398
  function createMigrations() {
428
399
  const result = [];
429
- for (const [ name, changeset ] of Object.entries(migrations))
430
- result.push({ name: getFileName(name, afterImage), suffix: '.hdbmigrationtable', changeset });
431
-
400
+ forEach(migrations, (name, changeset) => result.push({ name: getFileName(name, afterImage), suffix: '.hdbmigrationtable', changeset }));
432
401
  return result;
433
402
  }
434
403
  }
@@ -443,14 +412,14 @@ hdi.migration = hdiMigration;
443
412
  * @returns {HDBCDS} { <filename>:<content>, ...}
444
413
  */
445
414
  function hdbcds(csn, options = {}) {
446
- timetrace.start('to.hdbcds');
415
+ timetrace.timetrace.start('to.hdbcds');
447
416
  const internalOptions = prepareOptions.to.hdbcds(options);
448
417
  internalOptions.transformation = 'hdbcds';
449
418
 
450
419
  const hanaCsn = forHdbcds(csn, internalOptions);
451
420
 
452
421
  const result = flattenResultStructure(toHdbcdsSource(hanaCsn, internalOptions));
453
- timetrace.stop();
422
+ timetrace.timetrace.stop();
454
423
  return result;
455
424
  }
456
425
  /**
@@ -472,11 +441,11 @@ function edm(csn, options = {}) {
472
441
  let servicesEdmj;
473
442
  if (isPreTransformed(csn, 'odata')) {
474
443
  checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
475
- servicesEdmj = backends.preparedCsnToEdm(csn, service, internalOptions);
444
+ servicesEdmj = preparedCsnToEdm(csn, service, internalOptions);
476
445
  }
477
446
  else {
478
447
  const oDataCsn = odataInternal(csn, internalOptions);
479
- servicesEdmj = backends.preparedCsnToEdm(oDataCsn, service, internalOptions);
448
+ servicesEdmj = preparedCsnToEdm(oDataCsn, service, internalOptions);
480
449
  }
481
450
  return servicesEdmj.edmj;
482
451
  }
@@ -492,9 +461,9 @@ edm.all = edmall;
492
461
  */
493
462
  function edmall(csn, options = {}) {
494
463
  const internalOptions = prepareOptions.to.edm(options);
495
- const { error } = makeMessageFunction(csn, internalOptions, 'for.odata');
464
+ const { error } = messages.makeMessageFunction(csn, internalOptions, 'for.odata');
496
465
 
497
- if (internalOptions.version === 'v2')
466
+ if (internalOptions.odataVersion === 'v2')
498
467
  error(null, null, 'OData JSON output is not available for OData V2');
499
468
 
500
469
  const result = {};
@@ -506,13 +475,11 @@ function edmall(csn, options = {}) {
506
475
  else
507
476
  oDataCsn = odataInternal(csn, internalOptions);
508
477
 
509
- const servicesJson = backends.preparedCsnToEdmAll(oDataCsn, internalOptions);
478
+ const servicesJson = preparedCsnToEdmAll(oDataCsn, internalOptions);
510
479
  const services = servicesJson.edmj;
511
- for (const serviceName in services) {
512
- const lEdm = services[serviceName];
513
- // FIXME: Why only metadata_json - isn't this rather a 'combined_json' ? If so, rename it!
514
- result[serviceName] = lEdm;
515
- }
480
+ for (const serviceName in services)
481
+ result[serviceName] = services[serviceName];
482
+
516
483
  return result;
517
484
  }
518
485
  /**
@@ -534,11 +501,11 @@ function edmx(csn, options = {}) {
534
501
  let services;
535
502
  if (isPreTransformed(csn, 'odata')) {
536
503
  checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
537
- services = backends.preparedCsnToEdmx(csn, service, internalOptions);
504
+ services = preparedCsnToEdmx(csn, service, internalOptions);
538
505
  }
539
506
  else {
540
507
  const oDataCsn = odataInternal(csn, internalOptions);
541
- services = backends.preparedCsnToEdmx(oDataCsn, service, internalOptions);
508
+ services = preparedCsnToEdmx(oDataCsn, service, internalOptions);
542
509
  }
543
510
 
544
511
  return services.edmx;
@@ -565,7 +532,7 @@ function edmxall(csn, options = {}) {
565
532
  else
566
533
  oDataCsn = odataInternal(csn, internalOptions);
567
534
 
568
- const servicesEdmx = backends.preparedCsnToEdmxAll(oDataCsn, internalOptions);
535
+ const servicesEdmx = preparedCsnToEdmxAll(oDataCsn, internalOptions);
569
536
  const services = servicesEdmx.edmx;
570
537
  // Create annotations and metadata once per service
571
538
  for (const serviceName in services) {
@@ -576,6 +543,79 @@ function edmxall(csn, options = {}) {
576
543
  return result;
577
544
  }
578
545
 
546
+ /**
547
+ * Generate edmx for given 'service' based on 'csn' (new-style compact, already prepared for OData)
548
+ * using 'options'
549
+ *
550
+ * @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
551
+ * @param {string} service Service name to use. If you want all services, use preparedCsnToEdmxAll()
552
+ * @param {ODataOptions} options OData / EDMX specific options.
553
+ * @returns {object} Rendered EDMX string for the given service.
554
+ */
555
+ function preparedCsnToEdmx(csn, service, options) {
556
+ const e = csn2edm(csn, service, options);
557
+ return {
558
+ edmx: (e ? e.toXML('all') : undefined),
559
+ };
560
+ }
561
+
562
+ /**
563
+ * Generate edmx for given 'service' based on 'csn' (new-style compact, already prepared for OData)
564
+ * using 'options'.
565
+ *
566
+ * @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
567
+ * @param {ODataOptions} options OData / EDMX specific options.
568
+ * @returns {object} Dictionary of rendered EDMX strings for each service.
569
+ */
570
+ function preparedCsnToEdmxAll(csn, options) {
571
+ const edmxResult = csn2edmAll(csn, options);
572
+ for (const service in edmxResult)
573
+ edmxResult[service] = edmxResult[service].toXML('all');
574
+
575
+ return {
576
+ edmx: edmxResult,
577
+ };
578
+ }
579
+
580
+ /**
581
+ * Generate edm-json for given 'service' based on 'csn' (new-style compact, already prepared for OData)
582
+ * using 'options'
583
+ *
584
+ * @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
585
+ * @param {string} service Service name for which EDMX should be rendered.
586
+ * @param {ODataOptions} options OData / EDMX specific options.
587
+ * @returns {object} Rendered EDM JSON object for of the given service.
588
+ */
589
+ function preparedCsnToEdm(csn, service, options) {
590
+ // Override OData version as edm json is always v4
591
+ options.odataVersion = 'v4';
592
+ const e = csn2edm(csn, service, options);
593
+ return {
594
+ edmj: (e ? e.toJSON() : undefined),
595
+ };
596
+ }
597
+
598
+ /**
599
+ * Generate edm-json for given 'service' based on 'csn' (new-style compact, already prepared for OData)
600
+ * using 'options'
601
+ *
602
+ * @param {CSN.Model} csn Input CSN model. Must be OData transformed CSN.
603
+ * @param {ODataOptions} options OData / EDMX specific options.
604
+ * @returns {object} Dictionary of rendered EDM JSON objects for each service.
605
+ */
606
+ function preparedCsnToEdmAll(csn, options) {
607
+ // Override OData version as edm json is always v4
608
+ options.odataVersion = 'v4';
609
+ const edmj = csn2edmAll(csn, options);
610
+ for (const service in edmj)
611
+ edmj[service] = edmj[service].toJSON();
612
+
613
+ return {
614
+ edmj,
615
+ };
616
+ }
617
+
618
+
579
619
  /**
580
620
  * Flatten the result structure to a flat map.
581
621
  *
@@ -586,12 +626,13 @@ function edmxall(csn, options = {}) {
586
626
  */
587
627
  function flattenResultStructure(toProcess) {
588
628
  const result = {};
589
- for (const [ fileType, artifacts ] of Object.entries(toProcess)) {
629
+ forEach(toProcess, (fileType, artifacts) => {
590
630
  if (fileType === 'messages')
591
- continue;
592
- for (const filename of Object.keys(artifacts))
631
+ return;
632
+ forEach(artifacts, (filename) => {
593
633
  result[`${ filename }.${ fileType }`] = artifacts[filename];
594
- }
634
+ });
635
+ });
595
636
 
596
637
  return result;
597
638
  }
@@ -608,7 +649,9 @@ module.exports = {
608
649
  for_sql: publishCsnProcessor(forSql, 'for.sql'),
609
650
  for_hdi: publishCsnProcessor(forHdi, 'for.hdi'),
610
651
  for_hdbcds: publishCsnProcessor(forHdbcds, 'for.hdbcds'),
611
- /** */
652
+ /** Deprecated, will be removed in cds-compiler@v4 */
653
+ preparedCsnToEdmx,
654
+ preparedCsnToEdm,
612
655
  };
613
656
 
614
657
 
@@ -641,29 +684,101 @@ function publishCsnProcessor( processor, _name ) {
641
684
  */
642
685
  function api( csn, options = {}, ...args ) {
643
686
  try {
687
+ if (options.deprecated) {
688
+ const messageFunctions = messages.makeMessageFunction(csn, options, 'api');
689
+ checkRemovedDeprecatedFlags( options, messageFunctions );
690
+ }
691
+ checkOutdatedOptions( options );
644
692
  return processor( csn, options, ...args );
645
693
  }
646
694
  catch (err) {
647
- if (err instanceof CompilationError || options.noRecompile || isPreTransformed(csn, 'odata')) // we cannot recompile a pre-transformed CSN
695
+ if (err instanceof messages.CompilationError || options.noRecompile || isPreTransformed(csn, 'odata')) // we cannot recompile a pre-transformed CSN
648
696
  throw err;
649
697
 
650
698
  if (options.testMode && !(err instanceof TypeError) &&
651
699
  !(err instanceof ModelError))
652
700
  throw err;
653
701
 
654
- const { info } = makeMessageFunction( csn, options, 'compile' );
655
- const msg = info( 'api-recompiled-csn', emptyLocation('csn.json'), {}, 'CSN input had to be recompiled' );
702
+ const { info } = messages.makeMessageFunction( csn, options, 'compile' );
703
+ const msg = info( 'api-recompiled-csn', location.emptyLocation('csn.json'), {}, 'CSN input had to be recompiled' );
656
704
  if (options.internalMsg)
657
705
  msg.error = err; // Attach original error
658
706
 
659
707
  // next line to be replaced by CSN parser call which reads the CSN object
660
- const xsn = recompileX(csn, options);
661
- const recompiledCsn = compactModel(xsn);
708
+ const xsn = compiler.recompileX(csn, options);
709
+ const recompiledCsn = toCsn.compactModel(xsn);
662
710
  return processor( recompiledCsn, options, ...args );
663
711
  }
664
712
  }
665
713
  }
666
714
 
715
+ // Note: No toCsn, because @sap/cds may still use it (2022-06-15)
716
+ const oldBackendOptionNames = [ 'toSql', 'toOdata', 'toHana', 'forHana' ];
717
+ /**
718
+ * Checks if outdated options are used and if so, throw a compiler error.
719
+ * These include:
720
+ * - magicVars (now variableReplacements)
721
+ * - toOdata/toSql/toHana/forHana -> now flat options
722
+ *
723
+ * @param {CSN.Options} options Backend options
724
+ */
725
+ function checkOutdatedOptions(options) {
726
+ const { error, throwWithError } = messages.makeMessageFunction(null, options, 'api');
727
+
728
+ // This error has been emitted once, we don't need to emit it again.
729
+ if (options.messages?.some(m => m.messageId === 'api-invalid-option')) {
730
+ throwWithError();
731
+ return;
732
+ }
733
+
734
+ for (const name of oldBackendOptionNames) {
735
+ if (typeof options[name] === 'object') // may be a boolean due to internal options
736
+ error('api-invalid-option', null, { '#': 'std', name });
737
+ }
738
+
739
+ if (options.magicVars)
740
+ error('api-invalid-option', null, { '#': 'magicVars' });
741
+
742
+ // Don't check `options.magicVars`. It's likely that the user renamed `magicVars` but
743
+ // forgot about user -> $user and locale -> $user.locale
744
+ if (options.variableReplacements?.user)
745
+ error('api-invalid-option', null, { '#': 'user' });
746
+ if (options.variableReplacements?.locale)
747
+ error('api-invalid-option', null, { '#': 'locale' });
748
+
749
+ forEachKey(options.variableReplacements || {}, (name) => {
750
+ if (!name.startsWith('$') && name !== 'user' && name !== 'locale')
751
+ error('api-invalid-option', null, { '#': 'noDollar', name });
752
+ });
753
+
754
+ throwWithError();
755
+ }
756
+
757
+ /**
758
+ * Load the module on-demand and not immediately.
759
+ *
760
+ * @param {string} moduleName Name of the module to load - like with require
761
+ * @returns {object} A Proxy that handles the on-demand loading
762
+ */
763
+ function lazyload(moduleName) {
764
+ let module;
765
+ return new Proxy(((...args) => {
766
+ if (!module) // eslint-disable-next-line global-require
767
+ module = require(moduleName);
768
+
769
+ if (module.apply && typeof module.apply === 'function')
770
+ return module.apply(this, args);
771
+ return module; // for destructured calls
772
+ }), {
773
+ get(target, name) {
774
+ if (!module) // eslint-disable-next-line global-require
775
+ module = require(moduleName);
776
+
777
+ return module[name];
778
+ },
779
+ });
780
+ }
781
+
667
782
 
668
783
  /**
669
784
  * Option format used by the old API, where they are grouped thematically.