@sap/cds-compiler 4.8.0 → 4.9.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 (95) hide show
  1. package/CHANGELOG.md +38 -4
  2. package/bin/cds_remove_invalid_whitespace.js +135 -0
  3. package/bin/cds_update_annotations.js +180 -0
  4. package/bin/cds_update_identifiers.js +3 -4
  5. package/bin/cdsc.js +30 -17
  6. package/doc/CHANGELOG_BETA.md +19 -0
  7. package/lib/api/main.js +59 -24
  8. package/lib/api/options.js +12 -1
  9. package/lib/api/validate.js +1 -5
  10. package/lib/base/builtins.js +27 -0
  11. package/lib/base/message-registry.js +38 -21
  12. package/lib/base/messages.js +51 -20
  13. package/lib/base/model.js +4 -5
  14. package/lib/checks/actionsFunctions.js +2 -2
  15. package/lib/checks/annotationsOData.js +3 -0
  16. package/lib/checks/defaultValues.js +5 -2
  17. package/lib/checks/queryNoDbArtifacts.js +3 -2
  18. package/lib/checks/validator.js +2 -34
  19. package/lib/compiler/assert-consistency.js +10 -3
  20. package/lib/compiler/checks.js +44 -18
  21. package/lib/compiler/define.js +38 -30
  22. package/lib/compiler/extend.js +33 -10
  23. package/lib/compiler/index.js +0 -1
  24. package/lib/compiler/lsp-api.js +5 -0
  25. package/lib/compiler/populate.js +0 -2
  26. package/lib/compiler/propagator.js +23 -19
  27. package/lib/compiler/resolve.js +48 -29
  28. package/lib/compiler/shared.js +60 -20
  29. package/lib/compiler/tweak-assocs.js +72 -116
  30. package/lib/compiler/xpr-rewrite.js +762 -0
  31. package/lib/edm/annotations/edmJson.js +24 -7
  32. package/lib/edm/annotations/genericTranslation.js +81 -61
  33. package/lib/edm/edm.js +4 -4
  34. package/lib/edm/edmInboundChecks.js +33 -0
  35. package/lib/edm/edmPreprocessor.js +9 -6
  36. package/lib/gen/Dictionary.json +129 -14
  37. package/lib/gen/language.checksum +1 -1
  38. package/lib/gen/language.interp +1 -1
  39. package/lib/gen/languageParser.js +1523 -1518
  40. package/lib/json/from-csn.js +13 -4
  41. package/lib/json/to-csn.js +12 -12
  42. package/lib/language/genericAntlrParser.js +14 -6
  43. package/lib/main.d.ts +67 -14
  44. package/lib/main.js +1 -0
  45. package/lib/model/cloneCsn.js +6 -3
  46. package/lib/model/csnRefs.js +23 -11
  47. package/lib/model/csnUtils.js +13 -7
  48. package/lib/model/enrichCsn.js +3 -1
  49. package/lib/model/revealInternalProperties.js +2 -1
  50. package/lib/model/sortViews.js +14 -6
  51. package/lib/modelCompare/compare.js +33 -34
  52. package/lib/optionProcessor.js +27 -2
  53. package/lib/render/DuplicateChecker.js +6 -6
  54. package/lib/render/manageConstraints.js +1 -0
  55. package/lib/render/toCdl.js +3 -1
  56. package/lib/transform/db/applyTransformations.js +33 -0
  57. package/lib/transform/db/constraints.js +75 -28
  58. package/lib/transform/db/expansion.js +8 -3
  59. package/lib/transform/db/flattening.js +2 -2
  60. package/lib/transform/db/groupByOrderBy.js +2 -2
  61. package/lib/transform/db/temporal.js +6 -3
  62. package/lib/transform/db/transformExists.js +2 -2
  63. package/lib/transform/effective/annotations.js +194 -0
  64. package/lib/transform/effective/main.js +6 -8
  65. package/lib/transform/effective/misc.js +31 -10
  66. package/lib/transform/forOdata.js +23 -7
  67. package/lib/transform/forRelationalDB.js +3 -3
  68. package/lib/transform/localized.js +7 -6
  69. package/lib/transform/odata/flattening.js +221 -124
  70. package/lib/transform/odata/toFinalBaseType.js +1 -1
  71. package/lib/transform/odata/typesExposure.js +15 -12
  72. package/lib/transform/parseExpr.js +4 -4
  73. package/lib/transform/transformUtils.js +47 -42
  74. package/lib/transform/translateAssocsToJoins.js +47 -47
  75. package/lib/transform/universalCsn/universalCsnEnricher.js +16 -19
  76. package/package.json +1 -1
  77. package/share/messages/anno-missing-rewrite.md +45 -0
  78. package/share/messages/message-explanations.json +1 -0
  79. package/bin/.eslintrc.json +0 -17
  80. package/lib/api/.eslintrc.json +0 -37
  81. package/lib/checks/.eslintrc.json +0 -31
  82. package/lib/compiler/.eslintrc.json +0 -8
  83. package/lib/edm/.eslintrc.json +0 -46
  84. package/lib/inspect/.eslintrc.json +0 -4
  85. package/lib/json/.eslintrc.json +0 -4
  86. package/lib/language/.eslintrc.json +0 -4
  87. package/lib/model/.eslintrc.json +0 -13
  88. package/lib/modelCompare/utils/.eslintrc.json +0 -22
  89. package/lib/render/.eslintrc.json +0 -22
  90. package/lib/transform/.eslintrc.json +0 -13
  91. package/lib/transform/db/.eslintrc.json +0 -41
  92. package/lib/transform/draft/.eslintrc.json +0 -4
  93. package/lib/transform/effective/.eslintrc.json +0 -4
  94. package/lib/transform/universalCsn/.eslintrc.json +0 -37
  95. package/lib/utils/.eslintrc.json +0 -7
@@ -58,7 +58,7 @@ function parseExpr(xpr, state = { array: true, nary: false }) {
58
58
  return { 'cast': [ xpr.cast, { [castKeys[0]]: xpr[castKeys[0]] } ] };
59
59
  }
60
60
  else {
61
- for(let n in xpr) {
61
+ for(const n in xpr) {
62
62
  // xpr could be an array with polluted prototype
63
63
  if (Object.hasOwnProperty.call(xpr, n))
64
64
  xpr[n] = Cast(xpr[n], state)
@@ -214,7 +214,7 @@ function parseExpr(xpr, state = { array: true, nary: false }) {
214
214
  while(i < e && xpr[i] !== 'and') i++;
215
215
  const a = i < e ? i : -1;
216
216
  if(b >= 0) {
217
- let token = [ 'between' ];
217
+ const token = [ 'between' ];
218
218
  not = (xpr[b-1] === 'not');
219
219
  if(not)
220
220
  token.splice(0,0, 'not');
@@ -312,7 +312,7 @@ function parseExpr(xpr, state = { array: true, nary: false }) {
312
312
  if (typeof xpr === 'object') {
313
313
  // if(xpr?.func && funkyfuncs.includes(xpr?.func))
314
314
  // return xpr;
315
- for(let n in xpr) {
315
+ for(const n in xpr) {
316
316
  // xpr could be an array with polluted prototype
317
317
  if (!Object.hasOwnProperty.call(xpr, n))
318
318
  continue;
@@ -349,7 +349,7 @@ function parseExpr(xpr, state = { array: true, nary: false }) {
349
349
  s = p+tl;
350
350
  [tl, p] = findToken(s, e);
351
351
  while(p >= 0) {
352
- let rhs = next(xpr, s, p, state);
352
+ const rhs = next(xpr, s, p, state);
353
353
  naryExpr.push(...op, rhs);
354
354
  if(state.array)
355
355
  lhs = [ lhs, ...op, rhs ];
@@ -162,14 +162,14 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
162
162
  // Note that this must happen after struct flattening(flattenStructuredElement) - both fot elements and foreign keys.
163
163
  // Return the newly generated foreign key element.
164
164
  function createForeignKeyElement(assoc, assocName, foreignKey, artifact, artifactName, path) {
165
- let result = {};
165
+ const result = {};
166
166
 
167
167
  // FIXME: Duplicate code (postfix is added herein, can this be optimized?)
168
168
  // Assemble foreign key element name from assoc name, '_' and foreign key name/alias
169
- let foreignKeyElementName = `${assocName.replace(/\./g, pathDelimiter)}${pathDelimiter}${foreignKey.as || foreignKey.ref.join(pathDelimiter)}`;
169
+ const foreignKeyElementName = `${assocName.replace(/\./g, pathDelimiter)}${pathDelimiter}${foreignKey.as || foreignKey.ref.join(pathDelimiter)}`;
170
170
 
171
171
 
172
- let fkArtifact = inspectRef(path).art;
172
+ const fkArtifact = inspectRef(path).art;
173
173
  newForeignKey(fkArtifact, foreignKeyElementName);
174
174
 
175
175
  function processAssociationOrComposition(fkArtifact,foreignKeyElementName) {
@@ -188,7 +188,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
188
188
  return;
189
189
  }
190
190
 
191
- let foreignKeyElement = createRealFK(fkArtifact, assoc, assocName, foreignKey, path, foreignKeyElementName);
191
+ const foreignKeyElement = createRealFK(fkArtifact, assoc, assocName, foreignKey, path, foreignKeyElementName);
192
192
 
193
193
  // FIXME: must this code go into createRealFK?
194
194
  // Not present in getForeignKeyArtifact
@@ -229,7 +229,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
229
229
  // length: 42 },
230
230
  // }
231
231
  function flattenStructuredElement(elem, elemName, parentElementPath=[], pathInCsn=[]) {
232
- let elementPath=parentElementPath.concat(elemName); // elementPath contains only element names without the csn structure node names
232
+ const elementPath=parentElementPath.concat(elemName); // elementPath contains only element names without the csn structure node names
233
233
  // in case the element is of user defined type => take the definition of the type
234
234
  // for type of 'x' -> elem.type is an object, not a string -> use directly
235
235
  let elemType;
@@ -241,7 +241,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
241
241
  // Collect all child elements (recursively) into 'result'
242
242
  // TODO: Do not report collisions in the generated elements here, but instead
243
243
  // leave that work to the receiver of this result
244
- let result = Object.create(null);
244
+ const result = Object.create(null);
245
245
  const addGeneratedFlattenedElement = (e, eName) => {
246
246
  if (result[eName])
247
247
  error('name-duplicate-element', pathInCsn, { '#': 'flatten-element-gen', name: eName })
@@ -251,10 +251,10 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
251
251
  forEach(struct, (childName, childElem) => {
252
252
  if (isStructured(childElem)) {
253
253
  // Descend recursively into structured children
254
- let grandChildElems = flattenStructuredElement(childElem, childName, elementPath, pathInCsn.concat('elements',childName));
255
- for (let grandChildName in grandChildElems) {
256
- let flatElemName = elemName + pathDelimiter + grandChildName;
257
- let flatElem = grandChildElems[grandChildName];
254
+ const grandChildElems = flattenStructuredElement(childElem, childName, elementPath, pathInCsn.concat('elements',childName));
255
+ for (const grandChildName in grandChildElems) {
256
+ const flatElemName = elemName + pathDelimiter + grandChildName;
257
+ const flatElem = grandChildElems[grandChildName];
258
258
  addGeneratedFlattenedElement(flatElem, flatElemName);
259
259
  // TODO: check with values. In CSN such elements have only "@Core.Computed": true
260
260
  // If the original element had a value, construct one for the flattened element
@@ -265,8 +265,8 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
265
265
  }
266
266
  } else {
267
267
  // Primitive child - clone it and restore its cross references
268
- let flatElemName = elemName + pathDelimiter + childName;
269
- let flatElem = cloneCsnNonDict(childElem, { ...options, hiddenPropertiesToClone: [ '$structRef', '$fkExtensions' ] } );
268
+ const flatElemName = elemName + pathDelimiter + childName;
269
+ const flatElem = cloneCsnNonDict(childElem, { ...options, hiddenPropertiesToClone: [ '$structRef', '$fkExtensions' ] } );
270
270
  // Don't take over notNull from leaf elements
271
271
  delete flatElem.notNull;
272
272
  setProp(flatElem, '_flatElementNameWithDots', elementPath.concat(childName).join('.'));
@@ -302,7 +302,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
302
302
  // 'localized' is needed for OData
303
303
  if(options.toOdata)
304
304
  props.push('localized');
305
- for (let p of props) {
305
+ for (const p of props) {
306
306
  if (elem[p]) {
307
307
  flatElem[p] = elem[p];
308
308
  }
@@ -324,12 +324,14 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
324
324
  * @param {string} [scope] Pre-resolved scope for the given ref - if not provided, will be calculated JIT
325
325
  * @param {WeakMap} [resolvedLinkTypes=new WeakMap()] A WeakMap with already resolved types for each link-step - safes an `artifactRef` call
326
326
  * @param {bool} [suspend] suspend flattening by caller until association path step
327
- * @returns {string[]}
327
+ * @param {int} [suspendPos] suspend if starting pos is lower or equal to suspendPos and suspend is true
328
+ * @param {bool} [revokeAtSuspendPos] revoke suspension after suspendPos (binding parameter path use case)
329
+ * @returns [string[], bool]
328
330
  */
329
- function flattenStructStepsInRef(ref, path, links, scope, resolvedLinkTypes=new WeakMap(), suspend=false, suspendPos=0) {
331
+ function flattenStructStepsInRef(ref, path, links, scope, resolvedLinkTypes=new WeakMap(), suspend=false, suspendPos=0, revokeAtSuspendPos=false) {
330
332
  // Refs of length 1 cannot contain steps - no need to check
331
333
  if (ref.length < 2 || (scope === '$self' && ref.length === 2)) {
332
- return ref;
334
+ return [ ref, false ];
333
335
  }
334
336
 
335
337
  const result = scope === '$self' ? [ref[0]] : [];
@@ -340,7 +342,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
340
342
  scope = res.scope;
341
343
  }
342
344
  if (scope === '$magic')
343
- return ref;
345
+ return [ ref, false ];
344
346
 
345
347
  // Don't process a leading $self - it will a .art with .elements!
346
348
  let i = scope === '$self' ? 1 : 0;
@@ -351,7 +353,8 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
351
353
  effectiveType(links[i].art)[propName] ||
352
354
  (resolvedLinkTypes.get(links[i])||{})[propName]);
353
355
 
354
- let flattenStep = false;
356
+ let refChanged = false
357
+ let flattenStep = false;
355
358
  suspend = !!art('items') || (suspend && i <= suspendPos);
356
359
  for(; i < links.length; i++) {
357
360
 
@@ -363,14 +366,16 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
363
366
  ref[i].id = result[result.length-1];
364
367
  result[result.length-1] = ref[i];
365
368
  }
369
+ refChanged = true;
366
370
  // suspend flattening if the next path step has some 'items'
367
371
  suspend = !!art('items');
368
372
  }
369
373
  else {
370
374
  result.push(ref[i]);
375
+ suspend ||= !!art('items');
371
376
  }
372
377
  // revoke items suspension for next assoc step
373
- if(suspend && art('target'))
378
+ if(suspend && art('target') || (revokeAtSuspendPos && i === suspendPos))
374
379
  suspend = false;
375
380
 
376
381
  flattenStep = !links[i].art?.kind &&
@@ -378,7 +383,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
378
383
  !links[i].art?.from &&
379
384
  art('elements');
380
385
  }
381
- return result;
386
+ return [ result, refChanged ];
382
387
  }
383
388
 
384
389
  /**
@@ -471,11 +476,11 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
471
476
  // Add the created projection to the model and complain if artifact already exists.
472
477
  // Used by Draft generation
473
478
  function createExposingProjection(art, artName, projectionId, service) {
474
- let projectionAbsoluteName = `${service}.${projectionId}`;
479
+ const projectionAbsoluteName = `${service}.${projectionId}`;
475
480
  // Create elements matching the artifact's elements
476
- let elements = Object.create(null);
481
+ const elements = Object.create(null);
477
482
  art.elements && Object.entries(art.elements).forEach(([elemName, artElem]) => {
478
- let elem = Object.assign({}, artElem);
483
+ const elem = Object.assign({}, artElem);
479
484
  // Transfer xrefs, that are redirected to the projection
480
485
  // TODO: shall we remove the transferred elements from the original?
481
486
  // if (artElem._xref) {
@@ -485,7 +490,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
485
490
  elements[elemName] = elem;
486
491
  });
487
492
 
488
- let query = {
493
+ const query = {
489
494
  'SELECT': {
490
495
  'from': {
491
496
  'ref': [
@@ -495,13 +500,13 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
495
500
  }
496
501
  };
497
502
  // Assemble the projection itself and add it into the model
498
- let projection = {
503
+ const projection = {
499
504
  'kind': 'entity',
500
505
  projection: query.SELECT, // it is important that projection and query refer to the same object!
501
506
  elements
502
507
  };
503
508
  // copy annotations from art to projection
504
- for (let a of Object.keys(art).filter(x => x.startsWith('@'))) {
509
+ for (const a of Object.keys(art).filter(x => x.startsWith('@'))) {
505
510
  projection[a] = art[a];
506
511
  }
507
512
  model.definitions[projectionAbsoluteName] = projection;
@@ -606,7 +611,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
606
611
  if (!isBuiltinType(typeName) && !model.definitions[typeName]) {
607
612
  throw new ModelError('Expecting valid type name: ' + typeName);
608
613
  }
609
- let result = {
614
+ const result = {
610
615
  [elemName]: {
611
616
  type: typeName
612
617
  }
@@ -634,16 +639,16 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
634
639
  // keys: [{ ref: ['id'] }]
635
640
  // } }
636
641
  function createAssociationElement(elemName, target, isManaged = false) {
637
- let elem = createScalarElement(elemName, 'cds.Association', false, undefined);
638
- let assoc = elem[elemName];
642
+ const elem = createScalarElement(elemName, 'cds.Association', false, undefined);
643
+ const assoc = elem[elemName];
639
644
  assoc.target = target;
640
645
 
641
646
  if (isManaged) {
642
647
  assoc.keys = [];
643
- let targetArt = getCsnDef(target);
648
+ const targetArt = getCsnDef(target);
644
649
  targetArt.elements && Object.entries(targetArt.elements).forEach(([keyElemName, keyElem]) => {
645
650
  if (keyElem.key) {
646
- let foreignKey = createForeignKey(keyElemName, keyElem);
651
+ const foreignKey = createForeignKey(keyElemName, keyElem);
647
652
  addForeignKey(foreignKey, assoc);
648
653
  }
649
654
  });
@@ -700,7 +705,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
700
705
  if (!artifact.elements) {
701
706
  throw new ModelError('Expecting artifact with elements: ' + JSON.stringify(artifact));
702
707
  }
703
- let elemName = Object.keys(elem)[0];
708
+ const elemName = Object.keys(elem)[0];
704
709
  // Element must not exist
705
710
  if (artifact.elements[elemName]) {
706
711
  let path = null;
@@ -737,7 +742,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
737
742
  error(null, path, { name: elementName }, 'Generated element $(NAME) conflicts with existing element');
738
743
  }
739
744
 
740
- let result = Object.create(null);
745
+ const result = Object.create(null);
741
746
  result[elementName] = {};
742
747
  elem && Object.entries(elem).forEach(([prop, value]) => {
743
748
  result[elementName][prop] = value;
@@ -750,13 +755,13 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
750
755
  // of type name 'paramTypeName'
751
756
  function createAction(actionName, returnTypeName = undefined, paramName = undefined, paramTypeName = undefined) {
752
757
  // Assemble the action
753
- let result = {
758
+ const result = {
754
759
  [actionName]: {
755
760
  kind: 'action'
756
761
  }
757
762
  };
758
763
 
759
- let action = result[actionName];
764
+ const action = result[actionName];
760
765
 
761
766
  if (returnTypeName) {
762
767
  if (!isBuiltinType(returnTypeName) && !model.definitions[returnTypeName])
@@ -791,7 +796,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
791
796
  artifact.actions = Object.create(null);
792
797
  }
793
798
 
794
- let actionName = Object.keys(action)[0];
799
+ const actionName = Object.keys(action)[0];
795
800
  // Element must not exist
796
801
  if (!artifact.actions[actionName]) {
797
802
  // Add the action
@@ -808,7 +813,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
808
813
  * second field if it has @cds.valid.to. Default value is [] for each field.
809
814
  */
810
815
  function extractValidFromToKeyElement(element, path) {
811
- let validFroms = [], validTos = [], validKeys = [];
816
+ const validFroms = [], validTos = [], validKeys = [];
812
817
  if (hasAnnotationValue(element, '@cds.valid.from')) {
813
818
  validFroms.push({ element, path: [...path] });
814
819
  }
@@ -878,7 +883,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
878
883
  */
879
884
  function recurseElements(artifact, path, callback) {
880
885
  callback(artifact, path);
881
- let elements = artifact.elements;
886
+ const elements = artifact.elements;
882
887
  if (elements) {
883
888
  path.push('elements', null);
884
889
  forEach(elements, (name, obj) => {
@@ -892,7 +897,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
892
897
 
893
898
  // Rename annotation 'fromName' in 'node' to 'toName' (both names including '@')
894
899
  function renameAnnotation(node, fromName, toName) {
895
- let annotation = node && node[fromName];
900
+ const annotation = node && node[fromName];
896
901
  // Sanity checks
897
902
  if (!fromName.startsWith('@')) {
898
903
  throw new CompilerAssertion('Annotation name should start with "@": ' + fromName);
@@ -1039,7 +1044,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
1039
1044
  if(art) {
1040
1045
  if(art && !((art.items && art.items.elements) || art.elements)) {
1041
1046
  if(followMgdAssoc && art.target && art.keys) {
1042
- let rc = [];
1047
+ const rc = [];
1043
1048
  for(const k of art.keys) {
1044
1049
  const nps = { ref: k.ref.map(p => fullRef ? { id: p } : p ) };
1045
1050
  setProp(nps, '_art', k._art);
@@ -1057,7 +1062,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
1057
1062
  }
1058
1063
  const elements = art.items && art.items.elements || art.elements;
1059
1064
  if(elements) {
1060
- let rc = []
1065
+ const rc = []
1061
1066
  Object.entries(elements).forEach(([en, elt]) => {
1062
1067
  const nps = { ref: [ (fullRef ? { id: en, _art: elt } : en )] };
1063
1068
  setProp(nps, '_art', elt);
@@ -1106,7 +1111,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
1106
1111
  Flattening stops on all non-structured types.
1107
1112
  */
1108
1113
  function expand(expr, location) {
1109
- let rc = [];
1114
+ const rc = [];
1110
1115
  for(let i = 0; i < expr.length; i++)
1111
1116
  {
1112
1117
  if(Array.isArray(expr[i]))
@@ -29,10 +29,10 @@ function translateAssocsToJoinsCSN(csn, options){
29
29
  // Use the effective elements list as columns
30
30
  forEachDefinition(model, art => {
31
31
  if (art.$queries) {
32
- for (let query of art.$queries) {
32
+ for (const query of art.$queries) {
33
33
  query.columns = Object.values(query.elements);
34
34
  // TODO: Remove viaAll
35
- for (let elemName in query.elements) {
35
+ for (const elemName in query.elements) {
36
36
  const elem = query.elements[elemName];
37
37
  if (elem.$inferred === '*')
38
38
  delete elem.$inferred;
@@ -172,8 +172,8 @@ function translateAssocsToJoins(model, inputOptions = {})
172
172
  // Transform each FROM table path into a join tree and attach the tree to the path object
173
173
  function createInnerJoins(fromPathNode, env)
174
174
  {
175
- let fqat = env.lead.$tableAliases[fromPathNode.name.id].$fqat;
176
- let joinTree = createJoinTree(env, undefined, fqat, 'inner', '$fqat', undefined);
175
+ const fqat = env.lead.$tableAliases[fromPathNode.name.id].$fqat;
176
+ const joinTree = createJoinTree(env, undefined, fqat, 'inner', '$fqat', undefined);
177
177
 
178
178
  replaceTableAliasInPlace( fromPathNode, joinTree);
179
179
  }
@@ -185,11 +185,11 @@ function translateAssocsToJoins(model, inputOptions = {})
185
185
  {
186
186
  env.lead = query;
187
187
  let joinTree = query.from;
188
- for(let tan in query.$tableAliases)
188
+ for(const tan in query.$tableAliases)
189
189
  {
190
190
  if(query.$tableAliases[tan].kind !== '$self') // don't drive into $projection/$self tableAlias (yet)
191
191
  {
192
- let ta = query.$tableAliases[tan];
192
+ const ta = query.$tableAliases[tan];
193
193
  joinTree = createJoinTree(env, joinTree, ta.$qat, 'left', '$qat', ta.$QA);
194
194
  }
195
195
  }
@@ -207,9 +207,9 @@ function translateAssocsToJoins(model, inputOptions = {})
207
207
  */
208
208
  function createQAForFromClauseSubQuery(query, env)
209
209
  {
210
- for (let taName in query.$tableAliases) {
210
+ for (const taName in query.$tableAliases) {
211
211
  if (query.$tableAliases[taName].kind !== '$self') {
212
- let ta = query.$tableAliases[taName];
212
+ const ta = query.$tableAliases[taName];
213
213
  if(!ta.$QA) {
214
214
  let alias = taName;
215
215
  if (ta.name.$inferred === '$internal') {
@@ -306,8 +306,8 @@ function translateAssocsToJoins(model, inputOptions = {})
306
306
  if(env.location === 'onCondFrom')
307
307
  {
308
308
  if(checkPathDictionary(pathNode, env)) {
309
- let [ tableAlias, tail ] = constructTableAliasAndTailPath(pathNode.path);
310
- let pathStr = translateONCondPath(tail).map(ps => ps.id).join(pathDelimiter);
309
+ const [ tableAlias, tail ] = constructTableAliasAndTailPath(pathNode.path);
310
+ const pathStr = translateONCondPath(tail).map(ps => ps.id).join(pathDelimiter);
311
311
  replaceNodeContent(pathNode,
312
312
  constructPathNode([ tableAlias, { id: pathStr, _artifact: pathNode._artifact } ]));
313
313
  }
@@ -329,11 +329,11 @@ function translateAssocsToJoins(model, inputOptions = {})
329
329
  tail = pathNode.path;
330
330
  // if tail.length > 1, search bottom up for QA
331
331
  // default to rootQA, _parent.$QA has precedence
332
- let [QA, ps] = rightMostQA(tail, head._navigation._parent.$QA || head._navigation.$QA);
332
+ const [QA, ps] = rightMostQA(tail, head._navigation._parent.$QA || head._navigation.$QA);
333
333
  if(!QA) {
334
334
  error(null, pathNode.$location,
335
335
  { name: pathName(pathNode.path) },
336
- 'Please debug me: No QA found for generic path rewriting in $(NAME)')
336
+ 'Debug me: No QA found for generic path rewriting in $(NAME)')
337
337
  return;
338
338
  }
339
339
  // if the found QA is the mixin QA and if the path length is one,
@@ -360,7 +360,7 @@ function translateAssocsToJoins(model, inputOptions = {})
360
360
  // const revealInternalProperties = require('../model/revealInternalProperties.js');
361
361
  // console.log('++++++++ Path tail: ', revealInternalProperties(tail[tail.length-1]._artifact));
362
362
  // console.log('******** Flat FKs\n', tail[i]._artifact.$flatSrcFKs.map(f => revealInternalProperties(f._artifact)));
363
- throw new CompilerAssertion('Please debug me: No flat FK found for FK rewriting');
363
+ throw new CompilerAssertion('Debug me: No flat FK found for FK rewriting');
364
364
  }
365
365
  // replace tail path with flattened foreign key including prefix
366
366
  tail.splice(i, tail.length, fk);
@@ -431,7 +431,7 @@ function translateAssocsToJoins(model, inputOptions = {})
431
431
  */
432
432
  function createJoinTree(env, joinTree, parentQat, joinType, qatAttribName, lastAssocQA)
433
433
  {
434
- for(let childQatId in parentQat)
434
+ for(const childQatId in parentQat)
435
435
  {
436
436
  const childQat = parentQat[childQatId];
437
437
 
@@ -454,10 +454,10 @@ function translateAssocsToJoins(model, inputOptions = {})
454
454
  if(childQat._filter)
455
455
  {
456
456
  // Filter conditions are unique for each JOIN, they don't need to be copied
457
- let filter = childQat._filter;
457
+ const filter = childQat._filter;
458
458
  rewritePathsInFilterExpression(filter, function(pathNode) {
459
459
  return [ /* tableAlias=> */ constructTableAliasPathStep(childQat.$QA),
460
- /* filterPath=> */ pathNode.path ]; // eslint-disable-line indent-legacy
460
+ /* filterPath=> */ pathNode.path ];
461
461
  }, env);
462
462
 
463
463
  if(!env.lead.$startFilters)
@@ -486,28 +486,28 @@ function translateAssocsToJoins(model, inputOptions = {})
486
486
 
487
487
  function createJoinQA(joinType, lhs, rhs, assocQAT, assocSourceQA, env)
488
488
  {
489
- let node = { op: { val: 'join' }, join: { val: joinType }, args: [ lhs, rhs ] };
489
+ const node = { op: { val: 'join' }, join: { val: joinType }, args: [ lhs, rhs ] };
490
490
  const assoc = assocQAT._origin;
491
491
  if(isBetaEnabled(options, 'mapAssocToJoinCardinality')) {
492
492
  node.cardinality = mapAssocToJoinCardinality(assoc);
493
493
  }
494
494
  // 'path steps' for the src/tgt table alias
495
- let srcTableAlias = constructTableAliasPathStep(assocSourceQA);
496
- let tgtTableAlias = constructTableAliasPathStep(assocQAT.$QA);
495
+ const srcTableAlias = constructTableAliasPathStep(assocSourceQA);
496
+ const tgtTableAlias = constructTableAliasPathStep(assocQAT.$QA);
497
497
 
498
498
  node.on = createOnCondition(assoc, srcTableAlias, tgtTableAlias, options.tenantDiscriminator);
499
499
 
500
500
  if(assocQAT._filter)
501
501
  {
502
502
  // Filter conditions are unique for each JOIN, they don't need to be copied
503
- let filter = assocQAT._filter;
503
+ const filter = assocQAT._filter;
504
504
  rewritePathsInFilterExpression(filter, function(pathNode) {
505
505
  return [ tgtTableAlias, pathNode.path ];
506
506
  }, env);
507
507
 
508
508
  // If toplevel ON cond op is AND add filter condition to the args array,
509
509
  // create a new toplevel AND op otherwise
510
- let onCond = (Array.isArray(node.on) ? node.on[0] : node.on);
510
+ const onCond = (Array.isArray(node.on) ? node.on[0] : node.on);
511
511
 
512
512
  if(onCond.op.val === 'and')
513
513
  onCond.args.push(parenthesise(filter));
@@ -567,7 +567,7 @@ function translateAssocsToJoins(model, inputOptions = {})
567
567
  // produce the ON condition for a given association
568
568
  function createOnCondition(assoc, srcAlias, tgtAlias, compareTenants)
569
569
  {
570
- let prefixes = [ assoc.name.id ];
570
+ const prefixes = [ assoc.name.id ];
571
571
  /* This is no art and can be removed once ON cond for published
572
572
  and renamed backlink assocs are publicly available. Example:
573
573
 
@@ -600,12 +600,12 @@ function translateAssocsToJoins(model, inputOptions = {})
600
600
  Put all src/tgt path siblings into the EQ term and create the proper path objects
601
601
  with the src/tgt table alias path steps in front.
602
602
  */
603
- let args = compareTenants && addTenantComparison(assoc) || [];
603
+ const args = compareTenants && addTenantComparison(assoc) || [];
604
604
  for(let i = 0; i < assoc.$flatSrcFKs.length; i++)
605
605
  {
606
606
  args.push({op: {val: '=' },
607
607
  args: [ constructPathNode( [ srcAlias, prefixFK(assoc.$elementPrefix, assoc.$flatSrcFKs[i]) ] ),
608
- constructPathNode( [ tgtAlias, assoc.$flatTgtFKs[i] ] ) ] }); // eslint-disable-line indent-legacy
608
+ constructPathNode( [ tgtAlias, assoc.$flatTgtFKs[i] ] ) ] });
609
609
  }
610
610
  return parenthesise((args.length > 1 ? { op: { val: 'and' }, args: [ ...args.map(parenthesise) ] } : args[0] ));
611
611
  }
@@ -761,7 +761,7 @@ function translateAssocsToJoins(model, inputOptions = {})
761
761
  // If this is an ordinary expression, clone it and mangle its arguments
762
762
  // this will substitute multiple backlink conditions ($self = ... AND $self = ...AND ...)
763
763
  if(expr.op) {
764
- let x = clone(expr);
764
+ const x = clone(expr);
765
765
  if(expr.args)
766
766
  x.args = expr.args.map(cloneOnCondition);
767
767
  return x;
@@ -845,7 +845,7 @@ function translateAssocsToJoins(model, inputOptions = {})
845
845
  $projection must be removed from $projection.yid (get's aliased with the mixinAssocQAT.$QA)
846
846
  */
847
847
  if(env.assocStack.length < 2) {
848
- let value = env.lead.elements[path[0].id].value;
848
+ const value = env.lead.elements[path[0].id].value;
849
849
  /*
850
850
  If the value is an expression in the select block, return the unmodified
851
851
  expression. rewriteGenericPaths will check and rewrite these paths later
@@ -957,7 +957,7 @@ function translateAssocsToJoins(model, inputOptions = {})
957
957
  if(head.id === env.assocStack.id()) {
958
958
  // source side from view point of view (target side from forward point of view)
959
959
  path = tail; // pop assoc step
960
- let elt = env.lead._combined[path[0].id];
960
+ const elt = env.lead._combined[path[0].id];
961
961
 
962
962
  if(elt) {
963
963
  if(Array.isArray(elt)) {
@@ -1008,7 +1008,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1008
1008
  path = translateONCondPath(path, !hasDollarSelfPrefix ? assoc.$elementPrefix : undefined);
1009
1009
  }
1010
1010
  }
1011
- let pathStr = path.map(ps => ps.id).join(pathDelimiter);
1011
+ const pathStr = path.map(ps => ps.id).join(pathDelimiter);
1012
1012
  return constructPathNode([ tableAlias, { id: pathStr, _artifact: pathNode._artifact } ]);
1013
1013
  }
1014
1014
 
@@ -1209,11 +1209,11 @@ function translateAssocsToJoins(model, inputOptions = {})
1209
1209
  */
1210
1210
  function constructTableAliasAndTailPath(path, navigation=undefined)
1211
1211
  {
1212
- let [head, ...tail] = path;
1212
+ const [head, ...tail] = path;
1213
1213
  if(navigation === undefined)
1214
1214
  navigation = head._navigation;
1215
1215
 
1216
- let QA = navigation.$QA || navigation._parent.$QA;
1216
+ const QA = navigation.$QA || navigation._parent.$QA;
1217
1217
 
1218
1218
  // First path step is table alias, use and pop it off
1219
1219
  if(navigation.$QA && tail.length > 0)
@@ -1251,7 +1251,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1251
1251
  {
1252
1252
  if(!pathStr)
1253
1253
  pathStr = '';
1254
- let assocStep = path.find(ps => {
1254
+ const assocStep = path.find(ps => {
1255
1255
  if(pathStr.length > 0)
1256
1256
  pathStr += pathDelimiter;
1257
1257
  pathStr += ps.id;
@@ -1303,7 +1303,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1303
1303
  pathStr += fk.name.id;
1304
1304
  }
1305
1305
 
1306
- let tail = path.slice(path.indexOf(fkPs)+1);
1306
+ const tail = path.slice(path.indexOf(fkPs)+1);
1307
1307
  // If foreign key is an association itself, apply substituteFKAliasForPath on tail
1308
1308
  if(fk && fk.targetElement._artifact.target && tail.length)
1309
1309
  return substituteFKAliasForPath(fk.targetElement, tail, pathStr);
@@ -1377,7 +1377,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1377
1377
  if(ps._artifact && ps._artifact.target)
1378
1378
  {
1379
1379
  // if this is not the last path step, complain
1380
- let la1 = pathDict.path[pathDict.path.indexOf(ps)+1];
1380
+ const la1 = pathDict.path[pathDict.path.indexOf(ps)+1];
1381
1381
  if(la1) {
1382
1382
  if(ps._artifact.on)
1383
1383
  {
@@ -1438,7 +1438,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1438
1438
  */
1439
1439
  function mergePathIntoQAT(pathDict, env)
1440
1440
  {
1441
- let path = pathDict.path;
1441
+ const path = pathDict.path;
1442
1442
 
1443
1443
  if(path.length === 0)
1444
1444
  return;
@@ -1505,7 +1505,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1505
1505
  if(qatParent == undefined)
1506
1506
  throw new CompilerAssertion('table alias/qathost not found for path: ' + pathAsStr(path));
1507
1507
 
1508
- let rootQat = qatParent;
1508
+ const rootQat = qatParent;
1509
1509
 
1510
1510
  // Create the very first QAT if it doesn't exist
1511
1511
  // (filter condition for table alias prefix not allowed)
@@ -1526,7 +1526,7 @@ function translateAssocsToJoins(model, inputOptions = {})
1526
1526
  }
1527
1527
  if(pathStep.args) {
1528
1528
  // sort named arguments
1529
- let sortedNamedArgs = Object.create(null);
1529
+ const sortedNamedArgs = Object.create(null);
1530
1530
  Object.keys(pathStep.args).sort().forEach(p => {
1531
1531
  sortedNamedArgs[p] = compactExpr(pathStep.args[p]);
1532
1532
  })
@@ -1687,10 +1687,10 @@ function translateAssocsToJoins(model, inputOptions = {})
1687
1687
  // eslint-disable-next-line no-unused-vars
1688
1688
  function printPath(pathDict, env)
1689
1689
  {
1690
- let alias = (pathDict.name && pathDict.name.id) || '<undefined>'
1691
- let path = pathDict.path;
1692
- let s = pathAsStr(path, '"');
1693
- let me = env.lead && (env.lead.name.id || env.lead.op);
1690
+ const alias = (pathDict.name && pathDict.name.id) || '<undefined>'
1691
+ const path = pathDict.path;
1692
+ const s = pathAsStr(path, '"');
1693
+ const me = env.lead && (env.lead.name.id || env.lead.op);
1694
1694
  // eslint-disable-next-line no-console
1695
1695
  console.log(me + ': ' + env.location + ': ' + s + ' alias: ' + alias);
1696
1696
  }
@@ -1708,9 +1708,9 @@ function translateAssocsToJoins(model, inputOptions = {})
1708
1708
  else
1709
1709
  newObj = {};
1710
1710
 
1711
- let props = Object.getOwnPropertyNames(obj); // clone own properties only, not inherited ones
1712
- for (let p of props) {
1713
- let pd = Object.getOwnPropertyDescriptor(obj, p);
1711
+ const props = Object.getOwnPropertyNames(obj); // clone own properties only, not inherited ones
1712
+ for (const p of props) {
1713
+ const pd = Object.getOwnPropertyDescriptor(obj, p);
1714
1714
  if (pd && pd.enumerable === false)
1715
1715
  {
1716
1716
  pd.value = obj[p]; // don't copy references
@@ -1740,10 +1740,10 @@ function translateAssocsToJoins(model, inputOptions = {})
1740
1740
  */
1741
1741
  function constructPathNode(pathSteps, alias, rewritten=true)
1742
1742
  {
1743
- let node = {
1743
+ const node = {
1744
1744
  $rewritten: rewritten,
1745
1745
  path : pathSteps.map(p => {
1746
- let o = {};
1746
+ const o = {};
1747
1747
  Object.keys(p).forEach(k => {
1748
1748
  if(!(rewritten && ['_'].includes(k[0])))
1749
1749
  o[k] = p[k];
@@ -1784,7 +1784,7 @@ function walkQuery(query, env)
1784
1784
  env.location = 'select';
1785
1785
  if(env.walkover[env.location])
1786
1786
  {
1787
- for(let alias in query.elements)
1787
+ for(const alias in query.elements)
1788
1788
  walk(query.elements[alias].value, env);
1789
1789
 
1790
1790
  env.location = 'Where';
@@ -1809,7 +1809,7 @@ function walkQuery(query, env)
1809
1809
 
1810
1810
  function walkFrom(fromBlock)
1811
1811
  {
1812
- let aliases = [];
1812
+ const aliases = [];
1813
1813
  env.position = fromBlock;
1814
1814
  if(fromBlock)
1815
1815
  {