@sap/cds-compiler 2.15.4 → 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 (105) hide show
  1. package/CHANGELOG.md +33 -1590
  2. package/bin/cdsc.js +36 -33
  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 +220 -103
  10. package/lib/api/options.js +15 -85
  11. package/lib/api/validate.js +6 -10
  12. package/lib/base/keywords.js +216 -109
  13. package/lib/base/message-registry.js +60 -20
  14. package/lib/base/messages.js +65 -24
  15. package/lib/base/model.js +44 -2
  16. package/lib/checks/actionsFunctions.js +7 -5
  17. package/lib/checks/annotationsOData.js +1 -1
  18. package/lib/checks/cdsPersistence.js +1 -0
  19. package/lib/checks/elements.js +6 -6
  20. package/lib/checks/invalidTarget.js +1 -1
  21. package/lib/checks/nonexpandableStructured.js +1 -1
  22. package/lib/checks/queryNoDbArtifacts.js +2 -1
  23. package/lib/checks/selectItems.js +5 -1
  24. package/lib/checks/types.js +4 -2
  25. package/lib/checks/utils.js +2 -2
  26. package/lib/checks/validator.js +2 -1
  27. package/lib/compiler/assert-consistency.js +15 -10
  28. package/lib/compiler/builtins.js +87 -9
  29. package/lib/compiler/define.js +2 -2
  30. package/lib/compiler/extend.js +59 -11
  31. package/lib/compiler/finalize-parse-cdl.js +20 -9
  32. package/lib/compiler/index.js +25 -11
  33. package/lib/compiler/moduleLayers.js +7 -0
  34. package/lib/compiler/populate.js +13 -13
  35. package/lib/compiler/propagator.js +3 -3
  36. package/lib/compiler/resolve.js +193 -218
  37. package/lib/compiler/shared.js +47 -76
  38. package/lib/compiler/tweak-assocs.js +9 -10
  39. package/lib/compiler/utils.js +5 -0
  40. package/lib/edm/csn2edm.js +18 -21
  41. package/lib/edm/edmPreprocessor.js +25 -30
  42. package/lib/edm/edmUtils.js +10 -24
  43. package/lib/gen/language.checksum +1 -1
  44. package/lib/gen/language.interp +8 -30
  45. package/lib/gen/language.tokens +105 -114
  46. package/lib/gen/languageLexer.interp +1 -34
  47. package/lib/gen/languageLexer.js +889 -1007
  48. package/lib/gen/languageLexer.tokens +95 -106
  49. package/lib/gen/languageParser.js +20632 -22313
  50. package/lib/json/from-csn.js +56 -49
  51. package/lib/json/to-csn.js +10 -8
  52. package/lib/language/antlrParser.js +2 -2
  53. package/lib/language/docCommentParser.js +61 -38
  54. package/lib/language/errorStrategy.js +52 -40
  55. package/lib/language/genericAntlrParser.js +303 -229
  56. package/lib/language/language.g4 +573 -629
  57. package/lib/language/multiLineStringParser.js +14 -42
  58. package/lib/language/textUtils.js +44 -0
  59. package/lib/main.d.ts +27 -42
  60. package/lib/main.js +104 -81
  61. package/lib/model/csnRefs.js +1 -1
  62. package/lib/model/csnUtils.js +170 -283
  63. package/lib/model/revealInternalProperties.js +28 -8
  64. package/lib/model/sortViews.js +32 -31
  65. package/lib/optionProcessor.js +12 -21
  66. package/lib/render/.eslintrc.json +1 -1
  67. package/lib/render/DuplicateChecker.js +4 -7
  68. package/lib/render/manageConstraints.js +70 -2
  69. package/lib/render/toCdl.js +334 -339
  70. package/lib/render/toHdbcds.js +19 -15
  71. package/lib/render/toRename.js +44 -22
  72. package/lib/render/toSql.js +53 -51
  73. package/lib/render/utils/common.js +15 -1
  74. package/lib/render/utils/sql.js +20 -19
  75. package/lib/sql-identifier.js +6 -0
  76. package/lib/transform/db/.eslintrc.json +3 -2
  77. package/lib/transform/db/cdsPersistence.js +5 -15
  78. package/lib/transform/db/constraints.js +1 -1
  79. package/lib/transform/db/expansion.js +7 -6
  80. package/lib/transform/db/flattening.js +18 -19
  81. package/lib/transform/db/views.js +3 -3
  82. package/lib/transform/draft/.eslintrc.json +2 -2
  83. package/lib/transform/draft/db.js +6 -6
  84. package/lib/transform/draft/odata.js +6 -7
  85. package/lib/transform/forHanaNew.js +19 -22
  86. package/lib/transform/forOdataNew.js +10 -12
  87. package/lib/transform/localized.js +22 -16
  88. package/lib/transform/odata/toFinalBaseType.js +10 -10
  89. package/lib/transform/odata/typesExposure.js +3 -3
  90. package/lib/transform/odata/utils.js +1 -38
  91. package/lib/transform/transformUtilsNew.js +63 -77
  92. package/lib/transform/translateAssocsToJoins.js +2 -2
  93. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  94. package/lib/transform/universalCsn/coreComputed.js +11 -6
  95. package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
  96. package/lib/utils/file.js +3 -3
  97. package/lib/utils/timetrace.js +20 -21
  98. package/package.json +35 -4
  99. package/doc/ApiMigration.md +0 -237
  100. package/doc/CommandLineMigration.md +0 -58
  101. package/doc/ErrorMessages.md +0 -175
  102. package/doc/FioriAnnotations.md +0 -94
  103. package/doc/ODataTransformation.md +0 -273
  104. package/lib/backends.js +0 -529
  105. package/lib/fix_antlr4-8_warning.js +0 -56
@@ -8,12 +8,11 @@ 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 2.XX.YY
11
+ ## Version 3.0.0 - 2022-XX-YY
12
12
 
13
- ### Removed `assocsWithParams`
13
+ ### Removed `addTextsLanguageAssoc`
14
14
 
15
- Instead, of using the beta flag `assocsWithParams`, you can change the severity of the messages
16
- `def-unexpected-paramview-assoc` and `def-unexpected-calcview-assoc`.
15
+ Instead, use the option `addTextsLanguageAssoc`, which is available since v2.8.0.
17
16
 
18
17
  ## Version 2.12.0 - 2022-01-25
19
18
 
@@ -11,7 +11,41 @@ Note: `deprecated` features are listed in this ChangeLog just for information.
11
11
  **When the `deprecated` option is set, the `beta` option is ignored,
12
12
  and several new features are not available.**
13
13
 
14
- ## Version 2.XX.YY - 2022-MM-DD
14
+ ## Version 3.0.0 - 2022-XX-YY
15
+
16
+ Version 3 of the cds-compiler removes all v2 deprecated flags.
17
+
18
+ ### Removed `createLocalizedViews`
19
+
20
+ ### Removed `downgradableErrors`
21
+
22
+ ### Removed `generatedEntityNameWithUnderscore`
23
+
24
+ ### Removed `longAutoexposed`
25
+
26
+ ### Removed `noElementsExpansion`
27
+
28
+ ### Removed `noInheritedAutoexposeViaComposition`
29
+
30
+ ### Removed `noScopedRedirections`
31
+
32
+ ### Removed `oldVirtualNotNullPropagation`
33
+
34
+ ### Removed `parensAsStrings`
35
+
36
+ ### Removed `projectionAsQuery`
37
+
38
+ ### Removed `redirectInSubQueries`
39
+
40
+ ### Removed `renderVirtualElements`
41
+
42
+ ### Removed `shortAutoexposed`
43
+
44
+ ### Removed `unmanagedUpInComponent`
45
+
46
+ ### Removed `v1KeysForTemporal`
47
+
48
+ ## Version 2.13.0 - 2022-03-22
15
49
 
16
50
  ### Added `redirectInSubQueries`
17
51
 
@@ -1,8 +1,10 @@
1
1
  # Deprecated Options and How to Avoid Them
2
2
 
3
+ __Important__: With compiler v3, these deprecated options were removed!
4
+
3
5
  To ease the migration to CDS Compiler Version 2,
4
6
  the compiler can be called with an option `deprecated`
5
- which make the compiler behave more like Compiler Version 1 for certain features.
7
+ which makes the compiler behave more like Compiler Version 1 for certain features.
6
8
 
7
9
  As the name suggest, this option should be used only for a limited time.
8
10
  The support for certain v1 features might also be dropped after a while
package/doc/Versioning.md CHANGED
@@ -5,6 +5,17 @@ The cds-compiler uses [Semantic Versioning][SemVer] for its version numbers.
5
5
  This document clarifies how we use [SemVer] and what you can and what you can‘t
6
6
  expect from version updates.
7
7
 
8
+ <!-- toc: start -->
9
+
10
+ 1. [Public API](#public-api)
11
+ 2. [Patch Versions](#patch-versions)
12
+ 3. [Minor Versions](#minor-versions)
13
+ 4. [Beta Flags](#beta-flags)
14
+ 5. [Deprecated Flags](#deprecated-flags)
15
+ 6. [Command Line Tool `cdsc`](#command-line-tool-cdsc)
16
+
17
+ <!-- toc: end -->
18
+
8
19
  ## Public API
9
20
 
10
21
  According to [§1] of SemVer, a public API must be made available. Our public
@@ -41,7 +52,7 @@ we detect invalid CDS code.
41
52
 
42
53
  ## Beta Flags
43
54
 
44
- The compiler provides so called “beta flags” that enable or disable certain
55
+ The compiler provides so-called “beta flags” that enable or disable certain
45
56
  features. We do not guarantee that any such flags stay consistent between
46
57
  patch versions! Beta flags may change any time.
47
58
 
@@ -57,6 +68,14 @@ period.
57
68
  Refer to [CHANGELOG_DEPRECATED.md](./CHANGELOG_DEPRECATED.md) for changes
58
69
  to deprecated flags.
59
70
 
71
+ ## Command Line Tool `cdsc`
72
+
73
+ `bin/cdsc.js` as well as all other command line tools do _not_ guarantee any
74
+ stability. It is considered a compiler internal tool that only serves for
75
+ debugging. The official command line tool `cds` of the `@sap/cds` and
76
+ `@sap/cds-dk` packages are to be used by users. That means commands and
77
+ options may change any time without prior notice. Changes may still be listed
78
+ in [CHANGELOG.md](../CHANGELOG.md).
60
79
 
61
80
  [SemVer]: https://semver.org/
62
81
  [§1]: https://semver.org/#spec-item-1
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "root": true,
3
3
  "env": {
4
- "es6": true,
4
+ "es2020": true,
5
5
  "node": true
6
6
  },
7
7
  // we actually do not extend airbnb-base, as it weakens some eslint:recommended rules
8
8
  "extends": ["../../.eslintrc-ydkjsi.json", "plugin:jsdoc/recommended"],
9
9
  "parserOptions": {
10
- "ecmaVersion": 2018,
10
+ "ecmaVersion": 2020,
11
11
  "sourceType": "script"
12
12
  },
13
13
  "plugins": [
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,17 +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
32
  const { cloneCsnNonDict } = require('../model/csnUtils');
37
33
  const { toHdbcdsSource } = require('../render/toHdbcds');
38
34
  const { ModelError } = require('../base/error');
39
- const { forEach } = require('../utils/objectUtils');
35
+ const { forEach, forEachKey } = require('../utils/objectUtils');
36
+ const { checkRemovedDeprecatedFlags } = require('../base/model');
37
+ const { csn2edm, csn2edmAll } = require('../edm/csn2edm');
40
38
 
41
39
  const relevantGeneralOptions = [ /* for future generic options */ ];
42
40
  const relevantOdataOptions = [ 'sqlMapping', 'odataFormat' ];
@@ -54,15 +52,14 @@ const warnAboutMismatchOdata = [ 'odataVersion' ];
54
52
  function attachTransformerCharacteristics(csn, transformation, options,
55
53
  relevantOptionNames, optionalOptionNames = []) {
56
54
  const relevant = {};
57
- const propName = propertyToCheck[transformation];
58
55
  for (const name of relevantOptionNames ) {
59
- if (options[propName][name] !== undefined)
60
- relevant[name] = options[propName][name];
56
+ if (options[name] !== undefined)
57
+ relevant[name] = options[name];
61
58
  }
62
59
 
63
60
  for (const name of optionalOptionNames ) {
64
- if (options[propName][name] !== undefined)
65
- relevant[name] = options[propName][name];
61
+ if (options[name] !== undefined)
62
+ relevant[name] = options[name];
66
63
  }
67
64
 
68
65
  for (const name of relevantGeneralOptions ) {
@@ -70,10 +67,10 @@ function attachTransformerCharacteristics(csn, transformation, options,
70
67
  relevant[name] = options[name];
71
68
  }
72
69
  if (!csn.meta)
73
- setProp(csn, 'meta', {});
70
+ baseModel.setProp(csn, 'meta', {});
74
71
 
75
- setProp(csn.meta, 'options', relevant);
76
- setProp(csn.meta, 'transformation', transformation);
72
+ baseModel.setProp(csn.meta, 'options', relevant);
73
+ baseModel.setProp(csn.meta, 'transformation', transformation);
77
74
  }
78
75
 
79
76
  /**
@@ -92,7 +89,7 @@ function checkPreTransformedCsn(csn, options, relevantOptionNames, warnAboutMism
92
89
  // Not able to check
93
90
  return;
94
91
  }
95
- const { error, warning, throwWithAnyError } = makeMessageFunction(csn, options, module);
92
+ const { error, warning, throwWithAnyError } = messages.makeMessageFunction(csn, options, module);
96
93
 
97
94
  for (const name of relevantOptionNames ) {
98
95
  if (options[name] !== csn.meta.options[name])
@@ -128,7 +125,7 @@ function isPreTransformed(csn, transformation) {
128
125
  * @returns {object} Return an oData-pre-processed CSN
129
126
  */
130
127
  function odataInternal(csn, internalOptions) {
131
- const oDataCsn = transform4odataWithCsn(csn, internalOptions);
128
+ const oDataCsn = forOdataNew.transform4odataWithCsn(csn, internalOptions);
132
129
  attachTransformerCharacteristics(oDataCsn, 'odata', internalOptions, relevantOdataOptions, warnAboutMismatchOdata);
133
130
  return oDataCsn;
134
131
  }
@@ -150,13 +147,13 @@ function odata(csn, options = {}) {
150
147
  *
151
148
  * @param {object} csn CSN to process
152
149
  * @param {object} [externalOptions={}] Options
153
- * @returns {CDL} { <artifactName>: <CDL representation>, ...}
150
+ * @returns {object} { model: string, namespace: string, unappliedExtensions: string }
154
151
  */
155
152
  function cdl(csn, externalOptions = {}) {
156
153
  const internalOptions = prepareOptions.to.cdl(externalOptions);
157
- const { result } = backends.toCdlWithCsn(cloneCsnNonDict(csn, internalOptions), internalOptions);
158
- return result;
154
+ return toCdl.csnToCdl(cloneCsnNonDict(csn, internalOptions), internalOptions);
159
155
  }
156
+
160
157
  /**
161
158
  * Transform a CSN like to.sql
162
159
  *
@@ -168,8 +165,9 @@ function cdl(csn, externalOptions = {}) {
168
165
  function forSql(csn, options = {}) {
169
166
  const internalOptions = prepareOptions.to.sql(options);
170
167
  internalOptions.transformation = 'sql';
171
- internalOptions.toSql.csn = true;
172
- 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;
173
171
  }
174
172
  /**
175
173
  * Transform a CSN like to.hdi
@@ -181,8 +179,10 @@ function forSql(csn, options = {}) {
181
179
  */
182
180
  function forHdi(csn, options = {}) {
183
181
  const internalOptions = prepareOptions.to.hdi(options);
184
- internalOptions.toSql.csn = true;
185
- 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;
186
186
  }
187
187
  /**
188
188
  * Transform a CSN like to.hdbcds
@@ -196,9 +196,9 @@ function forHdbcds(csn, options = {}) {
196
196
  const internalOptions = prepareOptions.to.hdbcds(options);
197
197
  internalOptions.transformation = 'hdbcds';
198
198
 
199
- const hanaCsn = transformForHanaWithCsn(csn, internalOptions, 'to.hdbcds');
199
+ const hanaCsn = forHanaNew.transformForHanaWithCsn(csn, internalOptions, 'to.hdbcds');
200
200
 
201
- return internalOptions.testMode ? sortCsn(hanaCsn, internalOptions) : hanaCsn;
201
+ return internalOptions.testMode ? toCsn.sortCsn(hanaCsn, internalOptions) : hanaCsn;
202
202
  }
203
203
 
204
204
  /**
@@ -212,12 +212,16 @@ function sql(csn, options = {}) {
212
212
  const internalOptions = prepareOptions.to.sql(options);
213
213
  internalOptions.transformation = 'sql';
214
214
 
215
- // we need the CSN for view sorting
216
- 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\'');
217
219
 
218
- 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);
219
223
 
220
- const result = sortViews(intermediateResult);
224
+ const result = sortViews({ csn: transformedCsn, sql: sqls.sql });
221
225
 
222
226
  return result.map(obj => obj.sql).filter(create => create);
223
227
  }
@@ -233,12 +237,8 @@ function hdi(csn, options = {}) {
233
237
  const internalOptions = prepareOptions.to.hdi(options);
234
238
 
235
239
  // we need the CSN for view sorting
236
- internalOptions.toSql.csn = true;
237
-
238
- const intermediateResult = backends.toSqlWithCsn(csn, internalOptions);
239
-
240
- const sqlCSN = intermediateResult.csn;
241
- delete intermediateResult.csn;
240
+ const sqlCSN = forHdi(csn, options);
241
+ const sqls = toSql.toSqlDdl(sqlCSN, internalOptions);
242
242
 
243
243
  if (internalOptions.testMode) {
244
244
  // All this mapping is needed because sortViews crossmatches
@@ -246,7 +246,7 @@ function hdi(csn, options = {}) {
246
246
  // But we also need to return it with the correct file ending in the end
247
247
  // so remember and do lot's of mapping here.
248
248
 
249
- const flat = flattenResultStructure(intermediateResult);
249
+ const flat = flattenResultStructure(sqls);
250
250
 
251
251
  const nameMapping = Object.create(null);
252
252
  const sqlArtifactsWithCSNNamesToSort = Object.create(null);
@@ -277,7 +277,7 @@ function hdi(csn, options = {}) {
277
277
  return sorted;
278
278
  }
279
279
 
280
- return remapNames(flattenResultStructure(intermediateResult), sqlCSN, k => !k.endsWith('.hdbindex'));
280
+ return remapNames(flattenResultStructure(sqls), sqlCSN, k => !k.endsWith('.hdbindex'));
281
281
  }
282
282
  /**
283
283
  * Remap names so that they stay consistent between v1 and v2
@@ -343,45 +343,17 @@ function remapName(key, csn, filter = () => true) {
343
343
  * consists of a column drop and add).
344
344
  */
345
345
  function hdiMigration(csn, options, beforeImage) {
346
- /**
347
- * Swap arguments in case of inverted argument order.
348
- * This is for backward compatibility with @sap/cds@4.5.(2…3).
349
- *
350
- * @todo Remove in cds-compiler@2.x
351
- * @param {HdiOptions|CSN.Model} inputOptions Options or CSN image
352
- * @param {HdiOptions|CSN.Model} inputBeforeImage CSN image or options
353
- * @returns {Array} Array where the real options come first
354
- */
355
- function backwardCompatible(inputOptions, inputBeforeImage) {
356
- /**
357
- * Check whether the given argument is a CSN
358
- *
359
- * @param {object} arg Argument to verify
360
- * @returns {boolean} True if it is a CSN
361
- */
362
- function isBeforeImage(arg) {
363
- return arg === null || [ 'definitions', 'meta', '$version' ].some(key => key in arg);
364
- }
365
- return isBeforeImage(inputBeforeImage)
366
- ? [ inputOptions, inputBeforeImage ]
367
- : [ inputBeforeImage, inputOptions ];
368
- }
369
- [ options, beforeImage ] = backwardCompatible(options, beforeImage);
370
-
371
346
  const internalOptions = prepareOptions.to.hdi(options);
372
- internalOptions.toSql.csn = true;
373
347
 
374
348
  // Prepare after-image.
375
- // FIXME: Is this needed?
376
- // cloneCsnMessages(csn, options, internalOptions);
377
- const afterImage = backends.toSqlWithCsn(csn, internalOptions).csn;
349
+ const afterImage = forHdi(csn, options);
378
350
 
379
351
  // Compare both images.
380
- const diff = compareModels(beforeImage || afterImage, afterImage, internalOptions);
352
+ const diff = modelCompare.compareModels(beforeImage || afterImage, afterImage, internalOptions);
381
353
 
382
354
  // Convert the diff to SQL.
383
355
  internalOptions.forHana = true; // Make it pass the SQL rendering
384
- const { deletions, migrations, ...hdbkinds } = toSqlDdl(diff, internalOptions);
356
+ const { deletions, migrations, ...hdbkinds } = toSql.toSqlDdl(diff, internalOptions);
385
357
 
386
358
  return {
387
359
  afterImage,
@@ -440,14 +412,14 @@ hdi.migration = hdiMigration;
440
412
  * @returns {HDBCDS} { <filename>:<content>, ...}
441
413
  */
442
414
  function hdbcds(csn, options = {}) {
443
- timetrace.start('to.hdbcds');
415
+ timetrace.timetrace.start('to.hdbcds');
444
416
  const internalOptions = prepareOptions.to.hdbcds(options);
445
417
  internalOptions.transformation = 'hdbcds';
446
418
 
447
419
  const hanaCsn = forHdbcds(csn, internalOptions);
448
420
 
449
421
  const result = flattenResultStructure(toHdbcdsSource(hanaCsn, internalOptions));
450
- timetrace.stop();
422
+ timetrace.timetrace.stop();
451
423
  return result;
452
424
  }
453
425
  /**
@@ -469,11 +441,11 @@ function edm(csn, options = {}) {
469
441
  let servicesEdmj;
470
442
  if (isPreTransformed(csn, 'odata')) {
471
443
  checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
472
- servicesEdmj = backends.preparedCsnToEdm(csn, service, internalOptions);
444
+ servicesEdmj = preparedCsnToEdm(csn, service, internalOptions);
473
445
  }
474
446
  else {
475
447
  const oDataCsn = odataInternal(csn, internalOptions);
476
- servicesEdmj = backends.preparedCsnToEdm(oDataCsn, service, internalOptions);
448
+ servicesEdmj = preparedCsnToEdm(oDataCsn, service, internalOptions);
477
449
  }
478
450
  return servicesEdmj.edmj;
479
451
  }
@@ -489,9 +461,9 @@ edm.all = edmall;
489
461
  */
490
462
  function edmall(csn, options = {}) {
491
463
  const internalOptions = prepareOptions.to.edm(options);
492
- const { error } = makeMessageFunction(csn, internalOptions, 'for.odata');
464
+ const { error } = messages.makeMessageFunction(csn, internalOptions, 'for.odata');
493
465
 
494
- if (internalOptions.version === 'v2')
466
+ if (internalOptions.odataVersion === 'v2')
495
467
  error(null, null, 'OData JSON output is not available for OData V2');
496
468
 
497
469
  const result = {};
@@ -503,13 +475,11 @@ function edmall(csn, options = {}) {
503
475
  else
504
476
  oDataCsn = odataInternal(csn, internalOptions);
505
477
 
506
- const servicesJson = backends.preparedCsnToEdmAll(oDataCsn, internalOptions);
478
+ const servicesJson = preparedCsnToEdmAll(oDataCsn, internalOptions);
507
479
  const services = servicesJson.edmj;
508
- for (const serviceName in services) {
509
- const lEdm = services[serviceName];
510
- // FIXME: Why only metadata_json - isn't this rather a 'combined_json' ? If so, rename it!
511
- result[serviceName] = lEdm;
512
- }
480
+ for (const serviceName in services)
481
+ result[serviceName] = services[serviceName];
482
+
513
483
  return result;
514
484
  }
515
485
  /**
@@ -531,11 +501,11 @@ function edmx(csn, options = {}) {
531
501
  let services;
532
502
  if (isPreTransformed(csn, 'odata')) {
533
503
  checkPreTransformedCsn(csn, internalOptions, relevantOdataOptions, warnAboutMismatchOdata, 'for.odata');
534
- services = backends.preparedCsnToEdmx(csn, service, internalOptions);
504
+ services = preparedCsnToEdmx(csn, service, internalOptions);
535
505
  }
536
506
  else {
537
507
  const oDataCsn = odataInternal(csn, internalOptions);
538
- services = backends.preparedCsnToEdmx(oDataCsn, service, internalOptions);
508
+ services = preparedCsnToEdmx(oDataCsn, service, internalOptions);
539
509
  }
540
510
 
541
511
  return services.edmx;
@@ -562,7 +532,7 @@ function edmxall(csn, options = {}) {
562
532
  else
563
533
  oDataCsn = odataInternal(csn, internalOptions);
564
534
 
565
- const servicesEdmx = backends.preparedCsnToEdmxAll(oDataCsn, internalOptions);
535
+ const servicesEdmx = preparedCsnToEdmxAll(oDataCsn, internalOptions);
566
536
  const services = servicesEdmx.edmx;
567
537
  // Create annotations and metadata once per service
568
538
  for (const serviceName in services) {
@@ -573,6 +543,79 @@ function edmxall(csn, options = {}) {
573
543
  return result;
574
544
  }
575
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
+
576
619
  /**
577
620
  * Flatten the result structure to a flat map.
578
621
  *
@@ -606,7 +649,9 @@ module.exports = {
606
649
  for_sql: publishCsnProcessor(forSql, 'for.sql'),
607
650
  for_hdi: publishCsnProcessor(forHdi, 'for.hdi'),
608
651
  for_hdbcds: publishCsnProcessor(forHdbcds, 'for.hdbcds'),
609
- /** */
652
+ /** Deprecated, will be removed in cds-compiler@v4 */
653
+ preparedCsnToEdmx,
654
+ preparedCsnToEdm,
610
655
  };
611
656
 
612
657
 
@@ -639,29 +684,101 @@ function publishCsnProcessor( processor, _name ) {
639
684
  */
640
685
  function api( csn, options = {}, ...args ) {
641
686
  try {
687
+ if (options.deprecated) {
688
+ const messageFunctions = messages.makeMessageFunction(csn, options, 'api');
689
+ checkRemovedDeprecatedFlags( options, messageFunctions );
690
+ }
691
+ checkOutdatedOptions( options );
642
692
  return processor( csn, options, ...args );
643
693
  }
644
694
  catch (err) {
645
- 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
646
696
  throw err;
647
697
 
648
698
  if (options.testMode && !(err instanceof TypeError) &&
649
699
  !(err instanceof ModelError))
650
700
  throw err;
651
701
 
652
- const { info } = makeMessageFunction( csn, options, 'compile' );
653
- 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' );
654
704
  if (options.internalMsg)
655
705
  msg.error = err; // Attach original error
656
706
 
657
707
  // next line to be replaced by CSN parser call which reads the CSN object
658
- const xsn = recompileX(csn, options);
659
- const recompiledCsn = compactModel(xsn);
708
+ const xsn = compiler.recompileX(csn, options);
709
+ const recompiledCsn = toCsn.compactModel(xsn);
660
710
  return processor( recompiledCsn, options, ...args );
661
711
  }
662
712
  }
663
713
  }
664
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
+
665
782
 
666
783
  /**
667
784
  * Option format used by the old API, where they are grouped thematically.