@sap/cds-compiler 4.8.0 → 4.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/CHANGELOG.md +29 -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 +14 -1
  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 +32 -19
  12. package/lib/base/messages.js +50 -19
  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 +8 -2
  20. package/lib/compiler/checks.js +44 -18
  21. package/lib/compiler/define.js +34 -22
  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/propagator.js +21 -18
  26. package/lib/compiler/resolve.js +44 -28
  27. package/lib/compiler/shared.js +60 -20
  28. package/lib/compiler/tweak-assocs.js +13 -88
  29. package/lib/compiler/xpr-rewrite.js +689 -0
  30. package/lib/edm/annotations/genericTranslation.js +80 -60
  31. package/lib/edm/edm.js +4 -4
  32. package/lib/edm/edmInboundChecks.js +33 -0
  33. package/lib/edm/edmPreprocessor.js +9 -6
  34. package/lib/gen/Dictionary.json +129 -14
  35. package/lib/gen/language.checksum +1 -1
  36. package/lib/gen/language.interp +1 -1
  37. package/lib/gen/languageParser.js +1523 -1518
  38. package/lib/json/from-csn.js +13 -4
  39. package/lib/json/to-csn.js +10 -11
  40. package/lib/language/genericAntlrParser.js +14 -6
  41. package/lib/main.d.ts +67 -14
  42. package/lib/main.js +1 -0
  43. package/lib/model/cloneCsn.js +6 -3
  44. package/lib/model/csnRefs.js +12 -7
  45. package/lib/model/csnUtils.js +13 -7
  46. package/lib/model/enrichCsn.js +3 -1
  47. package/lib/model/revealInternalProperties.js +2 -1
  48. package/lib/model/sortViews.js +14 -6
  49. package/lib/modelCompare/compare.js +33 -34
  50. package/lib/optionProcessor.js +27 -2
  51. package/lib/render/DuplicateChecker.js +6 -6
  52. package/lib/render/manageConstraints.js +1 -0
  53. package/lib/render/toCdl.js +3 -1
  54. package/lib/transform/db/applyTransformations.js +33 -0
  55. package/lib/transform/db/constraints.js +1 -1
  56. package/lib/transform/db/expansion.js +8 -3
  57. package/lib/transform/db/groupByOrderBy.js +2 -2
  58. package/lib/transform/db/temporal.js +6 -3
  59. package/lib/transform/db/transformExists.js +2 -2
  60. package/lib/transform/effective/annotations.js +194 -0
  61. package/lib/transform/effective/main.js +6 -8
  62. package/lib/transform/effective/misc.js +31 -10
  63. package/lib/transform/forOdata.js +23 -7
  64. package/lib/transform/forRelationalDB.js +1 -1
  65. package/lib/transform/localized.js +7 -6
  66. package/lib/transform/odata/flattening.js +189 -106
  67. package/lib/transform/odata/toFinalBaseType.js +1 -1
  68. package/lib/transform/odata/typesExposure.js +15 -12
  69. package/lib/transform/parseExpr.js +4 -4
  70. package/lib/transform/transformUtils.js +40 -37
  71. package/lib/transform/translateAssocsToJoins.js +47 -47
  72. package/lib/transform/universalCsn/universalCsnEnricher.js +12 -16
  73. package/package.json +1 -1
  74. package/share/messages/anno-missing-rewrite.md +45 -0
  75. package/share/messages/message-explanations.json +1 -0
  76. package/bin/.eslintrc.json +0 -17
  77. package/lib/api/.eslintrc.json +0 -37
  78. package/lib/checks/.eslintrc.json +0 -31
  79. package/lib/compiler/.eslintrc.json +0 -8
  80. package/lib/edm/.eslintrc.json +0 -46
  81. package/lib/inspect/.eslintrc.json +0 -4
  82. package/lib/json/.eslintrc.json +0 -4
  83. package/lib/language/.eslintrc.json +0 -4
  84. package/lib/model/.eslintrc.json +0 -13
  85. package/lib/modelCompare/utils/.eslintrc.json +0 -22
  86. package/lib/render/.eslintrc.json +0 -22
  87. package/lib/transform/.eslintrc.json +0 -13
  88. package/lib/transform/db/.eslintrc.json +0 -41
  89. package/lib/transform/draft/.eslintrc.json +0 -4
  90. package/lib/transform/effective/.eslintrc.json +0 -4
  91. package/lib/transform/universalCsn/.eslintrc.json +0 -37
  92. package/lib/utils/.eslintrc.json +0 -7
@@ -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,9 +324,11 @@ 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
+ * @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)
327
329
  * @returns {string[]}
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
334
  return ref;
@@ -368,9 +370,10 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
368
370
  }
369
371
  else {
370
372
  result.push(ref[i]);
373
+ suspend ||= !!art('items');
371
374
  }
372
375
  // revoke items suspension for next assoc step
373
- if(suspend && art('target'))
376
+ if(suspend && art('target') || (revokeAtSuspendPos && i === suspendPos))
374
377
  suspend = false;
375
378
 
376
379
  flattenStep = !links[i].art?.kind &&
@@ -471,11 +474,11 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
471
474
  // Add the created projection to the model and complain if artifact already exists.
472
475
  // Used by Draft generation
473
476
  function createExposingProjection(art, artName, projectionId, service) {
474
- let projectionAbsoluteName = `${service}.${projectionId}`;
477
+ const projectionAbsoluteName = `${service}.${projectionId}`;
475
478
  // Create elements matching the artifact's elements
476
- let elements = Object.create(null);
479
+ const elements = Object.create(null);
477
480
  art.elements && Object.entries(art.elements).forEach(([elemName, artElem]) => {
478
- let elem = Object.assign({}, artElem);
481
+ const elem = Object.assign({}, artElem);
479
482
  // Transfer xrefs, that are redirected to the projection
480
483
  // TODO: shall we remove the transferred elements from the original?
481
484
  // if (artElem._xref) {
@@ -485,7 +488,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
485
488
  elements[elemName] = elem;
486
489
  });
487
490
 
488
- let query = {
491
+ const query = {
489
492
  'SELECT': {
490
493
  'from': {
491
494
  'ref': [
@@ -495,13 +498,13 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
495
498
  }
496
499
  };
497
500
  // Assemble the projection itself and add it into the model
498
- let projection = {
501
+ const projection = {
499
502
  'kind': 'entity',
500
503
  projection: query.SELECT, // it is important that projection and query refer to the same object!
501
504
  elements
502
505
  };
503
506
  // copy annotations from art to projection
504
- for (let a of Object.keys(art).filter(x => x.startsWith('@'))) {
507
+ for (const a of Object.keys(art).filter(x => x.startsWith('@'))) {
505
508
  projection[a] = art[a];
506
509
  }
507
510
  model.definitions[projectionAbsoluteName] = projection;
@@ -606,7 +609,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
606
609
  if (!isBuiltinType(typeName) && !model.definitions[typeName]) {
607
610
  throw new ModelError('Expecting valid type name: ' + typeName);
608
611
  }
609
- let result = {
612
+ const result = {
610
613
  [elemName]: {
611
614
  type: typeName
612
615
  }
@@ -634,16 +637,16 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
634
637
  // keys: [{ ref: ['id'] }]
635
638
  // } }
636
639
  function createAssociationElement(elemName, target, isManaged = false) {
637
- let elem = createScalarElement(elemName, 'cds.Association', false, undefined);
638
- let assoc = elem[elemName];
640
+ const elem = createScalarElement(elemName, 'cds.Association', false, undefined);
641
+ const assoc = elem[elemName];
639
642
  assoc.target = target;
640
643
 
641
644
  if (isManaged) {
642
645
  assoc.keys = [];
643
- let targetArt = getCsnDef(target);
646
+ const targetArt = getCsnDef(target);
644
647
  targetArt.elements && Object.entries(targetArt.elements).forEach(([keyElemName, keyElem]) => {
645
648
  if (keyElem.key) {
646
- let foreignKey = createForeignKey(keyElemName, keyElem);
649
+ const foreignKey = createForeignKey(keyElemName, keyElem);
647
650
  addForeignKey(foreignKey, assoc);
648
651
  }
649
652
  });
@@ -700,7 +703,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
700
703
  if (!artifact.elements) {
701
704
  throw new ModelError('Expecting artifact with elements: ' + JSON.stringify(artifact));
702
705
  }
703
- let elemName = Object.keys(elem)[0];
706
+ const elemName = Object.keys(elem)[0];
704
707
  // Element must not exist
705
708
  if (artifact.elements[elemName]) {
706
709
  let path = null;
@@ -737,7 +740,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
737
740
  error(null, path, { name: elementName }, 'Generated element $(NAME) conflicts with existing element');
738
741
  }
739
742
 
740
- let result = Object.create(null);
743
+ const result = Object.create(null);
741
744
  result[elementName] = {};
742
745
  elem && Object.entries(elem).forEach(([prop, value]) => {
743
746
  result[elementName][prop] = value;
@@ -750,13 +753,13 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
750
753
  // of type name 'paramTypeName'
751
754
  function createAction(actionName, returnTypeName = undefined, paramName = undefined, paramTypeName = undefined) {
752
755
  // Assemble the action
753
- let result = {
756
+ const result = {
754
757
  [actionName]: {
755
758
  kind: 'action'
756
759
  }
757
760
  };
758
761
 
759
- let action = result[actionName];
762
+ const action = result[actionName];
760
763
 
761
764
  if (returnTypeName) {
762
765
  if (!isBuiltinType(returnTypeName) && !model.definitions[returnTypeName])
@@ -791,7 +794,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
791
794
  artifact.actions = Object.create(null);
792
795
  }
793
796
 
794
- let actionName = Object.keys(action)[0];
797
+ const actionName = Object.keys(action)[0];
795
798
  // Element must not exist
796
799
  if (!artifact.actions[actionName]) {
797
800
  // Add the action
@@ -808,7 +811,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
808
811
  * second field if it has @cds.valid.to. Default value is [] for each field.
809
812
  */
810
813
  function extractValidFromToKeyElement(element, path) {
811
- let validFroms = [], validTos = [], validKeys = [];
814
+ const validFroms = [], validTos = [], validKeys = [];
812
815
  if (hasAnnotationValue(element, '@cds.valid.from')) {
813
816
  validFroms.push({ element, path: [...path] });
814
817
  }
@@ -878,7 +881,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
878
881
  */
879
882
  function recurseElements(artifact, path, callback) {
880
883
  callback(artifact, path);
881
- let elements = artifact.elements;
884
+ const elements = artifact.elements;
882
885
  if (elements) {
883
886
  path.push('elements', null);
884
887
  forEach(elements, (name, obj) => {
@@ -892,7 +895,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
892
895
 
893
896
  // Rename annotation 'fromName' in 'node' to 'toName' (both names including '@')
894
897
  function renameAnnotation(node, fromName, toName) {
895
- let annotation = node && node[fromName];
898
+ const annotation = node && node[fromName];
896
899
  // Sanity checks
897
900
  if (!fromName.startsWith('@')) {
898
901
  throw new CompilerAssertion('Annotation name should start with "@": ' + fromName);
@@ -1039,7 +1042,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
1039
1042
  if(art) {
1040
1043
  if(art && !((art.items && art.items.elements) || art.elements)) {
1041
1044
  if(followMgdAssoc && art.target && art.keys) {
1042
- let rc = [];
1045
+ const rc = [];
1043
1046
  for(const k of art.keys) {
1044
1047
  const nps = { ref: k.ref.map(p => fullRef ? { id: p } : p ) };
1045
1048
  setProp(nps, '_art', k._art);
@@ -1057,7 +1060,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
1057
1060
  }
1058
1061
  const elements = art.items && art.items.elements || art.elements;
1059
1062
  if(elements) {
1060
- let rc = []
1063
+ const rc = []
1061
1064
  Object.entries(elements).forEach(([en, elt]) => {
1062
1065
  const nps = { ref: [ (fullRef ? { id: en, _art: elt } : en )] };
1063
1066
  setProp(nps, '_art', elt);
@@ -1106,7 +1109,7 @@ function getTransformers(model, options, msgFunctions, pathDelimiter = '_') {
1106
1109
  Flattening stops on all non-structured types.
1107
1110
  */
1108
1111
  function expand(expr, location) {
1109
- let rc = [];
1112
+ const rc = [];
1110
1113
  for(let i = 0; i < expr.length; i++)
1111
1114
  {
1112
1115
  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
  {
@@ -9,7 +9,7 @@ const {
9
9
  applyTransformations,
10
10
  implicitAs,
11
11
  } = require('../../model/csnUtils');
12
- const { isBuiltinType } = require('../../base/builtins');
12
+ const { isBuiltinType, propagationRules } = require('../../base/builtins');
13
13
  const {
14
14
  forEachValue, forEach,
15
15
  } = require('../../utils/objectUtils');
@@ -29,24 +29,10 @@ module.exports = (csn, options) => {
29
29
  } = csnUtils;
30
30
  // Properties on definition level that we treat specially.
31
31
  const definitionPropagationRules = {
32
- '@cds.autoexpose': onlyViaArtifact,
33
- '@cds.external': skip,
34
- '@fiori.draft.enabled': onlyViaArtifact,
32
+ __proto__: null,
35
33
  '@': nullStopsPropagation,
36
34
  // Example: `type E : F;` does not have `elements`, but they are required for e.g. OData.
37
35
  elements: onlyTypeDef,
38
- '@cds.persistence.exists': skip,
39
- '@cds.persistence.table': skip,
40
- '@cds.persistence.calcview': skip,
41
- '@cds.persistence.udf': skip,
42
- '@cds.persistence.skip': notWithPersistenceTable,
43
- '@sql.append': skip,
44
- '@sql.prepend': skip,
45
- '@sql.replace': skip,
46
- '@Analytics.hidden': skip,
47
- '@Analytics.visible': skip,
48
- '@cds.autoexposed': skip,
49
- '@cds.redirection.target': skip,
50
36
  type: always,
51
37
  doc: nullStopsPropagation,
52
38
  length: always,
@@ -65,6 +51,16 @@ module.exports = (csn, options) => {
65
51
  keys: always,
66
52
  };
67
53
 
54
+ const ruleToFunction = {
55
+ __proto__: null,
56
+ never: skip,
57
+ onlyViaArtifact,
58
+ notWithPersistenceTable,
59
+ };
60
+
61
+ for (const rule in propagationRules)
62
+ definitionPropagationRules[rule] = ruleToFunction[propagationRules[rule]];
63
+
68
64
  // Properties on member level that we treat specially
69
65
  const memberPropagationRules = {
70
66
  key: skip,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "4.8.0",
3
+ "version": "4.9.0",
4
4
  "description": "CDS (Core Data Services) compiler and backends",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "author": "SAP SE (https://www.sap.com)",