@sap/cds-compiler 4.0.2 → 4.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/CHANGELOG.md +200 -5
  2. package/bin/cdsc.js +18 -15
  3. package/doc/CHANGELOG_BETA.md +16 -0
  4. package/doc/CHANGELOG_DEPRECATED.md +15 -0
  5. package/lib/api/main.js +33 -13
  6. package/lib/api/options.js +2 -2
  7. package/lib/api/validate.js +25 -25
  8. package/lib/base/location.js +6 -7
  9. package/lib/base/message-registry.js +123 -42
  10. package/lib/base/messages.js +18 -10
  11. package/lib/base/model.js +43 -10
  12. package/lib/checks/defaultValues.js +6 -6
  13. package/lib/checks/elements.js +11 -10
  14. package/lib/checks/foreignKeys.js +0 -5
  15. package/lib/checks/manyNavigations.js +33 -0
  16. package/lib/checks/onConditions.js +22 -14
  17. package/lib/checks/queryNoDbArtifacts.js +132 -73
  18. package/lib/checks/selectItems.js +4 -55
  19. package/lib/checks/sql-snippets.js +15 -4
  20. package/lib/checks/types.js +3 -3
  21. package/lib/checks/utils.js +4 -3
  22. package/lib/checks/validator.js +3 -1
  23. package/lib/compiler/.eslintrc.json +2 -1
  24. package/lib/compiler/assert-consistency.js +71 -40
  25. package/lib/compiler/base.js +7 -2
  26. package/lib/compiler/builtins.js +40 -41
  27. package/lib/compiler/checks.js +415 -367
  28. package/lib/compiler/classes.js +62 -0
  29. package/lib/compiler/cycle-detector.js +9 -9
  30. package/lib/compiler/define.js +124 -90
  31. package/lib/compiler/extend.js +115 -88
  32. package/lib/compiler/finalize-parse-cdl.js +26 -25
  33. package/lib/compiler/generate.js +57 -49
  34. package/lib/compiler/index.js +56 -56
  35. package/lib/compiler/kick-start.js +10 -7
  36. package/lib/compiler/moduleLayers.js +1 -1
  37. package/lib/compiler/populate.js +180 -144
  38. package/lib/compiler/propagator.js +10 -9
  39. package/lib/compiler/resolve.js +321 -246
  40. package/lib/compiler/shared.js +812 -433
  41. package/lib/compiler/tweak-assocs.js +114 -50
  42. package/lib/compiler/utils.js +241 -46
  43. package/lib/edm/.eslintrc.json +40 -1
  44. package/lib/edm/annotations/genericTranslation.js +721 -707
  45. package/lib/edm/annotations/preprocessAnnotations.js +88 -77
  46. package/lib/edm/csn2edm.js +389 -378
  47. package/lib/edm/edm.js +679 -770
  48. package/lib/edm/edmAnnoPreprocessor.js +132 -146
  49. package/lib/edm/edmInboundChecks.js +29 -27
  50. package/lib/edm/edmPreprocessor.js +689 -648
  51. package/lib/edm/edmUtils.js +279 -300
  52. package/lib/gen/Dictionary.json +34 -10
  53. package/lib/gen/language.checksum +1 -1
  54. package/lib/gen/language.interp +1 -1
  55. package/lib/gen/languageParser.js +2857 -2856
  56. package/lib/json/from-csn.js +77 -51
  57. package/lib/json/to-csn.js +15 -15
  58. package/lib/language/antlrParser.js +2 -1
  59. package/lib/language/genericAntlrParser.js +52 -43
  60. package/lib/language/language.g4 +61 -64
  61. package/lib/language/multiLineStringParser.js +2 -0
  62. package/lib/main.d.ts +65 -0
  63. package/lib/model/csnRefs.js +37 -19
  64. package/lib/model/csnUtils.js +51 -18
  65. package/lib/model/revealInternalProperties.js +30 -22
  66. package/lib/modelCompare/compare.js +149 -41
  67. package/lib/modelCompare/utils/filter.js +55 -25
  68. package/lib/optionProcessor.js +21 -9
  69. package/lib/render/manageConstraints.js +20 -17
  70. package/lib/render/toCdl.js +63 -23
  71. package/lib/render/toHdbcds.js +2 -2
  72. package/lib/render/toRename.js +4 -9
  73. package/lib/render/toSql.js +82 -35
  74. package/lib/render/utils/common.js +11 -9
  75. package/lib/render/utils/unique.js +52 -0
  76. package/lib/transform/db/applyTransformations.js +62 -21
  77. package/lib/transform/db/assertUnique.js +7 -8
  78. package/lib/transform/db/associations.js +2 -2
  79. package/lib/transform/db/cdsPersistence.js +9 -9
  80. package/lib/transform/db/constraints.js +47 -17
  81. package/lib/transform/db/expansion.js +138 -68
  82. package/lib/transform/db/flattening.js +98 -30
  83. package/lib/transform/db/rewriteCalculatedElements.js +20 -14
  84. package/lib/transform/db/temporal.js +1 -1
  85. package/lib/transform/db/transformExists.js +8 -7
  86. package/lib/transform/db/views.js +73 -33
  87. package/lib/transform/draft/db.js +11 -9
  88. package/lib/transform/draft/odata.js +1 -1
  89. package/lib/transform/{forOdataNew.js → forOdata.js} +10 -7
  90. package/lib/transform/forRelationalDB.js +148 -136
  91. package/lib/transform/localized.js +92 -54
  92. package/lib/transform/odata/toFinalBaseType.js +3 -3
  93. package/lib/transform/{transformUtilsNew.js → transformUtils.js} +13 -111
  94. package/lib/transform/translateAssocsToJoins.js +14 -28
  95. package/lib/utils/file.js +7 -7
  96. package/lib/utils/moduleResolve.js +210 -121
  97. package/lib/utils/objectUtils.js +1 -1
  98. package/package.json +5 -5
  99. package/share/messages/check-proper-type-of.md +1 -1
  100. package/share/messages/{check-proper-type.md → def-missing-type.md} +3 -5
  101. package/share/messages/message-explanations.json +1 -1
@@ -4,7 +4,7 @@ const { makeMessageFunction } = require('../../base/messages.js');
4
4
  const { forEachDefinition, forEachGeneric } = require('../../model/csnUtils.js');
5
5
 
6
6
 
7
- /**************************************************************************************************
7
+ /** ************************************************************************************************
8
8
  * preprocessAnnotations
9
9
  *
10
10
  * options:
@@ -14,9 +14,9 @@ const { forEachDefinition, forEachGeneric } = require('../../model/csnUtils.js')
14
14
  * try to proceed with the processing as good as possible.
15
15
  *
16
16
  */
17
- function preprocessAnnotations(csn, serviceName, options) {
17
+ function preprocessAnnotations( csn, serviceName, options ) {
18
18
  const { message } = makeMessageFunction(csn, options);
19
- let fkSeparator = '_';
19
+ const fkSeparator = '_';
20
20
 
21
21
  resolveShortcuts();
22
22
 
@@ -32,19 +32,20 @@ function preprocessAnnotations(csn, serviceName, options) {
32
32
  }
33
33
 
34
34
  // return value can be null is target has no key
35
- function getKeyOfTargetOfManagedAssoc(anno, assoc) {
35
+ function getKeyOfTargetOfManagedAssoc( anno, assoc ) {
36
36
  // assoc.target can be the name of the target or the object itself
37
37
  const targetName = (typeof assoc.target === 'object') ? assoc.target.name : assoc.target;
38
- const target = (typeof assoc.target === 'object') ? assoc.target : csn.definitions[assoc.target];
38
+ const target = (typeof assoc.target === 'object') ? assoc.target : csn.definitions[assoc.target];
39
39
 
40
40
  const keyNames = Object.keys(target.elements).filter(x => target.elements[x].key && !target.elements[x].target);
41
41
  if (keyNames.length === 0) {
42
42
  keyNames.push('MISSING');
43
43
  message('odata-anno-preproc', null, { anno, name: targetName, '#': 'nokey' },
44
- 'target $(NAME) has no key');
44
+ 'target $(NAME) has no key');
45
45
  }
46
- else if (keyNames.length > 1)
46
+ else if (keyNames.length > 1) {
47
47
  message('odata-anno-preproc', null, { anno, name: targetName, '#': 'multkeys' });
48
+ }
48
49
 
49
50
  return keyNames[0];
50
51
  }
@@ -58,28 +59,33 @@ function preprocessAnnotations(csn, serviceName, options) {
58
59
  function resolveShortcuts() {
59
60
  forEachDefinition(csn, (artifact, artifactName) => {
60
61
  const location = [ 'definitions', artifactName ];
61
- if(artifactName === serviceName || artifactName.startsWith(serviceName + '.')) {
62
+ if (artifactName === serviceName || artifactName.startsWith(`${serviceName}.`)) {
62
63
  handleAnnotations(artifactName, artifactName, artifact, location);
63
- artifact.elements && Object.entries(artifact.elements).forEach(([elementName, element]) => {
64
- handleAnnotations(artifactName, elementName, element, [ ...location, 'elements', elementName ]);
65
- });
66
- artifact.params && Object.entries(artifact.params).forEach(([paramName, param]) => {
67
- handleAnnotations(artifactName, paramName, param, [ ...location, 'actions', artifactName, 'params', paramName ]);
68
- });
69
- forEachGeneric(artifact, 'actions', (action, actionName) => {
70
- action.params && Object.entries(action.params).forEach(([paramName, param]) => {
71
- handleAnnotations(actionName, paramName, param, [ ...location, 'actions', actionName, 'params', paramName ]);
64
+ if (artifact.elements) {
65
+ Object.entries(artifact.elements).forEach(([ elementName, element ]) => {
66
+ handleAnnotations(artifactName, elementName, element, [ ...location, 'elements', elementName ]);
72
67
  });
68
+ }
69
+ if (artifact.params) {
70
+ Object.entries(artifact.params).forEach(([ paramName, param ]) => {
71
+ handleAnnotations(artifactName, paramName, param, [ ...location, 'actions', artifactName, 'params', paramName ]);
72
+ });
73
+ }
74
+ forEachGeneric(artifact, 'actions', (action, actionName) => {
75
+ if (action.params) {
76
+ Object.entries(action.params).forEach(([ paramName, param ]) => {
77
+ handleAnnotations(actionName, paramName, param, [ ...location, 'actions', actionName, 'params', paramName ]);
78
+ });
79
+ }
73
80
  });
74
81
  }
75
82
  });
76
83
 
77
- function handleAnnotations(defName, carrierName, carrier, location) {
78
-
84
+ function handleAnnotations( defName, carrierName, carrier, location ) {
79
85
  // collect the names of the carrier's annotation properties
80
- const annoNames = Object.keys(carrier).filter( x => x[0] === '@')
86
+ const annoNames = Object.keys(carrier).filter( x => x[0] === '@');
81
87
 
82
- annoNames.forEach(aName => {
88
+ annoNames.forEach((aName) => {
83
89
  const aNameWithoutQualifier = aName.split('#')[0];
84
90
 
85
91
  // Always - draft annotations, value is action name
@@ -97,45 +103,45 @@ function preprocessAnnotations(csn, serviceName, options) {
97
103
  });
98
104
 
99
105
  // inner functions
100
- function draftAnnotations(aName, aNameWithoutQualifier) {
106
+ function draftAnnotations( aName, aNameWithoutQualifier ) {
101
107
  if ((carrier.kind === 'entity') &&
102
108
  (aNameWithoutQualifier === '@Common.DraftRoot.PreparationAction' ||
103
109
  aNameWithoutQualifier === '@Common.DraftRoot.ActivationAction' ||
104
110
  aNameWithoutQualifier === '@Common.DraftRoot.EditAction' ||
105
111
  aNameWithoutQualifier === '@Common.DraftNode.PreparationAction')
106
- ) {
112
+ ) {
107
113
  let value = carrier[aName];
108
114
  // prefix with service name, if not already done
109
- if (value === 'draftPrepare' || value === 'draftActivate' || value === 'draftEdit') {
115
+ if (value === 'draftPrepare' || value === 'draftActivate' || value === 'draftEdit') {
110
116
  // mocha test has no whatsMySchemaName
111
117
  const schemaName = options.whatsMySchemaName && options.whatsMySchemaName(carrierName) || serviceName;
112
- value = carrier[aName] = schemaName + '.' + value;
118
+ carrier[aName] = `${schemaName}.${value}`;
119
+ value = carrier[aName];
113
120
  }
114
121
  // for v2: function imports live inside EntityContainer -> path needs to contain "EntityContainer/"
115
122
  // we decided to prefix names of bound action/functions with entity name -> needs to be reflected in path, too
116
123
  if (isV2()) {
117
- let entityNameShort = carrierName.split('.').pop();
118
- carrier[aName] = value.replace(/(draft(Prepare|Activate|Edit))$/, (match, p1) => 'EntityContainer/' + entityNameShort + '_' + p1)
124
+ const entityNameShort = carrierName.split('.').pop();
125
+ carrier[aName] = value.replace(/(draft(Prepare|Activate|Edit))$/, (match, p1) => `EntityContainer/${entityNameShort}_${p1}`);
119
126
  }
120
127
  }
121
128
  }
122
129
 
123
- function fixedValueListShortCut(anno) {
130
+ function fixedValueListShortCut( anno ) {
124
131
  if (anno === '@Common.ValueList.entity' ||
125
132
  anno === '@Common.ValueList.viaAssociation') {
126
-
127
133
  const _fixedValueListShortCut = () => {
128
134
  // note: we loop over all annotations that were originally present, even if they are
129
135
  // removed from the carrier via this handler
130
136
  // we don't remove anything from the array "annoNames"
131
137
 
132
138
  // if CollectionPath is explicitly given, no shortcut expansion is made
133
- if (carrier['@Common.ValueList.CollectionPath']) {
139
+ if (carrier['@Common.ValueList.CollectionPath'])
134
140
  return false;
135
- }
141
+
136
142
 
137
143
  if (carrier.kind === 'entity') {
138
- message('odata-anno-preproc', [...location, anno], { anno, '#': 'notforentity' });
144
+ message('odata-anno-preproc', [ ...location, anno ], { anno, '#': 'notforentity' });
139
145
  return false;
140
146
  }
141
147
 
@@ -149,12 +155,12 @@ function preprocessAnnotations(csn, serviceName, options) {
149
155
  // value is expected to be an expression, namely the path to an association of the carrier entity
150
156
  const assocName = carrier['@Common.ValueList.viaAssociation']['='];
151
157
  if (!assocName) {
152
- message('odata-anno-preproc', [...location, anno], { anno, '#': 'viaassoc' });
158
+ message('odata-anno-preproc', [ ...location, anno ], { anno, '#': 'viaassoc' });
153
159
  return false;
154
160
  }
155
161
  const assoc = csn.definitions[defName].elements[assocName];
156
162
  if (!assoc || !assoc.target) {
157
- message('odata-anno-preproc', [...location, anno], { anno, id: assocName, '#': 'noassoc' });
163
+ message('odata-anno-preproc', [ ...location, anno ], { anno, id: assocName, '#': 'noassoc' });
158
164
  return false;
159
165
  }
160
166
 
@@ -163,29 +169,31 @@ function preprocessAnnotations(csn, serviceName, options) {
163
169
  }
164
170
  else if (anno === '@Common.ValueList.entity') {
165
171
  // if both annotations are present, ignore 'entity' and raise a message
166
- if (annoNames.map(x=>x.split('#')[0]).find(x=>(x==='@Common.ValueList.viaAssociation'))) {
167
- message('odata-anno-preproc', [...location, anno],
168
- {
169
- name: '@Common.ValueList.entity', anno: '@Common.ValueList',
170
- value: 'entity', code: 'viaAssociation', '#': 'vallistignored'
171
- });
172
+ if (annoNames.map(x => x.split('#')[0]).find(x => (x === '@Common.ValueList.viaAssociation'))) {
173
+ message('odata-anno-preproc', [ ...location, anno ],
174
+ {
175
+ name: '@Common.ValueList.entity',
176
+ anno: '@Common.ValueList',
177
+ value: 'entity',
178
+ code: 'viaAssociation',
179
+ '#': 'vallistignored',
180
+ });
172
181
  return false;
173
182
  }
174
183
 
175
184
  const annoVal = carrier['@Common.ValueList.entity']; // name of value list entity
176
- if (annoVal['=']) {
177
- message('odata-anno-preproc', [...location, anno], { anno, '#': 'notastring' },
178
- );
179
- }
185
+ if (annoVal['='])
186
+ message('odata-anno-preproc', [ ...location, anno ], { anno, '#': 'notastring' });
187
+
180
188
  // mocha test has no whatsMySchemaName
181
- const schemaName = options.whatsMySchemaName && options.whatsMySchemaName(defName) || serviceName
189
+ const schemaName = options.whatsMySchemaName && options.whatsMySchemaName(defName) || serviceName;
182
190
  enameShort = annoVal['='] || annoVal;
183
- enameFull = schemaName + '.' + enameShort;
191
+ enameFull = `${schemaName}.${enameShort}`;
184
192
  }
185
193
 
186
194
  const vlEntity = csn.definitions[enameFull]; // (object) value list entity
187
195
  if (!vlEntity) {
188
- message('odata-anno-preproc', [...location, anno ], { anno, id: enameFull, '#': 'notexist' });
196
+ message('odata-anno-preproc', [ ...location, anno ], { anno, id: enameFull, '#': 'notexist' });
189
197
  return false;
190
198
  }
191
199
 
@@ -205,11 +213,10 @@ function preprocessAnnotations(csn, serviceName, options) {
205
213
 
206
214
  // if this carrier is a generated foreign key field and the association is marked @cds.api.ignore
207
215
  // rename the localDataProp to be 'assocName/key'
208
- if(carrier['@cds.api.ignore']) {
216
+ if (carrier['@cds.api.ignore']) {
209
217
  const assocName = carrier['@odata.foreignKey4'];
210
- if(assocName && options.isV4()) {
211
- localDataProp = localDataProp.replace(assocName+fkSeparator, assocName+'/');
212
- }
218
+ if (assocName && options.isV4())
219
+ localDataProp = localDataProp.replace(assocName + fkSeparator, `${assocName}/`);
213
220
  }
214
221
 
215
222
  // valueListProp: the (single) key field of the value list entity
@@ -217,11 +224,12 @@ function preprocessAnnotations(csn, serviceName, options) {
217
224
  let valueListProp = null;
218
225
  const keys = Object.keys(vlEntity.elements).filter( x => vlEntity.elements[x].key && !vlEntity.elements[x].target );
219
226
  if (keys.length === 0) {
220
- message('odata-anno-preproc', [...location, anno], { anno, name: enameFull, '#': 'vhlnokey' });
227
+ message('odata-anno-preproc', [ ...location, anno ], { anno, name: enameFull, '#': 'vhlnokey' });
221
228
  return false;
222
229
  }
223
- else if (keys.length > 1)
224
- message('odata-anno-preproc', [...location, anno], { anno, name: enameFull, '#': 'vhlmultkeys' });
230
+ else if (keys.length > 1) {
231
+ message('odata-anno-preproc', [ ...location, anno ], { anno, name: enameFull, '#': 'vhlmultkeys' });
232
+ }
225
233
  valueListProp = keys[0];
226
234
 
227
235
  // textField:
@@ -234,11 +242,14 @@ function preprocessAnnotations(csn, serviceName, options) {
234
242
  const Identification = vlEntity['@UI.Identification'];
235
243
  if (Identification && Identification[0] && Identification[0]['=']) {
236
244
  textField = Identification[0]['='];
237
- } else if (Identification && Identification[0] && Identification[0]['Value'] && Identification[0]['Value']['=']) {
238
- textField = Identification[0]['Value']['='];
239
- } else {
245
+ }
246
+ else if (Identification && Identification[0] && Identification[0].Value && Identification[0].Value['=']) {
247
+ textField = Identification[0].Value['='];
248
+ }
249
+ else {
240
250
  const stringFields = Object.keys(vlEntity.elements).filter(
241
- x => !vlEntity.elements[x].key && vlEntity.elements[x].type === 'cds.String')
251
+ x => !vlEntity.elements[x].key && vlEntity.elements[x].type === 'cds.String'
252
+ );
242
253
  if (stringFields.length === 1)
243
254
  textField = stringFields[0];
244
255
  }
@@ -246,21 +257,21 @@ function preprocessAnnotations(csn, serviceName, options) {
246
257
  // explicitly provided parameters win
247
258
  let parameters = carrier['@Common.ValueList.Parameters'];
248
259
  if (!parameters) {
249
- parameters = [{
250
- '$Type': 'Common.ValueListParameterInOut',
251
- 'LocalDataProperty' : { '=' : localDataProp },
252
- 'ValueListProperty' : valueListProp
253
- }];
260
+ parameters = [ {
261
+ $Type: 'Common.ValueListParameterInOut',
262
+ LocalDataProperty: { '=': localDataProp },
263
+ ValueListProperty: valueListProp,
264
+ } ];
254
265
  if (textField) {
255
266
  parameters[1] = {
256
- '$Type': 'Common.ValueListParameterDisplayOnly',
257
- 'ValueListProperty' : textField
267
+ $Type: 'Common.ValueListParameterDisplayOnly',
268
+ ValueListProperty: textField,
258
269
  };
259
270
  }
260
271
  }
261
272
 
262
273
  const newObj = Object.create( Object.getPrototypeOf(carrier) );
263
- Object.keys(carrier).forEach( e => {
274
+ Object.keys(carrier).forEach( (e) => {
264
275
  if (e === '@Common.ValueList.entity' || e === '@Common.ValueList.viaAssociation') {
265
276
  newObj['@Common.ValueList.Label'] = label;
266
277
  newObj['@Common.ValueList.CollectionPath'] = enameShort;
@@ -278,7 +289,7 @@ function preprocessAnnotations(csn, serviceName, options) {
278
289
  });
279
290
  Object.assign(carrier, newObj);
280
291
  return true;
281
- }
292
+ };
282
293
 
283
294
  const success = _fixedValueListShortCut();
284
295
  if (!success) {
@@ -289,23 +300,23 @@ function preprocessAnnotations(csn, serviceName, options) {
289
300
  }
290
301
  }
291
302
 
292
- function textArrangementReordering(aName, aNameWithoutQualifier) {
303
+ function textArrangementReordering( aName, aNameWithoutQualifier ) {
293
304
  if (aNameWithoutQualifier === '@Common.TextArrangement') {
294
- let value = carrier[aName];
295
- let textAnno = carrier['@Common.Text'];
305
+ const value = carrier[aName];
306
+ const textAnno = carrier['@Common.Text'];
296
307
  // can only occur if there is a @Common.Text annotation at the same target
297
- if (!textAnno) {
298
- message('odata-anno-preproc', [...location, '@Common.TextArrangement'], { anno: '@Common.TextArrangement', name: '@Common.Text', '#': 'txtarr' });
299
- }
308
+ if (!textAnno)
309
+ message('odata-anno-preproc', [ ...location, '@Common.TextArrangement' ], { anno: '@Common.TextArrangement', name: '@Common.Text', '#': 'txtarr' });
300
310
 
301
- //change the scalar anno into a "pseudo-structured" one
311
+
312
+ // change the scalar anno into a "pseudo-structured" one
302
313
  // TODO should be flattened, but then alphabetical order is destroyed
303
314
 
304
315
  // Do not overwrite existing nested annotation values, instead give existing
305
316
  // nested annotation precedence and remove outer annotation (always)
306
- if(!carrier['@Common.Text.@UI.TextArrangement'] && textAnno) {
307
- carrier['@Common.Text'] = { '$value': textAnno, '@UI.TextArrangement': value };
308
- }
317
+ if (!carrier['@Common.Text.@UI.TextArrangement'] && textAnno)
318
+ carrier['@Common.Text'] = { $value: textAnno, '@UI.TextArrangement': value };
319
+
309
320
  delete carrier[aName];
310
321
  }
311
322
  }