@sap/cds-compiler 2.4.4 → 2.10.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 (106) hide show
  1. package/CHANGELOG.md +241 -1
  2. package/bin/.eslintrc.json +17 -0
  3. package/bin/cds_update_identifiers.js +8 -7
  4. package/bin/cdsc.js +180 -132
  5. package/bin/cdshi.js +18 -11
  6. package/bin/cdsse.js +38 -32
  7. package/bin/cdsv2m.js +8 -7
  8. package/doc/CHANGELOG_BETA.md +36 -1
  9. package/lib/api/main.js +81 -100
  10. package/lib/api/options.js +17 -11
  11. package/lib/api/validate.js +12 -8
  12. package/lib/backends.js +0 -81
  13. package/lib/base/keywords.js +32 -2
  14. package/lib/base/location.js +2 -2
  15. package/lib/base/message-registry.js +66 -4
  16. package/lib/base/messages.js +84 -27
  17. package/lib/base/model.js +2 -61
  18. package/lib/checks/arrayOfs.js +0 -1
  19. package/lib/checks/defaultValues.js +27 -2
  20. package/lib/checks/elements.js +1 -6
  21. package/lib/checks/enricher.js +8 -2
  22. package/lib/checks/foreignKeys.js +0 -6
  23. package/lib/checks/managedWithoutKeys.js +17 -0
  24. package/lib/checks/nonexpandableStructured.js +38 -0
  25. package/lib/checks/onConditions.js +9 -45
  26. package/lib/checks/queryNoDbArtifacts.js +27 -9
  27. package/lib/checks/selectItems.js +25 -2
  28. package/lib/checks/types.js +26 -2
  29. package/lib/checks/unknownMagic.js +38 -0
  30. package/lib/checks/utils.js +61 -0
  31. package/lib/checks/validator.js +66 -13
  32. package/lib/compiler/assert-consistency.js +24 -12
  33. package/lib/compiler/builtins.js +2 -0
  34. package/lib/compiler/checks.js +6 -4
  35. package/lib/compiler/definer.js +101 -39
  36. package/lib/compiler/index.js +88 -59
  37. package/lib/compiler/resolver.js +455 -209
  38. package/lib/compiler/shared.js +57 -33
  39. package/lib/edm/annotations/genericTranslation.js +183 -187
  40. package/lib/edm/csn2edm.js +128 -99
  41. package/lib/edm/edm.js +18 -21
  42. package/lib/edm/edmPreprocessor.js +361 -127
  43. package/lib/edm/edmUtils.js +103 -33
  44. package/lib/gen/Dictionary.json +74 -28
  45. package/lib/gen/language.checksum +1 -1
  46. package/lib/gen/language.interp +18 -4
  47. package/lib/gen/language.tokens +124 -118
  48. package/lib/gen/languageLexer.interp +13 -1
  49. package/lib/gen/languageLexer.js +870 -839
  50. package/lib/gen/languageLexer.tokens +116 -111
  51. package/lib/gen/languageParser.js +5894 -5614
  52. package/lib/json/from-csn.js +152 -67
  53. package/lib/json/to-csn.js +334 -135
  54. package/lib/language/antlrParser.js +4 -3
  55. package/lib/language/errorStrategy.js +1 -0
  56. package/lib/language/genericAntlrParser.js +24 -14
  57. package/lib/language/language.g4 +188 -128
  58. package/lib/main.d.ts +435 -0
  59. package/lib/main.js +31 -7
  60. package/lib/model/api.js +78 -0
  61. package/lib/model/csnRefs.js +463 -187
  62. package/lib/model/csnUtils.js +280 -136
  63. package/lib/model/enrichCsn.js +75 -4
  64. package/lib/model/revealInternalProperties.js +2 -1
  65. package/lib/modelCompare/compare.js +70 -25
  66. package/lib/optionProcessor.js +13 -10
  67. package/lib/render/.eslintrc.json +4 -1
  68. package/lib/render/DuplicateChecker.js +8 -5
  69. package/lib/render/toCdl.js +123 -40
  70. package/lib/render/toHdbcds.js +156 -65
  71. package/lib/render/toSql.js +87 -11
  72. package/lib/render/utils/common.js +55 -9
  73. package/lib/render/utils/sql.js +3 -3
  74. package/lib/sql-identifier.js +6 -1
  75. package/lib/transform/{sql → db}/.eslintrc.json +0 -0
  76. package/lib/transform/{sql → db}/assertUnique.js +7 -8
  77. package/lib/transform/{sql → db}/constraints.js +35 -20
  78. package/lib/transform/db/draft.js +353 -0
  79. package/lib/transform/db/expansion.js +582 -0
  80. package/lib/transform/db/flattening.js +325 -0
  81. package/lib/transform/{sql → db}/groupByOrderBy.js +8 -16
  82. package/lib/transform/{sql → db}/helpers.js +0 -0
  83. package/lib/transform/{sql → db}/transformExists.js +256 -60
  84. package/lib/transform/forHanaNew.js +216 -765
  85. package/lib/transform/forOdataNew.js +60 -56
  86. package/lib/transform/localized.js +48 -26
  87. package/lib/transform/odata/attachPath.js +19 -4
  88. package/lib/transform/odata/expandStructKeysInAssociations.js +2 -2
  89. package/lib/transform/odata/generateForeignKeyElements.js +13 -12
  90. package/lib/transform/odata/referenceFlattener.js +60 -36
  91. package/lib/transform/odata/sortByAssociationDependency.js +4 -4
  92. package/lib/transform/odata/structuralPath.js +76 -0
  93. package/lib/transform/odata/structureFlattener.js +21 -22
  94. package/lib/transform/odata/toFinalBaseType.js +5 -5
  95. package/lib/transform/odata/typesExposure.js +27 -17
  96. package/lib/transform/odata/utils.js +2 -2
  97. package/lib/transform/transformUtilsNew.js +141 -77
  98. package/lib/transform/translateAssocsToJoins.js +17 -14
  99. package/lib/transform/universalCsnEnricher.js +67 -0
  100. package/lib/utils/file.js +0 -11
  101. package/lib/utils/moduleResolve.js +6 -8
  102. package/lib/utils/timetrace.js +6 -1
  103. package/package.json +2 -1
  104. package/lib/base/deepCopy.js +0 -66
  105. package/lib/json/walker.js +0 -26
  106. package/lib/utils/string.js +0 -17
@@ -8,9 +8,8 @@ const { hasErrors, makeMessageFunction } = require('../base/messages');
8
8
  const { setProp } = require('../base/model');
9
9
  const { csnRefs } = require('../model/csnRefs');
10
10
 
11
- const { copyAnnotations } = require('../model/csnUtils');
12
- const { cloneCsn, forEachMemberRecursively, forEachGeneric, forAllQueries,
13
- getUtils, isBuiltinType } = require('../model/csnUtils');
11
+ const { copyAnnotations, applyTransformations } = require('../model/csnUtils');
12
+ const { cloneCsn, getUtils, isBuiltinType } = require('../model/csnUtils');
14
13
 
15
14
  // Return the public functions of this module, with 'model' captured in a closure (for definitions, options etc).
16
15
  // Use 'pathDelimiter' for flattened names (e.g. of struct elements or foreign key elements).
@@ -22,7 +21,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
22
21
  const {
23
22
  getCsnDef,
24
23
  getFinalBaseType,
25
- hasBoolAnnotation,
24
+ hasAnnotationValue,
26
25
  inspectRef,
27
26
  isStructured,
28
27
  } = getUtils(model);
@@ -62,7 +61,8 @@ function getTransformers(model, options, pathDelimiter = '_') {
62
61
  recurseElements,
63
62
  renameAnnotation,
64
63
  setAnnotation,
65
- expandStructsInOnConditions,
64
+ resetAnnotation,
65
+ expandStructsInExpression,
66
66
  };
67
67
 
68
68
  // Try to apply length, precision, scale from options if no type facet is set on the primitive types 'cds.String' or 'cds.Decimal'.
@@ -268,7 +268,6 @@ function getTransformers(model, options, pathDelimiter = '_') {
268
268
  let flatElem = cloneCsn(childElem, options);
269
269
  // Don't take over notNull from leaf elements
270
270
  delete flatElem.notNull;
271
- setProp(flatElem, '$viaTransform', true); // FIXME: This name is not ideal but used elsewhere, too)
272
271
  setProp(flatElem, '_flatElementNameWithDots', elementPath.concat(childName).join('.'));
273
272
  addGeneratedFlattenedElement(flatElem, flatElemName);
274
273
  }
@@ -288,49 +287,56 @@ function getTransformers(model, options, pathDelimiter = '_') {
288
287
  return result;
289
288
  }
290
289
 
291
- // Return a copy of 'ref' where all path steps resulting from struct traversal are
292
- // fused together into one step, using '_' (so that the path fits again for flattened
293
- // structs), e.g.
294
- // [ (Entity), (struct1), (struct2), (assoc), (elem) ] should result in
295
- // [ (Entity), (struct1_struct2_assoc), (elem) ]
296
- // 'path' is the csn path to the ref
297
- function flattenStructStepsInRef(ref, path) {
290
+ /**
291
+ * Return a copy of 'ref' where all path steps resulting from struct traversal are
292
+ * fused together into one step, using '_' (so that the path fits again for flattened
293
+ * structs), e.g.
294
+ * [ (Entity), (struct1), (struct2), (assoc), (elem) ] should result in
295
+ * [ (Entity), (struct1_struct2_assoc), (elem) ]
296
+ *
297
+ * @param {string[]} ref
298
+ * @param {CSN.Path} path CSN path to the ref
299
+ * @param {object[]} [links] Pre-resolved links for the given ref - if not provided, will be calculated JIT
300
+ * @param {string} [scope] Pre-resolved scope for the given ref - if not provided, will be calculated JIT
301
+ * @param {WeakMap} [resolvedLinkTypes=new WeakMap()] A WeakMap with already resolved types for each link-step - safes an `artifactRef` call
302
+ * @returns {string[]}
303
+ */
304
+ function flattenStructStepsInRef(ref, path, links, scope, resolvedLinkTypes=new WeakMap()) {
298
305
  // Refs of length 1 cannot contain steps - no need to check
299
306
  if (ref.length < 2) {
300
307
  return ref;
301
308
  }
302
309
 
303
- try {
304
- return flatten(ref, path);
305
- } catch (e) {
306
- if (e.message && e.message === "Scope 'ref-where' but no entity was provided.") {
307
- return flatten(ref, path);
308
- } else {
309
- throw e;
310
- }
311
- }
310
+ return flatten(ref, path);
312
311
 
313
312
  function flatten(ref, path) {
314
313
  let result = [];
315
314
  //let stack = []; // IDs of path steps not yet processed or part of a struct traversal
316
- const { links, scope } = inspectRef(path);
315
+ if(!links && !scope) { // calculate JIT if not supplied
316
+ const res = inspectRef(path);
317
+ links = res.links;
318
+ scope = res.scope;
319
+ }
317
320
  if (scope === '$magic')
318
321
  return ref;
319
322
  let flattenStep = false;
320
323
  links.forEach((value, idx) => {
321
- if (flattenStep)
322
- result[result.length - 1] += pathDelimiter + ref[idx];
324
+ if (flattenStep) {
325
+ result[result.length - 1] += pathDelimiter + (ref[idx].id ? ref[idx].id : ref[idx]);
326
+ // if we had a filter or args, we had an assoc so this step is done
327
+ // we then keep along the filter/args by updating the id of the current ref
328
+ if(ref[idx].id) {
329
+ ref[idx].id = result[result.length-1];
330
+ result[result.length-1] = ref[idx];
331
+ }
332
+ }
323
333
  else {
324
334
  result.push(ref[idx]);
325
335
  }
326
- flattenStep = value.art && !(value.art.kind === 'entity') && !value.art.SELECT && !value.art.from && (value.art.elements || effectiveType(value.art).elements);
336
+
337
+ flattenStep = value.art && !value.art.kind && !value.art.SELECT && !value.art.from && (value.art.elements || effectiveType(value.art).elements || (resolvedLinkTypes.get(value)||{}).elements);
327
338
  });
328
- const magicVars = ['$now']
329
- // If the path starts with '$self', this is now redundant (because of flattening) and can be omitted,
330
- // making life easier for consumers
331
- if (result[0] === '$self' && result.length > 1 && !magicVars.includes(result[1])) {
332
- result = result.slice(1);
333
- }
339
+
334
340
  return result;
335
341
  }
336
342
  }
@@ -366,22 +372,31 @@ function getTransformers(model, options, pathDelimiter = '_') {
366
372
  Object.assign(node, { srid: typeDef.srid });
367
373
  }
368
374
 
369
- // Replace the type of 'node' with its final base type (in contrast to the compiler,
370
- // also unravel derived enum types, i.e. take the final base type of the enum's base type.
371
- // Similar with associations and compositions (we probably need a _baseType link)
372
- function toFinalBaseType(node) {
375
+ /**
376
+ * Replace the type of 'node' with its final base type (in contrast to the compiler,
377
+ * also unravel derived enum types, i.e. take the final base type of the enum's base type.
378
+ * Similar with associations and compositions (we probably need a _baseType link)
379
+ *
380
+ * @param {CSN.Artifact} node
381
+ * @param {WeakMap} [resolved] WeakMap containing already resolved refs
382
+ * @param {boolean} [keepLocalized=false] Wether to clone .localized from a type def
383
+ * @returns {void}
384
+ */
385
+ function toFinalBaseType(node, resolved, keepLocalized=false) {
373
386
  // Nothing to do if no type (or if array/struct type)
374
387
  if (!node || !node.type) return;
375
388
  // In case of a ref -> Follow the ref
376
389
  if (node.type && node.type.ref) {
377
- let finalBaseType = getFinalBaseType(node.type);
390
+ const finalBaseType = getFinalBaseType(node.type, undefined, resolved);
378
391
  if(finalBaseType === null)
379
392
  throw Error('Failed to obtain final base type for reference : ' + node.type.ref.join('/'));
380
- if (finalBaseType.elements) {
381
- node.elements = cloneCsn(finalBaseType.elements, options); // copy elements
393
+ if(finalBaseType.elements) {
394
+ // This changes the order - to be discussed!
395
+ node.elements = cloneCsn(finalBaseType, options).elements; // copy elements
382
396
  delete node.type; // delete the type reference as edm processing does not expect it
383
- } else if (finalBaseType.items) {
384
- node.items = cloneCsn(finalBaseType.items, options); // copy items
397
+ } else if(finalBaseType.items) {
398
+ // This changes the order - to be discussed!
399
+ node.items = cloneCsn(finalBaseType, options).items; // copy items
385
400
  delete node.type;
386
401
  } else {
387
402
  node.type=finalBaseType;
@@ -394,10 +409,31 @@ function getTransformers(model, options, pathDelimiter = '_') {
394
409
  // The type might already be a full fledged type def (array of)
395
410
  let typeDef = typeof node.type === 'string' ? getCsnDef(node.type) : node.type;
396
411
  // Nothing to do if type is an array or a struct type
397
- if (typeDef.items || typeDef.elements) return;
412
+ if (typeDef.items || typeDef.elements) {
413
+ if(!(options.transformation === 'hdbcds' || options.toSql))
414
+ return;
415
+
416
+ // cloneCsn only works correctly if we start "from the top"
417
+ const clone = cloneCsn({definitions: {'TypeDef': typeDef }}, options);
418
+ // With hdbcds-hdbcds, don't resolve structured types - but propagrate ".items", to turn into LargeString later on.
419
+ if(typeDef.items) {
420
+ delete node.type;
421
+ Object.assign(node, {items: clone.definitions.TypeDef.items});
422
+ }
423
+ if(typeDef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')) {
424
+ if(!typeDef.items)
425
+ delete node.type;
426
+ Object.assign(node, {elements: clone.definitions.TypeDef.elements});
427
+ }
428
+
429
+
430
+ return;
431
+ }
398
432
  // if the declared element is an enum, these values are with priority
399
- if (!node.enum && typeDef.enum)
400
- Object.assign(node, { enum: cloneCsn(typeDef.enum, options) });
433
+ if (!node.enum && typeDef.enum) {
434
+ const clone = cloneCsn({definitions: {'TypeDef': typeDef }}, options).definitions.TypeDef.enum;
435
+ Object.assign(node, { enum: clone });
436
+ }
401
437
  if (node.length === undefined && typeDef.length !== undefined)
402
438
  Object.assign(node, { length: typeDef.length });
403
439
  if (node.precision === undefined && typeDef.precision !== undefined)
@@ -406,6 +442,8 @@ function getTransformers(model, options, pathDelimiter = '_') {
406
442
  Object.assign(node, { scale: typeDef.scale });
407
443
  if (node.srid === undefined && typeDef.srid !== undefined)
408
444
  Object.assign(node, { srid: typeDef.srid });
445
+ if (keepLocalized && node.localized === undefined && typeDef.localized !== undefined)
446
+ Object.assign(node, { localized: typeDef.localized });
409
447
  node.type = typeDef.type;
410
448
  toFinalBaseType(node);
411
449
  }
@@ -765,13 +803,13 @@ function getTransformers(model, options, pathDelimiter = '_') {
765
803
  */
766
804
  function extractValidFromToKeyElement(element, path) {
767
805
  let validFroms = [], validTos = [], validKeys = [];
768
- if (hasBoolAnnotation(element, '@cds.valid.from')) {
806
+ if (hasAnnotationValue(element, '@cds.valid.from')) {
769
807
  validFroms.push({ element, path: [...path] });
770
808
  }
771
- if (hasBoolAnnotation(element, '@cds.valid.to')) {
809
+ if (hasAnnotationValue(element, '@cds.valid.to')) {
772
810
  validTos.push({ element, path: [...path] });
773
811
  }
774
- if (hasBoolAnnotation(element, '@cds.valid.key')) {
812
+ if (hasAnnotationValue(element, '@cds.valid.key')) {
775
813
  validKeys.push({ element, path: [...path] });
776
814
  }
777
815
  return [validFroms, validTos, validKeys];
@@ -856,7 +894,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
856
894
  if (!toName.startsWith('@')) {
857
895
  throw Error('Annotation name should start with "@": ' + toName);
858
896
  }
859
- if (annotation == undefined) {
897
+ if (annotation === undefined) {
860
898
  throw Error('Annotation ' + fromName + ' not found in ' + JSON.stringify(node));
861
899
  }
862
900
  if(node[toName] === undefined || node[toName] === null) {
@@ -886,6 +924,34 @@ function getTransformers(model, options, pathDelimiter = '_') {
886
924
  node[name] = value;
887
925
  }
888
926
 
927
+ /**
928
+ * Assigns unconditionally annotation to a node, which means it overwrites already existing annotation assignment.
929
+ * Overwritting is when the assignment differs from undefined and null, also when differs from the already set value.
930
+ * Setting new assignment results false as return value and overwriting - true.
931
+ *
932
+ * @param {object} node Assignee
933
+ * @param {string} name Annotation name
934
+ * @param {any} value Annotation value
935
+ * @param {function} info function that reports info messages
936
+ * @param {CSN.Path} path location of the warning
937
+ * @returns {boolean} wasOverwritten true when the annotation was overwritten
938
+ */
939
+ function resetAnnotation(node, name, value, info, path) {
940
+ if (!name.startsWith('@')) {
941
+ throw Error('Annotation name should start with "@": ' + name);
942
+ }
943
+ if (value === undefined) {
944
+ throw Error('Annotation value must not be undefined');
945
+ }
946
+
947
+ const wasOverwritten = node[name] !== undefined && node[name] !== null && node[name] !== value;
948
+ const oldValue = node[name];
949
+ node[name] = value;
950
+ if(wasOverwritten)
951
+ info(null, path, { anno: name, prop: value, otherprop: oldValue },
952
+ `Value $(OTHERPROP) of annotation $(ANNO) is overwritten with new value $(PROP)`);
953
+ return wasOverwritten;
954
+ }
889
955
 
890
956
  /*
891
957
  Resolve the type of an artifact
@@ -967,7 +1033,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
967
1033
  function flattenPath(path, fullRef=false, followMgdAssoc=false) {
968
1034
  let art = path._art;
969
1035
  if(art) {
970
- if(art && art.type && !((art.items && art.items.elements) || art.elements)) {
1036
+ if(art && !((art.items && art.items.elements) || art.elements)) {
971
1037
  if(followMgdAssoc && art.target && art.keys) {
972
1038
  let rc = [];
973
1039
  for(const k of art.keys) {
@@ -980,9 +1046,9 @@ function getTransformers(model, options, pathDelimiter = '_') {
980
1046
  }
981
1047
  return rc;
982
1048
  }
983
- if(art.type.ref)
1049
+ if(art.type && art.type.ref)
984
1050
  art = resolvePath(art.type);
985
- else if(!isBuiltinType(art.type))
1051
+ else if(art.type && !isBuiltinType(art.type))
986
1052
  art = model.definitions[art.type];
987
1053
  }
988
1054
  const elements = art.items && art.items.elements || art.elements;
@@ -1004,35 +1070,33 @@ function getTransformers(model, options, pathDelimiter = '_') {
1004
1070
  return [path];
1005
1071
  }
1006
1072
 
1007
- /*
1008
- * Expand structured ON condition arguments to flat reference paths.
1073
+ /**
1074
+ * Expand structured expression arguments to flat reference paths.
1009
1075
  * Structured elements are real sub element lists and managed associations.
1010
1076
  * All unmanaged association definitions are rewritten if applicable (elements/mixins).
1077
+ * Also, HAVING and WHERE clauses are rewritten. We also check for infix filters and
1078
+ * .xpr in columns.
1011
1079
  *
1012
- * TODO: Check if can be skipped for abstract entity and or cds.persistence.skip ?
1013
- */
1014
- function expandStructsInOnConditions(artifact, artifactName, prop, path) {
1015
- forEachMemberRecursively(artifact,
1016
- (elem, elemName, prop, path) => {
1017
- if(prop === 'elements') {
1018
- if(elem.target && elem.on) {
1019
- elem.on = expand(elem.on, path)
1020
- }
1021
- }
1022
- }, path);
1023
-
1024
- if(artifact.query) {
1025
- forAllQueries(artifact.query, (query) => {
1026
- if(query.SELECT && query.SELECT.mixin) {
1027
- forEachGeneric(query.SELECT, 'mixin', (mixin, mixinName, prop, path) => {
1028
- if(mixin.target && mixin.on) {
1029
- mixin.on = expand(mixin.on, path);
1030
- }
1031
- },
1032
- path);
1033
- }
1034
- }, path.concat([ 'query' ]));
1035
- }
1080
+ * @todo Check if can be skipped for abstract entity and or cds.persistence.skip ?
1081
+ * @param {CSN.Model} csn
1082
+ * @param {object} [options={}] "skipArtifact": (artifact, name) => Boolean to skip certain artifacts
1083
+ */
1084
+ function expandStructsInExpression(csn, options = {}) {
1085
+ applyTransformations(csn, {
1086
+ 'on': (parent, name, on, path) => {
1087
+ parent.on = expand(parent.on, path);
1088
+ },
1089
+ 'having': (parent, name, having, path) => {
1090
+ parent.having = expand(parent.having, path);
1091
+ },
1092
+ 'where': (parent, name, where, path) => {
1093
+ parent.where = expand(parent.where, path);
1094
+ },
1095
+ 'xpr': (parent, name, xpr, path) => {
1096
+ parent.xpr = expand(parent.xpr, path);
1097
+ }
1098
+ }, undefined, undefined, options);
1099
+
1036
1100
  /*
1037
1101
  flatten structured leaf types and return array of paths
1038
1102
  Flattening stops on all non-structured types.
@@ -1,7 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  const { setProp, forEachGeneric, forEachDefinition, isBetaEnabled } = require('../base/model');
4
- var { handleMessages, makeMessageFunction } = require('../base/messages');
4
+ var { makeMessageFunction } = require('../base/messages');
5
+ const { recompileX } = require('../compiler/index');
5
6
  var { linkToOrigin } = require('../compiler/shared');
6
7
  const {compactModel, compactExpr} = require('../json/to-csn');
7
8
  const { deduplicateMessages } = require('../base/messages');
@@ -12,13 +13,10 @@ const internalArtifactKinds = ['builtin'/*, '$parameters'*/, 'param'];
12
13
 
13
14
  function translateAssocsToJoinsCSN(csn, options){
14
15
  timetrace.start('Recompiling model');
15
- let { augment } = require('../json/from-csn');
16
- // Append `.csn` to `.cds` files to indicate recompilation.
17
- const file = csn.$location && csn.$location.file.replace(/[.]cds$/, '.cds.csn') || '<recompile>.csn';
18
- let xsn = augment(csn, file, options);
19
- const { compileSourcesX } = require('../compiler');
20
16
  // Do not re-complain about localized
21
- const model = compileSourcesX( { [file]: xsn }, { ...options, $recompile: true } );
17
+ const compileOptions = { ...options, $skipNameCheck: true };
18
+ delete compileOptions.csnFlavor;
19
+ const model = recompileX(csn, compileOptions);
22
20
  timetrace.stop();
23
21
  timetrace.start('Translating associations to joins');
24
22
  translateAssocsToJoins(model, options);
@@ -46,9 +44,9 @@ function translateAssocsToJoinsCSN(csn, options){
46
44
  }
47
45
 
48
46
  // If A2J reports error - end! Continuing with a broken CSN makes no sense
49
- handleMessages(model, options);
47
+ makeMessageFunction(model, options).throwWithError();
50
48
  // FIXME: Move this somewhere more appropriate
51
- const compact = compactModel(model);
49
+ const compact = compactModel(model, compileOptions);
52
50
  return compact;
53
51
  }
54
52
 
@@ -618,7 +616,7 @@ function translateAssocsToJoins(model, inputOptions = {})
618
616
  const id = this.id();
619
617
  if(elt) {
620
618
  let found = true;
621
- const epath = elt.split('.');
619
+ const epath = [elt];
622
620
  const epl = epath.length+offset;
623
621
  if(epl < path.length) {
624
622
  for(let i = 0; i < epl && found; i++) {
@@ -630,7 +628,7 @@ function translateAssocsToJoins(model, inputOptions = {})
630
628
  }
631
629
  if(id) {
632
630
  let found = true;
633
- const epath = id.split('.');
631
+ const epath = [id];
634
632
  const epl = epath.length+offset;
635
633
  if(epl < path.length) {
636
634
  for(let i = 0; i < epl && found; i++) {
@@ -745,9 +743,14 @@ function translateAssocsToJoins(model, inputOptions = {})
745
743
  function swapTableAliasesForFwdAssoc(fwdAssoc, srcAlias, tgtAlias) {
746
744
  let newSrcAlias = tgtAlias;
747
745
  let newTgtAlias = {};
748
- // first try to identify table alias for complex views or
749
- // redirected associations
750
- if(fwdAssoc._redirected && fwdAssoc._redirected.length) {
746
+ // first try to identify table alias for complex views or redirected associations
747
+ if(fwdAssoc._redirected && fwdAssoc._redirected.length &&
748
+ // redirected target must have a $QA
749
+ fwdAssoc._redirected[fwdAssoc._redirected.length-1].$QA &&
750
+ // $QA's artifact must either be same srcAlias artifact
751
+ (fwdAssoc._redirected[fwdAssoc._redirected.length-1].$QA._artifact === srcAlias._artifact ||
752
+ // OR original assoc is a mixin (then just use the $QA)
753
+ assoc.kind === 'mixin')) {
751
754
  newTgtAlias.id = fwdAssoc._redirected[fwdAssoc._redirected.length-1].$QA.name.id;
752
755
  newTgtAlias._artifact = fwdAssoc._redirected[fwdAssoc._redirected.length-1]._effectiveType;
753
756
  newTgtAlias._navigation = fwdAssoc._redirected[fwdAssoc._redirected.length-1].$QA.path[0]._navigation;
@@ -0,0 +1,67 @@
1
+ 'use strict';
2
+
3
+ const { forEachDefinition } = require('../base/model');
4
+ const {
5
+ applyTransformations,
6
+ cloneCsn,
7
+ getUtils,
8
+ isBuiltinType,
9
+ } = require('../model/csnUtils');
10
+
11
+ /**
12
+ * Loop through a universal CSN and enrich it with the properties
13
+ * from the source definition - modifies the input model in-place
14
+ *
15
+ * @param {CSN.Model} csn
16
+ * @param {CSN.Options} options
17
+ */
18
+ module.exports = function(csn, options) {
19
+ let { getOrigin, getFinalType, getFinalTypeDef } = getUtils(csn);
20
+ // User-defined structured types do not have the elements propagated any longer
21
+ // if there is no association among the elements. For that reason,
22
+ // as a first step propagate the elements of these
23
+ forEachDefinition(csn, (def) => {
24
+ if (def.kind === 'type' && def.type && !def.elements) {
25
+ const finalType = getFinalType(def.type);
26
+ if (isBuiltinType(finalType)) return;
27
+ const finalTypeDef = getFinalTypeDef(def.type);
28
+ if (finalTypeDef.elements)
29
+ def.elements = cloneCsn(finalTypeDef.elements, options);
30
+ }
31
+ });
32
+
33
+ // as a second step, loop through all the $origin properties in the model
34
+ // and propagate the properties from the origin definition
35
+ applyTransformations(csn, {
36
+ '$origin': (node, _$orign, $originValue, _path, parent, propName) => {
37
+ if (!node.kind) { // we do not want to replace whole definitions
38
+ if (Array.isArray($originValue))
39
+ propagatePropsFromOrigin(node, propName, parent);
40
+ else if ($originValue.$origin && Array.isArray($originValue.$origin)) {
41
+ // cover the case of query entity elements where we have own and ihnerited attributes/annotations
42
+ propagatePropsFromOrigin($originValue, propName, parent);
43
+ }
44
+
45
+ }
46
+ }
47
+ }, undefined, undefined, options);
48
+
49
+ function propagatePropsFromOrigin(member, memberName, construct) {
50
+ // TODO: shall the $origin be kept as part of the element?
51
+ const origin = getOrigin(member);
52
+ if (origin.kind) return;
53
+ if (member.elements && origin.type) {
54
+ delete member.$origin;
55
+ member.type = origin.type;
56
+ return;
57
+ }
58
+ let newMember = cloneCsn(origin, options);
59
+ // keep targets and keys of assoc, if it was redirected
60
+ if (origin.type === 'cds.Association') {
61
+ newMember.target = member.target || newMember.target;
62
+ newMember.keys = member.keys || newMember.keys;
63
+ }
64
+ // TODO: check if this works fine for items/returns/actions
65
+ construct[memberName] = newMember;
66
+ }
67
+ }
package/lib/utils/file.js CHANGED
@@ -15,16 +15,6 @@ function splitLines(src) {
15
15
  return src.split(/\r\n?|\n/);
16
16
  }
17
17
 
18
- /**
19
- * Change Windows style line endings to Unix style
20
- *
21
- * @param {string} src
22
- * @returns {string}
23
- */
24
- function normalizeLineEndings(src) {
25
- return (src && process.platform === 'win32') ? src.replace(/\r\n/g, '\n') : src;
26
- }
27
-
28
18
  /**
29
19
  * Returns filesystem utils readFile(), isFile(), realpath() for _CDS_ usage.
30
20
  * This includes a trace as well as usage of a file cache.
@@ -181,6 +171,5 @@ function cdsFs(fileCache, enableTrace) {
181
171
 
182
172
  module.exports = {
183
173
  splitLines,
184
- normalizeLineEndings,
185
174
  cdsFs,
186
175
  };
@@ -7,7 +7,6 @@
7
7
 
8
8
  const path = require('path');
9
9
 
10
- const { makeMessageFunction } = require('../base/messages');
11
10
  const { cdsFs } = require('./file');
12
11
 
13
12
  const DEFAULT_ENCODING = 'utf-8';
@@ -44,7 +43,7 @@ function adaptCdsModule(modulePath) {
44
43
  * @param {object} fileCache
45
44
  * @param {CSN.Options} options
46
45
  */
47
- function resolveModule( dep, fileCache, options ) {
46
+ function resolveModule( dep, fileCache, options, messageFunctions ) {
48
47
  const _fs = cdsFs(fileCache, options.traceFs);
49
48
  // let opts = { extensions, basedir: dep.basedir, preserveSymlinks: false };
50
49
  // `preserveSymlinks` option does not really work -> provide workaround anyway...
@@ -96,7 +95,7 @@ function resolveModule( dep, fileCache, options ) {
96
95
  }
97
96
  }
98
97
  }).catch( () => {
99
- _errorFileNotFound(dep, options);
98
+ _errorFileNotFound(dep, options, messageFunctions);
100
99
  return false;
101
100
  });
102
101
  }
@@ -107,7 +106,7 @@ function resolveModule( dep, fileCache, options ) {
107
106
  * @param {object} fileCache
108
107
  * @param {CSN.Options} options
109
108
  */
110
- function resolveModuleSync( dep, fileCache, options ) {
109
+ function resolveModuleSync( dep, fileCache, options, messageFunctions ) {
111
110
  const _fs = cdsFs(fileCache, options.traceFs);
112
111
  const opts = {
113
112
  extensions,
@@ -129,7 +128,7 @@ function resolveModuleSync( dep, fileCache, options ) {
129
128
  });
130
129
 
131
130
  if (error) {
132
- _errorFileNotFound(dep, options);
131
+ _errorFileNotFound(dep, options, messageFunctions);
133
132
  return false;
134
133
  }
135
134
 
@@ -148,7 +147,7 @@ function resolveModuleSync( dep, fileCache, options ) {
148
147
  }
149
148
 
150
149
  if (error) {
151
- _errorFileNotFound(dep, options);
150
+ _errorFileNotFound(dep, options, messageFunctions);
152
151
  return false;
153
152
  }
154
153
 
@@ -161,8 +160,7 @@ function resolveModuleSync( dep, fileCache, options ) {
161
160
  return result;
162
161
  }
163
162
 
164
- function _errorFileNotFound(dep, options) {
165
- const { error } = makeMessageFunction( null, options, 'compile' );
163
+ function _errorFileNotFound(dep, options, { error }) {
166
164
  if (dep.resolved) {
167
165
  let resolved = path.relative( dep.basedir, dep.resolved );
168
166
  if (options.testMode)
@@ -95,5 +95,10 @@ class TimeTracer {
95
95
  }
96
96
  }
97
97
 
98
+ const ignoreTimeTrace = {
99
+ start: () => { /* ignore */ },
100
+ stop: () => { /* ignore */ },
101
+ };
102
+
98
103
  const doTimeTrace = process && process.env && process.env.CDSC_TIMETRACING !== undefined;
99
- module.exports = doTimeTrace ? new TimeTracer() : { start: () => {}, stop: () => {} };
104
+ module.exports = doTimeTrace ? new TimeTracer() : ignoreTimeTrace;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "2.4.4",
3
+ "version": "2.10.2",
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)",
@@ -11,6 +11,7 @@
11
11
  "cdsse": "bin/cdsse.js"
12
12
  },
13
13
  "main": "lib/main.js",
14
+ "types": "lib/main.d.ts",
14
15
  "scripts": {
15
16
  "postinstall": "node lib/fix_antlr4-8_warning.js"
16
17
  },