@sap/cds-compiler 3.1.2 → 3.3.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 (100) hide show
  1. package/CHANGELOG.md +80 -3
  2. package/bin/cdsc.js +1 -1
  3. package/doc/CHANGELOG_BETA.md +18 -0
  4. package/lib/api/main.js +8 -13
  5. package/lib/base/error.js +2 -2
  6. package/lib/base/keywords.js +2 -24
  7. package/lib/base/message-registry.js +43 -14
  8. package/lib/base/messages.js +20 -10
  9. package/lib/base/model.js +1 -1
  10. package/lib/checks/actionsFunctions.js +1 -1
  11. package/lib/checks/annotationsOData.js +2 -2
  12. package/lib/checks/arrayOfs.js +15 -7
  13. package/lib/checks/cdsPersistence.js +1 -1
  14. package/lib/checks/checkForTypes.js +48 -0
  15. package/lib/checks/defaultValues.js +2 -2
  16. package/lib/checks/elements.js +81 -6
  17. package/lib/checks/foreignKeys.js +12 -13
  18. package/lib/checks/invalidTarget.js +10 -11
  19. package/lib/checks/managedInType.js +21 -15
  20. package/lib/checks/nullableKeys.js +1 -1
  21. package/lib/checks/onConditions.js +9 -9
  22. package/lib/checks/parameters.js +21 -0
  23. package/lib/checks/selectItems.js +1 -1
  24. package/lib/checks/types.js +2 -2
  25. package/lib/checks/utils.js +17 -7
  26. package/lib/checks/validator.js +26 -14
  27. package/lib/compiler/assert-consistency.js +13 -6
  28. package/lib/compiler/builtins.js +8 -0
  29. package/lib/compiler/checks.js +40 -33
  30. package/lib/compiler/define.js +50 -44
  31. package/lib/compiler/extend.js +303 -37
  32. package/lib/compiler/kick-start.js +2 -35
  33. package/lib/compiler/populate.js +83 -62
  34. package/lib/compiler/propagator.js +1 -1
  35. package/lib/compiler/resolve.js +61 -104
  36. package/lib/compiler/shared.js +16 -6
  37. package/lib/compiler/tweak-assocs.js +25 -12
  38. package/lib/compiler/utils.js +2 -2
  39. package/lib/edm/annotations/genericTranslation.js +3 -3
  40. package/lib/edm/csn2edm.js +10 -10
  41. package/lib/edm/edm.js +17 -9
  42. package/lib/edm/edmPreprocessor.js +53 -30
  43. package/lib/edm/edmUtils.js +7 -2
  44. package/lib/gen/Dictionary.json +14 -0
  45. package/lib/gen/language.checksum +1 -1
  46. package/lib/gen/language.interp +3 -2
  47. package/lib/gen/languageParser.js +4205 -4100
  48. package/lib/inspect/inspectModelStatistics.js +1 -1
  49. package/lib/inspect/inspectPropagation.js +23 -9
  50. package/lib/json/csnVersion.js +1 -1
  51. package/lib/json/from-csn.js +26 -19
  52. package/lib/json/to-csn.js +47 -5
  53. package/lib/language/antlrParser.js +1 -1
  54. package/lib/language/genericAntlrParser.js +29 -13
  55. package/lib/language/language.g4 +28 -8
  56. package/lib/main.d.ts +3 -6
  57. package/lib/model/.eslintrc.json +13 -0
  58. package/lib/model/api.js +4 -2
  59. package/lib/model/csnRefs.js +74 -47
  60. package/lib/model/csnUtils.js +236 -218
  61. package/lib/model/enrichCsn.js +41 -31
  62. package/lib/model/revealInternalProperties.js +61 -57
  63. package/lib/model/sortViews.js +31 -31
  64. package/lib/modelCompare/compare.js +6 -6
  65. package/lib/optionProcessor.js +5 -0
  66. package/lib/render/manageConstraints.js +2 -2
  67. package/lib/render/toCdl.js +31 -44
  68. package/lib/render/toHdbcds.js +7 -5
  69. package/lib/render/toRename.js +4 -4
  70. package/lib/render/toSql.js +11 -5
  71. package/lib/render/utils/common.js +20 -9
  72. package/lib/render/utils/sql.js +5 -5
  73. package/lib/transform/db/applyTransformations.js +32 -3
  74. package/lib/transform/db/expansion.js +81 -37
  75. package/lib/transform/db/flattening.js +1 -1
  76. package/lib/transform/db/temporal.js +1 -1
  77. package/lib/transform/db/transformExists.js +1 -1
  78. package/lib/transform/forOdataNew.js +10 -7
  79. package/lib/transform/{forHanaNew.js → forRelationalDB.js} +7 -7
  80. package/lib/transform/localized.js +28 -19
  81. package/lib/transform/odata/toFinalBaseType.js +8 -11
  82. package/lib/transform/odata/typesExposure.js +1 -1
  83. package/lib/transform/transformUtilsNew.js +101 -39
  84. package/lib/transform/translateAssocsToJoins.js +5 -4
  85. package/lib/utils/moduleResolve.js +5 -5
  86. package/lib/utils/objectUtils.js +3 -3
  87. package/package.json +2 -2
  88. package/share/messages/anno-duplicate-unrelated-layer.md +6 -6
  89. package/share/messages/check-proper-type-of.md +4 -4
  90. package/share/messages/check-proper-type.md +2 -2
  91. package/share/messages/duplicate-autoexposed.md +4 -4
  92. package/share/messages/extend-repeated-intralayer.md +4 -5
  93. package/share/messages/extend-unrelated-layer.md +4 -4
  94. package/share/messages/message-explanations.json +3 -1
  95. package/share/messages/redirected-to-ambiguous.md +7 -6
  96. package/share/messages/redirected-to-complex.md +63 -0
  97. package/share/messages/redirected-to-unrelated.md +6 -5
  98. package/share/messages/rewrite-not-supported.md +4 -4
  99. package/share/messages/syntax-expected-integer.md +3 -3
  100. package/share/messages/wildcard-excluding-one.md +37 -0
@@ -1,13 +1,17 @@
1
1
  'use strict';
2
2
 
3
3
  const { csnRefs, implicitAs } = require('../model/csnRefs');
4
- const { applyTransformations, applyTransformationsOnNonDictionary } = require('../transform/db/applyTransformations');
5
- const { isBuiltinType } = require('../compiler/builtins.js')
6
- const { sortCsn, cloneCsnDictionary: _cloneCsnDictionary } = require('../json/to-csn');
7
- const { ModelError } = require("../base/error");
8
- const { typeParameters } = require("../compiler/builtins");
4
+ const { applyTransformations, applyTransformationsOnNonDictionary, applyTransformationsOnDictionary } = require('../transform/db/applyTransformations');
5
+ const { isBuiltinType } = require('../compiler/builtins.js');
6
+ const {
7
+ sortCsn,
8
+ cloneCsnDictionary: _cloneCsnDictionary,
9
+ cloneAnnotationValue: _cloneAnnotationValue,
10
+ } = require('../json/to-csn');
11
+ const { ModelError } = require('../base/error');
12
+ const { typeParameters } = require('../compiler/builtins');
9
13
  const { forEach } = require('../utils/objectUtils');
10
- const version = require('../../package.json').version;
14
+ const { version } = require('../../package.json');
11
15
 
12
16
  // Low-level utility functions to work with compact CSN.
13
17
 
@@ -87,7 +91,7 @@ function getUtils(model, universalReady) {
87
91
  * @param {boolean} [isSubquery]
88
92
  * @returns {object} Map of sources
89
93
  */
90
- function getSources(query, isSubquery=false) {
94
+ function getSources(query, isSubquery = false) {
91
95
  // Remark CW: better just a while along query.SET.args[0]
92
96
  if (query.SET) {
93
97
  if (query.SET.args[0].SELECT && query.SET.args[0].SELECT.elements)
@@ -102,15 +106,15 @@ function getUtils(model, universalReady) {
102
106
  else if (query.SELECT.from.ref) {
103
107
  let art = artifactRef(query.SELECT.from);
104
108
 
105
- if(art.target)
109
+ if (art.target)
106
110
  art = artifactRef(art.target);
107
111
 
108
- if(isSubquery && !query.SELECT.elements)
112
+ if (isSubquery && !query.SELECT.elements)
109
113
  throw new ModelError('Expected subquery to have .elements');
110
114
 
111
115
  return mergeElementsIntoMap(Object.create(null), isSubquery ? query.SELECT.elements : art.elements, art.$location,
112
- query.SELECT.from.as || query.SELECT.from.ref[query.SELECT.from.ref.length - 1],
113
- query.SELECT.from.ref[query.SELECT.from.ref.length - 1] || query.SELECT.from.as );
116
+ query.SELECT.from.as || query.SELECT.from.ref[query.SELECT.from.ref.length - 1],
117
+ query.SELECT.from.ref[query.SELECT.from.ref.length - 1] || query.SELECT.from.as );
114
118
  }
115
119
  else if (query.SELECT.from.SET || query.SELECT.from.SELECT) {
116
120
  return getSources(query.SELECT.from, true);
@@ -163,18 +167,17 @@ function getUtils(model, universalReady) {
163
167
  * @param {object} elements elements to merge into the map
164
168
  * @param {CSN.Location} $location $location of the elements - where they come from
165
169
  * @param {any} [parent] Name of the parent of the elements, alias before ref
166
- * @param {any} [error_parent] Parent name to use for error messages, ref before alias
170
+ * @param {any} [errorParent] Parent name to use for error messages, ref before alias
167
171
  * @returns {object} existingMap
168
172
  */
169
- function mergeElementsIntoMap(existingMap, elements, $location, parent, error_parent) {
173
+ function mergeElementsIntoMap(existingMap, elements, $location, parent, errorParent) {
170
174
  for (const elementName in elements) {
171
175
  const element = elements[elementName];
172
176
  if (!existingMap[elementName])
173
177
  existingMap[elementName] = [];
174
178
 
175
-
176
179
  existingMap[elementName].push({
177
- element, name: elementName, source: $location, parent: getBaseName(parent), error_parent,
180
+ element, name: elementName, source: $location, parent: getBaseName(parent), errorParent,
178
181
  });
179
182
  }
180
183
 
@@ -190,9 +193,9 @@ function getUtils(model, universalReady) {
190
193
  return name;
191
194
 
192
195
  if (name.id)
193
- return name.id.substring( name.id.lastIndexOf('.')+1 );
196
+ return name.id.substring( name.id.lastIndexOf('.') + 1 );
194
197
 
195
- return name.substring( name.lastIndexOf('.')+1 )
198
+ return name.substring( name.lastIndexOf('.') + 1 );
196
199
  }
197
200
 
198
201
  /**
@@ -202,17 +205,17 @@ function getUtils(model, universalReady) {
202
205
  function getQueryPrimarySource(query) {
203
206
  if (!query)
204
207
  return undefined;
205
- else if (query.SELECT) {
208
+ else if (query.SELECT)
206
209
  return getQueryPrimarySource(query.SELECT);
207
- } else if (query.SET) {
210
+ else if (query.SET)
208
211
  return getQueryPrimarySource(query.SET);
209
- } else if (query.from) {
212
+ else if (query.from)
210
213
  return getQueryPrimarySource(query.from);
211
- } else if (query.ref) {
214
+ else if (query.ref)
212
215
  return query;
213
- } else if (query.args) {
216
+ else if (query.args)
214
217
  return getQueryPrimarySource(query.args[0]);
215
- }
218
+
216
219
  return undefined;
217
220
  }
218
221
 
@@ -222,7 +225,7 @@ function getUtils(model, universalReady) {
222
225
  * @param {string} [initialId] Initial entry (optional)
223
226
  */
224
227
  function createVisited(initialId) {
225
- let visited = Object.create(null);
228
+ const visited = Object.create(null);
226
229
  check(initialId);
227
230
  return { check };
228
231
 
@@ -232,10 +235,11 @@ function getUtils(model, universalReady) {
232
235
  * @param {string} id unique identifier
233
236
  */
234
237
  function check(id) {
235
- if (!id) return;
236
- if (visited[id]) {
238
+ if (!id)
239
+ return;
240
+ if (visited[id])
237
241
  throw new ModelError('Circular dependency');
238
- }
242
+
239
243
  visited[id] = true;
240
244
  }
241
245
  }
@@ -246,9 +250,8 @@ function getUtils(model, universalReady) {
246
250
  */
247
251
  function getCsnDef(defName) {
248
252
  if (model.definitions[defName])
249
- return model.definitions[defName]
250
- else
251
- throw new ModelError(`Nonexistent definition in the model: '${defName}'`);
253
+ return model.definitions[defName];
254
+ throw new ModelError(`Nonexistent definition in the model: '${ defName }'`);
252
255
  }
253
256
 
254
257
  /**
@@ -256,10 +259,10 @@ function getUtils(model, universalReady) {
256
259
  * or a typedef of a structured type.
257
260
  *
258
261
  * @param {object} obj
262
+ * @returns {boolean}
259
263
  */
260
264
  function isStructured(obj) {
261
- return obj.elements ||
262
- (obj.type && ((getFinalTypeDef(obj.type).elements) || (obj.type.ref && getFinalBaseTypeWithProps(obj.type)?.elements)));
265
+ return !!(obj.elements || (obj.type && getFinalBaseTypeWithProps(obj.type)?.elements));
263
266
  }
264
267
 
265
268
  /**
@@ -270,11 +273,11 @@ function getUtils(model, universalReady) {
270
273
  * @returns {object}
271
274
  */
272
275
  function getFinalTypeDef(typeName) {
273
- let visited = createVisited(typeName);
276
+ const visited = createVisited(typeName);
274
277
  let type = model.definitions[typeName];
275
- if (!type) {
278
+ if (!type)
276
279
  return typeName;
277
- }
280
+
278
281
  for (let nextType = type; nextType;) {
279
282
  type = nextType;
280
283
  visited.check(type.type);
@@ -289,7 +292,7 @@ function getUtils(model, universalReady) {
289
292
  * @returns {string}
290
293
  */
291
294
  function getFinalType(typeName) {
292
- let visited = createVisited(typeName);
295
+ const visited = createVisited(typeName);
293
296
  let type = model.definitions[typeName];
294
297
  while (type && type.type) {
295
298
  typeName = type.type;
@@ -313,7 +316,7 @@ function getUtils(model, universalReady) {
313
316
  function isAssocOrComposition(typeName) {
314
317
  if (typeName === 'cds.Association' || typeName === 'cds.Composition')
315
318
  return true;
316
- let visited = createVisited(typeName);
319
+ const visited = createVisited(typeName);
317
320
  let type = model.definitions[typeName];
318
321
  while (type) {
319
322
  if (type.type === 'cds.Association' || type.type === 'cds.Composition')
@@ -331,7 +334,7 @@ function getUtils(model, universalReady) {
331
334
  function isAssociation(typeName) {
332
335
  if (typeName === 'cds.Association')
333
336
  return true;
334
- let visited = createVisited(typeName);
337
+ const visited = createVisited(typeName);
335
338
  let type = model.definitions[typeName];
336
339
  while (type) {
337
340
  if (type.type === 'cds.Association')
@@ -349,7 +352,7 @@ function getUtils(model, universalReady) {
349
352
  function isComposition(typeName) {
350
353
  if (typeName === 'cds.Composition')
351
354
  return true;
352
- let visited = createVisited(typeName);
355
+ const visited = createVisited(typeName);
353
356
  let type = model.definitions[typeName];
354
357
  while (type) {
355
358
  if (type.type === 'cds.Composition')
@@ -366,12 +369,14 @@ function getUtils(model, universalReady) {
366
369
  */
367
370
  function getNamespaceOfArtifact(name) {
368
371
  let lastDotIdx = name.lastIndexOf('.');
369
- if (lastDotIdx === -1) return undefined;
372
+ if (lastDotIdx === -1)
373
+ return undefined;
370
374
  while (model.definitions[name]) {
371
375
  if (model.definitions[name].kind === 'namespace')
372
376
  return name;
373
377
  lastDotIdx = name.lastIndexOf('.');
374
- if (lastDotIdx === -1) return undefined;
378
+ if (lastDotIdx === -1)
379
+ return undefined;
375
380
  name = name.substring(0, lastDotIdx);
376
381
  }
377
382
  return name;
@@ -387,7 +392,8 @@ function getUtils(model, universalReady) {
387
392
  if (model.definitions[name].kind === 'context' || model.definitions[name].kind === 'service')
388
393
  return name;
389
394
  lastDotIdx = name.lastIndexOf('.');
390
- if (lastDotIdx === -1) return undefined;
395
+ if (lastDotIdx === -1)
396
+ return undefined;
391
397
  name = name.substring(0, lastDotIdx);
392
398
  }
393
399
  return undefined;
@@ -402,11 +408,11 @@ function getUtils(model, universalReady) {
402
408
  */
403
409
  function addStringAnnotationTo(absoluteName, theValue, node) {
404
410
  // Sanity check
405
- if (!absoluteName.startsWith('@')) {
406
- throw Error('Annotation name should start with "@": ' + absoluteName);
407
- }
411
+ if (!absoluteName.startsWith('@'))
412
+ throw Error(`Annotation name should start with "@": ${ absoluteName }`);
413
+
408
414
  // Only overwrite if undefined or null
409
- if(node[absoluteName] === undefined || node[absoluteName] === null) {
415
+ if (node[absoluteName] === undefined || node[absoluteName] === null) {
410
416
  // Assemble the annotation
411
417
  node[absoluteName] = theValue;
412
418
  }
@@ -420,14 +426,14 @@ function getUtils(model, universalReady) {
420
426
  * @returns {string|null}
421
427
  */
422
428
  function getServiceName(artifactName) {
423
- for(;;) {
424
- let idx = artifactName.lastIndexOf('.');
425
- if (idx === -1) return null;
429
+ for (;;) {
430
+ const idx = artifactName.lastIndexOf('.');
431
+ if (idx === -1)
432
+ return null;
426
433
  artifactName = artifactName.substring(0, idx);
427
- let artifact = model.definitions[artifactName];
428
- if (artifact && artifact.kind === 'service') {
434
+ const artifact = model.definitions[artifactName];
435
+ if (artifact && artifact.kind === 'service')
429
436
  return artifactName;
430
- }
431
437
  }
432
438
  }
433
439
 
@@ -450,35 +456,33 @@ function getUtils(model, universalReady) {
450
456
  * @returns {object}
451
457
  */
452
458
  function cloneWithTransformations(rootNode, transformers) {
453
-
454
459
  return transformNode(rootNode);
455
460
 
456
- // This general transformation function will be applied to each node recursively
461
+ // This general transformation function will be applied to each node recursively
457
462
  function transformNode(node) {
458
463
  // Return primitive values and null unchanged, but let objects and dictionaries through
459
464
  // (Note that 'node instanceof Object' would be false for dictionaries).
460
- if (node === null || typeof node !== 'object') {
461
- return node
462
- }
463
- // Simply return if node is to be ignored
464
- if (node == undefined || node._ignore)
465
+ if (node === null || typeof node !== 'object')
466
+ return node;
467
+
468
+ // Simply return if node is to be ignored
469
+ if (node === undefined || node._ignore)
465
470
  return undefined;
466
- // Transform arrays element-wise
467
- if (Array.isArray(node)) {
471
+ // Transform arrays element-wise
472
+ if (Array.isArray(node))
468
473
  return node.map(transformNode);
469
- }
470
- // Things not having 'proto' are dictionaries
471
- let proto = Object.getPrototypeOf(node);
472
- // Iterate own properties of 'node' and transform them into 'resultNode'
473
- let resultNode = Object.create(proto);
474
- for (let key of Object.keys(node)) {
474
+
475
+ // Things not having 'proto' are dictionaries
476
+ const proto = Object.getPrototypeOf(node);
477
+ // Iterate own properties of 'node' and transform them into 'resultNode'
478
+ const resultNode = Object.create(proto);
479
+ for (const key of Object.keys(node)) {
475
480
  // Dictionary always use transformNode(), other objects their transformer according to key
476
- let transformer = (proto == undefined) ? transformNode : transformers[key] || transformers[key.charAt(0)];
477
- // Apply transformer, or use transformNode() if there is none
478
- let resultValue = (transformer || transformNode)(node[key], node, resultNode, key);
479
- if (resultValue !== undefined) {
481
+ const transformer = (proto === null || proto === undefined) ? transformNode : transformers[key] || transformers[key.charAt(0)];
482
+ // Apply transformer, or use transformNode() if there is none
483
+ const resultValue = (transformer || transformNode)(node[key], node, resultNode, key);
484
+ if (resultValue !== undefined)
480
485
  resultNode[key] = resultValue;
481
- }
482
486
  }
483
487
  return resultNode;
484
488
  }
@@ -514,21 +518,21 @@ function getUtils(model, universalReady) {
514
518
  return null;
515
519
 
516
520
  // Nothing to copy from builtin type name.
517
- if (typeof type === 'string' && isBuiltinType( type )) {
521
+ if (typeof type === 'string' && isBuiltinType( type ))
518
522
  return { type };
519
- }
523
+
520
524
 
521
525
  // We differentiate between ref and type to avoid collisions due to dict key.
522
526
  // Delimiter chosen arbitrarily; just one that is rarely used.
523
- const resolvedKey = (typeof type === 'object') ? `ref[${type.ref.length}]:${type.ref.join('\\')}` : `type:${type}`;
527
+ const resolvedKey = (typeof type === 'object') ? `ref[${ type.ref.length }]:${ type.ref.join('\\') }` : `type:${ type }`;
524
528
 
525
529
  if (finalBaseTypeCache[resolvedKey]) {
526
530
  if (finalBaseTypeCache[resolvedKey] === true)
527
- throw new ModelError(`Detected circular type reference; can't resolve: ${resolvedKey}`);
531
+ throw new ModelError(`Detected circular type reference; can't resolve: ${ resolvedKey }`);
528
532
  return finalBaseTypeCache[resolvedKey];
529
533
  }
530
534
 
531
- let typeRef = artifactRef(type); // throws if not found
535
+ const typeRef = artifactRef(type); // throws if not found
532
536
  const isNonScalar = _cacheNonScalar(typeRef);
533
537
  if (isNonScalar)
534
538
  return finalBaseTypeCache[resolvedKey];
@@ -597,9 +601,8 @@ function getUtils(model, universalReady) {
597
601
  target.type = source.type;
598
602
  const typeProps = [ ...typeParameters.list, 'enum', 'default', 'localized' ];
599
603
  for (const param of typeProps) {
600
- if (target[param] === undefined && source[param] !== undefined) {
604
+ if (target[param] === undefined && source[param] !== undefined)
601
605
  target[param] = source[param];
602
- }
603
606
  }
604
607
  return target;
605
608
  }
@@ -609,7 +612,12 @@ function getUtils(model, universalReady) {
609
612
  /**
610
613
  * Deeply clone the given CSN model and return it.
611
614
  * In testMode (or with testSortCsn), definitions are sorted.
612
- * Note that annotations are only copied shallowly.
615
+ *
616
+ * This function is CSN aware! Don't put annotation values into it, or
617
+ * keys such as "elements" will be interpreted according to CSN rules!
618
+ *
619
+ * @see cloneAnnotationValue
620
+ * @see cloneCsnDictionary
613
621
  *
614
622
  * @param {object} csn Top-level CSN. You can pass non-dictionary values.
615
623
  * @param {CSN.Options} options CSN Options, only used for `dictionaryPrototype`, `testMode`, and `testSortCsn`
@@ -620,18 +628,34 @@ function cloneCsnNonDict(csn, options) {
620
628
 
621
629
  /**
622
630
  * Deeply clone the given CSN dictionary and return it.
623
- * Note that annotations are only copied shallowly.
624
631
  * This function does _not_ sort the given dictionary.
625
632
  * See cloneCsnNonDict() if you want sorted definitions.
626
633
  *
634
+ * This function is CSN aware! Don't put annotation values into it, or
635
+ * keys such as "elements" will be interpreted according to CSN rules!
636
+ *
637
+ * @see cloneAnnotationValue
638
+ * @see cloneCsnNonDict
639
+ *
627
640
  * @param {object} csn
628
641
  * @param {CSN.Options} options Only cloneOptions.dictionaryPrototype is
629
- * used and cloneOptions are passed to sort().
642
+ * used and cloneOptions are passed to sortCsn().
630
643
  */
631
644
  function cloneCsnDictionary(csn, options) {
632
645
  return _cloneCsnDictionary(csn, options);
633
646
  }
634
647
 
648
+ /**
649
+ * Clones the given annotation _value_. `value` must not be an object with annotations,
650
+ * but the annotation value itself, e.g. `[ { a: 1 } ]`, not `@anno: [...]`.
651
+ *
652
+ * @param {any} value
653
+ * @returns {any}
654
+ */
655
+ function cloneAnnotationValue(value) {
656
+ return _cloneAnnotationValue( value );
657
+ }
658
+
635
659
  /**
636
660
  * Apply function `callback` to all artifacts in dictionary
637
661
  * `model.definitions`. See function `forEachGeneric` for details.
@@ -660,28 +684,28 @@ function forEachDefinition( csn, callback, iterateOptions = {} ) {
660
684
  * @param {object} iterateOptions can be used to skip certain kinds from being iterated
661
685
  * @param constructCallback
662
686
  */
663
- function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
687
+ function forEachMember( construct, callback, path = [], ignoreIgnore = true, iterateOptions = {},
664
688
  constructCallback = (_construct, _prop, _path) => {}) {
665
689
  // Allow processing _ignored elements if requested
666
- if (ignoreIgnore && construct._ignore) {
690
+ if (ignoreIgnore && construct._ignore)
667
691
  return;
668
- }
692
+
669
693
 
670
694
  // `items` itself is a structure that can contain "elements", and more.
671
- if (construct.items) {
672
- forEachMember( construct.items, callback, [...path, 'items'], ignoreIgnore, iterateOptions, constructCallback );
673
- }
695
+ if (construct.items)
696
+ forEachMember( construct.items, callback, [ ...path, 'items' ], ignoreIgnore, iterateOptions, constructCallback );
697
+
674
698
 
675
699
  // Unlike XSN, we don't make "returns" a "params" in the callback.
676
700
  // Backends rely on the fact that `forEachElement` also goes through all
677
701
  // `elements` of the return type (if structured).
678
702
  // TODO: `returns` should be handled like a parameter just like XSN (maybe with different prop name)
679
- if (construct.returns && !iterateOptions.elementsOnly) {
680
- forEachMember( construct.returns, callback, [...path, 'returns'], ignoreIgnore, iterateOptions, constructCallback );
681
- }
703
+ if (construct.returns && !iterateOptions.elementsOnly)
704
+ forEachMember( construct.returns, callback, [ ...path, 'returns' ], ignoreIgnore, iterateOptions, constructCallback );
682
705
 
683
- path = [...path]; // Copy
684
- const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', 'actions', 'params']);
706
+
707
+ path = [ ...path ]; // Copy
708
+ const propsWithMembers = (iterateOptions.elementsOnly ? [ 'elements' ] : [ 'elements', 'enum', 'actions', 'params' ]);
685
709
  propsWithMembers.forEach((prop) => {
686
710
  forEachGeneric( construct, prop, callback, path, iterateOptions );
687
711
  if (construct[prop]) {
@@ -703,15 +727,14 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
703
727
  * @param {object} iterateOptions can be used to skip certain kinds from being iterated
704
728
  * @param {constructCallback|constructCallback[]} callback
705
729
  */
706
- function forEachMemberWithQuery( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
707
- constructCallback = (_construct, _prop, _path) => {}) {
730
+ function forEachMemberWithQuery( construct, callback, path = [], ignoreIgnore = true, iterateOptions = {},
731
+ constructCallback = (_construct, _prop, _path) => {}) {
708
732
  forEachMember(construct, callback, path, ignoreIgnore, iterateOptions, constructCallback);
709
733
  if (construct.query) {
710
734
  forAllQueries(construct.query, (q, p) => {
711
735
  const s = q.SELECT;
712
- if(s) {
736
+ if (s)
713
737
  forEachMember(s, callback, p, ignoreIgnore, iterateOptions);
714
- }
715
738
  }, [ ...path, 'query' ]);
716
739
  }
717
740
  }
@@ -727,10 +750,10 @@ function forEachMemberWithQuery( construct, callback, path=[], ignoreIgnore=true
727
750
  * @param {object} iterateOptions can be used to skip certain kinds from being iterated
728
751
  * @param {constructCallback|constructCallback[]} callback
729
752
  */
730
- function forEachMemberRecursively( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
753
+ function forEachMemberRecursively( construct, callback, path = [], ignoreIgnore = true, iterateOptions = {},
731
754
  constructCallback = (_construct, _prop, _path) => {}) {
732
755
  forEachMember( construct, ( member, memberName, prop, subpath, parent ) => {
733
- if(Array.isArray(callback))
756
+ if (Array.isArray(callback))
734
757
  callback.forEach(cb => cb( member, memberName, prop, subpath, parent ));
735
758
  else
736
759
  callback( member, memberName, prop, subpath, parent );
@@ -751,15 +774,14 @@ function forEachMemberRecursively( construct, callback, path=[], ignoreIgnore=tr
751
774
  * @param {object} iterateOptions can be used to skip certain kinds from being iterated
752
775
  * @param {constructCallback|constructCallback[]} callback
753
776
  */
754
- function forEachMemberRecursivelyWithQuery( construct, callback, path=[], ignoreIgnore=true, iterateOptions = {},
755
- constructCallback = (_construct, _prop, _path) => {}) {
777
+ function forEachMemberRecursivelyWithQuery( construct, callback, path = [], ignoreIgnore = true, iterateOptions = {},
778
+ constructCallback = (_construct, _prop, _path) => {}) {
756
779
  forEachMemberRecursively(construct, callback, path, ignoreIgnore, iterateOptions, constructCallback);
757
- if(construct.query) {
780
+ if (construct.query) {
758
781
  forAllQueries(construct.query, (q, p) => {
759
782
  const s = q.SELECT;
760
- if(s) {
783
+ if (s)
761
784
  forEachMemberRecursively(s, callback, p, ignoreIgnore, iterateOptions);
762
- }
763
785
  }, [ ...path, 'query' ]);
764
786
  }
765
787
  }
@@ -782,9 +804,9 @@ function forEachGeneric( construct, prop, callback, path = [], iterateOptions =
782
804
  if (!Object.prototype.hasOwnProperty.call(dict, name))
783
805
  continue;
784
806
  const dictObj = dict[name];
785
- if((iterateOptions.skip && iterateOptions.skip.includes(dictObj.kind))
786
- || (iterateOptions.skipArtifact && typeof iterateOptions.skipArtifact === 'function'
787
- && iterateOptions.skipArtifact(dictObj, name)))
807
+ if ((iterateOptions.skip && iterateOptions.skip.includes(dictObj.kind)) ||
808
+ (iterateOptions.skipArtifact && typeof iterateOptions.skipArtifact === 'function' &&
809
+ iterateOptions.skipArtifact(dictObj, name)))
788
810
  continue;
789
811
  executeCallbacks( dictObj, name );
790
812
  }
@@ -794,7 +816,7 @@ function forEachGeneric( construct, prop, callback, path = [], iterateOptions =
794
816
  if (Array.isArray(callback))
795
817
  callback.forEach(cb => cb( o, name, prop, path.concat(p), construct ));
796
818
  else
797
- callback( o, name, prop, path.concat(p), construct )
819
+ callback( o, name, prop, path.concat(p), construct );
798
820
  }
799
821
  }
800
822
 
@@ -803,13 +825,13 @@ function forEachGeneric( construct, prop, callback, path = [], iterateOptions =
803
825
  * @param {queryCallback|queryCallback[]} queryCallback
804
826
  * @param {CSN.Path} path
805
827
  */
806
- function forAllQueries(mainQuery, queryCallback, path = []){
828
+ function forAllQueries(mainQuery, queryCallback, path = []) {
807
829
  return traverseQuery(mainQuery, queryCallback, path);
808
830
  function traverseQuery( query, callback, queryPath ) {
809
831
  if (query.SELECT) {
810
832
  // The projection is turned into a normalized query - there
811
833
  // is no real SELECT, it is fake
812
- if(!(path.length === 3 && path[2] === 'projection'))
834
+ if (!(path.length === 3 && path[2] === 'projection'))
813
835
  queryPath.push('SELECT');
814
836
  executeCallbacks();
815
837
  query = query.SELECT;
@@ -821,25 +843,24 @@ function forAllQueries(mainQuery, queryCallback, path = []){
821
843
  }
822
844
 
823
845
  if (query.from)
824
- traverseFrom( query.from, callback, queryPath.concat(['from']) );
846
+ traverseFrom( query.from, callback, queryPath.concat([ 'from' ]) );
825
847
 
826
- for (const prop of ['args', 'xpr', 'columns', 'where', 'having']) {
848
+ for (const prop of [ 'args', 'xpr', 'columns', 'where', 'having' ]) {
827
849
  // all properties which could have sub queries (directly or indirectly)
828
850
  const expr = query[prop];
829
851
  if (expr && typeof expr === 'object') {
830
- if(Array.isArray(expr)){
831
- for(let i = 0; i < expr.length; i++){
832
- traverseQuery(expr[i], callback, queryPath.concat([prop, i]));
833
- }
834
- } else {
835
- for(const argName of Object.keys( expr )){
836
- traverseQuery(expr[argName], callback, queryPath.concat([prop, argName]))
837
- }
852
+ if (Array.isArray(expr)) {
853
+ for (let i = 0; i < expr.length; i++)
854
+ traverseQuery(expr[i], callback, queryPath.concat([ prop, i ]));
855
+ }
856
+ else {
857
+ for (const argName of Object.keys( expr ))
858
+ traverseQuery(expr[argName], callback, queryPath.concat([ prop, argName ]));
838
859
  }
839
860
  }
840
861
  }
841
862
  function executeCallbacks() {
842
- if(Array.isArray(callback))
863
+ if (Array.isArray(callback))
843
864
  callback.forEach(cb => cb( query, queryPath ));
844
865
  else
845
866
  callback( query, queryPath );
@@ -852,15 +873,16 @@ function forAllQueries(mainQuery, queryCallback, path = []){
852
873
  * @param {CSN.Path} csnPath
853
874
  */
854
875
  function traverseFrom( from, callback, csnPath = [] ) {
855
- if (from.ref) // ignore
876
+ if (from.ref) { // ignore
856
877
  return;
857
- else if (from.args){ // join
858
- for(let i = 0; i < from.args.length; i++){
859
- traverseFrom(from.args[i], callback, csnPath.concat(['args', i]));
860
- }
861
878
  }
862
- else
863
- traverseQuery( from, callback, csnPath ); // sub query in FROM
879
+ else if (from.args) { // join
880
+ for (let i = 0; i < from.args.length; i++)
881
+ traverseFrom(from.args[i], callback, csnPath.concat([ 'args', i ]));
882
+ }
883
+ else {
884
+ traverseQuery( from, callback, csnPath );
885
+ } // sub query in FROM
864
886
  }
865
887
  }
866
888
 
@@ -886,12 +908,11 @@ function forAllQueries(mainQuery, queryCallback, path = []){
886
908
  * @returns {boolean}
887
909
  */
888
910
  function hasAnnotationValue(artifact, annotationName, expected = true, caseInsensitive = false) {
889
- if(expected === false)
911
+ if (expected === false)
890
912
  return artifact[annotationName] === expected || artifact[annotationName] === null;
891
913
  else if (typeof artifact[annotationName] === 'string' && caseInsensitive === true)
892
914
  return artifact[annotationName].toLowerCase() === expected.toLowerCase();
893
- else
894
- return artifact[annotationName] === expected;
915
+ return artifact[annotationName] === expected;
895
916
  }
896
917
 
897
918
  /**
@@ -910,11 +931,11 @@ function isEdmPropertyRendered(elementCsn, options) {
910
931
  // V4 struct: on/off
911
932
  const renderForeignKey = (options.odataVersion === 'v4' && options.odataFormat === 'structured') ? !!options.odataForeignKeys : true;
912
933
  const isNotIgnored = !elementCsn.target ? !elementCsn['@cds.api.ignore'] : true;
913
- const isNavigable = elementCsn.target ?
914
- (elementCsn['@odata.navigable'] === undefined ||
934
+ const isNavigable = elementCsn.target
935
+ ? (elementCsn['@odata.navigable'] === undefined ||
915
936
  elementCsn['@odata.navigable'] !== undefined && (elementCsn['@odata.navigable'] === null || elementCsn['@odata.navigable'] === true)) : true;
916
937
  // Foreign Keys can be ignored
917
- if(elementCsn['@odata.foreignKey4'])
938
+ if (elementCsn['@odata.foreignKey4'])
918
939
  return isNotIgnored && renderForeignKey;
919
940
  // ordinary elements can be ignored and isNavigable is always true for them
920
941
  // assocs cannot be ignored but not navigable
@@ -941,18 +962,18 @@ function isEdmPropertyRendered(elementCsn, options) {
941
962
  * @returns {string} The resulting database name for (absolute) 'artifactName', depending on the current naming mode.
942
963
  */
943
964
  // eslint-disable-next-line no-unused-vars
944
- function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn, sqlDialect='plain') {
945
- if(csn && typeof csn === 'object' && csn.definitions) {
946
- isValidMappingDialectCombi(sqlDialect, sqlMapping)
947
- if (sqlMapping === 'quoted' || sqlMapping === 'hdbcds') {
965
+ function getArtifactDatabaseNameOf(artifactName, sqlMapping, csn, sqlDialect = 'plain') {
966
+ if (csn && typeof csn === 'object' && csn.definitions) {
967
+ isValidMappingDialectCombi(sqlDialect, sqlMapping);
968
+ if (sqlMapping === 'quoted' || sqlMapping === 'hdbcds')
948
969
  return getResultingName(csn, sqlMapping, artifactName);
949
- }
950
- else if (sqlMapping === 'plain') {
970
+
971
+ else if (sqlMapping === 'plain')
951
972
  return artifactName.replace(/\./g, '_').toUpperCase();
952
- } else {
953
- throw new Error('Unknown naming mode: ' + sqlMapping);
954
- }
955
- } else {
973
+
974
+ throw new Error(`Unknown naming mode: ${ sqlMapping }`);
975
+ }
976
+ else {
956
977
  throw new Error('A valid CSN model is required to correctly calculate the database name of an artifact.');
957
978
  }
958
979
  }
@@ -988,7 +1009,7 @@ function getResultingName(csn, namingMode, artifactName) {
988
1009
  const name = realParts ? realParts.join('.') : artifactName;
989
1010
 
990
1011
 
991
- return (namespace && namingMode === 'hdbcds') ? `${namespace}::${name.slice(namespace.length + 1)}` : name;
1012
+ return (namespace && namingMode === 'hdbcds') ? `${ namespace }::${ name.slice(namespace.length + 1) }` : name;
992
1013
  }
993
1014
 
994
1015
 
@@ -1019,7 +1040,8 @@ function getUnderscoredName(startIndex, parts, csn) {
1019
1040
  result.push(suffix);
1020
1041
 
1021
1042
  return result;
1022
- } else if(art && art.kind === 'service') {
1043
+ }
1044
+ else if (art && art.kind === 'service') {
1023
1045
  // inside services, we immediately turn . into _
1024
1046
  const prefix = parts.slice(0, i).join('.');
1025
1047
  const suffix = parts.slice(i).join('_');
@@ -1037,8 +1059,8 @@ function getUnderscoredName(startIndex, parts, csn) {
1037
1059
  }
1038
1060
 
1039
1061
  function isValidMappingDialectCombi(sqlDialect, sqlMapping) {
1040
- if(sqlMapping === 'hdbcds' && sqlDialect !== 'hana')
1041
- throw new Error(`sqlMapping "hdbcds" must only be used with sqlDialect "hana" - found: ${sqlDialect}`);
1062
+ if (sqlMapping === 'hdbcds' && sqlDialect !== 'hana')
1063
+ throw new Error(`sqlMapping "hdbcds" must only be used with sqlDialect "hana" - found: ${ sqlDialect }`);
1042
1064
  return true;
1043
1065
  }
1044
1066
 
@@ -1060,20 +1082,18 @@ function isValidMappingDialectCombi(sqlDialect, sqlMapping) {
1060
1082
  * @returns {string} The resulting database element name for 'elemName', depending on the current naming mode.
1061
1083
  */
1062
1084
  // eslint-disable-next-line no-unused-vars
1063
- function getElementDatabaseNameOf(elemName, sqlMapping, sqlDialect='plain') {
1064
- isValidMappingDialectCombi(sqlDialect, sqlMapping)
1065
- if (sqlMapping === 'hdbcds') {
1085
+ function getElementDatabaseNameOf(elemName, sqlMapping, sqlDialect = 'plain') {
1086
+ isValidMappingDialectCombi(sqlDialect, sqlMapping);
1087
+ if (sqlMapping === 'hdbcds')
1066
1088
  return elemName;
1067
- }
1068
- else if (sqlMapping === 'plain') {
1089
+
1090
+ else if (sqlMapping === 'plain')
1069
1091
  return elemName.replace(/\./g, '_').toUpperCase();
1070
- }
1071
- else if (sqlMapping === 'quoted') {
1092
+
1093
+ else if (sqlMapping === 'quoted')
1072
1094
  return elemName.replace(/\./g, '_');
1073
- }
1074
- else {
1075
- throw new Error('Unknown naming mode: ' + sqlMapping);
1076
- }
1095
+
1096
+ throw new Error(`Unknown naming mode: ${ sqlMapping }`);
1077
1097
  }
1078
1098
 
1079
1099
  const _dependencies = Symbol('_dependencies');
@@ -1094,30 +1114,30 @@ function setDependencies( csn ) {
1094
1114
  const { artifactRef } = csnRefs(csn);
1095
1115
 
1096
1116
  forEachDefinition(csn, (artifact, artifactName) => {
1097
- if(getNormalizedQuery(artifact).query) {
1117
+ if (getNormalizedQuery(artifact).query) {
1098
1118
  initDependencies(artifact);
1099
1119
  forAllQueries(getNormalizedQuery(artifact).query, (query) => {
1100
- if(query.SELECT && query.SELECT.from) {
1101
- if(query.SELECT.from.args) {
1120
+ if (query.SELECT && query.SELECT.from) {
1121
+ if (query.SELECT.from.args)
1102
1122
  handleArgs(artifact, artifactName, query.SELECT.from.args);
1103
- } else {
1104
- if(typeof query.SELECT.from === 'string' || query.SELECT.from.ref )
1105
- handleDependency(artifactRef(query.SELECT.from), artifact, artifactName);
1106
- }
1123
+
1124
+ else if (typeof query.SELECT.from === 'string' || query.SELECT.from.ref )
1125
+ handleDependency(artifactRef(query.SELECT.from), artifact, artifactName);
1107
1126
  }
1108
- }, ['definitions', artifactName, (artifact.projection ? 'projection' : 'query')])
1127
+ }, [ 'definitions', artifactName, (artifact.projection ? 'projection' : 'query') ]);
1109
1128
  }
1110
- })
1129
+ });
1111
1130
 
1112
- return {cleanup, csn, _dependents, _dependencies};
1131
+ return {
1132
+ cleanup, csn, _dependents, _dependencies,
1133
+ };
1113
1134
 
1114
- function handleArgs(artifact, artifactName, args){
1115
- for(let arg of args){
1116
- if (arg.args) {
1135
+ function handleArgs(artifact, artifactName, args) {
1136
+ for (const arg of args) {
1137
+ if (arg.args)
1117
1138
  handleArgs(artifact, artifactName, arg.args);
1118
- } else if (arg.ref) {
1119
- handleDependency(artifactRef(arg), artifact, artifactName)
1120
- }
1139
+ else if (arg.ref)
1140
+ handleDependency(artifactRef(arg), artifact, artifactName);
1121
1141
  }
1122
1142
  }
1123
1143
 
@@ -1127,15 +1147,15 @@ function setDependencies( csn ) {
1127
1147
  dependency[_dependents][dependantName] = dependant;
1128
1148
  }
1129
1149
 
1130
- function initDependents(obj){
1131
- if(!obj[_dependents]) {
1150
+ function initDependents(obj) {
1151
+ if (!obj[_dependents]) {
1132
1152
  obj[_dependents] = Object.create(null);
1133
1153
  cleanup.push(() => delete obj[_dependents]);
1134
1154
  }
1135
1155
  }
1136
1156
 
1137
- function initDependencies(obj){
1138
- if(!obj[_dependencies]) {
1157
+ function initDependencies(obj) {
1158
+ if (!obj[_dependencies]) {
1139
1159
  obj[_dependencies] = new Set();
1140
1160
  cleanup.push(() => delete obj[_dependencies]);
1141
1161
  }
@@ -1150,7 +1170,7 @@ function setDependencies( csn ) {
1150
1170
  * @returns {boolean}
1151
1171
  */
1152
1172
  function isPersistedOnDatabase(art) {
1153
- return !('entity' === art.kind && (art.abstract || hasAnnotationValue(art, '@cds.persistence.skip')));
1173
+ return !(art.kind === 'entity' && (art.abstract || hasAnnotationValue(art, '@cds.persistence.skip')));
1154
1174
  }
1155
1175
 
1156
1176
  /**
@@ -1160,7 +1180,7 @@ function isPersistedOnDatabase(art) {
1160
1180
  * @returns {string} String containing compiler version that was used to generate content
1161
1181
  */
1162
1182
  function generatedByCompilerVersion() {
1163
- return `generated by cds-compiler version ${version}`;
1183
+ return `generated by cds-compiler version ${ version }`;
1164
1184
  }
1165
1185
 
1166
1186
  /**
@@ -1170,9 +1190,9 @@ function generatedByCompilerVersion() {
1170
1190
  * @returns {object} Object with a query property.
1171
1191
  */
1172
1192
  function getNormalizedQuery(art) {
1173
- if (art.projection) {
1193
+ if (art.projection)
1174
1194
  return { query: { SELECT: art.projection } };
1175
- }
1195
+
1176
1196
  return art;
1177
1197
  }
1178
1198
 
@@ -1196,14 +1216,14 @@ function getRootArtifactName(artifactName, csn) {
1196
1216
  if (seen === '')
1197
1217
  seen = parts[i];
1198
1218
  else
1199
- seen = `${seen}.${parts[i]}`;
1219
+ seen = `${ seen }.${ parts[i] }`;
1200
1220
 
1201
1221
  const art = csn.definitions[seen];
1202
- // Our artifact seems to be contained in this context
1222
+ // Our artifact seems to be contained in this context
1203
1223
  if (art && (art.kind === 'context' || art.kind === 'service'))
1204
1224
  return seen;
1205
1225
  }
1206
- // Our artifact is a root artifact itself
1226
+ // Our artifact is a root artifact itself
1207
1227
  return seen;
1208
1228
  }
1209
1229
 
@@ -1223,7 +1243,7 @@ function getLastPartOf(name) {
1223
1243
  // ['foo'] => 'foo';
1224
1244
  // ['foo::bar'] => 'bar'
1225
1245
  function getLastPartOfRef(ref) {
1226
- let lastPathStep = ref[ref.length - 1];
1246
+ const lastPathStep = ref[ref.length - 1];
1227
1247
  return getLastPartOf(lastPathStep.id || lastPathStep);
1228
1248
  }
1229
1249
 
@@ -1244,9 +1264,8 @@ function copyAnnotations(fromNode, toNode, overwrite = false) {
1244
1264
  const annotations = Object.keys(fromNode).filter(key => key.startsWith('@'));
1245
1265
 
1246
1266
  for (const anno of annotations) {
1247
- if (toNode[anno] === undefined || overwrite) {
1267
+ if (toNode[anno] === undefined || overwrite)
1248
1268
  toNode[anno] = fromNode[anno];
1249
- }
1250
1269
  }
1251
1270
  }
1252
1271
 
@@ -1270,9 +1289,8 @@ function copyAnnotationsAndDoc(fromNode, toNode, overwrite = false) {
1270
1289
  .filter(key => key.startsWith('@') || key === 'doc');
1271
1290
 
1272
1291
  for (const anno of annotations) {
1273
- if (toNode[anno] === undefined || overwrite) {
1292
+ if (toNode[anno] === undefined || overwrite)
1274
1293
  toNode[anno] = fromNode[anno];
1275
- }
1276
1294
  }
1277
1295
  }
1278
1296
 
@@ -1296,7 +1314,7 @@ function applyAnnotationsFromExtensions(csn, config) {
1296
1314
  if (!csn.extensions)
1297
1315
  return;
1298
1316
 
1299
- const filter = config.filter || ((_name) => true);
1317
+ const filter = config.filter || (_name => true);
1300
1318
  for (let i = 0; i < csn.extensions.length; ++i) {
1301
1319
  const ext = csn.extensions[i];
1302
1320
  const name = ext.annotate || ext.extend;
@@ -1305,7 +1323,8 @@ function applyAnnotationsFromExtensions(csn, config) {
1305
1323
  if (def) {
1306
1324
  copyAnnotationsAndDoc(ext, def, config.override);
1307
1325
  applyAnnotationsToElements(ext, def);
1308
- } else if (config.notFound) {
1326
+ }
1327
+ else if (config.notFound) {
1309
1328
  config.notFound(name, i);
1310
1329
  }
1311
1330
  }
@@ -1343,8 +1362,7 @@ function isAspect(node) {
1343
1362
  */
1344
1363
  function hasValidSkipOrExists(artifact) {
1345
1364
  return artifact.kind === 'entity' &&
1346
- (hasAnnotationValue(artifact, '@cds.persistence.exists', true) || hasAnnotationValue(artifact, '@cds.persistence.skip', true))
1347
-
1365
+ (hasAnnotationValue(artifact, '@cds.persistence.exists', true) || hasAnnotationValue(artifact, '@cds.persistence.skip', true));
1348
1366
  }
1349
1367
 
1350
1368
  /**
@@ -1368,7 +1386,7 @@ function getNamespace(csn, artifactName) {
1368
1386
  for (let i = 1; i < parts.length; i++) {
1369
1387
  // This was definitely a namespace so far
1370
1388
  const previousArtifactName = seen;
1371
- seen = `${seen}.${parts[i]}`;
1389
+ seen = `${ seen }.${ parts[i] }`;
1372
1390
  // This might not be - if it isn't, return the result.
1373
1391
  const currentArtifact = csn.definitions[seen];
1374
1392
  if (currentArtifact && currentArtifact.kind !== 'namespace')
@@ -1385,7 +1403,7 @@ function getNamespace(csn, artifactName) {
1385
1403
  * @param {CSN.Options} options
1386
1404
  */
1387
1405
  function sortCsnDefinitionsForTests(csn, options) {
1388
- if (!options.testMode)
1406
+ if (!options.testMode && !options.testSortCsn)
1389
1407
  return;
1390
1408
  const sorted = Object.create(null);
1391
1409
  Object.keys(csn.definitions || {}).sort().forEach((name) => {
@@ -1401,11 +1419,10 @@ function sortCsnDefinitionsForTests(csn, options) {
1401
1419
  * @returns {string[]}
1402
1420
  */
1403
1421
  function getServiceNames(csn) {
1404
- let result = [];
1422
+ const result = [];
1405
1423
  forEachDefinition(csn, (artifact, artifactName) => {
1406
- if (artifact.kind === 'service' && !artifact.abstract) {
1424
+ if (artifact.kind === 'service' && !artifact.abstract)
1407
1425
  result.push(artifactName);
1408
- }
1409
1426
  });
1410
1427
  return result;
1411
1428
  }
@@ -1420,9 +1437,9 @@ function getServiceNames(csn) {
1420
1437
  function walkCsnPath(csn, path) {
1421
1438
  /** @type {object} */
1422
1439
  let obj = csn;
1423
- for(const segment of path){
1440
+ for (const segment of path)
1424
1441
  obj = obj[segment];
1425
- }
1442
+
1426
1443
 
1427
1444
  return obj;
1428
1445
  }
@@ -1436,23 +1453,21 @@ function walkCsnPath(csn, path) {
1436
1453
  * @returns {string|null}
1437
1454
  */
1438
1455
  function getVariableReplacement(ref, options) {
1439
- if(options && options.variableReplacements) {
1456
+ if (options && options.variableReplacements) {
1440
1457
  let replacement = options.variableReplacements;
1441
- for(const segment of ref) {
1458
+ for (const segment of ref) {
1442
1459
  replacement = replacement[segment];
1443
- if(replacement === undefined)
1460
+ if (replacement === undefined)
1444
1461
  return null;
1445
1462
  }
1446
1463
 
1447
- if(replacement === undefined)
1464
+ if (replacement === undefined)
1448
1465
  return null; // no valid replacement found
1449
- else if(typeof replacement === 'string')
1466
+ else if (typeof replacement === 'string')
1450
1467
  return replacement; // valid replacement
1451
- else
1452
- return null; // $user.foo, but we only have configured $user.foo.bar -> error
1453
- } else {
1454
- return null;
1468
+ return null; // $user.foo, but we only have configured $user.foo.bar -> error
1455
1469
  }
1470
+ return null;
1456
1471
  }
1457
1472
 
1458
1473
  /**
@@ -1470,21 +1485,22 @@ function isDeepEqual(obj, other, noExtendedProps) {
1470
1485
  let objectKeys = Object.keys(obj);
1471
1486
  let otherKeys = Object.keys(other);
1472
1487
 
1473
- if(noExtendedProps) {
1474
- objectKeys = objectKeys.filter(k => !['@', '$', '_'].includes(k[0]));
1475
- otherKeys = otherKeys.filter(k => !['@', '$', '_'].includes(k[0]));
1488
+ if (noExtendedProps) {
1489
+ objectKeys = objectKeys.filter(k => ![ '@', '$', '_' ].includes(k[0]));
1490
+ otherKeys = otherKeys.filter(k => ![ '@', '$', '_' ].includes(k[0]));
1476
1491
  }
1477
1492
  if (objectKeys.length !== otherKeys.length)
1478
1493
  return false;
1479
1494
 
1480
- for (let key of objectKeys) {
1481
- const areValuesObjects = (obj[key] != null && typeof obj[key] === 'object')
1482
- && (other[key] !== null && typeof other[key] === 'object');
1495
+ for (const key of objectKeys) {
1496
+ const areValuesObjects = (obj[key] != null && typeof obj[key] === 'object') &&
1497
+ (other[key] !== null && typeof other[key] === 'object');
1483
1498
 
1484
1499
  if (areValuesObjects) {
1485
1500
  if (!isDeepEqual(obj[key], other[key], noExtendedProps))
1486
1501
  return false;
1487
- } else if (obj[key] !== other[key]) {
1502
+ }
1503
+ else if (obj[key] !== other[key]) {
1488
1504
  return false;
1489
1505
  }
1490
1506
  }
@@ -1496,6 +1512,7 @@ module.exports = {
1496
1512
  cloneCsn: cloneCsnNonDict, // Umbrella relies on this name
1497
1513
  cloneCsnNonDict,
1498
1514
  cloneCsnDictionary,
1515
+ cloneAnnotationValue,
1499
1516
  isBuiltinType,
1500
1517
  applyAnnotationsFromExtensions,
1501
1518
  forEachGeneric,
@@ -1513,6 +1530,7 @@ module.exports = {
1513
1530
  getElementDatabaseNameOf,
1514
1531
  applyTransformations,
1515
1532
  applyTransformationsOnNonDictionary,
1533
+ applyTransformationsOnDictionary,
1516
1534
  setDependencies,
1517
1535
  isPersistedOnDatabase,
1518
1536
  generatedByCompilerVersion,